Linux Audio

Check our new training course

Loading...
v5.4
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * TPD12S015 HDMI ESD protection & level shifter chip driver
  4 *
  5 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
  6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
 
 
 
 
  7 */
  8
  9#include <linux/completion.h>
 10#include <linux/delay.h>
 11#include <linux/module.h>
 12#include <linux/slab.h>
 13#include <linux/platform_device.h>
 14#include <linux/gpio/consumer.h>
 15#include <linux/mutex.h>
 16
 17#include "../dss/omapdss.h"
 18
 19struct panel_drv_data {
 20	struct omap_dss_device dssdev;
 
 21	void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
 22	void *hpd_cb_data;
 
 23	struct mutex hpd_lock;
 24
 25	struct gpio_desc *ct_cp_hpd_gpio;
 26	struct gpio_desc *ls_oe_gpio;
 27	struct gpio_desc *hpd_gpio;
 
 
 28};
 29
 30#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
 31
 32static int tpd_connect(struct omap_dss_device *src,
 33		       struct omap_dss_device *dst)
 34{
 35	struct panel_drv_data *ddata = to_panel_data(dst);
 
 36	int r;
 37
 38	r = omapdss_device_connect(dst->dss, dst, dst->next);
 39	if (r)
 
 
 
 
 
 
 
 40		return r;
 
 
 
 
 41
 42	gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
 43	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
 44
 45	/* DC-DC converter needs at max 300us to get to 90% of 5V */
 46	udelay(300);
 47
 
 48	return 0;
 49}
 50
 51static void tpd_disconnect(struct omap_dss_device *src,
 52			   struct omap_dss_device *dst)
 53{
 54	struct panel_drv_data *ddata = to_panel_data(dst);
 
 
 
 
 
 
 55
 56	gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
 57	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
 58
 59	omapdss_device_disconnect(dst, dst->next);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 60}
 61
 62static bool tpd_detect(struct omap_dss_device *dssdev)
 63{
 64	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
 
 65
 66	return gpiod_get_value_cansleep(ddata->hpd_gpio);
 
 
 67}
 68
 69static void tpd_register_hpd_cb(struct omap_dss_device *dssdev,
 70				void (*cb)(void *cb_data,
 71					  enum drm_connector_status status),
 72				void *cb_data)
 73{
 74	struct panel_drv_data *ddata = to_panel_data(dssdev);
 75
 76	mutex_lock(&ddata->hpd_lock);
 77	ddata->hpd_cb = cb;
 78	ddata->hpd_cb_data = cb_data;
 79	mutex_unlock(&ddata->hpd_lock);
 
 
 80}
 81
 82static void tpd_unregister_hpd_cb(struct omap_dss_device *dssdev)
 83{
 84	struct panel_drv_data *ddata = to_panel_data(dssdev);
 85
 86	mutex_lock(&ddata->hpd_lock);
 87	ddata->hpd_cb = NULL;
 88	ddata->hpd_cb_data = NULL;
 89	mutex_unlock(&ddata->hpd_lock);
 90}
 91
 92static const struct omap_dss_device_ops tpd_ops = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 93	.connect		= tpd_connect,
 94	.disconnect		= tpd_disconnect,
 
 
 
 
 
 
 
 
 
 95	.detect			= tpd_detect,
 96	.register_hpd_cb	= tpd_register_hpd_cb,
 97	.unregister_hpd_cb	= tpd_unregister_hpd_cb,
 
 
 
 
 98};
 99
100static irqreturn_t tpd_hpd_isr(int irq, void *data)
101{
102	struct panel_drv_data *ddata = data;
103
104	mutex_lock(&ddata->hpd_lock);
105	if (ddata->hpd_cb) {
106		enum drm_connector_status status;
107
108		if (tpd_detect(&ddata->dssdev))
109			status = connector_status_connected;
110		else
111			status = connector_status_disconnected;
112
113		ddata->hpd_cb(ddata->hpd_cb_data, status);
114	}
115	mutex_unlock(&ddata->hpd_lock);
116
117	return IRQ_HANDLED;
118}
119
120static int tpd_probe(struct platform_device *pdev)
121{
122	struct omap_dss_device *dssdev;
123	struct panel_drv_data *ddata;
124	int r;
125	struct gpio_desc *gpio;
126
127	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
128	if (!ddata)
129		return -ENOMEM;
130
131	platform_set_drvdata(pdev, ddata);
132
133	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
134		 GPIOD_OUT_LOW);
135	if (IS_ERR(gpio))
136		return PTR_ERR(gpio);
137
138	ddata->ct_cp_hpd_gpio = gpio;
139
140	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
141		 GPIOD_OUT_LOW);
142	if (IS_ERR(gpio))
143		return PTR_ERR(gpio);
144
145	ddata->ls_oe_gpio = gpio;
146
147	gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
148		GPIOD_IN);
149	if (IS_ERR(gpio))
150		return PTR_ERR(gpio);
151
152	ddata->hpd_gpio = gpio;
153
154	mutex_init(&ddata->hpd_lock);
155
156	r = devm_request_threaded_irq(&pdev->dev, gpiod_to_irq(ddata->hpd_gpio),
157		NULL, tpd_hpd_isr,
158		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
159		"tpd12s015 hpd", ddata);
160	if (r)
161		return r;
162
163	dssdev = &ddata->dssdev;
164	dssdev->ops = &tpd_ops;
165	dssdev->dev = &pdev->dev;
166	dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
 
