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
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
--- /dev/null
+#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
#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 *
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
#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);
--- /dev/null
+/*
+ * 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, ®, &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;
+}
*/
#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
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;
}
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,
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;
}
*/
#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
*/
#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
*/
#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
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
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;
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;
}
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;
}
BIOSLOG(bios, "0x%04X: Reg: 0x%04X, Freq: %dkHz\n",
offset, reg, freq);
- setPLL(bios, reg, freq);
+ setPLL(bios->dev, reg, freq);
return 9;
}
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;
}
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;
}
"Type %02x Reg 0x%08x Freq %dKHz\n",
offset, type, reg, freq);
- setPLL(bios, reg, freq);
+ setPLL(bios->dev, reg, freq);
return len;
}
}
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)
{
/*
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 {
} 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;
-}
#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 *);
{
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);
+}
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
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
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,
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 *);
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
}
/* register access */
-#include "nouveau_compat.h"
#define nv_rd08 _nv_rd08
#define nv_wr08 _nv_wr08
#define nv_rd32 _nv_rd32
#include "nouveau_drv.h"
#include "nouveau_hw.h"
+#include <subdev/bios/pll.h>
+
#define CHIPSET_NFORCE 0x01a0
#define CHIPSET_NFORCE2 0x01f0
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
*/
}
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)
}
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;
* 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;
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);
/* 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);
}
#include "drmP.h"
#include "nouveau_drv.h"
+#include <subdev/bios/pll.h>
+
#define MASK(field) ( \
(0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field))
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);
/* 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)
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 = ®p->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;
* 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))
}
struct nv04_pm_clock {
- struct pll_lims pll;
+ struct nvbios_pll pll;
struct nouveau_pll_vals calc;
};
};
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;
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)
*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 {
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;
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;
+++ /dev/null
-/*
- * 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;
-}
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
};
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;
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;
.priv = info
};
struct hwsq_ucode *hwsq = &info->mclk_hwsq;
- struct pll_lims pll;
+ struct nvbios_pll pll;
int N, M, P;
int ret;
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;
}
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;
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;
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);
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;