brintos

brintos / linux-shallow public Read only

0
0
Text · 14.2 KiB · abc4764 Raw
571 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver4//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,125				    const unsigned int *tx_slot,126				    unsigned int rx_num,127				    const unsigned int *rx_slot)128{129	struct adau7118_data *st =130		snd_soc_component_get_drvdata(dai->component);131	int chan, ret;132 133	dev_dbg(st->dev, "Set channel map, %d", tx_num);134 135	for (chan = 0; chan < tx_num; chan++) {136		ret = snd_soc_component_update_bits(dai->component,137					ADAU7118_REG_SPT_CX(chan),138					ADAU7118_SPT_SLOT_MASK,139					ADAU7118_SPT_SLOT(tx_slot[chan]));140		if (ret < 0)141			return ret;142	}143 144	return 0;145}146 147static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)148{149	struct adau7118_data *st =150		snd_soc_component_get_drvdata(dai->component);151	int ret = 0;152	u32 regval;153 154	dev_dbg(st->dev, "Set format, fmt:%d\n", fmt);155 156	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {157	case SND_SOC_DAIFMT_I2S:158		ret = snd_soc_component_update_bits(dai->component,159						    ADAU7118_REG_SPT_CTRL1,160						    ADAU7118_DATA_FMT_MASK,161						    ADAU7118_DATA_FMT(0));162		break;163	case SND_SOC_DAIFMT_LEFT_J:164		ret = snd_soc_component_update_bits(dai->component,165						    ADAU7118_REG_SPT_CTRL1,166						    ADAU7118_DATA_FMT_MASK,167						    ADAU7118_DATA_FMT(1));168		break;169	case SND_SOC_DAIFMT_RIGHT_J:170		st->right_j = true;171		break;172	default:173		dev_err(st->dev, "Invalid format %d",174			fmt & SND_SOC_DAIFMT_FORMAT_MASK);175		return -EINVAL;176	}177 178	if (ret < 0)179		return ret;180 181	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {182	case SND_SOC_DAIFMT_NB_NF:183		regval = ADAU7118_LRCLK_BCLK_POL(0);184		break;185	case SND_SOC_DAIFMT_NB_IF:186		regval = ADAU7118_LRCLK_BCLK_POL(2);187		break;188	case SND_SOC_DAIFMT_IB_NF:189		regval = ADAU7118_LRCLK_BCLK_POL(1);190		break;191	case SND_SOC_DAIFMT_IB_IF:192		regval = ADAU7118_LRCLK_BCLK_POL(3);193		break;194	default:195		dev_err(st->dev, "Invalid Inv mask %d",196			fmt & SND_SOC_DAIFMT_INV_MASK);197		return -EINVAL;198	}199 200	ret = snd_soc_component_update_bits(dai->component,201					    ADAU7118_REG_SPT_CTRL2,202					    ADAU7118_LRCLK_BCLK_POL_MASK,203					    regval);204	if (ret < 0)205		return ret;206 207	return 0;208}209 210static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate)211{212	struct adau7118_data *st =213		snd_soc_component_get_drvdata(dai->component);214	int ret;215 216	dev_dbg(st->dev, "Set tristate, %d\n", tristate);217 218	ret = snd_soc_component_update_bits(dai->component,219					    ADAU7118_REG_SPT_CTRL1,220					    ADAU7118_TRISTATE_MASK,221					    ADAU7118_TRISTATE(tristate));222	if (ret < 0)223		return ret;224 225	return 0;226}227 228static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,229				 unsigned int rx_mask, int slots,230				 int slot_width)231{232	struct adau7118_data *st =233		snd_soc_component_get_drvdata(dai->component);234	int ret = 0;235	u32 regval;236 237	dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width);238 239	switch (slot_width) {240	case 32:241		regval = ADAU7118_SLOT_WIDTH(0);242		break;243	case 24:244		regval = ADAU7118_SLOT_WIDTH(2);245		break;246	case 16:247		regval = ADAU7118_SLOT_WIDTH(1);248		break;249	default:250		dev_err(st->dev, "Invalid slot width:%d\n", slot_width);251		return -EINVAL;252	}253 254	ret = snd_soc_component_update_bits(dai->component,255					    ADAU7118_REG_SPT_CTRL1,256					    ADAU7118_SLOT_WIDTH_MASK, regval);257	if (ret < 0)258		return ret;259 260	st->slot_width = slot_width;261	st->slots = slots;262 263	return 0;264}265 266static int adau7118_hw_params(struct snd_pcm_substream *substream,267			      struct snd_pcm_hw_params *params,268			      struct snd_soc_dai *dai)269{270	struct adau7118_data *st =271		snd_soc_component_get_drvdata(dai->component);272	u32 data_width = params_width(params), slots_width;273	int ret;274	u32 regval;275 276	if (!st->slots) {277		/* set stereo mode */278		ret = snd_soc_component_update_bits(dai->component,279						    ADAU7118_REG_SPT_CTRL1,280						    ADAU7118_SAI_MODE_MASK,281						    ADAU7118_SAI_MODE(0));282		if (ret < 0)283			return ret;284 285		slots_width = 32;286	} else {287		slots_width = st->slot_width;288	}289 290	if (data_width > slots_width) {291		dev_err(st->dev, "Invalid data_width:%d, slots_width:%d",292			data_width, slots_width);293		return -EINVAL;294	}295 296	if (st->right_j) {297		switch (slots_width - data_width) {298		case 8:299			/* delay bclck by 8 */300			regval = ADAU7118_DATA_FMT(2);301			break;302		case 12:303			/* delay bclck by 12 */304			regval = ADAU7118_DATA_FMT(3);305			break;306		case 16:307			/* delay bclck by 16 */308			regval = ADAU7118_DATA_FMT(4);309			break;310		default:311			dev_err(st->dev,312				"Cannot set right_j setting, slot_w:%d, data_w:%d\n",313					slots_width, data_width);314			return -EINVAL;315		}316 317		ret = snd_soc_component_update_bits(dai->component,318						    ADAU7118_REG_SPT_CTRL1,319						    ADAU7118_DATA_FMT_MASK,320						    regval);321		if (ret < 0)322			return ret;323	}324 325	return 0;326}327 328static int adau7118_set_bias_level(struct snd_soc_component *component,329				   enum snd_soc_bias_level level)330{331	struct adau7118_data *st = snd_soc_component_get_drvdata(component);332	int ret = 0;333 334	dev_dbg(st->dev, "Set bias level %d\n", level);335 336	switch (level) {337	case SND_SOC_BIAS_ON:338	case SND_SOC_BIAS_PREPARE:339		break;340 341	case SND_SOC_BIAS_STANDBY:342		if (snd_soc_component_get_bias_level(component) ==343							SND_SOC_BIAS_OFF) {344			/* power on */345			ret = regulator_enable(st->iovdd);346			if (ret)347				return ret;348 349			/* there's no timing constraints before enabling dvdd */350			ret = regulator_enable(st->dvdd);351			if (ret) {352				regulator_disable(st->iovdd);353				return ret;354			}355 356			if (st->hw_mode)357				return 0;358 359			regcache_cache_only(st->map, false);360			/* sync cache */361			ret = snd_soc_component_cache_sync(component);362		}363		break;364	case SND_SOC_BIAS_OFF:365		/* power off */366		ret = regulator_disable(st->dvdd);367		if (ret)368			return ret;369 370		ret = regulator_disable(st->iovdd);371		if (ret)372			return ret;373 374		if (st->hw_mode)375			return 0;376 377		/* cache only */378		regcache_mark_dirty(st->map);379		regcache_cache_only(st->map, true);380 381		break;382	}383 384	return ret;385}386 387static int adau7118_component_probe(struct snd_soc_component *component)388{389	struct adau7118_data *st = snd_soc_component_get_drvdata(component);390	struct snd_soc_dapm_context *dapm =391					snd_soc_component_get_dapm(component);392	int ret = 0;393 394	if (st->hw_mode) {395		ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw,396					ARRAY_SIZE(adau7118_widgets_hw));397		if (ret)398			return ret;399 400		ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw,401					      ARRAY_SIZE(adau7118_routes_hw));402	} else {403		snd_soc_component_init_regmap(component, st->map);404		ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw,405					ARRAY_SIZE(adau7118_widgets_sw));406		if (ret)407			return ret;408 409		ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw,410					      ARRAY_SIZE(adau7118_routes_sw));411	}412 413	return ret;414}415 416static const struct snd_soc_dai_ops adau7118_ops = {417	.hw_params = adau7118_hw_params,418	.set_channel_map = adau7118_set_channel_map,419	.set_fmt = adau7118_set_fmt,420	.set_tdm_slot = adau7118_set_tdm_slot,421	.set_tristate = adau7118_set_tristate,422};423 424static struct snd_soc_dai_driver adau7118_dai = {425	.name = "adau7118-hifi-capture",426	.capture = {427		.stream_name = "Capture",428		.channels_min = 1,429		.channels_max = 8,430		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |431			SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |432			SNDRV_PCM_FMTBIT_S24_3LE,433		.rates = SNDRV_PCM_RATE_CONTINUOUS,434		.rate_min = 4000,435		.rate_max = 192000,436		.sig_bits = 24,437	},438};439 440static const struct snd_soc_component_driver adau7118_component_driver = {441	.probe			= adau7118_component_probe,442	.set_bias_level		= adau7118_set_bias_level,443	.dapm_widgets		= adau7118_widgets,444	.num_dapm_widgets	= ARRAY_SIZE(adau7118_widgets),445	.use_pmdown_time	= 1,446	.endianness		= 1,447};448 449static int adau7118_regulator_setup(struct adau7118_data *st)450{451	st->iovdd = devm_regulator_get(st->dev, "iovdd");452	if (IS_ERR(st->iovdd)) {453		dev_err(st->dev, "Could not get iovdd: %ld\n",454			PTR_ERR(st->iovdd));455		return PTR_ERR(st->iovdd);456	}457 458	st->dvdd = devm_regulator_get(st->dev, "dvdd");459	if (IS_ERR(st->dvdd)) {460		dev_err(st->dev, "Could not get dvdd: %ld\n",461			PTR_ERR(st->dvdd));462		return PTR_ERR(st->dvdd);463	}464	/* just assume the device is in reset */465	if (!st->hw_mode) {466		regcache_mark_dirty(st->map);467		regcache_cache_only(st->map, true);468	}469 470	return 0;471}472 473static int adau7118_parset_dt(const struct adau7118_data *st)474{475	int ret;476	u32 dec_ratio = 0;477	/* 4 inputs */478	u32 clk_map[4], regval;479 480	if (st->hw_mode)481		return 0;482 483	ret = device_property_read_u32(st->dev, "adi,decimation-ratio",484				       &dec_ratio);485	if (!ret) {486		switch (dec_ratio) {487		case 64:488			regval = ADAU7118_DEC_RATIO(0);489			break;490		case 32:491			regval = ADAU7118_DEC_RATIO(1);492			break;493		case 16:494			regval = ADAU7118_DEC_RATIO(2);495			break;496		default:497			dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio);498			return -EINVAL;499		}500 501		ret = regmap_update_bits(st->map,502					 ADAU7118_REG_DEC_RATIO_CLK_MAP,503					 ADAU7118_DEC_RATIO_MASK, regval);504		if (ret)505			return ret;506	}507 508	ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map",509					     clk_map, ARRAY_SIZE(clk_map));510	if (!ret) {511		int pdm;512		u32 _clk_map = 0;513 514		for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++)515			_clk_map |= (clk_map[pdm] << (pdm + 4));516 517		ret = regmap_update_bits(st->map,518					 ADAU7118_REG_DEC_RATIO_CLK_MAP,519					 ADAU7118_CLK_MAP_MASK, _clk_map);520		if (ret)521			return ret;522	}523 524	return 0;525}526 527int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode)528{529	struct adau7118_data *st;530	int ret;531 532	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);533	if (!st)534		return -ENOMEM;535 536	st->dev = dev;537	st->hw_mode = hw_mode;538	dev_set_drvdata(dev, st);539 540	if (!hw_mode) {541		st->map = map;542		adau7118_dai.ops = &adau7118_ops;543		/*544		 * Perform a full soft reset. This will set all register's545		 * with their reset values.546		 */547		ret = regmap_update_bits(map, ADAU7118_REG_RESET,548					 ADAU7118_FULL_SOFT_R_MASK,549					 ADAU7118_FULL_SOFT_R(1));550		if (ret)551			return ret;552	}553 554	ret = adau7118_parset_dt(st);555	if (ret)556		return ret;557 558	ret = adau7118_regulator_setup(st);559	if (ret)560		return ret;561 562	return devm_snd_soc_register_component(dev,563					       &adau7118_component_driver,564					       &adau7118_dai, 1);565}566EXPORT_SYMBOL_GPL(adau7118_probe);567 568MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");569MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver");570MODULE_LICENSE("GPL");571