From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Divya Prakash Date: Thu, 7 Apr 2016 10:32:45 +0530 Subject: [PATCH] ALSA: hda: Enhance HD audio framework to support compress streams Introduce APIs for compress stream DMA assignment, BDL setup and buffer allocation. Change-Id: I8cf4c8b96367cacc131a304c758d0aef53010d25 Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- include/sound/compress_driver.h | 6 ++++ include/sound/hdaudio.h | 2 ++ include/sound/hdaudio_ext.h | 4 +++ sound/hda/ext/hdac_ext_stream.c | 43 +++++++++++++++++++++++++++ sound/hda/hdac_stream.c | 51 ++++++++++++++++++++++----------- 5 files changed, 89 insertions(+), 17 deletions(-) diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index 99855d1c7476..6a16156229f9 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -45,6 +45,11 @@ struct snd_compr_runtime { u64 total_bytes_transferred; wait_queue_head_t sleep; void *private_data; + /* -- DMA -- */ + unsigned char *dma_area; /* DMA area */ + dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ + size_t dma_bytes; /* size of DMA area */ + struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ }; /** @@ -71,6 +76,7 @@ struct snd_compr_stream { bool next_track; bool partial_drain; void *private_data; + struct snd_dma_buffer dma_buffer; }; /** diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 0aa0189017ba..5eee13a0c045 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -487,6 +487,7 @@ struct hdac_stream { struct snd_pcm_substream *substream; /* assigned substream, * set in PCM open */ + struct snd_compr_stream *stream; unsigned int format_val; /* format value to be set in the * controller and the codec */ @@ -500,6 +501,7 @@ struct hdac_stream { bool no_period_wakeup:1; bool locked:1; + unsigned long curr_pos; /* timestamp */ unsigned long start_wallclk; /* start + minimum wallclk */ unsigned long period_wallclk; /* wallclk for period */ diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index f34aced69ca8..2d93a039a286 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -88,6 +88,10 @@ void snd_hdac_link_free_all(struct hdac_bus *bus); struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); +struct hdac_ext_stream * +hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + int direction); void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type); void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index a835558ddbc9..4bd95f4b2234 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include /** * snd_hdac_ext_stream_init - initialize each stream (aka device) @@ -549,3 +551,44 @@ int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) return 0; } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); +struct hdac_ext_stream * +hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + int direction) +{ + struct hdac_ext_stream *res = NULL; + struct hdac_stream *stream = NULL; + struct hdac_bus *hbus = &ebus->bus; + + if (!hbus->ppcap) { + dev_err(hbus->dev, "stream type not supported\n"); + return NULL; + } + + list_for_each_entry(stream, &hbus->stream_list, list) { + struct hdac_ext_stream *hstream = container_of(stream, + struct hdac_ext_stream, + hstream); + if (stream->direction != direction) + continue; + + if (!stream->opened) { + if (!hstream->decoupled) + snd_hdac_ext_stream_decouple(ebus, + hstream, true); + res = hstream; + break; + } + } + if (res) { + spin_lock_irq(&hbus->reg_lock); + res->hstream.opened = 1; + res->hstream.running = 0; + res->hstream.stream = substream; + spin_unlock_irq(&hbus->reg_lock); + } + dev_dbg(hbus->dev, "Stream tag = %d, index = %d\n", + res->hstream.stream_tag, res->hstream.index); + return res; +} +EXPORT_SYMBOL_GPL(hdac_ext_host_stream_compr_assign); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index eee422390d8e..d3b1e22ac050 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "trace.h" /** @@ -363,11 +364,22 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; struct snd_pcm_substream *substream = azx_dev->substream; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_compr_stream *csubstream = azx_dev->stream; + struct snd_pcm_runtime *runtime = NULL; + struct snd_compr_runtime *cruntime = NULL; + struct snd_dma_buffer *dma_buffer_p = NULL; __le32 *bdl; int i, ofs, periods, period_bytes; int pos_adj, pos_align; + if (substream) { + runtime = substream->runtime; + dma_buffer_p = snd_pcm_get_dma_buf(substream); + } else if (csubstream) { + cruntime = csubstream->runtime; + dma_buffer_p = csubstream->runtime->dma_buffer_p; + } + /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); @@ -381,7 +393,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) azx_dev->frags = 0; pos_adj = bus->bdl_pos_adj; - if (!azx_dev->no_period_wakeup && pos_adj > 0) { + if (!azx_dev->no_period_wakeup && pos_adj > 0 && substream) { pos_align = pos_adj; pos_adj = (pos_adj * runtime->rate + 47999) / 48000; if (!pos_adj) @@ -395,8 +407,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) pos_adj); pos_adj = 0; } else { - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, + ofs = setup_bdle(bus, dma_buffer_p, azx_dev, &bdl, ofs, pos_adj, true); if (ofs < 0) goto error; @@ -406,14 +417,12 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) for (i = 0; i < periods; i++) { if (i == periods - 1 && pos_adj) - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes - pos_adj, 0); + ofs = setup_bdle(bus, dma_buffer_p, azx_dev, + &bdl, ofs, period_bytes - pos_adj, 0); else - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes, - !azx_dev->no_period_wakeup); + ofs = setup_bdle(bus, dma_buffer_p, azx_dev, + &bdl, ofs, period_bytes, + !azx_dev->no_period_wakeup); if (ofs < 0) goto error; } @@ -440,14 +449,21 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, unsigned int bufsize, period_bytes; struct snd_pcm_substream *substream = azx_dev->substream; - struct snd_pcm_runtime *runtime; + struct snd_compr_stream *csubstream = azx_dev->stream; + struct snd_pcm_runtime *runtime = NULL; + struct snd_compr_runtime *cruntime = NULL; int err; - if (!substream) + if (substream) { + runtime = substream->runtime; + bufsize = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + } else if (csubstream) { + cruntime = csubstream->runtime; + bufsize = cruntime->buffer_size; + period_bytes = cruntime->fragment_size; + } else return -EINVAL; - runtime = substream->runtime; - bufsize = snd_pcm_lib_buffer_bytes(substream); - period_bytes = snd_pcm_lib_period_bytes(substream); if (bufsize != azx_dev->bufsize || period_bytes != azx_dev->period_bytes || @@ -456,7 +472,8 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, azx_dev->bufsize = bufsize; azx_dev->period_bytes = period_bytes; azx_dev->format_val = format_val; - azx_dev->no_period_wakeup = runtime->no_period_wakeup; + if (substream) + azx_dev->no_period_wakeup = runtime->no_period_wakeup; err = snd_hdac_stream_setup_periods(azx_dev); if (err < 0) return err; -- https://clearlinux.org