• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * s3c2443-ac97.c  --  ALSA Soc Audio Layer
3  *
4  * (c) 2007 Wolfson Microelectronics PLC.
5  * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
6  *
7  *  Copyright (C) 2005, Sean Choi <sh428.choi@samsung.com>
8  *  All rights reserved.
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 
15 #include <linux/init.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/interrupt.h>
19 #include <linux/io.h>
20 #include <linux/wait.h>
21 #include <linux/delay.h>
22 #include <linux/clk.h>
23 
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>
29 
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>
35 #include <asm/dma.h>
36 #include <mach/dma.h>
37 
38 #include "s3c24xx-pcm.h"
39 #include "s3c24xx-ac97.h"
40 
41 struct s3c24xx_ac97_info {
42 	void __iomem	*regs;
43 	struct clk	*ac97_clk;
44 };
45 static struct s3c24xx_ac97_info s3c24xx_ac97;
46 
47 static DECLARE_COMPLETION(ac97_completion);
48 static u32 codec_ready;
49 static DECLARE_MUTEX(ac97_mutex);
50 
s3c2443_ac97_read(struct snd_ac97 * ac97,unsigned short reg)51 static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
52 	unsigned short reg)
53 {
54 	u32 ac_glbctrl;
55 	u32 ac_codec_cmd;
56 	u32 stat, addr, data;
57 
58 	down(&ac97_mutex);
59 
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);
64 
65 	udelay(50);
66 
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);
70 
71 	wait_for_completion(&ac97_completion);
72 
73 	stat = readl(s3c24xx_ac97.regs + S3C_AC97_STAT);
74 	addr = (stat >> 16) & 0x7f;
75 	data = (stat & 0xffff);
76 
77 	if (addr != reg)
78 		printk(KERN_ERR "s3c24xx-ac97: req addr = %02x,"
79 				" rep addr = %02x\n", reg, addr);
80 
81 	up(&ac97_mutex);
82 
83 	return (unsigned short)data;
84 }
85 
s3c2443_ac97_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short val)86 static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
87 	unsigned short val)
88 {
89 	u32 ac_glbctrl;
90 	u32 ac_codec_cmd;
91 
92 	down(&ac97_mutex);
93 
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);
98 
99 	udelay(50);
100 
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);
104 
105 	wait_for_completion(&ac97_completion);
106 
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);
110 
111 	up(&ac97_mutex);
112 
113 }
114 
s3c2443_ac97_warm_reset(struct snd_ac97 * ac97)115 static void s3c2443_ac97_warm_reset(struct snd_ac97 *ac97)
116 {
117 	u32 ac_glbctrl;
118 
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);
122 	msleep(1);
123 
124 	ac_glbctrl = 0;
125 	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
126 	msleep(1);
127 }
128 
s3c2443_ac97_cold_reset(struct snd_ac97 * ac97)129 static void s3c2443_ac97_cold_reset(struct snd_ac97 *ac97)
130 {
131 	u32 ac_glbctrl;
132 
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);
136 	msleep(1);
137 
138 	ac_glbctrl = 0;
139 	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
140 	msleep(1);
141 
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);
145 	msleep(1);
146 
147 	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
148 	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
149 	msleep(1);
150 
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);
154 }
155 
s3c2443_ac97_irq(int irq,void * dev_id)156 static irqreturn_t s3c2443_ac97_irq(int irq, void *dev_id)
157 {
158 	int status;
159 	u32 ac_glbctrl;
160 
161 	status = readl(s3c24xx_ac97.regs + S3C_AC97_GLBSTAT) & codec_ready;
162 
163 	if (status) {
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);
168 	}
169 	return IRQ_HANDLED;
170 }
171 
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,
177 };
178 
179 static struct s3c2410_dma_client s3c2443_dma_client_out = {
180 	.name = "AC97 PCM Stereo out"
181 };
182 
183 static struct s3c2410_dma_client s3c2443_dma_client_in = {
184 	.name = "AC97 PCM Stereo in"
185 };
186 
187 static struct s3c2410_dma_client s3c2443_dma_client_micin = {
188 	.name = "AC97 Mic Mono in"
189 };
190 
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,
195 	.dma_size	= 4,
196 };
197 
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,
202 	.dma_size	= 4,
203 };
204 
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,
209 	.dma_size	= 4,
210 };
211 
s3c2443_ac97_probe(struct platform_device * pdev,struct snd_soc_dai * dai)212 static int s3c2443_ac97_probe(struct platform_device *pdev,
213 			      struct snd_soc_dai *dai)
214 {
215 	int ret;
216 	u32 ac_glbctrl;
217 
218 	s3c24xx_ac97.regs = ioremap(S3C2440_PA_AC97, 0x100);
219 	if (s3c24xx_ac97.regs == NULL)
220 		return -ENXIO;
221 
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);
226 		return -ENODEV;
227 	}
228 	clk_enable(s3c24xx_ac97.ac97_clk);
229 
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);
235 
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);
239 	msleep(1);
240 
241 	ac_glbctrl = 0;
242 	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
243 	msleep(1);
244 
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);
248 	msleep(1);
249 
250 	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
251 	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
252 
253 	ret = request_irq(IRQ_S3C244x_AC97, s3c2443_ac97_irq,
254 		IRQF_DISABLED, "AC97", NULL);
255 	if (ret < 0) {
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);
260 	}
261 	return ret;
262 }
263 
s3c2443_ac97_remove(struct platform_device * pdev,struct snd_soc_dai * dai)264 static void s3c2443_ac97_remove(struct platform_device *pdev,
265 				struct snd_soc_dai *dai)
266 {
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);
271 }
272 
s3c2443_ac97_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)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)
276 {
277 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
278 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
279 
280 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
281 		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
282 	else
283 		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_in;
284 
285 	return 0;
286 }
287 
s3c2443_ac97_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)288 static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
289 				struct snd_soc_dai *dai)
290 {
291 	u32 ac_glbctrl;
292 
293 	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
294 	switch (cmd) {
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;
300 		else
301 			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
302 		break;
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;
308 		else
309 			ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
310 		break;
311 	}
312 	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
313 
314 	return 0;
315 }
316 
s3c2443_ac97_hw_mic_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)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)
320 {
321 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
322 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
323 
324 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
325 		return -ENODEV;
326 	else
327 		cpu_dai->dma_data = &s3c2443_ac97_mic_mono_in;
328 
329 	return 0;
330 }
331 
s3c2443_ac97_mic_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)332 static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
333 				    int cmd, struct snd_soc_dai *dai)
334 {
335 	u32 ac_glbctrl;
336 
337 	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
338 	switch (cmd) {
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;
343 		break;
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;
348 	}
349 	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
350 
351 	return 0;
352 }
353 
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)
357 
358 struct snd_soc_dai s3c2443_ac97_dai[] = {
359 {
360 	.name = "s3c2443-ac97",
361 	.id = 0,
362 	.ac97_control = 1,
363 	.probe = s3c2443_ac97_probe,
364 	.remove = s3c2443_ac97_remove,
365 	.playback = {
366 		.stream_name = "AC97 Playback",
367 		.channels_min = 2,
368 		.channels_max = 2,
369 		.rates = s3c2443_AC97_RATES,
370 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
371 	.capture = {
372 		.stream_name = "AC97 Capture",
373 		.channels_min = 2,
374 		.channels_max = 2,
375 		.rates = s3c2443_AC97_RATES,
376 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
377 	.ops = {
378 		.hw_params = s3c2443_ac97_hw_params,
379 		.trigger = s3c2443_ac97_trigger},
380 },
381 {
382 	.name = "pxa2xx-ac97-mic",
383 	.id = 1,
384 	.ac97_control = 1,
385 	.capture = {
386 		.stream_name = "AC97 Mic Capture",
387 		.channels_min = 1,
388 		.channels_max = 1,
389 		.rates = s3c2443_AC97_RATES,
390 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
391 	.ops = {
392 		.hw_params = s3c2443_ac97_hw_mic_params,
393 		.trigger = s3c2443_ac97_mic_trigger,},
394 },
395 };
396 EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
397 EXPORT_SYMBOL_GPL(soc_ac97_ops);
398 
s3c2443_ac97_init(void)399 static int __init s3c2443_ac97_init(void)
400 {
401 	return snd_soc_register_dais(s3c2443_ac97_dai,
402 				     ARRAY_SIZE(s3c2443_ac97_dai));
403 }
404 module_init(s3c2443_ac97_init);
405 
s3c2443_ac97_exit(void)406 static void __exit s3c2443_ac97_exit(void)
407 {
408 	snd_soc_unregister_dais(s3c2443_ac97_dai,
409 				ARRAY_SIZE(s3c2443_ac97_dai));
410 }
411 module_exit(s3c2443_ac97_exit);
412 
413 
414 MODULE_AUTHOR("Graeme Gregory");
415 MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
416 MODULE_LICENSE("GPL");
417