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