• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Driver for Renesas R-Car VIN
3  *
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
8  *
9  * Based on the soc-camera rcar_vin driver
10  *
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.
15  */
16 
17 #include <linux/module.h>
18 #include <linux/of.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>
23 
24 #include <media/v4l2-fwnode.h>
25 
26 #include "rcar-vin.h"
27 
28 /* -----------------------------------------------------------------------------
29  * Async notifier
30  */
31 
32 #define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier)
33 
rvin_find_pad(struct v4l2_subdev * sd,int direction)34 static int rvin_find_pad(struct v4l2_subdev *sd, int direction)
35 {
36 	unsigned int pad;
37 
38 	if (sd->entity.num_pads <= 1)
39 		return 0;
40 
41 	for (pad = 0; pad < sd->entity.num_pads; pad++)
42 		if (sd->entity.pads[pad].flags & direction)
43 			return pad;
44 
45 	return -EINVAL;
46 }
47 
rvin_mbus_supported(struct rvin_graph_entity * entity)48 static bool rvin_mbus_supported(struct rvin_graph_entity *entity)
49 {
50 	struct v4l2_subdev *sd = entity->subdev;
51 	struct v4l2_subdev_mbus_code_enum code = {
52 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
53 	};
54 
55 	code.index = 0;
56 	code.pad = entity->source_pad;
57 	while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) {
58 		code.index++;
59 		switch (code.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;
65 			return true;
66 		default:
67 			break;
68 		}
69 	}
70 
71 	return false;
72 }
73 
rvin_digital_notify_complete(struct v4l2_async_notifier * notifier)74 static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)
75 {
76 	struct rvin_dev *vin = notifier_to_vin(notifier);
77 	int ret;
78 
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);
83 		return -EINVAL;
84 	}
85 
86 	vin_dbg(vin, "Found media bus format for %s: %d\n",
87 		vin->digital.subdev->name, vin->digital.code);
88 
89 	ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
90 	if (ret < 0) {
91 		vin_err(vin, "Failed to register subdev nodes\n");
92 		return ret;
93 	}
94 
95 	return rvin_v4l2_probe(vin);
96 }
97 
rvin_digital_notify_unbind(struct v4l2_async_notifier * notifier,struct v4l2_subdev * subdev,struct v4l2_async_subdev * asd)98 static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
99 				       struct v4l2_subdev *subdev,
100 				       struct v4l2_async_subdev *asd)
101 {
102 	struct rvin_dev *vin = notifier_to_vin(notifier);
103 
104 	vin_dbg(vin, "unbind digital subdev %s\n", subdev->name);
105 	rvin_v4l2_remove(vin);
106 	vin->digital.subdev = NULL;
107 }
108 
rvin_digital_notify_bound(struct v4l2_async_notifier * notifier,struct v4l2_subdev * subdev,struct v4l2_async_subdev * asd)109 static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
110 				     struct v4l2_subdev *subdev,
111 				     struct v4l2_async_subdev *asd)
112 {
113 	struct rvin_dev *vin = notifier_to_vin(notifier);
114 	int ret;
115 
116 	v4l2_set_subdev_hostdata(subdev, vin);
117 
118 	/* Find source and sink pad of remote subdevice */
119 
120 	ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
121 	if (ret < 0)
122 		return ret;
123 	vin->digital.source_pad = ret;
124 
125 	ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
126 	vin->digital.sink_pad = ret < 0 ? 0 : ret;
127 
128 	vin->digital.subdev = subdev;
129 
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);
133 
134 	return 0;
135 }
136 
rvin_digitial_parse_v4l2(struct rvin_dev * vin,struct device_node * ep,struct v4l2_mbus_config * mbus_cfg)137 static int rvin_digitial_parse_v4l2(struct rvin_dev *vin,
138 				    struct device_node *ep,
139 				    struct v4l2_mbus_config *mbus_cfg)
140 {
141 	struct v4l2_fwnode_endpoint v4l2_ep;
142 	int ret;
143 
144 	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep);
145 	if (ret) {
146 		vin_err(vin, "Could not parse v4l2 endpoint\n");
147 		return -EINVAL;
148 	}
149 
150 	mbus_cfg->type = v4l2_ep.bus_type;
151 
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;
156 		break;
157 	case V4L2_MBUS_BT656:
158 		vin_dbg(vin, "Found BT656 media bus\n");
159 		mbus_cfg->flags = 0;
160 		break;
161 	default:
162 		vin_err(vin, "Unknown media bus type\n");
163 		return -EINVAL;
164 	}
165 
166 	return 0;
167 }
168 
rvin_digital_graph_parse(struct rvin_dev * vin)169 static int rvin_digital_graph_parse(struct rvin_dev *vin)
170 {
171 	struct device_node *ep, *np;
172 	int ret;
173 
174 	vin->digital.asd.match.fwnode.fwnode = NULL;
175 	vin->digital.subdev = NULL;
176 
177 	/*
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
180 	 */
181 	ep = of_graph_get_endpoint_by_regs(vin->dev->of_node, 0, 0);
182 	if (!ep)
183 		return 0;
184 
185 	np = of_graph_get_remote_port_parent(ep);
186 	if (!np) {
187 		vin_err(vin, "No remote parent for digital input\n");
188 		of_node_put(ep);
189 		return -EINVAL;
190 	}
191 	of_node_put(np);
192 
193 	ret = rvin_digitial_parse_v4l2(vin, ep, &vin->digital.mbus_cfg);
194 	of_node_put(ep);
195 	if (ret)
196 		return ret;
197 
198 	vin->digital.asd.match.fwnode.fwnode = of_fwnode_handle(np);
199 	vin->digital.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
200 
201 	return 0;
202 }
203 
rvin_digital_graph_init(struct rvin_dev * vin)204 static int rvin_digital_graph_init(struct rvin_dev *vin)
205 {
206 	struct v4l2_async_subdev **subdevs = NULL;
207 	int ret;
208 
209 	ret = rvin_digital_graph_parse(vin);
210 	if (ret)
211 		return ret;
212 
213 	if (!vin->digital.asd.match.fwnode.fwnode) {
214 		vin_dbg(vin, "No digital subdevice found\n");
215 		return -ENODEV;
216 	}
217 
218 	/* Register the subdevices notifier. */
219 	subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL);
220 	if (subdevs == NULL)
221 		return -ENOMEM;
222 
223 	subdevs[0] = &vin->digital.asd;
224 
225 	vin_dbg(vin, "Found digital subdevice %pOF\n",
226 		to_of_node(subdevs[0]->match.fwnode.fwnode));
227 
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;
233 
234 	ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
235 	if (ret < 0) {
236 		vin_err(vin, "Notifier registration failed\n");
237 		return ret;
238 	}
239 
240 	return 0;
241 }
242 
243 /* -----------------------------------------------------------------------------
244  * Platform Device Driver
245  */
246 
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 },
255 	{ },
256 };
257 MODULE_DEVICE_TABLE(of, rvin_of_id_table);
258 
rcar_vin_probe(struct platform_device * pdev)259 static int rcar_vin_probe(struct platform_device *pdev)
260 {
261 	const struct of_device_id *match;
262 	struct rvin_dev *vin;
263 	struct resource *mem;
264 	int irq, ret;
265 
266 	vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL);
267 	if (!vin)
268 		return -ENOMEM;
269 
270 	match = of_match_device(of_match_ptr(rvin_of_id_table), &pdev->dev);
271 	if (!match)
272 		return -ENODEV;
273 
274 	vin->dev = &pdev->dev;
275 	vin->chip = (enum chip_id)match->data;
276 
277 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
278 	if (mem == NULL)
279 		return -EINVAL;
280 
281 	vin->base = devm_ioremap_resource(vin->dev, mem);
282 	if (IS_ERR(vin->base))
283 		return PTR_ERR(vin->base);
284 
285 	irq = platform_get_irq(pdev, 0);
286 	if (irq < 0)
287 		return irq;
288 
289 	ret = rvin_dma_probe(vin, irq);
290 	if (ret)
291 		return ret;
292 
293 	ret = rvin_digital_graph_init(vin);
294 	if (ret < 0)
295 		goto error;
296 
297 	pm_suspend_ignore_children(&pdev->dev, true);
298 	pm_runtime_enable(&pdev->dev);
299 
300 	platform_set_drvdata(pdev, vin);
301 
302 	return 0;
303 error:
304 	rvin_dma_remove(vin);
305 
306 	return ret;
307 }
308 
rcar_vin_remove(struct platform_device * pdev)309 static int rcar_vin_remove(struct platform_device *pdev)
310 {
311 	struct rvin_dev *vin = platform_get_drvdata(pdev);
312 
313 	pm_runtime_disable(&pdev->dev);
314 
315 	v4l2_async_notifier_unregister(&vin->notifier);
316 
317 	rvin_dma_remove(vin);
318 
319 	return 0;
320 }
321 
322 static struct platform_driver rcar_vin_driver = {
323 	.driver = {
324 		.name = "rcar-vin",
325 		.of_match_table = rvin_of_id_table,
326 	},
327 	.probe = rcar_vin_probe,
328 	.remove = rcar_vin_remove,
329 };
330 
331 module_platform_driver(rcar_vin_driver);
332 
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");
336