diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm')
25 files changed, 321 insertions, 246 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c index 524a24eae1a0..0c0310498afd 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c @@ -25,6 +25,7 @@ #include "rootnv50.h" #include <core/client.h> +#include <core/notify.h> #include <core/ramht.h> #include <engine/dma.h> diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c index 7d2d929eec16..8392303b77ed 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c @@ -22,6 +22,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) { struct nvkm_device *device = dac->disp->engine.subdev.device; @@ -44,6 +52,7 @@ gf119_dac = { .state = gf119_dac_state, .power = nv50_dac_power, .sense = nv50_dac_sense, + .clock = gf119_dac_clock, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c index 64de64fdc9d1..119cd02a2000 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c @@ -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 diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c index 12e52529413c..7c5bed29ffef 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -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, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h index 34e58b70a394..59173c290525 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h @@ -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 **); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c index d25b3b7f4fb8..f4a05549f642 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c @@ -139,120 +139,6 @@ exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf) } 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) { struct nvkm_device *device = disp->base.engine.subdev.device; @@ -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) { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h index 829a0a8cfb2e..b04c49d2eeeb 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h @@ -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 *); }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c index d2bd6bb4a621..b33552757647 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c @@ -40,6 +40,13 @@ gf119_head_vblank_get(struct nvkm_head *head) } 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) { struct nvkm_device *device = head->disp->engine.subdev.device; @@ -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, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c index d4a9879f0d0a..c80d06d5168f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c @@ -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, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index 3a6e01f5a0e5..d6bfaa53f96b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -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); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c index ebe7657bf2af..f21e0e92d6f1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c @@ -130,6 +130,62 @@ nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp, } 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) { struct nvkm_outp *outp = ior->arm.outp; @@ -155,6 +211,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) { struct nvkm_ior *ior; @@ -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; - /* watermark / activesym */ - if ((ctrl & 0xf0000) == 0x60000) bits = 30; - else if ((ctrl & 0xf0000) == 0x50000) bits = 24; - else bits = 18; + ior->func->dp.audio_sym(ior, head->id, h, v); - link_data_rate = (pclk * bits / 8) / link_nr; + /* watermark / activesym */ + 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. - * - * 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. + /* For some reason, NVIDIA decided not to: * - * 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). + * A) Give dual-link LVDS a separate EVO protocol, like for TMDS. + * and + * B) Use SetControlOutputResource.PixelDepth on LVDS. * - * 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); + + /* Program RG clock divider. */ + head->func->rgclk(head, ior->asy.rgdiv); - nvkm_mask(device, hreg, 0x0000000f, hval); - nvkm_mask(device, oreg, mask, oval); + /* Mode-specific internal DP configuration. */ + if (ior->type == SOR && ior->asy.proto == DP) + nv50_disp_super_2_2_dp(head, ior); - nv50_disp_dptmds_war_2(disp, &outp->info); + /* 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) { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h index 310a568c3fdb..b6092b8b528a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h @@ -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 **); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h index da4347e27e65..7655d9293911 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h @@ -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 *); }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c index dc59c319377e..a1f24ef222c1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c @@ -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, }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c index 4c7b7090b1a0..1208524aae14 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c @@ -23,6 +23,7 @@ */ #include "rootnv50.h" #include "dmacnv50.h" +#include "dp.h" #include "head.h" #include "ior.h" diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c index 8913b5f58103..ec3a7db08118 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c @@ -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, }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c index 5abf563b0e84..bdace3852b37 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c @@ -27,6 +27,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) { struct nvkm_device *device = sor->disp->engine.subdev.device; @@ -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, }, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c index d37cd037ee03..5f8306b00f71 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c @@ -26,6 +26,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) { struct nvkm_device *device = sor->disp->engine.subdev.device; @@ -100,6 +117,19 @@ gf119_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) } 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) { struct nvkm_device *device = sor->disp->engine.subdev.device; @@ -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, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c index 24fc026cd18d..b94090edaebf 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c @@ -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, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c index e467f8eaf2ee..e6965dec09c9 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c @@ -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, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c index 25528a15516b..8bc019b6ffab 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c @@ -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, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c index 5e9126e832ff..54d134d4ca1d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c @@ -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, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c index 50d6c0d98e20..8a70dd25b13a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c @@ -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, }, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c index dd76351d2ccb..eac9c5be9166 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c @@ -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, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c index 2ca600c744cd..dbb303db3178 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c @@ -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 |