cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

adau7118.c (14988B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver
      4//
      5// Copyright 2019 Analog Devices Inc.
      6
      7#include <linux/bitfield.h>
      8#include <linux/module.h>
      9#include <linux/regmap.h>
     10#include <linux/regulator/consumer.h>
     11#include <sound/pcm_params.h>
     12#include <sound/soc.h>
     13
     14#include "adau7118.h"
     15
     16#define ADAU7118_DEC_RATIO_MASK		GENMASK(1, 0)
     17#define ADAU7118_DEC_RATIO(x)		FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x)
     18#define ADAU7118_CLK_MAP_MASK		GENMASK(7, 4)
     19#define ADAU7118_SLOT_WIDTH_MASK	GENMASK(5, 4)
     20#define ADAU7118_SLOT_WIDTH(x)		FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x)
     21#define ADAU7118_TRISTATE_MASK		BIT(6)
     22#define ADAU7118_TRISTATE(x)		FIELD_PREP(ADAU7118_TRISTATE_MASK, x)
     23#define ADAU7118_DATA_FMT_MASK		GENMASK(3, 1)
     24#define ADAU7118_DATA_FMT(x)		FIELD_PREP(ADAU7118_DATA_FMT_MASK, x)
     25#define ADAU7118_SAI_MODE_MASK		BIT(0)
     26#define ADAU7118_SAI_MODE(x)		FIELD_PREP(ADAU7118_SAI_MODE_MASK, x)
     27#define ADAU7118_LRCLK_BCLK_POL_MASK	GENMASK(1, 0)
     28#define ADAU7118_LRCLK_BCLK_POL(x) \
     29				FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x)
     30#define ADAU7118_SPT_SLOT_MASK		GENMASK(7, 4)
     31#define ADAU7118_SPT_SLOT(x)		FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x)
     32#define ADAU7118_FULL_SOFT_R_MASK	BIT(1)
     33#define ADAU7118_FULL_SOFT_R(x)		FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x)
     34
     35struct adau7118_data {
     36	struct regmap *map;
     37	struct device *dev;
     38	struct regulator *iovdd;
     39	struct regulator *dvdd;
     40	u32 slot_width;
     41	u32 slots;
     42	bool hw_mode;
     43	bool right_j;
     44};
     45
     46/* Input Enable */
     47static const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = {
     48	SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0),
     49	SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0),
     50	SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0),
     51	SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0),
     52};
     53
     54static const struct snd_soc_dapm_widget adau7118_widgets_sw[] = {
     55	/* Input Enable Switches */
     56	SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0,
     57			    &adau7118_dapm_pdm_control[0]),
     58	SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0,
     59			    &adau7118_dapm_pdm_control[1]),
     60	SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0,
     61			    &adau7118_dapm_pdm_control[2]),
     62	SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0,
     63			    &adau7118_dapm_pdm_control[3]),
     64
     65	/* PDM Clocks */
     66	SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0),
     67	SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0),
     68
     69	/* Output channels */
     70	SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0),
     71			     0, 0),
     72	SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1),
     73			     0, 0),
     74	SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2),
     75			     0, 0),
     76	SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3),
     77			     0, 0),
     78	SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4),
     79			     0, 0),
     80	SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5),
     81			     0, 0),
     82	SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6),
     83			     0, 0),
     84	SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7),
     85			     0, 0),
     86};
     87
     88static const struct snd_soc_dapm_route adau7118_routes_sw[] = {
     89	{ "PDM0", "Capture Switch", "PDM_DAT0" },
     90	{ "PDM1", "Capture Switch", "PDM_DAT1" },
     91	{ "PDM2", "Capture Switch", "PDM_DAT2" },
     92	{ "PDM3", "Capture Switch", "PDM_DAT3" },
     93	{ "AIF1TX1", NULL, "PDM0" },
     94	{ "AIF1TX2", NULL, "PDM0" },
     95	{ "AIF1TX3", NULL, "PDM1" },
     96	{ "AIF1TX4", NULL, "PDM1" },
     97	{ "AIF1TX5", NULL, "PDM2" },
     98	{ "AIF1TX6", NULL, "PDM2" },
     99	{ "AIF1TX7", NULL, "PDM3" },
    100	{ "AIF1TX8", NULL, "PDM3" },
    101	{ "Capture", NULL, "PDM_CLK0" },
    102	{ "Capture", NULL, "PDM_CLK1" },
    103};
    104
    105static const struct snd_soc_dapm_widget adau7118_widgets_hw[] = {
    106	SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0),
    107};
    108
    109static const struct snd_soc_dapm_route adau7118_routes_hw[] = {
    110	{ "AIF1TX", NULL, "PDM_DAT0" },
    111	{ "AIF1TX", NULL, "PDM_DAT1" },
    112	{ "AIF1TX", NULL, "PDM_DAT2" },
    113	{ "AIF1TX", NULL, "PDM_DAT3" },
    114};
    115
    116static const struct snd_soc_dapm_widget adau7118_widgets[] = {
    117	SND_SOC_DAPM_INPUT("PDM_DAT0"),
    118	SND_SOC_DAPM_INPUT("PDM_DAT1"),
    119	SND_SOC_DAPM_INPUT("PDM_DAT2"),
    120	SND_SOC_DAPM_INPUT("PDM_DAT3"),
    121};
    122
    123static int adau7118_set_channel_map(struct snd_soc_dai *dai,
    124				    unsigned int tx_num, unsigned int *tx_slot,
    125				    unsigned int rx_num, unsigned int *rx_slot)
    126{
    127	struct adau7118_data *st =
    128		snd_soc_component_get_drvdata(dai->component);
    129	int chan, ret;
    130
    131	dev_dbg(st->dev, "Set channel map, %d", tx_num);
    132
    133	for (chan = 0; chan < tx_num; chan++) {
    134		ret = snd_soc_component_update_bits(dai->component,
    135					ADAU7118_REG_SPT_CX(chan),
    136					ADAU7118_SPT_SLOT_MASK,
    137					ADAU7118_SPT_SLOT(tx_slot[chan]));
    138		if (ret < 0)
    139			return ret;
    140	}
    141
    142	return 0;
    143}
    144
    145static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
    146{
    147	struct adau7118_data *st =
    148		snd_soc_component_get_drvdata(dai->component);
    149	int ret = 0;
    150	u32 regval;
    151
    152	dev_dbg(st->dev, "Set format, fmt:%d\n", fmt);
    153
    154	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    155	case SND_SOC_DAIFMT_I2S:
    156		ret = snd_soc_component_update_bits(dai->component,
    157						    ADAU7118_REG_SPT_CTRL1,
    158						    ADAU7118_DATA_FMT_MASK,
    159						    ADAU7118_DATA_FMT(0));
    160		break;
    161	case SND_SOC_DAIFMT_LEFT_J:
    162		ret = snd_soc_component_update_bits(dai->component,
    163						    ADAU7118_REG_SPT_CTRL1,
    164						    ADAU7118_DATA_FMT_MASK,
    165						    ADAU7118_DATA_FMT(1));
    166		break;
    167	case SND_SOC_DAIFMT_RIGHT_J:
    168		st->right_j = true;
    169		break;
    170	default:
    171		dev_err(st->dev, "Invalid format %d",
    172			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
    173		return -EINVAL;
    174	}
    175
    176	if (ret < 0)
    177		return ret;
    178
    179	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    180	case SND_SOC_DAIFMT_NB_NF:
    181		regval = ADAU7118_LRCLK_BCLK_POL(0);
    182		break;
    183	case SND_SOC_DAIFMT_NB_IF:
    184		regval = ADAU7118_LRCLK_BCLK_POL(2);
    185		break;
    186	case SND_SOC_DAIFMT_IB_NF:
    187		regval = ADAU7118_LRCLK_BCLK_POL(1);
    188		break;
    189	case SND_SOC_DAIFMT_IB_IF:
    190		regval = ADAU7118_LRCLK_BCLK_POL(3);
    191		break;
    192	default:
    193		dev_err(st->dev, "Invalid Inv mask %d",
    194			fmt & SND_SOC_DAIFMT_INV_MASK);
    195		return -EINVAL;
    196	}
    197
    198	ret = snd_soc_component_update_bits(dai->component,
    199					    ADAU7118_REG_SPT_CTRL2,
    200					    ADAU7118_LRCLK_BCLK_POL_MASK,
    201					    regval);
    202	if (ret < 0)
    203		return ret;
    204
    205	return 0;
    206}
    207
    208static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate)
    209{
    210	struct adau7118_data *st =
    211		snd_soc_component_get_drvdata(dai->component);
    212	int ret;
    213
    214	dev_dbg(st->dev, "Set tristate, %d\n", tristate);
    215
    216	ret = snd_soc_component_update_bits(dai->component,
    217					    ADAU7118_REG_SPT_CTRL1,
    218					    ADAU7118_TRISTATE_MASK,
    219					    ADAU7118_TRISTATE(tristate));
    220	if (ret < 0)
    221		return ret;
    222
    223	return 0;
    224}
    225
    226static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
    227				 unsigned int rx_mask, int slots,
    228				 int slot_width)
    229{
    230	struct adau7118_data *st =
    231		snd_soc_component_get_drvdata(dai->component);
    232	int ret = 0;
    233	u32 regval;
    234
    235	dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width);
    236
    237	switch (slot_width) {
    238	case 32:
    239		regval = ADAU7118_SLOT_WIDTH(0);
    240		break;
    241	case 24:
    242		regval = ADAU7118_SLOT_WIDTH(2);
    243		break;
    244	case 16:
    245		regval = ADAU7118_SLOT_WIDTH(1);
    246		break;
    247	default:
    248		dev_err(st->dev, "Invalid slot width:%d\n", slot_width);
    249		return -EINVAL;
    250	}
    251
    252	ret = snd_soc_component_update_bits(dai->component,
    253					    ADAU7118_REG_SPT_CTRL1,
    254					    ADAU7118_SLOT_WIDTH_MASK, regval);
    255	if (ret < 0)
    256		return ret;
    257
    258	st->slot_width = slot_width;
    259	st->slots = slots;
    260
    261	return 0;
    262}
    263
    264static int adau7118_hw_params(struct snd_pcm_substream *substream,
    265			      struct snd_pcm_hw_params *params,
    266			      struct snd_soc_dai *dai)
    267{
    268	struct adau7118_data *st =
    269		snd_soc_component_get_drvdata(dai->component);
    270	u32 data_width = params_width(params), slots_width;
    271	int ret;
    272	u32 regval;
    273
    274	if (!st->slots) {
    275		/* set stereo mode */
    276		ret = snd_soc_component_update_bits(dai->component,
    277						    ADAU7118_REG_SPT_CTRL1,
    278						    ADAU7118_SAI_MODE_MASK,
    279						    ADAU7118_SAI_MODE(0));
    280		if (ret < 0)
    281			return ret;
    282
    283		slots_width = 32;
    284	} else {
    285		slots_width = st->slot_width;
    286	}
    287
    288	if (data_width > slots_width) {
    289		dev_err(st->dev, "Invalid data_width:%d, slots_width:%d",
    290			data_width, slots_width);
    291		return -EINVAL;
    292	}
    293
    294	if (st->right_j) {
    295		switch (slots_width - data_width) {
    296		case 8:
    297			/* delay bclck by 8 */
    298			regval = ADAU7118_DATA_FMT(2);
    299			break;
    300		case 12:
    301			/* delay bclck by 12 */
    302			regval = ADAU7118_DATA_FMT(3);
    303			break;
    304		case 16:
    305			/* delay bclck by 16 */
    306			regval = ADAU7118_DATA_FMT(4);
    307			break;
    308		default:
    309			dev_err(st->dev,
    310				"Cannot set right_j setting, slot_w:%d, data_w:%d\n",
    311					slots_width, data_width);
    312			return -EINVAL;
    313		}
    314
    315		ret = snd_soc_component_update_bits(dai->component,
    316						    ADAU7118_REG_SPT_CTRL1,
    317						    ADAU7118_DATA_FMT_MASK,
    318						    regval);
    319		if (ret < 0)
    320			return ret;
    321	}
    322
    323	return 0;
    324}
    325
    326static int adau7118_set_bias_level(struct snd_soc_component *component,
    327				   enum snd_soc_bias_level level)
    328{
    329	struct adau7118_data *st = snd_soc_component_get_drvdata(component);
    330	int ret = 0;
    331
    332	dev_dbg(st->dev, "Set bias level %d\n", level);
    333
    334	switch (level) {
    335	case SND_SOC_BIAS_ON:
    336	case SND_SOC_BIAS_PREPARE:
    337		break;
    338
    339	case SND_SOC_BIAS_STANDBY:
    340		if (snd_soc_component_get_bias_level(component) ==
    341							SND_SOC_BIAS_OFF) {
    342			/* power on */
    343			ret = regulator_enable(st->iovdd);
    344			if (ret)
    345				return ret;
    346
    347			/* there's no timing constraints before enabling dvdd */
    348			ret = regulator_enable(st->dvdd);
    349			if (ret) {
    350				regulator_disable(st->iovdd);
    351				return ret;
    352			}
    353
    354			if (st->hw_mode)
    355				return 0;
    356
    357			regcache_cache_only(st->map, false);
    358			/* sync cache */
    359			ret = snd_soc_component_cache_sync(component);
    360		}
    361		break;
    362	case SND_SOC_BIAS_OFF:
    363		/* power off */
    364		ret = regulator_disable(st->dvdd);
    365		if (ret)
    366			return ret;
    367
    368		ret = regulator_disable(st->iovdd);
    369		if (ret)
    370			return ret;
    371
    372		if (st->hw_mode)
    373			return 0;
    374
    375		/* cache only */
    376		regcache_mark_dirty(st->map);
    377		regcache_cache_only(st->map, true);
    378
    379		break;
    380	}
    381
    382	return ret;
    383}
    384
    385static int adau7118_component_probe(struct snd_soc_component *component)
    386{
    387	struct adau7118_data *st = snd_soc_component_get_drvdata(component);
    388	struct snd_soc_dapm_context *dapm =
    389					snd_soc_component_get_dapm(component);
    390	int ret = 0;
    391
    392	if (st->hw_mode) {
    393		ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw,
    394					ARRAY_SIZE(adau7118_widgets_hw));
    395		if (ret)
    396			return ret;
    397
    398		ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw,
    399					      ARRAY_SIZE(adau7118_routes_hw));
    400	} else {
    401		snd_soc_component_init_regmap(component, st->map);
    402		ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw,
    403					ARRAY_SIZE(adau7118_widgets_sw));
    404		if (ret)
    405			return ret;
    406
    407		ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw,
    408					      ARRAY_SIZE(adau7118_routes_sw));
    409	}
    410
    411	return ret;
    412}
    413
    414static const struct snd_soc_dai_ops adau7118_ops = {
    415	.hw_params = adau7118_hw_params,
    416	.set_channel_map = adau7118_set_channel_map,
    417	.set_fmt = adau7118_set_fmt,
    418	.set_tdm_slot = adau7118_set_tdm_slot,
    419	.set_tristate = adau7118_set_tristate,
    420};
    421
    422static struct snd_soc_dai_driver adau7118_dai = {
    423	.name = "adau7118-hifi-capture",
    424	.capture = {
    425		.stream_name = "Capture",
    426		.channels_min = 1,
    427		.channels_max = 8,
    428		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
    429			SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
    430			SNDRV_PCM_FMTBIT_S24_3LE,
    431		.rates = SNDRV_PCM_RATE_CONTINUOUS,
    432		.rate_min = 4000,
    433		.rate_max = 192000,
    434		.sig_bits = 24,
    435	},
    436};
    437
    438static const struct snd_soc_component_driver adau7118_component_driver = {
    439	.probe			= adau7118_component_probe,
    440	.set_bias_level		= adau7118_set_bias_level,
    441	.dapm_widgets		= adau7118_widgets,
    442	.num_dapm_widgets	= ARRAY_SIZE(adau7118_widgets),
    443	.use_pmdown_time	= 1,
    444	.endianness		= 1,
    445	.non_legacy_dai_naming	= 1,
    446};
    447
    448static void adau7118_regulator_disable(void *data)
    449{
    450	struct adau7118_data *st = data;
    451	int ret;
    452	/*
    453	 * If we fail to disable DVDD, don't bother in trying IOVDD. We
    454	 * actually don't want to be left in the situation where DVDD
    455	 * is enabled and IOVDD is disabled.
    456	 */
    457	ret = regulator_disable(st->dvdd);
    458	if (ret)
    459		return;
    460
    461	regulator_disable(st->iovdd);
    462}
    463
    464static int adau7118_regulator_setup(struct adau7118_data *st)
    465{
    466	st->iovdd = devm_regulator_get(st->dev, "iovdd");
    467	if (IS_ERR(st->iovdd)) {
    468		dev_err(st->dev, "Could not get iovdd: %ld\n",
    469			PTR_ERR(st->iovdd));
    470		return PTR_ERR(st->iovdd);
    471	}
    472
    473	st->dvdd = devm_regulator_get(st->dev, "dvdd");
    474	if (IS_ERR(st->dvdd)) {
    475		dev_err(st->dev, "Could not get dvdd: %ld\n",
    476			PTR_ERR(st->dvdd));
    477		return PTR_ERR(st->dvdd);
    478	}
    479	/* just assume the device is in reset */
    480	if (!st->hw_mode) {
    481		regcache_mark_dirty(st->map);
    482		regcache_cache_only(st->map, true);
    483	}
    484
    485	return devm_add_action_or_reset(st->dev, adau7118_regulator_disable,
    486					st);
    487}
    488
    489static int adau7118_parset_dt(const struct adau7118_data *st)
    490{
    491	int ret;
    492	u32 dec_ratio = 0;
    493	/* 4 inputs */
    494	u32 clk_map[4], regval;
    495
    496	if (st->hw_mode)
    497		return 0;
    498
    499	ret = device_property_read_u32(st->dev, "adi,decimation-ratio",
    500				       &dec_ratio);
    501	if (!ret) {
    502		switch (dec_ratio) {
    503		case 64:
    504			regval = ADAU7118_DEC_RATIO(0);
    505			break;
    506		case 32:
    507			regval = ADAU7118_DEC_RATIO(1);
    508			break;
    509		case 16:
    510			regval = ADAU7118_DEC_RATIO(2);
    511			break;
    512		default:
    513			dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio);
    514			return -EINVAL;
    515		}
    516
    517		ret = regmap_update_bits(st->map,
    518					 ADAU7118_REG_DEC_RATIO_CLK_MAP,
    519					 ADAU7118_DEC_RATIO_MASK, regval);
    520		if (ret)
    521			return ret;
    522	}
    523
    524	ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map",
    525					     clk_map, ARRAY_SIZE(clk_map));
    526	if (!ret) {
    527		int pdm;
    528		u32 _clk_map = 0;
    529
    530		for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++)
    531			_clk_map |= (clk_map[pdm] << (pdm + 4));
    532
    533		ret = regmap_update_bits(st->map,
    534					 ADAU7118_REG_DEC_RATIO_CLK_MAP,
    535					 ADAU7118_CLK_MAP_MASK, _clk_map);
    536		if (ret)
    537			return ret;
    538	}
    539
    540	return 0;
    541}
    542
    543int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode)
    544{
    545	struct adau7118_data *st;
    546	int ret;
    547
    548	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
    549	if (!st)
    550		return -ENOMEM;
    551
    552	st->dev = dev;
    553	st->hw_mode = hw_mode;
    554	dev_set_drvdata(dev, st);
    555
    556	if (!hw_mode) {
    557		st->map = map;
    558		adau7118_dai.ops = &adau7118_ops;
    559		/*
    560		 * Perform a full soft reset. This will set all register's
    561		 * with their reset values.
    562		 */
    563		ret = regmap_update_bits(map, ADAU7118_REG_RESET,
    564					 ADAU7118_FULL_SOFT_R_MASK,
    565					 ADAU7118_FULL_SOFT_R(1));
    566		if (ret)
    567			return ret;
    568	}
    569
    570	ret = adau7118_parset_dt(st);
    571	if (ret)
    572		return ret;
    573
    574	ret = adau7118_regulator_setup(st);
    575	if (ret)
    576		return ret;
    577
    578	return devm_snd_soc_register_component(dev,
    579					       &adau7118_component_driver,
    580					       &adau7118_dai, 1);
    581}
    582EXPORT_SYMBOL_GPL(adau7118_probe);
    583
    584MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
    585MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver");
    586MODULE_LICENSE("GPL");