int nvkm_hwsq_fini(struct nvkm_hwsq **phwsq, bool exec) { struct nvkm_hwsq *hwsq = *phwsq; int ret = 0, i; if (hwsq) { struct nvkm_bus *pbus = hwsq->pbus; hwsq->c.size = (hwsq->c.size + 4) / 4; if (hwsq->c.size <= pbus->hwsq_size) { if (exec) ret = pbus->hwsq_exec(pbus, (u32 *)hwsq->c.data, hwsq->c.size); if (ret) nv_error(pbus, "hwsq exec failed: %d\n", ret); } else { nv_error(pbus, "hwsq ucode too large\n"); ret = -ENOSPC; } for (i = 0; ret && i < hwsq->c.size; i++) nv_error(pbus, "\t0x%08x\n", ((u32 *)hwsq->c.data)[i]); *phwsq = NULL; kfree(hwsq); } return ret; }
static void nvc0_bus_intr(struct nouveau_subdev *subdev) { struct nouveau_bus *pbus = nouveau_bus(subdev); u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140); if (stat & 0x0000000e) { u32 addr = nv_rd32(pbus, 0x009084); u32 data = nv_rd32(pbus, 0x009088); nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x [ %s%s%s]\n", (addr & 0x00000002) ? "write" : "read", data, (addr & 0x00fffffc), (stat & 0x00000002) ? "!ENGINE " : "", (stat & 0x00000004) ? "IBUS " : "", (stat & 0x00000008) ? "TIMEOUT " : ""); nv_wr32(pbus, 0x009084, 0x00000000); nv_wr32(pbus, 0x001100, (stat & 0x0000000e)); stat &= ~0x0000000e; } if (stat) { nv_error(pbus, "unknown intr 0x%08x\n", stat); nv_mask(pbus, 0x001140, stat, 0x00000000); } }
static int nv50_disp_pioc_init(struct nouveau_object *object) { struct nv50_disp_priv *priv = (void *)object->engine; struct nv50_disp_pioc *pioc = (void *)object; int chid = pioc->base.chid; int ret; ret = nv50_disp_chan_init(&pioc->base); if (ret) return ret; nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00002000); if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00000000, 0x00000000)) { nv_error(pioc, "timeout0: 0x%08x\n", nv_rd32(priv, 0x610200 + (chid * 0x10))); return -EBUSY; } nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00000001); if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00030000, 0x00010000)) { nv_error(pioc, "timeout1: 0x%08x\n", nv_rd32(priv, 0x610200 + (chid * 0x10))); return -EBUSY; } return 0; }
static int anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh) { struct anx9805_i2c_port *chan = (void *)port; struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent; u8 tmp, i; nv_wri2cr(mast, chan->addr, 0xa0, link_bw); nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00)); nv_wri2cr(mast, chan->addr, 0xa2, 0x01); nv_wri2cr(mast, chan->addr, 0xa8, 0x01); i = 0; while ((tmp = nv_rdi2cr(mast, chan->addr, 0xa8)) & 0x01) { mdelay(5); if (i++ == 100) { nv_error(port, "link training timed out\n"); return -ETIMEDOUT; } } if (tmp & 0x70) { nv_error(port, "link training failed: 0x%02x\n", tmp); return -EIO; } return 1; }
static int nv84_graph_tlb_flush(struct nouveau_engine *engine) { struct nouveau_timer *ptimer = nouveau_timer(engine); struct nv50_graph_priv *priv = (void *)engine; bool idle, timeout = false; unsigned long flags; u64 start; u32 tmp; spin_lock_irqsave(&priv->lock, flags); nv_mask(priv, 0x400500, 0x00000001, 0x00000000); start = ptimer->read(ptimer); do { idle = true; for (tmp = nv_rd32(priv, 0x400380); tmp && idle; tmp >>= 3) { if ((tmp & 7) == 1) idle = false; } for (tmp = nv_rd32(priv, 0x400384); tmp && idle; tmp >>= 3) { if ((tmp & 7) == 1) idle = false; } for (tmp = nv_rd32(priv, 0x400388); tmp && idle; tmp >>= 3) { if ((tmp & 7) == 1) idle = false; } } while (!idle && !(timeout = ptimer->read(ptimer) - start > 2000000000)); if (timeout) { nv_error(priv, "PGRAPH TLB flush idle timeout fail\n"); tmp = nv_rd32(priv, 0x400700); nv_error(priv, "PGRAPH_STATUS : 0x%08x", tmp); nouveau_bitfield_print(nv50_pgraph_status, tmp); pr_cont("\n"); nouveau_pgraph_vstatus_print(priv, 0, nv50_pgraph_vstatus_0, nv_rd32(priv, 0x400380)); nouveau_pgraph_vstatus_print(priv, 1, nv50_pgraph_vstatus_1, nv_rd32(priv, 0x400384)); nouveau_pgraph_vstatus_print(priv, 2, nv50_pgraph_vstatus_2, nv_rd32(priv, 0x400388)); } nv_wr32(priv, 0x100c80, 0x00000001); if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) nv_error(priv, "vm flush timeout\n"); nv_mask(priv, 0x400500, 0x00000001, 0x00000001); spin_unlock_irqrestore(&priv->lock, flags); return timeout ? -EBUSY : 0; }
static int nve0_ram_calc_data(struct nouveau_fb *pfb, u32 freq, struct nouveau_ram_data *data) { struct nouveau_bios *bios = nouveau_bios(pfb); struct nve0_ram *ram = (void *)pfb->ram; u8 strap, cnt, len; /* lookup memory config data relevant to the target frequency */ ram->base.rammap.data = nvbios_rammapEp(bios, freq / 1000, &ram->base.rammap.version, &ram->base.rammap.size, &cnt, &len, &data->bios); if (!ram->base.rammap.data || ram->base.rammap.version != 0x11 || ram->base.rammap.size < 0x09) { nv_error(pfb, "invalid/missing rammap entry\n"); return -EINVAL; } /* locate specific data set for the attached memory */ strap = nvbios_ramcfg_index(nv_subdev(pfb)); ram->base.ramcfg.data = nvbios_rammapSp(bios, ram->base.rammap.data, ram->base.rammap.version, ram->base.rammap.size, cnt, len, strap, &ram->base.ramcfg.version, &ram->base.ramcfg.size, &data->bios); if (!ram->base.ramcfg.data || ram->base.ramcfg.version != 0x11 || ram->base.ramcfg.size < 0x08) { nv_error(pfb, "invalid/missing ramcfg entry\n"); return -EINVAL; } /* lookup memory timings, if bios says they're present */ strap = nv_ro08(bios, ram->base.ramcfg.data + 0x00); if (strap != 0xff) { ram->base.timing.data = nvbios_timingEp(bios, strap, &ram->base.timing.version, &ram->base.timing.size, &cnt, &len, &data->bios); if (!ram->base.timing.data || ram->base.timing.version != 0x20 || ram->base.timing.size < 0x33) { nv_error(pfb, "invalid/missing timing entry\n"); return -EINVAL; } } else { ram->base.timing.data = 0; } data->freq = freq; return 0; }
/* * Send the given nv structure via conn. * We keep headers in nv structure and pass data in separate argument. * There can be no data at all (data is NULL then). */ int hast_proto_send(const struct hast_resource *res, struct proto_conn *conn, struct nv *nv, const void *data, size_t size) { struct hast_main_header hdr; struct ebuf *eb; bool freedata; void *dptr, *hptr; size_t hsize; int ret; dptr = (void *)(uintptr_t)data; freedata = false; ret = -1; if (data != NULL) { unsigned int ii; for (ii = 0; ii < sizeof(pipeline) / sizeof(pipeline[0]); ii++) { (void)pipeline[ii].hps_send(res, nv, &dptr, &size, &freedata); } nv_add_uint32(nv, size, "size"); if (nv_error(nv) != 0) { errno = nv_error(nv); goto end; } } eb = nv_hton(nv); if (eb == NULL) goto end; hdr.version = res != NULL ? res->hr_version : HAST_PROTO_VERSION; hdr.size = htole32((uint32_t)ebuf_size(eb)); if (ebuf_add_head(eb, &hdr, sizeof(hdr)) == -1) goto end; hptr = ebuf_data(eb, &hsize); if (proto_send(conn, hptr, hsize) == -1) goto end; if (data != NULL && proto_send(conn, dptr, size) == -1) goto end; ret = 0; end: if (freedata) free(dptr); return (ret); }
static u16 exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info) { struct nouveau_bios *bios = nouveau_bios(priv); u16 mask, type, data; if (outp < 4) { type = DCB_OUTPUT_ANALOG; mask = 0; } else if (outp < 8) { switch (ctrl & 0x00000f00) { case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; default: nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); return 0x0000; } outp -= 4; } else { outp = outp - 8; type = 0x0010; mask = 0; switch (ctrl & 0x00000f00) { case 0x00000000: type |= priv->pior.type[outp]; break; default: nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl); return 0x0000; } } mask = 0x00c0 & (mask << 6); mask |= 0x0001 << outp; mask |= 0x0100 << head; data = dcb_outp_match(bios, type, mask, ver, hdr, dcb); if (!data) return 0x0000; /* off-chip encoders require matching the exact encoder type */ if (dcb->location != 0) type |= dcb->extdev << 8; return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); }
static void nve0_graph_ctxctl_isr(struct nvc0_graph_priv *priv) { u32 ustat = nv_rd32(priv, 0x409c18); if (ustat & 0x00000001) nv_error(priv, "CTXCTRL ucode error\n"); if (ustat & 0x00080000) nv_error(priv, "CTXCTRL watchdog timeout\n"); if (ustat & ~0x00080001) nv_error(priv, "CTXCTRL 0x%08x\n", ustat); nvc0_graph_ctxctl_debug(priv); nv_wr32(priv, 0x409c20, ustat); }
static irqreturn_t nouveau_mc_intr(int irq, void *arg) { struct nouveau_mc *pmc = arg; const struct nouveau_mc_intr *map = pmc->intr_map; struct nouveau_subdev *unit; u32 stat, intr; intr = stat = nv_rd32(pmc, 0x000100); while (stat && map->stat) { if (stat & map->stat) { unit = nouveau_subdev(pmc, map->unit); if (unit && unit->intr) unit->intr(unit); intr &= ~map->stat; } map++; } if (intr) { nv_error(pmc, "unknown intr 0x%08x\n", stat); } return stat ? IRQ_HANDLED : IRQ_NONE; }
/* 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; }
static void nve0_graph_mp_trap(struct nvc0_graph_priv *priv, int gpc, int tp) { int i; u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x648)); u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x650)); nv_error(priv, "GPC%i/TP%i/MP trap:", gpc, tp); for (i = 0; i <= 31; ++i) { if (!(gerr & (1 << i))) continue; pr_cont(" "); nouveau_enum_print(nve0_mp_global_error, i); } if (werr) { pr_cont(" "); nouveau_enum_print(nve0_mp_warp_error, werr & 0xffff); } pr_cont("\n"); /* disable MP trap to avoid spam */ nv_mask(priv, TPC_UNIT(gpc, tp, 0x50c), 0x2, 0x0); /* TODO: figure out how to resume after an MP trap */ }
int nv50_mpeg_init(struct nvkm_object *object) { struct nv50_mpeg_priv *priv = (void *)object; int ret; ret = nvkm_mpeg_init(&priv->base); if (ret) return ret; nv_wr32(priv, 0x00b32c, 0x00000000); nv_wr32(priv, 0x00b314, 0x00000100); nv_wr32(priv, 0x00b0e0, 0x0000001a); nv_wr32(priv, 0x00b220, 0x00000044); nv_wr32(priv, 0x00b300, 0x00801ec1); nv_wr32(priv, 0x00b390, 0x00000000); nv_wr32(priv, 0x00b394, 0x00000000); nv_wr32(priv, 0x00b398, 0x00000000); nv_mask(priv, 0x00b32c, 0x00000001, 0x00000001); nv_wr32(priv, 0x00b100, 0xffffffff); nv_wr32(priv, 0x00b140, 0xffffffff); if (!nv_wait(priv, 0x00b200, 0x00000001, 0x00000000)) { nv_error(priv, "timeout 0x%08x\n", nv_rd32(priv, 0x00b200)); return -EBUSY; } return 0; }
static void nv20_devinit_meminit(struct nouveau_devinit *devinit) { struct nv20_devinit_priv *priv = (void *)devinit; struct nouveau_device *device = nv_device(priv); uint32_t mask = (device->chipset >= 0x25 ? 0x300 : 0x900); uint32_t amount, off; struct io_mapping *fb; /* Map the framebuffer aperture */ fb = fbmem_init(nv_device(priv)->pdev); if (!fb) { nv_error(priv, "failed to map fb\n"); return; } nv_wr32(priv, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1); /* Allow full addressing */ nv_mask(priv, NV04_PFB_CFG0, 0, mask); amount = nv_rd32(priv, 0x10020c); for (off = amount; off > 0x2000000; off -= 0x2000000) fbmem_poke(fb, off - 4, off); amount = nv_rd32(priv, 0x10020c); if (amount != fbmem_peek(fb, amount - 4)) /* IC missing - disable the upper half memory space. */ nv_mask(priv, NV04_PFB_CFG0, mask, 0); fbmem_fini(fb); }
static int nv50_disp_dmac_init(struct nouveau_object *object) { struct nv50_disp_priv *priv = (void *)object->engine; struct nv50_disp_dmac *dmac = (void *)object; int chid = dmac->base.chid; int ret; ret = nv50_disp_chan_init(&dmac->base); if (ret) return ret; /* enable error reporting */ nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00010001 << chid); /* initialise channel for dma command submission */ nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push); nv_wr32(priv, 0x610208 + (chid * 0x0010), 0x00010000); nv_wr32(priv, 0x61020c + (chid * 0x0010), chid); nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00000010, 0x00000010); nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000); nv_wr32(priv, 0x610200 + (chid * 0x0010), 0x00000013); /* wait for it to go inactive */ if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x80000000, 0x00000000)) { nv_error(dmac, "init timeout, 0x%08x\n", nv_rd32(priv, 0x610200 + (chid * 0x10))); return -EBUSY; } return 0; }
static int nvd0_disp_mast_init(struct nouveau_object *object) { struct nv50_disp_priv *priv = (void *)object->engine; struct nv50_disp_dmac *mast = (void *)object; int ret; ret = nv50_disp_chan_init(&mast->base); if (ret) return ret; /* enable error reporting */ nv_mask(priv, 0x610090, 0x00000001, 0x00000001); nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001); /* initialise channel for dma command submission */ nv_wr32(priv, 0x610494, mast->push); nv_wr32(priv, 0x610498, 0x00010000); nv_wr32(priv, 0x61049c, 0x00000001); nv_mask(priv, 0x610490, 0x00000010, 0x00000010); nv_wr32(priv, 0x640000, 0x00000000); nv_wr32(priv, 0x610490, 0x01000013); /* wait for it to go inactive */ if (!nv_wait(priv, 0x610490, 0x80000000, 0x00000000)) { nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610490)); return -EBUSY; } return 0; }
static u16 therm_table(struct nouveau_device *device, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) { struct bit_entry bit_P; u16 therm = 0; if (!nouveau_bit_entry(device, 'P', &bit_P)) { if (bit_P.version == 1) therm = nv_ro16(device, bit_P.offset + 12); else if (bit_P.version == 2) therm = nv_ro16(device, bit_P.offset + 16); else nv_error(device, "unknown offset for thermal in BIT P %d\n", bit_P.version); } /* exit now if we haven't found the thermal table */ if (!therm) return 0x0000; *ver = nv_ro08(device, therm + 0); *hdr = nv_ro08(device, therm + 1); *len = nv_ro08(device, therm + 2); *cnt = nv_ro08(device, therm + 3); return therm + nv_ro08(device, therm + 1); }
static int nvd0_disp_pioc_init(struct nouveau_object *object) { struct nv50_disp_priv *priv = (void *)object->engine; struct nv50_disp_pioc *pioc = (void *)object; int chid = pioc->base.chid; int ret; ret = nv50_disp_chan_init(&pioc->base); if (ret) return ret; /* enable error reporting */ nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); /* activate channel */ nv_wr32(priv, 0x610490 + (chid * 0x10), 0x00000001); if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00010000)) { nv_error(pioc, "init: 0x%08x\n", nv_rd32(priv, 0x610490 + (chid * 0x10))); return -EBUSY; } return 0; }
static void nv84_crypt_intr(struct nouveau_subdev *subdev) { struct nouveau_fifo *pfifo = nouveau_fifo(subdev); struct nouveau_engine *engine = nv_engine(subdev); struct nouveau_object *engctx; struct nv84_crypt_priv *priv = (void *)subdev; u32 stat = nv_rd32(priv, 0x102130); u32 mthd = nv_rd32(priv, 0x102190); u32 data = nv_rd32(priv, 0x102194); u32 inst = nv_rd32(priv, 0x102188) & 0x7fffffff; int chid; engctx = nouveau_engctx_get(engine, inst); chid = pfifo->chid(pfifo, engctx); if (stat) { nv_error(priv, "%s", ""); nouveau_bitfield_print(nv84_crypt_intr_mask, stat); pr_cont(" ch %d [0x%010llx %s] mthd 0x%04x data 0x%08x\n", chid, (u64)inst << 12, nouveau_client_name(engctx), mthd, data); } nv_wr32(priv, 0x102130, stat); nv_wr32(priv, 0x10200c, 0x10); nouveau_engctx_put(engctx); }
static void nv10_devinit_meminit(struct nouveau_devinit *devinit) { struct nv10_devinit_priv *priv = (void *)devinit; const int mem_width[] = { 0x10, 0x00, 0x20 }; const int mem_width_count = nv_device(priv)->chipset >= 0x17 ? 3 : 2; uint32_t patt = 0xdeadbeef; struct io_mapping *fb; int i, j, k; /* Map the framebuffer aperture */ fb = fbmem_init(nv_device(priv)->pdev); if (!fb) { nv_error(priv, "failed to map fb\n"); return; } nv_wr32(priv, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1); /* Probe memory bus width */ for (i = 0; i < mem_width_count; i++) { nv_mask(priv, NV04_PFB_CFG0, 0x30, mem_width[i]); for (j = 0; j < 4; j++) { for (k = 0; k < 4; k++) fbmem_poke(fb, 0x1c, 0); fbmem_poke(fb, 0x1c, patt); fbmem_poke(fb, 0x3c, 0); if (fbmem_peek(fb, 0x1c) == patt) goto mem_width_found; } } mem_width_found: patt <<= 1; /* Probe amount of installed memory */ for (i = 0; i < 4; i++) { int off = nv_rd32(priv, 0x10020c) - 0x100000; fbmem_poke(fb, off, patt); fbmem_poke(fb, 0, 0); fbmem_peek(fb, 0); fbmem_peek(fb, 0); fbmem_peek(fb, 0); fbmem_peek(fb, 0); if (fbmem_peek(fb, off) == patt) goto amount_found; } /* IC missing - disable the upper half memory space. */ nv_mask(priv, NV04_PFB_CFG0, 0x1000, 0); amount_found: fbmem_fini(fb); }
static int nvd0_disp_base_init(struct nouveau_object *object) { struct nv50_disp_priv *priv = (void *)object->engine; struct nv50_disp_base *base = (void *)object; int ret, i; u32 tmp; ret = nouveau_parent_init(&base->base); if (ret) return ret; /* The below segments of code copying values from one register to * another appear to inform EVO of the display capabilities or * something similar. */ /* ... CRTC caps */ for (i = 0; i < priv->head.nr; i++) { tmp = nv_rd32(priv, 0x616104 + (i * 0x800)); nv_wr32(priv, 0x6101b4 + (i * 0x800), tmp); tmp = nv_rd32(priv, 0x616108 + (i * 0x800)); nv_wr32(priv, 0x6101b8 + (i * 0x800), tmp); tmp = nv_rd32(priv, 0x61610c + (i * 0x800)); nv_wr32(priv, 0x6101bc + (i * 0x800), tmp); } /* ... DAC caps */ for (i = 0; i < priv->dac.nr; i++) { tmp = nv_rd32(priv, 0x61a000 + (i * 0x800)); nv_wr32(priv, 0x6101c0 + (i * 0x800), tmp); } /* ... SOR caps */ for (i = 0; i < priv->sor.nr; i++) { tmp = nv_rd32(priv, 0x61c000 + (i * 0x800)); nv_wr32(priv, 0x6301c4 + (i * 0x800), tmp); } /* steal display away from vbios, or something like that */ if (nv_rd32(priv, 0x6100ac) & 0x00000100) { nv_wr32(priv, 0x6100ac, 0x00000100); nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000); if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) { nv_error(priv, "timeout acquiring display\n"); return -EBUSY; } } /* point at display engine memory area (hash table, objects) */ nv_wr32(priv, 0x610010, (nv_gpuobj(object->parent)->addr >> 8) | 9); /* enable supervisor interrupts, disable everything else */ nv_wr32(priv, 0x610090, 0x00000000); nv_wr32(priv, 0x6100a0, 0x00000000); nv_wr32(priv, 0x6100b0, 0x00000307); return 0; }
int nva3_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info, u32 freq, int *pN, int *pfN, int *pM, int *P) { u32 best_err = ~0, err; int M, lM, hM, N, fN; *P = info->vco1.max_freq / freq; if (*P > info->max_p) *P = info->max_p; if (*P < info->min_p) *P = info->min_p; lM = (info->refclk + info->vco1.max_inputfreq) / info->vco1.max_inputfreq; lM = max(lM, (int)info->vco1.min_m); hM = (info->refclk + info->vco1.min_inputfreq) / info->vco1.min_inputfreq; hM = min(hM, (int)info->vco1.max_m); lM = min(lM, hM); for (M = lM; M <= hM; M++) { u32 tmp = freq * *P * M; N = tmp / info->refclk; fN = tmp % info->refclk; if (!pfN) { if (fN >= info->refclk / 2) N++; } else { if (fN < info->refclk / 2) N--; fN = tmp - (N * info->refclk); } if (N < info->vco1.min_n) continue; if (N > info->vco1.max_n) break; err = abs(freq - (info->refclk * N / M / *P)); if (err < best_err) { best_err = err; *pN = N; *pM = M; } if (pfN) { *pfN = ((fN << 13) + info->refclk / 2) / info->refclk; *pfN = (*pfN - 4096) & 0xffff; return freq; } } if (unlikely(best_err == ~0)) { nv_error(subdev, "unable to find matching pll values\n"); return -EINVAL; } return info->refclk * *pN / *pM / *P; }
void nv_wrvgai(void *obj, int head, u16 port, u8 index, u8 value) { if (port == 0x03c4) nv_wrvgas(obj, head, index, value); else if (port == 0x03ce) nv_wrvgag(obj, head, index, value); else if (port == 0x03d4) nv_wrvgac(obj, head, index, value); else nv_error(obj, "unknown indexed vga port 0x%04x\n", port); }
int metadata_write(struct hast_resource *res) { struct ebuf *eb; struct nv *nv; unsigned char *buf, *ptr; size_t size; ssize_t done; int ret; buf = calloc(1, METADATA_SIZE); if (buf == NULL) { pjdlog_error("Unable to allocate %zu bytes for metadata.", (size_t)METADATA_SIZE); return (-1); } ret = -1; nv = nv_alloc(); nv_add_string(nv, res->hr_name, "resource"); nv_add_uint64(nv, (uint64_t)res->hr_datasize, "datasize"); nv_add_uint32(nv, (uint32_t)res->hr_extentsize, "extentsize"); nv_add_uint32(nv, (uint32_t)res->hr_keepdirty, "keepdirty"); nv_add_uint64(nv, (uint64_t)res->hr_localoff, "offset"); nv_add_uint64(nv, res->hr_resuid, "resuid"); if (res->hr_role == HAST_ROLE_PRIMARY || res->hr_role == HAST_ROLE_INIT) { nv_add_uint64(nv, res->hr_primary_localcnt, "localcnt"); nv_add_uint64(nv, res->hr_primary_remotecnt, "remotecnt"); } else /* if (res->hr_role == HAST_ROLE_SECONDARY) */ { PJDLOG_ASSERT(res->hr_role == HAST_ROLE_SECONDARY); nv_add_uint64(nv, res->hr_secondary_localcnt, "localcnt"); nv_add_uint64(nv, res->hr_secondary_remotecnt, "remotecnt"); } nv_add_string(nv, role2str(res->hr_role), "prevrole"); if (nv_error(nv) != 0) { pjdlog_error("Unable to create metadata."); goto end; } res->hr_previous_role = res->hr_role; eb = nv_hton(nv); PJDLOG_ASSERT(eb != NULL); ptr = ebuf_data(eb, &size); PJDLOG_ASSERT(ptr != NULL); PJDLOG_ASSERT(size < METADATA_SIZE); bcopy(ptr, buf, size); done = pwrite(res->hr_localfd, buf, METADATA_SIZE, 0); if (done == -1 || done != METADATA_SIZE) { pjdlog_errno(LOG_ERR, "Unable to write metadata"); goto end; } ret = 0; end: free(buf); nv_free(nv); return (ret); }
static void nv50_disp_intr_error(struct nv50_disp_priv *priv, int chid) { struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass; u32 data = nv_rd32(priv, 0x610084 + (chid * 0x08)); u32 addr = nv_rd32(priv, 0x610080 + (chid * 0x08)); u32 code = (addr & 0x00ff0000) >> 16; u32 type = (addr & 0x00007000) >> 12; u32 mthd = (addr & 0x00000ffc); const struct nouveau_enum *ec, *et; char ecunk[6], etunk[6]; et = nouveau_enum_find(nv50_disp_intr_error_type, type); if (!et) snprintf(etunk, sizeof(etunk), "UNK%02X", type); ec = nouveau_enum_find(nv50_disp_intr_error_code, code); if (!ec) snprintf(ecunk, sizeof(ecunk), "UNK%02X", code); nv_error(priv, "%s [%s] chid %d mthd 0x%04x data 0x%08x\n", et ? et->name : etunk, ec ? ec->name : ecunk, chid, mthd, data); if (chid == 0) { switch (mthd) { case 0x0080: nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0, impl->mthd.core); break; default: break; } } else if (chid <= 2) { switch (mthd) { case 0x0080: nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1, impl->mthd.base); break; default: break; } } else if (chid <= 4) { switch (mthd) { case 0x0080: nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 3, impl->mthd.ovly); break; default: break; } } nv_wr32(priv, 0x610020, 0x00010000 << chid); nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000); }
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 nouveau_therm_fan_ctor(struct nouveau_therm *therm) { struct nouveau_therm_priv *priv = (void *)therm; struct nouveau_gpio *gpio = nouveau_gpio(therm); struct nouveau_bios *bios = nouveau_bios(therm); struct dcb_gpio_func func; int ret; /* attempt to locate a drivable fan, and determine control method */ ret = gpio->find(gpio, 0, DCB_GPIO_FAN, 0xff, &func); if (ret == 0) { /* FIXME: is this really the place to perform such checks ? */ if (func.line != 16 && func.log[0] & DCB_GPIO_LOG_DIR_IN) { nv_debug(therm, "GPIO_FAN is in input mode\n"); ret = -EINVAL; } else { ret = nouveau_fanpwm_create(therm, &func); if (ret != 0) ret = nouveau_fantog_create(therm, &func); } } /* no controllable fan found, create a dummy fan module */ if (ret != 0) { ret = nouveau_fannil_create(therm); if (ret) return ret; } nv_info(therm, "FAN control: %s\n", priv->fan->type); /* read the current speed, it is useful when resuming */ priv->fan->percent = nouveau_therm_fan_get(therm); /* attempt to detect a tachometer connection */ ret = gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &priv->fan->tach); if (ret) priv->fan->tach.func = DCB_GPIO_UNUSED; /* initialise fan bump/slow update handling */ priv->fan->parent = therm; nouveau_alarm_init(&priv->fan->alarm, nouveau_fan_alarm); spin_lock_init(&priv->fan->lock); /* other random init... */ nouveau_therm_fan_set_defaults(therm); nvbios_perf_fan_parse(bios, &priv->fan->perf); if (!nvbios_fan_parse(bios, &priv->fan->bios)) { nv_debug(therm, "parsing the fan table failed\n"); if (nvbios_therm_fan_parse(bios, &priv->fan->bios)) nv_error(therm, "parsing both fan tables failed\n"); } nouveau_therm_fan_safety_checks(therm); return 0; }
static int nv50_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq) { struct nv50_devinit_priv *priv = (void *)devinit; struct nouveau_bios *bios = nouveau_bios(priv); struct nvbios_pll info; int N1, M1, N2, M2, P; int ret; ret = nvbios_pll_parse(bios, type, &info); if (ret) { nv_error(devinit, "failed to retrieve pll data, %d\n", ret); return ret; } ret = nv04_pll_calc(nv_subdev(devinit), &info, freq, &N1, &M1, &N2, &M2, &P); if (!ret) { nv_error(devinit, "failed pll calculation\n"); return ret; } switch (info.type) { case PLL_VPLL0: case PLL_VPLL1: nv_wr32(priv, info.reg + 0, 0x10000611); nv_mask(priv, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1); nv_mask(priv, info.reg + 8, 0x7fff00ff, (P << 28) | (M2 << 16) | N2); break; case PLL_MEMORY: nv_mask(priv, info.reg + 0, 0x01ff0000, (P << 22) | (info.bias_p << 19) | (P << 16)); nv_wr32(priv, info.reg + 4, (N1 << 8) | M1); break; default: nv_mask(priv, info.reg + 0, 0x00070000, (P << 16)); nv_wr32(priv, info.reg + 4, (N1 << 8) | M1); break; } return 0; }
static u16 exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info) { struct nouveau_bios *bios = nouveau_bios(priv); u16 mask, type, data; if (outp < 4) { type = DCB_OUTPUT_ANALOG; mask = 0; } else { outp -= 4; switch (ctrl & 0x00000f00) { case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; default: nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); return 0x0000; } dcb->sorconf.link = mask; } mask = 0x00c0 & (mask << 6); mask |= 0x0001 << outp; mask |= 0x0100 << head; data = dcb_outp_match(bios, type, mask, ver, hdr, dcb); if (!data) return 0x0000; return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); }
static void nve0_graph_tp_trap(struct nvc0_graph_priv *priv, int gpc, int tp) { u32 stat = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x508)); if (stat & 0x1) { u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x224)); nv_error(priv, "GPC%i/TP%i/TEX trap: %08x\n", gpc, tp, trap); nv_wr32(priv, TPC_UNIT(gpc, tp, 0x224), 0xc0000000); stat &= ~0x1; } if (stat & 0x2) { nve0_graph_mp_trap(priv, gpc, tp); stat &= ~0x2; } if (stat & 0x4) { u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x084)); nv_error(priv, "GPC%i/TP%i/POLY trap: %08x\n", gpc, tp, trap); nv_wr32(priv, TPC_UNIT(gpc, tp, 0x084), 0xc0000000); stat &= ~0x4; } if (stat & 0x8) { u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x48c)); nv_error(priv, "GPC%i/TP%i/L1C trap: %08x\n", gpc, tp, trap); nv_wr32(priv, TPC_UNIT(gpc, tp, 0x48c), 0xc0000000); stat &= ~0x8; } if (stat) { nv_error(priv, "GPC%i/TP%i: unknown stat %08x\n", gpc, tp, stat); } }