Merge branch 'linux-4.9' of git://github.com/skeggsb/linux into drm-next
authorDave Airlie <airlied@redhat.com>
Fri, 28 Oct 2016 04:24:56 +0000 (14:24 +1000)
committerDave Airlie <airlied@redhat.com>
Fri, 28 Oct 2016 04:24:56 +0000 (14:24 +1000)
Karol's work which greatly improves volt/clock changes on a
heap of boards, nothing too exciting beyond a random collection of fixes.

* 'linux-4.9' of git://github.com/skeggsb/linux: (33 commits)
  drm/nouveau/fb/nv50: defer DMA mapping of scratch page to oneinit() hook
  drm/nouveau/fb/gf100: defer DMA mapping of scratch page to oneinit() hook
  drm/nouveau/pci: set streaming DMA mask early
  drm/nouveau/kms: add Maxwell to backlight initialization
  drm/nouveau/bar/nv50: fix bar2 vm size
  drm/nouveau/disp: remove unused function in sorg94.c
  drm/nouveau/volt: use kernel's 64-bit signed division function
  drm/nouveau/core: add missing header dependencies
  drm/nouveau/gr/nv3x: add 0x0597 kelvin 3d class support
  drm/nouveau/drm/nouveau: add a LED driver for the NVIDIA logo
  drm/nouveau/fb/ram: Use Kepler implementation on Maxwell
  drm/nouveau/volt: Make use of cvb coefficients
  drm/nouveau/volt/gf100-: Add speedo
  drm/nouveau/volt: Add implementation for gf100
  drm/nouveau/bios/vmap: unk0 field is the mode
  drm/nouveau/volt: Don't require perfect fit
  drm/nouveau/clk: Allow boosting only when NvBoost is set
  drm/nouveau/bios: Add parsing of VPSTATE table
  drm/nouveau/clk: Respect voltage limits in nvkm_cstate_prog
  drm/nouveau/clk: Fixup cstate selection
  ...

44 files changed:
drivers/gpu/drm/nouveau/Kbuild
drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/iccsense.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/vmap.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/volt.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/vpstate.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h
drivers/gpu/drm/nouveau/nouveau_backlight.c
drivers/gpu/drm/nouveau/nouveau_bios.h
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_led.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nouveau_led.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/core/firmware.c
drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c
drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c
drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c
drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c
drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild
drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c
drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c
drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c
drivers/gpu/drm/nouveau/nvkm/subdev/bios/vpstate.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c
drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c
drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c
drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c
drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h
drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c
drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild
drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c
drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf100.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c
drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h

index 2527bf4ca5d94d220cc60c29c97f057c70f6e01a..fde6e3656636f482cee865877acd78bec083b10d 100644 (file)
@@ -22,6 +22,7 @@ nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o
 nouveau-y += nouveau_drm.o
 nouveau-y += nouveau_hwmon.o
 nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
+nouveau-$(CONFIG_LEDS_CLASS) += nouveau_led.o
 nouveau-y += nouveau_nvif.o
 nouveau-$(CONFIG_NOUVEAU_PLATFORM_DRIVER) += nouveau_platform.o
 nouveau-y += nouveau_usif.o # userspace <-> nvif
