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"
12 /* max pads per internal-sd */
13 #define MAX_INTERNAL_PADS 8
14 /* max links per internal-sd pad */
15 #define MAX_INTERNAL_LINKS 8
17 struct internal_subdev;
19 struct internal_link {
27 struct internal_link link[MAX_INTERNAL_LINKS];
30 struct internal_subdev {
32 struct internal_pad pad[MAX_INTERNAL_PADS];
34 struct v4l2_subdev * (*sync_register)(struct v4l2_device *v4l2_dev,
35 struct device *ipu_dev,
38 int (*sync_unregister)(struct v4l2_subdev *sd);
41 static const struct internal_subdev int_subdev[NUM_IPU_SUBDEVS] = {
43 .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0,
44 .pad[CSI_SRC_PAD_DIRECT] = {
48 .local_pad = CSI_SRC_PAD_DIRECT,
50 .remote_pad = PRP_SINK_PAD,
52 .local_pad = CSI_SRC_PAD_DIRECT,
54 .remote_pad = VDIC_SINK_PAD_DIRECT,
61 .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1,
62 .pad[CSI_SRC_PAD_DIRECT] = {
66 .local_pad = CSI_SRC_PAD_DIRECT,
68 .remote_pad = PRP_SINK_PAD,
70 .local_pad = CSI_SRC_PAD_DIRECT,
72 .remote_pad = VDIC_SINK_PAD_DIRECT,
79 .grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC,
80 .sync_register = imx_media_vdic_register,
81 .sync_unregister = imx_media_vdic_unregister,
82 .pad[VDIC_SRC_PAD_DIRECT] = {
86 .local_pad = VDIC_SRC_PAD_DIRECT,
88 .remote_pad = PRP_SINK_PAD,
95 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP,
96 .sync_register = imx_media_ic_register,
97 .sync_unregister = imx_media_ic_unregister,
98 .pad[PRP_SRC_PAD_PRPENC] = {
102 .local_pad = PRP_SRC_PAD_PRPENC,
103 .remote = IPU_IC_PRPENC,
104 .remote_pad = PRPENCVF_SINK_PAD,
108 .pad[PRP_SRC_PAD_PRPVF] = {
112 .local_pad = PRP_SRC_PAD_PRPVF,
113 .remote = IPU_IC_PRPVF,
114 .remote_pad = PRPENCVF_SINK_PAD,
121 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC,
122 .sync_register = imx_media_ic_register,
123 .sync_unregister = imx_media_ic_unregister,
127 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF,
128 .sync_register = imx_media_ic_register,
129 .sync_unregister = imx_media_ic_unregister,
133 static int create_internal_link(struct imx_media_dev *imxmd,
134 struct v4l2_subdev *src,
135 struct v4l2_subdev *sink,
136 const struct internal_link *link)
140 /* skip if this link already created */
141 if (media_entity_find_link(&src->entity.pads[link->local_pad],
142 &sink->entity.pads[link->remote_pad]))
145 dev_dbg(imxmd->md.dev, "%s:%d -> %s:%d\n",
146 src->name, link->local_pad,
147 sink->name, link->remote_pad);
149 ret = media_create_pad_link(&src->entity, link->local_pad,
150 &sink->entity, link->remote_pad, 0);
152 v4l2_err(&imxmd->v4l2_dev, "%s failed: %d\n", __func__, ret);
157 static int create_ipu_internal_links(struct imx_media_dev *imxmd,
158 const struct internal_subdev *intsd,
159 struct v4l2_subdev *sd,
162 const struct internal_pad *intpad;
163 const struct internal_link *link;
164 struct media_pad *pad;
167 /* create the source->sink links */
168 for (i = 0; i < sd->entity.num_pads; i++) {
169 intpad = &intsd->pad[i];
170 pad = &sd->entity.pads[i];
172 if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
175 for (j = 0; j < intpad->num_links; j++) {
176 struct v4l2_subdev *sink;
178 link = &intpad->link[j];
179 sink = imxmd->sync_sd[ipu_id][link->remote];
181 ret = create_internal_link(imxmd, sd, sink, link);
190 int imx_media_register_ipu_internal_subdevs(struct imx_media_dev *imxmd,
191 struct v4l2_subdev *csi)
193 struct device *ipu_dev = csi->dev->parent;
194 const struct internal_subdev *intsd;
195 struct v4l2_subdev *sd;
199 ipu = dev_get_drvdata(ipu_dev);
201 v4l2_err(&imxmd->v4l2_dev, "invalid IPU device!\n");
205 ipu_id = ipu_get_num(ipu);
207 v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id);
211 mutex_lock(&imxmd->mutex);
213 /* record this IPU */
214 if (!imxmd->ipu[ipu_id])
215 imxmd->ipu[ipu_id] = ipu;
217 /* register the synchronous subdevs */
218 for (i = 0; i < NUM_IPU_SUBDEVS; i++) {
219 intsd = &int_subdev[i];
221 sd = imxmd->sync_sd[ipu_id][i];
224 * skip if this sync subdev already registered or its
225 * not a sync subdev (one of the CSIs)
227 if (sd || !intsd->sync_register)
230 mutex_unlock(&imxmd->mutex);
231 sd = intsd->sync_register(&imxmd->v4l2_dev, ipu_dev, ipu,
233 mutex_lock(&imxmd->mutex);
239 imxmd->sync_sd[ipu_id][i] = sd;
243 * all the sync subdevs are registered, create the media links
246 for (i = 0; i < NUM_IPU_SUBDEVS; i++) {
247 intsd = &int_subdev[i];
249 if (intsd->grp_id == csi->grp_id) {
252 sd = imxmd->sync_sd[ipu_id][i];
257 ret = create_ipu_internal_links(imxmd, intsd, sd, ipu_id);
259 mutex_unlock(&imxmd->mutex);
260 imx_media_unregister_ipu_internal_subdevs(imxmd);
265 mutex_unlock(&imxmd->mutex);
270 intsd = &int_subdev[i];
271 sd = imxmd->sync_sd[ipu_id][i];
272 if (!sd || !intsd->sync_unregister)
274 mutex_unlock(&imxmd->mutex);
275 intsd->sync_unregister(sd);
276 mutex_lock(&imxmd->mutex);
279 mutex_unlock(&imxmd->mutex);
283 void imx_media_unregister_ipu_internal_subdevs(struct imx_media_dev *imxmd)
285 const struct internal_subdev *intsd;
286 struct v4l2_subdev *sd;
289 mutex_lock(&imxmd->mutex);
291 for (i = 0; i < 2; i++) {
292 for (j = 0; j < NUM_IPU_SUBDEVS; j++) {
293 intsd = &int_subdev[j];
294 sd = imxmd->sync_sd[i][j];
296 if (!sd || !intsd->sync_unregister)
299 mutex_unlock(&imxmd->mutex);
300 intsd->sync_unregister(sd);
301 mutex_lock(&imxmd->mutex);
305 mutex_unlock(&imxmd->mutex);