Skip to content

Commit

Permalink
ALSA: hda: Fix single byte writing issue for Hygon family 18h model 5h
Browse files Browse the repository at this point in the history
Severity: Important

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: fuhao <[email protected]>
Signed-off-by: WangYuli <[email protected]>
  • Loading branch information
fuhao87 authored and Avenger-285714 committed May 11, 2024
1 parent db7d498 commit 9c1e59e
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 11 deletions.
1 change: 1 addition & 0 deletions include/sound/hdaudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
10 changes: 8 additions & 2 deletions sound/hda/hdac_controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,10 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
{
unsigned long timeout;

snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
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);
while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout))
Expand Down Expand Up @@ -475,7 +478,10 @@ static void azx_int_disable(struct hdac_bus *bus)

/* disable interrupts in stream descriptor */
list_for_each_entry(azx_dev, &bus->stream_list, list)
snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
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 */
snd_hdac_chip_writel(bus, INTCTL, 0);
Expand Down
39 changes: 30 additions & 9 deletions sound/hda/hdac_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
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
Expand All @@ -166,11 +170,21 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
*/
static void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
{
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);
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;
}

Expand Down Expand Up @@ -225,20 +239,27 @@ 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;

snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
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 */
snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, (val & SD_CTL_STREAM_RESET), 3, 300);

if (azx_dev->bus->dma_stop_delay && dma_run_state)
udelay(azx_dev->bus->dma_stop_delay);

snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0);
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 */
snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, !(val & SD_CTL_STREAM_RESET), 3, 300);
Expand Down
4 changes: 4 additions & 0 deletions sound/pci/hda/hda_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1880,6 +1880,10 @@ static int azx_first_init(struct azx *chip)
bus->access_sdnctl_in_dword = 1;
}

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;
Expand Down

0 comments on commit 9c1e59e

Please sign in to comment.