]> git.openfabrics.org - ~emulex/infiniband.git/commitdiff
drm/nouveau/clock: pull in the implementation from all over the place
authorBen Skeggs <bskeggs@redhat.com>
Tue, 10 Jul 2012 07:26:46 +0000 (17:26 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 3 Oct 2012 03:12:47 +0000 (13:12 +1000)
Still missing the main bits we use to change performance levels, I'll get
to it after all the hard yakka has been finished.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
29 files changed:
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/clock.h
drivers/gpu/drm/nouveau/core/include/subdev/vga.h
drivers/gpu/drm/nouveau/core/subdev/bios/pll.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c
drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/clock/pll.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bios.h
drivers/gpu/drm/nouveau/nouveau_calc.c
drivers/gpu/drm/nouveau/nouveau_compat.c
drivers/gpu/drm/nouveau/nouveau_compat.h
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_hw.c
drivers/gpu/drm/nouveau/nouveau_hw.h
drivers/gpu/drm/nouveau/nv04_crtc.c
drivers/gpu/drm/nouveau/nv04_pm.c
drivers/gpu/drm/nouveau/nv40_pm.c
drivers/gpu/drm/nouveau/nv50_calc.c [deleted file]
drivers/gpu/drm/nouveau/nv50_crtc.c
drivers/gpu/drm/nouveau/nv50_pm.c
drivers/gpu/drm/nouveau/nva3_pm.c
drivers/gpu/drm/nouveau/nvc0_pm.c

index 2964d71d3996bac6e38ff5a9b63c931a43acef43..2574fd35122acd23df2972b3582433ef143fb0e2 100644 (file)
@@ -23,11 +23,14 @@ nouveau-y += core/subdev/bios/bit.o
 nouveau-y += core/subdev/bios/dcb.o
 nouveau-y += core/subdev/bios/gpio.o
 nouveau-y += core/subdev/bios/i2c.o
+nouveau-y += core/subdev/bios/pll.o
 nouveau-y += core/subdev/clock/nv04.o
 nouveau-y += core/subdev/clock/nv40.o
 nouveau-y += core/subdev/clock/nv50.o
 nouveau-y += core/subdev/clock/nva3.o
 nouveau-y += core/subdev/clock/nvc0.o
+nouveau-y += core/subdev/clock/pllnv04.o
+nouveau-y += core/subdev/clock/pllnva3.o
 nouveau-y += core/subdev/device/base.o
 nouveau-y += core/subdev/device/nv04.o
 nouveau-y += core/subdev/device/nv10.o
@@ -114,7 +117,6 @@ nouveau-y += nouveau_drm.o nouveau_compat.o \
              nv50_cursor.o nv50_display.o \
              nvd0_display.o \
              nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \
-            nv50_calc.o \
             nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \
             nouveau_prime.o
 
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h
new file mode 100644 (file)
index 0000000..c345097
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef __NVBIOS_PLL_H__
+#define __NVBIOS_PLL_H__
+
+/*XXX: kill me */
+struct nouveau_pll_vals {
+       union {
+               struct {
+#ifdef __BIG_ENDIAN
+                       uint8_t N1, M1, N2, M2;
+#else
+                       uint8_t M1, N1, M2, N2;
+#endif
+               };
+               struct {
+                       uint16_t NM1, NM2;
+               } __attribute__((packed));
+       };
+       int log2P;
+
+       int refclk;
+};
+
+struct nouveau_bios;
+
+/* these match types in pll limits table version 0x40,
+ * nouveau uses them on all chipsets internally where a
+ * specific pll needs to be referenced, but the exact
+ * register isn't known.
+ */
+enum nvbios_pll_type {
+       PLL_CORE   = 0x01,
+       PLL_SHADER = 0x02,
+       PLL_UNK03  = 0x03,
+       PLL_MEMORY = 0x04,
+       PLL_VDEC   = 0x05,
+       PLL_UNK40  = 0x40,
+       PLL_UNK41  = 0x41,
+       PLL_UNK42  = 0x42,
+       PLL_VPLL0  = 0x80,
+       PLL_VPLL1  = 0x81,
+       PLL_MAX    = 0xff
+};
+
+struct nvbios_pll {
+       enum nvbios_pll_type type;
+       u32 reg;
+       u32 refclk;
+
+       u8 min_p;
+       u8 max_p;
+       u8 bias_p;
+
+       /*
+        * for most pre nv50 cards setting a log2P of 7 (the common max_log2p
+        * value) is no different to 6 (at least for vplls) so allowing the MNP
+        * calc to use 7 causes the generated clock to be out by a factor of 2.
+        * however, max_log2p cannot be fixed-up during parsing as the
+        * unmodified max_log2p value is still needed for setting mplls, hence
+        * an additional max_usable_log2p member
+        */
+       u8 max_p_usable;
+
+       struct {
+               u32 min_freq;
+               u32 max_freq;
+               u32 min_inputfreq;
+               u32 max_inputfreq;
+               u8  min_m;
+               u8  max_m;
+               u8  min_n;
+               u8  max_n;
+       } vco1, vco2;
+};
+
+int nvbios_pll_parse(struct nouveau_bios *, u32 type, struct nvbios_pll *);
+
+#endif
index 137c4598a1b676ec93d705a002fed8be97660f3e..39e73b91d360a3753dbcc3c210ca1e77f00332ec 100644 (file)
@@ -4,9 +4,21 @@
 #include <core/device.h>
 #include <core/subdev.h>
 
+struct nouveau_pll_vals;
+struct nvbios_pll;
+
 struct nouveau_clock {
        struct nouveau_subdev base;
-       void (*pll_set)(struct nouveau_clock *, u32 type, u32 freq);
+
+       int (*pll_set)(struct nouveau_clock *, u32 type, u32 freq);
+
+       /*XXX: die, these are here *only* to support the completely
+        *     bat-shit insane what-was-nouveau_hw.c code
+        */
+       int (*pll_calc)(struct nouveau_clock *, struct nvbios_pll *,
+                       int clk, struct nouveau_pll_vals *pv);
+       int (*pll_prog)(struct nouveau_clock *, u32 reg1,
+                       struct nouveau_pll_vals *pv);
 };
 
 static inline struct nouveau_clock *
@@ -37,4 +49,11 @@ extern struct nouveau_oclass nv50_clock_oclass;
 extern struct nouveau_oclass nva3_clock_oclass;
 extern struct nouveau_oclass nvc0_clock_oclass;
 
+int nv04_clock_pll_set(struct nouveau_clock *, u32 type, u32 freq);
+int nv04_clock_pll_calc(struct nouveau_clock *, struct nvbios_pll *,
+                       int clk, struct nouveau_pll_vals *);
+int nv04_clock_pll_prog(struct nouveau_clock *, u32 reg1,
+                       struct nouveau_pll_vals *);
+
+
 #endif
index d81df1ae442ddcb7c35ea831ce51437cb59b300f..fee09ad818e48899d84cb4bfd2bbd4fc6228d5d3 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __NOUVEAU_VGA_H__
 #define __NOUVEAU_VGA_H__
 
+#include <core/os.h>
+
 /* access to various legacy io ports */
 u8   nv_rdport(void *obj, int head, u16 port);
 void nv_wrport(void *obj, int head, u16 port, u8 value);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/pll.c b/drivers/gpu/drm/nouveau/core/subdev/bios/pll.c
new file mode 100644 (file)
index 0000000..5e5f4cd
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2005-2006 Erik Waling
+ * Copyright 2006 Stephane Marchesin
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * 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 AUTHORS 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 <subdev/vga.h>
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/bmp.h>
+#include <subdev/bios/pll.h>
+
+struct pll_mapping {
+       u8  type;
+       u32 reg;
+};
+
+static struct pll_mapping
+nv04_pll_mapping[] = {
+       { PLL_CORE  , 0x680500 },
+       { PLL_MEMORY, 0x680504 },
+       { PLL_VPLL0 , 0x680508 },
+       { PLL_VPLL1 , 0x680520 },
+       {}
+};
+
+static struct pll_mapping
+nv40_pll_mapping[] = {
+       { PLL_CORE  , 0x004000 },
+       { PLL_MEMORY, 0x004020 },
+       { PLL_VPLL0 , 0x680508 },
+       { PLL_VPLL1 , 0x680520 },
+       {}
+};
+
+static struct pll_mapping
+nv50_pll_mapping[] = {
+       { PLL_CORE  , 0x004028 },
+       { PLL_SHADER, 0x004020 },
+       { PLL_UNK03 , 0x004000 },
+       { PLL_MEMORY, 0x004008 },
+       { PLL_UNK40 , 0x00e810 },
+       { PLL_UNK41 , 0x00e818 },
+       { PLL_UNK42 , 0x00e824 },
+       { PLL_VPLL0 , 0x614100 },
+       { PLL_VPLL1 , 0x614900 },
+       {}
+};
+
+static struct pll_mapping
+nv84_pll_mapping[] = {
+       { PLL_CORE  , 0x004028 },
+       { PLL_SHADER, 0x004020 },
+       { PLL_MEMORY, 0x004008 },
+       { PLL_VDEC  , 0x004030 },
+       { PLL_UNK41 , 0x00e818 },
+       { PLL_VPLL0 , 0x614100 },
+       { PLL_VPLL1 , 0x614900 },
+       {}
+};
+
+static u16
+pll_limits_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       struct bit_entry bit_C;
+
+       if (!bit_entry(bios, 'C', &bit_C) && bit_C.length >= 10) {
+               u16 data = nv_ro16(bios, bit_C.offset + 8);
+               if (data) {
+                       *ver = nv_ro08(bios, data + 0);
+                       *hdr = nv_ro08(bios, data + 1);
+                       *len = nv_ro08(bios, data + 2);
+                       *cnt = nv_ro08(bios, data + 3);
+                       return data;
+               }
+       }
+
+       if (bmp_version(bios) >= 0x0524) {
+               u16 data = nv_ro16(bios, bios->bmp_offset + 142);
+               if (data) {
+                       *ver = nv_ro08(bios, data + 0);
+                       *hdr = 1;
+                       *cnt = 1;
+                       *len = 0x18;
+                       return data;
+               }
+       }
+
+       *ver = 0x00;
+       return 0x0000;
+}
+
+static struct pll_mapping *
+pll_map(struct nouveau_bios *bios)
+{
+       switch (nv_device(bios)->card_type) {
+       case NV_04:
+       case NV_10:
+       case NV_20:
+       case NV_30:
+               return nv04_pll_mapping;
+               break;
+       case NV_40:
+               return nv40_pll_mapping;
+       case NV_50:
+               if (nv_device(bios)->chipset == 0x50)
+                       return nv50_pll_mapping;
+               else
+               if (nv_device(bios)->chipset <  0xa3 ||
+                   nv_device(bios)->chipset == 0xaa ||
+                   nv_device(bios)->chipset == 0xac)
+                       return nv84_pll_mapping;
+       default:
+               return NULL;
+       }
+}
+
+static u16
+pll_map_reg(struct nouveau_bios *bios, u32 reg, u32 *type, u8 *ver, u8 *len)
+{
+       struct pll_mapping *map;
+       u8  hdr, cnt;
+       u16 data;
+
+       data = pll_limits_table(bios, ver, &hdr, &cnt, len);
+       if (data && *ver >= 0x30) {
+               data += hdr;
+               while (cnt--) {
+                       if (nv_ro32(bios, data + 3) == reg) {
+                               *type = nv_ro08(bios, data + 0);
+                               return data;
+                       }
+                       data += *len;
+               }
+               return 0x0000;
+       }
+
+       map = pll_map(bios);
+       while (map->reg) {
+               if (map->reg == reg && *ver >= 0x20) {
+                       u16 addr = (data += hdr);
+                       while (cnt--) {
+                               if (nv_ro32(bios, data) == map->reg) {
+                                       *type = map->type;
+                                       return data;
+                               }
+                               data += *len;
+                       }
+                       return addr;
+               } else
+               if (map->reg == reg) {
+                       *type = map->type;
+                       return data + 1;
+               }
+               map++;
+       }
+
+       return 0x0000;
+}
+
+static u16
+pll_map_type(struct nouveau_bios *bios, u8 type, u32 *reg, u8 *ver, u8 *len)
+{
+       struct pll_mapping *map;
+       u8  hdr, cnt;
+       u16 data;
+
+       data = pll_limits_table(bios, ver, &hdr, &cnt, len);
+       if (data && *ver >= 0x30) {
+               data += hdr;
+               while (cnt--) {
+                       if (nv_ro08(bios, data + 0) == type) {
+                               *reg = nv_ro32(bios, data + 3);
+                               return data;
+                       }
+                       data += *len;
+               }
+               return 0x0000;
+       }
+
+       map = pll_map(bios);
+       while (map->reg) {
+               if (map->type == type && *ver >= 0x20) {
+                       u16 addr = (data += hdr);
+                       while (cnt--) {
+                               if (nv_ro32(bios, data) == map->reg) {
+                                       *reg = map->reg;
+                                       return data;
+                               }
+                               data += *len;
+                       }
+                       return addr;
+               } else
+               if (map->type == type) {
+                       *reg = map->reg;
+                       return data + 1;
+               }
+               map++;
+       }
+
+       return 0x0000;
+}
+
+int
+nvbios_pll_parse(struct nouveau_bios *bios, u32 type, struct nvbios_pll *info)
+{
+       u8  ver, len;
+       u32 reg = type;
+       u16 data;
+
+       if (type > PLL_MAX) {
+               reg  = type;
+               data = pll_map_reg(bios, reg, &type, &ver, &len);
+       } else {
+               data = pll_map_type(bios, type, &reg, &ver, &len);
+       }
+
+       if (ver && !data)
+               return -ENOENT;
+
+       memset(info, 0, sizeof(*info));
+       info->type = type;
+       info->reg = reg;
+
+       switch (ver) {
+       case 0x00:
+               break;
+       case 0x10:
+       case 0x11:
+               info->vco1.min_freq = nv_ro32(bios, data + 0);
+               info->vco1.max_freq = nv_ro32(bios, data + 4);
+               info->vco2.min_freq = nv_ro32(bios, data + 8);
+               info->vco2.max_freq = nv_ro32(bios, data + 12);
+               info->vco1.min_inputfreq = nv_ro32(bios, data + 16);
+               info->vco2.min_inputfreq = nv_ro32(bios, data + 20);
+               info->vco1.max_inputfreq = INT_MAX;
+               info->vco2.max_inputfreq = INT_MAX;
+
+               info->max_p = 0x7;
+               info->max_p_usable = 0x6;
+
+               /* these values taken from nv30/31/36 */
+               switch (bios->version.chip) {
+               case 0x36:
+                       info->vco1.min_n = 0x5;
+                       break;
+               default:
+                       info->vco1.min_n = 0x1;
+                       break;
+               }
+               info->vco1.max_n = 0xff;
+               info->vco1.min_m = 0x1;
+               info->vco1.max_m = 0xd;
+
+               /*
+                * On nv30, 31, 36 (i.e. all cards with two stage PLLs with this
+                * table version (apart from nv35)), N2 is compared to
+                * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and
+                * save a comparison
+                */
+               info->vco2.min_n = 0x4;
+               switch (bios->version.chip) {
+               case 0x30:
+               case 0x35:
+                       info->vco2.max_n = 0x1f;
+                       break;
+               default:
+                       info->vco2.max_n = 0x28;
+                       break;
+               }
+               info->vco2.min_m = 0x1;
+               info->vco2.max_m = 0x4;
+               break;
+       case 0x20:
+       case 0x21:
+               info->vco1.min_freq = nv_ro16(bios, data + 4) * 1000;
+               info->vco1.max_freq = nv_ro16(bios, data + 6) * 1000;
+               info->vco2.min_freq = nv_ro16(bios, data + 8) * 1000;
+               info->vco2.max_freq = nv_ro16(bios, data + 10) * 1000;
+               info->vco1.min_inputfreq = nv_ro16(bios, data + 12) * 1000;
+               info->vco2.min_inputfreq = nv_ro16(bios, data + 14) * 1000;
+               info->vco1.max_inputfreq = nv_ro16(bios, data + 16) * 1000;
+               info->vco2.max_inputfreq = nv_ro16(bios, data + 18) * 1000;
+               info->vco1.min_n = nv_ro08(bios, data + 20);
+               info->vco1.max_n = nv_ro08(bios, data + 21);
+               info->vco1.min_m = nv_ro08(bios, data + 22);
+               info->vco1.max_m = nv_ro08(bios, data + 23);
+               info->vco2.min_n = nv_ro08(bios, data + 24);
+               info->vco2.max_n = nv_ro08(bios, data + 25);
+               info->vco2.min_m = nv_ro08(bios, data + 26);
+               info->vco2.max_m = nv_ro08(bios, data + 27);
+
+               info->max_p = nv_ro08(bios, data + 29);
+               info->max_p_usable = info->max_p;
+               if (bios->version.chip < 0x60)
+                       info->max_p_usable = 0x6;
+               info->bias_p = nv_ro08(bios, data + 30);
+
+               if (len > 0x22)
+                       info->refclk = nv_ro32(bios, data + 31);
+               break;
+       case 0x30:
+               data = nv_ro16(bios, data + 1);
+
+               info->vco1.min_freq = nv_ro16(bios, data + 0) * 1000;
+               info->vco1.max_freq = nv_ro16(bios, data + 2) * 1000;
+               info->vco2.min_freq = nv_ro16(bios, data + 4) * 1000;
+               info->vco2.max_freq = nv_ro16(bios, data + 6) * 1000;
+               info->vco1.min_inputfreq = nv_ro16(bios, data + 8) * 1000;
+               info->vco2.min_inputfreq = nv_ro16(bios, data + 10) * 1000;
+               info->vco1.max_inputfreq = nv_ro16(bios, data + 12) * 1000;
+               info->vco2.max_inputfreq = nv_ro16(bios, data + 14) * 1000;
+               info->vco1.min_n = nv_ro08(bios, data + 16);
+               info->vco1.max_n = nv_ro08(bios, data + 17);
+               info->vco1.min_m = nv_ro08(bios, data + 18);
+               info->vco1.max_m = nv_ro08(bios, data + 19);
+               info->vco2.min_n = nv_ro08(bios, data + 20);
+               info->vco2.max_n = nv_ro08(bios, data + 21);
+               info->vco2.min_m = nv_ro08(bios, data + 22);
+               info->vco2.max_m = nv_ro08(bios, data + 23);
+               info->max_p_usable = info->max_p = nv_ro08(bios, data + 25);
+               info->bias_p = nv_ro08(bios, data + 27);
+               info->refclk = nv_ro32(bios, data + 28);
+               break;
+       case 0x40:
+               info->refclk = nv_ro16(bios, data + 9) * 1000;
+               data = nv_ro16(bios, data + 1);
+
+               info->vco1.min_freq = nv_ro16(bios, data + 0) * 1000;
+               info->vco1.max_freq = nv_ro16(bios, data + 2) * 1000;
+               info->vco1.min_inputfreq = nv_ro16(bios, data + 4) * 1000;
+               info->vco1.max_inputfreq = nv_ro16(bios, data + 6) * 1000;
+               info->vco1.min_m = nv_ro08(bios, data + 8);
+               info->vco1.max_m = nv_ro08(bios, data + 9);
+               info->vco1.min_n = nv_ro08(bios, data + 10);
+               info->vco1.max_n = nv_ro08(bios, data + 11);
+               info->min_p = nv_ro08(bios, data + 12);
+               info->max_p = nv_ro08(bios, data + 13);
+               break;
+       default:
+               nv_error(bios, "unknown pll limits version 0x%02x\n", ver);
+               return -EINVAL;
+       }
+
+       if (!info->refclk) {
+               info->refclk = nv_device(bios)->crystal;
+               if (bios->version.chip == 0x51) {
+                       u32 sel_clk = nv_rd32(bios, 0x680524);
+                       if ((info->reg == 0x680508 && sel_clk & 0x20) ||
+                           (info->reg == 0x680520 && sel_clk & 0x80)) {
+                               if (nv_rdvgac(bios, 0, 0x27) < 0xa3)
+                                       info->refclk = 200000;
+                               else
+                                       info->refclk = 25000;
+                       }
+               }
+       }
+
+       /*
+        * By now any valid limit table ought to have set a max frequency for
+        * vco1, so if it's zero it's either a pre limit table bios, or one
+        * with an empty limit table (seen on nv18)
+        */
+       if (!info->vco1.max_freq) {
+               info->vco1.max_freq = nv_ro32(bios, bios->bmp_offset + 67);
+               info->vco1.min_freq = nv_ro32(bios, bios->bmp_offset + 71);
+               if (bmp_version(bios) < 0x0506) {
+                       info->vco1.max_freq = 256000;
+                       info->vco1.min_freq = 128000;
+               }
+
+               info->vco1.min_inputfreq = 0;
+               info->vco1.max_inputfreq = INT_MAX;
+               info->vco1.min_n = 0x1;
+               info->vco1.max_n = 0xff;
+               info->vco1.min_m = 0x1;
+
+               if (nv_device(bios)->crystal == 13500) {
+                       /* nv05 does this, nv11 doesn't, nv10 unknown */
+                       if (bios->version.chip < 0x11)
+                               info->vco1.min_m = 0x7;
+                       info->vco1.max_m = 0xd;
+               } else {
+                       if (bios->version.chip < 0x11)
+                               info->vco1.min_m = 0x8;
+                       info->vco1.max_m = 0xe;
+               }
+
+               if (bios->version.chip <  0x17 ||
+                   bios->version.chip == 0x1a ||
+                   bios->version.chip == 0x20)
+                       info->max_p = 4;
+               else
+                       info->max_p = 5;
+               info->max_p_usable = info->max_p;
+       }
+
+       return 0;
+}
index 3a3b3b149690e5f9acc503a4fcbde2b050c2e90b..b7fd1151166e0adcedec217fed8e186ce5ab2cd8 100644 (file)
  */
 
 #include <subdev/clock.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+#include "pll.h"
 
 struct nv04_clock_priv {
        struct nouveau_clock base;
 };
 
