18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// tegra210_dmic.c - Tegra210 DMIC driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/math64.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of_device.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <sound/core.h> 168c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 178c2ecf20Sopenharmony_ci#include <sound/soc.h> 188c2ecf20Sopenharmony_ci#include "tegra210_dmic.h" 198c2ecf20Sopenharmony_ci#include "tegra_cif.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic const struct reg_default tegra210_dmic_reg_defaults[] = { 228c2ecf20Sopenharmony_ci { TEGRA210_DMIC_TX_INT_MASK, 0x00000001 }, 238c2ecf20Sopenharmony_ci { TEGRA210_DMIC_TX_CIF_CTRL, 0x00007700 }, 248c2ecf20Sopenharmony_ci { TEGRA210_DMIC_CG, 0x1 }, 258c2ecf20Sopenharmony_ci { TEGRA210_DMIC_CTRL, 0x00000301 }, 268c2ecf20Sopenharmony_ci /* Below enables all filters - DCR, LP and SC */ 278c2ecf20Sopenharmony_ci { TEGRA210_DMIC_DBG_CTRL, 0xe }, 288c2ecf20Sopenharmony_ci /* Below as per latest POR value */ 298c2ecf20Sopenharmony_ci { TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4, 0x0 }, 308c2ecf20Sopenharmony_ci /* LP filter is configured for pass through and used to apply gain */ 318c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_0_COEF_0, 0x00800000 }, 328c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_0_COEF_1, 0x0 }, 338c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_0_COEF_2, 0x0 }, 348c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_0_COEF_3, 0x0 }, 358c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_0_COEF_4, 0x0 }, 368c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_1_COEF_0, 0x00800000 }, 378c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_1_COEF_1, 0x0 }, 388c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_1_COEF_2, 0x0 }, 398c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_1_COEF_3, 0x0 }, 408c2ecf20Sopenharmony_ci { TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, 0x0 }, 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int __maybe_unused tegra210_dmic_runtime_suspend(struct device *dev) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = dev_get_drvdata(dev); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci regcache_cache_only(dmic->regmap, true); 488c2ecf20Sopenharmony_ci regcache_mark_dirty(dmic->regmap); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci clk_disable_unprepare(dmic->clk_dmic); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int __maybe_unused tegra210_dmic_runtime_resume(struct device *dev) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = dev_get_drvdata(dev); 588c2ecf20Sopenharmony_ci int err; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci err = clk_prepare_enable(dmic->clk_dmic); 618c2ecf20Sopenharmony_ci if (err) { 628c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable DMIC clock, err: %d\n", err); 638c2ecf20Sopenharmony_ci return err; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci regcache_cache_only(dmic->regmap, false); 678c2ecf20Sopenharmony_ci regcache_sync(dmic->regmap); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int tegra210_dmic_hw_params(struct snd_pcm_substream *substream, 738c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 748c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_dai_get_drvdata(dai); 778c2ecf20Sopenharmony_ci unsigned int srate, clk_rate, channels; 788c2ecf20Sopenharmony_ci struct tegra_cif_conf cif_conf; 798c2ecf20Sopenharmony_ci unsigned long long gain_q23 = DEFAULT_GAIN_Q23; 808c2ecf20Sopenharmony_ci int err; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci channels = params_channels(params); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci cif_conf.audio_ch = channels; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci switch (dmic->ch_select) { 898c2ecf20Sopenharmony_ci case DMIC_CH_SELECT_LEFT: 908c2ecf20Sopenharmony_ci case DMIC_CH_SELECT_RIGHT: 918c2ecf20Sopenharmony_ci cif_conf.client_ch = 1; 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci case DMIC_CH_SELECT_STEREO: 948c2ecf20Sopenharmony_ci cif_conf.client_ch = 2; 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci default: 978c2ecf20Sopenharmony_ci dev_err(dai->dev, "invalid DMIC client channels\n"); 988c2ecf20Sopenharmony_ci return -EINVAL; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci srate = params_rate(params); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * DMIC clock rate is a multiple of 'Over Sampling Ratio' and 1058c2ecf20Sopenharmony_ci * 'Sample Rate'. The supported OSR values are 64, 128 and 256. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci clk_rate = (DMIC_OSR_FACTOR << dmic->osr_val) * srate; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci err = clk_set_rate(dmic->clk_dmic, clk_rate); 1108c2ecf20Sopenharmony_ci if (err) { 1118c2ecf20Sopenharmony_ci dev_err(dai->dev, "can't set DMIC clock rate %u, err: %d\n", 1128c2ecf20Sopenharmony_ci clk_rate, err); 1138c2ecf20Sopenharmony_ci return err; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci regmap_update_bits(dmic->regmap, 1178c2ecf20Sopenharmony_ci /* Reg */ 1188c2ecf20Sopenharmony_ci TEGRA210_DMIC_CTRL, 1198c2ecf20Sopenharmony_ci /* Mask */ 1208c2ecf20Sopenharmony_ci TEGRA210_DMIC_CTRL_LRSEL_POLARITY_MASK | 1218c2ecf20Sopenharmony_ci TEGRA210_DMIC_CTRL_OSR_MASK | 1228c2ecf20Sopenharmony_ci TEGRA210_DMIC_CTRL_CHANNEL_SELECT_MASK, 1238c2ecf20Sopenharmony_ci /* Value */ 1248c2ecf20Sopenharmony_ci (dmic->lrsel << LRSEL_POL_SHIFT) | 1258c2ecf20Sopenharmony_ci (dmic->osr_val << OSR_SHIFT) | 1268c2ecf20Sopenharmony_ci ((dmic->ch_select + 1) << CH_SEL_SHIFT)); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * Use LP filter gain register to apply boost. 1308c2ecf20Sopenharmony_ci * Boost Gain Volume control has 100x factor. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci if (dmic->boost_gain) 1338c2ecf20Sopenharmony_ci gain_q23 = div_u64(gain_q23 * dmic->boost_gain, 100); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci regmap_write(dmic->regmap, TEGRA210_DMIC_LP_FILTER_GAIN, 1368c2ecf20Sopenharmony_ci (unsigned int)gain_q23); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci switch (params_format(params)) { 1398c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 1408c2ecf20Sopenharmony_ci cif_conf.audio_bits = TEGRA_ACIF_BITS_16; 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 1438c2ecf20Sopenharmony_ci cif_conf.audio_bits = TEGRA_ACIF_BITS_32; 1448c2ecf20Sopenharmony_ci break; 1458c2ecf20Sopenharmony_ci default: 1468c2ecf20Sopenharmony_ci dev_err(dai->dev, "unsupported format!\n"); 1478c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci cif_conf.client_bits = TEGRA_ACIF_BITS_24; 1518c2ecf20Sopenharmony_ci cif_conf.mono_conv = dmic->mono_to_stereo; 1528c2ecf20Sopenharmony_ci cif_conf.stereo_conv = dmic->stereo_to_mono; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci tegra_set_cif(dmic->regmap, TEGRA210_DMIC_TX_CIF_CTRL, &cif_conf); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int tegra210_dmic_get_boost_gain(struct snd_kcontrol *kcontrol, 1608c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 1638c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = dmic->boost_gain; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int tegra210_dmic_put_boost_gain(struct snd_kcontrol *kcontrol, 1718c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 1748c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 1758c2ecf20Sopenharmony_ci int value = ucontrol->value.integer.value[0]; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (value == dmic->boost_gain) 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci dmic->boost_gain = value; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 1; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int tegra210_dmic_get_ch_select(struct snd_kcontrol *kcontrol, 1868c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 1898c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dmic->ch_select; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int tegra210_dmic_put_ch_select(struct snd_kcontrol *kcontrol, 1978c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 2008c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 2018c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (value == dmic->ch_select) 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci dmic->ch_select = value; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 1; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int tegra210_dmic_get_mono_to_stereo(struct snd_kcontrol *kcontrol, 2128c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 2158c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dmic->mono_to_stereo; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int tegra210_dmic_put_mono_to_stereo(struct snd_kcontrol *kcontrol, 2238c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 2268c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 2278c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (value == dmic->mono_to_stereo) 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci dmic->mono_to_stereo = value; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 1; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int tegra210_dmic_get_stereo_to_mono(struct snd_kcontrol *kcontrol, 2388c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 2418c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dmic->stereo_to_mono; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int tegra210_dmic_put_stereo_to_mono(struct snd_kcontrol *kcontrol, 2498c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 2528c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 2538c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (value == dmic->stereo_to_mono) 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci dmic->stereo_to_mono = value; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 1; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int tegra210_dmic_get_osr_val(struct snd_kcontrol *kcontrol, 2648c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 2678c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dmic->osr_val; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int tegra210_dmic_put_osr_val(struct snd_kcontrol *kcontrol, 2758c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 2788c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 2798c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (value == dmic->osr_val) 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci dmic->osr_val = value; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return 1; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int tegra210_dmic_get_pol_sel(struct snd_kcontrol *kcontrol, 2908c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 2938c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dmic->lrsel; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int tegra210_dmic_put_pol_sel(struct snd_kcontrol *kcontrol, 3018c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); 3048c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); 3058c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (value == dmic->lrsel) 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci dmic->lrsel = value; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return 1; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops tegra210_dmic_dai_ops = { 3168c2ecf20Sopenharmony_ci .hw_params = tegra210_dmic_hw_params, 3178c2ecf20Sopenharmony_ci}; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver tegra210_dmic_dais[] = { 3208c2ecf20Sopenharmony_ci { 3218c2ecf20Sopenharmony_ci .name = "DMIC-CIF", 3228c2ecf20Sopenharmony_ci .capture = { 3238c2ecf20Sopenharmony_ci .stream_name = "CIF-Capture", 3248c2ecf20Sopenharmony_ci .channels_min = 1, 3258c2ecf20Sopenharmony_ci .channels_max = 2, 3268c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 3278c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 3288c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 3298c2ecf20Sopenharmony_ci }, 3308c2ecf20Sopenharmony_ci }, 3318c2ecf20Sopenharmony_ci { 3328c2ecf20Sopenharmony_ci .name = "DMIC-DAP", 3338c2ecf20Sopenharmony_ci .capture = { 3348c2ecf20Sopenharmony_ci .stream_name = "DAP-Capture", 3358c2ecf20Sopenharmony_ci .channels_min = 1, 3368c2ecf20Sopenharmony_ci .channels_max = 2, 3378c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 3388c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 3398c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 3408c2ecf20Sopenharmony_ci }, 3418c2ecf20Sopenharmony_ci .ops = &tegra210_dmic_dai_ops, 3428c2ecf20Sopenharmony_ci .symmetric_rates = 1, 3438c2ecf20Sopenharmony_ci }, 3448c2ecf20Sopenharmony_ci}; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget tegra210_dmic_widgets[] = { 3478c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_DMIC_ENABLE, 0, 0), 3488c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("MIC", NULL), 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route tegra210_dmic_routes[] = { 3528c2ecf20Sopenharmony_ci { "XBAR-RX", NULL, "XBAR-Capture" }, 3538c2ecf20Sopenharmony_ci { "XBAR-Capture", NULL, "CIF-Capture" }, 3548c2ecf20Sopenharmony_ci { "CIF-Capture", NULL, "TX" }, 3558c2ecf20Sopenharmony_ci { "TX", NULL, "DAP-Capture" }, 3568c2ecf20Sopenharmony_ci { "DAP-Capture", NULL, "MIC" }, 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic const char * const tegra210_dmic_ch_select[] = { 3608c2ecf20Sopenharmony_ci "Left", "Right", "Stereo", 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic const struct soc_enum tegra210_dmic_ch_enum = 3648c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_ch_select), 3658c2ecf20Sopenharmony_ci tegra210_dmic_ch_select); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic const char * const tegra210_dmic_mono_conv_text[] = { 3688c2ecf20Sopenharmony_ci "Zero", "Copy", 3698c2ecf20Sopenharmony_ci}; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic const char * const tegra210_dmic_stereo_conv_text[] = { 3728c2ecf20Sopenharmony_ci "CH0", "CH1", "AVG", 3738c2ecf20Sopenharmony_ci}; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic const struct soc_enum tegra210_dmic_mono_conv_enum = 3768c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_mono_conv_text), 3778c2ecf20Sopenharmony_ci tegra210_dmic_mono_conv_text); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic const struct soc_enum tegra210_dmic_stereo_conv_enum = 3808c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_stereo_conv_text), 3818c2ecf20Sopenharmony_ci tegra210_dmic_stereo_conv_text); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic const char * const tegra210_dmic_osr_text[] = { 3848c2ecf20Sopenharmony_ci "OSR_64", "OSR_128", "OSR_256", 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic const struct soc_enum tegra210_dmic_osr_enum = 3888c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_osr_text), 3898c2ecf20Sopenharmony_ci tegra210_dmic_osr_text); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic const char * const tegra210_dmic_lrsel_text[] = { 3928c2ecf20Sopenharmony_ci "Left", "Right", 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const struct soc_enum tegra210_dmic_lrsel_enum = 3968c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_lrsel_text), 3978c2ecf20Sopenharmony_ci tegra210_dmic_lrsel_text); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new tegra210_dmic_controls[] = { 4008c2ecf20Sopenharmony_ci SOC_SINGLE_EXT("Boost Gain Volume", 0, 0, MAX_BOOST_GAIN, 0, 4018c2ecf20Sopenharmony_ci tegra210_dmic_get_boost_gain, 4028c2ecf20Sopenharmony_ci tegra210_dmic_put_boost_gain), 4038c2ecf20Sopenharmony_ci SOC_ENUM_EXT("Channel Select", tegra210_dmic_ch_enum, 4048c2ecf20Sopenharmony_ci tegra210_dmic_get_ch_select, tegra210_dmic_put_ch_select), 4058c2ecf20Sopenharmony_ci SOC_ENUM_EXT("Mono To Stereo", 4068c2ecf20Sopenharmony_ci tegra210_dmic_mono_conv_enum, 4078c2ecf20Sopenharmony_ci tegra210_dmic_get_mono_to_stereo, 4088c2ecf20Sopenharmony_ci tegra210_dmic_put_mono_to_stereo), 4098c2ecf20Sopenharmony_ci SOC_ENUM_EXT("Stereo To Mono", 4108c2ecf20Sopenharmony_ci tegra210_dmic_stereo_conv_enum, 4118c2ecf20Sopenharmony_ci tegra210_dmic_get_stereo_to_mono, 4128c2ecf20Sopenharmony_ci tegra210_dmic_put_stereo_to_mono), 4138c2ecf20Sopenharmony_ci SOC_ENUM_EXT("OSR Value", tegra210_dmic_osr_enum, 4148c2ecf20Sopenharmony_ci tegra210_dmic_get_osr_val, tegra210_dmic_put_osr_val), 4158c2ecf20Sopenharmony_ci SOC_ENUM_EXT("LR Polarity Select", tegra210_dmic_lrsel_enum, 4168c2ecf20Sopenharmony_ci tegra210_dmic_get_pol_sel, tegra210_dmic_put_pol_sel), 4178c2ecf20Sopenharmony_ci}; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver tegra210_dmic_compnt = { 4208c2ecf20Sopenharmony_ci .dapm_widgets = tegra210_dmic_widgets, 4218c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tegra210_dmic_widgets), 4228c2ecf20Sopenharmony_ci .dapm_routes = tegra210_dmic_routes, 4238c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(tegra210_dmic_routes), 4248c2ecf20Sopenharmony_ci .controls = tegra210_dmic_controls, 4258c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(tegra210_dmic_controls), 4268c2ecf20Sopenharmony_ci}; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic bool tegra210_dmic_wr_reg(struct device *dev, unsigned int reg) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci switch (reg) { 4318c2ecf20Sopenharmony_ci case TEGRA210_DMIC_TX_INT_MASK ... TEGRA210_DMIC_TX_CIF_CTRL: 4328c2ecf20Sopenharmony_ci case TEGRA210_DMIC_ENABLE ... TEGRA210_DMIC_CG: 4338c2ecf20Sopenharmony_ci case TEGRA210_DMIC_CTRL: 4348c2ecf20Sopenharmony_ci case TEGRA210_DMIC_DBG_CTRL: 4358c2ecf20Sopenharmony_ci case TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4 ... TEGRA210_DMIC_LP_BIQUAD_1_COEF_4: 4368c2ecf20Sopenharmony_ci return true; 4378c2ecf20Sopenharmony_ci default: 4388c2ecf20Sopenharmony_ci return false; 4398c2ecf20Sopenharmony_ci }; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic bool tegra210_dmic_rd_reg(struct device *dev, unsigned int reg) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci if (tegra210_dmic_wr_reg(dev, reg)) 4458c2ecf20Sopenharmony_ci return true; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci switch (reg) { 4488c2ecf20Sopenharmony_ci case TEGRA210_DMIC_TX_STATUS: 4498c2ecf20Sopenharmony_ci case TEGRA210_DMIC_TX_INT_STATUS: 4508c2ecf20Sopenharmony_ci case TEGRA210_DMIC_STATUS: 4518c2ecf20Sopenharmony_ci case TEGRA210_DMIC_INT_STATUS: 4528c2ecf20Sopenharmony_ci return true; 4538c2ecf20Sopenharmony_ci default: 4548c2ecf20Sopenharmony_ci return false; 4558c2ecf20Sopenharmony_ci }; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic bool tegra210_dmic_volatile_reg(struct device *dev, unsigned int reg) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci switch (reg) { 4618c2ecf20Sopenharmony_ci case TEGRA210_DMIC_TX_STATUS: 4628c2ecf20Sopenharmony_ci case TEGRA210_DMIC_TX_INT_STATUS: 4638c2ecf20Sopenharmony_ci case TEGRA210_DMIC_TX_INT_SET: 4648c2ecf20Sopenharmony_ci case TEGRA210_DMIC_SOFT_RESET: 4658c2ecf20Sopenharmony_ci case TEGRA210_DMIC_STATUS: 4668c2ecf20Sopenharmony_ci case TEGRA210_DMIC_INT_STATUS: 4678c2ecf20Sopenharmony_ci return true; 4688c2ecf20Sopenharmony_ci default: 4698c2ecf20Sopenharmony_ci return false; 4708c2ecf20Sopenharmony_ci }; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic const struct regmap_config tegra210_dmic_regmap_config = { 4748c2ecf20Sopenharmony_ci .reg_bits = 32, 4758c2ecf20Sopenharmony_ci .reg_stride = 4, 4768c2ecf20Sopenharmony_ci .val_bits = 32, 4778c2ecf20Sopenharmony_ci .max_register = TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, 4788c2ecf20Sopenharmony_ci .writeable_reg = tegra210_dmic_wr_reg, 4798c2ecf20Sopenharmony_ci .readable_reg = tegra210_dmic_rd_reg, 4808c2ecf20Sopenharmony_ci .volatile_reg = tegra210_dmic_volatile_reg, 4818c2ecf20Sopenharmony_ci .reg_defaults = tegra210_dmic_reg_defaults, 4828c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(tegra210_dmic_reg_defaults), 4838c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 4848c2ecf20Sopenharmony_ci}; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int tegra210_dmic_probe(struct platform_device *pdev) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4898c2ecf20Sopenharmony_ci struct tegra210_dmic *dmic; 4908c2ecf20Sopenharmony_ci void __iomem *regs; 4918c2ecf20Sopenharmony_ci int err; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci dmic = devm_kzalloc(dev, sizeof(*dmic), GFP_KERNEL); 4948c2ecf20Sopenharmony_ci if (!dmic) 4958c2ecf20Sopenharmony_ci return -ENOMEM; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci dmic->osr_val = DMIC_OSR_64; 4988c2ecf20Sopenharmony_ci dmic->ch_select = DMIC_CH_SELECT_STEREO; 4998c2ecf20Sopenharmony_ci dmic->lrsel = DMIC_LRSEL_LEFT; 5008c2ecf20Sopenharmony_ci dmic->boost_gain = 0; 5018c2ecf20Sopenharmony_ci dmic->stereo_to_mono = 0; /* "CH0" */ 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci dev_set_drvdata(dev, dmic); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci dmic->clk_dmic = devm_clk_get(dev, "dmic"); 5068c2ecf20Sopenharmony_ci if (IS_ERR(dmic->clk_dmic)) { 5078c2ecf20Sopenharmony_ci dev_err(dev, "can't retrieve DMIC clock\n"); 5088c2ecf20Sopenharmony_ci return PTR_ERR(dmic->clk_dmic); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci regs = devm_platform_ioremap_resource(pdev, 0); 5128c2ecf20Sopenharmony_ci if (IS_ERR(regs)) 5138c2ecf20Sopenharmony_ci return PTR_ERR(regs); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci dmic->regmap = devm_regmap_init_mmio(dev, regs, 5168c2ecf20Sopenharmony_ci &tegra210_dmic_regmap_config); 5178c2ecf20Sopenharmony_ci if (IS_ERR(dmic->regmap)) { 5188c2ecf20Sopenharmony_ci dev_err(dev, "regmap init failed\n"); 5198c2ecf20Sopenharmony_ci return PTR_ERR(dmic->regmap); 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci regcache_cache_only(dmic->regmap, true); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci err = devm_snd_soc_register_component(dev, &tegra210_dmic_compnt, 5258c2ecf20Sopenharmony_ci tegra210_dmic_dais, 5268c2ecf20Sopenharmony_ci ARRAY_SIZE(tegra210_dmic_dais)); 5278c2ecf20Sopenharmony_ci if (err) { 5288c2ecf20Sopenharmony_ci dev_err(dev, "can't register DMIC component, err: %d\n", err); 5298c2ecf20Sopenharmony_ci return err; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci return 0; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic int tegra210_dmic_remove(struct platform_device *pdev) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return 0; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tegra210_dmic_pm_ops = { 5458c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(tegra210_dmic_runtime_suspend, 5468c2ecf20Sopenharmony_ci tegra210_dmic_runtime_resume, NULL) 5478c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 5488c2ecf20Sopenharmony_ci pm_runtime_force_resume) 5498c2ecf20Sopenharmony_ci}; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic const struct of_device_id tegra210_dmic_of_match[] = { 5528c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra210-dmic" }, 5538c2ecf20Sopenharmony_ci {}, 5548c2ecf20Sopenharmony_ci}; 5558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra210_dmic_of_match); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic struct platform_driver tegra210_dmic_driver = { 5588c2ecf20Sopenharmony_ci .driver = { 5598c2ecf20Sopenharmony_ci .name = "tegra210-dmic", 5608c2ecf20Sopenharmony_ci .of_match_table = tegra210_dmic_of_match, 5618c2ecf20Sopenharmony_ci .pm = &tegra210_dmic_pm_ops, 5628c2ecf20Sopenharmony_ci }, 5638c2ecf20Sopenharmony_ci .probe = tegra210_dmic_probe, 5648c2ecf20Sopenharmony_ci .remove = tegra210_dmic_remove, 5658c2ecf20Sopenharmony_ci}; 5668c2ecf20Sopenharmony_cimodule_platform_driver(tegra210_dmic_driver) 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rahul Mittal <rmittal@nvidia.com>"); 5698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra210 ASoC DMIC driver"); 5708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 571