Commit ea543f1e authored by Krzysztof Helt's avatar Krzysztof Helt Committed by Jaroslav Kysela

[ALSA] sparc dbri: SMP fixes

The dbri driver hangs when used in kernel compiled with SMP
support due to inproper locking. The patch fixes it.
Signed-off-by: default avatarKrzysztof Helt <krzysztof.h1@wp.pl>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@suse.cz>
parent 679e28ee
...@@ -635,10 +635,16 @@ to send them to the DBRI. ...@@ -635,10 +635,16 @@ to send them to the DBRI.
static void dbri_cmdwait(struct snd_dbri *dbri) static void dbri_cmdwait(struct snd_dbri *dbri)
{ {
int maxloops = MAXLOOPS; int maxloops = MAXLOOPS;
unsigned long flags;
/* Delay if previous commands are still being processed */ /* 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); msleep_interruptible(1);
spin_lock_irqsave(&dbri->lock, flags);
}
spin_unlock_irqrestore(&dbri->lock, flags);
if (maxloops == 0) { if (maxloops == 0) {
printk(KERN_ERR "DBRI: Chip never completed command buffer\n"); printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
...@@ -671,11 +677,12 @@ static s32 *dbri_cmdlock(struct snd_dbri * dbri, int len) ...@@ -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 last WAIT cmd and force DBRI to reread the cmd.
* The JUMP cmd points to the new cmd string. * The JUMP cmd points to the new cmd string.
* It also releases the cmdlock spinlock. * 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) static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len)
{ {
s32 tmp, addr; s32 tmp, addr;
unsigned long flags;
static int wait_id = 0; static int wait_id = 0;
wait_id++; wait_id++;
...@@ -706,12 +713,10 @@ static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len) ...@@ -706,12 +713,10 @@ static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len)
} }
#endif #endif
spin_lock_irqsave(&dbri->lock, flags);
/* Reread the last command */ /* Reread the last command */
tmp = sbus_readl(dbri->regs + REG0); tmp = sbus_readl(dbri->regs + REG0);
tmp |= D_P; tmp |= D_P;
sbus_writel(tmp, dbri->regs + REG0); sbus_writel(tmp, dbri->regs + REG0);
spin_unlock_irqrestore(&dbri->lock, flags);
dbri->cmdptr = cmd; dbri->cmdptr = cmd;
spin_unlock(&dbri->cmdlock); spin_unlock(&dbri->cmdlock);
...@@ -777,9 +782,9 @@ static void dbri_initialize(struct snd_dbri * dbri) ...@@ -777,9 +782,9 @@ static void dbri_initialize(struct snd_dbri * dbri)
dma_addr = dbri->dma_dvma + dbri_dma_off(cmd, 0); dma_addr = dbri->dma_dvma + dbri_dma_off(cmd, 0);
sbus_writel(dma_addr, dbri->regs + REG8); sbus_writel(dma_addr, dbri->regs + REG8);
spin_unlock(&dbri->cmdlock); spin_unlock(&dbri->cmdlock);
dbri_cmdwait(dbri);
spin_unlock_irqrestore(&dbri->lock, flags); spin_unlock_irqrestore(&dbri->lock, flags);
dbri_cmdwait(dbri);
} }
/* /*
...@@ -840,6 +845,9 @@ static void reset_pipe(struct snd_dbri * dbri, int pipe) ...@@ -840,6 +845,9 @@ static void reset_pipe(struct snd_dbri * dbri, int pipe)
dbri->pipes[pipe].first_desc = -1; 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) static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp)
{ {
if (pipe < 0 || pipe > DBRI_MAX_PIPE) { if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
...@@ -866,6 +874,9 @@ static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp) ...@@ -866,6 +874,9 @@ static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp)
reset_pipe(dbri, pipe); reset_pipe(dbri, pipe);
} }
/*
* Lock must be held before calling this.
*/
static void link_time_slot(struct snd_dbri * dbri, int pipe, static void link_time_slot(struct snd_dbri * dbri, int pipe,
int prevpipe, int nextpipe, int prevpipe, int nextpipe,
int length, int cycle) int length, int cycle)
...@@ -920,6 +931,10 @@ static void link_time_slot(struct snd_dbri * dbri, int pipe, ...@@ -920,6 +931,10 @@ static void link_time_slot(struct snd_dbri * dbri, int pipe,
dbri_cmdsend(dbri, cmd, 4); 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, static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
enum in_or_out direction, int prevpipe, enum in_or_out direction, int prevpipe,
int nextpipe) int nextpipe)
...@@ -952,6 +967,7 @@ static void unlink_time_slot(struct snd_dbri * dbri, int pipe, ...@@ -952,6 +967,7 @@ static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
dbri_cmdsend(dbri, cmd, 4); dbri_cmdsend(dbri, cmd, 4);
} }
#endif
/* xmit_fixed() / recv_fixed() /* xmit_fixed() / recv_fixed()
* *
...@@ -965,11 +981,14 @@ static void unlink_time_slot(struct snd_dbri * dbri, int pipe, ...@@ -965,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 * the actual time slot is. The interrupt handler takes care of bit
* ordering and alignment. An 8-bit time slot will always end up * 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, * 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) static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data)
{ {
s32 *cmd; s32 *cmd;
unsigned long flags;
if (pipe < 16 || pipe > DBRI_MAX_PIPE) { if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
printk(KERN_ERR "DBRI: xmit_fixed: Illegal pipe number\n"); printk(KERN_ERR "DBRI: xmit_fixed: Illegal pipe number\n");
...@@ -1002,8 +1021,11 @@ static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data) ...@@ -1002,8 +1021,11 @@ static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data)
*(cmd++) = data; *(cmd++) = data;
*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
spin_lock_irqsave(&dbri->lock, flags);
dbri_cmdsend(dbri, cmd, 3); dbri_cmdsend(dbri, cmd, 3);
spin_unlock_irqrestore(&dbri->lock, flags);
dbri_cmdwait(dbri); dbri_cmdwait(dbri);
} }
static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr) static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr)
...@@ -1039,6 +1061,8 @@ static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr) ...@@ -1039,6 +1061,8 @@ static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr)
* be spread across multiple descriptors. * be spread across multiple descriptors.
* *
* All descriptors create a ring buffer. * 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) static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period)
{ {
...@@ -1186,6 +1210,9 @@ multiplexed serial interface which the DBRI can operate in either master ...@@ -1186,6 +1210,9 @@ multiplexed serial interface which the DBRI can operate in either master
enum master_or_slave { CHImaster, CHIslave }; 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, static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_slave,
int bits_per_frame) int bits_per_frame)
{ {
...@@ -1258,9 +1285,14 @@ static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_sla ...@@ -1258,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 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. 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) static void cs4215_setup_pipes(struct snd_dbri * dbri)
{ {
unsigned long flags;
spin_lock_irqsave(&dbri->lock, flags);
/* /*
* Data mode: * Data mode:
* Pipe 4: Send timeslots 1-4 (audio data) * Pipe 4: Send timeslots 1-4 (audio data)
...@@ -1284,6 +1316,7 @@ static void cs4215_setup_pipes(struct snd_dbri * dbri) ...@@ -1284,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, 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, 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); setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
spin_unlock_irqrestore(&dbri->lock, flags);
dbri_cmdwait(dbri); dbri_cmdwait(dbri);
} }
...@@ -1358,6 +1391,7 @@ static void cs4215_open(struct snd_dbri * dbri) ...@@ -1358,6 +1391,7 @@ static void cs4215_open(struct snd_dbri * dbri)
{ {
int data_width; int data_width;
u32 tmp; u32 tmp;
unsigned long flags;
dprintk(D_MM, "cs4215_open: %d channels, %d bits\n", dprintk(D_MM, "cs4215_open: %d channels, %d bits\n",
dbri->mm.channels, dbri->mm.precision); dbri->mm.channels, dbri->mm.precision);
...@@ -1382,6 +1416,7 @@ static void cs4215_open(struct snd_dbri * dbri) ...@@ -1382,6 +1416,7 @@ static void cs4215_open(struct snd_dbri * dbri)
* bits. The CS4215, it seems, observes TSIN (the delayed signal) * bits. The CS4215, it seems, observes TSIN (the delayed signal)
* even if it's the CHI master. Don't ask me... * even if it's the CHI master. Don't ask me...
*/ */
spin_lock_irqsave(&dbri->lock, flags);
tmp = sbus_readl(dbri->regs + REG0); tmp = sbus_readl(dbri->regs + REG0);
tmp &= ~(D_C); /* Disable CHI */ tmp &= ~(D_C); /* Disable CHI */
sbus_writel(tmp, dbri->regs + REG0); sbus_writel(tmp, dbri->regs + REG0);
...@@ -1409,6 +1444,7 @@ static void cs4215_open(struct snd_dbri * dbri) ...@@ -1409,6 +1444,7 @@ static void cs4215_open(struct snd_dbri * dbri)
tmp = sbus_readl(dbri->regs + REG0); tmp = sbus_readl(dbri->regs + REG0);
tmp |= D_C; /* Enable CHI */ tmp |= D_C; /* Enable CHI */
sbus_writel(tmp, dbri->regs + REG0); sbus_writel(tmp, dbri->regs + REG0);
spin_unlock_irqrestore(&dbri->lock, flags);
cs4215_setdata(dbri, 0); cs4215_setdata(dbri, 0);
} }
...@@ -1420,6 +1456,7 @@ static int cs4215_setctrl(struct snd_dbri * dbri) ...@@ -1420,6 +1456,7 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
{ {
int i, val; int i, val;
u32 tmp; u32 tmp;
unsigned long flags;
/* FIXME - let the CPU do something useful during these delays */ /* FIXME - let the CPU do something useful during these delays */
...@@ -1456,6 +1493,7 @@ static int cs4215_setctrl(struct snd_dbri * dbri) ...@@ -1456,6 +1493,7 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
* done in hardware by a TI 248 that delays the DBRI->4215 * done in hardware by a TI 248 that delays the DBRI->4215
* frame sync signal by eight clock cycles. Anybody know why? * frame sync signal by eight clock cycles. Anybody know why?
*/ */
spin_lock_irqsave(&dbri->lock, flags);
tmp = sbus_readl(dbri->regs + REG0); tmp = sbus_readl(dbri->regs + REG0);
tmp &= ~D_C; /* Disable CHI */ tmp &= ~D_C; /* Disable CHI */
sbus_writel(tmp, dbri->regs + REG0); sbus_writel(tmp, dbri->regs + REG0);
...@@ -1472,14 +1510,17 @@ static int cs4215_setctrl(struct snd_dbri * dbri) ...@@ -1472,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, 17, 16, 16, 32, dbri->mm.offset);
link_time_slot(dbri, 18, 16, 16, 8, 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); 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 */ /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
dbri->mm.ctrl[0] &= ~CS4215_CLB; dbri->mm.ctrl[0] &= ~CS4215_CLB;
xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
spin_lock_irqsave(&dbri->lock, flags);
tmp = sbus_readl(dbri->regs + REG0); tmp = sbus_readl(dbri->regs + REG0);
tmp |= D_C; /* Enable CHI */ tmp |= D_C; /* Enable CHI */
sbus_writel(tmp, dbri->regs + REG0); sbus_writel(tmp, dbri->regs + REG0);
spin_unlock_irqrestore(&dbri->lock, flags);
for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i) { for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i) {
msleep_interruptible(1); msleep_interruptible(1);
...@@ -1688,6 +1729,7 @@ static void xmit_descs(struct snd_dbri *dbri) ...@@ -1688,6 +1729,7 @@ static void xmit_descs(struct snd_dbri *dbri)
dbri->pipes[info->pipe].desc = first_td; dbri->pipes[info->pipe].desc = first_td;
} }
} }
spin_unlock_irqrestore(&dbri->lock, flags); spin_unlock_irqrestore(&dbri->lock, flags);
} }
...@@ -2093,7 +2135,6 @@ static int snd_dbri_prepare(struct snd_pcm_substream *substream) ...@@ -2093,7 +2135,6 @@ static int snd_dbri_prepare(struct snd_pcm_substream *substream)
{ {
struct snd_dbri *dbri = snd_pcm_substream_chip(substream); struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream); struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int ret; int ret;
info->size = snd_pcm_lib_buffer_bytes(substream); info->size = snd_pcm_lib_buffer_bytes(substream);
...@@ -2232,7 +2273,6 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol, ...@@ -2232,7 +2273,6 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
{ {
struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol); struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
struct dbri_streaminfo *info = &dbri->stream_info[kcontrol->private_value]; struct dbri_streaminfo *info = &dbri->stream_info[kcontrol->private_value];
unsigned long flags;
int changed = 0; int changed = 0;
if (info->left_gain != ucontrol->value.integer.value[0]) { if (info->left_gain != ucontrol->value.integer.value[0]) {
...@@ -2247,13 +2287,9 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol, ...@@ -2247,13 +2287,9 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
/* First mute outputs, and wait 1/8000 sec (125 us) /* First mute outputs, and wait 1/8000 sec (125 us)
* to make sure this takes. This avoids clicking noises. * to make sure this takes. This avoids clicking noises.
*/ */
spin_lock_irqsave(&dbri->lock, flags);
cs4215_setdata(dbri, 1); cs4215_setdata(dbri, 1);
udelay(125); udelay(125);
cs4215_setdata(dbri, 0); cs4215_setdata(dbri, 0);
spin_unlock_irqrestore(&dbri->lock, flags);
} }
return changed; return changed;
} }
...@@ -2300,7 +2336,6 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol, ...@@ -2300,7 +2336,6 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol); struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int elem = kcontrol->private_value & 0xff; int elem = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff;
...@@ -2333,13 +2368,9 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol, ...@@ -2333,13 +2368,9 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
/* First mute outputs, and wait 1/8000 sec (125 us) /* First mute outputs, and wait 1/8000 sec (125 us)
* to make sure this takes. This avoids clicking noises. * to make sure this takes. This avoids clicking noises.
*/ */
spin_lock_irqsave(&dbri->lock, flags);
cs4215_setdata(dbri, 1); cs4215_setdata(dbri, 1);
udelay(125); udelay(125);
cs4215_setdata(dbri, 0); cs4215_setdata(dbri, 0);
spin_unlock_irqrestore(&dbri->lock, flags);
} }
return changed; return changed;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment