diff options
author | Wu Fengguang <fengguang.wu@intel.com> | 2009-08-01 06:45:16 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-08-03 02:26:13 -0400 |
commit | deadff1665491afce124a8ff83f00f784161f660 (patch) | |
tree | b0de54cef8d75623f5839651331b31018e1ea217 /sound | |
parent | ce577e8cf5ddb4216553c9d563a9835d6de70ffa (diff) |
ALSA: hda: track CIRB/CORB command/response states for each codec
Recently we hit a bug in our dev board, whose HDMI codec#3 may emit
redundant/spurious responses, which were then taken as responses to
command for another onboard Realtek codec#2, and mess up both codecs.
Extend the azx_rb.cmds and azx_rb.res to array and track each codec's
commands/responses separately. This helps keep good codec safe from
broken ones.
Signed-off-by: Wu Fengguang <fengguang.wu@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 2 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 2 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 76 |
3 files changed, 56 insertions, 24 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 88480c0c58a0..c7df01b72cac 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -174,7 +174,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, | |||
174 | mutex_lock(&bus->cmd_mutex); | 174 | mutex_lock(&bus->cmd_mutex); |
175 | err = bus->ops.command(bus, cmd); | 175 | err = bus->ops.command(bus, cmd); |
176 | if (!err && res) | 176 | if (!err && res) |
177 | *res = bus->ops.get_response(bus); | 177 | *res = bus->ops.get_response(bus, codec->addr); |
178 | mutex_unlock(&bus->cmd_mutex); | 178 | mutex_unlock(&bus->cmd_mutex); |
179 | snd_hda_power_down(codec); | 179 | snd_hda_power_down(codec); |
180 | if (res && *res == -1 && bus->rirb_error) { | 180 | if (res && *res == -1 && bus->rirb_error) { |
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index cad79efaabc9..1b75f28ed092 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -568,7 +568,7 @@ struct hda_bus_ops { | |||
568 | /* send a single command */ | 568 | /* send a single command */ |
569 | int (*command)(struct hda_bus *bus, unsigned int cmd); | 569 | int (*command)(struct hda_bus *bus, unsigned int cmd); |
570 | /* get a response from the last command */ | 570 | /* get a response from the last command */ |
571 | unsigned int (*get_response)(struct hda_bus *bus); | 571 | unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr); |
572 | /* free the private data */ | 572 | /* free the private data */ |
573 | void (*private_free)(struct hda_bus *); | 573 | void (*private_free)(struct hda_bus *); |
574 | /* attach a PCM stream */ | 574 | /* attach a PCM stream */ |
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 77c1b840ca8b..19e67a1b6026 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c | |||
@@ -253,7 +253,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; | |||
253 | 253 | ||
254 | /* STATESTS int mask: S3,SD2,SD1,SD0 */ | 254 | /* STATESTS int mask: S3,SD2,SD1,SD0 */ |
255 | #define AZX_MAX_CODECS 4 | 255 | #define AZX_MAX_CODECS 4 |
256 | #define STATESTS_INT_MASK 0x0f | 256 | #define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) |
257 | 257 | ||
258 | /* SD_CTL bits */ | 258 | /* SD_CTL bits */ |
259 | #define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ | 259 | #define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ |
@@ -361,8 +361,8 @@ struct azx_rb { | |||
361 | dma_addr_t addr; /* physical address of CORB/RIRB buffer */ | 361 | dma_addr_t addr; /* physical address of CORB/RIRB buffer */ |
362 | /* for RIRB */ | 362 | /* for RIRB */ |
363 | unsigned short rp, wp; /* read/write pointers */ | 363 | unsigned short rp, wp; /* read/write pointers */ |
364 | int cmds; /* number of pending requests */ | 364 | int cmds[AZX_MAX_CODECS]; /* number of pending requests */ |
365 | u32 res; /* last read value */ | 365 | u32 res[AZX_MAX_CODECS]; /* last read value */ |
366 | }; | 366 | }; |
367 | 367 | ||
368 | struct azx { | 368 | struct azx { |
@@ -531,7 +531,8 @@ static void azx_init_cmd_io(struct azx *chip) | |||
531 | /* RIRB set up */ | 531 | /* RIRB set up */ |
532 | chip->rirb.addr = chip->rb.addr + 2048; | 532 | chip->rirb.addr = chip->rb.addr + 2048; |
533 | chip->rirb.buf = (u32 *)(chip->rb.area + 2048); | 533 | chip->rirb.buf = (u32 *)(chip->rb.area + 2048); |
534 | chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0; | 534 | chip->rirb.wp = chip->rirb.rp = 0; |
535 | memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds)); | ||
535 | azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr); | 536 | azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr); |
536 | azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr)); | 537 | azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr)); |
537 | 538 | ||
@@ -552,10 +553,35 @@ static void azx_free_cmd_io(struct azx *chip) | |||
552 | azx_writeb(chip, CORBCTL, 0); | 553 | azx_writeb(chip, CORBCTL, 0); |
553 | } | 554 | } |
554 | 555 | ||
556 | static unsigned int azx_command_addr(u32 cmd) | ||
557 | { | ||
558 | unsigned int addr = cmd >> 28; | ||
559 | |||
560 | if (addr >= AZX_MAX_CODECS) { | ||
561 | snd_BUG(); | ||
562 | addr = 0; | ||
563 | } | ||
564 | |||
565 | return addr; | ||
566 | } | ||
567 | |||
568 | static unsigned int azx_response_addr(u32 res) | ||
569 | { | ||
570 | unsigned int addr = res & 0xf; | ||
571 | |||
572 | if (addr >= AZX_MAX_CODECS) { | ||
573 | snd_BUG(); | ||
574 | addr = 0; | ||
575 | } | ||
576 | |||
577 | return addr; | ||
578 | } | ||
579 | |||
555 | /* send a command */ | 580 | /* send a command */ |
556 | static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) | 581 | static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) |
557 | { | 582 | { |
558 | struct azx *chip = bus->private_data; | 583 | struct azx *chip = bus->private_data; |
584 | unsigned int addr = azx_command_addr(val); | ||
559 | unsigned int wp; | 585 | unsigned int wp; |
560 | 586 | ||
561 | /* add command to corb */ | 587 | /* add command to corb */ |
@@ -564,7 +590,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) | |||
564 | wp %= ICH6_MAX_CORB_ENTRIES; | 590 | wp %= ICH6_MAX_CORB_ENTRIES; |
565 | 591 | ||
566 | spin_lock_irq(&chip->reg_lock); | 592 | spin_lock_irq(&chip->reg_lock); |
567 | chip->rirb.cmds++; | 593 | chip->rirb.cmds[addr]++; |
568 | chip->corb.buf[wp] = cpu_to_le32(val); | 594 | chip->corb.buf[wp] = cpu_to_le32(val); |
569 | azx_writel(chip, CORBWP, wp); | 595 | azx_writel(chip, CORBWP, wp); |
570 | spin_unlock_irq(&chip->reg_lock); | 596 | spin_unlock_irq(&chip->reg_lock); |
@@ -578,13 +604,14 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) | |||
578 | static void azx_update_rirb(struct azx *chip) | 604 | static void azx_update_rirb(struct azx *chip) |
579 | { | 605 | { |
580 | unsigned int rp, wp; | 606 | unsigned int rp, wp; |
607 | unsigned int addr; | ||
581 | u32 res, res_ex; | 608 | u32 res, res_ex; |
582 | 609 | ||
583 | wp = azx_readb(chip, RIRBWP); | 610 | wp = azx_readb(chip, RIRBWP); |
584 | if (wp == chip->rirb.wp) | 611 | if (wp == chip->rirb.wp) |
585 | return; | 612 | return; |
586 | chip->rirb.wp = wp; | 613 | chip->rirb.wp = wp; |
587 | 614 | ||
588 | while (chip->rirb.rp != wp) { | 615 | while (chip->rirb.rp != wp) { |
589 | chip->rirb.rp++; | 616 | chip->rirb.rp++; |
590 | chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES; | 617 | chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES; |
@@ -592,18 +619,20 @@ static void azx_update_rirb(struct azx *chip) | |||
592 | rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */ | 619 | rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */ |
593 | res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]); | 620 | res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]); |
594 | res = le32_to_cpu(chip->rirb.buf[rp]); | 621 | res = le32_to_cpu(chip->rirb.buf[rp]); |
622 | addr = azx_response_addr(res_ex); | ||
595 | if (res_ex & ICH6_RIRB_EX_UNSOL_EV) | 623 | if (res_ex & ICH6_RIRB_EX_UNSOL_EV) |
596 | snd_hda_queue_unsol_event(chip->bus, res, res_ex); | 624 | snd_hda_queue_unsol_event(chip->bus, res, res_ex); |
597 | else if (chip->rirb.cmds) { | 625 | else if (chip->rirb.cmds[addr]) { |
598 | chip->rirb.res = res; | 626 | chip->rirb.res[addr] = res; |
599 | smp_wmb(); | 627 | smp_wmb(); |
600 | chip->rirb.cmds--; | 628 | chip->rirb.cmds[addr]--; |
601 | } | 629 | } |
602 | } | 630 | } |
603 | } | 631 | } |
604 | 632 | ||
605 | /* receive a response */ | 633 | /* receive a response */ |
606 | static unsigned int azx_rirb_get_response(struct hda_bus *bus) | 634 | static unsigned int azx_rirb_get_response(struct hda_bus *bus, |
635 | unsigned int addr) | ||
607 | { | 636 | { |
608 | struct azx *chip = bus->private_data; | 637 | struct azx *chip = bus->private_data; |
609 | unsigned long timeout; | 638 | unsigned long timeout; |
@@ -616,10 +645,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) | |||
616 | azx_update_rirb(chip); | 645 | azx_update_rirb(chip); |
617 | spin_unlock_irq(&chip->reg_lock); | 646 | spin_unlock_irq(&chip->reg_lock); |
618 | } | 647 | } |
619 | if (!chip->rirb.cmds) { | 648 | if (!chip->rirb.cmds[addr]) { |
620 | smp_rmb(); | 649 | smp_rmb(); |
621 | bus->rirb_error = 0; | 650 | bus->rirb_error = 0; |
622 | return chip->rirb.res; /* the last value */ | 651 | return chip->rirb.res[addr]; /* the last value */ |
623 | } | 652 | } |
624 | if (time_after(jiffies, timeout)) | 653 | if (time_after(jiffies, timeout)) |
625 | break; | 654 | break; |
@@ -692,7 +721,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) | |||
692 | */ | 721 | */ |
693 | 722 | ||
694 | /* receive a response */ | 723 | /* receive a response */ |
695 | static int azx_single_wait_for_response(struct azx *chip) | 724 | static int azx_single_wait_for_response(struct azx *chip, unsigned int addr) |
696 | { | 725 | { |
697 | int timeout = 50; | 726 | int timeout = 50; |
698 | 727 | ||
@@ -700,7 +729,7 @@ static int azx_single_wait_for_response(struct azx *chip) | |||
700 | /* check IRV busy bit */ | 729 | /* check IRV busy bit */ |
701 | if (azx_readw(chip, IRS) & ICH6_IRS_VALID) { | 730 | if (azx_readw(chip, IRS) & ICH6_IRS_VALID) { |
702 | /* reuse rirb.res as the response return value */ | 731 | /* reuse rirb.res as the response return value */ |
703 | chip->rirb.res = azx_readl(chip, IR); | 732 | chip->rirb.res[addr] = azx_readl(chip, IR); |
704 | return 0; | 733 | return 0; |
705 | } | 734 | } |
706 | udelay(1); | 735 | udelay(1); |
@@ -708,7 +737,7 @@ static int azx_single_wait_for_response(struct azx *chip) | |||
708 | if (printk_ratelimit()) | 737 | if (printk_ratelimit()) |
709 | snd_printd(SFX "get_response timeout: IRS=0x%x\n", | 738 | snd_printd(SFX "get_response timeout: IRS=0x%x\n", |
710 | azx_readw(chip, IRS)); | 739 | azx_readw(chip, IRS)); |
711 | chip->rirb.res = -1; | 740 | chip->rirb.res[addr] = -1; |
712 | return -EIO; | 741 | return -EIO; |
713 | } | 742 | } |
714 | 743 | ||
@@ -716,6 +745,7 @@ static int azx_single_wait_for_response(struct azx *chip) | |||
716 | static int azx_single_send_cmd(struct hda_bus *bus, u32 val) | 745 | static int azx_single_send_cmd(struct hda_bus *bus, u32 val) |
717 | { | 746 | { |
718 | struct azx *chip = bus->private_data; | 747 | struct azx *chip = bus->private_data; |
748 | unsigned int addr = azx_command_addr(val); | ||
719 | int timeout = 50; | 749 | int timeout = 50; |
720 | 750 | ||
721 | bus->rirb_error = 0; | 751 | bus->rirb_error = 0; |
@@ -728,7 +758,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) | |||
728 | azx_writel(chip, IC, val); | 758 | azx_writel(chip, IC, val); |
729 | azx_writew(chip, IRS, azx_readw(chip, IRS) | | 759 | azx_writew(chip, IRS, azx_readw(chip, IRS) | |
730 | ICH6_IRS_BUSY); | 760 | ICH6_IRS_BUSY); |
731 | return azx_single_wait_for_response(chip); | 761 | return azx_single_wait_for_response(chip, addr); |
732 | } | 762 | } |
733 | udelay(1); | 763 | udelay(1); |
734 | } | 764 | } |
@@ -739,10 +769,11 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) | |||
739 | } | 769 | } |
740 | 770 | ||
741 | /* receive a response */ | 771 | /* receive a response */ |
742 | static unsigned int azx_single_get_response(struct hda_bus *bus) | 772 | static unsigned int azx_single_get_response(struct hda_bus *bus, |
773 | unsigned int addr) | ||
743 | { | 774 | { |
744 | struct azx *chip = bus->private_data; | 775 | struct azx *chip = bus->private_data; |
745 | return chip->rirb.res; | 776 | return chip->rirb.res[addr]; |
746 | } | 777 | } |
747 | 778 | ||
748 | /* | 779 | /* |
@@ -765,13 +796,14 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val) | |||
765 | } | 796 | } |
766 | 797 | ||
767 | /* get a response */ | 798 | /* get a response */ |
768 | static unsigned int azx_get_response(struct hda_bus *bus) | 799 | static unsigned int azx_get_response(struct hda_bus *bus, |
800 | unsigned int addr) | ||
769 | { | 801 | { |
770 | struct azx *chip = bus->private_data; | 802 | struct azx *chip = bus->private_data; |
771 | if (chip->single_cmd) | 803 | if (chip->single_cmd) |
772 | return azx_single_get_response(bus); | 804 | return azx_single_get_response(bus, addr); |
773 | else | 805 | else |
774 | return azx_rirb_get_response(bus); | 806 | return azx_rirb_get_response(bus, addr); |
775 | } | 807 | } |
776 | 808 | ||
777 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 809 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
@@ -1245,7 +1277,7 @@ static int probe_codec(struct azx *chip, int addr) | |||
1245 | 1277 | ||
1246 | chip->probing = 1; | 1278 | chip->probing = 1; |
1247 | azx_send_cmd(chip->bus, cmd); | 1279 | azx_send_cmd(chip->bus, cmd); |
1248 | res = azx_get_response(chip->bus); | 1280 | res = azx_get_response(chip->bus, addr); |
1249 | chip->probing = 0; | 1281 | chip->probing = 0; |
1250 | if (res == -1) | 1282 | if (res == -1) |
1251 | return -EIO; | 1283 | return -EIO; |