+static int
+powerctrl_1_shift(int chip_version, int reg)
+{
+       int shift = -4;
+
+       if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
+               return shift;
+
+       switch (reg) {
+       case 0x680520:
+               shift += 4;
+       case 0x680508:
+               shift += 4;
+       case 0x680504:
+               shift += 4;
+       case 0x680500:
+               shift += 4;
+       }
+
+       /*
+        * the shift for vpll regs is only used for nv3x chips with a single
+        * stage pll
+        */
+       if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
+                         chip_version == 0x36 || chip_version >= 0x40))
+               shift = -4;
+
+       return shift;
+}
+
 static void
+setPLL_single(struct nv04_clock_priv *priv, u32 reg,
+             struct nouveau_pll_vals *pv)
+{
+       int chip_version = nouveau_bios(priv)->version.chip;
+       uint32_t oldpll = nv_rd32(priv, reg);
+       int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
+       uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
+       uint32_t saved_powerctrl_1 = 0;
+       int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
+
+       if (oldpll == pll)
+               return; /* already set */
+
+       if (shift_powerctrl_1 >= 0) {
+               saved_powerctrl_1 = nv_rd32(priv, 0x001584);
+               nv_wr32(priv, 0x001584,
+                       (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
+                       1 << shift_powerctrl_1);
+       }
+
+       if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
+               /* upclock -- write new post divider first */
+               nv_wr32(priv, reg, pv->log2P << 16 | (oldpll & 0xffff));
+       else
+               /* downclock -- write new NM first */
+               nv_wr32(priv, reg, (oldpll & 0xffff0000) | pv->NM1);
+
+       if (chip_version < 0x17 && chip_version != 0x11)
+               /* wait a bit on older chips */
+               msleep(64);
+       nv_rd32(priv, reg);
+
+       /* then write the other half as well */
+       nv_wr32(priv, reg, pll);
+
+       if (shift_powerctrl_1 >= 0)
+               nv_wr32(priv, 0x001584, saved_powerctrl_1);
+}
+
+static uint32_t
+new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
+{
+       bool head_a = (reg1 == 0x680508);
+
+       if (ss) /* single stage pll mode */
+               ramdac580 |= head_a ? 0x00000100 : 0x10000000;
+       else
+               ramdac580 &= head_a ? 0xfffffeff : 0xefffffff;
+
+       return ramdac580;
+}
+
+static void
+setPLL_double_highregs(struct nv04_clock_priv *priv, u32 reg1,
+                      struct nouveau_pll_vals *pv)
+{
+       int chip_version = nouveau_bios(priv)->version.chip;
+       bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
+       uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70);
+       uint32_t oldpll1 = nv_rd32(priv, reg1);
+       uint32_t oldpll2 = !nv3035 ? nv_rd32(priv, reg2) : 0;
+       uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
+       uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
+       uint32_t oldramdac580 = 0, ramdac580 = 0;
+       bool single_stage = !pv->NM2 || pv->N2 == pv->M2;       /* nv41+ only */
+       uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
+       int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
+
+       /* model specific additions to generic pll1 and pll2 set up above */
+       if (nv3035) {
+               pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
+                      (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
+               pll2 = 0;
+       }
+       if (chip_version > 0x40 && reg1 >= 0x680508) { /* !nv40 */
+               oldramdac580 = nv_rd32(priv, 0x680580);
+               ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
+               if (oldramdac580 != ramdac580)
+                       oldpll1 = ~0;   /* force mismatch */
+               if (single_stage)
+                       /* magic value used by nvidia in single stage mode */
+                       pll2 |= 0x011f;
+       }
+       if (chip_version > 0x70)
+               /* magic bits set by the blob (but not the bios) on g71-73 */
+               pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
+
+       if (oldpll1 == pll1 && oldpll2 == pll2)
+               return; /* already set */
+
+       if (shift_powerctrl_1 >= 0) {
+               saved_powerctrl_1 = nv_rd32(priv, 0x001584);
+               nv_wr32(priv, 0x001584,
+                       (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
+                       1 << shift_powerctrl_1);
+       }
+
+       if (chip_version >= 0x40) {
+               int shift_c040 = 14;
+
+               switch (reg1) {
+               case 0x680504:
+                       shift_c040 += 2;
+               case 0x680500:
+                       shift_c040 += 2;
+               case 0x680520:
+                       shift_c040 += 2;
+               case 0x680508:
+                       shift_c040 += 2;
+               }
+
+               savedc040 = nv_rd32(priv, 0xc040);
+               if (shift_c040 != 14)
+                       nv_wr32(priv, 0xc040, savedc040 & ~(3 << shift_c040));
+       }
+
+       if (oldramdac580 != ramdac580)
+               nv_wr32(priv, 0x680580, ramdac580);
+
+       if (!nv3035)
+               nv_wr32(priv, reg2, pll2);
+       nv_wr32(priv, reg1, pll1);
+
+       if (shift_powerctrl_1 >= 0)
+               nv_wr32(priv, 0x001584, saved_powerctrl_1);
+       if (chip_version >= 0x40)
+               nv_wr32(priv, 0xc040, savedc040);
+}
+
+static void
+setPLL_double_lowregs(struct nv04_clock_priv *priv, u32 NMNMreg,
+                     struct nouveau_pll_vals *pv)
+{
+       /* When setting PLLs, there is a merry game of disabling and enabling
+        * various bits of hardware during the process. This function is a
+        * synthesis of six nv4x traces, nearly each card doing a subtly
+        * different thing. With luck all the necessary bits for each card are
+        * combined herein. Without luck it deviates from each card's formula
+        * so as to not work on any :)
+        */
+
+       uint32_t Preg = NMNMreg - 4;
+       bool mpll = Preg == 0x4020;
+       uint32_t oldPval = nv_rd32(priv, Preg);
+       uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
+       uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) |
+                       0xc << 28 | pv->log2P << 16;
+       uint32_t saved4600 = 0;
+       /* some cards have different maskc040s */
+       uint32_t maskc040 = ~(3 << 14), savedc040;
+       bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
+
+       if (nv_rd32(priv, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
+               return;
+
+       if (Preg == 0x4000)
+               maskc040 = ~0x333;
+       if (Preg == 0x4058)
+               maskc040 = ~(0xc << 24);
+
+       if (mpll) {
+               struct nvbios_pll info;
+               uint8_t Pval2;
+
+               if (nvbios_pll_parse(nouveau_bios(priv), Preg, &info))
+                       return;
+
+               Pval2 = pv->log2P + info.bias_p;
+               if (Pval2 > info.max_p)
+                       Pval2 = info.max_p;
+               Pval |= 1 << 28 | Pval2 << 20;
+
+               saved4600 = nv_rd32(priv, 0x4600);
+               nv_wr32(priv, 0x4600, saved4600 | 8 << 28);
+       }
+       if (single_stage)
+               Pval |= mpll ? 1 << 12 : 1 << 8;
+
+       nv_wr32(priv, Preg, oldPval | 1 << 28);
+       nv_wr32(priv, Preg, Pval & ~(4 << 28));
+       if (mpll) {
+               Pval |= 8 << 20;
+               nv_wr32(priv, 0x4020, Pval & ~(0xc << 28));
+               nv_wr32(priv, 0x4038, Pval & ~(0xc << 28));
+       }
+
+       savedc040 = nv_rd32(priv, 0xc040);
+       nv_wr32(priv, 0xc040, savedc040 & maskc040);
+
+       nv_wr32(priv, NMNMreg, NMNM);
+       if (NMNMreg == 0x4024)
+               nv_wr32(priv, 0x403c, NMNM);
+
+       nv_wr32(priv, Preg, Pval);
+       if (mpll) {
+               Pval &= ~(8 << 20);
+               nv_wr32(priv, 0x4020, Pval);
+               nv_wr32(priv, 0x4038, Pval);
+               nv_wr32(priv, 0x4600, saved4600);
+       }
+
+       nv_wr32(priv, 0xc040, savedc040);
+
+       if (mpll) {
+               nv_wr32(priv, 0x4020, Pval & ~(1 << 28));
+               nv_wr32(priv, 0x4038, Pval & ~(1 << 28));
+       }
+}
+
+int
 nv04_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
 {
        struct nv04_clock_priv *priv = (void *)clk;
+       struct nouveau_pll_vals pv;
+       struct nvbios_pll info;
+       int ret;
+
+       ret = nvbios_pll_parse(nouveau_bios(priv), type > 0x405c ?
+                              type : type - 4, &info);
+       if (ret)
+               return ret;
+
+       ret = clk->pll_calc(clk, &info, freq, &pv);
+       if (!ret)
+               return ret;
+
+       return clk->pll_prog(clk, type, &pv);
+}
 
-       nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+int
+nv04_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
+                   int clk, struct nouveau_pll_vals *pv)
+{
+       int N1, M1, N2, M2, P;
+       int ret = nv04_pll_calc(clock, info, clk, &N1, &M1, &N2, &M2, &P);
+       if (ret) {
+               pv->refclk = info->refclk;
+               pv->N1 = N1;
+               pv->M1 = M1;
+               pv->N2 = N2;
+               pv->M2 = M2;
+               pv->log2P = P;
+       }
+       return ret;
+}
+
+int
+nv04_clock_pll_prog(struct nouveau_clock *clk, u32 reg1,
+                   struct nouveau_pll_vals *pv)
+{
+       struct nv04_clock_priv *priv = (void *)clk;
+       int cv = nouveau_bios(clk)->version.chip;
+
+       if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
+           cv >= 0x40) {
+               if (reg1 > 0x405c)
+                       setPLL_double_highregs(priv, reg1, pv);
+               else
+                       setPLL_double_lowregs(priv, reg1, pv);
+       } else
+               setPLL_single(priv, reg1, pv);
+
+       return 0;
 }
 
 static int
@@ -50,6 +342,8 @@ nv04_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        priv->base.pll_set = nv04_clock_pll_set;
+       priv->base.pll_calc = nv04_clock_pll_calc;
+       priv->base.pll_prog = nv04_clock_pll_prog;
        return 0;
 }
 