index a47d46dda70478a2239b8e12e4fae0cd196289e9..b7a54e6054698a68ae215f26d852f4c227414bd8 100644 (file)
@@ -6,6 +6,7 @@ enum dcb_gpio_func_name {
        DCB_GPIO_TVDAC1 = 0x2d,
        DCB_GPIO_FAN = 0x09,
        DCB_GPIO_FAN_SENSE = 0x3d,
+       DCB_GPIO_LOGO_LED_PWM = 0x84,
        DCB_GPIO_UNUSED = 0xff,
        DCB_GPIO_VID0 = 0x04,
        DCB_GPIO_VID1 = 0x05,
index 9cb97477248bb438a107bdbdeb5a1eaa6f5812ca..e933d3eede70f82da1a058a65257fb8104f02f95 100644 (file)
@@ -1,10 +1,16 @@
 #ifndef __NVBIOS_ICCSENSE_H__
 #define __NVBIOS_ICCSENSE_H__
+struct pwr_rail_resistor_t {
+       u8 mohm;
+       bool enabled;
+};
+
 struct pwr_rail_t {
        u8 mode;
        u8 extdev_id;
-       u8 resistor_mohm;
-       u8 rail;
+       u8 resistor_count;
+       struct pwr_rail_resistor_t resistors[3];
+       u16 config;
 };
 
 struct nvbios_iccsense {
index 6633c6db9281eb897b5f378fc5c0c2d360fc6399..8fa1294c27b7086e334085a6d437c758e0a6f1fc 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef __NVBIOS_VMAP_H__
 #define __NVBIOS_VMAP_H__
 struct nvbios_vmap {
+       u8  max0;
+       u8  max1;
+       u8  max2;
 };
 
 u16 nvbios_vmap_table(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
@@ -8,7 +11,7 @@ u16 nvbios_vmap_parse(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
                      struct nvbios_vmap *);
 
 struct nvbios_vmap_entry {
-       u8  unk0;
+       u8  mode;
        u8  link;
        u32 min;
        u32 max;
index b0df610cec2b5ba3a28966092ec53726ef9e00d9..23f3d1b93ebbbe3852d8c0a530a43e858772de43 100644 (file)
@@ -13,8 +13,9 @@ struct nvbios_volt {
        u32 base;
 
        /* GPIO mode */
-       u8  vidmask;
-       s16 step;
+       bool ranged;
+       u8   vidmask;
+       s16  step;
 
        /* PWM mode */
        u32 pwm_freq;
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/vpstate.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/vpstate.h
new file mode 100644 (file)
index 0000000..87f804f
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __NVBIOS_VPSTATE_H__
+#define __NVBIOS_VPSTATE_H__
+struct nvbios_vpstate_header {
+       u32 offset;
+
+       u8 version;
+       u8 hlen;
+       u8 ecount;
+       u8 elen;
+       u8 scount;
+       u8 slen;
+
+       u8 base_id;
+       u8 boost_id;
+       u8 tdp_id;
+};
+struct nvbios_vpstate_entry {
+       u8  pstate;
+       u16 clock_mhz;
+};
+int nvbios_vpstate_parse(struct nvkm_bios *, struct nvbios_vpstate_header *);
+int nvbios_vpstate_entry(struct nvkm_bios *, struct nvbios_vpstate_header *,
+                        u8 idx, struct nvbios_vpstate_entry *);
+#endif
index fb54417bc458898e6304b480bf58d4d783aa82e4..e5275f742977f01a0fca31e099ac0a16639b2ef3 100644 (file)
@@ -6,6 +6,10 @@
 struct nvbios_pll;
 struct nvkm_pll_vals;
 
+#define NVKM_CLK_CSTATE_DEFAULT -1 /* POSTed default */
+#define NVKM_CLK_CSTATE_BASE    -2 /* pstate base */
+#define NVKM_CLK_CSTATE_HIGHEST -3 /* highest possible */
+
 enum nv_clk_src {
        nv_clk_src_crystal,
        nv_clk_src_href,
@@ -52,6 +56,7 @@ struct nvkm_cstate {
        struct list_head head;
        u8  voltage;
        u32 domain[nv_clk_src_max];
+       u8  id;
 };
 
 struct nvkm_pstate {
@@ -67,7 +72,8 @@ struct nvkm_pstate {
 struct nvkm_domain {
        enum nv_clk_src name;
        u8 bios; /* 0xff for none */
-#define NVKM_CLK_DOM_FLAG_CORE 0x01
+#define NVKM_CLK_DOM_FLAG_CORE    0x01
+#define NVKM_CLK_DOM_FLAG_VPSTATE 0x02
        u8 flags;
        const char *mname;
        int mdiv;
@@ -93,10 +99,16 @@ struct nvkm_clk {
        int ustate_ac; /* user-requested (-1 disabled, -2 perfmon) */
        int ustate_dc; /* user-requested (-1 disabled, -2 perfmon) */
        int astate; /* perfmon adjustment (base) */
-       int tstate; /* thermal adjustment (max-) */
        int dstate; /* display adjustment (min+) */
+       u8  temp;
 
        bool allow_reclock;
+#define NVKM_CLK_BOOST_NONE 0x0
+#define NVKM_CLK_BOOST_BIOS 0x1
+#define NVKM_CLK_BOOST_FULL 0x2
+       u8  boost_mode;
+       u32 base_khz;
+       u32 boost_khz;
 
        /*XXX: die, these are here *only* to support the completely
         *     bat-shit insane what-was-nouveau_hw.c code
@@ -110,7 +122,7 @@ int nvkm_clk_read(struct nvkm_clk *, enum nv_clk_src);
 int nvkm_clk_ustate(struct nvkm_clk *, int req, int pwr);
 int nvkm_clk_astate(struct nvkm_clk *, int req, int rel, bool wait);
 int nvkm_clk_dstate(struct nvkm_clk *, int req, int rel);
-int nvkm_clk_tstate(struct nvkm_clk *, int req, int rel);
+int nvkm_clk_tstate(struct nvkm_clk *, u8 temperature);
 
 int nv04_clk_new(struct nvkm_device *, int, struct nvkm_clk **);
 int nv40_clk_new(struct nvkm_device *, int, struct nvkm_clk **);
index b765f4ffcde63ba284aeba11ba0d757624e91253..08ef9983c6435825d8deeb08573349e3a0e6ebd8 100644 (file)
@@ -15,12 +15,28 @@ struct nvkm_volt {
 
        u32 max_uv;
        u32 min_uv;
+
+       /*
+        * These are fully functional map entries creating a sw ceiling for
+        * the voltage. These all can describe different kind of curves, so
+        * that for any given temperature a different one can return the lowest
+        * value of all three.
+        */
+       u8 max0_id;
+       u8 max1_id;
+       u8 max2_id;
+
+       int speedo;
 };
 
+int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature);
+int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id);
 int nvkm_volt_get(struct nvkm_volt *);
-int nvkm_volt_set_id(struct nvkm_volt *, u8 id, int condition);
+int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, u8 temp,
+                    int condition);
 
 int nv40_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
+int gf100_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
 int gk104_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
 int gk20a_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
 int gm20b_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
index f5101be806cbf4e1baf5a1db5661eb760588d0bf..5e2c5685b4ddca47ace50c755d8b23c05e4b3cb7 100644 (file)
@@ -232,6 +232,7 @@ nouveau_backlight_init(struct drm_device *dev)
                case NV_DEVICE_INFO_V0_TESLA:
                case NV_DEVICE_INFO_V0_FERMI:
                case NV_DEVICE_INFO_V0_KEPLER:
+               case NV_DEVICE_INFO_V0_MAXWELL:
                        return nv50_backlight_init(connector);
                default:
                        break;
index 0067586eb015b51acb5796a26646a812e9b29df3..18eb061ccafb1bed715c834c90e0aaac50d765b0 100644 (file)
 
 #define DCB_LOC_ON_CHIP 0
 
-#define ROM16(x) le16_to_cpu(*(u16 *)&(x))
-#define ROM32(x) le32_to_cpu(*(u32 *)&(x))
-#define ROM48(x) ({ u8 *p = &(x); (u64)ROM16(p[4]) << 32 | ROM32(p[0]); })
-#define ROM64(x) le64_to_cpu(*(u64 *)&(x))
+#define ROM16(x) get_unaligned_le16(&(x))
+#define ROM32(x) get_unaligned_le32(&(x))
 #define ROMPTR(d,x) ({            \
        struct nouveau_drm *drm = nouveau_drm((d)); \
        ROM16(x) ? &drm->vbios.data[ROM16(x)] : NULL; \
index 3100fd88a015f0e5288aade4c2c0e60027c2daed..6adf9478941737ca56ce17304666a91a974e0aa2 100644 (file)
@@ -47,6 +47,7 @@
 #include "nouveau_ttm.h"
 #include "nouveau_gem.h"
 #include "nouveau_vga.h"
+#include "nouveau_led.h"
 #include "nouveau_hwmon.h"
 #include "nouveau_acpi.h"
 #include "nouveau_bios.h"
@@ -475,6 +476,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
        nouveau_hwmon_init(dev);
        nouveau_accel_init(drm);
        nouveau_fbcon_init(dev);
+       nouveau_led_init(dev);
 
        if (nouveau_runtime_pm != 0) {
                pm_runtime_use_autosuspend(dev->dev);
@@ -510,6 +512,7 @@ nouveau_drm_unload(struct drm_device *dev)
                pm_runtime_forbid(dev->dev);
        }
 
+       nouveau_led_fini(dev);
        nouveau_fbcon_fini(dev);
        nouveau_accel_fini(drm);
        nouveau_hwmon_fini(dev);
@@ -561,6 +564,8 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime)
        struct nouveau_cli *cli;
        int ret;
 
+       nouveau_led_suspend(dev);
+
        if (dev->mode_config.num_crtc) {
                NV_INFO(drm, "suspending console...\n");
                nouveau_fbcon_set_suspend(dev, 1);
@@ -649,6 +654,8 @@ nouveau_do_resume(struct drm_device *dev, bool runtime)
                nouveau_fbcon_set_suspend(dev, 0);
        }
 
+       nouveau_led_resume(dev);
+
        return 0;
 }
 
index 822a0212cd481c1a6522bb0803ca902a80f99912..c0e2b320750383e10c9f9dcbcdd3158179f1aa58 100644 (file)
@@ -166,6 +166,9 @@ struct nouveau_drm {
        struct nouveau_hwmon *hwmon;
        struct nouveau_debugfs *debugfs;
 
+       /* led management */
+       struct nouveau_led *led;
+
        /* display power reference */
        bool have_disp_power_ref;
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_led.c b/drivers/gpu/drm/nouveau/nouveau_led.c
new file mode 100644 (file)
index 0000000..3e2f1b6
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 Martin Peres
+ *
+ * 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 (including the
+ * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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@free.fr>
+ */
+
+#include <linux/leds.h>
+
+#include "nouveau_led.h"
+#include <nvkm/subdev/gpio.h>
+
+static enum led_brightness
+nouveau_led_get_brightness(struct led_classdev *led)
+{
+       struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev;
+       struct nouveau_drm *drm = nouveau_drm(drm_dev);
+       struct nvif_object *device = &drm->device.object;
+       u32 div, duty;
+
+       div =  nvif_rd32(device, 0x61c880) & 0x00ffffff;
+       duty = nvif_rd32(device, 0x61c884) & 0x00ffffff;
+
+       if (div > 0)
+               return duty * LED_FULL / div;
+       else
+               return 0;
+}
+
+static void
+nouveau_led_set_brightness(struct led_classdev *led, enum led_brightness value)
+{
+       struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev;
+       struct nouveau_drm *drm = nouveau_drm(drm_dev);
+       struct nvif_object *device = &drm->device.object;
+
+       u32 input_clk = 27e6; /* PDISPLAY.SOR[1].PWM is connected to the crystal */
+       u32 freq = 100; /* this is what nvidia uses and it should be good-enough */
+       u32 div, duty;
+
+       div = input_clk / freq;
+       duty = value * div / LED_FULL;
+
+       /* for now, this is safe to directly poke those registers because:
+        *  - A: nvidia never puts the logo led to any other PWM controler
+        *       than PDISPLAY.SOR[1].PWM.
+        *  - B: nouveau does not touch these registers anywhere else
+        */
+       nvif_wr32(device, 0x61c880, div);
+       nvif_wr32(device, 0x61c884, 0xc0000000 | duty);
+}
+
+
+int
+nouveau_led_init(struct drm_device *dev)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nvkm_gpio *gpio = nvxx_gpio(&drm->device);
+       struct dcb_gpio_func logo_led;
+       int ret;
+
+       if (!gpio)
+               return 0;
+
+       /* check that there is a GPIO controlling the logo LED */
+       if (nvkm_gpio_find(gpio, 0, DCB_GPIO_LOGO_LED_PWM, 0xff, &logo_led))
+               return 0;
+
+       drm->led = kzalloc(sizeof(*drm->led), GFP_KERNEL);
+       if (!drm->led)
+               return -ENOMEM;
+       drm->led->dev = dev;
+
+       drm->led->led.name = "nvidia-logo";
+       drm->led->led.max_brightness = 255;
+       drm->led->led.brightness_get = nouveau_led_get_brightness;
+       drm->led->led.brightness_set = nouveau_led_set_brightness;
+
+       ret = led_classdev_register(dev->dev, &drm->led->led);
+       if (ret) {
+               kfree(drm->led);
+               return ret;
+       }
+
+       return 0;
+}
+
+void
+nouveau_led_suspend(struct drm_device *dev)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+
+       if (drm->led)
+               led_classdev_suspend(&drm->led->led);
+}
+
+void
+nouveau_led_resume(struct drm_device *dev)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+
+       if (drm->led)
+               led_classdev_resume(&drm->led->led);
+}
+
+void
+nouveau_led_fini(struct drm_device *dev)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+
+       if (drm->led) {
+               led_classdev_unregister(&drm->led->led);
+               kfree(drm->led);
+               drm->led = NULL;
+       }
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_led.h b/drivers/gpu/drm/nouveau/nouveau_led.h
new file mode 100644 (file)
index 0000000..187ecdb
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Martin Peres
+ *
+ * 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@free.fr>
+ */
+
+#ifndef __NOUVEAU_LED_H__
+#define __NOUVEAU_LED_H__
+
+#include "nouveau_drv.h"
+
+struct led_classdev;
+
+struct nouveau_led {
+       struct drm_device *dev;
+
+       struct led_classdev led;
+};
+
+static inline struct nouveau_led *
+nouveau_led(struct drm_device *dev)
+{
+       return nouveau_drm(dev)->led;
+}
+
+/* nouveau_led.c */
+#if IS_ENABLED(CONFIG_LEDS_CLASS)
+int  nouveau_led_init(struct drm_device *dev);
+void nouveau_led_suspend(struct drm_device *dev);
+void nouveau_led_resume(struct drm_device *dev);
+void nouveau_led_fini(struct drm_device *dev);
+#else
+static inline int  nouveau_led_init(struct drm_device *dev) { return 0; };
+static inline void nouveau_led_suspend(struct drm_device *dev) { };
+static inline void nouveau_led_resume(struct drm_device *dev) { };
+static inline void nouveau_led_fini(struct drm_device *dev) { };
+#endif
+
+#endif
index 34ecd4a7e0c1f2cc9cb521f8a8659a9cba345cbc..058ff46b5f16676ee56ab9323252109724a31595 100644 (file)
@@ -20,6 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 #include <core/device.h>
+#include <core/firmware.h>
 
 /**
  * nvkm_firmware_get - load firmware from the official nvidia/chip/ directory
index 7218a067a6c5e85764f1f330cb05b10c7e50132c..53d171729353a1f5686e37597a7c61e690129382 100644 (file)
@@ -1357,7 +1357,7 @@ nvc0_chipset = {
        .pmu = gf100_pmu_new,
        .therm = gt215_therm_new,
        .timer = nv41_timer_new,
-       .volt = nv40_volt_new,
+       .volt = gf100_volt_new,
        .ce[0] = gf100_ce_new,
        .ce[1] = gf100_ce_new,
        .disp = gt215_disp_new,
@@ -1394,7 +1394,7 @@ nvc1_chipset = {
        .pmu = gf100_pmu_new,
        .therm = gt215_therm_new,
        .timer = nv41_timer_new,
-       .volt = nv40_volt_new,
+       .volt = gf100_volt_new,
        .ce[0] = gf100_ce_new,
        .disp = gt215_disp_new,
        .dma = gf100_dma_new,
@@ -1430,7 +1430,7 @@ nvc3_chipset = {
        .pmu = gf100_pmu_new,
        .therm = gt215_therm_new,
        .timer = nv41_timer_new,
-       .volt = nv40_volt_new,
+       .volt = gf100_volt_new,
        .ce[0] = gf100_ce_new,
        .disp = gt215_disp_new,
        .dma = gf100_dma_new,
@@ -1466,7 +1466,7 @@ nvc4_chipset = {
        .pmu = gf100_pmu_new,
        .therm = gt215_therm_new,
        .timer = nv41_timer_new,
-       .volt = nv40_volt_new,
+       .volt = gf100_volt_new,
        .ce[0] = gf100_ce_new,
        .ce[1] = gf100_ce_new,
        .disp = gt215_disp_new,
@@ -1503,7 +1503,7 @@ nvc8_chipset = {
        .pmu = gf100_pmu_new,
        .therm = gt215_therm_new,
        .timer = nv41_timer_new,
-       .volt = nv40_volt_new,
+       .volt = gf100_volt_new,
        .ce[0] = gf100_ce_new,
        .ce[1] = gf100_ce_new,
        .disp = gt215_disp_new,
@@ -1540,7 +1540,7 @@ nvce_chipset = {
        .pmu = gf100_pmu_new,
        .therm = gt215_therm_new,
        .timer = nv41_timer_new,
-       .volt = nv40_volt_new,
+       .volt = gf100_volt_new,
        .ce[0] = gf100_ce_new,
        .ce[1] = gf100_ce_new,
        .disp = gt215_disp_new,
@@ -1577,7 +1577,7 @@ nvcf_chipset = {
        .pmu = gf100_pmu_new,
        .therm = gt215_therm_new,
        .timer = nv41_timer_new,
-       .volt = nv40_volt_new,
+       .volt = gf100_volt_new,
        .ce[0] = gf100_ce_new,
        .disp = gt215_disp_new,
        .dma = gf100_dma_new,
@@ -1612,6 +1612,7 @@ nvd7_chipset = {
        .pci = gf106_pci_new,
        .therm = gf119_therm_new,
        .timer = nv41_timer_new,
+       .volt = gf100_volt_new,
        .ce[0] = gf100_ce_new,
        .disp = gf119_disp_new,
        .dma = gf119_dma_new,
@@ -1647,7 +1648,7 @@ nvd9_chipset = {
        .pmu = gf119_pmu_new,
        .therm = gf119_therm_new,
        .timer = nv41_timer_new,
-       .volt = nv40_volt_new,
+       .volt = gf100_volt_new,
        .ce[0] = gf100_ce_new,
        .disp = gf119_disp_new,
        .dma = gf119_dma_new,
index 62ad0300cfa5ab0af8610773ae7d5c4b49e310bb..0030cd9543b24f6614c957edf673526e97c3539c 100644 (file)
@@ -1665,14 +1665,31 @@ nvkm_device_pci_new(struct pci_dev *pci_dev, const char *cfg, const char *dbg,
        *pdevice = &pdev->device;
        pdev->pdev = pci_dev;
 
-       return nvkm_device_ctor(&nvkm_device_pci_func, quirk, &pci_dev->dev,
-                               pci_is_pcie(pci_dev) ? NVKM_DEVICE_PCIE :
-                               pci_find_capability(pci_dev, PCI_CAP_ID_AGP) ?
-                               NVKM_DEVICE_AGP : NVKM_DEVICE_PCI,
-                               (u64)pci_domain_nr(pci_dev->bus) << 32 |
-                                    pci_dev->bus->number << 16 |
-                                    PCI_SLOT(pci_dev->devfn) << 8 |
-                                    PCI_FUNC(pci_dev->devfn), name,
-                               cfg, dbg, detect, mmio, subdev_mask,
-                               &pdev->device);
+       ret = nvkm_device_ctor(&nvkm_device_pci_func, quirk, &pci_dev->dev,
+                              pci_is_pcie(pci_dev) ? NVKM_DEVICE_PCIE :
+                              pci_find_capability(pci_dev, PCI_CAP_ID_AGP) ?
+                              NVKM_DEVICE_AGP : NVKM_DEVICE_PCI,
+                              (u64)pci_domain_nr(pci_dev->bus) << 32 |
+                                   pci_dev->bus->number << 16 |
+                                   PCI_SLOT(pci_dev->devfn) << 8 |
+                                   PCI_FUNC(pci_dev->devfn), name,
+                              cfg, dbg, detect, mmio, subdev_mask,
+                              &pdev->device);
+
+       if (ret)
+               return ret;
+
+       /*
+        * Set a preliminary DMA mask based on the .dma_bits member of the
+        * MMU subdevice. This allows other subdevices to create DMA mappings
+        * in their init() or oneinit() methods, which may be called before the
+        * TTM layer sets the DMA mask definitively.
+        * This is necessary for platforms where the default DMA mask of 32
+        * does not cover any system memory, i.e., when all RAM is > 4 GB.
+        */
+       if (subdev_mask & BIT(NVKM_SUBDEV_MMU))
+               dma_set_mask_and_coherent(&pci_dev->dev,
+                               DMA_BIT_MASK(pdev->device.mmu->dma_bits));
+
+       return 0;
 }
index 1bb9d661e9b35d10406cbe5507305a64314789d8..4510cb6e10a832e5a054bcf7534ba08a251954f7 100644 (file)
@@ -45,14 +45,6 @@ static const struct nvkm_output_func
 g94_sor_output_func = {
 };
 
-int
-g94_sor_output_new(struct nvkm_disp *disp, int index,
-                  struct dcb_output *dcbE, struct nvkm_output **poutp)
-{
-       return nvkm_output_new_(&g94_sor_output_func, disp,
-                               index, dcbE, poutp);
-}
-
 /*******************************************************************************
  * DisplayPort
  ******************************************************************************/
index f1e15a4d4f64fe95a2fe38a1fa2566ba376e2698..b4e3c50badc77e759fa8f93021220f340577b56d 100644 (file)
@@ -187,6 +187,7 @@ nv30_gr = {
                { -1, -1, 0x038a, &nv04_gr_object }, /* ifc (nv30) */
                { -1, -1, 0x039e, &nv04_gr_object }, /* swzsurf (nv30) */
                { -1, -1, 0x0397, &nv04_gr_object }, /* rankine */
+               { -1, -1, 0x0597, &nv04_gr_object }, /* kelvin */
                {}
        }
 };
index 300f5ed5de0b0fe618e1707aa0d78b12b62b370f..e7ed04b935cd1f7014cb26fba9d366ea8db3431f 100644 (file)
@@ -123,6 +123,7 @@ nv34_gr = {
                { -1, -1, 0x0389, &nv04_gr_object }, /* sifm (nv30) */
                { -1, -1, 0x038a, &nv04_gr_object }, /* ifc (nv30) */
                { -1, -1, 0x039e, &nv04_gr_object }, /* swzsurf (nv30) */
+               { -1, -1, 0x0597, &nv04_gr_object }, /* kelvin */
                { -1, -1, 0x0697, &nv04_gr_object }, /* rankine */
                {}
        }
index 740df0f52c38882bb808a7fba19edb6f4dfed5d8..5e8abacbacc64df941b55530ce3e2d2137e71041 100644 (file)
@@ -124,6 +124,7 @@ nv35_gr = {
                { -1, -1, 0x038a, &nv04_gr_object }, /* ifc (nv30) */
                { -1, -1, 0x039e, &nv04_gr_object }, /* swzsurf (nv30) */
                { -1, -1, 0x0497, &nv04_gr_object }, /* rankine */
+               { -1, -1, 0x0597, &nv04_gr_object }, /* kelvin */
                {}
        }
 };
