1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/platform_device.h>
14#include "imx-media.h"
15
16enum isd_enum {
17 isd_csi0 = 0,
18 isd_csi1,
19 isd_vdic,
20 isd_ic_prp,
21 isd_ic_prpenc,
22 isd_ic_prpvf,
23 num_isd,
24};
25
26static const struct internal_subdev_id {
27 enum isd_enum index;
28 const char *name;
29 u32 grp_id;
30} isd_id[num_isd] = {
31 [isd_csi0] = {
32 .index = isd_csi0,
33 .grp_id = IMX_MEDIA_GRP_ID_CSI0,
34 .name = "imx-ipuv3-csi",
35 },
36 [isd_csi1] = {
37 .index = isd_csi1,
38 .grp_id = IMX_MEDIA_GRP_ID_CSI1,
39 .name = "imx-ipuv3-csi",
40 },
41 [isd_vdic] = {
42 .index = isd_vdic,
43 .grp_id = IMX_MEDIA_GRP_ID_VDIC,
44 .name = "imx-ipuv3-vdic",
45 },
46 [isd_ic_prp] = {
47 .index = isd_ic_prp,
48 .grp_id = IMX_MEDIA_GRP_ID_IC_PRP,
49 .name = "imx-ipuv3-ic",
50 },
51 [isd_ic_prpenc] = {
52 .index = isd_ic_prpenc,
53 .grp_id = IMX_MEDIA_GRP_ID_IC_PRPENC,
54 .name = "imx-ipuv3-ic",
55 },
56 [isd_ic_prpvf] = {
57 .index = isd_ic_prpvf,
58 .grp_id = IMX_MEDIA_GRP_ID_IC_PRPVF,
59 .name = "imx-ipuv3-ic",
60 },
61};
62
63struct internal_subdev;
64
65struct internal_link {
66 const struct internal_subdev *remote;
67 int local_pad;
68 int remote_pad;
69};
70
71
72#define MAX_INTERNAL_PADS 8
73
74#define MAX_INTERNAL_LINKS 8
75
76struct internal_pad {
77 struct internal_link link[MAX_INTERNAL_LINKS];
78};
79
80static const struct internal_subdev {
81 const struct internal_subdev_id *id;
82 struct internal_pad pad[MAX_INTERNAL_PADS];
83} int_subdev[num_isd] = {
84 [isd_csi0] = {
85 .id = &isd_id[isd_csi0],
86 .pad[CSI_SRC_PAD_DIRECT] = {
87 .link = {
88 {
89 .local_pad = CSI_SRC_PAD_DIRECT,
90 .remote = &int_subdev[isd_ic_prp],
91 .remote_pad = PRP_SINK_PAD,
92 }, {
93 .local_pad = CSI_SRC_PAD_DIRECT,
94 .remote = &int_subdev[isd_vdic],
95 .remote_pad = VDIC_SINK_PAD_DIRECT,
96 },
97 },
98 },
99 },
100
101 [isd_csi1] = {
102 .id = &isd_id[isd_csi1],
103 .pad[CSI_SRC_PAD_DIRECT] = {
104 .link = {
105 {
106 .local_pad = CSI_SRC_PAD_DIRECT,
107 .remote = &int_subdev[isd_ic_prp],
108 .remote_pad = PRP_SINK_PAD,
109 }, {
110 .local_pad = CSI_SRC_PAD_DIRECT,
111 .remote = &int_subdev[isd_vdic],
112 .remote_pad = VDIC_SINK_PAD_DIRECT,
113 },
114 },
115 },
116 },
117
118 [isd_vdic] = {
119 .id = &isd_id[isd_vdic],
120 .pad[VDIC_SRC_PAD_DIRECT] = {
121 .link = {
122 {
123 .local_pad = VDIC_SRC_PAD_DIRECT,
124 .remote = &int_subdev[isd_ic_prp],
125 .remote_pad = PRP_SINK_PAD,
126 },
127 },
128 },
129 },
130
131 [isd_ic_prp] = {
132 .id = &isd_id[isd_ic_prp],
133 .pad[PRP_SRC_PAD_PRPENC] = {
134 .link = {
135 {
136 .local_pad = PRP_SRC_PAD_PRPENC,
137 .remote = &int_subdev[isd_ic_prpenc],
138 .remote_pad = 0,
139 },
140 },
141 },
142 .pad[PRP_SRC_PAD_PRPVF] = {
143 .link = {
144 {
145 .local_pad = PRP_SRC_PAD_PRPVF,
146 .remote = &int_subdev[isd_ic_prpvf],
147 .remote_pad = 0,
148 },
149 },
150 },
151 },
152
153 [isd_ic_prpenc] = {
154 .id = &isd_id[isd_ic_prpenc],
155 },
156
157 [isd_ic_prpvf] = {
158 .id = &isd_id[isd_ic_prpvf],
159 },
160};
161
162
163static inline void isd_to_devname(char *devname, int sz,
164 const struct internal_subdev *isd,
165 int ipu_id)
166{
167 int pdev_id = ipu_id * num_isd + isd->id->index;
168
169 snprintf(devname, sz, "%s.%d", isd->id->name, pdev_id);
170}
171
172static const struct internal_subdev *find_intsd_by_grp_id(u32 grp_id)
173{
174 enum isd_enum i;
175
176 for (i = 0; i < num_isd; i++) {
177 const struct internal_subdev *isd = &int_subdev[i];
178
179 if (isd->id->grp_id == grp_id)
180 return isd;
181 }
182
183 return NULL;
184}
185
186static struct v4l2_subdev *find_sink(struct imx_media_dev *imxmd,
187 struct v4l2_subdev *src,
188 const struct internal_link *link)
189{
190 char sink_devname[32];
191 int ipu_id;
192
193
194
195
196
197
198
199 if (sscanf(src->name, "ipu%d", &ipu_id) != 1)
200 return NULL;
201
202 isd_to_devname(sink_devname, sizeof(sink_devname),
203 link->remote, ipu_id - 1);
204
205 return imx_media_find_subdev_by_devname(imxmd, sink_devname);
206}
207
208static int create_ipu_internal_link(struct imx_media_dev *imxmd,
209 struct v4l2_subdev *src,
210 const struct internal_link *link)
211{
212 struct v4l2_subdev *sink;
213 int ret;
214
215 sink = find_sink(imxmd, src, link);
216 if (!sink)
217 return -ENODEV;
218
219 v4l2_info(&imxmd->v4l2_dev, "%s:%d -> %s:%d\n",
220 src->name, link->local_pad,
221 sink->name, link->remote_pad);
222
223 ret = media_create_pad_link(&src->entity, link->local_pad,
224 &sink->entity, link->remote_pad, 0);
225 if (ret)
226 v4l2_err(&imxmd->v4l2_dev,
227 "create_pad_link failed: %d\n", ret);
228
229 return ret;
230}
231
232int imx_media_create_internal_links(struct imx_media_dev *imxmd,
233 struct v4l2_subdev *sd)
234{
235 const struct internal_subdev *intsd;
236 const struct internal_pad *intpad;
237 const struct internal_link *link;
238 struct media_pad *pad;
239 int i, j, ret;
240
241 intsd = find_intsd_by_grp_id(sd->grp_id);
242 if (!intsd)
243 return -ENODEV;
244
245
246 for (i = 0; i < sd->entity.num_pads; i++) {
247 intpad = &intsd->pad[i];
248 pad = &sd->entity.pads[i];
249
250 if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
251 continue;
252
253 for (j = 0; ; j++) {
254 link = &intpad->link[j];
255
256 if (!link->remote)
257 break;
258
259 ret = create_ipu_internal_link(imxmd, sd, link);
260 if (ret)
261 return ret;
262 }
263 }
264
265 return 0;
266}
267
268
269static int add_internal_subdev(struct imx_media_dev *imxmd,
270 const struct internal_subdev *isd,
271 int ipu_id)
272{
273 struct imx_media_internal_sd_platformdata pdata;
274 struct platform_device_info pdevinfo = {};
275 struct platform_device *pdev;
276
277 pdata.grp_id = isd->id->grp_id;
278
279
280 pdata.ipu_id = ipu_id;
281
282
283 imx_media_grp_id_to_sd_name(pdata.sd_name, sizeof(pdata.sd_name),
284 pdata.grp_id, ipu_id);
285
286 pdevinfo.name = isd->id->name;
287 pdevinfo.id = ipu_id * num_isd + isd->id->index;
288 pdevinfo.parent = imxmd->md.dev;
289 pdevinfo.data = &pdata;
290 pdevinfo.size_data = sizeof(pdata);
291 pdevinfo.dma_mask = DMA_BIT_MASK(32);
292
293 pdev = platform_device_register_full(&pdevinfo);
294 if (IS_ERR(pdev))
295 return PTR_ERR(pdev);
296
297 return imx_media_add_async_subdev(imxmd, NULL, pdev);
298}
299
300
301static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id)
302{
303 enum isd_enum i;
304
305 for (i = 0; i < num_isd; i++) {
306 const struct internal_subdev *isd = &int_subdev[i];
307 int ret;
308
309
310
311
312
313
314 switch (isd->id->grp_id) {
315 case IMX_MEDIA_GRP_ID_CSI0:
316 case IMX_MEDIA_GRP_ID_CSI1:
317 ret = 0;
318 break;
319 default:
320 ret = add_internal_subdev(imxmd, isd, ipu_id);
321 break;
322 }
323
324 if (ret)
325 return ret;
326 }
327
328 return 0;
329}
330
331int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd)
332{
333 int ret;
334
335 ret = add_ipu_internal_subdevs(imxmd, 0);
336 if (ret)
337 goto remove;
338
339 ret = add_ipu_internal_subdevs(imxmd, 1);
340 if (ret)
341 goto remove;
342
343 return 0;
344
345remove:
346 imx_media_remove_internal_subdevs(imxmd);
347 return ret;
348}
349
350void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd)
351{
352 struct imx_media_async_subdev *imxasd;
353 struct v4l2_async_subdev *asd;
354
355 list_for_each_entry(asd, &imxmd->notifier.asd_list, asd_list) {
356 imxasd = to_imx_media_asd(asd);
357
358 if (!imxasd->pdev)
359 continue;
360
361 platform_device_unregister(imxasd->pdev);
362 }
363}
364