index 60d1ca44e39cf64990a8db6db232421ac355a6e4..a4b2b7ebf9af6f48c3b44c50e11a5a07cbd99341 100644 (file)
@@ -28,14 +28,6 @@ struct nv40_clock_priv {
        struct nouveau_clock base;
 };
 
-static void
-nv40_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
-{
-       struct nv40_clock_priv *priv = (void *)clk;
-
-       nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
-}
-
 static int
 nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_oclass *oclass, void *data, u32 size,
@@ -49,7 +41,9 @@ nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       priv->base.pll_set = nv40_clock_pll_set;
+       priv->base.pll_set = nv04_clock_pll_set;
+       priv->base.pll_calc = nv04_clock_pll_calc;
+       priv->base.pll_prog = nv04_clock_pll_prog;
        return 0;
 }
 
index 82804bdcec31fca0f1340259c0f2cb884e99642e..fd181fbceddb9313cc58e1c7b04b77659c41eff8 100644 (file)
  */
 
 #include <subdev/clock.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+#include "pll.h"
 
 struct nv50_clock_priv {
        struct nouveau_clock base;
 };
 
-static void
+static int
 nv50_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
 {
        struct nv50_clock_priv *priv = (void *)clk;
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll info;
+       int N1, M1, N2, M2, P;
+       int ret;
+
+       ret = nvbios_pll_parse(bios, type, &info);
+       if (ret) {
+               nv_error(clk, "failed to retrieve pll data, %d\n", ret);
+               return ret;
+       }
 
-       nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+       ret = nv04_pll_calc(clk, &info, freq, &N1, &M1, &N2, &M2, &P);
+       if (!ret) {
+               nv_error(clk, "failed pll calculation\n");
+               return ret;
+       }
+
+       switch (info.type) {
+       case PLL_VPLL0:
+       case PLL_VPLL1:
+               nv_wr32(priv, info.reg + 0, 0x10000611);
+               nv_mask(priv, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1);
+               nv_mask(priv, info.reg + 8, 0x7fff00ff, (P  << 28) |
+                                                       (M2 << 16) | N2);
+               break;
+       case PLL_MEMORY:
+               nv_mask(priv, info.reg + 0, 0x01ff0000, (P << 22) |
+                                                       (info.bias_p << 19) |
+                                                       (P << 16));
+               nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
+               break;
+       default:
+               nv_mask(priv, info.reg + 0, 0x00070000, (P << 16));
+               nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
+               break;
+       }
+
+       return 0;
 }
 
 static int
index 876ec46e2b411e55992392611e6e7665723b4510..cc8d7d162d7c348da6770d7404f195e9191d41e1 100644 (file)
  */
 
 #include <subdev/clock.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+#include "pll.h"
 
 struct nva3_clock_priv {
        struct nouveau_clock base;
 };
 
-static void
+static int
 nva3_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
 {
        struct nva3_clock_priv *priv = (void *)clk;
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll info;
+       int N, fN, M, P;
+       int ret;
+
+       ret = nvbios_pll_parse(bios, type, &info);
+       if (ret)
+               return ret;
+
+       ret = nva3_pll_calc(clk, &info, freq, &N, &fN, &M, &P);
+       if (ret < 0)
+               return ret;
+
+       switch (info.type) {
+       case PLL_VPLL0:
+       case PLL_VPLL1:
+               nv_wr32(priv, info.reg + 0, 0x50000610);
+               nv_mask(priv, info.reg + 4, 0x003fffff,
+                                           (P << 16) | (M << 8) | N);
+               nv_wr32(priv, info.reg + 8, fN);
+               break;
+       default:
+               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+               ret = -EINVAL;
+               break;
+       }
 
-       nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+       return ret;
 }
 
 static int
index 00641566f2e7ed2979e4c9ad16937b9253f52165..5ccce0b17bf3dd209bde225486b41bab55fcdf79 100644 (file)
  */
 
 #include <subdev/clock.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+#include "pll.h"
 
 struct nvc0_clock_priv {
        struct nouveau_clock base;
 };
 
-static void
+static int
 nvc0_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
 {
        struct nvc0_clock_priv *priv = (void *)clk;
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll info;
+       int N, fN, M, P;
+       int ret;
+
+       ret = nvbios_pll_parse(bios, type, &info);
+       if (ret)
+               return ret;
+
+       ret = nva3_pll_calc(clk, &info, freq, &N, &fN, &M, &P);
+       if (ret < 0)
+               return ret;
+
+       switch (info.type) {
+       case PLL_VPLL0:
+       case PLL_VPLL1:
+               nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100);
+               nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M);
+               nv_wr32(priv, info.reg + 0x10, fN << 16);
+               break;
+       default:
+               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+               ret = -EINVAL;
+               break;
+       }
 
-       nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+       return ret;
 }
 
 static int
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h b/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h
new file mode 100644 (file)
index 0000000..ef2c007
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __NOUVEAU_PLL_H__
+#define __NOUVEAU_PLL_H__
+
+int nv04_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq,
+                 int *N1, int *M1, int *N2, int *M2, int *P);
+int nva3_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq,
+                 int *N, int *fN, int *M, int *P);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c
new file mode 100644 (file)
index 0000000..a2ab6d0
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright 1993-2003 NVIDIA, Corporation
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * 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 AUTHORS 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 <subdev/clock.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+#include "pll.h"
+
+static int
+getMNP_single(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
+             int *pN, int *pM, int *pP)
+{
+       /* Find M, N and P for a single stage PLL
+        *
+        * Note that some bioses (NV3x) have lookup tables of precomputed MNP
+        * values, but we're too lazy to use those atm
+        *
+        * "clk" parameter in kHz
+        * returns calculated clock
+        */
+       int cv = nouveau_bios(clock)->version.chip;
+       int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq;
+       int minM = info->vco1.min_m, maxM = info->vco1.max_m;
+       int minN = info->vco1.min_n, maxN = info->vco1.max_n;
+       int minU = info->vco1.min_inputfreq;
+       int maxU = info->vco1.max_inputfreq;
+       int minP = info->min_p;
+       int maxP = info->max_p_usable;
+       int crystal = info->refclk;
+       int M, N, thisP, P;
+       int clkP, calcclk;
+       int delta, bestdelta = INT_MAX;
+       int bestclk = 0;
+
+       /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
+       /* possibly correlated with introduction of 27MHz crystal */
+       if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
+               if (clk > 250000)
+                       maxM = 6;
+               if (clk > 340000)
+                       maxM = 2;
+       } else if (cv < 0x40) {
+               if (clk > 150000)
+                       maxM = 6;
+               if (clk > 200000)
+                       maxM = 4;
+               if (clk > 340000)
+                       maxM = 2;
+       }
+
+       P = 1 << maxP;
+       if ((clk * P) < minvco) {
+               minvco = clk * maxP;
+               maxvco = minvco * 2;
+       }
+
+       if (clk + clk/200 > maxvco)     /* +0.5% */
+               maxvco = clk + clk/200;
+
+       /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
+       for (thisP = minP; thisP <= maxP; thisP++) {
+               P = 1 << thisP;
+               clkP = clk * P;
+
+               if (clkP < minvco)
+                       continue;
+               if (clkP > maxvco)
+                       return bestclk;
+
+               for (M = minM; M <= maxM; M++) {
+                       if (crystal/M < minU)
+                               return bestclk;
+                       if (crystal/M > maxU)
+                               continue;
+
+                       /* add crystal/2 to round better */
+                       N = (clkP * M + crystal/2) / crystal;
+
+                       if (N < minN)
+                               continue;
+                       if (N > maxN)
+                               break;
+
+                       /* more rounding additions */
+                       calcclk = ((N * crystal + P/2) / P + M/2) / M;
+                       delta = abs(calcclk - clk);
+                       /* we do an exhaustive search rather than terminating
+                        * on an optimality condition...
+                        */
+                       if (delta < bestdelta) {
+                               bestdelta = delta;
+                               bestclk = calcclk;
+                               *pN = N;
+                               *pM = M;
+                               *pP = thisP;
+                               if (delta == 0) /* except this one */
+                                       return bestclk;
+                       }
+               }
+       }
+
+       return bestclk;
+}
+
+static int
+getMNP_double(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
+             int *pN1, int *pM1, int *pN2, int *pM2, int *pP)
+{
+       /* Find M, N and P for a two stage PLL
+        *
+        * Note that some bioses (NV30+) have lookup tables of precomputed MNP
+        * values, but we're too lazy to use those atm
+        *
+        * "clk" parameter in kHz
+        * returns calculated clock
+        */
+       int chip_version = nouveau_bios(clock)->version.chip;
+       int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq;
+       int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq;
+       int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq;
+       int maxU1 = info->vco1.max_inputfreq, maxU2 = info->vco2.max_inputfreq;
+       int minM1 = info->vco1.min_m, maxM1 = info->vco1.max_m;
+       int minN1 = info->vco1.min_n, maxN1 = info->vco1.max_n;
+       int minM2 = info->vco2.min_m, maxM2 = info->vco2.max_m;
+       int minN2 = info->vco2.min_n, maxN2 = info->vco2.max_n;
+       int maxlog2P = info->max_p_usable;
+       int crystal = info->refclk;
+       bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
+       int M1, N1, M2, N2, log2P;
+       int clkP, calcclk1, calcclk2, calcclkout;
+       int delta, bestdelta = INT_MAX;
+       int bestclk = 0;
+
+       int vco2 = (maxvco2 - maxvco2/200) / 2;
+       for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
+               ;
+       clkP = clk << log2P;
+
+       if (maxvco2 < clk + clk/200)    /* +0.5% */
+               maxvco2 = clk + clk/200;
+
+       for (M1 = minM1; M1 <= maxM1; M1++) {
+               if (crystal/M1 < minU1)
+                       return bestclk;
+               if (crystal/M1 > maxU1)
+                       continue;
+
+               for (N1 = minN1; N1 <= maxN1; N1++) {
+                       calcclk1 = crystal * N1 / M1;
+                       if (calcclk1 < minvco1)
+                               continue;
+                       if (calcclk1 > maxvco1)
+                               break;
+
+                       for (M2 = minM2; M2 <= maxM2; M2++) {
+                               if (calcclk1/M2 < minU2)
+                                       break;
+                               if (calcclk1/M2 > maxU2)
+                                       continue;
+
+                               /* add calcclk1/2 to round better */
+                               N2 = (clkP * M2 + calcclk1/2) / calcclk1;
+                               if (N2 < minN2)
+                                       continue;
+                               if (N2 > maxN2)
+                                       break;
+
+                               if (!fixedgain2) {
+                                       if (chip_version < 0x60)
+                                               if (N2/M2 < 4 || N2/M2 > 10)
+                                                       continue;
+
+                                       calcclk2 = calcclk1 * N2 / M2;
+                                       if (calcclk2 < minvco2)
+                                               break;
+                                       if (calcclk2 > maxvco2)
+                                               continue;
+                               } else
+                                       calcclk2 = calcclk1;
+
+                               calcclkout = calcclk2 >> log2P;
+                               delta = abs(calcclkout - clk);
+                               /* we do an exhaustive search rather than terminating
+                                * on an optimality condition...
+                                */
+                               if (delta < bestdelta) {
+                                       bestdelta = delta;
+                                       bestclk = calcclkout;
+                                       *pN1 = N1;
+                                       *pM1 = M1;
+                                       *pN2 = N2;
+                                       *pM2 = M2;
+                                       *pP = log2P;
+                                       if (delta == 0) /* except this one */
+                                               return bestclk;
+                               }
+                       }
+               }
+       }
+
+       return bestclk;
+}
+
+int
+nv04_pll_calc(struct nouveau_clock *clk, struct nvbios_pll *info, u32 freq,
+             int *N1, int *M1, int *N2, int *M2, int *P)
+{
+       int ret;
+
+       if (!info->vco2.max_freq) {
+               ret = getMNP_single(clk, info, freq, N1, M1, P);
+               *N2 = 1;
+               *M2 = 1;
+       } else {
+               ret = getMNP_double(clk, info, freq, N1, M1, N2, M2, P);
+       }
+
+       if (!ret)
+               nv_error(clk, "unable to compute acceptable pll values\n");
+       return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c
new file mode 100644 (file)
index 0000000..eed5c16
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2010 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/clock.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+#include "pll.h"
+
+int
+nva3_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
+             u32 freq, int *pN, int *pfN, int *pM, int *P)
+{
+       u32 best_err = ~0, err;
+       int M, lM, hM, N, fN;
+
+       *P = info->vco1.max_freq / freq;
+       if (*P > info->max_p)
+               *P = info->max_p;
+       if (*P < info->min_p)
+               *P = info->min_p;
+
+       lM = (info->refclk + info->vco1.max_inputfreq) / info->vco1.max_inputfreq;
+       lM = max(lM, (int)info->vco1.min_m);
+       hM = (info->refclk + info->vco1.min_inputfreq) / info->vco1.min_inputfreq;
+       hM = min(hM, (int)info->vco1.max_m);
+
+       for (M = lM; M <= hM; M++) {
+               u32 tmp = freq * *P * M;
+               N  = tmp / info->refclk;
+               fN = tmp % info->refclk;
+               if (!pfN && fN >= info->refclk / 2)
+                       N++;
+
+               if (N < info->vco1.min_n)
+                       continue;
+               if (N > info->vco1.max_n)
+                       break;
+
+               err = abs(freq - (info->refclk * N / M / *P));
+               if (err < best_err) {
+                       best_err = err;
+                       *pN = N;
+                       *pM = M;
+               }
+
+               if (pfN) {
+                       *pfN = (((fN << 13) / info->refclk) - 4096) & 0xffff;
+                       return freq;
+               }
+       }
+
+       if (unlikely(best_err == ~0)) {
+               nv_error(clock, "unable to find matching pll values\n");
+               return -EINVAL;
+       }
+
+       return info->refclk * *pN / *pM / *P;
+}
index 35b0a8f9c008f36e2c0082af27a546d239559623..98eaac9da30e356c3ae19bd2c5a6270699e1df7d 100644 (file)
@@ -441,70 +441,6 @@ io_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
        return (data == cmpval);
 }
 
-static int
-nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk)
-{
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nouveau_pll_vals pll;
-       struct pll_lims pll_limits;
-       u32 ctrl, mask, coef;
-       int ret;
-
-       ret = get_pll_limits(dev, reg, &pll_limits);
-       if (ret)
-               return ret;
-
-       clk = nouveau_calc_pll_mnp(dev, &pll_limits, clk, &pll);
-       if (!clk)
-               return -ERANGE;
-
-       coef = pll.N1 << 8 | pll.M1;
-       ctrl = pll.log2P << 16;
-       mask = 0x00070000;
-       if (reg == 0x004008) {
-               mask |= 0x01f80000;
-               ctrl |= (pll_limits.log2p_bias << 19);
-               ctrl |= (pll.log2P << 22);
-       }
-
-       if (!dev_priv->vbios.execute)
-               return 0;
-
-       nv_mask(dev, reg + 0, mask, ctrl);
-       nv_wr32(dev, reg + 4, coef);
-       return 0;
-}
-
-static int
-setPLL(struct nvbios *bios, uint32_t reg, uint32_t clk)
-{
-       struct drm_device *dev = bios->dev;
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       /* clk in kHz */
-       struct pll_lims pll_lim;
-       struct nouveau_pll_vals pllvals;
-       int ret;
-
-       if (dev_priv->card_type >= NV_50)
-               return nv50_pll_set(dev, reg, clk);
-
-       /* high regs (such as in the mac g5 table) are not -= 4 */
-       ret = get_pll_limits(dev, reg > 0x405c ? reg : reg - 4, &pll_lim);
-       if (ret)
-               return ret;
-
-       clk = nouveau_calc_pll_mnp(dev, &pll_lim, clk, &pllvals);
-       if (!clk)
-               return -ERANGE;
-
-       if (bios->execute) {
-               still_alive();
-               nouveau_hw_setpll(dev, reg, &pllvals);
-       }
-
-       return 0;
-}
-
 static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -752,7 +688,7 @@ init_io_restrict_pll(struct nvbios *bios, uint16_t offset,
        BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %d0kHz\n",
                offset, reg, config, freq);
 
-       setPLL(bios, reg, freq * 10);
+       setPLL(bios->dev, reg, freq * 10);
 
        return len;
 }
@@ -1110,7 +1046,7 @@ init_io_restrict_pll2(struct nvbios *bios, uint16_t offset,
        BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %dkHz\n",
                offset, reg, config, freq);
 
-       setPLL(bios, reg, freq);
+       setPLL(bios->dev, reg, freq);
 
        return len;
 }
@@ -1137,7 +1073,7 @@ init_pll2(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
        BIOSLOG(bios, "0x%04X: Reg: 0x%04X, Freq: %dkHz\n",
                offset, reg, freq);
 
-       setPLL(bios, reg, freq);
+       setPLL(bios->dev, reg, freq);
        return 9;
 }
 
@@ -2376,12 +2312,12 @@ init_configure_clk(struct nvbios *bios, uint16_t offset,
                return 0;
 
        clock = ROM16(bios->data[meminitoffs + 4]) * 10;
-       setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock);
+       setPLL(bios->dev, NV_PRAMDAC_NVPLL_COEFF, clock);
 
        clock = ROM16(bios->data[meminitoffs + 2]) * 10;
        if (bios->data[meminitoffs] & 1) /* DDR */
                clock *= 2;
-       setPLL(bios, NV_PRAMDAC_MPLL_COEFF, clock);
+       setPLL(bios->dev, NV_PRAMDAC_MPLL_COEFF, clock);
 
        return 1;
 }
@@ -2820,7 +2756,7 @@ init_pll(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
 
        BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Freq: %d0kHz\n", offset, reg, freq);
 
-       setPLL(bios, reg, freq * 10);
+       setPLL(bios->dev, reg, freq * 10);
 
        return 7;
 }
@@ -2898,7 +2834,7 @@ init_ram_restrict_pll(struct nvbios *bios, uint16_t offset,
                                      "Type %02x Reg 0x%08x Freq %dKHz\n",
                                offset, type, reg, freq);
 
-                       setPLL(bios, reg, freq);
+                       setPLL(bios->dev, reg, freq);
                        return len;
                }
        }
@@ -4305,447 +4241,6 @@ int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, i
        return 0;
 }
 
