/* * Copyright 2009-2010 Francisco Jerez * All Rights Reserved. * * 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 (including the * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ #include #include #include #include #include "nvhw.h" #include "util.h" #define mmio_domain(p) \ lambda(uint32_t *x, nv_rd32(p, *x)) #define vga_crtc_domain(p, i) \ lambda(uint32_t *x, (uint32_t)read_vga_crtc(p, i, *x)) #define itv_domain(p) \ lambda(uint32_t *x, (uint32_t)read_itv(p, *x)) #define tmds_domain(p, i, j) \ lambda(uint32_t *x, (uint32_t)read_tmds(p, i, j, *x)) #define head_domain(p, i) \ lambda(uint32_t *x, { \ *x += i * 0x2000; \ nv_rd32(p, *x); \ }) static void dump_block(struct reg *rs, uint32_t (*f)(uint32_t *), const char *name) { printf("-- %s registers\n", name); for (; rs->class; rs++) { if (nv_class(rs->class)) { uint32_t addr = rs->offset; uint32_t val = f(&addr); printf(" %s 0x%x: 0x%-8x", name, addr, val); if (rs->name) printf("\t(%s)", rs->name); printf("\n"); } } printf("\n"); } static void dump_pll(void *regs, const char *name, uint32_t reg) { int pll = nv_rd32(regs, reg); int n1, m1, n2, m2, p, vco2; uint32_t strap_mask; float f0; strap_mask = 1 << 6; if (nv_class(DUALHEAD_CLASS)) strap_mask |= 1 << 22; switch (nv_rd32(regs, NV_PEXTDEV_BOOT_0) & strap_mask) { case 0: f0 = 13.500; break; case (1 << 6): f0 = 14.318; break; case (1 << 22): f0 = 27.000; break; case (1 << 22 | 1 << 6): f0 = 25.000; break; } if (nv_class(TWOREG_PLL_CLASS)) { uint32_t reg2 = reg + (reg == NV_PRAMDAC_VPLL2 ? 0x5c : 0x70); uint32_t vco2_ctrl = nv_rd32(regs, 0x680580); int pll2 = nv_rd32(regs, reg2); vco2 = (chipset < 0x40 || (reg == NV_PRAMDAC_VPLL1 && !(vco2_ctrl & (1 << 8))) || (reg == NV_PRAMDAC_VPLL2 && !(vco2_ctrl & (1 << 28)))); n1 = (pll & 0xff00) >> 8; m1 = pll & 0xff; p = (pll & 0x70000) >> 16; n2 = (pll2 & 0xff00) >> 8; m2 = pll2 & 0xff; } else if (nv_class(TWOSTAGE_PLL_CLASS)) { n1 = (pll & 0xff00) >> 8; m1 = pll & 0xf; n2 = (pll & 0x380000) >> 19 | (pll & 0x3000000) >> 24 << 3; m2 = (pll & 0x70) >> 4; p = (pll & 0x70000) >> 16; vco2 = pll & 0x80; } else { n1 = (pll & 0xff00) >> 8; m1 = pll & 0xff; n2 = 1; m2 = 1; p = (pll & 0x70000) >> 16; vco2 = 0; } printf(" %s: 0x%x N1=0x%x M1=0x%x N2=0x%x M2=0x%x" " P=0x%x VCO2=%d f0=%f f=%f\n", name, pll, n1, m1, n2, m2, p, vco2, f0, (f0 * (float)n1 / m1 * (vco2 ? n2 / m2 : 1) * (float)(1 << (7 - p)) / (1 << 7))); } static void dump_crtc_timings(void *regs, int head) { int hdisplay, htotal, hsync_start, hsync_end, hblank_start, hblank_end, vdisplay, vtotal, vsync_start, vsync_end, vblank_start, vblank_end, ilace, dblscan, clkdiv, nhsync, nvsync; write_vga_crtc(regs, 0, NV_CIO_SR_LOCK, 0x57); write_vga_crtc(regs, 0, 0x44, head); hdisplay = read_vga_crtc(regs, head, NV_CIO_CR_HDE) | (read_vga_crtc(regs, head, NV_CIO_CRE_HEB) & 0x2) >> 1 << 8; htotal = read_vga_crtc(regs, head, NV_CIO_CR_HDT) | (read_vga_crtc(regs, head, NV_CIO_CRE_HEB) & 0x1) << 8; hsync_start = read_vga_crtc(regs, head, NV_CIO_CR_HRS) | (read_vga_crtc(regs, head, NV_CIO_CRE_HEB) & 0x8) >> 3 << 8; hsync_end = read_vga_crtc(regs, head, NV_CIO_CR_HRE) & 0x1F; hblank_start = read_vga_crtc(regs, head, NV_CIO_CR_HBS) | (read_vga_crtc(regs, head, NV_CIO_CRE_HEB) & 0x4) >> 2 << 8; hblank_end = (read_vga_crtc(regs, head, NV_CIO_CR_HBE) & 0x1f) | (read_vga_crtc(regs, head, NV_CIO_CR_HRE) & 0x80) >> 7 << 5 | (read_vga_crtc(regs, head, NV_CIO_CRE_LSR) & 0x10) >> 4 << 6; vdisplay = read_vga_crtc(regs, head, NV_CIO_CR_VDE) | (read_vga_crtc(regs, head, NV_CIO_CR_OVL) & 0x2) >> 1 << 8 | (read_vga_crtc(regs, head, NV_CIO_CR_OVL) & 0x40) >> 6 << 9 | (read_vga_crtc(regs, head, NV_CIO_CRE_LSR) & 0x2) >> 1 << 10 | (read_vga_crtc(regs, head, NV_CIO_CRE_EBR) & 0x4) >> 2 << 11; vtotal = read_vga_crtc(regs, head, NV_CIO_CR_VDT) | (read_vga_crtc(regs, head, NV_CIO_CR_OVL) & 0x20) >> 5 << 9 | (read_vga_crtc(regs, head, NV_CIO_CR_OVL) & 0x1) << 8 | (read_vga_crtc(regs, head, NV_CIO_CRE_LSR) & 0x1) << 10 | (read_vga_crtc(regs, head, NV_CIO_CRE_EBR) & 0x1) << 11; vsync_start = read_vga_crtc(regs, head, NV_CIO_CR_VRS) | (read_vga_crtc(regs, head, NV_CIO_CR_OVL) & 0x80) >> 7 << 9 | (read_vga_crtc(regs, head, NV_CIO_CR_OVL) & 0x4) >> 2 << 8 | (read_vga_crtc(regs, head, NV_CIO_CRE_LSR) & 0x4) >> 2 << 10 | (read_vga_crtc(regs, head, NV_CIO_CRE_EBR) & 0x10) >> 4 << 11; vsync_end = read_vga_crtc(regs, head, NV_CIO_CR_VRE) & 0xF; vblank_start = read_vga_crtc(regs, head, NV_CIO_CR_VBS) | (read_vga_crtc(regs, head, NV_CIO_CR_OVL) & 0x8) >> 3 << 8 | (read_vga_crtc(regs, head, NV_CIO_CR_CELL_HT) & 0x20) >> 5 << 9 | (read_vga_crtc(regs, head, NV_CIO_CRE_LSR) & 0x8) >> 3 << 10 | (read_vga_crtc(regs, head, NV_CIO_CRE_EBR) & 0x40) >> 6 << 11; vblank_end = read_vga_crtc(regs, head, NV_CIO_CR_VBE); ilace = read_vga_crtc(regs, head, NV_CIO_CRE_ILACE) | (read_vga_crtc(regs, head, NV_CIO_CRE_HEB) & 0x10) >> 4 << 8; dblscan = read_vga_crtc(regs, head, NV_CIO_CR_CELL_HT) & 0x80; clkdiv = read_vga_seq(regs, head, NV_VIO_SR_CLOCK) & 0x8; nhsync = read_prmvio(regs, head, NV_PRMVIO_MISC__READ) & 0x40; nvsync = read_prmvio(regs, head, NV_PRMVIO_MISC__READ) & 0x80; printf("-- CRTC%d timings\n", head); printf(" hdisplay: %d\n", (hdisplay + 1) * 8); printf(" htotal: %d\n", (htotal + 5) * 8); printf(" hsync start: %d\n", (hsync_start - 1) * 8); printf(" hsync end: %d\n", (subm(hsync_end, hsync_start, 32) + hsync_start - 1) * 8); printf(" hblank start: %d\n", (hblank_start + 1) * 8); printf(" hblank end: %d\n", (subm(hblank_end, hblank_start, 128) + hblank_start + 1) * 8); printf(" vdisplay: %d\n", vdisplay + 1); printf(" vtotal: %d\n", vtotal + 2); printf(" vsync start: %d\n", vsync_start + 1); printf(" vsync end: %d\n", subm(vsync_end, vsync_start, 16) + vsync_start + 1); printf(" vblank start: %d\n", vblank_start + 1); printf(" vblank end: %d\n", subm(vblank_end, vblank_start, 256) + vblank_start + 1); printf(" ilace: %d%s\n", ilace, (ilace & 0xff) == 0xff ? " [off]" : ""); printf(" dblscan: %d\n", dblscan); printf(" clkdiv2: %d\n", clkdiv); printf(" nhsync: %d\n", nhsync); printf(" nvsync: %d\n", nvsync); printf("\n"); } #define block_name(buf, ...) \ ({ \ snprintf(buf, sizeof(buf), __VA_ARGS__); \ buf; \ }) static int tv_dump(void *regs) { char buf[64]; int i, j; nv_detect_chipset(regs); printf("-- Chipset: NV%x\n", chipset); dump_block(pmc_block, mmio_domain(regs), block_name(buf, "PMC")); dump_block(pbus_block, mmio_domain(regs), block_name(buf, "PBUS")); dump_block(pfb_block, mmio_domain(regs), block_name(buf, "PFB")); printf("-- PLLs\n"); dump_pll(regs, "NVPLL", NV_PRAMDAC_NVPLL); dump_pll(regs, "MPLL", NV_PRAMDAC_MPLL); dump_pll(regs, "VPLL1", NV_PRAMDAC_VPLL1); dump_pll(regs, "VPLL2", NV_PRAMDAC_VPLL2); printf("\n"); for (i = 0; i < 2; i++) { dump_block(pcrtc_block, head_domain(regs, i), block_name(buf, "PCRTC%d", i)); dump_block(vga_crtc_block, vga_crtc_domain(regs, i), block_name(buf, "CIO%d", i)); dump_crtc_timings(regs, i); } for (i = 0; i < 2; i++) dump_block(pramdac_block, head_domain(regs, i), block_name(buf, "PRAMDAC%d", i)); dump_block(ptv_block, mmio_domain(regs), block_name(buf, "PTV")); dump_block(itv_block, itv_domain(regs), block_name(buf, "ITV")); for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) { dump_block(tmds_block, tmds_domain(regs, i, j), block_name(buf, "TMDS%dL%d", i, j)); } } dump_block(pvideo_block, mmio_domain(regs), block_name(buf, "PVIDEO")); return 0; } int main(int argc, char *argv[]) { struct pci_id_match match = { 0x10de, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0x30000, 0xffff0000 }; struct pci_device_iterator *it; struct pci_device *dev; void *regs; int ret; ret = pci_system_init(); if (ret) goto out; it = pci_id_match_iterator_create(&match); if (!it) { ret = ENOMEM; goto out_sys; } dev = pci_device_next(it); if (!dev) { ret = ENODEV; goto out_iter; } ret = pci_device_probe(dev); if (ret) goto out_iter; ret = pci_device_map_range(dev, dev->regions[0].base_addr, dev->regions[0].size, PCI_DEV_MAP_FLAG_WRITABLE, ®s); if (ret) goto out_iter; ret = tv_dump(regs); pci_device_unmap_range(dev, regs, dev->regions[0].size); out_iter: pci_iterator_destroy(it); out_sys: pci_system_cleanup(); out: if (ret) fprintf(stderr, "%s: %s\n", argv[0], strerror(ret)); return ret; }