Commit 73560bc8e347e8c71bd646e977282efab204ff44
1 parent
79b02417
Clean-up/rewrite audio over I^2S support.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3704 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
227 additions
and
110 deletions
hw/omap.c
| @@ -997,7 +997,8 @@ static void omap_dma_clk_update(void *opaque, int line, int on) | @@ -997,7 +997,8 @@ static void omap_dma_clk_update(void *opaque, int line, int on) | ||
| 997 | struct omap_dma_s *s = (struct omap_dma_s *) opaque; | 997 | struct omap_dma_s *s = (struct omap_dma_s *) opaque; |
| 998 | 998 | ||
| 999 | if (on) { | 999 | if (on) { |
| 1000 | - s->delay = ticks_per_sec >> 7; | 1000 | + /* TODO: make a clever calculation */ |
| 1001 | + s->delay = ticks_per_sec >> 8; | ||
| 1001 | if (s->run_count) | 1002 | if (s->run_count) |
| 1002 | qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay); | 1003 | qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay); |
| 1003 | } else { | 1004 | } else { |
| @@ -4097,8 +4098,11 @@ struct omap_mcbsp_s { | @@ -4097,8 +4098,11 @@ struct omap_mcbsp_s { | ||
| 4097 | int tx_rate; | 4098 | int tx_rate; |
| 4098 | int rx_rate; | 4099 | int rx_rate; |
| 4099 | int tx_req; | 4100 | int tx_req; |
| 4101 | + int rx_req; | ||
| 4100 | 4102 | ||
| 4101 | struct i2s_codec_s *codec; | 4103 | struct i2s_codec_s *codec; |
| 4104 | + QEMUTimer *source_timer; | ||
| 4105 | + QEMUTimer *sink_timer; | ||
| 4102 | }; | 4106 | }; |
| 4103 | 4107 | ||
| 4104 | static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) | 4108 | 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) | @@ -4134,88 +4138,149 @@ static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) | ||
| 4134 | qemu_set_irq(s->txirq, irq); | 4138 | qemu_set_irq(s->txirq, irq); |
| 4135 | } | 4139 | } |
| 4136 | 4140 | ||
| 4137 | -static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) | 4141 | +static void omap_mcbsp_rx_newdata(struct omap_mcbsp_s *s) |
| 4138 | { | 4142 | { |
| 4139 | - int prev = s->tx_req; | ||
| 4140 | - | ||
| 4141 | - s->tx_req = (s->tx_rate || | ||
| 4142 | - (s->spcr[0] & (1 << 12))) && /* CLKSTP */ | ||
| 4143 | - (s->spcr[1] & (1 << 6)) && /* GRST */ | ||
| 4144 | - (s->spcr[1] & (1 << 0)); /* XRST */ | ||
| 4145 | - | ||
| 4146 | - if (!s->tx_req && prev) { | ||
| 4147 | - s->spcr[1] &= ~(1 << 1); /* XRDY */ | ||
| 4148 | - qemu_irq_lower(s->txdrq); | ||
| 4149 | - omap_mcbsp_intr_update(s); | ||
| 4150 | - | ||
| 4151 | - if (s->codec) | ||
| 4152 | - s->codec->tx_swallow(s->codec->opaque); | ||
| 4153 | - } else if (s->codec && s->tx_req && !prev) { | ||
| 4154 | - s->spcr[1] |= 1 << 1; /* XRDY */ | ||
| 4155 | - qemu_irq_raise(s->txdrq); | ||
| 4156 | - omap_mcbsp_intr_update(s); | ||
| 4157 | - } | 4143 | + if ((s->spcr[0] >> 1) & 1) /* RRDY */ |
| 4144 | + s->spcr[0] |= 1 << 2; /* RFULL */ | ||
| 4145 | + s->spcr[0] |= 1 << 1; /* RRDY */ | ||
| 4146 | + qemu_irq_raise(s->rxdrq); | ||
| 4147 | + omap_mcbsp_intr_update(s); | ||
| 4158 | } | 4148 | } |
| 4159 | 4149 | ||
| 4160 | -static void omap_mcbsp_rate_update(struct omap_mcbsp_s *s) | 4150 | +static void omap_mcbsp_source_tick(void *opaque) |
| 4161 | { | 4151 | { |
| 4162 | - int rx_clk = 0, tx_clk = 0; | ||
| 4163 | - int cpu_rate = 1500000; /* XXX */ | ||
| 4164 | - if (!s->codec) | 4152 | + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; |
| 4153 | + static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 }; | ||
| 4154 | + | ||
| 4155 | + if (!s->rx_rate) | ||
| 4165 | return; | 4156 | return; |
| 4157 | + if (s->rx_req) | ||
| 4158 | + printf("%s: Rx FIFO overrun\n", __FUNCTION__); | ||
| 4166 | 4159 | ||
| 4167 | - if (s->spcr[1] & (1 << 6)) { /* GRST */ | ||
| 4168 | - if (s->spcr[0] & (1 << 0)) /* RRST */ | ||
| 4169 | - if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ | ||
| 4170 | - (s->pcr & (1 << 8))) /* CLKRM */ | ||
| 4171 | - if (~s->pcr & (1 << 7)) /* SCLKME */ | ||
| 4172 | - rx_clk = cpu_rate / | ||
| 4173 | - ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ | ||
| 4174 | - if (s->spcr[1] & (1 << 0)) /* XRST */ | ||
| 4175 | - if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ | ||
| 4176 | - (s->pcr & (1 << 9))) /* CLKXM */ | ||
| 4177 | - if (~s->pcr & (1 << 7)) /* SCLKME */ | ||
| 4178 | - tx_clk = cpu_rate / | ||
| 4179 | - ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ | ||
| 4180 | - } | 4160 | + s->rx_req = s->rx_rate << bps[(s->rcr[0] >> 5) & 7]; |
| 4181 | 4161 | ||
| 4182 | - s->codec->set_rate(s->codec->opaque, rx_clk, tx_clk); | 4162 | + omap_mcbsp_rx_newdata(s); |
| 4163 | + qemu_mod_timer(s->source_timer, qemu_get_clock(vm_clock) + ticks_per_sec); | ||
| 4183 | } | 4164 | } |
| 4184 | 4165 | ||
| 4185 | static void omap_mcbsp_rx_start(struct omap_mcbsp_s *s) | 4166 | static void omap_mcbsp_rx_start(struct omap_mcbsp_s *s) |
| 4186 | { | 4167 | { |
| 4187 | - if (!(s->spcr[0] & 1)) { /* RRST */ | ||
| 4188 | - if (s->codec) | ||
| 4189 | - s->codec->in.len = 0; | ||
| 4190 | - return; | 4168 | + if (!s->codec || !s->codec->rts) |
| 4169 | + omap_mcbsp_source_tick(s); | ||
| 4170 | + else if (s->codec->in.len) { | ||
| 4171 | + s->rx_req = s->codec->in.len; | ||
| 4172 | + omap_mcbsp_rx_newdata(s); | ||
| 4191 | } | 4173 | } |
| 4192 | - | ||
| 4193 | - if ((s->spcr[0] >> 1) & 1) /* RRDY */ | ||
| 4194 | - s->spcr[0] |= 1 << 2; /* RFULL */ | ||
| 4195 | - s->spcr[0] |= 1 << 1; /* RRDY */ | ||
| 4196 | - qemu_irq_raise(s->rxdrq); | ||
| 4197 | - omap_mcbsp_intr_update(s); | ||
| 4198 | } | 4174 | } |
| 4199 | 4175 | ||
| 4200 | static void omap_mcbsp_rx_stop(struct omap_mcbsp_s *s) | 4176 | static void omap_mcbsp_rx_stop(struct omap_mcbsp_s *s) |
| 4201 | { | 4177 | { |
| 4178 | + qemu_del_timer(s->source_timer); | ||
| 4179 | +} | ||
| 4180 | + | ||
| 4181 | +static void omap_mcbsp_rx_done(struct omap_mcbsp_s *s) | ||
| 4182 | +{ | ||
| 4202 | s->spcr[0] &= ~(1 << 1); /* RRDY */ | 4183 | s->spcr[0] &= ~(1 << 1); /* RRDY */ |
| 4203 | qemu_irq_lower(s->rxdrq); | 4184 | qemu_irq_lower(s->rxdrq); |
| 4204 | omap_mcbsp_intr_update(s); | 4185 | omap_mcbsp_intr_update(s); |
| 4205 | } | 4186 | } |
| 4206 | 4187 | ||
| 4207 | -static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s) | 4188 | +static void omap_mcbsp_tx_newdata(struct omap_mcbsp_s *s) |
| 4189 | +{ | ||
| 4190 | + s->spcr[1] |= 1 << 1; /* XRDY */ | ||
| 4191 | + qemu_irq_raise(s->txdrq); | ||
| 4192 | + omap_mcbsp_intr_update(s); | ||
| 4193 | +} | ||
| 4194 | + | ||
| 4195 | +static void omap_mcbsp_sink_tick(void *opaque) | ||
| 4208 | { | 4196 | { |
| 4209 | - if (s->tx_rate) | 4197 | + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; |
| 4198 | + static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 }; | ||
| 4199 | + | ||
| 4200 | + if (!s->tx_rate) | ||
| 4210 | return; | 4201 | return; |
| 4211 | - s->tx_rate = 1; | ||
| 4212 | - omap_mcbsp_req_update(s); | 4202 | + if (s->tx_req) |
| 4203 | + printf("%s: Tx FIFO underrun\n", __FUNCTION__); | ||
| 4204 | + | ||
| 4205 | + s->tx_req = s->tx_rate << bps[(s->xcr[0] >> 5) & 7]; | ||
| 4206 | + | ||
| 4207 | + omap_mcbsp_tx_newdata(s); | ||
| 4208 | + qemu_mod_timer(s->sink_timer, qemu_get_clock(vm_clock) + ticks_per_sec); | ||
| 4209 | +} | ||
| 4210 | + | ||
| 4211 | +static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s) | ||
| 4212 | +{ | ||
| 4213 | + if (!s->codec || !s->codec->cts) | ||
| 4214 | + omap_mcbsp_sink_tick(s); | ||
| 4215 | + else if (s->codec->out.size) { | ||
| 4216 | + s->tx_req = s->codec->out.size; | ||
| 4217 | + omap_mcbsp_tx_newdata(s); | ||
| 4218 | + } | ||
| 4219 | +} | ||
| 4220 | + | ||
| 4221 | +static void omap_mcbsp_tx_done(struct omap_mcbsp_s *s) | ||
| 4222 | +{ | ||
| 4223 | + s->spcr[1] &= ~(1 << 1); /* XRDY */ | ||
| 4224 | + qemu_irq_lower(s->txdrq); | ||
| 4225 | + omap_mcbsp_intr_update(s); | ||
| 4226 | + if (s->codec && s->codec->cts) | ||
| 4227 | + s->codec->tx_swallow(s->codec->opaque); | ||
| 4213 | } | 4228 | } |
| 4214 | 4229 | ||
| 4215 | static void omap_mcbsp_tx_stop(struct omap_mcbsp_s *s) | 4230 | static void omap_mcbsp_tx_stop(struct omap_mcbsp_s *s) |
| 4216 | { | 4231 | { |
| 4217 | - s->tx_rate = 0; | ||
| 4218 | - omap_mcbsp_req_update(s); | 4232 | + s->tx_req = 0; |
| 4233 | + omap_mcbsp_tx_done(s); | ||
| 4234 | + qemu_del_timer(s->sink_timer); | ||
| 4235 | +} | ||
| 4236 | + | ||
| 4237 | +static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) | ||
| 4238 | +{ | ||
| 4239 | + int prev_rx_rate, prev_tx_rate; | ||
| 4240 | + int rx_rate = 0, tx_rate = 0; | ||
| 4241 | + int cpu_rate = 1500000; /* XXX */ | ||
| 4242 | + | ||
| 4243 | + /* TODO: check CLKSTP bit */ | ||
| 4244 | + if (s->spcr[1] & (1 << 6)) { /* GRST */ | ||
| 4245 | + if (s->spcr[0] & (1 << 0)) { /* RRST */ | ||
| 4246 | + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ | ||
| 4247 | + (s->pcr & (1 << 8))) { /* CLKRM */ | ||
| 4248 | + if (~s->pcr & (1 << 7)) /* SCLKME */ | ||
| 4249 | + rx_rate = cpu_rate / | ||
| 4250 | + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ | ||
| 4251 | + } else | ||
| 4252 | + if (s->codec) | ||
| 4253 | + rx_rate = s->codec->rx_rate; | ||
| 4254 | + } | ||
| 4255 | + | ||
| 4256 | + if (s->spcr[1] & (1 << 0)) { /* XRST */ | ||
| 4257 | + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ | ||
| 4258 | + (s->pcr & (1 << 9))) { /* CLKXM */ | ||
| 4259 | + if (~s->pcr & (1 << 7)) /* SCLKME */ | ||
| 4260 | + tx_rate = cpu_rate / | ||
| 4261 | + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ | ||
| 4262 | + } else | ||
| 4263 | + if (s->codec) | ||
| 4264 | + tx_rate = s->codec->tx_rate; | ||
| 4265 | + } | ||
| 4266 | + } | ||
| 4267 | + prev_tx_rate = s->tx_rate; | ||
| 4268 | + prev_rx_rate = s->rx_rate; | ||
| 4269 | + s->tx_rate = tx_rate; | ||
| 4270 | + s->rx_rate = rx_rate; | ||
| 4271 | + | ||
| 4272 | + if (s->codec) | ||
| 4273 | + s->codec->set_rate(s->codec->opaque, rx_rate, tx_rate); | ||
| 4274 | + | ||
| 4275 | + if (!prev_tx_rate && tx_rate) | ||
| 4276 | + omap_mcbsp_tx_start(s); | ||
| 4277 | + else if (s->tx_rate && !tx_rate) | ||
| 4278 | + omap_mcbsp_tx_stop(s); | ||
| 4279 | + | ||
| 4280 | + if (!prev_rx_rate && rx_rate) | ||
| 4281 | + omap_mcbsp_rx_start(s); | ||
| 4282 | + else if (prev_tx_rate && !tx_rate) | ||
| 4283 | + omap_mcbsp_rx_stop(s); | ||
| 4219 | } | 4284 | } |
| 4220 | 4285 | ||
| 4221 | static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr) | 4286 | 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) | @@ -4230,17 +4295,19 @@ static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr) | ||
| 4230 | return 0x0000; | 4295 | return 0x0000; |
| 4231 | /* Fall through. */ | 4296 | /* Fall through. */ |
| 4232 | case 0x02: /* DRR1 */ | 4297 | case 0x02: /* DRR1 */ |
| 4233 | - if (!s->codec) | ||
| 4234 | - return 0x0000; | ||
| 4235 | - if (s->codec->in.len < 2) { | 4298 | + if (s->rx_req < 2) { |
| 4236 | printf("%s: Rx FIFO underrun\n", __FUNCTION__); | 4299 | printf("%s: Rx FIFO underrun\n", __FUNCTION__); |
| 4237 | - omap_mcbsp_rx_stop(s); | 4300 | + omap_mcbsp_rx_done(s); |
| 4238 | } else { | 4301 | } else { |
| 4239 | - s->codec->in.len -= 2; | ||
| 4240 | - ret = s->codec->in.fifo[s->codec->in.start ++] << 8; | ||
| 4241 | - ret |= s->codec->in.fifo[s->codec->in.start ++]; | ||
| 4242 | - if (!s->codec->in.len) | ||
| 4243 | - omap_mcbsp_rx_stop(s); | 4302 | + s->tx_req -= 2; |
| 4303 | + if (s->codec && s->codec->in.len >= 2) { | ||
| 4304 | + ret = s->codec->in.fifo[s->codec->in.start ++] << 8; | ||
| 4305 | + ret |= s->codec->in.fifo[s->codec->in.start ++]; | ||
| 4306 | + s->codec->in.len -= 2; | ||
| 4307 | + } else | ||
| 4308 | + ret = 0x0000; | ||
| 4309 | + if (!s->tx_req) | ||
| 4310 | + omap_mcbsp_rx_done(s); | ||
| 4244 | return ret; | 4311 | return ret; |
| 4245 | } | 4312 | } |
| 4246 | return 0x0000; | 4313 | return 0x0000; |
| @@ -4309,7 +4376,7 @@ static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr) | @@ -4309,7 +4376,7 @@ static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr) | ||
| 4309 | return 0; | 4376 | return 0; |
| 4310 | } | 4377 | } |
| 4311 | 4378 | ||
| 4312 | -static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | 4379 | +static void omap_mcbsp_writeh(void *opaque, target_phys_addr_t addr, |
| 4313 | uint32_t value) | 4380 | uint32_t value) |
| 4314 | { | 4381 | { |
| 4315 | struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; | 4382 | 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, | @@ -4326,18 +4393,14 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | ||
| 4326 | return; | 4393 | return; |
| 4327 | /* Fall through. */ | 4394 | /* Fall through. */ |
| 4328 | case 0x06: /* DXR1 */ | 4395 | case 0x06: /* DXR1 */ |
| 4329 | - if (!s->codec) | ||
| 4330 | - return; | ||
| 4331 | - if (s->tx_req) { | ||
| 4332 | - if (s->codec->out.len > s->codec->out.size - 2) { | ||
| 4333 | - printf("%s: Tx FIFO overrun\n", __FUNCTION__); | ||
| 4334 | - omap_mcbsp_tx_stop(s); | ||
| 4335 | - } else { | 4396 | + if (s->tx_req > 1) { |
| 4397 | + s->tx_req -= 2; | ||
| 4398 | + if (s->codec && s->codec->cts) { | ||
| 4336 | s->codec->out.fifo[s->codec->out.len ++] = (value >> 8) & 0xff; | 4399 | s->codec->out.fifo[s->codec->out.len ++] = (value >> 8) & 0xff; |
| 4337 | s->codec->out.fifo[s->codec->out.len ++] = (value >> 0) & 0xff; | 4400 | s->codec->out.fifo[s->codec->out.len ++] = (value >> 0) & 0xff; |
| 4338 | - if (s->codec->out.len >= s->codec->out.size) | ||
| 4339 | - omap_mcbsp_tx_stop(s); | ||
| 4340 | } | 4401 | } |
| 4402 | + if (s->tx_req < 2) | ||
| 4403 | + omap_mcbsp_tx_done(s); | ||
| 4341 | } else | 4404 | } else |
| 4342 | printf("%s: Tx FIFO overrun\n", __FUNCTION__); | 4405 | printf("%s: Tx FIFO overrun\n", __FUNCTION__); |
| 4343 | return; | 4406 | return; |
| @@ -4346,14 +4409,8 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | @@ -4346,14 +4409,8 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | ||
| 4346 | s->spcr[1] &= 0x0002; | 4409 | s->spcr[1] &= 0x0002; |
| 4347 | s->spcr[1] |= 0x03f9 & value; | 4410 | s->spcr[1] |= 0x03f9 & value; |
| 4348 | s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ | 4411 | s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ |
| 4349 | - if (~value & 1) { /* XRST */ | 4412 | + if (~value & 1) /* XRST */ |
| 4350 | s->spcr[1] &= ~6; | 4413 | s->spcr[1] &= ~6; |
| 4351 | - qemu_irq_lower(s->rxdrq); | ||
| 4352 | - if (s->codec) | ||
| 4353 | - s->codec->out.len = 0; | ||
| 4354 | - } | ||
| 4355 | - if (s->codec) | ||
| 4356 | - omap_mcbsp_rate_update(s); | ||
| 4357 | omap_mcbsp_req_update(s); | 4414 | omap_mcbsp_req_update(s); |
| 4358 | return; | 4415 | return; |
| 4359 | case 0x0a: /* SPCR1 */ | 4416 | case 0x0a: /* SPCR1 */ |
| @@ -4363,12 +4420,9 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | @@ -4363,12 +4420,9 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | ||
| 4363 | printf("%s: Digital Loopback mode enable attempt\n", __FUNCTION__); | 4420 | printf("%s: Digital Loopback mode enable attempt\n", __FUNCTION__); |
| 4364 | if (~value & 1) { /* RRST */ | 4421 | if (~value & 1) { /* RRST */ |
| 4365 | s->spcr[0] &= ~6; | 4422 | s->spcr[0] &= ~6; |
| 4366 | - qemu_irq_lower(s->txdrq); | ||
| 4367 | - if (s->codec) | ||
| 4368 | - s->codec->in.len = 0; | 4423 | + s->rx_req = 0; |
| 4424 | + omap_mcbsp_rx_done(s); | ||
| 4369 | } | 4425 | } |
| 4370 | - if (s->codec) | ||
| 4371 | - omap_mcbsp_rate_update(s); | ||
| 4372 | omap_mcbsp_req_update(s); | 4426 | omap_mcbsp_req_update(s); |
| 4373 | return; | 4427 | return; |
| 4374 | 4428 | ||
| @@ -4386,11 +4440,11 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | @@ -4386,11 +4440,11 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | ||
| 4386 | return; | 4440 | return; |
| 4387 | case 0x14: /* SRGR2 */ | 4441 | case 0x14: /* SRGR2 */ |
| 4388 | s->srgr[1] = value & 0xffff; | 4442 | s->srgr[1] = value & 0xffff; |
| 4389 | - omap_mcbsp_rate_update(s); | 4443 | + omap_mcbsp_req_update(s); |
| 4390 | return; | 4444 | return; |
| 4391 | case 0x16: /* SRGR1 */ | 4445 | case 0x16: /* SRGR1 */ |
| 4392 | s->srgr[0] = value & 0xffff; | 4446 | s->srgr[0] = value & 0xffff; |
| 4393 | - omap_mcbsp_rate_update(s); | 4447 | + omap_mcbsp_req_update(s); |
| 4394 | return; | 4448 | return; |
| 4395 | case 0x18: /* MCR2 */ | 4449 | case 0x18: /* MCR2 */ |
| 4396 | s->mcr[1] = value & 0x03e3; | 4450 | s->mcr[1] = value & 0x03e3; |
| @@ -4460,6 +4514,37 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | @@ -4460,6 +4514,37 @@ static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | ||
| 4460 | OMAP_BAD_REG(addr); | 4514 | OMAP_BAD_REG(addr); |
| 4461 | } | 4515 | } |
| 4462 | 4516 | ||
| 4517 | +static void omap_mcbsp_writew(void *opaque, target_phys_addr_t addr, | ||
| 4518 | + uint32_t value) | ||
| 4519 | +{ | ||
| 4520 | + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; | ||
| 4521 | + int offset = addr & OMAP_MPUI_REG_MASK; | ||
| 4522 | + | ||
| 4523 | + if (offset == 0x04) { /* DXR */ | ||
| 4524 | + if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ | ||
| 4525 | + return; | ||
| 4526 | + if (s->tx_req > 3) { | ||
| 4527 | + s->tx_req -= 4; | ||
| 4528 | + if (s->codec && s->codec->cts) { | ||
| 4529 | + s->codec->out.fifo[s->codec->out.len ++] = | ||
| 4530 | + (value >> 24) & 0xff; | ||
| 4531 | + s->codec->out.fifo[s->codec->out.len ++] = | ||
| 4532 | + (value >> 16) & 0xff; | ||
| 4533 | + s->codec->out.fifo[s->codec->out.len ++] = | ||
| 4534 | + (value >> 8) & 0xff; | ||
| 4535 | + s->codec->out.fifo[s->codec->out.len ++] = | ||
| 4536 | + (value >> 0) & 0xff; | ||
| 4537 | + } | ||
| 4538 | + if (s->tx_req < 4) | ||
| 4539 | + omap_mcbsp_tx_done(s); | ||
| 4540 | + } else | ||
| 4541 | + printf("%s: Tx FIFO overrun\n", __FUNCTION__); | ||
| 4542 | + return; | ||
| 4543 | + } | ||
| 4544 | + | ||
| 4545 | + omap_badwidth_write16(opaque, addr, value); | ||
| 4546 | +} | ||
| 4547 | + | ||
| 4463 | static CPUReadMemoryFunc *omap_mcbsp_readfn[] = { | 4548 | static CPUReadMemoryFunc *omap_mcbsp_readfn[] = { |
| 4464 | omap_badwidth_read16, | 4549 | omap_badwidth_read16, |
| 4465 | omap_mcbsp_read, | 4550 | omap_mcbsp_read, |
| @@ -4468,8 +4553,8 @@ static CPUReadMemoryFunc *omap_mcbsp_readfn[] = { | @@ -4468,8 +4553,8 @@ static CPUReadMemoryFunc *omap_mcbsp_readfn[] = { | ||
| 4468 | 4553 | ||
| 4469 | static CPUWriteMemoryFunc *omap_mcbsp_writefn[] = { | 4554 | static CPUWriteMemoryFunc *omap_mcbsp_writefn[] = { |
| 4470 | omap_badwidth_write16, | 4555 | omap_badwidth_write16, |
| 4471 | - omap_mcbsp_write, | ||
| 4472 | - omap_badwidth_write16, | 4556 | + omap_mcbsp_writeh, |
| 4557 | + omap_mcbsp_writew, | ||
| 4473 | }; | 4558 | }; |
| 4474 | 4559 | ||
| 4475 | static void omap_mcbsp_reset(struct omap_mcbsp_s *s) | 4560 | static void omap_mcbsp_reset(struct omap_mcbsp_s *s) |
| @@ -4484,8 +4569,11 @@ static void omap_mcbsp_reset(struct omap_mcbsp_s *s) | @@ -4484,8 +4569,11 @@ static void omap_mcbsp_reset(struct omap_mcbsp_s *s) | ||
| 4484 | memset(&s->rcer, 0, sizeof(s->rcer)); | 4569 | memset(&s->rcer, 0, sizeof(s->rcer)); |
| 4485 | memset(&s->xcer, 0, sizeof(s->xcer)); | 4570 | memset(&s->xcer, 0, sizeof(s->xcer)); |
| 4486 | s->tx_req = 0; | 4571 | s->tx_req = 0; |
| 4572 | + s->rx_req = 0; | ||
| 4487 | s->tx_rate = 0; | 4573 | s->tx_rate = 0; |
| 4488 | s->rx_rate = 0; | 4574 | s->rx_rate = 0; |
| 4575 | + qemu_del_timer(s->source_timer); | ||
| 4576 | + qemu_del_timer(s->sink_timer); | ||
| 4489 | } | 4577 | } |
| 4490 | 4578 | ||
| 4491 | struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base, | 4579 | 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, | @@ -4500,6 +4588,8 @@ struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base, | ||
| 4500 | s->rxirq = irq[1]; | 4588 | s->rxirq = irq[1]; |
| 4501 | s->txdrq = dma[0]; | 4589 | s->txdrq = dma[0]; |
| 4502 | s->rxdrq = dma[1]; | 4590 | s->rxdrq = dma[1]; |
| 4591 | + s->sink_timer = qemu_new_timer(vm_clock, omap_mcbsp_sink_tick, s); | ||
| 4592 | + s->source_timer = qemu_new_timer(vm_clock, omap_mcbsp_source_tick, s); | ||
| 4503 | omap_mcbsp_reset(s); | 4593 | omap_mcbsp_reset(s); |
| 4504 | 4594 | ||
| 4505 | iomemtype = cpu_register_io_memory(0, omap_mcbsp_readfn, | 4595 | 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) | @@ -4513,14 +4603,20 @@ static void omap_mcbsp_i2s_swallow(void *opaque, int line, int level) | ||
| 4513 | { | 4603 | { |
| 4514 | struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; | 4604 | struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; |
| 4515 | 4605 | ||
| 4516 | - omap_mcbsp_rx_start(s); | 4606 | + if (s->rx_rate) { |
| 4607 | + s->rx_req = s->codec->in.len; | ||
| 4608 | + omap_mcbsp_rx_newdata(s); | ||
| 4609 | + } | ||
| 4517 | } | 4610 | } |
| 4518 | 4611 | ||
| 4519 | static void omap_mcbsp_i2s_start(void *opaque, int line, int level) | 4612 | static void omap_mcbsp_i2s_start(void *opaque, int line, int level) |
| 4520 | { | 4613 | { |
| 4521 | struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; | 4614 | struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; |
| 4522 | 4615 | ||
| 4523 | - omap_mcbsp_tx_start(s); | 4616 | + if (s->tx_rate) { |
| 4617 | + s->tx_req = s->codec->out.size; | ||
| 4618 | + omap_mcbsp_tx_newdata(s); | ||
| 4619 | + } | ||
| 4524 | } | 4620 | } |
| 4525 | 4621 | ||
| 4526 | void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, struct i2s_codec_s *slave) | 4622 | void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, struct i2s_codec_s *slave) |
hw/omap.h
| @@ -491,6 +491,11 @@ struct i2s_codec_s { | @@ -491,6 +491,11 @@ struct i2s_codec_s { | ||
| 491 | qemu_irq rx_swallow; | 491 | qemu_irq rx_swallow; |
| 492 | qemu_irq tx_start; | 492 | qemu_irq tx_start; |
| 493 | 493 | ||
| 494 | + int tx_rate; | ||
| 495 | + int cts; | ||
| 496 | + int rx_rate; | ||
| 497 | + int rts; | ||
| 498 | + | ||
| 494 | struct i2s_fifo_s { | 499 | struct i2s_fifo_s { |
| 495 | uint8_t *fifo; | 500 | uint8_t *fifo; |
| 496 | int len; | 501 | int len; |
hw/tsc210x.c
| @@ -283,10 +283,30 @@ static void tsc210x_audio_out_cb(struct tsc210x_state_s *s, int free_b) | @@ -283,10 +283,30 @@ static void tsc210x_audio_out_cb(struct tsc210x_state_s *s, int free_b) | ||
| 283 | qemu_irq_raise(s->codec.tx_start); | 283 | qemu_irq_raise(s->codec.tx_start); |
| 284 | } | 284 | } |
| 285 | 285 | ||
| 286 | -static void tsc2102_audio_set_format(struct tsc210x_state_s *s) | 286 | +static void tsc2102_audio_rate_update(struct tsc210x_state_s *s) |
| 287 | { | 287 | { |
| 288 | - int enable; | ||
| 289 | const struct tsc210x_rate_info_s *rate; | 288 | const struct tsc210x_rate_info_s *rate; |
| 289 | + | ||
| 290 | + s->codec.tx_rate = 0; | ||
| 291 | + s->codec.rx_rate = 0; | ||
| 292 | + if (s->dac_power & (1 << 15)) /* PWDNC */ | ||
| 293 | + return; | ||
| 294 | + | ||
| 295 | + for (rate = tsc2102_rates; rate->rate; rate ++) | ||
| 296 | + if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */ | ||
| 297 | + rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */ | ||
| 298 | + break; | ||
| 299 | + if (!rate->rate) { | ||
| 300 | + printf("%s: unknown sampling rate configured\n", __FUNCTION__); | ||
| 301 | + return; | ||
| 302 | + } | ||
| 303 | + | ||
| 304 | + s->codec.tx_rate = rate->rate; | ||
| 305 | +} | ||
| 306 | + | ||
| 307 | +static void tsc2102_audio_output_update(struct tsc210x_state_s *s) | ||
| 308 | +{ | ||
| 309 | + int enable; | ||
| 290 | audsettings_t fmt; | 310 | audsettings_t fmt; |
| 291 | 311 | ||
| 292 | if (s->dac_voice[0]) { | 312 | if (s->dac_voice[0]) { |
| @@ -296,32 +316,26 @@ static void tsc2102_audio_set_format(struct tsc210x_state_s *s) | @@ -296,32 +316,26 @@ static void tsc2102_audio_set_format(struct tsc210x_state_s *s) | ||
| 296 | AUD_close_out(&s->card, s->dac_voice[0]); | 316 | AUD_close_out(&s->card, s->dac_voice[0]); |
| 297 | s->dac_voice[0] = 0; | 317 | s->dac_voice[0] = 0; |
| 298 | } | 318 | } |
| 319 | + s->codec.cts = 0; | ||
| 299 | 320 | ||
| 300 | enable = | 321 | enable = |
| 301 | (~s->dac_power & (1 << 15)) && /* PWDNC */ | 322 | (~s->dac_power & (1 << 15)) && /* PWDNC */ |
| 302 | (~s->dac_power & (1 << 10)); /* DAPWDN */ | 323 | (~s->dac_power & (1 << 10)); /* DAPWDN */ |
| 303 | - if (!enable) | ||
| 304 | - return; | ||
| 305 | - | ||
| 306 | - for (rate = tsc2102_rates; rate->rate; rate ++) | ||
| 307 | - if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */ | ||
| 308 | - rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */ | ||
| 309 | - break; | ||
| 310 | - if (!rate->rate) { | ||
| 311 | - printf("%s: unknown sampling rate configured\n", __FUNCTION__); | 324 | + if (!enable || !s->codec.tx_rate) |
| 312 | return; | 325 | return; |
| 313 | - } | ||
| 314 | 326 | ||
| 315 | /* Force our own sampling rate even in slave DAC mode */ | 327 | /* Force our own sampling rate even in slave DAC mode */ |
| 316 | fmt.endianness = 0; | 328 | fmt.endianness = 0; |
| 317 | fmt.nchannels = 2; | 329 | fmt.nchannels = 2; |
| 318 | - fmt.freq = rate->rate; | 330 | + fmt.freq = s->codec.tx_rate; |
| 319 | fmt.fmt = AUD_FMT_S16; | 331 | fmt.fmt = AUD_FMT_S16; |
| 320 | 332 | ||
| 321 | s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], | 333 | s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], |
| 322 | "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); | 334 | "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); |
| 323 | - if (s->dac_voice[0]) | 335 | + if (s->dac_voice[0]) { |
| 336 | + s->codec.cts = 1; | ||
| 324 | AUD_set_active_out(s->dac_voice[0], 1); | 337 | AUD_set_active_out(s->dac_voice[0], 1); |
| 338 | + } | ||
| 325 | } | 339 | } |
| 326 | 340 | ||
| 327 | static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg) | 341 | static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg) |
| @@ -587,8 +601,9 @@ static void tsc2102_audio_register_write( | @@ -587,8 +601,9 @@ static void tsc2102_audio_register_write( | ||
| 587 | fprintf(stderr, "tsc2102_audio_register_write: " | 601 | fprintf(stderr, "tsc2102_audio_register_write: " |
| 588 | "wrong value written into Audio 1\n"); | 602 | "wrong value written into Audio 1\n"); |
| 589 | #endif | 603 | #endif |
| 604 | + tsc2102_audio_rate_update(s); | ||
| 590 | if (s->audio) | 605 | if (s->audio) |
| 591 | - tsc2102_audio_set_format(s); | 606 | + tsc2102_audio_output_update(s); |
| 592 | return; | 607 | return; |
| 593 | 608 | ||
| 594 | case 0x01: | 609 | case 0x01: |
| @@ -631,8 +646,9 @@ static void tsc2102_audio_register_write( | @@ -631,8 +646,9 @@ static void tsc2102_audio_register_write( | ||
| 631 | fprintf(stderr, "tsc2102_audio_register_write: " | 646 | fprintf(stderr, "tsc2102_audio_register_write: " |
| 632 | "wrong value written into Power\n"); | 647 | "wrong value written into Power\n"); |
| 633 | #endif | 648 | #endif |
| 649 | + tsc2102_audio_rate_update(s); | ||
| 634 | if (s->audio) | 650 | if (s->audio) |
| 635 | - tsc2102_audio_set_format(s); | 651 | + tsc2102_audio_output_update(s); |
| 636 | return; | 652 | return; |
| 637 | 653 | ||
| 638 | case 0x06: /* Audio Control 3 */ | 654 | case 0x06: /* Audio Control 3 */ |
| @@ -644,7 +660,7 @@ static void tsc2102_audio_register_write( | @@ -644,7 +660,7 @@ static void tsc2102_audio_register_write( | ||
| 644 | "wrong value written into Audio 3\n"); | 660 | "wrong value written into Audio 3\n"); |
| 645 | #endif | 661 | #endif |
| 646 | if (s->audio) | 662 | if (s->audio) |
| 647 | - tsc2102_audio_set_format(s); | 663 | + tsc2102_audio_output_update(s); |
| 648 | return; | 664 | return; |
| 649 | 665 | ||
| 650 | case 0x07: /* LCH_BASS_BOOST_N0 */ | 666 | case 0x07: /* LCH_BASS_BOOST_N0 */ |