/* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied) * it affects only the 8 bit vga io regs, which we access using mmio at * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d* * in general, the set value of cr44 does not matter: reg access works as * expected and values can be set for the appropriate head by using a 0x2000 * offset as required * however: * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and * cr44 must be set to 0 or 3 for accessing values on the correct head * through the common 0xc03c* addresses * b) in tied mode (4) head B is programmed to the values set on head A, and * access using the head B addresses can have strange results, ergo we leave * tied mode in init once we know to what cr44 should be restored on exit * * the owner parameter is slightly abused: * 0 and 1 are treated as head values and so the set value is (owner * 3) * other values are treated as literal values to set */ u8 nv_rdvgaowner(void *obj) { if (nv_device(obj)->card_type < NV_50) { if (nv_device(obj)->chipset == 0x11) { u32 tied = nv_rd32(obj, 0x001084) & 0x10000000; if (tied == 0) { u8 slA = nv_rdvgac(obj, 0, 0x28) & 0x80; u8 tvA = nv_rdvgac(obj, 0, 0x33) & 0x01; u8 slB = nv_rdvgac(obj, 1, 0x28) & 0x80; u8 tvB = nv_rdvgac(obj, 1, 0x33) & 0x01; if (slA && !tvA) return 0x00; if (slB && !tvB) return 0x03; if (slA) return 0x00; if (slB) return 0x03; return 0x00; } return 0x04; } return nv_rdvgac(obj, 0, 0x44); } nv_error(obj, "rdvgaowner after nv4x\n"); return 0x00; }
u8 nv_rdvgai(void *obj, int head, u16 port, u8 index) { if (port == 0x03c4) return nv_rdvgas(obj, head, index); if (port == 0x03ce) return nv_rdvgag(obj, head, index); if (port == 0x03d4) return nv_rdvgac(obj, head, index); nv_error(obj, "unknown indexed vga port 0x%04x\n", port); return 0x00; }
int nv04_devinit_init(struct nouveau_object *object) { struct nv04_devinit_priv *priv = (void *)object; if (!priv->base.post) { u32 htotal = nv_rdvgac(priv, 0, 0x06); htotal |= (nv_rdvgac(priv, 0, 0x07) & 0x01) << 8; htotal |= (nv_rdvgac(priv, 0, 0x07) & 0x20) << 4; htotal |= (nv_rdvgac(priv, 0, 0x25) & 0x01) << 10; htotal |= (nv_rdvgac(priv, 0, 0x41) & 0x01) << 11; if (!htotal) { nv_info(priv, "adaptor not initialised\n"); priv->base.post = true; } } return nouveau_devinit_init(&priv->base); }
void nv_wrvgaowner(void *obj, u8 select) { if (nv_device(obj)->card_type < NV_50) { u8 owner = (select == 1) ? 3 : select; if (nv_device(obj)->chipset == 0x11) { /* workaround hw lockup bug */ nv_rdvgac(obj, 0, 0x1f); nv_rdvgac(obj, 1, 0x1f); } nv_wrvgac(obj, 0, 0x44, owner); if (nv_device(obj)->chipset == 0x11) { nv_wrvgac(obj, 0, 0x2e, owner); nv_wrvgac(obj, 0, 0x2e, owner); } } else nv_error(obj, "wrvgaowner after nv4x\n"); }
bool nv_lockvgac(void *obj, bool lock) { bool locked = !nv_rdvgac(obj, 0, 0x1f); u8 data = lock ? 0x99 : 0x57; nv_wrvgac(obj, 0, 0x1f, data); if (nv_device(obj)->chipset == 0x11) { if (!(nv_rd32(obj, 0x001084) & 0x10000000)) nv_wrvgac(obj, 1, 0x1f, data); } return locked; }
bool nv_lockvgac(void *obj, bool lock) { struct nouveau_device *dev = nv_device(obj); bool locked = !nv_rdvgac(obj, 0, 0x1f); u8 data = lock ? 0x99 : 0x57; if (dev->card_type < NV_50) nv_wrvgac(obj, 0, 0x1f, data); else nv_wrvgac(obj, 0, 0x3f, data); if (dev->chipset == 0x11) { if (!(nv_rd32(obj, 0x001084) & 0x10000000)) nv_wrvgac(obj, 1, 0x1f, data); } return locked; }
int nv50_devinit_init(struct nouveau_object *object) { struct nouveau_bios *bios = nouveau_bios(object); struct nv50_devinit_priv *priv = (void *)object; struct nvbios_outp info; struct dcb_output outp; u8 ver = 0xff, hdr, cnt, len; int ret, i = 0; if (!priv->base.post) { if (!nv_rdvgac(priv, 0, 0x00) && !nv_rdvgac(priv, 0, 0x1a)) { nv_info(priv, "adaptor not initialised\n"); priv->base.post = true; } } ret = nouveau_devinit_init(&priv->base); if (ret) return ret; /* if we ran the init tables, we have to execute the first script * pointer of each dcb entry's display encoder table in order * to properly initialise each encoder. */ while (priv->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) { if (nvbios_outp_match(bios, outp.hasht, outp.hashm, &ver, &hdr, &cnt, &len, &info)) { struct nvbios_init init = { .subdev = nv_subdev(priv), .bios = bios, .offset = info.script[0], .outp = &outp, .crtc = -1, .execute = 1, }; nvbios_exec(&init); } i++; } return 0; } static int nv50_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) { struct nv50_devinit_priv *priv; int ret; ret = nouveau_devinit_create(parent, engine, oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.pll_set = nv50_devinit_pll_set; return 0; } struct nouveau_oclass nv50_devinit_oclass = { .handle = NV_SUBDEV(DEVINIT, 0x50), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv50_devinit_ctor, .dtor = _nouveau_devinit_dtor, .init = nv50_devinit_init, .fini = _nouveau_devinit_fini, }, };