From 1f93a7dfd1f8d5ff7a5c53246c7534fe2332d6f4 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 02:46:07 +0000 Subject: [PATCH] add audio --- kernel/sound/hda/hdac_stream.c | 109 ++++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 85 insertions(+), 24 deletions(-) diff --git a/kernel/sound/hda/hdac_stream.c b/kernel/sound/hda/hdac_stream.c index eee4223..ce77a53 100644 --- a/kernel/sound/hda/hdac_stream.c +++ b/kernel/sound/hda/hdac_stream.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HD-audio stream operations */ @@ -11,6 +12,40 @@ #include <sound/hdaudio.h> #include <sound/hda_register.h> #include "trace.h" + +/** + * snd_hdac_get_stream_stripe_ctl - get stripe control value + * @bus: HD-audio core bus + * @substream: PCM substream + */ +int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int channels = runtime->channels, + rate = runtime->rate, + bits_per_sample = runtime->sample_bits, + max_sdo_lines, value, sdo_line; + + /* T_AZA_GCAP_NSDO is 1:2 bitfields in GCAP */ + max_sdo_lines = snd_hdac_chip_readl(bus, GCAP) & AZX_GCAP_NSDO; + + /* following is from HD audio spec */ + for (sdo_line = max_sdo_lines; sdo_line > 0; sdo_line >>= 1) { + if (rate > 48000) + value = (channels * bits_per_sample * + (rate / 48000)) / sdo_line; + else + value = (channels * bits_per_sample) / sdo_line; + + if (value >= bus->sdo_limit) + break; + } + + /* stripe value: 0 for 1SDO, 1 for 2SDO, 2 for 4SDO lines */ + return sdo_line >> 1; +} +EXPORT_SYMBOL_GPL(snd_hdac_get_stream_stripe_ctl); /** * snd_hdac_stream_init - initialize each stream (aka device) @@ -48,6 +83,7 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) { struct hdac_bus *bus = azx_dev->bus; + int stripe_ctl; trace_snd_hdac_stream_start(bus, azx_dev); @@ -56,7 +92,18 @@ azx_dev->start_wallclk -= azx_dev->period_wallclk; /* enable SIE */ - snd_hdac_chip_updatel(bus, INTCTL, 0, 1 << azx_dev->index); + snd_hdac_chip_updatel(bus, INTCTL, + 1 << azx_dev->index, + 1 << azx_dev->index); + /* set stripe control */ + if (azx_dev->stripe) { + if (azx_dev->substream) + 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, + stripe_ctl); + } /* set DMA start and interrupt mask */ snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK); @@ -73,6 +120,8 @@ 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); @@ -101,8 +150,11 @@ { unsigned char val; int timeout; + int dma_run_state; 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); udelay(3); @@ -113,6 +165,10 @@ if (val) break; } while (--timeout); + + if (azx_dev->bus->dma_stop_delay && dma_run_state) + udelay(azx_dev->bus->dma_stop_delay); + val &= ~SD_CTL_STREAM_RESET; snd_hdac_stream_writeb(azx_dev, SD_CTL, val); udelay(3); @@ -183,11 +239,7 @@ /* set the interrupt enable bits in the descriptor control register */ snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK); - if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) - azx_dev->fifo_size = - snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1; - else - azx_dev->fifo_size = 0; + azx_dev->fifo_size = snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1; /* when LPIB delay correction gives a small negative value, * we ignore it; currently set the threshold statically to @@ -244,6 +296,7 @@ int key = (substream->pcm->device << 16) | (substream->number << 2) | (substream->stream + 1); + spin_lock_irq(&bus->reg_lock); list_for_each_entry(azx_dev, &bus->stream_list, list) { if (azx_dev->direction != substream->stream) continue; @@ -257,13 +310,12 @@ res = azx_dev; } if (res) { - spin_lock_irq(&bus->reg_lock); res->opened = 1; res->running = 0; res->assigned_key = key; res->substream = substream; - spin_unlock_irq(&bus->reg_lock); } + spin_unlock_irq(&bus->reg_lock); return res; } EXPORT_SYMBOL_GPL(snd_hdac_stream_assign); @@ -545,7 +597,9 @@ /** * snd_hdac_stream_sync_trigger - turn on/off stream sync register * @azx_dev: HD-audio core stream (master stream) + * @set: true = set, false = clear * @streams: bit flags of streams to sync + * @reg: the stream sync register address */ void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set, unsigned int streams, unsigned int reg) @@ -584,20 +638,27 @@ nwait = 0; i = 0; list_for_each_entry(s, &bus->stream_list, list) { - if (streams & (1 << i)) { - if (start) { - /* check FIFO gets ready */ - if (!(snd_hdac_stream_readb(s, SD_STS) & - SD_STS_FIFO_READY)) - nwait++; - } else { - /* check RUN bit is cleared */ - if (snd_hdac_stream_readb(s, SD_CTL) & - SD_CTL_DMA_START) - nwait++; + if (!(streams & (1 << i++))) + continue; + + if (start) { + /* check FIFO gets ready */ + if (!(snd_hdac_stream_readb(s, SD_STS) & + SD_STS_FIFO_READY)) + nwait++; + } else { + /* check RUN bit is cleared */ + if (snd_hdac_stream_readb(s, SD_CTL) & + SD_CTL_DMA_START) { + nwait++; + /* + * Perform stream reset if DMA RUN + * bit not cleared within given timeout + */ + if (timeout == 1) + snd_hdac_stream_reset(s); } } - i++; } if (!nwait) break; @@ -634,8 +695,8 @@ azx_dev->locked = true; spin_unlock_irq(&bus->reg_lock); - err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG, - byte_size, bufp); + err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, + byte_size, bufp); if (err < 0) goto err_alloc; @@ -661,7 +722,7 @@ return azx_dev->stream_tag; error: - bus->io_ops->dma_free_pages(bus, bufp); + snd_dma_free_pages(bufp); err_alloc: spin_lock_irq(&bus->reg_lock); azx_dev->locked = false; @@ -708,7 +769,7 @@ azx_dev->period_bytes = 0; azx_dev->format_val = 0; - bus->io_ops->dma_free_pages(bus, dmab); + snd_dma_free_pages(dmab); dmab->area = NULL; spin_lock_irq(&bus->reg_lock); -- Gitblit v1.6.2