1 /*
2  * TPD12S015 HDMI ESD protection & level shifter chip driver
3  *
4  * Copyright (C) 2013 Texas Instruments
5  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published by
9  * the Free Software Foundation.
10  */
11 
12 #include <linux/completion.h>
13 #include <linux/delay.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include <linux/gpio.h>
17 #include <linux/platform_device.h>
18 #include <linux/of_gpio.h>
19 
20 #include <video/omapdss.h>
21 #include <video/omap-panel-data.h>
22 
23 struct panel_drv_data {
24 	struct omap_dss_device dssdev;
25 	struct omap_dss_device *in;
26 
27 	int ct_cp_hpd_gpio;
28 	int ls_oe_gpio;
29 	int hpd_gpio;
30 
31 	struct omap_video_timings timings;
32 };
33 
34 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
35 
tpd_connect(struct omap_dss_device * dssdev,struct omap_dss_device * dst)36 static int tpd_connect(struct omap_dss_device *dssdev,
37 		struct omap_dss_device *dst)
38 {
39 	struct panel_drv_data *ddata = to_panel_data(dssdev);
40 	struct omap_dss_device *in = ddata->in;
41 	int r;
42 
43 	r = in->ops.hdmi->connect(in, dssdev);
44 	if (r)
45 		return r;
46 
47 	dst->src = dssdev;
48 	dssdev->dst = dst;
49 
50 	gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
51 	/* DC-DC converter needs at max 300us to get to 90% of 5V */
52 	udelay(300);
53 
54 	return 0;
55 }
56 
tpd_disconnect(struct omap_dss_device * dssdev,struct omap_dss_device * dst)57 static void tpd_disconnect(struct omap_dss_device *dssdev,
58 		struct omap_dss_device *dst)
59 {
60 	struct panel_drv_data *ddata = to_panel_data(dssdev);
61 	struct omap_dss_device *in = ddata->in;
62 
63 	WARN_ON(dst != dssdev->dst);
64 
65 	if (dst != dssdev->dst)
66 		return;
67 
68 	gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
69 
70 	dst->src = NULL;
71 	dssdev->dst = NULL;
72 
73 	in->ops.hdmi->disconnect(in, &ddata->dssdev);
74 }
75 
tpd_enable(struct omap_dss_device * dssdev)76 static int tpd_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 	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
83 		return 0;
84 
85 	in->ops.hdmi->set_timings(in, &ddata->timings);
86 
87 	r = in->ops.hdmi->enable(in);
88 	if (r)
89 		return r;
90 
91 	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
92 
93 	return r;
94 }
95 
tpd_disable(struct omap_dss_device * dssdev)96 static void tpd_disable(struct omap_dss_device *dssdev)
97 {
98 	struct panel_drv_data *ddata = to_panel_data(dssdev);
99 	struct omap_dss_device *in = ddata->in;
100 
101 	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
102 		return;
103 
104 	in->ops.hdmi->disable(in);
105 
106 	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
107 }
108 
tpd_set_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)109 static void tpd_set_timings(struct omap_dss_device *dssdev,
110 		struct omap_video_timings *timings)
111 {
112 	struct panel_drv_data *ddata = to_panel_data(dssdev);
113 	struct omap_dss_device *in = ddata->in;
114 
115 	ddata->timings = *timings;
116 	dssdev->panel.timings = *timings;
117 
118 	in->ops.hdmi->set_timings(in, timings);
119 }
120 
tpd_get_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)121 static void tpd_get_timings(struct omap_dss_device *dssdev,
122 		struct omap_video_timings *timings)
123 {
124 	struct panel_drv_data *ddata = to_panel_data(dssdev);
125 
126 	*timings = ddata->timings;
127 }
128 
tpd_check_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)129 static int tpd_check_timings(struct omap_dss_device *dssdev,
130 		struct omap_video_timings *timings)
131 {
132 	struct panel_drv_data *ddata = to_panel_data(dssdev);
133 	struct omap_dss_device *in = ddata->in;
134 	int r;
135 
136 	r = in->ops.hdmi->check_timings(in, timings);
137 
138 	return r;
139 }
140 
tpd_read_edid(struct omap_dss_device * dssdev,u8 * edid,int len)141 static int tpd_read_edid(struct omap_dss_device *dssdev,
142 		u8 *edid, int len)
143 {
144 	struct panel_drv_data *ddata = to_panel_data(dssdev);
145 	struct omap_dss_device *in = ddata->in;
146 	int r;
147 
148 	if (!gpio_get_value_cansleep(ddata->hpd_gpio))
149 		return -ENODEV;
150 
151 	if (gpio_is_valid(ddata->ls_oe_gpio))
152 		gpio_set_value_cansleep(ddata->ls_oe_gpio, 1);
153 
154 	r = in->ops.hdmi->read_edid(in, edid, len);
155 
156 	if (gpio_is_valid(ddata->ls_oe_gpio))
157 		gpio_set_value_cansleep(ddata->ls_oe_gpio, 0);
158 
159 	return r;
160 }
161 
tpd_detect(struct omap_dss_device * dssdev)162 static bool tpd_detect(struct omap_dss_device *dssdev)
163 {
164 	struct panel_drv_data *ddata = to_panel_data(dssdev);
165 
166 	return gpio_get_value_cansleep(ddata->hpd_gpio);
167 }
168 
tpd_set_infoframe(struct omap_dss_device * dssdev,const struct hdmi_avi_infoframe * avi)169 static int tpd_set_infoframe(struct omap_dss_device *dssdev,
170 		const struct hdmi_avi_infoframe *avi)
171 {
172 	struct panel_drv_data *ddata = to_panel_data(dssdev);
173 	struct omap_dss_device *in = ddata->in;
174 
175 	return in->ops.hdmi->set_infoframe(in, avi);
176 }
177 
tpd_set_hdmi_mode(struct omap_dss_device * dssdev,bool hdmi_mode)178 static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
179 		bool hdmi_mode)
180 {
181 	struct panel_drv_data *ddata = to_panel_data(dssdev);
182 	struct omap_dss_device *in = ddata->in;
183 
184 	return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
185 }
186 
187 static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
188 	.connect		= tpd_connect,
189 	.disconnect		= tpd_disconnect,
190 
191 	.enable			= tpd_enable,
192 	.disable		= tpd_disable,
193 
194 	.check_timings		= tpd_check_timings,
195 	.set_timings		= tpd_set_timings,
196 	.get_timings		= tpd_get_timings,
197 
198 	.read_edid		= tpd_read_edid,
199 	.detect			= tpd_detect,
200 	.set_infoframe		= tpd_set_infoframe,
201 	.set_hdmi_mode		= tpd_set_hdmi_mode,
202 };
203 
tpd_probe_pdata(struct platform_device * pdev)204 static int tpd_probe_pdata(struct platform_device *pdev)
205 {
206 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
207 	struct encoder_tpd12s015_platform_data *pdata;
208 	struct omap_dss_device *dssdev, *in;
209 
210 	pdata = dev_get_platdata(&pdev->dev);
211 
212 	ddata->ct_cp_hpd_gpio = pdata->ct_cp_hpd_gpio;
213 	ddata->ls_oe_gpio = pdata->ls_oe_gpio;
214 	ddata->hpd_gpio = pdata->hpd_gpio;
215 
216 	in = omap_dss_find_output(pdata->source);
217 	if (in == NULL) {
218 		dev_err(&pdev->dev, "Failed to find video source\n");
219 		return -ENODEV;
220 	}
221 
222 	ddata->in = in;
223 
224 	dssdev = &ddata->dssdev;
225 	dssdev->name = pdata->name;
226 
227 	return 0;
228 }
229 
tpd_probe_of(struct platform_device * pdev)230 static int tpd_probe_of(struct platform_device *pdev)
231 {
232 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
233 	struct device_node *node = pdev->dev.of_node;
234 	struct omap_dss_device *in;
235 	int gpio;
236 
237 	/* CT CP HPD GPIO */
238 	gpio = of_get_gpio(node, 0);
239 	if (!gpio_is_valid(gpio)) {
240 		dev_err(&pdev->dev, "failed to parse CT CP HPD gpio\n");
241 		return gpio;
242 	}
243 	ddata->ct_cp_hpd_gpio = gpio;
244 
245 	/* LS OE GPIO */
246 	gpio = of_get_gpio(node, 1);
247 	if (gpio_is_valid(gpio) || gpio == -ENOENT) {
248 		ddata->ls_oe_gpio = gpio;
249 	} else {
250 		dev_err(&pdev->dev, "failed to parse LS OE gpio\n");
251 		return gpio;
252 	}
253 
254 	/* HPD GPIO */
255 	gpio = of_get_gpio(node, 2);
256 	if (!gpio_is_valid(gpio)) {
257 		dev_err(&pdev->dev, "failed to parse HPD gpio\n");
258 		return gpio;
259 	}
260 	ddata->hpd_gpio = gpio;
261 
262 	in = omapdss_of_find_source_for_first_ep(node);
263 	if (IS_ERR(in)) {
264 		dev_err(&pdev->dev, "failed to find video source\n");
265 		return PTR_ERR(in);
266 	}
267 
268 	ddata->in = in;
269 
270 	return 0;
271 }
272 
tpd_probe(struct platform_device * pdev)273 static int tpd_probe(struct platform_device *pdev)
274 {
275 	struct omap_dss_device *in, *dssdev;
276 	struct panel_drv_data *ddata;
277 	int r;
278 
279 	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
280 	if (!ddata)
281 		return -ENOMEM;
282 
283 	platform_set_drvdata(pdev, ddata);
284 
285 	if (dev_get_platdata(&pdev->dev)) {
286 		r = tpd_probe_pdata(pdev);
287 		if (r)
288 			return r;
289 	} else if (pdev->dev.of_node) {
290 		r = tpd_probe_of(pdev);
291 		if (r)
292 			return r;
293 	} else {
294 		return -ENODEV;
295 	}
296 
297 	r = devm_gpio_request_one(&pdev->dev, ddata->ct_cp_hpd_gpio,
298 			GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd");
299 	if (r)
300 		goto err_gpio;
301 
302 	if (gpio_is_valid(ddata->ls_oe_gpio)) {
303 		r = devm_gpio_request_one(&pdev->dev, ddata->ls_oe_gpio,
304 				GPIOF_OUT_INIT_LOW, "hdmi_ls_oe");
305 		if (r)
306 			goto err_gpio;
307 	}
308 
309 	r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
310 			GPIOF_DIR_IN, "hdmi_hpd");
311 	if (r)
312 		goto err_gpio;
313 
314 	dssdev = &ddata->dssdev;
315 	dssdev->ops.hdmi = &tpd_hdmi_ops;
316 	dssdev->dev = &pdev->dev;
317 	dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
318 	dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
319 	dssdev->owner = THIS_MODULE;
320 	dssdev->port_num = 1;
321 
322 	in = ddata->in;
323 
324 	r = omapdss_register_output(dssdev);
325 	if (r) {
326 		dev_err(&pdev->dev, "Failed to register output\n");
327 		goto err_reg;
328 	}
329 
330 	return 0;
331 err_reg:
332 err_gpio:
333 	omap_dss_put_device(ddata->in);
334 	return r;
335 }
336 
tpd_remove(struct platform_device * pdev)337 static int __exit tpd_remove(struct platform_device *pdev)
338 {
339 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
340 	struct omap_dss_device *dssdev = &ddata->dssdev;
341 	struct omap_dss_device *in = ddata->in;
342 
343 	omapdss_unregister_output(&ddata->dssdev);
344 
345 	WARN_ON(omapdss_device_is_enabled(dssdev));
346 	if (omapdss_device_is_enabled(dssdev))
347 		tpd_disable(dssdev);
348 
349 	WARN_ON(omapdss_device_is_connected(dssdev));
350 	if (omapdss_device_is_connected(dssdev))
351 		tpd_disconnect(dssdev, dssdev->dst);
352 
353 	omap_dss_put_device(in);
354 
355 	return 0;
356 }
357 
358 static const struct of_device_id tpd_of_match[] = {
359 	{ .compatible = "omapdss,ti,tpd12s015", },
360 	{},
361 };
362 
363 MODULE_DEVICE_TABLE(of, tpd_of_match);
364 
365 static struct platform_driver tpd_driver = {
366 	.probe	= tpd_probe,
367 	.remove	= __exit_p(tpd_remove),
368 	.driver	= {
369 		.name	= "tpd12s015",
370 		.of_match_table = tpd_of_match,
371 		.suppress_bind_attrs = true,
372 	},
373 };
374 
375 module_platform_driver(tpd_driver);
376 
377 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
378 MODULE_DESCRIPTION("TPD12S015 driver");
379 MODULE_LICENSE("GPL");
380