aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/s3c24xx/s3c64xx-i2s.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/s3c24xx/s3c64xx-i2s.c')
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s.c206
1 files changed, 129 insertions, 77 deletions
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c
index 1d85cb85a7d2..ae7acb6c4f1d 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.c
@@ -12,15 +12,15 @@
12 * published by the Free Software Foundation. 12 * published by the Free Software Foundation.
13 */ 13 */
14 14
15#include <linux/module.h>
15#include <linux/clk.h> 16#include <linux/clk.h>
16#include <linux/gpio.h> 17#include <linux/gpio.h>
17#include <linux/io.h> 18#include <linux/io.h>
19#include <linux/slab.h>
18 20
19#include <sound/soc.h> 21#include <sound/soc.h>
20 22
21#include <mach/gpio-bank-d.h> 23#include <plat/audio.h>
22#include <mach/gpio-bank-e.h>
23#include <plat/gpio-cfg.h>
24 24
25#include <mach/map.h> 25#include <mach/map.h>
26#include <mach/dma.h> 26#include <mach/dma.h>
@@ -46,45 +46,107 @@ static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[MAX_I2SV3];
46static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[MAX_I2SV3]; 46static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[MAX_I2SV3];
47static struct s3c_i2sv2_info s3c64xx_i2s[MAX_I2SV3]; 47static struct s3c_i2sv2_info s3c64xx_i2s[MAX_I2SV3];
48 48
49struct snd_soc_dai s3c64xx_i2s_dai[MAX_I2SV3]; 49struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai)
50EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
51
52static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
53{ 50{
54 return cpu_dai->private_data; 51 struct s3c_i2sv2_info *i2s = snd_soc_dai_get_drvdata(dai);
52 u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
53
54 if (iismod & S3C2412_IISMOD_IMS_SYSMUX)
55 return i2s->iis_cclk;
56 else
57 return i2s->iis_pclk;
55} 58}
59EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock);
56 60
57static int s3c64xx_i2s_probe(struct platform_device *pdev, 61static int s3c64xx_i2s_probe(struct snd_soc_dai *dai)
58 struct snd_soc_dai *dai)
59{ 62{
60 /* configure GPIO for i2s port */ 63 struct s3c_i2sv2_info *i2s;
61 switch (dai->id) { 64 int ret;
62 case 0: 65
63 s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_I2S0_CLK); 66 if (dai->id >= MAX_I2SV3) {
64 s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_I2S0_CDCLK); 67 dev_err(dai->dev, "id %d out of range\n", dai->id);
65 s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_I2S0_LRCLK); 68 return -EINVAL;
66 s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_I2S0_DI); 69 }
67 s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_I2S0_D0); 70
68 break; 71 i2s = &s3c64xx_i2s[dai->id];
69 case 1: 72 snd_soc_dai_set_drvdata(dai, i2s);
70 s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_I2S1_CLK); 73
71 s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_I2S1_CDCLK); 74 i2s->iis_cclk = clk_get(dai->dev, "audio-bus");
72 s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_I2S1_LRCLK); 75 if (IS_ERR(i2s->iis_cclk)) {
73 s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_I2S1_DI); 76 dev_err(dai->dev, "failed to get audio-bus\n");
74 s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_I2S1_D0); 77 ret = PTR_ERR(i2s->iis_cclk);
78 goto err;
75 } 79 }
76 80
81 clk_enable(i2s->iis_cclk);
82
83 ret = s3c_i2sv2_probe(dai, i2s, i2s->base);
84 if (ret)
85 goto err_clk;
86
77 return 0; 87 return 0;
88
89err_clk:
90 clk_disable(i2s->iis_cclk);
91 clk_put(i2s->iis_cclk);
92err:
93 kfree(i2s);
94 return ret;
78} 95}
79 96
97static int s3c64xx_i2s_remove(struct snd_soc_dai *dai)
98{
99 struct s3c_i2sv2_info *i2s = snd_soc_dai_get_drvdata(dai);
100
101 clk_disable(i2s->iis_cclk);
102 clk_put(i2s->iis_cclk);
103 kfree(i2s);
104 return 0;
105}
80 106
81static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops; 107static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops;
82 108
109static struct snd_soc_dai_driver s3c64xx_i2s_dai[MAX_I2SV3] = {
110{
111 .name = "s3c64xx-i2s-0",
112 .probe = s3c64xx_i2s_probe,
113 .remove = s3c64xx_i2s_remove,
114 .playback = {
115 .channels_min = 2,
116 .channels_max = 2,
117 .rates = S3C64XX_I2S_RATES,
118 .formats = S3C64XX_I2S_FMTS,},
119 .capture = {
120 .channels_min = 2,
121 .channels_max = 2,
122 .rates = S3C64XX_I2S_RATES,
123 .formats = S3C64XX_I2S_FMTS,},
124 .ops = &s3c64xx_i2s_dai_ops,
125 .symmetric_rates = 1,
126}, {
127 .name = "s3c64xx-i2s-1",
128 .probe = s3c64xx_i2s_probe,
129 .remove = s3c64xx_i2s_remove,
130 .playback = {
131 .channels_min = 2,
132 .channels_max = 2,
133 .rates = S3C64XX_I2S_RATES,
134 .formats = S3C64XX_I2S_FMTS,},
135 .capture = {
136 .channels_min = 2,
137 .channels_max = 2,
138 .rates = S3C64XX_I2S_RATES,
139 .formats = S3C64XX_I2S_FMTS,},
140 .ops = &s3c64xx_i2s_dai_ops,
141 .symmetric_rates = 1,
142},};
143
83static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev) 144static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev)
84{ 145{
146 struct s3c_audio_pdata *i2s_pdata;
85 struct s3c_i2sv2_info *i2s; 147 struct s3c_i2sv2_info *i2s;
86 struct snd_soc_dai *dai; 148 struct resource *res;
87 int ret; 149 int i, ret;
88 150
89 if (pdev->id >= MAX_I2SV3) { 151 if (pdev->id >= MAX_I2SV3) {
90 dev_err(&pdev->dev, "id %d out of range\n", pdev->id); 152 dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
@@ -92,74 +154,63 @@ static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev)
92 } 154 }
93 155
94 i2s = &s3c64xx_i2s[pdev->id]; 156 i2s = &s3c64xx_i2s[pdev->id];
95 dai = &s3c64xx_i2s_dai[pdev->id];
96 dai->dev = &pdev->dev;
97 dai->name = "s3c64xx-i2s";
98 dai->id = pdev->id;
99 dai->symmetric_rates = 1;
100 dai->playback.channels_min = 2;
101 dai->playback.channels_max = 2;
102 dai->playback.rates = S3C64XX_I2S_RATES;
103 dai->playback.formats = S3C64XX_I2S_FMTS;
104 dai->capture.channels_min = 2;
105 dai->capture.channels_max = 2;
106 dai->capture.rates = S3C64XX_I2S_RATES;
107 dai->capture.formats = S3C64XX_I2S_FMTS;
108 dai->probe = s3c64xx_i2s_probe;
109 dai->ops = &s3c64xx_i2s_dai_ops;
110
111 i2s->feature |= S3C_FEATURE_CDCLKCON;
112 157
113 i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id]; 158 i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id];
114 i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id]; 159 i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id];
115 160
116 if (pdev->id == 0) { 161 res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
117 i2s->dma_capture->channel = DMACH_I2S0_IN; 162 if (!res) {
118 i2s->dma_capture->dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISRXD; 163 dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n");
119 i2s->dma_playback->channel = DMACH_I2S0_OUT; 164 return -ENXIO;
120 i2s->dma_playback->dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISTXD; 165 }
121 } else { 166 i2s->dma_playback->channel = res->start;
122 i2s->dma_capture->channel = DMACH_I2S1_IN; 167
123 i2s->dma_capture->dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISRXD; 168 res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
124 i2s->dma_playback->channel = DMACH_I2S1_OUT; 169 if (!res) {
125 i2s->dma_playback->dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISTXD; 170 dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n");
171 return -ENXIO;
172 }
173 i2s->dma_capture->channel = res->start;
174
175 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
176 if (!res) {
177 dev_err(&pdev->dev, "Unable to get I2S SFR address\n");
178 return -ENXIO;
126 } 179 }
127 180
181 if (!request_mem_region(res->start, resource_size(res),
182 "s3c64xx-i2s")) {
183 dev_err(&pdev->dev, "Unable to request SFR region\n");
184 return -EBUSY;
185 }
186 i2s->base = res->start;
187
188 i2s_pdata = pdev->dev.platform_data;
189 if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
190 dev_err(&pdev->dev, "Unable to configure gpio\n");
191 return -EINVAL;
192 }
193 i2s->dma_capture->dma_addr = res->start + S3C2412_IISRXD;
194 i2s->dma_playback->dma_addr = res->start + S3C2412_IISTXD;
195
128 i2s->dma_capture->client = &s3c64xx_dma_client_in; 196 i2s->dma_capture->client = &s3c64xx_dma_client_in;
129 i2s->dma_capture->dma_size = 4; 197 i2s->dma_capture->dma_size = 4;
130 i2s->dma_playback->client = &s3c64xx_dma_client_out; 198 i2s->dma_playback->client = &s3c64xx_dma_client_out;
131 i2s->dma_playback->dma_size = 4; 199 i2s->dma_playback->dma_size = 4;
132 200
133 i2s->iis_cclk = clk_get(&pdev->dev, "audio-bus"); 201 for (i = 0; i < ARRAY_SIZE(s3c64xx_i2s_dai); i++) {
134 if (IS_ERR(i2s->iis_cclk)) { 202 ret = s3c_i2sv2_register_dai(&pdev->dev, i,
135 dev_err(&pdev->dev, "failed to get audio-bus\n"); 203 &s3c64xx_i2s_dai[i]);
136 ret = PTR_ERR(i2s->iis_cclk); 204 if (ret != 0)
137 goto err; 205 return ret;
138 } 206 }
139 207
140 clk_enable(i2s->iis_cclk);
141
142 ret = s3c_i2sv2_probe(pdev, dai, i2s, 0);
143 if (ret)
144 goto err_clk;
145
146 ret = s3c_i2sv2_register_dai(dai);
147 if (ret != 0)
148 goto err_i2sv2;
149
150 return 0; 208 return 0;
151
152err_i2sv2:
153 /* Not implemented for I2Sv2 core yet */
154err_clk:
155 clk_put(i2s->iis_cclk);
156err:
157 return ret;
158} 209}
159 210
160static __devexit int s3c64xx_iis_dev_remove(struct platform_device *pdev) 211static __devexit int s3c64xx_iis_dev_remove(struct platform_device *pdev)
161{ 212{
162 dev_err(&pdev->dev, "Device removal not yet supported\n"); 213 snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(s3c64xx_i2s_dai));
163 return 0; 214 return 0;
164} 215}
165 216
@@ -188,3 +239,4 @@ module_exit(s3c64xx_i2s_exit);
188MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); 239MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
189MODULE_DESCRIPTION("S3C64XX I2S SoC Interface"); 240MODULE_DESCRIPTION("S3C64XX I2S SoC Interface");
190MODULE_LICENSE("GPL"); 241MODULE_LICENSE("GPL");
242MODULE_ALIAS("platform:s3c64xx-iis");