-struct pll_mapping {
-       u8  type;
-       u32 reg;
-};
-
-static struct pll_mapping nv04_pll_mapping[] = {
-       { PLL_CORE  , NV_PRAMDAC_NVPLL_COEFF },
-       { PLL_MEMORY, NV_PRAMDAC_MPLL_COEFF },
-       { PLL_VPLL0 , NV_PRAMDAC_VPLL_COEFF },
-       { PLL_VPLL1 , NV_RAMDAC_VPLL2 },
-       {}
-};
-
-static struct pll_mapping nv40_pll_mapping[] = {
-       { PLL_CORE  , 0x004000 },
-       { PLL_MEMORY, 0x004020 },
-       { PLL_VPLL0 , NV_PRAMDAC_VPLL_COEFF },
-       { PLL_VPLL1 , NV_RAMDAC_VPLL2 },
-       {}
-};
-
-static struct pll_mapping nv50_pll_mapping[] = {
-       { PLL_CORE  , 0x004028 },
-       { PLL_SHADER, 0x004020 },
-       { PLL_UNK03 , 0x004000 },
-       { PLL_MEMORY, 0x004008 },
-       { PLL_UNK40 , 0x00e810 },
-       { PLL_UNK41 , 0x00e818 },
-       { PLL_UNK42 , 0x00e824 },
-       { PLL_VPLL0 , 0x614100 },
-       { PLL_VPLL1 , 0x614900 },
-       {}
-};
-
-static struct pll_mapping nv84_pll_mapping[] = {
-       { PLL_CORE  , 0x004028 },
-       { PLL_SHADER, 0x004020 },
-       { PLL_MEMORY, 0x004008 },
-       { PLL_VDEC  , 0x004030 },
-       { PLL_UNK41 , 0x00e818 },
-       { PLL_VPLL0 , 0x614100 },
-       { PLL_VPLL1 , 0x614900 },
-       {}
-};
-
-u32
-get_pll_register(struct drm_device *dev, enum pll_types type)
-{
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->vbios;
-       struct pll_mapping *map;
-       int i;
-
-       if (dev_priv->card_type < NV_40)
-               map = nv04_pll_mapping;
-       else
-       if (dev_priv->card_type < NV_50)
-               map = nv40_pll_mapping;
-       else {
-               u8 *plim = &bios->data[bios->pll_limit_tbl_ptr];
-
-               if (plim[0] >= 0x30) {
-                       u8 *entry = plim + plim[1];
-                       for (i = 0; i < plim[3]; i++, entry += plim[2]) {
-                               if (entry[0] == type)
-                                       return ROM32(entry[3]);
-                       }
-
-                       return 0;
-               }
-
-               if (dev_priv->chipset == 0x50)
-                       map = nv50_pll_mapping;
-               else
-                       map = nv84_pll_mapping;
-       }
-
-       while (map->reg) {
-               if (map->type == type)
-                       return map->reg;
-               map++;
-       }
-
-       return 0;
-}
-
-int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim)
-{
-       /*
-        * PLL limits table
-        *
-        * Version 0x10: NV30, NV31
-        * One byte header (version), one record of 24 bytes
-        * Version 0x11: NV36 - Not implemented
-        * Seems to have same record style as 0x10, but 3 records rather than 1
-        * Version 0x20: Found on Geforce 6 cards
-        * Trivial 4 byte BIT header. 31 (0x1f) byte record length
-        * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards
-        * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record
-        * length in general, some (integrated) have an extra configuration byte
-        * Version 0x30: Found on Geforce 8, separates the register mapping
-        * from the limits tables.
-        */
-
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->vbios;
-       int cv = bios->chip_version, pllindex = 0;
-       uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0;
-       uint32_t crystal_strap_mask, crystal_straps;
-
-       if (!bios->pll_limit_tbl_ptr) {
-               if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
-                   cv >= 0x40) {
-                       NV_ERROR(dev, "Pointer to PLL limits table invalid\n");
-                       return -EINVAL;
-               }
-       } else
-               pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr];
-
-       crystal_strap_mask = 1 << 6;
-       /* open coded dev->twoHeads test */
-       if (cv > 0x10 && cv != 0x15 && cv != 0x1a && cv != 0x20)
-               crystal_strap_mask |= 1 << 22;
-       crystal_straps = nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) &
-                                                       crystal_strap_mask;
-
-       switch (pll_lim_ver) {
-       /*
-        * We use version 0 to indicate a pre limit table bios (single stage
-        * pll) and load the hard coded limits instead.
-        */
-       case 0:
-               break;
-       case 0x10:
-       case 0x11:
-               /*
-                * Strictly v0x11 has 3 entries, but the last two don't seem
-                * to get used.
-                */
-               headerlen = 1;
-               recordlen = 0x18;
-               entries = 1;
-               pllindex = 0;
-               break;
-       case 0x20:
-       case 0x21:
-       case 0x30:
-       case 0x40:
-               headerlen = bios->data[bios->pll_limit_tbl_ptr + 1];
-               recordlen = bios->data[bios->pll_limit_tbl_ptr + 2];
-               entries = bios->data[bios->pll_limit_tbl_ptr + 3];
-               break;
-       default:
-               NV_ERROR(dev, "PLL limits table revision 0x%X not currently "
-                               "supported\n", pll_lim_ver);
-               return -ENOSYS;
-       }
-
-       /* initialize all members to zero */
-       memset(pll_lim, 0, sizeof(struct pll_lims));
-
-       /* if we were passed a type rather than a register, figure
-        * out the register and store it
-        */
-       if (limit_match > PLL_MAX)
-               pll_lim->reg = limit_match;
-       else {
-               pll_lim->reg = get_pll_register(dev, limit_match);
-               if (!pll_lim->reg)
-                       return -ENOENT;
-       }
-
-       if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) {
-               uint8_t *pll_rec = &bios->data[bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex];
-
-               pll_lim->vco1.minfreq = ROM32(pll_rec[0]);
-               pll_lim->vco1.maxfreq = ROM32(pll_rec[4]);
-               pll_lim->vco2.minfreq = ROM32(pll_rec[8]);
-               pll_lim->vco2.maxfreq = ROM32(pll_rec[12]);
-               pll_lim->vco1.min_inputfreq = ROM32(pll_rec[16]);
-               pll_lim->vco2.min_inputfreq = ROM32(pll_rec[20]);
-               pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX;
-
-               /* these values taken from nv30/31/36 */
-               pll_lim->vco1.min_n = 0x1;
-               if (cv == 0x36)
-                       pll_lim->vco1.min_n = 0x5;
-               pll_lim->vco1.max_n = 0xff;
-               pll_lim->vco1.min_m = 0x1;
-               pll_lim->vco1.max_m = 0xd;
-               pll_lim->vco2.min_n = 0x4;
-               /*
-                * On nv30, 31, 36 (i.e. all cards with two stage PLLs with this
-                * table version (apart from nv35)), N2 is compared to
-                * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and
-                * save a comparison
-                */
-               pll_lim->vco2.max_n = 0x28;
-               if (cv == 0x30 || cv == 0x35)
-                       /* only 5 bits available for N2 on nv30/35 */
-                       pll_lim->vco2.max_n = 0x1f;
-               pll_lim->vco2.min_m = 0x1;
-               pll_lim->vco2.max_m = 0x4;
-               pll_lim->max_log2p = 0x7;
-               pll_lim->max_usable_log2p = 0x6;
-       } else if (pll_lim_ver == 0x20 || pll_lim_ver == 0x21) {
-               uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen;
-               uint8_t *pll_rec;
-               int i;
-
-               /*
-                * First entry is default match, if nothing better. warn if
-                * reg field nonzero
-                */
-               if (ROM32(bios->data[plloffs]))
-                       NV_WARN(dev, "Default PLL limit entry has non-zero "
-                                      "register field\n");
-
-               for (i = 1; i < entries; i++)
-                       if (ROM32(bios->data[plloffs + recordlen * i]) == pll_lim->reg) {
-                               pllindex = i;
-                               break;
-                       }
-
-               if ((dev_priv->card_type >= NV_50) && (pllindex == 0)) {
-                       NV_ERROR(dev, "Register 0x%08x not found in PLL "
-                                "limits table", pll_lim->reg);
-                       return -ENOENT;
-               }
-
-               pll_rec = &bios->data[plloffs + recordlen * pllindex];
-
-               BIOSLOG(bios, "Loading PLL limits for reg 0x%08x\n",
-                       pllindex ? pll_lim->reg : 0);
-
-               /*
-                * Frequencies are stored in tables in MHz, kHz are more
-                * useful, so we convert.
-                */
-
-               /* What output frequencies can each VCO generate? */
-               pll_lim->vco1.minfreq = ROM16(pll_rec[4]) * 1000;
-               pll_lim->vco1.maxfreq = ROM16(pll_rec[6]) * 1000;
-               pll_lim->vco2.minfreq = ROM16(pll_rec[8]) * 1000;
-               pll_lim->vco2.maxfreq = ROM16(pll_rec[10]) * 1000;
-
-               /* What input frequencies they accept (past the m-divider)? */
-               pll_lim->vco1.min_inputfreq = ROM16(pll_rec[12]) * 1000;
-               pll_lim->vco2.min_inputfreq = ROM16(pll_rec[14]) * 1000;
-               pll_lim->vco1.max_inputfreq = ROM16(pll_rec[16]) * 1000;
-               pll_lim->vco2.max_inputfreq = ROM16(pll_rec[18]) * 1000;
-
-               /* What values are accepted as multiplier and divider? */
-               pll_lim->vco1.min_n = pll_rec[20];
-               pll_lim->vco1.max_n = pll_rec[21];
-               pll_lim->vco1.min_m = pll_rec[22];
-               pll_lim->vco1.max_m = pll_rec[23];
-               pll_lim->vco2.min_n = pll_rec[24];
-               pll_lim->vco2.max_n = pll_rec[25];
-               pll_lim->vco2.min_m = pll_rec[26];
-               pll_lim->vco2.max_m = pll_rec[27];
-
-               pll_lim->max_usable_log2p = pll_lim->max_log2p = pll_rec[29];
-               if (pll_lim->max_log2p > 0x7)
-                       /* pll decoding in nv_hw.c assumes never > 7 */
-                       NV_WARN(dev, "Max log2 P value greater than 7 (%d)\n",
-                               pll_lim->max_log2p);
-               if (cv < 0x60)
-                       pll_lim->max_usable_log2p = 0x6;
-               pll_lim->log2p_bias = pll_rec[30];
-
-               if (recordlen > 0x22)
-                       pll_lim->refclk = ROM32(pll_rec[31]);
-
-               if (recordlen > 0x23 && pll_rec[35])
-                       NV_WARN(dev,
-                               "Bits set in PLL configuration byte (%x)\n",
-                               pll_rec[35]);
-
-               /* C51 special not seen elsewhere */
-               if (cv == 0x51 && !pll_lim->refclk) {
-                       uint32_t sel_clk = bios_rd32(bios, NV_PRAMDAC_SEL_CLK);
-
-                       if ((pll_lim->reg == NV_PRAMDAC_VPLL_COEFF && sel_clk & 0x20) ||
-                           (pll_lim->reg == NV_RAMDAC_VPLL2 && sel_clk & 0x80)) {
-                               if (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_CHIP_ID_INDEX) < 0xa3)
-                                       pll_lim->refclk = 200000;
-                               else
-                                       pll_lim->refclk = 25000;
-                       }
-               }
-       } else if (pll_lim_ver == 0x30) { /* ver 0x30 */
-               uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen];
-               uint8_t *record = NULL;
-               int i;
-
-               BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
-                       pll_lim->reg);
-
-               for (i = 0; i < entries; i++, entry += recordlen) {
-                       if (ROM32(entry[3]) == pll_lim->reg) {
-                               record = &bios->data[ROM16(entry[1])];
-                               break;
-                       }
-               }
-
-               if (!record) {
-                       NV_ERROR(dev, "Register 0x%08x not found in PLL "
-                                "limits table", pll_lim->reg);
-                       return -ENOENT;
-               }
-
-               pll_lim->vco1.minfreq = ROM16(record[0]) * 1000;
-               pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000;
-               pll_lim->vco2.minfreq = ROM16(record[4]) * 1000;
-               pll_lim->vco2.maxfreq = ROM16(record[6]) * 1000;
-               pll_lim->vco1.min_inputfreq = ROM16(record[8]) * 1000;
-               pll_lim->vco2.min_inputfreq = ROM16(record[10]) * 1000;
-               pll_lim->vco1.max_inputfreq = ROM16(record[12]) * 1000;
-               pll_lim->vco2.max_inputfreq = ROM16(record[14]) * 1000;
-               pll_lim->vco1.min_n = record[16];
-               pll_lim->vco1.max_n = record[17];
-               pll_lim->vco1.min_m = record[18];
-               pll_lim->vco1.max_m = record[19];
-               pll_lim->vco2.min_n = record[20];
-               pll_lim->vco2.max_n = record[21];
-               pll_lim->vco2.min_m = record[22];
-               pll_lim->vco2.max_m = record[23];
-               pll_lim->max_usable_log2p = pll_lim->max_log2p = record[25];
-               pll_lim->log2p_bias = record[27];
-               pll_lim->refclk = ROM32(record[28]);
-       } else if (pll_lim_ver) { /* ver 0x40 */
-               uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen];
-               uint8_t *record = NULL;
-               int i;
-
-               BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
-                       pll_lim->reg);
-
-               for (i = 0; i < entries; i++, entry += recordlen) {
-                       if (ROM32(entry[3]) == pll_lim->reg) {
-                               record = &bios->data[ROM16(entry[1])];
-                               break;
-                       }
-               }
-
-               if (!record) {
-                       NV_ERROR(dev, "Register 0x%08x not found in PLL "
-                                "limits table", pll_lim->reg);
-                       return -ENOENT;
-               }
-
-               pll_lim->vco1.minfreq = ROM16(record[0]) * 1000;
-               pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000;
-               pll_lim->vco1.min_inputfreq = ROM16(record[4]) * 1000;
-               pll_lim->vco1.max_inputfreq = ROM16(record[6]) * 1000;
-               pll_lim->vco1.min_m = record[8];
-               pll_lim->vco1.max_m = record[9];
-               pll_lim->vco1.min_n = record[10];
-               pll_lim->vco1.max_n = record[11];
-               pll_lim->min_p = record[12];
-               pll_lim->max_p = record[13];
-               pll_lim->refclk = ROM16(entry[9]) * 1000;
-       }
-
-       /*
-        * By now any valid limit table ought to have set a max frequency for
-        * vco1, so if it's zero it's either a pre limit table bios, or one
-        * with an empty limit table (seen on nv18)
-        */
-       if (!pll_lim->vco1.maxfreq) {
-               pll_lim->vco1.minfreq = bios->fminvco;
-               pll_lim->vco1.maxfreq = bios->fmaxvco;
-               pll_lim->vco1.min_inputfreq = 0;
-               pll_lim->vco1.max_inputfreq = INT_MAX;
-               pll_lim->vco1.min_n = 0x1;
-               pll_lim->vco1.max_n = 0xff;
-               pll_lim->vco1.min_m = 0x1;
-               if (crystal_straps == 0) {
-                       /* nv05 does this, nv11 doesn't, nv10 unknown */
-                       if (cv < 0x11)
-                               pll_lim->vco1.min_m = 0x7;
-                       pll_lim->vco1.max_m = 0xd;
-               } else {
-                       if (cv < 0x11)
-                               pll_lim->vco1.min_m = 0x8;
-                       pll_lim->vco1.max_m = 0xe;
-               }
-               if (cv < 0x17 || cv == 0x1a || cv == 0x20)
-                       pll_lim->max_log2p = 4;
-               else
-                       pll_lim->max_log2p = 5;
-               pll_lim->max_usable_log2p = pll_lim->max_log2p;
-       }
-
-       if (!pll_lim->refclk)
-               switch (crystal_straps) {
-               case 0:
-                       pll_lim->refclk = 13500;
-                       break;
-               case (1 << 6):
-                       pll_lim->refclk = 14318;
-                       break;
-               case (1 << 22):
-                       pll_lim->refclk = 27000;
-                       break;
-               case (1 << 22 | 1 << 6):
-                       pll_lim->refclk = 25000;
-                       break;
-               }
-
-       NV_DEBUG(dev, "pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
-       NV_DEBUG(dev, "pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
-       NV_DEBUG(dev, "pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
-       NV_DEBUG(dev, "pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
-       NV_DEBUG(dev, "pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
-       NV_DEBUG(dev, "pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
-       NV_DEBUG(dev, "pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
-       NV_DEBUG(dev, "pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
-       if (pll_lim->vco2.maxfreq) {
-               NV_DEBUG(dev, "pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
-               NV_DEBUG(dev, "pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
-               NV_DEBUG(dev, "pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
-               NV_DEBUG(dev, "pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
-               NV_DEBUG(dev, "pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
-               NV_DEBUG(dev, "pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
-               NV_DEBUG(dev, "pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
-               NV_DEBUG(dev, "pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
-       }
-       if (!pll_lim->max_p) {
-               NV_DEBUG(dev, "pll.max_log2p: %d\n", pll_lim->max_log2p);
-               NV_DEBUG(dev, "pll.log2p_bias: %d\n", pll_lim->log2p_bias);
-       } else {
-               NV_DEBUG(dev, "pll.min_p: %d\n", pll_lim->min_p);
-               NV_DEBUG(dev, "pll.max_p: %d\n", pll_lim->max_p);
-       }
-       NV_DEBUG(dev, "pll.refclk: %d\n", pll_lim->refclk);
-
-       return 0;
-}
-
 static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint16_t offset)
 {
        /*
index 52fce11e9d26a1ecd1bc9703fbeee0032f3099f9..e9bb14ec8c3b9c3badecc2324d0f5b259314d793 100644 (file)
@@ -144,58 +144,6 @@ enum LVDS_script {
        LVDS_PANEL_OFF
 };
 
-/* these match types in pll limits table version 0x40,
- * nouveau uses them on all chipsets internally where a
- * specific pll needs to be referenced, but the exact
- * register isn't known.
- */
-enum pll_types {
-       PLL_CORE   = 0x01,
-       PLL_SHADER = 0x02,
-       PLL_UNK03  = 0x03,
-       PLL_MEMORY = 0x04,
-       PLL_VDEC   = 0x05,
-       PLL_UNK40  = 0x40,
-       PLL_UNK41  = 0x41,
-       PLL_UNK42  = 0x42,
-       PLL_VPLL0  = 0x80,
-       PLL_VPLL1  = 0x81,
-       PLL_MAX    = 0xff
-};
-
-struct pll_lims {
-       u32 reg;
-
-       struct {
-               int minfreq;
-               int maxfreq;
-               int min_inputfreq;
-               int max_inputfreq;
-
-               uint8_t min_m;
-               uint8_t max_m;
-               uint8_t min_n;
-               uint8_t max_n;
-       } vco1, vco2;
-
-       uint8_t max_log2p;
-       /*
-        * for most pre nv50 cards setting a log2P of 7 (the common max_log2p
-        * value) is no different to 6 (at least for vplls) so allowing the MNP
-        * calc to use 7 causes the generated clock to be out by a factor of 2.
-        * however, max_log2p cannot be fixed-up during parsing as the
-        * unmodified max_log2p value is still needed for setting mplls, hence
-        * an additional max_usable_log2p member
-        */
-       uint8_t max_usable_log2p;
-       uint8_t log2p_bias;
-
-       uint8_t min_p;
-       uint8_t max_p;
-
-       int refclk;
-};
-
 struct nvbios {
        struct drm_device *dev;
        enum {
index dad96cce5e390a77ff3de98d63658632c9603cc0..5b487655d6b893ebf054dd1c5d51d66132a1a7bf 100644 (file)
@@ -260,219 +260,3 @@ nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm
        } else
                nv20_update_arb(burst, lwm);
 }
-
-static int
-getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
-             struct nouveau_pll_vals *bestpv)
-{
-       /* Find M, N and P for a single stage PLL
-        *
-        * Note that some bioses (NV3x) have lookup tables of precomputed MNP
-        * values, but we're too lazy to use those atm
-        *
-        * "clk" parameter in kHz
-        * returns calculated clock
-        */
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int cv = dev_priv->vbios.chip_version;
-       int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq;
-       int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m;
-       int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n;
-       int minU = pll_lim->vco1.min_inputfreq;
-       int maxU = pll_lim->vco1.max_inputfreq;
-       int minP = pll_lim->max_p ? pll_lim->min_p : 0;
-       int maxP = pll_lim->max_p ? pll_lim->max_p : pll_lim->max_usable_log2p;
-       int crystal = pll_lim->refclk;
-       int M, N, thisP, P;
-       int clkP, calcclk;
-       int delta, bestdelta = INT_MAX;
-       int bestclk = 0;
-
-       /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
-       /* possibly correlated with introduction of 27MHz crystal */
-       if (dev_priv->card_type < NV_50) {
-               if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
-                       if (clk > 250000)
-                               maxM = 6;
-                       if (clk > 340000)
-                               maxM = 2;
-               } else if (cv < 0x40) {
-                       if (clk > 150000)
-                               maxM = 6;
-                       if (clk > 200000)
-                               maxM = 4;
-                       if (clk > 340000)
-                               maxM = 2;
-               }
-       }
-
-       P = pll_lim->max_p ? maxP : (1 << maxP);
-       if ((clk * P) < minvco) {
-               minvco = clk * maxP;
-               maxvco = minvco * 2;
-       }
-
-       if (clk + clk/200 > maxvco)     /* +0.5% */
-               maxvco = clk + clk/200;
-
-       /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
-       for (thisP = minP; thisP <= maxP; thisP++) {
-               P = pll_lim->max_p ? thisP : (1 << thisP);
-               clkP = clk * P;
-
-               if (clkP < minvco)
-                       continue;
-               if (clkP > maxvco)
-                       return bestclk;
-
-               for (M = minM; M <= maxM; M++) {
-                       if (crystal/M < minU)
-                               return bestclk;
-                       if (crystal/M > maxU)
-                               continue;
-
-                       /* add crystal/2 to round better */
-                       N = (clkP * M + crystal/2) / crystal;
-
-                       if (N < minN)
-                               continue;
-                       if (N > maxN)
-                               break;
-
-                       /* more rounding additions */
-                       calcclk = ((N * crystal + P/2) / P + M/2) / M;
-                       delta = abs(calcclk - clk);
-                       /* we do an exhaustive search rather than terminating
-                        * on an optimality condition...
-                        */
-                       if (delta < bestdelta) {
-                               bestdelta = delta;
-                               bestclk = calcclk;
-                               bestpv->N1 = N;
-                               bestpv->M1 = M;
-                               bestpv->log2P = thisP;
-                               if (delta == 0) /* except this one */
-                                       return bestclk;
-                       }
-               }
-       }
-
-       return bestclk;
-}
-
-static int
-getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
-             struct nouveau_pll_vals *bestpv)
-{
-       /* Find M, N and P for a two stage PLL
-        *
-        * Note that some bioses (NV30+) have lookup tables of precomputed MNP
-        * values, but we're too lazy to use those atm
-        *
-        * "clk" parameter in kHz
-        * returns calculated clock
-        */
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int chip_version = dev_priv->vbios.chip_version;
-       int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq;
-       int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq;
-       int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq;
-       int maxU1 = pll_lim->vco1.max_inputfreq, maxU2 = pll_lim->vco2.max_inputfreq;
-       int minM1 = pll_lim->vco1.min_m, maxM1 = pll_lim->vco1.max_m;
-       int minN1 = pll_lim->vco1.min_n, maxN1 = pll_lim->vco1.max_n;
-       int minM2 = pll_lim->vco2.min_m, maxM2 = pll_lim->vco2.max_m;
-       int minN2 = pll_lim->vco2.min_n, maxN2 = pll_lim->vco2.max_n;
-       int maxlog2P = pll_lim->max_usable_log2p;
-       int crystal = pll_lim->refclk;
-       bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
-       int M1, N1, M2, N2, log2P;
-       int clkP, calcclk1, calcclk2, calcclkout;
-       int delta, bestdelta = INT_MAX;
-       int bestclk = 0;
-
-       int vco2 = (maxvco2 - maxvco2/200) / 2;
-       for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
-               ;
-       clkP = clk << log2P;
-
-       if (maxvco2 < clk + clk/200)    /* +0.5% */
-               maxvco2 = clk + clk/200;
-
-       for (M1 = minM1; M1 <= maxM1; M1++) {
-               if (crystal/M1 < minU1)
-                       return bestclk;
-               if (crystal/M1 > maxU1)
-                       continue;
-
-               for (N1 = minN1; N1 <= maxN1; N1++) {
-                       calcclk1 = crystal * N1 / M1;
-                       if (calcclk1 < minvco1)
-                               continue;
-                       if (calcclk1 > maxvco1)
-                               break;
-
-                       for (M2 = minM2; M2 <= maxM2; M2++) {
-                               if (calcclk1/M2 < minU2)
-                                       break;
-                               if (calcclk1/M2 > maxU2)
-                                       continue;
-
-                               /* add calcclk1/2 to round better */
-                               N2 = (clkP * M2 + calcclk1/2) / calcclk1;
-                               if (N2 < minN2)
-                                       continue;
-                               if (N2 > maxN2)
-                                       break;
-
-                               if (!fixedgain2) {
-                                       if (chip_version < 0x60)
-                                               if (N2/M2 < 4 || N2/M2 > 10)
-                                                       continue;
-
-                                       calcclk2 = calcclk1 * N2 / M2;
-                                       if (calcclk2 < minvco2)
-                                               break;
-                                       if (calcclk2 > maxvco2)
-                                               continue;
-                               } else
-                                       calcclk2 = calcclk1;
-
-                               calcclkout = calcclk2 >> log2P;
-                               delta = abs(calcclkout - clk);
-                               /* we do an exhaustive search rather than terminating
-                                * on an optimality condition...
-                                */
-                               if (delta < bestdelta) {
-                                       bestdelta = delta;
-                                       bestclk = calcclkout;
-                                       bestpv->N1 = N1;
-                                       bestpv->M1 = M1;
-                                       bestpv->N2 = N2;
-                                       bestpv->M2 = M2;
-                                       bestpv->log2P = log2P;
-                                       if (delta == 0) /* except this one */
-                                               return bestclk;
-                               }
-                       }
-               }
-       }
-
-       return bestclk;
-}
-
-int
-nouveau_calc_pll_mnp(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
-                    struct nouveau_pll_vals *pv)
-{
-       int outclk;
-
-       if (!pll_lim->vco2.maxfreq)
-               outclk = getMNP_single(dev, pll_lim, clk, pv);
-       else
-               outclk = getMNP_double(dev, pll_lim, clk, pv);
-
-       if (!outclk)
-               NV_ERROR(dev, "Could not find a compatible set of PLL values\n");
-
-       return outclk;
-}
index 76582b0a9df831a16a8f2718716185ece2d49b1b..30431c820bb3b61aefc21bcaedd7269e06c5b7f5 100644 (file)
@@ -2,8 +2,10 @@
 #include "nouveau_compat.h"
 
 #include <subdev/bios.h>
+#include <subdev/bios/pll.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
+#include <subdev/clock.h>
 
 void *nouveau_newpriv(struct drm_device *);
 
@@ -180,3 +182,76 @@ auxch_wr(struct drm_device *dev, struct nouveau_i2c_port *port,
 {
        return nv_wraux(port, addr, data, size);
 }
+
+u32
+get_pll_register(struct drm_device *dev, u32 type)
+{
+       struct nouveau_drm *drm = nouveau_newpriv(dev);
+       struct nouveau_bios *bios = nouveau_bios(drm->device);
+       struct nvbios_pll info;
+
+       if (nvbios_pll_parse(bios, type, &info))
+               return 0;
+       return info.reg;
+}
+
+int
+get_pll_limits(struct drm_device *dev, u32 type, struct nvbios_pll *info)
+{
+       struct nouveau_drm *drm = nouveau_newpriv(dev);
+       struct nouveau_bios *bios = nouveau_bios(drm->device);
+
+       return nvbios_pll_parse(bios, type, info);
+}
+
+int
+setPLL(struct drm_device *dev, u32 reg, u32 freq)
+{
+       struct nouveau_drm *drm = nouveau_newpriv(dev);
+       struct nouveau_clock *clk = nouveau_clock(drm->device);
+       int ret = -ENODEV;
+
+       if (clk->pll_set)
+               ret = clk->pll_set(clk, reg, freq);
+       return ret;
+}
+
+
+int
+nouveau_calc_pll_mnp(struct drm_device *dev, struct nvbios_pll *info,
+                    int freq, struct nouveau_pll_vals *pv)
+{
+       struct nouveau_drm *drm = nouveau_newpriv(dev);
+       struct nouveau_clock *clk = nouveau_clock(drm->device);
+       int ret = 0;
+
+       if (clk->pll_calc)
+               ret = clk->pll_calc(clk, info, freq, pv);
+       return ret;
+}
+
+int
+nouveau_hw_setpll(struct drm_device *dev, u32 reg1,
+                 struct nouveau_pll_vals *pv)
+{
+       struct nouveau_drm *drm = nouveau_newpriv(dev);
+       struct nouveau_clock *clk = nouveau_clock(drm->device);
+       int ret = -ENODEV;
+
+       if (clk->pll_prog)
+               ret = clk->pll_prog(clk, reg1, pv);
+       return ret;
+}
+
+int nva3_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq,
+                 int *N, int *fN, int *M, int *P);
+
+int
+nva3_calc_pll(struct drm_device *dev, struct nvbios_pll *info, u32 freq,
+             int *N, int *fN, int *M, int *P)
+{
+       struct nouveau_drm *drm = nouveau_newpriv(dev);
+       struct nouveau_clock *clk = nouveau_clock(drm->device);
+
+       return nva3_pll_calc(clk, info, freq, N, fN, M, P);
+}
index 9b3298bfe4ed23ac9749733ad4c0fee1ae91e23f..8bf5bec81710e2177beeecabcb16fac7c9cab6ea 100644 (file)
@@ -32,5 +32,17 @@ int nouveau_i2c_identify(struct drm_device *dev, const char *what,
 int auxch_rd(struct drm_device *, struct nouveau_i2c_port *, u32, u8 *, u8);
 int auxch_wr(struct drm_device *, struct nouveau_i2c_port *, u32, u8 *, u8);
 
+struct nvbios_pll;
+struct nouveau_pll_vals;
+
+u32 get_pll_register(struct drm_device *dev, u32 type);
+int get_pll_limits(struct drm_device *, u32, struct nvbios_pll *);
+int setPLL(struct drm_device *, u32 reg, u32 clk);
+
+int nouveau_calc_pll_mnp(struct drm_device *, struct nvbios_pll *,
+                        int, struct nouveau_pll_vals *);
+int nva3_calc_pll(struct drm_device *dev, struct nvbios_pll *info, u32 freq,
+             int *N, int *fN, int *M, int *P);
+int nouveau_hw_setpll(struct drm_device *, u32, struct nouveau_pll_vals *);
 
 #endif
index afe978702097549ccd3ef6bba3d2e1703717f3f0..2b519b5cfac951e8549ef71220d79ccca1dfe19a 100644 (file)
@@ -68,6 +68,9 @@ struct nouveau_grctx;
 struct nouveau_mem;
 #include <subdev/vm.h>
 
+#include <subdev/bios/pll.h>
+#include "nouveau_compat.h"
+
 #define MAX_NUM_DCB_ENTRIES 16
 
 #define NOUVEAU_MAX_CHANNEL_NR 4096
@@ -549,24 +552,6 @@ struct nouveau_engine {
        struct nouveau_vram_engine    vram;
 };
 
-struct nouveau_pll_vals {
-       union {
-               struct {
-#ifdef __BIG_ENDIAN
-                       uint8_t N1, M1, N2, M2;
-#else
-                       uint8_t M1, N1, M2, N2;
-#endif
-               };
-               struct {
-                       uint16_t NM1, NM2;
-               } __attribute__((packed));
-       };
-       int log2P;
-
-       int refclk;
-};
-
 enum nv04_fp_display_regs {
        FP_DISPLAY_END,
        FP_TOTAL,
@@ -1060,9 +1045,6 @@ extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table,
 extern void nouveau_bios_init_exec(struct drm_device *, uint16_t table);
 extern struct dcb_connector_table_entry *
 nouveau_bios_connector_entry(struct drm_device *, int index);
-extern u32 get_pll_register(struct drm_device *, enum pll_types);
-extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
-                         struct pll_lims *);
 extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk,
                                          struct dcb_entry *, int crtc);
 extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *);
@@ -1365,12 +1347,6 @@ int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *,
 int nouveau_display_dumb_destroy(struct drm_file *, struct drm_device *,
                                 uint32_t handle);
 
-/* nv50_calc.c */
-int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk,
-                 int *N1, int *M1, int *N2, int *M2, int *P);
-int nva3_calc_pll(struct drm_device *, struct pll_lims *,
-                 int clk, int *N, int *fN, int *M, int *P);
-
 #ifndef ioread32_native
 #ifdef __BIG_ENDIAN
 #define ioread16_native ioread16be
@@ -1398,7 +1374,6 @@ static inline void nvchan_wr32(struct nouveau_channel *chan,
 }
 
 /* register access */
-#include "nouveau_compat.h"
 #define nv_rd08 _nv_rd08
 #define nv_wr08 _nv_wr08
 #define nv_rd32 _nv_rd32
index b87ad3bd7739cb9a0ad69ec01730761ed9708638..fdd667b705287e269a035a04dfff073f945223bc 100644 (file)
@@ -26,6 +26,8 @@
 #include "nouveau_drv.h"
 #include "nouveau_hw.h"
 
+#include <subdev/bios/pll.h>
+
 #define CHIPSET_NFORCE 0x01a0
 #define CHIPSET_NFORCE2 0x01f0
 
@@ -122,270 +124,6 @@ NVBlankScreen(struct drm_device *dev, int head, bool blank)
        NVVgaSeqReset(dev, head, false);
 }
 
-/*
- * PLL setting
- */
-
-static int
-powerctrl_1_shift(int chip_version, int reg)
-{
-       int shift = -4;
-
-       if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
-               return shift;
-
-       switch (reg) {
-       case NV_RAMDAC_VPLL2:
-               shift += 4;
-       case NV_PRAMDAC_VPLL_COEFF:
-               shift += 4;
-       case NV_PRAMDAC_MPLL_COEFF:
-               shift += 4;
-       case NV_PRAMDAC_NVPLL_COEFF:
-               shift += 4;
-       }
-
-       /*
-        * the shift for vpll regs is only used for nv3x chips with a single
-        * stage pll
-        */
-       if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
-                         chip_version == 0x36 || chip_version >= 0x40))
-               shift = -4;
-
-       return shift;
-}
-
-static void
-setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv)
-{
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int chip_version = dev_priv->vbios.chip_version;
-       uint32_t oldpll = NVReadRAMDAC(dev, 0, reg);
-       int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
-       uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
-       uint32_t saved_powerctrl_1 = 0;
-       int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
-
-       if (oldpll == pll)
-               return; /* already set */
-
-       if (shift_powerctrl_1 >= 0) {
-               saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
-               nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
-                       (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
-                       1 << shift_powerctrl_1);
-       }
-
-       if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
-               /* upclock -- write new post divider first */
-               NVWriteRAMDAC(dev, 0, reg, pv->log2P << 16 | (oldpll & 0xffff));
-       else
-               /* downclock -- write new NM first */
-               NVWriteRAMDAC(dev, 0, reg, (oldpll & 0xffff0000) | pv->NM1);
-
-       if (chip_version < 0x17 && chip_version != 0x11)
-               /* wait a bit on older chips */
-               msleep(64);
-       NVReadRAMDAC(dev, 0, reg);
-
-       /* then write the other half as well */
-       NVWriteRAMDAC(dev, 0, reg, pll);
-
-       if (shift_powerctrl_1 >= 0)
-               nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
-}
-
-static uint32_t
-new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
-{
-       bool head_a = (reg1 == NV_PRAMDAC_VPLL_COEFF);
-
-       if (ss) /* single stage pll mode */
-               ramdac580 |= head_a ? NV_RAMDAC_580_VPLL1_ACTIVE :
-                                     NV_RAMDAC_580_VPLL2_ACTIVE;
-       else
-               ramdac580 &= head_a ? ~NV_RAMDAC_580_VPLL1_ACTIVE :
-                                     ~NV_RAMDAC_580_VPLL2_ACTIVE;
-
-       return ramdac580;
-}
-
-static void
-setPLL_double_highregs(struct drm_device *dev, uint32_t reg1,
-                      struct nouveau_pll_vals *pv)
-{
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int chip_version = dev_priv->vbios.chip_version;
-       bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
-       uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70);
-       uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1);
-       uint32_t oldpll2 = !nv3035 ? NVReadRAMDAC(dev, 0, reg2) : 0;
-       uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
-       uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
-       uint32_t oldramdac580 = 0, ramdac580 = 0;
-       bool single_stage = !pv->NM2 || pv->N2 == pv->M2;       /* nv41+ only */
-       uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
-       int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
-
-       /* model specific additions to generic pll1 and pll2 set up above */
-       if (nv3035) {
-               pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
-                      (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
-               pll2 = 0;
-       }
-       if (chip_version > 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { /* !nv40 */
-               oldramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580);
-               ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
-               if (oldramdac580 != ramdac580)
-                       oldpll1 = ~0;   /* force mismatch */
-               if (single_stage)
-                       /* magic value used by nvidia in single stage mode */
-                       pll2 |= 0x011f;
-       }
-       if (chip_version > 0x70)
-               /* magic bits set by the blob (but not the bios) on g71-73 */
-               pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
-
-       if (oldpll1 == pll1 && oldpll2 == pll2)
-               return; /* already set */
-
-       if (shift_powerctrl_1 >= 0) {
-               saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
-               nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
-                       (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
-                       1 << shift_powerctrl_1);
-       }
-
-       if (chip_version >= 0x40) {
-               int shift_c040 = 14;
-
-               switch (reg1) {
-               case NV_PRAMDAC_MPLL_COEFF:
-                       shift_c040 += 2;
-               case NV_PRAMDAC_NVPLL_COEFF:
-                       shift_c040 += 2;
-               case NV_RAMDAC_VPLL2:
-                       shift_c040 += 2;
-               case NV_PRAMDAC_VPLL_COEFF:
-                       shift_c040 += 2;
-               }
-
-               savedc040 = nvReadMC(dev, 0xc040);
-               if (shift_c040 != 14)
-                       nvWriteMC(dev, 0xc040, savedc040 & ~(3 << shift_c040));
-       }
-
-       if (oldramdac580 != ramdac580)
-               NVWriteRAMDAC(dev, 0, NV_PRAMDAC_580, ramdac580);
-
-       if (!nv3035)
-               NVWriteRAMDAC(dev, 0, reg2, pll2);
-       NVWriteRAMDAC(dev, 0, reg1, pll1);
-
-       if (shift_powerctrl_1 >= 0)
-               nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
-       if (chip_version >= 0x40)
-               nvWriteMC(dev, 0xc040, savedc040);
-}
-
-static void
-setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg,
-                     struct nouveau_pll_vals *pv)
-{
-       /* When setting PLLs, there is a merry game of disabling and enabling
-        * various bits of hardware during the process. This function is a
-        * synthesis of six nv4x traces, nearly each card doing a subtly
-        * different thing. With luck all the necessary bits for each card are
-        * combined herein. Without luck it deviates from each card's formula
-        * so as to not work on any :)
-        */
-
-       uint32_t Preg = NMNMreg - 4;
-       bool mpll = Preg == 0x4020;
-       uint32_t oldPval = nvReadMC(dev, Preg);
-       uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
-       uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) |
-                       0xc << 28 | pv->log2P << 16;
-       uint32_t saved4600 = 0;
-       /* some cards have different maskc040s */
-       uint32_t maskc040 = ~(3 << 14), savedc040;
-       bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
-
-       if (nvReadMC(dev, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
-               return;
-
-       if (Preg == 0x4000)
-               maskc040 = ~0x333;
-       if (Preg == 0x4058)
-               maskc040 = ~(0xc << 24);
-
-       if (mpll) {
-               struct pll_lims pll_lim;
-               uint8_t Pval2;
-
-               if (get_pll_limits(dev, Preg, &pll_lim))
-                       return;
-
-               Pval2 = pv->log2P + pll_lim.log2p_bias;
-               if (Pval2 > pll_lim.max_log2p)
-                       Pval2 = pll_lim.max_log2p;
-               Pval |= 1 << 28 | Pval2 << 20;
-
-               saved4600 = nvReadMC(dev, 0x4600);
-               nvWriteMC(dev, 0x4600, saved4600 | 8 << 28);
-       }
-       if (single_stage)
-               Pval |= mpll ? 1 << 12 : 1 << 8;
-
-       nvWriteMC(dev, Preg, oldPval | 1 << 28);
-       nvWriteMC(dev, Preg, Pval & ~(4 << 28));
-       if (mpll) {
-               Pval |= 8 << 20;
-               nvWriteMC(dev, 0x4020, Pval & ~(0xc << 28));
-               nvWriteMC(dev, 0x4038, Pval & ~(0xc << 28));
-       }
-
-       savedc040 = nvReadMC(dev, 0xc040);
-       nvWriteMC(dev, 0xc040, savedc040 & maskc040);
-
-       nvWriteMC(dev, NMNMreg, NMNM);
-       if (NMNMreg == 0x4024)
-               nvWriteMC(dev, 0x403c, NMNM);
-
-       nvWriteMC(dev, Preg, Pval);
-       if (mpll) {
-               Pval &= ~(8 << 20);
-               nvWriteMC(dev, 0x4020, Pval);
-               nvWriteMC(dev, 0x4038, Pval);
-               nvWriteMC(dev, 0x4600, saved4600);
-       }
-
-       nvWriteMC(dev, 0xc040, savedc040);
-
-       if (mpll) {
-               nvWriteMC(dev, 0x4020, Pval & ~(1 << 28));
-               nvWriteMC(dev, 0x4038, Pval & ~(1 << 28));
-       }
-}
-
-void
-nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1,
-                 struct nouveau_pll_vals *pv)
-{
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int cv = dev_priv->vbios.chip_version;
-
-       if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
-           cv >= 0x40) {
-               if (reg1 > 0x405c)
-                       setPLL_double_highregs(dev, reg1, pv);
-               else
-                       setPLL_double_lowregs(dev, reg1, pv);
-       } else
-               setPLL_single(dev, reg1, pv);
-}
-
 /*
  * PLL getting
  */
@@ -423,12 +161,12 @@ nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1,
 }
 
 int
-nouveau_hw_get_pllvals(struct drm_device *dev, enum pll_types plltype,
+nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype,
                       struct nouveau_pll_vals *pllvals)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        uint32_t reg1 = get_pll_register(dev, plltype), pll1, pll2 = 0;
-       struct pll_lims pll_lim;
+       struct nvbios_pll pll_lim;
        int ret;
 
        if (reg1 == 0)
@@ -478,7 +216,7 @@ nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pv)
 }
 
 int