index 370dcd8ff7b5abd0f0c3424a8783dcc6acbffee3..6eff637ac301af422e83ff2d083413ce4656910d 100644 (file)
@@ -84,7 +84,7 @@ nv50_bar_oneinit(struct nvkm_bar *base)
        start = 0x0100000000ULL;
        limit = start + device->func->resource_size(device, 3);
 
-       ret = nvkm_vm_new(device, start, limit, start, &bar3_lock, &vm);
+       ret = nvkm_vm_new(device, start, limit - start, start, &bar3_lock, &vm);
        if (ret)
                return ret;
 
@@ -117,7 +117,7 @@ nv50_bar_oneinit(struct nvkm_bar *base)
        start = 0x0000000000ULL;
        limit = start + device->func->resource_size(device, 1);
 
-       ret = nvkm_vm_new(device, start, limit--, start, &bar1_lock, &vm);
+       ret = nvkm_vm_new(device, start, limit-- - start, start, &bar1_lock, &vm);
        if (ret)
                return ret;
 
index dbcb0ef21587e5a64670205d516eaab24b42ddc5..be57220a2e01455d03355485547329a9231f5f39 100644 (file)
@@ -31,6 +31,7 @@ nvkm-y += nvkm/subdev/bios/timing.o
 nvkm-y += nvkm/subdev/bios/therm.o
 nvkm-y += nvkm/subdev/bios/vmap.o
 nvkm-y += nvkm/subdev/bios/volt.o
+nvkm-y += nvkm/subdev/bios/vpstate.o
 nvkm-y += nvkm/subdev/bios/xpio.o
 nvkm-y += nvkm/subdev/bios/M0203.o
 nvkm-y += nvkm/subdev/bios/M0205.o
index 084328028af14e06b678cf33bcb438c32d19d1e7..aafd5e17b1c8070c6851dc802946e0bba45945d5 100644 (file)
@@ -23,6 +23,7 @@
  */
 #include <subdev/bios.h>
 #include <subdev/bios/bit.h>
+#include <subdev/bios/extdev.h>
 #include <subdev/bios/iccsense.h>
 
 static u16
