drm/omap: dss: Acquire next dssdev at probe time
[platform/kernel/linux-starfive.git] / drivers / gpu / drm / omapdrm / displays / encoder-opa362.c
1 /*
2  * OPA362 analog video amplifier with output/power control
3  *
4  * Copyright (C) 2014 Golden Delicious Computers
5  * Author: H. Nikolaus Schaller <hns@goldelico.com>
6  *
7  * based on encoder-tfp410
8  *
9  * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
10  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License version 2 as published by
14  * the Free Software Foundation.
15  */
16
17 #include <linux/gpio/consumer.h>
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/slab.h>
21
22 #include "../dss/omapdss.h"
23
24 struct panel_drv_data {
25         struct omap_dss_device dssdev;
26
27         struct gpio_desc *enable_gpio;
28
29         struct videomode vm;
30 };
31
32 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
33
34 static int opa362_connect(struct omap_dss_device *dssdev,
35                 struct omap_dss_device *dst)
36 {
37         struct omap_dss_device *src;
38         int r;
39
40         src = omapdss_of_find_connected_device(dssdev->dev->of_node, 0);
41         if (IS_ERR(src)) {
42                 dev_err(dssdev->dev, "failed to find video source\n");
43                 return PTR_ERR(src);
44         }
45
46         r = omapdss_device_connect(dssdev->dss, src, dssdev);
47         if (r) {
48                 omapdss_device_put(src);
49                 return r;
50         }
51
52         return 0;
53 }
54
55 static void opa362_disconnect(struct omap_dss_device *dssdev,
56                 struct omap_dss_device *dst)
57 {
58         struct panel_drv_data *ddata = to_panel_data(dssdev);
59         struct omap_dss_device *src = dssdev->src;
60
61         omapdss_device_disconnect(src, &ddata->dssdev);
62
63         omapdss_device_put(src);
64 }
65
66 static int opa362_enable(struct omap_dss_device *dssdev)
67 {
68         struct panel_drv_data *ddata = to_panel_data(dssdev);
69         struct omap_dss_device *src = dssdev->src;
70         int r;
71
72         dev_dbg(dssdev->dev, "enable\n");
73
74         if (!omapdss_device_is_connected(dssdev))
75                 return -ENODEV;
76
77         if (omapdss_device_is_enabled(dssdev))
78                 return 0;
79
80         src->ops->set_timings(src, &ddata->vm);
81
82         r = src->ops->enable(src);
83         if (r)
84                 return r;
85
86         if (ddata->enable_gpio)
87                 gpiod_set_value_cansleep(ddata->enable_gpio, 1);
88
89         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
90
91         return 0;
92 }
93
94 static void opa362_disable(struct omap_dss_device *dssdev)
95 {
96         struct panel_drv_data *ddata = to_panel_data(dssdev);
97         struct omap_dss_device *src = dssdev->src;
98
99         dev_dbg(dssdev->dev, "disable\n");
100
101         if (!omapdss_device_is_enabled(dssdev))
102                 return;
103
104         if (ddata->enable_gpio)
105                 gpiod_set_value_cansleep(ddata->enable_gpio, 0);
106
107         src->ops->disable(src);
108
109         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
110 }
111
112 static void opa362_set_timings(struct omap_dss_device *dssdev,
113                                struct videomode *vm)
114 {
115         struct panel_drv_data *ddata = to_panel_data(dssdev);
116         struct omap_dss_device *src = dssdev->src;
117
118         dev_dbg(dssdev->dev, "set_timings\n");
119
120         ddata->vm = *vm;
121
122         src->ops->set_timings(src, vm);
123 }
124
125 static int opa362_check_timings(struct omap_dss_device *dssdev,
126                                 struct videomode *vm)
127 {
128         struct omap_dss_device *src = dssdev->src;
129
130         dev_dbg(dssdev->dev, "check_timings\n");
131
132         return src->ops->check_timings(src, vm);
133 }
134
135 static const struct omap_dss_device_ops opa362_ops = {
136         .connect        = opa362_connect,
137         .disconnect     = opa362_disconnect,
138         .enable         = opa362_enable,
139         .disable        = opa362_disable,
140         .check_timings  = opa362_check_timings,
141         .set_timings    = opa362_set_timings,
142 };
143
144 static int opa362_probe(struct platform_device *pdev)
145 {
146         struct panel_drv_data *ddata;
147         struct omap_dss_device *dssdev;
148         struct gpio_desc *gpio;
149
150         dev_dbg(&pdev->dev, "probe\n");
151
152         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
153         if (!ddata)
154                 return -ENOMEM;
155
156         platform_set_drvdata(pdev, ddata);
157
158         gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
159         if (IS_ERR(gpio))
160                 return PTR_ERR(gpio);
161
162         ddata->enable_gpio = gpio;
163
164         dssdev = &ddata->dssdev;
165         dssdev->ops = &opa362_ops;
166         dssdev->dev = &pdev->dev;
167         dssdev->type = OMAP_DISPLAY_TYPE_VENC;
168         dssdev->output_type = OMAP_DISPLAY_TYPE_VENC;
169         dssdev->owner = THIS_MODULE;
170         dssdev->of_ports = BIT(1) | BIT(0);
171
172         dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1);
173         if (IS_ERR(dssdev->next)) {
174                 if (PTR_ERR(dssdev->next) != -EPROBE_DEFER)
175                         dev_err(&pdev->dev, "failed to find video sink\n");
176                 return PTR_ERR(dssdev->next);
177         }
178
179         omapdss_device_register(dssdev);
180
181         return 0;
182 }
183
184 static int __exit opa362_remove(struct platform_device *pdev)
185 {
186         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
187         struct omap_dss_device *dssdev = &ddata->dssdev;
188
189         if (dssdev->next)
190                 omapdss_device_put(dssdev->next);
191         omapdss_device_unregister(&ddata->dssdev);
192
193         WARN_ON(omapdss_device_is_enabled(dssdev));
194         if (omapdss_device_is_enabled(dssdev))
195                 opa362_disable(dssdev);
196
197         WARN_ON(omapdss_device_is_connected(dssdev));
198         if (omapdss_device_is_connected(dssdev))
199                 omapdss_device_disconnect(dssdev, dssdev->dst);
200
201         return 0;
202 }
203
204 static const struct of_device_id opa362_of_match[] = {
205         { .compatible = "omapdss,ti,opa362", },
206         {},
207 };
208 MODULE_DEVICE_TABLE(of, opa362_of_match);
209
210 static struct platform_driver opa362_driver = {
211         .probe  = opa362_probe,
212         .remove = __exit_p(opa362_remove),
213         .driver = {
214                 .name   = "amplifier-opa362",
215                 .of_match_table = opa362_of_match,
216                 .suppress_bind_attrs = true,
217         },
218 };
219
220 module_platform_driver(opa362_driver);
221
222 MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
223 MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control");
224 MODULE_LICENSE("GPL v2");