From 73560bc8e347e8c71bd646e977282efab204ff44 Mon Sep 17 00:00:00 2001 From: balrog Date: Mon, 19 Nov 2007 03:43:51 +0000 Subject: [PATCH] Clean-up/rewrite audio over I^2S support. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3704 c046a42c-6fe2-441c-8c8c-71466251a162 --- hw/omap.c | 282 +++++++++++++++++++++++++++++++++++++++-------------------- hw/omap.h | 5 ++ hw/tsc210x.c | 50 +++++++---- 3 files changed, 227 insertions(+), 110 deletions(-) diff --git a/hw/omap.c b/hw/omap.c index 1e79831..662332b 100644 --- a/hw/omap.c +++ b/hw/omap.c @@ -997,7 +997,8 @@ 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 >> 7; + /* TODO: make a clever calculation */ + s->delay = ticks_per_sec >> 8; if (s->run_count) qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay); } else { @@ -4097,8 +4098,11 @@ struct omap_mcbsp_s { int tx_rate; int rx_rate; int tx_req; + int rx_req; struct i2s_codec_s *codec; + QEMUTimer *source_timer; + QEMUTimer *sink_timer; }; static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) @@ -4134,88 +4138,149 @@ static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) qemu_set_irq(s->txirq, irq); } -static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) +static void omap_mcbsp_rx_newdata(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); - } + 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_rate_update(struct omap_mcbsp_s *s) +static void omap_mcbsp_source_tick(void *opaque) { - int rx_clk = 0, tx_clk = 0; - int cpu_rate = 1500000; /* XXX */ - if (!s->codec) + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 }; + + if (!s->rx_rate) return; + if (s->rx_req) + printf("%s: Rx FIFO overrun\n", __FUNCTION__); - 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->rx_req = s->rx_rate << bps[(s->rcr[0] >> 5) & 7]; - s->codec->set_rate(s->codec->opaque, rx_clk, tx_clk); + omap_mcbsp_rx_newdata(s); + qemu_mod_timer(s->source_timer, qemu_get_clock(vm_clock) + ticks_per_sec); } 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->codec || !s->codec->rts) + omap_mcbsp_source_tick(s); + else if (s->codec->in.len) { + s->rx_req = s->codec->in.len; + omap_mcbsp_rx_newdata(s); } - - 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) { + qemu_del_timer(s->source_timer); +} + +static void omap_mcbsp_rx_done(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) +static void omap_mcbsp_tx_newdata(struct omap_mcbsp_s *s) +{ + s->spcr[1] |= 1 << 1; /* XRDY */ + qemu_irq_raise(s->txdrq); + omap_mcbsp_intr_update(s); +} + +static void omap_mcbsp_sink_tick(void *opaque) { - if (s->tx_rate) + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 }; + + if (!s->tx_rate) return; - s->tx_rate = 1; - omap_mcbsp_req_update(s); + if (s->tx_req) + printf("%s: Tx FIFO underrun\n", __FUNCTION__); + + s->tx_req = s->tx_rate << bps[(s->xcr[0] >> 5) & 7]; + + omap_mcbsp_tx_newdata(s); + qemu_mod_timer(s->sink_timer, qemu_get_clock(vm_clock) + ticks_per_sec); +} + +static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s) +{ + if (!s->codec || !s->codec->cts) + omap_mcbsp_sink_tick(s); + else if (s->codec->out.size) { + s->tx_req = s->codec->out.size; + omap_mcbsp_tx_newdata(s); + } +} + +static void omap_mcbsp_tx_done(struct omap_mcbsp_s *s) +{ + s->spcr[1] &= ~(1 << 1); /* XRDY */ + qemu_irq_lower(s->txdrq); + omap_mcbsp_intr_update(s); + if (s->codec && s->codec->cts) + s->codec->tx_swallow(s->codec->opaque); } static void omap_mcbsp_tx_stop(struct omap_mcbsp_s *s) { - s->tx_rate = 0; - omap_mcbsp_req_update(s); + s->tx_req = 0; + omap_mcbsp_tx_done(s); + qemu_del_timer(s->sink_timer); +} + +static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) +{ + int prev_rx_rate, prev_tx_rate; + int rx_rate = 0, tx_rate = 0; + int cpu_rate = 1500000; /* XXX */ + + /* TODO: check CLKSTP bit */ + 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_rate = cpu_rate / + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ + } else + if (s->codec) + rx_rate = s->codec->rx_rate; + } + + 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_rate = cpu_rate / + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ + } else + if (s->codec) + tx_rate = s->codec->tx_rate; + } + } + prev_tx_rate = s->tx_rate; + prev_rx_rate = s->rx_rate; + s->tx_rate = tx_rate; + s->rx_rate = rx_rate; + + if (s->codec) + s->codec->set_rate(s->codec->opaque, rx_rate, tx_rate); + + if (!prev_tx_rate && tx_rate) + omap_mcbsp_tx_start(s); + else if (s->tx_rate && !tx_rate) + omap_mcbsp_tx_stop(s); + + if (!prev_rx_rate && rx_rate) + omap_mcbsp_rx_start(s); + else if (prev_tx_rate && !tx_rate) + omap_mcbsp_rx_stop(s); } static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr) @@ -4230,17 +4295,19 @@ static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr) return 0x0000; /* Fall through. */ case 0x02: /* DRR1 */ - if (!s->codec) - return 0x0000; - if (s->codec->in.len < 2) { + if (s->rx_req < 2) { printf("%s: Rx FIFO underrun\n", __FUNCTION__); - omap_mcbsp_rx_stop(s); + omap_mcbsp_rx_done(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); + s->tx_req -= 2; + if (s->codec && s->codec->in.len >= 2) { + ret = s->codec->in.fifo[s->codec->in.start ++] << 8; + ret |= s->codec->in.fifo[s->codec->in.start ++]; + s->codec->in.len -= 2; + } else + ret = 0x0000; + if (!s->tx_req) + omap_mcbsp_rx_done(s); return ret; } return 0x0000; @@ -4309,7 +4376,7 @@ static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr) return 0; } -static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, +static void omap_mcbsp_writeh(void *opaque, target_phys_addr_t addr, uint32_t value) { struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; @@ -4326,18 +4393,14 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, 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 { + if (s->tx_req > 1) { + s->tx_req -= 2; + if (s->codec && s->codec->cts) { 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); } + if (s->tx_req < 2) + omap_mcbsp_tx_done(s); } else printf("%s: Tx FIFO overrun\n", __FUNCTION__); return; @@ -4346,14 +4409,8 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, s->spcr[1] &= 0x0002; s->spcr[1] |= 0x03f9 & value; s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ - if (~value & 1) { /* 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 */ @@ -4363,12 +4420,9 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, 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; + s->rx_req = 0; + omap_mcbsp_rx_done(s); } - if (s->codec) - omap_mcbsp_rate_update(s); omap_mcbsp_req_update(s); return; @@ -4386,11 +4440,11 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, return; case 0x14: /* SRGR2 */ s->srgr[1] = value & 0xffff; - omap_mcbsp_rate_update(s); + omap_mcbsp_req_update(s); return; case 0x16: /* SRGR1 */ s->srgr[0] = value & 0xffff; - omap_mcbsp_rate_update(s); + omap_mcbsp_req_update(s); return; case 0x18: /* MCR2 */ s->mcr[1] = value & 0x03e3; @@ -4460,6 +4514,37 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, OMAP_BAD_REG(addr); } +static void omap_mcbsp_writew(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; + + if (offset == 0x04) { /* DXR */ + if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ + return; + if (s->tx_req > 3) { + s->tx_req -= 4; + if (s->codec && s->codec->cts) { + s->codec->out.fifo[s->codec->out.len ++] = + (value >> 24) & 0xff; + s->codec->out.fifo[s->codec->out.len ++] = + (value >> 16) & 0xff; + s->codec->out.fifo[s->codec->out.len ++] = + (value >> 8) & 0xff; + s->codec->out.fifo[s->codec->out.len ++] = + (value >> 0) & 0xff; + } + if (s->tx_req < 4) + omap_mcbsp_tx_done(s); + } else + printf("%s: Tx FIFO overrun\n", __FUNCTION__); + return; + } + + omap_badwidth_write16(opaque, addr, value); +} + static CPUReadMemoryFunc *omap_mcbsp_readfn[] = { omap_badwidth_read16, omap_mcbsp_read, @@ -4468,8 +4553,8 @@ static CPUReadMemoryFunc *omap_mcbsp_readfn[] = { static CPUWriteMemoryFunc *omap_mcbsp_writefn[] = { omap_badwidth_write16, - omap_mcbsp_write, - omap_badwidth_write16, + omap_mcbsp_writeh, + omap_mcbsp_writew, }; static void omap_mcbsp_reset(struct omap_mcbsp_s *s) @@ -4484,8 +4569,11 @@ static void omap_mcbsp_reset(struct omap_mcbsp_s *s) memset(&s->rcer, 0, sizeof(s->rcer)); memset(&s->xcer, 0, sizeof(s->xcer)); s->tx_req = 0; + s->rx_req = 0; s->tx_rate = 0; s->rx_rate = 0; + qemu_del_timer(s->source_timer); + qemu_del_timer(s->sink_timer); } struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base, @@ -4500,6 +4588,8 @@ struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base, s->rxirq = irq[1]; s->txdrq = dma[0]; s->rxdrq = dma[1]; + s->sink_timer = qemu_new_timer(vm_clock, omap_mcbsp_sink_tick, s); + s->source_timer = qemu_new_timer(vm_clock, omap_mcbsp_source_tick, s); omap_mcbsp_reset(s); iomemtype = cpu_register_io_memory(0, omap_mcbsp_readfn, @@ -4513,14 +4603,20 @@ static 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); + if (s->rx_rate) { + s->rx_req = s->codec->in.len; + omap_mcbsp_rx_newdata(s); + } } static 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); + if (s->tx_rate) { + s->tx_req = s->codec->out.size; + omap_mcbsp_tx_newdata(s); + } } void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, struct i2s_codec_s *slave) diff --git a/hw/omap.h b/hw/omap.h index c315030..e58fb3c 100644 --- a/hw/omap.h +++ b/hw/omap.h @@ -491,6 +491,11 @@ struct i2s_codec_s { qemu_irq rx_swallow; qemu_irq tx_start; + int tx_rate; + int cts; + int rx_rate; + int rts; + struct i2s_fifo_s { uint8_t *fifo; int len; diff --git a/hw/tsc210x.c b/hw/tsc210x.c index f04b19d..6082aa0 100644 --- a/hw/tsc210x.c +++ b/hw/tsc210x.c @@ -283,10 +283,30 @@ static void tsc210x_audio_out_cb(struct tsc210x_state_s *s, int free_b) qemu_irq_raise(s->codec.tx_start); } -static void tsc2102_audio_set_format(struct tsc210x_state_s *s) +static void tsc2102_audio_rate_update(struct tsc210x_state_s *s) { - int enable; const struct tsc210x_rate_info_s *rate; + + s->codec.tx_rate = 0; + s->codec.rx_rate = 0; + if (s->dac_power & (1 << 15)) /* PWDNC */ + 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; + } + + s->codec.tx_rate = rate->rate; +} + +static void tsc2102_audio_output_update(struct tsc210x_state_s *s) +{ + int enable; audsettings_t fmt; if (s->dac_voice[0]) { @@ -296,32 +316,26 @@ static void tsc2102_audio_set_format(struct tsc210x_state_s *s) AUD_close_out(&s->card, s->dac_voice[0]); s->dac_voice[0] = 0; } + s->codec.cts = 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__); + if (!enable || !s->codec.tx_rate) return; - } /* Force our own sampling rate even in slave DAC mode */ fmt.endianness = 0; fmt.nchannels = 2; - fmt.freq = rate->rate; + fmt.freq = s->codec.tx_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]) + if (s->dac_voice[0]) { + s->codec.cts = 1; AUD_set_active_out(s->dac_voice[0], 1); + } } static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg) @@ -587,8 +601,9 @@ static void tsc2102_audio_register_write( fprintf(stderr, "tsc2102_audio_register_write: " "wrong value written into Audio 1\n"); #endif + tsc2102_audio_rate_update(s); if (s->audio) - tsc2102_audio_set_format(s); + tsc2102_audio_output_update(s); return; case 0x01: @@ -631,8 +646,9 @@ static void tsc2102_audio_register_write( fprintf(stderr, "tsc2102_audio_register_write: " "wrong value written into Power\n"); #endif + tsc2102_audio_rate_update(s); if (s->audio) - tsc2102_audio_set_format(s); + tsc2102_audio_output_update(s); return; case 0x06: /* Audio Control 3 */ @@ -644,7 +660,7 @@ static void tsc2102_audio_register_write( "wrong value written into Audio 3\n"); #endif if (s->audio) - tsc2102_audio_set_format(s); + tsc2102_audio_output_update(s); return; case 0x07: /* LCH_BASS_BOOST_N0 */ -- 2.7.4