mirror of
https://github.com/brain-hackers/linux-brain.git
synced 2024-06-09 23:36:23 +09:00
Merge remote-tracking branch 'origin/audio/spdif' into audio/next
* origin/audio/spdif: (20 commits) LF-106: ASoC: fsl_spdif: request BUS_FREQ_HIGH ASoC: fsl_spdif:Support multi power domains ASoC: fsl_spdif: Add pm_runtime_enable in probe MLK-21484-2: ASoC: fsl_spdif: ensure clk is unprepared before reparent MLK-19154-5: ASoC: fsl_spdif: refine PLL switch handling ...
This commit is contained in:
commit
635de10e16
|
@ -6,7 +6,9 @@ a fibre cable.
|
|||
|
||||
Required properties:
|
||||
|
||||
- compatible : Compatible list, must contain "fsl,imx35-spdif".
|
||||
- compatible : Compatible list, must contain "fsl,imx35-spdif",
|
||||
"fsl,vf610-spdif", "fsl,imx8qm-spdif",
|
||||
"fsl,imx8mm-spdif"
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
|
||||
//
|
||||
// Copyright (C) 2013 Freescale Semiconductor, Inc.
|
||||
// Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
|
||||
//
|
||||
// Based on stmp3xxx_spdif_dai.c
|
||||
// Vladimir Barinov <vbarinov@embeddedalley.com>
|
||||
|
@ -11,11 +11,15 @@
|
|||
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.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 <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/busfreq-imx.h>
|
||||
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
@ -42,6 +46,16 @@ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
|
|||
|
||||
#define DEFAULT_RXCLK_SRC 1
|
||||
|
||||
struct fsl_spdif_soc_data {
|
||||
bool imx;
|
||||
bool constrain_period_size;
|
||||
u32 tx_burst;
|
||||
u32 rx_burst;
|
||||
u32 interrupts;
|
||||
u64 tx_formats;
|
||||
u64 rx_rates;
|
||||
};
|
||||
|
||||
/*
|
||||
* SPDIF control structure
|
||||
* Defines channel status, subcode and Q sub
|
||||
|
@ -99,15 +113,58 @@ struct fsl_spdif_priv {
|
|||
u16 sysclk_df[SPDIF_TXRATE_MAX];
|
||||
u8 txclk_src[SPDIF_TXRATE_MAX];
|
||||
u8 rxclk_src;
|
||||
struct clk *txclk[SPDIF_TXRATE_MAX];
|
||||
struct clk *txclk[STC_TXCLK_SRC_MAX];
|
||||
struct clk *rxclk;
|
||||
struct clk *coreclk;
|
||||
struct clk *sysclk;
|
||||
struct clk *spbaclk;
|
||||
const struct fsl_spdif_soc_data *soc;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
/* regcache for SRPC */
|
||||
u32 regcache_srpc;
|
||||
struct clk *pll8k_clk;
|
||||
struct clk *pll11k_clk;
|
||||
};
|
||||
|
||||
static struct fsl_spdif_soc_data fsl_spdif_vf610 = {
|
||||
.imx = false,
|
||||
.tx_burst = FSL_SPDIF_TXFIFO_WML,
|
||||
.rx_burst = FSL_SPDIF_RXFIFO_WML,
|
||||
.interrupts = 1,
|
||||
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
|
||||
.rx_rates = FSL_SPDIF_RATES_CAPTURE,
|
||||
.constrain_period_size = false,
|
||||
};
|
||||
|
||||
static struct fsl_spdif_soc_data fsl_spdif_imx35 = {
|
||||
.imx = true,
|
||||
.tx_burst = FSL_SPDIF_TXFIFO_WML,
|
||||
.rx_burst = FSL_SPDIF_RXFIFO_WML,
|
||||
.interrupts = 1,
|
||||
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
|
||||
.rx_rates = FSL_SPDIF_RATES_CAPTURE,
|
||||
.constrain_period_size = false,
|
||||
};
|
||||
|
||||
static struct fsl_spdif_soc_data fsl_spdif_imx8qm = {
|
||||
.imx = true,
|
||||
.tx_burst = 2,
|
||||
.rx_burst = 2,
|
||||
.interrupts = 2,
|
||||
.tx_formats = SNDRV_PCM_FMTBIT_S24_LE,
|
||||
.rx_rates = (FSL_SPDIF_RATES_CAPTURE | SNDRV_PCM_RATE_192000),
|
||||
.constrain_period_size = true,
|
||||
};
|
||||
|
||||
static struct fsl_spdif_soc_data fsl_spdif_imx8mm = {
|
||||
.imx = true,
|
||||
.tx_burst = FSL_SPDIF_TXFIFO_WML,
|
||||
.rx_burst = FSL_SPDIF_RXFIFO_WML,
|
||||
.interrupts = 1,
|
||||
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
|
||||
.rx_rates = (FSL_SPDIF_RATES_CAPTURE | SNDRV_PCM_RATE_192000),
|
||||
.constrain_period_size = false,
|
||||
};
|
||||
|
||||
/* DPLL locked and lock loss interrupt handler */
|
||||
|
@ -378,7 +435,6 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
|||
u32 stc, mask, rate;
|
||||
u16 sysclk_df;
|
||||
u8 clk, txclk_df;
|
||||
int ret;
|
||||
|
||||
switch (sample_rate) {
|
||||
case 32000:
|
||||
|
@ -420,23 +476,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
|||
|
||||
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 * txclk_df */
|
||||
ret = clk_set_rate(spdif_priv->txclk[rate],
|
||||
64 * sample_rate * txclk_df);
|
||||
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 * txclk_df * sysclk_df));
|
||||
dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
|
||||
clk_get_rate(spdif_priv->txclk[rate]));
|
||||
clk_get_rate(spdif_priv->txclk[clk]));
|
||||
|
||||
/* set fs field in consumer channel status */
|
||||
spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
|
||||
|
@ -462,25 +505,10 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
|||
struct platform_device *pdev = spdif_priv->pdev;
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 scr, mask;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (!IS_ERR(spdif_priv->spbaclk)) {
|
||||
ret = clk_prepare_enable(spdif_priv->spbaclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable spba clock\n");
|
||||
goto err_spbaclk;
|
||||
}
|
||||
}
|
||||
|
||||
ret = spdif_softreset(spdif_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to soft reset\n");
|
||||
|
@ -498,35 +526,30 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
|||
mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
|
||||
SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
|
||||
SCR_TXFIFO_FSEL_MASK;
|
||||
for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
|
||||
ret = clk_prepare_enable(spdif_priv->txclk[i]);
|
||||
if (ret)
|
||||
goto disable_txclk;
|
||||
}
|
||||
} else {
|
||||
scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
|
||||
mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
|
||||
SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
|
||||
ret = clk_prepare_enable(spdif_priv->rxclk);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
|
||||
|
||||
/* Power up SPDIF module */
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
|
||||
|
||||
if (spdif_priv->soc->constrain_period_size) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
spdif_priv->dma_params_tx.maxburst);
|
||||
else
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
spdif_priv->dma_params_rx.maxburst);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
disable_txclk:
|
||||
for (i--; i >= 0; i--)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
err:
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
err_spbaclk:
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -536,20 +559,17 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 scr, mask, i;
|
||||
u32 scr, mask;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
scr = 0;
|
||||
mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
|
||||
SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
|
||||
SCR_TXFIFO_FSEL_MASK;
|
||||
for (i = 0; i < SPDIF_TXRATE_MAX; i++)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
} else {
|
||||
scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
|
||||
mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
|
||||
SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
|
||||
clk_disable_unprepare(spdif_priv->rxclk);
|
||||
}
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
|
||||
|
||||
|
@ -558,9 +578,6 @@ 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);
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,14 +640,178 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
|
||||
struct clk *clk, u64 savesub,
|
||||
enum spdif_txrate index, bool round)
|
||||
{
|
||||
static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
|
||||
bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk);
|
||||
u64 rate_actual, sub;
|
||||
u32 arate;
|
||||
u16 sysclk_dfmin, sysclk_dfmax, sysclk_df;
|
||||
u8 txclk_df;
|
||||
|
||||
/* The sysclk has an extra divisor [2, 512] */
|
||||
sysclk_dfmin = is_sysclk ? 2 : 1;
|
||||
sysclk_dfmax = is_sysclk ? 512 : 1;
|
||||
|
||||
for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
|
||||
for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
|
||||
|
||||
rate_actual = clk_get_rate(clk);
|
||||
|
||||
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 = (u64)(arate - rate[index]) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
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 = (u64)(rate[index] - arate) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return savesub;
|
||||
}
|
||||
|
||||
static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
|
||||
enum spdif_txrate index)
|
||||
{
|
||||
static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
|
||||
struct platform_device *pdev = spdif_priv->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
u64 savesub = 100000, ret;
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
|
||||
clk = spdif_priv->txclk[i];
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "no rxtx%d clock in devicetree\n", i);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
if (!clk_get_rate(clk))
|
||||
continue;
|
||||
|
||||
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
|
||||
i == STC_TXCLK_SPDIF_ROOT);
|
||||
if (savesub == ret)
|
||||
continue;
|
||||
|
||||
savesub = ret;
|
||||
spdif_priv->txclk_src[index] = i;
|
||||
|
||||
/* To quick catch a divisor, we allow a 0.1% deviation */
|
||||
if (savesub < 100)
|
||||
break;
|
||||
}
|
||||
|
||||
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 txclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->txclk_df[index], rate[index]);
|
||||
if (clk_is_match(spdif_priv->txclk[spdif_priv->txclk_src[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;
|
||||
}
|
||||
|
||||
static int fsl_spdif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct fsl_spdif_priv *data = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct platform_device *pdev = data->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk, *p, *pll = 0, *npll = 0;
|
||||
u64 ratio = freq;
|
||||
int ret, i;
|
||||
bool reparent = false;
|
||||
|
||||
if (dir != SND_SOC_CLOCK_OUT || freq == 0 || clk_id != STC_TXCLK_SPDIF_ROOT)
|
||||
return 0;
|
||||
|
||||
if (data->pll8k_clk == NULL || data->pll11k_clk == NULL)
|
||||
return 0;
|
||||
|
||||
clk = data->txclk[clk_id];
|
||||
if (IS_ERR_OR_NULL(clk)) {
|
||||
dev_err(dev, "no rxtx%d clock in devicetree\n", clk_id);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
p = clk;
|
||||
while (p && data->pll8k_clk && data->pll11k_clk) {
|
||||
struct clk *pp = clk_get_parent(p);
|
||||
|
||||
if (clk_is_match(pp, data->pll8k_clk) ||
|
||||
clk_is_match(pp, data->pll11k_clk)) {
|
||||
pll = pp;
|
||||
break;
|
||||
}
|
||||
p = pp;
|
||||
}
|
||||
|
||||
npll = (do_div(ratio, 8000) ? data->pll11k_clk : data->pll8k_clk);
|
||||
reparent = (pll && !clk_is_match(pll, npll));
|
||||
|
||||
clk_disable_unprepare(clk);
|
||||
if (reparent) {
|
||||
ret = clk_set_parent(p, npll);
|
||||
if (ret < 0)
|
||||
dev_warn(cpu_dai->dev, "failed to set parent %s: %d\n",
|
||||
__clk_get_name(npll), ret);
|
||||
}
|
||||
|
||||
ret = clk_set_rate(clk, freq);
|
||||
if (ret < 0)
|
||||
dev_warn(cpu_dai->dev, "failed to set clock rate (%u): %d\n",
|
||||
freq, ret);
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
|
||||
ret = fsl_spdif_probe_txclk(data, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops fsl_spdif_dai_ops = {
|
||||
.startup = fsl_spdif_startup,
|
||||
.set_sysclk = fsl_spdif_set_dai_sysclk,
|
||||
.hw_params = fsl_spdif_hw_params,
|
||||
.trigger = fsl_spdif_trigger,
|
||||
.shutdown = fsl_spdif_shutdown,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* FSL SPDIF IEC958 controller(mixer) functions
|
||||
*
|
||||
|
@ -769,19 +950,8 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
|
|||
}
|
||||
|
||||
/* Valid bit information */
|
||||
static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get valid good bit from interrupt status register */
|
||||
static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
|
||||
static int fsl_spdif_rx_vbit_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
|
@ -796,6 +966,68 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spdif_tx_vbit_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 val;
|
||||
|
||||
regmap_read(regmap, REG_SPDIF_SCR, &val);
|
||||
val = (val & SCR_VAL_MASK) >> SCR_VAL_OFFSET;
|
||||
val = 1 - val;
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spdif_tx_vbit_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 val = (1 - ucontrol->value.integer.value[0]) << SCR_VAL_OFFSET;
|
||||
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_VAL_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spdif_rx_rcm_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 val;
|
||||
|
||||
regmap_read(regmap, REG_SPDIF_SCR, &val);
|
||||
val = (val & SCR_RAW_CAPTURE_MODE) ? 1 : 0;
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spdif_rx_rcm_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 val = (ucontrol->value.integer.value[0] ? SCR_RAW_CAPTURE_MODE : 0);
|
||||
|
||||
if (val)
|
||||
cpu_dai->driver->capture.formats |= SNDRV_PCM_FMTBIT_S32_LE;
|
||||
else
|
||||
cpu_dai->driver->capture.formats &= ~SNDRV_PCM_FMTBIT_S32_LE;
|
||||
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_RAW_CAPTURE_MODE, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* DPLL lock information */
|
||||
static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
|
@ -863,18 +1095,6 @@ static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* User bit sync mode info */
|
||||
static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* User bit sync mode:
|
||||
* 1 CD User channel subcode
|
||||
|
@ -953,11 +1173,21 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
|
|||
/* Valid bit error controller */
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 V-Bit Errors",
|
||||
.name = "IEC958 Rx V-Bit Errors",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = fsl_spdif_vbit_info,
|
||||
.get = fsl_spdif_vbit_get,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = fsl_spdif_rx_vbit_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Tx V-Bit",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_WRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = fsl_spdif_tx_vbit_get,
|
||||
.put = fsl_spdif_tx_vbit_put,
|
||||
},
|
||||
/* DPLL lock info get controller */
|
||||
{
|
||||
|
@ -975,10 +1205,20 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
|
|||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_WRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = fsl_spdif_usync_info,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = fsl_spdif_usync_get,
|
||||
.put = fsl_spdif_usync_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Rx Raw Capture Mode Bit",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_WRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = fsl_spdif_rx_rcm_get,
|
||||
.put = fsl_spdif_rx_rcm_put,
|
||||
},
|
||||
};
|
||||
|
||||
static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
|
||||
|
@ -1103,114 +1343,14 @@ static const struct regmap_config fsl_spdif_regmap_config = {
|
|||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
|
||||
struct clk *clk, u64 savesub,
|
||||
enum spdif_txrate index, bool round)
|
||||
{
|
||||
static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
|
||||
bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk);
|
||||
u64 rate_ideal, rate_actual, sub;
|
||||
u32 arate;
|
||||
u16 sysclk_dfmin, sysclk_dfmax, sysclk_df;
|
||||
u8 txclk_df;
|
||||
|
||||
/* The sysclk has an extra divisor [2, 512] */
|
||||
sysclk_dfmin = is_sysclk ? 2 : 1;
|
||||
sysclk_dfmax = is_sysclk ? 512 : 1;
|
||||
|
||||
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 * 64ULL;
|
||||
if (round)
|
||||
rate_actual = clk_round_rate(clk, rate_ideal);
|
||||
else
|
||||
rate_actual = clk_get_rate(clk);
|
||||
|
||||
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 = (u64)(arate - rate[index]) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
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 = (u64)(rate[index] - arate) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return savesub;
|
||||
}
|
||||
|
||||
static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
|
||||
enum spdif_txrate index)
|
||||
{
|
||||
static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
|
||||
struct platform_device *pdev = spdif_priv->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
u64 savesub = 100000, ret;
|
||||
struct clk *clk;
|
||||
char tmp[16];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
|
||||
sprintf(tmp, "rxtx%d", i);
|
||||
clk = devm_clk_get(&pdev->dev, tmp);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "no rxtx%d clock in devicetree\n", i);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
if (!clk_get_rate(clk))
|
||||
continue;
|
||||
|
||||
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
|
||||
i == STC_TXCLK_SPDIF_ROOT);
|
||||
if (savesub == ret)
|
||||
continue;
|
||||
|
||||
savesub = ret;
|
||||
spdif_priv->txclk[index] = clk;
|
||||
spdif_priv->txclk_src[index] = i;
|
||||
|
||||
/* To quick catch a divisor, we allow a 0.1% deviation */
|
||||
if (savesub < 100)
|
||||
break;
|
||||
}
|
||||
|
||||
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 txclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->txclk_df[index], rate[index]);
|
||||
if (clk_is_match(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;
|
||||
}
|
||||
static const struct of_device_id fsl_spdif_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8mm-spdif", .data = &fsl_spdif_imx8mm, },
|
||||
{ .compatible = "fsl,imx8qm-spdif", .data = &fsl_spdif_imx8qm, },
|
||||
{ .compatible = "fsl,imx35-spdif", .data = &fsl_spdif_imx35, },
|
||||
{ .compatible = "fsl,vf610-spdif", .data = &fsl_spdif_vf610, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
|
||||
|
||||
static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -1218,8 +1358,11 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
struct fsl_spdif_priv *spdif_priv;
|
||||
struct spdif_mixer_control *ctrl;
|
||||
struct resource *res;
|
||||
const struct of_device_id *of_id;
|
||||
void __iomem *regs;
|
||||
int irq, ret, i;
|
||||
char tmp[16];
|
||||
int num_domains = 0;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
@ -1230,9 +1373,19 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
|
||||
spdif_priv->pdev = pdev;
|
||||
|
||||
of_id = of_match_device(fsl_spdif_dt_ids, &pdev->dev);
|
||||
if (!of_id || !of_id->data)
|
||||
return -EINVAL;
|
||||
|
||||
spdif_priv->soc = of_id->data;
|
||||
|
||||
/* Initialize this copy of the CPU DAI driver structure */
|
||||
memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
|
||||
spdif_priv->cpu_dai_drv.name = dev_name(&pdev->dev);
|
||||
spdif_priv->cpu_dai_drv.playback.formats =
|
||||
spdif_priv->soc->tx_formats;
|
||||
spdif_priv->cpu_dai_drv.capture.rates =
|
||||
spdif_priv->soc->rx_rates;
|
||||
|
||||
/* Get the addresses and IRQ */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -1260,8 +1413,50 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (spdif_priv->soc->interrupts > 1) {
|
||||
irq = platform_get_irq(pdev, 1);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
|
||||
dev_name(&pdev->dev), spdif_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not claim irq %u\n", irq);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
num_domains = of_count_phandle_with_args(np, "power-domains",
|
||||
"#power-domain-cells");
|
||||
for (i = 0; i < num_domains; i++) {
|
||||
struct device *pd_dev;
|
||||
struct device_link *link;
|
||||
|
||||
pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i);
|
||||
if (IS_ERR(pd_dev))
|
||||
return PTR_ERR(pd_dev);
|
||||
|
||||
link = device_link_add(&pdev->dev, pd_dev,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (IS_ERR(link))
|
||||
return PTR_ERR(link);
|
||||
}
|
||||
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
|
||||
sprintf(tmp, "rxtx%d", i);
|
||||
spdif_priv->txclk[i] = devm_clk_get(&pdev->dev, tmp);
|
||||
if (IS_ERR(spdif_priv->txclk[i])) {
|
||||
dev_err(&pdev->dev, "no rxtx%d clock in devicetree\n", i);
|
||||
return PTR_ERR(spdif_priv->txclk[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get system clock for rx clock rate calculation */
|
||||
spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5");
|
||||
spdif_priv->sysclk = spdif_priv->txclk[5];
|
||||
if (IS_ERR(spdif_priv->sysclk)) {
|
||||
dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n");
|
||||
return PTR_ERR(spdif_priv->sysclk);
|
||||
|
@ -1279,13 +1474,21 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
dev_warn(&pdev->dev, "no spba clock in devicetree\n");
|
||||
|
||||
/* Select clock source for rx/tx clock */
|
||||
spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
|
||||
spdif_priv->rxclk = spdif_priv->txclk[1];
|
||||
if (IS_ERR(spdif_priv->rxclk)) {
|
||||
dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n");
|
||||
return PTR_ERR(spdif_priv->rxclk);
|
||||
}
|
||||
spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
|
||||
|
||||
spdif_priv->pll8k_clk = devm_clk_get(&pdev->dev, "pll8k");
|
||||
if (IS_ERR(spdif_priv->pll8k_clk))
|
||||
spdif_priv->pll8k_clk = NULL;
|
||||
|
||||
spdif_priv->pll11k_clk = devm_clk_get(&pdev->dev, "pll11k");
|
||||
if (IS_ERR(spdif_priv->pll11k_clk))
|
||||
spdif_priv->pll11k_clk = NULL;
|
||||
|
||||
for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
|
||||
ret = fsl_spdif_probe_txclk(spdif_priv, i);
|
||||
if (ret)
|
||||
|
@ -1306,11 +1509,17 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
|
||||
spdif_priv->dpll_locked = false;
|
||||
|
||||
spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML;
|
||||
spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML;
|
||||
spdif_priv->dma_params_tx.maxburst = spdif_priv->soc->tx_burst;
|
||||
spdif_priv->dma_params_rx.maxburst = spdif_priv->soc->rx_burst;
|
||||
spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL;
|
||||
spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL;
|
||||
|
||||
/*Clear the val bit for Tx*/
|
||||
regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SCR,
|
||||
SCR_VAL_MASK, 1 << SCR_VAL_OFFSET);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/* Register with ASoC */
|
||||
dev_set_drvdata(&pdev->dev, spdif_priv);
|
||||
|
||||
|
@ -1328,45 +1537,88 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int fsl_spdif_suspend(struct device *dev)
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_spdif_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
|
||||
&spdif_priv->regcache_srpc);
|
||||
ret = clk_prepare_enable(spdif_priv->coreclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable core clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regcache_cache_only(spdif_priv->regmap, true);
|
||||
regcache_mark_dirty(spdif_priv->regmap);
|
||||
if (!IS_ERR(spdif_priv->spbaclk)) {
|
||||
ret = clk_prepare_enable(spdif_priv->spbaclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable spba clock\n");
|
||||
goto disable_core_clk;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
|
||||
ret = clk_prepare_enable(spdif_priv->txclk[i]);
|
||||
if (ret)
|
||||
goto disable_spba_clk;
|
||||
}
|
||||
|
||||
static int fsl_spdif_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
|
||||
request_bus_freq(BUS_FREQ_HIGH);
|
||||
|
||||
regcache_cache_only(spdif_priv->regmap, false);
|
||||
regcache_mark_dirty(spdif_priv->regmap);
|
||||
|
||||
regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC,
|
||||
SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
|
||||
spdif_priv->regcache_srpc);
|
||||
|
||||
return regcache_sync(spdif_priv->regmap);
|
||||
ret = regcache_sync(spdif_priv->regmap);
|
||||
if (ret)
|
||||
goto disable_tx_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_tx_clk:
|
||||
disable_spba_clk:
|
||||
for (i--; i >= 0; i--)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
disable_core_clk:
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int fsl_spdif_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
|
||||
&spdif_priv->regcache_srpc);
|
||||
regcache_cache_only(spdif_priv->regmap, true);
|
||||
|
||||
release_bus_freq(BUS_FREQ_HIGH);
|
||||
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops fsl_spdif_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(fsl_spdif_runtime_suspend, fsl_spdif_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
static struct platform_driver fsl_spdif_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-spdif-dai",
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#define SCR_TXFIFO_FSEL_IF4 (0x1 << SCR_TXFIFO_FSEL_OFFSET)
|
||||
#define SCR_TXFIFO_FSEL_IF8 (0x2 << SCR_TXFIFO_FSEL_OFFSET)
|
||||
#define SCR_TXFIFO_FSEL_IF12 (0x3 << SCR_TXFIFO_FSEL_OFFSET)
|
||||
#define SCR_RAW_CAPTURE_MODE (1 << 14)
|
||||
#define SCR_LOW_POWER (1 << 13)
|
||||
#define SCR_SOFT_RESET (1 << 12)
|
||||
#define SCR_TXFIFO_CTRL_OFFSET 10
|
||||
|
|
|
@ -1,16 +1,44 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Copyright (C) 2013 Freescale Semiconductor, Inc.
|
||||
// Copyright (C) 2013-2019 Freescale Semiconductor, Inc.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <sound/soc.h>
|
||||
#include "fsl_spdif.h"
|
||||
|
||||
struct imx_spdif_data {
|
||||
struct snd_soc_dai_link dai;
|
||||
struct snd_soc_card card;
|
||||
};
|
||||
|
||||
#define CLK_8K_FREQ 24576000
|
||||
#define CLK_11K_FREQ 22579200
|
||||
|
||||
static int imx_spdif_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct device *dev = rtd->card->dev;
|
||||
int ret = 0;
|
||||
u64 rate = params_rate(params);
|
||||
unsigned int freq;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
freq = do_div(rate, 8000) ? CLK_11K_FREQ : CLK_8K_FREQ;
|
||||
ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, STC_TXCLK_SPDIF_ROOT,
|
||||
freq, SND_SOC_CLOCK_OUT);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops imx_spdif_ops = {
|
||||
.hw_params = imx_spdif_hw_params,
|
||||
};
|
||||
|
||||
static int imx_spdif_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *spdif_np, *np = pdev->dev.of_node;
|
||||
|
@ -48,6 +76,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
|
|||
data->dai.platforms->of_node = spdif_np;
|
||||
data->dai.playback_only = true;
|
||||
data->dai.capture_only = true;
|
||||
data->dai.ops = &imx_spdif_ops;
|
||||
|
||||
if (of_property_read_bool(np, "spdif-out"))
|
||||
data->dai.capture_only = false;
|
||||
|
|
Loading…
Reference in New Issue
Block a user