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