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]);
}
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;
}
/* 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;
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. */
for (lt.repeater = lt.repeaters; lt.repeater >= 0; lt.repeater--) {
if (lt.repeater)
/* Program selected link configuration. */
ior->dp.bw = dp->rate[rate].rate / 27000;
ior->dp.nr = nr;
- ret = nvkm_dp_train_links(dp);
+ ret = nvkm_dp_train_links(dp, rate);
}
}
}
return ret;
}
+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)
{
if (dp->lttprs && dp->lttpr[1])
rate_max = min_t(int, rate_max, dp->lttpr[1]);
- if (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++;
}
u8 dpcd[16];
struct {
+ int dpcd; /* -1, or index into SUPPORTED_LINK_RATES table */
u32 rate;
- } rate[4];
+ } rate[8];
int rates;
int links;
#define DPCD_RC03_MAX_DOWNSPREAD 0x01
#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
#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