1*59dd5aa8SSimon Glass /* 2*59dd5aa8SSimon Glass * Copyright (c) 2011-2013, NVIDIA Corporation. 3*59dd5aa8SSimon Glass * Copyright 2014 Google Inc. 4*59dd5aa8SSimon Glass * 5*59dd5aa8SSimon Glass * SPDX-License-Identifier: GPL-2.0 6*59dd5aa8SSimon Glass */ 7*59dd5aa8SSimon Glass 8*59dd5aa8SSimon Glass #include <common.h> 9*59dd5aa8SSimon Glass #include <displayport.h> 10*59dd5aa8SSimon Glass #include <dm.h> 11*59dd5aa8SSimon Glass #include <div64.h> 12*59dd5aa8SSimon Glass #include <errno.h> 13*59dd5aa8SSimon Glass #include <fdtdec.h> 14*59dd5aa8SSimon Glass #include <asm/io.h> 15*59dd5aa8SSimon Glass #include <asm/arch-tegra/dc.h> 16*59dd5aa8SSimon Glass #include "displayport.h" 17*59dd5aa8SSimon Glass #include "edid.h" 18*59dd5aa8SSimon Glass #include "sor.h" 19*59dd5aa8SSimon Glass 20*59dd5aa8SSimon Glass DECLARE_GLOBAL_DATA_PTR; 21*59dd5aa8SSimon Glass 22*59dd5aa8SSimon Glass struct tegra_dp_plat { 23*59dd5aa8SSimon Glass ulong base; 24*59dd5aa8SSimon Glass }; 25*59dd5aa8SSimon Glass 26*59dd5aa8SSimon Glass struct tegra_dp_priv { 27*59dd5aa8SSimon Glass struct dpaux_ctlr *regs; 28*59dd5aa8SSimon Glass struct tegra_dc_sor_data *sor; 29*59dd5aa8SSimon Glass u8 revision; 30*59dd5aa8SSimon Glass int enabled; 31*59dd5aa8SSimon Glass }; 32*59dd5aa8SSimon Glass 33*59dd5aa8SSimon Glass struct tegra_dp_priv dp_data; 34*59dd5aa8SSimon Glass 35*59dd5aa8SSimon Glass static inline u32 tegra_dpaux_readl(struct tegra_dp_priv *dp, u32 reg) 36*59dd5aa8SSimon Glass { 37*59dd5aa8SSimon Glass return readl((u32 *)dp->regs + reg); 38*59dd5aa8SSimon Glass } 39*59dd5aa8SSimon Glass 40*59dd5aa8SSimon Glass static inline void tegra_dpaux_writel(struct tegra_dp_priv *dp, u32 reg, 41*59dd5aa8SSimon Glass u32 val) 42*59dd5aa8SSimon Glass { 43*59dd5aa8SSimon Glass writel(val, (u32 *)dp->regs + reg); 44*59dd5aa8SSimon Glass } 45*59dd5aa8SSimon Glass 46*59dd5aa8SSimon Glass static inline u32 tegra_dc_dpaux_poll_register(struct tegra_dp_priv *dp, 47*59dd5aa8SSimon Glass u32 reg, u32 mask, u32 exp_val, 48*59dd5aa8SSimon Glass u32 poll_interval_us, 49*59dd5aa8SSimon Glass u32 timeout_us) 50*59dd5aa8SSimon Glass { 51*59dd5aa8SSimon Glass u32 reg_val = 0; 52*59dd5aa8SSimon Glass u32 temp = timeout_us; 53*59dd5aa8SSimon Glass 54*59dd5aa8SSimon Glass do { 55*59dd5aa8SSimon Glass udelay(poll_interval_us); 56*59dd5aa8SSimon Glass reg_val = tegra_dpaux_readl(dp, reg); 57*59dd5aa8SSimon Glass if (timeout_us > poll_interval_us) 58*59dd5aa8SSimon Glass timeout_us -= poll_interval_us; 59*59dd5aa8SSimon Glass else 60*59dd5aa8SSimon Glass break; 61*59dd5aa8SSimon Glass } while ((reg_val & mask) != exp_val); 62*59dd5aa8SSimon Glass 63*59dd5aa8SSimon Glass if ((reg_val & mask) == exp_val) 64*59dd5aa8SSimon Glass return 0; /* success */ 65*59dd5aa8SSimon Glass debug("dpaux_poll_register 0x%x: timeout: (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n", 66*59dd5aa8SSimon Glass reg, reg_val, mask, exp_val); 67*59dd5aa8SSimon Glass return temp; 68*59dd5aa8SSimon Glass } 69*59dd5aa8SSimon Glass 70*59dd5aa8SSimon Glass static inline int tegra_dpaux_wait_transaction(struct tegra_dp_priv *dp) 71*59dd5aa8SSimon Glass { 72*59dd5aa8SSimon Glass /* According to DP spec, each aux transaction needs to finish 73*59dd5aa8SSimon Glass within 40ms. */ 74*59dd5aa8SSimon Glass if (tegra_dc_dpaux_poll_register(dp, DPAUX_DP_AUXCTL, 75*59dd5aa8SSimon Glass DPAUX_DP_AUXCTL_TRANSACTREQ_MASK, 76*59dd5aa8SSimon Glass DPAUX_DP_AUXCTL_TRANSACTREQ_DONE, 77*59dd5aa8SSimon Glass 100, DP_AUX_TIMEOUT_MS * 1000) != 0) { 78*59dd5aa8SSimon Glass debug("dp: DPAUX transaction timeout\n"); 79*59dd5aa8SSimon Glass return -1; 80*59dd5aa8SSimon Glass } 81*59dd5aa8SSimon Glass return 0; 82*59dd5aa8SSimon Glass } 83*59dd5aa8SSimon Glass 84*59dd5aa8SSimon Glass static int tegra_dc_dpaux_write_chunk(struct tegra_dp_priv *dp, u32 cmd, 85*59dd5aa8SSimon Glass u32 addr, u8 *data, u32 *size, 86*59dd5aa8SSimon Glass u32 *aux_stat) 87*59dd5aa8SSimon Glass { 88*59dd5aa8SSimon Glass int i; 89*59dd5aa8SSimon Glass u32 reg_val; 90*59dd5aa8SSimon Glass u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; 91*59dd5aa8SSimon Glass u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; 92*59dd5aa8SSimon Glass u32 temp_data; 93*59dd5aa8SSimon Glass 94*59dd5aa8SSimon Glass if (*size > DP_AUX_MAX_BYTES) 95*59dd5aa8SSimon Glass return -1; /* only write one chunk of data */ 96*59dd5aa8SSimon Glass 97*59dd5aa8SSimon Glass /* Make sure the command is write command */ 98*59dd5aa8SSimon Glass switch (cmd) { 99*59dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_I2CWR: 100*59dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_MOTWR: 101*59dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_AUXWR: 102*59dd5aa8SSimon Glass break; 103*59dd5aa8SSimon Glass default: 104*59dd5aa8SSimon Glass debug("dp: aux write cmd 0x%x is invalid\n", cmd); 105*59dd5aa8SSimon Glass return -EINVAL; 106*59dd5aa8SSimon Glass } 107*59dd5aa8SSimon Glass 108*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr); 109*59dd5aa8SSimon Glass for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) { 110*59dd5aa8SSimon Glass memcpy(&temp_data, data, 4); 111*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXDATA_WRITE_W(i), temp_data); 112*59dd5aa8SSimon Glass data += 4; 113*59dd5aa8SSimon Glass } 114*59dd5aa8SSimon Glass 115*59dd5aa8SSimon Glass reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL); 116*59dd5aa8SSimon Glass reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK; 117*59dd5aa8SSimon Glass reg_val |= cmd; 118*59dd5aa8SSimon Glass reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD; 119*59dd5aa8SSimon Glass reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT); 120*59dd5aa8SSimon Glass 121*59dd5aa8SSimon Glass while ((timeout_retries > 0) && (defer_retries > 0)) { 122*59dd5aa8SSimon Glass if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) || 123*59dd5aa8SSimon Glass (defer_retries != DP_AUX_DEFER_MAX_TRIES)) 124*59dd5aa8SSimon Glass udelay(1); 125*59dd5aa8SSimon Glass 126*59dd5aa8SSimon Glass reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING; 127*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val); 128*59dd5aa8SSimon Glass 129*59dd5aa8SSimon Glass if (tegra_dpaux_wait_transaction(dp)) 130*59dd5aa8SSimon Glass debug("dp: aux write transaction timeout\n"); 131*59dd5aa8SSimon Glass 132*59dd5aa8SSimon Glass *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); 133*59dd5aa8SSimon Glass 134*59dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) || 135*59dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) || 136*59dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) || 137*59dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) { 138*59dd5aa8SSimon Glass if (timeout_retries-- > 0) { 139*59dd5aa8SSimon Glass debug("dp: aux write retry (0x%x) -- %d\n", 140*59dd5aa8SSimon Glass *aux_stat, timeout_retries); 141*59dd5aa8SSimon Glass /* clear the error bits */ 142*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, 143*59dd5aa8SSimon Glass *aux_stat); 144*59dd5aa8SSimon Glass continue; 145*59dd5aa8SSimon Glass } else { 146*59dd5aa8SSimon Glass debug("dp: aux write got error (0x%x)\n", 147*59dd5aa8SSimon Glass *aux_stat); 148*59dd5aa8SSimon Glass return -ETIMEDOUT; 149*59dd5aa8SSimon Glass } 150*59dd5aa8SSimon Glass } 151*59dd5aa8SSimon Glass 152*59dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) || 153*59dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) { 154*59dd5aa8SSimon Glass if (defer_retries-- > 0) { 155*59dd5aa8SSimon Glass debug("dp: aux write defer (0x%x) -- %d\n", 156*59dd5aa8SSimon Glass *aux_stat, defer_retries); 157*59dd5aa8SSimon Glass /* clear the error bits */ 158*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, 159*59dd5aa8SSimon Glass *aux_stat); 160*59dd5aa8SSimon Glass continue; 161*59dd5aa8SSimon Glass } else { 162*59dd5aa8SSimon Glass debug("dp: aux write defer exceeds max retries (0x%x)\n", 163*59dd5aa8SSimon Glass *aux_stat); 164*59dd5aa8SSimon Glass return -ETIMEDOUT; 165*59dd5aa8SSimon Glass } 166*59dd5aa8SSimon Glass } 167*59dd5aa8SSimon Glass 168*59dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) == 169*59dd5aa8SSimon Glass DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) { 170*59dd5aa8SSimon Glass *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK); 171*59dd5aa8SSimon Glass return 0; 172*59dd5aa8SSimon Glass } else { 173*59dd5aa8SSimon Glass debug("dp: aux write failed (0x%x)\n", *aux_stat); 174*59dd5aa8SSimon Glass return -EIO; 175*59dd5aa8SSimon Glass } 176*59dd5aa8SSimon Glass } 177*59dd5aa8SSimon Glass /* Should never come to here */ 178*59dd5aa8SSimon Glass return -EIO; 179*59dd5aa8SSimon Glass } 180*59dd5aa8SSimon Glass 181*59dd5aa8SSimon Glass static int tegra_dc_dpaux_read_chunk(struct tegra_dp_priv *dp, u32 cmd, 182*59dd5aa8SSimon Glass u32 addr, u8 *data, u32 *size, 183*59dd5aa8SSimon Glass u32 *aux_stat) 184*59dd5aa8SSimon Glass { 185*59dd5aa8SSimon Glass u32 reg_val; 186*59dd5aa8SSimon Glass u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; 187*59dd5aa8SSimon Glass u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; 188*59dd5aa8SSimon Glass 189*59dd5aa8SSimon Glass if (*size > DP_AUX_MAX_BYTES) { 190*59dd5aa8SSimon Glass debug("only read one chunk\n"); 191*59dd5aa8SSimon Glass return -EIO; /* only read one chunk */ 192*59dd5aa8SSimon Glass } 193*59dd5aa8SSimon Glass 194*59dd5aa8SSimon Glass /* Check to make sure the command is read command */ 195*59dd5aa8SSimon Glass switch (cmd) { 196*59dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_I2CRD: 197*59dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT: 198*59dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_MOTRD: 199*59dd5aa8SSimon Glass case DPAUX_DP_AUXCTL_CMD_AUXRD: 200*59dd5aa8SSimon Glass break; 201*59dd5aa8SSimon Glass default: 202*59dd5aa8SSimon Glass debug("dp: aux read cmd 0x%x is invalid\n", cmd); 203*59dd5aa8SSimon Glass return -EIO; 204*59dd5aa8SSimon Glass } 205*59dd5aa8SSimon Glass 206*59dd5aa8SSimon Glass *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); 207*59dd5aa8SSimon Glass if (!(*aux_stat & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) { 208*59dd5aa8SSimon Glass debug("dp: HPD is not detected\n"); 209*59dd5aa8SSimon Glass return -EIO; 210*59dd5aa8SSimon Glass } 211*59dd5aa8SSimon Glass 212*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr); 213*59dd5aa8SSimon Glass 214*59dd5aa8SSimon Glass reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL); 215*59dd5aa8SSimon Glass reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK; 216*59dd5aa8SSimon Glass reg_val |= cmd; 217*59dd5aa8SSimon Glass reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD; 218*59dd5aa8SSimon Glass reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT); 219*59dd5aa8SSimon Glass while ((timeout_retries > 0) && (defer_retries > 0)) { 220*59dd5aa8SSimon Glass if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) || 221*59dd5aa8SSimon Glass (defer_retries != DP_AUX_DEFER_MAX_TRIES)) 222*59dd5aa8SSimon Glass udelay(DP_DPCP_RETRY_SLEEP_NS * 2); 223*59dd5aa8SSimon Glass 224*59dd5aa8SSimon Glass reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING; 225*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val); 226*59dd5aa8SSimon Glass 227*59dd5aa8SSimon Glass if (tegra_dpaux_wait_transaction(dp)) 228*59dd5aa8SSimon Glass debug("dp: aux read transaction timeout\n"); 229*59dd5aa8SSimon Glass 230*59dd5aa8SSimon Glass *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); 231*59dd5aa8SSimon Glass 232*59dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) || 233*59dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) || 234*59dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) || 235*59dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) { 236*59dd5aa8SSimon Glass if (timeout_retries-- > 0) { 237*59dd5aa8SSimon Glass debug("dp: aux read retry (0x%x) -- %d\n", 238*59dd5aa8SSimon Glass *aux_stat, timeout_retries); 239*59dd5aa8SSimon Glass /* clear the error bits */ 240*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, 241*59dd5aa8SSimon Glass *aux_stat); 242*59dd5aa8SSimon Glass continue; /* retry */ 243*59dd5aa8SSimon Glass } else { 244*59dd5aa8SSimon Glass debug("dp: aux read got error (0x%x)\n", 245*59dd5aa8SSimon Glass *aux_stat); 246*59dd5aa8SSimon Glass return -ETIMEDOUT; 247*59dd5aa8SSimon Glass } 248*59dd5aa8SSimon Glass } 249*59dd5aa8SSimon Glass 250*59dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) || 251*59dd5aa8SSimon Glass (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) { 252*59dd5aa8SSimon Glass if (defer_retries-- > 0) { 253*59dd5aa8SSimon Glass debug("dp: aux read defer (0x%x) -- %d\n", 254*59dd5aa8SSimon Glass *aux_stat, defer_retries); 255*59dd5aa8SSimon Glass /* clear the error bits */ 256*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, 257*59dd5aa8SSimon Glass *aux_stat); 258*59dd5aa8SSimon Glass continue; 259*59dd5aa8SSimon Glass } else { 260*59dd5aa8SSimon Glass debug("dp: aux read defer exceeds max retries (0x%x)\n", 261*59dd5aa8SSimon Glass *aux_stat); 262*59dd5aa8SSimon Glass return -ETIMEDOUT; 263*59dd5aa8SSimon Glass } 264*59dd5aa8SSimon Glass } 265*59dd5aa8SSimon Glass 266*59dd5aa8SSimon Glass if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) == 267*59dd5aa8SSimon Glass DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) { 268*59dd5aa8SSimon Glass int i; 269*59dd5aa8SSimon Glass u32 temp_data[4]; 270*59dd5aa8SSimon Glass 271*59dd5aa8SSimon Glass for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) 272*59dd5aa8SSimon Glass temp_data[i] = tegra_dpaux_readl(dp, 273*59dd5aa8SSimon Glass DPAUX_DP_AUXDATA_READ_W(i)); 274*59dd5aa8SSimon Glass 275*59dd5aa8SSimon Glass *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK); 276*59dd5aa8SSimon Glass memcpy(data, temp_data, *size); 277*59dd5aa8SSimon Glass 278*59dd5aa8SSimon Glass return 0; 279*59dd5aa8SSimon Glass } else { 280*59dd5aa8SSimon Glass debug("dp: aux read failed (0x%x\n", *aux_stat); 281*59dd5aa8SSimon Glass return -EIO; 282*59dd5aa8SSimon Glass } 283*59dd5aa8SSimon Glass } 284*59dd5aa8SSimon Glass /* Should never come to here */ 285*59dd5aa8SSimon Glass debug("%s: can't\n", __func__); 286*59dd5aa8SSimon Glass 287*59dd5aa8SSimon Glass return -EIO; 288*59dd5aa8SSimon Glass } 289*59dd5aa8SSimon Glass 290*59dd5aa8SSimon Glass static int tegra_dc_dpaux_read(struct tegra_dp_priv *dp, u32 cmd, u32 addr, 291*59dd5aa8SSimon Glass u8 *data, u32 *size, u32 *aux_stat) 292*59dd5aa8SSimon Glass { 293*59dd5aa8SSimon Glass u32 finished = 0; 294*59dd5aa8SSimon Glass u32 cur_size; 295*59dd5aa8SSimon Glass int ret = 0; 296*59dd5aa8SSimon Glass 297*59dd5aa8SSimon Glass do { 298*59dd5aa8SSimon Glass cur_size = *size - finished; 299*59dd5aa8SSimon Glass if (cur_size > DP_AUX_MAX_BYTES) 300*59dd5aa8SSimon Glass cur_size = DP_AUX_MAX_BYTES; 301*59dd5aa8SSimon Glass 302*59dd5aa8SSimon Glass ret = tegra_dc_dpaux_read_chunk(dp, cmd, addr, 303*59dd5aa8SSimon Glass data, &cur_size, aux_stat); 304*59dd5aa8SSimon Glass if (ret) 305*59dd5aa8SSimon Glass break; 306*59dd5aa8SSimon Glass 307*59dd5aa8SSimon Glass /* cur_size should be the real size returned */ 308*59dd5aa8SSimon Glass addr += cur_size; 309*59dd5aa8SSimon Glass data += cur_size; 310*59dd5aa8SSimon Glass finished += cur_size; 311*59dd5aa8SSimon Glass 312*59dd5aa8SSimon Glass } while (*size > finished); 313*59dd5aa8SSimon Glass *size = finished; 314*59dd5aa8SSimon Glass 315*59dd5aa8SSimon Glass return ret; 316*59dd5aa8SSimon Glass } 317*59dd5aa8SSimon Glass 318*59dd5aa8SSimon Glass static int tegra_dc_dp_dpcd_read(struct tegra_dp_priv *dp, u32 cmd, 319*59dd5aa8SSimon Glass u8 *data_ptr) 320*59dd5aa8SSimon Glass { 321*59dd5aa8SSimon Glass u32 size = 1; 322*59dd5aa8SSimon Glass u32 status = 0; 323*59dd5aa8SSimon Glass int ret; 324*59dd5aa8SSimon Glass 325*59dd5aa8SSimon Glass ret = tegra_dc_dpaux_read_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, 326*59dd5aa8SSimon Glass cmd, data_ptr, &size, &status); 327*59dd5aa8SSimon Glass if (ret) { 328*59dd5aa8SSimon Glass debug("dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n", 329*59dd5aa8SSimon Glass cmd, status); 330*59dd5aa8SSimon Glass } 331*59dd5aa8SSimon Glass 332*59dd5aa8SSimon Glass return ret; 333*59dd5aa8SSimon Glass } 334*59dd5aa8SSimon Glass 335*59dd5aa8SSimon Glass static int tegra_dc_dp_dpcd_write(struct tegra_dp_priv *dp, u32 cmd, 336*59dd5aa8SSimon Glass u8 data) 337*59dd5aa8SSimon Glass { 338*59dd5aa8SSimon Glass u32 size = 1; 339*59dd5aa8SSimon Glass u32 status = 0; 340*59dd5aa8SSimon Glass int ret; 341*59dd5aa8SSimon Glass 342*59dd5aa8SSimon Glass ret = tegra_dc_dpaux_write_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXWR, 343*59dd5aa8SSimon Glass cmd, &data, &size, &status); 344*59dd5aa8SSimon Glass if (ret) { 345*59dd5aa8SSimon Glass debug("dp: Failed to write DPCD data. CMD 0x%x, Status 0x%x\n", 346*59dd5aa8SSimon Glass cmd, status); 347*59dd5aa8SSimon Glass } 348*59dd5aa8SSimon Glass 349*59dd5aa8SSimon Glass return ret; 350*59dd5aa8SSimon Glass } 351*59dd5aa8SSimon Glass 352*59dd5aa8SSimon Glass static int tegra_dc_i2c_aux_read(struct tegra_dp_priv *dp, u32 i2c_addr, 353*59dd5aa8SSimon Glass u8 addr, u8 *data, u32 size, u32 *aux_stat) 354*59dd5aa8SSimon Glass { 355*59dd5aa8SSimon Glass u32 finished = 0; 356*59dd5aa8SSimon Glass int ret = 0; 357*59dd5aa8SSimon Glass 358*59dd5aa8SSimon Glass do { 359*59dd5aa8SSimon Glass u32 cur_size = min((u32)DP_AUX_MAX_BYTES, size - finished); 360*59dd5aa8SSimon Glass 361*59dd5aa8SSimon Glass u32 len = 1; 362*59dd5aa8SSimon Glass ret = tegra_dc_dpaux_write_chunk( 363*59dd5aa8SSimon Glass dp, DPAUX_DP_AUXCTL_CMD_MOTWR, i2c_addr, 364*59dd5aa8SSimon Glass &addr, &len, aux_stat); 365*59dd5aa8SSimon Glass if (ret) { 366*59dd5aa8SSimon Glass debug("%s: error sending address to read.\n", 367*59dd5aa8SSimon Glass __func__); 368*59dd5aa8SSimon Glass return ret; 369*59dd5aa8SSimon Glass } 370*59dd5aa8SSimon Glass 371*59dd5aa8SSimon Glass ret = tegra_dc_dpaux_read_chunk( 372*59dd5aa8SSimon Glass dp, DPAUX_DP_AUXCTL_CMD_I2CRD, i2c_addr, 373*59dd5aa8SSimon Glass data, &cur_size, aux_stat); 374*59dd5aa8SSimon Glass if (ret) { 375*59dd5aa8SSimon Glass debug("%s: error reading data.\n", __func__); 376*59dd5aa8SSimon Glass return ret; 377*59dd5aa8SSimon Glass } 378*59dd5aa8SSimon Glass 379*59dd5aa8SSimon Glass /* cur_size should be the real size returned */ 380*59dd5aa8SSimon Glass addr += cur_size; 381*59dd5aa8SSimon Glass data += cur_size; 382*59dd5aa8SSimon Glass finished += cur_size; 383*59dd5aa8SSimon Glass } while (size > finished); 384*59dd5aa8SSimon Glass 385*59dd5aa8SSimon Glass return finished; 386*59dd5aa8SSimon Glass } 387*59dd5aa8SSimon Glass 388*59dd5aa8SSimon Glass static void tegra_dc_dpaux_enable(struct tegra_dp_priv *dp) 389*59dd5aa8SSimon Glass { 390*59dd5aa8SSimon Glass /* clear interrupt */ 391*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_INTR_AUX, 0xffffffff); 392*59dd5aa8SSimon Glass /* do not enable interrupt for now. Enable them when Isr in place */ 393*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_INTR_EN_AUX, 0x0); 394*59dd5aa8SSimon Glass 395*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_HYBRID_PADCTL, 396*59dd5aa8SSimon Glass DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 | 397*59dd5aa8SSimon Glass DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 | 398*59dd5aa8SSimon Glass 0x18 << DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT | 399*59dd5aa8SSimon Glass DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE); 400*59dd5aa8SSimon Glass 401*59dd5aa8SSimon Glass tegra_dpaux_writel(dp, DPAUX_HYBRID_SPARE, 402*59dd5aa8SSimon Glass DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP); 403*59dd5aa8SSimon Glass } 404*59dd5aa8SSimon Glass 405*59dd5aa8SSimon Glass #ifdef DEBUG 406*59dd5aa8SSimon Glass static void tegra_dc_dp_dump_link_cfg(struct tegra_dp_priv *dp, 407*59dd5aa8SSimon Glass const struct tegra_dp_link_config *link_cfg) 408*59dd5aa8SSimon Glass { 409*59dd5aa8SSimon Glass debug("DP config: cfg_name cfg_value\n"); 410*59dd5aa8SSimon Glass debug(" Lane Count %d\n", 411*59dd5aa8SSimon Glass link_cfg->max_lane_count); 412*59dd5aa8SSimon Glass debug(" SupportEnhancedFraming %s\n", 413*59dd5aa8SSimon Glass link_cfg->support_enhanced_framing ? "Y" : "N"); 414*59dd5aa8SSimon Glass debug(" Bandwidth %d\n", 415*59dd5aa8SSimon Glass link_cfg->max_link_bw); 416*59dd5aa8SSimon Glass debug(" bpp %d\n", 417*59dd5aa8SSimon Glass link_cfg->bits_per_pixel); 418*59dd5aa8SSimon Glass debug(" EnhancedFraming %s\n", 419*59dd5aa8SSimon Glass link_cfg->enhanced_framing ? "Y" : "N"); 420*59dd5aa8SSimon Glass debug(" Scramble_enabled %s\n", 421*59dd5aa8SSimon Glass link_cfg->scramble_ena ? "Y" : "N"); 422*59dd5aa8SSimon Glass debug(" LinkBW %d\n", 423*59dd5aa8SSimon Glass link_cfg->link_bw); 424*59dd5aa8SSimon Glass debug(" lane_count %d\n", 425*59dd5aa8SSimon Glass link_cfg->lane_count); 426*59dd5aa8SSimon Glass debug(" activespolarity %d\n", 427*59dd5aa8SSimon Glass link_cfg->activepolarity); 428*59dd5aa8SSimon Glass debug(" active_count %d\n", 429*59dd5aa8SSimon Glass link_cfg->active_count); 430*59dd5aa8SSimon Glass debug(" tu_size %d\n", 431*59dd5aa8SSimon Glass link_cfg->tu_size); 432*59dd5aa8SSimon Glass debug(" active_frac %d\n", 433*59dd5aa8SSimon Glass link_cfg->active_frac); 434*59dd5aa8SSimon Glass debug(" watermark %d\n", 435*59dd5aa8SSimon Glass link_cfg->watermark); 436*59dd5aa8SSimon Glass debug(" hblank_sym %d\n", 437*59dd5aa8SSimon Glass link_cfg->hblank_sym); 438*59dd5aa8SSimon Glass debug(" vblank_sym %d\n", 439*59dd5aa8SSimon Glass link_cfg->vblank_sym); 440*59dd5aa8SSimon Glass } 441*59dd5aa8SSimon Glass #endif 442*59dd5aa8SSimon Glass 443*59dd5aa8SSimon Glass /* 444*59dd5aa8SSimon Glass * Calcuate if given cfg can meet the mode request. 445*59dd5aa8SSimon Glass * Return 0 if mode is possible, -1 otherwise 446*59dd5aa8SSimon Glass */ 447*59dd5aa8SSimon Glass static int tegra_dc_dp_calc_config(struct tegra_dp_priv *dp, 448*59dd5aa8SSimon Glass const struct display_timing *timing, 449*59dd5aa8SSimon Glass struct tegra_dp_link_config *link_cfg) 450*59dd5aa8SSimon Glass { 451*59dd5aa8SSimon Glass const u32 link_rate = 27 * link_cfg->link_bw * 1000 * 1000; 452*59dd5aa8SSimon Glass const u64 f = 100000; /* precision factor */ 453*59dd5aa8SSimon Glass u32 num_linkclk_line; /* Number of link clocks per line */ 454*59dd5aa8SSimon Glass u64 ratio_f; /* Ratio of incoming to outgoing data rate */ 455*59dd5aa8SSimon Glass u64 frac_f; 456*59dd5aa8SSimon Glass u64 activesym_f; /* Activesym per TU */ 457*59dd5aa8SSimon Glass u64 activecount_f; 458*59dd5aa8SSimon Glass u32 activecount; 459*59dd5aa8SSimon Glass u32 activepolarity; 460*59dd5aa8SSimon Glass u64 approx_value_f; 461*59dd5aa8SSimon Glass u32 activefrac = 0; 462*59dd5aa8SSimon Glass u64 accumulated_error_f = 0; 463*59dd5aa8SSimon Glass u32 lowest_neg_activecount = 0; 464*59dd5aa8SSimon Glass u32 lowest_neg_activepolarity = 0; 465*59dd5aa8SSimon Glass u32 lowest_neg_tusize = 64; 466*59dd5aa8SSimon Glass u32 num_symbols_per_line; 467*59dd5aa8SSimon Glass u64 lowest_neg_activefrac = 0; 468*59dd5aa8SSimon Glass u64 lowest_neg_error_f = 64 * f; 469*59dd5aa8SSimon Glass u64 watermark_f; 470*59dd5aa8SSimon Glass int i; 471*59dd5aa8SSimon Glass int neg; 472*59dd5aa8SSimon Glass 473*59dd5aa8SSimon Glass if (!link_rate || !link_cfg->lane_count || !timing->pixelclock.typ || 474*59dd5aa8SSimon Glass !link_cfg->bits_per_pixel) 475*59dd5aa8SSimon Glass return -1; 476*59dd5aa8SSimon Glass 477*59dd5aa8SSimon Glass if ((u64)timing->pixelclock.typ * link_cfg->bits_per_pixel >= 478*59dd5aa8SSimon Glass (u64)link_rate * 8 * link_cfg->lane_count) 479*59dd5aa8SSimon Glass return -1; 480*59dd5aa8SSimon Glass 481*59dd5aa8SSimon Glass num_linkclk_line = (u32)(lldiv(link_rate * timing->hactive.typ, 482*59dd5aa8SSimon Glass timing->pixelclock.typ)); 483*59dd5aa8SSimon Glass 484*59dd5aa8SSimon Glass ratio_f = (u64)timing->pixelclock.typ * link_cfg->bits_per_pixel * f; 485*59dd5aa8SSimon Glass ratio_f /= 8; 486*59dd5aa8SSimon Glass do_div(ratio_f, link_rate * link_cfg->lane_count); 487*59dd5aa8SSimon Glass 488*59dd5aa8SSimon Glass for (i = 64; i >= 32; --i) { 489*59dd5aa8SSimon Glass activesym_f = ratio_f * i; 490*59dd5aa8SSimon Glass activecount_f = lldiv(activesym_f, (u32)f) * f; 491*59dd5aa8SSimon Glass frac_f = activesym_f - activecount_f; 492*59dd5aa8SSimon Glass activecount = (u32)(lldiv(activecount_f, (u32)f)); 493*59dd5aa8SSimon Glass 494*59dd5aa8SSimon Glass if (frac_f < (lldiv(f, 2))) /* fraction < 0.5 */ 495*59dd5aa8SSimon Glass activepolarity = 0; 496*59dd5aa8SSimon Glass else { 497*59dd5aa8SSimon Glass activepolarity = 1; 498*59dd5aa8SSimon Glass frac_f = f - frac_f; 499*59dd5aa8SSimon Glass } 500*59dd5aa8SSimon Glass 501*59dd5aa8SSimon Glass if (frac_f != 0) { 502*59dd5aa8SSimon Glass /* warning: frac_f should be 64-bit */ 503*59dd5aa8SSimon Glass frac_f = lldiv(f * f, frac_f); /* 1 / fraction */ 504*59dd5aa8SSimon Glass if (frac_f > (15 * f)) 505*59dd5aa8SSimon Glass activefrac = activepolarity ? 1 : 15; 506*59dd5aa8SSimon Glass else 507*59dd5aa8SSimon Glass activefrac = activepolarity ? 508*59dd5aa8SSimon Glass (u32)lldiv(frac_f, (u32)f) + 1 : 509*59dd5aa8SSimon Glass (u32)lldiv(frac_f, (u32)f); 510*59dd5aa8SSimon Glass } 511*59dd5aa8SSimon Glass 512*59dd5aa8SSimon Glass if (activefrac == 1) 513*59dd5aa8SSimon Glass activepolarity = 0; 514*59dd5aa8SSimon Glass 515*59dd5aa8SSimon Glass if (activepolarity == 1) 516*59dd5aa8SSimon Glass approx_value_f = activefrac ? lldiv( 517*59dd5aa8SSimon Glass (activecount_f + (activefrac * f - f) * f), 518*59dd5aa8SSimon Glass (activefrac * f)) : 519*59dd5aa8SSimon Glass activecount_f + f; 520*59dd5aa8SSimon Glass else 521*59dd5aa8SSimon Glass approx_value_f = activefrac ? 522*59dd5aa8SSimon Glass activecount_f + lldiv(f, activefrac) : 523*59dd5aa8SSimon Glass activecount_f; 524*59dd5aa8SSimon Glass 525*59dd5aa8SSimon Glass if (activesym_f < approx_value_f) { 526*59dd5aa8SSimon Glass accumulated_error_f = num_linkclk_line * 527*59dd5aa8SSimon Glass lldiv(approx_value_f - activesym_f, i); 528*59dd5aa8SSimon Glass neg = 1; 529*59dd5aa8SSimon Glass } else { 530*59dd5aa8SSimon Glass accumulated_error_f = num_linkclk_line * 531*59dd5aa8SSimon Glass lldiv(activesym_f - approx_value_f, i); 532*59dd5aa8SSimon Glass neg = 0; 533*59dd5aa8SSimon Glass } 534*59dd5aa8SSimon Glass 535*59dd5aa8SSimon Glass if ((neg && (lowest_neg_error_f > accumulated_error_f)) || 536*59dd5aa8SSimon Glass (accumulated_error_f == 0)) { 537*59dd5aa8SSimon Glass lowest_neg_error_f = accumulated_error_f; 538*59dd5aa8SSimon Glass lowest_neg_tusize = i; 539*59dd5aa8SSimon Glass lowest_neg_activecount = activecount; 540*59dd5aa8SSimon Glass lowest_neg_activepolarity = activepolarity; 541*59dd5aa8SSimon Glass lowest_neg_activefrac = activefrac; 542*59dd5aa8SSimon Glass 543*59dd5aa8SSimon Glass if (accumulated_error_f == 0) 544*59dd5aa8SSimon Glass break; 545*59dd5aa8SSimon Glass } 546*59dd5aa8SSimon Glass } 547*59dd5aa8SSimon Glass 548*59dd5aa8SSimon Glass if (lowest_neg_activefrac == 0) { 549*59dd5aa8SSimon Glass link_cfg->activepolarity = 0; 550*59dd5aa8SSimon Glass link_cfg->active_count = lowest_neg_activepolarity ? 551*59dd5aa8SSimon Glass lowest_neg_activecount : lowest_neg_activecount - 1; 552*59dd5aa8SSimon Glass link_cfg->tu_size = lowest_neg_tusize; 553*59dd5aa8SSimon Glass link_cfg->active_frac = 1; 554*59dd5aa8SSimon Glass } else { 555*59dd5aa8SSimon Glass link_cfg->activepolarity = lowest_neg_activepolarity; 556*59dd5aa8SSimon Glass link_cfg->active_count = (u32)lowest_neg_activecount; 557*59dd5aa8SSimon Glass link_cfg->tu_size = lowest_neg_tusize; 558*59dd5aa8SSimon Glass link_cfg->active_frac = (u32)lowest_neg_activefrac; 559*59dd5aa8SSimon Glass } 560*59dd5aa8SSimon Glass 561*59dd5aa8SSimon Glass watermark_f = lldiv(ratio_f * link_cfg->tu_size * (f - ratio_f), f); 562*59dd5aa8SSimon Glass link_cfg->watermark = (u32)(lldiv(watermark_f + lowest_neg_error_f, 563*59dd5aa8SSimon Glass f)) + link_cfg->bits_per_pixel / 4 - 1; 564*59dd5aa8SSimon Glass num_symbols_per_line = (timing->hactive.typ * 565*59dd5aa8SSimon Glass link_cfg->bits_per_pixel) / 566*59dd5aa8SSimon Glass (8 * link_cfg->lane_count); 567*59dd5aa8SSimon Glass 568*59dd5aa8SSimon Glass if (link_cfg->watermark > 30) { 569*59dd5aa8SSimon Glass debug("dp: sor setting: unable to get a good tusize, force watermark to 30\n"); 570*59dd5aa8SSimon Glass link_cfg->watermark = 30; 571*59dd5aa8SSimon Glass return -1; 572*59dd5aa8SSimon Glass } else if (link_cfg->watermark > num_symbols_per_line) { 573*59dd5aa8SSimon Glass debug("dp: sor setting: force watermark to the number of symbols in the line\n"); 574*59dd5aa8SSimon Glass link_cfg->watermark = num_symbols_per_line; 575*59dd5aa8SSimon Glass return -1; 576*59dd5aa8SSimon Glass } 577*59dd5aa8SSimon Glass 578*59dd5aa8SSimon Glass /* 579*59dd5aa8SSimon Glass * Refer to dev_disp.ref for more information. 580*59dd5aa8SSimon Glass * # symbols/hblank = ((SetRasterBlankEnd.X + SetRasterSize.Width - 581*59dd5aa8SSimon Glass * SetRasterBlankStart.X - 7) * link_clk / pclk) 582*59dd5aa8SSimon Glass * - 3 * enhanced_framing - Y 583*59dd5aa8SSimon Glass * where Y = (# lanes == 4) 3 : (# lanes == 2) ? 6 : 12 584*59dd5aa8SSimon Glass */ 585*59dd5aa8SSimon Glass link_cfg->hblank_sym = (int)lldiv(((uint64_t)timing->hback_porch.typ + 586*59dd5aa8SSimon Glass timing->hfront_porch.typ + timing->hsync_len.typ - 7) * 587*59dd5aa8SSimon Glass link_rate, timing->pixelclock.typ) - 588*59dd5aa8SSimon Glass 3 * link_cfg->enhanced_framing - 589*59dd5aa8SSimon Glass (12 / link_cfg->lane_count); 590*59dd5aa8SSimon Glass 591*59dd5aa8SSimon Glass if (link_cfg->hblank_sym < 0) 592*59dd5aa8SSimon Glass link_cfg->hblank_sym = 0; 593*59dd5aa8SSimon Glass 594*59dd5aa8SSimon Glass 595*59dd5aa8SSimon Glass /* 596*59dd5aa8SSimon Glass * Refer to dev_disp.ref for more information. 597*59dd5aa8SSimon Glass * # symbols/vblank = ((SetRasterBlankStart.X - 598*59dd5aa8SSimon Glass * SetRasterBlankEen.X - 25) * link_clk / pclk) 599*59dd5aa8SSimon Glass * - Y - 1; 600*59dd5aa8SSimon Glass * where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39 601*59dd5aa8SSimon Glass */ 602*59dd5aa8SSimon Glass link_cfg->vblank_sym = (int)lldiv(((uint64_t)timing->hactive.typ - 25) 603*59dd5aa8SSimon Glass * link_rate, timing->pixelclock.typ) - (36 / 604*59dd5aa8SSimon Glass link_cfg->lane_count) - 4; 605*59dd5aa8SSimon Glass 606*59dd5aa8SSimon Glass if (link_cfg->vblank_sym < 0) 607*59dd5aa8SSimon Glass link_cfg->vblank_sym = 0; 608*59dd5aa8SSimon Glass 609*59dd5aa8SSimon Glass link_cfg->is_valid = 1; 610*59dd5aa8SSimon Glass #ifdef DEBUG 611*59dd5aa8SSimon Glass tegra_dc_dp_dump_link_cfg(dp, link_cfg); 612*59dd5aa8SSimon Glass #endif 613*59dd5aa8SSimon Glass 614*59dd5aa8SSimon Glass return 0; 615*59dd5aa8SSimon Glass } 616*59dd5aa8SSimon Glass 617*59dd5aa8SSimon Glass static int tegra_dc_dp_init_max_link_cfg( 618*59dd5aa8SSimon Glass const struct display_timing *timing, 619*59dd5aa8SSimon Glass struct tegra_dp_priv *dp, 620*59dd5aa8SSimon Glass struct tegra_dp_link_config *link_cfg) 621*59dd5aa8SSimon Glass { 622*59dd5aa8SSimon Glass const int drive_current = 0x40404040; 623*59dd5aa8SSimon Glass const int preemphasis = 0x0f0f0f0f; 624*59dd5aa8SSimon Glass const int postcursor = 0; 625*59dd5aa8SSimon Glass u8 dpcd_data; 626*59dd5aa8SSimon Glass int ret; 627*59dd5aa8SSimon Glass 628*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LANE_COUNT, &dpcd_data); 629*59dd5aa8SSimon Glass if (ret) 630*59dd5aa8SSimon Glass return ret; 631*59dd5aa8SSimon Glass link_cfg->max_lane_count = dpcd_data & DP_MAX_LANE_COUNT_MASK; 632*59dd5aa8SSimon Glass 633*59dd5aa8SSimon Glass link_cfg->support_enhanced_framing = 634*59dd5aa8SSimon Glass (dpcd_data & DP_MAX_LANE_COUNT_ENHANCED_FRAMING_YES) ? 635*59dd5aa8SSimon Glass 1 : 0; 636*59dd5aa8SSimon Glass 637*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_DOWNSPREAD, &dpcd_data); 638*59dd5aa8SSimon Glass if (ret) 639*59dd5aa8SSimon Glass return ret; 640*59dd5aa8SSimon Glass link_cfg->downspread = (dpcd_data & DP_MAX_DOWNSPREAD_VAL_0_5_PCT) ? 641*59dd5aa8SSimon Glass 1 : 0; 642*59dd5aa8SSimon Glass 643*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LINK_RATE, 644*59dd5aa8SSimon Glass &link_cfg->max_link_bw); 645*59dd5aa8SSimon Glass if (ret) 646*59dd5aa8SSimon Glass return ret; 647*59dd5aa8SSimon Glass 648*59dd5aa8SSimon Glass /* 649*59dd5aa8SSimon Glass * Set to a high value for link training and attach. 650*59dd5aa8SSimon Glass * Will be re-programmed when dp is enabled. 651*59dd5aa8SSimon Glass */ 652*59dd5aa8SSimon Glass link_cfg->drive_current = drive_current; 653*59dd5aa8SSimon Glass link_cfg->preemphasis = preemphasis; 654*59dd5aa8SSimon Glass link_cfg->postcursor = postcursor; 655*59dd5aa8SSimon Glass 656*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_EDP_CONFIGURATION_CAP, &dpcd_data); 657*59dd5aa8SSimon Glass if (ret) 658*59dd5aa8SSimon Glass return ret; 659*59dd5aa8SSimon Glass 660*59dd5aa8SSimon Glass link_cfg->alt_scramber_reset_cap = 661*59dd5aa8SSimon Glass (dpcd_data & DP_EDP_CONFIGURATION_CAP_ASC_RESET_YES) ? 662*59dd5aa8SSimon Glass 1 : 0; 663*59dd5aa8SSimon Glass link_cfg->only_enhanced_framing = 664*59dd5aa8SSimon Glass (dpcd_data & DP_EDP_CONFIGURATION_CAP_FRAMING_CHANGE_YES) ? 665*59dd5aa8SSimon Glass 1 : 0; 666*59dd5aa8SSimon Glass 667*59dd5aa8SSimon Glass link_cfg->lane_count = link_cfg->max_lane_count; 668*59dd5aa8SSimon Glass link_cfg->link_bw = link_cfg->max_link_bw; 669*59dd5aa8SSimon Glass link_cfg->enhanced_framing = link_cfg->support_enhanced_framing; 670*59dd5aa8SSimon Glass 671*59dd5aa8SSimon Glass tegra_dc_dp_calc_config(dp, timing, link_cfg); 672*59dd5aa8SSimon Glass return 0; 673*59dd5aa8SSimon Glass } 674*59dd5aa8SSimon Glass 675*59dd5aa8SSimon Glass static int tegra_dc_dp_set_assr(struct tegra_dp_priv *dp, 676*59dd5aa8SSimon Glass struct tegra_dc_sor_data *sor, int ena) 677*59dd5aa8SSimon Glass { 678*59dd5aa8SSimon Glass int ret; 679*59dd5aa8SSimon Glass 680*59dd5aa8SSimon Glass u8 dpcd_data = ena ? 681*59dd5aa8SSimon Glass DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE : 682*59dd5aa8SSimon Glass DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE; 683*59dd5aa8SSimon Glass 684*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_write(dp, DP_EDP_CONFIGURATION_SET, 685*59dd5aa8SSimon Glass dpcd_data); 686*59dd5aa8SSimon Glass if (ret) 687*59dd5aa8SSimon Glass return ret; 688*59dd5aa8SSimon Glass 689*59dd5aa8SSimon Glass /* Also reset the scrambler to 0xfffe */ 690*59dd5aa8SSimon Glass tegra_dc_sor_set_internal_panel(sor, ena); 691*59dd5aa8SSimon Glass return 0; 692*59dd5aa8SSimon Glass } 693*59dd5aa8SSimon Glass 694*59dd5aa8SSimon Glass static int tegra_dp_set_link_bandwidth(struct tegra_dp_priv *dp, 695*59dd5aa8SSimon Glass struct tegra_dc_sor_data *sor, 696*59dd5aa8SSimon Glass u8 link_bw) 697*59dd5aa8SSimon Glass { 698*59dd5aa8SSimon Glass tegra_dc_sor_set_link_bandwidth(sor, link_bw); 699*59dd5aa8SSimon Glass 700*59dd5aa8SSimon Glass /* Sink side */ 701*59dd5aa8SSimon Glass return tegra_dc_dp_dpcd_write(dp, DP_LINK_BW_SET, link_bw); 702*59dd5aa8SSimon Glass } 703*59dd5aa8SSimon Glass 704*59dd5aa8SSimon Glass static int tegra_dp_set_lane_count(struct tegra_dp_priv *dp, 705*59dd5aa8SSimon Glass const struct tegra_dp_link_config *link_cfg, 706*59dd5aa8SSimon Glass struct tegra_dc_sor_data *sor) 707*59dd5aa8SSimon Glass { 708*59dd5aa8SSimon Glass u8 dpcd_data; 709*59dd5aa8SSimon Glass int ret; 710*59dd5aa8SSimon Glass 711*59dd5aa8SSimon Glass /* check if panel support enhanched_framing */ 712*59dd5aa8SSimon Glass dpcd_data = link_cfg->lane_count; 713*59dd5aa8SSimon Glass if (link_cfg->enhanced_framing) 714*59dd5aa8SSimon Glass dpcd_data |= DP_LANE_COUNT_SET_ENHANCEDFRAMING_T; 715*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_write(dp, DP_LANE_COUNT_SET, dpcd_data); 716*59dd5aa8SSimon Glass if (ret) 717*59dd5aa8SSimon Glass return ret; 718*59dd5aa8SSimon Glass 719*59dd5aa8SSimon Glass tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count); 720*59dd5aa8SSimon Glass 721*59dd5aa8SSimon Glass /* Also power down lanes that will not be used */ 722*59dd5aa8SSimon Glass return 0; 723*59dd5aa8SSimon Glass } 724*59dd5aa8SSimon Glass 725*59dd5aa8SSimon Glass static int tegra_dc_dp_link_trained(struct tegra_dp_priv *dp, 726*59dd5aa8SSimon Glass const struct tegra_dp_link_config *cfg) 727*59dd5aa8SSimon Glass { 728*59dd5aa8SSimon Glass u32 lane; 729*59dd5aa8SSimon Glass u8 mask; 730*59dd5aa8SSimon Glass u8 data; 731*59dd5aa8SSimon Glass int ret; 732*59dd5aa8SSimon Glass 733*59dd5aa8SSimon Glass for (lane = 0; lane < cfg->lane_count; ++lane) { 734*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, (lane / 2) ? 735*59dd5aa8SSimon Glass DP_LANE2_3_STATUS : DP_LANE0_1_STATUS, 736*59dd5aa8SSimon Glass &data); 737*59dd5aa8SSimon Glass if (ret) 738*59dd5aa8SSimon Glass return ret; 739*59dd5aa8SSimon Glass mask = (lane & 1) ? 740*59dd5aa8SSimon Glass NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES | 741*59dd5aa8SSimon Glass NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES | 742*59dd5aa8SSimon Glass NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES : 743*59dd5aa8SSimon Glass DP_LANE_CR_DONE | 744*59dd5aa8SSimon Glass DP_LANE_CHANNEL_EQ_DONE | 745*59dd5aa8SSimon Glass DP_LANE_SYMBOL_LOCKED; 746*59dd5aa8SSimon Glass if ((data & mask) != mask) 747*59dd5aa8SSimon Glass return -1; 748*59dd5aa8SSimon Glass } 749*59dd5aa8SSimon Glass return 0; 750*59dd5aa8SSimon Glass } 751*59dd5aa8SSimon Glass 752*59dd5aa8SSimon Glass /* 753*59dd5aa8SSimon Glass * All link training functions are ported from kernel dc driver. 754*59dd5aa8SSimon Glass * See more details at drivers/video/tegra/dc/dp.c 755*59dd5aa8SSimon Glass */ 756*59dd5aa8SSimon Glass static int tegra_dc_dp_fast_link_training(struct tegra_dp_priv *dp, 757*59dd5aa8SSimon Glass const struct tegra_dp_link_config *link_cfg, 758*59dd5aa8SSimon Glass struct tegra_dc_sor_data *sor) 759*59dd5aa8SSimon Glass { 760*59dd5aa8SSimon Glass u8 link_bw; 761*59dd5aa8SSimon Glass u8 lane_count; 762*59dd5aa8SSimon Glass u16 data16; 763*59dd5aa8SSimon Glass u32 data32; 764*59dd5aa8SSimon Glass u32 size; 765*59dd5aa8SSimon Glass u32 status; 766*59dd5aa8SSimon Glass int j; 767*59dd5aa8SSimon Glass u32 mask = 0xffff >> ((4 - link_cfg->lane_count) * 4); 768*59dd5aa8SSimon Glass 769*59dd5aa8SSimon Glass tegra_dc_sor_set_lane_parm(sor, link_cfg); 770*59dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_MAIN_LINK_CHANNEL_CODING_SET, 771*59dd5aa8SSimon Glass DP_SET_ANSI_8B10B); 772*59dd5aa8SSimon Glass 773*59dd5aa8SSimon Glass /* Send TP1 */ 774*59dd5aa8SSimon Glass tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_1, link_cfg); 775*59dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 776*59dd5aa8SSimon Glass DP_TRAINING_PATTERN_1); 777*59dd5aa8SSimon Glass 778*59dd5aa8SSimon Glass for (j = 0; j < link_cfg->lane_count; ++j) 779*59dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24); 780*59dd5aa8SSimon Glass udelay(520); 781*59dd5aa8SSimon Glass 782*59dd5aa8SSimon Glass size = sizeof(data16); 783*59dd5aa8SSimon Glass tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, 784*59dd5aa8SSimon Glass DP_LANE0_1_STATUS, (u8 *)&data16, &size, &status); 785*59dd5aa8SSimon Glass status = mask & 0x1111; 786*59dd5aa8SSimon Glass if ((data16 & status) != status) { 787*59dd5aa8SSimon Glass debug("dp: Link training error for TP1 (%#x, status %#x)\n", 788*59dd5aa8SSimon Glass data16, status); 789*59dd5aa8SSimon Glass return -EFAULT; 790*59dd5aa8SSimon Glass } 791*59dd5aa8SSimon Glass 792*59dd5aa8SSimon Glass /* enable ASSR */ 793*59dd5aa8SSimon Glass tegra_dc_dp_set_assr(dp, sor, link_cfg->scramble_ena); 794*59dd5aa8SSimon Glass tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_3, link_cfg); 795*59dd5aa8SSimon Glass 796*59dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 797*59dd5aa8SSimon Glass link_cfg->link_bw == 20 ? 0x23 : 0x22); 798*59dd5aa8SSimon Glass for (j = 0; j < link_cfg->lane_count; ++j) 799*59dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24); 800*59dd5aa8SSimon Glass udelay(520); 801*59dd5aa8SSimon Glass 802*59dd5aa8SSimon Glass size = sizeof(data32); 803*59dd5aa8SSimon Glass tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, DP_LANE0_1_STATUS, 804*59dd5aa8SSimon Glass (u8 *)&data32, &size, &status); 805*59dd5aa8SSimon Glass if ((data32 & mask) != (0x7777 & mask)) { 806*59dd5aa8SSimon Glass debug("dp: Link training error for TP2/3 (0x%x)\n", data32); 807*59dd5aa8SSimon Glass return -EFAULT; 808*59dd5aa8SSimon Glass } 809*59dd5aa8SSimon Glass 810*59dd5aa8SSimon Glass tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_disabled, 811*59dd5aa8SSimon Glass link_cfg); 812*59dd5aa8SSimon Glass tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 0); 813*59dd5aa8SSimon Glass 814*59dd5aa8SSimon Glass if (tegra_dc_dp_link_trained(dp, link_cfg)) { 815*59dd5aa8SSimon Glass tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count); 816*59dd5aa8SSimon Glass debug("Fast link training failed, link bw %d, lane # %d\n", 817*59dd5aa8SSimon Glass link_bw, lane_count); 818*59dd5aa8SSimon Glass return -EFAULT; 819*59dd5aa8SSimon Glass } 820*59dd5aa8SSimon Glass 821*59dd5aa8SSimon Glass debug("Fast link training succeeded, link bw %d, lane %d\n", 822*59dd5aa8SSimon Glass link_cfg->link_bw, link_cfg->lane_count); 823*59dd5aa8SSimon Glass 824*59dd5aa8SSimon Glass return 0; 825*59dd5aa8SSimon Glass } 826*59dd5aa8SSimon Glass 827*59dd5aa8SSimon Glass static int tegra_dp_link_config(struct tegra_dp_priv *dp, 828*59dd5aa8SSimon Glass const struct tegra_dp_link_config *link_cfg, 829*59dd5aa8SSimon Glass struct tegra_dc_sor_data *sor) 830*59dd5aa8SSimon Glass { 831*59dd5aa8SSimon Glass u8 dpcd_data; 832*59dd5aa8SSimon Glass u8 link_bw; 833*59dd5aa8SSimon Glass u8 lane_count; 834*59dd5aa8SSimon Glass u32 retry; 835*59dd5aa8SSimon Glass int ret; 836*59dd5aa8SSimon Glass 837*59dd5aa8SSimon Glass if (link_cfg->lane_count == 0) { 838*59dd5aa8SSimon Glass debug("dp: error: lane count is 0. Can not set link config.\n"); 839*59dd5aa8SSimon Glass return -1; 840*59dd5aa8SSimon Glass } 841*59dd5aa8SSimon Glass 842*59dd5aa8SSimon Glass /* Set power state if it is not in normal level */ 843*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_read(dp, DP_SET_POWER, &dpcd_data); 844*59dd5aa8SSimon Glass if (ret) 845*59dd5aa8SSimon Glass return ret; 846*59dd5aa8SSimon Glass if (dpcd_data == DP_SET_POWER_D3) { 847*59dd5aa8SSimon Glass dpcd_data = DP_SET_POWER_D0; 848*59dd5aa8SSimon Glass retry = 3; /* DP spec requires 3 retries */ 849*59dd5aa8SSimon Glass do { 850*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_write(dp, 851*59dd5aa8SSimon Glass DP_SET_POWER, dpcd_data); 852*59dd5aa8SSimon Glass } while ((--retry > 0) && ret); 853*59dd5aa8SSimon Glass if (ret) { 854*59dd5aa8SSimon Glass debug("dp: Failed to set DP panel power\n"); 855*59dd5aa8SSimon Glass return ret; 856*59dd5aa8SSimon Glass } 857*59dd5aa8SSimon Glass } 858*59dd5aa8SSimon Glass 859*59dd5aa8SSimon Glass /* Enable ASSR if possible */ 860*59dd5aa8SSimon Glass if (link_cfg->alt_scramber_reset_cap) { 861*59dd5aa8SSimon Glass ret = tegra_dc_dp_set_assr(dp, sor, 1); 862*59dd5aa8SSimon Glass if (ret) 863*59dd5aa8SSimon Glass return ret; 864*59dd5aa8SSimon Glass } 865*59dd5aa8SSimon Glass 866*59dd5aa8SSimon Glass ret = tegra_dp_set_link_bandwidth(dp, sor, link_cfg->link_bw); 867*59dd5aa8SSimon Glass if (ret) { 868*59dd5aa8SSimon Glass debug("dp: Failed to set link bandwidth\n"); 869*59dd5aa8SSimon Glass return ret; 870*59dd5aa8SSimon Glass } 871*59dd5aa8SSimon Glass ret = tegra_dp_set_lane_count(dp, link_cfg, sor); 872*59dd5aa8SSimon Glass if (ret) { 873*59dd5aa8SSimon Glass debug("dp: Failed to set lane count\n"); 874*59dd5aa8SSimon Glass return ret; 875*59dd5aa8SSimon Glass } 876*59dd5aa8SSimon Glass tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg); 877*59dd5aa8SSimon Glass 878*59dd5aa8SSimon Glass /* Now do the fast link training for eDP */ 879*59dd5aa8SSimon Glass ret = tegra_dc_dp_fast_link_training(dp, link_cfg, sor); 880*59dd5aa8SSimon Glass if (ret) { 881*59dd5aa8SSimon Glass debug("dp: fast link training failed\n"); 882*59dd5aa8SSimon Glass return ret; 883*59dd5aa8SSimon Glass } 884*59dd5aa8SSimon Glass 885*59dd5aa8SSimon Glass /* Everything is good; double check the link config */ 886*59dd5aa8SSimon Glass tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count); 887*59dd5aa8SSimon Glass 888*59dd5aa8SSimon Glass if ((link_cfg->link_bw == link_bw) && 889*59dd5aa8SSimon Glass (link_cfg->lane_count == lane_count)) 890*59dd5aa8SSimon Glass return 0; 891*59dd5aa8SSimon Glass else 892*59dd5aa8SSimon Glass return -EFAULT; 893*59dd5aa8SSimon Glass } 894*59dd5aa8SSimon Glass 895*59dd5aa8SSimon Glass static int tegra_dc_dp_explore_link_cfg(struct tegra_dp_priv *dp, 896*59dd5aa8SSimon Glass struct tegra_dp_link_config *link_cfg, 897*59dd5aa8SSimon Glass struct tegra_dc_sor_data *sor, 898*59dd5aa8SSimon Glass const struct display_timing *timing) 899*59dd5aa8SSimon Glass { 900*59dd5aa8SSimon Glass struct tegra_dp_link_config temp_cfg; 901*59dd5aa8SSimon Glass 902*59dd5aa8SSimon Glass if (!timing->pixelclock.typ || !timing->hactive.typ || 903*59dd5aa8SSimon Glass !timing->vactive.typ) { 904*59dd5aa8SSimon Glass debug("dp: error mode configuration"); 905*59dd5aa8SSimon Glass return -EINVAL; 906*59dd5aa8SSimon Glass } 907*59dd5aa8SSimon Glass if (!link_cfg->max_link_bw || !link_cfg->max_lane_count) { 908*59dd5aa8SSimon Glass debug("dp: error link configuration"); 909*59dd5aa8SSimon Glass return -EINVAL; 910*59dd5aa8SSimon Glass } 911*59dd5aa8SSimon Glass 912*59dd5aa8SSimon Glass link_cfg->is_valid = 0; 913*59dd5aa8SSimon Glass 914*59dd5aa8SSimon Glass memcpy(&temp_cfg, link_cfg, sizeof(temp_cfg)); 915*59dd5aa8SSimon Glass 916*59dd5aa8SSimon Glass temp_cfg.link_bw = temp_cfg.max_link_bw; 917*59dd5aa8SSimon Glass temp_cfg.lane_count = temp_cfg.max_lane_count; 918*59dd5aa8SSimon Glass 919*59dd5aa8SSimon Glass /* 920*59dd5aa8SSimon Glass * set to max link config 921*59dd5aa8SSimon Glass */ 922*59dd5aa8SSimon Glass if ((!tegra_dc_dp_calc_config(dp, timing, &temp_cfg)) && 923*59dd5aa8SSimon Glass (!(tegra_dp_link_config(dp, &temp_cfg, sor)))) 924*59dd5aa8SSimon Glass /* the max link cfg is doable */ 925*59dd5aa8SSimon Glass memcpy(link_cfg, &temp_cfg, sizeof(temp_cfg)); 926*59dd5aa8SSimon Glass 927*59dd5aa8SSimon Glass return link_cfg->is_valid ? 0 : -EFAULT; 928*59dd5aa8SSimon Glass } 929*59dd5aa8SSimon Glass 930*59dd5aa8SSimon Glass static int tegra_dp_hpd_plug(struct tegra_dp_priv *dp) 931*59dd5aa8SSimon Glass { 932*59dd5aa8SSimon Glass const int vdd_to_hpd_delay_ms = 200; 933*59dd5aa8SSimon Glass u32 val; 934*59dd5aa8SSimon Glass ulong start; 935*59dd5aa8SSimon Glass 936*59dd5aa8SSimon Glass start = get_timer(0); 937*59dd5aa8SSimon Glass do { 938*59dd5aa8SSimon Glass val = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); 939*59dd5aa8SSimon Glass if (val & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED) 940*59dd5aa8SSimon Glass return 0; 941*59dd5aa8SSimon Glass udelay(100); 942*59dd5aa8SSimon Glass } while (get_timer(start) < vdd_to_hpd_delay_ms); 943*59dd5aa8SSimon Glass 944*59dd5aa8SSimon Glass return -EIO; 945*59dd5aa8SSimon Glass } 946*59dd5aa8SSimon Glass 947*59dd5aa8SSimon Glass int tegra_dp_enable(struct udevice *dev, int panel_bpp, 948*59dd5aa8SSimon Glass const struct display_timing *timing) 949*59dd5aa8SSimon Glass { 950*59dd5aa8SSimon Glass struct tegra_dp_priv *priv = dev_get_priv(dev); 951*59dd5aa8SSimon Glass struct tegra_dp_link_config slink_cfg, *link_cfg = &slink_cfg; 952*59dd5aa8SSimon Glass struct tegra_dc_sor_data *sor; 953*59dd5aa8SSimon Glass int data; 954*59dd5aa8SSimon Glass int retry; 955*59dd5aa8SSimon Glass int ret; 956*59dd5aa8SSimon Glass 957*59dd5aa8SSimon Glass memset(link_cfg, '\0', sizeof(*link_cfg)); 958*59dd5aa8SSimon Glass link_cfg->is_valid = 0; 959*59dd5aa8SSimon Glass link_cfg->scramble_ena = 1; 960*59dd5aa8SSimon Glass 961*59dd5aa8SSimon Glass tegra_dc_dpaux_enable(priv); 962*59dd5aa8SSimon Glass 963*59dd5aa8SSimon Glass if (tegra_dp_hpd_plug(priv) < 0) { 964*59dd5aa8SSimon Glass debug("dp: hpd plug failed\n"); 965*59dd5aa8SSimon Glass return -EIO; 966*59dd5aa8SSimon Glass } 967*59dd5aa8SSimon Glass 968*59dd5aa8SSimon Glass link_cfg->bits_per_pixel = panel_bpp; 969*59dd5aa8SSimon Glass if (tegra_dc_dp_init_max_link_cfg(timing, priv, link_cfg)) { 970*59dd5aa8SSimon Glass debug("dp: failed to init link configuration\n"); 971*59dd5aa8SSimon Glass return -ENOLINK; 972*59dd5aa8SSimon Glass } 973*59dd5aa8SSimon Glass 974*59dd5aa8SSimon Glass ret = tegra_dc_sor_init(&sor); 975*59dd5aa8SSimon Glass if (ret) 976*59dd5aa8SSimon Glass return ret; 977*59dd5aa8SSimon Glass priv->sor = sor; 978*59dd5aa8SSimon Glass ret = tegra_dc_sor_enable_dp(sor, link_cfg); 979*59dd5aa8SSimon Glass if (ret) 980*59dd5aa8SSimon Glass return ret; 981*59dd5aa8SSimon Glass 982*59dd5aa8SSimon Glass tegra_dc_sor_set_panel_power(sor, 1); 983*59dd5aa8SSimon Glass 984*59dd5aa8SSimon Glass /* Write power on to DPCD */ 985*59dd5aa8SSimon Glass data = DP_SET_POWER_D0; 986*59dd5aa8SSimon Glass retry = 0; 987*59dd5aa8SSimon Glass do { 988*59dd5aa8SSimon Glass ret = tegra_dc_dp_dpcd_write(priv, DP_SET_POWER, data); 989*59dd5aa8SSimon Glass } while ((retry++ < DP_POWER_ON_MAX_TRIES) && ret); 990*59dd5aa8SSimon Glass 991*59dd5aa8SSimon Glass if (ret || retry >= DP_POWER_ON_MAX_TRIES) { 992*59dd5aa8SSimon Glass debug("dp: failed to power on panel (0x%x)\n", ret); 993*59dd5aa8SSimon Glass return -ENETUNREACH; 994*59dd5aa8SSimon Glass goto error_enable; 995*59dd5aa8SSimon Glass } 996*59dd5aa8SSimon Glass 997*59dd5aa8SSimon Glass /* Confirm DP plugging status */ 998*59dd5aa8SSimon Glass if (!(tegra_dpaux_readl(priv, DPAUX_DP_AUXSTAT) & 999*59dd5aa8SSimon Glass DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) { 1000*59dd5aa8SSimon Glass debug("dp: could not detect HPD\n"); 1001*59dd5aa8SSimon Glass return -ENXIO; 1002*59dd5aa8SSimon Glass } 1003*59dd5aa8SSimon Glass 1004*59dd5aa8SSimon Glass /* Check DP version */ 1005*59dd5aa8SSimon Glass if (tegra_dc_dp_dpcd_read(priv, DP_DPCD_REV, &priv->revision)) { 1006*59dd5aa8SSimon Glass debug("dp: failed to read the revision number from sink\n"); 1007*59dd5aa8SSimon Glass return -EIO; 1008*59dd5aa8SSimon Glass } 1009*59dd5aa8SSimon Glass 1010*59dd5aa8SSimon Glass if (tegra_dc_dp_explore_link_cfg(priv, link_cfg, sor, timing)) { 1011*59dd5aa8SSimon Glass debug("dp: error configuring link\n"); 1012*59dd5aa8SSimon Glass return -ENOMEDIUM; 1013*59dd5aa8SSimon Glass } 1014*59dd5aa8SSimon Glass 1015*59dd5aa8SSimon Glass tegra_dc_sor_set_power_state(sor, 1); 1016*59dd5aa8SSimon Glass ret = tegra_dc_sor_attach(sor, link_cfg, timing); 1017*59dd5aa8SSimon Glass if (ret && ret != -EEXIST) 1018*59dd5aa8SSimon Glass return ret; 1019*59dd5aa8SSimon Glass 1020*59dd5aa8SSimon Glass /* Power down the unused lanes to save power - a few hundred mW */ 1021*59dd5aa8SSimon Glass tegra_dc_sor_power_down_unused_lanes(sor, link_cfg); 1022*59dd5aa8SSimon Glass 1023*59dd5aa8SSimon Glass priv->enabled = true; 1024*59dd5aa8SSimon Glass error_enable: 1025*59dd5aa8SSimon Glass return 0; 1026*59dd5aa8SSimon Glass } 1027*59dd5aa8SSimon Glass 1028*59dd5aa8SSimon Glass static int tegra_dp_ofdata_to_platdata(struct udevice *dev) 1029*59dd5aa8SSimon Glass { 1030*59dd5aa8SSimon Glass struct tegra_dp_plat *plat = dev_get_platdata(dev); 1031*59dd5aa8SSimon Glass const void *blob = gd->fdt_blob; 1032*59dd5aa8SSimon Glass 1033*59dd5aa8SSimon Glass plat->base = fdtdec_get_addr(blob, dev->of_offset, "reg"); 1034*59dd5aa8SSimon Glass 1035*59dd5aa8SSimon Glass return 0; 1036*59dd5aa8SSimon Glass } 1037*59dd5aa8SSimon Glass 1038*59dd5aa8SSimon Glass static int tegra_dp_read_edid(struct udevice *dev, u8 *buf, int buf_size) 1039*59dd5aa8SSimon Glass { 1040*59dd5aa8SSimon Glass struct tegra_dp_priv *priv = dev_get_priv(dev); 1041*59dd5aa8SSimon Glass const int tegra_edid_i2c_address = 0x50; 1042*59dd5aa8SSimon Glass u32 aux_stat = 0; 1043*59dd5aa8SSimon Glass 1044*59dd5aa8SSimon Glass tegra_dc_dpaux_enable(priv); 1045*59dd5aa8SSimon Glass 1046*59dd5aa8SSimon Glass return tegra_dc_i2c_aux_read(priv, tegra_edid_i2c_address, 0, buf, 1047*59dd5aa8SSimon Glass buf_size, &aux_stat); 1048*59dd5aa8SSimon Glass } 1049*59dd5aa8SSimon Glass 1050*59dd5aa8SSimon Glass static const struct dm_display_port_ops dp_tegra_ops = { 1051*59dd5aa8SSimon Glass .read_edid = tegra_dp_read_edid, 1052*59dd5aa8SSimon Glass .enable = tegra_dp_enable, 1053*59dd5aa8SSimon Glass }; 1054*59dd5aa8SSimon Glass 1055*59dd5aa8SSimon Glass static int dp_tegra_probe(struct udevice *dev) 1056*59dd5aa8SSimon Glass { 1057*59dd5aa8SSimon Glass struct tegra_dp_plat *plat = dev_get_platdata(dev); 1058*59dd5aa8SSimon Glass struct tegra_dp_priv *priv = dev_get_priv(dev); 1059*59dd5aa8SSimon Glass 1060*59dd5aa8SSimon Glass priv->regs = (struct dpaux_ctlr *)plat->base; 1061*59dd5aa8SSimon Glass priv->enabled = false; 1062*59dd5aa8SSimon Glass 1063*59dd5aa8SSimon Glass return 0; 1064*59dd5aa8SSimon Glass } 1065*59dd5aa8SSimon Glass 1066*59dd5aa8SSimon Glass static const struct udevice_id tegra_dp_ids[] = { 1067*59dd5aa8SSimon Glass { .compatible = "nvidia,tegra124-dpaux" }, 1068*59dd5aa8SSimon Glass { } 1069*59dd5aa8SSimon Glass }; 1070*59dd5aa8SSimon Glass 1071*59dd5aa8SSimon Glass U_BOOT_DRIVER(dp_tegra) = { 1072*59dd5aa8SSimon Glass .name = "dpaux_tegra", 1073*59dd5aa8SSimon Glass .id = UCLASS_DISPLAY_PORT, 1074*59dd5aa8SSimon Glass .of_match = tegra_dp_ids, 1075*59dd5aa8SSimon Glass .ofdata_to_platdata = tegra_dp_ofdata_to_platdata, 1076*59dd5aa8SSimon Glass .probe = dp_tegra_probe, 1077*59dd5aa8SSimon Glass .ops = &dp_tegra_ops, 1078*59dd5aa8SSimon Glass .priv_auto_alloc_size = sizeof(struct tegra_dp_priv), 1079*59dd5aa8SSimon Glass .platdata_auto_alloc_size = sizeof(struct tegra_dp_plat), 1080*59dd5aa8SSimon Glass }; 1081