Merge branch 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux...
authorDave Airlie <airlied@redhat.com>
Wed, 20 Feb 2013 07:46:25 +0000 (17:46 +1000)
committerDave Airlie <airlied@redhat.com>
Wed, 20 Feb 2013 07:54:13 +0000 (17:54 +1000)
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.
  ...

149 files changed:
Documentation/thermal/nouveau_thermal [new file with mode: 0644]
drivers/gpu/drm/nouveau/Kconfig
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/core/core/client.c
drivers/gpu/drm/nouveau/core/core/enum.c
drivers/gpu/drm/nouveau/core/core/event.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/copy/nva3.c
drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c
drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c
drivers/gpu/drm/nouveau/core/engine/disp/base.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/dport.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/dport.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
drivers/gpu/drm/nouveau/core/engine/fifo/base.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
drivers/gpu/drm/nouveau/core/engine/graph/nv04.c
drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
drivers/gpu/drm/nouveau/core/engine/graph/nve0.c
drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
drivers/gpu/drm/nouveau/core/engine/software/nv50.c
drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
drivers/gpu/drm/nouveau/core/include/core/class.h
drivers/gpu/drm/nouveau/core/include/core/client.h
drivers/gpu/drm/nouveau/core/include/core/device.h
drivers/gpu/drm/nouveau/core/include/core/enum.h
drivers/gpu/drm/nouveau/core/include/core/event.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/core/object.h
drivers/gpu/drm/nouveau/core/include/core/printk.h
drivers/gpu/drm/nouveau/core/include/engine/disp.h
drivers/gpu/drm/nouveau/core/include/engine/fifo.h
drivers/gpu/drm/nouveau/core/include/engine/software.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/gpio.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/i2c.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/xpio.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/bus.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/gpio.h
drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
drivers/gpu/drm/nouveau/core/include/subdev/therm.h
drivers/gpu/drm/nouveau/core/include/subdev/timer.h
drivers/gpu/drm/nouveau/core/os.h
drivers/gpu/drm/nouveau/core/subdev/bios/base.c
drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c
drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c
drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c
drivers/gpu/drm/nouveau/core/subdev/bios/i2c.c
drivers/gpu/drm/nouveau/core/subdev/bios/init.c
drivers/gpu/drm/nouveau/core/subdev/bios/therm.c
drivers/gpu/drm/nouveau/core/subdev/bios/xpio.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/device/base.c
drivers/gpu/drm/nouveau/core/subdev/device/nv04.c
drivers/gpu/drm/nouveau/core/subdev/device/nv10.c
drivers/gpu/drm/nouveau/core/subdev/device/nv20.c
drivers/gpu/drm/nouveau/core/subdev/device/nv30.c
drivers/gpu/drm/nouveau/core/subdev/device/nv40.c
drivers/gpu/drm/nouveau/core/subdev/device/nv50.c
drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/device/nve0.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c
drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/mxm/mxms.c
drivers/gpu/drm/nouveau/core/subdev/therm/base.c
drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
drivers/gpu/drm/nouveau/core/subdev/therm/fannil.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c
drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
drivers/gpu/drm/nouveau/core/subdev/therm/temp.c
drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c
drivers/gpu/drm/nouveau/nouveau_acpi.h
drivers/gpu/drm/nouveau/nouveau_backlight.c
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bios.h
drivers/gpu/drm/nouveau/nouveau_bo.c
drivers/gpu/drm/nouveau/nouveau_chan.c
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_connector.h
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_display.h
drivers/gpu/drm/nouveau/nouveau_dma.h
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_drm.h
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nouveau_fence.c
drivers/gpu/drm/nouveau/nouveau_fence.h
drivers/gpu/drm/nouveau/nouveau_gem.c
drivers/gpu/drm/nouveau/nouveau_pm.c
drivers/gpu/drm/nouveau/nv04_dfp.c
drivers/gpu/drm/nouveau/nv04_display.c
drivers/gpu/drm/nouveau/nv04_display.h
drivers/gpu/drm/nouveau/nv04_fence.c
drivers/gpu/drm/nouveau/nv10_fence.c
drivers/gpu/drm/nouveau/nv10_fence.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/nv17_fence.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_fence.c
drivers/gpu/drm/nouveau/nv84_fence.c
drivers/gpu/drm/nouveau/nvc0_fence.c

diff --git a/Documentation/thermal/nouveau_thermal b/Documentation/thermal/nouveau_thermal
new file mode 100644 (file)
index 0000000..efceb78
--- /dev/null
@@ -0,0 +1,81 @@
+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
index 47ccc1a..a7ff6d5 100644 (file)
@@ -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.
index ab25752..51d0972 100644 (file)
@@ -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
index 8bbb58f..295c221 100644 (file)
@@ -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;
+}
index 7cc7133..dd43479 100644 (file)
@@ -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);
 }
diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c
new file mode 100644 (file)
index 0000000..6d01e0f
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * 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;
+}
index 283248c..d6dc2a6 100644 (file)
@@ -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;
        }
index b974905..5bc021f 100644 (file)
@@ -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);
index 21986f3..8bf8955 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs
  */
 
+#include <core/client.h>
 #include <core/os.h>
 #include <core/enum.h>
 #include <core/class.h>
@@ -102,8 +103,9 @@ nv98_crypt_intr(struct nouveau_subdev *subdev)
        if (stat & 0x00000040) {
                nv_error(priv, "DISPATCH_ERROR [");
                nouveau_enum_print(nv98_crypt_isr_error_name, ssta);
-               printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n",
-                      chid, (u64)inst << 12, subc, mthd, data);
+               pr_cont("] ch %d [0x%010llx %s] subc %d mthd 0x%04x data 0x%08x\n",
+                      chid, (u64)inst << 12, nouveau_client_name(engctx),
+                      subc, mthd, data);
                nv_wr32(priv, 0x087004, 0x00000040);
                stat &= ~0x00000040;
        }
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/base.c b/drivers/gpu/drm/nouveau/core/engine/disp/base.c
new file mode 100644 (file)
index 0000000..7a5cae4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <engine/disp.h>
+
+void
+_nouveau_disp_dtor(struct nouveau_object *object)
+{
+       struct nouveau_disp *disp = (void *)object;
+       nouveau_event_destroy(&disp->vblank);
+       nouveau_engine_destroy(&disp->base);
+}
+
+int
+nouveau_disp_create_(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, int heads,
+                    const char *intname, const char *extname,
+                    int length, void **pobject)
+{
+       struct nouveau_disp *disp;
+       int ret;
+
+       ret = nouveau_engine_create_(parent, engine, oclass, true,
+                                    intname, extname, length, pobject);
+       disp = *pobject;
+       if (ret)
+               return ret;
+
+       return nouveau_event_create(heads, &disp->vblank);
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
new file mode 100644 (file)
index 0000000..fa27b02
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/dp.h>
+#include <subdev/bios/init.h>
+#include <subdev/i2c.h>
+
+#include <engine/disp.h>
+
+#include "dport.h"
+
+#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt,             \
+                                  dp->outp->hasht, dp->outp->hashm, ##args)
+#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt,             \
+                                  dp->outp->hasht, dp->outp->hashm, ##args)
+
+/******************************************************************************
+ * link training
+ *****************************************************************************/
+struct dp_state {
+       const struct nouveau_dp_func *func;
+       struct nouveau_disp *disp;
+       struct dcb_output *outp;
+       struct nvbios_dpout info;
+       u8 version;
+       struct nouveau_i2c_port *aux;
+       int head;
+       u8  dpcd[4];
+       int link_nr;
+       u32 link_bw;
+       u8  stat[6];
+       u8  conf[4];
+};
+
+static int
+dp_set_link_config(struct dp_state *dp)
+{
+       struct nouveau_disp *disp = dp->disp;
+       struct nouveau_bios *bios = nouveau_bios(disp);
+       struct nvbios_init init = {
+               .subdev = nv_subdev(dp->disp),
+               .bios = bios,
+               .offset = 0x0000,
+               .outp = dp->outp,
+               .crtc = dp->head,
+               .execute = 1,
+       };
+       u32 lnkcmp;
+       u8 sink[2];
+
+       DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
+
+       /* set desired link configuration on the sink */
+       sink[0] = dp->link_bw / 27000;
+       sink[1] = dp->link_nr;
+       if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
+               sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
+
+       nv_wraux(dp->aux, DPCD_LC00, sink, 2);
+
+       /* set desired link configuration on the source */
+       if ((lnkcmp = dp->info.lnkcmp)) {
+               if (dp->version < 0x30) {
+                       while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp))
+                               lnkcmp += 4;
+                       init.offset = nv_ro16(bios, lnkcmp + 2);
+               } else {
+                       while ((dp->link_bw / 27000) < nv_ro08(bios, lnkcmp))
+                               lnkcmp += 3;
+                       init.offset = nv_ro16(bios, lnkcmp + 1);
+               }
+
+               nvbios_exec(&init);
+       }
+
+       return dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
+                                dp->link_nr, dp->link_bw / 27000,
+                                dp->dpcd[DPCD_RC02] &
+                                         DPCD_RC02_ENHANCED_FRAME_CAP);
+}
+
+static void
+dp_set_training_pattern(struct dp_state *dp, u8 pattern)
+{
+       u8 sink_tp;
+
+       DBG("training pattern %d\n", pattern);
+       dp->func->pattern(dp->disp, dp->outp, dp->head, pattern);
+
+       nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
+       sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
+       sink_tp |= pattern;
+       nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
+}
+
+static int
+dp_link_train_commit(struct dp_state *dp)
+{
+       int i;
+
+       for (i = 0; i < dp->link_nr; i++) {
+               u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
+               u8 lpre = (lane & 0x0c) >> 2;
+               u8 lvsw = (lane & 0x03) >> 0;
+
+               dp->conf[i] = (lpre << 3) | lvsw;
+               if (lvsw == 3)
+                       dp->conf[i] |= DPCD_LC03_MAX_SWING_REACHED;
+               if (lpre == 3)
+                       dp->conf[i] |= DPCD_LC03_MAX_PRE_EMPHASIS_REACHED;
+
+               DBG("config lane %d %02x\n", i, dp->conf[i]);
+               dp->func->drv_ctl(dp->disp, dp->outp, dp->head, i, lvsw, lpre);
+       }
+
+       return nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4);
+}
+
+static int
+dp_link_train_update(struct dp_state *dp, u32 delay)
+{
+       int ret;
+
+       udelay(delay);
+
+       ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6);
+       if (ret)
+               return ret;
+
+       DBG("status %*ph\n", 6, dp->stat);
+       return 0;
+}
+
+static int
+dp_link_train_cr(struct dp_state *dp)
+{
+       bool cr_done = false, abort = false;
+       int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
+       int tries = 0, i;
+
+       dp_set_training_pattern(dp, 1);
+
+       do {
+               if (dp_link_train_commit(dp) ||
+                   dp_link_train_update(dp, 100))
+                       break;
+
+               cr_done = true;
+               for (i = 0; i < dp->link_nr; i++) {
+                       u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
+                       if (!(lane & DPCD_LS02_LANE0_CR_DONE)) {
+                               cr_done = false;
+                               if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED)
+                                       abort = true;
+                               break;
+                       }
+               }
+
+               if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) {
+                       voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
+                       tries = 0;
+               }
+       } while (!cr_done && !abort && ++tries < 5);
+
+       return cr_done ? 0 : -1;
+}
+
+static int
+dp_link_train_eq(struct dp_state *dp)
+{
+       bool eq_done, cr_done = true;
+       int tries = 0, i;
+
+       dp_set_training_pattern(dp, 2);
+
+       do {
+               if (dp_link_train_update(dp, 400))
+                       break;
+
+               eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
+               for (i = 0; i < dp->link_nr && eq_done; i++) {
+                       u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
+                       if (!(lane & DPCD_LS02_LANE0_CR_DONE))
+                               cr_done = false;
+                       if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
+                           !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED))
+                               eq_done = false;
+               }
+
+               if (dp_link_train_commit(dp))
+                       break;
+       } while (!eq_done && cr_done && ++tries <= 5);
+
+       return eq_done ? 0 : -1;
+}
+
+static void
+dp_link_train_init(struct dp_state *dp, bool spread)
+{
+       struct nvbios_init init = {
+               .subdev = nv_subdev(dp->disp),
+               .bios = nouveau_bios(dp->disp),
+               .outp = dp->outp,
+               .crtc = dp->head,
+               .execute = 1,
+       };
+
+       /* set desired spread */
+       if (spread)
+               init.offset = dp->info.script[2];
+       else
+               init.offset = dp->info.script[3];
+       nvbios_exec(&init);
+
+       /* pre-train script */
+       init.offset = dp->info.script[0];
+       nvbios_exec(&init);
+}
+
+static void
+dp_link_train_fini(struct dp_state *dp)
+{
+       struct nvbios_init init = {
+               .subdev = nv_subdev(dp->disp),
+               .bios = nouveau_bios(dp->disp),
+               .outp = dp->outp,
+               .crtc = dp->head,
+               .execute = 1,
+       };
+
+       /* post-train script */
+       init.offset = dp->info.script[1],
+       nvbios_exec(&init);
+}
+
+int
+nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
+                struct dcb_output *outp, int head, u32 datarate)
+{
+       struct nouveau_bios *bios = nouveau_bios(disp);
+       struct nouveau_i2c *i2c = nouveau_i2c(disp);
+       struct dp_state _dp = {
+               .disp = disp,
+               .func = func,
+               .outp = outp,
+               .head = head,
+       }, *dp = &_dp;
+       const u32 bw_list[] = { 270000, 162000, 0 };
+       const u32 *link_bw = bw_list;
+       u8  hdr, cnt, len;
+       u32 data;
+       int ret;
+
+       /* find the bios displayport data relevant to this output */
+       data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version,
+                                &hdr, &cnt, &len, &dp->info);
+       if (!data) {
+               ERR("bios data not found\n");
+               return -EINVAL;
+       }
+
+       /* acquire the aux channel and fetch some info about the display */
+       if (outp->location)
+               dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
+       else
+               dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index));
+       if (!dp->aux) {
+               ERR("no aux channel?!\n");
+               return -ENODEV;
+       }
+
+       ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd));
+       if (ret) {
+               ERR("failed to read DPCD\n");
+               return ret;
+       }
+
+       /* adjust required bandwidth for 8B/10B coding overhead */
+       datarate = (datarate / 8) * 10;
+
+       /* enable down-spreading and execute pre-train script from vbios */
+       dp_link_train_init(dp, dp->dpcd[3] & 0x01);
+
+       /* start off at highest link rate supported by encoder and display */
+       while (*link_bw > (dp->dpcd[1] * 27000))
+               link_bw++;
+
+       while (link_bw[0]) {
+               /* find minimum required lane count at this link rate */
+               dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT;
+               while ((dp->link_nr >> 1) * link_bw[0] > datarate)
+                       dp->link_nr >>= 1;
+
+               /* drop link rate to minimum with this lane count */
+               while ((link_bw[1] * dp->link_nr) > datarate)
+                       link_bw++;
+               dp->link_bw = link_bw[0];
+
+               /* program selected link configuration */
+               ret = dp_set_link_config(dp);
+               if (ret == 0) {
+                       /* attempt to train the link at this configuration */
+                       memset(dp->stat, 0x00, sizeof(dp->stat));
+                       if (!dp_link_train_cr(dp) &&
+                           !dp_link_train_eq(dp))
+                               break;
+               } else
+               if (ret >= 1) {
+                       /* dp_set_link_config() handled training */
+                       break;
+               }
+
+               /* retry at lower rate */
+               link_bw++;
+       }
+
+       /* finish link training */
+       dp_set_training_pattern(dp, 0);
+
+       /* execute post-train script from vbios */
+       dp_link_train_fini(dp);
+       return true;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h
new file mode 100644 (file)
index 0000000..0e1bbd1
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef __NVKM_DISP_DPORT_H__
+#define __NVKM_DISP_DPORT_H__
+
+/* DPCD Receiver Capabilities */
+#define DPCD_RC00                                                       0x00000
+#define DPCD_RC00_DPCD_REV                                                 0xff
+#define DPCD_RC01                                                       0x00001
+#define DPCD_RC01_MAX_LINK_RATE                                            0xff
+#define DPCD_RC02                                                       0x00002
+#define DPCD_RC02_ENHANCED_FRAME_CAP                                       0x80
+#define DPCD_RC02_MAX_LANE_COUNT                                           0x1f
+#define DPCD_RC03                                                       0x00003
+#define DPCD_RC03_MAX_DOWNSPREAD                                           0x01
+
+/* DPCD Link Configuration */
+#define DPCD_LC00                                                       0x00100
+#define DPCD_LC00_LINK_BW_SET                                              0xff
+#define DPCD_LC01                                                       0x00101
+#define DPCD_LC01_ENHANCED_FRAME_EN                                        0x80
+#define DPCD_LC01_LANE_COUNT_SET                                           0x1f
+#define DPCD_LC02                                                       0x00102
+#define DPCD_LC02_TRAINING_PATTERN_SET                                     0x03
+#define DPCD_LC03(l)                                            ((l) +  0x00103)
+#define DPCD_LC03_MAX_PRE_EMPHASIS_REACHED                                 0x20
+#define DPCD_LC03_PRE_EMPHASIS_SET                                         0x18
+#define DPCD_LC03_MAX_SWING_REACHED                                        0x04
+#define DPCD_LC03_VOLTAGE_SWING_SET                                        0x03
+
+/* DPCD Link/Sink Status */
+#define DPCD_LS02                                                       0x00202
+#define DPCD_LS02_LANE1_SYMBOL_LOCKED                                      0x40
+#define DPCD_LS02_LANE1_CHANNEL_EQ_DONE                                    0x20
+#define DPCD_LS02_LANE1_CR_DONE                                            0x10
+#define DPCD_LS02_LANE0_SYMBOL_LOCKED                                      0x04
+#define DPCD_LS02_LANE0_CHANNEL_EQ_DONE                                    0x02
+#define DPCD_LS02_LANE0_CR_DONE                                            0x01
+#define DPCD_LS03                                                       0x00203
+#define DPCD_LS03_LANE3_SYMBOL_LOCKED                                      0x40
+#define DPCD_LS03_LANE3_CHANNEL_EQ_DONE                                    0x20
+#define DPCD_LS03_LANE3_CR_DONE                                            0x10
+#define DPCD_LS03_LANE2_SYMBOL_LOCKED                                      0x04
+#define DPCD_LS03_LANE2_CHANNEL_EQ_DONE                                    0x02
+#define DPCD_LS03_LANE2_CR_DONE                                            0x01
+#define DPCD_LS04                                                       0x00204
+#define DPCD_LS04_LINK_STATUS_UPDATED                                      0x80
+#define DPCD_LS04_DOWNSTREAM_PORT_STATUS_CHANGED                           0x40
+#define DPCD_LS04_INTERLANE_ALIGN_DONE                                     0x01
+#define DPCD_LS06                                                       0x00206
+#define DPCD_LS06_LANE1_PRE_EMPHASIS                                       0xc0
+#define DPCD_LS06_LANE1_VOLTAGE_SWING                                      0x30
+#define DPCD_LS06_LANE0_PRE_EMPHASIS                                       0x0c
+#define DPCD_LS06_LANE0_VOLTAGE_SWING                                      0x03
+#define DPCD_LS07                                                       0x00207
+#define DPCD_LS07_LANE3_PRE_EMPHASIS                                       0xc0
+#define DPCD_LS07_LANE3_VOLTAGE_SWING                                      0x30
+#define DPCD_LS07_LANE2_PRE_EMPHASIS                                       0x0c
+#define DPCD_LS07_LANE2_VOLTAGE_SWING                                      0x03
+
+struct nouveau_disp;
+struct dcb_output;
+
+struct nouveau_dp_func {
+       int (*pattern)(struct nouveau_disp *, struct dcb_output *,
+                      int head, int pattern);
+       int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
+                      int link_nr, int link_bw, bool enh_frame);
+       int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
+                      int lane, int swing, int preem);
+};
+
+extern const struct nouveau_dp_func nv94_sor_dp_func;
+extern const struct nouveau_dp_func nvd0_sor_dp_func;
+extern const struct nouveau_dp_func nv50_pior_dp_func;
+
+int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *,
+                    struct dcb_output *, int, u32);
+
+#endif
index 1c919f2..05e903f 100644 (file)
 
 #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;
 }
 
index ca1a7d7..314dda6 100644 (file)
@@ -27,7 +27,6 @@
 #include <core/handle.h>
 #include <core/class.h>
 
-#include <engine/software.h>
 #include <engine/disp.h>
 
 #include <subdev/bios.h>
@@ -37,7 +36,6 @@
 #include <subdev/bios/pll.h>
 #include <subdev/timer.h>
 #include <subdev/fb.h>
-#include <subdev/bar.h>
 #include <subdev/clock.h>
 
 #include "nv50.h"
@@ -335,7 +333,7 @@ nv50_disp_sync_ctor(struct nouveau_object *parent,
        struct nv50_disp_dmac *dmac;
        int ret;
 
-       if (size < sizeof(*data) || args->head > 1)
+       if (size < sizeof(*args) || args->head > 1)
                return -EINVAL;
 
        ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -374,7 +372,7 @@ nv50_disp_ovly_ctor(struct nouveau_object *parent,
        struct nv50_disp_dmac *dmac;
        int ret;
 
-       if (size < sizeof(*data) || args->head > 1)
+       if (size < sizeof(*args) || args->head > 1)
                return -EINVAL;
 
        ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -543,6 +541,18 @@ nv50_disp_curs_ofuncs = {
  * Base display object
  ******************************************************************************/
 
+static void
+nv50_disp_base_vblank_enable(struct nouveau_event *event, int head)
+{
+       nv_mask(event->priv, 0x61002c, (1 << head), (1 << head));
+}
+
+static void
+nv50_disp_base_vblank_disable(struct nouveau_event *event, int head)
+{
+       nv_mask(event->priv, 0x61002c, (1 << head), (0 << head));
+}
+
 static int
 nv50_disp_base_ctor(struct nouveau_object *parent,
                    struct nouveau_object *engine,
@@ -559,6 +569,9 @@ nv50_disp_base_ctor(struct nouveau_object *parent,
        if (ret)
                return ret;
 
+       priv->base.vblank->priv = priv;
+       priv->base.vblank->enable = nv50_disp_base_vblank_enable;
+       priv->base.vblank->disable = nv50_disp_base_vblank_disable;
        return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht);
 }
 
@@ -613,7 +626,7 @@ nv50_disp_base_init(struct nouveau_object *object)
                nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp);
        }
 
-       /* ... EXT caps */
+       /* ... PIOR caps */
        for (i = 0; i < 3; i++) {
                tmp = nv_rd32(priv, 0x61e000 + (i * 0x800));
                nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp);
@@ -665,6 +678,9 @@ nv50_disp_base_omthds[] = {
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
        { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR)    , nv50_pior_mthd },
        {},
 };
 
@@ -756,50 +772,6 @@ nv50_disp_intr_error(struct nv50_disp_priv *priv)
        }
 }
 
-static void
-nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
-{
-       struct nouveau_bar *bar = nouveau_bar(priv);
-       struct nouveau_disp *disp = &priv->base;
-       struct nouveau_software_chan *chan, *temp;
-       unsigned long flags;
-
-       spin_lock_irqsave(&disp->vblank.lock, flags);
-       list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) {
-               if (chan->vblank.crtc != crtc)
-                       continue;
-
-               if (nv_device(priv)->chipset >= 0xc0) {
-                       nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
-                       bar->flush(bar);
-                       nv_wr32(priv, 0x06000c,
-                               upper_32_bits(chan->vblank.offset));
-                       nv_wr32(priv, 0x060010,
-                               lower_32_bits(chan->vblank.offset));
-                       nv_wr32(priv, 0x060014, chan->vblank.value);
-               } else {
-                       nv_wr32(priv, 0x001704, chan->vblank.channel);
-                       nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma);
-                       bar->flush(bar);
-                       if (nv_device(priv)->chipset == 0x50) {
-                               nv_wr32(priv, 0x001570, chan->vblank.offset);
-                               nv_wr32(priv, 0x001574, chan->vblank.value);
-                       } else {
-                               nv_wr32(priv, 0x060010, chan->vblank.offset);
-                               nv_wr32(priv, 0x060014, chan->vblank.value);
-                       }
-               }
-
-               list_del(&chan->vblank.head);
-               if (disp->vblank.put)
-                       disp->vblank.put(disp->vblank.data, crtc);
-       }
-       spin_unlock_irqrestore(&disp->vblank.lock, flags);
-
-       if (disp->vblank.notify)
-               disp->vblank.notify(disp->vblank.data, crtc);
-}
-
 static u16
 exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
            struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
@@ -811,8 +783,8 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
        if (outp < 4) {
                type = DCB_OUTPUT_ANALOG;
                mask = 0;
-       } else {
-               outp -= 4;
+       } else
+       if (outp < 8) {
                switch (ctrl & 0x00000f00) {
                case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
                case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
@@ -824,6 +796,17 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
                        nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
                        return 0x0000;
                }
+               outp -= 4;
+       } else {
+               outp = outp - 8;
+               type = 0x0010;
+               mask = 0;
+               switch (ctrl & 0x00000f00) {
+               case 0x00000000: type |= priv->pior.type[outp]; break;
+               default:
+                       nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl);
+                       return 0x0000;
+               }
        }
 
        mask  = 0x00c0 & (mask << 6);
@@ -834,6 +817,10 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
        if (!data)
                return 0x0000;
 
+       /* off-chip encoders require matching the exact encoder type */
+       if (dcb->location != 0)
+               type |= dcb->extdev << 8;
+
        return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
 }
 
@@ -848,9 +835,11 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
        u32 ctrl = 0x00000000;
        int i;
 
+       /* DAC */
        for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
                ctrl = nv_rd32(priv, 0x610b5c + (i * 8));
 
+       /* SOR */
        if (!(ctrl & (1 << head))) {
                if (nv_device(priv)->chipset  < 0x90 ||
                    nv_device(priv)->chipset == 0x92 ||
@@ -865,6 +854,13 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
                }
        }
 
+       /* PIOR */
+       if (!(ctrl & (1 << head))) {
+               for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
+                       ctrl = nv_rd32(priv, 0x610b84 + (i * 8));
+               i += 8;
+       }
+
        if (!(ctrl & (1 << head)))
                return false;
        i--;
@@ -894,13 +890,15 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
        struct nvbios_outp info1;
        struct nvbios_ocfg info2;
        u8  ver, hdr, cnt, len;
-       u16 data, conf;
        u32 ctrl = 0x00000000;
+       u32 data, conf = ~0;
        int i;
 
+       /* DAC */
        for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
                ctrl = nv_rd32(priv, 0x610b58 + (i * 8));
 
+       /* SOR */
        if (!(ctrl & (1 << head))) {
                if (nv_device(priv)->chipset  < 0x90 ||
                    nv_device(priv)->chipset == 0x92 ||
@@ -915,34 +913,46 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
                }
        }
 
+       /* PIOR */
+       if (!(ctrl & (1 << head))) {
+               for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
+                       ctrl = nv_rd32(priv, 0x610b80 + (i * 8));
+               i += 8;
+       }
+
        if (!(ctrl & (1 << head)))
-               return 0x0000;
+               return conf;
        i--;
 
        data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1);
        if (!data)
-               return 0x0000;
-
-       switch (outp->type) {
-       case DCB_OUTPUT_TMDS:
-               conf = (ctrl & 0x00000f00) >> 8;
-               if (pclk >= 165000)
-                       conf |= 0x0100;
-               break;
-       case DCB_OUTPUT_LVDS:
-               conf = priv->sor.lvdsconf;
-               break;
-       case DCB_OUTPUT_DP:
+               return conf;
+
+       if (outp->location == 0) {
+               switch (outp->type) {
+               case DCB_OUTPUT_TMDS:
+                       conf = (ctrl & 0x00000f00) >> 8;
+                       if (pclk >= 165000)
+                               conf |= 0x0100;
+                       break;
+               case DCB_OUTPUT_LVDS:
+                       conf = priv->sor.lvdsconf;
+                       break;
+               case DCB_OUTPUT_DP:
+                       conf = (ctrl & 0x00000f00) >> 8;
+                       break;
+               case DCB_OUTPUT_ANALOG:
+               default:
+                       conf = 0x00ff;
+                       break;
+               }
+       } else {
                conf = (ctrl & 0x00000f00) >> 8;
-               break;
-       case DCB_OUTPUT_ANALOG:
-       default:
-               conf = 0x00ff;
-               break;
+               pclk = pclk / 2;
        }
 
        data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
-       if (data) {
+       if (data && id < 0xff) {
                data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
                if (data) {
                        struct nvbios_init init = {
@@ -954,13 +964,11 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
                                .execute = 1,
                        };
 
-                       if (nvbios_exec(&init))
-                               return 0x0000;
-                       return conf;
+                       nvbios_exec(&init);
                }
        }
 
-       return 0x0000;
+       return conf;
 }
 
 static void
@@ -973,7 +981,6 @@ nv50_disp_intr_unk10(struct nv50_disp_priv *priv, u32 super)
                        exec_script(priv, head, 1);
        }
 
-       nv_wr32(priv, 0x610024, 0x00000010);
        nv_wr32(priv, 0x610030, 0x80000000);
 }
 
@@ -1088,7 +1095,6 @@ static void
 nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super)
 {
        struct dcb_output outp;
-       u32 addr, mask, data;
        int head;
 
        /* finish detaching encoder? */
@@ -1104,33 +1110,58 @@ nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super)
                        struct nouveau_clock *clk = nouveau_clock(priv);
                        clk->pll_set(clk, PLL_VPLL0 + head, pclk);
                }
-
-               nv_mask(priv, 0x614200 + head * 0x800, 0x0000000f, 0x00000000);
        }
 
        /* (re)attach the relevant OR to the head */
        head = ffs((super & 0x00000180) >> 7) - 1;
        if (head >= 0) {
                u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-               u32 conf = exec_clkcmp(priv, head, 0, pclk, &outp);
-               if (conf) {
-                       if (outp.type == DCB_OUTPUT_ANALOG) {
-                               addr = 0x614280 + (ffs(outp.or) - 1) * 0x800;
-                               mask = 0xffffffff;
-                               data = 0x00000000;
-                       } else {
+               u32 hval, hreg = 0x614200 + (head * 0x800);
+               u32 oval, oreg;
+               u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
+               if (conf != ~0) {
+                       if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) {
+                               u32 soff = (ffs(outp.or) - 1) * 0x08;
+                               u32 ctrl = nv_rd32(priv, 0x610798 + soff);
+                               u32 datarate;
+
+                               switch ((ctrl & 0x000f0000) >> 16) {
+                               case 6: datarate = pclk * 30 / 8; break;
+                               case 5: datarate = pclk * 24 / 8; break;
+                               case 2:
+                               default:
+                                       datarate = pclk * 18 / 8;
+                                       break;
+                               }
+
+                               nouveau_dp_train(&priv->base, priv->sor.dp,
+                                                &outp, head, datarate);
+                       }
+
+                       exec_clkcmp(priv, head, 0, pclk, &outp);
+
+                       if (!outp.location && outp.type == DCB_OUTPUT_ANALOG) {
+                               oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800;
+                               oval = 0x00000000;
+                               hval = 0x00000000;
+                       } else
+                       if (!outp.location) {
                                if (outp.type == DCB_OUTPUT_DP)
                                        nv50_disp_intr_unk20_dp(priv, &outp, pclk);
-                               addr = 0x614300 + (ffs(outp.or) - 1) * 0x800;
-                               mask = 0x00000707;
-                               data = (conf & 0x0100) ? 0x0101 : 0x0000;
+                               oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800;
+                               oval = (conf & 0x0100) ? 0x0101 : 0x0000;
+                               hval = 0x00000000;
+                       } else {
+                               oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800;
+                               oval = 0x00000001;
+                               hval = 0x00000001;
                        }
 
-                       nv_mask(priv, addr, mask, data);
+                       nv_mask(priv, hreg, 0x0000000f, hval);
+                       nv_mask(priv, oreg, 0x00000707, oval);
                }
        }
 
-       nv_wr32(priv, 0x610024, 0x00000020);
        nv_wr32(priv, 0x610030, 0x80000000);
 }
 
@@ -1163,28 +1194,47 @@ nv50_disp_intr_unk40(struct nv50_disp_priv *priv, u32 super)
        if (head >= 0) {
                struct dcb_output outp;
                u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-               if (pclk && exec_clkcmp(priv, head, 1, pclk, &outp)) {
-                       if (outp.type == DCB_OUTPUT_TMDS)
+               if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) {
+                       if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS)
                                nv50_disp_intr_unk40_tmds(priv, &outp);
+                       else
+                       if (outp.location == 1 && outp.type == DCB_OUTPUT_DP) {
+                               u32 soff = (ffs(outp.or) - 1) * 0x08;
+                               u32 ctrl = nv_rd32(priv, 0x610b84 + soff);
+                               u32 datarate;
+
+                               switch ((ctrl & 0x000f0000) >> 16) {
+                               case 6: datarate = pclk * 30 / 8; break;
+                               case 5: datarate = pclk * 24 / 8; break;
+                               case 2:
+                               default:
+                                       datarate = pclk * 18 / 8;
+                                       break;
+                               }
+
+                               nouveau_dp_train(&priv->base, priv->pior.dp,
+                                                &outp, head, datarate);
+                       }
                }
        }
 
-       nv_wr32(priv, 0x610024, 0x00000040);
        nv_wr32(priv, 0x610030, 0x80000000);
 }
 
-static void
-nv50_disp_intr_super(struct nv50_disp_priv *priv, u32 intr1)
+void
+nv50_disp_intr_supervisor(struct work_struct *work)
 {
+       struct nv50_disp_priv *priv =
+               container_of(work, struct nv50_disp_priv, supervisor);
        u32 super = nv_rd32(priv, 0x610030);
 
-       nv_debug(priv, "supervisor 0x%08x 0x%08x\n", intr1, super);
+       nv_debug(priv, "supervisor 0x%08x 0x%08x\n", priv->super, super);
 
-       if (intr1 & 0x00000010)
+       if (priv->super & 0x00000010)
                nv50_disp_intr_unk10(priv, super);
-       if (intr1 & 0x00000020)
+       if (priv->super & 0x00000020)
                nv50_disp_intr_unk20(priv, super);
-       if (intr1 & 0x00000040)
+       if (priv->super & 0x00000040)
                nv50_disp_intr_unk40(priv, super);
 }
 
@@ -1201,19 +1251,21 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
        }
 
        if (intr1 & 0x00000004) {
-               nv50_disp_intr_vblank(priv, 0);
+               nouveau_event_trigger(priv->base.vblank, 0);
                nv_wr32(priv, 0x610024, 0x00000004);
                intr1 &= ~0x00000004;
        }
 
        if (intr1 & 0x00000008) {
-               nv50_disp_intr_vblank(priv, 1);
+               nouveau_event_trigger(priv->base.vblank, 1);
                nv_wr32(priv, 0x610024, 0x00000008);
                intr1 &= ~0x00000008;
        }
 
        if (intr1 & 0x00000070) {
-               nv50_disp_intr_super(priv, intr1);
+               priv->super = (intr1 & 0x00000070);
+               schedule_work(&priv->supervisor);
+               nv_wr32(priv, 0x610024, priv->super);
                intr1 &= ~0x00000070;
        }
 }
@@ -1226,7 +1278,7 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_disp_priv *priv;
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+       ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
                                  "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
@@ -1235,16 +1287,17 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_engine(priv)->sclass = nv50_disp_base_oclass;
        nv_engine(priv)->cclass = &nv50_disp_cclass;
        nv_subdev(priv)->intr = nv50_disp_intr;
+       INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
        priv->sclass = nv50_disp_sclass;
        priv->head.nr = 2;
        priv->dac.nr = 3;
        priv->sor.nr = 2;
+       priv->pior.nr = 3;
        priv->dac.power = nv50_dac_power;
        priv->dac.sense = nv50_dac_sense;
        priv->sor.power = nv50_sor_power;
-
-       INIT_LIST_HEAD(&priv->base.vblank.list);
-       spin_lock_init(&priv->base.vblank.lock);
+       priv->pior.power = nv50_pior_power;
+       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
index a6bb931..1ae6ceb 100644 (file)
@@ -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
index fc84eac..d8c74c0 100644 (file)
@@ -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;
 }
 
index ba9dfd4..a66f949 100644 (file)
@@ -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;
 }
 
index 5d63902..6cf8eef 100644 (file)
@@ -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;
 }
 
index e9192ca..b754131 100644 (file)
@@ -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;
 }
 
index 9e38ebf..7106a72 100644 (file)
 #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;
 }
 
index 259537c..20725b3 100644 (file)
@@ -51,10 +51,11 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_object **pobject)
 {
        struct nv50_disp_priv *priv;
+       int heads = nv_rd32(parent, 0x022448);
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
-                                 "display", &priv);
+       ret = nouveau_disp_create(parent, engine, oclass, heads,
+                                 "PDISP", "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -62,8 +63,9 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_engine(priv)->sclass = nve0_disp_base_oclass;
        nv_engine(priv)->cclass = &nv50_disp_cclass;
        nv_subdev(priv)->intr = nvd0_disp_intr;
+       INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor);
        priv->sclass = nve0_disp_sclass;
-       priv->head.nr = nv_rd32(priv, 0x022448);
+       priv->head.nr = heads;
        priv->dac.nr = 3;
        priv->sor.nr = 4;
        priv->dac.power = nv50_dac_power;
@@ -71,14 +73,7 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hda_eld = nvd0_hda_eld;
        priv->sor.hdmi = nvd0_hdmi_ctrl;
-       priv->sor.dp_train = nvd0_sor_dp_train;
-       priv->sor.dp_train_init = nv94_sor_dp_train_init;
-       priv->sor.dp_train_fini = nv94_sor_dp_train_fini;
-       priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl;
-       priv->sor.dp_drvctl = nvd0_sor_dp_drvctl;
-
-       INIT_LIST_HEAD(&priv->base.vblank.list);
-       spin_lock_init(&priv->base.vblank.lock);
+       priv->sor.dp = &nvd0_sor_dp_func;
        return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
