GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / staging / media / imx / imx-media-internal-sd.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Media driver for Freescale i.MX5/6 SOC
4  *
5  * Adds the IPU internal subdevices and the media links between them.
6  *
7  * Copyright (c) 2016 Mentor Graphics Inc.
8  */
9 #include <linux/platform_device.h>
10 #include "imx-media.h"
11
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
16
17 struct internal_subdev;
18
19 struct internal_link {
20         int remote;
21         int local_pad;
22         int remote_pad;
23 };
24
25 struct internal_pad {
26         int num_links;
27         struct internal_link link[MAX_INTERNAL_LINKS];
28 };
29
30 struct internal_subdev {
31         u32 grp_id;
32         struct internal_pad pad[MAX_INTERNAL_PADS];
33
34         struct v4l2_subdev * (*sync_register)(struct v4l2_device *v4l2_dev,
35                                               struct device *ipu_dev,
36                                               struct ipu_soc *ipu,
37                                               u32 grp_id);
38         int (*sync_unregister)(struct v4l2_subdev *sd);
39 };
40
41 static const struct internal_subdev int_subdev[NUM_IPU_SUBDEVS] = {
42         [IPU_CSI0] = {
43                 .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0,
44                 .pad[CSI_SRC_PAD_DIRECT] = {
45                         .num_links = 2,
46                         .link = {
47                                 {
48                                         .local_pad = CSI_SRC_PAD_DIRECT,
49                                         .remote = IPU_IC_PRP,
50                                         .remote_pad = PRP_SINK_PAD,
51                                 }, {
52                                         .local_pad = CSI_SRC_PAD_DIRECT,
53                                         .remote = IPU_VDIC,
54                                         .remote_pad = VDIC_SINK_PAD_DIRECT,
55                                 },
56                         },
57                 },
58         },
59
60         [IPU_CSI1] = {
61                 .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1,
62                 .pad[CSI_SRC_PAD_DIRECT] = {
63                         .num_links = 2,
64                         .link = {
65                                 {
66                                         .local_pad = CSI_SRC_PAD_DIRECT,
67                                         .remote = IPU_IC_PRP,
68                                         .remote_pad = PRP_SINK_PAD,
69                                 }, {
70                                         .local_pad = CSI_SRC_PAD_DIRECT,
71                                         .remote = IPU_VDIC,
72                                         .remote_pad = VDIC_SINK_PAD_DIRECT,
73                                 },
74                         },
75                 },
76         },
77
78         [IPU_VDIC] = {
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] = {
83                         .num_links = 1,
84                         .link = {
85                                 {
86                                         .local_pad = VDIC_SRC_PAD_DIRECT,
87                                         .remote = IPU_IC_PRP,
88                                         .remote_pad = PRP_SINK_PAD,
89                                 },
90                         },
91                 },
92         },
93
94         [IPU_IC_PRP] = {
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] = {
99                         .num_links = 1,
100                         .link = {
101                                 {
102                                         .local_pad = PRP_SRC_PAD_PRPENC,
103                                         .remote = IPU_IC_PRPENC,
104                                         .remote_pad = PRPENCVF_SINK_PAD,
105                                 },
106                         },
107                 },
108                 .pad[PRP_SRC_PAD_PRPVF] = {
109                         .num_links = 1,
110                         .link = {
111                                 {
112                                         .local_pad = PRP_SRC_PAD_PRPVF,
113                                         .remote = IPU_IC_PRPVF,
114                                         .remote_pad = PRPENCVF_SINK_PAD,
115                                 },
116                         },
117                 },
118         },
119
120         [IPU_IC_PRPENC] = {
121                 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC,
122                 .sync_register = imx_media_ic_register,
123                 .sync_unregister = imx_media_ic_unregister,
124         },
125
126         [IPU_IC_PRPVF] = {
127                 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF,
128                 .sync_register = imx_media_ic_register,
129                 .sync_unregister = imx_media_ic_unregister,
130         },
131 };
132
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)
137 {
138         int ret;
139
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]))
143                 return 0;
144
145         dev_dbg(imxmd->md.dev, "%s:%d -> %s:%d\n",
146                 src->name, link->local_pad,
147                 sink->name, link->remote_pad);
148
149         ret = media_create_pad_link(&src->entity, link->local_pad,
150                                     &sink->entity, link->remote_pad, 0);
151         if (ret)
152                 v4l2_err(&imxmd->v4l2_dev, "%s failed: %d\n", __func__, ret);
153
154         return ret;
155 }
156
157 static int create_ipu_internal_links(struct imx_media_dev *imxmd,
158                                      const struct internal_subdev *intsd,
159                                      struct v4l2_subdev *sd,
160                                      int ipu_id)
161 {
162         const struct internal_pad *intpad;
163         const struct internal_link *link;
164         struct media_pad *pad;
165         int i, j, ret;
166
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];
171
172                 if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
173                         continue;
174
175                 for (j = 0; j < intpad->num_links; j++) {
176                         struct v4l2_subdev *sink;
177
178                         link = &intpad->link[j];
179                         sink = imxmd->sync_sd[ipu_id][link->remote];
180
181                         ret = create_internal_link(imxmd, sd, sink, link);
182                         if (ret)
183                                 return ret;
184                 }
185         }
186
187         return 0;
188 }
189
190 int imx_media_register_ipu_internal_subdevs(struct imx_media_dev *imxmd,
191                                             struct v4l2_subdev *csi)
192 {
193         struct device *ipu_dev = csi->dev->parent;
194         const struct internal_subdev *intsd;
195         struct v4l2_subdev *sd;
196         struct ipu_soc *ipu;
197         int i, ipu_id, ret;
198
199         ipu = dev_get_drvdata(ipu_dev);
200         if (!ipu) {
201                 v4l2_err(&imxmd->v4l2_dev, "invalid IPU device!\n");
202                 return -ENODEV;
203         }
204
205         ipu_id = ipu_get_num(ipu);
206         if (ipu_id > 1) {
207                 v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id);
208                 return -ENODEV;
209         }
210
211         mutex_lock(&imxmd->mutex);
212
213         /* record this IPU */
214         if (!imxmd->ipu[ipu_id])
215                 imxmd->ipu[ipu_id] = ipu;
216
217         /* register the synchronous subdevs */
218         for (i = 0; i < NUM_IPU_SUBDEVS; i++) {
219                 intsd = &int_subdev[i];
220
221                 sd = imxmd->sync_sd[ipu_id][i];
222
223                 /*
224                  * skip if this sync subdev already registered or its
225                  * not a sync subdev (one of the CSIs)
226                  */
227                 if (sd || !intsd->sync_register)
228                         continue;
229
230                 mutex_unlock(&imxmd->mutex);
231                 sd = intsd->sync_register(&imxmd->v4l2_dev, ipu_dev, ipu,
232                                           intsd->grp_id);
233                 mutex_lock(&imxmd->mutex);
234                 if (IS_ERR(sd)) {
235                         ret = PTR_ERR(sd);
236                         goto err_unwind;
237                 }
238
239                 imxmd->sync_sd[ipu_id][i] = sd;
240         }
241
242         /*
243          * all the sync subdevs are registered, create the media links
244          * between them.
245          */
246         for (i = 0; i < NUM_IPU_SUBDEVS; i++) {
247                 intsd = &int_subdev[i];
248
249                 if (intsd->grp_id == csi->grp_id) {
250                         sd = csi;
251                 } else {
252                         sd = imxmd->sync_sd[ipu_id][i];
253                         if (!sd)
254                                 continue;
255                 }
256
257                 ret = create_ipu_internal_links(imxmd, intsd, sd, ipu_id);
258                 if (ret) {
259                         mutex_unlock(&imxmd->mutex);
260                         imx_media_unregister_ipu_internal_subdevs(imxmd);
261                         return ret;
262                 }
263         }
264
265         mutex_unlock(&imxmd->mutex);
266         return 0;
267
268 err_unwind:
269         while (--i >= 0) {
270                 intsd = &int_subdev[i];
271                 sd = imxmd->sync_sd[ipu_id][i];
272                 if (!sd || !intsd->sync_unregister)
273                         continue;
274                 mutex_unlock(&imxmd->mutex);
275                 intsd->sync_unregister(sd);
276                 mutex_lock(&imxmd->mutex);
277         }
278
279         mutex_unlock(&imxmd->mutex);
280         return ret;
281 }
282
283 void imx_media_unregister_ipu_internal_subdevs(struct imx_media_dev *imxmd)
284 {
285         const struct internal_subdev *intsd;
286         struct v4l2_subdev *sd;
287         int i, j;
288
289         mutex_lock(&imxmd->mutex);
290
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];
295
296                         if (!sd || !intsd->sync_unregister)
297                                 continue;
298
299                         mutex_unlock(&imxmd->mutex);
300                         intsd->sync_unregister(sd);
301                         mutex_lock(&imxmd->mutex);
302                 }
303         }
304
305         mutex_unlock(&imxmd->mutex);
306 }