ALSA: pcxhr: Use nonatomic PCM ops

This time PCXHR, another Digigram boards: like the previous patches,
the conversion is straightforward, replacing spinlocks with mutexes,
merging the irq tasklet into the threaded irq handler and the PCM
trigger tasklet back to the trigger callback.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2014-09-10 12:21:38 +02:00
parent 8d3a8b5cb5
commit 9bef72bdb2
4 changed files with 77 additions and 78 deletions

View File

@ -702,13 +702,11 @@ static inline int pcxhr_stream_scheduled_get_pipe(struct pcxhr_stream *stream,
return 0; return 0;
} }
static void pcxhr_trigger_tasklet(unsigned long arg) static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr)
{ {
unsigned long flags;
int i, j, err; int i, j, err;
struct pcxhr_pipe *pipe; struct pcxhr_pipe *pipe;
struct snd_pcxhr *chip; struct snd_pcxhr *chip;
struct pcxhr_mgr *mgr = (struct pcxhr_mgr*)(arg);
int capture_mask = 0; int capture_mask = 0;
int playback_mask = 0; int playback_mask = 0;
@ -736,11 +734,11 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
} }
if (capture_mask == 0 && playback_mask == 0) { if (capture_mask == 0 && playback_mask == 0) {
mutex_unlock(&mgr->setup_mutex); mutex_unlock(&mgr->setup_mutex);
dev_err(&mgr->pci->dev, "pcxhr_trigger_tasklet : no pipes\n"); dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : no pipes\n");
return; return;
} }
dev_dbg(&mgr->pci->dev, "pcxhr_trigger_tasklet : " dev_dbg(&mgr->pci->dev, "pcxhr_start_linked_stream : "
"playback_mask=%x capture_mask=%x\n", "playback_mask=%x capture_mask=%x\n",
playback_mask, capture_mask); playback_mask, capture_mask);
@ -748,7 +746,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0); err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0);
if (err) { if (err) {
mutex_unlock(&mgr->setup_mutex); mutex_unlock(&mgr->setup_mutex);
dev_err(&mgr->pci->dev, "pcxhr_trigger_tasklet : " dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : "
"error stop pipes (P%x C%x)\n", "error stop pipes (P%x C%x)\n",
playback_mask, capture_mask); playback_mask, capture_mask);
return; return;
@ -793,7 +791,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1); err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
if (err) { if (err) {
mutex_unlock(&mgr->setup_mutex); mutex_unlock(&mgr->setup_mutex);
dev_err(&mgr->pci->dev, "pcxhr_trigger_tasklet : " dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : "
"error start pipes (P%x C%x)\n", "error start pipes (P%x C%x)\n",
playback_mask, capture_mask); playback_mask, capture_mask);
return; return;
@ -802,7 +800,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
/* put the streams into the running state now /* put the streams into the running state now
* (increment pointer by interrupt) * (increment pointer by interrupt)
*/ */
spin_lock_irqsave(&mgr->lock, flags); mutex_lock(&mgr->lock);
for ( i =0; i < mgr->num_cards; i++) { for ( i =0; i < mgr->num_cards; i++) {
struct pcxhr_stream *stream; struct pcxhr_stream *stream;
chip = mgr->chip[i]; chip = mgr->chip[i];
@ -820,13 +818,13 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
} }
} }
} }
spin_unlock_irqrestore(&mgr->lock, flags); mutex_unlock(&mgr->lock);
mutex_unlock(&mgr->setup_mutex); mutex_unlock(&mgr->setup_mutex);
#ifdef CONFIG_SND_DEBUG_VERBOSE #ifdef CONFIG_SND_DEBUG_VERBOSE
do_gettimeofday(&my_tv2); do_gettimeofday(&my_tv2);
dev_dbg(&mgr->pci->dev, "***TRIGGER TASKLET*** TIME = %ld (err = %x)\n", dev_dbg(&mgr->pci->dev, "***TRIGGER START*** TIME = %ld (err = %x)\n",
(long)(my_tv2.tv_usec - my_tv1.tv_usec), err); (long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
#endif #endif
} }
@ -853,7 +851,7 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
PCXHR_STREAM_STATUS_SCHEDULE_RUN; PCXHR_STREAM_STATUS_SCHEDULE_RUN;
snd_pcm_trigger_done(s, subs); snd_pcm_trigger_done(s, subs);
} }
tasklet_schedule(&chip->mgr->trigger_taskq); pcxhr_start_linked_stream(chip->mgr);
} else { } else {
stream = subs->runtime->private_data; stream = subs->runtime->private_data;
snd_printdd("Only one Substream %c %d\n", snd_printdd("Only one Substream %c %d\n",
@ -1127,20 +1125,19 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
static snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs) static snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs)
{ {
unsigned long flags;
u_int32_t timer_period_frag; u_int32_t timer_period_frag;
int timer_buf_periods; int timer_buf_periods;
struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
struct snd_pcm_runtime *runtime = subs->runtime; struct snd_pcm_runtime *runtime = subs->runtime;
struct pcxhr_stream *stream = runtime->private_data; struct pcxhr_stream *stream = runtime->private_data;
spin_lock_irqsave(&chip->mgr->lock, flags); mutex_lock(&chip->mgr->lock);
/* get the period fragment and the nb of periods in the buffer */ /* get the period fragment and the nb of periods in the buffer */
timer_period_frag = stream->timer_period_frag; timer_period_frag = stream->timer_period_frag;
timer_buf_periods = stream->timer_buf_periods; timer_buf_periods = stream->timer_buf_periods;
spin_unlock_irqrestore(&chip->mgr->lock, flags); mutex_unlock(&chip->mgr->lock);
return (snd_pcm_uframes_t)((timer_buf_periods * runtime->period_size) + return (snd_pcm_uframes_t)((timer_buf_periods * runtime->period_size) +
timer_period_frag); timer_period_frag);
@ -1181,6 +1178,7 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcxhr_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcxhr_ops);
pcm->info_flags = 0; pcm->info_flags = 0;
pcm->nonatomic = true;
strcpy(pcm->name, name); strcpy(pcm->name, name);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
@ -1588,7 +1586,8 @@ static int pcxhr_probe(struct pci_dev *pci,
mgr->pci = pci; mgr->pci = pci;
mgr->irq = -1; mgr->irq = -1;
if (request_irq(pci->irq, pcxhr_interrupt, IRQF_SHARED, if (request_threaded_irq(pci->irq, pcxhr_interrupt,
pcxhr_threaded_irq, IRQF_SHARED,
KBUILD_MODNAME, mgr)) { KBUILD_MODNAME, mgr)) {
dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq); dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
pcxhr_free(mgr); pcxhr_free(mgr);
@ -1601,19 +1600,13 @@ static int pcxhr_probe(struct pci_dev *pci,
mgr->shortname, mgr->shortname,
mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq); mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
/* ISR spinlock */ /* ISR lock */
spin_lock_init(&mgr->lock); mutex_init(&mgr->lock);
spin_lock_init(&mgr->msg_lock); mutex_init(&mgr->msg_lock);
/* init setup mutex*/ /* init setup mutex*/
mutex_init(&mgr->setup_mutex); mutex_init(&mgr->setup_mutex);
/* init taslket */
tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet,
(unsigned long) mgr);
tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet,
(unsigned long) mgr);
mgr->prmh = kmalloc(sizeof(*mgr->prmh) + mgr->prmh = kmalloc(sizeof(*mgr->prmh) +
sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS - sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
PCXHR_SIZE_MAX_STATUS), PCXHR_SIZE_MAX_STATUS),

View File

@ -78,14 +78,10 @@ struct pcxhr_mgr {
char shortname[32]; /* short name of this soundcard */ char shortname[32]; /* short name of this soundcard */
char longname[96]; /* name of this soundcard */ char longname[96]; /* name of this soundcard */
/* message tasklet */
struct tasklet_struct msg_taskq;
struct pcxhr_rmh *prmh; struct pcxhr_rmh *prmh;
/* trigger tasklet */
struct tasklet_struct trigger_taskq;
spinlock_t lock; /* interrupt spinlock */ struct mutex lock; /* interrupt lock */
spinlock_t msg_lock; /* message spinlock */ struct mutex msg_lock; /* message lock */
struct mutex setup_mutex; /* mutex used in hw_params, open and close */ struct mutex setup_mutex; /* mutex used in hw_params, open and close */
struct mutex mixer_mutex; /* mutex for mixer */ struct mutex mixer_mutex; /* mutex for mixer */

View File

@ -767,11 +767,11 @@ void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh *rmh, int capture,
*/ */
int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh) int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
{ {
unsigned long flags;
int err; int err;
spin_lock_irqsave(&mgr->msg_lock, flags);
mutex_lock(&mgr->msg_lock);
err = pcxhr_send_msg_nolock(mgr, rmh); err = pcxhr_send_msg_nolock(mgr, rmh);
spin_unlock_irqrestore(&mgr->msg_lock, flags); mutex_unlock(&mgr->msg_lock);
return err; return err;
} }
@ -971,17 +971,16 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
unsigned int value, int *changed) unsigned int value, int *changed)
{ {
struct pcxhr_rmh rmh; struct pcxhr_rmh rmh;
unsigned long flags;
int err; int err;
spin_lock_irqsave(&mgr->msg_lock, flags); mutex_lock(&mgr->msg_lock);
if ((mgr->io_num_reg_cont & mask) == value) { if ((mgr->io_num_reg_cont & mask) == value) {
dev_dbg(&mgr->pci->dev, dev_dbg(&mgr->pci->dev,
"IO_NUM_REG_CONT mask %x already is set to %x\n", "IO_NUM_REG_CONT mask %x already is set to %x\n",
mask, value); mask, value);
if (changed) if (changed)
*changed = 0; *changed = 0;
spin_unlock_irqrestore(&mgr->msg_lock, flags); mutex_unlock(&mgr->msg_lock);
return 0; /* already programmed */ return 0; /* already programmed */
} }
pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
@ -996,7 +995,7 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
if (changed) if (changed)
*changed = 1; *changed = 1;
} }
spin_unlock_irqrestore(&mgr->msg_lock, flags); mutex_unlock(&mgr->msg_lock);
return err; return err;
} }
@ -1043,22 +1042,21 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
} }
void pcxhr_msg_tasklet(unsigned long arg) static void pcxhr_msg_thread(struct pcxhr_mgr *mgr)
{ {
struct pcxhr_mgr *mgr = (struct pcxhr_mgr *)(arg);
struct pcxhr_rmh *prmh = mgr->prmh; struct pcxhr_rmh *prmh = mgr->prmh;
int err; int err;
int i, j; int i, j;
if (mgr->src_it_dsp & PCXHR_IRQ_FREQ_CHANGE) if (mgr->src_it_dsp & PCXHR_IRQ_FREQ_CHANGE)
dev_dbg(&mgr->pci->dev, dev_dbg(&mgr->pci->dev,
"TASKLET : PCXHR_IRQ_FREQ_CHANGE event occurred\n"); "PCXHR_IRQ_FREQ_CHANGE event occurred\n");
if (mgr->src_it_dsp & PCXHR_IRQ_TIME_CODE) if (mgr->src_it_dsp & PCXHR_IRQ_TIME_CODE)
dev_dbg(&mgr->pci->dev, dev_dbg(&mgr->pci->dev,
"TASKLET : PCXHR_IRQ_TIME_CODE event occurred\n"); "PCXHR_IRQ_TIME_CODE event occurred\n");
if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY) if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY)
dev_dbg(&mgr->pci->dev, dev_dbg(&mgr->pci->dev,
"TASKLET : PCXHR_IRQ_NOTIFY event occurred\n"); "PCXHR_IRQ_NOTIFY event occurred\n");
if (mgr->src_it_dsp & (PCXHR_IRQ_FREQ_CHANGE | PCXHR_IRQ_TIME_CODE)) { if (mgr->src_it_dsp & (PCXHR_IRQ_FREQ_CHANGE | PCXHR_IRQ_TIME_CODE)) {
/* clear events FREQ_CHANGE and TIME_CODE */ /* clear events FREQ_CHANGE and TIME_CODE */
pcxhr_init_rmh(prmh, CMD_TEST_IT); pcxhr_init_rmh(prmh, CMD_TEST_IT);
@ -1068,7 +1066,7 @@ void pcxhr_msg_tasklet(unsigned long arg)
} }
if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) { if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) {
dev_dbg(&mgr->pci->dev, dev_dbg(&mgr->pci->dev,
"TASKLET : PCXHR_IRQ_ASYNC event occurred\n"); "PCXHR_IRQ_ASYNC event occurred\n");
pcxhr_init_rmh(prmh, CMD_ASYNC); pcxhr_init_rmh(prmh, CMD_ASYNC);
prmh->cmd[0] |= 1; /* add SEL_ASYNC_EVENTS */ prmh->cmd[0] |= 1; /* add SEL_ASYNC_EVENTS */
@ -1076,7 +1074,7 @@ void pcxhr_msg_tasklet(unsigned long arg)
prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS; prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS;
err = pcxhr_send_msg(mgr, prmh); err = pcxhr_send_msg(mgr, prmh);
if (err) if (err)
dev_err(&mgr->pci->dev, "ERROR pcxhr_msg_tasklet=%x;\n", dev_err(&mgr->pci->dev, "ERROR pcxhr_msg_thread=%x;\n",
err); err);
i = 1; i = 1;
while (i < prmh->stat_len) { while (i < prmh->stat_len) {
@ -1220,9 +1218,9 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
} }
if (elapsed) { if (elapsed) {
spin_unlock(&mgr->lock); mutex_unlock(&mgr->lock);
snd_pcm_period_elapsed(stream->substream); snd_pcm_period_elapsed(stream->substream);
spin_lock(&mgr->lock); mutex_lock(&mgr->lock);
} }
} }
} }
@ -1231,14 +1229,10 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
{ {
struct pcxhr_mgr *mgr = dev_id; struct pcxhr_mgr *mgr = dev_id;
unsigned int reg; unsigned int reg;
int i, j; bool wake_thread = false;
struct snd_pcxhr *chip;
spin_lock(&mgr->lock);
reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS); reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS);
if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) { if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) {
spin_unlock(&mgr->lock);
/* this device did not cause the interrupt */ /* this device did not cause the interrupt */
return IRQ_NONE; return IRQ_NONE;
} }
@ -1250,6 +1244,44 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
/* timer irq occurred */ /* timer irq occurred */
if (reg & PCXHR_IRQ_TIMER) { if (reg & PCXHR_IRQ_TIMER) {
int timer_toggle = reg & PCXHR_IRQ_TIMER; int timer_toggle = reg & PCXHR_IRQ_TIMER;
if (timer_toggle == mgr->timer_toggle) {
dev_dbg(&mgr->pci->dev, "ERROR TIMER TOGGLE\n");
mgr->dsp_time_err++;
}
mgr->timer_toggle = timer_toggle;
mgr->src_it_dsp = reg;
wake_thread = true;
}
/* other irq's handled in the thread */
if (reg & PCXHR_IRQ_MASK) {
if (reg & PCXHR_IRQ_ASYNC) {
/* as we didn't request any async notifications,
* some kind of xrun error will probably occurred
*/
/* better resynchronize all streams next interrupt : */
mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
}
mgr->src_it_dsp = reg;
wake_thread = true;
}
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (reg & PCXHR_FATAL_DSP_ERR)
dev_dbg(&mgr->pci->dev, "FATAL DSP ERROR : %x\n", reg);
#endif
return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
}
irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id)
{
struct pcxhr_mgr *mgr = dev_id;
int i, j;
struct snd_pcxhr *chip;
mutex_lock(&mgr->lock);
if (mgr->src_it_dsp & PCXHR_IRQ_TIMER) {
/* is a 24 bit counter */ /* is a 24 bit counter */
int dsp_time_new = int dsp_time_new =
PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK; PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK;
@ -1290,13 +1322,6 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
#endif #endif
mgr->dsp_time_last = dsp_time_new; mgr->dsp_time_last = dsp_time_new;
if (timer_toggle == mgr->timer_toggle) {
dev_dbg(&mgr->pci->dev, "ERROR TIMER TOGGLE\n");
mgr->dsp_time_err++;
}
mgr->timer_toggle = timer_toggle;
reg &= ~PCXHR_IRQ_TIMER;
for (i = 0; i < mgr->num_cards; i++) { for (i = 0; i < mgr->num_cards; i++) {
chip = mgr->chip[i]; chip = mgr->chip[i];
for (j = 0; j < chip->nb_streams_capt; j++) for (j = 0; j < chip->nb_streams_capt; j++)
@ -1312,22 +1337,7 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
dsp_time_diff); dsp_time_diff);
} }
} }
/* other irq's handled in the tasklet */
if (reg & PCXHR_IRQ_MASK) { pcxhr_msg_thread(mgr);
if (reg & PCXHR_IRQ_ASYNC) { return IRQ_HANDLED;
/* as we didn't request any async notifications,
* some kind of xrun error will probably occurred
*/
/* better resynchronize all streams next interrupt : */
mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
}
mgr->src_it_dsp = reg;
tasklet_schedule(&mgr->msg_taskq);
}
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (reg & PCXHR_FATAL_DSP_ERR)
dev_dbg(&mgr->pci->dev, "FATAL DSP ERROR : %x\n", reg);
#endif
spin_unlock(&mgr->lock);
return IRQ_HANDLED; /* this device caused the interrupt */
} }

View File

@ -200,6 +200,6 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
/* interrupt handling */ /* interrupt handling */
irqreturn_t pcxhr_interrupt(int irq, void *dev_id); irqreturn_t pcxhr_interrupt(int irq, void *dev_id);
void pcxhr_msg_tasklet(unsigned long arg); irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id);
#endif /* __SOUND_PCXHR_CORE_H */ #endif /* __SOUND_PCXHR_CORE_H */