aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/core/engine
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/engine')
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/copy/nva3.c6
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c6
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/base.c52
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/dport.c346
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/dport.h78
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv04.c33
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv50.c371
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv50.h37
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv84.c12
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv94.c24
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nva0.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nva3.c24
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c309
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nve0.c17
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c140
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c25
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c153
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c90
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/base.c21
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c187
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c5
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c22
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c109
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c64
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv04.c16
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv10.c16
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv20.c15
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv40.c16
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv50.c53
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c33
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nve0.c44
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c7
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nv50.c40
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nvc0.c29
35 files changed, 1627 insertions, 790 deletions
diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c
index 283248c7b050..d6dc2a65ccd1 100644
--- a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/falcon.h>
#include <core/class.h>
#include <core/enum.h>
@@ -100,8 +101,9 @@ nva3_copy_intr(struct nouveau_subdev *subdev)
if (stat & 0x00000040) {
nv_error(falcon, "DISPATCH_ERROR [");
nouveau_enum_print(nva3_copy_isr_error_name, ssta);
- printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n",
- chid, inst << 12, subc, mthd, data);
+ pr_cont("] ch %d [0x%010llx %s] subc %d mthd 0x%04x data 0x%08x\n",
+ chid, inst << 12, nouveau_client_name(engctx), subc,
+ mthd, data);
nv_wo32(falcon, 0x004, 0x00000040);
stat &= ~0x00000040;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c
index b97490512723..5bc021f471f9 100644
--- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/os.h>
#include <core/enum.h>
#include <core/class.h>
@@ -126,10 +127,11 @@ nv84_crypt_intr(struct nouveau_subdev *subdev)
chid = pfifo->chid(pfifo, engctx);
if (stat) {
- nv_error(priv, "");
+ nv_error(priv, "%s", "");
nouveau_bitfield_print(nv84_crypt_intr_mask, stat);
- printk(" ch %d [0x%010llx] mthd 0x%04x data 0x%08x\n",
- chid, (u64)inst << 12, mthd, data);
+ pr_cont(" ch %d [0x%010llx %s] mthd 0x%04x data 0x%08x\n",
+ chid, (u64)inst << 12, nouveau_client_name(engctx),
+ mthd, data);
}
nv_wr32(priv, 0x102130, stat);
diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c
index 21986f3bf0c8..8bf8955051d4 100644
--- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c
+++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/os.h>
#include <core/enum.h>
#include <core/class.h>
@@ -102,8 +103,9 @@ nv98_crypt_intr(struct nouveau_subdev *subdev)
if (stat & 0x00000040) {
nv_error(priv, "DISPATCH_ERROR [");
nouveau_enum_print(nv98_crypt_isr_error_name, ssta);
- printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n",
- chid, (u64)inst << 12, subc, mthd, data);
+ pr_cont("] ch %d [0x%010llx %s] subc %d mthd 0x%04x data 0x%08x\n",
+ chid, (u64)inst << 12, nouveau_client_name(engctx),
+ subc, mthd, data);
nv_wr32(priv, 0x087004, 0x00000040);
stat &= ~0x00000040;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/base.c b/drivers/gpu/drm/nouveau/core/engine/disp/base.c
new file mode 100644
index 000000000000..7a5cae42834f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/base.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <engine/disp.h>
+
+void
+_nouveau_disp_dtor(struct nouveau_object *object)
+{
+ struct nouveau_disp *disp = (void *)object;
+ nouveau_event_destroy(&disp->vblank);
+ nouveau_engine_destroy(&disp->base);
+}
+
+int
+nouveau_disp_create_(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, int heads,
+ const char *intname, const char *extname,
+ int length, void **pobject)
+{
+ struct nouveau_disp *disp;
+ int ret;
+
+ ret = nouveau_engine_create_(parent, engine, oclass, true,
+ intname, extname, length, pobject);
+ disp = *pobject;
+ if (ret)
+ return ret;
+
+ return nouveau_event_create(heads, &disp->vblank);
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
new file mode 100644
index 000000000000..fa27b02ff829
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/dp.h>
+#include <subdev/bios/init.h>
+#include <subdev/i2c.h>
+
+#include <engine/disp.h>
+
+#include "dport.h"
+
+#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt, \
+ dp->outp->hasht, dp->outp->hashm, ##args)
+#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt, \
+ dp->outp->hasht, dp->outp->hashm, ##args)
+
+/******************************************************************************
+ * link training
+ *****************************************************************************/
+struct dp_state {
+ const struct nouveau_dp_func *func;
+ struct nouveau_disp *disp;
+ struct dcb_output *outp;
+ struct nvbios_dpout info;
+ u8 version;
+ struct nouveau_i2c_port *aux;
+ int head;
+ u8 dpcd[4];
+ int link_nr;
+ u32 link_bw;
+ u8 stat[6];
+ u8 conf[4];
+};
+
+static int
+dp_set_link_config(struct dp_state *dp)
+{
+ struct nouveau_disp *disp = dp->disp;
+ struct nouveau_bios *bios = nouveau_bios(disp);
+ struct nvbios_init init = {
+ .subdev = nv_subdev(dp->disp),
+ .bios = bios,
+ .offset = 0x0000,
+ .outp = dp->outp,
+ .crtc = dp->head,
+ .execute = 1,
+ };
+ u32 lnkcmp;
+ u8 sink[2];
+
+ DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
+
+ /* set desired link configuration on the sink */
+ sink[0] = dp->link_bw / 27000;
+ sink[1] = dp->link_nr;
+ if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
+ sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
+
+ nv_wraux(dp->aux, DPCD_LC00, sink, 2);
+
+ /* set desired link configuration on the source */
+ if ((lnkcmp = dp->info.lnkcmp)) {
+ if (dp->version < 0x30) {
+ while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp))
+ lnkcmp += 4;
+ init.offset = nv_ro16(bios, lnkcmp + 2);
+ } else {
+ while ((dp->link_bw / 27000) < nv_ro08(bios, lnkcmp))
+ lnkcmp += 3;
+ init.offset = nv_ro16(bios, lnkcmp + 1);
+ }
+
+ nvbios_exec(&init);
+ }
+
+ return dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
+ dp->link_nr, dp->link_bw / 27000,
+ dp->dpcd[DPCD_RC02] &
+ DPCD_RC02_ENHANCED_FRAME_CAP);
+}
+
+static void
+dp_set_training_pattern(struct dp_state *dp, u8 pattern)
+{
+ u8 sink_tp;
+
+ DBG("training pattern %d\n", pattern);
+ dp->func->pattern(dp->disp, dp->outp, dp->head, pattern);
+
+ nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
+ sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
+ sink_tp |= pattern;
+ nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
+}
+
+static int
+dp_link_train_commit(struct dp_state *dp)
+{
+ int i;
+
+ for (i = 0; i < dp->link_nr; i++) {
+ u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
+ u8 lpre = (lane & 0x0c) >> 2;
+ u8 lvsw = (lane & 0x03) >> 0;
+
+ dp->conf[i] = (lpre << 3) | lvsw;
+ if (lvsw == 3)
+ dp->conf[i] |= DPCD_LC03_MAX_SWING_REACHED;
+ if (lpre == 3)
+ dp->conf[i] |= DPCD_LC03_MAX_PRE_EMPHASIS_REACHED;
+
+ DBG("config lane %d %02x\n", i, dp->conf[i]);
+ dp->func->drv_ctl(dp->disp, dp->outp, dp->head, i, lvsw, lpre);
+ }
+
+ return nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4);
+}
+
+static int
+dp_link_train_update(struct dp_state *dp, u32 delay)
+{
+ int ret;
+
+ udelay(delay);
+
+ ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6);
+ if (ret)
+ return ret;
+
+ DBG("status %*ph\n", 6, dp->stat);
+ return 0;
+}
+
+static int
+dp_link_train_cr(struct dp_state *dp)
+{
+ bool cr_done = false, abort = false;
+ int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
+ int tries = 0, i;
+
+ dp_set_training_pattern(dp, 1);
+
+ do {
+ if (dp_link_train_commit(dp) ||
+ dp_link_train_update(dp, 100))
+ break;
+
+ cr_done = true;
+ for (i = 0; i < dp->link_nr; i++) {
+ u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
+ if (!(lane & DPCD_LS02_LANE0_CR_DONE)) {
+ cr_done = false;
+ if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED)
+ abort = true;
+ break;
+ }
+ }
+
+ if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) {
+ voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
+ tries = 0;
+ }
+ } while (!cr_done && !abort && ++tries < 5);
+
+ return cr_done ? 0 : -1;
+}
+
+static int
+dp_link_train_eq(struct dp_state *dp)
+{
+ bool eq_done, cr_done = true;
+ int tries = 0, i;
+
+ dp_set_training_pattern(dp, 2);
+
+ do {
+ if (dp_link_train_update(dp, 400))
+ break;
+
+ eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
+ for (i = 0; i < dp->link_nr && eq_done; i++) {
+ u8 lane = (dp->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;
+ }
+
+ if (dp_link_train_commit(dp))
+ break;
+ } while (!eq_done && cr_done && ++tries <= 5);
+
+ return eq_done ? 0 : -1;
+}
+
+static void
+dp_link_train_init(struct dp_state *dp, bool spread)
+{
+ struct nvbios_init init = {
+ .subdev = nv_subdev(dp->disp),
+ .bios = nouveau_bios(dp->disp),
+ .outp = dp->outp,
+ .crtc = dp->head,
+ .execute = 1,
+ };
+
+ /* set desired spread */
+ if (spread)
+ init.offset = dp->info.script[2];
+ else
+ init.offset = dp->info.script[3];
+ nvbios_exec(&init);
+
+ /* pre-train script */
+ init.offset = dp->info.script[0];
+ nvbios_exec(&init);
+}
+
+static void
+dp_link_train_fini(struct dp_state *dp)
+{
+ struct nvbios_init init = {
+ .subdev = nv_subdev(dp->disp),
+ .bios = nouveau_bios(dp->disp),
+ .outp = dp->outp,
+ .crtc = dp->head,
+ .execute = 1,
+ };
+
+ /* post-train script */
+ init.offset = dp->info.script[1],
+ nvbios_exec(&init);
+}
+
+int
+nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
+ struct dcb_output *outp, int head, u32 datarate)
+{
+ struct nouveau_bios *bios = nouveau_bios(disp);
+ struct nouveau_i2c *i2c = nouveau_i2c(disp);
+ struct dp_state _dp = {
+ .disp = disp,
+ .func = func,
+ .outp = outp,
+ .head = head,
+ }, *dp = &_dp;
+ const u32 bw_list[] = { 270000, 162000, 0 };
+ const u32 *link_bw = bw_list;
+ u8 hdr, cnt, len;
+ u32 data;
+ int ret;
+
+ /* find the bios displayport data relevant to this output */
+ data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version,
+ &hdr, &cnt, &len, &dp->info);
+ if (!data) {
+ ERR("bios data not found\n");
+ return -EINVAL;
+ }
+
+ /* acquire the aux channel and fetch some info about the display */
+ if (outp->location)
+ dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
+ else
+ dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index));
+ if (!dp->aux) {
+ ERR("no aux channel?!\n");
+ return -ENODEV;
+ }
+
+ ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd));
+ if (ret) {
+ ERR("failed to read DPCD\n");
+ return ret;
+ }
+
+ /* adjust required bandwidth for 8B/10B coding overhead */
+ datarate = (datarate / 8) * 10;
+
+ /* enable down-spreading and execute pre-train script from vbios */
+ dp_link_train_init(dp, dp->dpcd[3] & 0x01);
+
+ /* start off at highest link rate supported by encoder and display */
+ while (*link_bw > (dp->dpcd[1] * 27000))
+ link_bw++;
+
+ while (link_bw[0]) {
+ /* find minimum required lane count at this link rate */
+ dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT;
+ while ((dp->link_nr >> 1) * link_bw[0] > datarate)
+ dp->link_nr >>= 1;
+
+ /* drop link rate to minimum with this lane count */
+ while ((link_bw[1] * dp->link_nr) > datarate)
+ link_bw++;
+ dp->link_bw = link_bw[0];
+
+ /* program selected link configuration */
+ ret = dp_set_link_config(dp);
+ if (ret == 0) {
+ /* attempt to train the link at this configuration */
+ memset(dp->stat, 0x00, sizeof(dp->stat));
+ if (!dp_link_train_cr(dp) &&
+ !dp_link_train_eq(dp))
+ break;
+ } else
+ if (ret >= 1) {
+ /* dp_set_link_config() handled training */
+ break;
+ }
+
+ /* retry at lower rate */
+ link_bw++;
+ }
+
+ /* finish link training */
+ dp_set_training_pattern(dp, 0);
+
+ /* execute post-train script from vbios */
+ dp_link_train_fini(dp);
+ return true;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h
new file mode 100644
index 000000000000..0e1bbd18ff6c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h
@@ -0,0 +1,78 @@
+#ifndef __NVKM_DISP_DPORT_H__
+#define __NVKM_DISP_DPORT_H__
+
+/* DPCD Receiver Capabilities */
+#define DPCD_RC00 0x00000
+#define DPCD_RC00_DPCD_REV 0xff
+#define DPCD_RC01 0x00001
+#define DPCD_RC01_MAX_LINK_RATE 0xff
+#define DPCD_RC02 0x00002
+#define DPCD_RC02_ENHANCED_FRAME_CAP 0x80
+#define DPCD_RC02_MAX_LANE_COUNT 0x1f
+#define DPCD_RC03 0x00003
+#define DPCD_RC03_MAX_DOWNSPREAD 0x01
+
+/* DPCD Link Configuration */
+#define DPCD_LC00 0x00100
+#define DPCD_LC00_LINK_BW_SET 0xff
+#define DPCD_LC01 0x00101
+#define DPCD_LC01_ENHANCED_FRAME_EN 0x80
+#define DPCD_LC01_LANE_COUNT_SET 0x1f
+#define DPCD_LC02 0x00102
+#define DPCD_LC02_TRAINING_PATTERN_SET 0x03
+#define DPCD_LC03(l) ((l) + 0x00103)
+#define DPCD_LC03_MAX_PRE_EMPHASIS_REACHED 0x20
+#define DPCD_LC03_PRE_EMPHASIS_SET 0x18
+#define DPCD_LC03_MAX_SWING_REACHED 0x04
+#define DPCD_LC03_VOLTAGE_SWING_SET 0x03
+
+/* DPCD Link/Sink Status */
+#define DPCD_LS02 0x00202
+#define DPCD_LS02_LANE1_SYMBOL_LOCKED 0x40
+#define DPCD_LS02_LANE1_CHANNEL_EQ_DONE 0x20
+#define DPCD_LS02_LANE1_CR_DONE 0x10
+#define DPCD_LS02_LANE0_SYMBOL_LOCKED 0x04
+#define DPCD_LS02_LANE0_CHANNEL_EQ_DONE 0x02
+#define DPCD_LS02_LANE0_CR_DONE 0x01
+#define DPCD_LS03 0x00203
+#define DPCD_LS03_LANE3_SYMBOL_LOCKED 0x40
+#define DPCD_LS03_LANE3_CHANNEL_EQ_DONE 0x20
+#define DPCD_LS03_LANE3_CR_DONE 0x10
+#define DPCD_LS03_LANE2_SYMBOL_LOCKED 0x04
+#define DPCD_LS03_LANE2_CHANNEL_EQ_DONE 0x02
+#define DPCD_LS03_LANE2_CR_DONE 0x01
+#define DPCD_LS04 0x00204
+#define DPCD_LS04_LINK_STATUS_UPDATED 0x80
+#define DPCD_LS04_DOWNSTREAM_PORT_STATUS_CHANGED 0x40
+#define DPCD_LS04_INTERLANE_ALIGN_DONE 0x01
+#define DPCD_LS06 0x00206
+#define DPCD_LS06_LANE1_PRE_EMPHASIS 0xc0
+#define DPCD_LS06_LANE1_VOLTAGE_SWING 0x30
+#define DPCD_LS06_LANE0_PRE_EMPHASIS 0x0c
+#define DPCD_LS06_LANE0_VOLTAGE_SWING 0x03
+#define DPCD_LS07 0x00207
+#define DPCD_LS07_LANE3_PRE_EMPHASIS 0xc0
+#define DPCD_LS07_LANE3_VOLTAGE_SWING 0x30
+#define DPCD_LS07_LANE2_PRE_EMPHASIS 0x0c
+#define DPCD_LS07_LANE2_VOLTAGE_SWING 0x03
+
+struct nouveau_disp;
+struct dcb_output;
+
+struct nouveau_dp_func {
+ int (*pattern)(struct nouveau_disp *, struct dcb_output *,
+ int head, int pattern);
+ int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
+ int link_nr, int link_bw, bool enh_frame);
+ int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
+ int lane, int swing, int preem);
+};
+
+extern const struct nouveau_dp_func nv94_sor_dp_func;
+extern const struct nouveau_dp_func nvd0_sor_dp_func;
+extern const struct nouveau_dp_func nv50_pior_dp_func;
+
+int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *,
+ struct dcb_output *, int, u32);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
index 1c919f2af89f..05e903f08a36 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
@@ -24,21 +24,33 @@
#include <engine/disp.h>
+#include <core/event.h>
+#include <core/class.h>
+
struct nv04_disp_priv {
struct nouveau_disp base;
};
static struct nouveau_oclass
nv04_disp_sclass[] = {
+ { NV04_DISP_CLASS, &nouveau_object_ofuncs },
{},
};
+/*******************************************************************************
+ * Display engine implementation
+ ******************************************************************************/
+
+static void
+nv04_disp_vblank_enable(struct nouveau_event *event, int head)
+{
+ nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000001);
+}
+
static void
-nv04_disp_intr_vblank(struct nv04_disp_priv *priv, int crtc)
+nv04_disp_vblank_disable(struct nouveau_event *event, int head)
{
- struct nouveau_disp *disp = &priv->base;
- if (disp->vblank.notify)
- disp->vblank.notify(disp->vblank.data, crtc);
+ nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000000);
}
static void
@@ -49,25 +61,25 @@ nv04_disp_intr(struct nouveau_subdev *subdev)
u32 crtc1 = nv_rd32(priv, 0x602100);
if (crtc0 & 0x00000001) {
- nv04_disp_intr_vblank(priv, 0);
+ nouveau_event_trigger(priv->base.vblank, 0);
nv_wr32(priv, 0x600100, 0x00000001);
}
if (crtc1 & 0x00000001) {
- nv04_disp_intr_vblank(priv, 1);
+ nouveau_event_trigger(priv->base.vblank, 1);
nv_wr32(priv, 0x602100, 0x00000001);
}
}
static int
nv04_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
{
struct nv04_disp_priv *priv;
int ret;
- ret = nouveau_disp_create(parent, engine, oclass, "DISPLAY",
+ ret = nouveau_disp_create(parent, engine, oclass, 2, "DISPLAY",
"display", &priv);
*pobject = nv_object(priv);
if (ret)
@@ -75,6 +87,9 @@ nv04_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nv04_disp_sclass;
nv_subdev(priv)->intr = nv04_disp_intr;
+ priv->base.vblank->priv = priv;
+ priv->base.vblank->enable = nv04_disp_vblank_enable;
+ priv->base.vblank->disable = nv04_disp_vblank_disable;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
index ca1a7d76a95b..5fa13267bd9f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
@@ -27,7 +27,6 @@
#include <core/handle.h>
#include <core/class.h>
-#include <engine/software.h>
#include <engine/disp.h>
#include <subdev/bios.h>
@@ -37,7 +36,6 @@
#include <subdev/bios/pll.h>
#include <subdev/timer.h>
#include <subdev/fb.h>
-#include <subdev/bar.h>
#include <subdev/clock.h>
#include "nv50.h"
@@ -335,7 +333,7 @@ nv50_disp_sync_ctor(struct nouveau_object *parent,
struct nv50_disp_dmac *dmac;
int ret;
- if (size < sizeof(*data) || args->head > 1)
+ if (size < sizeof(*args) || args->head > 1)
return -EINVAL;
ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -374,7 +372,7 @@ nv50_disp_ovly_ctor(struct nouveau_object *parent,
struct nv50_disp_dmac *dmac;
int ret;
- if (size < sizeof(*data) || args->head > 1)
+ if (size < sizeof(*args) || args->head > 1)
return -EINVAL;
ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -543,6 +541,18 @@ nv50_disp_curs_ofuncs = {
* Base display object
******************************************************************************/
+static void
+nv50_disp_base_vblank_enable(struct nouveau_event *event, int head)
+{
+ nv_mask(event->priv, 0x61002c, (1 << head), (1 << head));
+}
+
+static void
+nv50_disp_base_vblank_disable(struct nouveau_event *event, int head)
+{
+ nv_mask(event->priv, 0x61002c, (1 << head), (0 << head));
+}
+
static int
nv50_disp_base_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
@@ -559,6 +569,9 @@ nv50_disp_base_ctor(struct nouveau_object *parent,
if (ret)
return ret;
+ priv->base.vblank->priv = priv;
+ priv->base.vblank->enable = nv50_disp_base_vblank_enable;
+ priv->base.vblank->disable = nv50_disp_base_vblank_disable;
return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht);
}
@@ -613,7 +626,7 @@ nv50_disp_base_init(struct nouveau_object *object)
nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp);
}
- /* ... EXT caps */
+ /* ... PIOR caps */
for (i = 0; i < 3; i++) {
tmp = nv_rd32(priv, 0x61e000 + (i * 0x800));
nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp);
@@ -665,6 +678,9 @@ nv50_disp_base_omthds[] = {
{ SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
{ DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
{ DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
{},
};
@@ -756,50 +772,6 @@ nv50_disp_intr_error(struct nv50_disp_priv *priv)
}
}
-static void
-nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
-{
- struct nouveau_bar *bar = nouveau_bar(priv);
- struct nouveau_disp *disp = &priv->base;
- struct nouveau_software_chan *chan, *temp;
- unsigned long flags;
-
- spin_lock_irqsave(&disp->vblank.lock, flags);
- list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) {
- if (chan->vblank.crtc != crtc)
- continue;
-
- if (nv_device(priv)->chipset >= 0xc0) {
- nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
- bar->flush(bar);
- nv_wr32(priv, 0x06000c,
- upper_32_bits(chan->vblank.offset));
- nv_wr32(priv, 0x060010,
- lower_32_bits(chan->vblank.offset));
- nv_wr32(priv, 0x060014, chan->vblank.value);
- } else {
- nv_wr32(priv, 0x001704, chan->vblank.channel);
- nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma);
- bar->flush(bar);
- if (nv_device(priv)->chipset == 0x50) {
- nv_wr32(priv, 0x001570, chan->vblank.offset);
- nv_wr32(priv, 0x001574, chan->vblank.value);
- } else {
- nv_wr32(priv, 0x060010, chan->vblank.offset);
- nv_wr32(priv, 0x060014, chan->vblank.value);
- }
- }
-
- list_del(&chan->vblank.head);
- if (disp->vblank.put)
- disp->vblank.put(disp->vblank.data, crtc);
- }
- spin_unlock_irqrestore(&disp->vblank.lock, flags);
-
- if (disp->vblank.notify)
- disp->vblank.notify(disp->vblank.data, crtc);
-}
-
static u16
exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
@@ -811,8 +783,8 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
if (outp < 4) {
type = DCB_OUTPUT_ANALOG;
mask = 0;
- } else {
- outp -= 4;
+ } else
+ if (outp < 8) {
switch (ctrl & 0x00000f00) {
case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
@@ -824,6 +796,17 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
return 0x0000;
}
+ outp -= 4;
+ } else {
+ outp = outp - 8;
+ type = 0x0010;
+ mask = 0;
+ switch (ctrl & 0x00000f00) {
+ case 0x00000000: type |= priv->pior.type[outp]; break;
+ default:
+ nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl);
+ return 0x0000;
+ }
}
mask = 0x00c0 & (mask << 6);
@@ -834,6 +817,10 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
if (!data)
return 0x0000;
+ /* off-chip encoders require matching the exact encoder type */
+ if (dcb->location != 0)
+ type |= dcb->extdev << 8;
+
return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
}
@@ -848,9 +835,11 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
u32 ctrl = 0x00000000;
int i;
+ /* DAC */
for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
ctrl = nv_rd32(priv, 0x610b5c + (i * 8));
+ /* SOR */
if (!(ctrl & (1 << head))) {
if (nv_device(priv)->chipset < 0x90 ||
nv_device(priv)->chipset == 0x92 ||
@@ -865,6 +854,13 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
}
}
+ /* PIOR */
+ if (!(ctrl & (1 << head))) {
+ for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
+ ctrl = nv_rd32(priv, 0x610b84 + (i * 8));
+ i += 8;
+ }
+
if (!(ctrl & (1 << head)))
return false;
i--;
@@ -894,13 +890,15 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
struct nvbios_outp info1;
struct nvbios_ocfg info2;
u8 ver, hdr, cnt, len;
- u16 data, conf;
u32 ctrl = 0x00000000;
+ u32 data, conf = ~0;
int i;
+ /* DAC */
for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
ctrl = nv_rd32(priv, 0x610b58 + (i * 8));
+ /* SOR */
if (!(ctrl & (1 << head))) {
if (nv_device(priv)->chipset < 0x90 ||
nv_device(priv)->chipset == 0x92 ||
@@ -915,34 +913,46 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
}
}
+ /* PIOR */
+ if (!(ctrl & (1 << head))) {
+ for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
+ ctrl = nv_rd32(priv, 0x610b80 + (i * 8));
+ i += 8;
+ }
+
if (!(ctrl & (1 << head)))
- return 0x0000;
+ return conf;
i--;
data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1);
if (!data)
- return 0x0000;
-
- switch (outp->type) {
- case DCB_OUTPUT_TMDS:
- conf = (ctrl & 0x00000f00) >> 8;
- if (pclk >= 165000)
- conf |= 0x0100;
- break;
- case DCB_OUTPUT_LVDS:
- conf = priv->sor.lvdsconf;
- break;
- case DCB_OUTPUT_DP:
+ return conf;
+
+ if (outp->location == 0) {
+ switch (outp->type) {
+ case DCB_OUTPUT_TMDS:
+ conf = (ctrl & 0x00000f00) >> 8;
+ if (pclk >= 165000)
+ conf |= 0x0100;
+ break;
+ case DCB_OUTPUT_LVDS:
+ conf = priv->sor.lvdsconf;
+ break;
+ case DCB_OUTPUT_DP:
+ conf = (ctrl & 0x00000f00) >> 8;
+ break;
+ case DCB_OUTPUT_ANALOG:
+ default:
+ conf = 0x00ff;
+ break;
+ }
+ } else {
conf = (ctrl & 0x00000f00) >> 8;
- break;
- case DCB_OUTPUT_ANALOG:
- default:
- conf = 0x00ff;
- break;
+ pclk = pclk / 2;
}
data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
- if (data) {
+ if (data && id < 0xff) {
data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
if (data) {
struct nvbios_init init = {
@@ -954,32 +964,37 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
.execute = 1,
};
- if (nvbios_exec(&init))
- return 0x0000;
- return conf;
+ nvbios_exec(&init);
}
}
- return 0x0000;
+ return conf;
}
static void
-nv50_disp_intr_unk10(struct nv50_disp_priv *priv, u32 super)
+nv50_disp_intr_unk10_0(struct nv50_disp_priv *priv, int head)
{
- int head = ffs((super & 0x00000060) >> 5) - 1;
- if (head >= 0) {
- head = ffs((super & 0x00000180) >> 7) - 1;
- if (head >= 0)
- exec_script(priv, head, 1);
- }
+ exec_script(priv, head, 1);
+}
- nv_wr32(priv, 0x610024, 0x00000010);
- nv_wr32(priv, 0x610030, 0x80000000);
+static void
+nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head)
+{
+ exec_script(priv, head, 2);
+}
+
+static void
+nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head)
+{
+ struct nouveau_clock *clk = nouveau_clock(priv);
+ u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
+ if (pclk)
+ clk->pll_set(clk, PLL_VPLL0 + head, pclk);
}
static void
-nv50_disp_intr_unk20_dp(struct nv50_disp_priv *priv,
- struct dcb_output *outp, u32 pclk)
+nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
+ struct dcb_output *outp, u32 pclk)
{
const int link = !(outp->sorconf.link & 1);
const int or = ffs(outp->or) - 1;
@@ -1085,53 +1100,54 @@ nv50_disp_intr_unk20_dp(struct nv50_disp_priv *priv,
}
static void
-nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super)
+nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
{
struct dcb_output outp;
- u32 addr, mask, data;
- int head;
+ u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
+ u32 hval, hreg = 0x614200 + (head * 0x800);
+ u32 oval, oreg;
+ u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
+ if (conf != ~0) {
+ if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) {
+ u32 soff = (ffs(outp.or) - 1) * 0x08;
+ u32 ctrl = nv_rd32(priv, 0x610798 + soff);
+ u32 datarate;
+
+ switch ((ctrl & 0x000f0000) >> 16) {
+ case 6: datarate = pclk * 30 / 8; break;
+ case 5: datarate = pclk * 24 / 8; break;
+ case 2:
+ default:
+ datarate = pclk * 18 / 8;
+ break;
+ }
- /* finish detaching encoder? */
- head = ffs((super & 0x00000180) >> 7) - 1;
- if (head >= 0)
- exec_script(priv, head, 2);
-
- /* check whether a vpll change is required */
- head = ffs((super & 0x00000600) >> 9) - 1;
- if (head >= 0) {
- u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
- if (pclk) {
- struct nouveau_clock *clk = nouveau_clock(priv);
- clk->pll_set(clk, PLL_VPLL0 + head, pclk);
+ nouveau_dp_train(&priv->base, priv->sor.dp,
+ &outp, head, datarate);
}
- nv_mask(priv, 0x614200 + head * 0x800, 0x0000000f, 0x00000000);
- }
-
- /* (re)attach the relevant OR to the head */
- head = ffs((super & 0x00000180) >> 7) - 1;
- if (head >= 0) {
- u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
- u32 conf = exec_clkcmp(priv, head, 0, pclk, &outp);
- if (conf) {
- if (outp.type == DCB_OUTPUT_ANALOG) {
- addr = 0x614280 + (ffs(outp.or) - 1) * 0x800;
- mask = 0xffffffff;
- data = 0x00000000;
- } else {
- if (outp.type == DCB_OUTPUT_DP)
- nv50_disp_intr_unk20_dp(priv, &outp, pclk);
- addr = 0x614300 + (ffs(outp.or) - 1) * 0x800;
- mask = 0x00000707;
- data = (conf & 0x0100) ? 0x0101 : 0x0000;
- }
-
- nv_mask(priv, addr, mask, data);
+ exec_clkcmp(priv, head, 0, pclk, &outp);
+
+ if (!outp.location && outp.type == DCB_OUTPUT_ANALOG) {
+ oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800;
+ oval = 0x00000000;
+ hval = 0x00000000;
+ } else
+ if (!outp.location) {
+ if (outp.type == DCB_OUTPUT_DP)
+ nv50_disp_intr_unk20_2_dp(priv, &outp, pclk);
+ oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800;
+ oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
+ hval = 0x00000000;
+ } else {
+ oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800;
+ oval = 0x00000001;
+ hval = 0x00000001;
}
- }
- nv_wr32(priv, 0x610024, 0x00000020);
- nv_wr32(priv, 0x610030, 0x80000000);
+ nv_mask(priv, hreg, 0x0000000f, hval);
+ nv_mask(priv, oreg, 0x00000707, oval);
+ }
}
/* If programming a TMDS output on a SOR that can also be configured for
@@ -1143,7 +1159,7 @@ nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super)
* programmed for DisplayPort.
*/
static void
-nv50_disp_intr_unk40_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp)
+nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp)
{
struct nouveau_bios *bios = nouveau_bios(priv);
const int link = !(outp->sorconf.link & 1);
@@ -1157,35 +1173,79 @@ nv50_disp_intr_unk40_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp)
}
static void
-nv50_disp_intr_unk40(struct nv50_disp_priv *priv, u32 super)
+nv50_disp_intr_unk40_0(struct nv50_disp_priv *priv, int head)
{
- int head = ffs((super & 0x00000180) >> 7) - 1;
- if (head >= 0) {
- struct dcb_output outp;
- u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
- if (pclk && exec_clkcmp(priv, head, 1, pclk, &outp)) {
- if (outp.type == DCB_OUTPUT_TMDS)
- nv50_disp_intr_unk40_tmds(priv, &outp);
+ struct dcb_output outp;
+ u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
+ if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) {
+ if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS)
+ nv50_disp_intr_unk40_0_tmds(priv, &outp);
+ else
+ if (outp.location == 1 && outp.type == DCB_OUTPUT_DP) {
+ u32 soff = (ffs(outp.or) - 1) * 0x08;
+ u32 ctrl = nv_rd32(priv, 0x610b84 + soff);
+ u32 datarate;
+
+ switch ((ctrl & 0x000f0000) >> 16) {
+ case 6: datarate = pclk * 30 / 8; break;
+ case 5: datarate = pclk * 24 / 8; break;
+ case 2:
+ default:
+ datarate = pclk * 18 / 8;
+ break;
+ }
+
+ nouveau_dp_train(&priv->base, priv->pior.dp,
+ &outp, head, datarate);
}
}
-
- nv_wr32(priv, 0x610024, 0x00000040);
- nv_wr32(priv, 0x610030, 0x80000000);
}
-static void
-nv50_disp_intr_super(struct nv50_disp_priv *priv, u32 intr1)
+void
+nv50_disp_intr_supervisor(struct work_struct *work)
{
+ struct nv50_disp_priv *priv =
+ container_of(work, struct nv50_disp_priv, supervisor);
u32 super = nv_rd32(priv, 0x610030);
+ int head;
- nv_debug(priv, "supervisor 0x%08x 0x%08x\n", intr1, super);
+ nv_debug(priv, "supervisor 0x%08x 0x%08x\n", priv->super, super);
- if (intr1 & 0x00000010)
- nv50_disp_intr_unk10(priv, super);
- if (intr1 & 0x00000020)
- nv50_disp_intr_unk20(priv, super);
- if (intr1 & 0x00000040)
- nv50_disp_intr_unk40(priv, super);
+ if (priv->super & 0x00000010) {
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(super & (0x00000020 << head)))
+ continue;
+ if (!(super & (0x00000080 << head)))
+ continue;
+ nv50_disp_intr_unk10_0(priv, head);
+ }
+ } else
+ if (priv->super & 0x00000020) {
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(super & (0x00000080 << head)))
+ continue;
+ nv50_disp_intr_unk20_0(priv, head);
+ }
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(super & (0x00000200 << head)))
+ continue;
+ nv50_disp_intr_unk20_1(priv, head);
+ }
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(super & (0x00000080 << head)))
+ continue;
+ nv50_disp_intr_unk20_2(priv, head);
+ }
+ } else
+ if (priv->super & 0x00000040) {
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(super & (0x00000080 << head)))
+ continue;
+ nv50_disp_intr_unk40_0(priv, head);
+ }
+ }
+
+ nv_wr32(priv, 0x610030, 0x80000000);
}
void
@@ -1201,19 +1261,21 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
}
if (intr1 & 0x00000004) {
- nv50_disp_intr_vblank(priv, 0);
+ nouveau_event_trigger(priv->base.vblank, 0);
nv_wr32(priv, 0x610024, 0x00000004);
intr1 &= ~0x00000004;
}
if (intr1 & 0x00000008) {
- nv50_disp_intr_vblank(priv, 1);
+ nouveau_event_trigger(priv->base.vblank, 1);
nv_wr32(priv, 0x610024, 0x00000008);
intr1 &= ~0x00000008;
}
if (intr1 & 0x00000070) {
- nv50_disp_intr_super(priv, intr1);
+ priv->super = (intr1 & 0x00000070);
+ schedule_work(&priv->supervisor);
+ nv_wr32(priv, 0x610024, priv->super);
intr1 &= ~0x00000070;
}
}
@@ -1226,7 +1288,7 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nv50_disp_priv *priv;
int ret;
- ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+ ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
"display", &priv);
*pobject = nv_object(priv);
if (ret)
@@ -1235,16 +1297,17 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nv50_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
+ INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
priv->sclass = nv50_disp_sclass;
priv->head.nr = 2;
priv->dac.nr = 3;
priv->sor.nr = 2;
+ priv->pior.nr = 3;
priv->dac.power = nv50_dac_power;
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
-
- INIT_LIST_HEAD(&priv->base.vblank.list);
- spin_lock_init(&priv->base.vblank.lock);
+ priv->pior.power = nv50_pior_power;
+ priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
index a6bb931450f1..1ae6ceb56704 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
@@ -3,16 +3,22 @@
#include <core/parent.h>
#include <core/namedb.h>
+#include <core/engctx.h>
#include <core/ramht.h>
+#include <core/event.h>
#include <engine/dmaobj.h>
#include <engine/disp.h>
-struct dcb_output;
+#include "dport.h"
struct nv50_disp_priv {
struct nouveau_disp base;
struct nouveau_oclass *sclass;
+
+ struct work_struct supervisor;
+ u32 super;
+
struct {
int nr;
} head;
@@ -26,23 +32,15 @@ struct nv50_disp_priv {
int (*power)(struct nv50_disp_priv *, int sor, u32 data);
int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32);
int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32);
- int (*dp_train_init)(struct nv50_disp_priv *, int sor, int link,
- int head, u16 type, u16 mask, u32 data,
- struct dcb_output *);
- int (*dp_train_fini)(struct nv50_disp_priv *, int sor, int link,
- int head, u16 type, u16 mask, u32 data,
- struct dcb_output *);
- int (*dp_train)(struct nv50_disp_priv *, int sor, int link,
- u16 type, u16 mask, u32 data,
- struct dcb_output *);
- int (*dp_lnkctl)(struct nv50_disp_priv *, int sor, int link,
- int head, u16 type, u16 mask, u32 data,
- struct dcb_output *);
- int (*dp_drvctl)(struct nv50_disp_priv *, int sor, int link,
- int lane, u16 type, u16 mask, u32 data,
- struct dcb_output *);
u32 lvdsconf;
+ const struct nouveau_dp_func *dp;
} sor;
+ struct {
+ int nr;
+ int (*power)(struct nv50_disp_priv *, int ext, u32 data);
+ u8 type[3];
+ const struct nouveau_dp_func *dp;
+ } pior;
};
#define DAC_MTHD(n) (n), (n) + 0x03
@@ -81,6 +79,11 @@ int nvd0_sor_dp_lnkctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32,
int nvd0_sor_dp_drvctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32,
struct dcb_output *);
+#define PIOR_MTHD(n) (n), (n) + 0x03
+
+int nv50_pior_mthd(struct nouveau_object *, u32, void *, u32);
+int nv50_pior_power(struct nv50_disp_priv *, int, u32);
+
struct nv50_disp_base {
struct nouveau_parent base;
struct nouveau_ramht *ramht;
@@ -124,6 +127,7 @@ extern struct nouveau_ofuncs nv50_disp_oimm_ofuncs;
extern struct nouveau_ofuncs nv50_disp_curs_ofuncs;
extern struct nouveau_ofuncs nv50_disp_base_ofuncs;
extern struct nouveau_oclass nv50_disp_cclass;
+void nv50_disp_intr_supervisor(struct work_struct *);
void nv50_disp_intr(struct nouveau_subdev *);
extern struct nouveau_omthds nv84_disp_base_omthds[];
@@ -137,6 +141,7 @@ extern struct nouveau_ofuncs nvd0_disp_oimm_ofuncs;
extern struct nouveau_ofuncs nvd0_disp_curs_ofuncs;
extern struct nouveau_ofuncs nvd0_disp_base_ofuncs;
extern struct nouveau_oclass nvd0_disp_cclass;
+void nvd0_disp_intr_supervisor(struct work_struct *);
void nvd0_disp_intr(struct nouveau_subdev *);
#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
index fc84eacdfbec..d8c74c0883a1 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
@@ -46,6 +46,9 @@ nv84_disp_base_omthds[] = {
{ SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
{ DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
{ DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
{},
};
@@ -63,7 +66,7 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nv50_disp_priv *priv;
int ret;
- ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+ ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
"display", &priv);
*pobject = nv_object(priv);
if (ret)
@@ -72,17 +75,18 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nv84_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
+ INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
priv->sclass = nv84_disp_sclass;
priv->head.nr = 2;
priv->dac.nr = 3;
priv->sor.nr = 2;
+ priv->pior.nr = 3;
priv->dac.power = nv50_dac_power;
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
priv->sor.hdmi = nv84_hdmi_ctrl;
-
- INIT_LIST_HEAD(&priv->base.vblank.list);
- spin_lock_init(&priv->base.vblank.lock);
+ priv->pior.power = nv50_pior_power;
+ priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
index ba9dfd4669a2..a66f949c1f84 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
@@ -44,14 +44,11 @@ nv94_disp_base_omthds[] = {
{ SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd },
{ SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd },
{ SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN) , nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL) , nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd },
{ DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
{ DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
{},
};
@@ -69,7 +66,7 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nv50_disp_priv *priv;
int ret;
- ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+ ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
"display", &priv);
*pobject = nv_object(priv);
if (ret)
@@ -78,22 +75,19 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nv94_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
+ INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
priv->sclass = nv94_disp_sclass;
priv->head.nr = 2;
priv->dac.nr = 3;
priv->sor.nr = 4;
+ priv->pior.nr = 3;
priv->dac.power = nv50_dac_power;
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
priv->sor.hdmi = nv84_hdmi_ctrl;
- priv->sor.dp_train = nv94_sor_dp_train;
- priv->sor.dp_train_init = nv94_sor_dp_train_init;
- priv->sor.dp_train_fini = nv94_sor_dp_train_fini;
- priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl;
- priv->sor.dp_drvctl = nv94_sor_dp_drvctl;
-
- INIT_LIST_HEAD(&priv->base.vblank.list);
- spin_lock_init(&priv->base.vblank.lock);
+ priv->sor.dp = &nv94_sor_dp_func;
+ priv->pior.power = nv50_pior_power;
+ priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
index 5d63902cdeda..6cf8eefac368 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
@@ -53,7 +53,7 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nv50_disp_priv *priv;
int ret;
- ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+ ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
"display", &priv);
*pobject = nv_object(priv);
if (ret)
@@ -62,17 +62,18 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nva0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
+ INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
priv->sclass = nva0_disp_sclass;
priv->head.nr = 2;
priv->dac.nr = 3;
priv->sor.nr = 2;
+ priv->pior.nr = 3;
priv->dac.power = nv50_dac_power;
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
priv->sor.hdmi = nv84_hdmi_ctrl;
-
- INIT_LIST_HEAD(&priv->base.vblank.list);
- spin_lock_init(&priv->base.vblank.lock);
+ priv->pior.power = nv50_pior_power;
+ priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
index e9192ca389fa..b75413169eae 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
@@ -45,14 +45,11 @@ nva3_disp_base_omthds[] = {
{ SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd },
{ SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd },
{ SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN) , nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL) , nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd },
{ DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
{ DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
+ { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
{},
};
@@ -70,7 +67,7 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nv50_disp_priv *priv;
int ret;
- ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+ ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
"display", &priv);
*pobject = nv_object(priv);
if (ret)
@@ -79,23 +76,20 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nva3_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
+ INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
priv->sclass = nva3_disp_sclass;
priv->head.nr = 2;
priv->dac.nr = 3;
priv->sor.nr = 4;
+ priv->pior.nr = 3;
priv->dac.power = nv50_dac_power;
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nva3_hda_eld;
priv->sor.hdmi = nva3_hdmi_ctrl;
- priv->sor.dp_train = nv94_sor_dp_train;
- priv->sor.dp_train_init = nv94_sor_dp_train_init;
- priv->sor.dp_train_fini = nv94_sor_dp_train_fini;
- priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl;
- priv->sor.dp_drvctl = nv94_sor_dp_drvctl;
-
- INIT_LIST_HEAD(&priv->base.vblank.list);
- spin_lock_init(&priv->base.vblank.lock);
+ priv->sor.dp = &nv94_sor_dp_func;
+ priv->pior.power = nv50_pior_power;
+ priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
index 9e38ebff5fb3..788dd34ccb54 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
@@ -27,12 +27,10 @@
#include <core/handle.h>
#include <core/class.h>
-#include <engine/software.h>
#include <engine/disp.h>
#include <subdev/timer.h>
#include <subdev/fb.h>
-#include <subdev/bar.h>
#include <subdev/clock.h>
#include <subdev/bios.h>
@@ -230,7 +228,7 @@ nvd0_disp_sync_ctor(struct nouveau_object *parent,
struct nv50_disp_dmac *dmac;
int ret;
- if (size < sizeof(*data) || args->head >= priv->head.nr)
+ if (size < sizeof(*args) || args->head >= priv->head.nr)
return -EINVAL;
ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -270,7 +268,7 @@ nvd0_disp_ovly_ctor(struct nouveau_object *parent,
struct nv50_disp_dmac *dmac;
int ret;
- if (size < sizeof(*data) || args->head >= priv->head.nr)
+ if (size < sizeof(*args) || args->head >= priv->head.nr)
return -EINVAL;
ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -443,6 +441,18 @@ nvd0_disp_curs_ofuncs = {
* Base display object
******************************************************************************/
+static void
+nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head)
+{
+ nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001);
+}
+
+static void
+nvd0_disp_base_vblank_disable(struct nouveau_event *event, int head)
+{
+ nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
+}
+
static int
nvd0_disp_base_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
@@ -459,6 +469,10 @@ nvd0_disp_base_ctor(struct nouveau_object *parent,
if (ret)
return ret;
+ priv->base.vblank->priv = priv;
+ priv->base.vblank->enable = nvd0_disp_base_vblank_enable;
+ priv->base.vblank->disable = nvd0_disp_base_vblank_disable;
+
return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht);
}
@@ -609,13 +623,24 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
}
static bool
-exec_script(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, int id)
+exec_script(struct nv50_disp_priv *priv, int head, int id)
{
struct nouveau_bios *bios = nouveau_bios(priv);
struct nvbios_outp info;
struct dcb_output dcb;
u8 ver, hdr, cnt, len;
+ u32 ctrl = 0x00000000;
u16 data;
+ int outp;
+
+ for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
+ ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20));
+ if (ctrl & (1 << head))
+ break;
+ }
+
+ if (outp == 8)
+ return false;
data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
if (data) {
@@ -635,21 +660,31 @@ exec_script(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, int id)
}
static u32
-exec_clkcmp(struct nv50_disp_priv *priv, int head, int outp,
- u32 ctrl, int id, u32 pclk)
+exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
+ u32 pclk, struct dcb_output *dcb)
{
struct nouveau_bios *bios = nouveau_bios(priv);
struct nvbios_outp info1;
struct nvbios_ocfg info2;
- struct dcb_output dcb;
u8 ver, hdr, cnt, len;
- u16 data, conf;
+ u32 ctrl = 0x00000000;
+ u32 data, conf = ~0;
+ int outp;
- data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info1);
- if (data == 0x0000)
+ for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
+ ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20));
+ if (ctrl & (1 << head))
+ break;
+ }
+
+ if (outp == 8)
return false;
- switch (dcb.type) {
+ data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1);
+ if (data == 0x0000)
+ return conf;
+
+ switch (dcb->type) {
case DCB_OUTPUT_TMDS:
conf = (ctrl & 0x00000f00) >> 8;
if (pclk >= 165000)
@@ -668,46 +703,52 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int outp,
}
data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
- if (data) {
+ if (data && id < 0xff) {
data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
if (data) {
struct nvbios_init init = {
.subdev = nv_subdev(priv),
.bios = bios,
.offset = data,
- .outp = &dcb,
+ .outp = dcb,
.crtc = head,
.execute = 1,
};
- if (nvbios_exec(&init))
- return 0x0000;
- return conf;
+ nvbios_exec(&init);
}
}
- return 0x0000;
+ return conf;
}
static void
-nvd0_display_unk1_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
+nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head)
{
- int i;
+ exec_script(priv, head, 1);
+}
- for (i = 0; mask && i < 8; i++) {
- u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20));
- if (mcc & (1 << head))
- exec_script(priv, head, i, mcc, 1);
- }
+static void
+nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head)
+{
+ exec_script(priv, head, 2);
+}
- nv_wr32(priv, 0x6101d4, 0x00000000);
- nv_wr32(priv, 0x6109d4, 0x00000000);
- nv_wr32(priv, 0x6101d0, 0x80000000);
+static void
+nvd0_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head)
+{
+ struct nouveau_clock *clk = nouveau_clock(priv);
+ u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
+ if (pclk)
+ clk->pll_set(clk, PLL_VPLL0 + head, pclk);
+ nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);
}
static void
-nvd0_display_unk2_calc_tu(struct nv50_disp_priv *priv, int head, int or)
+nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
+ struct dcb_output *outp)
{
+ 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 u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
@@ -750,105 +791,102 @@ nvd0_display_unk2_calc_tu(struct nv50_disp_priv *priv, int head, int or)
}
static void
-nvd0_display_unk2_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
+nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head)
{
- u32 pclk;
- int i;
-
- for (i = 0; mask && i < 8; i++) {
- u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20));
- if (mcc & (1 << head))
- exec_script(priv, head, i, mcc, 2);
- }
+ struct dcb_output outp;
+ u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
+ u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
+ if (conf != ~0) {
+ u32 addr, data;
+
+ if (outp.type == DCB_OUTPUT_DP) {
+ u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300));
+ switch ((sync & 0x000003c0) >> 6) {
+ case 6: pclk = pclk * 30 / 8; break;
+ case 5: pclk = pclk * 24 / 8; break;
+ case 2:
+ default:
+ pclk = pclk * 18 / 8;
+ break;
+ }
- pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
- nv_debug(priv, "head %d pclk %d mask 0x%08x\n", head, pclk, mask);
- if (pclk && (mask & 0x00010000)) {
- struct nouveau_clock *clk = nouveau_clock(priv);
- clk->pll_set(clk, PLL_VPLL0 + head, pclk);
- }
+ nouveau_dp_train(&priv->base, priv->sor.dp,
+ &outp, head, pclk);
+ }
- nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);
+ exec_clkcmp(priv, head, 0, pclk, &outp);
- for (i = 0; mask && i < 8; i++) {
- u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20)), cfg;
- if (mcp & (1 << head)) {
- if ((cfg = exec_clkcmp(priv, head, i, mcp, 0, pclk))) {
- u32 addr, mask, data = 0x00000000;
- if (i < 4) {
- addr = 0x612280 + ((i - 0) * 0x800);
- mask = 0xffffffff;
- } else {
- switch (mcp & 0x00000f00) {
- case 0x00000800:
- case 0x00000900:
- nvd0_display_unk2_calc_tu(priv, head, i - 4);
- break;
- default:
- break;
- }
-
- addr = 0x612300 + ((i - 4) * 0x800);
- mask = 0x00000707;
- if (cfg & 0x00000100)
- data = 0x00000101;
- }
- nv_mask(priv, addr, mask, data);
- }
- break;
+ if (outp.type == DCB_OUTPUT_ANALOG) {
+ addr = 0x612280 + (ffs(outp.or) - 1) * 0x800;
+ data = 0x00000000;
+ } else {
+ if (outp.type == DCB_OUTPUT_DP)
+ nvd0_disp_intr_unk2_2_tu(priv, head, &outp);
+ addr = 0x612300 + (ffs(outp.or) - 1) * 0x800;
+ data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
}
- }
- nv_wr32(priv, 0x6101d4, 0x00000000);
- nv_wr32(priv, 0x6109d4, 0x00000000);
- nv_wr32(priv, 0x6101d0, 0x80000000);
+ nv_mask(priv, addr, 0x00000707, data);
+ }
}
static void
-nvd0_display_unk4_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
+nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head)
{
- int pclk, i;
-
- pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
+ struct dcb_output outp;
+ u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
+ exec_clkcmp(priv, head, 1, pclk, &outp);
+}
- for (i = 0; mask && i < 8; i++) {
- u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20));
- if (mcp & (1 << head))
- exec_clkcmp(priv, head, i, mcp, 1, pclk);
+void
+nvd0_disp_intr_supervisor(struct work_struct *work)
+{
+ struct nv50_disp_priv *priv =
+ container_of(work, struct nv50_disp_priv, supervisor);
+ u32 mask[4];
+ int head;
+
+ nv_debug(priv, "supervisor %08x\n", priv->super);
+ for (head = 0; head < priv->head.nr; head++) {
+ mask[head] = nv_rd32(priv, 0x6101d4 + (head * 0x800));
+ nv_debug(priv, "head %d: 0x%08x\n", head, mask[head]);
}
- nv_wr32(priv, 0x6101d4, 0x00000000);
- nv_wr32(priv, 0x6109d4, 0x00000000);
- nv_wr32(priv, 0x6101d0, 0x80000000);
-}
-
-static void
-nvd0_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
-{
- struct nouveau_bar *bar = nouveau_bar(priv);
- struct nouveau_disp *disp = &priv->base;
- struct nouveau_software_chan *chan, *temp;
- unsigned long flags;
-
- spin_lock_irqsave(&disp->vblank.lock, flags);
- list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) {
- if (chan->vblank.crtc != crtc)
- continue;
-
- nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
- bar->flush(bar);
- nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset));
- nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset));
- nv_wr32(priv, 0x060014, chan->vblank.value);
-
- list_del(&chan->vblank.head);
- if (disp->vblank.put)
- disp->vblank.put(disp->vblank.data, crtc);
+ if (priv->super & 0x00000001) {
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(mask[head] & 0x00001000))
+ continue;
+ nvd0_disp_intr_unk1_0(priv, head);
+ }
+ } else
+ if (priv->super & 0x00000002) {
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(mask[head] & 0x00001000))
+ continue;
+ nvd0_disp_intr_unk2_0(priv, head);
+ }
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(mask[head] & 0x00010000))
+ continue;
+ nvd0_disp_intr_unk2_1(priv, head);
+ }
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(mask[head] & 0x00001000))
+ continue;
+ nvd0_disp_intr_unk2_2(priv, head);
+ }
+ } else
+ if (priv->super & 0x00000004) {
+ for (head = 0; head < priv->head.nr; head++) {
+ if (!(mask[head] & 0x00001000))
+ continue;
+ nvd0_disp_intr_unk4_0(priv, head);
+ }
}
- spin_unlock_irqrestore(&disp->vblank.lock, flags);
- if (disp->vblank.notify)
- disp->vblank.notify(disp->vblank.data, crtc);
+ for (head = 0; head < priv->head.nr; head++)
+ nv_wr32(priv, 0x6101d4 + (head * 0x800), 0x00000000);
+ nv_wr32(priv, 0x6101d0, 0x80000000);
}
void
@@ -884,27 +922,11 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
if (intr & 0x00100000) {
u32 stat = nv_rd32(priv, 0x6100ac);
- u32 mask = 0, crtc = ~0;
-
- while (!mask && ++crtc < priv->head.nr)
- mask = nv_rd32(priv, 0x6101d4 + (crtc * 0x800));
-
- if (stat & 0x00000001) {
- nv_wr32(priv, 0x6100ac, 0x00000001);
- nvd0_display_unk1_handler(priv, crtc, mask);
- stat &= ~0x00000001;
- }
-
- if (stat & 0x00000002) {
- nv_wr32(priv, 0x6100ac, 0x00000002);
- nvd0_display_unk2_handler(priv, crtc, mask);
- stat &= ~0x00000002;
- }
-
- if (stat & 0x00000004) {
- nv_wr32(priv, 0x6100ac, 0x00000004);
- nvd0_display_unk4_handler(priv, crtc, mask);
- stat &= ~0x00000004;
+ if (stat & 0x00000007) {
+ priv->super = (stat & 0x00000007);
+ schedule_work(&priv->supervisor);
+ nv_wr32(priv, 0x6100ac, priv->super);
+ stat &= ~0x00000007;
}
if (stat) {
@@ -920,7 +942,7 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
if (mask & intr) {
u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
if (stat & 0x00000001)
- nvd0_disp_intr_vblank(priv, i);
+ nouveau_event_trigger(priv->base.vblank, i);
nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0);
nv_rd32(priv, 0x6100c0 + (i * 0x800));
}
@@ -933,10 +955,11 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_object **pobject)
{
struct nv50_disp_priv *priv;
+ int heads = nv_rd32(parent, 0x022448);
int ret;
- ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
- "display", &priv);
+ ret = nouveau_disp_create(parent, engine, oclass, heads,
+ "PDISP", "display", &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
@@ -944,8 +967,9 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nvd0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr;
+ INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor);
priv->sclass = nvd0_disp_sclass;
- priv->head.nr = nv_rd32(priv, 0x022448);
+ priv->head.nr = heads;
priv->dac.nr = 3;
priv->sor.nr = 4;
priv->dac.power = nv50_dac_power;
@@ -953,14 +977,7 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld;
priv->sor.hdmi = nvd0_hdmi_ctrl;
- priv->sor.dp_train = nvd0_sor_dp_train;
- priv->sor.dp_train_init = nv94_sor_dp_train_init;
- priv->sor.dp_train_fini = nv94_sor_dp_train_fini;
- priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl;
- priv->sor.dp_drvctl = nvd0_sor_dp_drvctl;
-
- INIT_LIST_HEAD(&priv->base.vblank.list);
- spin_lock_init(&priv->base.vblank.lock);
+ priv->sor.dp = &nvd0_sor_dp_func;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
index 259537c4587e..20725b363d58 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
@@ -51,10 +51,11 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_object **pobject)
{
struct nv50_disp_priv *priv;
+ int heads = nv_rd32(parent, 0x022448);
int ret;
- ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
- "display", &priv);
+ ret = nouveau_disp_create(parent, engine, oclass, heads,
+ "PDISP", "display", &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
@@ -62,8 +63,9 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nve0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr;
+ INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor);
priv->sclass = nve0_disp_sclass;
- priv->head.nr = nv_rd32(priv, 0x022448);
+ priv->head.nr = heads;
priv->dac.nr = 3;
priv->sor.nr = 4;
priv->dac.power = nv50_dac_power;
@@ -71,14 +73,7 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld;
priv->sor.hdmi = nvd0_hdmi_ctrl;
- priv->sor.dp_train = nvd0_sor_dp_train;
- priv->sor.dp_train_init = nv94_sor_dp_train_init;
- priv->sor.dp_train_fini = nv94_sor_dp_train_fini;
- priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl;
- priv->sor.dp_drvctl = nvd0_sor_dp_drvctl;
-
- INIT_LIST_HEAD(&priv->base.vblank.list);
- spin_lock_init(&priv->base.vblank.lock);
+ priv->sor.dp = &nvd0_sor_dp_func;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
new file mode 100644
index 000000000000..2c8ce351b52d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/os.h>
+#include <core/class.h>
+
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/timer.h>
+#include <subdev/i2c.h>
+
+#include "nv50.h"
+
+/******************************************************************************
+ * DisplayPort
+ *****************************************************************************/
+static struct nouveau_i2c_port *
+nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp)
+{
+ struct nouveau_i2c *i2c = nouveau_i2c(disp);
+ return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
+}
+
+static int
+nv50_pior_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
+ int head, int pattern)
+{
+ struct nouveau_i2c_port *port;
+ int ret = -EINVAL;
+
+ port = nv50_pior_dp_find(disp, outp);
+ if (port) {
+ if (port->func->pattern)
+ ret = port->func->pattern(port, pattern);
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int
+nv50_pior_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+ int head, int lane_nr, int link_bw, bool enh)
+{
+ struct nouveau_i2c_port *port;
+ int ret = -EINVAL;
+
+ port = nv50_pior_dp_find(disp, outp);
+ if (port && port->func->lnk_ctl)
+ ret = port->func->lnk_ctl(port, lane_nr, link_bw, enh);
+
+ return ret;
+}
+
+static int
+nv50_pior_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+ int head, int lane, int vsw, int pre)
+{
+ struct nouveau_i2c_port *port;
+ int ret = -EINVAL;
+
+ port = nv50_pior_dp_find(disp, outp);
+ if (port) {
+ if (port->func->drv_ctl)
+ ret = port->func->drv_ctl(port, lane, vsw, pre);
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+const struct nouveau_dp_func
+nv50_pior_dp_func = {
+ .pattern = nv50_pior_dp_pattern,
+ .lnk_ctl = nv50_pior_dp_lnk_ctl,
+ .drv_ctl = nv50_pior_dp_drv_ctl,
+};
+
+/******************************************************************************
+ * General PIOR handling
+ *****************************************************************************/
+int
+nv50_pior_power(struct nv50_disp_priv *priv, int or, u32 data)
+{
+ const u32 stat = data & NV50_DISP_PIOR_PWR_STATE;
+ const u32 soff = (or * 0x800);
+ nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000);
+ nv_mask(priv, 0x61e004 + soff, 0x80000101, 0x80000000 | stat);
+ nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000);
+ return 0;
+}
+
+int
+nv50_pior_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
+{
+ struct nv50_disp_priv *priv = (void *)object->engine;
+ const u8 type = (mthd & NV50_DISP_PIOR_MTHD_TYPE) >> 12;
+ const u8 or = (mthd & NV50_DISP_PIOR_MTHD_OR);
+ u32 *data = args;
+ int ret;
+
+ if (size < sizeof(u32))
+ return -EINVAL;
+
+ mthd &= ~NV50_DISP_PIOR_MTHD_TYPE;
+ mthd &= ~NV50_DISP_PIOR_MTHD_OR;
+ switch (mthd) {
+ case NV50_DISP_PIOR_PWR:
+ ret = priv->pior.power(priv, or, data[0]);
+ priv->pior.type[or] = type;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
index 39b6b67732d0..ab1e918469a8 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
@@ -79,31 +79,6 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
priv->sor.lvdsconf = data & NV50_DISP_SOR_LVDS_SCRIPT_ID;
ret = 0;
break;
- case NV94_DISP_SOR_DP_TRAIN:
- switch (data & NV94_DISP_SOR_DP_TRAIN_OP) {
- case NV94_DISP_SOR_DP_TRAIN_OP_PATTERN:
- ret = priv->sor.dp_train(priv, or, link, type, mask, data, &outp);
- break;
- case NV94_DISP_SOR_DP_TRAIN_OP_INIT:
- ret = priv->sor.dp_train_init(priv, or, link, head, type, mask, data, &outp);
- break;
- case NV94_DISP_SOR_DP_TRAIN_OP_FINI:
- ret = priv->sor.dp_train_fini(priv, or, link, head, type, mask, data, &outp);
- break;
- default:
- break;
- }
- break;
- case NV94_DISP_SOR_DP_LNKCTL:
- ret = priv->sor.dp_lnkctl(priv, or, link, head, type, mask, data, &outp);
- break;
- case NV94_DISP_SOR_DP_DRVCTL(0):
- case NV94_DISP_SOR_DP_DRVCTL(1):
- case NV94_DISP_SOR_DP_DRVCTL(2):
- case NV94_DISP_SOR_DP_DRVCTL(3):
- ret = priv->sor.dp_drvctl(priv, or, link, (mthd & 0xc0) >> 6,
- type, mask, data, &outp);
- break;
default:
BUG_ON(1);
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
index f6edd009762e..7ec4ee83fb64 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
@@ -33,124 +33,53 @@
#include "nv50.h"
static inline u32
-nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
+nv94_sor_soff(struct dcb_output *outp)
{
- static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
- static const u8 nv94[] = { 16, 8, 0, 24 };
- if (nv_device(priv)->chipset == 0xaf)
- return nvaf[lane];
- return nv94[lane];
+ return (ffs(outp->or) - 1) * 0x800;
}
-int
-nv94_sor_dp_train_init(struct nv50_disp_priv *priv, int or, int link, int head,
- u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static inline u32
+nv94_sor_loff(struct dcb_output *outp)
{
- struct nouveau_bios *bios = nouveau_bios(priv);
- struct nvbios_dpout info;
- u8 ver, hdr, cnt, len;
- u16 outp;
-
- outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info);
- if (outp) {
- struct nvbios_init init = {
- .subdev = nv_subdev(priv),
- .bios = bios,
- .outp = dcbo,
- .crtc = head,
- .execute = 1,
- };
-
- if (data & NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON)
- init.offset = info.script[2];
- else
- init.offset = info.script[3];
- nvbios_exec(&init);
-
- init.offset = info.script[0];
- nvbios_exec(&init);
- }
-
- return 0;
+ return nv94_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
}
-int
-nv94_sor_dp_train_fini(struct nv50_disp_priv *priv, int or, int link, int head,
- u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static inline u32
+nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
{
- struct nouveau_bios *bios = nouveau_bios(priv);
- struct nvbios_dpout info;
- u8 ver, hdr, cnt, len;
- u16 outp;
-
- outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info);
- if (outp) {
- struct nvbios_init init = {
- .subdev = nv_subdev(priv),
- .bios = bios,
- .offset = info.script[1],
- .outp = dcbo,
- .crtc = head,
- .execute = 1,
- };
-
- nvbios_exec(&init);
- }
-
- return 0;
+ static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
+ static const u8 nv94[] = { 16, 8, 0, 24 };
+ if (nv_device(priv)->chipset == 0xaf)
+ return nvaf[lane];
+ return nv94[lane];
}
-int
-nv94_sor_dp_train(struct nv50_disp_priv *priv, int or, int link,
- u16 type, u16 mask, u32 data, struct dcb_output *info)
+static int
+nv94_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
+ int head, int pattern)
{
- const u32 loff = (or * 0x800) + (link * 0x80);
- const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN);
- nv_mask(priv, 0x61c10c + loff, 0x0f000000, patt << 24);
+ struct nv50_disp_priv *priv = (void *)disp;
+ const u32 loff = nv94_sor_loff(outp);
+ nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24);
return 0;
}
-int
-nv94_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
- u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+ int head, int link_nr, int link_bw, bool enh_frame)
{
- struct nouveau_bios *bios = nouveau_bios(priv);
- const u32 loff = (or * 0x800) + (link * 0x80);
- const u32 soff = (or * 0x800);
- u16 link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8;
- u8 link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT);
+ struct nv50_disp_priv *priv = (void *)disp;
+ const u32 soff = nv94_sor_soff(outp);
+ const u32 loff = nv94_sor_loff(outp);
u32 dpctrl = 0x00000000;
u32 clksor = 0x00000000;
- u32 outp, lane = 0;
- u8 ver, hdr, cnt, len;
- struct nvbios_dpout info;
+ u32 lane = 0;
int i;
- /* -> 10Khz units */
- link_bw *= 2700;
-
- outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info);
- if (outp && info.lnkcmp) {
- struct nvbios_init init = {
- .subdev = nv_subdev(priv),
- .bios = bios,
- .offset = 0x0000,
- .outp = dcbo,
- .crtc = head,
- .execute = 1,
- };
-
- while (link_bw < nv_ro16(bios, info.lnkcmp))
- info.lnkcmp += 4;
- init.offset = nv_ro16(bios, info.lnkcmp + 2);
-
- nvbios_exec(&init);
- }
-
dpctrl |= ((1 << link_nr) - 1) << 16;
- if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH)
+ if (enh_frame)
dpctrl |= 0x00004000;
- if (link_bw > 16200)
+ if (link_bw > 0x06)
clksor |= 0x00040000;
for (i = 0; i < link_nr; i++)
@@ -162,24 +91,25 @@ nv94_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
return 0;
}
-int
-nv94_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
- u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+ int head, int lane, int swing, int preem)
{
- struct nouveau_bios *bios = nouveau_bios(priv);
- const u32 loff = (or * 0x800) + (link * 0x80);
- const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8;
- const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE);
+ struct nouveau_bios *bios = nouveau_bios(disp);
+ struct nv50_disp_priv *priv = (void *)disp;
+ const u32 loff = nv94_sor_loff(outp);
u32 addr, shift = nv94_sor_dp_lane_map(priv, lane);
u8 ver, hdr, cnt, len;
- struct nvbios_dpout outp;
+ struct nvbios_dpout info;
struct nvbios_dpcfg ocfg;
- addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp);
+ addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
+ &ver, &hdr, &cnt, &len, &info);
if (!addr)
return -ENODEV;
- addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg);
+ addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
+ &ver, &hdr, &cnt, &len, &ocfg);
if (!addr)
return -EINVAL;
@@ -188,3 +118,10 @@ nv94_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
nv_mask(priv, 0x61c130 + loff, 0x0000ff00, ocfg.unk << 8);
return 0;
}
+
+const struct nouveau_dp_func
+nv94_sor_dp_func = {
+ .pattern = nv94_sor_dp_pattern,
+ .lnk_ctl = nv94_sor_dp_lnk_ctl,
+ .drv_ctl = nv94_sor_dp_drv_ctl,
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
index c37ce7e29f5d..9e1d435d7282 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
@@ -33,59 +33,49 @@
#include "nv50.h"
static inline u32
+nvd0_sor_soff(struct dcb_output *outp)
+{
+ return (ffs(outp->or) - 1) * 0x800;
+}
+
+static inline u32
+nvd0_sor_loff(struct dcb_output *outp)
+{
+ return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
+}
+
+static inline u32
nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
{
static const u8 nvd0[] = { 16, 8, 0, 24 };
return nvd0[lane];
}
-int
-nvd0_sor_dp_train(struct nv50_disp_priv *priv, int or, int link,
- u16 type, u16 mask, u32 data, struct dcb_output *info)
+static int
+nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
+ int head, int pattern)
{
- const u32 loff = (or * 0x800) + (link * 0x80);
- const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN);
- nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * patt);
+ struct nv50_disp_priv *priv = (void *)disp;
+ const u32 loff = nvd0_sor_loff(outp);
+ nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
return 0;
}
-int
-nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
- u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+ int head, int link_nr, int link_bw, bool enh_frame)
{
- struct nouveau_bios *bios = nouveau_bios(priv);
- const u32 loff = (or * 0x800) + (link * 0x80);
- const u32 soff = (or * 0x800);
- const u8 link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8;
- const u8 link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT);
+ struct nv50_disp_priv *priv = (void *)disp;
+ const u32 soff = nvd0_sor_soff(outp);
+ const u32 loff = nvd0_sor_loff(outp);
u32 dpctrl = 0x00000000;
u32 clksor = 0x00000000;
- u32 outp, lane = 0;
- u8 ver, hdr, cnt, len;
- struct nvbios_dpout info;
+ u32 lane = 0;
int i;
- outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info);
- if (outp && info.lnkcmp) {
- struct nvbios_init init = {
- .subdev = nv_subdev(priv),
- .bios = bios,
- .offset = 0x0000,
- .outp = dcbo,
- .crtc = head,
- .execute = 1,
- };
-
- while (nv_ro08(bios, info.lnkcmp) < link_bw)
- info.lnkcmp += 3;
- init.offset = nv_ro16(bios, info.lnkcmp + 1);
-
- nvbios_exec(&init);
- }
-
clksor |= link_bw << 18;
dpctrl |= ((1 << link_nr) - 1) << 16;
- if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH)
+ if (enh_frame)
dpctrl |= 0x00004000;
for (i = 0; i < link_nr; i++)
@@ -97,24 +87,25 @@ nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
return 0;
}
-int
-nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
- u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+ int head, int lane, int swing, int preem)
{
- struct nouveau_bios *bios = nouveau_bios(priv);
- const u32 loff = (or * 0x800) + (link * 0x80);
- const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8;
- const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE);
+ struct nouveau_bios *bios = nouveau_bios(disp);
+ struct nv50_disp_priv *priv = (void *)disp;
+ const u32 loff = nvd0_sor_loff(outp);
u32 addr, shift = nvd0_sor_dp_lane_map(priv, lane);
u8 ver, hdr, cnt, len;
- struct nvbios_dpout outp;
+ struct nvbios_dpout info;
struct nvbios_dpcfg ocfg;
- addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp);
+ addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
+ &ver, &hdr, &cnt, &len, &info);
if (!addr)
return -ENODEV;
- addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg);
+ addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
+ &ver, &hdr, &cnt, &len, &ocfg);
if (!addr)
return -EINVAL;
@@ -124,3 +115,10 @@ nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000);
return 0;
}
+
+const struct nouveau_dp_func
+nvd0_sor_dp_func = {
+ .pattern = nvd0_sor_dp_pattern,
+ .lnk_ctl = nvd0_sor_dp_lnk_ctl,
+ .drv_ctl = nvd0_sor_dp_drv_ctl,
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
index c2b9db335816..7341ebe131fa 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
@@ -22,8 +22,10 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/object.h>
#include <core/handle.h>
+#include <core/event.h>
#include <core/class.h>
#include <engine/dmaobj.h>
@@ -146,10 +148,25 @@ nouveau_fifo_chid(struct nouveau_fifo *priv, struct nouveau_object *object)
return -1;
}
+const char *
+nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid)
+{
+ struct nouveau_fifo_chan *chan = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fifo->lock, flags);
+ if (chid >= fifo->min && chid <= fifo->max)
+ chan = (void *)fifo->channel[chid];
+ spin_unlock_irqrestore(&fifo->lock, flags);
+
+ return nouveau_client_name(chan);
+}
+
void
nouveau_fifo_destroy(struct nouveau_fifo *priv)
{
kfree(priv->channel);
+ nouveau_event_destroy(&priv->uevent);
nouveau_engine_destroy(&priv->base);
}
@@ -174,6 +191,10 @@ nouveau_fifo_create_(struct nouveau_object *parent,
if (!priv->channel)
return -ENOMEM;
+ ret = nouveau_event_create(1, &priv->uevent);
+ if (ret)
+ return ret;
+
priv->chid = nouveau_fifo_chid;
spin_lock_init(&priv->lock);
return 0;
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
index a47a8548f9e0..f877bd524a92 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
@@ -28,6 +28,7 @@
#include <core/namedb.h>
#include <core/handle.h>
#include <core/ramht.h>
+#include <core/event.h>
#include <subdev/instmem.h>
#include <subdev/instmem/nv04.h>
@@ -398,6 +399,98 @@ out:
return handled;
}
+static void
+nv04_fifo_cache_error(struct nouveau_device *device,
+ struct nv04_fifo_priv *priv, u32 chid, u32 get)
+{
+ u32 mthd, data;
+ int ptr;
+
+ /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before wrapping on my
+ * G80 chips, but CACHE1 isn't big enough for this much data.. Tests
+ * show that it wraps around to the start at GET=0x800.. No clue as to
+ * why..
+ */
+ ptr = (get & 0x7ff) >> 2;
+
+ if (device->card_type < NV_40) {
+ mthd = nv_rd32(priv, NV04_PFIFO_CACHE1_METHOD(ptr));
+ data = nv_rd32(priv, NV04_PFIFO_CACHE1_DATA(ptr));
+ } else {
+ mthd = nv_rd32(priv, NV40_PFIFO_CACHE1_METHOD(ptr));
+ data = nv_rd32(priv, NV40_PFIFO_CACHE1_DATA(ptr));
+ }
+
+ if (!nv04_fifo_swmthd(priv, chid, mthd, data)) {
+ const char *client_name =
+ nouveau_client_name_for_fifo_chid(&priv->base, chid);
+ nv_error(priv,
+ "CACHE_ERROR - ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
+ chid, client_name, (mthd >> 13) & 7, mthd & 0x1ffc,
+ data);
+ }
+
+ nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
+ nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_CACHE_ERROR);
+
+ nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
+ nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1);
+ nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
+ nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
+ nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1);
+ nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0);
+
+ nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH,
+ nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
+ nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
+}
+
+static void
+nv04_fifo_dma_pusher(struct nouveau_device *device, struct nv04_fifo_priv *priv,
+ u32 chid)
+{
+ const char *client_name;
+ u32 dma_get = nv_rd32(priv, 0x003244);
+ u32 dma_put = nv_rd32(priv, 0x003240);
+ u32 push = nv_rd32(priv, 0x003220);
+ u32 state = nv_rd32(priv, 0x003228);
+
+ client_name = nouveau_client_name_for_fifo_chid(&priv->base, chid);
+
+ if (device->card_type == NV_50) {
+ u32 ho_get = nv_rd32(priv, 0x003328);
+ u32 ho_put = nv_rd32(priv, 0x003320);
+ u32 ib_get = nv_rd32(priv, 0x003334);
+ u32 ib_put = nv_rd32(priv, 0x003330);
+
+ nv_error(priv,
+ "DMA_PUSHER - ch %d [%s] get 0x%02x%08x put 0x%02x%08x ib_get 0x%08x ib_put 0x%08x state 0x%08x (err: %s) push 0x%08x\n",
+ chid, client_name, ho_get, dma_get, ho_put, dma_put,
+ ib_get, ib_put, state, nv_dma_state_err(state), push);
+
+ /* METHOD_COUNT, in DMA_STATE on earlier chipsets */
+ nv_wr32(priv, 0x003364, 0x00000000);
+ if (dma_get != dma_put || ho_get != ho_put) {
+ nv_wr32(priv, 0x003244, dma_put);
+ nv_wr32(priv, 0x003328, ho_put);
+ } else
+ if (ib_get != ib_put)
+ nv_wr32(priv, 0x003334, ib_put);
+ } else {
+ nv_error(priv,
+ "DMA_PUSHER - ch %d [%s] get 0x%08x put 0x%08x state 0x%08x (err: %s) push 0x%08x\n",
+ chid, client_name, dma_get, dma_put, state,
+ nv_dma_state_err(state), push);
+
+ if (dma_get != dma_put)
+ nv_wr32(priv, 0x003244, dma_put);
+ }
+
+ nv_wr32(priv, 0x003228, 0x00000000);
+ nv_wr32(priv, 0x003220, 0x00000001);
+ nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER);
+}
+
void
nv04_fifo_intr(struct nouveau_subdev *subdev)
{
@@ -416,96 +509,12 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
get = nv_rd32(priv, NV03_PFIFO_CACHE1_GET);
if (status & NV_PFIFO_INTR_CACHE_ERROR) {
- uint32_t mthd, data;
- int ptr;
-
- /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before
- * wrapping on my G80 chips, but CACHE1 isn't big
- * enough for this much data.. Tests show that it
- * wraps around to the start at GET=0x800.. No clue
- * as to why..
- */
- ptr = (get & 0x7ff) >> 2;
-
- if (device->card_type < NV_40) {
- mthd = nv_rd32(priv,
- NV04_PFIFO_CACHE1_METHOD(ptr));
- data = nv_rd32(priv,
- NV04_PFIFO_CACHE1_DATA(ptr));
- } else {
- mthd = nv_rd32(priv,
- NV40_PFIFO_CACHE1_METHOD(ptr));
- data = nv_rd32(priv,
- NV40_PFIFO_CACHE1_DATA(ptr));
- }
-
- if (!nv04_fifo_swmthd(priv, chid, mthd, data)) {
- nv_error(priv, "CACHE_ERROR - Ch %d/%d "
- "Mthd 0x%04x Data 0x%08x\n",
- chid, (mthd >> 13) & 7, mthd & 0x1ffc,
- data);
- }
-
- nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
- nv_wr32(priv, NV03_PFIFO_INTR_0,
- NV_PFIFO_INTR_CACHE_ERROR);
-
- nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
- nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1);
- nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
- nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
- nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1);
- nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0);
-
- nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH,
- nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
- nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
-
+ nv04_fifo_cache_error(device, priv, chid, get);
status &= ~NV_PFIFO_INTR_CACHE_ERROR;
}
if (status & NV_PFIFO_INTR_DMA_PUSHER) {
- u32 dma_get = nv_rd32(priv, 0x003244);
- u32 dma_put = nv_rd32(priv, 0x003240);
- u32 push = nv_rd32(priv, 0x003220);
- u32 state = nv_rd32(priv, 0x003228);
-
- if (device->card_type == NV_50) {
- u32 ho_get = nv_rd32(priv, 0x003328);
- u32 ho_put = nv_rd32(priv, 0x003320);
- u32 ib_get = nv_rd32(priv, 0x003334);
- u32 ib_put = nv_rd32(priv, 0x003330);
-
- nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%02x%08x "
- "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x "
- "State 0x%08x (err: %s) Push 0x%08x\n",
- chid, ho_get, dma_get, ho_put,
- dma_put, ib_get, ib_put, state,
- nv_dma_state_err(state),
- push);
-
- /* METHOD_COUNT, in DMA_STATE on earlier chipsets */
- nv_wr32(priv, 0x003364, 0x00000000);
- if (dma_get != dma_put || ho_get != ho_put) {
- nv_wr32(priv, 0x003244, dma_put);
- nv_wr32(priv, 0x003328, ho_put);
- } else
- if (ib_get != ib_put) {
- nv_wr32(priv, 0x003334, ib_put);
- }
- } else {
- nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%08x "
- "Put 0x%08x State 0x%08x (err: %s) Push 0x%08x\n",
- chid, dma_get, dma_put, state,
- nv_dma_state_err(state), push);
-
- if (dma_get != dma_put)
- nv_wr32(priv, 0x003244, dma_put);
- }
-
- nv_wr32(priv, 0x003228, 0x00000000);
- nv_wr32(priv, 0x003220, 0x00000001);
- nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER);
+ nv04_fifo_dma_pusher(device, priv, chid);
status &= ~NV_PFIFO_INTR_DMA_PUSHER;
}
@@ -528,6 +537,12 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
status &= ~0x00000010;
nv_wr32(priv, 0x002100, 0x00000010);
}
+
+ if (status & 0x40000000) {
+ nouveau_event_trigger(priv->base.uevent, 0);
+ nv_wr32(priv, 0x002100, 0x40000000);
+ status &= ~0x40000000;
+ }
}
if (status) {
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
index bd096364f680..840af6172788 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
@@ -129,7 +129,8 @@ nv50_fifo_context_detach(struct nouveau_object *parent, bool suspend,
/* do the kickoff... */
nv_wr32(priv, 0x0032fc, nv_gpuobj(base)->addr >> 12);
if (!nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff)) {
- nv_error(priv, "channel %d unload timeout\n", chan->base.chid);
+ nv_error(priv, "channel %d [%s] unload timeout\n",
+ chan->base.chid, nouveau_client_name(chan));
if (suspend)
ret = -EBUSY;
}
@@ -480,7 +481,7 @@ nv50_fifo_init(struct nouveau_object *object)
nv_wr32(priv, 0x002044, 0x01003fff);
nv_wr32(priv, 0x002100, 0xffffffff);
- nv_wr32(priv, 0x002140, 0xffffffff);
+ nv_wr32(priv, 0x002140, 0xbfffffff);
for (i = 0; i < 128; i++)
nv_wr32(priv, 0x002600 + (i * 4), 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
index 1eb1c512f503..094000e87871 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
@@ -26,6 +26,7 @@
#include <core/client.h>
#include <core/engctx.h>
#include <core/ramht.h>
+#include <core/event.h>
#include <core/class.h>
#include <core/math.h>
@@ -100,7 +101,8 @@ nv84_fifo_context_detach(struct nouveau_object *parent, bool suspend,
done = nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff);
nv_wr32(priv, 0x002520, save);
if (!done) {
- nv_error(priv, "channel %d unload timeout\n", chan->base.chid);
+ nv_error(priv, "channel %d [%s] unload timeout\n",
+ chan->base.chid, nouveau_client_name(chan));
if (suspend)
return -EBUSY;
}
@@ -378,6 +380,20 @@ nv84_fifo_cclass = {
* PFIFO engine
******************************************************************************/
+static void
+nv84_fifo_uevent_enable(struct nouveau_event *event, int index)
+{
+ struct nv84_fifo_priv *priv = event->priv;
+ nv_mask(priv, 0x002140, 0x40000000, 0x40000000);
+}
+
+static void
+nv84_fifo_uevent_disable(struct nouveau_event *event, int index)
+{
+ struct nv84_fifo_priv *priv = event->priv;
+ nv_mask(priv, 0x002140, 0x40000000, 0x00000000);
+}
+
static int
nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -401,6 +417,10 @@ nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ priv->base.uevent->enable = nv84_fifo_uevent_enable;
+ priv->base.uevent->disable = nv84_fifo_uevent_disable;
+ priv->base.uevent->priv = priv;
+
nv_subdev(priv)->unit = 0x00000100;
nv_subdev(priv)->intr = nv04_fifo_intr;
nv_engine(priv)->cclass = &nv84_fifo_cclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
index b4365dde1859..4f226afb5591 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
@@ -27,6 +27,7 @@
#include <core/namedb.h>
#include <core/gpuobj.h>
#include <core/engctx.h>
+#include <core/event.h>
#include <core/class.h>
#include <core/math.h>
#include <core/enum.h>
@@ -149,7 +150,8 @@ nvc0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
nv_wr32(priv, 0x002634, chan->base.chid);
if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) {
- nv_error(priv, "channel %d kick timeout\n", chan->base.chid);
+ nv_error(priv, "channel %d [%s] kick timeout\n",
+ chan->base.chid, nouveau_client_name(chan));
if (suspend)
return -EBUSY;
}
@@ -333,17 +335,17 @@ nvc0_fifo_cclass = {
******************************************************************************/
static const struct nouveau_enum nvc0_fifo_fault_unit[] = {
- { 0x00, "PGRAPH" },
+ { 0x00, "PGRAPH", NULL, NVDEV_ENGINE_GR },
{ 0x03, "PEEPHOLE" },
{ 0x04, "BAR1" },
{ 0x05, "BAR3" },
- { 0x07, "PFIFO" },
- { 0x10, "PBSP" },
- { 0x11, "PPPP" },
+ { 0x07, "PFIFO", NULL, NVDEV_ENGINE_FIFO },
+ { 0x10, "PBSP", NULL, NVDEV_ENGINE_BSP },
+ { 0x11, "PPPP", NULL, NVDEV_ENGINE_PPP },
{ 0x13, "PCOUNTER" },
- { 0x14, "PVP" },
- { 0x15, "PCOPY0" },
- { 0x16, "PCOPY1" },
+ { 0x14, "PVP", NULL, NVDEV_ENGINE_VP },
+ { 0x15, "PCOPY0", NULL, NVDEV_ENGINE_COPY0 },
+ { 0x16, "PCOPY1", NULL, NVDEV_ENGINE_COPY1 },
{ 0x17, "PDAEMON" },
{}
};
@@ -402,6 +404,9 @@ nvc0_fifo_isr_vm_fault(struct nvc0_fifo_priv *priv, int unit)
u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10));
u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10));
u32 client = (stat & 0x00001f00) >> 8;
+ const struct nouveau_enum *en;
+ struct nouveau_engine *engine;
+ struct nouveau_object *engctx = NULL;
switch (unit) {
case 3: /* PEEPHOLE */
@@ -420,16 +425,26 @@ nvc0_fifo_isr_vm_fault(struct nvc0_fifo_priv *priv, int unit)
nv_error(priv, "%s fault at 0x%010llx [", (stat & 0x00000080) ?
"write" : "read", (u64)vahi << 32 | valo);
nouveau_enum_print(nvc0_fifo_fault_reason, stat & 0x0000000f);
- printk("] from ");
- nouveau_enum_print(nvc0_fifo_fault_unit, unit);
+ pr_cont("] from ");
+ en = nouveau_enum_print(nvc0_fifo_fault_unit, unit);
if (stat & 0x00000040) {
- printk("/");
+ pr_cont("/");
nouveau_enum_print(nvc0_fifo_fault_hubclient, client);
} else {
- printk("/GPC%d/", (stat & 0x1f000000) >> 24);
+ pr_cont("/GPC%d/", (stat & 0x1f000000) >> 24);
nouveau_enum_print(nvc0_fifo_fault_gpcclient, client);
}
- printk(" on channel 0x%010llx\n", (u64)inst << 12);
+
+ if (en && en->data2) {
+ engine = nouveau_engine(priv, en->data2);
+ if (engine)
+ engctx = nouveau_engctx_get(engine, inst);
+
+ }
+ pr_cont(" on channel 0x%010llx [%s]\n", (u64)inst << 12,
+ nouveau_client_name(engctx));
+
+ nouveau_engctx_put(engctx);
}
static int
@@ -484,10 +499,12 @@ nvc0_fifo_isr_subfifo_intr(struct nvc0_fifo_priv *priv, int unit)
if (show) {
nv_error(priv, "SUBFIFO%d:", unit);
nouveau_bitfield_print(nvc0_fifo_subfifo_intr, show);
- printk("\n");
- nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x "
- "data 0x%08x\n",
- unit, chid, subc, mthd, data);
+ pr_cont("\n");
+ nv_error(priv,
+ "SUBFIFO%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
+ unit, chid,
+ nouveau_client_name_for_fifo_chid(&priv->base, chid),
+ subc, mthd, data);
}
nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008);
@@ -501,12 +518,34 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
u32 mask = nv_rd32(priv, 0x002140);
u32 stat = nv_rd32(priv, 0x002100) & mask;
+ if (stat & 0x00000001) {
+ u32 intr = nv_rd32(priv, 0x00252c);
+ nv_warn(priv, "INTR 0x00000001: 0x%08x\n", intr);
+ nv_wr32(priv, 0x002100, 0x00000001);
+ stat &= ~0x00000001;
+ }
+
if (stat & 0x00000100) {
- nv_warn(priv, "unknown status 0x00000100\n");
+ u32 intr = nv_rd32(priv, 0x00254c);
+ nv_warn(priv, "INTR 0x00000100: 0x%08x\n", intr);
nv_wr32(priv, 0x002100, 0x00000100);
stat &= ~0x00000100;
}
+ if (stat & 0x00010000) {
+ u32 intr = nv_rd32(priv, 0x00256c);
+ nv_warn(priv, "INTR 0x00010000: 0x%08x\n", intr);
+ nv_wr32(priv, 0x002100, 0x00010000);
+ stat &= ~0x00010000;
+ }
+
+ if (stat & 0x01000000) {
+ u32 intr = nv_rd32(priv, 0x00258c);
+ nv_warn(priv, "INTR 0x01000000: 0x%08x\n", intr);
+ nv_wr32(priv, 0x002100, 0x01000000);
+ stat &= ~0x01000000;
+ }
+
if (stat & 0x10000000) {
u32 units = nv_rd32(priv, 0x00259c);
u32 u = units;
@@ -536,11 +575,20 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
}
if (stat & 0x40000000) {
- nv_warn(priv, "unknown status 0x40000000\n");
- nv_mask(priv, 0x002a00, 0x00000000, 0x00000000);
+ u32 intr0 = nv_rd32(priv, 0x0025a4);
+ u32 intr1 = nv_mask(priv, 0x002a00, 0x00000000, 0x00000);
+ nv_debug(priv, "INTR 0x40000000: 0x%08x 0x%08x\n",
+ intr0, intr1);
stat &= ~0x40000000;
}
+ if (stat & 0x80000000) {
+ u32 intr = nv_mask(priv, 0x0025a8, 0x00000000, 0x00000000);
+ nouveau_event_trigger(priv->base.uevent, 0);
+ nv_debug(priv, "INTR 0x80000000: 0x%08x\n", intr);
+ stat &= ~0x80000000;
+ }
+
if (stat) {
nv_fatal(priv, "unhandled status 0x%08x\n", stat);
nv_wr32(priv, 0x002100, stat);
@@ -548,6 +596,20 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
}
}
+static void
+nvc0_fifo_uevent_enable(struct nouveau_event *event, int index)
+{
+ struct nvc0_fifo_priv *priv = event->priv;
+ nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
+}
+
+static void
+nvc0_fifo_uevent_disable(struct nouveau_event *event, int index)
+{
+ struct nvc0_fifo_priv *priv = event->priv;
+ nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
+}
+
static int
nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -581,6 +643,10 @@ nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ priv->base.uevent->enable = nvc0_fifo_uevent_enable;
+ priv->base.uevent->disable = nvc0_fifo_uevent_disable;
+ priv->base.uevent->priv = priv;
+
nv_subdev(priv)->unit = 0x00000100;
nv_subdev(priv)->intr = nvc0_fifo_intr;
nv_engine(priv)->cclass = &nvc0_fifo_cclass;
@@ -639,7 +705,8 @@ nvc0_fifo_init(struct nouveau_object *object)
nv_wr32(priv, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */
nv_wr32(priv, 0x002100, 0xffffffff);
- nv_wr32(priv, 0x002140, 0xbfffffff);
+ nv_wr32(priv, 0x002140, 0x3fffffff);
+ nv_wr32(priv, 0x002628, 0x00000001); /* makes mthd 0x20 work */
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
index c930da99c2c1..4419e40d88e9 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
@@ -27,6 +27,7 @@
#include <core/namedb.h>
#include <core/gpuobj.h>
#include <core/engctx.h>
+#include <core/event.h>
#include <core/class.h>
#include <core/math.h>
#include <core/enum.h>
@@ -184,7 +185,8 @@ nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
nv_wr32(priv, 0x002634, chan->base.chid);
if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) {
- nv_error(priv, "channel %d kick timeout\n", chan->base.chid);
+ nv_error(priv, "channel %d [%s] kick timeout\n",
+ chan->base.chid, nouveau_client_name(chan));
if (suspend)
return -EBUSY;
}
@@ -412,20 +414,34 @@ nve0_fifo_isr_vm_fault(struct nve0_fifo_priv *priv, int unit)
u32 vahi = nv_rd32(priv, 0x2808 + (unit * 0x10));
u32 stat = nv_rd32(priv, 0x280c + (unit * 0x10));
u32 client = (stat & 0x00001f00) >> 8;
+ const struct nouveau_enum *en;
+ struct nouveau_engine *engine;
+ struct nouveau_object *engctx = NULL;
nv_error(priv, "PFIFO: %s fault at 0x%010llx [", (stat & 0x00000080) ?
"write" : "read", (u64)vahi << 32 | valo);
nouveau_enum_print(nve0_fifo_fault_reason, stat & 0x0000000f);
- printk("] from ");
- nouveau_enum_print(nve0_fifo_fault_unit, unit);
+ pr_cont("] from ");
+ en = nouveau_enum_print(nve0_fifo_fault_unit, unit);
if (stat & 0x00000040) {
- printk("/");
+ pr_cont("/");
nouveau_enum_print(nve0_fifo_fault_hubclient, client);
} else {
- printk("/GPC%d/", (stat & 0x1f000000) >> 24);
+ pr_cont("/GPC%d/", (stat & 0x1f000000) >> 24);
nouveau_enum_print(nve0_fifo_fault_gpcclient, client);
}
- printk(" on channel 0x%010llx\n", (u64)inst << 12);
+
+ if (en && en->data2) {
+ engine = nouveau_engine(priv, en->data2);
+ if (engine)
+ engctx = nouveau_engctx_get(engine, inst);
+
+ }
+
+ pr_cont(" on channel 0x%010llx [%s]\n", (u64)inst << 12,
+ nouveau_client_name(engctx));
+
+ nouveau_engctx_put(engctx);
}
static int
@@ -480,10 +496,12 @@ nve0_fifo_isr_subfifo_intr(struct nve0_fifo_priv *priv, int unit)
if (show) {
nv_error(priv, "SUBFIFO%d:", unit);
nouveau_bitfield_print(nve0_fifo_subfifo_intr, show);
- printk("\n");
- nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x "
- "data 0x%08x\n",
- unit, chid, subc, mthd, data);
+ pr_cont("\n");
+ nv_error(priv,
+ "SUBFIFO%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
+ unit, chid,
+ nouveau_client_name_for_fifo_chid(&priv->base, chid),
+ subc, mthd, data);
}
nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008);
@@ -537,6 +555,12 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
stat &= ~0x40000000;
}
+ if (stat & 0x80000000) {
+ nouveau_event_trigger(priv->base.uevent, 0);
+ nv_wr32(priv, 0x002100, 0x80000000);
+ stat &= ~0x80000000;
+ }
+
if (stat) {
nv_fatal(priv, "unhandled status 0x%08x\n", stat);
nv_wr32(priv, 0x002100, stat);
@@ -544,6 +568,20 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
}
}
+static void
+nve0_fifo_uevent_enable(struct nouveau_event *event, int index)
+{
+ struct nve0_fifo_priv *priv = event->priv;
+ nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
+}
+
+static void
+nve0_fifo_uevent_disable(struct nouveau_event *event, int index)
+{
+ struct nve0_fifo_priv *priv = event->priv;
+ nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
+}
+
static int
nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -567,6 +605,10 @@ nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ priv->base.uevent->enable = nve0_fifo_uevent_enable;
+ priv->base.uevent->disable = nve0_fifo_uevent_disable;
+ priv->base.uevent->priv = priv;
+
nv_subdev(priv)->unit = 0x00000100;
nv_subdev(priv)->intr = nve0_fifo_intr;
nv_engine(priv)->cclass = &nve0_fifo_cclass;
@@ -617,7 +659,7 @@ nve0_fifo_init(struct nouveau_object *object)
nv_wr32(priv, 0x002a00, 0xffffffff);
nv_wr32(priv, 0x002100, 0xffffffff);
- nv_wr32(priv, 0x002140, 0xbfffffff);
+ nv_wr32(priv, 0x002140, 0x3fffffff);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c
index e30a9c5ff1fc..ad13dcdd15f9 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c
@@ -22,6 +22,7 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include <core/client.h>
#include <core/os.h>
#include <core/class.h>
#include <core/handle.h>
@@ -1297,16 +1298,17 @@ nv04_graph_intr(struct nouveau_subdev *subdev)
nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001);
if (show) {
- nv_error(priv, "");
+ nv_error(priv, "%s", "");
nouveau_bitfield_print(nv04_graph_intr_name, show);
- printk(" nsource:");
+ pr_cont(" nsource:");
nouveau_bitfield_print(nv04_graph_nsource, nsource);
- printk(" nstatus:");
+ pr_cont(" nstatus:");
nouveau_bitfield_print(nv04_graph_nstatus, nstatus);
- printk("\n");
- nv_error(priv, "ch %d/%d class 0x%04x "
- "mthd 0x%04x data 0x%08x\n",
- chid, subc, class, mthd, data);
+ pr_cont("\n");
+ nv_error(priv,
+ "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, nouveau_client_name(chan), subc, class, mthd,
+ data);
}
nouveau_namedb_put(handle);
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
index 5c0f843ea249..23c143aaa556 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
@@ -22,6 +22,7 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include <core/client.h>
#include <core/os.h>
#include <core/class.h>
#include <core/handle.h>
@@ -1193,16 +1194,17 @@ nv10_graph_intr(struct nouveau_subdev *subdev)
nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001);
if (show) {
- nv_error(priv, "");
+ nv_error(priv, "%s", "");
nouveau_bitfield_print(nv10_graph_intr_name, show);
- printk(" nsource:");
+ pr_cont(" nsource:");
nouveau_bitfield_print(nv04_graph_nsource, nsource);
- printk(" nstatus:");
+ pr_cont(" nstatus:");
nouveau_bitfield_print(nv10_graph_nstatus, nstatus);
- printk("\n");
- nv_error(priv, "ch %d/%d class 0x%04x "
- "mthd 0x%04x data 0x%08x\n",
- chid, subc, class, mthd, data);
+ pr_cont("\n");
+ nv_error(priv,
+ "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, nouveau_client_name(chan), subc, class, mthd,
+ data);
}
nouveau_namedb_put(handle);
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
index 5b20401bf911..0607b9801748 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
@@ -1,3 +1,4 @@
+#include <core/client.h>
#include <core/os.h>
#include <core/class.h>
#include <core/engctx.h>
@@ -224,15 +225,17 @@ nv20_graph_intr(struct nouveau_subdev *subdev)
nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001);
if (show) {
- nv_error(priv, "");
+ nv_error(priv, "%s", "");
nouveau_bitfield_print(nv10_graph_intr_name, show);
- printk(" nsource:");
+ pr_cont(" nsource:");
nouveau_bitfield_print(nv04_graph_nsource, nsource);
- printk(" nstatus:");
+ pr_cont(" nstatus:");
nouveau_bitfield_print(nv10_graph_nstatus, nstatus);
- printk("\n");
- nv_error(priv, "ch %d/%d class 0x%04x mthd 0x%04x data 0x%08x\n",
- chid, subc, class, mthd, data);
+ pr_cont("\n");
+ nv_error(priv,
+ "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, nouveau_client_name(engctx), subc, class, mthd,
+ data);
}
nouveau_engctx_put(engctx);
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
index 0b36dd3deebd..17049d5c723d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/os.h>
#include <core/class.h>
#include <core/handle.h>
@@ -321,16 +322,17 @@ nv40_graph_intr(struct nouveau_subdev *subdev)
nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001);
if (show) {
- nv_error(priv, "");
+ nv_error(priv, "%s", "");
nouveau_bitfield_print(nv10_graph_intr_name, show);
- printk(" nsource:");
+ pr_cont(" nsource:");
nouveau_bitfield_print(nv04_graph_nsource, nsource);
- printk(" nstatus:");
+ pr_cont(" nstatus:");
nouveau_bitfield_print(nv10_graph_nstatus, nstatus);
- printk("\n");
- nv_error(priv, "ch %d [0x%08x] subc %d class 0x%04x "
- "mthd 0x%04x data 0x%08x\n",
- chid, inst << 4, subc, class, mthd, data);
+ pr_cont("\n");
+ nv_error(priv,
+ "ch %d [0x%08x %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, inst << 4, nouveau_client_name(engctx), subc,
+ class, mthd, data);
}
nouveau_engctx_put(engctx);
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
index b1c3d835b4c2..f2b1a7a124f2 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
@@ -24,6 +24,7 @@
#include <core/os.h>
#include <core/class.h>
+#include <core/client.h>
#include <core/handle.h>
#include <core/engctx.h>
#include <core/enum.h>
@@ -418,7 +419,7 @@ nv50_priv_mp_trap(struct nv50_graph_priv *priv, int tpid, int display)
nv_error(priv, "TRAP_MP_EXEC - "
"TP %d MP %d: ", tpid, i);
nouveau_enum_print(nv50_mp_exec_error_names, status);
- printk(" at %06x warp %d, opcode %08x %08x\n",
+ pr_cont(" at %06x warp %d, opcode %08x %08x\n",
pc&0xffffff, pc >> 24,
oplow, ophigh);
}
@@ -532,7 +533,7 @@ nv50_priv_tp_trap(struct nv50_graph_priv *priv, int type, u32 ustatus_old,
static int
nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
- int chid, u64 inst)
+ int chid, u64 inst, struct nouveau_object *engctx)
{
u32 status = nv_rd32(priv, 0x400108);
u32 ustatus;
@@ -565,12 +566,11 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
nv_error(priv, "TRAP DISPATCH_FAULT\n");
if (display && (addr & 0x80000000)) {
- nv_error(priv, "ch %d [0x%010llx] "
- "subc %d class 0x%04x mthd 0x%04x "
- "data 0x%08x%08x "
- "400808 0x%08x 400848 0x%08x\n",
- chid, inst, subc, class, mthd, datah,
- datal, addr, r848);
+ nv_error(priv,
+ "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x%08x 400808 0x%08x 400848 0x%08x\n",
+ chid, inst,
+ nouveau_client_name(engctx), subc,
+ class, mthd, datah, datal, addr, r848);
} else
if (display) {
nv_error(priv, "no stuck command?\n");
@@ -591,11 +591,11 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
nv_error(priv, "TRAP DISPATCH_QUERY\n");
if (display && (addr & 0x80000000)) {
- nv_error(priv, "ch %d [0x%010llx] "
- "subc %d class 0x%04x mthd 0x%04x "
- "data 0x%08x 40084c 0x%08x\n",
- chid, inst, subc, class, mthd,
- data, addr);
+ nv_error(priv,
+ "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x 40084c 0x%08x\n",
+ chid, inst,
+ nouveau_client_name(engctx), subc,
+ class, mthd, data, addr);
} else
if (display) {
nv_error(priv, "no stuck command?\n");
@@ -623,7 +623,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
if (display) {
nv_error(priv, "TRAP_M2MF");
nouveau_bitfield_print(nv50_graph_trap_m2mf, ustatus);
- printk("\n");
+ pr_cont("\n");
nv_error(priv, "TRAP_M2MF %08x %08x %08x %08x\n",
nv_rd32(priv, 0x406804), nv_rd32(priv, 0x406808),
nv_rd32(priv, 0x40680c), nv_rd32(priv, 0x406810));
@@ -644,7 +644,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
if (display) {
nv_error(priv, "TRAP_VFETCH");
nouveau_bitfield_print(nv50_graph_trap_vfetch, ustatus);
- printk("\n");
+ pr_cont("\n");
nv_error(priv, "TRAP_VFETCH %08x %08x %08x %08x\n",
nv_rd32(priv, 0x400c00), nv_rd32(priv, 0x400c08),
nv_rd32(priv, 0x400c0c), nv_rd32(priv, 0x400c10));
@@ -661,7 +661,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
if (display) {
nv_error(priv, "TRAP_STRMOUT");
nouveau_bitfield_print(nv50_graph_trap_strmout, ustatus);
- printk("\n");
+ pr_cont("\n");
nv_error(priv, "TRAP_STRMOUT %08x %08x %08x %08x\n",
nv_rd32(priv, 0x401804), nv_rd32(priv, 0x401808),
nv_rd32(priv, 0x40180c), nv_rd32(priv, 0x401810));
@@ -682,7 +682,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
if (display) {
nv_error(priv, "TRAP_CCACHE");
nouveau_bitfield_print(nv50_graph_trap_ccache, ustatus);
- printk("\n");
+ pr_cont("\n");
nv_error(priv, "TRAP_CCACHE %08x %08x %08x %08x"
" %08x %08x %08x\n",
nv_rd32(priv, 0x405000), nv_rd32(priv, 0x405004),
@@ -774,11 +774,12 @@ nv50_graph_intr(struct nouveau_subdev *subdev)
u32 ecode = nv_rd32(priv, 0x400110);
nv_error(priv, "DATA_ERROR ");
nouveau_enum_print(nv50_data_error_names, ecode);
- printk("\n");
+ pr_cont("\n");
}
if (stat & 0x00200000) {
- if (!nv50_graph_trap_handler(priv, show, chid, (u64)inst << 12))
+ if (!nv50_graph_trap_handler(priv, show, chid, (u64)inst << 12,
+ engctx))
show &= ~0x00200000;
}
@@ -786,12 +787,13 @@ nv50_graph_intr(struct nouveau_subdev *subdev)
nv_wr32(priv, 0x400500, 0x00010001);
if (show) {
- nv_error(priv, "");
+ nv_error(priv, "%s", "");
nouveau_bitfield_print(nv50_graph_intr_name, show);
- printk("\n");
- nv_error(priv, "ch %d [0x%010llx] subc %d class 0x%04x "
- "mthd 0x%04x data 0x%08x\n",
- chid, (u64)inst << 12, subc, class, mthd, data);
+ pr_cont("\n");
+ nv_error(priv,
+ "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, (u64)inst << 12, nouveau_client_name(engctx),
+ subc, class, mthd, data);
}
if (nv_rd32(priv, 0x400824) & (1 << 31))
@@ -907,9 +909,8 @@ nv50_graph_init(struct nouveau_object *object)
nv_wr32(priv, 0x400828, 0x00000000);
nv_wr32(priv, 0x40082c, 0x00000000);
nv_wr32(priv, 0x400830, 0x00000000);
- nv_wr32(priv, 0x400724, 0x00000000);
nv_wr32(priv, 0x40032c, 0x00000000);
- nv_wr32(priv, 0x400320, 4); /* CTXCTL_CMD = NEWCTXDMA */
+ nv_wr32(priv, 0x400330, 0x00000000);
/* some unknown zcull magic */
switch (nv_device(priv)->chipset & 0xf0) {
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
index 45aff5f5085a..0de0dd724aff 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
@@ -433,10 +433,10 @@ nvc0_graph_intr(struct nouveau_subdev *subdev)
if (stat & 0x00000010) {
handle = nouveau_handle_get_class(engctx, class);
if (!handle || nv_call(handle->object, mthd, data)) {
- nv_error(priv, "ILLEGAL_MTHD ch %d [0x%010llx] "
- "subc %d class 0x%04x mthd 0x%04x "
- "data 0x%08x\n",
- chid, inst << 12, subc, class, mthd, data);
+ nv_error(priv,
+ "ILLEGAL_MTHD ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, inst << 12, nouveau_client_name(engctx),
+ subc, class, mthd, data);
}
nouveau_handle_put(handle);
nv_wr32(priv, 0x400100, 0x00000010);
@@ -444,9 +444,10 @@ nvc0_graph_intr(struct nouveau_subdev *subdev)
}
if (stat & 0x00000020) {
- nv_error(priv, "ILLEGAL_CLASS ch %d [0x%010llx] subc %d "
- "class 0x%04x mthd 0x%04x data 0x%08x\n",
- chid, inst << 12, subc, class, mthd, data);
+ nv_error(priv,
+ "ILLEGAL_CLASS ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, inst << 12, nouveau_client_name(engctx), subc,
+ class, mthd, data);
nv_wr32(priv, 0x400100, 0x00000020);
stat &= ~0x00000020;
}
@@ -454,15 +455,16 @@ nvc0_graph_intr(struct nouveau_subdev *subdev)
if (stat & 0x00100000) {
nv_error(priv, "DATA_ERROR [");
nouveau_enum_print(nv50_data_error_names, code);
- printk("] ch %d [0x%010llx] subc %d class 0x%04x "
- "mthd 0x%04x data 0x%08x\n",
- chid, inst << 12, subc, class, mthd, data);
+ pr_cont("] ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, inst << 12, nouveau_client_name(engctx), subc,
+ class, mthd, data);
nv_wr32(priv, 0x400100, 0x00100000);
stat &= ~0x00100000;
}
if (stat & 0x00200000) {
- nv_error(priv, "TRAP ch %d [0x%010llx]\n", chid, inst << 12);
+ nv_error(priv, "TRAP ch %d [0x%010llx %s]\n", chid, inst << 12,
+ nouveau_client_name(engctx));
nvc0_graph_trap_intr(priv);
nv_wr32(priv, 0x400100, 0x00200000);
stat &= ~0x00200000;
@@ -611,10 +613,8 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
static void
nvc0_graph_dtor_fw(struct nvc0_graph_fuc *fuc)
{
- if (fuc->data) {
- kfree(fuc->data);
- fuc->data = NULL;
- }
+ kfree(fuc->data);
+ fuc->data = NULL;
}
void
@@ -622,8 +622,7 @@ nvc0_graph_dtor(struct nouveau_object *object)
{
struct nvc0_graph_priv *priv = (void *)object;
- if (priv->data)
- kfree(priv->data);
+ kfree(priv->data);
nvc0_graph_dtor_fw(&priv->fuc409c);
nvc0_graph_dtor_fw(&priv->fuc409d);
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c
index 9f82e9702b46..61cec0f6ff1c 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c
@@ -78,15 +78,16 @@ nve0_graph_ctxctl_isr(struct nvc0_graph_priv *priv)
}
static void
-nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst)
+nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst,
+ struct nouveau_object *engctx)
{
u32 trap = nv_rd32(priv, 0x400108);
int rop;
if (trap & 0x00000001) {
u32 stat = nv_rd32(priv, 0x404000);
- nv_error(priv, "DISPATCH ch %d [0x%010llx] 0x%08x\n",
- chid, inst, stat);
+ nv_error(priv, "DISPATCH ch %d [0x%010llx %s] 0x%08x\n",
+ chid, inst, nouveau_client_name(engctx), stat);
nv_wr32(priv, 0x404000, 0xc0000000);
nv_wr32(priv, 0x400108, 0x00000001);
trap &= ~0x00000001;
@@ -94,8 +95,8 @@ nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst)
if (trap & 0x00000010) {
u32 stat = nv_rd32(priv, 0x405840);
- nv_error(priv, "SHADER ch %d [0x%010llx] 0x%08x\n",
- chid, inst, stat);
+ nv_error(priv, "SHADER ch %d [0x%010llx %s] 0x%08x\n",
+ chid, inst, nouveau_client_name(engctx), stat);
nv_wr32(priv, 0x405840, 0xc0000000);
nv_wr32(priv, 0x400108, 0x00000010);
trap &= ~0x00000010;
@@ -105,8 +106,10 @@ nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst)
for (rop = 0; rop < priv->rop_nr; rop++) {
u32 statz = nv_rd32(priv, ROP_UNIT(rop, 0x070));
u32 statc = nv_rd32(priv, ROP_UNIT(rop, 0x144));
- nv_error(priv, "ROP%d ch %d [0x%010llx] 0x%08x 0x%08x\n",
- rop, chid, inst, statz, statc);
+ nv_error(priv,
+ "ROP%d ch %d [0x%010llx %s] 0x%08x 0x%08x\n",
+ rop, chid, inst, nouveau_client_name(engctx),
+ statz, statc);
nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000);
nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000);
}
@@ -115,8 +118,8 @@ nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst)
}
if (trap) {
- nv_error(priv, "TRAP ch %d [0x%010llx] 0x%08x\n",
- chid, inst, trap);
+ nv_error(priv, "TRAP ch %d [0x%010llx %s] 0x%08x\n",
+ chid, inst, nouveau_client_name(engctx), trap);
nv_wr32(priv, 0x400108, trap);
}
}
@@ -145,10 +148,10 @@ nve0_graph_intr(struct nouveau_subdev *subdev)
if (stat & 0x00000010) {
handle = nouveau_handle_get_class(engctx, class);
if (!handle || nv_call(handle->object, mthd, data)) {
- nv_error(priv, "ILLEGAL_MTHD ch %d [0x%010llx] "
- "subc %d class 0x%04x mthd 0x%04x "
- "data 0x%08x\n",
- chid, inst, subc, class, mthd, data);
+ nv_error(priv,
+ "ILLEGAL_MTHD ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, inst, nouveau_client_name(engctx), subc,
+ class, mthd, data);
}
nouveau_handle_put(handle);
nv_wr32(priv, 0x400100, 0x00000010);
@@ -156,9 +159,10 @@ nve0_graph_intr(struct nouveau_subdev *subdev)
}
if (stat & 0x00000020) {
- nv_error(priv, "ILLEGAL_CLASS ch %d [0x%010llx] subc %d "
- "class 0x%04x mthd 0x%04x data 0x%08x\n",
- chid, inst, subc, class, mthd, data);
+ nv_error(priv,
+ "ILLEGAL_CLASS ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, inst, nouveau_client_name(engctx), subc, class,
+ mthd, data);
nv_wr32(priv, 0x400100, 0x00000020);
stat &= ~0x00000020;
}
@@ -166,15 +170,15 @@ nve0_graph_intr(struct nouveau_subdev *subdev)
if (stat & 0x00100000) {
nv_error(priv, "DATA_ERROR [");
nouveau_enum_print(nv50_data_error_names, code);
- printk("] ch %d [0x%010llx] subc %d class 0x%04x "
- "mthd 0x%04x data 0x%08x\n",
- chid, inst, subc, class, mthd, data);
+ pr_cont("] ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+ chid, inst, nouveau_client_name(engctx), subc, class,
+ mthd, data);
nv_wr32(priv, 0x400100, 0x00100000);
stat &= ~0x00100000;
}
if (stat & 0x00200000) {
- nve0_graph_trap_isr(priv, chid, inst);
+ nve0_graph_trap_isr(priv, chid, inst, engctx);
nv_wr32(priv, 0x400100, 0x00200000);
stat &= ~0x00200000;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
index 9fd86375f4c4..49ecbb859b25 100644
--- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
+++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/os.h>
#include <core/class.h>
#include <core/engctx.h>
@@ -231,8 +232,10 @@ nv31_mpeg_intr(struct nouveau_subdev *subdev)
nv_wr32(priv, 0x00b230, 0x00000001);
if (show) {
- nv_error(priv, "ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n",
- chid, inst << 4, stat, type, mthd, data);
+ nv_error(priv,
+ "ch %d [0x%08x %s] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ chid, inst << 4, nouveau_client_name(engctx), stat,
+ type, mthd, data);
}
nouveau_engctx_put(engctx);
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c
index b0e7e1c01ce6..c48e74953771 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c
@@ -28,6 +28,9 @@
#include <core/namedb.h>
#include <core/handle.h>
#include <core/gpuobj.h>
+#include <core/event.h>
+
+#include <subdev/bar.h>
#include <engine/software.h>
#include <engine/disp.h>
@@ -90,18 +93,11 @@ nv50_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
{
struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
struct nouveau_disp *disp = nouveau_disp(object);
- unsigned long flags;
u32 crtc = *(u32 *)args;
-
if (crtc > 1)
return -EINVAL;
- disp->vblank.get(disp->vblank.data, crtc);
-
- spin_lock_irqsave(&disp->vblank.lock, flags);
- list_add(&chan->base.vblank.head, &disp->vblank.list);
- chan->base.vblank.crtc = crtc;
- spin_unlock_irqrestore(&disp->vblank.lock, flags);
+ nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
return 0;
}
@@ -136,6 +132,29 @@ nv50_software_sclass[] = {
******************************************************************************/
static int
+nv50_software_vblsem_release(struct nouveau_eventh *event, int head)
+{
+ struct nouveau_software_chan *chan =
+ container_of(event, struct nouveau_software_chan, vblank.event);
+ struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
+ struct nouveau_bar *bar = nouveau_bar(priv);
+
+ nv_wr32(priv, 0x001704, chan->vblank.channel);
+ nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma);
+ bar->flush(bar);
+
+ if (nv_device(priv)->chipset == 0x50) {
+ nv_wr32(priv, 0x001570, chan->vblank.offset);
+ nv_wr32(priv, 0x001574, chan->vblank.value);
+ } else {
+ nv_wr32(priv, 0x060010, chan->vblank.offset);
+ nv_wr32(priv, 0x060014, chan->vblank.value);
+ }
+
+ return NVKM_EVENT_DROP;
+}
+
+static int
nv50_software_context_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -150,6 +169,7 @@ nv50_software_context_ctor(struct nouveau_object *parent,
return ret;
chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
+ chan->base.vblank.event.func = nv50_software_vblsem_release;
return 0;
}
@@ -170,8 +190,8 @@ nv50_software_cclass = {
static int
nv50_software_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
{
struct nv50_software_priv *priv;
int ret;
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
index 282a1cd1bc2f..a523eaad47e3 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
@@ -25,6 +25,9 @@
#include <core/os.h>
#include <core/class.h>
#include <core/engctx.h>
+#include <core/event.h>
+
+#include <subdev/bar.h>
#include <engine/software.h>
#include <engine/disp.h>
@@ -72,18 +75,12 @@ nvc0_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
{
struct nvc0_software_chan *chan = (void *)nv_engctx(object->parent);
struct nouveau_disp *disp = nouveau_disp(object);
- unsigned long flags;
u32 crtc = *(u32 *)args;
if ((nv_device(object)->card_type < NV_E0 && crtc > 1) || crtc > 3)
return -EINVAL;
- disp->vblank.get(disp->vblank.data, crtc);
-
- spin_lock_irqsave(&disp->vblank.lock, flags);
- list_add(&chan->base.vblank.head, &disp->vblank.list);
- chan->base.vblank.crtc = crtc;
- spin_unlock_irqrestore(&disp->vblank.lock, flags);
+ nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
return 0;
}
@@ -118,6 +115,23 @@ nvc0_software_sclass[] = {
******************************************************************************/
static int
+nvc0_software_vblsem_release(struct nouveau_eventh *event, int head)
+{
+ struct nouveau_software_chan *chan =
+ container_of(event, struct nouveau_software_chan, vblank.event);
+ struct nvc0_software_priv *priv = (void *)nv_object(chan)->engine;
+ struct nouveau_bar *bar = nouveau_bar(priv);
+
+ nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
+ bar->flush(bar);
+ nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset));
+ nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset));
+ nv_wr32(priv, 0x060014, chan->vblank.value);
+
+ return NVKM_EVENT_DROP;
+}
+
+static int
nvc0_software_context_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -132,6 +146,7 @@ nvc0_software_context_ctor(struct nouveau_object *parent,
return ret;
chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
+ chan->base.vblank.event.func = nvc0_software_vblsem_release;
return 0;
}