brintos

brintos / linux-shallow public Read only

0
0
Text · 15.0 KiB · 508128b Raw
553 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.3//4// tegra186_dspk.c - Tegra186 DSPK driver5 6#include <linux/clk.h>7#include <linux/device.h>8#include <linux/mod_devicetable.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/platform_device.h>12#include <linux/pm_runtime.h>13#include <linux/regmap.h>14#include <sound/core.h>15#include <sound/pcm_params.h>16#include <sound/soc.h>17#include "tegra186_dspk.h"18#include "tegra_cif.h"19 20static const struct reg_default tegra186_dspk_reg_defaults[] = {21	{ TEGRA186_DSPK_RX_INT_MASK, 0x00000007 },22	{ TEGRA186_DSPK_RX_CIF_CTRL, 0x00007700 },23	{ TEGRA186_DSPK_CG,	     0x00000001 },24	{ TEGRA186_DSPK_CORE_CTRL,   0x00000310 },25	{ TEGRA186_DSPK_CODEC_CTRL,  0x03000000 },26};27 28static int tegra186_dspk_get_fifo_th(struct snd_kcontrol *kcontrol,29				     struct snd_ctl_elem_value *ucontrol)30{31	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);32	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);33 34	ucontrol->value.integer.value[0] = dspk->rx_fifo_th;35 36	return 0;37}38 39static int tegra186_dspk_put_fifo_th(struct snd_kcontrol *kcontrol,40				     struct snd_ctl_elem_value *ucontrol)41{42	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);43	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);44	int value = ucontrol->value.integer.value[0];45 46	if (value == dspk->rx_fifo_th)47		return 0;48 49	dspk->rx_fifo_th = value;50 51	return 1;52}53 54static int tegra186_dspk_get_osr_val(struct snd_kcontrol *kcontrol,55				     struct snd_ctl_elem_value *ucontrol)56{57	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);58	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);59 60	ucontrol->value.enumerated.item[0] = dspk->osr_val;61 62	return 0;63}64 65static int tegra186_dspk_put_osr_val(struct snd_kcontrol *kcontrol,66				     struct snd_ctl_elem_value *ucontrol)67{68	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);69	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);70	unsigned int value = ucontrol->value.enumerated.item[0];71 72	if (value == dspk->osr_val)73		return 0;74 75	dspk->osr_val = value;76 77	return 1;78}79 80static int tegra186_dspk_get_pol_sel(struct snd_kcontrol *kcontrol,81				     struct snd_ctl_elem_value *ucontrol)82{83	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);84	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);85 86	ucontrol->value.enumerated.item[0] = dspk->lrsel;87 88	return 0;89}90 91static int tegra186_dspk_put_pol_sel(struct snd_kcontrol *kcontrol,92				     struct snd_ctl_elem_value *ucontrol)93{94	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);95	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);96	unsigned int value = ucontrol->value.enumerated.item[0];97 98	if (value == dspk->lrsel)99		return 0;100 101	dspk->lrsel = value;102 103	return 1;104}105 106static int tegra186_dspk_get_ch_sel(struct snd_kcontrol *kcontrol,107				    struct snd_ctl_elem_value *ucontrol)108{109	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);110	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);111 112	ucontrol->value.enumerated.item[0] = dspk->ch_sel;113 114	return 0;115}116 117static int tegra186_dspk_put_ch_sel(struct snd_kcontrol *kcontrol,118				    struct snd_ctl_elem_value *ucontrol)119{120	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);121	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);122	unsigned int value = ucontrol->value.enumerated.item[0];123 124	if (value == dspk->ch_sel)125		return 0;126 127	dspk->ch_sel = value;128 129	return 1;130}131 132static int tegra186_dspk_get_mono_to_stereo(struct snd_kcontrol *kcontrol,133					    struct snd_ctl_elem_value *ucontrol)134{135	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);136	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);137 138	ucontrol->value.enumerated.item[0] = dspk->mono_to_stereo;139 140	return 0;141}142 143static int tegra186_dspk_put_mono_to_stereo(struct snd_kcontrol *kcontrol,144					    struct snd_ctl_elem_value *ucontrol)145{146	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);147	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);148	unsigned int value = ucontrol->value.enumerated.item[0];149 150	if (value == dspk->mono_to_stereo)151		return 0;152 153	dspk->mono_to_stereo = value;154 155	return 1;156}157 158static int tegra186_dspk_get_stereo_to_mono(struct snd_kcontrol *kcontrol,159					    struct snd_ctl_elem_value *ucontrol)160{161	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);162	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);163 164	ucontrol->value.enumerated.item[0] = dspk->stereo_to_mono;165 166	return 0;167}168 169static int tegra186_dspk_put_stereo_to_mono(struct snd_kcontrol *kcontrol,170					    struct snd_ctl_elem_value *ucontrol)171{172	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);173	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);174	unsigned int value = ucontrol->value.enumerated.item[0];175 176	if (value == dspk->stereo_to_mono)177		return 0;178 179	dspk->stereo_to_mono = value;180 181	return 1;182}183 184static int __maybe_unused tegra186_dspk_runtime_suspend(struct device *dev)185{186	struct tegra186_dspk *dspk = dev_get_drvdata(dev);187 188	regcache_cache_only(dspk->regmap, true);189	regcache_mark_dirty(dspk->regmap);190 191	clk_disable_unprepare(dspk->clk_dspk);192 193	return 0;194}195 196static int __maybe_unused tegra186_dspk_runtime_resume(struct device *dev)197{198	struct tegra186_dspk *dspk = dev_get_drvdata(dev);199	int err;200 201	err = clk_prepare_enable(dspk->clk_dspk);202	if (err) {203		dev_err(dev, "failed to enable DSPK clock, err: %d\n", err);204		return err;205	}206 207	regcache_cache_only(dspk->regmap, false);208	regcache_sync(dspk->regmap);209 210	return 0;211}212 213static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,214				   struct snd_pcm_hw_params *params,215				   struct snd_soc_dai *dai)216{217	struct tegra186_dspk *dspk = snd_soc_dai_get_drvdata(dai);218	unsigned int channels, srate, dspk_clk;219	struct device *dev = dai->dev;220	struct tegra_cif_conf cif_conf;221	unsigned int max_th;222	int err;223 224	memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));225 226	channels = params_channels(params);227	cif_conf.audio_ch = channels;228 229	/* Client channel */230	switch (dspk->ch_sel) {231	case DSPK_CH_SELECT_LEFT:232	case DSPK_CH_SELECT_RIGHT:233		cif_conf.client_ch = 1;234		break;235	case DSPK_CH_SELECT_STEREO:236		cif_conf.client_ch = 2;237		break;238	default:239		dev_err(dev, "Invalid DSPK client channels\n");240		return -EINVAL;241	}242 243	switch (params_format(params)) {244	case SNDRV_PCM_FORMAT_S16_LE:245		cif_conf.audio_bits = TEGRA_ACIF_BITS_16;246		cif_conf.client_bits = TEGRA_ACIF_BITS_16;247		break;248	case SNDRV_PCM_FORMAT_S32_LE:249		cif_conf.audio_bits = TEGRA_ACIF_BITS_32;250		cif_conf.client_bits = TEGRA_ACIF_BITS_24;251		break;252	default:253		dev_err(dev, "unsupported format!\n");254		return -EOPNOTSUPP;255	}256 257	srate = params_rate(params);258 259	/* RX FIFO threshold in terms of frames */260	max_th = (TEGRA186_DSPK_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1;261 262	if (dspk->rx_fifo_th > max_th)263		dspk->rx_fifo_th = max_th;264 265	cif_conf.threshold = dspk->rx_fifo_th;266	cif_conf.mono_conv = dspk->mono_to_stereo;267	cif_conf.stereo_conv = dspk->stereo_to_mono;268 269	tegra_set_cif(dspk->regmap, TEGRA186_DSPK_RX_CIF_CTRL,270		      &cif_conf);271 272	/*273	 * DSPK clock and PDM codec clock should be synchronous with 4:1 ratio,274	 * this is because it takes 4 clock cycles to send out one sample to275	 * codec by sigma delta modulator. Finally the clock rate is a multiple276	 * of 'Over Sampling Ratio', 'Sample Rate' and 'Interface Clock Ratio'.277	 */278	dspk_clk = (DSPK_OSR_FACTOR << dspk->osr_val) * srate * DSPK_CLK_RATIO;279 280	err = clk_set_rate(dspk->clk_dspk, dspk_clk);281	if (err) {282		dev_err(dev, "can't set DSPK clock rate %u, err: %d\n",283			dspk_clk, err);284 285		return err;286	}287 288	regmap_update_bits(dspk->regmap,289			   /* Reg */290			   TEGRA186_DSPK_CORE_CTRL,291			   /* Mask */292			   TEGRA186_DSPK_OSR_MASK |293			   TEGRA186_DSPK_CHANNEL_SELECT_MASK |294			   TEGRA186_DSPK_CTRL_LRSEL_POLARITY_MASK,295			   /* Value */296			   (dspk->osr_val << DSPK_OSR_SHIFT) |297			   ((dspk->ch_sel + 1) << CH_SEL_SHIFT) |298			   (dspk->lrsel << LRSEL_POL_SHIFT));299 300	return 0;301}302 303static const struct snd_soc_dai_ops tegra186_dspk_dai_ops = {304	.hw_params	= tegra186_dspk_hw_params,305};306 307static struct snd_soc_dai_driver tegra186_dspk_dais[] = {308	{309	    .name = "DSPK-CIF",310	    .playback = {311		.stream_name = "CIF-Playback",312		.channels_min = 1,313		.channels_max = 2,314		.rates = SNDRV_PCM_RATE_8000_48000,315		.formats = SNDRV_PCM_FMTBIT_S16_LE |316			   SNDRV_PCM_FMTBIT_S32_LE,317	    },318	},319	{320	    .name = "DSPK-DAP",321	    .playback = {322		.stream_name = "DAP-Playback",323		.channels_min = 1,324		.channels_max = 2,325		.rates = SNDRV_PCM_RATE_8000_48000,326		.formats = SNDRV_PCM_FMTBIT_S16_LE |327			   SNDRV_PCM_FMTBIT_S32_LE,328	    },329	    .ops = &tegra186_dspk_dai_ops,330	    .symmetric_rate = 1,331	},332};333 334static const struct snd_soc_dapm_widget tegra186_dspk_widgets[] = {335	SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA186_DSPK_ENABLE, 0, 0),336	SND_SOC_DAPM_SPK("SPK", NULL),337};338 339static const struct snd_soc_dapm_route tegra186_dspk_routes[] = {340	{ "XBAR-Playback",	NULL,	"XBAR-TX" },341	{ "CIF-Playback",	NULL,	"XBAR-Playback" },342	{ "RX",			NULL,	"CIF-Playback" },343	{ "DAP-Playback",	NULL,	"RX" },344	{ "SPK",		NULL,	"DAP-Playback" },345};346 347static const char * const tegra186_dspk_ch_sel_text[] = {348	"Left", "Right", "Stereo",349};350 351static const struct soc_enum tegra186_dspk_ch_sel_enum =352	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_ch_sel_text),353			tegra186_dspk_ch_sel_text);354 355static const char * const tegra186_dspk_osr_text[] = {356	"OSR_32", "OSR_64", "OSR_128", "OSR_256",357};358 359static const struct soc_enum tegra186_dspk_osr_enum =360	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_osr_text),361			tegra186_dspk_osr_text);362 363static const char * const tegra186_dspk_lrsel_text[] = {364	"Left", "Right",365};366 367static const char * const tegra186_dspk_mono_conv_text[] = {368	"Zero", "Copy",369};370 371static const struct soc_enum tegra186_dspk_mono_conv_enum =372	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,373			ARRAY_SIZE(tegra186_dspk_mono_conv_text),374			tegra186_dspk_mono_conv_text);375 376static const char * const tegra186_dspk_stereo_conv_text[] = {377	"CH0", "CH1", "AVG",378};379 380static const struct soc_enum tegra186_dspk_stereo_conv_enum =381	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,382			ARRAY_SIZE(tegra186_dspk_stereo_conv_text),383			tegra186_dspk_stereo_conv_text);384 385static const struct soc_enum tegra186_dspk_lrsel_enum =386	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_lrsel_text),387			tegra186_dspk_lrsel_text);388 389static const struct snd_kcontrol_new tegrat186_dspk_controls[] = {390	SOC_SINGLE_EXT("FIFO Threshold", SND_SOC_NOPM, 0,391		       TEGRA186_DSPK_RX_FIFO_DEPTH - 1, 0,392		       tegra186_dspk_get_fifo_th, tegra186_dspk_put_fifo_th),393	SOC_ENUM_EXT("OSR Value", tegra186_dspk_osr_enum,394		     tegra186_dspk_get_osr_val, tegra186_dspk_put_osr_val),395	SOC_ENUM_EXT("LR Polarity Select", tegra186_dspk_lrsel_enum,396		     tegra186_dspk_get_pol_sel, tegra186_dspk_put_pol_sel),397	SOC_ENUM_EXT("Channel Select", tegra186_dspk_ch_sel_enum,398		     tegra186_dspk_get_ch_sel, tegra186_dspk_put_ch_sel),399	SOC_ENUM_EXT("Mono To Stereo", tegra186_dspk_mono_conv_enum,400		     tegra186_dspk_get_mono_to_stereo,401		     tegra186_dspk_put_mono_to_stereo),402	SOC_ENUM_EXT("Stereo To Mono", tegra186_dspk_stereo_conv_enum,403		     tegra186_dspk_get_stereo_to_mono,404		     tegra186_dspk_put_stereo_to_mono),405};406 407static const struct snd_soc_component_driver tegra186_dspk_cmpnt = {408	.dapm_widgets = tegra186_dspk_widgets,409	.num_dapm_widgets = ARRAY_SIZE(tegra186_dspk_widgets),410	.dapm_routes = tegra186_dspk_routes,411	.num_dapm_routes = ARRAY_SIZE(tegra186_dspk_routes),412	.controls = tegrat186_dspk_controls,413	.num_controls = ARRAY_SIZE(tegrat186_dspk_controls),414};415 416static bool tegra186_dspk_wr_reg(struct device *dev, unsigned int reg)417{418	switch (reg) {419	case TEGRA186_DSPK_RX_INT_MASK ... TEGRA186_DSPK_RX_CIF_CTRL:420	case TEGRA186_DSPK_ENABLE ... TEGRA186_DSPK_CG:421	case TEGRA186_DSPK_CORE_CTRL ... TEGRA186_DSPK_CODEC_CTRL:422		return true;423	default:424		return false;425	}426}427 428static bool tegra186_dspk_rd_reg(struct device *dev, unsigned int reg)429{430	if (tegra186_dspk_wr_reg(dev, reg))431		return true;432 433	switch (reg) {434	case TEGRA186_DSPK_RX_STATUS:435	case TEGRA186_DSPK_RX_INT_STATUS:436	case TEGRA186_DSPK_STATUS:437	case TEGRA186_DSPK_INT_STATUS:438		return true;439	default:440		return false;441	}442}443 444static bool tegra186_dspk_volatile_reg(struct device *dev, unsigned int reg)445{446	switch (reg) {447	case TEGRA186_DSPK_RX_STATUS:448	case TEGRA186_DSPK_RX_INT_STATUS:449	case TEGRA186_DSPK_STATUS:450	case TEGRA186_DSPK_INT_STATUS:451		return true;452	default:453		return false;454	}455}456 457static const struct regmap_config tegra186_dspk_regmap = {458	.reg_bits		= 32,459	.reg_stride		= 4,460	.val_bits		= 32,461	.max_register		= TEGRA186_DSPK_CODEC_CTRL,462	.writeable_reg		= tegra186_dspk_wr_reg,463	.readable_reg		= tegra186_dspk_rd_reg,464	.volatile_reg		= tegra186_dspk_volatile_reg,465	.reg_defaults		= tegra186_dspk_reg_defaults,466	.num_reg_defaults	= ARRAY_SIZE(tegra186_dspk_reg_defaults),467	.cache_type		= REGCACHE_FLAT,468};469 470static const struct of_device_id tegra186_dspk_of_match[] = {471	{ .compatible = "nvidia,tegra186-dspk" },472	{},473};474MODULE_DEVICE_TABLE(of, tegra186_dspk_of_match);475 476static int tegra186_dspk_platform_probe(struct platform_device *pdev)477{478	struct device *dev = &pdev->dev;479	struct tegra186_dspk *dspk;480	void __iomem *regs;481	int err;482 483	dspk = devm_kzalloc(dev, sizeof(*dspk), GFP_KERNEL);484	if (!dspk)485		return -ENOMEM;486 487	dspk->osr_val = DSPK_OSR_64;488	dspk->lrsel = DSPK_LRSEL_LEFT;489	dspk->ch_sel = DSPK_CH_SELECT_STEREO;490	dspk->mono_to_stereo = 0; /* "Zero" */491 492	dev_set_drvdata(dev, dspk);493 494	dspk->clk_dspk = devm_clk_get(dev, "dspk");495	if (IS_ERR(dspk->clk_dspk)) {496		dev_err(dev, "can't retrieve DSPK clock\n");497		return PTR_ERR(dspk->clk_dspk);498	}499 500	regs = devm_platform_ioremap_resource(pdev, 0);501	if (IS_ERR(regs))502		return PTR_ERR(regs);503 504	dspk->regmap = devm_regmap_init_mmio(dev, regs, &tegra186_dspk_regmap);505	if (IS_ERR(dspk->regmap)) {506		dev_err(dev, "regmap init failed\n");507		return PTR_ERR(dspk->regmap);508	}509 510	regcache_cache_only(dspk->regmap, true);511 512	err = devm_snd_soc_register_component(dev, &tegra186_dspk_cmpnt,513					      tegra186_dspk_dais,514					      ARRAY_SIZE(tegra186_dspk_dais));515	if (err) {516		dev_err(dev, "can't register DSPK component, err: %d\n",517			err);518		return err;519	}520 521	pm_runtime_enable(dev);522 523	return 0;524}525 526static void tegra186_dspk_platform_remove(struct platform_device *pdev)527{528	pm_runtime_disable(&pdev->dev);529}530 531static const struct dev_pm_ops tegra186_dspk_pm_ops = {532	SET_RUNTIME_PM_OPS(tegra186_dspk_runtime_suspend,533			   tegra186_dspk_runtime_resume, NULL)534	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,535				pm_runtime_force_resume)536};537 538static struct platform_driver tegra186_dspk_driver = {539	.driver = {540		.name = "tegra186-dspk",541		.of_match_table = tegra186_dspk_of_match,542		.pm = &tegra186_dspk_pm_ops,543	},544	.probe = tegra186_dspk_platform_probe,545	.remove = tegra186_dspk_platform_remove,546};547module_platform_driver(tegra186_dspk_driver);548 549MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");550MODULE_AUTHOR("Sameer Pujar <spujar@nvidia.com>");551MODULE_DESCRIPTION("Tegra186 ASoC DSPK driver");552MODULE_LICENSE("GPL v2");553