Loading drivers/gpu/drm/nouveau/nouveau_dp.c +110 −1 Original line number Diff line number Diff line Loading @@ -194,6 +194,116 @@ auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int 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); } static int nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) { Loading Loading @@ -617,7 +727,6 @@ static int nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap; struct drm_device *dev = auxch->dev; struct i2c_msg *msg = msgs; int ret, mcnt = num; Loading drivers/gpu/drm/nouveau/nouveau_drv.h +1 −0 Original line number Diff line number Diff line Loading @@ -1101,6 +1101,7 @@ 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 *); void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); /* nv04_fb.c */ extern int nv04_fb_init(struct drm_device *); Loading drivers/gpu/drm/nouveau/nouveau_encoder.h +0 −3 Original line number Diff line number Diff line Loading @@ -49,9 +49,6 @@ struct nouveau_encoder { union { struct { int mc_unknown; uint32_t unk0; uint32_t unk1; int dpcd_version; int link_nr; int link_bw; Loading drivers/gpu/drm/nouveau/nouveau_reg.h +1 −1 Original line number Diff line number Diff line Loading @@ -843,7 +843,7 @@ #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 #define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) Loading drivers/gpu/drm/nouveau/nv50_display.c +7 −32 Original line number Diff line number Diff line Loading @@ -701,37 +701,6 @@ nv50_display_unk10_handler(struct drm_device *dev) nv_wr32(dev, 0x610030, 0x80000000); } static void nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb) { int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1); struct drm_encoder *encoder; uint32_t tmp, unk0 = 0, unk1 = 0; if (dcb->type != OUTPUT_DP) return; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); if (nv_encoder->dcb == dcb) { unk0 = nv_encoder->dp.unk0; unk1 = nv_encoder->dp.unk1; break; } } if (unk0 || unk1) { tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); tmp &= 0xfffffe03; nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0); tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link)); tmp &= 0xfef080c0; nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1); } } static void nv50_display_unk20_handler(struct drm_device *dev) { Loading Loading @@ -830,7 +799,13 @@ nv50_display_unk20_handler(struct drm_device *dev) script = nv50_display_script_select(dev, dcb, mc, pclk); nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); nv50_display_unk20_dp_hack(dev, dcb); if (type == OUTPUT_DP) { int link = !(dcb->dpconf.sor.link & 1); if ((mc & 0x000f0000) == 0x00020000) nouveau_dp_tu_update(dev, or, link, pclk, 18); else nouveau_dp_tu_update(dev, or, link, pclk, 24); } if (dcb->type != OUTPUT_ANALOG) { tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); Loading Loading
drivers/gpu/drm/nouveau/nouveau_dp.c +110 −1 Original line number Diff line number Diff line Loading @@ -194,6 +194,116 @@ auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int 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); } static int nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) { Loading Loading @@ -617,7 +727,6 @@ static int nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap; struct drm_device *dev = auxch->dev; struct i2c_msg *msg = msgs; int ret, mcnt = num; Loading
drivers/gpu/drm/nouveau/nouveau_drv.h +1 −0 Original line number Diff line number Diff line Loading @@ -1101,6 +1101,7 @@ 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 *); void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); /* nv04_fb.c */ extern int nv04_fb_init(struct drm_device *); Loading
drivers/gpu/drm/nouveau/nouveau_encoder.h +0 −3 Original line number Diff line number Diff line Loading @@ -49,9 +49,6 @@ struct nouveau_encoder { union { struct { int mc_unknown; uint32_t unk0; uint32_t unk1; int dpcd_version; int link_nr; int link_bw; Loading
drivers/gpu/drm/nouveau/nouveau_reg.h +1 −1 Original line number Diff line number Diff line Loading @@ -843,7 +843,7 @@ #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 #define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) Loading
drivers/gpu/drm/nouveau/nv50_display.c +7 −32 Original line number Diff line number Diff line Loading @@ -701,37 +701,6 @@ nv50_display_unk10_handler(struct drm_device *dev) nv_wr32(dev, 0x610030, 0x80000000); } static void nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb) { int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1); struct drm_encoder *encoder; uint32_t tmp, unk0 = 0, unk1 = 0; if (dcb->type != OUTPUT_DP) return; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); if (nv_encoder->dcb == dcb) { unk0 = nv_encoder->dp.unk0; unk1 = nv_encoder->dp.unk1; break; } } if (unk0 || unk1) { tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); tmp &= 0xfffffe03; nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0); tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link)); tmp &= 0xfef080c0; nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1); } } static void nv50_display_unk20_handler(struct drm_device *dev) { Loading Loading @@ -830,7 +799,13 @@ nv50_display_unk20_handler(struct drm_device *dev) script = nv50_display_script_select(dev, dcb, mc, pclk); nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); nv50_display_unk20_dp_hack(dev, dcb); if (type == OUTPUT_DP) { int link = !(dcb->dpconf.sor.link & 1); if ((mc & 0x000f0000) == 0x00020000) nouveau_dp_tu_update(dev, or, link, pclk, 18); else nouveau_dp_tu_update(dev, or, link, pclk, 24); } if (dcb->type != OUTPUT_ANALOG) { tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); Loading