Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/nv50-/disp: move DP link training to core and train from supervisor

We need to be able to do link training for PIOR-connected ANX9805 from
the third supervisor handler (due to script ordering in the bios, can't
have the "user" call train because some settings are overwritten from
the modesetting bios scripts).

This moves link training for SOR-connected DP encoders to the second
supervisor interrupt, *before* we call the modesetting scripts (yes,
different ordering from PIOR is necessary). This is useful since we
should now be able to remove some hacks to workaround races between
the supervisor and link training paths.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>

+570 -555
+1
drivers/gpu/drm/nouveau/Makefile
··· 159 159 nouveau-y += core/engine/disp/nvd0.o 160 160 nouveau-y += core/engine/disp/nve0.o 161 161 nouveau-y += core/engine/disp/dacnv50.o 162 + nouveau-y += core/engine/disp/dport.o 162 163 nouveau-y += core/engine/disp/hdanva3.o 163 164 nouveau-y += core/engine/disp/hdanvd0.o 164 165 nouveau-y += core/engine/disp/hdminv84.o
+346
drivers/gpu/drm/nouveau/core/engine/disp/dport.c
··· 1 + /* 2 + * Copyright 2013 Red Hat Inc. 3 + * 4 + * Permission is hereby granted, free of charge, to any person obtaining a 5 + * copy of this software and associated documentation files (the "Software"), 6 + * to deal in the Software without restriction, including without limitation 7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 + * and/or sell copies of the Software, and to permit persons to whom the 9 + * Software is furnished to do so, subject to the following conditions: 10 + * 11 + * The above copyright notice and this permission notice shall be included in 12 + * all copies or substantial portions of the Software. 13 + * 14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 + * OTHER DEALINGS IN THE SOFTWARE. 21 + * 22 + * Authors: Ben Skeggs 23 + */ 24 + 25 + #include <subdev/bios.h> 26 + #include <subdev/bios/dcb.h> 27 + #include <subdev/bios/dp.h> 28 + #include <subdev/bios/init.h> 29 + #include <subdev/i2c.h> 30 + 31 + #include <engine/disp.h> 32 + 33 + #include "dport.h" 34 + 35 + #define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt, \ 36 + dp->outp->hasht, dp->outp->hashm, ##args) 37 + #define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt, \ 38 + dp->outp->hasht, dp->outp->hashm, ##args) 39 + 40 + /****************************************************************************** 41 + * link training 42 + *****************************************************************************/ 43 + struct dp_state { 44 + const struct nouveau_dp_func *func; 45 + struct nouveau_disp *disp; 46 + struct dcb_output *outp; 47 + struct nvbios_dpout info; 48 + u8 version; 49 + struct nouveau_i2c_port *aux; 50 + int head; 51 + u8 dpcd[4]; 52 + int link_nr; 53 + u32 link_bw; 54 + u8 stat[6]; 55 + u8 conf[4]; 56 + }; 57 + 58 + static int 59 + dp_set_link_config(struct dp_state *dp) 60 + { 61 + struct nouveau_disp *disp = dp->disp; 62 + struct nouveau_bios *bios = nouveau_bios(disp); 63 + struct nvbios_init init = { 64 + .subdev = nv_subdev(dp->disp), 65 + .bios = bios, 66 + .offset = 0x0000, 67 + .outp = dp->outp, 68 + .crtc = dp->head, 69 + .execute = 1, 70 + }; 71 + u32 lnkcmp; 72 + u8 sink[2]; 73 + 74 + DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); 75 + 76 + /* set desired link configuration on the sink */ 77 + sink[0] = dp->link_bw / 27000; 78 + sink[1] = dp->link_nr; 79 + if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) 80 + sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; 81 + 82 + nv_wraux(dp->aux, DPCD_LC00, sink, 2); 83 + 84 + /* set desired link configuration on the source */ 85 + if ((lnkcmp = dp->info.lnkcmp)) { 86 + if (dp->version < 0x30) { 87 + while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp)) 88 + lnkcmp += 4; 89 + init.offset = nv_ro16(bios, lnkcmp + 2); 90 + } else { 91 + while ((dp->link_bw / 27000) < nv_ro08(bios, lnkcmp)) 92 + lnkcmp += 3; 93 + init.offset = nv_ro16(bios, lnkcmp + 1); 94 + } 95 + 96 + nvbios_exec(&init); 97 + } 98 + 99 + return dp->func->lnk_ctl(dp->disp, dp->outp, dp->head, 100 + dp->link_nr, dp->link_bw / 27000, 101 + dp->dpcd[DPCD_RC02] & 102 + DPCD_RC02_ENHANCED_FRAME_CAP); 103 + } 104 + 105 + static void 106 + dp_set_training_pattern(struct dp_state *dp, u8 pattern) 107 + { 108 + u8 sink_tp; 109 + 110 + DBG("training pattern %d\n", pattern); 111 + dp->func->pattern(dp->disp, dp->outp, dp->head, pattern); 112 + 113 + nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1); 114 + sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; 115 + sink_tp |= pattern; 116 + nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1); 117 + } 118 + 119 + static int 120 + dp_link_train_commit(struct dp_state *dp) 121 + { 122 + int i; 123 + 124 + for (i = 0; i < dp->link_nr; i++) { 125 + u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; 126 + u8 lpre = (lane & 0x0c) >> 2; 127 + u8 lvsw = (lane & 0x03) >> 0; 128 + 129 + dp->conf[i] = (lpre << 3) | lvsw; 130 + if (lvsw == 3) 131 + dp->conf[i] |= DPCD_LC03_MAX_SWING_REACHED; 132 + if (lpre == 3) 133 + dp->conf[i] |= DPCD_LC03_MAX_PRE_EMPHASIS_REACHED; 134 + 135 + DBG("config lane %d %02x\n", i, dp->conf[i]); 136 + dp->func->drv_ctl(dp->disp, dp->outp, dp->head, i, lvsw, lpre); 137 + } 138 + 139 + return nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4); 140 + } 141 + 142 + static int 143 + dp_link_train_update(struct dp_state *dp, u32 delay) 144 + { 145 + int ret; 146 + 147 + udelay(delay); 148 + 149 + ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6); 150 + if (ret) 151 + return ret; 152 + 153 + DBG("status %*ph\n", 6, dp->stat); 154 + return 0; 155 + } 156 + 157 + static int 158 + dp_link_train_cr(struct dp_state *dp) 159 + { 160 + bool cr_done = false, abort = false; 161 + int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; 162 + int tries = 0, i; 163 + 164 + dp_set_training_pattern(dp, 1); 165 + 166 + do { 167 + if (dp_link_train_commit(dp) || 168 + dp_link_train_update(dp, 100)) 169 + break; 170 + 171 + cr_done = true; 172 + for (i = 0; i < dp->link_nr; i++) { 173 + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; 174 + if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { 175 + cr_done = false; 176 + if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED) 177 + abort = true; 178 + break; 179 + } 180 + } 181 + 182 + if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { 183 + voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; 184 + tries = 0; 185 + } 186 + } while (!cr_done && !abort && ++tries < 5); 187 + 188 + return cr_done ? 0 : -1; 189 + } 190 + 191 + static int 192 + dp_link_train_eq(struct dp_state *dp) 193 + { 194 + bool eq_done, cr_done = true; 195 + int tries = 0, i; 196 + 197 + dp_set_training_pattern(dp, 2); 198 + 199 + do { 200 + if (dp_link_train_update(dp, 400)) 201 + break; 202 + 203 + eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); 204 + for (i = 0; i < dp->link_nr && eq_done; i++) { 205 + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; 206 + if (!(lane & DPCD_LS02_LANE0_CR_DONE)) 207 + cr_done = false; 208 + if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || 209 + !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) 210 + eq_done = false; 211 + } 212 + 213 + if (dp_link_train_commit(dp)) 214 + break; 215 + } while (!eq_done && cr_done && ++tries <= 5); 216 + 217 + return eq_done ? 0 : -1; 218 + } 219 + 220 + static void 221 + dp_link_train_init(struct dp_state *dp, bool spread) 222 + { 223 + struct nvbios_init init = { 224 + .subdev = nv_subdev(dp->disp), 225 + .bios = nouveau_bios(dp->disp), 226 + .outp = dp->outp, 227 + .crtc = dp->head, 228 + .execute = 1, 229 + }; 230 + 231 + /* set desired spread */ 232 + if (spread) 233 + init.offset = dp->info.script[2]; 234 + else 235 + init.offset = dp->info.script[3]; 236 + nvbios_exec(&init); 237 + 238 + /* pre-train script */ 239 + init.offset = dp->info.script[0]; 240 + nvbios_exec(&init); 241 + } 242 + 243 + static void 244 + dp_link_train_fini(struct dp_state *dp) 245 + { 246 + struct nvbios_init init = { 247 + .subdev = nv_subdev(dp->disp), 248 + .bios = nouveau_bios(dp->disp), 249 + .outp = dp->outp, 250 + .crtc = dp->head, 251 + .execute = 1, 252 + }; 253 + 254 + /* post-train script */ 255 + init.offset = dp->info.script[1], 256 + nvbios_exec(&init); 257 + } 258 + 259 + int 260 + nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, 261 + struct dcb_output *outp, int head, u32 datarate) 262 + { 263 + struct nouveau_bios *bios = nouveau_bios(disp); 264 + struct nouveau_i2c *i2c = nouveau_i2c(disp); 265 + struct dp_state _dp = { 266 + .disp = disp, 267 + .func = func, 268 + .outp = outp, 269 + .head = head, 270 + }, *dp = &_dp; 271 + const u32 bw_list[] = { 270000, 162000, 0 }; 272 + const u32 *link_bw = bw_list; 273 + u8 hdr, cnt, len; 274 + u32 data; 275 + int ret; 276 + 277 + /* find the bios displayport data relevant to this output */ 278 + data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version, 279 + &hdr, &cnt, &len, &dp->info); 280 + if (!data) { 281 + ERR("bios data not found\n"); 282 + return -EINVAL; 283 + } 284 + 285 + /* acquire the aux channel and fetch some info about the display */ 286 + if (outp->location) 287 + dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev)); 288 + else 289 + dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index)); 290 + if (!dp->aux) { 291 + ERR("no aux channel?!\n"); 292 + return -ENODEV; 293 + } 294 + 295 + ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd)); 296 + if (ret) { 297 + ERR("failed to read DPCD\n"); 298 + return ret; 299 + } 300 + 301 + /* adjust required bandwidth for 8B/10B coding overhead */ 302 + datarate = (datarate / 8) * 10; 303 + 304 + /* enable down-spreading and execute pre-train script from vbios */ 305 + dp_link_train_init(dp, dp->dpcd[3] & 0x01); 306 + 307 + /* start off at highest link rate supported by encoder and display */ 308 + while (*link_bw > (dp->dpcd[1] * 27000)) 309 + link_bw++; 310 + 311 + while (link_bw[0]) { 312 + /* find minimum required lane count at this link rate */ 313 + dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT; 314 + while ((dp->link_nr >> 1) * link_bw[0] > datarate) 315 + dp->link_nr >>= 1; 316 + 317 + /* drop link rate to minimum with this lane count */ 318 + while ((link_bw[1] * dp->link_nr) > datarate) 319 + link_bw++; 320 + dp->link_bw = link_bw[0]; 321 + 322 + /* program selected link configuration */ 323 + ret = dp_set_link_config(dp); 324 + if (ret == 0) { 325 + /* attempt to train the link at this configuration */ 326 + memset(dp->stat, 0x00, sizeof(dp->stat)); 327 + if (!dp_link_train_cr(dp) && 328 + !dp_link_train_eq(dp)) 329 + break; 330 + } else 331 + if (ret >= 1) { 332 + /* dp_set_link_config() handled training */ 333 + break; 334 + } 335 + 336 + /* retry at lower rate */ 337 + link_bw++; 338 + } 339 + 340 + /* finish link training */ 341 + dp_set_training_pattern(dp, 0); 342 + 343 + /* execute post-train script from vbios */ 344 + dp_link_train_fini(dp); 345 + return true; 346 + }
+77
drivers/gpu/drm/nouveau/core/engine/disp/dport.h
··· 1 + #ifndef __NVKM_DISP_DPORT_H__ 2 + #define __NVKM_DISP_DPORT_H__ 3 + 4 + /* DPCD Receiver Capabilities */ 5 + #define DPCD_RC00 0x00000 6 + #define DPCD_RC00_DPCD_REV 0xff 7 + #define DPCD_RC01 0x00001 8 + #define DPCD_RC01_MAX_LINK_RATE 0xff 9 + #define DPCD_RC02 0x00002 10 + #define DPCD_RC02_ENHANCED_FRAME_CAP 0x80 11 + #define DPCD_RC02_MAX_LANE_COUNT 0x1f 12 + #define DPCD_RC03 0x00003 13 + #define DPCD_RC03_MAX_DOWNSPREAD 0x01 14 + 15 + /* DPCD Link Configuration */ 16 + #define DPCD_LC00 0x00100 17 + #define DPCD_LC00_LINK_BW_SET 0xff 18 + #define DPCD_LC01 0x00101 19 + #define DPCD_LC01_ENHANCED_FRAME_EN 0x80 20 + #define DPCD_LC01_LANE_COUNT_SET 0x1f 21 + #define DPCD_LC02 0x00102 22 + #define DPCD_LC02_TRAINING_PATTERN_SET 0x03 23 + #define DPCD_LC03(l) ((l) + 0x00103) 24 + #define DPCD_LC03_MAX_PRE_EMPHASIS_REACHED 0x20 25 + #define DPCD_LC03_PRE_EMPHASIS_SET 0x18 26 + #define DPCD_LC03_MAX_SWING_REACHED 0x04 27 + #define DPCD_LC03_VOLTAGE_SWING_SET 0x03 28 + 29 + /* DPCD Link/Sink Status */ 30 + #define DPCD_LS02 0x00202 31 + #define DPCD_LS02_LANE1_SYMBOL_LOCKED 0x40 32 + #define DPCD_LS02_LANE1_CHANNEL_EQ_DONE 0x20 33 + #define DPCD_LS02_LANE1_CR_DONE 0x10 34 + #define DPCD_LS02_LANE0_SYMBOL_LOCKED 0x04 35 + #define DPCD_LS02_LANE0_CHANNEL_EQ_DONE 0x02 36 + #define DPCD_LS02_LANE0_CR_DONE 0x01 37 + #define DPCD_LS03 0x00203 38 + #define DPCD_LS03_LANE3_SYMBOL_LOCKED 0x40 39 + #define DPCD_LS03_LANE3_CHANNEL_EQ_DONE 0x20 40 + #define DPCD_LS03_LANE3_CR_DONE 0x10 41 + #define DPCD_LS03_LANE2_SYMBOL_LOCKED 0x04 42 + #define DPCD_LS03_LANE2_CHANNEL_EQ_DONE 0x02 43 + #define DPCD_LS03_LANE2_CR_DONE 0x01 44 + #define DPCD_LS04 0x00204 45 + #define DPCD_LS04_LINK_STATUS_UPDATED 0x80 46 + #define DPCD_LS04_DOWNSTREAM_PORT_STATUS_CHANGED 0x40 47 + #define DPCD_LS04_INTERLANE_ALIGN_DONE 0x01 48 + #define DPCD_LS06 0x00206 49 + #define DPCD_LS06_LANE1_PRE_EMPHASIS 0xc0 50 + #define DPCD_LS06_LANE1_VOLTAGE_SWING 0x30 51 + #define DPCD_LS06_LANE0_PRE_EMPHASIS 0x0c 52 + #define DPCD_LS06_LANE0_VOLTAGE_SWING 0x03 53 + #define DPCD_LS07 0x00207 54 + #define DPCD_LS07_LANE3_PRE_EMPHASIS 0xc0 55 + #define DPCD_LS07_LANE3_VOLTAGE_SWING 0x30 56 + #define DPCD_LS07_LANE2_PRE_EMPHASIS 0x0c 57 + #define DPCD_LS07_LANE2_VOLTAGE_SWING 0x03 58 + 59 + struct nouveau_disp; 60 + struct dcb_output; 61 + 62 + struct nouveau_dp_func { 63 + int (*pattern)(struct nouveau_disp *, struct dcb_output *, 64 + int head, int pattern); 65 + int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head, 66 + int link_nr, int link_bw, bool enh_frame); 67 + int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head, 68 + int lane, int swing, int preem); 69 + }; 70 + 71 + extern const struct nouveau_dp_func nv94_sor_dp_func; 72 + extern const struct nouveau_dp_func nvd0_sor_dp_func; 73 + 74 + int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *, 75 + struct dcb_output *, int, u32); 76 + 77 + #endif
+24 -5
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
··· 911 911 } 912 912 913 913 data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); 914 - if (data) { 914 + if (data && id < 0xff) { 915 915 data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); 916 916 if (data) { 917 917 struct nvbios_init init = { ··· 1078 1078 head = ffs((super & 0x00000180) >> 7) - 1; 1079 1079 if (head >= 0) { 1080 1080 u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; 1081 - u32 conf = exec_clkcmp(priv, head, 0, pclk, &outp); 1081 + u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); 1082 1082 if (conf != ~0) { 1083 + if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) { 1084 + u32 soff = (ffs(outp.or) - 1) * 0x08; 1085 + u32 ctrl = nv_rd32(priv, 0x610798 + soff); 1086 + u32 datarate; 1087 + 1088 + switch ((ctrl & 0x000f0000) >> 16) { 1089 + case 6: datarate = pclk * 30 / 8; break; 1090 + case 5: datarate = pclk * 24 / 8; break; 1091 + case 2: 1092 + default: 1093 + datarate = pclk * 18 / 8; 1094 + break; 1095 + } 1096 + 1097 + nouveau_dp_train(&priv->base, priv->sor.dp, 1098 + &outp, head, datarate); 1099 + } 1100 + 1101 + exec_clkcmp(priv, head, 0, pclk, &outp); 1102 + 1083 1103 if (outp.type == DCB_OUTPUT_ANALOG) { 1084 1104 addr = 0x614280 + (ffs(outp.or) - 1) * 0x800; 1085 1105 mask = 0xffffffff; ··· 1148 1128 if (head >= 0) { 1149 1129 struct dcb_output outp; 1150 1130 u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; 1151 - if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) { 1152 - if (outp.type == DCB_OUTPUT_TMDS) 1131 + if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) 1132 + if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS) 1153 1133 nv50_disp_intr_unk40_tmds(priv, &outp); 1154 - } 1155 1134 } 1156 1135 1157 1136 nv_wr32(priv, 0x610030, 0x80000000);
+2 -16
drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
··· 10 10 #include <engine/dmaobj.h> 11 11 #include <engine/disp.h> 12 12 13 - struct dcb_output; 13 + #include "dport.h" 14 14 15 15 struct nv50_disp_priv { 16 16 struct nouveau_disp base; ··· 32 32 int (*power)(struct nv50_disp_priv *, int sor, u32 data); 33 33 int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32); 34 34 int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32); 35 - int (*dp_train_init)(struct nv50_disp_priv *, int sor, int link, 36 - int head, u16 type, u16 mask, u32 data, 37 - struct dcb_output *); 38 - int (*dp_train_fini)(struct nv50_disp_priv *, int sor, int link, 39 - int head, u16 type, u16 mask, u32 data, 40 - struct dcb_output *); 41 - int (*dp_train)(struct nv50_disp_priv *, int sor, int link, 42 - u16 type, u16 mask, u32 data, 43 - struct dcb_output *); 44 - int (*dp_lnkctl)(struct nv50_disp_priv *, int sor, int link, 45 - int head, u16 type, u16 mask, u32 data, 46 - struct dcb_output *); 47 - int (*dp_drvctl)(struct nv50_disp_priv *, int sor, int link, 48 - int lane, u16 type, u16 mask, u32 data, 49 - struct dcb_output *); 50 35 u32 lvdsconf; 36 + const struct nouveau_dp_func *dp; 51 37 } sor; 52 38 }; 53 39
+1 -11
drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
··· 44 44 { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, 45 45 { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, 46 46 { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, 47 - { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN) , nv50_sor_mthd }, 48 - { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL) , nv50_sor_mthd }, 49 - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd }, 50 - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd }, 51 - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd }, 52 - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd }, 53 47 { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, 54 48 { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, 55 49 {}, ··· 81 87 priv->dac.sense = nv50_dac_sense; 82 88 priv->sor.power = nv50_sor_power; 83 89 priv->sor.hdmi = nv84_hdmi_ctrl; 84 - priv->sor.dp_train = nv94_sor_dp_train; 85 - priv->sor.dp_train_init = nv94_sor_dp_train_init; 86 - priv->sor.dp_train_fini = nv94_sor_dp_train_fini; 87 - priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl; 88 - priv->sor.dp_drvctl = nv94_sor_dp_drvctl; 90 + priv->sor.dp = &nv94_sor_dp_func; 89 91 return 0; 90 92 } 91 93
+1 -11
drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
··· 45 45 { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, 46 46 { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, 47 47 { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, 48 - { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN) , nv50_sor_mthd }, 49 - { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL) , nv50_sor_mthd }, 50 - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd }, 51 - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd }, 52 - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd }, 53 - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd }, 54 48 { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, 55 49 { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, 56 50 {}, ··· 83 89 priv->sor.power = nv50_sor_power; 84 90 priv->sor.hda_eld = nva3_hda_eld; 85 91 priv->sor.hdmi = nva3_hdmi_ctrl; 86 - priv->sor.dp_train = nv94_sor_dp_train; 87 - priv->sor.dp_train_init = nv94_sor_dp_train_init; 88 - priv->sor.dp_train_fini = nv94_sor_dp_train_fini; 89 - priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl; 90 - priv->sor.dp_drvctl = nv94_sor_dp_drvctl; 92 + priv->sor.dp = &nv94_sor_dp_func; 91 93 return 0; 92 94 } 93 95
+28 -13
drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
··· 650 650 651 651 static u32 652 652 exec_clkcmp(struct nv50_disp_priv *priv, int head, int outp, 653 - u32 ctrl, int id, u32 pclk) 653 + u32 ctrl, int id, u32 pclk, struct dcb_output *dcb) 654 654 { 655 655 struct nouveau_bios *bios = nouveau_bios(priv); 656 656 struct nvbios_outp info1; 657 657 struct nvbios_ocfg info2; 658 - struct dcb_output dcb; 659 658 u8 ver, hdr, cnt, len; 660 659 u32 data, conf = ~0; 661 660 662 - data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info1); 661 + data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1); 663 662 if (data == 0x0000) 664 663 return conf; 665 664 666 - switch (dcb.type) { 665 + switch (dcb->type) { 667 666 case DCB_OUTPUT_TMDS: 668 667 conf = (ctrl & 0x00000f00) >> 8; 669 668 if (pclk >= 165000) ··· 681 682 } 682 683 683 684 data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); 684 - if (data) { 685 + if (data && id < 0xff) { 685 686 data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); 686 687 if (data) { 687 688 struct nvbios_init init = { 688 689 .subdev = nv_subdev(priv), 689 690 .bios = bios, 690 691 .offset = data, 691 - .outp = &dcb, 692 + .outp = dcb, 692 693 .crtc = head, 693 694 .execute = 1, 694 695 }; ··· 763 764 static void 764 765 nvd0_display_unk2_handler(struct nv50_disp_priv *priv, u32 head, u32 mask) 765 766 { 767 + struct dcb_output outp; 766 768 u32 pclk; 767 769 int i; 768 770 ··· 785 785 for (i = 0; mask && i < 8; i++) { 786 786 u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20)); 787 787 if (mcp & (1 << head)) { 788 - u32 cfg = exec_clkcmp(priv, head, i, mcp, 0, pclk); 788 + u32 cfg = exec_clkcmp(priv, head, i, mcp, 0xff, pclk, &outp); 789 789 if (cfg != ~0) { 790 790 u32 addr, mask, data = 0x00000000; 791 + 792 + if (outp.type == DCB_OUTPUT_DP) { 793 + switch ((mcp & 0x000f0000) >> 16) { 794 + case 6: pclk = pclk * 30 / 8; break; 795 + case 5: pclk = pclk * 24 / 8; break; 796 + case 2: 797 + default: 798 + pclk = pclk * 18 / 8; 799 + break; 800 + } 801 + 802 + nouveau_dp_train(&priv->base, 803 + priv->sor.dp, 804 + &outp, head, pclk); 805 + } 806 + 807 + exec_clkcmp(priv, head, i, mcp, 0, pclk, &outp); 808 + 791 809 if (i < 4) { 792 810 addr = 0x612280 + ((i - 0) * 0x800); 793 811 mask = 0xffffffff; ··· 838 820 static void 839 821 nvd0_display_unk4_handler(struct nv50_disp_priv *priv, u32 head, u32 mask) 840 822 { 823 + struct dcb_output outp; 841 824 int pclk, i; 842 825 843 826 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; ··· 846 827 for (i = 0; mask && i < 8; i++) { 847 828 u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20)); 848 829 if (mcp & (1 << head)) 849 - exec_clkcmp(priv, head, i, mcp, 1, pclk); 830 + exec_clkcmp(priv, head, i, mcp, 1, pclk, &outp); 850 831 } 851 832 852 833 nv_wr32(priv, 0x6101d4, 0x00000000); ··· 962 943 priv->sor.power = nv50_sor_power; 963 944 priv->sor.hda_eld = nvd0_hda_eld; 964 945 priv->sor.hdmi = nvd0_hdmi_ctrl; 965 - priv->sor.dp_train = nvd0_sor_dp_train; 966 - priv->sor.dp_train_init = nv94_sor_dp_train_init; 967 - priv->sor.dp_train_fini = nv94_sor_dp_train_fini; 968 - priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl; 969 - priv->sor.dp_drvctl = nvd0_sor_dp_drvctl; 946 + priv->sor.dp = &nvd0_sor_dp_func; 970 947 return 0; 971 948 } 972 949
+1 -5
drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
··· 73 73 priv->sor.power = nv50_sor_power; 74 74 priv->sor.hda_eld = nvd0_hda_eld; 75 75 priv->sor.hdmi = nvd0_hdmi_ctrl; 76 - priv->sor.dp_train = nvd0_sor_dp_train; 77 - priv->sor.dp_train_init = nv94_sor_dp_train_init; 78 - priv->sor.dp_train_fini = nv94_sor_dp_train_fini; 79 - priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl; 80 - priv->sor.dp_drvctl = nvd0_sor_dp_drvctl; 76 + priv->sor.dp = &nvd0_sor_dp_func; 81 77 return 0; 82 78 } 83 79
-25
drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
··· 79 79 priv->sor.lvdsconf = data & NV50_DISP_SOR_LVDS_SCRIPT_ID; 80 80 ret = 0; 81 81 break; 82 - case NV94_DISP_SOR_DP_TRAIN: 83 - switch (data & NV94_DISP_SOR_DP_TRAIN_OP) { 84 - case NV94_DISP_SOR_DP_TRAIN_OP_PATTERN: 85 - ret = priv->sor.dp_train(priv, or, link, type, mask, data, &outp); 86 - break; 87 - case NV94_DISP_SOR_DP_TRAIN_OP_INIT: 88 - ret = priv->sor.dp_train_init(priv, or, link, head, type, mask, data, &outp); 89 - break; 90 - case NV94_DISP_SOR_DP_TRAIN_OP_FINI: 91 - ret = priv->sor.dp_train_fini(priv, or, link, head, type, mask, data, &outp); 92 - break; 93 - default: 94 - break; 95 - } 96 - break; 97 - case NV94_DISP_SOR_DP_LNKCTL: 98 - ret = priv->sor.dp_lnkctl(priv, or, link, head, type, mask, data, &outp); 99 - break; 100 - case NV94_DISP_SOR_DP_DRVCTL(0): 101 - case NV94_DISP_SOR_DP_DRVCTL(1): 102 - case NV94_DISP_SOR_DP_DRVCTL(2): 103 - case NV94_DISP_SOR_DP_DRVCTL(3): 104 - ret = priv->sor.dp_drvctl(priv, or, link, (mthd & 0xc0) >> 6, 105 - type, mask, data, &outp); 106 - break; 107 82 default: 108 83 BUG_ON(1); 109 84 }
+45 -108
drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
··· 33 33 #include "nv50.h" 34 34 35 35 static inline u32 36 + nv94_sor_soff(struct dcb_output *outp) 37 + { 38 + return (ffs(outp->or) - 1) * 0x800; 39 + } 40 + 41 + static inline u32 42 + nv94_sor_loff(struct dcb_output *outp) 43 + { 44 + return nv94_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80; 45 + } 46 + 47 + static inline u32 36 48 nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) 37 49 { 38 50 static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ ··· 54 42 return nv94[lane]; 55 43 } 56 44 57 - int 58 - nv94_sor_dp_train_init(struct nv50_disp_priv *priv, int or, int link, int head, 59 - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) 45 + static int 46 + nv94_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, 47 + int head, int pattern) 60 48 { 61 - struct nouveau_bios *bios = nouveau_bios(priv); 62 - struct nvbios_dpout info; 63 - u8 ver, hdr, cnt, len; 64 - u16 outp; 65 - 66 - outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); 67 - if (outp) { 68 - struct nvbios_init init = { 69 - .subdev = nv_subdev(priv), 70 - .bios = bios, 71 - .outp = dcbo, 72 - .crtc = head, 73 - .execute = 1, 74 - }; 75 - 76 - if (data & NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON) 77 - init.offset = info.script[2]; 78 - else 79 - init.offset = info.script[3]; 80 - nvbios_exec(&init); 81 - 82 - init.offset = info.script[0]; 83 - nvbios_exec(&init); 84 - } 85 - 49 + struct nv50_disp_priv *priv = (void *)disp; 50 + const u32 loff = nv94_sor_loff(outp); 51 + nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24); 86 52 return 0; 87 53 } 88 54 89 - int 90 - nv94_sor_dp_train_fini(struct nv50_disp_priv *priv, int or, int link, int head, 91 - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) 55 + static int 56 + nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, 57 + int head, int link_nr, int link_bw, bool enh_frame) 92 58 { 93 - struct nouveau_bios *bios = nouveau_bios(priv); 94 - struct nvbios_dpout info; 95 - u8 ver, hdr, cnt, len; 96 - u16 outp; 97 - 98 - outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); 99 - if (outp) { 100 - struct nvbios_init init = { 101 - .subdev = nv_subdev(priv), 102 - .bios = bios, 103 - .offset = info.script[1], 104 - .outp = dcbo, 105 - .crtc = head, 106 - .execute = 1, 107 - }; 108 - 109 - nvbios_exec(&init); 110 - } 111 - 112 - return 0; 113 - } 114 - 115 - int 116 - nv94_sor_dp_train(struct nv50_disp_priv *priv, int or, int link, 117 - u16 type, u16 mask, u32 data, struct dcb_output *info) 118 - { 119 - const u32 loff = (or * 0x800) + (link * 0x80); 120 - const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN); 121 - nv_mask(priv, 0x61c10c + loff, 0x0f000000, patt << 24); 122 - return 0; 123 - } 124 - 125 - int 126 - nv94_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head, 127 - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) 128 - { 129 - struct nouveau_bios *bios = nouveau_bios(priv); 130 - const u32 loff = (or * 0x800) + (link * 0x80); 131 - const u32 soff = (or * 0x800); 132 - u16 link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8; 133 - u8 link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT); 59 + struct nv50_disp_priv *priv = (void *)disp; 60 + const u32 soff = nv94_sor_soff(outp); 61 + const u32 loff = nv94_sor_loff(outp); 134 62 u32 dpctrl = 0x00000000; 135 63 u32 clksor = 0x00000000; 136 - u32 outp, lane = 0; 137 - u8 ver, hdr, cnt, len; 138 - struct nvbios_dpout info; 64 + u32 lane = 0; 139 65 int i; 140 66 141 - /* -> 10Khz units */ 142 - link_bw *= 2700; 143 - 144 - outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); 145 - if (outp && info.lnkcmp) { 146 - struct nvbios_init init = { 147 - .subdev = nv_subdev(priv), 148 - .bios = bios, 149 - .offset = 0x0000, 150 - .outp = dcbo, 151 - .crtc = head, 152 - .execute = 1, 153 - }; 154 - 155 - while (link_bw < nv_ro16(bios, info.lnkcmp)) 156 - info.lnkcmp += 4; 157 - init.offset = nv_ro16(bios, info.lnkcmp + 2); 158 - 159 - nvbios_exec(&init); 160 - } 161 - 162 67 dpctrl |= ((1 << link_nr) - 1) << 16; 163 - if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH) 68 + if (enh_frame) 164 69 dpctrl |= 0x00004000; 165 - if (link_bw > 16200) 70 + if (link_bw > 0x06) 166 71 clksor |= 0x00040000; 167 72 168 73 for (i = 0; i < link_nr; i++) ··· 91 162 return 0; 92 163 } 93 164 94 - int 95 - nv94_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane, 96 - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) 165 + static int 166 + nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, 167 + int head, int lane, int swing, int preem) 97 168 { 98 - struct nouveau_bios *bios = nouveau_bios(priv); 99 - const u32 loff = (or * 0x800) + (link * 0x80); 100 - const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8; 101 - const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE); 169 + struct nouveau_bios *bios = nouveau_bios(disp); 170 + struct nv50_disp_priv *priv = (void *)disp; 171 + const u32 loff = nv94_sor_loff(outp); 102 172 u32 addr, shift = nv94_sor_dp_lane_map(priv, lane); 103 173 u8 ver, hdr, cnt, len; 104 - struct nvbios_dpout outp; 174 + struct nvbios_dpout info; 105 175 struct nvbios_dpcfg ocfg; 106 176 107 - addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp); 177 + addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm, 178 + &ver, &hdr, &cnt, &len, &info); 108 179 if (!addr) 109 180 return -ENODEV; 110 181 111 - addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg); 182 + addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, 183 + &ver, &hdr, &cnt, &len, &ocfg); 112 184 if (!addr) 113 185 return -EINVAL; 114 186 ··· 118 188 nv_mask(priv, 0x61c130 + loff, 0x0000ff00, ocfg.unk << 8); 119 189 return 0; 120 190 } 191 + 192 + const struct nouveau_dp_func 193 + nv94_sor_dp_func = { 194 + .pattern = nv94_sor_dp_pattern, 195 + .lnk_ctl = nv94_sor_dp_lnk_ctl, 196 + .drv_ctl = nv94_sor_dp_drv_ctl, 197 + };
+44 -46
drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
··· 33 33 #include "nv50.h" 34 34 35 35 static inline u32 36 + nvd0_sor_soff(struct dcb_output *outp) 37 + { 38 + return (ffs(outp->or) - 1) * 0x800; 39 + } 40 + 41 + static inline u32 42 + nvd0_sor_loff(struct dcb_output *outp) 43 + { 44 + return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80; 45 + } 46 + 47 + static inline u32 36 48 nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) 37 49 { 38 50 static const u8 nvd0[] = { 16, 8, 0, 24 }; 39 51 return nvd0[lane]; 40 52 } 41 53 42 - int 43 - nvd0_sor_dp_train(struct nv50_disp_priv *priv, int or, int link, 44 - u16 type, u16 mask, u32 data, struct dcb_output *info) 54 + static int 55 + nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, 56 + int head, int pattern) 45 57 { 46 - const u32 loff = (or * 0x800) + (link * 0x80); 47 - const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN); 48 - nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * patt); 58 + struct nv50_disp_priv *priv = (void *)disp; 59 + const u32 loff = nvd0_sor_loff(outp); 60 + nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern); 49 61 return 0; 50 62 } 51 63 52 - int 53 - nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head, 54 - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) 64 + static int 65 + nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, 66 + int head, int link_nr, int link_bw, bool enh_frame) 55 67 { 56 - struct nouveau_bios *bios = nouveau_bios(priv); 57 - const u32 loff = (or * 0x800) + (link * 0x80); 58 - const u32 soff = (or * 0x800); 59 - const u8 link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8; 60 - const u8 link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT); 68 + struct nv50_disp_priv *priv = (void *)disp; 69 + const u32 soff = nvd0_sor_soff(outp); 70 + const u32 loff = nvd0_sor_loff(outp); 61 71 u32 dpctrl = 0x00000000; 62 72 u32 clksor = 0x00000000; 63 - u32 outp, lane = 0; 64 - u8 ver, hdr, cnt, len; 65 - struct nvbios_dpout info; 73 + u32 lane = 0; 66 74 int i; 67 - 68 - outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); 69 - if (outp && info.lnkcmp) { 70 - struct nvbios_init init = { 71 - .subdev = nv_subdev(priv), 72 - .bios = bios, 73 - .offset = 0x0000, 74 - .outp = dcbo, 75 - .crtc = head, 76 - .execute = 1, 77 - }; 78 - 79 - while (nv_ro08(bios, info.lnkcmp) < link_bw) 80 - info.lnkcmp += 3; 81 - init.offset = nv_ro16(bios, info.lnkcmp + 1); 82 - 83 - nvbios_exec(&init); 84 - } 85 75 86 76 clksor |= link_bw << 18; 87 77 dpctrl |= ((1 << link_nr) - 1) << 16; 88 - if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH) 78 + if (enh_frame) 89 79 dpctrl |= 0x00004000; 90 80 91 81 for (i = 0; i < link_nr; i++) ··· 87 97 return 0; 88 98 } 89 99 90 - int 91 - nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane, 92 - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) 100 + static int 101 + nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, 102 + int head, int lane, int swing, int preem) 93 103 { 94 - struct nouveau_bios *bios = nouveau_bios(priv); 95 - const u32 loff = (or * 0x800) + (link * 0x80); 96 - const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8; 97 - const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE); 104 + struct nouveau_bios *bios = nouveau_bios(disp); 105 + struct nv50_disp_priv *priv = (void *)disp; 106 + const u32 loff = nvd0_sor_loff(outp); 98 107 u32 addr, shift = nvd0_sor_dp_lane_map(priv, lane); 99 108 u8 ver, hdr, cnt, len; 100 - struct nvbios_dpout outp; 109 + struct nvbios_dpout info; 101 110 struct nvbios_dpcfg ocfg; 102 111 103 - addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp); 112 + addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm, 113 + &ver, &hdr, &cnt, &len, &info); 104 114 if (!addr) 105 115 return -ENODEV; 106 116 107 - addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg); 117 + addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, 118 + &ver, &hdr, &cnt, &len, &ocfg); 108 119 if (!addr) 109 120 return -EINVAL; 110 121 ··· 115 124 nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000); 116 125 return 0; 117 126 } 127 + 128 + const struct nouveau_dp_func 129 + nvd0_sor_dp_func = { 130 + .pattern = nvd0_sor_dp_pattern, 131 + .lnk_ctl = nvd0_sor_dp_lnk_ctl, 132 + .drv_ctl = nvd0_sor_dp_drv_ctl, 133 + };
-19
drivers/gpu/drm/nouveau/core/include/core/class.h
··· 198 198 #define NV84_DISP_SOR_HDMI_PWR_REKEY 0x0000007f 199 199 #define NV50_DISP_SOR_LVDS_SCRIPT 0x00013000 200 200 #define NV50_DISP_SOR_LVDS_SCRIPT_ID 0x0000ffff 201 - #define NV94_DISP_SOR_DP_TRAIN 0x00016000 202 - #define NV94_DISP_SOR_DP_TRAIN_OP 0xf0000000 203 - #define NV94_DISP_SOR_DP_TRAIN_OP_PATTERN 0x00000000 204 - #define NV94_DISP_SOR_DP_TRAIN_OP_INIT 0x10000000 205 - #define NV94_DISP_SOR_DP_TRAIN_OP_FINI 0x20000000 206 - #define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD 0x00000001 207 - #define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF 0x00000000 208 - #define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON 0x00000001 209 - #define NV94_DISP_SOR_DP_TRAIN_PATTERN 0x00000003 210 - #define NV94_DISP_SOR_DP_TRAIN_PATTERN_DISABLED 0x00000000 211 - #define NV94_DISP_SOR_DP_LNKCTL 0x00016040 212 - #define NV94_DISP_SOR_DP_LNKCTL_FRAME 0x80000000 213 - #define NV94_DISP_SOR_DP_LNKCTL_FRAME_STD 0x00000000 214 - #define NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH 0x80000000 215 - #define NV94_DISP_SOR_DP_LNKCTL_WIDTH 0x00001f00 216 - #define NV94_DISP_SOR_DP_LNKCTL_COUNT 0x00000007 217 - #define NV94_DISP_SOR_DP_DRVCTL(l) ((l) * 0x40 + 0x00016100) 218 - #define NV94_DISP_SOR_DP_DRVCTL_VS 0x00000300 219 - #define NV94_DISP_SOR_DP_DRVCTL_PE 0x00000003 220 201 221 202 #define NV50_DISP_DAC_MTHD 0x00020000 222 203 #define NV50_DISP_DAC_MTHD_TYPE 0x0000f000
-293
drivers/gpu/drm/nouveau/nouveau_dp.c
··· 35 35 #include <subdev/gpio.h> 36 36 #include <subdev/i2c.h> 37 37 38 - /****************************************************************************** 39 - * link training 40 - *****************************************************************************/ 41 - struct dp_state { 42 - struct nouveau_i2c_port *auxch; 43 - struct nouveau_object *core; 44 - struct dcb_output *dcb; 45 - int crtc; 46 - u8 *dpcd; 47 - int link_nr; 48 - u32 link_bw; 49 - u8 stat[6]; 50 - u8 conf[4]; 51 - }; 52 - 53 - static void 54 - dp_set_link_config(struct drm_device *dev, struct dp_state *dp) 55 - { 56 - struct nouveau_drm *drm = nouveau_drm(dev); 57 - struct dcb_output *dcb = dp->dcb; 58 - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); 59 - const u32 moff = (dp->crtc << 3) | (link << 2) | or; 60 - u8 sink[2]; 61 - u32 data; 62 - 63 - NV_DEBUG(drm, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); 64 - 65 - /* set desired link configuration on the source */ 66 - data = ((dp->link_bw / 27000) << 8) | dp->link_nr; 67 - if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) 68 - data |= NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH; 69 - 70 - nv_call(dp->core, NV94_DISP_SOR_DP_LNKCTL + moff, data); 71 - 72 - /* inform the sink of the new configuration */ 73 - sink[0] = dp->link_bw / 27000; 74 - sink[1] = dp->link_nr; 75 - if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) 76 - sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; 77 - 78 - nv_wraux(dp->auxch, DP_LINK_BW_SET, sink, 2); 79 - } 80 - 81 - static void 82 - dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern) 83 - { 84 - struct nouveau_drm *drm = nouveau_drm(dev); 85 - struct dcb_output *dcb = dp->dcb; 86 - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); 87 - const u32 moff = (dp->crtc << 3) | (link << 2) | or; 88 - u8 sink_tp; 89 - 90 - NV_DEBUG(drm, "training pattern %d\n", pattern); 91 - 92 - nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, pattern); 93 - 94 - nv_rdaux(dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1); 95 - sink_tp &= ~DP_TRAINING_PATTERN_MASK; 96 - sink_tp |= pattern; 97 - nv_wraux(dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1); 98 - } 99 - 100 - static int 101 - dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) 102 - { 103 - struct nouveau_drm *drm = nouveau_drm(dev); 104 - struct dcb_output *dcb = dp->dcb; 105 - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); 106 - const u32 moff = (dp->crtc << 3) | (link << 2) | or; 107 - int i; 108 - 109 - for (i = 0; i < dp->link_nr; i++) { 110 - u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; 111 - u8 lpre = (lane & 0x0c) >> 2; 112 - u8 lvsw = (lane & 0x03) >> 0; 113 - 114 - dp->conf[i] = (lpre << 3) | lvsw; 115 - if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) 116 - dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; 117 - if ((lpre << 3) == DP_TRAIN_PRE_EMPHASIS_9_5) 118 - dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; 119 - 120 - NV_DEBUG(drm, "config lane %d %02x\n", i, dp->conf[i]); 121 - 122 - nv_call(dp->core, NV94_DISP_SOR_DP_DRVCTL(i) + moff, (lvsw << 8) | lpre); 123 - } 124 - 125 - return nv_wraux(dp->auxch, DP_TRAINING_LANE0_SET, dp->conf, 4); 126 - } 127 - 128 - static int 129 - dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay) 130 - { 131 - struct nouveau_drm *drm = nouveau_drm(dev); 132 - int ret; 133 - 134 - udelay(delay); 135 - 136 - ret = nv_rdaux(dp->auxch, DP_LANE0_1_STATUS, dp->stat, 6); 137 - if (ret) 138 - return ret; 139 - 140 - NV_DEBUG(drm, "status %*ph\n", 6, dp->stat); 141 - return 0; 142 - } 143 - 144 - static int 145 - dp_link_train_cr(struct drm_device *dev, struct dp_state *dp) 146 - { 147 - bool cr_done = false, abort = false; 148 - int voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; 149 - int tries = 0, i; 150 - 151 - dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_1); 152 - 153 - do { 154 - if (dp_link_train_commit(dev, dp) || 155 - dp_link_train_update(dev, dp, 100)) 156 - break; 157 - 158 - cr_done = true; 159 - for (i = 0; i < dp->link_nr; i++) { 160 - u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; 161 - if (!(lane & DP_LANE_CR_DONE)) { 162 - cr_done = false; 163 - if (dp->conf[i] & DP_TRAIN_MAX_SWING_REACHED) 164 - abort = true; 165 - break; 166 - } 167 - } 168 - 169 - if ((dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { 170 - voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; 171 - tries = 0; 172 - } 173 - } while (!cr_done && !abort && ++tries < 5); 174 - 175 - return cr_done ? 0 : -1; 176 - } 177 - 178 - static int 179 - dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) 180 - { 181 - bool eq_done, cr_done = true; 182 - int tries = 0, i; 183 - 184 - dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2); 185 - 186 - do { 187 - if (dp_link_train_update(dev, dp, 400)) 188 - break; 189 - 190 - eq_done = !!(dp->stat[2] & DP_INTERLANE_ALIGN_DONE); 191 - for (i = 0; i < dp->link_nr && eq_done; i++) { 192 - u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; 193 - if (!(lane & DP_LANE_CR_DONE)) 194 - cr_done = false; 195 - if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || 196 - !(lane & DP_LANE_SYMBOL_LOCKED)) 197 - eq_done = false; 198 - } 199 - 200 - if (dp_link_train_commit(dev, dp)) 201 - break; 202 - } while (!eq_done && cr_done && ++tries <= 5); 203 - 204 - return eq_done ? 0 : -1; 205 - } 206 - 207 - static void 208 - dp_link_train_init(struct drm_device *dev, struct dp_state *dp, bool spread) 209 - { 210 - struct dcb_output *dcb = dp->dcb; 211 - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); 212 - const u32 moff = (dp->crtc << 3) | (link << 2) | or; 213 - 214 - nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, (spread ? 215 - NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON : 216 - NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF) | 217 - NV94_DISP_SOR_DP_TRAIN_OP_INIT); 218 - } 219 - 220 - static void 221 - dp_link_train_fini(struct drm_device *dev, struct dp_state *dp) 222 - { 223 - struct dcb_output *dcb = dp->dcb; 224 - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); 225 - const u32 moff = (dp->crtc << 3) | (link << 2) | or; 226 - 227 - nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, 228 - NV94_DISP_SOR_DP_TRAIN_OP_FINI); 229 - } 230 - 231 - static bool 232 - nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, 233 - struct nouveau_object *core) 234 - { 235 - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 236 - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 237 - struct nouveau_connector *nv_connector = 238 - nouveau_encoder_connector_get(nv_encoder); 239 - struct drm_device *dev = encoder->dev; 240 - struct nouveau_drm *drm = nouveau_drm(dev); 241 - struct nouveau_gpio *gpio = nouveau_gpio(drm->device); 242 - const u32 bw_list[] = { 270000, 162000, 0 }; 243 - const u32 *link_bw = bw_list; 244 - struct dp_state dp; 245 - 246 - dp.auxch = nv_encoder->i2c; 247 - if (!dp.auxch) 248 - return false; 249 - 250 - dp.core = core; 251 - dp.dcb = nv_encoder->dcb; 252 - dp.crtc = nv_crtc->index; 253 - dp.dpcd = nv_encoder->dp.dpcd; 254 - 255 - /* adjust required bandwidth for 8B/10B coding overhead */ 256 - datarate = (datarate / 8) * 10; 257 - 258 - /* some sinks toggle hotplug in response to some of the actions 259 - * we take during link training (DP_SET_POWER is one), we need 260 - * to ignore them for the moment to avoid races. 261 - */ 262 - nouveau_event_put(gpio->events, nv_connector->hpd.line, 263 - &nv_connector->hpd_func); 264 - 265 - /* enable down-spreading and execute pre-train script from vbios */ 266 - dp_link_train_init(dev, &dp, nv_encoder->dp.dpcd[3] & 1); 267 - 268 - /* start off at highest link rate supported by encoder and display */ 269 - while (*link_bw > nv_encoder->dp.link_bw) 270 - link_bw++; 271 - 272 - while (link_bw[0]) { 273 - /* find minimum required lane count at this link rate */ 274 - dp.link_nr = nv_encoder->dp.link_nr; 275 - while ((dp.link_nr >> 1) * link_bw[0] > datarate) 276 - dp.link_nr >>= 1; 277 - 278 - /* drop link rate to minimum with this lane count */ 279 - while ((link_bw[1] * dp.link_nr) > datarate) 280 - link_bw++; 281 - dp.link_bw = link_bw[0]; 282 - 283 - /* program selected link configuration */ 284 - dp_set_link_config(dev, &dp); 285 - 286 - /* attempt to train the link at this configuration */ 287 - memset(dp.stat, 0x00, sizeof(dp.stat)); 288 - if (!dp_link_train_cr(dev, &dp) && 289 - !dp_link_train_eq(dev, &dp)) 290 - break; 291 - 292 - /* retry at lower rate */ 293 - link_bw++; 294 - } 295 - 296 - /* finish link training */ 297 - dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE); 298 - 299 - /* execute post-train script from vbios */ 300 - dp_link_train_fini(dev, &dp); 301 - 302 - /* re-enable hotplug detect */ 303 - nouveau_event_get(gpio->events, nv_connector->hpd.line, 304 - &nv_connector->hpd_func); 305 - return true; 306 - } 307 - 308 - void 309 - nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate, 310 - struct nouveau_object *core) 311 - { 312 - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 313 - struct nouveau_i2c_port *auxch; 314 - u8 status; 315 - 316 - auxch = nv_encoder->i2c; 317 - if (!auxch) 318 - return; 319 - 320 - if (mode == DRM_MODE_DPMS_ON) 321 - status = DP_SET_POWER_D0; 322 - else 323 - status = DP_SET_POWER_D3; 324 - 325 - nv_wraux(auxch, DP_SET_POWER, &status, 1); 326 - 327 - if (mode == DRM_MODE_DPMS_ON) 328 - nouveau_dp_link_train(encoder, datarate, core); 329 - } 330 - 331 38 static void 332 39 nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch, 333 40 u8 *dpcd)
-3
drivers/gpu/drm/nouveau/nv50_display.c
··· 1683 1683 } 1684 1684 1685 1685 nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON)); 1686 - 1687 - if (nv_encoder->dcb->type == DCB_OUTPUT_DP) 1688 - nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, disp->core); 1689 1686 } 1690 1687 1691 1688 static bool