1831 lines
50 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA SoC Audio Layer - Rockchip ASRC Controller driver
*
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/dma-mapping.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "rockchip_asrc.h"
/*
* structure:
* tx: memory->asrc->sai->codec
* rx: codec->sai->asrc->memory
* dma structure:
* memory -> asrc dma_tx -> asrc tx_mem -> sai dma_tx -> codec
* codec -> sai dma_rx -> asrc rx_mem -> asrc dma_rx -> memory
*
* So the asrc and other dai's dma driver must be added in the component.
*
* The asoc path with asrc should be prepared in rockchip_multicodecs.c
*/
#define DRV_NAME "rockchip-asrc"
/* directions */
#define IN 0
#define OUT 1
#define MAXBURST_PER_FIFO 8
#define DEFAULT_SAMPLE_RATE 48000
#define ASRC_DEFAULT_CLK 200000000
/* Platform Definition */
/* rk3576 */
#define RK3576_SYS_GRF_SOC_CON9 0x24
#define ASRC0_4CH_SRC_SEL_SHIFT 0
#define ASRC0_4CH_SRC_SEL_MASK (0x1f << (ASRC0_4CH_SRC_SEL_SHIFT + 16))
#define ASRC0_4CH_SRC_SEL(x) (x << ASRC0_4CH_SRC_SEL_SHIFT)
#define ASRC0_4CH_DST_SEL_SHIFT 5
#define ASRC0_4CH_DST_SEL_MASK (0x1f << (ASRC0_4CH_DST_SEL_SHIFT + 16))
#define ASRC0_4CH_DST_SEL(x) (x << ASRC0_4CH_DST_SEL_SHIFT)
#define ASRC1_4CH_SRC_SEL_SHIFT 10
#define ASRC1_4CH_SRC_SEL_MASK (0x1f << (ASRC1_4CH_SRC_SEL_SHIFT + 16))
#define ASRC1_4CH_SRC_SEL(x) (x << (ASRC1_4CH_SRC_SEL_SHIFT))
#define RK3576_SYS_GRF_SOC_CON10 0x28
#define ASRC1_4CH_DST_SEL_SHIFT 0
#define ASRC1_4CH_DST_SEL_MASK (0x1f << (ASRC1_4CH_DST_SEL_SHIFT + 16))
#define ASRC1_4CH_DST_SEL(x) (x << ASRC1_4CH_DST_SEL_SHIFT)
#define ASRC2_2CH_SRC_SEL_SHIFT 5
#define ASRC2_2CH_SRC_SEL_MASK (0x1f << (ASRC2_2CH_SRC_SEL_SHIFT+16))
#define ASRC2_2CH_SRC_SEL(x) (x << ASRC2_2CH_SRC_SEL_SHIFT)
#define ASRC2_2CH_DST_SEL_SHIFT 10
#define ASRC2_2CH_DST_SEL_MASK (0x1f << (ASRC2_2CH_DST_SEL_SHIFT + 16))
#define ASRC2_2CH_DST_SEL(x) (x << ASRC2_2CH_DST_SEL_SHIFT)
#define RK3576_SYS_GRF_SOC_CON11 0x2c
#define ASRC3_2CH_SRC_SEL_SHIFT 0
#define ASRC3_2CH_SRC_SEL_MASK (0x1f << (ASRC3_2CH_SRC_SEL_SHIFT + 16))
#define ASRC3_2CH_SRC_SEL(x) (x << ASRC3_2CH_SRC_SEL_SHIFT)
#define ASRC3_2CH_DST_SEL_SHIFT 5
#define ASRC3_2CH_DST_SEL_MASK (0x1f << (ASRC3_2CH_DST_SEL_SHIFT + 16))
#define ASRC3_2CH_DST_SEL(x) (x << ASRC3_2CH_DST_SEL_SHIFT)
#define RK3576_SRC_LRCK_FROM_SAI0 0x0
#define RK3576_SRC_LRCK_FROM_SAI1 0x1
#define RK3576_SRC_LRCK_FROM_SAI2 0x2
#define RK3576_SRC_LRCK_FROM_SAI3 0x3
#define RK3576_SRC_LRCK_FROM_SAI4 0x4
#define RK3576_SRC_LRCK_FROM_SAI5 0x5
#define RK3576_SRC_LRCK_FROM_SAI6 0x6
#define RK3576_SRC_LRCK_FROM_SAI7 0x7
#define RK3576_SRC_LRCK_FROM_SAI8 0x8
#define RK3576_SRC_LRCK_FROM_SAI9 0x9
#define RK3576_SRC_LRCK_FROM_PDM0 0xa
#define RK3576_SRC_LRCK_FROM_PDM1 0xb
#define RK3576_SRC_LRCK_FROM_SPDIF_RX0 0xc
#define RK3576_SRC_LRCK_FROM_SPDIF_RX1 0xd
#define RK3576_SRC_LRCK_FROM_SPDIF_RX2 0xe
#define RK3576_SRC_LRCK_FROM_CRU0 0xf
#define RK3576_SRC_LRCK_FROM_CRU1 0x10
#define RK3576_DST_LRCK_FROM_SAI0 0x0
#define RK3576_DST_LRCK_FROM_SAI1 0x1
#define RK3576_DST_LRCK_FROM_SAI2 0x2
#define RK3576_DST_LRCK_FROM_SAI3 0x3
#define RK3576_DST_LRCK_FROM_SAI4 0x4
#define RK3576_DST_LRCK_FROM_SAI5 0x5
#define RK3576_DST_LRCK_FROM_SAI6 0x6
#define RK3576_DST_LRCK_FROM_SAI7 0x7
#define RK3576_DST_LRCK_FROM_SAI8 0x8
#define RK3576_DST_LRCK_FROM_SAI9 0x9
#define RK3576_DST_LRCK_FROM_SPDIF_TX0 0xa
#define RK3576_DST_LRCK_FROM_SPDIF_TX1 0xb
#define RK3576_DST_LRCK_FROM_SPDIF_TX2 0xc
#define RK3576_DST_LRCK_FROM_SPDIF_TX3 0xd
#define RK3576_DST_LRCK_FROM_SPDIF_TX4 0xe
#define RK3576_DST_LRCK_FROM_SPDIF_TX5 0xf
#define RK3576_DST_LRCK_FROM_CRU0 0x10
#define RK3576_DST_LRCK_FROM_CRU1 0x11
#define RK3576_ASRC0 0x2a690000
#define RK3576_ASRC1 0x2a6a0000
#define RK3576_ASRC2 0x2a6b0000
#define RK3576_ASRC3 0x2a6c0000
/* Common Definition */
#define DAI_ID_UNKNOWN -1
#define DAI_ID_ASRC0 0
#define DAI_ID_ASRC1 1
#define DAI_ID_ASRC2 2
#define DAI_ID_ASRC3 3
#define DAI_ID_ASRC4 4
#define DAI_ID_ASRC5 5
#define DAI_ID_ASRC6 6
#define DAI_ID_ASRC7 7
#define DAI_ID_ASRC8 8
#define DAI_ID_ASRC9 9
#define DAI_ID_ASRC10 10
#define DAI_ID_ASRC11 11
#define DAI_ID_ASRC12 12
#define DAI_ID_ASRC13 13
#define DAI_ID_ASRC14 14
#define DAI_ID_ASRC15 15
#define DAI_ID_SAI0 16
#define DAI_ID_SAI1 17
#define DAI_ID_SAI2 18
#define DAI_ID_SAI3 19
#define DAI_ID_SAI4 20
#define DAI_ID_SAI5 21
#define DAI_ID_SAI6 22
#define DAI_ID_SAI7 23
#define DAI_ID_SAI8 24
#define DAI_ID_SAI9 25
#define DAI_ID_SAI10 26
#define DAI_ID_SAI11 27
#define DAI_ID_SAI12 28
#define DAI_ID_SAI13 29
#define DAI_ID_SAI14 30
#define DAI_ID_SAI15 31
#define DAI_ID_PDM0 32
#define DAI_ID_PDM1 33
#define DAI_ID_PDM2 34
#define DAI_ID_PDM3 35
#define DAI_ID_PDM4 36
#define DAI_ID_PDM5 37
#define DAI_ID_PDM6 38
#define DAI_ID_PDM7 39
#define DAI_ID_SPDIF_TX0 40
#define DAI_ID_SPDIF_TX1 41
#define DAI_ID_SPDIF_TX2 42
#define DAI_ID_SPDIF_TX3 43
#define DAI_ID_SPDIF_TX4 44
#define DAI_ID_SPDIF_TX5 45
#define DAI_ID_SPDIF_TX6 46
#define DAI_ID_SPDIF_TX7 47
#define DAI_ID_SPDIF_RX0 48
#define DAI_ID_SPDIF_RX1 49
#define DAI_ID_SPDIF_RX2 50
#define DAI_ID_SPDIF_RX3 51
#define DAI_ID_SPDIF_RX4 52
#define DAI_ID_SPDIF_RX5 53
#define DAI_ID_SPDIF_RX6 54
#define DAI_ID_SPDIF_RX7 55
#define DAI_ID_MAX 64
struct rk_asrc_soc_data {
int (*lrck_clk_init)(struct device *dev);
int (*lrck_clk_set)(struct device *dev);
int (*lrck_clk_en)(struct device *dev);
int (*lrck_clk_dis)(struct device *dev);
int lrck_source_freq;
};
struct rockchip_asrc_pair {
struct rockchip_asrc *asrc;
unsigned int error;
unsigned int channels;
struct dma_async_tx_descriptor *desc[2];
struct dma_chan *dma_chan[2];
unsigned int pos;
bool req_dma_chan;
void *private;
void *private_m2m;
};
struct rockchip_asrc {
struct device *dev;
struct platform_device *pdev;
struct regmap *grf;
struct regmap *regmap;
struct clk *mclk;
struct clk *hclk;
struct clk *cru_src0; // Used if src or dst is MEM.
struct clk *cru_src1;
struct clk *src_lrck;
struct clk *dst_lrck;
struct clk *src_lrck_parent;
struct clk *dst_lrck_parent;
struct list_head clk_list_head;
struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1];
const struct rk_asrc_soc_data *soc_data;
struct rockchip_asrc_pair *pair[2];
struct snd_dmaengine_dai_dma_data dma_data_rx;
struct snd_dmaengine_dai_dma_data dma_data_tx;
dma_addr_t paddr;
int chan_num;
int sample_bits;
int sample_rate;
int resample_rate;
int dst_link_dai_id; /* This must be set firstly by amixer/tinymixer */
int src_link_dai_id; /* This must be set firstly by amixer/tinymixer */
};
static int rockchip_asrc_calculate_ratio(struct rockchip_asrc *asrc,
int numerator, int denominator)
{
int i, integerPart, remainder, ratio, digit;
unsigned int temp = 0;
if (denominator == 0) {
dev_err(asrc->dev, "The denominator can not be zero.\n");
return 0;
}
integerPart = numerator / denominator;
remainder = numerator % denominator;
ratio = integerPart << 22;
for (i = 0; i < 8; i++) {
remainder <<= 4;
digit = remainder / denominator;
temp |= (digit << (28 - i * 4));
remainder %= denominator;
}
ratio += (temp >> 10);
return ratio;
}
static int rockchip_asrc_calculate_dma_thresh(int dma_burst, struct rockchip_asrc *asrc)
{
int n = asrc->sample_bits == 16 ? 1 : 0;
return dma_burst * (n + 1) / (asrc->chan_num + 1) / 2 - 1;
}
static bool rockchip_asrc_is_link_mem(int dai_id)
{
return (DAI_ID_ASRC0 <= dai_id && dai_id <= DAI_ID_ASRC15);
}
static struct dma_chan *rockchip_asrc_get_dma_channel(struct rockchip_asrc *asrc, bool dir)
{
struct dma_chan *chan = NULL;
if (dir == OUT)
chan = dma_request_chan(asrc->dev, "rx");
else
chan = dma_request_chan(asrc->dev, "tx");
return chan;
}
static void rockchip_asrc_ratio_update(struct rockchip_asrc *asrc, int stream)
{
/* get the sampling frequency, then set the ratio */
regmap_write(asrc->regmap, ASRC_SAMPLE_RATE, asrc->sample_rate);
regmap_write(asrc->regmap, ASRC_RESAMPLE_RATE, asrc->resample_rate);
regmap_write(asrc->regmap, ASRC_MANUAL_RATIO,
rockchip_asrc_calculate_ratio(asrc, asrc->sample_rate, asrc->resample_rate));
}
static int rockchip_asrc_lrck_clks_set(struct rockchip_asrc *asrc)
{
if (asrc->src_link_dai_id < 0 || asrc->dst_link_dai_id < 0) {
dev_warn(asrc->dev, "Invalid DAI_ID, Please set dai id by amixer or tinymix firstly!\n");
return -EINVAL;
}
/*
* Set which device attach to asrc, then set their
* clks which connect to asrc.
*/
return asrc->soc_data->lrck_clk_set(asrc->dev);
}
static int rockchip_asrc_lrck_clks_en(struct rockchip_asrc *asrc)
{
return asrc->soc_data->lrck_clk_en(asrc->dev);
}
static int rockchip_asrc_lrck_clks_dis(struct rockchip_asrc *asrc)
{
return asrc->soc_data->lrck_clk_dis(asrc->dev);
}
static int rockchip_asrc_start(struct rockchip_asrc *asrc, int stream)
{
unsigned int val = 0;
int ret;
ret = rockchip_asrc_lrck_clks_set(asrc);
if (ret)
return ret;
ret = rockchip_asrc_lrck_clks_en(asrc);
if (ret)
return ret;
rockchip_asrc_ratio_update(asrc, stream);
/* Set the real time here */
if (rockchip_asrc_is_link_mem(asrc->src_link_dai_id) &&
rockchip_asrc_is_link_mem(asrc->dst_link_dai_id))
val = ASRC_M2M;
else if (rockchip_asrc_is_link_mem(asrc->src_link_dai_id) &&
!rockchip_asrc_is_link_mem(asrc->dst_link_dai_id))
val = ASRC_M2D;
else if (!rockchip_asrc_is_link_mem(asrc->src_link_dai_id) &&
rockchip_asrc_is_link_mem(asrc->dst_link_dai_id))
val = ASRC_S2M;
else
val = ASRC_S2D;
regmap_update_bits(asrc->regmap, ASRC_CON,
ASRC_MODE_MSK | ASRC_OUT_MSK |
ASRC_IN_MSK | ASRC_REAL_TIME_MODE_MSK,
ASRC_REAL_TIME | ASRC_OUT_START |
ASRC_IN_START | val);
regmap_update_bits(asrc->regmap, ASRC_INT_CON,
ASRC_CONV_ERROR_MSK, ASRC_CONV_ERROR_EN);
/* Now the dma is single direction */
regmap_update_bits(asrc->regmap, ASRC_CON, ASRC_MSK, ASRC_EN);
return 0;
}
static int rockchip_asrc_stop(struct rockchip_asrc *asrc, int stream)
{
regmap_update_bits(asrc->regmap, ASRC_CON,
ASRC_OUT_MSK | ASRC_IN_MSK,
ASRC_OUT_STOP | ASRC_IN_STOP);
regmap_update_bits(asrc->regmap, ASRC_INT_CON,
ASRC_CONV_ERROR_MSK, ASRC_CONV_ERROR_DIS);
regmap_update_bits(asrc->regmap, ASRC_CON, ASRC_MSK, ASRC_DIS);
rockchip_asrc_lrck_clks_dis(asrc);
return 0;
}
static int rockchip_asrc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rockchip_asrc *asrc = snd_soc_dai_get_drvdata(dai);
if (asrc->substreams[substream->stream])
return -EBUSY;
asrc->substreams[substream->stream] = substream;
return 0;
}
static void rockchip_asrc_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rockchip_asrc *asrc = snd_soc_dai_get_drvdata(dai);
asrc->substreams[substream->stream] = NULL;
}
static int rockchip_asrc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct rockchip_asrc *asrc = snd_soc_dai_get_drvdata(dai);
unsigned int val;
/* Set sample rate and resample rate */
asrc->sample_rate = params_rate(params);
/* Set channel */
regmap_update_bits(asrc->regmap, ASRC_CON, ASRC_CHAN_NUM_MSK,
ASRC_CHAN_NUM(params_channels(params)));
asrc->chan_num = (params_channels(params) - 2) / 2;
/* Set size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
val = ASRC_IWL_16BIT | ASRC_OWL_16BIT |
ASRC_OFMT_16 | ASRC_IFMT_16;
asrc->sample_bits = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S32_LE:
val = ASRC_IWL_24BIT | ASRC_OWL_24BIT |
ASRC_OFMT_32 | ASRC_IFMT_32;
asrc->sample_bits = 32;
break;
default:
return -EINVAL;
}
regmap_update_bits(asrc->regmap, ASRC_DATA_FMT,
ASRC_OWL_MSK | ASRC_IWL_MSK |
ASRC_OFMT_MSK | ASRC_IFMT_MSK,
val);
return 0;
}
static int rockchip_asrc_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct rockchip_asrc *asrc = snd_soc_dai_get_drvdata(dai);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = rockchip_asrc_start(asrc, substream->stream);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = rockchip_asrc_stop(asrc, substream->stream);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int rockchip_asrc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
/* No need to set here */
return 0;
}
static int rockchip_asrc_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct rockchip_asrc *asrc = snd_soc_dai_get_drvdata(dai);
int ret;
/* Set the module clock */
ret = clk_set_rate(asrc->mclk, ASRC_DEFAULT_CLK);
if (ret)
dev_err(asrc->dev, "Failed to set mclk %d\n", ret);
return 0;
}
static const struct snd_soc_dai_ops rockchip_asrc_dai_ops = {
.startup = rockchip_asrc_startup,
.shutdown = rockchip_asrc_shutdown,
.hw_params = rockchip_asrc_hw_params,
.trigger = rockchip_asrc_trigger,
.set_fmt = rockchip_asrc_set_fmt,
.set_sysclk = rockchip_asrc_set_sysclk,
};
static int rockchip_asrc_dai_probe(struct snd_soc_dai *dai)
{
struct rockchip_asrc *asrc = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai, &asrc->dma_data_tx,
&asrc->dma_data_rx);
return 0;
}
static struct snd_soc_dai_driver rockchip_asrc_dai = {
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.probe = rockchip_asrc_dai_probe,
.ops = &rockchip_asrc_dai_ops,
.symmetric_rate = 1,
};
static bool rockchip_asrc_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case ASRC_VERSION:
case ASRC_CON:
case ASRC_CLKDIV_CON:
case ASRC_DATA_FMT:
case ASRC_LOOP_CON0:
case ASRC_LOOP_CON1:
case ASRC_LOOP_CON2:
case ASRC_MANUAL_RATIO:
case ASRC_SAMPLE_RATE:
case ASRC_RESAMPLE_RATE:
case ASRC_TRACK_PERIOD:
case ASRC_RATIO_MARGIN:
case ASRC_LRCK_MARGIN:
case ASRC_FETCH_LEN:
case ASRC_DMA_THRESH:
case ASRC_INT_CON:
case ASRC_INT_ST:
case ASRC_ST:
case ASRC_RATIO_ST:
case ASRC_RESAMPLE_RATE_ST:
case ASRC_THETA_CNT_ST:
case ASRC_DECI_THETA_ACC_ST:
case ASRC_FIFO_IN_WRCNT:
case ASRC_FIFO_IN_RDCNT:
case ASRC_FIFO_OUT_WRCNT:
case ASRC_FIFO_OUT_RDCNT:
case ASRC_RXDR:
case ASRC_TXDR:
return true;
default:
return false;
}
}
static bool rockchip_asrc_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case ASRC_CON:
case ASRC_CLKDIV_CON:
case ASRC_DATA_FMT:
case ASRC_LOOP_CON0:
case ASRC_LOOP_CON1:
case ASRC_LOOP_CON2:
case ASRC_MANUAL_RATIO:
case ASRC_SAMPLE_RATE:
case ASRC_RESAMPLE_RATE:
case ASRC_TRACK_PERIOD:
case ASRC_RATIO_MARGIN:
case ASRC_LRCK_MARGIN:
case ASRC_FETCH_LEN:
case ASRC_DMA_THRESH:
case ASRC_INT_CON:
case ASRC_INT_ST:
case ASRC_FIFO_IN_WRCNT:
case ASRC_FIFO_IN_RDCNT:
case ASRC_RXDR:
case ASRC_TXDR:
return true;
default:
return false;
}
}
static bool rockchip_asrc_volatile_reg(struct device *dev, unsigned int reg)
{
return true;
}
static bool rockchip_asrc_precious_reg(struct device *dev, unsigned int reg)
{
if (reg == ASRC_RXDR)
return true;
return false;
}
static const struct reg_default rockchip_asrc_reg[] = {
{ ASRC_VERSION, 0x00000001 },
};
static const struct regmap_config rockchip_asrc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = ASRC_FIFO_OUT_DATA,
.reg_defaults = rockchip_asrc_reg,
.num_reg_defaults = ARRAY_SIZE(rockchip_asrc_reg),
.readable_reg = rockchip_asrc_readable_reg,
.writeable_reg = rockchip_asrc_writeable_reg,
.volatile_reg = rockchip_asrc_volatile_reg,
.precious_reg = rockchip_asrc_precious_reg,
.cache_type = REGCACHE_FLAT,
};
#define RK_ASRC_DMABUF_SIZE (16 * 1024)
static struct snd_pcm_hardware snd_rk_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = RK_ASRC_DMABUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = 0x2000, /* Limited by asrc, max 0x4000 */
.periods_min = 2,
.periods_max = 2,
.fifo_size = 0,
};
static void rockchip_asrc_dma_complete(void *arg)
{
struct snd_pcm_substream *substream = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
struct rockchip_asrc_pair *pair = runtime->private_data;
pair->pos += snd_pcm_lib_period_bytes(substream);
if (pair->pos >= snd_pcm_lib_buffer_bytes(substream))
pair->pos = 0;
snd_pcm_period_elapsed(substream);
}
static int rockchip_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream,
struct snd_soc_component *component)
{
u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
struct snd_pcm_runtime *runtime = substream->runtime;
struct rockchip_asrc_pair *pair = runtime->private_data;
struct rockchip_asrc *asrc = pair->asrc;
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;
pair->pos = 0;
pair->desc[!dir] = dmaengine_prep_dma_cyclic(
pair->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 (!pair->desc[!dir]) {
dev_err(dev, "failed to prepare slave DMA for Front-End\n");
return -ENOMEM;
}
pair->desc[!dir]->callback = rockchip_asrc_dma_complete;
pair->desc[!dir]->callback_param = substream;
dmaengine_submit(pair->desc[!dir]);
/* Prepare and submit Back-End DMA channel */
pair->desc[dir] = dmaengine_prep_dma_cyclic(
pair->dma_chan[dir],
dir == OUT ? (asrc->paddr + ASRC_FIFO_OUT_DATA) : (asrc->paddr + ASRC_FIFO_IN_DATA),
RK_ASRC_DMABUF_SIZE, RK_ASRC_DMABUF_SIZE / 2,
dir == OUT ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, 0);
if (!pair->desc[dir]) {
dev_err(dev, "failed to prepare slave DMA for Back-End\n");
return -ENOMEM;
}
dmaengine_submit(pair->desc[dir]);
return 0;
}
static int rockchip_asrc_dma_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct rockchip_asrc_pair *pair = runtime->private_data;
int ret;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = rockchip_asrc_dma_prepare_and_submit(substream, component);
if (ret)
return ret;
dma_async_issue_pending(pair->dma_chan[IN]);
dma_async_issue_pending(pair->dma_chan[OUT]);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dmaengine_terminate_async(pair->dma_chan[OUT]);
dmaengine_terminate_async(pair->dma_chan[IN]);
break;
default:
return -EINVAL;
}
return 0;
}
static int rockchip_asrc_dma_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
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 rockchip_asrc_pair *pair = runtime->private_data;
struct dma_chan *tmp_chan = NULL, *be_chan = NULL;
struct snd_soc_component *component_be = NULL;
struct rockchip_asrc *asrc = pair->asrc;
struct dma_slave_config config_fe = {}, config_be = {};
struct device *dev = component->dev;
int stream = substream->stream;
struct snd_soc_dpcm *dpcm;
struct device *dev_be;
u8 dir = tx ? OUT : IN;
dma_cap_mask_t mask;
int ret, dma_thresh;
/* Fetch the Back-End dma_data from DPCM */
for_each_dpcm_be(rtd, stream, dpcm) {
struct snd_soc_pcm_runtime *be = dpcm->be;
struct snd_pcm_substream *substream_be;
struct snd_soc_dai *dai = asoc_rtd_to_cpu(be, 0);
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(asoc_rtd_to_cpu(rtd, 0), substream);
if (tx)
dma_params_fe->addr = asrc->paddr + ASRC_RXDR;
else
dma_params_fe->addr = asrc->paddr + ASRC_TXDR;
dma_params_fe->maxburst = dma_params_be->maxburst;
pair->dma_chan[!dir] = rockchip_asrc_get_dma_channel(asrc, !dir);
if (IS_ERR_OR_NULL(pair->dma_chan[!dir])) {
dev_err(dev, "failed to request DMA channel\n");
ret = PTR_ERR(pair->dma_chan[!dir]);
pair->dma_chan[!dir] = NULL;
return ret;
}
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;
}
if (tx && rockchip_asrc_is_link_mem(asrc->src_link_dai_id)) {
dma_thresh = rockchip_asrc_calculate_dma_thresh(config_fe.dst_maxburst, asrc);
regmap_update_bits(asrc->regmap, ASRC_DMA_THRESH,
ASRC_DMA_TX_THRESH_MSK | ASRC_IN_THRESH_MSK,
ASRC_DMA_TX_THRESH(dma_thresh) | ASRC_IN_THRESH(dma_thresh));
} else if (!tx && rockchip_asrc_is_link_mem(asrc->dst_link_dai_id)) {
dma_thresh = rockchip_asrc_calculate_dma_thresh(config_fe.src_maxburst, asrc);
regmap_update_bits(asrc->regmap, ASRC_DMA_THRESH,
ASRC_DMA_RX_THRESH_MSK | ASRC_IN_THRESH_MSK,
ASRC_DMA_RX_THRESH(dma_thresh) | ASRC_IN_THRESH(dma_thresh));
}
ret = dmaengine_slave_config(pair->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);
/*
* The Back-End device might have already requested a DMA channel,
* so try to reuse it first, and then request a new one upon NULL.
*/
if (!IS_ENABLED(CONFIG_SND_SOC_DYNAMIC_DMA_CHAN)) {
component_be = snd_soc_lookup_component_nolocked(dev_be, SND_DMAENGINE_PCM_DRV_NAME);
if (component_be) {
be_chan = soc_component_to_pcm(component_be)->chan[substream->stream];
tmp_chan = be_chan;
}
}
if (!tmp_chan) {
tmp_chan = dma_request_chan(dev_be, tx ? "tx" : "rx");
if (IS_ERR(tmp_chan)) {
dev_err(dev, "failed to request DMA channel for Back-End\n");
return PTR_ERR(tmp_chan);
}
}
pair->dma_chan[dir] = tmp_chan;
/* Do not flag to release if we are reusing the Back-End one */
pair->req_dma_chan = !be_chan;
if (!pair->dma_chan[dir]) {
dev_err(dev, "failed to request DMA channel for Back-End\n");
return -EINVAL;
}
config_be.direction = (dir == OUT ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
config_be.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
config_be.src_maxburst = dma_params_be->maxburst;
config_be.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
config_be.dst_maxburst = dma_params_be->maxburst;
if (tx) {
config_be.src_addr = asrc->paddr + ASRC_FIFO_OUT_DATA;
config_be.dst_addr = dma_params_be->addr;
} else {
config_be.dst_addr = asrc->paddr + ASRC_FIFO_IN_DATA;
config_be.src_addr = dma_params_be->addr;
}
ret = dmaengine_slave_config(pair->dma_chan[dir], &config_be);
if (ret) {
dev_err(dev, "failed to config DMA channel for Back-End\n");
if (pair->req_dma_chan)
dma_release_channel(pair->dma_chan[dir]);
return ret;
}
return 0;
}
static int rockchip_asrc_dma_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_pcm_runtime *runtime = substream->runtime;
struct rockchip_asrc_pair *pair = runtime->private_data;
u8 dir = tx ? OUT : IN;
if (pair->dma_chan[!dir])
dma_release_channel(pair->dma_chan[!dir]);
/* release dev_to_dev chan if we aren't reusing the Back-End one */
if (pair->dma_chan[dir] && pair->req_dma_chan)
dma_release_channel(pair->dma_chan[dir]);
pair->dma_chan[!dir] = NULL;
pair->dma_chan[dir] = NULL;
return 0;
}
static int rockchip_asrc_dma_startup(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dmaengine_dai_dma_data *dma_data;
struct device *dev = component->dev;
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
struct rockchip_asrc_pair *pair;
struct dma_chan *tmp_chan = NULL;
u8 dir = tx ? OUT : IN;
bool release_pair = true;
int ret = 0;
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;
}
pair = kzalloc(sizeof(*pair), GFP_KERNEL);
if (!pair)
return -ENOMEM;
pair->asrc = asrc;
runtime->private_data = pair;
/* Request a dummy pair, which will be released later.
* Request pair function needs channel num as input, for this
* dummy pair, we just request "1" channel temporarily.
*/
/* Request a dummy dma channel, which will be released later. */
dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
tmp_chan = rockchip_asrc_get_dma_channel(asrc, dir);
if (IS_ERR(tmp_chan)) {
dev_err(dev, "failed to get dma channel\n");
ret = -EINVAL;
goto req_pair_err;
}
/* Refine the snd_rk_hardware according to caps of DMA. */
ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream,
dma_data,
&snd_rk_hardware,
tmp_chan);
if (ret < 0) {
dev_err(dev, "failed to refine runtime hwparams\n");
goto out;
}
release_pair = false;
snd_soc_set_runtime_hwparams(substream, &snd_rk_hardware);
out:
dma_release_channel(tmp_chan);
req_pair_err:
if (release_pair)
kfree(pair);
return ret;
}
static int rockchip_asrc_dma_shutdown(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct rockchip_asrc_pair *pair = runtime->private_data;
if (!pair)
return 0;
kfree(pair);
runtime->private_data = NULL;
return 0;
}
static snd_pcm_uframes_t rockchip_asrc_dma_pcm_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct rockchip_asrc_pair *pair = runtime->private_data;
return bytes_to_frames(substream->runtime, pair->pos);
}
static int rockchip_asrc_dma_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
int ret;
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;
}
return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
card->dev, RK_ASRC_DMABUF_SIZE);
}
static const char * const asrc_link_dai_text[] = {
"asrc0", "asrc1", "asrc2", "asrc3",
"asrc4", "asrc5", "asrc6", "asrc7",
"asrc8", "asrc9", "asrc10", "asrc11",
"asrc12", "asrc13", "asrc14", "asrc15",
"sai0", "sai1", "sai2", "sai3",
"sai4", "sai5", "sai6", "sai7",
"sai8", "sai9", "sai10", "sai11",
"sai12", "sai13", "sai14", "sai15",
"pdm0", "pdm1", "pdm2", "pdm3",
"pdm4", "pdm5", "pdm6", "pdm7",
"spdiftx0", "spdiftx1", "spdiftx2", "spdiftx4",
"spdiftx4", "spdiftx5", "spdiftx6", "spdiftx7",
"spdifrx0", "spdifrx1", "spdifrx2", "spdifrx4",
"spdifrx4", "spdifrx5", "spdifrx6", "spdifrx7" };
static const struct soc_enum asrc_tx_link_dai_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(asrc_link_dai_text), asrc_link_dai_text);
static const struct soc_enum asrc_rx_link_dai_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(asrc_link_dai_text), asrc_link_dai_text);
static int rockchip_asrc_tx_dai_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct rockchip_asrc *asrc = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = asrc->dst_link_dai_id;
return 0;
}
static int rockchip_asrc_tx_dai_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct rockchip_asrc *asrc = snd_soc_component_get_drvdata(component);
int num;
num = ucontrol->value.enumerated.item[0];
if (num >= ARRAY_SIZE(asrc_link_dai_text))
return -EINVAL;
asrc->dst_link_dai_id = num;
return 1;
}
static int rockchip_asrc_rx_dai_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct rockchip_asrc *asrc = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = asrc->src_link_dai_id;
return 0;
}
static int rockchip_asrc_rx_dai_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct rockchip_asrc *asrc = snd_soc_component_get_drvdata(component);
int num;
num = ucontrol->value.enumerated.item[0];
if (num >= ARRAY_SIZE(asrc_link_dai_text))
return -EINVAL;
asrc->src_link_dai_id = num;
return 1;
}
static int rockchip_asrc_resample_rate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct rockchip_asrc *asrc = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = asrc->resample_rate;
return 0;
}
static int rockchip_asrc_resample_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct rockchip_asrc *asrc = snd_soc_component_get_drvdata(component);
if ((ucontrol->value.integer.value[0] < 0) ||
(ucontrol->value.integer.value[0] > 192000))
return -EINVAL;
asrc->resample_rate = ucontrol->value.integer.value[0];
return 1;
}
/*
* 1.resamplerate
* 2.dev rx & tx lrck select
*/
static const struct snd_kcontrol_new rockchip_asrc_controls[] = {
SOC_ENUM_EXT("TX LINK DAI Select", asrc_tx_link_dai_enum,
rockchip_asrc_tx_dai_get, rockchip_asrc_tx_dai_put),
SOC_ENUM_EXT("RX LINK DAI Select", asrc_rx_link_dai_enum,
rockchip_asrc_rx_dai_get, rockchip_asrc_rx_dai_put),
SOC_SINGLE_EXT("SET RESAMPLE RATE", 0, 0, 192000, 0,
rockchip_asrc_resample_rate_get,
rockchip_asrc_resample_rate_put),
};
static const struct snd_soc_component_driver rockchip_asrc_component = {
.name = DRV_NAME,
.controls = rockchip_asrc_controls,
.num_controls = ARRAY_SIZE(rockchip_asrc_controls),
.hw_params = rockchip_asrc_dma_hw_params,
.hw_free = rockchip_asrc_dma_hw_free,
.trigger = rockchip_asrc_dma_trigger,
.open = rockchip_asrc_dma_startup,
.close = rockchip_asrc_dma_shutdown,
.pointer = rockchip_asrc_dma_pcm_pointer,
.pcm_construct = rockchip_asrc_dma_pcm_new,
.legacy_dai_naming = 1,
};
static int rockchip_asrc_init(struct rockchip_asrc *asrc)
{
/* clear interrupt */
regmap_write(asrc->regmap, ASRC_INT_CON, 0x0);
/* Set the dma config */
regmap_update_bits(asrc->regmap, ASRC_DMA_THRESH,
ASRC_DMA_RX_THRESH_MSK | ASRC_DMA_TX_THRESH_MSK |
ASRC_OUT_THRESH_MSK | ASRC_IN_THRESH_MSK |
ASRC_NEG_THRESH_MSK | ASRC_POS_THRESH_MSK,
ASRC_DMA_RX_THRESH(3) | ASRC_DMA_TX_THRESH(0x3) |
ASRC_OUT_THRESH(11) | ASRC_IN_THRESH(3) |
ASRC_NEG_THRESH(3) | ASRC_POS_THRESH(0x1c));
/*
* Set default mode: real time, S2D, track and ratio exc
* track: track clock, but no adjust ratio
* ratio exc: adjust ratio
* ratio filt: sliding average filtering ratio to prevent anomalies
*/
regmap_update_bits(asrc->regmap, ASRC_CON,
ASRC_RATIO_TRACK_MODE | ASRC_RATIO_TRACK_MSK | ASRC_RATIO_EXC_MSK |
ASRC_RATIO_FILT_MSK | ASRC_REAL_TIME_MODE_MSK | ASRC_MODE_MSK,
ASRC_RATIO_TRACK_MODE | ASRC_RATIO_TRACK_DIS | ASRC_RATIO_EXC_DIS |
ASRC_RATIO_FILT_EN | ASRC_S2D | ASRC_REAL_TIME);
regmap_update_bits(asrc->regmap, ASRC_TRACK_PERIOD,
ASRC_RATIO_TRACK_DIV_MSK | ASRC_RATIO_TRACK_PERIOD_MSK,
ASRC_RATIO_TRACK_DIV(3) | ASRC_RATIO_TRACK_PERIOD(1023));
return 0;
}
static int rockchip_asrc_runtime_suspend(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
regcache_cache_only(asrc->regmap, true);
clk_disable_unprepare(asrc->mclk);
clk_disable_unprepare(asrc->hclk);
rockchip_asrc_lrck_clks_dis(asrc);
return 0;
}
static int rockchip_asrc_runtime_resume(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(asrc->hclk);
if (ret)
goto err_hclk;
ret = clk_prepare_enable(asrc->mclk);
if (ret)
goto err_mclk;
ret = rockchip_asrc_lrck_clks_en(asrc);
if (ret)
goto err_asrc_lrck_clks;
regcache_cache_only(asrc->regmap, false);
regcache_mark_dirty(asrc->regmap);
ret = regcache_sync(asrc->regmap);
if (ret)
goto err_regmap;
return 0;
err_regmap:
rockchip_asrc_lrck_clks_dis(asrc);
err_asrc_lrck_clks:
clk_disable_unprepare(asrc->mclk);
err_mclk:
clk_disable_unprepare(asrc->hclk);
err_hclk:
return ret;
}
static irqreturn_t rockchip_asrc_isr(int irq, void *devid)
{
struct rockchip_asrc *asrc = (struct rockchip_asrc *)devid;
u32 status;
regmap_read(asrc->regmap, ASRC_INT_ST, &status);
/* clear the interrupt */
regmap_write(asrc->regmap, ASRC_INT_ST, status);
if (status & ASRC_FIFO_OUT_EMPTY_ST)
dev_err_ratelimited(asrc->dev, "ASRC FIFO out empty\n");
if (status & ASRC_FIFO_OUT_FULL_ST)
dev_err_ratelimited(asrc->dev, "ASRC FIFO out full\n");
if (status & ASRC_FIFO_IN_EMPTY_ST)
dev_err_ratelimited(asrc->dev, "ASRC FIFO in empty\n");
if (status & ASRC_FIFO_IN_FULL_ST)
dev_err_ratelimited(asrc->dev, "ASRC FIFO in full\n");
if (status & ASRC_CONV_ERROR_ST)
dev_err_ratelimited(asrc->dev, "ASRC conv error\n");
return IRQ_HANDLED;
}
static int rockchip_asrc_get_clk_all_parents_name(struct clk *clk, char *clk_names[], int n)
{
struct clk_hw *hw = __clk_get_hw(clk);
unsigned int num_parents = clk_hw_get_num_parents(hw);
struct clk_hw *parent;
int i;
if (num_parents > n)
num_parents = n;
for (i = 0; i < num_parents; i++) {
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
break;
clk_names[i] = (char *)clk_hw_get_name(parent);
}
return i;
}
static struct clk *rockchip_asrc_get_clk_parent_by_name(struct clk *clk, char *clk_names[], int n, char *name)
{
struct clk_hw *hw = __clk_get_hw(clk);
struct clk_hw *parent = NULL;
char *result = NULL;
int i;
for (i = 0; i < n; i++) {
result = strstr(clk_names[i], name);
if (!result) {
continue;
} else {
parent = clk_hw_get_parent_by_index(hw, i);
return parent->clk;
}
}
return ERR_PTR(-ENOENT);
}
static struct clk *rockchip_asrc_get_clk_parent(struct clk *clk, char *clk_names[], int n, char *name)
{
struct clk *parent;
char *name_temp;
int name_len;
parent = rockchip_asrc_get_clk_parent_by_name(clk, clk_names, n, name);
if (!IS_ERR(parent))
return parent;
name_len = strlen(name);
if ((name_len > 1) && (name[name_len - 1] == '0') &&
(name[name_len - 2] < '0' || name[name_len - 2] > '9')) {
name_temp = kstrdup(name, GFP_KERNEL);
if (!name_temp)
return ERR_PTR(-ENOMEM);
name_temp[name_len - 1] = 0;
parent = rockchip_asrc_get_clk_parent_by_name(clk, clk_names, n, name_temp);
kfree(name_temp);
return parent;
}
return ERR_PTR(-ENOENT);
}
static void rockchip_asrc_lrck_div_set(struct rockchip_asrc *asrc)
{
int dst_lrck_div = 0, src_lrck_div = 0;
switch (asrc->src_link_dai_id) {
case DAI_ID_ASRC0 ... DAI_ID_ASRC15:
src_lrck_div = asrc->soc_data->lrck_source_freq / asrc->sample_rate;
break;
case DAI_ID_SPDIF_TX0 ... DAI_ID_SPDIF_RX7:
src_lrck_div = 128;
break;
default:
break;
}
switch (asrc->dst_link_dai_id) {
case DAI_ID_ASRC0 ... DAI_ID_ASRC15:
dst_lrck_div = asrc->soc_data->lrck_source_freq / asrc->resample_rate;
break;
case DAI_ID_SPDIF_TX0 ... DAI_ID_SPDIF_RX7:
dst_lrck_div = 128;
break;
default:
break;
}
if (dst_lrck_div) {
regmap_update_bits(asrc->regmap, ASRC_CLKDIV_CON,
ASRC_DST_LRCK_DIV_MSK |
ASRC_DST_LRCK_DIV_CON_MSK,
ASRC_DST_LRCK_DIV_EN |
ASRC_DST_LRCK_DIV(dst_lrck_div));
}
if (src_lrck_div) {
regmap_update_bits(asrc->regmap, ASRC_CLKDIV_CON,
ASRC_SRC_LRCK_DIV_MSK |
ASRC_SRC_LRCK_DIV_CON_MSK,
ASRC_SRC_LRCK_DIV_EN |
ASRC_SRC_LRCK_DIV(src_lrck_div));
}
}
static void rockchip_asrc_lrck_div_close(struct rockchip_asrc *asrc)
{
regmap_update_bits(asrc->regmap, ASRC_CLKDIV_CON,
ASRC_DST_LRCK_DIV_MSK,
ASRC_DST_LRCK_DIV_DIS);
regmap_update_bits(asrc->regmap, ASRC_CLKDIV_CON,
ASRC_SRC_LRCK_DIV_MSK,
ASRC_SRC_LRCK_DIV_DIS);
}
static int rk3506_asrc_lrck_clk_init(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
asrc->src_lrck = devm_clk_get(dev, "src_lrck");
if (IS_ERR(asrc->src_lrck)) {
dev_err(dev, "Failed to get src_clk\n");
return PTR_ERR(asrc->src_lrck);
}
asrc->dst_lrck = devm_clk_get(dev, "dst_lrck");
if (IS_ERR(asrc->dst_lrck)) {
dev_err(dev, "Failed to get dst_lrck\n");
return PTR_ERR(asrc->dst_lrck);
}
return 0;
}
static int rk3506_asrc_lrck_clk_set(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
char *clk_names[DAI_ID_MAX] = {0};
int n;
n = rockchip_asrc_get_clk_all_parents_name(asrc->dst_lrck, clk_names, ARRAY_SIZE(clk_names));
asrc->dst_lrck_parent = rockchip_asrc_get_clk_parent(asrc->dst_lrck, clk_names, n,
(char *)asrc_link_dai_text[asrc->dst_link_dai_id]);
if (IS_ERR(asrc->dst_lrck_parent)) {
dev_err(dev, "Failed to get dst_lrck_parent\n");
return PTR_ERR(asrc->dst_lrck_parent);
}
n = rockchip_asrc_get_clk_all_parents_name(asrc->src_lrck, clk_names, ARRAY_SIZE(clk_names));
asrc->src_lrck_parent = rockchip_asrc_get_clk_parent(asrc->src_lrck, clk_names, n,
(char *)asrc_link_dai_text[asrc->src_link_dai_id]);
if (IS_ERR(asrc->src_lrck_parent)) {
dev_err(dev, "Failed to get src_lrck_parent\n");
return PTR_ERR(asrc->src_lrck_parent);
}
clk_set_parent(asrc->src_lrck, asrc->src_lrck_parent);
clk_set_parent(asrc->dst_lrck, asrc->dst_lrck_parent);
if (rockchip_asrc_is_link_mem(asrc->src_link_dai_id)) {
if (clk_set_rate(asrc->src_lrck_parent, asrc->soc_data->lrck_source_freq)) {
dev_err(asrc->dev, "Failed to set src_lrck_parent, freq is %d\n",
asrc->soc_data->lrck_source_freq);
return -EINVAL;
}
}
if (rockchip_asrc_is_link_mem(asrc->dst_link_dai_id)) {
if (clk_set_rate(asrc->dst_lrck_parent, asrc->soc_data->lrck_source_freq)) {
dev_err(asrc->dev, "Failed to set dst_lrck_parent, freq is %d\n",
asrc->soc_data->lrck_source_freq);
return -EINVAL;
}
}
rockchip_asrc_lrck_div_set(asrc);
return 0;
}
static int rk3506_asrc_lrck_clk_en(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
int ret = 0;
ret = clk_prepare_enable(asrc->src_lrck_parent);
if (ret)
return ret;
ret = clk_prepare_enable(asrc->dst_lrck_parent);
if (ret)
goto err_dst_lrck_parent;
ret = clk_prepare_enable(asrc->src_lrck);
if (ret)
goto err_src_lrck;
ret = clk_prepare_enable(asrc->dst_lrck);
if (ret)
goto err_dst_lrck;
return 0;
err_dst_lrck:
clk_disable_unprepare(asrc->src_lrck);
err_src_lrck:
clk_disable_unprepare(asrc->dst_lrck_parent);
err_dst_lrck_parent:
clk_disable_unprepare(asrc->src_lrck_parent);
return ret;
}
static int rk3506_asrc_lrck_clk_dis(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
clk_disable_unprepare(asrc->src_lrck);
clk_disable_unprepare(asrc->dst_lrck);
clk_disable_unprepare(asrc->src_lrck_parent);
clk_disable_unprepare(asrc->dst_lrck_parent);
rockchip_asrc_lrck_div_close(asrc);
return 0;
}
static int rk3576_asrc_lrck_clk_init(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
struct device_node *node = dev->of_node;
asrc->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
if (IS_ERR(asrc->grf))
return PTR_ERR(asrc->grf);
asrc->cru_src0 = devm_clk_get(dev, "cru_src0");
if (IS_ERR(asrc->cru_src0)) {
dev_err(dev, "Failed to get cru_src0\n");
return PTR_ERR(asrc->cru_src0);
}
asrc->cru_src1 = devm_clk_get(dev, "cru_src1");
if (IS_ERR(asrc->cru_src1)) {
dev_err(dev, "Failed to get cru_src1\n");
return PTR_ERR(asrc->cru_src1);
}
return 0;
}
static int rk3576_asrc_lrck_clk_set(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
unsigned int dst_val = 0, src_val = 0;
switch (asrc->dst_link_dai_id) {
case DAI_ID_ASRC0:
dst_val = RK3576_DST_LRCK_FROM_CRU0;
break;
case DAI_ID_ASRC1:
dst_val = RK3576_DST_LRCK_FROM_CRU1;
break;
case DAI_ID_SAI0:
dst_val = RK3576_DST_LRCK_FROM_SAI0;
break;
case DAI_ID_SAI1:
dst_val = RK3576_DST_LRCK_FROM_SAI1;
break;
case DAI_ID_SAI2:
dst_val = RK3576_DST_LRCK_FROM_SAI2;
break;
case DAI_ID_SAI3:
dst_val = RK3576_DST_LRCK_FROM_SAI3;
break;
case DAI_ID_SAI4:
dst_val = RK3576_DST_LRCK_FROM_SAI4;
break;
case DAI_ID_SAI5:
dst_val = RK3576_DST_LRCK_FROM_SAI5;
break;
case DAI_ID_SAI6:
dst_val = RK3576_DST_LRCK_FROM_SAI6;
break;
case DAI_ID_SAI7:
dst_val = RK3576_DST_LRCK_FROM_SAI7;
break;
case DAI_ID_SAI8:
dst_val = RK3576_DST_LRCK_FROM_SAI8;
break;
case DAI_ID_SAI9:
dst_val = RK3576_DST_LRCK_FROM_SAI9;
break;
case DAI_ID_SPDIF_TX0:
dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX0;
break;
case DAI_ID_SPDIF_TX1:
dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX1;
break;
case DAI_ID_SPDIF_TX2:
dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX2;
break;
case DAI_ID_SPDIF_TX3:
dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX3;
break;
case DAI_ID_SPDIF_TX4:
dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX4;
break;
case DAI_ID_SPDIF_TX5:
dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX5;
break;
default:
return -EINVAL;
}
switch (asrc->src_link_dai_id) {
case DAI_ID_ASRC0:
src_val = RK3576_SRC_LRCK_FROM_CRU0;
break;
case DAI_ID_ASRC1:
src_val = RK3576_SRC_LRCK_FROM_CRU1;
break;
case DAI_ID_SAI0:
src_val = RK3576_SRC_LRCK_FROM_SAI0;
break;
case DAI_ID_SAI1:
src_val = RK3576_SRC_LRCK_FROM_SAI1;
break;
case DAI_ID_SAI2:
src_val = RK3576_SRC_LRCK_FROM_SAI2;
break;
case DAI_ID_SAI3:
src_val = RK3576_SRC_LRCK_FROM_SAI3;
break;
case DAI_ID_SAI4:
src_val = RK3576_SRC_LRCK_FROM_SAI4;
break;
case DAI_ID_SAI5:
src_val = RK3576_SRC_LRCK_FROM_SAI5;
break;
case DAI_ID_SAI6:
src_val = RK3576_SRC_LRCK_FROM_SAI6;
break;
case DAI_ID_SAI7:
src_val = RK3576_SRC_LRCK_FROM_SAI7;
break;
case DAI_ID_SAI8:
src_val = RK3576_SRC_LRCK_FROM_SAI8;
break;
case DAI_ID_SAI9:
src_val = RK3576_SRC_LRCK_FROM_SAI9;
break;
case DAI_ID_PDM0:
src_val = RK3576_SRC_LRCK_FROM_PDM0;
break;
case DAI_ID_PDM1:
src_val = RK3576_SRC_LRCK_FROM_PDM1;
break;
case DAI_ID_SPDIF_RX0:
src_val = RK3576_SRC_LRCK_FROM_SPDIF_RX0;
break;
case DAI_ID_SPDIF_RX1:
src_val = RK3576_SRC_LRCK_FROM_SPDIF_RX1;
break;
case DAI_ID_SPDIF_RX2:
src_val = RK3576_SRC_LRCK_FROM_SPDIF_RX2;
break;
default:
return -EINVAL;
}
if (asrc->paddr == RK3576_ASRC0) {
regmap_write(asrc->grf, RK3576_SYS_GRF_SOC_CON9,
ASRC0_4CH_SRC_SEL_MASK | ASRC0_4CH_DST_SEL_MASK |
ASRC0_4CH_SRC_SEL(src_val) | ASRC0_4CH_DST_SEL(dst_val));
} else if (asrc->paddr == RK3576_ASRC1) {
regmap_write(asrc->grf, RK3576_SYS_GRF_SOC_CON9,
ASRC1_4CH_SRC_SEL_MASK | ASRC1_4CH_SRC_SEL(src_val));
regmap_write(asrc->grf, RK3576_SYS_GRF_SOC_CON10,
ASRC1_4CH_DST_SEL_MASK | ASRC1_4CH_DST_SEL(dst_val));
} else if (asrc->paddr == RK3576_ASRC2) {
regmap_write(asrc->grf, RK3576_SYS_GRF_SOC_CON10,
ASRC2_2CH_SRC_SEL_MASK | ASRC2_2CH_DST_SEL_MASK |
ASRC2_2CH_SRC_SEL(src_val) | ASRC2_2CH_DST_SEL(dst_val));
} else if (asrc->paddr == RK3576_ASRC3) {
regmap_write(asrc->grf, RK3576_SYS_GRF_SOC_CON11,
ASRC3_2CH_SRC_SEL_MASK | ASRC3_2CH_DST_SEL_MASK |
ASRC3_2CH_SRC_SEL(src_val) | ASRC3_2CH_DST_SEL(dst_val));
} else {
return -EINVAL;
}
if (rockchip_asrc_is_link_mem(asrc->src_link_dai_id)) {
if (clk_set_rate(asrc->cru_src0, asrc->soc_data->lrck_source_freq)) {
dev_err(asrc->dev, "Failed to set cru_src0, freq is %d\n",
asrc->soc_data->lrck_source_freq);
return -EINVAL;
}
}
if (rockchip_asrc_is_link_mem(asrc->dst_link_dai_id)) {
if (clk_set_rate(asrc->cru_src1, asrc->soc_data->lrck_source_freq)) {
dev_err(asrc->dev, "Failed to set cru_src1, freq is %d\n",
asrc->soc_data->lrck_source_freq);
return -EINVAL;
}
}
rockchip_asrc_lrck_div_set(asrc);
return 0;
}
static int rk3576_asrc_lrck_clk_en(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
int ret = 0;
ret = clk_prepare_enable(asrc->cru_src0);
if (ret)
return ret;
ret = clk_prepare_enable(asrc->cru_src1);
if (ret)
clk_disable_unprepare(asrc->cru_src0);
return ret;
}
static int rk3576_asrc_lrck_clk_dis(struct device *dev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(dev);
clk_disable_unprepare(asrc->cru_src0);
clk_disable_unprepare(asrc->cru_src1);
rockchip_asrc_lrck_div_close(asrc);
return 0;
}
static const struct rk_asrc_soc_data rk3506_data = {
.lrck_clk_init = rk3506_asrc_lrck_clk_init,
.lrck_clk_set = rk3506_asrc_lrck_clk_set,
.lrck_clk_en = rk3506_asrc_lrck_clk_en,
.lrck_clk_dis = rk3506_asrc_lrck_clk_dis,
.lrck_source_freq = 98304000,
};
static const struct rk_asrc_soc_data rk3576_data = {
.lrck_clk_init = rk3576_asrc_lrck_clk_init,
.lrck_clk_set = rk3576_asrc_lrck_clk_set,
.lrck_clk_en = rk3576_asrc_lrck_clk_en,
.lrck_clk_dis = rk3576_asrc_lrck_clk_dis,
.lrck_source_freq = 49152000,
};
static const struct of_device_id rockchip_asrc_match[] = {
{ .compatible = "rockchip,rk3506-asrc", .data = &rk3506_data},
{ .compatible = "rockchip,rk3576-asrc", .data = &rk3576_data},
{}
};
MODULE_DEVICE_TABLE(of, rockchip_asrc_match);
static int rockchip_asrc_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct rockchip_asrc *asrc;
struct resource *res;
void __iomem *regs;
int ret, irq;
asrc = devm_kzalloc(&pdev->dev, sizeof(*asrc), GFP_KERNEL);
if (!asrc)
return -ENOMEM;
asrc->dev = &pdev->dev;
asrc->pdev = pdev;
dev_set_drvdata(&pdev->dev, asrc);
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
asrc->paddr = res->start;
asrc->soc_data = device_get_match_data(&pdev->dev);
if (!asrc->soc_data)
return -EINVAL;
asrc->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&rockchip_asrc_regmap_config);
if (IS_ERR(asrc->regmap)) {
dev_err(&pdev->dev, "failed to init regmap\n");
return PTR_ERR(asrc->regmap);
}
irq = platform_get_irq_optional(pdev, 0);
if (irq > 0) {
ret = devm_request_irq(&pdev->dev, irq, rockchip_asrc_isr,
IRQF_SHARED, node->name, asrc);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %d\n", irq);
return ret;
}
}
ret = asrc->soc_data->lrck_clk_init(asrc->dev);
if (ret)
return ret;
asrc->mclk = devm_clk_get(&pdev->dev, "mclk");
if (IS_ERR(asrc->mclk)) {
dev_err(&pdev->dev, "Failed to get mclk\n");
return PTR_ERR(asrc->mclk);
}
asrc->hclk = devm_clk_get(&pdev->dev, "hclk");
if (IS_ERR(asrc->hclk)) {
dev_err(&pdev->dev, "Failed to get hclk\n");
return PTR_ERR(asrc->hclk);
}
ret = clk_prepare_enable(asrc->hclk);
if (ret)
return ret;
/* Set the default value here */
asrc->resample_rate = DEFAULT_SAMPLE_RATE;
asrc->sample_rate = DEFAULT_SAMPLE_RATE;
/* Set the default link dai here */
asrc->dst_link_dai_id = DAI_ID_UNKNOWN;
asrc->src_link_dai_id = DAI_ID_UNKNOWN;
/* DMA data init */
asrc->dma_data_tx.addr = res->start + ASRC_TXDR;
asrc->dma_data_tx.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
asrc->dma_data_tx.maxburst = MAXBURST_PER_FIFO;
asrc->dma_data_rx.addr = res->start + ASRC_RXDR;
asrc->dma_data_rx.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
asrc->dma_data_rx.maxburst = MAXBURST_PER_FIFO;
platform_set_drvdata(pdev, asrc);
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = rockchip_asrc_runtime_resume(&pdev->dev);
if (ret)
goto err_runtime_disable;
}
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
goto err_runtime_suspend;
ret = rockchip_asrc_init(asrc);
if (ret) {
dev_err(&pdev->dev, "Asrc init error.\n");
goto err_runtime_suspend;
}
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_asrc_component,
&rockchip_asrc_dai, 1);
if (ret)
goto err_runtime_suspend;
clk_disable_unprepare(asrc->hclk);
return 0;
err_runtime_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
rockchip_asrc_runtime_suspend(&pdev->dev);
err_runtime_disable:
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(asrc->hclk);
return ret;
}
static int rockchip_asrc_remove(struct platform_device *pdev)
{
struct rockchip_asrc *asrc = dev_get_drvdata(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
rockchip_asrc_runtime_suspend(&pdev->dev);
clk_disable_unprepare(asrc->hclk);
return 0;
}
static const struct dev_pm_ops rockchip_asrc_pm_ops = {
SET_RUNTIME_PM_OPS(rockchip_asrc_runtime_suspend, rockchip_asrc_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver rockchip_asrc_driver = {
.probe = rockchip_asrc_probe,
.remove = rockchip_asrc_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = rockchip_asrc_match,
.pm = &rockchip_asrc_pm_ops,
},
};
module_platform_driver(rockchip_asrc_driver);
MODULE_DESCRIPTION("Rockchip ASRC ASoC Interface");
MODULE_AUTHOR("Jason Zhu <jason.zhu@rock-chips.com>");
MODULE_LICENSE("GPL");