diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau')
35 files changed, 2706 insertions, 1372 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 9f27e3d9e69a..1a2ad7eb1734 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -14,7 +14,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nouveau_mm.o nouveau_vm.o nouveau_mxm.o nouveau_gpio.o \ nv04_timer.o \ nv04_mc.o nv40_mc.o nv50_mc.o \ - nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \ + nv04_fb.o nv10_fb.o nv20_fb.o nv30_fb.o nv40_fb.o \ + nv50_fb.o nvc0_fb.o \ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o nvc0_fifo.o \ nv04_graph.o nv10_graph.o nv20_graph.o \ nv40_graph.o nv50_graph.o nvc0_graph.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index e5cbead85e50..8dbeeea91872 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -65,195 +65,232 @@ static bool nv_cksum(const uint8_t *data, unsigned int length) } static int -score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable) +score_vbios(struct nvbios *bios, const bool writeable) { - if (!(data[0] == 0x55 && data[1] == 0xAA)) { - NV_TRACEWARN(dev, "... BIOS signature not found\n"); + if (!bios->data || bios->data[0] != 0x55 || bios->data[1] != 0xAA) { + NV_TRACEWARN(bios->dev, "... BIOS signature not found\n"); return 0; } - if (nv_cksum(data, data[2] * 512)) { - NV_TRACEWARN(dev, "... BIOS checksum invalid\n"); + if (nv_cksum(bios->data, bios->data[2] * 512)) { + NV_TRACEWARN(bios->dev, "... BIOS checksum invalid\n"); /* if a ro image is somewhat bad, it's probably all rubbish */ return writeable ? 2 : 1; - } else - NV_TRACE(dev, "... appears to be valid\n"); + } + NV_TRACE(bios->dev, "... appears to be valid\n"); return 3; } -static void load_vbios_prom(struct drm_device *dev, uint8_t *data) +static void +bios_shadow_prom(struct nvbios *bios) { + struct drm_device *dev = bios->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t pci_nv_20, save_pci_nv_20; - int pcir_ptr; + u32 pcireg, access; + u16 pcir; int i; + /* enable access to rom */ if (dev_priv->card_type >= NV_50) - pci_nv_20 = 0x88050; + pcireg = 0x088050; else - pci_nv_20 = NV_PBUS_PCI_NV_20; + pcireg = NV_PBUS_PCI_NV_20; + access = nv_mask(dev, pcireg, 0x00000001, 0x00000000); - /* enable ROM access */ - save_pci_nv_20 = nvReadMC(dev, pci_nv_20); - nvWriteMC(dev, pci_nv_20, - save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); + /* bail if no rom signature, with a workaround for a PROM reading + * issue on some chipsets. the first read after a period of + * inactivity returns the wrong result, so retry the first header + * byte a few times before giving up as a workaround + */ + i = 16; + do { + if (nv_rd08(dev, NV_PROM_OFFSET + 0) == 0x55) + break; + } while (i--); - /* bail if no rom signature */ - if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 || - nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa) + if (!i || nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa) goto out; /* additional check (see note below) - read PCI record header */ - pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) | - nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8; - if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' || - nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' || - nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' || - nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R') + pcir = nv_rd08(dev, NV_PROM_OFFSET + 0x18) | + nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8; + if (nv_rd08(dev, NV_PROM_OFFSET + pcir + 0) != 'P' || + nv_rd08(dev, NV_PROM_OFFSET + pcir + 1) != 'C' || + nv_rd08(dev, NV_PROM_OFFSET + pcir + 2) != 'I' || + nv_rd08(dev, NV_PROM_OFFSET + pcir + 3) != 'R') goto out; - /* on some 6600GT/6800LE prom reads are messed up. nvclock alleges a - * a good read may be obtained by waiting or re-reading (cargocult: 5x) - * each byte. we'll hope pramin has something usable instead - */ - for (i = 0; i < NV_PROM_SIZE; i++) - data[i] = nv_rd08(dev, NV_PROM_OFFSET + i); + /* read entire bios image to system memory */ + bios->length = nv_rd08(dev, NV_PROM_OFFSET + 2) * 512; + bios->data = kmalloc(bios->length, GFP_KERNEL); + if (bios->data) { + for (i = 0; i < bios->length; i++) + bios->data[i] = nv_rd08(dev, NV_PROM_OFFSET + i); + } out: - /* disable ROM access */ - nvWriteMC(dev, pci_nv_20, - save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); + /* disable access to rom */ + nv_wr32(dev, pcireg, access); } -static void load_vbios_pramin(struct drm_device *dev, uint8_t *data) +static void +bios_shadow_pramin(struct nvbios *bios) { + struct drm_device *dev = bios->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t old_bar0_pramin = 0; + u32 bar0 = 0; int i; if (dev_priv->card_type >= NV_50) { u64 addr = (u64)(nv_rd32(dev, 0x619f04) & 0xffffff00) << 8; if (!addr) { - addr = (u64)nv_rd32(dev, 0x1700) << 16; + addr = (u64)nv_rd32(dev, 0x001700) << 16; addr += 0xf0000; } - old_bar0_pramin = nv_rd32(dev, 0x1700); - nv_wr32(dev, 0x1700, addr >> 16); + bar0 = nv_mask(dev, 0x001700, 0xffffffff, addr >> 16); } /* bail if no rom signature */ - if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 || + if (nv_rd08(dev, NV_PRAMIN_OFFSET + 0) != 0x55 || nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa) goto out; - for (i = 0; i < NV_PROM_SIZE; i++) - data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i); + bios->length = nv_rd08(dev, NV_PRAMIN_OFFSET + 2) * 512; + bios->data = kmalloc(bios->length, GFP_KERNEL); + if (bios->data) { + for (i = 0; i < bios->length; i++) + bios->data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i); + } out: if (dev_priv->card_type >= NV_50) - nv_wr32(dev, 0x1700, old_bar0_pramin); + nv_wr32(dev, 0x001700, bar0); } -static void load_vbios_pci(struct drm_device *dev, uint8_t *data) +static void +bios_shadow_pci(struct nvbios *bios) +{ + struct pci_dev *pdev = bios->dev->pdev; + size_t length; + + if (!pci_enable_rom(pdev)) { + void __iomem *rom = pci_map_rom(pdev, &length); + if (rom) { + bios->data = kmalloc(length, GFP_KERNEL); + if (bios->data) { + memcpy_fromio(bios->data, rom, length); + bios->length = length; + } + pci_unmap_rom(pdev, rom); + } + + pci_disable_rom(pdev); + } +} + +static void +bios_shadow_acpi(struct nvbios *bios) { - void __iomem *rom = NULL; - size_t rom_len; - int ret; + struct pci_dev *pdev = bios->dev->pdev; + int ptr, len, ret; + u8 data[3]; - ret = pci_enable_rom(dev->pdev); - if (ret) + if (!nouveau_acpi_rom_supported(pdev)) return; - rom = pci_map_rom(dev->pdev, &rom_len); - if (!rom) - goto out; - memcpy_fromio(data, rom, rom_len); - pci_unmap_rom(dev->pdev, rom); + ret = nouveau_acpi_get_bios_chunk(data, 0, sizeof(data)); + if (ret != sizeof(data)) + return; -out: - pci_disable_rom(dev->pdev); -} + bios->length = min(data[2] * 512, 65536); + bios->data = kmalloc(bios->length, GFP_KERNEL); + if (!bios->data) + return; -static void load_vbios_acpi(struct drm_device *dev, uint8_t *data) -{ - int i; - int ret; - int size = 64 * 1024; + len = bios->length; + ptr = 0; + while (len) { + int size = (len > ROM_BIOS_PAGE) ? ROM_BIOS_PAGE : len; - if (!nouveau_acpi_rom_supported(dev->pdev)) - return; + ret = nouveau_acpi_get_bios_chunk(bios->data, ptr, size); + if (ret != size) { + kfree(bios->data); + bios->data = NULL; + return; + } - for (i = 0; i < (size / ROM_BIOS_PAGE); i++) { - ret = nouveau_acpi_get_bios_chunk(data, - (i * ROM_BIOS_PAGE), - ROM_BIOS_PAGE); - if (ret <= 0) - break; + len -= size; + ptr += size; } - return; } struct methods { const char desc[8]; - void (*loadbios)(struct drm_device *, uint8_t *); + void (*shadow)(struct nvbios *); const bool rw; + int score; + u32 size; + u8 *data; }; -static struct methods shadow_methods[] = { - { "PRAMIN", load_vbios_pramin, true }, - { "PROM", load_vbios_prom, false }, - { "PCIROM", load_vbios_pci, true }, - { "ACPI", load_vbios_acpi, true }, -}; -#define NUM_SHADOW_METHODS ARRAY_SIZE(shadow_methods) - -static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data) -{ - struct methods *methods = shadow_methods; - int testscore = 3; - int scores[NUM_SHADOW_METHODS], i; +static bool +bios_shadow(struct drm_device *dev) +{ + struct methods shadow_methods[] = { + { "PRAMIN", bios_shadow_pramin, true, 0, 0, NULL }, + { "PROM", bios_shadow_prom, false, 0, 0, NULL }, + { "ACPI", bios_shadow_acpi, true, 0, 0, NULL }, + { "PCIROM", bios_shadow_pci, true, 0, 0, NULL }, + {} + }; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + struct methods *mthd, *best; if (nouveau_vbios) { - for (i = 0; i < NUM_SHADOW_METHODS; i++) - if (!strcasecmp(nouveau_vbios, methods[i].desc)) - break; - - if (i < NUM_SHADOW_METHODS) { - NV_INFO(dev, "Attempting to use BIOS image from %s\n", - methods[i].desc); + mthd = shadow_methods; + do { + if (strcasecmp(nouveau_vbios, mthd->desc)) + continue; + NV_INFO(dev, "VBIOS source: %s\n", mthd->desc); - methods[i].loadbios(dev, data); - if (score_vbios(dev, data, methods[i].rw)) + mthd->shadow(bios); + mthd->score = score_vbios(bios, mthd->rw); + if (mthd->score) return true; - } + } while ((++mthd)->shadow); NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios); } - for (i = 0; i < NUM_SHADOW_METHODS; i++) { - NV_TRACE(dev, "Attempting to load BIOS image from %s\n", - methods[i].desc); - data[0] = data[1] = 0; /* avoid reuse of previous image */ - methods[i].loadbios(dev, data); - scores[i] = score_vbios(dev, data, methods[i].rw); - if (scores[i] == testscore) - return true; - } - - while (--testscore > 0) { - for (i = 0; i < NUM_SHADOW_METHODS; i++) { - if (scores[i] == testscore) { - NV_TRACE(dev, "Using BIOS image from %s\n", - methods[i].desc); - methods[i].loadbios(dev, data); - return true; - } + mthd = shadow_methods; + do { + NV_TRACE(dev, "Checking %s for VBIOS\n", mthd->desc); + mthd->shadow(bios); + mthd->score = score_vbios(bios, mthd->rw); + mthd->size = bios->length; + mthd->data = bios->data; + } while (mthd->score != 3 && (++mthd)->shadow); + + mthd = shadow_methods; + best = mthd; + do { + if (mthd->score > best->score) { + kfree(best->data); + best = mthd; } + } while ((++mthd)->shadow); + + if (best->score) { + NV_TRACE(dev, "Using VBIOS from %s\n", best->desc); + bios->length = best->size; + bios->data = best->data; + return true; } - NV_ERROR(dev, "No valid BIOS image found\n"); + NV_ERROR(dev, "No valid VBIOS image found\n"); return false; } @@ -6334,11 +6371,7 @@ static bool NVInitVBIOS(struct drm_device *dev) spin_lock_init(&bios->lock); bios->dev = dev; - if (!NVShadowVBIOS(dev, bios->data)) - return false; - - bios->length = NV_PROM_SIZE; - return true; + return bios_shadow(dev); } static int nouveau_parse_vbios_struct(struct drm_device *dev) @@ -6498,6 +6531,10 @@ nouveau_bios_init(struct drm_device *dev) void nouveau_bios_takedown(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + nouveau_mxm_fini(dev); nouveau_i2c_fini(dev); + + kfree(dev_priv->vbios.data); } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index a37c31e358aa..1f3233df00e6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -75,6 +75,8 @@ enum dcb_connector_type { DCB_CONNECTOR_eDP = 0x47, DCB_CONNECTOR_HDMI_0 = 0x60, DCB_CONNECTOR_HDMI_1 = 0x61, + DCB_CONNECTOR_DMS59_DP0 = 0x64, + DCB_CONNECTOR_DMS59_DP1 = 0x65, DCB_CONNECTOR_NONE = 0xff }; @@ -209,6 +211,8 @@ struct nvbios { NVBIOS_BIT } type; uint16_t offset; + uint32_t length; + uint8_t *data; uint8_t chip_version; @@ -219,8 +223,6 @@ struct nvbios { spinlock_t lock; - uint8_t data[NV_PROM_SIZE]; - unsigned int length; bool execute; uint8_t major_version; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index f3ce34be082a..9f9d50dbca7f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -519,6 +519,19 @@ nouveau_connector_set_property(struct drm_connector *connector, return nv_crtc->set_dither(nv_crtc, true); } + if (nv_crtc && nv_crtc->set_color_vibrance) { + /* Hue */ + if (property == disp->vibrant_hue_property) { + nv_crtc->vibrant_hue = value - 90; + return nv_crtc->set_color_vibrance(nv_crtc, true); + } + /* Saturation */ + if (property == disp->color_vibrance_property) { + nv_crtc->color_vibrance = value - 100; + return nv_crtc->set_color_vibrance(nv_crtc, true); + } + } + if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV) return get_slave_funcs(encoder)->set_property( encoder, connector, property, value); @@ -858,6 +871,8 @@ drm_conntype_from_dcb(enum dcb_connector_type dcb) case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID; case DCB_CONNECTOR_LVDS : case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS; + case DCB_CONNECTOR_DMS59_DP0: + case DCB_CONNECTOR_DMS59_DP1: case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort; case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP; case DCB_CONNECTOR_HDMI_0 : @@ -1002,7 +1017,9 @@ nouveau_connector_create(struct drm_device *dev, int index) nv_connector->type == DCB_CONNECTOR_DVI_I || nv_connector->type == DCB_CONNECTOR_HDMI_0 || nv_connector->type == DCB_CONNECTOR_HDMI_1 || - nv_connector->type == DCB_CONNECTOR_DP)) { + nv_connector->type == DCB_CONNECTOR_DP || + nv_connector->type == DCB_CONNECTOR_DMS59_DP0 || + nv_connector->type == DCB_CONNECTOR_DMS59_DP1)) { drm_connector_attach_property(connector, disp->underscan_property, UNDERSCAN_OFF); @@ -1014,6 +1031,16 @@ nouveau_connector_create(struct drm_device *dev, int index) 0); } + /* Add hue and saturation options */ + if (disp->vibrant_hue_property) + drm_connector_attach_property(connector, + disp->vibrant_hue_property, + 90); + if (disp->color_vibrance_property) + drm_connector_attach_property(connector, + disp->color_vibrance_property, + 150); + switch (nv_connector->type) { case DCB_CONNECTOR_VGA: if (dev_priv->card_type >= NV_50) { diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index 686f6b4a1da3..e6d0d1eb0133 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -35,6 +35,8 @@ struct nouveau_crtc { uint32_t dpms_saved_fp_control; uint32_t fp_users; int saturation; + int color_vibrance; + int vibrant_hue; int sharpness; int last_dpms; @@ -67,6 +69,7 @@ struct nouveau_crtc { int (*set_dither)(struct nouveau_crtc *crtc, bool update); int (*set_scale)(struct nouveau_crtc *crtc, bool update); + int (*set_color_vibrance)(struct nouveau_crtc *crtc, bool update); }; static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 795a9e3c990a..c01ae781e2a7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -155,20 +155,20 @@ static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { }; -struct drm_prop_enum_list { +struct nouveau_drm_prop_enum_list { u8 gen_mask; int type; char *name; }; -static struct drm_prop_enum_list underscan[] = { +static struct nouveau_drm_prop_enum_list underscan[] = { { 6, UNDERSCAN_AUTO, "auto" }, { 6, UNDERSCAN_OFF, "off" }, { 6, UNDERSCAN_ON, "on" }, {} }; -static struct drm_prop_enum_list dither_mode[] = { +static struct nouveau_drm_prop_enum_list dither_mode[] = { { 7, DITHERING_MODE_AUTO, "auto" }, { 7, DITHERING_MODE_OFF, "off" }, { 1, DITHERING_MODE_ON, "on" }, @@ -178,7 +178,7 @@ static struct drm_prop_enum_list dither_mode[] = { {} }; -static struct drm_prop_enum_list dither_depth[] = { +static struct nouveau_drm_prop_enum_list dither_depth[] = { { 6, DITHERING_DEPTH_AUTO, "auto" }, { 6, DITHERING_DEPTH_6BPC, "6 bpc" }, { 6, DITHERING_DEPTH_8BPC, "8 bpc" }, @@ -186,7 +186,7 @@ static struct drm_prop_enum_list dither_depth[] = { }; #define PROP_ENUM(p,gen,n,list) do { \ - struct drm_prop_enum_list *l = (list); \ + struct nouveau_drm_prop_enum_list *l = (list); \ int c = 0; \ while (l->gen_mask) { \ if (l->gen_mask & (1 << (gen))) \ @@ -281,16 +281,24 @@ nouveau_display_create(struct drm_device *dev) PROP_ENUM(disp->underscan_property, gen, "underscan", underscan); disp->underscan_hborder_property = - drm_property_create(dev, DRM_MODE_PROP_RANGE, - "underscan hborder", 2); - disp->underscan_hborder_property->values[0] = 0; - disp->underscan_hborder_property->values[1] = 128; + drm_property_create_range(dev, 0, "underscan hborder", 0, 128); disp->underscan_vborder_property = - drm_property_create(dev, DRM_MODE_PROP_RANGE, - "underscan vborder", 2); - disp->underscan_vborder_property->values[0] = 0; - disp->underscan_vborder_property->values[1] = 128; + drm_property_create_range(dev, 0, "underscan vborder", 0, 128); + + if (gen == 1) { + disp->vibrant_hue_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "vibrant hue", 2); + disp->vibrant_hue_property->values[0] = 0; + disp->vibrant_hue_property->values[1] = 180; /* -90..+90 */ + + disp->color_vibrance_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "color vibrance", 2); + disp->color_vibrance_property->values[0] = 0; + disp->color_vibrance_property->values[1] = 200; /* -100..+100 */ + } dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); @@ -309,6 +317,9 @@ nouveau_display_create(struct drm_device *dev) dev->mode_config.max_height = 8192; } + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; + drm_kms_helper_poll_init(dev); drm_kms_helper_poll_disable(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 9b93b703ceab..302b2f7d0678 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -161,116 +161,6 @@ out: 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) { @@ -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; @@ -335,142 +222,58 @@ 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 & |