aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorT. H. Huth <th.huth@googlemail.com>2008-01-16 09:57:08 -0500
committerJaroslav Kysela <perex@perex.cz>2008-01-31 11:30:01 -0500
commite70515dd518bbd5b9e2e5c90a56347df0e871389 (patch)
treeaa6579fdf4f4ba186b8daceca661cd8a27b81a49 /sound
parent599c3e76fe89b314667e699a20ad08f8d16d0454 (diff)
[ALSA] snd-powermac: handle dead DMA transfers
This patch provides the snd-powermac sound driver with the ability to handle dead DMA transfers. If a dead DMA transfer is detected, the driver now sets up a new DMA transfer to continue with the sound output at the point where the old transfer died. This dead DMA transfer handling has become necessary with recent kernels on certain G4 PowerMacs. Please refer to the following URLs for more information: https://bugtrack.alsa-project.org/alsa-bug/view.php?id=3126 https://bugs.launchpad.net/ubuntu/+source/linux-source-2.6.20/+bug/87652 http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=436723 The patch is based on the dead DMA transfer handling code from the old dmasound driver which can be found in the file sound/oss/dmasound/dmasound_awacs.c in the Linux source code. Signed-off-by: T. H. Huth <th.huth@googlemail.com> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound')
-rw-r--r--sound/ppc/pmac.c107
1 files changed, 102 insertions, 5 deletions
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index aada1d7dc3c4..613a565e04de 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -44,6 +44,18 @@ static int tumbler_freqs[1] = {
44 44100 44 44100
45}; 45};
46 46
47
48/*
49 * we will allocate a single 'emergency' dbdma cmd block to use if the
50 * tx status comes up "DEAD". This happens on some PowerComputing Pmac
51 * clones, either owing to a bug in dbdma or some interaction between
52 * IDE and sound. However, this measure would deal with DEAD status if
53 * it appeared elsewhere.
54 */
55static struct pmac_dbdma emergency_dbdma;
56static int emergency_in_use;
57
58
47/* 59/*
48 * allocate DBDMA command arrays 60 * allocate DBDMA command arrays
49 */ 61 */
@@ -375,6 +387,75 @@ static snd_pcm_uframes_t snd_pmac_capture_pointer(struct snd_pcm_substream *subs
375 387
376 388
377/* 389/*
390 * Handle DEAD DMA transfers:
391 * if the TX status comes up "DEAD" - reported on some Power Computing machines
392 * we need to re-start the dbdma - but from a different physical start address
393 * and with a different transfer length. It would get very messy to do this
394 * with the normal dbdma_cmd blocks - we would have to re-write the buffer start
395 * addresses each time. So, we will keep a single dbdma_cmd block which can be
396 * fiddled with.
397 * When DEAD status is first reported the content of the faulted dbdma block is
398 * copied into the emergency buffer and we note that the buffer is in use.
399 * we then bump the start physical address by the amount that was successfully
400 * output before it died.
401 * On any subsequent DEAD result we just do the bump-ups (we know that we are
402 * already using the emergency dbdma_cmd).
403 * CHECK: this just tries to "do it". It is possible that we should abandon
404 * xfers when the number of residual bytes gets below a certain value - I can
405 * see that this might cause a loop-forever if a too small transfer causes
406 * DEAD status. However this is a TODO for now - we'll see what gets reported.
407 * When we get a successful transfer result with the emergency buffer we just
408 * pretend that it completed using the original dmdma_cmd and carry on. The
409 * 'next_cmd' field will already point back to the original loop of blocks.
410 */
411static inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec,
412 volatile struct dbdma_cmd __iomem *cp)
413{
414 unsigned short req, res ;
415 unsigned int phy ;
416
417 /* printk(KERN_WARNING "snd-powermac: DMA died - patching it up!\n"); */
418
419 /* to clear DEAD status we must first clear RUN
420 set it to quiescent to be on the safe side */
421 (void)in_le32(&rec->dma->status);
422 out_le32(&rec->dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
423
424 if (!emergency_in_use) { /* new problem */
425 memcpy((void *)emergency_dbdma.cmds, (void *)cp,
426 sizeof(struct dbdma_cmd));
427 emergency_in_use = 1;
428 st_le16(&cp->xfer_status, 0);
429 st_le16(&cp->req_count, rec->period_size);
430 cp = emergency_dbdma.cmds;
431 }
432
433 /* now bump the values to reflect the amount
434 we haven't yet shifted */
435 req = ld_le16(&cp->req_count);
436 res = ld_le16(&cp->res_count);
437 phy = ld_le32(&cp->phy_addr);
438 phy += (req - res);
439 st_le16(&cp->req_count, res);
440 st_le16(&cp->res_count, 0);
441 st_le16(&cp->xfer_status, 0);
442 st_le32(&cp->phy_addr, phy);
443
444 st_le32(&cp->cmd_dep, rec->cmd.addr
445 + sizeof(struct dbdma_cmd)*((rec->cur_period+1)%rec->nperiods));
446
447 st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS);
448
449 /* point at our patched up command block */
450 out_le32(&rec->dma->cmdptr, emergency_dbdma.addr);
451
452 /* we must re-start the controller */
453 (void)in_le32(&rec->dma->status);
454 /* should complete clearing the DEAD status */
455 out_le32(&rec->dma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
456}
457
458/*
378 * update playback/capture pointer from interrupts 459 * update playback/capture pointer from interrupts
379 */ 460 */
380static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) 461static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
@@ -385,11 +466,26 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
385 466
386 spin_lock(&chip->reg_lock); 467 spin_lock(&chip->reg_lock);
387 if (rec->running) { 468 if (rec->running) {
388 cp = &rec->cmd.cmds[rec->cur_period];
389 for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */ 469 for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */
470
471 if (emergency_in_use) /* already using DEAD xfer? */
472 cp = emergency_dbdma.cmds;
473 else
474 cp = &rec->cmd.cmds[rec->cur_period];
475
390 stat = ld_le16(&cp->xfer_status); 476 stat = ld_le16(&cp->xfer_status);
477
478 if (stat & DEAD) {
479 snd_pmac_pcm_dead_xfer(rec, cp);
480 break; /* this block is still going */
481 }
482
483 if (emergency_in_use)
484 emergency_in_use = 0 ; /* done that */
485
391 if (! (stat & ACTIVE)) 486 if (! (stat & ACTIVE))
392 break; 487 break;
488
393 /*printk("update frag %d\n", rec->cur_period);*/ 489 /*printk("update frag %d\n", rec->cur_period);*/
394 st_le16(&cp->xfer_status, 0); 490 st_le16(&cp->xfer_status, 0);
395 st_le16(&cp->req_count, rec->period_size); 491 st_le16(&cp->req_count, rec->period_size);
@@ -397,9 +493,8 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
397 rec->cur_period++; 493 rec->cur_period++;
398 if (rec->cur_period >= rec->nperiods) { 494 if (rec->cur_period >= rec->nperiods) {
399 rec->cur_period = 0; 495 rec->cur_period = 0;
400 cp = rec->cmd.cmds; 496 }
401 } else 497
402 cp++;
403 spin_unlock(&chip->reg_lock); 498 spin_unlock(&chip->reg_lock);
404 snd_pcm_period_elapsed(rec->substream); 499 snd_pcm_period_elapsed(rec->substream);
405 spin_lock(&chip->reg_lock); 500 spin_lock(&chip->reg_lock);
@@ -769,6 +864,7 @@ static int snd_pmac_free(struct snd_pmac *chip)
769 snd_pmac_dbdma_free(chip, &chip->playback.cmd); 864 snd_pmac_dbdma_free(chip, &chip->playback.cmd);
770 snd_pmac_dbdma_free(chip, &chip->capture.cmd); 865 snd_pmac_dbdma_free(chip, &chip->capture.cmd);
771 snd_pmac_dbdma_free(chip, &chip->extra_dma); 866 snd_pmac_dbdma_free(chip, &chip->extra_dma);
867 snd_pmac_dbdma_free(chip, &emergency_dbdma);
772 if (chip->macio_base) 868 if (chip->macio_base)
773 iounmap(chip->macio_base); 869 iounmap(chip->macio_base);
774 if (chip->latch_base) 870 if (chip->latch_base)
@@ -1107,7 +1203,8 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
1107 1203
1108 if (snd_pmac_dbdma_alloc(chip, &chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 || 1204 if (snd_pmac_dbdma_alloc(chip, &chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 ||
1109 snd_pmac_dbdma_alloc(chip, &chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 || 1205 snd_pmac_dbdma_alloc(chip, &chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 ||
1110 snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0) { 1206 snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0 ||
1207 snd_pmac_dbdma_alloc(chip, &emergency_dbdma, 2) < 0) {
1111 err = -ENOMEM; 1208 err = -ENOMEM;
1112 goto __error; 1209 goto __error;
1113 } 1210 }