new file mode 100644 (file)
index 0000000..2c8ce35
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/os.h>
+#include <core/class.h>
+
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/timer.h>
+#include <subdev/i2c.h>
+
+#include "nv50.h"
+
+/******************************************************************************
+ * DisplayPort
+ *****************************************************************************/
+static struct nouveau_i2c_port *
+nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp)
+{
+       struct nouveau_i2c *i2c = nouveau_i2c(disp);
+       return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
+}
+
+static int
+nv50_pior_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
+                    int head, int pattern)
+{
+       struct nouveau_i2c_port *port;
+       int ret = -EINVAL;
+
+       port = nv50_pior_dp_find(disp, outp);
+       if (port) {
+               if (port->func->pattern)
+                       ret = port->func->pattern(port, pattern);
+               else
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+static int
+nv50_pior_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                    int head, int lane_nr, int link_bw, bool enh)
+{
+       struct nouveau_i2c_port *port;
+       int ret = -EINVAL;
+
+       port = nv50_pior_dp_find(disp, outp);
+       if (port && port->func->lnk_ctl)
+               ret = port->func->lnk_ctl(port, lane_nr, link_bw, enh);
+
+       return ret;
+}
+
+static int
+nv50_pior_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                    int head, int lane, int vsw, int pre)
+{
+       struct nouveau_i2c_port *port;
+       int ret = -EINVAL;
+
+       port = nv50_pior_dp_find(disp, outp);
+       if (port) {
+               if (port->func->drv_ctl)
+                       ret = port->func->drv_ctl(port, lane, vsw, pre);
+               else
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+const struct nouveau_dp_func
+nv50_pior_dp_func = {
+       .pattern = nv50_pior_dp_pattern,
+       .lnk_ctl = nv50_pior_dp_lnk_ctl,
+       .drv_ctl = nv50_pior_dp_drv_ctl,
+};
+
+/******************************************************************************
+ * General PIOR handling
+ *****************************************************************************/
+int
+nv50_pior_power(struct nv50_disp_priv *priv, int or, u32 data)
+{
+       const u32 stat = data & NV50_DISP_PIOR_PWR_STATE;
+       const u32 soff = (or * 0x800);
+       nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000);
+       nv_mask(priv, 0x61e004 + soff, 0x80000101, 0x80000000 | stat);
+       nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000);
+       return 0;
+}
+
+int
+nv50_pior_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
+{
+       struct nv50_disp_priv *priv = (void *)object->engine;
+       const u8 type = (mthd & NV50_DISP_PIOR_MTHD_TYPE) >> 12;
+       const u8 or   = (mthd & NV50_DISP_PIOR_MTHD_OR);
+       u32 *data = args;
+       int ret;
+
+       if (size < sizeof(u32))
+               return -EINVAL;
+
+       mthd &= ~NV50_DISP_PIOR_MTHD_TYPE;
+       mthd &= ~NV50_DISP_PIOR_MTHD_OR;
+       switch (mthd) {
+       case NV50_DISP_PIOR_PWR:
+               ret = priv->pior.power(priv, or, data[0]);
+               priv->pior.type[or] = type;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ret;
+}
index 39b6b67..ab1e918 100644 (file)
@@ -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);
        }
index f6edd00..7ec4ee8 100644 (file)
 #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,
+};
index c37ce7e..9e1d435 100644 (file)
 #include "nv50.h"
 
 static inline u32
+nvd0_sor_soff(struct dcb_output *outp)
+{
+       return (ffs(outp->or) - 1) * 0x800;
+}
+
+static inline u32
+nvd0_sor_loff(struct dcb_output *outp)
+{
+       return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
+}
+
+static inline u32
 nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
 {
        static const u8 nvd0[] = { 16, 8, 0, 24 };
        return nvd0[lane];
 }
 
-int
-nvd0_sor_dp_train(struct nv50_disp_priv *priv, int or, int link,
-                 u16 type, u16 mask, u32 data, struct dcb_output *info)
+static int
+nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
+                   int head, int pattern)
 {
-       const u32 loff = (or * 0x800) + (link * 0x80);
-       const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN);
-       nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * patt);
+       struct nv50_disp_priv *priv = (void *)disp;
+       const u32 loff = nvd0_sor_loff(outp);
+       nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
        return 0;
 }
 
-int
-nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
-                  u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                   int head, int link_nr, int link_bw, bool enh_frame)
 {
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       const u32 loff = (or * 0x800) + (link * 0x80);
-       const u32 soff = (or * 0x800);
-       const u8  link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8;
-       const u8  link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT);
+       struct nv50_disp_priv *priv = (void *)disp;
+       const u32 soff = nvd0_sor_soff(outp);
+       const u32 loff = nvd0_sor_loff(outp);
        u32 dpctrl = 0x00000000;
        u32 clksor = 0x00000000;
-       u32 outp, lane = 0;
-       u8  ver, hdr, cnt, len;
-       struct nvbios_dpout info;
+       u32 lane = 0;
        int i;
 
-       outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info);
-       if (outp && info.lnkcmp) {
-               struct nvbios_init init = {
-                       .subdev = nv_subdev(priv),
-                       .bios = bios,
-                       .offset = 0x0000,
-                       .outp = dcbo,
-                       .crtc = head,
-                       .execute = 1,
-               };
-
-               while (nv_ro08(bios, info.lnkcmp) < link_bw)
-                       info.lnkcmp += 3;
-               init.offset = nv_ro16(bios, info.lnkcmp + 1);
-
-               nvbios_exec(&init);
-       }
-
        clksor |= link_bw << 18;
        dpctrl |= ((1 << link_nr) - 1) << 16;
-       if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH)
+       if (enh_frame)
                dpctrl |= 0x00004000;
 
        for (i = 0; i < link_nr; i++)
@@ -97,24 +87,25 @@ nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
        return 0;
 }
 
-int
-nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
-                  u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                   int head, int lane, int swing, int preem)
 {
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       const u32 loff = (or * 0x800) + (link * 0x80);
-       const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8;
-       const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE);
+       struct nouveau_bios *bios = nouveau_bios(disp);
+       struct nv50_disp_priv *priv = (void *)disp;
+       const u32 loff = nvd0_sor_loff(outp);
        u32 addr, shift = nvd0_sor_dp_lane_map(priv, lane);
        u8  ver, hdr, cnt, len;
-       struct nvbios_dpout outp;
+       struct nvbios_dpout info;
        struct nvbios_dpcfg ocfg;
 
-       addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp);
+       addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
+                                &ver, &hdr, &cnt, &len, &info);
        if (!addr)
                return -ENODEV;
 
-       addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg);
+       addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
+                                &ver, &hdr, &cnt, &len, &ocfg);
        if (!addr)
                return -EINVAL;
 
@@ -124,3 +115,10 @@ nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
        nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000);
        return 0;
 }
+
+const struct nouveau_dp_func
+nvd0_sor_dp_func = {
+       .pattern = nvd0_sor_dp_pattern,
+       .lnk_ctl = nvd0_sor_dp_lnk_ctl,
+       .drv_ctl = nvd0_sor_dp_drv_ctl,
+};
index c2b9db3..7341ebe 100644 (file)
  * 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;
index a47a854..f877bd5 100644 (file)
@@ -28,6 +28,7 @@
 #include <core/namedb.h>
 #include <core/handle.h>
 #include <core/ramht.h>
+#include <core/event.h>
 
 #include <subdev/instmem.h>
 #include <subdev/instmem/nv04.h>
@@ -398,6 +399,98 @@ out:
        return handled;
 }
 
+static void
+nv04_fifo_cache_error(struct nouveau_device *device,
+               struct nv04_fifo_priv *priv, u32 chid, u32 get)
+{
+       u32 mthd, data;
+       int ptr;
+
+       /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before wrapping on my
+        * G80 chips, but CACHE1 isn't big enough for this much data.. Tests
+        * show that it wraps around to the start at GET=0x800.. No clue as to
+        * why..
+        */
+       ptr = (get & 0x7ff) >> 2;
+
+       if (device->card_type < NV_40) {
+               mthd = nv_rd32(priv, NV04_PFIFO_CACHE1_METHOD(ptr));
+               data = nv_rd32(priv, NV04_PFIFO_CACHE1_DATA(ptr));
+       } else {
+               mthd = nv_rd32(priv, NV40_PFIFO_CACHE1_METHOD(ptr));
+               data = nv_rd32(priv, NV40_PFIFO_CACHE1_DATA(ptr));
+       }
+
+       if (!nv04_fifo_swmthd(priv, chid, mthd, data)) {
+               const char *client_name =
+                       nouveau_client_name_for_fifo_chid(&priv->base, chid);
+               nv_error(priv,
+                        "CACHE_ERROR - ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
+                        chid, client_name, (mthd >> 13) & 7, mthd & 0x1ffc,
+                        data);
+       }
+
+       nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
+       nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_CACHE_ERROR);
+
+       nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
+               nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1);
+       nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
+       nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
+               nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1);
+       nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0);
+
+       nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH,
+               nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
+       nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
+}
+
+static void
+nv04_fifo_dma_pusher(struct nouveau_device *device, struct nv04_fifo_priv *priv,
+               u32 chid)
+{
+       const char *client_name;
+       u32 dma_get = nv_rd32(priv, 0x003244);
+       u32 dma_put = nv_rd32(priv, 0x003240);
+       u32 push = nv_rd32(priv, 0x003220);
+       u32 state = nv_rd32(priv, 0x003228);
+
+       client_name = nouveau_client_name_for_fifo_chid(&priv->base, chid);
+
+       if (device->card_type == NV_50) {
+               u32 ho_get = nv_rd32(priv, 0x003328);
+               u32 ho_put = nv_rd32(priv, 0x003320);
+               u32 ib_get = nv_rd32(priv, 0x003334);
+               u32 ib_put = nv_rd32(priv, 0x003330);
+
+               nv_error(priv,
+                        "DMA_PUSHER - ch %d [%s] get 0x%02x%08x put 0x%02x%08x ib_get 0x%08x ib_put 0x%08x state 0x%08x (err: %s) push 0x%08x\n",
+                        chid, client_name, ho_get, dma_get, ho_put, dma_put,
+                        ib_get, ib_put, state, nv_dma_state_err(state), push);
+
+               /* METHOD_COUNT, in DMA_STATE on earlier chipsets */
+               nv_wr32(priv, 0x003364, 0x00000000);
+               if (dma_get != dma_put || ho_get != ho_put) {
+                       nv_wr32(priv, 0x003244, dma_put);
+                       nv_wr32(priv, 0x003328, ho_put);
+               } else
+               if (ib_get != ib_put)
+                       nv_wr32(priv, 0x003334, ib_put);
+       } else {
+               nv_error(priv,
+                        "DMA_PUSHER - ch %d [%s] get 0x%08x put 0x%08x state 0x%08x (err: %s) push 0x%08x\n",
+                        chid, client_name, dma_get, dma_put, state,
+                        nv_dma_state_err(state), push);
+
+               if (dma_get != dma_put)
+                       nv_wr32(priv, 0x003244, dma_put);
+       }
+
+       nv_wr32(priv, 0x003228, 0x00000000);
+       nv_wr32(priv, 0x003220, 0x00000001);
+       nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER);
+}
+
 void
 nv04_fifo_intr(struct nouveau_subdev *subdev)
 {
@@ -416,96 +509,12 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
                get  = nv_rd32(priv, NV03_PFIFO_CACHE1_GET);
 
                if (status & NV_PFIFO_INTR_CACHE_ERROR) {
-                       uint32_t mthd, data;
-                       int ptr;
-
-                       /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before
-                        * wrapping on my G80 chips, but CACHE1 isn't big
-                        * enough for this much data.. Tests show that it
-                        * wraps around to the start at GET=0x800.. No clue
-                        * as to why..
-                        */
-                       ptr = (get & 0x7ff) >> 2;
-
-                       if (device->card_type < NV_40) {
-                               mthd = nv_rd32(priv,
-                                       NV04_PFIFO_CACHE1_METHOD(ptr));
-                               data = nv_rd32(priv,
-                                       NV04_PFIFO_CACHE1_DATA(ptr));
-                       } else {
-                               mthd = nv_rd32(priv,
-                                       NV40_PFIFO_CACHE1_METHOD(ptr));
-                               data = nv_rd32(priv,
-                                       NV40_PFIFO_CACHE1_DATA(ptr));
-                       }
-
-                       if (!nv04_fifo_swmthd(priv, chid, mthd, data)) {
-                               nv_error(priv, "CACHE_ERROR - Ch %d/%d "
-                                             "Mthd 0x%04x Data 0x%08x\n",
-                                       chid, (mthd >> 13) & 7, mthd & 0x1ffc,
-                                       data);
-                       }
-
-                       nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
-                       nv_wr32(priv, NV03_PFIFO_INTR_0,
-                                               NV_PFIFO_INTR_CACHE_ERROR);
-
-                       nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
-                               nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1);
-                       nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
-                       nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
-                               nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1);
-                       nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0);
-
-                       nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH,
-                               nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
-                       nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
-
+                       nv04_fifo_cache_error(device, priv, chid, get);
                        status &= ~NV_PFIFO_INTR_CACHE_ERROR;
                }
 
                if (status & NV_PFIFO_INTR_DMA_PUSHER) {
-                       u32 dma_get = nv_rd32(priv, 0x003244);
-                       u32 dma_put = nv_rd32(priv, 0x003240);
-                       u32 push = nv_rd32(priv, 0x003220);
-                       u32 state = nv_rd32(priv, 0x003228);
-
-                       if (device->card_type == NV_50) {
-                               u32 ho_get = nv_rd32(priv, 0x003328);
-                               u32 ho_put = nv_rd32(priv, 0x003320);
-                               u32 ib_get = nv_rd32(priv, 0x003334);
-                               u32 ib_put = nv_rd32(priv, 0x003330);
-
-                               nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%02x%08x "
-                                    "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x "
-                                    "State 0x%08x (err: %s) Push 0x%08x\n",
-                                       chid, ho_get, dma_get, ho_put,
-                                       dma_put, ib_get, ib_put, state,
-                                       nv_dma_state_err(state),
-                                       push);
-
-                               /* METHOD_COUNT, in DMA_STATE on earlier chipsets */
-                               nv_wr32(priv, 0x003364, 0x00000000);
-                               if (dma_get != dma_put || ho_get != ho_put) {
-                                       nv_wr32(priv, 0x003244, dma_put);
-                                       nv_wr32(priv, 0x003328, ho_put);
-                               } else
-                               if (ib_get != ib_put) {
-                                       nv_wr32(priv, 0x003334, ib_put);
-                               }
-                       } else {
-                               nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%08x "
-                                            "Put 0x%08x State 0x%08x (err: %s) Push 0x%08x\n",
-                                       chid, dma_get, dma_put, state,
-                                       nv_dma_state_err(state), push);
-
-                               if (dma_get != dma_put)
-                                       nv_wr32(priv, 0x003244, dma_put);
-                       }
-
-                       nv_wr32(priv, 0x003228, 0x00000000);
-                       nv_wr32(priv, 0x003220, 0x00000001);
-                       nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER);
+                       nv04_fifo_dma_pusher(device, priv, chid);
                        status &= ~NV_PFIFO_INTR_DMA_PUSHER;
                }
 
@@ -528,6 +537,12 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
                                status &= ~0x00000010;
                                nv_wr32(priv, 0x002100, 0x00000010);
                        }
+
+                       if (status & 0x40000000) {
+                               nouveau_event_trigger(priv->base.uevent, 0);
+                               nv_wr32(priv, 0x002100, 0x40000000);
+                               status &= ~0x40000000;
+                       }
                }
 
                if (status) {
index bd09636..840af61 100644 (file)
@@ -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);
index 1eb1c51..094000e 100644 (file)
@@ -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;
index b4365dd..4f226af 100644 (file)
@@ -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;
 }
 
index c930da9..4419e40 100644 (file)
@@ -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;
 }
 
index e30a9c5..ad13dcd 100644 (file)
@@ -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);
index 5c0f843..23c143a 100644 (file)
@@ -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);
index 5b20401..0607b98 100644 (file)
@@ -1,3 +1,4 @@
+#include <core/client.h>
 #include <core/os.h>
 #include <core/class.h>
 #include <core/engctx.h>
@@ -224,15 +225,17 @@ nv20_graph_intr(struct nouveau_subdev *subdev)
        nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001);
 
        if (show) {
-               nv_error(priv, "");
+               nv_error(priv, "%s", "");
                nouveau_bitfield_print(nv10_graph_intr_name, show);
-               printk(" nsource:");
+               pr_cont(" nsource:");
                nouveau_bitfield_print(nv04_graph_nsource, nsource);
-               printk(" nstatus:");
+               pr_cont(" nstatus:");
                nouveau_bitfield_print(nv10_graph_nstatus, nstatus);
-               printk("\n");
-               nv_error(priv, "ch %d/%d class 0x%04x mthd 0x%04x data 0x%08x\n",
-                       chid, subc, class, mthd, data);
+               pr_cont("\n");
+               nv_error(priv,
+                        "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                        chid, nouveau_client_name(engctx), subc, class, mthd,
+                        data);
        }
 
        nouveau_engctx_put(engctx);
index 0b36dd3..17049d5 100644 (file)
@@ -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);
index b1c3d83..f2b1a7a 100644 (file)
@@ -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) {
index 45aff5f..0de0dd7 100644 (file)
@@ -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);
index 9f82e97..61cec0f 100644 (file)
@@ -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;
        }
index 9fd8637..49ecbb8 100644 (file)
@@ -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);
index b0e7e1c..c48e749 100644 (file)
@@ -28,6 +28,9 @@
 #include <core/namedb.h>
 #include <core/handle.h>
 #include <core/gpuobj.h>
+#include <core/event.h>
+
+#include <subdev/bar.h>
 
 #include <engine/software.h>
 #include <engine/disp.h>
@@ -90,18 +93,11 @@ nv50_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
 {
        struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
        struct nouveau_disp *disp = nouveau_disp(object);
-       unsigned long flags;
        u32 crtc = *(u32 *)args;
-
        if (crtc > 1)
                return -EINVAL;
 
-       disp->vblank.get(disp->vblank.data, crtc);
-
-       spin_lock_irqsave(&disp->vblank.lock, flags);
-       list_add(&chan->base.vblank.head, &disp->vblank.list);
-       chan->base.vblank.crtc = crtc;
-       spin_unlock_irqrestore(&disp->vblank.lock, flags);
+       nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
        return 0;
 }
 
@@ -136,6 +132,29 @@ nv50_software_sclass[] = {
  ******************************************************************************/
 
 static int
+nv50_software_vblsem_release(struct nouveau_eventh *event, int head)
+{
+       struct nouveau_software_chan *chan =
+               container_of(event, struct nouveau_software_chan, vblank.event);
+       struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
+       struct nouveau_bar *bar = nouveau_bar(priv);
+
+       nv_wr32(priv, 0x001704, chan->vblank.channel);
+       nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma);
+       bar->flush(bar);
+
+       if (nv_device(priv)->chipset == 0x50) {
+               nv_wr32(priv, 0x001570, chan->vblank.offset);
+               nv_wr32(priv, 0x001574, chan->vblank.value);
+       } else {
+               nv_wr32(priv, 0x060010, chan->vblank.offset);
+               nv_wr32(priv, 0x060014, chan->vblank.value);
+       }
+
+       return NVKM_EVENT_DROP;
+}
+
+static int
 nv50_software_context_ctor(struct nouveau_object *parent,
                           struct nouveau_object *engine,
                           struct nouveau_oclass *oclass, void *data, u32 size,
@@ -150,6 +169,7 @@ nv50_software_context_ctor(struct nouveau_object *parent,
                return ret;
 
        chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
+       chan->base.vblank.event.func = nv50_software_vblsem_release;
        return 0;
 }
 
@@ -170,8 +190,8 @@ nv50_software_cclass = {
 
 static int
 nv50_software_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
+                  struct nouveau_oclass *oclass, void *data, u32 size,
+                  struct nouveau_object **pobject)
 {
        struct nv50_software_priv *priv;
        int ret;
index 282a1cd..a523eaa 100644 (file)
@@ -25,6 +25,9 @@
 #include <core/os.h>
 #include <core/class.h>
 #include <core/engctx.h>
+#include <core/event.h>
+
+#include <subdev/bar.h>
 
 #include <engine/software.h>
 #include <engine/disp.h>
@@ -72,18 +75,12 @@ nvc0_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
 {
        struct nvc0_software_chan *chan = (void *)nv_engctx(object->parent);
        struct nouveau_disp *disp = nouveau_disp(object);
-       unsigned long flags;
        u32 crtc = *(u32 *)args;
 
        if ((nv_device(object)->card_type < NV_E0 && crtc > 1) || crtc > 3)
                return -EINVAL;
 
-       disp->vblank.get(disp->vblank.data, crtc);
-
-       spin_lock_irqsave(&disp->vblank.lock, flags);
-       list_add(&chan->base.vblank.head, &disp->vblank.list);
-       chan->base.vblank.crtc = crtc;
-       spin_unlock_irqrestore(&disp->vblank.lock, flags);
+       nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
        return 0;
 }
 
@@ -118,6 +115,23 @@ nvc0_software_sclass[] = {
  ******************************************************************************/
 
 static int
+nvc0_software_vblsem_release(struct nouveau_eventh *event, int head)
+{
+       struct nouveau_software_chan *chan =
+               container_of(event, struct nouveau_software_chan, vblank.event);
+       struct nvc0_software_priv *priv = (void *)nv_object(chan)->engine;
+       struct nouveau_bar *bar = nouveau_bar(priv);
+
+       nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
+       bar->flush(bar);
+       nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset));
+       nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset));
+       nv_wr32(priv, 0x060014, chan->vblank.value);
+
+       return NVKM_EVENT_DROP;
+}
+
+static int
 nvc0_software_context_ctor(struct nouveau_object *parent,
                           struct nouveau_object *engine,
                           struct nouveau_oclass *oclass, void *data, u32 size,
@@ -132,6 +146,7 @@ nvc0_software_context_ctor(struct nouveau_object *parent,
                return ret;
 
        chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
+       chan->base.vblank.event.func = nvc0_software_vblsem_release;
        return 0;
 }
 
index 47c4b3a..92d3ab1 100644 (file)
@@ -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 {
 };
 
index 63acc03..c66eac5 100644 (file)
@@ -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
index e58b6f0..d351a4e 100644 (file)
@@ -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,
index e7b1e18..4fc62bb 100644 (file)
@@ -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 {
diff --git a/drivers/gpu/drm/nouveau/core/include/core/event.h b/drivers/gpu/drm/nouveau/core/include/core/event.h
new file mode 100644 (file)
index 0000000..9e09440
--- /dev/null
@@ -0,0 +1,36 @@
+#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
index 5982935..6a90267 100644 (file)
@@ -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);
 }
 
index 1d62966..febed2e 100644 (file)
@@ -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)                                \
index 4694828..28da677 100644 (file)
@@ -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
 
index f18846c..b46c197 100644 (file)
@@ -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
index c945691..4579948 100644 (file)
@@ -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 *);
index b79025d..123270e 100644 (file)
@@ -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;
index e6563b5..96d3364 100644 (file)
@@ -1,17 +1,22 @@
 #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;
index 5079bed..10b57a1 100644 (file)
@@ -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);
index a2c4296..083541d 100644 (file)
@@ -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 {
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/xpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/xpio.h
new file mode 100644 (file)
index 0000000..360baab
--- /dev/null
@@ -0,0 +1,19 @@
+#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
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bus.h b/drivers/gpu/drm/nouveau/core/include/subdev/bus.h
new file mode 100644 (file)
index 0000000..7d88ec4
--- /dev/null
@@ -0,0 +1,41 @@
+#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
index b75e8f1..c85b9f1 100644 (file)
@@ -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>
 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
index b93ab01..888384c 100644 (file)
 #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
index faee569..6b17b61 100644 (file)
@@ -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
index c24ec8a..e465d15 100644 (file)
@@ -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);
index cfe3b9c..eb49603 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/vmalloc.h>
 #include <linux/acpi.h>
 #include <linux/dmi.h>
+#include <linux/reboot.h>
 
 #include <asm/unaligned.h>
 
index f621f69..e816f06 100644 (file)
@@ -172,7 +172,7 @@ out:
        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
index 0fd87df..2d9b9d7 100644 (file)
@@ -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;
                }
index 5afb568..b2a676e 100644 (file)
@@ -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;
index c84e93f..172a4f9 100644 (file)
@@ -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;
index ad577db..cfb9288 100644 (file)
@@ -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;
        }
 
index 690ed43..2cc1e6a 100644 (file)
@@ -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--) {
index 862a08a..22a2057 100644 (file)
@@ -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;
                }
        }
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/xpio.c b/drivers/gpu/drm/nouveau/core/subdev/bios/xpio.c
new file mode 100644 (file)
index 0000000..e9b8e5d
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c
new file mode 100644 (file)
index 0000000..8c7f805
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c
new file mode 100644 (file)
index 0000000..34132ae
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c
new file mode 100644 (file)
index 0000000..f5b2117
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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 nv50_bus_priv {
+       struct nouveau_bus base;
+};
+
+static void
+nv50_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 & 0x00000008) {
+               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 & 0x00010000) {
+               subdev = nouveau_subdev(pbus, NVDEV_SUBDEV_THERM);
+               if (subdev && subdev->intr)
+                       subdev->intr(subdev);
+               stat &= ~0x00010000;
+               nv_wr32(pbus, 0x001100, 0x00010000);
+       }
+
+       if (stat) {
+               nv_error(pbus, "unknown intr 0x%08x\n", stat);
+               nv_mask(pbus, 0x001140, stat, 0);
+       }
+}
+
+static int
+nv50_bus_init(struct nouveau_object *object)
+{
+       struct nv50_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, 0x00010008);
+       return 0;
+}
+
+static int
+nv50_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv50_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 = nv50_bus_intr;
+       return 0;
+}
+
+struct nouveau_oclass
+nv50_bus_oclass = {
+       .handle = NV_SUBDEV(BUS, 0x50),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_bus_ctor,
+               .dtor = _nouveau_bus_dtor,
+               .init = nv50_bus_init,
+               .fini = _nouveau_bus_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c
new file mode 100644 (file)
index 0000000..b192d62
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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 nvc0_bus_priv {
+       struct nouveau_bus base;
+};
+
+static void
+nvc0_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 & 0x0000000e) {
+               u32 addr = nv_rd32(pbus, 0x009084);
+               u32 data = nv_rd32(pbus, 0x009088);
+
+               nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x [ %s%s%s]\n",
+                        (addr & 0x00000002) ? "write" : "read", data,
+                        (addr & 0x00fffffc),
+                        (stat & 0x00000002) ? "!ENGINE " : "",
+                        (stat & 0x00000004) ? "IBUS " : "",
+                        (stat & 0x00000008) ? "TIMEOUT " : "");
+
+               nv_wr32(pbus, 0x009084, 0x00000000);
+               nv_wr32(pbus, 0x001100, (stat & 0x0000000e));
+               stat &= ~0x0000000e;
+       }
+
+       if (stat) {
+               nv_error(pbus, "unknown intr 0x%08x\n", stat);
+               nv_mask(pbus, 0x001140, stat, 0x00000000);
+       }
+}
+
+static int
+nvc0_bus_init(struct nouveau_object *object)
+{
+       struct nvc0_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, 0x0000000e);
+       return 0;
+}
+
+static int
+nvc0_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nvc0_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 = nvc0_bus_intr;
+       return 0;
+}
+
+struct nouveau_oclass
+nvc0_bus_oclass = {
+       .handle = NV_SUBDEV(BUS, 0xc0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_bus_ctor,
+               .dtor = _nouveau_bus_dtor,
+               .init = nvc0_bus_init,
+               .fini = _nouveau_bus_fini,
+       },
+};
index f8a7ed4..3937ced 100644 (file)
@@ -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;
 
index 8626d0d..473c5c0 100644 (file)
@@ -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;
index 9c40b0f..d0774f5 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -48,10 +49,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV10";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv10_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     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -64,10 +66,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV15";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv10_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     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -82,10 +85,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV16";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv10_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     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -100,10 +104,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "nForce";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv1a_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     ] = &nv1a_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -118,10 +123,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV11";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv10_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     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -136,10 +142,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV17";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv10_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     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -154,10 +161,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "nForce2";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv1a_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     ] = &nv1a_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -172,10 +180,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV18";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv10_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     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
index 74f88f4..ab920e0 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -49,10 +50,11 @@ nv20_identify(struct nouveau_device *device)
                device->cname = "NV20";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv20_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     ] = &nv20_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -67,10 +69,11 @@ nv20_identify(struct nouveau_device *device)
                device->cname = "NV25";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv20_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     ] = &nv25_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -85,10 +88,11 @@ nv20_identify(struct nouveau_device *device)
                device->cname = "NV28";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv20_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     ] = &nv25_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -103,10 +107,11 @@ nv20_identify(struct nouveau_device *device)
                device->cname = "NV2A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv20_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     ] = &nv25_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
index 0ac1b2c..5f21102 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -49,10 +50,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV30";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv20_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     ] = &nv30_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -67,10 +69,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV35";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv20_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     ] = &nv35_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -85,10 +88,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV31";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -104,10 +108,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV36";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv36_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -123,10 +128,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV34";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_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] = &nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
index 41d5968..f3d55ef 100644 (file)
@@ -24,6 +24,8 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
+#include <subdev/vm.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -50,11 +52,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV40";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -70,11 +73,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV41";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv41_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -90,11 +94,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV42";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv41_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -110,11 +115,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV43";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv41_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -130,11 +136,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV45";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -150,11 +157,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "G70";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv47_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -170,11 +178,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "G71";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv49_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -190,11 +199,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "G73";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv49_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -210,11 +220,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV44";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv44_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -230,11 +241,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "G72";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -250,11 +262,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV44A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv44_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -270,11 +283,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C61";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -290,11 +304,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C51";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv4e_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv4e_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -310,11 +325,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C73";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -330,11 +346,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C67";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -350,11 +367,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C68";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
index 6ccfd85..5ed2fa5 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -57,12 +58,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G80";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -79,12 +81,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G84";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -104,12 +107,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G86";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -129,12 +133,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G92";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -154,12 +159,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G94";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -179,12 +185,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G96";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -204,12 +211,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G98";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -229,12 +237,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G200";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -254,12 +263,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "MCP77/MCP78";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -279,12 +289,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "MCP79/MCP7A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -304,12 +315,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "GT215";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -330,12 +342,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "GT216";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -355,12 +368,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "GT218";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -380,12 +394,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "MCP89";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
index f046168..4393eb4 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -57,12 +58,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF100";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -85,12 +87,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF104";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -113,12 +116,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF106";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -141,12 +145,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF114";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -169,12 +174,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF116";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -197,12 +203,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF108";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -225,12 +232,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF110";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -253,12 +261,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF119";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -282,4 +291,4 @@ nvc0_identify(struct nouveau_device *device)
        }
 
        return 0;
-}
+       }
index 03a6528..5c12391 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -56,13 +57,14 @@ nve0_identify(struct nouveau_device *device)
        case 0xe4:
                device->cname = "GK104";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -84,13 +86,14 @@ nve0_identify(struct nouveau_device *device)
        case 0xe7:
                device->cname = "GK107";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -112,13 +115,14 @@ nve0_identify(struct nouveau_device *device)
        case 0xe6:
                device->cname = "GK106";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
index ae7249b..4a85778 100644 (file)
@@ -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;
 }
index 487cb8c..a4338d9 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <core/object.h>
+#include <core/client.h>
 #include <core/enum.h>
+#include <core/engctx.h>
+#include <core/object.h>
 
 #include <subdev/fb.h>
 #include <subdev/bios.h>
@@ -302,17 +304,18 @@ static const struct nouveau_enum vm_client[] = {
 };
 
 static const struct nouveau_enum vm_engine[] = {
-       { 0x00000000, "PGRAPH", NULL },
-       { 0x00000001, "PVP", NULL },
+       { 0x00000000, "PGRAPH", NULL, NVDEV_ENGINE_GR },
+       { 0x00000001, "PVP", NULL, NVDEV_ENGINE_VP },
        { 0x00000004, "PEEPHOLE", NULL },
-       { 0x00000005, "PFIFO", vm_pfifo_subclients },
+       { 0x00000005, "PFIFO", vm_pfifo_subclients, NVDEV_ENGINE_FIFO },
        { 0x00000006, "BAR", vm_bar_subclients },
-       { 0x00000008, "PPPP", NULL },
-       { 0x00000009, "PBSP", NULL },
-       { 0x0000000a, "PCRYPT", NULL },
+       { 0x00000008, "PPPP", NULL, NVDEV_ENGINE_PPP },
+       { 0x00000008, "PMPEG", NULL, NVDEV_ENGINE_MPEG },
+       { 0x00000009, "PBSP", NULL, NVDEV_ENGINE_BSP },
+       { 0x0000000a, "PCRYPT", NULL, NVDEV_ENGINE_CRYPT },
        { 0x0000000b, "PCOUNTER", NULL },
        { 0x0000000c, "SEMAPHORE_BG", NULL },
-       { 0x0000000d, "PCOPY", NULL },
+       { 0x0000000d, "PCOPY", NULL, NVDEV_ENGINE_COPY0 },
        { 0x0000000e, "PDAEMON", NULL },
        {}
 };
@@ -334,8 +337,10 @@ static void
 nv50_fb_intr(struct nouveau_subdev *subdev)
 {
        struct nouveau_device *device = nv_device(subdev);
+       struct nouveau_engine *engine;
        struct nv50_fb_priv *priv = (void *)subdev;
        const struct nouveau_enum *en, *cl;
+       struct nouveau_object *engctx = NULL;
        u32 trap[6], idx, chan;
        u8 st0, st1, st2, st3;
        int i;
@@ -366,36 +371,55 @@ nv50_fb_intr(struct nouveau_subdev *subdev)
        }
        chan = (trap[2] << 16) | trap[1];
 
-       nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x ",
+       en = nouveau_enum_find(vm_engine, st0);
+
+       if (en && en->data2) {
+               const struct nouveau_enum *orig_en = en;
+               while (en->name && en->value == st0 && en->data2) {
+                       engine = nouveau_engine(subdev, en->data2);
+                       if (engine) {
+                               engctx = nouveau_engctx_get(engine, chan);
+                               if (engctx)
+                                       break;
+                       }
+                       en++;
+               }
+               if (!engctx)
+                       en = orig_en;
+       }
+
+       nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x [%s] ",
                 (trap[5] & 0x00000100) ? "read" : "write",
-                trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan);
+                trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan,
+                nouveau_client_name(engctx));
+
+       nouveau_engctx_put(engctx);
 
-       en = nouveau_enum_find(vm_engine, st0);
        if (en)
-               printk("%s/", en->name);
+               pr_cont("%s/", en->name);
        else
-               printk("%02x/", st0);
+               pr_cont("%02x/", st0);
 
        cl = nouveau_enum_find(vm_client, st2);
        if (cl)
-               printk("%s/", cl->name);
+               pr_cont("%s/", cl->name);
        else
-               printk("%02x/", st2);
+               pr_cont("%02x/", st2);
 
        if      (cl && cl->data) cl = nouveau_enum_find(cl->data, st3);
        else if (en && en->data) cl = nouveau_enum_find(en->data, st3);
        else                     cl = NULL;
        if (cl)
-               printk("%s", cl->name);
+               pr_cont("%s", cl->name);
        else
-               printk("%02x", st3);
+               pr_cont("%02x", st3);
 
-       printk(" reason: ");
+       pr_cont(" reason: ");
        en = nouveau_enum_find(vm_fault, st1);
        if (en)
-               printk("%s\n", en->name);
+               pr_cont("%s\n", en->name);
        else
-               printk("0x%08x\n", st1);
+               pr_cont("0x%08x\n", st1);
 }
 
 static int
index 9fb0f9b..d422acc 100644 (file)
@@ -102,135 +102,19 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
        return ret;
 }
 
-static int
-nouveau_gpio_irq(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, bool on)
-{
-       struct dcb_gpio_func func;
-       int ret;
-
-       ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
-       if (ret == 0) {
-               if (idx == 0 && gpio->irq_enable)
-                       gpio->irq_enable(gpio, func.line, on);
-               else
-                       ret = -ENODEV;
-       }
-
-       return ret;
-}
-
-struct gpio_isr {
-       struct nouveau_gpio *gpio;
-       struct list_head head;
-       struct work_struct work;
-       int idx;
-       struct dcb_gpio_func func;
-       void (*handler)(void *, int);
-       void *data;
-       bool inhibit;
-};
-
-static void
-nouveau_gpio_isr_bh(struct work_struct *work)
-{
-       struct gpio_isr *isr = container_of(work, struct gpio_isr, work);
-       struct nouveau_gpio *gpio = isr->gpio;
-       unsigned long flags;
-       int state;
-
-       state = nouveau_gpio_get(gpio, isr->idx, isr->func.func,
-                                                isr->func.line);
-       if (state >= 0)
-               isr->handler(isr->data, state);
-
-       spin_lock_irqsave(&gpio->lock, flags);
-       isr->inhibit = false;
-       spin_unlock_irqrestore(&gpio->lock, flags);
-}
-
-static void
-nouveau_gpio_isr_run(struct nouveau_gpio *gpio, int idx, u32 line_mask)
-{
-       struct gpio_isr *isr;
-
-       if (idx != 0)
-               return;
-
-       spin_lock(&gpio->lock);
-       list_for_each_entry(isr, &gpio->isr, head) {
-               if (line_mask & (1 << isr->func.line)) {
-                       if (isr->inhibit)
-                               continue;
-                       isr->inhibit = true;
-                       schedule_work(&isr->work);
-               }
-       }
-       spin_unlock(&gpio->lock);
-}
-
-static int
-nouveau_gpio_isr_add(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
-                    void (*handler)(void *, int), void *data)
-{
-       struct gpio_isr *isr;
-       unsigned long flags;
-       int ret;
-
-       isr = kzalloc(sizeof(*isr), GFP_KERNEL);
-       if (!isr)
-               return -ENOMEM;
-
-       ret = nouveau_gpio_find(gpio, idx, tag, line, &isr->func);
-       if (ret) {
-               kfree(isr);
-               return ret;
-       }
-
-       INIT_WORK(&isr->work, nouveau_gpio_isr_bh);
-       isr->gpio = gpio;
-       isr->handler = handler;
-       isr->data = data;
-       isr->idx = idx;
-
-       spin_lock_irqsave(&gpio->lock, flags);
-       list_add(&isr->head, &gpio->isr);
-       spin_unlock_irqrestore(&gpio->lock, flags);
-       return 0;
-}
-
-static void
-nouveau_gpio_isr_del(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
-                    void (*handler)(void *, int), void *data)
+void
+_nouveau_gpio_dtor(struct nouveau_object *object)
 {
-       struct gpio_isr *isr, *tmp;
-       struct dcb_gpio_func func;
-       unsigned long flags;
-       LIST_HEAD(tofree);
-       int ret;
-
-       ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
-       if (ret == 0) {
-               spin_lock_irqsave(&gpio->lock, flags);
-               list_for_each_entry_safe(isr, tmp, &gpio->isr, head) {
-                       if (memcmp(&isr->func, &func, sizeof(func)) ||
-                           isr->idx != idx ||
-                           isr->handler != handler || isr->data != data)
-                               continue;
-                       list_move_tail(&isr->head, &tofree);
-               }
-               spin_unlock_irqrestore(&gpio->lock, flags);
-
-               list_for_each_entry_safe(isr, tmp, &tofree, head) {
-                       flush_work(&isr->work);
-                       kfree(isr);
-               }
-       }
+       struct nouveau_gpio *gpio = (void *)object;
+       nouveau_event_destroy(&gpio->events);
+       nouveau_subdev_destroy(&gpio->base);
 }
 
 int
 nouveau_gpio_create_(struct nouveau_object *parent,
                     struct nouveau_object *engine,
-                    struct nouveau_oclass *oclass, int length, void **pobject)
+                    struct nouveau_oclass *oclass, int lines,
+                    int length, void **pobject)
 {
        struct nouveau_gpio *gpio;
        int ret;
@@ -241,15 +125,13 @@ nouveau_gpio_create_(struct nouveau_object *parent,
        if (ret)
                return ret;
 
+       ret = nouveau_event_create(lines, &gpio->events);
+       if (ret)
+               return ret;
+
        gpio->find = nouveau_gpio_find;
        gpio->set  = nouveau_gpio_set;
        gpio->get  = nouveau_gpio_get;
-       gpio->irq  = nouveau_gpio_irq;
-       gpio->isr_run = nouveau_gpio_isr_run;
-       gpio->isr_add = nouveau_gpio_isr_add;
-       gpio->isr_del = nouveau_gpio_isr_del;
-       INIT_LIST_HEAD(&gpio->isr);
-       spin_lock_init(&gpio->lock);
        return 0;
 }
 
index 168d16a..76d5d54 100644 (file)
@@ -24,7 +24,7 @@
  *
  */
 
-#include <subdev/gpio.h>
+#include "priv.h"
 
 struct nv10_gpio_priv {
        struct nouveau_gpio base;
@@ -83,27 +83,36 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
 }
 
 static void
-nv10_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on)
-{
-       u32 mask = 0x00010001 << line;
-
-       nv_wr32(gpio, 0x001104, mask);
-       nv_mask(gpio, 0x001144, mask, on ? mask : 0);
-}
-
-static void
 nv10_gpio_intr(struct nouveau_subdev *subdev)
 {
        struct nv10_gpio_priv *priv = (void *)subdev;
        u32 intr = nv_rd32(priv, 0x001104);
        u32 hi = (intr & 0x0000ffff) >> 0;
        u32 lo = (intr & 0xffff0000) >> 16;
+       int i;
 
-       priv->base.isr_run(&priv->base, 0, hi | lo);
+       for (i = 0; (hi | lo) && i < 32; i++) {
+               if ((hi | lo) & (1 << i))
+                       nouveau_event_trigger(priv->base.events, i);
+       }
 
        nv_wr32(priv, 0x001104, intr);
 }
 
