media: staging/imx: remove static media link arrays
[platform/kernel/linux-rpi.git] / drivers / staging / media / imx / imx-media-internal-sd.c
1 /*
2  * Media driver for Freescale i.MX5/6 SOC
3  *
4  * Adds the internal subdevices and the media links between them.
5  *
6  * Copyright (c) 2016 Mentor Graphics Inc.
7  *
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.
12  */
13 #include <linux/platform_device.h>
14 #include "imx-media.h"
15
16 enum 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
26 static 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
63 struct internal_subdev;
64
65 struct internal_link {
66         const struct internal_subdev *remote;
67         int local_pad;
68         int remote_pad;
69 };
70
71 /* max links per internal-sd pad */
72 #define MAX_INTERNAL_LINKS  8
73
74 struct internal_pad {
75         struct internal_link link[MAX_INTERNAL_LINKS];
76 };
77
78 static const struct internal_subdev {
79         const struct internal_subdev_id *id;
80         struct internal_pad pad[IMX_MEDIA_MAX_PADS];
81         int num_sink_pads;
82         int num_src_pads;
83 } int_subdev[num_isd] = {
84         [isd_csi0] = {
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] = {
89                         .link = {
90                                 {
91                                         .local_pad = CSI_SRC_PAD_DIRECT,
92                                         .remote = &int_subdev[isd_ic_prp],
93                                         .remote_pad = PRP_SINK_PAD,
94                                 }, {
95                                         .local_pad = CSI_SRC_PAD_DIRECT,
96                                         .remote = &int_subdev[isd_vdic],
97                                         .remote_pad = VDIC_SINK_PAD_DIRECT,
98                                 },
99                         },
100                 },
101         },
102
103         [isd_csi1] = {
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] = {
108                         .link = {
109                                 {
110                                         .local_pad = CSI_SRC_PAD_DIRECT,
111                                         .remote = &int_subdev[isd_ic_prp],
112                                         .remote_pad = PRP_SINK_PAD,
113                                 }, {
114                                         .local_pad = CSI_SRC_PAD_DIRECT,
115                                         .remote = &int_subdev[isd_vdic],
116                                         .remote_pad = VDIC_SINK_PAD_DIRECT,
117                                 },
118                         },
119                 },
120         },
121
122         [isd_vdic] = {
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] = {
127                         .link = {
128                                 {
129                                         .local_pad = VDIC_SRC_PAD_DIRECT,
130                                         .remote = &int_subdev[isd_ic_prp],
131                                         .remote_pad = PRP_SINK_PAD,
132                                 },
133                         },
134                 },
135         },
136
137         [isd_ic_prp] = {
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] = {
142                         .link = {
143                                 {
144                                         .local_pad = PRP_SRC_PAD_PRPENC,
145                                         .remote = &int_subdev[isd_ic_prpenc],
146                                         .remote_pad = 0,
147                                 },
148                         },
149                 },
150                 .pad[PRP_SRC_PAD_PRPVF] = {
151                         .link = {
152                                 {
153                                         .local_pad = PRP_SRC_PAD_PRPVF,
154                                         .remote = &int_subdev[isd_ic_prpvf],
155                                         .remote_pad = 0,
156                                 },
157                         },
158                 },
159         },
160
161         [isd_ic_prpenc] = {
162                 .id = &isd_id[isd_ic_prpenc],
163                 .num_sink_pads = PRPENCVF_NUM_SINK_PADS,
164                 .num_src_pads = PRPENCVF_NUM_SRC_PADS,
165         },
166
167         [isd_ic_prpvf] = {
168                 .id = &isd_id[isd_ic_prpvf],
169                 .num_sink_pads = PRPENCVF_NUM_SINK_PADS,
170                 .num_src_pads = PRPENCVF_NUM_SRC_PADS,
171         },
172 };
173
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,
177                                   int ipu_id)
178 {
179         int pdev_id = ipu_id * num_isd + isd->id->index;
180
181         snprintf(devname, sz, "%s.%d", isd->id->name, pdev_id);
182 }
183
184 static const struct internal_subdev *find_intsd_by_grp_id(u32 grp_id)
185 {
186         enum isd_enum i;
187
188         for (i = 0; i < num_isd; i++) {
189                 const struct internal_subdev *isd = &int_subdev[i];
190
191                 if (isd->id->grp_id == grp_id)
192                         return isd;
193         }
194
195         return NULL;
196 }
197
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)
201 {
202         char sink_devname[32];
203         int ipu_id;
204
205         /*
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.
210          */
211         if (sscanf(src->sd->name, "ipu%d", &ipu_id) != 1)
212                 return NULL;
213
214         isd_to_devname(sink_devname, sizeof(sink_devname),
215                        link->remote, ipu_id - 1);
216
217         return imx_media_find_async_subdev(imxmd, NULL, sink_devname);
218 }
219
220 static int create_ipu_internal_link(struct imx_media_dev *imxmd,
221                                     struct imx_media_subdev *src,
222                                     const struct internal_link *link)
223 {
224         struct imx_media_subdev *sink;
225         int ret;
226
227         sink = find_sink(imxmd, src, link);
228         if (!sink)
229                 return -ENODEV;
230
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);
234
235         ret = media_create_pad_link(&src->sd->entity, link->local_pad,
236                                     &sink->sd->entity, link->remote_pad, 0);
237         if (ret)
238                 v4l2_err(&imxmd->v4l2_dev,
239                          "create_pad_link failed: %d\n", ret);
240
241         return ret;
242 }
243
244 int imx_media_create_internal_links(struct imx_media_dev *imxmd,
245                                     struct imx_media_subdev *imxsd)
246 {
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;
252         int i, j, ret;
253
254         intsd = find_intsd_by_grp_id(imxsd->sd->grp_id);
255         if (!intsd)
256                 return -ENODEV;
257
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];
262
263                 if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
264                         continue;
265
266                 for (j = 0; ; j++) {
267                         link = &intpad->link[j];
268
269                         if (!link->remote)
270                                 break;
271
272                         ret = create_ipu_internal_link(imxmd, imxsd, link);
273                         if (ret)
274                                 return ret;
275                 }
276         }
277
278         return 0;
279 }
280
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,
284                                int ipu_id)
285 {
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;
290
291         pdata.grp_id = isd->id->grp_id;
292
293         /* the id of IPU this subdev will control */
294         pdata.ipu_id = ipu_id;
295
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);
299
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);
306
307         pdev = platform_device_register_full(&pdevinfo);
308         if (IS_ERR(pdev))
309                 return PTR_ERR(pdev);
310
311         imxsd = imx_media_add_async_subdev(imxmd, NULL, pdev);
312         if (IS_ERR(imxsd))
313                 return PTR_ERR(imxsd);
314
315         imxsd->num_sink_pads = isd->num_sink_pads;
316         imxsd->num_src_pads = isd->num_src_pads;
317
318         return 0;
319 }
320
321 /* adds the internal subdevs in one ipu */
322 static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id)
323 {
324         enum isd_enum i;
325
326         for (i = 0; i < num_isd; i++) {
327                 const struct internal_subdev *isd = &int_subdev[i];
328                 int ret;
329
330                 /*
331                  * the CSIs are represented in the device-tree, so those
332                  * devices are already added to the async subdev list by
333                  * of_parse_subdev().
334                  */
335                 switch (isd->id->grp_id) {
336                 case IMX_MEDIA_GRP_ID_CSI0:
337                 case IMX_MEDIA_GRP_ID_CSI1:
338                         ret = 0;
339                         break;
340                 default:
341                         ret = add_internal_subdev(imxmd, isd, ipu_id);
342                         break;
343                 }
344
345                 if (ret)
346                         return ret;
347         }
348
349         return 0;
350 }
351
352 int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd)
353 {
354         int ret;
355
356         ret = add_ipu_internal_subdevs(imxmd, 0);
357         if (ret)
358                 goto remove;
359
360         ret = add_ipu_internal_subdevs(imxmd, 1);
361         if (ret)
362                 goto remove;
363
364         return 0;
365
366 remove:
367         imx_media_remove_internal_subdevs(imxmd);
368         return ret;
369 }
370
371 void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd)
372 {
373         struct imx_media_subdev *imxsd;
374         int i;
375
376         for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) {
377                 imxsd = &imxmd->subdev[i];
378                 if (!imxsd->pdev)
379                         continue;
380                 platform_device_unregister(imxsd->pdev);
381         }
382 }