brintos

brintos / linux-shallow public Read only

0
0
Text · 15.2 KiB · 109f763 Raw
542 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// tegra210_adx.c - Tegra210 ADX driver4//5// Copyright (c) 2021-2023 NVIDIA CORPORATION.  All rights reserved.6 7#include <linux/clk.h>8#include <linux/device.h>9#include <linux/io.h>10#include <linux/mod_devicetable.h>11#include <linux/module.h>12#include <linux/platform_device.h>13#include <linux/pm_runtime.h>14#include <linux/regmap.h>15#include <sound/core.h>16#include <sound/pcm.h>17#include <sound/pcm_params.h>18#include <sound/soc.h>19 20#include "tegra210_adx.h"21#include "tegra_cif.h"22 23static const struct reg_default tegra210_adx_reg_defaults[] = {24	{ TEGRA210_ADX_RX_INT_MASK, 0x00000001},25	{ TEGRA210_ADX_RX_CIF_CTRL, 0x00007000},26	{ TEGRA210_ADX_TX_INT_MASK, 0x0000000f },27	{ TEGRA210_ADX_TX1_CIF_CTRL, 0x00007000},28	{ TEGRA210_ADX_TX2_CIF_CTRL, 0x00007000},29	{ TEGRA210_ADX_TX3_CIF_CTRL, 0x00007000},30	{ TEGRA210_ADX_TX4_CIF_CTRL, 0x00007000},31	{ TEGRA210_ADX_CG, 0x1},32	{ TEGRA210_ADX_CFG_RAM_CTRL, 0x00004000},33};34 35static void tegra210_adx_write_map_ram(struct tegra210_adx *adx)36{37	int i;38 39	regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL,40		     TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN |41		     TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN |42		     TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE);43 44	for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++)45		regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA,46			     adx->map[i]);47 48	regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN0, adx->byte_mask[0]);49	regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN1, adx->byte_mask[1]);50}51 52static int tegra210_adx_startup(struct snd_pcm_substream *substream,53				struct snd_soc_dai *dai)54{55	struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);56	unsigned int val;57	int err;58 59	/* Ensure if ADX status is disabled */60	err = regmap_read_poll_timeout_atomic(adx->regmap, TEGRA210_ADX_STATUS,61					      val, !(val & 0x1), 10, 10000);62	if (err < 0) {63		dev_err(dai->dev, "failed to stop ADX, err = %d\n", err);64		return err;65	}66 67	/*68	 * Soft Reset: Below performs module soft reset which clears69	 * all FSM logic, flushes flow control of FIFO and resets the70	 * state register. It also brings module back to disabled71	 * state (without flushing the data in the pipe).72	 */73	regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET,74			   TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK,75			   TEGRA210_ADX_SOFT_RESET_SOFT_EN);76 77	err = regmap_read_poll_timeout(adx->regmap, TEGRA210_ADX_SOFT_RESET,78				       val, !(val & 0x1), 10, 10000);79	if (err < 0) {80		dev_err(dai->dev, "failed to reset ADX, err = %d\n", err);81		return err;82	}83 84	return 0;85}86 87static int __maybe_unused tegra210_adx_runtime_suspend(struct device *dev)88{89	struct tegra210_adx *adx = dev_get_drvdata(dev);90 91	regcache_cache_only(adx->regmap, true);92	regcache_mark_dirty(adx->regmap);93 94	return 0;95}96 97static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev)98{99	struct tegra210_adx *adx = dev_get_drvdata(dev);100 101	regcache_cache_only(adx->regmap, false);102	regcache_sync(adx->regmap);103 104	tegra210_adx_write_map_ram(adx);105 106	return 0;107}108 109static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai,110				      unsigned int channels,111				      snd_pcm_format_t format,112				      unsigned int reg)113{114	struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);115	struct tegra_cif_conf cif_conf;116	int audio_bits;117 118	memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));119 120	if (channels < 1 || channels > 16)121		return -EINVAL;122 123	switch (format) {124	case SNDRV_PCM_FORMAT_S8:125		audio_bits = TEGRA_ACIF_BITS_8;126		break;127	case SNDRV_PCM_FORMAT_S16_LE:128		audio_bits = TEGRA_ACIF_BITS_16;129		break;130	case SNDRV_PCM_FORMAT_S32_LE:131		audio_bits = TEGRA_ACIF_BITS_32;132		break;133	default:134		return -EINVAL;135	}136 137	cif_conf.audio_ch = channels;138	cif_conf.client_ch = channels;139	cif_conf.audio_bits = audio_bits;140	cif_conf.client_bits = audio_bits;141 142	tegra_set_cif(adx->regmap, reg, &cif_conf);143 144	return 0;145}146 147static int tegra210_adx_out_hw_params(struct snd_pcm_substream *substream,148				      struct snd_pcm_hw_params *params,149				      struct snd_soc_dai *dai)150{151	return tegra210_adx_set_audio_cif(dai, params_channels(params),152			params_format(params),153			TEGRA210_ADX_TX1_CIF_CTRL + ((dai->id - 1) * TEGRA210_ADX_AUDIOCIF_CH_STRIDE));154}155 156static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream,157				     struct snd_pcm_hw_params *params,158				     struct snd_soc_dai *dai)159{160	return tegra210_adx_set_audio_cif(dai, params_channels(params),161					  params_format(params),162					  TEGRA210_ADX_RX_CIF_CTRL);163}164 165static int tegra210_adx_get_byte_map(struct snd_kcontrol *kcontrol,166				     struct snd_ctl_elem_value *ucontrol)167{168	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);169	struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);170	struct soc_mixer_control *mc;171	unsigned char *bytes_map = (unsigned char *)&adx->map;172	int enabled;173 174	mc = (struct soc_mixer_control *)kcontrol->private_value;175	enabled = adx->byte_mask[mc->reg / 32] & (1 << (mc->reg % 32));176 177	/*178	 * TODO: Simplify this logic to just return from bytes_map[]179	 *180	 * Presently below is required since bytes_map[] is181	 * tightly packed and cannot store the control value of 256.182	 * Byte mask state is used to know if 256 needs to be returned.183	 * Note that for control value of 256, the put() call stores 0184	 * in the bytes_map[] and disables the corresponding bit in185	 * byte_mask[].186	 */187	if (enabled)188		ucontrol->value.integer.value[0] = bytes_map[mc->reg];189	else190		ucontrol->value.integer.value[0] = 256;191 192	return 0;193}194 195static int tegra210_adx_put_byte_map(struct snd_kcontrol *kcontrol,196				     struct snd_ctl_elem_value *ucontrol)197{198	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);199	struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);200	unsigned char *bytes_map = (unsigned char *)&adx->map;201	int value = ucontrol->value.integer.value[0];202	struct soc_mixer_control *mc =203		(struct soc_mixer_control *)kcontrol->private_value;204	unsigned int mask_val = adx->byte_mask[mc->reg / 32];205 206	if (value >= 0 && value <= 255)207		mask_val |= (1 << (mc->reg % 32));208	else209		mask_val &= ~(1 << (mc->reg % 32));210 211	if (mask_val == adx->byte_mask[mc->reg / 32])212		return 0;213 214	/* Update byte map and slot */215	bytes_map[mc->reg] = value % 256;216	adx->byte_mask[mc->reg / 32] = mask_val;217 218	return 1;219}220 221static const struct snd_soc_dai_ops tegra210_adx_in_dai_ops = {222	.hw_params	= tegra210_adx_in_hw_params,223	.startup	= tegra210_adx_startup,224};225 226static const struct snd_soc_dai_ops tegra210_adx_out_dai_ops = {227	.hw_params	= tegra210_adx_out_hw_params,228};229 230#define IN_DAI							\231	{							\232		.name = "ADX-RX-CIF",				\233		.playback = {					\234			.stream_name = "RX-CIF-Playback",	\235			.channels_min = 1,			\236			.channels_max = 16,			\237			.rates = SNDRV_PCM_RATE_8000_192000,	\238			.formats = SNDRV_PCM_FMTBIT_S8 |	\239				   SNDRV_PCM_FMTBIT_S16_LE |	\240				   SNDRV_PCM_FMTBIT_S32_LE,	\241		},						\242		.capture = {					\243			.stream_name = "RX-CIF-Capture",	\244			.channels_min = 1,			\245			.channels_max = 16,			\246			.rates = SNDRV_PCM_RATE_8000_192000,	\247			.formats = SNDRV_PCM_FMTBIT_S8 |	\248				   SNDRV_PCM_FMTBIT_S16_LE |	\249				   SNDRV_PCM_FMTBIT_S32_LE,	\250		},						\251		.ops = &tegra210_adx_in_dai_ops,		\252	}253 254#define OUT_DAI(id)						\255	{							\256		.name = "ADX-TX" #id "-CIF",			\257		.playback = {					\258			.stream_name = "TX" #id "-CIF-Playback",\259			.channels_min = 1,			\260			.channels_max = 16,			\261			.rates = SNDRV_PCM_RATE_8000_192000,	\262			.formats = SNDRV_PCM_FMTBIT_S8 |	\263				   SNDRV_PCM_FMTBIT_S16_LE |	\264				   SNDRV_PCM_FMTBIT_S32_LE,	\265		},						\266		.capture = {					\267			.stream_name = "TX" #id "-CIF-Capture",	\268			.channels_min = 1,			\269			.channels_max = 16,			\270			.rates = SNDRV_PCM_RATE_8000_192000,	\271			.formats = SNDRV_PCM_FMTBIT_S8 |	\272				   SNDRV_PCM_FMTBIT_S16_LE |	\273				   SNDRV_PCM_FMTBIT_S32_LE,	\274		},						\275		.ops = &tegra210_adx_out_dai_ops,		\276	}277 278static struct snd_soc_dai_driver tegra210_adx_dais[] = {279	IN_DAI,280	OUT_DAI(1),281	OUT_DAI(2),282	OUT_DAI(3),283	OUT_DAI(4),284};285 286static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = {287	SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA210_ADX_ENABLE,288			    TEGRA210_ADX_ENABLE_SHIFT, 0),289	SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0),290	SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0),291	SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0),292	SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0),293};294 295#define STREAM_ROUTES(id, sname)					  \296	{ "XBAR-" sname,		NULL,	"XBAR-TX" },		  \297	{ "RX-CIF-" sname,		NULL,	"XBAR-" sname },	  \298	{ "RX",				NULL,	"RX-CIF-" sname },	  \299	{ "TX" #id,			NULL,	"RX" },			  \300	{ "TX" #id "-CIF-" sname,	NULL,	"TX" #id },		  \301	{ "TX" #id " XBAR-" sname,	NULL,	"TX" #id "-CIF-" sname }, \302	{ "TX" #id " XBAR-RX",		NULL,	"TX" #id " XBAR-" sname }303 304#define ADX_ROUTES(id)			\305	STREAM_ROUTES(id, "Playback"),	\306	STREAM_ROUTES(id, "Capture")307 308#define STREAM_ROUTES(id, sname)					  \309	{ "XBAR-" sname,		NULL,	"XBAR-TX" },		  \310	{ "RX-CIF-" sname,		NULL,	"XBAR-" sname },	  \311	{ "RX",				NULL,	"RX-CIF-" sname },	  \312	{ "TX" #id,			NULL,	"RX" },			  \313	{ "TX" #id "-CIF-" sname,	NULL,	"TX" #id },		  \314	{ "TX" #id " XBAR-" sname,	NULL,	"TX" #id "-CIF-" sname }, \315	{ "TX" #id " XBAR-RX",		NULL,	"TX" #id " XBAR-" sname }316 317#define ADX_ROUTES(id)			\318	STREAM_ROUTES(id, "Playback"),	\319	STREAM_ROUTES(id, "Capture")320 321static const struct snd_soc_dapm_route tegra210_adx_routes[] = {322	ADX_ROUTES(1),323	ADX_ROUTES(2),324	ADX_ROUTES(3),325	ADX_ROUTES(4),326};327 328#define TEGRA210_ADX_BYTE_MAP_CTRL(reg)			 \329	SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \330		       tegra210_adx_get_byte_map,	 \331		       tegra210_adx_put_byte_map)332 333static struct snd_kcontrol_new tegra210_adx_controls[] = {334	TEGRA210_ADX_BYTE_MAP_CTRL(0),335	TEGRA210_ADX_BYTE_MAP_CTRL(1),336	TEGRA210_ADX_BYTE_MAP_CTRL(2),337	TEGRA210_ADX_BYTE_MAP_CTRL(3),338	TEGRA210_ADX_BYTE_MAP_CTRL(4),339	TEGRA210_ADX_BYTE_MAP_CTRL(5),340	TEGRA210_ADX_BYTE_MAP_CTRL(6),341	TEGRA210_ADX_BYTE_MAP_CTRL(7),342	TEGRA210_ADX_BYTE_MAP_CTRL(8),343	TEGRA210_ADX_BYTE_MAP_CTRL(9),344	TEGRA210_ADX_BYTE_MAP_CTRL(10),345	TEGRA210_ADX_BYTE_MAP_CTRL(11),346	TEGRA210_ADX_BYTE_MAP_CTRL(12),347	TEGRA210_ADX_BYTE_MAP_CTRL(13),348	TEGRA210_ADX_BYTE_MAP_CTRL(14),349	TEGRA210_ADX_BYTE_MAP_CTRL(15),350	TEGRA210_ADX_BYTE_MAP_CTRL(16),351	TEGRA210_ADX_BYTE_MAP_CTRL(17),352	TEGRA210_ADX_BYTE_MAP_CTRL(18),353	TEGRA210_ADX_BYTE_MAP_CTRL(19),354	TEGRA210_ADX_BYTE_MAP_CTRL(20),355	TEGRA210_ADX_BYTE_MAP_CTRL(21),356	TEGRA210_ADX_BYTE_MAP_CTRL(22),357	TEGRA210_ADX_BYTE_MAP_CTRL(23),358	TEGRA210_ADX_BYTE_MAP_CTRL(24),359	TEGRA210_ADX_BYTE_MAP_CTRL(25),360	TEGRA210_ADX_BYTE_MAP_CTRL(26),361	TEGRA210_ADX_BYTE_MAP_CTRL(27),362	TEGRA210_ADX_BYTE_MAP_CTRL(28),363	TEGRA210_ADX_BYTE_MAP_CTRL(29),364	TEGRA210_ADX_BYTE_MAP_CTRL(30),365	TEGRA210_ADX_BYTE_MAP_CTRL(31),366	TEGRA210_ADX_BYTE_MAP_CTRL(32),367	TEGRA210_ADX_BYTE_MAP_CTRL(33),368	TEGRA210_ADX_BYTE_MAP_CTRL(34),369	TEGRA210_ADX_BYTE_MAP_CTRL(35),370	TEGRA210_ADX_BYTE_MAP_CTRL(36),371	TEGRA210_ADX_BYTE_MAP_CTRL(37),372	TEGRA210_ADX_BYTE_MAP_CTRL(38),373	TEGRA210_ADX_BYTE_MAP_CTRL(39),374	TEGRA210_ADX_BYTE_MAP_CTRL(40),375	TEGRA210_ADX_BYTE_MAP_CTRL(41),376	TEGRA210_ADX_BYTE_MAP_CTRL(42),377	TEGRA210_ADX_BYTE_MAP_CTRL(43),378	TEGRA210_ADX_BYTE_MAP_CTRL(44),379	TEGRA210_ADX_BYTE_MAP_CTRL(45),380	TEGRA210_ADX_BYTE_MAP_CTRL(46),381	TEGRA210_ADX_BYTE_MAP_CTRL(47),382	TEGRA210_ADX_BYTE_MAP_CTRL(48),383	TEGRA210_ADX_BYTE_MAP_CTRL(49),384	TEGRA210_ADX_BYTE_MAP_CTRL(50),385	TEGRA210_ADX_BYTE_MAP_CTRL(51),386	TEGRA210_ADX_BYTE_MAP_CTRL(52),387	TEGRA210_ADX_BYTE_MAP_CTRL(53),388	TEGRA210_ADX_BYTE_MAP_CTRL(54),389	TEGRA210_ADX_BYTE_MAP_CTRL(55),390	TEGRA210_ADX_BYTE_MAP_CTRL(56),391	TEGRA210_ADX_BYTE_MAP_CTRL(57),392	TEGRA210_ADX_BYTE_MAP_CTRL(58),393	TEGRA210_ADX_BYTE_MAP_CTRL(59),394	TEGRA210_ADX_BYTE_MAP_CTRL(60),395	TEGRA210_ADX_BYTE_MAP_CTRL(61),396	TEGRA210_ADX_BYTE_MAP_CTRL(62),397	TEGRA210_ADX_BYTE_MAP_CTRL(63),398};399 400static const struct snd_soc_component_driver tegra210_adx_cmpnt = {401	.dapm_widgets		= tegra210_adx_widgets,402	.num_dapm_widgets	= ARRAY_SIZE(tegra210_adx_widgets),403	.dapm_routes		= tegra210_adx_routes,404	.num_dapm_routes	= ARRAY_SIZE(tegra210_adx_routes),405	.controls		= tegra210_adx_controls,406	.num_controls		= ARRAY_SIZE(tegra210_adx_controls),407};408 409static bool tegra210_adx_wr_reg(struct device *dev,410				unsigned int reg)411{412	switch (reg) {413	case TEGRA210_ADX_TX_INT_MASK ... TEGRA210_ADX_TX4_CIF_CTRL:414	case TEGRA210_ADX_RX_INT_MASK ... TEGRA210_ADX_RX_CIF_CTRL:415	case TEGRA210_ADX_ENABLE ... TEGRA210_ADX_CG:416	case TEGRA210_ADX_CTRL ... TEGRA210_ADX_IN_BYTE_EN1:417	case TEGRA210_ADX_CFG_RAM_CTRL ... TEGRA210_ADX_CFG_RAM_DATA:418		return true;419	default:420		return false;421	}422}423 424static bool tegra210_adx_rd_reg(struct device *dev,425				unsigned int reg)426{427	switch (reg) {428	case TEGRA210_ADX_RX_STATUS ... TEGRA210_ADX_CFG_RAM_DATA:429		return true;430	default:431		return false;432	}433}434 435static bool tegra210_adx_volatile_reg(struct device *dev,436				unsigned int reg)437{438	switch (reg) {439	case TEGRA210_ADX_RX_STATUS:440	case TEGRA210_ADX_RX_INT_STATUS:441	case TEGRA210_ADX_RX_INT_SET:442	case TEGRA210_ADX_TX_STATUS:443	case TEGRA210_ADX_TX_INT_STATUS:444	case TEGRA210_ADX_TX_INT_SET:445	case TEGRA210_ADX_SOFT_RESET:446	case TEGRA210_ADX_STATUS:447	case TEGRA210_ADX_INT_STATUS:448	case TEGRA210_ADX_CFG_RAM_CTRL:449	case TEGRA210_ADX_CFG_RAM_DATA:450		return true;451	default:452		break;453	}454 455	return false;456}457 458static const struct regmap_config tegra210_adx_regmap_config = {459	.reg_bits		= 32,460	.reg_stride		= 4,461	.val_bits		= 32,462	.max_register		= TEGRA210_ADX_CFG_RAM_DATA,463	.writeable_reg		= tegra210_adx_wr_reg,464	.readable_reg		= tegra210_adx_rd_reg,465	.volatile_reg		= tegra210_adx_volatile_reg,466	.reg_defaults		= tegra210_adx_reg_defaults,467	.num_reg_defaults	= ARRAY_SIZE(tegra210_adx_reg_defaults),468	.cache_type		= REGCACHE_FLAT,469};470 471static const struct of_device_id tegra210_adx_of_match[] = {472	{ .compatible = "nvidia,tegra210-adx" },473	{},474};475MODULE_DEVICE_TABLE(of, tegra210_adx_of_match);476 477static int tegra210_adx_platform_probe(struct platform_device *pdev)478{479	struct device *dev = &pdev->dev;480	struct tegra210_adx *adx;481	void __iomem *regs;482	int err;483 484	adx = devm_kzalloc(dev, sizeof(*adx), GFP_KERNEL);485	if (!adx)486		return -ENOMEM;487 488	dev_set_drvdata(dev, adx);489 490	regs = devm_platform_ioremap_resource(pdev, 0);491	if (IS_ERR(regs))492		return PTR_ERR(regs);493 494	adx->regmap = devm_regmap_init_mmio(dev, regs,495					    &tegra210_adx_regmap_config);496	if (IS_ERR(adx->regmap)) {497		dev_err(dev, "regmap init failed\n");498		return PTR_ERR(adx->regmap);499	}500 501	regcache_cache_only(adx->regmap, true);502 503	err = devm_snd_soc_register_component(dev, &tegra210_adx_cmpnt,504					      tegra210_adx_dais,505					      ARRAY_SIZE(tegra210_adx_dais));506	if (err) {507		dev_err(dev, "can't register ADX component, err: %d\n", err);508		return err;509	}510 511	pm_runtime_enable(dev);512 513	return 0;514}515 516static void tegra210_adx_platform_remove(struct platform_device *pdev)517{518	pm_runtime_disable(&pdev->dev);519}520 521static const struct dev_pm_ops tegra210_adx_pm_ops = {522	SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend,523			   tegra210_adx_runtime_resume, NULL)524	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,525				pm_runtime_force_resume)526};527 528static struct platform_driver tegra210_adx_driver = {529	.driver = {530		.name = "tegra210-adx",531		.of_match_table = tegra210_adx_of_match,532		.pm = &tegra210_adx_pm_ops,533	},534	.probe = tegra210_adx_platform_probe,535	.remove = tegra210_adx_platform_remove,536};537module_platform_driver(tegra210_adx_driver);538 539MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");540MODULE_DESCRIPTION("Tegra210 ADX ASoC driver");541MODULE_LICENSE("GPL v2");542