root/sound/soc/tegra/tegra20_i2s.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tegra20_i2s_runtime_suspend
  2. tegra20_i2s_runtime_resume
  3. tegra20_i2s_set_fmt
  4. tegra20_i2s_hw_params
  5. tegra20_i2s_start_playback
  6. tegra20_i2s_stop_playback
  7. tegra20_i2s_start_capture
  8. tegra20_i2s_stop_capture
  9. tegra20_i2s_trigger
  10. tegra20_i2s_probe
  11. tegra20_i2s_wr_rd_reg
  12. tegra20_i2s_volatile_reg
  13. tegra20_i2s_precious_reg
  14. tegra20_i2s_platform_probe
  15. tegra20_i2s_platform_remove

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

/* [<][>][^][v][top][bottom][index][help] */