@@ -77,23 +78,47 @@ nvbios_iccsense_parse(struct nvkm_bios *bios, struct nvbios_iccsense *iccsense)
                return -ENOMEM;
 
        for (i = 0; i < cnt; ++i) {
+               struct nvbios_extdev_func extdev;
                struct pwr_rail_t *rail = &iccsense->rail[i];
+               u8 res_start = 0;
+               int r;
+
                entry = table + hdr + i * len;
 
                switch(ver) {
                case 0x10:
                        rail->mode = nvbios_rd08(bios, entry + 0x1);
                        rail->extdev_id = nvbios_rd08(bios, entry + 0x2);
-                       rail->resistor_mohm = nvbios_rd08(bios, entry + 0x3);
-                       rail->rail = nvbios_rd08(bios, entry + 0x4);
+                       res_start = 0x3;
                        break;
                case 0x20:
                        rail->mode = nvbios_rd08(bios, entry);
                        rail->extdev_id = nvbios_rd08(bios, entry + 0x1);
-                       rail->resistor_mohm = nvbios_rd08(bios, entry + 0x5);
-                       rail->rail = nvbios_rd08(bios, entry + 0x6);
+                       res_start = 0x5;
+                       break;
+               };
+
+               if (nvbios_extdev_parse(bios, rail->extdev_id, &extdev))
+                       continue;
+
+               switch (extdev.type) {
+               case NVBIOS_EXTDEV_INA209:
+               case NVBIOS_EXTDEV_INA219:
+                       rail->resistor_count = 1;
+                       break;
+               case NVBIOS_EXTDEV_INA3221:
+                       rail->resistor_count = 3;
+                       break;
+               default:
+                       rail->resistor_count = 0;
                        break;
                };
+
+               for (r = 0; r < rail->resistor_count; ++r) {
+                       rail->resistors[r].mohm = nvbios_rd08(bios, entry + res_start + r * 2);
+                       rail->resistors[r].enabled = !(nvbios_rd08(bios, entry + res_start + r * 2 + 1) & 0x40);
+               }
+               rail->config = nvbios_rd16(bios, entry + res_start + rail->resistor_count * 2);
        }
 
        return 0;
index 2f13db745948a303d24c3970fac4a6d245451886..32bd8b1d154f2b5dbf5e81b9f40f045912df979d 100644 (file)
@@ -61,7 +61,17 @@ nvbios_vmap_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
        memset(info, 0x00, sizeof(*info));
        switch (!!vmap * *ver) {
        case 0x10:
+               info->max0 = 0xff;
+               info->max1 = 0xff;
+               info->max2 = 0xff;
+               break;
        case 0x20:
+               info->max0 = nvbios_rd08(bios, vmap + 0x7);
+               info->max1 = nvbios_rd08(bios, vmap + 0x8);
+               if (*len >= 0xc)
+                       info->max2 = nvbios_rd08(bios, vmap + 0xc);
+               else
+                       info->max2 = 0xff;
                break;
        }
        return vmap;
@@ -95,7 +105,7 @@ nvbios_vmap_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len,
                info->arg[2] = nvbios_rd32(bios, vmap + 0x10);
                break;
        case 0x20:
-               info->unk0   = nvbios_rd08(bios, vmap + 0x00);
+               info->mode   = nvbios_rd08(bios, vmap + 0x00);
                info->link   = nvbios_rd08(bios, vmap + 0x01);
                info->min    = nvbios_rd32(bios, vmap + 0x02);
                info->max    = nvbios_rd32(bios, vmap + 0x06);
index 6e0a33648be92a0d1c15cc85272cfe846154f445..4504822ace51cb9a6dc07098b797269fa096f2c5 100644 (file)
@@ -75,20 +75,24 @@ nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
        case 0x12:
                info->type    = NVBIOS_VOLT_GPIO;
                info->vidmask = nvbios_rd08(bios, volt + 0x04);
+               info->ranged  = false;
                break;
        case 0x20:
                info->type    = NVBIOS_VOLT_GPIO;
                info->vidmask = nvbios_rd08(bios, volt + 0x05);
+               info->ranged  = false;
                break;
        case 0x30:
                info->type    = NVBIOS_VOLT_GPIO;
                info->vidmask = nvbios_rd08(bios, volt + 0x04);
+               info->ranged  = false;
                break;
        case 0x40:
                info->type    = NVBIOS_VOLT_GPIO;
                info->base    = nvbios_rd32(bios, volt + 0x04);
                info->step    = nvbios_rd16(bios, volt + 0x08);
                info->vidmask = nvbios_rd08(bios, volt + 0x0b);
+               info->ranged  = true; /* XXX: find the flag byte */
                /*XXX*/
                info->min     = 0;
                info->max     = info->base;
@@ -104,9 +108,11 @@ nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
                        info->pwm_freq  = nvbios_rd32(bios, volt + 0x5) / 1000;
                        info->pwm_range = nvbios_rd32(bios, volt + 0x16);
                } else {
-                       info->type      = NVBIOS_VOLT_GPIO;
-                       info->vidmask   = nvbios_rd08(bios, volt + 0x06);
-                       info->step      = nvbios_rd16(bios, volt + 0x16);
+                       info->type    = NVBIOS_VOLT_GPIO;
+                       info->vidmask = nvbios_rd08(bios, volt + 0x06);
+                       info->step    = nvbios_rd16(bios, volt + 0x16);
+                       info->ranged  =
+                               !!(nvbios_rd08(bios, volt + 0x4) & 0x2);
                }
                break;
        }
@@ -142,7 +148,10 @@ nvbios_volt_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len,
                info->vid     = nvbios_rd08(bios, volt + 0x01) >> 2;
                break;
        case 0x40:
+               break;
        case 0x50:
+               info->voltage = nvbios_rd32(bios, volt) & 0x001fffff;
+               info->vid     = (nvbios_rd32(bios, volt) >> 23) & 0xff;
                break;
        }
        return volt;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vpstate.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vpstate.c
new file mode 100644 (file)
index 0000000..f199270
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016 Karol Herbst
+ *
+ * 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: Karol Herbst
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/vpstate.h>
+
+static u32
+nvbios_vpstate_offset(struct nvkm_bios *b)
+{
+       struct bit_entry bit_P;
+
+       if (!bit_entry(b, 'P', &bit_P)) {
+               if (bit_P.version == 2)
+                       return nvbios_rd32(b, bit_P.offset + 0x38);
+       }
+
+       return 0x0000;
+}
+
+int
+nvbios_vpstate_parse(struct nvkm_bios *b, struct nvbios_vpstate_header *h)
+{
+       if (!h)
+               return -EINVAL;
+
+       h->offset = nvbios_vpstate_offset(b);
+       if (!h->offset)
+               return -ENODEV;
+
+       h->version = nvbios_rd08(b, h->offset);
+       switch (h->version) {
+       case 0x10:
+               h->hlen     = nvbios_rd08(b, h->offset + 0x1);
+               h->elen     = nvbios_rd08(b, h->offset + 0x2);
+               h->slen     = nvbios_rd08(b, h->offset + 0x3);
+               h->scount   = nvbios_rd08(b, h->offset + 0x4);
+               h->ecount   = nvbios_rd08(b, h->offset + 0x5);
+
+               h->base_id  = nvbios_rd08(b, h->offset + 0x0f);
+               h->boost_id = nvbios_rd08(b, h->offset + 0x10);
+               h->tdp_id   = nvbios_rd08(b, h->offset + 0x11);
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+int
+nvbios_vpstate_entry(struct nvkm_bios *b, struct nvbios_vpstate_header *h,
+                    u8 idx, struct nvbios_vpstate_entry *e)
+{
+       u32 offset;
+
+       if (!e || !h || idx > h->ecount)
+               return -EINVAL;
+
+       offset = h->offset + h->hlen + idx * (h->elen + (h->slen * h->scount));
+       e->pstate    = nvbios_rd08(b, offset);
+       e->clock_mhz = nvbios_rd16(b, offset + 0x5);
+       return 0;
+}
index 7102c25320fc273c64da5534e9339c7ff27cabce..fa1c12185e19f54abe95fd04e772dadf8b9a1470 100644 (file)
@@ -27,6 +27,7 @@
 #include <subdev/bios/boost.h>
 #include <subdev/bios/cstep.h>
 #include <subdev/bios/perf.h>
+#include <subdev/bios/vpstate.h>
 #include <subdev/fb.h>
 #include <subdev/therm.h>
 #include <subdev/volt.h>
@@ -74,6 +75,88 @@ nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust,
 /******************************************************************************
  * C-States
  *****************************************************************************/
+static bool
+nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate,
+                 u32 max_volt, int temp)
+{
+       const struct nvkm_domain *domain = clk->domains;
+       struct nvkm_volt *volt = clk->subdev.device->volt;
+       int voltage;
+
+       while (domain && domain->name != nv_clk_src_max) {
+               if (domain->flags & NVKM_CLK_DOM_FLAG_VPSTATE) {
+                       u32 freq = cstate->domain[domain->name];
+                       switch (clk->boost_mode) {
+                       case NVKM_CLK_BOOST_NONE:
+                               if (clk->base_khz && freq > clk->base_khz)
+                                       return false;
+                       case NVKM_CLK_BOOST_BIOS:
+                               if (clk->boost_khz && freq > clk->boost_khz)
+                                       return false;
+                       }
+               }
+               domain++;
+       }
+
+       if (!volt)
+               return true;
+
+       voltage = nvkm_volt_map(volt, cstate->voltage, temp);
+       if (voltage < 0)
+               return false;
+       return voltage <= min(max_volt, volt->max_uv);
+}
+
+static struct nvkm_cstate *
+nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate,
+                     struct nvkm_cstate *start)
+{
+       struct nvkm_device *device = clk->subdev.device;
+       struct nvkm_volt *volt = device->volt;
+       struct nvkm_cstate *cstate;
+       int max_volt;
+
+       if (!pstate || !start)
+               return NULL;
+
+       if (!volt)
+               return start;
+
+       max_volt = volt->max_uv;
+       if (volt->max0_id != 0xff)
+               max_volt = min(max_volt,
+                              nvkm_volt_map(volt, volt->max0_id, clk->temp));
+       if (volt->max1_id != 0xff)
+               max_volt = min(max_volt,
+                              nvkm_volt_map(volt, volt->max1_id, clk->temp));
+       if (volt->max2_id != 0xff)
+               max_volt = min(max_volt,
+                              nvkm_volt_map(volt, volt->max2_id, clk->temp));
+
+       for (cstate = start; &cstate->head != &pstate->list;
+            cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) {
+               if (nvkm_cstate_valid(clk, cstate, max_volt, clk->temp))
+                       break;
+       }
+
+       return cstate;
+}
+
+static struct nvkm_cstate *
+nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
+{
+       struct nvkm_cstate *cstate;
+       if (cstatei == NVKM_CLK_CSTATE_HIGHEST)
+               return list_last_entry(&pstate->list, typeof(*cstate), head);
+       else {
+               list_for_each_entry(cstate, &pstate->list, head) {
+                       if (cstate->id == cstatei)
+                               return cstate;
+               }
+       }
+       return NULL;
+}
+
 static int
 nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
 {
@@ -85,7 +168,8 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
        int ret;
 
        if (!list_empty(&pstate->list)) {
-               cstate = list_entry(pstate->list.prev, typeof(*cstate), head);
+               cstate = nvkm_cstate_get(clk, pstate, cstatei);
+               cstate = nvkm_cstate_find_best(clk, pstate, cstate);
        } else {
                cstate = &pstate->base;
        }
@@ -99,7 +183,8 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
        }
 
        if (volt) {
-               ret = nvkm_volt_set_id(volt, cstate->voltage, +1);
+               ret = nvkm_volt_set_id(volt, cstate->voltage,
+                                      pstate->base.voltage, clk->temp, +1);
                if (ret && ret != -ENODEV) {
                        nvkm_error(subdev, "failed to raise voltage: %d\n", ret);
                        return ret;
@@ -113,7 +198,8 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
        }
 
        if (volt) {
-               ret = nvkm_volt_set_id(volt, cstate->voltage, -1);
+               ret = nvkm_volt_set_id(volt, cstate->voltage,
+                                      pstate->base.voltage, clk->temp, -1);
                if (ret && ret != -ENODEV)
                        nvkm_error(subdev, "failed to lower voltage: %d\n", ret);
        }
@@ -138,6 +224,7 @@ static int
 nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate)
 {
        struct nvkm_bios *bios = clk->subdev.device->bios;
+       struct nvkm_volt *volt = clk->subdev.device->volt;
        const struct nvkm_domain *domain = clk->domains;
        struct nvkm_cstate *cstate = NULL;
        struct nvbios_cstepX cstepX;
@@ -148,12 +235,16 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate)
        if (!data)
                return -ENOENT;
 
