Commit 1f3a574a authored by Dave Airlie's avatar Dave Airlie

Merge branch 'drm-nouveau-next' of...

Merge branch 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux-2.6 into drm-next

Nothing terribly exciting in here probably:
- reworked thermal stuff from mupuf/I, has a chance of possibly working
well enough when we get to being able to reclock..
- driver will report mmio access faults on chipsets where it's supported
- will now sleep waiting on fences on nv84+ rather than polling
- some cleanup of the internal fencing, looking towards sli/dmabuf sync
- initial support for anx9805 dp/tmds encoder
- nv50+ display fixes related to the above, and also might fix a few
other issues
- nicer error reporting (will log process names with channel errors)
- various other random fixes

* 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux-2.6: (87 commits)
  nouveau: ACPI support depends on X86 and X86_PLATFORM_DEVICES
  drm/nouveau/i2c: add support for ddc/aux, and dp link training on anx9805
  drm/nv50: initial kms support for off-chip TMDS/DP encoders
  drm/nv50-/disp: initial supervisor support for off-chip encoders
  drm/nv50-/disp: initial work towards supporting external encoders
  drm/nv50-/kms: remove unnecessary wait-for-completion points
  drm/nv50-/disp: move DP link training to core and train from supervisor
  drm/nv50-/disp: handle supervisor tasks from workqueue
  drm/nouveau/i2c: create proper chipset-specific class implementations
  drm/nv50-/disp: 0x0000 is a valid udisp config value
  drm/nv50/devinit: reverse the logic for running encoder init scripts
  drm/nouveau/bios: store a type/mask hash in parsed dcb data
  drm/nouveau/i2c: extend type to 16-bits, add lookup-by-type function
  drm/nouveau/i2c: aux channels not necessarily on nvio
  drm/nouveau/i2c: fix a bit of a thinko in nv_wri2cr helper functions
  drm/nouveau/bios: parse external transmitter type if off-chip
  drm/nouveau: store i2c port pointer directly in nouveau_encoder
  drm/nouveau/i2c: handle i2c/aux mux outside of port lookup function
  drm/nv50/graph: avoid touching 400724, it doesn't exist
  drm/nouveau: Fix DPMS 1 on G4 Snowball, from snow white to coal black.
  ...
