18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
88c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
98c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
108c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_edid.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "edp.h"
148c2ecf20Sopenharmony_ci#include "edp.xml.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define VDDA_UA_ON_LOAD		100000	/* uA units */
178c2ecf20Sopenharmony_ci#define VDDA_UA_OFF_LOAD	100	/* uA units */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define DPCD_LINK_VOLTAGE_MAX		4
208c2ecf20Sopenharmony_ci#define DPCD_LINK_PRE_EMPHASIS_MAX	4
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define EDP_LINK_BW_MAX		DP_LINK_BW_2_7
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* Link training return value */
258c2ecf20Sopenharmony_ci#define EDP_TRAIN_FAIL		-1
268c2ecf20Sopenharmony_ci#define EDP_TRAIN_SUCCESS	0
278c2ecf20Sopenharmony_ci#define EDP_TRAIN_RECONFIG	1
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define EDP_CLK_MASK_AHB		BIT(0)
308c2ecf20Sopenharmony_ci#define EDP_CLK_MASK_AUX		BIT(1)
318c2ecf20Sopenharmony_ci#define EDP_CLK_MASK_LINK		BIT(2)
328c2ecf20Sopenharmony_ci#define EDP_CLK_MASK_PIXEL		BIT(3)
338c2ecf20Sopenharmony_ci#define EDP_CLK_MASK_MDP_CORE		BIT(4)
348c2ecf20Sopenharmony_ci#define EDP_CLK_MASK_LINK_CHAN	(EDP_CLK_MASK_LINK | EDP_CLK_MASK_PIXEL)
358c2ecf20Sopenharmony_ci#define EDP_CLK_MASK_AUX_CHAN	\
368c2ecf20Sopenharmony_ci	(EDP_CLK_MASK_AHB | EDP_CLK_MASK_AUX | EDP_CLK_MASK_MDP_CORE)
378c2ecf20Sopenharmony_ci#define EDP_CLK_MASK_ALL	(EDP_CLK_MASK_AUX_CHAN | EDP_CLK_MASK_LINK_CHAN)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define EDP_BACKLIGHT_MAX	255
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define EDP_INTR_STATUS1	\
428c2ecf20Sopenharmony_ci	(EDP_INTERRUPT_REG_1_HPD | EDP_INTERRUPT_REG_1_AUX_I2C_DONE | \
438c2ecf20Sopenharmony_ci	EDP_INTERRUPT_REG_1_WRONG_ADDR | EDP_INTERRUPT_REG_1_TIMEOUT | \
448c2ecf20Sopenharmony_ci	EDP_INTERRUPT_REG_1_NACK_DEFER | EDP_INTERRUPT_REG_1_WRONG_DATA_CNT | \
458c2ecf20Sopenharmony_ci	EDP_INTERRUPT_REG_1_I2C_NACK | EDP_INTERRUPT_REG_1_I2C_DEFER | \
468c2ecf20Sopenharmony_ci	EDP_INTERRUPT_REG_1_PLL_UNLOCK | EDP_INTERRUPT_REG_1_AUX_ERROR)
478c2ecf20Sopenharmony_ci#define EDP_INTR_MASK1	(EDP_INTR_STATUS1 << 2)
488c2ecf20Sopenharmony_ci#define EDP_INTR_STATUS2	\
498c2ecf20Sopenharmony_ci	(EDP_INTERRUPT_REG_2_READY_FOR_VIDEO | \
508c2ecf20Sopenharmony_ci	EDP_INTERRUPT_REG_2_IDLE_PATTERNs_SENT | \
518c2ecf20Sopenharmony_ci	EDP_INTERRUPT_REG_2_FRAME_END | EDP_INTERRUPT_REG_2_CRC_UPDATED)
528c2ecf20Sopenharmony_ci#define EDP_INTR_MASK2	(EDP_INTR_STATUS2 << 2)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct edp_ctrl {
558c2ecf20Sopenharmony_ci	struct platform_device *pdev;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	void __iomem *base;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* regulators */
608c2ecf20Sopenharmony_ci	struct regulator *vdda_vreg;	/* 1.8 V */
618c2ecf20Sopenharmony_ci	struct regulator *lvl_vreg;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/* clocks */
648c2ecf20Sopenharmony_ci	struct clk *aux_clk;
658c2ecf20Sopenharmony_ci	struct clk *pixel_clk;
668c2ecf20Sopenharmony_ci	struct clk *ahb_clk;
678c2ecf20Sopenharmony_ci	struct clk *link_clk;
688c2ecf20Sopenharmony_ci	struct clk *mdp_core_clk;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* gpios */
718c2ecf20Sopenharmony_ci	struct gpio_desc *panel_en_gpio;
728c2ecf20Sopenharmony_ci	struct gpio_desc *panel_hpd_gpio;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* completion and mutex */
758c2ecf20Sopenharmony_ci	struct completion idle_comp;
768c2ecf20Sopenharmony_ci	struct mutex dev_mutex; /* To protect device power status */
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* work queue */
798c2ecf20Sopenharmony_ci	struct work_struct on_work;
808c2ecf20Sopenharmony_ci	struct work_struct off_work;
818c2ecf20Sopenharmony_ci	struct workqueue_struct *workqueue;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Interrupt register lock */
848c2ecf20Sopenharmony_ci	spinlock_t irq_lock;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	bool edp_connected;
878c2ecf20Sopenharmony_ci	bool power_on;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* edid raw data */
908c2ecf20Sopenharmony_ci	struct edid *edid;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	struct drm_dp_aux *drm_aux;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* dpcd raw data */
958c2ecf20Sopenharmony_ci	u8 dpcd[DP_RECEIVER_CAP_SIZE];
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* Link status */
988c2ecf20Sopenharmony_ci	u8 link_rate;
998c2ecf20Sopenharmony_ci	u8 lane_cnt;
1008c2ecf20Sopenharmony_ci	u8 v_level;
1018c2ecf20Sopenharmony_ci	u8 p_level;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/* Timing status */
1048c2ecf20Sopenharmony_ci	u8 interlaced;
1058c2ecf20Sopenharmony_ci	u32 pixel_rate; /* in kHz */
1068c2ecf20Sopenharmony_ci	u32 color_depth;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	struct edp_aux *aux;
1098c2ecf20Sopenharmony_ci	struct edp_phy *phy;
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistruct edp_pixel_clk_div {
1138c2ecf20Sopenharmony_ci	u32 rate; /* in kHz */
1148c2ecf20Sopenharmony_ci	u32 m;
1158c2ecf20Sopenharmony_ci	u32 n;
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#define EDP_PIXEL_CLK_NUM 8
1198c2ecf20Sopenharmony_cistatic const struct edp_pixel_clk_div clk_divs[2][EDP_PIXEL_CLK_NUM] = {
1208c2ecf20Sopenharmony_ci	{ /* Link clock = 162MHz, source clock = 810MHz */
1218c2ecf20Sopenharmony_ci		{119000, 31,  211}, /* WSXGA+ 1680x1050@60Hz CVT */
1228c2ecf20Sopenharmony_ci		{130250, 32,  199}, /* UXGA 1600x1200@60Hz CVT */
1238c2ecf20Sopenharmony_ci		{148500, 11,  60},  /* FHD 1920x1080@60Hz */
1248c2ecf20Sopenharmony_ci		{154000, 50,  263}, /* WUXGA 1920x1200@60Hz CVT */
1258c2ecf20Sopenharmony_ci		{209250, 31,  120}, /* QXGA 2048x1536@60Hz CVT */
1268c2ecf20Sopenharmony_ci		{268500, 119, 359}, /* WQXGA 2560x1600@60Hz CVT */
1278c2ecf20Sopenharmony_ci		{138530, 33,  193}, /* AUO B116HAN03.0 Panel */
1288c2ecf20Sopenharmony_ci		{141400, 48,  275}, /* AUO B133HTN01.2 Panel */
1298c2ecf20Sopenharmony_ci	},
1308c2ecf20Sopenharmony_ci	{ /* Link clock = 270MHz, source clock = 675MHz */
1318c2ecf20Sopenharmony_ci		{119000, 52,  295}, /* WSXGA+ 1680x1050@60Hz CVT */
1328c2ecf20Sopenharmony_ci		{130250, 11,  57},  /* UXGA 1600x1200@60Hz CVT */
1338c2ecf20Sopenharmony_ci		{148500, 11,  50},  /* FHD 1920x1080@60Hz */
1348c2ecf20Sopenharmony_ci		{154000, 47,  206}, /* WUXGA 1920x1200@60Hz CVT */
1358c2ecf20Sopenharmony_ci		{209250, 31,  100}, /* QXGA 2048x1536@60Hz CVT */
1368c2ecf20Sopenharmony_ci		{268500, 107, 269}, /* WQXGA 2560x1600@60Hz CVT */
1378c2ecf20Sopenharmony_ci		{138530, 63,  307}, /* AUO B116HAN03.0 Panel */
1388c2ecf20Sopenharmony_ci		{141400, 53,  253}, /* AUO B133HTN01.2 Panel */
1398c2ecf20Sopenharmony_ci	},
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int edp_clk_init(struct edp_ctrl *ctrl)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct platform_device *pdev = ctrl->pdev;
1458c2ecf20Sopenharmony_ci	int ret;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	ctrl->aux_clk = msm_clk_get(pdev, "core");
1488c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->aux_clk)) {
1498c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctrl->aux_clk);
1508c2ecf20Sopenharmony_ci		pr_err("%s: Can't find core clock, %d\n", __func__, ret);
1518c2ecf20Sopenharmony_ci		ctrl->aux_clk = NULL;
1528c2ecf20Sopenharmony_ci		return ret;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	ctrl->pixel_clk = msm_clk_get(pdev, "pixel");
1568c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->pixel_clk)) {
1578c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctrl->pixel_clk);
1588c2ecf20Sopenharmony_ci		pr_err("%s: Can't find pixel clock, %d\n", __func__, ret);
1598c2ecf20Sopenharmony_ci		ctrl->pixel_clk = NULL;
1608c2ecf20Sopenharmony_ci		return ret;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	ctrl->ahb_clk = msm_clk_get(pdev, "iface");
1648c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->ahb_clk)) {
1658c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctrl->ahb_clk);
1668c2ecf20Sopenharmony_ci		pr_err("%s: Can't find iface clock, %d\n", __func__, ret);
1678c2ecf20Sopenharmony_ci		ctrl->ahb_clk = NULL;
1688c2ecf20Sopenharmony_ci		return ret;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	ctrl->link_clk = msm_clk_get(pdev, "link");
1728c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->link_clk)) {
1738c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctrl->link_clk);
1748c2ecf20Sopenharmony_ci		pr_err("%s: Can't find link clock, %d\n", __func__, ret);
1758c2ecf20Sopenharmony_ci		ctrl->link_clk = NULL;
1768c2ecf20Sopenharmony_ci		return ret;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* need mdp core clock to receive irq */
1808c2ecf20Sopenharmony_ci	ctrl->mdp_core_clk = msm_clk_get(pdev, "mdp_core");
1818c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->mdp_core_clk)) {
1828c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctrl->mdp_core_clk);
1838c2ecf20Sopenharmony_ci		pr_err("%s: Can't find mdp_core clock, %d\n", __func__, ret);
1848c2ecf20Sopenharmony_ci		ctrl->mdp_core_clk = NULL;
1858c2ecf20Sopenharmony_ci		return ret;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int edp_clk_enable(struct edp_ctrl *ctrl, u32 clk_mask)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	int ret;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	DBG("mask=%x", clk_mask);
1968c2ecf20Sopenharmony_ci	/* ahb_clk should be enabled first */
1978c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_AHB) {
1988c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(ctrl->ahb_clk);
1998c2ecf20Sopenharmony_ci		if (ret) {
2008c2ecf20Sopenharmony_ci			pr_err("%s: Failed to enable ahb clk\n", __func__);
2018c2ecf20Sopenharmony_ci			goto f0;
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_AUX) {
2058c2ecf20Sopenharmony_ci		ret = clk_set_rate(ctrl->aux_clk, 19200000);
2068c2ecf20Sopenharmony_ci		if (ret) {
2078c2ecf20Sopenharmony_ci			pr_err("%s: Failed to set rate aux clk\n", __func__);
2088c2ecf20Sopenharmony_ci			goto f1;
2098c2ecf20Sopenharmony_ci		}
2108c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(ctrl->aux_clk);
2118c2ecf20Sopenharmony_ci		if (ret) {
2128c2ecf20Sopenharmony_ci			pr_err("%s: Failed to enable aux clk\n", __func__);
2138c2ecf20Sopenharmony_ci			goto f1;
2148c2ecf20Sopenharmony_ci		}
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci	/* Need to set rate and enable link_clk prior to pixel_clk */
2178c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_LINK) {
2188c2ecf20Sopenharmony_ci		DBG("edp->link_clk, set_rate %ld",
2198c2ecf20Sopenharmony_ci				(unsigned long)ctrl->link_rate * 27000000);
2208c2ecf20Sopenharmony_ci		ret = clk_set_rate(ctrl->link_clk,
2218c2ecf20Sopenharmony_ci				(unsigned long)ctrl->link_rate * 27000000);
2228c2ecf20Sopenharmony_ci		if (ret) {
2238c2ecf20Sopenharmony_ci			pr_err("%s: Failed to set rate to link clk\n",
2248c2ecf20Sopenharmony_ci				__func__);
2258c2ecf20Sopenharmony_ci			goto f2;
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(ctrl->link_clk);
2298c2ecf20Sopenharmony_ci		if (ret) {
2308c2ecf20Sopenharmony_ci			pr_err("%s: Failed to enable link clk\n", __func__);
2318c2ecf20Sopenharmony_ci			goto f2;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_PIXEL) {
2358c2ecf20Sopenharmony_ci		DBG("edp->pixel_clk, set_rate %ld",
2368c2ecf20Sopenharmony_ci				(unsigned long)ctrl->pixel_rate * 1000);
2378c2ecf20Sopenharmony_ci		ret = clk_set_rate(ctrl->pixel_clk,
2388c2ecf20Sopenharmony_ci				(unsigned long)ctrl->pixel_rate * 1000);
2398c2ecf20Sopenharmony_ci		if (ret) {
2408c2ecf20Sopenharmony_ci			pr_err("%s: Failed to set rate to pixel clk\n",
2418c2ecf20Sopenharmony_ci				__func__);
2428c2ecf20Sopenharmony_ci			goto f3;
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(ctrl->pixel_clk);
2468c2ecf20Sopenharmony_ci		if (ret) {
2478c2ecf20Sopenharmony_ci			pr_err("%s: Failed to enable pixel clk\n", __func__);
2488c2ecf20Sopenharmony_ci			goto f3;
2498c2ecf20Sopenharmony_ci		}
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_MDP_CORE) {
2528c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(ctrl->mdp_core_clk);
2538c2ecf20Sopenharmony_ci		if (ret) {
2548c2ecf20Sopenharmony_ci			pr_err("%s: Failed to enable mdp core clk\n", __func__);
2558c2ecf20Sopenharmony_ci			goto f4;
2568c2ecf20Sopenharmony_ci		}
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return 0;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cif4:
2628c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_PIXEL)
2638c2ecf20Sopenharmony_ci		clk_disable_unprepare(ctrl->pixel_clk);
2648c2ecf20Sopenharmony_cif3:
2658c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_LINK)
2668c2ecf20Sopenharmony_ci		clk_disable_unprepare(ctrl->link_clk);
2678c2ecf20Sopenharmony_cif2:
2688c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_AUX)
2698c2ecf20Sopenharmony_ci		clk_disable_unprepare(ctrl->aux_clk);
2708c2ecf20Sopenharmony_cif1:
2718c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_AHB)
2728c2ecf20Sopenharmony_ci		clk_disable_unprepare(ctrl->ahb_clk);
2738c2ecf20Sopenharmony_cif0:
2748c2ecf20Sopenharmony_ci	return ret;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic void edp_clk_disable(struct edp_ctrl *ctrl, u32 clk_mask)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_MDP_CORE)
2808c2ecf20Sopenharmony_ci		clk_disable_unprepare(ctrl->mdp_core_clk);
2818c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_PIXEL)
2828c2ecf20Sopenharmony_ci		clk_disable_unprepare(ctrl->pixel_clk);
2838c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_LINK)
2848c2ecf20Sopenharmony_ci		clk_disable_unprepare(ctrl->link_clk);
2858c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_AUX)
2868c2ecf20Sopenharmony_ci		clk_disable_unprepare(ctrl->aux_clk);
2878c2ecf20Sopenharmony_ci	if (clk_mask & EDP_CLK_MASK_AHB)
2888c2ecf20Sopenharmony_ci		clk_disable_unprepare(ctrl->ahb_clk);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int edp_regulator_init(struct edp_ctrl *ctrl)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct device *dev = &ctrl->pdev->dev;
2948c2ecf20Sopenharmony_ci	int ret;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	DBG("");
2978c2ecf20Sopenharmony_ci	ctrl->vdda_vreg = devm_regulator_get(dev, "vdda");
2988c2ecf20Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(ctrl->vdda_vreg);
2998c2ecf20Sopenharmony_ci	if (ret) {
3008c2ecf20Sopenharmony_ci		pr_err("%s: Could not get vdda reg, ret = %d\n", __func__,
3018c2ecf20Sopenharmony_ci				ret);
3028c2ecf20Sopenharmony_ci		ctrl->vdda_vreg = NULL;
3038c2ecf20Sopenharmony_ci		return ret;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci	ctrl->lvl_vreg = devm_regulator_get(dev, "lvl-vdd");
3068c2ecf20Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(ctrl->lvl_vreg);
3078c2ecf20Sopenharmony_ci	if (ret) {
3088c2ecf20Sopenharmony_ci		pr_err("%s: Could not get lvl-vdd reg, ret = %d\n", __func__,
3098c2ecf20Sopenharmony_ci				ret);
3108c2ecf20Sopenharmony_ci		ctrl->lvl_vreg = NULL;
3118c2ecf20Sopenharmony_ci		return ret;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int edp_regulator_enable(struct edp_ctrl *ctrl)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	int ret;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	ret = regulator_set_load(ctrl->vdda_vreg, VDDA_UA_ON_LOAD);
3228c2ecf20Sopenharmony_ci	if (ret < 0) {
3238c2ecf20Sopenharmony_ci		pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__);
3248c2ecf20Sopenharmony_ci		goto vdda_set_fail;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	ret = regulator_enable(ctrl->vdda_vreg);
3288c2ecf20Sopenharmony_ci	if (ret) {
3298c2ecf20Sopenharmony_ci		pr_err("%s: Failed to enable vdda_vreg regulator.\n", __func__);
3308c2ecf20Sopenharmony_ci		goto vdda_enable_fail;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	ret = regulator_enable(ctrl->lvl_vreg);
3348c2ecf20Sopenharmony_ci	if (ret) {
3358c2ecf20Sopenharmony_ci		pr_err("Failed to enable lvl-vdd reg regulator, %d", ret);
3368c2ecf20Sopenharmony_ci		goto lvl_enable_fail;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	DBG("exit");
3408c2ecf20Sopenharmony_ci	return 0;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cilvl_enable_fail:
3438c2ecf20Sopenharmony_ci	regulator_disable(ctrl->vdda_vreg);
3448c2ecf20Sopenharmony_civdda_enable_fail:
3458c2ecf20Sopenharmony_ci	regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD);
3468c2ecf20Sopenharmony_civdda_set_fail:
3478c2ecf20Sopenharmony_ci	return ret;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void edp_regulator_disable(struct edp_ctrl *ctrl)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	regulator_disable(ctrl->lvl_vreg);
3538c2ecf20Sopenharmony_ci	regulator_disable(ctrl->vdda_vreg);
3548c2ecf20Sopenharmony_ci	regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic int edp_gpio_config(struct edp_ctrl *ctrl)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct device *dev = &ctrl->pdev->dev;
3608c2ecf20Sopenharmony_ci	int ret;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	ctrl->panel_hpd_gpio = devm_gpiod_get(dev, "panel-hpd", GPIOD_IN);
3638c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->panel_hpd_gpio)) {
3648c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctrl->panel_hpd_gpio);
3658c2ecf20Sopenharmony_ci		ctrl->panel_hpd_gpio = NULL;
3668c2ecf20Sopenharmony_ci		pr_err("%s: cannot get panel-hpd-gpios, %d\n", __func__, ret);
3678c2ecf20Sopenharmony_ci		return ret;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	ctrl->panel_en_gpio = devm_gpiod_get(dev, "panel-en", GPIOD_OUT_LOW);
3718c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->panel_en_gpio)) {
3728c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctrl->panel_en_gpio);
3738c2ecf20Sopenharmony_ci		ctrl->panel_en_gpio = NULL;
3748c2ecf20Sopenharmony_ci		pr_err("%s: cannot get panel-en-gpios, %d\n", __func__, ret);
3758c2ecf20Sopenharmony_ci		return ret;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	DBG("gpio on");
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return 0;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic void edp_ctrl_irq_enable(struct edp_ctrl *ctrl, int enable)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	unsigned long flags;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	DBG("%d", enable);
3888c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl->irq_lock, flags);
3898c2ecf20Sopenharmony_ci	if (enable) {
3908c2ecf20Sopenharmony_ci		edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_1, EDP_INTR_MASK1);
3918c2ecf20Sopenharmony_ci		edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_2, EDP_INTR_MASK2);
3928c2ecf20Sopenharmony_ci	} else {
3938c2ecf20Sopenharmony_ci		edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_1, 0x0);
3948c2ecf20Sopenharmony_ci		edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_2, 0x0);
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl->irq_lock, flags);
3978c2ecf20Sopenharmony_ci	DBG("exit");
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic void edp_fill_link_cfg(struct edp_ctrl *ctrl)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	u32 prate;
4038c2ecf20Sopenharmony_ci	u32 lrate;
4048c2ecf20Sopenharmony_ci	u32 bpp;
4058c2ecf20Sopenharmony_ci	u8 max_lane = drm_dp_max_lane_count(ctrl->dpcd);
4068c2ecf20Sopenharmony_ci	u8 lane;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	prate = ctrl->pixel_rate;
4098c2ecf20Sopenharmony_ci	bpp = ctrl->color_depth * 3;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/*
4128c2ecf20Sopenharmony_ci	 * By default, use the maximum link rate and minimum lane count,
4138c2ecf20Sopenharmony_ci	 * so that we can do rate down shift during link training.
4148c2ecf20Sopenharmony_ci	 */
4158c2ecf20Sopenharmony_ci	ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	prate *= bpp;
4188c2ecf20Sopenharmony_ci	prate /= 8; /* in kByte */
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	lrate = 270000; /* in kHz */
4218c2ecf20Sopenharmony_ci	lrate *= ctrl->link_rate;
4228c2ecf20Sopenharmony_ci	lrate /= 10; /* in kByte, 10 bits --> 8 bits */
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	for (lane = 1; lane <= max_lane; lane <<= 1) {
4258c2ecf20Sopenharmony_ci		if (lrate >= prate)
4268c2ecf20Sopenharmony_ci			break;
4278c2ecf20Sopenharmony_ci		lrate <<= 1;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	ctrl->lane_cnt = lane;
4318c2ecf20Sopenharmony_ci	DBG("rate=%d lane=%d", ctrl->link_rate, ctrl->lane_cnt);
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic void edp_config_ctrl(struct edp_ctrl *ctrl)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	u32 data;
4378c2ecf20Sopenharmony_ci	enum edp_color_depth depth;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	data = EDP_CONFIGURATION_CTRL_LANES(ctrl->lane_cnt - 1);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (drm_dp_enhanced_frame_cap(ctrl->dpcd))
4428c2ecf20Sopenharmony_ci		data |= EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	depth = EDP_6BIT;
4458c2ecf20Sopenharmony_ci	if (ctrl->color_depth == 8)
4468c2ecf20Sopenharmony_ci		depth = EDP_8BIT;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	data |= EDP_CONFIGURATION_CTRL_COLOR(depth);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (!ctrl->interlaced)	/* progressive */
4518c2ecf20Sopenharmony_ci		data |= EDP_CONFIGURATION_CTRL_PROGRESSIVE;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	data |= (EDP_CONFIGURATION_CTRL_SYNC_CLK |
4548c2ecf20Sopenharmony_ci		EDP_CONFIGURATION_CTRL_STATIC_MVID);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_CONFIGURATION_CTRL, data);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic void edp_state_ctrl(struct edp_ctrl *ctrl, u32 state)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_STATE_CTRL, state);
4628c2ecf20Sopenharmony_ci	/* Make sure H/W status is set */
4638c2ecf20Sopenharmony_ci	wmb();
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic int edp_lane_set_write(struct edp_ctrl *ctrl,
4678c2ecf20Sopenharmony_ci	u8 voltage_level, u8 pre_emphasis_level)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	int i;
4708c2ecf20Sopenharmony_ci	u8 buf[4];
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (voltage_level >= DPCD_LINK_VOLTAGE_MAX)
4738c2ecf20Sopenharmony_ci		voltage_level |= 0x04;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (pre_emphasis_level >= DPCD_LINK_PRE_EMPHASIS_MAX)
4768c2ecf20Sopenharmony_ci		pre_emphasis_level |= 0x04;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	pre_emphasis_level <<= 3;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
4818c2ecf20Sopenharmony_ci		buf[i] = voltage_level | pre_emphasis_level;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	DBG("%s: p|v=0x%x", __func__, voltage_level | pre_emphasis_level);
4848c2ecf20Sopenharmony_ci	if (drm_dp_dpcd_write(ctrl->drm_aux, 0x103, buf, 4) < 4) {
4858c2ecf20Sopenharmony_ci		pr_err("%s: Set sw/pe to panel failed\n", __func__);
4868c2ecf20Sopenharmony_ci		return -ENOLINK;
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	return 0;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic int edp_train_pattern_set_write(struct edp_ctrl *ctrl, u8 pattern)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	u8 p = pattern;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	DBG("pattern=%x", p);
4978c2ecf20Sopenharmony_ci	if (drm_dp_dpcd_write(ctrl->drm_aux,
4988c2ecf20Sopenharmony_ci				DP_TRAINING_PATTERN_SET, &p, 1) < 1) {
4998c2ecf20Sopenharmony_ci		pr_err("%s: Set training pattern to panel failed\n", __func__);
5008c2ecf20Sopenharmony_ci		return -ENOLINK;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	return 0;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic void edp_sink_train_set_adjust(struct edp_ctrl *ctrl,
5078c2ecf20Sopenharmony_ci	const u8 *link_status)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	int i;
5108c2ecf20Sopenharmony_ci	u8 max = 0;
5118c2ecf20Sopenharmony_ci	u8 data;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* use the max level across lanes */
5148c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl->lane_cnt; i++) {
5158c2ecf20Sopenharmony_ci		data = drm_dp_get_adjust_request_voltage(link_status, i);
5168c2ecf20Sopenharmony_ci		DBG("lane=%d req_voltage_swing=0x%x", i, data);
5178c2ecf20Sopenharmony_ci		if (max < data)
5188c2ecf20Sopenharmony_ci			max = data;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	ctrl->v_level = max >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* use the max level across lanes */
5248c2ecf20Sopenharmony_ci	max = 0;
5258c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl->lane_cnt; i++) {
5268c2ecf20Sopenharmony_ci		data = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
5278c2ecf20Sopenharmony_ci		DBG("lane=%d req_pre_emphasis=0x%x", i, data);
5288c2ecf20Sopenharmony_ci		if (max < data)
5298c2ecf20Sopenharmony_ci			max = data;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	ctrl->p_level = max >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
5338c2ecf20Sopenharmony_ci	DBG("v_level=%d, p_level=%d", ctrl->v_level, ctrl->p_level);
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void edp_host_train_set(struct edp_ctrl *ctrl, u32 train)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	int cnt = 10;
5398c2ecf20Sopenharmony_ci	u32 data;
5408c2ecf20Sopenharmony_ci	u32 shift = train - 1;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	DBG("train=%d", train);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	edp_state_ctrl(ctrl, EDP_STATE_CTRL_TRAIN_PATTERN_1 << shift);
5458c2ecf20Sopenharmony_ci	while (--cnt) {
5468c2ecf20Sopenharmony_ci		data = edp_read(ctrl->base + REG_EDP_MAINLINK_READY);
5478c2ecf20Sopenharmony_ci		if (data & (EDP_MAINLINK_READY_TRAIN_PATTERN_1_READY << shift))
5488c2ecf20Sopenharmony_ci			break;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	if (cnt == 0)
5528c2ecf20Sopenharmony_ci		pr_err("%s: set link_train=%d failed\n", __func__, train);
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic const u8 vm_pre_emphasis[4][4] = {
5568c2ecf20Sopenharmony_ci	{0x03, 0x06, 0x09, 0x0C},	/* pe0, 0 db */
5578c2ecf20Sopenharmony_ci	{0x03, 0x06, 0x09, 0xFF},	/* pe1, 3.5 db */
5588c2ecf20Sopenharmony_ci	{0x03, 0x06, 0xFF, 0xFF},	/* pe2, 6.0 db */
5598c2ecf20Sopenharmony_ci	{0x03, 0xFF, 0xFF, 0xFF}	/* pe3, 9.5 db */
5608c2ecf20Sopenharmony_ci};
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci/* voltage swing, 0.2v and 1.0v are not support */
5638c2ecf20Sopenharmony_cistatic const u8 vm_voltage_swing[4][4] = {
5648c2ecf20Sopenharmony_ci	{0x14, 0x18, 0x1A, 0x1E}, /* sw0, 0.4v  */
5658c2ecf20Sopenharmony_ci	{0x18, 0x1A, 0x1E, 0xFF}, /* sw1, 0.6 v */
5668c2ecf20Sopenharmony_ci	{0x1A, 0x1E, 0xFF, 0xFF}, /* sw1, 0.8 v */
5678c2ecf20Sopenharmony_ci	{0x1E, 0xFF, 0xFF, 0xFF}  /* sw1, 1.2 v, optional */
5688c2ecf20Sopenharmony_ci};
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic int edp_voltage_pre_emphasise_set(struct edp_ctrl *ctrl)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	u32 value0;
5738c2ecf20Sopenharmony_ci	u32 value1;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	DBG("v=%d p=%d", ctrl->v_level, ctrl->p_level);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	value0 = vm_pre_emphasis[(int)(ctrl->v_level)][(int)(ctrl->p_level)];
5788c2ecf20Sopenharmony_ci	value1 = vm_voltage_swing[(int)(ctrl->v_level)][(int)(ctrl->p_level)];
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	/* Configure host and panel only if both values are allowed */
5818c2ecf20Sopenharmony_ci	if (value0 != 0xFF && value1 != 0xFF) {
5828c2ecf20Sopenharmony_ci		msm_edp_phy_vm_pe_cfg(ctrl->phy, value0, value1);
5838c2ecf20Sopenharmony_ci		return edp_lane_set_write(ctrl, ctrl->v_level, ctrl->p_level);
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return -EINVAL;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic int edp_start_link_train_1(struct edp_ctrl *ctrl)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	u8 link_status[DP_LINK_STATUS_SIZE];
5928c2ecf20Sopenharmony_ci	u8 old_v_level;
5938c2ecf20Sopenharmony_ci	int tries;
5948c2ecf20Sopenharmony_ci	int ret;
5958c2ecf20Sopenharmony_ci	int rlen;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	DBG("");
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	edp_host_train_set(ctrl, DP_TRAINING_PATTERN_1);
6008c2ecf20Sopenharmony_ci	ret = edp_voltage_pre_emphasise_set(ctrl);
6018c2ecf20Sopenharmony_ci	if (ret)
6028c2ecf20Sopenharmony_ci		return ret;
6038c2ecf20Sopenharmony_ci	ret = edp_train_pattern_set_write(ctrl,
6048c2ecf20Sopenharmony_ci			DP_TRAINING_PATTERN_1 | DP_RECOVERED_CLOCK_OUT_EN);
6058c2ecf20Sopenharmony_ci	if (ret)
6068c2ecf20Sopenharmony_ci		return ret;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	tries = 0;
6098c2ecf20Sopenharmony_ci	old_v_level = ctrl->v_level;
6108c2ecf20Sopenharmony_ci	while (1) {
6118c2ecf20Sopenharmony_ci		drm_dp_link_train_clock_recovery_delay(ctrl->dpcd);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci		rlen = drm_dp_dpcd_read_link_status(ctrl->drm_aux, link_status);
6148c2ecf20Sopenharmony_ci		if (rlen < DP_LINK_STATUS_SIZE) {
6158c2ecf20Sopenharmony_ci			pr_err("%s: read link status failed\n", __func__);
6168c2ecf20Sopenharmony_ci			return -ENOLINK;
6178c2ecf20Sopenharmony_ci		}
6188c2ecf20Sopenharmony_ci		if (drm_dp_clock_recovery_ok(link_status, ctrl->lane_cnt)) {
6198c2ecf20Sopenharmony_ci			ret = 0;
6208c2ecf20Sopenharmony_ci			break;
6218c2ecf20Sopenharmony_ci		}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci		if (ctrl->v_level == DPCD_LINK_VOLTAGE_MAX) {
6248c2ecf20Sopenharmony_ci			ret = -1;
6258c2ecf20Sopenharmony_ci			break;
6268c2ecf20Sopenharmony_ci		}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci		if (old_v_level == ctrl->v_level) {
6298c2ecf20Sopenharmony_ci			tries++;
6308c2ecf20Sopenharmony_ci			if (tries >= 5) {
6318c2ecf20Sopenharmony_ci				ret = -1;
6328c2ecf20Sopenharmony_ci				break;
6338c2ecf20Sopenharmony_ci			}
6348c2ecf20Sopenharmony_ci		} else {
6358c2ecf20Sopenharmony_ci			tries = 0;
6368c2ecf20Sopenharmony_ci			old_v_level = ctrl->v_level;
6378c2ecf20Sopenharmony_ci		}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		edp_sink_train_set_adjust(ctrl, link_status);
6408c2ecf20Sopenharmony_ci		ret = edp_voltage_pre_emphasise_set(ctrl);
6418c2ecf20Sopenharmony_ci		if (ret)
6428c2ecf20Sopenharmony_ci			return ret;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	return ret;
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic int edp_start_link_train_2(struct edp_ctrl *ctrl)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	u8 link_status[DP_LINK_STATUS_SIZE];
6518c2ecf20Sopenharmony_ci	int tries = 0;
6528c2ecf20Sopenharmony_ci	int ret;
6538c2ecf20Sopenharmony_ci	int rlen;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	DBG("");
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	edp_host_train_set(ctrl, DP_TRAINING_PATTERN_2);
6588c2ecf20Sopenharmony_ci	ret = edp_voltage_pre_emphasise_set(ctrl);
6598c2ecf20Sopenharmony_ci	if (ret)
6608c2ecf20Sopenharmony_ci		return ret;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	ret = edp_train_pattern_set_write(ctrl,
6638c2ecf20Sopenharmony_ci			DP_TRAINING_PATTERN_2 | DP_RECOVERED_CLOCK_OUT_EN);
6648c2ecf20Sopenharmony_ci	if (ret)
6658c2ecf20Sopenharmony_ci		return ret;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	while (1) {
6688c2ecf20Sopenharmony_ci		drm_dp_link_train_channel_eq_delay(ctrl->dpcd);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci		rlen = drm_dp_dpcd_read_link_status(ctrl->drm_aux, link_status);
6718c2ecf20Sopenharmony_ci		if (rlen < DP_LINK_STATUS_SIZE) {
6728c2ecf20Sopenharmony_ci			pr_err("%s: read link status failed\n", __func__);
6738c2ecf20Sopenharmony_ci			return -ENOLINK;
6748c2ecf20Sopenharmony_ci		}
6758c2ecf20Sopenharmony_ci		if (drm_dp_channel_eq_ok(link_status, ctrl->lane_cnt)) {
6768c2ecf20Sopenharmony_ci			ret = 0;
6778c2ecf20Sopenharmony_ci			break;
6788c2ecf20Sopenharmony_ci		}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci		tries++;
6818c2ecf20Sopenharmony_ci		if (tries > 10) {
6828c2ecf20Sopenharmony_ci			ret = -1;
6838c2ecf20Sopenharmony_ci			break;
6848c2ecf20Sopenharmony_ci		}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		edp_sink_train_set_adjust(ctrl, link_status);
6878c2ecf20Sopenharmony_ci		ret = edp_voltage_pre_emphasise_set(ctrl);
6888c2ecf20Sopenharmony_ci		if (ret)
6898c2ecf20Sopenharmony_ci			return ret;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return ret;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int edp_link_rate_down_shift(struct edp_ctrl *ctrl)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	u32 prate, lrate, bpp;
6988c2ecf20Sopenharmony_ci	u8 rate, lane, max_lane;
6998c2ecf20Sopenharmony_ci	int changed = 0;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	rate = ctrl->link_rate;
7028c2ecf20Sopenharmony_ci	lane = ctrl->lane_cnt;
7038c2ecf20Sopenharmony_ci	max_lane = drm_dp_max_lane_count(ctrl->dpcd);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	bpp = ctrl->color_depth * 3;
7068c2ecf20Sopenharmony_ci	prate = ctrl->pixel_rate;
7078c2ecf20Sopenharmony_ci	prate *= bpp;
7088c2ecf20Sopenharmony_ci	prate /= 8; /* in kByte */
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	if (rate > DP_LINK_BW_1_62 && rate <= EDP_LINK_BW_MAX) {
7118c2ecf20Sopenharmony_ci		rate -= 4;	/* reduce rate */
7128c2ecf20Sopenharmony_ci		changed++;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (changed) {
7168c2ecf20Sopenharmony_ci		if (lane >= 1 && lane < max_lane)
7178c2ecf20Sopenharmony_ci			lane <<= 1;	/* increase lane */
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci		lrate = 270000; /* in kHz */
7208c2ecf20Sopenharmony_ci		lrate *= rate;
7218c2ecf20Sopenharmony_ci		lrate /= 10; /* kByte, 10 bits --> 8 bits */
7228c2ecf20Sopenharmony_ci		lrate *= lane;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		DBG("new lrate=%u prate=%u(kHz) rate=%d lane=%d p=%u b=%d",
7258c2ecf20Sopenharmony_ci			lrate, prate, rate, lane,
7268c2ecf20Sopenharmony_ci			ctrl->pixel_rate,
7278c2ecf20Sopenharmony_ci			bpp);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci		if (lrate > prate) {
7308c2ecf20Sopenharmony_ci			ctrl->link_rate = rate;
7318c2ecf20Sopenharmony_ci			ctrl->lane_cnt = lane;
7328c2ecf20Sopenharmony_ci			DBG("new rate=%d %d", rate, lane);
7338c2ecf20Sopenharmony_ci			return 0;
7348c2ecf20Sopenharmony_ci		}
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	return -EINVAL;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic int edp_clear_training_pattern(struct edp_ctrl *ctrl)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	int ret;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	ret = edp_train_pattern_set_write(ctrl, 0);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	drm_dp_link_train_channel_eq_delay(ctrl->dpcd);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	return ret;
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic int edp_do_link_train(struct edp_ctrl *ctrl)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	u8 values[2];
7548c2ecf20Sopenharmony_ci	int ret;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	DBG("");
7578c2ecf20Sopenharmony_ci	/*
7588c2ecf20Sopenharmony_ci	 * Set the current link rate and lane cnt to panel. They may have been
7598c2ecf20Sopenharmony_ci	 * adjusted and the values are different from them in DPCD CAP
7608c2ecf20Sopenharmony_ci	 */
7618c2ecf20Sopenharmony_ci	values[0] = ctrl->lane_cnt;
7628c2ecf20Sopenharmony_ci	values[1] = ctrl->link_rate;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (drm_dp_enhanced_frame_cap(ctrl->dpcd))
7658c2ecf20Sopenharmony_ci		values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (drm_dp_dpcd_write(ctrl->drm_aux, DP_LINK_BW_SET, values,
7688c2ecf20Sopenharmony_ci			      sizeof(values)) < 0)
7698c2ecf20Sopenharmony_ci		return EDP_TRAIN_FAIL;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	ctrl->v_level = 0; /* start from default level */
7728c2ecf20Sopenharmony_ci	ctrl->p_level = 0;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	edp_state_ctrl(ctrl, 0);
7758c2ecf20Sopenharmony_ci	if (edp_clear_training_pattern(ctrl))
7768c2ecf20Sopenharmony_ci		return EDP_TRAIN_FAIL;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	ret = edp_start_link_train_1(ctrl);
7798c2ecf20Sopenharmony_ci	if (ret < 0) {
7808c2ecf20Sopenharmony_ci		if (edp_link_rate_down_shift(ctrl) == 0) {
7818c2ecf20Sopenharmony_ci			DBG("link reconfig");
7828c2ecf20Sopenharmony_ci			ret = EDP_TRAIN_RECONFIG;
7838c2ecf20Sopenharmony_ci			goto clear;
7848c2ecf20Sopenharmony_ci		} else {
7858c2ecf20Sopenharmony_ci			pr_err("%s: Training 1 failed", __func__);
7868c2ecf20Sopenharmony_ci			ret = EDP_TRAIN_FAIL;
7878c2ecf20Sopenharmony_ci			goto clear;
7888c2ecf20Sopenharmony_ci		}
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci	DBG("Training 1 completed successfully");
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	edp_state_ctrl(ctrl, 0);
7938c2ecf20Sopenharmony_ci	if (edp_clear_training_pattern(ctrl))
7948c2ecf20Sopenharmony_ci		return EDP_TRAIN_FAIL;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	ret = edp_start_link_train_2(ctrl);
7978c2ecf20Sopenharmony_ci	if (ret < 0) {
7988c2ecf20Sopenharmony_ci		if (edp_link_rate_down_shift(ctrl) == 0) {
7998c2ecf20Sopenharmony_ci			DBG("link reconfig");
8008c2ecf20Sopenharmony_ci			ret = EDP_TRAIN_RECONFIG;
8018c2ecf20Sopenharmony_ci			goto clear;
8028c2ecf20Sopenharmony_ci		} else {
8038c2ecf20Sopenharmony_ci			pr_err("%s: Training 2 failed", __func__);
8048c2ecf20Sopenharmony_ci			ret = EDP_TRAIN_FAIL;
8058c2ecf20Sopenharmony_ci			goto clear;
8068c2ecf20Sopenharmony_ci		}
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci	DBG("Training 2 completed successfully");
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	edp_state_ctrl(ctrl, EDP_STATE_CTRL_SEND_VIDEO);
8118c2ecf20Sopenharmony_ciclear:
8128c2ecf20Sopenharmony_ci	edp_clear_training_pattern(ctrl);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	return ret;
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_cistatic void edp_clock_synchrous(struct edp_ctrl *ctrl, int sync)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	u32 data;
8208c2ecf20Sopenharmony_ci	enum edp_color_depth depth;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	data = edp_read(ctrl->base + REG_EDP_MISC1_MISC0);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	if (sync)
8258c2ecf20Sopenharmony_ci		data |= EDP_MISC1_MISC0_SYNC;
8268c2ecf20Sopenharmony_ci	else
8278c2ecf20Sopenharmony_ci		data &= ~EDP_MISC1_MISC0_SYNC;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/* only legacy rgb mode supported */
8308c2ecf20Sopenharmony_ci	depth = EDP_6BIT; /* Default */
8318c2ecf20Sopenharmony_ci	if (ctrl->color_depth == 8)
8328c2ecf20Sopenharmony_ci		depth = EDP_8BIT;
8338c2ecf20Sopenharmony_ci	else if (ctrl->color_depth == 10)
8348c2ecf20Sopenharmony_ci		depth = EDP_10BIT;
8358c2ecf20Sopenharmony_ci	else if (ctrl->color_depth == 12)
8368c2ecf20Sopenharmony_ci		depth = EDP_12BIT;
8378c2ecf20Sopenharmony_ci	else if (ctrl->color_depth == 16)
8388c2ecf20Sopenharmony_ci		depth = EDP_16BIT;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	data |= EDP_MISC1_MISC0_COLOR(depth);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_MISC1_MISC0, data);
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic int edp_sw_mvid_nvid(struct edp_ctrl *ctrl, u32 m, u32 n)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	u32 n_multi, m_multi = 5;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	if (ctrl->link_rate == DP_LINK_BW_1_62) {
8508c2ecf20Sopenharmony_ci		n_multi = 1;
8518c2ecf20Sopenharmony_ci	} else if (ctrl->link_rate == DP_LINK_BW_2_7) {
8528c2ecf20Sopenharmony_ci		n_multi = 2;
8538c2ecf20Sopenharmony_ci	} else {
8548c2ecf20Sopenharmony_ci		pr_err("%s: Invalid link rate, %d\n", __func__,
8558c2ecf20Sopenharmony_ci			ctrl->link_rate);
8568c2ecf20Sopenharmony_ci		return -EINVAL;
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_SOFTWARE_MVID, m * m_multi);
8608c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_SOFTWARE_NVID, n * n_multi);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	return 0;
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic void edp_mainlink_ctrl(struct edp_ctrl *ctrl, int enable)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	u32 data = 0;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_MAINLINK_CTRL, EDP_MAINLINK_CTRL_RESET);
8708c2ecf20Sopenharmony_ci	/* Make sure fully reset */
8718c2ecf20Sopenharmony_ci	wmb();
8728c2ecf20Sopenharmony_ci	usleep_range(500, 1000);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (enable)
8758c2ecf20Sopenharmony_ci		data |= EDP_MAINLINK_CTRL_ENABLE;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_MAINLINK_CTRL, data);
8788c2ecf20Sopenharmony_ci}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_cistatic void edp_ctrl_phy_aux_enable(struct edp_ctrl *ctrl, int enable)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	if (enable) {
8838c2ecf20Sopenharmony_ci		edp_regulator_enable(ctrl);
8848c2ecf20Sopenharmony_ci		edp_clk_enable(ctrl, EDP_CLK_MASK_AUX_CHAN);
8858c2ecf20Sopenharmony_ci		msm_edp_phy_ctrl(ctrl->phy, 1);
8868c2ecf20Sopenharmony_ci		msm_edp_aux_ctrl(ctrl->aux, 1);
8878c2ecf20Sopenharmony_ci		gpiod_set_value(ctrl->panel_en_gpio, 1);
8888c2ecf20Sopenharmony_ci	} else {
8898c2ecf20Sopenharmony_ci		gpiod_set_value(ctrl->panel_en_gpio, 0);
8908c2ecf20Sopenharmony_ci		msm_edp_aux_ctrl(ctrl->aux, 0);
8918c2ecf20Sopenharmony_ci		msm_edp_phy_ctrl(ctrl->phy, 0);
8928c2ecf20Sopenharmony_ci		edp_clk_disable(ctrl, EDP_CLK_MASK_AUX_CHAN);
8938c2ecf20Sopenharmony_ci		edp_regulator_disable(ctrl);
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic void edp_ctrl_link_enable(struct edp_ctrl *ctrl, int enable)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	u32 m, n;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (enable) {
9028c2ecf20Sopenharmony_ci		/* Enable link channel clocks */
9038c2ecf20Sopenharmony_ci		edp_clk_enable(ctrl, EDP_CLK_MASK_LINK_CHAN);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci		msm_edp_phy_lane_power_ctrl(ctrl->phy, true, ctrl->lane_cnt);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci		msm_edp_phy_vm_pe_init(ctrl->phy);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci		/* Make sure phy is programed */
9108c2ecf20Sopenharmony_ci		wmb();
9118c2ecf20Sopenharmony_ci		msm_edp_phy_ready(ctrl->phy);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci		edp_config_ctrl(ctrl);
9148c2ecf20Sopenharmony_ci		msm_edp_ctrl_pixel_clock_valid(ctrl, ctrl->pixel_rate, &m, &n);
9158c2ecf20Sopenharmony_ci		edp_sw_mvid_nvid(ctrl, m, n);
9168c2ecf20Sopenharmony_ci		edp_mainlink_ctrl(ctrl, 1);
9178c2ecf20Sopenharmony_ci	} else {
9188c2ecf20Sopenharmony_ci		edp_mainlink_ctrl(ctrl, 0);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci		msm_edp_phy_lane_power_ctrl(ctrl->phy, false, 0);
9218c2ecf20Sopenharmony_ci		edp_clk_disable(ctrl, EDP_CLK_MASK_LINK_CHAN);
9228c2ecf20Sopenharmony_ci	}
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cistatic int edp_ctrl_training(struct edp_ctrl *ctrl)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	int ret;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	/* Do link training only when power is on */
9308c2ecf20Sopenharmony_ci	if (!ctrl->power_on)
9318c2ecf20Sopenharmony_ci		return -EINVAL;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_citrain_start:
9348c2ecf20Sopenharmony_ci	ret = edp_do_link_train(ctrl);
9358c2ecf20Sopenharmony_ci	if (ret == EDP_TRAIN_RECONFIG) {
9368c2ecf20Sopenharmony_ci		/* Re-configure main link */
9378c2ecf20Sopenharmony_ci		edp_ctrl_irq_enable(ctrl, 0);
9388c2ecf20Sopenharmony_ci		edp_ctrl_link_enable(ctrl, 0);
9398c2ecf20Sopenharmony_ci		msm_edp_phy_ctrl(ctrl->phy, 0);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci		/* Make sure link is fully disabled */
9428c2ecf20Sopenharmony_ci		wmb();
9438c2ecf20Sopenharmony_ci		usleep_range(500, 1000);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci		msm_edp_phy_ctrl(ctrl->phy, 1);
9468c2ecf20Sopenharmony_ci		edp_ctrl_link_enable(ctrl, 1);
9478c2ecf20Sopenharmony_ci		edp_ctrl_irq_enable(ctrl, 1);
9488c2ecf20Sopenharmony_ci		goto train_start;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	return ret;
9528c2ecf20Sopenharmony_ci}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_cistatic void edp_ctrl_on_worker(struct work_struct *work)
9558c2ecf20Sopenharmony_ci{
9568c2ecf20Sopenharmony_ci	struct edp_ctrl *ctrl = container_of(
9578c2ecf20Sopenharmony_ci				work, struct edp_ctrl, on_work);
9588c2ecf20Sopenharmony_ci	u8 value;
9598c2ecf20Sopenharmony_ci	int ret;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	mutex_lock(&ctrl->dev_mutex);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	if (ctrl->power_on) {
9648c2ecf20Sopenharmony_ci		DBG("already on");
9658c2ecf20Sopenharmony_ci		goto unlock_ret;
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	edp_ctrl_phy_aux_enable(ctrl, 1);
9698c2ecf20Sopenharmony_ci	edp_ctrl_link_enable(ctrl, 1);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	edp_ctrl_irq_enable(ctrl, 1);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
9748c2ecf20Sopenharmony_ci	if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
9758c2ecf20Sopenharmony_ci		ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
9768c2ecf20Sopenharmony_ci		if (ret < 0)
9778c2ecf20Sopenharmony_ci			goto fail;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci		value &= ~DP_SET_POWER_MASK;
9808c2ecf20Sopenharmony_ci		value |= DP_SET_POWER_D0;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci		ret = drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
9838c2ecf20Sopenharmony_ci		if (ret < 0)
9848c2ecf20Sopenharmony_ci			goto fail;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci		/*
9878c2ecf20Sopenharmony_ci		 * According to the DP 1.1 specification, a "Sink Device must
9888c2ecf20Sopenharmony_ci		 * exit the power saving state within 1 ms" (Section 2.5.3.1,
9898c2ecf20Sopenharmony_ci		 * Table 5-52, "Sink Control Field" (register 0x600).
9908c2ecf20Sopenharmony_ci		 */
9918c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
9928c2ecf20Sopenharmony_ci	}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	ctrl->power_on = true;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	/* Start link training */
9978c2ecf20Sopenharmony_ci	ret = edp_ctrl_training(ctrl);
9988c2ecf20Sopenharmony_ci	if (ret != EDP_TRAIN_SUCCESS)
9998c2ecf20Sopenharmony_ci		goto fail;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	DBG("DONE");
10028c2ecf20Sopenharmony_ci	goto unlock_ret;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cifail:
10058c2ecf20Sopenharmony_ci	edp_ctrl_irq_enable(ctrl, 0);
10068c2ecf20Sopenharmony_ci	edp_ctrl_link_enable(ctrl, 0);
10078c2ecf20Sopenharmony_ci	edp_ctrl_phy_aux_enable(ctrl, 0);
10088c2ecf20Sopenharmony_ci	ctrl->power_on = false;
10098c2ecf20Sopenharmony_ciunlock_ret:
10108c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl->dev_mutex);
10118c2ecf20Sopenharmony_ci}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cistatic void edp_ctrl_off_worker(struct work_struct *work)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci	struct edp_ctrl *ctrl = container_of(
10168c2ecf20Sopenharmony_ci				work, struct edp_ctrl, off_work);
10178c2ecf20Sopenharmony_ci	unsigned long time_left;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	mutex_lock(&ctrl->dev_mutex);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	if (!ctrl->power_on) {
10228c2ecf20Sopenharmony_ci		DBG("already off");
10238c2ecf20Sopenharmony_ci		goto unlock_ret;
10248c2ecf20Sopenharmony_ci	}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	reinit_completion(&ctrl->idle_comp);
10278c2ecf20Sopenharmony_ci	edp_state_ctrl(ctrl, EDP_STATE_CTRL_PUSH_IDLE);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	time_left = wait_for_completion_timeout(&ctrl->idle_comp,
10308c2ecf20Sopenharmony_ci						msecs_to_jiffies(500));
10318c2ecf20Sopenharmony_ci	if (!time_left)
10328c2ecf20Sopenharmony_ci		DBG("%s: idle pattern timedout\n", __func__);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	edp_state_ctrl(ctrl, 0);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
10378c2ecf20Sopenharmony_ci	if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
10388c2ecf20Sopenharmony_ci		u8 value;
10398c2ecf20Sopenharmony_ci		int ret;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci		ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
10428c2ecf20Sopenharmony_ci		if (ret > 0) {
10438c2ecf20Sopenharmony_ci			value &= ~DP_SET_POWER_MASK;
10448c2ecf20Sopenharmony_ci			value |= DP_SET_POWER_D3;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci			drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
10478c2ecf20Sopenharmony_ci		}
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	edp_ctrl_irq_enable(ctrl, 0);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	edp_ctrl_link_enable(ctrl, 0);
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	edp_ctrl_phy_aux_enable(ctrl, 0);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	ctrl->power_on = false;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ciunlock_ret:
10598c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl->dev_mutex);
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ciirqreturn_t msm_edp_ctrl_irq(struct edp_ctrl *ctrl)
10638c2ecf20Sopenharmony_ci{
10648c2ecf20Sopenharmony_ci	u32 isr1, isr2, mask1, mask2;
10658c2ecf20Sopenharmony_ci	u32 ack;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	DBG("");
10688c2ecf20Sopenharmony_ci	spin_lock(&ctrl->irq_lock);
10698c2ecf20Sopenharmony_ci	isr1 = edp_read(ctrl->base + REG_EDP_INTERRUPT_REG_1);
10708c2ecf20Sopenharmony_ci	isr2 = edp_read(ctrl->base + REG_EDP_INTERRUPT_REG_2);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	mask1 = isr1 & EDP_INTR_MASK1;
10738c2ecf20Sopenharmony_ci	mask2 = isr2 & EDP_INTR_MASK2;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	isr1 &= ~mask1;	/* remove masks bit */
10768c2ecf20Sopenharmony_ci	isr2 &= ~mask2;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	DBG("isr=%x mask=%x isr2=%x mask2=%x",
10798c2ecf20Sopenharmony_ci			isr1, mask1, isr2, mask2);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	ack = isr1 & EDP_INTR_STATUS1;
10828c2ecf20Sopenharmony_ci	ack <<= 1;	/* ack bits */
10838c2ecf20Sopenharmony_ci	ack |= mask1;
10848c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_1, ack);
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	ack = isr2 & EDP_INTR_STATUS2;
10878c2ecf20Sopenharmony_ci	ack <<= 1;	/* ack bits */
10888c2ecf20Sopenharmony_ci	ack |= mask2;
10898c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_2, ack);
10908c2ecf20Sopenharmony_ci	spin_unlock(&ctrl->irq_lock);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	if (isr1 & EDP_INTERRUPT_REG_1_HPD)
10938c2ecf20Sopenharmony_ci		DBG("edp_hpd");
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	if (isr2 & EDP_INTERRUPT_REG_2_READY_FOR_VIDEO)
10968c2ecf20Sopenharmony_ci		DBG("edp_video_ready");
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	if (isr2 & EDP_INTERRUPT_REG_2_IDLE_PATTERNs_SENT) {
10998c2ecf20Sopenharmony_ci		DBG("idle_patterns_sent");
11008c2ecf20Sopenharmony_ci		complete(&ctrl->idle_comp);
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	msm_edp_aux_irq(ctrl->aux, isr1);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_civoid msm_edp_ctrl_power(struct edp_ctrl *ctrl, bool on)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	if (on)
11118c2ecf20Sopenharmony_ci		queue_work(ctrl->workqueue, &ctrl->on_work);
11128c2ecf20Sopenharmony_ci	else
11138c2ecf20Sopenharmony_ci		queue_work(ctrl->workqueue, &ctrl->off_work);
11148c2ecf20Sopenharmony_ci}
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ciint msm_edp_ctrl_init(struct msm_edp *edp)
11178c2ecf20Sopenharmony_ci{
11188c2ecf20Sopenharmony_ci	struct edp_ctrl *ctrl = NULL;
11198c2ecf20Sopenharmony_ci	struct device *dev;
11208c2ecf20Sopenharmony_ci	int ret;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (!edp) {
11238c2ecf20Sopenharmony_ci		pr_err("%s: edp is NULL!\n", __func__);
11248c2ecf20Sopenharmony_ci		return -EINVAL;
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	dev = &edp->pdev->dev;
11288c2ecf20Sopenharmony_ci	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
11298c2ecf20Sopenharmony_ci	if (!ctrl)
11308c2ecf20Sopenharmony_ci		return -ENOMEM;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	edp->ctrl = ctrl;
11338c2ecf20Sopenharmony_ci	ctrl->pdev = edp->pdev;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	ctrl->base = msm_ioremap(ctrl->pdev, "edp", "eDP");
11368c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->base))
11378c2ecf20Sopenharmony_ci		return PTR_ERR(ctrl->base);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/* Get regulator, clock, gpio, pwm */
11408c2ecf20Sopenharmony_ci	ret = edp_regulator_init(ctrl);
11418c2ecf20Sopenharmony_ci	if (ret) {
11428c2ecf20Sopenharmony_ci		pr_err("%s:regulator init fail\n", __func__);
11438c2ecf20Sopenharmony_ci		return ret;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci	ret = edp_clk_init(ctrl);
11468c2ecf20Sopenharmony_ci	if (ret) {
11478c2ecf20Sopenharmony_ci		pr_err("%s:clk init fail\n", __func__);
11488c2ecf20Sopenharmony_ci		return ret;
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci	ret = edp_gpio_config(ctrl);
11518c2ecf20Sopenharmony_ci	if (ret) {
11528c2ecf20Sopenharmony_ci		pr_err("%s:failed to configure GPIOs: %d", __func__, ret);
11538c2ecf20Sopenharmony_ci		return ret;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	/* Init aux and phy */
11578c2ecf20Sopenharmony_ci	ctrl->aux = msm_edp_aux_init(dev, ctrl->base, &ctrl->drm_aux);
11588c2ecf20Sopenharmony_ci	if (!ctrl->aux || !ctrl->drm_aux) {
11598c2ecf20Sopenharmony_ci		pr_err("%s:failed to init aux\n", __func__);
11608c2ecf20Sopenharmony_ci		return -ENOMEM;
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	ctrl->phy = msm_edp_phy_init(dev, ctrl->base);
11648c2ecf20Sopenharmony_ci	if (!ctrl->phy) {
11658c2ecf20Sopenharmony_ci		pr_err("%s:failed to init phy\n", __func__);
11668c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11678c2ecf20Sopenharmony_ci		goto err_destory_aux;
11688c2ecf20Sopenharmony_ci	}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	spin_lock_init(&ctrl->irq_lock);
11718c2ecf20Sopenharmony_ci	mutex_init(&ctrl->dev_mutex);
11728c2ecf20Sopenharmony_ci	init_completion(&ctrl->idle_comp);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	/* setup workqueue */
11758c2ecf20Sopenharmony_ci	ctrl->workqueue = alloc_ordered_workqueue("edp_drm_work", 0);
11768c2ecf20Sopenharmony_ci	INIT_WORK(&ctrl->on_work, edp_ctrl_on_worker);
11778c2ecf20Sopenharmony_ci	INIT_WORK(&ctrl->off_work, edp_ctrl_off_worker);
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	return 0;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_cierr_destory_aux:
11828c2ecf20Sopenharmony_ci	msm_edp_aux_destroy(dev, ctrl->aux);
11838c2ecf20Sopenharmony_ci	ctrl->aux = NULL;
11848c2ecf20Sopenharmony_ci	return ret;
11858c2ecf20Sopenharmony_ci}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_civoid msm_edp_ctrl_destroy(struct edp_ctrl *ctrl)
11888c2ecf20Sopenharmony_ci{
11898c2ecf20Sopenharmony_ci	if (!ctrl)
11908c2ecf20Sopenharmony_ci		return;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	if (ctrl->workqueue) {
11938c2ecf20Sopenharmony_ci		flush_workqueue(ctrl->workqueue);
11948c2ecf20Sopenharmony_ci		destroy_workqueue(ctrl->workqueue);
11958c2ecf20Sopenharmony_ci		ctrl->workqueue = NULL;
11968c2ecf20Sopenharmony_ci	}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	if (ctrl->aux) {
11998c2ecf20Sopenharmony_ci		msm_edp_aux_destroy(&ctrl->pdev->dev, ctrl->aux);
12008c2ecf20Sopenharmony_ci		ctrl->aux = NULL;
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	kfree(ctrl->edid);
12048c2ecf20Sopenharmony_ci	ctrl->edid = NULL;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	mutex_destroy(&ctrl->dev_mutex);
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_cibool msm_edp_ctrl_panel_connected(struct edp_ctrl *ctrl)
12108c2ecf20Sopenharmony_ci{
12118c2ecf20Sopenharmony_ci	mutex_lock(&ctrl->dev_mutex);
12128c2ecf20Sopenharmony_ci	DBG("connect status = %d", ctrl->edp_connected);
12138c2ecf20Sopenharmony_ci	if (ctrl->edp_connected) {
12148c2ecf20Sopenharmony_ci		mutex_unlock(&ctrl->dev_mutex);
12158c2ecf20Sopenharmony_ci		return true;
12168c2ecf20Sopenharmony_ci	}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	if (!ctrl->power_on) {
12198c2ecf20Sopenharmony_ci		edp_ctrl_phy_aux_enable(ctrl, 1);
12208c2ecf20Sopenharmony_ci		edp_ctrl_irq_enable(ctrl, 1);
12218c2ecf20Sopenharmony_ci	}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	if (drm_dp_dpcd_read(ctrl->drm_aux, DP_DPCD_REV, ctrl->dpcd,
12248c2ecf20Sopenharmony_ci				DP_RECEIVER_CAP_SIZE) < DP_RECEIVER_CAP_SIZE) {
12258c2ecf20Sopenharmony_ci		pr_err("%s: AUX channel is NOT ready\n", __func__);
12268c2ecf20Sopenharmony_ci		memset(ctrl->dpcd, 0, DP_RECEIVER_CAP_SIZE);
12278c2ecf20Sopenharmony_ci	} else {
12288c2ecf20Sopenharmony_ci		ctrl->edp_connected = true;
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	if (!ctrl->power_on) {
12328c2ecf20Sopenharmony_ci		edp_ctrl_irq_enable(ctrl, 0);
12338c2ecf20Sopenharmony_ci		edp_ctrl_phy_aux_enable(ctrl, 0);
12348c2ecf20Sopenharmony_ci	}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	DBG("exit: connect status=%d", ctrl->edp_connected);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl->dev_mutex);
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	return ctrl->edp_connected;
12418c2ecf20Sopenharmony_ci}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ciint msm_edp_ctrl_get_panel_info(struct edp_ctrl *ctrl,
12448c2ecf20Sopenharmony_ci		struct drm_connector *connector, struct edid **edid)
12458c2ecf20Sopenharmony_ci{
12468c2ecf20Sopenharmony_ci	int ret = 0;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	mutex_lock(&ctrl->dev_mutex);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	if (ctrl->edid) {
12518c2ecf20Sopenharmony_ci		if (edid) {
12528c2ecf20Sopenharmony_ci			DBG("Just return edid buffer");
12538c2ecf20Sopenharmony_ci			*edid = ctrl->edid;
12548c2ecf20Sopenharmony_ci		}
12558c2ecf20Sopenharmony_ci		goto unlock_ret;
12568c2ecf20Sopenharmony_ci	}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	if (!ctrl->power_on) {
12598c2ecf20Sopenharmony_ci		edp_ctrl_phy_aux_enable(ctrl, 1);
12608c2ecf20Sopenharmony_ci		edp_ctrl_irq_enable(ctrl, 1);
12618c2ecf20Sopenharmony_ci	}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	/* Initialize link rate as panel max link rate */
12648c2ecf20Sopenharmony_ci	ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	ctrl->edid = drm_get_edid(connector, &ctrl->drm_aux->ddc);
12678c2ecf20Sopenharmony_ci	if (!ctrl->edid) {
12688c2ecf20Sopenharmony_ci		pr_err("%s: edid read fail\n", __func__);
12698c2ecf20Sopenharmony_ci		goto disable_ret;
12708c2ecf20Sopenharmony_ci	}
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	if (edid)
12738c2ecf20Sopenharmony_ci		*edid = ctrl->edid;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_cidisable_ret:
12768c2ecf20Sopenharmony_ci	if (!ctrl->power_on) {
12778c2ecf20Sopenharmony_ci		edp_ctrl_irq_enable(ctrl, 0);
12788c2ecf20Sopenharmony_ci		edp_ctrl_phy_aux_enable(ctrl, 0);
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ciunlock_ret:
12818c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl->dev_mutex);
12828c2ecf20Sopenharmony_ci	return ret;
12838c2ecf20Sopenharmony_ci}
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ciint msm_edp_ctrl_timing_cfg(struct edp_ctrl *ctrl,
12868c2ecf20Sopenharmony_ci				const struct drm_display_mode *mode,
12878c2ecf20Sopenharmony_ci				const struct drm_display_info *info)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	u32 hstart_from_sync, vstart_from_sync;
12908c2ecf20Sopenharmony_ci	u32 data;
12918c2ecf20Sopenharmony_ci	int ret = 0;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	mutex_lock(&ctrl->dev_mutex);
12948c2ecf20Sopenharmony_ci	/*
12958c2ecf20Sopenharmony_ci	 * Need to keep color depth, pixel rate and
12968c2ecf20Sopenharmony_ci	 * interlaced information in ctrl context
12978c2ecf20Sopenharmony_ci	 */
12988c2ecf20Sopenharmony_ci	ctrl->color_depth = info->bpc;
12998c2ecf20Sopenharmony_ci	ctrl->pixel_rate = mode->clock;
13008c2ecf20Sopenharmony_ci	ctrl->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	/* Fill initial link config based on passed in timing */
13038c2ecf20Sopenharmony_ci	edp_fill_link_cfg(ctrl);
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	if (edp_clk_enable(ctrl, EDP_CLK_MASK_AHB)) {
13068c2ecf20Sopenharmony_ci		pr_err("%s, fail to prepare enable ahb clk\n", __func__);
13078c2ecf20Sopenharmony_ci		ret = -EINVAL;
13088c2ecf20Sopenharmony_ci		goto unlock_ret;
13098c2ecf20Sopenharmony_ci	}
13108c2ecf20Sopenharmony_ci	edp_clock_synchrous(ctrl, 1);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	/* Configure eDP timing to HW */
13138c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_TOTAL_HOR_VER,
13148c2ecf20Sopenharmony_ci		EDP_TOTAL_HOR_VER_HORIZ(mode->htotal) |
13158c2ecf20Sopenharmony_ci		EDP_TOTAL_HOR_VER_VERT(mode->vtotal));
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	vstart_from_sync = mode->vtotal - mode->vsync_start;
13188c2ecf20Sopenharmony_ci	hstart_from_sync = mode->htotal - mode->hsync_start;
13198c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_START_HOR_VER_FROM_SYNC,
13208c2ecf20Sopenharmony_ci		EDP_START_HOR_VER_FROM_SYNC_HORIZ(hstart_from_sync) |
13218c2ecf20Sopenharmony_ci		EDP_START_HOR_VER_FROM_SYNC_VERT(vstart_from_sync));
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	data = EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT(
13248c2ecf20Sopenharmony_ci			mode->vsync_end - mode->vsync_start);
13258c2ecf20Sopenharmony_ci	data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ(
13268c2ecf20Sopenharmony_ci			mode->hsync_end - mode->hsync_start);
13278c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
13288c2ecf20Sopenharmony_ci		data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_NVSYNC;
13298c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
13308c2ecf20Sopenharmony_ci		data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_NHSYNC;
13318c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_HSYNC_VSYNC_WIDTH_POLARITY, data);
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	edp_write(ctrl->base + REG_EDP_ACTIVE_HOR_VER,
13348c2ecf20Sopenharmony_ci		EDP_ACTIVE_HOR_VER_HORIZ(mode->hdisplay) |
13358c2ecf20Sopenharmony_ci		EDP_ACTIVE_HOR_VER_VERT(mode->vdisplay));
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	edp_clk_disable(ctrl, EDP_CLK_MASK_AHB);
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ciunlock_ret:
13408c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl->dev_mutex);
13418c2ecf20Sopenharmony_ci	return ret;
13428c2ecf20Sopenharmony_ci}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_cibool msm_edp_ctrl_pixel_clock_valid(struct edp_ctrl *ctrl,
13458c2ecf20Sopenharmony_ci	u32 pixel_rate, u32 *pm, u32 *pn)
13468c2ecf20Sopenharmony_ci{
13478c2ecf20Sopenharmony_ci	const struct edp_pixel_clk_div *divs;
13488c2ecf20Sopenharmony_ci	u32 err = 1; /* 1% error tolerance */
13498c2ecf20Sopenharmony_ci	u32 clk_err;
13508c2ecf20Sopenharmony_ci	int i;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	if (ctrl->link_rate == DP_LINK_BW_1_62) {
13538c2ecf20Sopenharmony_ci		divs = clk_divs[0];
13548c2ecf20Sopenharmony_ci	} else if (ctrl->link_rate == DP_LINK_BW_2_7) {
13558c2ecf20Sopenharmony_ci		divs = clk_divs[1];
13568c2ecf20Sopenharmony_ci	} else {
13578c2ecf20Sopenharmony_ci		pr_err("%s: Invalid link rate,%d\n", __func__, ctrl->link_rate);
13588c2ecf20Sopenharmony_ci		return false;
13598c2ecf20Sopenharmony_ci	}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	for (i = 0; i < EDP_PIXEL_CLK_NUM; i++) {
13628c2ecf20Sopenharmony_ci		clk_err = abs(divs[i].rate - pixel_rate);
13638c2ecf20Sopenharmony_ci		if ((divs[i].rate * err / 100) >= clk_err) {
13648c2ecf20Sopenharmony_ci			if (pm)
13658c2ecf20Sopenharmony_ci				*pm = divs[i].m;
13668c2ecf20Sopenharmony_ci			if (pn)
13678c2ecf20Sopenharmony_ci				*pn = divs[i].n;
13688c2ecf20Sopenharmony_ci			return true;
13698c2ecf20Sopenharmony_ci		}
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	DBG("pixel clock %d(kHz) not supported", pixel_rate);
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	return false;
13758c2ecf20Sopenharmony_ci}
13768c2ecf20Sopenharmony_ci
1377