aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/soc/fsl/fsl_asrc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/fsl/fsl_asrc.c')
-rw-r--r--sound/soc/fsl/fsl_asrc.c185
1 files changed, 141 insertions, 44 deletions
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 02c81d2e34ad..b793263291dc 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -11,7 +11,7 @@
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of_platform.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
#include <linux/pm_runtime.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@@ -19,6 +19,8 @@
#include "fsl_asrc.h"
#define IDEAL_RATIO_DECIMAL_DEPTH 26
+#define DIVIDER_NUM 64
+#define INIT_RETRY_NUM 50
#define pair_err(fmt, ...) \
dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
@@ -26,6 +28,9 @@
#define pair_dbg(fmt, ...) \
dev_dbg(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+#define pair_warn(fmt, ...) \
+ dev_warn(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+
/* Corresponding to process_option */
static unsigned int supported_asrc_rate[] = {
5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
@@ -101,6 +106,55 @@ static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
},
};
+/*
+ * According to RM, the divider range is 1 ~ 8,
+ * prescaler is power of 2 from 1 ~ 128.
+ */
+static int asrc_clk_divider[DIVIDER_NUM] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, /* divider = 1 */
+ 2, 4, 8, 16, 32, 64, 128, 256, /* divider = 2 */
+ 3, 6, 12, 24, 48, 96, 192, 384, /* divider = 3 */
+ 4, 8, 16, 32, 64, 128, 256, 512, /* divider = 4 */
+ 5, 10, 20, 40, 80, 160, 320, 640, /* divider = 5 */
+ 6, 12, 24, 48, 96, 192, 384, 768, /* divider = 6 */
+ 7, 14, 28, 56, 112, 224, 448, 896, /* divider = 7 */
+ 8, 16, 32, 64, 128, 256, 512, 1024, /* divider = 8 */
+};
+
+/*
+ * Check if the divider is available for internal ratio mode
+ */
+static bool fsl_asrc_divider_avail(int clk_rate, int rate, int *div)
+{
+ u32 rem, i;
+ u64 n;
+
+ if (div)
+ *div = 0;
+
+ if (clk_rate == 0 || rate == 0)
+ return false;
+
+ n = clk_rate;
+ rem = do_div(n, rate);
+
+ if (div)
+ *div = n;
+
+ if (rem != 0)
+ return false;
+
+ for (i = 0; i < DIVIDER_NUM; i++) {
+ if (n == asrc_clk_divider[i])
+ break;
+ }
+
+ if (i == DIVIDER_NUM)
+ return false;
+
+ return true;
+}
+
/**
* fsl_asrc_sel_proc - Select the pre-processing and post-processing options
* @inrate: input sample rate
@@ -330,12 +384,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
enum asrc_word_width input_word_width;
enum asrc_word_width output_word_width;
u32 inrate, outrate, indiv, outdiv;
- u32 clk_index[2], div[2], rem[2];
+ u32 clk_index[2], div[2];
u64 clk_rate;
int in, out, channels;
int pre_proc, post_proc;
struct clk *clk;
- bool ideal;
+ bool ideal, div_avail;
if (!config) {
pair_err("invalid pair config\n");
@@ -415,8 +469,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
clk_rate = clk_get_rate(clk);
- rem[IN] = do_div(clk_rate, inrate);
- div[IN] = (u32)clk_rate;
+ div_avail = fsl_asrc_divider_avail(clk_rate, inrate, &div[IN]);
/*
* The divider range is [1, 1024], defined by the hardware. For non-
@@ -425,7 +478,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
* only result in different converting speeds. So remainder does not
* matter, as long as we keep the divider within its valid range.
*/
- if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) {
+ if (div[IN] == 0 || (!ideal && !div_avail)) {
pair_err("failed to support input sample rate %dHz by asrck_%x\n",
inrate, clk_index[ideal ? OUT : IN]);
return -EINVAL;
@@ -436,13 +489,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
clk = asrc_priv->asrck_clk[clk_index[OUT]];
clk_rate = clk_get_rate(clk);
if (ideal && use_ideal_rate)
- rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
+ div_avail = fsl_asrc_divider_avail(clk_rate, IDEAL_RATIO_RATE, &div[OUT]);
else
- rem[OUT] = do_div(clk_rate, outrate);
- div[OUT] = clk_rate;
+ div_avail = fsl_asrc_divider_avail(clk_rate, outrate, &div[OUT]);
/* Output divider has the same limitation as the input one */
- if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) {
+ if (div[OUT] == 0 || (!ideal && !div_avail)) {
pair_err("failed to support output sample rate %dHz by asrck_%x\n",
outrate, clk_index[OUT]);
return -EINVAL;
@@ -531,7 +583,7 @@ static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
- int reg, retry = 10, i;
+ int reg, retry = INIT_RETRY_NUM, i;
/* Enable the current pair */
regmap_update_bits(asrc->regmap, REG_ASRCTR,
@@ -544,6 +596,10 @@ static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
reg &= ASRCFG_INIRQi_MASK(index);
} while (!reg && --retry);
+ /* NOTE: Doesn't treat initialization timeout as an error */
+ if (!retry)
+ pair_warn("initialization isn't finished\n");
+
/* Make the input fifo to ASRC STALL level */
regmap_read(asrc->regmap, REG_ASRCNCR, &reg);
for (i = 0; i < pair->channels * 4; i++)
@@ -610,7 +666,7 @@ static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv,
struct asrc_config *config = pair_priv->config;
int rate[2], select_clk[2]; /* Array size 2 means IN and OUT */
int clk_rate, clk_index;
- int i = 0, j = 0;
+ int i, j;
rate[IN] = in_rate;
rate[OUT] = out_rate;
@@ -621,8 +677,7 @@ static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv,
clk_index = asrc_priv->clk_map[j][i];
clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]);
/* Only match a perfect clock source with no remainder */
- if (clk_rate != 0 && (clk_rate / rate[j]) <= 1024 &&
- (clk_rate % rate[j]) == 0)
+ if (fsl_asrc_divider_avail(clk_rate, rate[j], NULL))
break;
}
@@ -725,13 +780,6 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
-static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
- .startup = fsl_asrc_dai_startup,
- .hw_params = fsl_asrc_dai_hw_params,
- .hw_free = fsl_asrc_dai_hw_free,
- .trigger = fsl_asrc_dai_trigger,
-};
-
static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
{
struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
@@ -742,12 +790,19 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
+ .probe = fsl_asrc_dai_probe,
+ .startup = fsl_asrc_dai_startup,
+ .hw_params = fsl_asrc_dai_hw_params,
+ .hw_free = fsl_asrc_dai_hw_free,
+ .trigger = fsl_asrc_dai_trigger,
+};
+
#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE)
static struct snd_soc_dai_driver fsl_asrc_dai = {
- .probe = fsl_asrc_dai_probe,
.playback = {
.stream_name = "ASRC-Playback",
.channels_min = 1,
@@ -1008,6 +1063,9 @@ static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
return REG_ASRDx(dir, index);
}
+static int fsl_asrc_runtime_resume(struct device *dev);
+static int fsl_asrc_runtime_suspend(struct device *dev);
+
static int fsl_asrc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -1016,6 +1074,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *regs;
int irq, ret, i;
+ u32 asrc_fmt = 0;
u32 map_idx;
char tmp[16];
u32 width;
@@ -1032,15 +1091,13 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc->private = asrc_priv;
/* Get the addresses and IRQ */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, res);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
asrc->paddr = res->start;
- asrc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
- &fsl_asrc_regmap_config);
+ asrc->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_asrc_regmap_config);
if (IS_ERR(asrc->regmap)) {
dev_err(&pdev->dev, "failed to init regmap\n");
return PTR_ERR(asrc->regmap);
@@ -1083,11 +1140,6 @@ static int fsl_asrc_probe(struct platform_device *pdev)
}
asrc_priv->soc = of_device_get_match_data(&pdev->dev);
- if (!asrc_priv->soc) {
- dev_err(&pdev->dev, "failed to get soc data\n");
- return -ENODEV;
- }
-
asrc->use_edma = asrc_priv->soc->use_edma;
asrc->get_dma_channel = fsl_asrc_get_dma_channel;
asrc->request_pair = fsl_asrc_request_pair;
@@ -1122,12 +1174,6 @@ static int fsl_asrc_probe(struct platform_device *pdev)
}
}
- ret = fsl_asrc_init(asrc);
- if (ret) {
- dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
- return ret;
- }
-
asrc->channel_avail = 10;
ret = of_property_read_u32(np, "fsl,asrc-rate",
@@ -1137,7 +1183,8 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return ret;
}
- ret = of_property_read_u32(np, "fsl,asrc-format", &asrc->asrc_format);
+ ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt);
+ asrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt;
if (ret) {
ret = of_property_read_u32(np, "fsl,asrc-width", &width);
if (ret) {
@@ -1160,31 +1207,63 @@ static int fsl_asrc_probe(struct platform_device *pdev)
}
}
- if (!(FSL_ASRC_FORMATS & (1ULL << asrc->asrc_format))) {
+ if (!(FSL_ASRC_FORMATS & pcm_format_to_bits(asrc->asrc_format))) {
dev_warn(&pdev->dev, "unsupported width, use default S24_LE\n");
asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
}
platform_set_drvdata(pdev, asrc);
- pm_runtime_enable(&pdev->dev);
spin_lock_init(&asrc->lock);
- regcache_cache_only(asrc->regmap, true);
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = fsl_asrc_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
+ goto err_pm_get_sync;
+
+ ret = fsl_asrc_init(asrc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
+ goto err_pm_get_sync;
+ }
+
+ ret = pm_runtime_put_sync(&pdev->dev);
+ if (ret < 0 && ret != -ENOSYS)
+ goto err_pm_get_sync;
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
&fsl_asrc_dai, 1);
if (ret) {
dev_err(&pdev->dev, "failed to register ASoC DAI\n");
- return ret;
+ goto err_pm_get_sync;
}
return 0;
+
+err_pm_get_sync:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ fsl_asrc_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static void fsl_asrc_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ fsl_asrc_runtime_suspend(&pdev->dev);
}
-#ifdef CONFIG_PM
static int fsl_asrc_runtime_resume(struct device *dev)
{
struct fsl_asrc *asrc = dev_get_drvdata(dev);
struct fsl_asrc_priv *asrc_priv = asrc->private;
+ int reg, retry = INIT_RETRY_NUM;
int i, ret;
u32 asrctr;
@@ -1223,6 +1302,24 @@ static int fsl_asrc_runtime_resume(struct device *dev)
regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_ALL_MASK, asrctr);
+ /* Wait for status of initialization for all enabled pairs */
+ do {
+ udelay(5);
+ regmap_read(asrc->regmap, REG_ASRCFG, &reg);
+ reg = (reg >> ASRCFG_INIRQi_SHIFT(0)) & 0x7;
+ } while ((reg != ((asrctr >> ASRCTR_ASRCEi_SHIFT(0)) & 0x7)) && --retry);
+
+ /*
+ * NOTE: Doesn't treat initialization timeout as an error
+ * Some of the pairs may success, then still can continue.
+ */
+ if (!retry) {
+ for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
+ if ((asrctr & ASRCTR_ASRCEi_MASK(i)) && !(reg & (1 << i)))
+ dev_warn(dev, "Pair %c initialization isn't finished\n", 'A' + i);
+ }
+ }
+
return 0;
disable_asrck_clk:
@@ -1257,7 +1354,6 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops fsl_asrc_pm = {
SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
@@ -1296,6 +1392,7 @@ MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
static struct platform_driver fsl_asrc_driver = {
.probe = fsl_asrc_probe,
+ .remove_new = fsl_asrc_remove,
.driver = {
.name = "fsl-asrc",
.of_match_table = fsl_asrc_ids,