.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
---|
1 | 2 | /* |
---|
2 | 3 | * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC |
---|
3 | 4 | * |
---|
4 | | - * Copyright (c) 2016 Mentor Graphics Inc. |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
| 5 | + * Copyright (c) 2016-2019 Mentor Graphics Inc. |
---|
10 | 6 | */ |
---|
11 | | -#include <linux/delay.h> |
---|
12 | 7 | #include <linux/fs.h> |
---|
13 | 8 | #include <linux/module.h> |
---|
14 | | -#include <linux/of_graph.h> |
---|
15 | | -#include <linux/of_platform.h> |
---|
16 | | -#include <linux/pinctrl/consumer.h> |
---|
17 | 9 | #include <linux/platform_device.h> |
---|
18 | | -#include <linux/sched.h> |
---|
19 | | -#include <linux/slab.h> |
---|
20 | | -#include <linux/spinlock.h> |
---|
21 | | -#include <linux/timer.h> |
---|
22 | | -#include <media/v4l2-ctrls.h> |
---|
| 10 | +#include <media/v4l2-async.h> |
---|
23 | 11 | #include <media/v4l2-event.h> |
---|
24 | | -#include <media/v4l2-ioctl.h> |
---|
25 | | -#include <media/v4l2-mc.h> |
---|
26 | | -#include <video/imx-ipu-v3.h> |
---|
27 | 12 | #include <media/imx.h> |
---|
28 | 13 | #include "imx-media.h" |
---|
29 | 14 | |
---|
30 | 15 | static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) |
---|
31 | 16 | { |
---|
32 | | - return container_of(n, struct imx_media_dev, subdev_notifier); |
---|
33 | | -} |
---|
34 | | - |
---|
35 | | -/* |
---|
36 | | - * Find an asd by fwnode or device name. This is called during |
---|
37 | | - * driver load to form the async subdev list and bind them. |
---|
38 | | - */ |
---|
39 | | -static struct v4l2_async_subdev * |
---|
40 | | -find_async_subdev(struct imx_media_dev *imxmd, |
---|
41 | | - struct fwnode_handle *fwnode, |
---|
42 | | - const char *devname) |
---|
43 | | -{ |
---|
44 | | - struct imx_media_async_subdev *imxasd; |
---|
45 | | - struct v4l2_async_subdev *asd; |
---|
46 | | - |
---|
47 | | - list_for_each_entry(imxasd, &imxmd->asd_list, list) { |
---|
48 | | - asd = &imxasd->asd; |
---|
49 | | - switch (asd->match_type) { |
---|
50 | | - case V4L2_ASYNC_MATCH_FWNODE: |
---|
51 | | - if (fwnode && asd->match.fwnode == fwnode) |
---|
52 | | - return asd; |
---|
53 | | - break; |
---|
54 | | - case V4L2_ASYNC_MATCH_DEVNAME: |
---|
55 | | - if (devname && !strcmp(asd->match.device_name, |
---|
56 | | - devname)) |
---|
57 | | - return asd; |
---|
58 | | - break; |
---|
59 | | - default: |
---|
60 | | - break; |
---|
61 | | - } |
---|
62 | | - } |
---|
63 | | - |
---|
64 | | - return NULL; |
---|
65 | | -} |
---|
66 | | - |
---|
67 | | - |
---|
68 | | -/* |
---|
69 | | - * Adds a subdev to the async subdev list. If fwnode is non-NULL, adds |
---|
70 | | - * the async as a V4L2_ASYNC_MATCH_FWNODE match type, otherwise as |
---|
71 | | - * a V4L2_ASYNC_MATCH_DEVNAME match type using the dev_name of the |
---|
72 | | - * given platform_device. This is called during driver load when |
---|
73 | | - * forming the async subdev list. |
---|
74 | | - */ |
---|
75 | | -int imx_media_add_async_subdev(struct imx_media_dev *imxmd, |
---|
76 | | - struct fwnode_handle *fwnode, |
---|
77 | | - struct platform_device *pdev) |
---|
78 | | -{ |
---|
79 | | - struct device_node *np = to_of_node(fwnode); |
---|
80 | | - struct imx_media_async_subdev *imxasd; |
---|
81 | | - struct v4l2_async_subdev *asd; |
---|
82 | | - const char *devname = NULL; |
---|
83 | | - int ret = 0; |
---|
84 | | - |
---|
85 | | - mutex_lock(&imxmd->mutex); |
---|
86 | | - |
---|
87 | | - if (pdev) |
---|
88 | | - devname = dev_name(&pdev->dev); |
---|
89 | | - |
---|
90 | | - /* return -EEXIST if this asd already added */ |
---|
91 | | - if (find_async_subdev(imxmd, fwnode, devname)) { |
---|
92 | | - dev_dbg(imxmd->md.dev, "%s: already added %s\n", |
---|
93 | | - __func__, np ? np->name : devname); |
---|
94 | | - ret = -EEXIST; |
---|
95 | | - goto out; |
---|
96 | | - } |
---|
97 | | - |
---|
98 | | - imxasd = devm_kzalloc(imxmd->md.dev, sizeof(*imxasd), GFP_KERNEL); |
---|
99 | | - if (!imxasd) { |
---|
100 | | - ret = -ENOMEM; |
---|
101 | | - goto out; |
---|
102 | | - } |
---|
103 | | - asd = &imxasd->asd; |
---|
104 | | - |
---|
105 | | - if (fwnode) { |
---|
106 | | - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; |
---|
107 | | - asd->match.fwnode = fwnode; |
---|
108 | | - } else { |
---|
109 | | - asd->match_type = V4L2_ASYNC_MATCH_DEVNAME; |
---|
110 | | - asd->match.device_name = devname; |
---|
111 | | - imxasd->pdev = pdev; |
---|
112 | | - } |
---|
113 | | - |
---|
114 | | - list_add_tail(&imxasd->list, &imxmd->asd_list); |
---|
115 | | - |
---|
116 | | - imxmd->subdev_notifier.num_subdevs++; |
---|
117 | | - |
---|
118 | | - dev_dbg(imxmd->md.dev, "%s: added %s, match type %s\n", |
---|
119 | | - __func__, np ? np->name : devname, np ? "FWNODE" : "DEVNAME"); |
---|
120 | | - |
---|
121 | | -out: |
---|
122 | | - mutex_unlock(&imxmd->mutex); |
---|
123 | | - return ret; |
---|
124 | | -} |
---|
125 | | - |
---|
126 | | -/* |
---|
127 | | - * get IPU from this CSI and add it to the list of IPUs |
---|
128 | | - * the media driver will control. |
---|
129 | | - */ |
---|
130 | | -static int imx_media_get_ipu(struct imx_media_dev *imxmd, |
---|
131 | | - struct v4l2_subdev *csi_sd) |
---|
132 | | -{ |
---|
133 | | - struct ipu_soc *ipu; |
---|
134 | | - int ipu_id; |
---|
135 | | - |
---|
136 | | - ipu = dev_get_drvdata(csi_sd->dev->parent); |
---|
137 | | - if (!ipu) { |
---|
138 | | - v4l2_err(&imxmd->v4l2_dev, |
---|
139 | | - "CSI %s has no parent IPU!\n", csi_sd->name); |
---|
140 | | - return -ENODEV; |
---|
141 | | - } |
---|
142 | | - |
---|
143 | | - ipu_id = ipu_get_num(ipu); |
---|
144 | | - if (ipu_id > 1) { |
---|
145 | | - v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id); |
---|
146 | | - return -ENODEV; |
---|
147 | | - } |
---|
148 | | - |
---|
149 | | - if (!imxmd->ipu[ipu_id]) |
---|
150 | | - imxmd->ipu[ipu_id] = ipu; |
---|
151 | | - |
---|
152 | | - return 0; |
---|
| 17 | + return container_of(n, struct imx_media_dev, notifier); |
---|
153 | 18 | } |
---|
154 | 19 | |
---|
155 | 20 | /* async subdev bound notifier */ |
---|
.. | .. |
---|
158 | 23 | struct v4l2_async_subdev *asd) |
---|
159 | 24 | { |
---|
160 | 25 | struct imx_media_dev *imxmd = notifier2dev(notifier); |
---|
161 | | - int ret = 0; |
---|
162 | | - |
---|
163 | | - mutex_lock(&imxmd->mutex); |
---|
164 | | - |
---|
165 | | - if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) { |
---|
166 | | - ret = imx_media_get_ipu(imxmd, sd); |
---|
167 | | - if (ret) |
---|
168 | | - goto out; |
---|
169 | | - } |
---|
170 | | - |
---|
171 | | - v4l2_info(&imxmd->v4l2_dev, "subdev %s bound\n", sd->name); |
---|
172 | | -out: |
---|
173 | | - mutex_unlock(&imxmd->mutex); |
---|
174 | | - return ret; |
---|
175 | | -} |
---|
176 | | - |
---|
177 | | -/* |
---|
178 | | - * create the media links for all subdevs that registered async. |
---|
179 | | - * Called after all async subdevs have bound. |
---|
180 | | - */ |
---|
181 | | -static int imx_media_create_links(struct v4l2_async_notifier *notifier) |
---|
182 | | -{ |
---|
183 | | - struct imx_media_dev *imxmd = notifier2dev(notifier); |
---|
184 | | - struct v4l2_subdev *sd; |
---|
185 | 26 | int ret; |
---|
186 | 27 | |
---|
187 | | - /* |
---|
188 | | - * Only links are created between subdevices that are known |
---|
189 | | - * to the async notifier. If there are other non-async subdevices, |
---|
190 | | - * they were created internally by some subdevice (smiapp is one |
---|
191 | | - * example). In those cases it is expected the subdevice is |
---|
192 | | - * responsible for creating those internal links. |
---|
193 | | - */ |
---|
194 | | - list_for_each_entry(sd, ¬ifier->done, async_list) { |
---|
195 | | - switch (sd->grp_id) { |
---|
196 | | - case IMX_MEDIA_GRP_ID_VDIC: |
---|
197 | | - case IMX_MEDIA_GRP_ID_IC_PRP: |
---|
198 | | - case IMX_MEDIA_GRP_ID_IC_PRPENC: |
---|
199 | | - case IMX_MEDIA_GRP_ID_IC_PRPVF: |
---|
200 | | - case IMX_MEDIA_GRP_ID_CSI0: |
---|
201 | | - case IMX_MEDIA_GRP_ID_CSI1: |
---|
202 | | - ret = imx_media_create_internal_links(imxmd, sd); |
---|
203 | | - if (ret) |
---|
204 | | - return ret; |
---|
205 | | - /* |
---|
206 | | - * the CSIs straddle between the external and the IPU |
---|
207 | | - * internal entities, so create the external links |
---|
208 | | - * to the CSI sink pads. |
---|
209 | | - */ |
---|
210 | | - if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) |
---|
211 | | - imx_media_create_csi_of_links(imxmd, sd); |
---|
212 | | - break; |
---|
213 | | - default: |
---|
214 | | - /* this is an external fwnode subdev */ |
---|
215 | | - imx_media_create_of_links(imxmd, sd); |
---|
216 | | - break; |
---|
217 | | - } |
---|
218 | | - } |
---|
219 | | - |
---|
220 | | - return 0; |
---|
221 | | -} |
---|
222 | | - |
---|
223 | | -/* |
---|
224 | | - * adds given video device to given imx-media source pad vdev list. |
---|
225 | | - * Continues upstream from the pad entity's sink pads. |
---|
226 | | - */ |
---|
227 | | -static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd, |
---|
228 | | - struct imx_media_video_dev *vdev, |
---|
229 | | - struct media_pad *srcpad) |
---|
230 | | -{ |
---|
231 | | - struct media_entity *entity = srcpad->entity; |
---|
232 | | - struct imx_media_pad_vdev *pad_vdev; |
---|
233 | | - struct list_head *pad_vdev_list; |
---|
234 | | - struct media_link *link; |
---|
235 | | - struct v4l2_subdev *sd; |
---|
236 | | - int i, ret; |
---|
237 | | - |
---|
238 | | - /* skip this entity if not a v4l2_subdev */ |
---|
239 | | - if (!is_media_entity_v4l2_subdev(entity)) |
---|
240 | | - return 0; |
---|
241 | | - |
---|
242 | | - sd = media_entity_to_v4l2_subdev(entity); |
---|
243 | | - |
---|
244 | | - pad_vdev_list = to_pad_vdev_list(sd, srcpad->index); |
---|
245 | | - if (!pad_vdev_list) { |
---|
246 | | - v4l2_warn(&imxmd->v4l2_dev, "%s:%u has no vdev list!\n", |
---|
247 | | - entity->name, srcpad->index); |
---|
248 | | - /* |
---|
249 | | - * shouldn't happen, but no reason to fail driver load, |
---|
250 | | - * just skip this entity. |
---|
251 | | - */ |
---|
252 | | - return 0; |
---|
253 | | - } |
---|
254 | | - |
---|
255 | | - /* just return if we've been here before */ |
---|
256 | | - list_for_each_entry(pad_vdev, pad_vdev_list, list) { |
---|
257 | | - if (pad_vdev->vdev == vdev) |
---|
258 | | - return 0; |
---|
259 | | - } |
---|
260 | | - |
---|
261 | | - dev_dbg(imxmd->md.dev, "adding %s to pad %s:%u\n", |
---|
262 | | - vdev->vfd->entity.name, entity->name, srcpad->index); |
---|
263 | | - |
---|
264 | | - pad_vdev = devm_kzalloc(imxmd->md.dev, sizeof(*pad_vdev), GFP_KERNEL); |
---|
265 | | - if (!pad_vdev) |
---|
266 | | - return -ENOMEM; |
---|
267 | | - |
---|
268 | | - /* attach this vdev to this pad */ |
---|
269 | | - pad_vdev->vdev = vdev; |
---|
270 | | - list_add_tail(&pad_vdev->list, pad_vdev_list); |
---|
271 | | - |
---|
272 | | - /* move upstream from this entity's sink pads */ |
---|
273 | | - for (i = 0; i < entity->num_pads; i++) { |
---|
274 | | - struct media_pad *pad = &entity->pads[i]; |
---|
275 | | - |
---|
276 | | - if (!(pad->flags & MEDIA_PAD_FL_SINK)) |
---|
277 | | - continue; |
---|
278 | | - |
---|
279 | | - list_for_each_entry(link, &entity->links, list) { |
---|
280 | | - if (link->sink != pad) |
---|
281 | | - continue; |
---|
282 | | - ret = imx_media_add_vdev_to_pad(imxmd, vdev, |
---|
283 | | - link->source); |
---|
284 | | - if (ret) |
---|
285 | | - return ret; |
---|
286 | | - } |
---|
287 | | - } |
---|
288 | | - |
---|
289 | | - return 0; |
---|
290 | | -} |
---|
291 | | - |
---|
292 | | -/* |
---|
293 | | - * For every subdevice, allocate an array of list_head's, one list_head |
---|
294 | | - * for each pad, to hold the list of video devices reachable from that |
---|
295 | | - * pad. |
---|
296 | | - */ |
---|
297 | | -static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev *imxmd) |
---|
298 | | -{ |
---|
299 | | - struct list_head *vdev_lists; |
---|
300 | | - struct media_entity *entity; |
---|
301 | | - struct v4l2_subdev *sd; |
---|
302 | | - int i; |
---|
303 | | - |
---|
304 | | - list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { |
---|
305 | | - entity = &sd->entity; |
---|
306 | | - vdev_lists = devm_kcalloc( |
---|
307 | | - imxmd->md.dev, |
---|
308 | | - entity->num_pads, sizeof(*vdev_lists), |
---|
309 | | - GFP_KERNEL); |
---|
310 | | - if (!vdev_lists) |
---|
311 | | - return -ENOMEM; |
---|
312 | | - |
---|
313 | | - /* attach to the subdev's host private pointer */ |
---|
314 | | - sd->host_priv = vdev_lists; |
---|
315 | | - |
---|
316 | | - for (i = 0; i < entity->num_pads; i++) |
---|
317 | | - INIT_LIST_HEAD(to_pad_vdev_list(sd, i)); |
---|
318 | | - } |
---|
319 | | - |
---|
320 | | - return 0; |
---|
321 | | -} |
---|
322 | | - |
---|
323 | | -/* form the vdev lists in all imx-media source pads */ |
---|
324 | | -static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) |
---|
325 | | -{ |
---|
326 | | - struct imx_media_video_dev *vdev; |
---|
327 | | - struct media_link *link; |
---|
328 | | - int ret; |
---|
329 | | - |
---|
330 | | - ret = imx_media_alloc_pad_vdev_lists(imxmd); |
---|
331 | | - if (ret) |
---|
332 | | - return ret; |
---|
333 | | - |
---|
334 | | - list_for_each_entry(vdev, &imxmd->vdev_list, list) { |
---|
335 | | - link = list_first_entry(&vdev->vfd->entity.links, |
---|
336 | | - struct media_link, list); |
---|
337 | | - ret = imx_media_add_vdev_to_pad(imxmd, vdev, link->source); |
---|
| 28 | + if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) { |
---|
| 29 | + /* register the IPU internal subdevs */ |
---|
| 30 | + ret = imx_media_register_ipu_internal_subdevs(imxmd, sd); |
---|
338 | 31 | if (ret) |
---|
339 | 32 | return ret; |
---|
340 | 33 | } |
---|
| 34 | + |
---|
| 35 | + dev_dbg(imxmd->md.dev, "subdev %s bound\n", sd->name); |
---|
341 | 36 | |
---|
342 | 37 | return 0; |
---|
343 | 38 | } |
---|
344 | 39 | |
---|
345 | 40 | /* async subdev complete notifier */ |
---|
346 | | -static int imx_media_probe_complete(struct v4l2_async_notifier *notifier) |
---|
| 41 | +static int imx6_media_probe_complete(struct v4l2_async_notifier *notifier) |
---|
347 | 42 | { |
---|
348 | 43 | struct imx_media_dev *imxmd = notifier2dev(notifier); |
---|
349 | 44 | int ret; |
---|
350 | 45 | |
---|
| 46 | + /* call the imx5/6/7 common probe completion handler */ |
---|
| 47 | + ret = imx_media_probe_complete(notifier); |
---|
| 48 | + if (ret) |
---|
| 49 | + return ret; |
---|
| 50 | + |
---|
351 | 51 | mutex_lock(&imxmd->mutex); |
---|
352 | 52 | |
---|
353 | | - ret = imx_media_create_links(notifier); |
---|
354 | | - if (ret) |
---|
| 53 | + imxmd->m2m_vdev = imx_media_csc_scaler_device_init(imxmd); |
---|
| 54 | + if (IS_ERR(imxmd->m2m_vdev)) { |
---|
| 55 | + ret = PTR_ERR(imxmd->m2m_vdev); |
---|
| 56 | + imxmd->m2m_vdev = NULL; |
---|
355 | 57 | goto unlock; |
---|
| 58 | + } |
---|
356 | 59 | |
---|
357 | | - ret = imx_media_create_pad_vdev_lists(imxmd); |
---|
358 | | - if (ret) |
---|
359 | | - goto unlock; |
---|
360 | | - |
---|
361 | | - ret = v4l2_device_register_subdev_nodes(&imxmd->v4l2_dev); |
---|
| 60 | + ret = imx_media_csc_scaler_device_register(imxmd->m2m_vdev); |
---|
362 | 61 | unlock: |
---|
363 | 62 | mutex_unlock(&imxmd->mutex); |
---|
364 | | - if (ret) |
---|
365 | | - return ret; |
---|
366 | | - |
---|
367 | | - return media_device_register(&imxmd->md); |
---|
| 63 | + return ret; |
---|
368 | 64 | } |
---|
369 | 65 | |
---|
370 | | -static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { |
---|
| 66 | +/* async subdev complete notifier */ |
---|
| 67 | +static const struct v4l2_async_notifier_operations imx_media_notifier_ops = { |
---|
371 | 68 | .bound = imx_media_subdev_bound, |
---|
372 | | - .complete = imx_media_probe_complete, |
---|
373 | | -}; |
---|
374 | | - |
---|
375 | | -/* |
---|
376 | | - * adds controls to a video device from an entity subdevice. |
---|
377 | | - * Continues upstream from the entity's sink pads. |
---|
378 | | - */ |
---|
379 | | -static int imx_media_inherit_controls(struct imx_media_dev *imxmd, |
---|
380 | | - struct video_device *vfd, |
---|
381 | | - struct media_entity *entity) |
---|
382 | | -{ |
---|
383 | | - int i, ret = 0; |
---|
384 | | - |
---|
385 | | - if (is_media_entity_v4l2_subdev(entity)) { |
---|
386 | | - struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); |
---|
387 | | - |
---|
388 | | - dev_dbg(imxmd->md.dev, |
---|
389 | | - "adding controls to %s from %s\n", |
---|
390 | | - vfd->entity.name, sd->entity.name); |
---|
391 | | - |
---|
392 | | - ret = v4l2_ctrl_add_handler(vfd->ctrl_handler, |
---|
393 | | - sd->ctrl_handler, |
---|
394 | | - NULL); |
---|
395 | | - if (ret) |
---|
396 | | - return ret; |
---|
397 | | - } |
---|
398 | | - |
---|
399 | | - /* move upstream */ |
---|
400 | | - for (i = 0; i < entity->num_pads; i++) { |
---|
401 | | - struct media_pad *pad, *spad = &entity->pads[i]; |
---|
402 | | - |
---|
403 | | - if (!(spad->flags & MEDIA_PAD_FL_SINK)) |
---|
404 | | - continue; |
---|
405 | | - |
---|
406 | | - pad = media_entity_remote_pad(spad); |
---|
407 | | - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) |
---|
408 | | - continue; |
---|
409 | | - |
---|
410 | | - ret = imx_media_inherit_controls(imxmd, vfd, pad->entity); |
---|
411 | | - if (ret) |
---|
412 | | - break; |
---|
413 | | - } |
---|
414 | | - |
---|
415 | | - return ret; |
---|
416 | | -} |
---|
417 | | - |
---|
418 | | -static int imx_media_link_notify(struct media_link *link, u32 flags, |
---|
419 | | - unsigned int notification) |
---|
420 | | -{ |
---|
421 | | - struct media_entity *source = link->source->entity; |
---|
422 | | - struct imx_media_pad_vdev *pad_vdev; |
---|
423 | | - struct list_head *pad_vdev_list; |
---|
424 | | - struct imx_media_dev *imxmd; |
---|
425 | | - struct video_device *vfd; |
---|
426 | | - struct v4l2_subdev *sd; |
---|
427 | | - int pad_idx, ret; |
---|
428 | | - |
---|
429 | | - ret = v4l2_pipeline_link_notify(link, flags, notification); |
---|
430 | | - if (ret) |
---|
431 | | - return ret; |
---|
432 | | - |
---|
433 | | - /* don't bother if source is not a subdev */ |
---|
434 | | - if (!is_media_entity_v4l2_subdev(source)) |
---|
435 | | - return 0; |
---|
436 | | - |
---|
437 | | - sd = media_entity_to_v4l2_subdev(source); |
---|
438 | | - pad_idx = link->source->index; |
---|
439 | | - |
---|
440 | | - imxmd = dev_get_drvdata(sd->v4l2_dev->dev); |
---|
441 | | - |
---|
442 | | - pad_vdev_list = to_pad_vdev_list(sd, pad_idx); |
---|
443 | | - if (!pad_vdev_list) { |
---|
444 | | - /* shouldn't happen, but no reason to fail link setup */ |
---|
445 | | - return 0; |
---|
446 | | - } |
---|
447 | | - |
---|
448 | | - /* |
---|
449 | | - * Before disabling a link, reset controls for all video |
---|
450 | | - * devices reachable from this link. |
---|
451 | | - * |
---|
452 | | - * After enabling a link, refresh controls for all video |
---|
453 | | - * devices reachable from this link. |
---|
454 | | - */ |
---|
455 | | - if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && |
---|
456 | | - !(flags & MEDIA_LNK_FL_ENABLED)) { |
---|
457 | | - list_for_each_entry(pad_vdev, pad_vdev_list, list) { |
---|
458 | | - vfd = pad_vdev->vdev->vfd; |
---|
459 | | - dev_dbg(imxmd->md.dev, |
---|
460 | | - "reset controls for %s\n", |
---|
461 | | - vfd->entity.name); |
---|
462 | | - v4l2_ctrl_handler_free(vfd->ctrl_handler); |
---|
463 | | - v4l2_ctrl_handler_init(vfd->ctrl_handler, 0); |
---|
464 | | - } |
---|
465 | | - } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && |
---|
466 | | - (link->flags & MEDIA_LNK_FL_ENABLED)) { |
---|
467 | | - list_for_each_entry(pad_vdev, pad_vdev_list, list) { |
---|
468 | | - vfd = pad_vdev->vdev->vfd; |
---|
469 | | - dev_dbg(imxmd->md.dev, |
---|
470 | | - "refresh controls for %s\n", |
---|
471 | | - vfd->entity.name); |
---|
472 | | - ret = imx_media_inherit_controls(imxmd, vfd, |
---|
473 | | - &vfd->entity); |
---|
474 | | - if (ret) |
---|
475 | | - break; |
---|
476 | | - } |
---|
477 | | - } |
---|
478 | | - |
---|
479 | | - return ret; |
---|
480 | | -} |
---|
481 | | - |
---|
482 | | -static const struct media_device_ops imx_media_md_ops = { |
---|
483 | | - .link_notify = imx_media_link_notify, |
---|
| 69 | + .complete = imx6_media_probe_complete, |
---|
484 | 70 | }; |
---|
485 | 71 | |
---|
486 | 72 | static int imx_media_probe(struct platform_device *pdev) |
---|
487 | 73 | { |
---|
488 | 74 | struct device *dev = &pdev->dev; |
---|
489 | 75 | struct device_node *node = dev->of_node; |
---|
490 | | - struct imx_media_async_subdev *imxasd; |
---|
491 | | - struct v4l2_async_subdev **subdevs; |
---|
492 | 76 | struct imx_media_dev *imxmd; |
---|
493 | | - int num_subdevs, i, ret; |
---|
| 77 | + int ret; |
---|
494 | 78 | |
---|
495 | | - imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); |
---|
496 | | - if (!imxmd) |
---|
497 | | - return -ENOMEM; |
---|
498 | | - |
---|
499 | | - dev_set_drvdata(dev, imxmd); |
---|
500 | | - |
---|
501 | | - strlcpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model)); |
---|
502 | | - imxmd->md.ops = &imx_media_md_ops; |
---|
503 | | - imxmd->md.dev = dev; |
---|
504 | | - |
---|
505 | | - mutex_init(&imxmd->mutex); |
---|
506 | | - |
---|
507 | | - imxmd->v4l2_dev.mdev = &imxmd->md; |
---|
508 | | - strlcpy(imxmd->v4l2_dev.name, "imx-media", |
---|
509 | | - sizeof(imxmd->v4l2_dev.name)); |
---|
510 | | - |
---|
511 | | - media_device_init(&imxmd->md); |
---|
512 | | - |
---|
513 | | - ret = v4l2_device_register(dev, &imxmd->v4l2_dev); |
---|
514 | | - if (ret < 0) { |
---|
515 | | - v4l2_err(&imxmd->v4l2_dev, |
---|
516 | | - "Failed to register v4l2_device: %d\n", ret); |
---|
517 | | - goto cleanup; |
---|
518 | | - } |
---|
519 | | - |
---|
520 | | - dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd); |
---|
521 | | - |
---|
522 | | - INIT_LIST_HEAD(&imxmd->asd_list); |
---|
523 | | - INIT_LIST_HEAD(&imxmd->vdev_list); |
---|
| 79 | + imxmd = imx_media_dev_init(dev, NULL); |
---|
| 80 | + if (IS_ERR(imxmd)) |
---|
| 81 | + return PTR_ERR(imxmd); |
---|
524 | 82 | |
---|
525 | 83 | ret = imx_media_add_of_subdevs(imxmd, node); |
---|
526 | 84 | if (ret) { |
---|
527 | 85 | v4l2_err(&imxmd->v4l2_dev, |
---|
528 | 86 | "add_of_subdevs failed with %d\n", ret); |
---|
529 | | - goto unreg_dev; |
---|
| 87 | + goto cleanup; |
---|
530 | 88 | } |
---|
531 | 89 | |
---|
532 | | - ret = imx_media_add_internal_subdevs(imxmd); |
---|
533 | | - if (ret) { |
---|
534 | | - v4l2_err(&imxmd->v4l2_dev, |
---|
535 | | - "add_internal_subdevs failed with %d\n", ret); |
---|
536 | | - goto unreg_dev; |
---|
537 | | - } |
---|
538 | | - |
---|
539 | | - num_subdevs = imxmd->subdev_notifier.num_subdevs; |
---|
540 | | - |
---|
541 | | - /* no subdevs? just bail */ |
---|
542 | | - if (num_subdevs == 0) { |
---|
543 | | - ret = -ENODEV; |
---|
544 | | - goto unreg_dev; |
---|
545 | | - } |
---|
546 | | - |
---|
547 | | - subdevs = devm_kcalloc(imxmd->md.dev, num_subdevs, sizeof(*subdevs), |
---|
548 | | - GFP_KERNEL); |
---|
549 | | - if (!subdevs) { |
---|
550 | | - ret = -ENOMEM; |
---|
551 | | - goto unreg_dev; |
---|
552 | | - } |
---|
553 | | - |
---|
554 | | - i = 0; |
---|
555 | | - list_for_each_entry(imxasd, &imxmd->asd_list, list) |
---|
556 | | - subdevs[i++] = &imxasd->asd; |
---|
557 | | - |
---|
558 | | - /* prepare the async subdev notifier and register it */ |
---|
559 | | - imxmd->subdev_notifier.subdevs = subdevs; |
---|
560 | | - imxmd->subdev_notifier.ops = &imx_media_subdev_ops; |
---|
561 | | - ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, |
---|
562 | | - &imxmd->subdev_notifier); |
---|
563 | | - if (ret) { |
---|
564 | | - v4l2_err(&imxmd->v4l2_dev, |
---|
565 | | - "v4l2_async_notifier_register failed with %d\n", ret); |
---|
566 | | - goto del_int; |
---|
567 | | - } |
---|
| 90 | + ret = imx_media_dev_notifier_register(imxmd, &imx_media_notifier_ops); |
---|
| 91 | + if (ret) |
---|
| 92 | + goto cleanup; |
---|
568 | 93 | |
---|
569 | 94 | return 0; |
---|
570 | 95 | |
---|
571 | | -del_int: |
---|
572 | | - imx_media_remove_internal_subdevs(imxmd); |
---|
573 | | -unreg_dev: |
---|
574 | | - v4l2_device_unregister(&imxmd->v4l2_dev); |
---|
575 | 96 | cleanup: |
---|
| 97 | + v4l2_async_notifier_cleanup(&imxmd->notifier); |
---|
| 98 | + v4l2_device_unregister(&imxmd->v4l2_dev); |
---|
576 | 99 | media_device_cleanup(&imxmd->md); |
---|
| 100 | + |
---|
577 | 101 | return ret; |
---|
578 | 102 | } |
---|
579 | 103 | |
---|
.. | .. |
---|
584 | 108 | |
---|
585 | 109 | v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n"); |
---|
586 | 110 | |
---|
587 | | - v4l2_async_notifier_unregister(&imxmd->subdev_notifier); |
---|
588 | | - imx_media_remove_internal_subdevs(imxmd); |
---|
589 | | - v4l2_device_unregister(&imxmd->v4l2_dev); |
---|
| 111 | + if (imxmd->m2m_vdev) { |
---|
| 112 | + imx_media_csc_scaler_device_unregister(imxmd->m2m_vdev); |
---|
| 113 | + imxmd->m2m_vdev = NULL; |
---|
| 114 | + } |
---|
| 115 | + |
---|
| 116 | + v4l2_async_notifier_unregister(&imxmd->notifier); |
---|
| 117 | + imx_media_unregister_ipu_internal_subdevs(imxmd); |
---|
| 118 | + v4l2_async_notifier_cleanup(&imxmd->notifier); |
---|
590 | 119 | media_device_unregister(&imxmd->md); |
---|
| 120 | + v4l2_device_unregister(&imxmd->v4l2_dev); |
---|
591 | 121 | media_device_cleanup(&imxmd->md); |
---|
592 | 122 | |
---|
593 | 123 | return 0; |
---|