diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/engine')
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; } |