Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 99093
b: refs/heads/master
c: 675f25d
h: refs/heads/master
i:
  99091: cd1a6ef
v: v3
  • Loading branch information
Takashi Iwai authored and Jaroslav Kysela committed Jun 13, 2008
1 parent 7d65af2 commit 8e661e5
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 33 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 0a1b42db4bf9db233d1f29795086a9526e84c845
refs/heads/master: 675f25d4d3ff6501cbce608bcc2333a56ec4c105
117 changes: 85 additions & 32 deletions trunk/sound/pci/hda/hda_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ static int position_fix[SNDRV_CARDS];
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int single_cmd;
static int enable_msi;
static int bdl_pos_adj = 1;

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
Expand All @@ -77,6 +78,8 @@ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
"(for debugging only).");
module_param(enable_msi, int, 0444);
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
module_param(bdl_pos_adj, int, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset");

#ifdef CONFIG_SND_HDA_POWER_SAVE
/* power_save option is defined in hda_codec.c */
Expand Down Expand Up @@ -309,7 +312,8 @@ struct azx_dev {

unsigned int opened :1;
unsigned int running :1;
unsigned int irq_pending: 1;
unsigned int irq_pending :1;
unsigned int irq_ignore :1;
};

/* CORB/RIRB */
Expand Down Expand Up @@ -943,6 +947,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
if (!azx_dev->substream || !azx_dev->running)
continue;
/* ignore the first dummy IRQ (due to pos_adj) */
if (azx_dev->irq_ignore) {
azx_dev->irq_ignore = 0;
continue;
}
/* check whether this IRQ is really acceptable */
if (azx_position_ok(chip, azx_dev)) {
azx_dev->irq_pending = 0;
Expand Down Expand Up @@ -976,15 +985,54 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
}


/*
* set up a BDL entry
*/
static int setup_bdle(struct snd_pcm_substream *substream,
struct azx_dev *azx_dev, u32 **bdlp,
int ofs, int size, int with_ioc)
{
struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
u32 *bdl = *bdlp;

while (size > 0) {
dma_addr_t addr;
int chunk;

if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
return -EINVAL;

addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
/* program the address field of the BDL entry */
bdl[0] = cpu_to_le32((u32)addr);
bdl[1] = cpu_to_le32(upper_32bit(addr));
/* program the size field of the BDL entry */
chunk = PAGE_SIZE - (ofs % PAGE_SIZE);
if (size < chunk)
chunk = size;
bdl[2] = cpu_to_le32(chunk);
/* program the IOC to enable interrupt
* only when the whole fragment is processed
*/
size -= chunk;
bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
bdl += 4;
azx_dev->frags++;
ofs += chunk;
}
*bdlp = bdl;
return ofs;
}

/*
* set up BDL entries
*/
static int azx_setup_periods(struct snd_pcm_substream *substream,
struct azx_dev *azx_dev)
{
struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
u32 *bdl;
int i, ofs, periods, period_bytes;
int pos_adj = 0;

/* reset BDL address */
azx_sd_writel(azx_dev, SD_BDLPL, 0);
Expand All @@ -998,39 +1046,44 @@ static int azx_setup_periods(struct snd_pcm_substream *substream,
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;
azx_dev->irq_ignore = 0;
if (bdl_pos_adj > 0) {
struct snd_pcm_runtime *runtime = substream->runtime;
pos_adj = (bdl_pos_adj * runtime->rate + 47999) / 48000;
if (!pos_adj)
pos_adj = 1;
pos_adj = frames_to_bytes(runtime, pos_adj);
if (pos_adj >= period_bytes) {
snd_printk(KERN_WARNING "Too big adjustment %d\n",
bdl_pos_adj);
pos_adj = 0;
} else {
ofs = setup_bdle(substream, azx_dev,
&bdl, ofs, pos_adj, 1);
if (ofs < 0)
goto error;
azx_dev->irq_ignore = 1;
}
rest = period_bytes;
do {
dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
/* program the address field of the BDL entry */
bdl[0] = cpu_to_le32((u32)addr);
bdl[1] = cpu_to_le32(upper_32bit(addr));
/* program the size field of the BDL entry */
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);
}
for (i = 0; i < periods; i++) {
if (i == periods - 1 && pos_adj)
ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
period_bytes - pos_adj, 0);
else
ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
period_bytes, 1);
if (ofs < 0)
goto error;
}
return 0;

error:
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;
}

/*
Expand Down

0 comments on commit 8e661e5

Please sign in to comment.