Commit | Line | Data |
---|---|---|
f8cf8176 BD |
1 | /* sound/soc/s3c24xx/s3c64xx-i2s.c |
2 | * | |
3 | * ALSA SoC Audio Layer - S3C64XX I2S driver | |
4 | * | |
5 | * Copyright 2008 Openmoko, Inc. | |
6 | * Copyright 2008 Simtec Electronics | |
7 | * Ben Dooks <ben@simtec.co.uk> | |
8 | * http://armlinux.simtec.co.uk/ | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | ||
f0fba2ad | 15 | #include <linux/module.h> |
f8cf8176 | 16 | #include <linux/clk.h> |
f8cf8176 BD |
17 | #include <linux/gpio.h> |
18 | #include <linux/io.h> | |
f0fba2ad | 19 | #include <linux/slab.h> |
f8cf8176 | 20 | |
f8cf8176 BD |
21 | #include <sound/soc.h> |
22 | ||
f0fba2ad | 23 | #include <plat/audio.h> |
f8cf8176 BD |
24 | |
25 | #include <mach/map.h> | |
26 | #include <mach/dma.h> | |
27 | ||
d3ff5a3e | 28 | #include "s3c-dma.h" |
d07e7ce9 | 29 | #include "regs-i2s-v2.h" |
f8cf8176 BD |
30 | #include "s3c64xx-i2s.h" |
31 | ||
0fe69229 JB |
32 | /* The value should be set to maximum of the total number |
33 | * of I2Sv3 controllers that any supported SoC has. | |
34 | */ | |
35 | #define MAX_I2SV3 2 | |
36 | ||
f8cf8176 BD |
37 | static struct s3c2410_dma_client s3c64xx_dma_client_out = { |
38 | .name = "I2S PCM Stereo out" | |
39 | }; | |
40 | ||
41 | static struct s3c2410_dma_client s3c64xx_dma_client_in = { | |
42 | .name = "I2S PCM Stereo in" | |
43 | }; | |
44 | ||
0fe69229 JB |
45 | static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[MAX_I2SV3]; |
46 | static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[MAX_I2SV3]; | |
47 | static struct s3c_i2sv2_info s3c64xx_i2s[MAX_I2SV3]; | |
f8cf8176 | 48 | |
f0fba2ad | 49 | struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai) |
f8cf8176 | 50 | { |
f0fba2ad LG |
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; | |
f8cf8176 | 58 | } |
f0fba2ad | 59 | EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock); |
f8cf8176 | 60 | |
f0fba2ad | 61 | static int s3c64xx_i2s_probe(struct snd_soc_dai *dai) |
f8cf8176 | 62 | { |
f0fba2ad LG |
63 | struct s3c_i2sv2_info *i2s; |
64 | int ret; | |
65 | ||
66 | if (dai->id >= MAX_I2SV3) { | |
67 | dev_err(dai->dev, "id %d out of range\n", dai->id); | |
68 | return -EINVAL; | |
69 | } | |
70 | ||
71 | i2s = &s3c64xx_i2s[dai->id]; | |
72 | snd_soc_dai_set_drvdata(dai, i2s); | |
73 | ||
74 | i2s->iis_cclk = clk_get(dai->dev, "audio-bus"); | |
75 | if (IS_ERR(i2s->iis_cclk)) { | |
76 | dev_err(dai->dev, "failed to get audio-bus\n"); | |
77 | ret = PTR_ERR(i2s->iis_cclk); | |
78 | goto err; | |
f8cf8176 BD |
79 | } |
80 | ||
f0fba2ad LG |
81 | clk_enable(i2s->iis_cclk); |
82 | ||
83 | ret = s3c_i2sv2_probe(dai, i2s, i2s->base); | |
84 | if (ret) | |
85 | goto err_clk; | |
86 | ||
f8cf8176 | 87 | return 0; |
f0fba2ad LG |
88 | |
89 | err_clk: | |
90 | clk_disable(i2s->iis_cclk); | |
91 | clk_put(i2s->iis_cclk); | |
92 | err: | |
93 | kfree(i2s); | |
94 | return ret; | |
f8cf8176 BD |
95 | } |
96 | ||
f0fba2ad LG |
97 | static 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 | } | |
f8cf8176 | 106 | |
8a7c2518 | 107 | static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops; |
f2a5d6a2 | 108 | |
f0fba2ad LG |
109 | static 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 | ||
172fd9e2 MB |
144 | static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev) |
145 | { | |
f0fba2ad | 146 | struct s3c_audio_pdata *i2s_pdata; |
172fd9e2 | 147 | struct s3c_i2sv2_info *i2s; |
f0fba2ad LG |
148 | struct resource *res; |
149 | int i, ret; | |
172fd9e2 | 150 | |
0fe69229 | 151 | if (pdev->id >= MAX_I2SV3) { |
172fd9e2 MB |
152 | dev_err(&pdev->dev, "id %d out of range\n", pdev->id); |
153 | return -EINVAL; | |
154 | } | |
155 | ||
156 | i2s = &s3c64xx_i2s[pdev->id]; | |
8a7c2518 | 157 | |
172fd9e2 MB |
158 | i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id]; |
159 | i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id]; | |
160 | ||
f0fba2ad LG |
161 | res = platform_get_resource(pdev, IORESOURCE_DMA, 0); |
162 | if (!res) { | |
163 | dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n"); | |
164 | return -ENXIO; | |
165 | } | |
166 | i2s->dma_playback->channel = res->start; | |
167 | ||
168 | res = platform_get_resource(pdev, IORESOURCE_DMA, 1); | |
169 | if (!res) { | |
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; | |
0fe69229 JB |
179 | } |
180 | ||
f0fba2ad LG |
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 | ||
0fe69229 JB |
196 | i2s->dma_capture->client = &s3c64xx_dma_client_in; |
197 | i2s->dma_capture->dma_size = 4; | |
198 | i2s->dma_playback->client = &s3c64xx_dma_client_out; | |
199 | i2s->dma_playback->dma_size = 4; | |
200 | ||
f0fba2ad LG |
201 | for (i = 0; i < ARRAY_SIZE(s3c64xx_i2s_dai); i++) { |
202 | ret = s3c_i2sv2_register_dai(&pdev->dev, i, | |
203 | &s3c64xx_i2s_dai[i]); | |
204 | if (ret != 0) | |
205 | return ret; | |
172fd9e2 MB |
206 | } |
207 | ||
172fd9e2 | 208 | return 0; |
172fd9e2 MB |
209 | } |
210 | ||
211 | static __devexit int s3c64xx_iis_dev_remove(struct platform_device *pdev) | |
212 | { | |
f0fba2ad | 213 | snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(s3c64xx_i2s_dai)); |
172fd9e2 MB |
214 | return 0; |
215 | } | |
216 | ||
217 | static struct platform_driver s3c64xx_iis_driver = { | |
218 | .probe = s3c64xx_iis_dev_probe, | |
219 | .remove = s3c64xx_iis_dev_remove, | |
220 | .driver = { | |
221 | .name = "s3c64xx-iis", | |
222 | .owner = THIS_MODULE, | |
223 | }, | |
224 | }; | |
225 | ||
f8cf8176 BD |
226 | static int __init s3c64xx_i2s_init(void) |
227 | { | |
172fd9e2 | 228 | return platform_driver_register(&s3c64xx_iis_driver); |
f8cf8176 BD |
229 | } |
230 | module_init(s3c64xx_i2s_init); | |
231 | ||
232 | static void __exit s3c64xx_i2s_exit(void) | |
233 | { | |
172fd9e2 | 234 | platform_driver_unregister(&s3c64xx_iis_driver); |
f8cf8176 BD |
235 | } |
236 | module_exit(s3c64xx_i2s_exit); | |
237 | ||
238 | /* Module information */ | |
239 | MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); | |
240 | MODULE_DESCRIPTION("S3C64XX I2S SoC Interface"); | |
241 | MODULE_LICENSE("GPL"); | |
960d0697 | 242 | MODULE_ALIAS("platform:s3c64xx-iis"); |