diff options
Diffstat (limited to 'sound/ppc/pmac.c')
| -rw-r--r-- | sound/ppc/pmac.c | 110 |
1 files changed, 103 insertions, 7 deletions
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 4f9b19c90a43..613a565e04de 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c | |||
| @@ -20,7 +20,6 @@ | |||
| 20 | */ | 20 | */ |
| 21 | 21 | ||
| 22 | 22 | ||
| 23 | #include <sound/driver.h> | ||
| 24 | #include <asm/io.h> | 23 | #include <asm/io.h> |
| 25 | #include <asm/irq.h> | 24 | #include <asm/irq.h> |
| 26 | #include <linux/init.h> | 25 | #include <linux/init.h> |
| @@ -45,6 +44,18 @@ static int tumbler_freqs[1] = { | |||
| 45 | 44100 | 44 | 44100 |
| 46 | }; | 45 | }; |
| 47 | 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 | |||
| 48 | /* | 59 | /* |
| 49 | * allocate DBDMA command arrays | 60 | * allocate DBDMA command arrays |
| 50 | */ | 61 | */ |
| @@ -376,6 +387,75 @@ static snd_pcm_uframes_t snd_pmac_capture_pointer(struct snd_pcm_substream *subs | |||
| 376 | 387 | ||
| 377 | 388 | ||
| 378 | /* | 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 | /* | ||
| 379 | * update playback/capture pointer from interrupts | 459 | * update playback/capture pointer from interrupts |
| 380 | */ | 460 | */ |
| 381 | 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) |
| @@ -386,11 +466,26 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) | |||
| 386 | 466 | ||
| 387 | spin_lock(&chip->reg_lock); | 467 | spin_lock(&chip->reg_lock); |
| 388 | if (rec->running) { | 468 | if (rec->running) { |
| 389 | cp = &rec->cmd.cmds[rec->cur_period]; | ||
| 390 | 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 | |||
| 391 | 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 | |||
| 392 | if (! (stat & ACTIVE)) | 486 | if (! (stat & ACTIVE)) |
| 393 | break; | 487 | break; |
| 488 | |||
| 394 | /*printk("update frag %d\n", rec->cur_period);*/ | 489 | /*printk("update frag %d\n", rec->cur_period);*/ |
| 395 | st_le16(&cp->xfer_status, 0); | 490 | st_le16(&cp->xfer_status, 0); |
| 396 | st_le16(&cp->req_count, rec->period_size); | 491 | st_le16(&cp->req_count, rec->period_size); |
| @@ -398,9 +493,8 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) | |||
| 398 | rec->cur_period++; | 493 | rec->cur_period++; |
| 399 | if (rec->cur_period >= rec->nperiods) { | 494 | if (rec->cur_period >= rec->nperiods) { |
| 400 | rec->cur_period = 0; | 495 | rec->cur_period = 0; |
| 401 | cp = rec->cmd.cmds; | 496 | } |
| 402 | } else | 497 | |
| 403 | cp++; | ||
| 404 | spin_unlock(&chip->reg_lock); | 498 | spin_unlock(&chip->reg_lock); |
| 405 | snd_pcm_period_elapsed(rec->substream); | 499 | snd_pcm_period_elapsed(rec->substream); |
| 406 | spin_lock(&chip->reg_lock); | 500 | spin_lock(&chip->reg_lock); |
| @@ -770,6 +864,7 @@ static int snd_pmac_free(struct snd_pmac *chip) | |||
| 770 | snd_pmac_dbdma_free(chip, &chip->playback.cmd); | 864 | snd_pmac_dbdma_free(chip, &chip->playback.cmd); |
| 771 | snd_pmac_dbdma_free(chip, &chip->capture.cmd); | 865 | snd_pmac_dbdma_free(chip, &chip->capture.cmd); |
| 772 | 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); | ||
| 773 | if (chip->macio_base) | 868 | if (chip->macio_base) |
| 774 | iounmap(chip->macio_base); | 869 | iounmap(chip->macio_base); |
| 775 | if (chip->latch_base) | 870 | if (chip->latch_base) |
| @@ -1028,7 +1123,7 @@ static int pmac_auto_mute_put(struct snd_kcontrol *kcontrol, | |||
| 1028 | { | 1123 | { |
| 1029 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); | 1124 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
| 1030 | if (ucontrol->value.integer.value[0] != chip->auto_mute) { | 1125 | if (ucontrol->value.integer.value[0] != chip->auto_mute) { |
| 1031 | chip->auto_mute = ucontrol->value.integer.value[0]; | 1126 | chip->auto_mute = !!ucontrol->value.integer.value[0]; |
| 1032 | if (chip->update_automute) | 1127 | if (chip->update_automute) |
| 1033 | chip->update_automute(chip, 1); | 1128 | chip->update_automute(chip, 1); |
| 1034 | return 1; | 1129 | return 1; |
| @@ -1108,7 +1203,8 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) | |||
| 1108 | 1203 | ||
| 1109 | 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 || |
| 1110 | 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 || |
| 1111 | 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) { | ||
| 1112 | err = -ENOMEM; | 1208 | err = -ENOMEM; |
| 1113 | goto __error; | 1209 | goto __error; |
| 1114 | } | 1210 | } |
