mirror of
https://github.com/torvalds/linux.git
synced 2024-09-21 23:43:02 +00:00
drm/nouveau/disp: move link training out of supervisor
- preparation for GSP-RM 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-34-lyude@redhat.com
This commit is contained in:
parent
633716501c
commit
3147ce0d07
|
@ -35,6 +35,7 @@ union nvif_outp_args {
|
|||
#define NVIF_OUTP_V0_DP_AUX_XFER 0x71
|
||||
#define NVIF_OUTP_V0_DP_RATES 0x72
|
||||
#define NVIF_OUTP_V0_DP_TRAIN 0x73
|
||||
#define NVIF_OUTP_V0_DP_DRIVE 0x74
|
||||
#define NVIF_OUTP_V0_DP_MST_VCPI 0x78
|
||||
|
||||
union nvif_outp_detect_args {
|
||||
|
@ -211,6 +212,16 @@ union nvif_outp_dp_train_args {
|
|||
} v0;
|
||||
};
|
||||
|
||||
union nvif_outp_dp_drive_args {
|
||||
struct nvif_outp_dp_drive_v0 {
|
||||
__u8 version;
|
||||
__u8 pad01[2];
|
||||
__u8 lanes;
|
||||
__u8 pe[4];
|
||||
__u8 vs[4];
|
||||
} v0;
|
||||
};
|
||||
|
||||
union nvif_outp_dp_mst_vcpi_args {
|
||||
struct nvif_outp_dp_mst_vcpi_v0 {
|
||||
__u8 version;
|
||||
|
|
|
@ -67,6 +67,7 @@ int nvif_outp_dp_rates(struct nvif_outp *, struct nvif_outp_dp_rate *rate, int r
|
|||
int nvif_outp_dp_train(struct nvif_outp *, u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
u8 lttprs, u8 link_nr, u32 link_bw, bool mst, bool post_lt_adj,
|
||||
bool retrain);
|
||||
int nvif_outp_dp_drive(struct nvif_outp *, u8 link_nr, u8 pe[4], u8 vs[4]);
|
||||
int nvif_outp_dp_mst_vcpi(struct nvif_outp *, int head,
|
||||
u8 start_slot, u8 num_slots, u16 pbn, u16 aligned_pbn);
|
||||
#endif
|
||||
|
|
|
@ -320,15 +320,83 @@ nouveau_dp_power_down(struct nouveau_encoder *outp)
|
|||
static bool
|
||||
nouveau_dp_train_link(struct nouveau_encoder *outp, bool retrain)
|
||||
{
|
||||
int ret;
|
||||
struct drm_dp_aux *aux = &outp->conn->aux;
|
||||
bool post_lt = false;
|
||||
int ret, retries = 0;
|
||||
|
||||
if ( (outp->dp.dpcd[DP_MAX_LANE_COUNT] & 0x20) &&
|
||||
!(outp->dp.dpcd[DP_MAX_DOWNSPREAD] & DP_TPS4_SUPPORTED))
|
||||
post_lt = true;
|
||||
|
||||
retry:
|
||||
ret = nvif_outp_dp_train(&outp->outp, outp->dp.dpcd,
|
||||
outp->dp.lttpr.nr,
|
||||
outp->dp.lt.nr,
|
||||
outp->dp.lt.bw,
|
||||
outp->dp.lt.mst,
|
||||
false,
|
||||
post_lt,
|
||||
retrain);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
if (post_lt) {
|
||||
u8 stat[DP_LINK_STATUS_SIZE];
|
||||
u8 prev[2];
|
||||
u8 time = 0, adjusts = 0, tmp;
|
||||
|
||||
ret = drm_dp_dpcd_read_phy_link_status(aux, DP_PHY_DPRX, stat);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
for (;;) {
|
||||
if (!drm_dp_channel_eq_ok(stat, outp->dp.lt.nr)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(stat[2] & 0x02))
|
||||
break;
|
||||
|
||||
msleep(5);
|
||||
time += 5;
|
||||
|
||||
memcpy(prev, &stat[4], sizeof(prev));
|
||||
ret = drm_dp_dpcd_read_phy_link_status(aux, DP_PHY_DPRX, stat);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (!memcmp(prev, &stat[4], sizeof(prev))) {
|
||||
if (time > 200)
|
||||
break;
|
||||
} else {
|
||||
u8 pe[4], vs[4];
|
||||
|
||||
if (adjusts++ == 6)
|
||||
break;
|
||||
|
||||
for (int i = 0; i < outp->dp.lt.nr; i++) {
|
||||
pe[i] = drm_dp_get_adjust_request_pre_emphasis(stat, i) >>
|
||||
DP_TRAIN_PRE_EMPHASIS_SHIFT;
|
||||
vs[i] = drm_dp_get_adjust_request_voltage(stat, i) >>
|
||||
DP_TRAIN_VOLTAGE_SWING_SHIFT;
|
||||
}
|
||||
|
||||
ret = nvif_outp_dp_drive(&outp->outp, outp->dp.lt.nr, pe, vs);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (drm_dp_dpcd_readb(aux, DP_LANE_COUNT_SET, &tmp) == 1) {
|
||||
tmp &= ~0x20;
|
||||
drm_dp_dpcd_writeb(aux, DP_LANE_COUNT_SET, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 1 && retries++ < 3)
|
||||
goto retry;
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
|
@ -336,15 +404,44 @@ nouveau_dp_train_link(struct nouveau_encoder *outp, bool retrain)
|
|||
bool
|
||||
nouveau_dp_train(struct nouveau_encoder *outp, bool mst, u32 khz, u8 bpc)
|
||||
{
|
||||
bool ret;
|
||||
struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev);
|
||||
struct drm_dp_aux *aux = &outp->conn->aux;
|
||||
u32 min_rate;
|
||||
u8 pwr;
|
||||
bool ret = true;
|
||||
|
||||
if (mst)
|
||||
min_rate = outp->dp.link_nr * outp->dp.rate[0].rate;
|
||||
else
|
||||
min_rate = DIV_ROUND_UP(khz * bpc * 3, 8);
|
||||
|
||||
NV_DEBUG(drm, "%s link training (mst:%d min_rate:%d)\n",
|
||||
outp->base.base.name, mst, min_rate);
|
||||
|
||||
mutex_lock(&outp->dp.hpd_irq_lock);
|
||||
|
||||
outp->dp.lt.nr = outp->dp.link_nr;
|
||||
outp->dp.lt.bw = 0;
|
||||
outp->dp.lt.mst = mst;
|
||||
ret = nouveau_dp_train_link(outp, false);
|
||||
if (drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr) == 1) {
|
||||
if ((pwr & DP_SET_POWER_MASK) != DP_SET_POWER_D0) {
|
||||
pwr &= ~DP_SET_POWER_MASK;
|
||||
pwr |= DP_SET_POWER_D0;
|
||||
drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr);
|
||||
}
|
||||
}
|
||||
|
||||
for (int nr = outp->dp.link_nr; nr; nr >>= 1) {
|
||||
for (int rate = 0; rate < outp->dp.rate_nr; rate++) {
|
||||
if (outp->dp.rate[rate].rate * nr >= min_rate) {
|
||||
outp->dp.lt.nr = nr;
|
||||
outp->dp.lt.bw = outp->dp.rate[rate].rate;
|
||||
outp->dp.lt.mst = mst;
|
||||
if (nouveau_dp_train_link(outp, false))
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = false;
|
||||
done:
|
||||
mutex_unlock(&outp->dp.hpd_irq_lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -352,6 +449,17 @@ nouveau_dp_train(struct nouveau_encoder *outp, bool mst, u32 khz, u8 bpc)
|
|||
static bool
|
||||
nouveau_dp_link_check_locked(struct nouveau_encoder *outp)
|
||||
{
|
||||
u8 link_status[DP_LINK_STATUS_SIZE];
|
||||
|
||||
if (!outp || !outp->dp.lt.nr)
|
||||
return true;
|
||||
|
||||
if (drm_dp_dpcd_read_phy_link_status(&outp->conn->aux, DP_PHY_DPRX, link_status) < 0)
|
||||
return false;
|
||||
|
||||
if (drm_dp_channel_eq_ok(link_status, outp->dp.lt.nr))
|
||||
return true;
|
||||
|
||||
return nouveau_dp_train_link(outp, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,22 @@ nvif_outp_dp_mst_vcpi(struct nvif_outp *outp, int head,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nvif_outp_dp_drive(struct nvif_outp *outp, u8 link_nr, u8 pe[4], u8 vs[4])
|
||||
{
|
||||
struct nvif_outp_dp_drive_v0 args;
|
||||
int ret;
|
||||
|
||||
args.version = 0;
|
||||
args.lanes = link_nr;
|
||||
memcpy(args.pe, pe, sizeof(args.pe));
|
||||
memcpy(args.vs, vs, sizeof(args.vs));
|
||||
|
||||
ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_DRIVE, &args, sizeof(args));
|
||||
NVIF_ERRON(ret, &outp->object, "[DP_DRIVE lanes:%d]", args.lanes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nvif_outp_dp_train(struct nvif_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE], u8 lttprs,
|
||||
u8 link_nr, u32 link_bw, bool mst, bool post_lt_adj, bool retrain)
|
||||
|
|
|
@ -315,6 +315,8 @@ nvkm_dp_train_link(struct nvkm_outp *outp, int rate)
|
|||
sink[1] = ior->dp.nr;
|
||||
if (ior->dp.ef)
|
||||
sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
|
||||
if (outp->dp.lt.post_adj)
|
||||
sink[1] |= 0x20;
|
||||
|
||||
ret = nvkm_wraux(outp->dp.aux, DPCD_LC00_LINK_BW_SET, sink, 2);
|
||||
if (ret)
|
||||
|
@ -455,71 +457,58 @@ nvkm_dp_train_init(struct nvkm_outp *outp)
|
|||
}
|
||||
|
||||
static int
|
||||
nvkm_dp_train_(struct nvkm_outp *outp, bool retrain)
|
||||
nvkm_dp_drive(struct nvkm_outp *outp, u8 lanes, u8 pe[4], u8 vs[4])
|
||||
{
|
||||
if (retrain) {
|
||||
if (!atomic_read(&outp->dp.lt.done))
|
||||
return 0;
|
||||
struct lt_state lt = {
|
||||
.outp = outp,
|
||||
.stat[4] = (pe[0] << 2) | (vs[0] << 0) |
|
||||
(pe[1] << 6) | (vs[1] << 4),
|
||||
.stat[5] = (pe[2] << 2) | (vs[2] << 0) |
|
||||
(pe[3] << 6) | (vs[3] << 4),
|
||||
};
|
||||
|
||||
return outp->func->acquire(outp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nvkm_dp_train_drive(<, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps)
|
||||
nvkm_dp_train(struct nvkm_outp *outp, bool retrain)
|
||||
{
|
||||
struct nvkm_ior *ior = outp->ior;
|
||||
int ret = -EINVAL, nr, rate;
|
||||
u8 pwr;
|
||||
int ret, rate;
|
||||
|
||||
for (rate = 0; rate < outp->dp.rates; rate++) {
|
||||
if (outp->dp.rate[rate].rate == (retrain ? ior->dp.bw : outp->dp.lt.bw) * 27000)
|
||||
break;
|
||||
}
|
||||
|
||||
if (WARN_ON(rate == outp->dp.rates))
|
||||
return -EINVAL;
|
||||
|
||||
/* Retraining link? Skip source configuration, it can mess up the active modeset. */
|
||||
if (atomic_read(&outp->dp.lt.done)) {
|
||||
for (rate = 0; rate < outp->dp.rates; rate++) {
|
||||
if (outp->dp.rate[rate].rate == ior->dp.bw * 27000)
|
||||
return nvkm_dp_train_link(outp, ret);
|
||||
}
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
if (retrain) {
|
||||
mutex_lock(&outp->dp.mutex);
|
||||
ret = nvkm_dp_train_link(outp, rate);
|
||||
mutex_unlock(&outp->dp.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Ensure sink is not in a low-power state. */
|
||||
if (!nvkm_rdaux(outp->dp.aux, DPCD_SC00, &pwr, 1)) {
|
||||
if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) {
|
||||
pwr &= ~DPCD_SC00_SET_POWER;
|
||||
pwr |= DPCD_SC00_SET_POWER_D0;
|
||||
nvkm_wraux(outp->dp.aux, DPCD_SC00, &pwr, 1);
|
||||
}
|
||||
}
|
||||
mutex_lock(&outp->dp.mutex);
|
||||
OUTP_DBG(outp, "training");
|
||||
|
||||
ior->dp.mst = outp->dp.lt.mst;
|
||||
ior->dp.ef = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP;
|
||||
ior->dp.nr = 0;
|
||||
ior->dp.bw = outp->dp.lt.bw;
|
||||
ior->dp.nr = outp->dp.lt.nr;
|
||||
|
||||
/* Link training. */
|
||||
OUTP_DBG(outp, "training");
|
||||
nvkm_dp_train_init(outp);
|
||||
|
||||
/* Otherwise, loop through all valid link configurations that support the data rate. */
|
||||
for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) {
|
||||
for (rate = 0; ret < 0 && rate < outp->dp.rates; rate++) {
|
||||
if (outp->dp.rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) {
|
||||
/* Program selected link configuration. */
|
||||
ior->dp.bw = outp->dp.rate[rate].rate / 27000;
|
||||
ior->dp.nr = nr;
|
||||
ret = nvkm_dp_train_links(outp, rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish up. */
|
||||
ret = nvkm_dp_train_links(outp, rate);
|
||||
nvkm_dp_train_fini(outp);
|
||||
if (ret < 0)
|
||||
OUTP_ERR(outp, "training failed");
|
||||
else
|
||||
OUTP_DBG(outp, "training done");
|
||||
atomic_set(&outp->dp.lt.done, 1);
|
||||
|
||||
mutex_unlock(&outp->dp.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -537,69 +526,10 @@ nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior)
|
|||
static void
|
||||
nvkm_dp_release(struct nvkm_outp *outp)
|
||||
{
|
||||
/* Prevent link from being retrained if sink sends an IRQ. */
|
||||
atomic_set(&outp->dp.lt.done, 0);
|
||||
outp->ior->dp.nr = 0;
|
||||
}
|
||||
nvkm_dp_disable(outp, outp->ior);
|
||||
|
||||
static int
|
||||
nvkm_dp_acquire(struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_ior *ior = outp->ior;
|
||||
struct nvkm_head *head;
|
||||
bool retrain = true;
|
||||
u32 datakbps = 0;
|
||||
u32 dataKBps;
|
||||
u32 linkKBps;
|
||||
u8 stat[3];
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&outp->dp.mutex);
|
||||
|
||||
/* Check that link configuration meets current requirements. */
|
||||
list_for_each_entry(head, &outp->disp->heads, head) {
|
||||
if (ior->asy.head & (1 << head->id)) {
|
||||
u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000;
|
||||
datakbps += khz * head->asy.or.depth;
|
||||
}
|
||||
}
|
||||
|
||||
linkKBps = ior->dp.bw * 27000 * ior->dp.nr;
|
||||
dataKBps = DIV_ROUND_UP(datakbps, 8);
|
||||
OUTP_DBG(outp, "data %d KB/s link %d KB/s mst %d->%d",
|
||||
dataKBps, linkKBps, ior->dp.mst, outp->dp.lt.mst);
|
||||
if (linkKBps < dataKBps || ior->dp.mst != outp->dp.lt.mst) {
|
||||
OUTP_DBG(outp, "link requirements changed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check that link is still trained. */
|
||||
ret = nvkm_rdaux(outp->dp.aux, DPCD_LS02, stat, 3);
|
||||
if (ret) {
|
||||
OUTP_DBG(outp, "failed to read link status, assuming no sink");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) {
|
||||
for (i = 0; i < ior->dp.nr; i++) {
|
||||
u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f;
|
||||
if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
|
||||
!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
|
||||
!(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
|
||||
OUTP_DBG(outp, "lane %d not equalised", lane);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
retrain = false;
|
||||
} else {
|
||||
OUTP_DBG(outp, "no inter-lane alignment");
|
||||
}
|
||||
|
||||
done:
|
||||
if (retrain || !atomic_read(&outp->dp.lt.done))
|
||||
ret = nvkm_dp_train(outp, dataKBps);
|
||||
mutex_unlock(&outp->dp.mutex);
|
||||
return ret;
|
||||
nvkm_outp_release(outp);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -638,7 +568,6 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr)
|
|||
OUTP_DBG(outp, "aux power -> demand");
|
||||
nvkm_i2c_aux_monitor(aux, false);
|
||||
outp->dp.aux_pwr = false;
|
||||
atomic_set(&outp->dp.lt.done, 0);
|
||||
|
||||
/* Restore eDP panel GPIO to its prior state if we changed it, as
|
||||
* it could potentially interfere with other outputs.
|
||||
|
@ -677,14 +606,14 @@ nvkm_dp_func = {
|
|||
.fini = nvkm_dp_fini,
|
||||
.detect = nvkm_outp_detect,
|
||||
.inherit = nvkm_outp_inherit,
|
||||
.acquire = nvkm_dp_acquire,
|
||||
.acquire = nvkm_outp_acquire,
|
||||
.release = nvkm_dp_release,
|
||||
.disable = nvkm_dp_disable,
|
||||
.bl.get = nvkm_outp_bl_get,
|
||||
.bl.set = nvkm_outp_bl_set,
|
||||
.dp.aux_pwr = nvkm_dp_aux_pwr,
|
||||
.dp.aux_xfer = nvkm_dp_aux_xfer,
|
||||
.dp.train = nvkm_dp_train_,
|
||||
.dp.train = nvkm_dp_train,
|
||||
.dp.drive = nvkm_dp_drive,
|
||||
};
|
||||
|
||||
int
|
||||
|
@ -723,6 +652,5 @@ nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct n
|
|||
OUTP_DBG(outp, "bios dp %02x %02x %02x %02x", outp->dp.version, hdr, cnt, len);
|
||||
|
||||
mutex_init(&outp->dp.mutex);
|
||||
atomic_set(&outp->dp.lt.done, 0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1286,10 +1286,6 @@ nv50_disp_super_2_2(struct nvkm_disp *disp, struct nvkm_head *head)
|
|||
ior->asy.link = outp->lvds.dual ? 3 : 1;
|
||||
}
|
||||
|
||||
/* Handle any link training, etc. */
|
||||
if (outp && outp->func->acquire)
|
||||
outp->func->acquire(outp);
|
||||
|
||||
/* Execute OnInt2 IED script. */
|
||||
nv50_disp_super_ied_on(head, ior, 0, khz);
|
||||
|
||||
|
@ -1319,7 +1315,6 @@ nv50_disp_super_2_1(struct nvkm_disp *disp, struct nvkm_head *head)
|
|||
void
|
||||
nv50_disp_super_2_0(struct nvkm_disp *disp, struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_outp *outp;
|
||||
struct nvkm_ior *ior;
|
||||
|
||||
/* Determine which OR, if any, we're detaching from the head. */
|
||||
|
@ -1330,14 +1325,6 @@ nv50_disp_super_2_0(struct nvkm_disp *disp, struct nvkm_head *head)
|
|||
|
||||
/* Execute OffInt2 IED script. */
|
||||
nv50_disp_super_ied_off(head, ior, 2);
|
||||
|
||||
/* If we're shutting down the OR's only active head, execute
|
||||
* the output path's disable function.
|
||||
*/
|
||||
if (ior->arm.head == (1 << head->id)) {
|
||||
if ((outp = ior->arm.outp) && outp->func->disable)
|
||||
outp->func->disable(outp, ior);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include <subdev/gpio.h>
|
||||
#include <subdev/i2c.h>
|
||||
|
||||
void
|
||||
static void
|
||||
nvkm_outp_route(struct nvkm_disp *disp)
|
||||
{
|
||||
struct nvkm_outp *outp;
|
||||
|
@ -96,8 +96,6 @@ nvkm_outp_release_or(struct nvkm_outp *outp, u8 user)
|
|||
if (ior) {
|
||||
outp->acquired &= ~user;
|
||||
if (!outp->acquired) {
|
||||
if (outp->func->release && outp->ior)
|
||||
outp->func->release(outp);
|
||||
outp->ior->asy.outp = NULL;
|
||||
outp->ior = NULL;
|
||||
}
|
||||
|
@ -277,6 +275,18 @@ nvkm_outp_release(struct nvkm_outp *outp)
|
|||
nvkm_outp_route(outp->disp);
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_outp_acquire(struct nvkm_outp *outp, bool hda)
|
||||
{
|
||||
int ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_USER, hda);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvkm_outp_route(outp->disp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_outp_fini(struct nvkm_outp *outp)
|
||||
{
|
||||
|
@ -412,6 +422,8 @@ static const struct nvkm_outp_func
|
|||
nvkm_outp = {
|
||||
.detect = nvkm_outp_detect,
|
||||
.inherit = nvkm_outp_inherit,
|
||||
.acquire = nvkm_outp_acquire,
|
||||
.release = nvkm_outp_release,
|
||||
.bl.get = nvkm_outp_bl_get,
|
||||
.bl.set = nvkm_outp_bl_set,
|
||||
};
|
||||
|
|
|
@ -50,11 +50,9 @@ struct nvkm_outp {
|
|||
u32 rate;
|
||||
} rate[8];
|
||||
int rates;
|
||||
int links;
|
||||
|
||||
struct mutex mutex;
|
||||
struct {
|
||||
atomic_t done;
|
||||
u8 nr;
|
||||
u8 bw;
|
||||
bool mst;
|
||||
|
@ -79,11 +77,11 @@ void nvkm_outp_fini(struct nvkm_outp *);
|
|||
int nvkm_outp_detect(struct nvkm_outp *);
|
||||
|
||||
struct nvkm_ior *nvkm_outp_inherit(struct nvkm_outp *);
|
||||
int nvkm_outp_acquire(struct nvkm_outp *, bool hda);
|
||||
int nvkm_outp_acquire_or(struct nvkm_outp *, u8 user, bool hda);
|
||||
int nvkm_outp_acquire_ior(struct nvkm_outp *, u8 user, struct nvkm_ior *);
|
||||
void nvkm_outp_release(struct nvkm_outp *);
|
||||
void nvkm_outp_release_or(struct nvkm_outp *, u8 user);
|
||||
void nvkm_outp_route(struct nvkm_disp *);
|
||||
|
||||
int nvkm_outp_bl_get(struct nvkm_outp *);
|
||||
int nvkm_outp_bl_set(struct nvkm_outp *, int level);
|
||||
|
@ -97,9 +95,8 @@ struct nvkm_outp_func {
|
|||
int (*edid_get)(struct nvkm_outp *, u8 *data, u16 *size);
|
||||
|
||||
struct nvkm_ior *(*inherit)(struct nvkm_outp *);
|
||||
int (*acquire)(struct nvkm_outp *);
|
||||
int (*acquire)(struct nvkm_outp *, bool hda);
|
||||
void (*release)(struct nvkm_outp *);
|
||||
void (*disable)(struct nvkm_outp *, struct nvkm_ior *);
|
||||
|
||||
struct {
|
||||
int (*get)(struct nvkm_outp *);
|
||||
|
@ -111,6 +108,7 @@ struct nvkm_outp_func {
|
|||
int (*aux_xfer)(struct nvkm_outp *, u8 type, u32 addr, u8 *data, u8 *size);
|
||||
int (*rates)(struct nvkm_outp *);
|
||||
int (*train)(struct nvkm_outp *, bool retrain);
|
||||
int (*drive)(struct nvkm_outp *, u8 lanes, u8 pe[4], u8 vs[4]);
|
||||
} dp;
|
||||
};
|
||||
|
||||
|
|
|
@ -45,6 +45,19 @@ nvkm_uoutp_mthd_dp_mst_vcpi(struct nvkm_outp *outp, void *argv, u32 argc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_uoutp_mthd_dp_drive(struct nvkm_outp *outp, void *argv, u32 argc)
|
||||
{
|
||||
union nvif_outp_dp_drive_args *args = argv;
|
||||
|
||||
if (argc != sizeof(args->v0) || args->v0.version != 0)
|
||||
return -ENOSYS;
|
||||
if (!outp->func->dp.drive)
|
||||
return -EINVAL;
|
||||
|
||||
return outp->func->dp.drive(outp, args->v0.lanes, args->v0.pe, args->v0.vs);
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_uoutp_mthd_dp_train(struct nvkm_outp *outp, void *argv, u32 argc)
|
||||
{
|
||||
|
@ -58,9 +71,8 @@ nvkm_uoutp_mthd_dp_train(struct nvkm_outp *outp, void *argv, u32 argc)
|
|||
if (!args->v0.retrain) {
|
||||
memcpy(outp->dp.dpcd, args->v0.dpcd, sizeof(outp->dp.dpcd));
|
||||
outp->dp.lttprs = args->v0.lttprs;
|
||||
outp->dp.links = args->v0.link_nr;
|
||||
outp->dp.lt.nr = 0;
|
||||
outp->dp.lt.bw = 0;
|
||||
outp->dp.lt.nr = args->v0.link_nr;
|
||||
outp->dp.lt.bw = args->v0.link_bw / 27000;
|
||||
outp->dp.lt.mst = args->v0.mst;
|
||||
outp->dp.lt.post_adj = args->v0.post_lt_adj;
|
||||
}
|
||||
|
@ -279,7 +291,7 @@ nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc)
|
|||
if (!outp->ior)
|
||||
return -EINVAL;
|
||||
|
||||
nvkm_outp_release(outp);
|
||||
outp->func->release(outp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -297,10 +309,10 @@ nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc)
|
|||
switch (args->v0.type) {
|
||||
case NVIF_OUTP_ACQUIRE_V0_DAC:
|
||||
case NVIF_OUTP_ACQUIRE_V0_PIOR:
|
||||
ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_USER, false);
|
||||
ret = outp->func->acquire(outp, false);
|
||||
break;
|
||||
case NVIF_OUTP_ACQUIRE_V0_SOR:
|
||||
ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_USER, args->v0.sor.hda);
|
||||
ret = outp->func->acquire(outp, args->v0.sor.hda);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -310,8 +322,6 @@ nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvkm_outp_route(outp->disp);
|
||||
|
||||
args->v0.or = outp->ior->id;
|
||||
args->v0.link = outp->ior->asy.link;
|
||||
return 0;
|
||||
|
@ -450,6 +460,7 @@ nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
|
|||
case NVIF_OUTP_V0_INFOFRAME : return nvkm_uoutp_mthd_infoframe (outp, argv, argc);
|
||||
case NVIF_OUTP_V0_HDA_ELD : return nvkm_uoutp_mthd_hda_eld (outp, argv, argc);
|
||||
case NVIF_OUTP_V0_DP_TRAIN : return nvkm_uoutp_mthd_dp_train (outp, argv, argc);
|
||||
case NVIF_OUTP_V0_DP_DRIVE : return nvkm_uoutp_mthd_dp_drive (outp, argv, argc);
|
||||
case NVIF_OUTP_V0_DP_MST_VCPI : return nvkm_uoutp_mthd_dp_mst_vcpi (outp, argv, argc);
|
||||
default:
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue
Block a user