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