1
0
Fork 0

Merge remote-tracking branch 'origin/audio/asrc' into audio/next

* origin/audio/asrc: (68 commits)
  MLK-22722: ASoC: fsl_easrc: Use global variable instead local variable
  MLK-22591: ASoC: fsl_easrc: Add RUN_STOP in stop context
  MLK-22575-3: ASoC: fsl_easrc: reformat the fsl_easrc_config_slot
  MLK-22575-2: ASoC: fsl_easrc: disable PF_BYPASS_MODE
  MLK-22575-1: ASoC: fsl_easrc: configure slot according to pf memory size
  ...
5.4-rM2-2.2.x-imx-squashed
Dong Aisheng 2019-12-02 18:00:37 +08:00
commit b1d0e4ce42
13 changed files with 6445 additions and 190 deletions

View File

@ -8,7 +8,8 @@ three substreams within totally 10 channels.
Required properties:
- compatible : Contains "fsl,imx35-asrc" or "fsl,imx53-asrc".
- compatible : Contains "fsl,imx35-asrc", "fsl,imx53-asrc",
"fsl,imx8qm-asrc0" or "fsl,imx8qm-asrc1".
- reg : Offset and length of the register set for the device.

View File

@ -0,0 +1,53 @@
Freescale Asynchronous Sample Rate Converter (ASRC) Controller
The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
signal associated with an input clock into a signal associated with a different
output clock. The driver currently works as a Front End of DPCM with other Back
Ends Audio controller such as ESAI, SSI and SAI. It has four context to support
four substreams within totally 32 channels.
Required properties:
- compatible : Contains "fsl,imx8mn-easrc".
- reg : Offset and length of the register set for the device.
- interrupts : Contains the asrc interrupt.
- dmas : Generic dma devicetree binding as described in
Documentation/devicetree/bindings/dma/dma.txt.
- dma-names : Contains "ctx0_rx", "ctx0_tx", "ctx1_rx", "ctx1_tx",
"ctx2_rx", "ctx2_tx", "ctx3_rx", "ctx3_tx".
- clocks : Contains an entry for each entry in clock-names.
- clock-names : Contains the following entries
"mem" Peripheral clock to driver module.
- fsl,easrc-ram-script-name : The coefficient table for the filters
- fsl,asrc-rate : Defines a mutual sample rate used by DPCM Back Ends.
- fsl,asrc-width : Defines a mutual sample width used by DPCM Back Ends.
Example:
easrc: easrc@300C0000 {
compatible = "fsl,imx8mn-easrc";
reg = <0x0 0x300C0000 0x0 0x10000>;
interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
clock-names = "mem";
dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
<&sdma2 18 23 0> , <&sdma2 19 23 0>,
<&sdma2 20 23 0> , <&sdma2 21 23 0>,
<&sdma2 22 23 0> , <&sdma2 23 23 0>;
dma-names = "ctx0_rx", "ctx0_tx",
"ctx1_rx", "ctx1_tx",
"ctx2_rx", "ctx2_tx",
"ctx3_rx", "ctx3_tx";
fsl,easrc-ram-script-name = "imx/easrc/easrc-imx8mn.bin";
fsl,asrc-rate = <8000>;
fsl,asrc-width = <16>;
status = "disabled";
};

View File

@ -0,0 +1,165 @@
/*
* Copyright 2008-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*
* @file mxc_asrc.h
*
* @brief i.MX Asynchronous Sample Rate Converter
*
* @ingroup Audio
*/
#ifndef __MXC_ASRC_UAPI_H__
#define __MXC_ASRC_UAPI_H__
#define ASRC_IOC_MAGIC 'C'
#define ASRC_REQ_PAIR _IOWR(ASRC_IOC_MAGIC, 0, struct asrc_req)
#define ASRC_CONFIG_PAIR _IOWR(ASRC_IOC_MAGIC, 1, struct asrc_config)
#define ASRC_RELEASE_PAIR _IOW(ASRC_IOC_MAGIC, 2, enum asrc_pair_index)
#define ASRC_CONVERT _IOW(ASRC_IOC_MAGIC, 3, struct asrc_convert_buffer)
#define ASRC_START_CONV _IOW(ASRC_IOC_MAGIC, 4, enum asrc_pair_index)
#define ASRC_STOP_CONV _IOW(ASRC_IOC_MAGIC, 5, enum asrc_pair_index)
#define ASRC_STATUS _IOW(ASRC_IOC_MAGIC, 6, struct asrc_status_flags)
#define ASRC_FLUSH _IOW(ASRC_IOC_MAGIC, 7, enum asrc_pair_index)
enum asrc_pair_index {
ASRC_INVALID_PAIR = -1,
ASRC_PAIR_A = 0,
ASRC_PAIR_B = 1,
ASRC_PAIR_C = 2,
ASRC_PAIR_D = 3,
};
enum asrc_inclk {
INCLK_NONE = 0x03,
INCLK_ESAI_RX = 0x00,
INCLK_SSI1_RX = 0x01,
INCLK_SSI2_RX = 0x02,
INCLK_SSI3_RX = 0x07,
INCLK_SPDIF_RX = 0x04,
INCLK_MLB_CLK = 0x05,
INCLK_PAD = 0x06,
INCLK_ESAI_TX = 0x08,
INCLK_SSI1_TX = 0x09,
INCLK_SSI2_TX = 0x0a,
INCLK_SSI3_TX = 0x0b,
INCLK_SPDIF_TX = 0x0c,
INCLK_ASRCK1_CLK = 0x0f,
/* imx8 */
INCLK_AUD_PLL_DIV_CLK0 = 0x10,
INCLK_AUD_PLL_DIV_CLK1 = 0x11,
INCLK_AUD_CLK0 = 0x12,
INCLK_AUD_CLK1 = 0x13,
INCLK_ESAI0_RX_CLK = 0x14,
INCLK_ESAI0_TX_CLK = 0x15,
INCLK_SPDIF0_RX = 0x16,
INCLK_SPDIF1_RX = 0x17,
INCLK_SAI0_RX_BCLK = 0x18,
INCLK_SAI0_TX_BCLK = 0x19,
INCLK_SAI1_RX_BCLK = 0x1a,
INCLK_SAI1_TX_BCLK = 0x1b,
INCLK_SAI2_RX_BCLK = 0x1c,
INCLK_SAI3_RX_BCLK = 0x1d,
INCLK_ASRC0_MUX_CLK = 0x1e,
INCLK_ESAI1_RX_CLK = 0x20,
INCLK_ESAI1_TX_CLK = 0x21,
INCLK_SAI6_TX_BCLK = 0x22,
INCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
INCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
};
enum asrc_outclk {
OUTCLK_NONE = 0x03,
OUTCLK_ESAI_TX = 0x00,
OUTCLK_SSI1_TX = 0x01,
OUTCLK_SSI2_TX = 0x02,
OUTCLK_SSI3_TX = 0x07,
OUTCLK_SPDIF_TX = 0x04,
OUTCLK_MLB_CLK = 0x05,
OUTCLK_PAD = 0x06,
OUTCLK_ESAI_RX = 0x08,
OUTCLK_SSI1_RX = 0x09,
OUTCLK_SSI2_RX = 0x0a,
OUTCLK_SSI3_RX = 0x0b,
OUTCLK_SPDIF_RX = 0x0c,
OUTCLK_ASRCK1_CLK = 0x0f,
/* imx8 */
OUTCLK_AUD_PLL_DIV_CLK0 = 0x10,
OUTCLK_AUD_PLL_DIV_CLK1 = 0x11,
OUTCLK_AUD_CLK0 = 0x12,
OUTCLK_AUD_CLK1 = 0x13,
OUTCLK_ESAI0_RX_CLK = 0x14,
OUTCLK_ESAI0_TX_CLK = 0x15,
OUTCLK_SPDIF0_RX = 0x16,
OUTCLK_SPDIF1_RX = 0x17,
OUTCLK_SAI0_RX_BCLK = 0x18,
OUTCLK_SAI0_TX_BCLK = 0x19,
OUTCLK_SAI1_RX_BCLK = 0x1a,
OUTCLK_SAI1_TX_BCLK = 0x1b,
OUTCLK_SAI2_RX_BCLK = 0x1c,
OUTCLK_SAI3_RX_BCLK = 0x1d,
OUTCLK_ASRCO_MUX_CLK = 0x1e,
OUTCLK_ESAI1_RX_CLK = 0x20,
OUTCLK_ESAI1_TX_CLK = 0x21,
OUTCLK_SAI6_TX_BCLK = 0x22,
OUTCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
OUTCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
};
struct asrc_config {
enum asrc_pair_index pair;
unsigned int channel_num;
unsigned int dma_buffer_size;
unsigned int input_sample_rate;
unsigned int output_sample_rate;
snd_pcm_format_t input_format;
snd_pcm_format_t output_format;
enum asrc_inclk inclk;
enum asrc_outclk outclk;
};
struct asrc_req {
unsigned int chn_num;
enum asrc_pair_index index;
uint64_t supported_in_format;
uint64_t supported_out_format;
};
struct asrc_querybuf {
unsigned int buffer_index;
unsigned int input_length;
unsigned int output_length;
unsigned long input_offset;
unsigned long output_offset;
};
struct asrc_convert_buffer {
void *input_buffer_vaddr;
void *output_buffer_vaddr;
unsigned int input_buffer_length;
unsigned int output_buffer_length;
};
struct asrc_status_flags {
enum asrc_pair_index index;
unsigned int overload_error;
};
enum asrc_error_status {
ASRC_TASK_Q_OVERLOAD = 0x01,
ASRC_OUTPUT_TASK_OVERLOAD = 0x02,
ASRC_INPUT_TASK_OVERLOAD = 0x04,
ASRC_OUTPUT_BUFFER_OVERFLOW = 0x08,
ASRC_INPUT_BUFFER_UNDERRUN = 0x10,
};
#endif/* __MXC_ASRC_UAPI_H__ */

View File

@ -74,6 +74,16 @@ config SND_SOC_FSL_MICFIL
Say Y if you want to add Pulse Density Modulation microphone
interface (MICFIL) support for NXP.
config SND_SOC_FSL_EASRC
tristate "Enhanced ASRC module support"
select REGMAP_MMIO
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y if you want to add Enhanced ASRC support for NXP. The ASRC is
a digital module that converts audio from a source sample rate to a
destination sample rate. It is a new design module compare with the
old ASRC.
config SND_SOC_FSL_UTILS
tristate

View File

@ -23,6 +23,7 @@ snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-micfil-objs := fsl_micfil.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
snd-soc-fsl-easrc-objs := fsl_easrc.o fsl_easrc_dma.o
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
@ -34,6 +35,7 @@ obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
obj-$(CONFIG_SND_SOC_FSL_EASRC) += snd-soc-fsl-easrc.o
# MPC5200 Platform Support
obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o

View File

