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