123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- // SPDX-License-Identifier: GPL-2.0
- //
- // Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver
- //
- // Copyright 2019 Analog Devices Inc.
- #include <linux/bitfield.h>
- #include <linux/module.h>
- #include <linux/regmap.h>
- #include <linux/regulator/consumer.h>
- #include <sound/pcm_params.h>
- #include <sound/soc.h>
- #include "adau7118.h"
- #define ADAU7118_DEC_RATIO_MASK GENMASK(1, 0)
- #define ADAU7118_DEC_RATIO(x) FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x)
- #define ADAU7118_CLK_MAP_MASK GENMASK(7, 4)
- #define ADAU7118_SLOT_WIDTH_MASK GENMASK(5, 4)
- #define ADAU7118_SLOT_WIDTH(x) FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x)
- #define ADAU7118_TRISTATE_MASK BIT(6)
- #define ADAU7118_TRISTATE(x) FIELD_PREP(ADAU7118_TRISTATE_MASK, x)
- #define ADAU7118_DATA_FMT_MASK GENMASK(3, 1)
- #define ADAU7118_DATA_FMT(x) FIELD_PREP(ADAU7118_DATA_FMT_MASK, x)
- #define ADAU7118_SAI_MODE_MASK BIT(0)
- #define ADAU7118_SAI_MODE(x) FIELD_PREP(ADAU7118_SAI_MODE_MASK, x)
- #define ADAU7118_LRCLK_BCLK_POL_MASK GENMASK(1, 0)
- #define ADAU7118_LRCLK_BCLK_POL(x) \
- FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x)
- #define ADAU7118_SPT_SLOT_MASK GENMASK(7, 4)
- #define ADAU7118_SPT_SLOT(x) FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x)
- #define ADAU7118_FULL_SOFT_R_MASK BIT(1)
- #define ADAU7118_FULL_SOFT_R(x) FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x)
- struct adau7118_data {
- struct regmap *map;
- struct device *dev;
- struct regulator *iovdd;
- struct regulator *dvdd;
- u32 slot_width;
- u32 slots;
- bool hw_mode;
- bool right_j;
- };
- /* Input Enable */
- static const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = {
- SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0),
- SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0),
- SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0),
- SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0),
- };
- static const struct snd_soc_dapm_widget adau7118_widgets_sw[] = {
- /* Input Enable Switches */
- SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0,
- &adau7118_dapm_pdm_control[0]),
- SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0,
- &adau7118_dapm_pdm_control[1]),
- SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0,
- &adau7118_dapm_pdm_control[2]),
- SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0,
- &adau7118_dapm_pdm_control[3]),
- /* PDM Clocks */
- SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0),
- /* Output channels */
- SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0),
- 0, 0),
- SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1),
- 0, 0),
- SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2),
- 0, 0),
- SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3),
- 0, 0),
- SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4),
- 0, 0),
- SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5),
- 0, 0),
- SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6),
- 0, 0),
- SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7),
- 0, 0),
- };
- static const struct snd_soc_dapm_route adau7118_routes_sw[] = {
- { "PDM0", "Capture Switch", "PDM_DAT0" },
- { "PDM1", "Capture Switch", "PDM_DAT1" },
- { "PDM2", "Capture Switch", "PDM_DAT2" },
- { "PDM3", "Capture Switch", "PDM_DAT3" },
- { "AIF1TX1", NULL, "PDM0" },
- { "AIF1TX2", NULL, "PDM0" },
- { "AIF1TX3", NULL, "PDM1" },
- { "AIF1TX4", NULL, "PDM1" },
- { "AIF1TX5", NULL, "PDM2" },
- { "AIF1TX6", NULL, "PDM2" },
- { "AIF1TX7", NULL, "PDM3" },
- { "AIF1TX8", NULL, "PDM3" },
- { "Capture", NULL, "PDM_CLK0" },
- { "Capture", NULL, "PDM_CLK1" },
- };
- static const struct snd_soc_dapm_widget adau7118_widgets_hw[] = {
- SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0),
- };
- static const struct snd_soc_dapm_route adau7118_routes_hw[] = {
- { "AIF1TX", NULL, "PDM_DAT0" },
- { "AIF1TX", NULL, "PDM_DAT1" },
- { "AIF1TX", NULL, "PDM_DAT2" },
- { "AIF1TX", NULL, "PDM_DAT3" },
- };
- static const struct snd_soc_dapm_widget adau7118_widgets[] = {
- SND_SOC_DAPM_INPUT("PDM_DAT0"),
- SND_SOC_DAPM_INPUT("PDM_DAT1"),
- SND_SOC_DAPM_INPUT("PDM_DAT2"),
- SND_SOC_DAPM_INPUT("PDM_DAT3"),
- };
- static int adau7118_set_channel_map(struct snd_soc_dai *dai,
- unsigned int tx_num, unsigned int *tx_slot,
- unsigned int rx_num, unsigned int *rx_slot)
- {
- struct adau7118_data *st =
- snd_soc_component_get_drvdata(dai->component);
- int chan, ret;
- dev_dbg(st->dev, "Set channel map, %d", tx_num);
- for (chan = 0; chan < tx_num; chan++) {
- ret = snd_soc_component_update_bits(dai->component,
- ADAU7118_REG_SPT_CX(chan),
- ADAU7118_SPT_SLOT_MASK,
- ADAU7118_SPT_SLOT(tx_slot[chan]));
- if (ret < 0)
- return ret;
- }
- return 0;
- }
- static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
- {
- struct adau7118_data *st =
- snd_soc_component_get_drvdata(dai->component);
- int ret = 0;
- u32 regval;
- dev_dbg(st->dev, "Set format, fmt:%d\n", fmt);
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- ret = snd_soc_component_update_bits(dai->component,
- ADAU7118_REG_SPT_CTRL1,
- ADAU7118_DATA_FMT_MASK,
- ADAU7118_DATA_FMT(0));
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- ret = snd_soc_component_update_bits(dai->component,
- ADAU7118_REG_SPT_CTRL1,
- ADAU7118_DATA_FMT_MASK,
- ADAU7118_DATA_FMT(1));
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- st->right_j = true;
- break;
- default:
- dev_err(st->dev, "Invalid format %d",
- fmt & SND_SOC_DAIFMT_FORMAT_MASK);
- return -EINVAL;
- }
- if (ret < 0)
- return ret;
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- regval = ADAU7118_LRCLK_BCLK_POL(0);
- break;
- case SND_SOC_DAIFMT_NB_IF:
- regval = ADAU7118_LRCLK_BCLK_POL(2);
- break;
- case SND_SOC_DAIFMT_IB_NF:
- regval = ADAU7118_LRCLK_BCLK_POL(1);
- break;
- case SND_SOC_DAIFMT_IB_IF:
- regval = ADAU7118_LRCLK_BCLK_POL(3);
- break;
- default:
- dev_err(st->dev, "Invalid Inv mask %d",
- fmt & SND_SOC_DAIFMT_INV_MASK);
- return -EINVAL;
- }
- ret = snd_soc_component_update_bits(dai->component,
- ADAU7118_REG_SPT_CTRL2,
- ADAU7118_LRCLK_BCLK_POL_MASK,
- regval);
- if (ret < 0)
- return ret;
- return 0;
- }
- static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate)
- {
- struct adau7118_data *st =
- snd_soc_component_get_drvdata(dai->component);
- int ret;
- dev_dbg(st->dev, "Set tristate, %d\n", tristate);
- ret = snd_soc_component_update_bits(dai->component,
- ADAU7118_REG_SPT_CTRL1,
- ADAU7118_TRISTATE_MASK,
- ADAU7118_TRISTATE(tristate));
- if (ret < 0)
- return ret;
- return 0;
- }
- static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
- unsigned int rx_mask, int slots,
- int slot_width)
- {
- struct adau7118_data *st =
- snd_soc_component_get_drvdata(dai->component);
- int ret = 0;
- u32 regval;
- dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width);
- switch (slot_width) {
- case 32:
- regval = ADAU7118_SLOT_WIDTH(0);
- break;
- case 24:
- regval = ADAU7118_SLOT_WIDTH(2);
- break;
- case 16:
- regval = ADAU7118_SLOT_WIDTH(1);
- break;
- default:
- dev_err(st->dev, "Invalid slot width:%d\n", slot_width);
- return -EINVAL;
- }
- ret = snd_soc_component_update_bits(dai->component,
- ADAU7118_REG_SPT_CTRL1,
- ADAU7118_SLOT_WIDTH_MASK, regval);
- if (ret < 0)
- return ret;
- st->slot_width = slot_width;
- st->slots = slots;
- return 0;
- }
- static int adau7118_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
- {
- struct adau7118_data *st =
- snd_soc_component_get_drvdata(dai->component);
- u32 data_width = params_width(params), slots_width;
- int ret;
- u32 regval;
- if (!st->slots) {
- /* set stereo mode */
- ret = snd_soc_component_update_bits(dai->component,
- ADAU7118_REG_SPT_CTRL1,
- ADAU7118_SAI_MODE_MASK,
- ADAU7118_SAI_MODE(0));
- if (ret < 0)
- return ret;
- slots_width = 32;
- } else {
- slots_width = st->slot_width;
- }
- if (data_width > slots_width) {
- dev_err(st->dev, "Invalid data_width:%d, slots_width:%d",
- data_width, slots_width);
- return -EINVAL;
- }
- if (st->right_j) {
- switch (slots_width - data_width) {
- case 8:
- /* delay bclck by 8 */
- regval = ADAU7118_DATA_FMT(2);
- break;
- case 12:
- /* delay bclck by 12 */
- regval = ADAU7118_DATA_FMT(3);
- break;
- case 16:
- /* delay bclck by 16 */
- regval = ADAU7118_DATA_FMT(4);
- break;
- default:
- dev_err(st->dev,
- "Cannot set right_j setting, slot_w:%d, data_w:%d\n",
- slots_width, data_width);
- return -EINVAL;
- }
- ret = snd_soc_component_update_bits(dai->component,
- ADAU7118_REG_SPT_CTRL1,
- ADAU7118_DATA_FMT_MASK,
- regval);
- if (ret < 0)
- return ret;
- }
- return 0;
- }
- static int adau7118_set_bias_level(struct snd_soc_component *component,
- enum snd_soc_bias_level level)
- {
- struct adau7118_data *st = snd_soc_component_get_drvdata(component);
- int ret = 0;
- dev_dbg(st->dev, "Set bias level %d\n", level);
- switch (level) {
- case SND_SOC_BIAS_ON:
- case SND_SOC_BIAS_PREPARE:
- break;
- case SND_SOC_BIAS_STANDBY:
- if (snd_soc_component_get_bias_level(component) ==
- SND_SOC_BIAS_OFF) {
- /* power on */
- ret = regulator_enable(st->iovdd);
- if (ret)
- return ret;
- /* there's no timing constraints before enabling dvdd */
- ret = regulator_enable(st->dvdd);
- if (ret) {
- regulator_disable(st->iovdd);
- return ret;
- }
- if (st->hw_mode)
- return 0;
- regcache_cache_only(st->map, false);
- /* sync cache */
- ret = snd_soc_component_cache_sync(component);
- }
- break;
- case SND_SOC_BIAS_OFF:
- /* power off */
- ret = regulator_disable(st->dvdd);
- if (ret)
- return ret;
- ret = regulator_disable(st->iovdd);
- if (ret)
- return ret;
- if (st->hw_mode)
- return 0;
- /* cache only */
- regcache_mark_dirty(st->map);
- regcache_cache_only(st->map, true);
- break;
- }
- return ret;
- }
- static int adau7118_component_probe(struct snd_soc_component *component)
- {
- struct adau7118_data *st = snd_soc_component_get_drvdata(component);
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- int ret = 0;
- if (st->hw_mode) {
- ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw,
- ARRAY_SIZE(adau7118_widgets_hw));
- if (ret)
- return ret;
- ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw,
- ARRAY_SIZE(adau7118_routes_hw));
- } else {
- snd_soc_component_init_regmap(component, st->map);
- ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw,
- ARRAY_SIZE(adau7118_widgets_sw));
- if (ret)
- return ret;
- ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw,
- ARRAY_SIZE(adau7118_routes_sw));
- }
- return ret;
- }
- static const struct snd_soc_dai_ops adau7118_ops = {
- .hw_params = adau7118_hw_params,
- .set_channel_map = adau7118_set_channel_map,
- .set_fmt = adau7118_set_fmt,
- .set_tdm_slot = adau7118_set_tdm_slot,
- .set_tristate = adau7118_set_tristate,
- };
- static struct snd_soc_dai_driver adau7118_dai = {
- .name = "adau7118-hifi-capture",
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 8,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
- SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE,
- .rates = SNDRV_PCM_RATE_CONTINUOUS,
- .rate_min = 4000,
- .rate_max = 192000,
- .sig_bits = 24,
- },
- };
- static const struct snd_soc_component_driver adau7118_component_driver = {
- .probe = adau7118_component_probe,
- .set_bias_level = adau7118_set_bias_level,
- .dapm_widgets = adau7118_widgets,
- .num_dapm_widgets = ARRAY_SIZE(adau7118_widgets),
- .use_pmdown_time = 1,
- .endianness = 1,
- };
- static int adau7118_regulator_setup(struct adau7118_data *st)
- {
- st->iovdd = devm_regulator_get(st->dev, "iovdd");
- if (IS_ERR(st->iovdd)) {
- dev_err(st->dev, "Could not get iovdd: %ld\n",
- PTR_ERR(st->iovdd));
- return PTR_ERR(st->iovdd);
- }
- st->dvdd = devm_regulator_get(st->dev, "dvdd");
- if (IS_ERR(st->dvdd)) {
- dev_err(st->dev, "Could not get dvdd: %ld\n",
- PTR_ERR(st->dvdd));
- return PTR_ERR(st->dvdd);
- }
- /* just assume the device is in reset */
- if (!st->hw_mode) {
- regcache_mark_dirty(st->map);
- regcache_cache_only(st->map, true);
- }
- return 0;
- }
- static int adau7118_parset_dt(const struct adau7118_data *st)
- {
- int ret;
- u32 dec_ratio = 0;
- /* 4 inputs */
- u32 clk_map[4], regval;
- if (st->hw_mode)
- return 0;
- ret = device_property_read_u32(st->dev, "adi,decimation-ratio",
- &dec_ratio);
- if (!ret) {
- switch (dec_ratio) {
- case 64:
- regval = ADAU7118_DEC_RATIO(0);
- break;
- case 32:
- regval = ADAU7118_DEC_RATIO(1);
- break;
- case 16:
- regval = ADAU7118_DEC_RATIO(2);
- break;
- default:
- dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio);
- return -EINVAL;
- }
- ret = regmap_update_bits(st->map,
- ADAU7118_REG_DEC_RATIO_CLK_MAP,
- ADAU7118_DEC_RATIO_MASK, regval);
- if (ret)
- return ret;
- }
- ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map",
- clk_map, ARRAY_SIZE(clk_map));
- if (!ret) {
- int pdm;
- u32 _clk_map = 0;
- for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++)
- _clk_map |= (clk_map[pdm] << (pdm + 4));
- ret = regmap_update_bits(st->map,
- ADAU7118_REG_DEC_RATIO_CLK_MAP,
- ADAU7118_CLK_MAP_MASK, _clk_map);
- if (ret)
- return ret;
- }
- return 0;
- }
- int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode)
- {
- struct adau7118_data *st;
- int ret;
- st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
- if (!st)
- return -ENOMEM;
- st->dev = dev;
- st->hw_mode = hw_mode;
- dev_set_drvdata(dev, st);
- if (!hw_mode) {
- st->map = map;
- adau7118_dai.ops = &adau7118_ops;
- /*
- * Perform a full soft reset. This will set all register's
- * with their reset values.
- */
- ret = regmap_update_bits(map, ADAU7118_REG_RESET,
- ADAU7118_FULL_SOFT_R_MASK,
- ADAU7118_FULL_SOFT_R(1));
- if (ret)
- return ret;
- }
- ret = adau7118_parset_dt(st);
- if (ret)
- return ret;
- ret = adau7118_regulator_setup(st);
- if (ret)
- return ret;
- return devm_snd_soc_register_component(dev,
- &adau7118_component_driver,
- &adau7118_dai, 1);
- }
- EXPORT_SYMBOL_GPL(adau7118_probe);
- MODULE_AUTHOR("Nuno Sa <[email protected]>");
- MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver");
- MODULE_LICENSE("GPL");
|