+       if (volt && nvkm_volt_map_min(volt, cstepX.voltage) > volt->max_uv)
+               return -EINVAL;
+
        cstate = kzalloc(sizeof(*cstate), GFP_KERNEL);
        if (!cstate)
                return -ENOMEM;
 
        *cstate = pstate->base;
        cstate->voltage = cstepX.voltage;
+       cstate->id = idx;
 
        while (domain && domain->name != nv_clk_src_max) {
                if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) {
@@ -175,7 +266,7 @@ static int
 nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
 {
        struct nvkm_subdev *subdev = &clk->subdev;
-       struct nvkm_ram *ram = subdev->device->fb->ram;
+       struct nvkm_fb *fb = subdev->device->fb;
        struct nvkm_pci *pci = subdev->device->pci;
        struct nvkm_pstate *pstate;
        int ret, idx = 0;
@@ -190,7 +281,8 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
 
        nvkm_pcie_set_link(pci, pstate->pcie_speed, pstate->pcie_width);
 
-       if (ram && ram->func->calc) {
+       if (fb && fb->ram && fb->ram->func->calc) {
+               struct nvkm_ram *ram = fb->ram;
                int khz = pstate->base.domain[nv_clk_src_mem];
                do {
                        ret = ram->func->calc(ram, khz);
@@ -200,7 +292,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
                ram->func->tidy(ram);
        }
 
-       return nvkm_cstate_prog(clk, pstate, 0);
+       return nvkm_cstate_prog(clk, pstate, NVKM_CLK_CSTATE_HIGHEST);
 }
 
 static void
@@ -214,14 +306,14 @@ nvkm_pstate_work(struct work_struct *work)
                return;
        clk->pwrsrc = power_supply_is_system_supplied();
 
-       nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d D %d\n",
+       nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d°C D %d\n",
                   clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc,
-                  clk->astate, clk->tstate, clk->dstate);
+                  clk->astate, clk->temp, clk->dstate);
 
        pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc;
        if (clk->state_nr && pstate != -1) {
                pstate = (pstate < 0) ? clk->astate : pstate;
-               pstate = min(pstate, clk->state_nr - 1 + clk->tstate);
+               pstate = min(pstate, clk->state_nr - 1);
                pstate = max(pstate, clk->dstate);
        } else {
                pstate = clk->pstate = -1;
@@ -448,13 +540,12 @@ nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait)
 }
 
 int
-nvkm_clk_tstate(struct nvkm_clk *clk, int req, int rel)
+nvkm_clk_tstate(struct nvkm_clk *clk, u8 temp)
 {
-       if (!rel) clk->tstate  = req;
-       if ( rel) clk->tstate += rel;
-       clk->tstate = min(clk->tstate, 0);
-       clk->tstate = max(clk->tstate, -(clk->state_nr - 1));
-       return nvkm_pstate_calc(clk, true);
+       if (clk->temp == temp)
+               return 0;
+       clk->temp = temp;
+       return nvkm_pstate_calc(clk, false);
 }
 
 int
@@ -524,9 +615,9 @@ nvkm_clk_init(struct nvkm_subdev *subdev)
                return clk->func->init(clk);
 
        clk->astate = clk->state_nr - 1;
-       clk->tstate = 0;
        clk->dstate = 0;
        clk->pstate = -1;
+       clk->temp = 90; /* reasonable default value */
        nvkm_pstate_calc(clk, true);
        return 0;
 }
@@ -561,10 +652,22 @@ int
 nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device,
              int index, bool allow_reclock, struct nvkm_clk *clk)
 {
+       struct nvkm_subdev *subdev = &clk->subdev;
+       struct nvkm_bios *bios = device->bios;
        int ret, idx, arglen;
        const char *mode;
+       struct nvbios_vpstate_header h;
+
+       nvkm_subdev_ctor(&nvkm_clk, device, index, subdev);
+
+       if (bios && !nvbios_vpstate_parse(bios, &h)) {
+               struct nvbios_vpstate_entry base, boost;
+               if (!nvbios_vpstate_entry(bios, &h, h.boost_id, &boost))
+                       clk->boost_khz = boost.clock_mhz * 1000;
+               if (!nvbios_vpstate_entry(bios, &h, h.base_id, &base))
+                       clk->base_khz = base.clock_mhz * 1000;
+       }
 
-       nvkm_subdev_ctor(&nvkm_clk, device, index, &clk->subdev);
        clk->func = func;
        INIT_LIST_HEAD(&clk->states);
        clk->domains = func->domains;
@@ -607,6 +710,8 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device,
        if (mode)
                clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen);
 
+       clk->boost_mode = nvkm_longopt(device->cfgopt, "NvBoost",
+                                      NVKM_CLK_BOOST_NONE);
        return 0;
 }
 
index 89d5543118cff139e7736d2d69dcab0c12984041..7f67f9f5a550120e405337be5f5bc50bd7a419dd 100644 (file)
@@ -457,7 +457,7 @@ gf100_clk = {
                { nv_clk_src_hubk06 , 0x00 },
                { nv_clk_src_hubk01 , 0x01 },
                { nv_clk_src_copy   , 0x02 },
-               { nv_clk_src_gpc    , 0x03, 0, "core", 2000 },
+               { nv_clk_src_gpc    , 0x03, NVKM_CLK_DOM_FLAG_VPSTATE, "core", 2000 },
                { nv_clk_src_rop    , 0x04 },
                { nv_clk_src_mem    , 0x05, 0, "memory", 1000 },
                { nv_clk_src_vdec   , 0x06 },
index 06bc0d2d6ae195bb6c9cf5f88141bfef8e572a31..0b37e3da7feb6b8d0e7eecbfe64b7ee958fbe4b3 100644 (file)
@@ -491,7 +491,7 @@ gk104_clk = {
        .domains = {
                { nv_clk_src_crystal, 0xff },
                { nv_clk_src_href   , 0xff },
-               { nv_clk_src_gpc    , 0x00, NVKM_CLK_DOM_FLAG_CORE, "core", 2000 },
+               { nv_clk_src_gpc    , 0x00, NVKM_CLK_DOM_FLAG_CORE | NVKM_CLK_DOM_FLAG_VPSTATE, "core", 2000 },
                { nv_clk_src_hubk07 , 0x01, NVKM_CLK_DOM_FLAG_CORE },
                { nv_clk_src_rop    , 0x02, NVKM_CLK_DOM_FLAG_CORE },
                { nv_clk_src_mem    , 0x03, 0, "memory", 500 },
index 76433cc66fff2f1d96d1b4f81c5ab4a09b34e6e8..3841ad6be99e28568f3bf23912ed88309d7e7171 100644 (file)
@@ -50,24 +50,33 @@ gf100_fb_intr(struct nvkm_fb *base)
 }
 
 int
-gf100_fb_oneinit(struct nvkm_fb *fb)
+gf100_fb_oneinit(struct nvkm_fb *base)
 {
-       struct nvkm_device *device = fb->subdev.device;
+       struct gf100_fb *fb = gf100_fb(base);
+       struct nvkm_device *device = fb->base.subdev.device;
        int ret, size = 0x1000;
 
        size = nvkm_longopt(device->cfgopt, "MmuDebugBufferSize", size);
        size = min(size, 0x1000);
 
        ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, size, 0x1000,
-                             false, &fb->mmu_rd);
+                             false, &fb->base.mmu_rd);
        if (ret)
                return ret;
 
        ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, size, 0x1000,
-                             false, &fb->mmu_wr);
+                             false, &fb->base.mmu_wr);
        if (ret)
                return ret;
 
