Commit 357a1db94ecc5b3d605574b164d288cd7dbf8dbd

Authored by Jassi Brar
Committed by Mark Brown
1 parent acf1aef9ec

ASoC: Added the CPU driver for PCM controllers

Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

Showing 4 changed files with 680 additions and 0 deletions Side-by-side Diff

sound/soc/s3c24xx/Kconfig
... ... @@ -24,6 +24,9 @@
24 24 select SND_S3C_I2SV2_SOC
25 25 select S3C64XX_DMA
26 26  
  27 +config SND_S3C_SOC_PCM
  28 + tristate
  29 +
27 30 config SND_S3C2443_SOC_AC97
28 31 tristate
29 32 select S3C2410_DMA
sound/soc/s3c24xx/Makefile
... ... @@ -5,6 +5,7 @@
5 5 snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o
6 6 snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
7 7 snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
  8 +snd-soc-s3c-pcm-objs := s3c-pcm.o
8 9  
9 10 obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
10 11 obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
... ... @@ -12,6 +13,7 @@
12 13 obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
13 14 obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o
14 15 obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
  16 +obj-$(CONFIG_SND_S3C_SOC_PCM) += snd-soc-s3c-pcm.o
15 17  
16 18 # S3C24XX Machine Support
17 19 snd-soc-jive-wm8750-objs := jive_wm8750.o
sound/soc/s3c24xx/s3c-pcm.c
  1 +/* sound/soc/s3c24xx/s3c-pcm.c
  2 + *
  3 + * ALSA SoC Audio Layer - S3C PCM-Controller driver
  4 + *
  5 + * Copyright (c) 2009 Samsung Electronics Co. Ltd
  6 + * Author: Jaswinder Singh <jassi.brar@samsung.com>
  7 + * based upon I2S drivers by Ben Dooks.
  8 + *
  9 + * This program is free software; you can redistribute it and/or modify
  10 + * it under the terms of the GNU General Public License version 2 as
  11 + * published by the Free Software Foundation.
  12 + */
  13 +
  14 +#include <linux/init.h>
  15 +#include <linux/module.h>
  16 +#include <linux/device.h>
  17 +#include <linux/delay.h>
  18 +#include <linux/clk.h>
  19 +#include <linux/kernel.h>
  20 +#include <linux/gpio.h>
  21 +#include <linux/io.h>
  22 +
  23 +#include <sound/core.h>
  24 +#include <sound/pcm.h>
  25 +#include <sound/pcm_params.h>
  26 +#include <sound/initval.h>
  27 +#include <sound/soc.h>
  28 +
  29 +#include <plat/audio.h>
  30 +#include <plat/dma.h>
  31 +
  32 +#include "s3c-dma.h"
  33 +#include "s3c-pcm.h"
  34 +
  35 +static struct s3c2410_dma_client s3c_pcm_dma_client_out = {
  36 + .name = "PCM Stereo out"
  37 +};
  38 +
  39 +static struct s3c2410_dma_client s3c_pcm_dma_client_in = {
  40 + .name = "PCM Stereo in"
  41 +};
  42 +
  43 +static struct s3c_dma_params s3c_pcm_stereo_out[] = {
  44 + [0] = {
  45 + .client = &s3c_pcm_dma_client_out,
  46 + .dma_size = 4,
  47 + },
  48 + [1] = {
  49 + .client = &s3c_pcm_dma_client_out,
  50 + .dma_size = 4,
  51 + },
  52 +};
  53 +
  54 +static struct s3c_dma_params s3c_pcm_stereo_in[] = {
  55 + [0] = {
  56 + .client = &s3c_pcm_dma_client_in,
  57 + .dma_size = 4,
  58 + },
  59 + [1] = {
  60 + .client = &s3c_pcm_dma_client_in,
  61 + .dma_size = 4,
  62 + },
  63 +};
  64 +
  65 +static struct s3c_pcm_info s3c_pcm[2];
  66 +
  67 +static inline struct s3c_pcm_info *to_info(struct snd_soc_dai *cpu_dai)
  68 +{
  69 + return cpu_dai->private_data;
  70 +}
  71 +
  72 +static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on)
  73 +{
  74 + void __iomem *regs = pcm->regs;
  75 + u32 ctl, clkctl;
  76 +
  77 + clkctl = readl(regs + S3C_PCM_CLKCTL);
  78 + ctl = readl(regs + S3C_PCM_CTL);
  79 + ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK
  80 + << S3C_PCM_CTL_TXDIPSTICK_SHIFT);
  81 +
  82 + if (on) {
  83 + ctl |= S3C_PCM_CTL_TXDMA_EN;
  84 + ctl |= S3C_PCM_CTL_TXFIFO_EN;
  85 + ctl |= S3C_PCM_CTL_ENABLE;
  86 + ctl |= (0x20<<S3C_PCM_CTL_TXDIPSTICK_SHIFT);
  87 + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
  88 + } else {
  89 + ctl &= ~S3C_PCM_CTL_TXDMA_EN;
  90 + ctl &= ~S3C_PCM_CTL_TXFIFO_EN;
  91 +
  92 + if (!(ctl & S3C_PCM_CTL_RXFIFO_EN)) {
  93 + ctl &= ~S3C_PCM_CTL_ENABLE;
  94 + if (!pcm->idleclk)
  95 + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
  96 + }
  97 + }
  98 +
  99 + writel(clkctl, regs + S3C_PCM_CLKCTL);
  100 + writel(ctl, regs + S3C_PCM_CTL);
  101 +}
  102 +
  103 +static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on)
  104 +{
  105 + void __iomem *regs = pcm->regs;
  106 + u32 ctl, clkctl;
  107 +
  108 + ctl = readl(regs + S3C_PCM_CTL);
  109 + clkctl = readl(regs + S3C_PCM_CLKCTL);
  110 +
  111 + if (on) {
  112 + ctl |= S3C_PCM_CTL_RXDMA_EN;
  113 + ctl |= S3C_PCM_CTL_RXFIFO_EN;
  114 + ctl |= S3C_PCM_CTL_ENABLE;
  115 + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
  116 + } else {
  117 + ctl &= ~S3C_PCM_CTL_RXDMA_EN;
  118 + ctl &= ~S3C_PCM_CTL_RXFIFO_EN;
  119 +
  120 + if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) {
  121 + ctl &= ~S3C_PCM_CTL_ENABLE;
  122 + if (!pcm->idleclk)
  123 + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
  124 + }
  125 + }
  126 +
  127 + writel(clkctl, regs + S3C_PCM_CLKCTL);
  128 + writel(ctl, regs + S3C_PCM_CTL);
  129 +}
  130 +
  131 +static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
  132 + struct snd_soc_dai *dai)
  133 +{
  134 + struct snd_soc_pcm_runtime *rtd = substream->private_data;
  135 + struct s3c_pcm_info *pcm = to_info(rtd->dai->cpu_dai);
  136 + unsigned long flags;
  137 +
  138 + dev_dbg(pcm->dev, "Entered %s\n", __func__);
  139 +
  140 + switch (cmd) {
  141 + case SNDRV_PCM_TRIGGER_START:
  142 + case SNDRV_PCM_TRIGGER_RESUME:
  143 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  144 + spin_lock_irqsave(&pcm->lock, flags);
  145 +
  146 + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  147 + s3c_pcm_snd_rxctrl(pcm, 1);
  148 + else
  149 + s3c_pcm_snd_txctrl(pcm, 1);
  150 +
  151 + spin_unlock_irqrestore(&pcm->lock, flags);
  152 + break;
  153 +
  154 + case SNDRV_PCM_TRIGGER_STOP:
  155 + case SNDRV_PCM_TRIGGER_SUSPEND:
  156 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  157 + spin_lock_irqsave(&pcm->lock, flags);
  158 +
  159 + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  160 + s3c_pcm_snd_rxctrl(pcm, 0);
  161 + else
  162 + s3c_pcm_snd_txctrl(pcm, 0);
  163 +
  164 + spin_unlock_irqrestore(&pcm->lock, flags);
  165 + break;
  166 +
  167 + default:
  168 + return -EINVAL;
  169 + }
  170 +
  171 + return 0;
  172 +}
  173 +
  174 +static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
  175 + struct snd_pcm_hw_params *params,
  176 + struct snd_soc_dai *socdai)
  177 +{
  178 + struct snd_soc_pcm_runtime *rtd = substream->private_data;
  179 + struct snd_soc_dai_link *dai = rtd->dai;
  180 + struct s3c_pcm_info *pcm = to_info(dai->cpu_dai);
  181 + void __iomem *regs = pcm->regs;
  182 + struct clk *clk;
  183 + int sclk_div, sync_div;
  184 + unsigned long flags;
  185 + u32 clkctl;
  186 +
  187 + dev_dbg(pcm->dev, "Entered %s\n", __func__);
  188 +
  189 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  190 + dai->cpu_dai->dma_data = pcm->dma_playback;
  191 + else
  192 + dai->cpu_dai->dma_data = pcm->dma_capture;
  193 +
  194 + /* Strictly check for sample size */
  195 + switch (params_format(params)) {
  196 + case SNDRV_PCM_FORMAT_S16_LE:
  197 + break;
  198 + default:
  199 + return -EINVAL;
  200 + }
  201 +
  202 + spin_lock_irqsave(&pcm->lock, flags);
  203 +
  204 + /* Get hold of the PCMSOURCE_CLK */
  205 + clkctl = readl(regs + S3C_PCM_CLKCTL);
  206 + if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK)
  207 + clk = pcm->pclk;
  208 + else
  209 + clk = pcm->cclk;
  210 +
  211 + /* Set the SCLK divider */
  212 + sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs /
  213 + params_rate(params) / 2 - 1;
  214 +
  215 + clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK
  216 + << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
  217 + clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK)
  218 + << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
  219 +
  220 + /* Set the SYNC divider */
  221 + sync_div = pcm->sclk_per_fs - 1;
  222 +
  223 + clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK
  224 + << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
  225 + clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK)
  226 + << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
  227 +
  228 + writel(clkctl, regs + S3C_PCM_CLKCTL);
  229 +
  230 + spin_unlock_irqrestore(&pcm->lock, flags);
  231 +
  232 + dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs \
  233 + SCLK_DIV=%d SYNC_DIV=%d\n",
  234 + clk_get_rate(clk), pcm->sclk_per_fs,
  235 + sclk_div, sync_div);
  236 +
  237 + return 0;
  238 +}
  239 +
  240 +static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai,
  241 + unsigned int fmt)
  242 +{
  243 + struct s3c_pcm_info *pcm = to_info(cpu_dai);
  244 + void __iomem *regs = pcm->regs;
  245 + unsigned long flags;
  246 + int ret = 0;
  247 + u32 ctl;
  248 +
  249 + dev_dbg(pcm->dev, "Entered %s\n", __func__);
  250 +
  251 + spin_lock_irqsave(&pcm->lock, flags);
  252 +
  253 + ctl = readl(regs + S3C_PCM_CTL);
  254 +
  255 + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
  256 + case SND_SOC_DAIFMT_NB_NF:
  257 + /* Nothing to do, NB_NF by default */
  258 + break;
  259 + default:
  260 + dev_err(pcm->dev, "Unsupported clock inversion!\n");
  261 + ret = -EINVAL;
  262 + goto exit;
  263 + }
  264 +
  265 + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
  266 + case SND_SOC_DAIFMT_CBS_CFS:
  267 + /* Nothing to do, Master by default */
  268 + break;
  269 + default:
  270 + dev_err(pcm->dev, "Unsupported master/slave format!\n");
  271 + ret = -EINVAL;
  272 + goto exit;
  273 + }
  274 +
  275 + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
  276 + case SND_SOC_DAIFMT_CONT:
  277 + pcm->idleclk = 1;
  278 + break;
  279 + case SND_SOC_DAIFMT_GATED:
  280 + pcm->idleclk = 0;
  281 + break;
  282 + default:
  283 + dev_err(pcm->dev, "Invalid Clock gating request!\n");
  284 + ret = -EINVAL;
  285 + goto exit;
  286 + }
  287 +
  288 + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
  289 + case SND_SOC_DAIFMT_DSP_A:
  290 + ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
  291 + ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
  292 + break;
  293 + case SND_SOC_DAIFMT_DSP_B:
  294 + ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
  295 + ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
  296 + break;
  297 + default:
  298 + dev_err(pcm->dev, "Unsupported data format!\n");
  299 + ret = -EINVAL;
  300 + goto exit;
  301 + }
  302 +
  303 + writel(ctl, regs + S3C_PCM_CTL);
  304 +
  305 +exit:
  306 + spin_unlock_irqrestore(&pcm->lock, flags);
  307 +
  308 + return ret;
  309 +}
  310 +
  311 +static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai,
  312 + int div_id, int div)
  313 +{
  314 + struct s3c_pcm_info *pcm = to_info(cpu_dai);
  315 +
  316 + switch (div_id) {
  317 + case S3C_PCM_SCLK_PER_FS:
  318 + pcm->sclk_per_fs = div;
  319 + break;
  320 +
  321 + default:
  322 + return -EINVAL;
  323 + }
  324 +
  325 + return 0;
  326 +}
  327 +
  328 +static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai,
  329 + int clk_id, unsigned int freq, int dir)
  330 +{
  331 + struct s3c_pcm_info *pcm = to_info(cpu_dai);
  332 + void __iomem *regs = pcm->regs;
  333 + u32 clkctl = readl(regs + S3C_PCM_CLKCTL);
  334 +
  335 + switch (clk_id) {
  336 + case S3C_PCM_CLKSRC_PCLK:
  337 + clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
  338 + break;
  339 +
  340 + case S3C_PCM_CLKSRC_MUX:
  341 + clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
  342 +
  343 + if (clk_get_rate(pcm->cclk) != freq)
  344 + clk_set_rate(pcm->cclk, freq);
  345 +
  346 + break;
  347 +
  348 + default:
  349 + return -EINVAL;
  350 + }
  351 +
  352 + writel(clkctl, regs + S3C_PCM_CLKCTL);
  353 +
  354 + return 0;
  355 +}
  356 +
  357 +static struct snd_soc_dai_ops s3c_pcm_dai_ops = {
  358 + .set_sysclk = s3c_pcm_set_sysclk,
  359 + .set_clkdiv = s3c_pcm_set_clkdiv,
  360 + .trigger = s3c_pcm_trigger,
  361 + .hw_params = s3c_pcm_hw_params,
  362 + .set_fmt = s3c_pcm_set_fmt,
  363 +};
  364 +
  365 +#define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000
  366 +
  367 +#define S3C_PCM_DECLARE(n) \
  368 +{ \
  369 + .name = "samsung-pcm", \
  370 + .id = (n), \
  371 + .symmetric_rates = 1, \
  372 + .ops = &s3c_pcm_dai_ops, \
  373 + .playback = { \
  374 + .channels_min = 2, \
  375 + .channels_max = 2, \
  376 + .rates = S3C_PCM_RATES, \
  377 + .formats = SNDRV_PCM_FMTBIT_S16_LE, \
  378 + }, \
  379 + .capture = { \
  380 + .channels_min = 2, \
  381 + .channels_max = 2, \
  382 + .rates = S3C_PCM_RATES, \
  383 + .formats = SNDRV_PCM_FMTBIT_S16_LE, \
  384 + }, \
  385 +}
  386 +
  387 +struct snd_soc_dai s3c_pcm_dai[] = {
  388 + S3C_PCM_DECLARE(0),
  389 + S3C_PCM_DECLARE(1),
  390 +};
  391 +EXPORT_SYMBOL_GPL(s3c_pcm_dai);
  392 +
  393 +static __devinit int s3c_pcm_dev_probe(struct platform_device *pdev)
  394 +{
  395 + struct s3c_pcm_info *pcm;
  396 + struct snd_soc_dai *dai;
  397 + struct resource *mem_res, *dmatx_res, *dmarx_res;
  398 + struct s3c_audio_pdata *pcm_pdata;
  399 + int ret;
  400 +
  401 + /* Check for valid device index */
  402 + if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) {
  403 + dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
  404 + return -EINVAL;
  405 + }
  406 +
  407 + pcm_pdata = pdev->dev.platform_data;
  408 +
  409 + /* Check for availability of necessary resource */
  410 + dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
  411 + if (!dmatx_res) {
  412 + dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n");
  413 + return -ENXIO;
  414 + }
  415 +
  416 + dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
  417 + if (!dmarx_res) {
  418 + dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n");
  419 + return -ENXIO;
  420 + }
  421 +
  422 + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  423 + if (!mem_res) {
  424 + dev_err(&pdev->dev, "Unable to get register resource\n");
  425 + return -ENXIO;
  426 + }
  427 +
  428 + if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) {
  429 + dev_err(&pdev->dev, "Unable to configure gpio\n");
  430 + return -EINVAL;
  431 + }
  432 +
  433 + pcm = &s3c_pcm[pdev->id];
  434 + pcm->dev = &pdev->dev;
  435 +
  436 + spin_lock_init(&pcm->lock);
  437 +
  438 + dai = &s3c_pcm_dai[pdev->id];
  439 + dai->dev = &pdev->dev;
  440 +
  441 + /* Default is 128fs */
  442 + pcm->sclk_per_fs = 128;
  443 +
  444 + pcm->cclk = clk_get(&pdev->dev, "audio-bus");
  445 + if (IS_ERR(pcm->cclk)) {
  446 + dev_err(&pdev->dev, "failed to get audio-bus\n");
  447 + ret = PTR_ERR(pcm->cclk);
  448 + goto err1;
  449 + }
  450 + clk_enable(pcm->cclk);
  451 +
  452 + /* record our pcm structure for later use in the callbacks */
  453 + dai->private_data = pcm;
  454 +
  455 + if (!request_mem_region(mem_res->start,
  456 + resource_size(mem_res), "samsung-pcm")) {
  457 + dev_err(&pdev->dev, "Unable to request register region\n");
  458 + ret = -EBUSY;
  459 + goto err2;
  460 + }
  461 +
  462 + pcm->regs = ioremap(mem_res->start, 0x100);
  463 + if (pcm->regs == NULL) {
  464 + dev_err(&pdev->dev, "cannot ioremap registers\n");
  465 + ret = -ENXIO;
  466 + goto err3;
  467 + }
  468 +
  469 + pcm->pclk = clk_get(&pdev->dev, "pcm");
  470 + if (IS_ERR(pcm->pclk)) {
  471 + dev_err(&pdev->dev, "failed to get pcm_clock\n");
  472 + ret = -ENOENT;
  473 + goto err4;
  474 + }
  475 + clk_enable(pcm->pclk);
  476 +
  477 + ret = snd_soc_register_dai(dai);
  478 + if (ret != 0) {
  479 + dev_err(&pdev->dev, "failed to get pcm_clock\n");
  480 + goto err5;
  481 + }
  482 +
  483 + s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start
  484 + + S3C_PCM_RXFIFO;
  485 + s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start
  486 + + S3C_PCM_TXFIFO;
  487 +
  488 + s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start;
  489 + s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start;
  490 +
  491 + pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
  492 + pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
  493 +
  494 + return 0;
  495 +
  496 +err5:
  497 + clk_disable(pcm->pclk);
  498 + clk_put(pcm->pclk);
  499 +err4:
  500 + iounmap(pcm->regs);
  501 +err3:
  502 + release_mem_region(mem_res->start, resource_size(mem_res));
  503 +err2:
  504 + clk_disable(pcm->cclk);
  505 + clk_put(pcm->cclk);
  506 +err1:
  507 + return ret;
  508 +}
  509 +
  510 +static __devexit int s3c_pcm_dev_remove(struct platform_device *pdev)
  511 +{
  512 + struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
  513 + struct resource *mem_res;
  514 +
  515 + iounmap(pcm->regs);
  516 +
  517 + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  518 + release_mem_region(mem_res->start, resource_size(mem_res));
  519 +
  520 + clk_disable(pcm->cclk);
  521 + clk_disable(pcm->pclk);
  522 + clk_put(pcm->pclk);
  523 + clk_put(pcm->cclk);
  524 +
  525 + return 0;
  526 +}
  527 +
  528 +static struct platform_driver s3c_pcm_driver = {
  529 + .probe = s3c_pcm_dev_probe,
  530 + .remove = s3c_pcm_dev_remove,
  531 + .driver = {
  532 + .name = "samsung-pcm",
  533 + .owner = THIS_MODULE,
  534 + },
  535 +};
  536 +
  537 +static int __init s3c_pcm_init(void)
  538 +{
  539 + return platform_driver_register(&s3c_pcm_driver);
  540 +}
  541 +module_init(s3c_pcm_init);
  542 +
  543 +static void __exit s3c_pcm_exit(void)
  544 +{
  545 + platform_driver_unregister(&s3c_pcm_driver);
  546 +}
  547 +module_exit(s3c_pcm_exit);
  548 +
  549 +/* Module information */
  550 +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
  551 +MODULE_DESCRIPTION("S3C PCM Controller Driver");
  552 +MODULE_LICENSE("GPL");
