Commit | Line | Data |
---|---|---|
aa7b8278 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
f00add96 NS |
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 | |
f00add96 NS |
11 | */ |
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/of_device.h> | |
16 | #include <linux/of_graph.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/pm_runtime.h> | |
3bb4c3bc | 19 | #include <linux/slab.h> |
48ad6c2b | 20 | #include <linux/sys_soc.h> |
f00add96 | 21 | |
85999e8e | 22 | #include <media/v4l2-async.h> |
859969b3 | 23 | #include <media/v4l2-fwnode.h> |
c0cc5aef | 24 | #include <media/v4l2-mc.h> |
f00add96 NS |
25 | |
26 | #include "rcar-vin.h" | |
27 | ||
c7e80b67 NS |
28 | /* |
29 | * The companion CSI-2 receiver driver (rcar-csi2) is known | |
30 | * and we know it has one source pad (pad 0) and four sink | |
31 | * pads (pad 1-4). So to translate a pad on the remote | |
32 | * CSI-2 receiver to/from the VIN internal channel number simply | |
33 | * subtract/add one from the pad/channel number. | |
34 | */ | |
35 | #define rvin_group_csi_pad_to_channel(pad) ((pad) - 1) | |
36 | #define rvin_group_csi_channel_to_pad(channel) ((channel) + 1) | |
37 | ||
38 | /* | |
39 | * Not all VINs are created equal, master VINs control the | |
40 | * routing for other VIN's. We can figure out which VIN is | |
41 | * master by looking at a VINs id. | |
42 | */ | |
43 | #define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4) | |
44 | ||
2241ea75 JM |
45 | #define v4l2_dev_to_vin(d) container_of(d, struct rvin_dev, v4l2_dev) |
46 | ||
c0cc5aef NS |
47 | /* ----------------------------------------------------------------------------- |
48 | * Media Controller link notification | |
49 | */ | |
50 | ||
51 | /* group lock should be held when calling this function. */ | |
52 | static int rvin_group_entity_to_csi_id(struct rvin_group *group, | |
53 | struct media_entity *entity) | |
54 | { | |
55 | struct v4l2_subdev *sd; | |
56 | unsigned int i; | |
57 | ||
58 | sd = media_entity_to_v4l2_subdev(entity); | |
59 | ||
60 | for (i = 0; i < RVIN_CSI_MAX; i++) | |
61 | if (group->csi[i].subdev == sd) | |
62 | return i; | |
63 | ||
64 | return -ENODEV; | |
65 | } | |
66 | ||
67 | static unsigned int rvin_group_get_mask(struct rvin_dev *vin, | |
68 | enum rvin_csi_id csi_id, | |
69 | unsigned char channel) | |
70 | { | |
71 | const struct rvin_group_route *route; | |
72 | unsigned int mask = 0; | |
73 | ||
74 | for (route = vin->info->routes; route->mask; route++) { | |
75 | if (route->vin == vin->id && | |
76 | route->csi == csi_id && | |
77 | route->channel == channel) { | |
78 | vin_dbg(vin, | |
79 | "Adding route: vin: %d csi: %d channel: %d\n", | |
80 | route->vin, route->csi, route->channel); | |
81 | mask |= route->mask; | |
82 | } | |
83 | } | |
84 | ||
85 | return mask; | |
86 | } | |
87 | ||
88 | /* | |
89 | * Link setup for the links between a VIN and a CSI-2 receiver is a bit | |
90 | * complex. The reason for this is that the register controlling routing | |
91 | * is not present in each VIN instance. There are special VINs which | |
92 | * control routing for themselves and other VINs. There are not many | |
93 | * different possible links combinations that can be enabled at the same | |
94 | * time, therefor all already enabled links which are controlled by a | |
95 | * master VIN need to be taken into account when making the decision | |
96 | * if a new link can be enabled or not. | |
97 | * | |
98 | * 1. Find out which VIN the link the user tries to enable is connected to. | |
99 | * 2. Lookup which master VIN controls the links for this VIN. | |
100 | * 3. Start with a bitmask with all bits set. | |
101 | * 4. For each previously enabled link from the master VIN bitwise AND its | |
102 | * route mask (see documentation for mask in struct rvin_group_route) | |
103 | * with the bitmask. | |
104 | * 5. Bitwise AND the mask for the link the user tries to enable to the bitmask. | |
105 | * 6. If the bitmask is not empty at this point the new link can be enabled | |
106 | * while keeping all previous links enabled. Update the CHSEL value of the | |
107 | * master VIN and inform the user that the link could be enabled. | |
108 | * | |
109 | * Please note that no link can be enabled if any VIN in the group is | |
110 | * currently open. | |
111 | */ | |
112 | static int rvin_group_link_notify(struct media_link *link, u32 flags, | |
113 | unsigned int notification) | |
114 | { | |
115 | struct rvin_group *group = container_of(link->graph_obj.mdev, | |
116 | struct rvin_group, mdev); | |
117 | unsigned int master_id, channel, mask_new, i; | |
118 | unsigned int mask = ~0; | |
119 | struct media_entity *entity; | |
120 | struct video_device *vdev; | |
121 | struct media_pad *csi_pad; | |
122 | struct rvin_dev *vin = NULL; | |
123 | int csi_id, ret; | |
124 | ||
125 | ret = v4l2_pipeline_link_notify(link, flags, notification); | |
126 | if (ret) | |
127 | return ret; | |
128 | ||
129 | /* Only care about link enablement for VIN nodes. */ | |
130 | if (!(flags & MEDIA_LNK_FL_ENABLED) || | |
131 | !is_media_entity_v4l2_video_device(link->sink->entity)) | |
132 | return 0; | |
133 | ||
134 | /* If any entity is in use don't allow link changes. */ | |
135 | media_device_for_each_entity(entity, &group->mdev) | |
136 | if (entity->use_count) | |
137 | return -EBUSY; | |
138 | ||
139 | mutex_lock(&group->lock); | |
140 | ||
141 | /* Find the master VIN that controls the routes. */ | |
142 | vdev = media_entity_to_video_device(link->sink->entity); | |
143 | vin = container_of(vdev, struct rvin_dev, vdev); | |
144 | master_id = rvin_group_id_to_master(vin->id); | |
145 | ||
146 | if (WARN_ON(!group->vin[master_id])) { | |
147 | ret = -ENODEV; | |
148 | goto out; | |
149 | } | |
150 | ||
151 | /* Build a mask for already enabled links. */ | |
152 | for (i = master_id; i < master_id + 4; i++) { | |
153 | if (!group->vin[i]) | |
154 | continue; | |
155 | ||
156 | /* Get remote CSI-2, if any. */ | |
157 | csi_pad = media_entity_remote_pad( | |
158 | &group->vin[i]->vdev.entity.pads[0]); | |
159 | if (!csi_pad) | |
160 | continue; | |
161 | ||
162 | csi_id = rvin_group_entity_to_csi_id(group, csi_pad->entity); | |
163 | channel = rvin_group_csi_pad_to_channel(csi_pad->index); | |
164 | ||
165 | mask &= rvin_group_get_mask(group->vin[i], csi_id, channel); | |
166 | } | |
167 | ||
168 | /* Add the new link to the existing mask and check if it works. */ | |
169 | csi_id = rvin_group_entity_to_csi_id(group, link->source->entity); | |
1284605d JM |
170 | |
171 | if (csi_id == -ENODEV) { | |
172 | struct v4l2_subdev *sd; | |
1284605d JM |
173 | |
174 | /* | |
175 | * Make sure the source entity subdevice is registered as | |
176 | * a parallel input of one of the enabled VINs if it is not | |
177 | * one of the CSI-2 subdevices. | |
178 | * | |
179 | * No hardware configuration required for parallel inputs, | |
180 | * we can return here. | |
181 | */ | |
182 | sd = media_entity_to_v4l2_subdev(link->source->entity); | |
183 | for (i = 0; i < RCAR_VIN_NUM; i++) { | |
184 | if (group->vin[i] && group->vin[i]->parallel && | |
185 | group->vin[i]->parallel->subdev == sd) { | |
186 | group->vin[i]->is_csi = false; | |
187 | ret = 0; | |
188 | goto out; | |
189 | } | |
190 | } | |
191 | ||
192 | vin_err(vin, "Subdevice %s not registered to any VIN\n", | |
193 | link->source->entity->name); | |
194 | ret = -ENODEV; | |
195 | goto out; | |
196 | } | |
197 | ||
c0cc5aef NS |
198 | channel = rvin_group_csi_pad_to_channel(link->source->index); |
199 | mask_new = mask & rvin_group_get_mask(vin, csi_id, channel); | |
c0cc5aef NS |
200 | vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new); |
201 | ||
202 | if (!mask_new) { | |
203 | ret = -EMLINK; | |
204 | goto out; | |
205 | } | |
206 | ||
207 | /* New valid CHSEL found, set the new value. */ | |
208 | ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new)); | |
1284605d JM |
209 | if (ret) |
210 | goto out; | |
211 | ||
212 | vin->is_csi = true; | |
213 | ||
c0cc5aef NS |
214 | out: |
215 | mutex_unlock(&group->lock); | |
216 | ||
217 | return ret; | |
218 | } | |
219 | ||
220 | static const struct media_device_ops rvin_media_ops = { | |
221 | .link_notify = rvin_group_link_notify, | |
222 | }; | |
223 | ||
3bb4c3bc NS |
224 | /* ----------------------------------------------------------------------------- |
225 | * Gen3 CSI2 Group Allocator | |
226 | */ | |
227 | ||
228 | /* FIXME: This should if we find a system that supports more | |
229 | * than one group for the whole system be replaced with a linked | |
230 | * list of groups. And eventually all of this should be replaced | |
231 | * with a global device allocator API. | |
232 | * | |
233 | * But for now this works as on all supported systems there will | |
234 | * be only one group for all instances. | |
235 | */ | |
236 | ||
237 | static DEFINE_MUTEX(rvin_group_lock); | |
238 | static struct rvin_group *rvin_group_data; | |
239 | ||
240 | static void rvin_group_cleanup(struct rvin_group *group) | |
241 | { | |
242 | media_device_unregister(&group->mdev); | |
243 | media_device_cleanup(&group->mdev); | |
244 | mutex_destroy(&group->lock); | |
245 | } | |
246 | ||
247 | static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin) | |
248 | { | |
249 | struct media_device *mdev = &group->mdev; | |
250 | const struct of_device_id *match; | |
251 | struct device_node *np; | |
252 | int ret; | |
253 | ||
254 | mutex_init(&group->lock); | |
255 | ||
256 | /* Count number of VINs in the system */ | |
257 | group->count = 0; | |
258 | for_each_matching_node(np, vin->dev->driver->of_match_table) | |
259 | if (of_device_is_available(np)) | |
260 | group->count++; | |
261 | ||
262 | vin_dbg(vin, "found %u enabled VIN's in DT", group->count); | |
263 | ||
264 | mdev->dev = vin->dev; | |
c0cc5aef | 265 | mdev->ops = &rvin_media_ops; |
3bb4c3bc NS |
266 | |
267 | match = of_match_node(vin->dev->driver->of_match_table, | |
268 | vin->dev->of_node); | |
269 | ||
c0decac1 MCC |
270 | strscpy(mdev->driver_name, KBUILD_MODNAME, sizeof(mdev->driver_name)); |
271 | strscpy(mdev->model, match->compatible, sizeof(mdev->model)); | |
3bb4c3bc NS |
272 | snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", |
273 | dev_name(mdev->dev)); | |
274 | ||
275 | media_device_init(mdev); | |
276 | ||
277 | ret = media_device_register(&group->mdev); | |
278 | if (ret) | |
279 | rvin_group_cleanup(group); | |
280 | ||
281 | return ret; | |
282 | } | |
283 | ||
284 | static void rvin_group_release(struct kref *kref) | |
285 | { | |
286 | struct rvin_group *group = | |
287 | container_of(kref, struct rvin_group, refcount); | |
288 | ||
289 | mutex_lock(&rvin_group_lock); | |
290 | ||
291 | rvin_group_data = NULL; | |
292 | ||
293 | rvin_group_cleanup(group); | |
294 | ||
295 | kfree(group); | |
296 | ||
297 | mutex_unlock(&rvin_group_lock); | |
298 | } | |
299 | ||
300 | static int rvin_group_get(struct rvin_dev *vin) | |
301 | { | |
302 | struct rvin_group *group; | |
303 | u32 id; | |
304 | int ret; | |
305 | ||
306 | /* Make sure VIN id is present and sane */ | |
307 | ret = of_property_read_u32(vin->dev->of_node, "renesas,id", &id); | |
308 | if (ret) { | |
309 | vin_err(vin, "%pOF: No renesas,id property found\n", | |
310 | vin->dev->of_node); | |
311 | return -EINVAL; | |
312 | } | |
313 | ||
314 | if (id >= RCAR_VIN_NUM) { | |
315 | vin_err(vin, "%pOF: Invalid renesas,id '%u'\n", | |
316 | vin->dev->of_node, id); | |
317 | return -EINVAL; | |
318 | } | |
319 | ||
320 | /* Join or create a VIN group */ | |
321 | mutex_lock(&rvin_group_lock); | |
322 | if (rvin_group_data) { | |
323 | group = rvin_group_data; | |
324 | kref_get(&group->refcount); | |
325 | } else { | |
326 | group = kzalloc(sizeof(*group), GFP_KERNEL); | |
327 | if (!group) { | |
328 | ret = -ENOMEM; | |
329 | goto err_group; | |
330 | } | |
331 | ||
332 | ret = rvin_group_init(group, vin); | |
333 | if (ret) { | |
334 | kfree(group); | |
335 | vin_err(vin, "Failed to initialize group\n"); | |
336 | goto err_group; | |
337 | } | |
338 | ||
339 | kref_init(&group->refcount); | |
340 | ||
341 | rvin_group_data = group; | |
342 | } | |
343 | mutex_unlock(&rvin_group_lock); | |
344 | ||
345 | /* Add VIN to group */ | |
346 | mutex_lock(&group->lock); | |
347 | ||
348 | if (group->vin[id]) { | |
349 | vin_err(vin, "Duplicate renesas,id property value %u\n", id); | |
350 | mutex_unlock(&group->lock); | |
351 | kref_put(&group->refcount, rvin_group_release); | |
352 | return -EINVAL; | |
353 | } | |
354 | ||
355 | group->vin[id] = vin; | |
356 | ||
357 | vin->id = id; | |
358 | vin->group = group; | |
359 | vin->v4l2_dev.mdev = &group->mdev; | |
360 | ||
361 | mutex_unlock(&group->lock); | |
362 | ||
363 | return 0; | |
364 | err_group: | |
365 | mutex_unlock(&rvin_group_lock); | |
366 | return ret; | |
367 | } | |
368 | ||
369 | static void rvin_group_put(struct rvin_dev *vin) | |
370 | { | |
16cedd99 NS |
371 | struct rvin_group *group = vin->group; |
372 | ||
373 | mutex_lock(&group->lock); | |
3bb4c3bc NS |
374 | |
375 | vin->group = NULL; | |
376 | vin->v4l2_dev.mdev = NULL; | |
377 | ||
16cedd99 | 378 | if (WARN_ON(group->vin[vin->id] != vin)) |
3bb4c3bc NS |
379 | goto out; |
380 | ||
16cedd99 | 381 | group->vin[vin->id] = NULL; |
3bb4c3bc | 382 | out: |
16cedd99 | 383 | mutex_unlock(&group->lock); |
3bb4c3bc | 384 | |
16cedd99 | 385 | kref_put(&group->refcount, rvin_group_release); |
3bb4c3bc NS |
386 | } |
387 | ||
f00add96 NS |
388 | /* ----------------------------------------------------------------------------- |
389 | * Async notifier | |
390 | */ | |
391 | ||
f4dbfbc7 NS |
392 | static int rvin_find_pad(struct v4l2_subdev *sd, int direction) |
393 | { | |
394 | unsigned int pad; | |
395 | ||
396 | if (sd->entity.num_pads <= 1) | |
397 | return 0; | |
398 | ||
399 | for (pad = 0; pad < sd->entity.num_pads; pad++) | |
400 | if (sd->entity.pads[pad].flags & direction) | |
401 | return pad; | |
402 | ||
403 | return -EINVAL; | |
404 | } | |
405 | ||
68ee48d6 | 406 | /* ----------------------------------------------------------------------------- |
d24c029e | 407 | * Parallel async notifier |
68ee48d6 NS |
408 | */ |
409 | ||
23525ef9 | 410 | /* The vin lock should be held when calling the subdevice attach and detach */ |
d24c029e JM |
411 | static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, |
412 | struct v4l2_subdev *subdev) | |
f00add96 | 413 | { |
f00add96 NS |
414 | struct v4l2_subdev_mbus_code_enum code = { |
415 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, | |
416 | }; | |
23525ef9 NS |
417 | int ret; |
418 | ||
419 | /* Find source and sink pad of remote subdevice */ | |
420 | ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); | |
421 | if (ret < 0) | |
422 | return ret; | |
d24c029e | 423 | vin->parallel->source_pad = ret; |
f00add96 | 424 | |
23525ef9 | 425 | ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); |
d24c029e | 426 | vin->parallel->sink_pad = ret < 0 ? 0 : ret; |
23525ef9 | 427 | |
a597a772 JM |
428 | if (vin->info->use_mc) { |
429 | vin->parallel->subdev = subdev; | |
430 | return 0; | |
431 | } | |
432 | ||
23525ef9 | 433 | /* Find compatible subdevices mbus format */ |
c65c99b4 | 434 | vin->mbus_code = 0; |
f00add96 | 435 | code.index = 0; |
d24c029e | 436 | code.pad = vin->parallel->source_pad; |
c65c99b4 | 437 | while (!vin->mbus_code && |
23525ef9 | 438 | !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { |
f00add96 NS |
439 | code.index++; |
440 | switch (code.code) { | |
441 | case MEDIA_BUS_FMT_YUYV8_1X16: | |
01d72e9d | 442 | case MEDIA_BUS_FMT_UYVY8_1X16: |
6de690dd NS |
443 | case MEDIA_BUS_FMT_UYVY8_2X8: |
444 | case MEDIA_BUS_FMT_UYVY10_2X10: | |
f00add96 | 445 | case MEDIA_BUS_FMT_RGB888_1X24: |
c65c99b4 | 446 | vin->mbus_code = code.code; |
23525ef9 | 447 | vin_dbg(vin, "Found media bus format for %s: %d\n", |
c65c99b4 | 448 | subdev->name, vin->mbus_code); |
23525ef9 | 449 | break; |
f00add96 NS |
450 | default: |
451 | break; | |
452 | } | |
453 | } | |
454 | ||
c65c99b4 | 455 | if (!vin->mbus_code) { |
23525ef9 NS |
456 | vin_err(vin, "Unsupported media bus format for %s\n", |
457 | subdev->name); | |
458 | return -EINVAL; | |
459 | } | |
460 | ||
461 | /* Read tvnorms */ | |
462 | ret = v4l2_subdev_call(subdev, video, g_tvnorms, &vin->vdev.tvnorms); | |
463 | if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) | |
464 | return ret; | |
465 | ||
4f554bde NS |
466 | /* Read standard */ |
467 | vin->std = V4L2_STD_UNKNOWN; | |
468 | ret = v4l2_subdev_call(subdev, video, g_std, &vin->std); | |
469 | if (ret < 0 && ret != -ENOIOCTLCMD) | |
470 | return ret; | |
471 | ||
23525ef9 NS |
472 | /* Add the controls */ |
473 | ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); | |
474 | if (ret < 0) | |
475 | return ret; | |
476 | ||
477 | ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, subdev->ctrl_handler, | |
da1b1aea | 478 | NULL, true); |
23525ef9 NS |
479 | if (ret < 0) { |
480 | v4l2_ctrl_handler_free(&vin->ctrl_handler); | |
481 | return ret; | |
482 | } | |
483 | ||
484 | vin->vdev.ctrl_handler = &vin->ctrl_handler; | |
485 | ||
d24c029e | 486 | vin->parallel->subdev = subdev; |
23525ef9 NS |
487 | |
488 | return 0; | |
489 | } | |
490 | ||
d24c029e | 491 | static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) |
23525ef9 NS |
492 | { |
493 | rvin_v4l2_unregister(vin); | |
d24c029e | 494 | vin->parallel->subdev = NULL; |
a597a772 JM |
495 | |
496 | if (!vin->info->use_mc) { | |
497 | v4l2_ctrl_handler_free(&vin->ctrl_handler); | |
498 | vin->vdev.ctrl_handler = NULL; | |
499 | } | |
f00add96 NS |
500 | } |
501 | ||
d24c029e | 502 | static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) |
f00add96 | 503 | { |
2241ea75 | 504 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
a962a804 JM |
505 | struct media_entity *source; |
506 | struct media_entity *sink; | |
f00add96 NS |
507 | int ret; |
508 | ||
f00add96 NS |
509 | ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); |
510 | if (ret < 0) { | |
511 | vin_err(vin, "Failed to register subdev nodes\n"); | |
512 | return ret; | |
513 | } | |
514 | ||
a962a804 JM |
515 | if (!video_is_registered(&vin->vdev)) { |
516 | ret = rvin_v4l2_register(vin); | |
517 | if (ret < 0) | |
518 | return ret; | |
519 | } | |
520 | ||
521 | if (!vin->info->use_mc) | |
522 | return 0; | |
523 | ||
524 | /* If we're running with media-controller, link the subdevs. */ | |
525 | source = &vin->parallel->subdev->entity; | |
526 | sink = &vin->vdev.entity; | |
527 | ||
528 | ret = media_create_pad_link(source, vin->parallel->source_pad, | |
529 | sink, vin->parallel->sink_pad, 0); | |
530 | if (ret) | |
531 | vin_err(vin, "Error adding link from %s to %s: %d\n", | |
532 | source->name, sink->name, ret); | |
533 | ||
534 | return ret; | |
f00add96 NS |
535 | } |
536 | ||
d24c029e JM |
537 | static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, |
538 | struct v4l2_subdev *subdev, | |
539 | struct v4l2_async_subdev *asd) | |
f00add96 | 540 | { |
2241ea75 | 541 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
f00add96 | 542 | |
d24c029e | 543 | vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name); |
23525ef9 NS |
544 | |
545 | mutex_lock(&vin->lock); | |
d24c029e | 546 | rvin_parallel_subdevice_detach(vin); |
23525ef9 | 547 | mutex_unlock(&vin->lock); |
f00add96 NS |
548 | } |
549 | ||
d24c029e JM |
550 | static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, |
551 | struct v4l2_subdev *subdev, | |
552 | struct v4l2_async_subdev *asd) | |
f00add96 | 553 | { |
2241ea75 | 554 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
f4dbfbc7 | 555 | int ret; |
f00add96 | 556 | |
23525ef9 | 557 | mutex_lock(&vin->lock); |
d24c029e | 558 | ret = rvin_parallel_subdevice_attach(vin, subdev); |
23525ef9 NS |
559 | mutex_unlock(&vin->lock); |
560 | if (ret) | |
f8a668f7 | 561 | return ret; |
f4dbfbc7 | 562 | |
23525ef9 | 563 | v4l2_set_subdev_hostdata(subdev, vin); |
f4dbfbc7 | 564 | |
f8a668f7 | 565 | vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", |
d24c029e JM |
566 | subdev->name, vin->parallel->source_pad, |
567 | vin->parallel->sink_pad); | |
f00add96 | 568 | |
f8a668f7 | 569 | return 0; |
f00add96 | 570 | } |
23525ef9 | 571 | |
d24c029e JM |
572 | static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = { |
573 | .bound = rvin_parallel_notify_bound, | |
574 | .unbind = rvin_parallel_notify_unbind, | |
575 | .complete = rvin_parallel_notify_complete, | |
b6ee3f0d LP |
576 | }; |
577 | ||
d24c029e JM |
578 | static int rvin_parallel_parse_v4l2(struct device *dev, |
579 | struct v4l2_fwnode_endpoint *vep, | |
580 | struct v4l2_async_subdev *asd) | |
f00add96 | 581 | { |
85999e8e | 582 | struct rvin_dev *vin = dev_get_drvdata(dev); |
158e2a53 JM |
583 | struct rvin_parallel_entity *rvpe = |
584 | container_of(asd, struct rvin_parallel_entity, asd); | |
f00add96 | 585 | |
85999e8e SA |
586 | if (vep->base.port || vep->base.id) |
587 | return -ENOTCONN; | |
f00add96 | 588 | |
158e2a53 JM |
589 | vin->parallel = rvpe; |
590 | vin->parallel->mbus_type = vep->bus_type; | |
f00add96 | 591 | |
158e2a53 | 592 | switch (vin->parallel->mbus_type) { |
83fba2c0 NS |
593 | case V4L2_MBUS_PARALLEL: |
594 | vin_dbg(vin, "Found PARALLEL media bus\n"); | |
158e2a53 | 595 | vin->parallel->mbus_flags = vep->bus.parallel.flags; |
83fba2c0 NS |
596 | break; |
597 | case V4L2_MBUS_BT656: | |
598 | vin_dbg(vin, "Found BT656 media bus\n"); | |
158e2a53 | 599 | vin->parallel->mbus_flags = 0; |
83fba2c0 NS |
600 | break; |
601 | default: | |
602 | vin_err(vin, "Unknown media bus type\n"); | |
603 | return -EINVAL; | |
604 | } | |
f00add96 | 605 | |
83fba2c0 | 606 | return 0; |
f00add96 NS |
607 | } |
608 | ||
a597a772 | 609 | static int rvin_parallel_init(struct rvin_dev *vin) |
f00add96 | 610 | { |
f00add96 NS |
611 | int ret; |
612 | ||
eae2aed1 SL |
613 | v4l2_async_notifier_init(&vin->notifier); |
614 | ||
a597a772 JM |
615 | ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( |
616 | vin->dev, &vin->notifier, sizeof(struct rvin_parallel_entity), | |
617 | 0, rvin_parallel_parse_v4l2); | |
83fba2c0 NS |
618 | if (ret) |
619 | return ret; | |
f00add96 | 620 | |
a597a772 | 621 | /* If using mc, it's fine not to have any input registered. */ |
d24c029e | 622 | if (!vin->parallel) |
a597a772 | 623 | return vin->info->use_mc ? 0 : -ENODEV; |
f00add96 | 624 | |
d24c029e JM |
625 | vin_dbg(vin, "Found parallel subdevice %pOF\n", |
626 | to_of_node(vin->parallel->asd.match.fwnode)); | |
83fba2c0 | 627 | |
d24c029e | 628 | vin->notifier.ops = &rvin_parallel_notify_ops; |
f00add96 NS |
629 | ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); |
630 | if (ret < 0) { | |
631 | vin_err(vin, "Notifier registration failed\n"); | |
9863bc86 | 632 | v4l2_async_notifier_cleanup(&vin->group->notifier); |
83fba2c0 | 633 | return ret; |
f00add96 NS |
634 | } |
635 | ||
83fba2c0 | 636 | return 0; |
f00add96 NS |
637 | } |
638 | ||
c7e80b67 NS |
639 | /* ----------------------------------------------------------------------------- |
640 | * Group async notifier | |
641 | */ | |
642 | ||
643 | static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) | |
644 | { | |
2241ea75 | 645 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
c7e80b67 NS |
646 | const struct rvin_group_route *route; |
647 | unsigned int i; | |
648 | int ret; | |
649 | ||
650 | ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); | |
651 | if (ret) { | |
652 | vin_err(vin, "Failed to register subdev nodes\n"); | |
653 | return ret; | |
654 | } | |
655 | ||
656 | /* Register all video nodes for the group. */ | |
657 | for (i = 0; i < RCAR_VIN_NUM; i++) { | |
a962a804 JM |
658 | if (vin->group->vin[i] && |
659 | !video_is_registered(&vin->group->vin[i]->vdev)) { | |
c7e80b67 NS |
660 | ret = rvin_v4l2_register(vin->group->vin[i]); |
661 | if (ret) | |
662 | return ret; | |
663 | } | |
664 | } | |
665 | ||
666 | /* Create all media device links between VINs and CSI-2's. */ | |
667 | mutex_lock(&vin->group->lock); | |
668 | for (route = vin->info->routes; route->mask; route++) { | |
669 | struct media_pad *source_pad, *sink_pad; | |
670 | struct media_entity *source, *sink; | |
671 | unsigned int source_idx; | |
672 | ||
673 | /* Check that VIN is part of the group. */ | |
674 | if (!vin->group->vin[route->vin]) | |
675 | continue; | |
676 | ||
677 | /* Check that VIN' master is part of the group. */ | |
678 | if (!vin->group->vin[rvin_group_id_to_master(route->vin)]) | |
679 | continue; | |
680 | ||
681 | /* Check that CSI-2 is part of the group. */ | |
682 | if (!vin->group->csi[route->csi].subdev) | |
683 | continue; | |
684 | ||
685 | source = &vin->group->csi[route->csi].subdev->entity; | |
686 | source_idx = rvin_group_csi_channel_to_pad(route->channel); | |
687 | source_pad = &source->pads[source_idx]; | |
688 | ||
689 | sink = &vin->group->vin[route->vin]->vdev.entity; | |
690 | sink_pad = &sink->pads[0]; | |
691 | ||
692 | /* Skip if link already exists. */ | |
693 | if (media_entity_find_link(source_pad, sink_pad)) | |
694 | continue; | |
695 | ||
696 | ret = media_create_pad_link(source, source_idx, sink, 0, 0); | |
697 | if (ret) { | |
698 | vin_err(vin, "Error adding link from %s to %s\n", | |
699 | source->name, sink->name); | |
700 | break; | |
701 | } | |
702 | } | |
703 | mutex_unlock(&vin->group->lock); | |
704 | ||
705 | return ret; | |
706 | } | |
707 | ||
708 | static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, | |
709 | struct v4l2_subdev *subdev, | |
710 | struct v4l2_async_subdev *asd) | |
711 | { | |
2241ea75 | 712 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
c7e80b67 NS |
713 | unsigned int i; |
714 | ||
715 | for (i = 0; i < RCAR_VIN_NUM; i++) | |
716 | if (vin->group->vin[i]) | |
717 | rvin_v4l2_unregister(vin->group->vin[i]); | |
718 | ||
719 | mutex_lock(&vin->group->lock); | |
720 | ||
721 | for (i = 0; i < RVIN_CSI_MAX; i++) { | |
722 | if (vin->group->csi[i].fwnode != asd->match.fwnode) | |
723 | continue; | |
724 | vin->group->csi[i].subdev = NULL; | |
725 | vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i); | |
726 | break; | |
727 | } | |
728 | ||
729 | mutex_unlock(&vin->group->lock); | |
730 | } | |
731 | ||
732 | static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, | |
733 | struct v4l2_subdev *subdev, | |
734 | struct v4l2_async_subdev *asd) | |
735 | { | |
2241ea75 | 736 | struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); |
c7e80b67 NS |
737 | unsigned int i; |
738 | ||
739 | mutex_lock(&vin->group->lock); | |
740 | ||
741 | for (i = 0; i < RVIN_CSI_MAX; i++) { | |
742 | if (vin->group->csi[i].fwnode != asd->match.fwnode) | |
743 | continue; | |
744 | vin->group->csi[i].subdev = subdev; | |
745 | vin_dbg(vin, "Bound CSI-2 %s to slot %u\n", subdev->name, i); | |
746 | break; | |
747 | } | |
748 | ||
749 | mutex_unlock(&vin->group->lock); | |
750 | ||
751 | return 0; | |
752 | } | |
753 | ||
754 | static const struct v4l2_async_notifier_operations rvin_group_notify_ops = { | |
755 | .bound = rvin_group_notify_bound, | |
756 | .unbind = rvin_group_notify_unbind, | |
757 | .complete = rvin_group_notify_complete, | |
758 | }; | |
759 | ||
760 | static int rvin_mc_parse_of_endpoint(struct device *dev, | |
761 | struct v4l2_fwnode_endpoint *vep, | |
762 | struct v4l2_async_subdev *asd) | |
763 | { | |
764 | struct rvin_dev *vin = dev_get_drvdata(dev); | |
765 | ||
766 | if (vep->base.port != 1 || vep->base.id >= RVIN_CSI_MAX) | |
767 | return -EINVAL; | |
768 | ||
769 | if (!of_device_is_available(to_of_node(asd->match.fwnode))) { | |
c7e80b67 NS |
770 | vin_dbg(vin, "OF device %pOF disabled, ignoring\n", |
771 | to_of_node(asd->match.fwnode)); | |
772 | return -ENOTCONN; | |
c7e80b67 NS |
773 | } |
774 | ||
775 | if (vin->group->csi[vep->base.id].fwnode) { | |
776 | vin_dbg(vin, "OF device %pOF already handled\n", | |
777 | to_of_node(asd->match.fwnode)); | |
778 | return -ENOTCONN; | |
779 | } | |
780 | ||
781 | vin->group->csi[vep->base.id].fwnode = asd->match.fwnode; | |
782 | ||
783 | vin_dbg(vin, "Add group OF device %pOF to slot %u\n", | |
784 | to_of_node(asd->match.fwnode), vep->base.id); | |
785 | ||
786 | return 0; | |
787 | } | |
788 | ||
789 | static int rvin_mc_parse_of_graph(struct rvin_dev *vin) | |
790 | { | |
791 | unsigned int count = 0; | |
792 | unsigned int i; | |
793 | int ret; | |
794 | ||
795 | mutex_lock(&vin->group->lock); | |
796 | ||
c7e80b67 NS |
797 | /* If not all VIN's are registered don't register the notifier. */ |
798 | for (i = 0; i < RCAR_VIN_NUM; i++) | |
799 | if (vin->group->vin[i]) | |
800 | count++; | |
801 | ||
802 | if (vin->group->count != count) { | |
803 | mutex_unlock(&vin->group->lock); | |
804 | return 0; | |
805 | } | |
806 | ||
eae2aed1 SL |
807 | v4l2_async_notifier_init(&vin->group->notifier); |
808 | ||
c7e80b67 | 809 | /* |
2241ea75 JM |
810 | * Have all VIN's look for CSI-2 subdevices. Some subdevices will |
811 | * overlap but the parser function can handle it, so each subdevice | |
812 | * will only be registered once with the group notifier. | |
c7e80b67 | 813 | */ |
c7e80b67 NS |
814 | for (i = 0; i < RCAR_VIN_NUM; i++) { |
815 | if (!vin->group->vin[i]) | |
816 | continue; | |
817 | ||
818 | ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( | |
2241ea75 | 819 | vin->group->vin[i]->dev, &vin->group->notifier, |
c7e80b67 NS |
820 | sizeof(struct v4l2_async_subdev), 1, |
821 | rvin_mc_parse_of_endpoint); | |
822 | if (ret) { | |
823 | mutex_unlock(&vin->group->lock); | |
824 | return ret; | |
825 | } | |
826 | } | |
827 | ||
828 | mutex_unlock(&vin->group->lock); | |
829 | ||
d079f94c | 830 | if (list_empty(&vin->group->notifier.asd_list)) |
2241ea75 | 831 | return 0; |
c7e80b67 | 832 | |
2241ea75 JM |
833 | vin->group->notifier.ops = &rvin_group_notify_ops; |
834 | ret = v4l2_async_notifier_register(&vin->v4l2_dev, | |
835 | &vin->group->notifier); | |
c7e80b67 NS |
836 | if (ret < 0) { |
837 | vin_err(vin, "Notifier registration failed\n"); | |
9863bc86 | 838 | v4l2_async_notifier_cleanup(&vin->group->notifier); |
c7e80b67 NS |
839 | return ret; |
840 | } | |
841 | ||
842 | return 0; | |
843 | } | |
844 | ||
68ee48d6 NS |
845 | static int rvin_mc_init(struct rvin_dev *vin) |
846 | { | |
3bb4c3bc NS |
847 | int ret; |
848 | ||
68ee48d6 | 849 | vin->pad.flags = MEDIA_PAD_FL_SINK; |
3bb4c3bc NS |
850 | ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad); |
851 | if (ret) | |
852 | return ret; | |
853 | ||
c7e80b67 NS |
854 | ret = rvin_group_get(vin); |
855 | if (ret) | |
856 | return ret; | |
857 | ||
858 | ret = rvin_mc_parse_of_graph(vin); | |
859 | if (ret) | |
860 | rvin_group_put(vin); | |
861 | ||
862 | return ret; | |
68ee48d6 NS |
863 | } |
864 | ||
f00add96 NS |
865 | /* ----------------------------------------------------------------------------- |
866 | * Platform Device Driver | |
867 | */ | |
868 | ||
bb3ed3fd NS |
869 | static const struct rvin_info rcar_info_h1 = { |
870 | .model = RCAR_H1, | |
47ba5bbf | 871 | .use_mc = false, |
16cdb7d3 NS |
872 | .max_width = 2048, |
873 | .max_height = 2048, | |
bb3ed3fd NS |
874 | }; |
875 | ||
876 | static const struct rvin_info rcar_info_m1 = { | |
877 | .model = RCAR_M1, | |
47ba5bbf | 878 | .use_mc = false, |
16cdb7d3 NS |
879 | .max_width = 2048, |
880 | .max_height = 2048, | |
bb3ed3fd NS |
881 | }; |
882 | ||
883 | static const struct rvin_info rcar_info_gen2 = { | |
884 | .model = RCAR_GEN2, | |
47ba5bbf | 885 | .use_mc = false, |
16cdb7d3 NS |
886 | .max_width = 2048, |
887 | .max_height = 2048, | |
bb3ed3fd NS |
888 | }; |
889 | ||
48ad6c2b NS |
890 | static const struct rvin_group_route rcar_info_r8a7795_routes[] = { |
891 | { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, | |
892 | { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, | |
893 | { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, | |
894 | { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, | |
895 | { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, | |
896 | { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, | |
897 | { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, | |
898 | { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) }, | |
899 | { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, | |
900 | { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, | |
901 | { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, | |
902 | { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, | |
903 | { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, | |
904 | { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) }, | |
905 | { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, | |
906 | { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, | |
907 | { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, | |
908 | { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, | |
909 | { .csi = RVIN_CSI41, .channel = 1, .vin = 4, .mask = BIT(2) }, | |
910 | { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, | |
911 | { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, | |
912 | { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) }, | |
913 | { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, | |
914 | { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, | |
915 | { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) }, | |
916 | { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, | |
917 | { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) }, | |
918 | { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, | |
919 | { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) }, | |
920 | { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, | |
921 | { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) }, | |
922 | { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, | |
923 | { /* Sentinel */ } | |
924 | }; | |
925 | ||
926 | static const struct rvin_info rcar_info_r8a7795 = { | |
927 | .model = RCAR_GEN3, | |
928 | .use_mc = true, | |
929 | .max_width = 4096, | |
930 | .max_height = 4096, | |
931 | .routes = rcar_info_r8a7795_routes, | |
932 | }; | |
933 | ||
934 | static const struct rvin_group_route rcar_info_r8a7795es1_routes[] = { | |
935 | { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, | |
936 | { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, | |
937 | { .csi = RVIN_CSI21, .channel = 0, .vin = 0, .mask = BIT(2) | BIT(5) }, | |
938 | { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, | |
939 | { .csi = RVIN_CSI21, .channel = 0, .vin = 1, .mask = BIT(1) }, | |
940 | { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, | |
941 | { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, | |
942 | { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, | |
943 | { .csi = RVIN_CSI21, .channel = 1, .vin = 1, .mask = BIT(5) }, | |
944 | { .csi = RVIN_CSI21, .channel = 0, .vin = 2, .mask = BIT(0) }, | |
945 | { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, | |
946 | { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, | |
947 | { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, | |
948 | { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, | |
949 | { .csi = RVIN_CSI21, .channel = 2, .vin = 2, .mask = BIT(5) }, | |
950 | { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, | |
951 | { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) }, | |
952 | { .csi = RVIN_CSI21, .channel = 1, .vin = 3, .mask = BIT(2) }, | |
953 | { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, | |
954 | { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, | |
955 | { .csi = RVIN_CSI21, .channel = 3, .vin = 3, .mask = BIT(5) }, | |
956 | { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, | |
957 | { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, | |
958 | { .csi = RVIN_CSI21, .channel = 0, .vin = 4, .mask = BIT(2) | BIT(5) }, | |
959 | { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, | |
960 | { .csi = RVIN_CSI21, .channel = 0, .vin = 5, .mask = BIT(1) }, | |
961 | { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) }, | |
962 | { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(3) }, | |
963 | { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, | |
964 | { .csi = RVIN_CSI21, .channel = 1, .vin = 5, .mask = BIT(5) }, | |
965 | { .csi = RVIN_CSI21, .channel = 0, .vin = 6, .mask = BIT(0) }, | |
966 | { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) }, | |
967 | { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, | |
968 | { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) }, | |
969 | { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, | |
970 | { .csi = RVIN_CSI21, .channel = 2, .vin = 6, .mask = BIT(5) }, | |
971 | { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) }, | |
972 | { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) }, | |
973 | { .csi = RVIN_CSI21, .channel = 1, .vin = 7, .mask = BIT(2) }, | |
974 | { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) }, | |
975 | { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, | |
976 | { .csi = RVIN_CSI21, .channel = 3, .vin = 7, .mask = BIT(5) }, | |
977 | { /* Sentinel */ } | |
978 | }; | |
979 | ||
980 | static const struct rvin_info rcar_info_r8a7795es1 = { | |
981 | .model = RCAR_GEN3, | |
982 | .use_mc = true, | |
983 | .max_width = 4096, | |
984 | .max_height = 4096, | |
985 | .routes = rcar_info_r8a7795es1_routes, | |
986 | }; | |
987 | ||
2f42acf8 NS |
988 | static const struct rvin_group_route rcar_info_r8a7796_routes[] = { |
989 | { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, | |
990 | { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, | |
991 | { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, | |
992 | { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, | |
993 | { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, | |
994 | { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, | |
995 | { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, | |
996 | { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, | |
997 | { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, | |
998 | { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, | |
999 | { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, | |
1000 | { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) }, | |
1001 | { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, | |
1002 | { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, | |
1003 | { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, | |
1004 | { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, | |
1005 | { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, | |
1006 | { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) }, | |
1007 | { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(3) }, | |
1008 | { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, | |
1009 | { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) }, | |
1010 | { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, | |
1011 | { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) }, | |
1012 | { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, | |
1013 | { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) }, | |
1014 | { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) }, | |
1015 | { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) }, | |
1016 | { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, | |
1017 | { /* Sentinel */ } | |
1018 | }; | |
1019 | ||
1020 | static const struct rvin_info rcar_info_r8a7796 = { | |
1021 | .model = RCAR_GEN3, | |
1022 | .use_mc = true, | |
1023 | .max_width = 4096, | |
1024 | .max_height = 4096, | |
1025 | .routes = rcar_info_r8a7796_routes, | |
1026 | }; | |
1027 | ||
a740e3b2 NS |
1028 | static const struct rvin_group_route rcar_info_r8a77965_routes[] = { |
1029 | { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, | |
1030 | { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, | |
1031 | { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, | |
1032 | { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, | |
1033 | { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, | |
1034 | { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, | |
1035 | { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, | |
1036 | { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) }, | |
1037 | { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, | |
1038 | { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, | |
1039 | { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, | |
1040 | { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, | |
1041 | { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, | |
1042 | { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) }, | |
1043 | { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, | |
1044 | { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, | |
1045 | { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, | |
1046 | { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, | |
1047 | { .csi = RVIN_CSI40, .channel = 1, .vin = 4, .mask = BIT(2) }, | |
1048 | { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, | |
1049 | { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, | |
1050 | { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) }, | |
1051 | { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, | |
1052 | { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, | |
1053 | { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) }, | |
1054 | { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, | |
1055 | { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) }, | |
1056 | { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, | |
1057 | { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) }, | |
1058 | { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, | |
1059 | { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) }, | |
1060 | { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, | |
1061 | { /* Sentinel */ } | |
1062 | }; | |
1063 | ||
1064 | static const struct rvin_info rcar_info_r8a77965 = { | |
1065 | .model = RCAR_GEN3, | |
1066 | .use_mc = true, | |
1067 | .max_width = 4096, | |
1068 | .max_height = 4096, | |
1069 | .routes = rcar_info_r8a77965_routes, | |
1070 | }; | |
1071 | ||
5cda0fca | 1072 | static const struct rvin_group_route rcar_info_r8a77970_routes[] = { |
2e1cd38d NS |
1073 | { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, |
1074 | { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, | |
1075 | { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, | |
1076 | { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, | |
1077 | { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, | |
1078 | { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, | |
1079 | { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, | |
1080 | { /* Sentinel */ } | |
1081 | }; | |
1082 | ||
1083 | static const struct rvin_info rcar_info_r8a77970 = { | |
1084 | .model = RCAR_GEN3, | |
1085 | .use_mc = true, | |
1086 | .max_width = 4096, | |
1087 | .max_height = 4096, | |
5cda0fca | 1088 | .routes = rcar_info_r8a77970_routes, |
2e1cd38d NS |
1089 | }; |
1090 | ||
aa2446ef JM |
1091 | static const struct rvin_group_route rcar_info_r8a77995_routes[] = { |
1092 | { /* Sentinel */ } | |
1093 | }; | |
1094 | ||
1095 | static const struct rvin_info rcar_info_r8a77995 = { | |
1096 | .model = RCAR_GEN3, | |
1097 | .use_mc = true, | |
1098 | .max_width = 4096, | |
1099 | .max_height = 4096, | |
1100 | .routes = rcar_info_r8a77995_routes, | |
1101 | }; | |
1102 | ||
f00add96 | 1103 | static const struct of_device_id rvin_of_id_table[] = { |
bb3ed3fd NS |
1104 | { |
1105 | .compatible = "renesas,vin-r8a7778", | |
1106 | .data = &rcar_info_m1, | |
1107 | }, | |
1108 | { | |
1109 | .compatible = "renesas,vin-r8a7779", | |
1110 | .data = &rcar_info_h1, | |
1111 | }, | |
1112 | { | |
1113 | .compatible = "renesas,vin-r8a7790", | |
1114 | .data = &rcar_info_gen2, | |
1115 | }, | |
1116 | { | |
1117 | .compatible = "renesas,vin-r8a7791", | |
1118 | .data = &rcar_info_gen2, | |
1119 | }, | |
1120 | { | |
1121 | .compatible = "renesas,vin-r8a7793", | |
1122 | .data = &rcar_info_gen2, | |
1123 | }, | |
1124 | { | |
1125 | .compatible = "renesas,vin-r8a7794", | |
1126 | .data = &rcar_info_gen2, | |
1127 | }, | |
1128 | { | |
1129 | .compatible = "renesas,rcar-gen2-vin", | |
1130 | .data = &rcar_info_gen2, | |
1131 | }, | |
48ad6c2b NS |
1132 | { |
1133 | .compatible = "renesas,vin-r8a7795", | |
1134 | .data = &rcar_info_r8a7795, | |
1135 | }, | |
2f42acf8 NS |
1136 | { |
1137 | .compatible = "renesas,vin-r8a7796", | |
1138 | .data = &rcar_info_r8a7796, | |
1139 | }, | |
a740e3b2 NS |
1140 | { |
1141 | .compatible = "renesas,vin-r8a77965", | |
1142 | .data = &rcar_info_r8a77965, | |
1143 | }, | |
2e1cd38d NS |
1144 | { |
1145 | .compatible = "renesas,vin-r8a77970", | |
1146 | .data = &rcar_info_r8a77970, | |
1147 | }, | |
aa2446ef JM |
1148 | { |
1149 | .compatible = "renesas,vin-r8a77995", | |
1150 | .data = &rcar_info_r8a77995, | |
1151 | }, | |
bb3ed3fd | 1152 | { /* Sentinel */ }, |
f00add96 NS |
1153 | }; |
1154 | MODULE_DEVICE_TABLE(of, rvin_of_id_table); | |
1155 | ||
48ad6c2b NS |
1156 | static const struct soc_device_attribute r8a7795es1[] = { |
1157 | { | |
1158 | .soc_id = "r8a7795", .revision = "ES1.*", | |
1159 | .data = &rcar_info_r8a7795es1, | |
1160 | }, | |
1161 | { /* Sentinel */ } | |
1162 | }; | |
1163 | ||
f00add96 NS |
1164 | static int rcar_vin_probe(struct platform_device *pdev) |
1165 | { | |
48ad6c2b | 1166 | const struct soc_device_attribute *attr; |
f00add96 NS |
1167 | struct rvin_dev *vin; |
1168 | struct resource *mem; | |
1169 | int irq, ret; | |
1170 | ||
1171 | vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL); | |
1172 | if (!vin) | |
1173 | return -ENOMEM; | |
1174 | ||
83fba2c0 | 1175 | vin->dev = &pdev->dev; |
bb3ed3fd | 1176 | vin->info = of_device_get_match_data(&pdev->dev); |
f00add96 | 1177 | |
48ad6c2b NS |
1178 | /* |
1179 | * Special care is needed on r8a7795 ES1.x since it | |
1180 | * uses different routing than r8a7795 ES2.0. | |
1181 | */ | |
1182 | attr = soc_device_match(r8a7795es1); | |
1183 | if (attr) | |
1184 | vin->info = attr->data; | |
1185 | ||
f00add96 NS |
1186 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1187 | if (mem == NULL) | |
1188 | return -EINVAL; | |
1189 | ||
1190 | vin->base = devm_ioremap_resource(vin->dev, mem); | |
1191 | if (IS_ERR(vin->base)) | |
1192 | return PTR_ERR(vin->base); | |
1193 | ||
1194 | irq = platform_get_irq(pdev, 0); | |
fc738177 NS |
1195 | if (irq < 0) |
1196 | return irq; | |
f00add96 | 1197 | |
d6ad012e | 1198 | ret = rvin_dma_register(vin, irq); |
f00add96 NS |
1199 | if (ret) |
1200 | return ret; | |
1201 | ||
85999e8e | 1202 | platform_set_drvdata(pdev, vin); |
a597a772 JM |
1203 | |
1204 | if (vin->info->use_mc) { | |
68ee48d6 | 1205 | ret = rvin_mc_init(vin); |
a597a772 JM |
1206 | if (ret) |
1207 | goto error_dma_unregister; | |
1208 | } | |
1209 | ||
1210 | ret = rvin_parallel_init(vin); | |
1211 | if (ret) | |
1212 | goto error_group_unregister; | |
f00add96 NS |
1213 | |
1214 | pm_suspend_ignore_children(&pdev->dev, true); | |
1215 | pm_runtime_enable(&pdev->dev); | |
1216 | ||
f00add96 | 1217 | return 0; |
a597a772 JM |
1218 | |
1219 | error_group_unregister: | |
1220 | if (vin->info->use_mc) { | |
1221 | mutex_lock(&vin->group->lock); | |
1222 | if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { | |
1223 | v4l2_async_notifier_unregister(&vin->group->notifier); | |
1224 | v4l2_async_notifier_cleanup(&vin->group->notifier); | |
1225 | } | |
1226 | mutex_unlock(&vin->group->lock); | |
1227 | rvin_group_put(vin); | |
1228 | } | |
1229 | ||
1230 | error_dma_unregister: | |
d6ad012e | 1231 | rvin_dma_unregister(vin); |
f00add96 NS |
1232 | |
1233 | return ret; | |
1234 | } | |
1235 | ||
1236 | static int rcar_vin_remove(struct platform_device *pdev) | |
1237 | { | |
1238 | struct rvin_dev *vin = platform_get_drvdata(pdev); | |
1239 | ||
1240 | pm_runtime_disable(&pdev->dev); | |
1241 | ||
a31ffe93 NS |
1242 | rvin_v4l2_unregister(vin); |
1243 | ||
f00add96 | 1244 | v4l2_async_notifier_unregister(&vin->notifier); |
85999e8e | 1245 | v4l2_async_notifier_cleanup(&vin->notifier); |
f00add96 | 1246 | |
c7e80b67 NS |
1247 | if (vin->info->use_mc) { |
1248 | mutex_lock(&vin->group->lock); | |
2241ea75 JM |
1249 | if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { |
1250 | v4l2_async_notifier_unregister(&vin->group->notifier); | |
1251 | v4l2_async_notifier_cleanup(&vin->group->notifier); | |
1252 | } | |
c7e80b67 | 1253 | mutex_unlock(&vin->group->lock); |
3bb4c3bc | 1254 | rvin_group_put(vin); |
c7e80b67 | 1255 | } else { |
47ba5bbf | 1256 | v4l2_ctrl_handler_free(&vin->ctrl_handler); |
c7e80b67 | 1257 | } |
23525ef9 | 1258 | |
d6ad012e | 1259 | rvin_dma_unregister(vin); |
f00add96 NS |
1260 | |
1261 | return 0; | |
1262 | } | |
1263 | ||
1264 | static struct platform_driver rcar_vin_driver = { | |
1265 | .driver = { | |
1266 | .name = "rcar-vin", | |
1267 | .of_match_table = rvin_of_id_table, | |
1268 | }, | |
1269 | .probe = rcar_vin_probe, | |
1270 | .remove = rcar_vin_remove, | |
1271 | }; | |
1272 | ||
1273 | module_platform_driver(rcar_vin_driver); | |
1274 | ||
1275 | MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>"); | |
1276 | MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); | |
aa7b8278 | 1277 | MODULE_LICENSE("GPL"); |