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
14static 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
63static void enable_hpd_clocks(struct hdmi *hdmi, bool enable)
64{
65	const struct hdmi_platform_config *config = hdmi->config;
66	struct device *dev = &hdmi->pdev->dev;
67	int i, ret;
68
69	if (enable) {
70		for (i = 0; i < config->hpd_clk_cnt; i++) {
71			if (config->hpd_freq && config->hpd_freq[i]) {
72				ret = clk_set_rate(hdmi->hpd_clks[i],
73						   config->hpd_freq[i]);
74				if (ret)
75					dev_warn(dev,
76						 "failed to set clk %s (%d)\n",
77						 config->hpd_clk_names[i], ret);
78			}
79
80			ret = clk_prepare_enable(hdmi->hpd_clks[i]);
81			if (ret) {
82				DRM_DEV_ERROR(dev,
83					"failed to enable hpd clk: %s (%d)\n",
84					config->hpd_clk_names[i], ret);
85			}
86		}
87	} else {
88		for (i = config->hpd_clk_cnt - 1; i >= 0; i--)
89			clk_disable_unprepare(hdmi->hpd_clks[i]);
90	}
91}
92
93int msm_hdmi_hpd_enable(struct drm_bridge *bridge)
94{
95	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
96	struct hdmi *hdmi = hdmi_bridge->hdmi;
97	const struct hdmi_platform_config *config = hdmi->config;
98	struct device *dev = &hdmi->pdev->dev;
99	uint32_t hpd_ctrl;
100	int i, ret;
101	unsigned long flags;
102
103	for (i = 0; i < config->hpd_reg_cnt; i++) {
104		ret = regulator_enable(hdmi->hpd_regs[i]);
105		if (ret) {
106			DRM_DEV_ERROR(dev, "failed to enable hpd regulator: %s (%d)\n",
107					config->hpd_reg_names[i], ret);
108			goto fail;
109		}
110	}
111
112	ret = pinctrl_pm_select_default_state(dev);
113	if (ret) {
114		DRM_DEV_ERROR(dev, "pinctrl state chg failed: %d\n", ret);
115		goto fail;
116	}
117
118	if (hdmi->hpd_gpiod)
119		gpiod_set_value_cansleep(hdmi->hpd_gpiod, 1);
120
121	pm_runtime_get_sync(dev);
122	enable_hpd_clocks(hdmi, true);
123
124	msm_hdmi_set_mode(hdmi, false);
125	msm_hdmi_phy_reset(hdmi);
126	msm_hdmi_set_mode(hdmi, true);
127
128	hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
129
130	/* enable HPD events: */
131	hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
132			HDMI_HPD_INT_CTRL_INT_CONNECT |
133			HDMI_HPD_INT_CTRL_INT_EN);
134
135	/* set timeout to 4.1ms (max) for hardware debounce */
136	spin_lock_irqsave(&hdmi->reg_lock, flags);
137	hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
138	hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
139
140	/* Toggle HPD circuit to trigger HPD sense */
141	hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
142			~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
143	hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
144			HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
145	spin_unlock_irqrestore(&hdmi->reg_lock, flags);
146
147	return 0;
148
149fail:
150	return ret;
151}
152
153void msm_hdmi_hpd_disable(struct hdmi_bridge *hdmi_bridge)
154{
155	struct hdmi *hdmi = hdmi_bridge->hdmi;
156	const struct hdmi_platform_config *config = hdmi->config;
157	struct device *dev = &hdmi->pdev->dev;
158	int i, ret = 0;
159
160	/* Disable HPD interrupt */
161	hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
162
163	msm_hdmi_set_mode(hdmi, false);
164
165	enable_hpd_clocks(hdmi, false);
166	pm_runtime_put_autosuspend(dev);
167
168	ret = pinctrl_pm_select_sleep_state(dev);
169	if (ret)
170		dev_warn(dev, "pinctrl state chg failed: %d\n", ret);
171
172	for (i = 0; i < config->hpd_reg_cnt; i++) {
173		ret = regulator_disable(hdmi->hpd_regs[i]);
174		if (ret)
175			dev_warn(dev, "failed to disable hpd regulator: %s (%d)\n",
176					config->hpd_reg_names[i], ret);
177	}
178}
179
180void msm_hdmi_hpd_irq(struct drm_bridge *bridge)
181{
182	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
183	struct hdmi *hdmi = hdmi_bridge->hdmi;
184	uint32_t hpd_int_status, hpd_int_ctrl;
185
186	/* Process HPD: */
187	hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
188	hpd_int_ctrl   = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);
189
190	if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&
191			(hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {
192		bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED);
193
194		/* ack & disable (temporarily) HPD events: */
195		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
196			HDMI_HPD_INT_CTRL_INT_ACK);
197
198		DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl);
199
200		/* detect disconnect if we are connected or visa versa: */
201		hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
202		if (!detected)
203			hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
204		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
205
206		queue_work(hdmi->workq, &hdmi_bridge->hpd_work);
207	}
208}
209
210static enum drm_connector_status detect_reg(struct hdmi *hdmi)
211{
212	uint32_t hpd_int_status;
213
214	pm_runtime_get_sync(&hdmi->pdev->dev);
215	enable_hpd_clocks(hdmi, true);
216
217	hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
218
219	enable_hpd_clocks(hdmi, false);
220	pm_runtime_put_autosuspend(&hdmi->pdev->dev);
221
222	return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
223			connector_status_connected : connector_status_disconnected;
224}
225
226#define HPD_GPIO_INDEX	2
227static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
228{
229	return gpiod_get_value(hdmi->hpd_gpiod) ?
230			connector_status_connected :
231			connector_status_disconnected;
232}
233
234enum drm_connector_status msm_hdmi_bridge_detect(
235		struct drm_bridge *bridge)
236{
237	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
238	struct hdmi *hdmi = hdmi_bridge->hdmi;
239	enum drm_connector_status stat_gpio, stat_reg;
240	int retry = 20;
241
242	/*
243	 * some platforms may not have hpd gpio. Rely only on the status
244	 * provided by REG_HDMI_HPD_INT_STATUS in this case.
245	 */
246	if (!hdmi->hpd_gpiod)
247		return detect_reg(hdmi);
248
249	do {
250		stat_gpio = detect_gpio(hdmi);
251		stat_reg  = detect_reg(hdmi);
252
253		if (stat_gpio == stat_reg)
254			break;
255
256		mdelay(10);
257	} while (--retry);
258
259	/* the status we get from reading gpio seems to be more reliable,
260	 * so trust that one the most if we didn't manage to get hdmi and
261	 * gpio status to agree:
262	 */
263	if (stat_gpio != stat_reg) {
264		DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);
265		DBG("hpd gpio tells us: %d", stat_gpio);
266	}
267
268	return stat_gpio;
269}
270