drm/nouveau/disp/dp: make use of existing output data for link training
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Родитель
2bd651ea43
Коммит
3b52a1f906
|
@ -33,24 +33,13 @@
|
|||
#include <core/class.h>
|
||||
|
||||
#include "dport.h"
|
||||
|
||||
#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt, \
|
||||
dp->outp->hasht, dp->outp->hashm, ##args)
|
||||
#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt, \
|
||||
dp->outp->hasht, dp->outp->hashm, ##args)
|
||||
#include "outpdp.h"
|
||||
|
||||
/******************************************************************************
|
||||
* link training
|
||||
*****************************************************************************/
|
||||
struct dp_state {
|
||||
const struct nouveau_dp_func *func;
|
||||
struct nouveau_disp *disp;
|
||||
struct dcb_output *outp;
|
||||
struct nvbios_dpout info;
|
||||
u8 version;
|
||||
struct nouveau_i2c_port *aux;
|
||||
int head;
|
||||
u8 dpcd[16];
|
||||
struct nvkm_output_dp *outp;
|
||||
int link_nr;
|
||||
u32 link_bw;
|
||||
u8 stat[6];
|
||||
|
@ -63,14 +52,16 @@ struct dp_state {
|
|||
static int
|
||||
dp_set_link_config(struct dp_state *dp)
|
||||
{
|
||||
struct nouveau_disp *disp = dp->disp;
|
||||
struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
struct nouveau_disp *disp = nouveau_disp(outp);
|
||||
struct nouveau_bios *bios = nouveau_bios(disp);
|
||||
struct nvbios_init init = {
|
||||
.subdev = nv_subdev(dp->disp),
|
||||
.subdev = nv_subdev(disp),
|
||||
.bios = bios,
|
||||
.offset = 0x0000,
|
||||
.outp = dp->outp,
|
||||
.crtc = dp->head,
|
||||
.outp = &outp->base.info,
|
||||
.crtc = -1,
|
||||
.execute = 1,
|
||||
};
|
||||
u32 lnkcmp;
|
||||
|
@ -80,8 +71,8 @@ dp_set_link_config(struct dp_state *dp)
|
|||
DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
|
||||
|
||||
/* set desired link configuration on the source */
|
||||
if ((lnkcmp = dp->info.lnkcmp)) {
|
||||
if (dp->version < 0x30) {
|
||||
if ((lnkcmp = dp->outp->info.lnkcmp)) {
|
||||
if (outp->version < 0x30) {
|
||||
while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp))
|
||||
lnkcmp += 4;
|
||||
init.offset = nv_ro16(bios, lnkcmp + 2);
|
||||
|
@ -94,43 +85,45 @@ dp_set_link_config(struct dp_state *dp)
|
|||
nvbios_exec(&init);
|
||||
}
|
||||
|
||||
ret = dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
|
||||
dp->link_nr, dp->link_bw / 27000,
|
||||
dp->dpcd[DPCD_RC02] &
|
||||
DPCD_RC02_ENHANCED_FRAME_CAP);
|
||||
ret = impl->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000,
|
||||
outp->dpcd[DPCD_RC02] &
|
||||
DPCD_RC02_ENHANCED_FRAME_CAP);
|
||||
if (ret) {
|
||||
ERR("lnk_ctl failed with %d\n", ret);
|
||||
if (ret < 0)
|
||||
ERR("lnk_ctl failed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set desired link configuration on the sink */
|
||||
sink[0] = dp->link_bw / 27000;
|
||||
sink[1] = dp->link_nr;
|
||||
if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
|
||||
if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
|
||||
sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
|
||||
|
||||
return nv_wraux(dp->aux, DPCD_LC00, sink, 2);
|
||||
return nv_wraux(outp->base.edid, DPCD_LC00, sink, 2);
|
||||
}
|
||||
|
||||
static void
|
||||
dp_set_training_pattern(struct dp_state *dp, u8 pattern)
|
||||
{
|
||||
struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
u8 sink_tp;
|
||||
|
||||
DBG("training pattern %d\n", pattern);
|
||||
dp->func->pattern(dp->disp, dp->outp, dp->head, pattern);
|
||||
impl->pattern(outp, pattern);
|
||||
|
||||
nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
|
||||
nv_rdaux(outp->base.edid, DPCD_LC02, &sink_tp, 1);
|
||||
sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
|
||||
sink_tp |= pattern;
|
||||
nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
|
||||
nv_wraux(outp->base.edid, DPCD_LC02, &sink_tp, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
dp_link_train_commit(struct dp_state *dp, bool pc)
|
||||
{
|
||||
const struct nouveau_dp_func *func = dp->func;
|
||||
struct nouveau_disp *disp = dp->disp;
|
||||
struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < dp->link_nr; i++) {
|
||||
|
@ -146,15 +139,15 @@ dp_link_train_commit(struct dp_state *dp, bool pc)
|
|||
dp->pc2conf[i >> 1] |= 4 << ((i & 1) * 4);
|
||||
|
||||
DBG("config lane %d %02x\n", i, dp->conf[i]);
|
||||
func->drv_ctl(disp, dp->outp, dp->head, i, lvsw, lpre);
|
||||
impl->drv_ctl(outp, i, lvsw, lpre, 0);
|
||||
}
|
||||
|
||||
ret = nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4);
|
||||
ret = nv_wraux(outp->base.edid, DPCD_LC03(0), dp->conf, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pc) {
|
||||
ret = nv_wraux(dp->aux, DPCD_LC0F, dp->pc2conf, 2);
|
||||
ret = nv_wraux(outp->base.edid, DPCD_LC0F, dp->pc2conf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -165,19 +158,20 @@ dp_link_train_commit(struct dp_state *dp, bool pc)
|
|||
static int
|
||||
dp_link_train_update(struct dp_state *dp, bool pc, u32 delay)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
int ret;
|
||||
|
||||
if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
|
||||
mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
|
||||
if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
|
||||
mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
|
||||
else
|
||||
udelay(delay);
|
||||
|
||||
ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6);
|
||||
ret = nv_rdaux(outp->base.edid, DPCD_LS02, dp->stat, 6);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pc) {
|
||||
ret = nv_rdaux(dp->aux, DPCD_LS0C, &dp->pc2stat, 1);
|
||||
ret = nv_rdaux(outp->base.edid, DPCD_LS0C, &dp->pc2stat, 1);
|
||||
if (ret)
|
||||
dp->pc2stat = 0x00;
|
||||
DBG("status %6ph pc2 %02x\n", dp->stat, dp->pc2stat);
|
||||
|
@ -225,10 +219,11 @@ dp_link_train_cr(struct dp_state *dp)
|
|||
static int
|
||||
dp_link_train_eq(struct dp_state *dp)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
bool eq_done = false, cr_done = true;
|
||||
int tries = 0, i;
|
||||
|
||||
if (dp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED)
|
||||
if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED)
|
||||
dp_set_training_pattern(dp, 3);
|
||||
else
|
||||
dp_set_training_pattern(dp, 2);
|
||||
|
@ -257,39 +252,45 @@ dp_link_train_eq(struct dp_state *dp)
|
|||
static void
|
||||
dp_link_train_init(struct dp_state *dp, bool spread)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
struct nouveau_disp *disp = nouveau_disp(outp);
|
||||
struct nouveau_bios *bios = nouveau_bios(disp);
|
||||
struct nvbios_init init = {
|
||||
.subdev = nv_subdev(dp->disp),
|
||||
.bios = nouveau_bios(dp->disp),
|
||||
.outp = dp->outp,
|
||||
.crtc = dp->head,
|
||||
.subdev = nv_subdev(disp),
|
||||
.bios = bios,
|
||||
.outp = &outp->base.info,
|
||||
.crtc = -1,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
/* set desired spread */
|
||||
if (spread)
|
||||
init.offset = dp->info.script[2];
|
||||
init.offset = outp->info.script[2];
|
||||
else
|
||||
init.offset = dp->info.script[3];
|
||||
init.offset = outp->info.script[3];
|
||||
nvbios_exec(&init);
|
||||
|
||||
/* pre-train script */
|
||||
init.offset = dp->info.script[0];
|
||||
init.offset = outp->info.script[0];
|
||||
nvbios_exec(&init);
|
||||
}
|
||||
|
||||
static void
|
||||
dp_link_train_fini(struct dp_state *dp)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
struct nouveau_disp *disp = nouveau_disp(outp);
|
||||
struct nouveau_bios *bios = nouveau_bios(disp);
|
||||
struct nvbios_init init = {
|
||||
.subdev = nv_subdev(dp->disp),
|
||||
.bios = nouveau_bios(dp->disp),
|
||||
.outp = dp->outp,
|
||||
.crtc = dp->head,
|
||||
.subdev = nv_subdev(disp),
|
||||
.bios = bios,
|
||||
.outp = &outp->base.info,
|
||||
.crtc = -1,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
/* post-train script */
|
||||
init.offset = dp->info.script[1],
|
||||
init.offset = outp->info.script[1],
|
||||
nvbios_exec(&init);
|
||||
}
|
||||
|
||||
|
@ -311,65 +312,25 @@ static const struct dp_rates {
|
|||
};
|
||||
|
||||
int
|
||||
nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
|
||||
struct dcb_output *outp, int head, u32 datarate)
|
||||
nouveau_dp_train(struct nvkm_output_dp *outp, u32 datarate)
|
||||
{
|
||||
struct nouveau_bios *bios = nouveau_bios(disp);
|
||||
struct nouveau_i2c *i2c = nouveau_i2c(disp);
|
||||
struct nouveau_disp *disp = nouveau_disp(outp);
|
||||
const struct dp_rates *cfg = nouveau_dp_rates;
|
||||
struct dp_state _dp = {
|
||||
.disp = disp,
|
||||
.func = func,
|
||||
.outp = outp,
|
||||
.head = head,
|
||||
}, *dp = &_dp;
|
||||
u8 hdr, cnt, len;
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
/* find the bios displayport data relevant to this output */
|
||||
data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version,
|
||||
&hdr, &cnt, &len, &dp->info);
|
||||
if (!data) {
|
||||
ERR("bios data not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* acquire the aux channel and fetch some info about the display */
|
||||
if (outp->location)
|
||||
dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
|
||||
else
|
||||
dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index));
|
||||
if (!dp->aux) {
|
||||
ERR("no aux channel?!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd));
|
||||
if (ret) {
|
||||
/* it's possible the display has been unplugged before we
|
||||
* get here. we still need to execute the full set of
|
||||
* vbios scripts, and program the OR at a high enough
|
||||
* frequency to satisfy the target mode. failure to do
|
||||
* so results at best in an UPDATE hanging, and at worst
|
||||
* with PDISP running away to join the circus.
|
||||
*/
|
||||
dp->dpcd[1] = dp->outp->dpconf.link_bw;
|
||||
dp->dpcd[2] = dp->outp->dpconf.link_nr;
|
||||
dp->dpcd[3] = 0x00;
|
||||
ERR("failed to read DPCD\n");
|
||||
}
|
||||
|
||||
/* bring capabilities within encoder limits */
|
||||
if (nv_mclass(disp) < NVD0_DISP_CLASS)
|
||||
dp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
|
||||
if ((dp->dpcd[2] & 0x1f) > dp->outp->dpconf.link_nr) {
|
||||
dp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
|
||||
dp->dpcd[2] |= dp->outp->dpconf.link_nr;
|
||||
outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
|
||||
if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) {
|
||||
outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
|
||||
outp->dpcd[2] |= outp->base.info.dpconf.link_nr;
|
||||
}
|
||||
if (dp->dpcd[1] > dp->outp->dpconf.link_bw)
|
||||
dp->dpcd[1] = dp->outp->dpconf.link_bw;
|
||||
dp->pc2 = dp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED;
|
||||
if (outp->dpcd[1] > outp->base.info.dpconf.link_bw)
|
||||
outp->dpcd[1] = outp->base.info.dpconf.link_bw;
|
||||
dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED;
|
||||
|
||||
/* restrict link config to the lowest required rate, if requested */
|
||||
if (datarate) {
|
||||
|
@ -380,12 +341,12 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
|
|||
cfg--;
|
||||
|
||||
/* enable down-spreading and execute pre-train script from vbios */
|
||||
dp_link_train_init(dp, dp->dpcd[3] & 0x01);
|
||||
dp_link_train_init(dp, outp->dpcd[3] & 0x01);
|
||||
|
||||
while (ret = -EIO, (++cfg)->rate) {
|
||||
/* select next configuration supported by encoder and sink */
|
||||
while (cfg->nr > (dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) ||
|
||||
cfg->bw > (dp->dpcd[DPCD_RC01_MAX_LINK_RATE]))
|
||||
while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) ||
|
||||
cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE]))
|
||||
cfg++;
|
||||
dp->link_bw = cfg->bw * 27000;
|
||||
dp->link_nr = cfg->nr;
|
||||
|
|
|
@ -71,23 +71,4 @@
|
|||
#define DPCD_LS0C_LANE1_POST_CURSOR2 0x0c
|
||||
#define DPCD_LS0C_LANE0_POST_CURSOR2 0x03
|
||||
|
||||
struct nouveau_disp;
|
||||
struct dcb_output;
|
||||
|
||||
struct nouveau_dp_func {
|
||||
int (*pattern)(struct nouveau_disp *, struct dcb_output *,
|
||||
int head, int pattern);
|
||||
int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
|
||||
int link_nr, int link_bw, bool enh_frame);
|
||||
int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
|
||||
int lane, int swing, int preem);
|
||||
};
|
||||
|
||||
extern const struct nouveau_dp_func nv94_sor_dp_func;
|
||||
extern const struct nouveau_dp_func nvd0_sor_dp_func;
|
||||
extern const struct nouveau_dp_func nv50_pior_dp_func;
|
||||
|
||||
int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *,
|
||||
struct dcb_output *, int, u32);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -81,7 +81,6 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|||
priv->sor.power = nv50_sor_power;
|
||||
priv->sor.hda_eld = nvd0_hda_eld;
|
||||
priv->sor.hdmi = nvd0_hdmi_ctrl;
|
||||
priv->sor.dp = &nvd0_sor_dp_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1471,8 +1471,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
|
|||
break;
|
||||
}
|
||||
|
||||
nouveau_dp_train(&priv->base, priv->sor.dp,
|
||||
&outp->info, head, datarate);
|
||||
nouveau_dp_train((void *)outp, datarate);
|
||||
}
|
||||
|
||||
exec_clkcmp(priv, head, 0, pclk, &conf);
|
||||
|
@ -1551,8 +1550,7 @@ nv50_disp_intr_unk40_0(struct nv50_disp_priv *priv, int head)
|
|||
break;
|
||||
}
|
||||
|
||||
nouveau_dp_train(&priv->base, priv->pior.dp,
|
||||
&outp->info, head, datarate);
|
||||
nouveau_dp_train((void *)outp, datarate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1665,7 +1663,6 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|||
priv->dac.sense = nv50_dac_sense;
|
||||
priv->sor.power = nv50_sor_power;
|
||||
priv->pior.power = nv50_pior_power;
|
||||
priv->pior.dp = &nv50_pior_dp_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,13 +45,11 @@ struct nv50_disp_priv {
|
|||
int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32);
|
||||
int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32);
|
||||
u32 lvdsconf;
|
||||
const struct nouveau_dp_func *dp;
|
||||
} sor;
|
||||
struct {
|
||||
int nr;
|
||||
int (*power)(struct nv50_disp_priv *, int ext, u32 data);
|
||||
u8 type[3];
|
||||
const struct nouveau_dp_func *dp;
|
||||
} pior;
|
||||
};
|
||||
|
||||
|
|
|
@ -264,7 +264,6 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|||
priv->sor.power = nv50_sor_power;
|
||||
priv->sor.hdmi = nv84_hdmi_ctrl;
|
||||
priv->pior.power = nv50_pior_power;
|
||||
priv->pior.dp = &nv50_pior_dp_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,9 +122,7 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|||
priv->dac.sense = nv50_dac_sense;
|
||||
priv->sor.power = nv50_sor_power;
|
||||
priv->sor.hdmi = nv84_hdmi_ctrl;
|
||||
priv->sor.dp = &nv94_sor_dp_func;
|
||||
priv->pior.power = nv50_pior_power;
|
||||
priv->pior.dp = &nv50_pior_dp_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -126,7 +126,6 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|||
priv->sor.power = nv50_sor_power;
|
||||
priv->sor.hdmi = nv84_hdmi_ctrl;
|
||||
priv->pior.power = nv50_pior_power;
|
||||
priv->pior.dp = &nv50_pior_dp_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -96,9 +96,7 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|||
priv->sor.power = nv50_sor_power;
|
||||
priv->sor.hda_eld = nva3_hda_eld;
|
||||
priv->sor.hdmi = nva3_hdmi_ctrl;
|
||||
priv->sor.dp = &nv94_sor_dp_func;
|
||||
priv->pior.power = nv50_pior_power;
|
||||
priv->pior.dp = &nv50_pior_dp_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1149,8 +1149,7 @@ nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head)
|
|||
break;
|
||||
}
|
||||
|
||||
nouveau_dp_train(&priv->base, priv->sor.dp,
|
||||
&outp->info, head, pclk);
|
||||
nouveau_dp_train((void *)outp, pclk);
|
||||
}
|
||||
|
||||
exec_clkcmp(priv, head, 0, pclk, &conf);
|
||||
|
@ -1360,7 +1359,6 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|||
priv->sor.power = nv50_sor_power;
|
||||
priv->sor.hda_eld = nvd0_hda_eld;
|
||||
priv->sor.hdmi = nvd0_hdmi_ctrl;
|
||||
priv->sor.dp = &nvd0_sor_dp_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -246,7 +246,6 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|||
priv->sor.power = nv50_sor_power;
|
||||
priv->sor.hda_eld = nvd0_hda_eld;
|
||||
priv->sor.hdmi = nvd0_hdmi_ctrl;
|
||||
priv->sor.dp = &nvd0_sor_dp_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,6 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|||
priv->sor.power = nv50_sor_power;
|
||||
priv->sor.hda_eld = nvd0_hda_eld;
|
||||
priv->sor.hdmi = nvd0_hdmi_ctrl;
|
||||
priv->sor.dp = &nvd0_sor_dp_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,11 @@ int _nvkm_output_dp_fini(struct nouveau_object *, bool);
|
|||
|
||||
struct nvkm_output_dp_impl {
|
||||
struct nvkm_output_impl base;
|
||||
int (*pattern)(struct nvkm_output_dp *, int);
|
||||
int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef);
|
||||
int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc);
|
||||
};
|
||||
|
||||
int nouveau_dp_train(struct nvkm_output_dp *, u32 rate);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -70,70 +70,33 @@ nv50_pior_tmds_impl = {
|
|||
* DisplayPort
|
||||
*****************************************************************************/
|
||||
|
||||
static struct nouveau_i2c_port *
|
||||
nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp)
|
||||
static int
|
||||
nv50_pior_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||
{
|
||||
struct nouveau_i2c *i2c = nouveau_i2c(disp);
|
||||
return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
|
||||
struct nouveau_i2c_port *port = outp->base.edid;
|
||||
if (port && port->func->pattern)
|
||||
return port->func->pattern(port, pattern);
|
||||
return port ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_pior_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int pattern)
|
||||
nv50_pior_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
||||
{
|
||||
struct nouveau_i2c_port *port;
|
||||
int ret = -EINVAL;
|
||||
|
||||
port = nv50_pior_dp_find(disp, outp);
|
||||
if (port) {
|
||||
if (port->func->pattern)
|
||||
ret = port->func->pattern(port, pattern);
|
||||
else
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_pior_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int lane_nr, int link_bw, bool enh)
|
||||
{
|
||||
struct nouveau_i2c_port *port;
|
||||
int ret = -EINVAL;
|
||||
|
||||
port = nv50_pior_dp_find(disp, outp);
|
||||
struct nouveau_i2c_port *port = outp->base.edid;
|
||||
if (port && port->func->lnk_ctl)
|
||||
ret = port->func->lnk_ctl(port, lane_nr, link_bw, enh);
|
||||
|
||||
return ret;
|
||||
return port->func->lnk_ctl(port, nr, bw, ef);
|
||||
return port ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_pior_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int lane, int vsw, int pre)
|
||||
nv50_pior_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
||||
{
|
||||
struct nouveau_i2c_port *port;
|
||||
int ret = -EINVAL;
|
||||
|
||||
port = nv50_pior_dp_find(disp, outp);
|
||||
if (port) {
|
||||
if (port->func->drv_ctl)
|
||||
ret = port->func->drv_ctl(port, lane, vsw, pre);
|
||||
else
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
struct nouveau_i2c_port *port = outp->base.edid;
|
||||
if (port && port->func->drv_ctl)
|
||||
return port->func->drv_ctl(port, ln, vs, pe);
|
||||
return port ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
const struct nouveau_dp_func
|
||||
nv50_pior_dp_func = {
|
||||
.pattern = nv50_pior_dp_pattern,
|
||||
.lnk_ctl = nv50_pior_dp_lnk_ctl,
|
||||
.drv_ctl = nv50_pior_dp_drv_ctl,
|
||||
};
|
||||
|
||||
static int
|
||||
nv50_pior_dp_ctor(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
|
@ -163,6 +126,9 @@ nv50_pior_dp_impl = {
|
|||
.init = _nvkm_output_dp_init,
|
||||
.fini = _nvkm_output_dp_fini,
|
||||
},
|
||||
.pattern = nv50_pior_dp_pattern,
|
||||
.lnk_ctl = nv50_pior_dp_lnk_ctl,
|
||||
.drv_ctl = nv50_pior_dp_drv_ctl,
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
|
|
|
@ -34,15 +34,15 @@
|
|||
#include "outpdp.h"
|
||||
|
||||
static inline u32
|
||||
nv94_sor_soff(struct dcb_output *outp)
|
||||
nv94_sor_soff(struct nvkm_output_dp *outp)
|
||||
{
|
||||
return (ffs(outp->or) - 1) * 0x800;
|
||||
return (ffs(outp->base.info.or) - 1) * 0x800;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
nv94_sor_loff(struct dcb_output *outp)
|
||||
nv94_sor_loff(struct nvkm_output_dp *outp)
|
||||
{
|
||||
return nv94_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
|
||||
return nv94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
|
@ -56,20 +56,18 @@ nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
|
|||
}
|
||||
|
||||
static int
|
||||
nv94_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int pattern)
|
||||
nv94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||
{
|
||||
struct nv50_disp_priv *priv = (void *)disp;
|
||||
struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
|
||||
const u32 loff = nv94_sor_loff(outp);
|
||||
nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int link_nr, int link_bw, bool enh_frame)
|
||||
nv94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
||||
{
|
||||
struct nv50_disp_priv *priv = (void *)disp;
|
||||
struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
|
||||
const u32 soff = nv94_sor_soff(outp);
|
||||
const u32 loff = nv94_sor_loff(outp);
|
||||
u32 dpctrl = 0x00000000;
|
||||
|
@ -77,13 +75,13 @@ nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
|||
u32 lane = 0;
|
||||
int i;
|
||||
|
||||
dpctrl |= ((1 << link_nr) - 1) << 16;
|
||||
if (enh_frame)
|
||||
dpctrl |= ((1 << nr) - 1) << 16;
|
||||
if (ef)
|
||||
dpctrl |= 0x00004000;
|
||||
if (link_bw > 0x06)
|
||||
if (bw > 0x06)
|
||||
clksor |= 0x00040000;
|
||||
|
||||
for (i = 0; i < link_nr; i++)
|
||||
for (i = 0; i < nr; i++)
|
||||
lane |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3);
|
||||
|
||||
nv_mask(priv, 0x614300 + soff, 0x000c0000, clksor);
|
||||
|
@ -93,24 +91,24 @@ nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
|||
}
|
||||
|
||||
static int
|
||||
nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int lane, int swing, int preem)
|
||||
nv94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
||||
{
|
||||
struct nouveau_bios *bios = nouveau_bios(disp);
|
||||
struct nv50_disp_priv *priv = (void *)disp;
|
||||
const u32 shift = nv94_sor_dp_lane_map(priv, lane);
|
||||
struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
|
||||
struct nouveau_bios *bios = nouveau_bios(priv);
|
||||
const u32 shift = nv94_sor_dp_lane_map(priv, ln);
|
||||
const u32 loff = nv94_sor_loff(outp);
|
||||
u32 addr, data[3];
|
||||
u8 ver, hdr, cnt, len;
|
||||
struct nvbios_dpout info;
|
||||
struct nvbios_dpcfg ocfg;
|
||||
|
||||
addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
|
||||
addr = nvbios_dpout_match(bios, outp->base.info.hasht,
|
||||
outp->base.info.hashm,
|
||||
&ver, &hdr, &cnt, &len, &info);
|
||||
if (!addr)
|
||||
return -ENODEV;
|
||||
|
||||
addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
|
||||
addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
|
||||
&ver, &hdr, &cnt, &len, &ocfg);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
@ -124,13 +122,6 @@ nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
const struct nouveau_dp_func
|
||||
nv94_sor_dp_func = {
|
||||
.pattern = nv94_sor_dp_pattern,
|
||||
.lnk_ctl = nv94_sor_dp_lnk_ctl,
|
||||
.drv_ctl = nv94_sor_dp_drv_ctl,
|
||||
};
|
||||
|
||||
struct nvkm_output_dp_impl
|
||||
nv94_sor_dp_impl = {
|
||||
.base.base.handle = DCB_OUTPUT_DP,
|
||||
|
@ -140,4 +131,7 @@ nv94_sor_dp_impl = {
|
|||
.init = _nvkm_output_dp_init,
|
||||
.fini = _nvkm_output_dp_fini,
|
||||
},
|
||||
.pattern = nv94_sor_dp_pattern,
|
||||
.lnk_ctl = nv94_sor_dp_lnk_ctl,
|
||||
.drv_ctl = nv94_sor_dp_drv_ctl,
|
||||
};
|
||||
|
|
|
@ -33,15 +33,15 @@
|
|||
#include "nv50.h"
|
||||
|
||||
static inline u32
|
||||
nvd0_sor_soff(struct dcb_output *outp)
|
||||
nvd0_sor_soff(struct nvkm_output_dp *outp)
|
||||
{
|
||||
return (ffs(outp->or) - 1) * 0x800;
|
||||
return (ffs(outp->base.info.or) - 1) * 0x800;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
nvd0_sor_loff(struct dcb_output *outp)
|
||||
nvd0_sor_loff(struct nvkm_output_dp *outp)
|
||||
{
|
||||
return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
|
||||
return nvd0_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
|
@ -52,20 +52,18 @@ nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
|
|||
}
|
||||
|
||||
static int
|
||||
nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int pattern)
|
||||
nvd0_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||
{
|
||||
struct nv50_disp_priv *priv = (void *)disp;
|
||||
struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
|
||||
const u32 loff = nvd0_sor_loff(outp);
|
||||
nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int link_nr, int link_bw, bool enh_frame)
|
||||
nvd0_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
||||
{
|
||||
struct nv50_disp_priv *priv = (void *)disp;
|
||||
struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
|
||||
const u32 soff = nvd0_sor_soff(outp);
|
||||
const u32 loff = nvd0_sor_loff(outp);
|
||||
u32 dpctrl = 0x00000000;
|
||||
|
@ -73,12 +71,12 @@ nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
|||
u32 lane = 0;
|
||||
int i;
|
||||
|
||||
clksor |= link_bw << 18;
|
||||
dpctrl |= ((1 << link_nr) - 1) << 16;
|
||||
if (enh_frame)
|
||||
clksor |= bw << 18;
|
||||
dpctrl |= ((1 << nr) - 1) << 16;
|
||||
if (ef)
|
||||
dpctrl |= 0x00004000;
|
||||
|
||||
for (i = 0; i < link_nr; i++)
|
||||
for (i = 0; i < nr; i++)
|
||||
lane |= 1 << (nvd0_sor_dp_lane_map(priv, i) >> 3);
|
||||
|
||||
nv_mask(priv, 0x612300 + soff, 0x007c0000, clksor);
|
||||
|
@ -88,24 +86,24 @@ nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
|||
}
|
||||
|
||||
static int
|
||||
nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int lane, int swing, int preem)
|
||||
nvd0_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
||||
{
|
||||
struct nouveau_bios *bios = nouveau_bios(disp);
|
||||
struct nv50_disp_priv *priv = (void *)disp;
|
||||
const u32 shift = nvd0_sor_dp_lane_map(priv, lane);
|
||||
struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
|
||||
struct nouveau_bios *bios = nouveau_bios(priv);
|
||||
const u32 shift = nvd0_sor_dp_lane_map(priv, ln);
|
||||
const u32 loff = nvd0_sor_loff(outp);
|
||||
u32 addr, data[3];
|
||||
u8 ver, hdr, cnt, len;
|
||||
struct nvbios_dpout info;
|
||||
struct nvbios_dpcfg ocfg;
|
||||
|
||||
addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
|
||||
addr = nvbios_dpout_match(bios, outp->base.info.hasht,
|
||||
outp->base.info.hashm,
|
||||
&ver, &hdr, &cnt, &len, &info);
|
||||
if (!addr)
|
||||
return -ENODEV;
|
||||
|
||||
addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
|
||||
addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
|
||||
&ver, &hdr, &cnt, &len, &ocfg);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
@ -120,13 +118,6 @@ nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
const struct nouveau_dp_func
|
||||
nvd0_sor_dp_func = {
|
||||
.pattern = nvd0_sor_dp_pattern,
|
||||
.lnk_ctl = nvd0_sor_dp_lnk_ctl,
|
||||
.drv_ctl = nvd0_sor_dp_drv_ctl,
|
||||
};
|
||||
|
||||
struct nvkm_output_dp_impl
|
||||
nvd0_sor_dp_impl = {
|
||||
.base.base.handle = DCB_OUTPUT_DP,
|
||||
|
@ -136,4 +127,7 @@ nvd0_sor_dp_impl = {
|
|||
.init = _nvkm_output_dp_init,
|
||||
.fini = _nvkm_output_dp_fini,
|
||||
},
|
||||
.pattern = nvd0_sor_dp_pattern,
|
||||
.lnk_ctl = nvd0_sor_dp_lnk_ctl,
|
||||
.drv_ctl = nvd0_sor_dp_drv_ctl,
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче