ALSA: lola - Use a single BDL

Use a single BDL for both buffers instead of allocating for each.

Also a few tune-up to avoid the stream stalls in the PCM code and
the prelimianry work for SG-buffer support are added, too.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2011-05-03 16:41:02 +02:00
parent a426c78723
commit 333ff3971f
2 changed files with 49 additions and 31 deletions

View File

@ -299,7 +299,6 @@ struct lola_stream {
unsigned int bufsize;
unsigned int period_bytes;
unsigned int frags;
struct snd_dma_buffer bdl; /* BDL buffer */
/* format + channel setup */
unsigned int format_verb;
@ -314,6 +313,7 @@ struct lola_stream {
struct lola_pcm {
unsigned int num_streams;
struct snd_dma_buffer bdl; /* BDL buffer */
struct lola_stream streams[MAX_STREAM_COUNT];
};

View File

@ -26,10 +26,28 @@
#include <sound/pcm.h>
#include "lola.h"
#define BDL_SIZE 4096
#define LOLA_MAX_BDL_ENTRIES (BDL_SIZE / 16)
#define LOLA_MAX_FRAG 32
/* #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)
#ifdef USE_SG_BUFFER
#define get_addr(substream, ofs) \
snd_pcm_sgbuf_get_addr(substream, ofs)
#define get_size(substream, ofs, size) \
snd_pcm_sgbuf_get_chunk_size(substream, ofs, size)
#define ops_page snd_pcm_sgbuf_ops_page
#define PREALLOC_TYPE SNDRV_DMA_TYPE_DEV_SG
#else
#define get_addr(substream, ofs) \
((substream)->runtime->dma_addr + ofs)
#define get_size(substream, ofs, size) \
(size)
#define ops_page NULL
#define PREALLOC_TYPE SNDRV_DMA_TYPE_DEV
#endif
static struct lola_pcm *lola_get_pcm(struct snd_pcm_substream *substream)
{
@ -101,7 +119,7 @@ static void lola_stream_clear(struct lola *chip, struct lola_stream *str)
static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str)
{
unsigned long end_time = jiffies + msecs_to_jiffies(50);
unsigned long end_time = jiffies + msecs_to_jiffies(200);
while (time_before(jiffies, end_time)) {
unsigned int val;
val = lola_dsd_read(chip, str->dsd, CTL);
@ -115,16 +133,16 @@ static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str)
static void lola_stream_reset(struct lola *chip, struct lola_stream *str)
{
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);
wait_for_srst_clear(chip, str);
}
static int lola_stream_wait_for_fifo_ready(struct lola *chip,
struct lola_stream *str)
{
unsigned long end_time = jiffies + msecs_to_jiffies(50);
unsigned long end_time = jiffies + msecs_to_jiffies(1000);
while (time_before(jiffies, end_time)) {
unsigned int val = lola_dsd_read(chip, str->dsd, STS);
if (val & LOLA_DSD_STS_FIFORDY)
@ -164,21 +182,12 @@ static int lola_pcm_open(struct snd_pcm_substream *substream)
struct lola_pcm *pcm = lola_get_pcm(substream);
struct lola_stream *str = lola_get_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
mutex_lock(&chip->open_mutex);
if (str->opened) {
mutex_unlock(&chip->open_mutex);
return -EBUSY;
}
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
PAGE_SIZE, &str->bdl);
if (err < 0) {
mutex_unlock(&chip->open_mutex);
printk(KERN_ERR SFX "Can't allocate BDL\n");
return err;
}
str->substream = substream;
str->master = NULL;
str->opened = 1;
@ -216,7 +225,6 @@ static int lola_pcm_close(struct snd_pcm_substream *substream)
str->substream = NULL;
str->opened = 0;
}
snd_dma_free_pages(&str->bdl);
mutex_unlock(&chip->open_mutex);
return 0;
}
@ -262,12 +270,12 @@ static int setup_bdle(struct snd_pcm_substream *substream,
if (str->frags >= LOLA_MAX_BDL_ENTRIES)
return -EINVAL;
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
addr = get_addr(substream, ofs);
/* program the address field of the BDL entry */
bdl[0] = cpu_to_le32((u32)addr);
bdl[1] = cpu_to_le32(upper_32_bits(addr));
/* program the size field of the BDL entry */
chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
chunk = get_size(substream, ofs, size);
bdl[2] = cpu_to_le32(chunk);
/* program the IOC to enable interrupt
* only when the whole fragment is processed
@ -285,7 +293,7 @@ static int setup_bdle(struct snd_pcm_substream *substream,
/*
* set up BDL entries
*/
static int lola_setup_periods(struct lola *chip,
static int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm,
struct snd_pcm_substream *substream,
struct lola_stream *str)
{
@ -296,7 +304,7 @@ static int lola_setup_periods(struct lola *chip,
periods = str->bufsize / period_bytes;
/* program the initial BDL entries */
bdl = (u32 *)str->bdl.area;
bdl = (u32 *)(pcm->bdl.area + LOLA_BDL_ENTRY_SIZE * str->index);
ofs = 0;
str->frags = 0;
for (i = 0; i < periods; i++) {
@ -371,13 +379,14 @@ static int lola_set_stream_config(struct lola *chip,
/*
* set up the SD for streaming
*/
static int lola_setup_controller(struct lola *chip, struct lola_stream *str)
static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm,
struct lola_stream *str)
{
/* make sure the run bit is zero for SD */
lola_stream_clear(chip, str);
dma_addr_t bdl;
/* set up BDL */
lola_dsd_write(chip, str->dsd, BDPL, (u32)str->bdl.addr);
lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(str->bdl.addr));
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));
@ -418,7 +427,7 @@ static int lola_pcm_prepare(struct snd_pcm_substream *substream)
str->bufsize = bufsize;
str->period_bytes = period_bytes;
str->format_verb = format_verb;
err = lola_setup_periods(chip, substream, str);
err = lola_setup_periods(chip, pcm, substream, str);
if (err < 0)
return err;
}
@ -427,7 +436,7 @@ static int lola_pcm_prepare(struct snd_pcm_substream *substream)
if (err < 0)
return err;
return lola_setup_controller(chip, str);
return lola_setup_controller(chip, pcm, str);
}
static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
@ -504,7 +513,7 @@ static struct snd_pcm_ops lola_pcm_ops = {
.prepare = lola_pcm_prepare,
.trigger = lola_pcm_trigger,
.pointer = lola_pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
.page = ops_page,
};
int __devinit lola_create_pcm(struct lola *chip)
@ -512,6 +521,14 @@ int __devinit lola_create_pcm(struct lola *chip)
struct snd_pcm *pcm;
int i, err;
for (i = 0; i < 2; i++) {
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
PAGE_SIZE, &chip->pcm[i].bdl);
if (err < 0)
return err;
}
err = snd_pcm_new(chip->card, "Digigram Lola", 0,
chip->pcm[SNDRV_PCM_STREAM_PLAYBACK].num_streams,
chip->pcm[SNDRV_PCM_STREAM_CAPTURE].num_streams,
@ -525,7 +542,7 @@ int __devinit lola_create_pcm(struct lola *chip)
snd_pcm_set_ops(pcm, i, &lola_pcm_ops);
}
/* buffer pre-allocation */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_pcm_lib_preallocate_pages_for_all(pcm, PREALLOC_TYPE,
snd_dma_pci_data(chip->pci),
1024 * 64, 32 * 1024 * 1024);
return 0;
@ -533,7 +550,8 @@ int __devinit lola_create_pcm(struct lola *chip)
void lola_free_pcm(struct lola *chip)
{
/* nothing to do */
snd_dma_free_pages(&chip->pcm[0].bdl);
snd_dma_free_pages(&chip->pcm[1].bdl);
}
/*