ASoC: fsl: add memory to memory function for ASRC
Merge series from Shengjiu Wang <shengjiu.wang@nxp.com>:
This function is base on the accelerator implementation
for compress API:
04177158cf ("ALSA: compress_offload: introduce accel operation mode")
Audio signal processing also has the requirement for memory to
memory similar as Video.
This asrc memory to memory (memory ->asrc->memory) case is a non
real time use case.
User fills the input buffer to the asrc module, after conversion, then asrc
sends back the output buffer to user. So it is not a traditional ALSA playback
and capture case.
Because we had implemented the "memory -> asrc ->i2s device-> codec"
use case in ALSA. Now the "memory->asrc->memory" needs
to reuse the code in asrc driver, so the patch 1 and patch 2 is for refining
the code to make it can be shared by the "memory->asrc->memory"
driver.
Other change is to add memory to memory support for two kinds of i.MX ASRC
modules.
This commit is contained in:
@@ -334,6 +334,14 @@ union snd_codec_options {
|
||||
struct snd_dec_wma wma_d;
|
||||
struct snd_dec_alac alac_d;
|
||||
struct snd_dec_ape ape_d;
|
||||
struct {
|
||||
__u32 out_sample_rate;
|
||||
} src_d;
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct snd_codec_desc_src {
|
||||
__u32 out_sample_rate_min;
|
||||
__u32 out_sample_rate_max;
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/** struct snd_codec_desc - description of codec capabilities
|
||||
@@ -347,6 +355,9 @@ union snd_codec_options {
|
||||
* @modes: Supported modes. See SND_AUDIOMODE defines
|
||||
* @formats: Supported formats. See SND_AUDIOSTREAMFORMAT defines
|
||||
* @min_buffer: Minimum buffer size handled by codec implementation
|
||||
* @pcm_formats: Output (for decoders) or input (for encoders)
|
||||
* PCM formats (required to accel mode, 0 for other modes)
|
||||
* @u_space: union space (for codec dependent data)
|
||||
* @reserved: reserved for future use
|
||||
*
|
||||
* This structure provides a scalar value for profiles, modes and stream
|
||||
@@ -370,7 +381,12 @@ struct snd_codec_desc {
|
||||
__u32 modes;
|
||||
__u32 formats;
|
||||
__u32 min_buffer;
|
||||
__u32 reserved[15];
|
||||
__u32 pcm_formats;
|
||||
union {
|
||||
__u32 u_space[6];
|
||||
struct snd_codec_desc_src src;
|
||||
} __attribute__((packed, aligned(4)));
|
||||
__u32 reserved[8];
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/** struct snd_codec
|
||||
@@ -395,6 +411,8 @@ struct snd_codec_desc {
|
||||
* @align: Block alignment in bytes of an audio sample.
|
||||
* Only required for PCM or IEC formats.
|
||||
* @options: encoder-specific settings
|
||||
* @pcm_format: Output (for decoders) or input (for encoders)
|
||||
* PCM formats (required to accel mode, 0 for other modes)
|
||||
* @reserved: reserved for future use
|
||||
*/
|
||||
|
||||
@@ -411,7 +429,8 @@ struct snd_codec {
|
||||
__u32 format;
|
||||
__u32 align;
|
||||
union snd_codec_options options;
|
||||
__u32 reserved[3];
|
||||
__u32 pcm_format;
|
||||
__u32 reserved[2];
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,8 @@ config SND_SOC_FSL_ASRC
|
||||
depends on HAS_DMA
|
||||
select REGMAP_MMIO
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
select SND_COMPRESS_ACCEL
|
||||
select SND_COMPRESS_OFFLOAD
|
||||
help
|
||||
Say Y if you want to add Asynchronous Sample Rate Converter (ASRC)
|
||||
support for the Freescale CPUs.
|
||||
|
||||
@@ -10,7 +10,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
|
||||
# Freescale SSI/DMA/SAI/SPDIF Support
|
||||
snd-soc-fsl-audmix-y := fsl_audmix.o
|
||||
snd-soc-fsl-asoc-card-y := fsl-asoc-card.o
|
||||
snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o
|
||||
snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o fsl_asrc_m2m.o
|
||||
snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o
|
||||
snd-soc-fsl-sai-y := fsl_sai.o
|
||||
snd-soc-fsl-ssi-y := fsl_ssi.o
|
||||
|
||||
+175
-4
@@ -1063,6 +1063,139 @@ static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
|
||||
return REG_ASRDx(dir, index);
|
||||
}
|
||||
|
||||
/* Get sample numbers in FIFO */
|
||||
static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
u32 val;
|
||||
|
||||
regmap_read(asrc->regmap, REG_ASRFST(index), &val);
|
||||
|
||||
val &= ASRFSTi_OUTPUT_FIFO_MASK;
|
||||
|
||||
return val >> ASRFSTi_OUTPUT_FIFO_SHIFT;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_asrc_pair_priv *pair_priv = pair->private;
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
struct asrc_config config;
|
||||
int ret;
|
||||
|
||||
/* fill config */
|
||||
config.pair = pair->index;
|
||||
config.channel_num = pair->channels;
|
||||
config.input_sample_rate = pair->rate[IN];
|
||||
config.output_sample_rate = pair->rate[OUT];
|
||||
config.input_format = pair->sample_format[IN];
|
||||
config.output_format = pair->sample_format[OUT];
|
||||
config.inclk = INCLK_NONE;
|
||||
config.outclk = OUTCLK_ASRCK1_CLK;
|
||||
|
||||
pair_priv->config = &config;
|
||||
ret = fsl_asrc_config_pair(pair, true);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to config pair: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pair->first_convert = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_start(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
if (pair->first_convert) {
|
||||
fsl_asrc_start_pair(pair);
|
||||
pair->first_convert = 0;
|
||||
}
|
||||
/*
|
||||
* Clear DMA request during the stall state of ASRC:
|
||||
* During STALL state, the remaining in input fifo would never be
|
||||
* smaller than the input threshold while the output fifo would not
|
||||
* be bigger than output one. Thus the DMA request would be cleared.
|
||||
*/
|
||||
fsl_asrc_set_watermarks(pair, ASRC_FIFO_THRESHOLD_MIN,
|
||||
ASRC_FIFO_THRESHOLD_MAX);
|
||||
|
||||
/* Update the real input threshold to raise DMA request */
|
||||
fsl_asrc_set_watermarks(pair, ASRC_M2M_INPUTFIFO_WML,
|
||||
ASRC_M2M_OUTPUTFIFO_WML);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_stop(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
if (!pair->first_convert) {
|
||||
fsl_asrc_stop_pair(pair);
|
||||
pair->first_convert = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* calculate capture data length according to output data length and sample rate */
|
||||
static int fsl_asrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length)
|
||||
{
|
||||
unsigned int in_width, out_width;
|
||||
unsigned int channels = pair->channels;
|
||||
unsigned int in_samples, out_samples;
|
||||
unsigned int out_length;
|
||||
|
||||
in_width = snd_pcm_format_physical_width(pair->sample_format[IN]) / 8;
|
||||
out_width = snd_pcm_format_physical_width(pair->sample_format[OUT]) / 8;
|
||||
|
||||
in_samples = input_buffer_length / in_width / channels;
|
||||
out_samples = pair->rate[OUT] * in_samples / pair->rate[IN];
|
||||
out_length = (out_samples - ASRC_OUTPUT_LAST_SAMPLE) * out_width * channels;
|
||||
|
||||
return out_length;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
struct fsl_asrc_priv *asrc_priv = asrc->private;
|
||||
int wml = (dir == IN) ? ASRC_M2M_INPUTFIFO_WML : ASRC_M2M_OUTPUTFIFO_WML;
|
||||
|
||||
if (!asrc_priv->soc->use_edma)
|
||||
return wml * pair->channels;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap)
|
||||
{
|
||||
cap->fmt_in = FSL_ASRC_FORMATS;
|
||||
cap->fmt_out = FSL_ASRC_FORMATS | SNDRV_PCM_FMTBIT_S8;
|
||||
|
||||
cap->rate_in = supported_asrc_rate;
|
||||
cap->rate_in_count = ARRAY_SIZE(supported_asrc_rate);
|
||||
cap->rate_out = supported_asrc_rate;
|
||||
cap->rate_out_count = ARRAY_SIZE(supported_asrc_rate);
|
||||
cap->chan_min = 1;
|
||||
cap->chan_max = 10;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_pair_resume(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pair->channels * 4; i++)
|
||||
regmap_write(asrc->regmap, REG_ASRDI(pair->index), 0);
|
||||
|
||||
pair->first_convert = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_runtime_resume(struct device *dev);
|
||||
static int fsl_asrc_runtime_suspend(struct device *dev);
|
||||
|
||||
@@ -1147,6 +1280,15 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
||||
asrc->get_fifo_addr = fsl_asrc_get_fifo_addr;
|
||||
asrc->pair_priv_size = sizeof(struct fsl_asrc_pair_priv);
|
||||
|
||||
asrc->m2m_prepare = fsl_asrc_m2m_prepare;
|
||||
asrc->m2m_start = fsl_asrc_m2m_start;
|
||||
asrc->m2m_stop = fsl_asrc_m2m_stop;
|
||||
asrc->get_output_fifo_size = fsl_asrc_get_output_fifo_size;
|
||||
asrc->m2m_calc_out_len = fsl_asrc_m2m_calc_out_len;
|
||||
asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst;
|
||||
asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume;
|
||||
asrc->m2m_get_cap = fsl_asrc_m2m_get_cap;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
|
||||
asrc_priv->clk_map[IN] = input_clk_map_imx35;
|
||||
asrc_priv->clk_map[OUT] = output_clk_map_imx35;
|
||||
@@ -1242,6 +1384,12 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
||||
goto err_pm_get_sync;
|
||||
}
|
||||
|
||||
ret = fsl_asrc_m2m_init(asrc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pm_get_sync:
|
||||
@@ -1254,6 +1402,10 @@ err_pm_disable:
|
||||
|
||||
static void fsl_asrc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_asrc *asrc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
fsl_asrc_m2m_exit(asrc);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
fsl_asrc_runtime_suspend(&pdev->dev);
|
||||
@@ -1355,10 +1507,29 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_asrc *asrc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
fsl_asrc_m2m_suspend(asrc);
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_asrc_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_asrc *asrc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
fsl_asrc_m2m_resume(asrc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops fsl_asrc_pm = {
|
||||
SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
|
||||
};
|
||||
|
||||
static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
|
||||
@@ -1396,7 +1567,7 @@ static struct platform_driver fsl_asrc_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-asrc",
|
||||
.of_match_table = fsl_asrc_ids,
|
||||
.pm = &fsl_asrc_pm,
|
||||
.pm = pm_ptr(&fsl_asrc_pm),
|
||||
},
|
||||
};
|
||||
module_platform_driver(fsl_asrc_driver);
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include "fsl_asrc_common.h"
|
||||
|
||||
#define ASRC_M2M_INPUTFIFO_WML 0x4
|
||||
#define ASRC_M2M_OUTPUTFIFO_WML 0x2
|
||||
#define ASRC_DMA_BUFFER_NUM 2
|
||||
#define ASRC_INPUTFIFO_THRESHOLD 32
|
||||
#define ASRC_OUTPUTFIFO_THRESHOLD 32
|
||||
|
||||
@@ -21,6 +21,26 @@ enum asrc_pair_index {
|
||||
|
||||
#define PAIR_CTX_NUM 0x4
|
||||
|
||||
/**
|
||||
* struct fsl_asrc_m2m_cap - capability data
|
||||
* @fmt_in: input sample format
|
||||
* @fmt_out: output sample format
|
||||
* @chan_min: minimum channel number
|
||||
* @chan_max: maximum channel number
|
||||
* @rate_in: minimum rate
|
||||
* @rate_out: maximum rete
|
||||
*/
|
||||
struct fsl_asrc_m2m_cap {
|
||||
u64 fmt_in;
|
||||
u64 fmt_out;
|
||||
int chan_min;
|
||||
int chan_max;
|
||||
const unsigned int *rate_in;
|
||||
int rate_in_count;
|
||||
const unsigned int *rate_out;
|
||||
int rate_out_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* fsl_asrc_pair: ASRC Pair common data
|
||||
*
|
||||
@@ -34,6 +54,14 @@ enum asrc_pair_index {
|
||||
* @pos: hardware pointer position
|
||||
* @req_dma_chan: flag to release dev_to_dev chan
|
||||
* @private: pair private area
|
||||
* @complete: dma task complete
|
||||
* @sample_format: format of m2m
|
||||
* @rate: rate of m2m
|
||||
* @buf_len: buffer length of m2m
|
||||
* @dma_buffer: buffer pointers
|
||||
* @first_convert: start of conversion
|
||||
* @ratio_mod_flag: flag for new ratio modifier
|
||||
* @ratio_mod: ratio modification
|
||||
*/
|
||||
struct fsl_asrc_pair {
|
||||
struct fsl_asrc *asrc;
|
||||
@@ -49,6 +77,16 @@ struct fsl_asrc_pair {
|
||||
bool req_dma_chan;
|
||||
|
||||
void *private;
|
||||
|
||||
/* used for m2m */
|
||||
struct completion complete[2];
|
||||
snd_pcm_format_t sample_format[2];
|
||||
unsigned int rate[2];
|
||||
unsigned int buf_len[2];
|
||||
struct snd_dma_buffer dma_buffer[2];
|
||||
unsigned int first_convert;
|
||||
bool ratio_mod_flag;
|
||||
unsigned int ratio_mod;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -62,6 +100,7 @@ struct fsl_asrc_pair {
|
||||
* @mem_clk: clock source to access register
|
||||
* @ipg_clk: clock source to drive peripheral
|
||||
* @spba_clk: SPBA clock (optional, depending on SoC design)
|
||||
* @card: compress sound card
|
||||
* @lock: spin lock for resource protection
|
||||
* @pair: pair pointers
|
||||
* @channel_avail: non-occupied channel numbers
|
||||
@@ -72,6 +111,17 @@ struct fsl_asrc_pair {
|
||||
* @request_pair: function pointer
|
||||
* @release_pair: function pointer
|
||||
* @get_fifo_addr: function pointer
|
||||
* @m2m_get_cap: function pointer
|
||||
* @m2m_prepare: function pointer
|
||||
* @m2m_start: function pointer
|
||||
* @m2m_unprepare: function pointer
|
||||
* @m2m_stop: function pointer
|
||||
* @m2m_calc_out_len: function pointer
|
||||
* @m2m_get_maxburst: function pointer
|
||||
* @m2m_pair_suspend: function pointer
|
||||
* @m2m_pair_resume: function pointer
|
||||
* @m2m_set_ratio_mod: function pointer
|
||||
* @get_output_fifo_size: function pointer
|
||||
* @pair_priv_size: size of pair private struct.
|
||||
* @private: private data structure
|
||||
*/
|
||||
@@ -84,6 +134,7 @@ struct fsl_asrc {
|
||||
struct clk *mem_clk;
|
||||
struct clk *ipg_clk;
|
||||
struct clk *spba_clk;
|
||||
struct snd_card *card;
|
||||
spinlock_t lock; /* spin lock for resource protection */
|
||||
|
||||
struct fsl_asrc_pair *pair[PAIR_CTX_NUM];
|
||||
@@ -97,6 +148,20 @@ struct fsl_asrc {
|
||||
int (*request_pair)(int channels, struct fsl_asrc_pair *pair);
|
||||
void (*release_pair)(struct fsl_asrc_pair *pair);
|
||||
int (*get_fifo_addr)(u8 dir, enum asrc_pair_index index);
|
||||
int (*m2m_get_cap)(struct fsl_asrc_m2m_cap *cap);
|
||||
|
||||
int (*m2m_prepare)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_start)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_unprepare)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_stop)(struct fsl_asrc_pair *pair);
|
||||
|
||||
int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length);
|
||||
int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair);
|
||||
int (*m2m_pair_suspend)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_pair_resume)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_set_ratio_mod)(struct fsl_asrc_pair *pair, int val);
|
||||
|
||||
unsigned int (*get_output_fifo_size)(struct fsl_asrc_pair *pair);
|
||||
size_t pair_priv_size;
|
||||
|
||||
void *private;
|
||||
@@ -105,4 +170,9 @@ struct fsl_asrc {
|
||||
#define DRV_NAME "fsl-asrc-dai"
|
||||
extern struct snd_soc_component_driver fsl_asrc_component;
|
||||
|
||||
int fsl_asrc_m2m_init(struct fsl_asrc *asrc);
|
||||
void fsl_asrc_m2m_exit(struct fsl_asrc *asrc);
|
||||
int fsl_asrc_m2m_resume(struct fsl_asrc *asrc);
|
||||
int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc);
|
||||
|
||||
#endif /* _FSL_ASRC_COMMON_H */
|
||||
|
||||
@@ -0,0 +1,727 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
|
||||
// Copyright (C) 2019-2024 NXP
|
||||
//
|
||||
// Freescale ASRC Memory to Memory (M2M) driver
|
||||
|
||||
#include <linux/dma/imx-dma.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/asound.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include "fsl_asrc_common.h"
|
||||
|
||||
#define DIR_STR(dir) (dir) == IN ? "in" : "out"
|
||||
|
||||
#define ASRC_xPUT_DMA_CALLBACK(dir) \
|
||||
(((dir) == IN) ? asrc_input_dma_callback \
|
||||
: asrc_output_dma_callback)
|
||||
|
||||
/* Maximum output and capture buffer size */
|
||||
#define ASRC_M2M_BUFFER_SIZE (512 * 1024)
|
||||
|
||||
/* Maximum output and capture period size */
|
||||
#define ASRC_M2M_PERIOD_SIZE (48 * 1024)
|
||||
|
||||
/* dma complete callback */
|
||||
static void asrc_input_dma_callback(void *data)
|
||||
{
|
||||
struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
|
||||
|
||||
complete(&pair->complete[IN]);
|
||||
}
|
||||
|
||||
/* dma complete callback */
|
||||
static void asrc_output_dma_callback(void *data)
|
||||
{
|
||||
struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
|
||||
|
||||
complete(&pair->complete[OUT]);
|
||||
}
|
||||
|
||||
/**
|
||||
*asrc_read_last_fifo: read all the remaining data from FIFO
|
||||
*@pair: Structure pointer of fsl_asrc_pair
|
||||
*@dma_vaddr: virtual address of capture buffer
|
||||
*@length: payload length of capture buffer
|
||||
*/
|
||||
static void asrc_read_last_fifo(struct fsl_asrc_pair *pair, void *dma_vaddr, u32 *length)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
u32 i, reg, size, t_size = 0, width;
|
||||
u32 *reg32 = NULL;
|
||||
u16 *reg16 = NULL;
|
||||
u8 *reg24 = NULL;
|
||||
|
||||
width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
|
||||
if (width == 32)
|
||||
reg32 = dma_vaddr + *length;
|
||||
else if (width == 16)
|
||||
reg16 = dma_vaddr + *length;
|
||||
else
|
||||
reg24 = dma_vaddr + *length;
|
||||
retry:
|
||||
size = asrc->get_output_fifo_size(pair);
|
||||
if (size + *length > ASRC_M2M_BUFFER_SIZE)
|
||||
goto end;
|
||||
|
||||
for (i = 0; i < size * pair->channels; i++) {
|
||||
regmap_read(asrc->regmap, asrc->get_fifo_addr(OUT, index), ®);
|
||||
if (reg32) {
|
||||
*reg32++ = reg;
|
||||
} else if (reg16) {
|
||||
*reg16++ = (u16)reg;
|
||||
} else {
|
||||
*reg24++ = (u8)reg;
|
||||
*reg24++ = (u8)(reg >> 8);
|
||||
*reg24++ = (u8)(reg >> 16);
|
||||
}
|
||||
}
|
||||
t_size += size;
|
||||
|
||||
/* In case there is data left in FIFO */
|
||||
if (size)
|
||||
goto retry;
|
||||
end:
|
||||
/* Update payload length */
|
||||
if (reg32)
|
||||
*length += t_size * pair->channels * 4;
|
||||
else if (reg16)
|
||||
*length += t_size * pair->channels * 2;
|
||||
else
|
||||
*length += t_size * pair->channels * 3;
|
||||
}
|
||||
|
||||
/* config dma channel */
|
||||
static int asrc_dmaconfig(struct fsl_asrc_pair *pair,
|
||||
struct dma_chan *chan,
|
||||
u32 dma_addr, dma_addr_t buf_addr, u32 buf_len,
|
||||
int dir, int width)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
struct dma_slave_config slave_config;
|
||||
enum dma_slave_buswidth buswidth;
|
||||
unsigned int sg_len, max_period_size;
|
||||
struct scatterlist *sg;
|
||||
int ret, i;
|
||||
|
||||
switch (width) {
|
||||
case 8:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
break;
|
||||
case 16:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
break;
|
||||
case 24:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
|
||||
break;
|
||||
case 32:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid word width\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&slave_config, 0, sizeof(slave_config));
|
||||
if (dir == IN) {
|
||||
slave_config.direction = DMA_MEM_TO_DEV;
|
||||
slave_config.dst_addr = dma_addr;
|
||||
slave_config.dst_addr_width = buswidth;
|
||||
slave_config.dst_maxburst = asrc->m2m_get_maxburst(IN, pair);
|
||||
} else {
|
||||
slave_config.direction = DMA_DEV_TO_MEM;
|
||||
slave_config.src_addr = dma_addr;
|
||||
slave_config.src_addr_width = buswidth;
|
||||
slave_config.src_maxburst = asrc->m2m_get_maxburst(OUT, pair);
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(chan, &slave_config);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to config dmaengine for %s task: %d\n",
|
||||
DIR_STR(dir), ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
max_period_size = rounddown(ASRC_M2M_PERIOD_SIZE, width * pair->channels / 8);
|
||||
/* scatter gather mode */
|
||||
sg_len = buf_len / max_period_size;
|
||||
if (buf_len % max_period_size)
|
||||
sg_len += 1;
|
||||
|
||||
sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL);
|
||||
if (!sg)
|
||||
return -ENOMEM;
|
||||
|
||||
sg_init_table(sg, sg_len);
|
||||
for (i = 0; i < (sg_len - 1); i++) {
|
||||
sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
|
||||
sg_dma_len(&sg[i]) = max_period_size;
|
||||
}
|
||||
sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
|
||||
sg_dma_len(&sg[i]) = buf_len - i * max_period_size;
|
||||
|
||||
pair->desc[dir] = dmaengine_prep_slave_sg(chan, sg, sg_len,
|
||||
slave_config.direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
kfree(sg);
|
||||
if (!pair->desc[dir]) {
|
||||
dev_err(dev, "failed to prepare dmaengine for %s task\n", DIR_STR(dir));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir);
|
||||
pair->desc[dir]->callback_param = pair;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* main function of converter */
|
||||
static void asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task_runtime *task)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
struct snd_dma_buffer *src_buf, *dst_buf;
|
||||
unsigned int in_buf_len;
|
||||
unsigned int out_dma_len;
|
||||
unsigned int width;
|
||||
u32 fifo_addr;
|
||||
int ret;
|
||||
|
||||
/* set ratio mod */
|
||||
if (asrc->m2m_set_ratio_mod) {
|
||||
if (pair->ratio_mod_flag) {
|
||||
asrc->m2m_set_ratio_mod(pair, pair->ratio_mod);
|
||||
pair->ratio_mod_flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
src_buf = &pair->dma_buffer[IN];
|
||||
dst_buf = &pair->dma_buffer[OUT];
|
||||
|
||||
width = snd_pcm_format_physical_width(pair->sample_format[IN]);
|
||||
fifo_addr = asrc->paddr + asrc->get_fifo_addr(IN, index);
|
||||
|
||||
in_buf_len = task->input_size;
|
||||
|
||||
if (in_buf_len < width * pair->channels / 8 ||
|
||||
in_buf_len > ASRC_M2M_BUFFER_SIZE ||
|
||||
in_buf_len % (width * pair->channels / 8)) {
|
||||
dev_err(dev, "out buffer size is error: [%d]\n", in_buf_len);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* dma config for output dma channel */
|
||||
ret = asrc_dmaconfig(pair,
|
||||
pair->dma_chan[IN],
|
||||
fifo_addr,
|
||||
src_buf->addr,
|
||||
in_buf_len, IN, width);
|
||||
if (ret) {
|
||||
dev_err(dev, "out dma config error\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
|
||||
fifo_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index);
|
||||
out_dma_len = asrc->m2m_calc_out_len(pair, in_buf_len);
|
||||
if (out_dma_len > 0 && out_dma_len <= ASRC_M2M_BUFFER_SIZE) {
|
||||
/* dma config for capture dma channel */
|
||||
ret = asrc_dmaconfig(pair,
|
||||
pair->dma_chan[OUT],
|
||||
fifo_addr,
|
||||
dst_buf->addr,
|
||||
out_dma_len, OUT, width);
|
||||
if (ret) {
|
||||
dev_err(dev, "cap dma config error\n");
|
||||
goto end;
|
||||
}
|
||||
} else if (out_dma_len > ASRC_M2M_BUFFER_SIZE) {
|
||||
dev_err(dev, "cap buffer size error\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
reinit_completion(&pair->complete[IN]);
|
||||
reinit_completion(&pair->complete[OUT]);
|
||||
|
||||
/* Submit DMA request */
|
||||
dmaengine_submit(pair->desc[IN]);
|
||||
dma_async_issue_pending(pair->desc[IN]->chan);
|
||||
if (out_dma_len > 0) {
|
||||
dmaengine_submit(pair->desc[OUT]);
|
||||
dma_async_issue_pending(pair->desc[OUT]->chan);
|
||||
}
|
||||
|
||||
asrc->m2m_start(pair);
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) {
|
||||
dev_err(dev, "out DMA task timeout\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (out_dma_len > 0) {
|
||||
if (!wait_for_completion_interruptible_timeout(&pair->complete[OUT], 10 * HZ)) {
|
||||
dev_err(dev, "cap DMA task timeout\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* read the last words from FIFO */
|
||||
asrc_read_last_fifo(pair, dst_buf->area, &out_dma_len);
|
||||
/* update payload length for capture */
|
||||
task->output_size = out_dma_len;
|
||||
end:
|
||||
return;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_open(struct snd_compr_stream *stream)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
struct fsl_asrc_pair *pair;
|
||||
int size, ret;
|
||||
|
||||
pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL);
|
||||
if (!pair)
|
||||
return -ENOMEM;
|
||||
|
||||
pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
|
||||
pair->asrc = asrc;
|
||||
|
||||
init_completion(&pair->complete[IN]);
|
||||
init_completion(&pair->complete[OUT]);
|
||||
|
||||
runtime->private_data = pair;
|
||||
|
||||
size = ASRC_M2M_BUFFER_SIZE;
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[IN]);
|
||||
if (ret)
|
||||
goto error_alloc_in_buf;
|
||||
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[OUT]);
|
||||
if (ret)
|
||||
goto error_alloc_out_buf;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to power up asrc\n");
|
||||
goto err_pm_runtime;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pm_runtime:
|
||||
snd_dma_free_pages(&pair->dma_buffer[OUT]);
|
||||
error_alloc_out_buf:
|
||||
snd_dma_free_pages(&pair->dma_buffer[IN]);
|
||||
error_alloc_in_buf:
|
||||
kfree(pair);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_release(struct snd_compr_stream *stream)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
snd_dma_free_pages(&pair->dma_buffer[IN]);
|
||||
snd_dma_free_pages(&pair->dma_buffer[OUT]);
|
||||
|
||||
kfree(runtime->private_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream,
|
||||
struct snd_compr_params *params)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
struct fsl_asrc_m2m_cap cap;
|
||||
int ret, i;
|
||||
|
||||
ret = asrc->m2m_get_cap(&cap);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
if (pcm_format_to_bits(params->codec.format) & cap.fmt_in)
|
||||
pair->sample_format[IN] = params->codec.format;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (pcm_format_to_bits(params->codec.pcm_format) & cap.fmt_out)
|
||||
pair->sample_format[OUT] = params->codec.pcm_format;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* check input rate is in scope */
|
||||
for (i = 0; i < cap.rate_in_count; i++)
|
||||
if (params->codec.sample_rate == cap.rate_in[i]) {
|
||||
pair->rate[IN] = params->codec.sample_rate;
|
||||
break;
|
||||
}
|
||||
if (i == cap.rate_in_count)
|
||||
return -EINVAL;
|
||||
|
||||
/* check output rate is in scope */
|
||||
for (i = 0; i < cap.rate_out_count; i++)
|
||||
if (params->codec.options.src_d.out_sample_rate == cap.rate_out[i]) {
|
||||
pair->rate[OUT] = params->codec.options.src_d.out_sample_rate;
|
||||
break;
|
||||
}
|
||||
if (i == cap.rate_out_count)
|
||||
return -EINVAL;
|
||||
|
||||
if (params->codec.ch_in != params->codec.ch_out ||
|
||||
params->codec.ch_in < cap.chan_min ||
|
||||
params->codec.ch_in > cap.chan_max)
|
||||
return -EINVAL;
|
||||
|
||||
pair->channels = params->codec.ch_in;
|
||||
pair->buf_len[IN] = params->buffer.fragment_size;
|
||||
pair->buf_len[OUT] = params->buffer.fragment_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_dma_buffer *dmab = dmabuf->priv;
|
||||
|
||||
return snd_dma_buffer_mmap(dmab, vma);
|
||||
}
|
||||
|
||||
static struct sg_table *fsl_asrc_m2m_map_dma_buf(struct dma_buf_attachment *attachment,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct snd_dma_buffer *dmab = attachment->dmabuf->priv;
|
||||
struct sg_table *sgt;
|
||||
|
||||
sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
|
||||
if (!sgt)
|
||||
return NULL;
|
||||
|
||||
if (dma_get_sgtable(attachment->dev, sgt, dmab->area, dmab->addr, dmab->bytes) < 0)
|
||||
goto free;
|
||||
|
||||
if (dma_map_sgtable(attachment->dev, sgt, direction, 0))
|
||||
goto free;
|
||||
|
||||
return sgt;
|
||||
|
||||
free:
|
||||
sg_free_table(sgt);
|
||||
kfree(sgt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fsl_asrc_m2m_unmap_dma_buf(struct dma_buf_attachment *attachment,
|
||||
struct sg_table *table,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
dma_unmap_sgtable(attachment->dev, table, direction, 0);
|
||||
}
|
||||
|
||||
static void fsl_asrc_m2m_release(struct dma_buf *dmabuf)
|
||||
{
|
||||
/* buffer is released by fsl_asrc_m2m_comp_release() */
|
||||
}
|
||||
|
||||
static const struct dma_buf_ops fsl_asrc_m2m_dma_buf_ops = {
|
||||
.mmap = fsl_asrc_m2m_mmap,
|
||||
.map_dma_buf = fsl_asrc_m2m_map_dma_buf,
|
||||
.unmap_dma_buf = fsl_asrc_m2m_unmap_dma_buf,
|
||||
.release = fsl_asrc_m2m_release,
|
||||
};
|
||||
|
||||
static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream,
|
||||
struct snd_compr_task_runtime *task)
|
||||
{
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info_in);
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info_out);
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
int ret;
|
||||
|
||||
exp_info_in.ops = &fsl_asrc_m2m_dma_buf_ops;
|
||||
exp_info_in.size = ASRC_M2M_BUFFER_SIZE;
|
||||
exp_info_in.flags = O_RDWR;
|
||||
exp_info_in.priv = &pair->dma_buffer[IN];
|
||||
task->input = dma_buf_export(&exp_info_in);
|
||||
if (IS_ERR(task->input)) {
|
||||
ret = PTR_ERR(task->input);
|
||||
return ret;
|
||||
}
|
||||
|
||||
exp_info_out.ops = &fsl_asrc_m2m_dma_buf_ops;
|
||||
exp_info_out.size = ASRC_M2M_BUFFER_SIZE;
|
||||
exp_info_out.flags = O_RDWR;
|
||||
exp_info_out.priv = &pair->dma_buffer[OUT];
|
||||
task->output = dma_buf_export(&exp_info_out);
|
||||
if (IS_ERR(task->output)) {
|
||||
ret = PTR_ERR(task->output);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Request asrc pair/context */
|
||||
ret = asrc->request_pair(pair->channels, pair);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request pair: %d\n", ret);
|
||||
goto err_request_pair;
|
||||
}
|
||||
|
||||
ret = asrc->m2m_prepare(pair);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to start pair part one: %d\n", ret);
|
||||
goto err_start_part_one;
|
||||
}
|
||||
|
||||
/* Request dma channels */
|
||||
pair->dma_chan[IN] = asrc->get_dma_channel(pair, IN);
|
||||
if (!pair->dma_chan[IN]) {
|
||||
dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index);
|
||||
ret = -EBUSY;
|
||||
goto err_dma_channel_in;
|
||||
}
|
||||
|
||||
pair->dma_chan[OUT] = asrc->get_dma_channel(pair, OUT);
|
||||
if (!pair->dma_chan[OUT]) {
|
||||
dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index);
|
||||
ret = -EBUSY;
|
||||
goto err_dma_channel_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_dma_channel_out:
|
||||
dma_release_channel(pair->dma_chan[IN]);
|
||||
err_dma_channel_in:
|
||||
if (asrc->m2m_unprepare)
|
||||
asrc->m2m_unprepare(pair);
|
||||
err_start_part_one:
|
||||
asrc->release_pair(pair);
|
||||
err_request_pair:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_task_start(struct snd_compr_stream *stream,
|
||||
struct snd_compr_task_runtime *task)
|
||||
{
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
|
||||
asrc_m2m_device_run(pair, task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_task_stop(struct snd_compr_stream *stream,
|
||||
struct snd_compr_task_runtime *task)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_task_free(struct snd_compr_stream *stream,
|
||||
struct snd_compr_task_runtime *task)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
|
||||
/* Stop & release pair/context */
|
||||
if (asrc->m2m_stop)
|
||||
asrc->m2m_stop(pair);
|
||||
|
||||
if (asrc->m2m_unprepare)
|
||||
asrc->m2m_unprepare(pair);
|
||||
asrc->release_pair(pair);
|
||||
|
||||
/* Release dma channel */
|
||||
if (pair->dma_chan[IN])
|
||||
dma_release_channel(pair->dma_chan[IN]);
|
||||
if (pair->dma_chan[OUT])
|
||||
dma_release_channel(pair->dma_chan[OUT]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_get_caps(struct snd_compr_stream *cstream,
|
||||
struct snd_compr_caps *caps)
|
||||
{
|
||||
caps->num_codecs = 1;
|
||||
caps->min_fragment_size = 4096;
|
||||
caps->max_fragment_size = 4096;
|
||||
caps->min_fragments = 1;
|
||||
caps->max_fragments = 1;
|
||||
caps->codecs[0] = SND_AUDIOCODEC_PCM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
|
||||
struct snd_compr_codec_caps *codec)
|
||||
{
|
||||
struct fsl_asrc_m2m_cap cap;
|
||||
snd_pcm_format_t k;
|
||||
int j = 0;
|
||||
int ret;
|
||||
|
||||
ret = asrc->m2m_get_cap(&cap);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
pcm_for_each_format(k) {
|
||||
if (pcm_format_to_bits(k) & cap.fmt_in) {
|
||||
codec->descriptor[j].max_ch = cap.chan_max;
|
||||
memcpy(codec->descriptor[j].sample_rates,
|
||||
cap.rate_in,
|
||||
cap.rate_in_count * sizeof(__u32));
|
||||
codec->descriptor[j].num_sample_rates = cap.rate_in_count;
|
||||
codec->descriptor[j].formats = k;
|
||||
codec->descriptor[j].pcm_formats = cap.fmt_out;
|
||||
codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0];
|
||||
codec->descriptor[j].src.out_sample_rate_max =
|
||||
cap.rate_out[cap.rate_out_count - 1];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
codec->codec = SND_AUDIOCODEC_PCM;
|
||||
codec->num_descriptors = j;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_get_codec_caps(struct snd_compr_stream *stream,
|
||||
struct snd_compr_codec_caps *codec)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
|
||||
return fsl_asrc_m2m_fill_codec_caps(asrc, codec);
|
||||
}
|
||||
|
||||
static struct snd_compr_ops fsl_asrc_m2m_compr_ops = {
|
||||
.open = fsl_asrc_m2m_comp_open,
|
||||
.free = fsl_asrc_m2m_comp_release,
|
||||
.set_params = fsl_asrc_m2m_comp_set_params,
|
||||
.get_caps = fsl_asrc_m2m_get_caps,
|
||||
.get_codec_caps = fsl_asrc_m2m_get_codec_caps,
|
||||
.task_create = fsl_asrc_m2m_comp_task_create,
|
||||
.task_start = fsl_asrc_m2m_comp_task_start,
|
||||
.task_stop = fsl_asrc_m2m_comp_task_stop,
|
||||
.task_free = fsl_asrc_m2m_comp_task_free,
|
||||
};
|
||||
|
||||
int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc)
|
||||
{
|
||||
struct fsl_asrc_pair *pair;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PAIR_CTX_NUM; i++) {
|
||||
pair = asrc->pair[i];
|
||||
if (!pair)
|
||||
continue;
|
||||
if (!completion_done(&pair->complete[IN])) {
|
||||
if (pair->dma_chan[IN])
|
||||
dmaengine_terminate_all(pair->dma_chan[IN]);
|
||||
asrc_input_dma_callback((void *)pair);
|
||||
}
|
||||
if (!completion_done(&pair->complete[OUT])) {
|
||||
if (pair->dma_chan[OUT])
|
||||
dmaengine_terminate_all(pair->dma_chan[OUT]);
|
||||
asrc_output_dma_callback((void *)pair);
|
||||
}
|
||||
|
||||
if (asrc->m2m_pair_suspend)
|
||||
asrc->m2m_pair_suspend(pair);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_suspend);
|
||||
|
||||
int fsl_asrc_m2m_resume(struct fsl_asrc *asrc)
|
||||
{
|
||||
struct fsl_asrc_pair *pair;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PAIR_CTX_NUM; i++) {
|
||||
pair = asrc->pair[i];
|
||||
if (!pair)
|
||||
continue;
|
||||
if (asrc->m2m_pair_resume)
|
||||
asrc->m2m_pair_resume(pair);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_resume);
|
||||
|
||||
int fsl_asrc_m2m_init(struct fsl_asrc *asrc)
|
||||
{
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
struct snd_card *card;
|
||||
struct snd_compr *compr;
|
||||
int ret;
|
||||
|
||||
ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
|
||||
THIS_MODULE, 0, &card);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
strscpy(card->driver, "fsl-asrc-m2m", sizeof(card->driver));
|
||||
strscpy(card->shortname, "ASRC-M2M", sizeof(card->shortname));
|
||||
strscpy(card->longname, "ASRC-M2M", sizeof(card->shortname));
|
||||
|
||||
asrc->card = card;
|
||||
|
||||
compr = devm_kzalloc(dev, sizeof(*compr), GFP_KERNEL);
|
||||
if (!compr) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
compr->ops = &fsl_asrc_m2m_compr_ops;
|
||||
compr->private_data = asrc;
|
||||
|
||||
ret = snd_compress_new(card, 0, SND_COMPRESS_ACCEL, "ASRC M2M", compr);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = snd_card_register(card);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
snd_card_free(card);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_init);
|
||||
|
||||
void fsl_asrc_m2m_exit(struct fsl_asrc *asrc)
|
||||
{
|
||||
struct snd_card *card = asrc->card;
|
||||
|
||||
snd_card_free(card);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_exit);
|
||||
|
||||
MODULE_IMPORT_NS("DMA_BUF");
|
||||
MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
|
||||
MODULE_DESCRIPTION("Freescale ASRC M2M driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
+259
-2
@@ -1861,6 +1861,224 @@ static int fsl_easrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
|
||||
return REG_EASRC_FIFO(dir, index);
|
||||
}
|
||||
|
||||
/* Get sample numbers in FIFO */
|
||||
static unsigned int fsl_easrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
u32 val;
|
||||
|
||||
regmap_read(asrc->regmap, REG_EASRC_SFS(index), &val);
|
||||
val &= EASRC_SFS_NSGO_MASK;
|
||||
|
||||
return val >> EASRC_SFS_NSGO_SHIFT;
|
||||
}
|
||||
|
||||
static int fsl_easrc_m2m_prepare(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
int ret;
|
||||
|
||||
ctx_priv->in_params.sample_rate = pair->rate[IN];
|
||||
ctx_priv->in_params.sample_format = pair->sample_format[IN];
|
||||
ctx_priv->out_params.sample_rate = pair->rate[OUT];
|
||||
ctx_priv->out_params.sample_format = pair->sample_format[OUT];
|
||||
|
||||
ctx_priv->in_params.fifo_wtmk = FSL_EASRC_INPUTFIFO_WML;
|
||||
ctx_priv->out_params.fifo_wtmk = FSL_EASRC_OUTPUTFIFO_WML;
|
||||
/* Fill the right half of the re-sampler with zeros */
|
||||
ctx_priv->rs_init_mode = 0x2;
|
||||
/* Zero fill the right half of the prefilter */
|
||||
ctx_priv->pf_init_mode = 0x2;
|
||||
|
||||
ret = fsl_easrc_set_ctx_format(pair,
|
||||
&ctx_priv->in_params.sample_format,
|
||||
&ctx_priv->out_params.sample_format);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set context format: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fsl_easrc_config_context(asrc, pair->index);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to config context %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx_priv->in_params.iterations = 1;
|
||||
ctx_priv->in_params.group_len = pair->channels;
|
||||
ctx_priv->in_params.access_len = pair->channels;
|
||||
ctx_priv->out_params.iterations = 1;
|
||||
ctx_priv->out_params.group_len = pair->channels;
|
||||
ctx_priv->out_params.access_len = pair->channels;
|
||||
|
||||
ret = fsl_easrc_set_ctx_organziation(pair);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set fifo organization\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The context start flag */
|
||||
pair->first_convert = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_m2m_start(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
/* start context once */
|
||||
if (pair->first_convert) {
|
||||
fsl_easrc_start_context(pair);
|
||||
pair->first_convert = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_m2m_stop(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
/* Stop pair/context */
|
||||
if (!pair->first_convert) {
|
||||
fsl_easrc_stop_context(pair);
|
||||
pair->first_convert = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* calculate capture data length according to output data length and sample rate */
|
||||
static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length)
|
||||
{
|
||||
struct fsl_asrc *easrc = pair->asrc;
|
||||
struct fsl_easrc_priv *easrc_priv = easrc->private;
|
||||
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
|
||||
unsigned int in_rate = ctx_priv->in_params.norm_rate;
|
||||
unsigned int out_rate = ctx_priv->out_params.norm_rate;
|
||||
unsigned int channels = pair->channels;
|
||||
unsigned int in_samples, out_samples;
|
||||
unsigned int in_width, out_width;
|
||||
unsigned int out_length;
|
||||
unsigned int frac_bits;
|
||||
u64 val1, val2;
|
||||
|
||||
switch (easrc_priv->rs_num_taps) {
|
||||
case EASRC_RS_32_TAPS:
|
||||
/* integer bits = 5; */
|
||||
frac_bits = 39;
|
||||
break;
|
||||
case EASRC_RS_64_TAPS:
|
||||
/* integer bits = 6; */
|
||||
frac_bits = 38;
|
||||
break;
|
||||
case EASRC_RS_128_TAPS:
|
||||
/* integer bits = 7; */
|
||||
frac_bits = 37;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val1 = (u64)in_rate << frac_bits;
|
||||
do_div(val1, out_rate);
|
||||
val1 += (s64)ctx_priv->ratio_mod << (frac_bits - 31);
|
||||
|
||||
in_width = snd_pcm_format_physical_width(ctx_priv->in_params.sample_format) / 8;
|
||||
out_width = snd_pcm_format_physical_width(ctx_priv->out_params.sample_format) / 8;
|
||||
|
||||
ctx_priv->in_filled_len += input_buffer_length;
|
||||
if (ctx_priv->in_filled_len <= ctx_priv->in_filled_sample * in_width * channels) {
|
||||
out_length = 0;
|
||||
} else {
|
||||
in_samples = ctx_priv->in_filled_len / (in_width * channels) -
|
||||
ctx_priv->in_filled_sample;
|
||||
|
||||
/* right shift 12 bit to make ratio in 32bit space */
|
||||
val2 = (u64)in_samples << (frac_bits - 12);
|
||||
val1 = val1 >> 12;
|
||||
do_div(val2, val1);
|
||||
out_samples = val2;
|
||||
|
||||
out_length = out_samples * out_width * channels;
|
||||
ctx_priv->in_filled_len = ctx_priv->in_filled_sample * in_width * channels;
|
||||
}
|
||||
|
||||
return out_length;
|
||||
}
|
||||
|
||||
static int fsl_easrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
|
||||
|
||||
if (dir == IN)
|
||||
return ctx_priv->in_params.fifo_wtmk * pair->channels;
|
||||
else
|
||||
return ctx_priv->out_params.fifo_wtmk * pair->channels;
|
||||
}
|
||||
|
||||
static int fsl_easrc_m2m_pair_suspend(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
fsl_easrc_stop_context(pair);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_m2m_pair_resume(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
|
||||
|
||||
pair->first_convert = 1;
|
||||
ctx_priv->in_filled_len = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* val is Q31 */
|
||||
static int fsl_easrc_m2m_set_ratio_mod(struct fsl_asrc_pair *pair, int val)
|
||||
{
|
||||
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
|
||||
struct fsl_asrc *easrc = pair->asrc;
|
||||
struct fsl_easrc_priv *easrc_priv = easrc->private;
|
||||
unsigned int frac_bits;
|
||||
|
||||
ctx_priv->ratio_mod += val;
|
||||
|
||||
switch (easrc_priv->rs_num_taps) {
|
||||
case EASRC_RS_32_TAPS:
|
||||
/* integer bits = 5; */
|
||||
frac_bits = 39;
|
||||
break;
|
||||
case EASRC_RS_64_TAPS:
|
||||
/* integer bits = 6; */
|
||||
frac_bits = 38;
|
||||
break;
|
||||
case EASRC_RS_128_TAPS:
|
||||
/* integer bits = 7; */
|
||||
frac_bits = 37;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val <<= (frac_bits - 31);
|
||||
regmap_write(easrc->regmap, REG_EASRC_RUC(pair->index), EASRC_RSUC_RS_RM(val));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap)
|
||||
{
|
||||
cap->fmt_in = FSL_EASRC_FORMATS;
|
||||
cap->fmt_out = FSL_EASRC_FORMATS | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
|
||||
cap->rate_in = easrc_rates;
|
||||
cap->rate_in_count = ARRAY_SIZE(easrc_rates);
|
||||
cap->rate_out = easrc_rates;
|
||||
cap->rate_out_count = ARRAY_SIZE(easrc_rates);
|
||||
cap->chan_min = 1;
|
||||
cap->chan_max = 32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_easrc_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8mn-easrc",},
|
||||
{}
|
||||
@@ -1926,6 +2144,16 @@ static int fsl_easrc_probe(struct platform_device *pdev)
|
||||
easrc->release_pair = fsl_easrc_release_context;
|
||||
easrc->get_fifo_addr = fsl_easrc_get_fifo_addr;
|
||||
easrc->pair_priv_size = sizeof(struct fsl_easrc_ctx_priv);
|
||||
easrc->m2m_prepare = fsl_easrc_m2m_prepare;
|
||||
easrc->m2m_start = fsl_easrc_m2m_start;
|
||||
easrc->m2m_stop = fsl_easrc_m2m_stop;
|
||||
easrc->get_output_fifo_size = fsl_easrc_get_output_fifo_size;
|
||||
easrc->m2m_calc_out_len = fsl_easrc_m2m_calc_out_len;
|
||||
easrc->m2m_get_maxburst = fsl_easrc_m2m_get_maxburst;
|
||||
easrc->m2m_pair_suspend = fsl_easrc_m2m_pair_suspend;
|
||||
easrc->m2m_pair_resume = fsl_easrc_m2m_pair_resume;
|
||||
easrc->m2m_set_ratio_mod = fsl_easrc_m2m_set_ratio_mod;
|
||||
easrc->m2m_get_cap = fsl_easrc_m2m_get_cap;
|
||||
|
||||
easrc_priv->rs_num_taps = EASRC_RS_32_TAPS;
|
||||
easrc_priv->const_coeff = 0x3FF0000000000000;
|
||||
@@ -1976,6 +2204,12 @@ static int fsl_easrc_probe(struct platform_device *pdev)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
ret = fsl_asrc_m2m_init(easrc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pm_disable:
|
||||
@@ -1985,6 +2219,10 @@ err_pm_disable:
|
||||
|
||||
static void fsl_easrc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_asrc *easrc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
fsl_asrc_m2m_exit(easrc);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
@@ -2085,10 +2323,29 @@ disable_mem_clk:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_easrc_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_asrc *easrc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
fsl_asrc_m2m_suspend(easrc);
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_easrc_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_asrc *easrc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
fsl_asrc_m2m_resume(easrc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops fsl_easrc_pm_ops = {
|
||||
RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, fsl_easrc_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SYSTEM_SLEEP_PM_OPS(fsl_easrc_suspend, fsl_easrc_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_easrc_driver = {
|
||||
|
||||
@@ -601,6 +601,8 @@ struct fsl_easrc_slot {
|
||||
* @out_missed_sample: sample missed in output
|
||||
* @st1_addexp: exponent added for stage1
|
||||
* @st2_addexp: exponent added for stage2
|
||||
* @ratio_mod: update ratio
|
||||
* @in_filled_len: input filled length
|
||||
*/
|
||||
struct fsl_easrc_ctx_priv {
|
||||
struct fsl_easrc_io_params in_params;
|
||||
@@ -618,6 +620,8 @@ struct fsl_easrc_ctx_priv {
|
||||
int out_missed_sample;
|
||||
int st1_addexp;
|
||||
int st2_addexp;
|
||||
int ratio_mod;
|
||||
unsigned int in_filled_len;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user