+static void
+nv10_gpio_intr_enable(struct nouveau_event *event, int line)
+{
+       nv_wr32(event->priv, 0x001104, 0x00010001 << line);
+       nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00010001 << line);
+}
+
+static void
+nv10_gpio_intr_disable(struct nouveau_event *event, int line)
+{
+       nv_wr32(event->priv, 0x001104, 0x00010001 << line);
+       nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00000000);
+}
+
 static int
 nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -112,14 +121,16 @@ nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv10_gpio_priv *priv;
        int ret;
 
-       ret = nouveau_gpio_create(parent, engine, oclass, &priv);
+       ret = nouveau_gpio_create(parent, engine, oclass, 16, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
        priv->base.drive = nv10_gpio_drive;
        priv->base.sense = nv10_gpio_sense;
-       priv->base.irq_enable = nv10_gpio_irq_enable;
+       priv->base.events->priv = priv;
+       priv->base.events->enable = nv10_gpio_intr_enable;
+       priv->base.events->disable = nv10_gpio_intr_disable;
        nv_subdev(priv)->intr = nv10_gpio_intr;
        return 0;
 }
@@ -141,8 +152,6 @@ nv10_gpio_init(struct nouveau_object *object)
        if (ret)
                return ret;
 
-       nv_wr32(priv, 0x001140, 0x00000000);
-       nv_wr32(priv, 0x001100, 0xffffffff);
        nv_wr32(priv, 0x001144, 0x00000000);
        nv_wr32(priv, 0x001104, 0xffffffff);
        return 0;
@@ -152,7 +161,6 @@ static int
 nv10_gpio_fini(struct nouveau_object *object, bool suspend)
 {
        struct nv10_gpio_priv *priv = (void *)object;
-       nv_wr32(priv, 0x001140, 0x00000000);
        nv_wr32(priv, 0x001144, 0x00000000);
        return nouveau_gpio_fini(&priv->base, suspend);
 }
index bf13a12..bf489dc 100644 (file)
@@ -22,7 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include <subdev/gpio.h>
+#include "priv.h"
 
 struct nv50_gpio_priv {
        struct nouveau_gpio base;
@@ -95,21 +95,12 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line)
 }
 
 void
-nv50_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on)
-{
-       u32 reg  = line < 16 ? 0xe050 : 0xe070;
-       u32 mask = 0x00010001 << (line & 0xf);
-
-       nv_wr32(gpio, reg + 4, mask);
-       nv_mask(gpio, reg + 0, mask, on ? mask : 0);
-}
-
-void
 nv50_gpio_intr(struct nouveau_subdev *subdev)
 {
        struct nv50_gpio_priv *priv = (void *)subdev;
        u32 intr0, intr1 = 0;
        u32 hi, lo;
+       int i;
 
        intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050);
        if (nv_device(priv)->chipset >= 0x90)
@@ -117,13 +108,35 @@ nv50_gpio_intr(struct nouveau_subdev *subdev)
 
        hi = (intr0 & 0x0000ffff) | (intr1 << 16);
        lo = (intr0 >> 16) | (intr1 & 0xffff0000);
-       priv->base.isr_run(&priv->base, 0, hi | lo);
+
+       for (i = 0; (hi | lo) && i < 32; i++) {
+               if ((hi | lo) & (1 << i))
+                       nouveau_event_trigger(priv->base.events, i);
+       }
 
        nv_wr32(priv, 0xe054, intr0);
        if (nv_device(priv)->chipset >= 0x90)
                nv_wr32(priv, 0xe074, intr1);
 }
 
+void
+nv50_gpio_intr_enable(struct nouveau_event *event, int line)
+{
+       const u32 addr = line < 16 ? 0xe050 : 0xe070;
+       const u32 mask = 0x00010001 << (line & 0xf);
+       nv_wr32(event->priv, addr + 0x04, mask);
+       nv_mask(event->priv, addr + 0x00, mask, mask);
+}
+
+void
+nv50_gpio_intr_disable(struct nouveau_event *event, int line)
+{
+       const u32 addr = line < 16 ? 0xe050 : 0xe070;
+       const u32 mask = 0x00010001 << (line & 0xf);
+       nv_wr32(event->priv, addr + 0x04, mask);
+       nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
+}
+
 static int
 nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -132,7 +145,9 @@ nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_gpio_priv *priv;
        int ret;
 
-       ret = nouveau_gpio_create(parent, engine, oclass, &priv);
+       ret = nouveau_gpio_create(parent, engine, oclass,
+                                 nv_device(parent)->chipset >= 0x90 ? 32 : 16,
+                                 &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -140,7 +155,9 @@ nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->base.reset = nv50_gpio_reset;
        priv->base.drive = nv50_gpio_drive;
        priv->base.sense = nv50_gpio_sense;
-       priv->base.irq_enable = nv50_gpio_irq_enable;
+       priv->base.events->priv = priv;
+       priv->base.events->enable = nv50_gpio_intr_enable;
+       priv->base.events->disable = nv50_gpio_intr_disable;
        nv_subdev(priv)->intr = nv50_gpio_intr;
        return 0;
 }
index 83e8b8f..010431e 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/gpio.h>
+#include "priv.h"
 
 struct nvd0_gpio_priv {
        struct nouveau_gpio base;
 };
 
-static void
+void
 nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match)
 {
        struct nouveau_bios *bios = nouveau_bios(gpio);
@@ -57,7 +57,7 @@ nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match)
        }
 }
 
-static int
+int
 nvd0_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
 {
        u32 data = ((dir ^ 1) << 13) | (out << 12);
@@ -66,7 +66,7 @@ nvd0_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
        return 0;
 }
 
-static int
+int
 nvd0_gpio_sense(struct nouveau_gpio *gpio, int line)
 {
        return !!(nv_rd32(gpio, 0x00d610 + (line * 4)) & 0x00004000);
@@ -80,7 +80,7 @@ nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nvd0_gpio_priv *priv;
        int ret;
 
-       ret = nouveau_gpio_create(parent, engine, oclass, &priv);
+       ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -88,7 +88,9 @@ nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->base.reset = nvd0_gpio_reset;
        priv->base.drive = nvd0_gpio_drive;
        priv->base.sense = nvd0_gpio_sense;
-       priv->base.irq_enable = nv50_gpio_irq_enable;
+       priv->base.events->priv = priv;
+       priv->base.events->enable = nv50_gpio_intr_enable;
+       priv->base.events->disable = nv50_gpio_intr_disable;
        nv_subdev(priv)->intr = nv50_gpio_intr;
        return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c
new file mode 100644 (file)
index 0000000..16b8c5b
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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 "priv.h"
+
+struct nve0_gpio_priv {
+       struct nouveau_gpio base;
+};
+
+void
+nve0_gpio_intr(struct nouveau_subdev *subdev)
+{
+       struct nve0_gpio_priv *priv = (void *)subdev;
+       u32 intr0 = nv_rd32(priv, 0xdc00) & nv_rd32(priv, 0xdc08);
+       u32 intr1 = nv_rd32(priv, 0xdc80) & nv_rd32(priv, 0xdc88);
+       u32 hi = (intr0 & 0x0000ffff) | (intr1 << 16);
+       u32 lo = (intr0 >> 16) | (intr1 & 0xffff0000);
+       int i;
+
+       for (i = 0; (hi | lo) && i < 32; i++) {
+               if ((hi | lo) & (1 << i))
+                       nouveau_event_trigger(priv->base.events, i);
+       }
+
+       nv_wr32(priv, 0xdc00, intr0);
+       nv_wr32(priv, 0xdc88, intr1);
+}
+
+void
+nve0_gpio_intr_enable(struct nouveau_event *event, int line)
+{
+       const u32 addr = line < 16 ? 0xdc00 : 0xdc80;
+       const u32 mask = 0x00010001 << (line & 0xf);
+       nv_wr32(event->priv, addr + 0x08, mask);
+       nv_mask(event->priv, addr + 0x00, mask, mask);
+}
+
+void
+nve0_gpio_intr_disable(struct nouveau_event *event, int line)
+{
+       const u32 addr = line < 16 ? 0xdc00 : 0xdc80;
+       const u32 mask = 0x00010001 << (line & 0xf);
+       nv_wr32(event->priv, addr + 0x08, mask);
+       nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
+}
+
+int
+nve0_gpio_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nve0_gpio_priv *priv = (void *)object;
+       nv_wr32(priv, 0xdc08, 0x00000000);
+       nv_wr32(priv, 0xdc88, 0x00000000);
+       return nouveau_gpio_fini(&priv->base, suspend);
+}
+
+int
+nve0_gpio_init(struct nouveau_object *object)
+{
+       struct nve0_gpio_priv *priv = (void *)object;
+       int ret;
+
+       ret = nouveau_gpio_init(&priv->base);
+       if (ret)
+               return ret;
+
+       nv_wr32(priv, 0xdc00, 0xffffffff);
+       nv_wr32(priv, 0xdc80, 0xffffffff);
+       return 0;
+}
+
+void
+nve0_gpio_dtor(struct nouveau_object *object)
+{
+       struct nve0_gpio_priv *priv = (void *)object;
+       nouveau_gpio_destroy(&priv->base);
+}
+
+static int
+nve0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+              struct nouveau_oclass *oclass, void *data, u32 size,
+              struct nouveau_object **pobject)
+{
+       struct nve0_gpio_priv *priv;
+       int ret;
+
+       ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.reset = nvd0_gpio_reset;
+       priv->base.drive = nvd0_gpio_drive;
+       priv->base.sense = nvd0_gpio_sense;
+       priv->base.events->priv = priv;
+       priv->base.events->enable = nve0_gpio_intr_enable;
+       priv->base.events->disable = nve0_gpio_intr_disable;
+       nv_subdev(priv)->intr = nve0_gpio_intr;
+       return 0;
+}
+
+struct nouveau_oclass
+nve0_gpio_oclass = {
+       .handle = NV_SUBDEV(GPIO, 0xe0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nve0_gpio_ctor,
+               .dtor = nv50_gpio_dtor,
+               .init = nve0_gpio_init,
+               .fini = nve0_gpio_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h
new file mode 100644 (file)
index 0000000..2ee1c89
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __NVKM_GPIO_H__
+#define __NVKM_GPIO_H__
+
+#include <subdev/gpio.h>
+
+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_intr_enable(struct nouveau_event *, int line);
+void nv50_gpio_intr_disable(struct nouveau_event *, int line);
+
+void nvd0_gpio_reset(struct nouveau_gpio *, u8);
+int  nvd0_gpio_drive(struct nouveau_gpio *, int, int, int);
+int  nvd0_gpio_sense(struct nouveau_gpio *, int);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c
new file mode 100644 (file)
index 0000000..dec94e9
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * 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 <bskeggs@redhat.com>
+ */
+
+#include <subdev/i2c.h>
+
+struct anx9805_i2c_port {
+       struct nouveau_i2c_port base;
+       u32 addr;
+       u32 ctrl;
+};
+
+static int
+anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh)
+{
+       struct anx9805_i2c_port *chan = (void *)port;
+       struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent;
+       u8 tmp, i;
+
+       nv_wri2cr(mast, chan->addr, 0xa0, link_bw);
+       nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00));
+       nv_wri2cr(mast, chan->addr, 0xa2, 0x01);
+       nv_wri2cr(mast, chan->addr, 0xa8, 0x01);
+
+       i = 0;
+       while ((tmp = nv_rdi2cr(mast, chan->addr, 0xa8)) & 0x01) {
+               mdelay(5);
+               if (i++ == 100) {
+                       nv_error(port, "link training timed out\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
+       if (tmp & 0x70) {
+               nv_error(port, "link training failed: 0x%02x\n", tmp);
+               return -EIO;
+       }
+
+       return 1;
+}
+
+static int
+anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size)
+{
+       struct anx9805_i2c_port *chan = (void *)port;
+       struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent;
+       int i, ret = -ETIMEDOUT;
+       u8 tmp;
+
+       tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04;
+       nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04);
+       nv_wri2cr(mast, chan->ctrl, 0x07, tmp);
+       nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
+
+       nv_wri2cr(mast, chan->addr, 0xe4, 0x80);
+       for (i = 0; !(type & 1) && i < size; i++)
+               nv_wri2cr(mast, chan->addr, 0xf0 + i, data[i]);
+       nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type);
+       nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >>  0);
+       nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >>  8);
+       nv_wri2cr(mast, chan->addr, 0xe8, (addr & 0xf0000) >> 16);
+       nv_wri2cr(mast, chan->addr, 0xe9, 0x01);
+
+       i = 0;
+       while ((tmp = nv_rdi2cr(mast, chan->addr, 0xe9)) & 0x01) {
+               mdelay(5);
+               if (i++ == 32)
+                       goto done;
+       }
+
+       if ((tmp = nv_rdi2cr(mast, chan->ctrl, 0xf7)) & 0x01) {
+               ret = -EIO;
+               goto done;
+       }
+
+       for (i = 0; (type & 1) && i < size; i++)
+               data[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i);
+       ret = 0;
+done:
+       nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
+       return ret;
+}
+
+static const struct nouveau_i2c_func
+anx9805_aux_func = {
+       .aux = anx9805_aux,
+       .lnk_ctl = anx9805_train,
+};
+
+static int
+anx9805_aux_chan_ctor(struct nouveau_object *parent,
+                     struct nouveau_object *engine,
+                     struct nouveau_oclass *oclass, void *data, u32 index,
+                     struct nouveau_object **pobject)
+{
+       struct nouveau_i2c_port *mast = (void *)parent;
+       struct anx9805_i2c_port *chan;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_aux_algo, &chan);
+       *pobject = nv_object(chan);
+       if (ret)
+               return ret;
+
+       switch ((oclass->handle & 0xff00) >> 8) {
+       case 0x0d:
+               chan->addr = 0x38;
+               chan->ctrl = 0x39;
+               break;
+       case 0x0e:
+               chan->addr = 0x3c;
+               chan->ctrl = 0x3b;
+               break;
+       default:
+               BUG_ON(1);
+       }
+
+       if (mast->adapter.algo == &i2c_bit_algo) {
+               struct i2c_algo_bit_data *algo = mast->adapter.algo_data;
+               algo->udelay = max(algo->udelay, 40);
+       }
+
+       chan->base.func = &anx9805_aux_func;
+       return 0;
+}
+
+static struct nouveau_ofuncs
+anx9805_aux_ofuncs = {
+       .ctor =  anx9805_aux_chan_ctor,
+       .dtor = _nouveau_i2c_port_dtor,
+       .init = _nouveau_i2c_port_init,
+       .fini = _nouveau_i2c_port_fini,
+};
+
+static int
+anx9805_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       struct anx9805_i2c_port *port = adap->algo_data;
+       struct nouveau_i2c_port *mast = (void *)nv_object(port)->parent;
+       struct i2c_msg *msg = msgs;
+       int ret = -ETIMEDOUT;
+       int i, j, cnt = num;
+       u8 seg = 0x00, off = 0x00, tmp;
+
+       tmp = nv_rdi2cr(mast, port->ctrl, 0x07) & ~0x10;
+       nv_wri2cr(mast, port->ctrl, 0x07, tmp | 0x10);
+       nv_wri2cr(mast, port->ctrl, 0x07, tmp);
+       nv_wri2cr(mast, port->addr, 0x43, 0x05);
+       mdelay(5);
+
+       while (cnt--) {
+               if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) {
+                       nv_wri2cr(mast, port->addr, 0x40, msg->addr << 1);
+                       nv_wri2cr(mast, port->addr, 0x41, seg);
+                       nv_wri2cr(mast, port->addr, 0x42, off);
+                       nv_wri2cr(mast, port->addr, 0x44, msg->len);
+                       nv_wri2cr(mast, port->addr, 0x45, 0x00);
+                       nv_wri2cr(mast, port->addr, 0x43, 0x01);
+                       for (i = 0; i < msg->len; i++) {
+                               j = 0;
+                               while (nv_rdi2cr(mast, port->addr, 0x46) & 0x10) {
+                                       mdelay(5);
+                                       if (j++ == 32)
+                                               goto done;
+                               }
+                               msg->buf[i] = nv_rdi2cr(mast, port->addr, 0x47);
+                       }
+               } else
+               if (!(msg->flags & I2C_M_RD)) {
+                       if (msg->addr == 0x50 && msg->len == 0x01) {
+                               off = msg->buf[0];
+                       } else
+                       if (msg->addr == 0x30 && msg->len == 0x01) {
+                               seg = msg->buf[0];
+                       } else
+                               goto done;
+               } else {
+                       goto done;
+               }
+               msg++;
+       }
+
+       ret = num;
+done:
+       nv_wri2cr(mast, port->addr, 0x43, 0x00);
+       return ret;
+}
+
+static u32
+anx9805_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm
+anx9805_i2c_algo = {
+       .master_xfer = anx9805_xfer,
+       .functionality = anx9805_func
+};
+
+static const struct nouveau_i2c_func
+anx9805_i2c_func = {
+};
+
+static int
+anx9805_ddc_port_ctor(struct nouveau_object *parent,
+                     struct nouveau_object *engine,
+                     struct nouveau_oclass *oclass, void *data, u32 index,
+                     struct nouveau_object **pobject)
+{
+       struct nouveau_i2c_port *mast = (void *)parent;
+       struct anx9805_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &anx9805_i2c_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       switch ((oclass->handle & 0xff00) >> 8) {
+       case 0x0d:
+               port->addr = 0x3d;
+               port->ctrl = 0x39;
+               break;
+       case 0x0e:
+               port->addr = 0x3f;
+               port->ctrl = 0x3b;
+               break;
+       default:
+               BUG_ON(1);
+       }
+
+       if (mast->adapter.algo == &i2c_bit_algo) {
+               struct i2c_algo_bit_data *algo = mast->adapter.algo_data;
+               algo->udelay = max(algo->udelay, 40);
+       }
+
+       port->base.func = &anx9805_i2c_func;
+       return 0;
+}
+
+static struct nouveau_ofuncs
+anx9805_ddc_ofuncs = {
+       .ctor =  anx9805_ddc_port_ctor,
+       .dtor = _nouveau_i2c_port_dtor,
+       .init = _nouveau_i2c_port_init,
+       .fini = _nouveau_i2c_port_fini,
+};
+
+struct nouveau_oclass
+nouveau_anx9805_sclass[] = {
+       { .handle = NV_I2C_TYPE_EXTDDC(0x0d), .ofuncs = &anx9805_ddc_ofuncs },
+       { .handle = NV_I2C_TYPE_EXTAUX(0x0d), .ofuncs = &anx9805_aux_ofuncs },
+       { .handle = NV_I2C_TYPE_EXTDDC(0x0e), .ofuncs = &anx9805_ddc_ofuncs },
+       { .handle = NV_I2C_TYPE_EXTAUX(0x0e), .ofuncs = &anx9805_aux_ofuncs },
+       {}
+};
index dc27e79..5de074a 100644 (file)
 
 #include <subdev/i2c.h>
 
-/******************************************************************************
- * aux channel util functions
- *****************************************************************************/
-#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
-#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
-
-static void
-auxch_fini(struct nouveau_i2c *aux, int ch)
-{
-       nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
-}
-
-static int
-auxch_init(struct nouveau_i2c *aux, int ch)
-{
-       const u32 unksel = 1; /* nfi which to use, or if it matters.. */
-       const u32 ureq = unksel ? 0x00100000 : 0x00200000;
-       const u32 urep = unksel ? 0x01000000 : 0x02000000;
-       u32 ctrl, timeout;
-
-       /* wait up to 1ms for any previous transaction to be done... */
-       timeout = 1000;
-       do {
-               ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
-               udelay(1);
-               if (!timeout--) {
-                       AUX_ERR("begin idle timeout 0x%08x\n", ctrl);
-                       return -EBUSY;
-               }
-       } while (ctrl & 0x03010000);
-
-       /* set some magic, and wait up to 1ms for it to appear */
-       nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
-       timeout = 1000;
-       do {
-               ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
-               udelay(1);
-               if (!timeout--) {
-                       AUX_ERR("magic wait 0x%08x\n", ctrl);
-                       auxch_fini(aux, ch);
-                       return -EBUSY;
-               }
-       } while ((ctrl & 0x03000000) != urep);
-
-       return 0;
-}
-
-static int
-auxch_tx(struct nouveau_i2c *aux, int ch, u8 type, u32 addr, u8 *data, u8 size)
-{
-       u32 ctrl, stat, timeout, retries;
-       u32 xbuf[4] = {};
-       int ret, i;
-
-       AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
-
-       ret = auxch_init(aux, ch);
-       if (ret)
-               goto out;
-
-       stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50));
-       if (!(stat & 0x10000000)) {
-               AUX_DBG("sink not detected\n");
-               ret = -ENXIO;
-               goto out;
-       }
-
-       if (!(type & 1)) {
-               memcpy(xbuf, data, size);
-               for (i = 0; i < 16; i += 4) {
-                       AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
-                       nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
-               }
-       }
-
-       ctrl  = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
-       ctrl &= ~0x0001f0ff;
-       ctrl |= type << 12;
-       ctrl |= size - 1;
-       nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
-
-       /* retry transaction a number of times on failure... */
-       ret = -EREMOTEIO;
-       for (retries = 0; retries < 32; retries++) {
-               /* reset, and delay a while if this is a retry */
-               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
-               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
-               if (retries)
-                       udelay(400);
-
-               /* transaction request, wait up to 1ms for it to complete */
-               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
-
-               timeout = 1000;
-               do {
-                       ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
-                       udelay(1);
-                       if (!timeout--) {
-                               AUX_ERR("tx req timeout 0x%08x\n", ctrl);
-                               goto out;
-                       }
-               } while (ctrl & 0x00010000);
-
-               /* read status, and check if transaction completed ok */
-               stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
-               if (!(stat & 0x000f0f00)) {
-                       ret = 0;
-                       break;
-               }
-
-               AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
-       }
-
-       if (type & 1) {
-               for (i = 0; i < 16; i += 4) {
-                       xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i);
-                       AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
-               }
-               memcpy(data, xbuf, size);
-       }
-
-out:
-       auxch_fini(aux, ch);
-       return ret;
-}
-
 int
-nv_rdaux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size)
+nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
 {
-       return auxch_tx(auxch->i2c, auxch->drive, 9, addr, data, size);
+       if (port->func->aux) {
+               if (port->func->acquire)
+                       port->func->acquire(port);
+               return port->func->aux(port, 9, addr, data, size);
+       }
+       return -ENODEV;
 }
 
 int
-nv_wraux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size)
+nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
 {
-       return auxch_tx(auxch->i2c, auxch->drive, 8, addr, data, size);
+       if (port->func->aux) {
+               if (port->func->acquire)
+                       port->func->acquire(port);
+               return port->func->aux(port, 8, addr, data, size);
+       }
+       return -ENODEV;
 }
 
 static int
 aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
-       struct nouveau_i2c_port *auxch = (struct nouveau_i2c_port *)adap;
+       struct nouveau_i2c_port *port = adap->algo_data;
        struct i2c_msg *msg = msgs;
        int ret, mcnt = num;
 
+       if (!port->func->aux)
+               return -ENODEV;
+       if ( port->func->acquire)
+               port->func->acquire(port);
+
        while (mcnt--) {
                u8 remaining = msg->len;
                u8 *ptr = msg->buf;
@@ -185,8 +74,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
                        if (mcnt || remaining > 16)
                                cmd |= 4; /* MOT */
 
-                       ret = auxch_tx(auxch->i2c, auxch->drive, cmd,
-                                      msg->addr, ptr, cnt);
+                       ret = port->func->aux(port, cmd, msg->addr, ptr, cnt);
                        if (ret < 0)
                                return ret;
 
index dbfc2ab..a114a0e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Red Hat Inc.
+ * 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"),
  * Authors: Ben Skeggs
  */
 
-#include "core/option.h"
+#include <core/option.h>
 
-#include "subdev/i2c.h"
-#include "subdev/vga.h"
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/i2c.h>
+#include <subdev/i2c.h>
+#include <subdev/vga.h>
 
-int
-nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg)
+/******************************************************************************
+ * interface to linux i2c bit-banging algorithm
+ *****************************************************************************/
+
+#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT
+#define CSTMSEL true
+#else
+#define CSTMSEL false
+#endif
+
+static int
+nouveau_i2c_pre_xfer(struct i2c_adapter *adap)
 {
-       u8 val;
-       struct i2c_msg msgs[] = {
-               { .addr = addr, .flags = 0, .len = 1, .buf = &reg },
-               { .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = &val },
-       };
+       struct i2c_algo_bit_data *bit = adap->algo_data;
+       struct nouveau_i2c_port *port = bit->data;
+       if (port->func->acquire)
+               port->func->acquire(port);
+       return 0;
+}
 
-       int ret = i2c_transfer(&port->adapter, msgs, 2);
-       if (ret != 2)
-               return -EIO;
+static void
+nouveau_i2c_setscl(void *data, int state)
+{
+       struct nouveau_i2c_port *port = data;
+       port->func->drive_scl(port, state);
+}
 
-       return val;
+static void
+nouveau_i2c_setsda(void *data, int state)
+{
+       struct nouveau_i2c_port *port = data;
+       port->func->drive_sda(port, state);
 }
 
-int
-nv_wri2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg, u8 val)
+static int
+nouveau_i2c_getscl(void *data)
 {
-       struct i2c_msg msgs[] = {
-               { .addr = addr, .flags = 0, .len = 1, .buf = &reg },
-               { .addr = addr, .flags = 0, .len = 1, .buf = &val },
-       };
+       struct nouveau_i2c_port *port = data;
+       return port->func->sense_scl(port);
+}
 
-       int ret = i2c_transfer(&port->adapter, msgs, 2);
-       if (ret != 2)
-               return -EIO;
+static int
+nouveau_i2c_getsda(void *data)
+{
+       struct nouveau_i2c_port *port = data;
+       return port->func->sense_sda(port);
+}
 
-       return 0;
+/******************************************************************************
+ * base i2c "port" class implementation
+ *****************************************************************************/
+
+void
+_nouveau_i2c_port_dtor(struct nouveau_object *object)
+{
+       struct nouveau_i2c_port *port = (void *)object;
+       i2c_del_adapter(&port->adapter);
+       nouveau_object_destroy(&port->base);
 }
 
-bool
-nv_probe_i2c(struct nouveau_i2c_port *port, u8 addr)
+int
+nouveau_i2c_port_create_(struct nouveau_object *parent,
+                        struct nouveau_object *engine,
+                        struct nouveau_oclass *oclass, u8 index,
+                        const struct i2c_algorithm *algo,
+                        int size, void **pobject)
 {
-       u8 buf[] = { 0 };
-       struct i2c_msg msgs[] = {
-               {
-                       .addr = addr,
-                       .flags = 0,
-                       .len = 1,
-                       .buf = buf,
-               },
-               {
-                       .addr = addr,
-                       .flags = I2C_M_RD,
-                       .len = 1,
-                       .buf = buf,
-               }
-       };
+       struct nouveau_device *device = nv_device(parent);
+       struct nouveau_i2c *i2c = (void *)engine;
+       struct nouveau_i2c_port *port;
+       int ret;
 
-       return i2c_transfer(&port->adapter, msgs, 2) == 2;
+       ret = nouveau_object_create_(parent, engine, oclass, 0, size, pobject);
+       port = *pobject;
+       if (ret)
+               return ret;
+
+       snprintf(port->adapter.name, sizeof(port->adapter.name),
+                "nouveau-%s-%d", device->name, index);
+       port->adapter.owner = THIS_MODULE;
+       port->adapter.dev.parent = &device->pdev->dev;
+       port->index = index;
+       i2c_set_adapdata(&port->adapter, i2c);
+
+       if ( algo == &nouveau_i2c_bit_algo &&
+           !nouveau_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) {
+               struct i2c_algo_bit_data *bit;
+
+               bit = kzalloc(sizeof(*bit), GFP_KERNEL);
+               if (!bit)
+                       return -ENOMEM;
+
+               bit->udelay = 10;
+               bit->timeout = usecs_to_jiffies(2200);
+               bit->data = port;
+               bit->pre_xfer = nouveau_i2c_pre_xfer;
+               bit->setsda = nouveau_i2c_setsda;
+               bit->setscl = nouveau_i2c_setscl;
+               bit->getsda = nouveau_i2c_getsda;
+               bit->getscl = nouveau_i2c_getscl;
+
+               port->adapter.algo_data = bit;
+               ret = i2c_bit_add_bus(&port->adapter);
+       } else {
+               port->adapter.algo_data = port;
+               port->adapter.algo = algo;
+               ret = i2c_add_adapter(&port->adapter);
+       }
+
+       /* drop port's i2c subdev refcount, i2c handles this itself */
+       if (ret == 0) {
+               list_add_tail(&port->head, &i2c->ports);
+               atomic_dec(&engine->refcount);
+       }
+
+       return ret;
 }
 
+/******************************************************************************
+ * base i2c subdev class implementation
+ *****************************************************************************/
+
 static struct nouveau_i2c_port *
 nouveau_i2c_find(struct nouveau_i2c *i2c, u8 index)
 {
@@ -103,29 +175,23 @@ nouveau_i2c_find(struct nouveau_i2c *i2c, u8 index)
 
        list_for_each_entry(port, &i2c->ports, head) {
                if (port->index == index)
-                       break;
+                       return port;
        }
 
-       if (&port->head == &i2c->ports)
-               return NULL;
+       return NULL;
+}
 
-       if (nv_device(i2c)->card_type >= NV_50 && (port->dcb & 0x00000100)) {
-               u32 reg = 0x00e500, val;
-               if (port->type == 6) {
-                       reg += port->drive * 0x50;
-                       val  = 0x2002;
-               } else {
-                       reg += ((port->dcb & 0x1e00) >> 9) * 0x50;
-                       val  = 0xe001;
-               }
+static struct nouveau_i2c_port *
+nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type)
+{
+       struct nouveau_i2c_port *port;
 
-               /* nfi, but neither auxch or i2c work if it's 1 */
-               nv_mask(i2c, reg + 0x0c, 0x00000001, 0x00000000);
-               /* nfi, but switches auxch vs normal i2c */
-               nv_mask(i2c, reg + 0x00, 0x0000f003, val);
+       list_for_each_entry(port, &i2c->ports, head) {
+               if (nv_hclass(port) == type)
+                       return port;
        }
 
-       return port;
+       return NULL;
 }
 
 static int
@@ -155,109 +221,86 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
        return -ENODEV;
 }
 
