summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/engine/disp/nv50.c')
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv50.c129
1 files changed, 111 insertions, 18 deletions
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
index f8cbb512132f..2df3a937037d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
@@ -29,6 +29,7 @@
#include <core/enum.h>
#include <nvif/unpack.h>
#include <nvif/class.h>
+#include <nvif/event.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
@@ -82,6 +83,71 @@ nv50_disp_chan_destroy(struct nv50_disp_chan *chan)
nouveau_namedb_destroy(&chan->base);
}
+static void
+nv50_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, 0x610028, 0x00000001 << index, 0x00000000 << index);
+}
+
+static void
+nv50_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, 0x610028, 0x00000001 << index, 0x00000001 << index);
+}
+
+void
+nv50_disp_chan_uevent_send(struct nv50_disp_priv *priv, int chid)
+{
+ struct nvif_notify_uevent_rep {
+ } rep;
+
+ nvkm_event_send(&priv->uevent, 1, chid, &rep, sizeof(rep));
+}
+
+int
+nv50_disp_chan_uevent_ctor(struct nouveau_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ struct nv50_disp_dmac *dmac = (void *)object;
+ union {
+ struct nvif_notify_uevent_req none;
+ } *args = data;
+ int ret;
+
+ if (nvif_unvers(args->none)) {
+ notify->size = sizeof(struct nvif_notify_uevent_rep);
+ notify->types = 1;
+ notify->index = dmac->base.chid;
+ return 0;
+ }
+
+ return ret;
+}
+
+const struct nvkm_event_func
+nv50_disp_chan_uevent = {
+ .ctor = nv50_disp_chan_uevent_ctor,
+ .init = nv50_disp_chan_uevent_init,
+ .fini = nv50_disp_chan_uevent_fini,
+};
+
+int
+nv50_disp_chan_ntfy(struct nouveau_object *object, u32 type,
+ struct nvkm_event **pevent)
+{
+ struct nv50_disp_priv *priv = (void *)object->engine;
+ switch (type) {
+ case NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT:
+ *pevent = &priv->uevent;
+ return 0;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
int
nv50_disp_chan_map(struct nouveau_object *object, u64 *addr, u32 *size)
{
@@ -195,7 +261,7 @@ nv50_disp_dmac_init(struct nouveau_object *object)
return ret;
/* enable error reporting */
- nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00010001 << chid);
+ nv_mask(priv, 0x610028, 0x00010000 << chid, 0x00010000 << chid);
/* initialise channel for dma command submission */
nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push);
@@ -232,7 +298,7 @@ nv50_disp_dmac_fini(struct nouveau_object *object, bool suspend)
return -EBUSY;
}
- /* disable error reporting */
+ /* disable error reporting and completion notifications */
nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00000000 << chid);
return nv50_disp_chan_fini(&dmac->base, suspend);
@@ -454,7 +520,7 @@ nv50_disp_mast_init(struct nouveau_object *object)
return ret;
/* enable error reporting */
- nv_mask(priv, 0x610028, 0x00010001, 0x00010001);
+ nv_mask(priv, 0x610028, 0x00010000, 0x00010000);
/* attempt to unstick channel from some unknown state */
if ((nv_rd32(priv, 0x610200) & 0x009f0000) == 0x00020000)
@@ -494,7 +560,7 @@ nv50_disp_mast_fini(struct nouveau_object *object, bool suspend)
return -EBUSY;
}
- /* disable error reporting */
+ /* disable error reporting and completion notifications */
nv_mask(priv, 0x610028, 0x00010001, 0x00000000);
return nv50_disp_chan_fini(&mast->base, suspend);
@@ -507,6 +573,7 @@ nv50_disp_mast_ofuncs = {
.base.init = nv50_disp_mast_init,
.base.fini = nv50_disp_mast_fini,
.base.map = nv50_disp_chan_map,
+ .base.ntfy = nv50_disp_chan_ntfy,
.base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32,
.chid = 0,
@@ -607,6 +674,7 @@ nv50_disp_sync_ofuncs = {
.base.dtor = nv50_disp_dmac_dtor,
.base.init = nv50_disp_dmac_init,
.base.fini = nv50_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,
@@ -696,6 +764,7 @@ nv50_disp_ovly_ofuncs = {
.base.dtor = nv50_disp_dmac_dtor,
.base.init = nv50_disp_dmac_init,
.base.fini = nv50_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,
@@ -813,6 +882,7 @@ nv50_disp_oimm_ofuncs = {
.base.dtor = nv50_disp_pioc_dtor,
.base.init = nv50_disp_pioc_init,
.base.fini = nv50_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,
@@ -860,6 +930,7 @@ nv50_disp_curs_ofuncs = {
.base.dtor = nv50_disp_pioc_dtor,
.base.init = nv50_disp_pioc_init,
.base.fini = nv50_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,
@@ -1559,7 +1630,7 @@ nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head)
}
static void
-nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
+nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, int head,
struct dcb_output *outp, u32 pclk)
{
const int link = !(outp->sorconf.link & 1);
@@ -1568,24 +1639,36 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
const u32 loff = (link * 0x080) + soff;
const u32 ctrl = nv_rd32(priv, 0x610794 + (or * 8));
const u32 symbol = 100000;
- u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x0000f0000;
+ const s32 vactive = nv_rd32(priv, 0x610af8 + (head * 0x540)) & 0xffff;
+ const s32 vblanke = nv_rd32(priv, 0x610ae8 + (head * 0x540)) & 0xffff;
+ const s32 vblanks = nv_rd32(priv, 0x610af0 + (head * 0x540)) & 0xffff;
+ u32 dpctrl = nv_rd32(priv, 0x61c10c + loff);
u32 clksor = nv_rd32(priv, 0x614300 + soff);
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;
-
- /* calculate packed data rate for each lane */
- if (dpctrl > 0x00030000) link_nr = 4;
- else if (dpctrl > 0x00010000) link_nr = 2;
- else link_nr = 1;
-
- if (clksor & 0x000c0000)
- link_bw = 270000;
- else
- link_bw = 162000;
-
+ u64 value;
+
+ link_bw = (clksor & 0x000c0000) ? 270000 : 162000;
+ link_nr = hweight32(dpctrl & 0x000f0000);
+
+ /* 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, 0x61c1e8 + soff, 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, 0x61c1ec + soff, 0x00ffffff, value);
+
+ /* watermark / activesym */
if ((ctrl & 0xf0000) == 0x60000) bits = 30;
else if ((ctrl & 0xf0000) == 0x50000) bits = 24;
else bits = 18;
@@ -1731,7 +1814,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
} else
if (!outp->info.location) {
if (outp->info.type == DCB_OUTPUT_DP)
- nv50_disp_intr_unk20_2_dp(priv, &outp->info, pclk);
+ nv50_disp_intr_unk20_2_dp(priv, head, &outp->info, pclk);
oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800;
oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
hval = 0x00000000;
@@ -1847,6 +1930,12 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
intr0 &= ~(0x00010000 << chid);
}
+ while (intr0 & 0x0000001f) {
+ u32 chid = __ffs(intr0 & 0x0000001f);
+ nv50_disp_chan_uevent_send(priv, chid);
+ intr0 &= ~(0x00000001 << chid);
+ }
+
if (intr1 & 0x00000004) {
nouveau_disp_vblank(&priv->base, 0);
nv_wr32(priv, 0x610024, 0x00000004);
@@ -1881,6 +1970,10 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = nv50_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;