Commit 4ce107b9 authored by Takashi Iwai's avatar Takashi Iwai

[ALSA] hda-intel - Use SG buffer

Use SG buffers for the HD-audio instead of linear buffers.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent b76c850f
...@@ -206,8 +206,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; ...@@ -206,8 +206,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define MAX_AZX_DEV 16 #define MAX_AZX_DEV 16
/* max number of fragments - we may use more if allocating more pages for BDL */ /* max number of fragments - we may use more if allocating more pages for BDL */
#define BDL_SIZE PAGE_ALIGN(8192) #define BDL_SIZE 4096
#define AZX_MAX_FRAG (BDL_SIZE / (MAX_AZX_DEV * 16)) #define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
#define AZX_MAX_FRAG 32
/* max buffer size - no h/w limit, you can increase as you like */ /* max buffer size - no h/w limit, you can increase as you like */
#define AZX_MAX_BUF_SIZE (1024*1024*1024) #define AZX_MAX_BUF_SIZE (1024*1024*1024)
/* max number of PCM devics per card */ /* max number of PCM devics per card */
...@@ -282,12 +283,10 @@ enum { ...@@ -282,12 +283,10 @@ enum {
*/ */
struct azx_dev { struct azx_dev {
u32 *bdl; /* virtual address of the BDL */ struct snd_dma_buffer bdl; /* BDL buffer */
dma_addr_t bdl_addr; /* physical address of the BDL */
u32 *posbuf; /* position buffer pointer */ u32 *posbuf; /* position buffer pointer */
unsigned int bufsize; /* size of the play buffer in bytes */ unsigned int bufsize; /* size of the play buffer in bytes */
unsigned int fragsize; /* size of each period in bytes */
unsigned int frags; /* number for period in the play buffer */ unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */ unsigned int fifo_size; /* FIFO size */
...@@ -358,8 +357,7 @@ struct azx { ...@@ -358,8 +357,7 @@ struct azx {
struct azx_rb corb; struct azx_rb corb;
struct azx_rb rirb; struct azx_rb rirb;
/* BDL, CORB/RIRB and position buffers */ /* CORB/RIRB and position buffers */
struct snd_dma_buffer bdl;
struct snd_dma_buffer rb; struct snd_dma_buffer rb;
struct snd_dma_buffer posbuf; struct snd_dma_buffer posbuf;
...@@ -962,30 +960,57 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) ...@@ -962,30 +960,57 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
/* /*
* set up BDL entries * set up BDL entries
*/ */
static void azx_setup_periods(struct azx_dev *azx_dev) static int azx_setup_periods(struct snd_pcm_substream *substream,
struct azx_dev *azx_dev)
{ {
u32 *bdl = azx_dev->bdl; struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr; u32 *bdl;
int idx; int i, ofs, periods, period_bytes;
/* reset BDL address */ /* reset BDL address */
azx_sd_writel(azx_dev, SD_BDLPL, 0); azx_sd_writel(azx_dev, SD_BDLPL, 0);
azx_sd_writel(azx_dev, SD_BDLPU, 0); azx_sd_writel(azx_dev, SD_BDLPU, 0);
period_bytes = snd_pcm_lib_period_bytes(substream);
periods = azx_dev->bufsize / period_bytes;
/* program the initial BDL entries */ /* program the initial BDL entries */
for (idx = 0; idx < azx_dev->frags; idx++) { bdl = (u32 *)azx_dev->bdl.area;
unsigned int off = idx << 2; /* 4 dword step */ ofs = 0;
dma_addr_t addr = dma_addr + idx * azx_dev->fragsize; azx_dev->frags = 0;
for (i = 0; i < periods; i++) {
int size, rest;
if (i >= AZX_MAX_BDL_ENTRIES) {
snd_printk(KERN_ERR "Too many BDL entries: "
"buffer=%d, period=%d\n",
azx_dev->bufsize, period_bytes);
/* reset */
azx_sd_writel(azx_dev, SD_BDLPL, 0);
azx_sd_writel(azx_dev, SD_BDLPU, 0);
return -EINVAL;
}
rest = period_bytes;
do {
dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
/* program the address field of the BDL entry */ /* program the address field of the BDL entry */
bdl[off] = cpu_to_le32((u32)addr); bdl[0] = cpu_to_le32((u32)addr);
bdl[off+1] = cpu_to_le32(upper_32bit(addr)); bdl[1] = cpu_to_le32(upper_32bit(addr));
/* program the size field of the BDL entry */ /* program the size field of the BDL entry */
bdl[off+2] = cpu_to_le32(azx_dev->fragsize); size = PAGE_SIZE - (ofs % PAGE_SIZE);
if (rest < size)
/* program the IOC to enable interrupt when buffer completes */ size = rest;
bdl[off+3] = cpu_to_le32(0x01); bdl[2] = cpu_to_le32(size);
/* program the IOC to enable interrupt
* only when the whole fragment is processed
*/
rest -= size;
bdl[3] = rest ? 0 : cpu_to_le32(0x01);
bdl += 4;
azx_dev->frags++;
ofs += size;
} while (rest > 0);
} }
return 0;
} }
/* /*
...@@ -1034,9 +1059,9 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) ...@@ -1034,9 +1059,9 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
/* program the BDL address */ /* program the BDL address */
/* lower BDL address */ /* lower BDL address */
azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr); azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
/* upper BDL address */ /* upper BDL address */
azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr)); azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl.addr));
/* enable the position buffer */ /* enable the position buffer */
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
...@@ -1272,8 +1297,6 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -1272,8 +1297,6 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream); azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);
azx_dev->fragsize = snd_pcm_lib_period_bytes(substream);
azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize;
azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate, azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,
runtime->channels, runtime->channels,
runtime->format, runtime->format,
...@@ -1288,7 +1311,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -1288,7 +1311,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, " snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, "
"format=0x%x\n", "format=0x%x\n",
azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val); azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val);
azx_setup_periods(azx_dev); if (azx_setup_periods(substream, azx_dev) < 0)
return -EINVAL;
azx_setup_controller(chip, azx_dev); azx_setup_controller(chip, azx_dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
...@@ -1375,6 +1399,7 @@ static struct snd_pcm_ops azx_pcm_ops = { ...@@ -1375,6 +1399,7 @@ static struct snd_pcm_ops azx_pcm_ops = {
.prepare = azx_pcm_prepare, .prepare = azx_pcm_prepare,
.trigger = azx_pcm_trigger, .trigger = azx_pcm_trigger,
.pointer = azx_pcm_pointer, .pointer = azx_pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
}; };
static void azx_pcm_free(struct snd_pcm *pcm) static void azx_pcm_free(struct snd_pcm *pcm)
...@@ -1417,7 +1442,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, ...@@ -1417,7 +1442,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
if (cpcm->stream[1].substreams) if (cpcm->stream[1].substreams)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci), snd_dma_pci_data(chip->pci),
1024 * 64, 1024 * 1024); 1024 * 64, 1024 * 1024);
chip->pcm[cpcm->device] = pcm; chip->pcm[cpcm->device] = pcm;
...@@ -1507,10 +1532,7 @@ static int __devinit azx_init_stream(struct azx *chip) ...@@ -1507,10 +1532,7 @@ static int __devinit azx_init_stream(struct azx *chip)
* and initialize * and initialize
*/ */
for (i = 0; i < chip->num_streams; i++) { for (i = 0; i < chip->num_streams; i++) {
unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4);
struct azx_dev *azx_dev = &chip->azx_dev[i]; struct azx_dev *azx_dev = &chip->azx_dev[i];
azx_dev->bdl = (u32 *)(chip->bdl.area + off);
azx_dev->bdl_addr = chip->bdl.addr + off;
azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8); azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
...@@ -1646,8 +1668,9 @@ static int azx_resume(struct pci_dev *pci) ...@@ -1646,8 +1668,9 @@ static int azx_resume(struct pci_dev *pci)
*/ */
static int azx_free(struct azx *chip) static int azx_free(struct azx *chip)
{ {
if (chip->initialized) {
int i; int i;
if (chip->initialized) {
for (i = 0; i < chip->num_streams; i++) for (i = 0; i < chip->num_streams; i++)
azx_stream_stop(chip, &chip->azx_dev[i]); azx_stream_stop(chip, &chip->azx_dev[i]);
azx_stop_chip(chip); azx_stop_chip(chip);
...@@ -1662,8 +1685,11 @@ static int azx_free(struct azx *chip) ...@@ -1662,8 +1685,11 @@ static int azx_free(struct azx *chip)
if (chip->remap_addr) if (chip->remap_addr)
iounmap(chip->remap_addr); iounmap(chip->remap_addr);
if (chip->bdl.area) if (chip->azx_dev) {
snd_dma_free_pages(&chip->bdl); for (i = 0; i < chip->num_streams; i++)
if (chip->azx_dev[i].bdl.area)
snd_dma_free_pages(&chip->azx_dev[i].bdl);
}
if (chip->rb.area) if (chip->rb.area)
snd_dma_free_pages(&chip->rb); snd_dma_free_pages(&chip->rb);
if (chip->posbuf.area) if (chip->posbuf.area)
...@@ -1745,7 +1771,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, ...@@ -1745,7 +1771,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
struct azx **rchip) struct azx **rchip)
{ {
struct azx *chip; struct azx *chip;
int err; int i, err;
unsigned short gcap; unsigned short gcap;
static struct snd_device_ops ops = { static struct snd_device_ops ops = {
.dev_free = azx_dev_free, .dev_free = azx_dev_free,
...@@ -1857,14 +1883,16 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, ...@@ -1857,14 +1883,16 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
goto errout; goto errout;
} }
for (i = 0; i < chip->num_streams; i++) {
/* allocate memory for the BDL for each stream */ /* allocate memory for the BDL for each stream */
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), snd_dma_pci_data(chip->pci),
BDL_SIZE, &chip->bdl); BDL_SIZE, &chip->azx_dev[i].bdl);
if (err < 0) { if (err < 0) {
snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
goto errout; goto errout;
} }
}
/* allocate memory for the position buffer */ /* allocate memory for the position buffer */
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), snd_dma_pci_data(chip->pci),
......
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