diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2012-07-10 17:26:46 +1000 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-10-03 13:12:47 +1000 |
commit | 70790f4f819875e8f390871fd15bbbf823f28e1b (patch) | |
tree | 47949ac5a0af23a9fe1ace1ac5fd8b8823b8e055 | |
parent | 8aceb7de47ea2491abc1a577dc875b19e9947a54 (diff) | |
download | blackbird-op-linux-70790f4f819875e8f390871fd15bbbf823f28e1b.tar.gz blackbird-op-linux-70790f4f819875e8f390871fd15bbbf823f28e1b.zip |
drm/nouveau/clock: pull in the implementation from all over the place
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>
28 files changed, 1328 insertions, 1213 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 2964d71d3996..2574fd35122a 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -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 index 000000000000..c345097592f2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h @@ -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 diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h index 137c4598a1b6..39e73b91d360 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h @@ -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 diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/vga.h b/drivers/gpu/drm/nouveau/core/include/subdev/vga.h index d81df1ae442d..fee09ad818e4 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/vga.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/vga.h @@ -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 index 000000000000..5e5f4cddae3c --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/pll.c @@ -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, ®, &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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c index 3a3b3b149690..b7fd1151166e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c @@ -23,17 +23,309 @@ */ #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; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c index 60d1ca44e39c..a4b2b7ebf9af 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c @@ -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; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c index 82804bdcec31..fd181fbceddb 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c @@ -23,17 +23,57 @@ */ #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 diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c index 876ec46e2b41..cc8d7d162d7c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c @@ -23,17 +23,47 @@ */ #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 diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c index 00641566f2e7..5ccce0b17bf3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c @@ -23,17 +23,46 @@ */ #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 index 000000000000..ef2c0078f337 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h @@ -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 index 000000000000..a2ab6d051ba8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c @@ -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/nv50_calc.c b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c index 8cf63a8b30cd..eed5c16cf610 100644 --- a/drivers/gpu/drm/nouveau/nv50_calc.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c @@ -22,60 +22,43 @@ * Authors: Ben Skeggs */ -#include "drmP.h" -#include "nouveau_drv.h" -#include "nouveau_hw.h" +#include <subdev/clock.h> +#include <subdev/bios.h> +#include <subdev/bios/pll.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; -} +#include "pll.h" int -nva3_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk, - int *pN, int *pfN, int *pM, int *P) +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 = pll->vco1.maxfreq / clk; - if (*P > pll->max_p) - *P = pll->max_p; - if (*P < pll->min_p) - *P = pll->min_p; + *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 = (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); + 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 = clk * *P * M; - N = tmp / pll->refclk; - fN = tmp % pll->refclk; - if (!pfN && fN >= pll->refclk / 2) + u32 tmp = freq * *P * M; + N = tmp / info->refclk; + fN = tmp % info->refclk; + if (!pfN && fN >= info->refclk / 2) N++; - if (N < pll->vco1.min_n) + if (N < info->vco1.min_n) continue; - if (N > pll->vco1.max_n) + if (N > info->vco1.max_n) break; - err = abs(clk - (pll->refclk * N / M / *P)); + err = abs(freq - (info->refclk * N / M / *P)); if (err < best_err) { best_err = err; *pN = N; @@ -83,15 +66,15 @@ nva3_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk, } if (pfN) { - *pfN = (((fN << 13) / pll->refclk) - 4096) & 0xffff; - return clk; + *pfN = (((fN << 13) / info->refclk) - 4096) & 0xffff; + return freq; } } if (unlikely(best_err == ~0)) { - NV_ERROR(dev, "unable to find matching pll values\n"); + nv_error(clock, "unable to find matching pll values\n"); return -EINVAL; } - return pll->refclk * *pN / *pM / *P; + return info->refclk * *pN / *pM / *P; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 35b0a8f9c008..98eaac9da30e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -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) { /* diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 52fce11e9d26..e9bb14ec8c3b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -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 { diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c index dad96cce5e39..5b487655d6b8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_calc.c +++ b/drivers/gpu/drm/nouveau/nouveau_calc.c @@ -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; -} diff --git a/drivers/gpu/drm/nouveau/nouveau_compat.c b/drivers/gpu/drm/nouveau/nouveau_compat.c index 76582b0a9df8..30431c820bb3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_compat.c +++ b/drivers/gpu/drm/nouveau/nouveau_compat.c @@ -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); +} diff --git a/drivers/gpu/drm/nouveau/nouveau_compat.h b/drivers/gpu/drm/nouveau/nouveau_compat.h index 9b3298bfe4ed..8bf5bec81710 100644 --- a/drivers/gpu/drm/nouveau/nouveau_compat.h +++ b/drivers/gpu/drm/nouveau/nouveau_compat.h @@ -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 diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index afe978702097..2b519b5cfac9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -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 diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c index b87ad3bd7739..fdd667b70528 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hw.c +++ b/drivers/gpu/drm/nouveau/nouveau_hw.c @@ -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 @@ -123,270 +125,6 @@ NVBlankScreen(struct drm_device *dev, int head, bool blank) } /* - * 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); } diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.h b/drivers/gpu/drm/nouveau/nouveau_hw.h index 2989090b9434..ff7f31c279d8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hw.h +++ b/drivers/gpu/drm/nouveau/nouveau_hw.h @@ -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) diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c index 43accc11102f..93ca09b90da5 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -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 = ®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; @@ -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)) diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c index 6e7589918fa9..4528d48dc52b 100644 --- a/drivers/gpu/drm/nouveau/nv04_pm.c +++ b/drivers/gpu/drm/nouveau/nv04_pm.c @@ -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; }; diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index 661d9cfd980d..d857525666ee 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -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_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 22cebd5dd694..40042c1bbc6e 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -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 diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index be5704fd4a86..378ca8ca9d6a 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -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; diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index ff0fafbfe621..847c616de12f 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -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; diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c index 81b649864fa1..4a21a7264c3c 100644 --- a/drivers/gpu/drm/nouveau/nvc0_pm.c +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -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; |