drm/nouveau/disp: add dp rates method
- moves building of link rates table from NVKM to DRM - preparing to move link training out of supervisor Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Reviewed-by: Lyude Paul <lyude@redhat.com> Acked-by: Danilo Krummrich <me@dakr.org> Signed-off-by: Lyude Paul <lyude@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20230919220442.202488-29-lyude@redhat.com
This commit is contained in:
parent
bd7a61bcbb
commit
bfb03a077b
@ -33,6 +33,7 @@ union nvif_outp_args {
|
||||
|
||||
#define NVIF_OUTP_V0_DP_AUX_PWR 0x70
|
||||
#define NVIF_OUTP_V0_DP_AUX_XFER 0x71
|
||||
#define NVIF_OUTP_V0_DP_RATES 0x72
|
||||
#define NVIF_OUTP_V0_DP_RETRAIN 0x73
|
||||
#define NVIF_OUTP_V0_DP_MST_VCPI 0x78
|
||||
|
||||
@ -194,6 +195,18 @@ union nvif_outp_dp_aux_xfer_args {
|
||||
} v0;
|
||||
};
|
||||
|
||||
union nvif_outp_dp_rates_args {
|
||||
struct nvif_outp_dp_rates_v0 {
|
||||
__u8 version;
|
||||
__u8 pad01[6];
|
||||
__u8 rates;
|
||||
struct {
|
||||
__s8 dpcd;
|
||||
__u32 rate;
|
||||
} rate[8];
|
||||
} v0;
|
||||
};
|
||||
|
||||
union nvif_outp_dp_retrain_args {
|
||||
struct nvif_outp_dp_retrain_vn {
|
||||
} vn;
|
||||
|
@ -59,6 +59,14 @@ int nvif_outp_hda_eld(struct nvif_outp *, int head, void *data, u32 size);
|
||||
|
||||
int nvif_outp_dp_aux_pwr(struct nvif_outp *, bool enable);
|
||||
int nvif_outp_dp_aux_xfer(struct nvif_outp *, u8 type, u8 *size, u32 addr, u8 *data);
|
||||
|
||||
struct nvif_outp_dp_rate {
|
||||
int dpcd; /* -1 for non-indexed rates */
|
||||
u32 rate;
|
||||
};
|
||||
|
||||
int nvif_outp_dp_rates(struct nvif_outp *, struct nvif_outp_dp_rate *rate, int rate_nr);
|
||||
|
||||
int nvif_outp_dp_retrain(struct nvif_outp *);
|
||||
int nvif_outp_dp_mst_vcpi(struct nvif_outp *, int head,
|
||||
u8 start_slot, u8 num_slots, u16 pbn, u16 aligned_pbn);
|
||||
|
@ -42,6 +42,21 @@ nouveau_dp_has_sink_count(struct drm_connector *connector,
|
||||
return drm_dp_read_sink_count_cap(connector, outp->dp.dpcd, &outp->dp.desc);
|
||||
}
|
||||
|
||||
static bool
|
||||
nouveau_dp_probe_lttpr(struct nouveau_encoder *outp)
|
||||
{
|
||||
u8 rev, size = sizeof(rev);
|
||||
int ret;
|
||||
|
||||
ret = nvif_outp_dp_aux_xfer(&outp->outp, DP_AUX_NATIVE_READ, &size,
|
||||
DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV,
|
||||
&rev);
|
||||
if (ret || size < sizeof(rev) || rev < 0x14)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector,
|
||||
struct nouveau_encoder *outp)
|
||||
@ -53,10 +68,99 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector,
|
||||
int ret;
|
||||
u8 *dpcd = outp->dp.dpcd;
|
||||
|
||||
outp->dp.lttpr.nr = 0;
|
||||
outp->dp.rate_nr = 0;
|
||||
outp->dp.link_nr = 0;
|
||||
outp->dp.link_bw = 0;
|
||||
|
||||
if (connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
|
||||
nouveau_dp_probe_lttpr(outp) &&
|
||||
!drm_dp_read_dpcd_caps(aux, dpcd) &&
|
||||
!drm_dp_read_lttpr_common_caps(aux, dpcd, outp->dp.lttpr.caps)) {
|
||||
int nr = drm_dp_lttpr_count(outp->dp.lttpr.caps);
|
||||
|
||||
if (nr > 0)
|
||||
outp->dp.lttpr.nr = nr;
|
||||
}
|
||||
|
||||
ret = drm_dp_read_dpcd_caps(aux, dpcd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
outp->dp.link_nr = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
|
||||
if (outp->dcb->dpconf.link_nr < outp->dp.link_nr)
|
||||
outp->dp.link_nr = outp->dcb->dpconf.link_nr;
|
||||
|
||||
if (outp->dp.lttpr.nr) {
|
||||
int links = drm_dp_lttpr_max_lane_count(outp->dp.lttpr.caps);
|
||||
|
||||
if (links && links < outp->dp.link_nr)
|
||||
outp->dp.link_nr = links;
|
||||
}
|
||||
|
||||
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP && dpcd[DP_DPCD_REV] >= 0x13) {
|
||||
__le16 rates[DP_MAX_SUPPORTED_RATES];
|
||||
|
||||
ret = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES, rates, sizeof(rates));
|
||||
if (ret == sizeof(rates)) {
|
||||
for (int i = 0; i < ARRAY_SIZE(rates); i++) {
|
||||
u32 rate = (le16_to_cpu(rates[i]) * 200) / 10;
|
||||
int j;
|
||||
|
||||
if (!rate)
|
||||
break;
|
||||
|
||||
for (j = 0; j < outp->dp.rate_nr; j++) {
|
||||
if (rate > outp->dp.rate[j].rate) {
|
||||
for (int k = outp->dp.rate_nr; k > j; k--)
|
||||
outp->dp.rate[k] = outp->dp.rate[k - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outp->dp.rate[j].dpcd = i;
|
||||
outp->dp.rate[j].rate = rate;
|
||||
outp->dp.rate_nr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!outp->dp.rate_nr) {
|
||||
const u32 rates[] = { 810000, 540000, 270000, 162000 };
|
||||
u32 max_rate = dpcd[DP_MAX_LINK_RATE] * 27000;
|
||||
|
||||
if (outp->dp.lttpr.nr) {
|
||||
int rate = drm_dp_lttpr_max_link_rate(outp->dp.lttpr.caps);
|
||||
|
||||
if (rate && rate < max_rate)
|
||||
max_rate = rate;
|
||||
}
|
||||
|
||||
max_rate = min_t(int, max_rate, outp->dcb->dpconf.link_bw);
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(rates); i++) {
|
||||
if (rates[i] <= max_rate) {
|
||||
outp->dp.rate[outp->dp.rate_nr].dpcd = -1;
|
||||
outp->dp.rate[outp->dp.rate_nr].rate = rates[i];
|
||||
outp->dp.rate_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(!outp->dp.rate_nr))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nvif_outp_dp_rates(&outp->outp, outp->dp.rate, outp->dp.rate_nr);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
for (int i = 0; i < outp->dp.rate_nr; i++) {
|
||||
u32 link_bw = outp->dp.rate[i].rate;
|
||||
|
||||
if (link_bw > outp->dp.link_bw)
|
||||
outp->dp.link_bw = link_bw;
|
||||
}
|
||||
|
||||
ret = drm_dp_read_desc(aux, &outp->dp.desc, drm_dp_is_branch(dpcd));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -151,39 +255,14 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector,
|
||||
goto out;
|
||||
}
|
||||
|
||||
nv_encoder->dp.link_bw = 27000 * dpcd[DP_MAX_LINK_RATE];
|
||||
nv_encoder->dp.link_nr =
|
||||
dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
|
||||
NV_DEBUG(drm, "sink dpcd version: 0x%02x\n", dpcd[DP_DPCD_REV]);
|
||||
for (int i = 0; i < nv_encoder->dp.rate_nr; i++)
|
||||
NV_DEBUG(drm, "sink rate %d: %d\n", i, nv_encoder->dp.rate[i].rate);
|
||||
|
||||
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]);
|
||||
NV_DEBUG(drm, "encoder: %dx%d\n",
|
||||
nv_encoder->dcb->dpconf.link_nr,
|
||||
nv_encoder->dcb->dpconf.link_bw);
|
||||
|
||||
if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr)
|
||||
nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
|
||||
if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw)
|
||||
nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw;
|
||||
|
||||
NV_DEBUG(drm, "maximum: %dx%d\n",
|
||||
nv_encoder->dp.link_nr, nv_encoder->dp.link_bw);
|
||||
NV_DEBUG(drm, "encoder: %dx%d\n", nv_encoder->dcb->dpconf.link_nr,
|
||||
nv_encoder->dcb->dpconf.link_bw);
|
||||
NV_DEBUG(drm, "maximum: %dx%d\n", nv_encoder->dp.link_nr,
|
||||
nv_encoder->dp.link_bw);
|
||||
|
||||
if (mstm && mstm->can_mst) {
|
||||
ret = nv50_mstm_detect(nv_encoder);
|
||||
|
@ -75,6 +75,17 @@ struct nouveau_encoder {
|
||||
|
||||
struct {
|
||||
struct nv50_mstm *mstm;
|
||||
|
||||
struct {
|
||||
u8 caps[DP_LTTPR_COMMON_CAP_SIZE];
|
||||
u8 nr;
|
||||
} lttpr;
|
||||
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
|
||||
struct nvif_outp_dp_rate rate[8];
|
||||
int rate_nr;
|
||||
|
||||
int link_nr;
|
||||
int link_bw;
|
||||
|
||||
@ -83,7 +94,6 @@ struct nouveau_encoder {
|
||||
*/
|
||||
struct mutex hpd_irq_lock;
|
||||
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
|
||||
struct drm_dp_desc desc;
|
||||
|
||||
|
@ -76,6 +76,27 @@ nvif_outp_acquire_dp(struct nvif_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nvif_outp_dp_rates(struct nvif_outp *outp, struct nvif_outp_dp_rate *rate, int rate_nr)
|
||||
{
|
||||
struct nvif_outp_dp_rates_v0 args;
|
||||
int ret;
|
||||
|
||||
if (rate_nr > ARRAY_SIZE(args.rate))
|
||||
return -EINVAL;
|
||||
|
||||
args.version = 0;
|
||||
args.rates = rate_nr;
|
||||
for (int i = 0; i < args.rates; i++, rate++) {
|
||||
args.rate[i].dpcd = rate->dpcd;
|
||||
args.rate[i].rate = rate->rate;
|
||||
}
|
||||
|
||||
ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_RATES, &args, sizeof(args));
|
||||
NVIF_ERRON(ret, &outp->object, "[DP_RATES rates:%d]", args.rates);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nvif_outp_dp_aux_xfer(struct nvif_outp *outp, u8 type, u8 *psize, u32 addr, u8 *data)
|
||||
{
|
||||
|
@ -615,48 +615,6 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp)
|
||||
{
|
||||
u8 sink_rates[DPCD_RC10_SUPPORTED_LINK_RATES__SIZE];
|
||||
int i, j, k;
|
||||
|
||||
if (outp->conn->info.type != DCB_CONNECTOR_eDP ||
|
||||
outp->dp.dpcd[DPCD_RC00_DPCD_REV] < 0x13 ||
|
||||
nvkm_rdaux(outp->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(outp->dp.rates == ARRAY_SIZE(outp->dp.rate)))
|
||||
break;
|
||||
|
||||
if (rate > outp->info.dpconf.link_bw * 27000) {
|
||||
OUTP_DBG(outp, "rate %d !outp", rate);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < outp->dp.rates; j++) {
|
||||
if (rate > outp->dp.rate[j].rate) {
|
||||
for (k = outp->dp.rates; k > j; k--)
|
||||
outp->dp.rate[k] = outp->dp.rate[k - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outp->dp.rate[j].dpcd = i / 2;
|
||||
outp->dp.rate[j].rate = rate;
|
||||
outp->dp.rates++;
|
||||
}
|
||||
|
||||
for (i = 0; i < outp->dp.rates; i++)
|
||||
OUTP_DBG(outp, "link_rate[%d] = %d", outp->dp.rate[i].dpcd, outp->dp.rate[i].rate);
|
||||
|
||||
return outp->dp.rates != 0;
|
||||
}
|
||||
|
||||
/* XXX: This is a big fat hack, and this is just drm_dp_read_dpcd_caps()
|
||||
* converted to work inside nvkm. This is a temporary holdover until we start
|
||||
* passing the drm_dp_aux device through NVKM
|
||||
@ -757,34 +715,10 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr)
|
||||
}
|
||||
|
||||
if (!nvkm_dp_read_dpcd_caps(outp)) {
|
||||
const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 };
|
||||
const u8 *rate;
|
||||
int rate_max;
|
||||
|
||||
outp->dp.rates = 0;
|
||||
outp->dp.links = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT;
|
||||
outp->dp.links = min(outp->dp.links, outp->info.dpconf.link_nr);
|
||||
if (outp->dp.lttprs && outp->dp.lttpr[4])
|
||||
outp->dp.links = min_t(int, outp->dp.links, outp->dp.lttpr[4]);
|
||||
|
||||
rate_max = outp->dp.dpcd[DPCD_RC01_MAX_LINK_RATE];
|
||||
rate_max = min(rate_max, outp->info.dpconf.link_bw);
|
||||
if (outp->dp.lttprs && outp->dp.lttpr[1])
|
||||
rate_max = min_t(int, rate_max, outp->dp.lttpr[1]);
|
||||
|
||||
if (!nvkm_dp_enable_supported_link_rates(outp)) {
|
||||
for (rate = rates; *rate; rate++) {
|
||||
if (*rate > rate_max)
|
||||
continue;
|
||||
|
||||
if (WARN_ON(outp->dp.rates == ARRAY_SIZE(outp->dp.rate)))
|
||||
break;
|
||||
|
||||
outp->dp.rate[outp->dp.rates].dpcd = -1;
|
||||
outp->dp.rate[outp->dp.rates].rate = *rate * 27000;
|
||||
outp->dp.rates++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
if (!auxpwr && outp->dp.aux_pwr) {
|
||||
|
@ -108,6 +108,7 @@ struct nvkm_outp_func {
|
||||
struct {
|
||||
int (*aux_pwr)(struct nvkm_outp *, bool pu);
|
||||
int (*aux_xfer)(struct nvkm_outp *, u8 type, u32 addr, u8 *data, u8 *size);
|
||||
int (*rates)(struct nvkm_outp *);
|
||||
} dp;
|
||||
};
|
||||
|
||||
|
@ -70,6 +70,29 @@ nvkm_uoutp_mthd_acquire_dp(struct nvkm_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE]
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_uoutp_mthd_dp_rates(struct nvkm_outp *outp, void *argv, u32 argc)
|
||||
{
|
||||
union nvif_outp_dp_rates_args *args = argv;
|
||||
|
||||
if (argc != sizeof(args->v0) || args->v0.version != 0)
|
||||
return -ENOSYS;
|
||||
if (args->v0.rates > ARRAY_SIZE(outp->dp.rate))
|
||||
return -EINVAL;
|
||||
|
||||
for (int i = 0; i < args->v0.rates; i++) {
|
||||
outp->dp.rate[i].dpcd = args->v0.rate[i].dpcd;
|
||||
outp->dp.rate[i].rate = args->v0.rate[i].rate;
|
||||
}
|
||||
|
||||
outp->dp.rates = args->v0.rates;
|
||||
|
||||
if (outp->func->dp.rates)
|
||||
outp->func->dp.rates(outp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_uoutp_mthd_dp_aux_xfer(struct nvkm_outp *outp, void *argv, u32 argc)
|
||||
{
|
||||
@ -457,6 +480,7 @@ nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc
|
||||
case NVIF_OUTP_V0_BL_SET : return nvkm_uoutp_mthd_bl_set (outp, argv, argc);
|
||||
case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc);
|
||||
case NVIF_OUTP_V0_DP_AUX_XFER: return nvkm_uoutp_mthd_dp_aux_xfer(outp, argv, argc);
|
||||
case NVIF_OUTP_V0_DP_RATES : return nvkm_uoutp_mthd_dp_rates (outp, argv, argc);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user