-void
-nouveau_i2c_drive_scl(void *data, int state)
+int
+_nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
 {
-       struct nouveau_i2c_port *port = data;
+       struct nouveau_i2c *i2c = (void *)object;
+       struct nouveau_i2c_port *port;
+       int ret;
 
-       if (port->type == DCB_I2C_NV04_BIT) {
-               u8 val = nv_rdvgac(port->i2c, 0, port->drive);
-               if (state) val |= 0x20;
-               else       val &= 0xdf;
-               nv_wrvgac(port->i2c, 0, port->drive, val | 0x01);
-       } else
-       if (port->type == DCB_I2C_NV4E_BIT) {
-               nv_mask(port->i2c, port->drive, 0x2f, state ? 0x21 : 0x01);
-       } else
-       if (port->type == DCB_I2C_NVIO_BIT) {
-               if (state) port->state |= 0x01;
-               else       port->state &= 0xfe;
-               nv_wr32(port->i2c, port->drive, 4 | port->state);
+       list_for_each_entry(port, &i2c->ports, head) {
+               ret = nv_ofuncs(port)->fini(nv_object(port), suspend);
+               if (ret && suspend)
+                       goto fail;
        }
-}
-
-void
-nouveau_i2c_drive_sda(void *data, int state)
-{
-       struct nouveau_i2c_port *port = data;
 
-       if (port->type == DCB_I2C_NV04_BIT) {
-               u8 val = nv_rdvgac(port->i2c, 0, port->drive);
-               if (state) val |= 0x10;
-               else       val &= 0xef;
-               nv_wrvgac(port->i2c, 0, port->drive, val | 0x01);
-       } else
-       if (port->type == DCB_I2C_NV4E_BIT) {
-               nv_mask(port->i2c, port->drive, 0x1f, state ? 0x11 : 0x01);
-       } else
-       if (port->type == DCB_I2C_NVIO_BIT) {
-               if (state) port->state |= 0x02;
-               else       port->state &= 0xfd;
-               nv_wr32(port->i2c, port->drive, 4 | port->state);
+       return nouveau_subdev_fini(&i2c->base, suspend);
+fail:
+       list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
+               nv_ofuncs(port)->init(nv_object(port));
        }
+
+       return ret;
 }
 
 int
-nouveau_i2c_sense_scl(void *data)
+_nouveau_i2c_init(struct nouveau_object *object)
 {
-       struct nouveau_i2c_port *port = data;
-       struct nouveau_device *device = nv_device(port->i2c);
-
-       if (port->type == DCB_I2C_NV04_BIT) {
-               return !!(nv_rdvgac(port->i2c, 0, port->sense) & 0x04);
-       } else
-       if (port->type == DCB_I2C_NV4E_BIT) {
-               return !!(nv_rd32(port->i2c, port->sense) & 0x00040000);
-       } else
-       if (port->type == DCB_I2C_NVIO_BIT) {
-               if (device->card_type < NV_D0)
-                       return !!(nv_rd32(port->i2c, port->sense) & 0x01);
-               else
-                       return !!(nv_rd32(port->i2c, port->sense) & 0x10);
+       struct nouveau_i2c *i2c = (void *)object;
+       struct nouveau_i2c_port *port;
+       int ret;
+
+       ret = nouveau_subdev_init(&i2c->base);
+       if (ret == 0) {
+               list_for_each_entry(port, &i2c->ports, head) {
+                       ret = nv_ofuncs(port)->init(nv_object(port));
+                       if (ret)
+                               goto fail;
+               }
        }
 
-       return 0;
+       return ret;
+fail:
+       list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
+               nv_ofuncs(port)->fini(nv_object(port), false);
+       }
+
+       return ret;
 }
 
-int
-nouveau_i2c_sense_sda(void *data)
+void
+_nouveau_i2c_dtor(struct nouveau_object *object)
 {
-       struct nouveau_i2c_port *port = data;
-       struct nouveau_device *device = nv_device(port->i2c);
-
-       if (port->type == DCB_I2C_NV04_BIT) {
-               return !!(nv_rdvgac(port->i2c, 0, port->sense) & 0x08);
-       } else
-       if (port->type == DCB_I2C_NV4E_BIT) {
-               return !!(nv_rd32(port->i2c, port->sense) & 0x00080000);
-       } else
-       if (port->type == DCB_I2C_NVIO_BIT) {
-               if (device->card_type < NV_D0)
-                       return !!(nv_rd32(port->i2c, port->sense) & 0x02);
-               else
-                       return !!(nv_rd32(port->i2c, port->sense) & 0x20);
+       struct nouveau_i2c *i2c = (void *)object;
+       struct nouveau_i2c_port *port, *temp;
+
+       list_for_each_entry_safe(port, temp, &i2c->ports, head) {
+               nouveau_object_ref(NULL, (struct nouveau_object **)&port);
        }
 
-       return 0;
+       nouveau_subdev_destroy(&i2c->base);
 }
 
-static const u32 nv50_i2c_port[] = {
-       0x00e138, 0x00e150, 0x00e168, 0x00e180,
-       0x00e254, 0x00e274, 0x00e764, 0x00e780,
-       0x00e79c, 0x00e7b8
+static struct nouveau_oclass *
+nouveau_i2c_extdev_sclass[] = {
+       nouveau_anx9805_sclass,
 };
 
-static int
-nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-                struct nouveau_oclass *oclass, void *data, u32 size,
-                struct nouveau_object **pobject)
+int
+nouveau_i2c_create_(struct nouveau_object *parent,
+                   struct nouveau_object *engine,
+                   struct nouveau_oclass *oclass,
+                   struct nouveau_oclass *sclass,
+                   int length, void **pobject)
 {
-       struct nouveau_device *device = nv_device(parent);
        struct nouveau_bios *bios = nouveau_bios(parent);
-       struct nouveau_i2c_port *port;
        struct nouveau_i2c *i2c;
+       struct nouveau_object *object;
        struct dcb_i2c_entry info;
-       int ret, i = -1;
+       int ret, i, j, index = -1;
+       struct dcb_output outp;
+       u8  ver, hdr;
+       u32 data;
 
        ret = nouveau_subdev_create(parent, engine, oclass, 0,
                                    "I2C", "i2c", &i2c);
@@ -266,142 +309,60 @@ nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        i2c->find = nouveau_i2c_find;
+       i2c->find_type = nouveau_i2c_find_type;
        i2c->identify = nouveau_i2c_identify;
        INIT_LIST_HEAD(&i2c->ports);
 
-       while (!dcb_i2c_parse(bios, ++i, &info)) {
+       while (!dcb_i2c_parse(bios, ++index, &info)) {
                if (info.type == DCB_I2C_UNUSED)
                        continue;
 
-               port = kzalloc(sizeof(*port), GFP_KERNEL);
-               if (!port) {
-                       nv_error(i2c, "failed port memory alloc at %d\n", i);
-                       break;
-               }
-
-               port->type = info.type;
-               switch (port->type) {
-               case DCB_I2C_NV04_BIT:
-                       port->drive = info.drive;
-                       port->sense = info.sense;
-                       break;
-               case DCB_I2C_NV4E_BIT:
-                       port->drive = 0x600800 + info.drive;
-                       port->sense = port->drive;
-                       break;
-               case DCB_I2C_NVIO_BIT:
-                       port->drive = info.drive & 0x0f;
-                       if (device->card_type < NV_D0) {
-                               if (port->drive >= ARRAY_SIZE(nv50_i2c_port))
-                                       break;
-                               port->drive = nv50_i2c_port[port->drive];
-                               port->sense = port->drive;
-                       } else {
-                               port->drive = 0x00d014 + (port->drive * 0x20);
-                               port->sense = port->drive;
+               oclass = sclass;
+               do {
+                       ret = -EINVAL;
+                       if (oclass->handle == info.type) {
+                               ret = nouveau_object_ctor(*pobject, *pobject,
+                                                         oclass, &info,
+                                                         index, &object);
                        }
+               } while (ret && (++oclass)->handle);
+       }
+
+       /* in addition to the busses specified in the i2c table, there
+        * may be ddc/aux channels hiding behind external tmds/dp/etc
+        * transmitters.
+        */
+       index = ((index + 0x0f) / 0x10) * 0x10;
+       i = -1;
+       while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &outp))) {
+               if (!outp.location || !outp.extdev)
+                       continue;
+
+               switch (outp.type) {
+               case DCB_OUTPUT_TMDS:
+                       info.type = NV_I2C_TYPE_EXTDDC(outp.extdev);
                        break;
-               case DCB_I2C_NVIO_AUX:
-                       port->drive = info.drive & 0x0f;
-                       port->sense = port->drive;
-                       port->adapter.algo = &nouveau_i2c_aux_algo;
+               case DCB_OUTPUT_DP:
+                       info.type = NV_I2C_TYPE_EXTAUX(outp.extdev);
                        break;
                default:
-                       break;
-               }
-
-               if (!port->adapter.algo && !port->drive) {
-                       nv_error(i2c, "I2C%d: type %d index %x/%x unknown\n",
-                                i, port->type, port->drive, port->sense);
-                       kfree(port);
                        continue;
                }
 
-               snprintf(port->adapter.name, sizeof(port->adapter.name),
-                        "nouveau-%s-%d", device->name, i);
-               port->adapter.owner = THIS_MODULE;
-               port->adapter.dev.parent = &device->pdev->dev;
-               port->i2c = i2c;
-               port->index = i;
-               port->dcb = info.data;
-               i2c_set_adapdata(&port->adapter, i2c);
-
-               if (port->adapter.algo != &nouveau_i2c_aux_algo) {
-                       nouveau_i2c_drive_scl(port, 0);
-                       nouveau_i2c_drive_sda(port, 1);
-                       nouveau_i2c_drive_scl(port, 1);
-
-#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT
-                       if (nouveau_boolopt(device->cfgopt, "NvI2C", true)) {
-#else
-                       if (nouveau_boolopt(device->cfgopt, "NvI2C", false)) {
-#endif
-                               port->adapter.algo = &nouveau_i2c_bit_algo;
-                               ret = i2c_add_adapter(&port->adapter);
-                       } else {
-                               port->adapter.algo_data = &port->bit;
-                               port->bit.udelay = 10;
-                               port->bit.timeout = usecs_to_jiffies(2200);
-                               port->bit.data = port;
-                               port->bit.setsda = nouveau_i2c_drive_sda;
-                               port->bit.setscl = nouveau_i2c_drive_scl;
-                               port->bit.getsda = nouveau_i2c_sense_sda;
-                               port->bit.getscl = nouveau_i2c_sense_scl;
-                               ret = i2c_bit_add_bus(&port->adapter);
-                       }
-               } else {
-                       port->adapter.algo = &nouveau_i2c_aux_algo;
-                       ret = i2c_add_adapter(&port->adapter);
-               }
-
-               if (ret) {
-                       nv_error(i2c, "I2C%d: failed register: %d\n", i, ret);
-                       kfree(port);
-                       continue;
+               ret = -ENODEV;
+               j = -1;
+               while (ret && ++j < ARRAY_SIZE(nouveau_i2c_extdev_sclass)) {
+                       parent = nv_object(i2c->find(i2c, outp.i2c_index));
+                       oclass = nouveau_i2c_extdev_sclass[j];
+                       do {
+                               if (oclass->handle != info.type)
+                                       continue;
+                               ret = nouveau_object_ctor(parent, *pobject,
+                                                         oclass, NULL,
+                                                         index++, &object);
+                       } while (ret && (++oclass)->handle);
                }
-
-               list_add_tail(&port->head, &i2c->ports);
        }
 
        return 0;
 }
-
-static void
-nouveau_i2c_dtor(struct nouveau_object *object)
-{
-       struct nouveau_i2c *i2c = (void *)object;
-       struct nouveau_i2c_port *port, *temp;
-
-       list_for_each_entry_safe(port, temp, &i2c->ports, head) {
-               i2c_del_adapter(&port->adapter);
-               list_del(&port->head);
-               kfree(port);
-       }
-
-       nouveau_subdev_destroy(&i2c->base);
-}
-
-static int
-nouveau_i2c_init(struct nouveau_object *object)
-{
-       struct nouveau_i2c *i2c = (void *)object;
-       return nouveau_subdev_init(&i2c->base);
-}
-
-static int
-nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
-{
-       struct nouveau_i2c *i2c = (void *)object;
-       return nouveau_subdev_fini(&i2c->base, suspend);
-}
-
-struct nouveau_oclass
-nouveau_i2c_oclass = {
-       .handle = NV_SUBDEV(I2C, 0x00),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nouveau_i2c_ctor,
-               .dtor = nouveau_i2c_dtor,
-               .init = nouveau_i2c_init,
-               .fini = nouveau_i2c_fini,
-       },
-};
index 1c4c9a5..a6e72d3 100644 (file)
 static inline void
 i2c_drive_scl(struct nouveau_i2c_port *port, int state)
 {
-       nouveau_i2c_drive_scl(port, state);
+       port->func->drive_scl(port, state);
 }
 
 static inline void
 i2c_drive_sda(struct nouveau_i2c_port *port, int state)
 {
-       nouveau_i2c_drive_sda(port, state);
+       port->func->drive_sda(port, state);
 }
 
 static inline int
 i2c_sense_scl(struct nouveau_i2c_port *port)
 {
-       return nouveau_i2c_sense_scl(port);
+       return port->func->sense_scl(port);
 }
 
 static inline int
 i2c_sense_sda(struct nouveau_i2c_port *port)
 {
-       return nouveau_i2c_sense_sda(port);
+       return port->func->sense_sda(port);
 }
 
 static void
@@ -77,9 +77,8 @@ i2c_start(struct nouveau_i2c_port *port)
 {
        int ret = 0;
 
-       port->state  = i2c_sense_scl(port);
-       port->state |= i2c_sense_sda(port) << 1;
-       if (port->state != 3) {
+       if (!i2c_sense_scl(port) ||
+           !i2c_sense_sda(port)) {
                i2c_drive_scl(port, 0);
                i2c_drive_sda(port, 1);
                if (!i2c_raise_scl(port))
@@ -184,10 +183,13 @@ i2c_addr(struct nouveau_i2c_port *port, struct i2c_msg *msg)
 static int
 i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
-       struct nouveau_i2c_port *port = (struct nouveau_i2c_port *)adap;
+       struct nouveau_i2c_port *port = adap->algo_data;
        struct i2c_msg *msg = msgs;
        int ret = 0, mcnt = num;
 
+       if (port->func->acquire)
+               port->func->acquire(port);
+
        while (!ret && mcnt--) {
                u8 remaining = msg->len;
                u8 *ptr = msg->buf;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c
new file mode 100644 (file)
index 0000000..2ad1884
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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/i2c.h>
+#include <subdev/vga.h>
+
+struct nv04_i2c_priv {
+       struct nouveau_i2c base;
+};
+
+struct nv04_i2c_port {
+       struct nouveau_i2c_port base;
+       u8 drive;
+       u8 sense;
+};
+
+static void
+nv04_i2c_drive_scl(struct nouveau_i2c_port *base, int state)
+{
+       struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv04_i2c_port *port = (void *)base;
+       u8 val = nv_rdvgac(priv, 0, port->drive);
+       if (state) val |= 0x20;
+       else       val &= 0xdf;
+       nv_wrvgac(priv, 0, port->drive, val | 0x01);
+}
+
+static void
+nv04_i2c_drive_sda(struct nouveau_i2c_port *base, int state)
+{
+       struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv04_i2c_port *port = (void *)base;
+       u8 val = nv_rdvgac(priv, 0, port->drive);
+       if (state) val |= 0x10;
+       else       val &= 0xef;
+       nv_wrvgac(priv, 0, port->drive, val | 0x01);
+}
+
+static int
+nv04_i2c_sense_scl(struct nouveau_i2c_port *base)
+{
+       struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv04_i2c_port *port = (void *)base;
+       return !!(nv_rdvgac(priv, 0, port->sense) & 0x04);
+}
+
+static int
+nv04_i2c_sense_sda(struct nouveau_i2c_port *base)
+{
+       struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv04_i2c_port *port = (void *)base;
+       return !!(nv_rdvgac(priv, 0, port->sense) & 0x08);
+}
+
+static const struct nouveau_i2c_func
+nv04_i2c_func = {
+       .drive_scl = nv04_i2c_drive_scl,
+       .drive_sda = nv04_i2c_drive_sda,
+       .sense_scl = nv04_i2c_sense_scl,
+       .sense_sda = nv04_i2c_sense_sda,
+};
+
+static int
+nv04_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv04_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       port->base.func = &nv04_i2c_func;
+       port->drive = info->drive;
+       port->sense = info->sense;
+       return 0;
+}
+
+static struct nouveau_oclass
+nv04_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV04_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv04_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = _nouveau_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nv04_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv04_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nv04_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nv04_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0x04),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c
new file mode 100644 (file)
index 0000000..f501ae2
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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/i2c.h>
+#include <subdev/vga.h>
+
+struct nv4e_i2c_priv {
+       struct nouveau_i2c base;
+};
+
+struct nv4e_i2c_port {
+       struct nouveau_i2c_port base;
+       u32 addr;
+};
+
+static void
+nv4e_i2c_drive_scl(struct nouveau_i2c_port *base, int state)
+{
+       struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv4e_i2c_port *port = (void *)base;
+       nv_mask(priv, port->addr, 0x2f, state ? 0x21 : 0x01);
+}
+
+static void
+nv4e_i2c_drive_sda(struct nouveau_i2c_port *base, int state)
+{
+       struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv4e_i2c_port *port = (void *)base;
+       nv_mask(priv, port->addr, 0x1f, state ? 0x11 : 0x01);
+}
+
+static int
+nv4e_i2c_sense_scl(struct nouveau_i2c_port *base)
+{
+       struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv4e_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00040000);
+}
+
+static int
+nv4e_i2c_sense_sda(struct nouveau_i2c_port *base)
+{
+       struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv4e_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00080000);
+}
+
+static const struct nouveau_i2c_func
+nv4e_i2c_func = {
+       .drive_scl = nv4e_i2c_drive_scl,
+       .drive_sda = nv4e_i2c_drive_sda,
+       .sense_scl = nv4e_i2c_sense_scl,
+       .sense_sda = nv4e_i2c_sense_sda,
+};
+
+static int
+nv4e_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv4e_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       port->base.func = &nv4e_i2c_func;
+       port->addr = 0x600800 + info->drive;
+       return 0;
+}
+
+static struct nouveau_oclass
+nv4e_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV4E_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv4e_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = _nouveau_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nv4e_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv4e_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nv4e_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nv4e_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0x4e),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv4e_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c
new file mode 100644 (file)
index 0000000..378dfa3
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * 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 "nv50.h"
+
+void
+nv50_i2c_drive_scl(struct nouveau_i2c_port *base, int state)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       if (state) port->state |= 0x01;
+       else       port->state &= 0xfe;
+       nv_wr32(priv, port->addr, port->state);
+}
+
+void
+nv50_i2c_drive_sda(struct nouveau_i2c_port *base, int state)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       if (state) port->state |= 0x02;
+       else       port->state &= 0xfd;
+       nv_wr32(priv, port->addr, port->state);
+}
+
+int
+nv50_i2c_sense_scl(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00000001);
+}
+
+int
+nv50_i2c_sense_sda(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00000002);
+}
+
+static const struct nouveau_i2c_func
+nv50_i2c_func = {
+       .drive_scl = nv50_i2c_drive_scl,
+       .drive_sda = nv50_i2c_drive_sda,
+       .sense_scl = nv50_i2c_sense_scl,
+       .sense_sda = nv50_i2c_sense_sda,
+};
+
+const u32 nv50_i2c_addr[] = {
+       0x00e138, 0x00e150, 0x00e168, 0x00e180,
+       0x00e254, 0x00e274, 0x00e764, 0x00e780,
+       0x00e79c, 0x00e7b8
+};
+const int nv50_i2c_addr_nr = ARRAY_SIZE(nv50_i2c_addr);
+
+static int
+nv50_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv50_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       if (info->drive >= nv50_i2c_addr_nr)
+               return -EINVAL;
+
+       port->base.func = &nv50_i2c_func;
+       port->state = 0x00000007;
+       port->addr = nv50_i2c_addr[info->drive];
+       return 0;
+}
+
+int
+nv50_i2c_port_init(struct nouveau_object *object)
+{
+       struct nv50_i2c_priv *priv = (void *)object->engine;
+       struct nv50_i2c_port *port = (void *)object;
+       nv_wr32(priv, port->addr, port->state);
+       return nouveau_i2c_port_init(&port->base);
+}
+
+static struct nouveau_oclass
+nv50_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv50_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = nv50_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nv50_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv50_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nv50_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nv50_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0x50),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h
new file mode 100644 (file)
index 0000000..4e5ba48
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __NV50_I2C_H__
+#define __NV50_I2C_H__
+
+#include <subdev/i2c.h>
+
+struct nv50_i2c_priv {
+       struct nouveau_i2c base;
+};
+
+struct nv50_i2c_port {
+       struct nouveau_i2c_port base;
+       u32 addr;
+       u32 ctrl;
+       u32 data;
+       u32 state;
+};
+
+extern const u32 nv50_i2c_addr[];
+extern const int nv50_i2c_addr_nr;
+int  nv50_i2c_port_init(struct nouveau_object *);
+int  nv50_i2c_sense_scl(struct nouveau_i2c_port *);
+int  nv50_i2c_sense_sda(struct nouveau_i2c_port *);
+void nv50_i2c_drive_scl(struct nouveau_i2c_port *, int state);
+void nv50_i2c_drive_sda(struct nouveau_i2c_port *, int state);
+
+int  nv94_aux_port_ctor(struct nouveau_object *, struct nouveau_object *,
+                       struct nouveau_oclass *, void *, u32,
+                       struct nouveau_object **);
+void nv94_i2c_acquire(struct nouveau_i2c_port *);
+void nv94_i2c_release(struct nouveau_i2c_port *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c
new file mode 100644 (file)
index 0000000..61b7716
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * 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 "nv50.h"
+
+#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
+#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
+
+static void
+auxch_fini(struct nouveau_i2c *aux, int ch)
+{
+       nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
+}
+
+static int
+auxch_init(struct nouveau_i2c *aux, int ch)
+{
+       const u32 unksel = 1; /* nfi which to use, or if it matters.. */
+       const u32 ureq = unksel ? 0x00100000 : 0x00200000;
+       const u32 urep = unksel ? 0x01000000 : 0x02000000;
+       u32 ctrl, timeout;
+
+       /* wait up to 1ms for any previous transaction to be done... */
+       timeout = 1000;
+       do {
+               ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+               udelay(1);
+               if (!timeout--) {
+                       AUX_ERR("begin idle timeout 0x%08x\n", ctrl);
+                       return -EBUSY;
+               }
+       } while (ctrl & 0x03010000);
+
+       /* set some magic, and wait up to 1ms for it to appear */
+       nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
+       timeout = 1000;
+       do {
+               ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+               udelay(1);
+               if (!timeout--) {
+                       AUX_ERR("magic wait 0x%08x\n", ctrl);
+                       auxch_fini(aux, ch);
+                       return -EBUSY;
+               }
+       } while ((ctrl & 0x03000000) != urep);
+
+       return 0;
+}
+
+int
+nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
+{
+       struct nouveau_i2c *aux = nouveau_i2c(base);
+       struct nv50_i2c_port *port = (void *)base;
+       u32 ctrl, stat, timeout, retries;
+       u32 xbuf[4] = {};
+       int ch = port->addr;
+       int ret, i;
+
+       AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
+
+       ret = auxch_init(aux, ch);
+       if (ret)
+               goto out;
+
+       stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50));
+       if (!(stat & 0x10000000)) {
+               AUX_DBG("sink not detected\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       if (!(type & 1)) {
+               memcpy(xbuf, data, size);
+               for (i = 0; i < 16; i += 4) {
+                       AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
+                       nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
+               }
+       }
+
+       ctrl  = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+       ctrl &= ~0x0001f0ff;
+       ctrl |= type << 12;
+       ctrl |= size - 1;
+       nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
+
+       /* retry transaction a number of times on failure... */
+       ret = -EREMOTEIO;
+       for (retries = 0; retries < 32; retries++) {
+               /* reset, and delay a while if this is a retry */
+               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
+               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
+               if (retries)
+                       udelay(400);
+
+               /* transaction request, wait up to 1ms for it to complete */
+               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
+
+               timeout = 1000;
+               do {
+                       ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+                       udelay(1);
+                       if (!timeout--) {
+                               AUX_ERR("tx req timeout 0x%08x\n", ctrl);
+                               goto out;
+                       }
+               } while (ctrl & 0x00010000);
+
+               /* read status, and check if transaction completed ok */
+               stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
+               if (!(stat & 0x000f0f00)) {
+                       ret = 0;
+                       break;
+               }
+
+               AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
+       }
+
+       if (type & 1) {
+               for (i = 0; i < 16; i += 4) {
+                       xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i);
+                       AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
+               }
+               memcpy(data, xbuf, size);
+       }
+
+out:
+       auxch_fini(aux, ch);
+       return ret;
+}
+
+void
+nv94_i2c_acquire(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       if (port->ctrl) {
+               nv_mask(priv, port->ctrl + 0x0c, 0x00000001, 0x00000000);
+               nv_mask(priv, port->ctrl + 0x00, 0x0000f003, port->data);
+       }
+}
+
+void
+nv94_i2c_release(struct nouveau_i2c_port *base)
+{
+}
+
+static const struct nouveau_i2c_func
+nv94_i2c_func = {
+       .acquire   = nv94_i2c_acquire,
+       .release   = nv94_i2c_release,
+       .drive_scl = nv50_i2c_drive_scl,
+       .drive_sda = nv50_i2c_drive_sda,
+       .sense_scl = nv50_i2c_sense_scl,
+       .sense_sda = nv50_i2c_sense_sda,
+};
+
+static int
+nv94_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv50_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       if (info->drive >= nv50_i2c_addr_nr)
+               return -EINVAL;
+
+       port->base.func = &nv94_i2c_func;
+       port->state = 7;
+       port->addr = nv50_i2c_addr[info->drive];
+       if (info->share != DCB_I2C_UNUSED) {
+               port->ctrl = 0x00e500 + (info->share * 0x50);
+               port->data = 0x0000e001;
+       }
+       return 0;
+}
+
+static const struct nouveau_i2c_func
+nv94_aux_func = {
+       .acquire   = nv94_i2c_acquire,
+       .release   = nv94_i2c_release,
+       .aux       = nv94_aux,
+};
+
+int
+nv94_aux_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv50_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_aux_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       port->base.func = &nv94_aux_func;
+       port->addr = info->drive;
+       if (info->share != DCB_I2C_UNUSED) {
+               port->ctrl = 0x00e500 + (info->drive * 0x50);
+               port->data = 0x00002002;
+       }
+
+       return 0;
+}
+
+static struct nouveau_oclass
+nv94_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv94_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = nv50_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv94_aux_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = _nouveau_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nv94_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv50_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nv94_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nv94_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0x94),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv94_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c
new file mode 100644 (file)
index 0000000..f761b8a
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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 "nv50.h"
+
+static int
+nvd0_i2c_sense_scl(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00000010);
+}
+
+static int
+nvd0_i2c_sense_sda(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00000020);
+}
+
+static const struct nouveau_i2c_func
+nvd0_i2c_func = {
+       .acquire   = nv94_i2c_acquire,
+       .release   = nv94_i2c_release,
+       .drive_scl = nv50_i2c_drive_scl,
+       .drive_sda = nv50_i2c_drive_sda,
+       .sense_scl = nvd0_i2c_sense_scl,
+       .sense_sda = nvd0_i2c_sense_sda,
+};
+
+static int
+nvd0_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv50_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       port->base.func = &nvd0_i2c_func;
+       port->state = 0x00000007;
+       port->addr = 0x00d014 + (info->drive * 0x20);
+       if (info->share != DCB_I2C_UNUSED) {
+               port->ctrl = 0x00e500 + (info->share * 0x50);
+               port->data = 0x0000e001;
+       }
+       return 0;
+}
+
+static struct nouveau_oclass
+nvd0_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nvd0_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = nv50_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv94_aux_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = _nouveau_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nvd0_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv50_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nvd0_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nvd0_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0xd0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvd0_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
index 23ebe47..89da8fa 100644 (file)
@@ -37,7 +37,7 @@ nv04_mc_intr[] = {
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x01000000, NVDEV_ENGINE_DISP },      /* NV04- PCRTC0 */
        { 0x02000000, NVDEV_ENGINE_DISP },      /* NV11- PCRTC1 */
-       { 0x10000000, NVDEV_SUBDEV_GPIO },      /* PBUS */
+       { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        {}
 };
index 8d759f8..5965add 100644 (file)
@@ -38,6 +38,7 @@ nv50_mc_intr[] = {
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x00200000, NVDEV_SUBDEV_GPIO },
        { 0x04000000, NVDEV_ENGINE_DISP },
+       { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        { 0x0000d101, NVDEV_SUBDEV_FB },
        {},
index ceb5c83..3a80b29 100644 (file)
@@ -35,10 +35,12 @@ nv98_mc_intr[] = {
        { 0x00001000, NVDEV_ENGINE_GR },
        { 0x00004000, NVDEV_ENGINE_CRYPT },     /* NV84:NVA3 */
        { 0x00008000, NVDEV_ENGINE_BSP },
+       { 0x00080000, NVDEV_SUBDEV_THERM },     /* NVA3:NVC0 */
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x00200000, NVDEV_SUBDEV_GPIO },
        { 0x00400000, NVDEV_ENGINE_COPY0 },     /* NVA3-     */
        { 0x04000000, NVDEV_ENGINE_DISP },
+       { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        { 0x0040d101, NVDEV_SUBDEV_FB },
        {},
index 9279668..42bbf72 100644 (file)
@@ -36,11 +36,13 @@ nvc0_mc_intr[] = {
        { 0x00000100, NVDEV_ENGINE_FIFO },
        { 0x00001000, NVDEV_ENGINE_GR },
        { 0x00008000, NVDEV_ENGINE_BSP },
+       { 0x00040000, NVDEV_SUBDEV_THERM },
        { 0x00020000, NVDEV_ENGINE_VP },
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x00200000, NVDEV_SUBDEV_GPIO },
        { 0x02000000, NVDEV_SUBDEV_LTCG },
        { 0x04000000, NVDEV_ENGINE_DISP },
+       { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x40000000, NVDEV_SUBDEV_IBUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        {},
index 839ca1e..4bde7f7 100644 (file)
@@ -156,15 +156,15 @@ mxms_foreach(struct nouveau_mxm *mxm, u8 types,
 
                        nv_debug(mxm, "%4s: ", mxms_desc_name[type]);
                        for (j = headerlen - 1; j >= 0; j--)
-                               printk("%02x", dump[j]);
-                       printk("\n");
+                               pr_cont("%02x", dump[j]);
+                       pr_cont("\n");
                        dump += headerlen;
 
                        for (i = 0; i < entries; i++, dump += recordlen) {
                                nv_debug(mxm, "      ");
                                for (j = recordlen - 1; j >= 0; j--)
-                                       printk("%02x", dump[j]);
-                               printk("\n");
+                                       pr_cont("%02x", dump[j]);
+                               pr_cont("\n");
                        }
                }
 
index 1674c74..f794dc8 100644 (file)
 
 #include "priv.h"
 
+static int
+nouveau_therm_update_trip(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nouveau_therm_trip_point *trip = priv->fan->bios.trip,
+                                       *cur_trip = NULL,
+                                       *last_trip = priv->last_trip;
+       u8  temp = therm->temp_get(therm);
+       u16 duty, i;
+
+       /* look for the trip point corresponding to the current temperature */
+       cur_trip = NULL;
+       for (i = 0; i < priv->fan->bios.nr_fan_trip; i++) {
+               if (temp >= trip[i].temp)
+                       cur_trip = &trip[i];
+       }
+
+       /* account for the hysteresis cycle */
+       if (last_trip && temp <= (last_trip->temp) &&
+           temp > (last_trip->temp - last_trip->hysteresis))
+               cur_trip = last_trip;
+
+       if (cur_trip) {
+               duty = cur_trip->fan_duty;
+               priv->last_trip = cur_trip;
+       } else {
+               duty = 0;
+               priv->last_trip = NULL;
+       }
+
+       return duty;
+}
+
+static int
+nouveau_therm_update_linear(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       u8  linear_min_temp = priv->fan->bios.linear_min_temp;
+       u8  linear_max_temp = priv->fan->bios.linear_max_temp;
+       u8  temp = therm->temp_get(therm);
+       u16 duty;
+
+       /* handle the non-linear part first */
+       if (temp < linear_min_temp)
+               return priv->fan->bios.min_duty;
+       else if (temp > linear_max_temp)
+               return priv->fan->bios.max_duty;
+
+       /* we are in the linear zone */
+       duty  = (temp - linear_min_temp);
+       duty *= (priv->fan->bios.max_duty - priv->fan->bios.min_duty);
+       duty /= (linear_max_temp - linear_min_temp);
+       duty += priv->fan->bios.min_duty;
+
+       return duty;
+}
+
+static void
+nouveau_therm_update(struct nouveau_therm *therm, int mode)
+{
+       struct nouveau_timer *ptimer = nouveau_timer(therm);
+       struct nouveau_therm_priv *priv = (void *)therm;
+       unsigned long flags;
+       int duty;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (mode < 0)
+               mode = priv->mode;
+       priv->mode = mode;
+
+       switch (mode) {
+       case NOUVEAU_THERM_CTRL_MANUAL:
+               duty = nouveau_therm_fan_get(therm);
+               if (duty < 0)
+                       duty = 100;
+               break;
+       case NOUVEAU_THERM_CTRL_AUTO:
+               if (priv->fan->bios.nr_fan_trip)
+                       duty = nouveau_therm_update_trip(therm);
+               else
+                       duty = nouveau_therm_update_linear(therm);
+               break;
+       case NOUVEAU_THERM_CTRL_NONE:
+       default:
+               goto done;
+       }
+
+       nv_debug(therm, "FAN target request: %d%%\n", duty);
+       nouveau_therm_fan_set(therm, (mode != NOUVEAU_THERM_CTRL_AUTO), duty);
+
+done:
+       if (list_empty(&priv->alarm.head) && (mode == NOUVEAU_THERM_CTRL_AUTO))
+               ptimer->alarm(ptimer, 1000000000ULL, &priv->alarm);
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void
+nouveau_therm_alarm(struct nouveau_alarm *alarm)
+{
+       struct nouveau_therm_priv *priv =
+              container_of(alarm, struct nouveau_therm_priv, alarm);
+       nouveau_therm_update(&priv->base, -1);
+}
+
+int
+nouveau_therm_mode(struct nouveau_therm *therm, int mode)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nouveau_device *device = nv_device(therm);
+       static const char *name[] = {
+               "disabled",
+               "manual",
+               "automatic"
+       };
+
+       /* The default PDAEMON ucode interferes with fan management */
+       if ((mode >= ARRAY_SIZE(name)) ||
+           (mode != NOUVEAU_THERM_CTRL_NONE && device->card_type >= NV_C0))
+               return -EINVAL;
+
+       if (priv->mode == mode)
+               return 0;
+
+       nv_info(therm, "Thermal management: %s\n", name[mode]);
+       nouveau_therm_update(therm, mode);
+       return 0;
+}
+
 int
 nouveau_therm_attr_get(struct nouveau_therm *therm,
                       enum nouveau_therm_attr_type type)
@@ -37,11 +165,11 @@ nouveau_therm_attr_get(struct nouveau_therm *therm,
 
        switch (type) {
        case NOUVEAU_THERM_ATTR_FAN_MIN_DUTY:
-               return priv->bios_fan.min_duty;
+               return priv->fan->bios.min_duty;
        case NOUVEAU_THERM_ATTR_FAN_MAX_DUTY:
-               return priv->bios_fan.max_duty;
+               return priv->fan->bios.max_duty;
        case NOUVEAU_THERM_ATTR_FAN_MODE:
-               return priv->fan.mode;
+               return priv->mode;
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:
                return priv->bios_sensor.thrs_fan_boost.temp;
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST:
@@ -73,42 +201,50 @@ nouveau_therm_attr_set(struct nouveau_therm *therm,
        case NOUVEAU_THERM_ATTR_FAN_MIN_DUTY:
                if (value < 0)
                        value = 0;
-               if (value > priv->bios_fan.max_duty)
-                       value = priv->bios_fan.max_duty;
-               priv->bios_fan.min_duty = value;
+               if (value > priv->fan->bios.max_duty)
+                       value = priv->fan->bios.max_duty;
+               priv->fan->bios.min_duty = value;
                return 0;
        case NOUVEAU_THERM_ATTR_FAN_MAX_DUTY:
                if (value < 0)
                        value = 0;
-               if (value < priv->bios_fan.min_duty)
-                       value = priv->bios_fan.min_duty;
-               priv->bios_fan.max_duty = value;
+               if (value < priv->fan->bios.min_duty)
+                       value = priv->fan->bios.min_duty;
+               priv->fan->bios.max_duty = value;
                return 0;
        case NOUVEAU_THERM_ATTR_FAN_MODE:
-               return nouveau_therm_fan_set_mode(therm, value);
+               return nouveau_therm_mode(therm, value);
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:
                priv->bios_sensor.thrs_fan_boost.temp = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST:
                priv->bios_sensor.thrs_fan_boost.hysteresis = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK:
                priv->bios_sensor.thrs_down_clock.temp = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST:
                priv->bios_sensor.thrs_down_clock.hysteresis = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_CRITICAL:
                priv->bios_sensor.thrs_critical.temp = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST:
                priv->bios_sensor.thrs_critical.hysteresis = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN:
                priv->bios_sensor.thrs_shutdown.temp = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST:
                priv->bios_sensor.thrs_shutdown.hysteresis = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        }
 
@@ -116,7 +252,7 @@ nouveau_therm_attr_set(struct nouveau_therm *therm,
 }
 
 int
-nouveau_therm_init(struct nouveau_object *object)
+_nouveau_therm_init(struct nouveau_object *object)
 {
        struct nouveau_therm *therm = (void *)object;
        struct nouveau_therm_priv *priv = (void *)therm;
@@ -126,19 +262,69 @@ nouveau_therm_init(struct nouveau_object *object)
        if (ret)
                return ret;
 
-       if (priv->fan.percent >= 0)
-               therm->fan_set(therm, priv->fan.percent);
-
+       if (priv->suspend >= 0)
+               nouveau_therm_mode(therm, priv->mode);
+       priv->sensor.program_alarms(therm);
        return 0;
 }
 
 int
-nouveau_therm_fini(struct nouveau_object *object, bool suspend)
+_nouveau_therm_fini(struct nouveau_object *object, bool suspend)
 {
        struct nouveau_therm *therm = (void *)object;
        struct nouveau_therm_priv *priv = (void *)therm;
 
-       priv->fan.percent = therm->fan_get(therm);
+       if (suspend) {
+               priv->suspend = priv->mode;
+               priv->mode = NOUVEAU_THERM_CTRL_NONE;
+       }
 
        return nouveau_subdev_fini(&therm->base, suspend);
 }
+
+int
+nouveau_therm_create_(struct nouveau_object *parent,
+                     struct nouveau_object *engine,
+                     struct nouveau_oclass *oclass,
+                     int length, void **pobject)
+{
+       struct nouveau_therm_priv *priv;
+       int ret;
+
+       ret = nouveau_subdev_create_(parent, engine, oclass, 0, "PTHERM",
+                                    "therm", length, pobject);
+       priv = *pobject;
+       if (ret)
+               return ret;
+
+       nouveau_alarm_init(&priv->alarm, nouveau_therm_alarm);
+       spin_lock_init(&priv->lock);
+       spin_lock_init(&priv->sensor.alarm_program_lock);
+
+       priv->base.fan_get = nouveau_therm_fan_user_get;
+       priv->base.fan_set = nouveau_therm_fan_user_set;
+       priv->base.fan_sense = nouveau_therm_fan_sense;
+       priv->base.attr_get = nouveau_therm_attr_get;
+       priv->base.attr_set = nouveau_therm_attr_set;
+       priv->mode = priv->suspend = -1; /* undefined */
+       return 0;
+}
+
+int
+nouveau_therm_preinit(struct nouveau_therm *therm)
+{
+       nouveau_therm_ic_ctor(therm);
+       nouveau_therm_sensor_ctor(therm);
+       nouveau_therm_fan_ctor(therm);
+
+       nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_NONE);
+       return 0;
+}
+
+void
+_nouveau_therm_dtor(struct nouveau_object *object)
+{
+       struct nouveau_therm_priv *priv = (void *)object;
+       kfree(priv->fan);
+       nouveau_subdev_destroy(&priv->base.base);
+}
index 5231786..c728380 100644 (file)
 
 #include <core/object.h>
 #include <core/device.h>
+
 #include <subdev/gpio.h>
 #include <subdev/timer.h>
 
-int
-nouveau_therm_fan_get(struct nouveau_therm *therm)
+static int
+nouveau_fan_update(struct nouveau_fan *fan, bool immediate, int target)
 {
+       struct nouveau_therm *therm = fan->parent;
        struct nouveau_therm_priv *priv = (void *)therm;
-       struct nouveau_gpio *gpio = nouveau_gpio(therm);
-       struct dcb_gpio_func func;
-       int card_type = nv_device(therm)->card_type;
-       u32 divs, duty;
-       int ret;
-
-       if (!priv->fan.pwm_get)
-               return -ENODEV;
+       struct nouveau_timer *ptimer = nouveau_timer(priv);
+       unsigned long flags;
+       int ret = 0;
+       int duty;
+
+       /* update target fan speed, restricting to allowed range */
+       spin_lock_irqsave(&fan->lock, flags);
+       if (target < 0)
+               target = fan->percent;
+       target = max_t(u8, target, fan->bios.min_duty);
+       target = min_t(u8, target, fan->bios.max_duty);
+       if (fan->percent != target) {
+               nv_debug(therm, "FAN target: %d\n", target);
+               fan->percent = target;
+       }
 
-       ret = gpio->find(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, &func);
-       if (ret == 0) {
-               ret = priv->fan.pwm_get(therm, func.line, &divs, &duty);
-               if (ret == 0 && divs) {
-                       divs = max(divs, duty);
-                       if (card_type <= NV_40 || (func.log[0] & 1))
-                               duty = divs - duty;
-                       return (duty * 100) / divs;
-               }
+       /* check that we're not already at the target duty cycle */
+       duty = fan->get(therm);
+       if (duty == target)
+               goto done;
+
+       /* smooth out the fanspeed increase/decrease */
+       if (!immediate && duty >= 0) {
+               /* the constant "3" is a rough approximation taken from
+                * nvidia's behaviour.
+                * it is meant to bump the fan speed more incrementally
+                */
+               if (duty < target)
+                       duty = min(duty + 3, target);
+               else if (duty > target)
+                       duty = max(duty - 3, target);
+       } else {
+               duty = target;
+       }
 
-               return gpio->get(gpio, 0, func.func, func.line) * 100;
+       nv_debug(therm, "FAN update: %d\n", duty);
+       ret = fan->set(therm, duty);
+       if (ret)
+               goto done;
+
+       /* schedule next fan update, if not at target speed already */
+       if (list_empty(&fan->alarm.head) && target != duty) {
+               u16 bump_period = fan->bios.bump_period;
+               u16 slow_down_period = fan->bios.slow_down_period;
+               u64 delay;
+
+               if (duty > target)
+                       delay = slow_down_period;
+               else if (duty == target)
+                       delay = min(bump_period, slow_down_period) ;
+               else
+                       delay = bump_period;
+
+               ptimer->alarm(ptimer, delay * 1000 * 1000, &fan->alarm);
        }
 
-       return -ENODEV;
+done:
+       spin_unlock_irqrestore(&fan->lock, flags);
+       return ret;
+}
+
+static void
+nouveau_fan_alarm(struct nouveau_alarm *alarm)
+{
+       struct nouveau_fan *fan = container_of(alarm, struct nouveau_fan, alarm);
+       nouveau_fan_update(fan, false, -1);
 }
 
 int
-nouveau_therm_fan_set(struct nouveau_therm *therm, int percent)
+nouveau_therm_fan_get(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
-       struct nouveau_gpio *gpio = nouveau_gpio(therm);
-       struct dcb_gpio_func func;
-       int card_type = nv_device(therm)->card_type;
-       u32 divs, duty;
-       int ret;
-
-       if (priv->fan.mode == FAN_CONTROL_NONE)
-               return -EINVAL;
-
-       if (!priv->fan.pwm_set)
-               return -ENODEV;
-
-       if (percent < priv->bios_fan.min_duty)
-               percent = priv->bios_fan.min_duty;
-       if (percent > priv->bios_fan.max_duty)
-               percent = priv->bios_fan.max_duty;
-
-       ret = gpio->find(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, &func);
-       if (ret == 0) {
-               divs = priv->bios_perf_fan.pwm_divisor;
-               if (priv->bios_fan.pwm_freq) {
-                       divs = 1;
-                       if (priv->fan.pwm_clock)
-                               divs = priv->fan.pwm_clock(therm);
-                       divs /= priv->bios_fan.pwm_freq;
-               }
-
-               duty = ((divs * percent) + 99) / 100;
-               if (card_type <= NV_40 || (func.log[0] & 1))
-                       duty = divs - duty;
-
-               ret = priv->fan.pwm_set(therm, func.line, divs, duty);
-               return ret;
-       }
+       return priv->fan->get(therm);
+}
 
-       return -ENODEV;
+int
+nouveau_therm_fan_set(struct nouveau_therm *therm, bool immediate, int percent)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       return nouveau_fan_update(priv->fan, immediate, percent);
 }
 
 int
 nouveau_therm_fan_sense(struct nouveau_therm *therm)
 {
+       struct nouveau_therm_priv *priv = (void *)therm;
        struct nouveau_timer *ptimer = nouveau_timer(therm);
        struct nouveau_gpio *gpio = nouveau_gpio(therm);
-       struct dcb_gpio_func func;
        u32 cycles, cur, prev;
        u64 start, end, tach;
 
-       if (gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &func))
+       if (priv->fan->tach.func == DCB_GPIO_UNUSED)
                return -ENODEV;
 
        /* Time a complete rotation and extrapolate to RPM:
@@ -118,12 +135,12 @@ nouveau_therm_fan_sense(struct nouveau_therm *therm)
         * We get 4 changes (0 -> 1 -> 0 -> 1) per complete rotation.
         */
        start = ptimer->read(ptimer);
-       prev = gpio->get(gpio, 0, func.func, func.line);
+       prev = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line);
        cycles = 0;
        do {
                usleep_range(500, 1000); /* supports 0 < rpm < 7500 */
 
-               cur = gpio->get(gpio, 0, func.func, func.line);
+               cur = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line);
                if (prev != cur) {
                        if (!start)
                                start = ptimer->read(ptimer);
@@ -142,34 +159,6 @@ nouveau_therm_fan_sense(struct nouveau_therm *therm)
 }
 
 int
-nouveau_therm_fan_set_mode(struct nouveau_therm *therm,
-                          enum nouveau_therm_fan_mode mode)
-{
-       struct nouveau_therm_priv *priv = (void *)therm;
-
-       if (priv->fan.mode == mode)
-               return 0;
-
-       if (mode < FAN_CONTROL_NONE || mode >= FAN_CONTROL_NR)
-               return -EINVAL;
-
-       switch (mode)
-       {
-       case FAN_CONTROL_NONE:
-               nv_info(therm, "switch fan to no-control mode\n");
-               break;
-       case FAN_CONTROL_MANUAL:
-               nv_info(therm, "switch fan to manual mode\n");
-               break;
-       case FAN_CONTROL_NR:
-               break;
-       }
-
-       priv->fan.mode = mode;
-       return 0;
-}
-
-int
 nouveau_therm_fan_user_get(struct nouveau_therm *therm)
 {
        return nouveau_therm_fan_get(therm);
@@ -180,55 +169,86 @@ nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
 
-       if (priv->fan.mode != FAN_CONTROL_MANUAL)
+       if (priv->mode != NOUVEAU_THERM_CTRL_MANUAL)
                return -EINVAL;
 
-       return nouveau_therm_fan_set(therm, percent);
+       return nouveau_therm_fan_set(therm, true, percent);
 }
 
-void
+static void
 nouveau_therm_fan_set_defaults(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
 
-       priv->bios_fan.pwm_freq = 0;
-       priv->bios_fan.min_duty = 0;
-       priv->bios_fan.max_duty = 100;
+       priv->fan->bios.pwm_freq = 0;
+       priv->fan->bios.min_duty = 0;
+       priv->fan->bios.max_duty = 100;
+       priv->fan->bios.bump_period = 500;
+       priv->fan->bios.slow_down_period = 2000;
+       priv->fan->bios.linear_min_temp = 40;
+       priv->fan->bios.linear_max_temp = 85;
 }
 
-
 static void
 nouveau_therm_fan_safety_checks(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
 
-       if (priv->bios_fan.min_duty > 100)
-               priv->bios_fan.min_duty = 100;
-       if (priv->bios_fan.max_duty > 100)
-               priv->bios_fan.max_duty = 100;
+       if (priv->fan->bios.min_duty > 100)
+               priv->fan->bios.min_duty = 100;
+       if (priv->fan->bios.max_duty > 100)
+               priv->fan->bios.max_duty = 100;
 
-       if (priv->bios_fan.min_duty > priv->bios_fan.max_duty)
-               priv->bios_fan.min_duty = priv->bios_fan.max_duty;
-}
-
-int nouveau_fan_pwm_clock_dummy(struct nouveau_therm *therm)
-{
-       return 1;
+       if (priv->fan->bios.min_duty > priv->fan->bios.max_duty)
+               priv->fan->bios.min_duty = priv->fan->bios.max_duty;
 }
 
 int
 nouveau_therm_fan_ctor(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
+       struct nouveau_gpio *gpio = nouveau_gpio(therm);
        struct nouveau_bios *bios = nouveau_bios(therm);
+       struct dcb_gpio_func func;
+       int ret;
 
+       /* attempt to locate a drivable fan, and determine control method */
+       ret = gpio->find(gpio, 0, DCB_GPIO_FAN, 0xff, &func);
+       if (ret == 0) {
+               if (func.log[0] & DCB_GPIO_LOG_DIR_IN) {
+                       nv_debug(therm, "GPIO_FAN is in input mode\n");
+                       ret = -EINVAL;
+               } else {
+                       ret = nouveau_fanpwm_create(therm, &func);
+                       if (ret != 0)
+                               ret = nouveau_fantog_create(therm, &func);
+               }
+       }
+
+       /* no controllable fan found, create a dummy fan module */
+       if (ret != 0) {
+               ret = nouveau_fannil_create(therm);
+               if (ret)
+                       return ret;
+       }
+
+       nv_info(therm, "FAN control: %s\n", priv->fan->type);
+
+       /* attempt to detect a tachometer connection */
+       ret = gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &priv->fan->tach);
+       if (ret)
+               priv->fan->tach.func = DCB_GPIO_UNUSED;
+
+       /* initialise fan bump/slow update handling */
+       priv->fan->parent = therm;
+       nouveau_alarm_init(&priv->fan->alarm, nouveau_fan_alarm);
+       spin_lock_init(&priv->fan->lock);
+
+       /* other random init... */
        nouveau_therm_fan_set_defaults(therm);
-       nvbios_perf_fan_parse(bios, &priv->bios_perf_fan);
-       if (nvbios_therm_fan_parse(bios, &priv->bios_fan))
+       nvbios_perf_fan_parse(bios, &priv->fan->perf);
+       if (nvbios_therm_fan_parse(bios, &priv->fan->bios))
                nv_error(therm, "parsing the thermal table failed\n");
        nouveau_therm_fan_safety_checks(therm);
-
-       nouveau_therm_fan_set_mode(therm, FAN_CONTROL_NONE);
-
        return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fannil.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fannil.c
new file mode 100644 (file)
index 0000000..b78c182
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 "priv.h"
+
+static int
+nouveau_fannil_get(struct nouveau_therm *therm)
+{
+       return -ENODEV;
+}
+
+static int
+nouveau_fannil_set(struct nouveau_therm *therm, int percent)
+{
+       return -ENODEV;
+}
+
+int
+nouveau_fannil_create(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fan *priv;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       tpriv->fan = priv;
+       if (!priv)
+               return -ENOMEM;
+
+       priv->type = "none / external";
+       priv->get = nouveau_fannil_get;
+       priv->set = nouveau_fannil_set;
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c
new file mode 100644 (file)
index 0000000..5f71db8
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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
+ *         Martin Peres
+ */
+
+#include <core/option.h>
+#include <subdev/gpio.h>
+
+#include "priv.h"
+
+struct nouveau_fanpwm_priv {
+       struct nouveau_fan base;
+       struct dcb_gpio_func func;
+};
+
+static int
+nouveau_fanpwm_get(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fanpwm_priv *priv = (void *)tpriv->fan;
+       struct nouveau_gpio *gpio = nouveau_gpio(therm);
+       int card_type = nv_device(therm)->card_type;
+       u32 divs, duty;
+       int ret;
+
+       ret = therm->pwm_get(therm, priv->func.line, &divs, &duty);
+       if (ret == 0 && divs) {
+               divs = max(divs, duty);
+               if (card_type <= NV_40 || (priv->func.log[0] & 1))
+                       duty = divs - duty;
+               return (duty * 100) / divs;
+       }
+
+       return gpio->get(gpio, 0, priv->func.func, priv->func.line) * 100;
+}
+
+static int
+nouveau_fanpwm_set(struct nouveau_therm *therm, int percent)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fanpwm_priv *priv = (void *)tpriv->fan;
+       int card_type = nv_device(therm)->card_type;
+       u32 divs, duty;
+       int ret;
+
+       divs = priv->base.perf.pwm_divisor;
+       if (priv->base.bios.pwm_freq) {
+               divs = 1;
+               if (therm->pwm_clock)
+                       divs = therm->pwm_clock(therm);
+               divs /= priv->base.bios.pwm_freq;
+       }
+
+       duty = ((divs * percent) + 99) / 100;
+       if (card_type <= NV_40 || (priv->func.log[0] & 1))
+               duty = divs - duty;
+
+       ret = therm->pwm_set(therm, priv->func.line, divs, duty);
+       if (ret == 0)
+               ret = therm->pwm_ctrl(therm, priv->func.line, true);
+       return ret;
+}
+
+int
+nouveau_fanpwm_create(struct nouveau_therm *therm, struct dcb_gpio_func *func)
+{
+       struct nouveau_device *device = nv_device(therm);
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fanpwm_priv *priv;
+       u32 divs, duty;
+
+       if (!nouveau_boolopt(device->cfgopt, "NvFanPWM", func->param) ||
+           !therm->pwm_ctrl ||
+            therm->pwm_get(therm, func->line, &divs, &duty) == -ENODEV)
+               return -ENODEV;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       tpriv->fan = &priv->base;
+       if (!priv)
+               return -ENOMEM;
+
+       priv->base.type = "PWM";
+       priv->base.get = nouveau_fanpwm_get;
+       priv->base.set = nouveau_fanpwm_set;
+       priv->func = *func;
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c
new file mode 100644 (file)
index 0000000..e601773
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 The 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
+ */
+
+#include "priv.h"
+
+#include <core/object.h>
+#include <core/device.h>
+
+#include <subdev/gpio.h>
+#include <subdev/timer.h>
+
+struct nouveau_fantog_priv {
+       struct nouveau_fan base;
+       struct nouveau_alarm alarm;
+       spinlock_t lock;
+       u32 period_us;
+       u32 percent;
+       struct dcb_gpio_func func;
+};
+
+static void
+nouveau_fantog_update(struct nouveau_fantog_priv *priv, int percent)
+{
+       struct nouveau_therm_priv *tpriv = (void *)priv->base.parent;
+       struct nouveau_timer *ptimer = nouveau_timer(tpriv);
+       struct nouveau_gpio *gpio = nouveau_gpio(tpriv);
+       unsigned long flags;
+       int duty;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (percent < 0)
+               percent = priv->percent;
+       priv->percent = percent;
+
+       duty = !gpio->get(gpio, 0, DCB_GPIO_FAN, 0xff);
+       gpio->set(gpio, 0, DCB_GPIO_FAN, 0xff, duty);
+
+       if (list_empty(&priv->alarm.head) && percent != (duty * 100)) {
+               u64 next_change = (percent * priv->period_us) / 100;
+               if (!duty)
+                       next_change = priv->period_us - next_change;
+               ptimer->alarm(ptimer, next_change * 1000, &priv->alarm);
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void
+nouveau_fantog_alarm(struct nouveau_alarm *alarm)
+{
+       struct nouveau_fantog_priv *priv =
+              container_of(alarm, struct nouveau_fantog_priv, alarm);
+       nouveau_fantog_update(priv, -1);
+}
+
+static int
+nouveau_fantog_get(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fantog_priv *priv = (void *)tpriv->fan;
+       return priv->percent;
+}
+
+static int
+nouveau_fantog_set(struct nouveau_therm *therm, int percent)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fantog_priv *priv = (void *)tpriv->fan;
+       if (therm->pwm_ctrl)
+               therm->pwm_ctrl(therm, priv->func.line, false);
+       nouveau_fantog_update(priv, percent);
+       return 0;
+}
+
+int
+nouveau_fantog_create(struct nouveau_therm *therm, struct dcb_gpio_func *func)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fantog_priv *priv;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       tpriv->fan = &priv->base;
+       if (!priv)
+               return -ENOMEM;
+
+       priv->base.type = "toggle";
+       priv->base.get = nouveau_fantog_get;
+       priv->base.set = nouveau_fantog_set;
+       nouveau_alarm_init(&priv->alarm, nouveau_fantog_alarm);
+       priv->period_us = 100000; /* 10Hz */
+       priv->percent = 100;
+       priv->func = *func;
+       spin_lock_init(&priv->lock);
+       return 0;
+}
index e512ff0..e24090b 100644 (file)
@@ -31,7 +31,7 @@ static bool
 probe_monitoring_device(struct nouveau_i2c_port *i2c,
                        struct i2c_board_info *info)
 {
-       struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c->i2c);
+       struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c);
        struct i2c_client *client;
 
        request_module("%s%s", I2C_MODULE_PREFIX, info->type);
@@ -53,6 +53,31 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c,
        return true;
 }
 
+static struct i2c_board_info
+nv_board_infos[] = {
+       { I2C_BOARD_INFO("w83l785ts", 0x2d) },
+       { I2C_BOARD_INFO("w83781d", 0x2d) },
+       { I2C_BOARD_INFO("adt7473", 0x2e) },
+       { I2C_BOARD_INFO("adt7473", 0x2d) },
+       { I2C_BOARD_INFO("adt7473", 0x2c) },
+       { I2C_BOARD_INFO("f75375", 0x2e) },
+       { I2C_BOARD_INFO("lm99", 0x4c) },
+       { I2C_BOARD_INFO("lm90", 0x4c) },
+       { I2C_BOARD_INFO("lm90", 0x4d) },
+       { I2C_BOARD_INFO("adm1021", 0x18) },
+       { I2C_BOARD_INFO("adm1021", 0x19) },
+       { I2C_BOARD_INFO("adm1021", 0x1a) },
+       { I2C_BOARD_INFO("adm1021", 0x29) },
+       { I2C_BOARD_INFO("adm1021", 0x2a) },
+       { I2C_BOARD_INFO("adm1021", 0x2b) },
+       { I2C_BOARD_INFO("adm1021", 0x4c) },
+       { I2C_BOARD_INFO("adm1021", 0x4d) },
+       { I2C_BOARD_INFO("adm1021", 0x4e) },
+       { I2C_BOARD_INFO("lm63", 0x18) },
+       { I2C_BOARD_INFO("lm63", 0x4e) },
+       { }
+};
+
 void
 nouveau_therm_ic_ctor(struct nouveau_therm *therm)
 {
@@ -60,29 +85,6 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm)
        struct nouveau_bios *bios = nouveau_bios(therm);
        struct nouveau_i2c *i2c = nouveau_i2c(therm);
        struct nvbios_extdev_func extdev_entry;
-       struct i2c_board_info info[] = {
-               { I2C_BOARD_INFO("w83l785ts", 0x2d) },
-               { I2C_BOARD_INFO("w83781d", 0x2d) },
-               { I2C_BOARD_INFO("adt7473", 0x2e) },
-               { I2C_BOARD_INFO("adt7473", 0x2d) },
-               { I2C_BOARD_INFO("adt7473", 0x2c) },
-               { I2C_BOARD_INFO("f75375", 0x2e) },
-               { I2C_BOARD_INFO("lm99", 0x4c) },
-               { I2C_BOARD_INFO("lm90", 0x4c) },
-               { I2C_BOARD_INFO("lm90", 0x4d) },
-               { I2C_BOARD_INFO("adm1021", 0x18) },
-               { I2C_BOARD_INFO("adm1021", 0x19) },
-               { I2C_BOARD_INFO("adm1021", 0x1a) },
-               { I2C_BOARD_INFO("adm1021", 0x29) },
-               { I2C_BOARD_INFO("adm1021", 0x2a) },
-               { I2C_BOARD_INFO("adm1021", 0x2b) },
-               { I2C_BOARD_INFO("adm1021", 0x4c) },
-               { I2C_BOARD_INFO("adm1021", 0x4d) },
-               { I2C_BOARD_INFO("adm1021", 0x4e) },
-               { I2C_BOARD_INFO("lm63", 0x18) },
-               { I2C_BOARD_INFO("lm63", 0x4e) },
-               { }
-       };
 
        if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_LM89, &extdev_entry)) {
                struct i2c_board_info board[] = {
@@ -111,6 +113,6 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm)
        /* The vbios doesn't provide the address of an exisiting monitoring
           device. Let's try our static list.
         */
-       i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", info,
-                     probe_monitoring_device);
+       i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
+                     nv_board_infos, probe_monitoring_device);
 }
