diff options
Diffstat (limited to 'sound/spi/at73c213.c')
| -rw-r--r-- | sound/spi/at73c213.c | 149 |
1 files changed, 70 insertions, 79 deletions
diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index fee869bcc95..39522367897 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -18,10 +18,10 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/io.h> -#include <sound/driver.h> #include <sound/initval.h> #include <sound/control.h> #include <sound/core.h> @@ -69,15 +69,16 @@ struct snd_at73c213 { int irq; int period; unsigned long bitrate; - struct clk *bitclk; struct ssc_device *ssc; struct spi_device *spi; u8 spi_wbuffer[2]; u8 spi_rbuffer[2]; /* Image of the SPI registers in AT73C213. */ u8 reg_image[18]; - /* Protect registers against concurrent access. */ + /* Protect SSC registers against concurrent access. */ spinlock_t lock; + /* Protect mixer registers against concurrent access. */ + struct mutex mixer_lock; }; #define get_chip(card) ((struct snd_at73c213 *)card->private_data) @@ -116,7 +117,7 @@ static struct snd_pcm_hardware snd_at73c213_playback_hw = { .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, /* Replaced by chip->bitrate later. */ .rate_max = 50000, /* Replaced by chip->bitrate later. */ - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 64 * 1024 - 1, .period_bytes_min = 512, @@ -131,7 +132,8 @@ static struct snd_pcm_hardware snd_at73c213_playback_hw = { static int snd_at73c213_set_bitrate(struct snd_at73c213 *chip) { unsigned long ssc_rate = clk_get_rate(chip->ssc->clk); - unsigned long dac_rate_new, ssc_div, status; + unsigned long dac_rate_new, ssc_div; + int status; unsigned long ssc_div_max, ssc_div_min; int max_tries; @@ -153,7 +155,7 @@ static int snd_at73c213_set_bitrate(struct snd_at73c213 *chip) if (max_tries < 1) max_tries = 1; - /* ssc_div must be a power of 2. */ + /* ssc_div must be even. */ ssc_div = (ssc_div + 1) & ~1UL; if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN) { @@ -172,7 +174,7 @@ static int snd_at73c213_set_bitrate(struct snd_at73c213 *chip) dac_rate_new = 8 * (ssc_rate / ssc_div); status = clk_round_rate(chip->board->dac_clk, dac_rate_new); - if (status < 0) + if (status <= 0) return status; /* Ignore difference smaller than 256 Hz. */ @@ -207,7 +209,13 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream) { struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + int err; + /* ensure buffer_size is a multiple of period_size */ + err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + return err; snd_at73c213_playback_hw.rate_min = chip->bitrate; snd_at73c213_playback_hw.rate_max = chip->bitrate; runtime->hw = snd_at73c213_playback_hw; @@ -226,6 +234,14 @@ static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream) static int snd_at73c213_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + int channels = params_channels(hw_params); + int val; + + val = ssc_readl(chip->ssc->regs, TFMR); + val = SSC_BFINS(TFMR_DATNB, channels - 1, val); + ssc_writel(chip->ssc->regs, TFMR, val); + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } @@ -247,10 +263,12 @@ static int snd_at73c213_pcm_prepare(struct snd_pcm_substream *substream) ssc_writel(chip->ssc->regs, PDC_TPR, (long)runtime->dma_addr); - ssc_writel(chip->ssc->regs, PDC_TCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TCR, + runtime->period_size * runtime->channels); ssc_writel(chip->ssc->regs, PDC_TNPR, (long)runtime->dma_addr + block_size); - ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TNCR, + runtime->period_size * runtime->channels); return 0; } @@ -312,16 +330,7 @@ static struct snd_pcm_ops at73c213_playback_ops = { .pointer = snd_at73c213_pcm_pointer, }; -static void snd_at73c213_pcm_free(struct snd_pcm *pcm) -{ - struct snd_at73c213 *chip = snd_pcm_chip(pcm); - if (chip->pcm) { - snd_pcm_lib_preallocate_free_for_all(chip->pcm); - chip->pcm = NULL; - } -} - -static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) +static int snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) { struct snd_pcm *pcm; int retval; @@ -332,7 +341,6 @@ static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) goto out; pcm->private_data = chip; - pcm->private_free = snd_at73c213_pcm_free; pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER; strcpy(pcm->name, "at73c213"); chip->pcm = pcm; @@ -373,7 +381,8 @@ static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id) ssc_writel(chip->ssc->regs, PDC_TNPR, (long)runtime->dma_addr + offset); - ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TNCR, + runtime->period_size * runtime->channels); retval = IRQ_HANDLED; } @@ -398,7 +407,7 @@ static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol, int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); ucontrol->value.integer.value[0] = (chip->reg_image[reg] >> shift) & mask; @@ -407,7 +416,7 @@ static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); return 0; } @@ -428,13 +437,13 @@ static int snd_at73c213_mono_put(struct snd_kcontrol *kcontrol, val = mask - val; val <<= shift; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); val = (chip->reg_image[reg] & ~(mask << shift)) | val; change = val != chip->reg_image[reg]; retval = snd_at73c213_write_reg(chip, reg, val); - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); if (retval) return retval; @@ -470,7 +479,7 @@ static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol, int mask = (kcontrol->private_value >> 24) & 0xff; int invert = (kcontrol->private_value >> 22) & 1; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); ucontrol->value.integer.value[0] = (chip->reg_image[left_reg] >> shift_left) & mask; @@ -484,7 +493,7 @@ static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol, mask - ucontrol->value.integer.value[1]; } - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); return 0; } @@ -511,7 +520,7 @@ static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol, val1 <<= shift_left; val2 <<= shift_right; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); val1 = (chip->reg_image[left_reg] & ~(mask << shift_left)) | val1; val2 = (chip->reg_image[right_reg] & ~(mask << shift_right)) | val2; @@ -519,16 +528,16 @@ static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol, || val2 != chip->reg_image[right_reg]; retval = snd_at73c213_write_reg(chip, left_reg, val1); if (retval) { - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); goto out; } retval = snd_at73c213_write_reg(chip, right_reg, val2); if (retval) { - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); goto out; } - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); return change; @@ -536,16 +545,7 @@ out: return retval; } -static int snd_at73c213_mono_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - - return 0; -} +#define snd_at73c213_mono_switch_info snd_ctl_boolean_mono_info static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -555,7 +555,7 @@ static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol, int shift = (kcontrol->private_value >> 8) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); ucontrol->value.integer.value[0] = (chip->reg_image[reg] >> shift) & 0x01; @@ -564,7 +564,7 @@ static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = 0x01 - ucontrol->value.integer.value[0]; - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); return 0; } @@ -589,14 +589,14 @@ static int snd_at73c213_mono_switch_put(struct snd_kcontrol *kcontrol, val = mask - val; val <<= shift; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); val |= (chip->reg_image[reg] & ~(mask << shift)); change = val != chip->reg_image[reg]; retval = snd_at73c213_write_reg(chip, reg, val); - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); if (retval) return retval; @@ -665,7 +665,7 @@ static int snd_at73c213_aux_capture_volume_info( | (mask << 24) | (invert << 22)) \ } -static struct snd_kcontrol_new snd_at73c213_controls[] __devinitdata = { +static struct snd_kcontrol_new snd_at73c213_controls[] = { AT73C213_STEREO("Master Playback Volume", 0, DAC_LMPG, DAC_RMPG, 0, 0, 0x1f, 1), AT73C213_STEREO("Master Playback Switch", 0, DAC_LMPG, DAC_RMPG, 5, 5, 1, 1), AT73C213_STEREO("PCM Playback Volume", 0, DAC_LLOG, DAC_RLOG, 0, 0, 0x1f, 1), @@ -709,7 +709,7 @@ AT73C213_MONO_SWITCH("Aux Capture Switch", 0, DAC_CTRL, DAC_CTRL_ONAUXIN, AT73C213_MONO_SWITCH("Line Capture Switch", 0, DAC_CTRL, 0, 0x03, 0), }; -static int __devinit snd_at73c213_mixer(struct snd_at73c213 *chip) +static int snd_at73c213_mixer(struct snd_at73c213 *chip) { struct snd_card *card; int errval, idx; @@ -879,8 +879,8 @@ static int snd_at73c213_dev_free(struct snd_device *device) return 0; } -static int __devinit snd_at73c213_dev_init(struct snd_card *card, - struct spi_device *spi) +static int snd_at73c213_dev_init(struct snd_card *card, + struct spi_device *spi) { static struct snd_device_ops ops = { .dev_free = snd_at73c213_dev_free, @@ -893,6 +893,7 @@ static int __devinit snd_at73c213_dev_init(struct snd_card *card, return irq; spin_lock_init(&chip->lock); + mutex_init(&chip->mixer_lock); chip->card = card; chip->irq = -1; @@ -926,8 +927,6 @@ static int __devinit snd_at73c213_dev_init(struct snd_card *card, if (retval) goto out_snd_dev; - snd_card_set_dev(card, &spi->dev); - goto out; out_snd_dev: @@ -963,12 +962,11 @@ static int snd_at73c213_probe(struct spi_device *spi) return PTR_ERR(board->dac_clk); } - retval = -ENOMEM; - /* Allocate "card" using some unused identifiers. */ snprintf(id, sizeof id, "at73c213_%d", board->ssc_id); - card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct snd_at73c213)); - if (!card) + retval = snd_card_new(&spi->dev, -1, id, THIS_MODULE, + sizeof(struct snd_at73c213), &card); + if (retval < 0) goto out; chip = card->private_data; @@ -1007,7 +1005,7 @@ out: return retval; } -static int __devexit snd_at73c213_remove(struct spi_device *spi) +static int snd_at73c213_remove(struct spi_device *spi) { struct snd_card *card = dev_get_drvdata(&spi->dev); struct snd_at73c213 *chip = card->private_data; @@ -1070,15 +1068,15 @@ out: ssc_free(chip->ssc); snd_card_free(card); - dev_set_drvdata(&spi->dev, NULL); return 0; } -#ifdef CONFIG_PM -static int snd_at73c213_suspend(struct spi_device *spi, pm_message_t msg) +#ifdef CONFIG_PM_SLEEP + +static int snd_at73c213_suspend(struct device *dev) { - struct snd_card *card = dev_get_drvdata(&spi->dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_at73c213 *chip = card->private_data; ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); @@ -1087,9 +1085,9 @@ static int snd_at73c213_suspend(struct spi_device *spi, pm_message_t msg) return 0; } -static int snd_at73c213_resume(struct spi_device *spi) +static int snd_at73c213_resume(struct device *dev) { - struct snd_card *card = dev_get_drvdata(&spi->dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_at73c213 *chip = card->private_data; clk_enable(chip->board->dac_clk); @@ -1097,33 +1095,26 @@ static int snd_at73c213_resume(struct spi_device *spi) return 0; } + +static SIMPLE_DEV_PM_OPS(at73c213_pm_ops, snd_at73c213_suspend, + snd_at73c213_resume); +#define AT73C213_PM_OPS (&at73c213_pm_ops) + #else -#define snd_at73c213_suspend NULL -#define snd_at73c213_resume NULL +#define AT73C213_PM_OPS NULL #endif static struct spi_driver at73c213_driver = { .driver = { .name = "at73c213", + .pm = AT73C213_PM_OPS, }, .probe = snd_at73c213_probe, - .suspend = snd_at73c213_suspend, - .resume = snd_at73c213_resume, - .remove = __devexit_p(snd_at73c213_remove), + .remove = snd_at73c213_remove, }; -static int __init at73c213_init(void) -{ - return spi_register_driver(&at73c213_driver); -} -module_init(at73c213_init); - -static void __exit at73c213_exit(void) -{ - spi_unregister_driver(&at73c213_driver); -} -module_exit(at73c213_exit); +module_spi_driver(at73c213_driver); -MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); +MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); MODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC"); MODULE_LICENSE("GPL"); |
