diff options
-rw-r--r-- | sound/ppc/pmac.c | 107 |
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 | */ | ||
55 | static struct pmac_dbdma emergency_dbdma; | ||
56 | static 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 | */ | ||
411 | static 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 | */ |
380 | static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) | 461 | static 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 | } |