aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau')
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c17
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ttm.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c317
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h35
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c18
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c9
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, &lt->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, &lt->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(&lt);
- if (ret == 0)
- ret = nvkm_dp_train_eq(&lt);
- nvkm_dp_train_pattern(&lt, 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(&lt);
+ if (ret == 0)
+ ret = nvkm_dp_train_eq(&lt);
+ nvkm_dp_train_pattern(&lt, 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