@ -2,7 +2,8 @@
//
// Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
//
// Copyright (C) 2014 Freescale Semiconductor, Inc.
// Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
// Copyright 2017 NXP
//
// Author: Nicolin Chen <nicoleotsuka@gmail.com>
@ -13,6 +14,9 @@
#include <linux/of_platform.h>
#include <linux/platform_data/dma-imx.h>
#include <linux/pm_runtime.h>
#include <linux/miscdevice.h>
#include <linux/sched/signal.h>
#include <linux/pm_domain.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@ -23,6 +27,9 @@
#define pair_err(fmt, ...) \
dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
#define pair_warn(fmt, ...) \
dev_warn(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
#define pair_dbg(fmt, ...) \
dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
@ -41,26 +48,58 @@ static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = {
* The following tables map the relationship between asrc_inclk/asrc_outclk in
* fsl_asrc.h and the registers of ASRCSR
*/
#define CLK_MAP_NUM 48
static unsigned char input_clk_map_imx35[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
};
static unsigned char output_clk_map_imx35[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
};
/* i.MX53 uses the same map for input and output */
static unsigned char input_clk_map_imx53[] = {
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
};
static unsigned char output_clk_map_imx53[] = {
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
};
static unsigned char *clk_map[2];
/* i.MX8 uses the same map for input and output */
static unsigned char input_clk_map_imx8_0[] = {
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
};
static unsigned char output_clk_map_imx8_0[] = {
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
};
static unsigned char input_clk_map_imx8_1[] = {
0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
};
static unsigned char output_clk_map_imx8_1[] = {
0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
};
/**
* Select the pre-processing and post-processing options
@ -115,7 +154,7 @@ static void fsl_asrc_sel_proc(int inrate, int outrate,
* within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
* while pair A and pair C are comparatively independent.
*/
static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
{
enum asrc_pair_index index = ASRC_INVALID_PAIR;
struct fsl_asrc *asrc_priv = pair->asrc_priv;
@ -158,7 +197,7 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
*
* It clears the resource from asrc_priv and releases the occupied channels.
*/
static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc_priv = pair->asrc_priv;
enum asrc_pair_index index = pair->index;
@ -260,17 +299,20 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
* of struct asrc_config which includes in/output sample rate, width, channel
* and clock settings.
*/
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool p2p_in, bool p2p_out)
{
struct asrc_config *config = pair->config;
struct fsl_asrc *asrc_priv = pair->asrc_priv;
enum asrc_pair_index index = pair->index;
u32 inrate, outrate, indiv, outdiv;
u32 clk_index[2], div[2];
u32 clk_index[2], div[2], rem[2];
u64 clk_rate;
int in, out, channels;
int pre_proc, post_proc;
struct clk *clk;
bool ideal;
enum asrc_word_width input_word_width;
enum asrc_word_width output_word_width;
if (!config) {
pair_err("invalid pair config\n");
@ -283,9 +325,32 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
return -EINVAL;
}
/* Validate output width */
if (config->output_word_width == ASRC_WIDTH_8_BIT) {
pair_err("does not support 8bit width output\n");
switch (snd_pcm_format_width(config->input_format)) {
case 8:
input_word_width = ASRC_WIDTH_8_BIT;
break;
case 16:
input_word_width = ASRC_WIDTH_16_BIT;
break;
case 24:
input_word_width = ASRC_WIDTH_24_BIT;
break;
default:
pair_err("does not support this input format, %d\n",
config->input_format);
return -EINVAL;
}
switch (snd_pcm_format_width(config->output_format)) {
case 16:
output_word_width = ASRC_WIDTH_16_BIT;
break;
case 24:
output_word_width = ASRC_WIDTH_24_BIT;
break;
default:
pair_err("does not support this output format, %d\n",
config->output_format);
return -EINVAL;
}
@ -320,13 +385,14 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
}
/* Validate input and output clock sources */
clk_index[IN] = clk_map[IN][config->inclk];
clk_index[OUT] = clk_map[OUT][config->outclk];
clk_index[IN] = asrc_priv->clk_map[IN][config->inclk];
clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk];
/* We only have output clock for ideal ratio mode */
clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
div[IN] = clk_get_rate(clk) / inrate;
clk_rate = clk_get_rate(clk);
rem[IN] = do_div(clk_rate, inrate);
div[IN] = (u32)clk_rate;
if (div[IN] == 0) {
pair_err("failed to support input sample rate %dHz by asrck_%x\n",
inrate, clk_index[ideal ? OUT : IN]);
@ -335,11 +401,20 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
clk = asrc_priv->asrck_clk[clk_index[OUT]];
/* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
if (ideal)
div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
else
div[OUT] = clk_get_rate(clk) / outrate;
/*
* When P2P mode, output rate should align with the out samplerate.
* if set too high output rate, there will be lots of Overload.
* When M2M mode, output rate should also need to align with the out
* samplerate, but M2M must use less time to achieve good performance.
*/
clk_rate = clk_get_rate(clk);
if (p2p_out || p2p_in || (!ideal)) {
rem[OUT] = do_div(clk_rate, outrate);
div[OUT] = clk_rate;
} else {
rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
div[OUT] = clk_rate;
}
if (div[OUT] == 0) {
pair_err("failed to support output sample rate %dHz by asrck_%x\n",
@ -347,6 +422,23 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
return -EINVAL;
}
if (!ideal && (div[IN] > 1024 || div[OUT] > 1024 ||
rem[IN] != 0 || rem[OUT] != 0)) {
pair_err("The divider can't be used for non ideal mode\n");
return -EINVAL;
}
if (ideal && div[IN] > 1024 && div[OUT] > 1024) {
pair_warn("both divider (%d, %d) are larger than threshold\n",
div[IN], div[OUT]);
}
if (div[IN] > 1024)
div[IN] = 1024;
if (div[OUT] > 1024)
div[OUT] = 1024;
/* Set the channel number */
channels = config->channel_num;
@ -361,8 +453,11 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
/* Default setting: Automatic selection for processing mode */
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
/* Default setting: use internal measured ratio */
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
ASRCTR_USRi_MASK(index), 0);
ASRCTR_USRi_MASK(index) | ASRCTR_IDRi_MASK(index),
ASRCTR_USR(index));
/* Set the input and output clock sources */
regmap_update_bits(asrc_priv->regmap, REG_ASRCSR,
@ -383,8 +478,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
/* Implement word_width configurations */
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
ASRMCR1i_OW16(config->output_word_width) |
ASRMCR1i_IWD(config->input_word_width));
ASRMCR1i_OW16(output_word_width) |
ASRMCR1i_IWD(input_word_width));
/* Enable BUFFER STALL */
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
@ -427,7 +522,7 @@ static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc_priv = pair->asrc_priv;
enum asrc_pair_index index = pair->index;
int reg, retry = 10, i;
int reg, retry = 50, i;
/* Enable the current pair */
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
@ -440,6 +535,9 @@ static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
reg &= ASRCFG_INIRQi_MASK(index);
} while (!reg && --retry);
if (retry == 0)
pair_warn("initialization is not finished\n");
/* Make the input fifo to ASRC STALL level */
regmap_read(asrc_priv->regmap, REG_ASRCNCR, &reg);
for (i = 0; i < pair->channels * 4; i++)
@ -492,18 +590,73 @@ static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_RATE, &fsl_asrc_rate_constraints);
}
static int fsl_asrc_select_clk(struct fsl_asrc *asrc_priv,
struct fsl_asrc_pair *pair,
int in_rate,
int out_rate)
{
struct asrc_config *config = pair->config;
int clk_rate;
int clk_index;
int i = 0, j = 0;
int rate[2];
int select_clk[2];
bool clk_sel[2];
rate[0] = in_rate;
rate[1] = out_rate;
/*select proper clock for asrc p2p mode*/
for (j = 0; j < 2; j++) {
for (i = 0; i < CLK_MAP_NUM; i++) {
clk_index = asrc_priv->clk_map[j][i];
clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]);
if (clk_rate != 0 && (clk_rate / rate[j]) <= 1024 &&
(clk_rate % rate[j]) == 0)
break;
}
if (i == CLK_MAP_NUM) {
select_clk[j] = OUTCLK_ASRCK1_CLK;
clk_sel[j] = false;
} else {
select_clk[j] = i;
clk_sel[j] = true;
}
}
if (clk_sel[0] != true || clk_sel[1] != true)
select_clk[IN] = INCLK_NONE;
config->inclk = select_clk[IN];
config->outclk = select_clk[OUT];
/*
* FIXME: workaroud for 176400/192000 with 8 channel input case
* the output sample rate is 48kHz.
* with ideal ratio mode, the asrc seems has performance issue
* that the output sound is not correct. so switch to non-ideal
* ratio mode
*/
if (config->channel_num >= 8 && config->input_sample_rate >= 176400
&& config->inclk == INCLK_NONE)
config->inclk = INCLK_ASRCK1_CLK;
return 0;
}
static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
int width = params_width(params);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
struct asrc_config config;
int word_width, ret;
snd_pcm_format_t format;
int ret;
ret = fsl_asrc_request_pair(channels, pair);
if (ret) {
@ -511,39 +664,56 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
return ret;
}
pair->pair_streams |= BIT(substream->stream);
pair->config = &config;
if (width == 16)
width = ASRC_WIDTH_16_BIT;
else
width = ASRC_WIDTH_24_BIT;
if (asrc_priv->asrc_width == 16)
word_width = ASRC_WIDTH_16_BIT;
format = SNDRV_PCM_FORMAT_S16_LE;
else
word_width = ASRC_WIDTH_24_BIT;
format = SNDRV_PCM_FORMAT_S24_LE;
config.pair = pair->index;
config.channel_num = channels;
config.inclk = INCLK_NONE;
config.outclk = OUTCLK_ASRCK1_CLK;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
config.input_word_width = width;
config.output_word_width = word_width;
config.input_format = params_format(params);
config.output_format = format;
config.input_sample_rate = rate;
config.output_sample_rate = asrc_priv->asrc_rate;
ret = fsl_asrc_select_clk(asrc_priv, pair,
config.input_sample_rate,
config.output_sample_rate);
if (ret) {
dev_err(dai->dev, "fail to select clock\n");
return ret;
}
ret = fsl_asrc_config_pair(pair, false, true);
if (ret) {
dev_err(dai->dev, "fail to config asrc pair\n");
return ret;
}
} else {
config.input_word_width = word_width;
config.output_word_width = width;
config.input_format = format;
config.output_format = params_format(params);
config.input_sample_rate = asrc_priv->asrc_rate;
config.output_sample_rate = rate;
}
ret = fsl_asrc_config_pair(pair);
if (ret) {
dev_err(dai->dev, "fail to config asrc pair\n");
return ret;
ret = fsl_asrc_select_clk(asrc_priv, pair,
config.input_sample_rate,
config.output_sample_rate);
if (ret) {
dev_err(dai->dev, "fail to select clock\n");
return ret;
}
ret = fsl_asrc_config_pair(pair, true, false);
if (ret) {
dev_err(dai->dev, "fail to config asrc pair\n");
return ret;
}
}
return 0;
@ -555,8 +725,10 @@ static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
if (pair)
if (pair && (pair->pair_streams & BIT(substream->stream))) {
fsl_asrc_release_pair(pair);
pair->pair_streams &= ~BIT(substream->stream);
}
return 0;
}
@ -572,6 +744,8 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
fsl_asrc_start_pair(pair);
/* Output enough data to content the DMA burstsize of BE */
mdelay(1);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
@ -602,9 +776,13 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
return 0;
}
#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
#define FSL_ASRC_FORMATS_RX (SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE)
SNDRV_PCM_FMTBIT_S24_3LE)
#define FSL_ASRC_FORMATS_TX (SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S24_3LE)
static struct snd_soc_dai_driver fsl_asrc_dai = {
.probe = fsl_asrc_dai_probe,
@ -615,7 +793,7 @@ static struct snd_soc_dai_driver fsl_asrc_dai = {
.rate_min = 5512,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = FSL_ASRC_FORMATS,
.formats = FSL_ASRC_FORMATS_TX,
},
.capture = {
.stream_name = "ASRC-Capture",
@ -624,7 +802,7 @@ static struct snd_soc_dai_driver fsl_asrc_dai = {
.rate_min = 5512,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = FSL_ASRC_FORMATS,
.formats = FSL_ASRC_FORMATS_RX,
},
.ops = &fsl_asrc_dai_ops,
};
@ -772,11 +950,15 @@ static const struct regmap_config fsl_asrc_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
#include "fsl_asrc_m2m.c"
/**
* Initialize ASRC registers with a default configurations
*/
static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
{
unsigned long ipg_rate;
/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
@ -794,11 +976,12 @@ static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1,
ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
/* Set the processing clock for 76KHz to 133M */
regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6);
/* Set the processing clock for 56KHz to 133M */
return regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947);
ipg_rate = clk_get_rate(asrc_priv->ipg_clk);
/* Set the period of the 76KHz and 56KHz sampling clocks based on
* the ASRC processing clock.
*/
regmap_write(asrc_priv->regmap, REG_ASR76K, ipg_rate / 76000);
return regmap_write(asrc_priv->regmap, REG_ASR56K, ipg_rate / 56000);
}
/**
@ -862,6 +1045,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
void __iomem *regs;
int irq, ret, i;
char tmp[16];
int num_domains = 0;
asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
if (!asrc_priv)
@ -920,14 +1104,52 @@ static int fsl_asrc_probe(struct platform_device *pdev)
}
}
num_domains = of_count_phandle_with_args(np, "power-domains",
"#power-domain-cells");
for (i = 0; i < num_domains; i++) {
struct device *pd_dev;
struct device_link *link;
pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i);
if (IS_ERR(pd_dev))
return PTR_ERR(pd_dev);
link = device_link_add(&pdev->dev, pd_dev,
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE);
if (IS_ERR(link))
return PTR_ERR(link);
}
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
asrc_priv->channel_bits = 3;
clk_map[IN] = input_clk_map_imx35;
clk_map[OUT] = output_clk_map_imx35;
} else {
strncpy(asrc_priv->name, "mxc_asrc",
sizeof(asrc_priv->name) - 1);
asrc_priv->clk_map[IN] = input_clk_map_imx35;
asrc_priv->clk_map[OUT] = output_clk_map_imx35;
asrc_priv->dma_type = DMA_SDMA;
} else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
asrc_priv->channel_bits = 4;
clk_map[IN] = input_clk_map_imx53;
clk_map[OUT] = output_clk_map_imx53;
strncpy(asrc_priv->name, "mxc_asrc",
sizeof(asrc_priv->name) - 1);
asrc_priv->clk_map[IN] = input_clk_map_imx53;
asrc_priv->clk_map[OUT] = output_clk_map_imx53;
asrc_priv->dma_type = DMA_SDMA;
} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc0")) {
asrc_priv->channel_bits = 4;
strncpy(asrc_priv->name, "mxc_asrc",
sizeof(asrc_priv->name) - 1);
asrc_priv->clk_map[IN] = input_clk_map_imx8_0;
asrc_priv->clk_map[OUT] = output_clk_map_imx8_0;
asrc_priv->dma_type = DMA_EDMA;
} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc1")) {
asrc_priv->channel_bits = 4;
strncpy(asrc_priv->name, "mxc_asrc1",
sizeof(asrc_priv->name) - 1);
asrc_priv->clk_map[IN] = input_clk_map_imx8_1;
asrc_priv->clk_map[OUT] = output_clk_map_imx8_1;
asrc_priv->dma_type = DMA_EDMA;
}
ret = fsl_asrc_init(asrc_priv);
@ -961,6 +1183,8 @@ static int fsl_asrc_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
spin_lock_init(&asrc_priv->lock);
regcache_cache_only(asrc_priv->regmap, true);
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
&fsl_asrc_dai, 1);
if (ret) {
@ -968,6 +1192,12 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return ret;
}
ret = fsl_asrc_m2m_init(asrc_priv);
if (ret) {
dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
return ret;
}
return 0;
}
@ -976,6 +1206,9 @@ static int fsl_asrc_runtime_resume(struct device *dev)
{
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
int i, ret;
u32 asrctr;
u32 reg;
int retry = 50;
ret = clk_prepare_enable(asrc_priv->mem_clk);
if (ret)
@ -994,6 +1227,34 @@ static int fsl_asrc_runtime_resume(struct device *dev)
goto disable_asrck_clk;
}
/* Stop all pairs provisionally */
regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr);
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_ALL_MASK, 0);
/* Restore all registers */
regcache_cache_only(asrc_priv->regmap, false);
regcache_mark_dirty(asrc_priv->regmap);
regcache_sync(asrc_priv->regmap);
regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
/* Restart enabled pairs */
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_ALL_MASK, asrctr);
/* Wait for status of initialization */
do {
udelay(5);
regmap_read(asrc_priv->regmap, REG_ASRCFG, &reg);
reg = (reg >> ASRCFG_INIRQi_SHIFT(0)) & 0x7;
} while (!(reg == ((asrctr & 0xE) >> 1)) && --retry);
if (retry == 0)
dev_warn(dev, "initialization is not finished\n");
return 0;
disable_asrck_clk:
@ -1013,6 +1274,11 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
int i;
regmap_read(asrc_priv->regmap, REG_ASRCFG,
&asrc_priv->regcache_cfg);
regcache_cache_only(asrc_priv->regmap, true);
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
if (!IS_ERR(asrc_priv->spba_clk))
@ -1028,39 +1294,25 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
static int fsl_asrc_suspend(struct device *dev)
{
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
int ret;
regmap_read(asrc_priv->regmap, REG_ASRCFG,
&asrc_priv->regcache_cfg);
fsl_asrc_m2m_suspend(asrc_priv);
regcache_cache_only(asrc_priv->regmap, true);
regcache_mark_dirty(asrc_priv->regmap);
ret = pm_runtime_force_suspend(dev);
return 0;
return ret;
}
static int fsl_asrc_resume(struct device *dev)
{
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
u32 asrctr;
int ret;
/* Stop all pairs provisionally */
regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr);
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_ALL_MASK, 0);
ret = pm_runtime_force_resume(dev);
/* Restore all registers */
regcache_cache_only(asrc_priv->regmap, false);
regcache_sync(asrc_priv->regmap);
fsl_asrc_m2m_resume(asrc_priv);
regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
/* Restart enabled pairs */
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_ALL_MASK, asrctr);
return 0;
return ret;
}
#endif /* CONFIG_PM_SLEEP */
@ -1072,12 +1324,15 @@ static const struct dev_pm_ops fsl_asrc_pm = {
static const struct of_device_id fsl_asrc_ids[] = {
{ .compatible = "fsl,imx35-asrc", },
{ .compatible = "fsl,imx53-asrc", },
{ .compatible = "fsl,imx8qm-asrc0", },
{ .compatible = "fsl,imx8qm-asrc1", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
static struct platform_driver fsl_asrc_driver = {
.probe = fsl_asrc_probe,
.remove = fsl_asrc_m2m_remove,
.driver = {
.name = "fsl-asrc",
.of_match_table = fsl_asrc_ids,

View File

@ -2,7 +2,7 @@
/*
* fsl_asrc.h - Freescale ASRC ALSA SoC header file
*
* Copyright (C) 2014 Freescale Semiconductor, Inc.
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
*
* Author: Nicolin Chen <nicoleotsuka@gmail.com>
*/
@ -10,6 +10,12 @@
#ifndef _FSL_ASRC_H
#define _FSL_ASRC_H
#include <sound/asound.h>
#include <uapi/linux/mxc_asrc.h>
#include <linux/miscdevice.h>
#define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1)
#define IN 0
#define OUT 1
@ -20,7 +26,8 @@
#define ASRC_FIFO_THRESHOLD_MAX 63
#define ASRC_DMA_BUFFER_SIZE (1024 * 48 * 4)
#define ASRC_MAX_BUFFER_SIZE (1024 * 48)
#define ASRC_OUTPUT_LAST_SAMPLE 8
#define ASRC_OUTPUT_LAST_SAMPLE_MAX 32
#define ASRC_OUTPUT_LAST_SAMPLE 4
#define IDEAL_RATIO_RATE 1000000
@ -283,106 +290,15 @@
#define ASRMCR1i_OW16_MASK (1 << ASRMCR1i_OW16_SHIFT)
#define ASRMCR1i_OW16(v) ((v) << ASRMCR1i_OW16_SHIFT)
enum asrc_pair_index {
ASRC_INVALID_PAIR = -1,
ASRC_PAIR_A = 0,
ASRC_PAIR_B = 1,
ASRC_PAIR_C = 2,
};
#define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1)
enum asrc_inclk {
INCLK_NONE = 0x03,
INCLK_ESAI_RX = 0x00,
INCLK_SSI1_RX = 0x01,
INCLK_SSI2_RX = 0x02,
INCLK_SSI3_RX = 0x07,
INCLK_SPDIF_RX = 0x04,
INCLK_MLB_CLK = 0x05,
INCLK_PAD = 0x06,
INCLK_ESAI_TX = 0x08,
INCLK_SSI1_TX = 0x09,
INCLK_SSI2_TX = 0x0a,
INCLK_SSI3_TX = 0x0b,
INCLK_SPDIF_TX = 0x0c,
INCLK_ASRCK1_CLK = 0x0f,
};
enum asrc_outclk {
OUTCLK_NONE = 0x03,
OUTCLK_ESAI_TX = 0x00,
OUTCLK_SSI1_TX = 0x01,
OUTCLK_SSI2_TX = 0x02,
OUTCLK_SSI3_TX = 0x07,
OUTCLK_SPDIF_TX = 0x04,
OUTCLK_MLB_CLK = 0x05,
OUTCLK_PAD = 0x06,
OUTCLK_ESAI_RX = 0x08,
OUTCLK_SSI1_RX = 0x09,
OUTCLK_SSI2_RX = 0x0a,
OUTCLK_SSI3_RX = 0x0b,
OUTCLK_SPDIF_RX = 0x0c,
OUTCLK_ASRCK1_CLK = 0x0f,
};
#define ASRC_CLK_MAX_NUM 16
enum asrc_word_width {
ASRC_WIDTH_24_BIT = 0,
ASRC_WIDTH_16_BIT = 1,
ASRC_WIDTH_8_BIT = 2,
};
struct asrc_config {
enum asrc_pair_index pair;
unsigned int channel_num;
unsigned int buffer_num;
unsigned int dma_buffer_size;
unsigned int input_sample_rate;
unsigned int output_sample_rate;
enum asrc_word_width input_word_width;
enum asrc_word_width output_word_width;
enum asrc_inclk inclk;
enum asrc_outclk outclk;
};
struct asrc_req {
unsigned int chn_num;
enum asrc_pair_index index;
};
struct asrc_querybuf {
unsigned int buffer_index;
unsigned int input_length;
unsigned int output_length;
unsigned long input_offset;
unsigned long output_offset;
};
struct asrc_convert_buffer {
void *input_buffer_vaddr;
void *output_buffer_vaddr;
unsigned int input_buffer_length;
unsigned int output_buffer_length;
};
struct asrc_status_flags {
enum asrc_pair_index index;
unsigned int overload_error;
};
enum asrc_error_status {
ASRC_TASK_Q_OVERLOAD = 0x01,
ASRC_OUTPUT_TASK_OVERLOAD = 0x02,
ASRC_INPUT_TASK_OVERLOAD = 0x04,
ASRC_OUTPUT_BUFFER_OVERFLOW = 0x08,
ASRC_INPUT_BUFFER_UNDERRUN = 0x10,
ASRC_WIDTH_8_BIT = 2,
};
struct dma_block {
dma_addr_t dma_paddr;
void *dma_vaddr;
unsigned int length;
};
@ -413,6 +329,7 @@ struct fsl_asrc_pair {
struct dma_chan *dma_chan[2];
struct imx_dma_data dma_data;
unsigned int pos;
unsigned int pair_streams;
void *private;
};
@ -433,6 +350,7 @@ struct fsl_asrc_pair {
* @pair: pair pointers
* @channel_bits: width of ASRCNCR register for each pair
* @channel_avail: non-occupied channel numbers
* @pair_streams:indicat which substream is running
* @asrc_rate: default sample rate for ASoC Back-Ends
* @asrc_width: default sample width for ASoC Back-Ends
* @regcache_cfg: store register value of REG_ASRCFG
@ -447,19 +365,29 @@ struct fsl_asrc {
struct clk *ipg_clk;
struct clk *spba_clk;
struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
unsigned char *clk_map[2];
spinlock_t lock;
struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM];
struct miscdevice asrc_miscdev;
unsigned int channel_bits;
unsigned int channel_avail;
int asrc_rate;
int asrc_width;
int dma_type; /* 0 is sdma, 1 is edma */
u32 regcache_cfg;
char name[20];
};
#define DMA_SDMA 0
#define DMA_EDMA 1
#define DRV_NAME "fsl-asrc-dai"
extern struct snd_soc_component_driver fsl_asrc_component;
struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair);
void fsl_asrc_release_pair(struct fsl_asrc_pair *pair);
#endif /* _FSL_ASRC_H */

View File

@ -16,16 +16,14 @@
#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
static const struct snd_pcm_hardware snd_imx_hardware = {
static struct snd_pcm_hardware snd_imx_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = 65535, /* Limited by SDMA engine */
.period_bytes_max = 65532, /* Limited by SDMA engine */
.periods_min = 2,
.periods_max = 255,
.fifo_size = 0,
@ -148,6 +146,7 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
struct device *dev_be;
u8 dir = tx ? OUT : IN;
dma_cap_mask_t mask;
enum sdma_peripheral_type be_peripheral_type;
int ret;
/* Fetch the Back-End dma_data from DPCM */
@ -201,19 +200,43 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
/* Get DMA request of Back-End */
tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
tmp_data = tmp_chan->private;
pair->dma_data.dma_request = tmp_data->dma_request;
dma_release_channel(tmp_chan);
if (tmp_chan) {
tmp_data = tmp_chan->private;
if (tmp_data) {
pair->dma_data.dma_request = tmp_data->dma_request;
be_peripheral_type = tmp_data->peripheral_type;
if (tx && be_peripheral_type == IMX_DMATYPE_SSI_DUAL)
pair->dma_data.dst_dualfifo = true;
if (!tx && be_peripheral_type == IMX_DMATYPE_SSI_DUAL)
pair->dma_data.src_dualfifo = true;
}
dma_release_channel(tmp_chan);
}
/* Get DMA request of Front-End */
tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
tmp_data = tmp_chan->private;
pair->dma_data.dma_request2 = tmp_data->dma_request;
pair->dma_data.peripheral_type = tmp_data->peripheral_type;
pair->dma_data.priority = tmp_data->priority;
dma_release_channel(tmp_chan);
if (tmp_chan) {
tmp_data = tmp_chan->private;
if (tmp_data) {
pair->dma_data.dma_request2 = tmp_data->dma_request;
pair->dma_data.peripheral_type =
tmp_data->peripheral_type;
pair->dma_data.priority = tmp_data->priority;
}
dma_release_channel(tmp_chan);
}
/* For sdma DEV_TO_DEV, there is two dma request
* But for emda DEV_TO_DEV, there is only one dma request, which is
* from the BE.
*/
if (pair->dma_data.dma_request2 != pair->dma_data.dma_request)
pair->dma_chan[dir] =
dma_request_channel(mask, filter, &pair->dma_data);
else
pair->dma_chan[dir] =
fsl_asrc_get_dma_channel(pair, dir);
pair->dma_chan[dir] = dma_request_channel(mask, filter, &pair->dma_data);
if (!pair->dma_chan[dir]) {
dev_err(dev, "failed to request DMA channel for Back-End\n");
return -EINVAL;
@ -224,6 +247,8 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
else
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
memset(&config_be, 0, sizeof(config_be));
config_be.direction = DMA_DEV_TO_DEV;
config_be.src_addr_width = buswidth;
config_be.src_maxburst = dma_params_be->maxburst;
@ -276,6 +301,16 @@ static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
struct device *dev = component->dev;
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
struct fsl_asrc_pair *pair;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u8 dir = tx ? OUT : IN;
struct dma_slave_caps dma_caps;
struct dma_chan *tmp_chan;
struct snd_dmaengine_dai_dma_data *dma_data;
u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
int ret;
int i;
pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
if (!pair)
@ -285,8 +320,78 @@ static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
runtime->private_data = pair;
snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
ret = snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0) {
dev_err(dev, "failed to set pcm hw params periods\n");
return ret;
}
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
fsl_asrc_request_pair(1, pair);
tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
if (!tmp_chan) {
dev_err(dev, "can't get dma channel\n");
return -EINVAL;
}
ret = dma_get_slave_caps(tmp_chan, &dma_caps);
if (ret == 0) {
if (dma_caps.cmd_pause)
snd_imx_hardware.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
snd_imx_hardware.info |= SNDRV_PCM_INFO_BATCH;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
addr_widths = dma_caps.dst_addr_widths;
else
addr_widths = dma_caps.src_addr_widths;
}
/*
* If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
* hw.formats set to 0, meaning no restrictions are in place.
* In this case it's the responsibility of the DAI driver to
* provide the supported format information.
*/
if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
/*
* Prepare formats mask for valid/allowed sample types. If the
* dma does not have support for the given physical word size,
* it needs to be masked out so user space can not use the
* format which produces corrupted audio.
* In case the dma driver does not implement the slave_caps the
* default assumption is that it supports 1, 2 and 4 bytes
* widths.
*/
for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
int bits = snd_pcm_format_physical_width(i);
/*
* Enable only samples with DMA supported physical
* widths
*/
switch (bits) {
case 8:
case 16:
case 24:
case 32:
case 64:
if (addr_widths & (1 << (bits / 8)))
snd_imx_hardware.formats |= (1LL << i);
break;
default:
/* Unsupported types */
break;
}
}
if (tmp_chan)
dma_release_channel(tmp_chan);
fsl_asrc_release_pair(pair);
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
return 0;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,698 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 NXP
*/
#ifndef _FSL_EASRC_H
#define _FSL_EASRC_H
#include <sound/asound.h>
#include <uapi/linux/mxc_asrc.h>
#include <linux/miscdevice.h>
#include <linux/platform_data/dma-imx.h>
/* EASRC Register Map */
/* ASRC Input Write FIFO */
#define REG_EASRC_WRFIFO(ctx) (0x000 + 4 * (ctx))
/* ASRC Output Read FIFO */
#define REG_EASRC_RDFIFO(ctx) (0x010 + 4 * (ctx))
/* ASRC Context Control */
#define REG_EASRC_CC(ctx) (0x020 + 4 * (ctx))
/* ASRC Context Control Extended 1 */
#define REG_EASRC_CCE1(ctx) (0x030 + 4 * (ctx))
/* ASRC Context Control Extended 2 */
#define REG_EASRC_CCE2(ctx) (0x040 + 4 * (ctx))
/* ASRC Control Input Access */
#define REG_EASRC_CIA(ctx) (0x050 + 4 * (ctx))
/* ASRC Datapath Processor Control Slot0 */
#define REG_EASRC_DPCS0R0(ctx) (0x060 + 4 * (ctx))
#define REG_EASRC_DPCS0R1(ctx) (0x070 + 4 * (ctx))
#define REG_EASRC_DPCS0R2(ctx) (0x080 + 4 * (ctx))
#define REG_EASRC_DPCS0R3(ctx) (0x090 + 4 * (ctx))
/* ASRC Datapath Processor Control Slot1 */
#define REG_EASRC_DPCS1R0(ctx) (0x0A0 + 4 * (ctx))
#define REG_EASRC_DPCS1R1(ctx) (0x0B0 + 4 * (ctx))
#define REG_EASRC_DPCS1R2(ctx) (0x0C0 + 4 * (ctx))
#define REG_EASRC_DPCS1R3(ctx) (0x0D0 + 4 * (ctx))
/* ASRC Context Output Control */
#define REG_EASRC_COC(ctx) (0x0E0 + 4 * (ctx))
/* ASRC Control Output Access */
#define REG_EASRC_COA(ctx) (0x0F0 + 4 * (ctx))
/* ASRC Sample FIFO Status */
#define REG_EASRC_SFS(ctx) (0x100 + 4 * (ctx))
/* ASRC Resampling Ratio Low */
#define REG_EASRC_RRL(ctx) (0x110 + 8 * (ctx))
/* ASRC Resampling Ratio High */
#define REG_EASRC_RRH(ctx) (0x114 + 8 * (ctx))
/* ASRC Resampling Ratio Update Control */
#define REG_EASRC_RUC(ctx) (0x130 + 4 * (ctx))
/* ASRC Resampling Ratio Update Rate */
#define REG_EASRC_RUR(ctx) (0x140 + 4 * (ctx))
/* ASRC Resampling Center Tap Coefficient Low */
#define REG_EASRC_RCTCL (0x150)
/* ASRC Resampling Center Tap Coefficient High */
#define REG_EASRC_RCTCH (0x154)
/* ASRC Prefilter Coefficient FIFO */
#define REG_EASRC_PCF(ctx) (0x160 + 4 * (ctx))
/* ASRC Context Resampling Coefficient Memory */
#define REG_EASRC_CRCM 0x170
/* ASRC Context Resampling Coefficient Control*/
#define REG_EASRC_CRCC 0x174
/* ASRC Interrupt Control */
#define REG_EASRC_IRQC 0x178
/* ASRC Interrupt Status Flags */
#define REG_EASRC_IRQF 0x17C
/* ASRC Channel Status 0 */
#define REG_EASRC_CS0(ctx) (0x180 + 4 * (ctx))
/* ASRC Channel Status 1 */
#define REG_EASRC_CS1(ctx) (0x190 + 4 * (ctx))
/* ASRC Channel Status 2 */
#define REG_EASRC_CS2(ctx) (0x1A0 + 4 * (ctx))
/* ASRC Channel Status 3 */
#define REG_EASRC_CS3(ctx) (0x1B0 + 4 * (ctx))
/* ASRC Channel Status 4 */
#define REG_EASRC_CS4(ctx) (0x1C0 + 4 * (ctx))
/* ASRC Channel Status 5 */
#define REG_EASRC_CS5(ctx) (0x1D0 + 4 * (ctx))
/* ASRC Debug Control Register */
#define REG_EASRC_DBGC 0x1E0
/* ASRC Debug Status Register */
#define REG_EASRC_DBGS 0x1E4
#define REG_EASRC_FIFO(x, ctx) (x == IN ? REG_EASRC_WRFIFO(ctx) \
: REG_EASRC_RDFIFO(ctx))
/* ASRC Context Control (CC) */
#define EASRC_CC_EN_SHIFT 31
#define EASRC_CC_EN_MASK BIT(EASRC_CC_EN_SHIFT)
#define EASRC_CC_EN BIT(EASRC_CC_EN_SHIFT)
#define EASRC_CC_STOP_SHIFT 29
#define EASRC_CC_STOP_MASK BIT(EASRC_CC_STOP_SHIFT)
#define EASRC_CC_STOP BIT(EASRC_CC_STOP_SHIFT)
#define EASRC_CC_FWMDE_SHIFT 28
#define EASRC_CC_FWMDE_MASK BIT(EASRC_CC_FWMDE_SHIFT)
#define EASRC_CC_FWMDE BIT(EASRC_CC_FWMDE_SHIFT)
#define EASRC_CC_FIFO_WTMK_SHIFT 16
#define EASRC_CC_FIFO_WTMK_WIDTH 7
#define EASRC_CC_FIFO_WTMK_MASK ((BIT(EASRC_CC_FIFO_WTMK_WIDTH) - 1) \
<< EASRC_CC_FIFO_WTMK_SHIFT)
#define EASRC_CC_FIFO_WTMK(v) (((v) << EASRC_CC_FIFO_WTMK_SHIFT) \
& EASRC_CC_FIFO_WTMK_MASK)
#define EASRC_CC_SAMPLE_POS_SHIFT 11
#define EASRC_CC_SAMPLE_POS_WIDTH 5
#define EASRC_CC_SAMPLE_POS_MASK ((BIT(EASRC_CC_SAMPLE_POS_WIDTH) - 1) \
<< EASRC_CC_SAMPLE_POS_SHIFT)
#define EASRC_CC_SAMPLE_POS(v) (((v) << EASRC_CC_SAMPLE_POS_SHIFT) \
& EASRC_CC_SAMPLE_POS_MASK)
#define EASRC_CC_ENDIANNESS_SHIFT 10
#define EASRC_CC_ENDIANNESS_MASK BIT(EASRC_CC_ENDIANNESS_SHIFT)
#define EASRC_CC_ENDIANNESS BIT(EASRC_CC_ENDIANNESS_SHIFT)
#define EASRC_CC_BPS_SHIFT 8
#define EASRC_CC_BPS_WIDTH 2
#define EASRC_CC_BPS_MASK ((BIT(EASRC_CC_BPS_WIDTH) - 1) \
<< EASRC_CC_BPS_SHIFT)
#define EASRC_CC_BPS(v) (((v) << EASRC_CC_BPS_SHIFT) \
& EASRC_CC_BPS_MASK)
#define EASRC_CC_FMT_SHIFT 7
#define EASRC_CC_FMT_MASK BIT(EASRC_CC_FMT_SHIFT)
#define EASRC_CC_FMT BIT(EASRC_CC_FMT_SHIFT)
#define EASRC_CC_INSIGN_SHIFT 6
#define EASRC_CC_INSIGN_MASK BIT(EASRC_CC_INSIGN_SHIFT)
#define EASRC_CC_INSIGN BIT(EASRC_CC_INSIGN_SHIFT)
#define EASRC_CC_CHEN_SHIFT 0
#define EASRC_CC_CHEN_WIDTH 5
#define EASRC_CC_CHEN_MASK ((BIT(EASRC_CC_CHEN_WIDTH) - 1) \
<< EASRC_CC_CHEN_SHIFT)
#define EASRC_CC_CHEN(v) (((v) << EASRC_CC_CHEN_SHIFT) \
& EASRC_CC_CHEN_MASK)
/* ASRC Context Control Extended 1 (CCE1) */
#define EASRC_CCE1_COEF_WS_SHIFT 25
#define EASRC_CCE1_COEF_WS_MASK BIT(EASRC_CCE1_COEF_WS_SHIFT)
#define EASRC_CCE1_COEF_WS BIT(EASRC_CCE1_COEF_WS_SHIFT)
#define EASRC_CCE1_COEF_MEM_RST_SHIFT 24
#define EASRC_CCE1_COEF_MEM_RST_MASK BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
#define EASRC_CCE1_COEF_MEM_RST BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
#define EASRC_CCE1_PF_EXP_SHIFT 16
#define EASRC_CCE1_PF_EXP_WIDTH 8
#define EASRC_CCE1_PF_EXP_MASK ((BIT(EASRC_CCE1_PF_EXP_WIDTH) - 1) \
<< EASRC_CCE1_PF_EXP_SHIFT)
#define EASRC_CCE1_PF_EXP(v) (((v) << EASRC_CCE1_PF_EXP_SHIFT) \
& EASRC_CCE1_PF_EXP_MASK)
#define EASRC_CCE1_PF_ST1_WBFP_SHIFT 9
#define EASRC_CCE1_PF_ST1_WBFP_MASK BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
#define EASRC_CCE1_PF_ST1_WBFP BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
#define EASRC_CCE1_PF_TSEN_SHIFT 8
#define EASRC_CCE1_PF_TSEN_MASK BIT(EASRC_CCE1_PF_TSEN_SHIFT)
#define EASRC_CCE1_PF_TSEN BIT(EASRC_CCE1_PF_TSEN_SHIFT)
#define EASRC_CCE1_RS_BYPASS_SHIFT 7
#define EASRC_CCE1_RS_BYPASS_MASK BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
#define EASRC_CCE1_RS_BYPASS BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
#define EASRC_CCE1_PF_BYPASS_SHIFT 6
#define EASRC_CCE1_PF_BYPASS_MASK BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
#define EASRC_CCE1_PF_BYPASS BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
#define EASRC_CCE1_RS_STOP_SHIFT 5
#define EASRC_CCE1_RS_STOP_MASK BIT(EASRC_CCE1_RS_STOP_SHIFT)
#define EASRC_CCE1_RS_STOP BIT(EASRC_CCE1_RS_STOP_SHIFT)
#define EASRC_CCE1_PF_STOP_SHIFT 4
#define EASRC_CCE1_PF_STOP_MASK BIT(EASRC_CCE1_PF_STOP_SHIFT)
#define EASRC_CCE1_PF_STOP BIT(EASRC_CCE1_PF_STOP_SHIFT)
#define EASRC_CCE1_RS_INIT_SHIFT 2
#define EASRC_CCE1_RS_INIT_WIDTH 2
#define EASRC_CCE1_RS_INIT_MASK ((BIT(EASRC_CCE1_RS_INIT_WIDTH) - 1) \
<< EASRC_CCE1_RS_INIT_SHIFT)
#define EASRC_CCE1_RS_INIT(v) (((v) << EASRC_CCE1_RS_INIT_SHIFT) \
& EASRC_CCE1_RS_INIT_MASK)
#define EASRC_CCE1_PF_INIT_SHIFT 0
#define EASRC_CCE1_PF_INIT_WIDTH 2
#define EASRC_CCE1_PF_INIT_MASK ((BIT(EASRC_CCE1_PF_INIT_WIDTH) - 1) \
<< EASRC_CCE1_PF_INIT_SHIFT)
#define EASRC_CCE1_PF_INIT(v) (((v) << EASRC_CCE1_PF_INIT_SHIFT) \
& EASRC_CCE1_PF_INIT_MASK)
/* ASRC Context Control Extended 2 (CCE2) */
#define EASRC_CCE2_ST2_TAPS_SHIFT 16
#define EASRC_CCE2_ST2_TAPS_WIDTH 9
#define EASRC_CCE2_ST2_TAPS_MASK ((BIT(EASRC_CCE2_ST2_TAPS_WIDTH) - 1) \
<< EASRC_CCE2_ST2_TAPS_SHIFT)
#define EASRC_CCE2_ST2_TAPS(v) (((v) << EASRC_CCE2_ST2_TAPS_SHIFT) \
& EASRC_CCE2_ST2_TAPS_MASK)
#define EASRC_CCE2_ST1_TAPS_SHIFT 0
#define EASRC_CCE2_ST1_TAPS_WIDTH 9
#define EASRC_CCE2_ST1_TAPS_MASK ((BIT(EASRC_CCE2_ST1_TAPS_WIDTH) - 1) \
<< EASRC_CCE2_ST1_TAPS_SHIFT)
#define EASRC_CCE2_ST1_TAPS(v) (((v) << EASRC_CCE2_ST1_TAPS_SHIFT) \
& EASRC_CCE2_ST1_TAPS_MASK)
/* ASRC Control Input Access (CIA) */
#define EASRC_CIA_ITER_SHIFT 16
#define EASRC_CIA_ITER_WIDTH 6
#define EASRC_CIA_ITER_MASK ((BIT(EASRC_CIA_ITER_WIDTH) - 1) \
<< EASRC_CIA_ITER_SHIFT)
#define EASRC_CIA_ITER(v) (((v) << EASRC_CIA_ITER_SHIFT) \
& EASRC_CIA_ITER_MASK)
#define EASRC_CIA_GRLEN_SHIFT 8
#define EASRC_CIA_GRLEN_WIDTH 6
#define EASRC_CIA_GRLEN_MASK ((BIT(EASRC_CIA_GRLEN_WIDTH) - 1) \
<< EASRC_CIA_GRLEN_SHIFT)
#define EASRC_CIA_GRLEN(v) (((v) << EASRC_CIA_GRLEN_SHIFT) \
& EASRC_CIA_GRLEN_MASK)
#define EASRC_CIA_ACCLEN_SHIFT 0
#define EASRC_CIA_ACCLEN_WIDTH 6
#define EASRC_CIA_ACCLEN_MASK ((BIT(EASRC_CIA_ACCLEN_WIDTH) - 1) \
<< EASRC_CIA_ACCLEN_SHIFT)
#define EASRC_CIA_ACCLEN(v) (((v) << EASRC_CIA_ACCLEN_SHIFT) \
& EASRC_CIA_ACCLEN_MASK)
/* ASRC Datapath Processor Control Slot0 Register0 (DPCS0R0) */
#define EASRC_DPCS0R0_MAXCH_SHIFT 24
#define EASRC_DPCS0R0_MAXCH_WIDTH 5
#define EASRC_DPCS0R0_MAXCH_MASK ((BIT(EASRC_DPCS0R0_MAXCH_WIDTH) - 1) \
<< EASRC_DPCS0R0_MAXCH_SHIFT)
#define EASRC_DPCS0R0_MAXCH(v) (((v) << EASRC_DPCS0R0_MAXCH_SHIFT) \
& EASRC_DPCS0R0_MAXCH_MASK)
#define EASRC_DPCS0R0_MINCH_SHIFT 16
#define EASRC_DPCS0R0_MINCH_WIDTH 5
#define EASRC_DPCS0R0_MINCH_MASK ((BIT(EASRC_DPCS0R0_MINCH_WIDTH) - 1) \
<< EASRC_DPCS0R0_MINCH_SHIFT)
#define EASRC_DPCS0R0_MINCH(v) (((v) << EASRC_DPCS0R0_MINCH_SHIFT) \
& EASRC_DPCS0R0_MINCH_MASK)
#define EASRC_DPCS0R0_NUMCH_SHIFT 8
#define EASRC_DPCS0R0_NUMCH_WIDTH 5
#define EASRC_DPCS0R0_NUMCH_MASK ((BIT(EASRC_DPCS0R0_NUMCH_WIDTH) - 1) \
<< EASRC_DPCS0R0_NUMCH_SHIFT)
#define EASRC_DPCS0R0_NUMCH(v) (((v) << EASRC_DPCS0R0_NUMCH_SHIFT) \
& EASRC_DPCS0R0_NUMCH_MASK)
#define EASRC_DPCS0R0_CTXNUM_SHIFT 1
#define EASRC_DPCS0R0_CTXNUM_WIDTH 2
#define EASRC_DPCS0R0_CTXNUM_MASK ((BIT(EASRC_DPCS0R0_CTXNUM_WIDTH) - 1) \
<< EASRC_DPCS0R0_CTXNUM_SHIFT)
#define EASRC_DPCS0R0_CTXNUM(v) (((v) << EASRC_DPCS0R0_CTXNUM_SHIFT) \
& EASRC_DPCS0R0_CTXNUM_MASK)
#define EASRC_DPCS0R0_EN_SHIFT 0
#define EASRC_DPCS0R0_EN_MASK BIT(EASRC_DPCS0R0_EN_SHIFT)
#define EASRC_DPCS0R0_EN BIT(EASRC_DPCS0R0_EN_SHIFT)
/* ASRC Datapath Processor Control Slot0 Register1 (DPCS0R1) */
#define EASRC_DPCS0R1_ST1_EXP_SHIFT 0
#define EASRC_DPCS0R1_ST1_EXP_WIDTH 13
#define EASRC_DPCS0R1_ST1_EXP_MASK ((BIT(EASRC_DPCS0R1_ST1_EXP_WIDTH) - 1) \
<< EASRC_DPCS0R1_ST1_EXP_SHIFT)
#define EASRC_DPCS0R1_ST1_EXP(v) (((v) << EASRC_DPCS0R1_ST1_EXP_SHIFT) \
& EASRC_DPCS0R1_ST1_EXP_MASK)
/* ASRC Datapath Processor Control Slot0 Register2 (DPCS0R2) */
#define EASRC_DPCS0R2_ST1_MA_SHIFT 16
#define EASRC_DPCS0R2_ST1_MA_WIDTH 13
#define EASRC_DPCS0R2_ST1_MA_MASK ((BIT(EASRC_DPCS0R2_ST1_MA_WIDTH) - 1) \
<< EASRC_DPCS0R2_ST1_MA_SHIFT)
#define EASRC_DPCS0R2_ST1_MA(v) (((v) << EASRC_DPCS0R2_ST1_MA_SHIFT) \
& EASRC_DPCS0R2_ST1_MA_MASK)
#define EASRC_DPCS0R2_ST1_SA_SHIFT 0
#define EASRC_DPCS0R2_ST1_SA_WIDTH 13
#define EASRC_DPCS0R2_ST1_SA_MASK ((BIT(EASRC_DPCS0R2_ST1_SA_WIDTH) - 1) \
<< EASRC_DPCS0R2_ST1_SA_SHIFT)
#define EASRC_DPCS0R2_ST1_SA(v) (((v) << EASRC_DPCS0R2_ST1_SA_SHIFT) \
& EASRC_DPCS0R2_ST1_SA_MASK)
/* ASRC Datapath Processor Control Slot0 Register3 (DPCS0R3) */
#define EASRC_DPCS0R3_ST2_MA_SHIFT 16
#define EASRC_DPCS0R3_ST2_MA_WIDTH 13
#define EASRC_DPCS0R3_ST2_MA_MASK ((BIT(EASRC_DPCS0R3_ST2_MA_WIDTH) - 1) \
<< EASRC_DPCS0R3_ST2_MA_SHIFT)
#define EASRC_DPCS0R3_ST2_MA(v) (((v) << EASRC_DPCS0R3_ST2_MA_SHIFT) \
& EASRC_DPCS0R3_ST2_MA_MASK)
#define EASRC_DPCS0R3_ST2_SA_SHIFT 0
#define EASRC_DPCS0R3_ST2_SA_WIDTH 13
#define EASRC_DPCS0R3_ST2_SA_MASK ((BIT(EASRC_DPCS0R3_ST2_SA_WIDTH) - 1) \
<< EASRC_DPCS0R3_ST2_SA_SHIFT)
#define EASRC_DPCS0R3_ST2_SA(v) (((v) << EASRC_DPCS0R3_ST2_SA_SHIFT) \
& EASRC_DPCS0R3_ST2_SA_MASK)
/* ASRC Context Output Control (COC) */
#define EASRC_COC_FWMDE_SHIFT 28
#define EASRC_COC_FWMDE_MASK BIT(EASRC_COC_FWMDE_SHIFT)
#define EASRC_COC_FWMDE BIT(EASRC_COC_FWMDE_SHIFT)
#define EASRC_COC_FIFO_WTMK_SHIFT 16
#define EASRC_COC_FIFO_WTMK_WIDTH 7
#define EASRC_COC_FIFO_WTMK_MASK ((BIT(EASRC_COC_FIFO_WTMK_WIDTH) - 1) \
<< EASRC_COC_FIFO_WTMK_SHIFT)
#define EASRC_COC_FIFO_WTMK(v) (((v) << EASRC_COC_FIFO_WTMK_SHIFT) \
& EASRC_COC_FIFO_WTMK_MASK)
#define EASRC_COC_SAMPLE_POS_SHIFT 11
#define EASRC_COC_SAMPLE_POS_WIDTH 5
#define EASRC_COC_SAMPLE_POS_MASK ((BIT(EASRC_COC_SAMPLE_POS_WIDTH) - 1) \
<< EASRC_COC_SAMPLE_POS_SHIFT)
#define EASRC_COC_SAMPLE_POS(v) (((v) << EASRC_COC_SAMPLE_POS_SHIFT) \
& EASRC_COC_SAMPLE_POS_MASK)
#define EASRC_COC_ENDIANNESS_SHIFT 10
#define EASRC_COC_ENDIANNESS_MASK BIT(EASRC_COC_ENDIANNESS_SHIFT)
#define EASRC_COC_ENDIANNESS BIT(EASRC_COC_ENDIANNESS_SHIFT)
#define EASRC_COC_BPS_SHIFT 8
#define EASRC_COC_BPS_WIDTH 2
#define EASRC_COC_BPS_MASK ((BIT(EASRC_COC_BPS_WIDTH) - 1) \
<< EASRC_COC_BPS_SHIFT)
#define EASRC_COC_BPS(v) (((v) << EASRC_COC_BPS_SHIFT) \
& EASRC_COC_BPS_MASK)
#define EASRC_COC_FMT_SHIFT 7
#define EASRC_COC_FMT_MASK BIT(EASRC_COC_FMT_SHIFT)
#define EASRC_COC_FMT BIT(EASRC_COC_FMT_SHIFT)
#define EASRC_COC_OUTSIGN_SHIFT 6
#define EASRC_COC_OUTSIGN_MASK BIT(EASRC_COC_OUTSIGN_SHIFT)
#define EASRC_COC_OUTSIGN_OUT BIT(EASRC_COC_OUTSIGN_SHIFT)
#define EASRC_COC_IEC_VDATA_SHIFT 2
#define EASRC_COC_IEC_VDATA_MASK BIT(EASRC_COC_IEC_VDATA_SHIFT)
#define EASRC_COC_IEC_VDATA BIT(EASRC_COC_IEC_VDATA_SHIFT)
#define EASRC_COC_IEC_EN_SHIFT 1
#define EASRC_COC_IEC_EN_MASK BIT(EASRC_COC_IEC_EN_SHIFT)
#define EASRC_COC_IEC_EN BIT(EASRC_COC_IEC_EN_SHIFT)
#define EASRC_COC_DITHER_EN_SHIFT 0
#define EASRC_COC_DITHER_EN_MASK BIT(EASRC_COC_DITHER_EN_SHIFT)
#define EASRC_COC_DITHER_EN BIT(EASRC_COC_DITHER_EN_SHIFT)
/* ASRC Control Output Access (COA) */
#define EASRC_COA_ITER_SHIFT 16
#define EASRC_COA_ITER_WIDTH 6
#define EASRC_COA_ITER_MASK ((BIT(EASRC_COA_ITER_WIDTH) - 1) \
<< EASRC_COA_ITER_SHIFT)
#define EASRC_COA_ITER(v) (((v) << EASRC_COA_ITER_SHIFT) \
& EASRC_COA_ITER_MASK)
#define EASRC_COA_GRLEN_SHIFT 8
#define EASRC_COA_GRLEN_WIDTH 6
#define EASRC_COA_GRLEN_MASK ((BIT(EASRC_COA_GRLEN_WIDTH) - 1) \
<< EASRC_COA_GRLEN_SHIFT)
#define EASRC_COA_GRLEN(v) (((v) << EASRC_COA_GRLEN_SHIFT) \
& EASRC_COA_GRLEN_MASK)
#define EASRC_COA_ACCLEN_SHIFT 0
#define EASRC_COA_ACCLEN_WIDTH 6
#define EASRC_COA_ACCLEN_MASK ((BIT(EASRC_COA_ACCLEN_WIDTH) - 1) \
<< EASRC_COA_ACCLEN_SHIFT)
#define EASRC_COA_ACCLEN(v) (((v) << EASRC_COA_ACCLEN_SHIFT) \
& EASRC_COA_ACCLEN_MASK)
/* ASRC Sample FIFO Status (SFS) */
#define EASRC_SFS_IWTMK_SHIFT 23
#define EASRC_SFS_IWTMK_MASK BIT(EASRC_SFS_IWTMK_SHIFT)
#define EASRC_SFS_IWTMK BIT(EASRC_SFS_IWTMK_SHIFT)
#define EASRC_SFS_NSGI_SHIFT 16
#define EASRC_SFS_NSGI_WIDTH 7
#define EASRC_SFS_NSGI_MASK ((BIT(EASRC_SFS_NSGI_WIDTH) - 1) \
<< EASRC_SFS_NSGI_SHIFT)
#define EASRC_SFS_NSGI(v) (((v) << EASRC_SFS_NSGI_SHIFT) \
& EASRC_SFS_NSGI_MASK)
#define EASRC_SFS_OWTMK_SHIFT 7
#define EASRC_SFS_OWTMK_MASK BIT(EASRC_SFS_OWTMK_SHIFT)
#define EASRC_SFS_OWTMK BIT(EASRC_SFS_OWTMK_SHIFT)
#define EASRC_SFS_NSGO_SHIFT 0
#define EASRC_SFS_NSGO_WIDTH 7
#define EASRC_SFS_NSGO_MASK ((BIT(EASRC_SFS_NSGO_WIDTH) - 1) \
<< EASRC_SFS_NSGO_SHIFT)
#define EASRC_SFS_NSGO(v) (((v) << EASRC_SFS_NSGO_SHIFT) \
& EASRC_SFS_NSGO_MASK)
/* ASRC Resampling Ratio Low (RRL) */
#define EASRC_RRL_RS_RL_SHIFT 0
#define EASRC_RRL_RS_RL_WIDTH 32
#define EASRC_RRL_RS_RL_MASK ((BIT(EASRC_RRL_RS_RL_WIDTH) - 1) \
<< EASRC_RRL_RS_RL_SHIFT)
#define EASRC_RRL_RS_RL(v) (((v) << EASRC_RRL_RS_RL_SHIFT) \
& EASRC_RRL_RS_RL_MASK)
/* ASRC Resampling Ratio High (RRH) */
#define EASRC_RRH_RS_VLD_SHIFT 31
#define EASRC_RRH_RS_VLD_MASK BIT(EASRC_RRH_RS_VLD_SHIFT)
#define EASRC_RRH_RS_VLD BIT(EASRC_RRH_RS_VLD_SHIFT)
#define EASRC_RRH_RS_RH_SHIFT 0
#define EASRC_RRH_RS_RH_WIDTH 12
#define EASRC_RRH_RS_RH_MASK ((BIT(EASRC_RRH_RS_RH_WIDTH) - 1) \
<< EASRC_RRH_RS_RH_SHIFT)
#define EASRC_RRH_RS_RH(v) (((v) << EASRC_RRH_RS_RH_SHIFT) \
& EASRC_RRH_RS_RH_MASK)
/* ASRC Resampling Ratio Update Control (RSUC) */
#define EASRC_RSUC_RS_RM_SHIFT 0
#define EASRC_RSUC_RS_RM_WIDTH 32
#define EASRC_RSUC_RS_RM_MASK ((BIT(EASRC_RSUC_RS_RM_WIDTH) - 1) \
<< EASRC_RSUC_RS_RM_SHIFT)
#define EASRC_RSUC_RS_RM(v) (((v) << EASRC_RSUC_RS_RM_SHIFT) \
& EASRC_RSUC_RS_RM_MASK)
/* ASRC Resampling Ratio Update Rate (RRUR) */
#define EASRC_RRUR_RRR_SHIFT 0
#define EASRC_RRUR_RRR_WIDTH 31
#define EASRC_RRUR_RRR_MASK ((BIT(EASRC_RRUR_RRR_WIDTH) - 1) \
<< EASRC_RRUR_RRR_SHIFT)
#define EASRC_RRUR_RRR(v) (((v) << EASRC_RRUR_RRR_SHIFT) \
& EASRC_RRUR_RRR_MASK)
/* ASRC Resampling Center Tap Coefficient Low (RCTCL) */
#define EASRC_RCTCL_RS_CL_SHIFT 0
#define EASRC_RCTCL_RS_CL_WIDTH 32
#define EASRC_RCTCL_RS_CL_MASK ((BIT(EASRC_RCTCL_RS_CL_WIDTH) - 1) \
<< EASRC_RCTCL_RS_CL_SHIFT)
#define EASRC_RCTCL_RS_CL(v) (((v) << EASRC_RCTCL_RS_CL_SHIFT) \
& EASRC_RCTCL_RS_CL_MASK)
/* ASRC Resampling Center Tap Coefficient High (RCTCH) */
#define EASRC_RCTCH_RS_CH_SHIFT 0
#define EASRC_RCTCH_RS_CH_WIDTH 32
#define EASRC_RCTCH_RS_CH_MASK ((BIT(EASRC_RCTCH_RS_CH_WIDTH) - 1) \
<< EASRC_RCTCH_RS_CH_SHIFT)
#define EASRC_RCTCH_RS_CH(v) (((v) << EASRC_RCTCH_RS_CH_SHIFT) \
& EASRC_RCTCH_RS_CH_MASK)
/* ASRC Prefilter Coefficient FIFO (PCF) */
#define EASRC_PCF_CD_SHIFT 0
#define EASRC_PCF_CD_WIDTH 32
#define EASRC_PCF_CD_MASK ((BIT(EASRC_PCF_CD_WIDTH) - 1) \
<< EASRC_PCF_CD_SHIFT)
#define EASRC_PCF_CD(v) (((v) << EASRC_PCF_CD_SHIFT) \
& EASRC_PCF_CD_MASK)
/* ASRC Context Resampling Coefficient Memory (CRCM) */
#define EASRC_CRCM_RS_CWD_SHIFT 0
#define EASRC_CRCM_RS_CWD_WIDTH 32
#define EASRC_CRCM_RS_CWD_MASK ((BIT(EASRC_CRCM_RS_CWD_WIDTH) - 1) \
<< EASRC_CRCM_RS_CWD_SHIFT)
#define EASRC_CRCM_RS_CWD(v) (((v) << EASRC_CRCM_RS_CWD_SHIFT) \
& EASRC_CRCM_RS_CWD_MASK)
/* ASRC Context Resampling Coefficient Control (CRCC) */
#define EASRC_CRCC_RS_CA_SHIFT 16
#define EASRC_CRCC_RS_CA_WIDTH 11
#define EASRC_CRCC_RS_CA_MASK ((BIT(EASRC_CRCC_RS_CA_WIDTH) - 1) \
<< EASRC_CRCC_RS_CA_SHIFT)
#define EASRC_CRCC_RS_CA(v) (((v) << EASRC_CRCC_RS_CA_SHIFT) \
& EASRC_CRCC_RS_CA_MASK)
#define EASRC_CRCC_RS_TAPS_SHIFT 1
#define EASRC_CRCC_RS_TAPS_WIDTH 2
#define EASRC_CRCC_RS_TAPS_MASK ((BIT(EASRC_CRCC_RS_TAPS_WIDTH) - 1) \
<< EASRC_CRCC_RS_TAPS_SHIFT)
#define EASRC_CRCC_RS_TAPS(v) (((v) << EASRC_CRCC_RS_TAPS_SHIFT) \
& EASRC_CRCC_RS_TAPS_MASK)
#define EASRC_CRCC_RS_CPR_SHIFT 0
#define EASRC_CRCC_RS_CPR_MASK BIT(EASRC_CRCC_RS_CPR_SHIFT)
#define EASRC_CRCC_RS_CPR BIT(EASRC_CRCC_RS_CPR_SHIFT)
/* ASRC Interrupt_Control (IC) */
#define EASRC_IRQC_RSDM_SHIFT 8
#define EASRC_IRQC_RSDM_WIDTH 4
#define EASRC_IRQC_RSDM_MASK ((BIT(EASRC_IRQC_RSDM_WIDTH) - 1) \
<< EASRC_IRQC_RSDM_SHIFT)
#define EASRC_IRQC_RSDM(v) (((v) << EASRC_IRQC_RSDM_SHIFT) \
& EASRC_IRQC_RSDM_MASK)
#define EASRC_IRQC_OERM_SHIFT 4
#define EASRC_IRQC_OERM_WIDTH 4
#define EASRC_IRQC_OERM_MASK ((BIT(EASRC_IRQC_OERM_WIDTH) - 1) \
<< EASRC_IRQC_OERM_SHIFT)
#define EASRC_IRQC_OERM(v) (((v) << EASRC_IRQC_OERM_SHIFT) \
& EASRC_IEQC_OERM_MASK)
#define EASRC_IRQC_IOM_SHIFT 0
#define EASRC_IRQC_IOM_WIDTH 4
#define EASRC_IRQC_IOM_MASK ((BIT(EASRC_IRQC_IOM_WIDTH) - 1) \
<< EASRC_IRQC_IOM_SHIFT)
#define EASRC_IRQC_IOM(v) (((v) << EASRC_IRQC_IOM_SHIFT) \
& EASRC_IRQC_IOM_MASK)
/* ASRC Interrupt Status Flags (ISF) */
#define EASRC_IRQF_RSD_SHIFT 8
#define EASRC_IRQF_RSD_WIDTH 4
#define EASRC_IRQF_RSD_MASK ((BIT(EASRC_IRQF_RSD_WIDTH) - 1) \
<< EASRC_IRQF_RSD_SHIFT)
#define EASRC_IRQF_RSD(v) (((v) << EASRC_IRQF_RSD_SHIFT) \
& EASRC_IRQF_RSD_MASK)
#define EASRC_IRQF_OER_SHIFT 4
#define EASRC_IRQF_OER_WIDTH 4
#define EASRC_IRQF_OER_MASK ((BIT(EASRC_IRQF_OER_WIDTH) - 1) \
<< EASRC_IRQF_OER_SHIFT)
#define EASRC_IRQF_OER(v) (((v) << EASRC_IRQF_OER_SHIFT) \
& EASRC_IRQF_OER_MASK)
#define EASRC_IRQF_IFO_SHIFT 0
#define EASRC_IRQF_IFO_WIDTH 4
#define EASRC_IRQF_IFO_MASK ((BIT(EASRC_IRQF_IFO_WIDTH) - 1) \
<< EASRC_IRQF_IFO_SHIFT)
#define EASRC_IRQF_IFO(v) (((v) << EASRC_IRQF_IFO_SHIFT) \
& EASRC_IRQF_IFO_MASK)
/* ASRC Context Channel STAT */
#define EASRC_CSx_CSx_SHIFT 0
#define EASRC_CSx_CSx_WIDTH 32
#define EASRC_CSx_CSx_MASK ((BIT(EASRC_CSx_CSx_WIDTH) - 1) \
<< EASRC_CSx_CSx_SHIFT)
#define EASRC_CSx_CSx(v) (((v) << EASRC_CSx_CSx_SHIFT) \
& EASRC_CSx_CSx_MASK)
/* ASRC Debug Control Register */
#define EASRC_DBGC_DMS_SHIFT 0
#define EASRC_DBGC_DMS_WIDTH 6
#define EASRC_DBGC_DMS_MASK ((BIT(EASRC_DBGC_DMS_WIDTH) - 1) \
<< EASRC_DBGC_DMS_SHIFT)
#define EASRC_DBGC_DMS(v) (((v) << EASRC_DBGC_DMS_SHIFT) \
& EASRC_DBGC_DMS_MASK)
/* ASRC Debug Status Register */
#define EASRC_DBGS_DS_SHIFT 0
#define EASRC_DBGS_DS_WIDTH 32
#define EASRC_DBGS_DS_MASK ((BIT(EASRC_DBGS_DS_WIDTH) - 1) \
<< EASRC_DBGS_DS_SHIFT)
#define EASRC_DBGS_DS(v) (((v) << EASRC_DBGS_DS_SHIFT) \
& EASRC_DBGS_DS_MASK)
/* General Constants */
#define EASRC_CTX_MAX_NUM 4
#define EASRC_32b_MASK (BIT(32) - 1)
#define EASRC_64b_MASK (BIT(64) - 1)
#define EASRC_RS_COEFF_MEM 0
#define EASRC_PF_COEFF_MEM 1
/* Prefilter constants */
#define EASRC_PF_ST1_ONLY 0
#define EASRC_PF_TWO_STAGE_MODE 1
#define EASRC_PF_ST1_COEFF_WR 0
#define EASRC_PF_ST2_COEFF_WR 1
#define EASRC_MAX_PF_TAPS 384
/* Resampling constants */
#define EASRC_RS_32_TAPS 0
#define EASRC_RS_64_TAPS 1
#define EASRC_RS_128_TAPS 2
/* Initialization mode */
#define EASRC_INIT_MODE_SW_CONTROL 0
#define EASRC_INIT_MODE_REPLICATE 1
#define EASRC_INIT_MODE_ZERO_FILL 2
/* directions */
#define IN 0
#define OUT 1
/* FIFO watermarks */
#define FSL_EASRC_INPUTFIFO_WML 0x4
#define FSL_EASRC_OUTPUTFIFO_WML 0x1
#define EASRC_INPUTFIFO_THRESHOLD_MIN 0
#define EASRC_INPUTFIFO_THRESHOLD_MAX 127
#define EASRC_OUTPUTFIFO_THRESHOLD_MIN 0
#define EASRC_OUTPUTFIFO_THRESHOLD_MAX 63
#define EASRC_DMA_BUFFER_SIZE (1024 * 48 * 9)
#define EASRC_MAX_BUFFER_SIZE (1024 * 48)
#define FIRMWARE_MAGIC 0xDEAD
#define FIRMWARE_VERSION 1
enum easrc_word_width {
EASRC_WIDTH_16_BIT = 0,
EASRC_WIDTH_20_BIT = 1,
EASRC_WIDTH_24_BIT = 2,
EASRC_WIDTH_32_BIT = 3,
};
struct __attribute__((__packed__)) asrc_firmware_hdr {
u32 magic;
u32 interp_scen;
u32 prefil_scen;
u32 firmware_version;
};
struct __attribute__((__packed__)) interp_params {
u32 magic;
u32 num_taps;
u32 num_phases;
u64 center_tap;
u64 coeff[8192];
};
struct __attribute__((__packed__)) prefil_params {
u32 magic;
u32 insr;
u32 outsr;
u32 st1_taps;
u32 st2_taps;
u32 st1_exp;
u64 coeff[256];
};
struct dma_block {
void *dma_vaddr;
unsigned int length;
unsigned int max_buf_size;
};
struct fsl_easrc_data_fmt {
unsigned int width : 2;
unsigned int endianness : 1;
unsigned int unsign : 1;
unsigned int floating_point : 1;
unsigned int iec958: 1;
unsigned int sample_pos: 5;
unsigned int addexp;
};
struct fsl_easrc_io_params {
struct fsl_easrc_data_fmt fmt;
unsigned int group_len;
unsigned int iterations;
unsigned int access_len;
unsigned int fifo_wtmk;
unsigned int sample_rate;
unsigned int sample_format;
unsigned int norm_rate;
};
struct fsl_easrc_slot {
bool busy;
int ctx_index;
int num_channel; /*maximum is 8*/
int min_channel;
int max_channel;
int pf_mem_used;
};
struct fsl_easrc_context {
enum asrc_pair_index index;
struct fsl_easrc *easrc;
struct dma_chan *dma_chan[2];
struct dma_async_tx_descriptor *desc[2];
struct fsl_easrc_io_params in_params;
struct fsl_easrc_io_params out_params;
struct imx_dma_data dma_data;
unsigned int channels;
unsigned int st1_num_taps;
unsigned int st2_num_taps;
unsigned int st1_num_exp;
unsigned int pf_init_mode;
unsigned int rs_init_mode;
unsigned int ctx_streams;
unsigned int pos;
u64 rs_ratio;
u64 *st1_coeff;
u64 *st2_coeff;
int in_filled_sample;
int out_missed_sample;
int st1_addexp;
int st2_addexp;
void *private_data;
};
/**
* fsl_easrc: EASRC private data
*
* name : name of EASRC device
* @pdev: platform device pointer
* @regmap: regmap handler
* @dma_params_rx: DMA parameters for receive channel
* @dma_params_tx: DMA parameters for transmit channel
* @ctx: context pointer
* @slot: slot setting
* @mem_clk: clock source to access register
* @firmware_hdr: the header of firmware
* @interp: pointer to interpolation filter coeff
* @prefil: pointer to prefilter coeff
* @fw: firmware of coeff table
* @fw_name: firmware name
* @paddr: physical address to the base address of registers
* @rs_num_taps: resample filter taps, 32, 64, or 128
* @bps_i2c958: bits per sample of iec958
* @chn_avail: available channels, maximum 32
* @lock: spin lock for resource protection
* @easrc_rate: default sample rate for ASoC Back-Ends
* @easrc_format: default sample format for ASoC Back-Ends
*/
struct fsl_easrc {
char name[32];
struct platform_device *pdev;
struct regmap *regmap;
struct miscdevice easrc_miscdev;
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct fsl_easrc_context *ctx[EASRC_CTX_MAX_NUM];
struct fsl_easrc_slot slot[EASRC_CTX_MAX_NUM][2];
struct clk *mem_clk;
struct asrc_firmware_hdr *firmware_hdr;
struct interp_params *interp;
struct prefil_params *prefil;
const struct firmware *fw;
const char *fw_name;
unsigned long paddr;
unsigned int rs_num_taps;
unsigned int bps_iec958[EASRC_CTX_MAX_NUM];
unsigned int chn_avail;
u64 *rs_coeff;
u64 const_coeff;
int firmware_loaded;
spinlock_t lock; /* spin lock for resource protection */
int easrc_rate;
int easrc_format;
};
struct dma_chan *fsl_easrc_get_dma_channel(
struct fsl_easrc_context *ctx, bool dir);
int fsl_easrc_request_context(
struct fsl_easrc_context *ctx, unsigned int channels);
int fsl_easrc_release_context(struct fsl_easrc_context *ctx);
#define DRV_NAME "fsl-easrc-dma"
#endif /* _FSL_EASRC_H */

