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