1 // SPDX-License-Identifier: GPL-2.0-only
3 * TPD12S015 HDMI ESD protection & level shifter chip driver
5 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
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>
17 #include "../dss/omapdss.h"
19 struct panel_drv_data
{
20 struct omap_dss_device dssdev
;
21 void (*hpd_cb
)(void *cb_data
, enum drm_connector_status status
);
23 struct mutex hpd_lock
;
25 struct gpio_desc
*ct_cp_hpd_gpio
;
26 struct gpio_desc
*ls_oe_gpio
;
27 struct gpio_desc
*hpd_gpio
;
30 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
32 static int tpd_connect(struct omap_dss_device
*src
,
33 struct omap_dss_device
*dst
)
35 struct panel_drv_data
*ddata
= to_panel_data(dst
);
38 r
= omapdss_device_connect(dst
->dss
, dst
, dst
->next
);
42 gpiod_set_value_cansleep(ddata
->ct_cp_hpd_gpio
, 1);
43 gpiod_set_value_cansleep(ddata
->ls_oe_gpio
, 1);
45 /* DC-DC converter needs at max 300us to get to 90% of 5V */
51 static void tpd_disconnect(struct omap_dss_device
*src
,
52 struct omap_dss_device
*dst
)
54 struct panel_drv_data
*ddata
= to_panel_data(dst
);
56 gpiod_set_value_cansleep(ddata
->ct_cp_hpd_gpio
, 0);
57 gpiod_set_value_cansleep(ddata
->ls_oe_gpio
, 0);
59 omapdss_device_disconnect(dst
, dst
->next
);
62 static bool tpd_detect(struct omap_dss_device
*dssdev
)
64 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
66 return gpiod_get_value_cansleep(ddata
->hpd_gpio
);
69 static void tpd_register_hpd_cb(struct omap_dss_device
*dssdev
,
70 void (*cb
)(void *cb_data
,
71 enum drm_connector_status status
),
74 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
76 mutex_lock(&ddata
->hpd_lock
);
78 ddata
->hpd_cb_data
= cb_data
;
79 mutex_unlock(&ddata
->hpd_lock
);
82 static void tpd_unregister_hpd_cb(struct omap_dss_device
*dssdev
)
84 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
86 mutex_lock(&ddata
->hpd_lock
);
88 ddata
->hpd_cb_data
= NULL
;
89 mutex_unlock(&ddata
->hpd_lock
);
92 static const struct omap_dss_device_ops tpd_ops
= {
93 .connect
= tpd_connect
,
94 .disconnect
= tpd_disconnect
,
96 .register_hpd_cb
= tpd_register_hpd_cb
,
97 .unregister_hpd_cb
= tpd_unregister_hpd_cb
,
100 static irqreturn_t
tpd_hpd_isr(int irq
, void *data
)
102 struct panel_drv_data
*ddata
= data
;
104 mutex_lock(&ddata
->hpd_lock
);
106 enum drm_connector_status status
;
108 if (tpd_detect(&ddata
->dssdev
))
109 status
= connector_status_connected
;
111 status
= connector_status_disconnected
;
113 ddata
->hpd_cb(ddata
->hpd_cb_data
, status
);
115 mutex_unlock(&ddata
->hpd_lock
);
120 static int tpd_probe(struct platform_device
*pdev
)
122 struct omap_dss_device
*dssdev
;
123 struct panel_drv_data
*ddata
;
125 struct gpio_desc
*gpio
;
127 ddata
= devm_kzalloc(&pdev
->dev
, sizeof(*ddata
), GFP_KERNEL
);
131 platform_set_drvdata(pdev
, ddata
);
133 gpio
= devm_gpiod_get_index_optional(&pdev
->dev
, NULL
, 0,
136 return PTR_ERR(gpio
);
138 ddata
->ct_cp_hpd_gpio
= gpio
;
140 gpio
= devm_gpiod_get_index_optional(&pdev
->dev
, NULL
, 1,
143 return PTR_ERR(gpio
);
145 ddata
->ls_oe_gpio
= gpio
;
147 gpio
= devm_gpiod_get_index(&pdev
->dev
, NULL
, 2,
150 return PTR_ERR(gpio
);
152 ddata
->hpd_gpio
= gpio
;
154 mutex_init(&ddata
->hpd_lock
);
156 r
= devm_request_threaded_irq(&pdev
->dev
, gpiod_to_irq(ddata
->hpd_gpio
),
158 IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING
| IRQF_ONESHOT
,
159 "tpd12s015 hpd", ddata
);
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
;
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
);
179 omapdss_device_register(dssdev
);
184 static int __exit
tpd_remove(struct platform_device
*pdev
)
186 struct panel_drv_data
*ddata
= platform_get_drvdata(pdev
);
187 struct omap_dss_device
*dssdev
= &ddata
->dssdev
;
190 omapdss_device_put(dssdev
->next
);
191 omapdss_device_unregister(&ddata
->dssdev
);
196 static const struct of_device_id tpd_of_match
[] = {
197 { .compatible
= "omapdss,ti,tpd12s015", },
201 MODULE_DEVICE_TABLE(of
, tpd_of_match
);
203 static struct platform_driver tpd_driver
= {
205 .remove
= __exit_p(tpd_remove
),
208 .of_match_table
= tpd_of_match
,
209 .suppress_bind_attrs
= true,
213 module_platform_driver(tpd_driver
);
215 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
216 MODULE_DESCRIPTION("TPD12S015 driver");
217 MODULE_LICENSE("GPL");