brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 9ce0ffa Raw
263 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013 Red Hat4 * 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 *hdmi)151{152	const struct hdmi_platform_config *config = hdmi->config;153	struct device *dev = &hdmi->pdev->dev;154	int ret;155 156	/* Disable HPD interrupt */157	hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);158 159	msm_hdmi_set_mode(hdmi, false);160 161	enable_hpd_clocks(hdmi, false);162	pm_runtime_put(dev);163 164	ret = pinctrl_pm_select_sleep_state(dev);165	if (ret)166		dev_warn(dev, "pinctrl state chg failed: %d\n", ret);167 168	ret = regulator_bulk_disable(config->hpd_reg_cnt, hdmi->hpd_regs);169	if (ret)170		dev_warn(dev, "failed to disable hpd regulator: %d\n", ret);171}172 173void msm_hdmi_hpd_irq(struct drm_bridge *bridge)174{175	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);176	struct hdmi *hdmi = hdmi_bridge->hdmi;177	uint32_t hpd_int_status, hpd_int_ctrl;178 179	/* Process HPD: */180	hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);181	hpd_int_ctrl   = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);182 183	if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&184			(hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {185		bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED);186 187		/* ack & disable (temporarily) HPD events: */188		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,189			HDMI_HPD_INT_CTRL_INT_ACK);190 191		DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl);192 193		/* detect disconnect if we are connected or visa versa: */194		hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;195		if (!detected)196			hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;197		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);198 199		queue_work(hdmi->workq, &hdmi_bridge->hpd_work);200	}201}202 203static enum drm_connector_status detect_reg(struct hdmi *hdmi)204{205	uint32_t hpd_int_status;206 207	pm_runtime_get_sync(&hdmi->pdev->dev);208	enable_hpd_clocks(hdmi, true);209 210	hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);211 212	enable_hpd_clocks(hdmi, false);213	pm_runtime_put(&hdmi->pdev->dev);214 215	return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?216			connector_status_connected : connector_status_disconnected;217}218 219#define HPD_GPIO_INDEX	2220static enum drm_connector_status detect_gpio(struct hdmi *hdmi)221{222	return gpiod_get_value(hdmi->hpd_gpiod) ?223			connector_status_connected :224			connector_status_disconnected;225}226 227enum drm_connector_status msm_hdmi_bridge_detect(228		struct drm_bridge *bridge)229{230	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);231	struct hdmi *hdmi = hdmi_bridge->hdmi;232	enum drm_connector_status stat_gpio, stat_reg;233	int retry = 20;234 235	/*236	 * some platforms may not have hpd gpio. Rely only on the status237	 * provided by REG_HDMI_HPD_INT_STATUS in this case.238	 */239	if (!hdmi->hpd_gpiod)240		return detect_reg(hdmi);241 242	do {243		stat_gpio = detect_gpio(hdmi);244		stat_reg  = detect_reg(hdmi);245 246		if (stat_gpio == stat_reg)247			break;248 249		mdelay(10);250	} while (--retry);251 252	/* the status we get from reading gpio seems to be more reliable,253	 * so trust that one the most if we didn't manage to get hdmi and254	 * gpio status to agree:255	 */256	if (stat_gpio != stat_reg) {257		DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);258		DBG("hpd gpio tells us: %d", stat_gpio);259	}260 261	return stat_gpio;262}263