diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau')
-rw-r--r-- | drivers/gpu/drm/nouveau/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/dispnv50/disp.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_connector.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dp.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_encoder.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_mem.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_mem.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_ttm.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c | 317 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h | 35 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c | 9 |
15 files changed, 334 insertions, 121 deletions
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index 9436310d0854..3ec690b6f0b4 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -4,6 +4,7 @@ config DRM_NOUVEAU depends on DRM && PCI && MMU select IOMMU_API select FW_LOADER + select DRM_DP_HELPER select DRM_KMS_HELPER select DRM_TTM select DRM_TTM_HELPER diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index ae1f41205520..df58c6445c51 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -35,7 +35,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_fb_helper.h> #include <drm/drm_plane_helper.h> diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 40f90e353540..1b173191cc41 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -36,7 +36,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_encoder.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_util.h> #include "nouveau_crtc.h" diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 040ed88d362d..724d40ddd452 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -22,7 +22,7 @@ * Authors: Ben Skeggs */ -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #include "nouveau_drv.h" #include "nouveau_connector.h" @@ -147,6 +147,21 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, nv_encoder->dp.link_nr = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP && dpcd[DP_DPCD_REV] >= 0x13) { + struct drm_dp_aux *aux = &nv_connector->aux; + int ret, i; + u8 sink_rates[16]; + + ret = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES, sink_rates, sizeof(sink_rates)); + if (ret == sizeof(sink_rates)) { + for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) { + int val = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10; + if (val && (i == 0 || val > nv_encoder->dp.link_bw)) + nv_encoder->dp.link_bw = val; + } + } + } + NV_DEBUG(drm, "display: %dx%d dpcd 0x%02x\n", nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[DP_DPCD_REV]); diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 77c2fed76e8b..65ed84f88cca 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -30,8 +30,8 @@ #include <subdev/bios/dcb.h> #include <drm/drm_encoder_slave.h> -#include <drm/drm_dp_helper.h> -#include <drm/drm_dp_mst_helper.h> +#include <drm/dp/drm_dp_helper.h> +#include <drm/dp/drm_dp_mst_helper.h> #include "dispnv04/disp.h" struct nv50_head_atom; struct nouveau_connector; diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 2ca3207c13fc..2e517cdc24c9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -162,11 +162,12 @@ nouveau_mem_vram(struct ttm_resource *reg, bool contig, u8 page) } void -nouveau_mem_del(struct ttm_resource *reg) +nouveau_mem_del(struct ttm_resource_manager *man, struct ttm_resource *reg) { struct nouveau_mem *mem = nouveau_mem(reg); nouveau_mem_fini(mem); + ttm_resource_fini(man, reg); kfree(mem); } diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.h b/drivers/gpu/drm/nouveau/nouveau_mem.h index 2c01166a90f2..325551eba5cd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.h +++ b/drivers/gpu/drm/nouveau/nouveau_mem.h @@ -23,7 +23,8 @@ nouveau_mem(struct ttm_resource *reg) int nouveau_mem_new(struct nouveau_cli *, u8 kind, u8 comp, struct ttm_resource **); -void nouveau_mem_del(struct ttm_resource *); +void nouveau_mem_del(struct ttm_resource_manager *man, + struct ttm_resource *); int nouveau_mem_vram(struct ttm_resource *, bool contig, u8 page); int nouveau_mem_host(struct ttm_resource *, struct ttm_tt *); void nouveau_mem_fini(struct nouveau_mem *); diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index 2ca9d9a9e5d5..85f1f5a0fe5d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -36,9 +36,10 @@ #include <core/tegra.h> static void -nouveau_manager_del(struct ttm_resource_manager *man, struct ttm_resource *reg) +nouveau_manager_del(struct ttm_resource_manager *man, + struct ttm_resource *reg) { - nouveau_mem_del(reg); + nouveau_mem_del(man, reg); } static int @@ -62,7 +63,7 @@ nouveau_vram_manager_new(struct ttm_resource_manager *man, ret = nouveau_mem_vram(*res, nvbo->contig, nvbo->page); if (ret) { - nouveau_mem_del(*res); + nouveau_mem_del(man, *res); return ret; } @@ -118,7 +119,7 @@ nv04_gart_manager_new(struct ttm_resource_manager *man, ret = nvif_vmm_get(&mem->cli->vmm.vmm, PTES, false, 12, 0, (long)(*res)->num_pages << PAGE_SHIFT, &mem->vma[0]); if (ret) { - nouveau_mem_del(*res); + nouveau_mem_del(man, *res); return ret; } @@ -163,7 +164,7 @@ nouveau_ttm_init_vram(struct nouveau_drm *drm) man->func = &nouveau_vram_manager; - ttm_resource_manager_init(man, + ttm_resource_manager_init(man, &drm->ttm.bdev, drm->gem.vram_available >> PAGE_SHIFT); ttm_set_driver_manager(&drm->ttm.bdev, TTM_PL_VRAM, man); ttm_resource_manager_set_used(man, true); @@ -210,7 +211,7 @@ nouveau_ttm_init_gtt(struct nouveau_drm *drm) man->func = func; man->use_tt = true; - ttm_resource_manager_init(man, size_pages); + ttm_resource_manager_init(man, &drm->ttm.bdev, size_pages); ttm_set_driver_manager(&drm->ttm.bdev, TTM_PL_TT, man); ttm_resource_manager_set_used(man, true); return 0; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c index 9669472a2749..8e09315b8fb3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -41,6 +41,10 @@ struct lt_state { struct nvkm_dp *dp; + + int repeaters; + int repeater; + u8 stat[6]; u8 conf[4]; bool pc2; @@ -52,14 +56,26 @@ static int nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay) { struct nvkm_dp *dp = lt->dp; + u32 addr; int ret; - if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) - mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); + usleep_range(delay, delay * 2); + + if (lt->repeater) + addr = DPCD_LTTPR_LANE0_1_STATUS(lt->repeater); + else + addr = DPCD_LS02; + + ret = nvkm_rdaux(dp->aux, addr, <->stat[0], 3); + if (ret) + return ret; + + if (lt->repeater) + addr = DPCD_LTTPR_LANE0_1_ADJUST(lt->repeater); else - udelay(delay); + addr = DPCD_LS06; - ret = nvkm_rdaux(dp->aux, DPCD_LS02, lt->stat, 6); + ret = nvkm_rdaux(dp->aux, addr, <->stat[4], 2); if (ret) return ret; @@ -85,6 +101,7 @@ nvkm_dp_train_drive(struct lt_state *lt, bool pc) struct nvbios_dpout info; struct nvbios_dpcfg ocfg; u8 ver, hdr, cnt, len; + u32 addr; u32 data; int ret, i; @@ -113,6 +130,9 @@ nvkm_dp_train_drive(struct lt_state *lt, bool pc) OUTP_TRACE(&dp->outp, "config lane %d %02x %02x", i, lt->conf[i], lpc2); + if (lt->repeater != lt->repeaters) + continue; + data = nvbios_dpout_match(bios, dp->outp.info.hasht, dp->outp.info.hashm, &ver, &hdr, &cnt, &len, &info); @@ -129,7 +149,12 @@ nvkm_dp_train_drive(struct lt_state *lt, bool pc) ocfg.pe, ocfg.tx_pu); } - ret = nvkm_wraux(dp->aux, DPCD_LC03(0), lt->conf, 4); + if (lt->repeater) + addr = DPCD_LTTPR_LANE0_SET(lt->repeater); + else + addr = DPCD_LC03(0); + + ret = nvkm_wraux(dp->aux, addr, lt->conf, 4); if (ret) return ret; @@ -146,32 +171,59 @@ static void nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern) { struct nvkm_dp *dp = lt->dp; + u32 addr; u8 sink_tp; OUTP_TRACE(&dp->outp, "training pattern %d", pattern); dp->outp.ior->func->dp.pattern(dp->outp.ior, pattern); - nvkm_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1); + if (lt->repeater) + addr = DPCD_LTTPR_PATTERN_SET(lt->repeater); + else + addr = DPCD_LC02; + + nvkm_rdaux(dp->aux, addr, &sink_tp, 1); sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; - sink_tp |= pattern; - nvkm_wraux(dp->aux, DPCD_LC02, &sink_tp, 1); + sink_tp |= (pattern != 4) ? pattern : 7; + + if (pattern != 0) + sink_tp |= DPCD_LC02_SCRAMBLING_DISABLE; + else + sink_tp &= ~DPCD_LC02_SCRAMBLING_DISABLE; + nvkm_wraux(dp->aux, addr, &sink_tp, 1); } static int nvkm_dp_train_eq(struct lt_state *lt) { + struct nvkm_i2c_aux *aux = lt->dp->aux; bool eq_done = false, cr_done = true; - int tries = 0, i; + int tries = 0, usec = 0, i; + u8 data; - if (lt->dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED) - nvkm_dp_train_pattern(lt, 3); - else - nvkm_dp_train_pattern(lt, 2); + if (lt->repeater) { + if (!nvkm_rdaux(aux, DPCD_LTTPR_AUX_RD_INTERVAL(lt->repeater), &data, sizeof(data))) + usec = (data & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; + + nvkm_dp_train_pattern(lt, 4); + } else { + if (lt->dp->dpcd[DPCD_RC00_DPCD_REV] >= 0x14 && + lt->dp->dpcd[DPCD_RC03] & DPCD_RC03_TPS4_SUPPORTED) + nvkm_dp_train_pattern(lt, 4); + else + if (lt->dp->dpcd[DPCD_RC00_DPCD_REV] >= 0x12 && + lt->dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED) + nvkm_dp_train_pattern(lt, 3); + else + nvkm_dp_train_pattern(lt, 2); + + usec = (lt->dp->dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; + } do { if ((tries && nvkm_dp_train_drive(lt, lt->pc2)) || - nvkm_dp_train_sense(lt, lt->pc2, 400)) + nvkm_dp_train_sense(lt, lt->pc2, usec ? usec : 400)) break; eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); @@ -193,13 +245,16 @@ nvkm_dp_train_cr(struct lt_state *lt) { bool cr_done = false, abort = false; int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; - int tries = 0, i; + int tries = 0, usec = 0, i; nvkm_dp_train_pattern(lt, 1); + if (lt->dp->dpcd[DPCD_RC00_DPCD_REV] < 0x14 && !lt->repeater) + usec = (lt->dp->dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; + do { if (nvkm_dp_train_drive(lt, false) || - nvkm_dp_train_sense(lt, false, 100)) + nvkm_dp_train_sense(lt, false, usec ? usec : 100)) break; cr_done = true; @@ -223,7 +278,7 @@ nvkm_dp_train_cr(struct lt_state *lt) } static int -nvkm_dp_train_links(struct nvkm_dp *dp) +nvkm_dp_train_links(struct nvkm_dp *dp, int rate) { struct nvkm_ior *ior = dp->outp.ior; struct nvkm_disp *disp = dp->outp.disp; @@ -233,13 +288,15 @@ nvkm_dp_train_links(struct nvkm_dp *dp) .dp = dp, }; u32 lnkcmp; - u8 sink[2]; + u8 sink[2], data; int ret; OUTP_DBG(&dp->outp, "training %d x %d MB/s", ior->dp.nr, ior->dp.bw * 27); /* Intersect misc. capabilities of the OR and sink. */ + if (disp->engine.subdev.device->chipset < 0x110) + dp->dpcd[DPCD_RC03] &= ~DPCD_RC03_TPS4_SUPPORTED; if (disp->engine.subdev.device->chipset < 0xd0) dp->dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED; lt.pc2 = dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED; @@ -287,8 +344,22 @@ nvkm_dp_train_links(struct nvkm_dp *dp) ior->func->dp.power(ior, ior->dp.nr); + /* Select LTTPR non-transparent mode if we have a valid configuration, + * use transparent mode otherwise. + */ + if (dp->lttpr[0] >= 0x14) { + data = DPCD_LTTPR_MODE_TRANSPARENT; + nvkm_wraux(dp->aux, DPCD_LTTPR_MODE, &data, sizeof(data)); + + if (dp->lttprs) { + data = DPCD_LTTPR_MODE_NON_TRANSPARENT; + nvkm_wraux(dp->aux, DPCD_LTTPR_MODE, &data, sizeof(data)); + lt.repeaters = dp->lttprs; + } + } + /* Set desired link configuration on the sink. */ - sink[0] = ior->dp.bw; + sink[0] = (dp->rate[rate].dpcd < 0) ? ior->dp.bw : 0; sink[1] = ior->dp.nr; if (ior->dp.ef) sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; @@ -297,12 +368,33 @@ nvkm_dp_train_links(struct nvkm_dp *dp) if (ret) return ret; + if (dp->rate[rate].dpcd >= 0) { + ret = nvkm_rdaux(dp->aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0])); + if (ret) + return ret; + + sink[0] &= ~DPCD_LC15_LINK_RATE_SET_MASK; + sink[0] |= dp->rate[rate].dpcd; + + ret = nvkm_wraux(dp->aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0])); + if (ret) + return ret; + } + /* Attempt to train the link in this configuration. */ - memset(lt.stat, 0x00, sizeof(lt.stat)); - ret = nvkm_dp_train_cr(<); - if (ret == 0) - ret = nvkm_dp_train_eq(<); - nvkm_dp_train_pattern(<, 0); + for (lt.repeater = lt.repeaters; lt.repeater >= 0; lt.repeater--) { + if (lt.repeater) + OUTP_DBG(&dp->outp, "training LTTPR%d", lt.repeater); + else + OUTP_DBG(&dp->outp, "training sink"); + + memset(lt.stat, 0x00, sizeof(lt.stat)); + ret = nvkm_dp_train_cr(<); + if (ret == 0) + ret = nvkm_dp_train_eq(<); + nvkm_dp_train_pattern(<, 0); + } + return ret; } @@ -345,63 +437,13 @@ nvkm_dp_train_init(struct nvkm_dp *dp) } } -static const struct dp_rates { - u32 rate; - u8 bw; - u8 nr; -} nvkm_dp_rates[] = { - { 2160000, 0x14, 4 }, - { 1080000, 0x0a, 4 }, - { 1080000, 0x14, 2 }, - { 648000, 0x06, 4 }, - { 540000, 0x0a, 2 }, - { 540000, 0x14, 1 }, - { 324000, 0x06, 2 }, - { 270000, 0x0a, 1 }, - { 162000, 0x06, 1 }, - {} -}; - static int nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) { struct nvkm_ior *ior = dp->outp.ior; - const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; - const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE]; - const u8 outp_nr = dp->outp.info.dpconf.link_nr; - const u8 outp_bw = dp->outp.info.dpconf.link_bw; - const struct dp_rates *failsafe = NULL, *cfg; - int ret = -EINVAL; + int ret = -EINVAL, nr, rate; u8 pwr; - /* Find the lowest configuration of the OR that can support - * the required link rate. - * - * We will refuse to program the OR to lower rates, even if - * link training fails at higher rates (or even if the sink - * can't support the rate at all, though the DD is supposed - * to prevent such situations from happening). - * - * Attempting to do so can cause the entire display to hang, - * and it's better to have a failed modeset than that. - */ - for (cfg = nvkm_dp_rates; cfg->rate; cfg++) { - if (cfg->nr <= outp_nr && cfg->bw <= outp_bw) { - /* Try to respect sink limits too when selecting - * lowest link configuration. - */ - if (!failsafe || - (cfg->nr <= sink_nr && cfg->bw <= sink_bw)) - failsafe = cfg; - } - - if (failsafe && cfg[1].rate < dataKBps) - break; - } - - if (WARN_ON(!failsafe)) - return ret; - /* Ensure sink is not in a low-power state. */ if (!nvkm_rdaux(dp->aux, DPCD_SC00, &pwr, 1)) { if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { @@ -411,25 +453,22 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) } } + ior->dp.mst = dp->lt.mst; + ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; + ior->dp.nr = 0; + /* Link training. */ - OUTP_DBG(&dp->outp, "training (min: %d x %d MB/s)", - failsafe->nr, failsafe->bw * 27); + OUTP_DBG(&dp->outp, "training"); nvkm_dp_train_init(dp); - for (cfg = nvkm_dp_rates; ret < 0 && cfg <= failsafe; cfg++) { - /* Skip configurations not supported by both OR and sink. */ - if ((cfg->nr > outp_nr || cfg->bw > outp_bw || - cfg->nr > sink_nr || cfg->bw > sink_bw)) { - if (cfg != failsafe) - continue; - OUTP_ERR(&dp->outp, "link rate unsupported by sink"); + for (nr = dp->links; ret < 0 && nr; nr >>= 1) { + for (rate = 0; ret < 0 && rate < dp->rates; rate++) { + if (dp->rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) { + /* Program selected link configuration. */ + ior->dp.bw = dp->rate[rate].rate / 27000; + ior->dp.nr = nr; + ret = nvkm_dp_train_links(dp, rate); + } } - ior->dp.mst = dp->lt.mst; - ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; - ior->dp.bw = cfg->bw; - ior->dp.nr = cfg->nr; - - /* Program selected link configuration. */ - ret = nvkm_dp_train_links(dp); } nvkm_dp_train_fini(dp); if (ret < 0) @@ -527,6 +566,47 @@ done: } static bool +nvkm_dp_enable_supported_link_rates(struct nvkm_dp *dp) +{ + u8 sink_rates[DPCD_RC10_SUPPORTED_LINK_RATES__SIZE]; + int i, j, k; + + if (dp->outp.conn->info.type != DCB_CONNECTOR_eDP || + dp->dpcd[DPCD_RC00_DPCD_REV] < 0x13 || + nvkm_rdaux(dp->aux, DPCD_RC10_SUPPORTED_LINK_RATES(0), sink_rates, sizeof(sink_rates))) + return false; + + for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) { + const u32 rate = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10; + + if (!rate || WARN_ON(dp->rates == ARRAY_SIZE(dp->rate))) + break; + + if (rate > dp->outp.info.dpconf.link_bw * 27000) { + OUTP_DBG(&dp->outp, "rate %d !outp", rate); + continue; + } + + for (j = 0; j < dp->rates; j++) { + if (rate > dp->rate[j].rate) { + for (k = dp->rates; k > j; k--) + dp->rate[k] = dp->rate[k - 1]; + break; + } + } + + dp->rate[j].dpcd = i / 2; + dp->rate[j].rate = rate; + dp->rates++; + } + + for (i = 0; i < dp->rates; i++) + OUTP_DBG(&dp->outp, "link_rate[%d] = %d", dp->rate[i].dpcd, dp->rate[i].rate); + + return dp->rates != 0; +} + +static bool nvkm_dp_enable(struct nvkm_dp *dp, bool enable) { struct nvkm_i2c_aux *aux = dp->aux; @@ -538,9 +618,60 @@ nvkm_dp_enable(struct nvkm_dp *dp, bool enable) dp->present = true; } - if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, - sizeof(dp->dpcd))) + /* Detect any LTTPRs before reading DPCD receiver caps. */ + if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, dp->lttpr, sizeof(dp->lttpr)) && + dp->lttpr[0] >= 0x14 && dp->lttpr[2]) { + switch (dp->lttpr[2]) { + case 0x80: dp->lttprs = 1; break; + case 0x40: dp->lttprs = 2; break; + case 0x20: dp->lttprs = 3; break; + case 0x10: dp->lttprs = 4; break; + case 0x08: dp->lttprs = 5; break; + case 0x04: dp->lttprs = 6; break; + case 0x02: dp->lttprs = 7; break; + case 0x01: dp->lttprs = 8; break; + default: + /* Unknown LTTPR count, we'll switch to transparent mode. */ + WARN_ON(1); + dp->lttprs = 0; + break; + } + } else { + /* No LTTPR support, or zero LTTPR count - don't touch it at all. */ + memset(dp->lttpr, 0x00, sizeof(dp->lttpr)); + } + + if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, sizeof(dp->dpcd))) { + const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 }; + const u8 *rate; + int rate_max; + + dp->rates = 0; + dp->links = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; + dp->links = min(dp->links, dp->outp.info.dpconf.link_nr); + if (dp->lttprs && dp->lttpr[4]) + dp->links = min_t(int, dp->links, dp->lttpr[4]); + + rate_max = dp->dpcd[DPCD_RC01_MAX_LINK_RATE]; + rate_max = min(rate_max, dp->outp.info.dpconf.link_bw); + if (dp->lttprs && dp->lttpr[1]) + rate_max = min_t(int, rate_max, dp->lttpr[1]); + + if (!nvkm_dp_enable_supported_link_rates(dp)) { + for (rate = rates; *rate; rate++) { + if (*rate <= rate_max) { + if (WARN_ON(dp->rates == ARRAY_SIZE(dp->rate))) + break; + + dp->rate[dp->rates].dpcd = -1; + dp->rate[dp->rates].rate = *rate * 27000; + dp->rates++; + } + } + } + return true; + } } if (dp->present) { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h index e484d0c3b0d4..8e59dd469da6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h @@ -9,10 +9,7 @@ #include <subdev/bios/dp.h> struct nvkm_dp { - union { - struct nvkm_outp base; - struct nvkm_outp outp; - }; + struct nvkm_outp outp; struct nvbios_dpout info; u8 version; @@ -21,8 +18,17 @@ struct nvkm_dp { struct nvkm_notify hpd; bool present; + u8 lttpr[6]; + u8 lttprs; u8 dpcd[16]; + struct { + int dpcd; /* -1, or index into SUPPORTED_LINK_RATES table */ + u32 rate; + } rate[8]; + int rates; + int links; + struct mutex mutex; struct { atomic_t done; @@ -42,8 +48,12 @@ void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *); #define DPCD_RC02_TPS3_SUPPORTED 0x40 #define DPCD_RC02_MAX_LANE_COUNT 0x1f #define DPCD_RC03 0x00003 +#define DPCD_RC03_TPS4_SUPPORTED 0x80 #define DPCD_RC03_MAX_DOWNSPREAD 0x01 -#define DPCD_RC0E_AUX_RD_INTERVAL 0x0000e +#define DPCD_RC0E 0x0000e +#define DPCD_RC0E_AUX_RD_INTERVAL 0x7f +#define DPCD_RC10_SUPPORTED_LINK_RATES(i) 0x00010 +#define DPCD_RC10_SUPPORTED_LINK_RATES__SIZE 16 /* DPCD Link Configuration */ #define DPCD_LC00_LINK_BW_SET 0x00100 @@ -51,7 +61,8 @@ void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *); #define DPCD_LC01_ENHANCED_FRAME_EN 0x80 #define DPCD_LC01_LANE_COUNT_SET 0x1f #define DPCD_LC02 0x00102 -#define DPCD_LC02_TRAINING_PATTERN_SET 0x03 +#define DPCD_LC02_TRAINING_PATTERN_SET 0x0f +#define DPCD_LC02_SCRAMBLING_DISABLE 0x20 #define DPCD_LC03(l) ((l) + 0x00103) #define DPCD_LC03_MAX_PRE_EMPHASIS_REACHED 0x20 #define DPCD_LC03_PRE_EMPHASIS_SET 0x18 @@ -67,6 +78,8 @@ void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *); #define DPCD_LC10_LANE3_POST_CURSOR2_SET 0x30 #define DPCD_LC10_LANE2_MAX_POST_CURSOR2_REACHED 0x04 #define DPCD_LC10_LANE2_POST_CURSOR2_SET 0x03 +#define DPCD_LC15_LINK_RATE_SET 0x00115 +#define DPCD_LC15_LINK_RATE_SET_MASK 0x07 /* DPCD Link/Sink Status */ #define DPCD_LS02 0x00202 @@ -108,4 +121,14 @@ void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *); #define DPCD_SC00_SET_POWER 0x03 #define DPCD_SC00_SET_POWER_D0 0x01 #define DPCD_SC00_SET_POWER_D3 0x03 + +#define DPCD_LTTPR_REV 0xf0000 +#define DPCD_LTTPR_MODE 0xf0003 +#define DPCD_LTTPR_MODE_TRANSPARENT 0x55 +#define DPCD_LTTPR_MODE_NON_TRANSPARENT 0xaa +#define DPCD_LTTPR_PATTERN_SET(i) ((i - 1) * 0x50 + 0xf0010) +#define DPCD_LTTPR_LANE0_SET(i) ((i - 1) * 0x50 + 0xf0011) +#define DPCD_LTTPR_AUX_RD_INTERVAL(i) ((i - 1) * 0x50 + 0xf0020) +#define DPCD_LTTPR_LANE0_1_STATUS(i) ((i - 1) * 0x50 + 0xf0030) +#define DPCD_LTTPR_LANE0_1_ADJUST(i) ((i - 1) * 0x50 + 0xf0033) #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c index 4d59d02525d9..56b8f4411988 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c @@ -77,7 +77,18 @@ g94_sor_dp_pattern(struct nvkm_ior *sor, int pattern) { struct nvkm_device *device = sor->disp->engine.subdev.device; const u32 loff = nv50_sor_link(sor); - nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24); + u32 data; + + switch (pattern) { + case 0: data = 0x00001000; break; + case 1: data = 0x01000000; break; + case 2: data = 0x02000000; break; + default: + WARN_ON(1); + return; + } + + nvkm_mask(device, 0x61c10c + loff, 0x0f001000, data); } void diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c index 033827de9116..d2c05f5c4aa0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c @@ -37,6 +37,10 @@ ga102_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) case 0x0a: clksor |= 0x00040000; break; case 0x14: clksor |= 0x00080000; break; case 0x1e: clksor |= 0x000c0000; break; + case 0x08: clksor |= 0x00100000; break; + case 0x09: clksor |= 0x00140000; break; + case 0x0c: clksor |= 0x00180000; break; + case 0x10: clksor |= 0x001c0000; break; default: WARN_ON(1); return -EINVAL; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c index 3b3643fb1019..c431e0b9fc11 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c @@ -92,7 +92,19 @@ gf119_sor_dp_pattern(struct nvkm_ior *sor, int pattern) { struct nvkm_device *device = sor->disp->engine.subdev.device; const u32 soff = nv50_ior_base(sor); - nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern); + u32 data; + + switch (pattern) { + case 0: data = 0x10101010; break; + case 1: data = 0x01010101; break; + case 2: data = 0x02020202; break; + case 3: data = 0x03030303; break; + default: + WARN_ON(1); + return; + } + + nvkm_mask(device, 0x61c110 + soff, 0x1f1f1f1f, data); } int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c index 38045c92197f..3696bfd3bfd7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c @@ -28,11 +28,23 @@ gm107_sor_dp_pattern(struct nvkm_ior *sor, int pattern) { struct nvkm_device *device = sor->disp->engine.subdev.device; const u32 soff = nv50_ior_base(sor); - const u32 data = 0x01010101 * pattern; + u32 mask = 0x1f1f1f1f, data; + + switch (pattern) { + case 0: data = 0x10101010; break; + case 1: data = 0x01010101; break; + case 2: data = 0x02020202; break; + case 3: data = 0x03030303; break; + case 4: data = 0x1b1b1b1b; break; + default: + WARN_ON(1); + return; + } + if (sor->asy.link & 1) - nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, data); + nvkm_mask(device, 0x61c110 + soff, mask, data); else - nvkm_mask(device, 0x61c12c + soff, 0x0f0f0f0f, data); + nvkm_mask(device, 0x61c12c + soff, mask, data); } static const struct nvkm_ior_func diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c index 667fa016496e..a6ea89a5d51a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c @@ -142,11 +142,12 @@ nvkm_acr_hsfw_load_bl(struct nvkm_acr *acr, const char *name, int ver, hsfw->imem_size = desc->code_size; hsfw->imem_tag = desc->start_tag; - hsfw->imem = kmalloc(desc->code_size, GFP_KERNEL); - memcpy(hsfw->imem, data + desc->code_off, desc->code_size); - + hsfw->imem = kmemdup(data + desc->code_off, desc->code_size, GFP_KERNEL); nvkm_firmware_put(fw); - return 0; + if (!hsfw->imem) + return -ENOMEM; + else + return 0; } int |