+       fb->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+       if (fb->r100c10_page) {
+               fb->r100c10 = dma_map_page(device->dev, fb->r100c10_page, 0,
+                                          PAGE_SIZE, DMA_BIDIRECTIONAL);
+               if (dma_mapping_error(device->dev, fb->r100c10))
+                       return -EFAULT;
+       }
+
        return 0;
 }
 
@@ -123,14 +132,6 @@ gf100_fb_new_(const struct nvkm_fb_func *func, struct nvkm_device *device,
        nvkm_fb_ctor(func, device, index, &fb->base);
        *pfb = &fb->base;
 
-       fb->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
-       if (fb->r100c10_page) {
-               fb->r100c10 = dma_map_page(device->dev, fb->r100c10_page, 0,
-                                          PAGE_SIZE, DMA_BIDIRECTIONAL);
-               if (dma_mapping_error(device->dev, fb->r100c10))
-                       return -EFAULT;
-       }
-
        return 0;
 }
 
index 1b5fb02eab2ae0c75f4a744f76e411b0a9f8b632..0595e0722bfcc0a050cb02185ba2c37449bebb0e 100644 (file)
@@ -210,6 +210,23 @@ nv50_fb_intr(struct nvkm_fb *base)
        nvkm_fifo_chan_put(fifo, flags, &chan);
 }
 
