Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+71 −43
Original line number Diff line number Diff line
@@ -206,8 +206,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define MAX_AZX_DEV		16

/* max number of fragments - we may use more if allocating more pages for BDL */
#define BDL_SIZE		PAGE_ALIGN(8192)
#define AZX_MAX_FRAG		(BDL_SIZE / (MAX_AZX_DEV * 16))
#define BDL_SIZE		4096
#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 */
#define AZX_MAX_BUF_SIZE	(1024*1024*1024)
/* max number of PCM devics per card */
@@ -282,12 +283,10 @@ enum {
 */

struct azx_dev {
	u32 *bdl;		/* virtual address of the BDL */
	dma_addr_t bdl_addr;	/* physical address of the BDL */
	struct snd_dma_buffer bdl; /* BDL buffer */
	u32 *posbuf;		/* position buffer pointer */

	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 fifo_size;	/* FIFO size */

@@ -358,8 +357,7 @@ struct azx {
	struct azx_rb corb;
	struct azx_rb rirb;

	/* BDL, CORB/RIRB and position buffers */
	struct snd_dma_buffer bdl;
	/* CORB/RIRB and position buffers */
	struct snd_dma_buffer rb;
	struct snd_dma_buffer posbuf;

@@ -962,30 +960,57 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
/*
 * 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;
	dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr;
	int idx;
	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
	u32 *bdl;
	int i, ofs, periods, period_bytes;

	/* reset BDL address */
	azx_sd_writel(azx_dev, SD_BDLPL, 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 */
	for (idx = 0; idx < azx_dev->frags; idx++) {
		unsigned int off = idx << 2; /* 4 dword step */
		dma_addr_t addr = dma_addr + idx * azx_dev->fragsize;
	bdl = (u32 *)azx_dev->bdl.area;
	ofs = 0;
	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 */
		bdl[off] = cpu_to_le32((u32)addr);
		bdl[off+1] = cpu_to_le32(upper_32bit(addr));

			bdl[0] = cpu_to_le32((u32)addr);
			bdl[1] = cpu_to_le32(upper_32bit(addr));
			/* program the size field of the BDL entry */
		bdl[off+2] = cpu_to_le32(azx_dev->fragsize);

		/* program the IOC to enable interrupt when buffer completes */
		bdl[off+3] = cpu_to_le32(0x01);
			size = PAGE_SIZE - (ofs % PAGE_SIZE);
			if (rest < size)
				size = rest;
			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)

	/* program the 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 */
	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 */
	if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
@@ -1272,8 +1297,6 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
	struct snd_pcm_runtime *runtime = substream->runtime;

	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,
							 runtime->channels,
							 runtime->format,
@@ -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, "
		    "format=0x%x\n",
		    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);
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
@@ -1375,6 +1399,7 @@ static struct snd_pcm_ops azx_pcm_ops = {
	.prepare = azx_pcm_prepare,
	.trigger = azx_pcm_trigger,
	.pointer = azx_pcm_pointer,
	.page = snd_pcm_sgbuf_ops_page,
};

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,
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
	if (cpcm->stream[1].substreams)
		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),
					      1024 * 64, 1024 * 1024);
	chip->pcm[cpcm->device] = pcm;
@@ -1507,10 +1532,7 @@ static int __devinit azx_init_stream(struct azx *chip)
	 * and initialize
	 */
	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];
		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);
		/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
		azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
@@ -1646,8 +1668,9 @@ static int azx_resume(struct pci_dev *pci)
 */
static int azx_free(struct azx *chip)
{
	if (chip->initialized) {
	int i;

	if (chip->initialized) {
		for (i = 0; i < chip->num_streams; i++)
			azx_stream_stop(chip, &chip->azx_dev[i]);
		azx_stop_chip(chip);
@@ -1662,8 +1685,11 @@ static int azx_free(struct azx *chip)
	if (chip->remap_addr)
		iounmap(chip->remap_addr);

	if (chip->bdl.area)
		snd_dma_free_pages(&chip->bdl);
	if (chip->azx_dev) {
		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)
		snd_dma_free_pages(&chip->rb);
	if (chip->posbuf.area)
@@ -1745,7 +1771,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
				struct azx **rchip)
{
	struct azx *chip;
	int err;
	int i, err;
	unsigned short gcap;
	static struct snd_device_ops ops = {
		.dev_free = azx_dev_free,
@@ -1857,14 +1883,16 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
		goto errout;
	}

	for (i = 0; i < chip->num_streams; i++) {
		/* allocate memory for the BDL for each stream */
		err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
					  snd_dma_pci_data(chip->pci),
				  BDL_SIZE, &chip->bdl);
					  BDL_SIZE, &chip->azx_dev[i].bdl);
		if (err < 0) {
			snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
			goto errout;
		}
	}
	/* allocate memory for the position buffer */
	err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
				  snd_dma_pci_data(chip->pci),