GNU Linux-libre 4.9.304-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-of.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 bool rvin_mbus_supported(struct rvin_graph_entity *entity)
35 {
36         struct v4l2_subdev *sd = entity->subdev;
37         struct v4l2_subdev_mbus_code_enum code = {
38                 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
39         };
40
41         code.index = 0;
42         while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) {
43                 code.index++;
44                 switch (code.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;
50                         return true;
51                 default:
52                         break;
53                 }
54         }
55
56         return false;
57 }
58
59 static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)
60 {
61         struct rvin_dev *vin = notifier_to_vin(notifier);
62         int ret;
63
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);
68                 return -EINVAL;
69         }
70
71         vin_dbg(vin, "Found media bus format for %s: %d\n",
72                 vin->digital.subdev->name, vin->digital.code);
73
74         ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
75         if (ret < 0) {
76                 vin_err(vin, "Failed to register subdev nodes\n");
77                 return ret;
78         }
79
80         return rvin_v4l2_probe(vin);
81 }
82
83 static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
84                                        struct v4l2_subdev *subdev,
85                                        struct v4l2_async_subdev *asd)
86 {
87         struct rvin_dev *vin = notifier_to_vin(notifier);
88
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;
93                 return;
94         }
95
96         vin_err(vin, "no entity for subdev %s to unbind\n", subdev->name);
97 }
98
99 static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
100                                      struct v4l2_subdev *subdev,
101                                      struct v4l2_async_subdev *asd)
102 {
103         struct rvin_dev *vin = notifier_to_vin(notifier);
104
105         v4l2_set_subdev_hostdata(subdev, vin);
106
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;
110                 return 0;
111         }
112
113         vin_err(vin, "no entity for subdev %s to bind\n", subdev->name);
114         return -EINVAL;
115 }
116
117 static int rvin_digitial_parse_v4l2(struct rvin_dev *vin,
118                                     struct device_node *ep,
119                                     struct v4l2_mbus_config *mbus_cfg)
120 {
121         struct v4l2_of_endpoint v4l2_ep;
122         int ret;
123
124         ret = v4l2_of_parse_endpoint(ep, &v4l2_ep);
125         if (ret) {
126                 vin_err(vin, "Could not parse v4l2 endpoint\n");
127                 return -EINVAL;
128         }
129
130         mbus_cfg->type = v4l2_ep.bus_type;
131
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;
136                 break;
137         case V4L2_MBUS_BT656:
138                 vin_dbg(vin, "Found BT656 media bus\n");
139                 mbus_cfg->flags = 0;
140                 break;
141         default:
142                 vin_err(vin, "Unknown media bus type\n");
143                 return -EINVAL;
144         }
145
146         return 0;
147 }
148
149 static int rvin_digital_graph_parse(struct rvin_dev *vin)
150 {
151         struct device_node *ep, *np;
152         int ret;
153
154         vin->digital.asd.match.of.node = NULL;
155         vin->digital.subdev = NULL;
156
157         /*
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
160          */
161         ep = of_graph_get_endpoint_by_regs(vin->dev->of_node, 0, 0);
162         if (!ep)
163                 return 0;
164
165         np = of_graph_get_remote_port_parent(ep);
166         if (!np) {
167                 vin_err(vin, "No remote parent for digital input\n");
168                 of_node_put(ep);
169                 return -EINVAL;
170         }
171         of_node_put(np);
172
173         ret = rvin_digitial_parse_v4l2(vin, ep, &vin->digital.mbus_cfg);
174         of_node_put(ep);
175         if (ret)
176                 return ret;
177
178         vin->digital.asd.match.of.node = np;
179         vin->digital.asd.match_type = V4L2_ASYNC_MATCH_OF;
180
181         return 0;
182 }
183
184 static int rvin_digital_graph_init(struct rvin_dev *vin)
185 {
186         struct v4l2_async_subdev **subdevs = NULL;
187         int ret;
188
189         ret = rvin_digital_graph_parse(vin);
190         if (ret)
191                 return ret;
192
193         if (!vin->digital.asd.match.of.node) {
194                 vin_dbg(vin, "No digital subdevice found\n");
195                 return -ENODEV;
196         }
197
198         /* Register the subdevices notifier. */
199         subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL);
200         if (subdevs == NULL)
201                 return -ENOMEM;
202
203         subdevs[0] = &vin->digital.asd;
204
205         vin_dbg(vin, "Found digital subdevice %s\n",
206                 of_node_full_name(subdevs[0]->match.of.node));
207
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;
213
214         ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
215         if (ret < 0) {
216                 vin_err(vin, "Notifier registration failed\n");
217                 return ret;
218         }
219
220         return 0;
221 }
222
223 /* -----------------------------------------------------------------------------
224  * Platform Device Driver
225  */
226
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 },
235         { },
236 };
237 MODULE_DEVICE_TABLE(of, rvin_of_id_table);
238
239 static int rcar_vin_probe(struct platform_device *pdev)
240 {
241         const struct of_device_id *match;
242         struct rvin_dev *vin;
243         struct resource *mem;
244         int irq, ret;
245
246         vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL);
247         if (!vin)
248                 return -ENOMEM;
249
250         match = of_match_device(of_match_ptr(rvin_of_id_table), &pdev->dev);
251         if (!match)
252                 return -ENODEV;
253
254         vin->dev = &pdev->dev;
255         vin->chip = (enum chip_id)match->data;
256
257         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
258         if (mem == NULL)
259                 return -EINVAL;
260
261         vin->base = devm_ioremap_resource(vin->dev, mem);
262         if (IS_ERR(vin->base))
263                 return PTR_ERR(vin->base);
264
265         irq = platform_get_irq(pdev, 0);
266         if (irq < 0)
267                 return irq;
268
269         ret = rvin_dma_probe(vin, irq);
270         if (ret)
271                 return ret;
272
273         ret = rvin_digital_graph_init(vin);
274         if (ret < 0)
275                 goto error;
276
277         pm_suspend_ignore_children(&pdev->dev, true);
278         pm_runtime_enable(&pdev->dev);
279
280         platform_set_drvdata(pdev, vin);
281
282         return 0;
283 error:
284         rvin_dma_remove(vin);
285
286         return ret;
287 }
288
289 static int rcar_vin_remove(struct platform_device *pdev)
290 {
291         struct rvin_dev *vin = platform_get_drvdata(pdev);
292
293         pm_runtime_disable(&pdev->dev);
294
295         v4l2_async_notifier_unregister(&vin->notifier);
296
297         rvin_dma_remove(vin);
298
299         return 0;
300 }
301
302 static struct platform_driver rcar_vin_driver = {
303         .driver = {
304                 .name = "rcar-vin",
305                 .of_match_table = rvin_of_id_table,
306         },
307         .probe = rcar_vin_probe,
308         .remove = rcar_vin_remove,
309 };
310
311 module_platform_driver(rcar_vin_driver);
312
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");