Commit 1360969d authored by Pu Wen's avatar Pu Wen
Browse files

ALSA: hda: Fix single byte writing issue for Hygon family 18h model 5h

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


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 87f4adc0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -350,6 +350,7 @@ struct hdac_bus {
	bool needs_damn_long_delay:1;
	bool not_use_interrupts:1;	/* prohibiting the RIRB IRQ */
	bool access_sdnctl_in_dword:1;	/* accessing the sdnctl register by dword */
	bool hygon_dword_access:1;

	int poll_count;

+8 −2
Original line number Diff line number Diff line
@@ -410,6 +410,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);
@@ -475,6 +478,9 @@ 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 & disable controller CIE and GIE */
+30 −9
Original line number Diff line number Diff line
@@ -146,11 +146,15 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev)
			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->access_sdnctl_in_dword)
	if (bus->access_sdnctl_in_dword || bus->hygon_dword_access)
		snd_hdac_stream_updatel(azx_dev, SD_CTL,
				0, SD_CTL_DMA_START | SD_INT_MASK);
	else
@@ -166,11 +170,21 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
 */
static 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;
}

@@ -225,11 +239,15 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
{
	unsigned char val;
	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);

	/* wait for hardware to report that the stream entered reset */
@@ -238,6 +256,9 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
	if (azx_dev->bus->dma_stop_delay && dma_run_state)
		udelay(azx_dev->bus->dma_stop_delay);

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

	/* wait for hardware to report that the stream is out of reset */
+4 −0
Original line number Diff line number Diff line
@@ -1916,6 +1916,10 @@ static int azx_first_init(struct azx *chip)
	if (chip->driver_type == AZX_DRIVER_ZXHDMI)
		azx_init_pci_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 = pcim_iomap_regions(pci, 1 << 0, "ICH HD audio");
	if (err < 0)
		return err;