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 #include +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 + /* 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 +#include +#include +#include +#include + +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 +#include +#include + +#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; - nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); + 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); +} + +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 +#include +#include + +#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; - nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); + ret = nvbios_pll_parse(bios, type, &info); + if (ret) { + nv_error(clk, "failed to retrieve pll data, %d\n", ret); + return ret; + } + + 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 +#include +#include + +#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; - nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); + 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; + } + + 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 +#include +#include + +#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; - nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); + 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; + } + + 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 +#include +#include + +#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 similarity index 52% rename from drivers/gpu/drm/nouveau/nv50_calc.c rename to 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 +#include +#include + +#include "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; -} - -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 +#include #include #include +#include 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 +#include +#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 + #define CHIPSET_NFORCE 0x01a0 #define CHIPSET_NFORCE2 0x01f0 @@ -122,270 +124,6 @@ NVBlankScreen(struct drm_device *dev, int head, bool blank) NVVgaSeqReset(dev, head, false); } -/* - * PLL setting - */ - -static int -powerctrl_1_shift(int chip_version, int reg) -{ - int shift = -4; - - if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20) - return shift; - - switch (reg) { - case NV_RAMDAC_VPLL2: - shift += 4; - case NV_PRAMDAC_VPLL_COEFF: - shift += 4; - case NV_PRAMDAC_MPLL_COEFF: - shift += 4; - case NV_PRAMDAC_NVPLL_COEFF: - shift += 4; - } - - /* - * the shift for vpll regs is only used for nv3x chips with a single - * stage pll - */ - if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 || - chip_version == 0x36 || chip_version >= 0x40)) - shift = -4; - - return shift; -} - -static void -setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int chip_version = dev_priv->vbios.chip_version; - uint32_t oldpll = NVReadRAMDAC(dev, 0, reg); - int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff; - uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1; - uint32_t saved_powerctrl_1 = 0; - int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg); - - if (oldpll == pll) - return; /* already set */ - - if (shift_powerctrl_1 >= 0) { - saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1); - nvWriteMC(dev, NV_PBUS_POWERCTRL_1, - (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | - 1 << shift_powerctrl_1); - } - - if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1)) - /* upclock -- write new post divider first */ - NVWriteRAMDAC(dev, 0, reg, pv->log2P << 16 | (oldpll & 0xffff)); - else - /* downclock -- write new NM first */ - NVWriteRAMDAC(dev, 0, reg, (oldpll & 0xffff0000) | pv->NM1); - - if (chip_version < 0x17 && chip_version != 0x11) - /* wait a bit on older chips */ - msleep(64); - NVReadRAMDAC(dev, 0, reg); - - /* then write the other half as well */ - NVWriteRAMDAC(dev, 0, reg, pll); - - if (shift_powerctrl_1 >= 0) - nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1); -} - -static uint32_t -new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580) -{ - bool head_a = (reg1 == NV_PRAMDAC_VPLL_COEFF); - - if (ss) /* single stage pll mode */ - ramdac580 |= head_a ? NV_RAMDAC_580_VPLL1_ACTIVE : - NV_RAMDAC_580_VPLL2_ACTIVE; - else - ramdac580 &= head_a ? ~NV_RAMDAC_580_VPLL1_ACTIVE : - ~NV_RAMDAC_580_VPLL2_ACTIVE; - - return ramdac580; -} - -static void -setPLL_double_highregs(struct drm_device *dev, uint32_t reg1, - struct nouveau_pll_vals *pv) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int chip_version = dev_priv->vbios.chip_version; - bool nv3035 = chip_version == 0x30 || chip_version == 0x35; - uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70); - uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1); - uint32_t oldpll2 = !nv3035 ? NVReadRAMDAC(dev, 0, reg2) : 0; - uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1; - uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2; - uint32_t oldramdac580 = 0, ramdac580 = 0; - bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */ - uint32_t saved_powerctrl_1 = 0, savedc040 = 0; - int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1); - - /* model specific additions to generic pll1 and pll2 set up above */ - if (nv3035) { - pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 | - (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4; - pll2 = 0; - } - if (chip_version > 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { /* !nv40 */ - oldramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580); - ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580); - if (oldramdac580 != ramdac580) - oldpll1 = ~0; /* force mismatch */ - if (single_stage) - /* magic value used by nvidia in single stage mode */ - pll2 |= 0x011f; - } - if (chip_version > 0x70) - /* magic bits set by the blob (but not the bios) on g71-73 */ - pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28; - - if (oldpll1 == pll1 && oldpll2 == pll2) - return; /* already set */ - - if (shift_powerctrl_1 >= 0) { - saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1); - nvWriteMC(dev, NV_PBUS_POWERCTRL_1, - (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | - 1 << shift_powerctrl_1); - } - - if (chip_version >= 0x40) { - int shift_c040 = 14; - - switch (reg1) { - case NV_PRAMDAC_MPLL_COEFF: - shift_c040 += 2; - case NV_PRAMDAC_NVPLL_COEFF: - shift_c040 += 2; - case NV_RAMDAC_VPLL2: - shift_c040 += 2; - case NV_PRAMDAC_VPLL_COEFF: - shift_c040 += 2; - } - - savedc040 = nvReadMC(dev, 0xc040); - if (shift_c040 != 14) - nvWriteMC(dev, 0xc040, savedc040 & ~(3 << shift_c040)); - } - - if (oldramdac580 != ramdac580) - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_580, ramdac580); - - if (!nv3035) - NVWriteRAMDAC(dev, 0, reg2, pll2); - NVWriteRAMDAC(dev, 0, reg1, pll1); - - if (shift_powerctrl_1 >= 0) - nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1); - if (chip_version >= 0x40) - nvWriteMC(dev, 0xc040, savedc040); -} - -static void -setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg, - struct nouveau_pll_vals *pv) -{ - /* When setting PLLs, there is a merry game of disabling and enabling - * various bits of hardware during the process. This function is a - * synthesis of six nv4x traces, nearly each card doing a subtly - * different thing. With luck all the necessary bits for each card are - * combined herein. Without luck it deviates from each card's formula - * so as to not work on any :) - */ - - uint32_t Preg = NMNMreg - 4; - bool mpll = Preg == 0x4020; - uint32_t oldPval = nvReadMC(dev, Preg); - uint32_t NMNM = pv->NM2 << 16 | pv->NM1; - uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) | - 0xc << 28 | pv->log2P << 16; - uint32_t saved4600 = 0; - /* some cards have different maskc040s */ - uint32_t maskc040 = ~(3 << 14), savedc040; - bool single_stage = !pv->NM2 || pv->N2 == pv->M2; - - if (nvReadMC(dev, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval) - return; - - if (Preg == 0x4000) - maskc040 = ~0x333; - if (Preg == 0x4058) - maskc040 = ~(0xc << 24); - - if (mpll) { - struct pll_lims pll_lim; - uint8_t Pval2; - - if (get_pll_limits(dev, Preg, &pll_lim)) - return; - - Pval2 = pv->log2P + pll_lim.log2p_bias; - if (Pval2 > pll_lim.max_log2p) - Pval2 = pll_lim.max_log2p; - Pval |= 1 << 28 | Pval2 << 20; - - saved4600 = nvReadMC(dev, 0x4600); - nvWriteMC(dev, 0x4600, saved4600 | 8 << 28); - } - if (single_stage) - Pval |= mpll ? 1 << 12 : 1 << 8; - - nvWriteMC(dev, Preg, oldPval | 1 << 28); - nvWriteMC(dev, Preg, Pval & ~(4 << 28)); - if (mpll) { - Pval |= 8 << 20; - nvWriteMC(dev, 0x4020, Pval & ~(0xc << 28)); - nvWriteMC(dev, 0x4038, Pval & ~(0xc << 28)); - } - - savedc040 = nvReadMC(dev, 0xc040); - nvWriteMC(dev, 0xc040, savedc040 & maskc040); - - nvWriteMC(dev, NMNMreg, NMNM); - if (NMNMreg == 0x4024) - nvWriteMC(dev, 0x403c, NMNM); - - nvWriteMC(dev, Preg, Pval); - if (mpll) { - Pval &= ~(8 << 20); - nvWriteMC(dev, 0x4020, Pval); - nvWriteMC(dev, 0x4038, Pval); - nvWriteMC(dev, 0x4600, saved4600); - } - - nvWriteMC(dev, 0xc040, savedc040); - - if (mpll) { - nvWriteMC(dev, 0x4020, Pval & ~(1 << 28)); - nvWriteMC(dev, 0x4038, Pval & ~(1 << 28)); - } -} - -void -nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1, - struct nouveau_pll_vals *pv) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int cv = dev_priv->vbios.chip_version; - - if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 || - cv >= 0x40) { - if (reg1 > 0x405c) - setPLL_double_highregs(dev, reg1, pv); - else - setPLL_double_lowregs(dev, reg1, pv); - } else - setPLL_single(dev, reg1, pv); -} - /* * PLL getting */ @@ -423,12 +161,12 @@ nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1, } int -nouveau_hw_get_pllvals(struct drm_device *dev, enum pll_types plltype, +nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype, struct nouveau_pll_vals *pllvals) { struct drm_nouveau_private *dev_priv = dev->dev_private; uint32_t reg1 = get_pll_register(dev, plltype), pll1, pll2 = 0; - struct pll_lims pll_lim; + struct nvbios_pll pll_lim; int ret; if (reg1 == 0) @@ -478,7 +216,7 @@ nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pv) } int -nouveau_hw_get_clock(struct drm_device *dev, enum pll_types plltype) +nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype) { struct nouveau_pll_vals pllvals; int ret; @@ -517,9 +255,9 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head) * when such a condition detected. only seen on nv11 to date */ - struct pll_lims pll_lim; + struct nvbios_pll pll_lim; struct nouveau_pll_vals pv; - enum pll_types pll = head ? PLL_VPLL1 : PLL_VPLL0; + enum nvbios_pll_type pll = head ? PLL_VPLL1 : PLL_VPLL0; if (get_pll_limits(dev, pll, &pll_lim)) return; @@ -527,7 +265,7 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head) if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m && pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n && - pv.log2P <= pll_lim.max_log2p) + pv.log2P <= pll_lim.max_p) return; NV_WARN(dev, "VPLL %d outwith limits, attempting to fix\n", head + 1); @@ -535,7 +273,7 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head) /* set lowest clock within static limits */ pv.M1 = pll_lim.vco1.max_m; pv.N1 = pll_lim.vco1.min_n; - pv.log2P = pll_lim.max_usable_log2p; + pv.log2P = pll_lim.max_p_usable; nouveau_hw_setpll(dev, pll_lim.reg, &pv); } 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 + #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;