summaryrefslogtreecommitdiff
path: root/sound/soc/fsl/fsl_easrc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/fsl/fsl_easrc.c')
-rw-r--r--sound/soc/fsl/fsl_easrc.c393
1 files changed, 356 insertions, 37 deletions
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
index d22f0621c465..114a6c0b6b73 100644
--- a/sound/soc/fsl/fsl_easrc.c
+++ b/sound/soc/fsl/fsl_easrc.c
@@ -52,10 +52,16 @@ static int fsl_easrc_iec958_put_bits(struct snd_kcontrol *kcontrol,
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
unsigned int regval = ucontrol->value.integer.value[0];
+ int ret;
+
+ if (regval < EASRC_WIDTH_16_BIT || regval > EASRC_WIDTH_24_BIT)
+ return -EINVAL;
+
+ ret = (easrc_priv->bps_iec958[mc->regbase] != regval);
easrc_priv->bps_iec958[mc->regbase] = regval;
- return 0;
+ return ret;
}
static int fsl_easrc_iec958_get_bits(struct snd_kcontrol *kcontrol,
@@ -67,22 +73,52 @@ static int fsl_easrc_iec958_get_bits(struct snd_kcontrol *kcontrol,
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
- ucontrol->value.enumerated.item[0] = easrc_priv->bps_iec958[mc->regbase];
+ ucontrol->value.integer.value[0] = easrc_priv->bps_iec958[mc->regbase];
return 0;
}
+static int fsl_easrc_iec958_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
static int fsl_easrc_get_reg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
- unsigned int regval;
+ struct fsl_asrc *easrc = snd_soc_component_get_drvdata(component);
+ unsigned int *regval = (unsigned int *)ucontrol->value.iec958.status;
+ int ret;
+
+ ret = regmap_read(easrc->regmap, REG_EASRC_CS0(mc->regbase), &regval[0]);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(easrc->regmap, REG_EASRC_CS1(mc->regbase), &regval[1]);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(easrc->regmap, REG_EASRC_CS2(mc->regbase), &regval[2]);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(easrc->regmap, REG_EASRC_CS3(mc->regbase), &regval[3]);
+ if (ret)
+ return ret;
- regval = snd_soc_component_read(component, mc->regbase);
+ ret = regmap_read(easrc->regmap, REG_EASRC_CS4(mc->regbase), &regval[4]);
+ if (ret)
+ return ret;
- ucontrol->value.integer.value[0] = regval;
+ ret = regmap_read(easrc->regmap, REG_EASRC_CS5(mc->regbase), &regval[5]);
+ if (ret)
+ return ret;
return 0;
}
@@ -93,20 +129,63 @@ static int fsl_easrc_set_reg(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
- unsigned int regval = ucontrol->value.integer.value[0];
+ struct fsl_asrc *easrc = snd_soc_component_get_drvdata(component);
+ unsigned int *regval = (unsigned int *)ucontrol->value.iec958.status;
+ bool changed, changed_all = false;
int ret;
- ret = snd_soc_component_write(component, mc->regbase, regval);
- if (ret < 0)
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret)
return ret;
- return 0;
+ ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS0(mc->regbase),
+ GENMASK(31, 0), regval[0], &changed);
+ if (ret != 0)
+ goto err;
+ changed_all |= changed;
+
+ ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS1(mc->regbase),
+ GENMASK(31, 0), regval[1], &changed);
+ if (ret != 0)
+ goto err;
+ changed_all |= changed;
+
+ ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS2(mc->regbase),
+ GENMASK(31, 0), regval[2], &changed);
+ if (ret != 0)
+ goto err;
+ changed_all |= changed;
+
+ ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS3(mc->regbase),
+ GENMASK(31, 0), regval[3], &changed);
+ if (ret != 0)
+ goto err;
+ changed_all |= changed;
+
+ ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS4(mc->regbase),
+ GENMASK(31, 0), regval[4], &changed);
+ if (ret != 0)
+ goto err;
+ changed_all |= changed;
+
+ ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS5(mc->regbase),
+ GENMASK(31, 0), regval[5], &changed);
+ if (ret != 0)
+ goto err;
+ changed_all |= changed;
+err:
+ pm_runtime_put_autosuspend(component->dev);
+
+ if (ret != 0)
+ return ret;
+ else
+ return changed_all;
}
#define SOC_SINGLE_REG_RW(xname, xreg) \
{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = snd_soc_info_xr_sx, .get = fsl_easrc_get_reg, \
+ .info = fsl_easrc_iec958_info, .get = fsl_easrc_get_reg, \
.put = fsl_easrc_set_reg, \
.private_value = (unsigned long)&(struct soc_mreg_control) \
{ .regbase = xreg, .regcount = 1, .nbits = 32, \
@@ -137,30 +216,10 @@ static const struct snd_kcontrol_new fsl_easrc_snd_controls[] = {
SOC_SINGLE_VAL_RW("Context 2 IEC958 Bits Per Sample", 2),
SOC_SINGLE_VAL_RW("Context 3 IEC958 Bits Per Sample", 3),
- SOC_SINGLE_REG_RW("Context 0 IEC958 CS0", REG_EASRC_CS0(0)),
- SOC_SINGLE_REG_RW("Context 1 IEC958 CS0", REG_EASRC_CS0(1)),
- SOC_SINGLE_REG_RW("Context 2 IEC958 CS0", REG_EASRC_CS0(2)),
- SOC_SINGLE_REG_RW("Context 3 IEC958 CS0", REG_EASRC_CS0(3)),
- SOC_SINGLE_REG_RW("Context 0 IEC958 CS1", REG_EASRC_CS1(0)),
- SOC_SINGLE_REG_RW("Context 1 IEC958 CS1", REG_EASRC_CS1(1)),
- SOC_SINGLE_REG_RW("Context 2 IEC958 CS1", REG_EASRC_CS1(2)),
- SOC_SINGLE_REG_RW("Context 3 IEC958 CS1", REG_EASRC_CS1(3)),
- SOC_SINGLE_REG_RW("Context 0 IEC958 CS2", REG_EASRC_CS2(0)),
- SOC_SINGLE_REG_RW("Context 1 IEC958 CS2", REG_EASRC_CS2(1)),
- SOC_SINGLE_REG_RW("Context 2 IEC958 CS2", REG_EASRC_CS2(2)),
- SOC_SINGLE_REG_RW("Context 3 IEC958 CS2", REG_EASRC_CS2(3)),
- SOC_SINGLE_REG_RW("Context 0 IEC958 CS3", REG_EASRC_CS3(0)),
- SOC_SINGLE_REG_RW("Context 1 IEC958 CS3", REG_EASRC_CS3(1)),
- SOC_SINGLE_REG_RW("Context 2 IEC958 CS3", REG_EASRC_CS3(2)),
- SOC_SINGLE_REG_RW("Context 3 IEC958 CS3", REG_EASRC_CS3(3)),
- SOC_SINGLE_REG_RW("Context 0 IEC958 CS4", REG_EASRC_CS4(0)),
- SOC_SINGLE_REG_RW("Context 1 IEC958 CS4", REG_EASRC_CS4(1)),
- SOC_SINGLE_REG_RW("Context 2 IEC958 CS4", REG_EASRC_CS4(2)),
- SOC_SINGLE_REG_RW("Context 3 IEC958 CS4", REG_EASRC_CS4(3)),
- SOC_SINGLE_REG_RW("Context 0 IEC958 CS5", REG_EASRC_CS5(0)),
- SOC_SINGLE_REG_RW("Context 1 IEC958 CS5", REG_EASRC_CS5(1)),
- SOC_SINGLE_REG_RW("Context 2 IEC958 CS5", REG_EASRC_CS5(2)),
- SOC_SINGLE_REG_RW("Context 3 IEC958 CS5", REG_EASRC_CS5(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS", 0),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS", 1),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS", 2),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS", 3),
};
/*
@@ -1280,7 +1339,7 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx)
/*
* Release the context
*
- * This funciton is mainly doing the revert thing in request context
+ * This function is mainly doing the revert thing in request context
*/
static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx)
{
@@ -1577,6 +1636,9 @@ static const struct snd_soc_component_driver fsl_easrc_component = {
.controls = fsl_easrc_snd_controls,
.num_controls = ARRAY_SIZE(fsl_easrc_snd_controls),
.legacy_dai_naming = 1,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_prefix = "easrc",
+#endif
};
static const struct reg_default fsl_easrc_reg_defaults[] = {
@@ -1861,6 +1923,224 @@ static int fsl_easrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
return REG_EASRC_FIFO(dir, index);
}
+/* Get sample numbers in FIFO */
+static unsigned int fsl_easrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ enum asrc_pair_index index = pair->index;
+ u32 val;
+
+ regmap_read(asrc->regmap, REG_EASRC_SFS(index), &val);
+ val &= EASRC_SFS_NSGO_MASK;
+
+ return val >> EASRC_SFS_NSGO_SHIFT;
+}
+
+static int fsl_easrc_m2m_prepare(struct fsl_asrc_pair *pair)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
+ int ret;
+
+ ctx_priv->in_params.sample_rate = pair->rate[IN];
+ ctx_priv->in_params.sample_format = pair->sample_format[IN];
+ ctx_priv->out_params.sample_rate = pair->rate[OUT];
+ ctx_priv->out_params.sample_format = pair->sample_format[OUT];
+
+ ctx_priv->in_params.fifo_wtmk = FSL_EASRC_INPUTFIFO_WML;
+ ctx_priv->out_params.fifo_wtmk = FSL_EASRC_OUTPUTFIFO_WML;
+ /* Fill the right half of the re-sampler with zeros */
+ ctx_priv->rs_init_mode = 0x2;
+ /* Zero fill the right half of the prefilter */
+ ctx_priv->pf_init_mode = 0x2;
+
+ ret = fsl_easrc_set_ctx_format(pair,
+ &ctx_priv->in_params.sample_format,
+ &ctx_priv->out_params.sample_format);
+ if (ret) {
+ dev_err(dev, "failed to set context format: %d\n", ret);
+ return ret;
+ }
+
+ ret = fsl_easrc_config_context(asrc, pair->index);
+ if (ret) {
+ dev_err(dev, "failed to config context %d\n", ret);
+ return ret;
+ }
+
+ ctx_priv->in_params.iterations = 1;
+ ctx_priv->in_params.group_len = pair->channels;
+ ctx_priv->in_params.access_len = pair->channels;
+ ctx_priv->out_params.iterations = 1;
+ ctx_priv->out_params.group_len = pair->channels;
+ ctx_priv->out_params.access_len = pair->channels;
+
+ ret = fsl_easrc_set_ctx_organziation(pair);
+ if (ret) {
+ dev_err(dev, "failed to set fifo organization\n");
+ return ret;
+ }
+
+ /* The context start flag */
+ pair->first_convert = 1;
+ return 0;
+}
+
+static int fsl_easrc_m2m_start(struct fsl_asrc_pair *pair)
+{
+ /* start context once */
+ if (pair->first_convert) {
+ fsl_easrc_start_context(pair);
+ pair->first_convert = 0;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_m2m_stop(struct fsl_asrc_pair *pair)
+{
+ /* Stop pair/context */
+ if (!pair->first_convert) {
+ fsl_easrc_stop_context(pair);
+ pair->first_convert = 1;
+ }
+
+ return 0;
+}
+
+/* calculate capture data length according to output data length and sample rate */
+static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length)
+{
+ struct fsl_asrc *easrc = pair->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+ unsigned int in_rate = ctx_priv->in_params.norm_rate;
+ unsigned int out_rate = ctx_priv->out_params.norm_rate;
+ unsigned int channels = pair->channels;
+ unsigned int in_samples, out_samples;
+ unsigned int in_width, out_width;
+ unsigned int out_length;
+ unsigned int frac_bits;
+ u64 val1, val2;
+
+ switch (easrc_priv->rs_num_taps) {
+ case EASRC_RS_32_TAPS:
+ /* integer bits = 5; */
+ frac_bits = 39;
+ break;
+ case EASRC_RS_64_TAPS:
+ /* integer bits = 6; */
+ frac_bits = 38;
+ break;
+ case EASRC_RS_128_TAPS:
+ /* integer bits = 7; */
+ frac_bits = 37;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val1 = (u64)in_rate << frac_bits;
+ do_div(val1, out_rate);
+ val1 += (s64)ctx_priv->ratio_mod << (frac_bits - 31);
+
+ in_width = snd_pcm_format_physical_width(ctx_priv->in_params.sample_format) / 8;
+ out_width = snd_pcm_format_physical_width(ctx_priv->out_params.sample_format) / 8;
+
+ ctx_priv->in_filled_len += input_buffer_length;
+ if (ctx_priv->in_filled_len <= ctx_priv->in_filled_sample * in_width * channels) {
+ out_length = 0;
+ } else {
+ in_samples = ctx_priv->in_filled_len / (in_width * channels) -
+ ctx_priv->in_filled_sample;
+
+ /* right shift 12 bit to make ratio in 32bit space */
+ val2 = (u64)in_samples << (frac_bits - 12);
+ val1 = val1 >> 12;
+ do_div(val2, val1);
+ out_samples = val2;
+
+ out_length = out_samples * out_width * channels;
+ ctx_priv->in_filled_len = ctx_priv->in_filled_sample * in_width * channels;
+ }
+
+ return out_length;
+}
+
+static int fsl_easrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+
+ if (dir == IN)
+ return ctx_priv->in_params.fifo_wtmk * pair->channels;
+ else
+ return ctx_priv->out_params.fifo_wtmk * pair->channels;
+}
+
+static int fsl_easrc_m2m_pair_suspend(struct fsl_asrc_pair *pair)
+{
+ fsl_easrc_stop_context(pair);
+
+ return 0;
+}
+
+static int fsl_easrc_m2m_pair_resume(struct fsl_asrc_pair *pair)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+
+ pair->first_convert = 1;
+ ctx_priv->in_filled_len = 0;
+
+ return 0;
+}
+
+/* val is Q31 */
+static int fsl_easrc_m2m_set_ratio_mod(struct fsl_asrc_pair *pair, int val)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+ struct fsl_asrc *easrc = pair->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ unsigned int frac_bits;
+
+ ctx_priv->ratio_mod += val;
+
+ switch (easrc_priv->rs_num_taps) {
+ case EASRC_RS_32_TAPS:
+ /* integer bits = 5; */
+ frac_bits = 39;
+ break;
+ case EASRC_RS_64_TAPS:
+ /* integer bits = 6; */
+ frac_bits = 38;
+ break;
+ case EASRC_RS_128_TAPS:
+ /* integer bits = 7; */
+ frac_bits = 37;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= (frac_bits - 31);
+ regmap_write(easrc->regmap, REG_EASRC_RUC(pair->index), EASRC_RSUC_RS_RM(val));
+
+ return 0;
+}
+
+static int fsl_easrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap)
+{
+ cap->fmt_in = FSL_EASRC_FORMATS;
+ cap->fmt_out = FSL_EASRC_FORMATS | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+ cap->rate_in = easrc_rates;
+ cap->rate_in_count = ARRAY_SIZE(easrc_rates);
+ cap->rate_out = easrc_rates;
+ cap->rate_out_count = ARRAY_SIZE(easrc_rates);
+ cap->chan_min = 1;
+ cap->chan_max = 32;
+ return 0;
+}
+
static const struct of_device_id fsl_easrc_dt_ids[] = {
{ .compatible = "fsl,imx8mn-easrc",},
{}
@@ -1926,6 +2206,16 @@ static int fsl_easrc_probe(struct platform_device *pdev)
easrc->release_pair = fsl_easrc_release_context;
easrc->get_fifo_addr = fsl_easrc_get_fifo_addr;
easrc->pair_priv_size = sizeof(struct fsl_easrc_ctx_priv);
+ easrc->m2m_prepare = fsl_easrc_m2m_prepare;
+ easrc->m2m_start = fsl_easrc_m2m_start;
+ easrc->m2m_stop = fsl_easrc_m2m_stop;
+ easrc->get_output_fifo_size = fsl_easrc_get_output_fifo_size;
+ easrc->m2m_calc_out_len = fsl_easrc_m2m_calc_out_len;
+ easrc->m2m_get_maxburst = fsl_easrc_m2m_get_maxburst;
+ easrc->m2m_pair_suspend = fsl_easrc_m2m_pair_suspend;
+ easrc->m2m_pair_resume = fsl_easrc_m2m_pair_resume;
+ easrc->m2m_set_ratio_mod = fsl_easrc_m2m_set_ratio_mod;
+ easrc->m2m_get_cap = fsl_easrc_m2m_get_cap;
easrc_priv->rs_num_taps = EASRC_RS_32_TAPS;
easrc_priv->const_coeff = 0x3FF0000000000000;
@@ -1976,6 +2266,12 @@ static int fsl_easrc_probe(struct platform_device *pdev)
goto err_pm_disable;
}
+ ret = fsl_asrc_m2m_init(easrc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
+ return ret;
+ }
+
return 0;
err_pm_disable:
@@ -1985,6 +2281,10 @@ err_pm_disable:
static void fsl_easrc_remove(struct platform_device *pdev)
{
+ struct fsl_asrc *easrc = dev_get_drvdata(&pdev->dev);
+
+ fsl_asrc_m2m_exit(easrc);
+
pm_runtime_disable(&pdev->dev);
}
@@ -2085,10 +2385,29 @@ disable_mem_clk:
return ret;
}
+static int fsl_easrc_suspend(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ int ret;
+
+ fsl_asrc_m2m_suspend(easrc);
+ ret = pm_runtime_force_suspend(dev);
+ return ret;
+}
+
+static int fsl_easrc_resume(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ fsl_asrc_m2m_resume(easrc);
+ return ret;
+}
+
static const struct dev_pm_ops fsl_easrc_pm_ops = {
RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, fsl_easrc_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SYSTEM_SLEEP_PM_OPS(fsl_easrc_suspend, fsl_easrc_resume)
};
static struct platform_driver fsl_easrc_driver = {