{"CPU-Capture", NULL, "Capture"},
};
++++ +static const struct snd_soc_dapm_route audio_map_ac97[] = {
++++ + {"AC97 Playback", NULL, "ASRC-Playback"},
++++ + {"Playback", NULL, "AC97 Playback"},
++++ + {"ASRC-Capture", NULL, "AC97 Capture"},
++++ + {"AC97 Capture", NULL, "Capture"},
++++ +};
++++ +
/* Add all possible widgets into here without being redundant */
static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Line Out Jack", NULL),
enum snd_soc_bias_level level)
{
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
----- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+++++ struct snd_soc_pcm_runtime *rtd;
+++++ struct snd_soc_dai *codec_dai;
struct codec_priv *codec_priv = &priv->codec_priv;
struct device *dev = card->dev;
unsigned int pll_out;
int ret;
+++++ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+++++ codec_dai = rtd->codec_dai;
if (dapm->dev != codec_dai->dev)
return 0;
static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
{
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
----- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+++++ struct snd_soc_pcm_runtime *rtd = list_first_entry(
+++++ &card->rtd_list, struct snd_soc_pcm_runtime, list);
+++++ struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct codec_priv *codec_priv = &priv->codec_priv;
struct device *dev = card->dev;
int ret;
if (fsl_asoc_card_is_ac97(priv)) {
#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
----- struct snd_soc_codec *codec = card->rtd[0].codec;
+++++ struct snd_soc_codec *codec = rtd->codec;
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
/*
priv->card.dev = &pdev->dev;
priv->card.name = priv->name;
priv->card.dai_link = priv->dai_link;
---- - priv->card.dapm_routes = audio_map;
++++ + priv->card.dapm_routes = fsl_asoc_card_is_ac97(priv) ?
++++ + audio_map_ac97 : audio_map;
priv->card.late_probe = fsl_asoc_card_late_probe;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
/* Sample rates are aligned with that defined in pcm.h file */
---- -static const u8 process_option[][8][2] = {
---- - /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
---- - {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
---- - {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
---- - {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
---- - {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
---- - {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
---- - {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
---- - {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
---- - {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
---- - {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
---- - {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
---- - {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
---- - {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
---- - {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
++++ +static const u8 process_option[][12][2] = {
++++ + /* 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
++++ + {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
++++ + {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
++++ + {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
++++ + {{1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
++++ + {{1, 2}, {1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
++++ + {{1, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
++++ + {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
++++ + {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
++++ + {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
++++ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
++++ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
++++ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
++++ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
};
/* Corresponding to process_option */
};
static int supported_asrc_rate[] = {
---- - 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
++++ + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
};
/**
return -EINVAL;
}
++++ + if ((outrate > 8000 && outrate < 30000) &&
++++ + (outrate/inrate > 24 || inrate/outrate > 8)) {
++++ + pair_err("exceed supported ratio range [1/24, 8] for \
++++ + inrate/outrate: %d/%d\n", inrate, outrate);
++++ + return -EINVAL;
++++ + }
++++ +
/* Validate input and output clock sources */
clk_index[IN] = clk_map[IN][config->inclk];
clk_index[OUT] = clk_map[OUT][config->outclk];
struct snd_soc_dai *dai)
{
struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
--- -- int width = snd_pcm_format_width(params_format(params));
+++ ++ int width = params_width(params);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
unsigned int channels = params_channels(params);
return PTR_ERR(asrc_priv->ipg_clk);
}
++++ + asrc_priv->spba_clk = devm_clk_get(&pdev->dev, "spba");
++++ + if (IS_ERR(asrc_priv->spba_clk))
++++ + dev_warn(&pdev->dev, "failed to get spba clock\n");
++++ +
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
sprintf(tmp, "asrck_%x", i);
asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
ret = clk_prepare_enable(asrc_priv->ipg_clk);
if (ret)
goto disable_mem_clk;
++++ + if (!IS_ERR(asrc_priv->spba_clk)) {
++++ + ret = clk_prepare_enable(asrc_priv->spba_clk);
++++ + if (ret)
++++ + goto disable_ipg_clk;
++++ + }
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
if (ret)
disable_asrck_clk:
for (i--; i >= 0; i--)
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
++++ + if (!IS_ERR(asrc_priv->spba_clk))
++++ + clk_disable_unprepare(asrc_priv->spba_clk);
++++ +disable_ipg_clk:
clk_disable_unprepare(asrc_priv->ipg_clk);
disable_mem_clk:
clk_disable_unprepare(asrc_priv->mem_clk);
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
++++ + if (!IS_ERR(asrc_priv->spba_clk))
++++ + clk_disable_unprepare(asrc_priv->spba_clk);
clk_disable_unprepare(asrc_priv->ipg_clk);
clk_disable_unprepare(asrc_priv->mem_clk);
* @coreclk: clock source to access register
* @extalclk: esai clock source to derive HCK, SCK and FS
* @fsysclk: system clock source to derive HCK, SCK and FS
+++++ * @spbaclk: SPBA clock (optional, depending on SoC design)
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @slots: number of slots
struct clk *coreclk;
struct clk *extalclk;
struct clk *fsysclk;
+++++ struct clk *spbaclk;
u32 fifo_depth;
u32 slot_width;
u32 slots;
ret = clk_prepare_enable(esai_priv->coreclk);
if (ret)
return ret;
+++++ if (!IS_ERR(esai_priv->spbaclk)) {
+++++ ret = clk_prepare_enable(esai_priv->spbaclk);
+++++ if (ret)
+++++ goto err_spbaclk;
+++++ }
if (!IS_ERR(esai_priv->extalclk)) {
ret = clk_prepare_enable(esai_priv->extalclk);
if (ret)
if (!IS_ERR(esai_priv->extalclk))
clk_disable_unprepare(esai_priv->extalclk);
err_extalck:
+++++ if (!IS_ERR(esai_priv->spbaclk))
+++++ clk_disable_unprepare(esai_priv->spbaclk);
+++++ err_spbaclk:
clk_disable_unprepare(esai_priv->coreclk);
return ret;
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
--- -- u32 width = snd_pcm_format_width(params_format(params));
+++ ++ u32 width = params_width(params);
u32 channels = params_channels(params);
u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
u32 slot_width = width;
clk_disable_unprepare(esai_priv->fsysclk);
if (!IS_ERR(esai_priv->extalclk))
clk_disable_unprepare(esai_priv->extalclk);
+++++ if (!IS_ERR(esai_priv->spbaclk))
+++++ clk_disable_unprepare(esai_priv->spbaclk);
clk_disable_unprepare(esai_priv->coreclk);
}
};
static const struct reg_default fsl_esai_reg_defaults[] = {
--- -- {0x8, 0x00000000},
--- -- {0x10, 0x00000000},
--- -- {0x18, 0x00000000},
--- -- {0x98, 0x00000000},
--- -- {0xd0, 0x00000000},
--- -- {0xd4, 0x00000000},
--- -- {0xd8, 0x00000000},
--- -- {0xdc, 0x00000000},
--- -- {0xe0, 0x00000000},
--- -- {0xe4, 0x0000ffff},
--- -- {0xe8, 0x0000ffff},
--- -- {0xec, 0x0000ffff},
--- -- {0xf0, 0x0000ffff},
--- -- {0xf8, 0x00000000},
--- -- {0xfc, 0x00000000},
+++ ++ {REG_ESAI_ETDR, 0x00000000},
+++ ++ {REG_ESAI_ECR, 0x00000000},
+++ ++ {REG_ESAI_TFCR, 0x00000000},
+++ ++ {REG_ESAI_RFCR, 0x00000000},
+++ ++ {REG_ESAI_TX0, 0x00000000},
+++ ++ {REG_ESAI_TX1, 0x00000000},
+++ ++ {REG_ESAI_TX2, 0x00000000},
+++ ++ {REG_ESAI_TX3, 0x00000000},
+++ ++ {REG_ESAI_TX4, 0x00000000},
+++ ++ {REG_ESAI_TX5, 0x00000000},
+++ ++ {REG_ESAI_TSR, 0x00000000},
+++ ++ {REG_ESAI_SAICR, 0x00000000},
+++ ++ {REG_ESAI_TCR, 0x00000000},
+++ ++ {REG_ESAI_TCCR, 0x00000000},
+++ ++ {REG_ESAI_RCR, 0x00000000},
+++ ++ {REG_ESAI_RCCR, 0x00000000},
+++ ++ {REG_ESAI_TSMA, 0x0000ffff},
+++ ++ {REG_ESAI_TSMB, 0x0000ffff},
+++ ++ {REG_ESAI_RSMA, 0x0000ffff},
+++ ++ {REG_ESAI_RSMB, 0x0000ffff},
+++ ++ {REG_ESAI_PRRC, 0x00000000},
+++ ++ {REG_ESAI_PCRC, 0x00000000},
};
static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
--- -- case REG_ESAI_ETDR:
case REG_ESAI_ERDR:
case REG_ESAI_ESR:
case REG_ESAI_TFSR:
case REG_ESAI_RFSR:
--- -- case REG_ESAI_TX0:
--- -- case REG_ESAI_TX1:
--- -- case REG_ESAI_TX2:
--- -- case REG_ESAI_TX3:
--- -- case REG_ESAI_TX4:
--- -- case REG_ESAI_TX5:
case REG_ESAI_RX0:
case REG_ESAI_RX1:
case REG_ESAI_RX2:
dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",
PTR_ERR(esai_priv->fsysclk));
+++++ esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");
+++++ if (IS_ERR(esai_priv->spbaclk))
+++++ dev_warn(&pdev->dev, "failed to get spba clock: %ld\n",
+++++ PTR_ERR(esai_priv->spbaclk));
+++++
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
return IRQ_HANDLED;
}
+++ ++static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+++ ++ u32 rx_mask, int slots, int slot_width)
+++ ++{
+++ ++ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+++ ++
+++ ++ sai->slots = slots;
+++ ++ sai->slot_width = slot_width;
+++ ++
+++ ++ return 0;
+++ ++}
+++ ++
static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int fsl_dir)
{
return -EINVAL;
}
--- -- if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) {
+++ ++ /*
+++ ++ * 1) For Asynchronous mode, we must set RCR2 register for capture, and
+++ ++ * set TCR2 register for playback.
+++ ++ * 2) For Tx sync with Rx clock, we must set RCR2 register for playback
+++ ++ * and capture.
+++ ++ * 3) For Rx sync with Tx clock, we must set TCR2 register for playback
+++ ++ * and capture.
+++ ++ * 4) For Tx and Rx are both Synchronous with another SAI, we just
+++ ++ * ignore it.
+++ ++ */
+++ ++ if ((sai->synchronous[TX] && !sai->synchronous[RX]) ||
+++ ++ (!tx && !sai->synchronous[RX])) {
regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
FSL_SAI_CR2_DIV_MASK, savediv - 1);
--- -- } else {
+++ ++ } else if ((sai->synchronous[RX] && !sai->synchronous[TX]) ||
+++ ++ (tx && !sai->synchronous[TX])) {
regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int channels = params_channels(params);
--- -- u32 word_width = snd_pcm_format_width(params_format(params));
+++ ++ u32 word_width = params_width(params);
u32 val_cr4 = 0, val_cr5 = 0;
+++ ++ u32 slots = (channels == 1) ? 2 : channels;
+++ ++ u32 slot_width = word_width;
int ret;
+++ ++ if (sai->slots)
+++ ++ slots = sai->slots;
+++ ++
+++ ++ if (sai->slot_width)
+++ ++ slot_width = sai->slot_width;
+++ ++
if (!sai->is_slave_mode) {
ret = fsl_sai_set_bclk(cpu_dai, tx,
--- -- 2 * word_width * params_rate(params));
+++ ++ slots * slot_width * params_rate(params));
if (ret)
return ret;
sai->mclk_streams |= BIT(substream->stream);
}
--- --
}
if (!sai->is_dsp_mode)
--- -- val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
+++ ++ val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
--- -- val_cr5 |= FSL_SAI_CR5_WNW(word_width);
--- -- val_cr5 |= FSL_SAI_CR5_W0W(word_width);
+++ ++ val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
+++ ++ val_cr5 |= FSL_SAI_CR5_W0W(slot_width);
if (sai->is_lsb_first)
val_cr5 |= FSL_SAI_CR5_FBT(0);
else
val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
--- -- val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
+++ ++ val_cr4 |= FSL_SAI_CR4_FRSZ(slots);
+++ ++
+++ ++ /*
+++ ++ * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
+++ ++ * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
+++ ++ * RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync
+++ ++ * error.
+++ ++ */
+++ ++
+++ ++ if (!sai->is_slave_mode) {
+++ ++ if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) {
+++ ++ regmap_update_bits(sai->regmap, FSL_SAI_TCR4,
+++ ++ FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+++ ++ val_cr4);
+++ ++ regmap_update_bits(sai->regmap, FSL_SAI_TCR5,
+++ ++ FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+++ ++ FSL_SAI_CR5_FBT_MASK, val_cr5);
+++ ++ regmap_write(sai->regmap, FSL_SAI_TMR,
+++ ++ ~0UL - ((1 << channels) - 1));
+++ ++ } else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) {
+++ ++ regmap_update_bits(sai->regmap, FSL_SAI_RCR4,
+++ ++ FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+++ ++ val_cr4);
+++ ++ regmap_update_bits(sai->regmap, FSL_SAI_RCR5,
+++ ++ FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+++ ++ FSL_SAI_CR5_FBT_MASK, val_cr5);
+++ ++ regmap_write(sai->regmap, FSL_SAI_RMR,
+++ ++ ~0UL - ((1 << channels) - 1));
+++ ++ }
+++ ++ }
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
* Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
* Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
*/
----- regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0);
+++++ regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
+++++ sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
+++++
+++++ /*
+++++ * For sai master mode, after several open/close sai,
+++++ * there will be no frame clock, and can't recover
+++++ * anymore. Add software reset to fix this issue.
+++++ * This is a hardware bug, and will be fix in the
+++++ * next sai version.
+++++ */
+++++ if (!sai->is_slave_mode) {
+++++ /* Software Reset for both Tx and Rx */
+++++ regmap_write(sai->regmap,
+++++ FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+++++ regmap_write(sai->regmap,
+++++ FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+++++ /* Clear SR bit to finish the reset */
+++++ regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+++++ regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+++++ }
}
break;
default:
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt,
+++ ++ .set_tdm_slot = fsl_sai_set_dai_tdm_slot,
.hw_params = fsl_sai_hw_params,
.hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
.name = "fsl-sai",
};
+++ ++static struct reg_default fsl_sai_reg_defaults[] = {
+++ ++ {FSL_SAI_TCR1, 0},
+++ ++ {FSL_SAI_TCR2, 0},
+++ ++ {FSL_SAI_TCR3, 0},
+++ ++ {FSL_SAI_TCR4, 0},
+++ ++ {FSL_SAI_TCR5, 0},
+++ ++ {FSL_SAI_TDR, 0},
+++ ++ {FSL_SAI_TMR, 0},
+++ ++ {FSL_SAI_RCR1, 0},
+++ ++ {FSL_SAI_RCR2, 0},
+++ ++ {FSL_SAI_RCR3, 0},
+++ ++ {FSL_SAI_RCR4, 0},
+++ ++ {FSL_SAI_RCR5, 0},
+++ ++ {FSL_SAI_RMR, 0},
+++ ++};
+++ ++
static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case FSL_SAI_RCSR:
case FSL_SAI_TFR:
case FSL_SAI_RFR:
--- -- case FSL_SAI_TDR:
case FSL_SAI_RDR:
return true;
default:
return false;
}
--- --
}
static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
.val_bits = 32,
.max_register = FSL_SAI_RMR,
+++ ++ .reg_defaults = fsl_sai_reg_defaults,
+++ ++ .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults),
.readable_reg = fsl_sai_readable_reg,
.volatile_reg = fsl_sai_volatile_reg,
.writeable_reg = fsl_sai_writeable_reg,
struct snd_soc_dapm_widget *widget, int stream)
{
struct snd_soc_pcm_runtime *be;
----- int i, j;
+++++ int i;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
----- for (i = 0; i < card->num_links; i++) {
----- be = &card->rtd[i];
+++++ list_for_each_entry(be, &card->rtd_list, list) {
if (!be->dai_link->no_pcm)
continue;
if (be->cpu_dai->playback_widget == widget)
return be;
----- for (j = 0; j < be->num_codecs; j++) {
----- struct snd_soc_dai *dai = be->codec_dais[j];
+++++ for (i = 0; i < be->num_codecs; i++) {
+++++ struct snd_soc_dai *dai = be->codec_dais[i];
if (dai->playback_widget == widget)
return be;
}
}
} else {
----- for (i = 0; i < card->num_links; i++) {
----- be = &card->rtd[i];
+++++ list_for_each_entry(be, &card->rtd_list, list) {
if (!be->dai_link->no_pcm)
continue;
if (be->cpu_dai->capture_widget == widget)
return be;
----- for (j = 0; j < be->num_codecs; j++) {
----- struct snd_soc_dai *dai = be->codec_dais[j];
+++++ for (i = 0; i < be->num_codecs; i++) {
+++++ struct snd_soc_dai *dai = be->codec_dais[i];
if (dai->capture_widget == widget)
return be;
}
snd_pcm_stream_unlock_irq(substream);
}
+ ++++static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
+ ++++ int stream)
+ ++++{
+ ++++ struct snd_soc_dpcm *dpcm;
+ ++++ struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+ ++++ struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai;
+ ++++ int err;
+ ++++
+ ++++ /* apply symmetry for FE */
+ ++++ if (soc_pcm_has_symmetry(fe_substream))
+ ++++ fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+ ++++
+ ++++ /* Symmetry only applies if we've got an active stream. */
+ ++++ if (fe_cpu_dai->active) {
+ ++++ err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
+ ++++ if (err < 0)
+ ++++ return err;
+ ++++ }
+ ++++
+ ++++ /* apply symmetry for BE */
+ ++++ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ ++++ struct snd_soc_pcm_runtime *be = dpcm->be;
+ ++++ struct snd_pcm_substream *be_substream =
+ ++++ snd_soc_dpcm_get_substream(be, stream);
+ ++++ struct snd_soc_pcm_runtime *rtd = be_substream->private_data;
+ ++++ int i;
+ ++++
+ ++++ if (soc_pcm_has_symmetry(be_substream))
+ ++++ be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+ ++++
+ ++++ /* Symmetry only applies if we've got an active stream. */
+ ++++ if (rtd->cpu_dai->active) {
+ ++++ err = soc_pcm_apply_symmetry(be_substream, rtd->cpu_dai);
+ ++++ if (err < 0)
+ ++++ return err;
+ ++++ }
+ ++++
+ ++++ for (i = 0; i < rtd->num_codecs; i++) {
+ ++++ if (rtd->codec_dais[i]->active) {
+ ++++ err = soc_pcm_apply_symmetry(be_substream,
+ ++++ rtd->codec_dais[i]);
+ ++++ if (err < 0)
+ ++++ return err;
+ ++++ }
+ ++++ }
+ ++++ }
+ ++++
+ ++++ return 0;
+ ++++}
+ ++++
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
{
struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
dpcm_set_fe_runtime(fe_substream);
snd_pcm_limit_hw_rates(runtime);
+ ++++ ret = dpcm_apply_symmetry(fe_substream, stream);
+ ++++ if (ret < 0) {
+ ++++ dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n",
+ ++++ ret);
+ ++++ goto unwind;
+ ++++ }
+ ++++
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
continue;
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
- ---- (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ ++++ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+ ++++ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
continue;
dev_dbg(be->dev, "ASoC: prepare BE %s\n",
*/
int soc_dpcm_runtime_update(struct snd_soc_card *card)
{
----- int i, old, new, paths;
+++++ struct snd_soc_pcm_runtime *fe;
+++++ int old, new, paths;
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
----- for (i = 0; i < card->num_rtd; i++) {
+++++ list_for_each_entry(fe, &card->rtd_list, list) {
struct snd_soc_dapm_widget_list *list;
----- struct snd_soc_pcm_runtime *fe = &card->rtd[i];
/* make sure link is FE */
if (!fe->dai_link->dynamic)