]> git.proxmox.com Git - qemu.git/commitdiff
Clean-up/rewrite audio over I^2S support.
authorbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 19 Nov 2007 03:43:51 +0000 (03:43 +0000)
committerbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 19 Nov 2007 03:43:51 +0000 (03:43 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3704 c046a42c-6fe2-441c-8c8c-71466251a162

hw/omap.c
hw/omap.h
hw/tsc210x.c

index 1e79831d8fa4634a0419f5362c43ccca01175644..662332b7ca59b2a73c714e43ffb8e7fb89fca41f 100644 (file)
--- 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)
index c31503094d19de44decc1bcfc06b41ee8751c4c1..e58fb3c24c60e58a4dab28f75c7d0761b3ce6e21 100644 (file)
--- 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;
index f04b19d334f1c2b529d26f84f1ca8706f2188e39..6082aa0b9a8df1449d320de6855b6ce3652f619c 100644 (file)
@@ -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 */