aboutsummaryrefslogtreecommitdiff
path: root/sound/spi/at73c213.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/spi/at73c213.c')
-rw-r--r--sound/spi/at73c213.c149
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");