#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/err.h>
+#include <linux/amba/bus.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/hardware/amba.h>
+#include <asm/sizes.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/ac97_codec.h>
*/
#undef CONFIG_PM
-static void aaci_ac97_select_codec(struct aaci *aaci, ac97_t *ac97)
+static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97)
{
u32 v, maincr = aaci->maincr | MAINCR_SCRA(ac97->num);
* SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR
* register.
*/
-static void aaci_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
{
struct aaci *aaci = ac97->private_data;
u32 v;
+ int timeout = 5000;
if (ac97->num >= 4)
return;
- down(&aaci->ac97_sem);
+ mutex_lock(&aaci->ac97_sem);
aaci_ac97_select_codec(aaci, ac97);
*/
do {
v = readl(aaci->base + AACI_SLFR);
- } while (v & (SLFR_1TXB|SLFR_2TXB));
+ } while ((v & (SLFR_1TXB|SLFR_2TXB)) && timeout--);
+
+ if (!timeout)
+ dev_err(&aaci->dev->dev,
+ "timeout waiting for write to complete\n");
- up(&aaci->ac97_sem);
+ mutex_unlock(&aaci->ac97_sem);
}
/*
* Read an AC'97 register.
*/
-static unsigned short aaci_ac97_read(ac97_t *ac97, unsigned short reg)
+static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct aaci *aaci = ac97->private_data;
u32 v;
+ int timeout = 5000;
+ int retries = 10;
if (ac97->num >= 4)
return ~0;
- down(&aaci->ac97_sem);
+ mutex_lock(&aaci->ac97_sem);
aaci_ac97_select_codec(aaci, ac97);
*/
do {
v = readl(aaci->base + AACI_SLFR);
- } while (v & SLFR_1TXB);
+ } while ((v & SLFR_1TXB) && timeout--);
+
+ if (!timeout) {
+ dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n");
+ v = ~0;
+ goto out;
+ }
/*
* Give the AC'97 codec more than enough time
/*
* Wait for slot 2 to indicate data.
*/
+ timeout = 5000;
do {
cond_resched();
v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV);
- } while (v != (SLFR_1RXV|SLFR_2RXV));
+ } while ((v != (SLFR_1RXV|SLFR_2RXV)) && timeout--);
- v = readl(aaci->base + AACI_SL1RX) >> 12;
- if (v == reg) {
- v = readl(aaci->base + AACI_SL2RX) >> 4;
- } else {
- dev_err(&aaci->dev->dev,
- "wrong ac97 register read back (%x != %x)\n",
- v, reg);
+ if (!timeout) {
+ dev_err(&aaci->dev->dev, "timeout on RX valid\n");
v = ~0;
+ goto out;
}
- up(&aaci->ac97_sem);
+ do {
+ v = readl(aaci->base + AACI_SL1RX) >> 12;
+ if (v == reg) {
+ v = readl(aaci->base + AACI_SL2RX) >> 4;
+ break;
+ } else if (--retries) {
+ dev_warn(&aaci->dev->dev,
+ "ac97 read back fail. retry\n");
+ continue;
+ } else {
+ dev_warn(&aaci->dev->dev,
+ "wrong ac97 register read back (%x != %x)\n",
+ v, reg);
+ v = ~0;
+ }
+ } while (retries);
+ out:
+ mutex_unlock(&aaci->ac97_sem);
return v;
}
/*
* Interrupt support.
*/
-static void aaci_fifo_irq(struct aaci *aaci, u32 mask)
+static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
{
+ if (mask & ISR_ORINTR) {
+ dev_warn(&aaci->dev->dev, "RX overrun on chan %d\n", channel);
+ writel(ICLR_RXOEC1 << channel, aaci->base + AACI_INTCLR);
+ }
+
+ if (mask & ISR_RXTOINTR) {
+ dev_warn(&aaci->dev->dev, "RX timeout on chan %d\n", channel);
+ writel(ICLR_RXTOFEC1 << channel, aaci->base + AACI_INTCLR);
+ }
+
+ if (mask & ISR_RXINTR) {
+ struct aaci_runtime *aacirun = &aaci->capture;
+ void *ptr;
+
+ if (!aacirun->substream || !aacirun->start) {
+ dev_warn(&aaci->dev->dev, "RX interrupt???\n");
+ writel(0, aacirun->base + AACI_IE);
+ return;
+ }
+ ptr = aacirun->ptr;
+
+ do {
+ unsigned int len = aacirun->fifosz;
+ u32 val;
+
+ if (aacirun->bytes <= 0) {
+ aacirun->bytes += aacirun->period;
+ aacirun->ptr = ptr;
+ spin_unlock(&aaci->lock);
+ snd_pcm_period_elapsed(aacirun->substream);
+ spin_lock(&aaci->lock);
+ }
+ if (!(aacirun->cr & CR_EN))
+ break;
+
+ val = readl(aacirun->base + AACI_SR);
+ if (!(val & SR_RXHF))
+ break;
+ if (!(val & SR_RXFF))
+ len >>= 1;
+
+ aacirun->bytes -= len;
+
+ /* reading 16 bytes at a time */
+ for( ; len > 0; len -= 16) {
+ asm(
+ "ldmia %1, {r0, r1, r2, r3}\n\t"
+ "stmia %0!, {r0, r1, r2, r3}"
+ : "+r" (ptr)
+ : "r" (aacirun->fifo)
+ : "r0", "r1", "r2", "r3", "cc");
+
+ if (ptr >= aacirun->end)
+ ptr = aacirun->start;
+ }
+ } while(1);
+ aacirun->ptr = ptr;
+ }
+
if (mask & ISR_URINTR) {
- writel(ICLR_TXUEC1, aaci->base + AACI_INTCLR);
+ dev_dbg(&aaci->dev->dev, "TX underrun on chan %d\n", channel);
+ writel(ICLR_TXUEC1 << channel, aaci->base + AACI_INTCLR);
}
if (mask & ISR_TXINTR) {
void *ptr;
if (!aacirun->substream || !aacirun->start) {
- dev_warn(&aaci->dev->dev, "TX interrupt???");
+ dev_warn(&aaci->dev->dev, "TX interrupt???\n");
writel(0, aacirun->base + AACI_IE);
return;
}
snd_pcm_period_elapsed(aacirun->substream);
spin_lock(&aaci->lock);
}
- if (!(aacirun->cr & TXCR_TXEN))
+ if (!(aacirun->cr & CR_EN))
break;
val = readl(aacirun->base + AACI_SR);
}
}
-static irqreturn_t aaci_irq(int irq, void *devid, struct pt_regs *regs)
+static irqreturn_t aaci_irq(int irq, void *devid)
{
struct aaci *aaci = devid;
u32 mask;
u32 m = mask;
for (i = 0; i < 4; i++, m >>= 7) {
if (m & 0x7f) {
- aaci_fifo_irq(aaci, m);
+ aaci_fifo_irq(aaci, i, m);
}
}
}
* (unimplemented)
*/
static int
-aaci_rule_rate_by_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
+aaci_rule_rate_by_channels(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule)
{
struct aaci *aaci = rule->private;
unsigned int rate_mask = SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_5512;
- snd_interval_t *c = hw_param_interval(p, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *c = hw_param_interval(p, SNDRV_PCM_HW_PARAM_CHANNELS);
switch (c->max) {
case 6:
rate_mask);
}
-static snd_pcm_hardware_t aaci_hw_info = {
+static struct snd_pcm_hardware aaci_hw_info = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
.periods_max = PAGE_SIZE / 16,
};
-static int aaci_pcm_open(struct aaci *aaci, snd_pcm_substream_t *substream,
- struct aaci_runtime *aacirun)
+static int __aaci_pcm_open(struct aaci *aaci,
+ struct snd_pcm_substream *substream,
+ struct aaci_runtime *aacirun)
{
- snd_pcm_runtime_t *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
aacirun->substream = substream;
if (ret)
goto out;
- ret = request_irq(aaci->dev->irq[0], aaci_irq, SA_SHIRQ|SA_INTERRUPT,
+ ret = request_irq(aaci->dev->irq[0], aaci_irq, IRQF_SHARED|IRQF_DISABLED,
DRIVER_NAME, aaci);
if (ret)
goto out;
/*
* Common ALSA stuff
*/
-static int aaci_pcm_close(snd_pcm_substream_t *substream)
+static int aaci_pcm_close(struct snd_pcm_substream *substream)
{
struct aaci *aaci = substream->private_data;
struct aaci_runtime *aacirun = substream->runtime->private_data;
- WARN_ON(aacirun->cr & TXCR_TXEN);
+ WARN_ON(aacirun->cr & CR_EN);
aacirun->substream = NULL;
free_irq(aaci->dev->irq[0], aaci);
return 0;
}
-static int aaci_pcm_hw_free(snd_pcm_substream_t *substream)
+static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct aaci_runtime *aacirun = substream->runtime->private_data;
/*
* This must not be called with the device enabled.
*/
- WARN_ON(aacirun->cr & TXCR_TXEN);
+ WARN_ON(aacirun->cr & CR_EN);
if (aacirun->pcm_open)
snd_ac97_pcm_close(aacirun->pcm);
return 0;
}
-static int aaci_pcm_hw_params(snd_pcm_substream_t *substream,
+static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
struct aaci_runtime *aacirun,
- snd_pcm_hw_params_t *params)
+ struct snd_pcm_hw_params *params)
{
int err;
if (err < 0)
goto out;
- err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
- params_channels(params),
- aacirun->pcm->r[0].slots);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
+ params_channels(params),
+ aacirun->pcm->r[0].slots);
+ else
+ err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
+ params_channels(params),
+ aacirun->pcm->r[1].slots);
+
if (err)
goto out;
return err;
}
-static int aaci_pcm_prepare(snd_pcm_substream_t *substream)
+static int aaci_pcm_prepare(struct snd_pcm_substream *substream)
{
- snd_pcm_runtime_t *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct aaci_runtime *aacirun = runtime->private_data;
aacirun->start = (void *)runtime->dma_area;
return 0;
}
-static snd_pcm_uframes_t aaci_pcm_pointer(snd_pcm_substream_t *substream)
+static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
{
- snd_pcm_runtime_t *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct aaci_runtime *aacirun = runtime->private_data;
ssize_t bytes = aacirun->ptr - aacirun->start;
return bytes_to_frames(runtime, bytes);
}
-static int aaci_pcm_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *vma)
+static int aaci_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
{
return devdma_mmap(NULL, substream, vma);
}
* Playback specific ALSA stuff
*/
static const u32 channels_to_txmask[] = {
- [2] = TXCR_TX3 | TXCR_TX4,
- [4] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8,
- [6] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8 | TXCR_TX6 | TXCR_TX9,
+ [2] = CR_SL3 | CR_SL4,
+ [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8,
+ [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
};
/*
static unsigned int channel_list[] = { 2, 4, 6 };
static int
-aaci_rule_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
+aaci_rule_channels(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule)
{
struct aaci *aaci = rule->private;
unsigned int chan_mask = 1 << 0, slots;
chan_mask);
}
-static int aaci_pcm_playback_open(snd_pcm_substream_t *substream)
+static int aaci_pcm_open(struct snd_pcm_substream *substream)
{
struct aaci *aaci = substream->private_data;
int ret;
if (ret)
return ret;
- return aaci_pcm_open(aaci, substream, &aaci->playback);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = __aaci_pcm_open(aaci, substream, &aaci->playback);
+ } else {
+ ret = __aaci_pcm_open(aaci, substream, &aaci->capture);
+ }
+ return ret;
}
-static int aaci_pcm_playback_hw_params(snd_pcm_substream_t *substream,
- snd_pcm_hw_params_t *params)
+static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct aaci *aaci = substream->private_data;
struct aaci_runtime *aacirun = substream->runtime->private_data;
* FIXME: double rate slots?
*/
if (ret >= 0) {
- aacirun->cr = TXCR_FEN | TXCR_COMPACT | TXCR_TSZ16;
+ aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
aacirun->cr |= channels_to_txmask[channels];
aacirun->fifosz = aaci->fifosize * 4;
- if (aacirun->cr & TXCR_COMPACT)
+ if (aacirun->cr & CR_COMPACT)
aacirun->fifosz >>= 1;
}
return ret;
ie = readl(aacirun->base + AACI_IE);
ie &= ~(IE_URIE|IE_TXIE);
writel(ie, aacirun->base + AACI_IE);
- aacirun->cr &= ~TXCR_TXEN;
+ aacirun->cr &= ~CR_EN;
aaci_chan_wait_ready(aacirun);
writel(aacirun->cr, aacirun->base + AACI_TXCR);
}
u32 ie;
aaci_chan_wait_ready(aacirun);
- aacirun->cr |= TXCR_TXEN;
+ aacirun->cr |= CR_EN;
ie = readl(aacirun->base + AACI_IE);
ie |= IE_URIE | IE_TXIE;
writel(aacirun->cr, aacirun->base + AACI_TXCR);
}
-static int aaci_pcm_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct aaci *aaci = substream->private_data;
struct aaci_runtime *aacirun = substream->runtime->private_data;
return ret;
}
-static snd_pcm_ops_t aaci_playback_ops = {
- .open = aaci_pcm_playback_open,
+static struct snd_pcm_ops aaci_playback_ops = {
+ .open = aaci_pcm_open,
.close = aaci_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = aaci_pcm_playback_hw_params,
.mmap = aaci_pcm_mmap,
};
+static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct aaci *aaci = substream->private_data;
+ struct aaci_runtime *aacirun = substream->runtime->private_data;
+ int ret;
+
+ ret = aaci_pcm_hw_params(substream, aacirun, params);
+
+ if (ret >= 0) {
+ aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
+
+ /* Line in record: slot 3 and 4 */
+ aacirun->cr |= CR_SL3 | CR_SL4;
+
+ aacirun->fifosz = aaci->fifosize * 4;
+
+ if (aacirun->cr & CR_COMPACT)
+ aacirun->fifosz >>= 1;
+ }
+ return ret;
+}
+
+static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun)
+{
+ u32 ie;
+
+ aaci_chan_wait_ready(aacirun);
+
+ ie = readl(aacirun->base + AACI_IE);
+ ie &= ~(IE_ORIE | IE_RXIE);
+ writel(ie, aacirun->base+AACI_IE);
+
+ aacirun->cr &= ~CR_EN;
+
+ writel(aacirun->cr, aacirun->base + AACI_RXCR);
+}
+
+static void aaci_pcm_capture_start(struct aaci_runtime *aacirun)
+{
+ u32 ie;
+
+ aaci_chan_wait_ready(aacirun);
+
+#ifdef DEBUG
+ /* RX Timeout value: bits 28:17 in RXCR */
+ aacirun->cr |= 0xf << 17;
+#endif
+ aacirun->cr |= CR_EN;
+ writel(aacirun->cr, aacirun->base + AACI_RXCR);
+
+ ie = readl(aacirun->base + AACI_IE);
+ ie |= IE_ORIE |IE_RXIE; // overrun and rx interrupt -- half full
+ writel(ie, aacirun->base + AACI_IE);
+}
+
+static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct aaci *aaci = substream->private_data;
+ struct aaci_runtime *aacirun = substream->runtime->private_data;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&aaci->lock, flags);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ aaci_pcm_capture_start(aacirun);
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ aaci_pcm_capture_start(aacirun);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ aaci_pcm_capture_stop(aacirun);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ aaci_pcm_capture_stop(aacirun);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&aaci->lock, flags);
+
+ return ret;
+}
+
+static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct aaci *aaci = substream->private_data;
+
+ aaci_pcm_prepare(substream);
+
+ /* allow changing of sample rate */
+ aaci_ac97_write(aaci->ac97, AC97_EXTENDED_STATUS, 0x0001); /* VRA */
+ aaci_ac97_write(aaci->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+ aaci_ac97_write(aaci->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate);
+
+ /* Record select: Mic: 0, Aux: 3, Line: 4 */
+ aaci_ac97_write(aaci->ac97, AC97_REC_SEL, 0x0404);
+
+ return 0;
+}
+
+static struct snd_pcm_ops aaci_capture_ops = {
+ .open = aaci_pcm_open,
+ .close = aaci_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = aaci_pcm_capture_hw_params,
+ .hw_free = aaci_pcm_hw_free,
+ .prepare = aaci_pcm_capture_prepare,
+ .trigger = aaci_pcm_capture_trigger,
+ .pointer = aaci_pcm_pointer,
+ .mmap = aaci_pcm_mmap,
+};
/*
* Power Management.
*/
#ifdef CONFIG_PM
-static int aaci_do_suspend(snd_card_t *card, unsigned int state)
+static int aaci_do_suspend(struct snd_card *card, unsigned int state)
{
struct aaci *aaci = card->private_data;
- if (aaci->card->power_state != SNDRV_CTL_POWER_D3cold) {
- snd_pcm_suspend_all(aaci->pcm);
- snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D3cold);
- }
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
+ snd_pcm_suspend_all(aaci->pcm);
return 0;
}
-static int aaci_do_resume(snd_card_t *card, unsigned int state)
+static int aaci_do_resume(struct snd_card *card, unsigned int state)
{
- struct aaci *aaci = card->private_data;
- if (aaci->card->power_state != SNDRV_CTL_POWER_D0) {
- snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D0);
- }
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
static int aaci_suspend(struct amba_device *dev, pm_message_t state)
{
- snd_card_t *card = amba_get_drvdata(dev);
+ struct snd_card *card = amba_get_drvdata(dev);
return card ? aaci_do_suspend(card) : 0;
}
static int aaci_resume(struct amba_device *dev)
{
- snd_card_t *card = amba_get_drvdata(dev);
+ struct snd_card *card = amba_get_drvdata(dev);
return card ? aaci_do_resume(card) : 0;
}
#else
static struct ac97_pcm ac97_defs[] __devinitdata = {
- [0] = { /* Front PCM */
+ [0] = { /* Front PCM */
.exclusive = 1,
.r = {
[0] = {
}
};
-static ac97_bus_ops_t aaci_bus_ops = {
+static struct snd_ac97_bus_ops aaci_bus_ops = {
.write = aaci_ac97_write,
.read = aaci_ac97_read,
};
static int __devinit aaci_probe_ac97(struct aaci *aaci)
{
- ac97_template_t ac97_template;
- ac97_bus_t *ac97_bus;
- ac97_t *ac97;
+ struct snd_ac97_template ac97_template;
+ struct snd_ac97_bus *ac97_bus;
+ struct snd_ac97 *ac97;
int ret;
/*
ac97_bus->clock = 48000;
aaci->ac97_bus = ac97_bus;
- memset(&ac97_template, 0, sizeof(ac97_template_t));
+ memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
ac97_template.private_data = aaci;
ac97_template.num = 0;
ac97_template.scaps = AC97_SCAP_SKIP_MODEM;
ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
if (ret)
goto out;
+ aaci->ac97 = ac97;
/*
* Disable AC97 PC Beep input on audio codecs.
goto out;
aaci->playback.pcm = &ac97_bus->pcms[0];
+ aaci->capture.pcm = &ac97_bus->pcms[1];
out:
return ret;
}
-static void aaci_free_card(snd_card_t *card)
+static void aaci_free_card(struct snd_card *card)
{
struct aaci *aaci = card->private_data;
if (aaci->base)
static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
{
struct aaci *aaci;
- snd_card_t *card;
+ struct snd_card *card;
card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
THIS_MODULE, sizeof(struct aaci));
return ERR_PTR(-ENOMEM);
card->private_free = aaci_free_card;
- snd_card_set_pm_callback(card, aaci_do_suspend, aaci_do_resume, NULL);
strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%08lx, irq %d",
- card->shortname, dev->res.start, dev->irq[0]);
+ "%s at 0x%016llx, irq %d",
+ card->shortname, (unsigned long long)dev->res.start,
+ dev->irq[0]);
aaci = card->private_data;
- init_MUTEX(&aaci->ac97_sem);
+ mutex_init(&aaci->ac97_sem);
spin_lock_init(&aaci->lock);
aaci->card = card;
aaci->dev = dev;
static int __devinit aaci_init_pcm(struct aaci *aaci)
{
- snd_pcm_t *pcm;
+ struct snd_pcm *pcm;
int ret;
- ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 0, &pcm);
+ ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 1, &pcm);
if (ret == 0) {
aaci->pcm = pcm;
pcm->private_data = aaci;
strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
}
return ret;
static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
{
- void __iomem *base = aaci->base + AACI_CSCH1;
+ struct aaci_runtime *aacirun = &aaci->playback;
int i;
- writel(TXCR_FEN | TXCR_TSZ16 | TXCR_TXEN, base + AACI_TXCR);
+ writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR);
- for (i = 0; !(readl(base + AACI_SR) & SR_TXFF) && i < 4096; i++)
- writel(0, aaci->base + AACI_DR1);
+ for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++)
+ writel(0, aacirun->fifo);
- writel(0, base + AACI_TXCR);
+ writel(0, aacirun->base + AACI_TXCR);
/*
* Re-initialise the AACI after the FIFO depth test, to
aaci->playback.base = aaci->base + AACI_CSCH1;
aaci->playback.fifo = aaci->base + AACI_DR1;
+ /*
+ * Capture uses AACI channel 0
+ */
+ aaci->capture.base = aaci->base + AACI_CSCH1;
+ aaci->capture.fifo = aaci->base + AACI_DR1;
+
for (i = 0; i < 4; i++) {
void __iomem *base = aaci->base + i * 0x14;
writel(0x1fff, aaci->base + AACI_INTCLR);
writel(aaci->maincr, aaci->base + AACI_MAINCR);
+ ret = aaci_probe_ac97(aaci);
+ if (ret)
+ goto out;
+
/*
- * Size the FIFOs.
+ * Size the FIFOs (must be multiple of 16).
*/
aaci->fifosize = aaci_size_fifo(aaci);
-
- ret = aaci_probe_ac97(aaci);
- if (ret)
+ if (aaci->fifosize & 15) {
+ printk(KERN_WARNING "AACI: fifosize = %d not supported\n",
+ aaci->fifosize);
+ ret = -ENODEV;
goto out;
+ }
ret = aaci_init_pcm(aaci);
if (ret)
ret = snd_card_register(aaci->card);
if (ret == 0) {
dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname,
- aaci->fifosize);
+ aaci->fifosize);
amba_set_drvdata(dev, aaci->card);
return ret;
}
static int __devexit aaci_remove(struct amba_device *dev)
{
- snd_card_t *card = amba_get_drvdata(dev);
+ struct snd_card *card = amba_get_drvdata(dev);
amba_set_drvdata(dev, NULL);