[PATCH 0/5] This patch series enables DMA mode on Intel Keem Bay platform
v1: Initial patch version, which contains fix for S24_LE format and also enable DMA mode on Intel Keembay platform
Michael Sit Wei Hong (5): ASoC: Intel: KMB: Fix S24_LE configuration dt-bindings: sound: intel, keembay-i2s: Add info for device to use DMA ASoC: soc-generic-dmaengine-pcm: Add custom prepare and submit function ASoC: dmaengine_pcm: expose functions to header file for custom functions ASoC: Intel: KMB: Enable DMA transfer mode
.../bindings/sound/intel,keembay-i2s.yaml | 14 ++ include/sound/dmaengine_pcm.h | 21 ++ sound/core/pcm_dmaengine.c | 46 ++-- sound/soc/intel/Kconfig | 2 + sound/soc/intel/keembay/kmb_platform.c | 208 ++++++++++++++++-- sound/soc/intel/keembay/kmb_platform.h | 9 + sound/soc/soc-generic-dmaengine-pcm.c | 8 +- 7 files changed, 270 insertions(+), 38 deletions(-)
S24_LE is 24 bit audio in 32 bit container configuration Fixing the configuration to match the data arrangement of this audio format.
Fixes: c5477e966728 ("ASoC: Intel: Add KeemBay platform driver")
Signed-off-by: Michael Sit Wei Hong michael.wei.hong.sit@intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/intel/keembay/kmb_platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index f54b710ee1c2..291a686568c2 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -487,9 +487,9 @@ static int kmb_dai_hw_params(struct snd_pcm_substream *substream, kmb_i2s->xfer_resolution = 0x02; break; case SNDRV_PCM_FORMAT_S24_LE: - config->data_width = 24; - kmb_i2s->ccr = 0x08; - kmb_i2s->xfer_resolution = 0x04; + config->data_width = 32; + kmb_i2s->ccr = 0x14; + kmb_i2s->xfer_resolution = 0x05; break; case SNDRV_PCM_FORMAT_S32_LE: config->data_width = 32;
Add descriptions for entries needed for audio device to use DMA channels for audio playback and capture.
Signed-off-by: Michael Sit Wei Hong michael.wei.hong.sit@intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- .../bindings/sound/intel,keembay-i2s.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/intel,keembay-i2s.yaml b/Documentation/devicetree/bindings/sound/intel,keembay-i2s.yaml index d346e61ab708..e0658f122cbb 100644 --- a/Documentation/devicetree/bindings/sound/intel,keembay-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/intel,keembay-i2s.yaml @@ -45,6 +45,18 @@ properties: - const: osc - const: apb_clk
+ dmas: + items: + - description: DMA controller phandle and DMA channel + for TX and RX + + dma-names: + items: + - description: "tx" for the transmit channel + "rx" for the receive channel + - const: tx + - const: rx + required: - compatible - "#sound-dai-cells" @@ -70,4 +82,6 @@ examples: interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>; clock-names = "osc", "apb_clk"; clocks = <&scmi_clk KEEM_BAY_PSS_AUX_I2S3>, <&scmi_clk KEEM_BAY_PSS_I2S3>; + dmas = <&axi_dma0 29 &axi_dma0 33>; + dma-names = "tx", "rx"; };
Enabling custom prepare and submit function to overcome DMA limitation.
In the Intel KeemBay solution, the DW AXI-based DMA has a limitation on the number of DMA blocks per transfer. In the case of 16 bit audio ASoC would allocate blocks exceeding the DMA block limitation.
The ASoC layers are not aware of such DMA limitation, and the DMA engine does not provide an API to set the maximum number of blocks per linked link.
This patch suggests an additional callback to let the caller check and modify the number of blocks per transfer to work-around the limitations.
Signed-off-by: Michael Sit Wei Hong michael.wei.hong.sit@intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- include/sound/dmaengine_pcm.h | 6 ++++++ sound/core/pcm_dmaengine.c | 30 ++++++++++++++++++++++----- sound/soc/soc-generic-dmaengine-pcm.c | 8 ++++++- 3 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 8c5e38180fb0..9fae56d39ae2 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -28,6 +28,9 @@ snd_pcm_substream_to_dma_direction(const struct snd_pcm_substream *substream) int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, const struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config); int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd); +int snd_dmaengine_pcm_custom_trigger(struct snd_pcm_substream *substream, int cmd, + int (*custom_pcm_prepare_and_submit)(struct snd_pcm_substream *substream)); + snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream); snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream);
@@ -113,6 +116,8 @@ int snd_dmaengine_pcm_refine_runtime_hwparams( * which do not use devicetree. * @process: Callback used to apply processing on samples transferred from/to * user space. + * @custom_pcm_prepare_and_submit: Callback used to work-around DMA limitations + * related to link lists. * @compat_filter_fn: Will be used as the filter function when requesting a * channel for platforms which do not use devicetree. The filter parameter * will be the DAI's DMA data. @@ -138,6 +143,7 @@ struct snd_dmaengine_pcm_config { int (*process)(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, void *buf, unsigned long bytes); + int (*custom_pcm_prepare_and_submit)(struct snd_pcm_substream *substream); dma_filter_fn compat_filter_fn; struct device *dma_dev; const char *chan_names[SNDRV_PCM_STREAM_LAST + 1]; diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 4d059ff2b2e4..cbd1429de509 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -170,16 +170,20 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) }
/** - * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation + * snd_dmaengine_pcm_custom_trigger - customized PCM trigger implementation to + * work-around DMA limitations related to link lists. * @substream: PCM substream * @cmd: Trigger command + * @custom_pcm_prepare_and_submit: custom function to deal with DMA limitations * * Returns 0 on success, a negative error code otherwise. * - * This function can be used as the PCM trigger callback for dmaengine based PCM - * driver implementations. + * This function can be used as the PCM trigger callback for customized dmaengine + * based PCM driver implementations. */ -int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) + +int snd_dmaengine_pcm_custom_trigger(struct snd_pcm_substream *substream, int cmd, + int (*custom_pcm_prepare_and_submit)(struct snd_pcm_substream *substream)) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; @@ -187,7 +191,7 @@ int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) { case SNDRV_PCM_TRIGGER_START: - ret = dmaengine_pcm_prepare_and_submit(substream); + ret = custom_pcm_prepare_and_submit(substream); if (ret) return ret; dma_async_issue_pending(prtd->dma_chan); @@ -214,6 +218,22 @@ int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0; } +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_custom_trigger); + +/** + * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation + * @substream: PCM substream + * @cmd: Trigger command + * + * Returns 0 on success, a negative error code otherwise. + * + * This function can be used as the PCM trigger callback for dmaengine based PCM + * driver implementations. + */ +int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + return snd_dmaengine_pcm_custom_trigger(substream, cmd, dmaengine_pcm_prepare_and_submit); +} EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
/** diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 9ef80a48707e..88fca6402a36 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -173,7 +173,13 @@ static int dmaengine_pcm_close(struct snd_soc_component *component, static int dmaengine_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { - return snd_dmaengine_pcm_trigger(substream, cmd); + struct dmaengine_pcm *pcm = soc_component_to_pcm(component); + + if (pcm->config && pcm->config->custom_pcm_prepare_and_submit) + return snd_dmaengine_pcm_custom_trigger(substream, cmd, + pcm->config->custom_pcm_prepare_and_submit); + else + return snd_dmaengine_pcm_trigger(substream, cmd); }
static struct dma_chan *dmaengine_pcm_compat_request_channel(
On Mon, Nov 16, 2020 at 02:19:03PM +0800, Michael Sit Wei Hong wrote:
In the Intel KeemBay solution, the DW AXI-based DMA has a limitation on the number of DMA blocks per transfer. In the case of 16 bit audio ASoC would allocate blocks exceeding the DMA block limitation.
The ASoC layers are not aware of such DMA limitation, and the DMA engine does not provide an API to set the maximum number of blocks per linked link.
Can we not extend the dmaengine API so that the ASoC layer (and any other users) can become aware of this limitation and handle it appropriately rather than jumping straight to some client driver specific handling?
On 11/16/20 1:58 PM, Mark Brown wrote:
On Mon, Nov 16, 2020 at 02:19:03PM +0800, Michael Sit Wei Hong wrote:
In the Intel KeemBay solution, the DW AXI-based DMA has a limitation on the number of DMA blocks per transfer. In the case of 16 bit audio ASoC would allocate blocks exceeding the DMA block limitation.
The ASoC layers are not aware of such DMA limitation, and the DMA engine does not provide an API to set the maximum number of blocks per linked link.
Can we not extend the dmaengine API so that the ASoC layer (and any other users) can become aware of this limitation and handle it appropriately rather than jumping straight to some client driver specific handling?
This was supposed to be an RFC, I asked Vinod/Lars to be copied for feedback. Unfortunately the RFC tag is missing and Vinod's email wasn't the right one... (fixed now).
This patchset suggests an ALSA-only quirk, having other more generic means to deal with this limitation would be fine - we just wanted to have a discussion on preferred directions. The IPs used are not Intel-specific so sooner or later someone else will have similar limitations to work-around.
On Mon, Nov 16, 2020 at 02:10:16PM -0600, Pierre-Louis Bossart wrote:
On 11/16/20 1:58 PM, Mark Brown wrote:
On Mon, Nov 16, 2020 at 02:19:03PM +0800, Michael Sit Wei Hong wrote:
Can we not extend the dmaengine API so that the ASoC layer (and any other users) can become aware of this limitation and handle it appropriately rather than jumping straight to some client driver specific handling?
This was supposed to be an RFC, I asked Vinod/Lars to be copied for feedback. Unfortunately the RFC tag is missing and Vinod's email wasn't the right one... (fixed now).
This patchset suggests an ALSA-only quirk, having other more generic means to deal with this limitation would be fine - we just wanted to have a discussion on preferred directions. The IPs used are not Intel-specific so sooner or later someone else will have similar limitations to work-around.
Generally with the dmaengine stuff we've added new query APIs to dmaengine and then used those in the ALSA/ASoC code to enumerate things, this certainly sounds like something that another device might have so it seems worth following that approach.
Moving some functions to the header file to be used by custom prepare and submit function.
In the Intel KeemBay solution, there is a DMA limitation which requires a custom prepare and submit function to modify the number of blocks per linked link.
This patch exposes some of the functions used in the pcm_dmaengine.c to be used by the custom function.
Signed-off-by: Michael Sit Wei Hong michael.wei.hong.sit@intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- include/sound/dmaengine_pcm.h | 15 +++++++++++++++ sound/core/pcm_dmaengine.c | 16 ++-------------- 2 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 9fae56d39ae2..d45652a27f73 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -174,8 +174,23 @@ struct dmaengine_pcm { unsigned int flags; };
+struct dmaengine_pcm_runtime_data { + struct dma_chan *dma_chan; + dma_cookie_t cookie; + + unsigned int pos; +}; + +static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( + const struct snd_pcm_substream *substream) +{ + return substream->runtime->private_data; +} + static inline struct dmaengine_pcm *soc_component_to_pcm(struct snd_soc_component *p) { return container_of(p, struct dmaengine_pcm, component); } + +void dmaengine_pcm_dma_complete(void *arg); #endif diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index cbd1429de509..0f99c63964ec 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -19,19 +19,6 @@
#include <sound/dmaengine_pcm.h>
-struct dmaengine_pcm_runtime_data { - struct dma_chan *dma_chan; - dma_cookie_t cookie; - - unsigned int pos; -}; - -static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( - const struct snd_pcm_substream *substream) -{ - return substream->runtime->private_data; -} - struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); @@ -128,7 +115,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data( } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data);
-static void dmaengine_pcm_dma_complete(void *arg) +void dmaengine_pcm_dma_complete(void *arg) { struct snd_pcm_substream *substream = arg; struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); @@ -139,6 +126,7 @@ static void dmaengine_pcm_dma_complete(void *arg)
snd_pcm_period_elapsed(substream); } +EXPORT_SYMBOL_GPL(dmaengine_pcm_dma_complete);
static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) {
Enable DMA transfer mode for Intel Keem Bay ASoC platform driver.
The driver will search the device tree for DMA resources at boot time to enable DMA transfer mode, and will proceed to use DMA transfer if the resource is available, otherwise the default PIO mode will be used.
Due to DMA Block transfer limitation, a customized function is introduced to check and handle the limitation. Instead of limiting the maximum period bytes to the minimum supported, which forces the period size to less than 10.6ms in the worst case, adjusting the DMA to use a longer linked list will allow more flexible period sizes which does not force applications to use ridiculously small period sizes.
Signed-off-by: Michael Sit Wei Hong michael.wei.hong.sit@intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/intel/Kconfig | 2 + sound/soc/intel/keembay/kmb_platform.c | 202 +++++++++++++++++++++++-- sound/soc/intel/keembay/kmb_platform.h | 9 ++ 3 files changed, 198 insertions(+), 15 deletions(-)
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index a5b446d5af19..6114dadfc52f 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -200,6 +200,8 @@ config SND_SOC_INTEL_KEEMBAY tristate "Keembay Platforms" depends on ARM64 || COMPILE_TEST depends on COMMON_CLK + select SND_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM help If you have a Intel Keembay platform then enable this option by saying Y or m. diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index 291a686568c2..3041823e447b 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -6,10 +6,12 @@ //
#include <linux/clk.h> +#include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <sound/dmaengine_pcm.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -23,6 +25,7 @@ #define I2S_OPERATION 0 #define DATA_WIDTH_CONFIG_BIT 6 #define TDM_CHANNEL_CONFIG_BIT 3 +#define MAX_DMA_BLOCKS 1024
static const struct snd_pcm_hardware kmb_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | @@ -335,6 +338,45 @@ static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component, return pos < runtime->buffer_size ? pos : 0; }
+static int kmb_pcm_prepare_and_submit(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_chan *chan = prtd->dma_chan; + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction direction; + unsigned long flags = DMA_CTRL_ACK; + struct snd_pcm_runtime *runtime = substream->runtime; + int blocks; + + blocks = bytes_to_samples(runtime, snd_pcm_lib_period_bytes(substream)); + direction = snd_pcm_substream_to_dma_direction(substream); + + if (!substream->runtime->no_period_wakeup) + flags |= DMA_PREP_INTERRUPT; + + prtd->pos = 0; + /* Check if the number of blocks used exceed the DMA BLOCK limitation */ + if (blocks > MAX_DMA_BLOCKS && direction == DMA_DEV_TO_MEM) + desc = dmaengine_prep_dma_cyclic(chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + samples_to_bytes(runtime, MAX_DMA_BLOCKS), direction, flags); + else + desc = dmaengine_prep_dma_cyclic(chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), direction, flags); + + if (!desc) + return -ENOMEM; + + desc->callback = dmaengine_pcm_dma_complete; + desc->callback_param = substream; + prtd->cookie = dmaengine_submit(desc); + + return 0; +} + static const struct snd_soc_component_driver kmb_component = { .name = "kmb", .pcm_construct = kmb_platform_pcm_new, @@ -343,6 +385,53 @@ static const struct snd_soc_component_driver kmb_component = { .pointer = kmb_pcm_pointer, };
+static const struct snd_soc_component_driver kmb_component_dma = { + .name = "kmb", +}; + +static int kmb_probe(struct snd_soc_dai *cpu_dai) +{ + struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); + + if (kmb_i2s->use_pio) + return 0; + + snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data, + &kmb_i2s->capture_dma_data); + + return 0; +} + +static inline void kmb_i2s_enable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream) +{ + u32 dma_reg; + + dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR); + /* Enable DMA handshake for stream */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_reg |= I2S_DMAEN_TXBLOCK; + else + dma_reg |= I2S_DMAEN_RXBLOCK; + + writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR); +} + +static inline void kmb_i2s_disable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream) +{ + u32 dma_reg; + + dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR); + /* Disable DMA handshake for stream */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + dma_reg &= ~I2S_DMAEN_TXBLOCK; + writel(1, kmb_i2s->i2s_base + I2S_RTXDMA); + } else { + dma_reg &= ~I2S_DMAEN_RXBLOCK; + writel(1, kmb_i2s->i2s_base + I2S_RRXDMA); + } + writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR); +} + static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s, struct snd_pcm_substream *substream) { @@ -356,7 +445,11 @@ static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s, else writel(1, kmb_i2s->i2s_base + IRER);
- kmb_i2s_irq_trigger(kmb_i2s, substream->stream, config->chan_nr, true); + if (kmb_i2s->use_pio) + kmb_i2s_irq_trigger(kmb_i2s, substream->stream, + config->chan_nr, true); + else + kmb_i2s_enable_dma(kmb_i2s, substream->stream);
if (kmb_i2s->master) writel(1, kmb_i2s->i2s_base + CER); @@ -434,7 +527,8 @@ static int kmb_dai_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_STOP: kmb_i2s->active--; - kmb_i2s_stop(kmb_i2s, substream); + if (kmb_i2s->use_pio) + kmb_i2s_stop(kmb_i2s, substream); break; default: return -EINVAL; @@ -485,16 +579,22 @@ static int kmb_dai_hw_params(struct snd_pcm_substream *substream, config->data_width = 16; kmb_i2s->ccr = 0x00; kmb_i2s->xfer_resolution = 0x02; + kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case SNDRV_PCM_FORMAT_S24_LE: config->data_width = 32; kmb_i2s->ccr = 0x14; kmb_i2s->xfer_resolution = 0x05; + kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; break; case SNDRV_PCM_FORMAT_S32_LE: config->data_width = 32; kmb_i2s->ccr = 0x10; kmb_i2s->xfer_resolution = 0x05; + kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; break; default: dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt"); @@ -572,9 +672,56 @@ static int kmb_dai_prepare(struct snd_pcm_substream *substream, return 0; }
+static int kmb_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); + struct snd_dmaengine_dai_dma_data *dma_data; + + if (kmb_i2s->use_pio) + return 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data = &kmb_i2s->play_dma_data; + else + dma_data = &kmb_i2s->capture_dma_data; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); + + return 0; +} + +static int kmb_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); + /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ + if (kmb_i2s->use_pio) + kmb_i2s_clear_irqs(kmb_i2s, substream->stream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + writel(0, kmb_i2s->i2s_base + ITER); + else + writel(0, kmb_i2s->i2s_base + IRER); + + if (kmb_i2s->use_pio) + kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false); + else + kmb_i2s_disable_dma(kmb_i2s, substream->stream); + + if (!kmb_i2s->active) { + writel(0, kmb_i2s->i2s_base + CER); + writel(0, kmb_i2s->i2s_base + IER); + } + + return 0; +} + static struct snd_soc_dai_ops kmb_dai_ops = { + .startup = kmb_dai_startup, .trigger = kmb_dai_trigger, .hw_params = kmb_dai_hw_params, + .hw_free = kmb_dai_hw_free, .prepare = kmb_dai_prepare, .set_fmt = kmb_set_dai_fmt, }; @@ -607,6 +754,7 @@ static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = { SNDRV_PCM_FMTBIT_S16_LE), }, .ops = &kmb_dai_ops, + .probe = kmb_probe, }, };
@@ -626,6 +774,7 @@ static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = { SNDRV_PCM_FMTBIT_S16_LE), }, .ops = &kmb_dai_ops, + .probe = kmb_probe, }, };
@@ -635,12 +784,19 @@ static const struct of_device_id kmb_plat_of_match[] = { {} };
+static const struct snd_dmaengine_pcm_config kmb_dmaengine_pcm_config = { + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .custom_pcm_prepare_and_submit = kmb_pcm_prepare_and_submit, +}; + static int kmb_plat_dai_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct snd_soc_dai_driver *kmb_i2s_dai; const struct of_device_id *match; struct device *dev = &pdev->dev; struct kmb_i2s_info *kmb_i2s; + struct resource *res; int ret, irq; u32 comp1_reg;
@@ -682,7 +838,7 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) return PTR_ERR(kmb_i2s->clk_i2s); }
- kmb_i2s->i2s_base = devm_platform_ioremap_resource(pdev, 0); + kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(kmb_i2s->i2s_base)) return PTR_ERR(kmb_i2s->i2s_base);
@@ -692,22 +848,38 @@ static int kmb_plat_dai_probe(struct platform_device *pdev)
kmb_i2s->dev = &pdev->dev;
- irq = platform_get_irq_optional(pdev, 0); - if (irq > 0) { - ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, - pdev->name, kmb_i2s); - if (ret < 0) { - dev_err(dev, "failed to request irq\n"); - return ret; - } - } - comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
- ret = devm_snd_soc_register_component(dev, &kmb_component, - kmb_i2s_dai, 1); + kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas")); + + if (kmb_i2s->use_pio) { + irq = platform_get_irq_optional(pdev, 0); + if (irq > 0) { + ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, + pdev->name, kmb_i2s); + if (ret < 0) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + } + ret = devm_snd_soc_register_component(dev, &kmb_component, + kmb_i2s_dai, 1); + } else { + kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA; + kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA; + ret = snd_dmaengine_pcm_register(&pdev->dev, + &kmb_dmaengine_pcm_config, 0); + if (ret) { + dev_err(&pdev->dev, "could not register dmaengine: %d\n", + ret); + return ret; + } + ret = devm_snd_soc_register_component(dev, &kmb_component_dma, + kmb_i2s_dai, 1); + } + if (ret) { dev_err(dev, "not able to register dai\n"); return ret; diff --git a/sound/soc/intel/keembay/kmb_platform.h b/sound/soc/intel/keembay/kmb_platform.h index 9756b132c12f..fd5341b66279 100644 --- a/sound/soc/intel/keembay/kmb_platform.h +++ b/sound/soc/intel/keembay/kmb_platform.h @@ -12,6 +12,7 @@ #include <linux/bits.h> #include <linux/bitfield.h> #include <linux/types.h> +#include <sound/dmaengine_pcm.h>
/* Register values with reference to KMB databook v1.1 */ /* common register for all channel */ @@ -103,7 +104,12 @@ #define DW_I2S_MASTER BIT(3)
#define I2S_RXDMA 0x01C0 +#define I2S_RRXDMA 0x01C4 #define I2S_TXDMA 0x01C8 +#define I2S_RTXDMA 0x01CC +#define I2S_DMACR 0x0200 +#define I2S_DMAEN_RXBLOCK (1 << 16) +#define I2S_DMAEN_TXBLOCK (1 << 17)
/* * struct i2s_clk_config_data - represent i2s clk configuration data @@ -131,6 +137,9 @@ struct kmb_i2s_info { u32 xfer_resolution; u32 fifo_th; bool master; + /* data related to DMA transfers b/w i2s and DMAC */ + struct snd_dmaengine_dai_dma_data play_dma_data; + struct snd_dmaengine_dai_dma_data capture_dma_data;
struct i2s_clk_config_data config; int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
On Mon, 16 Nov 2020 14:19:00 +0800, Michael Sit Wei Hong wrote:
v1: Initial patch version, which contains fix for S24_LE format and also enable DMA mode on Intel Keembay platform
Michael Sit Wei Hong (5): ASoC: Intel: KMB: Fix S24_LE configuration dt-bindings: sound: intel, keembay-i2s: Add info for device to use DMA ASoC: soc-generic-dmaengine-pcm: Add custom prepare and submit function ASoC: dmaengine_pcm: expose functions to header file for custom functions ASoC: Intel: KMB: Enable DMA transfer mode
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[1/1] ASoC: Intel: KMB: Fix S24_LE configuration commit: 1bd7b0fc0165694897b7d2fb39751a07b98f6bf1
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
participants (3)
-
Mark Brown
-
Michael Sit Wei Hong
-
Pierre-Louis Bossart