mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 23:00:53 +07:00
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>
This commit is contained in:
parent
ce577e8cf5
commit
deadff1665
@ -174,7 +174,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
|
||||
mutex_lock(&bus->cmd_mutex);
|
||||
err = bus->ops.command(bus, cmd);
|
||||
if (!err && res)
|
||||
*res = bus->ops.get_response(bus);
|
||||
*res = bus->ops.get_response(bus, codec->addr);
|
||||
mutex_unlock(&bus->cmd_mutex);
|
||||
snd_hda_power_down(codec);
|
||||
if (res && *res == -1 && bus->rirb_error) {
|
||||
|
@ -568,7 +568,7 @@ struct hda_bus_ops {
|
||||
/* send a single command */
|
||||
int (*command)(struct hda_bus *bus, unsigned int cmd);
|
||||
/* get a response from the last command */
|
||||
unsigned int (*get_response)(struct hda_bus *bus);
|
||||
unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr);
|
||||
/* free the private data */
|
||||
void (*private_free)(struct hda_bus *);
|
||||
/* attach a PCM stream */
|
||||
|
@ -253,7 +253,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
||||
|
||||
/* STATESTS int mask: S3,SD2,SD1,SD0 */
|
||||
#define AZX_MAX_CODECS 4
|
||||
#define STATESTS_INT_MASK 0x0f
|
||||
#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
|
||||
|
||||
/* SD_CTL bits */
|
||||
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
|
||||
@ -361,8 +361,8 @@ struct azx_rb {
|
||||
dma_addr_t addr; /* physical address of CORB/RIRB buffer */
|
||||
/* for RIRB */
|
||||
unsigned short rp, wp; /* read/write pointers */
|
||||
int cmds; /* number of pending requests */
|
||||
u32 res; /* last read value */
|
||||
int cmds[AZX_MAX_CODECS]; /* number of pending requests */
|
||||
u32 res[AZX_MAX_CODECS]; /* last read value */
|
||||
};
|
||||
|
||||
struct azx {
|
||||
@ -531,7 +531,8 @@ static void azx_init_cmd_io(struct azx *chip)
|
||||
/* RIRB set up */
|
||||
chip->rirb.addr = chip->rb.addr + 2048;
|
||||
chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
|
||||
chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0;
|
||||
chip->rirb.wp = chip->rirb.rp = 0;
|
||||
memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
|
||||
azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
|
||||
azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
|
||||
|
||||
@ -552,10 +553,35 @@ static void azx_free_cmd_io(struct azx *chip)
|
||||
azx_writeb(chip, CORBCTL, 0);
|
||||
}
|
||||
|
||||
static unsigned int azx_command_addr(u32 cmd)
|
||||
{
|
||||
unsigned int addr = cmd >> 28;
|
||||
|
||||
if (addr >= AZX_MAX_CODECS) {
|
||||
snd_BUG();
|
||||
addr = 0;
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static unsigned int azx_response_addr(u32 res)
|
||||
{
|
||||
unsigned int addr = res & 0xf;
|
||||
|
||||
if (addr >= AZX_MAX_CODECS) {
|
||||
snd_BUG();
|
||||
addr = 0;
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* send a command */
|
||||
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;
|
||||
|
||||
/* add command to corb */
|
||||
@ -564,7 +590,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
|
||||
wp %= ICH6_MAX_CORB_ENTRIES;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
chip->rirb.cmds++;
|
||||
chip->rirb.cmds[addr]++;
|
||||
chip->corb.buf[wp] = cpu_to_le32(val);
|
||||
azx_writel(chip, CORBWP, wp);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
@ -578,13 +604,14 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
|
||||
static void azx_update_rirb(struct azx *chip)
|
||||
{
|
||||
unsigned int rp, wp;
|
||||
unsigned int addr;
|
||||
u32 res, res_ex;
|
||||
|
||||
wp = azx_readb(chip, RIRBWP);
|
||||
if (wp == chip->rirb.wp)
|
||||
return;
|
||||
chip->rirb.wp = wp;
|
||||
|
||||
|
||||
while (chip->rirb.rp != wp) {
|
||||
chip->rirb.rp++;
|
||||
chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
|
||||
@ -592,18 +619,20 @@ static void azx_update_rirb(struct azx *chip)
|
||||
rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
|
||||
res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
|
||||
res = le32_to_cpu(chip->rirb.buf[rp]);
|
||||
addr = azx_response_addr(res_ex);
|
||||
if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
|
||||
snd_hda_queue_unsol_event(chip->bus, res, res_ex);
|
||||
else if (chip->rirb.cmds) {
|
||||
chip->rirb.res = res;
|
||||
else if (chip->rirb.cmds[addr]) {
|
||||
chip->rirb.res[addr] = res;
|
||||
smp_wmb();
|
||||
chip->rirb.cmds--;
|
||||
chip->rirb.cmds[addr]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* receive a response */
|
||||
static unsigned int azx_rirb_get_response(struct hda_bus *bus)
|
||||
static unsigned int azx_rirb_get_response(struct hda_bus *bus,
|
||||
unsigned int addr)
|
||||
{
|
||||
struct azx *chip = bus->private_data;
|
||||
unsigned long timeout;
|
||||
@ -616,10 +645,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
|
||||
azx_update_rirb(chip);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
}
|
||||
if (!chip->rirb.cmds) {
|
||||
if (!chip->rirb.cmds[addr]) {
|
||||
smp_rmb();
|
||||
bus->rirb_error = 0;
|
||||
return chip->rirb.res; /* the last value */
|
||||
return chip->rirb.res[addr]; /* the last value */
|
||||
}
|
||||
if (time_after(jiffies, timeout))
|
||||
break;
|
||||
@ -692,7 +721,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
|
||||
*/
|
||||
|
||||
/* receive a response */
|
||||
static int azx_single_wait_for_response(struct azx *chip)
|
||||
static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
|
||||
{
|
||||
int timeout = 50;
|
||||
|
||||
@ -700,7 +729,7 @@ static int azx_single_wait_for_response(struct azx *chip)
|
||||
/* check IRV busy bit */
|
||||
if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
|
||||
/* reuse rirb.res as the response return value */
|
||||
chip->rirb.res = azx_readl(chip, IR);
|
||||
chip->rirb.res[addr] = azx_readl(chip, IR);
|
||||
return 0;
|
||||
}
|
||||
udelay(1);
|
||||
@ -708,7 +737,7 @@ static int azx_single_wait_for_response(struct azx *chip)
|
||||
if (printk_ratelimit())
|
||||
snd_printd(SFX "get_response timeout: IRS=0x%x\n",
|
||||
azx_readw(chip, IRS));
|
||||
chip->rirb.res = -1;
|
||||
chip->rirb.res[addr] = -1;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -716,6 +745,7 @@ static int azx_single_wait_for_response(struct azx *chip)
|
||||
static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
|
||||
{
|
||||
struct azx *chip = bus->private_data;
|
||||
unsigned int addr = azx_command_addr(val);
|
||||
int timeout = 50;
|
||||
|
||||
bus->rirb_error = 0;
|
||||
@ -728,7 +758,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
|
||||
azx_writel(chip, IC, val);
|
||||
azx_writew(chip, IRS, azx_readw(chip, IRS) |
|
||||
ICH6_IRS_BUSY);
|
||||
return azx_single_wait_for_response(chip);
|
||||
return azx_single_wait_for_response(chip, addr);
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
@ -739,10 +769,11 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
|
||||
}
|
||||
|
||||
/* receive a response */
|
||||
static unsigned int azx_single_get_response(struct hda_bus *bus)
|
||||
static unsigned int azx_single_get_response(struct hda_bus *bus,
|
||||
unsigned int addr)
|
||||
{
|
||||
struct azx *chip = bus->private_data;
|
||||
return chip->rirb.res;
|
||||
return chip->rirb.res[addr];
|
||||
}
|
||||
|
||||
/*
|
||||
@ -765,13 +796,14 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
|
||||
}
|
||||
|
||||
/* get a response */
|
||||
static unsigned int azx_get_response(struct hda_bus *bus)
|
||||
static unsigned int azx_get_response(struct hda_bus *bus,
|
||||
unsigned int addr)
|
||||
{
|
||||
struct azx *chip = bus->private_data;
|
||||
if (chip->single_cmd)
|
||||
return azx_single_get_response(bus);
|
||||
return azx_single_get_response(bus, addr);
|
||||
else
|
||||
return azx_rirb_get_response(bus);
|
||||
return azx_rirb_get_response(bus, addr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
@ -1245,7 +1277,7 @@ static int probe_codec(struct azx *chip, int addr)
|
||||
|
||||
chip->probing = 1;
|
||||
azx_send_cmd(chip->bus, cmd);
|
||||
res = azx_get_response(chip->bus);
|
||||
res = azx_get_response(chip->bus, addr);
|
||||
chip->probing = 0;
|
||||
if (res == -1)
|
||||
return -EIO;
|
||||
|
Loading…
Reference in New Issue
Block a user