159dd5aa8SSimon Glass /* 259dd5aa8SSimon Glass * Copyright (c) 2011-2013, NVIDIA Corporation. 359dd5aa8SSimon Glass * Copyright 2014 Google Inc. 459dd5aa8SSimon Glass * 559dd5aa8SSimon Glass * SPDX-License-Identifier: GPL-2.0 659dd5aa8SSimon Glass */ 759dd5aa8SSimon Glass 859dd5aa8SSimon Glass #include <common.h> 92dcf1433SSimon Glass #include <display.h> 1059dd5aa8SSimon Glass #include <dm.h> 1159dd5aa8SSimon Glass #include <div64.h> 1259dd5aa8SSimon Glass #include <errno.h> 1359dd5aa8SSimon Glass #include <fdtdec.h> 14*d7659212SSimon Glass #include <video_bridge.h> 1559dd5aa8SSimon Glass #include <asm/io.h> 1659dd5aa8SSimon Glass #include <asm/arch-tegra/dc.h> 172dcf1433SSimon Glass #include "display.h" 1859dd5aa8SSimon Glass #include "edid.h" 1959dd5aa8SSimon Glass #include "sor.h" 202dcf1433SSimon Glass #include "displayport.h" 2159dd5aa8SSimon Glass 2259dd5aa8SSimon Glass DECLARE_GLOBAL_DATA_PTR; 2359dd5aa8SSimon Glass 24dedc44b4SSimon Glass #define DO_FAST_LINK_TRAINING 1 25dedc44b4SSimon Glass 2659dd5aa8SSimon Glass struct tegra_dp_plat { 2759dd5aa8SSimon Glass ulong base; 2859dd5aa8SSimon Glass }; 2959dd5aa8SSimon Glass 30*d7659212SSimon Glass /** 31*d7659212SSimon Glass * struct tegra_dp_priv - private displayport driver info 32*d7659212SSimon Glass * 33*d7659212SSimon Glass * @dc_dev: Display controller device that is sending the video feed 34*d7659212SSimon Glass */ 3559dd5aa8SSimon Glass struct tegra_dp_priv { 36*d7659212SSimon Glass struct udevice *sor; 37*d7659212SSimon Glass struct udevice *dc_dev; 3859dd5aa8SSimon Glass struct dpaux_ctlr *regs; 3959dd5aa8SSimon Glass u8 revision; 4059dd5aa8SSimon Glass int enabled; 4159dd5aa8SSimon Glass }; 4259dd5aa8SSimon Glass 4359dd5aa8SSimon Glass struct tegra_dp_priv dp_data; 4459dd5aa8SSimon Glass 4559dd5aa8SSimon Glass static inline u32 tegra_dpaux_readl(struct tegra_dp_priv *dp, u32 reg) 4659dd5aa8SSimon Glass { 4759dd5aa8SSimon Glass return readl((u32 *)dp->regs + reg); 4859dd5aa8SSimon Glass } 4959dd5aa8SSimon Glass 5059dd5aa8SSimon Glass static inline void tegra_dpaux_writel(struct tegra_dp_priv *dp, u32 reg, 5159dd5aa8SSimon Glass u32 val) 5259dd5aa8SSimon Glass { 5359dd5aa8SSimon Glass writel(val, (u32 *)dp->regs + reg); 5459dd5aa8SSimon Glass } 5559dd5aa8SSimon Glass 5659dd5aa8SSimon Glass static inline u32 tegra_dc_dpaux_poll_register(struct tegra_dp_priv *dp, 5759dd5aa8SSimon Glass u32 reg, u32 mask, u32 exp_val, 5859dd5aa8SSimon Glass u32 poll_interval_us, 5959dd5aa8SSimon Glass u32 timeout_us) 6059dd5aa8SSimon Glass { 6159dd5aa8SSimon Glass u32 reg_val = 0; 6259dd5aa8SSimon Glass u32 temp = timeout_us; 6359dd5aa8SSimon Glass 6459dd5aa8SSimon Glass do { 6559dd5aa8SSimon Glass udelay(poll_interval_us); 6659dd5aa8SSimon Glass reg_val = tegra_dpaux_readl(dp, reg); 6759dd5aa8SSimon Glass if (timeout_us > poll_interval_us) 6859dd5aa8SSimon Glass timeout_us -= poll_interval_us; 6959dd5aa8SSimon Glass else 7059dd5aa8SSimon Glass break; 7159dd5aa8SSimon Glass } while ((reg_val & mask) != exp_val); 7259dd5aa8SSimon Glass 7359dd5aa8SSimon Glass if ((reg_val & mask) == exp_val) 7459dd5aa8SSimon Glass return 0; /* success */ 7559dd5aa8SSimon Glass debug("dpaux_poll_register 0x%x: timeout: (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n", 7659dd5aa8SSimon Glass reg, reg_val, mask, exp_val); 7759dd5aa8SSimon Glass return temp; 7859dd5aa8SSimon Glass } 7959dd5aa8SSimon Glass 8059dd5aa8SSimon Glass static inline int tegra_dpaux_wait_transaction(struct tegra_dp_priv *dp) 8159dd5aa8SSimon Glass { 8259dd5aa8SSimon Glass /* According to DP spec, each aux transaction needs to finish 8359dd5aa8SSimon Glass within 40ms. */ 8459dd5aa8SSimon Glass if (tegra_dc_dpaux_poll_register(dp, DPAUX_DP_AUXCTL, 8559dd5aa8SSimon Glass DPAUX_DP_AUXCTL_TRANSACTREQ_MASK, 8659dd5aa8SSimon Glass DPAUX_DP_AUXCTL_TRANSACTREQ_DONE, 8759dd5aa8SSimon Glass 100, DP_AUX_TIMEOUT_MS * 1000) != 0) { 8859dd5aa8SSimon Glass debug("dp: DPAUX transaction timeout\n"); 8959dd5aa8SSimon Glass return -1; 9059dd5aa8SSimon Glass } 9159dd5aa8SSimon Glass return 0; 9259dd5aa8SSimon Glass } 9359dd5aa8SSimon Glass 9459dd5aa8SSimon Glass static int tegra_dc_dpaux_write_chunk(struct tegra_dp_priv *dp, u32 cmd, 9559dd5aa8SSimon Glass u32 addr, u8 *data, u32 *size, 9659dd5aa8SSimon Glass u32 *aux_stat) 9759dd5aa8SSimon Glass { 9859dd5aa8SSimon Glass int i; 9959dd5aa8SSimon Glass u32 reg_val; 10059dd5aa8SSimon Glass u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; 10159dd5aa8SSimon Glass u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; 10259dd5aa8SSimon Glass u32 temp_data; 10359dd5aa8SSimon Glass 10459dd5aa8SSimon Glass if (*size > DP_AUX_MAX_BYTES) 10559dd5aa8SSimon Glass return -1; /* only write one chunk of data */ 10659dd5aa8SSimon Glass 10759dd5aa8SSimon Glass /* Make sure the command is write command */ 10859dd5aa8SSimon Glass switch (cmd) { 10959dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_I2CWR: 11059dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_MOTWR: 11159dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_AUXWR: 11259dd5aa8SSimon Glass break; 11359dd5aa8SSimon Glass default: 11459dd5aa8SSimon Glass debug("dp: aux write cmd 0x%x is invalid\n", cmd); 11559dd5aa8SSimon Glass return -EINVAL; 11659dd5aa8SSimon Glass } 11759dd5aa8SSimon Glass 11859dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr); 11959dd5aa8SSimon Glass for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) { 12059dd5aa8SSimon Glass memcpy(&temp_data, data, 4); 12159dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXDATA_WRITE_W(i), temp_data); 12259dd5aa8SSimon Glass data += 4; 12359dd5aa8SSimon Glass } 12459dd5aa8SSimon Glass 12559dd5aa8SSimon Glass reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL); 12659dd5aa8SSimon Glass reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK; 12759dd5aa8SSimon Glass reg_val |= cmd; 12859dd5aa8SSimon Glass reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD; 12959dd5aa8SSimon Glass reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT); 13059dd5aa8SSimon Glass 13159dd5aa8SSimon Glass while ((timeout_retries > 0) && (defer_retries > 0)) { 13259dd5aa8SSimon Glass if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) || 13359dd5aa8SSimon Glass (defer_retries != DP_AUX_DEFER_MAX_TRIES)) 13459dd5aa8SSimon Glass udelay(1); 13559dd5aa8SSimon Glass 13659dd5aa8SSimon Glass reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING; 13759dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val); 13859dd5aa8SSimon Glass 13959dd5aa8SSimon Glass if (tegra_dpaux_wait_transaction(dp)) 14059dd5aa8SSimon Glass debug("dp: aux write transaction timeout\n"); 14159dd5aa8SSimon Glass 14259dd5aa8SSimon Glass *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); 14359dd5aa8SSimon Glass 14459dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) || 14559dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) || 14659dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) || 14759dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) { 14859dd5aa8SSimon Glass if (timeout_retries-- > 0) { 14959dd5aa8SSimon Glass debug("dp: aux write retry (0x%x) -- %d\n", 15059dd5aa8SSimon Glass *aux_stat, timeout_retries); 15159dd5aa8SSimon Glass /* clear the error bits */ 15259dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, 15359dd5aa8SSimon Glass *aux_stat); 15459dd5aa8SSimon Glass continue; 15559dd5aa8SSimon Glass } else { 15659dd5aa8SSimon Glass debug("dp: aux write got error (0x%x)\n", 15759dd5aa8SSimon Glass *aux_stat); 15859dd5aa8SSimon Glass return -ETIMEDOUT; 15959dd5aa8SSimon Glass } 16059dd5aa8SSimon Glass } 16159dd5aa8SSimon Glass 16259dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) || 16359dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) { 16459dd5aa8SSimon Glass if (defer_retries-- > 0) { 16559dd5aa8SSimon Glass debug("dp: aux write defer (0x%x) -- %d\n", 16659dd5aa8SSimon Glass *aux_stat, defer_retries); 16759dd5aa8SSimon Glass /* clear the error bits */ 16859dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, 16959dd5aa8SSimon Glass *aux_stat); 17059dd5aa8SSimon Glass continue; 17159dd5aa8SSimon Glass } else { 17259dd5aa8SSimon Glass debug("dp: aux write defer exceeds max retries (0x%x)\n", 17359dd5aa8SSimon Glass *aux_stat); 17459dd5aa8SSimon Glass return -ETIMEDOUT; 17559dd5aa8SSimon Glass } 17659dd5aa8SSimon Glass } 17759dd5aa8SSimon Glass 17859dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) == 17959dd5aa8SSimon Glass DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) { 18059dd5aa8SSimon Glass *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK); 18159dd5aa8SSimon Glass return 0; 18259dd5aa8SSimon Glass } else { 18359dd5aa8SSimon Glass debug("dp: aux write failed (0x%x)\n", *aux_stat); 18459dd5aa8SSimon Glass return -EIO; 18559dd5aa8SSimon Glass } 18659dd5aa8SSimon Glass } 18759dd5aa8SSimon Glass /* Should never come to here */ 18859dd5aa8SSimon Glass return -EIO; 18959dd5aa8SSimon Glass } 19059dd5aa8SSimon Glass 19159dd5aa8SSimon Glass static int tegra_dc_dpaux_read_chunk(struct tegra_dp_priv *dp, u32 cmd, 19259dd5aa8SSimon Glass u32 addr, u8 *data, u32 *size, 19359dd5aa8SSimon Glass u32 *aux_stat) 19459dd5aa8SSimon Glass { 19559dd5aa8SSimon Glass u32 reg_val; 19659dd5aa8SSimon Glass u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; 19759dd5aa8SSimon Glass u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; 19859dd5aa8SSimon Glass 19959dd5aa8SSimon Glass if (*size > DP_AUX_MAX_BYTES) { 20059dd5aa8SSimon Glass debug("only read one chunk\n"); 20159dd5aa8SSimon Glass return -EIO; /* only read one chunk */ 20259dd5aa8SSimon Glass } 20359dd5aa8SSimon Glass 20459dd5aa8SSimon Glass /* Check to make sure the command is read command */ 20559dd5aa8SSimon Glass switch (cmd) { 20659dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_I2CRD: 20759dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT: 20859dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_MOTRD: 20959dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_AUXRD: 21059dd5aa8SSimon Glass break; 21159dd5aa8SSimon Glass default: 21259dd5aa8SSimon Glass debug("dp: aux read cmd 0x%x is invalid\n", cmd); 21359dd5aa8SSimon Glass return -EIO; 21459dd5aa8SSimon Glass } 21559dd5aa8SSimon Glass 21659dd5aa8SSimon Glass *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); 21759dd5aa8SSimon Glass if (!(*aux_stat & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) { 21859dd5aa8SSimon Glass debug("dp: HPD is not detected\n"); 21959dd5aa8SSimon Glass return -EIO; 22059dd5aa8SSimon Glass } 22159dd5aa8SSimon Glass 22259dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr); 22359dd5aa8SSimon Glass 22459dd5aa8SSimon Glass reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL); 22559dd5aa8SSimon Glass reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK; 22659dd5aa8SSimon Glass reg_val |= cmd; 22759dd5aa8SSimon Glass reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD; 22859dd5aa8SSimon Glass reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT); 22959dd5aa8SSimon Glass while ((timeout_retries > 0) && (defer_retries > 0)) { 23059dd5aa8SSimon Glass if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) || 23159dd5aa8SSimon Glass (defer_retries != DP_AUX_DEFER_MAX_TRIES)) 23259dd5aa8SSimon Glass udelay(DP_DPCP_RETRY_SLEEP_NS * 2); 23359dd5aa8SSimon Glass 23459dd5aa8SSimon Glass reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING; 23559dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val); 23659dd5aa8SSimon Glass 23759dd5aa8SSimon Glass if (tegra_dpaux_wait_transaction(dp)) 23859dd5aa8SSimon Glass debug("dp: aux read transaction timeout\n"); 23959dd5aa8SSimon Glass 24059dd5aa8SSimon Glass *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); 24159dd5aa8SSimon Glass 24259dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) || 24359dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) || 24459dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) || 24559dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) { 24659dd5aa8SSimon Glass if (timeout_retries-- > 0) { 24759dd5aa8SSimon Glass debug("dp: aux read retry (0x%x) -- %d\n", 24859dd5aa8SSimon Glass *aux_stat, timeout_retries); 24959dd5aa8SSimon Glass /* clear the error bits */ 25059dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, 25159dd5aa8SSimon Glass *aux_stat); 25259dd5aa8SSimon Glass continue; /* retry */ 25359dd5aa8SSimon Glass } else { 25459dd5aa8SSimon Glass debug("dp: aux read got error (0x%x)\n", 25559dd5aa8SSimon Glass *aux_stat); 25659dd5aa8SSimon Glass return -ETIMEDOUT; 25759dd5aa8SSimon Glass } 25859dd5aa8SSimon Glass } 25959dd5aa8SSimon Glass 26059dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) || 26159dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) { 26259dd5aa8SSimon Glass if (defer_retries-- > 0) { 26359dd5aa8SSimon Glass debug("dp: aux read defer (0x%x) -- %d\n", 26459dd5aa8SSimon Glass *aux_stat, defer_retries); 26559dd5aa8SSimon Glass /* clear the error bits */ 26659dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, 26759dd5aa8SSimon Glass *aux_stat); 26859dd5aa8SSimon Glass continue; 26959dd5aa8SSimon Glass } else { 27059dd5aa8SSimon Glass debug("dp: aux read defer exceeds max retries (0x%x)\n", 27159dd5aa8SSimon Glass *aux_stat); 27259dd5aa8SSimon Glass return -ETIMEDOUT; 27359dd5aa8SSimon Glass } 27459dd5aa8SSimon Glass } 27559dd5aa8SSimon Glass 27659dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) == 27759dd5aa8SSimon Glass DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) { 27859dd5aa8SSimon Glass int i; 27959dd5aa8SSimon Glass u32 temp_data[4]; 28059dd5aa8SSimon Glass 28159dd5aa8SSimon Glass for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) 28259dd5aa8SSimon Glass temp_data[i] = tegra_dpaux_readl(dp, 28359dd5aa8SSimon Glass DPAUX_DP_AUXDATA_READ_W(i)); 28459dd5aa8SSimon Glass 28559dd5aa8SSimon Glass *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK); 28659dd5aa8SSimon Glass memcpy(data, temp_data, *size); 28759dd5aa8SSimon Glass 28859dd5aa8SSimon Glass return 0; 28959dd5aa8SSimon Glass } else { 29059dd5aa8SSimon Glass debug("dp: aux read failed (0x%x\n", *aux_stat); 29159dd5aa8SSimon Glass return -EIO; 29259dd5aa8SSimon Glass } 29359dd5aa8SSimon Glass } 29459dd5aa8SSimon Glass /* Should never come to here */ 29559dd5aa8SSimon Glass debug("%s: can't\n", __func__); 29659dd5aa8SSimon Glass 29759dd5aa8SSimon Glass return -EIO; 29859dd5aa8SSimon Glass } 29959dd5aa8SSimon Glass 30059dd5aa8SSimon Glass static int tegra_dc_dpaux_read(struct tegra_dp_priv *dp, u32 cmd, u32 addr, 30159dd5aa8SSimon Glass u8 *data, u32 *size, u32 *aux_stat) 30259dd5aa8SSimon Glass { 30359dd5aa8SSimon Glass u32 finished = 0; 30459dd5aa8SSimon Glass u32 cur_size; 30559dd5aa8SSimon Glass int ret = 0; 30659dd5aa8SSimon Glass 30759dd5aa8SSimon Glass do { 30859dd5aa8SSimon Glass cur_size = *size - finished; 30959dd5aa8SSimon Glass if (cur_size > DP_AUX_MAX_BYTES) 31059dd5aa8SSimon Glass cur_size = DP_AUX_MAX_BYTES; 31159dd5aa8SSimon Glass 31259dd5aa8SSimon Glass ret = tegra_dc_dpaux_read_chunk(dp, cmd, addr, 31359dd5aa8SSimon Glass data, &cur_size, aux_stat); 31459dd5aa8SSimon Glass if (ret) 31559dd5aa8SSimon Glass break; 31659dd5aa8SSimon Glass 31759dd5aa8SSimon Glass /* cur_size should be the real size returned */ 31859dd5aa8SSimon Glass addr += cur_size; 31959dd5aa8SSimon Glass data += cur_size; 32059dd5aa8SSimon Glass finished += cur_size; 32159dd5aa8SSimon Glass 32259dd5aa8SSimon Glass } while (*size > finished); 32359dd5aa8SSimon Glass *size = finished; 32459dd5aa8SSimon Glass 32559dd5aa8SSimon Glass return ret; 32659dd5aa8SSimon Glass } 32759dd5aa8SSimon Glass 32859dd5aa8SSimon Glass static int tegra_dc_dp_dpcd_read(struct tegra_dp_priv *dp, u32 cmd, 32959dd5aa8SSimon Glass u8 *data_ptr) 33059dd5aa8SSimon Glass { 33159dd5aa8SSimon Glass u32 size = 1; 33259dd5aa8SSimon Glass u32 status = 0; 33359dd5aa8SSimon Glass int ret; 33459dd5aa8SSimon Glass 33559dd5aa8SSimon Glass ret = tegra_dc_dpaux_read_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, 33659dd5aa8SSimon Glass cmd, data_ptr, &size, &status); 33759dd5aa8SSimon Glass if (ret) { 33859dd5aa8SSimon Glass debug("dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n", 33959dd5aa8SSimon Glass cmd, status); 34059dd5aa8SSimon Glass } 34159dd5aa8SSimon Glass 34259dd5aa8SSimon Glass return ret; 34359dd5aa8SSimon Glass } 34459dd5aa8SSimon Glass 34559dd5aa8SSimon Glass static int tegra_dc_dp_dpcd_write(struct tegra_dp_priv *dp, u32 cmd, 34659dd5aa8SSimon Glass u8 data) 34759dd5aa8SSimon Glass { 34859dd5aa8SSimon Glass u32 size = 1; 34959dd5aa8SSimon Glass u32 status = 0; 35059dd5aa8SSimon Glass int ret; 35159dd5aa8SSimon Glass 35259dd5aa8SSimon Glass ret = tegra_dc_dpaux_write_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXWR, 35359dd5aa8SSimon Glass cmd, &data, &size, &status); 35459dd5aa8SSimon Glass if (ret) { 35559dd5aa8SSimon Glass debug("dp: Failed to write DPCD data. CMD 0x%x, Status 0x%x\n", 35659dd5aa8SSimon Glass cmd, status); 35759dd5aa8SSimon Glass } 35859dd5aa8SSimon Glass 35959dd5aa8SSimon Glass return ret; 36059dd5aa8SSimon Glass } 36159dd5aa8SSimon Glass 36259dd5aa8SSimon Glass static int tegra_dc_i2c_aux_read(struct tegra_dp_priv *dp, u32 i2c_addr, 36359dd5aa8SSimon Glass u8 addr, u8 *data, u32 size, u32 *aux_stat) 36459dd5aa8SSimon Glass { 36559dd5aa8SSimon Glass u32 finished = 0; 36659dd5aa8SSimon Glass int ret = 0; 36759dd5aa8SSimon Glass 36859dd5aa8SSimon Glass do { 36959dd5aa8SSimon Glass u32 cur_size = min((u32)DP_AUX_MAX_BYTES, size - finished); 37059dd5aa8SSimon Glass 37159dd5aa8SSimon Glass u32 len = 1; 37259dd5aa8SSimon Glass ret = tegra_dc_dpaux_write_chunk( 37359dd5aa8SSimon Glass dp, DPAUX_DP_AUXCTL_CMD_MOTWR, i2c_addr, 37459dd5aa8SSimon Glass &addr, &len, aux_stat); 37559dd5aa8SSimon Glass if (ret) { 37659dd5aa8SSimon Glass debug("%s: error sending address to read.\n", 37759dd5aa8SSimon Glass __func__); 37859dd5aa8SSimon Glass return ret; 37959dd5aa8SSimon Glass } 38059dd5aa8SSimon Glass 38159dd5aa8SSimon Glass ret = tegra_dc_dpaux_read_chunk( 38259dd5aa8SSimon Glass dp, DPAUX_DP_AUXCTL_CMD_I2CRD, i2c_addr, 38359dd5aa8SSimon Glass data, &cur_size, aux_stat); 38459dd5aa8SSimon Glass if (ret) { 38559dd5aa8SSimon Glass debug("%s: error reading data.\n", __func__); 38659dd5aa8SSimon Glass return ret; 38759dd5aa8SSimon Glass } 38859dd5aa8SSimon Glass 38959dd5aa8SSimon Glass /* cur_size should be the real size returned */ 39059dd5aa8SSimon Glass addr += cur_size; 39159dd5aa8SSimon Glass data += cur_size; 39259dd5aa8SSimon Glass finished += cur_size; 39359dd5aa8SSimon Glass } while (size > finished); 39459dd5aa8SSimon Glass 39559dd5aa8SSimon Glass return finished; 39659dd5aa8SSimon Glass } 39759dd5aa8SSimon Glass 39859dd5aa8SSimon Glass static void tegra_dc_dpaux_enable(struct tegra_dp_priv *dp) 39959dd5aa8SSimon Glass { 40059dd5aa8SSimon Glass /* clear interrupt */ 40159dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_INTR_AUX, 0xffffffff); 40259dd5aa8SSimon Glass /* do not enable interrupt for now. Enable them when Isr in place */ 40359dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_INTR_EN_AUX, 0x0); 40459dd5aa8SSimon Glass 40559dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_HYBRID_PADCTL, 40659dd5aa8SSimon Glass DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 | 40759dd5aa8SSimon Glass DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 | 40859dd5aa8SSimon Glass 0x18 << DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT | 40959dd5aa8SSimon Glass DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE); 41059dd5aa8SSimon Glass 41159dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_HYBRID_SPARE, 41259dd5aa8SSimon Glass DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP); 41359dd5aa8SSimon Glass } 41459dd5aa8SSimon Glass 41559dd5aa8SSimon Glass #ifdef DEBUG 41659dd5aa8SSimon Glass static void tegra_dc_dp_dump_link_cfg(struct tegra_dp_priv *dp, 41759dd5aa8SSimon Glass const struct tegra_dp_link_config *link_cfg) 41859dd5aa8SSimon Glass { 41959dd5aa8SSimon Glass debug("DP config: cfg_name cfg_value\n"); 42059dd5aa8SSimon Glass debug(" Lane Count %d\n", 42159dd5aa8SSimon Glass link_cfg->max_lane_count); 42259dd5aa8SSimon Glass debug(" SupportEnhancedFraming %s\n", 42359dd5aa8SSimon Glass link_cfg->support_enhanced_framing ? "Y" : "N"); 42459dd5aa8SSimon Glass debug(" Bandwidth %d\n", 42559dd5aa8SSimon Glass link_cfg->max_link_bw); 42659dd5aa8SSimon Glass debug(" bpp %d\n", 42759dd5aa8SSimon Glass link_cfg->bits_per_pixel); 42859dd5aa8SSimon Glass debug(" EnhancedFraming %s\n", 42959dd5aa8SSimon Glass link_cfg->enhanced_framing ? "Y" : "N"); 43059dd5aa8SSimon Glass debug(" Scramble_enabled %s\n", 43159dd5aa8SSimon Glass link_cfg->scramble_ena ? "Y" : "N"); 43259dd5aa8SSimon Glass debug(" LinkBW %d\n", 43359dd5aa8SSimon Glass link_cfg->link_bw); 43459dd5aa8SSimon Glass debug(" lane_count %d\n", 43559dd5aa8SSimon Glass link_cfg->lane_count); 43659dd5aa8SSimon Glass debug(" activespolarity %d\n", 43759dd5aa8SSimon Glass link_cfg->activepolarity); 43859dd5aa8SSimon Glass debug(" active_count %d\n", 43959dd5aa8SSimon Glass link_cfg->active_count); 44059dd5aa8SSimon Glass debug(" tu_size %d\n", 44159dd5aa8SSimon Glass link_cfg->tu_size); 44259dd5aa8SSimon Glass debug(" active_frac %d\n", 44359dd5aa8SSimon Glass link_cfg->active_frac); 44459dd5aa8SSimon Glass debug(" watermark %d\n", 44559dd5aa8SSimon Glass link_cfg->watermark); 44659dd5aa8SSimon Glass debug(" hblank_sym %d\n", 44759dd5aa8SSimon Glass link_cfg->hblank_sym); 44859dd5aa8SSimon Glass debug(" vblank_sym %d\n", 44959dd5aa8SSimon Glass link_cfg->vblank_sym); 45059dd5aa8SSimon Glass } 45159dd5aa8SSimon Glass #endif 45259dd5aa8SSimon Glass 453dedc44b4SSimon Glass static int _tegra_dp_lower_link_config(struct tegra_dp_priv *dp, 454dedc44b4SSimon Glass struct tegra_dp_link_config *cfg) 455dedc44b4SSimon Glass { 456dedc44b4SSimon Glass switch (cfg->link_bw) { 457dedc44b4SSimon Glass case SOR_LINK_SPEED_G1_62: 458dedc44b4SSimon Glass if (cfg->max_link_bw > SOR_LINK_SPEED_G1_62) 459dedc44b4SSimon Glass cfg->link_bw = SOR_LINK_SPEED_G2_7; 460dedc44b4SSimon Glass cfg->lane_count /= 2; 461dedc44b4SSimon Glass break; 462dedc44b4SSimon Glass case SOR_LINK_SPEED_G2_7: 463dedc44b4SSimon Glass cfg->link_bw = SOR_LINK_SPEED_G1_62; 464dedc44b4SSimon Glass break; 465dedc44b4SSimon Glass case SOR_LINK_SPEED_G5_4: 466dedc44b4SSimon Glass if (cfg->lane_count == 1) { 467dedc44b4SSimon Glass cfg->link_bw = SOR_LINK_SPEED_G2_7; 468dedc44b4SSimon Glass cfg->lane_count = cfg->max_lane_count; 469dedc44b4SSimon Glass } else { 470dedc44b4SSimon Glass cfg->lane_count /= 2; 471dedc44b4SSimon Glass } 472dedc44b4SSimon Glass break; 473dedc44b4SSimon Glass default: 474dedc44b4SSimon Glass debug("dp: Error link rate %d\n", cfg->link_bw); 475dedc44b4SSimon Glass return -ENOLINK; 476dedc44b4SSimon Glass } 477dedc44b4SSimon Glass 478dedc44b4SSimon Glass return (cfg->lane_count > 0) ? 0 : -ENOLINK; 479dedc44b4SSimon Glass } 480dedc44b4SSimon Glass 48159dd5aa8SSimon Glass /* 48259dd5aa8SSimon Glass * Calcuate if given cfg can meet the mode request. 48359dd5aa8SSimon Glass * Return 0 if mode is possible, -1 otherwise 48459dd5aa8SSimon Glass */ 48559dd5aa8SSimon Glass static int tegra_dc_dp_calc_config(struct tegra_dp_priv *dp, 48659dd5aa8SSimon Glass const struct display_timing *timing, 48759dd5aa8SSimon Glass struct tegra_dp_link_config *link_cfg) 48859dd5aa8SSimon Glass { 48959dd5aa8SSimon Glass const u32 link_rate = 27 * link_cfg->link_bw * 1000 * 1000; 49059dd5aa8SSimon Glass const u64 f = 100000; /* precision factor */ 49159dd5aa8SSimon Glass u32 num_linkclk_line; /* Number of link clocks per line */ 49259dd5aa8SSimon Glass u64 ratio_f; /* Ratio of incoming to outgoing data rate */ 49359dd5aa8SSimon Glass u64 frac_f; 49459dd5aa8SSimon Glass u64 activesym_f; /* Activesym per TU */ 49559dd5aa8SSimon Glass u64 activecount_f; 49659dd5aa8SSimon Glass u32 activecount; 49759dd5aa8SSimon Glass u32 activepolarity; 49859dd5aa8SSimon Glass u64 approx_value_f; 49959dd5aa8SSimon Glass u32 activefrac = 0; 50059dd5aa8SSimon Glass u64 accumulated_error_f = 0; 50159dd5aa8SSimon Glass u32 lowest_neg_activecount = 0; 50259dd5aa8SSimon Glass u32 lowest_neg_activepolarity = 0; 50359dd5aa8SSimon Glass u32 lowest_neg_tusize = 64; 50459dd5aa8SSimon Glass u32 num_symbols_per_line; 50559dd5aa8SSimon Glass u64 lowest_neg_activefrac = 0; 50659dd5aa8SSimon Glass u64 lowest_neg_error_f = 64 * f; 50759dd5aa8SSimon Glass u64 watermark_f; 50859dd5aa8SSimon Glass int i; 50959dd5aa8SSimon Glass int neg; 51059dd5aa8SSimon Glass 51159dd5aa8SSimon Glass if (!link_rate || !link_cfg->lane_count || !timing->pixelclock.typ || 51259dd5aa8SSimon Glass !link_cfg->bits_per_pixel) 51359dd5aa8SSimon Glass return -1; 51459dd5aa8SSimon Glass 51559dd5aa8SSimon Glass if ((u64)timing->pixelclock.typ * link_cfg->bits_per_pixel >= 51659dd5aa8SSimon Glass (u64)link_rate * 8 * link_cfg->lane_count) 51759dd5aa8SSimon Glass return -1; 51859dd5aa8SSimon Glass 51959dd5aa8SSimon Glass num_linkclk_line = (u32)(lldiv(link_rate * timing->hactive.typ, 52059dd5aa8SSimon Glass timing->pixelclock.typ)); 52159dd5aa8SSimon Glass 52259dd5aa8SSimon Glass ratio_f = (u64)timing->pixelclock.typ * link_cfg->bits_per_pixel * f; 52359dd5aa8SSimon Glass ratio_f /= 8; 52459dd5aa8SSimon Glass do_div(ratio_f, link_rate * link_cfg->lane_count); 52559dd5aa8SSimon Glass 52659dd5aa8SSimon Glass for (i = 64; i >= 32; --i) { 52759dd5aa8SSimon Glass activesym_f = ratio_f * i; 52859dd5aa8SSimon Glass activecount_f = lldiv(activesym_f, (u32)f) * f; 52959dd5aa8SSimon Glass frac_f = activesym_f - activecount_f; 53059dd5aa8SSimon Glass activecount = (u32)(lldiv(activecount_f, (u32)f)); 53159dd5aa8SSimon Glass 53259dd5aa8SSimon Glass if (frac_f < (lldiv(f, 2))) /* fraction < 0.5 */ 53359dd5aa8SSimon Glass activepolarity = 0; 53459dd5aa8SSimon Glass else { 53559dd5aa8SSimon Glass activepolarity = 1; 53659dd5aa8SSimon Glass frac_f = f - frac_f; 53759dd5aa8SSimon Glass } 53859dd5aa8SSimon Glass 53959dd5aa8SSimon Glass if (frac_f != 0) { 54059dd5aa8SSimon Glass /* warning: frac_f should be 64-bit */ 54159dd5aa8SSimon Glass frac_f = lldiv(f * f, frac_f); /* 1 / fraction */ 54259dd5aa8SSimon Glass if (frac_f > (15 * f)) 54359dd5aa8SSimon Glass activefrac = activepolarity ? 1 : 15; 54459dd5aa8SSimon Glass else 54559dd5aa8SSimon Glass activefrac = activepolarity ? 54659dd5aa8SSimon Glass (u32)lldiv(frac_f, (u32)f) + 1 : 54759dd5aa8SSimon Glass (u32)lldiv(frac_f, (u32)f); 54859dd5aa8SSimon Glass } 54959dd5aa8SSimon Glass 55059dd5aa8SSimon Glass if (activefrac == 1) 55159dd5aa8SSimon Glass activepolarity = 0; 55259dd5aa8SSimon Glass 55359dd5aa8SSimon Glass if (activepolarity == 1) 55459dd5aa8SSimon Glass approx_value_f = activefrac ? lldiv( 55559dd5aa8SSimon Glass (activecount_f + (activefrac * f - f) * f), 55659dd5aa8SSimon Glass (activefrac * f)) : 55759dd5aa8SSimon Glass activecount_f + f; 55859dd5aa8SSimon Glass else 55959dd5aa8SSimon Glass approx_value_f = activefrac ? 56059dd5aa8SSimon Glass activecount_f + lldiv(f, activefrac) : 56159dd5aa8SSimon Glass activecount_f; 56259dd5aa8SSimon Glass 56359dd5aa8SSimon Glass if (activesym_f < approx_value_f) { 56459dd5aa8SSimon Glass accumulated_error_f = num_linkclk_line * 56559dd5aa8SSimon Glass lldiv(approx_value_f - activesym_f, i); 56659dd5aa8SSimon Glass neg = 1; 56759dd5aa8SSimon Glass } else { 56859dd5aa8SSimon Glass accumulated_error_f = num_linkclk_line * 56959dd5aa8SSimon Glass lldiv(activesym_f - approx_value_f, i); 57059dd5aa8SSimon Glass neg = 0; 57159dd5aa8SSimon Glass } 57259dd5aa8SSimon Glass 57359dd5aa8SSimon Glass if ((neg && (lowest_neg_error_f > accumulated_error_f)) || 57459dd5aa8SSimon Glass (accumulated_error_f == 0)) { 57559dd5aa8SSimon Glass lowest_neg_error_f = accumulated_error_f; 57659dd5aa8SSimon Glass lowest_neg_tusize = i; 57759dd5aa8SSimon Glass lowest_neg_activecount = activecount; 57859dd5aa8SSimon Glass lowest_neg_activepolarity = activepolarity; 57959dd5aa8SSimon Glass lowest_neg_activefrac = activefrac; 58059dd5aa8SSimon Glass 58159dd5aa8SSimon Glass if (accumulated_error_f == 0) 58259dd5aa8SSimon Glass break; 58359dd5aa8SSimon Glass } 58459dd5aa8SSimon Glass } 58559dd5aa8SSimon Glass 58659dd5aa8SSimon Glass if (lowest_neg_activefrac == 0) { 58759dd5aa8SSimon Glass link_cfg->activepolarity = 0; 58859dd5aa8SSimon Glass link_cfg->active_count = lowest_neg_activepolarity ? 58959dd5aa8SSimon Glass lowest_neg_activecount : lowest_neg_activecount - 1; 59059dd5aa8SSimon Glass link_cfg->tu_size = lowest_neg_tusize; 59159dd5aa8SSimon Glass link_cfg->active_frac = 1; 59259dd5aa8SSimon Glass } else { 59359dd5aa8SSimon Glass link_cfg->activepolarity = lowest_neg_activepolarity; 59459dd5aa8SSimon Glass link_cfg->active_count = (u32)lowest_neg_activecount; 59559dd5aa8SSimon Glass link_cfg->tu_size = lowest_neg_tusize; 59659dd5aa8SSimon Glass link_cfg->active_frac = (u32)lowest_neg_activefrac; 59759dd5aa8SSimon Glass } 59859dd5aa8SSimon Glass 59959dd5aa8SSimon Glass watermark_f = lldiv(ratio_f * link_cfg->tu_size * (f - ratio_f), f); 60059dd5aa8SSimon Glass link_cfg->watermark = (u32)(lldiv(watermark_f + lowest_neg_error_f, 60159dd5aa8SSimon Glass f)) + link_cfg->bits_per_pixel / 4 - 1; 60259dd5aa8SSimon Glass num_symbols_per_line = (timing->hactive.typ * 60359dd5aa8SSimon Glass link_cfg->bits_per_pixel) / 60459dd5aa8SSimon Glass (8 * link_cfg->lane_count); 60559dd5aa8SSimon Glass 60659dd5aa8SSimon Glass if (link_cfg->watermark > 30) { 60759dd5aa8SSimon Glass debug("dp: sor setting: unable to get a good tusize, force watermark to 30\n"); 60859dd5aa8SSimon Glass link_cfg->watermark = 30; 60959dd5aa8SSimon Glass return -1; 61059dd5aa8SSimon Glass } else if (link_cfg->watermark > num_symbols_per_line) { 61159dd5aa8SSimon Glass debug("dp: sor setting: force watermark to the number of symbols in the line\n"); 61259dd5aa8SSimon Glass link_cfg->watermark = num_symbols_per_line; 61359dd5aa8SSimon Glass return -1; 61459dd5aa8SSimon Glass } 61559dd5aa8SSimon Glass 61659dd5aa8SSimon Glass /* 61759dd5aa8SSimon Glass * Refer to dev_disp.ref for more information. 61859dd5aa8SSimon Glass * # symbols/hblank = ((SetRasterBlankEnd.X + SetRasterSize.Width - 61959dd5aa8SSimon Glass * SetRasterBlankStart.X - 7) * link_clk / pclk) 62059dd5aa8SSimon Glass * - 3 * enhanced_framing - Y 62159dd5aa8SSimon Glass * where Y = (# lanes == 4) 3 : (# lanes == 2) ? 6 : 12 62259dd5aa8SSimon Glass */ 62359dd5aa8SSimon Glass link_cfg->hblank_sym = (int)lldiv(((uint64_t)timing->hback_porch.typ + 62459dd5aa8SSimon Glass timing->hfront_porch.typ + timing->hsync_len.typ - 7) * 62559dd5aa8SSimon Glass link_rate, timing->pixelclock.typ) - 62659dd5aa8SSimon Glass 3 * link_cfg->enhanced_framing - 62759dd5aa8SSimon Glass (12 / link_cfg->lane_count); 62859dd5aa8SSimon Glass 62959dd5aa8SSimon Glass if (link_cfg->hblank_sym < 0) 63059dd5aa8SSimon Glass link_cfg->hblank_sym = 0; 63159dd5aa8SSimon Glass 63259dd5aa8SSimon Glass 63359dd5aa8SSimon Glass /* 63459dd5aa8SSimon Glass * Refer to dev_disp.ref for more information. 63559dd5aa8SSimon Glass * # symbols/vblank = ((SetRasterBlankStart.X - 63659dd5aa8SSimon Glass * SetRasterBlankEen.X - 25) * link_clk / pclk) 63759dd5aa8SSimon Glass * - Y - 1; 63859dd5aa8SSimon Glass * where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39 63959dd5aa8SSimon Glass */ 64059dd5aa8SSimon Glass link_cfg->vblank_sym = (int)lldiv(((uint64_t)timing->hactive.typ - 25) 64159dd5aa8SSimon Glass * link_rate, timing->pixelclock.typ) - (36 / 64259dd5aa8SSimon Glass link_cfg->lane_count) - 4; 64359dd5aa8SSimon Glass 64459dd5aa8SSimon Glass if (link_cfg->vblank_sym < 0) 64559dd5aa8SSimon Glass link_cfg->vblank_sym = 0; 64659dd5aa8SSimon Glass 64759dd5aa8SSimon Glass link_cfg->is_valid = 1; 64859dd5aa8SSimon Glass #ifdef DEBUG 64959dd5aa8SSimon Glass tegra_dc_dp_dump_link_cfg(dp, link_cfg); 65059dd5aa8SSimon Glass #endif 65159dd5aa8SSimon Glass 65259dd5aa8SSimon Glass return 0; 65359dd5aa8SSimon Glass } 65459dd5aa8SSimon Glass 65559dd5aa8SSimon Glass static int tegra_dc_dp_init_max_link_cfg( 65659dd5aa8SSimon Glass const struct display_timing *timing, 65759dd5aa8SSimon Glass struct tegra_dp_priv *dp, 65859dd5aa8SSimon Glass struct tegra_dp_link_config *link_cfg) 65959dd5aa8SSimon Glass { 66059dd5aa8SSimon Glass const int drive_current = 0x40404040; 66159dd5aa8SSimon Glass const int preemphasis = 0x0f0f0f0f; 66259dd5aa8SSimon Glass const int postcursor = 0; 66359dd5aa8SSimon Glass u8 dpcd_data; 66459dd5aa8SSimon Glass int ret; 66559dd5aa8SSimon Glass 66659dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LANE_COUNT, &dpcd_data); 66759dd5aa8SSimon Glass if (ret) 66859dd5aa8SSimon Glass return ret; 66959dd5aa8SSimon Glass link_cfg->max_lane_count = dpcd_data & DP_MAX_LANE_COUNT_MASK; 670dedc44b4SSimon Glass link_cfg->tps3_supported = (dpcd_data & 671dedc44b4SSimon Glass DP_MAX_LANE_COUNT_TPS3_SUPPORTED_YES) ? 1 : 0; 67259dd5aa8SSimon Glass 67359dd5aa8SSimon Glass link_cfg->support_enhanced_framing = 67459dd5aa8SSimon Glass (dpcd_data & DP_MAX_LANE_COUNT_ENHANCED_FRAMING_YES) ? 67559dd5aa8SSimon Glass 1 : 0; 67659dd5aa8SSimon Glass 67759dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_DOWNSPREAD, &dpcd_data); 67859dd5aa8SSimon Glass if (ret) 67959dd5aa8SSimon Glass return ret; 68059dd5aa8SSimon Glass link_cfg->downspread = (dpcd_data & DP_MAX_DOWNSPREAD_VAL_0_5_PCT) ? 68159dd5aa8SSimon Glass 1 : 0; 68259dd5aa8SSimon Glass 683dedc44b4SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_TRAINING_AUX_RD_INTERVAL, 684dedc44b4SSimon Glass &link_cfg->aux_rd_interval); 685dedc44b4SSimon Glass if (ret) 686dedc44b4SSimon Glass return ret; 68759dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LINK_RATE, 68859dd5aa8SSimon Glass &link_cfg->max_link_bw); 68959dd5aa8SSimon Glass if (ret) 69059dd5aa8SSimon Glass return ret; 69159dd5aa8SSimon Glass 69259dd5aa8SSimon Glass /* 69359dd5aa8SSimon Glass * Set to a high value for link training and attach. 69459dd5aa8SSimon Glass * Will be re-programmed when dp is enabled. 69559dd5aa8SSimon Glass */ 69659dd5aa8SSimon Glass link_cfg->drive_current = drive_current; 69759dd5aa8SSimon Glass link_cfg->preemphasis = preemphasis; 69859dd5aa8SSimon Glass link_cfg->postcursor = postcursor; 69959dd5aa8SSimon Glass 70059dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_EDP_CONFIGURATION_CAP, &dpcd_data); 70159dd5aa8SSimon Glass if (ret) 70259dd5aa8SSimon Glass return ret; 70359dd5aa8SSimon Glass 70459dd5aa8SSimon Glass link_cfg->alt_scramber_reset_cap = 70559dd5aa8SSimon Glass (dpcd_data & DP_EDP_CONFIGURATION_CAP_ASC_RESET_YES) ? 70659dd5aa8SSimon Glass 1 : 0; 70759dd5aa8SSimon Glass link_cfg->only_enhanced_framing = 70859dd5aa8SSimon Glass (dpcd_data & DP_EDP_CONFIGURATION_CAP_FRAMING_CHANGE_YES) ? 70959dd5aa8SSimon Glass 1 : 0; 71059dd5aa8SSimon Glass 71159dd5aa8SSimon Glass link_cfg->lane_count = link_cfg->max_lane_count; 71259dd5aa8SSimon Glass link_cfg->link_bw = link_cfg->max_link_bw; 71359dd5aa8SSimon Glass link_cfg->enhanced_framing = link_cfg->support_enhanced_framing; 714dedc44b4SSimon Glass link_cfg->frame_in_ms = (1000 / 60) + 1; 71559dd5aa8SSimon Glass 71659dd5aa8SSimon Glass tegra_dc_dp_calc_config(dp, timing, link_cfg); 71759dd5aa8SSimon Glass return 0; 71859dd5aa8SSimon Glass } 71959dd5aa8SSimon Glass 720*d7659212SSimon Glass static int tegra_dc_dp_set_assr(struct tegra_dp_priv *priv, 721*d7659212SSimon Glass struct udevice *sor, int ena) 72259dd5aa8SSimon Glass { 72359dd5aa8SSimon Glass int ret; 72459dd5aa8SSimon Glass 72559dd5aa8SSimon Glass u8 dpcd_data = ena ? 72659dd5aa8SSimon Glass DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE : 72759dd5aa8SSimon Glass DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE; 72859dd5aa8SSimon Glass 729*d7659212SSimon Glass ret = tegra_dc_dp_dpcd_write(priv, DP_EDP_CONFIGURATION_SET, 73059dd5aa8SSimon Glass dpcd_data); 73159dd5aa8SSimon Glass if (ret) 73259dd5aa8SSimon Glass return ret; 73359dd5aa8SSimon Glass 73459dd5aa8SSimon Glass /* Also reset the scrambler to 0xfffe */ 73559dd5aa8SSimon Glass tegra_dc_sor_set_internal_panel(sor, ena); 73659dd5aa8SSimon Glass return 0; 73759dd5aa8SSimon Glass } 73859dd5aa8SSimon Glass 73959dd5aa8SSimon Glass static int tegra_dp_set_link_bandwidth(struct tegra_dp_priv *dp, 740*d7659212SSimon Glass struct udevice *sor, 74159dd5aa8SSimon Glass u8 link_bw) 74259dd5aa8SSimon Glass { 74359dd5aa8SSimon Glass tegra_dc_sor_set_link_bandwidth(sor, link_bw); 74459dd5aa8SSimon Glass 74559dd5aa8SSimon Glass /* Sink side */ 74659dd5aa8SSimon Glass return tegra_dc_dp_dpcd_write(dp, DP_LINK_BW_SET, link_bw); 74759dd5aa8SSimon Glass } 74859dd5aa8SSimon Glass 74959dd5aa8SSimon Glass static int tegra_dp_set_lane_count(struct tegra_dp_priv *dp, 75059dd5aa8SSimon Glass const struct tegra_dp_link_config *link_cfg, 751*d7659212SSimon Glass struct udevice *sor) 75259dd5aa8SSimon Glass { 75359dd5aa8SSimon Glass u8 dpcd_data; 75459dd5aa8SSimon Glass int ret; 75559dd5aa8SSimon Glass 75659dd5aa8SSimon Glass /* check if panel support enhanched_framing */ 75759dd5aa8SSimon Glass dpcd_data = link_cfg->lane_count; 75859dd5aa8SSimon Glass if (link_cfg->enhanced_framing) 75959dd5aa8SSimon Glass dpcd_data |= DP_LANE_COUNT_SET_ENHANCEDFRAMING_T; 76059dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_write(dp, DP_LANE_COUNT_SET, dpcd_data); 76159dd5aa8SSimon Glass if (ret) 76259dd5aa8SSimon Glass return ret; 76359dd5aa8SSimon Glass 76459dd5aa8SSimon Glass tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count); 76559dd5aa8SSimon Glass 76659dd5aa8SSimon Glass /* Also power down lanes that will not be used */ 76759dd5aa8SSimon Glass return 0; 76859dd5aa8SSimon Glass } 76959dd5aa8SSimon Glass 77059dd5aa8SSimon Glass static int tegra_dc_dp_link_trained(struct tegra_dp_priv *dp, 77159dd5aa8SSimon Glass const struct tegra_dp_link_config *cfg) 77259dd5aa8SSimon Glass { 77359dd5aa8SSimon Glass u32 lane; 77459dd5aa8SSimon Glass u8 mask; 77559dd5aa8SSimon Glass u8 data; 77659dd5aa8SSimon Glass int ret; 77759dd5aa8SSimon Glass 77859dd5aa8SSimon Glass for (lane = 0; lane < cfg->lane_count; ++lane) { 77959dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, (lane / 2) ? 78059dd5aa8SSimon Glass DP_LANE2_3_STATUS : DP_LANE0_1_STATUS, 78159dd5aa8SSimon Glass &data); 78259dd5aa8SSimon Glass if (ret) 78359dd5aa8SSimon Glass return ret; 78459dd5aa8SSimon Glass mask = (lane & 1) ? 78559dd5aa8SSimon Glass NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES | 78659dd5aa8SSimon Glass NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES | 78759dd5aa8SSimon Glass NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES : 78859dd5aa8SSimon Glass DP_LANE_CR_DONE | 78959dd5aa8SSimon Glass DP_LANE_CHANNEL_EQ_DONE | 79059dd5aa8SSimon Glass DP_LANE_SYMBOL_LOCKED; 79159dd5aa8SSimon Glass if ((data & mask) != mask) 79259dd5aa8SSimon Glass return -1; 79359dd5aa8SSimon Glass } 79459dd5aa8SSimon Glass return 0; 79559dd5aa8SSimon Glass } 79659dd5aa8SSimon Glass 797dedc44b4SSimon Glass static int tegra_dp_channel_eq_status(struct tegra_dp_priv *dp, 798dedc44b4SSimon Glass const struct tegra_dp_link_config *cfg) 799dedc44b4SSimon Glass { 800dedc44b4SSimon Glass u32 cnt; 801dedc44b4SSimon Glass u32 n_lanes = cfg->lane_count; 802dedc44b4SSimon Glass u8 data; 803dedc44b4SSimon Glass u8 ce_done = 1; 804dedc44b4SSimon Glass int ret; 805dedc44b4SSimon Glass 806dedc44b4SSimon Glass for (cnt = 0; cnt < n_lanes / 2; cnt++) { 807dedc44b4SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_LANE0_1_STATUS + cnt, &data); 808dedc44b4SSimon Glass if (ret) 809dedc44b4SSimon Glass return ret; 810dedc44b4SSimon Glass 811dedc44b4SSimon Glass if (n_lanes == 1) { 812dedc44b4SSimon Glass ce_done = (data & (0x1 << 813dedc44b4SSimon Glass NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) && 814dedc44b4SSimon Glass (data & (0x1 << 815dedc44b4SSimon Glass NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT)); 816dedc44b4SSimon Glass break; 817dedc44b4SSimon Glass } else if (!(data & (0x1 << 818dedc44b4SSimon Glass NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) || 819dedc44b4SSimon Glass !(data & (0x1 << 820dedc44b4SSimon Glass NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT)) || 821dedc44b4SSimon Glass !(data & (0x1 << 822dedc44b4SSimon Glass NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT)) || 823dedc44b4SSimon Glass !(data & (0x1 << 824dedc44b4SSimon Glass NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT))) 825dedc44b4SSimon Glass return -EIO; 826dedc44b4SSimon Glass } 827dedc44b4SSimon Glass 828dedc44b4SSimon Glass if (ce_done) { 829dedc44b4SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, 830dedc44b4SSimon Glass DP_LANE_ALIGN_STATUS_UPDATED, 831dedc44b4SSimon Glass &data); 832dedc44b4SSimon Glass if (ret) 833dedc44b4SSimon Glass return ret; 834dedc44b4SSimon Glass if (!(data & NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_YES)) 835dedc44b4SSimon Glass ce_done = 0; 836dedc44b4SSimon Glass } 837dedc44b4SSimon Glass 838dedc44b4SSimon Glass return ce_done ? 0 : -EIO; 839dedc44b4SSimon Glass } 840dedc44b4SSimon Glass 841dedc44b4SSimon Glass static int tegra_dp_clock_recovery_status(struct tegra_dp_priv *dp, 842dedc44b4SSimon Glass const struct tegra_dp_link_config *cfg) 843dedc44b4SSimon Glass { 844dedc44b4SSimon Glass u32 cnt; 845dedc44b4SSimon Glass u32 n_lanes = cfg->lane_count; 846dedc44b4SSimon Glass u8 data_ptr; 847dedc44b4SSimon Glass int ret; 848dedc44b4SSimon Glass 849dedc44b4SSimon Glass for (cnt = 0; cnt < n_lanes / 2; cnt++) { 850dedc44b4SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, (DP_LANE0_1_STATUS + cnt), 851dedc44b4SSimon Glass &data_ptr); 852dedc44b4SSimon Glass if (ret) 853dedc44b4SSimon Glass return ret; 854dedc44b4SSimon Glass 855dedc44b4SSimon Glass if (n_lanes == 1) 856dedc44b4SSimon Glass return (data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) ? 857dedc44b4SSimon Glass 1 : 0; 858dedc44b4SSimon Glass else if (!(data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) || 859dedc44b4SSimon Glass !(data_ptr & (NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES))) 860dedc44b4SSimon Glass return 0; 861dedc44b4SSimon Glass } 862dedc44b4SSimon Glass 863dedc44b4SSimon Glass return 1; 864dedc44b4SSimon Glass } 865dedc44b4SSimon Glass 866dedc44b4SSimon Glass static int tegra_dp_lt_adjust(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4], 867dedc44b4SSimon Glass u32 pc[4], u8 pc_supported, 868dedc44b4SSimon Glass const struct tegra_dp_link_config *cfg) 869dedc44b4SSimon Glass { 870dedc44b4SSimon Glass size_t cnt; 871dedc44b4SSimon Glass u8 data_ptr; 872dedc44b4SSimon Glass u32 n_lanes = cfg->lane_count; 873dedc44b4SSimon Glass int ret; 874dedc44b4SSimon Glass 875dedc44b4SSimon Glass for (cnt = 0; cnt < n_lanes / 2; cnt++) { 876dedc44b4SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_ADJUST_REQUEST_LANE0_1 + cnt, 877dedc44b4SSimon Glass &data_ptr); 878dedc44b4SSimon Glass if (ret) 879dedc44b4SSimon Glass return ret; 880dedc44b4SSimon Glass pe[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_PE_MASK) >> 881dedc44b4SSimon Glass NV_DPCD_ADJUST_REQ_LANEX_PE_SHIFT; 882dedc44b4SSimon Glass vs[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_DC_MASK) >> 883dedc44b4SSimon Glass NV_DPCD_ADJUST_REQ_LANEX_DC_SHIFT; 884dedc44b4SSimon Glass pe[1 + 2 * cnt] = 885dedc44b4SSimon Glass (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_MASK) >> 886dedc44b4SSimon Glass NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_SHIFT; 887dedc44b4SSimon Glass vs[1 + 2 * cnt] = 888dedc44b4SSimon Glass (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_MASK) >> 889dedc44b4SSimon Glass NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_SHIFT; 890dedc44b4SSimon Glass } 891dedc44b4SSimon Glass if (pc_supported) { 892dedc44b4SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_ADJUST_REQ_POST_CURSOR2, 893dedc44b4SSimon Glass &data_ptr); 894dedc44b4SSimon Glass if (ret) 895dedc44b4SSimon Glass return ret; 896dedc44b4SSimon Glass for (cnt = 0; cnt < n_lanes; cnt++) { 897dedc44b4SSimon Glass pc[cnt] = (data_ptr >> 898dedc44b4SSimon Glass NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_SHIFT(cnt)) & 899dedc44b4SSimon Glass NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK; 900dedc44b4SSimon Glass } 901dedc44b4SSimon Glass } 902dedc44b4SSimon Glass 903dedc44b4SSimon Glass return 0; 904dedc44b4SSimon Glass } 905dedc44b4SSimon Glass 906dedc44b4SSimon Glass static void tegra_dp_wait_aux_training(struct tegra_dp_priv *dp, 907dedc44b4SSimon Glass bool is_clk_recovery, 908dedc44b4SSimon Glass const struct tegra_dp_link_config *cfg) 909dedc44b4SSimon Glass { 910dedc44b4SSimon Glass if (!cfg->aux_rd_interval) 911dedc44b4SSimon Glass udelay(is_clk_recovery ? 200 : 500); 912dedc44b4SSimon Glass else 913dedc44b4SSimon Glass mdelay(cfg->aux_rd_interval * 4); 914dedc44b4SSimon Glass } 915dedc44b4SSimon Glass 916dedc44b4SSimon Glass static void tegra_dp_tpg(struct tegra_dp_priv *dp, u32 tp, u32 n_lanes, 917dedc44b4SSimon Glass const struct tegra_dp_link_config *cfg) 918dedc44b4SSimon Glass { 919dedc44b4SSimon Glass u8 data = (tp == training_pattern_disabled) 920dedc44b4SSimon Glass ? (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_F) 921dedc44b4SSimon Glass : (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T); 922dedc44b4SSimon Glass 923dedc44b4SSimon Glass tegra_dc_sor_set_dp_linkctl(dp->sor, 1, tp, cfg); 924dedc44b4SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, data); 925dedc44b4SSimon Glass } 926dedc44b4SSimon Glass 927dedc44b4SSimon Glass static int tegra_dp_link_config(struct tegra_dp_priv *dp, 928dedc44b4SSimon Glass const struct tegra_dp_link_config *link_cfg) 929dedc44b4SSimon Glass { 930dedc44b4SSimon Glass u8 dpcd_data; 931dedc44b4SSimon Glass u32 retry; 932dedc44b4SSimon Glass int ret; 933dedc44b4SSimon Glass 934dedc44b4SSimon Glass if (link_cfg->lane_count == 0) { 935dedc44b4SSimon Glass debug("dp: error: lane count is 0. Can not set link config.\n"); 936dedc44b4SSimon Glass return -ENOLINK; 937dedc44b4SSimon Glass } 938dedc44b4SSimon Glass 939dedc44b4SSimon Glass /* Set power state if it is not in normal level */ 940dedc44b4SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_SET_POWER, &dpcd_data); 941dedc44b4SSimon Glass if (ret) 942dedc44b4SSimon Glass return ret; 943dedc44b4SSimon Glass 944dedc44b4SSimon Glass if (dpcd_data == DP_SET_POWER_D3) { 945dedc44b4SSimon Glass dpcd_data = DP_SET_POWER_D0; 946dedc44b4SSimon Glass 947dedc44b4SSimon Glass /* DP spec requires 3 retries */ 948dedc44b4SSimon Glass for (retry = 3; retry > 0; --retry) { 949dedc44b4SSimon Glass ret = tegra_dc_dp_dpcd_write(dp, DP_SET_POWER, 950dedc44b4SSimon Glass dpcd_data); 951dedc44b4SSimon Glass if (!ret) 952dedc44b4SSimon Glass break; 953dedc44b4SSimon Glass if (retry == 1) { 954dedc44b4SSimon Glass debug("dp: Failed to set DP panel power\n"); 955dedc44b4SSimon Glass return ret; 956dedc44b4SSimon Glass } 957dedc44b4SSimon Glass } 958dedc44b4SSimon Glass } 959dedc44b4SSimon Glass 960dedc44b4SSimon Glass /* Enable ASSR if possible */ 961dedc44b4SSimon Glass if (link_cfg->alt_scramber_reset_cap) { 962dedc44b4SSimon Glass ret = tegra_dc_dp_set_assr(dp, dp->sor, 1); 963dedc44b4SSimon Glass if (ret) 964dedc44b4SSimon Glass return ret; 965dedc44b4SSimon Glass } 966dedc44b4SSimon Glass 967dedc44b4SSimon Glass ret = tegra_dp_set_link_bandwidth(dp, dp->sor, link_cfg->link_bw); 968dedc44b4SSimon Glass if (ret) { 969dedc44b4SSimon Glass debug("dp: Failed to set link bandwidth\n"); 970dedc44b4SSimon Glass return ret; 971dedc44b4SSimon Glass } 972dedc44b4SSimon Glass ret = tegra_dp_set_lane_count(dp, link_cfg, dp->sor); 973dedc44b4SSimon Glass if (ret) { 974dedc44b4SSimon Glass debug("dp: Failed to set lane count\n"); 975dedc44b4SSimon Glass return ret; 976dedc44b4SSimon Glass } 977dedc44b4SSimon Glass tegra_dc_sor_set_dp_linkctl(dp->sor, 1, training_pattern_none, 978dedc44b4SSimon Glass link_cfg); 979dedc44b4SSimon Glass 980dedc44b4SSimon Glass return 0; 981dedc44b4SSimon Glass } 982dedc44b4SSimon Glass 983dedc44b4SSimon Glass static int tegra_dp_lower_link_config(struct tegra_dp_priv *dp, 984dedc44b4SSimon Glass const struct display_timing *timing, 985dedc44b4SSimon Glass struct tegra_dp_link_config *cfg) 986dedc44b4SSimon Glass { 987dedc44b4SSimon Glass struct tegra_dp_link_config tmp_cfg; 988dedc44b4SSimon Glass int ret; 989dedc44b4SSimon Glass 990dedc44b4SSimon Glass tmp_cfg = *cfg; 991dedc44b4SSimon Glass cfg->is_valid = 0; 992dedc44b4SSimon Glass 993dedc44b4SSimon Glass ret = _tegra_dp_lower_link_config(dp, cfg); 994dedc44b4SSimon Glass if (!ret) 995dedc44b4SSimon Glass ret = tegra_dc_dp_calc_config(dp, timing, cfg); 996dedc44b4SSimon Glass if (!ret) 997dedc44b4SSimon Glass ret = tegra_dp_link_config(dp, cfg); 998dedc44b4SSimon Glass if (ret) 999dedc44b4SSimon Glass goto fail; 1000dedc44b4SSimon Glass 1001dedc44b4SSimon Glass return 0; 1002dedc44b4SSimon Glass 1003dedc44b4SSimon Glass fail: 1004dedc44b4SSimon Glass *cfg = tmp_cfg; 1005dedc44b4SSimon Glass tegra_dp_link_config(dp, &tmp_cfg); 1006dedc44b4SSimon Glass return ret; 1007dedc44b4SSimon Glass } 1008dedc44b4SSimon Glass 1009dedc44b4SSimon Glass static int tegra_dp_lt_config(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4], 1010dedc44b4SSimon Glass u32 pc[4], const struct tegra_dp_link_config *cfg) 1011dedc44b4SSimon Glass { 1012*d7659212SSimon Glass struct udevice *sor = dp->sor; 1013dedc44b4SSimon Glass u32 n_lanes = cfg->lane_count; 1014dedc44b4SSimon Glass u8 pc_supported = cfg->tps3_supported; 1015dedc44b4SSimon Glass u32 cnt; 1016dedc44b4SSimon Glass u32 val; 1017dedc44b4SSimon Glass 1018dedc44b4SSimon Glass for (cnt = 0; cnt < n_lanes; cnt++) { 1019dedc44b4SSimon Glass u32 mask = 0; 1020dedc44b4SSimon Glass u32 pe_reg, vs_reg, pc_reg; 1021dedc44b4SSimon Glass u32 shift = 0; 1022dedc44b4SSimon Glass 1023dedc44b4SSimon Glass switch (cnt) { 1024dedc44b4SSimon Glass case 0: 1025dedc44b4SSimon Glass mask = PR_LANE2_DP_LANE0_MASK; 1026dedc44b4SSimon Glass shift = PR_LANE2_DP_LANE0_SHIFT; 1027dedc44b4SSimon Glass break; 1028dedc44b4SSimon Glass case 1: 1029dedc44b4SSimon Glass mask = PR_LANE1_DP_LANE1_MASK; 1030dedc44b4SSimon Glass shift = PR_LANE1_DP_LANE1_SHIFT; 1031dedc44b4SSimon Glass break; 1032dedc44b4SSimon Glass case 2: 1033dedc44b4SSimon Glass mask = PR_LANE0_DP_LANE2_MASK; 1034dedc44b4SSimon Glass shift = PR_LANE0_DP_LANE2_SHIFT; 1035dedc44b4SSimon Glass break; 1036dedc44b4SSimon Glass case 3: 1037dedc44b4SSimon Glass mask = PR_LANE3_DP_LANE3_MASK; 1038dedc44b4SSimon Glass shift = PR_LANE3_DP_LANE3_SHIFT; 1039dedc44b4SSimon Glass break; 1040dedc44b4SSimon Glass default: 1041dedc44b4SSimon Glass debug("dp: incorrect lane cnt\n"); 1042dedc44b4SSimon Glass return -EINVAL; 1043dedc44b4SSimon Glass } 1044dedc44b4SSimon Glass 1045dedc44b4SSimon Glass pe_reg = tegra_dp_pe_regs[pc[cnt]][vs[cnt]][pe[cnt]]; 1046dedc44b4SSimon Glass vs_reg = tegra_dp_vs_regs[pc[cnt]][vs[cnt]][pe[cnt]]; 1047dedc44b4SSimon Glass pc_reg = tegra_dp_pc_regs[pc[cnt]][vs[cnt]][pe[cnt]]; 1048dedc44b4SSimon Glass 1049dedc44b4SSimon Glass tegra_dp_set_pe_vs_pc(sor, mask, pe_reg << shift, 1050dedc44b4SSimon Glass vs_reg << shift, pc_reg << shift, 1051dedc44b4SSimon Glass pc_supported); 1052dedc44b4SSimon Glass } 1053dedc44b4SSimon Glass 1054dedc44b4SSimon Glass tegra_dp_disable_tx_pu(dp->sor); 1055dedc44b4SSimon Glass udelay(20); 1056dedc44b4SSimon Glass 1057dedc44b4SSimon Glass for (cnt = 0; cnt < n_lanes; cnt++) { 1058dedc44b4SSimon Glass u32 max_vs_flag = tegra_dp_is_max_vs(pe[cnt], vs[cnt]); 1059dedc44b4SSimon Glass u32 max_pe_flag = tegra_dp_is_max_pe(pe[cnt], vs[cnt]); 1060dedc44b4SSimon Glass 1061dedc44b4SSimon Glass val = (vs[cnt] << NV_DPCD_TRAINING_LANEX_SET_DC_SHIFT) | 1062dedc44b4SSimon Glass (max_vs_flag ? 1063dedc44b4SSimon Glass NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_T : 1064dedc44b4SSimon Glass NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F) | 1065dedc44b4SSimon Glass (pe[cnt] << NV_DPCD_TRAINING_LANEX_SET_PE_SHIFT) | 1066dedc44b4SSimon Glass (max_pe_flag ? 1067dedc44b4SSimon Glass NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_T : 1068dedc44b4SSimon Glass NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_F); 1069dedc44b4SSimon Glass tegra_dc_dp_dpcd_write(dp, (DP_TRAINING_LANE0_SET + cnt), val); 1070dedc44b4SSimon Glass } 1071dedc44b4SSimon Glass 1072dedc44b4SSimon Glass if (pc_supported) { 1073dedc44b4SSimon Glass for (cnt = 0; cnt < n_lanes / 2; cnt++) { 1074dedc44b4SSimon Glass u32 max_pc_flag0 = tegra_dp_is_max_pc(pc[cnt]); 1075dedc44b4SSimon Glass u32 max_pc_flag1 = tegra_dp_is_max_pc(pc[cnt + 1]); 1076dedc44b4SSimon Glass val = (pc[cnt] << NV_DPCD_LANEX_SET2_PC2_SHIFT) | 1077dedc44b4SSimon Glass (max_pc_flag0 ? 1078dedc44b4SSimon Glass NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_T : 1079dedc44b4SSimon Glass NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_F) | 1080dedc44b4SSimon Glass (pc[cnt + 1] << 1081dedc44b4SSimon Glass NV_DPCD_LANEXPLUS1_SET2_PC2_SHIFT) | 1082dedc44b4SSimon Glass (max_pc_flag1 ? 1083dedc44b4SSimon Glass NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_T : 1084dedc44b4SSimon Glass NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_F); 1085dedc44b4SSimon Glass tegra_dc_dp_dpcd_write(dp, 1086dedc44b4SSimon Glass NV_DPCD_TRAINING_LANE0_1_SET2 + 1087dedc44b4SSimon Glass cnt, val); 1088dedc44b4SSimon Glass } 1089dedc44b4SSimon Glass } 1090dedc44b4SSimon Glass 1091dedc44b4SSimon Glass return 0; 1092dedc44b4SSimon Glass } 1093dedc44b4SSimon Glass 1094dedc44b4SSimon Glass static int _tegra_dp_channel_eq(struct tegra_dp_priv *dp, u32 pe[4], 1095dedc44b4SSimon Glass u32 vs[4], u32 pc[4], u8 pc_supported, 1096dedc44b4SSimon Glass u32 n_lanes, 1097dedc44b4SSimon Glass const struct tegra_dp_link_config *cfg) 1098dedc44b4SSimon Glass { 1099dedc44b4SSimon Glass u32 retry_cnt; 1100dedc44b4SSimon Glass 1101dedc44b4SSimon Glass for (retry_cnt = 0; retry_cnt < 4; retry_cnt++) { 1102dedc44b4SSimon Glass int ret; 1103dedc44b4SSimon Glass 1104dedc44b4SSimon Glass if (retry_cnt) { 1105dedc44b4SSimon Glass ret = tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported, 1106dedc44b4SSimon Glass cfg); 1107dedc44b4SSimon Glass if (ret) 1108dedc44b4SSimon Glass return ret; 1109dedc44b4SSimon Glass tegra_dp_lt_config(dp, pe, vs, pc, cfg); 1110dedc44b4SSimon Glass } 1111dedc44b4SSimon Glass 1112dedc44b4SSimon Glass tegra_dp_wait_aux_training(dp, false, cfg); 1113dedc44b4SSimon Glass 1114dedc44b4SSimon Glass if (!tegra_dp_clock_recovery_status(dp, cfg)) { 1115dedc44b4SSimon Glass debug("dp: CR failed in channel EQ sequence!\n"); 1116dedc44b4SSimon Glass break; 1117dedc44b4SSimon Glass } 1118dedc44b4SSimon Glass 1119dedc44b4SSimon Glass if (!tegra_dp_channel_eq_status(dp, cfg)) 1120dedc44b4SSimon Glass return 0; 1121dedc44b4SSimon Glass } 1122dedc44b4SSimon Glass 1123dedc44b4SSimon Glass return -EIO; 1124dedc44b4SSimon Glass } 1125dedc44b4SSimon Glass 1126dedc44b4SSimon Glass static int tegra_dp_channel_eq(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4], 1127dedc44b4SSimon Glass u32 pc[4], 1128dedc44b4SSimon Glass const struct tegra_dp_link_config *cfg) 1129dedc44b4SSimon Glass { 1130dedc44b4SSimon Glass u32 n_lanes = cfg->lane_count; 1131dedc44b4SSimon Glass u8 pc_supported = cfg->tps3_supported; 1132dedc44b4SSimon Glass int ret; 1133dedc44b4SSimon Glass u32 tp_src = training_pattern_2; 1134dedc44b4SSimon Glass 1135dedc44b4SSimon Glass if (pc_supported) 1136dedc44b4SSimon Glass tp_src = training_pattern_3; 1137dedc44b4SSimon Glass 1138dedc44b4SSimon Glass tegra_dp_tpg(dp, tp_src, n_lanes, cfg); 1139dedc44b4SSimon Glass 1140dedc44b4SSimon Glass ret = _tegra_dp_channel_eq(dp, pe, vs, pc, pc_supported, n_lanes, cfg); 1141dedc44b4SSimon Glass 1142dedc44b4SSimon Glass tegra_dp_tpg(dp, training_pattern_disabled, n_lanes, cfg); 1143dedc44b4SSimon Glass 1144dedc44b4SSimon Glass return ret; 1145dedc44b4SSimon Glass } 1146dedc44b4SSimon Glass 1147dedc44b4SSimon Glass static int _tegra_dp_clk_recovery(struct tegra_dp_priv *dp, u32 pe[4], 1148dedc44b4SSimon Glass u32 vs[4], u32 pc[4], u8 pc_supported, 1149dedc44b4SSimon Glass u32 n_lanes, 1150dedc44b4SSimon Glass const struct tegra_dp_link_config *cfg) 1151dedc44b4SSimon Glass { 1152dedc44b4SSimon Glass u32 vs_temp[4]; 1153dedc44b4SSimon Glass u32 retry_cnt = 0; 1154dedc44b4SSimon Glass 1155dedc44b4SSimon Glass do { 1156dedc44b4SSimon Glass tegra_dp_lt_config(dp, pe, vs, pc, cfg); 1157dedc44b4SSimon Glass tegra_dp_wait_aux_training(dp, true, cfg); 1158dedc44b4SSimon Glass 1159dedc44b4SSimon Glass if (tegra_dp_clock_recovery_status(dp, cfg)) 1160dedc44b4SSimon Glass return 0; 1161dedc44b4SSimon Glass 1162dedc44b4SSimon Glass memcpy(vs_temp, vs, sizeof(vs_temp)); 1163dedc44b4SSimon Glass tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported, cfg); 1164dedc44b4SSimon Glass 1165dedc44b4SSimon Glass if (memcmp(vs_temp, vs, sizeof(vs_temp))) 1166dedc44b4SSimon Glass retry_cnt = 0; 1167dedc44b4SSimon Glass else 1168dedc44b4SSimon Glass ++retry_cnt; 1169dedc44b4SSimon Glass } while (retry_cnt < 5); 1170dedc44b4SSimon Glass 1171dedc44b4SSimon Glass return -EIO; 1172dedc44b4SSimon Glass } 1173dedc44b4SSimon Glass 1174dedc44b4SSimon Glass static int tegra_dp_clk_recovery(struct tegra_dp_priv *dp, u32 pe[4], 1175dedc44b4SSimon Glass u32 vs[4], u32 pc[4], 1176dedc44b4SSimon Glass const struct tegra_dp_link_config *cfg) 1177dedc44b4SSimon Glass { 1178dedc44b4SSimon Glass u32 n_lanes = cfg->lane_count; 1179dedc44b4SSimon Glass u8 pc_supported = cfg->tps3_supported; 1180dedc44b4SSimon Glass int err; 1181dedc44b4SSimon Glass 1182dedc44b4SSimon Glass tegra_dp_tpg(dp, training_pattern_1, n_lanes, cfg); 1183dedc44b4SSimon Glass 1184dedc44b4SSimon Glass err = _tegra_dp_clk_recovery(dp, pe, vs, pc, pc_supported, n_lanes, 1185dedc44b4SSimon Glass cfg); 1186dedc44b4SSimon Glass if (err < 0) 1187dedc44b4SSimon Glass tegra_dp_tpg(dp, training_pattern_disabled, n_lanes, cfg); 1188dedc44b4SSimon Glass 1189dedc44b4SSimon Glass return err; 1190dedc44b4SSimon Glass } 1191dedc44b4SSimon Glass 1192dedc44b4SSimon Glass static int tegra_dc_dp_full_link_training(struct tegra_dp_priv *dp, 1193dedc44b4SSimon Glass const struct display_timing *timing, 1194dedc44b4SSimon Glass struct tegra_dp_link_config *cfg) 1195dedc44b4SSimon Glass { 1196*d7659212SSimon Glass struct udevice *sor = dp->sor; 1197dedc44b4SSimon Glass int err; 1198dedc44b4SSimon Glass u32 pe[4], vs[4], pc[4]; 1199dedc44b4SSimon Glass 1200dedc44b4SSimon Glass tegra_sor_precharge_lanes(sor, cfg); 1201dedc44b4SSimon Glass 1202dedc44b4SSimon Glass retry_cr: 1203dedc44b4SSimon Glass memset(pe, PREEMPHASIS_DISABLED, sizeof(pe)); 1204dedc44b4SSimon Glass memset(vs, DRIVECURRENT_LEVEL0, sizeof(vs)); 1205dedc44b4SSimon Glass memset(pc, POSTCURSOR2_LEVEL0, sizeof(pc)); 1206dedc44b4SSimon Glass 1207dedc44b4SSimon Glass err = tegra_dp_clk_recovery(dp, pe, vs, pc, cfg); 1208dedc44b4SSimon Glass if (err) { 1209dedc44b4SSimon Glass if (!tegra_dp_lower_link_config(dp, timing, cfg)) 1210dedc44b4SSimon Glass goto retry_cr; 1211dedc44b4SSimon Glass 1212dedc44b4SSimon Glass debug("dp: clk recovery failed\n"); 1213dedc44b4SSimon Glass goto fail; 1214dedc44b4SSimon Glass } 1215dedc44b4SSimon Glass 1216dedc44b4SSimon Glass err = tegra_dp_channel_eq(dp, pe, vs, pc, cfg); 1217dedc44b4SSimon Glass if (err) { 1218dedc44b4SSimon Glass if (!tegra_dp_lower_link_config(dp, timing, cfg)) 1219dedc44b4SSimon Glass goto retry_cr; 1220dedc44b4SSimon Glass 1221dedc44b4SSimon Glass debug("dp: channel equalization failed\n"); 1222dedc44b4SSimon Glass goto fail; 1223dedc44b4SSimon Glass } 1224dedc44b4SSimon Glass #ifdef DEBUG 1225dedc44b4SSimon Glass tegra_dc_dp_dump_link_cfg(dp, cfg); 1226dedc44b4SSimon Glass #endif 1227dedc44b4SSimon Glass return 0; 1228dedc44b4SSimon Glass 1229dedc44b4SSimon Glass fail: 1230dedc44b4SSimon Glass return err; 1231dedc44b4SSimon Glass } 1232dedc44b4SSimon Glass 123359dd5aa8SSimon Glass /* 123459dd5aa8SSimon Glass * All link training functions are ported from kernel dc driver. 123559dd5aa8SSimon Glass * See more details at drivers/video/tegra/dc/dp.c 123659dd5aa8SSimon Glass */ 123759dd5aa8SSimon Glass static int tegra_dc_dp_fast_link_training(struct tegra_dp_priv *dp, 123859dd5aa8SSimon Glass const struct tegra_dp_link_config *link_cfg, 1239*d7659212SSimon Glass struct udevice *sor) 124059dd5aa8SSimon Glass { 124159dd5aa8SSimon Glass u8 link_bw; 124259dd5aa8SSimon Glass u8 lane_count; 124359dd5aa8SSimon Glass u16 data16; 124459dd5aa8SSimon Glass u32 data32; 124559dd5aa8SSimon Glass u32 size; 124659dd5aa8SSimon Glass u32 status; 124759dd5aa8SSimon Glass int j; 124859dd5aa8SSimon Glass u32 mask = 0xffff >> ((4 - link_cfg->lane_count) * 4); 124959dd5aa8SSimon Glass 125059dd5aa8SSimon Glass tegra_dc_sor_set_lane_parm(sor, link_cfg); 125159dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_MAIN_LINK_CHANNEL_CODING_SET, 125259dd5aa8SSimon Glass DP_SET_ANSI_8B10B); 125359dd5aa8SSimon Glass 125459dd5aa8SSimon Glass /* Send TP1 */ 125559dd5aa8SSimon Glass tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_1, link_cfg); 125659dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 125759dd5aa8SSimon Glass DP_TRAINING_PATTERN_1); 125859dd5aa8SSimon Glass 125959dd5aa8SSimon Glass for (j = 0; j < link_cfg->lane_count; ++j) 126059dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24); 126159dd5aa8SSimon Glass udelay(520); 126259dd5aa8SSimon Glass 126359dd5aa8SSimon Glass size = sizeof(data16); 126459dd5aa8SSimon Glass tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, 126559dd5aa8SSimon Glass DP_LANE0_1_STATUS, (u8 *)&data16, &size, &status); 126659dd5aa8SSimon Glass status = mask & 0x1111; 126759dd5aa8SSimon Glass if ((data16 & status) != status) { 126859dd5aa8SSimon Glass debug("dp: Link training error for TP1 (%#x, status %#x)\n", 126959dd5aa8SSimon Glass data16, status); 127059dd5aa8SSimon Glass return -EFAULT; 127159dd5aa8SSimon Glass } 127259dd5aa8SSimon Glass 127359dd5aa8SSimon Glass /* enable ASSR */ 127459dd5aa8SSimon Glass tegra_dc_dp_set_assr(dp, sor, link_cfg->scramble_ena); 127559dd5aa8SSimon Glass tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_3, link_cfg); 127659dd5aa8SSimon Glass 127759dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 127859dd5aa8SSimon Glass link_cfg->link_bw == 20 ? 0x23 : 0x22); 127959dd5aa8SSimon Glass for (j = 0; j < link_cfg->lane_count; ++j) 128059dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24); 128159dd5aa8SSimon Glass udelay(520); 128259dd5aa8SSimon Glass 128359dd5aa8SSimon Glass size = sizeof(data32); 128459dd5aa8SSimon Glass tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, DP_LANE0_1_STATUS, 128559dd5aa8SSimon Glass (u8 *)&data32, &size, &status); 128659dd5aa8SSimon Glass if ((data32 & mask) != (0x7777 & mask)) { 128759dd5aa8SSimon Glass debug("dp: Link training error for TP2/3 (0x%x)\n", data32); 128859dd5aa8SSimon Glass return -EFAULT; 128959dd5aa8SSimon Glass } 129059dd5aa8SSimon Glass 129159dd5aa8SSimon Glass tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_disabled, 129259dd5aa8SSimon Glass link_cfg); 129359dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 0); 129459dd5aa8SSimon Glass 129559dd5aa8SSimon Glass if (tegra_dc_dp_link_trained(dp, link_cfg)) { 129659dd5aa8SSimon Glass tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count); 129759dd5aa8SSimon Glass debug("Fast link training failed, link bw %d, lane # %d\n", 129859dd5aa8SSimon Glass link_bw, lane_count); 129959dd5aa8SSimon Glass return -EFAULT; 130059dd5aa8SSimon Glass } 130159dd5aa8SSimon Glass 130259dd5aa8SSimon Glass debug("Fast link training succeeded, link bw %d, lane %d\n", 130359dd5aa8SSimon Glass link_cfg->link_bw, link_cfg->lane_count); 130459dd5aa8SSimon Glass 130559dd5aa8SSimon Glass return 0; 130659dd5aa8SSimon Glass } 130759dd5aa8SSimon Glass 1308dedc44b4SSimon Glass static int tegra_dp_do_link_training(struct tegra_dp_priv *dp, 1309dedc44b4SSimon Glass struct tegra_dp_link_config *link_cfg, 1310dedc44b4SSimon Glass const struct display_timing *timing, 1311*d7659212SSimon Glass struct udevice *sor) 131259dd5aa8SSimon Glass { 131359dd5aa8SSimon Glass u8 link_bw; 131459dd5aa8SSimon Glass u8 lane_count; 131559dd5aa8SSimon Glass int ret; 131659dd5aa8SSimon Glass 1317dedc44b4SSimon Glass if (DO_FAST_LINK_TRAINING) { 131859dd5aa8SSimon Glass ret = tegra_dc_dp_fast_link_training(dp, link_cfg, sor); 131959dd5aa8SSimon Glass if (ret) { 132059dd5aa8SSimon Glass debug("dp: fast link training failed\n"); 1321dedc44b4SSimon Glass } else { 1322dedc44b4SSimon Glass /* 1323dedc44b4SSimon Glass * set to a known-good drive setting if fast link 1324dedc44b4SSimon Glass * succeeded. Ignore any error. 1325dedc44b4SSimon Glass */ 1326dedc44b4SSimon Glass ret = tegra_dc_sor_set_voltage_swing(dp->sor, link_cfg); 1327dedc44b4SSimon Glass if (ret) 1328dedc44b4SSimon Glass debug("Failed to set voltage swing\n"); 1329dedc44b4SSimon Glass } 1330dedc44b4SSimon Glass } else { 1331dedc44b4SSimon Glass ret = -ENOSYS; 1332dedc44b4SSimon Glass } 1333dedc44b4SSimon Glass if (ret) { 1334dedc44b4SSimon Glass /* Try full link training then */ 1335dedc44b4SSimon Glass ret = tegra_dc_dp_full_link_training(dp, timing, link_cfg); 1336dedc44b4SSimon Glass if (ret) { 1337dedc44b4SSimon Glass debug("dp: full link training failed\n"); 133859dd5aa8SSimon Glass return ret; 133959dd5aa8SSimon Glass } 1340dedc44b4SSimon Glass } 134159dd5aa8SSimon Glass 134259dd5aa8SSimon Glass /* Everything is good; double check the link config */ 134359dd5aa8SSimon Glass tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count); 134459dd5aa8SSimon Glass 134559dd5aa8SSimon Glass if ((link_cfg->link_bw == link_bw) && 134659dd5aa8SSimon Glass (link_cfg->lane_count == lane_count)) 134759dd5aa8SSimon Glass return 0; 134859dd5aa8SSimon Glass else 134959dd5aa8SSimon Glass return -EFAULT; 135059dd5aa8SSimon Glass } 135159dd5aa8SSimon Glass 135259dd5aa8SSimon Glass static int tegra_dc_dp_explore_link_cfg(struct tegra_dp_priv *dp, 135359dd5aa8SSimon Glass struct tegra_dp_link_config *link_cfg, 1354*d7659212SSimon Glass struct udevice *sor, 135559dd5aa8SSimon Glass const struct display_timing *timing) 135659dd5aa8SSimon Glass { 135759dd5aa8SSimon Glass struct tegra_dp_link_config temp_cfg; 135859dd5aa8SSimon Glass 135959dd5aa8SSimon Glass if (!timing->pixelclock.typ || !timing->hactive.typ || 136059dd5aa8SSimon Glass !timing->vactive.typ) { 136159dd5aa8SSimon Glass debug("dp: error mode configuration"); 136259dd5aa8SSimon Glass return -EINVAL; 136359dd5aa8SSimon Glass } 136459dd5aa8SSimon Glass if (!link_cfg->max_link_bw || !link_cfg->max_lane_count) { 136559dd5aa8SSimon Glass debug("dp: error link configuration"); 136659dd5aa8SSimon Glass return -EINVAL; 136759dd5aa8SSimon Glass } 136859dd5aa8SSimon Glass 136959dd5aa8SSimon Glass link_cfg->is_valid = 0; 137059dd5aa8SSimon Glass 137159dd5aa8SSimon Glass memcpy(&temp_cfg, link_cfg, sizeof(temp_cfg)); 137259dd5aa8SSimon Glass 137359dd5aa8SSimon Glass temp_cfg.link_bw = temp_cfg.max_link_bw; 137459dd5aa8SSimon Glass temp_cfg.lane_count = temp_cfg.max_lane_count; 137559dd5aa8SSimon Glass 137659dd5aa8SSimon Glass /* 137759dd5aa8SSimon Glass * set to max link config 137859dd5aa8SSimon Glass */ 137959dd5aa8SSimon Glass if ((!tegra_dc_dp_calc_config(dp, timing, &temp_cfg)) && 1380dedc44b4SSimon Glass (!tegra_dp_link_config(dp, &temp_cfg)) && 1381dedc44b4SSimon Glass (!tegra_dp_do_link_training(dp, &temp_cfg, timing, sor))) 138259dd5aa8SSimon Glass /* the max link cfg is doable */ 138359dd5aa8SSimon Glass memcpy(link_cfg, &temp_cfg, sizeof(temp_cfg)); 138459dd5aa8SSimon Glass 138559dd5aa8SSimon Glass return link_cfg->is_valid ? 0 : -EFAULT; 138659dd5aa8SSimon Glass } 138759dd5aa8SSimon Glass 138859dd5aa8SSimon Glass static int tegra_dp_hpd_plug(struct tegra_dp_priv *dp) 138959dd5aa8SSimon Glass { 139059dd5aa8SSimon Glass const int vdd_to_hpd_delay_ms = 200; 139159dd5aa8SSimon Glass u32 val; 139259dd5aa8SSimon Glass ulong start; 139359dd5aa8SSimon Glass 139459dd5aa8SSimon Glass start = get_timer(0); 139559dd5aa8SSimon Glass do { 139659dd5aa8SSimon Glass val = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); 139759dd5aa8SSimon Glass if (val & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED) 139859dd5aa8SSimon Glass return 0; 139959dd5aa8SSimon Glass udelay(100); 140059dd5aa8SSimon Glass } while (get_timer(start) < vdd_to_hpd_delay_ms); 140159dd5aa8SSimon Glass 140259dd5aa8SSimon Glass return -EIO; 140359dd5aa8SSimon Glass } 140459dd5aa8SSimon Glass 1405dedc44b4SSimon Glass static int tegra_dc_dp_sink_out_of_sync(struct tegra_dp_priv *dp, u32 delay_ms) 1406dedc44b4SSimon Glass { 1407dedc44b4SSimon Glass u8 dpcd_data; 1408dedc44b4SSimon Glass int out_of_sync; 1409dedc44b4SSimon Glass int ret; 1410dedc44b4SSimon Glass 1411dedc44b4SSimon Glass debug("%s: delay=%d\n", __func__, delay_ms); 1412dedc44b4SSimon Glass mdelay(delay_ms); 1413dedc44b4SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_SINK_STATUS, &dpcd_data); 1414dedc44b4SSimon Glass if (ret) 1415dedc44b4SSimon Glass return ret; 1416dedc44b4SSimon Glass 1417dedc44b4SSimon Glass out_of_sync = !(dpcd_data & DP_SINK_STATUS_PORT0_IN_SYNC); 1418dedc44b4SSimon Glass if (out_of_sync) 1419dedc44b4SSimon Glass debug("SINK receive port 0 out of sync, data=%x\n", dpcd_data); 1420dedc44b4SSimon Glass else 1421dedc44b4SSimon Glass debug("SINK is in synchronization\n"); 1422dedc44b4SSimon Glass 1423dedc44b4SSimon Glass return out_of_sync; 1424dedc44b4SSimon Glass } 1425dedc44b4SSimon Glass 1426dedc44b4SSimon Glass static int tegra_dc_dp_check_sink(struct tegra_dp_priv *dp, 1427dedc44b4SSimon Glass struct tegra_dp_link_config *link_cfg, 1428dedc44b4SSimon Glass const struct display_timing *timing) 1429dedc44b4SSimon Glass { 1430dedc44b4SSimon Glass const int max_retry = 5; 1431dedc44b4SSimon Glass int delay_frame; 1432dedc44b4SSimon Glass int retries; 1433dedc44b4SSimon Glass 1434dedc44b4SSimon Glass /* 1435dedc44b4SSimon Glass * DP TCON may skip some main stream frames, thus we need to wait 1436dedc44b4SSimon Glass * some delay before reading the DPCD SINK STATUS register, starting 1437dedc44b4SSimon Glass * from 5 1438dedc44b4SSimon Glass */ 1439dedc44b4SSimon Glass delay_frame = 5; 1440dedc44b4SSimon Glass 1441dedc44b4SSimon Glass retries = max_retry; 1442dedc44b4SSimon Glass do { 1443dedc44b4SSimon Glass int ret; 1444dedc44b4SSimon Glass 1445dedc44b4SSimon Glass if (!tegra_dc_dp_sink_out_of_sync(dp, link_cfg->frame_in_ms * 1446dedc44b4SSimon Glass delay_frame)) 1447dedc44b4SSimon Glass return 0; 1448dedc44b4SSimon Glass 1449dedc44b4SSimon Glass debug("%s: retries left %d\n", __func__, retries); 1450dedc44b4SSimon Glass if (!retries--) { 1451dedc44b4SSimon Glass printf("DP: Out of sync after %d retries\n", max_retry); 1452dedc44b4SSimon Glass return -EIO; 1453dedc44b4SSimon Glass } 1454*d7659212SSimon Glass ret = tegra_dc_sor_detach(dp->dc_dev, dp->sor); 1455dedc44b4SSimon Glass if (ret) 1456dedc44b4SSimon Glass return ret; 1457dedc44b4SSimon Glass if (tegra_dc_dp_explore_link_cfg(dp, link_cfg, dp->sor, 1458dedc44b4SSimon Glass timing)) { 1459dedc44b4SSimon Glass debug("dp: %s: error to configure link\n", __func__); 1460dedc44b4SSimon Glass continue; 1461dedc44b4SSimon Glass } 1462dedc44b4SSimon Glass 1463dedc44b4SSimon Glass tegra_dc_sor_set_power_state(dp->sor, 1); 1464*d7659212SSimon Glass tegra_dc_sor_attach(dp->dc_dev, dp->sor, link_cfg, timing); 1465dedc44b4SSimon Glass 1466dedc44b4SSimon Glass /* Increase delay_frame for next try in case the sink is 1467dedc44b4SSimon Glass skipping more frames */ 1468dedc44b4SSimon Glass delay_frame += 10; 1469dedc44b4SSimon Glass } while (1); 1470dedc44b4SSimon Glass } 1471dedc44b4SSimon Glass 147259dd5aa8SSimon Glass int tegra_dp_enable(struct udevice *dev, int panel_bpp, 147359dd5aa8SSimon Glass const struct display_timing *timing) 147459dd5aa8SSimon Glass { 147559dd5aa8SSimon Glass struct tegra_dp_priv *priv = dev_get_priv(dev); 147659dd5aa8SSimon Glass struct tegra_dp_link_config slink_cfg, *link_cfg = &slink_cfg; 1477*d7659212SSimon Glass struct udevice *sor; 147859dd5aa8SSimon Glass int data; 147959dd5aa8SSimon Glass int retry; 148059dd5aa8SSimon Glass int ret; 148159dd5aa8SSimon Glass 148259dd5aa8SSimon Glass memset(link_cfg, '\0', sizeof(*link_cfg)); 148359dd5aa8SSimon Glass link_cfg->is_valid = 0; 148459dd5aa8SSimon Glass link_cfg->scramble_ena = 1; 148559dd5aa8SSimon Glass 148659dd5aa8SSimon Glass tegra_dc_dpaux_enable(priv); 148759dd5aa8SSimon Glass 148859dd5aa8SSimon Glass if (tegra_dp_hpd_plug(priv) < 0) { 148959dd5aa8SSimon Glass debug("dp: hpd plug failed\n"); 149059dd5aa8SSimon Glass return -EIO; 149159dd5aa8SSimon Glass } 149259dd5aa8SSimon Glass 149359dd5aa8SSimon Glass link_cfg->bits_per_pixel = panel_bpp; 149459dd5aa8SSimon Glass if (tegra_dc_dp_init_max_link_cfg(timing, priv, link_cfg)) { 149559dd5aa8SSimon Glass debug("dp: failed to init link configuration\n"); 149659dd5aa8SSimon Glass return -ENOLINK; 149759dd5aa8SSimon Glass } 149859dd5aa8SSimon Glass 1499*d7659212SSimon Glass ret = uclass_first_device(UCLASS_VIDEO_BRIDGE, &sor); 1500*d7659212SSimon Glass if (ret || !sor) { 1501*d7659212SSimon Glass debug("dp: failed to find SOR device: ret=%d\n", ret); 150259dd5aa8SSimon Glass return ret; 1503*d7659212SSimon Glass } 150459dd5aa8SSimon Glass priv->sor = sor; 150559dd5aa8SSimon Glass ret = tegra_dc_sor_enable_dp(sor, link_cfg); 150659dd5aa8SSimon Glass if (ret) 150759dd5aa8SSimon Glass return ret; 150859dd5aa8SSimon Glass 150959dd5aa8SSimon Glass tegra_dc_sor_set_panel_power(sor, 1); 151059dd5aa8SSimon Glass 151159dd5aa8SSimon Glass /* Write power on to DPCD */ 151259dd5aa8SSimon Glass data = DP_SET_POWER_D0; 151359dd5aa8SSimon Glass retry = 0; 151459dd5aa8SSimon Glass do { 151559dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_write(priv, DP_SET_POWER, data); 151659dd5aa8SSimon Glass } while ((retry++ < DP_POWER_ON_MAX_TRIES) && ret); 151759dd5aa8SSimon Glass 151859dd5aa8SSimon Glass if (ret || retry >= DP_POWER_ON_MAX_TRIES) { 151959dd5aa8SSimon Glass debug("dp: failed to power on panel (0x%x)\n", ret); 152059dd5aa8SSimon Glass return -ENETUNREACH; 152159dd5aa8SSimon Glass goto error_enable; 152259dd5aa8SSimon Glass } 152359dd5aa8SSimon Glass 152459dd5aa8SSimon Glass /* Confirm DP plugging status */ 152559dd5aa8SSimon Glass if (!(tegra_dpaux_readl(priv, DPAUX_DP_AUXSTAT) & 152659dd5aa8SSimon Glass DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) { 152759dd5aa8SSimon Glass debug("dp: could not detect HPD\n"); 152859dd5aa8SSimon Glass return -ENXIO; 152959dd5aa8SSimon Glass } 153059dd5aa8SSimon Glass 153159dd5aa8SSimon Glass /* Check DP version */ 153259dd5aa8SSimon Glass if (tegra_dc_dp_dpcd_read(priv, DP_DPCD_REV, &priv->revision)) { 153359dd5aa8SSimon Glass debug("dp: failed to read the revision number from sink\n"); 153459dd5aa8SSimon Glass return -EIO; 153559dd5aa8SSimon Glass } 153659dd5aa8SSimon Glass 153759dd5aa8SSimon Glass if (tegra_dc_dp_explore_link_cfg(priv, link_cfg, sor, timing)) { 153859dd5aa8SSimon Glass debug("dp: error configuring link\n"); 153959dd5aa8SSimon Glass return -ENOMEDIUM; 154059dd5aa8SSimon Glass } 154159dd5aa8SSimon Glass 154259dd5aa8SSimon Glass tegra_dc_sor_set_power_state(sor, 1); 1543*d7659212SSimon Glass ret = tegra_dc_sor_attach(priv->dc_dev, sor, link_cfg, timing); 154459dd5aa8SSimon Glass if (ret && ret != -EEXIST) 154559dd5aa8SSimon Glass return ret; 154659dd5aa8SSimon Glass 1547dedc44b4SSimon Glass /* 1548dedc44b4SSimon Glass * This takes a long time, but can apparently resolve a failure to 1549dedc44b4SSimon Glass * bring up the display correctly. 1550dedc44b4SSimon Glass */ 1551dedc44b4SSimon Glass if (0) { 1552dedc44b4SSimon Glass ret = tegra_dc_dp_check_sink(priv, link_cfg, timing); 1553dedc44b4SSimon Glass if (ret) 1554dedc44b4SSimon Glass return ret; 1555dedc44b4SSimon Glass } 1556dedc44b4SSimon Glass 155759dd5aa8SSimon Glass /* Power down the unused lanes to save power - a few hundred mW */ 155859dd5aa8SSimon Glass tegra_dc_sor_power_down_unused_lanes(sor, link_cfg); 155959dd5aa8SSimon Glass 1560*d7659212SSimon Glass ret = video_bridge_set_backlight(sor, 80); 1561*d7659212SSimon Glass if (ret) { 1562*d7659212SSimon Glass debug("dp: failed to set backlight\n"); 1563*d7659212SSimon Glass return ret; 1564*d7659212SSimon Glass } 1565*d7659212SSimon Glass 156659dd5aa8SSimon Glass priv->enabled = true; 156759dd5aa8SSimon Glass error_enable: 156859dd5aa8SSimon Glass return 0; 156959dd5aa8SSimon Glass } 157059dd5aa8SSimon Glass 157159dd5aa8SSimon Glass static int tegra_dp_ofdata_to_platdata(struct udevice *dev) 157259dd5aa8SSimon Glass { 157359dd5aa8SSimon Glass struct tegra_dp_plat *plat = dev_get_platdata(dev); 157459dd5aa8SSimon Glass 15754e9838c1SSimon Glass plat->base = dev_get_addr(dev); 157659dd5aa8SSimon Glass 157759dd5aa8SSimon Glass return 0; 157859dd5aa8SSimon Glass } 157959dd5aa8SSimon Glass 158059dd5aa8SSimon Glass static int tegra_dp_read_edid(struct udevice *dev, u8 *buf, int buf_size) 158159dd5aa8SSimon Glass { 158259dd5aa8SSimon Glass struct tegra_dp_priv *priv = dev_get_priv(dev); 158359dd5aa8SSimon Glass const int tegra_edid_i2c_address = 0x50; 158459dd5aa8SSimon Glass u32 aux_stat = 0; 158559dd5aa8SSimon Glass 158659dd5aa8SSimon Glass tegra_dc_dpaux_enable(priv); 158759dd5aa8SSimon Glass 158859dd5aa8SSimon Glass return tegra_dc_i2c_aux_read(priv, tegra_edid_i2c_address, 0, buf, 158959dd5aa8SSimon Glass buf_size, &aux_stat); 159059dd5aa8SSimon Glass } 159159dd5aa8SSimon Glass 15922dcf1433SSimon Glass static const struct dm_display_ops dp_tegra_ops = { 159359dd5aa8SSimon Glass .read_edid = tegra_dp_read_edid, 159459dd5aa8SSimon Glass .enable = tegra_dp_enable, 159559dd5aa8SSimon Glass }; 159659dd5aa8SSimon Glass 159759dd5aa8SSimon Glass static int dp_tegra_probe(struct udevice *dev) 159859dd5aa8SSimon Glass { 159959dd5aa8SSimon Glass struct tegra_dp_plat *plat = dev_get_platdata(dev); 160059dd5aa8SSimon Glass struct tegra_dp_priv *priv = dev_get_priv(dev); 1601*d7659212SSimon Glass struct display_plat *disp_uc_plat = dev_get_uclass_platdata(dev); 160259dd5aa8SSimon Glass 160359dd5aa8SSimon Glass priv->regs = (struct dpaux_ctlr *)plat->base; 160459dd5aa8SSimon Glass priv->enabled = false; 160559dd5aa8SSimon Glass 1606*d7659212SSimon Glass /* Remember the display controller that is sending us video */ 1607*d7659212SSimon Glass priv->dc_dev = disp_uc_plat->src_dev; 1608*d7659212SSimon Glass 160959dd5aa8SSimon Glass return 0; 161059dd5aa8SSimon Glass } 161159dd5aa8SSimon Glass 161259dd5aa8SSimon Glass static const struct udevice_id tegra_dp_ids[] = { 161359dd5aa8SSimon Glass { .compatible = "nvidia,tegra124-dpaux" }, 161459dd5aa8SSimon Glass { } 161559dd5aa8SSimon Glass }; 161659dd5aa8SSimon Glass 161759dd5aa8SSimon Glass U_BOOT_DRIVER(dp_tegra) = { 161859dd5aa8SSimon Glass .name = "dpaux_tegra", 16192dcf1433SSimon Glass .id = UCLASS_DISPLAY, 162059dd5aa8SSimon Glass .of_match = tegra_dp_ids, 162159dd5aa8SSimon Glass .ofdata_to_platdata = tegra_dp_ofdata_to_platdata, 162259dd5aa8SSimon Glass .probe = dp_tegra_probe, 162359dd5aa8SSimon Glass .ops = &dp_tegra_ops, 162459dd5aa8SSimon Glass .priv_auto_alloc_size = sizeof(struct tegra_dp_priv), 162559dd5aa8SSimon Glass .platdata_auto_alloc_size = sizeof(struct tegra_dp_plat), 162659dd5aa8SSimon Glass }; 1627