Merge branch '3.4-urgent' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target...
[linux-flexiantxendom0-3.2.10.git] / sound / soc / samsung / s3c2412-i2s.c
1 /* sound/soc/samsung/s3c2412-i2s.c
2  *
3  * ALSA Soc Audio Layer - S3C2412 I2S driver
4  *
5  * Copyright (c) 2006 Wolfson Microelectronics PLC.
6  *      Graeme Gregory graeme.gregory@wolfsonmicro.com
7  *      linux@wolfsonmicro.com
8  *
9  * Copyright (c) 2007, 2004-2005 Simtec Electronics
10  *      http://armlinux.simtec.co.uk/
11  *      Ben Dooks <ben@simtec.co.uk>
12  *
13  * This program is free software; you can redistribute  it and/or modify it
14  * under  the terms of  the GNU General  Public License as published by the
15  * Free Software Foundation;  either version 2 of the  License, or (at your
16  * option) any later version.
17  */
18
19 #include <linux/delay.h>
20 #include <linux/gpio.h>
21 #include <linux/clk.h>
22 #include <linux/io.h>
23 #include <linux/module.h>
24
25 #include <sound/soc.h>
26 #include <sound/pcm_params.h>
27
28 #include <mach/regs-gpio.h>
29 #include <mach/dma.h>
30
31 #include "dma.h"
32 #include "regs-i2s-v2.h"
33 #include "s3c2412-i2s.h"
34
35 static struct s3c2410_dma_client s3c2412_dma_client_out = {
36         .name           = "I2S PCM Stereo out"
37 };
38
39 static struct s3c2410_dma_client s3c2412_dma_client_in = {
40         .name           = "I2S PCM Stereo in"
41 };
42
43 static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = {
44         .client         = &s3c2412_dma_client_out,
45         .channel        = DMACH_I2S_OUT,
46         .dma_addr       = S3C2410_PA_IIS + S3C2412_IISTXD,
47         .dma_size       = 4,
48 };
49
50 static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
51         .client         = &s3c2412_dma_client_in,
52         .channel        = DMACH_I2S_IN,
53         .dma_addr       = S3C2410_PA_IIS + S3C2412_IISRXD,
54         .dma_size       = 4,
55 };
56
57 static struct s3c_i2sv2_info s3c2412_i2s;
58
59 static int s3c2412_i2s_probe(struct snd_soc_dai *dai)
60 {
61         int ret;
62
63         pr_debug("Entered %s\n", __func__);
64
65         ret = s3c_i2sv2_probe(dai, &s3c2412_i2s, S3C2410_PA_IIS);
66         if (ret)
67                 return ret;
68
69         s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in;
70         s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
71
72         s3c2412_i2s.iis_cclk = clk_get(dai->dev, "i2sclk");
73         if (IS_ERR(s3c2412_i2s.iis_cclk)) {
74                 pr_err("failed to get i2sclk clock\n");
75                 iounmap(s3c2412_i2s.regs);
76                 return PTR_ERR(s3c2412_i2s.iis_cclk);
77         }
78
79         /* Set MPLL as the source for IIS CLK */
80
81         clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
82         clk_enable(s3c2412_i2s.iis_cclk);
83
84         s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
85
86         /* Configure the I2S pins in correct mode */
87         s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
88         s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
89         s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
90         s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
91         s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
92
93         return 0;
94 }
95
96 static int s3c2412_i2s_remove(struct snd_soc_dai *dai)
97 {
98         clk_disable(s3c2412_i2s.iis_cclk);
99         clk_put(s3c2412_i2s.iis_cclk);
100         iounmap(s3c2412_i2s.regs);
101
102         return 0;
103 }
104
105 static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
106                                  struct snd_pcm_hw_params *params,
107                                  struct snd_soc_dai *cpu_dai)
108 {
109         struct s3c_i2sv2_info *i2s = snd_soc_dai_get_drvdata(cpu_dai);
110         struct s3c_dma_params *dma_data;
111         u32 iismod;
112
113         pr_debug("Entered %s\n", __func__);
114
115         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
116                 dma_data = i2s->dma_playback;
117         else
118                 dma_data = i2s->dma_capture;
119
120         snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
121
122         iismod = readl(i2s->regs + S3C2412_IISMOD);
123         pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
124
125         switch (params_format(params)) {
126         case SNDRV_PCM_FORMAT_S8:
127                 iismod |= S3C2412_IISMOD_8BIT;
128                 break;
129         case SNDRV_PCM_FORMAT_S16_LE:
130                 iismod &= ~S3C2412_IISMOD_8BIT;
131                 break;
132         }
133
134         writel(iismod, i2s->regs + S3C2412_IISMOD);
135         pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
136
137         return 0;
138 }
139
140 #define S3C2412_I2S_RATES \
141         (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
142         SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
143         SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
144
145 static const struct snd_soc_dai_ops s3c2412_i2s_dai_ops = {
146         .hw_params      = s3c2412_i2s_hw_params,
147 };
148
149 static struct snd_soc_dai_driver s3c2412_i2s_dai = {
150         .probe          = s3c2412_i2s_probe,
151         .remove = s3c2412_i2s_remove,
152         .playback = {
153                 .channels_min   = 2,
154                 .channels_max   = 2,
155                 .rates          = S3C2412_I2S_RATES,
156                 .formats        = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
157         },
158         .capture = {
159                 .channels_min   = 2,
160                 .channels_max   = 2,
161                 .rates          = S3C2412_I2S_RATES,
162                 .formats        = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
163         },
164         .ops = &s3c2412_i2s_dai_ops,
165 };
166
167 static __devinit int s3c2412_iis_dev_probe(struct platform_device *pdev)
168 {
169         return s3c_i2sv2_register_dai(&pdev->dev, -1, &s3c2412_i2s_dai);
170 }
171
172 static __devexit int s3c2412_iis_dev_remove(struct platform_device *pdev)
173 {
174         snd_soc_unregister_dai(&pdev->dev);
175         return 0;
176 }
177
178 static struct platform_driver s3c2412_iis_driver = {
179         .probe  = s3c2412_iis_dev_probe,
180         .remove = __devexit_p(s3c2412_iis_dev_remove),
181         .driver = {
182                 .name = "s3c2412-iis",
183                 .owner = THIS_MODULE,
184         },
185 };
186
187 module_platform_driver(s3c2412_iis_driver);
188
189 /* Module information */
190 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
191 MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
192 MODULE_LICENSE("GPL");
193 MODULE_ALIAS("platform:s3c2412-iis");