Loading drivers/gpu/drm/nouveau/nouveau_dp.c +16 −217 Original line number Diff line number Diff line Loading @@ -161,116 +161,6 @@ auxch_tx(struct drm_device *dev, int ch, u8 type, u32 addr, u8 *data, u8 size) return ret; } static u32 dp_link_bw_get(struct drm_device *dev, int or, int link) { u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800)); if (!(ctrl & 0x000c0000)) return 162000; return 270000; } static int dp_lane_count_get(struct drm_device *dev, int or, int link) { u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); switch (ctrl & 0x000f0000) { case 0x00010000: return 1; case 0x00030000: return 2; default: return 4; } } void nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) { const u32 symbol = 100000; int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; int TU, VTUi, VTUf, VTUa; u64 link_data_rate, link_ratio, unk; u32 best_diff = 64 * symbol; u32 link_nr, link_bw, r; /* calculate packed data rate for each lane */ link_nr = dp_lane_count_get(dev, or, link); link_data_rate = (clk * bpp / 8) / link_nr; /* calculate ratio of packed data rate to link symbol rate */ link_bw = dp_link_bw_get(dev, or, link); link_ratio = link_data_rate * symbol; r = do_div(link_ratio, link_bw); for (TU = 64; TU >= 32; TU--) { /* calculate average number of valid symbols in each TU */ u32 tu_valid = link_ratio * TU; u32 calc, diff; /* find a hw representation for the fraction.. */ VTUi = tu_valid / symbol; calc = VTUi * symbol; diff = tu_valid - calc; if (diff) { if (diff >= (symbol / 2)) { VTUf = symbol / (symbol - diff); if (symbol - (VTUf * diff)) VTUf++; if (VTUf <= 15) { VTUa = 1; calc += symbol - (symbol / VTUf); } else { VTUa = 0; VTUf = 1; calc += symbol; } } else { VTUa = 0; VTUf = min((int)(symbol / diff), 15); calc += symbol / VTUf; } diff = calc - tu_valid; } else { /* no remainder, but the hw doesn't like the fractional * part to be zero. decrement the integer part and * have the fraction add a whole symbol back */ VTUa = 0; VTUf = 1; VTUi--; } if (diff < best_diff) { best_diff = diff; bestTU = TU; bestVTUa = VTUa; bestVTUf = VTUf; bestVTUi = VTUi; if (diff == 0) break; } } if (!bestTU) { NV_ERROR(dev, "DP: unable to find suitable config\n"); return; } /* XXX close to vbios numbers, but not right */ unk = (symbol - link_ratio) * bestTU; unk *= link_ratio; r = do_div(unk, symbol); r = do_div(unk, symbol); unk += 6; nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | bestVTUf << 16 | bestVTUi << 8 | unk); } u8 * nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) { Loading Loading @@ -318,13 +208,10 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) * link training *****************************************************************************/ struct dp_state { struct dp_train_func *func; struct dcb_entry *dcb; u8 *table; u8 *entry; int auxch; int crtc; int or; int link; u8 *dpcd; int link_nr; u32 link_bw; Loading @@ -335,100 +222,48 @@ struct dp_state { static void dp_set_link_config(struct drm_device *dev, struct dp_state *dp) { int or = dp->or, link = dp->link; u8 *entry, sink[2]; u32 dp_ctrl; u16 script; u8 sink[2]; NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); /* set selected link rate on source */ switch (dp->link_bw) { case 270000: nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000); sink[0] = DP_LINK_BW_2_7; break; default: nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000); sink[0] = DP_LINK_BW_1_62; break; } /* offset +0x0a of each dp encoder table entry is a pointer to another * table, that has (among other things) pointers to more scripts that * need to be executed, this time depending on link speed. */ entry = ROMPTR(dev, dp->entry[10]); if (entry) { if (dp->table[0] < 0x30) { while (dp->link_bw < (ROM16(entry[0]) * 10)) entry += 4; script = ROM16(entry[2]); } else { while (dp->link_bw < (entry[0] * 27000)) entry += 3; script = ROM16(entry[1]); } nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); } /* set desired link configuration on the source */ dp->func->link_set(dev, dp->dcb, dp->crtc, dp->link_nr, dp->link_bw, dp->dpcd[2] & DP_ENHANCED_FRAME_CAP); /* configure lane count on the source */ dp_ctrl = ((1 << dp->link_nr) - 1) << 16; /* inform the sink of the new configuration */ sink[0] = dp->link_bw / 27000; sink[1] = dp->link_nr; if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) { dp_ctrl |= 0x00004000; if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; } nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); /* inform the sink of the new configuration */ auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); } static void dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern) { u8 sink_tp; NV_DEBUG_KMS(dev, "training pattern %d\n", tp); NV_DEBUG_KMS(dev, "training pattern %d\n", pattern); nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); dp->func->train_set(dev, dp->dcb, pattern); auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); sink_tp &= ~DP_TRAINING_PATTERN_MASK; sink_tp |= tp; sink_tp |= pattern; auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); } static const u8 nv50_lane_map[] = { 16, 8, 0, 24 }; static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 }; static int dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) { struct drm_nouveau_private *dev_priv = dev->dev_private; u32 mask = 0, drv = 0, pre = 0, unk = 0; const u8 *shifts; int link = dp->link; int or = dp->or; int i; if (dev_priv->chipset != 0xaf) shifts = nv50_lane_map; else shifts = nvaf_lane_map; for (i = 0; i < dp->link_nr; i++) { u8 *conf = dp->entry + dp->table[4]; u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; u8 lpre = (lane & 0x0c) >> 2; u8 lvsw = (lane & 0x03) >> 0; mask |= 0xff << shifts[i]; unk |= 1 << (shifts[i] >> 3); dp->conf[i] = (lpre << 3) | lvsw; if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; Loading @@ -436,41 +271,9 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); if (dp->table[0] < 0x30) { u8 *last = conf + (dp->entry[4] * dp->table[5]); while (lvsw != conf[0] || lpre != conf[1]) { conf += dp->table[5]; if (conf >= last) return -EINVAL; } conf += 2; } else { /* no lookup table anymore, set entries for each * combination of voltage swing and pre-emphasis * level allowed by the DP spec. */ switch (lvsw) { case 0: lpre += 0; break; case 1: lpre += 4; break; case 2: lpre += 7; break; case 3: lpre += 9; break; dp->func->train_adj(dev, dp->dcb, i, lvsw, lpre); } conf = conf + (lpre * dp->table[5]); conf++; } drv |= conf[0] << shifts[i]; pre |= conf[1] << shifts[i]; unk = (unk & ~0x0000ff00) | (conf[2] << 8); } nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv); nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre); nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk); return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); } Loading Loading @@ -598,7 +401,8 @@ dp_link_train_fini(struct drm_device *dev, struct dp_state *dp) } bool nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, struct dp_train_func *func) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); Loading @@ -614,15 +418,10 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) if (!auxch) return false; dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry); if (!dp.table) return -EINVAL; dp.func = func; dp.dcb = nv_encoder->dcb; dp.crtc = nv_crtc->index; dp.auxch = auxch->drive; dp.or = nv_encoder->or; dp.link = !(nv_encoder->dcb->sorconf.link & 1); dp.dpcd = nv_encoder->dp.dpcd; /* some sinks toggle hotplug in response to some of the actions Loading drivers/gpu/drm/nouveau/nouveau_drv.h +0 −8 Original line number Diff line number Diff line Loading @@ -1162,14 +1162,6 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); /* nouveau_hdmi.c */ void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); /* nouveau_dp.c */ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr); bool nouveau_dp_detect(struct drm_encoder *); bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate); void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); /* nv04_fb.c */ extern int nv04_fb_vram_init(struct drm_device *); extern int nv04_fb_init(struct drm_device *); Loading drivers/gpu/drm/nouveau/nouveau_encoder.h +18 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,14 @@ #define NV_DPMS_CLEARED 0x80 struct dp_train_func { void (*link_set)(struct drm_device *, struct dcb_entry *, int crtc, int nr, u32 bw, bool enhframe); void (*train_set)(struct drm_device *, struct dcb_entry *, u8 pattern); void (*train_adj)(struct drm_device *, struct dcb_entry *, u8 lane, u8 swing, u8 preem); }; struct nouveau_encoder { struct drm_encoder_slave base; Loading Loading @@ -78,9 +86,19 @@ get_slave_funcs(struct drm_encoder *enc) return to_encoder_slave(enc)->slave_funcs; } /* nouveau_dp.c */ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr); bool nouveau_dp_detect(struct drm_encoder *); bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate, struct dp_train_func *); u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); struct nouveau_connector * nouveau_encoder_connector_get(struct nouveau_encoder *encoder); int nv50_sor_create(struct drm_connector *, struct dcb_entry *); void nv50_sor_dp_calc_tu(struct drm_device *, int, int, u32, u32); int nv50_dac_create(struct drm_connector *, struct dcb_entry *); #endif /* __NOUVEAU_ENCODER_H__ */ drivers/gpu/drm/nouveau/nv50_display.c +2 −2 Original line number Diff line number Diff line Loading @@ -863,9 +863,9 @@ nv50_display_unk20_handler(struct drm_device *dev) if (type == OUTPUT_DP) { int link = !(dcb->dpconf.sor.link & 1); if ((mc & 0x000f0000) == 0x00020000) nouveau_dp_tu_update(dev, or, link, pclk, 18); nv50_sor_dp_calc_tu(dev, or, link, pclk, 18); else nouveau_dp_tu_update(dev, or, link, pclk, 24); nv50_sor_dp_calc_tu(dev, or, link, pclk, 24); } if (dcb->type != OUTPUT_ANALOG) { Loading drivers/gpu/drm/nouveau/nv50_sor.c +195 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,193 @@ #include "nouveau_crtc.h" #include "nv50_display.h" static u32 nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane) { struct drm_nouveau_private *dev_priv = dev->dev_private; static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ static const u8 nv50[] = { 16, 8, 0, 24 }; if (dev_priv->card_type == 0xaf) return nvaf[lane]; return nv50[lane]; } static void nv50_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern) { u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x0f000000, pattern << 24); } static void nv50_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb, u8 lane, u8 swing, u8 preem) { u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); u32 shift = nv50_sor_dp_lane_map(dev, dcb, lane); u32 mask = 0x000000ff << shift; u8 *table, *entry, *config; table = nouveau_dp_bios_data(dev, dcb, &entry); if (!table || (table[0] != 0x20 && table[0] != 0x21)) { NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); return; } config = entry + table[4]; while (config[0] != swing || config[1] != preem) { config += table[5]; if (config >= entry + table[4] + entry[4] * table[5]) return; } nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, config[2] << shift); nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, config[3] << shift); nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff00, config[4] << 8); } static void nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc, int link_nr, u32 link_bw, bool enhframe) { u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & ~0x001f4000; u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)) & ~0x000c0000; u8 *table, *entry, mask; int i; table = nouveau_dp_bios_data(dev, dcb, &entry); if (!table || (table[0] != 0x20 && table[0] != 0x21)) { NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); return; } entry = ROMPTR(dev, entry[10]); if (entry) { while (link_bw < ROM16(entry[0]) * 10) entry += 4; nouveau_bios_run_init_table(dev, ROM16(entry[2]), dcb, crtc); } dpctrl |= ((1 << link_nr) - 1) << 16; if (enhframe) dpctrl |= 0x00004000; if (link_bw > 162000) clksor |= 0x00040000; nv_wr32(dev, 0x614300 + (or * 0x800), clksor); nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), dpctrl); mask = 0; for (i = 0; i < link_nr; i++) mask |= 1 << (nv50_sor_dp_lane_map(dev, dcb, i) >> 3); nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000000f, mask); } static void nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw) { u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000; u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)); if (clksor & 0x000c0000) *bw = 270000; else *bw = 162000; if (dpctrl > 0x00030000) *nr = 4; else if (dpctrl > 0x00010000) *nr = 2; else *nr = 1; } void nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) { const u32 symbol = 100000; int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; int TU, VTUi, VTUf, VTUa; u64 link_data_rate, link_ratio, unk; u32 best_diff = 64 * symbol; u32 link_nr, link_bw, r; /* calculate packed data rate for each lane */ nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw); link_data_rate = (clk * bpp / 8) / link_nr; /* calculate ratio of packed data rate to link symbol rate */ link_ratio = link_data_rate * symbol; r = do_div(link_ratio, link_bw); for (TU = 64; TU >= 32; TU--) { /* calculate average number of valid symbols in each TU */ u32 tu_valid = link_ratio * TU; u32 calc, diff; /* find a hw representation for the fraction.. */ VTUi = tu_valid / symbol; calc = VTUi * symbol; diff = tu_valid - calc; if (diff) { if (diff >= (symbol / 2)) { VTUf = symbol / (symbol - diff); if (symbol - (VTUf * diff)) VTUf++; if (VTUf <= 15) { VTUa = 1; calc += symbol - (symbol / VTUf); } else { VTUa = 0; VTUf = 1; calc += symbol; } } else { VTUa = 0; VTUf = min((int)(symbol / diff), 15); calc += symbol / VTUf; } diff = calc - tu_valid; } else { /* no remainder, but the hw doesn't like the fractional * part to be zero. decrement the integer part and * have the fraction add a whole symbol back */ VTUa = 0; VTUf = 1; VTUi--; } if (diff < best_diff) { best_diff = diff; bestTU = TU; bestVTUa = VTUa; bestVTUf = VTUf; bestVTUi = VTUi; if (diff == 0) break; } } if (!bestTU) { NV_ERROR(dev, "DP: unable to find suitable config\n"); return; } /* XXX close to vbios numbers, but not right */ unk = (symbol - link_ratio) * bestTU; unk *= link_ratio; r = do_div(unk, symbol); r = do_div(unk, symbol); unk += 6; nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | bestVTUf << 16 | bestVTUi << 8 | unk); } static void nv50_sor_disconnect(struct drm_encoder *encoder) { Loading Loading @@ -124,9 +311,16 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) return; if (mode == DRM_MODE_DPMS_ON) { struct dp_train_func func = { .link_set = nv50_sor_dp_link_set, .train_set = nv50_sor_dp_train_set, .train_adj = nv50_sor_dp_train_adj }; u32 rate = nv_encoder->dp.datarate; u8 status = DP_SET_POWER_D0; nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); nouveau_dp_link_train(encoder, nv_encoder->dp.datarate); nouveau_dp_link_train(encoder, rate, &func); } else { u8 status = DP_SET_POWER_D3; nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); Loading Loading
drivers/gpu/drm/nouveau/nouveau_dp.c +16 −217 Original line number Diff line number Diff line Loading @@ -161,116 +161,6 @@ auxch_tx(struct drm_device *dev, int ch, u8 type, u32 addr, u8 *data, u8 size) return ret; } static u32 dp_link_bw_get(struct drm_device *dev, int or, int link) { u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800)); if (!(ctrl & 0x000c0000)) return 162000; return 270000; } static int dp_lane_count_get(struct drm_device *dev, int or, int link) { u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); switch (ctrl & 0x000f0000) { case 0x00010000: return 1; case 0x00030000: return 2; default: return 4; } } void nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) { const u32 symbol = 100000; int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; int TU, VTUi, VTUf, VTUa; u64 link_data_rate, link_ratio, unk; u32 best_diff = 64 * symbol; u32 link_nr, link_bw, r; /* calculate packed data rate for each lane */ link_nr = dp_lane_count_get(dev, or, link); link_data_rate = (clk * bpp / 8) / link_nr; /* calculate ratio of packed data rate to link symbol rate */ link_bw = dp_link_bw_get(dev, or, link); link_ratio = link_data_rate * symbol; r = do_div(link_ratio, link_bw); for (TU = 64; TU >= 32; TU--) { /* calculate average number of valid symbols in each TU */ u32 tu_valid = link_ratio * TU; u32 calc, diff; /* find a hw representation for the fraction.. */ VTUi = tu_valid / symbol; calc = VTUi * symbol; diff = tu_valid - calc; if (diff) { if (diff >= (symbol / 2)) { VTUf = symbol / (symbol - diff); if (symbol - (VTUf * diff)) VTUf++; if (VTUf <= 15) { VTUa = 1; calc += symbol - (symbol / VTUf); } else { VTUa = 0; VTUf = 1; calc += symbol; } } else { VTUa = 0; VTUf = min((int)(symbol / diff), 15); calc += symbol / VTUf; } diff = calc - tu_valid; } else { /* no remainder, but the hw doesn't like the fractional * part to be zero. decrement the integer part and * have the fraction add a whole symbol back */ VTUa = 0; VTUf = 1; VTUi--; } if (diff < best_diff) { best_diff = diff; bestTU = TU; bestVTUa = VTUa; bestVTUf = VTUf; bestVTUi = VTUi; if (diff == 0) break; } } if (!bestTU) { NV_ERROR(dev, "DP: unable to find suitable config\n"); return; } /* XXX close to vbios numbers, but not right */ unk = (symbol - link_ratio) * bestTU; unk *= link_ratio; r = do_div(unk, symbol); r = do_div(unk, symbol); unk += 6; nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | bestVTUf << 16 | bestVTUi << 8 | unk); } u8 * nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) { Loading Loading @@ -318,13 +208,10 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) * link training *****************************************************************************/ struct dp_state { struct dp_train_func *func; struct dcb_entry *dcb; u8 *table; u8 *entry; int auxch; int crtc; int or; int link; u8 *dpcd; int link_nr; u32 link_bw; Loading @@ -335,100 +222,48 @@ struct dp_state { static void dp_set_link_config(struct drm_device *dev, struct dp_state *dp) { int or = dp->or, link = dp->link; u8 *entry, sink[2]; u32 dp_ctrl; u16 script; u8 sink[2]; NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); /* set selected link rate on source */ switch (dp->link_bw) { case 270000: nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000); sink[0] = DP_LINK_BW_2_7; break; default: nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000); sink[0] = DP_LINK_BW_1_62; break; } /* offset +0x0a of each dp encoder table entry is a pointer to another * table, that has (among other things) pointers to more scripts that * need to be executed, this time depending on link speed. */ entry = ROMPTR(dev, dp->entry[10]); if (entry) { if (dp->table[0] < 0x30) { while (dp->link_bw < (ROM16(entry[0]) * 10)) entry += 4; script = ROM16(entry[2]); } else { while (dp->link_bw < (entry[0] * 27000)) entry += 3; script = ROM16(entry[1]); } nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); } /* set desired link configuration on the source */ dp->func->link_set(dev, dp->dcb, dp->crtc, dp->link_nr, dp->link_bw, dp->dpcd[2] & DP_ENHANCED_FRAME_CAP); /* configure lane count on the source */ dp_ctrl = ((1 << dp->link_nr) - 1) << 16; /* inform the sink of the new configuration */ sink[0] = dp->link_bw / 27000; sink[1] = dp->link_nr; if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) { dp_ctrl |= 0x00004000; if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; } nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); /* inform the sink of the new configuration */ auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); } static void dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern) { u8 sink_tp; NV_DEBUG_KMS(dev, "training pattern %d\n", tp); NV_DEBUG_KMS(dev, "training pattern %d\n", pattern); nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); dp->func->train_set(dev, dp->dcb, pattern); auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); sink_tp &= ~DP_TRAINING_PATTERN_MASK; sink_tp |= tp; sink_tp |= pattern; auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); } static const u8 nv50_lane_map[] = { 16, 8, 0, 24 }; static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 }; static int dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) { struct drm_nouveau_private *dev_priv = dev->dev_private; u32 mask = 0, drv = 0, pre = 0, unk = 0; const u8 *shifts; int link = dp->link; int or = dp->or; int i; if (dev_priv->chipset != 0xaf) shifts = nv50_lane_map; else shifts = nvaf_lane_map; for (i = 0; i < dp->link_nr; i++) { u8 *conf = dp->entry + dp->table[4]; u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; u8 lpre = (lane & 0x0c) >> 2; u8 lvsw = (lane & 0x03) >> 0; mask |= 0xff << shifts[i]; unk |= 1 << (shifts[i] >> 3); dp->conf[i] = (lpre << 3) | lvsw; if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; Loading @@ -436,41 +271,9 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); if (dp->table[0] < 0x30) { u8 *last = conf + (dp->entry[4] * dp->table[5]); while (lvsw != conf[0] || lpre != conf[1]) { conf += dp->table[5]; if (conf >= last) return -EINVAL; } conf += 2; } else { /* no lookup table anymore, set entries for each * combination of voltage swing and pre-emphasis * level allowed by the DP spec. */ switch (lvsw) { case 0: lpre += 0; break; case 1: lpre += 4; break; case 2: lpre += 7; break; case 3: lpre += 9; break; dp->func->train_adj(dev, dp->dcb, i, lvsw, lpre); } conf = conf + (lpre * dp->table[5]); conf++; } drv |= conf[0] << shifts[i]; pre |= conf[1] << shifts[i]; unk = (unk & ~0x0000ff00) | (conf[2] << 8); } nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv); nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre); nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk); return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); } Loading Loading @@ -598,7 +401,8 @@ dp_link_train_fini(struct drm_device *dev, struct dp_state *dp) } bool nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, struct dp_train_func *func) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); Loading @@ -614,15 +418,10 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) if (!auxch) return false; dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry); if (!dp.table) return -EINVAL; dp.func = func; dp.dcb = nv_encoder->dcb; dp.crtc = nv_crtc->index; dp.auxch = auxch->drive; dp.or = nv_encoder->or; dp.link = !(nv_encoder->dcb->sorconf.link & 1); dp.dpcd = nv_encoder->dp.dpcd; /* some sinks toggle hotplug in response to some of the actions Loading
drivers/gpu/drm/nouveau/nouveau_drv.h +0 −8 Original line number Diff line number Diff line Loading @@ -1162,14 +1162,6 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); /* nouveau_hdmi.c */ void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); /* nouveau_dp.c */ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr); bool nouveau_dp_detect(struct drm_encoder *); bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate); void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); /* nv04_fb.c */ extern int nv04_fb_vram_init(struct drm_device *); extern int nv04_fb_init(struct drm_device *); Loading
drivers/gpu/drm/nouveau/nouveau_encoder.h +18 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,14 @@ #define NV_DPMS_CLEARED 0x80 struct dp_train_func { void (*link_set)(struct drm_device *, struct dcb_entry *, int crtc, int nr, u32 bw, bool enhframe); void (*train_set)(struct drm_device *, struct dcb_entry *, u8 pattern); void (*train_adj)(struct drm_device *, struct dcb_entry *, u8 lane, u8 swing, u8 preem); }; struct nouveau_encoder { struct drm_encoder_slave base; Loading Loading @@ -78,9 +86,19 @@ get_slave_funcs(struct drm_encoder *enc) return to_encoder_slave(enc)->slave_funcs; } /* nouveau_dp.c */ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr); bool nouveau_dp_detect(struct drm_encoder *); bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate, struct dp_train_func *); u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); struct nouveau_connector * nouveau_encoder_connector_get(struct nouveau_encoder *encoder); int nv50_sor_create(struct drm_connector *, struct dcb_entry *); void nv50_sor_dp_calc_tu(struct drm_device *, int, int, u32, u32); int nv50_dac_create(struct drm_connector *, struct dcb_entry *); #endif /* __NOUVEAU_ENCODER_H__ */
drivers/gpu/drm/nouveau/nv50_display.c +2 −2 Original line number Diff line number Diff line Loading @@ -863,9 +863,9 @@ nv50_display_unk20_handler(struct drm_device *dev) if (type == OUTPUT_DP) { int link = !(dcb->dpconf.sor.link & 1); if ((mc & 0x000f0000) == 0x00020000) nouveau_dp_tu_update(dev, or, link, pclk, 18); nv50_sor_dp_calc_tu(dev, or, link, pclk, 18); else nouveau_dp_tu_update(dev, or, link, pclk, 24); nv50_sor_dp_calc_tu(dev, or, link, pclk, 24); } if (dcb->type != OUTPUT_ANALOG) { Loading
drivers/gpu/drm/nouveau/nv50_sor.c +195 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,193 @@ #include "nouveau_crtc.h" #include "nv50_display.h" static u32 nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane) { struct drm_nouveau_private *dev_priv = dev->dev_private; static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ static const u8 nv50[] = { 16, 8, 0, 24 }; if (dev_priv->card_type == 0xaf) return nvaf[lane]; return nv50[lane]; } static void nv50_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern) { u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x0f000000, pattern << 24); } static void nv50_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb, u8 lane, u8 swing, u8 preem) { u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); u32 shift = nv50_sor_dp_lane_map(dev, dcb, lane); u32 mask = 0x000000ff << shift; u8 *table, *entry, *config; table = nouveau_dp_bios_data(dev, dcb, &entry); if (!table || (table[0] != 0x20 && table[0] != 0x21)) { NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); return; } config = entry + table[4]; while (config[0] != swing || config[1] != preem) { config += table[5]; if (config >= entry + table[4] + entry[4] * table[5]) return; } nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, config[2] << shift); nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, config[3] << shift); nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff00, config[4] << 8); } static void nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc, int link_nr, u32 link_bw, bool enhframe) { u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & ~0x001f4000; u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)) & ~0x000c0000; u8 *table, *entry, mask; int i; table = nouveau_dp_bios_data(dev, dcb, &entry); if (!table || (table[0] != 0x20 && table[0] != 0x21)) { NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); return; } entry = ROMPTR(dev, entry[10]); if (entry) { while (link_bw < ROM16(entry[0]) * 10) entry += 4; nouveau_bios_run_init_table(dev, ROM16(entry[2]), dcb, crtc); } dpctrl |= ((1 << link_nr) - 1) << 16; if (enhframe) dpctrl |= 0x00004000; if (link_bw > 162000) clksor |= 0x00040000; nv_wr32(dev, 0x614300 + (or * 0x800), clksor); nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), dpctrl); mask = 0; for (i = 0; i < link_nr; i++) mask |= 1 << (nv50_sor_dp_lane_map(dev, dcb, i) >> 3); nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000000f, mask); } static void nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw) { u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000; u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)); if (clksor & 0x000c0000) *bw = 270000; else *bw = 162000; if (dpctrl > 0x00030000) *nr = 4; else if (dpctrl > 0x00010000) *nr = 2; else *nr = 1; } void nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) { const u32 symbol = 100000; int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; int TU, VTUi, VTUf, VTUa; u64 link_data_rate, link_ratio, unk; u32 best_diff = 64 * symbol; u32 link_nr, link_bw, r; /* calculate packed data rate for each lane */ nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw); link_data_rate = (clk * bpp / 8) / link_nr; /* calculate ratio of packed data rate to link symbol rate */ link_ratio = link_data_rate * symbol; r = do_div(link_ratio, link_bw); for (TU = 64; TU >= 32; TU--) { /* calculate average number of valid symbols in each TU */ u32 tu_valid = link_ratio * TU; u32 calc, diff; /* find a hw representation for the fraction.. */ VTUi = tu_valid / symbol; calc = VTUi * symbol; diff = tu_valid - calc; if (diff) { if (diff >= (symbol / 2)) { VTUf = symbol / (symbol - diff); if (symbol - (VTUf * diff)) VTUf++; if (VTUf <= 15) { VTUa = 1; calc += symbol - (symbol / VTUf); } else { VTUa = 0; VTUf = 1; calc += symbol; } } else { VTUa = 0; VTUf = min((int)(symbol / diff), 15); calc += symbol / VTUf; } diff = calc - tu_valid; } else { /* no remainder, but the hw doesn't like the fractional * part to be zero. decrement the integer part and * have the fraction add a whole symbol back */ VTUa = 0; VTUf = 1; VTUi--; } if (diff < best_diff) { best_diff = diff; bestTU = TU; bestVTUa = VTUa; bestVTUf = VTUf; bestVTUi = VTUi; if (diff == 0) break; } } if (!bestTU) { NV_ERROR(dev, "DP: unable to find suitable config\n"); return; } /* XXX close to vbios numbers, but not right */ unk = (symbol - link_ratio) * bestTU; unk *= link_ratio; r = do_div(unk, symbol); r = do_div(unk, symbol); unk += 6; nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | bestVTUf << 16 | bestVTUi << 8 | unk); } static void nv50_sor_disconnect(struct drm_encoder *encoder) { Loading Loading @@ -124,9 +311,16 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) return; if (mode == DRM_MODE_DPMS_ON) { struct dp_train_func func = { .link_set = nv50_sor_dp_link_set, .train_set = nv50_sor_dp_train_set, .train_adj = nv50_sor_dp_train_adj }; u32 rate = nv_encoder->dp.datarate; u8 status = DP_SET_POWER_D0; nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); nouveau_dp_link_train(encoder, nv_encoder->dp.datarate); nouveau_dp_link_train(encoder, rate, &func); } else { u8 status = DP_SET_POWER_D3; nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); Loading