diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c | 80 |
1 files changed, 65 insertions, 15 deletions
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index a4bb3c774ee1..747e64bb9c06 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -43,6 +43,31 @@ #include "nv50.h" /******************************************************************************* + * EVO channel base class + ******************************************************************************/ + +static void +nvd0_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000000 << index); +} + +static void +nvd0_disp_chan_uevent_init(struct nvkm_event *event, int types, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000001 << index); +} + +const struct nvkm_event_func +nvd0_disp_chan_uevent = { + .ctor = nv50_disp_chan_uevent_ctor, + .init = nvd0_disp_chan_uevent_init, + .fini = nvd0_disp_chan_uevent_fini, +}; + +/******************************************************************************* * EVO DMA channel base class ******************************************************************************/ @@ -77,7 +102,6 @@ nvd0_disp_dmac_init(struct nouveau_object *object) return ret; /* enable error reporting */ - nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); /* initialise channel for dma command submission */ @@ -115,7 +139,7 @@ nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend) return -EBUSY; } - /* disable error reporting */ + /* disable error reporting and completion notification */ nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); @@ -278,7 +302,6 @@ nvd0_disp_mast_init(struct nouveau_object *object) return ret; /* enable error reporting */ - nv_mask(priv, 0x610090, 0x00000001, 0x00000001); nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001); /* initialise channel for dma command submission */ @@ -313,7 +336,7 @@ nvd0_disp_mast_fini(struct nouveau_object *object, bool suspend) return -EBUSY; } - /* disable error reporting */ + /* disable error reporting and completion notification */ nv_mask(priv, 0x610090, 0x00000001, 0x00000000); nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000); @@ -326,6 +349,7 @@ nvd0_disp_mast_ofuncs = { .base.dtor = nv50_disp_dmac_dtor, .base.init = nvd0_disp_mast_init, .base.fini = nvd0_disp_mast_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -419,6 +443,7 @@ nvd0_disp_sync_ofuncs = { .base.dtor = nv50_disp_dmac_dtor, .base.init = nvd0_disp_dmac_init, .base.fini = nvd0_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -499,6 +524,7 @@ nvd0_disp_ovly_ofuncs = { .base.dtor = nv50_disp_dmac_dtor, .base.init = nvd0_disp_dmac_init, .base.fini = nvd0_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -524,7 +550,6 @@ nvd0_disp_pioc_init(struct nouveau_object *object) return ret; /* enable error reporting */ - nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); /* activate channel */ @@ -553,7 +578,7 @@ nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend) return -EBUSY; } - /* disable error reporting */ + /* disable error reporting and completion notification */ nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); @@ -570,6 +595,7 @@ nvd0_disp_oimm_ofuncs = { .base.dtor = nv50_disp_pioc_dtor, .base.init = nvd0_disp_pioc_init, .base.fini = nvd0_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -586,6 +612,7 @@ nvd0_disp_curs_ofuncs = { .base.dtor = nv50_disp_pioc_dtor, .base.init = nvd0_disp_pioc_init, .base.fini = nvd0_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -949,6 +976,9 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head, const int or = ffs(outp->or) - 1; const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020)); const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300)); + const s32 vactive = nv_rd32(priv, 0x660414 + (head * 0x300)) & 0xffff; + const s32 vblanke = nv_rd32(priv, 0x66041c + (head * 0x300)) & 0xffff; + const s32 vblanks = nv_rd32(priv, 0x660420 + (head * 0x300)) & 0xffff; const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1; const u32 hoff = (head * 0x800); @@ -956,23 +986,35 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head, const u32 loff = (link * 0x080) + soff; const u32 symbol = 100000; const u32 TU = 64; - u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x000f0000; + u32 dpctrl = nv_rd32(priv, 0x61c10c + loff); u32 clksor = nv_rd32(priv, 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); + nv_mask(priv, 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; + nv_mask(priv, 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; - if (dpctrl > 0x00030000) link_nr = 4; - else if (dpctrl > 0x00010000) link_nr = 2; - else link_nr = 1; - - link_bw = (clksor & 0x007c0000) >> 18; - link_bw *= 27000; - ratio = datarate; ratio *= symbol; do_div(ratio, link_nr * link_bw); @@ -1153,7 +1195,11 @@ nvd0_disp_intr(struct nouveau_subdev *subdev) if (intr & 0x00000001) { u32 stat = nv_rd32(priv, 0x61008c); - nv_wr32(priv, 0x61008c, stat); + while (stat) { + int chid = __ffs(stat); stat &= ~(1 << chid); + nv50_disp_chan_uevent_send(priv, chid); + nv_wr32(priv, 0x61008c, 1 << chid); + } intr &= ~0x00000001; } @@ -1209,6 +1255,10 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = nvd0_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nvd0_disp_intr; |