18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tegra30_i2s.c - Tegra30 I2S driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Stephen Warren <swarren@nvidia.com> 68c2ecf20Sopenharmony_ci * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on code copyright/by: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (c) 2009-2010, NVIDIA Corporation. 118c2ecf20Sopenharmony_ci * Scott Peterson <speterson@nvidia.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Copyright (C) 2010 Google, Inc. 148c2ecf20Sopenharmony_ci * Iliyan Malchev <malchev@google.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/clk.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <linux/of_device.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 258c2ecf20Sopenharmony_ci#include <linux/regmap.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <sound/core.h> 288c2ecf20Sopenharmony_ci#include <sound/pcm.h> 298c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 308c2ecf20Sopenharmony_ci#include <sound/soc.h> 318c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "tegra30_ahub.h" 348c2ecf20Sopenharmony_ci#include "tegra30_i2s.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define DRV_NAME "tegra30-i2s" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int tegra30_i2s_runtime_suspend(struct device *dev) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = dev_get_drvdata(dev); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci regcache_cache_only(i2s->regmap, true); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci clk_disable_unprepare(i2s->clk_i2s); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int tegra30_i2s_runtime_resume(struct device *dev) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = dev_get_drvdata(dev); 528c2ecf20Sopenharmony_ci int ret; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci ret = clk_prepare_enable(i2s->clk_i2s); 558c2ecf20Sopenharmony_ci if (ret) { 568c2ecf20Sopenharmony_ci dev_err(dev, "clk_enable failed: %d\n", ret); 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci regcache_cache_only(i2s->regmap, false); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int tegra30_i2s_set_fmt(struct snd_soc_dai *dai, 668c2ecf20Sopenharmony_ci unsigned int fmt) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); 698c2ecf20Sopenharmony_ci unsigned int mask = 0, val = 0; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 728c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci default: 758c2ecf20Sopenharmony_ci return -EINVAL; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci mask |= TEGRA30_I2S_CTRL_MASTER_ENABLE; 798c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 808c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 818c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_MASTER_ENABLE; 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci default: 868c2ecf20Sopenharmony_ci return -EINVAL; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci mask |= TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK | 908c2ecf20Sopenharmony_ci TEGRA30_I2S_CTRL_LRCK_MASK; 918c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 928c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 938c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC; 948c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_LRCK_L_LOW; 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 978c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC; 988c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_LRCK_R_LOW; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 1018c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK; 1028c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_LRCK_L_LOW; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 1058c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK; 1068c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_LRCK_L_LOW; 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 1098c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK; 1108c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_CTRL_LRCK_L_LOW; 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci default: 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci pm_runtime_get_sync(dai->dev); 1178c2ecf20Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, mask, val); 1188c2ecf20Sopenharmony_ci pm_runtime_put(dai->dev); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, 1248c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1258c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct device *dev = dai->dev; 1288c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); 1298c2ecf20Sopenharmony_ci unsigned int mask, val, reg; 1308c2ecf20Sopenharmony_ci int ret, sample_size, srate, i2sclock, bitcnt; 1318c2ecf20Sopenharmony_ci struct tegra30_ahub_cif_conf cif_conf; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (params_channels(params) != 2) 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci mask = TEGRA30_I2S_CTRL_BIT_SIZE_MASK; 1378c2ecf20Sopenharmony_ci switch (params_format(params)) { 1388c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 1398c2ecf20Sopenharmony_ci val = TEGRA30_I2S_CTRL_BIT_SIZE_16; 1408c2ecf20Sopenharmony_ci sample_size = 16; 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci default: 1438c2ecf20Sopenharmony_ci return -EINVAL; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, mask, val); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci srate = params_rate(params); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Final "* 2" required by Tegra hardware */ 1518c2ecf20Sopenharmony_ci i2sclock = srate * params_channels(params) * sample_size * 2; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci bitcnt = (i2sclock / (2 * srate)) - 1; 1548c2ecf20Sopenharmony_ci if (bitcnt < 0 || bitcnt > TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) 1558c2ecf20Sopenharmony_ci return -EINVAL; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci ret = clk_set_rate(i2s->clk_i2s, i2sclock); 1588c2ecf20Sopenharmony_ci if (ret) { 1598c2ecf20Sopenharmony_ci dev_err(dev, "Can't set I2S clock rate: %d\n", ret); 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (i2sclock % (2 * srate)) 1668c2ecf20Sopenharmony_ci val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci regmap_write(i2s->regmap, TEGRA30_I2S_TIMING, val); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci cif_conf.threshold = 0; 1718c2ecf20Sopenharmony_ci cif_conf.audio_channels = 2; 1728c2ecf20Sopenharmony_ci cif_conf.client_channels = 2; 1738c2ecf20Sopenharmony_ci cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; 1748c2ecf20Sopenharmony_ci cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; 1758c2ecf20Sopenharmony_ci cif_conf.expand = 0; 1768c2ecf20Sopenharmony_ci cif_conf.stereo_conv = 0; 1778c2ecf20Sopenharmony_ci cif_conf.replicate = 0; 1788c2ecf20Sopenharmony_ci cif_conf.truncate = 0; 1798c2ecf20Sopenharmony_ci cif_conf.mono_conv = 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 1828c2ecf20Sopenharmony_ci cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX; 1838c2ecf20Sopenharmony_ci reg = TEGRA30_I2S_CIF_RX_CTRL; 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX; 1868c2ecf20Sopenharmony_ci reg = TEGRA30_I2S_CIF_TX_CTRL; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci i2s->soc_data->set_audio_cif(i2s->regmap, reg, &cif_conf); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) | 1928c2ecf20Sopenharmony_ci (1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT); 1938c2ecf20Sopenharmony_ci regmap_write(i2s->regmap, TEGRA30_I2S_OFFSET, val); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void tegra30_i2s_start_playback(struct tegra30_i2s *i2s) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci tegra30_ahub_enable_tx_fifo(i2s->playback_fifo_cif); 2018c2ecf20Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, 2028c2ecf20Sopenharmony_ci TEGRA30_I2S_CTRL_XFER_EN_TX, 2038c2ecf20Sopenharmony_ci TEGRA30_I2S_CTRL_XFER_EN_TX); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void tegra30_i2s_stop_playback(struct tegra30_i2s *i2s) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci tegra30_ahub_disable_tx_fifo(i2s->playback_fifo_cif); 2098c2ecf20Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, 2108c2ecf20Sopenharmony_ci TEGRA30_I2S_CTRL_XFER_EN_TX, 0); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void tegra30_i2s_start_capture(struct tegra30_i2s *i2s) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci tegra30_ahub_enable_rx_fifo(i2s->capture_fifo_cif); 2168c2ecf20Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, 2178c2ecf20Sopenharmony_ci TEGRA30_I2S_CTRL_XFER_EN_RX, 2188c2ecf20Sopenharmony_ci TEGRA30_I2S_CTRL_XFER_EN_RX); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, 2248c2ecf20Sopenharmony_ci TEGRA30_I2S_CTRL_XFER_EN_RX, 0); 2258c2ecf20Sopenharmony_ci tegra30_ahub_disable_rx_fifo(i2s->capture_fifo_cif); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 2298c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci switch (cmd) { 2348c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2358c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2368c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 2378c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2388c2ecf20Sopenharmony_ci tegra30_i2s_start_playback(i2s); 2398c2ecf20Sopenharmony_ci else 2408c2ecf20Sopenharmony_ci tegra30_i2s_start_capture(i2s); 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2438c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2448c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 2458c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2468c2ecf20Sopenharmony_ci tegra30_i2s_stop_playback(i2s); 2478c2ecf20Sopenharmony_ci else 2488c2ecf20Sopenharmony_ci tegra30_i2s_stop_capture(i2s); 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci default: 2518c2ecf20Sopenharmony_ci return -EINVAL; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int tegra30_i2s_set_tdm(struct snd_soc_dai *dai, 2588c2ecf20Sopenharmony_ci unsigned int tx_mask, unsigned int rx_mask, 2598c2ecf20Sopenharmony_ci int slots, int slot_width) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); 2628c2ecf20Sopenharmony_ci unsigned int mask, val; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: txmask=0x%08x rxmask=0x%08x slots=%d width=%d\n", 2658c2ecf20Sopenharmony_ci __func__, tx_mask, rx_mask, slots, slot_width); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci mask = TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK | 2688c2ecf20Sopenharmony_ci TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK | 2698c2ecf20Sopenharmony_ci TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci val = (tx_mask << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT) | 2728c2ecf20Sopenharmony_ci (rx_mask << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT) | 2738c2ecf20Sopenharmony_ci ((slots - 1) << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci pm_runtime_get_sync(dai->dev); 2768c2ecf20Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA30_I2S_SLOT_CTRL, mask, val); 2778c2ecf20Sopenharmony_ci /* set the fsync width to minimum of 1 clock width */ 2788c2ecf20Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA30_I2S_CH_CTRL, 2798c2ecf20Sopenharmony_ci TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK, 0x0); 2808c2ecf20Sopenharmony_ci pm_runtime_put(dai->dev); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int tegra30_i2s_probe(struct snd_soc_dai *dai) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci dai->capture_dma_data = &i2s->capture_dma_data; 2908c2ecf20Sopenharmony_ci dai->playback_dma_data = &i2s->playback_dma_data; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops tegra30_i2s_dai_ops = { 2968c2ecf20Sopenharmony_ci .set_fmt = tegra30_i2s_set_fmt, 2978c2ecf20Sopenharmony_ci .hw_params = tegra30_i2s_hw_params, 2988c2ecf20Sopenharmony_ci .trigger = tegra30_i2s_trigger, 2998c2ecf20Sopenharmony_ci .set_tdm_slot = tegra30_i2s_set_tdm, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_driver tegra30_i2s_dai_template = { 3038c2ecf20Sopenharmony_ci .probe = tegra30_i2s_probe, 3048c2ecf20Sopenharmony_ci .playback = { 3058c2ecf20Sopenharmony_ci .stream_name = "Playback", 3068c2ecf20Sopenharmony_ci .channels_min = 2, 3078c2ecf20Sopenharmony_ci .channels_max = 2, 3088c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 3098c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 3108c2ecf20Sopenharmony_ci }, 3118c2ecf20Sopenharmony_ci .capture = { 3128c2ecf20Sopenharmony_ci .stream_name = "Capture", 3138c2ecf20Sopenharmony_ci .channels_min = 2, 3148c2ecf20Sopenharmony_ci .channels_max = 2, 3158c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 3168c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 3178c2ecf20Sopenharmony_ci }, 3188c2ecf20Sopenharmony_ci .ops = &tegra30_i2s_dai_ops, 3198c2ecf20Sopenharmony_ci .symmetric_rates = 1, 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver tegra30_i2s_component = { 3238c2ecf20Sopenharmony_ci .name = DRV_NAME, 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci switch (reg) { 3298c2ecf20Sopenharmony_ci case TEGRA30_I2S_CTRL: 3308c2ecf20Sopenharmony_ci case TEGRA30_I2S_TIMING: 3318c2ecf20Sopenharmony_ci case TEGRA30_I2S_OFFSET: 3328c2ecf20Sopenharmony_ci case TEGRA30_I2S_CH_CTRL: 3338c2ecf20Sopenharmony_ci case TEGRA30_I2S_SLOT_CTRL: 3348c2ecf20Sopenharmony_ci case TEGRA30_I2S_CIF_RX_CTRL: 3358c2ecf20Sopenharmony_ci case TEGRA30_I2S_CIF_TX_CTRL: 3368c2ecf20Sopenharmony_ci case TEGRA30_I2S_FLOWCTL: 3378c2ecf20Sopenharmony_ci case TEGRA30_I2S_TX_STEP: 3388c2ecf20Sopenharmony_ci case TEGRA30_I2S_FLOW_STATUS: 3398c2ecf20Sopenharmony_ci case TEGRA30_I2S_FLOW_TOTAL: 3408c2ecf20Sopenharmony_ci case TEGRA30_I2S_FLOW_OVER: 3418c2ecf20Sopenharmony_ci case TEGRA30_I2S_FLOW_UNDER: 3428c2ecf20Sopenharmony_ci case TEGRA30_I2S_LCOEF_1_4_0: 3438c2ecf20Sopenharmony_ci case TEGRA30_I2S_LCOEF_1_4_1: 3448c2ecf20Sopenharmony_ci case TEGRA30_I2S_LCOEF_1_4_2: 3458c2ecf20Sopenharmony_ci case TEGRA30_I2S_LCOEF_1_4_3: 3468c2ecf20Sopenharmony_ci case TEGRA30_I2S_LCOEF_1_4_4: 3478c2ecf20Sopenharmony_ci case TEGRA30_I2S_LCOEF_1_4_5: 3488c2ecf20Sopenharmony_ci case TEGRA30_I2S_LCOEF_2_4_0: 3498c2ecf20Sopenharmony_ci case TEGRA30_I2S_LCOEF_2_4_1: 3508c2ecf20Sopenharmony_ci case TEGRA30_I2S_LCOEF_2_4_2: 3518c2ecf20Sopenharmony_ci return true; 3528c2ecf20Sopenharmony_ci default: 3538c2ecf20Sopenharmony_ci return false; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci switch (reg) { 3608c2ecf20Sopenharmony_ci case TEGRA30_I2S_FLOW_STATUS: 3618c2ecf20Sopenharmony_ci case TEGRA30_I2S_FLOW_TOTAL: 3628c2ecf20Sopenharmony_ci case TEGRA30_I2S_FLOW_OVER: 3638c2ecf20Sopenharmony_ci case TEGRA30_I2S_FLOW_UNDER: 3648c2ecf20Sopenharmony_ci return true; 3658c2ecf20Sopenharmony_ci default: 3668c2ecf20Sopenharmony_ci return false; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic const struct regmap_config tegra30_i2s_regmap_config = { 3718c2ecf20Sopenharmony_ci .reg_bits = 32, 3728c2ecf20Sopenharmony_ci .reg_stride = 4, 3738c2ecf20Sopenharmony_ci .val_bits = 32, 3748c2ecf20Sopenharmony_ci .max_register = TEGRA30_I2S_LCOEF_2_4_2, 3758c2ecf20Sopenharmony_ci .writeable_reg = tegra30_i2s_wr_rd_reg, 3768c2ecf20Sopenharmony_ci .readable_reg = tegra30_i2s_wr_rd_reg, 3778c2ecf20Sopenharmony_ci .volatile_reg = tegra30_i2s_volatile_reg, 3788c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 3798c2ecf20Sopenharmony_ci}; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic const struct tegra30_i2s_soc_data tegra30_i2s_config = { 3828c2ecf20Sopenharmony_ci .set_audio_cif = tegra30_ahub_set_cif, 3838c2ecf20Sopenharmony_ci}; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic const struct tegra30_i2s_soc_data tegra124_i2s_config = { 3868c2ecf20Sopenharmony_ci .set_audio_cif = tegra124_ahub_set_cif, 3878c2ecf20Sopenharmony_ci}; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic const struct of_device_id tegra30_i2s_of_match[] = { 3908c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra124-i2s", .data = &tegra124_i2s_config }, 3918c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra30-i2s", .data = &tegra30_i2s_config }, 3928c2ecf20Sopenharmony_ci {}, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int tegra30_i2s_platform_probe(struct platform_device *pdev) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s; 3988c2ecf20Sopenharmony_ci const struct of_device_id *match; 3998c2ecf20Sopenharmony_ci u32 cif_ids[2]; 4008c2ecf20Sopenharmony_ci void __iomem *regs; 4018c2ecf20Sopenharmony_ci int ret; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_i2s), GFP_KERNEL); 4048c2ecf20Sopenharmony_ci if (!i2s) { 4058c2ecf20Sopenharmony_ci ret = -ENOMEM; 4068c2ecf20Sopenharmony_ci goto err; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, i2s); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci match = of_match_device(tegra30_i2s_of_match, &pdev->dev); 4118c2ecf20Sopenharmony_ci if (!match) { 4128c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error: No device match found\n"); 4138c2ecf20Sopenharmony_ci ret = -ENODEV; 4148c2ecf20Sopenharmony_ci goto err; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci i2s->soc_data = (struct tegra30_i2s_soc_data *)match->data; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci i2s->dai = tegra30_i2s_dai_template; 4198c2ecf20Sopenharmony_ci i2s->dai.name = dev_name(&pdev->dev); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(pdev->dev.of_node, 4228c2ecf20Sopenharmony_ci "nvidia,ahub-cif-ids", cif_ids, 4238c2ecf20Sopenharmony_ci ARRAY_SIZE(cif_ids)); 4248c2ecf20Sopenharmony_ci if (ret < 0) 4258c2ecf20Sopenharmony_ci goto err; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci i2s->playback_i2s_cif = cif_ids[0]; 4288c2ecf20Sopenharmony_ci i2s->capture_i2s_cif = cif_ids[1]; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci i2s->clk_i2s = clk_get(&pdev->dev, NULL); 4318c2ecf20Sopenharmony_ci if (IS_ERR(i2s->clk_i2s)) { 4328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); 4338c2ecf20Sopenharmony_ci ret = PTR_ERR(i2s->clk_i2s); 4348c2ecf20Sopenharmony_ci goto err; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci regs = devm_platform_ioremap_resource(pdev, 0); 4388c2ecf20Sopenharmony_ci if (IS_ERR(regs)) { 4398c2ecf20Sopenharmony_ci ret = PTR_ERR(regs); 4408c2ecf20Sopenharmony_ci goto err_clk_put; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, 4448c2ecf20Sopenharmony_ci &tegra30_i2s_regmap_config); 4458c2ecf20Sopenharmony_ci if (IS_ERR(i2s->regmap)) { 4468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "regmap init failed\n"); 4478c2ecf20Sopenharmony_ci ret = PTR_ERR(i2s->regmap); 4488c2ecf20Sopenharmony_ci goto err_clk_put; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci regcache_cache_only(i2s->regmap, true); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 4538c2ecf20Sopenharmony_ci if (!pm_runtime_enabled(&pdev->dev)) { 4548c2ecf20Sopenharmony_ci ret = tegra30_i2s_runtime_resume(&pdev->dev); 4558c2ecf20Sopenharmony_ci if (ret) 4568c2ecf20Sopenharmony_ci goto err_pm_disable; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 4608c2ecf20Sopenharmony_ci i2s->playback_dma_data.maxburst = 4; 4618c2ecf20Sopenharmony_ci ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif, 4628c2ecf20Sopenharmony_ci i2s->playback_dma_chan, 4638c2ecf20Sopenharmony_ci sizeof(i2s->playback_dma_chan), 4648c2ecf20Sopenharmony_ci &i2s->playback_dma_data.addr); 4658c2ecf20Sopenharmony_ci if (ret) { 4668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not alloc TX FIFO: %d\n", ret); 4678c2ecf20Sopenharmony_ci goto err_suspend; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci ret = tegra30_ahub_set_rx_cif_source(i2s->playback_i2s_cif, 4708c2ecf20Sopenharmony_ci i2s->playback_fifo_cif); 4718c2ecf20Sopenharmony_ci if (ret) { 4728c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not route TX FIFO: %d\n", ret); 4738c2ecf20Sopenharmony_ci goto err_free_tx_fifo; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 4778c2ecf20Sopenharmony_ci i2s->capture_dma_data.maxburst = 4; 4788c2ecf20Sopenharmony_ci ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif, 4798c2ecf20Sopenharmony_ci i2s->capture_dma_chan, 4808c2ecf20Sopenharmony_ci sizeof(i2s->capture_dma_chan), 4818c2ecf20Sopenharmony_ci &i2s->capture_dma_data.addr); 4828c2ecf20Sopenharmony_ci if (ret) { 4838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not alloc RX FIFO: %d\n", ret); 4848c2ecf20Sopenharmony_ci goto err_unroute_tx_fifo; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci ret = tegra30_ahub_set_rx_cif_source(i2s->capture_fifo_cif, 4878c2ecf20Sopenharmony_ci i2s->capture_i2s_cif); 4888c2ecf20Sopenharmony_ci if (ret) { 4898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not route TX FIFO: %d\n", ret); 4908c2ecf20Sopenharmony_ci goto err_free_rx_fifo; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci ret = snd_soc_register_component(&pdev->dev, &tegra30_i2s_component, 4948c2ecf20Sopenharmony_ci &i2s->dai, 1); 4958c2ecf20Sopenharmony_ci if (ret) { 4968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); 4978c2ecf20Sopenharmony_ci ret = -ENOMEM; 4988c2ecf20Sopenharmony_ci goto err_unroute_rx_fifo; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci ret = tegra_pcm_platform_register_with_chan_names(&pdev->dev, 5028c2ecf20Sopenharmony_ci &i2s->dma_config, i2s->playback_dma_chan, 5038c2ecf20Sopenharmony_ci i2s->capture_dma_chan); 5048c2ecf20Sopenharmony_ci if (ret) { 5058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); 5068c2ecf20Sopenharmony_ci goto err_unregister_component; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cierr_unregister_component: 5128c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 5138c2ecf20Sopenharmony_cierr_unroute_rx_fifo: 5148c2ecf20Sopenharmony_ci tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif); 5158c2ecf20Sopenharmony_cierr_free_rx_fifo: 5168c2ecf20Sopenharmony_ci tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif); 5178c2ecf20Sopenharmony_cierr_unroute_tx_fifo: 5188c2ecf20Sopenharmony_ci tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif); 5198c2ecf20Sopenharmony_cierr_free_tx_fifo: 5208c2ecf20Sopenharmony_ci tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif); 5218c2ecf20Sopenharmony_cierr_suspend: 5228c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 5238c2ecf20Sopenharmony_ci tegra30_i2s_runtime_suspend(&pdev->dev); 5248c2ecf20Sopenharmony_cierr_pm_disable: 5258c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5268c2ecf20Sopenharmony_cierr_clk_put: 5278c2ecf20Sopenharmony_ci clk_put(i2s->clk_i2s); 5288c2ecf20Sopenharmony_cierr: 5298c2ecf20Sopenharmony_ci return ret; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic int tegra30_i2s_platform_remove(struct platform_device *pdev) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = dev_get_drvdata(&pdev->dev); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5378c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 5388c2ecf20Sopenharmony_ci tegra30_i2s_runtime_suspend(&pdev->dev); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci tegra_pcm_platform_unregister(&pdev->dev); 5418c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif); 5448c2ecf20Sopenharmony_ci tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif); 5478c2ecf20Sopenharmony_ci tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci clk_put(i2s->clk_i2s); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5558c2ecf20Sopenharmony_cistatic int tegra30_i2s_suspend(struct device *dev) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = dev_get_drvdata(dev); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci regcache_mark_dirty(i2s->regmap); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci return 0; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int tegra30_i2s_resume(struct device *dev) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct tegra30_i2s *i2s = dev_get_drvdata(dev); 5678c2ecf20Sopenharmony_ci int ret; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 5708c2ecf20Sopenharmony_ci if (ret < 0) { 5718c2ecf20Sopenharmony_ci pm_runtime_put(dev); 5728c2ecf20Sopenharmony_ci return ret; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci ret = regcache_sync(i2s->regmap); 5758c2ecf20Sopenharmony_ci pm_runtime_put(dev); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci return ret; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci#endif 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tegra30_i2s_pm_ops = { 5828c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend, 5838c2ecf20Sopenharmony_ci tegra30_i2s_runtime_resume, NULL) 5848c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(tegra30_i2s_suspend, tegra30_i2s_resume) 5858c2ecf20Sopenharmony_ci}; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic struct platform_driver tegra30_i2s_driver = { 5888c2ecf20Sopenharmony_ci .driver = { 5898c2ecf20Sopenharmony_ci .name = DRV_NAME, 5908c2ecf20Sopenharmony_ci .of_match_table = tegra30_i2s_of_match, 5918c2ecf20Sopenharmony_ci .pm = &tegra30_i2s_pm_ops, 5928c2ecf20Sopenharmony_ci }, 5938c2ecf20Sopenharmony_ci .probe = tegra30_i2s_platform_probe, 5948c2ecf20Sopenharmony_ci .remove = tegra30_i2s_platform_remove, 5958c2ecf20Sopenharmony_ci}; 5968c2ecf20Sopenharmony_cimodule_platform_driver(tegra30_i2s_driver); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 5998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra30 I2S ASoC driver"); 6008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6018c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 6028c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra30_i2s_of_match); 603