2 * Driver for Renesas R-Car VIN
4 * Copyright (C) 2016 Renesas Electronics Corp.
5 * Copyright (C) 2011-2013 Renesas Solutions Corp.
6 * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
7 * Copyright (C) 2008 Magnus Damm
9 * Based on the soc-camera rcar_vin driver
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
17 #include <linux/module.h>
19 #include <linux/of_device.h>
20 #include <linux/of_graph.h>
21 #include <linux/platform_device.h>
22 #include <linux/pm_runtime.h>
24 #include <media/v4l2-fwnode.h>
28 /* -----------------------------------------------------------------------------
32 #define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier)
34 static int rvin_find_pad(struct v4l2_subdev
*sd
, int direction
)
38 if (sd
->entity
.num_pads
<= 1)
41 for (pad
= 0; pad
< sd
->entity
.num_pads
; pad
++)
42 if (sd
->entity
.pads
[pad
].flags
& direction
)
48 static bool rvin_mbus_supported(struct rvin_graph_entity
*entity
)
50 struct v4l2_subdev
*sd
= entity
->subdev
;
51 struct v4l2_subdev_mbus_code_enum code
= {
52 .which
= V4L2_SUBDEV_FORMAT_ACTIVE
,
56 code
.pad
= entity
->source_pad
;
57 while (!v4l2_subdev_call(sd
, pad
, enum_mbus_code
, NULL
, &code
)) {
60 case MEDIA_BUS_FMT_YUYV8_1X16
:
61 case MEDIA_BUS_FMT_UYVY8_2X8
:
62 case MEDIA_BUS_FMT_UYVY10_2X10
:
63 case MEDIA_BUS_FMT_RGB888_1X24
:
64 entity
->code
= code
.code
;
74 static int rvin_digital_notify_complete(struct v4l2_async_notifier
*notifier
)
76 struct rvin_dev
*vin
= notifier_to_vin(notifier
);
79 /* Verify subdevices mbus format */
80 if (!rvin_mbus_supported(&vin
->digital
)) {
81 vin_err(vin
, "Unsupported media bus format for %s\n",
82 vin
->digital
.subdev
->name
);
86 vin_dbg(vin
, "Found media bus format for %s: %d\n",
87 vin
->digital
.subdev
->name
, vin
->digital
.code
);
89 ret
= v4l2_device_register_subdev_nodes(&vin
->v4l2_dev
);
91 vin_err(vin
, "Failed to register subdev nodes\n");
95 return rvin_v4l2_probe(vin
);
98 static void rvin_digital_notify_unbind(struct v4l2_async_notifier
*notifier
,
99 struct v4l2_subdev
*subdev
,
100 struct v4l2_async_subdev
*asd
)
102 struct rvin_dev
*vin
= notifier_to_vin(notifier
);
104 vin_dbg(vin
, "unbind digital subdev %s\n", subdev
->name
);
105 rvin_v4l2_remove(vin
);
106 vin
->digital
.subdev
= NULL
;
109 static int rvin_digital_notify_bound(struct v4l2_async_notifier
*notifier
,
110 struct v4l2_subdev
*subdev
,
111 struct v4l2_async_subdev
*asd
)
113 struct rvin_dev
*vin
= notifier_to_vin(notifier
);
116 v4l2_set_subdev_hostdata(subdev
, vin
);
118 /* Find source and sink pad of remote subdevice */
120 ret
= rvin_find_pad(subdev
, MEDIA_PAD_FL_SOURCE
);
123 vin
->digital
.source_pad
= ret
;
125 ret
= rvin_find_pad(subdev
, MEDIA_PAD_FL_SINK
);
126 vin
->digital
.sink_pad
= ret
< 0 ? 0 : ret
;
128 vin
->digital
.subdev
= subdev
;
130 vin_dbg(vin
, "bound subdev %s source pad: %u sink pad: %u\n",
131 subdev
->name
, vin
->digital
.source_pad
,
132 vin
->digital
.sink_pad
);
137 static int rvin_digitial_parse_v4l2(struct rvin_dev
*vin
,
138 struct device_node
*ep
,
139 struct v4l2_mbus_config
*mbus_cfg
)
141 struct v4l2_fwnode_endpoint v4l2_ep
;
144 ret
= v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep
), &v4l2_ep
);
146 vin_err(vin
, "Could not parse v4l2 endpoint\n");
150 mbus_cfg
->type
= v4l2_ep
.bus_type
;
152 switch (mbus_cfg
->type
) {
153 case V4L2_MBUS_PARALLEL
:
154 vin_dbg(vin
, "Found PARALLEL media bus\n");
155 mbus_cfg
->flags
= v4l2_ep
.bus
.parallel
.flags
;
157 case V4L2_MBUS_BT656
:
158 vin_dbg(vin
, "Found BT656 media bus\n");
162 vin_err(vin
, "Unknown media bus type\n");
169 static int rvin_digital_graph_parse(struct rvin_dev
*vin
)
171 struct device_node
*ep
, *np
;
174 vin
->digital
.asd
.match
.fwnode
.fwnode
= NULL
;
175 vin
->digital
.subdev
= NULL
;
178 * Port 0 id 0 is local digital input, try to get it.
179 * Not all instances can or will have this, that is OK
181 ep
= of_graph_get_endpoint_by_regs(vin
->dev
->of_node
, 0, 0);
185 np
= of_graph_get_remote_port_parent(ep
);
187 vin_err(vin
, "No remote parent for digital input\n");
193 ret
= rvin_digitial_parse_v4l2(vin
, ep
, &vin
->digital
.mbus_cfg
);
198 vin
->digital
.asd
.match
.fwnode
.fwnode
= of_fwnode_handle(np
);
199 vin
->digital
.asd
.match_type
= V4L2_ASYNC_MATCH_FWNODE
;
204 static int rvin_digital_graph_init(struct rvin_dev
*vin
)
206 struct v4l2_async_subdev
**subdevs
= NULL
;
209 ret
= rvin_digital_graph_parse(vin
);
213 if (!vin
->digital
.asd
.match
.fwnode
.fwnode
) {
214 vin_dbg(vin
, "No digital subdevice found\n");
218 /* Register the subdevices notifier. */
219 subdevs
= devm_kzalloc(vin
->dev
, sizeof(*subdevs
), GFP_KERNEL
);
223 subdevs
[0] = &vin
->digital
.asd
;
225 vin_dbg(vin
, "Found digital subdevice %pOF\n",
226 to_of_node(subdevs
[0]->match
.fwnode
.fwnode
));
228 vin
->notifier
.num_subdevs
= 1;
229 vin
->notifier
.subdevs
= subdevs
;
230 vin
->notifier
.bound
= rvin_digital_notify_bound
;
231 vin
->notifier
.unbind
= rvin_digital_notify_unbind
;
232 vin
->notifier
.complete
= rvin_digital_notify_complete
;
234 ret
= v4l2_async_notifier_register(&vin
->v4l2_dev
, &vin
->notifier
);
236 vin_err(vin
, "Notifier registration failed\n");
243 /* -----------------------------------------------------------------------------
244 * Platform Device Driver
247 static const struct of_device_id rvin_of_id_table
[] = {
248 { .compatible
= "renesas,vin-r8a7794", .data
= (void *)RCAR_GEN2
},
249 { .compatible
= "renesas,vin-r8a7793", .data
= (void *)RCAR_GEN2
},
250 { .compatible
= "renesas,vin-r8a7791", .data
= (void *)RCAR_GEN2
},
251 { .compatible
= "renesas,vin-r8a7790", .data
= (void *)RCAR_GEN2
},
252 { .compatible
= "renesas,vin-r8a7779", .data
= (void *)RCAR_H1
},
253 { .compatible
= "renesas,vin-r8a7778", .data
= (void *)RCAR_M1
},
254 { .compatible
= "renesas,rcar-gen2-vin", .data
= (void *)RCAR_GEN2
},
257 MODULE_DEVICE_TABLE(of
, rvin_of_id_table
);
259 static int rcar_vin_probe(struct platform_device
*pdev
)
261 const struct of_device_id
*match
;
262 struct rvin_dev
*vin
;
263 struct resource
*mem
;
266 vin
= devm_kzalloc(&pdev
->dev
, sizeof(*vin
), GFP_KERNEL
);
270 match
= of_match_device(of_match_ptr(rvin_of_id_table
), &pdev
->dev
);
274 vin
->dev
= &pdev
->dev
;
275 vin
->chip
= (enum chip_id
)match
->data
;
277 mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
281 vin
->base
= devm_ioremap_resource(vin
->dev
, mem
);
282 if (IS_ERR(vin
->base
))
283 return PTR_ERR(vin
->base
);
285 irq
= platform_get_irq(pdev
, 0);
289 ret
= rvin_dma_probe(vin
, irq
);
293 ret
= rvin_digital_graph_init(vin
);
297 pm_suspend_ignore_children(&pdev
->dev
, true);
298 pm_runtime_enable(&pdev
->dev
);
300 platform_set_drvdata(pdev
, vin
);
304 rvin_dma_remove(vin
);
309 static int rcar_vin_remove(struct platform_device
*pdev
)
311 struct rvin_dev
*vin
= platform_get_drvdata(pdev
);
313 pm_runtime_disable(&pdev
->dev
);
315 v4l2_async_notifier_unregister(&vin
->notifier
);
317 rvin_dma_remove(vin
);
322 static struct platform_driver rcar_vin_driver
= {
325 .of_match_table
= rvin_of_id_table
,
327 .probe
= rcar_vin_probe
,
328 .remove
= rcar_vin_remove
,
331 module_platform_driver(rcar_vin_driver
);
333 MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
334 MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver");
335 MODULE_LICENSE("GPL v2");