parents b81e059e a91ed42d
Kernel driver nouveau
===================
Supported chips:
* NV43+
Authors: Martin Peres (mupuf) <martin.peres@labri.fr>
Description
---------
This driver allows to read the GPU core temperature, drive the GPU fan and
set temperature alarms.
Currently, due to the absence of in-kernel API to access HWMON drivers, Nouveau
cannot access any of the i2c external monitoring chips it may find. If you
have one of those, temperature and/or fan management through Nouveau's HWMON
interface is likely not to work. This document may then not cover your situation
entirely.
Temperature management
--------------------
Temperature is exposed under as a read-only HWMON attribute temp1_input.
In order to protect the GPU from overheating, Nouveau supports 4 configurable
temperature thresholds:
* Fan_boost: Fan speed is set to 100% when reaching this temperature;
* Downclock: The GPU will be downclocked to reduce its power dissipation;
* Critical: The GPU is put on hold to further lower power dissipation;
* Shutdown: Shut the computer down to protect your GPU.
WARNING: Some of these thresholds may not be used by Nouveau depending
on your chipset.
The default value for these thresholds comes from the GPU's vbios. These
thresholds can be configured thanks to the following HWMON attributes:
* Fan_boost: temp1_auto_point1_temp and temp1_auto_point1_temp_hyst;
* Downclock: temp1_max and temp1_max_hyst;
* Critical: temp1_crit and temp1_crit_hyst;
* Shutdown: temp1_emergency and temp1_emergency_hyst.
NOTE: Remember that the values are stored as milli degrees Celcius. Don't forget
to multiply!
Fan management
------------
Not all cards have a drivable fan. If you do, then the following HWMON
attributes should be available:
* pwm1_enable: Current fan management mode (NONE, MANUAL or AUTO);
* pwm1: Current PWM value (power percentage);
* pwm1_min: The minimum PWM speed allowed;
* pwm1_max: The maximum PWM speed allowed (bypassed when hitting Fan_boost);
You may also have the following attribute:
* fan1_input: Speed in RPM of your fan.
Your fan can be driven in different modes:
* 0: The fan is left untouched;
* 1: The fan can be driven in manual (use pwm1 to change the speed);
* 2; The fan is driven automatically depending on the temperature.
NOTE: Be sure to use the manual mode if you want to drive the fan speed manually
NOTE2: Not all fan management modes may be supported on all chipsets. We are
working on it.
Bug reports
---------
Thermal management on Nouveau is new and may not work on all cards. If you have
inquiries, please ping mupuf on IRC (#nouveau, freenode).
Bug reports should be filled on Freedesktop's bug tracker. Please follow
http://nouveau.freedesktop.org/wiki/Bugs
......@@ -11,8 +11,9 @@ config DRM_NOUVEAU
select FRAMEBUFFER_CONSOLE if !EXPERT
select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT
select ACPI_WMI if ACPI
select MXM_WMI if ACPI
select X86_PLATFORM_DEVICES if ACPI && X86
select ACPI_WMI if ACPI && X86
select MXM_WMI if ACPI && X86
select POWER_SUPPLY
help
Choose this option for open-source nVidia support.
......
......@@ -11,6 +11,7 @@ nouveau-y := core/core/client.o
nouveau-y += core/core/engctx.o
nouveau-y += core/core/engine.o
nouveau-y += core/core/enum.o
nouveau-y += core/core/event.o
nouveau-y += core/core/falcon.o
nouveau-y += core/core/gpuobj.o
nouveau-y += core/core/handle.o
......@@ -40,6 +41,11 @@ nouveau-y += core/subdev/bios/mxm.o
nouveau-y += core/subdev/bios/perf.o
nouveau-y += core/subdev/bios/pll.o
nouveau-y += core/subdev/bios/therm.o
nouveau-y += core/subdev/bios/xpio.o
nouveau-y += core/subdev/bus/nv04.o
nouveau-y += core/subdev/bus/nv31.o
nouveau-y += core/subdev/bus/nv50.o
nouveau-y += core/subdev/bus/nvc0.o
nouveau-y += core/subdev/clock/nv04.o
nouveau-y += core/subdev/clock/nv40.o
nouveau-y += core/subdev/clock/nv50.o
......@@ -85,9 +91,16 @@ nouveau-y += core/subdev/gpio/base.o
nouveau-y += core/subdev/gpio/nv10.o
nouveau-y += core/subdev/gpio/nv50.o
nouveau-y += core/subdev/gpio/nvd0.o
nouveau-y += core/subdev/gpio/nve0.o
nouveau-y += core/subdev/i2c/base.o
nouveau-y += core/subdev/i2c/anx9805.o
nouveau-y += core/subdev/i2c/aux.o
nouveau-y += core/subdev/i2c/bit.o
nouveau-y += core/subdev/i2c/nv04.o
nouveau-y += core/subdev/i2c/nv4e.o
nouveau-y += core/subdev/i2c/nv50.o
nouveau-y += core/subdev/i2c/nv94.o
nouveau-y += core/subdev/i2c/nvd0.o
nouveau-y += core/subdev/ibus/nvc0.o
nouveau-y += core/subdev/ibus/nve0.o
nouveau-y += core/subdev/instmem/base.o
......@@ -106,10 +119,15 @@ nouveau-y += core/subdev/mxm/mxms.o
nouveau-y += core/subdev/mxm/nv50.o
nouveau-y += core/subdev/therm/base.o
nouveau-y += core/subdev/therm/fan.o
nouveau-y += core/subdev/therm/fannil.o
nouveau-y += core/subdev/therm/fanpwm.o
nouveau-y += core/subdev/therm/fantog.o
nouveau-y += core/subdev/therm/ic.o
nouveau-y += core/subdev/therm/temp.o
nouveau-y += core/subdev/therm/nv40.o
nouveau-y += core/subdev/therm/nv50.o
nouveau-y += core/subdev/therm/temp.o
nouveau-y += core/subdev/therm/nva3.o
nouveau-y += core/subdev/therm/nvd0.o
nouveau-y += core/subdev/timer/base.o
nouveau-y += core/subdev/timer/nv04.o
nouveau-y += core/subdev/vm/base.o
......@@ -132,6 +150,7 @@ nouveau-y += core/engine/copy/nvc0.o
nouveau-y += core/engine/copy/nve0.o
nouveau-y += core/engine/crypt/nv84.o
nouveau-y += core/engine/crypt/nv98.o
nouveau-y += core/engine/disp/base.o
nouveau-y += core/engine/disp/nv04.o
nouveau-y += core/engine/disp/nv50.o
nouveau-y += core/engine/disp/nv84.o
......@@ -141,11 +160,13 @@ nouveau-y += core/engine/disp/nva3.o
nouveau-y += core/engine/disp/nvd0.o
nouveau-y += core/engine/disp/nve0.o
nouveau-y += core/engine/disp/dacnv50.o
nouveau-y += core/engine/disp/dport.o
nouveau-y += core/engine/disp/hdanva3.o
nouveau-y += core/engine/disp/hdanvd0.o
nouveau-y += core/engine/disp/hdminv84.o
nouveau-y += core/engine/disp/hdminva3.o
nouveau-y += core/engine/disp/hdminvd0.o
nouveau-y += core/engine/disp/piornv50.o
nouveau-y += core/engine/disp/sornv50.o
nouveau-y += core/engine/disp/sornv94.o
nouveau-y += core/engine/disp/sornvd0.o
......@@ -194,7 +215,8 @@ nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o
nouveau-y += nouveau_irq.o nouveau_vga.o nouveau_agp.o
nouveau-y += nouveau_ttm.o nouveau_sgdma.o nouveau_bo.o nouveau_gem.o
nouveau-y += nouveau_prime.o nouveau_abi16.o
nouveau-y += nv04_fence.o nv10_fence.o nv50_fence.o nv84_fence.o nvc0_fence.o
nouveau-y += nv04_fence.o nv10_fence.o nv17_fence.o
nouveau-y += nv50_fence.o nv84_fence.o nvc0_fence.o
# drm/kms
nouveau-y += nouveau_bios.o nouveau_fbcon.o nouveau_display.o
......@@ -216,7 +238,9 @@ nouveau-y += nouveau_mem.o
# other random bits
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
ifdef CONFIG_X86
nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
endif
nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
......@@ -99,3 +99,13 @@ nouveau_client_fini(struct nouveau_client *client, bool suspend)
nv_debug(client, "%s completed with %d\n", name[suspend], ret);
return ret;
}
const char *
nouveau_client_name(void *obj)
{
const char *client_name = "unknown";
struct nouveau_client *client = nouveau_client(obj);
if (client)
client_name = client->name;
return client_name;
}
......@@ -40,14 +40,15 @@ nouveau_enum_find(const struct nouveau_enum *en, u32 value)
return NULL;
}
void
const struct nouveau_enum *
nouveau_enum_print(const struct nouveau_enum *en, u32 value)
{
en = nouveau_enum_find(en, value);
if (en)
printk("%s", en->name);
pr_cont("%s", en->name);
else
printk("(unknown enum 0x%08x)", value);
pr_cont("(unknown enum 0x%08x)", value);
return en;
}
void
......@@ -55,7 +56,7 @@ nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value)
{
while (bf->name) {
if (value & bf->mask) {
printk(" %s", bf->name);
pr_cont(" %s", bf->name);
value &= ~bf->mask;
}
......@@ -63,5 +64,5 @@ nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value)
}
if (value)
printk(" (unknown bits 0x%08x)", value);
pr_cont(" (unknown bits 0x%08x)", value);
}
/*
* 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.
*/
#include <core/os.h>
#include <core/event.h>
static void
nouveau_event_put_locked(struct nouveau_event *event, int index,
struct nouveau_eventh *handler)
{
if (!--event->index[index].refs)
event->disable(event, index);
list_del(&handler->head);
}
void
nouveau_event_put(struct nouveau_event *event, int index,
struct nouveau_eventh *handler)
{
unsigned long flags;
spin_lock_irqsave(&event->lock, flags);
if (index < event->index_nr)
nouveau_event_put_locked(event, index, handler);
spin_unlock_irqrestore(&event->lock, flags);
}
void
nouveau_event_get(struct nouveau_event *event, int index,
struct nouveau_eventh *handler)
{
unsigned long flags;
spin_lock_irqsave(&event->lock, flags);
if (index < event->index_nr) {
list_add(&handler->head, &event->index[index].list);
if (!event->index[index].refs++)
event->enable(event, index);
}
spin_unlock_irqrestore(&event->lock, flags);
}
void
nouveau_event_trigger(struct nouveau_event *event, int index)
{
struct nouveau_eventh *handler, *temp;
unsigned long flags;
if (index >= event->index_nr)
return;
spin_lock_irqsave(&event->lock, flags);
list_for_each_entry_safe(handler, temp, &event->index[index].list, head) {
if (handler->func(handler, index) == NVKM_EVENT_DROP) {
nouveau_event_put_locked(event, index, handler);
}
}
spin_unlock_irqrestore(&event->lock, flags);
}
void
nouveau_event_destroy(struct nouveau_event **pevent)
{
struct nouveau_event *event = *pevent;
if (event) {
kfree(event);
*pevent = NULL;
}
}
int
nouveau_event_create(int index_nr, struct nouveau_event **pevent)
{
struct nouveau_event *event;
int i;
event = *pevent = kzalloc(sizeof(*event) + index_nr *
sizeof(event->index[0]), GFP_KERNEL);
if (!event)
return -ENOMEM;
spin_lock_init(&event->lock);
for (i = 0; i < index_nr; i++)
INIT_LIST_HEAD(&event->index[i].list);
event->index_nr = index_nr;
return 0;
}
......@@ -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;
}
......
......@@ -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);
......
......@@ -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;
}
......
/*
* 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);
}
/*
* 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;
}
#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
......@@ -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;
}
......
......@@ -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
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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);
}
......@@ -636,20 +650,19 @@ 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)
u32 ctrl, 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 data, conf = ~0;
data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info1);
data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1);
if (data == 0x0000)
return false;
return conf;
switch (dcb.type) {
switch (dcb->type) {
case DCB_OUTPUT_TMDS:
conf = (ctrl & 0x00000f00) >> 8;
if (pclk >= 165000)
......@@ -668,25 +681,23 @@ 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
......@@ -752,6 +763,7 @@ 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)
{
struct dcb_output outp;
u32 pclk;
int i;
......@@ -771,10 +783,29 @@ nvd0_display_unk2_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);
for (i = 0; mask && i < 8; i++) {
u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20)), cfg;
u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20));
if (mcp & (1 << head)) {
if ((cfg = exec_clkcmp(priv, head, i, mcp, 0, pclk))) {
u32 cfg = exec_clkcmp(priv, head, i, mcp, 0xff, pclk, &outp);
if (cfg != ~0) {
u32 addr, mask, data = 0x00000000;
if (outp.type == DCB_OUTPUT_DP) {
switch ((mcp & 0x000f0000) >> 16) {
case 6: pclk = pclk * 30 / 8; break;
case 5: pclk = pclk * 24 / 8; break;
case 2:
default:
pclk = pclk * 18 / 8;
break;
}
nouveau_dp_train(&priv->base,
priv->sor.dp,
&outp, head, pclk);
}
exec_clkcmp(priv, head, i, mcp, 0, pclk, &outp);
if (i < 4) {
addr = 0x612280 + ((i - 0) * 0x800);
mask = 0xffffffff;
......@@ -807,6 +838,7 @@ nvd0_display_unk2_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
static void
nvd0_display_unk4_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
{
struct dcb_output outp;
int pclk, i;
pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
......@@ -814,7 +846,7 @@ nvd0_display_unk4_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
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);
exec_clkcmp(priv, head, i, mcp, 1, pclk, &outp);
}
nv_wr32(priv, 0x6101d4, 0x00000000);
......@@ -822,33 +854,24 @@ nvd0_display_unk4_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
nv_wr32(priv, 0x6101d0, 0x80000000);
}
static void
nvd0_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
void
nvd0_disp_intr_supervisor(struct work_struct *work)
{
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);
}
spin_unlock_irqrestore(&disp->vblank.lock, flags);
struct nv50_disp_priv *priv =
container_of(work, struct nv50_disp_priv, supervisor);
u32 mask = 0, head = ~0;
if (disp->vblank.notify)
disp->vblank.notify(disp->vblank.data, crtc);
while (!mask && ++head < priv->head.nr)
mask = nv_rd32(priv, 0x6101d4 + (head * 0x800));
nv_debug(priv, "supervisor %08x %08x %d\n", priv->super, mask, head);
if (priv->super & 0x00000001)
nvd0_display_unk1_handler(priv, head, mask);
if (priv->super & 0x00000002)
nvd0_display_unk2_handler(priv, head, mask);
if (priv->super & 0x00000004)
nvd0_display_unk4_handler(priv, head, mask);
}
void
......@@ -884,27 +907,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 +927,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 +940,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 +952,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 +962,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;
}
......
......@@ -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;
}
......
/*
* 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;
}
......@@ -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);
}
......
......@@ -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,
};
......@@ -32,6 +32,18 @@
#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)
{
......@@ -39,53 +51,31 @@ nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
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,
};
......@@ -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;
......
......@@ -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 @@ nv04_fifo_swmthd(struct nv04_fifo_priv *priv, u32 chid, u32 addr, u32 data)
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) {
......
......@@ -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);
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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);
......
#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);
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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;
}
......@@ -135,6 +131,29 @@ nv50_software_sclass[] = {
* software context
******************************************************************************/
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,
......@@ -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;
......
......@@ -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;
}
......@@ -117,6 +114,23 @@ nvc0_software_sclass[] = {
* software context
******************************************************************************/
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,
......@@ -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;
}
......
......@@ -154,6 +154,14 @@ struct nve0_channel_ind_class {
u32 engine;
};
/* 0046: NV04_DISP
*/
#define NV04_DISP_CLASS 0x00000046
struct nv04_display_class {
};
/* 5070: NV50_DISP
* 8270: NV84_DISP
* 8370: NVA0_DISP
......@@ -190,25 +198,6 @@ struct nve0_channel_ind_class {
#define NV84_DISP_SOR_HDMI_PWR_REKEY 0x0000007f
#define NV50_DISP_SOR_LVDS_SCRIPT 0x00013000
#define NV50_DISP_SOR_LVDS_SCRIPT_ID 0x0000ffff
#define NV94_DISP_SOR_DP_TRAIN 0x00016000
#define NV94_DISP_SOR_DP_TRAIN_OP 0xf0000000
#define NV94_DISP_SOR_DP_TRAIN_OP_PATTERN 0x00000000
#define NV94_DISP_SOR_DP_TRAIN_OP_INIT 0x10000000
#define NV94_DISP_SOR_DP_TRAIN_OP_FINI 0x20000000
#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD 0x00000001
#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF 0x00000000
#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON 0x00000001
#define NV94_DISP_SOR_DP_TRAIN_PATTERN 0x00000003
#define NV94_DISP_SOR_DP_TRAIN_PATTERN_DISABLED 0x00000000
#define NV94_DISP_SOR_DP_LNKCTL 0x00016040
#define NV94_DISP_SOR_DP_LNKCTL_FRAME 0x80000000
#define NV94_DISP_SOR_DP_LNKCTL_FRAME_STD 0x00000000
#define NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH 0x80000000
#define NV94_DISP_SOR_DP_LNKCTL_WIDTH 0x00001f00
#define NV94_DISP_SOR_DP_LNKCTL_COUNT 0x00000007
#define NV94_DISP_SOR_DP_DRVCTL(l) ((l) * 0x40 + 0x00016100)
#define NV94_DISP_SOR_DP_DRVCTL_VS 0x00000300
#define NV94_DISP_SOR_DP_DRVCTL_PE 0x00000003
#define NV50_DISP_DAC_MTHD 0x00020000
#define NV50_DISP_DAC_MTHD_TYPE 0x0000f000
......@@ -230,6 +219,23 @@ struct nve0_channel_ind_class {
#define NV50_DISP_DAC_LOAD 0x0002000c
#define NV50_DISP_DAC_LOAD_VALUE 0x00000007
#define NV50_DISP_PIOR_MTHD 0x00030000
#define NV50_DISP_PIOR_MTHD_TYPE 0x0000f000
#define NV50_DISP_PIOR_MTHD_OR 0x00000003
#define NV50_DISP_PIOR_PWR 0x00030000
#define NV50_DISP_PIOR_PWR_STATE 0x00000001
#define NV50_DISP_PIOR_PWR_STATE_ON 0x00000001
#define NV50_DISP_PIOR_PWR_STATE_OFF 0x00000000
#define NV50_DISP_PIOR_TMDS_PWR 0x00032000
#define NV50_DISP_PIOR_TMDS_PWR_STATE 0x00000001
#define NV50_DISP_PIOR_TMDS_PWR_STATE_ON 0x00000001
#define NV50_DISP_PIOR_TMDS_PWR_STATE_OFF 0x00000000
#define NV50_DISP_PIOR_DP_PWR 0x00036000
#define NV50_DISP_PIOR_DP_PWR_STATE 0x00000001
#define NV50_DISP_PIOR_DP_PWR_STATE_ON 0x00000001
#define NV50_DISP_PIOR_DP_PWR_STATE_OFF 0x00000000
struct nv50_display_class {
};
......
......@@ -7,7 +7,7 @@ struct nouveau_client {
struct nouveau_namedb base;
struct nouveau_handle *root;
struct nouveau_object *device;
char name[16];
char name[32];
u32 debug;
struct nouveau_vm *vm;
};
......@@ -41,5 +41,6 @@ int nouveau_client_create_(const char *name, u64 device, const char *cfg,
int nouveau_client_init(struct nouveau_client *);
int nouveau_client_fini(struct nouveau_client *, bool suspend);
const char *nouveau_client_name(void *obj);
#endif
......@@ -26,6 +26,7 @@ enum nv_subdev_type {
*/
NVDEV_SUBDEV_MXM,
NVDEV_SUBDEV_MC,
NVDEV_SUBDEV_BUS,
NVDEV_SUBDEV_TIMER,
NVDEV_SUBDEV_FB,
NVDEV_SUBDEV_LTCG,
......
......@@ -5,12 +5,13 @@ struct nouveau_enum {
u32 value;
const char *name;
const void *data;
u32 data2;
};
const struct nouveau_enum *
nouveau_enum_find(const struct nouveau_enum *, u32 value);
void
const struct nouveau_enum *
nouveau_enum_print(const struct nouveau_enum *en, u32 value);
struct nouveau_bitfield {
......
#ifndef __NVKM_EVENT_H__
#define __NVKM_EVENT_H__
/* return codes from event handlers */
#define NVKM_EVENT_DROP 0
#define NVKM_EVENT_KEEP 1
struct nouveau_eventh {
struct list_head head;
int (*func)(struct nouveau_eventh *, int index);
};
struct nouveau_event {
spinlock_t lock;
void *priv;
void (*enable)(struct nouveau_event *, int index);
void (*disable)(struct nouveau_event *, int index);
int index_nr;
struct {
struct list_head list;
int refs;
} index[];
};
int nouveau_event_create(int index_nr, struct nouveau_event **);
void nouveau_event_destroy(struct nouveau_event **);
void nouveau_event_trigger(struct nouveau_event *, int index);
void nouveau_event_get(struct nouveau_event *, int index,
struct nouveau_eventh *);
void nouveau_event_put(struct nouveau_event *, int index,
struct nouveau_eventh *);
#endif
......@@ -133,7 +133,7 @@ static inline u8
nv_ro08(void *obj, u64 addr)
{
u8 data = nv_ofuncs(obj)->rd08(obj, addr);
nv_spam(obj, "nv_ro08 0x%08x 0x%02x\n", addr, data);
nv_spam(obj, "nv_ro08 0x%08llx 0x%02x\n", addr, data);
return data;
}
......@@ -141,7 +141,7 @@ static inline u16
nv_ro16(void *obj, u64 addr)
{
u16 data = nv_ofuncs(obj)->rd16(obj, addr);
nv_spam(obj, "nv_ro16 0x%08x 0x%04x\n", addr, data);
nv_spam(obj, "nv_ro16 0x%08llx 0x%04x\n", addr, data);
return data;
}
......@@ -149,28 +149,28 @@ static inline u32
nv_ro32(void *obj, u64 addr)
{
u32 data = nv_ofuncs(obj)->rd32(obj, addr);
nv_spam(obj, "nv_ro32 0x%08x 0x%08x\n", addr, data);
nv_spam(obj, "nv_ro32 0x%08llx 0x%08x\n", addr, data);
return data;
}
static inline void
nv_wo08(void *obj, u64 addr, u8 data)
{
nv_spam(obj, "nv_wo08 0x%08x 0x%02x\n", addr, data);
nv_spam(obj, "nv_wo08 0x%08llx 0x%02x\n", addr, data);
nv_ofuncs(obj)->wr08(obj, addr, data);
}
static inline void
nv_wo16(void *obj, u64 addr, u16 data)
{
nv_spam(obj, "nv_wo16 0x%08x 0x%04x\n", addr, data);
nv_spam(obj, "nv_wo16 0x%08llx 0x%04x\n", addr, data);
nv_ofuncs(obj)->wr16(obj, addr, data);
}
static inline void
nv_wo32(void *obj, u64 addr, u32 data)
{
nv_spam(obj, "nv_wo32 0x%08x 0x%08x\n", addr, data);
nv_spam(obj, "nv_wo32 0x%08llx 0x%08x\n", addr, data);
nv_ofuncs(obj)->wr32(obj, addr, data);
}
......
......@@ -15,7 +15,8 @@ struct nouveau_object;
#define NV_PRINTK_TRACE KERN_DEBUG
#define NV_PRINTK_SPAM KERN_DEBUG
void nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
void __printf(4, 5)
nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
#define nv_printk(o,l,f,a...) do { \
if (NV_DBG_##l <= CONFIG_NOUVEAU_DEBUG) \
......
......@@ -4,18 +4,11 @@
#include <core/object.h>
#include <core/engine.h>
#include <core/device.h>
#include <core/event.h>
struct nouveau_disp {
struct nouveau_engine base;
struct {
struct list_head list;
spinlock_t lock;
void (*notify)(void *, int);
void (*get)(void *, int);
void (*put)(void *, int);
void *data;
} vblank;
struct nouveau_event *vblank;
};
static inline struct nouveau_disp *
......@@ -24,16 +17,22 @@ nouveau_disp(void *obj)
return (void *)nv_device(obj)->subdev[NVDEV_ENGINE_DISP];
}
#define nouveau_disp_create(p,e,c,i,x,d) \
nouveau_engine_create((p), (e), (c), true, (i), (x), (d))
#define nouveau_disp_destroy(d) \
nouveau_engine_destroy(&(d)->base)
#define nouveau_disp_create(p,e,c,h,i,x,d) \
nouveau_disp_create_((p), (e), (c), (h), (i), (x), \
sizeof(**d), (void **)d)
#define nouveau_disp_destroy(d) ({ \
struct nouveau_disp *disp = (d); \
_nouveau_disp_dtor(nv_object(disp)); \
})
#define nouveau_disp_init(d) \
nouveau_engine_init(&(d)->base)
#define nouveau_disp_fini(d,s) \
nouveau_engine_fini(&(d)->base, (s))
#define _nouveau_disp_dtor _nouveau_engine_dtor
int nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, int heads,
const char *, const char *, int, void **);
void _nouveau_disp_dtor(struct nouveau_object *);
#define _nouveau_disp_init _nouveau_engine_init
#define _nouveau_disp_fini _nouveau_engine_fini
......
......@@ -65,6 +65,8 @@ struct nouveau_fifo_base {
struct nouveau_fifo {
struct nouveau_engine base;
struct nouveau_event *uevent;
struct nouveau_object **channel;
spinlock_t lock;
u16 min;
......@@ -92,6 +94,8 @@ int nouveau_fifo_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, int min, int max,
int size, void **);
void nouveau_fifo_destroy(struct nouveau_fifo *);
const char *
nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid);
#define _nouveau_fifo_init _nouveau_engine_init
#define _nouveau_fifo_fini _nouveau_engine_fini
......
......@@ -3,17 +3,17 @@
#include <core/engine.h>
#include <core/engctx.h>
#include <core/event.h>
struct nouveau_software_chan {
struct nouveau_engctx base;
struct {
struct list_head head;
struct nouveau_eventh event;
u32 channel;
u32 ctxdma;
u64 offset;
u32 value;
u32 crtc;
} vblank;
int (*flip)(void *);
......
......@@ -16,6 +16,8 @@ enum dcb_output_type {
struct dcb_output {
int index; /* may not be raw dcb index if merging has happened */
u16 hasht;
u16 hashm;
enum dcb_output_type type;
uint8_t i2c_index;
uint8_t heads;
......@@ -25,6 +27,7 @@ struct dcb_output {
uint8_t or;
uint8_t link;
bool duallink_possible;
uint8_t extdev;
union {
struct sor_conf {
int link;
......
#ifndef __NVBIOS_GPIO_H__
#define __NVBIOS_GPIO_H__
struct nouveau_bios;
enum dcb_gpio_func_name {
DCB_GPIO_PANEL_POWER = 0x01,
DCB_GPIO_TVDAC0 = 0x0c,
DCB_GPIO_TVDAC1 = 0x2d,
DCB_GPIO_PWM_FAN = 0x09,
DCB_GPIO_FAN = 0x09,
DCB_GPIO_FAN_SENSE = 0x3d,
DCB_GPIO_UNUSED = 0xff
};
#define DCB_GPIO_LOG_DIR 0x02
#define DCB_GPIO_LOG_DIR_OUT 0x00
#define DCB_GPIO_LOG_DIR_IN 0x02
#define DCB_GPIO_LOG_VAL 0x01
#define DCB_GPIO_LOG_VAL_LO 0x00
#define DCB_GPIO_LOG_VAL_HI 0x01
struct dcb_gpio_func {
u8 func;
u8 line;
......
......@@ -15,7 +15,7 @@ struct dcb_i2c_entry {
enum dcb_i2c_type type;
u8 drive;
u8 sense;
u32 data;
u8 share;
};
u16 dcb_i2c_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
......
......@@ -23,11 +23,27 @@ struct nvbios_therm_sensor {
struct nvbios_therm_threshold thrs_shutdown;
};
/* no vbios have more than 6 */
#define NOUVEAU_TEMP_FAN_TRIP_MAX 10
struct nouveau_therm_trip_point {
int fan_duty;
int temp;
int hysteresis;
};
struct nvbios_therm_fan {
u16 pwm_freq;
u8 min_duty;
u8 max_duty;
u16 bump_period;
u16 slow_down_period;
struct nouveau_therm_trip_point trip[NOUVEAU_TEMP_FAN_TRIP_MAX];
u8 nr_fan_trip;
u8 linear_min_temp;
u8 linear_max_temp;
};
enum nvbios_therm_domain {
......
#ifndef __NVBIOS_XPIO_H__
#define __NVBIOS_XPIO_H__
#define NVBIOS_XPIO_FLAG_AUX 0x10
#define NVBIOS_XPIO_FLAG_AUX0 0x00
#define NVBIOS_XPIO_FLAG_AUX1 0x10
struct nvbios_xpio {
u8 type;
u8 addr;
u8 flags;
};
u16 dcb_xpio_table(struct nouveau_bios *, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
u16 dcb_xpio_parse(struct nouveau_bios *, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_xpio *);
#endif
#ifndef __NOUVEAU_BUS_H__
#define __NOUVEAU_BUS_H__
#include <core/subdev.h>
#include <core/device.h>
struct nouveau_bus_intr {
u32 stat;
u32 unit;
};
struct nouveau_bus {
struct nouveau_subdev base;
};
static inline struct nouveau_bus *
nouveau_bus(void *obj)
{
return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_BUS];
}
#define nouveau_bus_create(p, e, o, d) \
nouveau_subdev_create_((p), (e), (o), 0, "PBUS", "master", \
sizeof(**d), (void **)d)
#define nouveau_bus_destroy(p) \
nouveau_subdev_destroy(&(p)->base)
#define nouveau_bus_init(p) \
nouveau_subdev_init(&(p)->base)
#define nouveau_bus_fini(p, s) \
nouveau_subdev_fini(&(p)->base, (s))
#define _nouveau_bus_dtor _nouveau_subdev_dtor
#define _nouveau_bus_init _nouveau_subdev_init
#define _nouveau_bus_fini _nouveau_subdev_fini
extern struct nouveau_oclass nv04_bus_oclass;
extern struct nouveau_oclass nv31_bus_oclass;
extern struct nouveau_oclass nv50_bus_oclass;
extern struct nouveau_oclass nvc0_bus_oclass;
#endif
......@@ -3,6 +3,7 @@
#include <core/subdev.h>
#include <core/device.h>
#include <core/event.h>
#include <subdev/bios.h>
#include <subdev/bios/gpio.h>
......@@ -10,28 +11,18 @@
struct nouveau_gpio {
struct nouveau_subdev base;
struct nouveau_event *events;
/* hardware interfaces */
void (*reset)(struct nouveau_gpio *, u8 func);
int (*drive)(struct nouveau_gpio *, int line, int dir, int out);
int (*sense)(struct nouveau_gpio *, int line);
void (*irq_enable)(struct nouveau_gpio *, int line, bool);
/* software interfaces */
int (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
struct dcb_gpio_func *);
int (*set)(struct nouveau_gpio *, int idx, u8 tag, u8 line, int state);
int (*get)(struct nouveau_gpio *, int idx, u8 tag, u8 line);
int (*irq)(struct nouveau_gpio *, int idx, u8 tag, u8 line, bool on);
/* interrupt handling */
struct list_head isr;
spinlock_t lock;
void (*isr_run)(struct nouveau_gpio *, int idx, u32 mask);
int (*isr_add)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
void (*)(void *, int state), void *data);
void (*isr_del)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
void (*)(void *, int state), void *data);
};
static inline struct nouveau_gpio *
......@@ -40,25 +31,23 @@ nouveau_gpio(void *obj)
return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_GPIO];
}
#define nouveau_gpio_create(p,e,o,d) \
nouveau_gpio_create_((p), (e), (o), sizeof(**d), (void **)d)
#define nouveau_gpio_destroy(p) \
nouveau_subdev_destroy(&(p)->base)
#define nouveau_gpio_create(p,e,o,l,d) \
nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d)
#define nouveau_gpio_destroy(p) ({ \
struct nouveau_gpio *gpio = (p); \
_nouveau_gpio_dtor(nv_object(gpio)); \
})
#define nouveau_gpio_fini(p,s) \
nouveau_subdev_fini(&(p)->base, (s))
int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, int, void **);
int nouveau_gpio_init(struct nouveau_gpio *);
int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, int, int, void **);
void _nouveau_gpio_dtor(struct nouveau_object *);
int nouveau_gpio_init(struct nouveau_gpio *);
extern struct nouveau_oclass nv10_gpio_oclass;
extern struct nouveau_oclass nv50_gpio_oclass;
extern struct nouveau_oclass nvd0_gpio_oclass;
void nv50_gpio_dtor(struct nouveau_object *);
int nv50_gpio_init(struct nouveau_object *);
int nv50_gpio_fini(struct nouveau_object *, bool);
void nv50_gpio_intr(struct nouveau_subdev *);
void nv50_gpio_irq_enable(struct nouveau_gpio *, int line, bool);
extern struct nouveau_oclass nve0_gpio_oclass;
#endif
......@@ -10,23 +10,59 @@
#define NV_I2C_PORT(n) (0x00 + (n))
#define NV_I2C_DEFAULT(n) (0x80 + (n))
#define NV_I2C_TYPE_DCBI2C(n) (0x0000 | (n))
#define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8)
#define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8)
struct nouveau_i2c_port {
struct nouveau_object base;
struct i2c_adapter adapter;
struct nouveau_i2c *i2c;
struct i2c_algo_bit_data bit;
struct list_head head;
u8 index;
u8 type;
u32 dcb;
u32 drive;
u32 sense;
u32 state;
const struct nouveau_i2c_func *func;
};
struct nouveau_i2c_func {
void (*acquire)(struct nouveau_i2c_port *);
void (*release)(struct nouveau_i2c_port *);
void (*drive_scl)(struct nouveau_i2c_port *, int);
void (*drive_sda)(struct nouveau_i2c_port *, int);
int (*sense_scl)(struct nouveau_i2c_port *);
int (*sense_sda)(struct nouveau_i2c_port *);
int (*aux)(struct nouveau_i2c_port *, u8, u32, u8 *, u8);
int (*pattern)(struct nouveau_i2c_port *, int pattern);
int (*lnk_ctl)(struct nouveau_i2c_port *, int nr, int bw, bool enh);
int (*drv_ctl)(struct nouveau_i2c_port *, int lane, int sw, int pe);
};
#define nouveau_i2c_port_create(p,e,o,i,a,d) \
nouveau_i2c_port_create_((p), (e), (o), (i), (a), \
sizeof(**d), (void **)d)
#define nouveau_i2c_port_destroy(p) ({ \
struct nouveau_i2c_port *port = (p); \
_nouveau_i2c_port_dtor(nv_object(i2c)); \
})
#define nouveau_i2c_port_init(p) \
nouveau_object_init(&(p)->base)
#define nouveau_i2c_port_fini(p,s) \
nouveau_object_fini(&(p)->base, (s))
int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, u8,
const struct i2c_algorithm *, int, void **);
void _nouveau_i2c_port_dtor(struct nouveau_object *);
#define _nouveau_i2c_port_init nouveau_object_init
#define _nouveau_i2c_port_fini nouveau_object_fini
struct nouveau_i2c {
struct nouveau_subdev base;
struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
int (*identify)(struct nouveau_i2c *, int index,
const char *what, struct i2c_board_info *,
bool (*match)(struct nouveau_i2c_port *,
......@@ -40,21 +76,76 @@ nouveau_i2c(void *obj)
return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_I2C];
}
extern struct nouveau_oclass nouveau_i2c_oclass;
#define nouveau_i2c_create(p,e,o,s,d) \
nouveau_i2c_create_((p), (e), (o), (s), sizeof(**d), (void **)d)
#define nouveau_i2c_destroy(p) ({ \
struct nouveau_i2c *i2c = (p); \
_nouveau_i2c_dtor(nv_object(i2c)); \
})
#define nouveau_i2c_init(p) ({ \
struct nouveau_i2c *i2c = (p); \
_nouveau_i2c_init(nv_object(i2c)); \
})
#define nouveau_i2c_fini(p,s) ({ \
struct nouveau_i2c *i2c = (p); \
_nouveau_i2c_fini(nv_object(i2c), (s)); \
})
void nouveau_i2c_drive_scl(void *, int);
void nouveau_i2c_drive_sda(void *, int);
int nouveau_i2c_sense_scl(void *);
int nouveau_i2c_sense_sda(void *);
int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, struct nouveau_oclass *,
int, void **);
void _nouveau_i2c_dtor(struct nouveau_object *);
int _nouveau_i2c_init(struct nouveau_object *);
int _nouveau_i2c_fini(struct nouveau_object *, bool);
int nv_rdi2cr(struct nouveau_i2c_port *, u8 addr, u8 reg);
int nv_wri2cr(struct nouveau_i2c_port *, u8 addr, u8 reg, u8 val);
bool nv_probe_i2c(struct nouveau_i2c_port *, u8 addr);
int nv_rdaux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
int nv_wraux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
extern struct nouveau_oclass nv04_i2c_oclass;
extern struct nouveau_oclass nv4e_i2c_oclass;
extern struct nouveau_oclass nv50_i2c_oclass;
extern struct nouveau_oclass nv94_i2c_oclass;
extern struct nouveau_oclass nvd0_i2c_oclass;
extern struct nouveau_oclass nouveau_anx9805_sclass[];
extern const struct i2c_algorithm nouveau_i2c_bit_algo;
extern const struct i2c_algorithm nouveau_i2c_aux_algo;
static inline int
nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg)
{
u8 val;
struct i2c_msg msgs[] = {
{ .addr = addr, .flags = 0, .len = 1, .buf = &reg },
{ .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = &val },
};
int ret = i2c_transfer(&port->adapter, msgs, 2);
if (ret != 2)
return -EIO;
return val;
}
static inline int
nv_wri2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg, u8 val)
{
u8 buf[2] = { reg, val };
struct i2c_msg msgs[] = {
{ .addr = addr, .flags = 0, .len = 2, .buf = buf },
};
int ret = i2c_transfer(&port->adapter, msgs, 1);
if (ret != 1)
return -EIO;
return 0;
}
static inline bool
nv_probe_i2c(struct nouveau_i2c_port *port, u8 addr)
{
return nv_rdi2cr(port, addr, 0) >= 0;
}
int nv_rdaux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
int nv_wraux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
#endif
......@@ -4,10 +4,10 @@
#include <core/device.h>
#include <core/subdev.h>
enum nouveau_therm_fan_mode {
FAN_CONTROL_NONE = 0,
FAN_CONTROL_MANUAL = 1,
FAN_CONTROL_NR,
enum nouveau_therm_mode {
NOUVEAU_THERM_CTRL_NONE = 0,
NOUVEAU_THERM_CTRL_MANUAL = 1,
NOUVEAU_THERM_CTRL_AUTO = 2,
};
enum nouveau_therm_attr_type {
......@@ -28,6 +28,11 @@ enum nouveau_therm_attr_type {
struct nouveau_therm {
struct nouveau_subdev base;
int (*pwm_ctrl)(struct nouveau_therm *, int line, bool);
int (*pwm_get)(struct nouveau_therm *, int line, u32 *, u32 *);
int (*pwm_set)(struct nouveau_therm *, int line, u32, u32);
int (*pwm_clock)(struct nouveau_therm *);
int (*fan_get)(struct nouveau_therm *);
int (*fan_set)(struct nouveau_therm *, int);
int (*fan_sense)(struct nouveau_therm *);
......@@ -46,13 +51,29 @@ nouveau_therm(void *obj)
}
#define nouveau_therm_create(p,e,o,d) \
nouveau_subdev_create((p), (e), (o), 0, "THERM", "therm", d)
#define nouveau_therm_destroy(p) \
nouveau_subdev_destroy(&(p)->base)
nouveau_therm_create_((p), (e), (o), sizeof(**d), (void **)d)
#define nouveau_therm_destroy(p) ({ \
struct nouveau_therm *therm = (p); \
_nouveau_therm_dtor(nv_object(therm)); \
})
#define nouveau_therm_init(p) ({ \
struct nouveau_therm *therm = (p); \
_nouveau_therm_init(nv_object(therm)); \
})
#define nouveau_therm_fini(p,s) ({ \
struct nouveau_therm *therm = (p); \
_nouveau_therm_init(nv_object(therm), (s)); \
})
#define _nouveau_therm_dtor _nouveau_subdev_dtor
int nouveau_therm_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, int, void **);
void _nouveau_therm_dtor(struct nouveau_object *);
int _nouveau_therm_init(struct nouveau_object *);
int _nouveau_therm_fini(struct nouveau_object *, bool);
extern struct nouveau_oclass nv40_therm_oclass;
extern struct nouveau_oclass nv50_therm_oclass;
extern struct nouveau_oclass nva3_therm_oclass;
extern struct nouveau_oclass nvd0_therm_oclass;
#endif
......@@ -10,6 +10,14 @@ struct nouveau_alarm {
void (*func)(struct nouveau_alarm *);
};
static inline void
nouveau_alarm_init(struct nouveau_alarm *alarm,
void (*func)(struct nouveau_alarm *))
{
INIT_LIST_HEAD(&alarm->head);
alarm->func = func;
}
bool nouveau_timer_wait_eq(void *, u64 nsec, u32 addr, u32 mask, u32 data);
bool nouveau_timer_wait_ne(void *, u64 nsec, u32 addr, u32 mask, u32 data);
bool nouveau_timer_wait_cb(void *, u64 nsec, bool (*func)(void *), void *data);
......
......@@ -16,6 +16,7 @@
#include <linux/vmalloc.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/reboot.h>
#include <asm/unaligned.h>
......
......@@ -172,7 +172,7 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios)
nv_wr32(bios, pcireg, access);
}
#if defined(CONFIG_ACPI)
#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len);
bool nouveau_acpi_rom_supported(struct pci_dev *pdev);
#else
......
......@@ -107,6 +107,18 @@ dcb_outp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
return 0x0000;
}
static inline u16
dcb_outp_hasht(struct dcb_output *outp)
{
return (outp->extdev << 8) | (outp->location << 4) | outp->type;
}
static inline u16
dcb_outp_hashm(struct dcb_output *outp)
{
return (outp->heads << 8) | (outp->link << 6) | outp->or;
}
u16
dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
struct dcb_output *outp)
......@@ -135,34 +147,28 @@ dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
case DCB_OUTPUT_DP:
outp->link = (conf & 0x00000030) >> 4;
outp->sorconf.link = outp->link; /*XXX*/
outp->extdev = 0x00;
if (outp->location != 0)
outp->extdev = (conf & 0x0000ff00) >> 8;
break;
default:
break;
}
}
outp->hasht = dcb_outp_hasht(outp);
outp->hashm = dcb_outp_hashm(outp);
}
return dcb;
}
static inline u16
dcb_outp_hasht(struct dcb_output *outp)
{
return outp->type;
}
static inline u16
dcb_outp_hashm(struct dcb_output *outp)
{
return (outp->heads << 8) | (outp->link << 6) | outp->or;
}
u16
dcb_outp_match(struct nouveau_bios *bios, u16 type, u16 mask,
u8 *ver, u8 *len, struct dcb_output *outp)
{
u16 dcb, idx = 0;
while ((dcb = dcb_outp_parse(bios, idx++, ver, len, outp))) {
if (dcb_outp_hasht(outp) == type) {
if ((dcb_outp_hasht(outp) & 0x00ff) == (type & 0x00ff)) {
if ((dcb_outp_hashm(outp) & mask) == mask)
break;
}
......
......@@ -48,7 +48,7 @@ extdev_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
return extdev + *hdr;
}
u16
static u16
nvbios_extdev_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
......
......@@ -25,6 +25,7 @@
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
#include <subdev/bios/gpio.h>
#include <subdev/bios/xpio.h>
u16
dcb_gpio_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
......@@ -60,8 +61,14 @@ dcb_gpio_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
u16
dcb_gpio_entry(struct nouveau_bios *bios, int idx, int ent, u8 *ver, u8 *len)
{
u8 hdr, cnt;
u16 gpio = !idx ? dcb_gpio_table(bios, ver, &hdr, &cnt, len) : 0x0000;
u8 hdr, cnt, xver; /* use gpio version for xpio entry parsing */
u16 gpio;
if (!idx--)
gpio = dcb_gpio_table(bios, ver, &hdr, &cnt, len);
else
gpio = dcb_xpio_table(bios, idx, &xver, &hdr, &cnt, len);
if (gpio && ent < cnt)
return gpio + hdr + (ent * *len);
return 0x0000;
......
......@@ -70,12 +70,12 @@ dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info)
u8 ver, len;
u16 ent = dcb_i2c_entry(bios, idx, &ver, &len);
if (ent) {
info->data = nv_ro32(bios, ent + 0);
info->type = nv_ro08(bios, ent + 3);
info->type = nv_ro08(bios, ent + 3);
info->share = DCB_I2C_UNUSED;
if (ver < 0x30) {
info->type &= 0x07;
if (info->type == 0x07)
info->type = 0xff;
info->type = DCB_I2C_UNUSED;
}
switch (info->type) {
......@@ -88,7 +88,11 @@ dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info)
return 0;
case DCB_I2C_NVIO_BIT:
case DCB_I2C_NVIO_AUX:
info->drive = nv_ro08(bios, ent + 0);
info->drive = nv_ro08(bios, ent + 0) & 0x0f;
if (nv_ro08(bios, ent + 1) & 0x01) {
info->share = nv_ro08(bios, ent + 1) >> 1;
info->share &= 0x0f;
}
return 0;
case DCB_I2C_UNUSED:
return 0;
......@@ -121,7 +125,8 @@ dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info)
if (!info->sense) info->sense = 0x36;
}
info->type = DCB_I2C_NV04_BIT;
info->type = DCB_I2C_NV04_BIT;
info->share = DCB_I2C_UNUSED;
return 0;
}
......
......@@ -231,6 +231,11 @@ init_i2c(struct nvbios_init *init, int index)
return NULL;
}
if (index == -2 && init->outp->location) {
index = NV_I2C_TYPE_EXTAUX(init->outp->extdev);
return i2c->find_type(i2c, index);
}
index = init->outp->i2c_index;
}
......@@ -258,7 +263,7 @@ init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val)
static int
init_rdauxr(struct nvbios_init *init, u32 addr)
{
struct nouveau_i2c_port *port = init_i2c(init, -1);
struct nouveau_i2c_port *port = init_i2c(init, -2);
u8 data;
if (port && init_exec(init)) {
......@@ -274,7 +279,7 @@ init_rdauxr(struct nvbios_init *init, u32 addr)
static int
init_wrauxr(struct nvbios_init *init, u32 addr, u8 data)
{
struct nouveau_i2c_port *port = init_i2c(init, -1);
struct nouveau_i2c_port *port = init_i2c(init, -2);
if (port && init_exec(init))
return nv_wraux(port, addr, &data, 1);
return -ENODEV;
......@@ -1816,7 +1821,7 @@ init_ram_restrict_zm_reg_group(struct nvbios_init *init)
u8 i, j;
trace("RAM_RESTRICT_ZM_REG_GROUP\t"
"R[%08x] 0x%02x 0x%02x\n", addr, incr, num);
"R[0x%08x] 0x%02x 0x%02x\n", addr, incr, num);
init->offset += 7;
for (i = 0; i < num; i++) {
......@@ -1849,7 +1854,7 @@ init_copy_zm_reg(struct nvbios_init *init)
u32 sreg = nv_ro32(bios, init->offset + 1);
u32 dreg = nv_ro32(bios, init->offset + 5);
trace("COPY_ZM_REG\tR[0x%06x] = R[0x%06x]\n", sreg, dreg);
trace("COPY_ZM_REG\tR[0x%06x] = R[0x%06x]\n", dreg, sreg);
init->offset += 9;
init_wr32(init, dreg, init_rd32(init, sreg));
......@@ -1866,7 +1871,7 @@ init_zm_reg_group(struct nvbios_init *init)
u32 addr = nv_ro32(bios, init->offset + 1);
u8 count = nv_ro08(bios, init->offset + 5);
trace("ZM_REG_GROUP\tR[0x%06x] =\n");
trace("ZM_REG_GROUP\tR[0x%06x] =\n", addr);
init->offset += 6;
while (count--) {
......
......@@ -55,7 +55,7 @@ therm_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
return therm + nv_ro08(bios, therm + 1);
}
u16
static u16
nvbios_therm_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
......@@ -155,10 +155,15 @@ int
nvbios_therm_fan_parse(struct nouveau_bios *bios,
struct nvbios_therm_fan *fan)
{
struct nouveau_therm_trip_point *cur_trip = NULL;
u8 ver, len, i;
u16 entry;
uint8_t duty_lut[] = { 0, 0, 25, 0, 40, 0, 50, 0,
75, 0, 85, 0, 100, 0, 100, 0 };
i = 0;
fan->nr_fan_trip = 0;
while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
s16 value = nv_ro16(bios, entry + 1);
......@@ -167,9 +172,30 @@ nvbios_therm_fan_parse(struct nouveau_bios *bios,
fan->min_duty = value & 0xff;
fan->max_duty = (value & 0xff00) >> 8;
break;
case 0x24:
fan->nr_fan_trip++;
cur_trip = &fan->trip[fan->nr_fan_trip - 1];
cur_trip->hysteresis = value & 0xf;
cur_trip->temp = (value & 0xff0) >> 4;
cur_trip->fan_duty = duty_lut[(value & 0xf000) >> 12];
break;
case 0x25:
cur_trip = &fan->trip[fan->nr_fan_trip - 1];
cur_trip->fan_duty = value;
break;
case 0x26:
fan->pwm_freq = value;
break;
case 0x3b:
fan->bump_period = value;
break;
case 0x3c:
fan->slow_down_period = value;
break;
case 0x46:
fan->linear_min_temp = nv_ro08(bios, entry + 1);
fan->linear_max_temp = nv_ro08(bios, entry + 2);
break;
}
}
......
/*
* 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 <subdev/bios.h>
#include <subdev/bios/gpio.h>
#include <subdev/bios/xpio.h>
static u16
dcb_xpiod_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u16 data = dcb_gpio_table(bios, ver, hdr, cnt, len);
if (data && *ver >= 0x40 && *hdr >= 0x06) {
u16 xpio = nv_ro16(bios, data + 0x04);
if (xpio) {
*ver = nv_ro08(bios, data + 0x00);
*hdr = nv_ro08(bios, data + 0x01);
*cnt = nv_ro08(bios, data + 0x02);
*len = nv_ro08(bios, data + 0x03);
return xpio;
}
}
return 0x0000;
}
u16
dcb_xpio_table(struct nouveau_bios *bios, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u16 data = dcb_xpiod_table(bios, ver, hdr, cnt, len);
if (data && idx < *cnt) {
u16 xpio = nv_ro16(bios, data + *hdr + (idx * *len));
if (xpio) {
*ver = nv_ro08(bios, data + 0x00);
*hdr = nv_ro08(bios, data + 0x01);
*cnt = nv_ro08(bios, data + 0x02);
*len = nv_ro08(bios, data + 0x03);
return xpio;
}
}
return 0x0000;
}
u16
dcb_xpio_parse(struct nouveau_bios *bios, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_xpio *info)
{
u16 data = dcb_xpio_table(bios, idx, ver, hdr, cnt, len);
if (data && *len >= 6) {
info->type = nv_ro08(bios, data + 0x04);
info->addr = nv_ro08(bios, data + 0x05);
info->flags = nv_ro08(bios, data + 0x06);
}
return 0x0000;
}
/*
* Copyright 2012 Nouveau Community
*
* 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: Martin Peres <martin.peres@labri.fr>
* Ben Skeggs
*/
#include <subdev/bus.h>
struct nv04_bus_priv {
struct nouveau_bus base;
};
static void
nv04_bus_intr(struct nouveau_subdev *subdev)
{
struct nouveau_bus *pbus = nouveau_bus(subdev);
u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
if (stat & 0x00000001) {
nv_error(pbus, "BUS ERROR\n");
stat &= ~0x00000001;
nv_wr32(pbus, 0x001100, 0x00000001);
}
if (stat & 0x00000110) {
subdev = nouveau_subdev(subdev, NVDEV_SUBDEV_GPIO);
if (subdev && subdev->intr)
subdev->intr(subdev);
stat &= ~0x00000110;
nv_wr32(pbus, 0x001100, 0x00000110);
}
if (stat) {
nv_error(pbus, "unknown intr 0x%08x\n", stat);
nv_mask(pbus, 0x001140, stat, 0x00000000);
}
}
static int
nv04_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nv04_bus_priv *priv;
int ret;
ret = nouveau_bus_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
nv_subdev(priv)->intr = nv04_bus_intr;
return 0;
}
static int
nv04_bus_init(struct nouveau_object *object)
{
struct nv04_bus_priv *priv = (void *)object;
nv_wr32(priv, 0x001100, 0xffffffff);
nv_wr32(priv, 0x001140, 0x00000111);
return nouveau_bus_init(&priv->base);
}
struct nouveau_oclass
nv04_bus_oclass = {
.handle = NV_SUBDEV(BUS, 0x04),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv04_bus_ctor,
.dtor = _nouveau_bus_dtor,
.init = nv04_bus_init,
.fini = _nouveau_bus_fini,
},
};
/*
* Copyright 2012 Nouveau Community
*
* 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: Martin Peres <martin.peres@labri.fr>
* Ben Skeggs
*/
#include <subdev/bus.h>
struct nv31_bus_priv {
struct nouveau_bus base;
};
static void
nv31_bus_intr(struct nouveau_subdev *subdev)
{
struct nouveau_bus *pbus = nouveau_bus(subdev);
u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
u32 gpio = nv_rd32(pbus, 0x001104) & nv_rd32(pbus, 0x001144);
if (gpio) {
subdev = nouveau_subdev(pbus, NVDEV_SUBDEV_GPIO);
if (subdev && subdev->intr)
subdev->intr(subdev);
}
if (stat & 0x00000008) { /* NV41- */
u32 addr = nv_rd32(pbus, 0x009084);
u32 data = nv_rd32(pbus, 0x009088);
nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x\n",
(addr & 0x00000002) ? "write" : "read", data,
(addr & 0x00fffffc));
stat &= ~0x00000008;
nv_wr32(pbus, 0x001100, 0x00000008);
}
if (stat & 0x00070000) {
subdev = nouveau_subdev(pbus, NVDEV_SUBDEV_THERM);
if (subdev && subdev->intr)
subdev->intr(subdev);
stat &= ~0x00070000;
nv_wr32(pbus, 0x001100, 0x00070000);
}
if (stat) {
nv_error(pbus, "unknown intr 0x%08x\n", stat);
nv_mask(pbus, 0x001140, stat, 0x00000000);
}
}
static int
nv31_bus_init(struct nouveau_object *object)
{
struct nv31_bus_priv *priv = (void *)object;
int ret;
ret = nouveau_bus_init(&priv->base);
if (ret)
return ret;
nv_wr32(priv, 0x001100, 0xffffffff);
nv_wr32(priv, 0x001140, 0x00070008);
return 0;
}
static int
nv31_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nv31_bus_priv *priv;
int ret;
ret = nouveau_bus_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
nv_subdev(priv)->intr = nv31_bus_intr;
return 0;
}
struct nouveau_oclass
nv31_bus_oclass = {
.handle = NV_SUBDEV(BUS, 0x31),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv31_bus_ctor,
.dtor = _nouveau_bus_dtor,
.init = nv31_bus_init,
.fini = _nouveau_bus_fini,
},
};
This diff is collapsed.
This diff is collapsed.
......@@ -66,6 +66,7 @@ static const u64 disable_map[] = {
[NVDEV_SUBDEV_CLOCK] = NV_DEVICE_DISABLE_CORE,
[NVDEV_SUBDEV_MXM] = NV_DEVICE_DISABLE_CORE,
[NVDEV_SUBDEV_MC] = NV_DEVICE_DISABLE_CORE,
[NVDEV_SUBDEV_BUS] = NV_DEVICE_DISABLE_CORE,
[NVDEV_SUBDEV_TIMER] = NV_DEVICE_DISABLE_CORE,
[NVDEV_SUBDEV_FB] = NV_DEVICE_DISABLE_CORE,
[NVDEV_SUBDEV_LTCG] = NV_DEVICE_DISABLE_CORE,
......@@ -103,8 +104,8 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
struct nouveau_device *device;
struct nouveau_devobj *devobj;
struct nv_device_class *args = data;
u64 disable, boot0, strap;
u64 mmio_base, mmio_size;
u32 boot0, strap;
u64 disable, mmio_base, mmio_size;
void __iomem *map;
int ret, i, c;
......
......@@ -24,6 +24,7 @@
#include <subdev/device.h>
#include <subdev/bios.h>
#include <subdev/bus.h>
#include <subdev/i2c.h>
#include <subdev/clock.h>
#include <subdev/devinit.h>
......@@ -46,10 +47,11 @@ nv04_identify(struct nouveau_device *device)
case 0x04:
device->cname = "NV04";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv04_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass;
device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = &nv04_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
......@@ -63,10 +65,11 @@ nv04_identify(struct nouveau_device *device)
case 0x05:
device->cname = "NV05";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv05_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass;
device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = &nv04_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
......
......@@ -78,12 +78,13 @@ nv50_devinit_init(struct nouveau_object *object)
if (ret)
return ret;
/* if we ran the init tables, execute first script pointer for each
* display table output entry that has a matching dcb entry.
/* if we ran the init tables, we have to execute the first script
* pointer of each dcb entry's display encoder table in order
* to properly initialise each encoder.
*/
while (priv->base.post && ver) {
u16 data = nvbios_outp_parse(bios, i++, &ver, &hdr, &cnt, &len, &info);
if (data && dcb_outp_match(bios, info.type, info.mask, &ver, &len, &outp)) {
while (priv->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) {
if (nvbios_outp_match(bios, outp.hasht, outp.hashm,
&ver, &hdr, &cnt, &len, &info)) {
struct nvbios_init init = {
.subdev = nv_subdev(priv),
.bios = bios,
......@@ -95,7 +96,8 @@ nv50_devinit_init(struct nouveau_object *object)
nvbios_exec(&init);
}
};
i++;
}
return 0;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment