diff options
author | Christian Pointner <equinox@helsinki.at> | 2024-05-10 18:26:46 (GMT) |
---|---|---|
committer | Christian Pointner <equinox@helsinki.at> | 2024-05-10 18:26:46 (GMT) |
commit | 627f7d488817e308d6f3a92fd9a877723ac7ae1d (patch) | |
tree | 554a3c53c90b20da5bd0da0c8da67a9b169bd10f /snd-alpx/alpx_streams.c |
import snd-alpx V3.4.3
Diffstat (limited to 'snd-alpx/alpx_streams.c')
-rw-r--r-- | snd-alpx/alpx_streams.c | 730 |
1 files changed, 730 insertions, 0 deletions
diff --git a/snd-alpx/alpx_streams.c b/snd-alpx/alpx_streams.c new file mode 100644 index 0000000..9215752 --- /dev/null +++ b/snd-alpx/alpx_streams.c @@ -0,0 +1,730 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Support for Digigram AlpX PCI-e boards +* +* Copyright (c) 2024 Digigram Digital (info@digigram.com) +*/ + +#include <linux/version.h> + +#include "alpx_reg.h" +#include "alpx.h" +#include "alpx_streams.h" +#include "alpx_led.h" + +#include <sound/asound.h> +#include <sound/core.h> +#include <linux/io.h> + + +extern unsigned int log_transfers; //defined in alpx_core.c + +#if !IS_ENABLED(CONFIG_SND_DMAENGINE_PCM) + #if defined (CONFIG_KERNEL_GENERIC) + #if KERNEL_VERSION(5, 17, 0) <= LINUX_VERSION_CODE + #include "core/generic/5.17/dmaengine_pcm.h" + #include "core/generic/5.17/pcm_dmaengine.c" + #elif KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE + #include "core/generic/5.12/dmaengine_pcm.h" + #include "core/generic/5.12/pcm_dmaengine.c" + #elif KERNEL_VERSION(5, 7, 0) <= LINUX_VERSION_CODE + #include "core/generic/5.7/dmaengine_pcm.h" + #include "core/generic/5.7/pcm_dmaengine.c" + #elif KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE + #include "core/generic/5.5/dmaengine_pcm.h" + #include "core/generic/5.5/pcm_dmaengine.c" + #elif KERNEL_VERSION(5, 2, 0) <= LINUX_VERSION_CODE + #include "core/generic/5.2/dmaengine_pcm.h" + #include "core/generic/5.2/pcm_dmaengine.c" + #elif KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE + #include "core/generic/4.19/dmaengine_pcm.h" + #include "core/generic/4.19/pcm_dmaengine.c" + #endif + #elif defined (CONFIG_KERNEL_REDHAT) + #if KERNEL_VERSION(5, 14, 0) <= LINUX_VERSION_CODE + #include "core/RedHat/5.14/dmaengine_pcm.h" + #include "core/RedHat/5.14/pcm_dmaengine.c" + #elif KERNEL_VERSION(4, 18, 0) == LINUX_VERSION_CODE + #include "core/RedHat/4.18/dmaengine_pcm.h" + #include "core/RedHat/4.18/pcm_dmaengine.c" + #else + #error RedHat kernel not supported yet. + #endif + #endif +#elif defined (CONFIG_KERNEL_GENERIC) + #include <sound/dmaengine_pcm.h> +#else + #error "No valid DMA Engine support!" +#endif + +/* The size (in bytes) of sample's container */ +const unsigned int ALPX_SAMPLE_CONTAINER_SIZE = 4; + + +/* Configure */ + +static int alpmadi_configure(struct alpx_device *alpx_dev, unsigned int rate) +{ + u32 config_fs, value; + + dev_dbg(alpx_dev->dev, "%s(): requesting rate %d\n", __func__, rate); + + switch (rate) { + case 44100: + config_fs = ALPMADI_CLK_MANAGER_CONFIG_FS_44_1K; + break; + case 48000: + config_fs = ALPMADI_CLK_MANAGER_CONFIG_FS_48K; + break; + case 88200: + config_fs = ALPMADI_CLK_MANAGER_CONFIG_FS_88_2K; + break; + case 96000: + config_fs = ALPMADI_CLK_MANAGER_CONFIG_FS_96K; + break; + case 176400: + config_fs = ALPMADI_CLK_MANAGER_CONFIG_FS_176_4K; + break; + case 192000: + config_fs = ALPMADI_CLK_MANAGER_CONFIG_FS_192K; + break; + default: + return -EINVAL; + } + + value = readl(ALPX_REG(alpx_dev, ALPMADI, CLK_MANAGER, CONFIG)); + dev_dbg(alpx_dev->dev, "%s(): alpMadi clk manager config %x\n", __func__, value); + + value &= ~ALPMADI_CLK_MANAGER_CONFIG_FS_MASK; + value |= config_fs; + + writel(value, ALPX_REG(alpx_dev, ALPMADI, CLK_MANAGER, CONFIG)); + dev_dbg(alpx_dev->dev, "%s(): alpMadi clk manager set to %x\n", __func__, value); + + return 0; +} + +static int alpdante_configure(struct alpx_device *alpx_dev, unsigned int rate) +{ + dev_dbg(alpx_dev->dev, "%s(): requesting rate %dHz\n", __func__, rate); + + if (alpx_dev->variant->capture_hw->rate_min == rate) { + dev_dbg(alpx_dev->dev, "%s(): requested rate %dHz is supported\n", __func__, rate); + return 0; + } + else { + dev_dbg(alpx_dev->dev, "Requested %dHz not supported, Currently supported rate is %dHz\n", rate, + alpx_dev->variant->capture_hw->rate_min); + return -EINVAL; + } + +} + +static int alpstereo_configure(struct alpx_device *alpx_dev, unsigned int rate) +{ + u32 config_fs, read_value, target_value; + u32 try_qty = 10; /* retries */ + + dev_dbg(alpx_dev->dev, "%s(): requesting rate %d\n", __func__, rate); + + switch (rate) { + case 8000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_8K; + break; + case 11025: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_11_025K; + break; + case 16000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_16K; + break; + case 22000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_22_05K; + break; + case 24000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_24K; + break; + case 32000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_32K; + break; + case 44100: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_44_1K; + break; + case 48000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_48K; + break; + case 64000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_64K; + break; + case 88200: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_88_2K; + break; + case 96000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_96K; + break; + case 128000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_128K; + break; + case 176400: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_176_4K; + break; + case 192000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_192K; + break; + default: + return -EINVAL; + } + + read_value = readl(ALPX_REG(alpx_dev, ALP222, CLK_MANAGER, CONFIG)); + + /* Only if needed */ + if ((read_value & ALP222_CLK_MANAGER_CONFIG_FS_MASK) == config_fs) + return 0; + + read_value &= ~ALP222_CLK_MANAGER_CONFIG_FS_MASK; + target_value = read_value | config_fs; + + do { + writel(target_value, ALPX_REG(alpx_dev, ALP222, CLK_MANAGER, CONFIG)); + dev_dbg(alpx_dev->dev, "%s(): alp222 new clk config set to %x try: %d\n", + __func__, target_value, try_qty); + + /* Add a delay to give time to the card to initialize its + * internals (PLL, buses,...) to avoid internal stalls. + */ + mdelay(1); + + read_value = readl(ALPX_REG(alpx_dev, ALP222, CLK_MANAGER, CONFIG)); + dev_dbg(alpx_dev->dev, "%s(): alp222 check clk config: %x\n", __func__, read_value); + } while (--try_qty && + ((read_value & ALP222_CLK_MANAGER_CONFIG_FS_MASK ) != + (target_value & ALP222_CLK_MANAGER_CONFIG_FS_MASK))); + + if ((read_value & ALP222_CLK_MANAGER_CONFIG_FS_MASK) != + (target_value & ALP222_CLK_MANAGER_CONFIG_FS_MASK)) + dev_err(alpx_dev->dev, "%s(): alp222 ERROR clk config, expected: 0x%x, actual: 0x%x, \n", __func__, + target_value, read_value); + + dev_dbg(alpx_dev->dev, "%s(): alp222 new current clk config: 0x%x\n", __func__, + readl(ALPX_REG(alpx_dev, ALP222, CLK_MANAGER, CONFIG))); + + return (read_value & ALP222_CLK_MANAGER_CONFIG_FS_MASK) == + (target_value & ALP222_CLK_MANAGER_CONFIG_FS_MASK) ? 0 : -EIO; +} + +static int alpmultichannel_configure(struct alpx_device *alpx_dev, unsigned int rate) +{ + u32 config_fs, value; + u32 effective_fs, source; + /* XXX: check clock source, if not internal, error out if rate is not + external one */ + dev_dbg(alpx_dev->dev, "%s(): requesting rate %d\n", __func__, rate); + + value = readl(ALPX_REG(alpx_dev, ALPMC, CLK_MANAGER, CONFIG)); + + + + source = ALPMC_CLK_MANAGER_SOURCE(value); + effective_fs = ALPMC_CLK_MANAGER_EFFECTIVE_FS(value); + + switch (rate) { + case 8000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_8K; + break; + case 11025: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_11_025K; + break; + case 16000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_16K; + break; + case 22050: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_22_05K; + break; + case 24000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_24K; + break; + case 32000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_32K; + break; + case 44100: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_44_1K; + break; + case 48000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_48K; + break; + case 64000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_64K; + break; + case 88200: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_88_2K; + break; + case 96000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_96K; + break; + case 128000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_128K; + break; + case 176400: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_176_4K; + break; + case 192000: + config_fs = ALPxxx_CLK_MANAGER_CLK_VALUE_192K; + break; + default: + return -EINVAL; + } + + if (source != ALPMC_CLK_MANAGER_SOURCE_INTERNAL) { + unsigned int effective_rate; + + switch(effective_fs) { + case ALPxxx_CLK_MANAGER_CLK_VALUE_8K : + effective_rate = 8000; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_11_025K : + effective_rate = 11025; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_16K : + effective_rate = 16000; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_22_05K : + effective_rate = 22050; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_24K : + effective_rate = 24000; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_32K: + effective_rate = 32000; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_44_1K: + effective_rate = 44100; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_48K : + effective_rate = 48000; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_64K : + effective_rate = 64000; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_88_2K : + effective_rate = 88200; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_96K : + effective_rate = 96000; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_128K : + effective_rate = 128000; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_176_4K : + effective_rate = 176400; + break; + case ALPxxx_CLK_MANAGER_CLK_VALUE_192K : + effective_rate = 192000; + break; + default : + dev_err(alpx_dev->dev, + "Invalid effective sample rate\n"); + return -EINVAL; + }; + + if (effective_rate != rate) { + dev_err(alpx_dev->dev, + "Requested sample rate (%u) does not match external clock sample rate (%u)\n", + rate, effective_rate); + return -EINVAL; + } + } else { + if (ALPMC_CLK_MANAGER_CONFIG_FS(value) != config_fs) { + + value &= ~ALPMC_CLK_MANAGER_CONFIG_FS_MASK; + value |= config_fs; + + writel(value, ALPX_REG(alpx_dev, ALPMC, CLK_MANAGER, CONFIG)); + //Add a delay to give time to the card (PLL, internal buses,...), w/o, the setting is not effective ! + mdelay(ALPMC_FS_SWITCH_DELAY); + } + } + + dev_dbg(alpx_dev->dev, "%s() : %s new current clk config: 0x%x\n", __func__, + alpx_dev->variant->shortname, + readl(ALPX_REG(alpx_dev, ALPMC, CLK_MANAGER, CONFIG))); + + return 0; +} + +static int alpx_configure(struct alpx_device *alpx_dev, unsigned int rate) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&alpx_dev->config.lock, flags); + + if (alpx_dev->config.users > 0 && rate != alpx_dev->config.rate) { + ret = -EINVAL; + goto complete; + } + + alpx_dev->config.rate = rate; + + if (alpx_is_882(alpx_dev)) + ret = alpmultichannel_configure(alpx_dev, rate); + else if (alpx_is_222(alpx_dev)) + ret = alpstereo_configure(alpx_dev, rate); + else if (alpx_is_madi(alpx_dev)) + ret = alpmadi_configure(alpx_dev, rate); + else if (alpx_is_dante(alpx_dev)) + ret = alpdante_configure(alpx_dev, rate); + else + snd_BUG(); + + if (!ret) + alpx_dev->config.users++; + +complete: + spin_unlock_irqrestore(&alpx_dev->config.lock, flags); + + return ret; +} + +static void alpx_configure_close(struct alpx_device *alpx_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&alpx_dev->config.lock, flags); + + alpx_dev->config.users--; + + spin_unlock_irqrestore(&alpx_dev->config.lock, flags); +} + +/* Pipe */ + +static int alpx_pipe_start(struct alpx_pipe *pipe, struct snd_pcm_substream *substream) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + + dev_dbg( alpx_dev->dev, "%s starting\n", pipe->xdma_write ? "Playback" : "Capture"); + + if (pipe->status != ALPX_PIPE_STATUS_IDLE && + pipe->status != ALPX_PIPE_STATUS_STOPPED) { + dev_warn(alpx_dev->dev, "%s pipe is busy!\n", + pipe->xdma_write ? "playback" : "capture"); + return -EBUSY; + } + + if (alpx_is_madi(alpx_dev)) + alpmadi_set_led_state(alpx_dev, true); + + snd_dmaengine_pcm_trigger(substream, SNDRV_PCM_TRIGGER_START); + pipe->period_qty_on_start = alpx_get_samples_counter(alpx_dev); + + dev_dbg( alpx_dev->dev, "%s Period:%u\n", pipe->xdma_write ? "Playback" : "Capture", pipe->period_qty_on_start); + + return 0; +} + +static int alpx_pipe_open(struct alpx_pipe *pipe, + struct snd_pcm_substream *substream) +{ + struct alpx_device *alpx_dev = substream->pcm->private_data; + struct dma_chan *chan; + + /* Check */ + if (!alpx_dev) { + printk(KERN_ERR"%s() : alpx_dev is NULL\n",__func__); + return -EINVAL; + } + + if (!alpx_dev->dev) { + printk(KERN_ERR"%s() : alpx_dev->dev is NULL\n",__func__); + return -EINVAL; + } + + dev_dbg(alpx_dev->dev, "%s(): CALLED\n", __func__); + + dev_dbg(alpx_dev->dev, "%s: matching with dev [%s]\n", __func__, dev_name(alpx_dev->dev)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + chan = dma_request_chan(alpx_dev->dev, "h2c-0"); + else + chan = dma_request_chan(alpx_dev->dev, "c2h-0"); + + if (IS_ERR(chan)) { + dev_err(alpx_dev->dev, "%s() : Error %ld when requesting DMA channel\n", + __func__, PTR_ERR(chan)); + return PTR_ERR(chan); + } + + pipe->substream = substream; + + return snd_dmaengine_pcm_open(substream, chan); + +} + +static int alpx_pipe_stop(struct alpx_pipe *pipe, struct snd_pcm_substream *substream) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int samples_counter, card_samples_qty; + + /* Retrieve the samples counter as soon as possible */ + samples_counter = alpx_get_samples_counter(alpx_dev); + snd_dmaengine_pcm_trigger(substream, SNDRV_PCM_TRIGGER_STOP); + + if (alpx_is_madi(alpx_dev)) + alpmadi_set_led_state(alpx_dev, false); + + /* + * Since we are under stream lock here and the transfer done callback + * also needs to grab stream lock, we cannot have a sync wait for the + * completion. As a result, we request a stop with the stopping status. + */ + card_samples_qty = samples_counter - pipe->period_qty_on_start; + dev_dbg(alpx_dev->dev, "%s stopped after %u samples (%u bytes)\n", + pipe->xdma_write ? "Playback" : "Capture", card_samples_qty, + runtime->hw.channels_max * ALPX_SAMPLE_CONTAINER_SIZE * card_samples_qty); + + if (pipe->status == ALPX_PIPE_STATUS_RUNNING) + pipe->status = ALPX_PIPE_STATUS_STOPPING; + else + pipe->status = ALPX_PIPE_STATUS_STOPPED; + + return 0; +} + +static int alpx_pipe_configure(struct alpx_pipe *pipe, + struct snd_pcm_hw_params *hw_params) +{ + struct alpx_device *alpx_dev = pipe->substream->pcm->private_data; + unsigned int rate = params_rate(hw_params); + int ret; + + dev_dbg(alpx_dev->dev, "%s hardware parameters:\n", + pipe->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); + dev_dbg(alpx_dev->dev, "* channels: %d\n", params_channels(hw_params)); + dev_dbg(alpx_dev->dev, "* rate: %d\n", params_rate(hw_params)); + dev_dbg(alpx_dev->dev, "* periods: %d\n", params_periods(hw_params)); + dev_dbg(alpx_dev->dev, "* period size: %d frames\n", + params_period_size(hw_params)); + dev_dbg(alpx_dev->dev, "* period bytes: %d bytes\n", + params_period_size(hw_params) * params_channels(hw_params) * 4); + dev_dbg(alpx_dev->dev, "* buffer size: %d frames\n", + params_buffer_size(hw_params)); + dev_dbg(alpx_dev->dev, "* buffer bytes: %d bytes\n", + params_buffer_bytes(hw_params)); + + ret = alpx_configure(alpx_dev, rate); + if (ret) + return ret; + + pipe->configured = true; + + return 0; +} + +static void alpx_pipe_close(struct alpx_pipe *pipe) +{ + struct alpx_device *alpx_dev = pipe->substream->pcm->private_data; + + dev_dbg(alpx_dev->dev, "%s(): CALLED\n", __func__); + + snd_dmaengine_pcm_close_release_chan(pipe->substream); + + if (pipe->configured) { + alpx_configure_close(alpx_dev); + pipe->configured = false; + } +} + +int alpx_pipe_init(struct alpx_pipe *pipe, bool xdma_write) +{ + pipe->xdma_write = xdma_write; + + pipe->status = ALPX_PIPE_STATUS_IDLE; + + return 0; +} + +/* Playback */ + +int alpx_playback_open(struct snd_pcm_substream *substream) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + //Add a break to avoid playback lock when chaining playbacks. This is required for AlpDANTE cards. Looks like the XDMA needs some time here. + msleep(200); + + dev_dbg(alpx_dev->dev, "CALLED\n"); + + ret = alpx_pipe_open(&alpx_dev->playback, substream); + if (!ret) + runtime->hw = *alpx_dev->variant->playback_hw; + + dev_dbg(alpx_dev->dev, "=> %u\n", ret); + return ret; +} + +int alpx_playback_close(struct snd_pcm_substream *substream) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + + alpx_pipe_close(&alpx_dev->playback); + return 0; +} + +int alpx_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + int ret; + + ret = alpx_pipe_configure(&alpx_dev->playback, hw_params); + if (ret) + return ret; + +#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE + return 0; +#else + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +#endif +} + +int alpx_playback_hw_free(struct snd_pcm_substream *substream) +{ + +#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE + return 0; +#else + return snd_pcm_lib_free_pages(substream); +#endif +} + +int alpx_playback_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +int alpx_playback_trigger(struct snd_pcm_substream *substream, + int command) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + + dev_dbg(alpx_dev->dev, "CALLED\n"); + + switch (command) { + case SNDRV_PCM_TRIGGER_START: + alpx_pipe_start(&alpx_dev->playback, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + alpx_pipe_stop(&alpx_dev->playback, substream); + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct snd_pcm_ops alpx_playback_ops = { + .open = alpx_playback_open, + .close = alpx_playback_close, +#if KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE + .ioctl = snd_pcm_lib_ioctl, +#endif + .hw_params = alpx_playback_hw_params, + .hw_free = alpx_playback_hw_free, + .prepare = alpx_playback_prepare, + .trigger = alpx_playback_trigger, + .pointer = snd_dmaengine_pcm_pointer, +}; + +/* Capture */ + +int alpx_capture_open(struct snd_pcm_substream *substream) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + ret = alpx_pipe_open(&alpx_dev->capture, substream); + if (ret) + return ret; + + runtime->hw = *alpx_dev->variant->capture_hw; + + return 0; +} + +int alpx_capture_close(struct snd_pcm_substream *substream) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + + alpx_pipe_close(&alpx_dev->capture); + + return 0; +} + +int alpx_capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + int ret; + + ret = alpx_pipe_configure(&alpx_dev->capture, hw_params); + if (ret) + return ret; + +#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE + return 0; +#else + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +#endif +} + +int alpx_capture_hw_free(struct snd_pcm_substream *substream) +{ +#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE + return 0; +#else + return snd_pcm_lib_free_pages(substream); +#endif +} + +int alpx_capture_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +int alpx_capture_trigger(struct snd_pcm_substream *substream, + int command) +{ + struct alpx_device *alpx_dev = snd_pcm_substream_chip(substream); + + switch (command) { + case SNDRV_PCM_TRIGGER_START: + alpx_pipe_start(&alpx_dev->capture, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + alpx_pipe_stop(&alpx_dev->capture, substream); + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct snd_pcm_ops alpx_capture_ops = { + .open = alpx_capture_open, + .close = alpx_capture_close, +#if KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE + .ioctl = snd_pcm_lib_ioctl, +#endif + .hw_params = alpx_capture_hw_params, + .hw_free = alpx_capture_hw_free, + .prepare = alpx_capture_prepare, + .trigger = alpx_capture_trigger, + .pointer = snd_dmaengine_pcm_pointer, +}; |