xref: /linux/drivers/gpu/drm/msm/hdmi/hdmi_hpd.c (revision 220994d61cebfc04f071d69049127657c7e8191b)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2013 Red Hat
4  * Author: Rob Clark <robdclark@gmail.com>
5  */
6 
7 #include <linux/delay.h>
8 #include <linux/gpio/consumer.h>
9 #include <linux/pinctrl/consumer.h>
10 
11 #include "msm_kms.h"
12 #include "hdmi.h"
13 
msm_hdmi_phy_reset(struct hdmi * hdmi)14 static void msm_hdmi_phy_reset(struct hdmi *hdmi)
15 {
16 	unsigned int val;
17 
18 	val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
19 
20 	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
21 		/* pull low */
22 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
23 				val & ~HDMI_PHY_CTRL_SW_RESET);
24 	} else {
25 		/* pull high */
26 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
27 				val | HDMI_PHY_CTRL_SW_RESET);
28 	}
29 
30 	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
31 		/* pull low */
32 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
33 				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
34 	} else {
35 		/* pull high */
36 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
37 				val | HDMI_PHY_CTRL_SW_RESET_PLL);
38 	}
39 
40 	msleep(100);
41 
42 	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
43 		/* pull high */
44 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
45 				val | HDMI_PHY_CTRL_SW_RESET);
46 	} else {
47 		/* pull low */
48 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
49 				val & ~HDMI_PHY_CTRL_SW_RESET);
50 	}
51 
52 	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
53 		/* pull high */
54 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
55 				val | HDMI_PHY_CTRL_SW_RESET_PLL);
56 	} else {
57 		/* pull low */
58 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
59 				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
60 	}
61 }
62 
msm_hdmi_hpd_enable(struct drm_bridge * bridge)63 void msm_hdmi_hpd_enable(struct drm_bridge *bridge)
64 {
65 	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
66 	struct hdmi *hdmi = hdmi_bridge->hdmi;
67 	struct device *dev = &hdmi->pdev->dev;
68 	uint32_t hpd_ctrl;
69 	int ret;
70 	unsigned long flags;
71 
72 	if (hdmi->hpd_gpiod)
73 		gpiod_set_value_cansleep(hdmi->hpd_gpiod, 1);
74 
75 	ret = pm_runtime_resume_and_get(dev);
76 	if (WARN_ON(ret))
77 		return;
78 
79 	mutex_lock(&hdmi->state_mutex);
80 	msm_hdmi_set_mode(hdmi, false);
81 	msm_hdmi_phy_reset(hdmi);
82 	msm_hdmi_set_mode(hdmi, true);
83 
84 	hdmi->hpd_enabled = true;
85 	mutex_unlock(&hdmi->state_mutex);
86 
87 	hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
88 
89 	/* enable HPD events: */
90 	hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
91 			HDMI_HPD_INT_CTRL_INT_CONNECT |
92 			HDMI_HPD_INT_CTRL_INT_EN);
93 
94 	/* set timeout to 4.1ms (max) for hardware debounce */
95 	spin_lock_irqsave(&hdmi->reg_lock, flags);
96 	hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
97 	hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
98 
99 	/* Toggle HPD circuit to trigger HPD sense */
100 	hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
101 			~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
102 	hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
103 			HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
104 	spin_unlock_irqrestore(&hdmi->reg_lock, flags);
105 }
106 
msm_hdmi_hpd_disable(struct drm_bridge * bridge)107 void msm_hdmi_hpd_disable(struct drm_bridge *bridge)
108 {
109 	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
110 	struct hdmi *hdmi = hdmi_bridge->hdmi;
111 	struct device *dev = &hdmi->pdev->dev;
112 
113 	/* Disable HPD interrupt */
114 	hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
115 
116 	mutex_lock(&hdmi->state_mutex);
117 	hdmi->hpd_enabled = false;
118 	msm_hdmi_set_mode(hdmi, hdmi->power_on);
119 	mutex_unlock(&hdmi->state_mutex);
120 
121 	pm_runtime_put(dev);
122 }
123 
msm_hdmi_hpd_irq(struct drm_bridge * bridge)124 void msm_hdmi_hpd_irq(struct drm_bridge *bridge)
125 {
126 	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
127 	struct hdmi *hdmi = hdmi_bridge->hdmi;
128 	uint32_t hpd_int_status, hpd_int_ctrl;
129 
130 	/* Process HPD: */
131 	hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
132 	hpd_int_ctrl   = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);
133 
134 	if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&
135 			(hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {
136 		bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED);
137 
138 		/* ack & disable (temporarily) HPD events: */
139 		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
140 			HDMI_HPD_INT_CTRL_INT_ACK);
141 
142 		DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl);
143 
144 		/* detect disconnect if we are connected or visa versa: */
145 		hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
146 		if (!detected)
147 			hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
148 		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
149 
150 		queue_work(hdmi->workq, &hdmi_bridge->hpd_work);
151 	}
152 }
153 
detect_reg(struct hdmi * hdmi)154 static enum drm_connector_status detect_reg(struct hdmi *hdmi)
155 {
156 	u32 hpd_int_status = 0;
157 	int ret;
158 
159 	ret = pm_runtime_resume_and_get(&hdmi->pdev->dev);
160 	if (ret)
161 		goto out;
162 
163 	hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
164 
165 out:
166 	pm_runtime_put(&hdmi->pdev->dev);
167 
168 	return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
169 			connector_status_connected : connector_status_disconnected;
170 }
171 
172 #define HPD_GPIO_INDEX	2
detect_gpio(struct hdmi * hdmi)173 static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
174 {
175 	return gpiod_get_value(hdmi->hpd_gpiod) ?
176 			connector_status_connected :
177 			connector_status_disconnected;
178 }
179 
180 enum drm_connector_status
msm_hdmi_bridge_detect(struct drm_bridge * bridge,struct drm_connector * connector)181 msm_hdmi_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
182 {
183 	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
184 	struct hdmi *hdmi = hdmi_bridge->hdmi;
185 	enum drm_connector_status stat_gpio, stat_reg;
186 	int retry = 20;
187 
188 	/*
189 	 * some platforms may not have hpd gpio. Rely only on the status
190 	 * provided by REG_HDMI_HPD_INT_STATUS in this case.
191 	 */
192 	if (!hdmi->hpd_gpiod)
193 		return detect_reg(hdmi);
194 
195 	do {
196 		stat_gpio = detect_gpio(hdmi);
197 		stat_reg  = detect_reg(hdmi);
198 
199 		if (stat_gpio == stat_reg)
200 			break;
201 
202 		mdelay(10);
203 	} while (--retry);
204 
205 	/* the status we get from reading gpio seems to be more reliable,
206 	 * so trust that one the most if we didn't manage to get hdmi and
207 	 * gpio status to agree:
208 	 */
209 	if (stat_gpio != stat_reg) {
210 		DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);
211 		DBG("hpd gpio tells us: %d", stat_gpio);
212 	}
213 
214 	return stat_gpio;
215 }
216