GNU Linux-libre 4.14.251-gnu1
[releases.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_link {
64         const struct internal_subdev_id *remote_id;
65         int remote_pad;
66 };
67
68 struct internal_pad {
69         bool devnode; /* does this pad link to a device node */
70         struct internal_link link[IMX_MEDIA_MAX_LINKS];
71 };
72
73 static const struct internal_subdev {
74         const struct internal_subdev_id *id;
75         struct internal_pad pad[IMX_MEDIA_MAX_PADS];
76         int num_sink_pads;
77         int num_src_pads;
78 } internal_subdev[num_isd] = {
79         [isd_csi0] = {
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] = {
84                         .link = {
85                                 {
86                                         .remote_id = &isd_id[isd_ic_prp],
87                                         .remote_pad = PRP_SINK_PAD,
88                                 }, {
89                                         .remote_id =  &isd_id[isd_vdic],
90                                         .remote_pad = VDIC_SINK_PAD_DIRECT,
91                                 },
92                         },
93                 },
94                 .pad[CSI_SRC_PAD_IDMAC] = {
95                         .devnode = true,
96                 },
97         },
98
99         [isd_csi1] = {
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] = {
104                         .link = {
105                                 {
106                                         .remote_id = &isd_id[isd_ic_prp],
107                                         .remote_pad = PRP_SINK_PAD,
108                                 }, {
109                                         .remote_id =  &isd_id[isd_vdic],
110                                         .remote_pad = VDIC_SINK_PAD_DIRECT,
111                                 },
112                         },
113                 },
114                 .pad[CSI_SRC_PAD_IDMAC] = {
115                         .devnode = true,
116                 },
117         },
118
119         [isd_vdic] = {
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] = {
124                         .devnode = true,
125                 },
126                 .pad[VDIC_SRC_PAD_DIRECT] = {
127                         .link = {
128                                 {
129                                         .remote_id =  &isd_id[isd_ic_prp],
130                                         .remote_pad = PRP_SINK_PAD,
131                                 },
132                         },
133                 },
134         },
135
136         [isd_ic_prp] = {
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] = {
141                         .link = {
142                                 {
143                                         .remote_id = &isd_id[isd_ic_prpenc],
144                                         .remote_pad = 0,
145                                 },
146                         },
147                 },
148                 .pad[PRP_SRC_PAD_PRPVF] = {
149                         .link = {
150                                 {
151                                         .remote_id = &isd_id[isd_ic_prpvf],
152                                         .remote_pad = 0,
153                                 },
154                         },
155                 },
156         },
157
158         [isd_ic_prpenc] = {
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] = {
163                         .devnode = true,
164                 },
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                 .pad[PRPENCVF_SRC_PAD] = {
172                         .devnode = true,
173                 },
174         },
175 };
176
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,
180                                      int ipu_id)
181 {
182         int pdev_id = ipu_id * num_isd + id->index;
183
184         snprintf(devname, sz, "%s.%d", id->name, pdev_id);
185 }
186
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,
191                               int ipu_id)
192 {
193         int i, num_pads, ret;
194
195         num_pads = isd->num_sink_pads + isd->num_src_pads;
196
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];
200                 int j;
201
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;
207
208                 for (j = 0; ; j++) {
209                         const struct internal_link *link;
210                         char remote_devname[32];
211
212                         link = &intpad->link[j];
213
214                         if (!link->remote_id)
215                                 break;
216
217                         isd_id_to_devname(remote_devname,
218                                           sizeof(remote_devname),
219                                           link->remote_id, ipu_id);
220
221                         ret = imx_media_add_pad_link(imxmd, pad,
222                                                      NULL, remote_devname,
223                                                      i, link->remote_pad);
224                         if (ret)
225                                 return ret;
226                 }
227         }
228
229         return 0;
230 }
231
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,
236                     int ipu_id)
237 {
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;
242
243         pdata.grp_id = isd->id->grp_id;
244
245         /* the id of IPU this subdev will control */
246         pdata.ipu_id = ipu_id;
247
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);
251
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);
258
259         pdev = platform_device_register_full(&pdevinfo);
260         if (IS_ERR(pdev))
261                 return ERR_CAST(pdev);
262
263         imxsd = imx_media_add_async_subdev(imxmd, NULL, pdev);
264         if (IS_ERR(imxsd))
265                 return imxsd;
266
267         imxsd->num_sink_pads = isd->num_sink_pads;
268         imxsd->num_src_pads = isd->num_src_pads;
269
270         return imxsd;
271 }
272
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,
277                                     int ipu_id)
278 {
279         enum isd_enum i;
280         int ret;
281
282         for (i = 0; i < num_isd; i++) {
283                 const struct internal_subdev *isd = &internal_subdev[i];
284                 struct imx_media_subdev *imxsd;
285
286                 /*
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.
291                  */
292                 switch (isd->id->grp_id) {
293                 case IMX_MEDIA_GRP_ID_CSI0:
294                         imxsd = csi0;
295                         break;
296                 case IMX_MEDIA_GRP_ID_CSI1:
297                         imxsd = csi1;
298                         break;
299                 default:
300                         imxsd = add_internal_subdev(imxmd, isd, ipu_id);
301                         break;
302                 }
303
304                 if (IS_ERR(imxsd))
305                         return PTR_ERR(imxsd);
306
307                 /* add the links from this subdev */
308                 if (imxsd) {
309                         ret = add_internal_links(imxmd, isd, imxsd, ipu_id);
310                         if (ret)
311                                 return ret;
312                 }
313         }
314
315         return 0;
316 }
317
318 int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd,
319                                    struct imx_media_subdev *csi[4])
320 {
321         int ret;
322
323         ret = add_ipu_internal_subdevs(imxmd, csi[0], csi[1], 0);
324         if (ret)
325                 goto remove;
326
327         ret = add_ipu_internal_subdevs(imxmd, csi[2], csi[3], 1);
328         if (ret)
329                 goto remove;
330
331         return 0;
332
333 remove:
334         imx_media_remove_internal_subdevs(imxmd);
335         return ret;
336 }
337
338 void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd)
339 {
340         struct imx_media_subdev *imxsd;
341         int i;
342
343         for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) {
344                 imxsd = &imxmd->subdev[i];
345                 if (!imxsd->pdev)
346                         continue;
347                 platform_device_unregister(imxsd->pdev);
348         }
349 }