From d8f699cb32c8c418b65aa6a2c252e097ae4716ae Mon Sep 17 00:00:00 2001 From: balrog Date: Sun, 4 Nov 2007 22:53:50 +0000 Subject: [PATCH] Zeroing ITR shouldn't ack irq zero. Fix PWT & PWL clocks, fix user refcounting for clocks, add 'hsab_ck' and 'usb_w2fc_ck'. Fix TCMI register addresses. Implement OMAP McBSP controller and connection to I2S-compatible CODECs. Add audio support for TSC2102 as an I2S CODEC. Connect TSC2102 I2S interface to CPU's McBSP1 interface in the Palm Tungsten|E. Correct '>' instead of '>>' typos. Implement GPIO PIN_CONTROL register (not in OMAP310 TRM, from OMAP1510). git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3534 c046a42c-6fe2-441c-8c8c-71466251a162 --- hw/omap.c | 568 +++++++++++++++++++++++++++++++++++++++++++++---- hw/omap.h | 123 +++++++++++ hw/omap1_clk.c | 20 +- hw/palm.c | 17 +- hw/tsc210x.c | 188 +++++++++++++++- vl.h | 3 +- 6 files changed, 858 insertions(+), 61 deletions(-) diff --git a/hw/omap.c b/hw/omap.c index 66e3b51f39..4efc5fa1be 100644 --- a/hw/omap.c +++ b/hw/omap.c @@ -254,7 +254,7 @@ static void omap_inth_write(void *opaque, target_phys_addr_t addr, switch (offset) { case 0x00: /* ITR */ - s->irqs &= value; + s->irqs &= value | 1; omap_inth_sir_update(s); omap_inth_update(s); return; @@ -992,7 +992,7 @@ static void omap_dma_clk_update(void *opaque, int line, int on) struct omap_dma_s *s = (struct omap_dma_s *) opaque; if (on) { - s->delay = ticks_per_sec >> 5; + s->delay = ticks_per_sec >> 7; if (s->run_count) qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay); } else { @@ -1325,8 +1325,10 @@ static void omap_wd_timer_write(void *opaque, target_phys_addr_t addr, s->mode |= (value >> 15) & 1; if (s->last_wr == 0xf5) { if ((value & 0xff) == 0xa0) { - s->mode = 0; - omap_clk_put(s->timer.clk); + if (s->mode) { + s->mode = 0; + omap_clk_put(s->timer.clk); + } } else { /* XXX: on T|E hardware somehow this has no effect, * on Zire 71 it works as specified. */ @@ -2217,23 +2219,23 @@ static uint32_t omap_tcmi_read(void *opaque, target_phys_addr_t addr) uint32_t ret; switch (offset) { - case 0xfffecc00: /* IMIF_PRIO */ - case 0xfffecc04: /* EMIFS_PRIO */ - case 0xfffecc08: /* EMIFF_PRIO */ - case 0xfffecc0c: /* EMIFS_CONFIG */ - case 0xfffecc10: /* EMIFS_CS0_CONFIG */ - case 0xfffecc14: /* EMIFS_CS1_CONFIG */ - case 0xfffecc18: /* EMIFS_CS2_CONFIG */ - case 0xfffecc1c: /* EMIFS_CS3_CONFIG */ - case 0xfffecc24: /* EMIFF_MRS */ - case 0xfffecc28: /* TIMEOUT1 */ - case 0xfffecc2c: /* TIMEOUT2 */ - case 0xfffecc30: /* TIMEOUT3 */ - case 0xfffecc3c: /* EMIFF_SDRAM_CONFIG_2 */ - case 0xfffecc40: /* EMIFS_CFG_DYN_WAIT */ + case 0x00: /* IMIF_PRIO */ + case 0x04: /* EMIFS_PRIO */ + case 0x08: /* EMIFF_PRIO */ + case 0x0c: /* EMIFS_CONFIG */ + case 0x10: /* EMIFS_CS0_CONFIG */ + case 0x14: /* EMIFS_CS1_CONFIG */ + case 0x18: /* EMIFS_CS2_CONFIG */ + case 0x1c: /* EMIFS_CS3_CONFIG */ + case 0x24: /* EMIFF_MRS */ + case 0x28: /* TIMEOUT1 */ + case 0x2c: /* TIMEOUT2 */ + case 0x30: /* TIMEOUT3 */ + case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ + case 0x40: /* EMIFS_CFG_DYN_WAIT */ return s->tcmi_regs[offset >> 2]; - case 0xfffecc20: /* EMIFF_SDRAM_CONFIG */ + case 0x20: /* EMIFF_SDRAM_CONFIG */ ret = s->tcmi_regs[offset >> 2]; s->tcmi_regs[offset >> 2] &= ~1; /* XXX: Clear SLRF on SDRAM access */ /* XXX: We can try using the VGA_DIRTY flag for this */ @@ -2251,23 +2253,23 @@ static void omap_tcmi_write(void *opaque, target_phys_addr_t addr, int offset = addr - s->tcmi_base; switch (offset) { - case 0xfffecc00: /* IMIF_PRIO */ - case 0xfffecc04: /* EMIFS_PRIO */ - case 0xfffecc08: /* EMIFF_PRIO */ - case 0xfffecc10: /* EMIFS_CS0_CONFIG */ - case 0xfffecc14: /* EMIFS_CS1_CONFIG */ - case 0xfffecc18: /* EMIFS_CS2_CONFIG */ - case 0xfffecc1c: /* EMIFS_CS3_CONFIG */ - case 0xfffecc20: /* EMIFF_SDRAM_CONFIG */ - case 0xfffecc24: /* EMIFF_MRS */ - case 0xfffecc28: /* TIMEOUT1 */ - case 0xfffecc2c: /* TIMEOUT2 */ - case 0xfffecc30: /* TIMEOUT3 */ - case 0xfffecc3c: /* EMIFF_SDRAM_CONFIG_2 */ - case 0xfffecc40: /* EMIFS_CFG_DYN_WAIT */ + case 0x00: /* IMIF_PRIO */ + case 0x04: /* EMIFS_PRIO */ + case 0x08: /* EMIFF_PRIO */ + case 0x10: /* EMIFS_CS0_CONFIG */ + case 0x14: /* EMIFS_CS1_CONFIG */ + case 0x18: /* EMIFS_CS2_CONFIG */ + case 0x1c: /* EMIFS_CS3_CONFIG */ + case 0x20: /* EMIFF_SDRAM_CONFIG */ + case 0x24: /* EMIFF_MRS */ + case 0x28: /* TIMEOUT1 */ + case 0x2c: /* TIMEOUT2 */ + case 0x30: /* TIMEOUT3 */ + case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ + case 0x40: /* EMIFS_CFG_DYN_WAIT */ s->tcmi_regs[offset >> 2] = value; break; - case 0xfffecc0c: /* EMIFS_CONFIG */ + case 0x0c: /* EMIFS_CONFIG */ s->tcmi_regs[offset >> 2] = (value & 0xf) | (1 << 4); break; @@ -2441,7 +2443,7 @@ static uint32_t omap_clkm_read(void *opaque, target_phys_addr_t addr) return s->clkm.arm_rstct2; case 0x18: /* ARM_SYSST */ - return (s->clkm.clocking_scheme < 11) | s->clkm.cold_start; + return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start; case 0x1c: /* ARM_CKOUT1 */ return s->clkm.arm_ckout1; @@ -2720,7 +2722,7 @@ static uint32_t omap_clkdsp_read(void *opaque, target_phys_addr_t addr) return s->clkm.dsp_rstct2; case 0x18: /* DSP_SYSST */ - return (s->clkm.clocking_scheme < 11) | s->clkm.cold_start | + return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start | (s->env->halted << 6); /* Quite useless... */ } @@ -2796,9 +2798,9 @@ static void omap_clkm_reset(struct omap_mpu_state_s *s) s->clkm.clocking_scheme = 0; omap_clkm_ckctl_update(s, ~0, 0x3000); s->clkm.arm_ckctl = 0x3000; - omap_clkm_idlect1_update(s, s->clkm.arm_idlect1 & 0x0400, 0x0400); + omap_clkm_idlect1_update(s, s->clkm.arm_idlect1 ^ 0x0400, 0x0400); s->clkm.arm_idlect1 = 0x0400; - omap_clkm_idlect2_update(s, s->clkm.arm_idlect2 & 0x0100, 0x0100); + omap_clkm_idlect2_update(s, s->clkm.arm_idlect2 ^ 0x0100, 0x0100); s->clkm.arm_idlect2 = 0x0100; s->clkm.arm_ewupct = 0x003f; s->clkm.arm_rstct1 = 0x0000; @@ -2822,8 +2824,11 @@ static void omap_clkm_init(target_phys_addr_t mpu_base, s->clkm.mpu_base = mpu_base; s->clkm.dsp_base = dsp_base; - s->clkm.cold_start = 0x3a; + s->clkm.arm_idlect1 = 0x03ff; + s->clkm.arm_idlect2 = 0x0100; + s->clkm.dsp_idlect1 = 0x0002; omap_clkm_reset(s); + s->clkm.cold_start = 0x3a; cpu_register_physical_memory(s->clkm.mpu_base, 0x100, iomemtype[0]); cpu_register_physical_memory(s->clkm.dsp_base, 0x1000, iomemtype[1]); @@ -2956,9 +2961,8 @@ static void omap_mpuio_write(void *opaque, target_phys_addr_t addr, switch (offset) { case 0x04: /* OUTPUT_REG */ - diff = s->outputs ^ (value & ~s->dir); + diff = (s->outputs ^ value) & ~s->dir; s->outputs = value; - value &= ~s->dir; while ((ln = ffs(diff))) { ln --; if (s->handler[ln]) @@ -3120,6 +3124,7 @@ struct omap_gpio_s { uint16_t edge; uint16_t mask; uint16_t ints; + uint16_t pins; }; static void omap_gpio_set(void *opaque, int line, int level) @@ -3146,7 +3151,7 @@ static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr) switch (offset) { case 0x00: /* DATA_INPUT */ - return s->inputs; + return s->inputs & s->pins; case 0x04: /* DATA_OUTPUT */ return s->outputs; @@ -3162,6 +3167,10 @@ static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr) case 0x14: /* INTERRUPT_STATUS */ return s->ints; + + case 0x18: /* PIN_CONTROL (not in OMAP310) */ + OMAP_BAD_REG(addr); + return s->pins; } OMAP_BAD_REG(addr); @@ -3219,6 +3228,11 @@ static void omap_gpio_write(void *opaque, target_phys_addr_t addr, qemu_irq_lower(s->irq); break; + case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ + OMAP_BAD_REG(addr); + s->pins = value; + break; + default: OMAP_BAD_REG(addr); return; @@ -3246,6 +3260,7 @@ void omap_gpio_reset(struct omap_gpio_s *s) s->edge = ~0; s->mask = ~0; s->ints = 0; + s->pins = ~0; } struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, @@ -4058,6 +4073,458 @@ struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base, return s; } +/* Multi-channel Buffered Serial Port interfaces */ +struct omap_mcbsp_s { + target_phys_addr_t base; + qemu_irq txirq; + qemu_irq rxirq; + qemu_irq txdrq; + qemu_irq rxdrq; + + uint16_t spcr[2]; + uint16_t rcr[2]; + uint16_t xcr[2]; + uint16_t srgr[2]; + uint16_t mcr[2]; + uint16_t pcr; + uint16_t rcer[8]; + uint16_t xcer[8]; + int tx_rate; + int rx_rate; + int tx_req; + + struct i2s_codec_s *codec; +}; + +static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) +{ + int irq; + + switch ((s->spcr[0] >> 4) & 3) { /* RINTM */ + case 0: + irq = (s->spcr[0] >> 1) & 1; /* RRDY */ + break; + case 3: + irq = (s->spcr[0] >> 3) & 1; /* RSYNCERR */ + break; + default: + irq = 0; + break; + } + + qemu_set_irq(s->rxirq, irq); + + switch ((s->spcr[1] >> 4) & 3) { /* XINTM */ + case 0: + irq = (s->spcr[1] >> 1) & 1; /* XRDY */ + break; + case 3: + irq = (s->spcr[1] >> 3) & 1; /* XSYNCERR */ + break; + default: + irq = 0; + break; + } + + qemu_set_irq(s->txirq, irq); +} + +static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) +{ + int prev = s->tx_req; + + s->tx_req = (s->tx_rate || + (s->spcr[0] & (1 << 12))) && /* CLKSTP */ + (s->spcr[1] & (1 << 6)) && /* GRST */ + (s->spcr[1] & (1 << 0)); /* XRST */ + + if (!s->tx_req && prev) { + s->spcr[1] &= ~(1 << 1); /* XRDY */ + qemu_irq_lower(s->txdrq); + omap_mcbsp_intr_update(s); + + if (s->codec) + s->codec->tx_swallow(s->codec->opaque); + } else if (s->codec && s->tx_req && !prev) { + s->spcr[1] |= 1 << 1; /* XRDY */ + qemu_irq_raise(s->txdrq); + omap_mcbsp_intr_update(s); + } +} + +static void omap_mcbsp_rate_update(struct omap_mcbsp_s *s) +{ + int rx_clk = 0, tx_clk = 0; + int cpu_rate = 1500000; /* XXX */ + if (!s->codec) + return; + + if (s->spcr[1] & (1 << 6)) { /* GRST */ + if (s->spcr[0] & (1 << 0)) /* RRST */ + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ + (s->pcr & (1 << 8))) /* CLKRM */ + if (~s->pcr & (1 << 7)) /* SCLKME */ + rx_clk = cpu_rate / + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ + if (s->spcr[1] & (1 << 0)) /* XRST */ + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ + (s->pcr & (1 << 9))) /* CLKXM */ + if (~s->pcr & (1 << 7)) /* SCLKME */ + tx_clk = cpu_rate / + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ + } + + s->codec->set_rate(s->codec->opaque, rx_clk, tx_clk); +} + +static void omap_mcbsp_rx_start(struct omap_mcbsp_s *s) +{ + if (!(s->spcr[0] & 1)) { /* RRST */ + if (s->codec) + s->codec->in.len = 0; + return; + } + + if ((s->spcr[0] >> 1) & 1) /* RRDY */ + s->spcr[0] |= 1 << 2; /* RFULL */ + s->spcr[0] |= 1 << 1; /* RRDY */ + qemu_irq_raise(s->rxdrq); + omap_mcbsp_intr_update(s); +} + +static void omap_mcbsp_rx_stop(struct omap_mcbsp_s *s) +{ + s->spcr[0] &= ~(1 << 1); /* RRDY */ + qemu_irq_lower(s->rxdrq); + omap_mcbsp_intr_update(s); +} + +static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s) +{ + if (s->tx_rate) + return; + s->tx_rate = 1; + omap_mcbsp_req_update(s); +} + +static void omap_mcbsp_tx_stop(struct omap_mcbsp_s *s) +{ + s->tx_rate = 0; + omap_mcbsp_req_update(s); +} + +static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + int offset = addr & OMAP_MPUI_REG_MASK; + uint16_t ret; + + switch (offset) { + case 0x00: /* DRR2 */ + if (((s->rcr[0] >> 5) & 7) < 3) /* RWDLEN1 */ + return 0x0000; + /* Fall through. */ + case 0x02: /* DRR1 */ + if (!s->codec) + return 0x0000; + if (s->codec->in.len < 2) { + printf("%s: Rx FIFO underrun\n", __FUNCTION__); + omap_mcbsp_rx_stop(s); + } else { + s->codec->in.len -= 2; + ret = s->codec->in.fifo[s->codec->in.start ++] << 8; + ret |= s->codec->in.fifo[s->codec->in.start ++]; + if (!s->codec->in.len) + omap_mcbsp_rx_stop(s); + return ret; + } + return 0x0000; + + case 0x04: /* DXR2 */ + case 0x06: /* DXR1 */ + return 0x0000; + + case 0x08: /* SPCR2 */ + return s->spcr[1]; + case 0x0a: /* SPCR1 */ + return s->spcr[0]; + case 0x0c: /* RCR2 */ + return s->rcr[1]; + case 0x0e: /* RCR1 */ + return s->rcr[0]; + case 0x10: /* XCR2 */ + return s->xcr[1]; + case 0x12: /* XCR1 */ + return s->xcr[0]; + case 0x14: /* SRGR2 */ + return s->srgr[1]; + case 0x16: /* SRGR1 */ + return s->srgr[0]; + case 0x18: /* MCR2 */ + return s->mcr[1]; + case 0x1a: /* MCR1 */ + return s->mcr[0]; + case 0x1c: /* RCERA */ + return s->rcer[0]; + case 0x1e: /* RCERB */ + return s->rcer[1]; + case 0x20: /* XCERA */ + return s->xcer[0]; + case 0x22: /* XCERB */ + return s->xcer[1]; + case 0x24: /* PCR0 */ + return s->pcr; + case 0x26: /* RCERC */ + return s->rcer[2]; + case 0x28: /* RCERD */ + return s->rcer[3]; + case 0x2a: /* XCERC */ + return s->xcer[2]; + case 0x2c: /* XCERD */ + return s->xcer[3]; + case 0x2e: /* RCERE */ + return s->rcer[4]; + case 0x30: /* RCERF */ + return s->rcer[5]; + case 0x32: /* XCERE */ + return s->xcer[4]; + case 0x34: /* XCERF */ + return s->xcer[5]; + case 0x36: /* RCERG */ + return s->rcer[6]; + case 0x38: /* RCERH */ + return s->rcer[7]; + case 0x3a: /* XCERG */ + return s->xcer[6]; + case 0x3c: /* XCERH */ + return s->xcer[7]; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + int offset = addr & OMAP_MPUI_REG_MASK; + + switch (offset) { + case 0x00: /* DRR2 */ + case 0x02: /* DRR1 */ + OMAP_RO_REG(addr); + return; + + case 0x04: /* DXR2 */ + if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ + return; + /* Fall through. */ + case 0x06: /* DXR1 */ + if (!s->codec) + return; + if (s->tx_req) { + if (s->codec->out.len > s->codec->out.size - 2) { + printf("%s: Tx FIFO overrun\n", __FUNCTION__); + omap_mcbsp_tx_stop(s); + } else { + s->codec->out.fifo[s->codec->out.len ++] = (value >> 8) & 0xff; + s->codec->out.fifo[s->codec->out.len ++] = (value >> 0) & 0xff; + if (s->codec->out.len >= s->codec->out.size) + omap_mcbsp_tx_stop(s); + } + } else + printf("%s: Tx FIFO overrun\n", __FUNCTION__); + return; + + case 0x08: /* SPCR2 */ + s->spcr[1] &= 0x0002; + s->spcr[1] |= 0x03f9 & value; + s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ + if (~value & 1) { /* XRST */ + s->spcr[1] &= ~6; + qemu_irq_lower(s->rxdrq); + if (s->codec) + s->codec->out.len = 0; + } + if (s->codec) + omap_mcbsp_rate_update(s); + omap_mcbsp_req_update(s); + return; + case 0x0a: /* SPCR1 */ + s->spcr[0] &= 0x0006; + s->spcr[0] |= 0xf8f9 & value; + if (value & (1 << 15)) /* DLB */ + printf("%s: Digital Loopback mode enable attempt\n", __FUNCTION__); + if (~value & 1) { /* RRST */ + s->spcr[0] &= ~6; + qemu_irq_lower(s->txdrq); + if (s->codec) + s->codec->in.len = 0; + } + if (s->codec) + omap_mcbsp_rate_update(s); + omap_mcbsp_req_update(s); + return; + + case 0x0c: /* RCR2 */ + s->rcr[1] = value & 0xffff; + return; + case 0x0e: /* RCR1 */ + s->rcr[0] = value & 0x7fe0; + return; + case 0x10: /* XCR2 */ + s->xcr[1] = value & 0xffff; + return; + case 0x12: /* XCR1 */ + s->xcr[0] = value & 0x7fe0; + return; + case 0x14: /* SRGR2 */ + s->srgr[1] = value & 0xffff; + omap_mcbsp_rate_update(s); + return; + case 0x16: /* SRGR1 */ + s->srgr[0] = value & 0xffff; + omap_mcbsp_rate_update(s); + return; + case 0x18: /* MCR2 */ + s->mcr[1] = value & 0x03e3; + if (value & 3) /* XMCM */ + printf("%s: Tx channel selection mode enable attempt\n", + __FUNCTION__); + return; + case 0x1a: /* MCR1 */ + s->mcr[0] = value & 0x03e1; + if (value & 1) /* RMCM */ + printf("%s: Rx channel selection mode enable attempt\n", + __FUNCTION__); + return; + case 0x1c: /* RCERA */ + s->rcer[0] = value & 0xffff; + return; + case 0x1e: /* RCERB */ + s->rcer[1] = value & 0xffff; + return; + case 0x20: /* XCERA */ + s->xcer[0] = value & 0xffff; + return; + case 0x22: /* XCERB */ + s->xcer[1] = value & 0xffff; + return; + case 0x24: /* PCR0 */ + s->pcr = value & 0x7faf; + return; + case 0x26: /* RCERC */ + s->rcer[2] = value & 0xffff; + return; + case 0x28: /* RCERD */ + s->rcer[3] = value & 0xffff; + return; + case 0x2a: /* XCERC */ + s->xcer[2] = value & 0xffff; + return; + case 0x2c: /* XCERD */ + s->xcer[3] = value & 0xffff; + return; + case 0x2e: /* RCERE */ + s->rcer[4] = value & 0xffff; + return; + case 0x30: /* RCERF */ + s->rcer[5] = value & 0xffff; + return; + case 0x32: /* XCERE */ + s->xcer[4] = value & 0xffff; + return; + case 0x34: /* XCERF */ + s->xcer[5] = value & 0xffff; + return; + case 0x36: /* RCERG */ + s->rcer[6] = value & 0xffff; + return; + case 0x38: /* RCERH */ + s->rcer[7] = value & 0xffff; + return; + case 0x3a: /* XCERG */ + s->xcer[6] = value & 0xffff; + return; + case 0x3c: /* XCERH */ + s->xcer[7] = value & 0xffff; + return; + } + + OMAP_BAD_REG(addr); +} + +static CPUReadMemoryFunc *omap_mcbsp_readfn[] = { + omap_badwidth_read16, + omap_mcbsp_read, + omap_badwidth_read16, +}; + +static CPUWriteMemoryFunc *omap_mcbsp_writefn[] = { + omap_badwidth_write16, + omap_mcbsp_write, + omap_badwidth_write16, +}; + +static void omap_mcbsp_reset(struct omap_mcbsp_s *s) +{ + memset(&s->spcr, 0, sizeof(s->spcr)); + memset(&s->rcr, 0, sizeof(s->rcr)); + memset(&s->xcr, 0, sizeof(s->xcr)); + s->srgr[0] = 0x0001; + s->srgr[1] = 0x2000; + memset(&s->mcr, 0, sizeof(s->mcr)); + memset(&s->pcr, 0, sizeof(s->pcr)); + memset(&s->rcer, 0, sizeof(s->rcer)); + memset(&s->xcer, 0, sizeof(s->xcer)); + s->tx_req = 0; + s->tx_rate = 0; + s->rx_rate = 0; +} + +struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base, + qemu_irq *irq, qemu_irq *dma, omap_clk clk) +{ + int iomemtype; + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) + qemu_mallocz(sizeof(struct omap_mcbsp_s)); + + s->base = base; + s->txirq = irq[0]; + s->rxirq = irq[1]; + s->txdrq = dma[0]; + s->rxdrq = dma[1]; + omap_mcbsp_reset(s); + + iomemtype = cpu_register_io_memory(0, omap_mcbsp_readfn, + omap_mcbsp_writefn, s); + cpu_register_physical_memory(s->base, 0x800, iomemtype); + + return s; +} + +void omap_mcbsp_i2s_swallow(void *opaque, int line, int level) +{ + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + + omap_mcbsp_rx_start(s); +} + +void omap_mcbsp_i2s_start(void *opaque, int line, int level) +{ + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + + omap_mcbsp_tx_start(s); +} + +void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, struct i2s_codec_s *slave) +{ + s->codec = slave; + slave->rx_swallow = qemu_allocate_irqs(omap_mcbsp_i2s_swallow, s, 1)[0]; + slave->tx_start = qemu_allocate_irqs(omap_mcbsp_i2s_start, s, 1)[0]; +} + /* General chip reset */ static void omap_mpu_reset(void *opaque) { @@ -4092,6 +4559,9 @@ static void omap_mpu_reset(void *opaque) omap_pwt_reset(mpu); omap_i2c_reset(mpu->i2c); omap_rtc_reset(mpu->rtc); + omap_mcbsp_reset(mpu->mcbsp1); + omap_mcbsp_reset(mpu->mcbsp2); + omap_mcbsp_reset(mpu->mcbsp3); cpu_reset(mpu->env); } @@ -4254,8 +4724,8 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, s->microwire = omap_uwire_init(0xfffb3000, &s->irq[1][OMAP_INT_uWireTX], s->drq[OMAP_DMA_UWIRE_TX], omap_findclk(s, "mpuper_ck")); - omap_pwl_init(0xfffb5800, s, omap_findclk(s, "clk32-kHz")); - omap_pwt_init(0xfffb6000, s, omap_findclk(s, "xtal_osc_12m")); + omap_pwl_init(0xfffb5800, s, omap_findclk(s, "armxor_ck")); + omap_pwt_init(0xfffb6000, s, omap_findclk(s, "armxor_ck")); s->i2c = omap_i2c_init(0xfffb3800, s->irq[1][OMAP_INT_I2C], &s->drq[OMAP_DMA_I2C_RX], omap_findclk(s, "mpuper_ck")); @@ -4263,14 +4733,18 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, s->rtc = omap_rtc_init(0xfffb4800, &s->irq[1][OMAP_INT_RTC_TIMER], omap_findclk(s, "clk32-kHz")); + s->mcbsp1 = omap_mcbsp_init(0xfffb1800, &s->irq[1][OMAP_INT_McBSP1TX], + &s->drq[OMAP_DMA_MCBSP1_TX], omap_findclk(s, "dspxor_ck")); + s->mcbsp2 = omap_mcbsp_init(0xfffb1000, &s->irq[0][OMAP_INT_310_McBSP2_TX], + &s->drq[OMAP_DMA_MCBSP2_TX], omap_findclk(s, "mpuper_ck")); + s->mcbsp3 = omap_mcbsp_init(0xfffb7000, &s->irq[1][OMAP_INT_McBSP3TX], + &s->drq[OMAP_DMA_MCBSP3_TX], omap_findclk(s, "dspxor_ck")); + /* Register mappings not currenlty implemented: - * McBSP2 Comm fffb1000 - fffb17ff - * McBSP1 Audio fffb1800 - fffb1fff (not mapped on OMAP310) * MCSI2 Comm fffb2000 - fffb27ff (not mapped on OMAP310) * MCSI1 Bluetooth fffb2800 - fffb2fff (not mapped on OMAP310) * USB W2FC fffb4000 - fffb47ff * Camera Interface fffb6800 - fffb6fff - * McBSP3 fffb7000 - fffb77ff (not mapped on OMAP310) * USB Host fffba000 - fffba7ff * FAC fffba800 - fffbafff * HDQ/1-Wire fffbc000 - fffbc7ff diff --git a/hw/omap.h b/hw/omap.h index dbfc6dd403..6de4f3e105 100644 --- a/hw/omap.h +++ b/hw/omap.h @@ -479,6 +479,30 @@ struct omap_rtc_s; struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base, qemu_irq *irq, omap_clk clk); +struct i2s_codec_s { + void *opaque; + + /* The CPU can call this if it is generating the clock signal on the + * i2s port. The CODEC can ignore it if it is set up as a clock + * master and generates its own clock. */ + void (*set_rate)(void *opaque, int in, int out); + + void (*tx_swallow)(void *opaque); + qemu_irq rx_swallow; + qemu_irq tx_start; + + struct i2s_fifo_s { + uint8_t *fifo; + int len; + int start; + int size; + } in, out; +}; +struct omap_mcbsp_s; +struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base, + qemu_irq *irq, qemu_irq *dma, omap_clk clk); +void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, struct i2s_codec_s *slave); + /* omap_lcdc.c */ struct omap_lcd_panel_s; void omap_lcdc_reset(struct omap_lcd_panel_s *s); @@ -536,6 +560,9 @@ struct omap_mpu_state_s { struct omap_gpio_s *gpio; + struct omap_mcbsp_s *mcbsp1; + struct omap_mcbsp_s *mcbsp3; + /* MPU public TIPB peripherals */ struct omap_32khz_timer_s *os_timer; @@ -563,6 +590,8 @@ struct omap_mpu_state_s { struct omap_rtc_s *rtc; + struct omap_mcbsp_s *mcbsp2; + /* MPU private TIPB peripherals */ struct omap_intr_handler_s *ih[2]; @@ -646,6 +675,7 @@ void omap_badwidth_write32(void *opaque, target_phys_addr_t addr, __FUNCTION__, paddr) # define TCMI_VERBOSE 1 +//# define MEM_VERBOSE 1 # ifdef TCMI_VERBOSE # define OMAP_8B_REG(paddr) \ @@ -665,4 +695,97 @@ void omap_badwidth_write32(void *opaque, target_phys_addr_t addr, # define OMAP_MPUI_REG_MASK 0x000007ff +# ifdef MEM_VERBOSE +struct io_fn { + CPUReadMemoryFunc **mem_read; + CPUWriteMemoryFunc **mem_write; + void *opaque; + int in; +}; + +static uint32_t io_readb(void *opaque, target_phys_addr_t addr) +{ + struct io_fn *s = opaque; + uint32_t ret; + + s->in ++; + ret = s->mem_read[0](s->opaque, addr); + s->in --; + if (!s->in) + fprintf(stderr, "%08x ---> %02x\n", (uint32_t) addr, ret); + return ret; +} +static uint32_t io_readh(void *opaque, target_phys_addr_t addr) +{ + struct io_fn *s = opaque; + uint32_t ret; + + s->in ++; + ret = s->mem_read[1](s->opaque, addr); + s->in --; + if (!s->in) + fprintf(stderr, "%08x ---> %04x\n", (uint32_t) addr, ret); + return ret; +} +static uint32_t io_readw(void *opaque, target_phys_addr_t addr) +{ + struct io_fn *s = opaque; + uint32_t ret; + + s->in ++; + ret = s->mem_read[2](s->opaque, addr); + s->in --; + if (!s->in) + fprintf(stderr, "%08x ---> %08x\n", (uint32_t) addr, ret); + return ret; +} +static void io_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + struct io_fn *s = opaque; + + if (!s->in) + fprintf(stderr, "%08x <--- %02x\n", (uint32_t) addr, value); + s->in ++; + s->mem_write[0](s->opaque, addr, value); + s->in --; +} +static void io_writeh(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + struct io_fn *s = opaque; + + if (!s->in) + fprintf(stderr, "%08x <--- %04x\n", (uint32_t) addr, value); + s->in ++; + s->mem_write[1](s->opaque, addr, value); + s->in --; +} +static void io_writew(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + struct io_fn *s = opaque; + + if (!s->in) + fprintf(stderr, "%08x <--- %08x\n", (uint32_t) addr, value); + s->in ++; + s->mem_write[2](s->opaque, addr, value); + s->in --; +} + +static CPUReadMemoryFunc *io_readfn[] = { io_readb, io_readh, io_readw, }; +static CPUWriteMemoryFunc *io_writefn[] = { io_writeb, io_writeh, io_writew, }; + +inline static int debug_register_io_memory(int io_index, + CPUReadMemoryFunc **mem_read, CPUWriteMemoryFunc **mem_write, + void *opaque) +{ + struct io_fn *s = qemu_malloc(sizeof(struct io_fn)); + + s->mem_read = mem_read; + s->mem_write = mem_write; + s->opaque = opaque; + s->in = 0; + return cpu_register_io_memory(io_index, io_readfn, io_writefn, s); +} +# define cpu_register_io_memory debug_register_io_memory +# endif + #endif /* hw_omap_h */ diff --git a/hw/omap1_clk.c b/hw/omap1_clk.c index c8540de70f..b2ec23c13c 100644 --- a/hw/omap1_clk.c +++ b/hw/omap1_clk.c @@ -307,6 +307,12 @@ static struct clk lbfree_ck = { .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; +static struct clk hsab_ck = { + .name = "hsab_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, +}; + static struct clk rhea1_ck = { .name = "rhea1_ck", .parent = &tc_ck, @@ -359,7 +365,7 @@ static struct clk uart2_ck = { static struct clk uart3_1510 = { .name = "uart3_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck,/* either armper_ck or dpll4 */ + .parent = &armper_ck, /* either armper_ck or dpll4 */ .rate = 12000000, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; @@ -395,11 +401,12 @@ static struct clk usb_hhc_ck16xx = { .flags = CLOCK_IN_OMAP16XX, }; -static struct clk usb_dc_ck = { - .name = "usb_dc_ck", - /* Direct from ULPD, no parent */ +static struct clk usb_w2fc_mclk = { + .name = "usb_w2fc_mclk", + .alias = "usb_w2fc_ck", + .parent = &ck_48m, .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk mclk_1510 = { @@ -539,6 +546,7 @@ static struct clk *onchip_clks[] = { &api_ck, &lb_ck, &lbfree_ck, + &hsab_ck, &rhea1_ck, &rhea2_ck, &lcd_ck_16xx, @@ -551,7 +559,6 @@ static struct clk *onchip_clks[] = { &uart3_16xx, &usb_clk0, &usb_hhc_ck1510, &usb_hhc_ck16xx, - &usb_dc_ck, &mclk_1510, &mclk_16xx, &mclk_310, &bclk_1510, &bclk_16xx, &bclk_310, &mmc1_ck, @@ -560,6 +567,7 @@ static struct clk *onchip_clks[] = { &cam_exclk, &cam_lclk, &clk32k, + &usb_w2fc_mclk, /* Virtual clocks */ &i2c_fck, &i2c_ick, diff --git a/hw/palm.c b/hw/palm.c index 8800dc608b..bd17930f4b 100644 --- a/hw/palm.c +++ b/hw/palm.c @@ -78,11 +78,18 @@ static CPUWriteMemoryFunc *static_writefn[] = { static void palmte_microwire_setup(struct omap_mpu_state_s *cpu) { - omap_uwire_attach( - cpu->microwire, - tsc2102_init( - omap_gpio_in_get(cpu->gpio)[PALMTE_PINTDAV_GPIO]), - 0); + struct uwire_slave_s *tsc; + AudioState *audio = 0; + +#ifdef HAS_AUDIO + audio = AUD_init(); +#endif + + tsc = tsc2102_init(omap_gpio_in_get(cpu->gpio)[PALMTE_PINTDAV_GPIO], + audio); + + omap_uwire_attach(cpu->microwire, tsc, 0); + omap_mcbsp_i2s_attach(cpu->mcbsp1, tsc210x_codec(tsc)); } static struct { diff --git a/hw/tsc210x.c b/hw/tsc210x.c index 31cf3a444e..50d3edacdf 100644 --- a/hw/tsc210x.c +++ b/hw/tsc210x.c @@ -32,7 +32,11 @@ struct tsc210x_state_s { qemu_irq pint; QEMUTimer *timer; + QEMUSoundCard card; struct uwire_slave_s chip; + struct i2s_codec_s codec; + uint8_t in_fifo[16384]; + uint8_t out_fifo[16384]; int x, y; int pressure; @@ -63,6 +67,13 @@ struct tsc210x_state_s { uint16_t dac_power; int64_t powerdown; uint16_t filter_data[0x14]; + + const char *name; + SWVoiceIn *adc_voice[1]; + SWVoiceOut *dac_voice[1]; + int i2s_rx_rate; + int i2s_tx_rate; + AudioState *audio; }; static const int resolution[4] = { 12, 8, 10, 12 }; @@ -171,9 +182,144 @@ static void tsc210x_reset(struct tsc210x_state_s *s) s->filter_data[0x12] = 0x7d83; s->filter_data[0x13] = 0x84ee; + s->i2s_tx_rate = 0; + s->i2s_rx_rate = 0; + qemu_set_irq(s->pint, !s->irq); } +struct tsc210x_rate_info_s { + int rate; + int dsor; + int fsref; +}; + +/* { rate, dsor, fsref } */ +static const struct tsc210x_rate_info_s tsc2101_rates[] = { + /* Fsref / 6.0 */ + { 7350, 7, 1 }, + { 8000, 7, 0 }, + /* Fsref / 5.5 */ + { 8018, 6, 1 }, + { 8727, 6, 0 }, + /* Fsref / 5.0 */ + { 8820, 5, 1 }, + { 9600, 5, 0 }, + /* Fsref / 4.0 */ + { 11025, 4, 1 }, + { 12000, 4, 0 }, + /* Fsref / 3.0 */ + { 14700, 3, 1 }, + { 16000, 3, 0 }, + /* Fsref / 2.0 */ + { 22050, 2, 1 }, + { 24000, 2, 0 }, + /* Fsref / 1.5 */ + { 29400, 1, 1 }, + { 32000, 1, 0 }, + /* Fsref */ + { 44100, 0, 1 }, + { 48000, 0, 0 }, + + { 0, 0, 0 }, +}; + +/* { rate, dsor, fsref } */ +static const struct tsc210x_rate_info_s tsc2102_rates[] = { + /* Fsref / 6.0 */ + { 7350, 63, 1 }, + { 8000, 63, 0 }, + /* Fsref / 6.0 */ + { 7350, 54, 1 }, + { 8000, 54, 0 }, + /* Fsref / 5.0 */ + { 8820, 45, 1 }, + { 9600, 45, 0 }, + /* Fsref / 4.0 */ + { 11025, 36, 1 }, + { 12000, 36, 0 }, + /* Fsref / 3.0 */ + { 14700, 27, 1 }, + { 16000, 27, 0 }, + /* Fsref / 2.0 */ + { 22050, 18, 1 }, + { 24000, 18, 0 }, + /* Fsref / 1.5 */ + { 29400, 9, 1 }, + { 32000, 9, 0 }, + /* Fsref */ + { 44100, 0, 1 }, + { 48000, 0, 0 }, + + { 0, 0, 0 }, +}; + +static inline void tsc210x_out_flush(struct tsc210x_state_s *s, int len) +{ + uint8_t *data = s->codec.out.fifo + s->codec.out.start; + uint8_t *end = data + len; + + while (data < end) + data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data); + + s->codec.out.len -= len; + if (s->codec.out.len) + memmove(s->codec.out.fifo, end, s->codec.out.len); + s->codec.out.start = 0; +} + +static void tsc210x_audio_out_cb(struct tsc210x_state_s *s, int free_b) +{ + if (s->codec.out.len >= free_b) { + tsc210x_out_flush(s, free_b); + return; + } + + s->codec.out.size = MIN(free_b, 16384); + qemu_irq_raise(s->codec.tx_start); +} + +static void tsc2102_audio_set_format(struct tsc210x_state_s *s) +{ + int enable; + const struct tsc210x_rate_info_s *rate; + audsettings_t fmt; + + if (s->dac_voice[0]) { + tsc210x_out_flush(s, s->codec.out.len); + s->codec.out.size = 0; + AUD_set_active_out(s->dac_voice[0], 0); + AUD_close_out(&s->card, s->dac_voice[0]); + s->dac_voice[0] = 0; + } + + enable = + (~s->dac_power & (1 << 15)) && /* PWDNC */ + (~s->dac_power & (1 << 10)); /* DAPWDN */ + if (!enable) + return; + + for (rate = tsc2102_rates; rate->rate; rate ++) + if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */ + rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */ + break; + if (!rate->rate) { + printf("%s: unknown sampling rate configured\n", __FUNCTION__); + return; + } + + /* Force our own sampling rate even in slave DAC mode */ + fmt.endianness = 0; + fmt.nchannels = 2; + fmt.freq = rate->rate; + fmt.fmt = AUD_FMT_S16; + + s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], + "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); + if (s->dac_voice[0]) + AUD_set_active_out(s->dac_voice[0], 1); +} + static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg) { switch (reg) { @@ -437,6 +583,8 @@ static void tsc2102_audio_register_write( fprintf(stderr, "tsc2102_audio_register_write: " "wrong value written into Audio 1\n"); #endif + if (s->audio) + tsc2102_audio_set_format(s); return; case 0x01: @@ -479,6 +627,8 @@ static void tsc2102_audio_register_write( fprintf(stderr, "tsc2102_audio_register_write: " "wrong value written into Power\n"); #endif + if (s->audio) + tsc2102_audio_set_format(s); return; case 0x06: /* Audio Control 3 */ @@ -489,6 +639,8 @@ static void tsc2102_audio_register_write( fprintf(stderr, "tsc2102_audio_register_write: " "wrong value written into Audio 3\n"); #endif + if (s->audio) + tsc2102_audio_set_format(s); return; case 0x07: /* LCH_BASS_BOOST_N0 */ @@ -718,6 +870,20 @@ static void tsc210x_touchscreen_event(void *opaque, tsc210x_pin_update(s); } +static void tsc210x_i2s_swallow(struct tsc210x_state_s *s) +{ + if (s->dac_voice[0]) + tsc210x_out_flush(s, s->codec.out.len); + else + s->codec.out.len = 0; +} + +static void tsc210x_i2s_set_rate(struct tsc210x_state_s *s, int in, int out) +{ + s->i2s_tx_rate = out; + s->i2s_rx_rate = in; +} + static void tsc210x_save(QEMUFile *f, void *opaque) { struct tsc210x_state_s *s = (struct tsc210x_state_s *) opaque; @@ -817,7 +983,7 @@ static int tsc210x_load(QEMUFile *f, void *opaque, int version_id) static int tsc2102_iid = 0; -struct uwire_slave_s *tsc2102_init(qemu_irq pint) +struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio) { struct tsc210x_state_s *s; @@ -830,19 +996,37 @@ struct uwire_slave_s *tsc2102_init(qemu_irq pint) s->precision = s->nextprecision = 0; s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s); s->pint = pint; + s->name = "tsc2102"; + s->audio = audio; s->chip.opaque = s; s->chip.send = (void *) tsc210x_write; s->chip.receive = (void *) tsc210x_read; + s->codec.opaque = s; + s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; + s->codec.set_rate = (void *) tsc210x_i2s_set_rate; + s->codec.in.fifo = s->in_fifo; + s->codec.out.fifo = s->out_fifo; + tsc210x_reset(s); qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, "QEMU TSC2102-driven Touchscreen"); + if (s->audio) + AUD_register_card(s->audio, s->name, &s->card); + qemu_register_reset((void *) tsc210x_reset, s); - register_savevm("tsc2102", tsc2102_iid ++, 0, + register_savevm(s->name, tsc2102_iid ++, 0, tsc210x_save, tsc210x_load, s); return &s->chip; } + +struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip) +{ + struct tsc210x_state_s *s = (struct tsc210x_state_s *) chip->opaque; + + return &s->codec; +} diff --git a/vl.h b/vl.h index ad4d05f0b6..6df63eb205 100644 --- a/vl.h +++ b/vl.h @@ -1667,7 +1667,8 @@ void qemu_get_ptimer(QEMUFile *f, ptimer_state *s); #include "hw/omap.h" /* tsc210x.c */ -struct uwire_slave_s *tsc2102_init(qemu_irq pint); +struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio); +struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip); /* mcf_uart.c */ uint32_t mcf_uart_read(void *opaque, target_phys_addr_t addr);