/*
* Driver for DBRI sound chip found on Sparcs.
- * Copyright (C) 2004 Martin Habets (mhabets@users.sourceforge.net)
+ * Copyright (C) 2004, 2005 Martin Habets (mhabets@users.sourceforge.net)
+ *
+ * Converted to ring buffered version by Krzysztof Helt (krzysztof.h1@wp.pl)
*
* Based entirely upon drivers/sbus/audio/dbri.c which is:
* Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
* (the second one is a monitor/tee pipe, valid only for serial input).
*
* The mmcodec is connected via the CHI bus and needs the data & some
- * parameters (volume, balance, output selection) timemultiplexed in 8 byte
+ * parameters (volume, output selection) timemultiplexed in 8 byte
* chunks. It also has a control mode, which serves for audio format setting.
*
* Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on
* audio devices. But the SUN HW group decided against it, at least on my
* LX the speakerbox connector has at least 1 pin missing and 1 wrongly
* connected.
+ *
+ * I've tried to stick to the following function naming conventions:
+ * snd_* ALSA stuff
+ * cs4215_* CS4215 codec specific stuff
+ * dbri_* DBRI high-level stuff
+ * other DBRI low-level stuff
*/
#include <sound/driver.h>
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Sun DBRI soundcard.");
-#define DBRI_DEBUG
+#undef DBRI_DEBUG
#define D_INT (1<<0)
#define D_GEN (1<<1)
#define D_USR (1<<4)
#define D_DESC (1<<5)
-static int dbri_debug = 0;
-module_param(dbri_debug, int, 0444);
+static int dbri_debug;
+module_param(dbri_debug, int, 0644);
MODULE_PARM_DESC(dbri_debug, "Debug value for Sun DBRI soundcard.");
#ifdef DBRI_DEBUG
#define dprintk(a, x...) if(dbri_debug & a) printk(KERN_DEBUG x)
-#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \
- (1 << 27) | \
- value)
#else
-#define dprintk(a, x...)
+#define dprintk(a, x...) do { } while (0)
-#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \
- (intr << 27) | \
- value)
#endif /* DBRI_DEBUG */
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \
+ (intr << 27) | \
+ value)
+
/***************************************************************************
CS4215 specific definitions and structures
****************************************************************************/
/* { NA, (1 << 4), (5 << 3) }, */
{ 48000, (1 << 4), (6 << 3) },
{ 9600, (1 << 4), (7 << 3) },
- { 5513, (2 << 4), (0 << 3) }, /* Actually 5512.5 */
+ { 5512, (2 << 4), (0 << 3) }, /* Actually 5512.5 */
{ 11025, (2 << 4), (1 << 3) },
{ 18900, (2 << 4), (2 << 3) },
{ 22050, (2 << 4), (3 << 3) },
#define REG9 0x24UL /* Interrupt Queue Pointer */
#define DBRI_NO_CMDS 64
-#define DBRI_NO_INTS 1 /* Note: the value of this define was
- * originally 2. The ringbuffer to store
- * interrupts in dma is currently broken.
- * This is a temporary fix until the ringbuffer
- * is fixed.
- */
#define DBRI_INT_BLK 64
#define DBRI_NO_DESCS 64
#define DBRI_NO_PIPES 32
-
-#define DBRI_MM_ONB 1
-#define DBRI_MM_SB 2
+#define DBRI_MAX_PIPE (DBRI_NO_PIPES - 1)
#define DBRI_REC 0
#define DBRI_PLAY 1
#define DBRI_NO_STREAMS 2
/* One transmit/receive descriptor */
+/* When ba != 0 descriptor is used */
struct dbri_mem {
volatile __u32 word1;
- volatile __u32 ba; /* Transmit/Receive Buffer Address */
- volatile __u32 nda; /* Next Descriptor Address */
+ __u32 ba; /* Transmit/Receive Buffer Address */
+ __u32 nda; /* Next Descriptor Address */
volatile __u32 word4;
};
* the CPU and the DBRI
*/
struct dbri_dma {
- volatile s32 cmd[DBRI_NO_CMDS]; /* Place for commands */
- volatile s32 intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */
+ s32 cmd[DBRI_NO_CMDS]; /* Place for commands */
+ volatile s32 intr[DBRI_INT_BLK]; /* Interrupt field */
struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */
};
struct dbri_pipe {
u32 sdp; /* SDP command word */
- enum in_or_out direction;
int nextpipe; /* Next pipe in linked list */
- int prevpipe;
- int cycle; /* Offset of timeslot (bits) */
int length; /* Length of timeslot (bits) */
int first_desc; /* Index of first descriptor */
int desc; /* Index of active descriptor */
volatile __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */
};
-struct dbri_desc {
- int inuse; /* Boolean flag */
- int next; /* Index of next desc, or -1 */
- unsigned int len;
-};
-
/* Per stream (playback or record) information */
-typedef struct dbri_streaminfo {
- snd_pcm_substream_t *substream;
+struct dbri_streaminfo {
+ struct snd_pcm_substream *substream;
u32 dvma_buffer; /* Device view of Alsa DMA buffer */
- int left; /* # of bytes left in DMA buffer */
int size; /* Size of DMA buffer */
size_t offset; /* offset in user buffer */
int pipe; /* Data pipe used */
int left_gain; /* mixer elements */
int right_gain;
- int balance;
-} dbri_streaminfo_t;
+};
/* This structure holds the information for both chips (DBRI & CS4215) */
-typedef struct snd_dbri {
- snd_card_t *card; /* ALSA card */
- snd_pcm_t *pcm;
+struct snd_dbri {
+ struct snd_card *card; /* ALSA card */
int regs_size, irq; /* Needed for unload */
struct sbus_dev *sdev; /* SBUS device info */
spinlock_t lock;
- volatile struct dbri_dma *dma; /* Pointer to our DMA block */
+ struct dbri_dma *dma; /* Pointer to our DMA block */
u32 dma_dvma; /* DBRI visible DMA address */
void __iomem *regs; /* dbri HW regs */
- int dbri_version; /* 'e' and up is OK */
int dbri_irqp; /* intr queue pointer */
- int wait_seen;
struct dbri_pipe pipes[DBRI_NO_PIPES]; /* DBRI's 32 data pipes */
- struct dbri_desc descs[DBRI_NO_DESCS];
+ int next_desc[DBRI_NO_DESCS]; /* Index of next desc, or -1 */
+ spinlock_t cmdlock; /* Protects cmd queue accesses */
+ s32 *cmdptr; /* Pointer to the last queued cmd */
- int chi_in_pipe;
- int chi_out_pipe;
int chi_bpf;
struct cs4215 mm; /* mmcodec special info */
struct dbri_streaminfo stream_info[DBRI_NO_STREAMS];
struct snd_dbri *next;
-} snd_dbri_t;
-
-/* Needed for the ALSA macros to work */
-#define chip_t snd_dbri_t
+};
#define DBRI_MAX_VOLUME 63 /* Output volume */
#define DBRI_MAX_GAIN 15 /* Input gain */
-#define DBRI_RIGHT_BALANCE 255
-#define DBRI_MID_BALANCE (DBRI_RIGHT_BALANCE >> 1)
/* DBRI Reg0 - Status Control Register - defines. (Page 17) */
#define D_P (1<<15) /* Program command & queue pointer valid */
#define DBRI_TD_TBC (1<<0) /* Transmit buffer Complete */
#define DBRI_TD_STATUS(v) ((v)&0xff) /* Transmit status */
/* Maximum buffer size per TD: almost 8Kb */
-#define DBRI_TD_MAXCNT ((1 << 13) - 1)
+#define DBRI_TD_MAXCNT ((1 << 13) - 4)
/* Receive descriptor defines */
#define DBRI_RD_F (1<<31) /* End of Frame */
/* Return a pointer to dbri_streaminfo */
#define DBRI_STREAM(dbri, substream) &dbri->stream_info[DBRI_STREAMNO(substream)]
-static snd_dbri_t *dbri_list = NULL; /* All DBRI devices */
+static struct snd_dbri *dbri_list; /* All DBRI devices */
/*
* Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
Commands are sent to the DBRI by building a list of them in memory,
then writing the address of the first list item to DBRI register 8.
-The list is terminated with a WAIT command, which can generate a
-CPU interrupt if required.
+The list is terminated with a WAIT command, which generates a
+CPU interrupt to signal completion.
Since the DBRI can run in parallel with the CPU, several means of
-synchronization present themselves. The original scheme (Rudolf's)
-was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
-an interrupt signaled completion, and wait on a wait_queue if a routine
-attempted to cmdlock while the flag was set. The problems arose when
-we tried to cmdlock from inside an interrupt handler, which might
-cause scheduling in an interrupt (if we waited), etc, etc
-
-A more sophisticated scheme might involve a circular command buffer
-or an array of command buffers. A routine could fill one with
-commands and link it onto a list. When a interrupt signaled
-completion of the current command buffer, look on the list for
-the next one.
-
-I've decided to implement something much simpler - after each command,
-the CPU waits for the DBRI to finish the command by polling the P bit
-in DBRI register 0. I've tried to implement this in such a way
-that might make implementing a more sophisticated scheme easier.
+synchronization present themselves. The method implemented here is only
+use of the dbri_cmdwait() to wait for execution of batch of sent commands.
+
+A circular command buffer is used here. A new command is being added
+while another can be executed. The scheme works by adding two WAIT commands
+after each sent batch of commands. When the next batch is prepared it is
+added after the WAIT commands then the WAITs are replaced with single JUMP
+command to the new batch. The the DBRI is forced to reread the last WAIT
+command (replaced by the JUMP by then). If the DBRI is still executing
+previous commands the request to reread the WAIT command is ignored.
Every time a routine wants to write commands to the DBRI, it must
-first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
-in return. After the commands have been writen, dbri_cmdsend() is
-called with the final pointer value.
+first call dbri_cmdlock() and get pointer to a free space in
+dbri->dma->cmd buffer. After this, the commands can be written to
+the buffer, and dbri_cmdsend() is called with the final pointer value
+to send them to the DBRI.
*/
-enum dbri_lock_t { NoGetLock, GetLock };
-
-static volatile s32 *dbri_cmdlock(snd_dbri_t * dbri, enum dbri_lock_t get)
+#define MAXLOOPS 20
+/*
+ * Wait for the current command string to execute
+ */
+static void dbri_cmdwait(struct snd_dbri *dbri)
{
-#ifndef SMP
- if ((get == GetLock) && spin_is_locked(&dbri->lock)) {
- printk(KERN_ERR "DBRI: cmdlock called while in spinlock.");
+ int maxloops = MAXLOOPS;
+ unsigned long flags;
+
+ /* Delay if previous commands are still being processed */
+ spin_lock_irqsave(&dbri->lock, flags);
+ while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P)) {
+ spin_unlock_irqrestore(&dbri->lock, flags);
+ msleep_interruptible(1);
+ spin_lock_irqsave(&dbri->lock, flags);
}
-#endif
+ spin_unlock_irqrestore(&dbri->lock, flags);
- /*if (get == GetLock) spin_lock(&dbri->lock); */
- return &dbri->dma->cmd[0];
+ if (maxloops == 0) {
+ printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
+ } else {
+ dprintk(D_CMD, "Chip completed command buffer (%d)\n",
+ MAXLOOPS - maxloops - 1);
+ }
}
+/*
+ * Lock the command queue and returns pointer to a space for len cmd words
+ * It locks the cmdlock spinlock.
+ */
+static s32 *dbri_cmdlock(struct snd_dbri * dbri, int len)
+{
+ /* Space for 2 WAIT cmds (replaced later by 1 JUMP cmd) */
+ len += 2;
+ spin_lock(&dbri->cmdlock);
+ if (dbri->cmdptr - dbri->dma->cmd + len < DBRI_NO_CMDS - 2)
+ return dbri->cmdptr + 2;
+ else if (len < sbus_readl(dbri->regs + REG8) - dbri->dma_dvma)
+ return dbri->dma->cmd;
+ else
+ printk(KERN_ERR "DBRI: no space for commands.");
-static void dbri_process_interrupt_buffer(snd_dbri_t *);
+ return NULL;
+}
-static void dbri_cmdsend(snd_dbri_t * dbri, volatile s32 * cmd)
+/*
+ * Send prepared cmd string. It works by writting a JUMP cmd into
+ * the last WAIT cmd and force DBRI to reread the cmd.
+ * The JUMP cmd points to the new cmd string.
+ * It also releases the cmdlock spinlock.
+ *
+ * Lock must not be held before calling this.
+ */
+static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len)
{
- int MAXLOOPS = 1000000;
- int maxloops = MAXLOOPS;
- volatile s32 *ptr;
+ s32 tmp, addr;
+ static int wait_id = 0;
- for (ptr = &dbri->dma->cmd[0]; ptr < cmd; ptr++) {
- dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
- }
+ wait_id++;
+ wait_id &= 0xffff; /* restrict it to a 16 bit counter. */
+ *(cmd) = DBRI_CMD(D_WAIT, 1, wait_id);
+ *(cmd+1) = DBRI_CMD(D_WAIT, 1, wait_id);
- if ((cmd - &dbri->dma->cmd[0]) >= DBRI_NO_CMDS - 1) {
- printk("DBRI: Command buffer overflow! (bug in driver)\n");
- /* Ignore the last part. */
- cmd = &dbri->dma->cmd[DBRI_NO_CMDS - 3];
- }
+ /* Replace the last command with JUMP */
+ addr = dbri->dma_dvma + (cmd - len - dbri->dma->cmd) * sizeof(s32);
+ *(dbri->cmdptr+1) = addr;
+ *(dbri->cmdptr) = DBRI_CMD(D_JUMP, 0, 0);
- *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
- dbri->wait_seen = 0;
- sbus_writel(dbri->dma_dvma, dbri->regs + REG8);
- while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P))
- barrier();
- if (maxloops == 0) {
- printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
- dprintk(D_CMD, "DBRI: Chip never completed command buffer\n");
+#ifdef DBRI_DEBUG
+ if (cmd > dbri->cmdptr) {
+ s32 *ptr;
+
+ for (ptr = dbri->cmdptr; ptr < cmd+2; ptr++)
+ dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
} else {
- while ((--maxloops) > 0 && (!dbri->wait_seen))
- dbri_process_interrupt_buffer(dbri);
- if (maxloops == 0) {
- printk(KERN_ERR "DBRI: Chip never acked WAIT\n");
- dprintk(D_CMD, "DBRI: Chip never acked WAIT\n");
- } else {
- dprintk(D_CMD, "Chip completed command "
- "buffer (%d)\n", MAXLOOPS - maxloops);
+ s32 *ptr = dbri->cmdptr;
+
+ dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+ ptr++;
+ dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+ for (ptr = dbri->dma->cmd; ptr < cmd+2; ptr++) {
+ dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
}
}
+#endif
+
+ /* Reread the last command */
+ tmp = sbus_readl(dbri->regs + REG0);
+ tmp |= D_P;
+ sbus_writel(tmp, dbri->regs + REG0);
- /*spin_unlock(&dbri->lock); */
+ dbri->cmdptr = cmd;
+ spin_unlock(&dbri->cmdlock);
}
/* Lock must be held when calling this */
-static void dbri_reset(snd_dbri_t * dbri)
+static void dbri_reset(struct snd_dbri * dbri)
{
int i;
+ u32 tmp;
dprintk(D_GEN, "reset 0:%x 2:%x 8:%x 9:%x\n",
sbus_readl(dbri->regs + REG0),
sbus_writel(D_R, dbri->regs + REG0); /* Soft Reset */
for (i = 0; (sbus_readl(dbri->regs + REG0) & D_R) && i < 64; i++)
udelay(10);
+
+ /* A brute approach - DBRI falls back to working burst size by itself
+ * On SS20 D_S does not work, so do not try so high. */
+ tmp = sbus_readl(dbri->regs + REG0);
+ tmp |= D_G | D_E;
+ tmp &= ~D_S;
+ sbus_writel(tmp, dbri->regs + REG0);
}
/* Lock must not be held before calling this */
-static void dbri_initialize(snd_dbri_t * dbri)
+static void dbri_initialize(struct snd_dbri * dbri)
{
- volatile s32 *cmd;
- u32 dma_addr, tmp;
+ s32 *cmd;
+ u32 dma_addr;
unsigned long flags;
int n;
dbri_reset(dbri);
- cmd = dbri_cmdlock(dbri, NoGetLock);
- dprintk(D_GEN, "init: cmd: %p, int: %p\n",
- &dbri->dma->cmd[0], &dbri->dma->intr[0]);
+ /* Initialize pipes */
+ for (n = 0; n < DBRI_NO_PIPES; n++)
+ dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
+ spin_lock_init(&dbri->cmdlock);
/*
* Initialize the interrupt ringbuffer.
*/
- for (n = 0; n < DBRI_NO_INTS - 1; n++) {
- dma_addr = dbri->dma_dvma;
- dma_addr += dbri_dma_off(intr, ((n + 1) & DBRI_INT_BLK));
- dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
- }
dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
- dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
+ dbri->dma->intr[0] = dma_addr;
dbri->dbri_irqp = 1;
-
- /* Initialize pipes */
- for (n = 0; n < DBRI_NO_PIPES; n++)
- dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
-
- /* We should query the openprom to see what burst sizes this
- * SBus supports. For now, just disable all SBus bursts */
- tmp = sbus_readl(dbri->regs + REG0);
- tmp &= ~(D_G | D_S | D_E);
- sbus_writel(tmp, dbri->regs + REG0);
-
/*
* Set up the interrupt queue
*/
- dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
+ spin_lock(&dbri->cmdlock);
+ cmd = dbri->cmdptr = dbri->dma->cmd;
*(cmd++) = DBRI_CMD(D_IIQ, 0, 0);
*(cmd++) = dma_addr;
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+ dbri->cmdptr = cmd;
+ *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+ *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+ dma_addr = dbri->dma_dvma + dbri_dma_off(cmd, 0);
+ sbus_writel(dma_addr, dbri->regs + REG8);
+ spin_unlock(&dbri->cmdlock);
- dbri_cmdsend(dbri, cmd);
spin_unlock_irqrestore(&dbri->lock, flags);
+ dbri_cmdwait(dbri);
}
/*
here interface closely with the transmit and receive interrupt code.
*/
-static int pipe_active(snd_dbri_t * dbri, int pipe)
+static int pipe_active(struct snd_dbri * dbri, int pipe)
{
return ((pipe >= 0) && (dbri->pipes[pipe].desc != -1));
}
* Called on an in-use pipe to clear anything being transmitted or received
* Lock must be held before calling this.
*/
-static void reset_pipe(snd_dbri_t * dbri, int pipe)
+static void reset_pipe(struct snd_dbri * dbri, int pipe)
{
int sdp;
int desc;
- volatile int *cmd;
+ s32 *cmd;
- if (pipe < 0 || pipe > 31) {
- printk("DBRI: reset_pipe called with illegal pipe number\n");
+ if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
+ printk(KERN_ERR "DBRI: reset_pipe called with illegal pipe number\n");
return;
}
sdp = dbri->pipes[pipe].sdp;
if (sdp == 0) {
- printk("DBRI: reset_pipe called on uninitialized pipe\n");
+ printk(KERN_ERR "DBRI: reset_pipe called on uninitialized pipe\n");
return;
}
- cmd = dbri_cmdlock(dbri, NoGetLock);
+ cmd = dbri_cmdlock(dbri, 3);
*(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P);
*(cmd++) = 0;
- dbri_cmdsend(dbri, cmd);
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+ dbri_cmdsend(dbri, cmd, 3);
desc = dbri->pipes[pipe].first_desc;
- while (desc != -1) {
- dbri->descs[desc].inuse = 0;
- desc = dbri->descs[desc].next;
- }
+ if ( desc >= 0)
+ do {
+ dbri->dma->desc[desc].nda = dbri->dma->desc[desc].ba = 0;
+ desc = dbri->next_desc[desc];
+ } while (desc != -1 && desc != dbri->pipes[pipe].first_desc);
dbri->pipes[pipe].desc = -1;
dbri->pipes[pipe].first_desc = -1;
}
-/* FIXME: direction as an argument? */
-static void setup_pipe(snd_dbri_t * dbri, int pipe, int sdp)
+/*
+ * Lock must be held before calling this.
+ */
+static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp)
{
- if (pipe < 0 || pipe > 31) {
- printk("DBRI: setup_pipe called with illegal pipe number\n");
+ if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
+ printk(KERN_ERR "DBRI: setup_pipe called with illegal pipe number\n");
return;
}
if ((sdp & 0xf800) != sdp) {
- printk("DBRI: setup_pipe called with strange SDP value\n");
+ printk(KERN_ERR "DBRI: setup_pipe called with strange SDP value\n");
/* sdp &= 0xf800; */
}
dbri->pipes[pipe].sdp = sdp;
dbri->pipes[pipe].desc = -1;
dbri->pipes[pipe].first_desc = -1;
- if (sdp & D_SDP_TO_SER)
- dbri->pipes[pipe].direction = PIPEoutput;
- else
- dbri->pipes[pipe].direction = PIPEinput;
reset_pipe(dbri, pipe);
}
-/* FIXME: direction not needed */
-static void link_time_slot(snd_dbri_t * dbri, int pipe,
- enum in_or_out direction, int basepipe,
+/*
+ * Lock must be held before calling this.
+ */
+static void link_time_slot(struct snd_dbri * dbri, int pipe,
+ int prevpipe, int nextpipe,
int length, int cycle)
{
- volatile s32 *cmd;
+ s32 *cmd;
int val;
- int prevpipe;
- int nextpipe;
- if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) {
- printk
- ("DBRI: link_time_slot called with illegal pipe number\n");
+ if (pipe < 0 || pipe > DBRI_MAX_PIPE
+ || prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
+ || nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
+ printk(KERN_ERR
+ "DBRI: link_time_slot called with illegal pipe number\n");
return;
}
- if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) {
- printk("DBRI: link_time_slot called on uninitialized pipe\n");
+ if (dbri->pipes[pipe].sdp == 0
+ || dbri->pipes[prevpipe].sdp == 0
+ || dbri->pipes[nextpipe].sdp == 0) {
+ printk(KERN_ERR "DBRI: link_time_slot called on uninitialized pipe\n");
return;
}
- /* Deal with CHI special case:
- * "If transmission on edges 0 or 1 is desired, then cycle n
- * (where n = # of bit times per frame...) must be used."
- * - DBRI data sheet, page 11
- */
- if (basepipe == 16 && direction == PIPEoutput && cycle == 0)
- cycle = dbri->chi_bpf;
-
- if (basepipe == pipe) {
- prevpipe = pipe;
- nextpipe = pipe;
- } else {
- /* We're not initializing a new linked list (basepipe != pipe),
- * so run through the linked list and find where this pipe
- * should be sloted in, based on its cycle. CHI confuses
- * things a bit, since it has a single anchor for both its
- * transmit and receive lists.
- */
- if (basepipe == 16) {
- if (direction == PIPEinput) {
- prevpipe = dbri->chi_in_pipe;
- } else {
- prevpipe = dbri->chi_out_pipe;
- }
- } else {
- prevpipe = basepipe;
- }
-
- nextpipe = dbri->pipes[prevpipe].nextpipe;
-
- while (dbri->pipes[nextpipe].cycle < cycle
- && dbri->pipes[nextpipe].nextpipe != basepipe) {
- prevpipe = nextpipe;
- nextpipe = dbri->pipes[nextpipe].nextpipe;
- }
- }
-
- if (prevpipe == 16) {
- if (direction == PIPEinput) {
- dbri->chi_in_pipe = pipe;
- } else {
- dbri->chi_out_pipe = pipe;
- }
- } else {
- dbri->pipes[prevpipe].nextpipe = pipe;
- }
-
+ dbri->pipes[prevpipe].nextpipe = pipe;
dbri->pipes[pipe].nextpipe = nextpipe;
- dbri->pipes[pipe].cycle = cycle;
dbri->pipes[pipe].length = length;
- cmd = dbri_cmdlock(dbri, NoGetLock);
+ cmd = dbri_cmdlock(dbri, 4);
- if (direction == PIPEinput) {
- val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+ if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
+ /* Deal with CHI special case:
+ * "If transmission on edges 0 or 1 is desired, then cycle n
+ * (where n = # of bit times per frame...) must be used."
+ * - DBRI data sheet, page 11
+ */
+ if (prevpipe == 16 && cycle == 0)
+ cycle = dbri->chi_bpf;
+
+ val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(cmd++) = 0;
*(cmd++) =
D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
- *(cmd++) = 0;
} else {
- val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
*(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = 0;
*(cmd++) =
D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+ *(cmd++) = 0;
}
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- dbri_cmdsend(dbri, cmd);
+ dbri_cmdsend(dbri, cmd, 4);
}
-static void unlink_time_slot(snd_dbri_t * dbri, int pipe,
+#if 0
+/*
+ * Lock must be held before calling this.
+ */
+static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
enum in_or_out direction, int prevpipe,
int nextpipe)
{
- volatile s32 *cmd;
+ s32 *cmd;
int val;
- if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) {
- printk
- ("DBRI: unlink_time_slot called with illegal pipe number\n");
+ if (pipe < 0 || pipe > DBRI_MAX_PIPE
+ || prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
+ || nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
+ printk(KERN_ERR
+ "DBRI: unlink_time_slot called with illegal pipe number\n");
return;
}
- cmd = dbri_cmdlock(dbri, NoGetLock);
+ cmd = dbri_cmdlock(dbri, 4);
if (direction == PIPEinput) {
val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe;
*(cmd++) = 0;
*(cmd++) = D_TS_NEXT(nextpipe);
}
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- dbri_cmdsend(dbri, cmd);
+ dbri_cmdsend(dbri, cmd, 4);
}
+#endif
/* xmit_fixed() / recv_fixed()
*
* the actual time slot is. The interrupt handler takes care of bit
* ordering and alignment. An 8-bit time slot will always end up
* in the low-order 8 bits, filled either MSB-first or LSB-first,
- * depending on the settings passed to setup_pipe()
+ * depending on the settings passed to setup_pipe().
+ *
+ * Lock must not be held before calling it.
*/
-static void xmit_fixed(snd_dbri_t * dbri, int pipe, unsigned int data)
+static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data)
{
- volatile s32 *cmd;
+ s32 *cmd;
+ unsigned long flags;
- if (pipe < 16 || pipe > 31) {
- printk("DBRI: xmit_fixed: Illegal pipe number\n");
+ if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
+ printk(KERN_ERR "DBRI: xmit_fixed: Illegal pipe number\n");
return;
}
if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) {
- printk("DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe);
+ printk(KERN_ERR "DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe);
return;
}
if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
- printk("DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe);
+ printk(KERN_ERR "DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe);
return;
}
if (!(dbri->pipes[pipe].sdp & D_SDP_TO_SER)) {
- printk("DBRI: xmit_fixed: Called on receive pipe %d\n", pipe);
+ printk(KERN_ERR "DBRI: xmit_fixed: Called on receive pipe %d\n", pipe);
return;
}
if (dbri->pipes[pipe].sdp & D_SDP_MSB)
data = reverse_bytes(data, dbri->pipes[pipe].length);
- cmd = dbri_cmdlock(dbri, GetLock);
+ cmd = dbri_cmdlock(dbri, 3);
*(cmd++) = DBRI_CMD(D_SSP, 0, pipe);
*(cmd++) = data;
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+
+ spin_lock_irqsave(&dbri->lock, flags);
+ dbri_cmdsend(dbri, cmd, 3);
+ spin_unlock_irqrestore(&dbri->lock, flags);
+ dbri_cmdwait(dbri);
- dbri_cmdsend(dbri, cmd);
}
-static void recv_fixed(snd_dbri_t * dbri, int pipe, volatile __u32 * ptr)
+static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr)
{
- if (pipe < 16 || pipe > 31) {
- printk("DBRI: recv_fixed called with illegal pipe number\n");
+ if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
+ printk(KERN_ERR "DBRI: recv_fixed called with illegal pipe number\n");
return;
}
if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
- printk("DBRI: recv_fixed called on non-fixed pipe %d\n", pipe);
+ printk(KERN_ERR "DBRI: recv_fixed called on non-fixed pipe %d\n", pipe);
return;
}
if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
- printk("DBRI: recv_fixed called on transmit pipe %d\n", pipe);
+ printk(KERN_ERR "DBRI: recv_fixed called on transmit pipe %d\n", pipe);
return;
}
* and work by building chains of descriptors which identify the
* data buffers. Buffers too large for a single descriptor will
* be spread across multiple descriptors.
+ *
+ * All descriptors create a ring buffer.
+ *
+ * Lock must be held before calling this.
*/
-static int setup_descs(snd_dbri_t * dbri, int streamno, unsigned int period)
+static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period)
{
- dbri_streaminfo_t *info = &dbri->stream_info[streamno];
+ struct dbri_streaminfo *info = &dbri->stream_info[streamno];
__u32 dvma_buffer;
- int desc = 0;
+ int desc;
int len;
int first_desc = -1;
int last_desc = -1;
if (info->pipe < 0 || info->pipe > 15) {
- printk("DBRI: setup_descs: Illegal pipe number\n");
+ printk(KERN_ERR "DBRI: setup_descs: Illegal pipe number\n");
return -2;
}
if (dbri->pipes[info->pipe].sdp == 0) {
- printk("DBRI: setup_descs: Uninitialized pipe %d\n",
+ printk(KERN_ERR "DBRI: setup_descs: Uninitialized pipe %d\n",
info->pipe);
return -2;
}
if (streamno == DBRI_PLAY) {
if (!(dbri->pipes[info->pipe].sdp & D_SDP_TO_SER)) {
- printk("DBRI: setup_descs: Called on receive pipe %d\n",
+ printk(KERN_ERR "DBRI: setup_descs: Called on receive pipe %d\n",
info->pipe);
return -2;
}
} else {
if (dbri->pipes[info->pipe].sdp & D_SDP_TO_SER) {
- printk
- ("DBRI: setup_descs: Called on transmit pipe %d\n",
+ printk(KERN_ERR
+ "DBRI: setup_descs: Called on transmit pipe %d\n",
info->pipe);
return -2;
}
/* Should be able to queue multiple buffers to receive on a pipe */
if (pipe_active(dbri, info->pipe)) {
- printk("DBRI: recv_on_pipe: Called on active pipe %d\n",
+ printk(KERN_ERR "DBRI: recv_on_pipe: Called on active pipe %d\n",
info->pipe);
return -2;
}
len &= ~3;
}
+ /* Free descriptors if pipe has any */
+ desc = dbri->pipes[info->pipe].first_desc;
+ if ( desc >= 0)
+ do {
+ dbri->dma->desc[desc].nda = dbri->dma->desc[desc].ba = 0;
+ desc = dbri->next_desc[desc];
+ } while (desc != -1 && desc != dbri->pipes[info->pipe].first_desc);
+
+ dbri->pipes[info->pipe].desc = -1;
+ dbri->pipes[info->pipe].first_desc = -1;
+
+ desc = 0;
while (len > 0) {
int mylen;
for (; desc < DBRI_NO_DESCS; desc++) {
- if (!dbri->descs[desc].inuse)
+ if (!dbri->dma->desc[desc].ba)
break;
}
if (desc == DBRI_NO_DESCS) {
- printk("DBRI: setup_descs: No descriptors\n");
+ printk(KERN_ERR "DBRI: setup_descs: No descriptors\n");
return -1;
}
- if (len > DBRI_TD_MAXCNT) {
- mylen = DBRI_TD_MAXCNT; /* 8KB - 1 */
- } else {
+ if (len > DBRI_TD_MAXCNT)
+ mylen = DBRI_TD_MAXCNT; /* 8KB - 4 */
+ else
mylen = len;
- }
- if (mylen > period) {
+
+ if (mylen > period)
mylen = period;
- }
- dbri->descs[desc].inuse = 1;
- dbri->descs[desc].next = -1;
+ dbri->next_desc[desc] = -1;
dbri->dma->desc[desc].ba = dvma_buffer;
dbri->dma->desc[desc].nda = 0;
if (streamno == DBRI_PLAY) {
- dbri->descs[desc].len = mylen;
dbri->dma->desc[desc].word1 = DBRI_TD_CNT(mylen);
dbri->dma->desc[desc].word4 = 0;
- if (first_desc != -1)
- dbri->dma->desc[desc].word1 |= DBRI_TD_M;
+ dbri->dma->desc[desc].word1 |=
+ DBRI_TD_F | DBRI_TD_B;
} else {
- dbri->descs[desc].len = 0;
dbri->dma->desc[desc].word1 = 0;
dbri->dma->desc[desc].word4 =
DBRI_RD_B | DBRI_RD_BCNT(mylen);
}
- if (first_desc == -1) {
+ if (first_desc == -1)
first_desc = desc;
- } else {
- dbri->descs[last_desc].next = desc;
+ else {
+ dbri->next_desc[last_desc] = desc;
dbri->dma->desc[last_desc].nda =
dbri->dma_dvma + dbri_dma_off(desc, desc);
}
}
if (first_desc == -1 || last_desc == -1) {
- printk("DBRI: setup_descs: Not enough descriptors available\n");
+ printk(KERN_ERR "DBRI: setup_descs: Not enough descriptors available\n");
return -1;
}
- dbri->dma->desc[last_desc].word1 &= ~DBRI_TD_M;
- if (streamno == DBRI_PLAY) {
- dbri->dma->desc[last_desc].word1 |=
- DBRI_TD_I | DBRI_TD_F | DBRI_TD_B;
- }
+ dbri->dma->desc[last_desc].nda =
+ dbri->dma_dvma + dbri_dma_off(desc, first_desc);
+ dbri->next_desc[last_desc] = first_desc;
dbri->pipes[info->pipe].first_desc = first_desc;
dbri->pipes[info->pipe].desc = first_desc;
- for (desc = first_desc; desc != -1; desc = dbri->descs[desc].next) {
+#ifdef DBRI_DEBUG
+ for (desc = first_desc; desc != -1; ) {
dprintk(D_DESC, "DESC %d: %08x %08x %08x %08x\n",
desc,
dbri->dma->desc[desc].word1,
dbri->dma->desc[desc].ba,
dbri->dma->desc[desc].nda, dbri->dma->desc[desc].word4);
+ desc = dbri->next_desc[desc];
+ if ( desc == first_desc )
+ break;
}
+#endif
return 0;
}
enum master_or_slave { CHImaster, CHIslave };
-static void reset_chi(snd_dbri_t * dbri, enum master_or_slave master_or_slave,
+/*
+ * Lock must not be held before calling it.
+ */
+static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_slave,
int bits_per_frame)
{
- volatile s32 *cmd;
+ s32 *cmd;
int val;
- static int chi_initialized = 0; /* FIXME: mutex? */
- if (!chi_initialized) {
+ /* Set CHI Anchor: Pipe 16 */
- cmd = dbri_cmdlock(dbri, GetLock);
-
- /* Set CHI Anchor: Pipe 16 */
-
- val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16);
- *(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
- *(cmd++) = 0;
-
- val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16);
- *(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = 0;
- *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
-
- dbri->pipes[16].sdp = 1;
- dbri->pipes[16].nextpipe = 16;
- dbri->chi_in_pipe = 16;
- dbri->chi_out_pipe = 16;
-
-#if 0
- chi_initialized++;
-#endif
- } else {
- int pipe;
+ cmd = dbri_cmdlock(dbri, 4);
+ val = D_DTS_VO | D_DTS_VI | D_DTS_INS
+ | D_DTS_PRVIN(16) | D_PIPE(16) | D_DTS_PRVOUT(16);
+ *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+ *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+ dbri_cmdsend(dbri, cmd, 4);
- for (pipe = dbri->chi_in_pipe;
- pipe != 16; pipe = dbri->pipes[pipe].nextpipe) {
- unlink_time_slot(dbri, pipe, PIPEinput,
- 16, dbri->pipes[pipe].nextpipe);
- }
- for (pipe = dbri->chi_out_pipe;
- pipe != 16; pipe = dbri->pipes[pipe].nextpipe) {
- unlink_time_slot(dbri, pipe, PIPEoutput,
- 16, dbri->pipes[pipe].nextpipe);
- }
+ dbri->pipes[16].sdp = 1;
+ dbri->pipes[16].nextpipe = 16;
- dbri->chi_in_pipe = 16;
- dbri->chi_out_pipe = 16;
-
- cmd = dbri_cmdlock(dbri, GetLock);
- }
+ cmd = dbri_cmdlock(dbri, 4);
if (master_or_slave == CHIslave) {
/* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
int divisor = 12288 / clockrate;
if (divisor > 255 || divisor * clockrate != 12288)
- printk("DBRI: illegal bits_per_frame in setup_chi\n");
+ printk(KERN_ERR "DBRI: illegal bits_per_frame in setup_chi\n");
*(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
| D_CHI_BPF(bits_per_frame));
*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
*(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE | D_CDM_XEN | D_CDM_REN);
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- dbri_cmdsend(dbri, cmd);
+ dbri_cmdsend(dbri, cmd, 4);
}
/*
In the standard SPARC audio configuration, the CS4215 codec is attached
to the DBRI via the CHI interface and few of the DBRI's PIO pins.
+ * Lock must not be held before calling it.
+
*/
-static void cs4215_setup_pipes(snd_dbri_t * dbri)
+static void cs4215_setup_pipes(struct snd_dbri * dbri)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dbri->lock, flags);
/*
* Data mode:
* Pipe 4: Send timeslots 1-4 (audio data)
setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+ spin_unlock_irqrestore(&dbri->lock, flags);
+
+ dbri_cmdwait(dbri);
}
static int cs4215_init_data(struct cs4215 *mm)
mm->status = 0;
mm->version = 0xff;
mm->precision = 8; /* For ULAW */
- mm->channels = 2;
+ mm->channels = 1;
return 0;
}
-static void cs4215_setdata(snd_dbri_t * dbri, int muted)
+static void cs4215_setdata(struct snd_dbri * dbri, int muted)
{
if (muted) {
dbri->mm.data[0] |= 63;
dbri->mm.data[3] &= ~15;
} else {
/* Start by setting the playback attenuation. */
- dbri_streaminfo_t *info = &dbri->stream_info[DBRI_PLAY];
- int left_gain = info->left_gain % 64;
- int right_gain = info->right_gain % 64;
-
- if (info->balance < DBRI_MID_BALANCE) {
- right_gain *= info->balance;
- right_gain /= DBRI_MID_BALANCE;
- } else {
- left_gain *= DBRI_RIGHT_BALANCE - info->balance;
- left_gain /= DBRI_MID_BALANCE;
- }
+ struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY];
+ int left_gain = info->left_gain & 0x3f;
+ int right_gain = info->right_gain & 0x3f;
dbri->mm.data[0] &= ~0x3f; /* Reset the volume bits */
dbri->mm.data[1] &= ~0x3f;
/* Now set the recording gain. */
info = &dbri->stream_info[DBRI_REC];
- left_gain = info->left_gain % 16;
- right_gain = info->right_gain % 16;
+ left_gain = info->left_gain & 0xf;
+ right_gain = info->right_gain & 0xf;
dbri->mm.data[2] |= CS4215_LG(left_gain);
dbri->mm.data[3] |= CS4215_RG(right_gain);
}
/*
* Set the CS4215 to data mode.
*/
-static void cs4215_open(snd_dbri_t * dbri)
+static void cs4215_open(struct snd_dbri * dbri)
{
int data_width;
u32 tmp;
+ unsigned long flags;
dprintk(D_MM, "cs4215_open: %d channels, %d bits\n",
dbri->mm.channels, dbri->mm.precision);
* bits. The CS4215, it seems, observes TSIN (the delayed signal)
* even if it's the CHI master. Don't ask me...
*/
+ spin_lock_irqsave(&dbri->lock, flags);
tmp = sbus_readl(dbri->regs + REG0);
tmp &= ~(D_C); /* Disable CHI */
sbus_writel(tmp, dbri->regs + REG0);
*/
data_width = dbri->mm.channels * dbri->mm.precision;
- link_time_slot(dbri, 20, PIPEoutput, 16, 32, dbri->mm.offset + 32);
- link_time_slot(dbri, 4, PIPEoutput, 16, data_width, dbri->mm.offset);
- link_time_slot(dbri, 6, PIPEinput, 16, data_width, dbri->mm.offset);
- link_time_slot(dbri, 21, PIPEinput, 16, 16, dbri->mm.offset + 40);
+ link_time_slot(dbri, 4, 16, 16, data_width, dbri->mm.offset);
+ link_time_slot(dbri, 20, 4, 16, 32, dbri->mm.offset + 32);
+ link_time_slot(dbri, 6, 16, 16, data_width, dbri->mm.offset);
+ link_time_slot(dbri, 21, 6, 16, 16, dbri->mm.offset + 40);
/* FIXME: enable CHI after _setdata? */
tmp = sbus_readl(dbri->regs + REG0);
tmp |= D_C; /* Enable CHI */
sbus_writel(tmp, dbri->regs + REG0);
+ spin_unlock_irqrestore(&dbri->lock, flags);
cs4215_setdata(dbri, 0);
}
/*
* Send the control information (i.e. audio format)
*/
-static int cs4215_setctrl(snd_dbri_t * dbri)
+static int cs4215_setctrl(struct snd_dbri * dbri)
{
int i, val;
u32 tmp;
+ unsigned long flags;
/* FIXME - let the CPU do something useful during these delays */
/* Temporarily mute outputs, and wait 1/8000 sec (125 us)
* to make sure this takes. This avoids clicking noises.
*/
-
cs4215_setdata(dbri, 1);
udelay(125);
* done in hardware by a TI 248 that delays the DBRI->4215
* frame sync signal by eight clock cycles. Anybody know why?
*/
+ spin_lock_irqsave(&dbri->lock, flags);
tmp = sbus_readl(dbri->regs + REG0);
tmp &= ~D_C; /* Disable CHI */
sbus_writel(tmp, dbri->regs + REG0);
* Pipe 19: Receive timeslot 7 (version).
*/
- link_time_slot(dbri, 17, PIPEoutput, 16, 32, dbri->mm.offset);
- link_time_slot(dbri, 18, PIPEinput, 16, 8, dbri->mm.offset);
- link_time_slot(dbri, 19, PIPEinput, 16, 8, dbri->mm.offset + 48);
+ link_time_slot(dbri, 17, 16, 16, 32, dbri->mm.offset);
+ link_time_slot(dbri, 18, 16, 16, 8, dbri->mm.offset);
+ link_time_slot(dbri, 19, 18, 16, 8, dbri->mm.offset + 48);
+ spin_unlock_irqrestore(&dbri->lock, flags);
/* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
dbri->mm.ctrl[0] &= ~CS4215_CLB;
xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
+ spin_lock_irqsave(&dbri->lock, flags);
tmp = sbus_readl(dbri->regs + REG0);
tmp |= D_C; /* Enable CHI */
sbus_writel(tmp, dbri->regs + REG0);
+ spin_unlock_irqrestore(&dbri->lock, flags);
- for (i = 64; ((dbri->mm.status & 0xe4) != 0x20); --i) {
- udelay(125);
+ for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i) {
+ msleep_interruptible(1);
}
if (i == 0) {
dprintk(D_MM, "CS4215 didn't respond to CLB (0x%02x)\n",
* As part of the process we resend the settings for the data
* timeslots as well.
*/
-static int cs4215_prepare(snd_dbri_t * dbri, unsigned int rate,
+static int cs4215_prepare(struct snd_dbri * dbri, unsigned int rate,
snd_pcm_format_t format, unsigned int channels)
{
int freq_idx;
CS4215_BSEL_128 | CS4215_FREQ[freq_idx].xtal;
dbri->mm.channels = channels;
- /* Stereo bit: 8 bit stereo not working yet. */
- if ((channels > 1) && (dbri->mm.precision == 16))
+ if (channels == 2)
dbri->mm.ctrl[1] |= CS4215_DFR_STEREO;
ret = cs4215_setctrl(dbri);
/*
*
*/
-static int cs4215_init(snd_dbri_t * dbri)
+static int cs4215_init(struct snd_dbri * dbri)
{
u32 reg2 = sbus_readl(dbri->regs + REG2);
dprintk(D_MM, "cs4215_init: reg2=0x%x\n", reg2);
}
cs4215_setup_pipes(dbri);
-
cs4215_init_data(&dbri->mm);
/* Enable capture of the status & version timeslots. */
Complicated interrupts are handled by dedicated functions (which
appear first in this file). Any pending interrupts can be serviced by
calling dbri_process_interrupt_buffer(), which works even if the CPU's
-interrupts are disabled. This function is used by dbri_cmdsend()
-to make sure we're synced up with the chip after each command sequence,
-even if we're running cli'ed.
+interrupts are disabled.
*/
/* xmit_descs()
*
- * Transmit the current TD's for recording/playing, if needed.
+ * Starts transmiting the current TD's for recording/playing.
* For playback, ALSA has filled the DMA memory with new data (we hope).
*/
-static void xmit_descs(unsigned long data)
+static void xmit_descs(struct snd_dbri *dbri)
{
- snd_dbri_t *dbri = (snd_dbri_t *) data;
- dbri_streaminfo_t *info;
- volatile s32 *cmd;
+ struct dbri_streaminfo *info;
+ s32 *cmd;
unsigned long flags;
int first_td;
if (dbri == NULL)
return; /* Disabled */
- /* First check the recording stream for buffer overflow */
info = &dbri->stream_info[DBRI_REC];
spin_lock_irqsave(&dbri->lock, flags);
- if ((info->left >= info->size) && (info->pipe >= 0)) {
+ if (info->pipe >= 0) {
first_td = dbri->pipes[info->pipe].first_desc;
dprintk(D_DESC, "xmit_descs rec @ TD %d\n", first_td);
/* Stream could be closed by the time we run. */
- if (first_td < 0) {
- goto play;
- }
-
- cmd = dbri_cmdlock(dbri, NoGetLock);
- *(cmd++) = DBRI_CMD(D_SDP, 0,
- dbri->pipes[info->pipe].sdp
- | D_SDP_P | D_SDP_EVERY | D_SDP_C);
- *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
- dbri_cmdsend(dbri, cmd);
+ if (first_td >= 0) {
+ cmd = dbri_cmdlock(dbri, 2);
+ *(cmd++) = DBRI_CMD(D_SDP, 0,
+ dbri->pipes[info->pipe].sdp
+ | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+ *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
+ dbri_cmdsend(dbri, cmd, 2);
- /* Reset our admin of the pipe & bytes read. */
- dbri->pipes[info->pipe].desc = first_td;
- info->left = 0;
+ /* Reset our admin of the pipe. */
+ dbri->pipes[info->pipe].desc = first_td;
+ }
}
-play:
- spin_unlock_irqrestore(&dbri->lock, flags);
-
- /* Now check the playback stream for buffer underflow */
info = &dbri->stream_info[DBRI_PLAY];
- spin_lock_irqsave(&dbri->lock, flags);
- if ((info->left <= 0) && (info->pipe >= 0)) {
+ if (info->pipe >= 0) {
first_td = dbri->pipes[info->pipe].first_desc;
dprintk(D_DESC, "xmit_descs play @ TD %d\n", first_td);
/* Stream could be closed by the time we run. */
- if (first_td < 0) {
- spin_unlock_irqrestore(&dbri->lock, flags);
- return;
- }
-
- cmd = dbri_cmdlock(dbri, NoGetLock);
- *(cmd++) = DBRI_CMD(D_SDP, 0,
- dbri->pipes[info->pipe].sdp
- | D_SDP_P | D_SDP_EVERY | D_SDP_C);
- *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
- dbri_cmdsend(dbri, cmd);
+ if (first_td >= 0) {
+ cmd = dbri_cmdlock(dbri, 2);
+ *(cmd++) = DBRI_CMD(D_SDP, 0,
+ dbri->pipes[info->pipe].sdp
+ | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+ *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
+ dbri_cmdsend(dbri, cmd, 2);
- /* Reset our admin of the pipe & bytes written. */
- dbri->pipes[info->pipe].desc = first_td;
- info->left = info->size;
+ /* Reset our admin of the pipe. */
+ dbri->pipes[info->pipe].desc = first_td;
+ }
}
+
spin_unlock_irqrestore(&dbri->lock, flags);
}
-DECLARE_TASKLET(xmit_descs_task, xmit_descs, 0);
-
/* transmission_complete_intr()
*
* Called by main interrupt handler when DBRI signals transmission complete
* on a pipe (interrupt triggered by the B bit in a transmit descriptor).
*
- * Walks through the pipe's list of transmit buffer descriptors, releasing
- * each one's DMA buffer (if present), flagging the descriptor available,
- * and signaling its callback routine (if present), before proceeding
- * to the next one. Stops when the first descriptor is found without
+ * Walks through the pipe's list of transmit buffer descriptors and marks
+ * them as available. Stops when the first descriptor is found without
* TBC (Transmit Buffer Complete) set, or we've run through them all.
+ *
+ * The DMA buffers are not released. They form a ring buffer and
+ * they are filled by ALSA while others are transmitted by DMA.
+ *
*/
-static void transmission_complete_intr(snd_dbri_t * dbri, int pipe)
+static void transmission_complete_intr(struct snd_dbri * dbri, int pipe)
{
- dbri_streaminfo_t *info;
+ struct dbri_streaminfo *info;
int td;
int status;
dprintk(D_INT, "TD %d, status 0x%02x\n", td, status);
dbri->dma->desc[td].word4 = 0; /* Reset it for next time. */
- info->offset += dbri->descs[td].len;
- info->left -= dbri->descs[td].len;
-
- /* On the last TD, transmit them all again. */
- if (dbri->descs[td].next == -1) {
- if (info->left > 0) {
- printk(KERN_WARNING
- "%d bytes left after last transfer.\n",
- info->left);
- info->left = 0;
- }
- tasklet_schedule(&xmit_descs_task);
- }
+ info->offset += DBRI_RD_CNT(dbri->dma->desc[td].word1);
- td = dbri->descs[td].next;
+ td = dbri->next_desc[td];
dbri->pipes[pipe].desc = td;
}
snd_pcm_period_elapsed(info->substream);
}
-static void reception_complete_intr(snd_dbri_t * dbri, int pipe)
+static void reception_complete_intr(struct snd_dbri * dbri, int pipe)
{
- dbri_streaminfo_t *info;
+ struct dbri_streaminfo *info;
int rd = dbri->pipes[pipe].desc;
s32 status;
return;
}
- dbri->descs[rd].inuse = 0;
- dbri->pipes[pipe].desc = dbri->descs[rd].next;
+ dbri->pipes[pipe].desc = dbri->next_desc[rd];
status = dbri->dma->desc[rd].word1;
dbri->dma->desc[rd].word1 = 0; /* Reset it for next time. */
info = &dbri->stream_info[DBRI_REC];
info->offset += DBRI_RD_CNT(status);
- info->left += DBRI_RD_CNT(status);
/* FIXME: Check status */
dprintk(D_INT, "Recv RD %d, status 0x%02x, len %d\n",
rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status));
- /* On the last TD, transmit them all again. */
- if (dbri->descs[rd].next == -1) {
- if (info->left > info->size) {
- printk(KERN_WARNING
- "%d bytes recorded in %d size buffer.\n",
- info->left, info->size);
- }
- tasklet_schedule(&xmit_descs_task);
- }
-
/* Notify ALSA */
if (spin_is_locked(&dbri->lock)) {
spin_unlock(&dbri->lock);
snd_pcm_period_elapsed(info->substream);
}
-static void dbri_process_one_interrupt(snd_dbri_t * dbri, int x)
+static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
{
int val = D_INTR_GETVAL(x);
int channel = D_INTR_GETCHAN(x);
channel, code, rval);
}
- if (channel == D_INTR_CMD && command == D_WAIT) {
- dbri->wait_seen++;
- return;
- }
-
switch (code) {
+ case D_INTR_CMDI:
+ if (command != D_WAIT)
+ printk(KERN_ERR "DBRI: Command read interrupt\n");
+ break;
case D_INTR_BRDY:
reception_complete_intr(dbri, channel);
break;
* resend SDP command with clear pipe bit (C) set
*/
{
- volatile s32 *cmd;
-
+ /* FIXME: do something useful in case of underrun */
+ printk(KERN_ERR "DBRI: Underrun error\n");
+#if 0
+ s32 *cmd;
int pipe = channel;
int td = dbri->pipes[pipe].desc;
| D_SDP_P | D_SDP_C | D_SDP_2SAME);
*(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, td);
dbri_cmdsend(dbri, cmd);
+#endif
}
break;
case D_INTR_FXDT:
/* dbri_process_interrupt_buffer advances through the DBRI's interrupt
* buffer until it finds a zero word (indicating nothing more to do
* right now). Non-zero words require processing and are handed off
- * to dbri_process_one_interrupt AFTER advancing the pointer. This
- * order is important since we might recurse back into this function
- * and need to make sure the pointer has been advanced first.
+ * to dbri_process_one_interrupt AFTER advancing the pointer.
*/
-static void dbri_process_interrupt_buffer(snd_dbri_t * dbri)
+static void dbri_process_interrupt_buffer(struct snd_dbri * dbri)
{
s32 x;
while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) {
dbri->dma->intr[dbri->dbri_irqp] = 0;
dbri->dbri_irqp++;
- if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK))
+ if (dbri->dbri_irqp == DBRI_INT_BLK)
dbri->dbri_irqp = 1;
- else if ((dbri->dbri_irqp & (DBRI_INT_BLK - 1)) == 0)
- dbri->dbri_irqp++;
dbri_process_one_interrupt(dbri, x);
}
}
-static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id)
{
- snd_dbri_t *dbri = dev_id;
+ struct snd_dbri *dbri = dev_id;
static int errcnt = 0;
int x;
* The only one I've seen is MRR, which will be triggered
* if you let a transmit pipe underrun, then try to CDP it.
*
- * If these things persist, we should probably reset
- * and re-init the chip.
+ * If these things persist, we reset the chip.
*/
if ((++errcnt) % 10 == 0) {
dprintk(D_INT, "Interrupt errors exceeded.\n");
dbri_process_interrupt_buffer(dbri);
- /* FIXME: Write 0 into regs to ACK interrupt */
-
spin_unlock(&dbri->lock);
return IRQ_HANDLED;
/****************************************************************************
PCM Interface
****************************************************************************/
-static snd_pcm_hardware_t snd_dbri_pcm_hw = {
+static struct snd_pcm_hardware snd_dbri_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_FMTBIT_A_LAW |
SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_BE,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .rate_min = 8000,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_5512,
+ .rate_min = 5512,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.periods_max = 1024,
};
-static int snd_dbri_open(snd_pcm_substream_t * substream)
+static int snd_hw_rule_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_mask fmt;
+
+ snd_mask_any(&fmt);
+ if (c->min > 1) {
+ fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_BE;
+ return snd_mask_refine(f, &fmt);
+ }
+ return 0;
+}
+
+static int snd_hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_interval ch;
+
+ snd_interval_any(&ch);
+ if (!(f->bits[0] & SNDRV_PCM_FMTBIT_S16_BE)) {
+ ch.min = ch.max = 1;
+ ch.integer = 1;
+ return snd_interval_refine(c, &ch);
+ }
+ return 0;
+}
+
+static int snd_dbri_open(struct snd_pcm_substream *substream)
{
- snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
- snd_pcm_runtime_t *runtime = substream->runtime;
- dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+ struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
unsigned long flags;
dprintk(D_USR, "open audio output.\n");
spin_lock_irqsave(&dbri->lock, flags);
info->substream = substream;
- info->left = 0;
info->offset = 0;
info->dvma_buffer = 0;
info->pipe = -1;
spin_unlock_irqrestore(&dbri->lock, flags);
+ snd_pcm_hw_rule_add(runtime,0,SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_hw_rule_format, NULL, SNDRV_PCM_HW_PARAM_FORMAT,
+ -1);
+ snd_pcm_hw_rule_add(runtime,0,SNDRV_PCM_HW_PARAM_FORMAT,
+ snd_hw_rule_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ -1);
+
cs4215_open(dbri);
return 0;
}
-static int snd_dbri_close(snd_pcm_substream_t * substream)
+static int snd_dbri_close(struct snd_pcm_substream *substream)
{
- snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
- dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+ struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
+ struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
dprintk(D_USR, "close audio output.\n");
info->substream = NULL;
- info->left = 0;
info->offset = 0;
return 0;
}
-static int snd_dbri_hw_params(snd_pcm_substream_t * substream,
- snd_pcm_hw_params_t * hw_params)
+static int snd_dbri_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
- snd_pcm_runtime_t *runtime = substream->runtime;
- snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
- dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
+ struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
int direction;
int ret;
if ((ret = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params))) < 0) {
- snd_printk(KERN_ERR "malloc_pages failed with %d\n", ret);
+ printk(KERN_ERR "malloc_pages failed with %d\n", ret);
return ret;
}
return 0;
}
-static int snd_dbri_hw_free(snd_pcm_substream_t * substream)
+static int snd_dbri_hw_free(struct snd_pcm_substream *substream)
{
- snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
- dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+ struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
+ struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
int direction;
+
dprintk(D_USR, "hw_free.\n");
/* hw_free can get called multiple times. Only unmap the DMA once.
substream->runtime->buffer_size, direction);
info->dvma_buffer = 0;
}
- info->pipe = -1;
+ if (info->pipe != -1) {
+ reset_pipe(dbri, info->pipe);
+ info->pipe = -1;
+ }
return snd_pcm_lib_free_pages(substream);
}
-static int snd_dbri_prepare(snd_pcm_substream_t * substream)
+static int snd_dbri_prepare(struct snd_pcm_substream *substream)
{
- snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
- dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
- snd_pcm_runtime_t *runtime = substream->runtime;
+ struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
+ struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
int ret;
info->size = snd_pcm_lib_buffer_bytes(substream);
if (DBRI_STREAMNO(substream) == DBRI_PLAY)
info->pipe = 4; /* Send pipe */
- else {
+ else
info->pipe = 6; /* Receive pipe */
- info->left = info->size; /* To trigger submittal */
- }
spin_lock_irq(&dbri->lock);
+ info->offset = 0;
/* Setup the all the transmit/receive desciptors to cover the
* whole DMA buffer.
ret = setup_descs(dbri, DBRI_STREAMNO(substream),
snd_pcm_lib_period_bytes(substream));
- runtime->stop_threshold = DBRI_TD_MAXCNT / runtime->channels;
-
spin_unlock_irq(&dbri->lock);
dprintk(D_USR, "prepare audio output. %d bytes\n", info->size);
return ret;
}
-static int snd_dbri_trigger(snd_pcm_substream_t * substream, int cmd)
+static int snd_dbri_trigger(struct snd_pcm_substream *substream, int cmd)
{
- snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
- dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+ struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
+ struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
dprintk(D_USR, "start audio, period is %d bytes\n",
(int)snd_pcm_lib_period_bytes(substream));
- /* Enable & schedule the tasklet that re-submits the TDs. */
- xmit_descs_task.data = (unsigned long)dbri;
- tasklet_schedule(&xmit_descs_task);
+ /* Re-submit the TDs. */
+ xmit_descs(dbri);
break;
case SNDRV_PCM_TRIGGER_STOP:
dprintk(D_USR, "stop audio.\n");
- /* Make the tasklet bail out immediately. */
- xmit_descs_task.data = 0;
reset_pipe(dbri, info->pipe);
break;
default:
return ret;
}
-static snd_pcm_uframes_t snd_dbri_pointer(snd_pcm_substream_t * substream)
+static snd_pcm_uframes_t snd_dbri_pointer(struct snd_pcm_substream *substream)
{
- snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
- dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+ struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
+ struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
snd_pcm_uframes_t ret;
ret = bytes_to_frames(substream->runtime, info->offset)
% substream->runtime->buffer_size;
- dprintk(D_USR, "I/O pointer: %ld frames, %d bytes left.\n",
- ret, info->left);
+ dprintk(D_USR, "I/O pointer: %ld frames of %ld.\n",
+ ret, substream->runtime->buffer_size);
return ret;
}
-static snd_pcm_ops_t snd_dbri_ops = {
+static struct snd_pcm_ops snd_dbri_ops = {
.open = snd_dbri_open,
.close = snd_dbri_close,
.ioctl = snd_pcm_lib_ioctl,
.pointer = snd_dbri_pointer,
};
-static int __devinit snd_dbri_pcm(snd_dbri_t * dbri)
+static int __devinit snd_dbri_pcm(struct snd_dbri * dbri)
{
- snd_pcm_t *pcm;
+ struct snd_pcm *pcm;
int err;
if ((err = snd_pcm_new(dbri->card,
pcm->private_data = dbri;
pcm->info_flags = 0;
strcpy(pcm->name, dbri->card->shortname);
- dbri->pcm = pcm;
if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
Mixer interface
*****************************************************************************/
-static int snd_cs4215_info_volume(snd_kcontrol_t * kcontrol,
- snd_ctl_elem_info_t * uinfo)
+static int snd_cs4215_info_volume(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
return 0;
}
-static int snd_cs4215_get_volume(snd_kcontrol_t * kcontrol,
- snd_ctl_elem_value_t * ucontrol)
+static int snd_cs4215_get_volume(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol);
- dbri_streaminfo_t *info;
+ struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
+ struct dbri_streaminfo *info;
snd_assert(dbri != NULL, return -EINVAL);
info = &dbri->stream_info[kcontrol->private_value];
snd_assert(info != NULL, return -EINVAL);
return 0;
}
-static int snd_cs4215_put_volume(snd_kcontrol_t * kcontrol,
- snd_ctl_elem_value_t * ucontrol)
+static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol);
- dbri_streaminfo_t *info = &dbri->stream_info[kcontrol->private_value];
- unsigned long flags;
+ struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
+ struct dbri_streaminfo *info = &dbri->stream_info[kcontrol->private_value];
int changed = 0;
if (info->left_gain != ucontrol->value.integer.value[0]) {
/* First mute outputs, and wait 1/8000 sec (125 us)
* to make sure this takes. This avoids clicking noises.
*/
- spin_lock_irqsave(&dbri->lock, flags);
-
cs4215_setdata(dbri, 1);
udelay(125);
cs4215_setdata(dbri, 0);
-
- spin_unlock_irqrestore(&dbri->lock, flags);
}
return changed;
}
-static int snd_cs4215_info_single(snd_kcontrol_t * kcontrol,
- snd_ctl_elem_info_t * uinfo)
+static int snd_cs4215_info_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
int mask = (kcontrol->private_value >> 16) & 0xff;
return 0;
}
-static int snd_cs4215_get_single(snd_kcontrol_t * kcontrol,
- snd_ctl_elem_value_t * ucontrol)
+static int snd_cs4215_get_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol);
+ struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
int elem = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
return 0;
}
-static int snd_cs4215_put_single(snd_kcontrol_t * kcontrol,
- snd_ctl_elem_value_t * ucontrol)
+static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
+ struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
int elem = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
/* First mute outputs, and wait 1/8000 sec (125 us)
* to make sure this takes. This avoids clicking noises.
*/
- spin_lock_irqsave(&dbri->lock, flags);
-
cs4215_setdata(dbri, 1);
udelay(125);
cs4215_setdata(dbri, 0);
-
- spin_unlock_irqrestore(&dbri->lock, flags);
}
return changed;
}
.get = snd_cs4215_get_single, .put = snd_cs4215_put_single, \
.private_value = entry | (shift << 8) | (mask << 16) | (invert << 24) },
-static snd_kcontrol_new_t dbri_controls[] __devinitdata = {
+static struct snd_kcontrol_new dbri_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Playback Volume",
CS4215_SINGLE("Mic boost", 4, 4, 1, 1)
};
-#define NUM_CS4215_CONTROLS (sizeof(dbri_controls)/sizeof(snd_kcontrol_new_t))
-
-static int __init snd_dbri_mixer(snd_dbri_t * dbri)
+static int __init snd_dbri_mixer(struct snd_dbri * dbri)
{
- snd_card_t *card;
+ struct snd_card *card;
int idx, err;
snd_assert(dbri != NULL && dbri->card != NULL, return -EINVAL);
card = dbri->card;
strcpy(card->mixername, card->shortname);
- for (idx = 0; idx < NUM_CS4215_CONTROLS; idx++) {
+ for (idx = 0; idx < ARRAY_SIZE(dbri_controls); idx++) {
if ((err = snd_ctl_add(card,
- snd_ctl_new1(&dbri_controls[idx],
- dbri))) < 0)
+ snd_ctl_new1(&dbri_controls[idx], dbri))) < 0)
return err;
}
for (idx = DBRI_REC; idx < DBRI_NO_STREAMS; idx++) {
dbri->stream_info[idx].left_gain = 0;
dbri->stream_info[idx].right_gain = 0;
- dbri->stream_info[idx].balance = DBRI_MID_BALANCE;
}
return 0;
/****************************************************************************
/proc interface
****************************************************************************/
-static void dbri_regs_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer)
+static void dbri_regs_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer)
{
- snd_dbri_t *dbri = entry->private_data;
+ struct snd_dbri *dbri = entry->private_data;
snd_iprintf(buffer, "REG0: 0x%x\n", sbus_readl(dbri->regs + REG0));
snd_iprintf(buffer, "REG2: 0x%x\n", sbus_readl(dbri->regs + REG2));
}
#ifdef DBRI_DEBUG
-static void dbri_debug_read(snd_info_entry_t * entry,
- snd_info_buffer_t * buffer)
+static void dbri_debug_read(struct snd_info_entry * entry,
+ struct snd_info_buffer *buffer)
{
- snd_dbri_t *dbri = entry->private_data;
+ struct snd_dbri *dbri = entry->private_data;
int pipe;
snd_iprintf(buffer, "debug=%d\n", dbri_debug);
- snd_iprintf(buffer, "CHI pipe in=%d, out=%d\n",
- dbri->chi_in_pipe, dbri->chi_out_pipe);
for (pipe = 0; pipe < 32; pipe++) {
if (pipe_active(dbri, pipe)) {
struct dbri_pipe *pptr = &dbri->pipes[pipe];
snd_iprintf(buffer,
"Pipe %d: %s SDP=0x%x desc=%d, "
- "len=%d @ %d prev: %d next %d\n",
+ "len=%d next %d\n",
pipe,
- (pptr->direction ==
- PIPEinput ? "input" : "output"), pptr->sdp,
- pptr->desc, pptr->length, pptr->cycle,
- pptr->prevpipe, pptr->nextpipe);
+ ((pptr->sdp & D_SDP_TO_SER) ? "output" : "input"),
+ pptr->sdp, pptr->desc,
+ pptr->length, pptr->nextpipe);
}
}
}
-
-static void dbri_debug_write(snd_info_entry_t * entry,
- snd_info_buffer_t * buffer)
-{
- char line[80];
- int i;
-
- if (snd_info_get_line(buffer, line, 80) == 0) {
- sscanf(line, "%d\n", &i);
- dbri_debug = i & 0x3f;
- }
-}
#endif
-void snd_dbri_proc(snd_dbri_t * dbri)
+void snd_dbri_proc(struct snd_dbri * dbri)
{
- snd_info_entry_t *entry;
- int err;
+ struct snd_info_entry *entry;
- err = snd_card_proc_new(dbri->card, "regs", &entry);
- snd_info_set_text_ops(entry, dbri, 1024, dbri_regs_read);
+ if (! snd_card_proc_new(dbri->card, "regs", &entry))
+ snd_info_set_text_ops(entry, dbri, dbri_regs_read);
#ifdef DBRI_DEBUG
- err = snd_card_proc_new(dbri->card, "debug", &entry);
- snd_info_set_text_ops(entry, dbri, 4096, dbri_debug_read);
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR; /* Writable for root */
- entry->c.text.write_size = 256;
- entry->c.text.write = dbri_debug_write;
+ if (! snd_card_proc_new(dbri->card, "debug", &entry)) {
+ snd_info_set_text_ops(entry, dbri, dbri_debug_read);
+ entry->mode = S_IFREG | S_IRUGO; /* Readable only. */
+ }
#endif
}
**************************** Initialization ********************************
****************************************************************************
*/
-static void snd_dbri_free(snd_dbri_t * dbri);
+static void snd_dbri_free(struct snd_dbri * dbri);
-static int __init snd_dbri_create(snd_card_t * card,
+static int __init snd_dbri_create(struct snd_card *card,
struct sbus_dev *sdev,
struct linux_prom_irqs *irq, int dev)
{
- snd_dbri_t *dbri = card->private_data;
+ struct snd_dbri *dbri = card->private_data;
int err;
spin_lock_init(&dbri->lock);
dbri->card = card;
dbri->sdev = sdev;
dbri->irq = irq->pri;
- dbri->dbri_version = sdev->prom_name[9];
dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma),
&dbri->dma_dvma);
return -EIO;
}
- err = request_irq(dbri->irq, snd_dbri_interrupt, SA_SHIRQ,
+ err = request_irq(dbri->irq, snd_dbri_interrupt, IRQF_SHARED,
"DBRI audio", dbri);
if (err) {
printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
return 0;
}
-static void snd_dbri_free(snd_dbri_t * dbri)
+static void snd_dbri_free(struct snd_dbri * dbri)
{
dprintk(D_GEN, "snd_dbri_free\n");
dbri_reset(dbri);
static int __init dbri_attach(int prom_node, struct sbus_dev *sdev)
{
- snd_dbri_t *dbri;
+ struct snd_dbri *dbri;
struct linux_prom_irqs irq;
struct resource *rp;
- snd_card_t *card;
+ struct snd_card *card;
static int dev = 0;
int err;
return -ENOENT;
}
- prom_getproperty(prom_node, "intr", (char *)&irq, sizeof(irq));
+ err = prom_getproperty(prom_node, "intr", (char *)&irq, sizeof(irq));
+ if (err < 0) {
+ printk(KERN_ERR "DBRI-%d: Firmware node lacks IRQ property.\n", dev);
+ return -ENODEV;
+ }
card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(snd_dbri_t));
+ sizeof(struct snd_dbri));
if (card == NULL)
return -ENOMEM;
strcpy(card->driver, "DBRI");
strcpy(card->shortname, "Sun DBRI");
rp = &sdev->resource[0];
- sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %s",
+ sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
card->shortname,
- rp->flags & 0xffL, rp->start, __irq_itoa(irq.pri));
+ rp->flags & 0xffL, (unsigned long long)rp->start, irq.pri);
if ((err = snd_dbri_create(card, sdev, &irq, dev)) < 0) {
snd_card_free(card);
return err;
}
- dbri = (snd_dbri_t *) card->private_data;
+ dbri = card->private_data;
if ((err = snd_dbri_pcm(dbri)) < 0)
goto _err;
/* /proc file handling */
snd_dbri_proc(dbri);
- if ((err = snd_card_set_generic_dev(card)) < 0)
- goto _err;
-
if ((err = snd_card_register(card)) < 0)
goto _err;
printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n",
dev, dbri->regs,
- dbri->irq, dbri->dbri_version, dbri->mm.version);
+ dbri->irq, sdev->prom_name[9], dbri->mm.version);
dev++;
return 0;
static void __exit dbri_exit(void)
{
- snd_dbri_t *this = dbri_list;
+ struct snd_dbri *this = dbri_list;
while (this != NULL) {
- snd_dbri_t *next = this->next;
- snd_card_t *card = this->card;
+ struct snd_dbri *next = this->next;
+ struct snd_card *card = this->card;
snd_dbri_free(this);
snd_card_free(card);