diff options
Diffstat (limited to 'sound/atmel/ac97c.c')
-rw-r--r-- | sound/atmel/ac97c.c | 128 |
1 files changed, 109 insertions, 19 deletions
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index dd72e00..0c0f877 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -1,5 +1,5 @@ /* - * Driver for the Atmel AC97C controller + * Driver for Atmel AC97C * * Copyright (C) 2005-2009 Atmel Corporation * @@ -10,6 +10,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/bitmap.h> +#include <linux/device.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <linux/init.h> @@ -65,6 +66,7 @@ struct atmel_ac97c { /* Serialize access to opened variable */ spinlock_t lock; void __iomem *regs; + int irq; int opened; int reset_pin; }; @@ -150,10 +152,10 @@ static struct snd_pcm_hardware atmel_ac97c_hw = { .rate_max = 48000, .channels_min = 1, .channels_max = 2, - .buffer_bytes_max = 64 * 4096, + .buffer_bytes_max = 2 * 2 * 64 * 2048, .period_bytes_min = 4096, .period_bytes_max = 4096, - .periods_min = 4, + .periods_min = 6, .periods_max = 64, }; @@ -297,9 +299,11 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) { struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long word = 0; + unsigned long word = ac97c_readl(chip, OCA); int retval; + word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); + /* assign channels to AC97C channel A */ switch (runtime->channels) { case 1: @@ -312,7 +316,6 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) default: /* TODO: support more than two channels */ return -EINVAL; - break; } ac97c_writel(chip, OCA, word); @@ -324,13 +327,25 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) word |= AC97C_CMR_CEM_LITTLE; break; case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ - default: word &= ~(AC97C_CMR_CEM_LITTLE); break; + default: + word = ac97c_readl(chip, OCA); + word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); + ac97c_writel(chip, OCA, word); + return -EINVAL; } + /* Enable underrun interrupt on channel A */ + word |= AC97C_CSR_UNRUN; + ac97c_writel(chip, CAMR, word); + /* Enable channel A event interrupt */ + word = ac97c_readl(chip, IMR); + word |= AC97C_SR_CAEVT; + ac97c_writel(chip, IER, word); + /* set variable rate if needed */ if (runtime->rate != 48000) { word = ac97c_readl(chip, MR); @@ -359,9 +374,11 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) { struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long word = 0; + unsigned long word = ac97c_readl(chip, ICA); int retval; + word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); + /* assign channels to AC97C channel A */ switch (runtime->channels) { case 1: @@ -374,7 +391,6 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) default: /* TODO: support more than two channels */ return -EINVAL; - break; } ac97c_writel(chip, ICA, word); @@ -386,13 +402,25 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) word |= AC97C_CMR_CEM_LITTLE; break; case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ - default: word &= ~(AC97C_CMR_CEM_LITTLE); break; + default: + word = ac97c_readl(chip, ICA); + word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); + ac97c_writel(chip, ICA, word); + return -EINVAL; } + /* Enable overrun interrupt on channel A */ + word |= AC97C_CSR_OVRUN; + ac97c_writel(chip, CAMR, word); + /* Enable channel A event interrupt */ + word = ac97c_readl(chip, IMR); + word |= AC97C_SR_CAEVT; + ac97c_writel(chip, IER, word); + /* set variable rate if needed */ if (runtime->rate != 48000) { word = ac97c_readl(chip, MR); @@ -543,6 +571,43 @@ static struct snd_pcm_ops atmel_ac97_capture_ops = { .pointer = atmel_ac97c_capture_pointer, }; +static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev) +{ + struct atmel_ac97c *chip = (struct atmel_ac97c *)dev; + irqreturn_t retval = IRQ_NONE; + u32 sr = ac97c_readl(chip, SR); + u32 casr = ac97c_readl(chip, CASR); + u32 cosr = ac97c_readl(chip, COSR); + + if (sr & AC97C_SR_CAEVT) { + dev_info(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n", + casr & AC97C_CSR_OVRUN ? " OVRUN" : "", + casr & AC97C_CSR_RXRDY ? " RXRDY" : "", + casr & AC97C_CSR_UNRUN ? " UNRUN" : "", + casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", + casr & AC97C_CSR_TXRDY ? " TXRDY" : "", + !casr ? " NONE" : ""); + retval = IRQ_HANDLED; + } + + if (sr & AC97C_SR_COEVT) { + dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n", + cosr & AC97C_CSR_OVRUN ? " OVRUN" : "", + cosr & AC97C_CSR_RXRDY ? " RXRDY" : "", + cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", + cosr & AC97C_CSR_TXRDY ? " TXRDY" : "", + !cosr ? " NONE" : ""); + retval = IRQ_HANDLED; + } + + if (retval == IRQ_NONE) { + dev_err(&chip->pdev->dev, "spurious interrupt sr 0x%08x " + "casr 0x%08x cosr 0x%08x\n", sr, casr, cosr); + } + + return retval; +} + static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip) { struct snd_pcm *pcm; @@ -665,17 +730,17 @@ static bool filter(struct dma_chan *chan, void *slave) static void atmel_ac97c_reset(struct atmel_ac97c *chip) { - ac97c_writel(chip, MR, AC97C_MR_WRST); + ac97c_writel(chip, MR, 0); + ac97c_writel(chip, MR, AC97C_MR_ENA); + ac97c_writel(chip, CAMR, 0); + ac97c_writel(chip, COMR, 0); if (gpio_is_valid(chip->reset_pin)) { gpio_set_value(chip->reset_pin, 0); /* AC97 v2.2 specifications says minimum 1 us. */ - udelay(10); + udelay(2); gpio_set_value(chip->reset_pin, 1); } - - udelay(1); - ac97c_writel(chip, MR, AC97C_MR_ENA); } static int __devinit atmel_ac97c_probe(struct platform_device *pdev) @@ -690,6 +755,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) .read = atmel_ac97c_read, }; int retval; + int irq; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { @@ -703,6 +769,12 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) return -ENXIO; } + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_dbg(&pdev->dev, "could not get irq\n"); + return -ENXIO; + } + pclk = clk_get(&pdev->dev, "pclk"); if (IS_ERR(pclk)) { dev_dbg(&pdev->dev, "no peripheral clock\n"); @@ -719,6 +791,13 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) chip = get_chip(card); + retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip); + if (retval) { + dev_dbg(&pdev->dev, "unable to request irq %d\n", irq); + goto err_request_irq; + } + chip->irq = irq; + spin_lock_init(&chip->lock); strcpy(card->driver, "Atmel AC97C"); @@ -747,14 +826,18 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) snd_card_set_dev(card, &pdev->dev); + atmel_ac97c_reset(chip); + + /* Enable overrun interrupt from codec channel */ + ac97c_writel(chip, COMR, AC97C_CSR_OVRUN); + ac97c_writel(chip, IER, ac97c_readl(chip, IMR) | AC97C_SR_COEVT); + retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus); if (retval) { dev_dbg(&pdev->dev, "could not register on ac97 bus\n"); goto err_ac97_bus; } - atmel_ac97c_reset(chip); - retval = atmel_ac97c_mixer_new(chip); if (retval) { dev_dbg(&pdev->dev, "could not register ac97 mixer\n"); @@ -773,7 +856,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) chip->dma.rx_chan = dma_request_channel(mask, filter, dws); dev_info(&chip->pdev->dev, "using %s for DMA RX\n", - chip->dma.rx_chan->dev->device.bus_id); + dev_name(&chip->dma.rx_chan->dev->device)); set_bit(DMA_RX_CHAN_PRESENT, &chip->flags); } @@ -789,7 +872,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) chip->dma.tx_chan = dma_request_channel(mask, filter, dws); dev_info(&chip->pdev->dev, "using %s for DMA TX\n", - chip->dma.tx_chan->dev->device.bus_id); + dev_name(&chip->dma.tx_chan->dev->device)); set_bit(DMA_TX_CHAN_PRESENT, &chip->flags); } @@ -809,7 +892,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) retval = snd_card_register(card); if (retval) { dev_dbg(&pdev->dev, "could not register sound card\n"); - goto err_ac97_bus; + goto err_dma; } platform_set_drvdata(pdev, card); @@ -836,6 +919,8 @@ err_ac97_bus: iounmap(chip->regs); err_ioremap: + free_irq(irq, chip); +err_request_irq: snd_card_free(card); err_snd_card_new: clk_disable(pclk); @@ -884,9 +969,14 @@ static int __devexit atmel_ac97c_remove(struct platform_device *pdev) if (gpio_is_valid(chip->reset_pin)) gpio_free(chip->reset_pin); + ac97c_writel(chip, CAMR, 0); + ac97c_writel(chip, COMR, 0); + ac97c_writel(chip, MR, 0); + clk_disable(chip->pclk); clk_put(chip->pclk); iounmap(chip->regs); + free_irq(chip->irq, chip); if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags)) dma_release_channel(chip->dma.rx_chan); |