Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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/platform_device.h>
 17#include <linux/gpio/consumer.h>
 18
 19#include <video/omapdss.h>
 20#include <video/omap-panel-data.h>
 21
 22struct panel_drv_data {
 23	struct omap_dss_device dssdev;
 24	struct omap_dss_device *in;
 25
 26	struct gpio_desc *ct_cp_hpd_gpio;
 27	struct gpio_desc *ls_oe_gpio;
 28	struct gpio_desc *hpd_gpio;
 29
 30	struct omap_video_timings timings;
 31};
 32
 33#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
 34
 35static int tpd_connect(struct omap_dss_device *dssdev,
 36		struct omap_dss_device *dst)
 37{
 38	struct panel_drv_data *ddata = to_panel_data(dssdev);
 39	struct omap_dss_device *in = ddata->in;
 40	int r;
 41
 42	r = in->ops.hdmi->connect(in, dssdev);
 43	if (r)
 44		return r;
 45
 46	dst->src = dssdev;
 47	dssdev->dst = dst;
 48
 49	gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
 50	/* DC-DC converter needs at max 300us to get to 90% of 5V */
 51	udelay(300);
 52
 53	return 0;
 54}
 55
 56static void tpd_disconnect(struct omap_dss_device *dssdev,
 57		struct omap_dss_device *dst)
 58{
 59	struct panel_drv_data *ddata = to_panel_data(dssdev);
 60	struct omap_dss_device *in = ddata->in;
 61
 62	WARN_ON(dst != dssdev->dst);
 63
 64	if (dst != dssdev->dst)
 65		return;
 66
 67	gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
 68
 69	dst->src = NULL;
 70	dssdev->dst = NULL;
 71
 72	in->ops.hdmi->disconnect(in, &ddata->dssdev);
 73}
 74
 75static int tpd_enable(struct omap_dss_device *dssdev)
 76{
 77	struct panel_drv_data *ddata = to_panel_data(dssdev);
 78	struct omap_dss_device *in = ddata->in;
 79	int r;
 80
 81	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
 82		return 0;
 83
 84	in->ops.hdmi->set_timings(in, &ddata->timings);
 85
 86	r = in->ops.hdmi->enable(in);
 87	if (r)
 88		return r;
 89
 90	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 91
 92	return r;
 93}
 94
 95static void tpd_disable(struct omap_dss_device *dssdev)
 96{
 97	struct panel_drv_data *ddata = to_panel_data(dssdev);
 98	struct omap_dss_device *in = ddata->in;
 99
100	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
101		return;
102
103	in->ops.hdmi->disable(in);
104
105	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
106}
107
108static void tpd_set_timings(struct omap_dss_device *dssdev,
109		struct omap_video_timings *timings)
110{
111	struct panel_drv_data *ddata = to_panel_data(dssdev);
112	struct omap_dss_device *in = ddata->in;
113
114	ddata->timings = *timings;
115	dssdev->panel.timings = *timings;
116
117	in->ops.hdmi->set_timings(in, timings);
118}
119
120static void tpd_get_timings(struct omap_dss_device *dssdev,
121		struct omap_video_timings *timings)
122{
123	struct panel_drv_data *ddata = to_panel_data(dssdev);
124
125	*timings = ddata->timings;
126}
127
128static int tpd_check_timings(struct omap_dss_device *dssdev,
129		struct omap_video_timings *timings)
130{
131	struct panel_drv_data *ddata = to_panel_data(dssdev);
132	struct omap_dss_device *in = ddata->in;
133	int r;
134
135	r = in->ops.hdmi->check_timings(in, timings);
136
137	return r;
138}
139
140static int tpd_read_edid(struct omap_dss_device *dssdev,
141		u8 *edid, int len)
142{
143	struct panel_drv_data *ddata = to_panel_data(dssdev);
144	struct omap_dss_device *in = ddata->in;
145	int r;
146
147	if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
148		return -ENODEV;
149
150	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
151
152	r = in->ops.hdmi->read_edid(in, edid, len);
153
154	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
155
156	return r;
157}
158
159static bool tpd_detect(struct omap_dss_device *dssdev)
160{
161	struct panel_drv_data *ddata = to_panel_data(dssdev);
162
163	return gpiod_get_value_cansleep(ddata->hpd_gpio);
164}
165
166static int tpd_set_infoframe(struct omap_dss_device *dssdev,
167		const struct hdmi_avi_infoframe *avi)
168{
169	struct panel_drv_data *ddata = to_panel_data(dssdev);
170	struct omap_dss_device *in = ddata->in;
171
172	return in->ops.hdmi->set_infoframe(in, avi);
173}
174
175static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
176		bool hdmi_mode)
177{
178	struct panel_drv_data *ddata = to_panel_data(dssdev);
179	struct omap_dss_device *in = ddata->in;
180
181	return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
182}
183
184static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
185	.connect		= tpd_connect,
186	.disconnect		= tpd_disconnect,
187
188	.enable			= tpd_enable,
189	.disable		= tpd_disable,
190
191	.check_timings		= tpd_check_timings,
192	.set_timings		= tpd_set_timings,
193	.get_timings		= tpd_get_timings,
194
195	.read_edid		= tpd_read_edid,
196	.detect			= tpd_detect,
197	.set_infoframe		= tpd_set_infoframe,
198	.set_hdmi_mode		= tpd_set_hdmi_mode,
199};
200
201static int tpd_probe_of(struct platform_device *pdev)
202{
203	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
204	struct device_node *node = pdev->dev.of_node;
205	struct omap_dss_device *in;
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	return 0;
216}
217
218static int tpd_probe(struct platform_device *pdev)
219{
220	struct omap_dss_device *in, *dssdev;
221	struct panel_drv_data *ddata;
222	int r;
223	struct gpio_desc *gpio;
224
225	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
226	if (!ddata)
227		return -ENOMEM;
228
229	platform_set_drvdata(pdev, ddata);
230
231	if (!pdev->dev.of_node)
232		return -ENODEV;
233
234	r = tpd_probe_of(pdev);
235	if (r)
236		return r;
237
238
239	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
240		 GPIOD_OUT_LOW);
241	if (IS_ERR(gpio))
242		goto err_gpio;
243
244	ddata->ct_cp_hpd_gpio = gpio;
245
246	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
247		 GPIOD_OUT_LOW);
248	if (IS_ERR(gpio))
249		goto err_gpio;
250
251	ddata->ls_oe_gpio = gpio;
252
253	gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
254		GPIOD_IN);
255	if (IS_ERR(gpio))
256		goto err_gpio;
257
258	ddata->hpd_gpio = gpio;
259
260	dssdev = &ddata->dssdev;
261	dssdev->ops.hdmi = &tpd_hdmi_ops;
262	dssdev->dev = &pdev->dev;
263	dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
264	dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
265	dssdev->owner = THIS_MODULE;
266	dssdev->port_num = 1;
267
268	in = ddata->in;
269
270	r = omapdss_register_output(dssdev);
271	if (r) {
272		dev_err(&pdev->dev, "Failed to register output\n");
273		goto err_reg;
274	}
275
276	return 0;
277err_reg:
278err_gpio:
279	omap_dss_put_device(ddata->in);
280	return r;
281}
282
283static int __exit tpd_remove(struct platform_device *pdev)
284{
285	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
286	struct omap_dss_device *dssdev = &ddata->dssdev;
287	struct omap_dss_device *in = ddata->in;
288
289	omapdss_unregister_output(&ddata->dssdev);
290
291	WARN_ON(omapdss_device_is_enabled(dssdev));
292	if (omapdss_device_is_enabled(dssdev))
293		tpd_disable(dssdev);
294
295	WARN_ON(omapdss_device_is_connected(dssdev));
296	if (omapdss_device_is_connected(dssdev))
297		tpd_disconnect(dssdev, dssdev->dst);
298
299	omap_dss_put_device(in);
300
301	return 0;
302}
303
304static const struct of_device_id tpd_of_match[] = {
305	{ .compatible = "omapdss,ti,tpd12s015", },
306	{},
307};
308
309MODULE_DEVICE_TABLE(of, tpd_of_match);
310
311static struct platform_driver tpd_driver = {
312	.probe	= tpd_probe,
313	.remove	= __exit_p(tpd_remove),
314	.driver	= {
315		.name	= "tpd12s015",
316		.of_match_table = tpd_of_match,
317		.suppress_bind_attrs = true,
318	},
319};
320
321module_platform_driver(tpd_driver);
322
323MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
324MODULE_DESCRIPTION("TPD12S015 driver");
325MODULE_LICENSE("GPL");