aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/hda/hda_intel.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-12-20 11:17:17 +0100
committerTakashi Iwai <tiwai@suse.de>2013-01-12 08:33:56 +0100
commit3bcce5c0d931bf623adc5974200e4d7636b10bef (patch)
tree0174be815bfcf29033effad302171275756dbab9 /sound/pci/hda/hda_intel.c
parentaa88a3553eebdcc3ce6801aabb4ed0223bfa198e (diff)
ALSA: hda - Check CORB overflow
Add an overflow check of CORB in HD-audio controller and codec drivers so that flood of sequential writes would work properly. In the controller side, add a check of CORB read-pointer to make returning -EAGAIN when it's full. Meanwhile in the codec side, when -EAGAIN error is received, it retries the write after flushing the pending verbs (calling get_response() essentially does it). Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_intel.c')
-rw-r--r--sound/pci/hda/hda_intel.c11
1 files changed, 9 insertions, 2 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 0b6aebacc56..0430436b003 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -797,7 +797,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
{
struct azx *chip = bus->private_data;
unsigned int addr = azx_command_addr(val);
- unsigned int wp;
+ unsigned int wp, rp;
spin_lock_irq(&chip->reg_lock);
@@ -806,11 +806,18 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
if (wp == 0xffff) {
/* something wrong, controller likely turned to D3 */
spin_unlock_irq(&chip->reg_lock);
- return -1;
+ return -EIO;
}
wp++;
wp %= ICH6_MAX_CORB_ENTRIES;
+ rp = azx_readw(chip, CORBRP);
+ if (wp == rp) {
+ /* oops, it's full */
+ spin_unlock_irq(&chip->reg_lock);
+ return -EAGAIN;
+ }
+
chip->rirb.cmds[addr]++;
chip->corb.buf[wp] = cpu_to_le32(val);
azx_writel(chip, CORBWP, wp);