mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-01 07:42:18 +00:00
04c1822c0a
Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey!
2348 lines
55 KiB
C
2348 lines
55 KiB
C
/*
|
|
* Copyright 2012 Red Hat Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* Authors: Ben Skeggs
|
|
*/
|
|
#include <subdev/bios.h>
|
|
#include <subdev/bios/bit.h>
|
|
#include <subdev/bios/bmp.h>
|
|
#include <subdev/bios/conn.h>
|
|
#include <subdev/bios/dcb.h>
|
|
#include <subdev/bios/dp.h>
|
|
#include <subdev/bios/gpio.h>
|
|
#include <subdev/bios/init.h>
|
|
#include <subdev/bios/ramcfg.h>
|
|
|
|
#include <subdev/devinit.h>
|
|
#include <subdev/gpio.h>
|
|
#include <subdev/i2c.h>
|
|
#include <subdev/vga.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#define bioslog(lvl, fmt, args...) do { \
|
|
nvkm_printk(init->subdev, lvl, info, "0x%08x[%c]: "fmt, \
|
|
init->offset, init_exec(init) ? \
|
|
'0' + (init->nested - 1) : ' ', ##args); \
|
|
} while(0)
|
|
#define cont(fmt, args...) do { \
|
|
if (init->subdev->debug >= NV_DBG_TRACE) \
|
|
printk(fmt, ##args); \
|
|
} while(0)
|
|
#define trace(fmt, args...) bioslog(TRACE, fmt, ##args)
|
|
#define warn(fmt, args...) bioslog(WARN, fmt, ##args)
|
|
#define error(fmt, args...) bioslog(ERROR, fmt, ##args)
|
|
|
|
/******************************************************************************
|
|
* init parser control flow helpers
|
|
*****************************************************************************/
|
|
|
|
static inline bool
|
|
init_exec(struct nvbios_init *init)
|
|
{
|
|
return (init->execute == 1) || ((init->execute & 5) == 5);
|
|
}
|
|
|
|
static inline void
|
|
init_exec_set(struct nvbios_init *init, bool exec)
|
|
{
|
|
if (exec) init->execute &= 0xfd;
|
|
else init->execute |= 0x02;
|
|
}
|
|
|
|
static inline void
|
|
init_exec_inv(struct nvbios_init *init)
|
|
{
|
|
init->execute ^= 0x02;
|
|
}
|
|
|
|
static inline void
|
|
init_exec_force(struct nvbios_init *init, bool exec)
|
|
{
|
|
if (exec) init->execute |= 0x04;
|
|
else init->execute &= 0xfb;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* init parser wrappers for normal register/i2c/whatever accessors
|
|
*****************************************************************************/
|
|
|
|
static inline int
|
|
init_or(struct nvbios_init *init)
|
|
{
|
|
if (init_exec(init)) {
|
|
if (init->or >= 0)
|
|
return init->or;
|
|
error("script needs OR!!\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
init_link(struct nvbios_init *init)
|
|
{
|
|
if (init_exec(init)) {
|
|
if (init->link)
|
|
return init->link == 2;
|
|
error("script needs OR link\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
init_head(struct nvbios_init *init)
|
|
{
|
|
if (init_exec(init)) {
|
|
if (init->head >= 0)
|
|
return init->head;
|
|
error("script needs head\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static u8
|
|
init_conn(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
struct nvbios_connE connE;
|
|
u8 ver, hdr;
|
|
u32 conn;
|
|
|
|
if (init_exec(init)) {
|
|
if (init->outp) {
|
|
conn = init->outp->connector;
|
|
conn = nvbios_connEp(bios, conn, &ver, &hdr, &connE);
|
|
if (conn)
|
|
return connE.type;
|
|
}
|
|
|
|
error("script needs connector type\n");
|
|
}
|
|
|
|
return 0xff;
|
|
}
|
|
|
|
static inline u32
|
|
init_nvreg(struct nvbios_init *init, u32 reg)
|
|
{
|
|
struct nvkm_devinit *devinit = init->subdev->device->devinit;
|
|
|
|
/* C51 (at least) sometimes has the lower bits set which the VBIOS
|
|
* interprets to mean that access needs to go through certain IO
|
|
* ports instead. The NVIDIA binary driver has been seen to access
|
|
* these through the NV register address, so lets assume we can
|
|
* do the same
|
|
*/
|
|
reg &= ~0x00000003;
|
|
|
|
/* GF8+ display scripts need register addresses mangled a bit to
|
|
* select a specific CRTC/OR
|
|
*/
|
|
if (init->subdev->device->card_type >= NV_50) {
|
|
if (reg & 0x80000000) {
|
|
reg += init_head(init) * 0x800;
|
|
reg &= ~0x80000000;
|
|
}
|
|
|
|
if (reg & 0x40000000) {
|
|
reg += init_or(init) * 0x800;
|
|
reg &= ~0x40000000;
|
|
if (reg & 0x20000000) {
|
|
reg += init_link(init) * 0x80;
|
|
reg &= ~0x20000000;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (reg & ~0x00fffffc)
|
|
warn("unknown bits in register 0x%08x\n", reg);
|
|
|
|
return nvkm_devinit_mmio(devinit, reg);
|
|
}
|
|
|
|
static u32
|
|
init_rd32(struct nvbios_init *init, u32 reg)
|
|
{
|
|
struct nvkm_device *device = init->subdev->device;
|
|
reg = init_nvreg(init, reg);
|
|
if (reg != ~0 && init_exec(init))
|
|
return nvkm_rd32(device, reg);
|
|
return 0x00000000;
|
|
}
|
|
|
|
static void
|
|
init_wr32(struct nvbios_init *init, u32 reg, u32 val)
|
|
{
|
|
struct nvkm_device *device = init->subdev->device;
|
|
reg = init_nvreg(init, reg);
|
|
if (reg != ~0 && init_exec(init))
|
|
nvkm_wr32(device, reg, val);
|
|
}
|
|
|
|
static u32
|
|
init_mask(struct nvbios_init *init, u32 reg, u32 mask, u32 val)
|
|
{
|
|
struct nvkm_device *device = init->subdev->device;
|
|
reg = init_nvreg(init, reg);
|
|
if (reg != ~0 && init_exec(init)) {
|
|
u32 tmp = nvkm_rd32(device, reg);
|
|
nvkm_wr32(device, reg, (tmp & ~mask) | val);
|
|
return tmp;
|
|
}
|
|
return 0x00000000;
|
|
}
|
|
|
|
static u8
|
|
init_rdport(struct nvbios_init *init, u16 port)
|
|
{
|
|
if (init_exec(init))
|
|
return nvkm_rdport(init->subdev->device, init->head, port);
|
|
return 0x00;
|
|
}
|
|
|
|
static void
|
|
init_wrport(struct nvbios_init *init, u16 port, u8 value)
|
|
{
|
|
if (init_exec(init))
|
|
nvkm_wrport(init->subdev->device, init->head, port, value);
|
|
}
|
|
|
|
static u8
|
|
init_rdvgai(struct nvbios_init *init, u16 port, u8 index)
|
|
{
|
|
struct nvkm_subdev *subdev = init->subdev;
|
|
if (init_exec(init)) {
|
|
int head = init->head < 0 ? 0 : init->head;
|
|
return nvkm_rdvgai(subdev->device, head, port, index);
|
|
}
|
|
return 0x00;
|
|
}
|
|
|
|
static void
|
|
init_wrvgai(struct nvbios_init *init, u16 port, u8 index, u8 value)
|
|
{
|
|
struct nvkm_device *device = init->subdev->device;
|
|
|
|
/* force head 0 for updates to cr44, it only exists on first head */
|
|
if (device->card_type < NV_50) {
|
|
if (port == 0x03d4 && index == 0x44)
|
|
init->head = 0;
|
|
}
|
|
|
|
if (init_exec(init)) {
|
|
int head = init->head < 0 ? 0 : init->head;
|
|
nvkm_wrvgai(device, head, port, index, value);
|
|
}
|
|
|
|
/* select head 1 if cr44 write selected it */
|
|
if (device->card_type < NV_50) {
|
|
if (port == 0x03d4 && index == 0x44 && value == 3)
|
|
init->head = 1;
|
|
}
|
|
}
|
|
|
|
static struct i2c_adapter *
|
|
init_i2c(struct nvbios_init *init, int index)
|
|
{
|
|
struct nvkm_i2c *i2c = init->subdev->device->i2c;
|
|
struct nvkm_i2c_bus *bus;
|
|
|
|
if (index == 0xff) {
|
|
index = NVKM_I2C_BUS_PRI;
|
|
if (init->outp && init->outp->i2c_upper_default)
|
|
index = NVKM_I2C_BUS_SEC;
|
|
} else
|
|
if (index == 0x80) {
|
|
index = NVKM_I2C_BUS_PRI;
|
|
} else
|
|
if (index == 0x81) {
|
|
index = NVKM_I2C_BUS_SEC;
|
|
}
|
|
|
|
bus = nvkm_i2c_bus_find(i2c, index);
|
|
return bus ? &bus->i2c : NULL;
|
|
}
|
|
|
|
static int
|
|
init_rdi2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg)
|
|
{
|
|
struct i2c_adapter *adap = init_i2c(init, index);
|
|
if (adap && init_exec(init))
|
|
return nvkm_rdi2cr(adap, addr, reg);
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int
|
|
init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val)
|
|
{
|
|
struct i2c_adapter *adap = init_i2c(init, index);
|
|
if (adap && init_exec(init))
|
|
return nvkm_wri2cr(adap, addr, reg, val);
|
|
return -ENODEV;
|
|
}
|
|
|
|
static struct nvkm_i2c_aux *
|
|
init_aux(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_i2c *i2c = init->subdev->device->i2c;
|
|
if (!init->outp) {
|
|
if (init_exec(init))
|
|
error("script needs output for aux\n");
|
|
return NULL;
|
|
}
|
|
return nvkm_i2c_aux_find(i2c, init->outp->i2c_index);
|
|
}
|
|
|
|
static u8
|
|
init_rdauxr(struct nvbios_init *init, u32 addr)
|
|
{
|
|
struct nvkm_i2c_aux *aux = init_aux(init);
|
|
u8 data;
|
|
|
|
if (aux && init_exec(init)) {
|
|
int ret = nvkm_rdaux(aux, addr, &data, 1);
|
|
if (ret == 0)
|
|
return data;
|
|
trace("auxch read failed with %d\n", ret);
|
|
}
|
|
|
|
return 0x00;
|
|
}
|
|
|
|
static int
|
|
init_wrauxr(struct nvbios_init *init, u32 addr, u8 data)
|
|
{
|
|
struct nvkm_i2c_aux *aux = init_aux(init);
|
|
if (aux && init_exec(init)) {
|
|
int ret = nvkm_wraux(aux, addr, &data, 1);
|
|
if (ret)
|
|
trace("auxch write failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
return -ENODEV;
|
|
}
|
|
|
|
static void
|
|
init_prog_pll(struct nvbios_init *init, u32 id, u32 freq)
|
|
{
|
|
struct nvkm_devinit *devinit = init->subdev->device->devinit;
|
|
if (init_exec(init)) {
|
|
int ret = nvkm_devinit_pll_set(devinit, id, freq);
|
|
if (ret)
|
|
warn("failed to prog pll 0x%08x to %dkHz\n", id, freq);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* parsing of bios structures that are required to execute init tables
|
|
*****************************************************************************/
|
|
|
|
static u16
|
|
init_table(struct nvkm_bios *bios, u16 *len)
|
|
{
|
|
struct bit_entry bit_I;
|
|
|
|
if (!bit_entry(bios, 'I', &bit_I)) {
|
|
*len = bit_I.length;
|
|
return bit_I.offset;
|
|
}
|
|
|
|
if (bmp_version(bios) >= 0x0510) {
|
|
*len = 14;
|
|
return bios->bmp_offset + 75;
|
|
}
|
|
|
|
return 0x0000;
|
|
}
|
|
|
|
static u16
|
|
init_table_(struct nvbios_init *init, u16 offset, const char *name)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 len, data = init_table(bios, &len);
|
|
if (data) {
|
|
if (len >= offset + 2) {
|
|
data = nvbios_rd16(bios, data + offset);
|
|
if (data)
|
|
return data;
|
|
|
|
warn("%s pointer invalid\n", name);
|
|
return 0x0000;
|
|
}
|
|
|
|
warn("init data too short for %s pointer", name);
|
|
return 0x0000;
|
|
}
|
|
|
|
warn("init data not found\n");
|
|
return 0x0000;
|
|
}
|
|
|
|
#define init_script_table(b) init_table_((b), 0x00, "script table")
|
|
#define init_macro_index_table(b) init_table_((b), 0x02, "macro index table")
|
|
#define init_macro_table(b) init_table_((b), 0x04, "macro table")
|
|
#define init_condition_table(b) init_table_((b), 0x06, "condition table")
|
|
#define init_io_condition_table(b) init_table_((b), 0x08, "io condition table")
|
|
#define init_io_flag_condition_table(b) init_table_((b), 0x0a, "io flag conditon table")
|
|
#define init_function_table(b) init_table_((b), 0x0c, "function table")
|
|
#define init_xlat_table(b) init_table_((b), 0x10, "xlat table");
|
|
|
|
static u16
|
|
init_script(struct nvkm_bios *bios, int index)
|
|
{
|
|
struct nvbios_init init = { .subdev = &bios->subdev };
|
|
u16 bmp_ver = bmp_version(bios), data;
|
|
|
|
if (bmp_ver && bmp_ver < 0x0510) {
|
|
if (index > 1 || bmp_ver < 0x0100)
|
|
return 0x0000;
|
|
|
|
data = bios->bmp_offset + (bmp_ver < 0x0200 ? 14 : 18);
|
|
return nvbios_rd16(bios, data + (index * 2));
|
|
}
|
|
|
|
data = init_script_table(&init);
|
|
if (data)
|
|
return nvbios_rd16(bios, data + (index * 2));
|
|
|
|
return 0x0000;
|
|
}
|
|
|
|
static u16
|
|
init_unknown_script(struct nvkm_bios *bios)
|
|
{
|
|
u16 len, data = init_table(bios, &len);
|
|
if (data && len >= 16)
|
|
return nvbios_rd16(bios, data + 14);
|
|
return 0x0000;
|
|
}
|
|
|
|
static u8
|
|
init_ram_restrict_group_count(struct nvbios_init *init)
|
|
{
|
|
return nvbios_ramcfg_count(init->subdev->device->bios);
|
|
}
|
|
|
|
static u8
|
|
init_ram_restrict(struct nvbios_init *init)
|
|
{
|
|
/* This appears to be the behaviour of the VBIOS parser, and *is*
|
|
* important to cache the NV_PEXTDEV_BOOT0 on later chipsets to
|
|
* avoid fucking up the memory controller (somehow) by reading it
|
|
* on every INIT_RAM_RESTRICT_ZM_GROUP opcode.
|
|
*
|
|
* Preserving the non-caching behaviour on earlier chipsets just
|
|
* in case *not* re-reading the strap causes similar breakage.
|
|
*/
|
|
if (!init->ramcfg || init->subdev->device->bios->version.major < 0x70)
|
|
init->ramcfg = 0x80000000 | nvbios_ramcfg_index(init->subdev);
|
|
return (init->ramcfg & 0x7fffffff);
|
|
}
|
|
|
|
static u8
|
|
init_xlat_(struct nvbios_init *init, u8 index, u8 offset)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 table = init_xlat_table(init);
|
|
if (table) {
|
|
u16 data = nvbios_rd16(bios, table + (index * 2));
|
|
if (data)
|
|
return nvbios_rd08(bios, data + offset);
|
|
warn("xlat table pointer %d invalid\n", index);
|
|
}
|
|
return 0x00;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* utility functions used by various init opcode handlers
|
|
*****************************************************************************/
|
|
|
|
static bool
|
|
init_condition_met(struct nvbios_init *init, u8 cond)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 table = init_condition_table(init);
|
|
if (table) {
|
|
u32 reg = nvbios_rd32(bios, table + (cond * 12) + 0);
|
|
u32 msk = nvbios_rd32(bios, table + (cond * 12) + 4);
|
|
u32 val = nvbios_rd32(bios, table + (cond * 12) + 8);
|
|
trace("\t[0x%02x] (R[0x%06x] & 0x%08x) == 0x%08x\n",
|
|
cond, reg, msk, val);
|
|
return (init_rd32(init, reg) & msk) == val;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
init_io_condition_met(struct nvbios_init *init, u8 cond)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 table = init_io_condition_table(init);
|
|
if (table) {
|
|
u16 port = nvbios_rd16(bios, table + (cond * 5) + 0);
|
|
u8 index = nvbios_rd08(bios, table + (cond * 5) + 2);
|
|
u8 mask = nvbios_rd08(bios, table + (cond * 5) + 3);
|
|
u8 value = nvbios_rd08(bios, table + (cond * 5) + 4);
|
|
trace("\t[0x%02x] (0x%04x[0x%02x] & 0x%02x) == 0x%02x\n",
|
|
cond, port, index, mask, value);
|
|
return (init_rdvgai(init, port, index) & mask) == value;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
init_io_flag_condition_met(struct nvbios_init *init, u8 cond)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 table = init_io_flag_condition_table(init);
|
|
if (table) {
|
|
u16 port = nvbios_rd16(bios, table + (cond * 9) + 0);
|
|
u8 index = nvbios_rd08(bios, table + (cond * 9) + 2);
|
|
u8 mask = nvbios_rd08(bios, table + (cond * 9) + 3);
|
|
u8 shift = nvbios_rd08(bios, table + (cond * 9) + 4);
|
|
u16 data = nvbios_rd16(bios, table + (cond * 9) + 5);
|
|
u8 dmask = nvbios_rd08(bios, table + (cond * 9) + 7);
|
|
u8 value = nvbios_rd08(bios, table + (cond * 9) + 8);
|
|
u8 ioval = (init_rdvgai(init, port, index) & mask) >> shift;
|
|
return (nvbios_rd08(bios, data + ioval) & dmask) == value;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static inline u32
|
|
init_shift(u32 data, u8 shift)
|
|
{
|
|
if (shift < 0x80)
|
|
return data >> shift;
|
|
return data << (0x100 - shift);
|
|
}
|
|
|
|
static u32
|
|
init_tmds_reg(struct nvbios_init *init, u8 tmds)
|
|
{
|
|
/* For mlv < 0x80, it is an index into a table of TMDS base addresses.
|
|
* For mlv == 0x80 use the "or" value of the dcb_entry indexed by
|
|
* CR58 for CR57 = 0 to index a table of offsets to the basic
|
|
* 0x6808b0 address.
|
|
* For mlv == 0x81 use the "or" value of the dcb_entry indexed by
|
|
* CR58 for CR57 = 0 to index a table of offsets to the basic
|
|
* 0x6808b0 address, and then flip the offset by 8.
|
|
*/
|
|
const int pramdac_offset[13] = {
|
|
0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 };
|
|
const u32 pramdac_table[4] = {
|
|
0x6808b0, 0x6808b8, 0x6828b0, 0x6828b8 };
|
|
|
|
if (tmds >= 0x80) {
|
|
if (init->outp) {
|
|
u32 dacoffset = pramdac_offset[init->outp->or];
|
|
if (tmds == 0x81)
|
|
dacoffset ^= 8;
|
|
return 0x6808b0 + dacoffset;
|
|
}
|
|
|
|
if (init_exec(init))
|
|
error("tmds opcodes need dcb\n");
|
|
} else {
|
|
if (tmds < ARRAY_SIZE(pramdac_table))
|
|
return pramdac_table[tmds];
|
|
|
|
error("tmds selector 0x%02x unknown\n", tmds);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* init opcode handlers
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* init_reserved - stub for various unknown/unused single-byte opcodes
|
|
*
|
|
*/
|
|
static void
|
|
init_reserved(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 opcode = nvbios_rd08(bios, init->offset);
|
|
u8 length, i;
|
|
|
|
switch (opcode) {
|
|
case 0xaa:
|
|
length = 4;
|
|
break;
|
|
default:
|
|
length = 1;
|
|
break;
|
|
}
|
|
|
|
trace("RESERVED 0x%02x\t", opcode);
|
|
for (i = 1; i < length; i++)
|
|
cont(" 0x%02x", nvbios_rd08(bios, init->offset + i));
|
|
cont("\n");
|
|
init->offset += length;
|
|
}
|
|
|
|
/**
|
|
* INIT_DONE - opcode 0x71
|
|
*
|
|
*/
|
|
static void
|
|
init_done(struct nvbios_init *init)
|
|
{
|
|
trace("DONE\n");
|
|
init->offset = 0x0000;
|
|
}
|
|
|
|
/**
|
|
* INIT_IO_RESTRICT_PROG - opcode 0x32
|
|
*
|
|
*/
|
|
static void
|
|
init_io_restrict_prog(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 port = nvbios_rd16(bios, init->offset + 1);
|
|
u8 index = nvbios_rd08(bios, init->offset + 3);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 4);
|
|
u8 shift = nvbios_rd08(bios, init->offset + 5);
|
|
u8 count = nvbios_rd08(bios, init->offset + 6);
|
|
u32 reg = nvbios_rd32(bios, init->offset + 7);
|
|
u8 conf, i;
|
|
|
|
trace("IO_RESTRICT_PROG\tR[0x%06x] = "
|
|
"((0x%04x[0x%02x] & 0x%02x) >> %d) [{\n",
|
|
reg, port, index, mask, shift);
|
|
init->offset += 11;
|
|
|
|
conf = (init_rdvgai(init, port, index) & mask) >> shift;
|
|
for (i = 0; i < count; i++) {
|
|
u32 data = nvbios_rd32(bios, init->offset);
|
|
|
|
if (i == conf) {
|
|
trace("\t0x%08x *\n", data);
|
|
init_wr32(init, reg, data);
|
|
} else {
|
|
trace("\t0x%08x\n", data);
|
|
}
|
|
|
|
init->offset += 4;
|
|
}
|
|
trace("}]\n");
|
|
}
|
|
|
|
/**
|
|
* INIT_REPEAT - opcode 0x33
|
|
*
|
|
*/
|
|
static void
|
|
init_repeat(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 count = nvbios_rd08(bios, init->offset + 1);
|
|
u16 repeat = init->repeat;
|
|
|
|
trace("REPEAT\t0x%02x\n", count);
|
|
init->offset += 2;
|
|
|
|
init->repeat = init->offset;
|
|
init->repend = init->offset;
|
|
while (count--) {
|
|
init->offset = init->repeat;
|
|
nvbios_exec(init);
|
|
if (count)
|
|
trace("REPEAT\t0x%02x\n", count);
|
|
}
|
|
init->offset = init->repend;
|
|
init->repeat = repeat;
|
|
}
|
|
|
|
/**
|
|
* INIT_IO_RESTRICT_PLL - opcode 0x34
|
|
*
|
|
*/
|
|
static void
|
|
init_io_restrict_pll(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 port = nvbios_rd16(bios, init->offset + 1);
|
|
u8 index = nvbios_rd08(bios, init->offset + 3);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 4);
|
|
u8 shift = nvbios_rd08(bios, init->offset + 5);
|
|
s8 iofc = nvbios_rd08(bios, init->offset + 6);
|
|
u8 count = nvbios_rd08(bios, init->offset + 7);
|
|
u32 reg = nvbios_rd32(bios, init->offset + 8);
|
|
u8 conf, i;
|
|
|
|
trace("IO_RESTRICT_PLL\tR[0x%06x] =PLL= "
|
|
"((0x%04x[0x%02x] & 0x%02x) >> 0x%02x) IOFCOND 0x%02x [{\n",
|
|
reg, port, index, mask, shift, iofc);
|
|
init->offset += 12;
|
|
|
|
conf = (init_rdvgai(init, port, index) & mask) >> shift;
|
|
for (i = 0; i < count; i++) {
|
|
u32 freq = nvbios_rd16(bios, init->offset) * 10;
|
|
|
|
if (i == conf) {
|
|
trace("\t%dkHz *\n", freq);
|
|
if (iofc > 0 && init_io_flag_condition_met(init, iofc))
|
|
freq *= 2;
|
|
init_prog_pll(init, reg, freq);
|
|
} else {
|
|
trace("\t%dkHz\n", freq);
|
|
}
|
|
|
|
init->offset += 2;
|
|
}
|
|
trace("}]\n");
|
|
}
|
|
|
|
/**
|
|
* INIT_END_REPEAT - opcode 0x36
|
|
*
|
|
*/
|
|
static void
|
|
init_end_repeat(struct nvbios_init *init)
|
|
{
|
|
trace("END_REPEAT\n");
|
|
init->offset += 1;
|
|
|
|
if (init->repeat) {
|
|
init->repend = init->offset;
|
|
init->offset = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_COPY - opcode 0x37
|
|
*
|
|
*/
|
|
static void
|
|
init_copy(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
|
u8 shift = nvbios_rd08(bios, init->offset + 5);
|
|
u8 smask = nvbios_rd08(bios, init->offset + 6);
|
|
u16 port = nvbios_rd16(bios, init->offset + 7);
|
|
u8 index = nvbios_rd08(bios, init->offset + 9);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 10);
|
|
u8 data;
|
|
|
|
trace("COPY\t0x%04x[0x%02x] &= 0x%02x |= "
|
|
"((R[0x%06x] %s 0x%02x) & 0x%02x)\n",
|
|
port, index, mask, reg, (shift & 0x80) ? "<<" : ">>",
|
|
(shift & 0x80) ? (0x100 - shift) : shift, smask);
|
|
init->offset += 11;
|
|
|
|
data = init_rdvgai(init, port, index) & mask;
|
|
data |= init_shift(init_rd32(init, reg), shift) & smask;
|
|
init_wrvgai(init, port, index, data);
|
|
}
|
|
|
|
/**
|
|
* INIT_NOT - opcode 0x38
|
|
*
|
|
*/
|
|
static void
|
|
init_not(struct nvbios_init *init)
|
|
{
|
|
trace("NOT\n");
|
|
init->offset += 1;
|
|
init_exec_inv(init);
|
|
}
|
|
|
|
/**
|
|
* INIT_IO_FLAG_CONDITION - opcode 0x39
|
|
*
|
|
*/
|
|
static void
|
|
init_io_flag_condition(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
|
|
|
trace("IO_FLAG_CONDITION\t0x%02x\n", cond);
|
|
init->offset += 2;
|
|
|
|
if (!init_io_flag_condition_met(init, cond))
|
|
init_exec_set(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_GENERIC_CONDITION - opcode 0x3a
|
|
*
|
|
*/
|
|
static void
|
|
init_generic_condition(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
struct nvbios_dpout info;
|
|
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
|
u8 size = nvbios_rd08(bios, init->offset + 2);
|
|
u8 ver, hdr, cnt, len;
|
|
u16 data;
|
|
|
|
trace("GENERIC_CONDITION\t0x%02x 0x%02x\n", cond, size);
|
|
init->offset += 3;
|
|
|
|
switch (cond) {
|
|
case 0: /* CONDITION_ID_INT_DP. */
|
|
if (init_conn(init) != DCB_CONNECTOR_eDP)
|
|
init_exec_set(init, false);
|
|
break;
|
|
case 1: /* CONDITION_ID_USE_SPPLL0. */
|
|
case 2: /* CONDITION_ID_USE_SPPLL1. */
|
|
if ( init->outp &&
|
|
(data = nvbios_dpout_match(bios, DCB_OUTPUT_DP,
|
|
(init->outp->or << 0) |
|
|
(init->outp->sorconf.link << 6),
|
|
&ver, &hdr, &cnt, &len, &info)))
|
|
{
|
|
if (!(info.flags & cond))
|
|
init_exec_set(init, false);
|
|
break;
|
|
}
|
|
|
|
if (init_exec(init))
|
|
warn("script needs dp output table data\n");
|
|
break;
|
|
case 5: /* CONDITION_ID_ASSR_SUPPORT. */
|
|
if (!(init_rdauxr(init, 0x0d) & 1))
|
|
init_exec_set(init, false);
|
|
break;
|
|
case 7: /* CONDITION_ID_NO_PANEL_SEQ_DELAYS. */
|
|
init_exec_set(init, false);
|
|
break;
|
|
default:
|
|
warn("INIT_GENERIC_CONDITION: unknown 0x%02x\n", cond);
|
|
init->offset += size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_IO_MASK_OR - opcode 0x3b
|
|
*
|
|
*/
|
|
static void
|
|
init_io_mask_or(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 index = nvbios_rd08(bios, init->offset + 1);
|
|
u8 or = init_or(init);
|
|
u8 data;
|
|
|
|
trace("IO_MASK_OR\t0x03d4[0x%02x] &= ~(1 << 0x%02x)\n", index, or);
|
|
init->offset += 2;
|
|
|
|
data = init_rdvgai(init, 0x03d4, index);
|
|
init_wrvgai(init, 0x03d4, index, data &= ~(1 << or));
|
|
}
|
|
|
|
/**
|
|
* INIT_IO_OR - opcode 0x3c
|
|
*
|
|
*/
|
|
static void
|
|
init_io_or(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 index = nvbios_rd08(bios, init->offset + 1);
|
|
u8 or = init_or(init);
|
|
u8 data;
|
|
|
|
trace("IO_OR\t0x03d4[0x%02x] |= (1 << 0x%02x)\n", index, or);
|
|
init->offset += 2;
|
|
|
|
data = init_rdvgai(init, 0x03d4, index);
|
|
init_wrvgai(init, 0x03d4, index, data | (1 << or));
|
|
}
|
|
|
|
/**
|
|
* INIT_ANDN_REG - opcode 0x47
|
|
*
|
|
*/
|
|
static void
|
|
init_andn_reg(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
|
u32 mask = nvbios_rd32(bios, init->offset + 5);
|
|
|
|
trace("ANDN_REG\tR[0x%06x] &= ~0x%08x\n", reg, mask);
|
|
init->offset += 9;
|
|
|
|
init_mask(init, reg, mask, 0);
|
|
}
|
|
|
|
/**
|
|
* INIT_OR_REG - opcode 0x48
|
|
*
|
|
*/
|
|
static void
|
|
init_or_reg(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
|
u32 mask = nvbios_rd32(bios, init->offset + 5);
|
|
|
|
trace("OR_REG\tR[0x%06x] |= 0x%08x\n", reg, mask);
|
|
init->offset += 9;
|
|
|
|
init_mask(init, reg, 0, mask);
|
|
}
|
|
|
|
/**
|
|
* INIT_INDEX_ADDRESS_LATCHED - opcode 0x49
|
|
*
|
|
*/
|
|
static void
|
|
init_idx_addr_latched(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 creg = nvbios_rd32(bios, init->offset + 1);
|
|
u32 dreg = nvbios_rd32(bios, init->offset + 5);
|
|
u32 mask = nvbios_rd32(bios, init->offset + 9);
|
|
u32 data = nvbios_rd32(bios, init->offset + 13);
|
|
u8 count = nvbios_rd08(bios, init->offset + 17);
|
|
|
|
trace("INDEX_ADDRESS_LATCHED\tR[0x%06x] : R[0x%06x]\n", creg, dreg);
|
|
trace("\tCTRL &= 0x%08x |= 0x%08x\n", mask, data);
|
|
init->offset += 18;
|
|
|
|
while (count--) {
|
|
u8 iaddr = nvbios_rd08(bios, init->offset + 0);
|
|
u8 idata = nvbios_rd08(bios, init->offset + 1);
|
|
|
|
trace("\t[0x%02x] = 0x%02x\n", iaddr, idata);
|
|
init->offset += 2;
|
|
|
|
init_wr32(init, dreg, idata);
|
|
init_mask(init, creg, ~mask, data | iaddr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_IO_RESTRICT_PLL2 - opcode 0x4a
|
|
*
|
|
*/
|
|
static void
|
|
init_io_restrict_pll2(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 port = nvbios_rd16(bios, init->offset + 1);
|
|
u8 index = nvbios_rd08(bios, init->offset + 3);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 4);
|
|
u8 shift = nvbios_rd08(bios, init->offset + 5);
|
|
u8 count = nvbios_rd08(bios, init->offset + 6);
|
|
u32 reg = nvbios_rd32(bios, init->offset + 7);
|
|
u8 conf, i;
|
|
|
|
trace("IO_RESTRICT_PLL2\t"
|
|
"R[0x%06x] =PLL= ((0x%04x[0x%02x] & 0x%02x) >> 0x%02x) [{\n",
|
|
reg, port, index, mask, shift);
|
|
init->offset += 11;
|
|
|
|
conf = (init_rdvgai(init, port, index) & mask) >> shift;
|
|
for (i = 0; i < count; i++) {
|
|
u32 freq = nvbios_rd32(bios, init->offset);
|
|
if (i == conf) {
|
|
trace("\t%dkHz *\n", freq);
|
|
init_prog_pll(init, reg, freq);
|
|
} else {
|
|
trace("\t%dkHz\n", freq);
|
|
}
|
|
init->offset += 4;
|
|
}
|
|
trace("}]\n");
|
|
}
|
|
|
|
/**
|
|
* INIT_PLL2 - opcode 0x4b
|
|
*
|
|
*/
|
|
static void
|
|
init_pll2(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
|
u32 freq = nvbios_rd32(bios, init->offset + 5);
|
|
|
|
trace("PLL2\tR[0x%06x] =PLL= %dkHz\n", reg, freq);
|
|
init->offset += 9;
|
|
|
|
init_prog_pll(init, reg, freq);
|
|
}
|
|
|
|
/**
|
|
* INIT_I2C_BYTE - opcode 0x4c
|
|
*
|
|
*/
|
|
static void
|
|
init_i2c_byte(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 index = nvbios_rd08(bios, init->offset + 1);
|
|
u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1;
|
|
u8 count = nvbios_rd08(bios, init->offset + 3);
|
|
|
|
trace("I2C_BYTE\tI2C[0x%02x][0x%02x]\n", index, addr);
|
|
init->offset += 4;
|
|
|
|
while (count--) {
|
|
u8 reg = nvbios_rd08(bios, init->offset + 0);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 1);
|
|
u8 data = nvbios_rd08(bios, init->offset + 2);
|
|
int val;
|
|
|
|
trace("\t[0x%02x] &= 0x%02x |= 0x%02x\n", reg, mask, data);
|
|
init->offset += 3;
|
|
|
|
val = init_rdi2cr(init, index, addr, reg);
|
|
if (val < 0)
|
|
continue;
|
|
init_wri2cr(init, index, addr, reg, (val & mask) | data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_I2C_BYTE - opcode 0x4d
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_i2c_byte(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 index = nvbios_rd08(bios, init->offset + 1);
|
|
u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1;
|
|
u8 count = nvbios_rd08(bios, init->offset + 3);
|
|
|
|
trace("ZM_I2C_BYTE\tI2C[0x%02x][0x%02x]\n", index, addr);
|
|
init->offset += 4;
|
|
|
|
while (count--) {
|
|
u8 reg = nvbios_rd08(bios, init->offset + 0);
|
|
u8 data = nvbios_rd08(bios, init->offset + 1);
|
|
|
|
trace("\t[0x%02x] = 0x%02x\n", reg, data);
|
|
init->offset += 2;
|
|
|
|
init_wri2cr(init, index, addr, reg, data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_I2C - opcode 0x4e
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_i2c(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 index = nvbios_rd08(bios, init->offset + 1);
|
|
u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1;
|
|
u8 count = nvbios_rd08(bios, init->offset + 3);
|
|
u8 data[256], i;
|
|
|
|
trace("ZM_I2C\tI2C[0x%02x][0x%02x]\n", index, addr);
|
|
init->offset += 4;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
data[i] = nvbios_rd08(bios, init->offset);
|
|
trace("\t0x%02x\n", data[i]);
|
|
init->offset++;
|
|
}
|
|
|
|
if (init_exec(init)) {
|
|
struct i2c_adapter *adap = init_i2c(init, index);
|
|
struct i2c_msg msg = {
|
|
.addr = addr, .flags = 0, .len = count, .buf = data,
|
|
};
|
|
int ret;
|
|
|
|
if (adap && (ret = i2c_transfer(adap, &msg, 1)) != 1)
|
|
warn("i2c wr failed, %d\n", ret);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_TMDS - opcode 0x4f
|
|
*
|
|
*/
|
|
static void
|
|
init_tmds(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 tmds = nvbios_rd08(bios, init->offset + 1);
|
|
u8 addr = nvbios_rd08(bios, init->offset + 2);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 3);
|
|
u8 data = nvbios_rd08(bios, init->offset + 4);
|
|
u32 reg = init_tmds_reg(init, tmds);
|
|
|
|
trace("TMDS\tT[0x%02x][0x%02x] &= 0x%02x |= 0x%02x\n",
|
|
tmds, addr, mask, data);
|
|
init->offset += 5;
|
|
|
|
if (reg == 0)
|
|
return;
|
|
|
|
init_wr32(init, reg + 0, addr | 0x00010000);
|
|
init_wr32(init, reg + 4, data | (init_rd32(init, reg + 4) & mask));
|
|
init_wr32(init, reg + 0, addr);
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_TMDS_GROUP - opcode 0x50
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_tmds_group(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 tmds = nvbios_rd08(bios, init->offset + 1);
|
|
u8 count = nvbios_rd08(bios, init->offset + 2);
|
|
u32 reg = init_tmds_reg(init, tmds);
|
|
|
|
trace("TMDS_ZM_GROUP\tT[0x%02x]\n", tmds);
|
|
init->offset += 3;
|
|
|
|
while (count--) {
|
|
u8 addr = nvbios_rd08(bios, init->offset + 0);
|
|
u8 data = nvbios_rd08(bios, init->offset + 1);
|
|
|
|
trace("\t[0x%02x] = 0x%02x\n", addr, data);
|
|
init->offset += 2;
|
|
|
|
init_wr32(init, reg + 4, data);
|
|
init_wr32(init, reg + 0, addr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_CR_INDEX_ADDRESS_LATCHED - opcode 0x51
|
|
*
|
|
*/
|
|
static void
|
|
init_cr_idx_adr_latch(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 addr0 = nvbios_rd08(bios, init->offset + 1);
|
|
u8 addr1 = nvbios_rd08(bios, init->offset + 2);
|
|
u8 base = nvbios_rd08(bios, init->offset + 3);
|
|
u8 count = nvbios_rd08(bios, init->offset + 4);
|
|
u8 save0;
|
|
|
|
trace("CR_INDEX_ADDR C[%02x] C[%02x]\n", addr0, addr1);
|
|
init->offset += 5;
|
|
|
|
save0 = init_rdvgai(init, 0x03d4, addr0);
|
|
while (count--) {
|
|
u8 data = nvbios_rd08(bios, init->offset);
|
|
|
|
trace("\t\t[0x%02x] = 0x%02x\n", base, data);
|
|
init->offset += 1;
|
|
|
|
init_wrvgai(init, 0x03d4, addr0, base++);
|
|
init_wrvgai(init, 0x03d4, addr1, data);
|
|
}
|
|
init_wrvgai(init, 0x03d4, addr0, save0);
|
|
}
|
|
|
|
/**
|
|
* INIT_CR - opcode 0x52
|
|
*
|
|
*/
|
|
static void
|
|
init_cr(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 addr = nvbios_rd08(bios, init->offset + 1);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 2);
|
|
u8 data = nvbios_rd08(bios, init->offset + 3);
|
|
u8 val;
|
|
|
|
trace("CR\t\tC[0x%02x] &= 0x%02x |= 0x%02x\n", addr, mask, data);
|
|
init->offset += 4;
|
|
|
|
val = init_rdvgai(init, 0x03d4, addr) & mask;
|
|
init_wrvgai(init, 0x03d4, addr, val | data);
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_CR - opcode 0x53
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_cr(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 addr = nvbios_rd08(bios, init->offset + 1);
|
|
u8 data = nvbios_rd08(bios, init->offset + 2);
|
|
|
|
trace("ZM_CR\tC[0x%02x] = 0x%02x\n", addr, data);
|
|
init->offset += 3;
|
|
|
|
init_wrvgai(init, 0x03d4, addr, data);
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_CR_GROUP - opcode 0x54
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_cr_group(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 count = nvbios_rd08(bios, init->offset + 1);
|
|
|
|
trace("ZM_CR_GROUP\n");
|
|
init->offset += 2;
|
|
|
|
while (count--) {
|
|
u8 addr = nvbios_rd08(bios, init->offset + 0);
|
|
u8 data = nvbios_rd08(bios, init->offset + 1);
|
|
|
|
trace("\t\tC[0x%02x] = 0x%02x\n", addr, data);
|
|
init->offset += 2;
|
|
|
|
init_wrvgai(init, 0x03d4, addr, data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_CONDITION_TIME - opcode 0x56
|
|
*
|
|
*/
|
|
static void
|
|
init_condition_time(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
|
u8 retry = nvbios_rd08(bios, init->offset + 2);
|
|
u8 wait = min((u16)retry * 50, 100);
|
|
|
|
trace("CONDITION_TIME\t0x%02x 0x%02x\n", cond, retry);
|
|
init->offset += 3;
|
|
|
|
if (!init_exec(init))
|
|
return;
|
|
|
|
while (wait--) {
|
|
if (init_condition_met(init, cond))
|
|
return;
|
|
mdelay(20);
|
|
}
|
|
|
|
init_exec_set(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_LTIME - opcode 0x57
|
|
*
|
|
*/
|
|
static void
|
|
init_ltime(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 msec = nvbios_rd16(bios, init->offset + 1);
|
|
|
|
trace("LTIME\t0x%04x\n", msec);
|
|
init->offset += 3;
|
|
|
|
if (init_exec(init))
|
|
mdelay(msec);
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_REG_SEQUENCE - opcode 0x58
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_reg_sequence(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 base = nvbios_rd32(bios, init->offset + 1);
|
|
u8 count = nvbios_rd08(bios, init->offset + 5);
|
|
|
|
trace("ZM_REG_SEQUENCE\t0x%02x\n", count);
|
|
init->offset += 6;
|
|
|
|
while (count--) {
|
|
u32 data = nvbios_rd32(bios, init->offset);
|
|
|
|
trace("\t\tR[0x%06x] = 0x%08x\n", base, data);
|
|
init->offset += 4;
|
|
|
|
init_wr32(init, base, data);
|
|
base += 4;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_PLL_INDIRECT - opcode 0x59
|
|
*
|
|
*/
|
|
static void
|
|
init_pll_indirect(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
|
u16 addr = nvbios_rd16(bios, init->offset + 5);
|
|
u32 freq = (u32)nvbios_rd16(bios, addr) * 1000;
|
|
|
|
trace("PLL_INDIRECT\tR[0x%06x] =PLL= VBIOS[%04x] = %dkHz\n",
|
|
reg, addr, freq);
|
|
init->offset += 7;
|
|
|
|
init_prog_pll(init, reg, freq);
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_REG_INDIRECT - opcode 0x5a
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_reg_indirect(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
|
u16 addr = nvbios_rd16(bios, init->offset + 5);
|
|
u32 data = nvbios_rd32(bios, addr);
|
|
|
|
trace("ZM_REG_INDIRECT\tR[0x%06x] = VBIOS[0x%04x] = 0x%08x\n",
|
|
reg, addr, data);
|
|
init->offset += 7;
|
|
|
|
init_wr32(init, addr, data);
|
|
}
|
|
|
|
/**
|
|
* INIT_SUB_DIRECT - opcode 0x5b
|
|
*
|
|
*/
|
|
static void
|
|
init_sub_direct(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 addr = nvbios_rd16(bios, init->offset + 1);
|
|
u16 save;
|
|
|
|
trace("SUB_DIRECT\t0x%04x\n", addr);
|
|
|
|
if (init_exec(init)) {
|
|
save = init->offset;
|
|
init->offset = addr;
|
|
if (nvbios_exec(init)) {
|
|
error("error parsing sub-table\n");
|
|
return;
|
|
}
|
|
init->offset = save;
|
|
}
|
|
|
|
init->offset += 3;
|
|
}
|
|
|
|
/**
|
|
* INIT_JUMP - opcode 0x5c
|
|
*
|
|
*/
|
|
static void
|
|
init_jump(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 offset = nvbios_rd16(bios, init->offset + 1);
|
|
|
|
trace("JUMP\t0x%04x\n", offset);
|
|
|
|
if (init_exec(init))
|
|
init->offset = offset;
|
|
else
|
|
init->offset += 3;
|
|
}
|
|
|
|
/**
|
|
* INIT_I2C_IF - opcode 0x5e
|
|
*
|
|
*/
|
|
static void
|
|
init_i2c_if(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 index = nvbios_rd08(bios, init->offset + 1);
|
|
u8 addr = nvbios_rd08(bios, init->offset + 2);
|
|
u8 reg = nvbios_rd08(bios, init->offset + 3);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 4);
|
|
u8 data = nvbios_rd08(bios, init->offset + 5);
|
|
u8 value;
|
|
|
|
trace("I2C_IF\tI2C[0x%02x][0x%02x][0x%02x] & 0x%02x == 0x%02x\n",
|
|
index, addr, reg, mask, data);
|
|
init->offset += 6;
|
|
init_exec_force(init, true);
|
|
|
|
value = init_rdi2cr(init, index, addr, reg);
|
|
if ((value & mask) != data)
|
|
init_exec_set(init, false);
|
|
|
|
init_exec_force(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_COPY_NV_REG - opcode 0x5f
|
|
*
|
|
*/
|
|
static void
|
|
init_copy_nv_reg(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 sreg = nvbios_rd32(bios, init->offset + 1);
|
|
u8 shift = nvbios_rd08(bios, init->offset + 5);
|
|
u32 smask = nvbios_rd32(bios, init->offset + 6);
|
|
u32 sxor = nvbios_rd32(bios, init->offset + 10);
|
|
u32 dreg = nvbios_rd32(bios, init->offset + 14);
|
|
u32 dmask = nvbios_rd32(bios, init->offset + 18);
|
|
u32 data;
|
|
|
|
trace("COPY_NV_REG\tR[0x%06x] &= 0x%08x |= "
|
|
"((R[0x%06x] %s 0x%02x) & 0x%08x ^ 0x%08x)\n",
|
|
dreg, dmask, sreg, (shift & 0x80) ? "<<" : ">>",
|
|
(shift & 0x80) ? (0x100 - shift) : shift, smask, sxor);
|
|
init->offset += 22;
|
|
|
|
data = init_shift(init_rd32(init, sreg), shift);
|
|
init_mask(init, dreg, ~dmask, (data & smask) ^ sxor);
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_INDEX_IO - opcode 0x62
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_index_io(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 port = nvbios_rd16(bios, init->offset + 1);
|
|
u8 index = nvbios_rd08(bios, init->offset + 3);
|
|
u8 data = nvbios_rd08(bios, init->offset + 4);
|
|
|
|
trace("ZM_INDEX_IO\tI[0x%04x][0x%02x] = 0x%02x\n", port, index, data);
|
|
init->offset += 5;
|
|
|
|
init_wrvgai(init, port, index, data);
|
|
}
|
|
|
|
/**
|
|
* INIT_COMPUTE_MEM - opcode 0x63
|
|
*
|
|
*/
|
|
static void
|
|
init_compute_mem(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_devinit *devinit = init->subdev->device->devinit;
|
|
|
|
trace("COMPUTE_MEM\n");
|
|
init->offset += 1;
|
|
|
|
init_exec_force(init, true);
|
|
if (init_exec(init))
|
|
nvkm_devinit_meminit(devinit);
|
|
init_exec_force(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_RESET - opcode 0x65
|
|
*
|
|
*/
|
|
static void
|
|
init_reset(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
|
u32 data1 = nvbios_rd32(bios, init->offset + 5);
|
|
u32 data2 = nvbios_rd32(bios, init->offset + 9);
|
|
u32 savepci19;
|
|
|
|
trace("RESET\tR[0x%08x] = 0x%08x, 0x%08x", reg, data1, data2);
|
|
init->offset += 13;
|
|
init_exec_force(init, true);
|
|
|
|
savepci19 = init_mask(init, 0x00184c, 0x00000f00, 0x00000000);
|
|
init_wr32(init, reg, data1);
|
|
udelay(10);
|
|
init_wr32(init, reg, data2);
|
|
init_wr32(init, 0x00184c, savepci19);
|
|
init_mask(init, 0x001850, 0x00000001, 0x00000000);
|
|
|
|
init_exec_force(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_CONFIGURE_MEM - opcode 0x66
|
|
*
|
|
*/
|
|
static u16
|
|
init_configure_mem_clk(struct nvbios_init *init)
|
|
{
|
|
u16 mdata = bmp_mem_init_table(init->subdev->device->bios);
|
|
if (mdata)
|
|
mdata += (init_rdvgai(init, 0x03d4, 0x3c) >> 4) * 66;
|
|
return mdata;
|
|
}
|
|
|
|
static void
|
|
init_configure_mem(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 mdata, sdata;
|
|
u32 addr, data;
|
|
|
|
trace("CONFIGURE_MEM\n");
|
|
init->offset += 1;
|
|
|
|
if (bios->version.major > 2) {
|
|
init_done(init);
|
|
return;
|
|
}
|
|
init_exec_force(init, true);
|
|
|
|
mdata = init_configure_mem_clk(init);
|
|
sdata = bmp_sdr_seq_table(bios);
|
|
if (nvbios_rd08(bios, mdata) & 0x01)
|
|
sdata = bmp_ddr_seq_table(bios);
|
|
mdata += 6; /* skip to data */
|
|
|
|
data = init_rdvgai(init, 0x03c4, 0x01);
|
|
init_wrvgai(init, 0x03c4, 0x01, data | 0x20);
|
|
|
|
for (; (addr = nvbios_rd32(bios, sdata)) != 0xffffffff; sdata += 4) {
|
|
switch (addr) {
|
|
case 0x10021c: /* CKE_NORMAL */
|
|
case 0x1002d0: /* CMD_REFRESH */
|
|
case 0x1002d4: /* CMD_PRECHARGE */
|
|
data = 0x00000001;
|
|
break;
|
|
default:
|
|
data = nvbios_rd32(bios, mdata);
|
|
mdata += 4;
|
|
if (data == 0xffffffff)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
init_wr32(init, addr, data);
|
|
}
|
|
|
|
init_exec_force(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_CONFIGURE_CLK - opcode 0x67
|
|
*
|
|
*/
|
|
static void
|
|
init_configure_clk(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 mdata, clock;
|
|
|
|
trace("CONFIGURE_CLK\n");
|
|
init->offset += 1;
|
|
|
|
if (bios->version.major > 2) {
|
|
init_done(init);
|
|
return;
|
|
}
|
|
init_exec_force(init, true);
|
|
|
|
mdata = init_configure_mem_clk(init);
|
|
|
|
/* NVPLL */
|
|
clock = nvbios_rd16(bios, mdata + 4) * 10;
|
|
init_prog_pll(init, 0x680500, clock);
|
|
|
|
/* MPLL */
|
|
clock = nvbios_rd16(bios, mdata + 2) * 10;
|
|
if (nvbios_rd08(bios, mdata) & 0x01)
|
|
clock *= 2;
|
|
init_prog_pll(init, 0x680504, clock);
|
|
|
|
init_exec_force(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_CONFIGURE_PREINIT - opcode 0x68
|
|
*
|
|
*/
|
|
static void
|
|
init_configure_preinit(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 strap;
|
|
|
|
trace("CONFIGURE_PREINIT\n");
|
|
init->offset += 1;
|
|
|
|
if (bios->version.major > 2) {
|
|
init_done(init);
|
|
return;
|
|
}
|
|
init_exec_force(init, true);
|
|
|
|
strap = init_rd32(init, 0x101000);
|
|
strap = ((strap << 2) & 0xf0) | ((strap & 0x40) >> 6);
|
|
init_wrvgai(init, 0x03d4, 0x3c, strap);
|
|
|
|
init_exec_force(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_IO - opcode 0x69
|
|
*
|
|
*/
|
|
static void
|
|
init_io(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 port = nvbios_rd16(bios, init->offset + 1);
|
|
u8 mask = nvbios_rd16(bios, init->offset + 3);
|
|
u8 data = nvbios_rd16(bios, init->offset + 4);
|
|
u8 value;
|
|
|
|
trace("IO\t\tI[0x%04x] &= 0x%02x |= 0x%02x\n", port, mask, data);
|
|
init->offset += 5;
|
|
|
|
/* ummm.. yes.. should really figure out wtf this is and why it's
|
|
* needed some day.. it's almost certainly wrong, but, it also
|
|
* somehow makes things work...
|
|
*/
|
|
if (bios->subdev.device->card_type >= NV_50 &&
|
|
port == 0x03c3 && data == 0x01) {
|
|
init_mask(init, 0x614100, 0xf0800000, 0x00800000);
|
|
init_mask(init, 0x00e18c, 0x00020000, 0x00020000);
|
|
init_mask(init, 0x614900, 0xf0800000, 0x00800000);
|
|
init_mask(init, 0x000200, 0x40000000, 0x00000000);
|
|
mdelay(10);
|
|
init_mask(init, 0x00e18c, 0x00020000, 0x00000000);
|
|
init_mask(init, 0x000200, 0x40000000, 0x40000000);
|
|
init_wr32(init, 0x614100, 0x00800018);
|
|
init_wr32(init, 0x614900, 0x00800018);
|
|
mdelay(10);
|
|
init_wr32(init, 0x614100, 0x10000018);
|
|
init_wr32(init, 0x614900, 0x10000018);
|
|
}
|
|
|
|
value = init_rdport(init, port) & mask;
|
|
init_wrport(init, port, data | value);
|
|
}
|
|
|
|
/**
|
|
* INIT_SUB - opcode 0x6b
|
|
*
|
|
*/
|
|
static void
|
|
init_sub(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 index = nvbios_rd08(bios, init->offset + 1);
|
|
u16 addr, save;
|
|
|
|
trace("SUB\t0x%02x\n", index);
|
|
|
|
addr = init_script(bios, index);
|
|
if (addr && init_exec(init)) {
|
|
save = init->offset;
|
|
init->offset = addr;
|
|
if (nvbios_exec(init)) {
|
|
error("error parsing sub-table\n");
|
|
return;
|
|
}
|
|
init->offset = save;
|
|
}
|
|
|
|
init->offset += 2;
|
|
}
|
|
|
|
/**
|
|
* INIT_RAM_CONDITION - opcode 0x6d
|
|
*
|
|
*/
|
|
static void
|
|
init_ram_condition(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 mask = nvbios_rd08(bios, init->offset + 1);
|
|
u8 value = nvbios_rd08(bios, init->offset + 2);
|
|
|
|
trace("RAM_CONDITION\t"
|
|
"(R[0x100000] & 0x%02x) == 0x%02x\n", mask, value);
|
|
init->offset += 3;
|
|
|
|
if ((init_rd32(init, 0x100000) & mask) != value)
|
|
init_exec_set(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_NV_REG - opcode 0x6e
|
|
*
|
|
*/
|
|
static void
|
|
init_nv_reg(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
|
u32 mask = nvbios_rd32(bios, init->offset + 5);
|
|
u32 data = nvbios_rd32(bios, init->offset + 9);
|
|
|
|
trace("NV_REG\tR[0x%06x] &= 0x%08x |= 0x%08x\n", reg, mask, data);
|
|
init->offset += 13;
|
|
|
|
init_mask(init, reg, ~mask, data);
|
|
}
|
|
|
|
/**
|
|
* INIT_MACRO - opcode 0x6f
|
|
*
|
|
*/
|
|
static void
|
|
init_macro(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 macro = nvbios_rd08(bios, init->offset + 1);
|
|
u16 table;
|
|
|
|
trace("MACRO\t0x%02x\n", macro);
|
|
|
|
table = init_macro_table(init);
|
|
if (table) {
|
|
u32 addr = nvbios_rd32(bios, table + (macro * 8) + 0);
|
|
u32 data = nvbios_rd32(bios, table + (macro * 8) + 4);
|
|
trace("\t\tR[0x%06x] = 0x%08x\n", addr, data);
|
|
init_wr32(init, addr, data);
|
|
}
|
|
|
|
init->offset += 2;
|
|
}
|
|
|
|
/**
|
|
* INIT_RESUME - opcode 0x72
|
|
*
|
|
*/
|
|
static void
|
|
init_resume(struct nvbios_init *init)
|
|
{
|
|
trace("RESUME\n");
|
|
init->offset += 1;
|
|
init_exec_set(init, true);
|
|
}
|
|
|
|
/**
|
|
* INIT_STRAP_CONDITION - opcode 0x73
|
|
*
|
|
*/
|
|
static void
|
|
init_strap_condition(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 mask = nvbios_rd32(bios, init->offset + 1);
|
|
u32 value = nvbios_rd32(bios, init->offset + 5);
|
|
|
|
trace("STRAP_CONDITION\t(R[0x101000] & 0x%08x) == 0x%08x\n", mask, value);
|
|
init->offset += 9;
|
|
|
|
if ((init_rd32(init, 0x101000) & mask) != value)
|
|
init_exec_set(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_TIME - opcode 0x74
|
|
*
|
|
*/
|
|
static void
|
|
init_time(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 usec = nvbios_rd16(bios, init->offset + 1);
|
|
|
|
trace("TIME\t0x%04x\n", usec);
|
|
init->offset += 3;
|
|
|
|
if (init_exec(init)) {
|
|
if (usec < 1000)
|
|
udelay(usec);
|
|
else
|
|
mdelay((usec + 900) / 1000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_CONDITION - opcode 0x75
|
|
*
|
|
*/
|
|
static void
|
|
init_condition(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
|
|
|
trace("CONDITION\t0x%02x\n", cond);
|
|
init->offset += 2;
|
|
|
|
if (!init_condition_met(init, cond))
|
|
init_exec_set(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_IO_CONDITION - opcode 0x76
|
|
*
|
|
*/
|
|
static void
|
|
init_io_condition(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
|
|
|
trace("IO_CONDITION\t0x%02x\n", cond);
|
|
init->offset += 2;
|
|
|
|
if (!init_io_condition_met(init, cond))
|
|
init_exec_set(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_REG16 - opcode 0x77
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_reg16(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
|
u16 data = nvbios_rd16(bios, init->offset + 5);
|
|
|
|
trace("ZM_REG\tR[0x%06x] = 0x%04x\n", addr, data);
|
|
init->offset += 7;
|
|
|
|
init_wr32(init, addr, data);
|
|
}
|
|
|
|
/**
|
|
* INIT_INDEX_IO - opcode 0x78
|
|
*
|
|
*/
|
|
static void
|
|
init_index_io(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u16 port = nvbios_rd16(bios, init->offset + 1);
|
|
u8 index = nvbios_rd16(bios, init->offset + 3);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 4);
|
|
u8 data = nvbios_rd08(bios, init->offset + 5);
|
|
u8 value;
|
|
|
|
trace("INDEX_IO\tI[0x%04x][0x%02x] &= 0x%02x |= 0x%02x\n",
|
|
port, index, mask, data);
|
|
init->offset += 6;
|
|
|
|
value = init_rdvgai(init, port, index) & mask;
|
|
init_wrvgai(init, port, index, data | value);
|
|
}
|
|
|
|
/**
|
|
* INIT_PLL - opcode 0x79
|
|
*
|
|
*/
|
|
static void
|
|
init_pll(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
|
u32 freq = nvbios_rd16(bios, init->offset + 5) * 10;
|
|
|
|
trace("PLL\tR[0x%06x] =PLL= %dkHz\n", reg, freq);
|
|
init->offset += 7;
|
|
|
|
init_prog_pll(init, reg, freq);
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_REG - opcode 0x7a
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_reg(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
|
u32 data = nvbios_rd32(bios, init->offset + 5);
|
|
|
|
trace("ZM_REG\tR[0x%06x] = 0x%08x\n", addr, data);
|
|
init->offset += 9;
|
|
|
|
if (addr == 0x000200)
|
|
data |= 0x00000001;
|
|
|
|
init_wr32(init, addr, data);
|
|
}
|
|
|
|
/**
|
|
* INIT_RAM_RESTRICT_PLL - opcde 0x87
|
|
*
|
|
*/
|
|
static void
|
|
init_ram_restrict_pll(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 type = nvbios_rd08(bios, init->offset + 1);
|
|
u8 count = init_ram_restrict_group_count(init);
|
|
u8 strap = init_ram_restrict(init);
|
|
u8 cconf;
|
|
|
|
trace("RAM_RESTRICT_PLL\t0x%02x\n", type);
|
|
init->offset += 2;
|
|
|
|
for (cconf = 0; cconf < count; cconf++) {
|
|
u32 freq = nvbios_rd32(bios, init->offset);
|
|
|
|
if (cconf == strap) {
|
|
trace("%dkHz *\n", freq);
|
|
init_prog_pll(init, type, freq);
|
|
} else {
|
|
trace("%dkHz\n", freq);
|
|
}
|
|
|
|
init->offset += 4;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_RESET_BEGUN - opcode 0x8c
|
|
*
|
|
*/
|
|
static void
|
|
init_reset_begun(struct nvbios_init *init)
|
|
{
|
|
trace("RESET_BEGUN\n");
|
|
init->offset += 1;
|
|
}
|
|
|
|
/**
|
|
* INIT_RESET_END - opcode 0x8d
|
|
*
|
|
*/
|
|
static void
|
|
init_reset_end(struct nvbios_init *init)
|
|
{
|
|
trace("RESET_END\n");
|
|
init->offset += 1;
|
|
}
|
|
|
|
/**
|
|
* INIT_GPIO - opcode 0x8e
|
|
*
|
|
*/
|
|
static void
|
|
init_gpio(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_gpio *gpio = init->subdev->device->gpio;
|
|
|
|
trace("GPIO\n");
|
|
init->offset += 1;
|
|
|
|
if (init_exec(init))
|
|
nvkm_gpio_reset(gpio, DCB_GPIO_UNUSED);
|
|
}
|
|
|
|
/**
|
|
* INIT_RAM_RESTRICT_ZM_GROUP - opcode 0x8f
|
|
*
|
|
*/
|
|
static void
|
|
init_ram_restrict_zm_reg_group(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
|
u8 incr = nvbios_rd08(bios, init->offset + 5);
|
|
u8 num = nvbios_rd08(bios, init->offset + 6);
|
|
u8 count = init_ram_restrict_group_count(init);
|
|
u8 index = init_ram_restrict(init);
|
|
u8 i, j;
|
|
|
|
trace("RAM_RESTRICT_ZM_REG_GROUP\t"
|
|
"R[0x%08x] 0x%02x 0x%02x\n", addr, incr, num);
|
|
init->offset += 7;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
trace("\tR[0x%06x] = {\n", addr);
|
|
for (j = 0; j < count; j++) {
|
|
u32 data = nvbios_rd32(bios, init->offset);
|
|
|
|
if (j == index) {
|
|
trace("\t\t0x%08x *\n", data);
|
|
init_wr32(init, addr, data);
|
|
} else {
|
|
trace("\t\t0x%08x\n", data);
|
|
}
|
|
|
|
init->offset += 4;
|
|
}
|
|
trace("\t}\n");
|
|
addr += incr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_COPY_ZM_REG - opcode 0x90
|
|
*
|
|
*/
|
|
static void
|
|
init_copy_zm_reg(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 sreg = nvbios_rd32(bios, init->offset + 1);
|
|
u32 dreg = nvbios_rd32(bios, init->offset + 5);
|
|
|
|
trace("COPY_ZM_REG\tR[0x%06x] = R[0x%06x]\n", dreg, sreg);
|
|
init->offset += 9;
|
|
|
|
init_wr32(init, dreg, init_rd32(init, sreg));
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_REG_GROUP - opcode 0x91
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_reg_group(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
|
u8 count = nvbios_rd08(bios, init->offset + 5);
|
|
|
|
trace("ZM_REG_GROUP\tR[0x%06x] =\n", addr);
|
|
init->offset += 6;
|
|
|
|
while (count--) {
|
|
u32 data = nvbios_rd32(bios, init->offset);
|
|
trace("\t0x%08x\n", data);
|
|
init_wr32(init, addr, data);
|
|
init->offset += 4;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_XLAT - opcode 0x96
|
|
*
|
|
*/
|
|
static void
|
|
init_xlat(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 saddr = nvbios_rd32(bios, init->offset + 1);
|
|
u8 sshift = nvbios_rd08(bios, init->offset + 5);
|
|
u8 smask = nvbios_rd08(bios, init->offset + 6);
|
|
u8 index = nvbios_rd08(bios, init->offset + 7);
|
|
u32 daddr = nvbios_rd32(bios, init->offset + 8);
|
|
u32 dmask = nvbios_rd32(bios, init->offset + 12);
|
|
u8 shift = nvbios_rd08(bios, init->offset + 16);
|
|
u32 data;
|
|
|
|
trace("INIT_XLAT\tR[0x%06x] &= 0x%08x |= "
|
|
"(X%02x((R[0x%06x] %s 0x%02x) & 0x%02x) << 0x%02x)\n",
|
|
daddr, dmask, index, saddr, (sshift & 0x80) ? "<<" : ">>",
|
|
(sshift & 0x80) ? (0x100 - sshift) : sshift, smask, shift);
|
|
init->offset += 17;
|
|
|
|
data = init_shift(init_rd32(init, saddr), sshift) & smask;
|
|
data = init_xlat_(init, index, data) << shift;
|
|
init_mask(init, daddr, ~dmask, data);
|
|
}
|
|
|
|
/**
|
|
* INIT_ZM_MASK_ADD - opcode 0x97
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_mask_add(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
|
u32 mask = nvbios_rd32(bios, init->offset + 5);
|
|
u32 add = nvbios_rd32(bios, init->offset + 9);
|
|
u32 data;
|
|
|
|
trace("ZM_MASK_ADD\tR[0x%06x] &= 0x%08x += 0x%08x\n", addr, mask, add);
|
|
init->offset += 13;
|
|
|
|
data = init_rd32(init, addr);
|
|
data = (data & mask) | ((data + add) & ~mask);
|
|
init_wr32(init, addr, data);
|
|
}
|
|
|
|
/**
|
|
* INIT_AUXCH - opcode 0x98
|
|
*
|
|
*/
|
|
static void
|
|
init_auxch(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
|
u8 count = nvbios_rd08(bios, init->offset + 5);
|
|
|
|
trace("AUXCH\tAUX[0x%08x] 0x%02x\n", addr, count);
|
|
init->offset += 6;
|
|
|
|
while (count--) {
|
|
u8 mask = nvbios_rd08(bios, init->offset + 0);
|
|
u8 data = nvbios_rd08(bios, init->offset + 1);
|
|
trace("\tAUX[0x%08x] &= 0x%02x |= 0x%02x\n", addr, mask, data);
|
|
mask = init_rdauxr(init, addr) & mask;
|
|
init_wrauxr(init, addr, mask | data);
|
|
init->offset += 2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_AUXCH - opcode 0x99
|
|
*
|
|
*/
|
|
static void
|
|
init_zm_auxch(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
|
u8 count = nvbios_rd08(bios, init->offset + 5);
|
|
|
|
trace("ZM_AUXCH\tAUX[0x%08x] 0x%02x\n", addr, count);
|
|
init->offset += 6;
|
|
|
|
while (count--) {
|
|
u8 data = nvbios_rd08(bios, init->offset + 0);
|
|
trace("\tAUX[0x%08x] = 0x%02x\n", addr, data);
|
|
init_wrauxr(init, addr, data);
|
|
init->offset += 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INIT_I2C_LONG_IF - opcode 0x9a
|
|
*
|
|
*/
|
|
static void
|
|
init_i2c_long_if(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
u8 index = nvbios_rd08(bios, init->offset + 1);
|
|
u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1;
|
|
u8 reglo = nvbios_rd08(bios, init->offset + 3);
|
|
u8 reghi = nvbios_rd08(bios, init->offset + 4);
|
|
u8 mask = nvbios_rd08(bios, init->offset + 5);
|
|
u8 data = nvbios_rd08(bios, init->offset + 6);
|
|
struct i2c_adapter *adap;
|
|
|
|
trace("I2C_LONG_IF\t"
|
|
"I2C[0x%02x][0x%02x][0x%02x%02x] & 0x%02x == 0x%02x\n",
|
|
index, addr, reglo, reghi, mask, data);
|
|
init->offset += 7;
|
|
|
|
adap = init_i2c(init, index);
|
|
if (adap) {
|
|
u8 i[2] = { reghi, reglo };
|
|
u8 o[1] = {};
|
|
struct i2c_msg msg[] = {
|
|
{ .addr = addr, .flags = 0, .len = 2, .buf = i },
|
|
{ .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = o }
|
|
};
|
|
int ret;
|
|
|
|
ret = i2c_transfer(adap, msg, 2);
|
|
if (ret == 2 && ((o[0] & mask) == data))
|
|
return;
|
|
}
|
|
|
|
init_exec_set(init, false);
|
|
}
|
|
|
|
/**
|
|
* INIT_GPIO_NE - opcode 0xa9
|
|
*
|
|
*/
|
|
static void
|
|
init_gpio_ne(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
struct nvkm_gpio *gpio = bios->subdev.device->gpio;
|
|
struct dcb_gpio_func func;
|
|
u8 count = nvbios_rd08(bios, init->offset + 1);
|
|
u8 idx = 0, ver, len;
|
|
u16 data, i;
|
|
|
|
trace("GPIO_NE\t");
|
|
init->offset += 2;
|
|
|
|
for (i = init->offset; i < init->offset + count; i++)
|
|
cont("0x%02x ", nvbios_rd08(bios, i));
|
|
cont("\n");
|
|
|
|
while ((data = dcb_gpio_parse(bios, 0, idx++, &ver, &len, &func))) {
|
|
if (func.func != DCB_GPIO_UNUSED) {
|
|
for (i = init->offset; i < init->offset + count; i++) {
|
|
if (func.func == nvbios_rd08(bios, i))
|
|
break;
|
|
}
|
|
|
|
trace("\tFUNC[0x%02x]", func.func);
|
|
if (i == (init->offset + count)) {
|
|
cont(" *");
|
|
if (init_exec(init))
|
|
nvkm_gpio_reset(gpio, func.func);
|
|
}
|
|
cont("\n");
|
|
}
|
|
}
|
|
|
|
init->offset += count;
|
|
}
|
|
|
|
static struct nvbios_init_opcode {
|
|
void (*exec)(struct nvbios_init *);
|
|
} init_opcode[] = {
|
|
[0x32] = { init_io_restrict_prog },
|
|
[0x33] = { init_repeat },
|
|
[0x34] = { init_io_restrict_pll },
|
|
[0x36] = { init_end_repeat },
|
|
[0x37] = { init_copy },
|
|
[0x38] = { init_not },
|
|
[0x39] = { init_io_flag_condition },
|
|
[0x3a] = { init_generic_condition },
|
|
[0x3b] = { init_io_mask_or },
|
|
[0x3c] = { init_io_or },
|
|
[0x47] = { init_andn_reg },
|
|
[0x48] = { init_or_reg },
|
|
[0x49] = { init_idx_addr_latched },
|
|
[0x4a] = { init_io_restrict_pll2 },
|
|
[0x4b] = { init_pll2 },
|
|
[0x4c] = { init_i2c_byte },
|
|
[0x4d] = { init_zm_i2c_byte },
|
|
[0x4e] = { init_zm_i2c },
|
|
[0x4f] = { init_tmds },
|
|
[0x50] = { init_zm_tmds_group },
|
|
[0x51] = { init_cr_idx_adr_latch },
|
|
[0x52] = { init_cr },
|
|
[0x53] = { init_zm_cr },
|
|
[0x54] = { init_zm_cr_group },
|
|
[0x56] = { init_condition_time },
|
|
[0x57] = { init_ltime },
|
|
[0x58] = { init_zm_reg_sequence },
|
|
[0x59] = { init_pll_indirect },
|
|
[0x5a] = { init_zm_reg_indirect },
|
|
[0x5b] = { init_sub_direct },
|
|
[0x5c] = { init_jump },
|
|
[0x5e] = { init_i2c_if },
|
|
[0x5f] = { init_copy_nv_reg },
|
|
[0x62] = { init_zm_index_io },
|
|
[0x63] = { init_compute_mem },
|
|
[0x65] = { init_reset },
|
|
[0x66] = { init_configure_mem },
|
|
[0x67] = { init_configure_clk },
|
|
[0x68] = { init_configure_preinit },
|
|
[0x69] = { init_io },
|
|
[0x6b] = { init_sub },
|
|
[0x6d] = { init_ram_condition },
|
|
[0x6e] = { init_nv_reg },
|
|
[0x6f] = { init_macro },
|
|
[0x71] = { init_done },
|
|
[0x72] = { init_resume },
|
|
[0x73] = { init_strap_condition },
|
|
[0x74] = { init_time },
|
|
[0x75] = { init_condition },
|
|
[0x76] = { init_io_condition },
|
|
[0x77] = { init_zm_reg16 },
|
|
[0x78] = { init_index_io },
|
|
[0x79] = { init_pll },
|
|
[0x7a] = { init_zm_reg },
|
|
[0x87] = { init_ram_restrict_pll },
|
|
[0x8c] = { init_reset_begun },
|
|
[0x8d] = { init_reset_end },
|
|
[0x8e] = { init_gpio },
|
|
[0x8f] = { init_ram_restrict_zm_reg_group },
|
|
[0x90] = { init_copy_zm_reg },
|
|
[0x91] = { init_zm_reg_group },
|
|
[0x92] = { init_reserved },
|
|
[0x96] = { init_xlat },
|
|
[0x97] = { init_zm_mask_add },
|
|
[0x98] = { init_auxch },
|
|
[0x99] = { init_zm_auxch },
|
|
[0x9a] = { init_i2c_long_if },
|
|
[0xa9] = { init_gpio_ne },
|
|
[0xaa] = { init_reserved },
|
|
};
|
|
|
|
int
|
|
nvbios_exec(struct nvbios_init *init)
|
|
{
|
|
struct nvkm_bios *bios = init->subdev->device->bios;
|
|
|
|
init->nested++;
|
|
while (init->offset) {
|
|
u8 opcode = nvbios_rd08(bios, init->offset);
|
|
if (opcode >= ARRAY_SIZE(init_opcode) ||
|
|
!init_opcode[opcode].exec) {
|
|
error("unknown opcode 0x%02x\n", opcode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
init_opcode[opcode].exec(init);
|
|
}
|
|
init->nested--;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
nvbios_post(struct nvkm_subdev *subdev, bool execute)
|
|
{
|
|
struct nvkm_bios *bios = subdev->device->bios;
|
|
int ret = 0;
|
|
int i = -1;
|
|
u16 data;
|
|
|
|
if (execute)
|
|
nvkm_debug(subdev, "running init tables\n");
|
|
while (!ret && (data = (init_script(bios, ++i)))) {
|
|
ret = nvbios_init(subdev, data,
|
|
init.execute = execute ? 1 : 0;
|
|
);
|
|
}
|
|
|
|
/* the vbios parser will run this right after the normal init
|
|
* tables, whereas the binary driver appears to run it later.
|
|
*/
|
|
if (!ret && (data = init_unknown_script(bios))) {
|
|
ret = nvbios_init(subdev, data,
|
|
init.execute = execute ? 1 : 0;
|
|
);
|
|
}
|
|
|
|
return ret;
|
|
}
|