-nouveau_hw_get_clock(struct drm_device *dev, enum pll_types plltype)
+nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype)
 {
        struct nouveau_pll_vals pllvals;
        int ret;
@@ -517,9 +255,9 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
         * when such a condition detected.  only seen on nv11 to date
         */
 
-       struct pll_lims pll_lim;
+       struct nvbios_pll pll_lim;
        struct nouveau_pll_vals pv;
-       enum pll_types pll = head ? PLL_VPLL1 : PLL_VPLL0;
+       enum nvbios_pll_type pll = head ? PLL_VPLL1 : PLL_VPLL0;
 
        if (get_pll_limits(dev, pll, &pll_lim))
                return;
@@ -527,7 +265,7 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
 
        if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m &&
            pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n &&
-           pv.log2P <= pll_lim.max_log2p)
+           pv.log2P <= pll_lim.max_p)
                return;
 
        NV_WARN(dev, "VPLL %d outwith limits, attempting to fix\n", head + 1);
@@ -535,7 +273,7 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
        /* set lowest clock within static limits */
        pv.M1 = pll_lim.vco1.max_m;
        pv.N1 = pll_lim.vco1.min_n;
-       pv.log2P = pll_lim.max_usable_log2p;
+       pv.log2P = pll_lim.max_p_usable;
        nouveau_hw_setpll(dev, pll_lim.reg, &pv);
 }
 