index fcf2cfe..0f5363e 100644 (file)
 
 #include "priv.h"
 
+struct nv40_therm_priv {
+       struct nouveau_therm_priv base;
+};
+
 static int
 nv40_sensor_setup(struct nouveau_therm *therm)
 {
@@ -34,6 +38,7 @@ nv40_sensor_setup(struct nouveau_therm *therm)
        if (device->chipset >= 0x46) {
                nv_mask(therm, 0x15b8, 0x80000000, 0);
                nv_wr32(therm, 0x15b0, 0x80003fff);
+               mdelay(10); /* wait for the temperature to stabilize */
                return nv_rd32(therm, 0x15b4) & 0x3fff;
        } else {
                nv_wr32(therm, 0x15b0, 0xff);
@@ -75,7 +80,20 @@ nv40_temp_get(struct nouveau_therm *therm)
        return core_temp;
 }
 
-int
+static int
+nv40_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable)
+{
+       u32 mask = enable ? 0x80000000 : 0x0000000;
+       if      (line == 2) nv_mask(therm, 0x0010f0, 0x80000000, mask);
+       else if (line == 9) nv_mask(therm, 0x0015f4, 0x80000000, mask);
+       else {
+               nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static int
 nv40_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
 {
        if (line == 2) {
@@ -101,15 +119,15 @@ nv40_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
        return -EINVAL;
 }
 
-int
+static int
 nv40_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
 {
        if (line == 2) {
-               nv_wr32(therm, 0x0010f0, 0x80000000 | (duty << 16) | divs);
+               nv_mask(therm, 0x0010f0, 0x7fff7fff, (duty << 16) | divs);
        } else
        if (line == 9) {
                nv_wr32(therm, 0x0015f8, divs);
-               nv_wr32(therm, 0x0015f4, duty | 0x80000000);
+               nv_mask(therm, 0x0015f4, 0x7fffffff, duty);
        } else {
                nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
                return -ENODEV;
@@ -118,37 +136,51 @@ nv40_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
        return 0;
 }
 
+static void
+nv40_therm_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_therm *therm = nouveau_therm(subdev);
+       uint32_t stat = nv_rd32(therm, 0x1100);
+
+       /* traitement */
+
+       /* ack all IRQs */
+       nv_wr32(therm, 0x1100, 0x70000);
+
+       nv_error(therm, "THERM received an IRQ: stat = %x\n", stat);
+}
+
 static int
 nv40_therm_ctor(struct nouveau_object *parent,
-                  struct nouveau_object *engine,
-                  struct nouveau_oclass *oclass, void *data, u32 size,
-                  struct nouveau_object **pobject)
+               struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
 {
-       struct nouveau_therm_priv *priv;
-       struct nouveau_therm *therm;
+       struct nv40_therm_priv *priv;
        int ret;
 
        ret = nouveau_therm_create(parent, engine, oclass, &priv);
        *pobject = nv_object(priv);
-       therm = (void *) priv;
        if (ret)
                return ret;
 
-       nouveau_therm_ic_ctor(therm);
-       nouveau_therm_sensor_ctor(therm);
-       nouveau_therm_fan_ctor(therm);
+       priv->base.base.pwm_ctrl = nv40_fan_pwm_ctrl;
+       priv->base.base.pwm_get = nv40_fan_pwm_get;
+       priv->base.base.pwm_set = nv40_fan_pwm_set;
+       priv->base.base.temp_get = nv40_temp_get;
+       priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
+       nv_subdev(priv)->intr = nv40_therm_intr;
+       return nouveau_therm_preinit(&priv->base.base);
+}
 
-       priv->fan.pwm_get = nv40_fan_pwm_get;
-       priv->fan.pwm_set = nv40_fan_pwm_set;
+static int
+nv40_therm_init(struct nouveau_object *object)
+{
+       struct nouveau_therm *therm = (void *)object;
 
-       therm->temp_get = nv40_temp_get;
-       therm->fan_get = nouveau_therm_fan_user_get;
-       therm->fan_set = nouveau_therm_fan_user_set;
-       therm->fan_sense = nouveau_therm_fan_sense;
-       therm->attr_get = nouveau_therm_attr_get;
-       therm->attr_set = nouveau_therm_attr_set;
+       nv40_sensor_setup(therm);
 
-       return 0;
+       return _nouveau_therm_init(object);
 }
 
 struct nouveau_oclass
@@ -157,7 +189,7 @@ nv40_therm_oclass = {
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv40_therm_ctor,
                .dtor = _nouveau_therm_dtor,
-               .init = nouveau_therm_init,
-               .fini = nouveau_therm_fini,
+               .init = nv40_therm_init,
+               .fini = _nouveau_therm_fini,
        },
-};
\ No newline at end of file
+};
index 9360ddd..86632cb 100644 (file)
 
 #include "priv.h"
 
+struct nv50_therm_priv {
+       struct nouveau_therm_priv base;
+};
+
 static int
 pwm_info(struct nouveau_therm *therm, int *line, int *ctrl, int *indx)
 {
@@ -51,6 +55,16 @@ pwm_info(struct nouveau_therm *therm, int *line, int *ctrl, int *indx)
 }
 
 int
+nv50_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable)
+{
+       u32 data = enable ? 0x00000001 : 0x00000000;
+       int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id);
+       if (ret == 0)
+               nv_mask(therm, ctrl, 0x00010001 << line, data << line);
+       return ret;
+}
+
+int
 nv50_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
 {
        int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id);
@@ -73,7 +87,6 @@ nv50_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
        if (ret)
                return ret;
 
-       nv_mask(therm, ctrl, 0x00010001 << line, 0x00000001 << line);
        nv_wr32(therm, 0x00e114 + (id * 8), divs);
        nv_wr32(therm, 0x00e118 + (id * 8), duty | 0x80000000);
        return 0;
@@ -111,38 +124,178 @@ nv50_temp_get(struct nouveau_therm *therm)
        return nv_rd32(therm, 0x20400);
 }
 
+static void
+nv50_therm_program_alarms(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
+
+       /* enable RISING and FALLING IRQs for shutdown, THRS 0, 1, 2 and 4 */
+       nv_wr32(therm, 0x20000, 0x000003ff);
+
+       /* shutdown: The computer should be shutdown when reached */
+       nv_wr32(therm, 0x20484, sensor->thrs_shutdown.hysteresis);
+       nv_wr32(therm, 0x20480, sensor->thrs_shutdown.temp);
+
+       /* THRS_1 : fan boost*/
+       nv_wr32(therm, 0x204c4, sensor->thrs_fan_boost.temp);
+
+       /* THRS_2 : critical */
+       nv_wr32(therm, 0x204c0, sensor->thrs_critical.temp);
+
+       /* THRS_4 : down clock */
+       nv_wr32(therm, 0x20414, sensor->thrs_down_clock.temp);
+       spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
+
+       nv_info(therm,
+               "Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
+               sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis,
+               sensor->thrs_down_clock.temp,
+               sensor->thrs_down_clock.hysteresis,
+               sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis,
+               sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis);
+
+}
+
+/* must be called with alarm_program_lock taken ! */
+static void
+nv50_therm_threshold_hyst_emulation(struct nouveau_therm *therm,
+                                  uint32_t thrs_reg, u8 status_bit,
+                                  const struct nvbios_therm_threshold *thrs,
+                                  enum nouveau_therm_thrs thrs_name)
+{
+       enum nouveau_therm_thrs_direction direction;
+       enum nouveau_therm_thrs_state prev_state, new_state;
+       int temp, cur;
+
+       prev_state = nouveau_therm_sensor_get_threshold_state(therm, thrs_name);
+       temp = nv_rd32(therm, thrs_reg);
+
+       /* program the next threshold */
+       if (temp == thrs->temp) {
+               nv_wr32(therm, thrs_reg, thrs->temp - thrs->hysteresis);
+               new_state = NOUVEAU_THERM_THRS_HIGHER;
+       } else {
+               nv_wr32(therm, thrs_reg, thrs->temp);
+               new_state = NOUVEAU_THERM_THRS_LOWER;
+       }
+
+       /* fix the state (in case someone reprogrammed the alarms) */
+       cur = therm->temp_get(therm);
+       if (new_state == NOUVEAU_THERM_THRS_LOWER && cur > thrs->temp)
+               new_state = NOUVEAU_THERM_THRS_HIGHER;
+       else if (new_state == NOUVEAU_THERM_THRS_HIGHER &&
+               cur < thrs->temp - thrs->hysteresis)
+               new_state = NOUVEAU_THERM_THRS_LOWER;
+       nouveau_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
+
+       /* find the direction */
+       if (prev_state < new_state)
+               direction = NOUVEAU_THERM_THRS_RISING;
+       else if (prev_state > new_state)
+               direction = NOUVEAU_THERM_THRS_FALLING;
+       else
+               return;
+
+       /* advertise a change in direction */
+       nouveau_therm_sensor_event(therm, thrs_name, direction);
+}
+
+static void
+nv50_therm_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_therm *therm = nouveau_therm(subdev);
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+       unsigned long flags;
+       uint32_t intr;
+
+       spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
+
+       intr = nv_rd32(therm, 0x20100);
+
+       /* THRS_4: downclock */
+       if (intr & 0x002) {
+               nv50_therm_threshold_hyst_emulation(therm, 0x20414, 24,
+                                                 &sensor->thrs_down_clock,
+                                                 NOUVEAU_THERM_THRS_DOWNCLOCK);
+               intr &= ~0x002;
+       }
+
+       /* shutdown */
+       if (intr & 0x004) {
+               nv50_therm_threshold_hyst_emulation(therm, 0x20480, 20,
+                                                  &sensor->thrs_shutdown,
+                                                  NOUVEAU_THERM_THRS_SHUTDOWN);
+               intr &= ~0x004;
+       }
+
+       /* THRS_1 : fan boost */
+       if (intr & 0x008) {
+               nv50_therm_threshold_hyst_emulation(therm, 0x204c4, 21,
+                                                  &sensor->thrs_fan_boost,
+                                                  NOUVEAU_THERM_THRS_FANBOOST);
+               intr &= ~0x008;
+       }
+
+       /* THRS_2 : critical */
+       if (intr & 0x010) {
+               nv50_therm_threshold_hyst_emulation(therm, 0x204c0, 22,
+                                                  &sensor->thrs_critical,
+                                                  NOUVEAU_THERM_THRS_CRITICAL);
+               intr &= ~0x010;
+       }
+
+       if (intr)
+               nv_error(therm, "unhandled intr 0x%08x\n", intr);
+
+       /* ACK everything */
+       nv_wr32(therm, 0x20100, 0xffffffff);
+       nv_wr32(therm, 0x1100, 0x10000); /* PBUS */
+
+       spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
+}
+
 static int
 nv50_therm_ctor(struct nouveau_object *parent,
-                  struct nouveau_object *engine,
-                  struct nouveau_oclass *oclass, void *data, u32 size,
-                  struct nouveau_object **pobject)
+               struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
 {
-       struct nouveau_therm_priv *priv;
-       struct nouveau_therm *therm;
+       struct nv50_therm_priv *priv;
        int ret;
 
        ret = nouveau_therm_create(parent, engine, oclass, &priv);
        *pobject = nv_object(priv);
-       therm = (void *) priv;
        if (ret)
                return ret;
 
-       nouveau_therm_ic_ctor(therm);
-       nouveau_therm_sensor_ctor(therm);
-       nouveau_therm_fan_ctor(therm);
+       priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl;
+       priv->base.base.pwm_get = nv50_fan_pwm_get;
+       priv->base.base.pwm_set = nv50_fan_pwm_set;
+       priv->base.base.pwm_clock = nv50_fan_pwm_clock;
+       priv->base.base.temp_get = nv50_temp_get;
+       priv->base.sensor.program_alarms = nv50_therm_program_alarms;
+       nv_subdev(priv)->intr = nv50_therm_intr;
 
-       priv->fan.pwm_get = nv50_fan_pwm_get;
-       priv->fan.pwm_set = nv50_fan_pwm_set;
-       priv->fan.pwm_clock = nv50_fan_pwm_clock;
+       /* init the thresholds */
+       nouveau_therm_sensor_set_threshold_state(&priv->base.base,
+                                                NOUVEAU_THERM_THRS_SHUTDOWN,
+                                                NOUVEAU_THERM_THRS_LOWER);
+       nouveau_therm_sensor_set_threshold_state(&priv->base.base,
+                                                NOUVEAU_THERM_THRS_FANBOOST,
+                                                NOUVEAU_THERM_THRS_LOWER);
+       nouveau_therm_sensor_set_threshold_state(&priv->base.base,
+                                                NOUVEAU_THERM_THRS_CRITICAL,
+                                                NOUVEAU_THERM_THRS_LOWER);
+       nouveau_therm_sensor_set_threshold_state(&priv->base.base,
+                                                NOUVEAU_THERM_THRS_DOWNCLOCK,
+                                                NOUVEAU_THERM_THRS_LOWER);
 
-       therm->temp_get = nv50_temp_get;
-       therm->fan_get = nouveau_therm_fan_user_get;
-       therm->fan_set = nouveau_therm_fan_user_set;
-       therm->fan_sense = nouveau_therm_fan_sense;
-       therm->attr_get = nouveau_therm_attr_get;
-       therm->attr_set = nouveau_therm_attr_set;
-
-       return 0;
+       return nouveau_therm_preinit(&priv->base.base);
 }
 
 struct nouveau_oclass
@@ -151,7 +304,7 @@ nv50_therm_oclass = {
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv50_therm_ctor,
                .dtor = _nouveau_therm_dtor,
-               .init = nouveau_therm_init,
-               .fini = nouveau_therm_fini,
+               .init = _nouveau_therm_init,
+               .fini = _nouveau_therm_fini,
        },
 };
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
new file mode 100644 (file)
index 0000000..2dcc543
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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/gpio.h>
+
+#include "priv.h"
+
+struct nva3_therm_priv {
+       struct nouveau_therm_priv base;
+};
+
+int
+nva3_therm_fan_sense(struct nouveau_therm *therm)
+{
+       u32 tach = nv_rd32(therm, 0x00e728) & 0x0000ffff;
+       u32 ctrl = nv_rd32(therm, 0x00e720);
+       if (ctrl & 0x00000001)
+               return tach * 60;
+       return -ENODEV;
+}
+
+static int
+nva3_therm_init(struct nouveau_object *object)
+{
+       struct nva3_therm_priv *priv = (void *)object;
+       struct dcb_gpio_func *tach = &priv->base.fan->tach;
+       int ret;
+
+       ret = nouveau_therm_init(&priv->base.base);
+       if (ret)
+               return ret;
+
+       /* enable fan tach, count revolutions per-second */
+       nv_mask(priv, 0x00e720, 0x00000003, 0x00000002);
+       if (tach->func != DCB_GPIO_UNUSED) {
+               nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000);
+               nv_mask(priv, 0x00e720, 0x001f0000, tach->line << 16);
+               nv_mask(priv, 0x00e720, 0x00000001, 0x00000001);
+       }
+       nv_mask(priv, 0x00e720, 0x00000002, 0x00000000);
+
+       return 0;
+}
+
+static int
+nva3_therm_ctor(struct nouveau_object *parent,
+               struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
+{
+       struct nva3_therm_priv *priv;
+       int ret;
+
+       ret = nouveau_therm_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl;
+       priv->base.base.pwm_get = nv50_fan_pwm_get;
+       priv->base.base.pwm_set = nv50_fan_pwm_set;
+       priv->base.base.pwm_clock = nv50_fan_pwm_clock;
+       priv->base.base.temp_get = nv50_temp_get;
+       priv->base.base.fan_sense = nva3_therm_fan_sense;
+       priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
+       return nouveau_therm_preinit(&priv->base.base);
+}
+
+struct nouveau_oclass
+nva3_therm_oclass = {
+       .handle = NV_SUBDEV(THERM, 0xa3),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nva3_therm_ctor,
+               .dtor = _nouveau_therm_dtor,
+               .init = nva3_therm_init,
+               .fini = _nouveau_therm_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
new file mode 100644 (file)
index 0000000..d7d30ee
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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 "priv.h"
+
+struct nvd0_therm_priv {
+       struct nouveau_therm_priv base;
+};
+
+static int
+pwm_info(struct nouveau_therm *therm, int line)
+{
+       u32 gpio = nv_rd32(therm, 0x00d610 + (line * 0x04));
+       switch (gpio & 0x000000c0) {
+       case 0x00000000: /* normal mode, possibly pwm forced off by us */
+       case 0x00000040: /* nvio special */
+               switch (gpio & 0x0000001f) {
+               case 0x19: return 1;
+               case 0x1c: return 0;
+               default:
+                       break;
+               }
+       default:
+               break;
+       }
+
+       nv_error(therm, "GPIO %d unknown PWM: 0x%08x\n", line, gpio);
+       return -ENODEV;
+}
+
+static int
+nvd0_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable)
+{
+       u32 data = enable ? 0x00000040 : 0x00000000;
+       int indx = pwm_info(therm, line);
+       if (indx < 0)
+               return indx;
+
+       nv_mask(therm, 0x00d610 + (line * 0x04), 0x000000c0, data);
+       return 0;
+}
+
+static int
+nvd0_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
+{
+       int indx = pwm_info(therm, line);
+       if (indx < 0)
+               return indx;
+
+       if (nv_rd32(therm, 0x00d610 + (line * 0x04)) & 0x00000040) {
+               *divs = nv_rd32(therm, 0x00e114 + (indx * 8));
+               *duty = nv_rd32(therm, 0x00e118 + (indx * 8));
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int
+nvd0_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
+{
+       int indx = pwm_info(therm, line);
+       if (indx < 0)
+               return indx;
+
+       nv_wr32(therm, 0x00e114 + (indx * 8), divs);
+       nv_wr32(therm, 0x00e118 + (indx * 8), duty | 0x80000000);
+       return 0;
+}
+
+static int
+nvd0_fan_pwm_clock(struct nouveau_therm *therm)
+{
+       return (nv_device(therm)->crystal * 1000) / 20;
+}
+
+static int
+nvd0_therm_init(struct nouveau_object *object)
+{
+       struct nvd0_therm_priv *priv = (void *)object;
+       int ret;
+
+       ret = nouveau_therm_init(&priv->base.base);
+       if (ret)
+               return ret;
+
+       /* enable fan tach, count revolutions per-second */
+       nv_mask(priv, 0x00e720, 0x00000003, 0x00000002);
+       if (priv->base.fan->tach.func != DCB_GPIO_UNUSED) {
+               nv_mask(priv, 0x00d79c, 0x000000ff, priv->base.fan->tach.line);
+               nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000);
+               nv_mask(priv, 0x00e720, 0x00000001, 0x00000001);
+       }
+       nv_mask(priv, 0x00e720, 0x00000002, 0x00000000);
+
+       return 0;
+}
+
+static int
+nvd0_therm_ctor(struct nouveau_object *parent,
+               struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
+{
+       struct nvd0_therm_priv *priv;
+       int ret;
+
+       ret = nouveau_therm_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.base.pwm_ctrl = nvd0_fan_pwm_ctrl;
+       priv->base.base.pwm_get = nvd0_fan_pwm_get;
+       priv->base.base.pwm_set = nvd0_fan_pwm_set;
+       priv->base.base.pwm_clock = nvd0_fan_pwm_clock;
+       priv->base.base.temp_get = nv50_temp_get;
+       priv->base.base.fan_sense = nva3_therm_fan_sense;
+       priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
+       return nouveau_therm_preinit(&priv->base.base);
+}
+
+struct nouveau_oclass
+nvd0_therm_oclass = {
+       .handle = NV_SUBDEV(THERM, 0xd0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvd0_therm_ctor,
+               .dtor = _nouveau_therm_dtor,
+               .init = nvd0_therm_init,
+               .fini = _nouveau_therm_fini,
+       },
+};
index 1c3cd6a..06b9870 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef __NVTHERM_PRIV_H__
+#define __NVTHERM_PRIV_H__
+
 /*
  * Copyright 2012 The Nouveau community
  *
 #include <subdev/therm.h>
 
 #include <subdev/bios/extdev.h>
+#include <subdev/bios/gpio.h>
 #include <subdev/bios/perf.h>
 #include <subdev/bios/therm.h>
+#include <subdev/timer.h>
+
+struct nouveau_fan {
+       struct nouveau_therm *parent;
+       const char *type;
+
+       struct nvbios_therm_fan bios;
+       struct nvbios_perf_fan perf;
+
+       struct nouveau_alarm alarm;
+       spinlock_t lock;
+       int percent;
+
+       int (*get)(struct nouveau_therm *therm);
+       int (*set)(struct nouveau_therm *therm, int percent);
+
+       struct dcb_gpio_func tach;
+};
+
+enum nouveau_therm_thrs_direction {
+       NOUVEAU_THERM_THRS_FALLING = 0,
+       NOUVEAU_THERM_THRS_RISING = 1
+};
+
+enum nouveau_therm_thrs_state {
+       NOUVEAU_THERM_THRS_LOWER = 0,
+       NOUVEAU_THERM_THRS_HIGHER = 1
+};
+
+enum nouveau_therm_thrs {
+       NOUVEAU_THERM_THRS_FANBOOST = 0,
+       NOUVEAU_THERM_THRS_DOWNCLOCK = 1,
+       NOUVEAU_THERM_THRS_CRITICAL = 2,
+       NOUVEAU_THERM_THRS_SHUTDOWN = 3,
+       NOUVEAU_THERM_THRS_NR
+};
 
 struct nouveau_therm_priv {
        struct nouveau_therm base;
 
+       /* automatic thermal management */
+       struct nouveau_alarm alarm;
+       spinlock_t lock;
+       struct nouveau_therm_trip_point *last_trip;
+       int mode;
+       int suspend;
+
        /* bios */
        struct nvbios_therm_sensor bios_sensor;
-       struct nvbios_therm_fan bios_fan;
-       struct nvbios_perf_fan bios_perf_fan;
 
        /* fan priv */
+       struct nouveau_fan *fan;
+
+       /* alarms priv */
        struct {
-               enum nouveau_therm_fan_mode mode;
-               int percent;
+               spinlock_t alarm_program_lock;
+               struct nouveau_alarm therm_poll_alarm;
+               enum nouveau_therm_thrs_state alarm_state[NOUVEAU_THERM_THRS_NR];
+               void (*program_alarms)(struct nouveau_therm *);
+       } sensor;
 
-               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 *);
-       } fan;
+       /* what should be done if the card overheats */
+       struct {
+               void (*downclock)(struct nouveau_therm *, bool active);
+               void (*pause)(struct nouveau_therm *, bool active);
+       } emergency;
 
        /* ic */
        struct i2c_client *ic;
 };
 
-int nouveau_therm_init(struct nouveau_object *object);
-int nouveau_therm_fini(struct nouveau_object *object, bool suspend);
+int nouveau_therm_mode(struct nouveau_therm *therm, int mode);
 int nouveau_therm_attr_get(struct nouveau_therm *therm,
                       enum nouveau_therm_attr_type type);
 int nouveau_therm_attr_set(struct nouveau_therm *therm,
@@ -63,11 +114,35 @@ int nouveau_therm_sensor_ctor(struct nouveau_therm *therm);
 
 int nouveau_therm_fan_ctor(struct nouveau_therm *therm);
 int nouveau_therm_fan_get(struct nouveau_therm *therm);
-int nouveau_therm_fan_set(struct nouveau_therm *therm, int percent);
+int nouveau_therm_fan_set(struct nouveau_therm *therm, bool now, int percent);
 int nouveau_therm_fan_user_get(struct nouveau_therm *therm);
 int nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent);
