18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// tegra186_dspk.c - Tegra186 DSPK 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/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of.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 "tegra186_dspk.h" 198c2ecf20Sopenharmony_ci#include "tegra_cif.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic const struct reg_default tegra186_dspk_reg_defaults[] = { 228c2ecf20Sopenharmony_ci { TEGRA186_DSPK_RX_INT_MASK, 0x00000007 }, 238c2ecf20Sopenharmony_ci { TEGRA186_DSPK_RX_CIF_CTRL, 0x00007700 }, 248c2ecf20Sopenharmony_ci { TEGRA186_DSPK_CG, 0x00000001 }, 258c2ecf20Sopenharmony_ci { TEGRA186_DSPK_CORE_CTRL, 0x00000310 }, 268c2ecf20Sopenharmony_ci { TEGRA186_DSPK_CODEC_CTRL, 0x03000000 }, 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int tegra186_dspk_get_fifo_th(struct snd_kcontrol *kcontrol, 308c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 338c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = dspk->rx_fifo_th; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int tegra186_dspk_put_fifo_th(struct snd_kcontrol *kcontrol, 418c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 448c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 458c2ecf20Sopenharmony_ci int value = ucontrol->value.integer.value[0]; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (value == dspk->rx_fifo_th) 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci dspk->rx_fifo_th = value; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return 1; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int tegra186_dspk_get_osr_val(struct snd_kcontrol *kcontrol, 568c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 598c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dspk->osr_val; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int tegra186_dspk_put_osr_val(struct snd_kcontrol *kcontrol, 678c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 708c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 718c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (value == dspk->osr_val) 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci dspk->osr_val = value; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return 1; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int tegra186_dspk_get_pol_sel(struct snd_kcontrol *kcontrol, 828c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 858c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dspk->lrsel; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int tegra186_dspk_put_pol_sel(struct snd_kcontrol *kcontrol, 938c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 968c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 978c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (value == dspk->lrsel) 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci dspk->lrsel = value; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 1; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int tegra186_dspk_get_ch_sel(struct snd_kcontrol *kcontrol, 1088c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 1118c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dspk->ch_sel; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int tegra186_dspk_put_ch_sel(struct snd_kcontrol *kcontrol, 1198c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 1228c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 1238c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (value == dspk->ch_sel) 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci dspk->ch_sel = value; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 1; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int tegra186_dspk_get_mono_to_stereo(struct snd_kcontrol *kcontrol, 1348c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 1378c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dspk->mono_to_stereo; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int tegra186_dspk_put_mono_to_stereo(struct snd_kcontrol *kcontrol, 1458c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 1488c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 1498c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (value == dspk->mono_to_stereo) 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci dspk->mono_to_stereo = value; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 1; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int tegra186_dspk_get_stereo_to_mono(struct snd_kcontrol *kcontrol, 1608c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 1638c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dspk->stereo_to_mono; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int tegra186_dspk_put_stereo_to_mono(struct snd_kcontrol *kcontrol, 1718c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); 1748c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); 1758c2ecf20Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (value == dspk->stereo_to_mono) 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci dspk->stereo_to_mono = value; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 1; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int __maybe_unused tegra186_dspk_runtime_suspend(struct device *dev) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = dev_get_drvdata(dev); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci regcache_cache_only(dspk->regmap, true); 1908c2ecf20Sopenharmony_ci regcache_mark_dirty(dspk->regmap); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci clk_disable_unprepare(dspk->clk_dspk); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int __maybe_unused tegra186_dspk_runtime_resume(struct device *dev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = dev_get_drvdata(dev); 2008c2ecf20Sopenharmony_ci int err; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci err = clk_prepare_enable(dspk->clk_dspk); 2038c2ecf20Sopenharmony_ci if (err) { 2048c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable DSPK clock, err: %d\n", err); 2058c2ecf20Sopenharmony_ci return err; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci regcache_cache_only(dspk->regmap, false); 2098c2ecf20Sopenharmony_ci regcache_sync(dspk->regmap); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int tegra186_dspk_hw_params(struct snd_pcm_substream *substream, 2158c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 2168c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk = snd_soc_dai_get_drvdata(dai); 2198c2ecf20Sopenharmony_ci unsigned int channels, srate, dspk_clk; 2208c2ecf20Sopenharmony_ci struct device *dev = dai->dev; 2218c2ecf20Sopenharmony_ci struct tegra_cif_conf cif_conf; 2228c2ecf20Sopenharmony_ci unsigned int max_th; 2238c2ecf20Sopenharmony_ci int err; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci channels = params_channels(params); 2288c2ecf20Sopenharmony_ci cif_conf.audio_ch = channels; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Client channel */ 2318c2ecf20Sopenharmony_ci switch (dspk->ch_sel) { 2328c2ecf20Sopenharmony_ci case DSPK_CH_SELECT_LEFT: 2338c2ecf20Sopenharmony_ci case DSPK_CH_SELECT_RIGHT: 2348c2ecf20Sopenharmony_ci cif_conf.client_ch = 1; 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci case DSPK_CH_SELECT_STEREO: 2378c2ecf20Sopenharmony_ci cif_conf.client_ch = 2; 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci default: 2408c2ecf20Sopenharmony_ci dev_err(dev, "Invalid DSPK client channels\n"); 2418c2ecf20Sopenharmony_ci return -EINVAL; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci cif_conf.client_bits = TEGRA_ACIF_BITS_24; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci switch (params_format(params)) { 2478c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 2488c2ecf20Sopenharmony_ci cif_conf.audio_bits = TEGRA_ACIF_BITS_16; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 2518c2ecf20Sopenharmony_ci cif_conf.audio_bits = TEGRA_ACIF_BITS_32; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci default: 2548c2ecf20Sopenharmony_ci dev_err(dev, "unsupported format!\n"); 2558c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci srate = params_rate(params); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* RX FIFO threshold in terms of frames */ 2618c2ecf20Sopenharmony_ci max_th = (TEGRA186_DSPK_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (dspk->rx_fifo_th > max_th) 2648c2ecf20Sopenharmony_ci dspk->rx_fifo_th = max_th; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci cif_conf.threshold = dspk->rx_fifo_th; 2678c2ecf20Sopenharmony_ci cif_conf.mono_conv = dspk->mono_to_stereo; 2688c2ecf20Sopenharmony_ci cif_conf.stereo_conv = dspk->stereo_to_mono; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci tegra_set_cif(dspk->regmap, TEGRA186_DSPK_RX_CIF_CTRL, 2718c2ecf20Sopenharmony_ci &cif_conf); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * DSPK clock and PDM codec clock should be synchronous with 4:1 ratio, 2758c2ecf20Sopenharmony_ci * this is because it takes 4 clock cycles to send out one sample to 2768c2ecf20Sopenharmony_ci * codec by sigma delta modulator. Finally the clock rate is a multiple 2778c2ecf20Sopenharmony_ci * of 'Over Sampling Ratio', 'Sample Rate' and 'Interface Clock Ratio'. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci dspk_clk = (DSPK_OSR_FACTOR << dspk->osr_val) * srate * DSPK_CLK_RATIO; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci err = clk_set_rate(dspk->clk_dspk, dspk_clk); 2828c2ecf20Sopenharmony_ci if (err) { 2838c2ecf20Sopenharmony_ci dev_err(dev, "can't set DSPK clock rate %u, err: %d\n", 2848c2ecf20Sopenharmony_ci dspk_clk, err); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return err; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci regmap_update_bits(dspk->regmap, 2908c2ecf20Sopenharmony_ci /* Reg */ 2918c2ecf20Sopenharmony_ci TEGRA186_DSPK_CORE_CTRL, 2928c2ecf20Sopenharmony_ci /* Mask */ 2938c2ecf20Sopenharmony_ci TEGRA186_DSPK_OSR_MASK | 2948c2ecf20Sopenharmony_ci TEGRA186_DSPK_CHANNEL_SELECT_MASK | 2958c2ecf20Sopenharmony_ci TEGRA186_DSPK_CTRL_LRSEL_POLARITY_MASK, 2968c2ecf20Sopenharmony_ci /* Value */ 2978c2ecf20Sopenharmony_ci (dspk->osr_val << DSPK_OSR_SHIFT) | 2988c2ecf20Sopenharmony_ci ((dspk->ch_sel + 1) << CH_SEL_SHIFT) | 2998c2ecf20Sopenharmony_ci (dspk->lrsel << LRSEL_POL_SHIFT)); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops tegra186_dspk_dai_ops = { 3058c2ecf20Sopenharmony_ci .hw_params = tegra186_dspk_hw_params, 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver tegra186_dspk_dais[] = { 3098c2ecf20Sopenharmony_ci { 3108c2ecf20Sopenharmony_ci .name = "DSPK-CIF", 3118c2ecf20Sopenharmony_ci .playback = { 3128c2ecf20Sopenharmony_ci .stream_name = "CIF-Playback", 3138c2ecf20Sopenharmony_ci .channels_min = 1, 3148c2ecf20Sopenharmony_ci .channels_max = 2, 3158c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 3168c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 3178c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 3188c2ecf20Sopenharmony_ci }, 3198c2ecf20Sopenharmony_ci }, 3208c2ecf20Sopenharmony_ci { 3218c2ecf20Sopenharmony_ci .name = "DSPK-DAP", 3228c2ecf20Sopenharmony_ci .playback = { 3238c2ecf20Sopenharmony_ci .stream_name = "DAP-Playback", 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 .ops = &tegra186_dspk_dai_ops, 3318c2ecf20Sopenharmony_ci .symmetric_rates = 1, 3328c2ecf20Sopenharmony_ci }, 3338c2ecf20Sopenharmony_ci}; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget tegra186_dspk_widgets[] = { 3368c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA186_DSPK_ENABLE, 0, 0), 3378c2ecf20Sopenharmony_ci SND_SOC_DAPM_SPK("SPK", NULL), 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route tegra186_dspk_routes[] = { 3418c2ecf20Sopenharmony_ci { "XBAR-Playback", NULL, "XBAR-TX" }, 3428c2ecf20Sopenharmony_ci { "CIF-Playback", NULL, "XBAR-Playback" }, 3438c2ecf20Sopenharmony_ci { "RX", NULL, "CIF-Playback" }, 3448c2ecf20Sopenharmony_ci { "DAP-Playback", NULL, "RX" }, 3458c2ecf20Sopenharmony_ci { "SPK", NULL, "DAP-Playback" }, 3468c2ecf20Sopenharmony_ci}; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic const char * const tegra186_dspk_ch_sel_text[] = { 3498c2ecf20Sopenharmony_ci "Left", "Right", "Stereo", 3508c2ecf20Sopenharmony_ci}; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic const struct soc_enum tegra186_dspk_ch_sel_enum = 3538c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_ch_sel_text), 3548c2ecf20Sopenharmony_ci tegra186_dspk_ch_sel_text); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic const char * const tegra186_dspk_osr_text[] = { 3578c2ecf20Sopenharmony_ci "OSR_32", "OSR_64", "OSR_128", "OSR_256", 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic const struct soc_enum tegra186_dspk_osr_enum = 3618c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_osr_text), 3628c2ecf20Sopenharmony_ci tegra186_dspk_osr_text); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic const char * const tegra186_dspk_lrsel_text[] = { 3658c2ecf20Sopenharmony_ci "Left", "Right", 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const char * const tegra186_dspk_mono_conv_text[] = { 3698c2ecf20Sopenharmony_ci "Zero", "Copy", 3708c2ecf20Sopenharmony_ci}; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic const struct soc_enum tegra186_dspk_mono_conv_enum = 3738c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 3748c2ecf20Sopenharmony_ci ARRAY_SIZE(tegra186_dspk_mono_conv_text), 3758c2ecf20Sopenharmony_ci tegra186_dspk_mono_conv_text); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic const char * const tegra186_dspk_stereo_conv_text[] = { 3788c2ecf20Sopenharmony_ci "CH0", "CH1", "AVG", 3798c2ecf20Sopenharmony_ci}; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic const struct soc_enum tegra186_dspk_stereo_conv_enum = 3828c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 3838c2ecf20Sopenharmony_ci ARRAY_SIZE(tegra186_dspk_stereo_conv_text), 3848c2ecf20Sopenharmony_ci tegra186_dspk_stereo_conv_text); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic const struct soc_enum tegra186_dspk_lrsel_enum = 3878c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_lrsel_text), 3888c2ecf20Sopenharmony_ci tegra186_dspk_lrsel_text); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new tegrat186_dspk_controls[] = { 3918c2ecf20Sopenharmony_ci SOC_SINGLE_EXT("FIFO Threshold", SND_SOC_NOPM, 0, 3928c2ecf20Sopenharmony_ci TEGRA186_DSPK_RX_FIFO_DEPTH - 1, 0, 3938c2ecf20Sopenharmony_ci tegra186_dspk_get_fifo_th, tegra186_dspk_put_fifo_th), 3948c2ecf20Sopenharmony_ci SOC_ENUM_EXT("OSR Value", tegra186_dspk_osr_enum, 3958c2ecf20Sopenharmony_ci tegra186_dspk_get_osr_val, tegra186_dspk_put_osr_val), 3968c2ecf20Sopenharmony_ci SOC_ENUM_EXT("LR Polarity Select", tegra186_dspk_lrsel_enum, 3978c2ecf20Sopenharmony_ci tegra186_dspk_get_pol_sel, tegra186_dspk_put_pol_sel), 3988c2ecf20Sopenharmony_ci SOC_ENUM_EXT("Channel Select", tegra186_dspk_ch_sel_enum, 3998c2ecf20Sopenharmony_ci tegra186_dspk_get_ch_sel, tegra186_dspk_put_ch_sel), 4008c2ecf20Sopenharmony_ci SOC_ENUM_EXT("Mono To Stereo", tegra186_dspk_mono_conv_enum, 4018c2ecf20Sopenharmony_ci tegra186_dspk_get_mono_to_stereo, 4028c2ecf20Sopenharmony_ci tegra186_dspk_put_mono_to_stereo), 4038c2ecf20Sopenharmony_ci SOC_ENUM_EXT("Stereo To Mono", tegra186_dspk_stereo_conv_enum, 4048c2ecf20Sopenharmony_ci tegra186_dspk_get_stereo_to_mono, 4058c2ecf20Sopenharmony_ci tegra186_dspk_put_stereo_to_mono), 4068c2ecf20Sopenharmony_ci}; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver tegra186_dspk_cmpnt = { 4098c2ecf20Sopenharmony_ci .dapm_widgets = tegra186_dspk_widgets, 4108c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tegra186_dspk_widgets), 4118c2ecf20Sopenharmony_ci .dapm_routes = tegra186_dspk_routes, 4128c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(tegra186_dspk_routes), 4138c2ecf20Sopenharmony_ci .controls = tegrat186_dspk_controls, 4148c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(tegrat186_dspk_controls), 4158c2ecf20Sopenharmony_ci}; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic bool tegra186_dspk_wr_reg(struct device *dev, unsigned int reg) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci switch (reg) { 4208c2ecf20Sopenharmony_ci case TEGRA186_DSPK_RX_INT_MASK ... TEGRA186_DSPK_RX_CIF_CTRL: 4218c2ecf20Sopenharmony_ci case TEGRA186_DSPK_ENABLE ... TEGRA186_DSPK_CG: 4228c2ecf20Sopenharmony_ci case TEGRA186_DSPK_CORE_CTRL ... TEGRA186_DSPK_CODEC_CTRL: 4238c2ecf20Sopenharmony_ci return true; 4248c2ecf20Sopenharmony_ci default: 4258c2ecf20Sopenharmony_ci return false; 4268c2ecf20Sopenharmony_ci }; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic bool tegra186_dspk_rd_reg(struct device *dev, unsigned int reg) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci if (tegra186_dspk_wr_reg(dev, reg)) 4328c2ecf20Sopenharmony_ci return true; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci switch (reg) { 4358c2ecf20Sopenharmony_ci case TEGRA186_DSPK_RX_STATUS: 4368c2ecf20Sopenharmony_ci case TEGRA186_DSPK_RX_INT_STATUS: 4378c2ecf20Sopenharmony_ci case TEGRA186_DSPK_STATUS: 4388c2ecf20Sopenharmony_ci case TEGRA186_DSPK_INT_STATUS: 4398c2ecf20Sopenharmony_ci return true; 4408c2ecf20Sopenharmony_ci default: 4418c2ecf20Sopenharmony_ci return false; 4428c2ecf20Sopenharmony_ci }; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic bool tegra186_dspk_volatile_reg(struct device *dev, unsigned int reg) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci switch (reg) { 4488c2ecf20Sopenharmony_ci case TEGRA186_DSPK_RX_STATUS: 4498c2ecf20Sopenharmony_ci case TEGRA186_DSPK_RX_INT_STATUS: 4508c2ecf20Sopenharmony_ci case TEGRA186_DSPK_STATUS: 4518c2ecf20Sopenharmony_ci case TEGRA186_DSPK_INT_STATUS: 4528c2ecf20Sopenharmony_ci return true; 4538c2ecf20Sopenharmony_ci default: 4548c2ecf20Sopenharmony_ci return false; 4558c2ecf20Sopenharmony_ci }; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic const struct regmap_config tegra186_dspk_regmap = { 4598c2ecf20Sopenharmony_ci .reg_bits = 32, 4608c2ecf20Sopenharmony_ci .reg_stride = 4, 4618c2ecf20Sopenharmony_ci .val_bits = 32, 4628c2ecf20Sopenharmony_ci .max_register = TEGRA186_DSPK_CODEC_CTRL, 4638c2ecf20Sopenharmony_ci .writeable_reg = tegra186_dspk_wr_reg, 4648c2ecf20Sopenharmony_ci .readable_reg = tegra186_dspk_rd_reg, 4658c2ecf20Sopenharmony_ci .volatile_reg = tegra186_dspk_volatile_reg, 4668c2ecf20Sopenharmony_ci .reg_defaults = tegra186_dspk_reg_defaults, 4678c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(tegra186_dspk_reg_defaults), 4688c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 4698c2ecf20Sopenharmony_ci}; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic const struct of_device_id tegra186_dspk_of_match[] = { 4728c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra186-dspk" }, 4738c2ecf20Sopenharmony_ci {}, 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra186_dspk_of_match); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int tegra186_dspk_platform_probe(struct platform_device *pdev) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4808c2ecf20Sopenharmony_ci struct tegra186_dspk *dspk; 4818c2ecf20Sopenharmony_ci void __iomem *regs; 4828c2ecf20Sopenharmony_ci int err; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci dspk = devm_kzalloc(dev, sizeof(*dspk), GFP_KERNEL); 4858c2ecf20Sopenharmony_ci if (!dspk) 4868c2ecf20Sopenharmony_ci return -ENOMEM; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci dspk->osr_val = DSPK_OSR_64; 4898c2ecf20Sopenharmony_ci dspk->lrsel = DSPK_LRSEL_LEFT; 4908c2ecf20Sopenharmony_ci dspk->ch_sel = DSPK_CH_SELECT_STEREO; 4918c2ecf20Sopenharmony_ci dspk->mono_to_stereo = 0; /* "Zero" */ 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci dev_set_drvdata(dev, dspk); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci dspk->clk_dspk = devm_clk_get(dev, "dspk"); 4968c2ecf20Sopenharmony_ci if (IS_ERR(dspk->clk_dspk)) { 4978c2ecf20Sopenharmony_ci dev_err(dev, "can't retrieve DSPK clock\n"); 4988c2ecf20Sopenharmony_ci return PTR_ERR(dspk->clk_dspk); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci regs = devm_platform_ioremap_resource(pdev, 0); 5028c2ecf20Sopenharmony_ci if (IS_ERR(regs)) 5038c2ecf20Sopenharmony_ci return PTR_ERR(regs); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci dspk->regmap = devm_regmap_init_mmio(dev, regs, &tegra186_dspk_regmap); 5068c2ecf20Sopenharmony_ci if (IS_ERR(dspk->regmap)) { 5078c2ecf20Sopenharmony_ci dev_err(dev, "regmap init failed\n"); 5088c2ecf20Sopenharmony_ci return PTR_ERR(dspk->regmap); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci regcache_cache_only(dspk->regmap, true); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci err = devm_snd_soc_register_component(dev, &tegra186_dspk_cmpnt, 5148c2ecf20Sopenharmony_ci tegra186_dspk_dais, 5158c2ecf20Sopenharmony_ci ARRAY_SIZE(tegra186_dspk_dais)); 5168c2ecf20Sopenharmony_ci if (err) { 5178c2ecf20Sopenharmony_ci dev_err(dev, "can't register DSPK component, err: %d\n", 5188c2ecf20Sopenharmony_ci err); 5198c2ecf20Sopenharmony_ci return err; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return 0; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic int tegra186_dspk_platform_remove(struct platform_device *pdev) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tegra186_dspk_pm_ops = { 5358c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(tegra186_dspk_runtime_suspend, 5368c2ecf20Sopenharmony_ci tegra186_dspk_runtime_resume, NULL) 5378c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 5388c2ecf20Sopenharmony_ci pm_runtime_force_resume) 5398c2ecf20Sopenharmony_ci}; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic struct platform_driver tegra186_dspk_driver = { 5428c2ecf20Sopenharmony_ci .driver = { 5438c2ecf20Sopenharmony_ci .name = "tegra186-dspk", 5448c2ecf20Sopenharmony_ci .of_match_table = tegra186_dspk_of_match, 5458c2ecf20Sopenharmony_ci .pm = &tegra186_dspk_pm_ops, 5468c2ecf20Sopenharmony_ci }, 5478c2ecf20Sopenharmony_ci .probe = tegra186_dspk_platform_probe, 5488c2ecf20Sopenharmony_ci .remove = tegra186_dspk_platform_remove, 5498c2ecf20Sopenharmony_ci}; 5508c2ecf20Sopenharmony_cimodule_platform_driver(tegra186_dspk_driver); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>"); 5538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sameer Pujar <spujar@nvidia.com>"); 5548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra186 ASoC DSPK driver"); 5558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 556