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_link {
64 const struct internal_subdev_id *remote_id;
69 bool devnode; /* does this pad link to a device node */
70 struct internal_link link[IMX_MEDIA_MAX_LINKS];
73 static const struct internal_subdev {
74 const struct internal_subdev_id *id;
75 struct internal_pad pad[IMX_MEDIA_MAX_PADS];
78 } internal_subdev[num_isd] = {
80 .id = &isd_id[isd_csi0],
81 .num_sink_pads = CSI_NUM_SINK_PADS,
82 .num_src_pads = CSI_NUM_SRC_PADS,
83 .pad[CSI_SRC_PAD_DIRECT] = {
86 .remote_id = &isd_id[isd_ic_prp],
87 .remote_pad = PRP_SINK_PAD,
89 .remote_id = &isd_id[isd_vdic],
90 .remote_pad = VDIC_SINK_PAD_DIRECT,
94 .pad[CSI_SRC_PAD_IDMAC] = {
100 .id = &isd_id[isd_csi1],
101 .num_sink_pads = CSI_NUM_SINK_PADS,
102 .num_src_pads = CSI_NUM_SRC_PADS,
103 .pad[CSI_SRC_PAD_DIRECT] = {
106 .remote_id = &isd_id[isd_ic_prp],
107 .remote_pad = PRP_SINK_PAD,
109 .remote_id = &isd_id[isd_vdic],
110 .remote_pad = VDIC_SINK_PAD_DIRECT,
114 .pad[CSI_SRC_PAD_IDMAC] = {
120 .id = &isd_id[isd_vdic],
121 .num_sink_pads = VDIC_NUM_SINK_PADS,
122 .num_src_pads = VDIC_NUM_SRC_PADS,
123 .pad[VDIC_SINK_PAD_IDMAC] = {
126 .pad[VDIC_SRC_PAD_DIRECT] = {
129 .remote_id = &isd_id[isd_ic_prp],
130 .remote_pad = PRP_SINK_PAD,
137 .id = &isd_id[isd_ic_prp],
138 .num_sink_pads = PRP_NUM_SINK_PADS,
139 .num_src_pads = PRP_NUM_SRC_PADS,
140 .pad[PRP_SRC_PAD_PRPENC] = {
143 .remote_id = &isd_id[isd_ic_prpenc],
148 .pad[PRP_SRC_PAD_PRPVF] = {
151 .remote_id = &isd_id[isd_ic_prpvf],
159 .id = &isd_id[isd_ic_prpenc],
160 .num_sink_pads = PRPENCVF_NUM_SINK_PADS,
161 .num_src_pads = PRPENCVF_NUM_SRC_PADS,
162 .pad[PRPENCVF_SRC_PAD] = {
168 .id = &isd_id[isd_ic_prpvf],
169 .num_sink_pads = PRPENCVF_NUM_SINK_PADS,
170 .num_src_pads = PRPENCVF_NUM_SRC_PADS,
171 .pad[PRPENCVF_SRC_PAD] = {
177 /* form a device name given a group id and ipu id */
178 static inline void isd_id_to_devname(char *devname, int sz,
179 const struct internal_subdev_id *id,
182 int pdev_id = ipu_id * num_isd + id->index;
184 snprintf(devname, sz, "%s.%d", id->name, pdev_id);
187 /* adds the links from given internal subdev */
188 static int add_internal_links(struct imx_media_dev *imxmd,
189 const struct internal_subdev *isd,
190 struct imx_media_subdev *imxsd,
193 int i, num_pads, ret;
195 num_pads = isd->num_sink_pads + isd->num_src_pads;
197 for (i = 0; i < num_pads; i++) {
198 const struct internal_pad *intpad = &isd->pad[i];
199 struct imx_media_pad *pad = &imxsd->pad[i];
202 /* init the pad flags for this internal subdev */
203 pad->pad.flags = (i < isd->num_sink_pads) ?
204 MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
205 /* export devnode pad flag to the subdevs */
206 pad->devnode = intpad->devnode;
209 const struct internal_link *link;
210 char remote_devname[32];
212 link = &intpad->link[j];
214 if (!link->remote_id)
217 isd_id_to_devname(remote_devname,
218 sizeof(remote_devname),
219 link->remote_id, ipu_id);
221 ret = imx_media_add_pad_link(imxmd, pad,
222 NULL, remote_devname,
223 i, link->remote_pad);
232 /* register an internal subdev as a platform device */
233 static struct imx_media_subdev *
234 add_internal_subdev(struct imx_media_dev *imxmd,
235 const struct internal_subdev *isd,
238 struct imx_media_internal_sd_platformdata pdata;
239 struct platform_device_info pdevinfo = {0};
240 struct imx_media_subdev *imxsd;
241 struct platform_device *pdev;
243 pdata.grp_id = isd->id->grp_id;
245 /* the id of IPU this subdev will control */
246 pdata.ipu_id = ipu_id;
248 /* create subdev name */
249 imx_media_grp_id_to_sd_name(pdata.sd_name, sizeof(pdata.sd_name),
250 pdata.grp_id, ipu_id);
252 pdevinfo.name = isd->id->name;
253 pdevinfo.id = ipu_id * num_isd + isd->id->index;
254 pdevinfo.parent = imxmd->md.dev;
255 pdevinfo.data = &pdata;
256 pdevinfo.size_data = sizeof(pdata);
257 pdevinfo.dma_mask = DMA_BIT_MASK(32);
259 pdev = platform_device_register_full(&pdevinfo);
261 return ERR_CAST(pdev);
263 imxsd = imx_media_add_async_subdev(imxmd, NULL, pdev);
267 imxsd->num_sink_pads = isd->num_sink_pads;
268 imxsd->num_src_pads = isd->num_src_pads;
273 /* adds the internal subdevs in one ipu */
274 static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd,
275 struct imx_media_subdev *csi0,
276 struct imx_media_subdev *csi1,
282 for (i = 0; i < num_isd; i++) {
283 const struct internal_subdev *isd = &internal_subdev[i];
284 struct imx_media_subdev *imxsd;
287 * the CSIs are represented in the device-tree, so those
288 * devices are added already, and are added to the async
289 * subdev list by of_parse_subdev(), so we are given those
290 * subdevs as csi0 and csi1.
292 switch (isd->id->grp_id) {
293 case IMX_MEDIA_GRP_ID_CSI0:
296 case IMX_MEDIA_GRP_ID_CSI1:
300 imxsd = add_internal_subdev(imxmd, isd, ipu_id);
305 return PTR_ERR(imxsd);
307 /* add the links from this subdev */
309 ret = add_internal_links(imxmd, isd, imxsd, ipu_id);
318 int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd,
319 struct imx_media_subdev *csi[4])
323 ret = add_ipu_internal_subdevs(imxmd, csi[0], csi[1], 0);
327 ret = add_ipu_internal_subdevs(imxmd, csi[2], csi[3], 1);
334 imx_media_remove_internal_subdevs(imxmd);
338 void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd)
340 struct imx_media_subdev *imxsd;
343 for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) {
344 imxsd = &imxmd->subdev[i];
347 platform_device_unregister(imxsd->pdev);