12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23424e3a4SYakir Yang /*
33424e3a4SYakir Yang * Analogix DP (Display Port) core interface driver.
43424e3a4SYakir Yang *
53424e3a4SYakir Yang * Copyright (C) 2012 Samsung Electronics Co., Ltd.
63424e3a4SYakir Yang * Author: Jingoo Han <jg1.han@samsung.com>
73424e3a4SYakir Yang */
83424e3a4SYakir Yang
93424e3a4SYakir Yang #include <linux/clk.h>
1095b60804SSam Ravnborg #include <linux/component.h>
1195b60804SSam Ravnborg #include <linux/err.h>
125b038dcfSLinus Walleij #include <linux/gpio/consumer.h>
1395b60804SSam Ravnborg #include <linux/interrupt.h>
143424e3a4SYakir Yang #include <linux/io.h>
15f9d56805Szain wang #include <linux/iopoll.h>
1695b60804SSam Ravnborg #include <linux/module.h>
173424e3a4SYakir Yang #include <linux/of.h>
183424e3a4SYakir Yang #include <linux/phy/phy.h>
1995b60804SSam Ravnborg #include <linux/platform_device.h>
203424e3a4SYakir Yang
213424e3a4SYakir Yang #include <drm/bridge/analogix_dp.h>
226c836d96SSean Paul #include <drm/drm_atomic.h>
2395b60804SSam Ravnborg #include <drm/drm_atomic_helper.h>
24ee68c743SBoris Brezillon #include <drm/drm_bridge.h>
2595b60804SSam Ravnborg #include <drm/drm_crtc.h>
2695b60804SSam Ravnborg #include <drm/drm_device.h>
27*255490f9SVille Syrjälä #include <drm/drm_edid.h>
2895b60804SSam Ravnborg #include <drm/drm_panel.h>
2995b60804SSam Ravnborg #include <drm/drm_print.h>
3095b60804SSam Ravnborg #include <drm/drm_probe_helper.h>
313424e3a4SYakir Yang
323424e3a4SYakir Yang #include "analogix_dp_core.h"
330d97ad03STomeu Vizoso #include "analogix_dp_reg.h"
343424e3a4SYakir Yang
353424e3a4SYakir Yang #define to_dp(nm) container_of(nm, struct analogix_dp_device, nm)
363424e3a4SYakir Yang
37f9d56805Szain wang static const bool verify_fast_training;
38f9d56805Szain wang
393424e3a4SYakir Yang struct bridge_init {
403424e3a4SYakir Yang struct i2c_client *client;
413424e3a4SYakir Yang struct device_node *node;
423424e3a4SYakir Yang };
433424e3a4SYakir Yang
analogix_dp_init_dp(struct analogix_dp_device * dp)448a335736Szain wang static int analogix_dp_init_dp(struct analogix_dp_device *dp)
453424e3a4SYakir Yang {
468a335736Szain wang int ret;
478a335736Szain wang
483424e3a4SYakir Yang analogix_dp_reset(dp);
493424e3a4SYakir Yang
503424e3a4SYakir Yang analogix_dp_swreset(dp);
513424e3a4SYakir Yang
523424e3a4SYakir Yang analogix_dp_init_analog_param(dp);
533424e3a4SYakir Yang analogix_dp_init_interrupt(dp);
543424e3a4SYakir Yang
553424e3a4SYakir Yang /* SW defined function Normal operation */
563424e3a4SYakir Yang analogix_dp_enable_sw_function(dp);
573424e3a4SYakir Yang
583424e3a4SYakir Yang analogix_dp_config_interrupt(dp);
598a335736Szain wang ret = analogix_dp_init_analog_func(dp);
608a335736Szain wang if (ret)
618a335736Szain wang return ret;
623424e3a4SYakir Yang
633424e3a4SYakir Yang analogix_dp_init_hpd(dp);
643424e3a4SYakir Yang analogix_dp_init_aux(dp);
658a335736Szain wang return 0;
663424e3a4SYakir Yang }
673424e3a4SYakir Yang
analogix_dp_detect_hpd(struct analogix_dp_device * dp)683424e3a4SYakir Yang static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
693424e3a4SYakir Yang {
703424e3a4SYakir Yang int timeout_loop = 0;
713424e3a4SYakir Yang
725cff007cSYakir Yang while (timeout_loop < DP_TIMEOUT_LOOP_COUNT) {
735cff007cSYakir Yang if (analogix_dp_get_plug_in_status(dp) == 0)
745cff007cSYakir Yang return 0;
755cff007cSYakir Yang
763424e3a4SYakir Yang timeout_loop++;
77606c5e64SLin Huang usleep_range(1000, 1100);
783424e3a4SYakir Yang }
793424e3a4SYakir Yang
805cff007cSYakir Yang /*
815cff007cSYakir Yang * Some edp screen do not have hpd signal, so we can't just
825cff007cSYakir Yang * return failed when hpd plug in detect failed, DT property
835cff007cSYakir Yang * "force-hpd" would indicate whether driver need this.
845cff007cSYakir Yang */
855cff007cSYakir Yang if (!dp->force_hpd)
865cff007cSYakir Yang return -ETIMEDOUT;
875cff007cSYakir Yang
885cff007cSYakir Yang /*
895cff007cSYakir Yang * The eDP TRM indicate that if HPD_STATUS(RO) is 0, AUX CH
905cff007cSYakir Yang * will not work, so we need to give a force hpd action to
915cff007cSYakir Yang * set HPD_STATUS manually.
925cff007cSYakir Yang */
935cff007cSYakir Yang dev_dbg(dp->dev, "failed to get hpd plug status, try to force hpd\n");
945cff007cSYakir Yang
955cff007cSYakir Yang analogix_dp_force_hpd(dp);
965cff007cSYakir Yang
975cff007cSYakir Yang if (analogix_dp_get_plug_in_status(dp) != 0) {
985cff007cSYakir Yang dev_err(dp->dev, "failed to get hpd plug in status\n");
995cff007cSYakir Yang return -EINVAL;
1005cff007cSYakir Yang }
1015cff007cSYakir Yang
1025cff007cSYakir Yang dev_dbg(dp->dev, "success to get plug in status after force hpd\n");
1035cff007cSYakir Yang
1043424e3a4SYakir Yang return 0;
1053424e3a4SYakir Yang }
1063424e3a4SYakir Yang
analogix_dp_detect_sink_psr(struct analogix_dp_device * dp)1076c836d96SSean Paul static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
1085b3f84f2SYakir Yang {
1095b3f84f2SYakir Yang unsigned char psr_version;
110ccdc578bSLin Huang int ret;
1115b3f84f2SYakir Yang
112ccdc578bSLin Huang ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_SUPPORT, &psr_version);
113ccdc578bSLin Huang if (ret != 1) {
114ccdc578bSLin Huang dev_err(dp->dev, "failed to get PSR version, disable it\n");
1156c836d96SSean Paul return false;
1165b3f84f2SYakir Yang }
1175b3f84f2SYakir Yang
118ccdc578bSLin Huang dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
1196c836d96SSean Paul return psr_version & DP_PSR_IS_SUPPORTED;
120ccdc578bSLin Huang }
121ccdc578bSLin Huang
analogix_dp_enable_sink_psr(struct analogix_dp_device * dp)122ccdc578bSLin Huang static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
1235b3f84f2SYakir Yang {
1245b3f84f2SYakir Yang unsigned char psr_en;
125ccdc578bSLin Huang int ret;
1265b3f84f2SYakir Yang
1275b3f84f2SYakir Yang /* Disable psr function */
128ccdc578bSLin Huang ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_EN_CFG, &psr_en);
129ccdc578bSLin Huang if (ret != 1) {
130ccdc578bSLin Huang dev_err(dp->dev, "failed to get psr config\n");
131ccdc578bSLin Huang goto end;
132ccdc578bSLin Huang }
133ccdc578bSLin Huang
1345b3f84f2SYakir Yang psr_en &= ~DP_PSR_ENABLE;
135ccdc578bSLin Huang ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
136ccdc578bSLin Huang if (ret != 1) {
137ccdc578bSLin Huang dev_err(dp->dev, "failed to disable panel psr\n");
138ccdc578bSLin Huang goto end;
139ccdc578bSLin Huang }
1405b3f84f2SYakir Yang
1415b3f84f2SYakir Yang /* Main-Link transmitter remains active during PSR active states */
1426c836d96SSean Paul psr_en = DP_PSR_CRC_VERIFICATION;
143ccdc578bSLin Huang ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
144ccdc578bSLin Huang if (ret != 1) {
145ccdc578bSLin Huang dev_err(dp->dev, "failed to set panel psr\n");
146ccdc578bSLin Huang goto end;
147ccdc578bSLin Huang }
1485b3f84f2SYakir Yang
1495b3f84f2SYakir Yang /* Enable psr function */
1506c836d96SSean Paul psr_en = DP_PSR_ENABLE | DP_PSR_CRC_VERIFICATION;
151ccdc578bSLin Huang ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
152ccdc578bSLin Huang if (ret != 1) {
153ccdc578bSLin Huang dev_err(dp->dev, "failed to set panel psr\n");
154ccdc578bSLin Huang goto end;
1555b3f84f2SYakir Yang }
1565b3f84f2SYakir Yang
157ccdc578bSLin Huang analogix_dp_enable_psr_crc(dp);
158ccdc578bSLin Huang
1596c836d96SSean Paul dp->psr_supported = true;
1606c836d96SSean Paul
161ccdc578bSLin Huang return 0;
162ccdc578bSLin Huang end:
163ccdc578bSLin Huang dev_err(dp->dev, "enable psr fail, force to disable psr\n");
164ccdc578bSLin Huang
165ccdc578bSLin Huang return ret;
166ccdc578bSLin Huang }
167ccdc578bSLin Huang
168ccdc578bSLin Huang static int
analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device * dp,bool enable)169bcbb7033SYakir Yang analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp,
1703424e3a4SYakir Yang bool enable)
1713424e3a4SYakir Yang {
1723424e3a4SYakir Yang u8 data;
173ccdc578bSLin Huang int ret;
1743424e3a4SYakir Yang
175ccdc578bSLin Huang ret = drm_dp_dpcd_readb(&dp->aux, DP_LANE_COUNT_SET, &data);
176ccdc578bSLin Huang if (ret != 1)
177ccdc578bSLin Huang return ret;
1783424e3a4SYakir Yang
1793424e3a4SYakir Yang if (enable)
180ccdc578bSLin Huang ret = drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
1813424e3a4SYakir Yang DP_LANE_COUNT_ENHANCED_FRAME_EN |
1823424e3a4SYakir Yang DPCD_LANE_COUNT_SET(data));
1833424e3a4SYakir Yang else
184ccdc578bSLin Huang ret = drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
1853424e3a4SYakir Yang DPCD_LANE_COUNT_SET(data));
186ccdc578bSLin Huang
187ccdc578bSLin Huang return ret < 0 ? ret : 0;
1883424e3a4SYakir Yang }
1893424e3a4SYakir Yang
analogix_dp_is_enhanced_mode_available(struct analogix_dp_device * dp,u8 * enhanced_mode_support)190ccdc578bSLin Huang static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp,
191ccdc578bSLin Huang u8 *enhanced_mode_support)
1923424e3a4SYakir Yang {
1933424e3a4SYakir Yang u8 data;
194ccdc578bSLin Huang int ret;
1953424e3a4SYakir Yang
196ccdc578bSLin Huang ret = drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data);
197ccdc578bSLin Huang if (ret != 1) {
198ccdc578bSLin Huang *enhanced_mode_support = 0;
199ccdc578bSLin Huang return ret;
2003424e3a4SYakir Yang }
2013424e3a4SYakir Yang
202ccdc578bSLin Huang *enhanced_mode_support = DPCD_ENHANCED_FRAME_CAP(data);
203ccdc578bSLin Huang
204ccdc578bSLin Huang return 0;
205ccdc578bSLin Huang }
206ccdc578bSLin Huang
analogix_dp_set_enhanced_mode(struct analogix_dp_device * dp)207ccdc578bSLin Huang static int analogix_dp_set_enhanced_mode(struct analogix_dp_device *dp)
2083424e3a4SYakir Yang {
2093424e3a4SYakir Yang u8 data;
210ccdc578bSLin Huang int ret;
2113424e3a4SYakir Yang
212ccdc578bSLin Huang ret = analogix_dp_is_enhanced_mode_available(dp, &data);
213ccdc578bSLin Huang if (ret < 0)
214ccdc578bSLin Huang return ret;
215ccdc578bSLin Huang
216ccdc578bSLin Huang ret = analogix_dp_enable_rx_to_enhanced_mode(dp, data);
217ccdc578bSLin Huang if (ret < 0)
218ccdc578bSLin Huang return ret;
219ccdc578bSLin Huang
2203424e3a4SYakir Yang analogix_dp_enable_enhanced_mode(dp, data);
221ccdc578bSLin Huang
222ccdc578bSLin Huang return 0;
2233424e3a4SYakir Yang }
2243424e3a4SYakir Yang
analogix_dp_training_pattern_dis(struct analogix_dp_device * dp)225ccdc578bSLin Huang static int analogix_dp_training_pattern_dis(struct analogix_dp_device *dp)
2263424e3a4SYakir Yang {
227ccdc578bSLin Huang int ret;
228ccdc578bSLin Huang
2293424e3a4SYakir Yang analogix_dp_set_training_pattern(dp, DP_NONE);
2303424e3a4SYakir Yang
231ccdc578bSLin Huang ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
2323424e3a4SYakir Yang DP_TRAINING_PATTERN_DISABLE);
233ccdc578bSLin Huang
234ccdc578bSLin Huang return ret < 0 ? ret : 0;
2353424e3a4SYakir Yang }
2363424e3a4SYakir Yang
237bcbb7033SYakir Yang static void
analogix_dp_set_lane_lane_pre_emphasis(struct analogix_dp_device * dp,int pre_emphasis,int lane)238bcbb7033SYakir Yang analogix_dp_set_lane_lane_pre_emphasis(struct analogix_dp_device *dp,
2393424e3a4SYakir Yang int pre_emphasis, int lane)
2403424e3a4SYakir Yang {
2413424e3a4SYakir Yang switch (lane) {
2423424e3a4SYakir Yang case 0:
2433424e3a4SYakir Yang analogix_dp_set_lane0_pre_emphasis(dp, pre_emphasis);
2443424e3a4SYakir Yang break;
2453424e3a4SYakir Yang case 1:
2463424e3a4SYakir Yang analogix_dp_set_lane1_pre_emphasis(dp, pre_emphasis);
2473424e3a4SYakir Yang break;
2483424e3a4SYakir Yang
2493424e3a4SYakir Yang case 2:
2503424e3a4SYakir Yang analogix_dp_set_lane2_pre_emphasis(dp, pre_emphasis);
2513424e3a4SYakir Yang break;
2523424e3a4SYakir Yang
2533424e3a4SYakir Yang case 3:
2543424e3a4SYakir Yang analogix_dp_set_lane3_pre_emphasis(dp, pre_emphasis);
2553424e3a4SYakir Yang break;
2563424e3a4SYakir Yang }
2573424e3a4SYakir Yang }
2583424e3a4SYakir Yang
analogix_dp_link_start(struct analogix_dp_device * dp)2593424e3a4SYakir Yang static int analogix_dp_link_start(struct analogix_dp_device *dp)
2603424e3a4SYakir Yang {
2613424e3a4SYakir Yang u8 buf[4];
2623424e3a4SYakir Yang int lane, lane_count, pll_tries, retval;
2633424e3a4SYakir Yang
2643424e3a4SYakir Yang lane_count = dp->link_train.lane_count;
2653424e3a4SYakir Yang
2663424e3a4SYakir Yang dp->link_train.lt_state = CLOCK_RECOVERY;
2673424e3a4SYakir Yang dp->link_train.eq_loop = 0;
2683424e3a4SYakir Yang
2693424e3a4SYakir Yang for (lane = 0; lane < lane_count; lane++)
2703424e3a4SYakir Yang dp->link_train.cr_loop[lane] = 0;
2713424e3a4SYakir Yang
2723424e3a4SYakir Yang /* Set link rate and count as you want to establish*/
2733424e3a4SYakir Yang analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
2743424e3a4SYakir Yang analogix_dp_set_lane_count(dp, dp->link_train.lane_count);
2753424e3a4SYakir Yang
2763424e3a4SYakir Yang /* Setup RX configuration */
2773424e3a4SYakir Yang buf[0] = dp->link_train.link_rate;
2783424e3a4SYakir Yang buf[1] = dp->link_train.lane_count;
2790d97ad03STomeu Vizoso retval = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, 2);
2800d97ad03STomeu Vizoso if (retval < 0)
2813424e3a4SYakir Yang return retval;
2821932250dSzain wang /* set enhanced mode if available */
283ccdc578bSLin Huang retval = analogix_dp_set_enhanced_mode(dp);
284ccdc578bSLin Huang if (retval < 0) {
285ccdc578bSLin Huang dev_err(dp->dev, "failed to set enhance mode\n");
286ccdc578bSLin Huang return retval;
287ccdc578bSLin Huang }
2883424e3a4SYakir Yang
2893424e3a4SYakir Yang /* Set TX pre-emphasis to minimum */
2903424e3a4SYakir Yang for (lane = 0; lane < lane_count; lane++)
2913424e3a4SYakir Yang analogix_dp_set_lane_lane_pre_emphasis(dp,
2923424e3a4SYakir Yang PRE_EMPHASIS_LEVEL_0, lane);
2933424e3a4SYakir Yang
2943424e3a4SYakir Yang /* Wait for PLL lock */
2953424e3a4SYakir Yang pll_tries = 0;
2963424e3a4SYakir Yang while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
2973424e3a4SYakir Yang if (pll_tries == DP_TIMEOUT_LOOP_COUNT) {
2983424e3a4SYakir Yang dev_err(dp->dev, "Wait for PLL lock timed out\n");
2993424e3a4SYakir Yang return -ETIMEDOUT;
3003424e3a4SYakir Yang }
3013424e3a4SYakir Yang
3023424e3a4SYakir Yang pll_tries++;
3033424e3a4SYakir Yang usleep_range(90, 120);
3043424e3a4SYakir Yang }
3053424e3a4SYakir Yang
3063424e3a4SYakir Yang /* Set training pattern 1 */
3073424e3a4SYakir Yang analogix_dp_set_training_pattern(dp, TRAINING_PTN1);
3083424e3a4SYakir Yang
3093424e3a4SYakir Yang /* Set RX training pattern */
3100d97ad03STomeu Vizoso retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
3110d97ad03STomeu Vizoso DP_LINK_SCRAMBLING_DISABLE |
3120d97ad03STomeu Vizoso DP_TRAINING_PATTERN_1);
3130d97ad03STomeu Vizoso if (retval < 0)
3143424e3a4SYakir Yang return retval;
3153424e3a4SYakir Yang
3163424e3a4SYakir Yang for (lane = 0; lane < lane_count; lane++)
3173424e3a4SYakir Yang buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 |
3183424e3a4SYakir Yang DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
3193424e3a4SYakir Yang
3200d97ad03STomeu Vizoso retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, buf,
3210d97ad03STomeu Vizoso lane_count);
3220d97ad03STomeu Vizoso if (retval < 0)
3233424e3a4SYakir Yang return retval;
3240d97ad03STomeu Vizoso
3250d97ad03STomeu Vizoso return 0;
3263424e3a4SYakir Yang }
3273424e3a4SYakir Yang
analogix_dp_get_lane_status(u8 link_status[2],int lane)3283424e3a4SYakir Yang static unsigned char analogix_dp_get_lane_status(u8 link_status[2], int lane)
3293424e3a4SYakir Yang {
3303424e3a4SYakir Yang int shift = (lane & 1) * 4;
3313424e3a4SYakir Yang u8 link_value = link_status[lane >> 1];
3323424e3a4SYakir Yang
3333424e3a4SYakir Yang return (link_value >> shift) & 0xf;
3343424e3a4SYakir Yang }
3353424e3a4SYakir Yang
analogix_dp_clock_recovery_ok(u8 link_status[2],int lane_count)3363424e3a4SYakir Yang static int analogix_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
3373424e3a4SYakir Yang {
3383424e3a4SYakir Yang int lane;
3393424e3a4SYakir Yang u8 lane_status;
3403424e3a4SYakir Yang
3413424e3a4SYakir Yang for (lane = 0; lane < lane_count; lane++) {
3423424e3a4SYakir Yang lane_status = analogix_dp_get_lane_status(link_status, lane);
3433424e3a4SYakir Yang if ((lane_status & DP_LANE_CR_DONE) == 0)
3443424e3a4SYakir Yang return -EINVAL;
3453424e3a4SYakir Yang }
3463424e3a4SYakir Yang return 0;
3473424e3a4SYakir Yang }
3483424e3a4SYakir Yang
analogix_dp_channel_eq_ok(u8 link_status[2],u8 link_align,int lane_count)3493424e3a4SYakir Yang static int analogix_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
3503424e3a4SYakir Yang int lane_count)
3513424e3a4SYakir Yang {
3523424e3a4SYakir Yang int lane;
3533424e3a4SYakir Yang u8 lane_status;
3543424e3a4SYakir Yang
3553424e3a4SYakir Yang if ((link_align & DP_INTERLANE_ALIGN_DONE) == 0)
3563424e3a4SYakir Yang return -EINVAL;
3573424e3a4SYakir Yang
3583424e3a4SYakir Yang for (lane = 0; lane < lane_count; lane++) {
3593424e3a4SYakir Yang lane_status = analogix_dp_get_lane_status(link_status, lane);
3603424e3a4SYakir Yang lane_status &= DP_CHANNEL_EQ_BITS;
3613424e3a4SYakir Yang if (lane_status != DP_CHANNEL_EQ_BITS)
3623424e3a4SYakir Yang return -EINVAL;
3633424e3a4SYakir Yang }
3643424e3a4SYakir Yang
3653424e3a4SYakir Yang return 0;
3663424e3a4SYakir Yang }
3673424e3a4SYakir Yang
368bcbb7033SYakir Yang static unsigned char
analogix_dp_get_adjust_request_voltage(u8 adjust_request[2],int lane)369bcbb7033SYakir Yang analogix_dp_get_adjust_request_voltage(u8 adjust_request[2], int lane)
3703424e3a4SYakir Yang {
3713424e3a4SYakir Yang int shift = (lane & 1) * 4;
3723424e3a4SYakir Yang u8 link_value = adjust_request[lane >> 1];
3733424e3a4SYakir Yang
3743424e3a4SYakir Yang return (link_value >> shift) & 0x3;
3753424e3a4SYakir Yang }
3763424e3a4SYakir Yang
analogix_dp_get_adjust_request_pre_emphasis(u8 adjust_request[2],int lane)3773424e3a4SYakir Yang static unsigned char analogix_dp_get_adjust_request_pre_emphasis(
3783424e3a4SYakir Yang u8 adjust_request[2],
3793424e3a4SYakir Yang int lane)
3803424e3a4SYakir Yang {
3813424e3a4SYakir Yang int shift = (lane & 1) * 4;
3823424e3a4SYakir Yang u8 link_value = adjust_request[lane >> 1];
3833424e3a4SYakir Yang
3843424e3a4SYakir Yang return ((link_value >> shift) & 0xc) >> 2;
3853424e3a4SYakir Yang }
3863424e3a4SYakir Yang
analogix_dp_set_lane_link_training(struct analogix_dp_device * dp,u8 training_lane_set,int lane)3873424e3a4SYakir Yang static void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp,
3883424e3a4SYakir Yang u8 training_lane_set, int lane)
3893424e3a4SYakir Yang {
3903424e3a4SYakir Yang switch (lane) {
3913424e3a4SYakir Yang case 0:
3923424e3a4SYakir Yang analogix_dp_set_lane0_link_training(dp, training_lane_set);
3933424e3a4SYakir Yang break;
3943424e3a4SYakir Yang case 1:
3953424e3a4SYakir Yang analogix_dp_set_lane1_link_training(dp, training_lane_set);
3963424e3a4SYakir Yang break;
3973424e3a4SYakir Yang
3983424e3a4SYakir Yang case 2:
3993424e3a4SYakir Yang analogix_dp_set_lane2_link_training(dp, training_lane_set);
4003424e3a4SYakir Yang break;
4013424e3a4SYakir Yang
4023424e3a4SYakir Yang case 3:
4033424e3a4SYakir Yang analogix_dp_set_lane3_link_training(dp, training_lane_set);
4043424e3a4SYakir Yang break;
4053424e3a4SYakir Yang }
4063424e3a4SYakir Yang }
4073424e3a4SYakir Yang
408bcbb7033SYakir Yang static unsigned int
analogix_dp_get_lane_link_training(struct analogix_dp_device * dp,int lane)409bcbb7033SYakir Yang analogix_dp_get_lane_link_training(struct analogix_dp_device *dp,
4103424e3a4SYakir Yang int lane)
4113424e3a4SYakir Yang {
4123424e3a4SYakir Yang u32 reg;
4133424e3a4SYakir Yang
4143424e3a4SYakir Yang switch (lane) {
4153424e3a4SYakir Yang case 0:
4163424e3a4SYakir Yang reg = analogix_dp_get_lane0_link_training(dp);
4173424e3a4SYakir Yang break;
4183424e3a4SYakir Yang case 1:
4193424e3a4SYakir Yang reg = analogix_dp_get_lane1_link_training(dp);
4203424e3a4SYakir Yang break;
4213424e3a4SYakir Yang case 2:
4223424e3a4SYakir Yang reg = analogix_dp_get_lane2_link_training(dp);
4233424e3a4SYakir Yang break;
4243424e3a4SYakir Yang case 3:
4253424e3a4SYakir Yang reg = analogix_dp_get_lane3_link_training(dp);
4263424e3a4SYakir Yang break;
4273424e3a4SYakir Yang default:
4283424e3a4SYakir Yang WARN_ON(1);
4293424e3a4SYakir Yang return 0;
4303424e3a4SYakir Yang }
4313424e3a4SYakir Yang
4323424e3a4SYakir Yang return reg;
4333424e3a4SYakir Yang }
4343424e3a4SYakir Yang
analogix_dp_reduce_link_rate(struct analogix_dp_device * dp)4353424e3a4SYakir Yang static void analogix_dp_reduce_link_rate(struct analogix_dp_device *dp)
4363424e3a4SYakir Yang {
4373424e3a4SYakir Yang analogix_dp_training_pattern_dis(dp);
4383424e3a4SYakir Yang analogix_dp_set_enhanced_mode(dp);
4393424e3a4SYakir Yang
4403424e3a4SYakir Yang dp->link_train.lt_state = FAILED;
4413424e3a4SYakir Yang }
4423424e3a4SYakir Yang
analogix_dp_get_adjust_training_lane(struct analogix_dp_device * dp,u8 adjust_request[2])4433424e3a4SYakir Yang static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp,
4443424e3a4SYakir Yang u8 adjust_request[2])
4453424e3a4SYakir Yang {
4463424e3a4SYakir Yang int lane, lane_count;
4473424e3a4SYakir Yang u8 voltage_swing, pre_emphasis, training_lane;
4483424e3a4SYakir Yang
4493424e3a4SYakir Yang lane_count = dp->link_train.lane_count;
4503424e3a4SYakir Yang for (lane = 0; lane < lane_count; lane++) {
4513424e3a4SYakir Yang voltage_swing = analogix_dp_get_adjust_request_voltage(
4523424e3a4SYakir Yang adjust_request, lane);
4533424e3a4SYakir Yang pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis(
4543424e3a4SYakir Yang adjust_request, lane);
4553424e3a4SYakir Yang training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
4563424e3a4SYakir Yang DPCD_PRE_EMPHASIS_SET(pre_emphasis);
4573424e3a4SYakir Yang
4583424e3a4SYakir Yang if (voltage_swing == VOLTAGE_LEVEL_3)
4593424e3a4SYakir Yang training_lane |= DP_TRAIN_MAX_SWING_REACHED;
4603424e3a4SYakir Yang if (pre_emphasis == PRE_EMPHASIS_LEVEL_3)
4613424e3a4SYakir Yang training_lane |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
4623424e3a4SYakir Yang
4633424e3a4SYakir Yang dp->link_train.training_lane[lane] = training_lane;
4643424e3a4SYakir Yang }
4653424e3a4SYakir Yang }
4663424e3a4SYakir Yang
analogix_dp_process_clock_recovery(struct analogix_dp_device * dp)4673424e3a4SYakir Yang static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
4683424e3a4SYakir Yang {
4693424e3a4SYakir Yang int lane, lane_count, retval;
4703424e3a4SYakir Yang u8 voltage_swing, pre_emphasis, training_lane;
4713424e3a4SYakir Yang u8 link_status[2], adjust_request[2];
4723424e3a4SYakir Yang
4733424e3a4SYakir Yang usleep_range(100, 101);
4743424e3a4SYakir Yang
4753424e3a4SYakir Yang lane_count = dp->link_train.lane_count;
4763424e3a4SYakir Yang
4770d97ad03STomeu Vizoso retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2);
4780d97ad03STomeu Vizoso if (retval < 0)
4793424e3a4SYakir Yang return retval;
4803424e3a4SYakir Yang
4810d97ad03STomeu Vizoso retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1,
4820d97ad03STomeu Vizoso adjust_request, 2);
4830d97ad03STomeu Vizoso if (retval < 0)
4843424e3a4SYakir Yang return retval;
4853424e3a4SYakir Yang
4863424e3a4SYakir Yang if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) {
4873424e3a4SYakir Yang /* set training pattern 2 for EQ */
4883424e3a4SYakir Yang analogix_dp_set_training_pattern(dp, TRAINING_PTN2);
4893424e3a4SYakir Yang
4900d97ad03STomeu Vizoso retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
4913424e3a4SYakir Yang DP_LINK_SCRAMBLING_DISABLE |
4923424e3a4SYakir Yang DP_TRAINING_PATTERN_2);
4930d97ad03STomeu Vizoso if (retval < 0)
4943424e3a4SYakir Yang return retval;
4953424e3a4SYakir Yang
496eb1d23d7SMarc Zyngier dev_dbg(dp->dev, "Link Training Clock Recovery success\n");
4973424e3a4SYakir Yang dp->link_train.lt_state = EQUALIZER_TRAINING;
4983424e3a4SYakir Yang } else {
4993424e3a4SYakir Yang for (lane = 0; lane < lane_count; lane++) {
5003424e3a4SYakir Yang training_lane = analogix_dp_get_lane_link_training(
5013424e3a4SYakir Yang dp, lane);
5023424e3a4SYakir Yang voltage_swing = analogix_dp_get_adjust_request_voltage(
5033424e3a4SYakir Yang adjust_request, lane);
5043424e3a4SYakir Yang pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis(
5053424e3a4SYakir Yang adjust_request, lane);
5063424e3a4SYakir Yang
5073424e3a4SYakir Yang if (DPCD_VOLTAGE_SWING_GET(training_lane) ==
5083424e3a4SYakir Yang voltage_swing &&
5093424e3a4SYakir Yang DPCD_PRE_EMPHASIS_GET(training_lane) ==
5103424e3a4SYakir Yang pre_emphasis)
5113424e3a4SYakir Yang dp->link_train.cr_loop[lane]++;
5123424e3a4SYakir Yang
5133424e3a4SYakir Yang if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP ||
5143424e3a4SYakir Yang voltage_swing == VOLTAGE_LEVEL_3 ||
5153424e3a4SYakir Yang pre_emphasis == PRE_EMPHASIS_LEVEL_3) {
5163424e3a4SYakir Yang dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n",
5173424e3a4SYakir Yang dp->link_train.cr_loop[lane],
5183424e3a4SYakir Yang voltage_swing, pre_emphasis);
5193424e3a4SYakir Yang analogix_dp_reduce_link_rate(dp);
5203424e3a4SYakir Yang return -EIO;
5213424e3a4SYakir Yang }
5223424e3a4SYakir Yang }
5233424e3a4SYakir Yang }
5243424e3a4SYakir Yang
5253424e3a4SYakir Yang analogix_dp_get_adjust_training_lane(dp, adjust_request);
5263424e3a4SYakir Yang
5273424e3a4SYakir Yang for (lane = 0; lane < lane_count; lane++)
5283424e3a4SYakir Yang analogix_dp_set_lane_link_training(dp,
5293424e3a4SYakir Yang dp->link_train.training_lane[lane], lane);
5303424e3a4SYakir Yang
5310d97ad03STomeu Vizoso retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
5320d97ad03STomeu Vizoso dp->link_train.training_lane, lane_count);
5330d97ad03STomeu Vizoso if (retval < 0)
5343424e3a4SYakir Yang return retval;
5353424e3a4SYakir Yang
5360d97ad03STomeu Vizoso return 0;
5373424e3a4SYakir Yang }
5383424e3a4SYakir Yang
analogix_dp_process_equalizer_training(struct analogix_dp_device * dp)5393424e3a4SYakir Yang static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
5403424e3a4SYakir Yang {
5413424e3a4SYakir Yang int lane, lane_count, retval;
5423424e3a4SYakir Yang u32 reg;
5436f4638a1Szain wang u8 link_align, link_status[2], adjust_request[2];
5443424e3a4SYakir Yang
5453424e3a4SYakir Yang usleep_range(400, 401);
5463424e3a4SYakir Yang
5473424e3a4SYakir Yang lane_count = dp->link_train.lane_count;
5483424e3a4SYakir Yang
5490d97ad03STomeu Vizoso retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2);
5500d97ad03STomeu Vizoso if (retval < 0)
5513424e3a4SYakir Yang return retval;
5523424e3a4SYakir Yang
5533424e3a4SYakir Yang if (analogix_dp_clock_recovery_ok(link_status, lane_count)) {
5543424e3a4SYakir Yang analogix_dp_reduce_link_rate(dp);
5553424e3a4SYakir Yang return -EIO;
5563424e3a4SYakir Yang }
5573424e3a4SYakir Yang
5580d97ad03STomeu Vizoso retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1,
5590d97ad03STomeu Vizoso adjust_request, 2);
5600d97ad03STomeu Vizoso if (retval < 0)
5613424e3a4SYakir Yang return retval;
5623424e3a4SYakir Yang
5630d97ad03STomeu Vizoso retval = drm_dp_dpcd_readb(&dp->aux, DP_LANE_ALIGN_STATUS_UPDATED,
5640d97ad03STomeu Vizoso &link_align);
5650d97ad03STomeu Vizoso if (retval < 0)
5663424e3a4SYakir Yang return retval;
5673424e3a4SYakir Yang
5683424e3a4SYakir Yang analogix_dp_get_adjust_training_lane(dp, adjust_request);
5693424e3a4SYakir Yang
5703424e3a4SYakir Yang if (!analogix_dp_channel_eq_ok(link_status, link_align, lane_count)) {
5713424e3a4SYakir Yang /* traing pattern Set to Normal */
572ccdc578bSLin Huang retval = analogix_dp_training_pattern_dis(dp);
573ccdc578bSLin Huang if (retval < 0)
574ccdc578bSLin Huang return retval;
5753424e3a4SYakir Yang
576eb1d23d7SMarc Zyngier dev_dbg(dp->dev, "Link Training success!\n");
5773424e3a4SYakir Yang analogix_dp_get_link_bandwidth(dp, ®);
5783424e3a4SYakir Yang dp->link_train.link_rate = reg;
5793424e3a4SYakir Yang dev_dbg(dp->dev, "final bandwidth = %.2x\n",
5803424e3a4SYakir Yang dp->link_train.link_rate);
5813424e3a4SYakir Yang
5823424e3a4SYakir Yang analogix_dp_get_lane_count(dp, ®);
5833424e3a4SYakir Yang dp->link_train.lane_count = reg;
5843424e3a4SYakir Yang dev_dbg(dp->dev, "final lane count = %.2x\n",
5853424e3a4SYakir Yang dp->link_train.lane_count);
5863424e3a4SYakir Yang
5873424e3a4SYakir Yang dp->link_train.lt_state = FINISHED;
5883424e3a4SYakir Yang
5893424e3a4SYakir Yang return 0;
5903424e3a4SYakir Yang }
5913424e3a4SYakir Yang
5923424e3a4SYakir Yang /* not all locked */
5933424e3a4SYakir Yang dp->link_train.eq_loop++;
5943424e3a4SYakir Yang
5953424e3a4SYakir Yang if (dp->link_train.eq_loop > MAX_EQ_LOOP) {
5963424e3a4SYakir Yang dev_err(dp->dev, "EQ Max loop\n");
5973424e3a4SYakir Yang analogix_dp_reduce_link_rate(dp);
5983424e3a4SYakir Yang return -EIO;
5993424e3a4SYakir Yang }
6003424e3a4SYakir Yang
6013424e3a4SYakir Yang for (lane = 0; lane < lane_count; lane++)
6023424e3a4SYakir Yang analogix_dp_set_lane_link_training(dp,
6033424e3a4SYakir Yang dp->link_train.training_lane[lane], lane);
6043424e3a4SYakir Yang
6050d97ad03STomeu Vizoso retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
6060d97ad03STomeu Vizoso dp->link_train.training_lane, lane_count);
6070d97ad03STomeu Vizoso if (retval < 0)
6083424e3a4SYakir Yang return retval;
6090d97ad03STomeu Vizoso
6100d97ad03STomeu Vizoso return 0;
6113424e3a4SYakir Yang }
6123424e3a4SYakir Yang
analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device * dp,u8 * bandwidth)6133424e3a4SYakir Yang static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
6143424e3a4SYakir Yang u8 *bandwidth)
6153424e3a4SYakir Yang {
6163424e3a4SYakir Yang u8 data;
6173424e3a4SYakir Yang
6183424e3a4SYakir Yang /*
6193424e3a4SYakir Yang * For DP rev.1.1, Maximum link rate of Main Link lanes
6203424e3a4SYakir Yang * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
62140fc7ce7SYakir Yang * For DP rev.1.2, Maximum link rate of Main Link lanes
62240fc7ce7SYakir Yang * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps, 0x14 = 5.4Gbps
6233424e3a4SYakir Yang */
6240d97ad03STomeu Vizoso drm_dp_dpcd_readb(&dp->aux, DP_MAX_LINK_RATE, &data);
6253424e3a4SYakir Yang *bandwidth = data;
6263424e3a4SYakir Yang }
6273424e3a4SYakir Yang
analogix_dp_get_max_rx_lane_count(struct analogix_dp_device * dp,u8 * lane_count)6283424e3a4SYakir Yang static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp,
6293424e3a4SYakir Yang u8 *lane_count)
6303424e3a4SYakir Yang {
6313424e3a4SYakir Yang u8 data;
6323424e3a4SYakir Yang
6333424e3a4SYakir Yang /*
6343424e3a4SYakir Yang * For DP rev.1.1, Maximum number of Main Link lanes
6353424e3a4SYakir Yang * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
6363424e3a4SYakir Yang */
6370d97ad03STomeu Vizoso drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data);
6383424e3a4SYakir Yang *lane_count = DPCD_MAX_LANE_COUNT(data);
6393424e3a4SYakir Yang }
6403424e3a4SYakir Yang
analogix_dp_full_link_train(struct analogix_dp_device * dp,u32 max_lanes,u32 max_rate)641f9d56805Szain wang static int analogix_dp_full_link_train(struct analogix_dp_device *dp,
642f9d56805Szain wang u32 max_lanes, u32 max_rate)
6433424e3a4SYakir Yang {
644f9d56805Szain wang int retval = 0;
645f9d56805Szain wang bool training_finished = false;
646f9d56805Szain wang
6473424e3a4SYakir Yang /*
6483424e3a4SYakir Yang * MACRO_RST must be applied after the PLL_LOCK to avoid
6493424e3a4SYakir Yang * the DP inter pair skew issue for at least 10 us
6503424e3a4SYakir Yang */
6513424e3a4SYakir Yang analogix_dp_reset_macro(dp);
6523424e3a4SYakir Yang
6533424e3a4SYakir Yang /* Initialize by reading RX's DPCD */
6543424e3a4SYakir Yang analogix_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
6553424e3a4SYakir Yang analogix_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
6563424e3a4SYakir Yang
65740fc7ce7SYakir Yang if ((dp->link_train.link_rate != DP_LINK_BW_1_62) &&
65840fc7ce7SYakir Yang (dp->link_train.link_rate != DP_LINK_BW_2_7) &&
65940fc7ce7SYakir Yang (dp->link_train.link_rate != DP_LINK_BW_5_4)) {
6603424e3a4SYakir Yang dev_err(dp->dev, "Rx Max Link Rate is abnormal :%x !\n",
6613424e3a4SYakir Yang dp->link_train.link_rate);
66240fc7ce7SYakir Yang dp->link_train.link_rate = DP_LINK_BW_1_62;
6633424e3a4SYakir Yang }
6643424e3a4SYakir Yang
6653424e3a4SYakir Yang if (dp->link_train.lane_count == 0) {
6663424e3a4SYakir Yang dev_err(dp->dev, "Rx Max Lane count is abnormal :%x !\n",
6673424e3a4SYakir Yang dp->link_train.lane_count);
6683424e3a4SYakir Yang dp->link_train.lane_count = (u8)LANE_COUNT1;
6693424e3a4SYakir Yang }
6703424e3a4SYakir Yang
6713424e3a4SYakir Yang /* Setup TX lane count & rate */
672f9d56805Szain wang if (dp->link_train.lane_count > max_lanes)
673f9d56805Szain wang dp->link_train.lane_count = max_lanes;
6743424e3a4SYakir Yang if (dp->link_train.link_rate > max_rate)
6753424e3a4SYakir Yang dp->link_train.link_rate = max_rate;
6763424e3a4SYakir Yang
6773424e3a4SYakir Yang /* All DP analog module power up */
6783424e3a4SYakir Yang analogix_dp_set_analog_power_down(dp, POWER_ALL, 0);
6793424e3a4SYakir Yang
6803424e3a4SYakir Yang dp->link_train.lt_state = START;
6813424e3a4SYakir Yang
6823424e3a4SYakir Yang /* Process here */
6833424e3a4SYakir Yang while (!retval && !training_finished) {
6843424e3a4SYakir Yang switch (dp->link_train.lt_state) {
6853424e3a4SYakir Yang case START:
6863424e3a4SYakir Yang retval = analogix_dp_link_start(dp);
6873424e3a4SYakir Yang if (retval)
6883424e3a4SYakir Yang dev_err(dp->dev, "LT link start failed!\n");
6893424e3a4SYakir Yang break;
6903424e3a4SYakir Yang case CLOCK_RECOVERY:
6913424e3a4SYakir Yang retval = analogix_dp_process_clock_recovery(dp);
6923424e3a4SYakir Yang if (retval)
6933424e3a4SYakir Yang dev_err(dp->dev, "LT CR failed!\n");
6943424e3a4SYakir Yang break;
6953424e3a4SYakir Yang case EQUALIZER_TRAINING:
6963424e3a4SYakir Yang retval = analogix_dp_process_equalizer_training(dp);
6973424e3a4SYakir Yang if (retval)
6983424e3a4SYakir Yang dev_err(dp->dev, "LT EQ failed!\n");
6993424e3a4SYakir Yang break;
7003424e3a4SYakir Yang case FINISHED:
7013424e3a4SYakir Yang training_finished = 1;
7023424e3a4SYakir Yang break;
7033424e3a4SYakir Yang case FAILED:
7043424e3a4SYakir Yang return -EREMOTEIO;
7053424e3a4SYakir Yang }
7063424e3a4SYakir Yang }
7073424e3a4SYakir Yang if (retval)
7083424e3a4SYakir Yang dev_err(dp->dev, "eDP link training failed (%d)\n", retval);
7093424e3a4SYakir Yang
7103424e3a4SYakir Yang return retval;
7113424e3a4SYakir Yang }
7123424e3a4SYakir Yang
analogix_dp_fast_link_train(struct analogix_dp_device * dp)713f9d56805Szain wang static int analogix_dp_fast_link_train(struct analogix_dp_device *dp)
7143424e3a4SYakir Yang {
715f9d56805Szain wang int i, ret;
716f9d56805Szain wang u8 link_align, link_status[2];
717f9d56805Szain wang enum pll_status status;
7183424e3a4SYakir Yang
719f9d56805Szain wang analogix_dp_reset_macro(dp);
7203424e3a4SYakir Yang
721f9d56805Szain wang analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
722f9d56805Szain wang analogix_dp_set_lane_count(dp, dp->link_train.lane_count);
723f9d56805Szain wang
724f9d56805Szain wang for (i = 0; i < dp->link_train.lane_count; i++) {
725f9d56805Szain wang analogix_dp_set_lane_link_training(dp,
726f9d56805Szain wang dp->link_train.training_lane[i], i);
7273424e3a4SYakir Yang }
7283424e3a4SYakir Yang
729f9d56805Szain wang ret = readx_poll_timeout(analogix_dp_get_pll_lock_status, dp, status,
730f9d56805Szain wang status != PLL_UNLOCKED, 120,
731f9d56805Szain wang 120 * DP_TIMEOUT_LOOP_COUNT);
732f9d56805Szain wang if (ret) {
733f9d56805Szain wang DRM_DEV_ERROR(dp->dev, "Wait for pll lock failed %d\n", ret);
734f9d56805Szain wang return ret;
735f9d56805Szain wang }
736f9d56805Szain wang
737f9d56805Szain wang /* source Set training pattern 1 */
738f9d56805Szain wang analogix_dp_set_training_pattern(dp, TRAINING_PTN1);
739f9d56805Szain wang /* From DP spec, pattern must be on-screen for a minimum 500us */
740f9d56805Szain wang usleep_range(500, 600);
741f9d56805Szain wang
742f9d56805Szain wang analogix_dp_set_training_pattern(dp, TRAINING_PTN2);
743f9d56805Szain wang /* From DP spec, pattern must be on-screen for a minimum 500us */
744f9d56805Szain wang usleep_range(500, 600);
745f9d56805Szain wang
746f9d56805Szain wang /* TODO: enhanced_mode?*/
747f9d56805Szain wang analogix_dp_set_training_pattern(dp, DP_NONE);
748f9d56805Szain wang
749f9d56805Szain wang /*
750f9d56805Szain wang * Useful for debugging issues with fast link training, disable for more
751f9d56805Szain wang * speed
752f9d56805Szain wang */
753f9d56805Szain wang if (verify_fast_training) {
754f9d56805Szain wang ret = drm_dp_dpcd_readb(&dp->aux, DP_LANE_ALIGN_STATUS_UPDATED,
755f9d56805Szain wang &link_align);
756f9d56805Szain wang if (ret < 0) {
757f9d56805Szain wang DRM_DEV_ERROR(dp->dev, "Read align status failed %d\n",
758f9d56805Szain wang ret);
759f9d56805Szain wang return ret;
760f9d56805Szain wang }
761f9d56805Szain wang
762f9d56805Szain wang ret = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status,
763f9d56805Szain wang 2);
764f9d56805Szain wang if (ret < 0) {
765f9d56805Szain wang DRM_DEV_ERROR(dp->dev, "Read link status failed %d\n",
766f9d56805Szain wang ret);
767f9d56805Szain wang return ret;
768f9d56805Szain wang }
769f9d56805Szain wang
770f9d56805Szain wang if (analogix_dp_clock_recovery_ok(link_status,
771f9d56805Szain wang dp->link_train.lane_count)) {
772f9d56805Szain wang DRM_DEV_ERROR(dp->dev, "Clock recovery failed\n");
773f9d56805Szain wang analogix_dp_reduce_link_rate(dp);
774f9d56805Szain wang return -EIO;
775f9d56805Szain wang }
776f9d56805Szain wang
777f9d56805Szain wang if (analogix_dp_channel_eq_ok(link_status, link_align,
778f9d56805Szain wang dp->link_train.lane_count)) {
779f9d56805Szain wang DRM_DEV_ERROR(dp->dev, "Channel EQ failed\n");
780f9d56805Szain wang analogix_dp_reduce_link_rate(dp);
781f9d56805Szain wang return -EIO;
782f9d56805Szain wang }
783f9d56805Szain wang }
784f9d56805Szain wang
785f9d56805Szain wang return 0;
786f9d56805Szain wang }
787f9d56805Szain wang
analogix_dp_train_link(struct analogix_dp_device * dp)788f9d56805Szain wang static int analogix_dp_train_link(struct analogix_dp_device *dp)
789f9d56805Szain wang {
7907ba8fb57Szain wang if (dp->fast_train_enable)
791f9d56805Szain wang return analogix_dp_fast_link_train(dp);
792f9d56805Szain wang
793f9d56805Szain wang return analogix_dp_full_link_train(dp, dp->video_info.max_lane_count,
794f9d56805Szain wang dp->video_info.max_link_rate);
7953424e3a4SYakir Yang }
7963424e3a4SYakir Yang
analogix_dp_config_video(struct analogix_dp_device * dp)7973424e3a4SYakir Yang static int analogix_dp_config_video(struct analogix_dp_device *dp)
7983424e3a4SYakir Yang {
7993424e3a4SYakir Yang int timeout_loop = 0;
8003424e3a4SYakir Yang int done_count = 0;
8013424e3a4SYakir Yang
8023424e3a4SYakir Yang analogix_dp_config_video_slave_mode(dp);
8033424e3a4SYakir Yang
8043424e3a4SYakir Yang analogix_dp_set_video_color_format(dp);
8053424e3a4SYakir Yang
8063424e3a4SYakir Yang if (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
8073424e3a4SYakir Yang dev_err(dp->dev, "PLL is not locked yet.\n");
8083424e3a4SYakir Yang return -EINVAL;
8093424e3a4SYakir Yang }
8103424e3a4SYakir Yang
8113424e3a4SYakir Yang for (;;) {
8123424e3a4SYakir Yang timeout_loop++;
8133424e3a4SYakir Yang if (analogix_dp_is_slave_video_stream_clock_on(dp) == 0)
8143424e3a4SYakir Yang break;
815bcbb7033SYakir Yang if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
81693cba9daSLin Huang dev_err(dp->dev, "Timeout of slave video streamclk ok\n");
8173424e3a4SYakir Yang return -ETIMEDOUT;
8183424e3a4SYakir Yang }
81993cba9daSLin Huang usleep_range(1000, 1001);
8203424e3a4SYakir Yang }
8213424e3a4SYakir Yang
8223424e3a4SYakir Yang /* Set to use the register calculated M/N video */
8233424e3a4SYakir Yang analogix_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0);
8243424e3a4SYakir Yang
8253424e3a4SYakir Yang /* For video bist, Video timing must be generated by register */
8263424e3a4SYakir Yang analogix_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE);
8273424e3a4SYakir Yang
8283424e3a4SYakir Yang /* Disable video mute */
8293424e3a4SYakir Yang analogix_dp_enable_video_mute(dp, 0);
8303424e3a4SYakir Yang
8313424e3a4SYakir Yang /* Configure video slave mode */
8323424e3a4SYakir Yang analogix_dp_enable_video_master(dp, 0);
8333424e3a4SYakir Yang
83493cba9daSLin Huang /* Enable video */
83593cba9daSLin Huang analogix_dp_start_video(dp);
83693cba9daSLin Huang
8373424e3a4SYakir Yang timeout_loop = 0;
8383424e3a4SYakir Yang
8393424e3a4SYakir Yang for (;;) {
8403424e3a4SYakir Yang timeout_loop++;
8413424e3a4SYakir Yang if (analogix_dp_is_video_stream_on(dp) == 0) {
8423424e3a4SYakir Yang done_count++;
8433424e3a4SYakir Yang if (done_count > 10)
8443424e3a4SYakir Yang break;
8453424e3a4SYakir Yang } else if (done_count) {
8463424e3a4SYakir Yang done_count = 0;
8473424e3a4SYakir Yang }
848bcbb7033SYakir Yang if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
849c4d3b1a2Szain wang dev_warn(dp->dev,
850c4d3b1a2Szain wang "Ignoring timeout of video streamclk ok\n");
851c4d3b1a2Szain wang break;
8523424e3a4SYakir Yang }
8533424e3a4SYakir Yang
8543424e3a4SYakir Yang usleep_range(1000, 1001);
8553424e3a4SYakir Yang }
8563424e3a4SYakir Yang
857c4712f27SSylwester Nawrocki return 0;
8583424e3a4SYakir Yang }
8593424e3a4SYakir Yang
analogix_dp_enable_scramble(struct analogix_dp_device * dp,bool enable)860ccdc578bSLin Huang static int analogix_dp_enable_scramble(struct analogix_dp_device *dp,
861bcbb7033SYakir Yang bool enable)
8623424e3a4SYakir Yang {
8633424e3a4SYakir Yang u8 data;
864ccdc578bSLin Huang int ret;
8653424e3a4SYakir Yang
8663424e3a4SYakir Yang if (enable) {
8673424e3a4SYakir Yang analogix_dp_enable_scrambling(dp);
8683424e3a4SYakir Yang
869ccdc578bSLin Huang ret = drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET,
870ccdc578bSLin Huang &data);
871ccdc578bSLin Huang if (ret != 1)
872ccdc578bSLin Huang return ret;
873ccdc578bSLin Huang ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
8743424e3a4SYakir Yang (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
8753424e3a4SYakir Yang } else {
8763424e3a4SYakir Yang analogix_dp_disable_scrambling(dp);
8773424e3a4SYakir Yang
878ccdc578bSLin Huang ret = drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET,
879ccdc578bSLin Huang &data);
880ccdc578bSLin Huang if (ret != 1)
881ccdc578bSLin Huang return ret;
882ccdc578bSLin Huang ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
8833424e3a4SYakir Yang (u8)(data | DP_LINK_SCRAMBLING_DISABLE));
8843424e3a4SYakir Yang }
885ccdc578bSLin Huang return ret < 0 ? ret : 0;
8863424e3a4SYakir Yang }
8873424e3a4SYakir Yang
analogix_dp_hardirq(int irq,void * arg)8887b4b7a8dSYakir Yang static irqreturn_t analogix_dp_hardirq(int irq, void *arg)
8893424e3a4SYakir Yang {
8903424e3a4SYakir Yang struct analogix_dp_device *dp = arg;
8917b4b7a8dSYakir Yang irqreturn_t ret = IRQ_NONE;
8923424e3a4SYakir Yang enum dp_irq_type irq_type;
8933424e3a4SYakir Yang
8943424e3a4SYakir Yang irq_type = analogix_dp_get_irq_type(dp);
8957b4b7a8dSYakir Yang if (irq_type != DP_IRQ_TYPE_UNKNOWN) {
8967b4b7a8dSYakir Yang analogix_dp_mute_hpd_interrupt(dp);
8977b4b7a8dSYakir Yang ret = IRQ_WAKE_THREAD;
8983424e3a4SYakir Yang }
8993424e3a4SYakir Yang
9007b4b7a8dSYakir Yang return ret;
9017b4b7a8dSYakir Yang }
9027b4b7a8dSYakir Yang
analogix_dp_irq_thread(int irq,void * arg)9037b4b7a8dSYakir Yang static irqreturn_t analogix_dp_irq_thread(int irq, void *arg)
9043424e3a4SYakir Yang {
9057b4b7a8dSYakir Yang struct analogix_dp_device *dp = arg;
9067b4b7a8dSYakir Yang enum dp_irq_type irq_type;
9073424e3a4SYakir Yang
9087b4b7a8dSYakir Yang irq_type = analogix_dp_get_irq_type(dp);
9097b4b7a8dSYakir Yang if (irq_type & DP_IRQ_TYPE_HP_CABLE_IN ||
9107b4b7a8dSYakir Yang irq_type & DP_IRQ_TYPE_HP_CABLE_OUT) {
9117b4b7a8dSYakir Yang dev_dbg(dp->dev, "Detected cable status changed!\n");
9123424e3a4SYakir Yang if (dp->drm_dev)
9133424e3a4SYakir Yang drm_helper_hpd_irq_event(dp->drm_dev);
9143424e3a4SYakir Yang }
9153424e3a4SYakir Yang
9167b4b7a8dSYakir Yang if (irq_type != DP_IRQ_TYPE_UNKNOWN) {
9177b4b7a8dSYakir Yang analogix_dp_clear_hotplug_interrupts(dp);
9187b4b7a8dSYakir Yang analogix_dp_unmute_hpd_interrupt(dp);
9197b4b7a8dSYakir Yang }
9207b4b7a8dSYakir Yang
9217b4b7a8dSYakir Yang return IRQ_HANDLED;
9227b4b7a8dSYakir Yang }
9237b4b7a8dSYakir Yang
analogix_dp_fast_link_train_detection(struct analogix_dp_device * dp)9246f4638a1Szain wang static int analogix_dp_fast_link_train_detection(struct analogix_dp_device *dp)
9256f4638a1Szain wang {
9266f4638a1Szain wang int ret;
9276f4638a1Szain wang u8 spread;
9286f4638a1Szain wang
9296f4638a1Szain wang ret = drm_dp_dpcd_readb(&dp->aux, DP_MAX_DOWNSPREAD, &spread);
9306f4638a1Szain wang if (ret != 1) {
9316f4638a1Szain wang dev_err(dp->dev, "failed to read downspread %d\n", ret);
9326f4638a1Szain wang return ret;
9336f4638a1Szain wang }
9346f4638a1Szain wang dp->fast_train_enable = !!(spread & DP_NO_AUX_HANDSHAKE_LINK_TRAINING);
9356f4638a1Szain wang dev_dbg(dp->dev, "fast link training %s\n",
9366f4638a1Szain wang dp->fast_train_enable ? "supported" : "unsupported");
9376f4638a1Szain wang return 0;
9386f4638a1Szain wang }
9396f4638a1Szain wang
analogix_dp_commit(struct analogix_dp_device * dp)9408a335736Szain wang static int analogix_dp_commit(struct analogix_dp_device *dp)
9413424e3a4SYakir Yang {
9423424e3a4SYakir Yang int ret;
9433424e3a4SYakir Yang
9443424e3a4SYakir Yang /* Keep the panel disabled while we configure video */
9453424e3a4SYakir Yang if (dp->plat_data->panel) {
9463424e3a4SYakir Yang if (drm_panel_disable(dp->plat_data->panel))
9473424e3a4SYakir Yang DRM_ERROR("failed to disable the panel\n");
9483424e3a4SYakir Yang }
9493424e3a4SYakir Yang
9508a335736Szain wang ret = analogix_dp_train_link(dp);
9513424e3a4SYakir Yang if (ret) {
952f9d56805Szain wang dev_err(dp->dev, "unable to do link train, ret=%d\n", ret);
9538a335736Szain wang return ret;
9543424e3a4SYakir Yang }
9553424e3a4SYakir Yang
956ccdc578bSLin Huang ret = analogix_dp_enable_scramble(dp, 1);
957ccdc578bSLin Huang if (ret < 0) {
958ccdc578bSLin Huang dev_err(dp->dev, "can not enable scramble\n");
959ccdc578bSLin Huang return ret;
960ccdc578bSLin Huang }
9613424e3a4SYakir Yang
9623424e3a4SYakir Yang analogix_dp_init_video(dp);
9633424e3a4SYakir Yang ret = analogix_dp_config_video(dp);
964ccdc578bSLin Huang if (ret) {
9653424e3a4SYakir Yang dev_err(dp->dev, "unable to config video\n");
966ccdc578bSLin Huang return ret;
967ccdc578bSLin Huang }
9683424e3a4SYakir Yang
9693424e3a4SYakir Yang /* Safe to enable the panel now */
9703424e3a4SYakir Yang if (dp->plat_data->panel) {
971ccdc578bSLin Huang ret = drm_panel_enable(dp->plat_data->panel);
972ccdc578bSLin Huang if (ret) {
9733424e3a4SYakir Yang DRM_ERROR("failed to enable the panel\n");
974ccdc578bSLin Huang return ret;
975ccdc578bSLin Huang }
9763424e3a4SYakir Yang }
9773424e3a4SYakir Yang
978ad309284SSean Paul /* Check whether panel supports fast training */
979ad309284SSean Paul ret = analogix_dp_fast_link_train_detection(dp);
980ad309284SSean Paul if (ret)
9816c836d96SSean Paul return ret;
982ad309284SSean Paul
9836c836d96SSean Paul if (analogix_dp_detect_sink_psr(dp)) {
984ccdc578bSLin Huang ret = analogix_dp_enable_sink_psr(dp);
9856f4638a1Szain wang if (ret)
9866f4638a1Szain wang return ret;
9876f4638a1Szain wang }
9886f4638a1Szain wang
9896c836d96SSean Paul return ret;
9906c836d96SSean Paul }
9916c836d96SSean Paul
analogix_dp_enable_psr(struct analogix_dp_device * dp)9926c836d96SSean Paul static int analogix_dp_enable_psr(struct analogix_dp_device *dp)
9936c836d96SSean Paul {
9946c836d96SSean Paul struct dp_sdp psr_vsc;
9956c836d96SSean Paul int ret;
9966c836d96SSean Paul u8 sink;
9976c836d96SSean Paul
9986c836d96SSean Paul ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_STATUS, &sink);
9996c836d96SSean Paul if (ret != 1)
10006c836d96SSean Paul DRM_DEV_ERROR(dp->dev, "Failed to read psr status %d\n", ret);
10016c836d96SSean Paul else if (sink == DP_PSR_SINK_ACTIVE_RFB)
10026c836d96SSean Paul return 0;
10036c836d96SSean Paul
10046c836d96SSean Paul /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
10056c836d96SSean Paul memset(&psr_vsc, 0, sizeof(psr_vsc));
10066c836d96SSean Paul psr_vsc.sdp_header.HB0 = 0;
10076c836d96SSean Paul psr_vsc.sdp_header.HB1 = 0x7;
10086c836d96SSean Paul psr_vsc.sdp_header.HB2 = 0x2;
10096c836d96SSean Paul psr_vsc.sdp_header.HB3 = 0x8;
10106c836d96SSean Paul psr_vsc.db[0] = 0;
10116c836d96SSean Paul psr_vsc.db[1] = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID;
10126c836d96SSean Paul
10136c836d96SSean Paul ret = analogix_dp_send_psr_spd(dp, &psr_vsc, true);
10146c836d96SSean Paul if (!ret)
10156c836d96SSean Paul analogix_dp_set_analog_power_down(dp, POWER_ALL, true);
1016ccdc578bSLin Huang
1017ccdc578bSLin Huang return ret;
10183424e3a4SYakir Yang }
10193424e3a4SYakir Yang
analogix_dp_disable_psr(struct analogix_dp_device * dp)10206c836d96SSean Paul static int analogix_dp_disable_psr(struct analogix_dp_device *dp)
10216c836d96SSean Paul {
10226c836d96SSean Paul struct dp_sdp psr_vsc;
10236c836d96SSean Paul int ret;
10246c836d96SSean Paul u8 sink;
10256c836d96SSean Paul
10266c836d96SSean Paul analogix_dp_set_analog_power_down(dp, POWER_ALL, false);
10276c836d96SSean Paul
10286c836d96SSean Paul ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
10296c836d96SSean Paul if (ret != 1) {
10306c836d96SSean Paul DRM_DEV_ERROR(dp->dev, "Failed to set DP Power0 %d\n", ret);
10316c836d96SSean Paul return ret;
10326c836d96SSean Paul }
10336c836d96SSean Paul
10346c836d96SSean Paul ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_STATUS, &sink);
10356c836d96SSean Paul if (ret != 1) {
10366c836d96SSean Paul DRM_DEV_ERROR(dp->dev, "Failed to read psr status %d\n", ret);
10376c836d96SSean Paul return ret;
10386c836d96SSean Paul } else if (sink == DP_PSR_SINK_INACTIVE) {
10396c836d96SSean Paul DRM_DEV_ERROR(dp->dev, "sink inactive, skip disable psr");
10406c836d96SSean Paul return 0;
10416c836d96SSean Paul }
10426c836d96SSean Paul
10436c836d96SSean Paul ret = analogix_dp_train_link(dp);
10446c836d96SSean Paul if (ret) {
10456c836d96SSean Paul DRM_DEV_ERROR(dp->dev, "Failed to train the link %d\n", ret);
10466c836d96SSean Paul return ret;
10476c836d96SSean Paul }
10486c836d96SSean Paul
10496c836d96SSean Paul /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
10506c836d96SSean Paul memset(&psr_vsc, 0, sizeof(psr_vsc));
10516c836d96SSean Paul psr_vsc.sdp_header.HB0 = 0;
10526c836d96SSean Paul psr_vsc.sdp_header.HB1 = 0x7;
10536c836d96SSean Paul psr_vsc.sdp_header.HB2 = 0x2;
10546c836d96SSean Paul psr_vsc.sdp_header.HB3 = 0x8;
10556c836d96SSean Paul
10566c836d96SSean Paul psr_vsc.db[0] = 0;
10576c836d96SSean Paul psr_vsc.db[1] = 0;
10586c836d96SSean Paul
10596c836d96SSean Paul return analogix_dp_send_psr_spd(dp, &psr_vsc, true);
10606c836d96SSean Paul }
10616c836d96SSean Paul
10620b8b059aSSean Paul /*
10630b8b059aSSean Paul * This function is a bit of a catch-all for panel preparation, hopefully
10640b8b059aSSean Paul * simplifying the logic of functions that need to prepare/unprepare the panel
10650b8b059aSSean Paul * below.
10660b8b059aSSean Paul *
10670b8b059aSSean Paul * If @prepare is true, this function will prepare the panel. Conversely, if it
10680b8b059aSSean Paul * is false, the panel will be unprepared.
10690b8b059aSSean Paul *
10700b8b059aSSean Paul * If @is_modeset_prepare is true, the function will disregard the current state
10710b8b059aSSean Paul * of the panel and either prepare/unprepare the panel based on @prepare. Once
10720b8b059aSSean Paul * it finishes, it will update dp->panel_is_modeset to reflect the current state
10730b8b059aSSean Paul * of the panel.
10740b8b059aSSean Paul */
analogix_dp_prepare_panel(struct analogix_dp_device * dp,bool prepare,bool is_modeset_prepare)10750b8b059aSSean Paul static int analogix_dp_prepare_panel(struct analogix_dp_device *dp,
10760b8b059aSSean Paul bool prepare, bool is_modeset_prepare)
10770b8b059aSSean Paul {
10780b8b059aSSean Paul int ret = 0;
10790b8b059aSSean Paul
10800b8b059aSSean Paul if (!dp->plat_data->panel)
10810b8b059aSSean Paul return 0;
10820b8b059aSSean Paul
10830b8b059aSSean Paul mutex_lock(&dp->panel_lock);
10840b8b059aSSean Paul
10850b8b059aSSean Paul /*
10860b8b059aSSean Paul * Exit early if this is a temporary prepare/unprepare and we're already
10870b8b059aSSean Paul * modeset (since we neither want to prepare twice or unprepare early).
10880b8b059aSSean Paul */
10890b8b059aSSean Paul if (dp->panel_is_modeset && !is_modeset_prepare)
10900b8b059aSSean Paul goto out;
10910b8b059aSSean Paul
10920b8b059aSSean Paul if (prepare)
10930b8b059aSSean Paul ret = drm_panel_prepare(dp->plat_data->panel);
10940b8b059aSSean Paul else
10950b8b059aSSean Paul ret = drm_panel_unprepare(dp->plat_data->panel);
10960b8b059aSSean Paul
10970b8b059aSSean Paul if (ret)
10980b8b059aSSean Paul goto out;
10990b8b059aSSean Paul
11000b8b059aSSean Paul if (is_modeset_prepare)
11010b8b059aSSean Paul dp->panel_is_modeset = prepare;
11020b8b059aSSean Paul
11030b8b059aSSean Paul out:
11040b8b059aSSean Paul mutex_unlock(&dp->panel_lock);
11050b8b059aSSean Paul return ret;
11060b8b059aSSean Paul }
11070b8b059aSSean Paul
analogix_dp_get_modes(struct drm_connector * connector)1108089cfdd9SBaoyou Xie static int analogix_dp_get_modes(struct drm_connector *connector)
11093424e3a4SYakir Yang {
11103424e3a4SYakir Yang struct analogix_dp_device *dp = to_dp(connector);
11110d97ad03STomeu Vizoso struct edid *edid;
11120b8b059aSSean Paul int ret, num_modes = 0;
11130b8b059aSSean Paul
1114f2600d08SSean Paul if (dp->plat_data->panel) {
111506c4a9c2SSam Ravnborg num_modes += drm_panel_get_modes(dp->plat_data->panel, connector);
1116f2600d08SSean Paul } else {
11170b8b059aSSean Paul ret = analogix_dp_prepare_panel(dp, true, false);
11180b8b059aSSean Paul if (ret) {
11190b8b059aSSean Paul DRM_ERROR("Failed to prepare panel (%d)\n", ret);
11200b8b059aSSean Paul return 0;
11210b8b059aSSean Paul }
11223424e3a4SYakir Yang
11230d97ad03STomeu Vizoso edid = drm_get_edid(connector, &dp->aux.ddc);
11240d97ad03STomeu Vizoso if (edid) {
1125c555f023SDaniel Vetter drm_connector_update_edid_property(&dp->connector,
11268c77e2c2SSean Paul edid);
1127398a3995SYakir Yang num_modes += drm_add_edid_modes(&dp->connector, edid);
11280d97ad03STomeu Vizoso kfree(edid);
1129398a3995SYakir Yang }
11303424e3a4SYakir Yang
11310b8b059aSSean Paul ret = analogix_dp_prepare_panel(dp, false, false);
11320b8b059aSSean Paul if (ret)
11330b8b059aSSean Paul DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
1134f2600d08SSean Paul }
1135f2600d08SSean Paul
1136f2600d08SSean Paul if (dp->plat_data->get_modes)
1137f2600d08SSean Paul num_modes += dp->plat_data->get_modes(dp->plat_data, connector);
11380b8b059aSSean Paul
11393424e3a4SYakir Yang return num_modes;
11403424e3a4SYakir Yang }
11413424e3a4SYakir Yang
11423424e3a4SYakir Yang static struct drm_encoder *
analogix_dp_best_encoder(struct drm_connector * connector)11433424e3a4SYakir Yang analogix_dp_best_encoder(struct drm_connector *connector)
11443424e3a4SYakir Yang {
11453424e3a4SYakir Yang struct analogix_dp_device *dp = to_dp(connector);
11463424e3a4SYakir Yang
11473424e3a4SYakir Yang return dp->encoder;
11483424e3a4SYakir Yang }
11493424e3a4SYakir Yang
11506c836d96SSean Paul
analogix_dp_atomic_check(struct drm_connector * connector,struct drm_atomic_state * state)11510486ad20SYueHaibing static int analogix_dp_atomic_check(struct drm_connector *connector,
11526c836d96SSean Paul struct drm_atomic_state *state)
11536c836d96SSean Paul {
11546c836d96SSean Paul struct analogix_dp_device *dp = to_dp(connector);
11556c836d96SSean Paul struct drm_connector_state *conn_state;
11566c836d96SSean Paul struct drm_crtc_state *crtc_state;
11576c836d96SSean Paul
11586c836d96SSean Paul conn_state = drm_atomic_get_new_connector_state(state, connector);
11596c836d96SSean Paul if (WARN_ON(!conn_state))
11606c836d96SSean Paul return -ENODEV;
11616c836d96SSean Paul
11626c836d96SSean Paul conn_state->self_refresh_aware = true;
11636c836d96SSean Paul
11646c836d96SSean Paul if (!conn_state->crtc)
11656c836d96SSean Paul return 0;
11666c836d96SSean Paul
11676c836d96SSean Paul crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
11686c836d96SSean Paul if (!crtc_state)
11696c836d96SSean Paul return 0;
11706c836d96SSean Paul
11716c836d96SSean Paul if (crtc_state->self_refresh_active && !dp->psr_supported)
11726c836d96SSean Paul return -EINVAL;
11736c836d96SSean Paul
11746c836d96SSean Paul return 0;
11756c836d96SSean Paul }
11766c836d96SSean Paul
11773424e3a4SYakir Yang static const struct drm_connector_helper_funcs analogix_dp_connector_helper_funcs = {
11783424e3a4SYakir Yang .get_modes = analogix_dp_get_modes,
11793424e3a4SYakir Yang .best_encoder = analogix_dp_best_encoder,
11806c836d96SSean Paul .atomic_check = analogix_dp_atomic_check,
11813424e3a4SYakir Yang };
11823424e3a4SYakir Yang
1183089cfdd9SBaoyou Xie static enum drm_connector_status
analogix_dp_detect(struct drm_connector * connector,bool force)11843424e3a4SYakir Yang analogix_dp_detect(struct drm_connector *connector, bool force)
11853424e3a4SYakir Yang {
11862b77a291SYakir Yang struct analogix_dp_device *dp = to_dp(connector);
11870b8b059aSSean Paul enum drm_connector_status status = connector_status_disconnected;
11880b8b059aSSean Paul int ret;
11892b77a291SYakir Yang
1190f2600d08SSean Paul if (dp->plat_data->panel)
1191f2600d08SSean Paul return connector_status_connected;
1192f2600d08SSean Paul
11930b8b059aSSean Paul ret = analogix_dp_prepare_panel(dp, true, false);
11940b8b059aSSean Paul if (ret) {
11950b8b059aSSean Paul DRM_ERROR("Failed to prepare panel (%d)\n", ret);
11962b77a291SYakir Yang return connector_status_disconnected;
11970b8b059aSSean Paul }
11982b77a291SYakir Yang
11990b8b059aSSean Paul if (!analogix_dp_detect_hpd(dp))
12000b8b059aSSean Paul status = connector_status_connected;
12010b8b059aSSean Paul
12020b8b059aSSean Paul ret = analogix_dp_prepare_panel(dp, false, false);
12030b8b059aSSean Paul if (ret)
12040b8b059aSSean Paul DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
12050b8b059aSSean Paul
12060b8b059aSSean Paul return status;
12073424e3a4SYakir Yang }
12083424e3a4SYakir Yang
12093424e3a4SYakir Yang static const struct drm_connector_funcs analogix_dp_connector_funcs = {
12103424e3a4SYakir Yang .fill_modes = drm_helper_probe_single_connector_modes,
12113424e3a4SYakir Yang .detect = analogix_dp_detect,
1212fdd8326aSMarek Vasut .destroy = drm_connector_cleanup,
12133424e3a4SYakir Yang .reset = drm_atomic_helper_connector_reset,
12143424e3a4SYakir Yang .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
12153424e3a4SYakir Yang .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
12163424e3a4SYakir Yang };
12173424e3a4SYakir Yang
analogix_dp_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)1218a25b988fSLaurent Pinchart static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
1219a25b988fSLaurent Pinchart enum drm_bridge_attach_flags flags)
12203424e3a4SYakir Yang {
12213424e3a4SYakir Yang struct analogix_dp_device *dp = bridge->driver_private;
12223424e3a4SYakir Yang struct drm_encoder *encoder = dp->encoder;
12232e9b3e74SMarek Szyprowski struct drm_connector *connector = NULL;
12242e9b3e74SMarek Szyprowski int ret = 0;
12253424e3a4SYakir Yang
1226a25b988fSLaurent Pinchart if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
1227a25b988fSLaurent Pinchart DRM_ERROR("Fix bridge driver to make connector optional!");
1228a25b988fSLaurent Pinchart return -EINVAL;
1229a25b988fSLaurent Pinchart }
1230a25b988fSLaurent Pinchart
12313424e3a4SYakir Yang if (!bridge->encoder) {
12323424e3a4SYakir Yang DRM_ERROR("Parent encoder object not found");
12333424e3a4SYakir Yang return -ENODEV;
12343424e3a4SYakir Yang }
12353424e3a4SYakir Yang
12362e9b3e74SMarek Szyprowski if (!dp->plat_data->skip_connector) {
12372e9b3e74SMarek Szyprowski connector = &dp->connector;
12383424e3a4SYakir Yang connector->polled = DRM_CONNECTOR_POLL_HPD;
12393424e3a4SYakir Yang
12403424e3a4SYakir Yang ret = drm_connector_init(dp->drm_dev, connector,
12413424e3a4SYakir Yang &analogix_dp_connector_funcs,
12423424e3a4SYakir Yang DRM_MODE_CONNECTOR_eDP);
12433424e3a4SYakir Yang if (ret) {
12443424e3a4SYakir Yang DRM_ERROR("Failed to initialize connector with drm\n");
12453424e3a4SYakir Yang return ret;
12463424e3a4SYakir Yang }
12473424e3a4SYakir Yang
12483424e3a4SYakir Yang drm_connector_helper_add(connector,
12493424e3a4SYakir Yang &analogix_dp_connector_helper_funcs);
1250cde4c44dSDaniel Vetter drm_connector_attach_encoder(connector, encoder);
12512e9b3e74SMarek Szyprowski }
12523424e3a4SYakir Yang
12533424e3a4SYakir Yang /*
12543424e3a4SYakir Yang * NOTE: the connector registration is implemented in analogix
12553424e3a4SYakir Yang * platform driver, that to say connector would be exist after
12563424e3a4SYakir Yang * plat_data->attch return, that's why we record the connector
12573424e3a4SYakir Yang * point after plat attached.
12583424e3a4SYakir Yang */
12593424e3a4SYakir Yang if (dp->plat_data->attach) {
12603424e3a4SYakir Yang ret = dp->plat_data->attach(dp->plat_data, bridge, connector);
12613424e3a4SYakir Yang if (ret) {
126229305d7eSEnric Balletbo i Serra DRM_ERROR("Failed at platform attach func\n");
12633424e3a4SYakir Yang return ret;
12643424e3a4SYakir Yang }
12653424e3a4SYakir Yang }
12663424e3a4SYakir Yang
12673424e3a4SYakir Yang return 0;
12683424e3a4SYakir Yang }
12693424e3a4SYakir Yang
12706c836d96SSean Paul static
analogix_dp_get_old_crtc(struct analogix_dp_device * dp,struct drm_atomic_state * state)1271ca871659SBrian Norris struct drm_crtc *analogix_dp_get_old_crtc(struct analogix_dp_device *dp,
1272ca871659SBrian Norris struct drm_atomic_state *state)
1273ca871659SBrian Norris {
1274ca871659SBrian Norris struct drm_encoder *encoder = dp->encoder;
1275ca871659SBrian Norris struct drm_connector *connector;
1276ca871659SBrian Norris struct drm_connector_state *conn_state;
1277ca871659SBrian Norris
1278ca871659SBrian Norris connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
1279ca871659SBrian Norris if (!connector)
1280ca871659SBrian Norris return NULL;
1281ca871659SBrian Norris
1282ca871659SBrian Norris conn_state = drm_atomic_get_old_connector_state(state, connector);
1283ca871659SBrian Norris if (!conn_state)
1284ca871659SBrian Norris return NULL;
1285ca871659SBrian Norris
1286ca871659SBrian Norris return conn_state->crtc;
1287ca871659SBrian Norris }
1288ca871659SBrian Norris
1289ca871659SBrian Norris static
analogix_dp_get_new_crtc(struct analogix_dp_device * dp,struct drm_atomic_state * state)12906c836d96SSean Paul struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp,
12916c836d96SSean Paul struct drm_atomic_state *state)
12926c836d96SSean Paul {
12936c836d96SSean Paul struct drm_encoder *encoder = dp->encoder;
12946c836d96SSean Paul struct drm_connector *connector;
12956c836d96SSean Paul struct drm_connector_state *conn_state;
12966c836d96SSean Paul
12976c836d96SSean Paul connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
12986c836d96SSean Paul if (!connector)
12996c836d96SSean Paul return NULL;
13006c836d96SSean Paul
13016c836d96SSean Paul conn_state = drm_atomic_get_new_connector_state(state, connector);
13026c836d96SSean Paul if (!conn_state)
13036c836d96SSean Paul return NULL;
13046c836d96SSean Paul
13056c836d96SSean Paul return conn_state->crtc;
13066c836d96SSean Paul }
13076c836d96SSean Paul
130841cf5712SBoris Brezillon static void
analogix_dp_bridge_atomic_pre_enable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)130941cf5712SBoris Brezillon analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
131041cf5712SBoris Brezillon struct drm_bridge_state *old_bridge_state)
13110b8b059aSSean Paul {
131241cf5712SBoris Brezillon struct drm_atomic_state *old_state = old_bridge_state->base.state;
13130b8b059aSSean Paul struct analogix_dp_device *dp = bridge->driver_private;
13146c836d96SSean Paul struct drm_crtc *crtc;
13156c836d96SSean Paul struct drm_crtc_state *old_crtc_state;
13160b8b059aSSean Paul int ret;
13170b8b059aSSean Paul
131841cf5712SBoris Brezillon crtc = analogix_dp_get_new_crtc(dp, old_state);
13196c836d96SSean Paul if (!crtc)
13206c836d96SSean Paul return;
13216c836d96SSean Paul
132241cf5712SBoris Brezillon old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
13236c836d96SSean Paul /* Don't touch the panel if we're coming back from PSR */
13246c836d96SSean Paul if (old_crtc_state && old_crtc_state->self_refresh_active)
13256c836d96SSean Paul return;
13266c836d96SSean Paul
13270b8b059aSSean Paul ret = analogix_dp_prepare_panel(dp, true, true);
13280b8b059aSSean Paul if (ret)
13290b8b059aSSean Paul DRM_ERROR("failed to setup the panel ret = %d\n", ret);
13300b8b059aSSean Paul }
13310b8b059aSSean Paul
analogix_dp_set_bridge(struct analogix_dp_device * dp)13328a335736Szain wang static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
13333424e3a4SYakir Yang {
13348a335736Szain wang int ret;
13353424e3a4SYakir Yang
13363424e3a4SYakir Yang pm_runtime_get_sync(dp->dev);
13373424e3a4SYakir Yang
133863872659SLin Huang ret = clk_prepare_enable(dp->clock);
133963872659SLin Huang if (ret < 0) {
134063872659SLin Huang DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret);
134163872659SLin Huang goto out_dp_clk_pre;
134263872659SLin Huang }
134363872659SLin Huang
13447bb3bb4dSDouglas Anderson if (dp->plat_data->power_on_start)
13457bb3bb4dSDouglas Anderson dp->plat_data->power_on_start(dp->plat_data);
13463424e3a4SYakir Yang
13473424e3a4SYakir Yang phy_power_on(dp->phy);
13483424e3a4SYakir Yang
13498a335736Szain wang ret = analogix_dp_init_dp(dp);
13508a335736Szain wang if (ret)
13518a335736Szain wang goto out_dp_init;
13528a335736Szain wang
13537f641414Szain wang /*
13547f641414Szain wang * According to DP spec v1.3 chap 3.5.1.2 Link Training,
13557f641414Szain wang * We should first make sure the HPD signal is asserted high by device
13567f641414Szain wang * when we want to establish a link with it.
13577f641414Szain wang */
13587f641414Szain wang ret = analogix_dp_detect_hpd(dp);
13597f641414Szain wang if (ret) {
13607f641414Szain wang DRM_ERROR("failed to get hpd single ret = %d\n", ret);
13617f641414Szain wang goto out_dp_init;
13627f641414Szain wang }
13637f641414Szain wang
13648a335736Szain wang ret = analogix_dp_commit(dp);
1365ccdc578bSLin Huang if (ret) {
1366ccdc578bSLin Huang DRM_ERROR("dp commit error, ret = %d\n", ret);
13678a335736Szain wang goto out_dp_init;
1368ccdc578bSLin Huang }
13698a335736Szain wang
13707bb3bb4dSDouglas Anderson if (dp->plat_data->power_on_end)
13717bb3bb4dSDouglas Anderson dp->plat_data->power_on_end(dp->plat_data);
13727bb3bb4dSDouglas Anderson
13738a335736Szain wang enable_irq(dp->irq);
13748a335736Szain wang return 0;
13758a335736Szain wang
13768a335736Szain wang out_dp_init:
13778a335736Szain wang phy_power_off(dp->phy);
13788a335736Szain wang if (dp->plat_data->power_off)
13798a335736Szain wang dp->plat_data->power_off(dp->plat_data);
138063872659SLin Huang clk_disable_unprepare(dp->clock);
138163872659SLin Huang out_dp_clk_pre:
13828a335736Szain wang pm_runtime_put_sync(dp->dev);
13838a335736Szain wang
13848a335736Szain wang return ret;
13858a335736Szain wang }
13868a335736Szain wang
138741cf5712SBoris Brezillon static void
analogix_dp_bridge_atomic_enable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)138841cf5712SBoris Brezillon analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
138941cf5712SBoris Brezillon struct drm_bridge_state *old_bridge_state)
13908a335736Szain wang {
139141cf5712SBoris Brezillon struct drm_atomic_state *old_state = old_bridge_state->base.state;
13928a335736Szain wang struct analogix_dp_device *dp = bridge->driver_private;
13936c836d96SSean Paul struct drm_crtc *crtc;
13946c836d96SSean Paul struct drm_crtc_state *old_crtc_state;
13958a335736Szain wang int timeout_loop = 0;
13966c836d96SSean Paul int ret;
13976c836d96SSean Paul
139841cf5712SBoris Brezillon crtc = analogix_dp_get_new_crtc(dp, old_state);
13996c836d96SSean Paul if (!crtc)
14006c836d96SSean Paul return;
14016c836d96SSean Paul
140241cf5712SBoris Brezillon old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
14036c836d96SSean Paul /* Not a full enable, just disable PSR and continue */
14046c836d96SSean Paul if (old_crtc_state && old_crtc_state->self_refresh_active) {
14056c836d96SSean Paul ret = analogix_dp_disable_psr(dp);
14066c836d96SSean Paul if (ret)
14076c836d96SSean Paul DRM_ERROR("Failed to disable psr %d\n", ret);
14086c836d96SSean Paul return;
14096c836d96SSean Paul }
14108a335736Szain wang
14118a335736Szain wang if (dp->dpms_mode == DRM_MODE_DPMS_ON)
14128a335736Szain wang return;
14138a335736Szain wang
14148a335736Szain wang while (timeout_loop < MAX_PLL_LOCK_LOOP) {
14158a335736Szain wang if (analogix_dp_set_bridge(dp) == 0) {
14163424e3a4SYakir Yang dp->dpms_mode = DRM_MODE_DPMS_ON;
14178a335736Szain wang return;
14188a335736Szain wang }
14198a335736Szain wang dev_err(dp->dev, "failed to set bridge, retry: %d\n",
14208a335736Szain wang timeout_loop);
14218a335736Szain wang timeout_loop++;
14228a335736Szain wang usleep_range(10, 11);
14238a335736Szain wang }
14248a335736Szain wang dev_err(dp->dev, "too many times retry set bridge, give it up\n");
14253424e3a4SYakir Yang }
14263424e3a4SYakir Yang
analogix_dp_bridge_disable(struct drm_bridge * bridge)14273424e3a4SYakir Yang static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
14283424e3a4SYakir Yang {
14293424e3a4SYakir Yang struct analogix_dp_device *dp = bridge->driver_private;
14300b8b059aSSean Paul int ret;
14313424e3a4SYakir Yang
14323424e3a4SYakir Yang if (dp->dpms_mode != DRM_MODE_DPMS_ON)
14333424e3a4SYakir Yang return;
14343424e3a4SYakir Yang
14353424e3a4SYakir Yang if (dp->plat_data->panel) {
14363424e3a4SYakir Yang if (drm_panel_disable(dp->plat_data->panel)) {
14373424e3a4SYakir Yang DRM_ERROR("failed to disable the panel\n");
14383424e3a4SYakir Yang return;
14393424e3a4SYakir Yang }
14403424e3a4SYakir Yang }
14413424e3a4SYakir Yang
14423424e3a4SYakir Yang disable_irq(dp->irq);
14433424e3a4SYakir Yang
14443424e3a4SYakir Yang if (dp->plat_data->power_off)
14453424e3a4SYakir Yang dp->plat_data->power_off(dp->plat_data);
14463424e3a4SYakir Yang
14472f8d2160SDouglas Anderson analogix_dp_set_analog_power_down(dp, POWER_ALL, 1);
14482f8d2160SDouglas Anderson phy_power_off(dp->phy);
14492f8d2160SDouglas Anderson
145063872659SLin Huang clk_disable_unprepare(dp->clock);
145163872659SLin Huang
14523424e3a4SYakir Yang pm_runtime_put_sync(dp->dev);
14533424e3a4SYakir Yang
14540b8b059aSSean Paul ret = analogix_dp_prepare_panel(dp, false, true);
14550b8b059aSSean Paul if (ret)
14560b8b059aSSean Paul DRM_ERROR("failed to setup the panel ret = %d\n", ret);
14570b8b059aSSean Paul
14587ba8fb57Szain wang dp->fast_train_enable = false;
14596c836d96SSean Paul dp->psr_supported = false;
14603424e3a4SYakir Yang dp->dpms_mode = DRM_MODE_DPMS_OFF;
14613424e3a4SYakir Yang }
14623424e3a4SYakir Yang
146341cf5712SBoris Brezillon static void
analogix_dp_bridge_atomic_disable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)146441cf5712SBoris Brezillon analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
146541cf5712SBoris Brezillon struct drm_bridge_state *old_bridge_state)
14666c836d96SSean Paul {
146741cf5712SBoris Brezillon struct drm_atomic_state *old_state = old_bridge_state->base.state;
14686c836d96SSean Paul struct analogix_dp_device *dp = bridge->driver_private;
1469ca871659SBrian Norris struct drm_crtc *old_crtc, *new_crtc;
1470ca871659SBrian Norris struct drm_crtc_state *old_crtc_state = NULL;
14716c836d96SSean Paul struct drm_crtc_state *new_crtc_state = NULL;
1472ca871659SBrian Norris int ret;
14736c836d96SSean Paul
1474ca871659SBrian Norris new_crtc = analogix_dp_get_new_crtc(dp, old_state);
1475ca871659SBrian Norris if (!new_crtc)
14766c836d96SSean Paul goto out;
14776c836d96SSean Paul
1478ca871659SBrian Norris new_crtc_state = drm_atomic_get_new_crtc_state(old_state, new_crtc);
14796c836d96SSean Paul if (!new_crtc_state)
14806c836d96SSean Paul goto out;
14816c836d96SSean Paul
14826c836d96SSean Paul /* Don't do a full disable on PSR transitions */
14836c836d96SSean Paul if (new_crtc_state->self_refresh_active)
14846c836d96SSean Paul return;
14856c836d96SSean Paul
14866c836d96SSean Paul out:
1487ca871659SBrian Norris old_crtc = analogix_dp_get_old_crtc(dp, old_state);
1488ca871659SBrian Norris if (old_crtc) {
1489ca871659SBrian Norris old_crtc_state = drm_atomic_get_old_crtc_state(old_state,
1490ca871659SBrian Norris old_crtc);
1491ca871659SBrian Norris
1492ca871659SBrian Norris /* When moving from PSR to fully disabled, exit PSR first. */
1493ca871659SBrian Norris if (old_crtc_state && old_crtc_state->self_refresh_active) {
1494ca871659SBrian Norris ret = analogix_dp_disable_psr(dp);
1495ca871659SBrian Norris if (ret)
1496ca871659SBrian Norris DRM_ERROR("Failed to disable psr (%d)\n", ret);
1497ca871659SBrian Norris }
1498ca871659SBrian Norris }
1499ca871659SBrian Norris
15006c836d96SSean Paul analogix_dp_bridge_disable(bridge);
15016c836d96SSean Paul }
15026c836d96SSean Paul
150341cf5712SBoris Brezillon static void
analogix_dp_bridge_atomic_post_disable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)150441cf5712SBoris Brezillon analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
150541cf5712SBoris Brezillon struct drm_bridge_state *old_bridge_state)
15066c836d96SSean Paul {
150741cf5712SBoris Brezillon struct drm_atomic_state *old_state = old_bridge_state->base.state;
15086c836d96SSean Paul struct analogix_dp_device *dp = bridge->driver_private;
15096c836d96SSean Paul struct drm_crtc *crtc;
15106c836d96SSean Paul struct drm_crtc_state *new_crtc_state;
15116c836d96SSean Paul int ret;
15126c836d96SSean Paul
151341cf5712SBoris Brezillon crtc = analogix_dp_get_new_crtc(dp, old_state);
15146c836d96SSean Paul if (!crtc)
15156c836d96SSean Paul return;
15166c836d96SSean Paul
151741cf5712SBoris Brezillon new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
15186c836d96SSean Paul if (!new_crtc_state || !new_crtc_state->self_refresh_active)
15196c836d96SSean Paul return;
15206c836d96SSean Paul
15216c836d96SSean Paul ret = analogix_dp_enable_psr(dp);
15226c836d96SSean Paul if (ret)
15236c836d96SSean Paul DRM_ERROR("Failed to enable psr (%d)\n", ret);
15246c836d96SSean Paul }
15256c836d96SSean Paul
analogix_dp_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * orig_mode,const struct drm_display_mode * mode)1526793ce4ebSYakir Yang static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
152763f8f3baSLaurent Pinchart const struct drm_display_mode *orig_mode,
152863f8f3baSLaurent Pinchart const struct drm_display_mode *mode)
1529793ce4ebSYakir Yang {
1530793ce4ebSYakir Yang struct analogix_dp_device *dp = bridge->driver_private;
1531793ce4ebSYakir Yang struct drm_display_info *display_info = &dp->connector.display_info;
1532793ce4ebSYakir Yang struct video_info *video = &dp->video_info;
1533793ce4ebSYakir Yang struct device_node *dp_node = dp->dev->of_node;
1534793ce4ebSYakir Yang int vic;
1535793ce4ebSYakir Yang
1536793ce4ebSYakir Yang /* Input video interlaces & hsync pol & vsync pol */
1537793ce4ebSYakir Yang video->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
1538793ce4ebSYakir Yang video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
1539793ce4ebSYakir Yang video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
1540793ce4ebSYakir Yang
1541793ce4ebSYakir Yang /* Input video dynamic_range & colorimetry */
1542793ce4ebSYakir Yang vic = drm_match_cea_mode(mode);
1543793ce4ebSYakir Yang if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) ||
1544793ce4ebSYakir Yang (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) {
1545793ce4ebSYakir Yang video->dynamic_range = CEA;
1546793ce4ebSYakir Yang video->ycbcr_coeff = COLOR_YCBCR601;
1547793ce4ebSYakir Yang } else if (vic) {
1548793ce4ebSYakir Yang video->dynamic_range = CEA;
1549793ce4ebSYakir Yang video->ycbcr_coeff = COLOR_YCBCR709;
1550793ce4ebSYakir Yang } else {
1551793ce4ebSYakir Yang video->dynamic_range = VESA;
1552793ce4ebSYakir Yang video->ycbcr_coeff = COLOR_YCBCR709;
1553793ce4ebSYakir Yang }
1554793ce4ebSYakir Yang
1555793ce4ebSYakir Yang /* Input vide bpc and color_formats */
1556793ce4ebSYakir Yang switch (display_info->bpc) {
1557793ce4ebSYakir Yang case 12:
1558793ce4ebSYakir Yang video->color_depth = COLOR_12;
1559793ce4ebSYakir Yang break;
1560793ce4ebSYakir Yang case 10:
1561793ce4ebSYakir Yang video->color_depth = COLOR_10;
1562793ce4ebSYakir Yang break;
1563793ce4ebSYakir Yang case 8:
1564793ce4ebSYakir Yang video->color_depth = COLOR_8;
1565793ce4ebSYakir Yang break;
1566793ce4ebSYakir Yang case 6:
1567793ce4ebSYakir Yang video->color_depth = COLOR_6;
1568793ce4ebSYakir Yang break;
1569793ce4ebSYakir Yang default:
1570793ce4ebSYakir Yang video->color_depth = COLOR_8;
1571793ce4ebSYakir Yang break;
1572793ce4ebSYakir Yang }
1573c03d0b52SMaxime Ripard if (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
1574793ce4ebSYakir Yang video->color_space = COLOR_YCBCR444;
1575c03d0b52SMaxime Ripard else if (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
1576793ce4ebSYakir Yang video->color_space = COLOR_YCBCR422;
1577793ce4ebSYakir Yang else
1578793ce4ebSYakir Yang video->color_space = COLOR_RGB;
1579793ce4ebSYakir Yang
1580793ce4ebSYakir Yang /*
1581793ce4ebSYakir Yang * NOTE: those property parsing code is used for providing backward
1582793ce4ebSYakir Yang * compatibility for samsung platform.
1583793ce4ebSYakir Yang * Due to we used the "of_property_read_u32" interfaces, when this
1584793ce4ebSYakir Yang * property isn't present, the "video_info" can keep the original
1585793ce4ebSYakir Yang * values and wouldn't be modified.
1586793ce4ebSYakir Yang */
1587793ce4ebSYakir Yang of_property_read_u32(dp_node, "samsung,color-space",
1588793ce4ebSYakir Yang &video->color_space);
1589793ce4ebSYakir Yang of_property_read_u32(dp_node, "samsung,dynamic-range",
1590793ce4ebSYakir Yang &video->dynamic_range);
1591793ce4ebSYakir Yang of_property_read_u32(dp_node, "samsung,ycbcr-coeff",
1592793ce4ebSYakir Yang &video->ycbcr_coeff);
1593793ce4ebSYakir Yang of_property_read_u32(dp_node, "samsung,color-depth",
1594793ce4ebSYakir Yang &video->color_depth);
1595793ce4ebSYakir Yang if (of_property_read_bool(dp_node, "hsync-active-high"))
1596793ce4ebSYakir Yang video->h_sync_polarity = true;
1597793ce4ebSYakir Yang if (of_property_read_bool(dp_node, "vsync-active-high"))
1598793ce4ebSYakir Yang video->v_sync_polarity = true;
1599793ce4ebSYakir Yang if (of_property_read_bool(dp_node, "interlaced"))
1600793ce4ebSYakir Yang video->interlaced = true;
1601793ce4ebSYakir Yang }
1602793ce4ebSYakir Yang
16033424e3a4SYakir Yang static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
1604d9aad8c2SBoris Brezillon .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
1605d9aad8c2SBoris Brezillon .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
1606d9aad8c2SBoris Brezillon .atomic_reset = drm_atomic_helper_bridge_reset,
16076c836d96SSean Paul .atomic_pre_enable = analogix_dp_bridge_atomic_pre_enable,
16086c836d96SSean Paul .atomic_enable = analogix_dp_bridge_atomic_enable,
16096c836d96SSean Paul .atomic_disable = analogix_dp_bridge_atomic_disable,
16106c836d96SSean Paul .atomic_post_disable = analogix_dp_bridge_atomic_post_disable,
1611793ce4ebSYakir Yang .mode_set = analogix_dp_bridge_mode_set,
16123424e3a4SYakir Yang .attach = analogix_dp_bridge_attach,
16133424e3a4SYakir Yang };
16143424e3a4SYakir Yang
analogix_dp_create_bridge(struct drm_device * drm_dev,struct analogix_dp_device * dp)16153424e3a4SYakir Yang static int analogix_dp_create_bridge(struct drm_device *drm_dev,
16163424e3a4SYakir Yang struct analogix_dp_device *dp)
16173424e3a4SYakir Yang {
16183424e3a4SYakir Yang struct drm_bridge *bridge;
16193424e3a4SYakir Yang
16203424e3a4SYakir Yang bridge = devm_kzalloc(drm_dev->dev, sizeof(*bridge), GFP_KERNEL);
16213424e3a4SYakir Yang if (!bridge) {
16223424e3a4SYakir Yang DRM_ERROR("failed to allocate for drm bridge\n");
16233424e3a4SYakir Yang return -ENOMEM;
16243424e3a4SYakir Yang }
16253424e3a4SYakir Yang
16263424e3a4SYakir Yang dp->bridge = bridge;
16273424e3a4SYakir Yang
16283424e3a4SYakir Yang bridge->driver_private = dp;
16293424e3a4SYakir Yang bridge->funcs = &analogix_dp_bridge_funcs;
16303424e3a4SYakir Yang
1631fb8d617fSLaurent Pinchart return drm_bridge_attach(dp->encoder, bridge, NULL, 0);
16323424e3a4SYakir Yang }
16333424e3a4SYakir Yang
analogix_dp_dt_parse_pdata(struct analogix_dp_device * dp)1634793ce4ebSYakir Yang static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
16353424e3a4SYakir Yang {
1636793ce4ebSYakir Yang struct device_node *dp_node = dp->dev->of_node;
1637793ce4ebSYakir Yang struct video_info *video_info = &dp->video_info;
16383424e3a4SYakir Yang
16390d0abd89SYakir Yang switch (dp->plat_data->dev_type) {
16400d0abd89SYakir Yang case RK3288_DP:
164182872e42SYakir Yang case RK3399_EDP:
16420d0abd89SYakir Yang /*
16430d0abd89SYakir Yang * Like Rk3288 DisplayPort TRM indicate that "Main link
16440d0abd89SYakir Yang * containing 4 physical lanes of 2.7/1.62 Gbps/lane".
16450d0abd89SYakir Yang */
16460d0abd89SYakir Yang video_info->max_link_rate = 0x0A;
16470d0abd89SYakir Yang video_info->max_lane_count = 0x04;
16480d0abd89SYakir Yang break;
16490d0abd89SYakir Yang case EXYNOS_DP:
16500d0abd89SYakir Yang /*
16510d0abd89SYakir Yang * NOTE: those property parseing code is used for
16520d0abd89SYakir Yang * providing backward compatibility for samsung platform.
16530d0abd89SYakir Yang */
16540d0abd89SYakir Yang of_property_read_u32(dp_node, "samsung,link-rate",
16550d0abd89SYakir Yang &video_info->max_link_rate);
16560d0abd89SYakir Yang of_property_read_u32(dp_node, "samsung,lane-count",
16570d0abd89SYakir Yang &video_info->max_lane_count);
16580d0abd89SYakir Yang break;
16593424e3a4SYakir Yang }
16603424e3a4SYakir Yang
1661793ce4ebSYakir Yang return 0;
16623424e3a4SYakir Yang }
16633424e3a4SYakir Yang
analogix_dpaux_transfer(struct drm_dp_aux * aux,struct drm_dp_aux_msg * msg)16640d97ad03STomeu Vizoso static ssize_t analogix_dpaux_transfer(struct drm_dp_aux *aux,
16650d97ad03STomeu Vizoso struct drm_dp_aux_msg *msg)
16660d97ad03STomeu Vizoso {
16670d97ad03STomeu Vizoso struct analogix_dp_device *dp = to_dp(aux);
16688fb6c44fSBrian Norris int ret;
16690d97ad03STomeu Vizoso
16708fb6c44fSBrian Norris pm_runtime_get_sync(dp->dev);
16718fb6c44fSBrian Norris
16728fb6c44fSBrian Norris ret = analogix_dp_detect_hpd(dp);
16738fb6c44fSBrian Norris if (ret)
16748fb6c44fSBrian Norris goto out;
16758fb6c44fSBrian Norris
16768fb6c44fSBrian Norris ret = analogix_dp_transfer(dp, msg);
16778fb6c44fSBrian Norris out:
1678f28dd507SBrian Norris pm_runtime_mark_last_busy(dp->dev);
1679f28dd507SBrian Norris pm_runtime_put_autosuspend(dp->dev);
16808fb6c44fSBrian Norris
16818fb6c44fSBrian Norris return ret;
16820d97ad03STomeu Vizoso }
16830d97ad03STomeu Vizoso
16846b2d8fd9SJeffy Chen struct analogix_dp_device *
analogix_dp_probe(struct device * dev,struct analogix_dp_plat_data * plat_data)1685152cce00SMarek Szyprowski analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data)
16863424e3a4SYakir Yang {
16873424e3a4SYakir Yang struct platform_device *pdev = to_platform_device(dev);
16883424e3a4SYakir Yang struct analogix_dp_device *dp;
16893424e3a4SYakir Yang struct resource *res;
16903424e3a4SYakir Yang unsigned int irq_flags;
16913424e3a4SYakir Yang int ret;
16923424e3a4SYakir Yang
16933424e3a4SYakir Yang if (!plat_data) {
16943424e3a4SYakir Yang dev_err(dev, "Invalided input plat_data\n");
16956b2d8fd9SJeffy Chen return ERR_PTR(-EINVAL);
16963424e3a4SYakir Yang }
16973424e3a4SYakir Yang
16983424e3a4SYakir Yang dp = devm_kzalloc(dev, sizeof(struct analogix_dp_device), GFP_KERNEL);
16993424e3a4SYakir Yang if (!dp)
17006b2d8fd9SJeffy Chen return ERR_PTR(-ENOMEM);
17013424e3a4SYakir Yang
17023424e3a4SYakir Yang dp->dev = &pdev->dev;
17033424e3a4SYakir Yang dp->dpms_mode = DRM_MODE_DPMS_OFF;
17043424e3a4SYakir Yang
17050b8b059aSSean Paul mutex_init(&dp->panel_lock);
17060b8b059aSSean Paul dp->panel_is_modeset = false;
17070b8b059aSSean Paul
17083424e3a4SYakir Yang /*
17093424e3a4SYakir Yang * platform dp driver need containor_of the plat_data to get
17103424e3a4SYakir Yang * the driver private data, so we need to store the point of
17113424e3a4SYakir Yang * plat_data, not the context of plat_data.
17123424e3a4SYakir Yang */
17133424e3a4SYakir Yang dp->plat_data = plat_data;
17143424e3a4SYakir Yang
1715793ce4ebSYakir Yang ret = analogix_dp_dt_parse_pdata(dp);
1716793ce4ebSYakir Yang if (ret)
17176b2d8fd9SJeffy Chen return ERR_PTR(ret);
17183424e3a4SYakir Yang
17193424e3a4SYakir Yang dp->phy = devm_phy_get(dp->dev, "dp");
17203424e3a4SYakir Yang if (IS_ERR(dp->phy)) {
17213424e3a4SYakir Yang dev_err(dp->dev, "no DP phy configured\n");
17223424e3a4SYakir Yang ret = PTR_ERR(dp->phy);
17233424e3a4SYakir Yang if (ret) {
17243424e3a4SYakir Yang /*
17253424e3a4SYakir Yang * phy itself is not enabled, so we can move forward
17263424e3a4SYakir Yang * assigning NULL to phy pointer.
17273424e3a4SYakir Yang */
17283424e3a4SYakir Yang if (ret == -ENOSYS || ret == -ENODEV)
17293424e3a4SYakir Yang dp->phy = NULL;
17303424e3a4SYakir Yang else
17316b2d8fd9SJeffy Chen return ERR_PTR(ret);
17323424e3a4SYakir Yang }
17333424e3a4SYakir Yang }
17343424e3a4SYakir Yang
17353424e3a4SYakir Yang dp->clock = devm_clk_get(&pdev->dev, "dp");
17363424e3a4SYakir Yang if (IS_ERR(dp->clock)) {
17373424e3a4SYakir Yang dev_err(&pdev->dev, "failed to get clock\n");
17386b2d8fd9SJeffy Chen return ERR_CAST(dp->clock);
17393424e3a4SYakir Yang }
17403424e3a4SYakir Yang
17413424e3a4SYakir Yang clk_prepare_enable(dp->clock);
17423424e3a4SYakir Yang
17433424e3a4SYakir Yang res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
17443424e3a4SYakir Yang
17453424e3a4SYakir Yang dp->reg_base = devm_ioremap_resource(&pdev->dev, res);
17469f15930bSMiaoqian Lin if (IS_ERR(dp->reg_base)) {
17479f15930bSMiaoqian Lin ret = PTR_ERR(dp->reg_base);
17489f15930bSMiaoqian Lin goto err_disable_clk;
17499f15930bSMiaoqian Lin }
17503424e3a4SYakir Yang
17515cff007cSYakir Yang dp->force_hpd = of_property_read_bool(dev->of_node, "force-hpd");
17525cff007cSYakir Yang
17535b038dcfSLinus Walleij /* Try two different names */
17545b038dcfSLinus Walleij dp->hpd_gpiod = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
17555b038dcfSLinus Walleij if (!dp->hpd_gpiod)
17565b038dcfSLinus Walleij dp->hpd_gpiod = devm_gpiod_get_optional(dev, "samsung,hpd",
17575b038dcfSLinus Walleij GPIOD_IN);
17585b038dcfSLinus Walleij if (IS_ERR(dp->hpd_gpiod)) {
17595b038dcfSLinus Walleij dev_err(dev, "error getting HDP GPIO: %ld\n",
17605b038dcfSLinus Walleij PTR_ERR(dp->hpd_gpiod));
17619f15930bSMiaoqian Lin ret = PTR_ERR(dp->hpd_gpiod);
17629f15930bSMiaoqian Lin goto err_disable_clk;
17635b038dcfSLinus Walleij }
17643424e3a4SYakir Yang
17655b038dcfSLinus Walleij if (dp->hpd_gpiod) {
17663424e3a4SYakir Yang /*
17673424e3a4SYakir Yang * Set up the hotplug GPIO from the device tree as an interrupt.
17683424e3a4SYakir Yang * Simply specifying a different interrupt in the device tree
17693424e3a4SYakir Yang * doesn't work since we handle hotplug rather differently when
17703424e3a4SYakir Yang * using a GPIO. We also need the actual GPIO specifier so
17713424e3a4SYakir Yang * that we can get the current state of the GPIO.
17723424e3a4SYakir Yang */
17735b038dcfSLinus Walleij dp->irq = gpiod_to_irq(dp->hpd_gpiod);
17743424e3a4SYakir Yang irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
17753424e3a4SYakir Yang } else {
17763424e3a4SYakir Yang dp->irq = platform_get_irq(pdev, 0);
17773424e3a4SYakir Yang irq_flags = 0;
17783424e3a4SYakir Yang }
17793424e3a4SYakir Yang
17803424e3a4SYakir Yang if (dp->irq == -ENXIO) {
17813424e3a4SYakir Yang dev_err(&pdev->dev, "failed to get irq\n");
17829f15930bSMiaoqian Lin ret = -ENODEV;
17839f15930bSMiaoqian Lin goto err_disable_clk;
17843424e3a4SYakir Yang }
17853424e3a4SYakir Yang
17867b4b7a8dSYakir Yang ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
17877b4b7a8dSYakir Yang analogix_dp_hardirq,
17887b4b7a8dSYakir Yang analogix_dp_irq_thread,
17893424e3a4SYakir Yang irq_flags, "analogix-dp", dp);
17903424e3a4SYakir Yang if (ret) {
17913424e3a4SYakir Yang dev_err(&pdev->dev, "failed to request irq\n");
17929f15930bSMiaoqian Lin goto err_disable_clk;
17933424e3a4SYakir Yang }
17943424e3a4SYakir Yang disable_irq(dp->irq);
17953424e3a4SYakir Yang
1796152cce00SMarek Szyprowski return dp;
17979f15930bSMiaoqian Lin
17989f15930bSMiaoqian Lin err_disable_clk:
17999f15930bSMiaoqian Lin clk_disable_unprepare(dp->clock);
18009f15930bSMiaoqian Lin return ERR_PTR(ret);
1801152cce00SMarek Szyprowski }
1802152cce00SMarek Szyprowski EXPORT_SYMBOL_GPL(analogix_dp_probe);
1803152cce00SMarek Szyprowski
analogix_dp_bind(struct analogix_dp_device * dp,struct drm_device * drm_dev)1804152cce00SMarek Szyprowski int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev)
1805152cce00SMarek Szyprowski {
1806152cce00SMarek Szyprowski int ret;
1807152cce00SMarek Szyprowski
18083424e3a4SYakir Yang dp->drm_dev = drm_dev;
18093424e3a4SYakir Yang dp->encoder = dp->plat_data->encoder;
18103424e3a4SYakir Yang
18110d97ad03STomeu Vizoso dp->aux.name = "DP-AUX";
18120d97ad03STomeu Vizoso dp->aux.transfer = analogix_dpaux_transfer;
1813152cce00SMarek Szyprowski dp->aux.dev = dp->dev;
18146cba3fe4SLyude Paul dp->aux.drm_dev = drm_dev;
18150d97ad03STomeu Vizoso
18160d97ad03STomeu Vizoso ret = drm_dp_aux_register(&dp->aux);
18170d97ad03STomeu Vizoso if (ret)
1818152cce00SMarek Szyprowski return ret;
1819f25c8358SMarek Szyprowski
1820f28dd507SBrian Norris pm_runtime_use_autosuspend(dp->dev);
1821f28dd507SBrian Norris pm_runtime_set_autosuspend_delay(dp->dev, 100);
1822152cce00SMarek Szyprowski pm_runtime_enable(dp->dev);
18230d97ad03STomeu Vizoso
18243424e3a4SYakir Yang ret = analogix_dp_create_bridge(drm_dev, dp);
18253424e3a4SYakir Yang if (ret) {
18263424e3a4SYakir Yang DRM_ERROR("failed to create bridge (%d)\n", ret);
18273424e3a4SYakir Yang goto err_disable_pm_runtime;
18283424e3a4SYakir Yang }
18293424e3a4SYakir Yang
1830152cce00SMarek Szyprowski return 0;
18313424e3a4SYakir Yang
18323424e3a4SYakir Yang err_disable_pm_runtime:
1833f28dd507SBrian Norris pm_runtime_dont_use_autosuspend(dp->dev);
1834152cce00SMarek Szyprowski pm_runtime_disable(dp->dev);
1835b3bdf89cSLyude Paul drm_dp_aux_unregister(&dp->aux);
1836f0a8b49cSMarek Szyprowski
1837152cce00SMarek Szyprowski return ret;
18383424e3a4SYakir Yang }
18393424e3a4SYakir Yang EXPORT_SYMBOL_GPL(analogix_dp_bind);
18403424e3a4SYakir Yang
analogix_dp_unbind(struct analogix_dp_device * dp)18416b2d8fd9SJeffy Chen void analogix_dp_unbind(struct analogix_dp_device *dp)
18423424e3a4SYakir Yang {
18433424e3a4SYakir Yang analogix_dp_bridge_disable(dp->bridge);
184437e04877SJeffy Chen dp->connector.funcs->destroy(&dp->connector);
18452b77a291SYakir Yang
18462b77a291SYakir Yang if (dp->plat_data->panel) {
18472b77a291SYakir Yang if (drm_panel_unprepare(dp->plat_data->panel))
18482b77a291SYakir Yang DRM_ERROR("failed to turnoff the panel\n");
18492b77a291SYakir Yang }
18502b77a291SYakir Yang
18517b017a58SJeffy Chen drm_dp_aux_unregister(&dp->aux);
1852f28dd507SBrian Norris pm_runtime_dont_use_autosuspend(dp->dev);
18536b2d8fd9SJeffy Chen pm_runtime_disable(dp->dev);
18543424e3a4SYakir Yang }
18553424e3a4SYakir Yang EXPORT_SYMBOL_GPL(analogix_dp_unbind);
18563424e3a4SYakir Yang
analogix_dp_remove(struct analogix_dp_device * dp)1857152cce00SMarek Szyprowski void analogix_dp_remove(struct analogix_dp_device *dp)
1858152cce00SMarek Szyprowski {
1859152cce00SMarek Szyprowski clk_disable_unprepare(dp->clock);
1860152cce00SMarek Szyprowski }
1861152cce00SMarek Szyprowski EXPORT_SYMBOL_GPL(analogix_dp_remove);
1862152cce00SMarek Szyprowski
18633424e3a4SYakir Yang #ifdef CONFIG_PM
analogix_dp_suspend(struct analogix_dp_device * dp)18646b2d8fd9SJeffy Chen int analogix_dp_suspend(struct analogix_dp_device *dp)
18653424e3a4SYakir Yang {
18663424e3a4SYakir Yang clk_disable_unprepare(dp->clock);
18673424e3a4SYakir Yang return 0;
18683424e3a4SYakir Yang }
18693424e3a4SYakir Yang EXPORT_SYMBOL_GPL(analogix_dp_suspend);
18703424e3a4SYakir Yang
analogix_dp_resume(struct analogix_dp_device * dp)18716b2d8fd9SJeffy Chen int analogix_dp_resume(struct analogix_dp_device *dp)
18723424e3a4SYakir Yang {
18733424e3a4SYakir Yang int ret;
18743424e3a4SYakir Yang
18753424e3a4SYakir Yang ret = clk_prepare_enable(dp->clock);
18763424e3a4SYakir Yang if (ret < 0) {
18773424e3a4SYakir Yang DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret);
18783424e3a4SYakir Yang return ret;
18793424e3a4SYakir Yang }
18803424e3a4SYakir Yang
18813424e3a4SYakir Yang return 0;
18823424e3a4SYakir Yang }
18833424e3a4SYakir Yang EXPORT_SYMBOL_GPL(analogix_dp_resume);
18843424e3a4SYakir Yang #endif
18853424e3a4SYakir Yang
analogix_dp_start_crc(struct drm_connector * connector)1886737d6e33STomeu Vizoso int analogix_dp_start_crc(struct drm_connector *connector)
1887737d6e33STomeu Vizoso {
1888737d6e33STomeu Vizoso struct analogix_dp_device *dp = to_dp(connector);
1889737d6e33STomeu Vizoso
1890737d6e33STomeu Vizoso if (!connector->state->crtc) {
1891737d6e33STomeu Vizoso DRM_ERROR("Connector %s doesn't currently have a CRTC.\n",
1892737d6e33STomeu Vizoso connector->name);
1893737d6e33STomeu Vizoso return -EINVAL;
1894737d6e33STomeu Vizoso }
1895737d6e33STomeu Vizoso
1896737d6e33STomeu Vizoso return drm_dp_start_crc(&dp->aux, connector->state->crtc);
1897737d6e33STomeu Vizoso }
1898737d6e33STomeu Vizoso EXPORT_SYMBOL_GPL(analogix_dp_start_crc);
1899737d6e33STomeu Vizoso
analogix_dp_stop_crc(struct drm_connector * connector)1900737d6e33STomeu Vizoso int analogix_dp_stop_crc(struct drm_connector *connector)
1901737d6e33STomeu Vizoso {
1902737d6e33STomeu Vizoso struct analogix_dp_device *dp = to_dp(connector);
1903737d6e33STomeu Vizoso
1904737d6e33STomeu Vizoso return drm_dp_stop_crc(&dp->aux);
1905737d6e33STomeu Vizoso }
1906737d6e33STomeu Vizoso EXPORT_SYMBOL_GPL(analogix_dp_stop_crc);
1907737d6e33STomeu Vizoso
19083424e3a4SYakir Yang MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
19093424e3a4SYakir Yang MODULE_DESCRIPTION("Analogix DP Core Driver");
19103424e3a4SYakir Yang MODULE_LICENSE("GPL v2");
1911