diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-05-03 16:47:03 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-05-03 16:47:03 +0200 |
commit | 0f8f56c959c9c4a65a5ad13f1b49d97db30b5c2f (patch) | |
tree | 87fde35926c524d466fa0c4897d05e68e3c26718 /sound/pci/lola | |
parent | 333ff3971f374d9a057fd6d1492a2ef78989974d (diff) | |
download | lwn-0f8f56c959c9c4a65a5ad13f1b49d97db30b5c2f.tar.gz lwn-0f8f56c959c9c4a65a5ad13f1b49d97db30b5c2f.zip |
ALSA: lola - Fix PCM stalls
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/lola')
-rw-r--r-- | sound/pci/lola/lola.c | 17 | ||||
-rw-r--r-- | sound/pci/lola/lola.h | 1 | ||||
-rw-r--r-- | sound/pci/lola/lola_pcm.c | 103 |
3 files changed, 74 insertions, 47 deletions
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c index 16c6cad2e7cd..4d8221505b9e 100644 --- a/sound/pci/lola/lola.c +++ b/sound/pci/lola/lola.c @@ -73,6 +73,7 @@ static int corb_send_verb(struct lola *chip, unsigned int nid, chip->last_data = data; chip->last_extdata = extdata; data |= (nid << 20) | (verb << 8); + spin_lock_irqsave(&chip->reg_lock, flags); if (chip->rirb.cmds < LOLA_CORB_ENTRIES - 1) { unsigned int wp = chip->corb.wp + 1; @@ -338,8 +339,6 @@ static int setup_corb_rirb(struct lola *chip) chip->corb.buf = (u32 *)chip->rb.area; chip->rirb.addr = chip->rb.addr + 2048; chip->rirb.buf = (u32 *)(chip->rb.area + 2048); - lola_writel(chip, BAR0, CORBLBASE, (u32)chip->corb.addr); - lola_writel(chip, BAR0, CORBUBASE, upper_32_bits(chip->corb.addr)); /* disable ringbuffer DMAs */ lola_writeb(chip, BAR0, RIRBCTL, 0); @@ -496,6 +495,10 @@ static int lola_parse_tree(struct lola *chip) if (!chip->cold_reset) { lola_reset_setups(chip); chip->cold_reset = 1; + } else { + /* set the granularity if it is not the default */ + if (chip->granularity != LOLA_GRANULARITY_MIN) + lola_set_granularity(chip, chip->granularity, true); } return 0; @@ -561,8 +564,14 @@ static int __devinit lola_create(struct snd_card *card, struct pci_dev *pci, chip->pci = pci; chip->irq = -1; - chip->sample_rate_min = 48000; - chip->granularity = LOLA_GRANULARITY_MIN; + /* below a sample_rate of 16kHz the analogue audio quality + * is NOT excellent + */ + chip->sample_rate_min = 16000; + /* for instance use always max granularity which is compatible + * with all sample rates + */ + chip->granularity = LOLA_GRANULARITY_MAX; err = pci_request_regions(pci, DRVNAME); if (err < 0) { diff --git a/sound/pci/lola/lola.h b/sound/pci/lola/lola.h index 1041b0f0e20f..839779dc32f9 100644 --- a/sound/pci/lola/lola.h +++ b/sound/pci/lola/lola.h @@ -305,6 +305,7 @@ struct lola_stream { /* flags */ unsigned int opened:1; + unsigned int prepared:1; unsigned int running:1; }; diff --git a/sound/pci/lola/lola_pcm.c b/sound/pci/lola/lola_pcm.c index a656439915c8..ef738e6c9919 100644 --- a/sound/pci/lola/lola_pcm.c +++ b/sound/pci/lola/lola_pcm.c @@ -29,7 +29,6 @@ /* #define USE_SG_BUFFER */ #define LOLA_MAX_BDL_ENTRIES 8 -#define LOLA_MAX_FRAG 8 #define LOLA_MAX_BUF_SIZE (1024*1024*1024) #define LOLA_BDL_ENTRY_SIZE (16 * 16) @@ -111,12 +110,6 @@ static void lola_stream_stop(struct lola *chip, struct lola_stream *str, lola_stream_clear_pending_irq(chip, str); } -static void lola_stream_clear(struct lola *chip, struct lola_stream *str) -{ - lola_dsd_write(chip, str->dsd, CTL, 0); - lola_stream_clear_pending_irq(chip, str); -} - static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str) { unsigned long end_time = jiffies + msecs_to_jiffies(200); @@ -130,22 +123,15 @@ static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str) printk(KERN_WARNING SFX "SRST not clear (stream %d)\n", str->dsd); } -static void lola_stream_reset(struct lola *chip, struct lola_stream *str) +static int lola_stream_wait_for_fifo(struct lola *chip, + struct lola_stream *str, + bool ready) { - lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST); - wait_for_srst_clear(chip, str); - lola_dsd_write(chip, str->dsd, LVI, 0); - lola_dsd_write(chip, str->dsd, BDPU, 0); - lola_dsd_write(chip, str->dsd, BDPL, 0); -} - -static int lola_stream_wait_for_fifo_ready(struct lola *chip, - struct lola_stream *str) -{ - unsigned long end_time = jiffies + msecs_to_jiffies(1000); + unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0; + unsigned long end_time = jiffies + msecs_to_jiffies(200); while (time_before(jiffies, end_time)) { - unsigned int val = lola_dsd_read(chip, str->dsd, STS); - if (val & LOLA_DSD_STS_FIFORDY) + unsigned int reg = lola_dsd_read(chip, str->dsd, STS); + if ((reg & LOLA_DSD_STS_FIFORDY) == val) return 0; msleep(1); } @@ -153,6 +139,23 @@ static int lola_stream_wait_for_fifo_ready(struct lola *chip, return -EIO; } +static void lola_stream_reset(struct lola *chip, struct lola_stream *str) +{ + if (str->prepared) { + str->prepared = 0; + + lola_dsd_write(chip, str->dsd, CTL, + LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE); + lola_stream_wait_for_fifo(chip, str, false); + lola_stream_clear_pending_irq(chip, str); + lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST); + lola_dsd_write(chip, str->dsd, LVI, 0); + lola_dsd_write(chip, str->dsd, BDPU, 0); + lola_dsd_write(chip, str->dsd, BDPL, 0); + wait_for_srst_clear(chip, str); + } +} + static struct snd_pcm_hardware lola_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | @@ -163,16 +166,16 @@ static struct snd_pcm_hardware lola_pcm_hw = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT_LE), - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = LOLA_MAX_BUF_SIZE, .period_bytes_min = 128, .period_bytes_max = LOLA_MAX_BUF_SIZE / 2, .periods_min = 2, - .periods_max = LOLA_MAX_FRAG, + .periods_max = LOLA_MAX_BDL_ENTRIES, .fifo_size = 0, }; @@ -194,10 +197,13 @@ static int lola_pcm_open(struct snd_pcm_substream *substream) runtime->hw = lola_pcm_hw; runtime->hw.channels_max = pcm->num_streams - str->index; snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - 128); - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - 128); + /* period size = multiple of chip->granularity (8, 16 or 32 frames) + * use LOLA_GRANULARITY_MAX = 32 for instance + */ + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + LOLA_GRANULARITY_MAX); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + LOLA_GRANULARITY_MAX); mutex_unlock(&chip->open_mutex); return 0; } @@ -383,16 +389,24 @@ static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm, struct lola_stream *str) { dma_addr_t bdl; + + if (str->prepared) + return -EINVAL; + /* set up BDL */ bdl = pcm->bdl.addr + LOLA_BDL_ENTRY_SIZE * str->index; lola_dsd_write(chip, str->dsd, BDPL, (u32)bdl); lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(bdl)); /* program the stream LVI (last valid index) of the BDL */ lola_dsd_write(chip, str->dsd, LVI, str->frags - 1); - lola_stream_stop(chip, str, lola_get_tstamp(chip, false)); - lola_stream_wait_for_fifo_ready(chip, str); + lola_stream_clear_pending_irq(chip, str); - return 0; + lola_dsd_write(chip, str->dsd, CTL, + LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE | LOLA_DSD_CTL_SRUN); + + str->prepared = 1; + + return lola_stream_wait_for_fifo(chip, str, true); } static int lola_pcm_prepare(struct snd_pcm_substream *substream) @@ -421,22 +435,25 @@ static int lola_pcm_prepare(struct snd_pcm_substream *substream) period_bytes = snd_pcm_lib_period_bytes(substream); format_verb = lola_get_format_verb(substream); - if (bufsize != str->bufsize || - period_bytes != str->period_bytes || - format_verb != str->format_verb) { - str->bufsize = bufsize; - str->period_bytes = period_bytes; - str->format_verb = format_verb; - err = lola_setup_periods(chip, pcm, substream, str); - if (err < 0) - return err; - } + str->bufsize = bufsize; + str->period_bytes = period_bytes; + str->format_verb = format_verb; + + err = lola_setup_periods(chip, pcm, substream, str); + if (err < 0) + return err; err = lola_set_stream_config(chip, str, runtime->channels); if (err < 0) return err; - return lola_setup_controller(chip, pcm, str); + err = lola_setup_controller(chip, pcm, str); + if (err < 0) { + lola_stream_reset(chip, str); + return err; + } + + return 0; } static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd) |