1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Driver for Renesas R-Car VIN |
4 | * |
5 | * Copyright (C) 2016 Renesas Electronics Corp. |
6 | * Copyright (C) 2011-2013 Renesas Solutions Corp. |
7 | * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> |
8 | * Copyright (C) 2008 Magnus Damm |
9 | * |
10 | * Based on the soc-camera rcar_vin driver |
11 | */ |
12 | |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/of_graph.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/pm_runtime.h> |
18 | #include <linux/slab.h> |
19 | |
20 | #include <media/v4l2-async.h> |
21 | #include <media/v4l2-fwnode.h> |
22 | #include <media/v4l2-mc.h> |
23 | |
24 | #include "rcar-vin.h" |
25 | |
26 | /* |
27 | * The companion CSI-2 receiver driver (rcar-csi2) is known |
28 | * and we know it has one source pad (pad 0) and four sink |
29 | * pads (pad 1-4). So to translate a pad on the remote |
30 | * CSI-2 receiver to/from the VIN internal channel number simply |
31 | * subtract/add one from the pad/channel number. |
32 | */ |
33 | #define rvin_group_csi_pad_to_channel(pad) ((pad) - 1) |
34 | #define rvin_group_csi_channel_to_pad(channel) ((channel) + 1) |
35 | |
36 | /* |
37 | * Not all VINs are created equal, master VINs control the |
38 | * routing for other VIN's. We can figure out which VIN is |
39 | * master by looking at a VINs id. |
40 | */ |
41 | #define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4) |
42 | |
43 | #define v4l2_dev_to_vin(d) container_of(d, struct rvin_dev, v4l2_dev) |
44 | |
45 | /* ----------------------------------------------------------------------------- |
46 | * Gen3 Group Allocator |
47 | */ |
48 | |
49 | /* FIXME: This should if we find a system that supports more |
50 | * than one group for the whole system be replaced with a linked |
51 | * list of groups. And eventually all of this should be replaced |
52 | * with a global device allocator API. |
53 | * |
54 | * But for now this works as on all supported systems there will |
55 | * be only one group for all instances. |
56 | */ |
57 | |
58 | static DEFINE_MUTEX(rvin_group_lock); |
59 | static struct rvin_group *rvin_group_data; |
60 | |
61 | static void rvin_group_cleanup(struct rvin_group *group) |
62 | { |
63 | media_device_cleanup(mdev: &group->mdev); |
64 | mutex_destroy(lock: &group->lock); |
65 | } |
66 | |
67 | static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin, |
68 | int (*link_setup)(struct rvin_dev *), |
69 | const struct media_device_ops *ops) |
70 | { |
71 | struct media_device *mdev = &group->mdev; |
72 | const struct of_device_id *match; |
73 | struct device_node *np; |
74 | |
75 | mutex_init(&group->lock); |
76 | |
77 | /* Count number of VINs in the system */ |
78 | group->count = 0; |
79 | for_each_matching_node(np, vin->dev->driver->of_match_table) |
80 | if (of_device_is_available(device: np)) |
81 | group->count++; |
82 | |
83 | vin_dbg(vin, "found %u enabled VIN's in DT" , group->count); |
84 | |
85 | group->link_setup = link_setup; |
86 | |
87 | mdev->dev = vin->dev; |
88 | mdev->ops = ops; |
89 | |
90 | match = of_match_node(matches: vin->dev->driver->of_match_table, |
91 | node: vin->dev->of_node); |
92 | |
93 | strscpy(mdev->driver_name, KBUILD_MODNAME, sizeof(mdev->driver_name)); |
94 | strscpy(mdev->model, match->compatible, sizeof(mdev->model)); |
95 | |
96 | media_device_init(mdev); |
97 | |
98 | return 0; |
99 | } |
100 | |
101 | static void rvin_group_release(struct kref *kref) |
102 | { |
103 | struct rvin_group *group = |
104 | container_of(kref, struct rvin_group, refcount); |
105 | |
106 | mutex_lock(&rvin_group_lock); |
107 | |
108 | rvin_group_data = NULL; |
109 | |
110 | rvin_group_cleanup(group); |
111 | |
112 | kfree(objp: group); |
113 | |
114 | mutex_unlock(lock: &rvin_group_lock); |
115 | } |
116 | |
117 | static int rvin_group_get(struct rvin_dev *vin, |
118 | int (*link_setup)(struct rvin_dev *), |
119 | const struct media_device_ops *ops) |
120 | { |
121 | struct rvin_group *group; |
122 | u32 id; |
123 | int ret; |
124 | |
125 | /* Make sure VIN id is present and sane */ |
126 | ret = of_property_read_u32(np: vin->dev->of_node, propname: "renesas,id" , out_value: &id); |
127 | if (ret) { |
128 | vin_err(vin, "%pOF: No renesas,id property found\n" , |
129 | vin->dev->of_node); |
130 | return -EINVAL; |
131 | } |
132 | |
133 | if (id >= RCAR_VIN_NUM) { |
134 | vin_err(vin, "%pOF: Invalid renesas,id '%u'\n" , |
135 | vin->dev->of_node, id); |
136 | return -EINVAL; |
137 | } |
138 | |
139 | /* Join or create a VIN group */ |
140 | mutex_lock(&rvin_group_lock); |
141 | if (rvin_group_data) { |
142 | group = rvin_group_data; |
143 | kref_get(kref: &group->refcount); |
144 | } else { |
145 | group = kzalloc(size: sizeof(*group), GFP_KERNEL); |
146 | if (!group) { |
147 | ret = -ENOMEM; |
148 | goto err_group; |
149 | } |
150 | |
151 | ret = rvin_group_init(group, vin, link_setup, ops); |
152 | if (ret) { |
153 | kfree(objp: group); |
154 | vin_err(vin, "Failed to initialize group\n" ); |
155 | goto err_group; |
156 | } |
157 | |
158 | kref_init(kref: &group->refcount); |
159 | |
160 | rvin_group_data = group; |
161 | } |
162 | mutex_unlock(lock: &rvin_group_lock); |
163 | |
164 | /* Add VIN to group */ |
165 | mutex_lock(&group->lock); |
166 | |
167 | if (group->vin[id]) { |
168 | vin_err(vin, "Duplicate renesas,id property value %u\n" , id); |
169 | mutex_unlock(lock: &group->lock); |
170 | kref_put(kref: &group->refcount, release: rvin_group_release); |
171 | return -EINVAL; |
172 | } |
173 | |
174 | group->vin[id] = vin; |
175 | |
176 | vin->id = id; |
177 | vin->group = group; |
178 | vin->v4l2_dev.mdev = &group->mdev; |
179 | |
180 | mutex_unlock(lock: &group->lock); |
181 | |
182 | return 0; |
183 | err_group: |
184 | mutex_unlock(lock: &rvin_group_lock); |
185 | return ret; |
186 | } |
187 | |
188 | static void rvin_group_put(struct rvin_dev *vin) |
189 | { |
190 | struct rvin_group *group = vin->group; |
191 | |
192 | mutex_lock(&group->lock); |
193 | |
194 | vin->group = NULL; |
195 | vin->v4l2_dev.mdev = NULL; |
196 | |
197 | if (WARN_ON(group->vin[vin->id] != vin)) |
198 | goto out; |
199 | |
200 | group->vin[vin->id] = NULL; |
201 | out: |
202 | mutex_unlock(lock: &group->lock); |
203 | |
204 | kref_put(kref: &group->refcount, release: rvin_group_release); |
205 | } |
206 | |
207 | /* group lock should be held when calling this function. */ |
208 | static int rvin_group_entity_to_remote_id(struct rvin_group *group, |
209 | struct media_entity *entity) |
210 | { |
211 | struct v4l2_subdev *sd; |
212 | unsigned int i; |
213 | |
214 | sd = media_entity_to_v4l2_subdev(entity); |
215 | |
216 | for (i = 0; i < RVIN_REMOTES_MAX; i++) |
217 | if (group->remotes[i].subdev == sd) |
218 | return i; |
219 | |
220 | return -ENODEV; |
221 | } |
222 | |
223 | static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) |
224 | { |
225 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
226 | unsigned int i; |
227 | int ret; |
228 | |
229 | ret = media_device_register(&vin->group->mdev); |
230 | if (ret) |
231 | return ret; |
232 | |
233 | ret = v4l2_device_register_subdev_nodes(v4l2_dev: &vin->v4l2_dev); |
234 | if (ret) { |
235 | vin_err(vin, "Failed to register subdev nodes\n" ); |
236 | return ret; |
237 | } |
238 | |
239 | /* Register all video nodes for the group. */ |
240 | for (i = 0; i < RCAR_VIN_NUM; i++) { |
241 | if (vin->group->vin[i] && |
242 | !video_is_registered(vdev: &vin->group->vin[i]->vdev)) { |
243 | ret = rvin_v4l2_register(vin: vin->group->vin[i]); |
244 | if (ret) |
245 | return ret; |
246 | } |
247 | } |
248 | |
249 | return vin->group->link_setup(vin); |
250 | } |
251 | |
252 | static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, |
253 | struct v4l2_subdev *subdev, |
254 | struct v4l2_async_connection *asc) |
255 | { |
256 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
257 | unsigned int i; |
258 | |
259 | for (i = 0; i < RCAR_VIN_NUM; i++) |
260 | if (vin->group->vin[i]) |
261 | rvin_v4l2_unregister(vin: vin->group->vin[i]); |
262 | |
263 | mutex_lock(&vin->group->lock); |
264 | |
265 | for (i = 0; i < RVIN_CSI_MAX; i++) { |
266 | if (vin->group->remotes[i].asc != asc) |
267 | continue; |
268 | vin->group->remotes[i].subdev = NULL; |
269 | vin_dbg(vin, "Unbind %s from slot %u\n" , subdev->name, i); |
270 | break; |
271 | } |
272 | |
273 | mutex_unlock(lock: &vin->group->lock); |
274 | |
275 | media_device_unregister(mdev: &vin->group->mdev); |
276 | } |
277 | |
278 | static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, |
279 | struct v4l2_subdev *subdev, |
280 | struct v4l2_async_connection *asc) |
281 | { |
282 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
283 | unsigned int i; |
284 | |
285 | mutex_lock(&vin->group->lock); |
286 | |
287 | for (i = 0; i < RVIN_CSI_MAX; i++) { |
288 | if (vin->group->remotes[i].asc != asc) |
289 | continue; |
290 | vin->group->remotes[i].subdev = subdev; |
291 | vin_dbg(vin, "Bound %s to slot %u\n" , subdev->name, i); |
292 | break; |
293 | } |
294 | |
295 | mutex_unlock(lock: &vin->group->lock); |
296 | |
297 | return 0; |
298 | } |
299 | |
300 | static const struct v4l2_async_notifier_operations rvin_group_notify_ops = { |
301 | .bound = rvin_group_notify_bound, |
302 | .unbind = rvin_group_notify_unbind, |
303 | .complete = rvin_group_notify_complete, |
304 | }; |
305 | |
306 | static int rvin_group_parse_of(struct rvin_dev *vin, unsigned int port, |
307 | unsigned int id) |
308 | { |
309 | struct fwnode_handle *ep, *fwnode; |
310 | struct v4l2_fwnode_endpoint vep = { |
311 | .bus_type = V4L2_MBUS_CSI2_DPHY, |
312 | }; |
313 | struct v4l2_async_connection *asc; |
314 | int ret; |
315 | |
316 | ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), port, endpoint: id, flags: 0); |
317 | if (!ep) |
318 | return 0; |
319 | |
320 | fwnode = fwnode_graph_get_remote_endpoint(fwnode: ep); |
321 | ret = v4l2_fwnode_endpoint_parse(fwnode: ep, vep: &vep); |
322 | fwnode_handle_put(fwnode: ep); |
323 | if (ret) { |
324 | vin_err(vin, "Failed to parse %pOF\n" , to_of_node(fwnode)); |
325 | ret = -EINVAL; |
326 | goto out; |
327 | } |
328 | |
329 | asc = v4l2_async_nf_add_fwnode(&vin->group->notifier, fwnode, |
330 | struct v4l2_async_connection); |
331 | if (IS_ERR(ptr: asc)) { |
332 | ret = PTR_ERR(ptr: asc); |
333 | goto out; |
334 | } |
335 | |
336 | vin->group->remotes[vep.base.id].asc = asc; |
337 | |
338 | vin_dbg(vin, "Add group OF device %pOF to slot %u\n" , |
339 | to_of_node(fwnode), vep.base.id); |
340 | out: |
341 | fwnode_handle_put(fwnode); |
342 | |
343 | return ret; |
344 | } |
345 | |
346 | static void rvin_group_notifier_cleanup(struct rvin_dev *vin) |
347 | { |
348 | if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { |
349 | v4l2_async_nf_unregister(notifier: &vin->group->notifier); |
350 | v4l2_async_nf_cleanup(notifier: &vin->group->notifier); |
351 | } |
352 | } |
353 | |
354 | static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port, |
355 | unsigned int max_id) |
356 | { |
357 | unsigned int count = 0, vin_mask = 0; |
358 | unsigned int i, id; |
359 | int ret; |
360 | |
361 | mutex_lock(&vin->group->lock); |
362 | |
363 | /* If not all VIN's are registered don't register the notifier. */ |
364 | for (i = 0; i < RCAR_VIN_NUM; i++) { |
365 | if (vin->group->vin[i]) { |
366 | count++; |
367 | vin_mask |= BIT(i); |
368 | } |
369 | } |
370 | |
371 | if (vin->group->count != count) { |
372 | mutex_unlock(lock: &vin->group->lock); |
373 | return 0; |
374 | } |
375 | |
376 | mutex_unlock(lock: &vin->group->lock); |
377 | |
378 | v4l2_async_nf_init(notifier: &vin->group->notifier, v4l2_dev: &vin->v4l2_dev); |
379 | |
380 | /* |
381 | * Some subdevices may overlap but the parser function can handle it and |
382 | * each subdevice will only be registered once with the group notifier. |
383 | */ |
384 | for (i = 0; i < RCAR_VIN_NUM; i++) { |
385 | if (!(vin_mask & BIT(i))) |
386 | continue; |
387 | |
388 | for (id = 0; id < max_id; id++) { |
389 | if (vin->group->remotes[id].asc) |
390 | continue; |
391 | |
392 | ret = rvin_group_parse_of(vin: vin->group->vin[i], port, id); |
393 | if (ret) |
394 | return ret; |
395 | } |
396 | } |
397 | |
398 | if (list_empty(head: &vin->group->notifier.waiting_list)) |
399 | return 0; |
400 | |
401 | vin->group->notifier.ops = &rvin_group_notify_ops; |
402 | ret = v4l2_async_nf_register(notifier: &vin->group->notifier); |
403 | if (ret < 0) { |
404 | vin_err(vin, "Notifier registration failed\n" ); |
405 | v4l2_async_nf_cleanup(notifier: &vin->group->notifier); |
406 | return ret; |
407 | } |
408 | |
409 | return 0; |
410 | } |
411 | |
412 | /* ----------------------------------------------------------------------------- |
413 | * Controls |
414 | */ |
415 | |
416 | static int rvin_s_ctrl(struct v4l2_ctrl *ctrl) |
417 | { |
418 | struct rvin_dev *vin = |
419 | container_of(ctrl->handler, struct rvin_dev, ctrl_handler); |
420 | |
421 | switch (ctrl->id) { |
422 | case V4L2_CID_ALPHA_COMPONENT: |
423 | rvin_set_alpha(vin, alpha: ctrl->val); |
424 | break; |
425 | } |
426 | |
427 | return 0; |
428 | } |
429 | |
430 | static const struct v4l2_ctrl_ops rvin_ctrl_ops = { |
431 | .s_ctrl = rvin_s_ctrl, |
432 | }; |
433 | |
434 | static void rvin_free_controls(struct rvin_dev *vin) |
435 | { |
436 | v4l2_ctrl_handler_free(hdl: &vin->ctrl_handler); |
437 | vin->vdev.ctrl_handler = NULL; |
438 | } |
439 | |
440 | static int rvin_create_controls(struct rvin_dev *vin, struct v4l2_subdev *subdev) |
441 | { |
442 | int ret; |
443 | |
444 | ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); |
445 | if (ret < 0) |
446 | return ret; |
447 | |
448 | /* The VIN directly deals with alpha component. */ |
449 | v4l2_ctrl_new_std(hdl: &vin->ctrl_handler, ops: &rvin_ctrl_ops, |
450 | V4L2_CID_ALPHA_COMPONENT, min: 0, max: 255, step: 1, def: 255); |
451 | |
452 | if (vin->ctrl_handler.error) { |
453 | ret = vin->ctrl_handler.error; |
454 | rvin_free_controls(vin); |
455 | return ret; |
456 | } |
457 | |
458 | /* For the non-MC mode add controls from the subdevice. */ |
459 | if (subdev) { |
460 | ret = v4l2_ctrl_add_handler(hdl: &vin->ctrl_handler, |
461 | add: subdev->ctrl_handler, NULL, from_other_dev: true); |
462 | if (ret < 0) { |
463 | rvin_free_controls(vin); |
464 | return ret; |
465 | } |
466 | } |
467 | |
468 | vin->vdev.ctrl_handler = &vin->ctrl_handler; |
469 | |
470 | return 0; |
471 | } |
472 | |
473 | /* ----------------------------------------------------------------------------- |
474 | * Async notifier |
475 | */ |
476 | |
477 | static int rvin_find_pad(struct v4l2_subdev *sd, int direction) |
478 | { |
479 | unsigned int pad; |
480 | |
481 | if (sd->entity.num_pads <= 1) |
482 | return 0; |
483 | |
484 | for (pad = 0; pad < sd->entity.num_pads; pad++) |
485 | if (sd->entity.pads[pad].flags & direction) |
486 | return pad; |
487 | |
488 | return -EINVAL; |
489 | } |
490 | |
491 | /* ----------------------------------------------------------------------------- |
492 | * Parallel async notifier |
493 | */ |
494 | |
495 | /* The vin lock should be held when calling the subdevice attach and detach */ |
496 | static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, |
497 | struct v4l2_subdev *subdev) |
498 | { |
499 | struct v4l2_subdev_mbus_code_enum code = { |
500 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, |
501 | }; |
502 | int ret; |
503 | |
504 | /* Find source and sink pad of remote subdevice */ |
505 | ret = rvin_find_pad(sd: subdev, MEDIA_PAD_FL_SOURCE); |
506 | if (ret < 0) |
507 | return ret; |
508 | vin->parallel.source_pad = ret; |
509 | |
510 | ret = rvin_find_pad(sd: subdev, MEDIA_PAD_FL_SINK); |
511 | vin->parallel.sink_pad = ret < 0 ? 0 : ret; |
512 | |
513 | if (vin->info->use_mc) { |
514 | vin->parallel.subdev = subdev; |
515 | return 0; |
516 | } |
517 | |
518 | /* Find compatible subdevices mbus format */ |
519 | vin->mbus_code = 0; |
520 | code.index = 0; |
521 | code.pad = vin->parallel.source_pad; |
522 | while (!vin->mbus_code && |
523 | !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { |
524 | code.index++; |
525 | switch (code.code) { |
526 | case MEDIA_BUS_FMT_YUYV8_1X16: |
527 | case MEDIA_BUS_FMT_UYVY8_1X16: |
528 | case MEDIA_BUS_FMT_UYVY8_2X8: |
529 | case MEDIA_BUS_FMT_UYVY10_2X10: |
530 | case MEDIA_BUS_FMT_RGB888_1X24: |
531 | vin->mbus_code = code.code; |
532 | vin_dbg(vin, "Found media bus format for %s: %d\n" , |
533 | subdev->name, vin->mbus_code); |
534 | break; |
535 | default: |
536 | break; |
537 | } |
538 | } |
539 | |
540 | if (!vin->mbus_code) { |
541 | vin_err(vin, "Unsupported media bus format for %s\n" , |
542 | subdev->name); |
543 | return -EINVAL; |
544 | } |
545 | |
546 | /* Read tvnorms */ |
547 | ret = v4l2_subdev_call(subdev, video, g_tvnorms, &vin->vdev.tvnorms); |
548 | if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) |
549 | return ret; |
550 | |
551 | /* Read standard */ |
552 | vin->std = V4L2_STD_UNKNOWN; |
553 | ret = v4l2_subdev_call(subdev, video, g_std, &vin->std); |
554 | if (ret < 0 && ret != -ENOIOCTLCMD) |
555 | return ret; |
556 | |
557 | /* Add the controls */ |
558 | ret = rvin_create_controls(vin, subdev); |
559 | if (ret < 0) |
560 | return ret; |
561 | |
562 | vin->parallel.subdev = subdev; |
563 | |
564 | return 0; |
565 | } |
566 | |
567 | static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) |
568 | { |
569 | rvin_v4l2_unregister(vin); |
570 | vin->parallel.subdev = NULL; |
571 | |
572 | if (!vin->info->use_mc) |
573 | rvin_free_controls(vin); |
574 | } |
575 | |
576 | static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) |
577 | { |
578 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
579 | struct media_entity *source; |
580 | struct media_entity *sink; |
581 | int ret; |
582 | |
583 | ret = v4l2_device_register_subdev_nodes(v4l2_dev: &vin->v4l2_dev); |
584 | if (ret < 0) { |
585 | vin_err(vin, "Failed to register subdev nodes\n" ); |
586 | return ret; |
587 | } |
588 | |
589 | if (!video_is_registered(vdev: &vin->vdev)) { |
590 | ret = rvin_v4l2_register(vin); |
591 | if (ret < 0) |
592 | return ret; |
593 | } |
594 | |
595 | if (!vin->info->use_mc) |
596 | return 0; |
597 | |
598 | /* If we're running with media-controller, link the subdevs. */ |
599 | source = &vin->parallel.subdev->entity; |
600 | sink = &vin->vdev.entity; |
601 | |
602 | ret = media_create_pad_link(source, source_pad: vin->parallel.source_pad, |
603 | sink, sink_pad: vin->parallel.sink_pad, flags: 0); |
604 | if (ret) |
605 | vin_err(vin, "Error adding link from %s to %s: %d\n" , |
606 | source->name, sink->name, ret); |
607 | |
608 | return ret; |
609 | } |
610 | |
611 | static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, |
612 | struct v4l2_subdev *subdev, |
613 | struct v4l2_async_connection *asc) |
614 | { |
615 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
616 | |
617 | vin_dbg(vin, "unbind parallel subdev %s\n" , subdev->name); |
618 | |
619 | mutex_lock(&vin->lock); |
620 | rvin_parallel_subdevice_detach(vin); |
621 | mutex_unlock(lock: &vin->lock); |
622 | } |
623 | |
624 | static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, |
625 | struct v4l2_subdev *subdev, |
626 | struct v4l2_async_connection *asc) |
627 | { |
628 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
629 | int ret; |
630 | |
631 | mutex_lock(&vin->lock); |
632 | ret = rvin_parallel_subdevice_attach(vin, subdev); |
633 | mutex_unlock(lock: &vin->lock); |
634 | if (ret) |
635 | return ret; |
636 | |
637 | v4l2_set_subdev_hostdata(sd: subdev, p: vin); |
638 | |
639 | vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n" , |
640 | subdev->name, vin->parallel.source_pad, |
641 | vin->parallel.sink_pad); |
642 | |
643 | return 0; |
644 | } |
645 | |
646 | static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = { |
647 | .bound = rvin_parallel_notify_bound, |
648 | .unbind = rvin_parallel_notify_unbind, |
649 | .complete = rvin_parallel_notify_complete, |
650 | }; |
651 | |
652 | static int rvin_parallel_parse_of(struct rvin_dev *vin) |
653 | { |
654 | struct fwnode_handle *ep, *fwnode; |
655 | struct v4l2_fwnode_endpoint vep = { |
656 | .bus_type = V4L2_MBUS_UNKNOWN, |
657 | }; |
658 | struct v4l2_async_connection *asc; |
659 | int ret; |
660 | |
661 | ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), port: 0, endpoint: 0, flags: 0); |
662 | if (!ep) |
663 | return 0; |
664 | |
665 | fwnode = fwnode_graph_get_remote_endpoint(fwnode: ep); |
666 | ret = v4l2_fwnode_endpoint_parse(fwnode: ep, vep: &vep); |
667 | fwnode_handle_put(fwnode: ep); |
668 | if (ret) { |
669 | vin_err(vin, "Failed to parse %pOF\n" , to_of_node(fwnode)); |
670 | ret = -EINVAL; |
671 | goto out; |
672 | } |
673 | |
674 | switch (vep.bus_type) { |
675 | case V4L2_MBUS_PARALLEL: |
676 | case V4L2_MBUS_BT656: |
677 | vin_dbg(vin, "Found %s media bus\n" , |
678 | vep.bus_type == V4L2_MBUS_PARALLEL ? |
679 | "PARALLEL" : "BT656" ); |
680 | vin->parallel.mbus_type = vep.bus_type; |
681 | vin->parallel.bus = vep.bus.parallel; |
682 | break; |
683 | default: |
684 | vin_err(vin, "Unknown media bus type\n" ); |
685 | ret = -EINVAL; |
686 | goto out; |
687 | } |
688 | |
689 | asc = v4l2_async_nf_add_fwnode(&vin->notifier, fwnode, |
690 | struct v4l2_async_connection); |
691 | if (IS_ERR(ptr: asc)) { |
692 | ret = PTR_ERR(ptr: asc); |
693 | goto out; |
694 | } |
695 | |
696 | vin->parallel.asc = asc; |
697 | |
698 | vin_dbg(vin, "Add parallel OF device %pOF\n" , to_of_node(fwnode)); |
699 | out: |
700 | fwnode_handle_put(fwnode); |
701 | |
702 | return ret; |
703 | } |
704 | |
705 | static void rvin_parallel_cleanup(struct rvin_dev *vin) |
706 | { |
707 | v4l2_async_nf_unregister(notifier: &vin->notifier); |
708 | v4l2_async_nf_cleanup(notifier: &vin->notifier); |
709 | } |
710 | |
711 | static int rvin_parallel_init(struct rvin_dev *vin) |
712 | { |
713 | int ret; |
714 | |
715 | v4l2_async_nf_init(notifier: &vin->notifier, v4l2_dev: &vin->v4l2_dev); |
716 | |
717 | ret = rvin_parallel_parse_of(vin); |
718 | if (ret) |
719 | return ret; |
720 | |
721 | if (!vin->parallel.asc) |
722 | return -ENODEV; |
723 | |
724 | vin_dbg(vin, "Found parallel subdevice %pOF\n" , |
725 | to_of_node(vin->parallel.asc->match.fwnode)); |
726 | |
727 | vin->notifier.ops = &rvin_parallel_notify_ops; |
728 | ret = v4l2_async_nf_register(notifier: &vin->notifier); |
729 | if (ret < 0) { |
730 | vin_err(vin, "Notifier registration failed\n" ); |
731 | v4l2_async_nf_cleanup(notifier: &vin->notifier); |
732 | return ret; |
733 | } |
734 | |
735 | return 0; |
736 | } |
737 | |
738 | /* ----------------------------------------------------------------------------- |
739 | * CSI-2 |
740 | */ |
741 | |
742 | /* |
743 | * Link setup for the links between a VIN and a CSI-2 receiver is a bit |
744 | * complex. The reason for this is that the register controlling routing |
745 | * is not present in each VIN instance. There are special VINs which |
746 | * control routing for themselves and other VINs. There are not many |
747 | * different possible links combinations that can be enabled at the same |
748 | * time, therefor all already enabled links which are controlled by a |
749 | * master VIN need to be taken into account when making the decision |
750 | * if a new link can be enabled or not. |
751 | * |
752 | * 1. Find out which VIN the link the user tries to enable is connected to. |
753 | * 2. Lookup which master VIN controls the links for this VIN. |
754 | * 3. Start with a bitmask with all bits set. |
755 | * 4. For each previously enabled link from the master VIN bitwise AND its |
756 | * route mask (see documentation for mask in struct rvin_group_route) |
757 | * with the bitmask. |
758 | * 5. Bitwise AND the mask for the link the user tries to enable to the bitmask. |
759 | * 6. If the bitmask is not empty at this point the new link can be enabled |
760 | * while keeping all previous links enabled. Update the CHSEL value of the |
761 | * master VIN and inform the user that the link could be enabled. |
762 | * |
763 | * Please note that no link can be enabled if any VIN in the group is |
764 | * currently open. |
765 | */ |
766 | static int rvin_csi2_link_notify(struct media_link *link, u32 flags, |
767 | unsigned int notification) |
768 | { |
769 | struct rvin_group *group = container_of(link->graph_obj.mdev, |
770 | struct rvin_group, mdev); |
771 | struct media_entity *entity; |
772 | struct video_device *vdev; |
773 | struct rvin_dev *vin; |
774 | unsigned int i; |
775 | int csi_id, ret; |
776 | |
777 | ret = v4l2_pipeline_link_notify(link, flags, notification); |
778 | if (ret) |
779 | return ret; |
780 | |
781 | /* Only care about link enablement for VIN nodes. */ |
782 | if (!(flags & MEDIA_LNK_FL_ENABLED) || |
783 | !is_media_entity_v4l2_video_device(entity: link->sink->entity)) |
784 | return 0; |
785 | |
786 | /* |
787 | * Don't allow link changes if any stream in the graph is active as |
788 | * modifying the CHSEL register fields can disrupt running streams. |
789 | */ |
790 | media_device_for_each_entity(entity, &group->mdev) |
791 | if (media_entity_is_streaming(entity)) |
792 | return -EBUSY; |
793 | |
794 | /* Find the master VIN that controls the routes. */ |
795 | vdev = media_entity_to_video_device(link->sink->entity); |
796 | vin = container_of(vdev, struct rvin_dev, vdev); |
797 | |
798 | mutex_lock(&group->lock); |
799 | |
800 | csi_id = rvin_group_entity_to_remote_id(group, entity: link->source->entity); |
801 | if (csi_id == -ENODEV) { |
802 | struct v4l2_subdev *sd; |
803 | |
804 | /* |
805 | * Make sure the source entity subdevice is registered as |
806 | * a parallel input of one of the enabled VINs if it is not |
807 | * one of the CSI-2 subdevices. |
808 | * |
809 | * No hardware configuration required for parallel inputs, |
810 | * we can return here. |
811 | */ |
812 | sd = media_entity_to_v4l2_subdev(link->source->entity); |
813 | for (i = 0; i < RCAR_VIN_NUM; i++) { |
814 | if (group->vin[i] && |
815 | group->vin[i]->parallel.subdev == sd) { |
816 | group->vin[i]->is_csi = false; |
817 | ret = 0; |
818 | goto out; |
819 | } |
820 | } |
821 | |
822 | vin_err(vin, "Subdevice %s not registered to any VIN\n" , |
823 | link->source->entity->name); |
824 | ret = -ENODEV; |
825 | } else { |
826 | const struct rvin_group_route *route; |
827 | unsigned int chsel = UINT_MAX; |
828 | unsigned int master_id; |
829 | |
830 | master_id = rvin_group_id_to_master(vin->id); |
831 | |
832 | if (WARN_ON(!group->vin[master_id])) { |
833 | ret = -ENODEV; |
834 | goto out; |
835 | } |
836 | |
837 | /* Make sure group is connected to same CSI-2 */ |
838 | for (i = master_id; i < master_id + 4; i++) { |
839 | struct media_pad *csi_pad; |
840 | |
841 | if (!group->vin[i]) |
842 | continue; |
843 | |
844 | /* Get remote CSI-2, if any. */ |
845 | csi_pad = media_pad_remote_pad_first( |
846 | pad: &group->vin[i]->vdev.entity.pads[0]); |
847 | if (!csi_pad) |
848 | continue; |
849 | |
850 | if (csi_pad->entity != link->source->entity) { |
851 | vin_dbg(vin, "Already attached to %s\n" , |
852 | csi_pad->entity->name); |
853 | ret = -EBUSY; |
854 | goto out; |
855 | } |
856 | } |
857 | |
858 | for (route = vin->info->routes; route->chsel; route++) { |
859 | if (route->master == master_id && route->csi == csi_id) { |
860 | chsel = route->chsel; |
861 | break; |
862 | } |
863 | } |
864 | |
865 | if (chsel == UINT_MAX) { |
866 | vin_err(vin, "No CHSEL value found\n" ); |
867 | ret = -EINVAL; |
868 | goto out; |
869 | } |
870 | |
871 | ret = rvin_set_channel_routing(vin: group->vin[master_id], chsel); |
872 | if (ret) |
873 | goto out; |
874 | |
875 | vin->is_csi = true; |
876 | } |
877 | out: |
878 | mutex_unlock(lock: &group->lock); |
879 | |
880 | return ret; |
881 | } |
882 | |
883 | static const struct media_device_ops rvin_csi2_media_ops = { |
884 | .link_notify = rvin_csi2_link_notify, |
885 | }; |
886 | |
887 | static int rvin_csi2_create_link(struct rvin_group *group, unsigned int id, |
888 | const struct rvin_group_route *route) |
889 | { |
890 | struct media_entity *source = &group->remotes[route->csi].subdev->entity; |
891 | struct media_entity *sink = &group->vin[id]->vdev.entity; |
892 | struct media_pad *sink_pad = &sink->pads[0]; |
893 | unsigned int channel; |
894 | int ret; |
895 | |
896 | for (channel = 0; channel < 4; channel++) { |
897 | unsigned int source_idx = rvin_group_csi_channel_to_pad(channel); |
898 | struct media_pad *source_pad = &source->pads[source_idx]; |
899 | |
900 | /* Skip if link already exists. */ |
901 | if (media_entity_find_link(source: source_pad, sink: sink_pad)) |
902 | continue; |
903 | |
904 | ret = media_create_pad_link(source, source_pad: source_idx, sink, sink_pad: 0, flags: 0); |
905 | if (ret) |
906 | return ret; |
907 | } |
908 | |
909 | return 0; |
910 | } |
911 | |
912 | static int rvin_csi2_setup_links(struct rvin_dev *vin) |
913 | { |
914 | const struct rvin_group_route *route; |
915 | unsigned int id; |
916 | int ret = -EINVAL; |
917 | |
918 | /* Create all media device links between VINs and CSI-2's. */ |
919 | mutex_lock(&vin->group->lock); |
920 | for (route = vin->info->routes; route->chsel; route++) { |
921 | /* Check that VIN' master is part of the group. */ |
922 | if (!vin->group->vin[route->master]) |
923 | continue; |
924 | |
925 | /* Check that CSI-2 is part of the group. */ |
926 | if (!vin->group->remotes[route->csi].subdev) |
927 | continue; |
928 | |
929 | for (id = route->master; id < route->master + 4; id++) { |
930 | /* Check that VIN is part of the group. */ |
931 | if (!vin->group->vin[id]) |
932 | continue; |
933 | |
934 | ret = rvin_csi2_create_link(group: vin->group, id, route); |
935 | if (ret) |
936 | goto out; |
937 | } |
938 | } |
939 | out: |
940 | mutex_unlock(lock: &vin->group->lock); |
941 | |
942 | return ret; |
943 | } |
944 | |
945 | static void rvin_csi2_cleanup(struct rvin_dev *vin) |
946 | { |
947 | rvin_parallel_cleanup(vin); |
948 | rvin_group_notifier_cleanup(vin); |
949 | rvin_group_put(vin); |
950 | rvin_free_controls(vin); |
951 | } |
952 | |
953 | static int rvin_csi2_init(struct rvin_dev *vin) |
954 | { |
955 | int ret; |
956 | |
957 | vin->pad.flags = MEDIA_PAD_FL_SINK; |
958 | ret = media_entity_pads_init(entity: &vin->vdev.entity, num_pads: 1, pads: &vin->pad); |
959 | if (ret) |
960 | return ret; |
961 | |
962 | ret = rvin_create_controls(vin, NULL); |
963 | if (ret < 0) |
964 | return ret; |
965 | |
966 | ret = rvin_group_get(vin, link_setup: rvin_csi2_setup_links, ops: &rvin_csi2_media_ops); |
967 | if (ret) |
968 | goto err_controls; |
969 | |
970 | /* It's OK to not have a parallel subdevice. */ |
971 | ret = rvin_parallel_init(vin); |
972 | if (ret && ret != -ENODEV) |
973 | goto err_group; |
974 | |
975 | ret = rvin_group_notifier_init(vin, port: 1, max_id: RVIN_CSI_MAX); |
976 | if (ret) |
977 | goto err_parallel; |
978 | |
979 | return 0; |
980 | err_parallel: |
981 | rvin_parallel_cleanup(vin); |
982 | err_group: |
983 | rvin_group_put(vin); |
984 | err_controls: |
985 | rvin_free_controls(vin); |
986 | |
987 | return ret; |
988 | } |
989 | |
990 | /* ----------------------------------------------------------------------------- |
991 | * ISP |
992 | */ |
993 | |
994 | static int rvin_isp_setup_links(struct rvin_dev *vin) |
995 | { |
996 | unsigned int i; |
997 | int ret = -EINVAL; |
998 | |
999 | /* Create all media device links between VINs and ISP's. */ |
1000 | mutex_lock(&vin->group->lock); |
1001 | for (i = 0; i < RCAR_VIN_NUM; i++) { |
1002 | struct media_pad *source_pad, *sink_pad; |
1003 | struct media_entity *source, *sink; |
1004 | unsigned int source_slot = i / 8; |
1005 | unsigned int source_idx = i % 8 + 1; |
1006 | |
1007 | if (!vin->group->vin[i]) |
1008 | continue; |
1009 | |
1010 | /* Check that ISP is part of the group. */ |
1011 | if (!vin->group->remotes[source_slot].subdev) |
1012 | continue; |
1013 | |
1014 | source = &vin->group->remotes[source_slot].subdev->entity; |
1015 | source_pad = &source->pads[source_idx]; |
1016 | |
1017 | sink = &vin->group->vin[i]->vdev.entity; |
1018 | sink_pad = &sink->pads[0]; |
1019 | |
1020 | /* Skip if link already exists. */ |
1021 | if (media_entity_find_link(source: source_pad, sink: sink_pad)) |
1022 | continue; |
1023 | |
1024 | ret = media_create_pad_link(source, source_pad: source_idx, sink, sink_pad: 0, |
1025 | MEDIA_LNK_FL_ENABLED | |
1026 | MEDIA_LNK_FL_IMMUTABLE); |
1027 | if (ret) { |
1028 | vin_err(vin, "Error adding link from %s to %s\n" , |
1029 | source->name, sink->name); |
1030 | break; |
1031 | } |
1032 | } |
1033 | mutex_unlock(lock: &vin->group->lock); |
1034 | |
1035 | return ret; |
1036 | } |
1037 | |
1038 | static void rvin_isp_cleanup(struct rvin_dev *vin) |
1039 | { |
1040 | rvin_group_notifier_cleanup(vin); |
1041 | rvin_group_put(vin); |
1042 | rvin_free_controls(vin); |
1043 | } |
1044 | |
1045 | static int rvin_isp_init(struct rvin_dev *vin) |
1046 | { |
1047 | int ret; |
1048 | |
1049 | vin->pad.flags = MEDIA_PAD_FL_SINK; |
1050 | ret = media_entity_pads_init(entity: &vin->vdev.entity, num_pads: 1, pads: &vin->pad); |
1051 | if (ret) |
1052 | return ret; |
1053 | |
1054 | ret = rvin_create_controls(vin, NULL); |
1055 | if (ret < 0) |
1056 | return ret; |
1057 | |
1058 | ret = rvin_group_get(vin, link_setup: rvin_isp_setup_links, NULL); |
1059 | if (ret) |
1060 | goto err_controls; |
1061 | |
1062 | ret = rvin_group_notifier_init(vin, port: 2, max_id: RVIN_ISP_MAX); |
1063 | if (ret) |
1064 | goto err_group; |
1065 | |
1066 | return 0; |
1067 | err_group: |
1068 | rvin_group_put(vin); |
1069 | err_controls: |
1070 | rvin_free_controls(vin); |
1071 | |
1072 | return ret; |
1073 | } |
1074 | |
1075 | /* ----------------------------------------------------------------------------- |
1076 | * Suspend / Resume |
1077 | */ |
1078 | |
1079 | static int __maybe_unused rvin_suspend(struct device *dev) |
1080 | { |
1081 | struct rvin_dev *vin = dev_get_drvdata(dev); |
1082 | |
1083 | if (vin->state != RUNNING) |
1084 | return 0; |
1085 | |
1086 | rvin_stop_streaming(vin); |
1087 | |
1088 | vin->state = SUSPENDED; |
1089 | |
1090 | return 0; |
1091 | } |
1092 | |
1093 | static int __maybe_unused rvin_resume(struct device *dev) |
1094 | { |
1095 | struct rvin_dev *vin = dev_get_drvdata(dev); |
1096 | |
1097 | if (vin->state != SUSPENDED) |
1098 | return 0; |
1099 | |
1100 | /* |
1101 | * Restore group master CHSEL setting. |
1102 | * |
1103 | * This needs to be done by every VIN resuming not only the master |
1104 | * as we don't know if and in which order the master VINs will |
1105 | * be resumed. |
1106 | */ |
1107 | if (vin->info->use_mc) { |
1108 | unsigned int master_id = rvin_group_id_to_master(vin->id); |
1109 | struct rvin_dev *master = vin->group->vin[master_id]; |
1110 | int ret; |
1111 | |
1112 | if (WARN_ON(!master)) |
1113 | return -ENODEV; |
1114 | |
1115 | ret = rvin_set_channel_routing(vin: master, chsel: master->chsel); |
1116 | if (ret) |
1117 | return ret; |
1118 | } |
1119 | |
1120 | return rvin_start_streaming(vin); |
1121 | } |
1122 | |
1123 | /* ----------------------------------------------------------------------------- |
1124 | * Platform Device Driver |
1125 | */ |
1126 | |
1127 | static const struct rvin_info rcar_info_h1 = { |
1128 | .model = RCAR_H1, |
1129 | .use_mc = false, |
1130 | .max_width = 2048, |
1131 | .max_height = 2048, |
1132 | .scaler = rvin_scaler_gen2, |
1133 | }; |
1134 | |
1135 | static const struct rvin_info rcar_info_m1 = { |
1136 | .model = RCAR_M1, |
1137 | .use_mc = false, |
1138 | .max_width = 2048, |
1139 | .max_height = 2048, |
1140 | .scaler = rvin_scaler_gen2, |
1141 | }; |
1142 | |
1143 | static const struct rvin_info rcar_info_gen2 = { |
1144 | .model = RCAR_GEN2, |
1145 | .use_mc = false, |
1146 | .max_width = 2048, |
1147 | .max_height = 2048, |
1148 | .scaler = rvin_scaler_gen2, |
1149 | }; |
1150 | |
1151 | static const struct rvin_group_route rcar_info_r8a774e1_routes[] = { |
1152 | { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, |
1153 | { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, |
1154 | { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, |
1155 | { /* Sentinel */ } |
1156 | }; |
1157 | |
1158 | static const struct rvin_info rcar_info_r8a774e1 = { |
1159 | .model = RCAR_GEN3, |
1160 | .use_mc = true, |
1161 | .max_width = 4096, |
1162 | .max_height = 4096, |
1163 | .routes = rcar_info_r8a774e1_routes, |
1164 | }; |
1165 | |
1166 | static const struct rvin_group_route rcar_info_r8a7795_routes[] = { |
1167 | { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, |
1168 | { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, |
1169 | { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, |
1170 | { .master = 4, .csi = RVIN_CSI41, .chsel = 0x03 }, |
1171 | { /* Sentinel */ } |
1172 | }; |
1173 | |
1174 | static const struct rvin_info rcar_info_r8a7795 = { |
1175 | .model = RCAR_GEN3, |
1176 | .use_mc = true, |
1177 | .nv12 = true, |
1178 | .max_width = 4096, |
1179 | .max_height = 4096, |
1180 | .routes = rcar_info_r8a7795_routes, |
1181 | .scaler = rvin_scaler_gen3, |
1182 | }; |
1183 | |
1184 | static const struct rvin_group_route rcar_info_r8a7796_routes[] = { |
1185 | { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, |
1186 | { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, |
1187 | { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, |
1188 | { .master = 4, .csi = RVIN_CSI40, .chsel = 0x03 }, |
1189 | { /* Sentinel */ } |
1190 | }; |
1191 | |
1192 | static const struct rvin_info rcar_info_r8a7796 = { |
1193 | .model = RCAR_GEN3, |
1194 | .use_mc = true, |
1195 | .nv12 = true, |
1196 | .max_width = 4096, |
1197 | .max_height = 4096, |
1198 | .routes = rcar_info_r8a7796_routes, |
1199 | .scaler = rvin_scaler_gen3, |
1200 | }; |
1201 | |
1202 | static const struct rvin_group_route rcar_info_r8a77965_routes[] = { |
1203 | { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, |
1204 | { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, |
1205 | { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, |
1206 | { .master = 4, .csi = RVIN_CSI40, .chsel = 0x03 }, |
1207 | { /* Sentinel */ } |
1208 | }; |
1209 | |
1210 | static const struct rvin_info rcar_info_r8a77965 = { |
1211 | .model = RCAR_GEN3, |
1212 | .use_mc = true, |
1213 | .nv12 = true, |
1214 | .max_width = 4096, |
1215 | .max_height = 4096, |
1216 | .routes = rcar_info_r8a77965_routes, |
1217 | .scaler = rvin_scaler_gen3, |
1218 | }; |
1219 | |
1220 | static const struct rvin_group_route rcar_info_r8a77970_routes[] = { |
1221 | { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, |
1222 | { /* Sentinel */ } |
1223 | }; |
1224 | |
1225 | static const struct rvin_info rcar_info_r8a77970 = { |
1226 | .model = RCAR_GEN3, |
1227 | .use_mc = true, |
1228 | .max_width = 4096, |
1229 | .max_height = 4096, |
1230 | .routes = rcar_info_r8a77970_routes, |
1231 | }; |
1232 | |
1233 | static const struct rvin_group_route rcar_info_r8a77980_routes[] = { |
1234 | { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, |
1235 | { .master = 4, .csi = RVIN_CSI41, .chsel = 0x03 }, |
1236 | { /* Sentinel */ } |
1237 | }; |
1238 | |
1239 | static const struct rvin_info rcar_info_r8a77980 = { |
1240 | .model = RCAR_GEN3, |
1241 | .use_mc = true, |
1242 | .nv12 = true, |
1243 | .max_width = 4096, |
1244 | .max_height = 4096, |
1245 | .routes = rcar_info_r8a77980_routes, |
1246 | }; |
1247 | |
1248 | static const struct rvin_group_route rcar_info_r8a77990_routes[] = { |
1249 | { .master = 4, .csi = RVIN_CSI40, .chsel = 0x03 }, |
1250 | { /* Sentinel */ } |
1251 | }; |
1252 | |
1253 | static const struct rvin_info rcar_info_r8a77990 = { |
1254 | .model = RCAR_GEN3, |
1255 | .use_mc = true, |
1256 | .nv12 = true, |
1257 | .max_width = 4096, |
1258 | .max_height = 4096, |
1259 | .routes = rcar_info_r8a77990_routes, |
1260 | .scaler = rvin_scaler_gen3, |
1261 | }; |
1262 | |
1263 | static const struct rvin_group_route rcar_info_r8a77995_routes[] = { |
1264 | { /* Sentinel */ } |
1265 | }; |
1266 | |
1267 | static const struct rvin_info rcar_info_r8a77995 = { |
1268 | .model = RCAR_GEN3, |
1269 | .use_mc = true, |
1270 | .nv12 = true, |
1271 | .max_width = 4096, |
1272 | .max_height = 4096, |
1273 | .routes = rcar_info_r8a77995_routes, |
1274 | .scaler = rvin_scaler_gen3, |
1275 | }; |
1276 | |
1277 | static const struct rvin_info rcar_info_r8a779a0 = { |
1278 | .model = RCAR_GEN3, |
1279 | .use_mc = true, |
1280 | .use_isp = true, |
1281 | .nv12 = true, |
1282 | .max_width = 4096, |
1283 | .max_height = 4096, |
1284 | }; |
1285 | |
1286 | static const struct rvin_info rcar_info_r8a779g0 = { |
1287 | .model = RCAR_GEN3, |
1288 | .use_mc = true, |
1289 | .use_isp = true, |
1290 | .nv12 = true, |
1291 | .max_width = 4096, |
1292 | .max_height = 4096, |
1293 | }; |
1294 | |
1295 | static const struct of_device_id rvin_of_id_table[] = { |
1296 | { |
1297 | .compatible = "renesas,vin-r8a774a1" , |
1298 | .data = &rcar_info_r8a7796, |
1299 | }, |
1300 | { |
1301 | .compatible = "renesas,vin-r8a774b1" , |
1302 | .data = &rcar_info_r8a77965, |
1303 | }, |
1304 | { |
1305 | .compatible = "renesas,vin-r8a774c0" , |
1306 | .data = &rcar_info_r8a77990, |
1307 | }, |
1308 | { |
1309 | .compatible = "renesas,vin-r8a774e1" , |
1310 | .data = &rcar_info_r8a774e1, |
1311 | }, |
1312 | { |
1313 | .compatible = "renesas,vin-r8a7778" , |
1314 | .data = &rcar_info_m1, |
1315 | }, |
1316 | { |
1317 | .compatible = "renesas,vin-r8a7779" , |
1318 | .data = &rcar_info_h1, |
1319 | }, |
1320 | { |
1321 | .compatible = "renesas,rcar-gen2-vin" , |
1322 | .data = &rcar_info_gen2, |
1323 | }, |
1324 | { |
1325 | .compatible = "renesas,vin-r8a7795" , |
1326 | .data = &rcar_info_r8a7795, |
1327 | }, |
1328 | { |
1329 | .compatible = "renesas,vin-r8a7796" , |
1330 | .data = &rcar_info_r8a7796, |
1331 | }, |
1332 | { |
1333 | .compatible = "renesas,vin-r8a77961" , |
1334 | .data = &rcar_info_r8a7796, |
1335 | }, |
1336 | { |
1337 | .compatible = "renesas,vin-r8a77965" , |
1338 | .data = &rcar_info_r8a77965, |
1339 | }, |
1340 | { |
1341 | .compatible = "renesas,vin-r8a77970" , |
1342 | .data = &rcar_info_r8a77970, |
1343 | }, |
1344 | { |
1345 | .compatible = "renesas,vin-r8a77980" , |
1346 | .data = &rcar_info_r8a77980, |
1347 | }, |
1348 | { |
1349 | .compatible = "renesas,vin-r8a77990" , |
1350 | .data = &rcar_info_r8a77990, |
1351 | }, |
1352 | { |
1353 | .compatible = "renesas,vin-r8a77995" , |
1354 | .data = &rcar_info_r8a77995, |
1355 | }, |
1356 | { |
1357 | .compatible = "renesas,vin-r8a779a0" , |
1358 | .data = &rcar_info_r8a779a0, |
1359 | }, |
1360 | { |
1361 | .compatible = "renesas,vin-r8a779g0" , |
1362 | .data = &rcar_info_r8a779g0, |
1363 | }, |
1364 | { /* Sentinel */ }, |
1365 | }; |
1366 | MODULE_DEVICE_TABLE(of, rvin_of_id_table); |
1367 | |
1368 | static int rcar_vin_probe(struct platform_device *pdev) |
1369 | { |
1370 | struct rvin_dev *vin; |
1371 | int irq, ret; |
1372 | |
1373 | vin = devm_kzalloc(dev: &pdev->dev, size: sizeof(*vin), GFP_KERNEL); |
1374 | if (!vin) |
1375 | return -ENOMEM; |
1376 | |
1377 | vin->dev = &pdev->dev; |
1378 | vin->info = of_device_get_match_data(dev: &pdev->dev); |
1379 | vin->alpha = 0xff; |
1380 | |
1381 | vin->base = devm_platform_ioremap_resource(pdev, index: 0); |
1382 | if (IS_ERR(ptr: vin->base)) |
1383 | return PTR_ERR(ptr: vin->base); |
1384 | |
1385 | irq = platform_get_irq(pdev, 0); |
1386 | if (irq < 0) |
1387 | return irq; |
1388 | |
1389 | ret = rvin_dma_register(vin, irq); |
1390 | if (ret) |
1391 | return ret; |
1392 | |
1393 | platform_set_drvdata(pdev, data: vin); |
1394 | |
1395 | if (vin->info->use_isp) { |
1396 | ret = rvin_isp_init(vin); |
1397 | } else if (vin->info->use_mc) { |
1398 | ret = rvin_csi2_init(vin); |
1399 | |
1400 | if (vin->info->scaler && |
1401 | rvin_group_id_to_master(vin->id) == vin->id) |
1402 | vin->scaler = vin->info->scaler; |
1403 | } else { |
1404 | ret = rvin_parallel_init(vin); |
1405 | |
1406 | if (vin->info->scaler) |
1407 | vin->scaler = vin->info->scaler; |
1408 | } |
1409 | |
1410 | if (ret) { |
1411 | rvin_dma_unregister(vin); |
1412 | return ret; |
1413 | } |
1414 | |
1415 | pm_suspend_ignore_children(dev: &pdev->dev, enable: true); |
1416 | pm_runtime_enable(dev: &pdev->dev); |
1417 | |
1418 | return 0; |
1419 | } |
1420 | |
1421 | static void rcar_vin_remove(struct platform_device *pdev) |
1422 | { |
1423 | struct rvin_dev *vin = platform_get_drvdata(pdev); |
1424 | |
1425 | pm_runtime_disable(dev: &pdev->dev); |
1426 | |
1427 | rvin_v4l2_unregister(vin); |
1428 | |
1429 | if (vin->info->use_isp) |
1430 | rvin_isp_cleanup(vin); |
1431 | else if (vin->info->use_mc) |
1432 | rvin_csi2_cleanup(vin); |
1433 | else |
1434 | rvin_parallel_cleanup(vin); |
1435 | |
1436 | rvin_dma_unregister(vin); |
1437 | } |
1438 | |
1439 | static SIMPLE_DEV_PM_OPS(rvin_pm_ops, rvin_suspend, rvin_resume); |
1440 | |
1441 | static struct platform_driver rcar_vin_driver = { |
1442 | .driver = { |
1443 | .name = "rcar-vin" , |
1444 | .suppress_bind_attrs = true, |
1445 | .pm = &rvin_pm_ops, |
1446 | .of_match_table = rvin_of_id_table, |
1447 | }, |
1448 | .probe = rcar_vin_probe, |
1449 | .remove_new = rcar_vin_remove, |
1450 | }; |
1451 | |
1452 | module_platform_driver(rcar_vin_driver); |
1453 | |
1454 | MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>" ); |
1455 | MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver" ); |
1456 | MODULE_LICENSE("GPL" ); |
1457 | |