linux/sound/soc/tegra/tegra20_i2s.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * tegra20_i2s.c - Tegra20 I2S driver
   4 *
   5 * Author: Stephen Warren <swarren@nvidia.com>
   6 * Copyright (C) 2010,2012 - NVIDIA, Inc.
   7 *
   8 * Based on code copyright/by:
   9 *
  10 * Copyright (c) 2009-2010, NVIDIA Corporation.
  11 * Scott Peterson <speterson@nvidia.com>
  12 *
  13 * Copyright (C) 2010 Google, Inc.
  14 * Iliyan Malchev <malchev@google.com>
  15 */
  16
  17#include <linux/clk.h>
  18#include <linux/device.h>
  19#include <linux/io.h>
  20#include <linux/module.h>
  21#include <linux/of.h>
  22#include <linux/platform_device.h>
  23#include <linux/pm_runtime.h>
  24#include <linux/regmap.h>
  25#include <linux/reset.h>
  26#include <linux/slab.h>
  27#include <sound/core.h>
  28#include <sound/pcm.h>
  29#include <sound/pcm_params.h>
  30#include <sound/soc.h>
  31#include <sound/dmaengine_pcm.h>
  32
  33#include "tegra20_i2s.h"
  34
  35#define DRV_NAME "tegra20-i2s"
  36
  37static __maybe_unused int tegra20_i2s_runtime_suspend(struct device *dev)
  38{
  39        struct tegra20_i2s *i2s = dev_get_drvdata(dev);
  40
  41        regcache_cache_only(i2s->regmap, true);
  42
  43        clk_disable_unprepare(i2s->clk_i2s);
  44
  45        return 0;
  46}
  47
  48static __maybe_unused int tegra20_i2s_runtime_resume(struct device *dev)
  49{
  50        struct tegra20_i2s *i2s = dev_get_drvdata(dev);
  51        int ret;
  52
  53        ret = reset_control_assert(i2s->reset);
  54        if (ret)
  55                return ret;
  56
  57        ret = clk_prepare_enable(i2s->clk_i2s);
  58        if (ret) {
  59                dev_err(dev, "clk_enable failed: %d\n", ret);
  60                return ret;
  61        }
  62
  63        usleep_range(10, 100);
  64
  65        ret = reset_control_deassert(i2s->reset);
  66        if (ret)
  67                goto disable_clocks;
  68
  69        regcache_cache_only(i2s->regmap, false);
  70        regcache_mark_dirty(i2s->regmap);
  71
  72        ret = regcache_sync(i2s->regmap);
  73        if (ret)
  74                goto disable_clocks;
  75
  76        return 0;
  77
  78disable_clocks:
  79        clk_disable_unprepare(i2s->clk_i2s);
  80
  81        return ret;
  82}
  83
  84static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai,
  85                                unsigned int fmt)
  86{
  87        struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
  88        unsigned int mask = 0, val = 0;
  89
  90        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
  91        case SND_SOC_DAIFMT_NB_NF:
  92                break;
  93        default:
  94                return -EINVAL;
  95        }
  96
  97        mask |= TEGRA20_I2S_CTRL_MASTER_ENABLE;
  98        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
  99        case SND_SOC_DAIFMT_CBS_CFS:
 100                val |= TEGRA20_I2S_CTRL_MASTER_ENABLE;
 101                break;
 102        case SND_SOC_DAIFMT_CBM_CFM:
 103                break;
 104        default:
 105                return -EINVAL;
 106        }
 107
 108        mask |= TEGRA20_I2S_CTRL_BIT_FORMAT_MASK |
 109                TEGRA20_I2S_CTRL_LRCK_MASK;
 110        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 111        case SND_SOC_DAIFMT_DSP_A:
 112                val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
 113                val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
 114                break;
 115        case SND_SOC_DAIFMT_DSP_B:
 116                val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
 117                val |= TEGRA20_I2S_CTRL_LRCK_R_LOW;
 118                break;
 119        case SND_SOC_DAIFMT_I2S:
 120                val |= TEGRA20_I2S_CTRL_BIT_FORMAT_I2S;
 121                val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
 122                break;
 123        case SND_SOC_DAIFMT_RIGHT_J:
 124                val |= TEGRA20_I2S_CTRL_BIT_FORMAT_RJM;
 125                val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
 126                break;
 127        case SND_SOC_DAIFMT_LEFT_J:
 128                val |= TEGRA20_I2S_CTRL_BIT_FORMAT_LJM;
 129                val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
 130                break;
 131        default:
 132                return -EINVAL;
 133        }
 134
 135        regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, mask, val);
 136
 137        return 0;
 138}
 139
 140static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream,
 141                                 struct snd_pcm_hw_params *params,
 142                                 struct snd_soc_dai *dai)
 143{
 144        struct device *dev = dai->dev;
 145        struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 146        unsigned int mask, val;
 147        int ret, sample_size, srate, i2sclock, bitcnt;
 148
 149        mask = TEGRA20_I2S_CTRL_BIT_SIZE_MASK;
 150        switch (params_format(params)) {
 151        case SNDRV_PCM_FORMAT_S16_LE:
 152                val = TEGRA20_I2S_CTRL_BIT_SIZE_16;
 153                sample_size = 16;
 154                break;
 155        case SNDRV_PCM_FORMAT_S24_LE:
 156                val = TEGRA20_I2S_CTRL_BIT_SIZE_24;
 157                sample_size = 24;
 158                break;
 159        case SNDRV_PCM_FORMAT_S32_LE:
 160                val = TEGRA20_I2S_CTRL_BIT_SIZE_32;
 161                sample_size = 32;
 162                break;
 163        default:
 164                return -EINVAL;
 165        }
 166
 167        mask |= TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK;
 168        val |= TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED;
 169
 170        regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, mask, val);
 171
 172        srate = params_rate(params);
 173
 174        /* Final "* 2" required by Tegra hardware */
 175        i2sclock = srate * params_channels(params) * sample_size * 2;
 176
 177        ret = clk_set_rate(i2s->clk_i2s, i2sclock);
 178        if (ret) {
 179                dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
 180                return ret;
 181        }
 182
 183        bitcnt = (i2sclock / (2 * srate)) - 1;
 184        if (bitcnt < 0 || bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
 185                return -EINVAL;
 186        val = bitcnt << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
 187
 188        if (i2sclock % (2 * srate))
 189                val |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE;
 190
 191        regmap_write(i2s->regmap, TEGRA20_I2S_TIMING, val);
 192
 193        regmap_write(i2s->regmap, TEGRA20_I2S_FIFO_SCR,
 194                     TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
 195                     TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
 196
 197        return 0;
 198}
 199
 200static void tegra20_i2s_start_playback(struct tegra20_i2s *i2s)
 201{
 202        regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
 203                           TEGRA20_I2S_CTRL_FIFO1_ENABLE,
 204                           TEGRA20_I2S_CTRL_FIFO1_ENABLE);
 205}
 206
 207static void tegra20_i2s_stop_playback(struct tegra20_i2s *i2s)
 208{
 209        regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
 210                           TEGRA20_I2S_CTRL_FIFO1_ENABLE, 0);
 211}
 212
 213static void tegra20_i2s_start_capture(struct tegra20_i2s *i2s)
 214{
 215        regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
 216                           TEGRA20_I2S_CTRL_FIFO2_ENABLE,
 217                           TEGRA20_I2S_CTRL_FIFO2_ENABLE);
 218}
 219
 220static void tegra20_i2s_stop_capture(struct tegra20_i2s *i2s)
 221{
 222        regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
 223                           TEGRA20_I2S_CTRL_FIFO2_ENABLE, 0);
 224}
 225
 226static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 227                               struct snd_soc_dai *dai)
 228{
 229        struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 230
 231        switch (cmd) {
 232        case SNDRV_PCM_TRIGGER_START:
 233        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 234        case SNDRV_PCM_TRIGGER_RESUME:
 235                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 236                        tegra20_i2s_start_playback(i2s);
 237                else
 238                        tegra20_i2s_start_capture(i2s);
 239                break;
 240        case SNDRV_PCM_TRIGGER_STOP:
 241        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 242        case SNDRV_PCM_TRIGGER_SUSPEND:
 243                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 244                        tegra20_i2s_stop_playback(i2s);
 245                else
 246                        tegra20_i2s_stop_capture(i2s);
 247                break;
 248        default:
 249                return -EINVAL;
 250        }
 251
 252        return 0;
 253}
 254
 255static int tegra20_i2s_probe(struct snd_soc_dai *dai)
 256{
 257        struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 258
 259        dai->capture_dma_data = &i2s->capture_dma_data;
 260        dai->playback_dma_data = &i2s->playback_dma_data;
 261
 262        return 0;
 263}
 264
 265static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = {
 266        .set_fmt        = tegra20_i2s_set_fmt,
 267        .hw_params      = tegra20_i2s_hw_params,
 268        .trigger        = tegra20_i2s_trigger,
 269};
 270
 271static const struct snd_soc_dai_driver tegra20_i2s_dai_template = {
 272        .probe = tegra20_i2s_probe,
 273        .playback = {
 274                .stream_name = "Playback",
 275                .channels_min = 2,
 276                .channels_max = 2,
 277                .rates = SNDRV_PCM_RATE_8000_96000,
 278                .formats = SNDRV_PCM_FMTBIT_S16_LE,
 279        },
 280        .capture = {
 281                .stream_name = "Capture",
 282                .channels_min = 2,
 283                .channels_max = 2,
 284                .rates = SNDRV_PCM_RATE_8000_96000,
 285                .formats = SNDRV_PCM_FMTBIT_S16_LE,
 286        },
 287        .ops = &tegra20_i2s_dai_ops,
 288        .symmetric_rate = 1,
 289};
 290
 291static const struct snd_soc_component_driver tegra20_i2s_component = {
 292        .name           = DRV_NAME,
 293};
 294
 295static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
 296{
 297        switch (reg) {
 298        case TEGRA20_I2S_CTRL:
 299        case TEGRA20_I2S_STATUS:
 300        case TEGRA20_I2S_TIMING:
 301        case TEGRA20_I2S_FIFO_SCR:
 302        case TEGRA20_I2S_PCM_CTRL:
 303        case TEGRA20_I2S_NW_CTRL:
 304        case TEGRA20_I2S_TDM_CTRL:
 305        case TEGRA20_I2S_TDM_TX_RX_CTRL:
 306        case TEGRA20_I2S_FIFO1:
 307        case TEGRA20_I2S_FIFO2:
 308                return true;
 309        default:
 310                return false;
 311        }
 312}
 313
 314static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg)
 315{
 316        switch (reg) {
 317        case TEGRA20_I2S_STATUS:
 318        case TEGRA20_I2S_FIFO_SCR:
 319        case TEGRA20_I2S_FIFO1:
 320        case TEGRA20_I2S_FIFO2:
 321                return true;
 322        default:
 323                return false;
 324        }
 325}
 326
 327static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg)
 328{
 329        switch (reg) {
 330        case TEGRA20_I2S_FIFO1:
 331        case TEGRA20_I2S_FIFO2:
 332                return true;
 333        default:
 334                return false;
 335        }
 336}
 337
 338static const struct regmap_config tegra20_i2s_regmap_config = {
 339        .reg_bits = 32,
 340        .reg_stride = 4,
 341        .val_bits = 32,
 342        .max_register = TEGRA20_I2S_FIFO2,
 343        .writeable_reg = tegra20_i2s_wr_rd_reg,
 344        .readable_reg = tegra20_i2s_wr_rd_reg,
 345        .volatile_reg = tegra20_i2s_volatile_reg,
 346        .precious_reg = tegra20_i2s_precious_reg,
 347        .cache_type = REGCACHE_FLAT,
 348};
 349
 350static int tegra20_i2s_platform_probe(struct platform_device *pdev)
 351{
 352        struct tegra20_i2s *i2s;
 353        struct resource *mem;
 354        void __iomem *regs;
 355        int ret;
 356
 357        i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL);
 358        if (!i2s) {
 359                ret = -ENOMEM;
 360                goto err;
 361        }
 362        dev_set_drvdata(&pdev->dev, i2s);
 363
 364        i2s->dai = tegra20_i2s_dai_template;
 365        i2s->dai.name = dev_name(&pdev->dev);
 366
 367        i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, "i2s");
 368        if (IS_ERR(i2s->reset)) {
 369                dev_err(&pdev->dev, "Can't retrieve i2s reset\n");
 370                return PTR_ERR(i2s->reset);
 371        }
 372
 373        i2s->clk_i2s = devm_clk_get(&pdev->dev, NULL);
 374        if (IS_ERR(i2s->clk_i2s)) {
 375                dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
 376                ret = PTR_ERR(i2s->clk_i2s);
 377                goto err;
 378        }
 379
 380        regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
 381        if (IS_ERR(regs)) {
 382                ret = PTR_ERR(regs);
 383                goto err;
 384        }
 385
 386        i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
 387                                            &tegra20_i2s_regmap_config);
 388        if (IS_ERR(i2s->regmap)) {
 389                dev_err(&pdev->dev, "regmap init failed\n");
 390                ret = PTR_ERR(i2s->regmap);
 391                goto err;
 392        }
 393
 394        i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2;
 395        i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 396        i2s->capture_dma_data.maxburst = 4;
 397
 398        i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1;
 399        i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 400        i2s->playback_dma_data.maxburst = 4;
 401
 402        pm_runtime_enable(&pdev->dev);
 403
 404        ret = snd_soc_register_component(&pdev->dev, &tegra20_i2s_component,
 405                                         &i2s->dai, 1);
 406        if (ret) {
 407                dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
 408                ret = -ENOMEM;
 409                goto err_pm_disable;
 410        }
 411
 412        ret = tegra_pcm_platform_register(&pdev->dev);
 413        if (ret) {
 414                dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
 415                goto err_unregister_component;
 416        }
 417
 418        return 0;
 419
 420err_unregister_component:
 421        snd_soc_unregister_component(&pdev->dev);
 422err_pm_disable:
 423        pm_runtime_disable(&pdev->dev);
 424err:
 425        return ret;
 426}
 427
 428static int tegra20_i2s_platform_remove(struct platform_device *pdev)
 429{
 430        tegra_pcm_platform_unregister(&pdev->dev);
 431        snd_soc_unregister_component(&pdev->dev);
 432        pm_runtime_disable(&pdev->dev);
 433
 434        return 0;
 435}
 436
 437static const struct of_device_id tegra20_i2s_of_match[] = {
 438        { .compatible = "nvidia,tegra20-i2s", },
 439        {},
 440};
 441
 442static const struct dev_pm_ops tegra20_i2s_pm_ops = {
 443        SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend,
 444                           tegra20_i2s_runtime_resume, NULL)
 445        SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 446                                pm_runtime_force_resume)
 447};
 448
 449static struct platform_driver tegra20_i2s_driver = {
 450        .driver = {
 451                .name = DRV_NAME,
 452                .of_match_table = tegra20_i2s_of_match,
 453                .pm = &tegra20_i2s_pm_ops,
 454        },
 455        .probe = tegra20_i2s_platform_probe,
 456        .remove = tegra20_i2s_platform_remove,
 457};
 458module_platform_driver(tegra20_i2s_driver);
 459
 460MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 461MODULE_DESCRIPTION("Tegra20 I2S ASoC driver");
 462MODULE_LICENSE("GPL");
 463MODULE_ALIAS("platform:" DRV_NAME);
 464MODULE_DEVICE_TABLE(of, tegra20_i2s_of_match);
 465