2 * Media driver for Freescale i.MX5/6 SOC
4 * Adds the internal subdevices and the media links between them.
6 * Copyright (c) 2016 Mentor Graphics Inc.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 #include <linux/platform_device.h>
14 #include "imx-media.h"
26 static const struct internal_subdev_id {
33 .grp_id = IMX_MEDIA_GRP_ID_CSI0,
34 .name = "imx-ipuv3-csi",
38 .grp_id = IMX_MEDIA_GRP_ID_CSI1,
39 .name = "imx-ipuv3-csi",
43 .grp_id = IMX_MEDIA_GRP_ID_VDIC,
44 .name = "imx-ipuv3-vdic",
48 .grp_id = IMX_MEDIA_GRP_ID_IC_PRP,
49 .name = "imx-ipuv3-ic",
52 .index = isd_ic_prpenc,
53 .grp_id = IMX_MEDIA_GRP_ID_IC_PRPENC,
54 .name = "imx-ipuv3-ic",
57 .index = isd_ic_prpvf,
58 .grp_id = IMX_MEDIA_GRP_ID_IC_PRPVF,
59 .name = "imx-ipuv3-ic",
63 struct internal_subdev;
65 struct internal_link {
66 const struct internal_subdev *remote;
71 /* max links per internal-sd pad */
72 #define MAX_INTERNAL_LINKS 8
75 struct internal_link link[MAX_INTERNAL_LINKS];
78 static const struct internal_subdev {
79 const struct internal_subdev_id *id;
80 struct internal_pad pad[IMX_MEDIA_MAX_PADS];
83 } int_subdev[num_isd] = {
85 .id = &isd_id[isd_csi0],
86 .num_sink_pads = CSI_NUM_SINK_PADS,
87 .num_src_pads = CSI_NUM_SRC_PADS,
88 .pad[CSI_SRC_PAD_DIRECT] = {
91 .local_pad = CSI_SRC_PAD_DIRECT,
92 .remote = &int_subdev[isd_ic_prp],
93 .remote_pad = PRP_SINK_PAD,
95 .local_pad = CSI_SRC_PAD_DIRECT,
96 .remote = &int_subdev[isd_vdic],
97 .remote_pad = VDIC_SINK_PAD_DIRECT,
104 .id = &isd_id[isd_csi1],
105 .num_sink_pads = CSI_NUM_SINK_PADS,
106 .num_src_pads = CSI_NUM_SRC_PADS,
107 .pad[CSI_SRC_PAD_DIRECT] = {
110 .local_pad = CSI_SRC_PAD_DIRECT,
111 .remote = &int_subdev[isd_ic_prp],
112 .remote_pad = PRP_SINK_PAD,
114 .local_pad = CSI_SRC_PAD_DIRECT,
115 .remote = &int_subdev[isd_vdic],
116 .remote_pad = VDIC_SINK_PAD_DIRECT,
123 .id = &isd_id[isd_vdic],
124 .num_sink_pads = VDIC_NUM_SINK_PADS,
125 .num_src_pads = VDIC_NUM_SRC_PADS,
126 .pad[VDIC_SRC_PAD_DIRECT] = {
129 .local_pad = VDIC_SRC_PAD_DIRECT,
130 .remote = &int_subdev[isd_ic_prp],
131 .remote_pad = PRP_SINK_PAD,
138 .id = &isd_id[isd_ic_prp],
139 .num_sink_pads = PRP_NUM_SINK_PADS,
140 .num_src_pads = PRP_NUM_SRC_PADS,
141 .pad[PRP_SRC_PAD_PRPENC] = {
144 .local_pad = PRP_SRC_PAD_PRPENC,
145 .remote = &int_subdev[isd_ic_prpenc],
150 .pad[PRP_SRC_PAD_PRPVF] = {
153 .local_pad = PRP_SRC_PAD_PRPVF,
154 .remote = &int_subdev[isd_ic_prpvf],
162 .id = &isd_id[isd_ic_prpenc],
163 .num_sink_pads = PRPENCVF_NUM_SINK_PADS,
164 .num_src_pads = PRPENCVF_NUM_SRC_PADS,
168 .id = &isd_id[isd_ic_prpvf],
169 .num_sink_pads = PRPENCVF_NUM_SINK_PADS,
170 .num_src_pads = PRPENCVF_NUM_SRC_PADS,
174 /* form a device name given an internal subdev and ipu id */
175 static inline void isd_to_devname(char *devname, int sz,
176 const struct internal_subdev *isd,
179 int pdev_id = ipu_id * num_isd + isd->id->index;
181 snprintf(devname, sz, "%s.%d", isd->id->name, pdev_id);
184 static const struct internal_subdev *find_intsd_by_grp_id(u32 grp_id)
188 for (i = 0; i < num_isd; i++) {
189 const struct internal_subdev *isd = &int_subdev[i];
191 if (isd->id->grp_id == grp_id)
198 static struct imx_media_subdev *find_sink(struct imx_media_dev *imxmd,
199 struct imx_media_subdev *src,
200 const struct internal_link *link)
202 char sink_devname[32];
206 * retrieve IPU id from subdev name, note: can't get this from
207 * struct imx_media_internal_sd_platformdata because if src is
208 * a CSI, it has different struct ipu_client_platformdata which
209 * does not contain IPU id.
211 if (sscanf(src->sd->name, "ipu%d", &ipu_id) != 1)
214 isd_to_devname(sink_devname, sizeof(sink_devname),
215 link->remote, ipu_id - 1);
217 return imx_media_find_async_subdev(imxmd, NULL, sink_devname);
220 static int create_ipu_internal_link(struct imx_media_dev *imxmd,
221 struct imx_media_subdev *src,
222 const struct internal_link *link)
224 struct imx_media_subdev *sink;
227 sink = find_sink(imxmd, src, link);
231 v4l2_info(&imxmd->v4l2_dev, "%s:%d -> %s:%d\n",
232 src->sd->name, link->local_pad,
233 sink->sd->name, link->remote_pad);
235 ret = media_create_pad_link(&src->sd->entity, link->local_pad,
236 &sink->sd->entity, link->remote_pad, 0);
238 v4l2_err(&imxmd->v4l2_dev,
239 "create_pad_link failed: %d\n", ret);
244 int imx_media_create_internal_links(struct imx_media_dev *imxmd,
245 struct imx_media_subdev *imxsd)
247 struct v4l2_subdev *sd = imxsd->sd;
248 const struct internal_subdev *intsd;
249 const struct internal_pad *intpad;
250 const struct internal_link *link;
251 struct media_pad *pad;
254 intsd = find_intsd_by_grp_id(imxsd->sd->grp_id);
258 /* create the source->sink links */
259 for (i = 0; i < sd->entity.num_pads; i++) {
260 intpad = &intsd->pad[i];
261 pad = &sd->entity.pads[i];
263 if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
267 link = &intpad->link[j];
272 ret = create_ipu_internal_link(imxmd, imxsd, link);
281 /* register an internal subdev as a platform device */
282 static int add_internal_subdev(struct imx_media_dev *imxmd,
283 const struct internal_subdev *isd,
286 struct imx_media_internal_sd_platformdata pdata;
287 struct platform_device_info pdevinfo = {0};
288 struct imx_media_subdev *imxsd;
289 struct platform_device *pdev;
291 pdata.grp_id = isd->id->grp_id;
293 /* the id of IPU this subdev will control */
294 pdata.ipu_id = ipu_id;
296 /* create subdev name */
297 imx_media_grp_id_to_sd_name(pdata.sd_name, sizeof(pdata.sd_name),
298 pdata.grp_id, ipu_id);
300 pdevinfo.name = isd->id->name;
301 pdevinfo.id = ipu_id * num_isd + isd->id->index;
302 pdevinfo.parent = imxmd->md.dev;
303 pdevinfo.data = &pdata;
304 pdevinfo.size_data = sizeof(pdata);
305 pdevinfo.dma_mask = DMA_BIT_MASK(32);
307 pdev = platform_device_register_full(&pdevinfo);
309 return PTR_ERR(pdev);
311 imxsd = imx_media_add_async_subdev(imxmd, NULL, pdev);
313 return PTR_ERR(imxsd);
315 imxsd->num_sink_pads = isd->num_sink_pads;
316 imxsd->num_src_pads = isd->num_src_pads;
321 /* adds the internal subdevs in one ipu */
322 static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id)
326 for (i = 0; i < num_isd; i++) {
327 const struct internal_subdev *isd = &int_subdev[i];
331 * the CSIs are represented in the device-tree, so those
332 * devices are already added to the async subdev list by
335 switch (isd->id->grp_id) {
336 case IMX_MEDIA_GRP_ID_CSI0:
337 case IMX_MEDIA_GRP_ID_CSI1:
341 ret = add_internal_subdev(imxmd, isd, ipu_id);
352 int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd)
356 ret = add_ipu_internal_subdevs(imxmd, 0);
360 ret = add_ipu_internal_subdevs(imxmd, 1);
367 imx_media_remove_internal_subdevs(imxmd);
371 void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd)
373 struct imx_media_subdev *imxsd;
376 for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) {
377 imxsd = &imxmd->subdev[i];
380 platform_device_unregister(imxsd->pdev);