IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
[safe/jmp/linux-2.6] / sound / sparc / dbri.c
index 3e6ad50..4ceb09d 100644 (file)
@@ -107,7 +107,7 @@ static char *cmds[] = {
 #define dprintk(a, x...) if(dbri_debug & a) printk(KERN_DEBUG x)
 
 #else
-#define dprintk(a, x...)
+#define dprintk(a, x...) do { } while (0)
 
 #endif                         /* DBRI_DEBUG */
 
@@ -610,10 +610,10 @@ CPU interrupt to signal completion.
 
 Since the DBRI can run in parallel with the CPU, several means of
 synchronization present themselves. The method implemented here is only
-to use the dbri_cmdwait() to wait for execution of batch of sent commands.
+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 other can be executed. The scheme works by adding two WAIT commands
+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 
@@ -628,17 +628,23 @@ to send them to the DBRI.
 
 */
 
-#define MAXLOOPS 10
+#define MAXLOOPS 20
 /*
  * Wait for the current command string to execute
  */
 static void dbri_cmdwait(struct snd_dbri *dbri)
 {
        int maxloops = MAXLOOPS;
+       unsigned long flags;
 
        /* Delay if previous commands are still being processed */
-       while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P))
+       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);
+       }
+       spin_unlock_irqrestore(&dbri->lock, flags);
 
        if (maxloops == 0) {
                printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
@@ -663,7 +669,7 @@ static s32 *dbri_cmdlock(struct snd_dbri * dbri, int len)
        else
                printk(KERN_ERR "DBRI: no space for commands.");
 
-       return 0;
+       return NULL;
 }
 
 /*
@@ -671,11 +677,12 @@ static s32 *dbri_cmdlock(struct snd_dbri * dbri, int len)
  * 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)
 {
        s32 tmp, addr;
-       unsigned long flags;
        static int wait_id = 0;
 
        wait_id++;
@@ -692,9 +699,8 @@ static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len)
        if (cmd > dbri->cmdptr) {
                s32 *ptr;
 
-               for (ptr = dbri->cmdptr; ptr < cmd+2; ptr++) {
+               for (ptr = dbri->cmdptr; ptr < cmd+2; ptr++)
                        dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
-               }
        } else {
                s32 *ptr = dbri->cmdptr;
 
@@ -707,12 +713,10 @@ static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len)
        }
 #endif
 
-       spin_lock_irqsave(&dbri->lock, flags);
        /* Reread the last command */
        tmp = sbus_readl(dbri->regs + REG0);
        tmp |= D_P;
        sbus_writel(tmp, dbri->regs + REG0);
-       spin_unlock_irqrestore(&dbri->lock, flags);
 
        dbri->cmdptr = cmd;
        spin_unlock(&dbri->cmdlock);
@@ -778,9 +782,9 @@ static void dbri_initialize(struct snd_dbri * dbri)
        dma_addr = dbri->dma_dvma + dbri_dma_off(cmd, 0);
        sbus_writel(dma_addr, dbri->regs + REG8);
        spin_unlock(&dbri->cmdlock);
-       dbri_cmdwait(dbri);
 
        spin_unlock_irqrestore(&dbri->lock, flags);
+       dbri_cmdwait(dbri);
 }
 
 /*
@@ -841,6 +845,9 @@ static void reset_pipe(struct snd_dbri * dbri, int pipe)
        dbri->pipes[pipe].first_desc = -1;
 }
 
+/*
+ * Lock must be held before calling this.
+ */
 static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp)
 {
        if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
@@ -867,6 +874,9 @@ static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp)
        reset_pipe(dbri, pipe);
 }
 
+/*
+ * 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)
@@ -921,6 +931,10 @@ static void link_time_slot(struct snd_dbri * dbri, int pipe,
        dbri_cmdsend(dbri, cmd, 4);
 }
 
+#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)
@@ -953,6 +967,7 @@ static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
 
        dbri_cmdsend(dbri, cmd, 4);
 }
+#endif
 
 /* xmit_fixed() / recv_fixed()
  *
@@ -966,11 +981,14 @@ static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
  * 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(struct snd_dbri * dbri, int pipe, unsigned int data)
 {
        s32 *cmd;
+       unsigned long flags;
 
        if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
                printk(KERN_ERR "DBRI: xmit_fixed: Illegal pipe number\n");
@@ -1003,8 +1021,11 @@ static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data)
        *(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);
+
 }
 
 static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr)
@@ -1040,12 +1061,14 @@ static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr)
  * be spread across multiple descriptors.
  *
  * All descriptors create a ring buffer.
+ *
+ * Lock must be held before calling this.
  */
 static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period)
 {
        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;
@@ -1088,6 +1111,18 @@ static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period
                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;
 
@@ -1141,13 +1176,9 @@ static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period
                return -1;
        }
 
