Merge remote-tracking branches 'asoc/topic/devm', 'asoc/topic/fsl', 'asoc/topic/fsl-esai', 'asoc/topic/fsl-sai', 'asoc/topic/fsl-spdif' and 'asoc/topic/fsl-ssi' into asoc-next
This commit is contained in:
@@ -13,18 +13,18 @@
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-private.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "fsl_spdif.h"
|
||||
#include "imx-pcm.h"
|
||||
@@ -69,17 +69,42 @@ struct spdif_mixer_control {
|
||||
u32 ready_buf;
|
||||
};
|
||||
|
||||
/**
|
||||
* fsl_spdif_priv: Freescale SPDIF private data
|
||||
*
|
||||
* @fsl_spdif_control: SPDIF control data
|
||||
* @cpu_dai_drv: cpu dai driver
|
||||
* @pdev: platform device pointer
|
||||
* @regmap: regmap handler
|
||||
* @dpll_locked: dpll lock flag
|
||||
* @txrate: the best rates for playback
|
||||
* @txclk_df: STC_TXCLK_DF dividers value for playback
|
||||
* @sysclk_df: STC_SYSCLK_DF dividers value for playback
|
||||
* @txclk_src: STC_TXCLK_SRC values for playback
|
||||
* @rxclk_src: SRPC_CLKSRC_SEL values for capture
|
||||
* @txclk: tx clock sources for playback
|
||||
* @rxclk: rx clock sources for capture
|
||||
* @coreclk: core clock for register access via DMA
|
||||
* @sysclk: system clock for rx clock rate measurement
|
||||
* @dma_params_tx: DMA parameters for transmit channel
|
||||
* @dma_params_rx: DMA parameters for receive channel
|
||||
* @name: driver name
|
||||
*/
|
||||
struct fsl_spdif_priv {
|
||||
struct spdif_mixer_control fsl_spdif_control;
|
||||
struct snd_soc_dai_driver cpu_dai_drv;
|
||||
struct platform_device *pdev;
|
||||
struct regmap *regmap;
|
||||
bool dpll_locked;
|
||||
u8 txclk_div[SPDIF_TXRATE_MAX];
|
||||
u16 txrate[SPDIF_TXRATE_MAX];
|
||||
u8 txclk_df[SPDIF_TXRATE_MAX];
|
||||
u8 sysclk_df[SPDIF_TXRATE_MAX];
|
||||
u8 txclk_src[SPDIF_TXRATE_MAX];
|
||||
u8 rxclk_src;
|
||||
struct clk *txclk[SPDIF_TXRATE_MAX];
|
||||
struct clk *rxclk;
|
||||
struct clk *coreclk;
|
||||
struct clk *sysclk;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
|
||||
@@ -349,7 +374,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
||||
struct platform_device *pdev = spdif_priv->pdev;
|
||||
unsigned long csfs = 0;
|
||||
u32 stc, mask, rate;
|
||||
u8 clk, div;
|
||||
u8 clk, txclk_df, sysclk_df;
|
||||
int ret;
|
||||
|
||||
switch (sample_rate) {
|
||||
@@ -376,25 +401,31 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
div = spdif_priv->txclk_div[rate];
|
||||
if (div == 0) {
|
||||
dev_err(&pdev->dev, "the divisor can't be zero\n");
|
||||
txclk_df = spdif_priv->txclk_df[rate];
|
||||
if (txclk_df == 0) {
|
||||
dev_err(&pdev->dev, "the txclk_df can't be zero\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sysclk_df = spdif_priv->sysclk_df[rate];
|
||||
|
||||
/* Don't mess up the clocks from other modules */
|
||||
if (clk != STC_TXCLK_SPDIF_ROOT)
|
||||
goto clk_set_bypass;
|
||||
|
||||
/*
|
||||
* The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block
|
||||
* will divide by (div). So request 64 * fs * (div+1) which will
|
||||
* get rounded.
|
||||
* The S/PDIF block needs a clock of 64 * fs * txclk_df.
|
||||
* So request 64 * fs * (txclk_df + 1) to get rounded.
|
||||
*/
|
||||
ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
|
||||
ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to set tx clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_set_bypass:
|
||||
dev_dbg(&pdev->dev, "expected clock rate = %d\n",
|
||||
(64 * sample_rate * div));
|
||||
(64 * sample_rate * txclk_df * sysclk_df));
|
||||
dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
|
||||
clk_get_rate(spdif_priv->txclk[rate]));
|
||||
|
||||
@@ -402,11 +433,15 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
||||
spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
|
||||
|
||||
/* select clock source and divisor */
|
||||
stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
|
||||
mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
|
||||
stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DF(txclk_df);
|
||||
mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK;
|
||||
regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
|
||||
|
||||
dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
|
||||
regmap_update_bits(regmap, REG_SPDIF_STC,
|
||||
STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df));
|
||||
|
||||
dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n",
|
||||
spdif_priv->txrate[rate], sample_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -423,10 +458,16 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
||||
|
||||
/* Reset module and interrupts only for first initialization */
|
||||
if (!cpu_dai->active) {
|
||||
ret = clk_prepare_enable(spdif_priv->coreclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable core clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spdif_softreset(spdif_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to soft reset\n");
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Disable all the interrupts */
|
||||
@@ -454,6 +495,11 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
|
||||
@@ -484,6 +530,7 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
|
||||
spdif_intr_status_clear(spdif_priv);
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR,
|
||||
SCR_LOW_POWER, SCR_LOW_POWER);
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,7 +801,7 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
|
||||
clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
|
||||
if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
|
||||
/* Get bus clock from system */
|
||||
busclk_freq = clk_get_rate(spdif_priv->rxclk);
|
||||
busclk_freq = clk_get_rate(spdif_priv->sysclk);
|
||||
}
|
||||
|
||||
/* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
|
||||
@@ -997,43 +1044,61 @@ static struct regmap_config fsl_spdif_regmap_config = {
|
||||
|
||||
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
|
||||
struct clk *clk, u64 savesub,
|
||||
enum spdif_txrate index)
|
||||
enum spdif_txrate index, bool round)
|
||||
{
|
||||
const u32 rate[] = { 32000, 44100, 48000 };
|
||||
bool is_sysclk = clk == spdif_priv->sysclk;
|
||||
u64 rate_ideal, rate_actual, sub;
|
||||
u32 div, arate;
|
||||
u32 sysclk_dfmin, sysclk_dfmax;
|
||||
u32 txclk_df, sysclk_df, arate;
|
||||
|
||||
for (div = 1; div <= 128; div++) {
|
||||
rate_ideal = rate[index] * (div + 1) * 64;
|
||||
rate_actual = clk_round_rate(clk, rate_ideal);
|
||||
/* The sysclk has an extra divisor [2, 512] */
|
||||
sysclk_dfmin = is_sysclk ? 2 : 1;
|
||||
sysclk_dfmax = is_sysclk ? 512 : 1;
|
||||
|
||||
arate = rate_actual / 64;
|
||||
arate /= div;
|
||||
for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
|
||||
for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
|
||||
rate_ideal = rate[index] * (txclk_df + 1) * 64;
|
||||
if (round)
|
||||
rate_actual = clk_round_rate(clk, rate_ideal);
|
||||
else
|
||||
rate_actual = clk_get_rate(clk);
|
||||
|
||||
if (arate == rate[index]) {
|
||||
/* We are lucky */
|
||||
savesub = 0;
|
||||
spdif_priv->txclk_div[index] = div;
|
||||
break;
|
||||
} else if (arate / rate[index] == 1) {
|
||||
/* A little bigger than expect */
|
||||
sub = (arate - rate[index]) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub < savesub) {
|
||||
arate = rate_actual / 64;
|
||||
arate /= txclk_df * sysclk_df;
|
||||
|
||||
if (arate == rate[index]) {
|
||||
/* We are lucky */
|
||||
savesub = 0;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
goto out;
|
||||
} else if (arate / rate[index] == 1) {
|
||||
/* A little bigger than expect */
|
||||
sub = (arate - rate[index]) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_div[index] = div;
|
||||
}
|
||||
} else if (rate[index] / arate == 1) {
|
||||
/* A little smaller than expect */
|
||||
sub = (rate[index] - arate) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub < savesub) {
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
} else if (rate[index] / arate == 1) {
|
||||
/* A little smaller than expect */
|
||||
sub = (rate[index] - arate) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_div[index] = div;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return savesub;
|
||||
}
|
||||
|
||||
@@ -1058,7 +1123,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
|
||||
if (!clk_get_rate(clk))
|
||||
continue;
|
||||
|
||||
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
|
||||
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
|
||||
i == STC_TXCLK_SPDIF_ROOT);
|
||||
if (savesub == ret)
|
||||
continue;
|
||||
|
||||
@@ -1073,8 +1139,13 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
|
||||
|
||||
dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
|
||||
spdif_priv->txclk_src[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n",
|
||||
spdif_priv->txclk_div[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->txclk_df[index], rate[index]);
|
||||
if (spdif_priv->txclk[index] == spdif_priv->sysclk)
|
||||
dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->sysclk_df[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
|
||||
rate[index], spdif_priv->txrate[index]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1134,6 +1205,20 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get system clock for rx clock rate calculation */
|
||||
spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5");
|
||||
if (IS_ERR(spdif_priv->sysclk)) {
|
||||
dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n");
|
||||
return PTR_ERR(spdif_priv->sysclk);
|
||||
}
|
||||
|
||||
/* Get core clock for data register access via DMA */
|
||||
spdif_priv->coreclk = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(spdif_priv->coreclk)) {
|
||||
dev_err(&pdev->dev, "no core clock in devicetree\n");
|
||||
return PTR_ERR(spdif_priv->coreclk);
|
||||
}
|
||||
|
||||
/* Select clock source for rx/tx clock */
|
||||
spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
|
||||
if (IS_ERR(spdif_priv->rxclk)) {
|
||||
@@ -1186,6 +1271,7 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id fsl_spdif_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx35-spdif", },
|
||||
{ .compatible = "fsl,vf610-spdif", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
|
||||
|
Reference in New Issue
Block a user