167	dssdev->owner = THIS_MODULE;
168	dssdev->of_ports = BIT(1) | BIT(0);
169	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_DETECT
170			  | OMAP_DSS_DEVICE_OP_HPD;
171
172	dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1);
173	if (IS_ERR(dssdev->next)) {
174		if (PTR_ERR(dssdev->next) != -EPROBE_DEFER)
175			dev_err(&pdev->dev, "failed to find video sink\n");
176		return PTR_ERR(dssdev->next);
177	}
178
179	omapdss_device_register(dssdev);
 
 
 
 
 
 
180
181	return 0;
182}
183
184static int __exit tpd_remove(struct platform_device *pdev)
185{
186	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
187	struct omap_dss_device *dssdev = &ddata->dssdev;
188
189	if (dssdev->next)
190		omapdss_device_put(dssdev->next);
191	omapdss_device_unregister(&ddata->dssdev);
 
 
 
 
 
 
192
193	return 0;
194}
195
196static const struct of_device_id tpd_of_match[] = {
197	{ .compatible = "omapdss,ti,tpd12s015", },
198	{},
199};
200
201MODULE_DEVICE_TABLE(of, tpd_of_match);
202
203static struct platform_driver tpd_driver = {
204	.probe	= tpd_probe,
205	.remove	= __exit_p(tpd_remove),
206	.driver	= {
207		.name	= "tpd12s015",
208		.of_match_table = tpd_of_match,
209		.suppress_bind_attrs = true,
210	},
211};
212
213module_platform_driver(tpd_driver);
214
215MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
216MODULE_DESCRIPTION("TPD12S015 driver");
217MODULE_LICENSE("GPL");
v4.17
 
  1/*
  2 * TPD12S015 HDMI ESD protection & level shifter chip driver
  3 *
  4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
  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#include <linux/mutex.h>
 19
 20#include "../dss/omapdss.h"
 21
 22struct panel_drv_data {
 23	struct omap_dss_device dssdev;
 24	struct omap_dss_device *in;
 25	void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
 26	void *hpd_cb_data;
 27	bool hpd_enabled;
 28	struct mutex hpd_lock;
 29
 30	struct gpio_desc *ct_cp_hpd_gpio;
 31	struct gpio_desc *ls_oe_gpio;
 32	struct gpio_desc *hpd_gpio;
 33
 34	struct videomode vm;
 35};
 36
 37#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
 38
 39static int tpd_connect(struct omap_dss_device *dssdev,
 40		struct omap_dss_device *dst)
 41{
 42	struct panel_drv_data *ddata = to_panel_data(dssdev);
 43	struct omap_dss_device *in;
 44	int r;
 45
 46	in = omapdss_of_find_source_for_first_ep(dssdev->dev->of_node);
 47	if (IS_ERR(in)) {
 48		dev_err(dssdev->dev, "failed to find video source\n");
 49		return PTR_ERR(in);
 50	}
 51
 52	r = in->ops.hdmi->connect(in, dssdev);
 53	if (r) {
 54		omap_dss_put_device(in);
 55		return r;
 56	}
 57
 58	dst->src = dssdev;
 59	dssdev->dst = dst;
 60
 61	gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
 62	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
 63
 64	/* DC-DC converter needs at max 300us to get to 90% of 5V */
 65	udelay(300);
 66
 67	ddata->in = in;
 68	return 0;
 69}
 70
 71static void tpd_disconnect(struct omap_dss_device *dssdev,
 72		struct omap_dss_device *dst)
 73{
 74	struct panel_drv_data *ddata = to_panel_data(dssdev);
 75	struct omap_dss_device *in = ddata->in;
 76
 77	WARN_ON(dst != dssdev->dst);
 78
 79	if (dst != dssdev->dst)
 80		return;
 81
 82	gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
 83	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
 84
 85	dst->src = NULL;
 86	dssdev->dst = NULL;
 87
 88	in->ops.hdmi->disconnect(in, &ddata->dssdev);
 89
 90	omap_dss_put_device(in);
 91	ddata->in = NULL;
 92}
 93
 94static int tpd_enable(struct omap_dss_device *dssdev)
 95{
 96	struct panel_drv_data *ddata = to_panel_data(dssdev);
 97	struct omap_dss_device *in = ddata->in;
 98	int r;
 99
100	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
101		return 0;
102
103	in->ops.hdmi->set_timings(in, &ddata->vm);
104
105	r = in->ops.hdmi->enable(in);
106	if (r)
107		return r;
108
109	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
110
111	return r;
112}
113
114static void tpd_disable(struct omap_dss_device *dssdev)
115{
116	struct panel_drv_data *ddata = to_panel_data(dssdev);
117	struct omap_dss_device *in = ddata->in;
118
119	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
120		return;
121
122	in->ops.hdmi->disable(in);
123
124	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
125}
126
127static void tpd_set_timings(struct omap_dss_device *dssdev,
128			    struct videomode *vm)
129{
130	struct panel_drv_data *ddata = to_panel_data(dssdev);
131	struct omap_dss_device *in = ddata->in;
132
133	ddata->vm = *vm;
134	dssdev->panel.vm = *vm;
135
136	in->ops.hdmi->set_timings(in, vm);
137}
138
139static void tpd_get_timings(struct omap_dss_device *dssdev,
140			    struct videomode *vm)
141{
142	struct panel_drv_data *ddata = to_panel_data(dssdev);
143
144	*vm = ddata->vm;
145}
146
147static int tpd_check_timings(struct omap_dss_device *dssdev,
148			     struct videomode *vm)
149{
150	struct panel_drv_data *ddata = to_panel_data(dssdev);
151	struct omap_dss_device *in = ddata->in;
152	int r;
153
154	r = in->ops.hdmi->check_timings(in, vm);
155
156	return r;
157}
158
159static int tpd_read_edid(struct omap_dss_device *dssdev,
160		u8 *edid, int len)
161{
162	struct panel_drv_data *ddata = to_panel_data(dssdev);
163	struct omap_dss_device *in = ddata->in;
164
165	if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
166		return -ENODEV;
167
168	return in->ops.hdmi->read_edid(in, edid, len);
169}
170
171static bool tpd_detect(struct omap_dss_device *dssdev)
172{
173	struct panel_drv_data *ddata = to_panel_data(dssdev);
174	struct omap_dss_device *in = ddata->in;
175	bool connected = gpiod_get_value_cansleep(ddata->hpd_gpio);
176
177	if (!connected && in->ops.hdmi->lost_hotplug)
178		in->ops.hdmi->lost_hotplug(in);
179	return connected;
180}
181
182static int tpd_register_hpd_cb(struct omap_dss_device *dssdev,
183			       void (*cb)(void *cb_data,
184					  enum drm_connector_status status),
185			       void *cb_data)
186{
187	struct panel_drv_data *ddata = to_panel_data(dssdev);
188
189	mutex_lock(&ddata->hpd_lock);
190	ddata->hpd_cb = cb;
191	ddata->hpd_cb_data = cb_data;
192	mutex_unlock(&ddata->hpd_lock);
193
194	return 0;
195}
196
197static void tpd_unregister_hpd_cb(struct omap_dss_device *dssdev)
198{
199	struct panel_drv_data *ddata = to_panel_data(dssdev);
200
201	mutex_lock(&ddata->hpd_lock);
202	ddata->hpd_cb = NULL;
203	ddata->hpd_cb_data = NULL;
204	mutex_unlock(&ddata->hpd_lock);
205}
206
207static void tpd_enable_hpd(struct omap_dss_device *dssdev)
208{
209	struct panel_drv_data *ddata = to_panel_data(dssdev);
210
211	mutex_lock(&ddata->hpd_lock);
212	ddata->hpd_enabled = true;
213	mutex_unlock(&ddata->hpd_lock);
214}
215
216static void tpd_disable_hpd(struct omap_dss_device *dssdev)
217{
218	struct panel_drv_data *ddata = to_panel_data(dssdev);
219
220	mutex_lock(&ddata->hpd_lock);
221	ddata->hpd_enabled = false;
222	mutex_unlock(&ddata->hpd_lock);
223}
224
225static int tpd_set_infoframe(struct omap_dss_device *dssdev,
226		const struct hdmi_avi_infoframe *avi)
227{
228	struct panel_drv_data *ddata = to_panel_data(dssdev);
229	struct omap_dss_device *in = ddata->in;
230
231	return in->ops.hdmi->set_infoframe(in, avi);
232}
233
234static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
235		bool hdmi_mode)
236{
237	struct panel_drv_data *ddata = to_panel_data(dssdev);
238	struct omap_dss_device *in = ddata->in;
239
240	return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
241}
242
243static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
244	.connect		= tpd_connect,
245	.disconnect		= tpd_disconnect,
246
247	.enable			= tpd_enable,
248	.disable		= tpd_disable,
249
250	.check_timings		= tpd_check_timings,
251	.set_timings		= tpd_set_timings,
252	.get_timings		= tpd_get_timings,
253
254	.read_edid		= tpd_read_edid,
255	.detect			= tpd_detect,
256	.register_hpd_cb	= tpd_register_hpd_cb,
257	.unregister_hpd_cb	= tpd_unregister_hpd_cb,
258	.enable_hpd		= tpd_enable_hpd,
259	.disable_hpd		= tpd_disable_hpd,
260	.set_infoframe		= tpd_set_infoframe,
261	.set_hdmi_mode		= tpd_set_hdmi_mode,
262};
263
264static irqreturn_t tpd_hpd_isr(int irq, void *data)
265{
266	struct panel_drv_data *ddata = data;
267
268	mutex_lock(&ddata->hpd_lock);
269	if (ddata->hpd_enabled && ddata->hpd_cb) {
270		enum drm_connector_status status;
271
272		if (tpd_detect(&ddata->dssdev))
273			status = connector_status_connected;
274		else
275			status = connector_status_disconnected;
276
277		ddata->hpd_cb(ddata->hpd_cb_data, status);
278	}
279	mutex_unlock(&ddata->hpd_lock);
280
281	return IRQ_HANDLED;
282}
283
284static int tpd_probe(struct platform_device *pdev)
285{
286	struct omap_dss_device *in, *dssdev;
287	struct panel_drv_data *ddata;
288	int r;
289	struct gpio_desc *gpio;
290
291	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
292	if (!ddata)
293		return -ENOMEM;
294
295	platform_set_drvdata(pdev, ddata);
296
297	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
298		 GPIOD_OUT_LOW);
299	if (IS_ERR(gpio))
300		return PTR_ERR(gpio);
301
302	ddata->ct_cp_hpd_gpio = gpio;
303
304	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
305		 GPIOD_OUT_LOW);
306	if (IS_ERR(gpio))
307		return PTR_ERR(gpio);
308
309	ddata->ls_oe_gpio = gpio;
310
311	gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
312		GPIOD_IN);
313	if (IS_ERR(gpio))
314		return PTR_ERR(gpio);
315
316	ddata->hpd_gpio = gpio;
317
318	mutex_init(&ddata->hpd_lock);
319
320	r = devm_request_threaded_irq(&pdev->dev, gpiod_to_irq(ddata->hpd_gpio),
321		NULL, tpd_hpd_isr,
322		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
323		"tpd12s015 hpd", ddata);
324	if (r)
325		return r;
326
327	dssdev = &ddata->dssdev;
328	dssdev->ops.hdmi = &tpd_hdmi_ops;
329	dssdev->dev = &pdev->dev;
330	dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
331	dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
332	dssdev->owner = THIS_MODULE;
333	dssdev->port_num = 1;
 
 
 
 
 
 
 
 
 
