brintos

brintos / linux-shallow public Read only

0
0
Text · 15.4 KiB · d9b577f Raw
569 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// tegra210_dmic.c - Tegra210 DMIC driver4//5// Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.6 7#include <linux/clk.h>8#include <linux/device.h>9#include <linux/math64.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_params.h>17#include <sound/soc.h>18#include "tegra210_dmic.h"19#include "tegra_cif.h"20 21static const struct reg_default tegra210_dmic_reg_defaults[] = {22	{ TEGRA210_DMIC_TX_INT_MASK, 0x00000001 },23	{ TEGRA210_DMIC_TX_CIF_CTRL, 0x00007700 },24	{ TEGRA210_DMIC_CG, 0x1 },25	{ TEGRA210_DMIC_CTRL, 0x00000301 },26	/* Below enables all filters - DCR, LP and SC */27	{ TEGRA210_DMIC_DBG_CTRL, 0xe },28	/* Below as per latest POR value */29	{ TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4, 0x0 },30	/* LP filter is configured for pass through and used to apply gain */31	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_0, 0x00800000 },32	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_1, 0x0 },33	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_2, 0x0 },34	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_3, 0x0 },35	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_4, 0x0 },36	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_0, 0x00800000 },37	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_1, 0x0 },38	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_2, 0x0 },39	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_3, 0x0 },40	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, 0x0 },41};42 43static int __maybe_unused tegra210_dmic_runtime_suspend(struct device *dev)44{45	struct tegra210_dmic *dmic = dev_get_drvdata(dev);46 47	regcache_cache_only(dmic->regmap, true);48	regcache_mark_dirty(dmic->regmap);49 50	clk_disable_unprepare(dmic->clk_dmic);51 52	return 0;53}54 55static int __maybe_unused tegra210_dmic_runtime_resume(struct device *dev)56{57	struct tegra210_dmic *dmic = dev_get_drvdata(dev);58	int err;59 60	err = clk_prepare_enable(dmic->clk_dmic);61	if (err) {62		dev_err(dev, "failed to enable DMIC clock, err: %d\n", err);63		return err;64	}65 66	regcache_cache_only(dmic->regmap, false);67	regcache_sync(dmic->regmap);68 69	return 0;70}71 72static int tegra210_dmic_hw_params(struct snd_pcm_substream *substream,73				   struct snd_pcm_hw_params *params,74				   struct snd_soc_dai *dai)75{76	struct tegra210_dmic *dmic = snd_soc_dai_get_drvdata(dai);77	unsigned int srate, clk_rate, channels;78	struct tegra_cif_conf cif_conf;79	unsigned long long gain_q23 = DEFAULT_GAIN_Q23;80	int err;81 82	memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));83 84	channels = params_channels(params);85 86	cif_conf.audio_ch = channels;87 88	switch (dmic->ch_select) {89	case DMIC_CH_SELECT_LEFT:90	case DMIC_CH_SELECT_RIGHT:91		cif_conf.client_ch = 1;92		break;93	case DMIC_CH_SELECT_STEREO:94		cif_conf.client_ch = 2;95		break;96	default:97		dev_err(dai->dev, "invalid DMIC client channels\n");98		return -EINVAL;99	}100 101	srate = params_rate(params);102 103	/*104	 * DMIC clock rate is a multiple of 'Over Sampling Ratio' and105	 * 'Sample Rate'. The supported OSR values are 64, 128 and 256.106	 */107	clk_rate = (DMIC_OSR_FACTOR << dmic->osr_val) * srate;108 109	err = clk_set_rate(dmic->clk_dmic, clk_rate);110	if (err) {111		dev_err(dai->dev, "can't set DMIC clock rate %u, err: %d\n",112			clk_rate, err);113		return err;114	}115 116	regmap_update_bits(dmic->regmap,117			   /* Reg */118			   TEGRA210_DMIC_CTRL,119			   /* Mask */120			   TEGRA210_DMIC_CTRL_LRSEL_POLARITY_MASK |121			   TEGRA210_DMIC_CTRL_OSR_MASK |122			   TEGRA210_DMIC_CTRL_CHANNEL_SELECT_MASK,123			   /* Value */124			   (dmic->lrsel << LRSEL_POL_SHIFT) |125			   (dmic->osr_val << OSR_SHIFT) |126			   ((dmic->ch_select + 1) << CH_SEL_SHIFT));127 128	/*129	 * Use LP filter gain register to apply boost.130	 * Boost Gain Volume control has 100x factor.131	 */132	if (dmic->boost_gain)133		gain_q23 = div_u64(gain_q23 * dmic->boost_gain, 100);134 135	regmap_write(dmic->regmap, TEGRA210_DMIC_LP_FILTER_GAIN,136		     (unsigned int)gain_q23);137 138	switch (params_format(params)) {139	case SNDRV_PCM_FORMAT_S16_LE:140		cif_conf.audio_bits = TEGRA_ACIF_BITS_16;141		break;142	case SNDRV_PCM_FORMAT_S32_LE:143		cif_conf.audio_bits = TEGRA_ACIF_BITS_32;144		break;145	default:146		dev_err(dai->dev, "unsupported format!\n");147		return -EOPNOTSUPP;148	}149 150	cif_conf.client_bits = TEGRA_ACIF_BITS_24;151	cif_conf.mono_conv = dmic->mono_to_stereo;152	cif_conf.stereo_conv = dmic->stereo_to_mono;153 154	tegra_set_cif(dmic->regmap, TEGRA210_DMIC_TX_CIF_CTRL, &cif_conf);155 156	return 0;157}158 159static int tegra210_dmic_get_boost_gain(struct snd_kcontrol *kcontrol,160					struct snd_ctl_elem_value *ucontrol)161{162	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);163	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);164 165	ucontrol->value.integer.value[0] = dmic->boost_gain;166 167	return 0;168}169 170static int tegra210_dmic_put_boost_gain(struct snd_kcontrol *kcontrol,171					struct snd_ctl_elem_value *ucontrol)172{173	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);174	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);175	int value = ucontrol->value.integer.value[0];176 177	if (value == dmic->boost_gain)178		return 0;179 180	dmic->boost_gain = value;181 182	return 1;183}184 185static int tegra210_dmic_get_ch_select(struct snd_kcontrol *kcontrol,186				       struct snd_ctl_elem_value *ucontrol)187{188	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);189	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);190 191	ucontrol->value.enumerated.item[0] = dmic->ch_select;192 193	return 0;194}195 196static int tegra210_dmic_put_ch_select(struct snd_kcontrol *kcontrol,197				       struct snd_ctl_elem_value *ucontrol)198{199	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);200	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);201	unsigned int value = ucontrol->value.enumerated.item[0];202 203	if (value == dmic->ch_select)204		return 0;205 206	dmic->ch_select = value;207 208	return 1;209}210 211static int tegra210_dmic_get_mono_to_stereo(struct snd_kcontrol *kcontrol,212					    struct snd_ctl_elem_value *ucontrol)213{214	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);215	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);216 217	ucontrol->value.enumerated.item[0] = dmic->mono_to_stereo;218 219	return 0;220}221 222static int tegra210_dmic_put_mono_to_stereo(struct snd_kcontrol *kcontrol,223					    struct snd_ctl_elem_value *ucontrol)224{225	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);226	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);227	unsigned int value = ucontrol->value.enumerated.item[0];228 229	if (value == dmic->mono_to_stereo)230		return 0;231 232	dmic->mono_to_stereo = value;233 234	return 1;235}236 237static int tegra210_dmic_get_stereo_to_mono(struct snd_kcontrol *kcontrol,238					    struct snd_ctl_elem_value *ucontrol)239{240	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);241	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);242 243	ucontrol->value.enumerated.item[0] = dmic->stereo_to_mono;244 245	return 0;246}247 248static int tegra210_dmic_put_stereo_to_mono(struct snd_kcontrol *kcontrol,249					    struct snd_ctl_elem_value *ucontrol)250{251	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);252	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);253	unsigned int value = ucontrol->value.enumerated.item[0];254 255	if (value == dmic->stereo_to_mono)256		return 0;257 258	dmic->stereo_to_mono = value;259 260	return 1;261}262 263static int tegra210_dmic_get_osr_val(struct snd_kcontrol *kcontrol,264				     struct snd_ctl_elem_value *ucontrol)265{266	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);267	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);268 269	ucontrol->value.enumerated.item[0] = dmic->osr_val;270 271	return 0;272}273 274static int tegra210_dmic_put_osr_val(struct snd_kcontrol *kcontrol,275				     struct snd_ctl_elem_value *ucontrol)276{277	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);278	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);279	unsigned int value = ucontrol->value.enumerated.item[0];280 281	if (value == dmic->osr_val)282		return 0;283 284	dmic->osr_val = value;285 286	return 1;287}288 289static int tegra210_dmic_get_pol_sel(struct snd_kcontrol *kcontrol,290				     struct snd_ctl_elem_value *ucontrol)291{292	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);293	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);294 295	ucontrol->value.enumerated.item[0] = dmic->lrsel;296 297	return 0;298}299 300static int tegra210_dmic_put_pol_sel(struct snd_kcontrol *kcontrol,301				     struct snd_ctl_elem_value *ucontrol)302{303	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);304	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);305	unsigned int value = ucontrol->value.enumerated.item[0];306 307	if (value == dmic->lrsel)308		return 0;309 310	dmic->lrsel = value;311 312	return 1;313}314 315static const struct snd_soc_dai_ops tegra210_dmic_dai_ops = {316	.hw_params	= tegra210_dmic_hw_params,317};318 319static struct snd_soc_dai_driver tegra210_dmic_dais[] = {320	{321		.name = "DMIC-CIF",322		.capture = {323			.stream_name = "CIF-Capture",324			.channels_min = 1,325			.channels_max = 2,326			.rates = SNDRV_PCM_RATE_8000_48000,327			.formats = SNDRV_PCM_FMTBIT_S16_LE |328				   SNDRV_PCM_FMTBIT_S32_LE,329		},330	},331	{332		.name = "DMIC-DAP",333		.capture = {334			.stream_name = "DAP-Capture",335			.channels_min = 1,336			.channels_max = 2,337			.rates = SNDRV_PCM_RATE_8000_48000,338			.formats = SNDRV_PCM_FMTBIT_S16_LE |339				   SNDRV_PCM_FMTBIT_S32_LE,340		},341		.ops = &tegra210_dmic_dai_ops,342		.symmetric_rate = 1,343	},344};345 346static const struct snd_soc_dapm_widget tegra210_dmic_widgets[] = {347	SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_DMIC_ENABLE, 0, 0),348	SND_SOC_DAPM_MIC("MIC", NULL),349};350 351static const struct snd_soc_dapm_route tegra210_dmic_routes[] = {352	{ "XBAR-RX",		NULL,	"XBAR-Capture" },353	{ "XBAR-Capture",	NULL,	"CIF-Capture" },354	{ "CIF-Capture",	NULL,	"TX" },355	{ "TX",			NULL,	"DAP-Capture" },356	{ "DAP-Capture",	NULL,	"MIC" },357};358 359static const char * const tegra210_dmic_ch_select[] = {360	"Left", "Right", "Stereo",361};362 363static const struct soc_enum tegra210_dmic_ch_enum =364	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_ch_select),365			tegra210_dmic_ch_select);366 367static const char * const tegra210_dmic_mono_conv_text[] = {368	"Zero", "Copy",369};370 371static const char * const tegra210_dmic_stereo_conv_text[] = {372	"CH0", "CH1", "AVG",373};374 375static const struct soc_enum tegra210_dmic_mono_conv_enum =376	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_mono_conv_text),377			tegra210_dmic_mono_conv_text);378 379static const struct soc_enum tegra210_dmic_stereo_conv_enum =380	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_stereo_conv_text),381			tegra210_dmic_stereo_conv_text);382 383static const char * const tegra210_dmic_osr_text[] = {384	"OSR_64", "OSR_128", "OSR_256",385};386 387static const struct soc_enum tegra210_dmic_osr_enum =388	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_osr_text),389			tegra210_dmic_osr_text);390 391static const char * const tegra210_dmic_lrsel_text[] = {392	"Left", "Right",393};394 395static const struct soc_enum tegra210_dmic_lrsel_enum =396	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_lrsel_text),397			tegra210_dmic_lrsel_text);398 399static const struct snd_kcontrol_new tegra210_dmic_controls[] = {400	SOC_SINGLE_EXT("Boost Gain Volume", 0, 0, MAX_BOOST_GAIN, 0,401		       tegra210_dmic_get_boost_gain,402		       tegra210_dmic_put_boost_gain),403	SOC_ENUM_EXT("Channel Select", tegra210_dmic_ch_enum,404		     tegra210_dmic_get_ch_select, tegra210_dmic_put_ch_select),405	SOC_ENUM_EXT("Mono To Stereo",406		     tegra210_dmic_mono_conv_enum,407		     tegra210_dmic_get_mono_to_stereo,408		     tegra210_dmic_put_mono_to_stereo),409	SOC_ENUM_EXT("Stereo To Mono",410		     tegra210_dmic_stereo_conv_enum,411		     tegra210_dmic_get_stereo_to_mono,412		     tegra210_dmic_put_stereo_to_mono),413	SOC_ENUM_EXT("OSR Value", tegra210_dmic_osr_enum,414		     tegra210_dmic_get_osr_val, tegra210_dmic_put_osr_val),415	SOC_ENUM_EXT("LR Polarity Select", tegra210_dmic_lrsel_enum,416		     tegra210_dmic_get_pol_sel, tegra210_dmic_put_pol_sel),417};418 419static const struct snd_soc_component_driver tegra210_dmic_compnt = {420	.dapm_widgets		= tegra210_dmic_widgets,421	.num_dapm_widgets	= ARRAY_SIZE(tegra210_dmic_widgets),422	.dapm_routes		= tegra210_dmic_routes,423	.num_dapm_routes	= ARRAY_SIZE(tegra210_dmic_routes),424	.controls		= tegra210_dmic_controls,425	.num_controls		= ARRAY_SIZE(tegra210_dmic_controls),426};427 428static bool tegra210_dmic_wr_reg(struct device *dev, unsigned int reg)429{430	switch (reg) {431	case TEGRA210_DMIC_TX_INT_MASK ... TEGRA210_DMIC_TX_CIF_CTRL:432	case TEGRA210_DMIC_ENABLE ... TEGRA210_DMIC_CG:433	case TEGRA210_DMIC_CTRL:434	case TEGRA210_DMIC_DBG_CTRL:435	case TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4 ... TEGRA210_DMIC_LP_BIQUAD_1_COEF_4:436		return true;437	default:438		return false;439	}440}441 442static bool tegra210_dmic_rd_reg(struct device *dev, unsigned int reg)443{444	if (tegra210_dmic_wr_reg(dev, reg))445		return true;446 447	switch (reg) {448	case TEGRA210_DMIC_TX_STATUS:449	case TEGRA210_DMIC_TX_INT_STATUS:450	case TEGRA210_DMIC_STATUS:451	case TEGRA210_DMIC_INT_STATUS:452		return true;453	default:454		return false;455	}456}457 458static bool tegra210_dmic_volatile_reg(struct device *dev, unsigned int reg)459{460	switch (reg) {461	case TEGRA210_DMIC_TX_STATUS:462	case TEGRA210_DMIC_TX_INT_STATUS:463	case TEGRA210_DMIC_TX_INT_SET:464	case TEGRA210_DMIC_SOFT_RESET:465	case TEGRA210_DMIC_STATUS:466	case TEGRA210_DMIC_INT_STATUS:467		return true;468	default:469		return false;470	}471}472 473static const struct regmap_config tegra210_dmic_regmap_config = {474	.reg_bits = 32,475	.reg_stride = 4,476	.val_bits = 32,477	.max_register = TEGRA210_DMIC_LP_BIQUAD_1_COEF_4,478	.writeable_reg = tegra210_dmic_wr_reg,479	.readable_reg = tegra210_dmic_rd_reg,480	.volatile_reg = tegra210_dmic_volatile_reg,481	.reg_defaults = tegra210_dmic_reg_defaults,482	.num_reg_defaults = ARRAY_SIZE(tegra210_dmic_reg_defaults),483	.cache_type = REGCACHE_FLAT,484};485 486static int tegra210_dmic_probe(struct platform_device *pdev)487{488	struct device *dev = &pdev->dev;489	struct tegra210_dmic *dmic;490	void __iomem *regs;491	int err;492 493	dmic = devm_kzalloc(dev, sizeof(*dmic), GFP_KERNEL);494	if (!dmic)495		return -ENOMEM;496 497	dmic->osr_val = DMIC_OSR_64;498	dmic->ch_select = DMIC_CH_SELECT_STEREO;499	dmic->lrsel = DMIC_LRSEL_LEFT;500	dmic->boost_gain = 0;501	dmic->stereo_to_mono = 0; /* "CH0" */502 503	dev_set_drvdata(dev, dmic);504 505	dmic->clk_dmic = devm_clk_get(dev, "dmic");506	if (IS_ERR(dmic->clk_dmic)) {507		dev_err(dev, "can't retrieve DMIC clock\n");508		return PTR_ERR(dmic->clk_dmic);509	}510 511	regs = devm_platform_ioremap_resource(pdev, 0);512	if (IS_ERR(regs))513		return PTR_ERR(regs);514 515	dmic->regmap = devm_regmap_init_mmio(dev, regs,516					     &tegra210_dmic_regmap_config);517	if (IS_ERR(dmic->regmap)) {518		dev_err(dev, "regmap init failed\n");519		return PTR_ERR(dmic->regmap);520	}521 522	regcache_cache_only(dmic->regmap, true);523 524	err = devm_snd_soc_register_component(dev, &tegra210_dmic_compnt,525					      tegra210_dmic_dais,526					      ARRAY_SIZE(tegra210_dmic_dais));527	if (err) {528		dev_err(dev, "can't register DMIC component, err: %d\n", err);529		return err;530	}531 532	pm_runtime_enable(dev);533 534	return 0;535}536 537static void tegra210_dmic_remove(struct platform_device *pdev)538{539	pm_runtime_disable(&pdev->dev);540}541 542static const struct dev_pm_ops tegra210_dmic_pm_ops = {543	SET_RUNTIME_PM_OPS(tegra210_dmic_runtime_suspend,544			   tegra210_dmic_runtime_resume, NULL)545	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,546				pm_runtime_force_resume)547};548 549static const struct of_device_id tegra210_dmic_of_match[] = {550	{ .compatible = "nvidia,tegra210-dmic" },551	{},552};553MODULE_DEVICE_TABLE(of, tegra210_dmic_of_match);554 555static struct platform_driver tegra210_dmic_driver = {556	.driver = {557		.name = "tegra210-dmic",558		.of_match_table = tegra210_dmic_of_match,559		.pm = &tegra210_dmic_pm_ops,560	},561	.probe = tegra210_dmic_probe,562	.remove = tegra210_dmic_remove,563};564module_platform_driver(tegra210_dmic_driver)565 566MODULE_AUTHOR("Rahul Mittal <rmittal@nvidia.com>");567MODULE_DESCRIPTION("Tegra210 ASoC DMIC driver");568MODULE_LICENSE("GPL v2");569