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