linux/sound/soc/samsung/spdif.c
<<
>>
Prefs
   1/* sound/soc/samsung/spdif.c
   2 *
   3 * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
   4 *
   5 * Copyright (c) 2010 Samsung Electronics Co. Ltd
   6 *              http://www.samsung.com/
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/clk.h>
  14#include <linux/io.h>
  15#include <linux/module.h>
  16
  17#include <sound/soc.h>
  18#include <sound/pcm_params.h>
  19
  20#include <linux/platform_data/asoc-s3c.h>
  21#include <mach/dma.h>
  22
  23#include "dma.h"
  24#include "spdif.h"
  25
  26/* Registers */
  27#define CLKCON                          0x00
  28#define CON                             0x04
  29#define BSTAS                           0x08
  30#define CSTAS                           0x0C
  31#define DATA_OUTBUF                     0x10
  32#define DCNT                            0x14
  33#define BSTAS_S                         0x18
  34#define DCNT_S                          0x1C
  35
  36#define CLKCTL_MASK                     0x7
  37#define CLKCTL_MCLK_EXT                 (0x1 << 2)
  38#define CLKCTL_PWR_ON                   (0x1 << 0)
  39
  40#define CON_MASK                        0x3ffffff
  41#define CON_FIFO_TH_SHIFT               19
  42#define CON_FIFO_TH_MASK                (0x7 << 19)
  43#define CON_USERDATA_23RDBIT            (0x1 << 12)
  44
  45#define CON_SW_RESET                    (0x1 << 5)
  46
  47#define CON_MCLKDIV_MASK                (0x3 << 3)
  48#define CON_MCLKDIV_256FS               (0x0 << 3)
  49#define CON_MCLKDIV_384FS               (0x1 << 3)
  50#define CON_MCLKDIV_512FS               (0x2 << 3)
  51
  52#define CON_PCM_MASK                    (0x3 << 1)
  53#define CON_PCM_16BIT                   (0x0 << 1)
  54#define CON_PCM_20BIT                   (0x1 << 1)
  55#define CON_PCM_24BIT                   (0x2 << 1)
  56
  57#define CON_PCM_DATA                    (0x1 << 0)
  58
  59#define CSTAS_MASK                      0x3fffffff
  60#define CSTAS_SAMP_FREQ_MASK            (0xF << 24)
  61#define CSTAS_SAMP_FREQ_44              (0x0 << 24)
  62#define CSTAS_SAMP_FREQ_48              (0x2 << 24)
  63#define CSTAS_SAMP_FREQ_32              (0x3 << 24)
  64#define CSTAS_SAMP_FREQ_96              (0xA << 24)
  65
  66#define CSTAS_CATEGORY_MASK             (0xFF << 8)
  67#define CSTAS_CATEGORY_CODE_CDP         (0x01 << 8)
  68
  69#define CSTAS_NO_COPYRIGHT              (0x1 << 2)
  70
  71/**
  72 * struct samsung_spdif_info - Samsung S/PDIF Controller information
  73 * @lock: Spin lock for S/PDIF.
  74 * @dev: The parent device passed to use from the probe.
  75 * @regs: The pointer to the device register block.
  76 * @clk_rate: Current clock rate for calcurate ratio.
  77 * @pclk: The peri-clock pointer for spdif master operation.
  78 * @sclk: The source clock pointer for making sync signals.
  79 * @save_clkcon: Backup clkcon reg. in suspend.
  80 * @save_con: Backup con reg. in suspend.
  81 * @save_cstas: Backup cstas reg. in suspend.
  82 * @dma_playback: DMA information for playback channel.
  83 */
  84struct samsung_spdif_info {
  85        spinlock_t      lock;
  86        struct device   *dev;
  87        void __iomem    *regs;
  88        unsigned long   clk_rate;
  89        struct clk      *pclk;
  90        struct clk      *sclk;
  91        u32             saved_clkcon;
  92        u32             saved_con;
  93        u32             saved_cstas;
  94        struct s3c_dma_params   *dma_playback;
  95};
  96
  97static struct s3c2410_dma_client spdif_dma_client_out = {
  98        .name           = "S/PDIF Stereo out",
  99};
 100
 101static struct s3c_dma_params spdif_stereo_out;
 102static struct samsung_spdif_info spdif_info;
 103
 104static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
 105{
 106        return snd_soc_dai_get_drvdata(cpu_dai);
 107}
 108
 109static void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on)
 110{
 111        void __iomem *regs = spdif->regs;
 112        u32 clkcon;
 113
 114        dev_dbg(spdif->dev, "Entered %s\n", __func__);
 115
 116        clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
 117        if (on)
 118                writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON);
 119        else
 120                writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
 121}
 122
 123static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai,
 124                                int clk_id, unsigned int freq, int dir)
 125{
 126        struct samsung_spdif_info *spdif = to_info(cpu_dai);
 127        u32 clkcon;
 128
 129        dev_dbg(spdif->dev, "Entered %s\n", __func__);
 130
 131        clkcon = readl(spdif->regs + CLKCON);
 132
 133        if (clk_id == SND_SOC_SPDIF_INT_MCLK)
 134                clkcon &= ~CLKCTL_MCLK_EXT;
 135        else
 136                clkcon |= CLKCTL_MCLK_EXT;
 137
 138        writel(clkcon, spdif->regs + CLKCON);
 139
 140        spdif->clk_rate = freq;
 141
 142        return 0;
 143}
 144
 145static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
 146                                struct snd_soc_dai *dai)
 147{
 148        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 149        struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
 150        unsigned long flags;
 151
 152        dev_dbg(spdif->dev, "Entered %s\n", __func__);
 153
 154        switch (cmd) {
 155        case SNDRV_PCM_TRIGGER_START:
 156        case SNDRV_PCM_TRIGGER_RESUME:
 157        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 158                spin_lock_irqsave(&spdif->lock, flags);
 159                spdif_snd_txctrl(spdif, 1);
 160                spin_unlock_irqrestore(&spdif->lock, flags);
 161                break;
 162        case SNDRV_PCM_TRIGGER_STOP:
 163        case SNDRV_PCM_TRIGGER_SUSPEND:
 164        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 165                spin_lock_irqsave(&spdif->lock, flags);
 166                spdif_snd_txctrl(spdif, 0);
 167                spin_unlock_irqrestore(&spdif->lock, flags);
 168                break;
 169        default:
 170                return -EINVAL;
 171        }
 172
 173        return 0;
 174}
 175
 176static int spdif_sysclk_ratios[] = {
 177        512, 384, 256,
 178};
 179
 180static int spdif_hw_params(struct snd_pcm_substream *substream,
 181                                struct snd_pcm_hw_params *params,
 182                                struct snd_soc_dai *socdai)
 183{
 184        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 185        struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
 186        void __iomem *regs = spdif->regs;
 187        struct s3c_dma_params *dma_data;
 188        u32 con, clkcon, cstas;
 189        unsigned long flags;
 190        int i, ratio;
 191
 192        dev_dbg(spdif->dev, "Entered %s\n", __func__);
 193
 194        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 195                dma_data = spdif->dma_playback;
 196        else {
 197                dev_err(spdif->dev, "Capture is not supported\n");
 198                return -EINVAL;
 199        }
 200
 201        snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
 202
 203        spin_lock_irqsave(&spdif->lock, flags);
 204
 205        con = readl(regs + CON) & CON_MASK;
 206        cstas = readl(regs + CSTAS) & CSTAS_MASK;
 207        clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
 208
 209        con &= ~CON_FIFO_TH_MASK;
 210        con |= (0x7 << CON_FIFO_TH_SHIFT);
 211        con |= CON_USERDATA_23RDBIT;
 212        con |= CON_PCM_DATA;
 213
 214        con &= ~CON_PCM_MASK;
 215        switch (params_format(params)) {
 216        case SNDRV_PCM_FORMAT_S16_LE:
 217                con |= CON_PCM_16BIT;
 218                break;
 219        default:
 220                dev_err(spdif->dev, "Unsupported data size.\n");
 221                goto err;
 222        }
 223
 224        ratio = spdif->clk_rate / params_rate(params);
 225        for (i = 0; i < ARRAY_SIZE(spdif_sysclk_ratios); i++)
 226                if (ratio == spdif_sysclk_ratios[i])
 227                        break;
 228        if (i == ARRAY_SIZE(spdif_sysclk_ratios)) {
 229                dev_err(spdif->dev, "Invalid clock ratio %ld/%d\n",
 230                                spdif->clk_rate, params_rate(params));
 231                goto err;
 232        }
 233
 234        con &= ~CON_MCLKDIV_MASK;
 235        switch (ratio) {
 236        case 256:
 237                con |= CON_MCLKDIV_256FS;
 238                break;
 239        case 384:
 240                con |= CON_MCLKDIV_384FS;
 241                break;
 242        case 512:
 243                con |= CON_MCLKDIV_512FS;
 244                break;
 245        }
 246
 247        cstas &= ~CSTAS_SAMP_FREQ_MASK;
 248        switch (params_rate(params)) {
 249        case 44100:
 250                cstas |= CSTAS_SAMP_FREQ_44;
 251                break;
 252        case 48000:
 253                cstas |= CSTAS_SAMP_FREQ_48;
 254                break;
 255        case 32000:
 256                cstas |= CSTAS_SAMP_FREQ_32;
 257                break;
 258        case 96000:
 259                cstas |= CSTAS_SAMP_FREQ_96;
 260                break;
 261        default:
 262                dev_err(spdif->dev, "Invalid sampling rate %d\n",
 263                                params_rate(params));
 264                goto err;
 265        }
 266
 267        cstas &= ~CSTAS_CATEGORY_MASK;
 268        cstas |= CSTAS_CATEGORY_CODE_CDP;
 269        cstas |= CSTAS_NO_COPYRIGHT;
 270
 271        writel(con, regs + CON);
 272        writel(cstas, regs + CSTAS);
 273        writel(clkcon, regs + CLKCON);
 274
 275        spin_unlock_irqrestore(&spdif->lock, flags);
 276
 277        return 0;
 278err:
 279        spin_unlock_irqrestore(&spdif->lock, flags);
 280        return -EINVAL;
 281}
 282
 283static void spdif_shutdown(struct snd_pcm_substream *substream,
 284                                struct snd_soc_dai *dai)
 285{
 286        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 287        struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
 288        void __iomem *regs = spdif->regs;
 289        u32 con, clkcon;
 290
 291        dev_dbg(spdif->dev, "Entered %s\n", __func__);
 292
 293        con = readl(regs + CON) & CON_MASK;
 294        clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
 295
 296        writel(con | CON_SW_RESET, regs + CON);
 297        cpu_relax();
 298
 299        writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
 300}
 301
 302#ifdef CONFIG_PM
 303static int spdif_suspend(struct snd_soc_dai *cpu_dai)
 304{
 305        struct samsung_spdif_info *spdif = to_info(cpu_dai);
 306        u32 con = spdif->saved_con;
 307
 308        dev_dbg(spdif->dev, "Entered %s\n", __func__);
 309
 310        spdif->saved_clkcon = readl(spdif->regs + CLKCON) & CLKCTL_MASK;
 311        spdif->saved_con = readl(spdif->regs + CON) & CON_MASK;
 312        spdif->saved_cstas = readl(spdif->regs + CSTAS) & CSTAS_MASK;
 313
 314        writel(con | CON_SW_RESET, spdif->regs + CON);
 315        cpu_relax();
 316
 317        return 0;
 318}
 319
 320static int spdif_resume(struct snd_soc_dai *cpu_dai)
 321{
 322        struct samsung_spdif_info *spdif = to_info(cpu_dai);
 323
 324        dev_dbg(spdif->dev, "Entered %s\n", __func__);
 325
 326        writel(spdif->saved_clkcon, spdif->regs + CLKCON);
 327        writel(spdif->saved_con, spdif->regs + CON);
 328        writel(spdif->saved_cstas, spdif->regs + CSTAS);
 329
 330        return 0;
 331}
 332#else
 333#define spdif_suspend NULL
 334#define spdif_resume NULL
 335#endif
 336
 337static const struct snd_soc_dai_ops spdif_dai_ops = {
 338        .set_sysclk     = spdif_set_sysclk,
 339        .trigger        = spdif_trigger,
 340        .hw_params      = spdif_hw_params,
 341        .shutdown       = spdif_shutdown,
 342};
 343
 344static struct snd_soc_dai_driver samsung_spdif_dai = {
 345        .name = "samsung-spdif",
 346        .playback = {
 347                .stream_name = "S/PDIF Playback",
 348                .channels_min = 2,
 349                .channels_max = 2,
 350                .rates = (SNDRV_PCM_RATE_32000 |
 351                                SNDRV_PCM_RATE_44100 |
 352                                SNDRV_PCM_RATE_48000 |
 353                                SNDRV_PCM_RATE_96000),
 354                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
 355        .ops = &spdif_dai_ops,
 356        .suspend = spdif_suspend,
 357        .resume = spdif_resume,
 358};
 359
 360static const struct snd_soc_component_driver samsung_spdif_component = {
 361        .name           = "samsung-spdif",
 362};
 363
 364static int spdif_probe(struct platform_device *pdev)
 365{
 366        struct s3c_audio_pdata *spdif_pdata;
 367        struct resource *mem_res, *dma_res;
 368        struct samsung_spdif_info *spdif;
 369        int ret;
 370
 371        spdif_pdata = pdev->dev.platform_data;
 372
 373        dev_dbg(&pdev->dev, "Entered %s\n", __func__);
 374
 375        dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 376        if (!dma_res) {
 377                dev_err(&pdev->dev, "Unable to get dma resource.\n");
 378                return -ENXIO;
 379        }
 380
 381        mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 382        if (!mem_res) {
 383                dev_err(&pdev->dev, "Unable to get register resource.\n");
 384                return -ENXIO;
 385        }
 386
 387        if (spdif_pdata && spdif_pdata->cfg_gpio
 388                        && spdif_pdata->cfg_gpio(pdev)) {
 389                dev_err(&pdev->dev, "Unable to configure GPIO pins\n");
 390                return -EINVAL;
 391        }
 392
 393        spdif = &spdif_info;
 394        spdif->dev = &pdev->dev;
 395
 396        spin_lock_init(&spdif->lock);
 397
 398        spdif->pclk = devm_clk_get(&pdev->dev, "spdif");
 399        if (IS_ERR(spdif->pclk)) {
 400                dev_err(&pdev->dev, "failed to get peri-clock\n");
 401                ret = -ENOENT;
 402                goto err0;
 403        }
 404        clk_prepare_enable(spdif->pclk);
 405
 406        spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif");
 407        if (IS_ERR(spdif->sclk)) {
 408                dev_err(&pdev->dev, "failed to get internal source clock\n");
 409                ret = -ENOENT;
 410                goto err1;
 411        }
 412        clk_prepare_enable(spdif->sclk);
 413
 414        /* Request S/PDIF Register's memory region */
 415        if (!request_mem_region(mem_res->start,
 416                                resource_size(mem_res), "samsung-spdif")) {
 417                dev_err(&pdev->dev, "Unable to request register region\n");
 418                ret = -EBUSY;
 419                goto err2;
 420        }
 421
 422        spdif->regs = ioremap(mem_res->start, 0x100);
 423        if (spdif->regs == NULL) {
 424                dev_err(&pdev->dev, "Cannot ioremap registers\n");
 425                ret = -ENXIO;
 426                goto err3;
 427        }
 428
 429        dev_set_drvdata(&pdev->dev, spdif);
 430
 431        ret = snd_soc_register_component(&pdev->dev, &samsung_spdif_component,
 432                                         &samsung_spdif_dai, 1);
 433        if (ret != 0) {
 434                dev_err(&pdev->dev, "fail to register dai\n");
 435                goto err4;
 436        }
 437
 438        spdif_stereo_out.dma_size = 2;
 439        spdif_stereo_out.client = &spdif_dma_client_out;
 440        spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
 441        spdif_stereo_out.channel = dma_res->start;
 442
 443        spdif->dma_playback = &spdif_stereo_out;
 444
 445        ret = samsung_asoc_dma_platform_register(&pdev->dev);
 446        if (ret) {
 447                dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
 448                goto err5;
 449        }
 450
 451        return 0;
 452err5:
 453        snd_soc_unregister_component(&pdev->dev);
 454err4:
 455        iounmap(spdif->regs);
 456err3:
 457        release_mem_region(mem_res->start, resource_size(mem_res));
 458err2:
 459        clk_disable_unprepare(spdif->sclk);
 460err1:
 461        clk_disable_unprepare(spdif->pclk);
 462err0:
 463        return ret;
 464}
 465
 466static int spdif_remove(struct platform_device *pdev)
 467{
 468        struct samsung_spdif_info *spdif = &spdif_info;
 469        struct resource *mem_res;
 470
 471        samsung_asoc_dma_platform_unregister(&pdev->dev);
 472        snd_soc_unregister_component(&pdev->dev);
 473
 474        iounmap(spdif->regs);
 475
 476        mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 477        if (mem_res)
 478                release_mem_region(mem_res->start, resource_size(mem_res));
 479
 480        clk_disable_unprepare(spdif->sclk);
 481        clk_disable_unprepare(spdif->pclk);
 482
 483        return 0;
 484}
 485
 486static struct platform_driver samsung_spdif_driver = {
 487        .probe  = spdif_probe,
 488        .remove = spdif_remove,
 489        .driver = {
 490                .name   = "samsung-spdif",
 491                .owner  = THIS_MODULE,
 492        },
 493};
 494
 495module_platform_driver(samsung_spdif_driver);
 496
 497MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
 498MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
 499MODULE_LICENSE("GPL");
 500MODULE_ALIAS("platform:samsung-spdif");
 501