diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2012-07-11 10:44:20 +1000 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-10-03 13:12:47 +1000 |
commit | cb75d97e9c77743ecfcc43375be135a55a4d9b25 (patch) | |
tree | e7903f00db28b25a88d295dcc863f14b3d3bd707 /drivers/gpu/drm/nouveau/nouveau_bios.c | |
parent | 70790f4f819875e8f390871fd15bbbf823f28e1b (diff) |
drm/nouveau: implement devinit subdev, and new init table parser
v2:
- make sure not to execute display scripts unless resuming
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_bios.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_bios.c | 3535 |
1 files changed, 61 insertions, 3474 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 98eaac9da30e..e7abde138e27 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -64,3372 +64,6 @@ static bool nv_cksum(const uint8_t *data, unsigned int length) return false; } -struct init_tbl_entry { - char *name; - uint8_t id; - /* Return: - * > 0: success, length of opcode - * 0: success, but abort further parsing of table (INIT_DONE etc) - * < 0: failure, table parsing will be aborted - */ - int (*handler)(struct nvbios *, uint16_t, struct init_exec *); -}; - -static int parse_init_table(struct nvbios *, uint16_t, struct init_exec *); - -#define MACRO_INDEX_SIZE 2 -#define MACRO_SIZE 8 -#define CONDITION_SIZE 12 -#define IO_FLAG_CONDITION_SIZE 9 -#define IO_CONDITION_SIZE 5 -#define MEM_INIT_SIZE 66 - -static void still_alive(void) -{ -#if 0 - sync(); - mdelay(2); -#endif -} - -static uint32_t -munge_reg(struct nvbios *bios, uint32_t reg) -{ - struct drm_nouveau_private *dev_priv = bios->dev->dev_private; - struct dcb_entry *dcbent = bios->display.output; - - if (dev_priv->card_type < NV_50) - return reg; - - if (reg & 0x80000000) { - BUG_ON(bios->display.crtc < 0); - reg += bios->display.crtc * 0x800; - } - - if (reg & 0x40000000) { - BUG_ON(!dcbent); - - reg += (ffs(dcbent->or) - 1) * 0x800; - if ((reg & 0x20000000) && !(dcbent->sorconf.link & 1)) - reg += 0x00000080; - } - - reg &= ~0xe0000000; - return reg; -} - -static int -valid_reg(struct nvbios *bios, uint32_t reg) -{ - struct drm_nouveau_private *dev_priv = bios->dev->dev_private; - struct drm_device *dev = bios->dev; - - /* C51 has misaligned regs on purpose. Marvellous */ - if (reg & 0x2 || - (reg & 0x1 && dev_priv->vbios.chip_version != 0x51)) - NV_ERROR(dev, "======= misaligned reg 0x%08X =======\n", reg); - - /* warn on C51 regs that haven't been verified accessible in tracing */ - if (reg & 0x1 && dev_priv->vbios.chip_version == 0x51 && - reg != 0x130d && reg != 0x1311 && reg != 0x60081d) - NV_WARN(dev, "=== C51 misaligned reg 0x%08X not verified ===\n", - reg); - - if (reg >= (8*1024*1024)) { - NV_ERROR(dev, "=== reg 0x%08x out of mapped bounds ===\n", reg); - return 0; - } - - return 1; -} - -static bool -valid_idx_port(struct nvbios *bios, uint16_t port) -{ - struct drm_nouveau_private *dev_priv = bios->dev->dev_private; - struct drm_device *dev = bios->dev; - - /* - * If adding more ports here, the read/write functions below will need - * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is - * used for the port in question - */ - if (dev_priv->card_type < NV_50) { - if (port == NV_CIO_CRX__COLOR) - return true; - if (port == NV_VIO_SRX) - return true; - } else { - if (port == NV_CIO_CRX__COLOR) - return true; - } - - NV_ERROR(dev, "========== unknown indexed io port 0x%04X ==========\n", - port); - - return false; -} - -static bool -valid_port(struct nvbios *bios, uint16_t port) -{ - struct drm_device *dev = bios->dev; - - /* - * If adding more ports here, the read/write functions below will need - * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is - * used for the port in question - */ - if (port == NV_VIO_VSE2) - return true; - - NV_ERROR(dev, "========== unknown io port 0x%04X ==========\n", port); - - return false; -} - -static uint32_t -bios_rd32(struct nvbios *bios, uint32_t reg) -{ - uint32_t data; - - reg = munge_reg(bios, reg); - if (!valid_reg(bios, reg)) - return 0; - - /* - * C51 sometimes uses regs with bit0 set in the address. For these - * cases there should exist a translation in a BIOS table to an IO - * port address which the BIOS uses for accessing the reg - * - * These only seem to appear for the power control regs to a flat panel, - * and the GPIO regs at 0x60081*. In C51 mmio traces the normal regs - * for 0x1308 and 0x1310 are used - hence the mask below. An S3 - * suspend-resume mmio trace from a C51 will be required to see if this - * is true for the power microcode in 0x14.., or whether the direct IO - * port access method is needed - */ - if (reg & 0x1) - reg &= ~0x1; - - data = nv_rd32(bios->dev, reg); - - BIOSLOG(bios, " Read: Reg: 0x%08X, Data: 0x%08X\n", reg, data); - - return data; -} - -static void -bios_wr32(struct nvbios *bios, uint32_t reg, uint32_t data) -{ - struct drm_nouveau_private *dev_priv = bios->dev->dev_private; - - reg = munge_reg(bios, reg); - if (!valid_reg(bios, reg)) - return; - - /* see note in bios_rd32 */ - if (reg & 0x1) - reg &= 0xfffffffe; - - LOG_OLD_VALUE(bios_rd32(bios, reg)); - BIOSLOG(bios, " Write: Reg: 0x%08X, Data: 0x%08X\n", reg, data); - - if (dev_priv->vbios.execute) { - still_alive(); - nv_wr32(bios->dev, reg, data); - } -} - -static uint8_t -bios_idxprt_rd(struct nvbios *bios, uint16_t port, uint8_t index) -{ - struct drm_nouveau_private *dev_priv = bios->dev->dev_private; - struct drm_device *dev = bios->dev; - uint8_t data; - - if (!valid_idx_port(bios, port)) - return 0; - - if (dev_priv->card_type < NV_50) { - if (port == NV_VIO_SRX) - data = NVReadVgaSeq(dev, bios->state.crtchead, index); - else /* assume NV_CIO_CRX__COLOR */ - data = NVReadVgaCrtc(dev, bios->state.crtchead, index); - } else { - uint32_t data32; - - data32 = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3)); - data = (data32 >> ((index & 3) << 3)) & 0xff; - } - - BIOSLOG(bios, " Indexed IO read: Port: 0x%04X, Index: 0x%02X, " - "Head: 0x%02X, Data: 0x%02X\n", - port, index, bios->state.crtchead, data); - return data; -} - -static void -bios_idxprt_wr(struct nvbios *bios, uint16_t port, uint8_t index, uint8_t data) -{ - struct drm_nouveau_private *dev_priv = bios->dev->dev_private; - struct drm_device *dev = bios->dev; - - if (!valid_idx_port(bios, port)) - return; - - /* - * The current head is maintained in the nvbios member state.crtchead. - * We trap changes to CR44 and update the head variable and hence the - * register set written. - * As CR44 only exists on CRTC0, we update crtchead to head0 in advance - * of the write, and to head1 after the write - */ - if (port == NV_CIO_CRX__COLOR && index == NV_CIO_CRE_44 && - data != NV_CIO_CRE_44_HEADB) - bios->state.crtchead = 0; - - LOG_OLD_VALUE(bios_idxprt_rd(bios, port, index)); - BIOSLOG(bios, " Indexed IO write: Port: 0x%04X, Index: 0x%02X, " - "Head: 0x%02X, Data: 0x%02X\n", - port, index, bios->state.crtchead, data); - - if (bios->execute && dev_priv->card_type < NV_50) { - still_alive(); - if (port == NV_VIO_SRX) - NVWriteVgaSeq(dev, bios->state.crtchead, index, data); - else /* assume NV_CIO_CRX__COLOR */ - NVWriteVgaCrtc(dev, bios->state.crtchead, index, data); - } else - if (bios->execute) { - uint32_t data32, shift = (index & 3) << 3; - - still_alive(); - - data32 = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3)); - data32 &= ~(0xff << shift); - data32 |= (data << shift); - bios_wr32(bios, NV50_PDISPLAY_VGACRTC(index & ~3), data32); - } - - if (port == NV_CIO_CRX__COLOR && - index == NV_CIO_CRE_44 && data == NV_CIO_CRE_44_HEADB) - bios->state.crtchead = 1; -} - -static uint8_t -bios_port_rd(struct nvbios *bios, uint16_t port) -{ - uint8_t data, head = bios->state.crtchead; - - if (!valid_port(bios, port)) - return 0; - - data = NVReadPRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port); - - BIOSLOG(bios, " IO read: Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n", - port, head, data); - - return data; -} - -static void -bios_port_wr(struct nvbios *bios, uint16_t port, uint8_t data) -{ - int head = bios->state.crtchead; - - if (!valid_port(bios, port)) - return; - - LOG_OLD_VALUE(bios_port_rd(bios, port)); - BIOSLOG(bios, " IO write: Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n", - port, head, data); - - if (!bios->execute) - return; - - still_alive(); - NVWritePRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port, data); -} - -static bool -io_flag_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond) -{ - /* - * The IO flag condition entry has 2 bytes for the CRTC port; 1 byte - * for the CRTC index; 1 byte for the mask to apply to the value - * retrieved from the CRTC; 1 byte for the shift right to apply to the - * masked CRTC value; 2 bytes for the offset to the flag array, to - * which the shifted value is added; 1 byte for the mask applied to the - * value read from the flag array; and 1 byte for the value to compare - * against the masked byte from the flag table. - */ - - uint16_t condptr = bios->io_flag_condition_tbl_ptr + cond * IO_FLAG_CONDITION_SIZE; - uint16_t crtcport = ROM16(bios->data[condptr]); - uint8_t crtcindex = bios->data[condptr + 2]; - uint8_t mask = bios->data[condptr + 3]; - uint8_t shift = bios->data[condptr + 4]; - uint16_t flagarray = ROM16(bios->data[condptr + 5]); - uint8_t flagarraymask = bios->data[condptr + 7]; - uint8_t cmpval = bios->data[condptr + 8]; - uint8_t data; - - BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, " - "Shift: 0x%02X, FlagArray: 0x%04X, FAMask: 0x%02X, " - "Cmpval: 0x%02X\n", - offset, crtcport, crtcindex, mask, shift, flagarray, flagarraymask, cmpval); - - data = bios_idxprt_rd(bios, crtcport, crtcindex); - - data = bios->data[flagarray + ((data & mask) >> shift)]; - data &= flagarraymask; - - BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n", - offset, data, cmpval); - - return (data == cmpval); -} - -static bool -bios_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond) -{ - /* - * The condition table entry has 4 bytes for the address of the - * register to check, 4 bytes for a mask to apply to the register and - * 4 for a test comparison value - */ - - uint16_t condptr = bios->condition_tbl_ptr + cond * CONDITION_SIZE; - uint32_t reg = ROM32(bios->data[condptr]); - uint32_t mask = ROM32(bios->data[condptr + 4]); - uint32_t cmpval = ROM32(bios->data[condptr + 8]); - uint32_t data; - - BIOSLOG(bios, "0x%04X: Cond: 0x%02X, Reg: 0x%08X, Mask: 0x%08X\n", - offset, cond, reg, mask); - - data = bios_rd32(bios, reg) & mask; - - BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n", - offset, data, cmpval); - - return (data == cmpval); -} - -static bool -io_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond) -{ - /* - * The IO condition entry has 2 bytes for the IO port address; 1 byte - * for the index to write to io_port; 1 byte for the mask to apply to - * the byte read from io_port+1; and 1 byte for the value to compare - * against the masked byte. - */ - - uint16_t condptr = bios->io_condition_tbl_ptr + cond * IO_CONDITION_SIZE; - uint16_t io_port = ROM16(bios->data[condptr]); - uint8_t port_index = bios->data[condptr + 2]; - uint8_t mask = bios->data[condptr + 3]; - uint8_t cmpval = bios->data[condptr + 4]; - - uint8_t data = bios_idxprt_rd(bios, io_port, port_index) & mask; - - BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n", - offset, data, cmpval); - - return (data == cmpval); -} - -static int dcb_entry_idx_from_crtchead(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - - /* - * For the results of this function to be correct, CR44 must have been - * set (using bios_idxprt_wr to set crtchead), CR58 set for CR57 = 0, - * and the DCB table parsed, before the script calling the function is - * run. run_digital_op_script is example of how to do such setup - */ - - uint8_t dcb_entry = NVReadVgaCrtc5758(dev, bios->state.crtchead, 0); - - if (dcb_entry > bios->dcb.entries) { - NV_ERROR(dev, "CR58 doesn't have a valid DCB entry currently " - "(%02X)\n", dcb_entry); - dcb_entry = 0x7f; /* unused / invalid marker */ - } - - return dcb_entry; -} - -static struct nouveau_i2c_port * -init_i2c_device_find(struct drm_device *dev, int i2c_index) -{ - if (i2c_index == 0xff) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_table *dcb = &dev_priv->vbios.dcb; - /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */ - int idx = dcb_entry_idx_from_crtchead(dev); - - i2c_index = 0x80; //NV_I2C_DEFAULT(0); - if (idx != 0x7f && dcb->entry[idx].i2c_upper_default) - i2c_index = 0x81; //NV_I2C_DEFAULT(1); - } - - return nouveau_i2c_find(dev, i2c_index); -} - -static uint32_t -get_tmds_index_reg(struct drm_device *dev, uint8_t mlv) -{ - /* - * 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. - */ - - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - const int pramdac_offset[13] = { - 0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 }; - const uint32_t pramdac_table[4] = { - 0x6808b0, 0x6808b8, 0x6828b0, 0x6828b8 }; - - if (mlv >= 0x80) { - int dcb_entry, dacoffset; - - /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */ - dcb_entry = dcb_entry_idx_from_crtchead(dev); - if (dcb_entry == 0x7f) - return 0; - dacoffset = pramdac_offset[bios->dcb.entry[dcb_entry].or]; - if (mlv == 0x81) - dacoffset ^= 8; - return 0x6808b0 + dacoffset; - } else { - if (mlv >= ARRAY_SIZE(pramdac_table)) { - NV_ERROR(dev, "Magic Lookup Value too big (%02X)\n", - mlv); - return 0; - } - return pramdac_table[mlv]; - } -} - -static int -init_io_restrict_prog(struct nvbios *bios, uint16_t offset, - struct init_exec *iexec) -{ - /* - * INIT_IO_RESTRICT_PROG opcode: 0x32 ('2') - * - * offset (8 bit): opcode - * offset + 1 (16 bit): CRTC port - * offset + 3 (8 bit): CRTC index - * offset + 4 (8 bit): mask - * offset + 5 (8 bit): shift - * offset + 6 (8 bit): count - * offset + 7 (32 bit): register - * offset + 11 (32 bit): configuration 1 - * ... - * - * Starting at offset + 11 there are "count" 32 bit values. - * To find out which value to use read index "CRTC index" on "CRTC - * port", AND this value with "mask" and then bit shift right "shift" - * bits. Read the appropriate value using this index and write to - * "register" - */ - - uint16_t crtcport = ROM16(bios->data[offset + 1]); - uint8_t crtcindex = bios->data[offset + 3]; - uint8_t mask = bios->data[offset + 4]; - uint8_t shift = bios->data[offset + 5]; - uint8_t count = bios->data[offset + 6]; - uint32_t reg = ROM32(bios->data[offset + 7]); - uint8_t config; - uint32_t configval; - int len = 11 + count * 4; - - if (!iexec->execute) - return len; - - BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, " - "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n", - offset, crtcport, crtcindex, mask, shift, count, reg); - - config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift; - if (config > count) { - NV_ERROR(bios->dev, - "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n", - offset, config, count); - return len; - } - - configval = ROM32(bios->data[offset + 11 + config * 4]); - - BIOSLOG(bios, "0x%04X: Writing config %02X\n", offset, config); - - bios_wr32(bios, reg, configval); - - return len; -} - -static int -init_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_REPEAT opcode: 0x33 ('3') - * - * offset (8 bit): opcode - * offset + 1 (8 bit): count - * - * Execute script following this opcode up to INIT_REPEAT_END - * "count" times - */ - - uint8_t count = bios->data[offset + 1]; - uint8_t i; - - /* no iexec->execute check by design */ - - BIOSLOG(bios, "0x%04X: Repeating following segment %d times\n", - offset, count); - - iexec->repeat = true; - - /* - * count - 1, as the script block will execute once when we leave this - * opcode -- this is compatible with bios behaviour as: - * a) the block is always executed at least once, even if count == 0 - * b) the bios interpreter skips to the op following INIT_END_REPEAT, - * while we don't - */ - for (i = 0; i < count - 1; i++) - parse_init_table(bios, offset + 2, iexec); - - iexec->repeat = false; - - return 2; -} - -static int -init_io_restrict_pll(struct nvbios *bios, uint16_t offset, - struct init_exec *iexec) -{ - /* - * INIT_IO_RESTRICT_PLL opcode: 0x34 ('4') - * - * offset (8 bit): opcode - * offset + 1 (16 bit): CRTC port - * offset + 3 (8 bit): CRTC index - * offset + 4 (8 bit): mask - * offset + 5 (8 bit): shift - * offset + 6 (8 bit): IO flag condition index - * offset + 7 (8 bit): count - * offset + 8 (32 bit): register - * offset + 12 (16 bit): frequency 1 - * ... - * - * Starting at offset + 12 there are "count" 16 bit frequencies (10kHz). - * Set PLL register "register" to coefficients for frequency n, - * selected by reading index "CRTC index" of "CRTC port" ANDed with - * "mask" and shifted right by "shift". - * - * If "IO flag condition index" > 0, and condition met, double - * frequency before setting it. - */ - - uint16_t crtcport = ROM16(bios->data[offset + 1]); - uint8_t crtcindex = bios->data[offset + 3]; - uint8_t mask = bios->data[offset + 4]; - uint8_t shift = bios->data[offset + 5]; - int8_t io_flag_condition_idx = bios->data[offset + 6]; - uint8_t count = bios->data[offset + 7]; - uint32_t reg = ROM32(bios->data[offset + 8]); - uint8_t config; - uint16_t freq; - int len = 12 + count * 2; - - if (!iexec->execute) - return len; - - BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, " - "Shift: 0x%02X, IO Flag Condition: 0x%02X, " - "Count: 0x%02X, Reg: 0x%08X\n", - offset, crtcport, crtcindex, mask, shift, - io_flag_condition_idx, count, reg); - - config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift; - if (config > count) { - NV_ERROR(bios->dev, - "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n", - offset, config, count); - return len; - } - - freq = ROM16(bios->data[offset + 12 + config * 2]); - - if (io_flag_condition_idx > 0) { - if (io_flag_condition_met(bios, offset, io_flag_condition_idx)) { - BIOSLOG(bios, "0x%04X: Condition fulfilled -- " - "frequency doubled\n", offset); - freq *= 2; - } else - BIOSLOG(bios, "0x%04X: Condition not fulfilled -- " - "frequency unchanged\n", offset); - } - - BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %d0kHz\n", - offset, reg, config, freq); - - setPLL(bios->dev, reg, freq * 10); - - return len; -} - -static int -init_end_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_END_REPEAT opcode: 0x36 ('6') - * - * offset (8 bit): opcode - * - * Marks the end of the block for INIT_REPEAT to repeat - */ - - /* no iexec->execute check by design */ - - /* - * iexec->repeat flag necessary to go past INIT_END_REPEAT opcode when - * we're not in repeat mode - */ - if (iexec->repeat) - return 0; - - return 1; -} - -static int -init_copy(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_COPY opcode: 0x37 ('7') - * - * offset (8 bit): opcode - * offset + 1 (32 bit): register - * offset + 5 (8 bit): shift - * offset + 6 (8 bit): srcmask - * offset + 7 (16 bit): CRTC port - * offset + 9 (8 bit): CRTC index - * offset + 10 (8 bit): mask - * - * Read index "CRTC index" on "CRTC port", AND with "mask", OR with - * (REGVAL("register") >> "shift" & "srcmask") and write-back to CRTC - * port - */ - - uint32_t reg = ROM32(bios->data[offset + 1]); - uint8_t shift = bios->data[offset + 5]; - uint8_t srcmask = bios->data[offset + 6]; - uint16_t crtcport = ROM16(bios->data[offset + 7]); - uint8_t crtcindex = bios->data[offset + 9]; - uint8_t mask = bios->data[offset + 10]; - uint32_t data; - uint8_t crtcdata; - - if (!iexec->execute) - return 11; - - BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Shift: 0x%02X, SrcMask: 0x%02X, " - "Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X\n", - offset, reg, shift, srcmask, crtcport, crtcindex, mask); - - data = bios_rd32(bios, reg); - - if (shift < 0x80) - data >>= shift; - else - data <<= (0x100 - shift); - - data &= srcmask; - - crtcdata = bios_idxprt_rd(bios, crtcport, crtcindex) & mask; - crtcdata |= (uint8_t)data; - bios_idxprt_wr(bios, crtcport, crtcindex, crtcdata); - - return 11; -} - -static int -init_not(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_NOT opcode: 0x38 ('8') - * - * offset (8 bit): opcode - * - * Invert the current execute / no-execute condition (i.e. "else") - */ - if (iexec->execute) - BIOSLOG(bios, "0x%04X: ------ Skipping following commands ------\n", offset); - else - BIOSLOG(bios, "0x%04X: ------ Executing following commands ------\n", offset); - - iexec->execute = !iexec->execute; - return 1; -} - -static int -init_io_flag_condition(struct nvbios *bios, uint16_t offset, - struct init_exec *iexec) -{ - /* - * INIT_IO_FLAG_CONDITION opcode: 0x39 ('9') - * - * offset (8 bit): opcode - * offset + 1 (8 bit): condition number - * - * Check condition "condition number" in the IO flag condition table. - * If condition not met skip subsequent opcodes until condition is - * inverted (INIT_NOT), or we hit INIT_RESUME - */ - - uint8_t cond = bios->data[offset + 1]; - - if (!iexec->execute) - return 2; - - if (io_flag_condition_met(bios, offset, cond)) - BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset); - else { - BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset); - iexec->execute = false; - } - - return 2; -} - -static int -init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_DP_CONDITION opcode: 0x3A ('') - * - * offset (8 bit): opcode - * offset + 1 (8 bit): "sub" opcode - * offset + 2 (8 bit): unknown - * - */ - - struct dcb_entry *dcb = bios->display.output; - struct drm_device *dev = bios->dev; - uint8_t cond = bios->data[offset + 1]; - uint8_t *table, *entry; - - BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond); - - if (!iexec->execute) - return 3; - - table = nouveau_dp_bios_data(dev, dcb, &entry); - if (!table) - return 3; - - switch (cond) { - case 0: - entry = dcb_conn(dev, dcb->connector); - if (!entry || entry[0] != DCB_CONNECTOR_eDP) - iexec->execute = false; - break; - case 1: - case 2: - if ((table[0] < 0x40 && !(entry[5] & cond)) || - (table[0] == 0x40 && !(entry[4] & cond))) - iexec->execute = false; - break; - case 5: - { - struct nouveau_i2c_port *auxch; - int ret; - - auxch = nouveau_i2c_find(dev, bios->display.output->i2c_index); - if (!auxch) { - NV_ERROR(dev, "0x%04X: couldn't get auxch\n", offset); - return 3; - } - - ret = auxch_rd(dev, auxch, 0xd, &cond, 1); - if (ret) { - NV_ERROR(dev, "0x%04X: auxch rd fail: %d\n", offset, ret); - return 3; - } - - if (!(cond & 1)) - iexec->execute = false; - } - break; - default: - NV_WARN(dev, "0x%04X: unknown INIT_3A op: %d\n", offset, cond); - break; - } - - if (iexec->execute) - BIOSLOG(bios, "0x%04X: continuing to execute\n", offset); - else - BIOSLOG(bios, "0x%04X: skipping following commands\n", offset); - - return 3; -} - -static int -init_op_3b(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_3B opcode: 0x3B ('') - * - * offset (8 bit): opcode - * offset + 1 (8 bit): crtc index - * - */ - - uint8_t or = ffs(bios->display.output->or) - 1; - uint8_t index = bios->data[offset + 1]; - uint8_t data; - - if (!iexec->execute) - return 2; - - data = bios_idxprt_rd(bios, 0x3d4, index); - bios_idxprt_wr(bios, 0x3d4, index, data & ~(1 << or)); - return 2; -} - -static int -init_op_3c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_3C opcode: 0x3C ('') - * - * offset (8 bit): opcode - * offset + 1 (8 bit): crtc index - * - */ - - uint8_t or = ffs(bios->display.output->or) - 1; - uint8_t index = bios->data[offset + 1]; - uint8_t data; - - if (!iexec->execute) - return 2; - - data = bios_idxprt_rd(bios, 0x3d4, index); - bios_idxprt_wr(bios, 0x3d4, index, data | (1 << or)); - return 2; -} - -static int -init_idx_addr_latched(struct nvbios *bios, uint16_t offset, - struct init_exec *iexec) -{ - /* - * INIT_INDEX_ADDRESS_LATCHED opcode: 0x49 ('I') - * - * offset (8 bit): opcode - * offset + 1 (32 bit): control register - * offset + 5 (32 bit): data register - * offset + 9 (32 bit): mask - * offset + 13 (32 bit): data - * offset + 17 (8 bit): count - * offset + 18 (8 bit): address 1 - * offset + 19 (8 bit): data 1 - * ... - * - * For each of "count" address and data pairs, write "data n" to - * "data register", read the current value of "control register", - * and write it back once ANDed with "mask", ORed with "data", - * and ORed with "address n" - */ - - uint32_t controlreg = ROM32(bios->data[offset + 1]); - uint32_t datareg = ROM32(bios->data[offset + 5]); - uint32_t mask = ROM32(bios->data[offset + 9]); - uint32_t data = ROM32(bios->data[offset + 13]); - uint8_t count = bios->data[offset + 17]; - int len = 18 + count * 2; - uint32_t value; - int i; - - if (!iexec->execute) - return len; - - BIOSLOG(bios, "0x%04X: ControlReg: 0x%08X, DataReg: 0x%08X, " - "Mask: 0x%08X, Data: 0x%08X, Count: 0x%02X\n", - offset, controlreg, datareg, mask, data, count); - - for (i = 0; i < count; i++) { - uint8_t instaddress = bios->data[offset + 18 + i * 2]; - uint8_t instdata = bios->data[offset + 19 + i * 2]; - - BIOSLOG(bios, "0x%04X: Address: 0x%02X, Data: 0x%02X\n", - offset, instaddress, instdata); - - bios_wr32(bios, datareg, instdata); - value = bios_rd32(bios, controlreg) & mask; - value |= data; - value |= instaddress; - bios_wr32(bios, controlreg, value); - } - - return len; -} - -static int -init_io_restrict_pll2(struct nvbios *bios, uint16_t offset, - struct init_exec *iexec) -{ - /* - * INIT_IO_RESTRICT_PLL2 opcode: 0x4A ('J') - * - * offset (8 bit): opcode - * offset + 1 (16 bit): CRTC port - * offset + 3 (8 bit): CRTC index - * offset + 4 (8 bit): mask - * offset + 5 (8 bit): shift - * offset + 6 (8 bit): count - * offset + 7 (32 bit): register - * offset + 11 (32 bit): frequency 1 - * ... - * - * Starting at offset + 11 there are "count" 32 bit frequencies (kHz). - * Set PLL register "register" to coefficients for frequency n, - * selected by reading index "CRTC index" of "CRTC port" ANDed with - * "mask" and shifted right by "shift". - */ - - uint16_t crtcport = ROM16(bios->data[offset + 1]); - uint8_t crtcindex = bios->data[offset + 3]; - uint8_t mask = bios->data[offset + 4]; - uint8_t shift = bios->data[offset + 5]; - uint8_t count = bios->data[offset + 6]; - uint32_t reg = ROM32(bios->data[offset + 7]); - int len = 11 + count * 4; - uint8_t config; - uint32_t freq; - - if (!iexec->execute) - return len; - - BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, " - "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n", - offset, crtcport, crtcindex, mask, shift, count, reg); - - if (!reg) - return len; - - config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift; - if (config > count) { - NV_ERROR(bios->dev, - "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n", - offset, config, count); - return len; - } - - freq = ROM32(bios->data[offset + 11 + config * 4]); - - BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %dkHz\n", - offset, reg, config, freq); - - setPLL(bios->dev, reg, freq); - - return len; -} - -static int -init_pll2(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_PLL2 opcode: 0x4B ('K') - * - * offset (8 bit): opcode - * offset + 1 (32 bit): register - * offset + 5 (32 bit): freq - * - * Set PLL register "register" to coefficients for frequency "freq" - */ - - uint32_t reg = ROM32(bios->data[offset + 1]); - uint32_t freq = ROM32(bios->data[offset + 5]); - - if (!iexec->execute) - return 9; - - BIOSLOG(bios, "0x%04X: Reg: 0x%04X, Freq: %dkHz\n", - offset, reg, freq); - - setPLL(bios->dev, reg, freq); - return 9; -} - -static int -init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_I2C_BYTE opcode: 0x4C ('L') - * - * offset (8 bit): opcode - * offset + 1 (8 bit): DCB I2C table entry index - * offset + 2 (8 bit): I2C slave address - * offset + 3 (8 bit): count - * offset + 4 (8 bit): I2C register 1 - * offset + 5 (8 bit): mask 1 - * offset + 6 (8 bit): data 1 - * ... - * - * For each of "count" registers given by "I2C register n" on the device - * addressed by "I2C slave address" on the I2C bus given by - * "DCB I2C table entry index", read the register, AND the result with - * "mask n" and OR it with "data n" before writing it back to the device - */ - - struct drm_device *dev = bios->dev; - uint8_t i2c_index = bios->data[offset + 1]; - uint8_t i2c_address = bios->data[offset + 2] >> 1; - uint8_t count = bios->data[offset + 3]; - struct nouveau_i2c_port *chan; - int len = 4 + count * 3; - int ret, i; - - if (!iexec->execute) - return len; - - BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, " - "Count: 0x%02X\n", - offset, i2c_index, i2c_address, count); - - chan = init_i2c_device_find(dev, i2c_index); - if (!chan) { - NV_ERROR(dev, "0x%04X: i2c bus not found\n", offset); - return len; - } - - for (i = 0; i < count; i++) { - uint8_t reg = bios->data[offset + 4 + i * 3]; - uint8_t mask = bios->data[offset + 5 + i * 3]; - uint8_t data = bios->data[offset + 6 + i * 3]; - union i2c_smbus_data val; - - ret = i2c_smbus_xfer(nouveau_i2c_adapter(chan), i2c_address, 0, - I2C_SMBUS_READ, reg, - I2C_SMBUS_BYTE_DATA, &val); - if (ret < 0) { - NV_ERROR(dev, "0x%04X: i2c rd fail: %d\n", offset, ret); - return len; - } - - BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, " - "Mask: 0x%02X, Data: 0x%02X\n", - offset, reg, val.byte, mask, data); - - if (!bios->execute) - continue; - - val.byte &= mask; - val.byte |= data; - ret = i2c_smbus_xfer(nouveau_i2c_adapter(chan), i2c_address, 0, - I2C_SMBUS_WRITE, reg, - I2C_SMBUS_BYTE_DATA, &val); - if (ret < 0) { - NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret); - return len; - } - } - - return len; -} - -static int -init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) -{ - /* - * INIT_ZM_I2C_BYTE opcode: 0x4D ('M') - * - * offset (8 bit): opcode - * offset + 1 (8 bit): DCB I2C table entry index - * offset + 2 (8 bit): I2C slave address - * offset + 3 (8 bit): count - * offset + 4 (8 bit): I2C register 1 - * offset + 5 (8 bit): data 1 - * ... - * - * For each of "count" registers given by "I2C register n" on the device - * addressed by "I2C slave address" on the I2C bus given by - * "DCB I2C table entry index", set the register to "data n" - */ - - struct drm_device *dev = bios->dev; - uint8_t i2c_index = bios->data[offset + 1]; - uint8_t i2c_address = bios->data[offset + 2] >> 1; - uint8_t count = bios->data[offset + 3]; - struct nouveau_i2c_port *chan; - int len = 4 + count * 2; - int ret, i; - - if (!iexec->execute) - return len; - - BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, " - "Count: 0x%02X\n", - offset, i2c_index, i2c_address, count); - - chan = init_i2c_device_find(dev, i2c_index); - if (!chan) { - NV_ERROR(dev, "0x%04X: i2c bus not found\n", offset); - return len; - } - - for (i = 0; i < count; i++) { - uint8_t reg = bios->data[offset + 4 + i * 2]; - union i2c_smbus_data val; - - val.byte = bios->data[offset + 5 + i * 2]; - - BIOSLOG(bios, "0x%04X: I2CReg: 0 |