162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright 2019 Analog Devices Inc. 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bitfield.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/regmap.h> 1062306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1162306a36Sopenharmony_ci#include <sound/pcm_params.h> 1262306a36Sopenharmony_ci#include <sound/soc.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "adau7118.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define ADAU7118_DEC_RATIO_MASK GENMASK(1, 0) 1762306a36Sopenharmony_ci#define ADAU7118_DEC_RATIO(x) FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x) 1862306a36Sopenharmony_ci#define ADAU7118_CLK_MAP_MASK GENMASK(7, 4) 1962306a36Sopenharmony_ci#define ADAU7118_SLOT_WIDTH_MASK GENMASK(5, 4) 2062306a36Sopenharmony_ci#define ADAU7118_SLOT_WIDTH(x) FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x) 2162306a36Sopenharmony_ci#define ADAU7118_TRISTATE_MASK BIT(6) 2262306a36Sopenharmony_ci#define ADAU7118_TRISTATE(x) FIELD_PREP(ADAU7118_TRISTATE_MASK, x) 2362306a36Sopenharmony_ci#define ADAU7118_DATA_FMT_MASK GENMASK(3, 1) 2462306a36Sopenharmony_ci#define ADAU7118_DATA_FMT(x) FIELD_PREP(ADAU7118_DATA_FMT_MASK, x) 2562306a36Sopenharmony_ci#define ADAU7118_SAI_MODE_MASK BIT(0) 2662306a36Sopenharmony_ci#define ADAU7118_SAI_MODE(x) FIELD_PREP(ADAU7118_SAI_MODE_MASK, x) 2762306a36Sopenharmony_ci#define ADAU7118_LRCLK_BCLK_POL_MASK GENMASK(1, 0) 2862306a36Sopenharmony_ci#define ADAU7118_LRCLK_BCLK_POL(x) \ 2962306a36Sopenharmony_ci FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x) 3062306a36Sopenharmony_ci#define ADAU7118_SPT_SLOT_MASK GENMASK(7, 4) 3162306a36Sopenharmony_ci#define ADAU7118_SPT_SLOT(x) FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x) 3262306a36Sopenharmony_ci#define ADAU7118_FULL_SOFT_R_MASK BIT(1) 3362306a36Sopenharmony_ci#define ADAU7118_FULL_SOFT_R(x) FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct adau7118_data { 3662306a36Sopenharmony_ci struct regmap *map; 3762306a36Sopenharmony_ci struct device *dev; 3862306a36Sopenharmony_ci struct regulator *iovdd; 3962306a36Sopenharmony_ci struct regulator *dvdd; 4062306a36Sopenharmony_ci u32 slot_width; 4162306a36Sopenharmony_ci u32 slots; 4262306a36Sopenharmony_ci bool hw_mode; 4362306a36Sopenharmony_ci bool right_j; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Input Enable */ 4762306a36Sopenharmony_cistatic const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = { 4862306a36Sopenharmony_ci SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0), 4962306a36Sopenharmony_ci SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0), 5062306a36Sopenharmony_ci SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0), 5162306a36Sopenharmony_ci SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0), 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget adau7118_widgets_sw[] = { 5562306a36Sopenharmony_ci /* Input Enable Switches */ 5662306a36Sopenharmony_ci SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0, 5762306a36Sopenharmony_ci &adau7118_dapm_pdm_control[0]), 5862306a36Sopenharmony_ci SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0, 5962306a36Sopenharmony_ci &adau7118_dapm_pdm_control[1]), 6062306a36Sopenharmony_ci SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0, 6162306a36Sopenharmony_ci &adau7118_dapm_pdm_control[2]), 6262306a36Sopenharmony_ci SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0, 6362306a36Sopenharmony_ci &adau7118_dapm_pdm_control[3]), 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* PDM Clocks */ 6662306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0), 6762306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0), 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Output channels */ 7062306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0), 7162306a36Sopenharmony_ci 0, 0), 7262306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1), 7362306a36Sopenharmony_ci 0, 0), 7462306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2), 7562306a36Sopenharmony_ci 0, 0), 7662306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3), 7762306a36Sopenharmony_ci 0, 0), 7862306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4), 7962306a36Sopenharmony_ci 0, 0), 8062306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5), 8162306a36Sopenharmony_ci 0, 0), 8262306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6), 8362306a36Sopenharmony_ci 0, 0), 8462306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7), 8562306a36Sopenharmony_ci 0, 0), 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic const struct snd_soc_dapm_route adau7118_routes_sw[] = { 8962306a36Sopenharmony_ci { "PDM0", "Capture Switch", "PDM_DAT0" }, 9062306a36Sopenharmony_ci { "PDM1", "Capture Switch", "PDM_DAT1" }, 9162306a36Sopenharmony_ci { "PDM2", "Capture Switch", "PDM_DAT2" }, 9262306a36Sopenharmony_ci { "PDM3", "Capture Switch", "PDM_DAT3" }, 9362306a36Sopenharmony_ci { "AIF1TX1", NULL, "PDM0" }, 9462306a36Sopenharmony_ci { "AIF1TX2", NULL, "PDM0" }, 9562306a36Sopenharmony_ci { "AIF1TX3", NULL, "PDM1" }, 9662306a36Sopenharmony_ci { "AIF1TX4", NULL, "PDM1" }, 9762306a36Sopenharmony_ci { "AIF1TX5", NULL, "PDM2" }, 9862306a36Sopenharmony_ci { "AIF1TX6", NULL, "PDM2" }, 9962306a36Sopenharmony_ci { "AIF1TX7", NULL, "PDM3" }, 10062306a36Sopenharmony_ci { "AIF1TX8", NULL, "PDM3" }, 10162306a36Sopenharmony_ci { "Capture", NULL, "PDM_CLK0" }, 10262306a36Sopenharmony_ci { "Capture", NULL, "PDM_CLK1" }, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget adau7118_widgets_hw[] = { 10662306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0), 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const struct snd_soc_dapm_route adau7118_routes_hw[] = { 11062306a36Sopenharmony_ci { "AIF1TX", NULL, "PDM_DAT0" }, 11162306a36Sopenharmony_ci { "AIF1TX", NULL, "PDM_DAT1" }, 11262306a36Sopenharmony_ci { "AIF1TX", NULL, "PDM_DAT2" }, 11362306a36Sopenharmony_ci { "AIF1TX", NULL, "PDM_DAT3" }, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget adau7118_widgets[] = { 11762306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("PDM_DAT0"), 11862306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("PDM_DAT1"), 11962306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("PDM_DAT2"), 12062306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("PDM_DAT3"), 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int adau7118_set_channel_map(struct snd_soc_dai *dai, 12462306a36Sopenharmony_ci unsigned int tx_num, unsigned int *tx_slot, 12562306a36Sopenharmony_ci unsigned int rx_num, unsigned int *rx_slot) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct adau7118_data *st = 12862306a36Sopenharmony_ci snd_soc_component_get_drvdata(dai->component); 12962306a36Sopenharmony_ci int chan, ret; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci dev_dbg(st->dev, "Set channel map, %d", tx_num); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (chan = 0; chan < tx_num; chan++) { 13462306a36Sopenharmony_ci ret = snd_soc_component_update_bits(dai->component, 13562306a36Sopenharmony_ci ADAU7118_REG_SPT_CX(chan), 13662306a36Sopenharmony_ci ADAU7118_SPT_SLOT_MASK, 13762306a36Sopenharmony_ci ADAU7118_SPT_SLOT(tx_slot[chan])); 13862306a36Sopenharmony_ci if (ret < 0) 13962306a36Sopenharmony_ci return ret; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct adau7118_data *st = 14862306a36Sopenharmony_ci snd_soc_component_get_drvdata(dai->component); 14962306a36Sopenharmony_ci int ret = 0; 15062306a36Sopenharmony_ci u32 regval; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci dev_dbg(st->dev, "Set format, fmt:%d\n", fmt); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 15562306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 15662306a36Sopenharmony_ci ret = snd_soc_component_update_bits(dai->component, 15762306a36Sopenharmony_ci ADAU7118_REG_SPT_CTRL1, 15862306a36Sopenharmony_ci ADAU7118_DATA_FMT_MASK, 15962306a36Sopenharmony_ci ADAU7118_DATA_FMT(0)); 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 16262306a36Sopenharmony_ci ret = snd_soc_component_update_bits(dai->component, 16362306a36Sopenharmony_ci ADAU7118_REG_SPT_CTRL1, 16462306a36Sopenharmony_ci ADAU7118_DATA_FMT_MASK, 16562306a36Sopenharmony_ci ADAU7118_DATA_FMT(1)); 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 16862306a36Sopenharmony_ci st->right_j = true; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci default: 17162306a36Sopenharmony_ci dev_err(st->dev, "Invalid format %d", 17262306a36Sopenharmony_ci fmt & SND_SOC_DAIFMT_FORMAT_MASK); 17362306a36Sopenharmony_ci return -EINVAL; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (ret < 0) 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 18062306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 18162306a36Sopenharmony_ci regval = ADAU7118_LRCLK_BCLK_POL(0); 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 18462306a36Sopenharmony_ci regval = ADAU7118_LRCLK_BCLK_POL(2); 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 18762306a36Sopenharmony_ci regval = ADAU7118_LRCLK_BCLK_POL(1); 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 19062306a36Sopenharmony_ci regval = ADAU7118_LRCLK_BCLK_POL(3); 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci default: 19362306a36Sopenharmony_ci dev_err(st->dev, "Invalid Inv mask %d", 19462306a36Sopenharmony_ci fmt & SND_SOC_DAIFMT_INV_MASK); 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ret = snd_soc_component_update_bits(dai->component, 19962306a36Sopenharmony_ci ADAU7118_REG_SPT_CTRL2, 20062306a36Sopenharmony_ci ADAU7118_LRCLK_BCLK_POL_MASK, 20162306a36Sopenharmony_ci regval); 20262306a36Sopenharmony_ci if (ret < 0) 20362306a36Sopenharmony_ci return ret; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct adau7118_data *st = 21162306a36Sopenharmony_ci snd_soc_component_get_drvdata(dai->component); 21262306a36Sopenharmony_ci int ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci dev_dbg(st->dev, "Set tristate, %d\n", tristate); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = snd_soc_component_update_bits(dai->component, 21762306a36Sopenharmony_ci ADAU7118_REG_SPT_CTRL1, 21862306a36Sopenharmony_ci ADAU7118_TRISTATE_MASK, 21962306a36Sopenharmony_ci ADAU7118_TRISTATE(tristate)); 22062306a36Sopenharmony_ci if (ret < 0) 22162306a36Sopenharmony_ci return ret; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, 22762306a36Sopenharmony_ci unsigned int rx_mask, int slots, 22862306a36Sopenharmony_ci int slot_width) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct adau7118_data *st = 23162306a36Sopenharmony_ci snd_soc_component_get_drvdata(dai->component); 23262306a36Sopenharmony_ci int ret = 0; 23362306a36Sopenharmony_ci u32 regval; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci switch (slot_width) { 23862306a36Sopenharmony_ci case 32: 23962306a36Sopenharmony_ci regval = ADAU7118_SLOT_WIDTH(0); 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case 24: 24262306a36Sopenharmony_ci regval = ADAU7118_SLOT_WIDTH(2); 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci case 16: 24562306a36Sopenharmony_ci regval = ADAU7118_SLOT_WIDTH(1); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci default: 24862306a36Sopenharmony_ci dev_err(st->dev, "Invalid slot width:%d\n", slot_width); 24962306a36Sopenharmony_ci return -EINVAL; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ret = snd_soc_component_update_bits(dai->component, 25362306a36Sopenharmony_ci ADAU7118_REG_SPT_CTRL1, 25462306a36Sopenharmony_ci ADAU7118_SLOT_WIDTH_MASK, regval); 25562306a36Sopenharmony_ci if (ret < 0) 25662306a36Sopenharmony_ci return ret; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci st->slot_width = slot_width; 25962306a36Sopenharmony_ci st->slots = slots; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int adau7118_hw_params(struct snd_pcm_substream *substream, 26562306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 26662306a36Sopenharmony_ci struct snd_soc_dai *dai) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct adau7118_data *st = 26962306a36Sopenharmony_ci snd_soc_component_get_drvdata(dai->component); 27062306a36Sopenharmony_ci u32 data_width = params_width(params), slots_width; 27162306a36Sopenharmony_ci int ret; 27262306a36Sopenharmony_ci u32 regval; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!st->slots) { 27562306a36Sopenharmony_ci /* set stereo mode */ 27662306a36Sopenharmony_ci ret = snd_soc_component_update_bits(dai->component, 27762306a36Sopenharmony_ci ADAU7118_REG_SPT_CTRL1, 27862306a36Sopenharmony_ci ADAU7118_SAI_MODE_MASK, 27962306a36Sopenharmony_ci ADAU7118_SAI_MODE(0)); 28062306a36Sopenharmony_ci if (ret < 0) 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci slots_width = 32; 28462306a36Sopenharmony_ci } else { 28562306a36Sopenharmony_ci slots_width = st->slot_width; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (data_width > slots_width) { 28962306a36Sopenharmony_ci dev_err(st->dev, "Invalid data_width:%d, slots_width:%d", 29062306a36Sopenharmony_ci data_width, slots_width); 29162306a36Sopenharmony_ci return -EINVAL; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (st->right_j) { 29562306a36Sopenharmony_ci switch (slots_width - data_width) { 29662306a36Sopenharmony_ci case 8: 29762306a36Sopenharmony_ci /* delay bclck by 8 */ 29862306a36Sopenharmony_ci regval = ADAU7118_DATA_FMT(2); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci case 12: 30162306a36Sopenharmony_ci /* delay bclck by 12 */ 30262306a36Sopenharmony_ci regval = ADAU7118_DATA_FMT(3); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case 16: 30562306a36Sopenharmony_ci /* delay bclck by 16 */ 30662306a36Sopenharmony_ci regval = ADAU7118_DATA_FMT(4); 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci default: 30962306a36Sopenharmony_ci dev_err(st->dev, 31062306a36Sopenharmony_ci "Cannot set right_j setting, slot_w:%d, data_w:%d\n", 31162306a36Sopenharmony_ci slots_width, data_width); 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = snd_soc_component_update_bits(dai->component, 31662306a36Sopenharmony_ci ADAU7118_REG_SPT_CTRL1, 31762306a36Sopenharmony_ci ADAU7118_DATA_FMT_MASK, 31862306a36Sopenharmony_ci regval); 31962306a36Sopenharmony_ci if (ret < 0) 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int adau7118_set_bias_level(struct snd_soc_component *component, 32762306a36Sopenharmony_ci enum snd_soc_bias_level level) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct adau7118_data *st = snd_soc_component_get_drvdata(component); 33062306a36Sopenharmony_ci int ret = 0; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci dev_dbg(st->dev, "Set bias level %d\n", level); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci switch (level) { 33562306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 33662306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 34062306a36Sopenharmony_ci if (snd_soc_component_get_bias_level(component) == 34162306a36Sopenharmony_ci SND_SOC_BIAS_OFF) { 34262306a36Sopenharmony_ci /* power on */ 34362306a36Sopenharmony_ci ret = regulator_enable(st->iovdd); 34462306a36Sopenharmony_ci if (ret) 34562306a36Sopenharmony_ci return ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* there's no timing constraints before enabling dvdd */ 34862306a36Sopenharmony_ci ret = regulator_enable(st->dvdd); 34962306a36Sopenharmony_ci if (ret) { 35062306a36Sopenharmony_ci regulator_disable(st->iovdd); 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (st->hw_mode) 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci regcache_cache_only(st->map, false); 35862306a36Sopenharmony_ci /* sync cache */ 35962306a36Sopenharmony_ci ret = snd_soc_component_cache_sync(component); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 36362306a36Sopenharmony_ci /* power off */ 36462306a36Sopenharmony_ci ret = regulator_disable(st->dvdd); 36562306a36Sopenharmony_ci if (ret) 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ret = regulator_disable(st->iovdd); 36962306a36Sopenharmony_ci if (ret) 37062306a36Sopenharmony_ci return ret; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (st->hw_mode) 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* cache only */ 37662306a36Sopenharmony_ci regcache_mark_dirty(st->map); 37762306a36Sopenharmony_ci regcache_cache_only(st->map, true); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return ret; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int adau7118_component_probe(struct snd_soc_component *component) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct adau7118_data *st = snd_soc_component_get_drvdata(component); 38862306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = 38962306a36Sopenharmony_ci snd_soc_component_get_dapm(component); 39062306a36Sopenharmony_ci int ret = 0; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (st->hw_mode) { 39362306a36Sopenharmony_ci ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw, 39462306a36Sopenharmony_ci ARRAY_SIZE(adau7118_widgets_hw)); 39562306a36Sopenharmony_ci if (ret) 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw, 39962306a36Sopenharmony_ci ARRAY_SIZE(adau7118_routes_hw)); 40062306a36Sopenharmony_ci } else { 40162306a36Sopenharmony_ci snd_soc_component_init_regmap(component, st->map); 40262306a36Sopenharmony_ci ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw, 40362306a36Sopenharmony_ci ARRAY_SIZE(adau7118_widgets_sw)); 40462306a36Sopenharmony_ci if (ret) 40562306a36Sopenharmony_ci return ret; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw, 40862306a36Sopenharmony_ci ARRAY_SIZE(adau7118_routes_sw)); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic const struct snd_soc_dai_ops adau7118_ops = { 41562306a36Sopenharmony_ci .hw_params = adau7118_hw_params, 41662306a36Sopenharmony_ci .set_channel_map = adau7118_set_channel_map, 41762306a36Sopenharmony_ci .set_fmt = adau7118_set_fmt, 41862306a36Sopenharmony_ci .set_tdm_slot = adau7118_set_tdm_slot, 41962306a36Sopenharmony_ci .set_tristate = adau7118_set_tristate, 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic struct snd_soc_dai_driver adau7118_dai = { 42362306a36Sopenharmony_ci .name = "adau7118-hifi-capture", 42462306a36Sopenharmony_ci .capture = { 42562306a36Sopenharmony_ci .stream_name = "Capture", 42662306a36Sopenharmony_ci .channels_min = 1, 42762306a36Sopenharmony_ci .channels_max = 8, 42862306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | 42962306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | 43062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE, 43162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 43262306a36Sopenharmony_ci .rate_min = 4000, 43362306a36Sopenharmony_ci .rate_max = 192000, 43462306a36Sopenharmony_ci .sig_bits = 24, 43562306a36Sopenharmony_ci }, 43662306a36Sopenharmony_ci}; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic const struct snd_soc_component_driver adau7118_component_driver = { 43962306a36Sopenharmony_ci .probe = adau7118_component_probe, 44062306a36Sopenharmony_ci .set_bias_level = adau7118_set_bias_level, 44162306a36Sopenharmony_ci .dapm_widgets = adau7118_widgets, 44262306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(adau7118_widgets), 44362306a36Sopenharmony_ci .use_pmdown_time = 1, 44462306a36Sopenharmony_ci .endianness = 1, 44562306a36Sopenharmony_ci}; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int adau7118_regulator_setup(struct adau7118_data *st) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci st->iovdd = devm_regulator_get(st->dev, "iovdd"); 45062306a36Sopenharmony_ci if (IS_ERR(st->iovdd)) { 45162306a36Sopenharmony_ci dev_err(st->dev, "Could not get iovdd: %ld\n", 45262306a36Sopenharmony_ci PTR_ERR(st->iovdd)); 45362306a36Sopenharmony_ci return PTR_ERR(st->iovdd); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci st->dvdd = devm_regulator_get(st->dev, "dvdd"); 45762306a36Sopenharmony_ci if (IS_ERR(st->dvdd)) { 45862306a36Sopenharmony_ci dev_err(st->dev, "Could not get dvdd: %ld\n", 45962306a36Sopenharmony_ci PTR_ERR(st->dvdd)); 46062306a36Sopenharmony_ci return PTR_ERR(st->dvdd); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci /* just assume the device is in reset */ 46362306a36Sopenharmony_ci if (!st->hw_mode) { 46462306a36Sopenharmony_ci regcache_mark_dirty(st->map); 46562306a36Sopenharmony_ci regcache_cache_only(st->map, true); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int adau7118_parset_dt(const struct adau7118_data *st) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int ret; 47462306a36Sopenharmony_ci u32 dec_ratio = 0; 47562306a36Sopenharmony_ci /* 4 inputs */ 47662306a36Sopenharmony_ci u32 clk_map[4], regval; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (st->hw_mode) 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci ret = device_property_read_u32(st->dev, "adi,decimation-ratio", 48262306a36Sopenharmony_ci &dec_ratio); 48362306a36Sopenharmony_ci if (!ret) { 48462306a36Sopenharmony_ci switch (dec_ratio) { 48562306a36Sopenharmony_ci case 64: 48662306a36Sopenharmony_ci regval = ADAU7118_DEC_RATIO(0); 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci case 32: 48962306a36Sopenharmony_ci regval = ADAU7118_DEC_RATIO(1); 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci case 16: 49262306a36Sopenharmony_ci regval = ADAU7118_DEC_RATIO(2); 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci default: 49562306a36Sopenharmony_ci dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio); 49662306a36Sopenharmony_ci return -EINVAL; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci ret = regmap_update_bits(st->map, 50062306a36Sopenharmony_ci ADAU7118_REG_DEC_RATIO_CLK_MAP, 50162306a36Sopenharmony_ci ADAU7118_DEC_RATIO_MASK, regval); 50262306a36Sopenharmony_ci if (ret) 50362306a36Sopenharmony_ci return ret; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map", 50762306a36Sopenharmony_ci clk_map, ARRAY_SIZE(clk_map)); 50862306a36Sopenharmony_ci if (!ret) { 50962306a36Sopenharmony_ci int pdm; 51062306a36Sopenharmony_ci u32 _clk_map = 0; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++) 51362306a36Sopenharmony_ci _clk_map |= (clk_map[pdm] << (pdm + 4)); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci ret = regmap_update_bits(st->map, 51662306a36Sopenharmony_ci ADAU7118_REG_DEC_RATIO_CLK_MAP, 51762306a36Sopenharmony_ci ADAU7118_CLK_MAP_MASK, _clk_map); 51862306a36Sopenharmony_ci if (ret) 51962306a36Sopenharmony_ci return ret; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ciint adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct adau7118_data *st; 52862306a36Sopenharmony_ci int ret; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); 53162306a36Sopenharmony_ci if (!st) 53262306a36Sopenharmony_ci return -ENOMEM; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci st->dev = dev; 53562306a36Sopenharmony_ci st->hw_mode = hw_mode; 53662306a36Sopenharmony_ci dev_set_drvdata(dev, st); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (!hw_mode) { 53962306a36Sopenharmony_ci st->map = map; 54062306a36Sopenharmony_ci adau7118_dai.ops = &adau7118_ops; 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * Perform a full soft reset. This will set all register's 54362306a36Sopenharmony_ci * with their reset values. 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci ret = regmap_update_bits(map, ADAU7118_REG_RESET, 54662306a36Sopenharmony_ci ADAU7118_FULL_SOFT_R_MASK, 54762306a36Sopenharmony_ci ADAU7118_FULL_SOFT_R(1)); 54862306a36Sopenharmony_ci if (ret) 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ret = adau7118_parset_dt(st); 55362306a36Sopenharmony_ci if (ret) 55462306a36Sopenharmony_ci return ret; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci ret = adau7118_regulator_setup(st); 55762306a36Sopenharmony_ci if (ret) 55862306a36Sopenharmony_ci return ret; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return devm_snd_soc_register_component(dev, 56162306a36Sopenharmony_ci &adau7118_component_driver, 56262306a36Sopenharmony_ci &adau7118_dai, 1); 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(adau7118_probe); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ciMODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); 56762306a36Sopenharmony_ciMODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver"); 56862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 569