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 int rvin_mbus_supported(struct rvin_dev
*vin
)
36 struct v4l2_subdev
*sd
;
37 struct v4l2_subdev_mbus_code_enum code
= {
38 .which
= V4L2_SUBDEV_FORMAT_ACTIVE
,
41 sd
= vin_to_source(vin
);
44 while (!v4l2_subdev_call(sd
, pad
, enum_mbus_code
, NULL
, &code
)) {
47 case MEDIA_BUS_FMT_YUYV8_1X16
:
48 case MEDIA_BUS_FMT_YUYV8_2X8
:
49 case MEDIA_BUS_FMT_YUYV10_2X10
:
50 case MEDIA_BUS_FMT_RGB888_1X24
:
51 vin
->source
.code
= code
.code
;
52 vin_dbg(vin
, "Found supported media bus format: %d\n",
63 static int rvin_graph_notify_complete(struct v4l2_async_notifier
*notifier
)
65 struct rvin_dev
*vin
= notifier_to_vin(notifier
);
68 ret
= v4l2_device_register_subdev_nodes(&vin
->v4l2_dev
);
70 vin_err(vin
, "Failed to register subdev nodes\n");
74 if (!rvin_mbus_supported(vin
)) {
75 vin_err(vin
, "No supported mediabus format found\n");
79 return rvin_v4l2_probe(vin
);
82 static void rvin_graph_notify_unbind(struct v4l2_async_notifier
*notifier
,
83 struct v4l2_subdev
*sd
,
84 struct v4l2_async_subdev
*asd
)
86 struct rvin_dev
*vin
= notifier_to_vin(notifier
);
88 rvin_v4l2_remove(vin
);
91 static int rvin_graph_notify_bound(struct v4l2_async_notifier
*notifier
,
92 struct v4l2_subdev
*subdev
,
93 struct v4l2_async_subdev
*asd
)
95 struct rvin_dev
*vin
= notifier_to_vin(notifier
);
97 vin_dbg(vin
, "subdev %s bound\n", subdev
->name
);
99 vin
->entity
.entity
= &subdev
->entity
;
100 vin
->entity
.subdev
= subdev
;
105 static int rvin_graph_parse(struct rvin_dev
*vin
,
106 struct device_node
*node
)
108 struct device_node
*remote
;
109 struct device_node
*ep
= NULL
;
110 struct device_node
*next
;
114 next
= of_graph_get_next_endpoint(node
, ep
);
121 remote
= of_graph_get_remote_port_parent(ep
);
127 /* Skip entities that we have already processed. */
128 if (remote
== vin
->dev
->of_node
) {
133 /* Remote node to connect */
134 if (!vin
->entity
.node
) {
135 vin
->entity
.node
= remote
;
136 vin
->entity
.asd
.match_type
= V4L2_ASYNC_MATCH_OF
;
137 vin
->entity
.asd
.match
.of
.node
= remote
;
147 static int rvin_graph_init(struct rvin_dev
*vin
)
149 struct v4l2_async_subdev
**subdevs
= NULL
;
152 /* Parse the graph to extract a list of subdevice DT nodes. */
153 ret
= rvin_graph_parse(vin
, vin
->dev
->of_node
);
155 vin_err(vin
, "Graph parsing failed\n");
160 vin_err(vin
, "No subdev found in graph\n");
165 vin_err(vin
, "More then one subdev found in graph\n");
169 /* Register the subdevices notifier. */
170 subdevs
= devm_kzalloc(vin
->dev
, sizeof(*subdevs
), GFP_KERNEL
);
171 if (subdevs
== NULL
) {
176 subdevs
[0] = &vin
->entity
.asd
;
178 vin
->notifier
.subdevs
= subdevs
;
179 vin
->notifier
.num_subdevs
= 1;
180 vin
->notifier
.bound
= rvin_graph_notify_bound
;
181 vin
->notifier
.unbind
= rvin_graph_notify_unbind
;
182 vin
->notifier
.complete
= rvin_graph_notify_complete
;
184 ret
= v4l2_async_notifier_register(&vin
->v4l2_dev
, &vin
->notifier
);
186 vin_err(vin
, "Notifier registration failed\n");
194 v4l2_async_notifier_unregister(&vin
->notifier
);
195 of_node_put(vin
->entity
.node
);
201 /* -----------------------------------------------------------------------------
202 * Platform Device Driver
205 static const struct of_device_id rvin_of_id_table
[] = {
206 { .compatible
= "renesas,vin-r8a7794", .data
= (void *)RCAR_GEN2
},
207 { .compatible
= "renesas,vin-r8a7793", .data
= (void *)RCAR_GEN2
},
208 { .compatible
= "renesas,vin-r8a7791", .data
= (void *)RCAR_GEN2
},
209 { .compatible
= "renesas,vin-r8a7790", .data
= (void *)RCAR_GEN2
},
210 { .compatible
= "renesas,vin-r8a7779", .data
= (void *)RCAR_H1
},
211 { .compatible
= "renesas,vin-r8a7778", .data
= (void *)RCAR_M1
},
214 MODULE_DEVICE_TABLE(of
, rvin_of_id_table
);
216 static int rvin_parse_dt(struct rvin_dev
*vin
)
218 const struct of_device_id
*match
;
219 struct v4l2_of_endpoint ep
;
220 struct device_node
*np
;
223 match
= of_match_device(of_match_ptr(rvin_of_id_table
), vin
->dev
);
227 vin
->chip
= (enum chip_id
)match
->data
;
229 np
= of_graph_get_next_endpoint(vin
->dev
->of_node
, NULL
);
231 vin_err(vin
, "Could not find endpoint\n");
235 ret
= v4l2_of_parse_endpoint(np
, &ep
);
237 vin_err(vin
, "Could not parse endpoint\n");
243 vin
->mbus_cfg
.type
= ep
.bus_type
;
245 switch (vin
->mbus_cfg
.type
) {
246 case V4L2_MBUS_PARALLEL
:
247 vin
->mbus_cfg
.flags
= ep
.bus
.parallel
.flags
;
249 case V4L2_MBUS_BT656
:
250 vin
->mbus_cfg
.flags
= 0;
253 vin_err(vin
, "Unknown media bus type\n");
260 static int rcar_vin_probe(struct platform_device
*pdev
)
262 struct rvin_dev
*vin
;
263 struct resource
*mem
;
266 vin
= devm_kzalloc(&pdev
->dev
, sizeof(*vin
), GFP_KERNEL
);
270 vin
->dev
= &pdev
->dev
;
272 ret
= rvin_parse_dt(vin
);
276 mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
280 vin
->base
= devm_ioremap_resource(vin
->dev
, mem
);
281 if (IS_ERR(vin
->base
))
282 return PTR_ERR(vin
->base
);
284 irq
= platform_get_irq(pdev
, 0);
288 ret
= rvin_dma_probe(vin
, irq
);
292 ret
= rvin_graph_init(vin
);
296 pm_suspend_ignore_children(&pdev
->dev
, true);
297 pm_runtime_enable(&pdev
->dev
);
299 platform_set_drvdata(pdev
, vin
);
303 rvin_dma_remove(vin
);
308 static int rcar_vin_remove(struct platform_device
*pdev
)
310 struct rvin_dev
*vin
= platform_get_drvdata(pdev
);
312 pm_runtime_disable(&pdev
->dev
);
314 v4l2_async_notifier_unregister(&vin
->notifier
);
316 rvin_dma_remove(vin
);
321 static struct platform_driver rcar_vin_driver
= {
324 .of_match_table
= rvin_of_id_table
,
326 .probe
= rcar_vin_probe
,
327 .remove
= rcar_vin_remove
,
330 module_platform_driver(rcar_vin_driver
);
332 MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
333 MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver");
334 MODULE_LICENSE("GPL v2");