/*
* linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
*
* Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Documentation: ARM DDI 0173B
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/device.h>
#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/sizes.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/ac97_codec.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "aaci.h"
#include "devdma.h"
#define DRIVER_NAME "aaci-pl041"
/*
* PM support is not complete. Turn it off.
*/
#undef CONFIG_PM
static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97)
{
u32 v, maincr = aaci->maincr | MAINCR_SCRA(ac97->num);
/*
* Ensure that the slot 1/2 RX registers are empty.
*/
v = readl(aaci->base + AACI_SLFR);
if (v & SLFR_2RXV)
readl(aaci->base + AACI_SL2RX);
if (v & SLFR_1RXV)
readl(aaci->base + AACI_SL1RX);
writel(maincr, aaci->base + AACI_MAINCR);
}
/*
* P29:
* The recommended use of programming the external codec through slot 1
* and slot 2 data is to use the channels during setup routines and the
* slot register at any other time. The data written into slot 1, slot 2
* and slot 12 registers is transmitted only when their corresponding
* SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR
* register.
*/
static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct aaci *aaci = ac97->private_data;
u32 v;
if (ac97->num >= 4)
return;
mutex_lock(&aaci->ac97_sem);
aaci_ac97_select_codec(aaci, ac97);
/*
* P54: You must ensure that AACI_SL2TX is always written
* to, if required, before data is written to AACI_SL1TX.
*/
writel(val << 4, aaci->base + AACI_SL2TX);
writel(reg << 12, aaci->base + AACI_SL1TX);
/*
* Wait for the transmission of both slots to complete.
*/
do {
v = readl(aaci->base + AACI_SLFR);
} while (v & (SLFR_1TXB|SLFR_2TXB));
mutex_unlock(&aaci->ac97_sem);
}
/*
* Read an AC'97 register.
*/
static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct aaci *aaci = ac97->private_data;
u32 v;
if (ac97->num >= 4)
return ~0;
mutex_lock(&aaci->ac97_sem);
aaci_ac97_select_codec(aaci, ac97);
/*
* Write the register address to slot 1.
*/
writel((reg << 12) | (1 << 19), aaci->base + AACI_SL1TX);
/*
* Wait for the transmission to complete.
*/
do {
v = readl(aaci->base + AACI_SLFR);
} while (v & SLFR_1TXB);
/*
* Give the AC'97 codec more than enough time
* to respond. (42us = ~2 frames at 48kHz.)
*/
udelay(42);
/*
* Wait for slot 2 to indicate data.
*/
do {
cond_resched();
v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV);
} while (v != (SLFR_1RXV|SLFR_2RXV));
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);
v = ~0;
}
mutex_unlock(&aaci->ac97_sem);
return v;
}
static inline void aaci_chan_wait_ready(struct aaci_runtime *aacirun)
{
u32 val;
int timeout = 5000;
do {
val = readl(aacirun->base + AACI_SR);
} while (val & (SR_TXB|SR_RXB) && timeout--);
}
/*
* Interrupt support.
*/
static void aaci_fifo_irq(struct aaci *aaci, u32 mask)
{
if (mask & ISR_URINTR) {
writel(ICLR_TXUEC1, aaci->base + AACI_INTCLR);
}
if (mask & ISR_TXINTR) {
struct aaci_runtime *aacirun = &aaci->playback;
void *ptr;
if (!aacirun->substream ||