cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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}