aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/samsung
diff options
context:
space:
mode:
authorJassi Brar <jassi.brar@samsung.com>2010-11-22 01:37:25 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-11-23 09:13:04 -0500
commit5033f43c66754296dfb0ac5c895208e4a7f93aac (patch)
tree343b6a07674afb35e026191c97bc6bc2f329f5bc /sound/soc/samsung
parenta964f34d8b516633d471191156963bae0d1bf730 (diff)
ASoC: Samsung: Rename from s3c24xx to samsung
Finally, move the 's3c24xx' directory to 'samsung' Signed-off-by: Jassi Brar <jassi.brar@samsung.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/samsung')
-rw-r--r--sound/soc/samsung/Kconfig155
-rw-r--r--sound/soc/samsung/Makefile51
-rw-r--r--sound/soc/samsung/ac97.c520
-rw-r--r--sound/soc/samsung/ac97.h21
-rw-r--r--sound/soc/samsung/dma.c502
-rw-r--r--sound/soc/samsung/dma.h30
-rw-r--r--sound/soc/samsung/goni_wm8994.c309
-rw-r--r--sound/soc/samsung/i2s.c1256
-rw-r--r--sound/soc/samsung/i2s.h29
-rw-r--r--sound/soc/samsung/jive_wm8750.c191
-rw-r--r--sound/soc/samsung/lm4857.h32
-rw-r--r--sound/soc/samsung/ln2440sbc_alc650.c77
-rw-r--r--sound/soc/samsung/neo1973_gta02_wm8753.c504
-rw-r--r--sound/soc/samsung/neo1973_wm8753.c706
-rw-r--r--sound/soc/samsung/pcm.c552
-rw-r--r--sound/soc/samsung/pcm.h124
-rw-r--r--sound/soc/samsung/regs-i2s-v2.h115
-rw-r--r--sound/soc/samsung/rx1950_uda1380.c319
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.c757
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.h106
-rw-r--r--sound/soc/samsung/s3c2412-i2s.c212
-rw-r--r--sound/soc/samsung/s3c2412-i2s.h27
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c519
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.h35
-rw-r--r--sound/soc/samsung/s3c24xx_simtec.c394
-rw-r--r--sound/soc/samsung/s3c24xx_simtec.h22
-rw-r--r--sound/soc/samsung/s3c24xx_simtec_hermes.c146
-rw-r--r--sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c134
-rw-r--r--sound/soc/samsung/s3c24xx_uda134x.c367
-rw-r--r--sound/soc/samsung/smartq_wm8987.c288
-rw-r--r--sound/soc/samsung/smdk2443_wm9710.c73
-rw-r--r--sound/soc/samsung/smdk_spdif.c223
-rw-r--r--sound/soc/samsung/smdk_wm8580.c285
-rw-r--r--sound/soc/samsung/smdk_wm9713.c107
-rw-r--r--sound/soc/samsung/spdif.c501
-rw-r--r--sound/soc/samsung/spdif.h19
36 files changed, 9708 insertions, 0 deletions
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
new file mode 100644
index 000000000000..eb45cf90d4e2
--- /dev/null
+++ b/sound/soc/samsung/Kconfig
@@ -0,0 +1,155 @@
1config ASOC_SAMSUNG
2 tristate "ASoC support for Samsung"
3 depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210
4 select S3C64XX_DMA if ARCH_S3C64XX
5 select S3C2410_DMA if ARCH_S3C2410
6 help
7 Say Y or M if you want to add support for codecs attached to
8 the Samsung SoCs' Audio interfaces. You will also need to
9 select the audio interfaces to support below.
10
11config SND_S3C24XX_I2S
12 tristate
13 select S3C2410_DMA
14
15config SND_S3C_I2SV2_SOC
16 tristate
17
18config SND_S3C2412_SOC_I2S
19 tristate
20 select SND_S3C_I2SV2_SOC
21 select S3C2410_DMA
22
23config SND_SAMSUNG_PCM
24 tristate
25
26config SND_SAMSUNG_AC97
27 tristate
28 select SND_SOC_AC97_BUS
29
30config SND_SAMSUNG_SPDIF
31 tristate
32 select SND_SOC_SPDIF
33
34config SND_SAMSUNG_I2S
35 tristate
36
37config ASOC_SAMSUNG_NEO1973_WM8753
38 tristate "SoC I2S Audio support for NEO1973 - WM8753"
39 depends on ASOC_SAMSUNG && MACH_NEO1973_GTA01
40 select SND_S3C24XX_I2S
41 select SND_SOC_WM8753
42 help
43 Say Y if you want to add support for SoC audio on smdk2440
44 with the WM8753.
45
46config ASOC_SAMSUNG_NEO1973_GTA02_WM8753
47 tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)"
48 depends on ASOC_SAMSUNG && MACH_NEO1973_GTA02
49 select SND_S3C24XX_I2S
50 select SND_SOC_WM8753
51 help
52 This driver provides audio support for the Openmoko Neo FreeRunner
53 smartphone.
54
55config ASOC_SAMSUNG_JIVE_WM8750
56 tristate "SoC I2S Audio support for Jive"
57 depends on ASOC_SAMSUNG && MACH_JIVE
58 select SND_SOC_WM8750
59 select SND_S3C2412_SOC_I2S
60 help
61 Sat Y if you want to add support for SoC audio on the Jive.
62
63config ASOC_SAMSUNG_SMDK_WM8580
64 tristate "SoC I2S Audio support for WM8580 on SMDK"
65 depends on ASOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100)
66 select SND_SOC_WM8580
67 select SND_SAMSUNG_I2S
68 help
69 Say Y if you want to add support for SoC audio on the SMDKs.
70
71config ASOC_SAMSUNG_SMDK2443_WM9710
72 tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
73 depends on ASOC_SAMSUNG && MACH_SMDK2443
74 select S3C2410_DMA
75 select AC97_BUS
76 select SND_SOC_AC97_CODEC
77 select SND_SAMSUNG_AC97
78 help
79 Say Y if you want to add support for SoC audio on smdk2443
80 with the WM9710.
81
82config ASOC_SAMSUNG_LN2440SBC_ALC650
83 tristate "SoC AC97 Audio support for LN2440SBC - ALC650"
84 depends on ASOC_SAMSUNG && ARCH_S3C2410
85 select S3C2410_DMA
86 select AC97_BUS
87 select SND_SOC_AC97_CODEC
88 select SND_SAMSUNG_AC97
89 help
90 Say Y if you want to add support for SoC audio on ln2440sbc
91 with the ALC650.
92
93config ASOC_SAMSUNG_S3C24XX_UDA134X
94 tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
95 depends on ASOC_SAMSUNG && ARCH_S3C2410
96 select SND_S3C24XX_I2S
97 select SND_SOC_L3
98 select SND_SOC_UDA134X
99
100config ASOC_SAMSUNG_SIMTEC
101 tristate
102 help
103 Internal node for common S3C24XX/Simtec suppor
104
105config ASOC_SAMSUNG_SIMTEC_TLV320AIC23
106 tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards"
107 depends on ASOC_SAMSUNG && ARCH_S3C2410
108 select SND_S3C24XX_I2S
109 select SND_SOC_TLV320AIC23
110 select ASOC_SAMSUNG_SIMTEC
111
112config ASOC_SAMSUNG_SIMTEC_HERMES
113 tristate "SoC I2S Audio support for Simtec Hermes board"
114 depends on ASOC_SAMSUNG && ARCH_S3C2410
115 select SND_S3C24XX_I2S
116 select SND_SOC_TLV320AIC3X
117 select ASOC_SAMSUNG_SIMTEC
118
119config ASOC_SAMSUNG_RX1950_UDA1380
120 tristate "Audio support for the HP iPAQ RX1950"
121 depends on ASOC_SAMSUNG && MACH_RX1950
122 select SND_S3C24XX_I2S
123 select SND_SOC_UDA1380
124 help
125 This driver provides audio support for HP iPAQ RX1950 PDA.
126
127config ASOC_SAMSUNG_SMDK_WM9713
128 tristate "SoC AC97 Audio support for SMDK with WM9713"
129 depends on ASOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110)
130 select SND_SOC_WM9713
131 select SND_SAMSUNG_AC97
132 help
133 Sat Y if you want to add support for SoC audio on the SMDK.
134
135config ASOC_SMARTQ
136 tristate "SoC I2S Audio support for SmartQ board"
137 depends on ASOC_SAMSUNG && MACH_SMARTQ
138 select SND_SAMSUNG_I2S
139 select SND_SOC_WM8750
140
141config ASOC_GONI_AQUILA_WM8994
142 tristate "SoC I2S Audio support for AQUILA/GONI - WM8994"
143 depends on ASOC_SAMSUNG && (MACH_GONI || MACH_AQUILA)
144 select SND_SAMSUNG_I2S
145 select SND_SOC_WM8994
146 help
147 Say Y if you want to add support for SoC audio on goni or aquila
148 with the WM8994.
149
150config ASOC_SAMSUNG_SMDK_SPDIF
151 tristate "SoC S/PDIF Audio support for SMDK"
152 depends on ASOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210)
153 select SND_SAMSUNG_SPDIF
154 help
155 Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
new file mode 100644
index 000000000000..0d24f95c8b1c
--- /dev/null
+++ b/sound/soc/samsung/Makefile
@@ -0,0 +1,51 @@
1# S3c24XX Platform Support
2snd-soc-s3c24xx-objs := dma.o
3snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
4snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
5snd-soc-ac97-objs := ac97.o
6snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
7snd-soc-samsung-spdif-objs := spdif.o
8snd-soc-pcm-objs := pcm.o
9snd-soc-i2s-objs := i2s.o
10
11obj-$(CONFIG_ASOC_SAMSUNG) += snd-soc-s3c24xx.o
12obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o
13obj-$(CONFIG_SND_SAMSUNG_AC97) += snd-soc-ac97.o
14obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
15obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
16obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o
17obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o
18obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o
19
20# S3C24XX Machine Support
21snd-soc-jive-wm8750-objs := jive_wm8750.o
22snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
23snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o
24snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
25snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
26snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
27snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
28snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
29snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
30snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o
31snd-soc-smdk-wm8580-objs := smdk_wm8580.o
32snd-soc-smdk-wm9713-objs := smdk_wm9713.o
33snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
34snd-soc-goni-wm8994-objs := goni_wm8994.o
35snd-soc-smdk-spdif-objs := smdk_spdif.o
36
37obj-$(CONFIG_ASOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
38obj-$(CONFIG_ASOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
39obj-$(CONFIG_ASOC_SAMSUNG_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o
40obj-$(CONFIG_ASOC_SAMSUNG_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
41obj-$(CONFIG_ASOC_SAMSUNG_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
42obj-$(CONFIG_ASOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
43obj-$(CONFIG_ASOC_SAMSUNG_SIMTEC) += snd-soc-s3c24xx-simtec.o
44obj-$(CONFIG_ASOC_SAMSUNG_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
45obj-$(CONFIG_ASOC_SAMSUNG_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
46obj-$(CONFIG_ASOC_SAMSUNG_RX1950_UDA1380) += snd-soc-rx1950-uda1380.o
47obj-$(CONFIG_ASOC_SAMSUNG_SMDK_WM8580) += snd-soc-smdk-wm8580.o
48obj-$(CONFIG_ASOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o
49obj-$(CONFIG_ASOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
50obj-$(CONFIG_ASOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
51obj-$(CONFIG_ASOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
new file mode 100644
index 000000000000..4770a9550341
--- /dev/null
+++ b/sound/soc/samsung/ac97.c
@@ -0,0 +1,520 @@
1/* sound/soc/samsung/ac97.c
2 *
3 * ALSA SoC Audio Layer - S3C AC97 Controller driver
4 * Evolved from s3c2443-ac97.c
5 *
6 * Copyright (c) 2010 Samsung Electronics Co. Ltd
7 * Author: Jaswinder Singh <jassi.brar@samsung.com>
8 * Credits: Graeme Gregory, Sean Choi
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/io.h>
18#include <linux/delay.h>
19#include <linux/clk.h>
20
21#include <sound/soc.h>
22
23#include <plat/regs-ac97.h>
24#include <mach/dma.h>
25#include <plat/audio.h>
26
27#include "dma.h"
28#include "ac97.h"
29
30#define AC_CMD_ADDR(x) (x << 16)
31#define AC_CMD_DATA(x) (x & 0xffff)
32
33struct s3c_ac97_info {
34 struct clk *ac97_clk;
35 void __iomem *regs;
36 struct mutex lock;
37 struct completion done;
38};
39static struct s3c_ac97_info s3c_ac97;
40
41static struct s3c2410_dma_client s3c_dma_client_out = {
42 .name = "AC97 PCMOut"
43};
44
45static struct s3c2410_dma_client s3c_dma_client_in = {
46 .name = "AC97 PCMIn"
47};
48
49static struct s3c2410_dma_client s3c_dma_client_micin = {
50 .name = "AC97 MicIn"
51};
52
53static struct s3c_dma_params s3c_ac97_pcm_out = {
54 .client = &s3c_dma_client_out,
55 .dma_size = 4,
56};
57
58static struct s3c_dma_params s3c_ac97_pcm_in = {
59 .client = &s3c_dma_client_in,
60 .dma_size = 4,
61};
62
63static struct s3c_dma_params s3c_ac97_mic_in = {
64 .client = &s3c_dma_client_micin,
65 .dma_size = 4,
66};
67
68static void s3c_ac97_activate(struct snd_ac97 *ac97)
69{
70 u32 ac_glbctrl, stat;
71
72 stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
73 if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE)
74 return; /* Return if already active */
75
76 INIT_COMPLETION(s3c_ac97.done);
77
78 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
79 ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
80 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
81 msleep(1);
82
83 ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
84 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
85 msleep(1);
86
87 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
88 ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
89 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
90
91 if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
92 pr_err("AC97: Unable to activate!");
93}
94
95static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
96 unsigned short reg)
97{
98 u32 ac_glbctrl, ac_codec_cmd;
99 u32 stat, addr, data;
100
101 mutex_lock(&s3c_ac97.lock);
102
103 s3c_ac97_activate(ac97);
104
105 INIT_COMPLETION(s3c_ac97.done);
106
107 ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
108 ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
109 writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
110
111 udelay(50);
112
113 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
114 ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
115 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
116
117 if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
118 pr_err("AC97: Unable to read!");
119
120 stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
121 addr = (stat >> 16) & 0x7f;
122 data = (stat & 0xffff);
123
124 if (addr != reg)
125 pr_err("ac97: req addr = %02x, rep addr = %02x\n",
126 reg, addr);
127
128 mutex_unlock(&s3c_ac97.lock);
129
130 return (unsigned short)data;
131}
132
133static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
134 unsigned short val)
135{
136 u32 ac_glbctrl, ac_codec_cmd;
137
138 mutex_lock(&s3c_ac97.lock);
139
140 s3c_ac97_activate(ac97);
141
142 INIT_COMPLETION(s3c_ac97.done);
143
144 ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
145 ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
146 writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
147
148 udelay(50);
149
150 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
151 ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
152 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
153
154 if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
155 pr_err("AC97: Unable to write!");
156
157 ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
158 ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
159 writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
160
161 mutex_unlock(&s3c_ac97.lock);
162}
163
164static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
165{
166 pr_debug("AC97: Cold reset\n");
167 writel(S3C_AC97_GLBCTRL_COLDRESET,
168 s3c_ac97.regs + S3C_AC97_GLBCTRL);
169 msleep(1);
170
171 writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
172 msleep(1);
173}
174
175static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
176{
177 u32 stat;
178
179 stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
180 if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE)
181 return; /* Return if already active */
182
183 pr_debug("AC97: Warm reset\n");
184
185 writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
186 msleep(1);
187
188 writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
189 msleep(1);
190
191 s3c_ac97_activate(ac97);
192}
193
194static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
195{
196 u32 ac_glbctrl, ac_glbstat;
197
198 ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
199
200 if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
201
202 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
203 ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
204 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
205
206 complete(&s3c_ac97.done);
207 }
208
209 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
210 ac_glbctrl |= (1<<30); /* Clear interrupt */
211 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
212
213 return IRQ_HANDLED;
214}
215
216struct snd_ac97_bus_ops soc_ac97_ops = {
217 .read = s3c_ac97_read,
218 .write = s3c_ac97_write,
219 .warm_reset = s3c_ac97_warm_reset,
220 .reset = s3c_ac97_cold_reset,
221};
222EXPORT_SYMBOL_GPL(soc_ac97_ops);
223
224static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
225 struct snd_pcm_hw_params *params,
226 struct snd_soc_dai *dai)
227{
228 struct snd_soc_pcm_runtime *rtd = substream->private_data;
229 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
230 struct s3c_dma_params *dma_data;
231
232 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
233 dma_data = &s3c_ac97_pcm_out;
234 else
235 dma_data = &s3c_ac97_pcm_in;
236
237 snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
238
239 return 0;
240}
241
242static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
243 struct snd_soc_dai *dai)
244{
245 u32 ac_glbctrl;
246 struct snd_soc_pcm_runtime *rtd = substream->private_data;
247 struct s3c_dma_params *dma_data =
248 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
249
250 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
251 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
252 ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
253 else
254 ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
255
256 switch (cmd) {
257 case SNDRV_PCM_TRIGGER_START:
258 case SNDRV_PCM_TRIGGER_RESUME:
259 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
260 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
261 ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
262 else
263 ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
264 break;
265
266 case SNDRV_PCM_TRIGGER_STOP:
267 case SNDRV_PCM_TRIGGER_SUSPEND:
268 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
269 break;
270 }
271
272 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
273
274 s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED);
275
276 return 0;
277}
278
279static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
280 struct snd_pcm_hw_params *params,
281 struct snd_soc_dai *dai)
282{
283 struct snd_soc_pcm_runtime *rtd = substream->private_data;
284 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
285
286 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
287 return -ENODEV;
288 else
289 snd_soc_dai_set_dma_data(cpu_dai, substream, &s3c_ac97_mic_in);
290
291 return 0;
292}
293
294static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
295 int cmd, struct snd_soc_dai *dai)
296{
297 u32 ac_glbctrl;
298 struct snd_soc_pcm_runtime *rtd = substream->private_data;
299 struct s3c_dma_params *dma_data =
300 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
301
302 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
303 ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
304
305 switch (cmd) {
306 case SNDRV_PCM_TRIGGER_START:
307 case SNDRV_PCM_TRIGGER_RESUME:
308 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
309 ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
310 break;
311
312 case SNDRV_PCM_TRIGGER_STOP:
313 case SNDRV_PCM_TRIGGER_SUSPEND:
314 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
315 break;
316 }
317
318 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
319
320 s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED);
321
322 return 0;
323}
324
325static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
326 .hw_params = s3c_ac97_hw_params,
327 .trigger = s3c_ac97_trigger,
328};
329
330static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
331 .hw_params = s3c_ac97_hw_mic_params,
332 .trigger = s3c_ac97_mic_trigger,
333};
334
335static struct snd_soc_dai_driver s3c_ac97_dai[] = {
336 [S3C_AC97_DAI_PCM] = {
337 .name = "samsung-ac97",
338 .ac97_control = 1,
339 .playback = {
340 .stream_name = "AC97 Playback",
341 .channels_min = 2,
342 .channels_max = 2,
343 .rates = SNDRV_PCM_RATE_8000_48000,
344 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
345 .capture = {
346 .stream_name = "AC97 Capture",
347 .channels_min = 2,
348 .channels_max = 2,
349 .rates = SNDRV_PCM_RATE_8000_48000,
350 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
351 .ops = &s3c_ac97_dai_ops,
352 },
353 [S3C_AC97_DAI_MIC] = {
354 .name = "samsung-ac97-mic",
355 .ac97_control = 1,
356 .capture = {
357 .stream_name = "AC97 Mic Capture",
358 .channels_min = 1,
359 .channels_max = 1,
360 .rates = SNDRV_PCM_RATE_8000_48000,
361 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
362 .ops = &s3c_ac97_mic_dai_ops,
363 },
364};
365
366static __devinit int s3c_ac97_probe(struct platform_device *pdev)
367{
368 struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
369 struct s3c_audio_pdata *ac97_pdata;
370 int ret;
371
372 ac97_pdata = pdev->dev.platform_data;
373 if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
374 dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
375 return -EINVAL;
376 }
377
378 /* Check for availability of necessary resource */
379 dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
380 if (!dmatx_res) {
381 dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
382 return -ENXIO;
383 }
384
385 dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
386 if (!dmarx_res) {
387 dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
388 return -ENXIO;
389 }
390
391 dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
392 if (!dmamic_res) {
393 dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
394 return -ENXIO;
395 }
396
397 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
398 if (!mem_res) {
399 dev_err(&pdev->dev, "Unable to get register resource\n");
400 return -ENXIO;
401 }
402
403 irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
404 if (!irq_res) {
405 dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
406 return -ENXIO;
407 }
408
409 if (!request_mem_region(mem_res->start,
410 resource_size(mem_res), "ac97")) {
411 dev_err(&pdev->dev, "Unable to request register region\n");
412 return -EBUSY;
413 }
414
415 s3c_ac97_pcm_out.channel = dmatx_res->start;
416 s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
417 s3c_ac97_pcm_in.channel = dmarx_res->start;
418 s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
419 s3c_ac97_mic_in.channel = dmamic_res->start;
420 s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
421
422 init_completion(&s3c_ac97.done);
423 mutex_init(&s3c_ac97.lock);
424
425 s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
426 if (s3c_ac97.regs == NULL) {
427 dev_err(&pdev->dev, "Unable to ioremap register region\n");
428 ret = -ENXIO;
429 goto err1;
430 }
431
432 s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
433 if (IS_ERR(s3c_ac97.ac97_clk)) {
434 dev_err(&pdev->dev, "ac97 failed to get ac97_clock\n");
435 ret = -ENODEV;
436 goto err2;
437 }
438 clk_enable(s3c_ac97.ac97_clk);
439
440 if (ac97_pdata->cfg_gpio(pdev)) {
441 dev_err(&pdev->dev, "Unable to configure gpio\n");
442 ret = -EINVAL;
443 goto err3;
444 }
445
446 ret = request_irq(irq_res->start, s3c_ac97_irq,
447 IRQF_DISABLED, "AC97", NULL);
448 if (ret < 0) {
449 dev_err(&pdev->dev, "ac97: interrupt request failed.\n");
450 goto err4;
451 }
452
453 ret = snd_soc_register_dais(&pdev->dev, s3c_ac97_dai,
454 ARRAY_SIZE(s3c_ac97_dai));
455 if (ret)
456 goto err5;
457
458 return 0;
459
460err5:
461 free_irq(irq_res->start, NULL);
462err4:
463err3:
464 clk_disable(s3c_ac97.ac97_clk);
465 clk_put(s3c_ac97.ac97_clk);
466err2:
467 iounmap(s3c_ac97.regs);
468err1:
469 release_mem_region(mem_res->start, resource_size(mem_res));
470
471 return ret;
472}
473
474static __devexit int s3c_ac97_remove(struct platform_device *pdev)
475{
476 struct resource *mem_res, *irq_res;
477
478 snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(s3c_ac97_dai));
479
480 irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
481 if (irq_res)
482 free_irq(irq_res->start, NULL);
483
484 clk_disable(s3c_ac97.ac97_clk);
485 clk_put(s3c_ac97.ac97_clk);
486
487 iounmap(s3c_ac97.regs);
488
489 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
490 if (mem_res)
491 release_mem_region(mem_res->start, resource_size(mem_res));
492
493 return 0;
494}
495
496static struct platform_driver s3c_ac97_driver = {
497 .probe = s3c_ac97_probe,
498 .remove = s3c_ac97_remove,
499 .driver = {
500 .name = "samsung-ac97",
501 .owner = THIS_MODULE,
502 },
503};
504
505static int __init s3c_ac97_init(void)
506{
507 return platform_driver_register(&s3c_ac97_driver);
508}
509module_init(s3c_ac97_init);
510
511static void __exit s3c_ac97_exit(void)
512{
513 platform_driver_unregister(&s3c_ac97_driver);
514}
515module_exit(s3c_ac97_exit);
516
517MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
518MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
519MODULE_LICENSE("GPL");
520MODULE_ALIAS("platform:samsung-ac97");
diff --git a/sound/soc/samsung/ac97.h b/sound/soc/samsung/ac97.h
new file mode 100644
index 000000000000..0d0e1b511457
--- /dev/null
+++ b/sound/soc/samsung/ac97.h
@@ -0,0 +1,21 @@
1/* sound/soc/samsung/ac97.h
2 *
3 * ALSA SoC Audio Layer - S3C AC97 Controller driver
4 * Evolved from s3c2443-ac97.h
5 *
6 * Copyright (c) 2010 Samsung Electronics Co. Ltd
7 * Author: Jaswinder Singh <jassi.brar@samsung.com>
8 * Credits: Graeme Gregory, Sean Choi
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#ifndef __S3C_AC97_H_
16#define __S3C_AC97_H_
17
18#define S3C_AC97_DAI_PCM 0
19#define S3C_AC97_DAI_MIC 1
20
21#endif /* __S3C_AC97_H_ */
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
new file mode 100644
index 000000000000..21240198c5d6
--- /dev/null
+++ b/sound/soc/samsung/dma.c
@@ -0,0 +1,502 @@
1/*
2 * dma.c -- ALSA Soc Audio Layer
3 *
4 * (c) 2006 Wolfson Microelectronics PLC.
5 * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
6 *
7 * Copyright 2004-2005 Simtec Electronics
8 * http://armlinux.simtec.co.uk/
9 * Ben Dooks <ben@simtec.co.uk>
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
15 */
16
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
22#include <linux/dma-mapping.h>
23
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include <sound/pcm_params.h>
27#include <sound/soc.h>
28
29#include <asm/dma.h>
30#include <mach/hardware.h>
31#include <mach/dma.h>
32
33#include "dma.h"
34
35static const struct snd_pcm_hardware dma_hardware = {
36 .info = SNDRV_PCM_INFO_INTERLEAVED |
37 SNDRV_PCM_INFO_BLOCK_TRANSFER |
38 SNDRV_PCM_INFO_MMAP |
39 SNDRV_PCM_INFO_MMAP_VALID |
40 SNDRV_PCM_INFO_PAUSE |
41 SNDRV_PCM_INFO_RESUME,
42 .formats = SNDRV_PCM_FMTBIT_S16_LE |
43 SNDRV_PCM_FMTBIT_U16_LE |
44 SNDRV_PCM_FMTBIT_U8 |
45 SNDRV_PCM_FMTBIT_S8,
46 .channels_min = 2,
47 .channels_max = 2,
48 .buffer_bytes_max = 128*1024,
49 .period_bytes_min = PAGE_SIZE,
50 .period_bytes_max = PAGE_SIZE*2,
51 .periods_min = 2,
52 .periods_max = 128,
53 .fifo_size = 32,
54};
55
56struct runtime_data {
57 spinlock_t lock;
58 int state;
59 unsigned int dma_loaded;
60 unsigned int dma_limit;
61 unsigned int dma_period;
62 dma_addr_t dma_start;
63 dma_addr_t dma_pos;
64 dma_addr_t dma_end;
65 struct s3c_dma_params *params;
66};
67
68/* dma_enqueue
69 *
70 * place a dma buffer onto the queue for the dma system
71 * to handle.
72*/
73static void dma_enqueue(struct snd_pcm_substream *substream)
74{
75 struct runtime_data *prtd = substream->runtime->private_data;
76 dma_addr_t pos = prtd->dma_pos;
77 unsigned int limit;
78 int ret;
79
80 pr_debug("Entered %s\n", __func__);
81
82 if (s3c_dma_has_circular())
83 limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
84 else
85 limit = prtd->dma_limit;
86
87 pr_debug("%s: loaded %d, limit %d\n",
88 __func__, prtd->dma_loaded, limit);
89
90 while (prtd->dma_loaded < limit) {
91 unsigned long len = prtd->dma_period;
92
93 pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
94
95 if ((pos + len) > prtd->dma_end) {
96 len = prtd->dma_end - pos;
97 pr_debug("%s: corrected dma len %ld\n", __func__, len);
98 }
99
100 ret = s3c2410_dma_enqueue(prtd->params->channel,
101 substream, pos, len);
102
103 if (ret == 0) {
104 prtd->dma_loaded++;
105 pos += prtd->dma_period;
106 if (pos >= prtd->dma_end)
107 pos = prtd->dma_start;
108 } else
109 break;
110 }
111
112 prtd->dma_pos = pos;
113}
114
115static void audio_buffdone(struct s3c2410_dma_chan *channel,
116 void *dev_id, int size,
117 enum s3c2410_dma_buffresult result)
118{
119 struct snd_pcm_substream *substream = dev_id;
120 struct runtime_data *prtd;
121
122 pr_debug("Entered %s\n", __func__);
123
124 if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
125 return;
126
127 prtd = substream->runtime->private_data;
128
129 if (substream)
130 snd_pcm_period_elapsed(substream);
131
132 spin_lock(&prtd->lock);
133 if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) {
134 prtd->dma_loaded--;
135 dma_enqueue(substream);
136 }
137
138 spin_unlock(&prtd->lock);
139}
140
141static int dma_hw_params(struct snd_pcm_substream *substream,
142 struct snd_pcm_hw_params *params)
143{
144 struct snd_pcm_runtime *runtime = substream->runtime;
145 struct runtime_data *prtd = runtime->private_data;
146 struct snd_soc_pcm_runtime *rtd = substream->private_data;
147 unsigned long totbytes = params_buffer_bytes(params);
148 struct s3c_dma_params *dma =
149 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
150 int ret = 0;
151
152
153 pr_debug("Entered %s\n", __func__);
154
155 /* return if this is a bufferless transfer e.g.
156 * codec <--> BT codec or GSM modem -- lg FIXME */
157 if (!dma)
158 return 0;
159
160 /* this may get called several times by oss emulation
161 * with different params -HW */
162 if (prtd->params == NULL) {
163 /* prepare DMA */
164 prtd->params = dma;
165
166 pr_debug("params %p, client %p, channel %d\n", prtd->params,
167 prtd->params->client, prtd->params->channel);
168
169 ret = s3c2410_dma_request(prtd->params->channel,
170 prtd->params->client, NULL);
171
172 if (ret < 0) {
173 printk(KERN_ERR "failed to get dma channel\n");
174 return ret;
175 }
176
177 /* use the circular buffering if we have it available. */
178 if (s3c_dma_has_circular())
179 s3c2410_dma_setflags(prtd->params->channel,
180 S3C2410_DMAF_CIRCULAR);
181 }
182
183 s3c2410_dma_set_buffdone_fn(prtd->params->channel,
184 audio_buffdone);
185
186 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
187
188 runtime->dma_bytes = totbytes;
189
190 spin_lock_irq(&prtd->lock);
191 prtd->dma_loaded = 0;
192 prtd->dma_limit = runtime->hw.periods_min;
193 prtd->dma_period = params_period_bytes(params);
194 prtd->dma_start = runtime->dma_addr;
195 prtd->dma_pos = prtd->dma_start;
196 prtd->dma_end = prtd->dma_start + totbytes;
197 spin_unlock_irq(&prtd->lock);
198
199 return 0;
200}
201
202static int dma_hw_free(struct snd_pcm_substream *substream)
203{
204 struct runtime_data *prtd = substream->runtime->private_data;
205
206 pr_debug("Entered %s\n", __func__);
207
208 /* TODO - do we need to ensure DMA flushed */
209 snd_pcm_set_runtime_buffer(substream, NULL);
210
211 if (prtd->params) {
212 s3c2410_dma_free(prtd->params->channel, prtd->params->client);
213 prtd->params = NULL;
214 }
215
216 return 0;
217}
218
219static int dma_prepare(struct snd_pcm_substream *substream)
220{
221 struct runtime_data *prtd = substream->runtime->private_data;
222 int ret = 0;
223
224 pr_debug("Entered %s\n", __func__);
225
226 /* return if this is a bufferless transfer e.g.
227 * codec <--> BT codec or GSM modem -- lg FIXME */
228 if (!prtd->params)
229 return 0;
230
231 /* channel needs configuring for mem=>device, increment memory addr,
232 * sync to pclk, half-word transfers to the IIS-FIFO. */
233 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
234 s3c2410_dma_devconfig(prtd->params->channel,
235 S3C2410_DMASRC_MEM,
236 prtd->params->dma_addr);
237 } else {
238 s3c2410_dma_devconfig(prtd->params->channel,
239 S3C2410_DMASRC_HW,
240 prtd->params->dma_addr);
241 }
242
243 s3c2410_dma_config(prtd->params->channel,
244 prtd->params->dma_size);
245
246 /* flush the DMA channel */
247 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH);
248 prtd->dma_loaded = 0;
249 prtd->dma_pos = prtd->dma_start;
250
251 /* enqueue dma buffers */
252 dma_enqueue(substream);
253
254 return ret;
255}
256
257static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
258{
259 struct runtime_data *prtd = substream->runtime->private_data;
260 int ret = 0;
261
262 pr_debug("Entered %s\n", __func__);
263
264 spin_lock(&prtd->lock);
265
266 switch (cmd) {
267 case SNDRV_PCM_TRIGGER_START:
268 case SNDRV_PCM_TRIGGER_RESUME:
269 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
270 prtd->state |= ST_RUNNING;
271 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);
272 break;
273
274 case SNDRV_PCM_TRIGGER_STOP:
275 case SNDRV_PCM_TRIGGER_SUSPEND:
276 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
277 prtd->state &= ~ST_RUNNING;
278 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP);
279 break;
280
281 default:
282 ret = -EINVAL;
283 break;
284 }
285
286 spin_unlock(&prtd->lock);
287
288 return ret;
289}
290
291static snd_pcm_uframes_t
292dma_pointer(struct snd_pcm_substream *substream)
293{
294 struct snd_pcm_runtime *runtime = substream->runtime;
295 struct runtime_data *prtd = runtime->private_data;
296 unsigned long res;
297 dma_addr_t src, dst;
298
299 pr_debug("Entered %s\n", __func__);
300
301 spin_lock(&prtd->lock);
302 s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
303
304 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
305 res = dst - prtd->dma_start;
306 else
307 res = src - prtd->dma_start;
308
309 spin_unlock(&prtd->lock);
310
311 pr_debug("Pointer %x %x\n", src, dst);
312
313 /* we seem to be getting the odd error from the pcm library due
314 * to out-of-bounds pointers. this is maybe due to the dma engine
315 * not having loaded the new values for the channel before being
316 * callled... (todo - fix )
317 */
318
319 if (res >= snd_pcm_lib_buffer_bytes(substream)) {
320 if (res == snd_pcm_lib_buffer_bytes(substream))
321 res = 0;
322 }
323
324 return bytes_to_frames(substream->runtime, res);
325}
326
327static int dma_open(struct snd_pcm_substream *substream)
328{
329 struct snd_pcm_runtime *runtime = substream->runtime;
330 struct runtime_data *prtd;
331
332 pr_debug("Entered %s\n", __func__);
333
334 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
335 snd_soc_set_runtime_hwparams(substream, &dma_hardware);
336
337 prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL);
338 if (prtd == NULL)
339 return -ENOMEM;
340
341 spin_lock_init(&prtd->lock);
342
343 runtime->private_data = prtd;
344 return 0;
345}
346
347static int dma_close(struct snd_pcm_substream *substream)
348{
349 struct snd_pcm_runtime *runtime = substream->runtime;
350 struct runtime_data *prtd = runtime->private_data;
351
352 pr_debug("Entered %s\n", __func__);
353
354 if (!prtd)
355 pr_debug("dma_close called with prtd == NULL\n");
356
357 kfree(prtd);
358
359 return 0;
360}
361
362static int dma_mmap(struct snd_pcm_substream *substream,
363 struct vm_area_struct *vma)
364{
365 struct snd_pcm_runtime *runtime = substream->runtime;
366
367 pr_debug("Entered %s\n", __func__);
368
369 return dma_mmap_writecombine(substream->pcm->card->dev, vma,
370 runtime->dma_area,
371 runtime->dma_addr,
372 runtime->dma_bytes);
373}
374
375static struct snd_pcm_ops dma_ops = {
376 .open = dma_open,
377 .close = dma_close,
378 .ioctl = snd_pcm_lib_ioctl,
379 .hw_params = dma_hw_params,
380 .hw_free = dma_hw_free,
381 .prepare = dma_prepare,
382 .trigger = dma_trigger,
383 .pointer = dma_pointer,
384 .mmap = dma_mmap,
385};
386
387static int preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
388{
389 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
390 struct snd_dma_buffer *buf = &substream->dma_buffer;
391 size_t size = dma_hardware.buffer_bytes_max;
392
393 pr_debug("Entered %s\n", __func__);
394
395 buf->dev.type = SNDRV_DMA_TYPE_DEV;
396 buf->dev.dev = pcm->card->dev;
397 buf->private_data = NULL;
398 buf->area = dma_alloc_writecombine(pcm->card->dev, size,
399 &buf->addr, GFP_KERNEL);
400 if (!buf->area)
401 return -ENOMEM;
402 buf->bytes = size;
403 return 0;
404}
405
406static void dma_free_dma_buffers(struct snd_pcm *pcm)
407{
408 struct snd_pcm_substream *substream;
409 struct snd_dma_buffer *buf;
410 int stream;
411
412 pr_debug("Entered %s\n", __func__);
413
414 for (stream = 0; stream < 2; stream++) {
415 substream = pcm->streams[stream].substream;
416 if (!substream)
417 continue;
418
419 buf = &substream->dma_buffer;
420 if (!buf->area)
421 continue;
422
423 dma_free_writecombine(pcm->card->dev, buf->bytes,
424 buf->area, buf->addr);
425 buf->area = NULL;
426 }
427}
428
429static u64 dma_mask = DMA_BIT_MASK(32);
430
431static int dma_new(struct snd_card *card,
432 struct snd_soc_dai *dai, struct snd_pcm *pcm)
433{
434 int ret = 0;
435
436 pr_debug("Entered %s\n", __func__);
437
438 if (!card->dev->dma_mask)
439 card->dev->dma_mask = &dma_mask;
440 if (!card->dev->coherent_dma_mask)
441 card->dev->coherent_dma_mask = 0xffffffff;
442
443 if (dai->driver->playback.channels_min) {
444 ret = preallocate_dma_buffer(pcm,
445 SNDRV_PCM_STREAM_PLAYBACK);
446 if (ret)
447 goto out;
448 }
449
450 if (dai->driver->capture.channels_min) {
451 ret = preallocate_dma_buffer(pcm,
452 SNDRV_PCM_STREAM_CAPTURE);
453 if (ret)
454 goto out;
455 }
456out:
457 return ret;
458}
459
460static struct snd_soc_platform_driver samsung_asoc_platform = {
461 .ops = &dma_ops,
462 .pcm_new = dma_new,
463 .pcm_free = dma_free_dma_buffers,
464};
465
466static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev)
467{
468 return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
469}
470
471static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev)
472{
473 snd_soc_unregister_platform(&pdev->dev);
474 return 0;
475}
476
477static struct platform_driver asoc_dma_driver = {
478 .driver = {
479 .name = "samsung-audio",
480 .owner = THIS_MODULE,
481 },
482
483 .probe = samsung_asoc_platform_probe,
484 .remove = __devexit_p(samsung_asoc_platform_remove),
485};
486
487static int __init samsung_asoc_init(void)
488{
489 return platform_driver_register(&asoc_dma_driver);
490}
491module_init(samsung_asoc_init);
492
493static void __exit samsung_asoc_exit(void)
494{
495 platform_driver_unregister(&asoc_dma_driver);
496}
497module_exit(samsung_asoc_exit);
498
499MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
500MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
501MODULE_LICENSE("GPL");
502MODULE_ALIAS("platform:samsung-audio");
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
new file mode 100644
index 000000000000..f8cd2b4223af
--- /dev/null
+++ b/sound/soc/samsung/dma.h
@@ -0,0 +1,30 @@
1/*
2 * dma.h --
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * ALSA PCM interface for the Samsung S3C24xx CPU
10 */
11
12#ifndef _S3C_AUDIO_H
13#define _S3C_AUDIO_H
14
15#define ST_RUNNING (1<<0)
16#define ST_OPENED (1<<1)
17
18struct s3c_dma_params {
19 struct s3c2410_dma_client *client; /* stream identifier */
20 int channel; /* Channel ID */
21 dma_addr_t dma_addr;
22 int dma_size; /* Size of the DMA transfer */
23};
24
25#define S3C24XX_DAI_I2S 0
26
27/* platform data */
28extern struct snd_ac97_bus_ops s3c24xx_ac97_ops;
29
30#endif
diff --git a/sound/soc/samsung/goni_wm8994.c b/sound/soc/samsung/goni_wm8994.c
new file mode 100644
index 000000000000..dcfac546cb81
--- /dev/null
+++ b/sound/soc/samsung/goni_wm8994.c
@@ -0,0 +1,309 @@
1/*
2 * goni_wm8994.c
3 *
4 * Copyright (C) 2010 Samsung Electronics Co.Ltd
5 * Author: Chanwoo Choi <cw00.choi@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/moduleparam.h>
16#include <linux/io.h>
17#include <linux/platform_device.h>
18#include <sound/soc.h>
19#include <sound/jack.h>
20#include <asm/mach-types.h>
21#include <mach/gpio.h>
22#include <mach/regs-clock.h>
23
24#include <linux/mfd/wm8994/core.h>
25#include <linux/mfd/wm8994/registers.h>
26#include "../codecs/wm8994.h"
27#include "dma.h"
28#include "i2s.h"
29
30#define MACHINE_NAME 0
31#define CPU_VOICE_DAI 1
32
33static const char *aquila_str[] = {
34 [MACHINE_NAME] = "aquila",
35 [CPU_VOICE_DAI] = "aquila-voice-dai",
36};
37
38static struct snd_soc_card goni;
39static struct platform_device *goni_snd_device;
40
41/* 3.5 pie jack */
42static struct snd_soc_jack jack;
43
44/* 3.5 pie jack detection DAPM pins */
45static struct snd_soc_jack_pin jack_pins[] = {
46 {
47 .pin = "Headset Mic",
48 .mask = SND_JACK_MICROPHONE,
49 }, {
50 .pin = "Headset Stereophone",
51 .mask = SND_JACK_HEADPHONE | SND_JACK_MECHANICAL |
52 SND_JACK_AVOUT,
53 },
54};
55
56/* 3.5 pie jack detection gpios */
57static struct snd_soc_jack_gpio jack_gpios[] = {
58 {
59 .gpio = S5PV210_GPH0(6),
60 .name = "DET_3.5",
61 .report = SND_JACK_HEADSET | SND_JACK_MECHANICAL |
62 SND_JACK_AVOUT,
63 .debounce_time = 200,
64 },
65};
66
67static const struct snd_soc_dapm_widget goni_dapm_widgets[] = {
68 SND_SOC_DAPM_SPK("Ext Left Spk", NULL),
69 SND_SOC_DAPM_SPK("Ext Right Spk", NULL),
70 SND_SOC_DAPM_SPK("Ext Rcv", NULL),
71 SND_SOC_DAPM_HP("Headset Stereophone", NULL),
72 SND_SOC_DAPM_MIC("Headset Mic", NULL),
73 SND_SOC_DAPM_MIC("Main Mic", NULL),
74 SND_SOC_DAPM_MIC("2nd Mic", NULL),
75 SND_SOC_DAPM_LINE("Radio In", NULL),
76};
77
78static const struct snd_soc_dapm_route goni_dapm_routes[] = {
79 {"Ext Left Spk", NULL, "SPKOUTLP"},
80 {"Ext Left Spk", NULL, "SPKOUTLN"},
81
82 {"Ext Right Spk", NULL, "SPKOUTRP"},
83 {"Ext Right Spk", NULL, "SPKOUTRN"},
84
85 {"Ext Rcv", NULL, "HPOUT2N"},
86 {"Ext Rcv", NULL, "HPOUT2P"},
87
88 {"Headset Stereophone", NULL, "HPOUT1L"},
89 {"Headset Stereophone", NULL, "HPOUT1R"},
90
91 {"IN1RN", NULL, "Headset Mic"},
92 {"IN1RP", NULL, "Headset Mic"},
93
94 {"IN1RN", NULL, "2nd Mic"},
95 {"IN1RP", NULL, "2nd Mic"},
96
97 {"IN1LN", NULL, "Main Mic"},
98 {"IN1LP", NULL, "Main Mic"},
99
100 {"IN2LN", NULL, "Radio In"},
101 {"IN2RN", NULL, "Radio In"},
102};
103
104static int goni_wm8994_init(struct snd_soc_pcm_runtime *rtd)
105{
106 struct snd_soc_codec *codec = rtd->codec;
107 struct snd_soc_dapm_context *dapm = &codec->dapm;
108 int ret;
109
110 /* add goni specific widgets */
111 snd_soc_dapm_new_controls(dapm, goni_dapm_widgets,
112 ARRAY_SIZE(goni_dapm_widgets));
113
114 /* set up goni specific audio routes */
115 snd_soc_dapm_add_routes(dapm, goni_dapm_routes,
116 ARRAY_SIZE(goni_dapm_routes));
117
118 /* set endpoints to not connected */
119 snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
120 snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
121 snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
122 snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
123 snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
124 snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
125
126 if (machine_is_aquila()) {
127 snd_soc_dapm_nc_pin(dapm, "SPKOUTRN");
128 snd_soc_dapm_nc_pin(dapm, "SPKOUTRP");
129 }
130
131 snd_soc_dapm_sync(dapm);
132
133 /* Headset jack detection */
134 ret = snd_soc_jack_new(&goni, "Headset Jack",
135 SND_JACK_HEADSET | SND_JACK_MECHANICAL | SND_JACK_AVOUT,
136 &jack);
137 if (ret)
138 return ret;
139
140 ret = snd_soc_jack_add_pins(&jack, ARRAY_SIZE(jack_pins), jack_pins);
141 if (ret)
142 return ret;
143
144 ret = snd_soc_jack_add_gpios(&jack, ARRAY_SIZE(jack_gpios), jack_gpios);
145 if (ret)
146 return ret;
147
148 return 0;
149}
150
151static int goni_hifi_hw_params(struct snd_pcm_substream *substream,
152 struct snd_pcm_hw_params *params)
153{
154 struct snd_soc_pcm_runtime *rtd = substream->private_data;
155 struct snd_soc_dai *codec_dai = rtd->codec_dai;
156 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
157 unsigned int pll_out = 24000000;
158 int ret = 0;
159
160 /* set the cpu DAI configuration */
161 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
162 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
163 if (ret < 0)
164 return ret;
165
166 /* set codec DAI configuration */
167 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
168 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
169 if (ret < 0)
170 return ret;
171
172 /* set the codec FLL */
173 ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, 0, pll_out,
174 params_rate(params) * 256);
175 if (ret < 0)
176 return ret;
177
178 /* set the codec system clock */
179 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
180 params_rate(params) * 256, SND_SOC_CLOCK_IN);
181 if (ret < 0)
182 return ret;
183
184 return 0;
185}
186
187static struct snd_soc_ops goni_hifi_ops = {
188 .hw_params = goni_hifi_hw_params,
189};
190
191static int goni_voice_hw_params(struct snd_pcm_substream *substream,
192 struct snd_pcm_hw_params *params)
193{
194 struct snd_soc_pcm_runtime *rtd = substream->private_data;
195 struct snd_soc_dai *codec_dai = rtd->codec_dai;
196 unsigned int pll_out = 24000000;
197 int ret = 0;
198
199 if (params_rate(params) != 8000)
200 return -EINVAL;
201
202 /* set codec DAI configuration */
203 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J |
204 SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
205 if (ret < 0)
206 return ret;
207
208 /* set the codec FLL */
209 ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, 0, pll_out,
210 params_rate(params) * 256);
211 if (ret < 0)
212 return ret;
213
214 /* set the codec system clock */
215 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2,
216 params_rate(params) * 256, SND_SOC_CLOCK_IN);
217 if (ret < 0)
218 return ret;
219
220 return 0;
221}
222
223static struct snd_soc_dai_driver voice_dai = {
224 .name = "goni-voice-dai",
225 .id = 0,
226 .playback = {
227 .channels_min = 1,
228 .channels_max = 2,
229 .rates = SNDRV_PCM_RATE_8000,
230 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
231 .capture = {
232 .channels_min = 1,
233 .channels_max = 2,
234 .rates = SNDRV_PCM_RATE_8000,
235 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
236};
237
238static struct snd_soc_ops goni_voice_ops = {
239 .hw_params = goni_voice_hw_params,
240};
241
242static struct snd_soc_dai_link goni_dai[] = {
243{
244 .name = "WM8994",
245 .stream_name = "WM8994 HiFi",
246 .cpu_dai_name = "samsung-i2s.0",
247 .codec_dai_name = "wm8994-hifi",
248 .platform_name = "samsung-audio",
249 .codec_name = "wm8994-codec.0-0x1a",
250 .init = goni_wm8994_init,
251 .ops = &goni_hifi_ops,
252}, {
253 .name = "WM8994 Voice",
254 .stream_name = "Voice",
255 .cpu_dai_name = "goni-voice-dai",
256 .codec_dai_name = "wm8994-voice",
257 .platform_name = "samsung-audio",
258 .codec_name = "wm8994-codec.0-0x1a",
259 .ops = &goni_voice_ops,
260},
261};
262
263static struct snd_soc_card goni = {
264 .name = "goni",
265 .dai_link = goni_dai,
266 .num_links = ARRAY_SIZE(goni_dai),
267};
268
269static int __init goni_init(void)
270{
271 int ret;
272
273 if (machine_is_aquila()) {
274 voice_dai.name = aquila_str[CPU_VOICE_DAI];
275 goni_dai[1].cpu_dai_name = aquila_str[CPU_VOICE_DAI];
276 goni.name = aquila_str[MACHINE_NAME];
277 } else if (!machine_is_goni())
278 return -ENODEV;
279
280 goni_snd_device = platform_device_alloc("soc-audio", -1);
281 if (!goni_snd_device)
282 return -ENOMEM;
283
284 /* register voice DAI here */
285 ret = snd_soc_register_dai(&goni_snd_device->dev, &voice_dai);
286 if (ret)
287 return ret;
288
289 platform_set_drvdata(goni_snd_device, &goni);
290 ret = platform_device_add(goni_snd_device);
291
292 if (ret)
293 platform_device_put(goni_snd_device);
294
295 return ret;
296}
297
298static void __exit goni_exit(void)
299{
300 platform_device_unregister(goni_snd_device);
301}
302
303module_init(goni_init);
304module_exit(goni_exit);
305
306/* Module information */
307MODULE_DESCRIPTION("ALSA SoC WM8994 GONI(S5PV210)");
308MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
309MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
new file mode 100644
index 000000000000..d6de3f0eeff8
--- /dev/null
+++ b/sound/soc/samsung/i2s.c
@@ -0,0 +1,1256 @@
1/* sound/soc/samsung/i2s.c
2 *
3 * ALSA SoC Audio Layer - Samsung I2S Controller driver
4 *
5 * Copyright (c) 2010 Samsung Electronics Co. Ltd.
6 * Jaswinder Singh <jassi.brar@samsung.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/delay.h>
14#include <linux/slab.h>
15#include <linux/clk.h>
16#include <linux/io.h>
17
18#include <sound/pcm.h>
19#include <sound/pcm_params.h>
20#include <sound/soc.h>
21
22#include <plat/audio.h>
23
24#include "dma.h"
25#include "i2s.h"
26
27#define I2SCON 0x0
28#define I2SMOD 0x4
29#define I2SFIC 0x8
30#define I2SPSR 0xc
31#define I2STXD 0x10
32#define I2SRXD 0x14
33#define I2SFICS 0x18
34#define I2STXDS 0x1c
35
36#define CON_RSTCLR (1 << 31)
37#define CON_FRXOFSTATUS (1 << 26)
38#define CON_FRXORINTEN (1 << 25)
39#define CON_FTXSURSTAT (1 << 24)
40#define CON_FTXSURINTEN (1 << 23)
41#define CON_TXSDMA_PAUSE (1 << 20)
42#define CON_TXSDMA_ACTIVE (1 << 18)
43
44#define CON_FTXURSTATUS (1 << 17)
45#define CON_FTXURINTEN (1 << 16)
46#define CON_TXFIFO2_EMPTY (1 << 15)
47#define CON_TXFIFO1_EMPTY (1 << 14)
48#define CON_TXFIFO2_FULL (1 << 13)
49#define CON_TXFIFO1_FULL (1 << 12)
50
51#define CON_LRINDEX (1 << 11)
52#define CON_TXFIFO_EMPTY (1 << 10)
53#define CON_RXFIFO_EMPTY (1 << 9)
54#define CON_TXFIFO_FULL (1 << 8)
55#define CON_RXFIFO_FULL (1 << 7)
56#define CON_TXDMA_PAUSE (1 << 6)
57#define CON_RXDMA_PAUSE (1 << 5)
58#define CON_TXCH_PAUSE (1 << 4)
59#define CON_RXCH_PAUSE (1 << 3)
60#define CON_TXDMA_ACTIVE (1 << 2)
61#define CON_RXDMA_ACTIVE (1 << 1)
62#define CON_ACTIVE (1 << 0)
63
64#define MOD_OPCLK_CDCLK_OUT (0 << 30)
65#define MOD_OPCLK_CDCLK_IN (1 << 30)
66#define MOD_OPCLK_BCLK_OUT (2 << 30)
67#define MOD_OPCLK_PCLK (3 << 30)
68#define MOD_OPCLK_MASK (3 << 30)
69#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
70
71#define MOD_BLCS_SHIFT 26
72#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT)
73#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT)
74#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT)
75#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT)
76#define MOD_BLCP_SHIFT 24
77#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT)
78#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT)
79#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT)
80#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT)
81
82#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */
83#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */
84#define MOD_C1DD_HHALF (1 << 19)
85#define MOD_C1DD_LHALF (1 << 18)
86#define MOD_DC2_EN (1 << 17)
87#define MOD_DC1_EN (1 << 16)
88#define MOD_BLC_16BIT (0 << 13)
89#define MOD_BLC_8BIT (1 << 13)
90#define MOD_BLC_24BIT (2 << 13)
91#define MOD_BLC_MASK (3 << 13)
92
93#define MOD_IMS_SYSMUX (1 << 10)
94#define MOD_SLAVE (1 << 11)
95#define MOD_TXONLY (0 << 8)
96#define MOD_RXONLY (1 << 8)
97#define MOD_TXRX (2 << 8)
98#define MOD_MASK (3 << 8)
99#define MOD_LR_LLOW (0 << 7)
100#define MOD_LR_RLOW (1 << 7)
101#define MOD_SDF_IIS (0 << 5)
102#define MOD_SDF_MSB (1 << 5)
103#define MOD_SDF_LSB (2 << 5)
104#define MOD_SDF_MASK (3 << 5)
105#define MOD_RCLK_256FS (0 << 3)
106#define MOD_RCLK_512FS (1 << 3)
107#define MOD_RCLK_384FS (2 << 3)
108#define MOD_RCLK_768FS (3 << 3)
109#define MOD_RCLK_MASK (3 << 3)
110#define MOD_BCLK_32FS (0 << 1)
111#define MOD_BCLK_48FS (1 << 1)
112#define MOD_BCLK_16FS (2 << 1)
113#define MOD_BCLK_24FS (3 << 1)
114#define MOD_BCLK_MASK (3 << 1)
115#define MOD_8BIT (1 << 0)
116
117#define MOD_CDCLKCON (1 << 12)
118
119#define PSR_PSREN (1 << 15)
120
121#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
122#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
123
124#define FIC_TXFLUSH (1 << 15)
125#define FIC_RXFLUSH (1 << 7)
126#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf)
127#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf)
128#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
129
130#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
131
132struct i2s_dai {
133 /* Platform device for this DAI */
134 struct platform_device *pdev;
135 /* IOREMAP'd SFRs */
136 void __iomem *addr;
137 /* Physical base address of SFRs */
138 u32 base;
139 /* Rate of RCLK source clock */
140 unsigned long rclk_srcrate;
141 /* Frame Clock */
142 unsigned frmclk;
143 /*
144 * Specifically requested RCLK,BCLK by MACHINE Driver.
145 * 0 indicates CPU driver is free to choose any value.
146 */
147 unsigned rfs, bfs;
148 /* I2S Controller's core clock */
149 struct clk *clk;
150 /* Clock for generating I2S signals */
151 struct clk *op_clk;
152 /* Array of clock names for op_clk */
153 const char **src_clk;
154 /* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */
155 struct i2s_dai *pri_dai;
156 /* Pointer to the Secondary_Fifo if it has one, NULL otherwise */
157 struct i2s_dai *sec_dai;
158#define DAI_OPENED (1 << 0) /* Dai is opened */
159#define DAI_MANAGER (1 << 1) /* Dai is the manager */
160 unsigned mode;
161 /* Driver for this DAI */
162 struct snd_soc_dai_driver i2s_dai_drv;
163 /* DMA parameters */
164 struct s3c_dma_params dma_playback;
165 struct s3c_dma_params dma_capture;
166 u32 quirks;
167 u32 suspend_i2smod;
168 u32 suspend_i2scon;
169 u32 suspend_i2spsr;
170};
171
172/* Lock for cross i/f checks */
173static DEFINE_SPINLOCK(lock);
174
175/* If this is the 'overlay' stereo DAI */
176static inline bool is_secondary(struct i2s_dai *i2s)
177{
178 return i2s->pri_dai ? true : false;
179}
180
181/* If operating in SoC-Slave mode */
182static inline bool is_slave(struct i2s_dai *i2s)
183{
184 return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false;
185}
186
187/* If this interface of the controller is transmitting data */
188static inline bool tx_active(struct i2s_dai *i2s)
189{
190 u32 active;
191
192 if (!i2s)
193 return false;
194
195 active = readl(i2s->addr + I2SMOD);
196
197 if (is_secondary(i2s))
198 active &= CON_TXSDMA_ACTIVE;
199 else
200 active &= CON_TXDMA_ACTIVE;
201
202 return active ? true : false;
203}
204
205/* If the other interface of the controller is transmitting data */
206static inline bool other_tx_active(struct i2s_dai *i2s)
207{
208 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
209
210 return tx_active(other);
211}
212
213/* If any interface of the controller is transmitting data */
214static inline bool any_tx_active(struct i2s_dai *i2s)
215{
216 return tx_active(i2s) || other_tx_active(i2s);
217}
218
219/* If this interface of the controller is receiving data */
220static inline bool rx_active(struct i2s_dai *i2s)
221{
222 u32 active;
223
224 if (!i2s)
225 return false;
226
227 active = readl(i2s->addr + I2SMOD) & CON_RXDMA_ACTIVE;
228
229 return active ? true : false;
230}
231
232/* If the other interface of the controller is receiving data */
233static inline bool other_rx_active(struct i2s_dai *i2s)
234{
235 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
236
237 return rx_active(other);
238}
239
240/* If any interface of the controller is receiving data */
241static inline bool any_rx_active(struct i2s_dai *i2s)
242{
243 return rx_active(i2s) || other_rx_active(i2s);
244}
245
246/* If the other DAI is transmitting or receiving data */
247static inline bool other_active(struct i2s_dai *i2s)
248{
249 return other_rx_active(i2s) || other_tx_active(i2s);
250}
251
252/* If this DAI is transmitting or receiving data */
253static inline bool this_active(struct i2s_dai *i2s)
254{
255 return tx_active(i2s) || rx_active(i2s);
256}
257
258/* If the controller is active anyway */
259static inline bool any_active(struct i2s_dai *i2s)
260{
261 return this_active(i2s) || other_active(i2s);
262}
263
264static inline struct i2s_dai *to_info(struct snd_soc_dai *dai)
265{
266 return snd_soc_dai_get_drvdata(dai);
267}
268
269static inline bool is_opened(struct i2s_dai *i2s)
270{
271 if (i2s && (i2s->mode & DAI_OPENED))
272 return true;
273 else
274 return false;
275}
276
277static inline bool is_manager(struct i2s_dai *i2s)
278{
279 if (is_opened(i2s) && (i2s->mode & DAI_MANAGER))
280 return true;
281 else
282 return false;
283}
284
285/* Read RCLK of I2S (in multiples of LRCLK) */
286static inline unsigned get_rfs(struct i2s_dai *i2s)
287{
288 u32 rfs = (readl(i2s->addr + I2SMOD) >> 3) & 0x3;
289
290 switch (rfs) {
291 case 3: return 768;
292 case 2: return 384;
293 case 1: return 512;
294 default: return 256;
295 }
296}
297
298/* Write RCLK of I2S (in multiples of LRCLK) */
299static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
300{
301 u32 mod = readl(i2s->addr + I2SMOD);
302
303 mod &= ~MOD_RCLK_MASK;
304
305 switch (rfs) {
306 case 768:
307 mod |= MOD_RCLK_768FS;
308 break;
309 case 512:
310 mod |= MOD_RCLK_512FS;
311 break;
312 case 384:
313 mod |= MOD_RCLK_384FS;
314 break;
315 default:
316 mod |= MOD_RCLK_256FS;
317 break;
318 }
319
320 writel(mod, i2s->addr + I2SMOD);
321}
322
323/* Read Bit-Clock of I2S (in multiples of LRCLK) */
324static inline unsigned get_bfs(struct i2s_dai *i2s)
325{
326 u32 bfs = (readl(i2s->addr + I2SMOD) >> 1) & 0x3;
327
328 switch (bfs) {
329 case 3: return 24;
330 case 2: return 16;
331 case 1: return 48;
332 default: return 32;
333 }
334}
335
336/* Write Bit-Clock of I2S (in multiples of LRCLK) */
337static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
338{
339 u32 mod = readl(i2s->addr + I2SMOD);
340
341 mod &= ~MOD_BCLK_MASK;
342
343 switch (bfs) {
344 case 48:
345 mod |= MOD_BCLK_48FS;
346 break;
347 case 32:
348 mod |= MOD_BCLK_32FS;
349 break;
350 case 24:
351 mod |= MOD_BCLK_24FS;
352 break;
353 case 16:
354 mod |= MOD_BCLK_16FS;
355 break;
356 default:
357 dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n");
358 return;
359 }
360
361 writel(mod, i2s->addr + I2SMOD);
362}
363
364/* Sample-Size */
365static inline int get_blc(struct i2s_dai *i2s)
366{
367 int blc = readl(i2s->addr + I2SMOD);
368
369 blc = (blc >> 13) & 0x3;
370
371 switch (blc) {
372 case 2: return 24;
373 case 1: return 8;
374 default: return 16;
375 }
376}
377
378/* TX Channel Control */
379static void i2s_txctrl(struct i2s_dai *i2s, int on)
380{
381 void __iomem *addr = i2s->addr;
382 u32 con = readl(addr + I2SCON);
383 u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
384
385 if (on) {
386 con |= CON_ACTIVE;
387 con &= ~CON_TXCH_PAUSE;
388
389 if (is_secondary(i2s)) {
390 con |= CON_TXSDMA_ACTIVE;
391 con &= ~CON_TXSDMA_PAUSE;
392 } else {
393 con |= CON_TXDMA_ACTIVE;
394 con &= ~CON_TXDMA_PAUSE;
395 }
396
397 if (any_rx_active(i2s))
398 mod |= MOD_TXRX;
399 else
400 mod |= MOD_TXONLY;
401 } else {
402 if (is_secondary(i2s)) {
403 con |= CON_TXSDMA_PAUSE;
404 con &= ~CON_TXSDMA_ACTIVE;
405 } else {
406 con |= CON_TXDMA_PAUSE;
407 con &= ~CON_TXDMA_ACTIVE;
408 }
409
410 if (other_tx_active(i2s)) {
411 writel(con, addr + I2SCON);
412 return;
413 }
414
415 con |= CON_TXCH_PAUSE;
416
417 if (any_rx_active(i2s))
418 mod |= MOD_RXONLY;
419 else
420 con &= ~CON_ACTIVE;
421 }
422
423 writel(mod, addr + I2SMOD);
424 writel(con, addr + I2SCON);
425}
426
427/* RX Channel Control */
428static void i2s_rxctrl(struct i2s_dai *i2s, int on)
429{
430 void __iomem *addr = i2s->addr;
431 u32 con = readl(addr + I2SCON);
432 u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
433
434 if (on) {
435 con |= CON_RXDMA_ACTIVE | CON_ACTIVE;
436 con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE);
437
438 if (any_tx_active(i2s))
439 mod |= MOD_TXRX;
440 else
441 mod |= MOD_RXONLY;
442 } else {
443 con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE;
444 con &= ~CON_RXDMA_ACTIVE;
445
446 if (any_tx_active(i2s))
447 mod |= MOD_TXONLY;
448 else
449 con &= ~CON_ACTIVE;
450 }
451
452 writel(mod, addr + I2SMOD);
453 writel(con, addr + I2SCON);
454}
455
456/* Flush FIFO of an interface */
457static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush)
458{
459 void __iomem *fic;
460 u32 val;
461
462 if (!i2s)
463 return;
464
465 if (is_secondary(i2s))
466 fic = i2s->addr + I2SFICS;
467 else
468 fic = i2s->addr + I2SFIC;
469
470 /* Flush the FIFO */
471 writel(readl(fic) | flush, fic);
472
473 /* Be patient */
474 val = msecs_to_loops(1) / 1000; /* 1 usec */
475 while (--val)
476 cpu_relax();
477
478 writel(readl(fic) & ~flush, fic);
479}
480
481static int i2s_set_sysclk(struct snd_soc_dai *dai,
482 int clk_id, unsigned int rfs, int dir)
483{
484 struct i2s_dai *i2s = to_info(dai);
485 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
486 u32 mod = readl(i2s->addr + I2SMOD);
487
488 switch (clk_id) {
489 case SAMSUNG_I2S_CDCLK:
490 /* Shouldn't matter in GATING(CLOCK_IN) mode */
491 if (dir == SND_SOC_CLOCK_IN)
492 rfs = 0;
493
494 if ((rfs && other->rfs && (other->rfs != rfs)) ||
495 (any_active(i2s) &&
496 (((dir == SND_SOC_CLOCK_IN)
497 && !(mod & MOD_CDCLKCON)) ||
498 ((dir == SND_SOC_CLOCK_OUT)
499 && (mod & MOD_CDCLKCON))))) {
500 dev_err(&i2s->pdev->dev,
501 "%s:%d Other DAI busy\n", __func__, __LINE__);
502 return -EAGAIN;
503 }
504
505 if (dir == SND_SOC_CLOCK_IN)
506 mod |= MOD_CDCLKCON;
507 else
508 mod &= ~MOD_CDCLKCON;
509
510 i2s->rfs = rfs;
511 break;
512
513 case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */
514 case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */
515 if ((i2s->quirks & QUIRK_NO_MUXPSR)
516 || (clk_id == SAMSUNG_I2S_RCLKSRC_0))
517 clk_id = 0;
518 else
519 clk_id = 1;
520
521 if (!any_active(i2s)) {
522 if (i2s->op_clk) {
523 if ((clk_id && !(mod & MOD_IMS_SYSMUX)) ||
524 (!clk_id && (mod & MOD_IMS_SYSMUX))) {
525 clk_disable(i2s->op_clk);
526 clk_put(i2s->op_clk);
527 } else {
528 return 0;
529 }
530 }
531
532 i2s->op_clk = clk_get(&i2s->pdev->dev,
533 i2s->src_clk[clk_id]);
534 clk_enable(i2s->op_clk);
535 i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
536
537 /* Over-ride the other's */
538 if (other) {
539 other->op_clk = i2s->op_clk;
540 other->rclk_srcrate = i2s->rclk_srcrate;
541 }
542 } else if ((!clk_id && (mod & MOD_IMS_SYSMUX))
543 || (clk_id && !(mod & MOD_IMS_SYSMUX))) {
544 dev_err(&i2s->pdev->dev,
545 "%s:%d Other DAI busy\n", __func__, __LINE__);
546 return -EAGAIN;
547 } else {
548 /* Call can't be on the active DAI */
549 i2s->op_clk = other->op_clk;
550 i2s->rclk_srcrate = other->rclk_srcrate;
551 return 0;
552 }
553
554 if (clk_id == 0)
555 mod &= ~MOD_IMS_SYSMUX;
556 else
557 mod |= MOD_IMS_SYSMUX;
558 break;
559
560 default:
561 dev_err(&i2s->pdev->dev, "We don't serve that!\n");
562 return -EINVAL;
563 }
564
565 writel(mod, i2s->addr + I2SMOD);
566
567 return 0;
568}
569
570static int i2s_set_fmt(struct snd_soc_dai *dai,
571 unsigned int fmt)
572{
573 struct i2s_dai *i2s = to_info(dai);
574 u32 mod = readl(i2s->addr + I2SMOD);
575 u32 tmp = 0;
576
577 /* Format is priority */
578 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
579 case SND_SOC_DAIFMT_RIGHT_J:
580 tmp |= MOD_LR_RLOW;
581 tmp |= MOD_SDF_MSB;
582 break;
583 case SND_SOC_DAIFMT_LEFT_J:
584 tmp |= MOD_LR_RLOW;
585 tmp |= MOD_SDF_LSB;
586 break;
587 case SND_SOC_DAIFMT_I2S:
588 tmp |= MOD_SDF_IIS;
589 break;
590 default:
591 dev_err(&i2s->pdev->dev, "Format not supported\n");
592 return -EINVAL;
593 }
594
595 /*
596 * INV flag is relative to the FORMAT flag - if set it simply
597 * flips the polarity specified by the Standard
598 */
599 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
600 case SND_SOC_DAIFMT_NB_NF:
601 break;
602 case SND_SOC_DAIFMT_NB_IF:
603 if (tmp & MOD_LR_RLOW)
604 tmp &= ~MOD_LR_RLOW;
605 else
606 tmp |= MOD_LR_RLOW;
607 break;
608 default:
609 dev_err(&i2s->pdev->dev, "Polarity not supported\n");
610 return -EINVAL;
611 }
612
613 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
614 case SND_SOC_DAIFMT_CBM_CFM:
615 tmp |= MOD_SLAVE;
616 break;
617 case SND_SOC_DAIFMT_CBS_CFS:
618 /* Set default source clock in Master mode */
619 if (i2s->rclk_srcrate == 0)
620 i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
621 0, SND_SOC_CLOCK_IN);
622 break;
623 default:
624 dev_err(&i2s->pdev->dev, "master/slave format not supported\n");
625 return -EINVAL;
626 }
627
628 if (any_active(i2s) &&
629 ((mod & (MOD_SDF_MASK | MOD_LR_RLOW
630 | MOD_SLAVE)) != tmp)) {
631 dev_err(&i2s->pdev->dev,
632 "%s:%d Other DAI busy\n", __func__, __LINE__);
633 return -EAGAIN;
634 }
635
636 mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE);
637 mod |= tmp;
638 writel(mod, i2s->addr + I2SMOD);
639
640 return 0;
641}
642
643static int i2s_hw_params(struct snd_pcm_substream *substream,
644 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
645{
646 struct i2s_dai *i2s = to_info(dai);
647 u32 mod = readl(i2s->addr + I2SMOD);
648
649 if (!is_secondary(i2s))
650 mod &= ~(MOD_DC2_EN | MOD_DC1_EN);
651
652 switch (params_channels(params)) {
653 case 6:
654 mod |= MOD_DC2_EN;
655 case 4:
656 mod |= MOD_DC1_EN;
657 break;
658 case 2:
659 break;
660 default:
661 dev_err(&i2s->pdev->dev, "%d channels not supported\n",
662 params_channels(params));
663 return -EINVAL;
664 }
665
666 if (is_secondary(i2s))
667 mod &= ~MOD_BLCS_MASK;
668 else
669 mod &= ~MOD_BLCP_MASK;
670
671 if (is_manager(i2s))
672 mod &= ~MOD_BLC_MASK;
673
674 switch (params_format(params)) {
675 case SNDRV_PCM_FORMAT_S8:
676 if (is_secondary(i2s))
677 mod |= MOD_BLCS_8BIT;
678 else
679 mod |= MOD_BLCP_8BIT;
680 if (is_manager(i2s))
681 mod |= MOD_BLC_8BIT;
682 break;
683 case SNDRV_PCM_FORMAT_S16_LE:
684 if (is_secondary(i2s))
685 mod |= MOD_BLCS_16BIT;
686 else
687 mod |= MOD_BLCP_16BIT;
688 if (is_manager(i2s))
689 mod |= MOD_BLC_16BIT;
690 break;
691 case SNDRV_PCM_FORMAT_S24_LE:
692 if (is_secondary(i2s))
693 mod |= MOD_BLCS_24BIT;
694 else
695 mod |= MOD_BLCP_24BIT;
696 if (is_manager(i2s))
697 mod |= MOD_BLC_24BIT;
698 break;
699 default:
700 dev_err(&i2s->pdev->dev, "Format(%d) not supported\n",
701 params_format(params));
702 return -EINVAL;
703 }
704 writel(mod, i2s->addr + I2SMOD);
705
706 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
707 snd_soc_dai_set_dma_data(dai, substream,
708 (void *)&i2s->dma_playback);
709 else
710 snd_soc_dai_set_dma_data(dai, substream,
711 (void *)&i2s->dma_capture);
712
713 i2s->frmclk = params_rate(params);
714
715 return 0;
716}
717
718/* We set constraints on the substream acc to the version of I2S */
719static int i2s_startup(struct snd_pcm_substream *substream,
720 struct snd_soc_dai *dai)
721{
722 struct i2s_dai *i2s = to_info(dai);
723 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
724 unsigned long flags;
725
726 spin_lock_irqsave(&lock, flags);
727
728 i2s->mode |= DAI_OPENED;
729
730 if (is_manager(other))
731 i2s->mode &= ~DAI_MANAGER;
732 else
733 i2s->mode |= DAI_MANAGER;
734
735 /* Enforce set_sysclk in Master mode */
736 i2s->rclk_srcrate = 0;
737
738 spin_unlock_irqrestore(&lock, flags);
739
740 return 0;
741}
742
743static void i2s_shutdown(struct snd_pcm_substream *substream,
744 struct snd_soc_dai *dai)
745{
746 struct i2s_dai *i2s = to_info(dai);
747 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
748 unsigned long flags;
749
750 spin_lock_irqsave(&lock, flags);
751
752 i2s->mode &= ~DAI_OPENED;
753 i2s->mode &= ~DAI_MANAGER;
754
755 if (is_opened(other))
756 other->mode |= DAI_MANAGER;
757
758 /* Reset any constraint on RFS and BFS */
759 i2s->rfs = 0;
760 i2s->bfs = 0;
761
762 spin_unlock_irqrestore(&lock, flags);
763
764 /* Gate CDCLK by default */
765 if (!is_opened(other))
766 i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
767 0, SND_SOC_CLOCK_IN);
768}
769
770static int config_setup(struct i2s_dai *i2s)
771{
772 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
773 unsigned rfs, bfs, blc;
774 u32 psr;
775
776 blc = get_blc(i2s);
777
778 bfs = i2s->bfs;
779
780 if (!bfs && other)
781 bfs = other->bfs;
782
783 /* Select least possible multiple(2) if no constraint set */
784 if (!bfs)
785 bfs = blc * 2;
786
787 rfs = i2s->rfs;
788
789 if (!rfs && other)
790 rfs = other->rfs;
791
792 if ((rfs == 256 || rfs == 512) && (blc == 24)) {
793 dev_err(&i2s->pdev->dev,
794 "%d-RFS not supported for 24-blc\n", rfs);
795 return -EINVAL;
796 }
797
798 if (!rfs) {
799 if (bfs == 16 || bfs == 32)
800 rfs = 256;
801 else
802 rfs = 384;
803 }
804
805 /* If already setup and running */
806 if (any_active(i2s) && (get_rfs(i2s) != rfs || get_bfs(i2s) != bfs)) {
807 dev_err(&i2s->pdev->dev,
808 "%s:%d Other DAI busy\n", __func__, __LINE__);
809 return -EAGAIN;
810 }
811
812 /* Don't bother RFS, BFS & PSR in Slave mode */
813 if (is_slave(i2s))
814 return 0;
815
816 set_bfs(i2s, bfs);
817 set_rfs(i2s, rfs);
818
819 if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
820 psr = i2s->rclk_srcrate / i2s->frmclk / rfs;
821 writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR);
822 dev_dbg(&i2s->pdev->dev,
823 "RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n",
824 i2s->rclk_srcrate, psr, rfs, bfs);
825 }
826
827 return 0;
828}
829
830static int i2s_trigger(struct snd_pcm_substream *substream,
831 int cmd, struct snd_soc_dai *dai)
832{
833 int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
834 struct snd_soc_pcm_runtime *rtd = substream->private_data;
835 struct i2s_dai *i2s = to_info(rtd->cpu_dai);
836 unsigned long flags;
837
838 switch (cmd) {
839 case SNDRV_PCM_TRIGGER_START:
840 case SNDRV_PCM_TRIGGER_RESUME:
841 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
842 local_irq_save(flags);
843
844 if (capture)
845 i2s_fifo(i2s, FIC_RXFLUSH);
846 else
847 i2s_fifo(i2s, FIC_TXFLUSH);
848
849 if (config_setup(i2s)) {
850 local_irq_restore(flags);
851 return -EINVAL;
852 }
853
854 if (capture)
855 i2s_rxctrl(i2s, 1);
856 else
857 i2s_txctrl(i2s, 1);
858
859 local_irq_restore(flags);
860 break;
861 case SNDRV_PCM_TRIGGER_STOP:
862 case SNDRV_PCM_TRIGGER_SUSPEND:
863 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
864 local_irq_save(flags);
865
866 if (capture)
867 i2s_rxctrl(i2s, 0);
868 else
869 i2s_txctrl(i2s, 0);
870
871 local_irq_restore(flags);
872 break;
873 }
874
875 return 0;
876}
877
878static int i2s_set_clkdiv(struct snd_soc_dai *dai,
879 int div_id, int div)
880{
881 struct i2s_dai *i2s = to_info(dai);
882 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
883
884 switch (div_id) {
885 case SAMSUNG_I2S_DIV_BCLK:
886 if ((any_active(i2s) && div && (get_bfs(i2s) != div))
887 || (other && other->bfs && (other->bfs != div))) {
888 dev_err(&i2s->pdev->dev,
889 "%s:%d Other DAI busy\n", __func__, __LINE__);
890 return -EAGAIN;
891 }
892 i2s->bfs = div;
893 break;
894 default:
895 dev_err(&i2s->pdev->dev,
896 "Invalid clock divider(%d)\n", div_id);
897 return -EINVAL;
898 }
899
900 return 0;
901}
902
903static snd_pcm_sframes_t
904i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
905{
906 struct i2s_dai *i2s = to_info(dai);
907 u32 reg = readl(i2s->addr + I2SFIC);
908 snd_pcm_sframes_t delay;
909
910 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
911 delay = FIC_RXCOUNT(reg);
912 else if (is_secondary(i2s))
913 delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS));
914 else
915 delay = FIC_TXCOUNT(reg);
916
917 return delay;
918}
919
920#ifdef CONFIG_PM
921static int i2s_suspend(struct snd_soc_dai *dai)
922{
923 struct i2s_dai *i2s = to_info(dai);
924
925 if (dai->active) {
926 i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
927 i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
928 i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
929 }
930
931 return 0;
932}
933
934static int i2s_resume(struct snd_soc_dai *dai)
935{
936 struct i2s_dai *i2s = to_info(dai);
937
938 if (dai->active) {
939 writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
940 writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
941 writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
942 }
943
944 return 0;
945}
946#else
947#define i2s_suspend NULL
948#define i2s_resume NULL
949#endif
950
951static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
952{
953 struct i2s_dai *i2s = to_info(dai);
954 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
955
956 if (other && other->clk) /* If this is probe on secondary */
957 goto probe_exit;
958
959 i2s->addr = ioremap(i2s->base, 0x100);
960 if (i2s->addr == NULL) {
961 dev_err(&i2s->pdev->dev, "cannot ioremap registers\n");
962 return -ENXIO;
963 }
964
965 i2s->clk = clk_get(&i2s->pdev->dev, "iis");
966 if (IS_ERR(i2s->clk)) {
967 dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n");
968 iounmap(i2s->addr);
969 return -ENOENT;
970 }
971 clk_enable(i2s->clk);
972
973 if (other) {
974 other->addr = i2s->addr;
975 other->clk = i2s->clk;
976 }
977
978 if (i2s->quirks & QUIRK_NEED_RSTCLR)
979 writel(CON_RSTCLR, i2s->addr + I2SCON);
980
981probe_exit:
982 /* Reset any constraint on RFS and BFS */
983 i2s->rfs = 0;
984 i2s->bfs = 0;
985 i2s_txctrl(i2s, 0);
986 i2s_rxctrl(i2s, 0);
987 i2s_fifo(i2s, FIC_TXFLUSH);
988 i2s_fifo(other, FIC_TXFLUSH);
989 i2s_fifo(i2s, FIC_RXFLUSH);
990
991 /* Gate CDCLK by default */
992 if (!is_opened(other))
993 i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
994 0, SND_SOC_CLOCK_IN);
995
996 return 0;
997}
998
999static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
1000{
1001 struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
1002 struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
1003
1004 if (!other || !other->clk) {
1005
1006 if (i2s->quirks & QUIRK_NEED_RSTCLR)
1007 writel(0, i2s->addr + I2SCON);
1008
1009 clk_disable(i2s->clk);
1010 clk_put(i2s->clk);
1011
1012 iounmap(i2s->addr);
1013 }
1014
1015 i2s->clk = NULL;
1016
1017 return 0;
1018}
1019
1020static struct snd_soc_dai_ops samsung_i2s_dai_ops = {
1021 .trigger = i2s_trigger,
1022 .hw_params = i2s_hw_params,
1023 .set_fmt = i2s_set_fmt,
1024 .set_clkdiv = i2s_set_clkdiv,
1025 .set_sysclk = i2s_set_sysclk,
1026 .startup = i2s_startup,
1027 .shutdown = i2s_shutdown,
1028 .delay = i2s_delay,
1029};
1030
1031#define SAMSUNG_I2S_RATES SNDRV_PCM_RATE_8000_96000
1032
1033#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
1034 SNDRV_PCM_FMTBIT_S16_LE | \
1035 SNDRV_PCM_FMTBIT_S24_LE)
1036
1037static __devinit
1038struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
1039{
1040 struct i2s_dai *i2s;
1041
1042 i2s = kzalloc(sizeof(struct i2s_dai), GFP_KERNEL);
1043 if (i2s == NULL)
1044 return NULL;
1045
1046 i2s->pdev = pdev;
1047 i2s->pri_dai = NULL;
1048 i2s->sec_dai = NULL;
1049 i2s->i2s_dai_drv.symmetric_rates = 1;
1050 i2s->i2s_dai_drv.probe = samsung_i2s_dai_probe;
1051 i2s->i2s_dai_drv.remove = samsung_i2s_dai_remove;
1052 i2s->i2s_dai_drv.ops = &samsung_i2s_dai_ops;
1053 i2s->i2s_dai_drv.suspend = i2s_suspend;
1054 i2s->i2s_dai_drv.resume = i2s_resume;
1055 i2s->i2s_dai_drv.playback.channels_min = 2;
1056 i2s->i2s_dai_drv.playback.channels_max = 2;
1057 i2s->i2s_dai_drv.playback.rates = SAMSUNG_I2S_RATES;
1058 i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS;
1059
1060 if (!sec) {
1061 i2s->i2s_dai_drv.capture.channels_min = 2;
1062 i2s->i2s_dai_drv.capture.channels_max = 2;
1063 i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES;
1064 i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
1065 } else { /* Create a new platform_device for Secondary */
1066 i2s->pdev = platform_device_register_resndata(NULL,
1067 pdev->name, pdev->id + SAMSUNG_I2S_SECOFF,
1068 NULL, 0, NULL, 0);
1069 if (IS_ERR(i2s->pdev)) {
1070 kfree(i2s);
1071 return NULL;
1072 }
1073 }
1074
1075 /* Pre-assign snd_soc_dai_set_drvdata */
1076 dev_set_drvdata(&i2s->pdev->dev, i2s);
1077
1078 return i2s;
1079}
1080
1081static __devinit int samsung_i2s_probe(struct platform_device *pdev)
1082{
1083 u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan;
1084 struct i2s_dai *pri_dai, *sec_dai = NULL;
1085 struct s3c_audio_pdata *i2s_pdata;
1086 struct samsung_i2s *i2s_cfg;
1087 struct resource *res;
1088 u32 regs_base, quirks;
1089 int ret = 0;
1090
1091 /* Call during Seconday interface registration */
1092 if (pdev->id >= SAMSUNG_I2S_SECOFF) {
1093 sec_dai = dev_get_drvdata(&pdev->dev);
1094 snd_soc_register_dai(&sec_dai->pdev->dev,
1095 &sec_dai->i2s_dai_drv);
1096 return 0;
1097 }
1098
1099 i2s_pdata = pdev->dev.platform_data;
1100 if (i2s_pdata == NULL) {
1101 dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
1102 return -EINVAL;
1103 }
1104
1105 res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
1106 if (!res) {
1107 dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n");
1108 return -ENXIO;
1109 }
1110 dma_pl_chan = res->start;
1111
1112 res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
1113 if (!res) {
1114 dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n");
1115 return -ENXIO;
1116 }
1117 dma_cp_chan = res->start;
1118
1119 res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
1120 if (res)
1121 dma_pl_sec_chan = res->start;
1122 else
1123 dma_pl_sec_chan = 0;
1124
1125 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1126 if (!res) {
1127 dev_err(&pdev->dev, "Unable to get I2S SFR address\n");
1128 return -ENXIO;
1129 }
1130
1131 if (!request_mem_region(res->start, resource_size(res),
1132 "samsung-i2s")) {
1133 dev_err(&pdev->dev, "Unable to request SFR region\n");
1134 return -EBUSY;
1135 }
1136 regs_base = res->start;
1137
1138 i2s_cfg = &i2s_pdata->type.i2s;
1139 quirks = i2s_cfg->quirks;
1140
1141 pri_dai = i2s_alloc_dai(pdev, false);
1142 if (!pri_dai) {
1143 dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
1144 ret = -ENOMEM;
1145 goto err1;
1146 }
1147
1148 pri_dai->dma_playback.dma_addr = regs_base + I2STXD;
1149 pri_dai->dma_capture.dma_addr = regs_base + I2SRXD;
1150 pri_dai->dma_playback.client =
1151 (struct s3c2410_dma_client *)&pri_dai->dma_playback;
1152 pri_dai->dma_capture.client =
1153 (struct s3c2410_dma_client *)&pri_dai->dma_capture;
1154 pri_dai->dma_playback.channel = dma_pl_chan;
1155 pri_dai->dma_capture.channel = dma_cp_chan;
1156 pri_dai->src_clk = i2s_cfg->src_clk;
1157 pri_dai->dma_playback.dma_size = 4;
1158 pri_dai->dma_capture.dma_size = 4;
1159 pri_dai->base = regs_base;
1160 pri_dai->quirks = quirks;
1161
1162 if (quirks & QUIRK_PRI_6CHAN)
1163 pri_dai->i2s_dai_drv.playback.channels_max = 6;
1164
1165 if (quirks & QUIRK_SEC_DAI) {
1166 sec_dai = i2s_alloc_dai(pdev, true);
1167 if (!sec_dai) {
1168 dev_err(&pdev->dev, "Unable to alloc I2S_sec\n");
1169 ret = -ENOMEM;
1170 goto err2;
1171 }
1172 sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
1173 sec_dai->dma_playback.client =
1174 (struct s3c2410_dma_client *)&sec_dai->dma_playback;
1175 /* Use iDMA always if SysDMA not provided */
1176 sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1;
1177 sec_dai->src_clk = i2s_cfg->src_clk;
1178 sec_dai->dma_playback.dma_size = 4;
1179 sec_dai->base = regs_base;
1180 sec_dai->quirks = quirks;
1181 sec_dai->pri_dai = pri_dai;
1182 pri_dai->sec_dai = sec_dai;
1183 }
1184
1185 if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
1186 dev_err(&pdev->dev, "Unable to configure gpio\n");
1187 ret = -EINVAL;
1188 goto err3;
1189 }
1190
1191 snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv);
1192
1193 return 0;
1194err3:
1195 kfree(sec_dai);
1196err2:
1197 kfree(pri_dai);
1198err1:
1199 release_mem_region(regs_base, resource_size(res));
1200
1201 return ret;
1202}
1203
1204static __devexit int samsung_i2s_remove(struct platform_device *pdev)
1205{
1206 struct i2s_dai *i2s, *other;
1207
1208 i2s = dev_get_drvdata(&pdev->dev);
1209 other = i2s->pri_dai ? : i2s->sec_dai;
1210
1211 if (other) {
1212 other->pri_dai = NULL;
1213 other->sec_dai = NULL;
1214 } else {
1215 struct resource *res;
1216 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1217 if (res)
1218 release_mem_region(res->start, resource_size(res));
1219 }
1220
1221 i2s->pri_dai = NULL;
1222 i2s->sec_dai = NULL;
1223
1224 kfree(i2s);
1225
1226 snd_soc_unregister_dai(&pdev->dev);
1227
1228 return 0;
1229}
1230
1231static struct platform_driver samsung_i2s_driver = {
1232 .probe = samsung_i2s_probe,
1233 .remove = samsung_i2s_remove,
1234 .driver = {
1235 .name = "samsung-i2s",
1236 .owner = THIS_MODULE,
1237 },
1238};
1239
1240static int __init samsung_i2s_init(void)
1241{
1242 return platform_driver_register(&samsung_i2s_driver);
1243}
1244module_init(samsung_i2s_init);
1245
1246static void __exit samsung_i2s_exit(void)
1247{
1248 platform_driver_unregister(&samsung_i2s_driver);
1249}
1250module_exit(samsung_i2s_exit);
1251
1252/* Module information */
1253MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
1254MODULE_DESCRIPTION("Samsung I2S Interface");
1255MODULE_ALIAS("platform:samsung-i2s");
1256MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/i2s.h b/sound/soc/samsung/i2s.h
new file mode 100644
index 000000000000..8e15f6a616d1
--- /dev/null
+++ b/sound/soc/samsung/i2s.h
@@ -0,0 +1,29 @@
1/* sound/soc/samsung/i2s.h
2 *
3 * ALSA SoC Audio Layer - Samsung I2S Controller driver
4 *
5 * Copyright (c) 2010 Samsung Electronics Co. Ltd.
6 * Jaswinder Singh <jassi.brar@samsung.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#ifndef __SND_SOC_SAMSUNG_I2S_H
14#define __SND_SOC_SAMSUNG_I2S_H
15
16/*
17 * Maximum number of I2S blocks that any SoC can have.
18 * The secondary interface of a CPU dai(if there exists any),
19 * is indexed at [cpu-dai's ID + SAMSUNG_I2S_SECOFF]
20 */
21#define SAMSUNG_I2S_SECOFF 4
22
23#define SAMSUNG_I2S_DIV_BCLK 1
24
25#define SAMSUNG_I2S_RCLKSRC_0 0
26#define SAMSUNG_I2S_RCLKSRC_1 1
27#define SAMSUNG_I2S_CDCLK 2
28
29#endif /* __SND_SOC_SAMSUNG_I2S_H */
diff --git a/sound/soc/samsung/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c
new file mode 100644
index 000000000000..08802520e014
--- /dev/null
+++ b/sound/soc/samsung/jive_wm8750.c
@@ -0,0 +1,191 @@
1/* sound/soc/samsung/jive_wm8750.c
2 *
3 * Copyright 2007,2008 Simtec Electronics
4 *
5 * Based on sound/soc/pxa/spitz.c
6 * Copyright 2005 Wolfson Microelectronics PLC.
7 * Copyright 2005 Openedhand Ltd.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <linux/module.h>
15#include <linux/moduleparam.h>
16#include <linux/timer.h>
17#include <linux/interrupt.h>
18#include <linux/platform_device.h>
19#include <linux/clk.h>
20
21#include <sound/core.h>
22#include <sound/pcm.h>
23#include <sound/soc.h>
24
25#include <asm/mach-types.h>
26
27#include "dma.h"
28#include "s3c2412-i2s.h"
29
30#include "../codecs/wm8750.h"
31
32static const struct snd_soc_dapm_route audio_map[] = {
33 { "Headphone Jack", NULL, "LOUT1" },
34 { "Headphone Jack", NULL, "ROUT1" },
35 { "Internal Speaker", NULL, "LOUT2" },
36 { "Internal Speaker", NULL, "ROUT2" },
37 { "LINPUT1", NULL, "Line Input" },
38 { "RINPUT1", NULL, "Line Input" },
39};
40
41static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
42 SND_SOC_DAPM_HP("Headphone Jack", NULL),
43 SND_SOC_DAPM_SPK("Internal Speaker", NULL),
44 SND_SOC_DAPM_LINE("Line In", NULL),
45};
46
47static int jive_hw_params(struct snd_pcm_substream *substream,
48 struct snd_pcm_hw_params *params)
49{
50 struct snd_soc_pcm_runtime *rtd = substream->private_data;
51 struct snd_soc_dai *codec_dai = rtd->codec_dai;
52 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
53 struct s3c_i2sv2_rate_calc div;
54 unsigned int clk = 0;
55 int ret = 0;
56
57 switch (params_rate(params)) {
58 case 8000:
59 case 16000:
60 case 48000:
61 case 96000:
62 clk = 12288000;
63 break;
64 case 11025:
65 case 22050:
66 case 44100:
67 clk = 11289600;
68 break;
69 }
70
71 s3c_i2sv2_iis_calc_rate(&div, NULL, params_rate(params),
72 s3c_i2sv2_get_clock(cpu_dai));
73
74 /* set codec DAI configuration */
75 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
76 SND_SOC_DAIFMT_NB_NF |
77 SND_SOC_DAIFMT_CBS_CFS);
78 if (ret < 0)
79 return ret;
80
81 /* set cpu DAI configuration */
82 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
83 SND_SOC_DAIFMT_NB_NF |
84 SND_SOC_DAIFMT_CBS_CFS);
85 if (ret < 0)
86 return ret;
87
88 /* set the codec system clock for DAC and ADC */
89 ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
90 SND_SOC_CLOCK_IN);
91 if (ret < 0)
92 return ret;
93
94 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_RCLK, div.fs_div);
95 if (ret < 0)
96 return ret;
97
98 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_PRESCALER,
99 div.clk_div - 1);
100 if (ret < 0)
101 return ret;
102
103 return 0;
104}
105
106static struct snd_soc_ops jive_ops = {
107 .hw_params = jive_hw_params,
108};
109
110static int jive_wm8750_init(struct snd_soc_pcm_runtime *rtd)
111{
112 struct snd_soc_codec *codec = rtd->codec;
113 struct snd_soc_dapm_context *dapm = &codec->dapm;
114 int err;
115
116 /* These endpoints are not being used. */
117 snd_soc_dapm_nc_pin(dapm, "LINPUT2");
118 snd_soc_dapm_nc_pin(dapm, "RINPUT2");
119 snd_soc_dapm_nc_pin(dapm, "LINPUT3");
120 snd_soc_dapm_nc_pin(dapm, "RINPUT3");
121 snd_soc_dapm_nc_pin(dapm, "OUT3");
122 snd_soc_dapm_nc_pin(dapm, "MONO");
123
124 /* Add jive specific widgets */
125 err = snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets,
126 ARRAY_SIZE(wm8750_dapm_widgets));
127 if (err) {
128 printk(KERN_ERR "%s: failed to add widgets (%d)\n",
129 __func__, err);
130 return err;
131 }
132
133 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
134 snd_soc_dapm_sync(dapm);
135
136 return 0;
137}
138
139static struct snd_soc_dai_link jive_dai = {
140 .name = "wm8750",
141 .stream_name = "WM8750",
142 .cpu_dai_name = "s3c2412-i2s",
143 .codec_dai_name = "wm8750-hifi",
144 .platform_name = "samsung-audio",
145 .codec_name = "wm8750-codec.0-0x1a",
146 .init = jive_wm8750_init,
147 .ops = &jive_ops,
148};
149
150/* jive audio machine driver */
151static struct snd_soc_card snd_soc_machine_jive = {
152 .name = "Jive",
153 .dai_link = &jive_dai,
154 .num_links = 1,
155};
156
157static struct platform_device *jive_snd_device;
158
159static int __init jive_init(void)
160{
161 int ret;
162
163 if (!machine_is_jive())
164 return 0;
165
166 printk("JIVE WM8750 Audio support\n");
167
168 jive_snd_device = platform_device_alloc("soc-audio", -1);
169 if (!jive_snd_device)
170 return -ENOMEM;
171
172 platform_set_drvdata(jive_snd_device, &snd_soc_machine_jive);
173 ret = platform_device_add(jive_snd_device);
174
175 if (ret)
176 platform_device_put(jive_snd_device);
177
178 return ret;
179}
180
181static void __exit jive_exit(void)
182{
183 platform_device_unregister(jive_snd_device);
184}
185
186module_init(jive_init);
187module_exit(jive_exit);
188
189MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
190MODULE_DESCRIPTION("ALSA SoC Jive Audio support");
191MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/lm4857.h b/sound/soc/samsung/lm4857.h
new file mode 100644
index 000000000000..0cf5b7011d6f
--- /dev/null
+++ b/sound/soc/samsung/lm4857.h
@@ -0,0 +1,32 @@
1/*
2 * lm4857.h -- ALSA Soc Audio Layer
3 *
4 * Copyright 2007 Wolfson Microelectronics PLC.
5 * Author: Graeme Gregory
6 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * Revision history
14 * 18th Jun 2007 Initial version.
15 */
16
17#ifndef LM4857_H_
18#define LM4857_H_
19
20/* The register offsets in the cache array */
21#define LM4857_MVOL 0
22#define LM4857_LVOL 1
23#define LM4857_RVOL 2
24#define LM4857_CTRL 3
25
26/* the shifts required to set these bits */
27#define LM4857_3D 5
28#define LM4857_WAKEUP 5
29#define LM4857_EPGAIN 4
30
31#endif /*LM4857_H_*/
32
diff --git a/sound/soc/samsung/ln2440sbc_alc650.c b/sound/soc/samsung/ln2440sbc_alc650.c
new file mode 100644
index 000000000000..a2bb34def740
--- /dev/null
+++ b/sound/soc/samsung/ln2440sbc_alc650.c
@@ -0,0 +1,77 @@
1/*
2 * SoC audio for ln2440sbc
3 *
4 * Copyright 2007 KonekTel, a.s.
5 * Author: Ivan Kuten
6 * ivan.kuten@promwad.com
7 *
8 * Heavily based on smdk2443_wm9710.c
9 * Copyright 2007 Wolfson Microelectronics PLC.
10 * Author: Graeme Gregory
11 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/device.h>
21#include <sound/core.h>
22#include <sound/pcm.h>
23#include <sound/soc.h>
24
25#include "dma.h"
26#include "ac97.h"
27
28static struct snd_soc_card ln2440sbc;
29
30static struct snd_soc_dai_link ln2440sbc_dai[] = {
31{
32 .name = "AC97",
33 .stream_name = "AC97 HiFi",
34 .cpu_dai_name = "samsung-ac97",
35 .codec_dai_name = "ac97-hifi",
36 .codec_name = "ac97-codec",
37 .platform_name = "samsung-audio",
38},
39};
40
41static struct snd_soc_card ln2440sbc = {
42 .name = "LN2440SBC",
43 .dai_link = ln2440sbc_dai,
44 .num_links = ARRAY_SIZE(ln2440sbc_dai),
45};
46
47static struct platform_device *ln2440sbc_snd_ac97_device;
48
49static int __init ln2440sbc_init(void)
50{
51 int ret;
52
53 ln2440sbc_snd_ac97_device = platform_device_alloc("soc-audio", -1);
54 if (!ln2440sbc_snd_ac97_device)
55 return -ENOMEM;
56
57 platform_set_drvdata(ln2440sbc_snd_ac97_device, &ln2440sbc);
58 ret = platform_device_add(ln2440sbc_snd_ac97_device);
59
60 if (ret)
61 platform_device_put(ln2440sbc_snd_ac97_device);
62
63 return ret;
64}
65
66static void __exit ln2440sbc_exit(void)
67{
68 platform_device_unregister(ln2440sbc_snd_ac97_device);
69}
70
71module_init(ln2440sbc_init);
72module_exit(ln2440sbc_exit);
73
74/* Module information */
75MODULE_AUTHOR("Ivan Kuten");
76MODULE_DESCRIPTION("ALSA SoC ALC650 LN2440SBC");
77MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/neo1973_gta02_wm8753.c b/sound/soc/samsung/neo1973_gta02_wm8753.c
new file mode 100644
index 000000000000..920e8d1f8841
--- /dev/null
+++ b/sound/soc/samsung/neo1973_gta02_wm8753.c
@@ -0,0 +1,504 @@
1/*
2 * neo1973_gta02_wm8753.c -- SoC audio for Openmoko Freerunner(GTA02)
3 *
4 * Copyright 2007 Openmoko Inc
5 * Author: Graeme Gregory <graeme@openmoko.org>
6 * Copyright 2007 Wolfson Microelectronics PLC.
7 * Author: Graeme Gregory <linux@wolfsonmicro.com>
8 * Copyright 2009 Wolfson Microelectronics
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 */
15
16#include <linux/module.h>
17#include <linux/moduleparam.h>
18#include <linux/timer.h>
19#include <linux/interrupt.h>
20#include <linux/platform_device.h>
21#include <linux/gpio.h>
22#include <sound/core.h>
23#include <sound/pcm.h>
24#include <sound/soc.h>
25
26#include <asm/mach-types.h>
27
28#include <plat/regs-iis.h>
29
30#include <mach/regs-clock.h>
31#include <asm/io.h>
32#include <mach/gta02.h>
33#include "../codecs/wm8753.h"
34#include "dma.h"
35#include "s3c24xx-i2s.h"
36
37static struct snd_soc_card neo1973_gta02;
38
39static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream,
40 struct snd_pcm_hw_params *params)
41{
42 struct snd_soc_pcm_runtime *rtd = substream->private_data;
43 struct snd_soc_dai *codec_dai = rtd->codec_dai;
44 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
45 unsigned int pll_out = 0, bclk = 0;
46 int ret = 0;
47 unsigned long iis_clkrate;
48
49 iis_clkrate = s3c24xx_i2s_get_clockrate();
50
51 switch (params_rate(params)) {
52 case 8000:
53 case 16000:
54 pll_out = 12288000;
55 break;
56 case 48000:
57 bclk = WM8753_BCLK_DIV_4;
58 pll_out = 12288000;
59 break;
60 case 96000:
61 bclk = WM8753_BCLK_DIV_2;
62 pll_out = 12288000;
63 break;
64 case 11025:
65 bclk = WM8753_BCLK_DIV_16;
66 pll_out = 11289600;
67 break;
68 case 22050:
69 bclk = WM8753_BCLK_DIV_8;
70 pll_out = 11289600;
71 break;
72 case 44100:
73 bclk = WM8753_BCLK_DIV_4;
74 pll_out = 11289600;
75 break;
76 case 88200:
77 bclk = WM8753_BCLK_DIV_2;
78 pll_out = 11289600;
79 break;
80 }
81
82 /* set codec DAI configuration */
83 ret = snd_soc_dai_set_fmt(codec_dai,
84 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
85 SND_SOC_DAIFMT_CBM_CFM);
86 if (ret < 0)
87 return ret;
88
89 /* set cpu DAI configuration */
90 ret = snd_soc_dai_set_fmt(cpu_dai,
91 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
92 SND_SOC_DAIFMT_CBM_CFM);
93 if (ret < 0)
94 return ret;
95
96 /* set the codec system clock for DAC and ADC */
97 ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
98 SND_SOC_CLOCK_IN);
99 if (ret < 0)
100 return ret;
101
102 /* set MCLK division for sample rate */
103 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
104 S3C2410_IISMOD_32FS);
105 if (ret < 0)
106 return ret;
107
108 /* set codec BCLK division for sample rate */
109 ret = snd_soc_dai_set_clkdiv(codec_dai,
110 WM8753_BCLKDIV, bclk);
111 if (ret < 0)
112 return ret;
113
114 /* set prescaler division for sample rate */
115 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
116 S3C24XX_PRESCALE(4, 4));
117 if (ret < 0)
118 return ret;
119
120 /* codec PLL input is PCLK/4 */
121 ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
122 iis_clkrate / 4, pll_out);
123 if (ret < 0)
124 return ret;
125
126 return 0;
127}
128
129static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream)
130{
131 struct snd_soc_pcm_runtime *rtd = substream->private_data;
132 struct snd_soc_dai *codec_dai = rtd->codec_dai;
133
134 /* disable the PLL */
135 return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
136}
137
138/*
139 * Neo1973 WM8753 HiFi DAI opserations.
140 */
141static struct snd_soc_ops neo1973_gta02_hifi_ops = {
142 .hw_params = neo1973_gta02_hifi_hw_params,
143 .hw_free = neo1973_gta02_hifi_hw_free,
144};
145
146static int neo1973_gta02_voice_hw_params(
147 struct snd_pcm_substream *substream,
148 struct snd_pcm_hw_params *params)
149{
150 struct snd_soc_pcm_runtime *rtd = substream->private_data;
151 struct snd_soc_dai *codec_dai = rtd->codec_dai;
152 unsigned int pcmdiv = 0;
153 int ret = 0;
154 unsigned long iis_clkrate;
155
156 iis_clkrate = s3c24xx_i2s_get_clockrate();
157
158 if (params_rate(params) != 8000)
159 return -EINVAL;
160 if (params_channels(params) != 1)
161 return -EINVAL;
162
163 pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
164
165 /* todo: gg check mode (DSP_B) against CSR datasheet */
166 /* set codec DAI configuration */
167 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
168 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
169 if (ret < 0)
170 return ret;
171
172 /* set the codec system clock for DAC and ADC */
173 ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK,
174 12288000, SND_SOC_CLOCK_IN);
175 if (ret < 0)
176 return ret;
177
178 /* set codec PCM division for sample rate */
179 ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV,
180 pcmdiv);
181 if (ret < 0)
182 return ret;
183
184 /* configure and enable PLL for 12.288MHz output */
185 ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
186 iis_clkrate / 4, 12288000);
187 if (ret < 0)
188 return ret;
189
190 return 0;
191}
192
193static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream)
194{
195 struct snd_soc_pcm_runtime *rtd = substream->private_data;
196 struct snd_soc_dai *codec_dai = rtd->codec_dai;
197
198 /* disable the PLL */
199 return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
200}
201
202static struct snd_soc_ops neo1973_gta02_voice_ops = {
203 .hw_params = neo1973_gta02_voice_hw_params,
204 .hw_free = neo1973_gta02_voice_hw_free,
205};
206
207#define LM4853_AMP 1
208#define LM4853_SPK 2
209
210static u8 lm4853_state;
211
212/* This has no effect, it exists only to maintain compatibility with
213 * existing ALSA state files.
214 */
215static int lm4853_set_state(struct snd_kcontrol *kcontrol,
216 struct snd_ctl_elem_value *ucontrol)
217{
218 int val = ucontrol->value.integer.value[0];
219
220 if (val)
221 lm4853_state |= LM4853_AMP;
222 else
223 lm4853_state &= ~LM4853_AMP;
224
225 return 0;
226}
227
228static int lm4853_get_state(struct snd_kcontrol *kcontrol,
229 struct snd_ctl_elem_value *ucontrol)
230{
231 ucontrol->value.integer.value[0] = lm4853_state & LM4853_AMP;
232
233 return 0;
234}
235
236static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
237 struct snd_ctl_elem_value *ucontrol)
238{
239 int val = ucontrol->value.integer.value[0];
240
241 if (val) {
242 lm4853_state |= LM4853_SPK;
243 gpio_set_value(GTA02_GPIO_HP_IN, 0);
244 } else {
245 lm4853_state &= ~LM4853_SPK;
246 gpio_set_value(GTA02_GPIO_HP_IN, 1);
247 }
248
249 return 0;
250}
251
252static int lm4853_get_spk(struct snd_kcontrol *kcontrol,
253 struct snd_ctl_elem_value *ucontrol)
254{
255 ucontrol->value.integer.value[0] = (lm4853_state & LM4853_SPK) >> 1;
256
257 return 0;
258}
259
260static int lm4853_event(struct snd_soc_dapm_widget *w,
261 struct snd_kcontrol *k,
262 int event)
263{
264 gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event));
265
266 return 0;
267}
268
269static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
270 SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
271 SND_SOC_DAPM_LINE("GSM Line Out", NULL),
272 SND_SOC_DAPM_LINE("GSM Line In", NULL),
273 SND_SOC_DAPM_MIC("Headset Mic", NULL),
274 SND_SOC_DAPM_MIC("Handset Mic", NULL),
275 SND_SOC_DAPM_SPK("Handset Spk", NULL),
276};
277
278
279/* example machine audio_mapnections */
280static const struct snd_soc_dapm_route audio_map[] = {
281
282 /* Connections to the lm4853 amp */
283 {"Stereo Out", NULL, "LOUT1"},
284 {"Stereo Out", NULL, "ROUT1"},
285
286 /* Connections to the GSM Module */
287 {"GSM Line Out", NULL, "MONO1"},
288 {"GSM Line Out", NULL, "MONO2"},
289 {"RXP", NULL, "GSM Line In"},
290 {"RXN", NULL, "GSM Line In"},
291
292 /* Connections to Headset */
293 {"MIC1", NULL, "Mic Bias"},
294 {"Mic Bias", NULL, "Headset Mic"},
295
296 /* Call Mic */
297 {"MIC2", NULL, "Mic Bias"},
298 {"MIC2N", NULL, "Mic Bias"},
299 {"Mic Bias", NULL, "Handset Mic"},
300
301 /* Call Speaker */
302 {"Handset Spk", NULL, "LOUT2"},
303 {"Handset Spk", NULL, "ROUT2"},
304
305 /* Connect the ALC pins */
306 {"ACIN", NULL, "ACOP"},
307};
308
309static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = {
310 SOC_DAPM_PIN_SWITCH("Stereo Out"),
311 SOC_DAPM_PIN_SWITCH("GSM Line Out"),
312 SOC_DAPM_PIN_SWITCH("GSM Line In"),
313 SOC_DAPM_PIN_SWITCH("Headset Mic"),
314 SOC_DAPM_PIN_SWITCH("Handset Mic"),
315 SOC_DAPM_PIN_SWITCH("Handset Spk"),
316
317 /* This has no effect, it exists only to maintain compatibility with
318 * existing ALSA state files.
319 */
320 SOC_SINGLE_EXT("Amp State Switch", 6, 0, 1, 0,
321 lm4853_get_state,
322 lm4853_set_state),
323 SOC_SINGLE_EXT("Amp Spk Switch", 7, 0, 1, 0,
324 lm4853_get_spk,
325 lm4853_set_spk),
326};
327
328/*
329 * This is an example machine initialisation for a wm8753 connected to a
330 * neo1973 GTA02.
331 */
332static int neo1973_gta02_wm8753_init(struct snd_soc_pcm_runtime *rtd)
333{
334 struct snd_soc_codec *codec = rtd->codec;
335 struct snd_soc_dapm_context *dapm = &codec->dapm;
336 int err;
337
338 /* set up NC codec pins */
339 snd_soc_dapm_nc_pin(dapm, "OUT3");
340 snd_soc_dapm_nc_pin(dapm, "OUT4");
341 snd_soc_dapm_nc_pin(dapm, "LINE1");
342 snd_soc_dapm_nc_pin(dapm, "LINE2");
343
344 /* Add neo1973 gta02 specific widgets */
345 snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets,
346 ARRAY_SIZE(wm8753_dapm_widgets));
347
348 /* add neo1973 gta02 specific controls */
349 err = snd_soc_add_controls(codec, wm8753_neo1973_gta02_controls,
350 ARRAY_SIZE(wm8753_neo1973_gta02_controls));
351
352 if (err < 0)
353 return err;
354
355 /* set up neo1973 gta02 specific audio path audio_map */
356 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
357
358 /* set endpoints to default off mode */
359 snd_soc_dapm_disable_pin(dapm, "Stereo Out");
360 snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
361 snd_soc_dapm_disable_pin(dapm, "GSM Line In");
362 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
363 snd_soc_dapm_disable_pin(dapm, "Handset Mic");
364 snd_soc_dapm_disable_pin(dapm, "Handset Spk");
365
366 /* allow audio paths from the GSM modem to run during suspend */
367 snd_soc_dapm_ignore_suspend(dapm, "Stereo Out");
368 snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out");
369 snd_soc_dapm_ignore_suspend(dapm, "GSM Line In");
370 snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
371 snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
372 snd_soc_dapm_ignore_suspend(dapm, "Handset Spk");
373
374 snd_soc_dapm_sync(dapm);
375
376 return 0;
377}
378
379/*
380 * BT Codec DAI
381 */
382static struct snd_soc_dai_driver bt_dai = {
383 .name = "bluetooth-dai",
384 .playback = {
385 .channels_min = 1,
386 .channels_max = 1,
387 .rates = SNDRV_PCM_RATE_8000,
388 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
389 .capture = {
390 .channels_min = 1,
391 .channels_max = 1,
392 .rates = SNDRV_PCM_RATE_8000,
393 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
394};
395
396static struct snd_soc_dai_link neo1973_gta02_dai[] = {
397{ /* Hifi Playback - for similatious use with voice below */
398 .name = "WM8753",
399 .stream_name = "WM8753 HiFi",
400 .cpu_dai_name = "s3c24xx-i2s",
401 .codec_dai_name = "wm8753-hifi",
402 .init = neo1973_gta02_wm8753_init,
403 .platform_name = "samsung-audio",
404 .codec_name = "wm8753-codec.0-0x1a",
405 .ops = &neo1973_gta02_hifi_ops,
406},
407{ /* Voice via BT */
408 .name = "Bluetooth",
409 .stream_name = "Voice",
410 .cpu_dai_name = "bluetooth-dai",
411 .codec_dai_name = "wm8753-voice",
412 .ops = &neo1973_gta02_voice_ops,
413 .codec_name = "wm8753-codec.0-0x1a",
414 .platform_name = "samsung-audio",
415},
416};
417
418static struct snd_soc_card neo1973_gta02 = {
419 .name = "neo1973-gta02",
420 .dai_link = neo1973_gta02_dai,
421 .num_links = ARRAY_SIZE(neo1973_gta02_dai),
422};
423
424static struct platform_device *neo1973_gta02_snd_device;
425
426static int __init neo1973_gta02_init(void)
427{
428 int ret;
429
430 if (!machine_is_neo1973_gta02()) {
431 printk(KERN_INFO
432 "Only GTA02 is supported by this ASoC driver\n");
433 return -ENODEV;
434 }
435
436 neo1973_gta02_snd_device = platform_device_alloc("soc-audio", -1);
437 if (!neo1973_gta02_snd_device)
438 return -ENOMEM;
439
440 /* register bluetooth DAI here */
441 ret = snd_soc_register_dai(&neo1973_gta02_snd_device->dev, -1, &bt_dai);
442 if (ret) {
443 platform_device_put(neo1973_gta02_snd_device);
444 return ret;
445 }
446
447 platform_set_drvdata(neo1973_gta02_snd_device, &neo1973_gta02);
448 ret = platform_device_add(neo1973_gta02_snd_device);
449
450 if (ret) {
451 platform_device_put(neo1973_gta02_snd_device);
452 return ret;
453 }
454
455 /* Initialise GPIOs used by amp */
456 ret = gpio_request(GTA02_GPIO_HP_IN, "GTA02_HP_IN");
457 if (ret) {
458 pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_HP_IN);
459 goto err_unregister_device;
460 }
461
462 ret = gpio_direction_output(GTA02_GPIO_HP_IN, 1);
463 if (ret) {
464 pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_HP_IN);
465 goto err_free_gpio_hp_in;
466 }
467
468 ret = gpio_request(GTA02_GPIO_AMP_SHUT, "GTA02_AMP_SHUT");
469 if (ret) {
470 pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_AMP_SHUT);
471 goto err_free_gpio_hp_in;
472 }
473
474 ret = gpio_direction_output(GTA02_GPIO_AMP_SHUT, 1);
475 if (ret) {
476 pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_AMP_SHUT);
477 goto err_free_gpio_amp_shut;
478 }
479
480 return 0;
481
482err_free_gpio_amp_shut:
483 gpio_free(GTA02_GPIO_AMP_SHUT);
484err_free_gpio_hp_in:
485 gpio_free(GTA02_GPIO_HP_IN);
486err_unregister_device:
487 platform_device_unregister(neo1973_gta02_snd_device);
488 return ret;
489}
490module_init(neo1973_gta02_init);
491
492static void __exit neo1973_gta02_exit(void)
493{
494 snd_soc_unregister_dai(&neo1973_gta02_snd_device->dev, -1);
495 platform_device_unregister(neo1973_gta02_snd_device);
496 gpio_free(GTA02_GPIO_HP_IN);
497 gpio_free(GTA02_GPIO_AMP_SHUT);
498}
499module_exit(neo1973_gta02_exit);
500
501/* Module information */
502MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org");
503MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 GTA02");
504MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c
new file mode 100644
index 000000000000..c7a24514beb5
--- /dev/null
+++ b/sound/soc/samsung/neo1973_wm8753.c
@@ -0,0 +1,706 @@
1/*
2 * neo1973_wm8753.c -- SoC audio for Neo1973
3 *
4 * Copyright 2007 Wolfson Microelectronics PLC.
5 * Author: Graeme Gregory
6 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/timer.h>
18#include <linux/interrupt.h>
19#include <linux/platform_device.h>
20#include <linux/i2c.h>
21#include <sound/core.h>
22#include <sound/pcm.h>
23#include <sound/soc.h>
24#include <sound/tlv.h>
25
26#include <asm/mach-types.h>
27#include <asm/hardware/scoop.h>
28#include <mach/regs-clock.h>
29#include <mach/regs-gpio.h>
30#include <mach/hardware.h>
31#include <linux/io.h>
32#include <mach/spi-gpio.h>
33
34#include <plat/regs-iis.h>
35
36#include "../codecs/wm8753.h"
37#include "lm4857.h"
38#include "dma.h"
39#include "s3c24xx-i2s.h"
40
41/* define the scenarios */
42#define NEO_AUDIO_OFF 0
43#define NEO_GSM_CALL_AUDIO_HANDSET 1
44#define NEO_GSM_CALL_AUDIO_HEADSET 2
45#define NEO_GSM_CALL_AUDIO_BLUETOOTH 3
46#define NEO_STEREO_TO_SPEAKERS 4
47#define NEO_STEREO_TO_HEADPHONES 5
48#define NEO_CAPTURE_HANDSET 6
49#define NEO_CAPTURE_HEADSET 7
50#define NEO_CAPTURE_BLUETOOTH 8
51
52static struct snd_soc_card neo1973;
53static struct i2c_client *i2c;
54
55static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
56 struct snd_pcm_hw_params *params)
57{
58 struct snd_soc_pcm_runtime *rtd = substream->private_data;
59 struct snd_soc_dai *codec_dai = rtd->codec_dai;
60 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
61 unsigned int pll_out = 0, bclk = 0;
62 int ret = 0;
63 unsigned long iis_clkrate;
64
65 pr_debug("Entered %s\n", __func__);
66
67 iis_clkrate = s3c24xx_i2s_get_clockrate();
68
69 switch (params_rate(params)) {
70 case 8000:
71 case 16000:
72 pll_out = 12288000;
73 break;
74 case 48000:
75 bclk = WM8753_BCLK_DIV_4;
76 pll_out = 12288000;
77 break;
78 case 96000:
79 bclk = WM8753_BCLK_DIV_2;
80 pll_out = 12288000;
81 break;
82 case 11025:
83 bclk = WM8753_BCLK_DIV_16;
84 pll_out = 11289600;
85 break;
86 case 22050:
87 bclk = WM8753_BCLK_DIV_8;
88 pll_out = 11289600;
89 break;
90 case 44100:
91 bclk = WM8753_BCLK_DIV_4;
92 pll_out = 11289600;
93 break;
94 case 88200:
95 bclk = WM8753_BCLK_DIV_2;
96 pll_out = 11289600;
97 break;
98 }
99
100 /* set codec DAI configuration */
101 ret = snd_soc_dai_set_fmt(codec_dai,
102 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
103 SND_SOC_DAIFMT_CBM_CFM);
104 if (ret < 0)
105 return ret;
106
107 /* set cpu DAI configuration */
108 ret = snd_soc_dai_set_fmt(cpu_dai,
109 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
110 SND_SOC_DAIFMT_CBM_CFM);
111 if (ret < 0)
112 return ret;
113
114 /* set the codec system clock for DAC and ADC */
115 ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
116 SND_SOC_CLOCK_IN);
117 if (ret < 0)
118 return ret;
119
120 /* set MCLK division for sample rate */
121 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
122 S3C2410_IISMOD_32FS);
123 if (ret < 0)
124 return ret;
125
126 /* set codec BCLK division for sample rate */
127 ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
128 if (ret < 0)
129 return ret;
130
131 /* set prescaler division for sample rate */
132 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
133 S3C24XX_PRESCALE(4, 4));
134 if (ret < 0)
135 return ret;
136
137 /* codec PLL input is PCLK/4 */
138 ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
139 iis_clkrate / 4, pll_out);
140 if (ret < 0)
141 return ret;
142
143 return 0;
144}
145
146static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
147{
148 struct snd_soc_pcm_runtime *rtd = substream->private_data;
149 struct snd_soc_dai *codec_dai = rtd->codec_dai;
150
151 pr_debug("Entered %s\n", __func__);
152
153 /* disable the PLL */
154 return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
155}
156
157/*
158 * Neo1973 WM8753 HiFi DAI opserations.
159 */
160static struct snd_soc_ops neo1973_hifi_ops = {
161 .hw_params = neo1973_hifi_hw_params,
162 .hw_free = neo1973_hifi_hw_free,
163};
164
165static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
166 struct snd_pcm_hw_params *params)
167{
168 struct snd_soc_pcm_runtime *rtd = substream->private_data;
169 struct snd_soc_dai *codec_dai = rtd->codec_dai;
170 unsigned int pcmdiv = 0;
171 int ret = 0;
172 unsigned long iis_clkrate;
173
174 pr_debug("Entered %s\n", __func__);
175
176 iis_clkrate = s3c24xx_i2s_get_clockrate();
177
178 if (params_rate(params) != 8000)
179 return -EINVAL;
180 if (params_channels(params) != 1)
181 return -EINVAL;
182
183 pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
184
185 /* todo: gg check mode (DSP_B) against CSR datasheet */
186 /* set codec DAI configuration */
187 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
188 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
189 if (ret < 0)
190 return ret;
191
192 /* set the codec system clock for DAC and ADC */
193 ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
194 SND_SOC_CLOCK_IN);
195 if (ret < 0)
196 return ret;
197
198 /* set codec PCM division for sample rate */
199 ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
200 if (ret < 0)
201 return ret;
202
203 /* configure and enable PLL for 12.288MHz output */
204 ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
205 iis_clkrate / 4, 12288000);
206 if (ret < 0)
207 return ret;
208
209 return 0;
210}
211
212static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
213{
214 struct snd_soc_pcm_runtime *rtd = substream->private_data;
215 struct snd_soc_dai *codec_dai = rtd->codec_dai;
216
217 pr_debug("Entered %s\n", __func__);
218
219 /* disable the PLL */
220 return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
221}
222
223static struct snd_soc_ops neo1973_voice_ops = {
224 .hw_params = neo1973_voice_hw_params,
225 .hw_free = neo1973_voice_hw_free,
226};
227
228static int neo1973_scenario;
229
230static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
231 struct snd_ctl_elem_value *ucontrol)
232{
233 ucontrol->value.integer.value[0] = neo1973_scenario;
234 return 0;
235}
236
237static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
238{
239 struct snd_soc_dapm_context *dapm = &codec->dapm;
240
241 pr_debug("Entered %s\n", __func__);
242
243 switch (neo1973_scenario) {
244 case NEO_AUDIO_OFF:
245 snd_soc_dapm_disable_pin(dapm, "Audio Out");
246 snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
247 snd_soc_dapm_disable_pin(dapm, "GSM Line In");
248 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
249 snd_soc_dapm_disable_pin(dapm, "Call Mic");
250 break;
251 case NEO_GSM_CALL_AUDIO_HANDSET:
252 snd_soc_dapm_enable_pin(dapm, "Audio Out");
253 snd_soc_dapm_enable_pin(dapm, "GSM Line Out");
254 snd_soc_dapm_enable_pin(dapm, "GSM Line In");
255 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
256 snd_soc_dapm_enable_pin(dapm, "Call Mic");
257 break;
258 case NEO_GSM_CALL_AUDIO_HEADSET:
259 snd_soc_dapm_enable_pin(dapm, "Audio Out");
260 snd_soc_dapm_enable_pin(dapm, "GSM Line Out");
261 snd_soc_dapm_enable_pin(dapm, "GSM Line In");
262 snd_soc_dapm_enable_pin(dapm, "Headset Mic");
263 snd_soc_dapm_disable_pin(dapm, "Call Mic");
264 break;
265 case NEO_GSM_CALL_AUDIO_BLUETOOTH:
266 snd_soc_dapm_disable_pin(dapm, "Audio Out");
267 snd_soc_dapm_enable_pin(dapm, "GSM Line Out");
268 snd_soc_dapm_enable_pin(dapm, "GSM Line In");
269 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
270 snd_soc_dapm_disable_pin(dapm, "Call Mic");
271 break;
272 case NEO_STEREO_TO_SPEAKERS:
273 snd_soc_dapm_enable_pin(dapm, "Audio Out");
274 snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
275 snd_soc_dapm_disable_pin(dapm, "GSM Line In");
276 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
277 snd_soc_dapm_disable_pin(dapm, "Call Mic");
278 break;
279 case NEO_STEREO_TO_HEADPHONES:
280 snd_soc_dapm_enable_pin(dapm, "Audio Out");
281 snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
282 snd_soc_dapm_disable_pin(dapm, "GSM Line In");
283 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
284 snd_soc_dapm_disable_pin(dapm, "Call Mic");
285 break;
286 case NEO_CAPTURE_HANDSET:
287 snd_soc_dapm_disable_pin(dapm, "Audio Out");
288 snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
289 snd_soc_dapm_disable_pin(dapm, "GSM Line In");
290 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
291 snd_soc_dapm_enable_pin(dapm, "Call Mic");
292 break;
293 case NEO_CAPTURE_HEADSET:
294 snd_soc_dapm_disable_pin(dapm, "Audio Out");
295 snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
296 snd_soc_dapm_disable_pin(dapm, "GSM Line In");
297 snd_soc_dapm_enable_pin(dapm, "Headset Mic");
298 snd_soc_dapm_disable_pin(dapm, "Call Mic");
299 break;
300 case NEO_CAPTURE_BLUETOOTH:
301 snd_soc_dapm_disable_pin(dapm, "Audio Out");
302 snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
303 snd_soc_dapm_disable_pin(dapm, "GSM Line In");
304 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
305 snd_soc_dapm_disable_pin(dapm, "Call Mic");
306 break;
307 default:
308 snd_soc_dapm_disable_pin(dapm, "Audio Out");
309 snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
310 snd_soc_dapm_disable_pin(dapm, "GSM Line In");
311 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
312 snd_soc_dapm_disable_pin(dapm, "Call Mic");
313 }
314
315 snd_soc_dapm_sync(dapm);
316
317 return 0;
318}
319
320static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
321 struct snd_ctl_elem_value *ucontrol)
322{
323 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
324
325 pr_debug("Entered %s\n", __func__);
326
327 if (neo1973_scenario == ucontrol->value.integer.value[0])
328 return 0;
329
330 neo1973_scenario = ucontrol->value.integer.value[0];
331 set_scenario_endpoints(codec, neo1973_scenario);
332 return 1;
333}
334
335static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
336
337static void lm4857_write_regs(void)
338{
339 pr_debug("Entered %s\n", __func__);
340
341 if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
342 printk(KERN_ERR "lm4857: i2c write failed\n");
343}
344
345static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
346 struct snd_ctl_elem_value *ucontrol)
347{
348 struct soc_mixer_control *mc =
349 (struct soc_mixer_control *)kcontrol->private_value;
350 int reg = mc->reg;
351 int shift = mc->shift;
352 int mask = mc->max;
353
354 pr_debug("Entered %s\n", __func__);
355
356 ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
357 return 0;
358}
359
360static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
361 struct snd_ctl_elem_value *ucontrol)
362{
363 struct soc_mixer_control *mc =
364 (struct soc_mixer_control *)kcontrol->private_value;
365 int reg = mc->reg;
366 int shift = mc->shift;
367 int mask = mc->max;
368
369 if (((lm4857_regs[reg] >> shift) & mask) ==
370 ucontrol->value.integer.value[0])
371 return 0;
372
373 lm4857_regs[reg] &= ~(mask << shift);
374 lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
375 lm4857_write_regs();
376 return 1;
377}
378
379static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
380 struct snd_ctl_elem_value *ucontrol)
381{
382 u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
383
384 pr_debug("Entered %s\n", __func__);
385
386 if (value)
387 value -= 5;
388
389 ucontrol->value.integer.value[0] = value;
390 return 0;
391}
392
393static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
394 struct snd_ctl_elem_value *ucontrol)
395{
396 u8 value = ucontrol->value.integer.value[0];
397
398 pr_debug("Entered %s\n", __func__);
399
400 if (value)
401 value += 5;
402
403 if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
404 return 0;
405
406 lm4857_regs[LM4857_CTRL] &= 0xF0;
407 lm4857_regs[LM4857_CTRL] |= value;
408 lm4857_write_regs();
409 return 1;
410}
411
412static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
413 SND_SOC_DAPM_LINE("Audio Out", NULL),
414 SND_SOC_DAPM_LINE("GSM Line Out", NULL),
415 SND_SOC_DAPM_LINE("GSM Line In", NULL),
416 SND_SOC_DAPM_MIC("Headset Mic", NULL),
417 SND_SOC_DAPM_MIC("Call Mic", NULL),
418};
419
420
421static const struct snd_soc_dapm_route dapm_routes[] = {
422
423 /* Connections to the lm4857 amp */
424 {"Audio Out", NULL, "LOUT1"},
425 {"Audio Out", NULL, "ROUT1"},
426
427 /* Connections to the GSM Module */
428 {"GSM Line Out", NULL, "MONO1"},
429 {"GSM Line Out", NULL, "MONO2"},
430 {"RXP", NULL, "GSM Line In"},
431 {"RXN", NULL, "GSM Line In"},
432
433 /* Connections to Headset */
434 {"MIC1", NULL, "Mic Bias"},
435 {"Mic Bias", NULL, "Headset Mic"},
436
437 /* Call Mic */
438 {"MIC2", NULL, "Mic Bias"},
439 {"MIC2N", NULL, "Mic Bias"},
440 {"Mic Bias", NULL, "Call Mic"},
441
442 /* Connect the ALC pins */
443 {"ACIN", NULL, "ACOP"},
444};
445
446static const char *lm4857_mode[] = {
447 "Off",
448 "Call Speaker",
449 "Stereo Speakers",
450 "Stereo Speakers + Headphones",
451 "Headphones"
452};
453
454static const struct soc_enum lm4857_mode_enum[] = {
455 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode),
456};
457
458static const char *neo_scenarios[] = {
459 "Off",
460 "GSM Handset",
461 "GSM Headset",
462 "GSM Bluetooth",
463 "Speakers",
464 "Headphones",
465 "Capture Handset",
466 "Capture Headset",
467 "Capture Bluetooth"
468};
469
470static const struct soc_enum neo_scenario_enum[] = {
471 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios),
472};
473
474static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
475static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
476
477static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
478 SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
479 lm4857_get_reg, lm4857_set_reg, stereo_tlv),
480 SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
481 lm4857_get_reg, lm4857_set_reg, stereo_tlv),
482 SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
483 lm4857_get_reg, lm4857_set_reg, mono_tlv),
484 SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
485 lm4857_get_mode, lm4857_set_mode),
486 SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
487 neo1973_get_scenario, neo1973_set_scenario),
488 SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
489 lm4857_get_reg, lm4857_set_reg),
490 SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
491 lm4857_get_reg, lm4857_set_reg),
492 SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
493 lm4857_get_reg, lm4857_set_reg),
494 SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
495 lm4857_get_reg, lm4857_set_reg),
496};
497
498/*
499 * This is an example machine initialisation for a wm8753 connected to a
500 * neo1973 II. It is missing logic to detect hp/mic insertions and logic
501 * to re-route the audio in such an event.
502 */
503static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)
504{
505 struct snd_soc_codec *codec = rtd->codec;
506 struct snd_soc_dapm_context *dapm = &codec->dapm;
507 int err;
508
509 pr_debug("Entered %s\n", __func__);
510
511 /* set up NC codec pins */
512 snd_soc_dapm_nc_pin(dapm, "LOUT2");
513 snd_soc_dapm_nc_pin(dapm, "ROUT2");
514 snd_soc_dapm_nc_pin(dapm, "OUT3");
515 snd_soc_dapm_nc_pin(dapm, "OUT4");
516 snd_soc_dapm_nc_pin(dapm, "LINE1");
517 snd_soc_dapm_nc_pin(dapm, "LINE2");
518
519 /* Add neo1973 specific widgets */
520 snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets,
521 ARRAY_SIZE(wm8753_dapm_widgets));
522
523 /* set endpoints to default mode */
524 set_scenario_endpoints(codec, NEO_AUDIO_OFF);
525
526 /* add neo1973 specific controls */
527 err = snd_soc_add_controls(codec, wm8753_neo1973_controls,
528 ARRAY_SIZE(8753_neo1973_controls));
529 if (err < 0)
530 return err;
531
532 /* set up neo1973 specific audio routes */
533 err = snd_soc_dapm_add_routes(dapm, dapm_routes,
534 ARRAY_SIZE(dapm_routes));
535
536 snd_soc_dapm_sync(dapm);
537 return 0;
538}
539
540/*
541 * BT Codec DAI
542 */
543static struct snd_soc_dai bt_dai = {
544 .name = "bluetooth-dai",
545 .playback = {
546 .channels_min = 1,
547 .channels_max = 1,
548 .rates = SNDRV_PCM_RATE_8000,
549 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
550 .capture = {
551 .channels_min = 1,
552 .channels_max = 1,
553 .rates = SNDRV_PCM_RATE_8000,
554 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
555};
556
557static struct snd_soc_dai_link neo1973_dai[] = {
558{ /* Hifi Playback - for similatious use with voice below */
559 .name = "WM8753",
560 .stream_name = "WM8753 HiFi",
561 .platform_name = "samsung-audio",
562 .cpu_dai_name = "s3c24xx-i2s",
563 .codec_dai_name = "wm8753-hifi",
564 .codec_name = "wm8753-codec.0-0x1a",
565 .init = neo1973_wm8753_init,
566 .ops = &neo1973_hifi_ops,
567},
568{ /* Voice via BT */
569 .name = "Bluetooth",
570 .stream_name = "Voice",
571 .platform_name = "samsung-audio",
572 .cpu_dai_name = "bluetooth-dai",
573 .codec_dai_name = "wm8753-voice",
574 .codec_name = "wm8753-codec.0-0x1a",
575 .ops = &neo1973_voice_ops,
576},
577};
578
579static struct snd_soc_card neo1973 = {
580 .name = "neo1973",
581 .dai_link = neo1973_dai,
582 .num_links = ARRAY_SIZE(neo1973_dai),
583};
584
585static int lm4857_i2c_probe(struct i2c_client *client,
586 const struct i2c_device_id *id)
587{
588 pr_debug("Entered %s\n", __func__);
589
590 i2c = client;
591
592 lm4857_write_regs();
593 return 0;
594}
595
596static int lm4857_i2c_remove(struct i2c_client *client)
597{
598 pr_debug("Entered %s\n", __func__);
599
600 i2c = NULL;
601
602 return 0;
603}
604
605static u8 lm4857_state;
606
607static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
608{
609 pr_debug("Entered %s\n", __func__);
610
611 dev_dbg(&dev->dev, "lm4857_suspend\n");
612 lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf;
613 if (lm4857_state) {
614 lm4857_regs[LM4857_CTRL] &= 0xf0;
615 lm4857_write_regs();
616 }
617 return 0;
618}
619
620static int lm4857_resume(struct i2c_client *dev)
621{
622 pr_debug("Entered %s\n", __func__);
623
624 if (lm4857_state) {
625 lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f);
626 lm4857_write_regs();
627 }
628 return 0;
629}
630
631static void lm4857_shutdown(struct i2c_client *dev)
632{
633 pr_debug("Entered %s\n", __func__);
634
635 dev_dbg(&dev->dev, "lm4857_shutdown\n");
636 lm4857_regs[LM4857_CTRL] &= 0xf0;
637 lm4857_write_regs();
638}
639
640static const struct i2c_device_id lm4857_i2c_id[] = {
641 { "neo1973_lm4857", 0 },
642 { }
643};
644
645static struct i2c_driver lm4857_i2c_driver = {
646 .driver = {
647 .name = "LM4857 I2C Amp",
648 .owner = THIS_MODULE,
649 },
650 .suspend = lm4857_suspend,
651 .resume = lm4857_resume,
652 .shutdown = lm4857_shutdown,
653 .probe = lm4857_i2c_probe,
654 .remove = lm4857_i2c_remove,
655 .id_table = lm4857_i2c_id,
656};
657
658static struct platform_device *neo1973_snd_device;
659
660static int __init neo1973_init(void)
661{
662 int ret;
663
664 pr_debug("Entered %s\n", __func__);
665
666 if (!machine_is_neo1973_gta01()) {
667 printk(KERN_INFO
668 "Only GTA01 hardware supported by ASoC driver\n");
669 return -ENODEV;
670 }
671
672 neo1973_snd_device = platform_device_alloc("soc-audio", -1);
673 if (!neo1973_snd_device)
674 return -ENOMEM;
675
676 platform_set_drvdata(neo1973_snd_device, &neo1973);
677 ret = platform_device_add(neo1973_snd_device);
678
679 if (ret) {
680 platform_device_put(neo1973_snd_device);
681 return ret;
682 }
683
684 ret = i2c_add_driver(&lm4857_i2c_driver);
685
686 if (ret != 0)
687 platform_device_unregister(neo1973_snd_device);
688
689 return ret;
690}
691
692static void __exit neo1973_exit(void)
693{
694 pr_debug("Entered %s\n", __func__);
695
696 i2c_del_driver(&lm4857_i2c_driver);
697 platform_device_unregister(neo1973_snd_device);
698}
699
700module_init(neo1973_init);
701module_exit(neo1973_exit);
702
703/* Module information */
704MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org");
705MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
706MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
new file mode 100644
index 000000000000..48d0b750406b
--- /dev/null
+++ b/sound/soc/samsung/pcm.c
@@ -0,0 +1,552 @@
1/* sound/soc/samsung/pcm.c
2 *
3 * ALSA SoC Audio Layer - S3C PCM-Controller driver
4 *
5 * Copyright (c) 2009 Samsung Electronics Co. Ltd
6 * Author: Jaswinder Singh <jassi.brar@samsung.com>
7 * based upon I2S drivers by Ben Dooks.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/device.h>
17#include <linux/delay.h>
18#include <linux/clk.h>
19#include <linux/kernel.h>
20#include <linux/gpio.h>
21#include <linux/io.h>
22
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/pcm_params.h>
26#include <sound/initval.h>
27#include <sound/soc.h>
28
29#include <plat/audio.h>
30#include <plat/dma.h>
31
32#include "dma.h"
33#include "pcm.h"
34
35static struct s3c2410_dma_client s3c_pcm_dma_client_out = {
36 .name = "PCM Stereo out"
37};
38
39static struct s3c2410_dma_client s3c_pcm_dma_client_in = {
40 .name = "PCM Stereo in"
41};
42
43static struct s3c_dma_params s3c_pcm_stereo_out[] = {
44 [0] = {
45 .client = &s3c_pcm_dma_client_out,
46 .dma_size = 4,
47 },
48 [1] = {
49 .client = &s3c_pcm_dma_client_out,
50 .dma_size = 4,
51 },
52};
53
54static struct s3c_dma_params s3c_pcm_stereo_in[] = {
55 [0] = {
56 .client = &s3c_pcm_dma_client_in,
57 .dma_size = 4,
58 },
59 [1] = {
60 .client = &s3c_pcm_dma_client_in,
61 .dma_size = 4,
62 },
63};
64
65static struct s3c_pcm_info s3c_pcm[2];
66
67static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on)
68{
69 void __iomem *regs = pcm->regs;
70 u32 ctl, clkctl;
71
72 clkctl = readl(regs + S3C_PCM_CLKCTL);
73 ctl = readl(regs + S3C_PCM_CTL);
74 ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK
75 << S3C_PCM_CTL_TXDIPSTICK_SHIFT);
76
77 if (on) {
78 ctl |= S3C_PCM_CTL_TXDMA_EN;
79 ctl |= S3C_PCM_CTL_TXFIFO_EN;
80 ctl |= S3C_PCM_CTL_ENABLE;
81 ctl |= (0x4<<S3C_PCM_CTL_TXDIPSTICK_SHIFT);
82 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
83 } else {
84 ctl &= ~S3C_PCM_CTL_TXDMA_EN;
85 ctl &= ~S3C_PCM_CTL_TXFIFO_EN;
86
87 if (!(ctl & S3C_PCM_CTL_RXFIFO_EN)) {
88 ctl &= ~S3C_PCM_CTL_ENABLE;
89 if (!pcm->idleclk)
90 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
91 }
92 }
93
94 writel(clkctl, regs + S3C_PCM_CLKCTL);
95 writel(ctl, regs + S3C_PCM_CTL);
96}
97
98static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on)
99{
100 void __iomem *regs = pcm->regs;
101 u32 ctl, clkctl;
102
103 ctl = readl(regs + S3C_PCM_CTL);
104 clkctl = readl(regs + S3C_PCM_CLKCTL);
105 ctl &= ~(S3C_PCM_CTL_RXDIPSTICK_MASK
106 << S3C_PCM_CTL_RXDIPSTICK_SHIFT);
107
108 if (on) {
109 ctl |= S3C_PCM_CTL_RXDMA_EN;
110 ctl |= S3C_PCM_CTL_RXFIFO_EN;
111 ctl |= S3C_PCM_CTL_ENABLE;
112 ctl |= (0x20<<S3C_PCM_CTL_RXDIPSTICK_SHIFT);
113 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
114 } else {
115 ctl &= ~S3C_PCM_CTL_RXDMA_EN;
116 ctl &= ~S3C_PCM_CTL_RXFIFO_EN;
117
118 if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) {
119 ctl &= ~S3C_PCM_CTL_ENABLE;
120 if (!pcm->idleclk)
121 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
122 }
123 }
124
125 writel(clkctl, regs + S3C_PCM_CLKCTL);
126 writel(ctl, regs + S3C_PCM_CTL);
127}
128
129static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
130 struct snd_soc_dai *dai)
131{
132 struct snd_soc_pcm_runtime *rtd = substream->private_data;
133 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
134 unsigned long flags;
135
136 dev_dbg(pcm->dev, "Entered %s\n", __func__);
137
138 switch (cmd) {
139 case SNDRV_PCM_TRIGGER_START:
140 case SNDRV_PCM_TRIGGER_RESUME:
141 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
142 spin_lock_irqsave(&pcm->lock, flags);
143
144 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
145 s3c_pcm_snd_rxctrl(pcm, 1);
146 else
147 s3c_pcm_snd_txctrl(pcm, 1);
148
149 spin_unlock_irqrestore(&pcm->lock, flags);
150 break;
151
152 case SNDRV_PCM_TRIGGER_STOP:
153 case SNDRV_PCM_TRIGGER_SUSPEND:
154 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
155 spin_lock_irqsave(&pcm->lock, flags);
156
157 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
158 s3c_pcm_snd_rxctrl(pcm, 0);
159 else
160 s3c_pcm_snd_txctrl(pcm, 0);
161
162 spin_unlock_irqrestore(&pcm->lock, flags);
163 break;
164
165 default:
166 return -EINVAL;
167 }
168
169 return 0;
170}
171
172static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
173 struct snd_pcm_hw_params *params,
174 struct snd_soc_dai *socdai)
175{
176 struct snd_soc_pcm_runtime *rtd = substream->private_data;
177 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
178 struct s3c_dma_params *dma_data;
179 void __iomem *regs = pcm->regs;
180 struct clk *clk;
181 int sclk_div, sync_div;
182 unsigned long flags;
183 u32 clkctl;
184
185 dev_dbg(pcm->dev, "Entered %s\n", __func__);
186
187 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
188 dma_data = pcm->dma_playback;
189 else
190 dma_data = pcm->dma_capture;
191
192 snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
193
194 /* Strictly check for sample size */
195 switch (params_format(params)) {
196 case SNDRV_PCM_FORMAT_S16_LE:
197 break;
198 default:
199 return -EINVAL;
200 }
201
202 spin_lock_irqsave(&pcm->lock, flags);
203
204 /* Get hold of the PCMSOURCE_CLK */
205 clkctl = readl(regs + S3C_PCM_CLKCTL);
206 if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK)
207 clk = pcm->pclk;
208 else
209 clk = pcm->cclk;
210
211 /* Set the SCLK divider */
212 sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs /
213 params_rate(params) / 2 - 1;
214
215 clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK
216 << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
217 clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK)
218 << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
219
220 /* Set the SYNC divider */
221 sync_div = pcm->sclk_per_fs - 1;
222
223 clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK
224 << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
225 clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK)
226 << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
227
228 writel(clkctl, regs + S3C_PCM_CLKCTL);
229
230 spin_unlock_irqrestore(&pcm->lock, flags);
231
232 dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs SCLK_DIV=%d SYNC_DIV=%d\n",
233 clk_get_rate(clk), pcm->sclk_per_fs,
234 sclk_div, sync_div);
235
236 return 0;
237}
238
239static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai,
240 unsigned int fmt)
241{
242 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
243 void __iomem *regs = pcm->regs;
244 unsigned long flags;
245 int ret = 0;
246 u32 ctl;
247
248 dev_dbg(pcm->dev, "Entered %s\n", __func__);
249
250 spin_lock_irqsave(&pcm->lock, flags);
251
252 ctl = readl(regs + S3C_PCM_CTL);
253
254 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
255 case SND_SOC_DAIFMT_NB_NF:
256 /* Nothing to do, NB_NF by default */
257 break;
258 default:
259 dev_err(pcm->dev, "Unsupported clock inversion!\n");
260 ret = -EINVAL;
261 goto exit;
262 }
263
264 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
265 case SND_SOC_DAIFMT_CBS_CFS:
266 /* Nothing to do, Master by default */
267 break;
268 default:
269 dev_err(pcm->dev, "Unsupported master/slave format!\n");
270 ret = -EINVAL;
271 goto exit;
272 }
273
274 switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
275 case SND_SOC_DAIFMT_CONT:
276 pcm->idleclk = 1;
277 break;
278 case SND_SOC_DAIFMT_GATED:
279 pcm->idleclk = 0;
280 break;
281 default:
282 dev_err(pcm->dev, "Invalid Clock gating request!\n");
283 ret = -EINVAL;
284 goto exit;
285 }
286
287 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
288 case SND_SOC_DAIFMT_DSP_A:
289 ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
290 ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
291 break;
292 case SND_SOC_DAIFMT_DSP_B:
293 ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
294 ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
295 break;
296 default:
297 dev_err(pcm->dev, "Unsupported data format!\n");
298 ret = -EINVAL;
299 goto exit;
300 }
301
302 writel(ctl, regs + S3C_PCM_CTL);
303
304exit:
305 spin_unlock_irqrestore(&pcm->lock, flags);
306
307 return ret;
308}
309
310static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai,
311 int div_id, int div)
312{
313 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
314
315 switch (div_id) {
316 case S3C_PCM_SCLK_PER_FS:
317 pcm->sclk_per_fs = div;
318 break;
319
320 default:
321 return -EINVAL;
322 }
323
324 return 0;
325}
326
327static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai,
328 int clk_id, unsigned int freq, int dir)
329{
330 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
331 void __iomem *regs = pcm->regs;
332 u32 clkctl = readl(regs + S3C_PCM_CLKCTL);
333
334 switch (clk_id) {
335 case S3C_PCM_CLKSRC_PCLK:
336 clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
337 break;
338
339 case S3C_PCM_CLKSRC_MUX:
340 clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
341
342 if (clk_get_rate(pcm->cclk) != freq)
343 clk_set_rate(pcm->cclk, freq);
344
345 break;
346
347 default:
348 return -EINVAL;
349 }
350
351 writel(clkctl, regs + S3C_PCM_CLKCTL);
352
353 return 0;
354}
355
356static struct snd_soc_dai_ops s3c_pcm_dai_ops = {
357 .set_sysclk = s3c_pcm_set_sysclk,
358 .set_clkdiv = s3c_pcm_set_clkdiv,
359 .trigger = s3c_pcm_trigger,
360 .hw_params = s3c_pcm_hw_params,
361 .set_fmt = s3c_pcm_set_fmt,
362};
363
364#define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000
365
366#define S3C_PCM_DAI_DECLARE \
367 .symmetric_rates = 1, \
368 .ops = &s3c_pcm_dai_ops, \
369 .playback = { \
370 .channels_min = 2, \
371 .channels_max = 2, \
372 .rates = S3C_PCM_RATES, \
373 .formats = SNDRV_PCM_FMTBIT_S16_LE, \
374 }, \
375 .capture = { \
376 .channels_min = 2, \
377 .channels_max = 2, \
378 .rates = S3C_PCM_RATES, \
379 .formats = SNDRV_PCM_FMTBIT_S16_LE, \
380 }
381
382struct snd_soc_dai_driver s3c_pcm_dai[] = {
383 [0] = {
384 .name = "samsung-pcm.0",
385 S3C_PCM_DAI_DECLARE,
386 },
387 [1] = {
388 .name = "samsung-pcm.1",
389 S3C_PCM_DAI_DECLARE,
390 },
391};
392EXPORT_SYMBOL_GPL(s3c_pcm_dai);
393
394static __devinit int s3c_pcm_dev_probe(struct platform_device *pdev)
395{
396 struct s3c_pcm_info *pcm;
397 struct resource *mem_res, *dmatx_res, *dmarx_res;
398 struct s3c_audio_pdata *pcm_pdata;
399 int ret;
400
401 /* Check for valid device index */
402 if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) {
403 dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
404 return -EINVAL;
405 }
406
407 pcm_pdata = pdev->dev.platform_data;
408
409 /* Check for availability of necessary resource */
410 dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
411 if (!dmatx_res) {
412 dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n");
413 return -ENXIO;
414 }
415
416 dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
417 if (!dmarx_res) {
418 dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n");
419 return -ENXIO;
420 }
421
422 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
423 if (!mem_res) {
424 dev_err(&pdev->dev, "Unable to get register resource\n");
425 return -ENXIO;
426 }
427
428 if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) {
429 dev_err(&pdev->dev, "Unable to configure gpio\n");
430 return -EINVAL;
431 }
432
433 pcm = &s3c_pcm[pdev->id];
434 pcm->dev = &pdev->dev;
435
436 spin_lock_init(&pcm->lock);
437
438 /* Default is 128fs */
439 pcm->sclk_per_fs = 128;
440
441 pcm->cclk = clk_get(&pdev->dev, "audio-bus");
442 if (IS_ERR(pcm->cclk)) {
443 dev_err(&pdev->dev, "failed to get audio-bus\n");
444 ret = PTR_ERR(pcm->cclk);
445 goto err1;
446 }
447 clk_enable(pcm->cclk);
448
449 /* record our pcm structure for later use in the callbacks */
450 dev_set_drvdata(&pdev->dev, pcm);
451
452 if (!request_mem_region(mem_res->start,
453 resource_size(mem_res), "samsung-pcm")) {
454 dev_err(&pdev->dev, "Unable to request register region\n");
455 ret = -EBUSY;
456 goto err2;
457 }
458
459 pcm->regs = ioremap(mem_res->start, 0x100);
460 if (pcm->regs == NULL) {
461 dev_err(&pdev->dev, "cannot ioremap registers\n");
462 ret = -ENXIO;
463 goto err3;
464 }
465
466 pcm->pclk = clk_get(&pdev->dev, "pcm");
467 if (IS_ERR(pcm->pclk)) {
468 dev_err(&pdev->dev, "failed to get pcm_clock\n");
469 ret = -ENOENT;
470 goto err4;
471 }
472 clk_enable(pcm->pclk);
473
474 ret = snd_soc_register_dai(&pdev->dev, &s3c_pcm_dai[pdev->id]);
475 if (ret != 0) {
476 dev_err(&pdev->dev, "failed to get pcm_clock\n");
477 goto err5;
478 }
479
480 s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start
481 + S3C_PCM_RXFIFO;
482 s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start
483 + S3C_PCM_TXFIFO;
484
485 s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start;
486 s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start;
487
488 pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
489 pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
490
491 return 0;
492
493err5:
494 clk_disable(pcm->pclk);
495 clk_put(pcm->pclk);
496err4:
497 iounmap(pcm->regs);
498err3:
499 release_mem_region(mem_res->start, resource_size(mem_res));
500err2:
501 clk_disable(pcm->cclk);
502 clk_put(pcm->cclk);
503err1:
504 return ret;
505}
506
507static __devexit int s3c_pcm_dev_remove(struct platform_device *pdev)
508{
509 struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
510 struct resource *mem_res;
511
512 snd_soc_unregister_dai(&pdev->dev);
513
514 iounmap(pcm->regs);
515
516 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
517 release_mem_region(mem_res->start, resource_size(mem_res));
518
519 clk_disable(pcm->cclk);
520 clk_disable(pcm->pclk);
521 clk_put(pcm->pclk);
522 clk_put(pcm->cclk);
523
524 return 0;
525}
526
527static struct platform_driver s3c_pcm_driver = {
528 .probe = s3c_pcm_dev_probe,
529 .remove = s3c_pcm_dev_remove,
530 .driver = {
531 .name = "samsung-pcm",
532 .owner = THIS_MODULE,
533 },
534};
535
536static int __init s3c_pcm_init(void)
537{
538 return platform_driver_register(&s3c_pcm_driver);
539}
540module_init(s3c_pcm_init);
541
542static void __exit s3c_pcm_exit(void)
543{
544 platform_driver_unregister(&s3c_pcm_driver);
545}
546module_exit(s3c_pcm_exit);
547
548/* Module information */
549MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
550MODULE_DESCRIPTION("S3C PCM Controller Driver");
551MODULE_LICENSE("GPL");
552MODULE_ALIAS("platform:samsung-pcm");
diff --git a/sound/soc/samsung/pcm.h b/sound/soc/samsung/pcm.h
new file mode 100644
index 000000000000..03393dcf852d
--- /dev/null
+++ b/sound/soc/samsung/pcm.h
@@ -0,0 +1,124 @@
1/* sound/soc/samsung/pcm.h
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
5 * published by the Free Software Foundation.
6 *
7 */
8
9#ifndef __S3C_PCM_H
10#define __S3C_PCM_H __FILE__
11
12/*Register Offsets */
13#define S3C_PCM_CTL (0x00)
14#define S3C_PCM_CLKCTL (0x04)
15#define S3C_PCM_TXFIFO (0x08)
16#define S3C_PCM_RXFIFO (0x0C)
17#define S3C_PCM_IRQCTL (0x10)
18#define S3C_PCM_IRQSTAT (0x14)
19#define S3C_PCM_FIFOSTAT (0x18)
20#define S3C_PCM_CLRINT (0x20)
21
22/* PCM_CTL Bit-Fields */
23#define S3C_PCM_CTL_TXDIPSTICK_MASK (0x3f)
24#define S3C_PCM_CTL_TXDIPSTICK_SHIFT (13)
25#define S3C_PCM_CTL_RXDIPSTICK_MASK (0x3f)
26#define S3C_PCM_CTL_RXDIPSTICK_SHIFT (7)
27#define S3C_PCM_CTL_TXDMA_EN (0x1<<6)
28#define S3C_PCM_CTL_RXDMA_EN (0x1<<5)
29#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1<<4)
30#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1<<3)
31#define S3C_PCM_CTL_TXFIFO_EN (0x1<<2)
32#define S3C_PCM_CTL_RXFIFO_EN (0x1<<1)
33#define S3C_PCM_CTL_ENABLE (0x1<<0)
34
35/* PCM_CLKCTL Bit-Fields */
36#define S3C_PCM_CLKCTL_SERCLK_EN (0x1<<19)
37#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1<<18)
38#define S3C_PCM_CLKCTL_SCLKDIV_MASK (0x1ff)
39#define S3C_PCM_CLKCTL_SYNCDIV_MASK (0x1ff)
40#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT (9)
41#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT (0)
42
43/* PCM_TXFIFO Bit-Fields */
44#define S3C_PCM_TXFIFO_DVALID (0x1<<16)
45#define S3C_PCM_TXFIFO_DATA_MSK (0xffff<<0)
46
47/* PCM_RXFIFO Bit-Fields */
48#define S3C_PCM_RXFIFO_DVALID (0x1<<16)
49#define S3C_PCM_RXFIFO_DATA_MSK (0xffff<<0)
50
51/* PCM_IRQCTL Bit-Fields */
52#define S3C_PCM_IRQCTL_IRQEN (0x1<<14)
53#define S3C_PCM_IRQCTL_WRDEN (0x1<<12)
54#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1<<11)
55#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1<<10)
56#define S3C_PCM_IRQCTL_TXFULLEN (0x1<<9)
57#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1<<8)
58#define S3C_PCM_IRQCTL_TXSTARVEN (0x1<<7)
59#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1<<6)
60#define S3C_PCM_IRQCTL_RXEMPTEN (0x1<<5)
61#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1<<4)
62#define S3C_PCM_IRQCTL_RXFULLEN (0x1<<3)
63#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1<<2)
64#define S3C_PCM_IRQCTL_RXSTARVEN (0x1<<1)
65#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1<<0)
66
67/* PCM_IRQSTAT Bit-Fields */
68#define S3C_PCM_IRQSTAT_IRQPND (0x1<<13)
69#define S3C_PCM_IRQSTAT_WRD_XFER (0x1<<12)
70#define S3C_PCM_IRQSTAT_TXEMPTY (0x1<<11)
71#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1<<10)
72#define S3C_PCM_IRQSTAT_TXFULL (0x1<<9)
73#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1<<8)
74#define S3C_PCM_IRQSTAT_TXSTARV (0x1<<7)
75#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1<<6)
76#define S3C_PCM_IRQSTAT_RXEMPT (0x1<<5)
77#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1<<4)
78#define S3C_PCM_IRQSTAT_RXFULL (0x1<<3)
79#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1<<2)
80#define S3C_PCM_IRQSTAT_RXSTARV (0x1<<1)
81#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1<<0)
82
83/* PCM_FIFOSTAT Bit-Fields */
84#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f<<14)
85#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1<<13)
86#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1<<12)
87#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1<<11)
88#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1<<10)
89#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f<<4)
90#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1<<3)
91#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1<<2)
92#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1<<1)
93#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1<<0)
94
95#define S3C_PCM_CLKSRC_PCLK 0
96#define S3C_PCM_CLKSRC_MUX 1
97
98#define S3C_PCM_SCLK_PER_FS 0
99
100/**
101 * struct s3c_pcm_info - S3C PCM Controller information
102 * @dev: The parent device passed to use from the probe.
103 * @regs: The pointer to the device register block.
104 * @dma_playback: DMA information for playback channel.
105 * @dma_capture: DMA information for capture channel.
106 */
107struct s3c_pcm_info {
108 spinlock_t lock;
109 struct device *dev;
110 void __iomem *regs;
111
112 unsigned int sclk_per_fs;
113
114 /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
115 unsigned int idleclk;
116
117 struct clk *pclk;
118 struct clk *cclk;
119
120 struct s3c_dma_params *dma_playback;
121 struct s3c_dma_params *dma_capture;
122};
123
124#endif /* __S3C_PCM_H */
diff --git a/sound/soc/samsung/regs-i2s-v2.h b/sound/soc/samsung/regs-i2s-v2.h
new file mode 100644
index 000000000000..5e5e5680580b
--- /dev/null
+++ b/sound/soc/samsung/regs-i2s-v2.h
@@ -0,0 +1,115 @@
1/* linux/include/asm-arm/plat-s3c24xx/regs-s3c2412-iis.h
2 *
3 * Copyright 2007 Simtec Electronics <linux@simtec.co.uk>
4 * http://armlinux.simtec.co.uk/
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * S3C2412 IIS register definition
11*/
12
13#ifndef __ASM_ARCH_REGS_S3C2412_IIS_H
14#define __ASM_ARCH_REGS_S3C2412_IIS_H
15
16#define S3C2412_IISCON (0x00)
17#define S3C2412_IISMOD (0x04)
18#define S3C2412_IISFIC (0x08)
19#define S3C2412_IISPSR (0x0C)
20#define S3C2412_IISTXD (0x10)
21#define S3C2412_IISRXD (0x14)
22
23#define S5PC1XX_IISFICS 0x18
24#define S5PC1XX_IISTXDS 0x1C
25
26#define S5PC1XX_IISCON_SW_RST (1 << 31)
27#define S5PC1XX_IISCON_FRXOFSTATUS (1 << 26)
28#define S5PC1XX_IISCON_FRXORINTEN (1 << 25)
29#define S5PC1XX_IISCON_FTXSURSTAT (1 << 24)
30#define S5PC1XX_IISCON_FTXSURINTEN (1 << 23)
31#define S5PC1XX_IISCON_TXSDMAPAUSE (1 << 20)
32#define S5PC1XX_IISCON_TXSDMACTIVE (1 << 18)
33
34#define S3C64XX_IISCON_FTXURSTATUS (1 << 17)
35#define S3C64XX_IISCON_FTXURINTEN (1 << 16)
36#define S3C64XX_IISCON_TXFIFO2_EMPTY (1 << 15)
37#define S3C64XX_IISCON_TXFIFO1_EMPTY (1 << 14)
38#define S3C64XX_IISCON_TXFIFO2_FULL (1 << 13)
39#define S3C64XX_IISCON_TXFIFO1_FULL (1 << 12)
40
41#define S3C2412_IISCON_LRINDEX (1 << 11)
42#define S3C2412_IISCON_TXFIFO_EMPTY (1 << 10)
43#define S3C2412_IISCON_RXFIFO_EMPTY (1 << 9)
44#define S3C2412_IISCON_TXFIFO_FULL (1 << 8)
45#define S3C2412_IISCON_RXFIFO_FULL (1 << 7)
46#define S3C2412_IISCON_TXDMA_PAUSE (1 << 6)
47#define S3C2412_IISCON_RXDMA_PAUSE (1 << 5)
48#define S3C2412_IISCON_TXCH_PAUSE (1 << 4)
49#define S3C2412_IISCON_RXCH_PAUSE (1 << 3)
50#define S3C2412_IISCON_TXDMA_ACTIVE (1 << 2)
51#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1)
52#define S3C2412_IISCON_IIS_ACTIVE (1 << 0)
53
54#define S5PC1XX_IISMOD_OPCLK_CDCLK_OUT (0 << 30)
55#define S5PC1XX_IISMOD_OPCLK_CDCLK_IN (1 << 30)
56#define S5PC1XX_IISMOD_OPCLK_BCLK_OUT (2 << 30)
57#define S5PC1XX_IISMOD_OPCLK_PCLK (3 << 30)
58#define S5PC1XX_IISMOD_OPCLK_MASK (3 << 30)
59#define S5PC1XX_IISMOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
60#define S5PC1XX_IISMOD_BLCS_MASK 0x3
61#define S5PC1XX_IISMOD_BLCS_SHIFT 26
62#define S5PC1XX_IISMOD_BLCP_MASK 0x3
63#define S5PC1XX_IISMOD_BLCP_SHIFT 24
64
65#define S3C64XX_IISMOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */
66#define S3C64XX_IISMOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */
67#define S3C64XX_IISMOD_C1DD_HHALF (1 << 19)
68#define S3C64XX_IISMOD_C1DD_LHALF (1 << 18)
69#define S3C64XX_IISMOD_DC2_EN (1 << 17)
70#define S3C64XX_IISMOD_DC1_EN (1 << 16)
71#define S3C64XX_IISMOD_BLC_16BIT (0 << 13)
72#define S3C64XX_IISMOD_BLC_8BIT (1 << 13)
73#define S3C64XX_IISMOD_BLC_24BIT (2 << 13)
74#define S3C64XX_IISMOD_BLC_MASK (3 << 13)
75
76#define S3C2412_IISMOD_IMS_SYSMUX (1 << 10)
77#define S3C2412_IISMOD_SLAVE (1 << 11)
78#define S3C2412_IISMOD_MODE_TXONLY (0 << 8)
79#define S3C2412_IISMOD_MODE_RXONLY (1 << 8)
80#define S3C2412_IISMOD_MODE_TXRX (2 << 8)
81#define S3C2412_IISMOD_MODE_MASK (3 << 8)
82#define S3C2412_IISMOD_LR_LLOW (0 << 7)
83#define S3C2412_IISMOD_LR_RLOW (1 << 7)
84#define S3C2412_IISMOD_SDF_IIS (0 << 5)
85#define S3C2412_IISMOD_SDF_MSB (1 << 5)
86#define S3C2412_IISMOD_SDF_LSB (2 << 5)
87#define S3C2412_IISMOD_SDF_MASK (3 << 5)
88#define S3C2412_IISMOD_RCLK_256FS (0 << 3)
89#define S3C2412_IISMOD_RCLK_512FS (1 << 3)
90#define S3C2412_IISMOD_RCLK_384FS (2 << 3)
91#define S3C2412_IISMOD_RCLK_768FS (3 << 3)
92#define S3C2412_IISMOD_RCLK_MASK (3 << 3)
93#define S3C2412_IISMOD_BCLK_32FS (0 << 1)
94#define S3C2412_IISMOD_BCLK_48FS (1 << 1)
95#define S3C2412_IISMOD_BCLK_16FS (2 << 1)
96#define S3C2412_IISMOD_BCLK_24FS (3 << 1)
97#define S3C2412_IISMOD_BCLK_MASK (3 << 1)
98#define S3C2412_IISMOD_8BIT (1 << 0)
99
100#define S3C64XX_IISMOD_CDCLKCON (1 << 12)
101
102#define S3C2412_IISPSR_PSREN (1 << 15)
103
104#define S3C64XX_IISFIC_TX2COUNT(x) (((x) >> 24) & 0xf)
105#define S3C64XX_IISFIC_TX1COUNT(x) (((x) >> 16) & 0xf)
106
107#define S3C2412_IISFIC_TXFLUSH (1 << 15)
108#define S3C2412_IISFIC_RXFLUSH (1 << 7)
109#define S3C2412_IISFIC_TXCOUNT(x) (((x) >> 8) & 0xf)
110#define S3C2412_IISFIC_RXCOUNT(x) (((x) >> 0) & 0xf)
111
112#define S5PC1XX_IISFICS_TXFLUSH (1 << 15)
113#define S5PC1XX_IISFICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
114
115#endif /* __ASM_ARCH_REGS_S3C2412_IIS_H */
diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c
new file mode 100644
index 000000000000..5a4587e02886
--- /dev/null
+++ b/sound/soc/samsung/rx1950_uda1380.c
@@ -0,0 +1,319 @@
1/*
2 * rx1950.c -- ALSA Soc Audio Layer
3 *
4 * Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com>
5 *
6 * Based on smdk2440.c and magician.c
7 *
8 * Authors: Graeme Gregory graeme.gregory@wolfsonmicro.com
9 * Philipp Zabel <philipp.zabel@gmail.com>
10 * Denis Grigoriev <dgreenday@gmail.com>
11 * Vasily Khoruzhick <anarsoul@gmail.com>
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
20#include <linux/module.h>
21#include <linux/moduleparam.h>
22#include <linux/platform_device.h>
23#include <linux/i2c.h>
24#include <linux/gpio.h>
25#include <linux/clk.h>
26
27#include <sound/soc.h>
28#include <sound/uda1380.h>
29#include <sound/jack.h>
30
31#include <plat/regs-iis.h>
32
33#include <mach/regs-clock.h>
34
35#include <asm/mach-types.h>
36
37#include "dma.h"
38#include "s3c24xx-i2s.h"
39#include "../codecs/uda1380.h"
40
41static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
42static int rx1950_startup(struct snd_pcm_substream *substream);
43static int rx1950_hw_params(struct snd_pcm_substream *substream,
44 struct snd_pcm_hw_params *params);
45static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
46 struct snd_kcontrol *kcontrol, int event);
47
48static unsigned int rates[] = {
49 16000,
50 44100,
51 48000,
52};
53
54static struct snd_pcm_hw_constraint_list hw_rates = {
55 .count = ARRAY_SIZE(rates),
56 .list = rates,
57 .mask = 0,
58};
59
60static struct snd_soc_jack hp_jack;
61
62static struct snd_soc_jack_pin hp_jack_pins[] = {
63 {
64 .pin = "Headphone Jack",
65 .mask = SND_JACK_HEADPHONE,
66 },
67 {
68 .pin = "Speaker",
69 .mask = SND_JACK_HEADPHONE,
70 .invert = 1,
71 },
72};
73
74static struct snd_soc_jack_gpio hp_jack_gpios[] = {
75 [0] = {
76 .gpio = S3C2410_GPG(12),
77 .name = "hp-gpio",
78 .report = SND_JACK_HEADPHONE,
79 .invert = 1,
80 .debounce_time = 200,
81 },
82};
83
84static struct snd_soc_ops rx1950_ops = {
85 .startup = rx1950_startup,
86 .hw_params = rx1950_hw_params,
87};
88
89/* s3c24xx digital audio interface glue - connects codec <--> CPU */
90static struct snd_soc_dai_link rx1950_uda1380_dai[] = {
91 {
92 .name = "uda1380",
93 .stream_name = "UDA1380 Duplex",
94 .cpu_dai_name = "s3c24xx-iis",
95 .codec_dai_name = "uda1380-hifi",
96 .init = rx1950_uda1380_init,
97 .platform_name = "samsung-audio",
98 .codec_name = "uda1380-codec.0-001a",
99 .ops = &rx1950_ops,
100 },
101};
102
103static struct snd_soc_card rx1950_asoc = {
104 .name = "rx1950",
105 .dai_link = rx1950_uda1380_dai,
106 .num_links = ARRAY_SIZE(rx1950_uda1380_dai),
107};
108
109/* rx1950 machine dapm widgets */
110static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
111 SND_SOC_DAPM_HP("Headphone Jack", NULL),
112 SND_SOC_DAPM_MIC("Mic Jack", NULL),
113 SND_SOC_DAPM_SPK("Speaker", rx1950_spk_power),
114};
115
116/* rx1950 machine audio_map */
117static const struct snd_soc_dapm_route audio_map[] = {
118 /* headphone connected to VOUTLHP, VOUTRHP */
119 {"Headphone Jack", NULL, "VOUTLHP"},
120 {"Headphone Jack", NULL, "VOUTRHP"},
121
122 /* ext speaker connected to VOUTL, VOUTR */
123 {"Speaker", NULL, "VOUTL"},
124 {"Speaker", NULL, "VOUTR"},
125
126 /* mic is connected to VINM */
127 {"VINM", NULL, "Mic Jack"},
128};
129
130static struct platform_device *s3c24xx_snd_device;
131
132static int rx1950_startup(struct snd_pcm_substream *substream)
133{
134 struct snd_pcm_runtime *runtime = substream->runtime;
135
136 runtime->hw.rate_min = hw_rates.list[0];
137 runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1];
138 runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
139
140 return snd_pcm_hw_constraint_list(runtime, 0,
141 SNDRV_PCM_HW_PARAM_RATE,
142 &hw_rates);
143}
144
145static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
146 struct snd_kcontrol *kcontrol, int event)
147{
148 if (SND_SOC_DAPM_EVENT_ON(event))
149 gpio_set_value(S3C2410_GPA(1), 1);
150 else
151 gpio_set_value(S3C2410_GPA(1), 0);
152
153 return 0;
154}
155
156static int rx1950_hw_params(struct snd_pcm_substream *substream,
157 struct snd_pcm_hw_params *params)
158{
159 struct snd_soc_pcm_runtime *rtd = substream->private_data;
160 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
161 struct snd_soc_dai *codec_dai = rtd->codec_dai;
162 int div;
163 int ret;
164 unsigned int rate = params_rate(params);
165 int clk_source, fs_mode;
166
167 switch (rate) {
168 case 16000:
169 case 48000:
170 clk_source = S3C24XX_CLKSRC_PCLK;
171 fs_mode = S3C2410_IISMOD_256FS;
172 div = s3c24xx_i2s_get_clockrate() / (256 * rate);
173 if (s3c24xx_i2s_get_clockrate() % (256 * rate) > (128 * rate))
174 div++;
175 break;
176 case 44100:
177 case 88200:
178 clk_source = S3C24XX_CLKSRC_MPLL;
179 fs_mode = S3C2410_IISMOD_384FS;
180 div = 1;
181 break;
182 default:
183 printk(KERN_ERR "%s: rate %d is not supported\n",
184 __func__, rate);
185 return -EINVAL;
186 }
187
188 /* set codec DAI configuration */
189 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
190 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
191 if (ret < 0)
192 return ret;
193
194 /* set cpu DAI configuration */
195 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
196 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
197 if (ret < 0)
198 return ret;
199
200 /* select clock source */
201 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source, rate,
202 SND_SOC_CLOCK_OUT);
203 if (ret < 0)
204 return ret;
205
206 /* set MCLK division for sample rate */
207 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
208 fs_mode);
209 if (ret < 0)
210 return ret;
211
212 /* set BCLK division for sample rate */
213 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
214 S3C2410_IISMOD_32FS);
215 if (ret < 0)
216 return ret;
217
218 /* set prescaler division for sample rate */
219 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
220 S3C24XX_PRESCALE(div, div));
221 if (ret < 0)
222 return ret;
223
224 return 0;
225}
226
227static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
228{
229 struct snd_soc_codec *codec = rtd->codec;
230 struct snd_soc_dapm_context *dapm = &codec->dapm;
231 int err;
232
233 /* Add rx1950 specific widgets */
234 err = snd_soc_dapm_new_controls(dapm, uda1380_dapm_widgets,
235 ARRAY_SIZE(uda1380_dapm_widgets));
236
237 if (err)
238 return err;
239
240 /* Set up rx1950 specific audio path audio_mapnects */
241 err = snd_soc_dapm_add_routes(dapm, audio_map,
242 ARRAY_SIZE(audio_map));
243
244 if (err)
245 return err;
246
247 snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
248 snd_soc_dapm_enable_pin(dapm, "Speaker");
249
250 snd_soc_dapm_sync(dapm);
251
252 snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
253 &hp_jack);
254
255 snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
256 hp_jack_pins);
257
258 snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
259 hp_jack_gpios);
260
261 return 0;
262}
263
264static int __init rx1950_init(void)
265{
266 int ret;
267
268 if (!machine_is_rx1950())
269 return -ENODEV;
270
271 /* configure some gpios */
272 ret = gpio_request(S3C2410_GPA(1), "speaker-power");
273 if (ret)
274 goto err_gpio;
275
276 ret = gpio_direction_output(S3C2410_GPA(1), 0);
277 if (ret)
278 goto err_gpio_conf;
279
280 s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
281 if (!s3c24xx_snd_device) {
282 ret = -ENOMEM;
283 goto err_plat_alloc;
284 }
285
286 platform_set_drvdata(s3c24xx_snd_device, &rx1950_asoc);
287 ret = platform_device_add(s3c24xx_snd_device);
288
289 if (ret) {
290 platform_device_put(s3c24xx_snd_device);
291 goto err_plat_add;
292 }
293
294 return 0;
295
296err_plat_add:
297err_plat_alloc:
298err_gpio_conf:
299 gpio_free(S3C2410_GPA(1));
300
301err_gpio:
302 return ret;
303}
304
305static void __exit rx1950_exit(void)
306{
307 platform_device_unregister(s3c24xx_snd_device);
308 snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
309 hp_jack_gpios);
310 gpio_free(S3C2410_GPA(1));
311}
312
313module_init(rx1950_init);
314module_exit(rx1950_exit);
315
316/* Module information */
317MODULE_AUTHOR("Vasily Khoruzhick");
318MODULE_DESCRIPTION("ALSA SoC RX1950");
319MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
new file mode 100644
index 000000000000..094f36e41e83
--- /dev/null
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -0,0 +1,757 @@
1/* sound/soc/samsung/s3c-i2c-v2.c
2 *
3 * ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
4 *
5 * Copyright (c) 2006 Wolfson Microelectronics PLC.
6 * Graeme Gregory graeme.gregory@wolfsonmicro.com
7 * linux@wolfsonmicro.com
8 *
9 * Copyright (c) 2008, 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/clk.h>
21#include <linux/io.h>
22
23#include <sound/pcm.h>
24#include <sound/pcm_params.h>
25#include <sound/soc.h>
26
27#include <mach/dma.h>
28
29#include "regs-i2s-v2.h"
30#include "s3c-i2s-v2.h"
31#include "dma.h"
32
33#undef S3C_IIS_V2_SUPPORTED
34
35#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) \
36 || defined(CONFIG_CPU_S5PV210)
37#define S3C_IIS_V2_SUPPORTED
38#endif
39
40#ifdef CONFIG_PLAT_S3C64XX
41#define S3C_IIS_V2_SUPPORTED
42#endif
43
44#ifndef S3C_IIS_V2_SUPPORTED
45#error Unsupported CPU model
46#endif
47
48#define S3C2412_I2S_DEBUG_CON 0
49
50static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
51{
52 return snd_soc_dai_get_drvdata(cpu_dai);
53}
54
55#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
56
57#if S3C2412_I2S_DEBUG_CON
58static void dbg_showcon(const char *fn, u32 con)
59{
60 printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
61 bit_set(con, S3C2412_IISCON_LRINDEX),
62 bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
63 bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
64 bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
65 bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
66
67 printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
68 fn,
69 bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
70 bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
71 bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
72 bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
73 printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
74 bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
75 bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
76 bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
77}
78#else
79static inline void dbg_showcon(const char *fn, u32 con)
80{
81}
82#endif
83
84
85/* Turn on or off the transmission path. */
86static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
87{
88 void __iomem *regs = i2s->regs;
89 u32 fic, con, mod;
90
91 pr_debug("%s(%d)\n", __func__, on);
92
93 fic = readl(regs + S3C2412_IISFIC);
94 con = readl(regs + S3C2412_IISCON);
95 mod = readl(regs + S3C2412_IISMOD);
96
97 pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
98
99 if (on) {
100 con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
101 con &= ~S3C2412_IISCON_TXDMA_PAUSE;
102 con &= ~S3C2412_IISCON_TXCH_PAUSE;
103
104 switch (mod & S3C2412_IISMOD_MODE_MASK) {
105 case S3C2412_IISMOD_MODE_TXONLY:
106 case S3C2412_IISMOD_MODE_TXRX:
107 /* do nothing, we are in the right mode */
108 break;
109
110 case S3C2412_IISMOD_MODE_RXONLY:
111 mod &= ~S3C2412_IISMOD_MODE_MASK;
112 mod |= S3C2412_IISMOD_MODE_TXRX;
113 break;
114
115 default:
116 dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n",
117 mod & S3C2412_IISMOD_MODE_MASK);
118 break;
119 }
120
121 writel(con, regs + S3C2412_IISCON);
122 writel(mod, regs + S3C2412_IISMOD);
123 } else {
124 /* Note, we do not have any indication that the FIFO problems
125 * tha the S3C2410/2440 had apply here, so we should be able
126 * to disable the DMA and TX without resetting the FIFOS.
127 */
128
129 con |= S3C2412_IISCON_TXDMA_PAUSE;
130 con |= S3C2412_IISCON_TXCH_PAUSE;
131 con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
132
133 switch (mod & S3C2412_IISMOD_MODE_MASK) {
134 case S3C2412_IISMOD_MODE_TXRX:
135 mod &= ~S3C2412_IISMOD_MODE_MASK;
136 mod |= S3C2412_IISMOD_MODE_RXONLY;
137 break;
138
139 case S3C2412_IISMOD_MODE_TXONLY:
140 mod &= ~S3C2412_IISMOD_MODE_MASK;
141 con &= ~S3C2412_IISCON_IIS_ACTIVE;
142 break;
143
144 default:
145 dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n",
146 mod & S3C2412_IISMOD_MODE_MASK);
147 break;
148 }
149
150 writel(mod, regs + S3C2412_IISMOD);
151 writel(con, regs + S3C2412_IISCON);
152 }
153
154 fic = readl(regs + S3C2412_IISFIC);
155 dbg_showcon(__func__, con);
156 pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
157}
158
159static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
160{
161 void __iomem *regs = i2s->regs;
162 u32 fic, con, mod;
163
164 pr_debug("%s(%d)\n", __func__, on);
165
166 fic = readl(regs + S3C2412_IISFIC);
167 con = readl(regs + S3C2412_IISCON);
168 mod = readl(regs + S3C2412_IISMOD);
169
170 pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
171
172 if (on) {
173 con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
174 con &= ~S3C2412_IISCON_RXDMA_PAUSE;
175 con &= ~S3C2412_IISCON_RXCH_PAUSE;
176
177 switch (mod & S3C2412_IISMOD_MODE_MASK) {
178 case S3C2412_IISMOD_MODE_TXRX:
179 case S3C2412_IISMOD_MODE_RXONLY:
180 /* do nothing, we are in the right mode */
181 break;
182
183 case S3C2412_IISMOD_MODE_TXONLY:
184 mod &= ~S3C2412_IISMOD_MODE_MASK;
185 mod |= S3C2412_IISMOD_MODE_TXRX;
186 break;
187
188 default:
189 dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n",
190 mod & S3C2412_IISMOD_MODE_MASK);
191 }
192
193 writel(mod, regs + S3C2412_IISMOD);
194 writel(con, regs + S3C2412_IISCON);
195 } else {
196 /* See txctrl notes on FIFOs. */
197
198 con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
199 con |= S3C2412_IISCON_RXDMA_PAUSE;
200 con |= S3C2412_IISCON_RXCH_PAUSE;
201
202 switch (mod & S3C2412_IISMOD_MODE_MASK) {
203 case S3C2412_IISMOD_MODE_RXONLY:
204 con &= ~S3C2412_IISCON_IIS_ACTIVE;
205 mod &= ~S3C2412_IISMOD_MODE_MASK;
206 break;
207
208 case S3C2412_IISMOD_MODE_TXRX:
209 mod &= ~S3C2412_IISMOD_MODE_MASK;
210 mod |= S3C2412_IISMOD_MODE_TXONLY;
211 break;
212
213 default:
214 dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n",
215 mod & S3C2412_IISMOD_MODE_MASK);
216 }
217
218 writel(con, regs + S3C2412_IISCON);
219 writel(mod, regs + S3C2412_IISMOD);
220 }
221
222 fic = readl(regs + S3C2412_IISFIC);
223 pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
224}
225
226#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
227
228/*
229 * Wait for the LR signal to allow synchronisation to the L/R clock
230 * from the codec. May only be needed for slave mode.
231 */
232static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
233{
234 u32 iiscon;
235 unsigned long loops = msecs_to_loops(5);
236
237 pr_debug("Entered %s\n", __func__);
238
239 while (--loops) {
240 iiscon = readl(i2s->regs + S3C2412_IISCON);
241 if (iiscon & S3C2412_IISCON_LRINDEX)
242 break;
243
244 cpu_relax();
245 }
246
247 if (!loops) {
248 printk(KERN_ERR "%s: timeout\n", __func__);
249 return -ETIMEDOUT;
250 }
251
252 return 0;
253}
254
255/*
256 * Set S3C2412 I2S DAI format
257 */
258static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
259 unsigned int fmt)
260{
261 struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
262 u32 iismod;
263
264 pr_debug("Entered %s\n", __func__);
265
266 iismod = readl(i2s->regs + S3C2412_IISMOD);
267 pr_debug("hw_params r: IISMOD: %x \n", iismod);
268
269 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
270 case SND_SOC_DAIFMT_CBM_CFM:
271 i2s->master = 0;
272 iismod |= S3C2412_IISMOD_SLAVE;
273 break;
274 case SND_SOC_DAIFMT_CBS_CFS:
275 i2s->master = 1;
276 iismod &= ~S3C2412_IISMOD_SLAVE;
277 break;
278 default:
279 pr_err("unknwon master/slave format\n");
280 return -EINVAL;
281 }
282
283 iismod &= ~S3C2412_IISMOD_SDF_MASK;
284
285 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
286 case SND_SOC_DAIFMT_RIGHT_J:
287 iismod |= S3C2412_IISMOD_LR_RLOW;
288 iismod |= S3C2412_IISMOD_SDF_MSB;
289 break;
290 case SND_SOC_DAIFMT_LEFT_J:
291 iismod |= S3C2412_IISMOD_LR_RLOW;
292 iismod |= S3C2412_IISMOD_SDF_LSB;
293 break;
294 case SND_SOC_DAIFMT_I2S:
295 iismod &= ~S3C2412_IISMOD_LR_RLOW;
296 iismod |= S3C2412_IISMOD_SDF_IIS;
297 break;
298 default:
299 pr_err("Unknown data format\n");
300 return -EINVAL;
301 }
302
303 writel(iismod, i2s->regs + S3C2412_IISMOD);
304 pr_debug("hw_params w: IISMOD: %x \n", iismod);
305 return 0;
306}
307
308static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream,
309 struct snd_pcm_hw_params *params,
310 struct snd_soc_dai *dai)
311{
312 struct s3c_i2sv2_info *i2s = to_info(dai);
313 struct s3c_dma_params *dma_data;
314 u32 iismod;
315
316 pr_debug("Entered %s\n", __func__);
317
318 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
319 dma_data = i2s->dma_playback;
320 else
321 dma_data = i2s->dma_capture;
322
323 snd_soc_dai_set_dma_data(dai, substream, dma_data);
324
325 /* Working copies of register */
326 iismod = readl(i2s->regs + S3C2412_IISMOD);
327 pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
328
329 iismod &= ~S3C64XX_IISMOD_BLC_MASK;
330 /* Sample size */
331 switch (params_format(params)) {
332 case SNDRV_PCM_FORMAT_S8:
333 iismod |= S3C64XX_IISMOD_BLC_8BIT;
334 break;
335 case SNDRV_PCM_FORMAT_S16_LE:
336 break;
337 case SNDRV_PCM_FORMAT_S24_LE:
338 iismod |= S3C64XX_IISMOD_BLC_24BIT;
339 break;
340 }
341
342 writel(iismod, i2s->regs + S3C2412_IISMOD);
343 pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
344
345 return 0;
346}
347
348static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai,
349 int clk_id, unsigned int freq, int dir)
350{
351 struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
352 u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
353
354 pr_debug("Entered %s\n", __func__);
355 pr_debug("%s r: IISMOD: %x\n", __func__, iismod);
356
357 switch (clk_id) {
358 case S3C_I2SV2_CLKSRC_PCLK:
359 iismod &= ~S3C2412_IISMOD_IMS_SYSMUX;
360 break;
361
362 case S3C_I2SV2_CLKSRC_AUDIOBUS:
363 iismod |= S3C2412_IISMOD_IMS_SYSMUX;
364 break;
365
366 case S3C_I2SV2_CLKSRC_CDCLK:
367 /* Error if controller doesn't have the CDCLKCON bit */
368 if (!(i2s->feature & S3C_FEATURE_CDCLKCON))
369 return -EINVAL;
370
371 switch (dir) {
372 case SND_SOC_CLOCK_IN:
373 iismod |= S3C64XX_IISMOD_CDCLKCON;
374 break;
375 case SND_SOC_CLOCK_OUT:
376 iismod &= ~S3C64XX_IISMOD_CDCLKCON;
377 break;
378 default:
379 return -EINVAL;
380 }
381 break;
382
383 default:
384 return -EINVAL;
385 }
386
387 writel(iismod, i2s->regs + S3C2412_IISMOD);
388 pr_debug("%s w: IISMOD: %x\n", __func__, iismod);
389
390 return 0;
391}
392
393static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
394 struct snd_soc_dai *dai)
395{
396 struct snd_soc_pcm_runtime *rtd = substream->private_data;
397 struct s3c_i2sv2_info *i2s = to_info(rtd->cpu_dai);
398 int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
399 unsigned long irqs;
400 int ret = 0;
401 struct s3c_dma_params *dma_data =
402 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
403
404 pr_debug("Entered %s\n", __func__);
405
406 switch (cmd) {
407 case SNDRV_PCM_TRIGGER_START:
408 /* On start, ensure that the FIFOs are cleared and reset. */
409
410 writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
411 i2s->regs + S3C2412_IISFIC);
412
413 /* clear again, just in case */
414 writel(0x0, i2s->regs + S3C2412_IISFIC);
415
416 case SNDRV_PCM_TRIGGER_RESUME:
417 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
418 if (!i2s->master) {
419 ret = s3c2412_snd_lrsync(i2s);
420 if (ret)
421 goto exit_err;
422 }
423
424 local_irq_save(irqs);
425
426 if (capture)
427 s3c2412_snd_rxctrl(i2s, 1);
428 else
429 s3c2412_snd_txctrl(i2s, 1);
430
431 local_irq_restore(irqs);
432
433 /*
434 * Load the next buffer to DMA to meet the reqirement
435 * of the auto reload mechanism of S3C24XX.
436 * This call won't bother S3C64XX.
437 */
438 s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED);
439
440 break;
441
442 case SNDRV_PCM_TRIGGER_STOP:
443 case SNDRV_PCM_TRIGGER_SUSPEND:
444 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
445 local_irq_save(irqs);
446
447 if (capture)
448 s3c2412_snd_rxctrl(i2s, 0);
449 else
450 s3c2412_snd_txctrl(i2s, 0);
451
452 local_irq_restore(irqs);
453 break;
454 default:
455 ret = -EINVAL;
456 break;
457 }
458
459exit_err:
460 return ret;
461}
462
463/*
464 * Set S3C2412 Clock dividers
465 */
466static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
467 int div_id, int div)
468{
469 struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
470 u32 reg;
471
472 pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
473
474 switch (div_id) {
475 case S3C_I2SV2_DIV_BCLK:
476 switch (div) {
477 case 16:
478 div = S3C2412_IISMOD_BCLK_16FS;
479 break;
480
481 case 32:
482 div = S3C2412_IISMOD_BCLK_32FS;
483 break;
484
485 case 24:
486 div = S3C2412_IISMOD_BCLK_24FS;
487 break;
488
489 case 48:
490 div = S3C2412_IISMOD_BCLK_48FS;
491 break;
492
493 default:
494 return -EINVAL;
495 }
496
497 reg = readl(i2s->regs + S3C2412_IISMOD);
498 reg &= ~S3C2412_IISMOD_BCLK_MASK;
499 writel(reg | div, i2s->regs + S3C2412_IISMOD);
500
501 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
502 break;
503
504 case S3C_I2SV2_DIV_RCLK:
505 switch (div) {
506 case 256:
507 div = S3C2412_IISMOD_RCLK_256FS;
508 break;
509
510 case 384:
511 div = S3C2412_IISMOD_RCLK_384FS;
512 break;
513
514 case 512:
515 div = S3C2412_IISMOD_RCLK_512FS;
516 break;
517
518 case 768:
519 div = S3C2412_IISMOD_RCLK_768FS;
520 break;
521
522 default:
523 return -EINVAL;
524 }
525
526 reg = readl(i2s->regs + S3C2412_IISMOD);
527 reg &= ~S3C2412_IISMOD_RCLK_MASK;
528 writel(reg | div, i2s->regs + S3C2412_IISMOD);
529 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
530 break;
531
532 case S3C_I2SV2_DIV_PRESCALER:
533 if (div >= 0) {
534 writel((div << 8) | S3C2412_IISPSR_PSREN,
535 i2s->regs + S3C2412_IISPSR);
536 } else {
537 writel(0x0, i2s->regs + S3C2412_IISPSR);
538 }
539 pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
540 break;
541
542 default:
543 return -EINVAL;
544 }
545
546 return 0;
547}
548
549static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream,
550 struct snd_soc_dai *dai)
551{
552 struct s3c_i2sv2_info *i2s = to_info(dai);
553 u32 reg = readl(i2s->regs + S3C2412_IISFIC);
554 snd_pcm_sframes_t delay;
555
556 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
557 delay = S3C2412_IISFIC_TXCOUNT(reg);
558 else
559 delay = S3C2412_IISFIC_RXCOUNT(reg);
560
561 return delay;
562}
563
564struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai)
565{
566 struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
567 u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
568
569 if (iismod & S3C2412_IISMOD_IMS_SYSMUX)
570 return i2s->iis_cclk;
571 else
572 return i2s->iis_pclk;
573}
574EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock);
575
576/* default table of all avaialable root fs divisors */
577static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
578
579int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
580 unsigned int *fstab,
581 unsigned int rate, struct clk *clk)
582{
583 unsigned long clkrate = clk_get_rate(clk);
584 unsigned int div;
585 unsigned int fsclk;
586 unsigned int actual;
587 unsigned int fs;
588 unsigned int fsdiv;
589 signed int deviation = 0;
590 unsigned int best_fs = 0;
591 unsigned int best_div = 0;
592 unsigned int best_rate = 0;
593 unsigned int best_deviation = INT_MAX;
594
595 pr_debug("Input clock rate %ldHz\n", clkrate);
596
597 if (fstab == NULL)
598 fstab = iis_fs_tab;
599
600 for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) {
601 fsdiv = iis_fs_tab[fs];
602
603 fsclk = clkrate / fsdiv;
604 div = fsclk / rate;
605
606 if ((fsclk % rate) > (rate / 2))
607 div++;
608
609 if (div <= 1)
610 continue;
611
612 actual = clkrate / (fsdiv * div);
613 deviation = actual - rate;
614
615 printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n",
616 fsdiv, div, actual, deviation);
617
618 deviation = abs(deviation);
619
620 if (deviation < best_deviation) {
621 best_fs = fsdiv;
622 best_div = div;
623 best_rate = actual;
624 best_deviation = deviation;
625 }
626
627 if (deviation == 0)
628 break;
629 }
630
631 printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n",
632 best_fs, best_div, best_rate);
633
634 info->fs_div = best_fs;
635 info->clk_div = best_div;
636
637 return 0;
638}
639EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate);
640
641int s3c_i2sv2_probe(struct snd_soc_dai *dai,
642 struct s3c_i2sv2_info *i2s,
643 unsigned long base)
644{
645 struct device *dev = dai->dev;
646 unsigned int iismod;
647
648 i2s->dev = dev;
649
650 /* record our i2s structure for later use in the callbacks */
651 snd_soc_dai_set_drvdata(dai, i2s);
652
653 i2s->regs = ioremap(base, 0x100);
654 if (i2s->regs == NULL) {
655 dev_err(dev, "cannot ioremap registers\n");
656 return -ENXIO;
657 }
658
659 i2s->iis_pclk = clk_get(dev, "iis");
660 if (IS_ERR(i2s->iis_pclk)) {
661 dev_err(dev, "failed to get iis_clock\n");
662 iounmap(i2s->regs);
663 return -ENOENT;
664 }
665
666 clk_enable(i2s->iis_pclk);
667
668 /* Mark ourselves as in TXRX mode so we can run through our cleanup
669 * process without warnings. */
670 iismod = readl(i2s->regs + S3C2412_IISMOD);
671 iismod |= S3C2412_IISMOD_MODE_TXRX;
672 writel(iismod, i2s->regs + S3C2412_IISMOD);
673 s3c2412_snd_txctrl(i2s, 0);
674 s3c2412_snd_rxctrl(i2s, 0);
675
676 return 0;
677}
678EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
679
680#ifdef CONFIG_PM
681static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
682{
683 struct s3c_i2sv2_info *i2s = to_info(dai);
684 u32 iismod;
685
686 if (dai->active) {
687 i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
688 i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
689 i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
690
691 /* some basic suspend checks */
692
693 iismod = readl(i2s->regs + S3C2412_IISMOD);
694
695 if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
696 pr_warning("%s: RXDMA active?\n", __func__);
697
698 if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
699 pr_warning("%s: TXDMA active?\n", __func__);
700
701 if (iismod & S3C2412_IISCON_IIS_ACTIVE)
702 pr_warning("%s: IIS active\n", __func__);
703 }
704
705 return 0;
706}
707
708static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
709{
710 struct s3c_i2sv2_info *i2s = to_info(dai);
711
712 pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
713 dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
714
715 if (dai->active) {
716 writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
717 writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
718 writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
719
720 writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
721 i2s->regs + S3C2412_IISFIC);
722
723 ndelay(250);
724 writel(0x0, i2s->regs + S3C2412_IISFIC);
725 }
726
727 return 0;
728}
729#else
730#define s3c2412_i2s_suspend NULL
731#define s3c2412_i2s_resume NULL
732#endif
733
734int s3c_i2sv2_register_dai(struct device *dev, int id,
735 struct snd_soc_dai_driver *drv)
736{
737 struct snd_soc_dai_ops *ops = drv->ops;
738
739 ops->trigger = s3c2412_i2s_trigger;
740 if (!ops->hw_params)
741 ops->hw_params = s3c_i2sv2_hw_params;
742 ops->set_fmt = s3c2412_i2s_set_fmt;
743 ops->set_clkdiv = s3c2412_i2s_set_clkdiv;
744 ops->set_sysclk = s3c_i2sv2_set_sysclk;
745
746 /* Allow overriding by (for example) IISv4 */
747 if (!ops->delay)
748 ops->delay = s3c2412_i2s_delay;
749
750 drv->suspend = s3c2412_i2s_suspend;
751 drv->resume = s3c2412_i2s_resume;
752
753 return snd_soc_register_dai(dev, drv);
754}
755EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai);
756
757MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c-i2s-v2.h b/sound/soc/samsung/s3c-i2s-v2.h
new file mode 100644
index 000000000000..f8297d9bb8a3
--- /dev/null
+++ b/sound/soc/samsung/s3c-i2s-v2.h
@@ -0,0 +1,106 @@
1/* sound/soc/samsung/s3c-i2s-v2.h
2 *
3 * ALSA Soc Audio Layer - S3C_I2SV2 I2S driver
4 *
5 * Copyright (c) 2007 Simtec Electronics
6 * http://armlinux.simtec.co.uk/
7 * Ben Dooks <ben@simtec.co.uk>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13*/
14
15/* This code is the core support for the I2S block found in a number of
16 * Samsung SoC devices which is unofficially named I2S-V2. Currently the
17 * S3C2412 and the S3C64XX series use this block to provide 1 or 2 I2S
18 * channels via configurable GPIO.
19 */
20
21#ifndef __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H
22#define __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H __FILE__
23
24#define S3C_I2SV2_DIV_BCLK (1)
25#define S3C_I2SV2_DIV_RCLK (2)
26#define S3C_I2SV2_DIV_PRESCALER (3)
27
28#define S3C_I2SV2_CLKSRC_PCLK 0
29#define S3C_I2SV2_CLKSRC_AUDIOBUS 1
30#define S3C_I2SV2_CLKSRC_CDCLK 2
31
32/* Set this flag for I2S controllers that have the bit IISMOD[12]
33 * bridge/break RCLK signal and external Xi2sCDCLK pin.
34 */
35#define S3C_FEATURE_CDCLKCON (1 << 0)
36
37/**
38 * struct s3c_i2sv2_info - S3C I2S-V2 information
39 * @dev: The parent device passed to use from the probe.
40 * @regs: The pointer to the device registe block.
41 * @feature: Set of bit-flags indicating features of the controller.
42 * @master: True if the I2S core is the I2S bit clock master.
43 * @dma_playback: DMA information for playback channel.
44 * @dma_capture: DMA information for capture channel.
45 * @suspend_iismod: PM save for the IISMOD register.
46 * @suspend_iiscon: PM save for the IISCON register.
47 * @suspend_iispsr: PM save for the IISPSR register.
48 *
49 * This is the private codec state for the hardware associated with an
50 * I2S channel such as the register mappings and clock sources.
51 */
52struct s3c_i2sv2_info {
53 struct device *dev;
54 void __iomem *regs;
55
56 u32 feature;
57
58 struct clk *iis_pclk;
59 struct clk *iis_cclk;
60
61 unsigned char master;
62
63 struct s3c_dma_params *dma_playback;
64 struct s3c_dma_params *dma_capture;
65
66 u32 suspend_iismod;
67 u32 suspend_iiscon;
68 u32 suspend_iispsr;
69
70 unsigned long base;
71};
72
73extern struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai);
74
75struct s3c_i2sv2_rate_calc {
76 unsigned int clk_div; /* for prescaler */
77 unsigned int fs_div; /* for root frame clock */
78};
79
80extern int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
81 unsigned int *fstab,
82 unsigned int rate, struct clk *clk);
83
84/**
85 * s3c_i2sv2_probe - probe for i2s device helper
86 * @dai: The ASoC DAI structure supplied to the original probe.
87 * @i2s: Our local i2s structure to fill in.
88 * @base: The base address for the registers.
89 */
90extern int s3c_i2sv2_probe(struct snd_soc_dai *dai,
91 struct s3c_i2sv2_info *i2s,
92 unsigned long base);
93
94/**
95 * s3c_i2sv2_register_dai - register dai with soc core
96 * @dev: DAI device
97 * @id: DAI ID
98 * @drv: The driver structure to register
99 *
100 * Fill in any missing fields and then register the given dai with the
101 * soc core.
102 */
103extern int s3c_i2sv2_register_dai(struct device *dev, int id,
104 struct snd_soc_dai_driver *drv);
105
106#endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
new file mode 100644
index 000000000000..7ea837867124
--- /dev/null
+++ b/sound/soc/samsung/s3c2412-i2s.c
@@ -0,0 +1,212 @@
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/init.h>
20#include <linux/module.h>
21#include <linux/device.h>
22#include <linux/delay.h>
23#include <linux/gpio.h>
24#include <linux/clk.h>
25#include <linux/kernel.h>
26#include <linux/io.h>
27
28#include <sound/core.h>
29#include <sound/pcm.h>
30#include <sound/pcm_params.h>
31#include <sound/initval.h>
32#include <sound/soc.h>
33#include <mach/hardware.h>
34
35#include <mach/regs-gpio.h>
36#include <mach/dma.h>
37
38#include "dma.h"
39#include "regs-i2s-v2.h"
40#include "s3c2412-i2s.h"
41
42#define S3C2412_I2S_DEBUG 0
43
44static struct s3c2410_dma_client s3c2412_dma_client_out = {
45 .name = "I2S PCM Stereo out"
46};
47
48static struct s3c2410_dma_client s3c2412_dma_client_in = {
49 .name = "I2S PCM Stereo in"
50};
51
52static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = {
53 .client = &s3c2412_dma_client_out,
54 .channel = DMACH_I2S_OUT,
55 .dma_addr = S3C2410_PA_IIS + S3C2412_IISTXD,
56 .dma_size = 4,
57};
58
59static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
60 .client = &s3c2412_dma_client_in,
61 .channel = DMACH_I2S_IN,
62 .dma_addr = S3C2410_PA_IIS + S3C2412_IISRXD,
63 .dma_size = 4,
64};
65
66static struct s3c_i2sv2_info s3c2412_i2s;
67
68static int s3c2412_i2s_probe(struct snd_soc_dai *dai)
69{
70 int ret;
71
72 pr_debug("Entered %s\n", __func__);
73
74 ret = s3c_i2sv2_probe(dai, &s3c2412_i2s, S3C2410_PA_IIS);
75 if (ret)
76 return ret;
77
78 s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in;
79 s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
80
81 s3c2412_i2s.iis_cclk = clk_get(dai->dev, "i2sclk");
82 if (s3c2412_i2s.iis_cclk == NULL) {
83 pr_err("failed to get i2sclk clock\n");
84 iounmap(s3c2412_i2s.regs);
85 return -ENODEV;
86 }
87
88 /* Set MPLL as the source for IIS CLK */
89
90 clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
91 clk_enable(s3c2412_i2s.iis_cclk);
92
93 s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
94
95 /* Configure the I2S pins in correct mode */
96 s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
97 s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
98 s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
99 s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
100 s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
101
102 return 0;
103}
104
105static int s3c2412_i2s_remove(struct snd_soc_dai *dai)
106{
107 clk_disable(s3c2412_i2s.iis_cclk);
108 clk_put(s3c2412_i2s.iis_cclk);
109 iounmap(s3c2412_i2s.regs);
110
111 return 0;
112}
113
114static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
115 struct snd_pcm_hw_params *params,
116 struct snd_soc_dai *cpu_dai)
117{
118 struct s3c_i2sv2_info *i2s = snd_soc_dai_get_drvdata(cpu_dai);
119 struct s3c_dma_params *dma_data;
120 u32 iismod;
121
122 pr_debug("Entered %s\n", __func__);
123
124 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
125 dma_data = i2s->dma_playback;
126 else
127 dma_data = i2s->dma_capture;
128
129 snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
130
131 iismod = readl(i2s->regs + S3C2412_IISMOD);
132 pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
133
134 switch (params_format(params)) {
135 case SNDRV_PCM_FORMAT_S8:
136 iismod |= S3C2412_IISMOD_8BIT;
137 break;
138 case SNDRV_PCM_FORMAT_S16_LE:
139 iismod &= ~S3C2412_IISMOD_8BIT;
140 break;
141 }
142
143 writel(iismod, i2s->regs + S3C2412_IISMOD);
144 pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
145
146 return 0;
147}
148
149#define S3C2412_I2S_RATES \
150 (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
151 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
152 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
153
154static struct snd_soc_dai_ops s3c2412_i2s_dai_ops = {
155 .hw_params = s3c2412_i2s_hw_params,
156};
157
158static struct snd_soc_dai_driver s3c2412_i2s_dai = {
159 .probe = s3c2412_i2s_probe,
160 .remove = s3c2412_i2s_remove,
161 .playback = {
162 .channels_min = 2,
163 .channels_max = 2,
164 .rates = S3C2412_I2S_RATES,
165 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
166 },
167 .capture = {
168 .channels_min = 2,
169 .channels_max = 2,
170 .rates = S3C2412_I2S_RATES,
171 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
172 },
173 .ops = &s3c2412_i2s_dai_ops,
174};
175
176static __devinit int s3c2412_iis_dev_probe(struct platform_device *pdev)
177{
178 return snd_soc_register_dai(&pdev->dev, &s3c2412_i2s_dai);
179}
180
181static __devexit int s3c2412_iis_dev_remove(struct platform_device *pdev)
182{
183 snd_soc_unregister_dai(&pdev->dev);
184 return 0;
185}
186
187static struct platform_driver s3c2412_iis_driver = {
188 .probe = s3c2412_iis_dev_probe,
189 .remove = s3c2412_iis_dev_remove,
190 .driver = {
191 .name = "s3c2412-iis",
192 .owner = THIS_MODULE,
193 },
194};
195
196static int __init s3c2412_i2s_init(void)
197{
198 return platform_driver_register(&s3c2412_iis_driver);
199}
200module_init(s3c2412_i2s_init);
201
202static void __exit s3c2412_i2s_exit(void)
203{
204 platform_driver_unregister(&s3c2412_iis_driver);
205}
206module_exit(s3c2412_i2s_exit);
207
208/* Module information */
209MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
210MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
211MODULE_LICENSE("GPL");
212MODULE_ALIAS("platform:s3c2412-iis");
diff --git a/sound/soc/samsung/s3c2412-i2s.h b/sound/soc/samsung/s3c2412-i2s.h
new file mode 100644
index 000000000000..02ad5794c0a9
--- /dev/null
+++ b/sound/soc/samsung/s3c2412-i2s.h
@@ -0,0 +1,27 @@
1/* sound/soc/samsung/s3c2412-i2s.c
2 *
3 * ALSA Soc Audio Layer - S3C2412 I2S driver
4 *
5 * Copyright (c) 2007 Simtec Electronics
6 * http://armlinux.simtec.co.uk/
7 * Ben Dooks <ben@simtec.co.uk>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13*/
14
15#ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H
16#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__
17
18#include "s3c-i2s-v2.h"
19
20#define S3C2412_DIV_BCLK S3C_I2SV2_DIV_BCLK
21#define S3C2412_DIV_RCLK S3C_I2SV2_DIV_RCLK
22#define S3C2412_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER
23
24#define S3C2412_CLKSRC_PCLK S3C_I2SV2_CLKSRC_PCLK
25#define S3C2412_CLKSRC_I2SCLK S3C_I2SV2_CLKSRC_AUDIOBUS
26
27#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
new file mode 100644
index 000000000000..13e41ed8e22b
--- /dev/null
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -0,0 +1,519 @@
1/*
2 * s3c24xx-i2s.c -- ALSA Soc Audio Layer
3 *
4 * (c) 2006 Wolfson Microelectronics PLC.
5 * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
6 *
7 * Copyright 2004-2005 Simtec Electronics
8 * http://armlinux.simtec.co.uk/
9 * Ben Dooks <ben@simtec.co.uk>
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
15 */
16
17#include <linux/init.h>
18#include <linux/module.h>
19#include <linux/device.h>
20#include <linux/delay.h>
21#include <linux/clk.h>
22#include <linux/jiffies.h>
23#include <linux/io.h>
24#include <linux/gpio.h>
25
26#include <sound/core.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/initval.h>
30#include <sound/soc.h>
31
32#include <mach/hardware.h>
33#include <mach/regs-gpio.h>
34#include <mach/regs-clock.h>
35
36#include <asm/dma.h>
37#include <mach/dma.h>
38
39#include <plat/regs-iis.h>
40
41#include "dma.h"
42#include "s3c24xx-i2s.h"
43
44static struct s3c2410_dma_client s3c24xx_dma_client_out = {
45 .name = "I2S PCM Stereo out"
46};
47
48static struct s3c2410_dma_client s3c24xx_dma_client_in = {
49 .name = "I2S PCM Stereo in"
50};
51
52static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = {
53 .client = &s3c24xx_dma_client_out,
54 .channel = DMACH_I2S_OUT,
55 .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO,
56 .dma_size = 2,
57};
58
59static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = {
60 .client = &s3c24xx_dma_client_in,
61 .channel = DMACH_I2S_IN,
62 .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO,
63 .dma_size = 2,
64};
65
66struct s3c24xx_i2s_info {
67 void __iomem *regs;
68 struct clk *iis_clk;
69 u32 iiscon;
70 u32 iismod;
71 u32 iisfcon;
72 u32 iispsr;
73};
74static struct s3c24xx_i2s_info s3c24xx_i2s;
75
76static void s3c24xx_snd_txctrl(int on)
77{
78 u32 iisfcon;
79 u32 iiscon;
80 u32 iismod;
81
82 pr_debug("Entered %s\n", __func__);
83
84 iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
85 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
86 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
87
88 pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
89
90 if (on) {
91 iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
92 iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
93 iiscon &= ~S3C2410_IISCON_TXIDLE;
94 iismod |= S3C2410_IISMOD_TXMODE;
95
96 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
97 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
98 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
99 } else {
100 /* note, we have to disable the FIFOs otherwise bad things
101 * seem to happen when the DMA stops. According to the
102 * Samsung supplied kernel, this should allow the DMA
103 * engine and FIFOs to reset. If this isn't allowed, the
104 * DMA engine will simply freeze randomly.
105 */
106
107 iisfcon &= ~S3C2410_IISFCON_TXENABLE;
108 iisfcon &= ~S3C2410_IISFCON_TXDMA;
109 iiscon |= S3C2410_IISCON_TXIDLE;
110 iiscon &= ~S3C2410_IISCON_TXDMAEN;
111 iismod &= ~S3C2410_IISMOD_TXMODE;
112
113 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
114 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
115 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
116 }
117
118 pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
119}
120
121static void s3c24xx_snd_rxctrl(int on)
122{
123 u32 iisfcon;
124 u32 iiscon;
125 u32 iismod;
126
127 pr_debug("Entered %s\n", __func__);
128
129 iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
130 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
131 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
132
133 pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
134
135 if (on) {
136 iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
137 iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
138 iiscon &= ~S3C2410_IISCON_RXIDLE;
139 iismod |= S3C2410_IISMOD_RXMODE;
140
141 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
142 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
143 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
144 } else {
145 /* note, we have to disable the FIFOs otherwise bad things
146 * seem to happen when the DMA stops. According to the
147 * Samsung supplied kernel, this should allow the DMA
148 * engine and FIFOs to reset. If this isn't allowed, the
149 * DMA engine will simply freeze randomly.
150 */
151
152 iisfcon &= ~S3C2410_IISFCON_RXENABLE;
153 iisfcon &= ~S3C2410_IISFCON_RXDMA;
154 iiscon |= S3C2410_IISCON_RXIDLE;
155 iiscon &= ~S3C2410_IISCON_RXDMAEN;
156 iismod &= ~S3C2410_IISMOD_RXMODE;
157
158 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
159 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
160 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
161 }
162
163 pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
164}
165
166/*
167 * Wait for the LR signal to allow synchronisation to the L/R clock
168 * from the codec. May only be needed for slave mode.
169 */
170static int s3c24xx_snd_lrsync(void)
171{
172 u32 iiscon;
173 int timeout = 50; /* 5ms */
174
175 pr_debug("Entered %s\n", __func__);
176
177 while (1) {
178 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
179 if (iiscon & S3C2410_IISCON_LRINDEX)
180 break;
181
182 if (!timeout--)
183 return -ETIMEDOUT;
184 udelay(100);
185 }
186
187 return 0;
188}
189
190/*
191 * Check whether CPU is the master or slave
192 */
193static inline int s3c24xx_snd_is_clkmaster(void)
194{
195 pr_debug("Entered %s\n", __func__);
196
197 return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
198}
199
200/*
201 * Set S3C24xx I2S DAI format
202 */
203static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
204 unsigned int fmt)
205{
206 u32 iismod;
207
208 pr_debug("Entered %s\n", __func__);
209
210 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
211 pr_debug("hw_params r: IISMOD: %x \n", iismod);
212
213 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
214 case SND_SOC_DAIFMT_CBM_CFM:
215 iismod |= S3C2410_IISMOD_SLAVE;
216 break;
217 case SND_SOC_DAIFMT_CBS_CFS:
218 iismod &= ~S3C2410_IISMOD_SLAVE;
219 break;
220 default:
221 return -EINVAL;
222 }
223
224 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
225 case SND_SOC_DAIFMT_LEFT_J:
226 iismod |= S3C2410_IISMOD_MSB;
227 break;
228 case SND_SOC_DAIFMT_I2S:
229 iismod &= ~S3C2410_IISMOD_MSB;
230 break;
231 default:
232 return -EINVAL;
233 }
234
235 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
236 pr_debug("hw_params w: IISMOD: %x \n", iismod);
237 return 0;
238}
239
240static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
241 struct snd_pcm_hw_params *params,
242 struct snd_soc_dai *dai)
243{
244 struct snd_soc_pcm_runtime *rtd = substream->private_data;
245 struct s3c_dma_params *dma_data;
246 u32 iismod;
247
248 pr_debug("Entered %s\n", __func__);
249
250 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
251 dma_data = &s3c24xx_i2s_pcm_stereo_out;
252 else
253 dma_data = &s3c24xx_i2s_pcm_stereo_in;
254
255 snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
256
257 /* Working copies of register */
258 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
259 pr_debug("hw_params r: IISMOD: %x\n", iismod);
260
261 switch (params_format(params)) {
262 case SNDRV_PCM_FORMAT_S8:
263 iismod &= ~S3C2410_IISMOD_16BIT;
264 dma_data->dma_size = 1;
265 break;
266 case SNDRV_PCM_FORMAT_S16_LE:
267 iismod |= S3C2410_IISMOD_16BIT;
268 dma_data->dma_size = 2;
269 break;
270 default:
271 return -EINVAL;
272 }
273
274 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
275 pr_debug("hw_params w: IISMOD: %x\n", iismod);
276 return 0;
277}
278
279static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
280 struct snd_soc_dai *dai)
281{
282 int ret = 0;
283 struct s3c_dma_params *dma_data =
284 snd_soc_dai_get_dma_data(dai, substream);
285
286 pr_debug("Entered %s\n", __func__);
287
288 switch (cmd) {
289 case SNDRV_PCM_TRIGGER_START:
290 case SNDRV_PCM_TRIGGER_RESUME:
291 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
292 if (!s3c24xx_snd_is_clkmaster()) {
293 ret = s3c24xx_snd_lrsync();
294 if (ret)
295 goto exit_err;
296 }
297
298 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
299 s3c24xx_snd_rxctrl(1);
300 else
301 s3c24xx_snd_txctrl(1);
302
303 s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED);
304 break;
305 case SNDRV_PCM_TRIGGER_STOP:
306 case SNDRV_PCM_TRIGGER_SUSPEND:
307 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
308 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
309 s3c24xx_snd_rxctrl(0);
310 else
311 s3c24xx_snd_txctrl(0);
312 break;
313 default:
314 ret = -EINVAL;
315 break;
316 }
317
318exit_err:
319 return ret;
320}
321
322/*
323 * Set S3C24xx Clock source
324 */
325static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
326 int clk_id, unsigned int freq, int dir)
327{
328 u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
329
330 pr_debug("Entered %s\n", __func__);
331
332 iismod &= ~S3C2440_IISMOD_MPLL;
333
334 switch (clk_id) {
335 case S3C24XX_CLKSRC_PCLK:
336 break;
337 case S3C24XX_CLKSRC_MPLL:
338 iismod |= S3C2440_IISMOD_MPLL;
339 break;
340 default:
341 return -EINVAL;
342 }
343
344 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
345 return 0;
346}
347
348/*
349 * Set S3C24xx Clock dividers
350 */
351static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
352 int div_id, int div)
353{
354 u32 reg;
355
356 pr_debug("Entered %s\n", __func__);
357
358 switch (div_id) {
359 case S3C24XX_DIV_BCLK:
360 reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
361 writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
362 break;
363 case S3C24XX_DIV_MCLK:
364 reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
365 writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
366 break;
367 case S3C24XX_DIV_PRESCALER:
368 writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
369 reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
370 writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
371 break;
372 default:
373 return -EINVAL;
374 }
375
376 return 0;
377}
378
379/*
380 * To avoid duplicating clock code, allow machine driver to
381 * get the clockrate from here.
382 */
383u32 s3c24xx_i2s_get_clockrate(void)
384{
385 return clk_get_rate(s3c24xx_i2s.iis_clk);
386}
387EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
388
389static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
390{
391 pr_debug("Entered %s\n", __func__);
392
393 s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
394 if (s3c24xx_i2s.regs == NULL)
395 return -ENXIO;
396
397 s3c24xx_i2s.iis_clk = clk_get(dai->dev, "iis");
398 if (s3c24xx_i2s.iis_clk == NULL) {
399 pr_err("failed to get iis_clock\n");
400 iounmap(s3c24xx_i2s.regs);
401 return -ENODEV;
402 }
403 clk_enable(s3c24xx_i2s.iis_clk);
404
405 /* Configure the I2S pins in correct mode */
406 s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
407 s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
408 s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
409 s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
410 s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
411
412 writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
413
414 s3c24xx_snd_txctrl(0);
415 s3c24xx_snd_rxctrl(0);
416
417 return 0;
418}
419
420#ifdef CONFIG_PM
421static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai)
422{
423 pr_debug("Entered %s\n", __func__);
424
425 s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
426 s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
427 s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
428 s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
429
430 clk_disable(s3c24xx_i2s.iis_clk);
431
432 return 0;
433}
434
435static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai)
436{
437 pr_debug("Entered %s\n", __func__);
438 clk_enable(s3c24xx_i2s.iis_clk);
439
440 writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
441 writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
442 writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
443 writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
444
445 return 0;
446}
447#else
448#define s3c24xx_i2s_suspend NULL
449#define s3c24xx_i2s_resume NULL
450#endif
451
452
453#define S3C24XX_I2S_RATES \
454 (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
455 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
456 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
457
458static struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
459 .trigger = s3c24xx_i2s_trigger,
460 .hw_params = s3c24xx_i2s_hw_params,
461 .set_fmt = s3c24xx_i2s_set_fmt,
462 .set_clkdiv = s3c24xx_i2s_set_clkdiv,
463 .set_sysclk = s3c24xx_i2s_set_sysclk,
464};
465
466static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
467 .probe = s3c24xx_i2s_probe,
468 .suspend = s3c24xx_i2s_suspend,
469 .resume = s3c24xx_i2s_resume,
470 .playback = {
471 .channels_min = 2,
472 .channels_max = 2,
473 .rates = S3C24XX_I2S_RATES,
474 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
475 .capture = {
476 .channels_min = 2,
477 .channels_max = 2,
478 .rates = S3C24XX_I2S_RATES,
479 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
480 .ops = &s3c24xx_i2s_dai_ops,
481};
482
483static __devinit int s3c24xx_iis_dev_probe(struct platform_device *pdev)
484{
485 return snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);
486}
487
488static __devexit int s3c24xx_iis_dev_remove(struct platform_device *pdev)
489{
490 snd_soc_unregister_dai(&pdev->dev);
491 return 0;
492}
493
494static struct platform_driver s3c24xx_iis_driver = {
495 .probe = s3c24xx_iis_dev_probe,
496 .remove = s3c24xx_iis_dev_remove,
497 .driver = {
498 .name = "s3c24xx-iis",
499 .owner = THIS_MODULE,
500 },
501};
502
503static int __init s3c24xx_i2s_init(void)
504{
505 return platform_driver_register(&s3c24xx_iis_driver);
506}
507module_init(s3c24xx_i2s_init);
508
509static void __exit s3c24xx_i2s_exit(void)
510{
511 platform_driver_unregister(&s3c24xx_iis_driver);
512}
513module_exit(s3c24xx_i2s_exit);
514
515/* Module information */
516MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
517MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
518MODULE_LICENSE("GPL");
519MODULE_ALIAS("platform:s3c24xx-iis");
diff --git a/sound/soc/samsung/s3c24xx-i2s.h b/sound/soc/samsung/s3c24xx-i2s.h
new file mode 100644
index 000000000000..f9ca04edacb7
--- /dev/null
+++ b/sound/soc/samsung/s3c24xx-i2s.h
@@ -0,0 +1,35 @@
1/*
2 * s3c24xx-i2s.c -- ALSA Soc Audio Layer
3 *
4 * Copyright 2005 Wolfson Microelectronics PLC.
5 * Author: Graeme Gregory
6 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * Revision history
14 * 10th Nov 2006 Initial version.
15 */
16
17#ifndef S3C24XXI2S_H_
18#define S3C24XXI2S_H_
19
20/* clock sources */
21#define S3C24XX_CLKSRC_PCLK 0
22#define S3C24XX_CLKSRC_MPLL 1
23
24/* Clock dividers */
25#define S3C24XX_DIV_MCLK 0
26#define S3C24XX_DIV_BCLK 1
27#define S3C24XX_DIV_PRESCALER 2
28
29/* prescaler */
30#define S3C24XX_PRESCALE(a,b) \
31 (((a - 1) << S3C2410_IISPSR_INTSHIFT) | ((b - 1) << S3C2410_IISPSR_EXTSHFIT))
32
33u32 s3c24xx_i2s_get_clockrate(void);
34
35#endif /*S3C24XXI2S_H_*/
diff --git a/sound/soc/samsung/s3c24xx_simtec.c b/sound/soc/samsung/s3c24xx_simtec.c
new file mode 100644
index 000000000000..a434032d1832
--- /dev/null
+++ b/sound/soc/samsung/s3c24xx_simtec.c
@@ -0,0 +1,394 @@
1/* sound/soc/samsung/s3c24xx_simtec.c
2 *
3 * Copyright 2009 Simtec Electronics
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8*/
9
10#include <linux/module.h>
11#include <linux/moduleparam.h>
12#include <linux/platform_device.h>
13#include <linux/gpio.h>
14#include <linux/clk.h>
15#include <linux/i2c.h>
16
17#include <sound/core.h>
18#include <sound/pcm.h>
19#include <sound/soc.h>
20
21#include <plat/audio-simtec.h>
22
23#include "dma.h"
24#include "s3c24xx-i2s.h"
25#include "s3c24xx_simtec.h"
26
27static struct s3c24xx_audio_simtec_pdata *pdata;
28static struct clk *xtal_clk;
29
30static int spk_gain;
31static int spk_unmute;
32
33/**
34 * speaker_gain_get - read the speaker gain setting.
35 * @kcontrol: The control for the speaker gain.
36 * @ucontrol: The value that needs to be updated.
37 *
38 * Read the value for the AMP gain control.
39 */
40static int speaker_gain_get(struct snd_kcontrol *kcontrol,
41 struct snd_ctl_elem_value *ucontrol)
42{
43 ucontrol->value.integer.value[0] = spk_gain;
44 return 0;
45}
46
47/**
48 * speaker_gain_set - set the value of the speaker amp gain
49 * @value: The value to write.
50 */
51static void speaker_gain_set(int value)
52{
53 gpio_set_value_cansleep(pdata->amp_gain[0], value & 1);
54 gpio_set_value_cansleep(pdata->amp_gain[1], value >> 1);
55}
56
57/**
58 * speaker_gain_put - set the speaker gain setting.
59 * @kcontrol: The control for the speaker gain.
60 * @ucontrol: The value that needs to be set.
61 *
62 * Set the value of the speaker gain from the specified
63 * @ucontrol setting.
64 *
65 * Note, if the speaker amp is muted, then we do not set a gain value
66 * as at-least one of the ICs that is fitted will try and power up even
67 * if the main control is set to off.
68 */
69static int speaker_gain_put(struct snd_kcontrol *kcontrol,
70 struct snd_ctl_elem_value *ucontrol)
71{
72 int value = ucontrol->value.integer.value[0];
73
74 spk_gain = value;
75
76 if (!spk_unmute)
77 speaker_gain_set(value);
78
79 return 0;
80}
81
82static const struct snd_kcontrol_new amp_gain_controls[] = {
83 SOC_SINGLE_EXT("Speaker Gain", 0, 0, 3, 0,
84 speaker_gain_get, speaker_gain_put),
85};
86
87/**
88 * spk_unmute_state - set the unmute state of the speaker
89 * @to: zero to unmute, non-zero to ununmute.
90 */
91static void spk_unmute_state(int to)
92{
93 pr_debug("%s: to=%d\n", __func__, to);
94
95 spk_unmute = to;
96 gpio_set_value(pdata->amp_gpio, to);
97
98 /* if we're umuting, also re-set the gain */
99 if (to && pdata->amp_gain[0] > 0)
100 speaker_gain_set(spk_gain);
101}
102
103/**
104 * speaker_unmute_get - read the speaker unmute setting.
105 * @kcontrol: The control for the speaker gain.
106 * @ucontrol: The value that needs to be updated.
107 *
108 * Read the value for the AMP gain control.
109 */
110static int speaker_unmute_get(struct snd_kcontrol *kcontrol,
111 struct snd_ctl_elem_value *ucontrol)
112{
113 ucontrol->value.integer.value[0] = spk_unmute;
114 return 0;
115}
116
117/**
118 * speaker_unmute_put - set the speaker unmute setting.
119 * @kcontrol: The control for the speaker gain.
120 * @ucontrol: The value that needs to be set.
121 *
122 * Set the value of the speaker gain from the specified
123 * @ucontrol setting.
124 */
125static int speaker_unmute_put(struct snd_kcontrol *kcontrol,
126 struct snd_ctl_elem_value *ucontrol)
127{
128 spk_unmute_state(ucontrol->value.integer.value[0]);
129 return 0;
130}
131
132/* This is added as a manual control as the speaker amps create clicks
133 * when their power state is changed, which are far more noticeable than
134 * anything produced by the CODEC itself.
135 */
136static const struct snd_kcontrol_new amp_unmute_controls[] = {
137 SOC_SINGLE_EXT("Speaker Switch", 0, 0, 1, 0,
138 speaker_unmute_get, speaker_unmute_put),
139};
140
141void simtec_audio_init(struct snd_soc_pcm_runtime *rtd)
142{
143 struct snd_soc_codec *codec = rtd->codec;
144
145 if (pdata->amp_gpio > 0) {
146 pr_debug("%s: adding amp routes\n", __func__);
147
148 snd_soc_add_controls(codec, amp_unmute_controls,
149 ARRAY_SIZE(amp_unmute_controls));
150 }
151
152 if (pdata->amp_gain[0] > 0) {
153 pr_debug("%s: adding amp controls\n", __func__);
154 snd_soc_add_controls(codec, amp_gain_controls,
155 ARRAY_SIZE(amp_gain_controls));
156 }
157}
158EXPORT_SYMBOL_GPL(simtec_audio_init);
159
160#define CODEC_CLOCK 12000000
161
162/**
163 * simtec_hw_params - update hardware parameters
164 * @substream: The audio substream instance.
165 * @params: The parameters requested.
166 *
167 * Update the codec data routing and configuration settings
168 * from the supplied data.
169 */
170static int simtec_hw_params(struct snd_pcm_substream *substream,
171 struct snd_pcm_hw_params *params)
172{
173 struct snd_soc_pcm_runtime *rtd = substream->private_data;
174 struct snd_soc_dai *codec_dai = rtd->codec_dai;
175 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
176 int ret;
177
178 /* Set the CODEC as the bus clock master, I2S */
179 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
180 SND_SOC_DAIFMT_NB_NF |
181 SND_SOC_DAIFMT_CBM_CFM);
182 if (ret) {
183 pr_err("%s: failed set cpu dai format\n", __func__);
184 return ret;
185 }
186
187 /* Set the CODEC as the bus clock master */
188 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
189 SND_SOC_DAIFMT_NB_NF |
190 SND_SOC_DAIFMT_CBM_CFM);
191 if (ret) {
192 pr_err("%s: failed set codec dai format\n", __func__);
193 return ret;
194 }
195
196 ret = snd_soc_dai_set_sysclk(codec_dai, 0,
197 CODEC_CLOCK, SND_SOC_CLOCK_IN);
198 if (ret) {
199 pr_err( "%s: failed setting codec sysclk\n", __func__);
200 return ret;
201 }
202
203 if (pdata->use_mpllin) {
204 ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
205 0, SND_SOC_CLOCK_OUT);
206
207 if (ret) {
208 pr_err("%s: failed to set MPLLin as clksrc\n",
209 __func__);
210 return ret;
211 }
212 }
213
214 if (pdata->output_cdclk) {
215 int cdclk_scale;
216
217 cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK;
218 cdclk_scale--;
219
220 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
221 cdclk_scale);
222 }
223
224 return 0;
225}
226
227static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd)
228{
229 /* call any board supplied startup code, this currently only
230 * covers the bast/vr1000 which have a CPLD in the way of the
231 * LRCLK */
232 if (pd->startup)
233 pd->startup();
234
235 return 0;
236}
237
238static struct snd_soc_ops simtec_snd_ops = {
239 .hw_params = simtec_hw_params,
240};
241
242/**
243 * attach_gpio_amp - get and configure the necessary gpios
244 * @dev: The device we're probing.
245 * @pd: The platform data supplied by the board.
246 *
247 * If there is a GPIO based amplifier attached to the board, claim
248 * the necessary GPIO lines for it, and set default values.
249 */
250static int attach_gpio_amp(struct device *dev,
251 struct s3c24xx_audio_simtec_pdata *pd)
252{
253 int ret;
254
255 /* attach gpio amp gain (if any) */
256 if (pdata->amp_gain[0] > 0) {
257 ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0");
258 if (ret) {
259 dev_err(dev, "cannot get amp gpio gain0\n");
260 return ret;
261 }
262
263 ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1");
264 if (ret) {
265 dev_err(dev, "cannot get amp gpio gain1\n");
266 gpio_free(pdata->amp_gain[0]);
267 return ret;
268 }
269
270 gpio_direction_output(pd->amp_gain[0], 0);
271 gpio_direction_output(pd->amp_gain[1], 0);
272 }
273
274 /* note, currently we assume GPA0 isn't valid amp */
275 if (pdata->amp_gpio > 0) {
276 ret = gpio_request(pd->amp_gpio, "gpio-amp");
277 if (ret) {
278 dev_err(dev, "cannot get amp gpio %d (%d)\n",
279 pd->amp_gpio, ret);
280 goto err_amp;
281 }
282
283 /* set the amp off at startup */
284 spk_unmute_state(0);
285 }
286
287 return 0;
288
289err_amp:
290 if (pd->amp_gain[0] > 0) {
291 gpio_free(pd->amp_gain[0]);
292 gpio_free(pd->amp_gain[1]);
293 }
294
295 return ret;
296}
297
298static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd)
299{
300 if (pd->amp_gain[0] > 0) {
301 gpio_free(pd->amp_gain[0]);
302 gpio_free(pd->amp_gain[1]);
303 }
304
305 if (pd->amp_gpio > 0)
306 gpio_free(pd->amp_gpio);
307}
308
309#ifdef CONFIG_PM
310int simtec_audio_resume(struct device *dev)
311{
312 simtec_call_startup(pdata);
313 return 0;
314}
315
316const struct dev_pm_ops simtec_audio_pmops = {
317 .resume = simtec_audio_resume,
318};
319EXPORT_SYMBOL_GPL(simtec_audio_pmops);
320#endif
321
322int __devinit simtec_audio_core_probe(struct platform_device *pdev,
323 struct snd_soc_card *card)
324{
325 struct platform_device *snd_dev;
326 int ret;
327
328 card->dai_link->ops = &simtec_snd_ops;
329
330 pdata = pdev->dev.platform_data;
331 if (!pdata) {
332 dev_err(&pdev->dev, "no platform data supplied\n");
333 return -EINVAL;
334 }
335
336 simtec_call_startup(pdata);
337
338 xtal_clk = clk_get(&pdev->dev, "xtal");
339 if (IS_ERR(xtal_clk)) {
340 dev_err(&pdev->dev, "could not get clkout0\n");
341 return -EINVAL;
342 }
343
344 dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk));
345
346 ret = attach_gpio_amp(&pdev->dev, pdata);
347 if (ret)
348 goto err_clk;
349
350 snd_dev = platform_device_alloc("soc-audio", -1);
351 if (!snd_dev) {
352 dev_err(&pdev->dev, "failed to alloc soc-audio devicec\n");
353 ret = -ENOMEM;
354 goto err_gpio;
355 }
356
357 platform_set_drvdata(snd_dev, card);
358
359 ret = platform_device_add(snd_dev);
360 if (ret) {
361 dev_err(&pdev->dev, "failed to add soc-audio dev\n");
362 goto err_pdev;
363 }
364
365 platform_set_drvdata(pdev, snd_dev);
366 return 0;
367
368err_pdev:
369 platform_device_put(snd_dev);
370
371err_gpio:
372 detach_gpio_amp(pdata);
373
374err_clk:
375 clk_put(xtal_clk);
376 return ret;
377}
378EXPORT_SYMBOL_GPL(simtec_audio_core_probe);
379
380int __devexit simtec_audio_remove(struct platform_device *pdev)
381{
382 struct platform_device *snd_dev = platform_get_drvdata(pdev);
383
384 platform_device_unregister(snd_dev);
385
386 detach_gpio_amp(pdata);
387 clk_put(xtal_clk);
388 return 0;
389}
390EXPORT_SYMBOL_GPL(simtec_audio_remove);
391
392MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
393MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support");
394MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c24xx_simtec.h b/sound/soc/samsung/s3c24xx_simtec.h
new file mode 100644
index 000000000000..8270748a2c41
--- /dev/null
+++ b/sound/soc/samsung/s3c24xx_simtec.h
@@ -0,0 +1,22 @@
1/* sound/soc/samsung/s3c24xx_simtec.h
2 *
3 * Copyright 2009 Simtec Electronics
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8*/
9
10extern void simtec_audio_init(struct snd_soc_pcm_runtime *rtd);
11
12extern int simtec_audio_core_probe(struct platform_device *pdev,
13 struct snd_soc_card *card);
14
15extern int simtec_audio_remove(struct platform_device *pdev);
16
17#ifdef CONFIG_PM
18extern const struct dev_pm_ops simtec_audio_pmops;
19#define simtec_audio_pm &simtec_audio_pmops
20#else
21#define simtec_audio_pm NULL
22#endif
diff --git a/sound/soc/samsung/s3c24xx_simtec_hermes.c b/sound/soc/samsung/s3c24xx_simtec_hermes.c
new file mode 100644
index 000000000000..0083aff5a83d
--- /dev/null
+++ b/sound/soc/samsung/s3c24xx_simtec_hermes.c
@@ -0,0 +1,146 @@
1/* sound/soc/samsung/s3c24xx_simtec_hermes.c
2 *
3 * Copyright 2009 Simtec Electronics
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8*/
9
10#include <linux/module.h>
11#include <linux/clk.h>
12#include <linux/platform_device.h>
13
14#include <sound/core.h>
15#include <sound/pcm.h>
16#include <sound/soc.h>
17
18#include <plat/audio-simtec.h>
19
20#include "dma.h"
21#include "s3c24xx-i2s.h"
22#include "s3c24xx_simtec.h"
23
24#include "../codecs/tlv320aic3x.h"
25
26static const struct snd_soc_dapm_widget dapm_widgets[] = {
27 SND_SOC_DAPM_LINE("GSM Out", NULL),
28 SND_SOC_DAPM_LINE("GSM In", NULL),
29 SND_SOC_DAPM_LINE("Line In", NULL),
30 SND_SOC_DAPM_LINE("Line Out", NULL),
31 SND_SOC_DAPM_LINE("ZV", NULL),
32 SND_SOC_DAPM_MIC("Mic Jack", NULL),
33 SND_SOC_DAPM_HP("Headphone Jack", NULL),
34};
35
36static const struct snd_soc_dapm_route base_map[] = {
37 /* Headphone connected to HP{L,R}OUT and HP{L,R}COM */
38
39 { "Headphone Jack", NULL, "HPLOUT" },
40 { "Headphone Jack", NULL, "HPLCOM" },
41 { "Headphone Jack", NULL, "HPROUT" },
42 { "Headphone Jack", NULL, "HPRCOM" },
43
44 /* ZV connected to Line1 */
45
46 { "LINE1L", NULL, "ZV" },
47 { "LINE1R", NULL, "ZV" },
48
49 /* Line In connected to Line2 */
50
51 { "LINE2L", NULL, "Line In" },
52 { "LINE2R", NULL, "Line In" },
53
54 /* Microphone connected to MIC3R and MIC_BIAS */
55
56 { "MIC3L", NULL, "Mic Jack" },
57
58 /* GSM connected to MONO_LOUT and MIC3L (in) */
59
60 { "GSM Out", NULL, "MONO_LOUT" },
61 { "MIC3L", NULL, "GSM In" },
62
63 /* Speaker is connected to LINEOUT{LN,LP,RN,RP}, however we are
64 * not using the DAPM to power it up and down as there it makes
65 * a click when powering up. */
66};
67
68/**
69 * simtec_hermes_init - initialise and add controls
70 * @codec; The codec instance to attach to.
71 *
72 * Attach our controls and configure the necessary codec
73 * mappings for our sound card instance.
74*/
75static int simtec_hermes_init(struct snd_soc_pcm_runtime *rtd)
76{
77 struct snd_soc_codec *codec = rtd->codec;
78 struct snd_soc_dapm_context *dapm = &codec->dapm;
79
80 snd_soc_dapm_new_controls(dapm, dapm_widgets,
81 ARRAY_SIZE(dapm_widgets));
82
83 snd_soc_dapm_add_routes(dapm, base_map, ARRAY_SIZE(base_map));
84
85 snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
86 snd_soc_dapm_enable_pin(dapm, "Line In");
87 snd_soc_dapm_enable_pin(dapm, "Line Out");
88 snd_soc_dapm_enable_pin(dapm, "Mic Jack");
89
90 simtec_audio_init(rtd);
91 snd_soc_dapm_sync(dapm);
92
93 return 0;
94}
95
96static struct snd_soc_dai_link simtec_dai_aic33 = {
97 .name = "tlv320aic33",
98 .stream_name = "TLV320AIC33",
99 .codec_name = "tlv320aic3x-codec.0-0x1a",
100 .cpu_dai_name = "s3c24xx-i2s",
101 .codec_dai_name = "tlv320aic3x-hifi",
102 .platform_name = "samsung-audio",
103 .init = simtec_hermes_init,
104};
105
106/* simtec audio machine driver */
107static struct snd_soc_card snd_soc_machine_simtec_aic33 = {
108 .name = "Simtec-Hermes",
109 .dai_link = &simtec_dai_aic33,
110 .num_links = 1,
111};
112
113static int __devinit simtec_audio_hermes_probe(struct platform_device *pd)
114{
115 dev_info(&pd->dev, "probing....\n");
116 return simtec_audio_core_probe(pd, &snd_soc_machine_simtec_aic33);
117}
118
119static struct platform_driver simtec_audio_hermes_platdrv = {
120 .driver = {
121 .owner = THIS_MODULE,
122 .name = "s3c24xx-simtec-hermes-snd",
123 .pm = simtec_audio_pm,
124 },
125 .probe = simtec_audio_hermes_probe,
126 .remove = __devexit_p(simtec_audio_remove),
127};
128
129MODULE_ALIAS("platform:s3c24xx-simtec-hermes-snd");
130
131static int __init simtec_hermes_modinit(void)
132{
133 return platform_driver_register(&simtec_audio_hermes_platdrv);
134}
135
136static void __exit simtec_hermes_modexit(void)
137{
138 platform_driver_unregister(&simtec_audio_hermes_platdrv);
139}
140
141module_init(simtec_hermes_modinit);
142module_exit(simtec_hermes_modexit);
143
144MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
145MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
146MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
new file mode 100644
index 000000000000..fbba4e367729
--- /dev/null
+++ b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
@@ -0,0 +1,134 @@
1/* sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
2 *
3 * Copyright 2009 Simtec Electronics
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8*/
9
10#include <linux/module.h>
11#include <linux/clk.h>
12#include <linux/platform_device.h>
13
14#include <sound/core.h>
15#include <sound/pcm.h>
16#include <sound/soc.h>
17
18#include <plat/audio-simtec.h>
19
20#include "dma.h"
21#include "s3c24xx-i2s.h"
22#include "s3c24xx_simtec.h"
23
24#include "../codecs/tlv320aic23.h"
25
26/* supported machines:
27 *
28 * Machine Connections AMP
29 * ------- ----------- ---
30 * BAST MIC, HPOUT, LOUT, LIN TPA2001D1 (HPOUTL,R) (gain hardwired)
31 * VR1000 HPOUT, LIN None
32 * VR2000 LIN, LOUT, MIC, HP LM4871 (HPOUTL,R)
33 * DePicture LIN, LOUT, MIC, HP LM4871 (HPOUTL,R)
34 * Anubis LIN, LOUT, MIC, HP TPA2001D1 (HPOUTL,R)
35 */
36
37static const struct snd_soc_dapm_widget dapm_widgets[] = {
38 SND_SOC_DAPM_HP("Headphone Jack", NULL),
39 SND_SOC_DAPM_LINE("Line In", NULL),
40 SND_SOC_DAPM_LINE("Line Out", NULL),
41 SND_SOC_DAPM_MIC("Mic Jack", NULL),
42};
43
44static const struct snd_soc_dapm_route base_map[] = {
45 { "Headphone Jack", NULL, "LHPOUT"},
46 { "Headphone Jack", NULL, "RHPOUT"},
47
48 { "Line Out", NULL, "LOUT" },
49 { "Line Out", NULL, "ROUT" },
50
51 { "LLINEIN", NULL, "Line In"},
52 { "RLINEIN", NULL, "Line In"},
53
54 { "MICIN", NULL, "Mic Jack"},
55};
56
57/**
58 * simtec_tlv320aic23_init - initialise and add controls
59 * @codec; The codec instance to attach to.
60 *
61 * Attach our controls and configure the necessary codec
62 * mappings for our sound card instance.
63*/
64static int simtec_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
65{
66 struct snd_soc_codec *codec = rtd->codec;
67 struct snd_soc_dapm_context *dapm = &codec->dapm;
68
69 snd_soc_dapm_new_controls(dapm, dapm_widgets,
70 ARRAY_SIZE(dapm_widgets));
71
72 snd_soc_dapm_add_routes(dapm, base_map, ARRAY_SIZE(base_map));
73
74 snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
75 snd_soc_dapm_enable_pin(dapm, "Line In");
76 snd_soc_dapm_enable_pin(dapm, "Line Out");
77 snd_soc_dapm_enable_pin(dapm, "Mic Jack");
78
79 simtec_audio_init(rtd);
80 snd_soc_dapm_sync(dapm);
81
82 return 0;
83}
84
85static struct snd_soc_dai_link simtec_dai_aic23 = {
86 .name = "tlv320aic23",
87 .stream_name = "TLV320AIC23",
88 .codec_name = "tlv320aic3x-codec.0-0x1a",
89 .cpu_dai_name = "s3c24xx-i2s",
90 .codec_dai_name = "tlv320aic3x-hifi",
91 .platform_name = "samsung-audio",
92 .init = simtec_tlv320aic23_init,
93};
94
95/* simtec audio machine driver */
96static struct snd_soc_card snd_soc_machine_simtec_aic23 = {
97 .name = "Simtec",
98 .dai_link = &simtec_dai_aic23,
99 .num_links = 1,
100};
101
102static int __devinit simtec_audio_tlv320aic23_probe(struct platform_device *pd)
103{
104 return simtec_audio_core_probe(pd, &snd_soc_machine_simtec_aic23);
105}
106
107static struct platform_driver simtec_audio_tlv320aic23_platdrv = {
108 .driver = {
109 .owner = THIS_MODULE,
110 .name = "s3c24xx-simtec-tlv320aic23",
111 .pm = simtec_audio_pm,
112 },
113 .probe = simtec_audio_tlv320aic23_probe,
114 .remove = __devexit_p(simtec_audio_remove),
115};
116
117MODULE_ALIAS("platform:s3c24xx-simtec-tlv320aic23");
118
119static int __init simtec_tlv320aic23_modinit(void)
120{
121 return platform_driver_register(&simtec_audio_tlv320aic23_platdrv);
122}
123
124static void __exit simtec_tlv320aic23_modexit(void)
125{
126 platform_driver_unregister(&simtec_audio_tlv320aic23_platdrv);
127}
128
129module_init(simtec_tlv320aic23_modinit);
130module_exit(simtec_tlv320aic23_modexit);
131
132MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
133MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
134MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c
new file mode 100644
index 000000000000..cdc8ecbcb8ef
--- /dev/null
+++ b/sound/soc/samsung/s3c24xx_uda134x.c
@@ -0,0 +1,367 @@
1/*
2 * Modifications by Christian Pellegrin <chripell@evolware.org>
3 *
4 * s3c24xx_uda134x.c -- S3C24XX_UDA134X ALSA SoC Audio board driver
5 *
6 * Copyright 2007 Dension Audio Systems Ltd.
7 * Author: Zoltan Devai
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/module.h>
15#include <linux/clk.h>
16#include <linux/mutex.h>
17#include <linux/gpio.h>
18#include <sound/pcm.h>
19#include <sound/pcm_params.h>
20#include <sound/soc.h>
21#include <sound/s3c24xx_uda134x.h>
22#include <sound/uda134x.h>
23
24#include <plat/regs-iis.h>
25
26#include "dma.h"
27#include "s3c24xx-i2s.h"
28#include "../codecs/uda134x.h"
29
30
31/* #define ENFORCE_RATES 1 */
32/*
33 Unfortunately the S3C24XX in master mode has a limited capacity of
34 generating the clock for the codec. If you define this only rates
35 that are really available will be enforced. But be careful, most
36 user level application just want the usual sampling frequencies (8,
37 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
38 operation for embedded systems. So if you aren't very lucky or your
39 hardware engineer wasn't very forward-looking it's better to leave
40 this undefined. If you do so an approximate value for the requested
41 sampling rate in the range -/+ 5% will be chosen. If this in not
42 possible an error will be returned.
43*/
44
45static struct clk *xtal;
46static struct clk *pclk;
47/* this is need because we don't have a place where to keep the
48 * pointers to the clocks in each substream. We get the clocks only
49 * when we are actually using them so we don't block stuff like
50 * frequency change or oscillator power-off */
51static int clk_users;
52static DEFINE_MUTEX(clk_lock);
53
54static unsigned int rates[33 * 2];
55#ifdef ENFORCE_RATES
56static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
57 .count = ARRAY_SIZE(rates),
58 .list = rates,
59 .mask = 0,
60};
61#endif
62
63static struct platform_device *s3c24xx_uda134x_snd_device;
64
65static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
66{
67 int ret = 0;
68#ifdef ENFORCE_RATES
69 struct snd_pcm_runtime *runtime = substream->runtime;
70#endif
71
72 mutex_lock(&clk_lock);
73 pr_debug("%s %d\n", __func__, clk_users);
74 if (clk_users == 0) {
75 xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
76 if (!xtal) {
77 printk(KERN_ERR "%s cannot get xtal\n", __func__);
78 ret = -EBUSY;
79 } else {
80 pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
81 "pclk");
82 if (!pclk) {
83 printk(KERN_ERR "%s cannot get pclk\n",
84 __func__);
85 clk_put(xtal);
86 ret = -EBUSY;
87 }
88 }
89 if (!ret) {
90 int i, j;
91
92 for (i = 0; i < 2; i++) {
93 int fs = i ? 256 : 384;
94
95 rates[i*33] = clk_get_rate(xtal) / fs;
96 for (j = 1; j < 33; j++)
97 rates[i*33 + j] = clk_get_rate(pclk) /
98 (j * fs);
99 }
100 }
101 }
102 clk_users += 1;
103 mutex_unlock(&clk_lock);
104 if (!ret) {
105#ifdef ENFORCE_RATES
106 ret = snd_pcm_hw_constraint_list(runtime, 0,
107 SNDRV_PCM_HW_PARAM_RATE,
108 &hw_constraints_rates);
109 if (ret < 0)
110 printk(KERN_ERR "%s cannot set constraints\n",
111 __func__);
112#endif
113 }
114 return ret;
115}
116
117static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
118{
119 mutex_lock(&clk_lock);
120 pr_debug("%s %d\n", __func__, clk_users);
121 clk_users -= 1;
122 if (clk_users == 0) {
123 clk_put(xtal);
124 xtal = NULL;
125 clk_put(pclk);
126 pclk = NULL;
127 }
128 mutex_unlock(&clk_lock);
129}
130
131static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
132 struct snd_pcm_hw_params *params)
133{
134 struct snd_soc_pcm_runtime *rtd = substream->private_data;
135 struct snd_soc_dai *codec_dai = rtd->codec_dai;
136 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
137 unsigned int clk = 0;
138 int ret = 0;
139 int clk_source, fs_mode;
140 unsigned long rate = params_rate(params);
141 long err, cerr;
142 unsigned int div;
143 int i, bi;
144
145 err = 999999;
146 bi = 0;
147 for (i = 0; i < 2*33; i++) {
148 cerr = rates[i] - rate;
149 if (cerr < 0)
150 cerr = -cerr;
151 if (cerr < err) {
152 err = cerr;
153 bi = i;
154 }
155 }
156 if (bi / 33 == 1)
157 fs_mode = S3C2410_IISMOD_256FS;
158 else
159 fs_mode = S3C2410_IISMOD_384FS;
160 if (bi % 33 == 0) {
161 clk_source = S3C24XX_CLKSRC_MPLL;
162 div = 1;
163 } else {
164 clk_source = S3C24XX_CLKSRC_PCLK;
165 div = bi % 33;
166 }
167 pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
168
169 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
170 pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
171 fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
172 clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
173 div, clk, err);
174
175 if ((err * 100 / rate) > 5) {
176 printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
177 "too different from desired (%ld%%)\n",
178 err * 100 / rate);
179 return -EINVAL;
180 }
181
182 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
183 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
184 if (ret < 0)
185 return ret;
186
187 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
188 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
189 if (ret < 0)
190 return ret;
191
192 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
193 SND_SOC_CLOCK_IN);
194 if (ret < 0)
195 return ret;
196
197 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
198 if (ret < 0)
199 return ret;
200
201 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
202 S3C2410_IISMOD_32FS);
203 if (ret < 0)
204 return ret;
205
206 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
207 S3C24XX_PRESCALE(div, div));
208 if (ret < 0)
209 return ret;
210
211 /* set the codec system clock for DAC and ADC */
212 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
213 SND_SOC_CLOCK_OUT);
214 if (ret < 0)
215 return ret;
216
217 return 0;
218}
219
220static struct snd_soc_ops s3c24xx_uda134x_ops = {
221 .startup = s3c24xx_uda134x_startup,
222 .shutdown = s3c24xx_uda134x_shutdown,
223 .hw_params = s3c24xx_uda134x_hw_params,
224};
225
226static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
227 .name = "UDA134X",
228 .stream_name = "UDA134X",
229 .codec_name = "uda134x-hifi",
230 .codec_dai_name = "uda134x-hifi",
231 .cpu_dai_name = "s3c24xx-i2s",
232 .ops = &s3c24xx_uda134x_ops,
233 .platform_name = "samsung-audio",
234};
235
236static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
237 .name = "S3C24XX_UDA134X",
238 .dai_link = &s3c24xx_uda134x_dai_link,
239 .num_links = 1,
240};
241
242static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
243
244static void setdat(int v)
245{
246 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
247}
248
249static void setclk(int v)
250{
251 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
252}
253
254static void setmode(int v)
255{
256 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
257}
258
259/* FIXME - This must be codec platform data but in which board file ?? */
260static struct uda134x_platform_data s3c24xx_uda134x = {
261 .l3 = {
262 .setdat = setdat,
263 .setclk = setclk,
264 .setmode = setmode,
265 .data_hold = 1,
266 .data_setup = 1,
267 .clock_high = 1,
268 .mode_hold = 1,
269 .mode = 1,
270 .mode_setup = 1,
271 },
272};
273
274static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
275{
276 if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
277 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
278 "l3 %s pin already in use", fun);
279 return -EBUSY;
280 }
281 gpio_direction_output(pin, 0);
282 return 0;
283}
284
285static int s3c24xx_uda134x_probe(struct platform_device *pdev)
286{
287 int ret;
288
289 printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
290
291 s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
292 if (s3c24xx_uda134x_l3_pins == NULL) {
293 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
294 "unable to find platform data\n");
295 return -ENODEV;
296 }
297 s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
298 s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
299
300 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
301 "data") < 0)
302 return -EBUSY;
303 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
304 "clk") < 0) {
305 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
306 return -EBUSY;
307 }
308 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
309 "mode") < 0) {
310 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
311 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
312 return -EBUSY;
313 }
314
315 s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
316 if (!s3c24xx_uda134x_snd_device) {
317 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
318 "Unable to register\n");
319 return -ENOMEM;
320 }
321
322 platform_set_drvdata(s3c24xx_uda134x_snd_device,
323 &snd_soc_s3c24xx_uda134x);
324 ret = platform_device_add(s3c24xx_uda134x_snd_device);
325 if (ret) {
326 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
327 platform_device_put(s3c24xx_uda134x_snd_device);
328 }
329
330 return ret;
331}
332
333static int s3c24xx_uda134x_remove(struct platform_device *pdev)
334{
335 platform_device_unregister(s3c24xx_uda134x_snd_device);
336 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
337 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
338 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
339 return 0;
340}
341
342static struct platform_driver s3c24xx_uda134x_driver = {
343 .probe = s3c24xx_uda134x_probe,
344 .remove = s3c24xx_uda134x_remove,
345 .driver = {
346 .name = "s3c24xx_uda134x",
347 .owner = THIS_MODULE,
348 },
349};
350
351static int __init s3c24xx_uda134x_init(void)
352{
353 return platform_driver_register(&s3c24xx_uda134x_driver);
354}
355
356static void __exit s3c24xx_uda134x_exit(void)
357{
358 platform_driver_unregister(&s3c24xx_uda134x_driver);
359}
360
361
362module_init(s3c24xx_uda134x_init);
363module_exit(s3c24xx_uda134x_exit);
364
365MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
366MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
367MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c
new file mode 100644
index 000000000000..d6a340b8c3f7
--- /dev/null
+++ b/sound/soc/samsung/smartq_wm8987.c
@@ -0,0 +1,288 @@
1/* sound/soc/samsung/smartq_wm8987.c
2 *
3 * Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
4 *
5 * Based on smdk6410_wm8987.c
6 * Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com
7 * Graeme Gregory - graeme.gregory@wolfsonmicro.com
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 *
14 */
15
16#include <linux/module.h>
17#include <linux/platform_device.h>
18#include <linux/gpio.h>
19
20#include <sound/pcm.h>
21#include <sound/pcm_params.h>
22#include <sound/soc.h>
23#include <sound/jack.h>
24
25#include <asm/mach-types.h>
26
27#include "dma.h"
28#include "i2s.h"
29
30#include "../codecs/wm8750.h"
31
32/*
33 * WM8987 is register compatible with WM8750, so using that as base driver.
34 */
35
36static struct snd_soc_card snd_soc_smartq;
37
38static int smartq_hifi_hw_params(struct snd_pcm_substream *substream,
39 struct snd_pcm_hw_params *params)
40{
41 struct snd_soc_pcm_runtime *rtd = substream->private_data;
42 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
43 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
44 unsigned int clk = 0;
45 int ret;
46
47 switch (params_rate(params)) {
48 case 8000:
49 case 16000:
50 case 32000:
51 case 48000:
52 case 96000:
53 clk = 12288000;
54 break;
55 case 11025:
56 case 22050:
57 case 44100:
58 case 88200:
59 clk = 11289600;
60 break;
61 }
62
63 /* set codec DAI configuration */
64 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
65 SND_SOC_DAIFMT_NB_NF |
66 SND_SOC_DAIFMT_CBS_CFS);
67 if (ret < 0)
68 return ret;
69
70 /* set cpu DAI configuration */
71 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
72 SND_SOC_DAIFMT_NB_NF |
73 SND_SOC_DAIFMT_CBS_CFS);
74 if (ret < 0)
75 return ret;
76
77 /* Use PCLK for I2S signal generation */
78 ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
79 0, SND_SOC_CLOCK_IN);
80 if (ret < 0)
81 return ret;
82
83 /* Gate the RCLK output on PAD */
84 ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
85 0, SND_SOC_CLOCK_IN);
86 if (ret < 0)
87 return ret;
88
89 /* set the codec system clock for DAC and ADC */
90 ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
91 SND_SOC_CLOCK_IN);
92 if (ret < 0)
93 return ret;
94
95 return 0;
96}
97
98/*
99 * SmartQ WM8987 HiFi DAI operations.
100 */
101static struct snd_soc_ops smartq_hifi_ops = {
102 .hw_params = smartq_hifi_hw_params,
103};
104
105static struct snd_soc_jack smartq_jack;
106
107static struct snd_soc_jack_pin smartq_jack_pins[] = {
108 /* Disable speaker when headphone is plugged in */
109 {
110 .pin = "Internal Speaker",
111 .mask = SND_JACK_HEADPHONE,
112 },
113};
114
115static struct snd_soc_jack_gpio smartq_jack_gpios[] = {
116 {
117 .gpio = S3C64XX_GPL(12),
118 .name = "headphone detect",
119 .report = SND_JACK_HEADPHONE,
120 .debounce_time = 200,
121 },
122};
123
124static const struct snd_kcontrol_new wm8987_smartq_controls[] = {
125 SOC_DAPM_PIN_SWITCH("Internal Speaker"),
126 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
127 SOC_DAPM_PIN_SWITCH("Internal Mic"),
128};
129
130static int smartq_speaker_event(struct snd_soc_dapm_widget *w,
131 struct snd_kcontrol *k,
132 int event)
133{
134 gpio_set_value(S3C64XX_GPK(12), SND_SOC_DAPM_EVENT_OFF(event));
135
136 return 0;
137}
138
139static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = {
140 SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event),
141 SND_SOC_DAPM_HP("Headphone Jack", NULL),
142 SND_SOC_DAPM_MIC("Internal Mic", NULL),
143};
144
145static const struct snd_soc_dapm_route audio_map[] = {
146 {"Headphone Jack", NULL, "LOUT2"},
147 {"Headphone Jack", NULL, "ROUT2"},
148
149 {"Internal Speaker", NULL, "LOUT2"},
150 {"Internal Speaker", NULL, "ROUT2"},
151
152 {"Mic Bias", NULL, "Internal Mic"},
153 {"LINPUT2", NULL, "Mic Bias"},
154};
155
156static int smartq_wm8987_init(struct snd_soc_codec *codec)
157{
158 struct snd_soc_dapm_context *dapm = &codec->dapm;
159 int err = 0;
160
161 /* Add SmartQ specific widgets */
162 snd_soc_dapm_new_controls(dapm, wm8987_dapm_widgets,
163 ARRAY_SIZE(wm8987_dapm_widgets));
164
165 /* add SmartQ specific controls */
166 err = snd_soc_add_controls(codec, wm8987_smartq_controls,
167 ARRAY_SIZE(wm8987_smartq_controls));
168
169 if (err < 0)
170 return err;
171
172 /* setup SmartQ specific audio path */
173 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
174
175 /* set endpoints to not connected */
176 snd_soc_dapm_nc_pin(dapm, "LINPUT1");
177 snd_soc_dapm_nc_pin(dapm, "RINPUT1");
178 snd_soc_dapm_nc_pin(dapm, "OUT3");
179 snd_soc_dapm_nc_pin(dapm, "ROUT1");
180
181 /* set endpoints to default off mode */
182 snd_soc_dapm_enable_pin(dapm, "Internal Speaker");
183 snd_soc_dapm_enable_pin(dapm, "Internal Mic");
184 snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
185
186 err = snd_soc_dapm_sync(dapm);
187 if (err)
188 return err;
189
190 /* Headphone jack detection */
191 err = snd_soc_jack_new(&snd_soc_smartq, "Headphone Jack",
192 SND_JACK_HEADPHONE, &smartq_jack);
193 if (err)
194 return err;
195
196 err = snd_soc_jack_add_pins(&smartq_jack, ARRAY_SIZE(smartq_jack_pins),
197 smartq_jack_pins);
198 if (err)
199 return err;
200
201 err = snd_soc_jack_add_gpios(&smartq_jack,
202 ARRAY_SIZE(smartq_jack_gpios),
203 smartq_jack_gpios);
204
205 return err;
206}
207
208static struct snd_soc_dai_link smartq_dai[] = {
209 {
210 .name = "wm8987",
211 .stream_name = "SmartQ Hi-Fi",
212 .cpu_dai_name = "samsung-i2s.0",
213 .codec_dai_name = "wm8750-hifi",
214 .platform_name = "samsung-audio",
215 .codec_name = "wm8750-codec.0-0x1a",
216 .init = smartq_wm8987_init,
217 .ops = &smartq_hifi_ops,
218 },
219};
220
221static struct snd_soc_card snd_soc_smartq = {
222 .name = "SmartQ",
223 .dai_link = smartq_dai,
224 .num_links = ARRAY_SIZE(smartq_dai),
225};
226
227static struct platform_device *smartq_snd_device;
228
229static int __init smartq_init(void)
230{
231 int ret;
232
233 if (!machine_is_smartq7() && !machine_is_smartq5()) {
234 pr_info("Only SmartQ is supported by this ASoC driver\n");
235 return -ENODEV;
236 }
237
238 smartq_snd_device = platform_device_alloc("soc-audio", -1);
239 if (!smartq_snd_device)
240 return -ENOMEM;
241
242 platform_set_drvdata(smartq_snd_device, &snd_soc_smartq);
243
244 ret = platform_device_add(smartq_snd_device);
245 if (ret) {
246 platform_device_put(smartq_snd_device);
247 return ret;
248 }
249
250 /* Initialise GPIOs used by amplifiers */
251 ret = gpio_request(S3C64XX_GPK(12), "amplifiers shutdown");
252 if (ret) {
253 dev_err(&smartq_snd_device->dev, "Failed to register GPK12\n");
254 goto err_unregister_device;
255 }
256
257 /* Disable amplifiers */
258 ret = gpio_direction_output(S3C64XX_GPK(12), 1);
259 if (ret) {
260 dev_err(&smartq_snd_device->dev, "Failed to configure GPK12\n");
261 goto err_free_gpio_amp_shut;
262 }
263
264 return 0;
265
266err_free_gpio_amp_shut:
267 gpio_free(S3C64XX_GPK(12));
268err_unregister_device:
269 platform_device_unregister(smartq_snd_device);
270
271 return ret;
272}
273
274static void __exit smartq_exit(void)
275{
276 snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios),
277 smartq_jack_gpios);
278
279 platform_device_unregister(smartq_snd_device);
280}
281
282module_init(smartq_init);
283module_exit(smartq_exit);
284
285/* Module information */
286MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>");
287MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987");
288MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smdk2443_wm9710.c b/sound/soc/samsung/smdk2443_wm9710.c
new file mode 100644
index 000000000000..3be7e7e92d6e
--- /dev/null
+++ b/sound/soc/samsung/smdk2443_wm9710.c
@@ -0,0 +1,73 @@
1/*
2 * smdk2443_wm9710.c -- SoC audio for smdk2443
3 *
4 * Copyright 2007 Wolfson Microelectronics PLC.
5 * Author: Graeme Gregory
6 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14
15#include <linux/module.h>
16#include <linux/device.h>
17#include <sound/core.h>
18#include <sound/pcm.h>
19#include <sound/soc.h>
20
21#include "dma.h"
22#include "ac97.h"
23
24static struct snd_soc_card smdk2443;
25
26static struct snd_soc_dai_link smdk2443_dai[] = {
27{
28 .name = "AC97",
29 .stream_name = "AC97 HiFi",
30 .cpu_dai_name = "samsung-ac97",
31 .codec_dai_name = "ac97-hifi",
32 .codec_name = "ac97-codec",
33 .platform_name = "samsung-audio",
34},
35};
36
37static struct snd_soc_card smdk2443 = {
38 .name = "SMDK2443",
39 .dai_link = smdk2443_dai,
40 .num_links = ARRAY_SIZE(smdk2443_dai),
41};
42
43static struct platform_device *smdk2443_snd_ac97_device;
44
45static int __init smdk2443_init(void)
46{
47 int ret;
48
49 smdk2443_snd_ac97_device = platform_device_alloc("soc-audio", -1);
50 if (!smdk2443_snd_ac97_device)
51 return -ENOMEM;
52
53 platform_set_drvdata(smdk2443_snd_ac97_device, &smdk2443);
54 ret = platform_device_add(smdk2443_snd_ac97_device);
55
56 if (ret)
57 platform_device_put(smdk2443_snd_ac97_device);
58
59 return ret;
60}
61
62static void __exit smdk2443_exit(void)
63{
64 platform_device_unregister(smdk2443_snd_ac97_device);
65}
66
67module_init(smdk2443_init);
68module_exit(smdk2443_exit);
69
70/* Module information */
71MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
72MODULE_DESCRIPTION("ALSA SoC WM9710 SMDK2443");
73MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c
new file mode 100644
index 000000000000..cd29da089c8f
--- /dev/null
+++ b/sound/soc/samsung/smdk_spdif.c
@@ -0,0 +1,223 @@
1/*
2 * smdk_spdif.c -- S/PDIF audio for SMDK
3 *
4 * Copyright 2010 Samsung Electronics Co. Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 */
12
13#include <linux/module.h>
14#include <linux/device.h>
15#include <linux/clk.h>
16
17#include <plat/devs.h>
18
19#include <sound/soc.h>
20
21#include "dma.h"
22#include "spdif.h"
23
24/* Audio clock settings are belonged to board specific part. Every
25 * board can set audio source clock setting which is matched with H/W
26 * like this function-'set_audio_clock_heirachy'.
27 */
28static int set_audio_clock_heirachy(struct platform_device *pdev)
29{
30 struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif;
31 int ret;
32
33 fout_epll = clk_get(NULL, "fout_epll");
34 if (IS_ERR(fout_epll)) {
35 printk(KERN_WARNING "%s: Cannot find fout_epll.\n",
36 __func__);
37 return -EINVAL;
38 }
39
40 mout_epll = clk_get(NULL, "mout_epll");
41 if (IS_ERR(mout_epll)) {
42 printk(KERN_WARNING "%s: Cannot find mout_epll.\n",
43 __func__);
44 ret = -EINVAL;
45 goto out1;
46 }
47
48 sclk_audio0 = clk_get(&pdev->dev, "sclk_audio");
49 if (IS_ERR(sclk_audio0)) {
50 printk(KERN_WARNING "%s: Cannot find sclk_audio.\n",
51 __func__);
52 ret = -EINVAL;
53 goto out2;
54 }
55
56 sclk_spdif = clk_get(NULL, "sclk_spdif");
57 if (IS_ERR(sclk_spdif)) {
58 printk(KERN_WARNING "%s: Cannot find sclk_spdif.\n",
59 __func__);
60 ret = -EINVAL;
61 goto out3;
62 }
63
64 /* Set audio clock heirachy for S/PDIF */
65 clk_set_parent(mout_epll, fout_epll);
66 clk_set_parent(sclk_audio0, mout_epll);
67 clk_set_parent(sclk_spdif, sclk_audio0);
68
69 clk_put(sclk_spdif);
70out3:
71 clk_put(sclk_audio0);
72out2:
73 clk_put(mout_epll);
74out1:
75 clk_put(fout_epll);
76
77 return ret;
78}
79
80/* We should haved to set clock directly on this part because of clock
81 * scheme of Samsudng SoCs did not support to set rates from abstrct
82 * clock of it's heirachy.
83 */
84static int set_audio_clock_rate(unsigned long epll_rate,
85 unsigned long audio_rate)
86{
87 struct clk *fout_epll, *sclk_spdif;
88
89 fout_epll = clk_get(NULL, "fout_epll");
90 if (IS_ERR(fout_epll)) {
91 printk(KERN_ERR "%s: failed to get fout_epll\n", __func__);
92 return -ENOENT;
93 }
94
95 clk_set_rate(fout_epll, epll_rate);
96 clk_put(fout_epll);
97
98 sclk_spdif = clk_get(NULL, "sclk_spdif");
99 if (IS_ERR(sclk_spdif)) {
100 printk(KERN_ERR "%s: failed to get sclk_spdif\n", __func__);
101 return -ENOENT;
102 }
103
104 clk_set_rate(sclk_spdif, audio_rate);
105 clk_put(sclk_spdif);
106
107 return 0;
108}
109
110static int smdk_hw_params(struct snd_pcm_substream *substream,
111 struct snd_pcm_hw_params *params)
112{
113 struct snd_soc_pcm_runtime *rtd = substream->private_data;
114 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
115 unsigned long pll_out, rclk_rate;
116 int ret, ratio;
117
118 switch (params_rate(params)) {
119 case 44100:
120 pll_out = 45158400;
121 break;
122 case 32000:
123 case 48000:
124 case 96000:
125 pll_out = 49152000;
126 break;
127 default:
128 return -EINVAL;
129 }
130
131 /* Setting ratio to 512fs helps to use S/PDIF with HDMI without
132 * modify S/PDIF ASoC machine driver.
133 */
134 ratio = 512;
135 rclk_rate = params_rate(params) * ratio;
136
137 /* Set audio source clock rates */
138 ret = set_audio_clock_rate(pll_out, rclk_rate);
139 if (ret < 0)
140 return ret;
141
142 /* Set S/PDIF uses internal source clock */
143 ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK,
144 rclk_rate, SND_SOC_CLOCK_IN);
145 if (ret < 0)
146 return ret;
147
148 return ret;
149}
150
151static struct snd_soc_ops smdk_spdif_ops = {
152 .hw_params = smdk_hw_params,
153};
154
155static struct snd_soc_card smdk;
156
157static struct snd_soc_dai_link smdk_dai = {
158 .name = "S/PDIF",
159 .stream_name = "S/PDIF PCM Playback",
160 .platform_name = "samsung-audio",
161 .cpu_dai_name = "samsung-spdif",
162 .codec_dai_name = "dit-hifi",
163 .codec_name = "spdif-dit",
164 .ops = &smdk_spdif_ops,
165};
166
167static struct snd_soc_card smdk = {
168 .name = "SMDK-S/PDIF",
169 .dai_link = &smdk_dai,
170 .num_links = 1,
171};
172
173static struct platform_device *smdk_snd_spdif_dit_device;
174static struct platform_device *smdk_snd_spdif_device;
175
176static int __init smdk_init(void)
177{
178 int ret;
179
180 smdk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1);
181 if (!smdk_snd_spdif_dit_device)
182 return -ENOMEM;
183
184 ret = platform_device_add(smdk_snd_spdif_dit_device);
185 if (ret)
186 goto err2;
187
188 smdk_snd_spdif_device = platform_device_alloc("soc-audio", -1);
189 if (!smdk_snd_spdif_device) {
190 ret = -ENOMEM;
191 goto err2;
192 }
193
194 platform_set_drvdata(smdk_snd_spdif_device, &smdk);
195
196 ret = platform_device_add(smdk_snd_spdif_device);
197 if (ret)
198 goto err1;
199
200 /* Set audio clock heirachy manually */
201 ret = set_audio_clock_heirachy(smdk_snd_spdif_device);
202 if (ret)
203 goto err1;
204
205 return 0;
206err1:
207 platform_device_put(smdk_snd_spdif_device);
208err2:
209 platform_device_put(smdk_snd_spdif_dit_device);
210 return ret;
211}
212
213static void __exit smdk_exit(void)
214{
215 platform_device_unregister(smdk_snd_spdif_device);
216}
217
218module_init(smdk_init);
219module_exit(smdk_exit);
220
221MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
222MODULE_DESCRIPTION("ALSA SoC SMDK+S/PDIF");
223MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c
new file mode 100644
index 000000000000..0ae3d57d79e6
--- /dev/null
+++ b/sound/soc/samsung/smdk_wm8580.c
@@ -0,0 +1,285 @@
1/*
2 * smdk_wm8580.c
3 *
4 * Copyright (c) 2009 Samsung Electronics Co. Ltd
5 * Author: Jaswinder Singh <jassi.brar@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 */
12
13#include <linux/platform_device.h>
14#include <linux/clk.h>
15#include <sound/core.h>
16#include <sound/pcm.h>
17#include <sound/pcm_params.h>
18#include <sound/soc.h>
19
20#include <asm/mach-types.h>
21
22#include "../codecs/wm8580.h"
23#include "dma.h"
24#include "i2s.h"
25
26/*
27 * Default CFG switch settings to use this driver:
28 *
29 * SMDK6410: Set CFG1 1-3 Off, CFG2 1-4 On
30 */
31
32/* SMDK has a 12MHZ crystal attached to WM8580 */
33#define SMDK_WM8580_FREQ 12000000
34
35static int smdk_hw_params(struct snd_pcm_substream *substream,
36 struct snd_pcm_hw_params *params)
37{
38 struct snd_soc_pcm_runtime *rtd = substream->private_data;
39 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
40 struct snd_soc_dai *codec_dai = rtd->codec_dai;
41 unsigned int pll_out;
42 int bfs, rfs, ret;
43
44 switch (params_format(params)) {
45 case SNDRV_PCM_FORMAT_U8:
46 case SNDRV_PCM_FORMAT_S8:
47 bfs = 16;
48 break;
49 case SNDRV_PCM_FORMAT_U16_LE:
50 case SNDRV_PCM_FORMAT_S16_LE:
51 bfs = 32;
52 break;
53 default:
54 return -EINVAL;
55 }
56
57 /* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
58 * This criterion can't be met if we request PLL output
59 * as {8000x256, 64000x256, 11025x256}Hz.
60 * As a wayout, we rather change rfs to a minimum value that
61 * results in (params_rate(params) * rfs), and itself, acceptable
62 * to both - the CODEC and the CPU.
63 */
64 switch (params_rate(params)) {
65 case 16000:
66 case 22050:
67 case 32000:
68 case 44100:
69 case 48000:
70 case 88200:
71 case 96000:
72 rfs = 256;
73 break;
74 case 64000:
75 rfs = 384;
76 break;
77 case 8000:
78 case 11025:
79 rfs = 512;
80 break;
81 default:
82 return -EINVAL;
83 }
84 pll_out = params_rate(params) * rfs;
85
86 /* Set the Codec DAI configuration */
87 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
88 | SND_SOC_DAIFMT_NB_NF
89 | SND_SOC_DAIFMT_CBM_CFM);
90 if (ret < 0)
91 return ret;
92
93 /* Set the AP DAI configuration */
94 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
95 | SND_SOC_DAIFMT_NB_NF
96 | SND_SOC_DAIFMT_CBM_CFM);
97 if (ret < 0)
98 return ret;
99
100 /* Set WM8580 to drive MCLK from its PLLA */
101 ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
102 WM8580_CLKSRC_PLLA);
103 if (ret < 0)
104 return ret;
105
106 ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
107 SMDK_WM8580_FREQ, pll_out);
108 if (ret < 0)
109 return ret;
110
111 ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
112 pll_out, SND_SOC_CLOCK_IN);
113 if (ret < 0)
114 return ret;
115
116 return 0;
117}
118
119/*
120 * SMDK WM8580 DAI operations.
121 */
122static struct snd_soc_ops smdk_ops = {
123 .hw_params = smdk_hw_params,
124};
125
126/* SMDK Playback widgets */
127static const struct snd_soc_dapm_widget wm8580_dapm_widgets_pbk[] = {
128 SND_SOC_DAPM_HP("Front", NULL),
129 SND_SOC_DAPM_HP("Center+Sub", NULL),
130 SND_SOC_DAPM_HP("Rear", NULL),
131};
132
133/* SMDK Capture widgets */
134static const struct snd_soc_dapm_widget wm8580_dapm_widgets_cpt[] = {
135 SND_SOC_DAPM_MIC("MicIn", NULL),
136 SND_SOC_DAPM_LINE("LineIn", NULL),
137};
138
139/* SMDK-PAIFTX connections */
140static const struct snd_soc_dapm_route audio_map_tx[] = {
141 /* MicIn feeds AINL */
142 {"AINL", NULL, "MicIn"},
143
144 /* LineIn feeds AINL/R */
145 {"AINL", NULL, "LineIn"},
146 {"AINR", NULL, "LineIn"},
147};
148
149/* SMDK-PAIFRX connections */
150static const struct snd_soc_dapm_route audio_map_rx[] = {
151 /* Front Left/Right are fed VOUT1L/R */
152 {"Front", NULL, "VOUT1L"},
153 {"Front", NULL, "VOUT1R"},
154
155 /* Center/Sub are fed VOUT2L/R */
156 {"Center+Sub", NULL, "VOUT2L"},
157 {"Center+Sub", NULL, "VOUT2R"},
158
159 /* Rear Left/Right are fed VOUT3L/R */
160 {"Rear", NULL, "VOUT3L"},
161 {"Rear", NULL, "VOUT3R"},
162};
163
164static int smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd)
165{
166 struct snd_soc_codec *codec = rtd->codec;
167 struct snd_soc_dapm_context *dapm = &codec->dapm;
168
169 /* Add smdk specific Capture widgets */
170 snd_soc_dapm_new_controls(dapm, wm8580_dapm_widgets_cpt,
171 ARRAY_SIZE(wm8580_dapm_widgets_cpt));
172
173 /* Set up PAIFTX audio path */
174 snd_soc_dapm_add_routes(dapm, audio_map_tx, ARRAY_SIZE(audio_map_tx));
175
176 /* Enabling the microphone requires the fitting of a 0R
177 * resistor to connect the line from the microphone jack.
178 */
179 snd_soc_dapm_disable_pin(dapm, "MicIn");
180
181 /* signal a DAPM event */
182 snd_soc_dapm_sync(dapm);
183
184 return 0;
185}
186
187static int smdk_wm8580_init_paifrx(struct snd_soc_pcm_runtime *rtd)
188{
189 struct snd_soc_codec *codec = rtd->codec;
190 struct snd_soc_dapm_context *dapm = &codec->dapm;
191
192 /* Add smdk specific Playback widgets */
193 snd_soc_dapm_new_controls(dapm, wm8580_dapm_widgets_pbk,
194 ARRAY_SIZE(wm8580_dapm_widgets_pbk));
195
196 /* Set up PAIFRX audio path */
197 snd_soc_dapm_add_routes(dapm, audio_map_rx, ARRAY_SIZE(audio_map_rx));
198
199 /* signal a DAPM event */
200 snd_soc_dapm_sync(dapm);
201
202 return 0;
203}
204
205enum {
206 PRI_PLAYBACK = 0,
207 PRI_CAPTURE,
208 SEC_PLAYBACK,
209};
210
211static struct snd_soc_dai_link smdk_dai[] = {
212 [PRI_PLAYBACK] = { /* Primary Playback i/f */
213 .name = "WM8580 PAIF RX",
214 .stream_name = "Playback",
215 .cpu_dai_name = "samsung-i2s.2",
216 .codec_dai_name = "wm8580-hifi-playback",
217 .platform_name = "samsung-audio",
218 .codec_name = "wm8580-codec.0-001b",
219 .init = smdk_wm8580_init_paifrx,
220 .ops = &smdk_ops,
221 },
222 [PRI_CAPTURE] = { /* Primary Capture i/f */
223 .name = "WM8580 PAIF TX",
224 .stream_name = "Capture",
225 .cpu_dai_name = "samsung-i2s.2",
226 .codec_dai_name = "wm8580-hifi-capture",
227 .platform_name = "samsung-audio",
228 .codec_name = "wm8580-codec.0-001b",
229 .init = smdk_wm8580_init_paiftx,
230 .ops = &smdk_ops,
231 },
232 [SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */
233 .name = "Sec_FIFO TX",
234 .stream_name = "Playback",
235 .cpu_dai_name = "samsung-i2s.x",
236 .codec_dai_name = "wm8580-hifi-playback",
237 .platform_name = "samsung-audio",
238 .codec_name = "wm8580-codec.0-001b",
239 .init = smdk_wm8580_init_paifrx,
240 .ops = &smdk_ops,
241 },
242};
243
244static struct snd_soc_card smdk = {
245 .name = "SMDK-I2S",
246 .dai_link = smdk_dai,
247 .num_links = 2,
248};
249
250static struct platform_device *smdk_snd_device;
251
252static int __init smdk_audio_init(void)
253{
254 int ret;
255 char *str;
256
257 if (machine_is_smdkc100()) {
258 smdk.num_links = 3;
259 /* S5PC100 has I2S0 as v5 */
260 str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
261 str[strlen(str) - 1] = '0';
262 str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name;
263 str[strlen(str) - 1] = '0';
264 /* Secondary is at offset SAMSUNG_I2S_SECOFF from Primary */
265 str = (char *)smdk_dai[SEC_PLAYBACK].cpu_dai_name;
266 str[strlen(str) - 1] = '0' + SAMSUNG_I2S_SECOFF;
267 }
268
269 smdk_snd_device = platform_device_alloc("soc-audio", -1);
270 if (!smdk_snd_device)
271 return -ENOMEM;
272
273 platform_set_drvdata(smdk_snd_device, &smdk);
274 ret = platform_device_add(smdk_snd_device);
275
276 if (ret)
277 platform_device_put(smdk_snd_device);
278
279 return ret;
280}
281module_init(smdk_audio_init);
282
283MODULE_AUTHOR("Jaswinder Singh, jassi.brar@samsung.com");
284MODULE_DESCRIPTION("ALSA SoC SMDK WM8580");
285MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smdk_wm9713.c b/sound/soc/samsung/smdk_wm9713.c
new file mode 100644
index 000000000000..7ce243086e7d
--- /dev/null
+++ b/sound/soc/samsung/smdk_wm9713.c
@@ -0,0 +1,107 @@
1/*
2 * smdk_wm9713.c -- SoC audio for SMDK
3 *
4 * Copyright 2010 Samsung Electronics Co. Ltd.
5 * Author: Jaswinder Singh Brar <jassi.brar@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/device.h>
16#include <sound/soc.h>
17
18#include "dma.h"
19#include "ac97.h"
20
21static struct snd_soc_card smdk;
22
23/*
24 * Default CFG switch settings to use this driver:
25 *
26 * SMDK6410: Set CFG1 1-3 On, CFG2 1-4 Off
27 * SMDKC100: Set CFG6 1-3 On, CFG7 1 On
28 * SMDKC110: Set CFGB10 1-2 Off, CFGB12 1-3 On
29 * SMDKV210: Set CFGB10 1-2 Off, CFGB12 1-3 On
30 */
31
32/*
33 Playback (HeadPhone):-
34 $ amixer sset 'Headphone' unmute
35 $ amixer sset 'Right Headphone Out Mux' 'Headphone'
36 $ amixer sset 'Left Headphone Out Mux' 'Headphone'
37 $ amixer sset 'Right HP Mixer PCM' unmute
38 $ amixer sset 'Left HP Mixer PCM' unmute
39
40 Capture (LineIn):-
41 $ amixer sset 'Right Capture Source' 'Line'
42 $ amixer sset 'Left Capture Source' 'Line'
43*/
44
45static struct snd_soc_dai_link smdk_dai = {
46 .name = "AC97",
47 .stream_name = "AC97 PCM",
48 .platform_name = "samsung-audio",
49 .cpu_dai_name = "samsung-ac97",
50 .codec_dai_name = "wm9713-hifi",
51 .codec_name = "wm9713-codec",
52};
53
54static struct snd_soc_card smdk = {
55 .name = "SMDK WM9713",
56 .dai_link = &smdk_dai,
57 .num_links = 1,
58};
59
60static struct platform_device *smdk_snd_wm9713_device;
61static struct platform_device *smdk_snd_ac97_device;
62
63static int __init smdk_init(void)
64{
65 int ret;
66
67 smdk_snd_wm9713_device = platform_device_alloc("wm9713-codec", -1);
68 if (!smdk_snd_wm9713_device)
69 return -ENOMEM;
70
71 ret = platform_device_add(smdk_snd_wm9713_device);
72 if (ret)
73 goto err;
74
75 smdk_snd_ac97_device = platform_device_alloc("soc-audio", -1);
76 if (!smdk_snd_ac97_device) {
77 ret = -ENOMEM;
78 goto err;
79 }
80
81 platform_set_drvdata(smdk_snd_ac97_device, &smdk);
82
83 ret = platform_device_add(smdk_snd_ac97_device);
84 if (ret) {
85 platform_device_put(smdk_snd_ac97_device);
86 goto err;
87 }
88
89 return 0;
90err:
91 platform_device_put(smdk_snd_wm9713_device);
92 return ret;
93}
94
95static void __exit smdk_exit(void)
96{
97 platform_device_unregister(smdk_snd_ac97_device);
98 platform_device_unregister(smdk_snd_wm9713_device);
99}
100
101module_init(smdk_init);
102module_exit(smdk_exit);
103
104/* Module information */
105MODULE_AUTHOR("Jaswinder Singh Brar, jassi.brar@samsung.com");
106MODULE_DESCRIPTION("ALSA SoC SMDK+WM9713");
107MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
new file mode 100644
index 000000000000..f0816404ea3e
--- /dev/null
+++ b/sound/soc/samsung/spdif.c
@@ -0,0 +1,501 @@
1/* sound/soc/samsung/spdif.c
2 *
3 * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
4 *
5 * Copyright (c) 2010 Samsung Electronics Co. Ltd
6 * http://www.samsung.com/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/clk.h>
14#include <linux/io.h>
15
16#include <sound/pcm.h>
17#include <sound/pcm_params.h>
18#include <sound/soc.h>
19
20#include <plat/audio.h>
21#include <mach/dma.h>
22
23#include "dma.h"
24#include "spdif.h"
25
26/* Registers */
27#define CLKCON 0x00
28#define CON 0x04
29#define BSTAS 0x08
30#define CSTAS 0x0C
31#define DATA_OUTBUF 0x10
32#define DCNT 0x14
33#define BSTAS_S 0x18
34#define DCNT_S 0x1C
35
36#define CLKCTL_MASK 0x7
37#define CLKCTL_MCLK_EXT (0x1 << 2)
38#define CLKCTL_PWR_ON (0x1 << 0)
39
40#define CON_MASK 0x3ffffff
41#define CON_FIFO_TH_SHIFT 19
42#define CON_FIFO_TH_MASK (0x7 << 19)
43#define CON_USERDATA_23RDBIT (0x1 << 12)
44
45#define CON_SW_RESET (0x1 << 5)
46
47#define CON_MCLKDIV_MASK (0x3 << 3)
48#define CON_MCLKDIV_256FS (0x0 << 3)
49#define CON_MCLKDIV_384FS (0x1 << 3)
50#define CON_MCLKDIV_512FS (0x2 << 3)
51
52#define CON_PCM_MASK (0x3 << 1)
53#define CON_PCM_16BIT (0x0 << 1)
54#define CON_PCM_20BIT (0x1 << 1)
55#define CON_PCM_24BIT (0x2 << 1)
56
57#define CON_PCM_DATA (0x1 << 0)
58
59#define CSTAS_MASK 0x3fffffff
60#define CSTAS_SAMP_FREQ_MASK (0xF << 24)
61#define CSTAS_SAMP_FREQ_44 (0x0 << 24)
62#define CSTAS_SAMP_FREQ_48 (0x2 << 24)
63#define CSTAS_SAMP_FREQ_32 (0x3 << 24)
64#define CSTAS_SAMP_FREQ_96 (0xA << 24)
65
66#define CSTAS_CATEGORY_MASK (0xFF << 8)
67#define CSTAS_CATEGORY_CODE_CDP (0x01 << 8)
68
69#define CSTAS_NO_COPYRIGHT (0x1 << 2)
70
71/**
72 * struct samsung_spdif_info - Samsung S/PDIF Controller information
73 * @lock: Spin lock for S/PDIF.
74 * @dev: The parent device passed to use from the probe.
75 * @regs: The pointer to the device register block.
76 * @clk_rate: Current clock rate for calcurate ratio.
77 * @pclk: The peri-clock pointer for spdif master operation.
78 * @sclk: The source clock pointer for making sync signals.
79 * @save_clkcon: Backup clkcon reg. in suspend.
80 * @save_con: Backup con reg. in suspend.
81 * @save_cstas: Backup cstas reg. in suspend.
82 * @dma_playback: DMA information for playback channel.
83 */
84struct samsung_spdif_info {
85 spinlock_t lock;
86 struct device *dev;
87 void __iomem *regs;
88 unsigned long clk_rate;
89 struct clk *pclk;
90 struct clk *sclk;
91 u32 saved_clkcon;
92 u32 saved_con;
93 u32 saved_cstas;
94 struct s3c_dma_params *dma_playback;
95};
96
97static struct s3c2410_dma_client spdif_dma_client_out = {
98 .name = "S/PDIF Stereo out",
99};
100
101static struct s3c_dma_params spdif_stereo_out;
102static struct samsung_spdif_info spdif_info;
103
104static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
105{
106 return snd_soc_dai_get_drvdata(cpu_dai);
107}
108
109static void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on)
110{
111 void __iomem *regs = spdif->regs;
112 u32 clkcon;
113
114 dev_dbg(spdif->dev, "Entered %s\n", __func__);
115
116 clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
117 if (on)
118 writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON);
119 else
120 writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
121}
122
123static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai,
124 int clk_id, unsigned int freq, int dir)
125{
126 struct samsung_spdif_info *spdif = to_info(cpu_dai);
127 u32 clkcon;
128
129 dev_dbg(spdif->dev, "Entered %s\n", __func__);
130
131 clkcon = readl(spdif->regs + CLKCON);
132
133 if (clk_id == SND_SOC_SPDIF_INT_MCLK)
134 clkcon &= ~CLKCTL_MCLK_EXT;
135 else
136 clkcon |= CLKCTL_MCLK_EXT;
137
138 writel(clkcon, spdif->regs + CLKCON);
139
140 spdif->clk_rate = freq;
141
142 return 0;
143}
144
145static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
146 struct snd_soc_dai *dai)
147{
148 struct snd_soc_pcm_runtime *rtd = substream->private_data;
149 struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
150 unsigned long flags;
151
152 dev_dbg(spdif->dev, "Entered %s\n", __func__);
153
154 switch (cmd) {
155 case SNDRV_PCM_TRIGGER_START:
156 case SNDRV_PCM_TRIGGER_RESUME:
157 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
158 spin_lock_irqsave(&spdif->lock, flags);
159 spdif_snd_txctrl(spdif, 1);
160 spin_unlock_irqrestore(&spdif->lock, flags);
161 break;
162 case SNDRV_PCM_TRIGGER_STOP:
163 case SNDRV_PCM_TRIGGER_SUSPEND:
164 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
165 spin_lock_irqsave(&spdif->lock, flags);
166 spdif_snd_txctrl(spdif, 0);
167 spin_unlock_irqrestore(&spdif->lock, flags);
168 break;
169 default:
170 return -EINVAL;
171 }
172
173 return 0;
174}
175
176static int spdif_sysclk_ratios[] = {
177 512, 384, 256,
178};
179
180static int spdif_hw_params(struct snd_pcm_substream *substream,
181 struct snd_pcm_hw_params *params,
182 struct snd_soc_dai *socdai)
183{
184 struct snd_soc_pcm_runtime *rtd = substream->private_data;
185 struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
186 void __iomem *regs = spdif->regs;
187 struct s3c_dma_params *dma_data;
188 u32 con, clkcon, cstas;
189 unsigned long flags;
190 int i, ratio;
191
192 dev_dbg(spdif->dev, "Entered %s\n", __func__);
193
194 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
195 dma_data = spdif->dma_playback;
196 else {
197 dev_err(spdif->dev, "Capture is not supported\n");
198 return -EINVAL;
199 }
200
201 snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
202
203 spin_lock_irqsave(&spdif->lock, flags);
204
205 con = readl(regs + CON) & CON_MASK;
206 cstas = readl(regs + CSTAS) & CSTAS_MASK;
207 clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
208
209 con &= ~CON_FIFO_TH_MASK;
210 con |= (0x7 << CON_FIFO_TH_SHIFT);
211 con |= CON_USERDATA_23RDBIT;
212 con |= CON_PCM_DATA;
213
214 con &= ~CON_PCM_MASK;
215 switch (params_format(params)) {
216 case SNDRV_PCM_FORMAT_S16_LE:
217 con |= CON_PCM_16BIT;
218 break;
219 default:
220 dev_err(spdif->dev, "Unsupported data size.\n");
221 goto err;
222 }
223
224 ratio = spdif->clk_rate / params_rate(params);
225 for (i = 0; i < ARRAY_SIZE(spdif_sysclk_ratios); i++)
226 if (ratio == spdif_sysclk_ratios[i])
227 break;
228 if (i == ARRAY_SIZE(spdif_sysclk_ratios)) {
229 dev_err(spdif->dev, "Invalid clock ratio %ld/%d\n",
230 spdif->clk_rate, params_rate(params));
231 goto err;
232 }
233
234 con &= ~CON_MCLKDIV_MASK;
235 switch (ratio) {
236 case 256:
237 con |= CON_MCLKDIV_256FS;
238 break;
239 case 384:
240 con |= CON_MCLKDIV_384FS;
241 break;
242 case 512:
243 con |= CON_MCLKDIV_512FS;
244 break;
245 }
246
247 cstas &= ~CSTAS_SAMP_FREQ_MASK;
248 switch (params_rate(params)) {
249 case 44100:
250 cstas |= CSTAS_SAMP_FREQ_44;
251 break;
252 case 48000:
253 cstas |= CSTAS_SAMP_FREQ_48;
254 break;
255 case 32000:
256 cstas |= CSTAS_SAMP_FREQ_32;
257 break;
258 case 96000:
259 cstas |= CSTAS_SAMP_FREQ_96;
260 break;
261 default:
262 dev_err(spdif->dev, "Invalid sampling rate %d\n",
263 params_rate(params));
264 goto err;
265 }
266
267 cstas &= ~CSTAS_CATEGORY_MASK;
268 cstas |= CSTAS_CATEGORY_CODE_CDP;
269 cstas |= CSTAS_NO_COPYRIGHT;
270
271 writel(con, regs + CON);
272 writel(cstas, regs + CSTAS);
273 writel(clkcon, regs + CLKCON);
274
275 spin_unlock_irqrestore(&spdif->lock, flags);
276
277 return 0;
278err:
279 spin_unlock_irqrestore(&spdif->lock, flags);
280 return -EINVAL;
281}
282
283static void spdif_shutdown(struct snd_pcm_substream *substream,
284 struct snd_soc_dai *dai)
285{
286 struct snd_soc_pcm_runtime *rtd = substream->private_data;
287 struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
288 void __iomem *regs = spdif->regs;
289 u32 con, clkcon;
290
291 dev_dbg(spdif->dev, "Entered %s\n", __func__);
292
293 con = readl(regs + CON) & CON_MASK;
294 clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
295
296 writel(con | CON_SW_RESET, regs + CON);
297 cpu_relax();
298
299 writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
300}
301
302#ifdef CONFIG_PM
303static int spdif_suspend(struct snd_soc_dai *cpu_dai)
304{
305 struct samsung_spdif_info *spdif = to_info(cpu_dai);
306 u32 con = spdif->saved_con;
307
308 dev_dbg(spdif->dev, "Entered %s\n", __func__);
309
310 spdif->saved_clkcon = readl(spdif->regs + CLKCON) & CLKCTL_MASK;
311 spdif->saved_con = readl(spdif->regs + CON) & CON_MASK;
312 spdif->saved_cstas = readl(spdif->regs + CSTAS) & CSTAS_MASK;
313
314 writel(con | CON_SW_RESET, spdif->regs + CON);
315 cpu_relax();
316
317 return 0;
318}
319
320static int spdif_resume(struct snd_soc_dai *cpu_dai)
321{
322 struct samsung_spdif_info *spdif = to_info(cpu_dai);
323
324 dev_dbg(spdif->dev, "Entered %s\n", __func__);
325
326 writel(spdif->saved_clkcon, spdif->regs + CLKCON);
327 writel(spdif->saved_con, spdif->regs + CON);
328 writel(spdif->saved_cstas, spdif->regs + CSTAS);
329
330 return 0;
331}
332#else
333#define spdif_suspend NULL
334#define spdif_resume NULL
335#endif
336
337static struct snd_soc_dai_ops spdif_dai_ops = {
338 .set_sysclk = spdif_set_sysclk,
339 .trigger = spdif_trigger,
340 .hw_params = spdif_hw_params,
341 .shutdown = spdif_shutdown,
342};
343
344struct snd_soc_dai_driver samsung_spdif_dai = {
345 .name = "samsung-spdif",
346 .playback = {
347 .stream_name = "S/PDIF Playback",
348 .channels_min = 2,
349 .channels_max = 2,
350 .rates = (SNDRV_PCM_RATE_32000 |
351 SNDRV_PCM_RATE_44100 |
352 SNDRV_PCM_RATE_48000 |
353 SNDRV_PCM_RATE_96000),
354 .formats = SNDRV_PCM_FMTBIT_S16_LE, },
355 .ops = &spdif_dai_ops,
356 .suspend = spdif_suspend,
357 .resume = spdif_resume,
358};
359
360static __devinit int spdif_probe(struct platform_device *pdev)
361{
362 struct s3c_audio_pdata *spdif_pdata;
363 struct resource *mem_res, *dma_res;
364 struct samsung_spdif_info *spdif;
365 int ret;
366
367 spdif_pdata = pdev->dev.platform_data;
368
369 dev_dbg(&pdev->dev, "Entered %s\n", __func__);
370
371 dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
372 if (!dma_res) {
373 dev_err(&pdev->dev, "Unable to get dma resource.\n");
374 return -ENXIO;
375 }
376
377 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
378 if (!mem_res) {
379 dev_err(&pdev->dev, "Unable to get register resource.\n");
380 return -ENXIO;
381 }
382
383 if (spdif_pdata && spdif_pdata->cfg_gpio
384 && spdif_pdata->cfg_gpio(pdev)) {
385 dev_err(&pdev->dev, "Unable to configure GPIO pins\n");
386 return -EINVAL;
387 }
388
389 spdif = &spdif_info;
390 spdif->dev = &pdev->dev;
391
392 spin_lock_init(&spdif->lock);
393
394 spdif->pclk = clk_get(&pdev->dev, "spdif");
395 if (IS_ERR(spdif->pclk)) {
396 dev_err(&pdev->dev, "failed to get peri-clock\n");
397 ret = -ENOENT;
398 goto err0;
399 }
400 clk_enable(spdif->pclk);
401
402 spdif->sclk = clk_get(&pdev->dev, "sclk_spdif");
403 if (IS_ERR(spdif->sclk)) {
404 dev_err(&pdev->dev, "failed to get internal source clock\n");
405 ret = -ENOENT;
406 goto err1;
407 }
408 clk_enable(spdif->sclk);
409
410 /* Request S/PDIF Register's memory region */
411 if (!request_mem_region(mem_res->start,
412 resource_size(mem_res), "samsung-spdif")) {
413 dev_err(&pdev->dev, "Unable to request register region\n");
414 ret = -EBUSY;
415 goto err2;
416 }
417
418 spdif->regs = ioremap(mem_res->start, 0x100);
419 if (spdif->regs == NULL) {
420 dev_err(&pdev->dev, "Cannot ioremap registers\n");
421 ret = -ENXIO;
422 goto err3;
423 }
424
425 dev_set_drvdata(&pdev->dev, spdif);
426
427 ret = snd_soc_register_dai(&pdev->dev, &samsung_spdif_dai);
428 if (ret != 0) {
429 dev_err(&pdev->dev, "fail to register dai\n");
430 goto err4;
431 }
432
433 spdif_stereo_out.dma_size = 2;
434 spdif_stereo_out.client = &spdif_dma_client_out;
435 spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
436 spdif_stereo_out.channel = dma_res->start;
437
438 spdif->dma_playback = &spdif_stereo_out;
439
440 return 0;
441
442err4:
443 iounmap(spdif->regs);
444err3:
445 release_mem_region(mem_res->start, resource_size(mem_res));
446err2:
447 clk_disable(spdif->sclk);
448 clk_put(spdif->sclk);
449err1:
450 clk_disable(spdif->pclk);
451 clk_put(spdif->pclk);
452err0:
453 return ret;
454}
455
456static __devexit int spdif_remove(struct platform_device *pdev)
457{
458 struct samsung_spdif_info *spdif = &spdif_info;
459 struct resource *mem_res;
460
461 snd_soc_unregister_dai(&pdev->dev);
462
463 iounmap(spdif->regs);
464
465 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
466 if (mem_res)
467 release_mem_region(mem_res->start, resource_size(mem_res));
468
469 clk_disable(spdif->sclk);
470 clk_put(spdif->sclk);
471 clk_disable(spdif->pclk);
472 clk_put(spdif->pclk);
473
474 return 0;
475}
476
477static struct platform_driver samsung_spdif_driver = {
478 .probe = spdif_probe,
479 .remove = spdif_remove,
480 .driver = {
481 .name = "samsung-spdif",
482 .owner = THIS_MODULE,
483 },
484};
485
486static int __init spdif_init(void)
487{
488 return platform_driver_register(&samsung_spdif_driver);
489}
490module_init(spdif_init);
491
492static void __exit spdif_exit(void)
493{
494 platform_driver_unregister(&samsung_spdif_driver);
495}
496module_exit(spdif_exit);
497
498MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
499MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
500MODULE_LICENSE("GPL");
501MODULE_ALIAS("platform:samsung-spdif");
diff --git a/sound/soc/samsung/spdif.h b/sound/soc/samsung/spdif.h
new file mode 100644
index 000000000000..4f72cb446dbf
--- /dev/null
+++ b/sound/soc/samsung/spdif.h
@@ -0,0 +1,19 @@
1/* sound/soc/samsung/spdif.h
2 *
3 * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
4 *
5 * Copyright (c) 2010 Samsung Electronics Co. Ltd
6 * http://www.samsung.com/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#ifndef __SND_SOC_SAMSUNG_SPDIF_H
14#define __SND_SOC_SAMSUNG_SPDIF_H __FILE__
15
16#define SND_SOC_SPDIF_INT_MCLK 0
17#define SND_SOC_SPDIF_EXT_MCLK 1
18
19#endif /* __SND_SOC_SAMSUNG_SPDIF_H */