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-of.h>
28 /* -----------------------------------------------------------------------------
32 #define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier)
34 static bool rvin_mbus_supported(struct rvin_graph_entity
*entity
)
36 struct v4l2_subdev
*sd
= entity
->subdev
;
37 struct v4l2_subdev_mbus_code_enum code
= {
38 .which
= V4L2_SUBDEV_FORMAT_ACTIVE
,
42 while (!v4l2_subdev_call(sd
, pad
, enum_mbus_code
, NULL
, &code
)) {
45 case MEDIA_BUS_FMT_YUYV8_1X16
:
46 case MEDIA_BUS_FMT_UYVY8_2X8
:
47 case MEDIA_BUS_FMT_UYVY10_2X10
:
48 case MEDIA_BUS_FMT_RGB888_1X24
:
49 entity
->code
= code
.code
;
59 static int rvin_digital_notify_complete(struct v4l2_async_notifier
*notifier
)
61 struct rvin_dev
*vin
= notifier_to_vin(notifier
);
64 /* Verify subdevices mbus format */
65 if (!rvin_mbus_supported(&vin
->digital
)) {
66 vin_err(vin
, "Unsupported media bus format for %s\n",
67 vin
->digital
.subdev
->name
);
71 vin_dbg(vin
, "Found media bus format for %s: %d\n",
72 vin
->digital
.subdev
->name
, vin
->digital
.code
);
74 ret
= v4l2_device_register_subdev_nodes(&vin
->v4l2_dev
);
76 vin_err(vin
, "Failed to register subdev nodes\n");
80 return rvin_v4l2_probe(vin
);
83 static void rvin_digital_notify_unbind(struct v4l2_async_notifier
*notifier
,
84 struct v4l2_subdev
*subdev
,
85 struct v4l2_async_subdev
*asd
)
87 struct rvin_dev
*vin
= notifier_to_vin(notifier
);
89 if (vin
->digital
.subdev
== subdev
) {
90 vin_dbg(vin
, "unbind digital subdev %s\n", subdev
->name
);
91 rvin_v4l2_remove(vin
);
92 vin
->digital
.subdev
= NULL
;
96 vin_err(vin
, "no entity for subdev %s to unbind\n", subdev
->name
);
99 static int rvin_digital_notify_bound(struct v4l2_async_notifier
*notifier
,
100 struct v4l2_subdev
*subdev
,
101 struct v4l2_async_subdev
*asd
)
103 struct rvin_dev
*vin
= notifier_to_vin(notifier
);
105 v4l2_set_subdev_hostdata(subdev
, vin
);
107 if (vin
->digital
.asd
.match
.of
.node
== subdev
->dev
->of_node
) {
108 vin_dbg(vin
, "bound digital subdev %s\n", subdev
->name
);
109 vin
->digital
.subdev
= subdev
;
113 vin_err(vin
, "no entity for subdev %s to bind\n", subdev
->name
);
117 static int rvin_digitial_parse_v4l2(struct rvin_dev
*vin
,
118 struct device_node
*ep
,
119 struct v4l2_mbus_config
*mbus_cfg
)
121 struct v4l2_of_endpoint v4l2_ep
;
124 ret
= v4l2_of_parse_endpoint(ep
, &v4l2_ep
);
126 vin_err(vin
, "Could not parse v4l2 endpoint\n");
130 mbus_cfg
->type
= v4l2_ep
.bus_type
;
132 switch (mbus_cfg
->type
) {
133 case V4L2_MBUS_PARALLEL
:
134 vin_dbg(vin
, "Found PARALLEL media bus\n");
135 mbus_cfg
->flags
= v4l2_ep
.bus
.parallel
.flags
;
137 case V4L2_MBUS_BT656
:
138 vin_dbg(vin
, "Found BT656 media bus\n");
142 vin_err(vin
, "Unknown media bus type\n");
149 static int rvin_digital_graph_parse(struct rvin_dev
*vin
)
151 struct device_node
*ep
, *np
;
154 vin
->digital
.asd
.match
.of
.node
= NULL
;
155 vin
->digital
.subdev
= NULL
;
158 * Port 0 id 0 is local digital input, try to get it.
159 * Not all instances can or will have this, that is OK
161 ep
= of_graph_get_endpoint_by_regs(vin
->dev
->of_node
, 0, 0);
165 np
= of_graph_get_remote_port_parent(ep
);
167 vin_err(vin
, "No remote parent for digital input\n");
173 ret
= rvin_digitial_parse_v4l2(vin
, ep
, &vin
->digital
.mbus_cfg
);
178 vin
->digital
.asd
.match
.of
.node
= np
;
179 vin
->digital
.asd
.match_type
= V4L2_ASYNC_MATCH_OF
;
184 static int rvin_digital_graph_init(struct rvin_dev
*vin
)
186 struct v4l2_async_subdev
**subdevs
= NULL
;
189 ret
= rvin_digital_graph_parse(vin
);
193 if (!vin
->digital
.asd
.match
.of
.node
) {
194 vin_dbg(vin
, "No digital subdevice found\n");
198 /* Register the subdevices notifier. */
199 subdevs
= devm_kzalloc(vin
->dev
, sizeof(*subdevs
), GFP_KERNEL
);
203 subdevs
[0] = &vin
->digital
.asd
;
205 vin_dbg(vin
, "Found digital subdevice %s\n",
206 of_node_full_name(subdevs
[0]->match
.of
.node
));
208 vin
->notifier
.num_subdevs
= 1;
209 vin
->notifier
.subdevs
= subdevs
;
210 vin
->notifier
.bound
= rvin_digital_notify_bound
;
211 vin
->notifier
.unbind
= rvin_digital_notify_unbind
;
212 vin
->notifier
.complete
= rvin_digital_notify_complete
;
214 ret
= v4l2_async_notifier_register(&vin
->v4l2_dev
, &vin
->notifier
);
216 vin_err(vin
, "Notifier registration failed\n");
223 /* -----------------------------------------------------------------------------
224 * Platform Device Driver
227 static const struct of_device_id rvin_of_id_table
[] = {
228 { .compatible
= "renesas,vin-r8a7794", .data
= (void *)RCAR_GEN2
},
229 { .compatible
= "renesas,vin-r8a7793", .data
= (void *)RCAR_GEN2
},
230 { .compatible
= "renesas,vin-r8a7791", .data
= (void *)RCAR_GEN2
},
231 { .compatible
= "renesas,vin-r8a7790", .data
= (void *)RCAR_GEN2
},
232 { .compatible
= "renesas,vin-r8a7779", .data
= (void *)RCAR_H1
},
233 { .compatible
= "renesas,vin-r8a7778", .data
= (void *)RCAR_M1
},
234 { .compatible
= "renesas,rcar-gen2-vin", .data
= (void *)RCAR_GEN2
},
237 MODULE_DEVICE_TABLE(of
, rvin_of_id_table
);
239 static int rcar_vin_probe(struct platform_device
*pdev
)
241 const struct of_device_id
*match
;
242 struct rvin_dev
*vin
;
243 struct resource
*mem
;
246 vin
= devm_kzalloc(&pdev
->dev
, sizeof(*vin
), GFP_KERNEL
);
250 match
= of_match_device(of_match_ptr(rvin_of_id_table
), &pdev
->dev
);
254 vin
->dev
= &pdev
->dev
;
255 vin
->chip
= (enum chip_id
)match
->data
;
257 mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
261 vin
->base
= devm_ioremap_resource(vin
->dev
, mem
);
262 if (IS_ERR(vin
->base
))
263 return PTR_ERR(vin
->base
);
265 irq
= platform_get_irq(pdev
, 0);
269 ret
= rvin_dma_probe(vin
, irq
);
273 ret
= rvin_digital_graph_init(vin
);
277 pm_suspend_ignore_children(&pdev
->dev
, true);
278 pm_runtime_enable(&pdev
->dev
);
280 platform_set_drvdata(pdev
, vin
);
284 rvin_dma_remove(vin
);
289 static int rcar_vin_remove(struct platform_device
*pdev
)
291 struct rvin_dev
*vin
= platform_get_drvdata(pdev
);
293 pm_runtime_disable(&pdev
->dev
);
295 v4l2_async_notifier_unregister(&vin
->notifier
);
297 rvin_dma_remove(vin
);
302 static struct platform_driver rcar_vin_driver
= {
305 .of_match_table
= rvin_of_id_table
,
307 .probe
= rcar_vin_probe
,
308 .remove
= rcar_vin_remove
,
311 module_platform_driver(rcar_vin_driver
);
313 MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
314 MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver");
315 MODULE_LICENSE("GPL v2");