-int nouveau_therm_fan_set_mode(struct nouveau_therm *therm,
-                          enum nouveau_therm_fan_mode mode);
-
 
 int nouveau_therm_fan_sense(struct nouveau_therm *therm);
+
+int nouveau_therm_preinit(struct nouveau_therm *);
+
+void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm,
+                                            enum nouveau_therm_thrs thrs,
+                                            enum nouveau_therm_thrs_state st);
+enum nouveau_therm_thrs_state
+nouveau_therm_sensor_get_threshold_state(struct nouveau_therm *therm,
+                                        enum nouveau_therm_thrs thrs);
+void nouveau_therm_sensor_event(struct nouveau_therm *therm,
+                               enum nouveau_therm_thrs thrs,
+                               enum nouveau_therm_thrs_direction dir);
+void nouveau_therm_program_alarms_polling(struct nouveau_therm *therm);
+
+int nv50_fan_pwm_ctrl(struct nouveau_therm *, int, bool);
+int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *);
+int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32);
+int nv50_fan_pwm_clock(struct nouveau_therm *);
+int nv50_temp_get(struct nouveau_therm *therm);
+
+int nva3_therm_fan_sense(struct nouveau_therm *);
+
+int nouveau_fanpwm_create(struct nouveau_therm *, struct dcb_gpio_func *);
+int nouveau_fantog_create(struct nouveau_therm *, struct dcb_gpio_func *);
+int nouveau_fannil_create(struct nouveau_therm *);
+
+#endif
index 2042823..b37624a 100644 (file)
@@ -58,11 +58,171 @@ static void
 nouveau_therm_temp_safety_checks(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
+       struct nvbios_therm_sensor *s = &priv->bios_sensor;
 
        if (!priv->bios_sensor.slope_div)
                priv->bios_sensor.slope_div = 1;
        if (!priv->bios_sensor.offset_den)
                priv->bios_sensor.offset_den = 1;
+
+       /* enforce a minimum hysteresis on thresholds */
+       s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2);
+       s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2);
+       s->thrs_critical.hysteresis = max_t(u8, s->thrs_critical.hysteresis, 2);
+       s->thrs_shutdown.hysteresis = max_t(u8, s->thrs_shutdown.hysteresis, 2);
+}
+
+/* must be called with alarm_program_lock taken ! */
+void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm,
+                                            enum nouveau_therm_thrs thrs,
+                                            enum nouveau_therm_thrs_state st)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       priv->sensor.alarm_state[thrs] = st;
+}
+
+/* must be called with alarm_program_lock taken ! */
+enum nouveau_therm_thrs_state
+nouveau_therm_sensor_get_threshold_state(struct nouveau_therm *therm,
+                                        enum nouveau_therm_thrs thrs)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       return priv->sensor.alarm_state[thrs];
+}
+
+static void
+nv_poweroff_work(struct work_struct *work)
+{
+       orderly_poweroff(true);
+       kfree(work);
+}
+
+void nouveau_therm_sensor_event(struct nouveau_therm *therm,
+                               enum nouveau_therm_thrs thrs,
+                               enum nouveau_therm_thrs_direction dir)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       bool active;
+       const char *thresolds[] = {
+               "fanboost", "downclock", "critical", "shutdown"
+       };
+       uint8_t temperature = therm->temp_get(therm);
+
+       if (thrs < 0 || thrs > 3)
+               return;
+
+       if (dir == NOUVEAU_THERM_THRS_FALLING)
+               nv_info(therm, "temperature (%u C) went below the '%s' threshold\n",
+                       temperature, thresolds[thrs]);
+       else
+               nv_info(therm, "temperature (%u C) hit the '%s' threshold\n",
+                       temperature, thresolds[thrs]);
+
+       active = (dir == NOUVEAU_THERM_THRS_RISING);
+       switch (thrs) {
+       case NOUVEAU_THERM_THRS_FANBOOST:
+               if (active) {
+                       nouveau_therm_fan_set(therm, true, 100);
+                       nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_AUTO);
+               }
+               break;
+       case NOUVEAU_THERM_THRS_DOWNCLOCK:
+               if (priv->emergency.downclock)
+                       priv->emergency.downclock(therm, active);
+               break;
+       case NOUVEAU_THERM_THRS_CRITICAL:
+               if (priv->emergency.pause)
+                       priv->emergency.pause(therm, active);
+               break;
+       case NOUVEAU_THERM_THRS_SHUTDOWN:
+               if (active) {
+                       struct work_struct *work;
+
+                       work = kmalloc(sizeof(*work), GFP_ATOMIC);
+                       if (work) {
+                               INIT_WORK(work, nv_poweroff_work);
+                               schedule_work(work);
+                       }
+               }
+               break;
+       case NOUVEAU_THERM_THRS_NR:
+               break;
+       }
+
+}
+
+/* must be called with alarm_program_lock taken ! */
+static void
+nouveau_therm_threshold_hyst_polling(struct nouveau_therm *therm,
+                                  const struct nvbios_therm_threshold *thrs,
+                                  enum nouveau_therm_thrs thrs_name)
+{
+       enum nouveau_therm_thrs_direction direction;
+       enum nouveau_therm_thrs_state prev_state, new_state;
+       int temp = therm->temp_get(therm);
+
+       prev_state = nouveau_therm_sensor_get_threshold_state(therm, thrs_name);
+
+       if (temp >= thrs->temp && prev_state == NOUVEAU_THERM_THRS_LOWER) {
+               direction = NOUVEAU_THERM_THRS_RISING;
+               new_state = NOUVEAU_THERM_THRS_HIGHER;
+       } else if (temp <= thrs->temp - thrs->hysteresis &&
+                       prev_state == NOUVEAU_THERM_THRS_HIGHER) {
+               direction = NOUVEAU_THERM_THRS_FALLING;
+               new_state = NOUVEAU_THERM_THRS_LOWER;
+       } else
+               return; /* nothing to do */
+
+       nouveau_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
+       nouveau_therm_sensor_event(therm, thrs_name, direction);
+}
+
+static void
+alarm_timer_callback(struct nouveau_alarm *alarm)
+{
+       struct nouveau_therm_priv *priv =
+       container_of(alarm, struct nouveau_therm_priv, sensor.therm_poll_alarm);
+       struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+       struct nouveau_timer *ptimer = nouveau_timer(priv);
+       struct nouveau_therm *therm = &priv->base;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
+
+       nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost,
+                                            NOUVEAU_THERM_THRS_FANBOOST);
+
+       nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_down_clock,
+                                            NOUVEAU_THERM_THRS_DOWNCLOCK);
+
+       nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_critical,
+                                            NOUVEAU_THERM_THRS_CRITICAL);
+
+       nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown,
+                                            NOUVEAU_THERM_THRS_SHUTDOWN);
+
+       /* schedule the next poll in one second */
+       if (list_empty(&alarm->head))
+               ptimer->alarm(ptimer, 1000 * 1000 * 1000, alarm);
+
+       spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
+}
+
+void
+nouveau_therm_program_alarms_polling(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+
+       nv_info(therm,
+               "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
+               sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis,
+               sensor->thrs_down_clock.temp,
+               sensor->thrs_down_clock.hysteresis,
+               sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis,
+               sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis);
+
+       alarm_timer_callback(&priv->sensor.therm_poll_alarm);
 }
 
 int
@@ -71,6 +231,8 @@ nouveau_therm_sensor_ctor(struct nouveau_therm *therm)
        struct nouveau_therm_priv *priv = (void *)therm;
        struct nouveau_bios *bios = nouveau_bios(therm);
 
+       nouveau_alarm_init(&priv->sensor.therm_poll_alarm, alarm_timer_callback);
+
        nouveau_therm_temp_set_defaults(therm);
        if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
                                      &priv->bios_sensor))
index c26ca9b..8e1bae4 100644 (file)
@@ -79,7 +79,7 @@ nv04_timer_alarm_trigger(struct nouveau_timer *ptimer)
 
        /* execute any pending alarm handlers */
        list_for_each_entry_safe(alarm, atemp, &exec, head) {
-               list_del(&alarm->head);
+               list_del_init(&alarm->head);
                alarm->func(alarm);
        }
 }
index d0da230..74acf0f 100644 (file)
@@ -3,7 +3,7 @@
 
 #define ROM_BIOS_PAGE 4096
 
-#if defined(CONFIG_ACPI)
+#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
 bool nouveau_is_optimus(void);
 bool nouveau_is_v1_dsm(void);
 void nouveau_register_dsm_handler(void);
index f65b20a..5d94030 100644 (file)
@@ -84,6 +84,8 @@ nv40_backlight_init(struct drm_connector *connector)
        props.max_brightness = 31;
        bd = backlight_device_register("nv_backlight", &connector->kdev, drm,
                                       &nv40_bl_ops, &props);
+       if (IS_ERR(bd))
+               return PTR_ERR(bd);
        drm->backlight = bd;
        bd->props.brightness = nv40_get_intensity(bd);
        backlight_update_status(bd);
index 865eddf..50a6dd0 100644 (file)
@@ -678,23 +678,6 @@ int run_tmds_table(struct drm_device *dev, struct dcb_output *dcbent, int head,
        return 0;
 }
 
-static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint16_t offset)
-{
-       /*
-        * offset + 0  (8 bits): Micro version
-        * offset + 1  (8 bits): Minor version
-        * offset + 2  (8 bits): Chip version
-        * offset + 3  (8 bits): Major version
-        */
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       bios->major_version = bios->data[offset + 3];
-       bios->chip_version = bios->data[offset + 2];
-       NV_INFO(drm, "Bios version %02x.%02x.%02x.%02x\n",
-                bios->data[offset + 3], bios->data[offset + 2],
-                bios->data[offset + 1], bios->data[offset]);
-}
-
 static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset)
 {
        /*
@@ -710,12 +693,6 @@ static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset)
         */
 
        bios->init_script_tbls_ptr = ROM16(bios->data[offset]);
-       bios->macro_index_tbl_ptr = ROM16(bios->data[offset + 2]);
-       bios->macro_tbl_ptr = ROM16(bios->data[offset + 4]);
-       bios->condition_tbl_ptr = ROM16(bios->data[offset + 6]);
-       bios->io_condition_tbl_ptr = ROM16(bios->data[offset + 8]);
-       bios->io_flag_condition_tbl_ptr = ROM16(bios->data[offset + 10]);
-       bios->init_function_tbl_ptr = ROM16(bios->data[offset + 12]);
 }
 
 static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
@@ -765,25 +742,6 @@ static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, st
        return 0;
 }
 
-static int parse_bit_C_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
-{
-       /*
-        * offset + 8  (16 bits): PLL limits table pointer
-        *
-        * There's more in here, but that's unknown.
-        */
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       if (bitentry->length < 10) {
-               NV_ERROR(drm, "Do not understand BIT C table\n");
-               return -EINVAL;
-       }
-
-       bios->pll_limit_tbl_ptr = ROM16(bios->data[bitentry->offset + 8]);
-
-       return 0;
-}
-
 static int parse_bit_display_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
 {
        /*
@@ -821,12 +779,6 @@ static int parse_bit_init_tbl_entry(struct drm_device *dev, struct nvbios *bios,
        }
 
        parse_script_table_pointers(bios, bitentry->offset);
-
-       if (bitentry->length >= 16)
-               bios->some_script_ptr = ROM16(bios->data[bitentry->offset + 14]);
-       if (bitentry->length >= 18)
-               bios->init96_tbl_ptr = ROM16(bios->data[bitentry->offset + 16]);
-
        return 0;
 }
 
@@ -852,8 +804,6 @@ static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, st
                return -EINVAL;
        }
 
-       parse_bios_version(dev, bios, bitentry->offset);
-
        /*
         * bit 4 seems to indicate a mobile bios (doesn't suffer from BMP's
         * Quadro identity crisis), other bits possibly as for BMP feature byte
@@ -1078,9 +1028,6 @@ parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset)
                return ret;
        if (bios->major_version >= 0x60) /* g80+ */
                parse_bit_table(bios, bitoffset, &BIT_TABLE('A', A));
-       ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('C', C));
-       if (ret)
-               return ret;
        parse_bit_table(bios, bitoffset, &BIT_TABLE('D', display));
        ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('I', init));
        if (ret)
@@ -1228,8 +1175,6 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
         */
        bios->feature_byte = bmp[9];
 
-       parse_bios_version(dev, bios, offset + 10);
-
        if (bmp_version_major < 5 || bmp_version_minor < 0x10)
                bios->old_style_init = true;
        legacy_scripts_offset = 18;
@@ -1276,8 +1221,10 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
                bios->fp.lvdsmanufacturerpointer = ROM16(bmp[117]);
                bios->fp.fpxlatemanufacturertableptr = ROM16(bmp[119]);
        }
+#if 0
        if (bmplength > 143)
                bios->pll_limit_tbl_ptr = ROM16(bmp[142]);
+#endif
 
        if (bmplength > 157)
                bios->fp.duallink_transition_clk = ROM16(bmp[156]) * 10;
@@ -1522,6 +1469,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
        }
        case DCB_OUTPUT_DP:
                entry->dpconf.sor.link = (conf & 0x00000030) >> 4;
+               entry->extdev = (conf & 0x0000ff00) >> 8;
                switch ((conf & 0x00e00000) >> 21) {
                case 0:
                        entry->dpconf.link_bw = 162000;
@@ -1543,8 +1491,10 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
                }
                break;
        case DCB_OUTPUT_TMDS:
-               if (dcb->version >= 0x40)
+               if (dcb->version >= 0x40) {
                        entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
+                       entry->extdev = (conf & 0x0000ff00) >> 8;
+               }
                else if (dcb->version >= 0x30)
                        entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8;
                else if (dcb->version >= 0x22)
@@ -1937,9 +1887,9 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
                if (conn[0] != 0xff) {
                        NV_INFO(drm, "DCB conn %02d: ", idx);
                        if (olddcb_conntab(dev)[3] < 4)
-                               printk("%04x\n", ROM16(conn[0]));
+                               pr_cont("%04x\n", ROM16(conn[0]));
                        else
-                               printk("%08x\n", ROM32(conn[0]));
+                               pr_cont("%08x\n", ROM32(conn[0]));
                }
        }
        dcb_fake_connectors(bios);
@@ -2052,45 +2002,29 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
 static bool NVInitVBIOS(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nvbios *bios = &drm->vbios;
-
-       memset(bios, 0, sizeof(struct nvbios));
-       spin_lock_init(&bios->lock);
-       bios->dev = dev;
-
-       bios->data = nouveau_bios(drm->device)->data;
-       bios->length = nouveau_bios(drm->device)->size;
-       return true;
-}
+       struct nouveau_bios *bios = nouveau_bios(drm->device);
+       struct nvbios *legacy = &drm->vbios;
+
+       memset(legacy, 0, sizeof(struct nvbios));
+       spin_lock_init(&legacy->lock);
+       legacy->dev = dev;
+
+       legacy->data = bios->data;
+       legacy->length = bios->size;
+       legacy->major_version = bios->version.major;
+       legacy->chip_version = bios->version.chip;
+       if (bios->bit_offset) {
+               legacy->type = NVBIOS_BIT;
+               legacy->offset = bios->bit_offset;
+               return !parse_bit_structure(legacy, legacy->offset + 6);
+       } else
+       if (bios->bmp_offset) {
+               legacy->type = NVBIOS_BMP;
+               legacy->offset = bios->bmp_offset;
+               return !parse_bmp_structure(dev, legacy, legacy->offset);
+       }
 
-static int nouveau_parse_vbios_struct(struct drm_device *dev)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nvbios *bios = &drm->vbios;
-       const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' };
-       const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 };
-       int offset;
-
-       offset = findstr(bios->data, bios->length,
-                                       bit_signature, sizeof(bit_signature));
-       if (offset) {
-               NV_INFO(drm, "BIT BIOS found\n");
-               bios->type = NVBIOS_BIT;
-               bios->offset = offset;
-               return parse_bit_structure(bios, offset + 6);
-       }
-
-       offset = findstr(bios->data, bios->length,
-                                       bmp_signature, sizeof(bmp_signature));
-       if (offset) {
-               NV_INFO(drm, "BMP BIOS found\n");
-               bios->type = NVBIOS_BMP;
-               bios->offset = offset;
-               return parse_bmp_structure(dev, bios, offset);
-       }
-
-       NV_ERROR(drm, "No known BIOS signature found\n");
-       return -ENODEV;
+       return false;
 }
 
 int
@@ -2146,10 +2080,6 @@ nouveau_bios_init(struct drm_device *dev)
        if (!NVInitVBIOS(dev))
                return -ENODEV;
 
-       ret = nouveau_parse_vbios_struct(dev);
-       if (ret)
-               return ret;
-
        ret = parse_dcb_table(dev, bios);
        if (ret)
                return ret;
index f68c54c..7ccd28f 100644 (file)
@@ -107,20 +107,10 @@ struct nvbios {
        bool old_style_init;
        uint16_t init_script_tbls_ptr;
        uint16_t extra_init_script_tbl_ptr;
-       uint16_t macro_index_tbl_ptr;
-       uint16_t macro_tbl_ptr;
-       uint16_t condition_tbl_ptr;
-       uint16_t io_condition_tbl_ptr;
-       uint16_t io_flag_condition_tbl_ptr;
-       uint16_t init_function_tbl_ptr;
-
-       uint16_t pll_limit_tbl_ptr;
+
        uint16_t ram_restrict_tbl_ptr;
        uint8_t ram_restrict_group_count;
 
-       uint16_t some_script_ptr; /* BIT I + 14 */
-       uint16_t init96_tbl_ptr; /* BIT I + 16 */
-
        struct dcb_table dcb;
 
        struct {
index 2f27414..11ca821 100644 (file)
@@ -562,7 +562,7 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan,
        struct nouveau_fence *fence = NULL;
        int ret;
 
-       ret = nouveau_fence_new(chan, &fence);
+       ret = nouveau_fence_new(chan, false, &fence);
        if (ret)
                return ret;
 
index 174300b..eaa80a2 100644 (file)
@@ -51,14 +51,15 @@ nouveau_channel_idle(struct nouveau_channel *chan)
        struct nouveau_fence *fence = NULL;
        int ret;
 
-       ret = nouveau_fence_new(chan, &fence);
+       ret = nouveau_fence_new(chan, false, &fence);
        if (!ret) {
                ret = nouveau_fence_wait(fence, false, false);
                nouveau_fence_unref(&fence);
        }
 
        if (ret)
-               NV_ERROR(cli, "failed to idle channel 0x%08x\n", chan->handle);
+               NV_ERROR(cli, "failed to idle channel 0x%08x [%s]\n",
+                        chan->handle, cli->base.name);
        return ret;
 }
 
index e620ba8..4dd7ae2 100644 (file)
@@ -55,8 +55,6 @@ MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)");
 static int nouveau_duallink = 1;
 module_param_named(duallink, nouveau_duallink, int, 0400);
 
-static void nouveau_connector_hotplug(void *, int);
-
 struct nouveau_encoder *
 find_encoder(struct drm_connector *connector, int type)
 {
@@ -100,22 +98,6 @@ static void
 nouveau_connector_destroy(struct drm_connector *connector)
 {
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
-       struct nouveau_gpio *gpio;
-       struct nouveau_drm *drm;
-       struct drm_device *dev;
-
-       if (!nv_connector)
-               return;
-
-       dev  = nv_connector->base.dev;
-       drm  = nouveau_drm(dev);
-       gpio = nouveau_gpio(drm->device);
-
-       if (gpio && nv_connector->hpd != DCB_GPIO_UNUSED) {
-               gpio->isr_del(gpio, 0, nv_connector->hpd, 0xff,
-                             nouveau_connector_hotplug, connector);
-       }
-
        kfree(nv_connector->edid);
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
@@ -130,7 +112,6 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
-       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct nouveau_i2c_port *port = NULL;
        int i, panel = -ENODEV;
 
@@ -160,8 +141,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
                        continue;
                nv_encoder = nouveau_encoder(obj_to_encoder(obj));
 
-               if (nv_encoder->dcb->i2c_index < 0xf)
-                       port = i2c->find(i2c, nv_encoder->dcb->i2c_index);
+               port = nv_encoder->i2c;
                if (port && nv_probe_i2c(port, 0x50)) {
                        *pnv_encoder = nv_encoder;
                        break;
@@ -399,9 +379,10 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
                struct edid *edid =
                        (struct edid *)nouveau_bios_embedded_edid(dev);
                if (edid) {
-                       nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
-                       *(nv_connector->edid) = *edid;
-                       status = connector_status_connected;
+                       nv_connector->edid =
+                                       kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
+                       if (nv_connector->edid)
+                               status = connector_status_connected;
                }
        }
 
@@ -911,6 +892,37 @@ nouveau_connector_funcs_lvds = {
        .force = nouveau_connector_force
 };
 
+static void
+nouveau_connector_hotplug_work(struct work_struct *work)
+{
+       struct nouveau_connector *nv_connector =
+               container_of(work, struct nouveau_connector, hpd_work);
+       struct drm_connector *connector = &nv_connector->base;
+       struct drm_device *dev = connector->dev;
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
+       bool plugged = gpio->get(gpio, 0, nv_connector->hpd.func, 0xff);
+
+       NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un",
+                drm_get_connector_name(connector));
+
+       if (plugged)
+               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
+       else
+               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+
+       drm_helper_hpd_irq_event(dev);
+}
+
+static int
+nouveau_connector_hotplug(struct nouveau_eventh *event, int index)
+{
+       struct nouveau_connector *nv_connector =
+               container_of(event, struct nouveau_connector, hpd_func);
+       schedule_work(&nv_connector->hpd_work);
+       return NVKM_EVENT_KEEP;
+}
+
 static int
 drm_conntype_from_dcb(enum dcb_connector_type dcb)
 {
@@ -961,6 +973,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
                return ERR_PTR(-ENOMEM);
 
        connector = &nv_connector->base;
+       INIT_WORK(&nv_connector->hpd_work, nouveau_connector_hotplug_work);
        nv_connector->index = index;
 
        /* attempt to parse vbios connector type and hotplug gpio */
@@ -975,8 +988,11 @@ nouveau_connector_create(struct drm_device *dev, int index)
                if (olddcb_conntab(dev)[3] >= 4)
                        entry |= (u32)ROM16(nv_connector->dcb[2]) << 16;
 
-               nv_connector->hpd = ffs((entry & 0x07033000) >> 12);
-               nv_connector->hpd = hpd[nv_connector->hpd];
+               ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)],
+                                DCB_GPIO_UNUSED, &nv_connector->hpd);
+               nv_connector->hpd_func.func = nouveau_connector_hotplug;
+               if (ret)
+                       nv_connector->hpd.func = DCB_GPIO_UNUSED;
 
                nv_connector->type = nv_connector->dcb[0];
                if (drm_conntype_from_dcb(nv_connector->type) ==
@@ -999,7 +1015,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
                }
        } else {
                nv_connector->type = DCB_CONNECTOR_NONE;
-               nv_connector->hpd = DCB_GPIO_UNUSED;
+               nv_connector->hpd.func = DCB_GPIO_UNUSED;
        }
 
        /* no vbios data, or an unknown dcb connector type - attempt to
@@ -1126,31 +1142,9 @@ nouveau_connector_create(struct drm_device *dev, int index)
        }
 
        connector->polled = DRM_CONNECTOR_POLL_CONNECT;
-       if (gpio && nv_connector->hpd != DCB_GPIO_UNUSED) {
-               ret = gpio->isr_add(gpio, 0, nv_connector->hpd, 0xff,
-                                   nouveau_connector_hotplug, connector);
-               if (ret == 0)
-                       connector->polled = DRM_CONNECTOR_POLL_HPD;
-       }
+       if (nv_connector->hpd.func != DCB_GPIO_UNUSED)
+               connector->polled = DRM_CONNECTOR_POLL_HPD;
 
        drm_sysfs_connector_add(connector);
        return connector;
 }
-
-static void
-nouveau_connector_hotplug(void *data, int plugged)
-{
-       struct drm_connector *connector = data;
-       struct drm_device *dev = connector->dev;
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un",
-                drm_get_connector_name(connector));
-
-       if (plugged)
-               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
-       else
-               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
-
-       drm_helper_hpd_irq_event(dev);
-}
index 20eb84c..6e399aa 100644 (file)
 #include <drm/drm_edid.h>
 #include "nouveau_crtc.h"
 
+#include <core/event.h>
+
+#include <subdev/bios.h>
+#include <subdev/bios/gpio.h>
+
 struct nouveau_i2c_port;
 
 enum nouveau_underscan_type {
@@ -61,7 +66,10 @@ struct nouveau_connector {
        enum dcb_connector_type type;
        u8 index;
        u8 *dcb;
-       u8 hpd;
+
+       struct dcb_gpio_func hpd;
+       struct work_struct hpd_work;
+       struct nouveau_eventh hpd_func;
 
        int dithering_mode;
        int dithering_depth;
index d42c9e8..4610c3a 100644 (file)
@@ -41,6 +41,8 @@
 #include <subdev/gpio.h>
 #include <engine/disp.h>
 
+#include <core/class.h>
+
 static void
 nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
 {
@@ -231,8 +233,10 @@ nouveau_display_init(struct drm_device *dev)
        /* enable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (gpio)
-                       gpio->irq(gpio, 0, conn->hpd, 0xff, true);
+               if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
+                       nouveau_event_get(gpio->events, conn->hpd.line,
+                                        &conn->hpd_func);
+               }
        }
 
        return ret;
@@ -249,37 +253,20 @@ nouveau_display_fini(struct drm_device *dev)
        /* disable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (gpio)
-                       gpio->irq(gpio, 0, conn->hpd, 0xff, false);
+               if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
+                       nouveau_event_put(gpio->events, conn->hpd.line,
+                                        &conn->hpd_func);
+               }
        }
 
        drm_kms_helper_poll_disable(dev);
        disp->fini(dev);
 }
 
-static void
-nouveau_display_vblank_notify(void *data, int crtc)
-{
-       drm_handle_vblank(data, crtc);
-}
-
-static void
-nouveau_display_vblank_get(void *data, int crtc)
-{
-       drm_vblank_get(data, crtc);
-}
-
-static void
-nouveau_display_vblank_put(void *data, int crtc)
-{
-       drm_vblank_put(data, crtc);
-}
-
 int
 nouveau_display_create(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
        struct nouveau_display *disp;
        u32 pclass = dev->pdev->class >> 8;
        int ret, gen;
@@ -288,11 +275,6 @@ nouveau_display_create(struct drm_device *dev)
        if (!disp)
                return -ENOMEM;
 
-       pdisp->vblank.data = dev;
-       pdisp->vblank.notify = nouveau_display_vblank_notify;
-       pdisp->vblank.get = nouveau_display_vblank_get;
-       pdisp->vblank.put = nouveau_display_vblank_put;
-
        drm_mode_config_init(dev);
        drm_mode_create_scaling_mode_property(dev);
        drm_mode_create_dvi_i_properties(dev);
@@ -316,17 +298,13 @@ nouveau_display_create(struct drm_device *dev)
                drm_property_create_range(dev, 0, "underscan vborder", 0, 128);
 
        if (gen >= 1) {
+               /* -90..+90 */
                disp->vibrant_hue_property =
-                       drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                           "vibrant hue", 2);
-               disp->vibrant_hue_property->values[0] = 0;
-               disp->vibrant_hue_property->values[1] = 180; /* -90..+90 */
+                       drm_property_create_range(dev, 0, "vibrant hue", 0, 180);
 
+               /* -100..+100 */
                disp->color_vibrance_property =
-                       drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                           "color vibrance", 2);
-               disp->color_vibrance_property->values[0] = 0;
-               disp->color_vibrance_property->values[1] = 200; /* -100..+100 */
+                       drm_property_create_range(dev, 0, "color vibrance", 0, 200);
        }
 
        dev->mode_config.funcs = &nouveau_mode_config_funcs;
@@ -478,39 +456,6 @@ nouveau_display_resume(struct drm_device *dev)
        }
 }
 
-int
-nouveau_vblank_enable(struct drm_device *dev, int crtc)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-
-       if (device->card_type >= NV_D0)
-               nv_mask(device, 0x6100c0 + (crtc * 0x800), 1, 1);
-       else
-       if (device->card_type >= NV_50)
-               nv_mask(device, NV50_PDISPLAY_INTR_EN_1, 0,
-                       NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc));
-       else
-               NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0,
-                           NV_PCRTC_INTR_0_VBLANK);
-
-       return 0;
-}
-
-void
-nouveau_vblank_disable(struct drm_device *dev, int crtc)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-
-       if (device->card_type >= NV_D0)
-               nv_mask(device, 0x6100c0 + (crtc * 0x800), 1, 0);
-       else
-       if (device->card_type >= NV_50)
-               nv_mask(device, NV50_PDISPLAY_INTR_EN_1,
-                       NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc), 0);
-       else
-               NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, 0);
-}
-
 static int
 nouveau_page_flip_reserve(struct nouveau_bo *old_bo,
                          struct nouveau_bo *new_bo)
@@ -595,7 +540,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan,
        }
        FIRE_RING (chan);
 
-       ret = nouveau_fence_new(chan, pfence);
+       ret = nouveau_fence_new(chan, false, pfence);
        if (ret)
                goto fail;
 
index 722548b..1ea3e47 100644 (file)
@@ -59,9 +59,6 @@ void nouveau_display_fini(struct drm_device *dev);
 int  nouveau_display_suspend(struct drm_device *dev);
 void nouveau_display_resume(struct drm_device *dev);
 
-int  nouveau_vblank_enable(struct drm_device *dev, int crtc);
-void nouveau_vblank_disable(struct drm_device *dev, int crtc);
-
 int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                            struct drm_pending_vblank_event *event);
 int  nouveau_finish_page_flip(struct nouveau_channel *,
index 5c2e229..690d593 100644 (file)
@@ -191,7 +191,7 @@ WIND_RING(struct nouveau_channel *chan)
 #define NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG                    0x00000002
 #define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL                0x00000004
 #define NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD                         0x00001000
-#define NV84_SUBCHAN_NOTIFY_INTR                                     0x00000020
+#define NV84_SUBCHAN_UEVENT                                          0x00000020
 #define NV84_SUBCHAN_WRCACHE_FLUSH                                   0x00000024
 #define NV10_SUBCHAN_REF_CNT                                         0x00000050
 #define NVSW_SUBCHAN_PAGE_FLIP                                       0x00000054
index 5983865..36fd225 100644 (file)
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 
-/******************************************************************************
- * link training
- *****************************************************************************/
-struct dp_state {
-       struct nouveau_i2c_port *auxch;
-       struct nouveau_object *core;
-       struct dcb_output *dcb;
-       int crtc;
-       u8 *dpcd;
-       int link_nr;
-       u32 link_bw;
-       u8  stat[6];
-       u8  conf[4];
-};
-
-static void
-dp_set_link_config(struct drm_device *dev, struct dp_state *dp)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-       u8 sink[2];
-       u32 data;
-
-       NV_DEBUG(drm, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
-
-       /* set desired link configuration on the source */
-       data = ((dp->link_bw / 27000) << 8) | dp->link_nr;
-       if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP)
-               data |= NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH;
-
-       nv_call(dp->core, NV94_DISP_SOR_DP_LNKCTL + moff, data);
-
-       /* inform the sink of the new configuration */
-       sink[0] = dp->link_bw / 27000;
-       sink[1] = dp->link_nr;
-       if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP)
-               sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
-
-       nv_wraux(dp->auxch, DP_LINK_BW_SET, sink, 2);
-}
-
-static void
-dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-       u8 sink_tp;
-
-       NV_DEBUG(drm, "training pattern %d\n", pattern);
-
-       nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, pattern);
-
-       nv_rdaux(dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
-       sink_tp &= ~DP_TRAINING_PATTERN_MASK;
-       sink_tp |= pattern;
-       nv_wraux(dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
-}
-
-static int
-dp_link_train_commit(struct drm_device *dev, struct dp_state *dp)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-       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 == DP_TRAIN_VOLTAGE_SWING_1200)
-                       dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED;
-               if ((lpre << 3) == DP_TRAIN_PRE_EMPHASIS_9_5)
-                       dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
-
-               NV_DEBUG(drm, "config lane %d %02x\n", i, dp->conf[i]);
-
-               nv_call(dp->core, NV94_DISP_SOR_DP_DRVCTL(i) + moff, (lvsw << 8) | lpre);
-       }
-
-       return nv_wraux(dp->auxch, DP_TRAINING_LANE0_SET, dp->conf, 4);
-}
-
-static int
-dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       int ret;
-
-       udelay(delay);
-
-       ret = nv_rdaux(dp->auxch, DP_LANE0_1_STATUS, dp->stat, 6);
-       if (ret)
-               return ret;
-
-       NV_DEBUG(drm, "status %*ph\n", 6, dp->stat);
-       return 0;
-}
-
-static int
-dp_link_train_cr(struct drm_device *dev, struct dp_state *dp)
-{
-       bool cr_done = false, abort = false;
-       int voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
-       int tries = 0, i;
-
-       dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_1);
-
-       do {
-               if (dp_link_train_commit(dev, dp) ||
-                   dp_link_train_update(dev, 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 & DP_LANE_CR_DONE)) {
-                               cr_done = false;
-                               if (dp->conf[i] & DP_TRAIN_MAX_SWING_REACHED)
-                                       abort = true;
-                               break;
-                       }
-               }
-
-               if ((dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) {
-                       voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
-                       tries = 0;
-               }
-       } while (!cr_done && !abort && ++tries < 5);
-
-       return cr_done ? 0 : -1;
-}
-
-static int
-dp_link_train_eq(struct drm_device *dev, struct dp_state *dp)
-{
-       bool eq_done, cr_done = true;
-       int tries = 0, i;
-
-       dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2);
-
-       do {
-               if (dp_link_train_update(dev, dp, 400))
-                       break;
-
-               eq_done = !!(dp->stat[2] & DP_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 & DP_LANE_CR_DONE))
-                               cr_done = false;
-                       if (!(lane & DP_LANE_CHANNEL_EQ_DONE) ||
-                           !(lane & DP_LANE_SYMBOL_LOCKED))
-                               eq_done = false;
-               }
-
-               if (dp_link_train_commit(dev, dp))
-                       break;
-       } while (!eq_done && cr_done && ++tries <= 5);
-
-       return eq_done ? 0 : -1;
-}
-
-static void
-dp_link_train_init(struct drm_device *dev, struct dp_state *dp, bool spread)
-{
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-
-       nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, (spread ?
-                         NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON :
-                         NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF) |
-                         NV94_DISP_SOR_DP_TRAIN_OP_INIT);
-}
-
-static void
-dp_link_train_fini(struct drm_device *dev, struct dp_state *dp)
-{
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-
-       nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff,
-                         NV94_DISP_SOR_DP_TRAIN_OP_FINI);
-}
-
-static bool
-nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate,
-                     struct nouveau_object *core)
-{
-       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-       struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
-       struct nouveau_connector *nv_connector =
-               nouveau_encoder_connector_get(nv_encoder);
-       struct drm_device *dev = encoder->dev;
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
-       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
-       const u32 bw_list[] = { 270000, 162000, 0 };
-       const u32 *link_bw = bw_list;
-       struct dp_state dp;
-
-       dp.auxch = i2c->find(i2c, nv_encoder->dcb->i2c_index);
-       if (!dp.auxch)
-               return false;
-
-       dp.core = core;
-       dp.dcb = nv_encoder->dcb;
-       dp.crtc = nv_crtc->index;
-       dp.dpcd = nv_encoder->dp.dpcd;
-
-       /* adjust required bandwidth for 8B/10B coding overhead */
-       datarate = (datarate / 8) * 10;
-
-       /* some sinks toggle hotplug in response to some of the actions
-        * we take during link training (DP_SET_POWER is one), we need
-        * to ignore them for the moment to avoid races.
-        */
-       gpio->irq(gpio, 0, nv_connector->hpd, 0xff, false);
-
-       /* enable down-spreading and execute pre-train script from vbios */
-       dp_link_train_init(dev, &dp, nv_encoder->dp.dpcd[3] & 1);
-
-       /* start off at highest link rate supported by encoder and display */
-       while (*link_bw > nv_encoder->dp.link_bw)
-               link_bw++;
-
-       while (link_bw[0]) {
-               /* find minimum required lane count at this link rate */
-               dp.link_nr = nv_encoder->dp.link_nr;
-               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 */
-               dp_set_link_config(dev, &dp);
-
-               /* attempt to train the link at this configuration */
-               memset(dp.stat, 0x00, sizeof(dp.stat));
-               if (!dp_link_train_cr(dev, &dp) &&
-                   !dp_link_train_eq(dev, &dp))
-                       break;
-
-               /* retry at lower rate */
-               link_bw++;
-       }
-
-       /* finish link training */
-       dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE);
-
-       /* execute post-train script from vbios */
-       dp_link_train_fini(dev, &dp);
-
-       /* re-enable hotplug detect */
-       gpio->irq(gpio, 0, nv_connector->hpd, 0xff, true);
-       return true;
-}
-
-void
-nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate,
-               struct nouveau_object *core)
-{
-       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-       struct nouveau_drm *drm = nouveau_drm(encoder->dev);
-       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
-       struct nouveau_i2c_port *auxch;
-       u8 status;
-
-       auxch = i2c->find(i2c, nv_encoder->dcb->i2c_index);
-       if (!auxch)
-               return;
-
-       if (mode == DRM_MODE_DPMS_ON)
-               status = DP_SET_POWER_D0;
-       else
-               status = DP_SET_POWER_D3;
-
-       nv_wraux(auxch, DP_SET_POWER, &status, 1);
-
-       if (mode == DRM_MODE_DPMS_ON)
-               nouveau_dp_link_train(encoder, datarate, core);
-}
-
 static void
 nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch,
                     u8 *dpcd)
@@ -355,12 +61,11 @@ nouveau_dp_detect(struct drm_encoder *encoder)
        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
        struct drm_device *dev = encoder->dev;
        struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct nouveau_i2c_port *auxch;
        u8 *dpcd = nv_encoder->dp.dpcd;
        int ret;
 
-       auxch = i2c->find(i2c, nv_encoder->dcb->i2c_index);
+       auxch = nv_encoder->i2c;
        if (!auxch)
                return false;
 
index 8e8e8ce..ccfe2be 100644 (file)
@@ -34,6 +34,8 @@
 #include <subdev/device.h>
 #include <subdev/vm.h>
 
+#include <engine/disp.h>
+
 #include "nouveau_drm.h"
 #include "nouveau_irq.h"
 #include "nouveau_dma.h"
@@ -68,6 +70,32 @@ module_param_named(modeset, nouveau_modeset, int, 0400);
 
 static struct drm_driver driver;
 