+static int
+nv50_fb_oneinit(struct nvkm_fb *base)
+{
+       struct nv50_fb *fb = nv50_fb(base);
+       struct nvkm_device *device = fb->base.subdev.device;
+
+       fb->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+       if (fb->r100c08_page) {
+               fb->r100c08 = dma_map_page(device->dev, fb->r100c08_page, 0,
+                                          PAGE_SIZE, DMA_BIDIRECTIONAL);
+               if (dma_mapping_error(device->dev, fb->r100c08))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
 static void
 nv50_fb_init(struct nvkm_fb *base)
 {
@@ -245,6 +262,7 @@ nv50_fb_dtor(struct nvkm_fb *base)
 static const struct nvkm_fb_func
 nv50_fb_ = {
        .dtor = nv50_fb_dtor,
+       .oneinit = nv50_fb_oneinit,
        .init = nv50_fb_init,
        .intr = nv50_fb_intr,
        .ram_new = nv50_fb_ram_new,
@@ -263,16 +281,6 @@ nv50_fb_new_(const struct nv50_fb_func *func, struct nvkm_device *device,
        fb->func = func;
        *pfb = &fb->base;
 
-       fb->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
-       if (fb->r100c08_page) {
-               fb->r100c08 = dma_map_page(device->dev, fb->r100c08_page, 0,
-                                          PAGE_SIZE, DMA_BIDIRECTIONAL);
-               if (dma_mapping_error(device->dev, fb->r100c08))
-                       return -EFAULT;
-       } else {
-               nvkm_warn(&fb->base.subdev, "failed 100c08 page alloc\n");
-       }
-
        return 0;
 }
 
index b9ec0ae6723ad1c71b93802c50236d27d0bc12bc..b60068b7d8f93c271aee7b3ce0218dcb4835cabc 100644 (file)
@@ -24,6 +24,7 @@ int  gf100_ram_ctor(const struct nvkm_ram_func *, struct nvkm_fb *,
 int  gf100_ram_get(struct nvkm_ram *, u64, u32, u32, u32, struct nvkm_mem **);
 void gf100_ram_put(struct nvkm_ram *, struct nvkm_mem **);
 
+int  gk104_ram_ctor(struct nvkm_fb *, struct nvkm_ram **, u32);
 int  gk104_ram_init(struct nvkm_ram *ram);
 
 /* RAM type-specific MR calculation routines */
index 1fa3ade468ae95783ae14aee88a223279e4d397b..7904fa41acef66aa86bbbe23e33cc83da160fc12 100644 (file)
@@ -259,7 +259,9 @@ gk104_ram_calc_gddr5(struct gk104_ram *ram, u32 freq)
 
        ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
        ram_block(fuc);
-       ram_wr32(fuc, 0x62c000, 0x0f0f0000);
+
+       if (nvkm_device_engine(ram->base.fb->subdev.device, NVKM_ENGINE_DISP))
+               ram_wr32(fuc, 0x62c000, 0x0f0f0000);
 
        /* MR1: turn termination on early, for some reason.. */
        if ((ram->base.mr[1] & 0x03c) != 0x030) {
@@ -658,7 +660,9 @@ gk104_ram_calc_gddr5(struct gk104_ram *ram, u32 freq)
                gk104_ram_train(fuc, 0x80020000, 0x01000000);
 
        ram_unblock(fuc);
-       ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
+
+       if (nvkm_device_engine(ram->base.fb->subdev.device, NVKM_ENGINE_DISP))
+               ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
 
        if (next->bios.rammap_11_08_01)
                data = 0x00000800;
@@ -706,7 +710,9 @@ gk104_ram_calc_sddr3(struct gk104_ram *ram, u32 freq)
 
        ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
        ram_block(fuc);
-       ram_wr32(fuc, 0x62c000, 0x0f0f0000);
+
+       if (nvkm_device_engine(ram->base.fb->subdev.device, NVKM_ENGINE_DISP))
+               ram_wr32(fuc, 0x62c000, 0x0f0f0000);
 
        if (vc == 1 && ram_have(fuc, gpio2E)) {
                u32 temp  = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]);
@@ -936,7 +942,9 @@ gk104_ram_calc_sddr3(struct gk104_ram *ram, u32 freq)
        ram_nsec(fuc, 1000);
 
        ram_unblock(fuc);
-       ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
+
+       if (nvkm_device_engine(ram->base.fb->subdev.device, NVKM_ENGINE_DISP))
+               ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
 
        if (next->bios.rammap_11_08_01)
                data = 0x00000800;
@@ -1529,6 +1537,12 @@ gk104_ram_func = {
 
 int
 gk104_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
+{
+       return gk104_ram_ctor(fb, pram, 0x022554);
+}
+
+int
+gk104_ram_ctor(struct nvkm_fb *fb, struct nvkm_ram **pram, u32 maskaddr)
 {
        struct nvkm_subdev *subdev = &fb->subdev;
        struct nvkm_device *device = subdev->device;
@@ -1544,7 +1558,7 @@ gk104_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
                return -ENOMEM;
        *pram = &ram->base;
 
-       ret = gf100_ram_ctor(&gk104_ram_func, fb, 0x022554, &ram->base);
+       ret = gf100_ram_ctor(&gk104_ram_func, fb, maskaddr, &ram->base);
        if (ret)
                return ret;
 
index 43d807f6ca7185f5851df5d01cda622acf70af5b..ac862d1d77bd6a75f9eac253fd8a068e680af32c 100644 (file)
  */
 #include "ram.h"
 
-static const struct nvkm_ram_func
-gm107_ram_func = {
-       .init = gk104_ram_init,
-       .get = gf100_ram_get,
-       .put = gf100_ram_put,
-};
-
 int
 gm107_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
 {
-       if (!(*pram = kzalloc(sizeof(**pram), GFP_KERNEL)))
-               return -ENOMEM;
-
-       return gf100_ram_ctor(&gm107_ram_func, fb, 0x021c14, *pram);
+       return gk104_ram_ctor(fb, pram, 0x021c14);
 }
index b7159b338fac06af9db9c83ca82b189d718d35b0..1a4ab825852ce2bf6649c28b8deacd9ace6f91ef 100644 (file)
@@ -29,7 +29,7 @@ gk20a_ibus_init_ibus_ring(struct nvkm_subdev *ibus)
        nvkm_mask(device, 0x137250, 0x3f, 0);
 
        nvkm_mask(device, 0x000200, 0x20, 0);
-       usleep_range(20, 30);
+       udelay(20);
        nvkm_mask(device, 0x000200, 0x20, 0x20);
 
        nvkm_wr32(device, 0x12004c, 0x4);
index 41bd5d0f76927e1e2f38ffe7e1d3c228a4d05139..658355fc9354fd7466a731fa5637d2443d5e544f 100644 (file)
@@ -95,61 +95,13 @@ nvkm_iccsense_ina3221_read(struct nvkm_iccsense *iccsense,
                                       40 * 8);
 }
 
-static void
-nvkm_iccsense_ina209_config(struct nvkm_iccsense *iccsense,
-                           struct nvkm_iccsense_sensor *sensor)
-{
-       struct nvkm_subdev *subdev = &iccsense->subdev;
-       /* configuration:
-        * 0x0007: 0x0007 shunt and bus continous
-        * 0x0078: 0x0078 128 samples shunt
-        * 0x0780: 0x0780 128 samples bus
-        * 0x1800: 0x0000 +-40 mV shunt range
-        * 0x2000: 0x0000 16V FSR
-         */
-       u16 value = 0x07ff;
-       nvkm_debug(subdev, "config for sensor id %i: 0x%x\n", sensor->id, value);
-       nv_wr16i2cr(sensor->i2c, sensor->addr, 0x00, value);
-}
-
-static void
-nvkm_iccsense_ina3221_config(struct nvkm_iccsense *iccsense,
-                            struct nvkm_iccsense_sensor *sensor)
-{
-       struct nvkm_subdev *subdev = &iccsense->subdev;
-       /* configuration:
-        * 0x0007: 0x0007 shunt and bus continous
-        * 0x0031: 0x0000 140 us conversion time shunt
-        * 0x01c0: 0x0000 140 us conversion time bus
-        * 0x0f00: 0x0f00 1024 samples
-        * 0x7000: 0x?000 channels
-         */
-       u16 value = 0x0e07;
-       if (sensor->rail_mask & 0x1)
-               value |= 0x1 << 14;
-       if (sensor->rail_mask & 0x2)
-               value |= 0x1 << 13;
-       if (sensor->rail_mask & 0x4)
-               value |= 0x1 << 12;
-       nvkm_debug(subdev, "config for sensor id %i: 0x%x\n", sensor->id, value);
-       nv_wr16i2cr(sensor->i2c, sensor->addr, 0x00, value);
-}
-
 static void
 nvkm_iccsense_sensor_config(struct nvkm_iccsense *iccsense,
                            struct nvkm_iccsense_sensor *sensor)
 {
-       switch (sensor->type) {
-       case NVBIOS_EXTDEV_INA209:
-       case NVBIOS_EXTDEV_INA219:
-               nvkm_iccsense_ina209_config(iccsense, sensor);
-               break;
-       case NVBIOS_EXTDEV_INA3221:
-               nvkm_iccsense_ina3221_config(iccsense, sensor);
-               break;
-       default:
-               break;
-       }
+       struct nvkm_subdev *subdev = &iccsense->subdev;
+       nvkm_trace(subdev, "write config of extdev %i: 0x%04x\n", sensor->id, sensor->config);
+       nv_wr16i2cr(sensor->i2c, sensor->addr, 0x00, sensor->config);
 }
 
 int
@@ -196,7 +148,6 @@ nvkm_iccsense_dtor(struct nvkm_subdev *subdev)
 static struct nvkm_iccsense_sensor*
 nvkm_iccsense_create_sensor(struct nvkm_iccsense *iccsense, u8 id)
 {
-
        struct nvkm_subdev *subdev = &iccsense->subdev;
        struct nvkm_bios *bios = subdev->device->bios;
        struct nvkm_i2c *i2c = subdev->device->i2c;
@@ -245,7 +196,7 @@ nvkm_iccsense_create_sensor(struct nvkm_iccsense *iccsense, u8 id)
        sensor->type = extdev.type;
        sensor->i2c = &i2c_bus->i2c;
        sensor->addr = addr;
-       sensor->rail_mask = 0x0;
+       sensor->config = 0x0;
        return sensor;
 }
 
@@ -273,48 +224,56 @@ nvkm_iccsense_oneinit(struct nvkm_subdev *subdev)
 
        iccsense->data_valid = true;
        for (i = 0; i < stbl.nr_entry; ++i) {
-               struct pwr_rail_t *r = &stbl.rail[i];
-               struct nvkm_iccsense_rail *rail;
+               struct pwr_rail_t *pwr_rail = &stbl.rail[i];
                struct nvkm_iccsense_sensor *sensor;
-               int (*read)(struct nvkm_iccsense *,
-                           struct nvkm_iccsense_rail *);
+               int r;
 
-               if (!r->mode || r->resistor_mohm == 0)
+               if (pwr_rail->mode != 1 || !pwr_rail->resistor_count)
                        continue;
 
-               sensor = nvkm_iccsense_get_sensor(iccsense, r->extdev_id);
+               sensor = nvkm_iccsense_get_sensor(iccsense, pwr_rail->extdev_id);
                if (!sensor)
                        continue;
 
-               switch (sensor->type) {
-               case NVBIOS_EXTDEV_INA209:
-                       if (r->rail != 0)
-                               continue;
-                       read = nvkm_iccsense_ina209_read;
-                       break;
-               case NVBIOS_EXTDEV_INA219:
-                       if (r->rail != 0)
+               if (!sensor->config)
+                       sensor->config = pwr_rail->config;
+               else if (sensor->config != pwr_rail->config)
+                       nvkm_error(subdev, "config mismatch found for extdev %i\n", pwr_rail->extdev_id);
+
+               for (r = 0; r < pwr_rail->resistor_count; ++r) {
+                       struct nvkm_iccsense_rail *rail;
+                       struct pwr_rail_resistor_t *res = &pwr_rail->resistors[r];
+                       int (*read)(struct nvkm_iccsense *,
+                                   struct nvkm_iccsense_rail *);
+
+                       if (!res->mohm || !res->enabled)
                                continue;
-                       read = nvkm_iccsense_ina219_read;
-                       break;
-               case NVBIOS_EXTDEV_INA3221:
-                       if (r->rail >= 3)
+
+                       switch (sensor->type) {
+                       case NVBIOS_EXTDEV_INA209:
+                               read = nvkm_iccsense_ina209_read;
+                               break;
+                       case NVBIOS_EXTDEV_INA219:
+                               read = nvkm_iccsense_ina219_read;
+                               break;
+                       case NVBIOS_EXTDEV_INA3221:
+                               read = nvkm_iccsense_ina3221_read;
+                               break;
+                       default:
                                continue;
-                       read = nvkm_iccsense_ina3221_read;
-                       break;
-               default:
-                       continue;
+                       }
+
+                       rail = kmalloc(sizeof(*rail), GFP_KERNEL);
+                       if (!rail)
+                               return -ENOMEM;
+
+                       rail->read = read;
+                       rail->sensor = sensor;
+                       rail->idx = r;
+                       rail->mohm = res->mohm;
+                       nvkm_debug(subdev, "create rail for extdev %i: { idx: %i, mohm: %i }\n", pwr_rail->extdev_id, r, rail->mohm);
+                       list_add_tail(&rail->head, &iccsense->rails);
                }
-
-               rail = kmalloc(sizeof(*rail), GFP_KERNEL);
-               if (!rail)
-                       return -ENOMEM;
-               sensor->rail_mask |= 1 << r->rail;
-               rail->read = read;
-               rail->sensor = sensor;
-               rail->idx = r->rail;
-               rail->mohm = r->resistor_mohm;
-               list_add_tail(&rail->head, &iccsense->rails);
        }
        return 0;
 }
index b72c31d2f90890ba6fcab8b024053776e9477aae..e90e0f6ed00884c81b101650f16227cea287f90d 100644 (file)
@@ -10,7 +10,7 @@ struct nvkm_iccsense_sensor {
        enum nvbios_extdev_type type;
        struct i2c_adapter *i2c;
        u8 addr;
-       u8 rail_mask;
+       u16 config;
 };
 
 struct nvkm_iccsense_rail {
index 45a2f8e784f9f01be2bd6b15c2199a9aa7470f26..9abfa5e2fe9f1368b05b4d9d6f89ca316af2c181 100644 (file)
@@ -23,8 +23,8 @@
  */
 #include "mxms.h"
 
-#define ROM16(x) le16_to_cpu(*(u16 *)&(x))
-#define ROM32(x) le32_to_cpu(*(u32 *)&(x))
+#define ROM16(x) get_unaligned_le16(&(x))
+#define ROM32(x) get_unaligned_le32(&(x))
 
 static u8 *
 mxms_data(struct nvkm_mxm *mxm)
index c34076223b7b23e5a3d952dc94ca4dda7370978a..bcd179ba11d0d2cd11fa40ef1c7559843a8a1881 100644 (file)
@@ -1,6 +1,7 @@
 nvkm-y += nvkm/subdev/volt/base.o
 nvkm-y += nvkm/subdev/volt/gpio.o
 nvkm-y += nvkm/subdev/volt/nv40.o
+nvkm-y += nvkm/subdev/volt/gf100.o
 nvkm-y += nvkm/subdev/volt/gk104.o
 nvkm-y += nvkm/subdev/volt/gk20a.o
 nvkm-y += nvkm/subdev/volt/gm20b.o
index 1c3d23b0e84ad063c0c11310828327e232ac03bd..e8569b04b55df316c3f1df9778aec4e37d089e96 100644 (file)
@@ -26,6 +26,7 @@
 #include <subdev/bios.h>
 #include <subdev/bios/vmap.h>
 #include <subdev/bios/volt.h>
+#include <subdev/therm.h>
 
 int
 nvkm_volt_get(struct nvkm_volt *volt)
@@ -50,23 +51,35 @@ static int
 nvkm_volt_set(struct nvkm_volt *volt, u32 uv)
 {
        struct nvkm_subdev *subdev = &volt->subdev;
-       int i, ret = -EINVAL;
+       int i, ret = -EINVAL, best_err = volt->max_uv, best = -1;
 
        if (volt->func->volt_set)
                return volt->func->volt_set(volt, uv);
 
        for (i = 0; i < volt->vid_nr; i++) {
-               if (volt->vid[i].uv == uv) {
-                       ret = volt->func->vid_set(volt, volt->vid[i].vid);
-                       nvkm_debug(subdev, "set %duv: %d\n", uv, ret);
+               int err = volt->vid[i].uv - uv;
+               if (err < 0 || err > best_err)
+                       continue;
+
+               best_err = err;
+               best = i;
+               if (best_err == 0)
                        break;
-               }
        }
+
+       if (best == -1) {
+               nvkm_error(subdev, "couldn't set %iuv\n", uv);
+               return ret;
+       }
+
+       ret = volt->func->vid_set(volt, volt->vid[best].vid);
+       nvkm_debug(subdev, "set req %duv to %duv: %d\n", uv,
+                  volt->vid[best].uv, ret);
        return ret;
 }
 
-static int
-nvkm_volt_map(struct nvkm_volt *volt, u8 id)
+int
+nvkm_volt_map_min(struct nvkm_volt *volt, u8 id)
 {
        struct nvkm_bios *bios = volt->subdev.device->bios;
        struct nvbios_vmap_entry info;
@@ -76,7 +89,7 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id)
        vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
        if (vmap) {
                if (info.link != 0xff) {
-                       int ret = nvkm_volt_map(volt, info.link);
+                       int ret = nvkm_volt_map_min(volt, info.link);
                        if (ret < 0)
                                return ret;
                        info.min += ret;
@@ -88,19 +101,79 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id)
 }
 
 int
-nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, int condition)
+nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp)
+{
+       struct nvkm_bios *bios = volt->subdev.device->bios;
+       struct nvbios_vmap_entry info;
+       u8  ver, len;
+       u16 vmap;
+
+       vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
+       if (vmap) {
+               s64 result;
+
+               if (volt->speedo < 0)
+                       return volt->speedo;
+
+               if (ver == 0x10 || (ver == 0x20 && info.mode == 0)) {
+                       result  = div64_s64((s64)info.arg[0], 10);
+                       result += div64_s64((s64)info.arg[1] * volt->speedo, 10);
+                       result += div64_s64((s64)info.arg[2] * volt->speedo * volt->speedo, 100000);
+               } else if (ver == 0x20) {
+                       switch (info.mode) {
+                       /* 0x0 handled above! */
+                       case 0x1:
+                               result =  ((s64)info.arg[0] * 15625) >> 18;
+                               result += ((s64)info.arg[1] * volt->speedo * 15625) >> 18;
+                               result += ((s64)info.arg[2] * temp * 15625) >> 10;
+                               result += ((s64)info.arg[3] * volt->speedo * temp * 15625) >> 18;
+                               result += ((s64)info.arg[4] * volt->speedo * volt->speedo * 15625) >> 30;
+                               result += ((s64)info.arg[5] * temp * temp * 15625) >> 18;
+                               break;
+                       case 0x3:
+                               result = (info.min + info.max) / 2;
+                               break;
+                       case 0x2:
+                       default:
+                               result = info.min;
+                               break;
+                       }
+               } else {
+                       return -ENODEV;
+               }
+
+               result = min(max(result, (s64)info.min), (s64)info.max);
+
+               if (info.link != 0xff) {
+                       int ret = nvkm_volt_map(volt, info.link, temp);
+                       if (ret < 0)
+                               return ret;
+                       result += ret;
+               }
+               return result;
+       }
+
+       return id ? id * 10000 : -ENODEV;
+}
+
+int
+nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, u8 temp,
+                int condition)
 {
        int ret;
 
        if (volt->func->set_id)
                return volt->func->set_id(volt, id, condition);
 
-       ret = nvkm_volt_map(volt, id);
+       ret = nvkm_volt_map(volt, id, temp);
        if (ret >= 0) {
                int prev = nvkm_volt_get(volt);
                if (!condition || prev < 0 ||
                    (condition < 0 && ret < prev) ||
                    (condition > 0 && ret > prev)) {
+                       int min = nvkm_volt_map(volt, min_id, temp);
+                       if (min >= 0)
+                               ret = max(min, ret);
                        ret = nvkm_volt_set(volt, ret);
                } else {
                        ret = 0;
@@ -112,6 +185,7 @@ nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, int condition)
 static void
 nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt)
 {
+       struct nvkm_subdev *subdev = &bios->subdev;
        struct nvbios_volt_entry ivid;
        struct nvbios_volt info;
        u8  ver, hdr, cnt, len;
@@ -119,7 +193,8 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt)
        int i;
 
        data = nvbios_volt_parse(bios, &ver, &hdr, &cnt, &len, &info);
-       if (data && info.vidmask && info.base && info.step) {
+       if (data && info.vidmask && info.base && info.step && info.ranged) {
+               nvkm_debug(subdev, "found ranged based VIDs\n");
                volt->min_uv = info.min;
                volt->max_uv = info.max;
                for (i = 0; i < info.vidmask + 1; i++) {
@@ -132,7 +207,8 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt)
                        info.base += info.step;
                }
                volt->vid_mask = info.vidmask;
-       } else if (data && info.vidmask) {
+       } else if (data && info.vidmask && !info.ranged) {
+               nvkm_debug(subdev, "found entry based VIDs\n");
                volt->min_uv = 0xffffffff;
                volt->max_uv = 0;
                for (i = 0; i < cnt; i++) {
@@ -153,6 +229,14 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt)
        }
 }
 
+static int
+nvkm_volt_speedo_read(struct nvkm_volt *volt)
+{
+       if (volt->func->speedo_read)
+               return volt->func->speedo_read(volt);
+       return -EINVAL;
+}
+
 static int
 nvkm_volt_init(struct nvkm_subdev *subdev)
 {
@@ -167,6 +251,21 @@ nvkm_volt_init(struct nvkm_subdev *subdev)
        return 0;
 }
 
+static int
+nvkm_volt_oneinit(struct nvkm_subdev *subdev)
+{
+       struct nvkm_volt *volt = nvkm_volt(subdev);
+
+       volt->speedo = nvkm_volt_speedo_read(volt);
+       if (volt->speedo > 0)
+               nvkm_debug(&volt->subdev, "speedo %x\n", volt->speedo);
+
+       if (volt->func->oneinit)
+               return volt->func->oneinit(volt);
+
+       return 0;
+}
+
 static void *
 nvkm_volt_dtor(struct nvkm_subdev *subdev)
 {
@@ -177,6 +276,7 @@ static const struct nvkm_subdev_func
 nvkm_volt = {
        .dtor = nvkm_volt_dtor,
        .init = nvkm_volt_init,
+       .oneinit = nvkm_volt_oneinit,
 };
 
 void
@@ -191,9 +291,22 @@ nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device,
 
        /* Assuming the non-bios device should build the voltage table later */
        if (bios) {
+               u8 ver, hdr, cnt, len;
+               struct nvbios_vmap vmap;
+
                nvkm_volt_parse_bios(bios, volt);
                nvkm_debug(&volt->subdev, "min: %iuv max: %iuv\n",
                           volt->min_uv, volt->max_uv);
+
+               if (nvbios_vmap_parse(bios, &ver, &hdr, &cnt, &len, &vmap)) {
+                       volt->max0_id = vmap.max0;
+                       volt->max1_id = vmap.max1;
+                       volt->max2_id = vmap.max2;
+               } else {
+                       volt->max0_id = 0xff;
+                       volt->max1_id = 0xff;
+                       volt->max2_id = 0xff;
+               }
        }
 
        if (volt->vid_nr) {
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf100.c
new file mode 100644 (file)
index 0000000..d9ed692
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Karol Herbst
+ *
+ * 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: Karol Herbst
+ */
+#include "priv.h"
+
+#include <subdev/fuse.h>
+
+static int
+gf100_volt_speedo_read(struct nvkm_volt *volt)
+{
+       struct nvkm_device *device = volt->subdev.device;
+       struct nvkm_fuse *fuse = device->fuse;
+
+       if (!fuse)
+               return -EINVAL;
+
+       return nvkm_fuse_read(fuse, 0x1cc);
+}
+
+int
+gf100_volt_oneinit(struct nvkm_volt *volt)
+{
+       struct nvkm_subdev *subdev = &volt->subdev;
+       if (volt->speedo <= 0)
+               nvkm_error(subdev, "couldn't find speedo value, volting not "
+                          "possible\n");
+       return 0;
+}
+
+static const struct nvkm_volt_func
+gf100_volt = {
+       .oneinit = gf100_volt_oneinit,
+       .vid_get = nvkm_voltgpio_get,
+       .vid_set = nvkm_voltgpio_set,
+       .speedo_read = gf100_volt_speedo_read,
+};
+
+int
+gf100_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt)
+{
+       struct nvkm_volt *volt;
+       int ret;
+
+       ret = nvkm_volt_new_(&gf100_volt, device, index, &volt);
+       *pvolt = volt;
+       if (ret)
+               return ret;
+
+       return nvkm_voltgpio_init(volt);
+}
index 420bd84d84834083cfddcb56203811af94a5d799..b2c5d1166a1394ebda84b739811b62db7d939fd8 100644 (file)
@@ -27,6 +27,7 @@
 #include <subdev/gpio.h>
 #include <subdev/bios.h>
 #include <subdev/bios/volt.h>
+#include <subdev/fuse.h>
 
 #define gk104_volt(p) container_of((p), struct gk104_volt, base)
 struct gk104_volt {
@@ -64,13 +65,33 @@ gk104_volt_set(struct nvkm_volt *base, u32 uv)
        return 0;
 }
 
+static int
+gk104_volt_speedo_read(struct nvkm_volt *volt)
+{
+       struct nvkm_device *device = volt->subdev.device;
+       struct nvkm_fuse *fuse = device->fuse;
+       int ret;
+
+       if (!fuse)
+               return -EINVAL;
+
+       nvkm_wr32(device, 0x122634, 0x0);
+       ret = nvkm_fuse_read(fuse, 0x3a8);
+       nvkm_wr32(device, 0x122634, 0x41);
+       return ret;
+}
+
 static const struct nvkm_volt_func
 gk104_volt_pwm = {
+       .oneinit = gf100_volt_oneinit,
        .volt_get = gk104_volt_get,
        .volt_set = gk104_volt_set,
+       .speedo_read = gk104_volt_speedo_read,
 }, gk104_volt_gpio = {
+       .oneinit = gf100_volt_oneinit,
        .vid_get = nvkm_voltgpio_get,
        .vid_set = nvkm_voltgpio_set,
+       .speedo_read = gk104_volt_speedo_read,
 };
 
 int
index d5140d991161318cf13f6f815d5ab9c76d4ee50e..354bafe4b4e2d7b9bac66060c5990d2cf8b78274 100644 (file)
@@ -9,11 +9,13 @@ int nvkm_volt_new_(const struct nvkm_volt_func *, struct nvkm_device *,
                   int index, struct nvkm_volt **);
 
 struct nvkm_volt_func {
+       int (*oneinit)(struct nvkm_volt *);
        int (*volt_get)(struct nvkm_volt *);
        int (*volt_set)(struct nvkm_volt *, u32 uv);
        int (*vid_get)(struct nvkm_volt *);
        int (*vid_set)(struct nvkm_volt *, u8 vid);
        int (*set_id)(struct nvkm_volt *, u8 id, int condition);
+       int (*speedo_read)(struct nvkm_volt *);
 };
 
 int nvkm_voltgpio_init(struct nvkm_volt *);
@@ -23,4 +25,6 @@ int nvkm_voltgpio_set(struct nvkm_volt *, u8);
 int nvkm_voltpwm_init(struct nvkm_volt *volt);
 int nvkm_voltpwm_get(struct nvkm_volt *volt);
 int nvkm_voltpwm_set(struct nvkm_volt *volt, u32 uv);
+
+int gf100_volt_oneinit(struct nvkm_volt *);
 #endif