diff options
Diffstat (limited to 'sound/ppc')
-rw-r--r-- | sound/ppc/awacs.c | 21 | ||||
-rw-r--r-- | sound/ppc/beep.c | 8 | ||||
-rw-r--r-- | sound/ppc/burgundy.c | 12 | ||||
-rw-r--r-- | sound/ppc/daca.c | 18 | ||||
-rw-r--r-- | sound/ppc/keywest.c | 1 | ||||
-rw-r--r-- | sound/ppc/pmac.c | 110 | ||||
-rw-r--r-- | sound/ppc/powermac.c | 1 | ||||
-rw-r--r-- | sound/ppc/snd_ps3.c | 2 | ||||
-rw-r--r-- | sound/ppc/tumbler.c | 56 |
9 files changed, 182 insertions, 47 deletions
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 05dabe45465..8441e780df0 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -20,7 +20,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/nvram.h> #include <linux/init.h> @@ -175,10 +174,12 @@ static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol, int inverted = (kcontrol->private_value >> 16) & 1; int val, oldval; unsigned long flags; - int vol[2]; + unsigned int vol[2]; vol[0] = ucontrol->value.integer.value[0]; vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] > 0x0f || vol[1] > 0x0f) + return -EINVAL; if (inverted) { vol[0] = 0x0f - vol[0]; vol[1] = 0x0f - vol[1]; @@ -421,10 +422,14 @@ static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol, struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; struct awacs_amp *amp = chip->mixer_data; + unsigned int val; snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); - if (ucontrol->value.integer.value[0] != amp->amp_tone[index]) { - amp->amp_tone[index] = ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0]; + if (val > 14) + return -EINVAL; + if (val != amp->amp_tone[index]) { + amp->amp_tone[index] = val; awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); return 1; } @@ -456,9 +461,13 @@ static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct awacs_amp *amp = chip->mixer_data; + unsigned int val; snd_assert(amp, return -EINVAL); - if (ucontrol->value.integer.value[0] != amp->amp_master) { - amp->amp_master = ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0]; + if (val > 99) + return -EINVAL; + if (val != amp->amp_master) { + amp->amp_master = val; awacs_amp_set_master(amp, amp->amp_master); return 1; } diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index 566b5ab9d4e..baa2a723737 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/init.h> @@ -195,10 +194,13 @@ static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); - int oval; + unsigned int oval, nval; snd_assert(chip->beep, return -ENXIO); oval = chip->beep->volume; - chip->beep->volume = ucontrol->value.integer.value[0]; + nval = ucontrol->value.integer.value[0]; + if (nval > 100) + return -EINVAL; + chip->beep->volume = nval; return oval != chip->beep->volume; } diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index e02263fe44d..1a545ac0de0 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/init.h> #include <linux/slab.h> @@ -136,6 +135,9 @@ snd_pmac_burgundy_write_volume(struct snd_pmac *chip, unsigned int address, { int hardvolume, lvolume, rvolume; + if (volume[0] < 0 || volume[0] > 100 || + volume[1] < 0 || volume[1] > 100) + return; /* -EINVAL */ lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; @@ -301,14 +303,14 @@ static int snd_pmac_burgundy_put_volume_out(struct snd_kcontrol *kcontrol, struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); int stereo = (kcontrol->private_value >> 24) & 1; - int oval, val; + unsigned int oval, val; oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; - val = ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0] & 15; if (stereo) - val |= ucontrol->value.integer.value[1] << 4; + val |= (ucontrol->value.integer.value[1] & 15) << 4; else - val |= ucontrol->value.integer.value[0] << 4; + val |= val << 4; val = ~val & 0xff; snd_pmac_burgundy_wcb(chip, addr, val); return val != oval; diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c index c5a1f0be6a4..8432c16cd6f 100644 --- a/sound/ppc/daca.c +++ b/sound/ppc/daca.c @@ -19,7 +19,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/kmod.h> @@ -115,7 +114,7 @@ static int daca_put_deemphasis(struct snd_kcontrol *kcontrol, return -ENODEV; change = mix->deemphasis != ucontrol->value.integer.value[0]; if (change) { - mix->deemphasis = ucontrol->value.integer.value[0]; + mix->deemphasis = !!ucontrol->value.integer.value[0]; daca_set_volume(mix); } return change; @@ -149,15 +148,20 @@ static int daca_put_volume(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_daca *mix; + unsigned int vol[2]; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->left_vol != ucontrol->value.integer.value[0] || - mix->right_vol != ucontrol->value.integer.value[1]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] > DACA_VOL_MAX || vol[1] > DACA_VOL_MAX) + return -EINVAL; + change = mix->left_vol != vol[0] || + mix->right_vol != vol[1]; if (change) { - mix->left_vol = ucontrol->value.integer.value[0]; - mix->right_vol = ucontrol->value.integer.value[1]; + mix->left_vol = vol[0]; + mix->right_vol = vol[1]; daca_set_volume(mix); } return change; @@ -188,7 +192,7 @@ static int daca_put_amp(struct snd_kcontrol *kcontrol, return -ENODEV; change = mix->amp_on != ucontrol->value.integer.value[0]; if (change) { - mix->amp_on = ucontrol->value.integer.value[0]; + mix->amp_on = !!ucontrol->value.integer.value[0]; i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG, mix->amp_on ? 0x05 : 0x04); } diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index bb7d744faff..6ff99ed7751 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -19,7 +19,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/delay.h> diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 4f9b19c90a4..613a565e04d 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -20,7 +20,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/init.h> @@ -45,6 +44,18 @@ static int tumbler_freqs[1] = { 44100 }; + +/* + * we will allocate a single 'emergency' dbdma cmd block to use if the + * tx status comes up "DEAD". This happens on some PowerComputing Pmac + * clones, either owing to a bug in dbdma or some interaction between + * IDE and sound. However, this measure would deal with DEAD status if + * it appeared elsewhere. + */ +static struct pmac_dbdma emergency_dbdma; +static int emergency_in_use; + + /* * allocate DBDMA command arrays */ @@ -376,6 +387,75 @@ static snd_pcm_uframes_t snd_pmac_capture_pointer(struct snd_pcm_substream *subs /* + * Handle DEAD DMA transfers: + * if the TX status comes up "DEAD" - reported on some Power Computing machines + * we need to re-start the dbdma - but from a different physical start address + * and with a different transfer length. It would get very messy to do this + * with the normal dbdma_cmd blocks - we would have to re-write the buffer start + * addresses each time. So, we will keep a single dbdma_cmd block which can be + * fiddled with. + * When DEAD status is first reported the content of the faulted dbdma block is + * copied into the emergency buffer and we note that the buffer is in use. + * we then bump the start physical address by the amount that was successfully + * output before it died. + * On any subsequent DEAD result we just do the bump-ups (we know that we are + * already using the emergency dbdma_cmd). + * CHECK: this just tries to "do it". It is possible that we should abandon + * xfers when the number of residual bytes gets below a certain value - I can + * see that this might cause a loop-forever if a too small transfer causes + * DEAD status. However this is a TODO for now - we'll see what gets reported. + * When we get a successful transfer result with the emergency buffer we just + * pretend that it completed using the original dmdma_cmd and carry on. The + * 'next_cmd' field will already point back to the original loop of blocks. + */ +static inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec, + volatile struct dbdma_cmd __iomem *cp) +{ + unsigned short req, res ; + unsigned int phy ; + + /* printk(KERN_WARNING "snd-powermac: DMA died - patching it up!\n"); */ + + /* to clear DEAD status we must first clear RUN + set it to quiescent to be on the safe side */ + (void)in_le32(&rec->dma->status); + out_le32(&rec->dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + + if (!emergency_in_use) { /* new problem */ + memcpy((void *)emergency_dbdma.cmds, (void *)cp, + sizeof(struct dbdma_cmd)); + emergency_in_use = 1; + st_le16(&cp->xfer_status, 0); + st_le16(&cp->req_count, rec->period_size); + cp = emergency_dbdma.cmds; + } + + /* now bump the values to reflect the amount + we haven't yet shifted */ + req = ld_le16(&cp->req_count); + res = ld_le16(&cp->res_count); + phy = ld_le32(&cp->phy_addr); + phy += (req - res); + st_le16(&cp->req_count, res); + st_le16(&cp->res_count, 0); + st_le16(&cp->xfer_status, 0); + st_le32(&cp->phy_addr, phy); + + st_le32(&cp->cmd_dep, rec->cmd.addr + + sizeof(struct dbdma_cmd)*((rec->cur_period+1)%rec->nperiods)); + + st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS); + + /* point at our patched up command block */ + out_le32(&rec->dma->cmdptr, emergency_dbdma.addr); + + /* we must re-start the controller */ + (void)in_le32(&rec->dma->status); + /* should complete clearing the DEAD status */ + out_le32(&rec->dma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); +} + +/* * update playback/capture pointer from interrupts */ 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) spin_lock(&chip->reg_lock); if (rec->running) { - cp = &rec->cmd.cmds[rec->cur_period]; for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */ + + if (emergency_in_use) /* already using DEAD xfer? */ + cp = emergency_dbdma.cmds; + else + cp = &rec->cmd.cmds[rec->cur_period]; + stat = ld_le16(&cp->xfer_status); + + if (stat & DEAD) { + snd_pmac_pcm_dead_xfer(rec, cp); + break; /* this block is still going */ + } + + if (emergency_in_use) + emergency_in_use = 0 ; /* done that */ + if (! (stat & ACTIVE)) break; + /*printk("update frag %d\n", rec->cur_period);*/ st_le16(&cp->xfer_status, 0); 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) rec->cur_period++; if (rec->cur_period >= rec->nperiods) { rec->cur_period = 0; - cp = rec->cmd.cmds; - } else - cp++; + } + spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(rec->substream); spin_lock(&chip->reg_lock); @@ -770,6 +864,7 @@ static int snd_pmac_free(struct snd_pmac *chip) snd_pmac_dbdma_free(chip, &chip->playback.cmd); snd_pmac_dbdma_free(chip, &chip->capture.cmd); snd_pmac_dbdma_free(chip, &chip->extra_dma); + snd_pmac_dbdma_free(chip, &emergency_dbdma); if (chip->macio_base) iounmap(chip->macio_base); if (chip->latch_base) @@ -1028,7 +1123,7 @@ static int pmac_auto_mute_put(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); if (ucontrol->value.integer.value[0] != chip->auto_mute) { - chip->auto_mute = ucontrol->value.integer.value[0]; + chip->auto_mute = !!ucontrol->value.integer.value[0]; if (chip->update_automute) chip->update_automute(chip, 1); return 1; @@ -1108,7 +1203,8 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) if (snd_pmac_dbdma_alloc(chip, &chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 || snd_pmac_dbdma_alloc(chip, &chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 || - snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0) { + snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0 || + snd_pmac_dbdma_alloc(chip, &emergency_dbdma, 2) < 0) { err = -ENOMEM; goto __error; } diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c index 2264574fa06..c936225771b 100644 --- a/sound/ppc/powermac.c +++ b/sound/ppc/powermac.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/platform_device.h> diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 27b61899fe8..d8d0b4b2395 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -22,7 +22,6 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/interrupt.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm.h> @@ -954,6 +953,7 @@ static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev) snd_ps3_init_avsetting(&the_card); /* register the card */ + snd_card_set_dev(the_card.card, &dev->core); ret = snd_card_register(the_card.card); if (ret < 0) goto clean_dma_map; diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 5821cdd0bec..71a7a976542 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -24,7 +24,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -275,14 +274,20 @@ static int tumbler_put_master_volume(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_tumbler *mix = chip->mixer_data; + unsigned int vol[2]; int change; snd_assert(mix, return -ENODEV); - change = mix->master_vol[0] != ucontrol->value.integer.value[0] || - mix->master_vol[1] != ucontrol->value.integer.value[1]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] >= ARRAY_SIZE(master_volume_table) || + vol[1] >= ARRAY_SIZE(master_volume_table)) + return -EINVAL; + change = mix->master_vol[0] != vol[0] || + mix->master_vol[1] != vol[1]; if (change) { - mix->master_vol[0] = ucontrol->value.integer.value[0]; - mix->master_vol[1] = ucontrol->value.integer.value[1]; + mix->master_vol[0] = vol[0]; + mix->master_vol[1] = vol[1]; tumbler_set_master_volume(mix); } return change; @@ -417,13 +422,22 @@ static int tumbler_put_drc_value(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_tumbler *mix; + unsigned int val; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->drc_range != ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0]; + if (chip->model == PMAC_TUMBLER) { + if (val > TAS3001_DRC_MAX) + return -EINVAL; + } else { + if (val > TAS3004_DRC_MAX) + return -EINVAL; + } + change = mix->drc_range != val; if (change) { - mix->drc_range = ucontrol->value.integer.value[0]; + mix->drc_range = val; if (chip->model == PMAC_TUMBLER) tumbler_set_drc(mix); else @@ -530,13 +544,17 @@ static int tumbler_put_mono(struct snd_kcontrol *kcontrol, struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_tumbler *mix; + unsigned int vol; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->mono_vol[info->index] != ucontrol->value.integer.value[0]; + vol = ucontrol->value.integer.value[0]; + if (vol >= info->max) + return -EINVAL; + change = mix->mono_vol[info->index] != vol; if (change) { - mix->mono_vol[info->index] = ucontrol->value.integer.value[0]; + mix->mono_vol[info->index] = vol; tumbler_set_mono_volume(mix, info); } return change; @@ -672,15 +690,21 @@ static int snapper_put_mix(struct snd_kcontrol *kcontrol, int idx = (int)kcontrol->private_value; struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_tumbler *mix; + unsigned int vol[2]; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] || - mix->mix_vol[idx][1] != ucontrol->value.integer.value[1]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] >= ARRAY_SIZE(mixer_volume_table) || + vol[1] >= ARRAY_SIZE(mixer_volume_table)) + return -EINVAL; + change = mix->mix_vol[idx][0] != vol[0] || + mix->mix_vol[idx][1] != vol[1]; if (change) { - mix->mix_vol[idx][0] = ucontrol->value.integer.value[0]; - mix->mix_vol[idx][1] = ucontrol->value.integer.value[1]; + mix->mix_vol[idx][0] = vol[0]; + mix->mix_vol[idx][1] = vol[1]; snapper_set_mix_vol(mix, idx); } return change; @@ -784,7 +808,7 @@ static int snapper_get_capture_source(struct snd_kcontrol *kcontrol, struct pmac_tumbler *mix = chip->mixer_data; snd_assert(mix, return -ENODEV); - ucontrol->value.integer.value[0] = mix->capture_source; + ucontrol->value.enumerated.item[0] = mix->capture_source; return 0; } @@ -796,9 +820,9 @@ static int snapper_put_capture_source(struct snd_kcontrol *kcontrol, int change; snd_assert(mix, return -ENODEV); - change = ucontrol->value.integer.value[0] != mix->capture_source; + change = ucontrol->value.enumerated.item[0] != mix->capture_source; if (change) { - mix->capture_source = !!ucontrol->value.integer.value[0]; + mix->capture_source = !!ucontrol->value.enumerated.item[0]; snapper_set_capture_source(mix); } return change; |