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