GNU Linux-libre 4.14.254-gnu1
[releases.git] / drivers / media / platform / rcar-vin / rcar-core.c
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
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
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
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
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
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
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
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
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
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
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");