-       if (streamno == DBRI_PLAY) {
-               dbri->dma->desc[last_desc].word1 |=
-                   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->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;
 
@@ -1179,6 +1210,9 @@ multiplexed serial interface which the DBRI can operate in either master
 
 enum master_or_slave { CHImaster, CHIslave };
 
+/*
+ * 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)
 {
@@ -1251,9 +1285,14 @@ static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_sla
 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(struct snd_dbri * dbri)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&dbri->lock, flags);
        /*
         * Data mode:
         * Pipe  4: Send timeslots 1-4 (audio data)
@@ -1277,6 +1316,7 @@ static void cs4215_setup_pipes(struct snd_dbri * dbri)
        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);
 }
@@ -1351,6 +1391,7 @@ 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);
@@ -1375,6 +1416,7 @@ static void cs4215_open(struct snd_dbri * dbri)
         * 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);
@@ -1402,6 +1444,7 @@ static void cs4215_open(struct snd_dbri * dbri)
        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);
 }
@@ -1413,6 +1456,7 @@ 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 */
 
@@ -1449,6 +1493,7 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
         * 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);
@@ -1465,14 +1510,17 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
        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 = 10; ((dbri->mm.status & 0xe4) != 0x20); --i) {
                msleep_interruptible(1);
@@ -1639,7 +1687,6 @@ static void xmit_descs(struct snd_dbri *dbri)
        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);
 
@@ -1649,27 +1696,20 @@ static void xmit_descs(struct snd_dbri *dbri)
                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, 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);
+               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;
+                       /* 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->pipe >= 0) {
                first_td = dbri->pipes[info->pipe].first_desc;
@@ -1685,10 +1725,11 @@ play:
                        *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
                        dbri_cmdsend(dbri, cmd, 2);
 
-                       /* Reset our admin of the pipe & bytes written. */
+                       /* Reset our admin of the pipe. */
                        dbri->pipes[info->pipe].desc = first_td;
                }
        }
+
        spin_unlock_irqrestore(&dbri->lock, flags);
 }
 
@@ -1755,7 +1796,6 @@ static void reception_complete_intr(struct snd_dbri * dbri, int pipe)
                return;
        }
 
-       dbri->dma->desc[rd].ba = 0;
        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. */
@@ -1768,18 +1808,6 @@ static void reception_complete_intr(struct snd_dbri * dbri, int pipe)
        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 0
-       if (dbri->next_desc[rd] == -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);
-       }
-#endif
-
        /* Notify ALSA */
        if (spin_is_locked(&dbri->lock)) {
                spin_unlock(&dbri->lock);
@@ -1875,8 +1903,7 @@ static void dbri_process_interrupt_buffer(struct snd_dbri * dbri)
        }
 }
 
-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)
 {
        struct snd_dbri *dbri = dev_id;
        static int errcnt = 0;
@@ -2009,10 +2036,10 @@ static int snd_dbri_open(struct snd_pcm_substream *substream)
        spin_unlock_irqrestore(&dbri->lock, flags);
 
        snd_pcm_hw_rule_add(runtime,0,SNDRV_PCM_HW_PARAM_CHANNELS,
-                           snd_hw_rule_format, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+                           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, 0
+                           snd_hw_rule_channels, NULL
                            SNDRV_PCM_HW_PARAM_CHANNELS,
                            -1);
                                
@@ -2080,6 +2107,7 @@ static int snd_dbri_hw_free(struct snd_pcm_substream *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.
@@ -2094,7 +2122,10 @@ static int snd_dbri_hw_free(struct snd_pcm_substream *substream)
                                  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);
 }
@@ -2103,7 +2134,6 @@ static int snd_dbri_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
        struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int ret;
 
        info->size = snd_pcm_lib_buffer_bytes(substream);
@@ -2113,6 +2143,7 @@ static int snd_dbri_prepare(struct snd_pcm_substream *substream)
                info->pipe = 6; /* Receive pipe */
 
        spin_lock_irq(&dbri->lock);
+       info->offset = 0;
 
        /* Setup the all the transmit/receive desciptors to cover the
         * whole DMA buffer.
@@ -2120,8 +2151,6 @@ static int snd_dbri_prepare(struct snd_pcm_substream *substream)
        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);
@@ -2243,7 +2272,6 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
 {
        struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
        struct dbri_streaminfo *info = &dbri->stream_info[kcontrol->private_value];
-       unsigned long flags;
        int changed = 0;
 
        if (info->left_gain != ucontrol->value.integer.value[0]) {
@@ -2258,13 +2286,9 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
                /* 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;
 }
@@ -2311,7 +2335,6 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
-       unsigned long flags;
        int elem = kcontrol->private_value & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0xff;
        int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -2344,13 +2367,9 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
                /* 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;
 }
@@ -2392,8 +2411,6 @@ static struct snd_kcontrol_new dbri_controls[] __devinitdata = {
        CS4215_SINGLE("Mic boost", 4, 4, 1, 1)
 };
 
-#define NUM_CS4215_CONTROLS (sizeof(dbri_controls)/sizeof(struct snd_kcontrol_new))
-
 static int __init snd_dbri_mixer(struct snd_dbri * dbri)
 {
        struct snd_card *card;
@@ -2404,7 +2421,7 @@ static int __init snd_dbri_mixer(struct snd_dbri * dbri)
        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)
                        return err;