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
commit
b1d0e4ce42
|
@ -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.
|
||||
|
||||
|
|
|
@ -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";
|
||||
};
|
|
@ -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__ */
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ®);
|
||||
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 >> 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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
@ -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 */
|
|
@ -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);
|
|
@ -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), ®);
|
||||
|
||||
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
|
Loading…
Reference in New Issue