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

Commit deadff16 authored by Wu Fengguang's avatar Wu Fengguang Committed by Takashi Iwai
Browse files

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: default avatarWu Fengguang <fengguang.wu@intel.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent ce577e8c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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) {
+1 −1
Original line number Diff line number Diff line
@@ -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 */
+54 −22
Original line number Diff line number Diff line
@@ -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,6 +604,7 @@ 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);
@@ -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;