1 // SPDX-License-Identifier: GPL-2.0+
3 * Media driver for Freescale i.MX5/6 SOC
5 * Adds the IPU internal subdevices and the media links between them.
7 * Copyright (c) 2016 Mentor Graphics Inc.
9 #include <linux/platform_device.h>
10 #include "imx-media.h"
22 static const struct internal_subdev_id {
29 .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0,
30 .name = "imx-ipuv3-csi",
34 .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1,
35 .name = "imx-ipuv3-csi",
39 .grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC,
40 .name = "imx-ipuv3-vdic",
44 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP,
45 .name = "imx-ipuv3-ic",
48 .index = isd_ic_prpenc,
49 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC,
50 .name = "imx-ipuv3-ic",
53 .index = isd_ic_prpvf,
54 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF,
55 .name = "imx-ipuv3-ic",
59 struct internal_subdev;
61 struct internal_link {
62 const struct internal_subdev *remote;
67 /* max pads per internal-sd */
68 #define MAX_INTERNAL_PADS 8
69 /* max links per internal-sd pad */
70 #define MAX_INTERNAL_LINKS 8
73 struct internal_link link[MAX_INTERNAL_LINKS];
76 static const struct internal_subdev {
77 const struct internal_subdev_id *id;
78 struct internal_pad pad[MAX_INTERNAL_PADS];
79 } int_subdev[num_isd] = {
81 .id = &isd_id[isd_csi0],
82 .pad[CSI_SRC_PAD_DIRECT] = {
85 .local_pad = CSI_SRC_PAD_DIRECT,
86 .remote = &int_subdev[isd_ic_prp],
87 .remote_pad = PRP_SINK_PAD,
89 .local_pad = CSI_SRC_PAD_DIRECT,
90 .remote = &int_subdev[isd_vdic],
91 .remote_pad = VDIC_SINK_PAD_DIRECT,
98 .id = &isd_id[isd_csi1],
99 .pad[CSI_SRC_PAD_DIRECT] = {
102 .local_pad = CSI_SRC_PAD_DIRECT,
103 .remote = &int_subdev[isd_ic_prp],
104 .remote_pad = PRP_SINK_PAD,
106 .local_pad = CSI_SRC_PAD_DIRECT,
107 .remote = &int_subdev[isd_vdic],
108 .remote_pad = VDIC_SINK_PAD_DIRECT,
115 .id = &isd_id[isd_vdic],
116 .pad[VDIC_SRC_PAD_DIRECT] = {
119 .local_pad = VDIC_SRC_PAD_DIRECT,
120 .remote = &int_subdev[isd_ic_prp],
121 .remote_pad = PRP_SINK_PAD,
128 .id = &isd_id[isd_ic_prp],
129 .pad[PRP_SRC_PAD_PRPENC] = {
132 .local_pad = PRP_SRC_PAD_PRPENC,
133 .remote = &int_subdev[isd_ic_prpenc],
138 .pad[PRP_SRC_PAD_PRPVF] = {
141 .local_pad = PRP_SRC_PAD_PRPVF,
142 .remote = &int_subdev[isd_ic_prpvf],
150 .id = &isd_id[isd_ic_prpenc],
154 .id = &isd_id[isd_ic_prpvf],
158 /* form a device name given an internal subdev and ipu id */
159 static inline void isd_to_devname(char *devname, int sz,
160 const struct internal_subdev *isd,
163 int pdev_id = ipu_id * num_isd + isd->id->index;
165 snprintf(devname, sz, "%s.%d", isd->id->name, pdev_id);
168 static const struct internal_subdev *find_intsd_by_grp_id(u32 grp_id)
172 for (i = 0; i < num_isd; i++) {
173 const struct internal_subdev *isd = &int_subdev[i];
175 if (isd->id->grp_id == grp_id)
182 static struct v4l2_subdev *find_sink(struct imx_media_dev *imxmd,
183 struct v4l2_subdev *src,
184 const struct internal_link *link)
186 char sink_devname[32];
190 * retrieve IPU id from subdev name, note: can't get this from
191 * struct imx_media_ipu_internal_sd_pdata because if src is
192 * a CSI, it has different struct ipu_client_platformdata which
193 * does not contain IPU id.
195 if (sscanf(src->name, "ipu%d", &ipu_id) != 1)
198 isd_to_devname(sink_devname, sizeof(sink_devname),
199 link->remote, ipu_id - 1);
201 return imx_media_find_subdev_by_devname(imxmd, sink_devname);
204 static int create_ipu_internal_link(struct imx_media_dev *imxmd,
205 struct v4l2_subdev *src,
206 const struct internal_link *link)
208 struct v4l2_subdev *sink;
211 sink = find_sink(imxmd, src, link);
215 v4l2_info(&imxmd->v4l2_dev, "%s:%d -> %s:%d\n",
216 src->name, link->local_pad,
217 sink->name, link->remote_pad);
219 ret = media_create_pad_link(&src->entity, link->local_pad,
220 &sink->entity, link->remote_pad, 0);
222 v4l2_err(&imxmd->v4l2_dev,
223 "create_pad_link failed: %d\n", ret);
228 int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd,
229 struct v4l2_subdev *sd)
231 const struct internal_subdev *intsd;
232 const struct internal_pad *intpad;
233 const struct internal_link *link;
234 struct media_pad *pad;
237 intsd = find_intsd_by_grp_id(sd->grp_id);
241 /* create the source->sink links */
242 for (i = 0; i < sd->entity.num_pads; i++) {
243 intpad = &intsd->pad[i];
244 pad = &sd->entity.pads[i];
246 if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
250 link = &intpad->link[j];
255 ret = create_ipu_internal_link(imxmd, sd, link);
264 /* register an internal subdev as a platform device */
265 static int add_internal_subdev(struct imx_media_dev *imxmd,
266 const struct internal_subdev *isd,
269 struct imx_media_ipu_internal_sd_pdata pdata;
270 struct platform_device_info pdevinfo = {};
271 struct platform_device *pdev;
273 pdata.grp_id = isd->id->grp_id;
275 /* the id of IPU this subdev will control */
276 pdata.ipu_id = ipu_id;
278 /* create subdev name */
279 imx_media_grp_id_to_sd_name(pdata.sd_name, sizeof(pdata.sd_name),
280 pdata.grp_id, ipu_id);
282 pdevinfo.name = isd->id->name;
283 pdevinfo.id = ipu_id * num_isd + isd->id->index;
284 pdevinfo.parent = imxmd->md.dev;
285 pdevinfo.data = &pdata;
286 pdevinfo.size_data = sizeof(pdata);
287 pdevinfo.dma_mask = DMA_BIT_MASK(32);
289 pdev = platform_device_register_full(&pdevinfo);
291 return PTR_ERR(pdev);
293 return imx_media_add_async_subdev(imxmd, NULL, pdev);
296 /* adds the internal subdevs in one ipu */
297 int imx_media_add_ipu_internal_subdevs(struct imx_media_dev *imxmd,
303 for (i = 0; i < num_isd; i++) {
304 const struct internal_subdev *isd = &int_subdev[i];
307 * the CSIs are represented in the device-tree, so those
308 * devices are already added to the async subdev list by
311 switch (isd->id->grp_id) {
312 case IMX_MEDIA_GRP_ID_IPU_CSI0:
313 case IMX_MEDIA_GRP_ID_IPU_CSI1:
317 ret = add_internal_subdev(imxmd, isd, ipu_id);
328 imx_media_remove_ipu_internal_subdevs(imxmd);
332 void imx_media_remove_ipu_internal_subdevs(struct imx_media_dev *imxmd)
334 struct imx_media_async_subdev *imxasd;
335 struct v4l2_async_subdev *asd;
337 list_for_each_entry(asd, &imxmd->notifier.asd_list, asd_list) {
338 imxasd = to_imx_media_asd(asd);
343 platform_device_unregister(imxasd->pdev);