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