2 * s3c2443-ac97.c -- ALSA Soc Audio Layer
4 * (c) 2007 Wolfson Microelectronics PLC.
5 * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
7 * Copyright (C) 2005, Sean Choi <sh428.choi@samsung.com>
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.
15 #include <linux/init.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/interrupt.h>
20 #include <linux/wait.h>
21 #include <linux/delay.h>
22 #include <linux/clk.h>
24 #include <sound/core.h>
25 #include <sound/pcm.h>
26 #include <sound/ac97_codec.h>
27 #include <sound/initval.h>
28 #include <sound/soc.h>
30 #include <mach/hardware.h>
31 #include <plat/regs-ac97.h>
32 #include <mach/regs-gpio.h>
33 #include <mach/regs-clock.h>
34 #include <mach/audio.h>
38 #include "s3c24xx-pcm.h"
39 #include "s3c24xx-ac97.h"
41 struct s3c24xx_ac97_info
{
45 static struct s3c24xx_ac97_info s3c24xx_ac97
;
47 static DECLARE_COMPLETION(ac97_completion
);
48 static u32 codec_ready
;
49 static DECLARE_MUTEX(ac97_mutex
);
51 static unsigned short s3c2443_ac97_read(struct snd_ac97
*ac97
,
60 codec_ready
= S3C_AC97_GLBSTAT_CODECREADY
;
61 ac_codec_cmd
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_CODEC_CMD
);
62 ac_codec_cmd
= S3C_AC97_CODEC_CMD_READ
| AC_CMD_ADDR(reg
);
63 writel(ac_codec_cmd
, s3c24xx_ac97
.regs
+ S3C_AC97_CODEC_CMD
);
67 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
68 ac_glbctrl
|= S3C_AC97_GLBCTRL_CODECREADYIE
;
69 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
71 wait_for_completion(&ac97_completion
);
73 stat
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_STAT
);
74 addr
= (stat
>> 16) & 0x7f;
75 data
= (stat
& 0xffff);
78 printk(KERN_ERR
"s3c24xx-ac97: req addr = %02x,"
79 " rep addr = %02x\n", reg
, addr
);
83 return (unsigned short)data
;
86 static void s3c2443_ac97_write(struct snd_ac97
*ac97
, unsigned short reg
,
94 codec_ready
= S3C_AC97_GLBSTAT_CODECREADY
;
95 ac_codec_cmd
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_CODEC_CMD
);
96 ac_codec_cmd
= AC_CMD_ADDR(reg
) | AC_CMD_DATA(val
);
97 writel(ac_codec_cmd
, s3c24xx_ac97
.regs
+ S3C_AC97_CODEC_CMD
);
101 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
102 ac_glbctrl
|= S3C_AC97_GLBCTRL_CODECREADYIE
;
103 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
105 wait_for_completion(&ac97_completion
);
107 ac_codec_cmd
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_CODEC_CMD
);
108 ac_codec_cmd
|= S3C_AC97_CODEC_CMD_READ
;
109 writel(ac_codec_cmd
, s3c24xx_ac97
.regs
+ S3C_AC97_CODEC_CMD
);
115 static void s3c2443_ac97_warm_reset(struct snd_ac97
*ac97
)
119 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
120 ac_glbctrl
= S3C_AC97_GLBCTRL_WARMRESET
;
121 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
125 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
129 static void s3c2443_ac97_cold_reset(struct snd_ac97
*ac97
)
133 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
134 ac_glbctrl
= S3C_AC97_GLBCTRL_COLDRESET
;
135 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
139 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
142 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
143 ac_glbctrl
= S3C_AC97_GLBCTRL_ACLINKON
;
144 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
147 ac_glbctrl
|= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE
;
148 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
151 ac_glbctrl
|= S3C_AC97_GLBCTRL_PCMOUTTM_DMA
|
152 S3C_AC97_GLBCTRL_PCMINTM_DMA
| S3C_AC97_GLBCTRL_MICINTM_DMA
;
153 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
156 static irqreturn_t
s3c2443_ac97_irq(int irq
, void *dev_id
)
161 status
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBSTAT
) & codec_ready
;
164 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
165 ac_glbctrl
&= ~S3C_AC97_GLBCTRL_CODECREADYIE
;
166 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
167 complete(&ac97_completion
);
172 struct snd_ac97_bus_ops soc_ac97_ops
= {
173 .read
= s3c2443_ac97_read
,
174 .write
= s3c2443_ac97_write
,
175 .warm_reset
= s3c2443_ac97_warm_reset
,
176 .reset
= s3c2443_ac97_cold_reset
,
179 static struct s3c2410_dma_client s3c2443_dma_client_out
= {
180 .name
= "AC97 PCM Stereo out"
183 static struct s3c2410_dma_client s3c2443_dma_client_in
= {
184 .name
= "AC97 PCM Stereo in"
187 static struct s3c2410_dma_client s3c2443_dma_client_micin
= {
188 .name
= "AC97 Mic Mono in"
191 static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out
= {
192 .client
= &s3c2443_dma_client_out
,
193 .channel
= DMACH_PCM_OUT
,
194 .dma_addr
= S3C2440_PA_AC97
+ S3C_AC97_PCM_DATA
,
198 static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in
= {
199 .client
= &s3c2443_dma_client_in
,
200 .channel
= DMACH_PCM_IN
,
201 .dma_addr
= S3C2440_PA_AC97
+ S3C_AC97_PCM_DATA
,
205 static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in
= {
206 .client
= &s3c2443_dma_client_micin
,
207 .channel
= DMACH_MIC_IN
,
208 .dma_addr
= S3C2440_PA_AC97
+ S3C_AC97_MIC_DATA
,
212 static int s3c2443_ac97_probe(struct platform_device
*pdev
,
213 struct snd_soc_dai
*dai
)
218 s3c24xx_ac97
.regs
= ioremap(S3C2440_PA_AC97
, 0x100);
219 if (s3c24xx_ac97
.regs
== NULL
)
222 s3c24xx_ac97
.ac97_clk
= clk_get(&pdev
->dev
, "ac97");
223 if (s3c24xx_ac97
.ac97_clk
== NULL
) {
224 printk(KERN_ERR
"s3c2443-ac97 failed to get ac97_clock\n");
225 iounmap(s3c24xx_ac97
.regs
);
228 clk_enable(s3c24xx_ac97
.ac97_clk
);
230 s3c2410_gpio_cfgpin(S3C2410_GPE0
, S3C2443_GPE0_AC_nRESET
);
231 s3c2410_gpio_cfgpin(S3C2410_GPE1
, S3C2443_GPE1_AC_SYNC
);
232 s3c2410_gpio_cfgpin(S3C2410_GPE2
, S3C2443_GPE2_AC_BITCLK
);
233 s3c2410_gpio_cfgpin(S3C2410_GPE3
, S3C2443_GPE3_AC_SDI
);
234 s3c2410_gpio_cfgpin(S3C2410_GPE4
, S3C2443_GPE4_AC_SDO
);
236 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
237 ac_glbctrl
= S3C_AC97_GLBCTRL_COLDRESET
;
238 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
242 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
245 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
246 ac_glbctrl
= S3C_AC97_GLBCTRL_ACLINKON
;
247 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
250 ac_glbctrl
|= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE
;
251 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
253 ret
= request_irq(IRQ_S3C244x_AC97
, s3c2443_ac97_irq
,
254 IRQF_DISABLED
, "AC97", NULL
);
256 printk(KERN_ERR
"s3c24xx-ac97: interrupt request failed.\n");
257 clk_disable(s3c24xx_ac97
.ac97_clk
);
258 clk_put(s3c24xx_ac97
.ac97_clk
);
259 iounmap(s3c24xx_ac97
.regs
);
264 static void s3c2443_ac97_remove(struct platform_device
*pdev
,
265 struct snd_soc_dai
*dai
)
267 free_irq(IRQ_S3C244x_AC97
, NULL
);
268 clk_disable(s3c24xx_ac97
.ac97_clk
);
269 clk_put(s3c24xx_ac97
.ac97_clk
);
270 iounmap(s3c24xx_ac97
.regs
);
273 static int s3c2443_ac97_hw_params(struct snd_pcm_substream
*substream
,
274 struct snd_pcm_hw_params
*params
,
275 struct snd_soc_dai
*dai
)
277 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
278 struct snd_soc_dai
*cpu_dai
= rtd
->dai
->cpu_dai
;
280 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
281 cpu_dai
->dma_data
= &s3c2443_ac97_pcm_stereo_out
;
283 cpu_dai
->dma_data
= &s3c2443_ac97_pcm_stereo_in
;
288 static int s3c2443_ac97_trigger(struct snd_pcm_substream
*substream
, int cmd
,
289 struct snd_soc_dai
*dai
)
293 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
295 case SNDRV_PCM_TRIGGER_START
:
296 case SNDRV_PCM_TRIGGER_RESUME
:
297 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
298 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
)
299 ac_glbctrl
|= S3C_AC97_GLBCTRL_PCMINTM_DMA
;
301 ac_glbctrl
|= S3C_AC97_GLBCTRL_PCMOUTTM_DMA
;
303 case SNDRV_PCM_TRIGGER_STOP
:
304 case SNDRV_PCM_TRIGGER_SUSPEND
:
305 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
306 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
)
307 ac_glbctrl
&= ~S3C_AC97_GLBCTRL_PCMINTM_MASK
;
309 ac_glbctrl
&= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK
;
312 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
317 static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream
*substream
,
318 struct snd_pcm_hw_params
*params
,
319 struct snd_soc_dai
*dai
)
321 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
322 struct snd_soc_dai
*cpu_dai
= rtd
->dai
->cpu_dai
;
324 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
327 cpu_dai
->dma_data
= &s3c2443_ac97_mic_mono_in
;
332 static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream
*substream
,
333 int cmd
, struct snd_soc_dai
*dai
)
337 ac_glbctrl
= readl(s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
339 case SNDRV_PCM_TRIGGER_START
:
340 case SNDRV_PCM_TRIGGER_RESUME
:
341 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
342 ac_glbctrl
|= S3C_AC97_GLBCTRL_PCMINTM_DMA
;
344 case SNDRV_PCM_TRIGGER_STOP
:
345 case SNDRV_PCM_TRIGGER_SUSPEND
:
346 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
347 ac_glbctrl
&= ~S3C_AC97_GLBCTRL_PCMINTM_MASK
;
349 writel(ac_glbctrl
, s3c24xx_ac97
.regs
+ S3C_AC97_GLBCTRL
);
354 #define s3c2443_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
355 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
356 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
358 struct snd_soc_dai s3c2443_ac97_dai
[] = {
360 .name
= "s3c2443-ac97",
363 .probe
= s3c2443_ac97_probe
,
364 .remove
= s3c2443_ac97_remove
,
366 .stream_name
= "AC97 Playback",
369 .rates
= s3c2443_AC97_RATES
,
370 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,},
372 .stream_name
= "AC97 Capture",
375 .rates
= s3c2443_AC97_RATES
,
376 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,},
378 .hw_params
= s3c2443_ac97_hw_params
,
379 .trigger
= s3c2443_ac97_trigger
},
382 .name
= "pxa2xx-ac97-mic",
386 .stream_name
= "AC97 Mic Capture",
389 .rates
= s3c2443_AC97_RATES
,
390 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,},
392 .hw_params
= s3c2443_ac97_hw_mic_params
,
393 .trigger
= s3c2443_ac97_mic_trigger
,},
396 EXPORT_SYMBOL_GPL(s3c2443_ac97_dai
);
397 EXPORT_SYMBOL_GPL(soc_ac97_ops
);
399 static int __init
s3c2443_ac97_init(void)
401 return snd_soc_register_dais(s3c2443_ac97_dai
,
402 ARRAY_SIZE(s3c2443_ac97_dai
));
404 module_init(s3c2443_ac97_init
);
406 static void __exit
s3c2443_ac97_exit(void)
408 snd_soc_unregister_dais(s3c2443_ac97_dai
,
409 ARRAY_SIZE(s3c2443_ac97_dai
));
411 module_exit(s3c2443_ac97_exit
);
414 MODULE_AUTHOR("Graeme Gregory");
415 MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
416 MODULE_LICENSE("GPL");