From 7932c03f4646d8b154f9250035d7a3f4fe8b67e9 Mon Sep 17 00:00:00 2001 From: espes Date: Mon, 11 Feb 2013 10:47:06 +1100 Subject: [PATCH] enough of the apu to get it deiniting cleanly --- hw/mcpx_apu.c | 364 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 343 insertions(+), 21 deletions(-) diff --git a/hw/mcpx_apu.c b/hw/mcpx_apu.c index 359fb252aa..2b04e2ca6c 100644 --- a/hw/mcpx_apu.c +++ b/hw/mcpx_apu.c @@ -23,6 +23,89 @@ #include "mcpx_apu.h" + +#define NV_PAPU_ISTS 0x00001000 +# define NV_PAPU_ISTS_GINTSTS (1 << 0) +# define NV_PAPU_ISTS_FETINTSTS (1 << 4) +#define NV_PAPU_IEN 0x00001004 +#define NV_PAPU_FECTL 0x00001100 +# define NV_PAPU_FECTL_FEMETHMODE 0x000000E0 +# define NV_PAPU_FECTL_FEMETHMODE_FREE_RUNNING 0x00000000 +# define NV_PAPU_FECTL_FEMETHMODE_HALTED 0x00000080 +# define NV_PAPU_FECTL_FEMETHMODE_TRAPPED 0x000000E0 +# define NV_PAPU_FECTL_FETRAPREASON 0x00000F00 +# define NV_PAPU_FECTL_FETRAPREASON_REQUESTED 0x00000F00 +#define NV_PAPU_FECV 0x00001110 +#define NV_PAPU_FEAV 0x00001118 +# define NV_PAPU_FEAV_VALUE 0x0000FFFF +# define NV_PAPU_FEAV_LST 0x00030000 +#define NV_PAPU_FEDECMETH 0x00001300 +#define NV_PAPU_FEDECPARAM 0x00001304 +#define NV_PAPU_FETFORCE0 0x00001500 +#define NV_PAPU_FETFORCE1 0x00001504 +# define NV_PAPU_FETFORCE1_SE2FE_IDLE_VOICE (1 << 15) +#define NV_PAPU_SECTL 0x00002000 +# define NV_PAPU_SECTL_XCNTMODE 0x00000018 +# define NV_PAPU_SECTL_XCNTMODE_OFF 0 +#define NV_PAPU_VPVADDR 0x0000202C +#define NV_PAPU_TVL2D 0x00002054 +#define NV_PAPU_CVL2D 0x00002058 +#define NV_PAPU_NVL2D 0x0000205C +#define NV_PAPU_TVL3D 0x00002060 +#define NV_PAPU_CVL3D 0x00002064 +#define NV_PAPU_NVL3D 0x00002068 +#define NV_PAPU_TVLMP 0x0000206C +#define NV_PAPU_CVLMP 0x00002070 +#define NV_PAPU_NVLMP 0x00002074 + +static const struct { + hwaddr top, current, next; +} voice_list_regs[] = { + {NV_PAPU_TVL2D, NV_PAPU_CVL2D, NV_PAPU_NVL2D}, //2D + {NV_PAPU_TVL3D, NV_PAPU_CVL3D, NV_PAPU_NVL3D}, //3D + {NV_PAPU_TVLMP, NV_PAPU_CVLMP, NV_PAPU_NVLMP}, //MP +}; + + +/* audio processor object / front-end messages */ +#define NV1BA0_PIO_FREE 0x00000010 +#define NV1BA0_PIO_SET_ANTECEDENT_VOICE 0x00000120 +# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_HANDLE 0x0000FFFF +# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST 0x00030000 +# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_INHERIT 0 +# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_2D_TOP 1 +# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_3D_TOP 2 +# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_MP_TOP 3 +#define NV1BA0_PIO_VOICE_ON 0x00000124 +# define NV1BA0_PIO_VOICE_ON_HANDLE 0x0000FFFF +#define NV1BA0_PIO_VOICE_OFF 0x00000128 +#define NV1BA0_PIO_SET_CURRENT_VOICE 0x000002F8 + +#define SE2FE_IDLE_VOICE 0x00008000 + + +/* voice structure */ +#define NV_PAVS_SIZE 0x00000080 +#define NV_PAVS_VOICE_PAR_STATE 0x00000054 +# define NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE (1 << 21) +#define NV_PAVS_VOICE_TAR_PITCH_LINK 0x0000007c +# define NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE 0x0000FFFF + + + +#define MCPX_HW_MAX_VOICES 256 + + +#define GET_MASK(v, mask) (((v) & (mask)) >> (ffs(mask)-1)) + +#define SET_MASK(v, mask, val) \ + do { \ + (v) &= ~(mask); \ + (v) |= ((val) << (ffs(mask)-1)) & (mask); \ + } while (0) + + + //#define DEBUG #ifdef DEBUG # define MCPX_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) @@ -33,61 +116,293 @@ typedef struct MCPXAPUState { PCIDevice dev; - qemu_irq irq; MemoryRegion mmio; - MemoryRegion vp; + + /* Setup Engine */ + struct { + QEMUTimer *frame_timer; + } se; + + /* Voice Processor */ + struct { + MemoryRegion mmio; + } vp; + + /* Global Processor */ + struct { + MemoryRegion mmio; + } gp; + + uint32_t regs[0x20000]; + } MCPXAPUState; #define MCPX_APU_DEVICE(obj) \ OBJECT_CHECK(MCPXAPUState, (obj), "mcpx-apu") +static uint32_t voice_get_mask(MCPXAPUState *d, + unsigned int voice_handle, + hwaddr offset, + uint32_t mask) +{ + assert(voice_handle != 0xFFFF); + hwaddr voice = d->regs[NV_PAPU_VPVADDR] + + voice_handle * NV_PAVS_SIZE; + return (ldl_le_phys(voice + offset) & mask) >> (ffs(mask)-1); +} +static void voice_set_mask(MCPXAPUState *d, + unsigned int voice_handle, + hwaddr offset, + uint32_t mask, + uint32_t val) +{ + assert(voice_handle != 0xFFFF); + hwaddr voice = d->regs[NV_PAPU_VPVADDR] + + voice_handle * NV_PAVS_SIZE; + uint32_t v = ldl_le_phys(voice + offset) & ~mask; + stl_le_phys(voice + offset, + v | ((val << (ffs(mask)-1)) & mask)); +} + + + +static void update_irq(MCPXAPUState *d) +{ + if ((d->regs[NV_PAPU_IEN] & NV_PAPU_ISTS_GINTSTS) + && ((d->regs[NV_PAPU_ISTS] & ~NV_PAPU_ISTS_GINTSTS) + & d->regs[NV_PAPU_IEN])) { + + d->regs[NV_PAPU_ISTS] |= NV_PAPU_ISTS_GINTSTS; + MCPX_DPRINTF("mcpx irq raise\n"); + qemu_irq_raise(d->irq); + } else { + d->regs[NV_PAPU_ISTS] &= ~NV_PAPU_ISTS_GINTSTS; + MCPX_DPRINTF("mcpx irq lower\n"); + qemu_irq_lower(d->irq); + } +} static uint64_t mcpx_apu_read(void *opaque, - hwaddr addr, unsigned int size) + hwaddr addr, unsigned int size) { - MCPX_DPRINTF("mcpx apu: read [0x%llx]\n", addr); - return 0; + MCPXAPUState *d = opaque; + + uint64_t r = 0; + switch (addr) { + default: + if (addr < 0x20000) { + r = d->regs[addr]; + } + break; + } + + MCPX_DPRINTF("mcpx apu: read [0x%llx] -> 0x%llx\n", addr, r); + return r; } static void mcpx_apu_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) + uint64_t val, unsigned int size) { + MCPXAPUState *d = opaque; + MCPX_DPRINTF("mcpx apu: [0x%llx] = 0x%llx\n", addr, val); + + switch (addr) { + case NV_PAPU_ISTS: + /* the bits of the interrupts to clear are wrtten */ + d->regs[NV_PAPU_ISTS] &= ~val; + update_irq(d); + break; + case NV_PAPU_SECTL: + if ( ((val & NV_PAPU_SECTL_XCNTMODE) >> 3) + == NV_PAPU_SECTL_XCNTMODE_OFF) { + qemu_del_timer(d->se.frame_timer); + } else { + qemu_mod_timer(d->se.frame_timer, qemu_get_clock_ms(vm_clock) + 10); + } + d->regs[addr] = val; + default: + if (addr < 0x20000) { + d->regs[addr] = val; + } + break; + } +} +static const MemoryRegionOps mcpx_apu_mmio_ops = { + .read = mcpx_apu_read, + .write = mcpx_apu_write, +}; + + +static void fe_method(MCPXAPUState *d, + uint32_t method, uint32_t argument) +{ + MCPX_DPRINTF("mcpx fe_method 0x%x 0x%x\n", method, argument); + + //assert((d->regs[NV_PAPU_FECTL] & NV_PAPU_FECTL_FEMETHMODE) == 0); + + d->regs[NV_PAPU_FEDECMETH] = method; + d->regs[NV_PAPU_FEDECPARAM] = argument; + unsigned int selected_handle, list; + switch (method) { + case NV1BA0_PIO_SET_ANTECEDENT_VOICE: + d->regs[NV_PAPU_FEAV] = argument; + break; + case NV1BA0_PIO_VOICE_ON: + selected_handle = argument & NV1BA0_PIO_VOICE_ON_HANDLE; + list = GET_MASK(d->regs[NV_PAPU_FEAV], NV_PAPU_FEAV_LST); + if (list != NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_INHERIT) { + /* voice is added to the top of the selected list */ + unsigned int top_reg = voice_list_regs[list-1].top; + voice_set_mask(d, selected_handle, + NV_PAVS_VOICE_TAR_PITCH_LINK, + NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE, + d->regs[top_reg]); + d->regs[top_reg] = selected_handle; + } else { + unsigned int antecedent_voice = + GET_MASK(d->regs[NV_PAPU_FEAV], NV_PAPU_FEAV_VALUE); + /* voice is added after the antecedent voice */ + assert(antecedent_voice != 0xFFFF); + + uint32_t next_handle = voice_get_mask(d, antecedent_voice, + NV_PAVS_VOICE_TAR_PITCH_LINK, + NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE); + voice_set_mask(d, selected_handle, + NV_PAVS_VOICE_TAR_PITCH_LINK, + NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE, + next_handle); + voice_set_mask(d, antecedent_voice, + NV_PAVS_VOICE_TAR_PITCH_LINK, + NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE, + selected_handle); + + voice_set_mask(d, selected_handle, + NV_PAVS_VOICE_PAR_STATE, + NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE, + 1); + } + break; + case NV1BA0_PIO_VOICE_OFF: + voice_set_mask(d, argument, + NV_PAVS_VOICE_PAR_STATE, + NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE, + 0); + break; + case NV1BA0_PIO_SET_CURRENT_VOICE: + d->regs[NV_PAPU_FECV] = argument; + break; + case SE2FE_IDLE_VOICE: + if (d->regs[NV_PAPU_FETFORCE1] & NV_PAPU_FETFORCE1_SE2FE_IDLE_VOICE) { + + d->regs[NV_PAPU_FECTL] &= ~NV_PAPU_FECTL_FEMETHMODE; + d->regs[NV_PAPU_FECTL] |= NV_PAPU_FECTL_FEMETHMODE_TRAPPED; + + d->regs[NV_PAPU_FECTL] &= ~NV_PAPU_FECTL_FETRAPREASON; + d->regs[NV_PAPU_FECTL] |= NV_PAPU_FECTL_FETRAPREASON_REQUESTED; + + d->regs[NV_PAPU_ISTS] |= NV_PAPU_ISTS_FETINTSTS; + update_irq(d); + } else { + assert(false); + } + break; + default: + assert(false); + break; + } } -/* Voice Processor */ -static uint64_t mcpx_apu_vp_read(void *opaque, - hwaddr addr, unsigned int size) +static uint64_t vp_read(void *opaque, + hwaddr addr, unsigned int size) { MCPX_DPRINTF("mcpx apu VP: read [0x%llx]\n", addr); switch (addr) { - case 0x10: /* instruction queue free space */ + case NV1BA0_PIO_FREE: + /* we don't simulate the queue for now, + * pretend to always be empty */ return 0x80; default: break; } return 0; } -static void mcpx_apu_vp_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) +static void vp_write(void *opaque, hwaddr addr, + uint64_t val, unsigned int size) { + MCPXAPUState *d = opaque; + MCPX_DPRINTF("mcpx apu VP: [0x%llx] = 0x%llx\n", addr, val); + + switch (addr) { + case NV1BA0_PIO_SET_ANTECEDENT_VOICE: + case NV1BA0_PIO_VOICE_ON: + case NV1BA0_PIO_VOICE_OFF: + case NV1BA0_PIO_SET_CURRENT_VOICE: + /* TODO: these should instead be queueing up fe commands */ + fe_method(d, addr, val); + break; + default: + break; + } } +static const MemoryRegionOps vp_ops = { + .read = vp_read, + .write = vp_write, +}; -static const MemoryRegionOps mcpx_apu_mmio_ops = { - .read = mcpx_apu_read, - .write = mcpx_apu_write, -}; -static const MemoryRegionOps mcpx_apu_vp_ops = { - .read = mcpx_apu_vp_read, - .write = mcpx_apu_vp_write, +/* Global Processor - programmable DSP */ +static uint64_t gp_read(void *opaque, + hwaddr addr, unsigned int size) +{ + MCPX_DPRINTF("mcpx apu GP: read [0x%llx]\n", addr); + return 0; +} +static void gp_write(void *opaque, hwaddr addr, + uint64_t val, unsigned int size) +{ + MCPX_DPRINTF("mcpx apu GP: [0x%llx] = 0x%llx\n", addr, val); +} +static const MemoryRegionOps gp_ops = { + .read = gp_read, + .write = gp_write, }; +/* TODO: this should be on a thread so it waits on the voice lock */ +static void se_frame(void *opaque) +{ + MCPXAPUState *d = opaque; + qemu_mod_timer(d->se.frame_timer, qemu_get_clock_ms(vm_clock) + 10); + MCPX_DPRINTF("mcpx frame ping\n"); + int list; + for (list=0; list < 3; list++) { + hwaddr top, current, next; + top = voice_list_regs[list].top; + current = voice_list_regs[list].current; + next = voice_list_regs[list].next; + + d->regs[current] = d->regs[top]; + while (d->regs[current] != 0xFFFF) { + d->regs[next] = voice_get_mask(d, d->regs[current], + NV_PAVS_VOICE_TAR_PITCH_LINK, + NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE); + if (!voice_get_mask(d, d->regs[current], + NV_PAVS_VOICE_PAR_STATE, + NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE)) { + MCPX_DPRINTF("voice %d not active...!\n", d->regs[current]); + fe_method(d, SE2FE_IDLE_VOICE, d->regs[current]); + } + d->regs[current] = d->regs[next]; + } + } +} + + static int mcpx_apu_initfn(PCIDevice *dev) { MCPXAPUState *d = MCPX_APU_DEVICE(dev); @@ -95,12 +410,19 @@ static int mcpx_apu_initfn(PCIDevice *dev) memory_region_init_io(&d->mmio, &mcpx_apu_mmio_ops, d, "mcpx-apu-mmio", 0x80000); - memory_region_init_io(&d->vp, &mcpx_apu_vp_ops, d, + memory_region_init_io(&d->vp.mmio, &vp_ops, d, "mcpx-apu-vp", 0x10000); - memory_region_add_subregion(&d->mmio, 0x20000, &d->vp); + memory_region_add_subregion(&d->mmio, 0x20000, &d->vp.mmio); + + memory_region_init_io(&d->gp.mmio, &gp_ops, d, + "mcpx-apu-gp", 0x10000); + memory_region_add_subregion(&d->mmio, 0x30000, &d->gp.mmio); pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); + + d->se.frame_timer = qemu_new_timer_ms(vm_clock, se_frame, d); + return 0; }