334
335	in = ddata->in;
336
337	r = omapdss_register_output(dssdev);
338	if (r) {
339		dev_err(&pdev->dev, "Failed to register output\n");
340		return r;
341	}
342
343	return 0;
344}
345
346static int __exit tpd_remove(struct platform_device *pdev)
347{
348	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
349	struct omap_dss_device *dssdev = &ddata->dssdev;
350
351	omapdss_unregister_output(&ddata->dssdev);
352
353	WARN_ON(omapdss_device_is_enabled(dssdev));
354	if (omapdss_device_is_enabled(dssdev))
355		tpd_disable(dssdev);
356
357	WARN_ON(omapdss_device_is_connected(dssdev));
358	if (omapdss_device_is_connected(dssdev))
359		tpd_disconnect(dssdev, dssdev->dst);
360
361	return 0;
362}
363
364static const struct of_device_id tpd_of_match[] = {
365	{ .compatible = "omapdss,ti,tpd12s015", },
366	{},
367};
368
369MODULE_DEVICE_TABLE(of, tpd_of_match);
370
371static struct platform_driver tpd_driver = {
372	.probe	= tpd_probe,
373	.remove	= __exit_p(tpd_remove),
374	.driver	= {
375		.name	= "tpd12s015",
376		.of_match_table = tpd_of_match,
377		.suppress_bind_attrs = true,
378	},
379};
380
381module_platform_driver(tpd_driver);
382
383MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
384MODULE_DESCRIPTION("TPD12S015 driver");
385MODULE_LICENSE("GPL");