linux/sound/soc/tegra/tegra20_spdif.c
<<
>>
Prefs
   1/*
   2 * tegra20_spdif.c - Tegra20 SPDIF driver
   3 *
   4 * Author: Stephen Warren <swarren@nvidia.com>
   5 * Copyright (C) 2011-2012 - NVIDIA, Inc.
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License
   9 * version 2 as published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful, but
  12 * WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  19 * 02110-1301 USA
  20 *
  21 */
  22
  23#include <linux/clk.h>
  24#include <linux/device.h>
  25#include <linux/io.h>
  26#include <linux/module.h>
  27#include <linux/platform_device.h>
  28#include <linux/pm_runtime.h>
  29#include <linux/regmap.h>
  30#include <linux/slab.h>
  31#include <sound/core.h>
  32#include <sound/pcm.h>
  33#include <sound/pcm_params.h>
  34#include <sound/soc.h>
  35
  36#include "tegra20_spdif.h"
  37
  38#define DRV_NAME "tegra20-spdif"
  39
  40static int tegra20_spdif_runtime_suspend(struct device *dev)
  41{
  42        struct tegra20_spdif *spdif = dev_get_drvdata(dev);
  43
  44        clk_disable_unprepare(spdif->clk_spdif_out);
  45
  46        return 0;
  47}
  48
  49static int tegra20_spdif_runtime_resume(struct device *dev)
  50{
  51        struct tegra20_spdif *spdif = dev_get_drvdata(dev);
  52        int ret;
  53
  54        ret = clk_prepare_enable(spdif->clk_spdif_out);
  55        if (ret) {
  56                dev_err(dev, "clk_enable failed: %d\n", ret);
  57                return ret;
  58        }
  59
  60        return 0;
  61}
  62
  63static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
  64                                struct snd_pcm_hw_params *params,
  65                                struct snd_soc_dai *dai)
  66{
  67        struct device *dev = dai->dev;
  68        struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
  69        unsigned int mask, val;
  70        int ret, spdifclock;
  71
  72        mask = TEGRA20_SPDIF_CTRL_PACK |
  73               TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
  74        switch (params_format(params)) {
  75        case SNDRV_PCM_FORMAT_S16_LE:
  76                val = TEGRA20_SPDIF_CTRL_PACK |
  77                      TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
  78                break;
  79        default:
  80                return -EINVAL;
  81        }
  82
  83        regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val);
  84
  85        switch (params_rate(params)) {
  86        case 32000:
  87                spdifclock = 4096000;
  88                break;
  89        case 44100:
  90                spdifclock = 5644800;
  91                break;
  92        case 48000:
  93                spdifclock = 6144000;
  94                break;
  95        case 88200:
  96                spdifclock = 11289600;
  97                break;
  98        case 96000:
  99                spdifclock = 12288000;
 100                break;
 101        case 176400:
 102                spdifclock = 22579200;
 103                break;
 104        case 192000:
 105                spdifclock = 24576000;
 106                break;
 107        default:
 108                return -EINVAL;
 109        }
 110
 111        ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
 112        if (ret) {
 113                dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
 114                return ret;
 115        }
 116
 117        return 0;
 118}
 119
 120static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
 121{
 122        regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
 123                           TEGRA20_SPDIF_CTRL_TX_EN,
 124                           TEGRA20_SPDIF_CTRL_TX_EN);
 125}
 126
 127static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
 128{
 129        regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
 130                           TEGRA20_SPDIF_CTRL_TX_EN, 0);
 131}
 132
 133static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
 134                                struct snd_soc_dai *dai)
 135{
 136        struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
 137
 138        switch (cmd) {
 139        case SNDRV_PCM_TRIGGER_START:
 140        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 141        case SNDRV_PCM_TRIGGER_RESUME:
 142                tegra20_spdif_start_playback(spdif);
 143                break;
 144        case SNDRV_PCM_TRIGGER_STOP:
 145        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 146        case SNDRV_PCM_TRIGGER_SUSPEND:
 147                tegra20_spdif_stop_playback(spdif);
 148                break;
 149        default:
 150                return -EINVAL;
 151        }
 152
 153        return 0;
 154}
 155
 156static int tegra20_spdif_probe(struct snd_soc_dai *dai)
 157{
 158        struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
 159
 160        dai->capture_dma_data = NULL;
 161        dai->playback_dma_data = &spdif->playback_dma_data;
 162
 163        return 0;
 164}
 165
 166static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
 167        .hw_params      = tegra20_spdif_hw_params,
 168        .trigger        = tegra20_spdif_trigger,
 169};
 170
 171static struct snd_soc_dai_driver tegra20_spdif_dai = {
 172        .name = DRV_NAME,
 173        .probe = tegra20_spdif_probe,
 174        .playback = {
 175                .stream_name = "Playback",
 176                .channels_min = 2,
 177                .channels_max = 2,
 178                .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
 179                                SNDRV_PCM_RATE_48000,
 180                .formats = SNDRV_PCM_FMTBIT_S16_LE,
 181        },
 182        .ops = &tegra20_spdif_dai_ops,
 183};
 184
 185static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
 186{
 187        switch (reg) {
 188        case TEGRA20_SPDIF_CTRL:
 189        case TEGRA20_SPDIF_STATUS:
 190        case TEGRA20_SPDIF_STROBE_CTRL:
 191        case TEGRA20_SPDIF_DATA_FIFO_CSR:
 192        case TEGRA20_SPDIF_DATA_OUT:
 193        case TEGRA20_SPDIF_DATA_IN:
 194        case TEGRA20_SPDIF_CH_STA_RX_A:
 195        case TEGRA20_SPDIF_CH_STA_RX_B:
 196        case TEGRA20_SPDIF_CH_STA_RX_C:
 197        case TEGRA20_SPDIF_CH_STA_RX_D:
 198        case TEGRA20_SPDIF_CH_STA_RX_E:
 199        case TEGRA20_SPDIF_CH_STA_RX_F:
 200        case TEGRA20_SPDIF_CH_STA_TX_A:
 201        case TEGRA20_SPDIF_CH_STA_TX_B:
 202        case TEGRA20_SPDIF_CH_STA_TX_C:
 203        case TEGRA20_SPDIF_CH_STA_TX_D:
 204        case TEGRA20_SPDIF_CH_STA_TX_E:
 205        case TEGRA20_SPDIF_CH_STA_TX_F:
 206        case TEGRA20_SPDIF_USR_STA_RX_A:
 207        case TEGRA20_SPDIF_USR_DAT_TX_A:
 208                return true;
 209        default:
 210                return false;
 211        };
 212}
 213
 214static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
 215{
 216        switch (reg) {
 217        case TEGRA20_SPDIF_STATUS:
 218        case TEGRA20_SPDIF_DATA_FIFO_CSR:
 219        case TEGRA20_SPDIF_DATA_OUT:
 220        case TEGRA20_SPDIF_DATA_IN:
 221        case TEGRA20_SPDIF_CH_STA_RX_A:
 222        case TEGRA20_SPDIF_CH_STA_RX_B:
 223        case TEGRA20_SPDIF_CH_STA_RX_C:
 224        case TEGRA20_SPDIF_CH_STA_RX_D:
 225        case TEGRA20_SPDIF_CH_STA_RX_E:
 226        case TEGRA20_SPDIF_CH_STA_RX_F:
 227        case TEGRA20_SPDIF_USR_STA_RX_A:
 228        case TEGRA20_SPDIF_USR_DAT_TX_A:
 229                return true;
 230        default:
 231                return false;
 232        };
 233}
 234
 235static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
 236{
 237        switch (reg) {
 238        case TEGRA20_SPDIF_DATA_OUT:
 239        case TEGRA20_SPDIF_DATA_IN:
 240        case TEGRA20_SPDIF_USR_STA_RX_A:
 241        case TEGRA20_SPDIF_USR_DAT_TX_A:
 242                return true;
 243        default:
 244                return false;
 245        };
 246}
 247
 248static const struct regmap_config tegra20_spdif_regmap_config = {
 249        .reg_bits = 32,
 250        .reg_stride = 4,
 251        .val_bits = 32,
 252        .max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
 253        .writeable_reg = tegra20_spdif_wr_rd_reg,
 254        .readable_reg = tegra20_spdif_wr_rd_reg,
 255        .volatile_reg = tegra20_spdif_volatile_reg,
 256        .precious_reg = tegra20_spdif_precious_reg,
 257        .cache_type = REGCACHE_RBTREE,
 258};
 259
 260static int tegra20_spdif_platform_probe(struct platform_device *pdev)
 261{
 262        struct tegra20_spdif *spdif;
 263        struct resource *mem, *memregion, *dmareq;
 264        void __iomem *regs;
 265        int ret;
 266
 267        spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
 268                             GFP_KERNEL);
 269        if (!spdif) {
 270                dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
 271                ret = -ENOMEM;
 272                goto err;
 273        }
 274        dev_set_drvdata(&pdev->dev, spdif);
 275
 276        spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
 277        if (IS_ERR(spdif->clk_spdif_out)) {
 278                pr_err("Can't retrieve spdif clock\n");
 279                ret = PTR_ERR(spdif->clk_spdif_out);
 280                goto err;
 281        }
 282
 283        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 284        if (!mem) {
 285                dev_err(&pdev->dev, "No memory resource\n");
 286                ret = -ENODEV;
 287                goto err_clk_put;
 288        }
 289
 290        dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 291        if (!dmareq) {
 292                dev_err(&pdev->dev, "No DMA resource\n");
 293                ret = -ENODEV;
 294                goto err_clk_put;
 295        }
 296
 297        memregion = devm_request_mem_region(&pdev->dev, mem->start,
 298                                            resource_size(mem), DRV_NAME);
 299        if (!memregion) {
 300                dev_err(&pdev->dev, "Memory region already claimed\n");
 301                ret = -EBUSY;
 302                goto err_clk_put;
 303        }
 304
 305        regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
 306        if (!regs) {
 307                dev_err(&pdev->dev, "ioremap failed\n");
 308                ret = -ENOMEM;
 309                goto err_clk_put;
 310        }
 311
 312        spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
 313                                            &tegra20_spdif_regmap_config);
 314        if (IS_ERR(spdif->regmap)) {
 315                dev_err(&pdev->dev, "regmap init failed\n");
 316                ret = PTR_ERR(spdif->regmap);
 317                goto err_clk_put;
 318        }
 319
 320        spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
 321        spdif->playback_dma_data.wrap = 4;
 322        spdif->playback_dma_data.width = 32;
 323        spdif->playback_dma_data.req_sel = dmareq->start;
 324
 325        pm_runtime_enable(&pdev->dev);
 326        if (!pm_runtime_enabled(&pdev->dev)) {
 327                ret = tegra20_spdif_runtime_resume(&pdev->dev);
 328                if (ret)
 329                        goto err_pm_disable;
 330        }
 331
 332        ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai);
 333        if (ret) {
 334                dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
 335                ret = -ENOMEM;
 336                goto err_suspend;
 337        }
 338
 339        ret = tegra_pcm_platform_register(&pdev->dev);
 340        if (ret) {
 341                dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
 342                goto err_unregister_dai;
 343        }
 344
 345        return 0;
 346
 347err_unregister_dai:
 348        snd_soc_unregister_dai(&pdev->dev);
 349err_suspend:
 350        if (!pm_runtime_status_suspended(&pdev->dev))
 351                tegra20_spdif_runtime_suspend(&pdev->dev);
 352err_pm_disable:
 353        pm_runtime_disable(&pdev->dev);
 354err_clk_put:
 355        clk_put(spdif->clk_spdif_out);
 356err:
 357        return ret;
 358}
 359
 360static int tegra20_spdif_platform_remove(struct platform_device *pdev)
 361{
 362        struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev);
 363
 364        pm_runtime_disable(&pdev->dev);
 365        if (!pm_runtime_status_suspended(&pdev->dev))
 366                tegra20_spdif_runtime_suspend(&pdev->dev);
 367
 368        tegra_pcm_platform_unregister(&pdev->dev);
 369        snd_soc_unregister_dai(&pdev->dev);
 370
 371        clk_put(spdif->clk_spdif_out);
 372
 373        return 0;
 374}
 375
 376static const struct dev_pm_ops tegra20_spdif_pm_ops = {
 377        SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
 378                           tegra20_spdif_runtime_resume, NULL)
 379};
 380
 381static struct platform_driver tegra20_spdif_driver = {
 382        .driver = {
 383                .name = DRV_NAME,
 384                .owner = THIS_MODULE,
 385                .pm = &tegra20_spdif_pm_ops,
 386        },
 387        .probe = tegra20_spdif_platform_probe,
 388        .remove = tegra20_spdif_platform_remove,
 389};
 390
 391module_platform_driver(tegra20_spdif_driver);
 392
 393MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 394MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
 395MODULE_LICENSE("GPL");
 396MODULE_ALIAS("platform:" DRV_NAME);
 397
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.