xref: /linux/drivers/gpu/drm/bridge/ti-tpd12s015.c (revision 260f6f4fda93c8485c8037865c941b42b9cba5d2)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * TPD12S015 HDMI ESD protection & level shifter chip driver
4  *
5  * Copyright (C) 2019 Texas Instruments Incorporated
6  *
7  * Based on the omapdrm-specific encoder-opa362 driver
8  *
9  * Copyright (C) 2013 Texas Instruments Incorporated
10  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
11  */
12 
13 #include <linux/delay.h>
14 #include <linux/gpio/consumer.h>
15 #include <linux/interrupt.h>
16 #include <linux/module.h>
17 #include <linux/mutex.h>
18 #include <linux/of.h>
19 #include <linux/of_graph.h>
20 #include <linux/platform_device.h>
21 
22 #include <drm/drm_bridge.h>
23 
24 struct tpd12s015_device {
25 	struct drm_bridge bridge;
26 
27 	struct gpio_desc *ct_cp_hpd_gpio;
28 	struct gpio_desc *ls_oe_gpio;
29 	struct gpio_desc *hpd_gpio;
30 	int hpd_irq;
31 
32 	struct drm_bridge *next_bridge;
33 };
34 
35 static inline struct tpd12s015_device *to_tpd12s015(struct drm_bridge *bridge)
36 {
37 	return container_of(bridge, struct tpd12s015_device, bridge);
38 }
39 
40 static int tpd12s015_attach(struct drm_bridge *bridge,
41 			    struct drm_encoder *encoder,
42 			    enum drm_bridge_attach_flags flags)
43 {
44 	struct tpd12s015_device *tpd = to_tpd12s015(bridge);
45 	int ret;
46 
47 	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
48 		return -EINVAL;
49 
50 	ret = drm_bridge_attach(encoder, tpd->next_bridge,
51 				bridge, flags);
52 	if (ret < 0)
53 		return ret;
54 
55 	gpiod_set_value_cansleep(tpd->ls_oe_gpio, 1);
56 
57 	/* DC-DC converter needs at max 300us to get to 90% of 5V. */
58 	usleep_range(300, 1000);
59 
60 	return 0;
61 }
62 
63 static void tpd12s015_detach(struct drm_bridge *bridge)
64 {
65 	struct tpd12s015_device *tpd = to_tpd12s015(bridge);
66 
67 	gpiod_set_value_cansleep(tpd->ls_oe_gpio, 0);
68 }
69 
70 static enum drm_connector_status tpd12s015_detect(struct drm_bridge *bridge)
71 {
72 	struct tpd12s015_device *tpd = to_tpd12s015(bridge);
73 
74 	if (gpiod_get_value_cansleep(tpd->hpd_gpio))
75 		return connector_status_connected;
76 	else
77 		return connector_status_disconnected;
78 }
79 
80 static enum drm_connector_status
81 tpd12s015_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
82 {
83 	return tpd12s015_detect(bridge);
84 }
85 
86 static void tpd12s015_hpd_enable(struct drm_bridge *bridge)
87 {
88 	struct tpd12s015_device *tpd = to_tpd12s015(bridge);
89 
90 	gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 1);
91 }
92 
93 static void tpd12s015_hpd_disable(struct drm_bridge *bridge)
94 {
95 	struct tpd12s015_device *tpd = to_tpd12s015(bridge);
96 
97 	gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 0);
98 }
99 
100 static const struct drm_bridge_funcs tpd12s015_bridge_funcs = {
101 	.attach			= tpd12s015_attach,
102 	.detach			= tpd12s015_detach,
103 	.detect			= tpd12s015_bridge_detect,
104 	.hpd_enable		= tpd12s015_hpd_enable,
105 	.hpd_disable		= tpd12s015_hpd_disable,
106 };
107 
108 static irqreturn_t tpd12s015_hpd_isr(int irq, void *data)
109 {
110 	struct tpd12s015_device *tpd = data;
111 	struct drm_bridge *bridge = &tpd->bridge;
112 
113 	drm_bridge_hpd_notify(bridge, tpd12s015_detect(bridge));
114 
115 	return IRQ_HANDLED;
116 }
117 
118 static int tpd12s015_probe(struct platform_device *pdev)
119 {
120 	struct tpd12s015_device *tpd;
121 	struct device_node *node;
122 	struct gpio_desc *gpio;
123 	int ret;
124 
125 	tpd = devm_drm_bridge_alloc(&pdev->dev, struct tpd12s015_device,
126 				    bridge, &tpd12s015_bridge_funcs);
127 	if (IS_ERR(tpd))
128 		return PTR_ERR(tpd);
129 
130 	platform_set_drvdata(pdev, tpd);
131 
132 	tpd->bridge.of_node = pdev->dev.of_node;
133 	tpd->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
134 	tpd->bridge.ops = DRM_BRIDGE_OP_DETECT;
135 
136 	/* Get the next bridge, connected to port@1. */
137 	node = of_graph_get_remote_node(pdev->dev.of_node, 1, -1);
138 	if (!node)
139 		return -ENODEV;
140 
141 	tpd->next_bridge = of_drm_find_bridge(node);
142 	of_node_put(node);
143 
144 	if (!tpd->next_bridge)
145 		return -EPROBE_DEFER;
146 
147 	/* Get the control and HPD GPIOs. */
148 	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
149 					     GPIOD_OUT_LOW);
150 	if (IS_ERR(gpio))
151 		return PTR_ERR(gpio);
152 
153 	tpd->ct_cp_hpd_gpio = gpio;
154 
155 	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
156 					     GPIOD_OUT_LOW);
157 	if (IS_ERR(gpio))
158 		return PTR_ERR(gpio);
159 
160 	tpd->ls_oe_gpio = gpio;
161 
162 	gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_IN);
163 	if (IS_ERR(gpio))
164 		return PTR_ERR(gpio);
165 
166 	tpd->hpd_gpio = gpio;
167 
168 	/* Register the IRQ if the HPD GPIO is IRQ-capable. */
169 	tpd->hpd_irq = gpiod_to_irq(tpd->hpd_gpio);
170 	if (tpd->hpd_irq >= 0) {
171 		ret = devm_request_threaded_irq(&pdev->dev, tpd->hpd_irq, NULL,
172 						tpd12s015_hpd_isr,
173 						IRQF_TRIGGER_RISING |
174 						IRQF_TRIGGER_FALLING |
175 						IRQF_ONESHOT,
176 						"tpd12s015 hpd", tpd);
177 		if (ret)
178 			return ret;
179 
180 		tpd->bridge.ops |= DRM_BRIDGE_OP_HPD;
181 	}
182 
183 	/* Register the DRM bridge. */
184 	drm_bridge_add(&tpd->bridge);
185 
186 	return 0;
187 }
188 
189 static void tpd12s015_remove(struct platform_device *pdev)
190 {
191 	struct tpd12s015_device *tpd = platform_get_drvdata(pdev);
192 
193 	drm_bridge_remove(&tpd->bridge);
194 }
195 
196 static const struct of_device_id tpd12s015_of_match[] = {
197 	{ .compatible = "ti,tpd12s015", },
198 	{},
199 };
200 
201 MODULE_DEVICE_TABLE(of, tpd12s015_of_match);
202 
203 static struct platform_driver tpd12s015_driver = {
204 	.probe	= tpd12s015_probe,
205 	.remove = tpd12s015_remove,
206 	.driver	= {
207 		.name	= "tpd12s015",
208 		.of_match_table = tpd12s015_of_match,
209 	},
210 };
211 
212 module_platform_driver(tpd12s015_driver);
213 
214 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
215 MODULE_DESCRIPTION("TPD12S015 HDMI level shifter and ESD protection driver");
216 MODULE_LICENSE("GPL");
217