treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500
[linux-2.6-microblaze.git] / drivers / video / fbdev / omap2 / omapfb / displays / encoder-opa362.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * OPA362 analog video amplifier with output/power control
4  *
5  * Copyright (C) 2014 Golden Delicious Computers
6  * Author: H. Nikolaus Schaller <hns@goldelico.com>
7  *
8  * based on encoder-tfp410
9  *
10  * Copyright (C) 2013 Texas Instruments
11  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
12  */
13
14 #include <linux/gpio.h>
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
18 #include <linux/of_gpio.h>
19
20 #include <video/omapfb_dss.h>
21
22 struct panel_drv_data {
23         struct omap_dss_device dssdev;
24         struct omap_dss_device *in;
25
26         struct gpio_desc *enable_gpio;
27
28         struct omap_video_timings timings;
29 };
30
31 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
32
33 static int opa362_connect(struct omap_dss_device *dssdev,
34                 struct omap_dss_device *dst)
35 {
36         struct panel_drv_data *ddata = to_panel_data(dssdev);
37         struct omap_dss_device *in = ddata->in;
38         int r;
39
40         dev_dbg(dssdev->dev, "connect\n");
41
42         if (omapdss_device_is_connected(dssdev))
43                 return -EBUSY;
44
45         r = in->ops.atv->connect(in, dssdev);
46         if (r)
47                 return r;
48
49         dst->src = dssdev;
50         dssdev->dst = dst;
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 *in = ddata->in;
60
61         dev_dbg(dssdev->dev, "disconnect\n");
62
63         WARN_ON(!omapdss_device_is_connected(dssdev));
64         if (!omapdss_device_is_connected(dssdev))
65                 return;
66
67         WARN_ON(dst != dssdev->dst);
68         if (dst != dssdev->dst)
69                 return;
70
71         dst->src = NULL;
72         dssdev->dst = NULL;
73
74         in->ops.atv->disconnect(in, &ddata->dssdev);
75 }
76
77 static int opa362_enable(struct omap_dss_device *dssdev)
78 {
79         struct panel_drv_data *ddata = to_panel_data(dssdev);
80         struct omap_dss_device *in = ddata->in;
81         int r;
82
83         dev_dbg(dssdev->dev, "enable\n");
84
85         if (!omapdss_device_is_connected(dssdev))
86                 return -ENODEV;
87
88         if (omapdss_device_is_enabled(dssdev))
89                 return 0;
90
91         in->ops.atv->set_timings(in, &ddata->timings);
92
93         r = in->ops.atv->enable(in);
94         if (r)
95                 return r;
96
97         if (ddata->enable_gpio)
98                 gpiod_set_value_cansleep(ddata->enable_gpio, 1);
99
100         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
101
102         return 0;
103 }
104
105 static void opa362_disable(struct omap_dss_device *dssdev)
106 {
107         struct panel_drv_data *ddata = to_panel_data(dssdev);
108         struct omap_dss_device *in = ddata->in;
109
110         dev_dbg(dssdev->dev, "disable\n");
111
112         if (!omapdss_device_is_enabled(dssdev))
113                 return;
114
115         if (ddata->enable_gpio)
116                 gpiod_set_value_cansleep(ddata->enable_gpio, 0);
117
118         in->ops.atv->disable(in);
119
120         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
121 }
122
123 static void opa362_set_timings(struct omap_dss_device *dssdev,
124                 struct omap_video_timings *timings)
125 {
126         struct panel_drv_data *ddata = to_panel_data(dssdev);
127         struct omap_dss_device *in = ddata->in;
128
129         dev_dbg(dssdev->dev, "set_timings\n");
130
131         ddata->timings = *timings;
132         dssdev->panel.timings = *timings;
133
134         in->ops.atv->set_timings(in, timings);
135 }
136
137 static void opa362_get_timings(struct omap_dss_device *dssdev,
138                 struct omap_video_timings *timings)
139 {
140         struct panel_drv_data *ddata = to_panel_data(dssdev);
141
142         dev_dbg(dssdev->dev, "get_timings\n");
143
144         *timings = ddata->timings;
145 }
146
147 static int opa362_check_timings(struct omap_dss_device *dssdev,
148                 struct omap_video_timings *timings)
149 {
150         struct panel_drv_data *ddata = to_panel_data(dssdev);
151         struct omap_dss_device *in = ddata->in;
152
153         dev_dbg(dssdev->dev, "check_timings\n");
154
155         return in->ops.atv->check_timings(in, timings);
156 }
157
158 static void opa362_set_type(struct omap_dss_device *dssdev,
159                 enum omap_dss_venc_type type)
160 {
161         /* we can only drive a COMPOSITE output */
162         WARN_ON(type != OMAP_DSS_VENC_TYPE_COMPOSITE);
163
164 }
165
166 static const struct omapdss_atv_ops opa362_atv_ops = {
167         .connect        = opa362_connect,
168         .disconnect     = opa362_disconnect,
169
170         .enable         = opa362_enable,
171         .disable        = opa362_disable,
172
173         .check_timings  = opa362_check_timings,
174         .set_timings    = opa362_set_timings,
175         .get_timings    = opa362_get_timings,
176
177         .set_type       = opa362_set_type,
178 };
179
180 static int opa362_probe(struct platform_device *pdev)
181 {
182         struct device_node *node = pdev->dev.of_node;
183         struct panel_drv_data *ddata;
184         struct omap_dss_device *dssdev, *in;
185         struct gpio_desc *gpio;
186         int r;
187
188         dev_dbg(&pdev->dev, "probe\n");
189
190         if (node == NULL) {
191                 dev_err(&pdev->dev, "Unable to find device tree\n");
192                 return -EINVAL;
193         }
194
195         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
196         if (!ddata)
197                 return -ENOMEM;
198
199         platform_set_drvdata(pdev, ddata);
200
201         gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
202         if (IS_ERR(gpio))
203                 return PTR_ERR(gpio);
204
205         ddata->enable_gpio = gpio;
206
207         in = omapdss_of_find_source_for_first_ep(node);
208         if (IS_ERR(in)) {
209                 dev_err(&pdev->dev, "failed to find video source\n");
210                 return PTR_ERR(in);
211         }
212
213         ddata->in = in;
214
215         dssdev = &ddata->dssdev;
216         dssdev->ops.atv = &opa362_atv_ops;
217         dssdev->dev = &pdev->dev;
218         dssdev->type = OMAP_DISPLAY_TYPE_VENC;
219         dssdev->output_type = OMAP_DISPLAY_TYPE_VENC;
220         dssdev->owner = THIS_MODULE;
221
222         r = omapdss_register_output(dssdev);
223         if (r) {
224                 dev_err(&pdev->dev, "Failed to register output\n");
225                 goto err_reg;
226         }
227
228         return 0;
229 err_reg:
230         omap_dss_put_device(ddata->in);
231         return r;
232 }
233
234 static int __exit opa362_remove(struct platform_device *pdev)
235 {
236         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
237         struct omap_dss_device *dssdev = &ddata->dssdev;
238         struct omap_dss_device *in = ddata->in;
239
240         omapdss_unregister_output(&ddata->dssdev);
241
242         WARN_ON(omapdss_device_is_enabled(dssdev));
243         if (omapdss_device_is_enabled(dssdev))
244                 opa362_disable(dssdev);
245
246         WARN_ON(omapdss_device_is_connected(dssdev));
247         if (omapdss_device_is_connected(dssdev))
248                 opa362_disconnect(dssdev, dssdev->dst);
249
250         omap_dss_put_device(in);
251
252         return 0;
253 }
254
255 static const struct of_device_id opa362_of_match[] = {
256         { .compatible = "omapdss,ti,opa362", },
257         {},
258 };
259 MODULE_DEVICE_TABLE(of, opa362_of_match);
260
261 static struct platform_driver opa362_driver = {
262         .probe  = opa362_probe,
263         .remove = __exit_p(opa362_remove),
264         .driver = {
265                 .name   = "amplifier-opa362",
266                 .of_match_table = opa362_of_match,
267                 .suppress_bind_attrs = true,
268         },
269 };
270
271 module_platform_driver(opa362_driver);
272
273 MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
274 MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control");
275 MODULE_LICENSE("GPL v2");