index 2989090b94349abdcd05096f89735f641a748c3b..ff7f31c279d8bb9624a0c02cc5aa4de404202f39 100644 (file)
@@ -26,6 +26,8 @@
 #include "drmP.h"
 #include "nouveau_drv.h"
 
+#include <subdev/bios/pll.h>
+
 #define MASK(field) ( \
        (0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field))
 
@@ -38,12 +40,10 @@ void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value);
 uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index);
 void NVSetOwner(struct drm_device *, int owner);
 void NVBlankScreen(struct drm_device *, int head, bool blank);
-void nouveau_hw_setpll(struct drm_device *, uint32_t reg1,
-                      struct nouveau_pll_vals *pv);
-int nouveau_hw_get_pllvals(struct drm_device *, enum pll_types plltype,
+int nouveau_hw_get_pllvals(struct drm_device *, enum nvbios_pll_type plltype,
                           struct nouveau_pll_vals *pllvals);
 int nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pllvals);
-int nouveau_hw_get_clock(struct drm_device *, enum pll_types plltype);
+int nouveau_hw_get_clock(struct drm_device *, enum nvbios_pll_type plltype);
 void nouveau_hw_save_vga_fonts(struct drm_device *, bool save);
 void nouveau_hw_save_state(struct drm_device *, int head,
                           struct nv04_mode_state *state);