View File

@ -0,0 +1,493 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright 2019 NXP
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/platform_data/dma-imx.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include "fsl_easrc.h"
#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
static struct snd_pcm_hardware snd_imx_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = 65532, /* Limited by SDMA engine */
.periods_min = 2,
.periods_max = 255,
.fifo_size = 0,
};
static bool filter(struct dma_chan *chan, void *param)
{
if (!imx_dma_is_general_purpose(chan))
return false;
chan->private = param;
return true;
}
static void fsl_easrc_dma_complete(void *arg)
{
struct snd_pcm_substream *substream = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_easrc_context *ctx = runtime->private_data;
ctx->pos += snd_pcm_lib_period_bytes(substream);
if (ctx->pos >= snd_pcm_lib_buffer_bytes(substream))
ctx->pos = 0;
snd_pcm_period_elapsed(substream);
}
static int fsl_easrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
{
u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_easrc_context *ctx = runtime->private_data;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
unsigned long flags = DMA_CTRL_ACK;
/* Prepare and submit Front-End DMA channel */
if (!substream->runtime->no_period_wakeup)
flags |= DMA_PREP_INTERRUPT;
ctx->pos = 0;
ctx->desc[!dir] = dmaengine_prep_dma_cyclic(
ctx->dma_chan[!dir], runtime->dma_addr,
snd_pcm_lib_buffer_bytes(substream),
snd_pcm_lib_period_bytes(substream),
dir == OUT ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, flags);
if (!ctx->desc[!dir]) {
dev_err(dev, "failed to prepare slave DMA for Front-End\n");
return -ENOMEM;
}
ctx->desc[!dir]->callback = fsl_easrc_dma_complete;
ctx->desc[!dir]->callback_param = substream;
dmaengine_submit(ctx->desc[!dir]);
/* Prepare and submit Back-End DMA channel */
ctx->desc[dir] = dmaengine_prep_dma_cyclic(
ctx->dma_chan[dir], 0xffff, 64, 64, DMA_DEV_TO_DEV, 0);
if (!ctx->desc[dir]) {
dev_err(dev, "failed to prepare slave DMA for Back-End\n");
return -ENOMEM;
}
dmaengine_submit(ctx->desc[dir]);
return 0;
}
static int fsl_easrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_easrc_context *ctx = runtime->private_data;
int ret;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = fsl_easrc_dma_prepare_and_submit(substream);
if (ret)
return ret;
dma_async_issue_pending(ctx->dma_chan[IN]);
dma_async_issue_pending(ctx->dma_chan[OUT]);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dmaengine_terminate_all(ctx->dma_chan[OUT]);
dmaengine_terminate_all(ctx->dma_chan[IN]);
break;
default:
return -EINVAL;
}
return 0;
}
static int fsl_easrc_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_easrc_context *ctx = runtime->private_data;
struct fsl_easrc *easrc = ctx->easrc;
struct dma_slave_config config_fe, config_be;
enum asrc_pair_index index = ctx->index;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
int stream = substream->stream;
struct imx_dma_data *tmp_data;
struct snd_soc_dpcm *dpcm;
struct dma_chan *tmp_chan;
struct device *dev_be;
u8 dir = tx ? OUT : IN;
dma_cap_mask_t mask;
enum sdma_peripheral_type be_peripheral_type;
int ret;
/* Fetch the Back-End dma_data from DPCM */
list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) {
struct snd_soc_pcm_runtime *be = dpcm->be;
struct snd_pcm_substream *substream_be;
struct snd_soc_dai *dai = be->cpu_dai;
if (dpcm->fe != rtd)
continue;
substream_be = snd_soc_dpcm_get_substream(be, stream);
dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be);
dev_be = dai->dev;
break;
}
if (!dma_params_be) {
dev_err(dev, "failed to get the substream of Back-End\n");
return -EINVAL;
}
/* Override dma_data of the Front-End and config its dmaengine */
dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
dma_params_fe->addr = easrc->paddr + REG_EASRC_FIFO(!dir, index);
dma_params_fe->maxburst = dma_params_be->maxburst;
ctx->dma_chan[!dir] = fsl_easrc_get_dma_channel(ctx, !dir);
if (!ctx->dma_chan[!dir]) {
dev_err(dev, "failed to request DMA channel\n");
return -EINVAL;
}
memset(&config_fe, 0, sizeof(config_fe));
ret = snd_dmaengine_pcm_prepare_slave_config(substream,
params, &config_fe);
if (ret) {
dev_err(dev, "failed to prepare DMA config for Front-End\n");
return ret;
}
ret = dmaengine_slave_config(ctx->dma_chan[!dir], &config_fe);
if (ret) {
dev_err(dev, "failed to config DMA channel for Front-End\n");
return ret;
}
/* Request and config DMA channel for Back-End */
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
dma_cap_set(DMA_CYCLIC, mask);
/* Get DMA request of Back-End */
tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
if (tmp_chan) {
tmp_data = tmp_chan->private;
if (tmp_data) {
ctx->dma_data.dma_request = tmp_data->dma_request;
be_peripheral_type = tmp_data->peripheral_type;
if (tx && be_peripheral_type == IMX_DMATYPE_SSI_DUAL)
ctx->dma_data.dst_dualfifo = true;
if (!tx && be_peripheral_type == IMX_DMATYPE_SSI_DUAL)
ctx->dma_data.src_dualfifo = true;
}
dma_release_channel(tmp_chan);
}
/* Get DMA request of Front-End */
tmp_chan = fsl_easrc_get_dma_channel(ctx, dir);
if (tmp_chan) {
tmp_data = tmp_chan->private;
if (tmp_data) {
ctx->dma_data.dma_request2 = tmp_data->dma_request;
ctx->dma_data.peripheral_type =
tmp_data->peripheral_type;
ctx->dma_data.priority = tmp_data->priority;
}
dma_release_channel(tmp_chan);
}
/* For sdma DEV_TO_DEV, there is two dma request
* But for emda DEV_TO_DEV, there is only one dma request, which is
* from the BE.
*/
if (ctx->dma_data.dma_request2 != ctx->dma_data.dma_request)
ctx->dma_chan[dir] =
dma_request_channel(mask, filter, &ctx->dma_data);
else
ctx->dma_chan[dir] =
dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
if (!ctx->dma_chan[dir]) {
dev_err(dev, "failed to request DMA channel for Back-End\n");
return -EINVAL;
}
if (easrc->easrc_format == SNDRV_PCM_FORMAT_S16_LE)
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
else
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
memset(&config_be, 0, sizeof(config_be));
config_be.direction = DMA_DEV_TO_DEV;
config_be.src_addr_width = buswidth;
config_be.src_maxburst = dma_params_be->maxburst;
config_be.dst_addr_width = buswidth;
config_be.dst_maxburst = dma_params_be->maxburst;
if (tx) {
config_be.src_addr = easrc->paddr + REG_EASRC_RDFIFO(index);
config_be.dst_addr = dma_params_be->addr;
} else {
config_be.dst_addr = easrc->paddr + REG_EASRC_WRFIFO(index);
config_be.src_addr = dma_params_be->addr;
}
ret = dmaengine_slave_config(ctx->dma_chan[dir], &config_be);
if (ret) {
dev_err(dev, "failed to config DMA channel for Back-End\n");
return ret;
}
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
return 0;
}
static int fsl_easrc_dma_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_easrc_context *ctx = runtime->private_data;
snd_pcm_set_runtime_buffer(substream, NULL);
if (ctx->dma_chan[IN])
dma_release_channel(ctx->dma_chan[IN]);
if (ctx->dma_chan[OUT])
dma_release_channel(ctx->dma_chan[OUT]);
ctx->dma_chan[IN] = NULL;
ctx->dma_chan[OUT] = NULL;
return 0;
}
static int fsl_easrc_dma_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct fsl_easrc *easrc = dev_get_drvdata(dev);
struct fsl_easrc_context *ctx;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u8 dir = tx ? OUT : IN;
struct dma_slave_caps dma_caps;
struct dma_chan *tmp_chan;
struct snd_dmaengine_dai_dma_data *dma_data;
u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
int ret;
int i;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->easrc = easrc;
runtime->private_data = ctx;
ret = snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0) {
dev_err(dev, "failed to set pcm hw params periods\n");
return ret;
}
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
fsl_easrc_request_context(ctx, 1);
tmp_chan = fsl_easrc_get_dma_channel(ctx, dir);
if (!tmp_chan) {
dev_err(dev, "can't get dma channel\n");
return -EINVAL;
}
ret = dma_get_slave_caps(tmp_chan, &dma_caps);
if (ret == 0) {
if (dma_caps.cmd_pause)
snd_imx_hardware.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
snd_imx_hardware.info |= SNDRV_PCM_INFO_BATCH;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
addr_widths = dma_caps.dst_addr_widths;
else
addr_widths = dma_caps.src_addr_widths;
}
/*
* If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
* hw.formats set to 0, meaning no restrictions are in place.
* In this case it's the responsibility of the DAI driver to
* provide the supported format information.
*/
if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
/*
* Prepare formats mask for valid/allowed sample types. If the
* dma does not have support for the given physical word size,
* it needs to be masked out so user space can not use the
* format which produces corrupted audio.
* In case the dma driver does not implement the slave_caps the
* default assumption is that it supports 1, 2 and 4 bytes
* widths.
*/
for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
int bits = snd_pcm_format_physical_width(i);
/*
* Enable only samples with DMA supported physical
* widths
*/
switch (bits) {
case 8:
case 16:
case 24:
case 32:
case 64:
if (addr_widths & (1 << (bits / 8)))
snd_imx_hardware.formats |= (1LL << i);
break;
default:
/* Unsupported types */
break;
}
}
if (tmp_chan)
dma_release_channel(tmp_chan);
fsl_easrc_release_context(ctx);
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
return 0;
}
static int fsl_easrc_dma_shutdown(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_easrc_context *ctx = runtime->private_data;
struct fsl_easrc *easrc;
if (!ctx)
return 0;
easrc = ctx->easrc;
if (easrc->ctx[ctx->index] == ctx)
easrc->ctx[ctx->index] = NULL;
kfree(ctx);
return 0;
}
static snd_pcm_uframes_t fsl_easrc_dma_pcm_pointer(
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_easrc_context *ctx = runtime->private_data;
return bytes_to_frames(substream->runtime, ctx->pos);
}
static const struct snd_pcm_ops fsl_easrc_dma_pcm_ops = {
.ioctl = snd_pcm_lib_ioctl,
.hw_params = fsl_easrc_dma_hw_params,
.hw_free = fsl_easrc_dma_hw_free,
.trigger = fsl_easrc_dma_trigger,
.open = fsl_easrc_dma_startup,
.close = fsl_easrc_dma_shutdown,
.pointer = fsl_easrc_dma_pcm_pointer,
};
static int fsl_easrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm_substream *substream;
struct snd_pcm *pcm = rtd->pcm;
int ret, i;
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(card->dev, "failed to set DMA mask\n");
return ret;
}
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
substream = pcm->streams[i].substream;
if (!substream)
continue;
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
FSL_ASRC_DMABUF_SIZE,
&substream->dma_buffer);
if (ret) {
dev_err(card->dev, "failed to allocate DMA buffer\n");
goto err;
}
}
return 0;
err:
if (--i == 0 && pcm->streams[i].substream)
snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer);
return ret;
}
static void fsl_easrc_dma_pcm_free(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
int i;
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
substream = pcm->streams[i].substream;
if (!substream)
continue;
snd_dma_free_pages(&substream->dma_buffer);
substream->dma_buffer.area = NULL;
substream->dma_buffer.addr = 0;
}
}
struct snd_soc_component_driver fsl_easrc_dma_component = {
.name = DRV_NAME,
.ops = &fsl_easrc_dma_pcm_ops,
.pcm_new = fsl_easrc_dma_pcm_new,
.pcm_free = fsl_easrc_dma_pcm_free,
};
EXPORT_SYMBOL_GPL(fsl_easrc_dma_component);

