mirror of https://gitee.com/openkylin/linux.git
drm/nouveau/disp/nv50-: implement a common supervisor 2.2
This makes use of all the additional routing and state added in previous commits, making it possible to deal with GM20x macro link routing, while also sharing code between the NV50 and GF119 implementations. Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
1f0c9eaf31
commit
8d7ef84d90
|
@ -25,6 +25,7 @@
|
|||
#include "rootnv50.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <core/notify.h>
|
||||
#include <core/ramht.h>
|
||||
#include <engine/dma.h>
|
||||
|
||||
|
|
|
@ -21,6 +21,14 @@
|
|||
*/
|
||||
#include "ior.h"
|
||||
|
||||
static void
|
||||
gf119_dac_clock(struct nvkm_ior *dac)
|
||||
{
|
||||
struct nvkm_device *device = dac->disp->engine.subdev.device;
|
||||
const u32 doff = nv50_ior_base(dac);
|
||||
nvkm_mask(device, 0x612280 + doff, 0x07070707, 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state)
|
||||
{
|
||||
|
@ -44,6 +52,7 @@ gf119_dac = {
|
|||
.state = gf119_dac_state,
|
||||
.power = nv50_dac_power,
|
||||
.sense = nv50_dac_sense,
|
||||
.clock = gf119_dac_clock,
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -25,6 +25,14 @@
|
|||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static void
|
||||
nv50_dac_clock(struct nvkm_ior *dac)
|
||||
{
|
||||
struct nvkm_device *device = dac->disp->engine.subdev.device;
|
||||
const u32 doff = nv50_ior_base(dac);
|
||||
nvkm_mask(device, 0x614280 + doff, 0x07070707, 0x00000000);
|
||||
}
|
||||
|
||||
int
|
||||
nv50_dac_sense(struct nvkm_ior *dac, u32 loadval)
|
||||
{
|
||||
|
@ -95,6 +103,7 @@ nv50_dac = {
|
|||
.state = nv50_dac_state,
|
||||
.power = nv50_dac_power,
|
||||
.sense = nv50_dac_sense,
|
||||
.clock = nv50_dac_clock,
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -428,8 +428,8 @@ nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior)
|
|||
);
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_output_dp_train(struct nvkm_outp *outp, u32 unused)
|
||||
static int
|
||||
nvkm_dp_acquire(struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_dp *dp = nvkm_dp(outp);
|
||||
struct nvkm_ior *ior = dp->outp.ior;
|
||||
|
@ -529,7 +529,7 @@ nvkm_dp_hpd(struct nvkm_notify *notify)
|
|||
OUTP_DBG(&dp->outp, "HPD: %d", line->mask);
|
||||
if (line->mask & NVKM_I2C_IRQ) {
|
||||
if (atomic_read(&dp->lt.done))
|
||||
nvkm_output_dp_train(&dp->outp, 0);
|
||||
dp->outp.func->acquire(&dp->outp);
|
||||
rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ;
|
||||
} else {
|
||||
nvkm_dp_enable(dp, true);
|
||||
|
@ -574,6 +574,7 @@ nvkm_dp_func = {
|
|||
.dtor = nvkm_dp_dtor,
|
||||
.init = nvkm_dp_init,
|
||||
.fini = nvkm_dp_fini,
|
||||
.acquire = nvkm_dp_acquire,
|
||||
.release = nvkm_dp_release,
|
||||
};
|
||||
|
||||
|
|
|
@ -29,10 +29,6 @@ struct nvkm_dp {
|
|||
} lt;
|
||||
};
|
||||
|
||||
#define nvkm_output_dp nvkm_dp
|
||||
|
||||
int nvkm_output_dp_train(struct nvkm_output *, u32 rate);
|
||||
|
||||
int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_outp **);
|
||||
|
||||
|
|
|
@ -138,120 +138,6 @@ exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
|
|||
return outp;
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_disp_intr_unk2_2_tu(struct nv50_disp *disp, int head,
|
||||
struct dcb_output *outp)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const int or = ffs(outp->or) - 1;
|
||||
const u32 ctrl = nvkm_rd32(device, 0x660200 + (or * 0x020));
|
||||
const u32 conf = nvkm_rd32(device, 0x660404 + (head * 0x300));
|
||||
const s32 vactive = nvkm_rd32(device, 0x660414 + (head * 0x300)) & 0xffff;
|
||||
const s32 vblanke = nvkm_rd32(device, 0x66041c + (head * 0x300)) & 0xffff;
|
||||
const s32 vblanks = nvkm_rd32(device, 0x660420 + (head * 0x300)) & 0xffff;
|
||||
const u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
|
||||
const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1;
|
||||
const u32 hoff = (head * 0x800);
|
||||
const u32 soff = ( or * 0x800);
|
||||
const u32 loff = (link * 0x080) + soff;
|
||||
const u32 symbol = 100000;
|
||||
const u32 TU = 64;
|
||||
u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff);
|
||||
u32 clksor = nvkm_rd32(device, 0x612300 + soff);
|
||||
u32 datarate, link_nr, link_bw, bits;
|
||||
u64 ratio, value;
|
||||
|
||||
link_nr = hweight32(dpctrl & 0x000f0000);
|
||||
link_bw = (clksor & 0x007c0000) >> 18;
|
||||
link_bw *= 27000;
|
||||
|
||||
/* symbols/hblank - algorithm taken from comments in tegra driver */
|
||||
value = vblanke + vactive - vblanks - 7;
|
||||
value = value * link_bw;
|
||||
do_div(value, pclk);
|
||||
value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
|
||||
nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, value);
|
||||
|
||||
/* symbols/vblank - algorithm taken from comments in tegra driver */
|
||||
value = vblanks - vblanke - 25;
|
||||
value = value * link_bw;
|
||||
do_div(value, pclk);
|
||||
value = value - ((36 / link_nr) + 3) - 1;
|
||||
nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, value);
|
||||
|
||||
/* watermark */
|
||||
if ((conf & 0x3c0) == 0x180) bits = 30;
|
||||
else if ((conf & 0x3c0) == 0x140) bits = 24;
|
||||
else bits = 18;
|
||||
datarate = (pclk * bits) / 8;
|
||||
|
||||
ratio = datarate;
|
||||
ratio *= symbol;
|
||||
do_div(ratio, link_nr * link_bw);
|
||||
|
||||
value = (symbol - ratio) * TU;
|
||||
value *= ratio;
|
||||
do_div(value, symbol);
|
||||
do_div(value, symbol);
|
||||
|
||||
value += 5;
|
||||
value |= 0x08000000;
|
||||
|
||||
nvkm_wr32(device, 0x616610 + hoff, value);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_disp_intr_unk2_2(struct nv50_disp *disp, int head)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
struct nvkm_output *outp;
|
||||
u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
|
||||
u32 conf, addr, data;
|
||||
|
||||
outp = exec_clkcmp(disp, head, 0xff, pclk, &conf);
|
||||
if (!outp)
|
||||
return;
|
||||
|
||||
/* see note in nv50_disp_intr_unk20_2() */
|
||||
if (outp->info.type == DCB_OUTPUT_DP) {
|
||||
u32 sync = nvkm_rd32(device, 0x660404 + (head * 0x300));
|
||||
switch ((sync & 0x000003c0) >> 6) {
|
||||
case 6: pclk = pclk * 30; break;
|
||||
case 5: pclk = pclk * 24; break;
|
||||
case 2:
|
||||
default:
|
||||
pclk = pclk * 18;
|
||||
break;
|
||||
}
|
||||
|
||||
if (nvkm_output_dp_train(outp, pclk))
|
||||
OUTP_ERR(outp, "link not trained before attach");
|
||||
}
|
||||
|
||||
exec_clkcmp(disp, head, 0, pclk, &conf);
|
||||
|
||||
if (outp->info.type == DCB_OUTPUT_ANALOG) {
|
||||
addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800;
|
||||
data = 0x00000000;
|
||||
} else {
|
||||
addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800;
|
||||
data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
|
||||
switch (outp->info.type) {
|
||||
case DCB_OUTPUT_TMDS:
|
||||
nvkm_mask(device, addr, 0x007c0000, 0x00280000);
|
||||
break;
|
||||
case DCB_OUTPUT_DP:
|
||||
gf119_disp_intr_unk2_2_tu(disp, head, &outp->info);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nvkm_mask(device, addr, 0x00000707, data);
|
||||
nvkm_wr32(device, 0x612200 + (head * 0x800), 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head)
|
||||
{
|
||||
|
@ -302,8 +188,7 @@ gf119_disp_super(struct work_struct *work)
|
|||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
if (!(mask[head->id] & 0x00001000))
|
||||
continue;
|
||||
nvkm_debug(subdev, "supervisor 2.2 - head %d\n", head->id);
|
||||
gf119_disp_intr_unk2_2(disp, head->id);
|
||||
nv50_disp_super_2_2(disp, head);
|
||||
}
|
||||
} else
|
||||
if (disp->super & 0x00000004) {
|
||||
|
|
|
@ -36,6 +36,7 @@ struct nvkm_head *nvkm_head_find(struct nvkm_disp *, int id);
|
|||
struct nvkm_head_func {
|
||||
void (*state)(struct nvkm_head *, struct nvkm_head_state *);
|
||||
void (*rgpos)(struct nvkm_head *, u16 *hline, u16 *vline);
|
||||
void (*rgclk)(struct nvkm_head *, int div);
|
||||
void (*vblank_get)(struct nvkm_head *);
|
||||
void (*vblank_put)(struct nvkm_head *);
|
||||
};
|
||||
|
|
|
@ -39,6 +39,13 @@ gf119_head_vblank_get(struct nvkm_head *head)
|
|||
nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000001);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_head_rgclk(struct nvkm_head *head, int div)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
nvkm_mask(device, 0x612200 + (head->id * 0x800), 0x0000000f, div);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
|
||||
{
|
||||
|
@ -77,6 +84,7 @@ static const struct nvkm_head_func
|
|||
gf119_head = {
|
||||
.state = gf119_head_state,
|
||||
.rgpos = nv50_head_rgpos,
|
||||
.rgclk = gf119_head_rgclk,
|
||||
.vblank_get = gf119_head_vblank_get,
|
||||
.vblank_put = gf119_head_vblank_put,
|
||||
};
|
||||
|
|
|
@ -37,6 +37,13 @@ nv50_head_vblank_get(struct nvkm_head *head)
|
|||
nvkm_mask(device, 0x61002c, (4 << head->id), (4 << head->id));
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_head_rgclk(struct nvkm_head *head, int div)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
nvkm_mask(device, 0x614200 + (head->id * 0x800), 0x0000000f, div);
|
||||
}
|
||||
|
||||
void
|
||||
nv50_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
|
||||
{
|
||||
|
@ -73,6 +80,7 @@ static const struct nvkm_head_func
|
|||
nv50_head = {
|
||||
.state = nv50_head_state,
|
||||
.rgpos = nv50_head_rgpos,
|
||||
.rgclk = nv50_head_rgclk,
|
||||
.vblank_get = nv50_head_vblank_get,
|
||||
.vblank_put = nv50_head_vblank_put,
|
||||
};
|
||||
|
|
|
@ -50,6 +50,8 @@ struct nvkm_ior_func {
|
|||
void (*power)(struct nvkm_ior *, bool normal, bool pu,
|
||||
bool data, bool vsync, bool hsync);
|
||||
int (*sense)(struct nvkm_ior *, u32 loadval);
|
||||
void (*clock)(struct nvkm_ior *);
|
||||
void (*war_2)(struct nvkm_ior *);
|
||||
|
||||
struct {
|
||||
void (*ctrl)(struct nvkm_ior *, int head, bool enable,
|
||||
|
@ -67,6 +69,10 @@ struct nvkm_ior_func {
|
|||
void (*vcpi)(struct nvkm_ior *, int head, u8 slot,
|
||||
u8 slot_nr, u16 pbn, u16 aligned);
|
||||
void (*audio)(struct nvkm_ior *, int head, bool enable);
|
||||
void (*audio_sym)(struct nvkm_ior *, int head, u16 h, u32 v);
|
||||
void (*activesym)(struct nvkm_ior *, int head,
|
||||
u8 TU, u8 VTUa, u8 VTUf, u8 VTUi);
|
||||
void (*watermark)(struct nvkm_ior *, int head, u8 watermark);
|
||||
} dp;
|
||||
|
||||
struct {
|
||||
|
@ -99,21 +105,28 @@ nv50_sor_link(struct nvkm_ior *ior)
|
|||
|
||||
void nv50_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
|
||||
void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool);
|
||||
void nv50_sor_clock(struct nvkm_ior *);
|
||||
|
||||
void g94_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
|
||||
int g94_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *);
|
||||
void g94_sor_dp_power(struct nvkm_ior *, int);
|
||||
void g94_sor_dp_pattern(struct nvkm_ior *, int);
|
||||
void g94_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int);
|
||||
void g94_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
|
||||
void g94_sor_dp_activesym(struct nvkm_ior *, int, u8, u8, u8, u8);
|
||||
void g94_sor_dp_watermark(struct nvkm_ior *, int, u8);
|
||||
|
||||
void gt215_sor_dp_audio(struct nvkm_ior *, int, bool);
|
||||
|
||||
void gf119_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
|
||||
void gf119_sor_clock(struct nvkm_ior *);
|
||||
int gf119_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *);
|
||||
void gf119_sor_dp_pattern(struct nvkm_ior *, int);
|
||||
void gf119_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int);
|
||||
void gf119_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16);
|
||||
void gf119_sor_dp_audio(struct nvkm_ior *, int, bool);
|
||||
void gf119_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
|
||||
void gf119_sor_dp_watermark(struct nvkm_ior *, int, u8);
|
||||
|
||||
void gm107_sor_dp_pattern(struct nvkm_ior *, int);
|
||||
|
||||
|
|
|
@ -129,6 +129,62 @@ nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp,
|
|||
return data;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_disp_super_ied_on(struct nvkm_head *head,
|
||||
struct nvkm_ior *ior, int id, u32 khz)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &head->disp->engine.subdev;
|
||||
struct nvkm_bios *bios = subdev->device->bios;
|
||||
struct nvkm_outp *outp = ior->asy.outp;
|
||||
struct nvbios_ocfg iedtrs;
|
||||
struct nvbios_outp iedt;
|
||||
u8 ver, hdr, cnt, len, flags = 0x00;
|
||||
u32 data;
|
||||
|
||||
if (!outp) {
|
||||
IOR_DBG(ior, "nothing to attach");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Lookup IED table for the device. */
|
||||
data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
/* Lookup IEDT runtime settings for the current configuration. */
|
||||
if (ior->type == SOR) {
|
||||
if (ior->asy.proto == LVDS) {
|
||||
if (head->asy.or.depth == 24)
|
||||
flags |= 0x02;
|
||||
}
|
||||
if (ior->asy.link == 3)
|
||||
flags |= 0x01;
|
||||
}
|
||||
|
||||
data = nvbios_ocfg_match(bios, data, ior->asy.proto_evo, flags,
|
||||
&ver, &hdr, &cnt, &len, &iedtrs);
|
||||
if (!data) {
|
||||
OUTP_DBG(outp, "missing IEDT RS for %02x:%02x",
|
||||
ior->asy.proto_evo, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Execute the OnInt[23] script for the current frequency. */
|
||||
data = nvbios_oclk_match(bios, iedtrs.clkcmp[id], khz);
|
||||
if (!data) {
|
||||
OUTP_DBG(outp, "missing IEDT RSS %d for %02x:%02x %d khz",
|
||||
id, ior->asy.proto_evo, flags, khz);
|
||||
return;
|
||||
}
|
||||
|
||||
nvbios_init(subdev, data,
|
||||
init.outp = &outp->info;
|
||||
init.or = ior->id;
|
||||
init.link = ior->asy.link;
|
||||
init.head = head->id;
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id)
|
||||
{
|
||||
|
@ -154,6 +210,20 @@ nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id)
|
|||
);
|
||||
}
|
||||
|
||||
static struct nvkm_ior *
|
||||
nv50_disp_super_ior_asy(struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_ior *ior;
|
||||
list_for_each_entry(ior, &head->disp->ior, head) {
|
||||
if (ior->asy.head & (1 << head->id)) {
|
||||
HEAD_DBG(head, "to %s", ior->name);
|
||||
return ior;
|
||||
}
|
||||
}
|
||||
HEAD_DBG(head, "nothing to attach");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct nvkm_ior *
|
||||
nv50_disp_super_ior_arm(struct nvkm_head *head)
|
||||
{
|
||||
|
@ -327,58 +397,40 @@ nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head)
|
|||
}
|
||||
|
||||
static void
|
||||
nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
|
||||
struct dcb_output *outp, u32 pclk)
|
||||
nv50_disp_super_2_2_dp(struct nvkm_head *head, struct nvkm_ior *ior)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
const int link = !(outp->sorconf.link & 1);
|
||||
const int or = ffs(outp->or) - 1;
|
||||
const u32 soff = ( or * 0x800);
|
||||
const u32 loff = (link * 0x080) + soff;
|
||||
const u32 ctrl = nvkm_rd32(device, 0x610794 + (or * 8));
|
||||
const u32 symbol = 100000;
|
||||
const s32 vactive = nvkm_rd32(device, 0x610af8 + (head * 0x540)) & 0xffff;
|
||||
const s32 vblanke = nvkm_rd32(device, 0x610ae8 + (head * 0x540)) & 0xffff;
|
||||
const s32 vblanks = nvkm_rd32(device, 0x610af0 + (head * 0x540)) & 0xffff;
|
||||
u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff);
|
||||
u32 clksor = nvkm_rd32(device, 0x614300 + soff);
|
||||
struct nvkm_subdev *subdev = &head->disp->engine.subdev;
|
||||
const u32 khz = head->asy.hz / 1000;
|
||||
const u32 linkKBps = ior->dp.bw * 27000;
|
||||
const u32 symbol = 100000;
|
||||
int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
|
||||
int TU, VTUi, VTUf, VTUa;
|
||||
u64 link_data_rate, link_ratio, unk;
|
||||
u32 best_diff = 64 * symbol;
|
||||
u32 link_nr, link_bw, bits;
|
||||
u64 value;
|
||||
|
||||
link_bw = (clksor & 0x000c0000) ? 270000 : 162000;
|
||||
link_nr = hweight32(dpctrl & 0x000f0000);
|
||||
u64 h, v;
|
||||
|
||||
/* symbols/hblank - algorithm taken from comments in tegra driver */
|
||||
value = vblanke + vactive - vblanks - 7;
|
||||
value = value * link_bw;
|
||||
do_div(value, pclk);
|
||||
value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
|
||||
nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, value);
|
||||
h = head->asy.hblanke + head->asy.htotal - head->asy.hblanks - 7;
|
||||
h = h * linkKBps;
|
||||
do_div(h, khz);
|
||||
h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr);
|
||||
|
||||
/* symbols/vblank - algorithm taken from comments in tegra driver */
|
||||
value = vblanks - vblanke - 25;
|
||||
value = value * link_bw;
|
||||
do_div(value, pclk);
|
||||
value = value - ((36 / link_nr) + 3) - 1;
|
||||
nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, value);
|
||||
v = head->asy.vblanks - head->asy.vblanke - 25;
|
||||
v = v * linkKBps;
|
||||
do_div(v, khz);
|
||||
v = v - ((36 / ior->dp.nr) + 3) - 1;
|
||||
|
||||
ior->func->dp.audio_sym(ior, head->id, h, v);
|
||||
|
||||
/* watermark / activesym */
|
||||
if ((ctrl & 0xf0000) == 0x60000) bits = 30;
|
||||
else if ((ctrl & 0xf0000) == 0x50000) bits = 24;
|
||||
else bits = 18;
|
||||
|
||||
link_data_rate = (pclk * bits / 8) / link_nr;
|
||||
link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr;
|
||||
|
||||
/* calculate ratio of packed data rate to link symbol rate */
|
||||
link_ratio = link_data_rate * symbol;
|
||||
do_div(link_ratio, link_bw);
|
||||
do_div(link_ratio, linkKBps);
|
||||
|
||||
for (TU = 64; TU >= 32; TU--) {
|
||||
for (TU = 64; ior->func->dp.activesym && TU >= 32; TU--) {
|
||||
/* calculate average number of valid symbols in each TU */
|
||||
u32 tu_valid = link_ratio * TU;
|
||||
u32 calc, diff;
|
||||
|
@ -429,9 +481,15 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
|
|||
}
|
||||
}
|
||||
|
||||
if (!bestTU) {
|
||||
nvkm_error(subdev, "unable to find suitable dp config\n");
|
||||
return;
|
||||
if (ior->func->dp.activesym) {
|
||||
if (!bestTU) {
|
||||
nvkm_error(subdev, "unable to determine dp config\n");
|
||||
return;
|
||||
}
|
||||
ior->func->dp.activesym(ior, head->id, bestTU,
|
||||
bestVTUa, bestVTUf, bestVTUi);
|
||||
} else {
|
||||
bestTU = 64;
|
||||
}
|
||||
|
||||
/* XXX close to vbios numbers, but not right */
|
||||
|
@ -441,102 +499,61 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
|
|||
do_div(unk, symbol);
|
||||
unk += 6;
|
||||
|
||||
nvkm_mask(device, 0x61c10c + loff, 0x000001fc, bestTU << 2);
|
||||
nvkm_mask(device, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 |
|
||||
bestVTUf << 16 |
|
||||
bestVTUi << 8 | unk);
|
||||
ior->func->dp.watermark(ior, head->id, unk);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_disp_intr_unk20_2(struct nv50_disp *disp, int head)
|
||||
void
|
||||
nv50_disp_super_2_2(struct nv50_disp *disp, struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
struct nvkm_output *outp;
|
||||
u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff;
|
||||
u32 hval, hreg = 0x614200 + (head * 0x800);
|
||||
u32 oval, oreg;
|
||||
u32 mask, conf;
|
||||
const u32 khz = head->asy.hz / 1000;
|
||||
struct nvkm_outp *outp;
|
||||
struct nvkm_ior *ior;
|
||||
|
||||
outp = exec_clkcmp(disp, head, 0xff, pclk, &conf);
|
||||
if (!outp)
|
||||
/* Determine which OR, if any, we're attaching from the head. */
|
||||
HEAD_DBG(head, "supervisor 2.2");
|
||||
ior = nv50_disp_super_ior_asy(head);
|
||||
if (!ior)
|
||||
return;
|
||||
|
||||
/* we allow both encoder attach and detach operations to occur
|
||||
* within a single supervisor (ie. modeset) sequence. the
|
||||
* encoder detach scripts quite often switch off power to the
|
||||
* lanes, which requires the link to be re-trained.
|
||||
/* For some reason, NVIDIA decided not to:
|
||||
*
|
||||
* this is not generally an issue as the sink "must" (heh)
|
||||
* signal an irq when it's lost sync so the driver can
|
||||
* re-train.
|
||||
* A) Give dual-link LVDS a separate EVO protocol, like for TMDS.
|
||||
* and
|
||||
* B) Use SetControlOutputResource.PixelDepth on LVDS.
|
||||
*
|
||||
* however, on some boards, if one does not configure at least
|
||||
* the gpu side of the link *before* attaching, then various
|
||||
* things can go horribly wrong (PDISP disappearing from mmio,
|
||||
* third supervisor never happens, etc).
|
||||
*
|
||||
* the solution is simply to retrain here, if necessary. last
|
||||
* i checked, the binary driver userspace does not appear to
|
||||
* trigger this situation (it forces an UPDATE between steps).
|
||||
* Override the values we usually read from HW with the same
|
||||
* data we pass though an ioctl instead.
|
||||
*/
|
||||
if (outp->info.type == DCB_OUTPUT_DP) {
|
||||
u32 soff = (ffs(outp->info.or) - 1) * 0x08;
|
||||
u32 ctrl, datarate;
|
||||
|
||||
if (outp->info.location == 0) {
|
||||
ctrl = nvkm_rd32(device, 0x610794 + soff);
|
||||
soff = 1;
|
||||
} else {
|
||||
ctrl = nvkm_rd32(device, 0x610b80 + soff);
|
||||
soff = 2;
|
||||
}
|
||||
|
||||
switch ((ctrl & 0x000f0000) >> 16) {
|
||||
case 6: datarate = pclk * 30; break;
|
||||
case 5: datarate = pclk * 24; break;
|
||||
case 2:
|
||||
default:
|
||||
datarate = pclk * 18;
|
||||
break;
|
||||
}
|
||||
|
||||
if (nvkm_output_dp_train(outp, datarate / soff))
|
||||
OUTP_ERR(outp, "link not trained before attach");
|
||||
if (ior->type == SOR && ior->asy.proto == LVDS) {
|
||||
head->asy.or.depth = (disp->sor.lvdsconf & 0x0200) ? 24 : 18;
|
||||
ior->asy.link = (disp->sor.lvdsconf & 0x0100) ? 3 : 1;
|
||||
}
|
||||
|
||||
exec_clkcmp(disp, head, 0, pclk, &conf);
|
||||
/* Handle any link training, etc. */
|
||||
if ((outp = ior->asy.outp) && outp->func->acquire)
|
||||
outp->func->acquire(outp);
|
||||
|
||||
if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) {
|
||||
oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800;
|
||||
oval = 0x00000000;
|
||||
hval = 0x00000000;
|
||||
mask = 0xffffffff;
|
||||
} else
|
||||
if (!outp->info.location) {
|
||||
if (outp->info.type == DCB_OUTPUT_DP)
|
||||
nv50_disp_intr_unk20_2_dp(disp, head, &outp->info, pclk);
|
||||
oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800;
|
||||
oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
|
||||
hval = 0x00000000;
|
||||
mask = 0x00000707;
|
||||
} else {
|
||||
oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800;
|
||||
oval = 0x00000001;
|
||||
hval = 0x00000001;
|
||||
mask = 0x00000707;
|
||||
}
|
||||
/* Execute OnInt2 IED script. */
|
||||
nv50_disp_super_ied_on(head, ior, 0, khz);
|
||||
|
||||
nvkm_mask(device, hreg, 0x0000000f, hval);
|
||||
nvkm_mask(device, oreg, mask, oval);
|
||||
/* Program RG clock divider. */
|
||||
head->func->rgclk(head, ior->asy.rgdiv);
|
||||
|
||||
nv50_disp_dptmds_war_2(disp, &outp->info);
|
||||
/* Mode-specific internal DP configuration. */
|
||||
if (ior->type == SOR && ior->asy.proto == DP)
|
||||
nv50_disp_super_2_2_dp(head, ior);
|
||||
|
||||
/* OR-specific handling. */
|
||||
ior->func->clock(ior);
|
||||
if (ior->func->war_2)
|
||||
ior->func->war_2(ior);
|
||||
}
|
||||
|
||||
void
|
||||
nv50_disp_super_2_1(struct nv50_disp *disp, struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_devinit *devinit = disp->base.engine.subdev.device->devinit;
|
||||
u32 khz = head->asy.hz / 1000;
|
||||
const u32 khz = head->asy.hz / 1000;
|
||||
HEAD_DBG(head, "supervisor 2.1 - %d khz", khz);
|
||||
if (khz)
|
||||
nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head->id, khz);
|
||||
|
@ -636,7 +653,7 @@ nv50_disp_super(struct work_struct *work)
|
|||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
if (!(super & (0x00000080 << head->id)))
|
||||
continue;
|
||||
nv50_disp_intr_unk20_2(disp, head->id);
|
||||
nv50_disp_super_2_2(disp, head);
|
||||
}
|
||||
} else
|
||||
if (disp->super & 0x00000040) {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define __NV50_DISP_H__
|
||||
#define nv50_disp(p) container_of((p), struct nv50_disp, base)
|
||||
#include "priv.h"
|
||||
#include "dp.h"
|
||||
struct nvkm_head;
|
||||
|
||||
struct nv50_disp {
|
||||
|
@ -30,6 +29,7 @@ void nv50_disp_super_1(struct nv50_disp *);
|
|||
void nv50_disp_super_1_0(struct nv50_disp *, struct nvkm_head *);
|
||||
void nv50_disp_super_2_0(struct nv50_disp *, struct nvkm_head *);
|
||||
void nv50_disp_super_2_1(struct nv50_disp *, struct nvkm_head *);
|
||||
void nv50_disp_super_2_2(struct nv50_disp *, struct nvkm_head *);
|
||||
|
||||
int nv50_disp_new_(const struct nv50_disp_func *, struct nvkm_device *,
|
||||
int index, int heads, struct nvkm_disp **);
|
||||
|
|
|
@ -39,6 +39,7 @@ struct nvkm_outp_func {
|
|||
void *(*dtor)(struct nvkm_outp *);
|
||||
void (*init)(struct nvkm_outp *);
|
||||
void (*fini)(struct nvkm_outp *);
|
||||
int (*acquire)(struct nvkm_outp *);
|
||||
void (*release)(struct nvkm_outp *, struct nvkm_ior *);
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,14 @@
|
|||
#include <subdev/i2c.h>
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static void
|
||||
nv50_pior_clock(struct nvkm_ior *pior)
|
||||
{
|
||||
struct nvkm_device *device = pior->disp->engine.subdev.device;
|
||||
const u32 poff = nv50_ior_base(pior);
|
||||
nvkm_mask(device, 0x614380 + poff, 0x00000707, 0x00000001);
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_pior_dp_links(struct nvkm_ior *pior, struct nvkm_i2c_aux *aux)
|
||||
{
|
||||
|
@ -110,6 +118,7 @@ static const struct nvkm_ior_func
|
|||
nv50_pior = {
|
||||
.state = nv50_pior_state,
|
||||
.power = nv50_pior_power,
|
||||
.clock = nv50_pior_clock,
|
||||
.dp = {
|
||||
.links = nv50_pior_dp_links,
|
||||
},
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
#include "rootnv50.h"
|
||||
#include "dmacnv50.h"
|
||||
#include "dp.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ static const struct nvkm_ior_func
|
|||
g84_sor = {
|
||||
.state = nv50_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = g84_hdmi_ctrl,
|
||||
},
|
||||
|
|
|
@ -26,6 +26,35 @@
|
|||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
void
|
||||
g94_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
nvkm_mask(device, 0x61c128 + loff, 0x0000003f, watermark);
|
||||
}
|
||||
|
||||
void
|
||||
g94_sor_dp_activesym(struct nvkm_ior *sor, int head,
|
||||
u8 TU, u8 VTUa, u8 VTUf, u8 VTUi)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
nvkm_mask(device, 0x61c10c + loff, 0x000001fc, TU << 2);
|
||||
nvkm_mask(device, 0x61c128 + loff, 0x010f7f00, VTUa << 24 |
|
||||
VTUf << 16 |
|
||||
VTUi << 8);
|
||||
}
|
||||
|
||||
void
|
||||
g94_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, h);
|
||||
nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, v);
|
||||
}
|
||||
|
||||
void
|
||||
g94_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu)
|
||||
{
|
||||
|
@ -123,6 +152,23 @@ nv50_disp_dptmds_war_needed(struct nv50_disp *disp, struct dcb_output *outp)
|
|||
|
||||
}
|
||||
|
||||
static bool
|
||||
g94_sor_war_needed(struct nvkm_ior *sor)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
if (sor->asy.proto == 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)
|
||||
{
|
||||
|
@ -191,13 +237,13 @@ nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nv50_disp_dptmds_war_2(struct nv50_disp *disp, struct dcb_output *outp)
|
||||
static void
|
||||
g94_sor_war_2(struct nvkm_ior *sor)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const u32 soff = __ffs(outp->or) * 0x800;
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
|
||||
if (!nv50_disp_dptmds_war_needed(disp, outp))
|
||||
if (!g94_sor_war_needed(sor))
|
||||
return;
|
||||
|
||||
nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000);
|
||||
|
@ -245,12 +291,17 @@ static const struct nvkm_ior_func
|
|||
g94_sor = {
|
||||
.state = g94_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.war_2 = g94_sor_war_2,
|
||||
.dp = {
|
||||
.lanes = { 2, 1, 0, 3},
|
||||
.links = g94_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = g94_sor_dp_pattern,
|
||||
.drive = g94_sor_dp_drive,
|
||||
.audio_sym = g94_sor_dp_audio_sym,
|
||||
.activesym = g94_sor_dp_activesym,
|
||||
.watermark = g94_sor_dp_watermark,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,23 @@
|
|||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
void
|
||||
gf119_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 hoff = head * 0x800;
|
||||
nvkm_mask(device, 0x616610 + hoff, 0x0800003f, 0x08000000 | watermark);
|
||||
}
|
||||
|
||||
void
|
||||
gf119_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 hoff = head * 0x800;
|
||||
nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, h);
|
||||
nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, v);
|
||||
}
|
||||
|
||||
void
|
||||
gf119_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable)
|
||||
{
|
||||
|
@ -99,6 +116,19 @@ gf119_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
gf119_sor_clock(struct nvkm_ior *sor)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const int div = sor->asy.link == 3;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
if (sor->asy.proto == TMDS) {
|
||||
/* NFI why, but this sets DP_LINK_BW_2_7 when using TMDS. */
|
||||
nvkm_mask(device, 0x612300 + soff, 0x007c0000, 0x0a << 18);
|
||||
}
|
||||
nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div);
|
||||
}
|
||||
|
||||
void
|
||||
gf119_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
|
||||
{
|
||||
|
@ -126,6 +156,7 @@ static const struct nvkm_ior_func
|
|||
gf119_sor = {
|
||||
.state = gf119_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = gf119_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gf119_hdmi_ctrl,
|
||||
},
|
||||
|
@ -136,6 +167,8 @@ gf119_sor = {
|
|||
.pattern = gf119_sor_dp_pattern,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
.audio = gf119_sor_dp_audio,
|
||||
.audio_sym = gf119_sor_dp_audio_sym,
|
||||
.watermark = gf119_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gf119_hda_hpd,
|
||||
|
|
|
@ -25,6 +25,7 @@ static const struct nvkm_ior_func
|
|||
gk104_sor = {
|
||||
.state = gf119_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = gf119_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gk104_hdmi_ctrl,
|
||||
},
|
||||
|
@ -36,6 +37,8 @@ gk104_sor = {
|
|||
.drive = gf119_sor_dp_drive,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
.audio = gf119_sor_dp_audio,
|
||||
.audio_sym = gf119_sor_dp_audio_sym,
|
||||
.watermark = gf119_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gf119_hda_hpd,
|
||||
|
|
|
@ -39,6 +39,7 @@ static const struct nvkm_ior_func
|
|||
gm107_sor = {
|
||||
.state = gf119_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = gf119_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gk104_hdmi_ctrl,
|
||||
},
|
||||
|
@ -50,6 +51,8 @@ gm107_sor = {
|
|||
.drive = gf119_sor_dp_drive,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
.audio = gf119_sor_dp_audio,
|
||||
.audio_sym = gf119_sor_dp_audio_sym,
|
||||
.watermark = gf119_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gf119_hda_hpd,
|
||||
|
|
|
@ -96,6 +96,7 @@ gm200_sor = {
|
|||
},
|
||||
.state = gf119_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = gf119_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gk104_hdmi_ctrl,
|
||||
},
|
||||
|
@ -107,6 +108,8 @@ gm200_sor = {
|
|||
.drive = gm200_sor_dp_drive,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
.audio = gf119_sor_dp_audio,
|
||||
.audio_sym = gf119_sor_dp_audio_sym,
|
||||
.watermark = gf119_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gf119_hda_hpd,
|
||||
|
|
|
@ -41,6 +41,7 @@ static const struct nvkm_ior_func
|
|||
gt215_sor = {
|
||||
.state = g94_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gt215_hdmi_ctrl,
|
||||
},
|
||||
|
@ -51,6 +52,9 @@ gt215_sor = {
|
|||
.pattern = g94_sor_dp_pattern,
|
||||
.drive = g94_sor_dp_drive,
|
||||
.audio = gt215_sor_dp_audio,
|
||||
.audio_sym = g94_sor_dp_audio_sym,
|
||||
.activesym = g94_sor_dp_activesym,
|
||||
.watermark = g94_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gt215_hda_hpd,
|
||||
|
|
|
@ -25,6 +25,7 @@ static const struct nvkm_ior_func
|
|||
mcp77_sor = {
|
||||
.state = g94_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = g84_hdmi_ctrl,
|
||||
},
|
||||
|
@ -34,6 +35,9 @@ mcp77_sor = {
|
|||
.power = g94_sor_dp_power,
|
||||
.pattern = g94_sor_dp_pattern,
|
||||
.drive = g94_sor_dp_drive,
|
||||
.audio_sym = g94_sor_dp_audio_sym,
|
||||
.activesym = g94_sor_dp_activesym,
|
||||
.watermark = g94_sor_dp_watermark,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ static const struct nvkm_ior_func
|
|||
mcp89_sor = {
|
||||
.state = g94_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gt215_hdmi_ctrl,
|
||||
},
|
||||
|
@ -35,6 +36,9 @@ mcp89_sor = {
|
|||
.pattern = g94_sor_dp_pattern,
|
||||
.drive = g94_sor_dp_drive,
|
||||
.audio = gt215_sor_dp_audio,
|
||||
.audio_sym = g94_sor_dp_audio_sym,
|
||||
.activesym = g94_sor_dp_activesym,
|
||||
.watermark = g94_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gt215_hda_hpd,
|
||||
|
|
|
@ -25,6 +25,15 @@
|
|||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
void
|
||||
nv50_sor_clock(struct nvkm_ior *sor)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const int div = sor->asy.link == 3;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
nvkm_mask(device, 0x614300 + soff, 0x00000707, (div << 8) | div);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_power_wait(struct nvkm_device *device, u32 soff)
|
||||
{
|
||||
|
@ -79,6 +88,7 @@ static const struct nvkm_ior_func
|
|||
nv50_sor = {
|
||||
.state = nv50_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
};
|
||||
|
||||
int
|
||||
|
|
Loading…
Reference in New Issue