Commit af85389c authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/disp: shuffle functions around

Upcoming changes to split OR from output path drastically change the
placement of various operations.

In order to make the real changes clearer, do the moving around part
ahead of time.
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 639d72e2
......@@ -12,9 +12,8 @@ nvkm-y += nvkm/engine/disp/gm107.o
nvkm-y += nvkm/engine/disp/gm200.o
nvkm-y += nvkm/engine/disp/gp100.o
nvkm-y += nvkm/engine/disp/gp102.o
nvkm-y += nvkm/engine/disp/vga.o
nvkm-y += nvkm/engine/disp/outp.o
nvkm-y += nvkm/engine/disp/outpdp.o
nvkm-y += nvkm/engine/disp/dacnv50.o
nvkm-y += nvkm/engine/disp/piornv50.o
nvkm-y += nvkm/engine/disp/sornv50.o
......@@ -22,20 +21,20 @@ nvkm-y += nvkm/engine/disp/sorg94.o
nvkm-y += nvkm/engine/disp/sorgf119.o
nvkm-y += nvkm/engine/disp/sorgm107.o
nvkm-y += nvkm/engine/disp/sorgm200.o
nvkm-y += nvkm/engine/disp/dport.o
nvkm-y += nvkm/engine/disp/conn.o
nvkm-y += nvkm/engine/disp/outp.o
nvkm-y += nvkm/engine/disp/dp.o
nvkm-y += nvkm/engine/disp/hdagt215.o
nvkm-y += nvkm/engine/disp/hdagf119.o
nvkm-y += nvkm/engine/disp/hdmi_infoframe.o
nvkm-y += nvkm/engine/disp/hdmi.o
nvkm-y += nvkm/engine/disp/hdmig84.o
nvkm-y += nvkm/engine/disp/hdmigt215.o
nvkm-y += nvkm/engine/disp/hdmigf119.o
nvkm-y += nvkm/engine/disp/hdmigk104.o
nvkm-y += nvkm/engine/disp/vga.o
nvkm-y += nvkm/engine/disp/conn.o
nvkm-y += nvkm/engine/disp/rootnv04.o
nvkm-y += nvkm/engine/disp/rootnv50.o
......
......@@ -30,40 +30,16 @@
#include <nvif/cl5070.h>
#include <nvif/unpack.h>
static const struct nvkm_output_func
nv50_dac_output_func = {
};
int
nv50_dac_power(NV50_DISP_MTHD_V1)
nv50_dac_output_new(struct nvkm_disp *disp, int index,
struct dcb_output *dcbE, struct nvkm_output **poutp)
{
struct nvkm_device *device = disp->base.engine.subdev.device;
const u32 doff = outp->or * 0x800;
union {
struct nv50_disp_dac_pwr_v0 v0;
} *args = data;
u32 stat;
int ret = -ENOSYS;
nvif_ioctl(object, "disp dac pwr size %d\n", size);
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(object, "disp dac pwr vers %d state %d data %d "
"vsync %d hsync %d\n",
args->v0.version, args->v0.state, args->v0.data,
args->v0.vsync, args->v0.hsync);
stat = 0x00000040 * !args->v0.state;
stat |= 0x00000010 * !args->v0.data;
stat |= 0x00000004 * !args->v0.vsync;
stat |= 0x00000001 * !args->v0.hsync;
} else
return ret;
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
break;
);
nvkm_mask(device, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat);
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
break;
);
return 0;
return nvkm_output_new_(&nv50_dac_output_func, disp,
index, dcbE, poutp);
}
int
......@@ -113,14 +89,38 @@ nv50_dac_sense(NV50_DISP_MTHD_V1)
return 0;
}
static const struct nvkm_output_func
nv50_dac_output_func = {
};
int
nv50_dac_output_new(struct nvkm_disp *disp, int index,
struct dcb_output *dcbE, struct nvkm_output **poutp)
nv50_dac_power(NV50_DISP_MTHD_V1)
{
return nvkm_output_new_(&nv50_dac_output_func, disp,
index, dcbE, poutp);
struct nvkm_device *device = disp->base.engine.subdev.device;
const u32 doff = outp->or * 0x800;
union {
struct nv50_disp_dac_pwr_v0 v0;
} *args = data;
u32 stat;
int ret = -ENOSYS;
nvif_ioctl(object, "disp dac pwr size %d\n", size);
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(object, "disp dac pwr vers %d state %d data %d "
"vsync %d hsync %d\n",
args->v0.version, args->v0.state, args->v0.data,
args->v0.vsync, args->v0.hsync);
stat = 0x00000040 * !args->v0.state;
stat |= 0x00000010 * !args->v0.data;
stat |= 0x00000004 * !args->v0.vsync;
stat |= 0x00000001 * !args->v0.hsync;
} else
return ret;
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
break;
);
nvkm_mask(device, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat);
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
break;
);
return 0;
}
......@@ -21,15 +21,370 @@
*
* Authors: Ben Skeggs
*/
#include "outpdp.h"
#include "dp.h"
#include "conn.h"
#include "dport.h"
#include "priv.h"
#include "nv50.h"
#include <subdev/bios.h>
#include <subdev/bios/init.h>
#include <subdev/i2c.h>
#include <nvif/event.h>
struct lt_state {
struct nvkm_output_dp *outp;
int link_nr;
u32 link_bw;
u8 stat[6];
u8 conf[4];
bool pc2;
u8 pc2stat;
u8 pc2conf[2];
};
static int
nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay)
{
struct nvkm_output_dp *outp = lt->outp;
int ret;
if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
else
udelay(delay);
ret = nvkm_rdaux(outp->aux, DPCD_LS02, lt->stat, 6);
if (ret)
return ret;
if (pc) {
ret = nvkm_rdaux(outp->aux, DPCD_LS0C, &lt->pc2stat, 1);
if (ret)
lt->pc2stat = 0x00;
OUTP_DBG(&outp->base, "status %6ph pc2 %02x",
lt->stat, lt->pc2stat);
} else {
OUTP_DBG(&outp->base, "status %6ph", lt->stat);
}
return 0;
}
static int
nvkm_dp_train_drive(struct lt_state *lt, bool pc)
{
struct nvkm_output_dp *outp = lt->outp;
int ret, i;
for (i = 0; i < lt->link_nr; i++) {
u8 lane = (lt->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
u8 lpc2 = (lt->pc2stat >> (i * 2)) & 0x3;
u8 lpre = (lane & 0x0c) >> 2;
u8 lvsw = (lane & 0x03) >> 0;
u8 hivs = 3 - lpre;
u8 hipe = 3;
u8 hipc = 3;
if (lpc2 >= hipc)
lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED;
if (lpre >= hipe) {
lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */
lvsw = hivs = 3 - (lpre & 3);
} else
if (lvsw >= hivs) {
lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED;
}
lt->conf[i] = (lpre << 3) | lvsw;
lt->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4);
OUTP_DBG(&outp->base, "config lane %d %02x %02x",
i, lt->conf[i], lpc2);
outp->func->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3);
}
ret = nvkm_wraux(outp->aux, DPCD_LC03(0), lt->conf, 4);
if (ret)
return ret;
if (pc) {
ret = nvkm_wraux(outp->aux, DPCD_LC0F, lt->pc2conf, 2);
if (ret)
return ret;
}
return 0;
}
static void
nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern)
{
struct nvkm_output_dp *outp = lt->outp;
u8 sink_tp;
OUTP_DBG(&outp->base, "training pattern %d", pattern);
outp->func->pattern(outp, pattern);
nvkm_rdaux(outp->aux, DPCD_LC02, &sink_tp, 1);
sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
sink_tp |= pattern;
nvkm_wraux(outp->aux, DPCD_LC02, &sink_tp, 1);
}
static int
nvkm_dp_train_eq(struct lt_state *lt)
{
struct nvkm_output_dp *outp = lt->outp;
bool eq_done = false, cr_done = true;
int tries = 0, i;
if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED)
nvkm_dp_train_pattern(lt, 3);
else
nvkm_dp_train_pattern(lt, 2);
do {
if ((tries &&
nvkm_dp_train_drive(lt, lt->pc2)) ||
nvkm_dp_train_sense(lt, lt->pc2, 400))
break;
eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
for (i = 0; i < lt->link_nr && eq_done; i++) {
u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
if (!(lane & DPCD_LS02_LANE0_CR_DONE))
cr_done = false;
if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
!(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED))
eq_done = false;
}
} while (!eq_done && cr_done && ++tries <= 5);
return eq_done ? 0 : -1;
}
static int
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;
nvkm_dp_train_pattern(lt, 1);
do {
if (nvkm_dp_train_drive(lt, false) ||
nvkm_dp_train_sense(lt, false, 100))
break;
cr_done = true;
for (i = 0; i < lt->link_nr; i++) {
u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
if (!(lane & DPCD_LS02_LANE0_CR_DONE)) {
cr_done = false;
if (lt->conf[i] & DPCD_LC03_MAX_SWING_REACHED)
abort = true;
break;
}
}
if ((lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) {
voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
tries = 0;
}
} while (!cr_done && !abort && ++tries < 5);
return cr_done ? 0 : -1;
}
static int
nvkm_dp_train_links(struct lt_state *lt)
{
struct nvkm_output_dp *outp = lt->outp;
struct nvkm_disp *disp = outp->base.disp;
struct nvkm_subdev *subdev = &disp->engine.subdev;
struct nvkm_bios *bios = subdev->device->bios;
struct nvbios_init init = {
.subdev = subdev,
.bios = bios,
.offset = 0x0000,
.outp = &outp->base.info,
.crtc = -1,
.execute = 1,
};
u32 lnkcmp;
u8 sink[2];
int ret;
OUTP_DBG(&outp->base, "%d lanes at %d KB/s", lt->link_nr, lt->link_bw);
/* Intersect misc. capabilities of the OR and sink. */
if (disp->engine.subdev.device->chipset < 0xd0)
outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
lt->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED;
/* Set desired link configuration on the source. */
if ((lnkcmp = lt->outp->info.lnkcmp)) {
if (outp->version < 0x30) {
while ((lt->link_bw / 10) < nvbios_rd16(bios, lnkcmp))
lnkcmp += 4;
init.offset = nvbios_rd16(bios, lnkcmp + 2);
} else {
while ((lt->link_bw / 27000) < nvbios_rd08(bios, lnkcmp))
lnkcmp += 3;
init.offset = nvbios_rd16(bios, lnkcmp + 1);
}
nvbios_exec(&init);
}
ret = outp->func->lnk_ctl(outp, lt->link_nr, lt->link_bw / 27000,
outp->dpcd[DPCD_RC02] &
DPCD_RC02_ENHANCED_FRAME_CAP);
if (ret) {
if (ret < 0)
OUTP_ERR(&outp->base, "lnk_ctl failed with %d", ret);
return ret;
}
outp->func->lnk_pwr(outp, lt->link_nr);
/* Set desired link configuration on the sink. */
sink[0] = lt->link_bw / 27000;
sink[1] = lt->link_nr;
if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
return nvkm_wraux(outp->aux, DPCD_LC00_LINK_BW_SET, sink, 2);
}
static void
nvkm_dp_train_fini(struct lt_state *lt)
{
struct nvkm_output_dp *outp = lt->outp;
struct nvkm_disp *disp = outp->base.disp;
struct nvkm_subdev *subdev = &disp->engine.subdev;
struct nvbios_init init = {
.subdev = subdev,
.bios = subdev->device->bios,
.outp = &outp->base.info,
.crtc = -1,
.execute = 1,
};
/* Execute AfterLinkTraining script from DP Info table. */
init.offset = outp->info.script[1],
nvbios_exec(&init);
}
static void
nvkm_dp_train_init(struct lt_state *lt, bool spread)
{
struct nvkm_output_dp *outp = lt->outp;
struct nvkm_disp *disp = outp->base.disp;
struct nvkm_subdev *subdev = &disp->engine.subdev;
struct nvbios_init init = {
.subdev = subdev,
.bios = subdev->device->bios,
.outp = &outp->base.info,
.crtc = -1,
.execute = 1,
};
/* Execute EnableSpread/DisableSpread script from DP Info table. */
if (spread)
init.offset = outp->info.script[2];
else
init.offset = outp->info.script[3];
nvbios_exec(&init);
/* Execute BeforeLinkTraining script from DP info table. */
init.offset = outp->info.script[0];
nvbios_exec(&init);
}
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 void
nvkm_dp_train(struct nvkm_output_dp *outp)
{
struct nv50_disp *disp = nv50_disp(outp->base.disp);
const struct dp_rates *cfg = nvkm_dp_rates - 1;
struct lt_state lt = {
.outp = outp,
};
u8 pwr;
int ret;
if (!outp->base.info.location && disp->func->sor.magic)
disp->func->sor.magic(&outp->base);
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 (outp->dpcd[1] > outp->base.info.dpconf.link_bw)
outp->dpcd[1] = outp->base.info.dpconf.link_bw;
/* Ensure sink is not in a low-power state. */
if (!nvkm_rdaux(outp->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->aux, DPCD_SC00, &pwr, 1);
}
}
/* Link training. */
nvkm_dp_train_init(&lt, outp->dpcd[3] & 0x01);
while (ret = -EIO, (++cfg)->rate) {
/* Skip configurations not supported by both OR and sink. */
while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) ||
cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE]))
cfg++;
lt.link_bw = cfg->bw * 27000;
lt.link_nr = cfg->nr;
/* Program selected link configuration. */
ret = nvkm_dp_train_links(&lt);
if (ret == 0) {
/* Attempt to train the link in this configuration. */
memset(lt.stat, 0x00, sizeof(lt.stat));
if (!nvkm_dp_train_cr(&lt) &&
!nvkm_dp_train_eq(&lt))
break;
} else
if (ret) {
/* nvkm_dp_train_links() handled training, or
* we failed to communicate with the sink.
*/
break;
}
}
nvkm_dp_train_pattern(&lt, 0);
nvkm_dp_train_fini(&lt);
if (ret < 0)
OUTP_ERR(&outp->base, "link training failed");
OUTP_DBG(&outp->base, "training complete");
atomic_set(&outp->lt.done, 1);
}
int
nvkm_output_dp_train(struct nvkm_output *base, u32 datarate)
{
......
#ifndef __NVKM_DISP_DPORT_H__
#define __NVKM_DISP_DPORT_H__
struct nvkm_output_dp;
#ifndef __NVKM_DISP_OUTP_DP_H__
#define __NVKM_DISP_OUTP_DP_H__
#define nvkm_output_dp(p) container_of((p), struct nvkm_output_dp, base)
#include "outp.h"
#include <core/notify.h>
#include <subdev/bios.h>
#include <subdev/bios/dp.h>
struct nvkm_output_dp {
const struct nvkm_output_dp_func *func;
struct nvkm_output base;
struct nvbios_dpout info;
u8 version;
struct nvkm_i2c_aux *aux;
struct nvkm_notify irq;
struct nvkm_notify hpd;
bool present;
u8 dpcd[16];
struct mutex mutex;
struct {
atomic_t done;
bool mst;
} lt;
};
struct nvkm_output_dp_func {
int (*pattern)(struct nvkm_output_dp *, int);
int (*lnk_pwr)(struct nvkm_output_dp *, int nr);
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);
void (*vcpi)(struct nvkm_output_dp *, int head, u8 start_slot,
u8 num_slots, u16 pbn, u16 aligned_pbn);
};
int nvkm_output_dp_train(struct nvkm_output *, u32 rate);
int nvkm_output_dp_ctor(const struct nvkm_output_dp_func *, struct nvkm_disp *,
int index, struct dcb_output *, struct nvkm_i2c_aux *,
struct nvkm_output_dp *);
int nvkm_output_dp_new_(const struct nvkm_output_dp_func *, struct nvkm_disp *,
int index, struct dcb_output *,
struct nvkm_output **);
int nv50_pior_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
int g94_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
int g94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int);
int gf119_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
int gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *, int, int, bool);
int gf119_sor_dp_drv_ctl(struct nvkm_output_dp *, int, int, int, int);
void gf119_sor_dp_vcpi(struct nvkm_output_dp *, int, u8, u8, u16, u16);
int gm107_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
int gm107_sor_dp_pattern(struct nvkm_output_dp *, int);
int gm200_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
/* DPCD Receiver Capabilities */
#define DPCD_RC00_DPCD_REV 0x00000
......@@ -76,6 +140,4 @@ struct nvkm_output_dp;
#define DPCD_SC00_SET_POWER 0x03
#define DPCD_SC00_SET_POWER_D0 0x01
#define DPCD_SC00_SET_POWER_D3 0x03
void nvkm_dp_train(struct nvkm_output_dp *);
#endif
This diff is collapsed.
......@@ -28,7 +28,7 @@ static const struct nv50_disp_func
g84_disp = {
.intr = nv50_disp_intr,
.uevent = &nv50_disp_chan_uevent,
.super = nv50_disp_intr_supervisor,
.super = nv50_disp_super,
.root = &g84_disp_root_oclass,
.head.vblank_init = nv50_disp_vblank_init,
.head.vblank_fini = nv50_disp_vblank_fini,
......
......@@ -28,7 +28,7 @@ static const struct nv50_disp_func
g94_disp = {
.intr = nv50_disp_intr,
.uevent = &nv50_disp_chan_uevent,
.super = nv50_disp_intr_supervisor,
.super = nv50_disp_super,
.root = &g94_disp_root_oclass,
.head.vblank_init = nv50_disp_vblank_init,
.head.vblank_fini = nv50_disp_vblank_fini,
......
......@@ -358,7 +358,7 @@ gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head)
}
void
gf119_disp_intr_supervisor(struct work_struct *work)
gf119_disp_super(struct work_struct *work)
{
struct nv50_disp *disp =
container_of(work, struct nv50_disp, supervisor);
......@@ -510,7 +510,7 @@ gf119_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
.super = gf119_disp_intr_supervisor,
.super = gf119_disp_super,
.root = &gf119_disp_root_oclass,
.head.vblank_init = gf119_disp_vblank_init,
.head.vblank_fini = gf119_disp_vblank_fini,
......
......@@ -29,7 +29,7 @@ gk104_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
.super = gf119_disp_intr_supervisor,
.super = gf119_disp_super,
.root = &gk104_disp_root_oclass,
.head.vblank_init = gf119_disp_vblank_init,
.head.vblank_fini = gf119_disp_vblank_fini,
......
......@@ -29,7 +29,7 @@ gk110_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
.super = gf119_disp_intr_supervisor,
.super = gf119_disp_super,
.root = &gk110_disp_root_oclass,
.head.vblank_init = gf119_disp_vblank_init,
.head.vblank_fini = gf119_disp_vblank_fini,
......
......@@ -29,7 +29,7 @@ gm107_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
.super = gf119_disp_intr_supervisor,
.super = gf119_disp_super,
.root = &gm107_disp_root_oclass,
.head.vblank_init = gf119_disp_vblank_init,
.head.vblank_fini = gf119_disp_vblank_fini,
......
......@@ -29,7 +29,7 @@ gm200_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
.super = gf119_disp_intr_supervisor,
.super = gf119_disp_super,
.root = &gm200_disp_root_oclass,
.head.vblank_init = gf119_disp_vblank_init,
.head.vblank_fini = gf119_disp_vblank_fini,
......
......@@ -29,7 +29,7 @@ gp100_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
.super = gf119_disp_intr_supervisor,
.super = gf119_disp_super,
.root = &gp100_disp_root_oclass,
.head.vblank_init = gf119_disp_vblank_init,
.head.vblank_fini = gf119_disp_vblank_fini,
......
......@@ -55,7 +55,7 @@ gp102_disp = {
.intr = gf119_disp_intr,
.intr_error = gp102_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
.super = gf119_disp_intr_supervisor,
.super = gf119_disp_super,
.root = &gp102_disp_root_oclass,
.head.vblank_init = gf119_disp_vblank_init,
.head.vblank_fini = gf119_disp_vblank_fini,
......
......@@ -28,7 +28,7 @@ static const struct nv50_disp_func
gt200_disp = {
.intr = nv50_disp_intr,
.uevent = &nv50_disp_chan_uevent,
.super = nv50_disp_intr_supervisor,
.super = nv50_disp_super,
.root = &gt200_disp_root_oclass,
.head.vblank_init = nv50_disp_vblank_init,
.head.vblank_fini = nv50_disp_vblank_fini,
......
......@@ -28,7 +28,7 @@ static const struct nv50_disp_func
gt215_disp = {
.intr = nv50_disp_intr,
.uevent = &nv50_disp_chan_uevent,
.super = nv50_disp_intr_supervisor,
.super = nv50_disp_super,
.root = &gt215_disp_root_oclass,
.head.vblank_init = nv50_disp_vblank_init,
.head.vblank_fini = nv50_disp_vblank_fini,
......
#include "nv50.h"
#include "hdmi.h"
void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame,
u8 *raw_frame, ssize_t len)
......
#ifndef __NVKM_DISP_HDMI_H__
#define __NVKM_DISP_HDMI_H__
#include "nv50.h"
struct packed_hdmi_infoframe {
u32 header;
u32 subpack0_low;
u32 subpack0_high;
u32 subpack1_low;
u32 subpack1_high;
};
void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame,
u8 *raw_frame, ssize_t len);
#endif
......@@ -21,7 +21,7 @@
*
* Authors: Ben Skeggs
*/
#include "nv50.h"
#include "hdmi.h"
#include <core/client.h>
......
......@@ -21,7 +21,7 @@
*
* Authors: Ben Skeggs
*/
#include "nv50.h"
#include "hdmi.h"
#include <core/client.h>
......
......@@ -21,7 +21,7 @@
*
* Authors: Ben Skeggs
*/
#include "nv50.h"
#include "hdmi.h"
#include <core/client.h>
......
......@@ -21,7 +21,7 @@
*
* Authors: Ben Skeggs
*/
#include "nv50.h"
#include "hdmi.h"
#include "outp.h"
#include <core/client.h>
......
......@@ -2,8 +2,7 @@
#define __NV50_DISP_H__
#define nv50_disp(p) container_of((p), struct nv50_disp, base)
#include "priv.h"
struct nvkm_output;
struct nvkm_output_dp;
#include "dp.h"
#define NV50_DISP_MTHD_ struct nvkm_object *object, \
struct nv50_disp *disp, void *data, u32 size
......@@ -40,17 +39,6 @@ int nv50_dac_sense(NV50_DISP_MTHD_V1);
int gt215_hda_eld(NV50_DISP_MTHD_V1);
int gf119_hda_eld(NV50_DISP_MTHD_V1);
struct packed_hdmi_infoframe {
u32 header;
u32 subpack0_low;
u32 subpack0_high;
u32 subpack1_low;
u32 subpack1_high;
};
void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame,
u8 *raw_frame, ssize_t len);
int g84_hdmi_ctrl(NV50_DISP_MTHD_V1);
int gt215_hdmi_ctrl(NV50_DISP_MTHD_V1);
int gf119_hdmi_ctrl(NV50_DISP_MTHD_V1);
......@@ -120,11 +108,15 @@ struct nv50_disp_func {
void nv50_disp_vblank_init(struct nv50_disp *, int);
void nv50_disp_vblank_fini(struct nv50_disp *, int);
void nv50_disp_intr(struct nv50_disp *);
void nv50_disp_intr_supervisor(struct work_struct *);
void nv50_disp_super(struct work_struct *);
void gf119_disp_vblank_init(struct nv50_disp *, int);
void gf119_disp_vblank_fini(struct nv50_disp *, int);
void gf119_disp_intr(struct nv50_disp *);
void gf119_disp_intr_supervisor(struct work_struct *);
void gf119_disp_super(struct work_struct *);
void gf119_disp_intr_error(struct nv50_disp *, int);
void nv50_disp_dptmds_war_2(struct nv50_disp *, struct dcb_output *);
void nv50_disp_dptmds_war_3(struct nv50_disp *, struct dcb_output *);
void nv50_disp_update_sppll1(struct nv50_disp *);
#endif
#ifndef __NVKM_DISP_OUTP_DP_H__
#define __NVKM_DISP_OUTP_DP_H__
#define nvkm_output_dp(p) container_of((p), struct nvkm_output_dp, base)
#ifndef MSG
#define MSG(l,f,a...) \
nvkm_##l(&outp->base.disp->engine.subdev, "%02x:%04x:%04x: "f, \
outp->base.index, outp->base.info.hasht, \
outp->base.info.hashm, ##a)
#define DBG(f,a...) MSG(debug, f, ##a)
#define ERR(f,a...) MSG(error, f, ##a)
#endif
#include "outp.h"
#include <core/notify.h>
#include <subdev/bios.h>
#include <subdev/bios/dp.h>
struct nvkm_output_dp {
const struct nvkm_output_dp_func *func;
struct nvkm_output base;
struct nvbios_dpout info;
u8 version;
struct nvkm_i2c_aux *aux;
struct nvkm_notify irq;
struct nvkm_notify hpd;
bool present;
u8 dpcd[16];
struct mutex mutex;
struct {
atomic_t done;
bool mst;
} lt;
};
struct nvkm_output_dp_func {
int (*pattern)(struct nvkm_output_dp *, int);
int (*lnk_pwr)(struct nvkm_output_dp *, int nr);
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);
void (*vcpi)(struct nvkm_output_dp *, int head, u8 start_slot,
u8 num_slots, u16 pbn, u16 aligned_pbn);
};
int nvkm_output_dp_train(struct nvkm_output *, u32 rate);
int nvkm_output_dp_ctor(const struct nvkm_output_dp_func *, struct nvkm_disp *,
int index, struct dcb_output *, struct nvkm_i2c_aux *,
struct nvkm_output_dp *);
int nvkm_output_dp_new_(const struct nvkm_output_dp_func *, struct nvkm_disp *,
int index, struct dcb_output *,
struct nvkm_output **);
int nv50_pior_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
int g94_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
int g94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int);
int gf119_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
int gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *, int, int, bool);
int gf119_sor_dp_drv_ctl(struct nvkm_output_dp *, int, int, int, int);
void gf119_sor_dp_vcpi(struct nvkm_output_dp *, int, u8, u8, u16, u16);
int gm107_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
int gm107_sor_dp_pattern(struct nvkm_output_dp *, int);
int gm200_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
struct nvkm_output **);
#endif
......@@ -21,7 +21,6 @@
*
* Authors: Ben Skeggs
*/
#include "outpdp.h"
#include "nv50.h"
#include <core/client.h>
......@@ -31,41 +30,6 @@
#include <nvif/cl5070.h>
#include <nvif/unpack.h>
int
nv50_pior_power(NV50_DISP_MTHD_V1)
{
struct nvkm_device *device = disp->base.engine.subdev.device;
const u32 soff = outp->or * 0x800;
union {
struct nv50_disp_pior_pwr_v0 v0;
} *args = data;
u32 ctrl, type;
int ret = -ENOSYS;
nvif_ioctl(object, "disp pior pwr size %d\n", size);
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(object, "disp pior pwr vers %d state %d type %x\n",
args->v0.version, args->v0.state, args->v0.type);
if (args->v0.type > 0x0f)
return -EINVAL;
ctrl = !!args->v0.state;
type = args->v0.type;
} else
return ret;
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000))
break;
);
nvkm_mask(device, 0x61e004 + soff, 0x80000101, 0x80000000 | ctrl);
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000))
break;
);
disp->pior.type[outp->or] = type;
return 0;
}
/******************************************************************************
* TMDS
*****************************************************************************/
......@@ -129,3 +93,38 @@ nv50_pior_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
return nvkm_output_dp_ctor(&nv50_pior_output_dp_func, disp,
index, dcbE, aux, outp);
}
int
nv50_pior_power(NV50_DISP_MTHD_V1)
{
struct nvkm_device *device = disp->base.engine.subdev.device;
const u32 soff = outp->or * 0x800;
union {
struct nv50_disp_pior_pwr_v0 v0;
} *args = data;
u32 ctrl, type;
int ret = -ENOSYS;
nvif_ioctl(object, "disp pior pwr size %d\n", size);
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(object, "disp pior pwr vers %d state %d type %x\n",
args->v0.version, args->v0.state, args->v0.type);
if (args->v0.type > 0x0f)
return -EINVAL;
ctrl = !!args->v0.state;
type = args->v0.type;
} else
return ret;
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000))
break;
);
nvkm_mask(device, 0x61e004 + soff, 0x80000101, 0x80000000 | ctrl);
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000))
break;
);
disp->pior.type[outp->or] = type;
return 0;
}
......@@ -2,7 +2,6 @@
#define __NVKM_DISP_PRIV_H__
#include <engine/disp.h>
#include "outp.h"
#include "outpdp.h"
int nvkm_disp_ctor(const struct nvkm_disp_func *, struct nvkm_device *,
int index, int heads, struct nvkm_disp *);
......
......@@ -22,7 +22,6 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
#include "outpdp.h"
#include <subdev/timer.h>
......@@ -54,6 +53,40 @@ g94_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
return g94[lane];
}
static int
g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
struct nvkm_bios *bios = device->bios;
const u32 shift = g94_sor_dp_lane_map(device, ln);
const u32 loff = g94_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->base.info.hasht,
outp->base.info.hashm,
&ver, &hdr, &cnt, &len, &info);
if (!addr)
return -ENODEV;
addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
&ver, &hdr, &cnt, &len, &ocfg);
if (!addr)
return -EINVAL;
data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
data[2] = nvkm_rd32(device, 0x61c130 + loff);
if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
nvkm_wr32(device, 0x61c130 + loff, data[2]);
return 0;
}
static int
g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
{
......@@ -103,40 +136,6 @@ g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
return 0;
}
static int
g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
struct nvkm_bios *bios = device->bios;
const u32 shift = g94_sor_dp_lane_map(device, ln);
const u32 loff = g94_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->base.info.hasht,
outp->base.info.hashm,
&ver, &hdr, &cnt, &len, &info);
if (!addr)
return -ENODEV;
addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
&ver, &hdr, &cnt, &len, &ocfg);
if (!addr)
return -EINVAL;
data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
data[2] = nvkm_rd32(device, 0x61c130 + loff);
if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
nvkm_wr32(device, 0x61c130 + loff, data[2]);
return 0;
}
static const struct nvkm_output_dp_func
g94_sor_dp_func = {
.pattern = g94_sor_dp_pattern,
......@@ -151,3 +150,129 @@ g94_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
{
return nvkm_output_dp_new_(&g94_sor_dp_func, disp, index, dcbE, poutp);
}
static bool
nv50_disp_dptmds_war(struct nvkm_device *device)
{
switch (device->chipset) {
case 0x94:
case 0x96:
case 0x98:
return true;
default:
break;
}
return false;
}
static bool
nv50_disp_dptmds_war_needed(struct nv50_disp *disp, struct dcb_output *outp)
{
struct nvkm_device *device = disp->base.engine.subdev.device;
const u32 soff = __ffs(outp->or) * 0x800;
if (nv50_disp_dptmds_war(device) && outp->type == DCB_OUTPUT_TMDS) {
switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) {
case 0x00000000:
case 0x00030000:
return true;
default:
break;
}
}
return false;
}
void
nv50_disp_update_sppll1(struct nv50_disp *disp)
{
struct nvkm_device *device = disp->base.engine.subdev.device;
bool used = false;
int sor;
if (!nv50_disp_dptmds_war(device))
return;
for (sor = 0; sor < disp->func->sor.nr; sor++) {
u32 clksor = nvkm_rd32(device, 0x614300 + (sor * 0x800));
switch (clksor & 0x03000000) {
case 0x02000000:
case 0x03000000:
used = true;
break;
default:
break;
}
}
if (used)
return;
nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000);
}
void
nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
{
struct nvkm_device *device = disp->base.engine.subdev.device;
const u32 soff = __ffs(outp->or) * 0x800;
u32 sorpwr;
if (!nv50_disp_dptmds_war_needed(disp, outp))
return;
sorpwr = nvkm_rd32(device, 0x61c004 + soff);
if (sorpwr & 0x00000001) {
u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
u32 pd_pc = (seqctl & 0x00000f00) >> 8;
u32 pu_pc = seqctl & 0x0000000f;
nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000);
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
break;
);
nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000);
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
break;
);
nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000);
nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000);
}
nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000);
nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000);
if (sorpwr & 0x00000001) {
nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001);
}
}
void
nv50_disp_dptmds_war_2(struct nv50_disp *disp, struct dcb_output *outp)
{
struct nvkm_device *device = disp->base.engine.subdev.device;
const u32 soff = __ffs(outp->or) * 0x800;
if (!nv50_disp_dptmds_war_needed(disp, outp))
return;
nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000);
nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000);
nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001);
nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000);
nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000);
nvkm_usec(device, 400, NVKM_DELAY);
nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000);
nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000);
if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) {
u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
u32 pu_pc = seqctl & 0x0000000f;
nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000);
}
}
......@@ -22,7 +22,17 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
#include "outpdp.h"
void
gf119_sor_dp_vcpi(struct nvkm_output_dp *outp, int head, u8 slot,
u8 slot_nr, u16 pbn, u16 aligned)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
const u32 hoff = head * 0x800;
nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot);
nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn);
}
static inline u32
gf119_sor_soff(struct nvkm_output_dp *outp)
......@@ -36,36 +46,6 @@ gf119_sor_loff(struct nvkm_output_dp *outp)
return gf119_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
}
static int
gf119_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
const u32 soff = gf119_sor_soff(outp);
nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern);
return 0;
}
int
gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
const u32 soff = gf119_sor_soff(outp);
const u32 loff = gf119_sor_loff(outp);
u32 dpctrl = 0x00000000;
u32 clksor = 0x00000000;
clksor |= bw << 18;
dpctrl |= ((1 << nr) - 1) << 16;
if (outp->lt.mst)
dpctrl |= 0x40000000;
if (ef)
dpctrl |= 0x00004000;
nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor);
nvkm_mask(device, 0x61c10c + loff, 0x401f4000, dpctrl);
return 0;
}
int
gf119_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
int ln, int vs, int pe, int pc)
......@@ -103,15 +83,34 @@ gf119_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
return 0;
}
void
gf119_sor_dp_vcpi(struct nvkm_output_dp *outp, int head, u8 slot,
u8 slot_nr, u16 pbn, u16 aligned)
static int
gf119_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
const u32 hoff = head * 0x800;
const u32 soff = gf119_sor_soff(outp);
nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern);
return 0;
}
nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot);
nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn);
int
gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
const u32 soff = gf119_sor_soff(outp);
const u32 loff = gf119_sor_loff(outp);
u32 dpctrl = 0x00000000;
u32 clksor = 0x00000000;
clksor |= bw << 18;
dpctrl |= ((1 << nr) - 1) << 16;
if (outp->lt.mst)
dpctrl |= 0x40000000;
if (ef)
dpctrl |= 0x00004000;
nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor);
nvkm_mask(device, 0x61c10c + loff, 0x401f4000, dpctrl);
return 0;
}
static const struct nvkm_output_dp_func
......
......@@ -22,7 +22,6 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include "nv50.h"
#include "outpdp.h"
int
gm107_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
......
......@@ -22,7 +22,6 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
#include "outpdp.h"
#include <subdev/timer.h>
......@@ -38,44 +37,12 @@ gm200_sor_loff(struct nvkm_output_dp *outp)
return gm200_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
}
void
gm200_sor_magic(struct nvkm_output *outp)
{
struct nvkm_device *device = outp->disp->engine.subdev.device;
const u32 soff = outp->or * 0x100;
const u32 data = outp->or + 1;
if (outp->info.sorconf.link & 1)
nvkm_mask(device, 0x612308 + soff, 0x0000001f, 0x00000000 | data);
if (outp->info.sorconf.link & 2)
nvkm_mask(device, 0x612388 + soff, 0x0000001f, 0x00000010 | data);
}
static inline u32
gm200_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
{
return lane * 0x08;
}
static int
gm200_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
const u32 soff = gm200_sor_soff(outp);
const u32 loff = gm200_sor_loff(outp);
u32 mask = 0, i;
for (i = 0; i < nr; i++)
mask |= 1 << (gm200_sor_dp_lane_map(device, i) >> 3);
nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000))
break;
);
return 0;
}
static int
gm200_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
int ln, int vs, int pe, int pc)
......@@ -114,6 +81,26 @@ gm200_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
return 0;
}
static int
gm200_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
const u32 soff = gm200_sor_soff(outp);
const u32 loff = gm200_sor_loff(outp);
u32 mask = 0, i;
for (i = 0; i < nr; i++)
mask |= 1 << (gm200_sor_dp_lane_map(device, i) >> 3);
nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000))
break;
);
return 0;
}
static const struct nvkm_output_dp_func
gm200_sor_dp_func = {
.pattern = gm107_sor_dp_pattern,
......@@ -129,3 +116,15 @@ gm200_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
{
return nvkm_output_dp_new_(&gm200_sor_dp_func, disp, index, dcbE, poutp);
}
void
gm200_sor_magic(struct nvkm_output *outp)
{
struct nvkm_device *device = outp->disp->engine.subdev.device;
const u32 soff = outp->or * 0x100;
const u32 data = outp->or + 1;
if (outp->info.sorconf.link & 1)
nvkm_mask(device, 0x612308 + soff, 0x0000001f, 0x00000000 | data);
if (outp->info.sorconf.link & 2)
nvkm_mask(device, 0x612388 + soff, 0x0000001f, 0x00000010 | data);
}
......@@ -30,6 +30,18 @@
#include <nvif/cl5070.h>
#include <nvif/unpack.h>
static const struct nvkm_output_func
nv50_sor_output_func = {
};
int
nv50_sor_output_new(struct nvkm_disp *disp, int index,
struct dcb_output *dcbE, struct nvkm_output **poutp)
{
return nvkm_output_new_(&nv50_sor_output_func, disp,
index, dcbE, poutp);
}
int
nv50_sor_power(NV50_DISP_MTHD_V1)
{
......@@ -65,15 +77,3 @@ nv50_sor_power(NV50_DISP_MTHD_V1)
);
return 0;
}
static const struct nvkm_output_func
nv50_sor_output_func = {
};
int
nv50_sor_output_new(struct nvkm_disp *disp, int index,
struct dcb_output *dcbE, struct nvkm_output **poutp)
{
return nvkm_output_new_(&nv50_sor_output_func, disp,
index, dcbE, poutp);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment