ALSA: lola - Fix PCM stalls
authorTakashi Iwai <tiwai@suse.de>
Tue, 3 May 2011 14:47:03 +0000 (16:47 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 3 May 2011 14:47:03 +0000 (16:47 +0200)
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/lola/lola.c
sound/pci/lola/lola.h
sound/pci/lola/lola_pcm.c

index 16c6cad..4d82215 100644 (file)
@@ -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) {
index 1041b0f..839779d 100644 (file)
@@ -305,6 +305,7 @@ struct lola_stream {
 
        /* flags */
        unsigned int opened:1;
+       unsigned int prepared:1;
        unsigned int running:1;
 };
 
index a656439..ef738e6 100644 (file)
@@ -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)