sound/soc/s3c24xx/s3c-pcm.h
  1 +/* sound/soc/s3c24xx/s3c-pcm.h
  2 + *
  3 + * This program is free software; you can redistribute it and/or modify
  4 + * it under the terms of the GNU General Public License version 2 as
  5 + * published by the Free Software Foundation.
  6 + *
  7 + */
  8 +
  9 +#ifndef __S3C_PCM_H
  10 +#define __S3C_PCM_H __FILE__
  11 +
  12 +/*Register Offsets */
  13 +#define S3C_PCM_CTL (0x00)
  14 +#define S3C_PCM_CLKCTL (0x04)
  15 +#define S3C_PCM_TXFIFO (0x08)
  16 +#define S3C_PCM_RXFIFO (0x0C)
  17 +#define S3C_PCM_IRQCTL (0x10)
  18 +#define S3C_PCM_IRQSTAT (0x14)
  19 +#define S3C_PCM_FIFOSTAT (0x18)
  20 +#define S3C_PCM_CLRINT (0x20)
  21 +
  22 +/* PCM_CTL Bit-Fields */
  23 +#define S3C_PCM_CTL_TXDIPSTICK_MASK (0x3f)
  24 +#define S3C_PCM_CTL_TXDIPSTICK_SHIFT (13)
  25 +#define S3C_PCM_CTL_RXDIPSTICK_MSK (0x3f<<7)
  26 +#define S3C_PCM_CTL_TXDMA_EN (0x1<<6)
  27 +#define S3C_PCM_CTL_RXDMA_EN (0x1<<5)
  28 +#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1<<4)
  29 +#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1<<3)
  30 +#define S3C_PCM_CTL_TXFIFO_EN (0x1<<2)
  31 +#define S3C_PCM_CTL_RXFIFO_EN (0x1<<1)
  32 +#define S3C_PCM_CTL_ENABLE (0x1<<0)
  33 +
  34 +/* PCM_CLKCTL Bit-Fields */
  35 +#define S3C_PCM_CLKCTL_SERCLK_EN (0x1<<19)
  36 +#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1<<18)
  37 +#define S3C_PCM_CLKCTL_SCLKDIV_MASK (0x1ff)
  38 +#define S3C_PCM_CLKCTL_SYNCDIV_MASK (0x1ff)
  39 +#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT (9)
  40 +#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT (0)
  41 +
  42 +/* PCM_TXFIFO Bit-Fields */
  43 +#define S3C_PCM_TXFIFO_DVALID (0x1<<16)
  44 +#define S3C_PCM_TXFIFO_DATA_MSK (0xffff<<0)
  45 +
  46 +/* PCM_RXFIFO Bit-Fields */
  47 +#define S3C_PCM_RXFIFO_DVALID (0x1<<16)
  48 +#define S3C_PCM_RXFIFO_DATA_MSK (0xffff<<0)
  49 +
  50 +/* PCM_IRQCTL Bit-Fields */
  51 +#define S3C_PCM_IRQCTL_IRQEN (0x1<<14)
  52 +#define S3C_PCM_IRQCTL_WRDEN (0x1<<12)
  53 +#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1<<11)
  54 +#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1<<10)
  55 +#define S3C_PCM_IRQCTL_TXFULLEN (0x1<<9)
  56 +#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1<<8)
  57 +#define S3C_PCM_IRQCTL_TXSTARVEN (0x1<<7)
  58 +#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1<<6)
  59 +#define S3C_PCM_IRQCTL_RXEMPTEN (0x1<<5)
  60 +#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1<<4)
  61 +#define S3C_PCM_IRQCTL_RXFULLEN (0x1<<3)
  62 +#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1<<2)
  63 +#define S3C_PCM_IRQCTL_RXSTARVEN (0x1<<1)
  64 +#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1<<0)
  65 +
  66 +/* PCM_IRQSTAT Bit-Fields */
  67 +#define S3C_PCM_IRQSTAT_IRQPND (0x1<<13)
  68 +#define S3C_PCM_IRQSTAT_WRD_XFER (0x1<<12)
  69 +#define S3C_PCM_IRQSTAT_TXEMPTY (0x1<<11)
  70 +#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1<<10)
  71 +#define S3C_PCM_IRQSTAT_TXFULL (0x1<<9)
  72 +#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1<<8)
  73 +#define S3C_PCM_IRQSTAT_TXSTARV (0x1<<7)
  74 +#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1<<6)
  75 +#define S3C_PCM_IRQSTAT_RXEMPT (0x1<<5)
  76 +#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1<<4)
  77 +#define S3C_PCM_IRQSTAT_RXFULL (0x1<<3)
  78 +#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1<<2)
  79 +#define S3C_PCM_IRQSTAT_RXSTARV (0x1<<1)
  80 +#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1<<0)
  81 +
  82 +/* PCM_FIFOSTAT Bit-Fields */
  83 +#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f<<14)
  84 +#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1<<13)
  85 +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1<<12)
  86 +#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1<<11)
  87 +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1<<10)
  88 +#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f<<4)
  89 +#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1<<3)
  90 +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1<<2)
  91 +#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1<<1)
  92 +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1<<0)
  93 +
  94 +#define S3C_PCM_CLKSRC_PCLK 0
  95 +#define S3C_PCM_CLKSRC_MUX 1
  96 +
  97 +#define S3C_PCM_SCLK_PER_FS 0
  98 +
  99 +/**
  100 + * struct s3c_pcm_info - S3C PCM Controller information
  101 + * @dev: The parent device passed to use from the probe.
  102 + * @regs: The pointer to the device register block.
  103 + * @dma_playback: DMA information for playback channel.
  104 + * @dma_capture: DMA information for capture channel.
  105 + */
  106 +struct s3c_pcm_info {
  107 + spinlock_t lock;
  108 + struct device *dev;
  109 + void __iomem *regs;
  110 +
  111 + unsigned int sclk_per_fs;
  112 +
  113 + /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
  114 + unsigned int idleclk;
  115 +
  116 + struct clk *pclk;
  117 + struct clk *cclk;
  118 +
  119 + struct s3c_dma_params *dma_playback;
  120 + struct s3c_dma_params *dma_capture;
  121 +};
  122 +
  123 +#endif /* __S3C_PCM_H */