+static int
+nouveau_drm_vblank_enable(struct drm_device *dev, int head)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
+       nouveau_event_get(pdisp->vblank, head, &drm->vblank);
+       return 0;
+}
+
+static void
+nouveau_drm_vblank_disable(struct drm_device *dev, int head)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
+       nouveau_event_put(pdisp->vblank, head, &drm->vblank);
+}
+
+static int
+nouveau_drm_vblank_handler(struct nouveau_eventh *event, int head)
+{
+       struct nouveau_drm *drm =
+               container_of(event, struct nouveau_drm, vblank);
+       drm_handle_vblank(drm->dev, head);
+       return NVKM_EVENT_KEEP;
+}
+
 static u64
 nouveau_name(struct pci_dev *pdev)
 {
@@ -132,7 +160,8 @@ nouveau_accel_init(struct nouveau_drm *drm)
 
        /* initialise synchronisation routines */
        if      (device->card_type < NV_10) ret = nv04_fence_create(drm);
-       else if (device->card_type < NV_50) ret = nv10_fence_create(drm);
+       else if (device->chipset   <  0x17) ret = nv10_fence_create(drm);
+       else if (device->card_type < NV_50) ret = nv17_fence_create(drm);
        else if (device->chipset   <  0x84) ret = nv50_fence_create(drm);
        else if (device->card_type < NV_C0) ret = nv84_fence_create(drm);
        else                                ret = nvc0_fence_create(drm);
@@ -259,6 +288,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 
        dev->dev_private = drm;
        drm->dev = dev;
+       drm->vblank.func = nouveau_drm_vblank_handler;
 
        INIT_LIST_HEAD(&drm->clients);
        spin_lock_init(&drm->tile.lock);
@@ -398,7 +428,7 @@ nouveau_drm_remove(struct pci_dev *pdev)
        nouveau_object_debug();
 }
 
-int
+static int
 nouveau_do_suspend(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
@@ -469,7 +499,7 @@ int nouveau_pmops_suspend(struct device *dev)
        return 0;
 }
 
-int
+static int
 nouveau_do_resume(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
@@ -543,10 +573,11 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
        struct pci_dev *pdev = dev->pdev;
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_cli *cli;
-       char name[16];
+       char name[32], tmpname[TASK_COMM_LEN];
        int ret;
 
-       snprintf(name, sizeof(name), "%d", pid_nr(fpriv->pid));
+       get_task_comm(tmpname, current);
+       snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid));
 
        ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli);
        if (ret)
@@ -642,8 +673,8 @@ driver = {
        .irq_handler = nouveau_irq_handler,
 
        .get_vblank_counter = drm_vblank_count,
-       .enable_vblank = nouveau_vblank_enable,
-       .disable_vblank = nouveau_vblank_disable,
+       .enable_vblank = nouveau_drm_vblank_enable,
+       .disable_vblank = nouveau_drm_vblank_disable,
 
        .ioctls = nouveau_ioctls,
        .fops = &nouveau_driver_fops,
index aa89eb9..b25df37 100644 (file)
@@ -13,6 +13,7 @@
 #define DRIVER_PATCHLEVEL      0
 
 #include <core/client.h>
+#include <core/event.h>
 
 #include <subdev/vm.h>
 
@@ -112,6 +113,7 @@ struct nouveau_drm {
        struct nvbios vbios;
        struct nouveau_display *display;
        struct backlight_device *backlight;
+       struct nouveau_eventh vblank;
 
        /* power management */
        struct nouveau_pm *pm;
index d0d95bd..e243412 100644 (file)
 
 struct nouveau_i2c_port;
 
-struct dp_train_func {
-       void (*link_set)(struct drm_device *, struct dcb_output *, int crtc,
-                        int nr, u32 bw, bool enhframe);
-       void (*train_set)(struct drm_device *, struct dcb_output *, u8 pattern);
-       void (*train_adj)(struct drm_device *, struct dcb_output *,
-                         u8 lane, u8 swing, u8 preem);
-};
-
 struct nouveau_encoder {
        struct drm_encoder_slave base;
 
        struct dcb_output *dcb;
        int or;
+       struct nouveau_i2c_port *i2c;
 
        /* different to drm_encoder.crtc, this reflects what's
         * actually programmed on the hw, not the proposed crtc */
index 1d049be..6c94683 100644 (file)
 #include "nouveau_dma.h"
 #include "nouveau_fence.h"
 
+#include <engine/fifo.h>
+
 void
 nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
 {
        struct nouveau_fence *fence, *fnext;
        spin_lock(&fctx->lock);
        list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
-               if (fence->work)
-                       fence->work(fence->priv, false);
                fence->channel = NULL;
                list_del(&fence->head);
                nouveau_fence_unref(&fence);
@@ -59,17 +59,14 @@ nouveau_fence_context_new(struct nouveau_fence_chan *fctx)
 static void
 nouveau_fence_update(struct nouveau_channel *chan)
 {
-       struct nouveau_fence_priv *priv = chan->drm->fence;
        struct nouveau_fence_chan *fctx = chan->fence;
        struct nouveau_fence *fence, *fnext;
 
        spin_lock(&fctx->lock);
        list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
-               if (priv->read(chan) < fence->sequence)
+               if (fctx->read(chan) < fence->sequence)
                        break;
 
-               if (fence->work)
-                       fence->work(fence->priv, true);
                fence->channel = NULL;
                list_del(&fence->head);
                nouveau_fence_unref(&fence);
@@ -80,7 +77,6 @@ nouveau_fence_update(struct nouveau_channel *chan)
 int
 nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
 {
-       struct nouveau_fence_priv *priv = chan->drm->fence;
        struct nouveau_fence_chan *fctx = chan->fence;
        int ret;
 
@@ -88,7 +84,7 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
        fence->timeout  = jiffies + (3 * DRM_HZ);
        fence->sequence = ++fctx->sequence;
 
-       ret = priv->emit(fence);
+       ret = fctx->emit(fence);
        if (!ret) {
                kref_get(&fence->kref);
                spin_lock(&fctx->lock);
@@ -107,13 +103,87 @@ nouveau_fence_done(struct nouveau_fence *fence)
        return !fence->channel;
 }
 
+struct nouveau_fence_uevent {
+       struct nouveau_eventh handler;
+       struct nouveau_fence_priv *priv;
+};
+
+static int
+nouveau_fence_wait_uevent_handler(struct nouveau_eventh *event, int index)
+{
+       struct nouveau_fence_uevent *uevent =
+               container_of(event, struct nouveau_fence_uevent, handler);
+       wake_up_all(&uevent->priv->waiting);
+       return NVKM_EVENT_KEEP;
+}
+
+static int
+nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
+
+{
+       struct nouveau_channel *chan = fence->channel;
+       struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device);
+       struct nouveau_fence_priv *priv = chan->drm->fence;
+       struct nouveau_fence_uevent uevent = {
+               .handler.func = nouveau_fence_wait_uevent_handler,
+               .priv = priv,
+       };
+       int ret = 0;
+
+       nouveau_event_get(pfifo->uevent, 0, &uevent.handler);
+
+       if (fence->timeout) {
+               unsigned long timeout = fence->timeout - jiffies;
+
+               if (time_before(jiffies, fence->timeout)) {
+                       if (intr) {
+                               ret = wait_event_interruptible_timeout(
+                                               priv->waiting,
+                                               nouveau_fence_done(fence),
+                                               timeout);
+                       } else {
+                               ret = wait_event_timeout(priv->waiting,
+                                               nouveau_fence_done(fence),
+                                               timeout);
+                       }
+               }
+
+               if (ret >= 0) {
+                       fence->timeout = jiffies + ret;
+                       if (time_after_eq(jiffies, fence->timeout))
+                               ret = -EBUSY;
+               }
+       } else {
+               if (intr) {
+                       ret = wait_event_interruptible(priv->waiting,
+                                       nouveau_fence_done(fence));
+               } else {
+                       wait_event(priv->waiting, nouveau_fence_done(fence));
+               }
+       }
+
+       nouveau_event_put(pfifo->uevent, 0, &uevent.handler);
+       if (unlikely(ret < 0))
+               return ret;
+
+       return 0;
+}
+
 int
 nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
 {
+       struct nouveau_channel *chan = fence->channel;
+       struct nouveau_fence_priv *priv = chan ? chan->drm->fence : NULL;
        unsigned long sleep_time = NSEC_PER_MSEC / 1000;
        ktime_t t;
        int ret = 0;
 
+       while (priv && priv->uevent && lazy && !nouveau_fence_done(fence)) {
+               ret = nouveau_fence_wait_uevent(fence, intr);
+               if (ret < 0)
+                       return ret;
+       }
+
        while (!nouveau_fence_done(fence)) {
                if (fence->timeout && time_after_eq(jiffies, fence->timeout)) {
                        ret = -EBUSY;
@@ -143,14 +213,14 @@ nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
 int
 nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan)
 {
-       struct nouveau_fence_priv *priv = chan->drm->fence;
+       struct nouveau_fence_chan *fctx = chan->fence;
        struct nouveau_channel *prev;
        int ret = 0;
 
        prev = fence ? fence->channel : NULL;
        if (prev) {
                if (unlikely(prev != chan && !nouveau_fence_done(fence))) {
-                       ret = priv->sync(fence, prev, chan);
+                       ret = fctx->sync(fence, prev, chan);
                        if (unlikely(ret))
                                ret = nouveau_fence_wait(fence, true, false);
                }
@@ -182,7 +252,8 @@ nouveau_fence_ref(struct nouveau_fence *fence)
 }
 
 int
-nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence)
+nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
+                 struct nouveau_fence **pfence)
 {
        struct nouveau_fence *fence;
        int ret = 0;
@@ -193,13 +264,13 @@ nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence)
        fence = kzalloc(sizeof(*fence), GFP_KERNEL);
        if (!fence)
                return -ENOMEM;
+
+       fence->sysmem = sysmem;
        kref_init(&fence->kref);
 
-       if (chan) {
-               ret = nouveau_fence_emit(fence, chan);
-               if (ret)
-                       nouveau_fence_unref(&fence);
-       }
+       ret = nouveau_fence_emit(fence, chan);
+       if (ret)
+               nouveau_fence_unref(&fence);
 
        *pfence = fence;
        return ret;
index cdb83ac..c899434 100644 (file)
@@ -7,15 +7,15 @@ struct nouveau_fence {
        struct list_head head;
        struct kref kref;
 
+       bool sysmem;
+
        struct nouveau_channel *channel;
        unsigned long timeout;
        u32 sequence;
-
-       void (*work)(void *priv, bool signalled);
-       void *priv;
 };
 
-int  nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **);
+int  nouveau_fence_new(struct nouveau_channel *, bool sysmem,
+                      struct nouveau_fence **);
 struct nouveau_fence *
 nouveau_fence_ref(struct nouveau_fence *);
 void nouveau_fence_unref(struct nouveau_fence **);
@@ -29,6 +29,13 @@ struct nouveau_fence_chan {
        struct list_head pending;
        struct list_head flip;
 
+       int  (*emit)(struct nouveau_fence *);
+       int  (*sync)(struct nouveau_fence *, struct nouveau_channel *,
+                    struct nouveau_channel *);
+       u32  (*read)(struct nouveau_channel *);
+       int  (*emit32)(struct nouveau_channel *, u64, u32);
+       int  (*sync32)(struct nouveau_channel *, u64, u32);
+
        spinlock_t lock;
        u32 sequence;
 };
@@ -39,10 +46,9 @@ struct nouveau_fence_priv {
        void (*resume)(struct nouveau_drm *);
        int  (*context_new)(struct nouveau_channel *);
        void (*context_del)(struct nouveau_channel *);
-       int  (*emit)(struct nouveau_fence *);
-       int  (*sync)(struct nouveau_fence *, struct nouveau_channel *,
-                    struct nouveau_channel *);
-       u32  (*read)(struct nouveau_channel *);
+
+       wait_queue_head_t waiting;
+       bool uevent;
 };
 
 #define nouveau_fence(drm) ((struct nouveau_fence_priv *)(drm)->fence)
@@ -60,13 +66,31 @@ u32  nv10_fence_read(struct nouveau_channel *);
 void nv10_fence_context_del(struct nouveau_channel *);
 void nv10_fence_destroy(struct nouveau_drm *);
 int  nv10_fence_create(struct nouveau_drm *);
+
+int  nv17_fence_create(struct nouveau_drm *);
 void nv17_fence_resume(struct nouveau_drm *drm);
 
 int nv50_fence_create(struct nouveau_drm *);
 int nv84_fence_create(struct nouveau_drm *);
 int nvc0_fence_create(struct nouveau_drm *);
-u64 nvc0_fence_crtc(struct nouveau_channel *, int crtc);
 
 int nouveau_flip_complete(void *chan);
 
+struct nv84_fence_chan {
+       struct nouveau_fence_chan base;
+       struct nouveau_vma vma;
+       struct nouveau_vma vma_gart;
+       struct nouveau_vma dispc_vma[4];
+};
+
+struct nv84_fence_priv {
+       struct nouveau_fence_priv base;
+       struct nouveau_bo *bo;
+       struct nouveau_bo *bo_gart;
+       u32 *suspend;
+};
+
+u64  nv84_fence_crtc(struct nouveau_channel *, int);
+int  nv84_fence_context_new(struct nouveau_channel *);
+
 #endif
index d98bee0..b4b4d0c 100644 (file)
@@ -203,6 +203,7 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
                      struct drm_file *file_priv)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
        struct nouveau_fb *pfb = nouveau_fb(drm->device);
        struct drm_nouveau_gem_new *req = data;
        struct nouveau_bo *nvbo = NULL;
@@ -211,7 +212,7 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
        drm->ttm.bdev.dev_mapping = drm->dev->dev_mapping;
 
        if (!pfb->memtype_valid(pfb, req->info.tile_flags)) {
-               NV_ERROR(drm, "bad page flags: 0x%08x\n", req->info.tile_flags);
+               NV_ERROR(cli, "bad page flags: 0x%08x\n", req->info.tile_flags);
                return -EINVAL;
        }
 
@@ -313,6 +314,7 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
              struct drm_nouveau_gem_pushbuf_bo *pbbo,
              int nr_buffers, struct validate_op *op)
 {
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
        struct drm_device *dev = chan->drm->dev;
        struct nouveau_drm *drm = nouveau_drm(dev);
        uint32_t sequence;
@@ -323,7 +325,7 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
        sequence = atomic_add_return(1, &drm->ttm.validate_sequence);
 retry:
        if (++trycnt > 100000) {
-               NV_ERROR(drm, "%s failed and gave up.\n", __func__);
+               NV_ERROR(cli, "%s failed and gave up.\n", __func__);
                return -EINVAL;
        }
 
@@ -334,7 +336,7 @@ retry:
 
                gem = drm_gem_object_lookup(dev, file_priv, b->handle);
                if (!gem) {
-                       NV_ERROR(drm, "Unknown handle 0x%08x\n", b->handle);
+                       NV_ERROR(cli, "Unknown handle 0x%08x\n", b->handle);
                        validate_fini(op, NULL);
                        return -ENOENT;
                }
@@ -346,7 +348,7 @@ retry:
                }
 
                if (nvbo->reserved_by && nvbo->reserved_by == file_priv) {
-                       NV_ERROR(drm, "multiple instances of buffer %d on "
+                       NV_ERROR(cli, "multiple instances of buffer %d on "
                                      "validation list\n", b->handle);
                        drm_gem_object_unreference_unlocked(gem);
                        validate_fini(op, NULL);
@@ -366,7 +368,7 @@ retry:
                        if (unlikely(ret)) {
                                drm_gem_object_unreference_unlocked(gem);
                                if (ret != -ERESTARTSYS)
-                                       NV_ERROR(drm, "fail reserve\n");
+                                       NV_ERROR(cli, "fail reserve\n");
                                return ret;
                        }
                }
@@ -384,7 +386,7 @@ retry:
                if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)
                        list_add_tail(&nvbo->entry, &op->gart_list);
                else {
-                       NV_ERROR(drm, "invalid valid domains: 0x%08x\n",
+                       NV_ERROR(cli, "invalid valid domains: 0x%08x\n",
                                 b->valid_domains);
                        list_add_tail(&nvbo->entry, &op->both_list);
                        validate_fini(op, NULL);
@@ -417,8 +419,9 @@ validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo)
 }
 
 static int
-validate_list(struct nouveau_channel *chan, struct list_head *list,
-             struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr)
+validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli,
+             struct list_head *list, struct drm_nouveau_gem_pushbuf_bo *pbbo,
+             uint64_t user_pbbo_ptr)
 {
        struct nouveau_drm *drm = chan->drm;
        struct drm_nouveau_gem_pushbuf_bo __user *upbbo =
@@ -431,7 +434,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
 
                ret = validate_sync(chan, nvbo);
                if (unlikely(ret)) {
-                       NV_ERROR(drm, "fail pre-validate sync\n");
+                       NV_ERROR(cli, "fail pre-validate sync\n");
                        return ret;
                }
 
@@ -439,20 +442,20 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
                                             b->write_domains,
                                             b->valid_domains);
                if (unlikely(ret)) {
-                       NV_ERROR(drm, "fail set_domain\n");
+                       NV_ERROR(cli, "fail set_domain\n");
                        return ret;
                }
 
                ret = nouveau_bo_validate(nvbo, true, false);
                if (unlikely(ret)) {
                        if (ret != -ERESTARTSYS)
-                               NV_ERROR(drm, "fail ttm_validate\n");
+                               NV_ERROR(cli, "fail ttm_validate\n");
                        return ret;
                }
 
                ret = validate_sync(chan, nvbo);
                if (unlikely(ret)) {
-                       NV_ERROR(drm, "fail post-validate sync\n");
+                       NV_ERROR(cli, "fail post-validate sync\n");
                        return ret;
                }
 
@@ -488,7 +491,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
                             uint64_t user_buffers, int nr_buffers,
                             struct validate_op *op, int *apply_relocs)
 {
-       struct nouveau_drm *drm = chan->drm;
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
        int ret, relocs = 0;
 
        INIT_LIST_HEAD(&op->vram_list);
@@ -501,32 +504,32 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
        ret = validate_init(chan, file_priv, pbbo, nr_buffers, op);
        if (unlikely(ret)) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate_init\n");
+                       NV_ERROR(cli, "validate_init\n");
                return ret;
        }
 
-       ret = validate_list(chan, &op->vram_list, pbbo, user_buffers);
+       ret = validate_list(chan, cli, &op->vram_list, pbbo, user_buffers);
        if (unlikely(ret < 0)) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate vram_list\n");
+                       NV_ERROR(cli, "validate vram_list\n");
                validate_fini(op, NULL);
                return ret;
        }
        relocs += ret;
 
-       ret = validate_list(chan, &op->gart_list, pbbo, user_buffers);
+       ret = validate_list(chan, cli, &op->gart_list, pbbo, user_buffers);
        if (unlikely(ret < 0)) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate gart_list\n");
+                       NV_ERROR(cli, "validate gart_list\n");
                validate_fini(op, NULL);
                return ret;
        }
        relocs += ret;
 
-       ret = validate_list(chan, &op->both_list, pbbo, user_buffers);
+       ret = validate_list(chan, cli, &op->both_list, pbbo, user_buffers);
        if (unlikely(ret < 0)) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate both_list\n");
+                       NV_ERROR(cli, "validate both_list\n");
                validate_fini(op, NULL);
                return ret;
        }
@@ -555,11 +558,10 @@ u_memcpya(uint64_t user, unsigned nmemb, unsigned size)
 }
 
 static int
-nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
+nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli,
                                struct drm_nouveau_gem_pushbuf *req,
                                struct drm_nouveau_gem_pushbuf_bo *bo)
 {
-       struct nouveau_drm *drm = nouveau_drm(dev);
        struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL;
        int ret = 0;
        unsigned i;
@@ -575,7 +577,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
                uint32_t data;
 
                if (unlikely(r->bo_index > req->nr_buffers)) {
-                       NV_ERROR(drm, "reloc bo index invalid\n");
+                       NV_ERROR(cli, "reloc bo index invalid\n");
                        ret = -EINVAL;
                        break;
                }
@@ -585,7 +587,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
                        continue;
 
                if (unlikely(r->reloc_bo_index > req->nr_buffers)) {
-                       NV_ERROR(drm, "reloc container bo index invalid\n");
+                       NV_ERROR(cli, "reloc container bo index invalid\n");
                        ret = -EINVAL;
                        break;
                }
@@ -593,7 +595,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
 
                if (unlikely(r->reloc_bo_offset + 4 >
                             nvbo->bo.mem.num_pages << PAGE_SHIFT)) {
-                       NV_ERROR(drm, "reloc outside of bo\n");
+                       NV_ERROR(cli, "reloc outside of bo\n");
                        ret = -EINVAL;
                        break;
                }
@@ -602,7 +604,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
                        ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages,
                                          &nvbo->kmap);
                        if (ret) {
-                               NV_ERROR(drm, "failed kmap for reloc\n");
+                               NV_ERROR(cli, "failed kmap for reloc\n");
                                break;
                        }
                        nvbo->validate_mapped = true;
@@ -627,7 +629,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
                ret = ttm_bo_wait(&nvbo->bo, false, false, false);
                spin_unlock(&nvbo->bo.bdev->fence_lock);
                if (ret) {
-                       NV_ERROR(drm, "reloc wait_idle failed: %d\n", ret);
+                       NV_ERROR(cli, "reloc wait_idle failed: %d\n", ret);
                        break;
                }
 
@@ -643,6 +645,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
                          struct drm_file *file_priv)
 {
        struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
        struct nouveau_abi16_chan *temp;
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct drm_nouveau_gem_pushbuf *req = data;
@@ -672,19 +675,19 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
                goto out_next;
 
        if (unlikely(req->nr_push > NOUVEAU_GEM_MAX_PUSH)) {
-               NV_ERROR(drm, "pushbuf push count exceeds limit: %d max %d\n",
+               NV_ERROR(cli, "pushbuf push count exceeds limit: %d max %d\n",
                         req->nr_push, NOUVEAU_GEM_MAX_PUSH);
                return nouveau_abi16_put(abi16, -EINVAL);
        }
 
        if (unlikely(req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS)) {
-               NV_ERROR(drm, "pushbuf bo count exceeds limit: %d max %d\n",
+               NV_ERROR(cli, "pushbuf bo count exceeds limit: %d max %d\n",
                         req->nr_buffers, NOUVEAU_GEM_MAX_BUFFERS);
                return nouveau_abi16_put(abi16, -EINVAL);
        }
 
        if (unlikely(req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS)) {
-               NV_ERROR(drm, "pushbuf reloc count exceeds limit: %d max %d\n",
+               NV_ERROR(cli, "pushbuf reloc count exceeds limit: %d max %d\n",
                         req->nr_relocs, NOUVEAU_GEM_MAX_RELOCS);
                return nouveau_abi16_put(abi16, -EINVAL);
        }
@@ -702,7 +705,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
        /* Ensure all push buffers are on validate list */
        for (i = 0; i < req->nr_push; i++) {
                if (push[i].bo_index >= req->nr_buffers) {
-                       NV_ERROR(drm, "push %d buffer not in list\n", i);
+                       NV_ERROR(cli, "push %d buffer not in list\n", i);
                        ret = -EINVAL;
                        goto out_prevalid;
                }
@@ -713,15 +716,15 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
                                           req->nr_buffers, &op, &do_reloc);
        if (ret) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate: %d\n", ret);
+                       NV_ERROR(cli, "validate: %d\n", ret);
                goto out_prevalid;
        }
 
        /* Apply any relocations that are required */
        if (do_reloc) {
-               ret = nouveau_gem_pushbuf_reloc_apply(dev, req, bo);
+               ret = nouveau_gem_pushbuf_reloc_apply(cli, req, bo);
                if (ret) {
-                       NV_ERROR(drm, "reloc apply: %d\n", ret);
+                       NV_ERROR(cli, "reloc apply: %d\n", ret);
                        goto out;
                }
        }
@@ -729,7 +732,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
        if (chan->dma.ib_max) {
                ret = nouveau_dma_wait(chan, req->nr_push + 1, 16);
                if (ret) {
-                       NV_ERROR(drm, "nv50cal_space: %d\n", ret);
+                       NV_ERROR(cli, "nv50cal_space: %d\n", ret);
                        goto out;
                }
 
@@ -744,7 +747,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
        if (nv_device(drm->device)->chipset >= 0x25) {
                ret = RING_SPACE(chan, req->nr_push * 2);
                if (ret) {
-                       NV_ERROR(drm, "cal_space: %d\n", ret);
+                       NV_ERROR(cli, "cal_space: %d\n", ret);
                        goto out;
                }
 
@@ -758,7 +761,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
        } else {
                ret = RING_SPACE(chan, req->nr_push * (2 + NOUVEAU_DMA_SKIPS));
                if (ret) {
-                       NV_ERROR(drm, "jmp_space: %d\n", ret);
+                       NV_ERROR(cli, "jmp_space: %d\n", ret);
                        goto out;
                }
 
@@ -794,9 +797,9 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
                }
        }
 
-       ret = nouveau_fence_new(chan, &fence);
+       ret = nouveau_fence_new(chan, false, &fence);
        if (ret) {
-               NV_ERROR(drm, "error fencing pushbuf: %d\n", ret);
+               NV_ERROR(cli, "error fencing pushbuf: %d\n", ret);
                WIND_RING(chan);
                goto out;
        }
index a701ff5..bb54098 100644 (file)
@@ -409,6 +409,81 @@ static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
                                                  NULL, 0);
 
 static ssize_t
+nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d,
+                                        struct device_attribute *a, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", 100);
+}
+static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO,
+                         nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0);
+
+static ssize_t
+nouveau_hwmon_temp1_auto_point1_temp(struct device *d,
+                                    struct device_attribute *a, char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+             therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d,
+                                        struct device_attribute *a,
+                                        const char *buf, size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO | S_IWUSR,
+                         nouveau_hwmon_temp1_auto_point1_temp,
+                         nouveau_hwmon_set_temp1_auto_point1_temp, 0);
+
+static ssize_t
+nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d,
+                                         struct device_attribute *a, char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+        therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d,
+                                             struct device_attribute *a,
+                                             const char *buf, size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+                         nouveau_hwmon_temp1_auto_point1_temp_hyst,
+                         nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0);
+
+static ssize_t
 nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
 {
        struct drm_device *dev = dev_get_drvdata(d);
@@ -439,6 +514,38 @@ static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
                                                  0);
 
 static ssize_t
+nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a,
+                           char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+         therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a,
+                                               const char *buf, size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
+                         nouveau_hwmon_max_temp_hyst,
+                         nouveau_hwmon_set_max_temp_hyst, 0);
+
+static ssize_t
 nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
                                                        char *buf)
 {
@@ -471,6 +578,107 @@ static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
                                                nouveau_hwmon_set_critical_temp,
                                                0);
 
+static ssize_t
+nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a,
+                                                       char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+         therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_critical_temp_hyst(struct device *d,
+                                    struct device_attribute *a,
+                                    const char *buf,
+                                    size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR,
+                         nouveau_hwmon_critical_temp_hyst,
+                         nouveau_hwmon_set_critical_temp_hyst, 0);
+static ssize_t
+nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a,
+                                                       char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+              therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a,
+                                                           const char *buf,
+                                                               size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN, value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO | S_IWUSR,
+                                       nouveau_hwmon_emergency_temp,
+                                       nouveau_hwmon_set_emergency_temp,
+                                       0);
+
+static ssize_t
+nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a,
+                                                       char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+         therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_emergency_temp_hyst(struct device *d,
+                                     struct device_attribute *a,
+                                     const char *buf,
+                                     size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO | S_IWUSR,
+                                       nouveau_hwmon_emergency_temp_hyst,
+                                       nouveau_hwmon_set_emergency_temp_hyst,
+                                       0);
+
 static ssize_t nouveau_hwmon_show_name(struct device *dev,
                                      struct device_attribute *attr,
                                      char *buf)
@@ -490,7 +698,7 @@ static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
                                                NULL, 0);
 
 static ssize_t
-nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr,
+nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr,
                              char *buf)
 {
        struct drm_device *dev = dev_get_drvdata(d);
@@ -499,7 +707,7 @@ nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr,
 
        return snprintf(buf, PAGE_SIZE, "%d\n", therm->fan_sense(therm));
 }
-static SENSOR_DEVICE_ATTR(fan0_input, S_IRUGO, nouveau_hwmon_show_fan0_input,
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input,
                          NULL, 0);
 
  static ssize_t
@@ -665,14 +873,21 @@ static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR,
 
 static struct attribute *hwmon_attributes[] = {
        &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr,
        &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
        &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_emergency.dev_attr.attr,
+       &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr,
        &sensor_dev_attr_name.dev_attr.attr,
        &sensor_dev_attr_update_rate.dev_attr.attr,
        NULL
 };
 static struct attribute *hwmon_fan_rpm_attributes[] = {
-       &sensor_dev_attr_fan0_input.dev_attr.attr,
+       &sensor_dev_attr_fan1_input.dev_attr.attr,
        NULL
 };
 static struct attribute *hwmon_pwm_fan_attributes[] = {
@@ -717,7 +932,7 @@ nouveau_hwmon_init(struct drm_device *dev)
        dev_set_drvdata(hwmon_dev, dev);
 
        /* default sysfs entries */
-       ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
+       ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_attrgroup);
        if (ret) {
                if (ret)
                        goto error;
@@ -728,7 +943,7 @@ nouveau_hwmon_init(struct drm_device *dev)
         *     the gpio entries for pwm fan control even when there's no
         *     actual fan connected to it... therm table? */
        if (therm->fan_get && therm->fan_get(therm) >= 0) {
-               ret = sysfs_create_group(&dev->pdev->dev.kobj,
+               ret = sysfs_create_group(&hwmon_dev->kobj,
                                         &hwmon_pwm_fan_attrgroup);
                if (ret)
                        goto error;
@@ -736,7 +951,7 @@ nouveau_hwmon_init(struct drm_device *dev)
 
        /* if the card can read the fan rpm */
        if (therm->fan_sense(therm) >= 0) {
-               ret = sysfs_create_group(&dev->pdev->dev.kobj,
+               ret = sysfs_create_group(&hwmon_dev->kobj,
                                         &hwmon_fan_rpm_attrgroup);
                if (ret)
                        goto error;
@@ -764,10 +979,10 @@ nouveau_hwmon_fini(struct drm_device *dev)
        struct nouveau_pm *pm = nouveau_pm(dev);
 
        if (pm->hwmon) {
-               sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
-               sysfs_remove_group(&dev->pdev->dev.kobj,
+               sysfs_remove_group(&pm->hwmon->kobj, &hwmon_attrgroup);
+               sysfs_remove_group(&pm->hwmon->kobj,
                                   &hwmon_pwm_fan_attrgroup);
-               sysfs_remove_group(&dev->pdev->dev.kobj,
+               sysfs_remove_group(&pm->hwmon->kobj,
                                   &hwmon_fan_rpm_attrgroup);
 
                hwmon_device_unregister(pm->hwmon);
index 39ffc07..7e24cdf 100644 (file)
@@ -490,8 +490,8 @@ static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode)
        /* BIOS scripts usually take care of the backlight, thanks
         * Apple for your consistency.
         */
-       if (dev->pci_device == 0x0179 || dev->pci_device == 0x0189 ||
-           dev->pci_device == 0x0329) {
+       if (dev->pci_device == 0x0174 || dev->pci_device == 0x0179 ||
+           dev->pci_device == 0x0189 || dev->pci_device == 0x0329) {
                if (mode == DRM_MODE_DPMS_ON) {
                        nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 0, 1 << 31);
                        nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 1);
index 4c6e9f8..ad48444 100644 (file)
@@ -22,6 +22,9 @@
  * Author: Ben Skeggs
  */
 
+#include <core/object.h>
+#include <core/class.h>
+
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 
@@ -31,6 +34,8 @@
 #include "nouveau_encoder.h"
 #include "nouveau_connector.h"
 
+#include <subdev/i2c.h>
+
 int
 nv04_display_early_init(struct drm_device *dev)
 {
@@ -53,6 +58,7 @@ int
 nv04_display_create(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct dcb_table *dcb = &drm->vbios.dcb;
        struct drm_connector *connector, *ct;
        struct drm_encoder *encoder;
@@ -71,6 +77,11 @@ nv04_display_create(struct drm_device *dev)
 
        nouveau_hw_save_vga_fonts(dev, 1);
 
+       ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, 0xd1500000,
+                                NV04_DISP_CLASS, NULL, 0, &disp->core);
+       if (ret)
+               return ret;
+
        nv04_crtc_create(dev, 0);
        if (nv_two_heads(dev))
                nv04_crtc_create(dev, 1);
@@ -114,6 +125,11 @@ nv04_display_create(struct drm_device *dev)
                }
        }
 
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+               nv_encoder->i2c = i2c->find(i2c, nv_encoder->dcb->i2c_index);
+       }
+
        /* Save previous state */
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
                crtc->funcs->save(crtc);
index 4532280..a0a031d 100644 (file)
@@ -80,6 +80,7 @@ struct nv04_display {
        struct nv04_mode_state saved_reg;
        uint32_t saved_vga_font[4][16384];
        uint32_t dac_users[4];
+       struct nouveau_object *core;
 };
 
 static inline struct nv04_display *
index a220b94..94eadd1 100644 (file)
@@ -78,6 +78,9 @@ nv04_fence_context_new(struct nouveau_channel *chan)
        struct nv04_fence_chan *fctx = kzalloc(sizeof(*fctx), GFP_KERNEL);
        if (fctx) {
                nouveau_fence_context_new(&fctx->base);
+               fctx->base.emit = nv04_fence_emit;
+               fctx->base.sync = nv04_fence_sync;
+               fctx->base.read = nv04_fence_read;
                chan->fence = fctx;
                return 0;
        }
@@ -104,8 +107,5 @@ nv04_fence_create(struct nouveau_drm *drm)
        priv->base.dtor = nv04_fence_destroy;
        priv->base.context_new = nv04_fence_context_new;
        priv->base.context_del = nv04_fence_context_del;
-       priv->base.emit = nv04_fence_emit;
-       priv->base.sync = nv04_fence_sync;
-       priv->base.read = nv04_fence_read;
        return 0;
 }
index 03017f2..06f434f 100644 (file)
 
 #include "nouveau_drm.h"
 #include "nouveau_dma.h"
-#include "nouveau_fence.h"
-
-struct nv10_fence_chan {
-       struct nouveau_fence_chan base;
-};
-
-struct nv10_fence_priv {
-       struct nouveau_fence_priv base;
-       struct nouveau_bo *bo;
-       spinlock_t lock;
-       u32 sequence;
-};
+#include "nv10_fence.h"
 
 int
 nv10_fence_emit(struct nouveau_fence *fence)
@@ -61,45 +50,6 @@ nv10_fence_sync(struct nouveau_fence *fence,
        return -ENODEV;
 }
 
-int
-nv17_fence_sync(struct nouveau_fence *fence,
-               struct nouveau_channel *prev, struct nouveau_channel *chan)
-{
-       struct nv10_fence_priv *priv = chan->drm->fence;
-       u32 value;
-       int ret;
-
-       if (!mutex_trylock(&prev->cli->mutex))
-               return -EBUSY;
-
-       spin_lock(&priv->lock);
-       value = priv->sequence;
-       priv->sequence += 2;
-       spin_unlock(&priv->lock);
-
-       ret = RING_SPACE(prev, 5);
-       if (!ret) {
-               BEGIN_NV04(prev, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
-               OUT_RING  (prev, NvSema);
-               OUT_RING  (prev, 0);
-               OUT_RING  (prev, value + 0);
-               OUT_RING  (prev, value + 1);
-               FIRE_RING (prev);
-       }
-
-       if (!ret && !(ret = RING_SPACE(chan, 5))) {
-               BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
-               OUT_RING  (chan, NvSema);
-               OUT_RING  (chan, 0);
-               OUT_RING  (chan, value + 1);
-               OUT_RING  (chan, value + 2);
-               FIRE_RING (chan);
-       }
-
-       mutex_unlock(&prev->cli->mutex);
-       return 0;
-}
-
 u32
 nv10_fence_read(struct nouveau_channel *chan)
 {
@@ -115,39 +65,20 @@ nv10_fence_context_del(struct nouveau_channel *chan)
        kfree(fctx);
 }
 
-static int
+int
 nv10_fence_context_new(struct nouveau_channel *chan)
 {
-       struct nv10_fence_priv *priv = chan->drm->fence;
        struct nv10_fence_chan *fctx;
-       int ret = 0;
 
        fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL);
        if (!fctx)
                return -ENOMEM;
 
        nouveau_fence_context_new(&fctx->base);
-
-       if (priv->bo) {
-               struct ttm_mem_reg *mem = &priv->bo->bo.mem;
-               struct nouveau_object *object;
-               u32 start = mem->start * PAGE_SIZE;
-               u32 limit = mem->start + mem->size - 1;
-
-               ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
-                                        NvSema, 0x0002,
-                                        &(struct nv_dma_class) {
-                                               .flags = NV_DMA_TARGET_VRAM |
-                                                        NV_DMA_ACCESS_RDWR,
-                                               .start = start,
-                                               .limit = limit,
-                                        }, sizeof(struct nv_dma_class),
-                                        &object);
-       }
-
-       if (ret)
-               nv10_fence_context_del(chan);
-       return ret;
+       fctx->base.emit = nv10_fence_emit;
+       fctx->base.read = nv10_fence_read;
+       fctx->base.sync = nv10_fence_sync;
+       return 0;
 }
 
 void
@@ -162,18 +93,10 @@ nv10_fence_destroy(struct nouveau_drm *drm)
        kfree(priv);
 }
 
-void nv17_fence_resume(struct nouveau_drm *drm)
-{
-       struct nv10_fence_priv *priv = drm->fence;
-
-       nouveau_bo_wr32(priv->bo, 0, priv->sequence);
-}
-
 int
 nv10_fence_create(struct nouveau_drm *drm)
 {
        struct nv10_fence_priv *priv;
-       int ret = 0;
 
        priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -182,33 +105,6 @@ nv10_fence_create(struct nouveau_drm *drm)
        priv->base.dtor = nv10_fence_destroy;
        priv->base.context_new = nv10_fence_context_new;
        priv->base.context_del = nv10_fence_context_del;
-       priv->base.emit = nv10_fence_emit;
-       priv->base.read = nv10_fence_read;
-       priv->base.sync = nv10_fence_sync;
        spin_lock_init(&priv->lock);
-
-       if (nv_device(drm->device)->chipset >= 0x17) {
-               ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
-                                    0, 0x0000, NULL, &priv->bo);
-               if (!ret) {
-                       ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
-                       if (!ret) {
-                               ret = nouveau_bo_map(priv->bo);
-                               if (ret)
-                                       nouveau_bo_unpin(priv->bo);
-                       }
-                       if (ret)
-                               nouveau_bo_ref(NULL, &priv->bo);
-               }
-
-               if (ret == 0) {
-                       nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);
-                       priv->base.sync = nv17_fence_sync;
-                       priv->base.resume = nv17_fence_resume;
-               }
-       }
-
-       if (ret)
-               nv10_fence_destroy(drm);
-       return ret;
+       return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/nv10_fence.h b/drivers/gpu/drm/nouveau/nv10_fence.h
new file mode 100644 (file)
index 0000000..e5d9204
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __NV10_FENCE_H_
+#define __NV10_FENCE_H_
+
+#include <core/os.h>
+#include "nouveau_fence.h"
+#include "nouveau_bo.h"
+
+struct nv10_fence_chan {
+       struct nouveau_fence_chan base;
+};
+
+struct nv10_fence_priv {
+       struct nouveau_fence_priv base;
+       struct nouveau_bo *bo;
+       spinlock_t lock;
+       u32 sequence;
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nv17_fence.c b/drivers/gpu/drm/nouveau/nv17_fence.c
new file mode 100644 (file)
index 0000000..8e47a9b
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * 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 <bskeggs@redhat.com>
+ */
+
+#include <core/object.h>
+#include <core/class.h>
+
+#include "nouveau_drm.h"
+#include "nouveau_dma.h"
+#include "nv10_fence.h"
+
+int
+nv17_fence_sync(struct nouveau_fence *fence,
+               struct nouveau_channel *prev, struct nouveau_channel *chan)
+{
+       struct nv10_fence_priv *priv = chan->drm->fence;
+       u32 value;
+       int ret;
+
+       if (!mutex_trylock(&prev->cli->mutex))
+               return -EBUSY;
+
+       spin_lock(&priv->lock);
+       value = priv->sequence;
+       priv->sequence += 2;
+       spin_unlock(&priv->lock);
+
+       ret = RING_SPACE(prev, 5);
+       if (!ret) {
+               BEGIN_NV04(prev, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
+               OUT_RING  (prev, NvSema);
+               OUT_RING  (prev, 0);
+               OUT_RING  (prev, value + 0);
+               OUT_RING  (prev, value + 1);
+               FIRE_RING (prev);
+       }
+
+       if (!ret && !(ret = RING_SPACE(chan, 5))) {
+               BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
+               OUT_RING  (chan, NvSema);
+               OUT_RING  (chan, 0);
+               OUT_RING  (chan, value + 1);
+               OUT_RING  (chan, value + 2);
+               FIRE_RING (chan);
+       }
+
+       mutex_unlock(&prev->cli->mutex);
+       return 0;
+}
+
+static int
+nv17_fence_context_new(struct nouveau_channel *chan)
+{
+       struct nv10_fence_priv *priv = chan->drm->fence;
+       struct nv10_fence_chan *fctx;
+       struct ttm_mem_reg *mem = &priv->bo->bo.mem;
+       struct nouveau_object *object;
+       u32 start = mem->start * PAGE_SIZE;
+       u32 limit = mem->start + mem->size - 1;
+       int ret = 0;
+
+       fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL);
+       if (!fctx)
+               return -ENOMEM;
+
+       nouveau_fence_context_new(&fctx->base);
+       fctx->base.emit = nv10_fence_emit;
+       fctx->base.read = nv10_fence_read;
+       fctx->base.sync = nv17_fence_sync;
+
+       ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
+                                NvSema, 0x0002,
+                                &(struct nv_dma_class) {
+                                       .flags = NV_DMA_TARGET_VRAM |
+                                                NV_DMA_ACCESS_RDWR,
+                                       .start = start,
+                                       .limit = limit,
+                                }, sizeof(struct nv_dma_class),
+                                &object);
+       if (ret)
+               nv10_fence_context_del(chan);
+       return ret;
+}
+
+void
+nv17_fence_resume(struct nouveau_drm *drm)
+{
+       struct nv10_fence_priv *priv = drm->fence;
+
+       nouveau_bo_wr32(priv->bo, 0, priv->sequence);
+}
+
+int
+nv17_fence_create(struct nouveau_drm *drm)
+{
+       struct nv10_fence_priv *priv;
+       int ret = 0;
+
+       priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->base.dtor = nv10_fence_destroy;
+       priv->base.resume = nv17_fence_resume;
+       priv->base.context_new = nv17_fence_context_new;
+       priv->base.context_del = nv10_fence_context_del;
+       spin_lock_init(&priv->lock);
+
+       ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
+                            0, 0x0000, NULL, &priv->bo);
+       if (!ret) {
+               ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
+               if (!ret) {
+                       ret = nouveau_bo_map(priv->bo);
+                       if (ret)
+                               nouveau_bo_unpin(priv->bo);
+               }
+               if (ret)
+                       nouveau_bo_ref(NULL, &priv->bo);
+       }
+
+       if (ret) {
+               nv10_fence_destroy(drm);
+               return ret;
+       }
+
+       nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);
+       return ret;
+}
index d4cbea1..45624c3 100644 (file)
@@ -43,6 +43,7 @@
 #include <subdev/timer.h>
 #include <subdev/bar.h>
 #include <subdev/fb.h>