@@ -55,8 +55,6 @@ void nouveau_hw_load_state_palette(struct drm_device *, int head,
 /* nouveau_calc.c */
 extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp,
                             int *burst, int *lwm);
-extern int nouveau_calc_pll_mnp(struct drm_device *, struct pll_lims *pll_lim,
-                               int clk, struct nouveau_pll_vals *pv);
 
 static inline uint32_t
 nvReadMC(struct drm_device *dev, uint32_t reg)
index 43accc11102f551e85882db7d150cf64e93ce895..93ca09b90da55077af903589d0c273eac15e1a9c 100644 (file)
@@ -108,7 +108,7 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod
        struct nv04_mode_state *state = &dev_priv->mode_reg;
        struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index];
        struct nouveau_pll_vals *pv = &regp->pllvals;
-       struct pll_lims pll_lim;
+       struct nvbios_pll pll_lim;
 
        if (get_pll_limits(dev, nv_crtc->index ? PLL_VPLL1 : PLL_VPLL0, &pll_lim))
                return;
@@ -126,7 +126,7 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod
         * has yet been observed in allowing the use a single stage pll on all
         * nv43 however.  the behaviour of single stage use is untested on nv40
         */
-       if (dev_priv->chipset > 0x40 && dot_clock <= (pll_lim.vco1.maxfreq / 2))
+       if (dev_priv->chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2))
                memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2));
 
        if (!nouveau_calc_pll_mnp(dev, &pll_lim, dot_clock, pv))
index 6e7589918fa93dbcf91eb7d4b27e082b3a78e86d..4528d48dc52bce30d47951634cd53cdd2a437ad4 100644 (file)
@@ -46,7 +46,7 @@ nv04_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 }
 
 struct nv04_pm_clock {
-       struct pll_lims pll;
+       struct nvbios_pll pll;
        struct nouveau_pll_vals calc;
 };
 
index 661d9cfd980d031e78ad862b4fe2f99bfff5e355..d857525666ee5437366c81e89c83a535618c129c 100644 (file)
@@ -107,7 +107,7 @@ struct nv40_pm_state {
 };
 
 static int
-nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
+nv40_calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll,
              u32 clk, int *N1, int *M1, int *N2, int *M2, int *log2P)
 {
        struct nouveau_pll_vals coef;
@@ -117,8 +117,8 @@ nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
        if (ret)
                return ret;
 
-       if (clk < pll->vco1.maxfreq)
-               pll->vco2.maxfreq = 0;
+       if (clk < pll->vco1.max_freq)
+               pll->vco2.max_freq = 0;
 
        ret = nouveau_calc_pll_mnp(dev, pll, clk, &coef);
        if (ret == 0)
@@ -127,7 +127,7 @@ nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
        *N1 = coef.N1;
        *M1 = coef.M1;
        if (N2 && M2) {
-               if (pll->vco2.maxfreq) {
+               if (pll->vco2.max_freq) {
                        *N2 = coef.N2;
                        *M2 = coef.M2;
                } else {
@@ -143,7 +143,7 @@ void *
 nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 {
        struct nv40_pm_state *info;
-       struct pll_lims pll;
+       struct nvbios_pll pll;
        int N1, N2, M1, M2, log2P;
        int ret;
 
@@ -191,7 +191,7 @@ nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
                goto out;
 
        info->mpll_ctrl  = 0x80000000 | (log2P << 16);
-       info->mpll_ctrl |= min2(pll.log2p_bias + log2P, pll.max_log2p) << 20;
+       info->mpll_ctrl |= min2(pll.bias_p + log2P, pll.max_p) << 20;
        if (N2 == M2) {
                info->mpll_ctrl |= 0x00000100;
                info->mpll_coef  = (N1 << 8) | M1;
diff --git a/drivers/gpu/drm/nouveau/nv50_calc.c b/drivers/gpu/drm/nouveau/nv50_calc.c
deleted file mode 100644 (file)
index 8cf63a8..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2010 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 "drmP.h"
-#include "nouveau_drv.h"
-#include "nouveau_hw.h"
-
-int
-nv50_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
-             int *N1, int *M1, int *N2, int *M2, int *P)
-{
-       struct nouveau_pll_vals pll_vals;
-       int ret;
-
-       ret = nouveau_calc_pll_mnp(dev, pll, clk, &pll_vals);
-       if (ret <= 0)
-               return ret;
-
-       *N1 = pll_vals.N1;
-       *M1 = pll_vals.M1;
-       *N2 = pll_vals.N2;
-       *M2 = pll_vals.M2;
-       *P = pll_vals.log2P;
-       return ret;
-}
-
-int
-nva3_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
-             int *pN, int *pfN, int *pM, int *P)
-{
-       u32 best_err = ~0, err;
-       int M, lM, hM, N, fN;
-
-       *P = pll->vco1.maxfreq / clk;
-       if (*P > pll->max_p)
-               *P = pll->max_p;
-       if (*P < pll->min_p)
-               *P = pll->min_p;
-
-       lM = (pll->refclk + pll->vco1.max_inputfreq) / pll->vco1.max_inputfreq;
-       lM = max(lM, (int)pll->vco1.min_m);
-       hM = (pll->refclk + pll->vco1.min_inputfreq) / pll->vco1.min_inputfreq;
-       hM = min(hM, (int)pll->vco1.max_m);
-
-       for (M = lM; M <= hM; M++) {
-               u32 tmp = clk * *P * M;
-               N  = tmp / pll->refclk;
-               fN = tmp % pll->refclk;
-               if (!pfN && fN >= pll->refclk / 2)
-                       N++;
-
-               if (N < pll->vco1.min_n)
-                       continue;
-               if (N > pll->vco1.max_n)
-                       break;
-
-               err = abs(clk - (pll->refclk * N / M / *P));
-               if (err < best_err) {
-                       best_err = err;
-                       *pN = N;
-                       *pM = M;
-               }
-
-               if (pfN) {
-                       *pfN = (((fN << 13) / pll->refclk) - 4096) & 0xffff;
-                       return clk;
-               }
-       }
-
-       if (unlikely(best_err == ~0)) {
-               NV_ERROR(dev, "unable to find matching pll values\n");
-               return -EINVAL;
-       }
-
-       return pll->refclk * *pN / *pM / *P;
-}
index 22cebd5dd694ecd584a3243bc45a21064a9293e7..40042c1bbc6ec05d5af62ef1aae48797e6a32856 100644 (file)
@@ -329,55 +329,7 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
 int
 nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
 {
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct pll_lims pll;
-       uint32_t reg1, reg2;
-       int ret, N1, M1, N2, M2, P;
-
-       ret = get_pll_limits(dev, PLL_VPLL0 + head, &pll);
-       if (ret)
-               return ret;
-
-       if (pll.vco2.maxfreq) {
-               ret = nv50_calc_pll(dev, &pll, pclk, &N1, &M1, &N2, &M2, &P);
-               if (ret <= 0)
-                       return 0;
-
-               NV_DEBUG(dev, "pclk %d out %d NM1 %d %d NM2 %d %d P %d\n",
-                        pclk, ret, N1, M1, N2, M2, P);
-
-               reg1 = nv_rd32(dev, pll.reg + 4) & 0xff00ff00;
-               reg2 = nv_rd32(dev, pll.reg + 8) & 0x8000ff00;
-               nv_wr32(dev, pll.reg + 0, 0x10000611);
-               nv_wr32(dev, pll.reg + 4, reg1 | (M1 << 16) | N1);
-               nv_wr32(dev, pll.reg + 8, reg2 | (P << 28) | (M2 << 16) | N2);
-       } else
-       if (dev_priv->chipset < NV_C0) {
-               ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P);
-               if (ret <= 0)
-                       return 0;
-
-               NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n",
-                        pclk, ret, N1, N2, M1, P);
-
-               reg1 = nv_rd32(dev, pll.reg + 4) & 0xffc00000;
-               nv_wr32(dev, pll.reg + 0, 0x50000610);
-               nv_wr32(dev, pll.reg + 4, reg1 | (P << 16) | (M1 << 8) | N1);
-               nv_wr32(dev, pll.reg + 8, N2);
-       } else {
-               ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P);
-               if (ret <= 0)
-                       return 0;
-
-               NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n",
-                        pclk, ret, N1, N2, M1, P);
-
-               nv_mask(dev, pll.reg + 0x0c, 0x00000000, 0x00000100);
-               nv_wr32(dev, pll.reg + 0x04, (P << 16) | (N1 << 8) | M1);
-               nv_wr32(dev, pll.reg + 0x10, N2 << 16);
-       }
-
-       return 0;
+       return setPLL(dev, PLL_VPLL0 + head, pclk);
 }
 
 static void
index be5704fd4a868f097eb0c82d33563915ed079744..378ca8ca9d6a8d392d814fe464ef654de0e01083 100644 (file)
@@ -363,7 +363,7 @@ struct nv50_pm_state {
 };
 
 static u32
-calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
+calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll,
         u32 clk, int *N1, int *M1, int *log2P)
 {
        struct nouveau_pll_vals coef;
@@ -373,7 +373,7 @@ calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
        if (ret)
                return 0;
 
-       pll->vco2.maxfreq = 0;
+       pll->vco2.max_freq = 0;
        pll->refclk = read_pll_ref(dev, reg);
        if (!pll->refclk)
                return 0;
@@ -542,7 +542,7 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl,
                .priv = info
        };
        struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-       struct pll_lims pll;
+       struct nvbios_pll pll;
        int N, M, P;
        int ret;
 
@@ -550,14 +550,14 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl,
        info->mctrl  = nv_rd32(dev, 0x004008);
        info->mctrl &= ~0x81ff0200;
        if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) {
-               info->mctrl |= 0x00000200 | (pll.log2p_bias << 19);
+               info->mctrl |= 0x00000200 | (pll.bias_p << 19);
        } else {
                ret = calc_pll(dev, 0x4008, &pll, perflvl->memory, &N, &M, &P);
                if (ret == 0)
                        return -EINVAL;
 
                info->mctrl |= 0x80000000 | (P << 22) | (P << 16);
-               info->mctrl |= pll.log2p_bias << 19;
+               info->mctrl |= pll.bias_p << 19;
                info->mcoef  = (N << 8) | M;
        }
 
@@ -590,7 +590,7 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nv50_pm_state *info;
        struct hwsq_ucode *hwsq;
-       struct pll_lims pll;
+       struct nvbios_pll pll;
        u32 out, mast, divs, ctrl;
        int clk, ret = -EINVAL;
        int N, M, P1, P2;
index ff0fafbfe6214976475e782a80464bd37fa974e6..847c616de12ff07cbf1fc0efbf70b021df13a77a 100644 (file)
@@ -111,7 +111,7 @@ struct creg {
 static int
 calc_clk(struct drm_device *dev, int clk, u32 pll, u32 khz, struct creg *reg)
 {
-       struct pll_lims limits;
+       struct nvbios_pll limits;
        u32 oclk, sclk, sdiv;
        int P, N, M, diff;
        int ret;
index 81b649864fa103dca2de884c24272df2d83ac29e..4a21a7264c3c1a8ad068b2ed263383a3305a14e6 100644 (file)
@@ -212,7 +212,7 @@ calc_src(struct drm_device *dev, int clk, u32 freq, u32 *dsrc, u32 *ddiv)
 static u32
 calc_pll(struct drm_device *dev, int clk, u32 freq, u32 *coef)
 {
-       struct pll_lims limits;
+       struct nvbios_pll limits;
        int N, M, P, ret;
 
        ret = get_pll_limits(dev, 0x137000 + (clk * 0x20), &limits);
@@ -308,7 +308,7 @@ calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq)
 static int
 calc_mem(struct drm_device *dev, struct nvc0_pm_clock *info, u32 freq)
 {
-       struct pll_lims pll;
+       struct nvbios_pll pll;
        int N, M, P, ret;
        u32 ctrl;