162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2017 NXP 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of_platform.h> 1162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1262306a36Sopenharmony_ci#include <sound/soc.h> 1362306a36Sopenharmony_ci#include <sound/pcm_params.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "fsl_audmix.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define SOC_ENUM_SINGLE_S(xreg, xshift, xtexts) \ 1862306a36Sopenharmony_ci SOC_ENUM_SINGLE(xreg, xshift, ARRAY_SIZE(xtexts), xtexts) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic const char 2162306a36Sopenharmony_ci *tdm_sel[] = { "TDM1", "TDM2", }, 2262306a36Sopenharmony_ci *mode_sel[] = { "Disabled", "TDM1", "TDM2", "Mixed", }, 2362306a36Sopenharmony_ci *width_sel[] = { "16b", "18b", "20b", "24b", "32b", }, 2462306a36Sopenharmony_ci *endis_sel[] = { "Disabled", "Enabled", }, 2562306a36Sopenharmony_ci *updn_sel[] = { "Downward", "Upward", }, 2662306a36Sopenharmony_ci *mask_sel[] = { "Unmask", "Mask", }; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const struct soc_enum fsl_audmix_enum[] = { 2962306a36Sopenharmony_ci/* FSL_AUDMIX_CTR enums */ 3062306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MIXCLK_SHIFT, tdm_sel), 3162306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTSRC_SHIFT, mode_sel), 3262306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTWIDTH_SHIFT, width_sel), 3362306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKRTDF_SHIFT, mask_sel), 3462306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKCKDF_SHIFT, mask_sel), 3562306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCMODE_SHIFT, endis_sel), 3662306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCSRC_SHIFT, tdm_sel), 3762306a36Sopenharmony_ci/* FSL_AUDMIX_ATCR0 enums */ 3862306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 0, endis_sel), 3962306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 1, updn_sel), 4062306a36Sopenharmony_ci/* FSL_AUDMIX_ATCR1 enums */ 4162306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 0, endis_sel), 4262306a36Sopenharmony_ciSOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 1, updn_sel), 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct fsl_audmix_state { 4662306a36Sopenharmony_ci u8 tdms; 4762306a36Sopenharmony_ci u8 clk; 4862306a36Sopenharmony_ci char msg[64]; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic const struct fsl_audmix_state prms[4][4] = {{ 5262306a36Sopenharmony_ci /* DIS->DIS, do nothing */ 5362306a36Sopenharmony_ci { .tdms = 0, .clk = 0, .msg = "" }, 5462306a36Sopenharmony_ci /* DIS->TDM1*/ 5562306a36Sopenharmony_ci { .tdms = 1, .clk = 1, .msg = "DIS->TDM1: TDM1 not started!\n" }, 5662306a36Sopenharmony_ci /* DIS->TDM2*/ 5762306a36Sopenharmony_ci { .tdms = 2, .clk = 2, .msg = "DIS->TDM2: TDM2 not started!\n" }, 5862306a36Sopenharmony_ci /* DIS->MIX */ 5962306a36Sopenharmony_ci { .tdms = 3, .clk = 0, .msg = "DIS->MIX: Please start both TDMs!\n" } 6062306a36Sopenharmony_ci}, { /* TDM1->DIS */ 6162306a36Sopenharmony_ci { .tdms = 1, .clk = 0, .msg = "TDM1->DIS: TDM1 not started!\n" }, 6262306a36Sopenharmony_ci /* TDM1->TDM1, do nothing */ 6362306a36Sopenharmony_ci { .tdms = 0, .clk = 0, .msg = "" }, 6462306a36Sopenharmony_ci /* TDM1->TDM2 */ 6562306a36Sopenharmony_ci { .tdms = 3, .clk = 2, .msg = "TDM1->TDM2: Please start both TDMs!\n" }, 6662306a36Sopenharmony_ci /* TDM1->MIX */ 6762306a36Sopenharmony_ci { .tdms = 3, .clk = 0, .msg = "TDM1->MIX: Please start both TDMs!\n" } 6862306a36Sopenharmony_ci}, { /* TDM2->DIS */ 6962306a36Sopenharmony_ci { .tdms = 2, .clk = 0, .msg = "TDM2->DIS: TDM2 not started!\n" }, 7062306a36Sopenharmony_ci /* TDM2->TDM1 */ 7162306a36Sopenharmony_ci { .tdms = 3, .clk = 1, .msg = "TDM2->TDM1: Please start both TDMs!\n" }, 7262306a36Sopenharmony_ci /* TDM2->TDM2, do nothing */ 7362306a36Sopenharmony_ci { .tdms = 0, .clk = 0, .msg = "" }, 7462306a36Sopenharmony_ci /* TDM2->MIX */ 7562306a36Sopenharmony_ci { .tdms = 3, .clk = 0, .msg = "TDM2->MIX: Please start both TDMs!\n" } 7662306a36Sopenharmony_ci}, { /* MIX->DIS */ 7762306a36Sopenharmony_ci { .tdms = 3, .clk = 0, .msg = "MIX->DIS: Please start both TDMs!\n" }, 7862306a36Sopenharmony_ci /* MIX->TDM1 */ 7962306a36Sopenharmony_ci { .tdms = 3, .clk = 1, .msg = "MIX->TDM1: Please start both TDMs!\n" }, 8062306a36Sopenharmony_ci /* MIX->TDM2 */ 8162306a36Sopenharmony_ci { .tdms = 3, .clk = 2, .msg = "MIX->TDM2: Please start both TDMs!\n" }, 8262306a36Sopenharmony_ci /* MIX->MIX, do nothing */ 8362306a36Sopenharmony_ci { .tdms = 0, .clk = 0, .msg = "" } 8462306a36Sopenharmony_ci}, }; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int fsl_audmix_state_trans(struct snd_soc_component *comp, 8762306a36Sopenharmony_ci unsigned int *mask, unsigned int *ctr, 8862306a36Sopenharmony_ci const struct fsl_audmix_state prm) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp); 9162306a36Sopenharmony_ci /* Enforce all required TDMs are started */ 9262306a36Sopenharmony_ci if ((priv->tdms & prm.tdms) != prm.tdms) { 9362306a36Sopenharmony_ci dev_dbg(comp->dev, "%s", prm.msg); 9462306a36Sopenharmony_ci return -EINVAL; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci switch (prm.clk) { 9862306a36Sopenharmony_ci case 1: 9962306a36Sopenharmony_ci case 2: 10062306a36Sopenharmony_ci /* Set mix clock */ 10162306a36Sopenharmony_ci (*mask) |= FSL_AUDMIX_CTR_MIXCLK_MASK; 10262306a36Sopenharmony_ci (*ctr) |= FSL_AUDMIX_CTR_MIXCLK(prm.clk - 1); 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci default: 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol, 11262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 11562306a36Sopenharmony_ci struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp); 11662306a36Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 11762306a36Sopenharmony_ci unsigned int *item = ucontrol->value.enumerated.item; 11862306a36Sopenharmony_ci unsigned int reg_val, val, mix_clk; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Get current state */ 12162306a36Sopenharmony_ci reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR); 12262306a36Sopenharmony_ci mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK) 12362306a36Sopenharmony_ci >> FSL_AUDMIX_CTR_MIXCLK_SHIFT); 12462306a36Sopenharmony_ci val = snd_soc_enum_item_to_val(e, item[0]); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /** 12962306a36Sopenharmony_ci * Ensure the current selected mixer clock is available 13062306a36Sopenharmony_ci * for configuration propagation 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci if (!(priv->tdms & BIT(mix_clk))) { 13362306a36Sopenharmony_ci dev_err(comp->dev, 13462306a36Sopenharmony_ci "Started TDM%d needed for config propagation!\n", 13562306a36Sopenharmony_ci mix_clk + 1); 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (!(priv->tdms & BIT(val))) { 14062306a36Sopenharmony_ci dev_err(comp->dev, 14162306a36Sopenharmony_ci "The selected clock source has no TDM%d enabled!\n", 14262306a36Sopenharmony_ci val + 1); 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return snd_soc_put_enum_double(kcontrol, ucontrol); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol, 15062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 15362306a36Sopenharmony_ci struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp); 15462306a36Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 15562306a36Sopenharmony_ci unsigned int *item = ucontrol->value.enumerated.item; 15662306a36Sopenharmony_ci u32 out_src, mix_clk; 15762306a36Sopenharmony_ci unsigned int reg_val, val, mask = 0, ctr = 0; 15862306a36Sopenharmony_ci int ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Get current state */ 16162306a36Sopenharmony_ci reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* "From" state */ 16462306a36Sopenharmony_ci out_src = ((reg_val & FSL_AUDMIX_CTR_OUTSRC_MASK) 16562306a36Sopenharmony_ci >> FSL_AUDMIX_CTR_OUTSRC_SHIFT); 16662306a36Sopenharmony_ci mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK) 16762306a36Sopenharmony_ci >> FSL_AUDMIX_CTR_MIXCLK_SHIFT); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* "To" state */ 17062306a36Sopenharmony_ci val = snd_soc_enum_item_to_val(e, item[0]); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Check if state is changing ... */ 17562306a36Sopenharmony_ci if (out_src == val) 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci /** 17862306a36Sopenharmony_ci * Ensure the current selected mixer clock is available 17962306a36Sopenharmony_ci * for configuration propagation 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci if (!(priv->tdms & BIT(mix_clk))) { 18262306a36Sopenharmony_ci dev_err(comp->dev, 18362306a36Sopenharmony_ci "Started TDM%d needed for config propagation!\n", 18462306a36Sopenharmony_ci mix_clk + 1); 18562306a36Sopenharmony_ci return -EINVAL; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Check state transition constraints */ 18962306a36Sopenharmony_ci ret = fsl_audmix_state_trans(comp, &mask, &ctr, prms[out_src][val]); 19062306a36Sopenharmony_ci if (ret) 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Complete transition to new state */ 19462306a36Sopenharmony_ci mask |= FSL_AUDMIX_CTR_OUTSRC_MASK; 19562306a36Sopenharmony_ci ctr |= FSL_AUDMIX_CTR_OUTSRC(val); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic const struct snd_kcontrol_new fsl_audmix_snd_controls[] = { 20162306a36Sopenharmony_ci /* FSL_AUDMIX_CTR controls */ 20262306a36Sopenharmony_ci SOC_ENUM_EXT("Mixing Clock Source", fsl_audmix_enum[0], 20362306a36Sopenharmony_ci snd_soc_get_enum_double, fsl_audmix_put_mix_clk_src), 20462306a36Sopenharmony_ci SOC_ENUM_EXT("Output Source", fsl_audmix_enum[1], 20562306a36Sopenharmony_ci snd_soc_get_enum_double, fsl_audmix_put_out_src), 20662306a36Sopenharmony_ci SOC_ENUM("Output Width", fsl_audmix_enum[2]), 20762306a36Sopenharmony_ci SOC_ENUM("Frame Rate Diff Error", fsl_audmix_enum[3]), 20862306a36Sopenharmony_ci SOC_ENUM("Clock Freq Diff Error", fsl_audmix_enum[4]), 20962306a36Sopenharmony_ci SOC_ENUM("Sync Mode Config", fsl_audmix_enum[5]), 21062306a36Sopenharmony_ci SOC_ENUM("Sync Mode Clk Source", fsl_audmix_enum[6]), 21162306a36Sopenharmony_ci /* TDM1 Attenuation controls */ 21262306a36Sopenharmony_ci SOC_ENUM("TDM1 Attenuation", fsl_audmix_enum[7]), 21362306a36Sopenharmony_ci SOC_ENUM("TDM1 Attenuation Direction", fsl_audmix_enum[8]), 21462306a36Sopenharmony_ci SOC_SINGLE("TDM1 Attenuation Step Divider", FSL_AUDMIX_ATCR0, 21562306a36Sopenharmony_ci 2, 0x00fff, 0), 21662306a36Sopenharmony_ci SOC_SINGLE("TDM1 Attenuation Initial Value", FSL_AUDMIX_ATIVAL0, 21762306a36Sopenharmony_ci 0, 0x3ffff, 0), 21862306a36Sopenharmony_ci SOC_SINGLE("TDM1 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP0, 21962306a36Sopenharmony_ci 0, 0x3ffff, 0), 22062306a36Sopenharmony_ci SOC_SINGLE("TDM1 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN0, 22162306a36Sopenharmony_ci 0, 0x3ffff, 0), 22262306a36Sopenharmony_ci SOC_SINGLE("TDM1 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT0, 22362306a36Sopenharmony_ci 0, 0x3ffff, 0), 22462306a36Sopenharmony_ci /* TDM2 Attenuation controls */ 22562306a36Sopenharmony_ci SOC_ENUM("TDM2 Attenuation", fsl_audmix_enum[9]), 22662306a36Sopenharmony_ci SOC_ENUM("TDM2 Attenuation Direction", fsl_audmix_enum[10]), 22762306a36Sopenharmony_ci SOC_SINGLE("TDM2 Attenuation Step Divider", FSL_AUDMIX_ATCR1, 22862306a36Sopenharmony_ci 2, 0x00fff, 0), 22962306a36Sopenharmony_ci SOC_SINGLE("TDM2 Attenuation Initial Value", FSL_AUDMIX_ATIVAL1, 23062306a36Sopenharmony_ci 0, 0x3ffff, 0), 23162306a36Sopenharmony_ci SOC_SINGLE("TDM2 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP1, 23262306a36Sopenharmony_ci 0, 0x3ffff, 0), 23362306a36Sopenharmony_ci SOC_SINGLE("TDM2 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN1, 23462306a36Sopenharmony_ci 0, 0x3ffff, 0), 23562306a36Sopenharmony_ci SOC_SINGLE("TDM2 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT1, 23662306a36Sopenharmony_ci 0, 0x3ffff, 0), 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int fsl_audmix_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct snd_soc_component *comp = dai->component; 24262306a36Sopenharmony_ci u32 mask = 0, ctr = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* AUDMIX is working in DSP_A format only */ 24562306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 24662306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci default: 24962306a36Sopenharmony_ci return -EINVAL; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* For playback the AUDMIX is consumer, and for record is provider */ 25362306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 25462306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 25562306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci default: 25862306a36Sopenharmony_ci return -EINVAL; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 26262306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 26362306a36Sopenharmony_ci /* Output data will be written on positive edge of the clock */ 26462306a36Sopenharmony_ci ctr |= FSL_AUDMIX_CTR_OUTCKPOL(0); 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 26762306a36Sopenharmony_ci /* Output data will be written on negative edge of the clock */ 26862306a36Sopenharmony_ci ctr |= FSL_AUDMIX_CTR_OUTCKPOL(1); 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci default: 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci mask |= FSL_AUDMIX_CTR_OUTCKPOL_MASK; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, 28062306a36Sopenharmony_ci struct snd_soc_dai *dai) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai); 28362306a36Sopenharmony_ci unsigned long lock_flags; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Capture stream shall not be handled */ 28662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci switch (cmd) { 29062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 29162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 29262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 29362306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, lock_flags); 29462306a36Sopenharmony_ci priv->tdms |= BIT(dai->driver->id); 29562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, lock_flags); 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 29862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 29962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 30062306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, lock_flags); 30162306a36Sopenharmony_ci priv->tdms &= ~BIT(dai->driver->id); 30262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, lock_flags); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci default: 30562306a36Sopenharmony_ci return -EINVAL; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic const struct snd_soc_dai_ops fsl_audmix_dai_ops = { 31262306a36Sopenharmony_ci .set_fmt = fsl_audmix_dai_set_fmt, 31362306a36Sopenharmony_ci .trigger = fsl_audmix_dai_trigger, 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic struct snd_soc_dai_driver fsl_audmix_dai[] = { 31762306a36Sopenharmony_ci { 31862306a36Sopenharmony_ci .id = 0, 31962306a36Sopenharmony_ci .name = "audmix-0", 32062306a36Sopenharmony_ci .playback = { 32162306a36Sopenharmony_ci .stream_name = "AUDMIX-Playback-0", 32262306a36Sopenharmony_ci .channels_min = 8, 32362306a36Sopenharmony_ci .channels_max = 8, 32462306a36Sopenharmony_ci .rate_min = 8000, 32562306a36Sopenharmony_ci .rate_max = 96000, 32662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 32762306a36Sopenharmony_ci .formats = FSL_AUDMIX_FORMATS, 32862306a36Sopenharmony_ci }, 32962306a36Sopenharmony_ci .capture = { 33062306a36Sopenharmony_ci .stream_name = "AUDMIX-Capture-0", 33162306a36Sopenharmony_ci .channels_min = 8, 33262306a36Sopenharmony_ci .channels_max = 8, 33362306a36Sopenharmony_ci .rate_min = 8000, 33462306a36Sopenharmony_ci .rate_max = 96000, 33562306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 33662306a36Sopenharmony_ci .formats = FSL_AUDMIX_FORMATS, 33762306a36Sopenharmony_ci }, 33862306a36Sopenharmony_ci .ops = &fsl_audmix_dai_ops, 33962306a36Sopenharmony_ci }, 34062306a36Sopenharmony_ci { 34162306a36Sopenharmony_ci .id = 1, 34262306a36Sopenharmony_ci .name = "audmix-1", 34362306a36Sopenharmony_ci .playback = { 34462306a36Sopenharmony_ci .stream_name = "AUDMIX-Playback-1", 34562306a36Sopenharmony_ci .channels_min = 8, 34662306a36Sopenharmony_ci .channels_max = 8, 34762306a36Sopenharmony_ci .rate_min = 8000, 34862306a36Sopenharmony_ci .rate_max = 96000, 34962306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 35062306a36Sopenharmony_ci .formats = FSL_AUDMIX_FORMATS, 35162306a36Sopenharmony_ci }, 35262306a36Sopenharmony_ci .capture = { 35362306a36Sopenharmony_ci .stream_name = "AUDMIX-Capture-1", 35462306a36Sopenharmony_ci .channels_min = 8, 35562306a36Sopenharmony_ci .channels_max = 8, 35662306a36Sopenharmony_ci .rate_min = 8000, 35762306a36Sopenharmony_ci .rate_max = 96000, 35862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 35962306a36Sopenharmony_ci .formats = FSL_AUDMIX_FORMATS, 36062306a36Sopenharmony_ci }, 36162306a36Sopenharmony_ci .ops = &fsl_audmix_dai_ops, 36262306a36Sopenharmony_ci }, 36362306a36Sopenharmony_ci}; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic const struct snd_soc_component_driver fsl_audmix_component = { 36662306a36Sopenharmony_ci .name = "fsl-audmix-dai", 36762306a36Sopenharmony_ci .controls = fsl_audmix_snd_controls, 36862306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(fsl_audmix_snd_controls), 36962306a36Sopenharmony_ci}; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic bool fsl_audmix_readable_reg(struct device *dev, unsigned int reg) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci switch (reg) { 37462306a36Sopenharmony_ci case FSL_AUDMIX_CTR: 37562306a36Sopenharmony_ci case FSL_AUDMIX_STR: 37662306a36Sopenharmony_ci case FSL_AUDMIX_ATCR0: 37762306a36Sopenharmony_ci case FSL_AUDMIX_ATIVAL0: 37862306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPUP0: 37962306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPDN0: 38062306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPTGT0: 38162306a36Sopenharmony_ci case FSL_AUDMIX_ATTNVAL0: 38262306a36Sopenharmony_ci case FSL_AUDMIX_ATSTP0: 38362306a36Sopenharmony_ci case FSL_AUDMIX_ATCR1: 38462306a36Sopenharmony_ci case FSL_AUDMIX_ATIVAL1: 38562306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPUP1: 38662306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPDN1: 38762306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPTGT1: 38862306a36Sopenharmony_ci case FSL_AUDMIX_ATTNVAL1: 38962306a36Sopenharmony_ci case FSL_AUDMIX_ATSTP1: 39062306a36Sopenharmony_ci return true; 39162306a36Sopenharmony_ci default: 39262306a36Sopenharmony_ci return false; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic bool fsl_audmix_writeable_reg(struct device *dev, unsigned int reg) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci switch (reg) { 39962306a36Sopenharmony_ci case FSL_AUDMIX_CTR: 40062306a36Sopenharmony_ci case FSL_AUDMIX_ATCR0: 40162306a36Sopenharmony_ci case FSL_AUDMIX_ATIVAL0: 40262306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPUP0: 40362306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPDN0: 40462306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPTGT0: 40562306a36Sopenharmony_ci case FSL_AUDMIX_ATCR1: 40662306a36Sopenharmony_ci case FSL_AUDMIX_ATIVAL1: 40762306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPUP1: 40862306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPDN1: 40962306a36Sopenharmony_ci case FSL_AUDMIX_ATSTPTGT1: 41062306a36Sopenharmony_ci return true; 41162306a36Sopenharmony_ci default: 41262306a36Sopenharmony_ci return false; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic const struct reg_default fsl_audmix_reg[] = { 41762306a36Sopenharmony_ci { FSL_AUDMIX_CTR, 0x00060 }, 41862306a36Sopenharmony_ci { FSL_AUDMIX_STR, 0x00003 }, 41962306a36Sopenharmony_ci { FSL_AUDMIX_ATCR0, 0x00000 }, 42062306a36Sopenharmony_ci { FSL_AUDMIX_ATIVAL0, 0x3FFFF }, 42162306a36Sopenharmony_ci { FSL_AUDMIX_ATSTPUP0, 0x2AAAA }, 42262306a36Sopenharmony_ci { FSL_AUDMIX_ATSTPDN0, 0x30000 }, 42362306a36Sopenharmony_ci { FSL_AUDMIX_ATSTPTGT0, 0x00010 }, 42462306a36Sopenharmony_ci { FSL_AUDMIX_ATTNVAL0, 0x00000 }, 42562306a36Sopenharmony_ci { FSL_AUDMIX_ATSTP0, 0x00000 }, 42662306a36Sopenharmony_ci { FSL_AUDMIX_ATCR1, 0x00000 }, 42762306a36Sopenharmony_ci { FSL_AUDMIX_ATIVAL1, 0x3FFFF }, 42862306a36Sopenharmony_ci { FSL_AUDMIX_ATSTPUP1, 0x2AAAA }, 42962306a36Sopenharmony_ci { FSL_AUDMIX_ATSTPDN1, 0x30000 }, 43062306a36Sopenharmony_ci { FSL_AUDMIX_ATSTPTGT1, 0x00010 }, 43162306a36Sopenharmony_ci { FSL_AUDMIX_ATTNVAL1, 0x00000 }, 43262306a36Sopenharmony_ci { FSL_AUDMIX_ATSTP1, 0x00000 }, 43362306a36Sopenharmony_ci}; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic const struct regmap_config fsl_audmix_regmap_config = { 43662306a36Sopenharmony_ci .reg_bits = 32, 43762306a36Sopenharmony_ci .reg_stride = 4, 43862306a36Sopenharmony_ci .val_bits = 32, 43962306a36Sopenharmony_ci .max_register = FSL_AUDMIX_ATSTP1, 44062306a36Sopenharmony_ci .reg_defaults = fsl_audmix_reg, 44162306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(fsl_audmix_reg), 44262306a36Sopenharmony_ci .readable_reg = fsl_audmix_readable_reg, 44362306a36Sopenharmony_ci .writeable_reg = fsl_audmix_writeable_reg, 44462306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 44562306a36Sopenharmony_ci}; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic const struct of_device_id fsl_audmix_ids[] = { 44862306a36Sopenharmony_ci { 44962306a36Sopenharmony_ci .compatible = "fsl,imx8qm-audmix", 45062306a36Sopenharmony_ci }, 45162306a36Sopenharmony_ci { /* sentinel */ } 45262306a36Sopenharmony_ci}; 45362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_audmix_ids); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int fsl_audmix_probe(struct platform_device *pdev) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 45862306a36Sopenharmony_ci struct fsl_audmix *priv; 45962306a36Sopenharmony_ci void __iomem *regs; 46062306a36Sopenharmony_ci int ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 46362306a36Sopenharmony_ci if (!priv) 46462306a36Sopenharmony_ci return -ENOMEM; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Get the addresses */ 46762306a36Sopenharmony_ci regs = devm_platform_ioremap_resource(pdev, 0); 46862306a36Sopenharmony_ci if (IS_ERR(regs)) 46962306a36Sopenharmony_ci return PTR_ERR(regs); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci priv->regmap = devm_regmap_init_mmio(dev, regs, &fsl_audmix_regmap_config); 47262306a36Sopenharmony_ci if (IS_ERR(priv->regmap)) { 47362306a36Sopenharmony_ci dev_err(dev, "failed to init regmap\n"); 47462306a36Sopenharmony_ci return PTR_ERR(priv->regmap); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci priv->ipg_clk = devm_clk_get(dev, "ipg"); 47862306a36Sopenharmony_ci if (IS_ERR(priv->ipg_clk)) { 47962306a36Sopenharmony_ci dev_err(dev, "failed to get ipg clock\n"); 48062306a36Sopenharmony_ci return PTR_ERR(priv->ipg_clk); 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci spin_lock_init(&priv->lock); 48462306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 48562306a36Sopenharmony_ci pm_runtime_enable(dev); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &fsl_audmix_component, 48862306a36Sopenharmony_ci fsl_audmix_dai, 48962306a36Sopenharmony_ci ARRAY_SIZE(fsl_audmix_dai)); 49062306a36Sopenharmony_ci if (ret) { 49162306a36Sopenharmony_ci dev_err(dev, "failed to register ASoC DAI\n"); 49262306a36Sopenharmony_ci goto err_disable_pm; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci priv->pdev = platform_device_register_data(dev, "imx-audmix", 0, NULL, 0); 49662306a36Sopenharmony_ci if (IS_ERR(priv->pdev)) { 49762306a36Sopenharmony_ci ret = PTR_ERR(priv->pdev); 49862306a36Sopenharmony_ci dev_err(dev, "failed to register platform: %d\n", ret); 49962306a36Sopenharmony_ci goto err_disable_pm; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cierr_disable_pm: 50562306a36Sopenharmony_ci pm_runtime_disable(dev); 50662306a36Sopenharmony_ci return ret; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void fsl_audmix_remove(struct platform_device *pdev) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (priv->pdev) 51662306a36Sopenharmony_ci platform_device_unregister(priv->pdev); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci#ifdef CONFIG_PM 52062306a36Sopenharmony_cistatic int fsl_audmix_runtime_resume(struct device *dev) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct fsl_audmix *priv = dev_get_drvdata(dev); 52362306a36Sopenharmony_ci int ret; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ret = clk_prepare_enable(priv->ipg_clk); 52662306a36Sopenharmony_ci if (ret) { 52762306a36Sopenharmony_ci dev_err(dev, "Failed to enable IPG clock: %d\n", ret); 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci regcache_cache_only(priv->regmap, false); 53262306a36Sopenharmony_ci regcache_mark_dirty(priv->regmap); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return regcache_sync(priv->regmap); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic int fsl_audmix_runtime_suspend(struct device *dev) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct fsl_audmix *priv = dev_get_drvdata(dev); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci regcache_cache_only(priv->regmap, true); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci clk_disable_unprepare(priv->ipg_clk); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci#endif /* CONFIG_PM */ 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic const struct dev_pm_ops fsl_audmix_pm = { 55062306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(fsl_audmix_runtime_suspend, 55162306a36Sopenharmony_ci fsl_audmix_runtime_resume, 55262306a36Sopenharmony_ci NULL) 55362306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 55462306a36Sopenharmony_ci pm_runtime_force_resume) 55562306a36Sopenharmony_ci}; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic struct platform_driver fsl_audmix_driver = { 55862306a36Sopenharmony_ci .probe = fsl_audmix_probe, 55962306a36Sopenharmony_ci .remove_new = fsl_audmix_remove, 56062306a36Sopenharmony_ci .driver = { 56162306a36Sopenharmony_ci .name = "fsl-audmix", 56262306a36Sopenharmony_ci .of_match_table = fsl_audmix_ids, 56362306a36Sopenharmony_ci .pm = &fsl_audmix_pm, 56462306a36Sopenharmony_ci }, 56562306a36Sopenharmony_ci}; 56662306a36Sopenharmony_cimodule_platform_driver(fsl_audmix_driver); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP AUDMIX ASoC DAI driver"); 56962306a36Sopenharmony_ciMODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>"); 57062306a36Sopenharmony_ciMODULE_ALIAS("platform:fsl-audmix"); 57162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 572