+#include <subdev/i2c.h>
 
 #define EVO_DMA_NR 9
 
@@ -433,7 +434,10 @@ evo_kick(u32 *push, void *evoc)
 static bool
 evo_sync_wait(void *data)
 {
-       return nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000;
+       if (nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000)
+               return true;
+       usleep_range(1, 2);
+       return false;
 }
 
 static int
@@ -512,7 +516,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                if (ret)
                        return ret;
 
-               if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) {
+               if (nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) {
                        BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2);
                        OUT_RING  (chan, NvEvoSema0 + nv_crtc->index);
                        OUT_RING  (chan, sync->sem.offset);
@@ -522,24 +526,36 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                        OUT_RING  (chan, sync->sem.offset ^ 0x10);
                        OUT_RING  (chan, 0x74b1e000);
                        BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
-                       if (nv_mclass(chan->object) < NV84_CHANNEL_DMA_CLASS)
-                               OUT_RING  (chan, NvSema);
-                       else
-                               OUT_RING  (chan, chan->vram);
+                       OUT_RING  (chan, NvSema);
+               } else
+               if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) {
+                       u64 offset = nv84_fence_crtc(chan, nv_crtc->index);
+                       offset += sync->sem.offset;
+
+                       BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
+                       OUT_RING  (chan, upper_32_bits(offset));
+                       OUT_RING  (chan, lower_32_bits(offset));
+                       OUT_RING  (chan, 0xf00d0000 | sync->sem.value);
+                       OUT_RING  (chan, 0x00000002);
+                       BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
+                       OUT_RING  (chan, upper_32_bits(offset));
+                       OUT_RING  (chan, lower_32_bits(offset ^ 0x10));
+                       OUT_RING  (chan, 0x74b1e000);
+                       OUT_RING  (chan, 0x00000001);
                } else {
-                       u64 offset = nvc0_fence_crtc(chan, nv_crtc->index);
+                       u64 offset = nv84_fence_crtc(chan, nv_crtc->index);
                        offset += sync->sem.offset;
 
                        BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
                        OUT_RING  (chan, upper_32_bits(offset));
                        OUT_RING  (chan, lower_32_bits(offset));
                        OUT_RING  (chan, 0xf00d0000 | sync->sem.value);
-                       OUT_RING  (chan, 0x1002);
+                       OUT_RING  (chan, 0x00001002);
                        BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
                        OUT_RING  (chan, upper_32_bits(offset));
                        OUT_RING  (chan, lower_32_bits(offset ^ 0x10));
                        OUT_RING  (chan, 0x74b1e000);
-                       OUT_RING  (chan, 0x1001);
+                       OUT_RING  (chan, 0x00001001);
                }
 
                FIRE_RING (chan);
@@ -1552,20 +1568,23 @@ static const struct drm_encoder_funcs nv50_dac_func = {
 static int
 nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe)
 {
-       struct drm_device *dev = connector->dev;
+       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct nouveau_encoder *nv_encoder;
        struct drm_encoder *encoder;
+       int type = DRM_MODE_ENCODER_DAC;
 
        nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
        if (!nv_encoder)
                return -ENOMEM;
        nv_encoder->dcb = dcbe;
        nv_encoder->or = ffs(dcbe->or) - 1;
+       nv_encoder->i2c = i2c->find(i2c, dcbe->i2c_index);
 
        encoder = to_drm_encoder(nv_encoder);
        encoder->possible_crtcs = dcbe->heads;
        encoder->possible_clones = 0;
-       drm_encoder_init(dev, encoder, &nv50_dac_func, DRM_MODE_ENCODER_DAC);
+       drm_encoder_init(connector->dev, encoder, &nv50_dac_func, type);
        drm_encoder_helper_add(encoder, &nv50_dac_hfunc);
 
        drm_mode_connector_attach_encoder(connector, encoder);
@@ -1674,9 +1693,6 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
        }
 
        nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON));
-
-       if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
-               nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, disp->core);
 }
 
 static bool
@@ -1733,14 +1749,6 @@ nv50_sor_disconnect(struct drm_encoder *encoder)
 }
 
 static void
-nv50_sor_prepare(struct drm_encoder *encoder)
-{
-       nv50_sor_disconnect(encoder);
-       if (nouveau_encoder(encoder)->dcb->type == DCB_OUTPUT_DP)
-               evo_sync(encoder->dev);
-}
-
-static void
 nv50_sor_commit(struct drm_encoder *encoder)
 {
 }
@@ -1835,8 +1843,13 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
        push = evo_wait(nv50_mast(dev), 8);
        if (push) {
                if (nv50_vers(mast) < NVD0_DISP_CLASS) {
+                       u32 ctrl = (depth << 16) | (proto << 8) | owner;
+                       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+                               ctrl |= 0x00001000;
+                       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+                               ctrl |= 0x00002000;
                        evo_mthd(push, 0x0600 + (nv_encoder->or * 0x040), 1);
-                       evo_data(push, (depth << 16) | (proto << 8) | owner);
+                       evo_data(push, ctrl);
                } else {
                        u32 magic = 0x31ec6000 | (nv_crtc->index << 25);
                        u32 syncs = 0x00000001;
@@ -1872,7 +1885,7 @@ nv50_sor_destroy(struct drm_encoder *encoder)
 static const struct drm_encoder_helper_funcs nv50_sor_hfunc = {
        .dpms = nv50_sor_dpms,
        .mode_fixup = nv50_sor_mode_fixup,
-       .prepare = nv50_sor_prepare,
+       .prepare = nv50_sor_disconnect,
        .commit = nv50_sor_commit,
        .mode_set = nv50_sor_mode_set,
        .disable = nv50_sor_disconnect,
@@ -1886,21 +1899,33 @@ static const struct drm_encoder_funcs nv50_sor_func = {
 static int
 nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
 {
-       struct drm_device *dev = connector->dev;
+       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct nouveau_encoder *nv_encoder;
        struct drm_encoder *encoder;
+       int type;
+
+       switch (dcbe->type) {
+       case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break;
+       case DCB_OUTPUT_TMDS:
+       case DCB_OUTPUT_DP:
+       default:
+               type = DRM_MODE_ENCODER_TMDS;
+               break;
+       }
 
        nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
        if (!nv_encoder)
                return -ENOMEM;
        nv_encoder->dcb = dcbe;
        nv_encoder->or = ffs(dcbe->or) - 1;
+       nv_encoder->i2c = i2c->find(i2c, dcbe->i2c_index);
        nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
 
        encoder = to_drm_encoder(nv_encoder);
        encoder->possible_crtcs = dcbe->heads;
        encoder->possible_clones = 0;
-       drm_encoder_init(dev, encoder, &nv50_sor_func, DRM_MODE_ENCODER_TMDS);
+       drm_encoder_init(connector->dev, encoder, &nv50_sor_func, type);
        drm_encoder_helper_add(encoder, &nv50_sor_hfunc);
 
        drm_mode_connector_attach_encoder(connector, encoder);
@@ -1908,6 +1933,184 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
 }
 
 /******************************************************************************
+ * PIOR
+ *****************************************************************************/
+
+static void
+nv50_pior_dpms(struct drm_encoder *encoder, int mode)
+{
+       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+       struct nv50_disp *disp = nv50_disp(encoder->dev);
+       u32 mthd = (nv_encoder->dcb->type << 12) | nv_encoder->or;
+       u32 ctrl = (mode == DRM_MODE_DPMS_ON);
+       nv_call(disp->core, NV50_DISP_PIOR_PWR + mthd, ctrl);
+}
+
+static bool
+nv50_pior_mode_fixup(struct drm_encoder *encoder,
+                    const struct drm_display_mode *mode,
+                    struct drm_display_mode *adjusted_mode)
+{
+       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+       struct nouveau_connector *nv_connector;
+
+       nv_connector = nouveau_encoder_connector_get(nv_encoder);
+       if (nv_connector && nv_connector->native_mode) {
+               if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) {
+                       int id = adjusted_mode->base.id;
+                       *adjusted_mode = *nv_connector->native_mode;
+                       adjusted_mode->base.id = id;
+               }
+       }
+
+       adjusted_mode->clock *= 2;
+       return true;
+}
+
+static void
+nv50_pior_commit(struct drm_encoder *encoder)
+{
+}
+
+static void
+nv50_pior_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+                  struct drm_display_mode *adjusted_mode)
+{
+       struct nv50_mast *mast = nv50_mast(encoder->dev);
+       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+       struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+       struct nouveau_connector *nv_connector;
+       u8 owner = 1 << nv_crtc->index;
+       u8 proto, depth;
+       u32 *push;
+
+       nv_connector = nouveau_encoder_connector_get(nv_encoder);
+       switch (nv_connector->base.display_info.bpc) {
+       case 10: depth = 0x6; break;
+       case  8: depth = 0x5; break;
+       case  6: depth = 0x2; break;
+       default: depth = 0x0; break;
+       }
+
+       switch (nv_encoder->dcb->type) {
+       case DCB_OUTPUT_TMDS:
+       case DCB_OUTPUT_DP:
+               proto = 0x0;
+               break;
+       default:
+               BUG_ON(1);
+               break;
+       }
+
+       nv50_pior_dpms(encoder, DRM_MODE_DPMS_ON);
+
+       push = evo_wait(mast, 8);
+       if (push) {
+               if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+                       u32 ctrl = (depth << 16) | (proto << 8) | owner;
+                       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+                               ctrl |= 0x00001000;
+                       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+                               ctrl |= 0x00002000;
+                       evo_mthd(push, 0x0700 + (nv_encoder->or * 0x040), 1);
+                       evo_data(push, ctrl);
+               }
+
+               evo_kick(push, mast);
+       }
+
+       nv_encoder->crtc = encoder->crtc;
+}
+
+static void
+nv50_pior_disconnect(struct drm_encoder *encoder)
+{
+       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+       struct nv50_mast *mast = nv50_mast(encoder->dev);
+       const int or = nv_encoder->or;
+       u32 *push;
+
+       if (nv_encoder->crtc) {
+               nv50_crtc_prepare(nv_encoder->crtc);
+
+               push = evo_wait(mast, 4);
+               if (push) {
+                       if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+                               evo_mthd(push, 0x0700 + (or * 0x040), 1);
+                               evo_data(push, 0x00000000);
+                       }
+
+                       evo_mthd(push, 0x0080, 1);
+                       evo_data(push, 0x00000000);
+                       evo_kick(push, mast);
+               }
+       }
+
+       nv_encoder->crtc = NULL;
+}
+
+static void
+nv50_pior_destroy(struct drm_encoder *encoder)
+{
+       drm_encoder_cleanup(encoder);
+       kfree(encoder);
+}
+
+static const struct drm_encoder_helper_funcs nv50_pior_hfunc = {
+       .dpms = nv50_pior_dpms,
+       .mode_fixup = nv50_pior_mode_fixup,
+       .prepare = nv50_pior_disconnect,
+       .commit = nv50_pior_commit,
+       .mode_set = nv50_pior_mode_set,
+       .disable = nv50_pior_disconnect,
+       .get_crtc = nv50_display_crtc_get,
+};
+
+static const struct drm_encoder_funcs nv50_pior_func = {
+       .destroy = nv50_pior_destroy,
+};
+
+static int
+nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
+{
+       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+       struct nouveau_i2c_port *ddc = NULL;
+       struct nouveau_encoder *nv_encoder;
+       struct drm_encoder *encoder;
+       int type;
+
+       switch (dcbe->type) {
+       case DCB_OUTPUT_TMDS:
+               ddc  = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(dcbe->extdev));
+               type = DRM_MODE_ENCODER_TMDS;
+               break;
+       case DCB_OUTPUT_DP:
+               ddc  = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(dcbe->extdev));
+               type = DRM_MODE_ENCODER_TMDS;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+       if (!nv_encoder)
+               return -ENOMEM;
+       nv_encoder->dcb = dcbe;
+       nv_encoder->or = ffs(dcbe->or) - 1;
+       nv_encoder->i2c = ddc;
+
+       encoder = to_drm_encoder(nv_encoder);
+       encoder->possible_crtcs = dcbe->heads;
+       encoder->possible_clones = 0;
+       drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type);
+       drm_encoder_helper_add(encoder, &nv50_pior_hfunc);
+
+       drm_mode_connector_attach_encoder(connector, encoder);
+       return 0;
+}
+
+/******************************************************************************
  * Init
  *****************************************************************************/
 void
@@ -1923,7 +2126,7 @@ nv50_display_init(struct drm_device *dev)
                evo_mthd(push, 0x0088, 1);
                evo_data(push, NvEvoSync);
                evo_kick(push, nv50_mast(dev));
-               return evo_sync(dev);
+               return 0;
        }
 
        return -EBUSY;
@@ -2029,25 +2232,28 @@ nv50_display_create(struct drm_device *dev)
                if (IS_ERR(connector))
                        continue;
 
-               if (dcbe->location != DCB_LOC_ON_CHIP) {
-                       NV_WARN(drm, "skipping off-chip encoder %d/%d\n",
-                               dcbe->type, ffs(dcbe->or) - 1);
-                       continue;
+               if (dcbe->location == DCB_LOC_ON_CHIP) {
+                       switch (dcbe->type) {
+                       case DCB_OUTPUT_TMDS:
+                       case DCB_OUTPUT_LVDS:
+                       case DCB_OUTPUT_DP:
+                               ret = nv50_sor_create(connector, dcbe);
+                               break;
+                       case DCB_OUTPUT_ANALOG:
+                               ret = nv50_dac_create(connector, dcbe);
+                               break;
+                       default:
+                               ret = -ENODEV;
+                               break;
+                       }
+               } else {
+                       ret = nv50_pior_create(connector, dcbe);
                }
 
-               switch (dcbe->type) {
-               case DCB_OUTPUT_TMDS:
-               case DCB_OUTPUT_LVDS:
-               case DCB_OUTPUT_DP:
-                       nv50_sor_create(connector, dcbe);
-                       break;
-               case DCB_OUTPUT_ANALOG:
-                       nv50_dac_create(connector, dcbe);
-                       break;
-               default:
-                       NV_WARN(drm, "skipping unsupported encoder %d/%d\n",
-                               dcbe->type, ffs(dcbe->or) - 1);
-                       continue;
+               if (ret) {
+                       NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n",
+                                    dcbe->location, dcbe->type,
+                                    ffs(dcbe->or) - 1, ret);
                }
        }
 
index d889f3a..f9701e5 100644 (file)
 
 #include "nouveau_drm.h"
 #include "nouveau_dma.h"
-#include "nouveau_fence.h"
+#include "nv10_fence.h"
 
 #include "nv50_display.h"
 
-struct nv50_fence_chan {
-       struct nouveau_fence_chan base;
-};
-
-struct nv50_fence_priv {
-       struct nouveau_fence_priv base;
-       struct nouveau_bo *bo;
-       spinlock_t lock;
-       u32 sequence;
-};
-
 static int
 nv50_fence_context_new(struct nouveau_channel *chan)
 {
        struct drm_device *dev = chan->drm->dev;
-       struct nv50_fence_priv *priv = chan->drm->fence;
-       struct nv50_fence_chan *fctx;
+       struct nv10_fence_priv *priv = chan->drm->fence;
+       struct nv10_fence_chan *fctx;
        struct ttm_mem_reg *mem = &priv->bo->bo.mem;
        struct nouveau_object *object;
        int ret, i;
@@ -57,6 +46,9 @@ nv50_fence_context_new(struct nouveau_channel *chan)
                return -ENOMEM;
 
        nouveau_fence_context_new(&fctx->base);
+       fctx->base.emit = nv10_fence_emit;
+       fctx->base.read = nv10_fence_read;
+       fctx->base.sync = nv17_fence_sync;
 
        ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
                                 NvSema, 0x0002,
@@ -91,7 +83,7 @@ nv50_fence_context_new(struct nouveau_channel *chan)
 int
 nv50_fence_create(struct nouveau_drm *drm)
 {
-       struct nv50_fence_priv *priv;
+       struct nv10_fence_priv *priv;
        int ret = 0;
 
        priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -99,11 +91,9 @@ nv50_fence_create(struct nouveau_drm *drm)
                return -ENOMEM;
 
        priv->base.dtor = nv10_fence_destroy;
+       priv->base.resume = nv17_fence_resume;
        priv->base.context_new = nv50_fence_context_new;
        priv->base.context_del = nv10_fence_context_del;
-       priv->base.emit = nv10_fence_emit;
-       priv->base.read = nv10_fence_read;
-       priv->base.sync = nv17_fence_sync;
        spin_lock_init(&priv->lock);
 
        ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
@@ -119,13 +109,11 @@ nv50_fence_create(struct nouveau_drm *drm)
                        nouveau_bo_ref(NULL, &priv->bo);
        }
 
-       if (ret == 0) {
-               nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);
-               priv->base.sync = nv17_fence_sync;
-               priv->base.resume = nv17_fence_resume;
+       if (ret) {
+               nv10_fence_destroy(drm);
+               return ret;
        }
 
-       if (ret)
-               nv10_fence_destroy(drm);
+       nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);
        return ret;
 }
index c686650..9fd475c 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 #include <core/object.h>
+#include <core/client.h>
 #include <core/class.h>
 
 #include <engine/fifo.h>
 
 #include "nv50_display.h"
 
-struct nv84_fence_chan {
-       struct nouveau_fence_chan base;
-};
-
-struct nv84_fence_priv {
-       struct nouveau_fence_priv base;
-       struct nouveau_gpuobj *mem;
-};
+u64
+nv84_fence_crtc(struct nouveau_channel *chan, int crtc)
+{
+       struct nv84_fence_chan *fctx = chan->fence;
+       return fctx->dispc_vma[crtc].offset;
+}
 
 static int
-nv84_fence_emit(struct nouveau_fence *fence)
+nv84_fence_emit32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
 {
-       struct nouveau_channel *chan = fence->channel;
-       struct nouveau_fifo_chan *fifo = (void *)chan->object;
-       int ret = RING_SPACE(chan, 7);
+       int ret = RING_SPACE(chan, 8);
        if (ret == 0) {
                BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
-               OUT_RING  (chan, NvSema);
-               BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
-               OUT_RING  (chan, upper_32_bits(fifo->chid * 16));
-               OUT_RING  (chan, lower_32_bits(fifo->chid * 16));
-               OUT_RING  (chan, fence->sequence);
+               OUT_RING  (chan, chan->vram);
+               BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 5);
+               OUT_RING  (chan, upper_32_bits(virtual));
+               OUT_RING  (chan, lower_32_bits(virtual));
+               OUT_RING  (chan, sequence);
                OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG);
+               OUT_RING  (chan, 0x00000000);
                FIRE_RING (chan);
        }
        return ret;
 }
 
-
 static int
-nv84_fence_sync(struct nouveau_fence *fence,
-               struct nouveau_channel *prev, struct nouveau_channel *chan)
+nv84_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
 {
-       struct nouveau_fifo_chan *fifo = (void *)prev->object;
        int ret = RING_SPACE(chan, 7);
        if (ret == 0) {
                BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
-               OUT_RING  (chan, NvSema);
+               OUT_RING  (chan, chan->vram);
                BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
-               OUT_RING  (chan, upper_32_bits(fifo->chid * 16));
-               OUT_RING  (chan, lower_32_bits(fifo->chid * 16));
-               OUT_RING  (chan, fence->sequence);
+               OUT_RING  (chan, upper_32_bits(virtual));
+               OUT_RING  (chan, lower_32_bits(virtual));
+               OUT_RING  (chan, sequence);
                OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL);
                FIRE_RING (chan);
        }
        return ret;
 }
 
+static int
+nv84_fence_emit(struct nouveau_fence *fence)
+{
+       struct nouveau_channel *chan = fence->channel;
+       struct nv84_fence_chan *fctx = chan->fence;
+       struct nouveau_fifo_chan *fifo = (void *)chan->object;
+       u64 addr = fifo->chid * 16;
+
+       if (fence->sysmem)
+               addr += fctx->vma_gart.offset;
+       else
+               addr += fctx->vma.offset;
+
+       return fctx->base.emit32(chan, addr, fence->sequence);
+}
+
+static int
+nv84_fence_sync(struct nouveau_fence *fence,
+               struct nouveau_channel *prev, struct nouveau_channel *chan)
+{
+       struct nv84_fence_chan *fctx = chan->fence;
+       struct nouveau_fifo_chan *fifo = (void *)prev->object;
+       u64 addr = fifo->chid * 16;
+
+       if (fence->sysmem)
+               addr += fctx->vma_gart.offset;
+       else
+               addr += fctx->vma.offset;
+
+       return fctx->base.sync32(chan, addr, fence->sequence);
+}
+
 static u32
 nv84_fence_read(struct nouveau_channel *chan)
 {
        struct nouveau_fifo_chan *fifo = (void *)chan->object;
        struct nv84_fence_priv *priv = chan->drm->fence;
-       return nv_ro32(priv->mem, fifo->chid * 16);
+       return nouveau_bo_rd32(priv->bo, fifo->chid * 16/4);
 }
 
 static void
 nv84_fence_context_del(struct nouveau_channel *chan)
 {
+       struct drm_device *dev = chan->drm->dev;
+       struct nv84_fence_priv *priv = chan->drm->fence;
        struct nv84_fence_chan *fctx = chan->fence;
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i);
+               nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]);
+       }
+
+       nouveau_bo_vma_del(priv->bo, &fctx->vma_gart);
+       nouveau_bo_vma_del(priv->bo, &fctx->vma);
        nouveau_fence_context_del(&fctx->base);
        chan->fence = NULL;
        kfree(fctx);
 }
 
-static int
+int
 nv84_fence_context_new(struct nouveau_channel *chan)
 {
-       struct drm_device *dev = chan->drm->dev;
        struct nouveau_fifo_chan *fifo = (void *)chan->object;
+       struct nouveau_client *client = nouveau_client(fifo);
        struct nv84_fence_priv *priv = chan->drm->fence;
        struct nv84_fence_chan *fctx;
-       struct nouveau_object *object;
        int ret, i;
 
        fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL);
@@ -113,44 +150,74 @@ nv84_fence_context_new(struct nouveau_channel *chan)
                return -ENOMEM;
 
        nouveau_fence_context_new(&fctx->base);
+       fctx->base.emit = nv84_fence_emit;
+       fctx->base.sync = nv84_fence_sync;
+       fctx->base.read = nv84_fence_read;
+       fctx->base.emit32 = nv84_fence_emit32;
+       fctx->base.sync32 = nv84_fence_sync32;
 
-       ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
-                                NvSema, 0x0002,
-                                &(struct nv_dma_class) {
-                                       .flags = NV_DMA_TARGET_VRAM |
-                                                NV_DMA_ACCESS_RDWR,
-                                       .start = priv->mem->addr,
-                                       .limit = priv->mem->addr +
-                                                priv->mem->size - 1,
-                                }, sizeof(struct nv_dma_class),
-                                &object);
-
-       /* dma objects for display sync channel semaphore blocks */
-       for (i = 0; !ret && i < dev->mode_config.num_crtc; i++) {
-               struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i);
+       ret = nouveau_bo_vma_add(priv->bo, client->vm, &fctx->vma);
+       if (ret == 0) {
+               ret = nouveau_bo_vma_add(priv->bo_gart, client->vm,
+                                       &fctx->vma_gart);
+       }
 
-               ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
-                                        NvEvoSema0 + i, 0x003d,
-                                        &(struct nv_dma_class) {
-                                               .flags = NV_DMA_TARGET_VRAM |
-                                                        NV_DMA_ACCESS_RDWR,
-                                               .start = bo->bo.offset,
-                                               .limit = bo->bo.offset + 0xfff,
-                                        }, sizeof(struct nv_dma_class),
-                                        &object);
+       /* map display semaphore buffers into channel's vm */
+       for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) {
+               struct nouveau_bo *bo = nv50_display_crtc_sema(chan->drm->dev, i);
+               ret = nouveau_bo_vma_add(bo, client->vm, &fctx->dispc_vma[i]);
        }
 
+       nouveau_bo_wr32(priv->bo, fifo->chid * 16/4, 0x00000000);
+
        if (ret)
                nv84_fence_context_del(chan);
-       nv_wo32(priv->mem, fifo->chid * 16, 0x00000000);
        return ret;
 }
 
+static bool
+nv84_fence_suspend(struct nouveau_drm *drm)
+{
+       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
+       struct nv84_fence_priv *priv = drm->fence;
+       int i;
+
+       priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32));
+       if (priv->suspend) {
+               for (i = 0; i <= pfifo->max; i++)
+                       priv->suspend[i] = nouveau_bo_rd32(priv->bo, i*4);
+       }
+
+       return priv->suspend != NULL;
+}
+
+static void
+nv84_fence_resume(struct nouveau_drm *drm)
+{
+       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
+       struct nv84_fence_priv *priv = drm->fence;
+       int i;
+
+       if (priv->suspend) {
+               for (i = 0; i <= pfifo->max; i++)
+                       nouveau_bo_wr32(priv->bo, i*4, priv->suspend[i]);
+               vfree(priv->suspend);
+               priv->suspend = NULL;
+       }
+}
+
 static void
 nv84_fence_destroy(struct nouveau_drm *drm)
 {
        struct nv84_fence_priv *priv = drm->fence;
-       nouveau_gpuobj_ref(NULL, &priv->mem);
+       nouveau_bo_unmap(priv->bo_gart);
+       if (priv->bo_gart)
+               nouveau_bo_unpin(priv->bo_gart);
+       nouveau_bo_ref(NULL, &priv->bo_gart);
+       nouveau_bo_unmap(priv->bo);
+       if (priv->bo)
+               nouveau_bo_unpin(priv->bo);
+       nouveau_bo_ref(NULL, &priv->bo);
        drm->fence = NULL;
        kfree(priv);
 }
@@ -160,7 +227,6 @@ nv84_fence_create(struct nouveau_drm *drm)
 {
        struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
        struct nv84_fence_priv *priv;
-       u32 chan = pfifo->max + 1;
        int ret;
 
        priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -168,14 +234,42 @@ nv84_fence_create(struct nouveau_drm *drm)
                return -ENOMEM;
 
        priv->base.dtor = nv84_fence_destroy;
+       priv->base.suspend = nv84_fence_suspend;
+       priv->base.resume = nv84_fence_resume;
        priv->base.context_new = nv84_fence_context_new;
        priv->base.context_del = nv84_fence_context_del;
-       priv->base.emit = nv84_fence_emit;
-       priv->base.sync = nv84_fence_sync;
-       priv->base.read = nv84_fence_read;
 
-       ret = nouveau_gpuobj_new(drm->device, NULL, chan * 16, 0x1000, 0,
-                               &priv->mem);
+       init_waitqueue_head(&priv->base.waiting);
+       priv->base.uevent = true;
+
+       ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
+                            TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo);
+       if (ret == 0) {
+               ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
+               if (ret == 0) {
+                       ret = nouveau_bo_map(priv->bo);
+                       if (ret)
+                               nouveau_bo_unpin(priv->bo);
+               }
+               if (ret)
+                       nouveau_bo_ref(NULL, &priv->bo);
+       }
+
+       if (ret == 0)
+               ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
+                                    TTM_PL_FLAG_TT, 0, 0, NULL,
+                                    &priv->bo_gart);
+       if (ret == 0) {
+               ret = nouveau_bo_pin(priv->bo_gart, TTM_PL_FLAG_TT);
+               if (ret == 0) {
+                       ret = nouveau_bo_map(priv->bo_gart);
+                       if (ret)
+                               nouveau_bo_unpin(priv->bo_gart);
+               }
+               if (ret)
+                       nouveau_bo_ref(NULL, &priv->bo_gart);
+       }
+
        if (ret)
                nv84_fence_destroy(drm);
        return ret;
index 2a56b1b..9566267 100644 (file)
 
 #include "nv50_display.h"
 
-struct nvc0_fence_priv {
-       struct nouveau_fence_priv base;
-       struct nouveau_bo *bo;
-       u32 *suspend;
-};
-
-struct nvc0_fence_chan {
-       struct nouveau_fence_chan base;
-       struct nouveau_vma vma;
-       struct nouveau_vma dispc_vma[4];
-};
-
-u64
-nvc0_fence_crtc(struct nouveau_channel *chan, int crtc)
-{
-       struct nvc0_fence_chan *fctx = chan->fence;
-       return fctx->dispc_vma[crtc].offset;
-}
-
 static int
-nvc0_fence_emit(struct nouveau_fence *fence)
+nvc0_fence_emit32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
 {
-       struct nouveau_channel *chan = fence->channel;
-       struct nvc0_fence_chan *fctx = chan->fence;
-       struct nouveau_fifo_chan *fifo = (void *)chan->object;
-       u64 addr = fctx->vma.offset + fifo->chid * 16;
-       int ret;
-
-       ret = RING_SPACE(chan, 5);
+       int ret = RING_SPACE(chan, 6);
        if (ret == 0) {
-               BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
-               OUT_RING  (chan, upper_32_bits(addr));
-               OUT_RING  (chan, lower_32_bits(addr));
-               OUT_RING  (chan, fence->sequence);
+               BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 5);
+               OUT_RING  (chan, upper_32_bits(virtual));
+               OUT_RING  (chan, lower_32_bits(virtual));
+               OUT_RING  (chan, sequence);
                OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG);
+               OUT_RING  (chan, 0x00000000);
                FIRE_RING (chan);
        }
-
        return ret;
 }
 
 static int
-nvc0_fence_sync(struct nouveau_fence *fence,
-               struct nouveau_channel *prev, struct nouveau_channel *chan)
+nvc0_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
 {
-       struct nvc0_fence_chan *fctx = chan->fence;
-       struct nouveau_fifo_chan *fifo = (void *)prev->object;
-       u64 addr = fctx->vma.offset + fifo->chid * 16;
-       int ret;
-
-       ret = RING_SPACE(chan, 5);
+       int ret = RING_SPACE(chan, 5);
        if (ret == 0) {
                BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
-               OUT_RING  (chan, upper_32_bits(addr));
-               OUT_RING  (chan, lower_32_bits(addr));
-               OUT_RING  (chan, fence->sequence);
+               OUT_RING  (chan, upper_32_bits(virtual));
+               OUT_RING  (chan, lower_32_bits(virtual));
+               OUT_RING  (chan, sequence);
                OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL |
                                 NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD);
                FIRE_RING (chan);
        }
-
        return ret;
 }
 
-static u32
-nvc0_fence_read(struct nouveau_channel *chan)
-{
-       struct nouveau_fifo_chan *fifo = (void *)chan->object;
-       struct nvc0_fence_priv *priv = chan->drm->fence;
-       return nouveau_bo_rd32(priv->bo, fifo->chid * 16/4);
-}
-
-static void
-nvc0_fence_context_del(struct nouveau_channel *chan)
-{
-       struct drm_device *dev = chan->drm->dev;
-       struct nvc0_fence_priv *priv = chan->drm->fence;
-       struct nvc0_fence_chan *fctx = chan->fence;
-       int i;
-
-       for (i = 0; i < dev->mode_config.num_crtc; i++) {
-               struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i);
-               nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]);
-       }
-
-       nouveau_bo_vma_del(priv->bo, &fctx->vma);
-       nouveau_fence_context_del(&fctx->base);
-       chan->fence = NULL;
-       kfree(fctx);
-}
-
 static int
 nvc0_fence_context_new(struct nouveau_channel *chan)
 {
-       struct nouveau_fifo_chan *fifo = (void *)chan->object;
-       struct nouveau_client *client = nouveau_client(fifo);
-       struct nvc0_fence_priv *priv = chan->drm->fence;
-       struct nvc0_fence_chan *fctx;
-       int ret, i;
-
-       fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL);
-       if (!fctx)
-               return -ENOMEM;
-
-       nouveau_fence_context_new(&fctx->base);
-
-       ret = nouveau_bo_vma_add(priv->bo, client->vm, &fctx->vma);
-       if (ret)
-               nvc0_fence_context_del(chan);
-
-       /* map display semaphore buffers into channel's vm */
-       for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) {
-               struct nouveau_bo *bo = nv50_display_crtc_sema(chan->drm->dev, i);
-               ret = nouveau_bo_vma_add(bo, client->vm, &fctx->dispc_vma[i]);
+       int ret = nv84_fence_context_new(chan);
+       if (ret == 0) {
+               struct nv84_fence_chan *fctx = chan->fence;
+               fctx->base.emit32 = nvc0_fence_emit32;
+               fctx->base.sync32 = nvc0_fence_sync32;
        }
-
-       nouveau_bo_wr32(priv->bo, fifo->chid * 16/4, 0x00000000);
        return ret;
 }
 
-static bool
-nvc0_fence_suspend(struct nouveau_drm *drm)
-{
-       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
-       struct nvc0_fence_priv *priv = drm->fence;
-       int i;
-
-       priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32));
-       if (priv->suspend) {
-               for (i = 0; i <= pfifo->max; i++)
-                       priv->suspend[i] = nouveau_bo_rd32(priv->bo, i);
-       }
-
-       return priv->suspend != NULL;
-}
-
-static void
-nvc0_fence_resume(struct nouveau_drm *drm)
-{
-       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
-       struct nvc0_fence_priv *priv = drm->fence;
-       int i;
-
-       if (priv->suspend) {
-               for (i = 0; i <= pfifo->max; i++)
-                       nouveau_bo_wr32(priv->bo, i, priv->suspend[i]);
-               vfree(priv->suspend);
-               priv->suspend = NULL;
-       }
-}
-
-static void
-nvc0_fence_destroy(struct nouveau_drm *drm)
-{
-       struct nvc0_fence_priv *priv = drm->fence;
-       nouveau_bo_unmap(priv->bo);
-       if (priv->bo)
-               nouveau_bo_unpin(priv->bo);
-       nouveau_bo_ref(NULL, &priv->bo);
-       drm->fence = NULL;
-       kfree(priv);
-}
-
 int
 nvc0_fence_create(struct nouveau_drm *drm)
 {
-       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
-       struct nvc0_fence_priv *priv;
-       int ret;
-
-       priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       priv->base.dtor = nvc0_fence_destroy;
-       priv->base.suspend = nvc0_fence_suspend;
-       priv->base.resume = nvc0_fence_resume;
-       priv->base.context_new = nvc0_fence_context_new;
-       priv->base.context_del = nvc0_fence_context_del;
-       priv->base.emit = nvc0_fence_emit;
-       priv->base.sync = nvc0_fence_sync;
-       priv->base.read = nvc0_fence_read;
-
-       ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
-                            TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo);
+       int ret = nv84_fence_create(drm);
        if (ret == 0) {
-               ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
-               if (ret == 0) {
-                       ret = nouveau_bo_map(priv->bo);
-                       if (ret)
-                               nouveau_bo_unpin(priv->bo);
-               }
-               if (ret)
-                       nouveau_bo_ref(NULL, &priv->bo);
+               struct nv84_fence_priv *priv = drm->fence;
+               priv->base.context_new = nvc0_fence_context_new;
        }
-
-       if (ret)
-               nvc0_fence_destroy(drm);
        return ret;
 }