View File

@ -0,0 +1,966 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright 2019 NXP
struct fsl_easrc_m2m {
struct fsl_easrc *easrc;
struct fsl_easrc_context *ctx;
struct completion complete[2];
struct dma_block dma_block[2];
unsigned int ctx_hold;
unsigned int easrc_active;
unsigned int first_convert;
unsigned int sg_nodes[2];
struct scatterlist sg[2][9];
struct dma_async_tx_descriptor *desc[2];
spinlock_t lock; /* protect mem resource */
};
void fsl_easrc_get_status(struct fsl_easrc_context *ctx,
struct asrc_status_flags *flags)
{
flags->overload_error = 0;
}
#define mxc_easrc_dma_umap_in(dev, m2m) \
dma_unmap_sg(dev, m2m->sg[IN], m2m->sg_nodes[IN], \
DMA_MEM_TO_DEV) \
#define mxc_easrc_dma_umap_out(dev, m2m) \
dma_unmap_sg(dev, m2m->sg[OUT], m2m->sg_nodes[OUT], \
DMA_DEV_TO_MEM) \
#define EASRC_xPUT_DMA_CALLBACK(dir) \
((dir == IN) ? fsl_easrc_input_dma_callback \
: fsl_easrc_output_dma_callback)
#define DIR_STR(dir) dir == IN ? "in" : "out"
static void fsl_easrc_input_dma_callback(void *data)
{
struct fsl_easrc_m2m *m2m = (struct fsl_easrc_m2m *)data;
complete(&m2m->complete[IN]);
}
static void fsl_easrc_output_dma_callback(void *data)
{
struct fsl_easrc_m2m *m2m = (struct fsl_easrc_m2m *)data;
complete(&m2m->complete[OUT]);
}
static int fsl_allocate_dma_buf(struct fsl_easrc_m2m *m2m)
{
struct dma_block *input = &m2m->dma_block[IN];
struct dma_block *output = &m2m->dma_block[OUT];
input->dma_vaddr = kzalloc(input->length, GFP_KERNEL);
if (!input->dma_vaddr)
return -ENOMEM;
output->dma_vaddr = kzalloc(output->length, GFP_KERNEL);
if (!output->dma_vaddr)
goto alloc_fail;
return 0;
alloc_fail:
kfree(input->dma_vaddr);
return -ENOMEM;
}
static int fsl_easrc_dmaconfig(struct fsl_easrc_m2m *m2m,
struct dma_chan *chan,
u32 dma_addr, void *buf_addr, u32 buf_len,
bool dir, int bits)
{
struct dma_async_tx_descriptor *desc = m2m->desc[dir];
struct fsl_easrc *easrc = m2m->easrc;
struct fsl_easrc_context *ctx = m2m->ctx;
struct device *dev = &easrc->pdev->dev;
unsigned int sg_nent = m2m->sg_nodes[dir];
struct scatterlist *sg = m2m->sg[dir];
struct dma_slave_config slave_config;
enum dma_slave_buswidth buswidth;
int ret, i;
switch (bits) {
case 16:
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
case 24:
buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
break;
default:
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
}
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 =
ctx->in_params.fifo_wtmk * ctx->channels;
} else {
slave_config.direction = DMA_DEV_TO_MEM;
slave_config.src_addr = dma_addr;
slave_config.src_addr_width = buswidth;
slave_config.src_maxburst =
ctx->out_params.fifo_wtmk * ctx->channels;
}
ret = dmaengine_slave_config(chan, &slave_config);
if (ret) {
dev_err(dev, "failed to config dmaengine for %sput task: %d\n",
DIR_STR(dir), ret);
return -EINVAL;
}
sg_init_table(sg, sg_nent);
switch (sg_nent) {
case 1:
sg_init_one(sg, buf_addr, buf_len);
break;
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
for (i = 0; i < (sg_nent - 1); i++)
sg_set_buf(&sg[i],
buf_addr + i * m2m->dma_block[dir].max_buf_size,
m2m->dma_block[dir].max_buf_size);
sg_set_buf(&sg[i],
buf_addr + i * m2m->dma_block[dir].max_buf_size,
buf_len - i * m2m->dma_block[dir].max_buf_size);
break;
default:
dev_err(dev, "invalid input DMA nodes number: %d\n", sg_nent);
return -EINVAL;
}
ret = dma_map_sg(dev, sg, sg_nent, slave_config.direction);
if (ret != sg_nent) {
dev_err(dev, "failed to map DMA sg for %sput task\n",
DIR_STR(dir));
return -EINVAL;
}
desc = dmaengine_prep_slave_sg(chan, sg, sg_nent,
slave_config.direction,
DMA_PREP_INTERRUPT);
if (!desc) {
dev_err(dev, "failed to prepare dmaengine for %sput task\n",
DIR_STR(dir));
return -EINVAL;
}
m2m->desc[dir] = desc;
m2m->desc[dir]->callback = EASRC_xPUT_DMA_CALLBACK(dir);
desc->callback = EASRC_xPUT_DMA_CALLBACK(dir);
desc->callback_param = m2m;
return 0;
}
static long fsl_easrc_calc_outbuf_len(struct fsl_easrc_m2m *m2m,
struct asrc_convert_buffer *pbuf)
{
struct fsl_easrc_context *ctx = m2m->ctx;
unsigned int out_length;
unsigned int in_width, out_width;
unsigned int channels = ctx->channels;
unsigned int in_samples, out_samples;
in_width = snd_pcm_format_physical_width(ctx->in_params.sample_format) / 8;
out_width = snd_pcm_format_physical_width(ctx->out_params.sample_format) / 8;
in_samples = pbuf->input_buffer_length / (in_width * channels);
out_samples = ctx->out_params.sample_rate * in_samples /
ctx->in_params.sample_rate;
out_length = out_samples * out_width * channels;
if (out_samples <= ctx->out_missed_sample) {
out_length = 0;
ctx->out_missed_sample -= out_samples;
} else {
out_length -= ctx->out_missed_sample * out_width * channels;
ctx->out_missed_sample = 0;
}
return out_length;
}
static long fsl_easrc_prepare_io_buffer(struct fsl_easrc_m2m *m2m,
struct asrc_convert_buffer *buf,
bool dir)
{
struct fsl_easrc *easrc = m2m->easrc;
struct device *dev = &easrc->pdev->dev;
struct fsl_easrc_context *ctx = m2m->ctx;
struct dma_chan *dma_chan = ctx->dma_chan[dir];
unsigned int *dma_len = &m2m->dma_block[dir].length;
unsigned int *sg_nodes = &m2m->sg_nodes[dir];
void *dma_vaddr = m2m->dma_block[dir].dma_vaddr;
enum asrc_pair_index index = m2m->ctx->index;
unsigned int buf_len, bits;
u32 fifo_addr;
void __user *buf_vaddr;
if (dir == IN) {
buf_vaddr = (void __user *)buf->input_buffer_vaddr;
buf_len = buf->input_buffer_length;
bits = snd_pcm_format_physical_width(ctx->in_params.sample_format);
fifo_addr = easrc->paddr + REG_EASRC_WRFIFO(index);
} else {
buf_vaddr = (void __user *)buf->output_buffer_vaddr;
buf_len = buf->output_buffer_length;
bits = snd_pcm_format_physical_width(ctx->out_params.sample_format);
fifo_addr = easrc->paddr + REG_EASRC_RDFIFO(index);
}
if (buf_len > EASRC_DMA_BUFFER_SIZE ||
(dir == IN && (buf_len % (bits / 8)))) {
dev_err(dev, "%sput buffer size is error: [%d]\n",
DIR_STR(dir), buf_len);
return -EINVAL;
}
if (dir == IN && copy_from_user(dma_vaddr, buf_vaddr, buf_len))
return -EFAULT;
*dma_len = buf_len;
if (dir == OUT)
*dma_len = fsl_easrc_calc_outbuf_len(m2m, buf);
if (*dma_len <= 0)
return 0;
*sg_nodes = *dma_len / m2m->dma_block[dir].max_buf_size + 1;
return fsl_easrc_dmaconfig(m2m, dma_chan, fifo_addr, dma_vaddr,
*dma_len, dir, bits);
}
static long fsl_easrc_prepare_buffer(struct fsl_easrc_m2m *m2m,
struct asrc_convert_buffer *buf)
{
struct fsl_easrc *easrc = m2m->easrc;
struct device *dev = &easrc->pdev->dev;
int ret;
ret = fsl_easrc_prepare_io_buffer(m2m, buf, IN);
if (ret) {
dev_err(dev, "failed to prepare input buffer %d\n", ret);
return ret;
}
ret = fsl_easrc_prepare_io_buffer(m2m, buf, OUT);
if (ret) {
dev_err(dev, "failed to prepare output buffer %d\n", ret);
return ret;
}
return 0;
}
int fsl_easrc_process_buffer_pre(struct fsl_easrc_m2m *m2m, bool dir)
{
struct fsl_easrc *easrc = m2m->easrc;
struct device *dev = &easrc->pdev->dev;
if (!wait_for_completion_interruptible_timeout(&m2m->complete[dir],
10 * HZ)) {
dev_err(dev, "%sput DMA task timeout\n", DIR_STR(dir));
return -ETIME;
} else if (signal_pending(current)) {
dev_err(dev, "%sput task forcibly aborted\n", DIR_STR(dir));
return -ERESTARTSYS;
}
return 0;
}
static unsigned int fsl_easrc_get_output_FIFO_size(struct fsl_easrc_m2m *m2m)
{
struct fsl_easrc *easrc = m2m->easrc;
enum asrc_pair_index index = m2m->ctx->index;
u32 val;
regmap_read(easrc->regmap, REG_EASRC_SFS(index), &val);
val &= EASRC_SFS_NSGO_MASK;
return val >> EASRC_SFS_NSGO_SHIFT;
}
static void fsl_easrc_read_last_FIFO(struct fsl_easrc_m2m *m2m)
{
struct fsl_easrc *easrc = m2m->easrc;
struct dma_block *output = &m2m->dma_block[OUT];
struct fsl_easrc_context *ctx = m2m->ctx;
enum asrc_pair_index index = m2m->ctx->index;
u32 i, reg, size, t_size = 0, width;
u32 *reg32 = NULL;
u16 *reg16 = NULL;
u8 *reg24 = NULL;
width = snd_pcm_format_physical_width(ctx->out_params.sample_format);
if (width == 32)
reg32 = output->dma_vaddr + output->length;
else if (width == 16)
reg16 = output->dma_vaddr + output->length;
else
reg24 = output->dma_vaddr + output->length;
retry:
size = fsl_easrc_get_output_FIFO_size(m2m);
for (i = 0; i < size * ctx->channels; i++) {
regmap_read(easrc->regmap, REG_EASRC_RDFIFO(index), &reg);
if (reg32) {
*(reg32) = reg;
reg32++;
} else if (reg16) {
*(reg16) = (u16)reg;
reg16++;
} else {
*reg24++ = (u8)reg;
*reg24++ = (u8)(reg >> 8);
*reg24++ = (u8)(reg >> 16);
}
}
t_size += size;
if (size)
goto retry;
if (reg32)
output->length += t_size * ctx->channels * 4;
else if (reg16)
output->length += t_size * ctx->channels * 2;
else
output->length += t_size * ctx->channels * 3;
}
static long fsl_easrc_process_buffer(struct fsl_easrc_m2m *m2m,
struct asrc_convert_buffer *buf)
{
struct fsl_easrc *easrc = m2m->easrc;
struct device *dev = &easrc->pdev->dev;
unsigned long lock_flags;
int ret;
/* Check input task first */
ret = fsl_easrc_process_buffer_pre(m2m, IN);
if (ret) {
mxc_easrc_dma_umap_in(dev, m2m);
if (m2m->dma_block[OUT].length > 0)
mxc_easrc_dma_umap_out(dev, m2m);
return ret;
}
/* ...then output task*/
if (m2m->dma_block[OUT].length > 0) {
ret = fsl_easrc_process_buffer_pre(m2m, OUT);
if (ret) {
mxc_easrc_dma_umap_in(dev, m2m);
mxc_easrc_dma_umap_out(dev, m2m);
return ret;
}
}
mxc_easrc_dma_umap_in(dev, m2m);
if (m2m->dma_block[OUT].length > 0)
mxc_easrc_dma_umap_out(dev, m2m);
spin_lock_irqsave(&m2m->lock, lock_flags);
if (!m2m->ctx_hold) {
spin_unlock_irqrestore(&m2m->lock, lock_flags);
return -EFAULT;
}
spin_unlock_irqrestore(&m2m->lock, lock_flags);
/* Fetch the remaining data */
fsl_easrc_read_last_FIFO(m2m);
/* Update final lengths after getting last FIFO */
buf->input_buffer_length = m2m->dma_block[IN].length;
buf->output_buffer_length = m2m->dma_block[OUT].length;
if (copy_to_user((void __user *)buf->output_buffer_vaddr,
m2m->dma_block[OUT].dma_vaddr,
m2m->dma_block[OUT].length))
return -EFAULT;
return 0;
}
void fsl_easrc_submit_dma(struct fsl_easrc_m2m *m2m)
{
/* Submit DMA request */
dmaengine_submit(m2m->desc[IN]);
dma_async_issue_pending(m2m->desc[IN]->chan);
if (m2m->dma_block[OUT].length > 0) {
dmaengine_submit(m2m->desc[OUT]);
dma_async_issue_pending(m2m->desc[OUT]->chan);
}
}
static long fsl_easrc_ioctl_req_context(struct fsl_easrc_m2m *m2m,
void __user *user)
{
struct fsl_easrc *easrc = m2m->easrc;
struct device *dev = &easrc->pdev->dev;
struct asrc_req req;
unsigned long lock_flags;
long ret;
ret = copy_from_user(&req, user, sizeof(req));
if (ret) {
dev_err(dev, "failed to get req from user space:%ld\n", ret);
return ret;
}
ret = fsl_easrc_request_context(m2m->ctx, req.chn_num);
if (ret < 0) {
dev_err(dev, "failed to request context:%ld\n", ret);
return ret;
}
/* request context returns the context id in case of success */
spin_lock_irqsave(&m2m->lock, lock_flags);
m2m->ctx_hold = 1;
req.index = m2m->ctx->index;
req.supported_in_format = FSL_EASRC_FORMATS;
req.supported_out_format = FSL_EASRC_FORMATS |
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
spin_unlock_irqrestore(&m2m->lock, lock_flags);
ret = copy_to_user(user, &req, sizeof(req));
if (ret) {
dev_err(dev, "failed to send req to user space: %ld\n", ret);
return ret;
}
return 0;
}
static long fsl_easrc_ioctl_config_context(struct fsl_easrc_m2m *m2m,
void __user *user)
{
struct fsl_easrc *easrc = m2m->easrc;
struct fsl_easrc_context *ctx = m2m->ctx;
enum asrc_pair_index index = m2m->ctx->index;
struct device *dev = &easrc->pdev->dev;
struct asrc_config config;
int ret;
int in_word_size, out_word_size;
ret = copy_from_user(&config, user, sizeof(config));
if (ret) {
dev_err(dev, "failed to get config from user space: %d\n", ret);
return ret;
}
/* set context configuration parameters received from userspace */
ctx->in_params.sample_rate = config.input_sample_rate;
ctx->out_params.sample_rate = config.output_sample_rate;
ctx->in_params.fifo_wtmk = FSL_EASRC_INPUTFIFO_WML;
ctx->out_params.fifo_wtmk = FSL_EASRC_OUTPUTFIFO_WML;
ctx->in_params.sample_format = config.input_format;
ctx->out_params.sample_format = config.output_format;
ctx->channels = config.channel_num;
ctx->rs_init_mode = 0x2;
ctx->pf_init_mode = 0x2;
ret = fsl_easrc_set_ctx_format(ctx,
&ctx->in_params.sample_format,
&ctx->out_params.sample_format);
if (ret)
return ret;
ret = fsl_easrc_config_context(easrc, index);
if (ret) {
dev_err(dev, "failed to config context %d\n", ret);
return ret;
}
ctx->in_params.iterations = 1;
ctx->in_params.group_len = ctx->channels;
ctx->in_params.access_len = ctx->channels;
ctx->out_params.iterations = 1;
ctx->out_params.group_len = ctx->channels;
ctx->out_params.access_len = ctx->channels;
/* You can also call fsl_easrc_set_ctx_organziation for
* sample interleaving support
*/
ret = fsl_easrc_set_ctx_organziation(ctx);
if (ret) {
dev_err(dev, "failed to set fifo organization\n");
return ret;
}
in_word_size = snd_pcm_format_physical_width(config.input_format) / 8;
out_word_size = snd_pcm_format_physical_width(config.output_format) / 8;
/* allocate dma buffers */
m2m->dma_block[IN].length = EASRC_DMA_BUFFER_SIZE;
m2m->dma_block[IN].max_buf_size = rounddown(EASRC_MAX_BUFFER_SIZE,
in_word_size * ctx->channels);
m2m->dma_block[OUT].length = EASRC_DMA_BUFFER_SIZE;
m2m->dma_block[OUT].max_buf_size = rounddown(EASRC_MAX_BUFFER_SIZE,
out_word_size * ctx->channels);
ret = fsl_allocate_dma_buf(m2m);
if (ret) {
dev_err(dev, "failed to allocate DMA buffers: %d\n", ret);
return ret;
}
ctx->dma_chan[IN] = fsl_easrc_get_dma_channel(ctx, IN);
if (!ctx->dma_chan[IN]) {
dev_err(dev, "[ctx%d] failed to get input DMA channel\n",
m2m->ctx->index);
return -EBUSY;
}
ctx->dma_chan[OUT] = fsl_easrc_get_dma_channel(ctx, OUT);
if (!ctx->dma_chan[OUT]) {
dev_err(dev, "[ctx%d] failed to get output DMA channel\n",
m2m->ctx->index);
return -EBUSY;
}
ret = copy_to_user(user, &config, sizeof(config));
if (ret) {
dev_err(dev, "failed to send config to user: %d\n", ret);
return ret;
}
return 0;
}
static long fsl_easrc_ioctl_release_context(struct fsl_easrc_m2m *m2m,
void __user *user)
{
struct fsl_easrc *easrc = m2m->easrc;
struct fsl_easrc_context *ctx = m2m->ctx;
struct device *dev = &easrc->pdev->dev;
enum asrc_pair_index index;
unsigned long lock_flags;
int ret;
ret = copy_from_user(&index, user, sizeof(index));
if (ret) {
dev_err(dev,
"[ctx%d] failed to get index from user space %d\n",
m2m->ctx->index, ret);
return ret;
}
if (index != m2m->ctx->index) {
dev_err(dev,
"[ctx%d] releasing wrong context - %d\n",
m2m->ctx->index, index);
return -EINVAL;
}
if (m2m->easrc_active) {
m2m->easrc_active = 0;
fsl_easrc_stop_context(ctx);
}
spin_lock_irqsave(&m2m->lock, lock_flags);
m2m->ctx_hold = 0;
spin_unlock_irqrestore(&m2m->lock, lock_flags);
if (ctx->dma_chan[IN])
dma_release_channel(ctx->dma_chan[IN]);
if (ctx->dma_chan[OUT])
dma_release_channel(ctx->dma_chan[OUT]);
ctx->dma_chan[IN] = NULL;
ctx->dma_chan[OUT] = NULL;
/* free buffers allocated in config context*/
kfree(m2m->dma_block[IN].dma_vaddr);
kfree(m2m->dma_block[OUT].dma_vaddr);
fsl_easrc_release_context(ctx);
return 0;
}
static long fsl_easrc_ioctl_convert(struct fsl_easrc_m2m *m2m,
void __user *user)
{
struct fsl_easrc *easrc = m2m->easrc;
struct device *dev = &easrc->pdev->dev;
struct fsl_easrc_context *ctx = m2m->ctx;
struct asrc_convert_buffer buf;
int ret;
ret = copy_from_user(&buf, user, sizeof(buf));
if (ret) {
dev_err(dev, "failed to get buf from user space: %d\n", ret);
return ret;
}
/* fsl_easrc_calc_last_period_size(ctx, &buf); */
ret = fsl_easrc_prepare_buffer(m2m, &buf);
if (ret) {
dev_err(dev, "failed to prepare buffer\n");
return ret;
}
init_completion(&m2m->complete[IN]);
init_completion(&m2m->complete[OUT]);
fsl_easrc_submit_dma(m2m);
if (m2m->first_convert) {
fsl_easrc_start_context(ctx);
m2m->first_convert = 0;
}
ret = fsl_easrc_process_buffer(m2m, &buf);
if (ret) {
dev_err(dev, "failed to process buffer %d\n", ret);
return ret;
}
ret = copy_to_user(user, &buf, sizeof(buf));
if (ret) {
dev_err(dev, "failed to send buffer to user: %d\n", ret);
return ret;
}
return 0;
}
static long fsl_easrc_ioctl_start_conv(struct fsl_easrc_m2m *m2m,
void __user *user)
{
struct fsl_easrc *easrc = m2m->easrc;
struct device *dev = &easrc->pdev->dev;
enum asrc_pair_index index;
int ret;
ret = copy_from_user(&index, user, sizeof(index));
if (ret) {
dev_err(dev, "failed to get index from user space: %d\n",
ret);
return ret;
}
if (index != m2m->ctx->index) {
dev_err(dev, "[ctx%d] attempting to start wrong context%d\n",
m2m->ctx->index, index);
return -EINVAL;
}
m2m->easrc_active = 1;
m2m->first_convert = 1;
return 0;
}
static long fsl_easrc_ioctl_stop_conv(struct fsl_easrc_m2m *m2m,
void __user *user)
{
struct fsl_easrc *easrc = m2m->easrc;
struct fsl_easrc_context *ctx = m2m->ctx;
struct device *dev = &easrc->pdev->dev;
enum asrc_pair_index index;
int ret;
ret = copy_from_user(&index, user, sizeof(index));
if (ret) {
dev_err(dev, "failed to get index from user space: %d\n",
ret);
return ret;
}
if (index != m2m->ctx->index) {
dev_err(dev, "[ctx%d] attempting to start wrong context%d\n",
m2m->ctx->index, index);
return -EINVAL;
}
dmaengine_terminate_all(ctx->dma_chan[IN]);
dmaengine_terminate_all(ctx->dma_chan[OUT]);
fsl_easrc_stop_context(ctx);
m2m->easrc_active = 0;
return 0;
}
static long fsl_easrc_ioctl_status(struct fsl_easrc_m2m *m2m,
void __user *user)
{
struct fsl_easrc *easrc = m2m->easrc;
struct device *dev = &easrc->pdev->dev;
struct fsl_easrc_context *ctx = m2m->ctx;
struct asrc_status_flags flags;
int ret;
ret = copy_from_user(&flags, user, sizeof(flags));
if (ret) {
dev_err(dev,
"[ctx%d] failed to get flags from user space: %d\n",
m2m->ctx->index, ret);
return ret;
}
if (m2m->ctx->index != flags.index) {
dev_err(dev, "[ctx%d] getting status for other context: %d\n",
m2m->ctx->index, flags.index);
return -EINVAL;
}
fsl_easrc_get_status(ctx, &flags);
ret = copy_to_user(user, &flags, sizeof(flags));
if (ret)
dev_err(dev, "[ctx%d] failed to send flags to user space\n",
m2m->ctx->index);
return ret;
}
static long fsl_easrc_ioctl_flush(struct fsl_easrc_m2m *m2m,
void __user *user)
{
struct fsl_easrc *easrc = m2m->easrc;
struct device *dev = &easrc->pdev->dev;
struct fsl_easrc_context *ctx = m2m->ctx;
/* Release DMA and request again */
dma_release_channel(ctx->dma_chan[IN]);
dma_release_channel(ctx->dma_chan[OUT]);
ctx->dma_chan[IN] = fsl_easrc_get_dma_channel(ctx, IN);
if (!ctx->dma_chan[IN]) {
dev_err(dev, "failed to request input task DMA channel\n");
return -EBUSY;
}
ctx->dma_chan[OUT] = fsl_easrc_get_dma_channel(ctx, OUT);
if (!ctx->dma_chan[OUT]) {
dev_err(dev, "failed to request output task DMA channel\n");
return -EBUSY;
}
return 0;
}
static int fsl_easrc_open(struct inode *inode, struct file *file)
{
struct miscdevice *easrc_miscdev = file->private_data;
struct fsl_easrc *easrc = dev_get_drvdata(easrc_miscdev->parent);
struct fsl_easrc_m2m *m2m;
struct fsl_easrc_context *ctx;
struct device *dev = &easrc->pdev->dev;
int ret;
ret = signal_pending(current);
if (ret) {
dev_err(dev, "current process has a signal pending\n");
return ret;
}
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
/* set the pointer to easrc private data */
m2m = kzalloc(sizeof(*m2m), GFP_KERNEL);
if (!m2m) {
ret = -ENOMEM;
goto out;
}
/* just save the pointer to easrc private data */
m2m->easrc = easrc;
m2m->ctx = ctx;
ctx->easrc = easrc;
ctx->private_data = m2m;
spin_lock_init(&m2m->lock);
/* context structs are already allocated in fsl_easrc->ctx[i] */
file->private_data = m2m;
pm_runtime_get_sync(dev);
return 0;
out:
kfree(ctx);
return ret;
}
static int fsl_easrc_close(struct inode *inode, struct file *file)
{
struct fsl_easrc_m2m *m2m = file->private_data;
struct fsl_easrc *easrc = m2m->easrc;
struct fsl_easrc_context *ctx = m2m->ctx;
struct device *dev = &easrc->pdev->dev;
unsigned long lock_flags;
if (m2m->easrc_active) {
m2m->easrc_active = 0;
dmaengine_terminate_all(ctx->dma_chan[IN]);
dmaengine_terminate_all(ctx->dma_chan[OUT]);
fsl_easrc_stop_context(ctx);
fsl_easrc_input_dma_callback((void *)m2m);
fsl_easrc_output_dma_callback((void *)m2m);
}
if (!ctx)
goto null_ctx;
spin_lock_irqsave(&m2m->lock, lock_flags);
if (m2m->ctx_hold) {
m2m->ctx_hold = 0;
spin_unlock_irqrestore(&m2m->lock, lock_flags);
if (ctx->dma_chan[IN])
dma_release_channel(ctx->dma_chan[IN]);
if (ctx->dma_chan[OUT])
dma_release_channel(ctx->dma_chan[OUT]);
kfree(m2m->dma_block[IN].dma_vaddr);
kfree(m2m->dma_block[OUT].dma_vaddr);
fsl_easrc_release_context(ctx);
} else {
spin_unlock_irqrestore(&m2m->lock, lock_flags);
}
null_ctx:
spin_lock_irqsave(&easrc->lock, lock_flags);
kfree(m2m);
kfree(ctx);
file->private_data = NULL;
spin_unlock_irqrestore(&easrc->lock, lock_flags);
pm_runtime_put_sync(dev);
return 0;
}
static long fsl_easrc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct fsl_easrc_m2m *m2m = file->private_data;
struct fsl_easrc *easrc = m2m->easrc;
void __user *user = (void __user *)arg;
long ret = 0;
switch (cmd) {
case ASRC_REQ_PAIR:
ret = fsl_easrc_ioctl_req_context(m2m, user);
break;
case ASRC_CONFIG_PAIR:
ret = fsl_easrc_ioctl_config_context(m2m, user);
break;
case ASRC_RELEASE_PAIR:
ret = fsl_easrc_ioctl_release_context(m2m, user);
break;
case ASRC_CONVERT:
ret = fsl_easrc_ioctl_convert(m2m, user);
break;
case ASRC_START_CONV:
ret = fsl_easrc_ioctl_start_conv(m2m, user);
break;
case ASRC_STOP_CONV:
ret = fsl_easrc_ioctl_stop_conv(m2m, user);
break;
case ASRC_STATUS:
ret = fsl_easrc_ioctl_status(m2m, user);
break;
case ASRC_FLUSH:
ret = fsl_easrc_ioctl_flush(m2m, user);
break;
default:
dev_err(&easrc->pdev->dev, "invalid ioctl command\n");
}
return ret;
}
static const struct file_operations easrc_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = fsl_easrc_ioctl,
.open = fsl_easrc_open,
.release = fsl_easrc_close,
};
static int fsl_easrc_m2m_init(struct fsl_easrc *easrc)
{
struct device *dev = &easrc->pdev->dev;
int ret;
easrc->easrc_miscdev.fops = &easrc_fops;
easrc->easrc_miscdev.parent = dev;
easrc->easrc_miscdev.name = easrc->name;
easrc->easrc_miscdev.minor = MISC_DYNAMIC_MINOR;
ret = misc_register(&easrc->easrc_miscdev);
if (ret)
dev_err(dev, "failed to register char device %d\n", ret);
return ret;
}
#ifdef CONFIG_PM_SLEEP
static void fsl_easrc_m2m_suspend(struct fsl_easrc *easrc)
{
struct fsl_easrc_context *ctx;
struct fsl_easrc_m2m *m2m;
unsigned long lock_flags;
int i;
for (i = 0; i < EASRC_CTX_MAX_NUM; i++) {
spin_lock_irqsave(&easrc->lock, lock_flags);
ctx = easrc->ctx[i];
if (!ctx || !ctx->private_data) {
spin_unlock_irqrestore(&easrc->lock, lock_flags);
continue;
}
m2m = ctx->private_data;
if (!completion_done(&m2m->complete[IN])) {
if (ctx->dma_chan[IN])
dmaengine_terminate_all(ctx->dma_chan[IN]);
fsl_easrc_input_dma_callback((void *)m2m);
}
if (!completion_done(&m2m->complete[OUT])) {
if (ctx->dma_chan[OUT])
dmaengine_terminate_all(ctx->dma_chan[OUT]);
fsl_easrc_output_dma_callback((void *)m2m);
}
m2m->first_convert = 1;
fsl_easrc_stop_context(ctx);
spin_unlock_irqrestore(&easrc->lock, lock_flags);
}
}
static void fsl_easrc_m2m_resume(struct fsl_easrc *easrc)
{
/* null */
}
#endif