]> Git Repo - J-linux.git/blob - drivers/gpu/drm/msm/hdmi/hdmi_hpd.c
Merge tag 'ovl-update-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
[J-linux.git] / drivers / gpu / drm / msm / hdmi / hdmi_hpd.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2013 Red Hat
4  * Author: Rob Clark <[email protected]>
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
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
63 static 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
93 int 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
146 fail:
147         return ret;
148 }
149
150 void 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
174 void 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
204 static 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
221 static 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
228 enum 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 }
This page took 0.04313 seconds and 4 git commands to generate.