return 0;
}
- -----------------int wm_adsp2_event(struct snd_soc_dapm_widget *w,
- ----------------- struct snd_kcontrol *kcontrol, int event)
+ +++++++++++++++++static void wm_adsp2_boot_work(struct work_struct *work)
{
- ----------------- struct snd_soc_codec *codec = w->codec;
- ----------------- struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
- ----------------- struct wm_adsp *dsp = &dsps[w->shift];
- ----------------- struct wm_adsp_alg_region *alg_region;
- ----------------- struct wm_coeff_ctl *ctl;
- ----------------- unsigned int val;
+ +++++++++++++++++ struct wm_adsp *dsp = container_of(work,
+ +++++++++++++++++ struct wm_adsp,
+ +++++++++++++++++ boot_work);
int ret;
+ +++++++++++++++++ unsigned int val;
- ----------------- dsp->card = codec->card;
+ +++++++++++++++++ /*
+ +++++++++++++++++ * For simplicity set the DSP clock rate to be the
+ +++++++++++++++++ * SYSCLK rate rather than making it configurable.
+ +++++++++++++++++ */
+ +++++++++++++++++ ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
+ +++++++++++++++++ if (ret != 0) {
+ +++++++++++++++++ adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
+ +++++++++++++++++ return;
+ +++++++++++++++++ }
+ +++++++++++++++++ val = (val & ARIZONA_SYSCLK_FREQ_MASK)
+ +++++++++++++++++ >> ARIZONA_SYSCLK_FREQ_SHIFT;
- ----------------- switch (event) {
- ----------------- case SND_SOC_DAPM_POST_PMU:
- ----------------- /*
- ----------------- * For simplicity set the DSP clock rate to be the
- ----------------- * SYSCLK rate rather than making it configurable.
- ----------------- */
- ----------------- ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
- ----------------- if (ret != 0) {
- ----------------- adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
- ----------------- ret);
- ----------------- return ret;
- ----------------- }
- ----------------- val = (val & ARIZONA_SYSCLK_FREQ_MASK)
- ----------------- >> ARIZONA_SYSCLK_FREQ_SHIFT;
+ +++++++++++++++++ ret = regmap_update_bits_async(dsp->regmap,
+ +++++++++++++++++ dsp->base + ADSP2_CLOCKING,
+ +++++++++++++++++ ADSP2_CLK_SEL_MASK, val);
+ +++++++++++++++++ if (ret != 0) {
+ +++++++++++++++++ adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+ +++++++++++++++++ return;
+ +++++++++++++++++ }
- ret = regmap_update_bits_async(dsp->regmap,
- dsp->base + ADSP2_CLOCKING,
- ADSP2_CLK_SEL_MASK, val);
----------------- ret = regmap_update_bits(dsp->regmap,
----------------- dsp->base + ADSP2_CLOCKING,
----------------- ADSP2_CLK_SEL_MASK, val);
+ +++++++++++++++++ if (dsp->dvfs) {
+ +++++++++++++++++ ret = regmap_read(dsp->regmap,
+ +++++++++++++++++ dsp->base + ADSP2_CLOCKING, &val);
if (ret != 0) {
- ----------------- adsp_err(dsp, "Failed to set clock rate: %d\n",
- ----------------- ret);
- ----------------- return ret;
+ +++++++++++++++++ dev_err(dsp->dev, "Failed to read clocking: %d\n", ret);
+ +++++++++++++++++ return;
}
- ----------------- if (dsp->dvfs) {
- ----------------- ret = regmap_read(dsp->regmap,
- ----------------- dsp->base + ADSP2_CLOCKING, &val);
+ +++++++++++++++++ if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
+ +++++++++++++++++ ret = regulator_enable(dsp->dvfs);
if (ret != 0) {
dev_err(dsp->dev,
- ----------------- "Failed to read clocking: %d\n", ret);
- ----------------- return ret;
+ +++++++++++++++++ "Failed to enable supply: %d\n",
+ +++++++++++++++++ ret);
+ +++++++++++++++++ return;
}
- ----------------- if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
- ----------------- ret = regulator_enable(dsp->dvfs);
- ----------------- if (ret != 0) {
- ----------------- dev_err(dsp->dev,
- ----------------- "Failed to enable supply: %d\n",
- ----------------- ret);
- ----------------- return ret;
- ----------------- }
- -----------------
- ----------------- ret = regulator_set_voltage(dsp->dvfs,
- ----------------- 1800000,
- ----------------- 1800000);
- ----------------- if (ret != 0) {
- ----------------- dev_err(dsp->dev,
- ----------------- "Failed to raise supply: %d\n",
- ----------------- ret);
- ----------------- return ret;
- ----------------- }
+ +++++++++++++++++ ret = regulator_set_voltage(dsp->dvfs,
+ +++++++++++++++++ 1800000,
+ +++++++++++++++++ 1800000);
+ +++++++++++++++++ if (ret != 0) {
+ +++++++++++++++++ dev_err(dsp->dev,
+ +++++++++++++++++ "Failed to raise supply: %d\n",
+ +++++++++++++++++ ret);
+ +++++++++++++++++ return;
}
}
+ +++++++++++++++++ }
- ----------------- ret = wm_adsp2_ena(dsp);
- ----------------- if (ret != 0)
- ----------------- return ret;
+ +++++++++++++++++ ret = wm_adsp2_ena(dsp);
+ +++++++++++++++++ if (ret != 0)
+ +++++++++++++++++ return;
- ----------------- ret = wm_adsp_load(dsp);
- ----------------- if (ret != 0)
- ----------------- goto err;
+ +++++++++++++++++ ret = wm_adsp_load(dsp);
+ +++++++++++++++++ if (ret != 0)
+ +++++++++++++++++ goto err;
- ----------------- ret = wm_adsp_setup_algs(dsp);
- ----------------- if (ret != 0)
- ----------------- goto err;
+ +++++++++++++++++ ret = wm_adsp_setup_algs(dsp);
+ +++++++++++++++++ if (ret != 0)
+ +++++++++++++++++ goto err;
- ----------------- ret = wm_adsp_load_coeff(dsp);
- ----------------- if (ret != 0)
- ----------------- goto err;
+ +++++++++++++++++ ret = wm_adsp_load_coeff(dsp);
+ +++++++++++++++++ if (ret != 0)
+ +++++++++++++++++ goto err;
- ----------------- /* Initialize caches for enabled and unset controls */
- ----------------- ret = wm_coeff_init_control_caches(dsp);
- ----------------- if (ret != 0)
- ----------------- goto err;
+ +++++++++++++++++ /* Initialize caches for enabled and unset controls */
+ +++++++++++++++++ ret = wm_coeff_init_control_caches(dsp);
+ +++++++++++++++++ if (ret != 0)
+ +++++++++++++++++ goto err;
- ----------------- /* Sync set controls */
- ----------------- ret = wm_coeff_sync_controls(dsp);
- ----------------- if (ret != 0)
- ----------------- goto err;
+ +++++++++++++++++ /* Sync set controls */
+ +++++++++++++++++ ret = wm_coeff_sync_controls(dsp);
+ +++++++++++++++++ if (ret != 0)
+ +++++++++++++++++ goto err;
+ +++++++++++++++++
+ +++++++++++++++++ ret = regmap_update_bits_async(dsp->regmap,
+ +++++++++++++++++ dsp->base + ADSP2_CONTROL,
+ +++++++++++++++++ ADSP2_CORE_ENA,
+ +++++++++++++++++ ADSP2_CORE_ENA);
+ +++++++++++++++++ if (ret != 0)
+ +++++++++++++++++ goto err;
+ +++++++++++++++++
+ +++++++++++++++++ dsp->running = true;
+ +++++++++++++++++
+ +++++++++++++++++ return;
+ +++++++++++++++++
+ +++++++++++++++++err:
+ +++++++++++++++++ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ +++++++++++++++++ ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+ +++++++++++++++++}
+ +++++++++++++++++
+ +++++++++++++++++int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
+ +++++++++++++++++ struct snd_kcontrol *kcontrol, int event)
+ +++++++++++++++++{
+ +++++++++++++++++ struct snd_soc_codec *codec = w->codec;
+ +++++++++++++++++ struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ +++++++++++++++++ struct wm_adsp *dsp = &dsps[w->shift];
+ +++++++++++++++++
+ +++++++++++++++++ dsp->card = codec->card;
+ +++++++++++++++++
+ +++++++++++++++++ switch (event) {
+ +++++++++++++++++ case SND_SOC_DAPM_PRE_PMU:
+ +++++++++++++++++ queue_work(system_unbound_wq, &dsp->boot_work);
+ +++++++++++++++++ break;
+ +++++++++++++++++ default:
+ +++++++++++++++++ break;
+ +++++++++++++++++ };
+ +++++++++++++++++
+ +++++++++++++++++ return 0;
+ +++++++++++++++++}
+ +++++++++++++++++EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
+ +++++++++++++++++
+ +++++++++++++++++int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+ +++++++++++++++++ struct snd_kcontrol *kcontrol, int event)
+ +++++++++++++++++{
+ +++++++++++++++++ struct snd_soc_codec *codec = w->codec;
+ +++++++++++++++++ struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ +++++++++++++++++ struct wm_adsp *dsp = &dsps[w->shift];
+ +++++++++++++++++ struct wm_adsp_alg_region *alg_region;
+ +++++++++++++++++ struct wm_coeff_ctl *ctl;
+ +++++++++++++++++ int ret;
+ +++++++++++++++++
+ +++++++++++++++++ switch (event) {
+ +++++++++++++++++ case SND_SOC_DAPM_POST_PMU:
+ +++++++++++++++++ flush_work(&dsp->boot_work);
+ +++++++++++++++++
+ +++++++++++++++++ if (!dsp->running)
+ +++++++++++++++++ return -EIO;
- ret = regmap_update_bits_async(dsp->regmap,
- dsp->base + ADSP2_CONTROL,
- ADSP2_CORE_ENA | ADSP2_START,
- ADSP2_CORE_ENA | ADSP2_START);
+ ret = regmap_update_bits(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
----------------- ADSP2_CORE_ENA | ADSP2_START,
----------------- ADSP2_CORE_ENA | ADSP2_START);
+ +++++++++++++++++ ADSP2_START,
+ +++++++++++++++++ ADSP2_START);
if (ret != 0)
goto err;
- -----------------
- ----------------- dsp->running = true;
break;
case SND_SOC_DAPM_PRE_PMD:
struct ccsr_ssi __iomem *ssi;
dma_addr_t ssi_phys;
unsigned int irq;
---- ------------- struct snd_pcm_substream *first_stream;
---- ------------- struct snd_pcm_substream *second_stream;
unsigned int fifo_depth;
struct snd_soc_dai_driver cpu_dai_drv;
----- ------------- struct device_attribute dev_attr;
struct platform_device *pdev;
+++++ +++++++++++++ enum fsl_ssi_type hw_type;
bool new_binding;
bool ssi_on_imx;
bool imx_ac97;
bool use_dma;
++++ +++++++++++++ bool baudclk_locked;
+++++ +++++++++++++ bool irq_stats;
+++++ +++++++++++++ bool offline_config;
++++ +++++++++++++ u8 i2s_mode;
++++ +++++++++++++ spinlock_t baudclk_lock;
++++ +++++++++++++ struct clk *baudclk;
struct clk *clk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
return ret;
}
+++++ +++++++++++++#if IS_ENABLED(CONFIG_DEBUG_FS)
+++++ +++++++++++++/* Show the statistics of a flag only if its interrupt is enabled. The
+++++ +++++++++++++ * compiler will optimze this code to a no-op if the interrupt is not
+++++ +++++++++++++ * enabled.
+++++ +++++++++++++ */
+++++ +++++++++++++#define SIER_SHOW(flag, name) \
+++++ +++++++++++++ do { \
+++++ +++++++++++++ if (FSLSSI_SISR_MASK & CCSR_SSI_SIER_##flag) \
+++++ +++++++++++++ seq_printf(s, #name "=%u\n", ssi_private->stats.name); \
+++++ +++++++++++++ } while (0)
+++++ +++++++++++++
+++++ +++++++++++++
+++++ +++++++++++++/**
+++++ +++++++++++++ * fsl_sysfs_ssi_show: display SSI statistics
+++++ +++++++++++++ *
+++++ +++++++++++++ * Display the statistics for the current SSI device. To avoid confusion,
+++++ +++++++++++++ * we only show those counts that are enabled.
+++++ +++++++++++++ */
+++++ +++++++++++++static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
+++++ +++++++++++++{
+++++ +++++++++++++ struct fsl_ssi_private *ssi_private = s->private;
+++++ +++++++++++++
+++++ +++++++++++++ SIER_SHOW(RFRC_EN, rfrc);
+++++ +++++++++++++ SIER_SHOW(TFRC_EN, tfrc);
+++++ +++++++++++++ SIER_SHOW(CMDAU_EN, cmdau);
+++++ +++++++++++++ SIER_SHOW(CMDDU_EN, cmddu);
+++++ +++++++++++++ SIER_SHOW(RXT_EN, rxt);
+++++ +++++++++++++ SIER_SHOW(RDR1_EN, rdr1);
+++++ +++++++++++++ SIER_SHOW(RDR0_EN, rdr0);
+++++ +++++++++++++ SIER_SHOW(TDE1_EN, tde1);
+++++ +++++++++++++ SIER_SHOW(TDE0_EN, tde0);
+++++ +++++++++++++ SIER_SHOW(ROE1_EN, roe1);
+++++ +++++++++++++ SIER_SHOW(ROE0_EN, roe0);
+++++ +++++++++++++ SIER_SHOW(TUE1_EN, tue1);
+++++ +++++++++++++ SIER_SHOW(TUE0_EN, tue0);
+++++ +++++++++++++ SIER_SHOW(TFS_EN, tfs);
+++++ +++++++++++++ SIER_SHOW(RFS_EN, rfs);
+++++ +++++++++++++ SIER_SHOW(TLS_EN, tls);
+++++ +++++++++++++ SIER_SHOW(RLS_EN, rls);
+++++ +++++++++++++ SIER_SHOW(RFF1_EN, rff1);
+++++ +++++++++++++ SIER_SHOW(RFF0_EN, rff0);
+++++ +++++++++++++ SIER_SHOW(TFE1_EN, tfe1);
+++++ +++++++++++++ SIER_SHOW(TFE0_EN, tfe0);
+++++ +++++++++++++
+++++ +++++++++++++ return 0;
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++static int fsl_ssi_stats_open(struct inode *inode, struct file *file)
+++++ +++++++++++++{
+++++ +++++++++++++ return single_open(file, fsl_ssi_stats_show, inode->i_private);
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++static const struct file_operations fsl_ssi_stats_ops = {
+++++ +++++++++++++ .open = fsl_ssi_stats_open,
+++++ +++++++++++++ .read = seq_read,
+++++ +++++++++++++ .llseek = seq_lseek,
+++++ +++++++++++++ .release = single_release,
+++++ +++++++++++++};
+++++ +++++++++++++
+++++ +++++++++++++static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
+++++ +++++++++++++ struct device *dev)
+++++ +++++++++++++{
+++++ +++++++++++++ ssi_private->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
+++++ +++++++++++++ if (!ssi_private->dbg_dir)
+++++ +++++++++++++ return -ENOMEM;
+++++ +++++++++++++
+++++ +++++++++++++ ssi_private->dbg_stats = debugfs_create_file("stats", S_IRUGO,
+++++ +++++++++++++ ssi_private->dbg_dir, ssi_private, &fsl_ssi_stats_ops);
+++++ +++++++++++++ if (!ssi_private->dbg_stats) {
+++++ +++++++++++++ debugfs_remove(ssi_private->dbg_dir);
+++++ +++++++++++++ return -ENOMEM;
+++++ +++++++++++++ }
+++++ +++++++++++++
+++++ +++++++++++++ return 0;
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
+++++ +++++++++++++{
+++++ +++++++++++++ debugfs_remove(ssi_private->dbg_stats);
+++++ +++++++++++++ debugfs_remove(ssi_private->dbg_dir);
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++#else
+++++ +++++++++++++
+++++ +++++++++++++static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
+++++ +++++++++++++ struct device *dev)
+++++ +++++++++++++{
+++++ +++++++++++++ return 0;
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
+++++ +++++++++++++{
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
+++++ +++++++++++++
+++++ +++++++++++++/*
+++++ +++++++++++++ * Enable/Disable all rx/tx config flags at once.
+++++ +++++++++++++ */
+++++ +++++++++++++static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
+++++ +++++++++++++ bool enable)
+++++ +++++++++++++{
+++++ +++++++++++++ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+++++ +++++++++++++ struct fsl_ssi_rxtx_reg_val *vals = &ssi_private->rxtx_reg_val;
+++++ +++++++++++++
+++++ +++++++++++++ if (enable) {
+++++ +++++++++++++ write_ssi_mask(&ssi->sier, 0, vals->rx.sier | vals->tx.sier);
+++++ +++++++++++++ write_ssi_mask(&ssi->srcr, 0, vals->rx.srcr | vals->tx.srcr);
+++++ +++++++++++++ write_ssi_mask(&ssi->stcr, 0, vals->rx.stcr | vals->tx.stcr);
+++++ +++++++++++++ } else {
+++++ +++++++++++++ write_ssi_mask(&ssi->srcr, vals->rx.srcr | vals->tx.srcr, 0);
+++++ +++++++++++++ write_ssi_mask(&ssi->stcr, vals->rx.stcr | vals->tx.stcr, 0);
+++++ +++++++++++++ write_ssi_mask(&ssi->sier, vals->rx.sier | vals->tx.sier, 0);
+++++ +++++++++++++ }
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++/*
+++++ +++++++++++++ * Enable/Disable a ssi configuration. You have to pass either
+++++ +++++++++++++ * ssi_private->rxtx_reg_val.rx or tx as vals parameter.
+++++ +++++++++++++ */
+++++ +++++++++++++static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
+++++ +++++++++++++ struct fsl_ssi_reg_val *vals)
+++++ +++++++++++++{
+++++ +++++++++++++ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+++++ +++++++++++++ struct fsl_ssi_reg_val *avals;
+++++ +++++++++++++ u32 scr_val = read_ssi(&ssi->scr);
+++++ +++++++++++++ int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
+++++ +++++++++++++ !!(scr_val & CCSR_SSI_SCR_RE);
+++++ +++++++++++++
+++++ +++++++++++++ /* Find the other direction values rx or tx which we do not want to
+++++ +++++++++++++ * modify */
+++++ +++++++++++++ if (&ssi_private->rxtx_reg_val.rx == vals)
+++++ +++++++++++++ avals = &ssi_private->rxtx_reg_val.tx;
+++++ +++++++++++++ else
+++++ +++++++++++++ avals = &ssi_private->rxtx_reg_val.rx;
+++++ +++++++++++++
+++++ +++++++++++++ /* If vals should be disabled, start with disabling the unit */
+++++ +++++++++++++ if (!enable) {
+++++ +++++++++++++ u32 scr = vals->scr & (vals->scr ^ avals->scr);
+++++ +++++++++++++ write_ssi_mask(&ssi->scr, scr, 0);
+++++ +++++++++++++ }
+++++ +++++++++++++
+++++ +++++++++++++ /*
+++++ +++++++++++++ * We are running on a SoC which does not support online SSI
+++++ +++++++++++++ * reconfiguration, so we have to enable all necessary flags at once
+++++ +++++++++++++ * even if we do not use them later (capture and playback configuration)
+++++ +++++++++++++ */
+++++ +++++++++++++ if (ssi_private->offline_config) {
+++++ +++++++++++++ if ((enable && !nr_active_streams) ||
+++++ +++++++++++++ (!enable && nr_active_streams == 1))
+++++ +++++++++++++ fsl_ssi_rxtx_config(ssi_private, enable);
+++++ +++++++++++++
+++++ +++++++++++++ goto config_done;
+++++ +++++++++++++ }
+++++ +++++++++++++
+++++ +++++++++++++ /*
+++++ +++++++++++++ * Configure single direction units while the SSI unit is running
+++++ +++++++++++++ * (online configuration)
+++++ +++++++++++++ */
+++++ +++++++++++++ if (enable) {
+++++ +++++++++++++ write_ssi_mask(&ssi->sier, 0, vals->sier);
+++++ +++++++++++++ write_ssi_mask(&ssi->srcr, 0, vals->srcr);
+++++ +++++++++++++ write_ssi_mask(&ssi->stcr, 0, vals->stcr);
+++++ +++++++++++++ } else {
+++++ +++++++++++++ u32 sier;
+++++ +++++++++++++ u32 srcr;
+++++ +++++++++++++ u32 stcr;
+++++ +++++++++++++
+++++ +++++++++++++ /*
+++++ +++++++++++++ * Disabling the necessary flags for one of rx/tx while the
+++++ +++++++++++++ * other stream is active is a little bit more difficult. We
+++++ +++++++++++++ * have to disable only those flags that differ between both
+++++ +++++++++++++ * streams (rx XOR tx) and that are set in the stream that is
+++++ +++++++++++++ * disabled now. Otherwise we could alter flags of the other
+++++ +++++++++++++ * stream
+++++ +++++++++++++ */
+++++ +++++++++++++
+++++ +++++++++++++ /* These assignments are simply vals without bits set in avals*/
+++++ +++++++++++++ sier = vals->sier & (vals->sier ^ avals->sier);
+++++ +++++++++++++ srcr = vals->srcr & (vals->srcr ^ avals->srcr);
+++++ +++++++++++++ stcr = vals->stcr & (vals->stcr ^ avals->stcr);
+++++ +++++++++++++
+++++ +++++++++++++ write_ssi_mask(&ssi->srcr, srcr, 0);
+++++ +++++++++++++ write_ssi_mask(&ssi->stcr, stcr, 0);
+++++ +++++++++++++ write_ssi_mask(&ssi->sier, sier, 0);
+++++ +++++++++++++ }
+++++ +++++++++++++
+++++ +++++++++++++config_done:
+++++ +++++++++++++ /* Enabling of subunits is done after configuration */
+++++ +++++++++++++ if (enable)
+++++ +++++++++++++ write_ssi_mask(&ssi->scr, 0, vals->scr);
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++
+++++ +++++++++++++static void fsl_ssi_rx_config(struct fsl_ssi_private *ssi_private, bool enable)
+++++ +++++++++++++{
+++++ +++++++++++++ fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.rx);
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++static void fsl_ssi_tx_config(struct fsl_ssi_private *ssi_private, bool enable)
+++++ +++++++++++++{
+++++ +++++++++++++ fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.tx);
+++++ +++++++++++++}
+++++ +++++++++++++
+++++ +++++++++++++/*
+++++ +++++++++++++ * Setup rx/tx register values used to enable/disable the streams. These will
+++++ +++++++++++++ * be used later in fsl_ssi_config to setup the streams without the need to
+++++ +++++++++++++ * check for all different SSI modes.
+++++ +++++++++++++ */
+++++ +++++++++++++static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private)
+++++ +++++++++++++{
+++++ +++++++++++++ struct fsl_ssi_rxtx_reg_val *reg = &ssi_private->rxtx_reg_val;
+++++ +++++++++++++
+++++ +++++++++++++ reg->rx.sier = CCSR_SSI_SIER_RFF0_EN;
+++++ +++++++++++++ reg->rx.srcr = CCSR_SSI_SRCR_RFEN0;
+++++ +++++++++++++ reg->rx.scr = 0;
+++++ +++++++++++++ reg->tx.sier = CCSR_SSI_SIER_TFE0_EN;
+++++ +++++++++++++ reg->tx.stcr = CCSR_SSI_STCR_TFEN0;
+++++ +++++++++++++ reg->tx.scr = 0;
+++++ +++++++++++++
+++++ +++++++++++++ if (!ssi_private->imx_ac97) {
+++++ +++++++++++++ reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE;
+++++ +++++++++++++ reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN;
+++++ +++++++++++++ reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE;
+++++ +++++++++++++ reg->tx.sier |= CCSR_SSI_SIER_TFE0_EN;
+++++ +++++++++++++ }
+++++ +++++++++++++
+++++ +++++++++++++ if (ssi_private->use_dma) {
+++++ +++++++++++++ reg->rx.sier |= CCSR_SSI_SIER_RDMAE;
+++++ +++++++++++++ reg->tx.sier |= CCSR_SSI_SIER_TDMAE;
+++++ +++++++++++++ } else {
+++++ +++++++++++++ reg->rx.sier |= CCSR_SSI_SIER_RIE;
+++++ +++++++++++++ reg->tx.sier |= CCSR_SSI_SIER_TIE;
+++++ +++++++++++++ }
+++++ +++++++++++++
+++++ +++++++++++++ reg->rx.sier |= FSLSSI_SIER_DBG_RX_FLAGS;
+++++ +++++++++++++ reg->tx.sier |= FSLSSI_SIER_DBG_TX_FLAGS;
+++++ +++++++++++++}
+++++ +++++++++++++
++++ +++++++++++++static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
++++ +++++++++++++{
++++ +++++++++++++ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
++++ +++++++++++++
++++ +++++++++++++ /*
++++ +++++++++++++ * Setup the clock control register
++++ +++++++++++++ */
++++ +++++++++++++ write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
++++ +++++++++++++ &ssi->stccr);
++++ +++++++++++++ write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
++++ +++++++++++++ &ssi->srccr);
++++ +++++++++++++
++++ +++++++++++++ /*
++++ +++++++++++++ * Enable AC97 mode and startup the SSI
++++ +++++++++++++ */
++++ +++++++++++++ write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV,
++++ +++++++++++++ &ssi->sacnt);
++++ +++++++++++++ write_ssi(0xff, &ssi->saccdis);
++++ +++++++++++++ write_ssi(0x300, &ssi->saccen);
++++ +++++++++++++
++++ +++++++++++++ /*
++++ +++++++++++++ * Enable SSI, Transmit and Receive. AC97 has to communicate with the
++++ +++++++++++++ * codec before a stream is started.
++++ +++++++++++++ */
++++ +++++++++++++ write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN |
++++ +++++++++++++ CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
++++ +++++++++++++
++++ +++++++++++++ write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
++++ +++++++++++++}
++++ +++++++++++++
static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
{
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
u8 wm;
int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
+++++ +++++++++++++ fsl_ssi_setup_reg_vals(ssi_private);
+++++ +++++++++++++
if (ssi_private->imx_ac97)
---- ------------- i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
++++ +++++++++++++ ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
else
---- ------------- i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
++++ +++++++++++++ ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
/*
* Section 16.5 of the MPC8610 reference manual says that the SSI needs
write_ssi_mask(&ssi->scr,
CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
CCSR_SSI_SCR_TFR_CLK_DIS |
---- ------------- i2s_mode |
++++ +++++++++++++ ssi_private->i2s_mode |
(synchronous ? CCSR_SSI_SCR_SYN : 0));
----- ------------- write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
----- ------------- CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
----- ------------- CCSR_SSI_STCR_TSCKP, &ssi->stcr);
+++++ +++++++++++++ write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFSI |
+++++ +++++++++++++ CCSR_SSI_STCR_TEFS | CCSR_SSI_STCR_TSCKP, &ssi->stcr);
+++++ +++++++++++++
+++++ +++++++++++++ write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFSI |
+++++ +++++++++++++ CCSR_SSI_SRCR_REFS | CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
----- ------------- write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
----- ------------- CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
----- ------------- CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
/*
* The DC and PM bits are only used if the SSI is the clock master.
*/
* because it is also running without an active substream. Normally SSI
* is only enabled when there is a substream.
*/
---- ------------- if (ssi_private->imx_ac97) {
---- ------------- /*
---- ------------- * Setup the clock control register
---- ------------- */
---- ------------- write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
---- ------------- &ssi->stccr);
---- ------------- write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
---- ------------- &ssi->srccr);
---- -------------
---- ------------- /*
---- ------------- * Enable AC97 mode and startup the SSI
---- ------------- */
---- ------------- write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV,
---- ------------- &ssi->sacnt);
---- ------------- write_ssi(0xff, &ssi->saccdis);
---- ------------- write_ssi(0x300, &ssi->saccen);
---- -------------
---- ------------- /*
---- ------------- * Enable SSI, Transmit and Receive
---- ------------- */
---- ------------- write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN |
---- ------------- CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
++++ +++++++++++++ if (ssi_private->imx_ac97)
++++ +++++++++++++ fsl_ssi_setup_ac97(ssi_private);
---- ------------- write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
+++++ +++++++++++++ /*
+++++ +++++++++++++ * Set a default slot number so that there is no need for those common
+++++ +++++++++++++ * cases like I2S mode to call the extra set_tdm_slot() any more.
+++++ +++++++++++++ */
+++++ +++++++++++++ if (!ssi_private->imx_ac97) {
+++++ +++++++++++++ write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK,
+++++ +++++++++++++ CCSR_SSI_SxCCR_DC(2));
+++++ +++++++++++++ write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK,
+++++ +++++++++++++ CCSR_SSI_SxCCR_DC(2));
+ }
+
return 0;
}
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
----- ------------- unsigned int sier_bits;
---- -------------
---- ------------- /*
---- ------------- * Enable only the interrupts and DMA requests
---- ------------- * that are needed for the channel. As the fiq
---- ------------- * is polling for this bits, we have to ensure
---- ------------- * that this are aligned with the preallocated
---- ------------- * buffers
---- ------------- */
---- -------------
---- ------------- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
---- ------------- if (ssi_private->use_dma)
---- ------------- sier_bits = SIER_FLAGS;
---- ------------- else
---- ------------- sier_bits = CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN;
---- ------------- } else {
---- ------------- if (ssi_private->use_dma)
---- ------------- sier_bits = SIER_FLAGS;
---- ------------- else
---- ------------- sier_bits = CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_RFF0_EN;
---- ------------- }
++++ +++++++++++++ unsigned long flags;
- /*
- * Enable only the interrupts and DMA requests
- * that are needed for the channel. As the fiq
- * is polling for this bits, we have to ensure
- * that this are aligned with the preallocated
- * buffers
- */
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (ssi_private->use_dma)
- sier_bits = SIER_FLAGS;
- else
- sier_bits = CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN;
- } else {
- if (ssi_private->use_dma)
- sier_bits = SIER_FLAGS;
- else
- sier_bits = CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_RFF0_EN;
- }
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
----- ------------- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
+++++ +++++++++++++ fsl_ssi_tx_config(ssi_private, false);
else
----- ------------- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
+++++ +++++++++++++ fsl_ssi_rx_config(ssi_private, false);
if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
---- ------------- (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0)
---- ------------- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
++++ +++++++++++++ (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) {
- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
++++ +++++++++++++ spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
++++ +++++++++++++ ssi_private->baudclk_locked = false;
++++ +++++++++++++ spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
++++ +++++++++++++ }
break;
default:
.name = "fsl-ssi",
};
----- -------------/**
----- ------------- * fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit.
----- ------------- *
----- ------------- * This function is called by ALSA to start, stop, pause, and resume the
----- ------------- * transfer of data.
----- ------------- */
----- -------------static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
----- ------------- struct snd_soc_dai *dai)
----- -------------{
----- ------------- struct snd_soc_pcm_runtime *rtd = substream->private_data;
----- ------------- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(
----- ------------- rtd->cpu_dai);
----- ------------- struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
----- -------------
----- ------------- switch (cmd) {
----- ------------- case SNDRV_PCM_TRIGGER_START:
----- ------------- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
----- ------------- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
----- ------------- write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE |
----- ------------- CCSR_SSI_SIER_TFE0_EN);
----- ------------- else
----- ------------- write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE |
----- ------------- CCSR_SSI_SIER_RFF0_EN);
----- ------------- break;
----- -------------
----- ------------- case SNDRV_PCM_TRIGGER_STOP:
----- ------------- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
----- ------------- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
----- ------------- write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE |
----- ------------- CCSR_SSI_SIER_TFE0_EN, 0);
----- ------------- else
----- ------------- write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE |
----- ------------- CCSR_SSI_SIER_RFF0_EN, 0);
----- ------------- break;
----- -------------
----- ------------- default:
----- ------------- return -EINVAL;
----- ------------- }
----- -------------
----- ------------- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
----- ------------- write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
----- ------------- else
----- ------------- write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor);
----- -------------
----- ------------- return 0;
----- -------------}
----- -------------
----- -------------static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = {
----- ------------- .startup = fsl_ssi_startup,
---- ------------- .shutdown = fsl_ssi_shutdown,
----- ------------- .trigger = fsl_ssi_ac97_trigger,
----- -------------};
----- -------------
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
.ac97_control = 1,
.playback = {
/* Older 8610 DTs didn't have the fifo-depth property */
ssi_private->fifo_depth = 8;
---- ------------- if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
++++ +++++++++++++ ssi_private->baudclk_locked = false;
++++ +++++++++++++ spin_lock_init(&ssi_private->baudclk_lock);
++++ +++++++++++++
- if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
+++++ +++++++++++++ /*
+++++ +++++++++++++ * imx51 and later SoCs have a slightly different IP that allows the
+++++ +++++++++++++ * SSI configuration while the SSI unit is running.
+++++ +++++++++++++ *
+++++ +++++++++++++ * More important, it is necessary on those SoCs to configure the
+++++ +++++++++++++ * sperate TX/RX DMA bits just before starting the stream
+++++ +++++++++++++ * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi
+++++ +++++++++++++ * sends any DMA requests to the SDMA unit, otherwise it is not defined
+++++ +++++++++++++ * how the SDMA unit handles the DMA request.
+++++ +++++++++++++ *
+++++ +++++++++++++ * SDMA units are present on devices starting at imx35 but the imx35
+++++ +++++++++++++ * reference manual states that the DMA bits should not be changed
+++++ +++++++++++++ * while the SSI unit is running (SSIEN). So we support the necessary
+++++ +++++++++++++ * online configuration of fsl-ssi starting at imx51.
+++++ +++++++++++++ */
+++++ +++++++++++++ switch (hw_type) {
+++++ +++++++++++++ case FSL_SSI_MCP8610:
+++++ +++++++++++++ case FSL_SSI_MX21:
+++++ +++++++++++++ case FSL_SSI_MX35:
+++++ +++++++++++++ ssi_private->offline_config = true;
+++++ +++++++++++++ break;
+++++ +++++++++++++ case FSL_SSI_MX51:
+++++ +++++++++++++ ssi_private->offline_config = false;
+++++ +++++++++++++ break;
+++++ +++++++++++++ }
+++++ +++++++++++++
+++++ +++++++++++++ if (hw_type == FSL_SSI_MX21 || hw_type == FSL_SSI_MX51 ||
+++++ +++++++++++++ hw_type == FSL_SSI_MX35) {
u32 dma_events[2];
ssi_private->ssi_on_imx = true;
goto error_irqmap;
}
- dev_warn(&pdev->dev, "could not get baud clock: %d\n", ret);
++++ +++++++++++++ /* For those SLAVE implementations, we ingore non-baudclk cases
++++ +++++++++++++ * and, instead, abandon MASTER mode that needs baud clock.
++++ +++++++++++++ */
++++ +++++++++++++ ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
++++ +++++++++++++ if (IS_ERR(ssi_private->baudclk))
+++++ +++++++++++++ dev_warn(&pdev->dev, "could not get baud clock: %ld\n",
+++++ +++++++++++++ PTR_ERR(ssi_private->baudclk));
++++ +++++++++++++ else
++++ +++++++++++++ clk_prepare_enable(ssi_private->baudclk);
++++ +++++++++++++
/*
* We have burstsize be "fifo_depth - 2" to match the SSI
* watermark setting in fsl_ssi_startup().
return 0;
error_dai:
---- ------------- if (ssi_private->ssi_on_imx)
---- ------------- imx_pcm_dma_exit(pdev);
+++++ +++++++++++++ if (ssi_private->ssi_on_imx && !ssi_private->use_dma)
+++++ +++++++++++++ imx_pcm_fiq_exit(pdev);
+++++ +++++++++++++
+++++ +++++++++++++error_pcm:
+++++ +++++++++++++ fsl_ssi_debugfs_remove(ssi_private);
+++++ +++++++++++++
+++++ +++++++++++++error_dbgfs:
snd_soc_unregister_component(&pdev->dev);
error_dev:
device_remove_file(&pdev->dev, dev_attr);
error_clk:
---- ------------- if (ssi_private->ssi_on_imx)
++++ +++++++++++++ if (ssi_private->ssi_on_imx) {
++++ +++++++++++++ if (!IS_ERR(ssi_private->baudclk))
++++ +++++++++++++ clk_disable_unprepare(ssi_private->baudclk);
clk_disable_unprepare(ssi_private->clk);
++++ +++++++++++++ }
error_irqmap:
----- ------------- irq_dispose_mapping(ssi_private->irq);
+++++ +++++++++++++ if (ssi_private->irq_stats)
+++++ +++++++++++++ irq_dispose_mapping(ssi_private->irq);
return ret;
}
{
struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
+++++ +++++++++++++ fsl_ssi_debugfs_remove(ssi_private);
+++++ +++++++++++++
if (!ssi_private->new_binding)
platform_device_unregister(ssi_private->pdev);
---- ------------- if (ssi_private->ssi_on_imx)
---- ------------- imx_pcm_dma_exit(pdev);
snd_soc_unregister_component(&pdev->dev);
----- ------------- device_remove_file(&pdev->dev, &ssi_private->dev_attr);
---- ------------- if (ssi_private->ssi_on_imx)
++++ +++++++++++++ if (ssi_private->ssi_on_imx) {
++++ +++++++++++++ if (!IS_ERR(ssi_private->baudclk))
++++ +++++++++++++ clk_disable_unprepare(ssi_private->baudclk);
clk_disable_unprepare(ssi_private->clk);
---- ------------- irq_dispose_mapping(ssi_private->irq);
++++ +++++++++++++ }
- irq_dispose_mapping(ssi_private->irq);
+++++ +++++++++++++ if (ssi_private->irq_stats)
+++++ +++++++++++++ irq_dispose_mapping(ssi_private->irq);
return 0;
}
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
----- ------------
----- ------------#include <linux/platform_device.h>
+++++ ++++++++++++#include <linux/clk.h>
+ #include <linux/module.h>
+++++ ++++++++++++#include <linux/of.h>
+++++ ++++++++++++#include <linux/platform_device.h>
- #include <linux/module.h>
++++++ ++++++++++++#include <linux/string.h>
#include <sound/simple_card.h>
------ ------------#define asoc_simple_get_card_info(p) \
------ ------------ container_of(p->dai_link, struct asoc_simple_card_info, snd_link)
------ ------------
static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
struct asoc_simple_dai *set,
unsigned int daifmt)
return 0;
}
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- goto parse_error;
- }
-
- dai->sysclk = clk_get_rate(clk);
+++++ ++++++++++++static int
+++++ ++++++++++++asoc_simple_card_sub_parse_of(struct device_node *np,
+++++ ++++++++++++ struct asoc_simple_dai *dai,
+++++ ++++++++++++ struct device_node **node)
+++++ ++++++++++++{
+++++ ++++++++++++ struct clk *clk;
+++++ ++++++++++++ int ret;
+++++ ++++++++++++
+++++ ++++++++++++ /*
+++++ ++++++++++++ * get node via "sound-dai = <&phandle port>"
+++++ ++++++++++++ * it will be used as xxx_of_node on soc_bind_dai_link()
+++++ ++++++++++++ */
+++++ ++++++++++++ *node = of_parse_phandle(np, "sound-dai", 0);
+++++ ++++++++++++ if (!*node)
+++++ ++++++++++++ return -ENODEV;
+++++ ++++++++++++
+++++ ++++++++++++ /* get dai->name */
+++++ ++++++++++++ ret = snd_soc_of_get_dai_name(np, &dai->name);
+++++ ++++++++++++ if (ret < 0)
+++++ ++++++++++++ goto parse_error;
+++++ ++++++++++++
+++++ ++++++++++++ /*
+++++ ++++++++++++ * bitclock-inversion, frame-inversion
+++++ ++++++++++++ * bitclock-master, frame-master
+++++ ++++++++++++ * and specific "format" if it has
+++++ ++++++++++++ */
+++++ ++++++++++++ dai->fmt = snd_soc_of_parse_daifmt(np, NULL);
+++++ ++++++++++++
+++++ ++++++++++++ /*
+++++ ++++++++++++ * dai->sysclk come from
+++++ ++++++++++++ * "clocks = <&xxx>" (if system has common clock)
+++++ ++++++++++++ * or "system-clock-frequency = <xxx>"
+++++ ++++++++++++ * or device's module clock.
+++++ ++++++++++++ */
+++++ ++++++++++++ if (of_property_read_bool(np, "clocks")) {
+++++ ++++++++++++ clk = of_clk_get(np, 0);
+++++ ++++++++++++ if (IS_ERR(clk)) {
+++++ ++++++++++++ ret = PTR_ERR(clk);
+++++ ++++++++++++ goto parse_error;
+++++ ++++++++++++ }
+++++ ++++++++++++
+++++ ++++++++++++ dai->sysclk = clk_get_rate(clk);
+++++ ++++++++++++ } else if (of_property_read_bool(np, "system-clock-frequency")) {
+++++ ++++++++++++ of_property_read_u32(np,
+++++ ++++++++++++ "system-clock-frequency",
+++++ ++++++++++++ &dai->sysclk);
+++++ ++++++++++++ } else {
+++++ ++++++++++++ clk = of_clk_get(*node, 0);
- ret = snd_soc_of_parse_audio_routing(&info->snd_card,
- "simple-audio-routing");
- if (ret)
- return ret;
++++++ ++++++++++++ if (!IS_ERR(clk))
++++++ ++++++++++++ dai->sysclk = clk_get_rate(clk);
+++++ ++++++++++++ }
+++++ ++++++++++++
+++++ ++++++++++++ ret = 0;
+++++ ++++++++++++
+++++ ++++++++++++parse_error:
+++++ ++++++++++++ of_node_put(*node);
+++++ ++++++++++++
+++++ ++++++++++++ return ret;
+++++ ++++++++++++}
+++++ ++++++++++++
+++++ ++++++++++++static int asoc_simple_card_parse_of(struct device_node *node,
+++++ ++++++++++++ struct asoc_simple_card_info *info,
+++++ ++++++++++++ struct device *dev,
+++++ ++++++++++++ struct device_node **of_cpu,
+++++ ++++++++++++ struct device_node **of_codec,
+++++ ++++++++++++ struct device_node **of_platform)
+++++ ++++++++++++{
+++++ ++++++++++++ struct device_node *np;
+++++ ++++++++++++ char *name;
+++++ ++++++++++++ int ret;
+++++ ++++++++++++
+++++ ++++++++++++ /* get CPU/CODEC common format via simple-audio-card,format */
+++++ ++++++++++++ info->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") &
+++++ ++++++++++++ (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK);
+++++ ++++++++++++
+++++ ++++++++++++ /* DAPM routes */
++++++ ++++++++++++ if (of_property_read_bool(node, "simple-audio-card,routing")) {
++++++ ++++++++++++ ret = snd_soc_of_parse_audio_routing(&info->snd_card,
++++++ ++++++++++++ "simple-audio-card,routing");
++++++ ++++++++++++ if (ret)
++++++ ++++++++++++ return ret;
++++++ ++++++++++++ }
+++++ ++++++++++++
+++++ ++++++++++++ /* CPU sub-node */
+++++ ++++++++++++ ret = -EINVAL;
+++++ ++++++++++++ np = of_get_child_by_name(node, "simple-audio-card,cpu");
+++++ ++++++++++++ if (np)
+++++ ++++++++++++ ret = asoc_simple_card_sub_parse_of(np,
+++++ ++++++++++++ &info->cpu_dai,
+++++ ++++++++++++ of_cpu);
+++++ ++++++++++++ if (ret < 0)
+++++ ++++++++++++ return ret;
+++++ ++++++++++++
+++++ ++++++++++++ /* CODEC sub-node */
+++++ ++++++++++++ ret = -EINVAL;
+++++ ++++++++++++ np = of_get_child_by_name(node, "simple-audio-card,codec");
+++++ ++++++++++++ if (np)
+++++ ++++++++++++ ret = asoc_simple_card_sub_parse_of(np,
+++++ ++++++++++++ &info->codec_dai,
+++++ ++++++++++++ of_codec);
+++++ ++++++++++++ if (ret < 0)
+++++ ++++++++++++ return ret;
+++++ ++++++++++++
++++++++++++++++++ if (!info->cpu_dai.name || !info->codec_dai.name)
++++++++++++++++++ return -EINVAL;
++++++++++++++++++
+++++ ++++++++++++ /* card name is created from CPU/CODEC dai name */
+++++ ++++++++++++ name = devm_kzalloc(dev,
+++++ ++++++++++++ strlen(info->cpu_dai.name) +
+++++ ++++++++++++ strlen(info->codec_dai.name) + 2,
+++++ ++++++++++++ GFP_KERNEL);
+++++ ++++++++++++ sprintf(name, "%s-%s", info->cpu_dai.name, info->codec_dai.name);
+++++ ++++++++++++ info->name = info->card = name;
+++++ ++++++++++++
+++++ ++++++++++++ /* simple-card assumes platform == cpu */
+++++ ++++++++++++ *of_platform = *of_cpu;
+++++ ++++++++++++
+++++ ++++++++++++ dev_dbg(dev, "card-name : %s\n", info->card);
+++++ ++++++++++++ dev_dbg(dev, "platform : %04x\n", info->daifmt);
+++++ ++++++++++++ dev_dbg(dev, "cpu : %s / %04x / %d\n",
+++++ ++++++++++++ info->cpu_dai.name,
+++++ ++++++++++++ info->cpu_dai.fmt,
+++++ ++++++++++++ info->cpu_dai.sysclk);
+++++ ++++++++++++ dev_dbg(dev, "codec : %s / %04x / %d\n",
+++++ ++++++++++++ info->codec_dai.name,
+++++ ++++++++++++ info->codec_dai.fmt,
+++++ ++++++++++++ info->codec_dai.sysclk);
+++++ ++++++++++++
+++++ ++++++++++++ return 0;
+++++ ++++++++++++}
+++++ ++++++++++++
static int asoc_simple_card_probe(struct platform_device *pdev)
{
----- ------------ struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
+++++ ++++++++++++ struct asoc_simple_card_info *cinfo;
+++++ ++++++++++++ struct device_node *np = pdev->dev.of_node;
+++++ ++++++++++++ struct device_node *of_cpu, *of_codec, *of_platform;
struct device *dev = &pdev->dev;
++++++ ++++++++++++ int ret;
----- ------------ if (!cinfo) {
----- ------------ dev_err(dev, "no info for asoc-simple-card\n");
----- ------------ return -EINVAL;
+++++ ++++++++++++ cinfo = NULL;
+++++ ++++++++++++ of_cpu = NULL;
+++++ ++++++++++++ of_codec = NULL;
+++++ ++++++++++++ of_platform = NULL;
++++++ ++++++++++++
++++++ ++++++++++++ cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL);
++++++ ++++++++++++ if (!cinfo)
++++++ ++++++++++++ return -ENOMEM;
++++++ ++++++++++++
+++++ ++++++++++++ if (np && of_device_is_available(np)) {
- cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL);
- if (cinfo) {
- int ret;
- cinfo->snd_card.dev = &pdev->dev;
- ret = asoc_simple_card_parse_of(np, cinfo, dev,
- &of_cpu,
- &of_codec,
- &of_platform);
- if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "parse error %d\n", ret);
- return ret;
- }
++++++ ++++++++++++ cinfo->snd_card.dev = dev;
++++++ ++++++++++++
++++++ ++++++++++++ ret = asoc_simple_card_parse_of(np, cinfo, dev,
++++++ ++++++++++++ &of_cpu,
++++++ ++++++++++++ &of_codec,
++++++ ++++++++++++ &of_platform);
++++++ ++++++++++++ if (ret < 0) {
++++++ ++++++++++++ if (ret != -EPROBE_DEFER)
++++++ ++++++++++++ dev_err(dev, "parse error %d\n", ret);
++++++ ++++++++++++ return ret;
+++++ ++++++++++++ }
+++++ ++++++++++++ } else {
- cinfo->snd_card.dev = &pdev->dev;
- cinfo = pdev->dev.platform_data;
- }
++++++ ++++++++++++ if (!dev->platform_data) {
++++++ ++++++++++++ dev_err(dev, "no info for asoc-simple-card\n");
++++++ ++++++++++++ return -EINVAL;
++++++ ++++++++++++ }
+++++ ++++++++++++
- if (!cinfo) {
- dev_err(dev, "no info for asoc-simple-card\n");
- return -EINVAL;
++++++ ++++++++++++ memcpy(cinfo, dev->platform_data, sizeof(*cinfo));
++++++ ++++++++++++ cinfo->snd_card.dev = dev;
}
if (!cinfo->name ||
cinfo->snd_card.owner = THIS_MODULE;
cinfo->snd_card.dai_link = &cinfo->snd_link;
cinfo->snd_card.num_links = 1;
----- ------------ cinfo->snd_card.dev = &pdev->dev;
----- ------------ return snd_soc_register_card(&cinfo->snd_card);
----- ------------}
++++++ ++++++++++++ snd_soc_card_set_drvdata(&cinfo->snd_card, cinfo);
+
----- ------------static int asoc_simple_card_remove(struct platform_device *pdev)
----- ------------{
----- ------------ struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
----- ------------
----- ------------ return snd_soc_unregister_card(&cinfo->snd_card);
+++++ ++++++++++++ return devm_snd_soc_register_card(&pdev->dev, &cinfo->snd_card);
}
+++++ ++++++++++++static const struct of_device_id asoc_simple_of_match[] = {
+++++ ++++++++++++ { .compatible = "simple-audio-card", },
+++++ ++++++++++++ {},
+++++ ++++++++++++};
+++++ ++++++++++++MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
+++++ ++++++++++++
static struct platform_driver asoc_simple_card = {
.driver = {
.name = "asoc-simple-card",