Commit 0ceea685 authored by Pu Wen's avatar Pu Wen
Browse files

ALSA: hda: Fix single byte write failure issue for Hygon

hygon inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I92NMX


CVE: NA

---------------------------

On Hygon family 18h model 5h controller, some registers such as
GCTL, SD_CTL and SD_CTL_3B should be accessed in dword, or the
writing will fail.

Signed-off-by: default avatarPu Wen <puwen@hygon.cn>
parent 77faee92
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2610,6 +2610,7 @@
#define PCI_VENDOR_ID_ZHAOXIN		0x1d17

#define PCI_VENDOR_ID_HYGON		0x1d94
#define PCI_DEVICE_ID_HYGON_18H_M05H_HDA	0x14a9
#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3	0x14b3

#define PCI_VENDOR_ID_HXT		0x1dbf
+1 −0
Original line number Diff line number Diff line
@@ -342,6 +342,7 @@ struct hdac_bus {
	bool corbrp_self_clear:1;	/* CORBRP clears itself after reset */
	bool polling_mode:1;
	bool needs_damn_long_delay:1;
	bool hygon_dword_access:1;

	int poll_count;

+12 −3
Original line number Diff line number Diff line
@@ -407,6 +407,9 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
{
	unsigned long timeout;

	if (bus->hygon_dword_access)
		snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
	else
		snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);

	timeout = jiffies + msecs_to_jiffies(100);
@@ -472,9 +475,15 @@ static void azx_int_disable(struct hdac_bus *bus)

	/* disable interrupts in stream descriptor */
	list_for_each_entry(azx_dev, &bus->stream_list, list)
		if (bus->hygon_dword_access)
			snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_INT_MASK, 0);
		else
			snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);

	/* disable SIE for all streams */
	if (bus->hygon_dword_access)
		snd_hdac_chip_writel(bus, INTCTL, 0);
	else
		snd_hdac_chip_writeb(bus, INTCTL, 0);

	/* disable controller CIE and GIE */
+36 −9
Original line number Diff line number Diff line
@@ -101,10 +101,18 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
			stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
		else
			stripe_ctl = 0;
		if (bus->hygon_dword_access)
			snd_hdac_stream_updatel(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
					stripe_ctl);
		else
			snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
					stripe_ctl);
	}
	/* set DMA start and interrupt mask */
	if (bus->hygon_dword_access)
		snd_hdac_stream_updatel(azx_dev, SD_CTL,
				0, SD_CTL_DMA_START | SD_INT_MASK);
	else
		snd_hdac_stream_updateb(azx_dev, SD_CTL,
				0, SD_CTL_DMA_START | SD_INT_MASK);
	azx_dev->running = true;
@@ -117,11 +125,21 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
 */
void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
{
	struct hdac_bus *bus = azx_dev->bus;

	if (bus->hygon_dword_access) {
		snd_hdac_stream_updatel(azx_dev, SD_CTL,
					SD_CTL_DMA_START | SD_INT_MASK, 0);
		snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
		if (azx_dev->stripe)
			snd_hdac_stream_updatel(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
	} else {
		snd_hdac_stream_updateb(azx_dev, SD_CTL,
					SD_CTL_DMA_START | SD_INT_MASK, 0);
		snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
		if (azx_dev->stripe)
			snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
	}
	azx_dev->running = false;
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_clear);
@@ -178,12 +196,17 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
	unsigned char val;
	int timeout;
	int dma_run_state;
	struct hdac_bus *bus = azx_dev->bus;

	snd_hdac_stream_clear(azx_dev);

	dma_run_state = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START;

	if (bus->hygon_dword_access)
		snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
	else
		snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);

	udelay(3);
	timeout = 300;
	do {
@@ -197,7 +220,11 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
		udelay(azx_dev->bus->dma_stop_delay);

	val &= ~SD_CTL_STREAM_RESET;
	if (bus->hygon_dword_access)
		snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0);
	else
		snd_hdac_stream_writeb(azx_dev, SD_CTL, val);

	udelay(3);

	timeout = 300;
+4 −0
Original line number Diff line number Diff line
@@ -1950,6 +1950,10 @@ static int azx_first_init(struct azx *chip)
	if (chip->driver_type == AZX_DRIVER_ZXHDMI)
		azx_init_chip_zx(chip);

	if (chip->driver_type == AZX_DRIVER_HYGON &&
	    chip->pci->device == PCI_DEVICE_ID_HYGON_18H_M05H_HDA)
		bus->hygon_dword_access = 1;

	err = pci_request_regions(pci, "ICH HD audio");
	if (err < 0)
		return err;