From 64b3855098cbf1036a85b2b3e672ad33ca08858e Mon Sep 17 00:00:00 2001 From: Jason Zhu Date: Fri, 2 Aug 2024 17:01:14 +0800 Subject: [PATCH 1/5] ASoC: rockchip: asrc: support rk3506 asrc Change-Id: Icc872b5ca37fbba0f43a6a088871c4d6dc96ab43 Signed-off-by: Jason Zhu --- sound/soc/rockchip/rockchip_asrc.c | 784 ++++++++++++++++++++--------- 1 file changed, 543 insertions(+), 241 deletions(-) diff --git a/sound/soc/rockchip/rockchip_asrc.c b/sound/soc/rockchip/rockchip_asrc.c index f214e0847546..09004d9b6bd7 100644 --- a/sound/soc/rockchip/rockchip_asrc.c +++ b/sound/soc/rockchip/rockchip_asrc.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -41,8 +42,8 @@ #define MAXBURST_PER_FIFO 8 #define DEFAULT_SAMPLE_RATE 48000 #define ASRC_DEFAULT_CLK 200000000 -#define CRU_LRCK_SOURCE_FREQ 49152000 +/* Platform Definition */ /* rk3576 */ #define RK3576_SYS_GRF_SOC_CON9 0x24 #define ASRC0_4CH_SRC_SEL_SHIFT 0 @@ -52,8 +53,8 @@ #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) -#define ASRC1_4CH_SRC_SEL(x) (x << (ASRC1_4CH_SRC_SEL_SHIFT + 16)) +#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 @@ -116,66 +117,72 @@ #define RK3576_ASRC2 0x2a6b0000 #define RK3576_ASRC3 0x2a6c0000 -#define DAI_ID_MEM 0 -#define DAI_ID_SAI0 1 -#define DAI_ID_SAI1 2 -#define DAI_ID_SAI2 3 -#define DAI_ID_SAI3 4 -#define DAI_ID_SAI4 5 -#define DAI_ID_SAI5 6 -#define DAI_ID_SAI6 7 -#define DAI_ID_SAI7 8 -#define DAI_ID_SAI8 9 -#define DAI_ID_SAI9 10 -#define DAI_ID_SAI10 11 -#define DAI_ID_SAI11 12 -#define DAI_ID_SAI12 13 -#define DAI_ID_SAI13 14 -#define DAI_ID_SAI14 15 -#define DAI_ID_SAI15 16 -#define DAI_ID_ASRC0 17 -#define DAI_ID_ASRC1 18 -#define DAI_ID_ASRC2 19 -#define DAI_ID_ASRC3 20 -#define DAI_ID_ASRC4 21 -#define DAI_ID_ASRC5 22 -#define DAI_ID_ASRC6 23 -#define DAI_ID_ASRC7 24 -#define DAI_ID_ASRC8 25 -#define DAI_ID_ASRC9 26 -#define DAI_ID_ASRC10 27 -#define DAI_ID_ASRC11 28 -#define DAI_ID_ASRC12 29 -#define DAI_ID_ASRC13 30 -#define DAI_ID_ASRC14 31 -#define DAI_ID_ASRC15 32 -#define DAI_ID_PDM0 33 -#define DAI_ID_PDM1 34 -#define DAI_ID_PDM2 35 -#define DAI_ID_PDM3 36 -#define DAI_ID_PDM4 37 -#define DAI_ID_PDM5 38 -#define DAI_ID_PDM6 39 -#define DAI_ID_PDM7 40 -#define DAI_ID_SPDIF_TX0 41 -#define DAI_ID_SPDIF_TX1 42 -#define DAI_ID_SPDIF_TX2 43 -#define DAI_ID_SPDIF_TX3 44 -#define DAI_ID_SPDIF_TX4 45 -#define DAI_ID_SPDIF_TX5 46 -#define DAI_ID_SPDIF_TX6 47 -#define DAI_ID_SPDIF_TX7 48 -#define DAI_ID_SPDIF_RX0 49 -#define DAI_ID_SPDIF_RX1 50 -#define DAI_ID_SPDIF_RX2 51 -#define DAI_ID_SPDIF_RX3 52 -#define DAI_ID_SPDIF_RX4 53 -#define DAI_ID_SPDIF_RX5 54 -#define DAI_ID_SPDIF_RX6 55 -#define DAI_ID_SPDIF_RX7 56 +/* 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 (*select_lrck_clk)(struct device *dev); + 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 { @@ -197,21 +204,28 @@ struct rockchip_asrc { struct regmap *regmap; struct clk *mclk; struct clk *hclk; - struct clk *cru_src0; + struct clk *cru_src0; // Used if src or dst is MEM. struct clk *cru_src1; - unsigned long paddr; + 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 tx_link_dai_id; /* This must be set firstly by amixer/tinymixer */ - int rx_link_dai_id; /* This must be set firstly by amixer/tinymixer */ + 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 asrc_calculate_ratio(struct rockchip_asrc *asrc, +static int rockchip_asrc_calculate_ratio(struct rockchip_asrc *asrc, int numerator, int denominator) { int i, integerPart, remainder, ratio, digit; @@ -238,6 +252,18 @@ static int asrc_calculate_ratio(struct rockchip_asrc *asrc, 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; @@ -250,37 +276,63 @@ static struct dma_chan *rockchip_asrc_get_dma_channel(struct rockchip_asrc *asrc return chan; } -static void rockchip_asrc_ratio_update_start(struct rockchip_asrc *asrc, int stream) +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, - asrc_calculate_ratio(asrc, asrc->sample_rate, asrc->resample_rate)); + rockchip_asrc_calculate_ratio(asrc, asrc->sample_rate, asrc->resample_rate)); } -static int rockchip_asrc_clks_setup(struct rockchip_asrc *asrc) +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->select_lrck_clk(asrc->dev); + return asrc->soc_data->lrck_clk_set(asrc->dev); } -static void rockchip_asrc_start(struct rockchip_asrc *asrc, int stream) +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; - rockchip_asrc_ratio_update_start(asrc, stream); + 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 (!asrc->rx_link_dai_id && asrc->tx_link_dai_id) - val = ASRC_M2D; - else if (asrc->rx_link_dai_id && !asrc->tx_link_dai_id) - val = ASRC_S2M; - else if (!asrc->rx_link_dai_id && !asrc->tx_link_dai_id) + 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; @@ -293,9 +345,11 @@ static void rockchip_asrc_start(struct rockchip_asrc *asrc, int stream) 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 void rockchip_asrc_stop(struct rockchip_asrc *asrc, int stream) +static int rockchip_asrc_stop(struct rockchip_asrc *asrc, int stream) { regmap_update_bits(asrc->regmap, ASRC_CON, ASRC_OUT_MSK | ASRC_IN_MSK, @@ -303,6 +357,9 @@ static void rockchip_asrc_stop(struct rockchip_asrc *asrc, int stream) 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, @@ -339,16 +396,19 @@ static int rockchip_asrc_hw_params(struct snd_pcm_substream *substream, /* 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; @@ -359,9 +419,6 @@ static int rockchip_asrc_hw_params(struct snd_pcm_substream *substream, ASRC_OFMT_MSK | ASRC_IFMT_MSK, val); - if (rockchip_asrc_clks_setup(asrc)) - return -EINVAL; - return 0; } @@ -375,12 +432,12 @@ static int rockchip_asrc_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - rockchip_asrc_start(asrc, substream->stream); + ret = rockchip_asrc_start(asrc, substream->stream); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - rockchip_asrc_stop(asrc, substream->stream); + ret = rockchip_asrc_stop(asrc, substream->stream); break; default: ret = -EINVAL; @@ -508,6 +565,7 @@ static bool rockchip_asrc_writeable_reg(struct device *dev, unsigned int reg) 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: @@ -520,18 +578,7 @@ static bool rockchip_asrc_writeable_reg(struct device *dev, unsigned int reg) static bool rockchip_asrc_volatile_reg(struct device *dev, unsigned int reg) { - switch (reg) { - case ASRC_INT_CON: - 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; - } + return true; } static bool rockchip_asrc_precious_reg(struct device *dev, unsigned int reg) @@ -684,7 +731,7 @@ static int rockchip_asrc_dma_hw_params(struct snd_soc_component *component, struct device *dev_be; u8 dir = tx ? OUT : IN; dma_cap_mask_t mask; - int ret; + int ret, dma_thresh; /* Fetch the Back-End dma_data from DPCM */ for_each_dpcm_be(rtd, stream, dpcm) { @@ -716,9 +763,11 @@ static int rockchip_asrc_dma_hw_params(struct snd_soc_component *component, dma_params_fe->maxburst = dma_params_be->maxburst; pair->dma_chan[!dir] = rockchip_asrc_get_dma_channel(asrc, !dir); - if (!pair->dma_chan[!dir]) { + if (IS_ERR_OR_NULL(pair->dma_chan[!dir])) { dev_err(dev, "failed to request DMA channel\n"); - return -EINVAL; + 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); @@ -727,6 +776,18 @@ static int rockchip_asrc_dma_hw_params(struct snd_soc_component *component, 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"); @@ -742,17 +803,19 @@ static int rockchip_asrc_dma_hw_params(struct snd_soc_component *component, * 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. */ - 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 (!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 -EINVAL; + return PTR_ERR(tmp_chan); } } @@ -920,20 +983,20 @@ static int rockchip_asrc_dma_pcm_new(struct snd_soc_component *component, } static const char * const asrc_link_dai_text[] = { - "MEM", "SAI0", "SAI1", "SAI2", "SAI3", - "SAI4", "SAI5", "SAI6", "SAI7", - "SAI8", "SAI9", "SAI10", "SAI11", - "SAI12", "SAI13", "SAI14", "SAI15", - "ASRC0", "ASRC1", "ASRC2", "ASRC3", - "ASRC4", "ASRC5", "ASRC6", "ASRC7", - "ASRC8", "ASRC9", "ASRC10", "ASRC11", - "ASRC12", "ASRC13", "ASRC14", "ASRC15", - "PDM0", "PDM1", "PDM2", "PDM3", - "PDM4", "PDM5", "PDM6", "PDM7", - "SPDIF_TX0", "SPDIF_TX1", "SPDIF_TX2", "SPDIF_TX4", - "SPDIF_TX4", "SPDIF_TX5", "SPDIF_TX6", "SPDIF_TX7", - "SPDIF_RX0", "SPDIF_RX1", "SPDIF_RX2", "SPDIF_RX4", - "SPDIF_RX4", "SPDIF_RX5", "SPDIF_RX6", "SPDIF_RX7" }; + "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); @@ -946,7 +1009,7 @@ static int rockchip_asrc_tx_dai_get(struct snd_kcontrol *kcontrol, 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->tx_link_dai_id; + ucontrol->value.enumerated.item[0] = asrc->dst_link_dai_id; return 0; } @@ -962,7 +1025,7 @@ static int rockchip_asrc_tx_dai_put(struct snd_kcontrol *kcontrol, if (num >= ARRAY_SIZE(asrc_link_dai_text)) return -EINVAL; - asrc->tx_link_dai_id = num; + asrc->dst_link_dai_id = num; return 1; } @@ -973,7 +1036,7 @@ static int rockchip_asrc_rx_dai_get(struct snd_kcontrol *kcontrol, 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->rx_link_dai_id; + ucontrol->value.enumerated.item[0] = asrc->src_link_dai_id; return 0; } @@ -989,7 +1052,7 @@ static int rockchip_asrc_rx_dai_put(struct snd_kcontrol *kcontrol, if (num >= ARRAY_SIZE(asrc_link_dai_text)) return -EINVAL; - asrc->rx_link_dai_id = num; + asrc->src_link_dai_id = num; return 1; } @@ -1089,19 +1152,7 @@ static int rockchip_asrc_runtime_suspend(struct device *dev) clk_disable_unprepare(asrc->mclk); clk_disable_unprepare(asrc->hclk); - if (asrc->tx_link_dai_id == DAI_ID_MEM) { - clk_disable_unprepare(asrc->cru_src0); - regmap_update_bits(asrc->regmap, ASRC_CLKDIV_CON, - ASRC_DST_LRCK_DIV_MSK, - ASRC_DST_LRCK_DIV_DIS); - } - - if (asrc->rx_link_dai_id == DAI_ID_MEM) { - clk_disable_unprepare(asrc->cru_src1); - regmap_update_bits(asrc->regmap, ASRC_CLKDIV_CON, - ASRC_SRC_LRCK_DIV_MSK, - ASRC_SRC_LRCK_DIV_DIS); - } + rockchip_asrc_lrck_clks_dis(asrc); return 0; } @@ -1119,32 +1170,20 @@ static int rockchip_asrc_runtime_resume(struct device *dev) if (ret) goto err_mclk; - if (asrc->tx_link_dai_id == DAI_ID_MEM) { - ret = clk_prepare_enable(asrc->cru_src0); - if (ret) - goto error_cru_src0; - } - - if (asrc->rx_link_dai_id == DAI_ID_MEM) { - ret = clk_prepare_enable(asrc->cru_src1); - if (ret) - goto error_cru_src1; - } + 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); + ret = regcache_sync(asrc->regmap); if (ret) goto err_regmap; return 0; err_regmap: - if (asrc->rx_link_dai_id == DAI_ID_MEM) - clk_disable_unprepare(asrc->cru_src1); -error_cru_src1: - if (asrc->tx_link_dai_id == DAI_ID_MEM) - clk_disable_unprepare(asrc->cru_src0); -error_cru_src0: + rockchip_asrc_lrck_clks_dis(asrc); +err_asrc_lrck_clks: clk_disable_unprepare(asrc->mclk); err_mclk: clk_disable_unprepare(asrc->hclk); @@ -1178,149 +1217,374 @@ static irqreturn_t rockchip_asrc_isr(int irq, void *devid) return IRQ_HANDLED; } -static void rockchip_asrc_set_lrck_div_from_cru(struct rockchip_asrc *asrc) +static int rockchip_asrc_get_clk_all_parents_name(struct clk *clk, char *clk_names[], int n) { - if (asrc->tx_link_dai_id == DAI_ID_MEM) { - if (clk_set_rate(asrc->cru_src0, CRU_LRCK_SOURCE_FREQ)) { - dev_err(asrc->dev, "Failed to set cru_src0\n"); - return; - } + struct clk_hw *hw = __clk_get_hw(clk); + unsigned int num_parents = clk_hw_get_num_parents(hw); + struct clk_hw *parent; + int i; - clk_prepare_enable(asrc->cru_src0); + 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(CRU_LRCK_SOURCE_FREQ / - asrc->resample_rate * 2)); - + ASRC_DST_LRCK_DIV(dst_lrck_div)); } - if (asrc->rx_link_dai_id == DAI_ID_MEM) { - if (clk_set_rate(asrc->cru_src1, CRU_LRCK_SOURCE_FREQ)) { - dev_err(asrc->dev, "Failed to set cru_src1\n"); - return; - } - - clk_prepare_enable(asrc->cru_src1); + 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(CRU_LRCK_SOURCE_FREQ / - asrc->sample_rate * 2)); + ASRC_SRC_LRCK_DIV(src_lrck_div)); } } -static int rk3576_asrc_select_lrck_clk(struct device *dev) +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); - unsigned int tx_val = 0, rx_val = 0; - switch (asrc->tx_link_dai_id) { - case DAI_ID_MEM: - tx_val = RK3576_DST_LRCK_FROM_CRU0; + 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: - tx_val = RK3576_DST_LRCK_FROM_SAI0; + dst_val = RK3576_DST_LRCK_FROM_SAI0; break; case DAI_ID_SAI1: - tx_val = RK3576_DST_LRCK_FROM_SAI1; + dst_val = RK3576_DST_LRCK_FROM_SAI1; break; case DAI_ID_SAI2: - tx_val = RK3576_DST_LRCK_FROM_SAI2; + dst_val = RK3576_DST_LRCK_FROM_SAI2; break; case DAI_ID_SAI3: - tx_val = RK3576_DST_LRCK_FROM_SAI3; + dst_val = RK3576_DST_LRCK_FROM_SAI3; break; case DAI_ID_SAI4: - tx_val = RK3576_DST_LRCK_FROM_SAI4; + dst_val = RK3576_DST_LRCK_FROM_SAI4; break; case DAI_ID_SAI5: - tx_val = RK3576_DST_LRCK_FROM_SAI5; + dst_val = RK3576_DST_LRCK_FROM_SAI5; break; case DAI_ID_SAI6: - tx_val = RK3576_DST_LRCK_FROM_SAI6; + dst_val = RK3576_DST_LRCK_FROM_SAI6; break; case DAI_ID_SAI7: - tx_val = RK3576_DST_LRCK_FROM_SAI7; + dst_val = RK3576_DST_LRCK_FROM_SAI7; break; case DAI_ID_SAI8: - tx_val = RK3576_DST_LRCK_FROM_SAI8; + dst_val = RK3576_DST_LRCK_FROM_SAI8; break; case DAI_ID_SAI9: - tx_val = RK3576_DST_LRCK_FROM_SAI9; + dst_val = RK3576_DST_LRCK_FROM_SAI9; break; case DAI_ID_SPDIF_TX0: - tx_val = RK3576_DST_LRCK_FROM_SPDIF_TX0; + dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX0; break; case DAI_ID_SPDIF_TX1: - tx_val = RK3576_DST_LRCK_FROM_SPDIF_TX1; + dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX1; break; case DAI_ID_SPDIF_TX2: - tx_val = RK3576_DST_LRCK_FROM_SPDIF_TX2; + dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX2; break; case DAI_ID_SPDIF_TX3: - tx_val = RK3576_DST_LRCK_FROM_SPDIF_TX3; + dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX3; break; case DAI_ID_SPDIF_TX4: - tx_val = RK3576_DST_LRCK_FROM_SPDIF_TX4; + dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX4; break; case DAI_ID_SPDIF_TX5: - tx_val = RK3576_DST_LRCK_FROM_SPDIF_TX5; + dst_val = RK3576_DST_LRCK_FROM_SPDIF_TX5; break; default: return -EINVAL; } - switch (asrc->rx_link_dai_id) { - case DAI_ID_MEM: - rx_val = RK3576_SRC_LRCK_FROM_CRU1; + 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: - rx_val = RK3576_SRC_LRCK_FROM_SAI0; + src_val = RK3576_SRC_LRCK_FROM_SAI0; break; case DAI_ID_SAI1: - rx_val = RK3576_SRC_LRCK_FROM_SAI1; + src_val = RK3576_SRC_LRCK_FROM_SAI1; break; case DAI_ID_SAI2: - rx_val = RK3576_SRC_LRCK_FROM_SAI2; + src_val = RK3576_SRC_LRCK_FROM_SAI2; break; case DAI_ID_SAI3: - rx_val = RK3576_SRC_LRCK_FROM_SAI3; + src_val = RK3576_SRC_LRCK_FROM_SAI3; break; case DAI_ID_SAI4: - rx_val = RK3576_SRC_LRCK_FROM_SAI4; + src_val = RK3576_SRC_LRCK_FROM_SAI4; break; case DAI_ID_SAI5: - rx_val = RK3576_SRC_LRCK_FROM_SAI5; + src_val = RK3576_SRC_LRCK_FROM_SAI5; break; case DAI_ID_SAI6: - rx_val = RK3576_SRC_LRCK_FROM_SAI6; + src_val = RK3576_SRC_LRCK_FROM_SAI6; break; case DAI_ID_SAI7: - rx_val = RK3576_SRC_LRCK_FROM_SAI7; + src_val = RK3576_SRC_LRCK_FROM_SAI7; break; case DAI_ID_SAI8: - rx_val = RK3576_SRC_LRCK_FROM_SAI8; + src_val = RK3576_SRC_LRCK_FROM_SAI8; break; case DAI_ID_SAI9: - rx_val = RK3576_SRC_LRCK_FROM_SAI9; + src_val = RK3576_SRC_LRCK_FROM_SAI9; break; case DAI_ID_PDM0: - rx_val = RK3576_SRC_LRCK_FROM_PDM0; + src_val = RK3576_SRC_LRCK_FROM_PDM0; break; case DAI_ID_PDM1: - rx_val = RK3576_SRC_LRCK_FROM_PDM1; + src_val = RK3576_SRC_LRCK_FROM_PDM1; break; case DAI_ID_SPDIF_RX0: - rx_val = RK3576_SRC_LRCK_FROM_SPDIF_RX0; + src_val = RK3576_SRC_LRCK_FROM_SPDIF_RX0; break; case DAI_ID_SPDIF_RX1: - rx_val = RK3576_SRC_LRCK_FROM_SPDIF_RX1; + src_val = RK3576_SRC_LRCK_FROM_SPDIF_RX1; break; case DAI_ID_SPDIF_RX2: - rx_val = RK3576_SRC_LRCK_FROM_SPDIF_RX2; + src_val = RK3576_SRC_LRCK_FROM_SPDIF_RX2; break; default: return -EINVAL; @@ -1329,34 +1593,90 @@ static int rk3576_asrc_select_lrck_clk(struct device *dev) 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(rx_val) | ASRC0_4CH_DST_SEL(tx_val)); + 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(rx_val)); + 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(tx_val)); + 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(rx_val) | ASRC2_2CH_DST_SEL(tx_val)); + 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(rx_val) | ASRC3_2CH_DST_SEL(tx_val)); + ASRC3_2CH_SRC_SEL(src_val) | ASRC3_2CH_DST_SEL(dst_val)); } else { return -EINVAL; } - rockchip_asrc_set_lrck_div_from_cru(asrc); + 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 = { - .select_lrck_clk = rk3576_asrc_select_lrck_clk, + .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}, {} }; @@ -1374,10 +1694,6 @@ static int rockchip_asrc_probe(struct platform_device *pdev) if (!asrc) return -ENOMEM; - asrc->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf"); - if (IS_ERR(asrc->grf)) - return PTR_ERR(asrc->grf); - asrc->dev = &pdev->dev; asrc->pdev = pdev; dev_set_drvdata(&pdev->dev, asrc); @@ -1407,6 +1723,10 @@ static int rockchip_asrc_probe(struct platform_device *pdev) } } + 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"); @@ -1419,18 +1739,6 @@ static int rockchip_asrc_probe(struct platform_device *pdev) return PTR_ERR(asrc->hclk); } - asrc->cru_src0 = devm_clk_get(&pdev->dev, "cru_src0"); - if (IS_ERR(asrc->cru_src0)) { - dev_err(&pdev->dev, "Failed to get cru_src0\n"); - return PTR_ERR(asrc->cru_src0); - } - - asrc->cru_src1 = devm_clk_get(&pdev->dev, "cru_src1"); - if (IS_ERR(asrc->cru_src1)) { - dev_err(&pdev->dev, "Failed to get cru_src1\n"); - return PTR_ERR(asrc->cru_src1); - } - ret = clk_prepare_enable(asrc->hclk); if (ret) return ret; @@ -1439,8 +1747,8 @@ static int rockchip_asrc_probe(struct platform_device *pdev) asrc->sample_rate = DEFAULT_SAMPLE_RATE; /* Set the default link dai here */ - asrc->tx_link_dai_id = DAI_ID_SAI0; - asrc->rx_link_dai_id = DAI_ID_SAI1; + 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; @@ -1498,12 +1806,6 @@ static int rockchip_asrc_remove(struct platform_device *pdev) clk_disable_unprepare(asrc->hclk); - if (asrc->tx_link_dai_id == DAI_ID_MEM) - clk_disable_unprepare(asrc->cru_src0); - - if (asrc->rx_link_dai_id == DAI_ID_MEM) - clk_disable_unprepare(asrc->cru_src1); - return 0; } From c465ea7656858c199f5ee09749e84c1656bc44f4 Mon Sep 17 00:00:00 2001 From: Jason Zhu Date: Fri, 9 Aug 2024 16:00:18 +0800 Subject: [PATCH 2/5] ARM: dts: rockchip: rk3506: add asrc node Change-Id: I306053f2db038f61f49d224cece8809ec213e838 Signed-off-by: Jason Zhu --- arch/arm/boot/dts/rk3506.dtsi | 75 +++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/arch/arm/boot/dts/rk3506.dtsi b/arch/arm/boot/dts/rk3506.dtsi index 118e940bebab..008580d38ccf 100644 --- a/arch/arm/boot/dts/rk3506.dtsi +++ b/arch/arm/boot/dts/rk3506.dtsi @@ -69,6 +69,13 @@ clock-output-names = "xin32k"; }; + clk_spdifrx_to_asrc: clk-spdifrx-to-asrc { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_spdifrx_to_asrc"; + }; + mclkin_sai0: mclkin-sai0 { compatible = "fixed-clock"; #clock-cells = <0>; @@ -141,6 +148,34 @@ assigned-clocks = <&pvtpll_core>; assigned-clock-rates = <1200000000>; }; + + sai0_fs: sai0-fs { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "sai0_fs"; + }; + + sai1_fs: sai1-fs { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "sai1_fs"; + }; + + sai2_fs: sai2-fs { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "sai2_fs"; + }; + + sai3_fs: sai3-fs { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "sai3_fs"; + }; }; cpus { @@ -909,6 +944,46 @@ status = "disabled"; }; + asrc0: asrc@ff3c0000 { + compatible = "rockchip,rk3506-asrc"; + reg = <0xff3c0000 0x1000>; + interrupts = ; + clocks = <&cru CLK_ASRC0>, <&cru HCLK_ASRC0>, + <&cru LRCK_ASRC0_SRC>, <&cru LRCK_ASRC0_DST>; + clock-names = "mclk", "hclk", + "src_lrck", "dst_lrck"; + // dmas = <&dmac0 0 0xff2880a4 0x00010001 0xff2880a8 0x00030002>, + // <&dmac0 1 0xff2880a4 0x00020002 0xff2880a8 0x000c0008>; + dmas = <&dmac1 16 0xff2880a4 0x00010000 0x0 0x0>, + <&dmac1 17 0xff2880a4 0x00020000 0x0 0x0>; + dma-names = "rx", "tx"; + resets = <&cru SRST_ASRC0>, <&cru SRST_H_ASRC0>; + reset-names = "m", "h"; + #sound-dai-cells = <0>; + sound-name-prefix = "ASRC0"; + status = "disabled"; + }; + + asrc1: asrc@ff3d0000 { + compatible = "rockchip,rk3506-asrc"; + reg = <0xff3d0000 0x1000>; + interrupts = ; + clocks = <&cru CLK_ASRC1>, <&cru HCLK_ASRC1>, + <&cru LRCK_ASRC1_SRC>, <&cru LRCK_ASRC1_DST>; + clock-names = "mclk", "hclk", + "src_lrck", "dst_lrck"; + // dmas = <&dmac0 2 0xff2880a4 0x00040004 0xff2880a8 0x00300020>, + // <&dmac0 3 0xff2880a4 0x00080008 0xff2880a8 0x00c00080>; + dmas = <&dmac1 18 0xff2880a4 0x00040000 0x0 0x0>, + <&dmac1 19 0xff2880a4 0x00080000 0x0 0x0>; + dma-names = "rx", "tx"; + resets = <&cru SRST_ASRC1>, <&cru SRST_H_ASRC1>; + reset-names = "m", "h"; + #sound-dai-cells = <0>; + sound-name-prefix = "ASRC1"; + status = "disabled"; + }; + mmc: mmc@ff480000 { compatible = "rockchip,rk3506-dw-mshc", "rockchip,rk3288-dw-mshc"; reg = <0xff480000 0x4000>; From 736d89f344156b75393b01ab2c0e3a06c39e110f Mon Sep 17 00:00:00 2001 From: Shuangjie Lin Date: Wed, 28 Aug 2024 16:40:46 +0800 Subject: [PATCH 3/5] driver: rknpu: Update rknpu driver, version: 0.9.8 * Fix muti-process run error * Fix mult-process domain switch error Signed-off-by: Shuangjie Lin Change-Id: I3052eca6ef577dc6420dd4c80a3ac7d0f17211ba --- drivers/rknpu/include/rknpu_drv.h | 5 +- drivers/rknpu/include/rknpu_iommu.h | 3 + drivers/rknpu/rknpu_drv.c | 8 ++- drivers/rknpu/rknpu_gem.c | 57 +++++++++++++++++-- drivers/rknpu/rknpu_iommu.c | 85 +++++++++++++++++++++++++---- drivers/rknpu/rknpu_job.c | 19 +++++-- 6 files changed, 151 insertions(+), 26 deletions(-) diff --git a/drivers/rknpu/include/rknpu_drv.h b/drivers/rknpu/include/rknpu_drv.h index 136be076b2d3..90e66d68b727 100644 --- a/drivers/rknpu/include/rknpu_drv.h +++ b/drivers/rknpu/include/rknpu_drv.h @@ -29,10 +29,10 @@ #define DRIVER_NAME "rknpu" #define DRIVER_DESC "RKNPU driver" -#define DRIVER_DATE "20240424" +#define DRIVER_DATE "20240828" #define DRIVER_MAJOR 0 #define DRIVER_MINOR 9 -#define DRIVER_PATCHLEVEL 7 +#define DRIVER_PATCHLEVEL 8 #define LOG_TAG "RKNPU" @@ -174,6 +174,7 @@ struct rknpu_device { int iommu_domain_id; struct iommu_domain *iommu_domains[RKNPU_MAX_IOMMU_DOMAIN_NUM]; struct sg_table *cache_sgt[RKNPU_CACHE_SG_TABLE_NUM]; + atomic_t iommu_domain_refcount; }; struct rknpu_session { diff --git a/drivers/rknpu/include/rknpu_iommu.h b/drivers/rknpu/include/rknpu_iommu.h index 75b77c63978e..40ea58e282d8 100644 --- a/drivers/rknpu/include/rknpu_iommu.h +++ b/drivers/rknpu/include/rknpu_iommu.h @@ -49,6 +49,9 @@ void rknpu_iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int rknpu_iommu_init_domain(struct rknpu_device *rknpu_dev); int rknpu_iommu_switch_domain(struct rknpu_device *rknpu_dev, int domain_id); void rknpu_iommu_free_domains(struct rknpu_device *rknpu_dev); +int rknpu_iommu_domain_get_and_switch(struct rknpu_device *rknpu_dev, + int domain_id); +int rknpu_iommu_domain_put(struct rknpu_device *rknpu_dev); #if KERNEL_VERSION(5, 10, 0) < LINUX_VERSION_CODE int iommu_get_dma_cookie(struct iommu_domain *domain); diff --git a/drivers/rknpu/rknpu_drv.c b/drivers/rknpu/rknpu_drv.c index 58c6798bcb63..e8afa9778c14 100644 --- a/drivers/rknpu/rknpu_drv.c +++ b/drivers/rknpu/rknpu_drv.c @@ -485,8 +485,11 @@ static int rknpu_action(struct rknpu_device *rknpu_dev, ret = 0; break; case RKNPU_SET_IOMMU_DOMAIN_ID: { - ret = rknpu_iommu_switch_domain(rknpu_dev, - *(int32_t *)&args->value); + ret = rknpu_iommu_domain_get_and_switch( + rknpu_dev, *(int32_t *)&args->value); + if (ret) + break; + rknpu_iommu_domain_put(rknpu_dev); break; } default: @@ -1497,6 +1500,7 @@ static int rknpu_probe(struct platform_device *pdev) rknpu_power_off(rknpu_dev); atomic_set(&rknpu_dev->power_refcount, 0); atomic_set(&rknpu_dev->cmdline_power_refcount, 0); + atomic_set(&rknpu_dev->iommu_domain_refcount, 0); rknpu_debugger_init(rknpu_dev); rknpu_init_timer(rknpu_dev); diff --git a/drivers/rknpu/rknpu_gem.c b/drivers/rknpu/rknpu_gem.c index 319d86b8cc95..a5c5354ed85c 100644 --- a/drivers/rknpu/rknpu_gem.c +++ b/drivers/rknpu/rknpu_gem.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -695,8 +696,13 @@ rknpu_gem_object_create(struct drm_device *drm, unsigned int flags, if (IS_ERR(rknpu_obj)) return rknpu_obj; - if (!rknpu_iommu_switch_domain(rknpu_dev, iommu_domain_id)) - rknpu_obj->iommu_domain_id = iommu_domain_id; + if (rknpu_iommu_domain_get_and_switch(rknpu_dev, iommu_domain_id)) { + LOG_DEV_ERROR(rknpu_dev->dev, "%s error\n", __func__); + rknpu_gem_release(rknpu_obj); + return ERR_PTR(-EINVAL); + } + + rknpu_obj->iommu_domain_id = iommu_domain_id; if (!rknpu_dev->iommu_en && (flags & RKNPU_MEM_NON_CONTIGUOUS)) { /* @@ -788,6 +794,8 @@ rknpu_gem_object_create(struct drm_device *drm, unsigned int flags, goto gem_release; } + rknpu_iommu_domain_put(rknpu_dev); + LOG_DEBUG( "created dma addr: %pad, cookie: %p, ddr size: %lu, sram size: %lu, nbuf size: %lu, attrs: %#lx, flags: %#x, iommu domain id: %d\n", &rknpu_obj->dma_addr, rknpu_obj->cookie, rknpu_obj->size, @@ -805,6 +813,8 @@ mm_free: gem_release: rknpu_gem_release(rknpu_obj); + rknpu_iommu_domain_put(rknpu_dev); + return ERR_PTR(ret); } @@ -812,13 +822,26 @@ void rknpu_gem_object_destroy(struct rknpu_gem_object *rknpu_obj) { struct drm_gem_object *obj = &rknpu_obj->base; struct rknpu_device *rknpu_dev = obj->dev->dev_private; + int wait_count = 0; + int ret = -EINVAL; LOG_DEBUG( "destroy dma addr: %pad, cookie: %p, size: %lu, attrs: %#lx, flags: %#x, handle count: %d\n", &rknpu_obj->dma_addr, rknpu_obj->cookie, rknpu_obj->size, rknpu_obj->dma_attrs, rknpu_obj->flags, obj->handle_count); - rknpu_iommu_switch_domain(rknpu_dev, rknpu_obj->iommu_domain_id); + do { + ret = rknpu_iommu_domain_get_and_switch( + rknpu_dev, rknpu_obj->iommu_domain_id); + + if (ret && ++wait_count >= 3) { + LOG_DEV_ERROR( + rknpu_dev->dev, + "failed to destroy dma addr: %pad, size: %lu\n", + &rknpu_obj->dma_addr, rknpu_obj->size); + return; + } + } while (ret); /* * do not release memory region from exporter. @@ -847,6 +870,7 @@ void rknpu_gem_object_destroy(struct rknpu_gem_object *rknpu_obj) } rknpu_gem_release(rknpu_obj); + rknpu_iommu_domain_put(rknpu_dev); } int rknpu_gem_create_ioctl(struct drm_device *drm, void *data, @@ -903,16 +927,29 @@ int rknpu_gem_destroy_ioctl(struct drm_device *drm, void *data, struct rknpu_device *rknpu_dev = drm->dev_private; struct rknpu_gem_object *rknpu_obj = NULL; struct rknpu_mem_destroy *args = data; + int ret = 0; + int wait_count = 0; rknpu_obj = rknpu_gem_object_find(file_priv, args->handle); if (!rknpu_obj) return -EINVAL; - rknpu_iommu_switch_domain(rknpu_dev, rknpu_obj->iommu_domain_id); + do { + ret = rknpu_iommu_domain_get_and_switch( + rknpu_dev, rknpu_obj->iommu_domain_id); - // rknpu_gem_object_put(&rknpu_obj->base); + if (ret && ++wait_count >= 3) { + LOG_DEV_ERROR(rknpu_dev->dev, + "failed to destroy memory\n"); + return ret; + } + } while (ret); - return rknpu_gem_handle_destroy(file_priv, args->handle); + ret = rknpu_gem_handle_destroy(file_priv, args->handle); + + rknpu_iommu_domain_put(rknpu_dev); + + return ret; } #if RKNPU_GEM_ALLOC_FROM_PAGES @@ -1647,6 +1684,12 @@ int rknpu_gem_sync_ioctl(struct drm_device *dev, void *data, if (!(rknpu_obj->flags & RKNPU_MEM_CACHEABLE)) return -EINVAL; + if (rknpu_iommu_domain_get_and_switch(rknpu_dev, + rknpu_obj->iommu_domain_id)) { + LOG_DEV_ERROR(rknpu_dev->dev, "%s error\n", __func__); + return -EINVAL; + } + if (!(rknpu_obj->flags & RKNPU_MEM_NON_CONTIGUOUS)) { if (args->flags & RKNPU_MEM_SYNC_TO_DEVICE) { dma_sync_single_range_for_device( @@ -1708,5 +1751,7 @@ int rknpu_gem_sync_ioctl(struct drm_device *dev, void *data, } } + rknpu_iommu_domain_put(rknpu_dev); + return 0; } diff --git a/drivers/rknpu/rknpu_iommu.c b/drivers/rknpu/rknpu_iommu.c index 4797f0fec598..efa97f39c8cc 100644 --- a/drivers/rknpu/rknpu_iommu.c +++ b/drivers/rknpu/rknpu_iommu.c @@ -5,9 +5,13 @@ */ #include +#include +#include #include "rknpu_iommu.h" +#define RKNPU_SWITCH_DOMAIN_WAIT_TIME_MS 6000 + dma_addr_t rknpu_iommu_dma_alloc_iova(struct iommu_domain *domain, size_t size, u64 dma_limit, struct device *dev, bool size_aligned) @@ -434,11 +438,8 @@ int rknpu_iommu_switch_domain(struct rknpu_device *rknpu_dev, int domain_id) if (!bus) return -EFAULT; - mutex_lock(&rknpu_dev->domain_lock); - src_domain_id = rknpu_dev->iommu_domain_id; if (domain_id == src_domain_id) { - mutex_unlock(&rknpu_dev->domain_lock); return 0; } @@ -447,7 +448,6 @@ int rknpu_iommu_switch_domain(struct rknpu_device *rknpu_dev, int domain_id) LOG_DEV_ERROR( rknpu_dev->dev, "mismatch domain get from iommu_get_domain_for_dev\n"); - mutex_unlock(&rknpu_dev->domain_lock); return -EINVAL; } @@ -466,7 +466,6 @@ int rknpu_iommu_switch_domain(struct rknpu_device *rknpu_dev, int domain_id) "failed to reattach src iommu domain, id: %d\n", src_domain_id); } - mutex_unlock(&rknpu_dev->domain_lock); return ret; } rknpu_dev->iommu_domain_id = domain_id; @@ -477,7 +476,6 @@ int rknpu_iommu_switch_domain(struct rknpu_device *rknpu_dev, int domain_id) if (!dst_domain) { LOG_DEV_ERROR(rknpu_dev->dev, "failed to allocate iommu domain\n"); - mutex_unlock(&rknpu_dev->domain_lock); return -EIO; } // init domain iova_cookie @@ -491,7 +489,6 @@ int rknpu_iommu_switch_domain(struct rknpu_device *rknpu_dev, int domain_id) "failed to attach iommu domain, id: %d, ret: %d\n", domain_id, ret); iommu_domain_free(dst_domain); - mutex_unlock(&rknpu_dev->domain_lock); return ret; } @@ -508,19 +505,74 @@ int rknpu_iommu_switch_domain(struct rknpu_device *rknpu_dev, int domain_id) // reset default iommu domain rknpu_dev->iommu_group->default_domain = dst_domain; - mutex_unlock(&rknpu_dev->domain_lock); - LOG_INFO("switch iommu domain from %d to %d\n", src_domain_id, domain_id); return ret; } +int rknpu_iommu_domain_get_and_switch(struct rknpu_device *rknpu_dev, + int domain_id) +{ + unsigned long timeout_jiffies = + msecs_to_jiffies(RKNPU_SWITCH_DOMAIN_WAIT_TIME_MS); + unsigned long start = jiffies; + int ret = -EINVAL; + + while (true) { + mutex_lock(&rknpu_dev->domain_lock); + + if (domain_id == rknpu_dev->iommu_domain_id) { + atomic_inc(&rknpu_dev->iommu_domain_refcount); + mutex_unlock(&rknpu_dev->domain_lock); + break; + } + + if (atomic_read(&rknpu_dev->iommu_domain_refcount) == 0) { + ret = rknpu_iommu_switch_domain(rknpu_dev, domain_id); + if (ret) { + LOG_DEV_ERROR( + rknpu_dev->dev, + "failed to switch iommu domain, id: %d, ret: %d\n", + domain_id, ret); + mutex_unlock(&rknpu_dev->domain_lock); + return ret; + } + atomic_inc(&rknpu_dev->iommu_domain_refcount); + mutex_unlock(&rknpu_dev->domain_lock); + break; + } + + mutex_unlock(&rknpu_dev->domain_lock); + + usleep_range(10, 100); + if (time_after(jiffies, start + timeout_jiffies)) { + LOG_DEV_ERROR( + rknpu_dev->dev, + "switch iommu domain time out, failed to switch iommu domain, id: %d\n", + domain_id); + return -EINVAL; + } + } + + return 0; +} + +int rknpu_iommu_domain_put(struct rknpu_device *rknpu_dev) +{ + atomic_dec(&rknpu_dev->iommu_domain_refcount); + + return 0; +} + void rknpu_iommu_free_domains(struct rknpu_device *rknpu_dev) { int i = 0; - rknpu_iommu_switch_domain(rknpu_dev, 0); + if (rknpu_iommu_domain_get_and_switch(rknpu_dev, 0)) { + LOG_DEV_ERROR(rknpu_dev->dev, "%s error\n", __func__); + return; + } for (i = 1; i < RKNPU_MAX_IOMMU_DOMAIN_NUM; i++) { struct iommu_domain *domain = rknpu_dev->iommu_domains[i]; @@ -533,6 +585,8 @@ void rknpu_iommu_free_domains(struct rknpu_device *rknpu_dev) rknpu_dev->iommu_domains[i] = NULL; } + + rknpu_iommu_domain_put(rknpu_dev); } #else @@ -547,6 +601,17 @@ int rknpu_iommu_switch_domain(struct rknpu_device *rknpu_dev, int domain_id) return 0; } +int rknpu_iommu_domain_get_and_switch(struct rknpu_device *rknpu_dev, + int domain_id) +{ + return 0; +} + +int rknpu_iommu_domain_put(struct rknpu_device *rknpu_dev) +{ + return 0; +} + void rknpu_iommu_free_domains(struct rknpu_device *rknpu_dev) { } diff --git a/drivers/rknpu/rknpu_job.c b/drivers/rknpu/rknpu_job.c index 312d9c73df75..23ed8e5cf841 100644 --- a/drivers/rknpu/rknpu_job.c +++ b/drivers/rknpu/rknpu_job.c @@ -210,8 +210,9 @@ static inline int rknpu_job_wait(struct rknpu_job *job) (elapse_time_us < args->timeout * 1000); spin_unlock_irqrestore(&rknpu_dev->irq_lock, flags); LOG_ERROR( - "job: %p, iommu domain id: %d, wait_count: %d, continue wait: %d, commit elapse time: %lldus, wait time: %lldus, timeout: %uus\n", - job, job->iommu_domain_id, wait_count, + "job: %p, mask: %#x, job iommu domain id: %d, dev iommu domain id: %d, wait_count: %d, continue wait: %d, commit elapse time: %lldus, wait time: %lldus, timeout: %uus\n", + job, args->core_mask, job->iommu_domain_id, + rknpu_dev->iommu_domain_id, wait_count, continue_wait, (job->hw_commit_time == 0 ? 0 : elapse_time_us), ktime_us_delta(ktime_get(), job->timestamp), @@ -452,9 +453,8 @@ static void rknpu_job_next(struct rknpu_device *rknpu_dev, int core_index) job->hw_recoder_time = job->hw_commit_time; spin_unlock_irqrestore(&rknpu_dev->irq_lock, flags); - if (atomic_dec_and_test(&job->run_count)) { + if (atomic_dec_and_test(&job->run_count)) rknpu_job_commit(job); - } } static void rknpu_job_done(struct rknpu_job *job, int ret, int core_index) @@ -485,6 +485,8 @@ static void rknpu_job_done(struct rknpu_job *job, int ret, int core_index) if (atomic_dec_and_test(&job->interrupt_count)) { int use_core_num = job->use_core_num; + rknpu_iommu_domain_put(rknpu_dev); + job->flags |= RKNPU_JOB_DONE; job->ret = ret; @@ -535,6 +537,11 @@ static void rknpu_job_schedule(struct rknpu_job *job) atomic_set(&job->interrupt_count, job->use_core_num); } + if (rknpu_iommu_domain_get_and_switch(rknpu_dev, job->iommu_domain_id)) { + job->ret = -EINVAL; + return; + } + spin_lock_irqsave(&rknpu_dev->irq_lock, flags); for (i = 0; i < rknpu_dev->config->num_irqs; i++) { if (job->args->core_mask & rknpu_core_mask(i)) { @@ -558,6 +565,8 @@ static void rknpu_job_abort(struct rknpu_job *job) unsigned long flags; int i = 0; + rknpu_iommu_domain_put(rknpu_dev); + msleep(100); spin_lock_irqsave(&rknpu_dev->irq_lock, flags); @@ -843,8 +852,6 @@ int rknpu_submit_ioctl(struct drm_device *dev, void *data, struct rknpu_device *rknpu_dev = dev_get_drvdata(dev->dev); struct rknpu_submit *args = data; - rknpu_iommu_switch_domain(rknpu_dev, args->iommu_domain_id); - return rknpu_submit(rknpu_dev, args); } #endif From 06ef7e6dc3f0fcecf83c8ebb3758f3a9c3346d14 Mon Sep 17 00:00:00 2001 From: Zhihuan He Date: Thu, 1 Aug 2024 10:03:59 +0800 Subject: [PATCH 4/5] memory: rockchip: dsmc: add dsmc local bus slave driver Change-Id: Icf4c4abe11595d4069e36265db5441f52593896a Signed-off-by: Zhihuan He --- drivers/memory/rockchip/Kconfig | 6 + drivers/memory/rockchip/Makefile | 2 + drivers/memory/rockchip/dsmc-lb-slave.c | 330 ++++++++++++++++++++++++ drivers/memory/rockchip/dsmc-lb-slave.h | 5 + 4 files changed, 343 insertions(+) create mode 100644 drivers/memory/rockchip/dsmc-lb-slave.c diff --git a/drivers/memory/rockchip/Kconfig b/drivers/memory/rockchip/Kconfig index 42944724fb20..74bd457232c7 100644 --- a/drivers/memory/rockchip/Kconfig +++ b/drivers/memory/rockchip/Kconfig @@ -8,3 +8,9 @@ config ROCKCHIP_DSMC depends on ARCH_ROCKCHIP help For enable the Rockchip DSMC driver. + +config ROCKCHIP_DSMC_SLAVE + tristate "Rockchip Double Data Rate Serial Memory Controller(DSMC) slave driver" + depends on ARCH_ROCKCHIP + help + For enable the Rockchip DSMC SLAVE driver. \ No newline at end of file diff --git a/drivers/memory/rockchip/Makefile b/drivers/memory/rockchip/Makefile index 36fca45367cb..cc7a9ffad583 100644 --- a/drivers/memory/rockchip/Makefile +++ b/drivers/memory/rockchip/Makefile @@ -5,3 +5,5 @@ obj-$(CONFIG_ROCKCHIP_DSMC) += dsmc.o dsmc-y += dsmc-controller.o dsmc-lb-device.o dsmc-host.o + +obj-$(CONFIG_ROCKCHIP_DSMC_SLAVE) += dsmc-lb-slave.o diff --git a/drivers/memory/rockchip/dsmc-lb-slave.c b/drivers/memory/rockchip/dsmc-lb-slave.c new file mode 100644 index 000000000000..f3738436615a --- /dev/null +++ b/drivers/memory/rockchip/dsmc-lb-slave.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Rockchip Electronics Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dsmc-host.h" +#include "dsmc-lb-slave.h" + +struct dsmc_soc_info { + int (*dsmc_soc_init)(struct platform_device *pdev); +}; + +struct dsmc_lb_slv_map { + phys_addr_t phys; + size_t size; +}; + +struct rockchip_dsmc_lb_slave { + const struct dsmc_soc_info *soc_info; + + struct device *dev; + /* Hardware resources */ + void __iomem *regs; + struct regmap *grf; + struct clk *aclk; + struct clk *hclk; + struct reset_control *reset; + struct reset_control *areset; + struct reset_control *hreset; + + struct dsmc_lb_slv_map lb_slv_mem; +}; + +static int rk3506_dsmc_lb_slv_init(struct platform_device *pdev) +{ + int ret = 0; + struct device *dev = &pdev->dev; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_active; + struct rockchip_dsmc_lb_slave *priv; + + priv = platform_get_drvdata(pdev); + + if (IS_ERR_OR_NULL(priv->grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + return -ENODEV; + } + + pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pinctrl)) { + dev_err(dev, "Failed to get pinctrl\n"); + return PTR_ERR(pinctrl); + } + + pinctrl_active = pinctrl_lookup_state(pinctrl, "active"); + if (IS_ERR(pinctrl_active)) { + dev_err(dev, "Failed to lookup active pinctrl state\n"); + return PTR_ERR(pinctrl_active); + } + + /* enable dsmc slave and rdyn to normal mode */ + regmap_write(priv->grf, RK3506_GRF_SOC_CON(1), + DSMC_SLAVE_ENABLE(1) | DSMC_SLAVE_RDYN_MODE(1)); + + /* The active iomux setting should be enabled after the pull-up week. */ + ret = pinctrl_select_state(pinctrl, pinctrl_active); + if (ret) { + dev_err(dev, "Failed to select active pinctrl state\n"); + return ret; + } + + return ret; +} + +static const struct dsmc_soc_info rk3506_soc_info = { + .dsmc_soc_init = rk3506_dsmc_lb_slv_init, +}; + +static const struct of_device_id dsmc_lb_slave_of_match[] = { + { .compatible = "rockchip,rk3506-dsmc-lb-slave", .data = &rk3506_soc_info }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, dsmc_lb_slave_of_match); + +static int rockchip_dsmc_platform_init(struct platform_device *pdev) +{ + struct rockchip_dsmc_lb_slave *priv; + struct device *dev = &pdev->dev; + int ret = 0; + + priv = platform_get_drvdata(pdev); + + priv->soc_info = device_get_match_data(dev); + if (!priv->soc_info) { + dev_err(dev, "Error: No device match found\n"); + return -ENODEV; + } + + ret = priv->soc_info->dsmc_soc_init(pdev); + if (ret) { + dev_err(dev, "Error: Soc init fail!\n"); + return ret; + } + + return ret; +} + +static int dsmc_lb_slave_init(struct rockchip_dsmc_lb_slave *priv) +{ + struct device *dev = priv->dev; + + if (priv->lb_slv_mem.phys < AXI_ADDR_4GB_RANGE) { + writel(priv->lb_slv_mem.phys, priv->regs + AXI_WR_ADDR_BASE); + writel(priv->lb_slv_mem.phys, priv->regs + AXI_RD_ADDR_BASE); + } else { + dev_err(dev, "Error: Invalid address for slave memory!\n"); + return -ENODEV; + } + + /* clear all h2s interrupt */ + writel(APP_H2S_INT_STA_MASK << APP_H2S_INT_STA_SHIFT, + priv->regs + APP_H2S_INT_STA); + + /* enable all h2s interrupt */ + writel(0xffffffff, priv->regs + APP_H2S_INT_STA_EN); + writel(0xffffffff, priv->regs + APP_H2S_INT_STA_SIG_EN); + + return 0; +} + +static int dsmc_lb_slave_dma_trigger(struct rockchip_dsmc_lb_slave *priv) +{ + int timeout = 1000; + + /* wait interrupt register empty */ + while (timeout-- > 0) { + if (!(readl(priv->regs + LBC_S2H_INT_STA) & + (0x1 << S2H_INT_FOR_DMA_NUM))) + break; + udelay(1); + if (timeout == 0) { + dev_err(priv->dev, "Timeout waiting for s2h interrupt empty!\n"); + return -ETIMEDOUT; + } + } + + /* trigger a slave to host interrupt which will start dma hardware mode copy */ + writel(0x1, priv->regs + APP_CON(S2H_INT_FOR_DMA_NUM)); + + return 0; +} + +static irqreturn_t rockchip_dsmc_lb_slave_irq(int irq, void *data) +{ + struct rockchip_dsmc_lb_slave *priv = data; + + if (readl(priv->regs + LBC_CON(15))) + dsmc_lb_slave_dma_trigger(priv); + + /* clear all h2s interrupt */ + writel(APP_H2S_INT_STA_MASK << APP_H2S_INT_STA_SHIFT, + priv->regs + APP_H2S_INT_STA); + + return IRQ_HANDLED; +} + +static int rk_dsmc_lb_slave_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct device_node *slv_map = NULL; + struct rockchip_dsmc_lb_slave *priv; + struct resource *mem; + int irq, ret = 0; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(priv->grf)) + dev_warn(dev, "Missing rockchip,grf property\n"); + + ret = rockchip_dsmc_platform_init(pdev); + if (ret) + return ret; + + priv->reset = devm_reset_control_get(dev, "dsmc_slv"); + if (IS_ERR(priv->reset)) { + ret = PTR_ERR(priv->reset); + dev_warn(dev, "failed to get dsmc slave reset: %d\n", ret); + } + priv->areset = devm_reset_control_get(dev, "a_dsmc_slv"); + if (IS_ERR(priv->areset)) { + ret = PTR_ERR(priv->areset); + dev_warn(dev, "failed to get dsmc slave areset: %d\n", ret); + } + priv->hreset = devm_reset_control_get(dev, "h_dsmc_slv"); + if (IS_ERR(priv->hreset)) { + ret = PTR_ERR(priv->hreset); + dev_warn(dev, "failed to get dsmc slave hreset: %d\n", ret); + } + + priv->aclk = devm_clk_get(dev, "aclk_dsmc_slv"); + if (IS_ERR(priv->aclk)) { + dev_err(dev, "Can't get dsmc_slv aclk\n"); + return PTR_ERR(priv->aclk); + } + priv->hclk = devm_clk_get(dev, "hclk_dsmc_slv"); + if (IS_ERR(priv->hclk)) { + dev_err(dev, "Can't get dsmc_slv hclk\n"); + return PTR_ERR(priv->hclk); + } + ret = clk_prepare_enable(priv->aclk); + if (ret) { + dev_err(dev, "Can't prepare enable dsmc aclk: %d\n", ret); + goto err_dis_aclk; + } + ret = clk_prepare_enable(priv->hclk); + if (ret) { + dev_err(dev, "Can't prepare enable dsmc hclk: %d\n", ret); + goto err_dis_hclk; + } + + slv_map = of_parse_phandle(np, "memory-region", 0); + if (!slv_map) { + ret = -EINVAL; + dev_err(dev, "missing memory-region property\n"); + goto err_dis_hclk; + } + + ret = of_address_to_resource(slv_map, 0, mem); + of_node_put(slv_map); + if (ret < 0) { + dev_err(dev, "memory-region missing reg property\n"); + goto err_dis_hclk; + } + if (resource_size(mem) <= 0) { + ret = -EINVAL; + dev_err(dev, "memory-region size error\n"); + goto err_dis_hclk; + } + + priv->lb_slv_mem.phys = mem->start; + priv->lb_slv_mem.size = resource_size(mem); + + priv->dev = dev; + if (dsmc_lb_slave_init(priv)) { + ret = -ENODEV; + dev_err(dev, "dsmc local bus slave init fail!\n"); + goto err_dis_hclk; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = -ENODEV; + dev_err(dev, "cannot find dsmc lb slave IRQ\n"); + goto err_dis_hclk; + } + ret = devm_request_irq(dev, irq, rockchip_dsmc_lb_slave_irq, + 0, dev_name(dev), priv); + if (ret < 0) { + dev_err(dev, "cannot request IRQ\n"); + goto err_dis_hclk; + } + + dev_info(dev, "rockchip dsmc local bus slave driver initialized\n"); + + return 0; + +err_dis_hclk: + clk_disable_unprepare(priv->hclk); +err_dis_aclk: + clk_disable_unprepare(priv->aclk); + return ret; +} + +static int rk_dsmc_lb_slave_remove(struct platform_device *pdev) +{ + struct rockchip_dsmc_lb_slave *priv; + + priv = platform_get_drvdata(pdev); + + if (priv->aclk) { + clk_disable_unprepare(priv->aclk); + priv->aclk = NULL; + } + if (priv->hclk) { + clk_disable_unprepare(priv->hclk); + priv->hclk = NULL; + } + + return 0; +} + +struct platform_driver rk_dsmc_lb_slave_driver = { + .probe = rk_dsmc_lb_slave_probe, + .remove = rk_dsmc_lb_slave_remove, + .driver = { + .name = "dsmc_lb_slave", + .of_match_table = dsmc_lb_slave_of_match, + }, +}; + +module_platform_driver(rk_dsmc_lb_slave_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhihuan He "); +MODULE_DESCRIPTION("ROCKCHIP DSMC SLAVE driver"); diff --git a/drivers/memory/rockchip/dsmc-lb-slave.h b/drivers/memory/rockchip/dsmc-lb-slave.h index 06398577486d..72368b3f32de 100644 --- a/drivers/memory/rockchip/dsmc-lb-slave.h +++ b/drivers/memory/rockchip/dsmc-lb-slave.h @@ -67,4 +67,9 @@ #define LBC_S2H_INT_STA_SIG_EN_SHIFT (0) #define LBC_S2H_INT_STA_SIG_EN_MASK (0xFFFF) +#define DSMC_SLAVE_ENABLE(n) ((0x1 << (4 + 16)) | ((n) << 4)) +#define DSMC_SLAVE_RDYN_MODE(n) ((0x1 << (5 + 16)) | ((n) << 5)) + +#define AXI_ADDR_4GB_RANGE (1ULL << 32) + #endif /* __BUS_ROCKCHIP_ROCKCHIP_DSMC_SLAVE_H */ From ffd1dbd70cc94d0f7e35cec54e47fa477958699d Mon Sep 17 00:00:00 2001 From: Zhihuan He Date: Thu, 1 Aug 2024 10:39:07 +0800 Subject: [PATCH 5/5] ARM: rk3506_defconfig: Enable DSMC and DSMC_SLAVE configs Change-Id: Ie887a72a7e26032d27ba8a193f67f48d04deb1cb Signed-off-by: Zhihuan He --- arch/arm/configs/rk3506_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/configs/rk3506_defconfig b/arch/arm/configs/rk3506_defconfig index 2c0eabcaaca7..3ad18ece3da4 100644 --- a/arch/arm/configs/rk3506_defconfig +++ b/arch/arm/configs/rk3506_defconfig @@ -306,6 +306,9 @@ CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y CONFIG_DEVFREQ_GOV_USERSPACE=y CONFIG_EXTCON=y CONFIG_EXTCON_USB_GPIO=y +CONFIG_MEMORY=y +CONFIG_ROCKCHIP_DSMC=y +CONFIG_ROCKCHIP_DSMC_SLAVE=y CONFIG_IIO=y CONFIG_ROCKCHIP_FLEXBUS_ADC=y CONFIG_ROCKCHIP_SARADC=y