diff options
Diffstat (limited to 'sound/soc')
42 files changed, 3823 insertions, 968 deletions
diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c index 43dd8cee83c6..70657534e6b1 100644 --- a/sound/soc/atmel/playpaq_wm8510.c +++ b/sound/soc/atmel/playpaq_wm8510.c | |||
@@ -164,38 +164,38 @@ static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream, | |||
164 | */ | 164 | */ |
165 | switch (params_rate(params)) { | 165 | switch (params_rate(params)) { |
166 | case 48000: | 166 | case 48000: |
167 | pll_out = 12288000; | 167 | pll_out = 24576000; |
168 | mclk_div = WM8510_MCLKDIV_1; | 168 | mclk_div = WM8510_MCLKDIV_2; |
169 | bclk = WM8510_BCLKDIV_8; | 169 | bclk = WM8510_BCLKDIV_8; |
170 | break; | 170 | break; |
171 | 171 | ||
172 | case 44100: | 172 | case 44100: |
173 | pll_out = 11289600; | 173 | pll_out = 22579200; |
174 | mclk_div = WM8510_MCLKDIV_1; | 174 | mclk_div = WM8510_MCLKDIV_2; |
175 | bclk = WM8510_BCLKDIV_8; | 175 | bclk = WM8510_BCLKDIV_8; |
176 | break; | 176 | break; |
177 | 177 | ||
178 | case 22050: | 178 | case 22050: |
179 | pll_out = 11289600; | 179 | pll_out = 22579200; |
180 | mclk_div = WM8510_MCLKDIV_2; | 180 | mclk_div = WM8510_MCLKDIV_4; |
181 | bclk = WM8510_BCLKDIV_8; | 181 | bclk = WM8510_BCLKDIV_8; |
182 | break; | 182 | break; |
183 | 183 | ||
184 | case 16000: | 184 | case 16000: |
185 | pll_out = 12288000; | 185 | pll_out = 24576000; |
186 | mclk_div = WM8510_MCLKDIV_3; | 186 | mclk_div = WM8510_MCLKDIV_6; |
187 | bclk = WM8510_BCLKDIV_8; | 187 | bclk = WM8510_BCLKDIV_8; |
188 | break; | 188 | break; |
189 | 189 | ||
190 | case 11025: | 190 | case 11025: |
191 | pll_out = 11289600; | 191 | pll_out = 22579200; |
192 | mclk_div = WM8510_MCLKDIV_4; | 192 | mclk_div = WM8510_MCLKDIV_8; |
193 | bclk = WM8510_BCLKDIV_8; | 193 | bclk = WM8510_BCLKDIV_8; |
194 | break; | 194 | break; |
195 | 195 | ||
196 | case 8000: | 196 | case 8000: |
197 | pll_out = 12288000; | 197 | pll_out = 24576000; |
198 | mclk_div = WM8510_MCLKDIV_6; | 198 | mclk_div = WM8510_MCLKDIV_12; |
199 | bclk = WM8510_BCLKDIV_8; | 199 | bclk = WM8510_BCLKDIV_8; |
200 | break; | 200 | break; |
201 | 201 | ||
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index 5885702c78ff..8a935f2d1767 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c | |||
@@ -357,8 +357,8 @@ sport_config_err: | |||
357 | sport_err: | 357 | sport_err: |
358 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | 358 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET |
359 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | 359 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); |
360 | #endif | ||
361 | gpio_err: | 360 | gpio_err: |
361 | #endif | ||
362 | peripheral_free_list(sport_req[sport_num]); | 362 | peripheral_free_list(sport_req[sport_num]); |
363 | peripheral_err: | 363 | peripheral_err: |
364 | free_page((unsigned long)cmd_count); | 364 | free_page((unsigned long)cmd_count); |
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 628a591c728f..b6c7f7a01cb0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -14,6 +14,7 @@ config SND_SOC_ALL_CODECS | |||
14 | select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS | 14 | select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS |
15 | select SND_SOC_AD1980 if SND_SOC_AC97_BUS | 15 | select SND_SOC_AD1980 if SND_SOC_AC97_BUS |
16 | select SND_SOC_AD73311 if I2C | 16 | select SND_SOC_AD73311 if I2C |
17 | select SND_SOC_AK4104 if SPI_MASTER | ||
17 | select SND_SOC_AK4535 if I2C | 18 | select SND_SOC_AK4535 if I2C |
18 | select SND_SOC_CS4270 if I2C | 19 | select SND_SOC_CS4270 if I2C |
19 | select SND_SOC_PCM3008 | 20 | select SND_SOC_PCM3008 |
@@ -25,6 +26,7 @@ config SND_SOC_ALL_CODECS | |||
25 | select SND_SOC_UDA134X | 26 | select SND_SOC_UDA134X |
26 | select SND_SOC_UDA1380 if I2C | 27 | select SND_SOC_UDA1380 if I2C |
27 | select SND_SOC_WM8350 if MFD_WM8350 | 28 | select SND_SOC_WM8350 if MFD_WM8350 |
29 | select SND_SOC_WM8400 if MFD_WM8400 | ||
28 | select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI | 30 | select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI |
29 | select SND_SOC_WM8580 if I2C | 31 | select SND_SOC_WM8580 if I2C |
30 | select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI | 32 | select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI |
@@ -60,6 +62,9 @@ config SND_SOC_AD1980 | |||
60 | config SND_SOC_AD73311 | 62 | config SND_SOC_AD73311 |
61 | tristate | 63 | tristate |
62 | 64 | ||
65 | config SND_SOC_AK4104 | ||
66 | tristate | ||
67 | |||
63 | config SND_SOC_AK4535 | 68 | config SND_SOC_AK4535 |
64 | tristate | 69 | tristate |
65 | 70 | ||
@@ -106,6 +111,9 @@ config SND_SOC_UDA1380 | |||
106 | config SND_SOC_WM8350 | 111 | config SND_SOC_WM8350 |
107 | tristate | 112 | tristate |
108 | 113 | ||
114 | config SND_SOC_WM8400 | ||
115 | tristate | ||
116 | |||
109 | config SND_SOC_WM8510 | 117 | config SND_SOC_WM8510 |
110 | tristate | 118 | tristate |
111 | 119 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3664cdc300b2..030d2454725f 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -1,6 +1,7 @@ | |||
1 | snd-soc-ac97-objs := ac97.o | 1 | snd-soc-ac97-objs := ac97.o |
2 | snd-soc-ad1980-objs := ad1980.o | 2 | snd-soc-ad1980-objs := ad1980.o |
3 | snd-soc-ad73311-objs := ad73311.o | 3 | snd-soc-ad73311-objs := ad73311.o |
4 | snd-soc-ak4104-objs := ak4104.o | ||
4 | snd-soc-ak4535-objs := ak4535.o | 5 | snd-soc-ak4535-objs := ak4535.o |
5 | snd-soc-cs4270-objs := cs4270.o | 6 | snd-soc-cs4270-objs := cs4270.o |
6 | snd-soc-l3-objs := l3.o | 7 | snd-soc-l3-objs := l3.o |
@@ -13,6 +14,7 @@ snd-soc-twl4030-objs := twl4030.o | |||
13 | snd-soc-uda134x-objs := uda134x.o | 14 | snd-soc-uda134x-objs := uda134x.o |
14 | snd-soc-uda1380-objs := uda1380.o | 15 | snd-soc-uda1380-objs := uda1380.o |
15 | snd-soc-wm8350-objs := wm8350.o | 16 | snd-soc-wm8350-objs := wm8350.o |
17 | snd-soc-wm8400-objs := wm8400.o | ||
16 | snd-soc-wm8510-objs := wm8510.o | 18 | snd-soc-wm8510-objs := wm8510.o |
17 | snd-soc-wm8580-objs := wm8580.o | 19 | snd-soc-wm8580-objs := wm8580.o |
18 | snd-soc-wm8728-objs := wm8728.o | 20 | snd-soc-wm8728-objs := wm8728.o |
@@ -30,6 +32,7 @@ snd-soc-wm9713-objs := wm9713.o | |||
30 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o | 32 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o |
31 | obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o | 33 | obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o |
32 | obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o | 34 | obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o |
35 | obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o | ||
33 | obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o | 36 | obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o |
34 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o | 37 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o |
35 | obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o | 38 | obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o |
@@ -42,6 +45,7 @@ obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o | |||
42 | obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o | 45 | obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o |
43 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o | 46 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o |
44 | obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o | 47 | obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o |
48 | obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o | ||
45 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o | 49 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o |
46 | obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o | 50 | obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o |
47 | obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o | 51 | obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o |
diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h index 507ce0c30edf..569573d2d4d7 100644 --- a/sound/soc/codecs/ad73311.h +++ b/sound/soc/codecs/ad73311.h | |||
@@ -70,7 +70,7 @@ | |||
70 | #define REGD_IGS(x) (x & 0x7) | 70 | #define REGD_IGS(x) (x & 0x7) |
71 | #define REGD_RMOD (1 << 3) | 71 | #define REGD_RMOD (1 << 3) |
72 | #define REGD_OGS(x) ((x & 0x7) << 4) | 72 | #define REGD_OGS(x) ((x & 0x7) << 4) |
73 | #define REGD_MUTE (x << 7) | 73 | #define REGD_MUTE (1 << 7) |
74 | 74 | ||
75 | /* Control register E */ | 75 | /* Control register E */ |
76 | #define CTRL_REG_E (4 << 8) | 76 | #define CTRL_REG_E (4 << 8) |
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c new file mode 100644 index 000000000000..4d47bc4f7428 --- /dev/null +++ b/sound/soc/codecs/ak4104.c | |||
@@ -0,0 +1,365 @@ | |||
1 | /* | ||
2 | * AK4104 ALSA SoC (ASoC) driver | ||
3 | * | ||
4 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> | ||
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 <linux/module.h> | ||
13 | #include <sound/core.h> | ||
14 | #include <sound/soc.h> | ||
15 | #include <sound/initval.h> | ||
16 | #include <linux/spi/spi.h> | ||
17 | #include <sound/asoundef.h> | ||
18 | |||
19 | #include "ak4104.h" | ||
20 | |||
21 | /* AK4104 registers addresses */ | ||
22 | #define AK4104_REG_CONTROL1 0x00 | ||
23 | #define AK4104_REG_RESERVED 0x01 | ||
24 | #define AK4104_REG_CONTROL2 0x02 | ||
25 | #define AK4104_REG_TX 0x03 | ||
26 | #define AK4104_REG_CHN_STATUS(x) ((x) + 0x04) | ||
27 | #define AK4104_NUM_REGS 10 | ||
28 | |||
29 | #define AK4104_REG_MASK 0x1f | ||
30 | #define AK4104_READ 0xc0 | ||
31 | #define AK4104_WRITE 0xe0 | ||
32 | #define AK4104_RESERVED_VAL 0x5b | ||
33 | |||
34 | /* Bit masks for AK4104 registers */ | ||
35 | #define AK4104_CONTROL1_RSTN (1 << 0) | ||
36 | #define AK4104_CONTROL1_PW (1 << 1) | ||
37 | #define AK4104_CONTROL1_DIF0 (1 << 2) | ||
38 | #define AK4104_CONTROL1_DIF1 (1 << 3) | ||
39 | |||
40 | #define AK4104_CONTROL2_SEL0 (1 << 0) | ||
41 | #define AK4104_CONTROL2_SEL1 (1 << 1) | ||
42 | #define AK4104_CONTROL2_MODE (1 << 2) | ||
43 | |||
44 | #define AK4104_TX_TXE (1 << 0) | ||
45 | #define AK4104_TX_V (1 << 1) | ||
46 | |||
47 | #define DRV_NAME "ak4104" | ||
48 | |||
49 | struct ak4104_private { | ||
50 | struct snd_soc_codec codec; | ||
51 | u8 reg_cache[AK4104_NUM_REGS]; | ||
52 | }; | ||
53 | |||
54 | static int ak4104_fill_cache(struct snd_soc_codec *codec) | ||
55 | { | ||
56 | int i; | ||
57 | u8 *reg_cache = codec->reg_cache; | ||
58 | struct spi_device *spi = codec->control_data; | ||
59 | |||
60 | for (i = 0; i < codec->reg_cache_size; i++) { | ||
61 | int ret = spi_w8r8(spi, i | AK4104_READ); | ||
62 | if (ret < 0) { | ||
63 | dev_err(&spi->dev, "SPI write failure\n"); | ||
64 | return ret; | ||
65 | } | ||
66 | |||
67 | reg_cache[i] = ret; | ||
68 | } | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static unsigned int ak4104_read_reg_cache(struct snd_soc_codec *codec, | ||
74 | unsigned int reg) | ||
75 | { | ||
76 | u8 *reg_cache = codec->reg_cache; | ||
77 | |||
78 | if (reg >= codec->reg_cache_size) | ||
79 | return -EINVAL; | ||
80 | |||
81 | return reg_cache[reg]; | ||
82 | } | ||
83 | |||
84 | static int ak4104_spi_write(struct snd_soc_codec *codec, unsigned int reg, | ||
85 | unsigned int value) | ||
86 | { | ||
87 | u8 *cache = codec->reg_cache; | ||
88 | struct spi_device *spi = codec->control_data; | ||
89 | |||
90 | if (reg >= codec->reg_cache_size) | ||
91 | return -EINVAL; | ||
92 | |||
93 | reg &= AK4104_REG_MASK; | ||
94 | reg |= AK4104_WRITE; | ||
95 | |||
96 | /* only write to the hardware if value has changed */ | ||
97 | if (cache[reg] != value) { | ||
98 | u8 tmp[2] = { reg, value }; | ||
99 | if (spi_write(spi, tmp, sizeof(tmp))) { | ||
100 | dev_err(&spi->dev, "SPI write failed\n"); | ||
101 | return -EIO; | ||
102 | } | ||
103 | |||
104 | cache[reg] = value; | ||
105 | } | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
111 | unsigned int format) | ||
112 | { | ||
113 | struct snd_soc_codec *codec = codec_dai->codec; | ||
114 | int val = 0; | ||
115 | |||
116 | val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1); | ||
117 | if (val < 0) | ||
118 | return val; | ||
119 | |||
120 | val &= ~(AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1); | ||
121 | |||
122 | /* set DAI format */ | ||
123 | switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
124 | case SND_SOC_DAIFMT_RIGHT_J: | ||
125 | break; | ||
126 | case SND_SOC_DAIFMT_LEFT_J: | ||
127 | val |= AK4104_CONTROL1_DIF0; | ||
128 | break; | ||
129 | case SND_SOC_DAIFMT_I2S: | ||
130 | val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1; | ||
131 | break; | ||
132 | default: | ||
133 | dev_err(codec->dev, "invalid dai format\n"); | ||
134 | return -EINVAL; | ||
135 | } | ||
136 | |||
137 | /* This device can only be slave */ | ||
138 | if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) | ||
139 | return -EINVAL; | ||
140 | |||
141 | return ak4104_spi_write(codec, AK4104_REG_CONTROL1, val); | ||
142 | } | ||
143 | |||
144 | static int ak4104_hw_params(struct snd_pcm_substream *substream, | ||
145 | struct snd_pcm_hw_params *params, | ||
146 | struct snd_soc_dai *dai) | ||
147 | { | ||
148 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
149 | struct snd_soc_device *socdev = rtd->socdev; | ||
150 | struct snd_soc_codec *codec = socdev->card->codec; | ||
151 | int val = 0; | ||
152 | |||
153 | /* set the IEC958 bits: consumer mode, no copyright bit */ | ||
154 | val |= IEC958_AES0_CON_NOT_COPYRIGHT; | ||
155 | ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(0), val); | ||
156 | |||
157 | val = 0; | ||
158 | |||
159 | switch (params_rate(params)) { | ||
160 | case 44100: | ||
161 | val |= IEC958_AES3_CON_FS_44100; | ||
162 | break; | ||
163 | case 48000: | ||
164 | val |= IEC958_AES3_CON_FS_48000; | ||
165 | break; | ||
166 | case 32000: | ||
167 | val |= IEC958_AES3_CON_FS_32000; | ||
168 | break; | ||
169 | default: | ||
170 | dev_err(codec->dev, "unsupported sampling rate\n"); | ||
171 | return -EINVAL; | ||
172 | } | ||
173 | |||
174 | return ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(3), val); | ||
175 | } | ||
176 | |||
177 | static struct snd_soc_dai_ops ak4101_dai_ops = { | ||
178 | .hw_params = ak4104_hw_params, | ||
179 | .set_fmt = ak4104_set_dai_fmt, | ||
180 | }; | ||
181 | |||
182 | struct snd_soc_dai ak4104_dai = { | ||
183 | .name = DRV_NAME, | ||
184 | .playback = { | ||
185 | .stream_name = "Playback", | ||
186 | .channels_min = 2, | ||
187 | .channels_max = 2, | ||
188 | .rates = SNDRV_PCM_RATE_44100 | | ||
189 | SNDRV_PCM_RATE_48000 | | ||
190 | SNDRV_PCM_RATE_32000, | ||
191 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | ||
192 | SNDRV_PCM_FMTBIT_S24_3LE | | ||
193 | SNDRV_PCM_FMTBIT_S24_LE | ||
194 | }, | ||
195 | .ops = &ak4101_dai_ops, | ||
196 | }; | ||
197 | |||
198 | static struct snd_soc_codec *ak4104_codec; | ||
199 | |||
200 | static int ak4104_spi_probe(struct spi_device *spi) | ||
201 | { | ||
202 | struct snd_soc_codec *codec; | ||
203 | struct ak4104_private *ak4104; | ||
204 | int ret, val; | ||
205 | |||
206 | spi->bits_per_word = 8; | ||
207 | spi->mode = SPI_MODE_0; | ||
208 | ret = spi_setup(spi); | ||
209 | if (ret < 0) | ||
210 | return ret; | ||
211 | |||
212 | ak4104 = kzalloc(sizeof(struct ak4104_private), GFP_KERNEL); | ||
213 | if (!ak4104) { | ||
214 | dev_err(&spi->dev, "could not allocate codec\n"); | ||
215 | return -ENOMEM; | ||
216 | } | ||
217 | |||
218 | codec = &ak4104->codec; | ||
219 | mutex_init(&codec->mutex); | ||
220 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
221 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
222 | |||
223 | codec->dev = &spi->dev; | ||
224 | codec->name = DRV_NAME; | ||
225 | codec->owner = THIS_MODULE; | ||
226 | codec->dai = &ak4104_dai; | ||
227 | codec->num_dai = 1; | ||
228 | codec->private_data = ak4104; | ||
229 | codec->control_data = spi; | ||
230 | codec->reg_cache = ak4104->reg_cache; | ||
231 | codec->reg_cache_size = AK4104_NUM_REGS; | ||
232 | |||
233 | /* read all regs and fill the cache */ | ||
234 | ret = ak4104_fill_cache(codec); | ||
235 | if (ret < 0) { | ||
236 | dev_err(&spi->dev, "failed to fill register cache\n"); | ||
237 | return ret; | ||
238 | } | ||
239 | |||
240 | /* read the 'reserved' register - according to the datasheet, it | ||
241 | * should contain 0x5b. Not a good way to verify the presence of | ||
242 | * the device, but there is no hardware ID register. */ | ||
243 | if (ak4104_read_reg_cache(codec, AK4104_REG_RESERVED) != | ||
244 | AK4104_RESERVED_VAL) { | ||
245 | ret = -ENODEV; | ||
246 | goto error_free_codec; | ||
247 | } | ||
248 | |||
249 | /* set power-up and non-reset bits */ | ||
250 | val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1); | ||
251 | val |= AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN; | ||
252 | ret = ak4104_spi_write(codec, AK4104_REG_CONTROL1, val); | ||
253 | if (ret < 0) | ||
254 | goto error_free_codec; | ||
255 | |||
256 | /* enable transmitter */ | ||
257 | val = ak4104_read_reg_cache(codec, AK4104_REG_TX); | ||
258 | val |= AK4104_TX_TXE; | ||
259 | ret = ak4104_spi_write(codec, AK4104_REG_TX, val); | ||
260 | if (ret < 0) | ||
261 | goto error_free_codec; | ||
262 | |||
263 | ak4104_codec = codec; | ||
264 | ret = snd_soc_register_dai(&ak4104_dai); | ||
265 | if (ret < 0) { | ||
266 | dev_err(&spi->dev, "failed to register DAI\n"); | ||
267 | goto error_free_codec; | ||
268 | } | ||
269 | |||
270 | spi_set_drvdata(spi, ak4104); | ||
271 | dev_info(&spi->dev, "SPI device initialized\n"); | ||
272 | return 0; | ||
273 | |||
274 | error_free_codec: | ||
275 | kfree(ak4104); | ||
276 | ak4104_dai.dev = NULL; | ||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | static int __devexit ak4104_spi_remove(struct spi_device *spi) | ||
281 | { | ||
282 | int ret, val; | ||
283 | struct ak4104_private *ak4104 = spi_get_drvdata(spi); | ||
284 | |||
285 | val = ak4104_read_reg_cache(&ak4104->codec, AK4104_REG_CONTROL1); | ||
286 | if (val < 0) | ||
287 | return val; | ||
288 | |||
289 | /* clear power-up and non-reset bits */ | ||
290 | val &= ~(AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN); | ||
291 | ret = ak4104_spi_write(&ak4104->codec, AK4104_REG_CONTROL1, val); | ||
292 | if (ret < 0) | ||
293 | return ret; | ||
294 | |||
295 | ak4104_codec = NULL; | ||
296 | kfree(ak4104); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int ak4104_probe(struct platform_device *pdev) | ||
301 | { | ||
302 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
303 | struct snd_soc_codec *codec = ak4104_codec; | ||
304 | int ret; | ||
305 | |||
306 | /* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */ | ||
307 | socdev->card->codec = codec; | ||
308 | |||
309 | /* Register PCMs */ | ||
310 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
311 | if (ret < 0) { | ||
312 | dev_err(codec->dev, "failed to create pcms\n"); | ||
313 | return ret; | ||
314 | } | ||
315 | |||
316 | /* Register the socdev */ | ||
317 | ret = snd_soc_init_card(socdev); | ||
318 | if (ret < 0) { | ||
319 | dev_err(codec->dev, "failed to register card\n"); | ||
320 | snd_soc_free_pcms(socdev); | ||
321 | return ret; | ||
322 | } | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static int ak4104_remove(struct platform_device *pdev) | ||
328 | { | ||
329 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
330 | snd_soc_free_pcms(socdev); | ||
331 | return 0; | ||
332 | }; | ||
333 | |||
334 | struct snd_soc_codec_device soc_codec_device_ak4104 = { | ||
335 | .probe = ak4104_probe, | ||
336 | .remove = ak4104_remove | ||
337 | }; | ||
338 | EXPORT_SYMBOL_GPL(soc_codec_device_ak4104); | ||
339 | |||
340 | static struct spi_driver ak4104_spi_driver = { | ||
341 | .driver = { | ||
342 | .name = DRV_NAME, | ||
343 | .owner = THIS_MODULE, | ||
344 | }, | ||
345 | .probe = ak4104_spi_probe, | ||
346 | .remove = __devexit_p(ak4104_spi_remove), | ||
347 | }; | ||
348 | |||
349 | static int __init ak4104_init(void) | ||
350 | { | ||
351 | pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n"); | ||
352 | return spi_register_driver(&ak4104_spi_driver); | ||
353 | } | ||
354 | module_init(ak4104_init); | ||
355 | |||
356 | static void __exit ak4104_exit(void) | ||
357 | { | ||
358 | spi_unregister_driver(&ak4104_spi_driver); | ||
359 | } | ||
360 | module_exit(ak4104_exit); | ||
361 | |||
362 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | ||
363 | MODULE_DESCRIPTION("Asahi Kasei AK4104 ALSA SoC driver"); | ||
364 | MODULE_LICENSE("GPL"); | ||
365 | |||
diff --git a/sound/soc/codecs/ak4104.h b/sound/soc/codecs/ak4104.h new file mode 100644 index 000000000000..eb88fe7e4def --- /dev/null +++ b/sound/soc/codecs/ak4104.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef _AK4104_H | ||
2 | #define _AK4104_H | ||
3 | |||
4 | extern struct snd_soc_dai ak4104_dai; | ||
5 | extern struct snd_soc_codec_device soc_codec_device_ak4104; | ||
6 | |||
7 | #endif | ||
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 7ae3d6520e3f..2137670c9b78 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c | |||
@@ -12,14 +12,13 @@ | |||
12 | * | 12 | * |
13 | * Current features/limitations: | 13 | * Current features/limitations: |
14 | * | 14 | * |
15 | * 1) Software mode is supported. Stand-alone mode is not supported. | 15 | * - Software mode is supported. Stand-alone mode is not supported. |
16 | * 2) Only I2C is supported, not SPI | 16 | * - Only I2C is supported, not SPI |
17 | * 3) Only Master mode is supported, not Slave. | 17 | * - Support for master and slave mode |
18 | * 4) The machine driver's 'startup' function must call | 18 | * - The machine driver's 'startup' function must call |
19 | * cs4270_set_dai_sysclk() with the value of MCLK. | 19 | * cs4270_set_dai_sysclk() with the value of MCLK. |
20 | * 5) Only I2S and left-justified modes are supported | 20 | * - Only I2S and left-justified modes are supported |
21 | * 6) Power management is not supported | 21 | * - Power management is not supported |
22 | * 7) The only supported control is volume and hardware mute (if enabled) | ||
23 | */ | 22 | */ |
24 | 23 | ||
25 | #include <linux/module.h> | 24 | #include <linux/module.h> |
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 535d8ce2c328..86bb15cc82ce 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -584,12 +584,11 @@ static int headsetl_event(struct snd_soc_dapm_widget *w, | |||
584 | 584 | ||
585 | /* Save the current volume */ | 585 | /* Save the current volume */ |
586 | hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET); | 586 | hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET); |
587 | hs_pop = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_POPN_SET); | ||
587 | 588 | ||
588 | switch (event) { | 589 | switch (event) { |
589 | case SND_SOC_DAPM_POST_PMU: | 590 | case SND_SOC_DAPM_POST_PMU: |
590 | /* Do the anti-pop/bias ramp enable according to the TRM */ | 591 | /* Do the anti-pop/bias ramp enable according to the TRM */ |
591 | hs_pop = TWL4030_RAMP_DELAY_645MS; | ||
592 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | ||
593 | hs_pop |= TWL4030_VMID_EN; | 592 | hs_pop |= TWL4030_VMID_EN; |
594 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 593 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
595 | /* Is this needed? Can we just use whatever gain here? */ | 594 | /* Is this needed? Can we just use whatever gain here? */ |
@@ -603,8 +602,6 @@ static int headsetl_event(struct snd_soc_dapm_widget *w, | |||
603 | break; | 602 | break; |
604 | case SND_SOC_DAPM_POST_PMD: | 603 | case SND_SOC_DAPM_POST_PMD: |
605 | /* Do the anti-pop/bias ramp disable according to the TRM */ | 604 | /* Do the anti-pop/bias ramp disable according to the TRM */ |
606 | hs_pop = twl4030_read_reg_cache(w->codec, | ||
607 | TWL4030_REG_HS_POPN_SET); | ||
608 | hs_pop &= ~TWL4030_RAMP_EN; | 605 | hs_pop &= ~TWL4030_RAMP_EN; |
609 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 606 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
610 | /* Bypass the reg_cache to mute the headset */ | 607 | /* Bypass the reg_cache to mute the headset */ |
@@ -847,6 +844,17 @@ static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); | |||
847 | */ | 844 | */ |
848 | static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); | 845 | static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); |
849 | 846 | ||
847 | static const char *twl4030_rampdelay_texts[] = { | ||
848 | "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms", | ||
849 | "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms", | ||
850 | "3495/2581/1748 ms" | ||
851 | }; | ||
852 | |||
853 | static const struct soc_enum twl4030_rampdelay_enum = | ||
854 | SOC_ENUM_SINGLE(TWL4030_REG_HS_POPN_SET, 2, | ||
855 | ARRAY_SIZE(twl4030_rampdelay_texts), | ||
856 | twl4030_rampdelay_texts); | ||
857 | |||
850 | static const struct snd_kcontrol_new twl4030_snd_controls[] = { | 858 | static const struct snd_kcontrol_new twl4030_snd_controls[] = { |
851 | /* Common playback gain controls */ | 859 | /* Common playback gain controls */ |
852 | SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", | 860 | SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", |
@@ -901,6 +909,8 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { | |||
901 | 909 | ||
902 | SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN, | 910 | SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN, |
903 | 0, 3, 5, 0, input_gain_tlv), | 911 | 0, 3, 5, 0, input_gain_tlv), |
912 | |||
913 | SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum), | ||
904 | }; | 914 | }; |
905 | 915 | ||
906 | static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | 916 | static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { |
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index cafa7684c0e7..5b21594e0e58 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/ioctl.h> | 25 | #include <linux/ioctl.h> |
26 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
27 | #include <linux/i2c.h> | 27 | #include <linux/i2c.h> |
28 | #include <linux/workqueue.h> | ||
28 | #include <sound/core.h> | 29 | #include <sound/core.h> |
29 | #include <sound/control.h> | 30 | #include <sound/control.h> |
30 | #include <sound/initval.h> | 31 | #include <sound/initval.h> |
@@ -35,7 +36,8 @@ | |||
35 | 36 | ||
36 | #include "uda1380.h" | 37 | #include "uda1380.h" |
37 | 38 | ||
38 | #define UDA1380_VERSION "0.6" | 39 | static struct work_struct uda1380_work; |
40 | static struct snd_soc_codec *uda1380_codec; | ||
39 | 41 | ||
40 | /* | 42 | /* |
41 | * uda1380 register cache | 43 | * uda1380 register cache |
@@ -52,6 +54,8 @@ static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = { | |||
52 | 0x0000, 0x8000, 0x0002, 0x0000, | 54 | 0x0000, 0x8000, 0x0002, 0x0000, |
53 | }; | 55 | }; |
54 | 56 | ||
57 | static unsigned long uda1380_cache_dirty; | ||
58 | |||
55 | /* | 59 | /* |
56 | * read uda1380 register cache | 60 | * read uda1380 register cache |
57 | */ | 61 | */ |
@@ -73,8 +77,11 @@ static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec, | |||
73 | u16 reg, unsigned int value) | 77 | u16 reg, unsigned int value) |
74 | { | 78 | { |
75 | u16 *cache = codec->reg_cache; | 79 | u16 *cache = codec->reg_cache; |
80 | |||
76 | if (reg >= UDA1380_CACHEREGNUM) | 81 | if (reg >= UDA1380_CACHEREGNUM) |
77 | return; | 82 | return; |
83 | if ((reg >= 0x10) && (cache[reg] != value)) | ||
84 | set_bit(reg - 0x10, &uda1380_cache_dirty); | ||
78 | cache[reg] = value; | 85 | cache[reg] = value; |
79 | } | 86 | } |
80 | 87 | ||
@@ -113,6 +120,8 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg, | |||
113 | (data[0]<<8) | data[1]); | 120 | (data[0]<<8) | data[1]); |
114 | return -EIO; | 121 | return -EIO; |
115 | } | 122 | } |
123 | if (reg >= 0x10) | ||
124 | clear_bit(reg - 0x10, &uda1380_cache_dirty); | ||
116 | return 0; | 125 | return 0; |
117 | } else | 126 | } else |
118 | return -EIO; | 127 | return -EIO; |
@@ -120,6 +129,20 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg, | |||
120 | 129 | ||
121 | #define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0) | 130 | #define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0) |
122 | 131 | ||
132 | static void uda1380_flush_work(struct work_struct *work) | ||
133 | { | ||
134 | int bit, reg; | ||
135 | |||
136 | for_each_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) { | ||
137 | reg = 0x10 + bit; | ||
138 | pr_debug("uda1380: flush reg %x val %x:\n", reg, | ||
139 | uda1380_read_reg_cache(uda1380_codec, reg)); | ||
140 | uda1380_write(uda1380_codec, reg, | ||
141 | uda1380_read_reg_cache(uda1380_codec, reg)); | ||
142 | clear_bit(bit, &uda1380_cache_dirty); | ||
143 | } | ||
144 | } | ||
145 | |||
123 | /* declarations of ALSA reg_elem_REAL controls */ | 146 | /* declarations of ALSA reg_elem_REAL controls */ |
124 | static const char *uda1380_deemp[] = { | 147 | static const char *uda1380_deemp[] = { |
125 | "None", | 148 | "None", |
@@ -254,7 +277,6 @@ static const struct snd_kcontrol_new uda1380_snd_controls[] = { | |||
254 | SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0), /* DA_POL_INV */ | 277 | SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0), /* DA_POL_INV */ |
255 | SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum), /* SEL_NS */ | 278 | SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum), /* SEL_NS */ |
256 | SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum), /* MIX_POS, MIX */ | 279 | SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum), /* MIX_POS, MIX */ |
257 | SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0), /* SILENCE, force DAC output to silence */ | ||
258 | SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0), /* SDET_ON */ | 280 | SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0), /* SDET_ON */ |
259 | SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum), /* SD_VALUE */ | 281 | SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum), /* SD_VALUE */ |
260 | SOC_ENUM("Oversampling Input", uda1380_os_enum), /* OS */ | 282 | SOC_ENUM("Oversampling Input", uda1380_os_enum), /* OS */ |
@@ -377,8 +399,9 @@ static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai, | |||
377 | iface |= R01_SFORI_MSB | R01_SFORO_MSB; | 399 | iface |= R01_SFORI_MSB | R01_SFORO_MSB; |
378 | } | 400 | } |
379 | 401 | ||
380 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM) | 402 | /* DATAI is slave only, so in single-link mode, this has to be slave */ |
381 | iface |= R01_SIM; | 403 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) |
404 | return -EINVAL; | ||
382 | 405 | ||
383 | uda1380_write(codec, UDA1380_IFACE, iface); | 406 | uda1380_write(codec, UDA1380_IFACE, iface); |
384 | 407 | ||
@@ -406,6 +429,10 @@ static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai, | |||
406 | iface |= R01_SFORI_MSB; | 429 | iface |= R01_SFORI_MSB; |
407 | } | 430 | } |
408 | 431 | ||
432 | /* DATAI is slave only, so this has to be slave */ | ||
433 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) | ||
434 | return -EINVAL; | ||
435 | |||
409 | uda1380_write(codec, UDA1380_IFACE, iface); | 436 | uda1380_write(codec, UDA1380_IFACE, iface); |
410 | 437 | ||
411 | return 0; | 438 | return 0; |
@@ -440,41 +467,28 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai, | |||
440 | return 0; | 467 | return 0; |
441 | } | 468 | } |
442 | 469 | ||
443 | /* | 470 | static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd, |
444 | * Flush reg cache | 471 | struct snd_soc_dai *dai) |
445 | * We can only write the interpolator and decimator registers | ||
446 | * when the DAI is being clocked by the CPU DAI. It's up to the | ||
447 | * machine and cpu DAI driver to do this before we are called. | ||
448 | */ | ||
449 | static int uda1380_pcm_prepare(struct snd_pcm_substream *substream, | ||
450 | struct snd_soc_dai *dai) | ||
451 | { | 472 | { |
452 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 473 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
453 | struct snd_soc_device *socdev = rtd->socdev; | 474 | struct snd_soc_device *socdev = rtd->socdev; |
454 | struct snd_soc_codec *codec = socdev->card->codec; | 475 | struct snd_soc_codec *codec = socdev->card->codec; |
455 | int reg, reg_start, reg_end, clk; | 476 | int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER); |
456 | 477 | ||
457 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 478 | switch (cmd) { |
458 | reg_start = UDA1380_MVOL; | 479 | case SNDRV_PCM_TRIGGER_START: |
459 | reg_end = UDA1380_MIXER; | 480 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
460 | } else { | 481 | uda1380_write_reg_cache(codec, UDA1380_MIXER, |
461 | reg_start = UDA1380_DEC; | 482 | mixer & ~R14_SILENCE); |
462 | reg_end = UDA1380_AGC; | 483 | schedule_work(&uda1380_work); |
463 | } | 484 | break; |
464 | 485 | case SNDRV_PCM_TRIGGER_STOP: | |
465 | /* FIXME disable DAC_CLK */ | 486 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
466 | clk = uda1380_read_reg_cache(codec, UDA1380_CLK); | 487 | uda1380_write_reg_cache(codec, UDA1380_MIXER, |
467 | uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK); | 488 | mixer | R14_SILENCE); |
468 | 489 | schedule_work(&uda1380_work); | |
469 | for (reg = reg_start; reg <= reg_end; reg++) { | 490 | break; |
470 | pr_debug("uda1380: flush reg %x val %x:", reg, | ||
471 | uda1380_read_reg_cache(codec, reg)); | ||
472 | uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg)); | ||
473 | } | 491 | } |
474 | |||
475 | /* FIXME restore DAC_CLK */ | ||
476 | uda1380_write(codec, UDA1380_CLK, clk); | ||
477 | |||
478 | return 0; | 492 | return 0; |
479 | } | 493 | } |
480 | 494 | ||
@@ -540,24 +554,6 @@ static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream, | |||
540 | uda1380_write(codec, UDA1380_CLK, clk); | 554 | uda1380_write(codec, UDA1380_CLK, clk); |
541 | } | 555 | } |
542 | 556 | ||
543 | static int uda1380_mute(struct snd_soc_dai *codec_dai, int mute) | ||
544 | { | ||
545 | struct snd_soc_codec *codec = codec_dai->codec; | ||
546 | u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM; | ||
547 | |||
548 | /* FIXME: mute(codec,0) is called when the magician clock is already | ||
549 | * set to WSPLL, but for some unknown reason writing to interpolator | ||
550 | * registers works only when clocked by SYSCLK */ | ||
551 | u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK); | ||
552 | uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk); | ||
553 | if (mute) | ||
554 | uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM); | ||
555 | else | ||
556 | uda1380_write(codec, UDA1380_DEEMP, mute_reg); | ||
557 | uda1380_write(codec, UDA1380_CLK, clk); | ||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | static int uda1380_set_bias_level(struct snd_soc_codec *codec, | 557 | static int uda1380_set_bias_level(struct snd_soc_codec *codec, |
562 | enum snd_soc_bias_level level) | 558 | enum snd_soc_bias_level level) |
563 | { | 559 | { |
@@ -586,23 +582,21 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, | |||
586 | static struct snd_soc_dai_ops uda1380_dai_ops = { | 582 | static struct snd_soc_dai_ops uda1380_dai_ops = { |
587 | .hw_params = uda1380_pcm_hw_params, | 583 | .hw_params = uda1380_pcm_hw_params, |
588 | .shutdown = uda1380_pcm_shutdown, | 584 | .shutdown = uda1380_pcm_shutdown, |
589 | .prepare = uda1380_pcm_prepare, | 585 | .trigger = uda1380_trigger, |
590 | .digital_mute = uda1380_mute, | ||
591 | .set_fmt = uda1380_set_dai_fmt_both, | 586 | .set_fmt = uda1380_set_dai_fmt_both, |
592 | }; | 587 | }; |
593 | 588 | ||
594 | static struct snd_soc_dai_ops uda1380_dai_ops_playback = { | 589 | static struct snd_soc_dai_ops uda1380_dai_ops_playback = { |
595 | .hw_params = uda1380_pcm_hw_params, | 590 | .hw_params = uda1380_pcm_hw_params, |
596 | .shutdown = uda1380_pcm_shutdown, | 591 | .shutdown = uda1380_pcm_shutdown, |
597 | .prepare = uda1380_pcm_prepare, | 592 | .trigger = uda1380_trigger, |
598 | .digital_mute = uda1380_mute, | ||
599 | .set_fmt = uda1380_set_dai_fmt_playback, | 593 | .set_fmt = uda1380_set_dai_fmt_playback, |
600 | }; | 594 | }; |
601 | 595 | ||
602 | static struct snd_soc_dai_ops uda1380_dai_ops_capture = { | 596 | static struct snd_soc_dai_ops uda1380_dai_ops_capture = { |
603 | .hw_params = uda1380_pcm_hw_params, | 597 | .hw_params = uda1380_pcm_hw_params, |
604 | .shutdown = uda1380_pcm_shutdown, | 598 | .shutdown = uda1380_pcm_shutdown, |
605 | .prepare = uda1380_pcm_prepare, | 599 | .trigger = uda1380_trigger, |
606 | .set_fmt = uda1380_set_dai_fmt_capture, | 600 | .set_fmt = uda1380_set_dai_fmt_capture, |
607 | }; | 601 | }; |
608 | 602 | ||
@@ -700,6 +694,9 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk) | |||
700 | codec->reg_cache_step = 1; | 694 | codec->reg_cache_step = 1; |
701 | uda1380_reset(codec); | 695 | uda1380_reset(codec); |
702 | 696 | ||
697 | uda1380_codec = codec; | ||
698 | INIT_WORK(&uda1380_work, uda1380_flush_work); | ||
699 | |||
703 | /* register pcms */ | 700 | /* register pcms */ |
704 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 701 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
705 | if (ret < 0) { | 702 | if (ret < 0) { |
@@ -832,8 +829,6 @@ static int uda1380_probe(struct platform_device *pdev) | |||
832 | struct snd_soc_codec *codec; | 829 | struct snd_soc_codec *codec; |
833 | int ret; | 830 | int ret; |
834 | 831 | ||
835 | pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION); | ||
836 | |||
837 | setup = socdev->codec_data; | 832 | setup = socdev->codec_data; |
838 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | 833 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); |
839 | if (codec == NULL) | 834 | if (codec == NULL) |
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c new file mode 100644 index 000000000000..4e1cefff8483 --- /dev/null +++ b/sound/soc/codecs/wm8400.c | |||
@@ -0,0 +1,1481 @@ | |||
1 | /* | ||
2 | * wm8400.c -- WM8400 ALSA Soc Audio driver | ||
3 | * | ||
4 | * Copyright 2008, 2009 Wolfson Microelectronics PLC. | ||
5 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/moduleparam.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/pm.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/regulator/consumer.h> | ||
22 | #include <linux/mfd/wm8400-audio.h> | ||
23 | #include <linux/mfd/wm8400-private.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/pcm_params.h> | ||
27 | #include <sound/soc.h> | ||
28 | #include <sound/soc-dapm.h> | ||
29 | #include <sound/initval.h> | ||
30 | #include <sound/tlv.h> | ||
31 | |||
32 | #include "wm8400.h" | ||
33 | |||
34 | /* Fake register for internal state */ | ||
35 | #define WM8400_INTDRIVBITS (WM8400_REGISTER_COUNT + 1) | ||
36 | #define WM8400_INMIXL_PWR 0 | ||
37 | #define WM8400_AINLMUX_PWR 1 | ||
38 | #define WM8400_INMIXR_PWR 2 | ||
39 | #define WM8400_AINRMUX_PWR 3 | ||
40 | |||
41 | static struct regulator_bulk_data power[] = { | ||
42 | { | ||
43 | .supply = "I2S1VDD", | ||
44 | }, | ||
45 | { | ||
46 | .supply = "I2S2VDD", | ||
47 | }, | ||
48 | { | ||
49 | .supply = "DCVDD", | ||
50 | }, | ||
51 | { | ||
52 | .supply = "FLLVDD", | ||
53 | }, | ||
54 | { | ||
55 | .supply = "HPVDD", | ||
56 | }, | ||
57 | { | ||
58 | .supply = "SPKVDD", | ||
59 | }, | ||
60 | }; | ||
61 | |||
62 | /* codec private data */ | ||
63 | struct wm8400_priv { | ||
64 | struct snd_soc_codec codec; | ||
65 | struct wm8400 *wm8400; | ||
66 | u16 fake_register; | ||
67 | unsigned int sysclk; | ||
68 | unsigned int pcmclk; | ||
69 | struct work_struct work; | ||
70 | }; | ||
71 | |||
72 | static inline unsigned int wm8400_read(struct snd_soc_codec *codec, | ||
73 | unsigned int reg) | ||
74 | { | ||
75 | struct wm8400_priv *wm8400 = codec->private_data; | ||
76 | |||
77 | if (reg == WM8400_INTDRIVBITS) | ||
78 | return wm8400->fake_register; | ||
79 | else | ||
80 | return wm8400_reg_read(wm8400->wm8400, reg); | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * write to the wm8400 register space | ||
85 | */ | ||
86 | static int wm8400_write(struct snd_soc_codec *codec, unsigned int reg, | ||
87 | unsigned int value) | ||
88 | { | ||
89 | struct wm8400_priv *wm8400 = codec->private_data; | ||
90 | |||
91 | if (reg == WM8400_INTDRIVBITS) { | ||
92 | wm8400->fake_register = value; | ||
93 | return 0; | ||
94 | } else | ||
95 | return wm8400_set_bits(wm8400->wm8400, reg, 0xffff, value); | ||
96 | } | ||
97 | |||
98 | static void wm8400_codec_reset(struct snd_soc_codec *codec) | ||
99 | { | ||
100 | struct wm8400_priv *wm8400 = codec->private_data; | ||
101 | |||
102 | wm8400_reset_codec_reg_cache(wm8400->wm8400); | ||
103 | } | ||
104 | |||
105 | static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600); | ||
106 | |||
107 | static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000); | ||
108 | |||
109 | static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, -2100, 0); | ||
110 | |||
111 | static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600); | ||
112 | |||
113 | static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0); | ||
114 | |||
115 | static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0); | ||
116 | |||
117 | static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763); | ||
118 | |||
119 | static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0); | ||
120 | |||
121 | static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, | ||
122 | struct snd_ctl_elem_value *ucontrol) | ||
123 | { | ||
124 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
125 | struct soc_mixer_control *mc = | ||
126 | (struct soc_mixer_control *)kcontrol->private_value; | ||
127 | int reg = mc->reg; | ||
128 | int ret; | ||
129 | u16 val; | ||
130 | |||
131 | ret = snd_soc_put_volsw(kcontrol, ucontrol); | ||
132 | if (ret < 0) | ||
133 | return ret; | ||
134 | |||
135 | /* now hit the volume update bits (always bit 8) */ | ||
136 | val = wm8400_read(codec, reg); | ||
137 | return wm8400_write(codec, reg, val | 0x0100); | ||
138 | } | ||
139 | |||
140 | #define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \ | ||
141 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ | ||
142 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | ||
143 | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | ||
144 | .tlv.p = (tlv_array), \ | ||
145 | .info = snd_soc_info_volsw, \ | ||
146 | .get = snd_soc_get_volsw, .put = wm8400_outpga_put_volsw_vu, \ | ||
147 | .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } | ||
148 | |||
149 | |||
150 | static const char *wm8400_digital_sidetone[] = | ||
151 | {"None", "Left ADC", "Right ADC", "Reserved"}; | ||
152 | |||
153 | static const struct soc_enum wm8400_left_digital_sidetone_enum = | ||
154 | SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE, | ||
155 | WM8400_ADC_TO_DACL_SHIFT, 2, wm8400_digital_sidetone); | ||
156 | |||
157 | static const struct soc_enum wm8400_right_digital_sidetone_enum = | ||
158 | SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE, | ||
159 | WM8400_ADC_TO_DACR_SHIFT, 2, wm8400_digital_sidetone); | ||
160 | |||
161 | static const char *wm8400_adcmode[] = | ||
162 | {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"}; | ||
163 | |||
164 | static const struct soc_enum wm8400_right_adcmode_enum = | ||
165 | SOC_ENUM_SINGLE(WM8400_ADC_CTRL, WM8400_ADC_HPF_CUT_SHIFT, 3, wm8400_adcmode); | ||
166 | |||
167 | static const struct snd_kcontrol_new wm8400_snd_controls[] = { | ||
168 | /* INMIXL */ | ||
169 | SOC_SINGLE("LIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L12MNBST_SHIFT, | ||
170 | 1, 0), | ||
171 | SOC_SINGLE("LIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L34MNBST_SHIFT, | ||
172 | 1, 0), | ||
173 | /* INMIXR */ | ||
174 | SOC_SINGLE("RIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R12MNBST_SHIFT, | ||
175 | 1, 0), | ||
176 | SOC_SINGLE("RIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R34MNBST_SHIFT, | ||
177 | 1, 0), | ||
178 | |||
179 | /* LOMIX */ | ||
180 | SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER3, | ||
181 | WM8400_LLI3LOVOL_SHIFT, 7, 0, out_mix_tlv), | ||
182 | SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3, | ||
183 | WM8400_LR12LOVOL_SHIFT, 7, 0, out_mix_tlv), | ||
184 | SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3, | ||
185 | WM8400_LL12LOVOL_SHIFT, 7, 0, out_mix_tlv), | ||
186 | SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER5, | ||
187 | WM8400_LRI3LOVOL_SHIFT, 7, 0, out_mix_tlv), | ||
188 | SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER5, | ||
189 | WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv), | ||
190 | SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER5, | ||
191 | WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv), | ||
192 | |||
193 | /* ROMIX */ | ||
194 | SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER4, | ||
195 | WM8400_RRI3ROVOL_SHIFT, 7, 0, out_mix_tlv), | ||
196 | SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4, | ||
197 | WM8400_RL12ROVOL_SHIFT, 7, 0, out_mix_tlv), | ||
198 | SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4, | ||
199 | WM8400_RR12ROVOL_SHIFT, 7, 0, out_mix_tlv), | ||
200 | SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER6, | ||
201 | WM8400_RLI3ROVOL_SHIFT, 7, 0, out_mix_tlv), | ||
202 | SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER6, | ||
203 | WM8400_RLBROVOL_SHIFT, 7, 0, out_mix_tlv), | ||
204 | SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER6, | ||
205 | WM8400_RRBROVOL_SHIFT, 7, 0, out_mix_tlv), | ||
206 | |||
207 | /* LOUT */ | ||
208 | WM8400_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8400_LEFT_OUTPUT_VOLUME, | ||
209 | WM8400_LOUTVOL_SHIFT, WM8400_LOUTVOL_MASK, 0, out_pga_tlv), | ||
210 | SOC_SINGLE("LOUT ZC", WM8400_LEFT_OUTPUT_VOLUME, WM8400_LOZC_SHIFT, 1, 0), | ||
211 | |||
212 | /* ROUT */ | ||
213 | WM8400_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8400_RIGHT_OUTPUT_VOLUME, | ||
214 | WM8400_ROUTVOL_SHIFT, WM8400_ROUTVOL_MASK, 0, out_pga_tlv), | ||
215 | SOC_SINGLE("ROUT ZC", WM8400_RIGHT_OUTPUT_VOLUME, WM8400_ROZC_SHIFT, 1, 0), | ||
216 | |||
217 | /* LOPGA */ | ||
218 | WM8400_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8400_LEFT_OPGA_VOLUME, | ||
219 | WM8400_LOPGAVOL_SHIFT, WM8400_LOPGAVOL_MASK, 0, out_pga_tlv), | ||
220 | SOC_SINGLE("LOPGA ZC Switch", WM8400_LEFT_OPGA_VOLUME, | ||
221 | WM8400_LOPGAZC_SHIFT, 1, 0), | ||
222 | |||
223 | /* ROPGA */ | ||
224 | WM8400_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8400_RIGHT_OPGA_VOLUME, | ||
225 | WM8400_ROPGAVOL_SHIFT, WM8400_ROPGAVOL_MASK, 0, out_pga_tlv), | ||
226 | SOC_SINGLE("ROPGA ZC Switch", WM8400_RIGHT_OPGA_VOLUME, | ||
227 | WM8400_ROPGAZC_SHIFT, 1, 0), | ||
228 | |||
229 | SOC_SINGLE("LON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME, | ||
230 | WM8400_LONMUTE_SHIFT, 1, 0), | ||
231 | SOC_SINGLE("LOP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME, | ||
232 | WM8400_LOPMUTE_SHIFT, 1, 0), | ||
233 | SOC_SINGLE("LOP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME, | ||
234 | WM8400_LOATTN_SHIFT, 1, 0), | ||
235 | SOC_SINGLE("RON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME, | ||
236 | WM8400_RONMUTE_SHIFT, 1, 0), | ||
237 | SOC_SINGLE("ROP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME, | ||
238 | WM8400_ROPMUTE_SHIFT, 1, 0), | ||
239 | SOC_SINGLE("ROP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME, | ||
240 | WM8400_ROATTN_SHIFT, 1, 0), | ||
241 | |||
242 | SOC_SINGLE("OUT3 Mute Switch", WM8400_OUT3_4_VOLUME, | ||
243 | WM8400_OUT3MUTE_SHIFT, 1, 0), | ||
244 | SOC_SINGLE("OUT3 Attenuation Switch", WM8400_OUT3_4_VOLUME, | ||
245 | WM8400_OUT3ATTN_SHIFT, 1, 0), | ||
246 | |||
247 | SOC_SINGLE("OUT4 Mute Switch", WM8400_OUT3_4_VOLUME, | ||
248 | WM8400_OUT4MUTE_SHIFT, 1, 0), | ||
249 | SOC_SINGLE("OUT4 Attenuation Switch", WM8400_OUT3_4_VOLUME, | ||
250 | WM8400_OUT4ATTN_SHIFT, 1, 0), | ||
251 | |||
252 | SOC_SINGLE("Speaker Mode Switch", WM8400_CLASSD1, | ||
253 | WM8400_CDMODE_SHIFT, 1, 0), | ||
254 | |||
255 | SOC_SINGLE("Speaker Output Attenuation Volume", WM8400_SPEAKER_VOLUME, | ||
256 | WM8400_SPKATTN_SHIFT, WM8400_SPKATTN_MASK, 0), | ||
257 | SOC_SINGLE("Speaker DC Boost Volume", WM8400_CLASSD3, | ||
258 | WM8400_DCGAIN_SHIFT, 6, 0), | ||
259 | SOC_SINGLE("Speaker AC Boost Volume", WM8400_CLASSD3, | ||
260 | WM8400_ACGAIN_SHIFT, 6, 0), | ||
261 | |||
262 | WM8400_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume", | ||
263 | WM8400_LEFT_DAC_DIGITAL_VOLUME, WM8400_DACL_VOL_SHIFT, | ||
264 | 127, 0, out_dac_tlv), | ||
265 | |||
266 | WM8400_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume", | ||
267 | WM8400_RIGHT_DAC_DIGITAL_VOLUME, WM8400_DACR_VOL_SHIFT, | ||
268 | 127, 0, out_dac_tlv), | ||
269 | |||
270 | SOC_ENUM("Left Digital Sidetone", wm8400_left_digital_sidetone_enum), | ||
271 | SOC_ENUM("Right Digital Sidetone", wm8400_right_digital_sidetone_enum), | ||
272 | |||
273 | SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE, | ||
274 | WM8400_ADCL_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv), | ||
275 | SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE, | ||
276 | WM8400_ADCR_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv), | ||
277 | |||
278 | SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8400_ADC_CTRL, | ||
279 | WM8400_ADC_HPF_ENA_SHIFT, 1, 0), | ||
280 | |||
281 | SOC_ENUM("ADC HPF Mode", wm8400_right_adcmode_enum), | ||
282 | |||
283 | WM8400_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume", | ||
284 | WM8400_LEFT_ADC_DIGITAL_VOLUME, | ||
285 | WM8400_ADCL_VOL_SHIFT, | ||
286 | WM8400_ADCL_VOL_MASK, | ||
287 | 0, | ||
288 | in_adc_tlv), | ||
289 | |||
290 | WM8400_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume", | ||
291 | WM8400_RIGHT_ADC_DIGITAL_VOLUME, | ||
292 | WM8400_ADCR_VOL_SHIFT, | ||
293 | WM8400_ADCR_VOL_MASK, | ||
294 | 0, | ||
295 | in_adc_tlv), | ||
296 | |||
297 | WM8400_OUTPGA_SINGLE_R_TLV("LIN12 Volume", | ||
298 | WM8400_LEFT_LINE_INPUT_1_2_VOLUME, | ||
299 | WM8400_LIN12VOL_SHIFT, | ||
300 | WM8400_LIN12VOL_MASK, | ||
301 | 0, | ||
302 | in_pga_tlv), | ||
303 | |||
304 | SOC_SINGLE("LIN12 ZC Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME, | ||
305 | WM8400_LI12ZC_SHIFT, 1, 0), | ||
306 | |||
307 | SOC_SINGLE("LIN12 Mute Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME, | ||
308 | WM8400_LI12MUTE_SHIFT, 1, 0), | ||
309 | |||
310 | WM8400_OUTPGA_SINGLE_R_TLV("LIN34 Volume", | ||
311 | WM8400_LEFT_LINE_INPUT_3_4_VOLUME, | ||
312 | WM8400_LIN34VOL_SHIFT, | ||
313 | WM8400_LIN34VOL_MASK, | ||
314 | 0, | ||
315 | in_pga_tlv), | ||
316 | |||
317 | SOC_SINGLE("LIN34 ZC Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME, | ||
318 | WM8400_LI34ZC_SHIFT, 1, 0), | ||
319 | |||
320 | SOC_SINGLE("LIN34 Mute Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME, | ||
321 | WM8400_LI34MUTE_SHIFT, 1, 0), | ||
322 | |||
323 | WM8400_OUTPGA_SINGLE_R_TLV("RIN12 Volume", | ||
324 | WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, | ||
325 | WM8400_RIN12VOL_SHIFT, | ||
326 | WM8400_RIN12VOL_MASK, | ||
327 | 0, | ||
328 | in_pga_tlv), | ||
329 | |||
330 | SOC_SINGLE("RIN12 ZC Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, | ||
331 | WM8400_RI12ZC_SHIFT, 1, 0), | ||
332 | |||
333 | SOC_SINGLE("RIN12 Mute Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, | ||
334 | WM8400_RI12MUTE_SHIFT, 1, 0), | ||
335 | |||
336 | WM8400_OUTPGA_SINGLE_R_TLV("RIN34 Volume", | ||
337 | WM8400_RIGHT_LINE_INPUT_3_4_VOLUME, | ||
338 | WM8400_RIN34VOL_SHIFT, | ||
339 | WM8400_RIN34VOL_MASK, | ||
340 | 0, | ||
341 | in_pga_tlv), | ||
342 | |||
343 | SOC_SINGLE("RIN34 ZC Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME, | ||
344 | WM8400_RI34ZC_SHIFT, 1, 0), | ||
345 | |||
346 | SOC_SINGLE("RIN34 Mute Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME, | ||
347 | WM8400_RI34MUTE_SHIFT, 1, 0), | ||
348 | |||
349 | }; | ||
350 | |||
351 | /* add non dapm controls */ | ||
352 | static int wm8400_add_controls(struct snd_soc_codec *codec) | ||
353 | { | ||
354 | int err, i; | ||
355 | |||
356 | for (i = 0; i < ARRAY_SIZE(wm8400_snd_controls); i++) { | ||
357 | err = snd_ctl_add(codec->card, | ||
358 | snd_soc_cnew(&wm8400_snd_controls[i],codec, | ||
359 | NULL)); | ||
360 | if (err < 0) | ||
361 | return err; | ||
362 | } | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * _DAPM_ Controls | ||
368 | */ | ||
369 | |||
370 | static int inmixer_event (struct snd_soc_dapm_widget *w, | ||
371 | struct snd_kcontrol *kcontrol, int event) | ||
372 | { | ||
373 | u16 reg, fakepower; | ||
374 | |||
375 | reg = wm8400_read(w->codec, WM8400_POWER_MANAGEMENT_2); | ||
376 | fakepower = wm8400_read(w->codec, WM8400_INTDRIVBITS); | ||
377 | |||
378 | if (fakepower & ((1 << WM8400_INMIXL_PWR) | | ||
379 | (1 << WM8400_AINLMUX_PWR))) { | ||
380 | reg |= WM8400_AINL_ENA; | ||
381 | } else { | ||
382 | reg &= ~WM8400_AINL_ENA; | ||
383 | } | ||
384 | |||
385 | if (fakepower & ((1 << WM8400_INMIXR_PWR) | | ||
386 | (1 << WM8400_AINRMUX_PWR))) { | ||
387 | reg |= WM8400_AINR_ENA; | ||
388 | } else { | ||
389 | reg &= ~WM8400_AINL_ENA; | ||
390 | } | ||
391 | wm8400_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg); | ||
392 | |||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | static int outmixer_event (struct snd_soc_dapm_widget *w, | ||
397 | struct snd_kcontrol * kcontrol, int event) | ||
398 | { | ||
399 | struct soc_mixer_control *mc = | ||
400 | (struct soc_mixer_control *)kcontrol->private_value; | ||
401 | u32 reg_shift = mc->shift; | ||
402 | int ret = 0; | ||
403 | u16 reg; | ||
404 | |||
405 | switch (reg_shift) { | ||
406 | case WM8400_SPEAKER_MIXER | (WM8400_LDSPK << 8) : | ||
407 | reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER1); | ||
408 | if (reg & WM8400_LDLO) { | ||
409 | printk(KERN_WARNING | ||
410 | "Cannot set as Output Mixer 1 LDLO Set\n"); | ||
411 | ret = -1; | ||
412 | } | ||
413 | break; | ||
414 | case WM8400_SPEAKER_MIXER | (WM8400_RDSPK << 8): | ||
415 | reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER2); | ||
416 | if (reg & WM8400_RDRO) { | ||
417 | printk(KERN_WARNING | ||
418 | "Cannot set as Output Mixer 2 RDRO Set\n"); | ||
419 | ret = -1; | ||
420 | } | ||
421 | break; | ||
422 | case WM8400_OUTPUT_MIXER1 | (WM8400_LDLO << 8): | ||
423 | reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER); | ||
424 | if (reg & WM8400_LDSPK) { | ||
425 | printk(KERN_WARNING | ||
426 | "Cannot set as Speaker Mixer LDSPK Set\n"); | ||
427 | ret = -1; | ||
428 | } | ||
429 | break; | ||
430 | case WM8400_OUTPUT_MIXER2 | (WM8400_RDRO << 8): | ||
431 | reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER); | ||
432 | if (reg & WM8400_RDSPK) { | ||
433 | printk(KERN_WARNING | ||
434 | "Cannot set as Speaker Mixer RDSPK Set\n"); | ||
435 | ret = -1; | ||
436 | } | ||
437 | break; | ||
438 | } | ||
439 | |||
440 | return ret; | ||
441 | } | ||
442 | |||
443 | /* INMIX dB values */ | ||
444 | static const unsigned int in_mix_tlv[] = { | ||
445 | TLV_DB_RANGE_HEAD(1), | ||
446 | 0,7, TLV_DB_LINEAR_ITEM(-1200, 600), | ||
447 | }; | ||
448 | |||
449 | /* Left In PGA Connections */ | ||
450 | static const struct snd_kcontrol_new wm8400_dapm_lin12_pga_controls[] = { | ||
451 | SOC_DAPM_SINGLE("LIN1 Switch", WM8400_INPUT_MIXER2, WM8400_LMN1_SHIFT, 1, 0), | ||
452 | SOC_DAPM_SINGLE("LIN2 Switch", WM8400_INPUT_MIXER2, WM8400_LMP2_SHIFT, 1, 0), | ||
453 | }; | ||
454 | |||
455 | static const struct snd_kcontrol_new wm8400_dapm_lin34_pga_controls[] = { | ||
456 | SOC_DAPM_SINGLE("LIN3 Switch", WM8400_INPUT_MIXER2, WM8400_LMN3_SHIFT, 1, 0), | ||
457 | SOC_DAPM_SINGLE("LIN4 Switch", WM8400_INPUT_MIXER2, WM8400_LMP4_SHIFT, 1, 0), | ||
458 | }; | ||
459 | |||
460 | /* Right In PGA Connections */ | ||
461 | static const struct snd_kcontrol_new wm8400_dapm_rin12_pga_controls[] = { | ||
462 | SOC_DAPM_SINGLE("RIN1 Switch", WM8400_INPUT_MIXER2, WM8400_RMN1_SHIFT, 1, 0), | ||
463 | SOC_DAPM_SINGLE("RIN2 Switch", WM8400_INPUT_MIXER2, WM8400_RMP2_SHIFT, 1, 0), | ||
464 | }; | ||
465 | |||
466 | static const struct snd_kcontrol_new wm8400_dapm_rin34_pga_controls[] = { | ||
467 | SOC_DAPM_SINGLE("RIN3 Switch", WM8400_INPUT_MIXER2, WM8400_RMN3_SHIFT, 1, 0), | ||
468 | SOC_DAPM_SINGLE("RIN4 Switch", WM8400_INPUT_MIXER2, WM8400_RMP4_SHIFT, 1, 0), | ||
469 | }; | ||
470 | |||
471 | /* INMIXL */ | ||
472 | static const struct snd_kcontrol_new wm8400_dapm_inmixl_controls[] = { | ||
473 | SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8400_INPUT_MIXER3, | ||
474 | WM8400_LDBVOL_SHIFT, WM8400_LDBVOL_MASK, 0, in_mix_tlv), | ||
475 | SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8400_INPUT_MIXER5, WM8400_LI2BVOL_SHIFT, | ||
476 | 7, 0, in_mix_tlv), | ||
477 | SOC_DAPM_SINGLE("LINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT, | ||
478 | 1, 0), | ||
479 | SOC_DAPM_SINGLE("LINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT, | ||
480 | 1, 0), | ||
481 | }; | ||
482 | |||
483 | /* INMIXR */ | ||
484 | static const struct snd_kcontrol_new wm8400_dapm_inmixr_controls[] = { | ||
485 | SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8400_INPUT_MIXER4, | ||
486 | WM8400_RDBVOL_SHIFT, WM8400_RDBVOL_MASK, 0, in_mix_tlv), | ||
487 | SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8400_INPUT_MIXER6, WM8400_RI2BVOL_SHIFT, | ||
488 | 7, 0, in_mix_tlv), | ||
489 | SOC_DAPM_SINGLE("RINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT, | ||
490 | 1, 0), | ||
491 | SOC_DAPM_SINGLE("RINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT, | ||
492 | 1, 0), | ||
493 | }; | ||
494 | |||
495 | /* AINLMUX */ | ||
496 | static const char *wm8400_ainlmux[] = | ||
497 | {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"}; | ||
498 | |||
499 | static const struct soc_enum wm8400_ainlmux_enum = | ||
500 | SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINLMODE_SHIFT, | ||
501 | ARRAY_SIZE(wm8400_ainlmux), wm8400_ainlmux); | ||
502 | |||
503 | static const struct snd_kcontrol_new wm8400_dapm_ainlmux_controls = | ||
504 | SOC_DAPM_ENUM("Route", wm8400_ainlmux_enum); | ||
505 | |||
506 | /* DIFFINL */ | ||
507 | |||
508 | /* AINRMUX */ | ||
509 | static const char *wm8400_ainrmux[] = | ||
510 | {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"}; | ||
511 | |||
512 | static const struct soc_enum wm8400_ainrmux_enum = | ||
513 | SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINRMODE_SHIFT, | ||
514 | ARRAY_SIZE(wm8400_ainrmux), wm8400_ainrmux); | ||
515 | |||
516 | static const struct snd_kcontrol_new wm8400_dapm_ainrmux_controls = | ||
517 | SOC_DAPM_ENUM("Route", wm8400_ainrmux_enum); | ||
518 | |||
519 | /* RXVOICE */ | ||
520 | static const struct snd_kcontrol_new wm8400_dapm_rxvoice_controls[] = { | ||
521 | SOC_DAPM_SINGLE_TLV("LIN4/RXN", WM8400_INPUT_MIXER5, WM8400_LR4BVOL_SHIFT, | ||
522 | WM8400_LR4BVOL_MASK, 0, in_mix_tlv), | ||
523 | SOC_DAPM_SINGLE_TLV("RIN4/RXP", WM8400_INPUT_MIXER6, WM8400_RL4BVOL_SHIFT, | ||
524 | WM8400_RL4BVOL_MASK, 0, in_mix_tlv), | ||
525 | }; | ||
526 | |||
527 | /* LOMIX */ | ||
528 | static const struct snd_kcontrol_new wm8400_dapm_lomix_controls[] = { | ||
529 | SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER1, | ||
530 | WM8400_LRBLO_SHIFT, 1, 0), | ||
531 | SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER1, | ||
532 | WM8400_LLBLO_SHIFT, 1, 0), | ||
533 | SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER1, | ||
534 | WM8400_LRI3LO_SHIFT, 1, 0), | ||
535 | SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER1, | ||
536 | WM8400_LLI3LO_SHIFT, 1, 0), | ||
537 | SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1, | ||
538 | WM8400_LR12LO_SHIFT, 1, 0), | ||
539 | SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1, | ||
540 | WM8400_LL12LO_SHIFT, 1, 0), | ||
541 | SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8400_OUTPUT_MIXER1, | ||
542 | WM8400_LDLO_SHIFT, 1, 0), | ||
543 | }; | ||
544 | |||
545 | /* ROMIX */ | ||
546 | static const struct snd_kcontrol_new wm8400_dapm_romix_controls[] = { | ||
547 | SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER2, | ||
548 | WM8400_RLBRO_SHIFT, 1, 0), | ||
549 | SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER2, | ||
550 | WM8400_RRBRO_SHIFT, 1, 0), | ||
551 | SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER2, | ||
552 | WM8400_RLI3RO_SHIFT, 1, 0), | ||
553 | SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER2, | ||
554 | WM8400_RRI3RO_SHIFT, 1, 0), | ||
555 | SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2, | ||
556 | WM8400_RL12RO_SHIFT, 1, 0), | ||
557 | SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2, | ||
558 | WM8400_RR12RO_SHIFT, 1, 0), | ||
559 | SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8400_OUTPUT_MIXER2, | ||
560 | WM8400_RDRO_SHIFT, 1, 0), | ||
561 | }; | ||
562 | |||
563 | /* LONMIX */ | ||
564 | static const struct snd_kcontrol_new wm8400_dapm_lonmix_controls[] = { | ||
565 | SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1, | ||
566 | WM8400_LLOPGALON_SHIFT, 1, 0), | ||
567 | SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER1, | ||
568 | WM8400_LROPGALON_SHIFT, 1, 0), | ||
569 | SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8400_LINE_MIXER1, | ||
570 | WM8400_LOPLON_SHIFT, 1, 0), | ||
571 | }; | ||
572 | |||
573 | /* LOPMIX */ | ||
574 | static const struct snd_kcontrol_new wm8400_dapm_lopmix_controls[] = { | ||
575 | SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER1, | ||
576 | WM8400_LR12LOP_SHIFT, 1, 0), | ||
577 | SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER1, | ||
578 | WM8400_LL12LOP_SHIFT, 1, 0), | ||
579 | SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1, | ||
580 | WM8400_LLOPGALOP_SHIFT, 1, 0), | ||
581 | }; | ||
582 | |||
583 | /* RONMIX */ | ||
584 | static const struct snd_kcontrol_new wm8400_dapm_ronmix_controls[] = { | ||
585 | SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2, | ||
586 | WM8400_RROPGARON_SHIFT, 1, 0), | ||
587 | SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER2, | ||
588 | WM8400_RLOPGARON_SHIFT, 1, 0), | ||
589 | SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8400_LINE_MIXER2, | ||
590 | WM8400_ROPRON_SHIFT, 1, 0), | ||
591 | }; | ||
592 | |||
593 | /* ROPMIX */ | ||
594 | static const struct snd_kcontrol_new wm8400_dapm_ropmix_controls[] = { | ||
595 | SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER2, | ||
596 | WM8400_RL12ROP_SHIFT, 1, 0), | ||
597 | SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER2, | ||
598 | WM8400_RR12ROP_SHIFT, 1, 0), | ||
599 | SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2, | ||
600 | WM8400_RROPGAROP_SHIFT, 1, 0), | ||
601 | }; | ||
602 | |||
603 | /* OUT3MIX */ | ||
604 | static const struct snd_kcontrol_new wm8400_dapm_out3mix_controls[] = { | ||
605 | SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER, | ||
606 | WM8400_LI4O3_SHIFT, 1, 0), | ||
607 | SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8400_OUT3_4_MIXER, | ||
608 | WM8400_LPGAO3_SHIFT, 1, 0), | ||
609 | }; | ||
610 | |||
611 | /* OUT4MIX */ | ||
612 | static const struct snd_kcontrol_new wm8400_dapm_out4mix_controls[] = { | ||
613 | SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8400_OUT3_4_MIXER, | ||
614 | WM8400_RPGAO4_SHIFT, 1, 0), | ||
615 | SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER, | ||
616 | WM8400_RI4O4_SHIFT, 1, 0), | ||
617 | }; | ||
618 | |||
619 | /* SPKMIX */ | ||
620 | static const struct snd_kcontrol_new wm8400_dapm_spkmix_controls[] = { | ||
621 | SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8400_SPEAKER_MIXER, | ||
622 | WM8400_LI2SPK_SHIFT, 1, 0), | ||
623 | SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8400_SPEAKER_MIXER, | ||
624 | WM8400_LB2SPK_SHIFT, 1, 0), | ||
625 | SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8400_SPEAKER_MIXER, | ||
626 | WM8400_LOPGASPK_SHIFT, 1, 0), | ||
627 | SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8400_SPEAKER_MIXER, | ||
628 | WM8400_LDSPK_SHIFT, 1, 0), | ||
629 | SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8400_SPEAKER_MIXER, | ||
630 | WM8400_RDSPK_SHIFT, 1, 0), | ||
631 | SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8400_SPEAKER_MIXER, | ||
632 | WM8400_ROPGASPK_SHIFT, 1, 0), | ||
633 | SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8400_SPEAKER_MIXER, | ||
634 | WM8400_RL12ROP_SHIFT, 1, 0), | ||
635 | SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8400_SPEAKER_MIXER, | ||
636 | WM8400_RI2SPK_SHIFT, 1, 0), | ||
637 | }; | ||
638 | |||
639 | static const struct snd_soc_dapm_widget wm8400_dapm_widgets[] = { | ||
640 | /* Input Side */ | ||
641 | /* Input Lines */ | ||
642 | SND_SOC_DAPM_INPUT("LIN1"), | ||
643 | SND_SOC_DAPM_INPUT("LIN2"), | ||
644 | SND_SOC_DAPM_INPUT("LIN3"), | ||
645 | SND_SOC_DAPM_INPUT("LIN4/RXN"), | ||
646 | SND_SOC_DAPM_INPUT("RIN3"), | ||
647 | SND_SOC_DAPM_INPUT("RIN4/RXP"), | ||
648 | SND_SOC_DAPM_INPUT("RIN1"), | ||
649 | SND_SOC_DAPM_INPUT("RIN2"), | ||
650 | SND_SOC_DAPM_INPUT("Internal ADC Source"), | ||
651 | |||
652 | /* DACs */ | ||
653 | SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8400_POWER_MANAGEMENT_2, | ||
654 | WM8400_ADCL_ENA_SHIFT, 0), | ||
655 | SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8400_POWER_MANAGEMENT_2, | ||
656 | WM8400_ADCR_ENA_SHIFT, 0), | ||
657 | |||
658 | /* Input PGAs */ | ||
659 | SND_SOC_DAPM_MIXER("LIN12 PGA", WM8400_POWER_MANAGEMENT_2, | ||
660 | WM8400_LIN12_ENA_SHIFT, | ||
661 | 0, &wm8400_dapm_lin12_pga_controls[0], | ||
662 | ARRAY_SIZE(wm8400_dapm_lin12_pga_controls)), | ||
663 | SND_SOC_DAPM_MIXER("LIN34 PGA", WM8400_POWER_MANAGEMENT_2, | ||
664 | WM8400_LIN34_ENA_SHIFT, | ||
665 | 0, &wm8400_dapm_lin34_pga_controls[0], | ||
666 | ARRAY_SIZE(wm8400_dapm_lin34_pga_controls)), | ||
667 | SND_SOC_DAPM_MIXER("RIN12 PGA", WM8400_POWER_MANAGEMENT_2, | ||
668 | WM8400_RIN12_ENA_SHIFT, | ||
669 | 0, &wm8400_dapm_rin12_pga_controls[0], | ||
670 | ARRAY_SIZE(wm8400_dapm_rin12_pga_controls)), | ||
671 | SND_SOC_DAPM_MIXER("RIN34 PGA", WM8400_POWER_MANAGEMENT_2, | ||
672 | WM8400_RIN34_ENA_SHIFT, | ||
673 | 0, &wm8400_dapm_rin34_pga_controls[0], | ||
674 | ARRAY_SIZE(wm8400_dapm_rin34_pga_controls)), | ||
675 | |||
676 | /* INMIXL */ | ||
677 | SND_SOC_DAPM_MIXER_E("INMIXL", WM8400_INTDRIVBITS, WM8400_INMIXL_PWR, 0, | ||
678 | &wm8400_dapm_inmixl_controls[0], | ||
679 | ARRAY_SIZE(wm8400_dapm_inmixl_controls), | ||
680 | inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
681 | |||
682 | /* AINLMUX */ | ||
683 | SND_SOC_DAPM_MUX_E("AILNMUX", WM8400_INTDRIVBITS, WM8400_AINLMUX_PWR, 0, | ||
684 | &wm8400_dapm_ainlmux_controls, inmixer_event, | ||
685 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
686 | |||
687 | /* INMIXR */ | ||
688 | SND_SOC_DAPM_MIXER_E("INMIXR", WM8400_INTDRIVBITS, WM8400_INMIXR_PWR, 0, | ||
689 | &wm8400_dapm_inmixr_controls[0], | ||
690 | ARRAY_SIZE(wm8400_dapm_inmixr_controls), | ||
691 | inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
692 | |||
693 | /* AINRMUX */ | ||
694 | SND_SOC_DAPM_MUX_E("AIRNMUX", WM8400_INTDRIVBITS, WM8400_AINRMUX_PWR, 0, | ||
695 | &wm8400_dapm_ainrmux_controls, inmixer_event, | ||
696 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
697 | |||
698 | /* Output Side */ | ||
699 | /* DACs */ | ||
700 | SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8400_POWER_MANAGEMENT_3, | ||
701 | WM8400_DACL_ENA_SHIFT, 0), | ||
702 | SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8400_POWER_MANAGEMENT_3, | ||
703 | WM8400_DACR_ENA_SHIFT, 0), | ||
704 | |||
705 | /* LOMIX */ | ||
706 | SND_SOC_DAPM_MIXER_E("LOMIX", WM8400_POWER_MANAGEMENT_3, | ||
707 | WM8400_LOMIX_ENA_SHIFT, | ||
708 | 0, &wm8400_dapm_lomix_controls[0], | ||
709 | ARRAY_SIZE(wm8400_dapm_lomix_controls), | ||
710 | outmixer_event, SND_SOC_DAPM_PRE_REG), | ||
711 | |||
712 | /* LONMIX */ | ||
713 | SND_SOC_DAPM_MIXER("LONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LON_ENA_SHIFT, | ||
714 | 0, &wm8400_dapm_lonmix_controls[0], | ||
715 | ARRAY_SIZE(wm8400_dapm_lonmix_controls)), | ||
716 | |||
717 | /* LOPMIX */ | ||
718 | SND_SOC_DAPM_MIXER("LOPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LOP_ENA_SHIFT, | ||
719 | 0, &wm8400_dapm_lopmix_controls[0], | ||
720 | ARRAY_SIZE(wm8400_dapm_lopmix_controls)), | ||
721 | |||
722 | /* OUT3MIX */ | ||
723 | SND_SOC_DAPM_MIXER("OUT3MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT3_ENA_SHIFT, | ||
724 | 0, &wm8400_dapm_out3mix_controls[0], | ||
725 | ARRAY_SIZE(wm8400_dapm_out3mix_controls)), | ||
726 | |||
727 | /* SPKMIX */ | ||
728 | SND_SOC_DAPM_MIXER_E("SPKMIX", WM8400_POWER_MANAGEMENT_1, WM8400_SPK_ENA_SHIFT, | ||
729 | 0, &wm8400_dapm_spkmix_controls[0], | ||
730 | ARRAY_SIZE(wm8400_dapm_spkmix_controls), outmixer_event, | ||
731 | SND_SOC_DAPM_PRE_REG), | ||
732 | |||
733 | /* OUT4MIX */ | ||
734 | SND_SOC_DAPM_MIXER("OUT4MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT4_ENA_SHIFT, | ||
735 | 0, &wm8400_dapm_out4mix_controls[0], | ||
736 | ARRAY_SIZE(wm8400_dapm_out4mix_controls)), | ||
737 | |||
738 | /* ROPMIX */ | ||
739 | SND_SOC_DAPM_MIXER("ROPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_ROP_ENA_SHIFT, | ||
740 | 0, &wm8400_dapm_ropmix_controls[0], | ||
741 | ARRAY_SIZE(wm8400_dapm_ropmix_controls)), | ||
742 | |||
743 | /* RONMIX */ | ||
744 | SND_SOC_DAPM_MIXER("RONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_RON_ENA_SHIFT, | ||
745 | 0, &wm8400_dapm_ronmix_controls[0], | ||
746 | ARRAY_SIZE(wm8400_dapm_ronmix_controls)), | ||
747 | |||
748 | /* ROMIX */ | ||
749 | SND_SOC_DAPM_MIXER_E("ROMIX", WM8400_POWER_MANAGEMENT_3, | ||
750 | WM8400_ROMIX_ENA_SHIFT, | ||
751 | 0, &wm8400_dapm_romix_controls[0], | ||
752 | ARRAY_SIZE(wm8400_dapm_romix_controls), | ||
753 | outmixer_event, SND_SOC_DAPM_PRE_REG), | ||
754 | |||
755 | /* LOUT PGA */ | ||
756 | SND_SOC_DAPM_PGA("LOUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_LOUT_ENA_SHIFT, | ||
757 | 0, NULL, 0), | ||
758 | |||
759 | /* ROUT PGA */ | ||
760 | SND_SOC_DAPM_PGA("ROUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_ROUT_ENA_SHIFT, | ||
761 | 0, NULL, 0), | ||
762 | |||
763 | /* LOPGA */ | ||
764 | SND_SOC_DAPM_PGA("LOPGA", WM8400_POWER_MANAGEMENT_3, WM8400_LOPGA_ENA_SHIFT, 0, | ||
765 | NULL, 0), | ||
766 | |||
767 | /* ROPGA */ | ||
768 | SND_SOC_DAPM_PGA("ROPGA", WM8400_POWER_MANAGEMENT_3, WM8400_ROPGA_ENA_SHIFT, 0, | ||
769 | NULL, 0), | ||
770 | |||
771 | /* MICBIAS */ | ||
772 | SND_SOC_DAPM_MICBIAS("MICBIAS", WM8400_POWER_MANAGEMENT_1, | ||
773 | WM8400_MIC1BIAS_ENA_SHIFT, 0), | ||
774 | |||
775 | SND_SOC_DAPM_OUTPUT("LON"), | ||
776 | SND_SOC_DAPM_OUTPUT("LOP"), | ||
777 | SND_SOC_DAPM_OUTPUT("OUT3"), | ||
778 | SND_SOC_DAPM_OUTPUT("LOUT"), | ||
779 | SND_SOC_DAPM_OUTPUT("SPKN"), | ||
780 | SND_SOC_DAPM_OUTPUT("SPKP"), | ||
781 | SND_SOC_DAPM_OUTPUT("ROUT"), | ||
782 | SND_SOC_DAPM_OUTPUT("OUT4"), | ||
783 | SND_SOC_DAPM_OUTPUT("ROP"), | ||
784 | SND_SOC_DAPM_OUTPUT("RON"), | ||
785 | |||
786 | SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), | ||
787 | }; | ||
788 | |||
789 | static const struct snd_soc_dapm_route audio_map[] = { | ||
790 | /* Make DACs turn on when playing even if not mixed into any outputs */ | ||
791 | {"Internal DAC Sink", NULL, "Left DAC"}, | ||
792 | {"Internal DAC Sink", NULL, "Right DAC"}, | ||
793 | |||
794 | /* Make ADCs turn on when recording | ||
795 | * even if not mixed from any inputs */ | ||
796 | {"Left ADC", NULL, "Internal ADC Source"}, | ||
797 | {"Right ADC", NULL, "Internal ADC Source"}, | ||
798 | |||
799 | /* Input Side */ | ||
800 | /* LIN12 PGA */ | ||
801 | {"LIN12 PGA", "LIN1 Switch", "LIN1"}, | ||
802 | {"LIN12 PGA", "LIN2 Switch", "LIN2"}, | ||
803 | /* LIN34 PGA */ | ||
804 | {"LIN34 PGA", "LIN3 Switch", "LIN3"}, | ||
805 | {"LIN34 PGA", "LIN4 Switch", "LIN4/RXN"}, | ||
806 | /* INMIXL */ | ||
807 | {"INMIXL", "Record Left Volume", "LOMIX"}, | ||
808 | {"INMIXL", "LIN2 Volume", "LIN2"}, | ||
809 | {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"}, | ||
810 | {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"}, | ||
811 | /* AILNMUX */ | ||
812 | {"AILNMUX", "INMIXL Mix", "INMIXL"}, | ||
813 | {"AILNMUX", "DIFFINL Mix", "LIN12 PGA"}, | ||
814 | {"AILNMUX", "DIFFINL Mix", "LIN34 PGA"}, | ||
815 | {"AILNMUX", "RXVOICE Mix", "LIN4/RXN"}, | ||
816 | {"AILNMUX", "RXVOICE Mix", "RIN4/RXP"}, | ||
817 | /* ADC */ | ||
818 | {"Left ADC", NULL, "AILNMUX"}, | ||
819 | |||
820 | /* RIN12 PGA */ | ||
821 | {"RIN12 PGA", "RIN1 Switch", "RIN1"}, | ||
822 | {"RIN12 PGA", "RIN2 Switch", "RIN2"}, | ||
823 | /* RIN34 PGA */ | ||
824 | {"RIN34 PGA", "RIN3 Switch", "RIN3"}, | ||
825 | {"RIN34 PGA", "RIN4 Switch", "RIN4/RXP"}, | ||
826 | /* INMIXL */ | ||
827 | {"INMIXR", "Record Right Volume", "ROMIX"}, | ||
828 | {"INMIXR", "RIN2 Volume", "RIN2"}, | ||
829 | {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"}, | ||
830 | {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"}, | ||
831 | /* AIRNMUX */ | ||
832 | {"AIRNMUX", "INMIXR Mix", "INMIXR"}, | ||
833 | {"AIRNMUX", "DIFFINR Mix", "RIN12 PGA"}, | ||
834 | {"AIRNMUX", "DIFFINR Mix", "RIN34 PGA"}, | ||
835 | {"AIRNMUX", "RXVOICE Mix", "LIN4/RXN"}, | ||
836 | {"AIRNMUX", "RXVOICE Mix", "RIN4/RXP"}, | ||
837 | /* ADC */ | ||
838 | {"Right ADC", NULL, "AIRNMUX"}, | ||
839 | |||
840 | /* LOMIX */ | ||
841 | {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"}, | ||
842 | {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"}, | ||
843 | {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, | ||
844 | {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, | ||
845 | {"LOMIX", "LOMIX Right ADC Bypass Switch", "AIRNMUX"}, | ||
846 | {"LOMIX", "LOMIX Left ADC Bypass Switch", "AILNMUX"}, | ||
847 | {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"}, | ||
848 | |||
849 | /* ROMIX */ | ||
850 | {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"}, | ||
851 | {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"}, | ||
852 | {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, | ||
853 | {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, | ||
854 | {"ROMIX", "ROMIX Right ADC Bypass Switch", "AIRNMUX"}, | ||
855 | {"ROMIX", "ROMIX Left ADC Bypass Switch", "AILNMUX"}, | ||
856 | {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"}, | ||
857 | |||
858 | /* SPKMIX */ | ||
859 | {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"}, | ||
860 | {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"}, | ||
861 | {"SPKMIX", "SPKMIX LADC Bypass Switch", "AILNMUX"}, | ||
862 | {"SPKMIX", "SPKMIX RADC Bypass Switch", "AIRNMUX"}, | ||
863 | {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"}, | ||
864 | {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"}, | ||
865 | {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"}, | ||
866 | {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"}, | ||
867 | |||
868 | /* LONMIX */ | ||
869 | {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"}, | ||
870 | {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"}, | ||
871 | {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"}, | ||
872 | |||
873 | /* LOPMIX */ | ||
874 | {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"}, | ||
875 | {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"}, | ||
876 | {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"}, | ||
877 | |||
878 | /* OUT3MIX */ | ||
879 | {"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXN"}, | ||
880 | {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"}, | ||
881 | |||
882 | /* OUT4MIX */ | ||
883 | {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"}, | ||
884 | {"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"}, | ||
885 | |||
886 | /* RONMIX */ | ||
887 | {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"}, | ||
888 | {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"}, | ||
889 | {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"}, | ||
890 | |||
891 | /* ROPMIX */ | ||
892 | {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"}, | ||
893 | {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"}, | ||
894 | {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"}, | ||
895 | |||
896 | /* Out Mixer PGAs */ | ||
897 | {"LOPGA", NULL, "LOMIX"}, | ||
898 | {"ROPGA", NULL, "ROMIX"}, | ||
899 | |||
900 | {"LOUT PGA", NULL, "LOMIX"}, | ||
901 | {"ROUT PGA", NULL, "ROMIX"}, | ||
902 | |||
903 | /* Output Pins */ | ||
904 | {"LON", NULL, "LONMIX"}, | ||
905 | {"LOP", NULL, "LOPMIX"}, | ||
906 | {"OUT3", NULL, "OUT3MIX"}, | ||
907 | {"LOUT", NULL, "LOUT PGA"}, | ||
908 | {"SPKN", NULL, "SPKMIX"}, | ||
909 | {"ROUT", NULL, "ROUT PGA"}, | ||
910 | {"OUT4", NULL, "OUT4MIX"}, | ||
911 | {"ROP", NULL, "ROPMIX"}, | ||
912 | {"RON", NULL, "RONMIX"}, | ||
913 | }; | ||
914 | |||
915 | static int wm8400_add_widgets(struct snd_soc_codec *codec) | ||
916 | { | ||
917 | snd_soc_dapm_new_controls(codec, wm8400_dapm_widgets, | ||
918 | ARRAY_SIZE(wm8400_dapm_widgets)); | ||
919 | |||
920 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
921 | |||
922 | snd_soc_dapm_new_widgets(codec); | ||
923 | return 0; | ||
924 | } | ||
925 | |||
926 | /* | ||
927 | * Clock after FLL and dividers | ||
928 | */ | ||
929 | static int wm8400_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
930 | int clk_id, unsigned int freq, int dir) | ||
931 | { | ||
932 | struct snd_soc_codec *codec = codec_dai->codec; | ||
933 | struct wm8400_priv *wm8400 = codec->private_data; | ||
934 | |||
935 | wm8400->sysclk = freq; | ||
936 | return 0; | ||
937 | } | ||
938 | |||
939 | /* | ||
940 | * Sets ADC and Voice DAC format. | ||
941 | */ | ||
942 | static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
943 | unsigned int fmt) | ||
944 | { | ||
945 | struct snd_soc_codec *codec = codec_dai->codec; | ||
946 | u16 audio1, audio3; | ||
947 | |||
948 | audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1); | ||
949 | audio3 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_3); | ||
950 | |||
951 | /* set master/slave audio interface */ | ||
952 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
953 | case SND_SOC_DAIFMT_CBS_CFS: | ||
954 | audio3 &= ~WM8400_AIF_MSTR1; | ||
955 | break; | ||
956 | case SND_SOC_DAIFMT_CBM_CFM: | ||
957 | audio3 |= WM8400_AIF_MSTR1; | ||
958 | break; | ||
959 | default: | ||
960 | return -EINVAL; | ||
961 | } | ||
962 | |||
963 | audio1 &= ~WM8400_AIF_FMT_MASK; | ||
964 | |||
965 | /* interface format */ | ||
966 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
967 | case SND_SOC_DAIFMT_I2S: | ||
968 | audio1 |= WM8400_AIF_FMT_I2S; | ||
969 | audio1 &= ~WM8400_AIF_LRCLK_INV; | ||
970 | break; | ||
971 | case SND_SOC_DAIFMT_RIGHT_J: | ||
972 | audio1 |= WM8400_AIF_FMT_RIGHTJ; | ||
973 | audio1 &= ~WM8400_AIF_LRCLK_INV; | ||
974 | break; | ||
975 | case SND_SOC_DAIFMT_LEFT_J: | ||
976 | audio1 |= WM8400_AIF_FMT_LEFTJ; | ||
977 | audio1 &= ~WM8400_AIF_LRCLK_INV; | ||
978 | break; | ||
979 | case SND_SOC_DAIFMT_DSP_A: | ||
980 | audio1 |= WM8400_AIF_FMT_DSP; | ||
981 | audio1 &= ~WM8400_AIF_LRCLK_INV; | ||
982 | break; | ||
983 | case SND_SOC_DAIFMT_DSP_B: | ||
984 | audio1 |= WM8400_AIF_FMT_DSP | WM8400_AIF_LRCLK_INV; | ||
985 | break; | ||
986 | default: | ||
987 | return -EINVAL; | ||
988 | } | ||
989 | |||
990 | wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); | ||
991 | wm8400_write(codec, WM8400_AUDIO_INTERFACE_3, audio3); | ||
992 | return 0; | ||
993 | } | ||
994 | |||
995 | static int wm8400_set_dai_clkdiv(struct snd_soc_dai *codec_dai, | ||
996 | int div_id, int div) | ||
997 | { | ||
998 | struct snd_soc_codec *codec = codec_dai->codec; | ||
999 | u16 reg; | ||
1000 | |||
1001 | switch (div_id) { | ||
1002 | case WM8400_MCLK_DIV: | ||
1003 | reg = wm8400_read(codec, WM8400_CLOCKING_2) & | ||
1004 | ~WM8400_MCLK_DIV_MASK; | ||
1005 | wm8400_write(codec, WM8400_CLOCKING_2, reg | div); | ||
1006 | break; | ||
1007 | case WM8400_DACCLK_DIV: | ||
1008 | reg = wm8400_read(codec, WM8400_CLOCKING_2) & | ||
1009 | ~WM8400_DAC_CLKDIV_MASK; | ||
1010 | wm8400_write(codec, WM8400_CLOCKING_2, reg | div); | ||
1011 | break; | ||
1012 | case WM8400_ADCCLK_DIV: | ||
1013 | reg = wm8400_read(codec, WM8400_CLOCKING_2) & | ||
1014 | ~WM8400_ADC_CLKDIV_MASK; | ||
1015 | wm8400_write(codec, WM8400_CLOCKING_2, reg | div); | ||
1016 | break; | ||
1017 | case WM8400_BCLK_DIV: | ||
1018 | reg = wm8400_read(codec, WM8400_CLOCKING_1) & | ||
1019 | ~WM8400_BCLK_DIV_MASK; | ||
1020 | wm8400_write(codec, WM8400_CLOCKING_1, reg | div); | ||
1021 | break; | ||
1022 | default: | ||
1023 | return -EINVAL; | ||
1024 | } | ||
1025 | |||
1026 | return 0; | ||
1027 | } | ||
1028 | |||
1029 | /* | ||
1030 | * Set PCM DAI bit size and sample rate. | ||
1031 | */ | ||
1032 | static int wm8400_hw_params(struct snd_pcm_substream *substream, | ||
1033 | struct snd_pcm_hw_params *params, | ||
1034 | struct snd_soc_dai *dai) | ||
1035 | { | ||
1036 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1037 | struct snd_soc_device *socdev = rtd->socdev; | ||
1038 | struct snd_soc_codec *codec = socdev->card->codec; | ||
1039 | u16 audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1); | ||
1040 | |||
1041 | audio1 &= ~WM8400_AIF_WL_MASK; | ||
1042 | /* bit size */ | ||
1043 | switch (params_format(params)) { | ||
1044 | case SNDRV_PCM_FORMAT_S16_LE: | ||
1045 | break; | ||
1046 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
1047 | audio1 |= WM8400_AIF_WL_20BITS; | ||
1048 | break; | ||
1049 | case SNDRV_PCM_FORMAT_S24_LE: | ||
1050 | audio1 |= WM8400_AIF_WL_24BITS; | ||
1051 | break; | ||
1052 | case SNDRV_PCM_FORMAT_S32_LE: | ||
1053 | audio1 |= WM8400_AIF_WL_32BITS; | ||
1054 | break; | ||
1055 | } | ||
1056 | |||
1057 | wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); | ||
1058 | return 0; | ||
1059 | } | ||
1060 | |||
1061 | static int wm8400_mute(struct snd_soc_dai *dai, int mute) | ||
1062 | { | ||
1063 | struct snd_soc_codec *codec = dai->codec; | ||
1064 | u16 val = wm8400_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE; | ||
1065 | |||
1066 | if (mute) | ||
1067 | wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); | ||
1068 | else | ||
1069 | wm8400_write(codec, WM8400_DAC_CTRL, val); | ||
1070 | |||
1071 | return 0; | ||
1072 | } | ||
1073 | |||
1074 | /* TODO: set bias for best performance at standby */ | ||
1075 | static int wm8400_set_bias_level(struct snd_soc_codec *codec, | ||
1076 | enum snd_soc_bias_level level) | ||
1077 | { | ||
1078 | struct wm8400_priv *wm8400 = codec->private_data; | ||
1079 | u16 val; | ||
1080 | int ret; | ||
1081 | |||
1082 | switch (level) { | ||
1083 | case SND_SOC_BIAS_ON: | ||
1084 | break; | ||
1085 | |||
1086 | case SND_SOC_BIAS_PREPARE: | ||
1087 | /* VMID=2*50k */ | ||
1088 | val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) & | ||
1089 | ~WM8400_VMID_MODE_MASK; | ||
1090 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2); | ||
1091 | break; | ||
1092 | |||
1093 | case SND_SOC_BIAS_STANDBY: | ||
1094 | if (codec->bias_level == SND_SOC_BIAS_OFF) { | ||
1095 | ret = regulator_bulk_enable(ARRAY_SIZE(power), | ||
1096 | &power[0]); | ||
1097 | if (ret != 0) { | ||
1098 | dev_err(wm8400->wm8400->dev, | ||
1099 | "Failed to enable regulators: %d\n", | ||
1100 | ret); | ||
1101 | return ret; | ||
1102 | } | ||
1103 | |||
1104 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, | ||
1105 | WM8400_CODEC_ENA | WM8400_SYSCLK_ENA); | ||
1106 | |||
1107 | /* Enable all output discharge bits */ | ||
1108 | wm8400_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE | | ||
1109 | WM8400_DIS_RLINE | WM8400_DIS_OUT3 | | ||
1110 | WM8400_DIS_OUT4 | WM8400_DIS_LOUT | | ||
1111 | WM8400_DIS_ROUT); | ||
1112 | |||
1113 | /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */ | ||
1114 | wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | | ||
1115 | WM8400_BUFDCOPEN | WM8400_POBCTRL); | ||
1116 | |||
1117 | msleep(500); | ||
1118 | |||
1119 | /* Enable outputs */ | ||
1120 | val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); | ||
1121 | val |= WM8400_SPK_ENA | WM8400_OUT3_ENA | | ||
1122 | WM8400_OUT4_ENA | WM8400_LOUT_ENA | | ||
1123 | WM8400_ROUT_ENA; | ||
1124 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); | ||
1125 | |||
1126 | /* disable all output discharge bits */ | ||
1127 | wm8400_write(codec, WM8400_ANTIPOP1, 0); | ||
1128 | |||
1129 | /* Enable VREF & VMID at 2x50k */ | ||
1130 | val |= 0x2 | WM8400_VREF_ENA; | ||
1131 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); | ||
1132 | |||
1133 | msleep(600); | ||
1134 | |||
1135 | /* Enable BUFIOEN */ | ||
1136 | wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | | ||
1137 | WM8400_BUFDCOPEN | WM8400_POBCTRL | | ||
1138 | WM8400_BUFIOEN); | ||
1139 | |||
1140 | /* Disable outputs */ | ||
1141 | val &= ~(WM8400_SPK_ENA | WM8400_OUT3_ENA | | ||
1142 | WM8400_OUT4_ENA | WM8400_LOUT_ENA | | ||
1143 | WM8400_ROUT_ENA); | ||
1144 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); | ||
1145 | |||
1146 | /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ | ||
1147 | wm8400_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN); | ||
1148 | } | ||
1149 | |||
1150 | /* VMID=2*300k */ | ||
1151 | val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) & | ||
1152 | ~WM8400_VMID_MODE_MASK; | ||
1153 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4); | ||
1154 | break; | ||
1155 | |||
1156 | case SND_SOC_BIAS_OFF: | ||
1157 | /* Enable POBCTRL and SOFT_ST */ | ||
1158 | wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | | ||
1159 | WM8400_POBCTRL | WM8400_BUFIOEN); | ||
1160 | |||
1161 | /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */ | ||
1162 | wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | | ||
1163 | WM8400_BUFDCOPEN | WM8400_POBCTRL | | ||
1164 | WM8400_BUFIOEN); | ||
1165 | |||
1166 | /* mute DAC */ | ||
1167 | val = wm8400_read(codec, WM8400_DAC_CTRL); | ||
1168 | wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); | ||
1169 | |||
1170 | /* Enable any disabled outputs */ | ||
1171 | val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); | ||
1172 | val |= WM8400_SPK_ENA | WM8400_OUT3_ENA | | ||
1173 | WM8400_OUT4_ENA | WM8400_LOUT_ENA | | ||
1174 | WM8400_ROUT_ENA; | ||
1175 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); | ||
1176 | |||
1177 | /* Disable VMID */ | ||
1178 | val &= ~WM8400_VMID_MODE_MASK; | ||
1179 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); | ||
1180 | |||
1181 | msleep(300); | ||
1182 | |||
1183 | /* Enable all output discharge bits */ | ||
1184 | wm8400_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE | | ||
1185 | WM8400_DIS_RLINE | WM8400_DIS_OUT3 | | ||
1186 | WM8400_DIS_OUT4 | WM8400_DIS_LOUT | | ||
1187 | WM8400_DIS_ROUT); | ||
1188 | |||
1189 | /* Disable VREF */ | ||
1190 | val &= ~WM8400_VREF_ENA; | ||
1191 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); | ||
1192 | |||
1193 | /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ | ||
1194 | wm8400_write(codec, WM8400_ANTIPOP2, 0x0); | ||
1195 | |||
1196 | ret = regulator_bulk_disable(ARRAY_SIZE(power), | ||
1197 | &power[0]); | ||
1198 | if (ret != 0) | ||
1199 | return ret; | ||
1200 | |||
1201 | break; | ||
1202 | } | ||
1203 | |||
1204 | codec->bias_level = level; | ||
1205 | return 0; | ||
1206 | } | ||
1207 | |||
1208 | #define WM8400_RATES SNDRV_PCM_RATE_8000_96000 | ||
1209 | |||
1210 | #define WM8400_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
1211 | SNDRV_PCM_FMTBIT_S24_LE) | ||
1212 | |||
1213 | static struct snd_soc_dai_ops wm8400_dai_ops = { | ||
1214 | .hw_params = wm8400_hw_params, | ||
1215 | .digital_mute = wm8400_mute, | ||
1216 | .set_fmt = wm8400_set_dai_fmt, | ||
1217 | .set_clkdiv = wm8400_set_dai_clkdiv, | ||
1218 | .set_sysclk = wm8400_set_dai_sysclk, | ||
1219 | }; | ||
1220 | |||
1221 | /* | ||
1222 | * The WM8400 supports 2 different and mutually exclusive DAI | ||
1223 | * configurations. | ||
1224 | * | ||
1225 | * 1. ADC/DAC on Primary Interface | ||
1226 | * 2. ADC on Primary Interface/DAC on secondary | ||
1227 | */ | ||
1228 | struct snd_soc_dai wm8400_dai = { | ||
1229 | /* ADC/DAC on primary */ | ||
1230 | .name = "WM8400 ADC/DAC Primary", | ||
1231 | .id = 1, | ||
1232 | .playback = { | ||
1233 | .stream_name = "Playback", | ||
1234 | .channels_min = 1, | ||
1235 | .channels_max = 2, | ||
1236 | .rates = WM8400_RATES, | ||
1237 | .formats = WM8400_FORMATS, | ||
1238 | }, | ||
1239 | .capture = { | ||
1240 | .stream_name = "Capture", | ||
1241 | .channels_min = 1, | ||
1242 | .channels_max = 2, | ||
1243 | .rates = WM8400_RATES, | ||
1244 | .formats = WM8400_FORMATS, | ||
1245 | }, | ||
1246 | .ops = &wm8400_dai_ops, | ||
1247 | }; | ||
1248 | EXPORT_SYMBOL_GPL(wm8400_dai); | ||
1249 | |||
1250 | static int wm8400_suspend(struct platform_device *pdev, pm_message_t state) | ||
1251 | { | ||
1252 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1253 | struct snd_soc_codec *codec = socdev->card->codec; | ||
1254 | |||
1255 | wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1256 | |||
1257 | return 0; | ||
1258 | } | ||
1259 | |||
1260 | static int wm8400_resume(struct platform_device *pdev) | ||
1261 | { | ||
1262 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1263 | struct snd_soc_codec *codec = socdev->card->codec; | ||
1264 | |||
1265 | wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
1266 | |||
1267 | return 0; | ||
1268 | } | ||
1269 | |||
1270 | static struct snd_soc_codec *wm8400_codec; | ||
1271 | |||
1272 | static int wm8400_probe(struct platform_device *pdev) | ||
1273 | { | ||
1274 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1275 | struct snd_soc_codec *codec; | ||
1276 | int ret; | ||
1277 | |||
1278 | if (!wm8400_codec) { | ||
1279 | dev_err(&pdev->dev, "wm8400 not yet discovered\n"); | ||
1280 | return -ENODEV; | ||
1281 | } | ||
1282 | codec = wm8400_codec; | ||
1283 | |||
1284 | socdev->card->codec = codec; | ||
1285 | |||
1286 | /* register pcms */ | ||
1287 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
1288 | if (ret < 0) { | ||
1289 | dev_err(&pdev->dev, "failed to create pcms\n"); | ||
1290 | goto pcm_err; | ||
1291 | } | ||
1292 | |||
1293 | wm8400_add_controls(codec); | ||
1294 | wm8400_add_widgets(codec); | ||
1295 | |||
1296 | ret = snd_soc_init_card(socdev); | ||
1297 | if (ret < 0) { | ||
1298 | dev_err(&pdev->dev, "failed to register card\n"); | ||
1299 | goto card_err; | ||
1300 | } | ||
1301 | |||
1302 | return ret; | ||
1303 | |||
1304 | card_err: | ||
1305 | snd_soc_free_pcms(socdev); | ||
1306 | snd_soc_dapm_free(socdev); | ||
1307 | pcm_err: | ||
1308 | return ret; | ||
1309 | } | ||
1310 | |||
1311 | /* power down chip */ | ||
1312 | static int wm8400_remove(struct platform_device *pdev) | ||
1313 | { | ||
1314 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1315 | |||
1316 | snd_soc_free_pcms(socdev); | ||
1317 | snd_soc_dapm_free(socdev); | ||
1318 | |||
1319 | return 0; | ||
1320 | } | ||
1321 | |||
1322 | struct snd_soc_codec_device soc_codec_dev_wm8400 = { | ||
1323 | .probe = wm8400_probe, | ||
1324 | .remove = wm8400_remove, | ||
1325 | .suspend = wm8400_suspend, | ||
1326 | .resume = wm8400_resume, | ||
1327 | }; | ||
1328 | |||
1329 | static void wm8400_probe_deferred(struct work_struct *work) | ||
1330 | { | ||
1331 | struct wm8400_priv *priv = container_of(work, struct wm8400_priv, | ||
1332 | work); | ||
1333 | struct snd_soc_codec *codec = &priv->codec; | ||
1334 | int ret; | ||
1335 | |||
1336 | /* charge output caps */ | ||
1337 | wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
1338 | |||
1339 | /* We're done, tell the subsystem. */ | ||
1340 | ret = snd_soc_register_codec(codec); | ||
1341 | if (ret != 0) { | ||
1342 | dev_err(priv->wm8400->dev, | ||
1343 | "Failed to register codec: %d\n", ret); | ||
1344 | goto err; | ||
1345 | } | ||
1346 | |||
1347 | ret = snd_soc_register_dai(&wm8400_dai); | ||
1348 | if (ret != 0) { | ||
1349 | dev_err(priv->wm8400->dev, | ||
1350 | "Failed to register DAI: %d\n", ret); | ||
1351 | goto err_codec; | ||
1352 | } | ||
1353 | |||
1354 | return; | ||
1355 | |||
1356 | err_codec: | ||
1357 | snd_soc_unregister_codec(codec); | ||
1358 | err: | ||
1359 | wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1360 | } | ||
1361 | |||
1362 | static int wm8400_codec_probe(struct platform_device *dev) | ||
1363 | { | ||
1364 | struct wm8400_priv *priv; | ||
1365 | int ret; | ||
1366 | u16 reg; | ||
1367 | struct snd_soc_codec *codec; | ||
1368 | |||
1369 | priv = kzalloc(sizeof(struct wm8400_priv), GFP_KERNEL); | ||
1370 | if (priv == NULL) | ||
1371 | return -ENOMEM; | ||
1372 | |||
1373 | codec = &priv->codec; | ||
1374 | codec->private_data = priv; | ||
1375 | codec->control_data = dev->dev.driver_data; | ||
1376 | priv->wm8400 = dev->dev.driver_data; | ||
1377 | |||
1378 | ret = regulator_bulk_get(priv->wm8400->dev, | ||
1379 | ARRAY_SIZE(power), &power[0]); | ||
1380 | if (ret != 0) { | ||
1381 | dev_err(&dev->dev, "Failed to get regulators: %d\n", ret); | ||
1382 | goto err; | ||
1383 | } | ||
1384 | |||
1385 | codec->dev = &dev->dev; | ||
1386 | wm8400_dai.dev = &dev->dev; | ||
1387 | |||
1388 | codec->name = "WM8400"; | ||
1389 | codec->owner = THIS_MODULE; | ||
1390 | codec->read = wm8400_read; | ||
1391 | codec->write = wm8400_write; | ||
1392 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
1393 | codec->set_bias_level = wm8400_set_bias_level; | ||
1394 | codec->dai = &wm8400_dai; | ||
1395 | codec->num_dai = 1; | ||
1396 | codec->reg_cache_size = WM8400_REGISTER_COUNT; | ||
1397 | mutex_init(&codec->mutex); | ||
1398 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
1399 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
1400 | INIT_WORK(&priv->work, wm8400_probe_deferred); | ||
1401 | |||
1402 | wm8400_codec_reset(codec); | ||
1403 | |||
1404 | reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); | ||
1405 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA); | ||
1406 | |||
1407 | /* Latch volume update bits */ | ||
1408 | reg = wm8400_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME); | ||
1409 | wm8400_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME, | ||
1410 | reg & WM8400_IPVU); | ||
1411 | reg = wm8400_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME); | ||
1412 | wm8400_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, | ||
1413 | reg & WM8400_IPVU); | ||
1414 | |||
1415 | wm8400_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); | ||
1416 | wm8400_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); | ||
1417 | |||
1418 | wm8400_codec = codec; | ||
1419 | |||
1420 | if (!schedule_work(&priv->work)) { | ||
1421 | ret = -EINVAL; | ||
1422 | goto err_regulator; | ||
1423 | } | ||
1424 | |||
1425 | return 0; | ||
1426 | |||
1427 | err_regulator: | ||
1428 | wm8400_codec = NULL; | ||
1429 | regulator_bulk_free(ARRAY_SIZE(power), power); | ||
1430 | err: | ||
1431 | kfree(priv); | ||
1432 | return ret; | ||
1433 | } | ||
1434 | |||
1435 | static int __exit wm8400_codec_remove(struct platform_device *dev) | ||
1436 | { | ||
1437 | struct wm8400_priv *priv = wm8400_codec->private_data; | ||
1438 | u16 reg; | ||
1439 | |||
1440 | snd_soc_unregister_dai(&wm8400_dai); | ||
1441 | snd_soc_unregister_codec(wm8400_codec); | ||
1442 | |||
1443 | reg = wm8400_read(wm8400_codec, WM8400_POWER_MANAGEMENT_1); | ||
1444 | wm8400_write(wm8400_codec, WM8400_POWER_MANAGEMENT_1, | ||
1445 | reg & (~WM8400_CODEC_ENA)); | ||
1446 | |||
1447 | regulator_bulk_free(ARRAY_SIZE(power), power); | ||
1448 | kfree(priv); | ||
1449 | |||
1450 | wm8400_codec = NULL; | ||
1451 | |||
1452 | return 0; | ||
1453 | } | ||
1454 | |||
1455 | static struct platform_driver wm8400_codec_driver = { | ||
1456 | .driver = { | ||
1457 | .name = "wm8400-codec", | ||
1458 | .owner = THIS_MODULE, | ||
1459 | }, | ||
1460 | .probe = wm8400_codec_probe, | ||
1461 | .remove = __exit_p(wm8400_codec_remove), | ||
1462 | }; | ||
1463 | |||
1464 | static int __init wm8400_codec_init(void) | ||
1465 | { | ||
1466 | return platform_driver_register(&wm8400_codec_driver); | ||
1467 | } | ||
1468 | module_init(wm8400_codec_init); | ||
1469 | |||
1470 | static void __exit wm8400_codec_exit(void) | ||
1471 | { | ||
1472 | platform_driver_unregister(&wm8400_codec_driver); | ||
1473 | } | ||
1474 | module_exit(wm8400_codec_exit); | ||
1475 | |||
1476 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8400); | ||
1477 | |||
1478 | MODULE_DESCRIPTION("ASoC WM8400 driver"); | ||
1479 | MODULE_AUTHOR("Mark Brown"); | ||
1480 | MODULE_LICENSE("GPL"); | ||
1481 | MODULE_ALIAS("platform:wm8400-codec"); | ||
diff --git a/sound/soc/codecs/wm8400.h b/sound/soc/codecs/wm8400.h new file mode 100644 index 000000000000..79c5934d4776 --- /dev/null +++ b/sound/soc/codecs/wm8400.h | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * wm8400.h -- audio driver for WM8400 | ||
3 | * | ||
4 | * Copyright 2008 Wolfson Microelectronics PLC. | ||
5 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.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 | #ifndef _WM8400_CODEC_H | ||
15 | #define _WM8400_CODEC_H | ||
16 | |||
17 | #define WM8400_MCLK_DIV 0 | ||
18 | #define WM8400_DACCLK_DIV 1 | ||
19 | #define WM8400_ADCCLK_DIV 2 | ||
20 | #define WM8400_BCLK_DIV 3 | ||
21 | |||
22 | #define WM8400_MCLK_DIV_1 0x400 | ||
23 | #define WM8400_MCLK_DIV_2 0x800 | ||
24 | |||
25 | #define WM8400_DAC_CLKDIV_1 0x00 | ||
26 | #define WM8400_DAC_CLKDIV_1_5 0x04 | ||
27 | #define WM8400_DAC_CLKDIV_2 0x08 | ||
28 | #define WM8400_DAC_CLKDIV_3 0x0c | ||
29 | #define WM8400_DAC_CLKDIV_4 0x10 | ||
30 | #define WM8400_DAC_CLKDIV_5_5 0x14 | ||
31 | #define WM8400_DAC_CLKDIV_6 0x18 | ||
32 | |||
33 | #define WM8400_ADC_CLKDIV_1 0x00 | ||
34 | #define WM8400_ADC_CLKDIV_1_5 0x20 | ||
35 | #define WM8400_ADC_CLKDIV_2 0x40 | ||
36 | #define WM8400_ADC_CLKDIV_3 0x60 | ||
37 | #define WM8400_ADC_CLKDIV_4 0x80 | ||
38 | #define WM8400_ADC_CLKDIV_5_5 0xa0 | ||
39 | #define WM8400_ADC_CLKDIV_6 0xc0 | ||
40 | |||
41 | |||
42 | #define WM8400_BCLK_DIV_1 (0x0 << 1) | ||
43 | #define WM8400_BCLK_DIV_1_5 (0x1 << 1) | ||
44 | #define WM8400_BCLK_DIV_2 (0x2 << 1) | ||
45 | #define WM8400_BCLK_DIV_3 (0x3 << 1) | ||
46 | #define WM8400_BCLK_DIV_4 (0x4 << 1) | ||
47 | #define WM8400_BCLK_DIV_5_5 (0x5 << 1) | ||
48 | #define WM8400_BCLK_DIV_6 (0x6 << 1) | ||
49 | #define WM8400_BCLK_DIV_8 (0x7 << 1) | ||
50 | #define WM8400_BCLK_DIV_11 (0x8 << 1) | ||
51 | #define WM8400_BCLK_DIV_12 (0x9 << 1) | ||
52 | #define WM8400_BCLK_DIV_16 (0xA << 1) | ||
53 | #define WM8400_BCLK_DIV_22 (0xB << 1) | ||
54 | #define WM8400_BCLK_DIV_24 (0xC << 1) | ||
55 | #define WM8400_BCLK_DIV_32 (0xD << 1) | ||
56 | #define WM8400_BCLK_DIV_44 (0xE << 1) | ||
57 | #define WM8400_BCLK_DIV_48 (0xF << 1) | ||
58 | |||
59 | extern struct snd_soc_dai wm8400_dai; | ||
60 | extern struct snd_soc_codec_device soc_codec_dev_wm8400; | ||
61 | |||
62 | #endif | ||
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index cc975a62fa5c..6a4cea09c45d 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c | |||
@@ -336,7 +336,7 @@ static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, | |||
336 | return 0; | 336 | return 0; |
337 | } | 337 | } |
338 | 338 | ||
339 | pll_factors(freq_out*8, freq_in); | 339 | pll_factors(freq_out*4, freq_in); |
340 | 340 | ||
341 | wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n); | 341 | wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n); |
342 | wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18); | 342 | wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18); |
@@ -367,7 +367,7 @@ static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai, | |||
367 | wm8510_write(codec, WM8510_GPIO, reg | div); | 367 | wm8510_write(codec, WM8510_GPIO, reg | div); |
368 | break; | 368 | break; |
369 | case WM8510_MCLKDIV: | 369 | case WM8510_MCLKDIV: |
370 | reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1f; | 370 | reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x11f; |
371 | wm8510_write(codec, WM8510_CLOCK, reg | div); | 371 | wm8510_write(codec, WM8510_CLOCK, reg | div); |
372 | break; | 372 | break; |
373 | case WM8510_ADCCLK: | 373 | case WM8510_ADCCLK: |
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index ee0af23a1acc..27f9e231bf69 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c | |||
@@ -35,8 +35,6 @@ | |||
35 | 35 | ||
36 | #include "wm8580.h" | 36 | #include "wm8580.h" |
37 | 37 | ||
38 | #define WM8580_VERSION "0.1" | ||
39 | |||
40 | struct pll_state { | 38 | struct pll_state { |
41 | unsigned int in; | 39 | unsigned int in; |
42 | unsigned int out; | 40 | unsigned int out; |
@@ -976,8 +974,6 @@ static int wm8580_probe(struct platform_device *pdev) | |||
976 | struct wm8580_priv *wm8580; | 974 | struct wm8580_priv *wm8580; |
977 | int ret = 0; | 975 | int ret = 0; |
978 | 976 | ||
979 | pr_info("WM8580 Audio Codec %s\n", WM8580_VERSION); | ||
980 | |||
981 | setup = socdev->codec_data; | 977 | setup = socdev->codec_data; |
982 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | 978 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); |
983 | if (codec == NULL) | 979 | if (codec == NULL) |
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index cc6e57f9acf8..a6e8f3f7f052 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c | |||
@@ -51,11 +51,6 @@ | |||
51 | 51 | ||
52 | #include "wm8753.h" | 52 | #include "wm8753.h" |
53 | 53 | ||
54 | #ifdef CONFIG_SPI_MASTER | ||
55 | static struct spi_driver wm8753_spi_driver; | ||
56 | static int wm8753_spi_write(struct spi_device *spi, const char *data, int len); | ||
57 | #endif | ||
58 | |||
59 | static int caps_charge = 2000; | 54 | static int caps_charge = 2000; |
60 | module_param(caps_charge, int, 0); | 55 | module_param(caps_charge, int, 0); |
61 | MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)"); | 56 | MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)"); |
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index b502741692d6..bd7392c9657e 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig | |||
@@ -20,7 +20,7 @@ config SND_DAVINCI_SOC_EVM | |||
20 | 20 | ||
21 | config SND_DAVINCI_SOC_SFFSDR | 21 | config SND_DAVINCI_SOC_SFFSDR |
22 | tristate "SoC Audio support for SFFSDR" | 22 | tristate "SoC Audio support for SFFSDR" |
23 | depends on SND_DAVINCI_SOC && MACH_DAVINCI_SFFSDR | 23 | depends on SND_DAVINCI_SOC && MACH_SFFSDR |
24 | select SND_DAVINCI_SOC_I2S | 24 | select SND_DAVINCI_SOC_I2S |
25 | select SND_SOC_PCM3008 | 25 | select SND_SOC_PCM3008 |
26 | select SFFSDR_FPGA | 26 | select SFFSDR_FPGA |
diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c index 0bf81abba8c7..40eccfe9e358 100644 --- a/sound/soc/davinci/davinci-sffsdr.c +++ b/sound/soc/davinci/davinci-sffsdr.c | |||
@@ -36,9 +36,16 @@ | |||
36 | #include "davinci-pcm.h" | 36 | #include "davinci-pcm.h" |
37 | #include "davinci-i2s.h" | 37 | #include "davinci-i2s.h" |
38 | 38 | ||
39 | /* | ||
40 | * CLKX and CLKR are the inputs for the Sample Rate Generator. | ||
41 | * FSX and FSR are outputs, driven by the sample Rate Generator. | ||
42 | */ | ||
43 | #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \ | ||
44 | SND_SOC_DAIFMT_CBM_CFS | \ | ||
45 | SND_SOC_DAIFMT_IB_NF) | ||
46 | |||
39 | static int sffsdr_hw_params(struct snd_pcm_substream *substream, | 47 | static int sffsdr_hw_params(struct snd_pcm_substream *substream, |
40 | struct snd_pcm_hw_params *params, | 48 | struct snd_pcm_hw_params *params) |
41 | struct snd_soc_dai *dai) | ||
42 | { | 49 | { |
43 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 50 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
44 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 51 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
@@ -56,13 +63,8 @@ static int sffsdr_hw_params(struct snd_pcm_substream *substream, | |||
56 | } | 63 | } |
57 | #endif | 64 | #endif |
58 | 65 | ||
59 | /* Set cpu DAI configuration: | 66 | /* set cpu DAI configuration */ |
60 | * CLKX and CLKR are the inputs for the Sample Rate Generator. | 67 | ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT); |
61 | * FSX and FSR are outputs, driven by the sample Rate Generator. */ | ||
62 | ret = snd_soc_dai_set_fmt(cpu_dai, | ||
63 | SND_SOC_DAIFMT_RIGHT_J | | ||
64 | SND_SOC_DAIFMT_CBM_CFS | | ||
65 | SND_SOC_DAIFMT_IB_NF); | ||
66 | if (ret < 0) | 68 | if (ret < 0) |
67 | return ret; | 69 | return ret; |
68 | 70 | ||
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 58a3fa497503..b3eb8570cd7b 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c | |||
@@ -142,7 +142,8 @@ static const struct snd_pcm_hardware fsl_dma_hardware = { | |||
142 | .info = SNDRV_PCM_INFO_INTERLEAVED | | 142 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
143 | SNDRV_PCM_INFO_MMAP | | 143 | SNDRV_PCM_INFO_MMAP | |
144 | SNDRV_PCM_INFO_MMAP_VALID | | 144 | SNDRV_PCM_INFO_MMAP_VALID | |
145 | SNDRV_PCM_INFO_JOINT_DUPLEX, | 145 | SNDRV_PCM_INFO_JOINT_DUPLEX | |
146 | SNDRV_PCM_INFO_PAUSE, | ||
146 | .formats = FSLDMA_PCM_FORMATS, | 147 | .formats = FSLDMA_PCM_FORMATS, |
147 | .rates = FSLDMA_PCM_RATES, | 148 | .rates = FSLDMA_PCM_RATES, |
148 | .rate_min = 5512, | 149 | .rate_min = 5512, |
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 0fddd437a7c9..169bca295b78 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c | |||
@@ -72,6 +72,7 @@ | |||
72 | * @dev: struct device pointer | 72 | * @dev: struct device pointer |
73 | * @playback: the number of playback streams opened | 73 | * @playback: the number of playback streams opened |
74 | * @capture: the number of capture streams opened | 74 | * @capture: the number of capture streams opened |
75 | * @asynchronous: 0=synchronous mode, 1=asynchronous mode | ||
75 | * @cpu_dai: the CPU DAI for this device | 76 | * @cpu_dai: the CPU DAI for this device |
76 | * @dev_attr: the sysfs device attribute structure | 77 | * @dev_attr: the sysfs device attribute structure |
77 | * @stats: SSI statistics | 78 | * @stats: SSI statistics |
@@ -86,6 +87,7 @@ struct fsl_ssi_private { | |||
86 | struct device *dev; | 87 | struct device *dev; |
87 | unsigned int playback; | 88 | unsigned int playback; |
88 | unsigned int capture; | 89 | unsigned int capture; |
90 | int asynchronous; | ||
89 | struct snd_soc_dai cpu_dai; | 91 | struct snd_soc_dai cpu_dai; |
90 | struct device_attribute dev_attr; | 92 | struct device_attribute dev_attr; |
91 | 93 | ||
@@ -301,9 +303,10 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
301 | * | 303 | * |
302 | * FIXME: Little-endian samples require a different shift dir | 304 | * FIXME: Little-endian samples require a different shift dir |
303 | */ | 305 | */ |
304 | clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK, | 306 | clrsetbits_be32(&ssi->scr, |
305 | CCSR_SSI_SCR_TFR_CLK_DIS | | 307 | CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, |
306 | CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN); | 308 | CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE |
309 | | (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN)); | ||
307 | 310 | ||
308 | out_be32(&ssi->stcr, | 311 | out_be32(&ssi->stcr, |
309 | CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | | 312 | CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | |
@@ -382,10 +385,15 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
382 | SNDRV_PCM_HW_PARAM_RATE, | 385 | SNDRV_PCM_HW_PARAM_RATE, |
383 | first_runtime->rate, first_runtime->rate); | 386 | first_runtime->rate, first_runtime->rate); |
384 | 387 | ||
385 | snd_pcm_hw_constraint_minmax(substream->runtime, | 388 | /* If we're in synchronous mode, then we need to constrain |
386 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | 389 | * the sample size as well. We don't support independent sample |
387 | first_runtime->sample_bits, | 390 | * rates in asynchronous mode. |
388 | first_runtime->sample_bits); | 391 | */ |
392 | if (!ssi_private->asynchronous) | ||
393 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
394 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | ||
395 | first_runtime->sample_bits, | ||
396 | first_runtime->sample_bits); | ||
389 | 397 | ||
390 | ssi_private->second_stream = substream; | 398 | ssi_private->second_stream = substream; |
391 | } | 399 | } |
@@ -421,13 +429,18 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, | |||
421 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | 429 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
422 | unsigned int sample_size = | 430 | unsigned int sample_size = |
423 | snd_pcm_format_width(params_format(hw_params)); | 431 | snd_pcm_format_width(params_format(hw_params)); |
424 | u32 wl; | 432 | u32 wl = CCSR_SSI_SxCCR_WL(sample_size); |
425 | 433 | ||
426 | /* The SSI should always be disabled at this points (SSIEN=0) */ | 434 | /* The SSI should always be disabled at this points (SSIEN=0) */ |
427 | wl = CCSR_SSI_SxCCR_WL(sample_size); | ||
428 | 435 | ||
429 | /* In synchronous mode, the SSI uses STCCR for capture */ | 436 | /* In synchronous mode, the SSI uses STCCR for capture */ |
430 | clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); | 437 | if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) || |
438 | !ssi_private->asynchronous) | ||
439 | clrsetbits_be32(&ssi->stccr, | ||
440 | CCSR_SSI_SxCCR_WL_MASK, wl); | ||
441 | else | ||
442 | clrsetbits_be32(&ssi->srccr, | ||
443 | CCSR_SSI_SxCCR_WL_MASK, wl); | ||
431 | } | 444 | } |
432 | 445 | ||
433 | return 0; | 446 | return 0; |
@@ -451,28 +464,33 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, | |||
451 | 464 | ||
452 | switch (cmd) { | 465 | switch (cmd) { |
453 | case SNDRV_PCM_TRIGGER_START: | 466 | case SNDRV_PCM_TRIGGER_START: |
454 | case SNDRV_PCM_TRIGGER_RESUME: | 467 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); |
455 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 468 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
456 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 469 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
457 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | ||
458 | setbits32(&ssi->scr, | 470 | setbits32(&ssi->scr, |
459 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE); | 471 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE); |
460 | } else { | 472 | } else { |
461 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | 473 | long timeout = jiffies + 10; |
474 | |||
462 | setbits32(&ssi->scr, | 475 | setbits32(&ssi->scr, |
463 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE); | 476 | CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE); |
464 | 477 | ||
465 | /* | 478 | /* Wait until the SSI has filled its FIFO. Without this |
466 | * I think we need this delay to allow time for the SSI | 479 | * delay, ALSA complains about overruns. When the FIFO |
467 | * to put data into its FIFO. Without it, ALSA starts | 480 | * is full, the DMA controller initiates its first |
468 | * to complain about overruns. | 481 | * transfer. Until then, however, the DMA's DAR |
482 | * register is zero, which translates to an | ||
483 | * out-of-bounds pointer. This makes ALSA think an | ||
484 | * overrun has occurred. | ||
469 | */ | 485 | */ |
470 | mdelay(1); | 486 | while (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0) && |
487 | (jiffies < timeout)); | ||
488 | if (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0)) | ||
489 | return -EIO; | ||
471 | } | 490 | } |
472 | break; | 491 | break; |
473 | 492 | ||
474 | case SNDRV_PCM_TRIGGER_STOP: | 493 | case SNDRV_PCM_TRIGGER_STOP: |
475 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
476 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 494 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
477 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 495 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
478 | clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); | 496 | clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); |
@@ -655,6 +673,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) | |||
655 | ssi_private->ssi_phys = ssi_info->ssi_phys; | 673 | ssi_private->ssi_phys = ssi_info->ssi_phys; |
656 | ssi_private->irq = ssi_info->irq; | 674 | ssi_private->irq = ssi_info->irq; |
657 | ssi_private->dev = ssi_info->dev; | 675 | ssi_private->dev = ssi_info->dev; |
676 | ssi_private->asynchronous = ssi_info->asynchronous; | ||
658 | 677 | ||
659 | ssi_private->dev->driver_data = fsl_ssi_dai; | 678 | ssi_private->dev->driver_data = fsl_ssi_dai; |
660 | 679 | ||
@@ -705,6 +724,14 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai) | |||
705 | } | 724 | } |
706 | EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); | 725 | EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); |
707 | 726 | ||
727 | static int __init fsl_ssi_init(void) | ||
728 | { | ||
729 | printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); | ||
730 | |||
731 | return 0; | ||
732 | } | ||
733 | module_init(fsl_ssi_init); | ||
734 | |||
708 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 735 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
709 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); | 736 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); |
710 | MODULE_LICENSE("GPL"); | 737 | MODULE_LICENSE("GPL"); |
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h index 83b44d700e33..eade01feaab6 100644 --- a/sound/soc/fsl/fsl_ssi.h +++ b/sound/soc/fsl/fsl_ssi.h | |||
@@ -208,6 +208,7 @@ struct ccsr_ssi { | |||
208 | * ssi_phys: physical address of the SSI registers | 208 | * ssi_phys: physical address of the SSI registers |
209 | * irq: IRQ of this SSI | 209 | * irq: IRQ of this SSI |
210 | * dev: struct device, used to create the sysfs statistics file | 210 | * dev: struct device, used to create the sysfs statistics file |
211 | * asynchronous: 0=synchronous mode, 1=asynchronous mode | ||
211 | */ | 212 | */ |
212 | struct fsl_ssi_info { | 213 | struct fsl_ssi_info { |
213 | unsigned int id; | 214 | unsigned int id; |
@@ -215,6 +216,7 @@ struct fsl_ssi_info { | |||
215 | dma_addr_t ssi_phys; | 216 | dma_addr_t ssi_phys; |
216 | unsigned int irq; | 217 | unsigned int irq; |
217 | struct device *dev; | 218 | struct device *dev; |
219 | int asynchronous; | ||
218 | }; | 220 | }; |
219 | 221 | ||
220 | struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info); | 222 | struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info); |
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index acf39a646b2f..ef67d1cdffe7 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c | |||
@@ -353,6 +353,11 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
353 | } | 353 | } |
354 | ssi_info.irq = machine_data->ssi_irq; | 354 | ssi_info.irq = machine_data->ssi_irq; |
355 | 355 | ||
356 | /* Do we want to use asynchronous mode? */ | ||
357 | ssi_info.asynchronous = | ||
358 | of_find_property(np, "fsl,ssi-asynchronous", NULL) ? 1 : 0; | ||
359 | if (ssi_info.asynchronous) | ||
360 | dev_info(&ofdev->dev, "using asynchronous mode\n"); | ||
356 | 361 | ||
357 | /* Map the global utilities registers. */ | 362 | /* Map the global utilities registers. */ |
358 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); | 363 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); |
diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c index cd41a948df7b..a952a4eb3361 100644 --- a/sound/soc/omap/osk5912.c +++ b/sound/soc/omap/osk5912.c | |||
@@ -186,13 +186,6 @@ static int __init osk_soc_init(void) | |||
186 | return -ENODEV; | 186 | return -ENODEV; |
187 | } | 187 | } |
188 | 188 | ||
189 | if (clk_get_usecount(tlv320aic23_mclk) > 0) { | ||
190 | /* MCLK is already in use */ | ||
191 | printk(KERN_WARNING | ||
192 | "MCLK in use at %d Hz. We change it to %d Hz\n", | ||
193 | (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK); | ||
194 | } | ||
195 | |||
196 | /* | 189 | /* |
197 | * Configure 12 MHz output on MCLK. | 190 | * Configure 12 MHz output on MCLK. |
198 | */ | 191 | */ |
@@ -205,9 +198,8 @@ static int __init osk_soc_init(void) | |||
205 | } | 198 | } |
206 | } | 199 | } |
207 | 200 | ||
208 | printk(KERN_INFO "MCLK = %d [%d], usecount = %d\n", | 201 | printk(KERN_INFO "MCLK = %d [%d]\n", |
209 | (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK, | 202 | (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK); |
210 | clk_get_usecount(tlv320aic23_mclk)); | ||
211 | 203 | ||
212 | return 0; | 204 | return 0; |
213 | err1: | 205 | err1: |
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index e226fa75669c..715c648203a4 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <sound/pcm.h> | 28 | #include <sound/pcm.h> |
29 | #include <sound/soc.h> | 29 | #include <sound/soc.h> |
30 | #include <sound/soc-dapm.h> | 30 | #include <sound/soc-dapm.h> |
31 | #include <sound/jack.h> | ||
31 | 32 | ||
32 | #include <asm/mach-types.h> | 33 | #include <asm/mach-types.h> |
33 | #include <mach/hardware.h> | 34 | #include <mach/hardware.h> |
@@ -81,12 +82,97 @@ static struct snd_soc_ops sdp3430_ops = { | |||
81 | .hw_params = sdp3430_hw_params, | 82 | .hw_params = sdp3430_hw_params, |
82 | }; | 83 | }; |
83 | 84 | ||
85 | /* SDP3430 machine DAPM */ | ||
86 | static const struct snd_soc_dapm_widget sdp3430_twl4030_dapm_widgets[] = { | ||
87 | SND_SOC_DAPM_MIC("Ext Mic", NULL), | ||
88 | SND_SOC_DAPM_SPK("Ext Spk", NULL), | ||
89 | SND_SOC_DAPM_HP("Headset Jack", NULL), | ||
90 | }; | ||
91 | |||
92 | static const struct snd_soc_dapm_route audio_map[] = { | ||
93 | /* External Mics: MAINMIC, SUBMIC with bias*/ | ||
94 | {"MAINMIC", NULL, "Mic Bias 1"}, | ||
95 | {"SUBMIC", NULL, "Mic Bias 2"}, | ||
96 | {"Mic Bias 1", NULL, "Ext Mic"}, | ||
97 | {"Mic Bias 2", NULL, "Ext Mic"}, | ||
98 | |||
99 | /* External Speakers: HFL, HFR */ | ||
100 | {"Ext Spk", NULL, "HFL"}, | ||
101 | {"Ext Spk", NULL, "HFR"}, | ||
102 | |||
103 | /* Headset: HSMIC (with bias), HSOL, HSOR */ | ||
104 | {"Headset Jack", NULL, "HSOL"}, | ||
105 | {"Headset Jack", NULL, "HSOR"}, | ||
106 | {"HSMIC", NULL, "Headset Mic Bias"}, | ||
107 | {"Headset Mic Bias", NULL, "Headset Jack"}, | ||
108 | }; | ||
109 | |||
110 | static int sdp3430_twl4030_init(struct snd_soc_codec *codec) | ||
111 | { | ||
112 | int ret; | ||
113 | |||
114 | /* Add SDP3430 specific widgets */ | ||
115 | ret = snd_soc_dapm_new_controls(codec, sdp3430_twl4030_dapm_widgets, | ||
116 | ARRAY_SIZE(sdp3430_twl4030_dapm_widgets)); | ||
117 | if (ret) | ||
118 | return ret; | ||
119 | |||
120 | /* Set up SDP3430 specific audio path audio_map */ | ||
121 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
122 | |||
123 | /* SDP3430 connected pins */ | ||
124 | snd_soc_dapm_enable_pin(codec, "Ext Mic"); | ||
125 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); | ||
126 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); | ||
127 | |||
128 | /* TWL4030 not connected pins */ | ||
129 | snd_soc_dapm_nc_pin(codec, "AUXL"); | ||
130 | snd_soc_dapm_nc_pin(codec, "AUXR"); | ||
131 | snd_soc_dapm_nc_pin(codec, "CARKITMIC"); | ||
132 | snd_soc_dapm_nc_pin(codec, "DIGIMIC0"); | ||
133 | snd_soc_dapm_nc_pin(codec, "DIGIMIC1"); | ||
134 | |||
135 | snd_soc_dapm_nc_pin(codec, "OUTL"); | ||
136 | snd_soc_dapm_nc_pin(codec, "OUTR"); | ||
137 | snd_soc_dapm_nc_pin(codec, "EARPIECE"); | ||
138 | snd_soc_dapm_nc_pin(codec, "PREDRIVEL"); | ||
139 | snd_soc_dapm_nc_pin(codec, "PREDRIVER"); | ||
140 | snd_soc_dapm_nc_pin(codec, "CARKITL"); | ||
141 | snd_soc_dapm_nc_pin(codec, "CARKITR"); | ||
142 | |||
143 | ret = snd_soc_dapm_sync(codec); | ||
144 | |||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | /* Headset jack */ | ||
149 | static struct snd_soc_jack hs_jack; | ||
150 | |||
151 | /* Headset jack detection DAPM pins */ | ||
152 | static struct snd_soc_jack_pin hs_jack_pins[] = { | ||
153 | { | ||
154 | .pin = "Headset Jack", | ||
155 | .mask = SND_JACK_HEADSET, | ||
156 | }, | ||
157 | }; | ||
158 | |||
159 | /* Headset jack detection gpios */ | ||
160 | static struct snd_soc_jack_gpio hs_jack_gpios[] = { | ||
161 | { | ||
162 | .gpio = (OMAP_MAX_GPIO_LINES + 2), | ||
163 | .name = "hsdet-gpio", | ||
164 | .report = SND_JACK_HEADSET, | ||
165 | .debounce_time = 200, | ||
166 | }, | ||
167 | }; | ||
168 | |||
84 | /* Digital audio interface glue - connects codec <--> CPU */ | 169 | /* Digital audio interface glue - connects codec <--> CPU */ |
85 | static struct snd_soc_dai_link sdp3430_dai = { | 170 | static struct snd_soc_dai_link sdp3430_dai = { |
86 | .name = "TWL4030", | 171 | .name = "TWL4030", |
87 | .stream_name = "TWL4030", | 172 | .stream_name = "TWL4030", |
88 | .cpu_dai = &omap_mcbsp_dai[0], | 173 | .cpu_dai = &omap_mcbsp_dai[0], |
89 | .codec_dai = &twl4030_dai, | 174 | .codec_dai = &twl4030_dai, |
175 | .init = sdp3430_twl4030_init, | ||
90 | .ops = &sdp3430_ops, | 176 | .ops = &sdp3430_ops, |
91 | }; | 177 | }; |
92 | 178 | ||
@@ -130,7 +216,21 @@ static int __init sdp3430_soc_init(void) | |||
130 | if (ret) | 216 | if (ret) |
131 | goto err1; | 217 | goto err1; |
132 | 218 | ||
133 | return 0; | 219 | /* Headset jack detection */ |
220 | ret = snd_soc_jack_new(&snd_soc_sdp3430, "SDP3430 headset jack", | ||
221 | SND_JACK_HEADSET, &hs_jack); | ||
222 | if (ret) | ||
223 | return ret; | ||
224 | |||
225 | ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), | ||
226 | hs_jack_pins); | ||
227 | if (ret) | ||
228 | return ret; | ||
229 | |||
230 | ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), | ||
231 | hs_jack_gpios); | ||
232 | |||
233 | return ret; | ||
134 | 234 | ||
135 | err1: | 235 | err1: |
136 | printk(KERN_ERR "Unable to add platform device\n"); | 236 | printk(KERN_ERR "Unable to add platform device\n"); |
@@ -142,6 +242,9 @@ module_init(sdp3430_soc_init); | |||
142 | 242 | ||
143 | static void __exit sdp3430_soc_exit(void) | 243 | static void __exit sdp3430_soc_exit(void) |
144 | { | 244 | { |
245 | snd_soc_jack_free_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), | ||
246 | hs_jack_gpios); | ||
247 | |||
145 | platform_device_unregister(sdp3430_snd_device); | 248 | platform_device_unregister(sdp3430_snd_device); |
146 | } | 249 | } |
147 | module_exit(sdp3430_soc_exit); | 250 | module_exit(sdp3430_soc_exit); |
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 3e18064e86b2..d3fa6357a9fd 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c | |||
@@ -300,7 +300,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | |||
300 | int val; | 300 | int val; |
301 | 301 | ||
302 | u32 sscr0 = ssp_read_reg(ssp, SSCR0) & | 302 | u32 sscr0 = ssp_read_reg(ssp, SSCR0) & |
303 | ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC); | 303 | ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); |
304 | 304 | ||
305 | dev_dbg(&ssp->pdev->dev, | 305 | dev_dbg(&ssp->pdev->dev, |
306 | "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n", | 306 | "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n", |
@@ -328,7 +328,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | |||
328 | case PXA_SSP_CLK_AUDIO: | 328 | case PXA_SSP_CLK_AUDIO: |
329 | priv->sysclk = 0; | 329 | priv->sysclk = 0; |
330 | ssp_set_scr(&priv->dev, 1); | 330 | ssp_set_scr(&priv->dev, 1); |
331 | sscr0 |= SSCR0_ADC; | 331 | sscr0 |= SSCR0_ACS; |
332 | break; | 332 | break; |
333 | default: | 333 | default: |
334 | return -ENODEV; | 334 | return -ENODEV; |
@@ -522,9 +522,20 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, | |||
522 | u32 sscr1; | 522 | u32 sscr1; |
523 | u32 sspsp; | 523 | u32 sspsp; |
524 | 524 | ||
525 | /* check if we need to change anything at all */ | ||
526 | if (priv->dai_fmt == fmt) | ||
527 | return 0; | ||
528 | |||
529 | /* we can only change the settings if the port is not in use */ | ||
530 | if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { | ||
531 | dev_err(&ssp->pdev->dev, | ||
532 | "can't change hardware dai format: stream is in use"); | ||
533 | return -EINVAL; | ||
534 | } | ||
535 | |||
525 | /* reset port settings */ | 536 | /* reset port settings */ |
526 | sscr0 = ssp_read_reg(ssp, SSCR0) & | 537 | sscr0 = ssp_read_reg(ssp, SSCR0) & |
527 | (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC); | 538 | (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); |
528 | sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); | 539 | sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); |
529 | sspsp = 0; | 540 | sspsp = 0; |
530 | 541 | ||
@@ -644,8 +655,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, | |||
644 | sscr0 |= SSCR0_FPCKE; | 655 | sscr0 |= SSCR0_FPCKE; |
645 | #endif | 656 | #endif |
646 | sscr0 |= SSCR0_DataSize(16); | 657 | sscr0 |= SSCR0_DataSize(16); |
647 | if (params_channels(params) > 1) | 658 | /* use network mode (2 slots) for 16 bit stereo */ |
648 | sscr0 |= SSCR0_EDSS; | ||
649 | break; | 659 | break; |
650 | case SNDRV_PCM_FORMAT_S24_LE: | 660 | case SNDRV_PCM_FORMAT_S24_LE: |
651 | sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8)); | 661 | sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8)); |
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 11cd0f289c16..cf809049272a 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c | |||
@@ -106,13 +106,13 @@ static int pxa2xx_ac97_resume(struct snd_soc_dai *dai) | |||
106 | static int pxa2xx_ac97_probe(struct platform_device *pdev, | 106 | static int pxa2xx_ac97_probe(struct platform_device *pdev, |
107 | struct snd_soc_dai *dai) | 107 | struct snd_soc_dai *dai) |
108 | { | 108 | { |
109 | return pxa2xx_ac97_hw_probe(pdev); | 109 | return pxa2xx_ac97_hw_probe(to_platform_device(dai->dev)); |
110 | } | 110 | } |
111 | 111 | ||
112 | static void pxa2xx_ac97_remove(struct platform_device *pdev, | 112 | static void pxa2xx_ac97_remove(struct platform_device *pdev, |
113 | struct snd_soc_dai *dai) | 113 | struct snd_soc_dai *dai) |
114 | { | 114 | { |
115 | pxa2xx_ac97_hw_remove(pdev); | 115 | pxa2xx_ac97_hw_remove(to_platform_device(dai->dev)); |
116 | } | 116 | } |
117 | 117 | ||
118 | static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, | 118 | static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, |
@@ -230,15 +230,45 @@ struct snd_soc_dai pxa_ac97_dai[] = { | |||
230 | EXPORT_SYMBOL_GPL(pxa_ac97_dai); | 230 | EXPORT_SYMBOL_GPL(pxa_ac97_dai); |
231 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | 231 | EXPORT_SYMBOL_GPL(soc_ac97_ops); |
232 | 232 | ||
233 | static int __init pxa_ac97_init(void) | 233 | static int __devinit pxa2xx_ac97_dev_probe(struct platform_device *pdev) |
234 | { | 234 | { |
235 | int i; | ||
236 | |||
237 | for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++) | ||
238 | pxa_ac97_dai[i].dev = &pdev->dev; | ||
239 | |||
240 | /* Punt most of the init to the SoC probe; we may need the machine | ||
241 | * driver to do interesting things with the clocking to get us up | ||
242 | * and running. | ||
243 | */ | ||
235 | return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); | 244 | return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); |
236 | } | 245 | } |
246 | |||
247 | static int __devexit pxa2xx_ac97_dev_remove(struct platform_device *pdev) | ||
248 | { | ||
249 | snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static struct platform_driver pxa2xx_ac97_driver = { | ||
255 | .probe = pxa2xx_ac97_dev_probe, | ||
256 | .remove = __devexit_p(pxa2xx_ac97_dev_remove), | ||
257 | .driver = { | ||
258 | .name = "pxa2xx-ac97", | ||
259 | .owner = THIS_MODULE, | ||
260 | }, | ||
261 | }; | ||
262 | |||
263 | static int __init pxa_ac97_init(void) | ||
264 | { | ||
265 | return platform_driver_register(&pxa2xx_ac97_driver); | ||
266 | } | ||
237 | module_init(pxa_ac97_init); | 267 | module_init(pxa_ac97_init); |
238 | 268 | ||
239 | static void __exit pxa_ac97_exit(void) | 269 | static void __exit pxa_ac97_exit(void) |
240 | { | 270 | { |
241 | snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); | 271 | platform_driver_unregister(&pxa2xx_ac97_driver); |
242 | } | 272 | } |
243 | module_exit(pxa_ac97_exit); | 273 | module_exit(pxa_ac97_exit); |
244 | 274 | ||
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index 0140a250db24..9f6116edbb84 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c | |||
@@ -127,8 +127,11 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, | |||
127 | if (ret < 0) | 127 | if (ret < 0) |
128 | return ret; | 128 | return ret; |
129 | 129 | ||
130 | /* We're not really in network mode but the emulation wants this. */ | 130 | /* Use network mode for stereo, one slot per channel. */ |
131 | ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 1); | 131 | if (params_channels(params) > 1) |
132 | ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 2); | ||
133 | else | ||
134 | ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 1); | ||
132 | if (ret < 0) | 135 | if (ret < 0) |
133 | return ret; | 136 | return ret; |
134 | 137 | ||
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index e05a71084c32..2f3a21eee051 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig | |||
@@ -1,19 +1,31 @@ | |||
1 | config SND_S3C24XX_SOC | 1 | config SND_S3C24XX_SOC |
2 | tristate "SoC Audio for the Samsung S3C24XX chips" | 2 | tristate "SoC Audio for the Samsung S3CXXXX chips" |
3 | depends on ARCH_S3C2410 | 3 | depends on ARCH_S3C2410 || ARCH_S3C64XX |
4 | help | 4 | help |
5 | Say Y or M if you want to add support for codecs attached to | 5 | Say Y or M if you want to add support for codecs attached to |
6 | the S3C24XX AC97, I2S or SSP interface. You will also need | 6 | the S3C24XX and S3C64XX AC97, I2S or SSP interface. You will |
7 | to select the audio interfaces to support below. | 7 | also need to select the audio interfaces to support below. |
8 | 8 | ||
9 | config SND_S3C24XX_SOC_I2S | 9 | config SND_S3C24XX_SOC_I2S |
10 | tristate | 10 | tristate |
11 | select S3C2410_DMA | ||
12 | |||
13 | config SND_S3C_I2SV2_SOC | ||
14 | tristate | ||
11 | 15 | ||
12 | config SND_S3C2412_SOC_I2S | 16 | config SND_S3C2412_SOC_I2S |
13 | tristate | 17 | tristate |
18 | select SND_S3C_I2SV2_SOC | ||
19 | select S3C2410_DMA | ||
20 | |||
21 | config SND_S3C64XX_SOC_I2S | ||
22 | tristate | ||
23 | select SND_S3C_I2SV2_SOC | ||
24 | select S3C64XX_DMA | ||
14 | 25 | ||
15 | config SND_S3C2443_SOC_AC97 | 26 | config SND_S3C2443_SOC_AC97 |
16 | tristate | 27 | tristate |
28 | select S3C2410_DMA | ||
17 | select AC97_BUS | 29 | select AC97_BUS |
18 | select SND_SOC_AC97_BUS | 30 | select SND_SOC_AC97_BUS |
19 | 31 | ||
@@ -26,6 +38,14 @@ config SND_S3C24XX_SOC_NEO1973_WM8753 | |||
26 | Say Y if you want to add support for SoC audio on smdk2440 | 38 | Say Y if you want to add support for SoC audio on smdk2440 |
27 | with the WM8753. | 39 | with the WM8753. |
28 | 40 | ||
41 | config SND_S3C24XX_SOC_JIVE_WM8750 | ||
42 | tristate "SoC I2S Audio support for Jive" | ||
43 | depends on SND_S3C24XX_SOC && MACH_JIVE | ||
44 | select SND_SOC_WM8750 | ||
45 | select SND_S3C2412_SOC_I2S | ||
46 | help | ||
47 | Sat Y if you want to add support for SoC audio on the Jive. | ||
48 | |||
29 | config SND_S3C24XX_SOC_SMDK2443_WM9710 | 49 | config SND_S3C24XX_SOC_SMDK2443_WM9710 |
30 | tristate "SoC AC97 Audio support for SMDK2443 - WM9710" | 50 | tristate "SoC AC97 Audio support for SMDK2443 - WM9710" |
31 | depends on SND_S3C24XX_SOC && MACH_SMDK2443 | 51 | depends on SND_S3C24XX_SOC && MACH_SMDK2443 |
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 96b3f3f617d4..07a93a2ebe5f 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile | |||
@@ -2,19 +2,25 @@ | |||
2 | snd-soc-s3c24xx-objs := s3c24xx-pcm.o | 2 | snd-soc-s3c24xx-objs := s3c24xx-pcm.o |
3 | snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o | 3 | snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o |
4 | snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o | 4 | snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o |
5 | snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o | ||
5 | snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o | 6 | snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o |
7 | snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o | ||
6 | 8 | ||
7 | obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o | 9 | obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o |
8 | obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o | 10 | obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o |
9 | obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o | 11 | obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o |
10 | obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o | 12 | obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o |
13 | obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o | ||
14 | obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o | ||
11 | 15 | ||
12 | # S3C24XX Machine Support | 16 | # S3C24XX Machine Support |
17 | snd-soc-jive-wm8750-objs := jive_wm8750.o | ||
13 | snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o | 18 | snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o |
14 | snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o | 19 | snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o |
15 | snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o | 20 | snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o |
16 | snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o | 21 | snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o |
17 | 22 | ||
23 | obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o | ||
18 | obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o | 24 | obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o |
19 | obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o | 25 | obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o |
20 | obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o | 26 | obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o |
diff --git a/sound/soc/s3c24xx/jive_wm8750.c b/sound/soc/s3c24xx/jive_wm8750.c new file mode 100644 index 000000000000..32063790d95b --- /dev/null +++ b/sound/soc/s3c24xx/jive_wm8750.c | |||
@@ -0,0 +1,201 @@ | |||
1 | /* sound/soc/s3c24xx/jive_wm8750.c | ||
2 | * | ||
3 | * Copyright 2007,2008 Simtec Electronics | ||
4 | * | ||
5 | * Based on sound/soc/pxa/spitz.c | ||
6 | * Copyright 2005 Wolfson Microelectronics PLC. | ||
7 | * Copyright 2005 Openedhand Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/moduleparam.h> | ||
16 | #include <linux/timer.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/clk.h> | ||
20 | |||
21 | #include <sound/core.h> | ||
22 | #include <sound/pcm.h> | ||
23 | #include <sound/soc.h> | ||
24 | #include <sound/soc-dapm.h> | ||
25 | |||
26 | #include <asm/mach-types.h> | ||
27 | |||
28 | #include "s3c24xx-pcm.h" | ||
29 | #include "s3c2412-i2s.h" | ||
30 | |||
31 | #include "../codecs/wm8750.h" | ||
32 | |||
33 | static const struct snd_soc_dapm_route audio_map[] = { | ||
34 | { "Headphone Jack", NULL, "LOUT1" }, | ||
35 | { "Headphone Jack", NULL, "ROUT1" }, | ||
36 | { "Internal Speaker", NULL, "LOUT2" }, | ||
37 | { "Internal Speaker", NULL, "ROUT2" }, | ||
38 | { "LINPUT1", NULL, "Line Input" }, | ||
39 | { "RINPUT1", NULL, "Line Input" }, | ||
40 | }; | ||
41 | |||
42 | static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { | ||
43 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
44 | SND_SOC_DAPM_SPK("Internal Speaker", NULL), | ||
45 | SND_SOC_DAPM_LINE("Line In", NULL), | ||
46 | }; | ||
47 | |||
48 | static int jive_hw_params(struct snd_pcm_substream *substream, | ||
49 | struct snd_pcm_hw_params *params) | ||
50 | { | ||
51 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
52 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
53 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
54 | struct s3c_i2sv2_rate_calc div; | ||
55 | unsigned int clk = 0; | ||
56 | int ret = 0; | ||
57 | |||
58 | switch (params_rate(params)) { | ||
59 | case 8000: | ||
60 | case 16000: | ||
61 | case 48000: | ||
62 | case 96000: | ||
63 | clk = 12288000; | ||
64 | break; | ||
65 | case 11025: | ||
66 | case 22050: | ||
67 | case 44100: | ||
68 | clk = 11289600; | ||
69 | break; | ||
70 | } | ||
71 | |||
72 | s3c_i2sv2_calc_rate(&div, NULL, params_rate(params), | ||
73 | s3c2412_get_iisclk()); | ||
74 | |||
75 | /* set codec DAI configuration */ | ||
76 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
77 | SND_SOC_DAIFMT_NB_NF | | ||
78 | SND_SOC_DAIFMT_CBS_CFS); | ||
79 | if (ret < 0) | ||
80 | return ret; | ||
81 | |||
82 | /* set cpu DAI configuration */ | ||
83 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
84 | SND_SOC_DAIFMT_NB_NF | | ||
85 | SND_SOC_DAIFMT_CBS_CFS); | ||
86 | if (ret < 0) | ||
87 | return ret; | ||
88 | |||
89 | /* set the codec system clock for DAC and ADC */ | ||
90 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, | ||
91 | SND_SOC_CLOCK_IN); | ||
92 | if (ret < 0) | ||
93 | return ret; | ||
94 | |||
95 | ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_RCLK, div.fs_div); | ||
96 | if (ret < 0) | ||
97 | return ret; | ||
98 | |||
99 | ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_PRESCALER, | ||
100 | div.clk_div - 1); | ||
101 | if (ret < 0) | ||
102 | return ret; | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static struct snd_soc_ops jive_ops = { | ||
108 | .hw_params = jive_hw_params, | ||
109 | }; | ||
110 | |||
111 | static int jive_wm8750_init(struct snd_soc_codec *codec) | ||
112 | { | ||
113 | int err; | ||
114 | |||
115 | /* These endpoints are not being used. */ | ||
116 | snd_soc_dapm_nc_pin(codec, "LINPUT2"); | ||
117 | snd_soc_dapm_nc_pin(codec, "RINPUT2"); | ||
118 | snd_soc_dapm_nc_pin(codec, "LINPUT3"); | ||
119 | snd_soc_dapm_nc_pin(codec, "RINPUT3"); | ||
120 | snd_soc_dapm_nc_pin(codec, "OUT3"); | ||
121 | snd_soc_dapm_nc_pin(codec, "MONO"); | ||
122 | |||
123 | /* Add jive specific widgets */ | ||
124 | err = snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets, | ||
125 | ARRAY_SIZE(wm8750_dapm_widgets)); | ||
126 | if (err) { | ||
127 | printk(KERN_ERR "%s: failed to add widgets (%d)\n", | ||
128 | __func__, err); | ||
129 | return err; | ||
130 | } | ||
131 | |||
132 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
133 | snd_soc_dapm_sync(codec); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static struct snd_soc_dai_link jive_dai = { | ||
139 | .name = "wm8750", | ||
140 | .stream_name = "WM8750", | ||
141 | .cpu_dai = &s3c2412_i2s_dai, | ||
142 | .codec_dai = &wm8750_dai, | ||
143 | .init = jive_wm8750_init, | ||
144 | .ops = &jive_ops, | ||
145 | }; | ||
146 | |||
147 | /* jive audio machine driver */ | ||
148 | static struct snd_soc_machine snd_soc_machine_jive = { | ||
149 | .name = "Jive", | ||
150 | .dai_link = &jive_dai, | ||
151 | .num_links = 1, | ||
152 | }; | ||
153 | |||
154 | /* jive audio private data */ | ||
155 | static struct wm8750_setup_data jive_wm8750_setup = { | ||
156 | }; | ||
157 | |||
158 | /* jive audio subsystem */ | ||
159 | static struct snd_soc_device jive_snd_devdata = { | ||
160 | .machine = &snd_soc_machine_jive, | ||
161 | .platform = &s3c24xx_soc_platform, | ||
162 | .codec_dev = &soc_codec_dev_wm8750_spi, | ||
163 | .codec_data = &jive_wm8750_setup, | ||
164 | }; | ||
165 | |||
166 | static struct platform_device *jive_snd_device; | ||
167 | |||
168 | static int __init jive_init(void) | ||
169 | { | ||
170 | int ret; | ||
171 | |||
172 | if (!machine_is_jive()) | ||
173 | return 0; | ||
174 | |||
175 | printk("JIVE WM8750 Audio support\n"); | ||
176 | |||
177 | jive_snd_device = platform_device_alloc("soc-audio", -1); | ||
178 | if (!jive_snd_device) | ||
179 | return -ENOMEM; | ||
180 | |||
181 | platform_set_drvdata(jive_snd_device, &jive_snd_devdata); | ||
182 | jive_snd_devdata.dev = &jive_snd_device->dev; | ||
183 | ret = platform_device_add(jive_snd_device); | ||
184 | |||
185 | if (ret) | ||
186 | platform_device_put(jive_snd_device); | ||
187 | |||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | static void __exit jive_exit(void) | ||
192 | { | ||
193 | platform_device_unregister(jive_snd_device); | ||
194 | } | ||
195 | |||
196 | module_init(jive_init); | ||
197 | module_exit(jive_exit); | ||
198 | |||
199 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); | ||
200 | MODULE_DESCRIPTION("ALSA SoC Jive Audio support"); | ||
201 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 286e11ad50ea..5f6aeec0437d 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c | |||
@@ -29,25 +29,17 @@ | |||
29 | #include <mach/regs-clock.h> | 29 | #include <mach/regs-clock.h> |
30 | #include <mach/regs-gpio.h> | 30 | #include <mach/regs-gpio.h> |
31 | #include <mach/hardware.h> | 31 | #include <mach/hardware.h> |
32 | #include <mach/audio.h> | 32 | #include <plat/audio.h> |
33 | #include <linux/io.h> | 33 | #include <linux/io.h> |
34 | #include <mach/spi-gpio.h> | 34 | #include <mach/spi-gpio.h> |
35 | 35 | ||
36 | #include <asm/plat-s3c24xx/regs-iis.h> | 36 | #include <plat/regs-iis.h> |
37 | 37 | ||
38 | #include "../codecs/wm8753.h" | 38 | #include "../codecs/wm8753.h" |
39 | #include "lm4857.h" | 39 | #include "lm4857.h" |
40 | #include "s3c24xx-pcm.h" | 40 | #include "s3c24xx-pcm.h" |
41 | #include "s3c24xx-i2s.h" | 41 | #include "s3c24xx-i2s.h" |
42 | 42 | ||
43 | /* Debugging stuff */ | ||
44 | #define S3C24XX_SOC_NEO1973_WM8753_DEBUG 0 | ||
45 | #if S3C24XX_SOC_NEO1973_WM8753_DEBUG | ||
46 | #define DBG(x...) printk(KERN_DEBUG "s3c24xx-soc-neo1973-wm8753: " x) | ||
47 | #else | ||
48 | #define DBG(x...) | ||
49 | #endif | ||
50 | |||
51 | /* define the scenarios */ | 43 | /* define the scenarios */ |
52 | #define NEO_AUDIO_OFF 0 | 44 | #define NEO_AUDIO_OFF 0 |
53 | #define NEO_GSM_CALL_AUDIO_HANDSET 1 | 45 | #define NEO_GSM_CALL_AUDIO_HANDSET 1 |
@@ -72,7 +64,7 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, | |||
72 | int ret = 0; | 64 | int ret = 0; |
73 | unsigned long iis_clkrate; | 65 | unsigned long iis_clkrate; |
74 | 66 | ||
75 | DBG("Entered %s\n", __func__); | 67 | pr_debug("Entered %s\n", __func__); |
76 | 68 | ||
77 | iis_clkrate = s3c24xx_i2s_get_clockrate(); | 69 | iis_clkrate = s3c24xx_i2s_get_clockrate(); |
78 | 70 | ||
@@ -158,7 +150,7 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) | |||
158 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 150 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
159 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | 151 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; |
160 | 152 | ||
161 | DBG("Entered %s\n", __func__); | 153 | pr_debug("Entered %s\n", __func__); |
162 | 154 | ||
163 | /* disable the PLL */ | 155 | /* disable the PLL */ |
164 | return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0); | 156 | return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0); |
@@ -181,7 +173,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, | |||
181 | int ret = 0; | 173 | int ret = 0; |
182 | unsigned long iis_clkrate; | 174 | unsigned long iis_clkrate; |
183 | 175 | ||
184 | DBG("Entered %s\n", __func__); | 176 | pr_debug("Entered %s\n", __func__); |
185 | 177 | ||
186 | iis_clkrate = s3c24xx_i2s_get_clockrate(); | 178 | iis_clkrate = s3c24xx_i2s_get_clockrate(); |
187 | 179 | ||
@@ -224,7 +216,7 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) | |||
224 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 216 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
225 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | 217 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; |
226 | 218 | ||
227 | DBG("Entered %s\n", __func__); | 219 | pr_debug("Entered %s\n", __func__); |
228 | 220 | ||
229 | /* disable the PLL */ | 221 | /* disable the PLL */ |
230 | return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0); | 222 | return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0); |
@@ -246,7 +238,7 @@ static int neo1973_get_scenario(struct snd_kcontrol *kcontrol, | |||
246 | 238 | ||
247 | static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario) | 239 | static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario) |
248 | { | 240 | { |
249 | DBG("Entered %s\n", __func__); | 241 | pr_debug("Entered %s\n", __func__); |
250 | 242 | ||
251 | switch (neo1973_scenario) { | 243 | switch (neo1973_scenario) { |
252 | case NEO_AUDIO_OFF: | 244 | case NEO_AUDIO_OFF: |
@@ -330,7 +322,7 @@ static int neo1973_set_scenario(struct snd_kcontrol *kcontrol, | |||
330 | { | 322 | { |
331 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 323 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
332 | 324 | ||
333 | DBG("Entered %s\n", __func__); | 325 | pr_debug("Entered %s\n", __func__); |
334 | 326 | ||
335 | if (neo1973_scenario == ucontrol->value.integer.value[0]) | 327 | if (neo1973_scenario == ucontrol->value.integer.value[0]) |
336 | return 0; | 328 | return 0; |
@@ -344,7 +336,7 @@ static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0}; | |||
344 | 336 | ||
345 | static void lm4857_write_regs(void) | 337 | static void lm4857_write_regs(void) |
346 | { | 338 | { |
347 | DBG("Entered %s\n", __func__); | 339 | pr_debug("Entered %s\n", __func__); |
348 | 340 | ||
349 | if (i2c_master_send(i2c, lm4857_regs, 4) != 4) | 341 | if (i2c_master_send(i2c, lm4857_regs, 4) != 4) |
350 | printk(KERN_ERR "lm4857: i2c write failed\n"); | 342 | printk(KERN_ERR "lm4857: i2c write failed\n"); |
@@ -357,7 +349,7 @@ static int lm4857_get_reg(struct snd_kcontrol *kcontrol, | |||
357 | int shift = (kcontrol->private_value >> 8) & 0x0F; | 349 | int shift = (kcontrol->private_value >> 8) & 0x0F; |
358 | int mask = (kcontrol->private_value >> 16) & 0xFF; | 350 | int mask = (kcontrol->private_value >> 16) & 0xFF; |
359 | 351 | ||
360 | DBG("Entered %s\n", __func__); | 352 | pr_debug("Entered %s\n", __func__); |
361 | 353 | ||
362 | ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask; | 354 | ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask; |
363 | return 0; | 355 | return 0; |
@@ -385,7 +377,7 @@ static int lm4857_get_mode(struct snd_kcontrol *kcontrol, | |||
385 | { | 377 | { |
386 | u8 value = lm4857_regs[LM4857_CTRL] & 0x0F; | 378 | u8 value = lm4857_regs[LM4857_CTRL] & 0x0F; |
387 | 379 | ||
388 | DBG("Entered %s\n", __func__); | 380 | pr_debug("Entered %s\n", __func__); |
389 | 381 | ||
390 | if (value) | 382 | if (value) |
391 | value -= 5; | 383 | value -= 5; |
@@ -399,7 +391,7 @@ static int lm4857_set_mode(struct snd_kcontrol *kcontrol, | |||
399 | { | 391 | { |
400 | u8 value = ucontrol->value.integer.value[0]; | 392 | u8 value = ucontrol->value.integer.value[0]; |
401 | 393 | ||
402 | DBG("Entered %s\n", __func__); | 394 | pr_debug("Entered %s\n", __func__); |
403 | 395 | ||
404 | if (value) | 396 | if (value) |
405 | value += 5; | 397 | value += 5; |
@@ -508,7 +500,7 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec) | |||
508 | { | 500 | { |
509 | int i, err; | 501 | int i, err; |
510 | 502 | ||
511 | DBG("Entered %s\n", __func__); | 503 | pr_debug("Entered %s\n", __func__); |
512 | 504 | ||
513 | /* set up NC codec pins */ | 505 | /* set up NC codec pins */ |
514 | snd_soc_dapm_nc_pin(codec, "LOUT2"); | 506 | snd_soc_dapm_nc_pin(codec, "LOUT2"); |
@@ -593,7 +585,7 @@ static struct snd_soc_device neo1973_snd_devdata = { | |||
593 | static int lm4857_i2c_probe(struct i2c_client *client, | 585 | static int lm4857_i2c_probe(struct i2c_client *client, |
594 | const struct i2c_device_id *id) | 586 | const struct i2c_device_id *id) |
595 | { | 587 | { |
596 | DBG("Entered %s\n", __func__); | 588 | pr_debug("Entered %s\n", __func__); |
597 | 589 | ||
598 | i2c = client; | 590 | i2c = client; |
599 | 591 | ||
@@ -603,7 +595,7 @@ static int lm4857_i2c_probe(struct i2c_client *client, | |||
603 | 595 | ||
604 | static int lm4857_i2c_remove(struct i2c_client *client) | 596 | static int lm4857_i2c_remove(struct i2c_client *client) |
605 | { | 597 | { |
606 | DBG("Entered %s\n", __func__); | 598 | pr_debug("Entered %s\n", __func__); |
607 | 599 | ||
608 | i2c = NULL; | 600 | i2c = NULL; |
609 | 601 | ||
@@ -614,7 +606,7 @@ static u8 lm4857_state; | |||
614 | 606 | ||
615 | static int lm4857_suspend(struct i2c_client *dev, pm_message_t state) | 607 | static int lm4857_suspend(struct i2c_client *dev, pm_message_t state) |
616 | { | 608 | { |
617 | DBG("Entered %s\n", __func__); | 609 | pr_debug("Entered %s\n", __func__); |
618 | 610 | ||
619 | dev_dbg(&dev->dev, "lm4857_suspend\n"); | 611 | dev_dbg(&dev->dev, "lm4857_suspend\n"); |
620 | lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf; | 612 | lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf; |
@@ -627,7 +619,7 @@ static int lm4857_suspend(struct i2c_client *dev, pm_message_t state) | |||
627 | 619 | ||
628 | static int lm4857_resume(struct i2c_client *dev) | 620 | static int lm4857_resume(struct i2c_client *dev) |
629 | { | 621 | { |
630 | DBG("Entered %s\n", __func__); | 622 | pr_debug("Entered %s\n", __func__); |
631 | 623 | ||
632 | if (lm4857_state) { | 624 | if (lm4857_state) { |
633 | lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f); | 625 | lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f); |
@@ -638,7 +630,7 @@ static int lm4857_resume(struct i2c_client *dev) | |||
638 | 630 | ||
639 | static void lm4857_shutdown(struct i2c_client *dev) | 631 | static void lm4857_shutdown(struct i2c_client *dev) |
640 | { | 632 | { |
641 | DBG("Entered %s\n", __func__); | 633 | pr_debug("Entered %s\n", __func__); |
642 | 634 | ||
643 | dev_dbg(&dev->dev, "lm4857_shutdown\n"); | 635 | dev_dbg(&dev->dev, "lm4857_shutdown\n"); |
644 | lm4857_regs[LM4857_CTRL] &= 0xf0; | 636 | lm4857_regs[LM4857_CTRL] &= 0xf0; |
@@ -669,7 +661,7 @@ static int __init neo1973_init(void) | |||
669 | { | 661 | { |
670 | int ret; | 662 | int ret; |
671 | 663 | ||
672 | DBG("Entered %s\n", __func__); | 664 | pr_debug("Entered %s\n", __func__); |
673 | 665 | ||
674 | if (!machine_is_neo1973_gta01()) { | 666 | if (!machine_is_neo1973_gta01()) { |
675 | printk(KERN_INFO | 667 | printk(KERN_INFO |
@@ -700,7 +692,7 @@ static int __init neo1973_init(void) | |||
700 | 692 | ||
701 | static void __exit neo1973_exit(void) | 693 | static void __exit neo1973_exit(void) |
702 | { | 694 | { |
703 | DBG("Entered %s\n", __func__); | 695 | pr_debug("Entered %s\n", __func__); |
704 | 696 | ||
705 | i2c_del_driver(&lm4857_i2c_driver); | 697 | i2c_del_driver(&lm4857_i2c_driver); |
706 | platform_device_unregister(neo1973_snd_device); | 698 | platform_device_unregister(neo1973_snd_device); |
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c new file mode 100644 index 000000000000..295a4c910262 --- /dev/null +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c | |||
@@ -0,0 +1,638 @@ | |||
1 | /* sound/soc/s3c24xx/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/init.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/device.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/clk.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/io.h> | ||
26 | |||
27 | #include <sound/core.h> | ||
28 | #include <sound/pcm.h> | ||
29 | #include <sound/pcm_params.h> | ||
30 | #include <sound/initval.h> | ||
31 | #include <sound/soc.h> | ||
32 | |||
33 | #include <plat/regs-s3c2412-iis.h> | ||
34 | |||
35 | #include <plat/audio.h> | ||
36 | #include <mach/dma.h> | ||
37 | |||
38 | #include "s3c-i2s-v2.h" | ||
39 | |||
40 | #define S3C2412_I2S_DEBUG_CON 0 | ||
41 | |||
42 | static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) | ||
43 | { | ||
44 | return cpu_dai->private_data; | ||
45 | } | ||
46 | |||
47 | #define bit_set(v, b) (((v) & (b)) ? 1 : 0) | ||
48 | |||
49 | #if S3C2412_I2S_DEBUG_CON | ||
50 | static void dbg_showcon(const char *fn, u32 con) | ||
51 | { | ||
52 | printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn, | ||
53 | bit_set(con, S3C2412_IISCON_LRINDEX), | ||
54 | bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY), | ||
55 | bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY), | ||
56 | bit_set(con, S3C2412_IISCON_TXFIFO_FULL), | ||
57 | bit_set(con, S3C2412_IISCON_RXFIFO_FULL)); | ||
58 | |||
59 | printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n", | ||
60 | fn, | ||
61 | bit_set(con, S3C2412_IISCON_TXDMA_PAUSE), | ||
62 | bit_set(con, S3C2412_IISCON_RXDMA_PAUSE), | ||
63 | bit_set(con, S3C2412_IISCON_TXCH_PAUSE), | ||
64 | bit_set(con, S3C2412_IISCON_RXCH_PAUSE)); | ||
65 | printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn, | ||
66 | bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE), | ||
67 | bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE), | ||
68 | bit_set(con, S3C2412_IISCON_IIS_ACTIVE)); | ||
69 | } | ||
70 | #else | ||
71 | static inline void dbg_showcon(const char *fn, u32 con) | ||
72 | { | ||
73 | } | ||
74 | #endif | ||
75 | |||
76 | |||
77 | /* Turn on or off the transmission path. */ | ||
78 | void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on) | ||
79 | { | ||
80 | void __iomem *regs = i2s->regs; | ||
81 | u32 fic, con, mod; | ||
82 | |||
83 | pr_debug("%s(%d)\n", __func__, on); | ||
84 | |||
85 | fic = readl(regs + S3C2412_IISFIC); | ||
86 | con = readl(regs + S3C2412_IISCON); | ||
87 | mod = readl(regs + S3C2412_IISMOD); | ||
88 | |||
89 | pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
90 | |||
91 | if (on) { | ||
92 | con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; | ||
93 | con &= ~S3C2412_IISCON_TXDMA_PAUSE; | ||
94 | con &= ~S3C2412_IISCON_TXCH_PAUSE; | ||
95 | |||
96 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
97 | case S3C2412_IISMOD_MODE_TXONLY: | ||
98 | case S3C2412_IISMOD_MODE_TXRX: | ||
99 | /* do nothing, we are in the right mode */ | ||
100 | break; | ||
101 | |||
102 | case S3C2412_IISMOD_MODE_RXONLY: | ||
103 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
104 | mod |= S3C2412_IISMOD_MODE_TXRX; | ||
105 | break; | ||
106 | |||
107 | default: | ||
108 | dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n"); | ||
109 | } | ||
110 | |||
111 | writel(con, regs + S3C2412_IISCON); | ||
112 | writel(mod, regs + S3C2412_IISMOD); | ||
113 | } else { | ||
114 | /* Note, we do not have any indication that the FIFO problems | ||
115 | * tha the S3C2410/2440 had apply here, so we should be able | ||
116 | * to disable the DMA and TX without resetting the FIFOS. | ||
117 | */ | ||
118 | |||
119 | con |= S3C2412_IISCON_TXDMA_PAUSE; | ||
120 | con |= S3C2412_IISCON_TXCH_PAUSE; | ||
121 | con &= ~S3C2412_IISCON_TXDMA_ACTIVE; | ||
122 | |||
123 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
124 | case S3C2412_IISMOD_MODE_TXRX: | ||
125 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
126 | mod |= S3C2412_IISMOD_MODE_RXONLY; | ||
127 | break; | ||
128 | |||
129 | case S3C2412_IISMOD_MODE_TXONLY: | ||
130 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
131 | con &= ~S3C2412_IISCON_IIS_ACTIVE; | ||
132 | break; | ||
133 | |||
134 | default: | ||
135 | dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n"); | ||
136 | } | ||
137 | |||
138 | writel(mod, regs + S3C2412_IISMOD); | ||
139 | writel(con, regs + S3C2412_IISCON); | ||
140 | } | ||
141 | |||
142 | fic = readl(regs + S3C2412_IISFIC); | ||
143 | dbg_showcon(__func__, con); | ||
144 | pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
145 | } | ||
146 | EXPORT_SYMBOL_GPL(s3c2412_snd_txctrl); | ||
147 | |||
148 | void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on) | ||
149 | { | ||
150 | void __iomem *regs = i2s->regs; | ||
151 | u32 fic, con, mod; | ||
152 | |||
153 | pr_debug("%s(%d)\n", __func__, on); | ||
154 | |||
155 | fic = readl(regs + S3C2412_IISFIC); | ||
156 | con = readl(regs + S3C2412_IISCON); | ||
157 | mod = readl(regs + S3C2412_IISMOD); | ||
158 | |||
159 | pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
160 | |||
161 | if (on) { | ||
162 | con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; | ||
163 | con &= ~S3C2412_IISCON_RXDMA_PAUSE; | ||
164 | con &= ~S3C2412_IISCON_RXCH_PAUSE; | ||
165 | |||
166 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
167 | case S3C2412_IISMOD_MODE_TXRX: | ||
168 | case S3C2412_IISMOD_MODE_RXONLY: | ||
169 | /* do nothing, we are in the right mode */ | ||
170 | break; | ||
171 | |||
172 | case S3C2412_IISMOD_MODE_TXONLY: | ||
173 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
174 | mod |= S3C2412_IISMOD_MODE_TXRX; | ||
175 | break; | ||
176 | |||
177 | default: | ||
178 | dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); | ||
179 | } | ||
180 | |||
181 | writel(mod, regs + S3C2412_IISMOD); | ||
182 | writel(con, regs + S3C2412_IISCON); | ||
183 | } else { | ||
184 | /* See txctrl notes on FIFOs. */ | ||
185 | |||
186 | con &= ~S3C2412_IISCON_RXDMA_ACTIVE; | ||
187 | con |= S3C2412_IISCON_RXDMA_PAUSE; | ||
188 | con |= S3C2412_IISCON_RXCH_PAUSE; | ||
189 | |||
190 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
191 | case S3C2412_IISMOD_MODE_RXONLY: | ||
192 | con &= ~S3C2412_IISCON_IIS_ACTIVE; | ||
193 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
194 | break; | ||
195 | |||
196 | case S3C2412_IISMOD_MODE_TXRX: | ||
197 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
198 | mod |= S3C2412_IISMOD_MODE_TXONLY; | ||
199 | break; | ||
200 | |||
201 | default: | ||
202 | dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); | ||
203 | } | ||
204 | |||
205 | writel(con, regs + S3C2412_IISCON); | ||
206 | writel(mod, regs + S3C2412_IISMOD); | ||
207 | } | ||
208 | |||
209 | fic = readl(regs + S3C2412_IISFIC); | ||
210 | pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
211 | } | ||
212 | EXPORT_SYMBOL_GPL(s3c2412_snd_rxctrl); | ||
213 | |||
214 | /* | ||
215 | * Wait for the LR signal to allow synchronisation to the L/R clock | ||
216 | * from the codec. May only be needed for slave mode. | ||
217 | */ | ||
218 | static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s) | ||
219 | { | ||
220 | u32 iiscon; | ||
221 | unsigned long timeout = jiffies + msecs_to_jiffies(5); | ||
222 | |||
223 | pr_debug("Entered %s\n", __func__); | ||
224 | |||
225 | while (1) { | ||
226 | iiscon = readl(i2s->regs + S3C2412_IISCON); | ||
227 | if (iiscon & S3C2412_IISCON_LRINDEX) | ||
228 | break; | ||
229 | |||
230 | if (timeout < jiffies) { | ||
231 | printk(KERN_ERR "%s: timeout\n", __func__); | ||
232 | return -ETIMEDOUT; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * Set S3C2412 I2S DAI format | ||
241 | */ | ||
242 | static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, | ||
243 | unsigned int fmt) | ||
244 | { | ||
245 | struct s3c_i2sv2_info *i2s = to_info(cpu_dai); | ||
246 | u32 iismod; | ||
247 | |||
248 | pr_debug("Entered %s\n", __func__); | ||
249 | |||
250 | iismod = readl(i2s->regs + S3C2412_IISMOD); | ||
251 | pr_debug("hw_params r: IISMOD: %x \n", iismod); | ||
252 | |||
253 | #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) | ||
254 | #define IISMOD_MASTER_MASK S3C2412_IISMOD_MASTER_MASK | ||
255 | #define IISMOD_SLAVE S3C2412_IISMOD_SLAVE | ||
256 | #define IISMOD_MASTER S3C2412_IISMOD_MASTER_INTERNAL | ||
257 | #endif | ||
258 | |||
259 | #if defined(CONFIG_PLAT_S3C64XX) | ||
260 | /* From Rev1.1 datasheet, we have two master and two slave modes: | ||
261 | * IMS[11:10]: | ||
262 | * 00 = master mode, fed from PCLK | ||
263 | * 01 = master mode, fed from CLKAUDIO | ||
264 | * 10 = slave mode, using PCLK | ||
265 | * 11 = slave mode, using I2SCLK | ||
266 | */ | ||
267 | #define IISMOD_MASTER_MASK (1 << 11) | ||
268 | #define IISMOD_SLAVE (1 << 11) | ||
269 | #define IISMOD_MASTER (0x0) | ||
270 | #endif | ||
271 | |||
272 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
273 | case SND_SOC_DAIFMT_CBM_CFM: | ||
274 | i2s->master = 0; | ||
275 | iismod &= ~IISMOD_MASTER_MASK; | ||
276 | iismod |= IISMOD_SLAVE; | ||
277 | break; | ||
278 | case SND_SOC_DAIFMT_CBS_CFS: | ||
279 | i2s->master = 1; | ||
280 | iismod &= ~IISMOD_MASTER_MASK; | ||
281 | iismod |= IISMOD_MASTER; | ||
282 | break; | ||
283 | default: | ||
284 | pr_debug("unknwon master/slave format\n"); | ||
285 | return -EINVAL; | ||
286 | } | ||
287 | |||
288 | iismod &= ~S3C2412_IISMOD_SDF_MASK; | ||
289 | |||
290 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
291 | case SND_SOC_DAIFMT_RIGHT_J: | ||
292 | iismod |= S3C2412_IISMOD_SDF_MSB; | ||
293 | break; | ||
294 | case SND_SOC_DAIFMT_LEFT_J: | ||
295 | iismod |= S3C2412_IISMOD_SDF_LSB; | ||
296 | break; | ||
297 | case SND_SOC_DAIFMT_I2S: | ||
298 | iismod |= S3C2412_IISMOD_SDF_IIS; | ||
299 | break; | ||
300 | default: | ||
301 | pr_debug("Unknown data format\n"); | ||
302 | return -EINVAL; | ||
303 | } | ||
304 | |||
305 | writel(iismod, i2s->regs + S3C2412_IISMOD); | ||
306 | pr_debug("hw_params w: IISMOD: %x \n", iismod); | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, | ||
311 | struct snd_pcm_hw_params *params, | ||
312 | struct snd_soc_dai *socdai) | ||
313 | { | ||
314 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
315 | struct snd_soc_dai_link *dai = rtd->dai; | ||
316 | struct s3c_i2sv2_info *i2s = to_info(dai->cpu_dai); | ||
317 | u32 iismod; | ||
318 | |||
319 | pr_debug("Entered %s\n", __func__); | ||
320 | |||
321 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
322 | dai->cpu_dai->dma_data = i2s->dma_playback; | ||
323 | else | ||
324 | dai->cpu_dai->dma_data = i2s->dma_capture; | ||
325 | |||
326 | /* Working copies of register */ | ||
327 | iismod = readl(i2s->regs + S3C2412_IISMOD); | ||
328 | pr_debug("%s: r: IISMOD: %x\n", __func__, iismod); | ||
329 | |||
330 | switch (params_format(params)) { | ||
331 | case SNDRV_PCM_FORMAT_S8: | ||
332 | iismod |= S3C2412_IISMOD_8BIT; | ||
333 | break; | ||
334 | case SNDRV_PCM_FORMAT_S16_LE: | ||
335 | iismod &= ~S3C2412_IISMOD_8BIT; | ||
336 | break; | ||
337 | } | ||
338 | |||
339 | writel(iismod, i2s->regs + S3C2412_IISMOD); | ||
340 | pr_debug("%s: w: IISMOD: %x\n", __func__, iismod); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | ||
345 | struct snd_soc_dai *dai) | ||
346 | { | ||
347 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
348 | struct s3c_i2sv2_info *i2s = to_info(rtd->dai->cpu_dai); | ||
349 | int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); | ||
350 | unsigned long irqs; | ||
351 | int ret = 0; | ||
352 | |||
353 | pr_debug("Entered %s\n", __func__); | ||
354 | |||
355 | switch (cmd) { | ||
356 | case SNDRV_PCM_TRIGGER_START: | ||
357 | /* On start, ensure that the FIFOs are cleared and reset. */ | ||
358 | |||
359 | writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH, | ||
360 | i2s->regs + S3C2412_IISFIC); | ||
361 | |||
362 | /* clear again, just in case */ | ||
363 | writel(0x0, i2s->regs + S3C2412_IISFIC); | ||
364 | |||
365 | case SNDRV_PCM_TRIGGER_RESUME: | ||
366 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
367 | if (!i2s->master) { | ||
368 | ret = s3c2412_snd_lrsync(i2s); | ||
369 | if (ret) | ||
370 | goto exit_err; | ||
371 | } | ||
372 | |||
373 | local_irq_save(irqs); | ||
374 | |||
375 | if (capture) | ||
376 | s3c2412_snd_rxctrl(i2s, 1); | ||
377 | else | ||
378 | s3c2412_snd_txctrl(i2s, 1); | ||
379 | |||
380 | local_irq_restore(irqs); | ||
381 | break; | ||
382 | |||
383 | case SNDRV_PCM_TRIGGER_STOP: | ||
384 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
385 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
386 | local_irq_save(irqs); | ||
387 | |||
388 | if (capture) | ||
389 | s3c2412_snd_rxctrl(i2s, 0); | ||
390 | else | ||
391 | s3c2412_snd_txctrl(i2s, 0); | ||
392 | |||
393 | local_irq_restore(irqs); | ||
394 | break; | ||
395 | default: | ||
396 | ret = -EINVAL; | ||
397 | break; | ||
398 | } | ||
399 | |||
400 | exit_err: | ||
401 | return ret; | ||
402 | } | ||
403 | |||
404 | /* | ||
405 | * Set S3C2412 Clock dividers | ||
406 | */ | ||
407 | static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, | ||
408 | int div_id, int div) | ||
409 | { | ||
410 | struct s3c_i2sv2_info *i2s = to_info(cpu_dai); | ||
411 | u32 reg; | ||
412 | |||
413 | pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); | ||
414 | |||
415 | switch (div_id) { | ||
416 | case S3C_I2SV2_DIV_BCLK: | ||
417 | reg = readl(i2s->regs + S3C2412_IISMOD); | ||
418 | reg &= ~S3C2412_IISMOD_BCLK_MASK; | ||
419 | writel(reg | div, i2s->regs + S3C2412_IISMOD); | ||
420 | |||
421 | pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); | ||
422 | break; | ||
423 | |||
424 | case S3C_I2SV2_DIV_RCLK: | ||
425 | if (div > 3) { | ||
426 | /* convert value to bit field */ | ||
427 | |||
428 | switch (div) { | ||
429 | case 256: | ||
430 | div = S3C2412_IISMOD_RCLK_256FS; | ||
431 | break; | ||
432 | |||
433 | case 384: | ||
434 | div = S3C2412_IISMOD_RCLK_384FS; | ||
435 | break; | ||
436 | |||
437 | case 512: | ||
438 | div = S3C2412_IISMOD_RCLK_512FS; | ||
439 | break; | ||
440 | |||
441 | case 768: | ||
442 | div = S3C2412_IISMOD_RCLK_768FS; | ||
443 | break; | ||
444 | |||
445 | default: | ||
446 | return -EINVAL; | ||
447 | } | ||
448 | } | ||
449 | |||
450 | reg = readl(i2s->regs + S3C2412_IISMOD); | ||
451 | reg &= ~S3C2412_IISMOD_RCLK_MASK; | ||
452 | writel(reg | div, i2s->regs + S3C2412_IISMOD); | ||
453 | pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); | ||
454 | break; | ||
455 | |||
456 | case S3C_I2SV2_DIV_PRESCALER: | ||
457 | if (div >= 0) { | ||
458 | writel((div << 8) | S3C2412_IISPSR_PSREN, | ||
459 | i2s->regs + S3C2412_IISPSR); | ||
460 | } else { | ||
461 | writel(0x0, i2s->regs + S3C2412_IISPSR); | ||
462 | } | ||
463 | pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); | ||
464 | break; | ||
465 | |||
466 | default: | ||
467 | return -EINVAL; | ||
468 | } | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | /* default table of all avaialable root fs divisors */ | ||
474 | static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 }; | ||
475 | |||
476 | int s3c2412_iis_calc_rate(struct s3c_i2sv2_rate_calc *info, | ||
477 | unsigned int *fstab, | ||
478 | unsigned int rate, struct clk *clk) | ||
479 | { | ||
480 | unsigned long clkrate = clk_get_rate(clk); | ||
481 | unsigned int div; | ||
482 | unsigned int fsclk; | ||
483 | unsigned int actual; | ||
484 | unsigned int fs; | ||
485 | unsigned int fsdiv; | ||
486 | signed int deviation = 0; | ||
487 | unsigned int best_fs = 0; | ||
488 | unsigned int best_div = 0; | ||
489 | unsigned int best_rate = 0; | ||
490 | unsigned int best_deviation = INT_MAX; | ||
491 | |||
492 | if (fstab == NULL) | ||
493 | fstab = iis_fs_tab; | ||
494 | |||
495 | for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) { | ||
496 | fsdiv = iis_fs_tab[fs]; | ||
497 | |||
498 | fsclk = clkrate / fsdiv; | ||
499 | div = fsclk / rate; | ||
500 | |||
501 | if ((fsclk % rate) > (rate / 2)) | ||
502 | div++; | ||
503 | |||
504 | if (div <= 1) | ||
505 | continue; | ||
506 | |||
507 | actual = clkrate / (fsdiv * div); | ||
508 | deviation = actual - rate; | ||
509 | |||
510 | printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n", | ||
511 | fsdiv, div, actual, deviation); | ||
512 | |||
513 | deviation = abs(deviation); | ||
514 | |||
515 | if (deviation < best_deviation) { | ||
516 | best_fs = fsdiv; | ||
517 | best_div = div; | ||
518 | best_rate = actual; | ||
519 | best_deviation = deviation; | ||
520 | } | ||
521 | |||
522 | if (deviation == 0) | ||
523 | break; | ||
524 | } | ||
525 | |||
526 | printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n", | ||
527 | best_fs, best_div, best_rate); | ||
528 | |||
529 | info->fs_div = best_fs; | ||
530 | info->clk_div = best_div; | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate); | ||
535 | |||
536 | int s3c_i2sv2_probe(struct platform_device *pdev, | ||
537 | struct snd_soc_dai *dai, | ||
538 | struct s3c_i2sv2_info *i2s, | ||
539 | unsigned long base) | ||
540 | { | ||
541 | struct device *dev = &pdev->dev; | ||
542 | |||
543 | i2s->dev = dev; | ||
544 | |||
545 | /* record our i2s structure for later use in the callbacks */ | ||
546 | dai->private_data = i2s; | ||
547 | |||
548 | i2s->regs = ioremap(base, 0x100); | ||
549 | if (i2s->regs == NULL) { | ||
550 | dev_err(dev, "cannot ioremap registers\n"); | ||
551 | return -ENXIO; | ||
552 | } | ||
553 | |||
554 | i2s->iis_pclk = clk_get(dev, "iis"); | ||
555 | if (i2s->iis_pclk == NULL) { | ||
556 | dev_err(dev, "failed to get iis_clock\n"); | ||
557 | iounmap(i2s->regs); | ||
558 | return -ENOENT; | ||
559 | } | ||
560 | |||
561 | clk_enable(i2s->iis_pclk); | ||
562 | |||
563 | s3c2412_snd_txctrl(i2s, 0); | ||
564 | s3c2412_snd_rxctrl(i2s, 0); | ||
565 | |||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | EXPORT_SYMBOL_GPL(s3c_i2sv2_probe); | ||
570 | |||
571 | #ifdef CONFIG_PM | ||
572 | static int s3c2412_i2s_suspend(struct snd_soc_dai *dai) | ||
573 | { | ||
574 | struct s3c_i2sv2_info *i2s = to_info(dai); | ||
575 | u32 iismod; | ||
576 | |||
577 | if (dai->active) { | ||
578 | i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD); | ||
579 | i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON); | ||
580 | i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR); | ||
581 | |||
582 | /* some basic suspend checks */ | ||
583 | |||
584 | iismod = readl(i2s->regs + S3C2412_IISMOD); | ||
585 | |||
586 | if (iismod & S3C2412_IISCON_RXDMA_ACTIVE) | ||
587 | pr_warning("%s: RXDMA active?\n", __func__); | ||
588 | |||
589 | if (iismod & S3C2412_IISCON_TXDMA_ACTIVE) | ||
590 | pr_warning("%s: TXDMA active?\n", __func__); | ||
591 | |||
592 | if (iismod & S3C2412_IISCON_IIS_ACTIVE) | ||
593 | pr_warning("%s: IIS active\n", __func__); | ||
594 | } | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | static int s3c2412_i2s_resume(struct snd_soc_dai *dai) | ||
600 | { | ||
601 | struct s3c_i2sv2_info *i2s = to_info(dai); | ||
602 | |||
603 | pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n", | ||
604 | dai->active, i2s->suspend_iismod, i2s->suspend_iiscon); | ||
605 | |||
606 | if (dai->active) { | ||
607 | writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON); | ||
608 | writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD); | ||
609 | writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR); | ||
610 | |||
611 | writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH, | ||
612 | i2s->regs + S3C2412_IISFIC); | ||
613 | |||
614 | ndelay(250); | ||
615 | writel(0x0, i2s->regs + S3C2412_IISFIC); | ||
616 | } | ||
617 | |||
618 | return 0; | ||
619 | } | ||
620 | #else | ||
621 | #define s3c2412_i2s_suspend NULL | ||
622 | #define s3c2412_i2s_resume NULL | ||
623 | #endif | ||
624 | |||
625 | int s3c_i2sv2_register_dai(struct snd_soc_dai *dai) | ||
626 | { | ||
627 | dai->ops.trigger = s3c2412_i2s_trigger; | ||
628 | dai->ops.hw_params = s3c2412_i2s_hw_params; | ||
629 | dai->ops.set_fmt = s3c2412_i2s_set_fmt; | ||
630 | dai->ops.set_clkdiv = s3c2412_i2s_set_clkdiv; | ||
631 | |||
632 | dai->suspend = s3c2412_i2s_suspend; | ||
633 | dai->resume = s3c2412_i2s_resume; | ||
634 | |||
635 | return snd_soc_register_dai(dai); | ||
636 | } | ||
637 | |||
638 | EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai); | ||
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.h b/sound/soc/s3c24xx/s3c-i2s-v2.h new file mode 100644 index 000000000000..f66854a77fb2 --- /dev/null +++ b/sound/soc/s3c24xx/s3c-i2s-v2.h | |||
@@ -0,0 +1,90 @@ | |||
1 | /* sound/soc/s3c24xx/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 | /** | ||
29 | * struct s3c_i2sv2_info - S3C I2S-V2 information | ||
30 | * @dev: The parent device passed to use from the probe. | ||
31 | * @regs: The pointer to the device registe block. | ||
32 | * @master: True if the I2S core is the I2S bit clock master. | ||
33 | * @dma_playback: DMA information for playback channel. | ||
34 | * @dma_capture: DMA information for capture channel. | ||
35 | * @suspend_iismod: PM save for the IISMOD register. | ||
36 | * @suspend_iiscon: PM save for the IISCON register. | ||
37 | * @suspend_iispsr: PM save for the IISPSR register. | ||
38 | * | ||
39 | * This is the private codec state for the hardware associated with an | ||
40 | * I2S channel such as the register mappings and clock sources. | ||
41 | */ | ||
42 | struct s3c_i2sv2_info { | ||
43 | struct device *dev; | ||
44 | void __iomem *regs; | ||
45 | |||
46 | struct clk *iis_pclk; | ||
47 | struct clk *iis_cclk; | ||
48 | struct clk *iis_clk; | ||
49 | |||
50 | unsigned char master; | ||
51 | |||
52 | struct s3c24xx_pcm_dma_params *dma_playback; | ||
53 | struct s3c24xx_pcm_dma_params *dma_capture; | ||
54 | |||
55 | u32 suspend_iismod; | ||
56 | u32 suspend_iiscon; | ||
57 | u32 suspend_iispsr; | ||
58 | }; | ||
59 | |||
60 | struct s3c_i2sv2_rate_calc { | ||
61 | unsigned int clk_div; /* for prescaler */ | ||
62 | unsigned int fs_div; /* for root frame clock */ | ||
63 | }; | ||
64 | |||
65 | extern int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info, | ||
66 | unsigned int *fstab, | ||
67 | unsigned int rate, struct clk *clk); | ||
68 | |||
69 | /** | ||
70 | * s3c_i2sv2_probe - probe for i2s device helper | ||
71 | * @pdev: The platform device supplied to the original probe. | ||
72 | * @dai: The ASoC DAI structure supplied to the original probe. | ||
73 | * @i2s: Our local i2s structure to fill in. | ||
74 | * @base: The base address for the registers. | ||
75 | */ | ||
76 | extern int s3c_i2sv2_probe(struct platform_device *pdev, | ||
77 | struct snd_soc_dai *dai, | ||
78 | struct s3c_i2sv2_info *i2s, | ||
79 | unsigned long base); | ||
80 | |||
81 | /** | ||
82 | * s3c_i2sv2_register_dai - register dai with soc core | ||
83 | * @dai: The snd_soc_dai structure to register | ||
84 | * | ||
85 | * Fill in any missing fields and then register the given dai with the | ||
86 | * soc core. | ||
87 | */ | ||
88 | extern int s3c_i2sv2_register_dai(struct snd_soc_dai *dai); | ||
89 | |||
90 | #endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */ | ||
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index 382d7eee53ef..1ca3cdaa8213 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/delay.h> | 22 | #include <linux/delay.h> |
23 | #include <linux/clk.h> | 23 | #include <linux/clk.h> |
24 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
25 | #include <linux/io.h> | ||
25 | 26 | ||
26 | #include <sound/core.h> | 27 | #include <sound/core.h> |
27 | #include <sound/pcm.h> | 28 | #include <sound/pcm.h> |
@@ -30,26 +31,16 @@ | |||
30 | #include <sound/soc.h> | 31 | #include <sound/soc.h> |
31 | #include <mach/hardware.h> | 32 | #include <mach/hardware.h> |
32 | 33 | ||
33 | #include <linux/io.h> | 34 | #include <plat/regs-s3c2412-iis.h> |
34 | #include <asm/dma.h> | ||
35 | |||
36 | #include <asm/plat-s3c24xx/regs-s3c2412-iis.h> | ||
37 | 35 | ||
38 | #include <mach/regs-gpio.h> | 36 | #include <plat/regs-gpio.h> |
39 | #include <mach/audio.h> | 37 | #include <plat/audio.h> |
40 | #include <mach/dma.h> | 38 | #include <mach/dma.h> |
41 | 39 | ||
42 | #include "s3c24xx-pcm.h" | 40 | #include "s3c24xx-pcm.h" |
43 | #include "s3c2412-i2s.h" | 41 | #include "s3c2412-i2s.h" |
44 | 42 | ||
45 | #define S3C2412_I2S_DEBUG 0 | 43 | #define S3C2412_I2S_DEBUG 0 |
46 | #define S3C2412_I2S_DEBUG_CON 0 | ||
47 | |||
48 | #if S3C2412_I2S_DEBUG | ||
49 | #define DBG(x...) printk(KERN_INFO x) | ||
50 | #else | ||
51 | #define DBG(x...) do { } while (0) | ||
52 | #endif | ||
53 | 44 | ||
54 | static struct s3c2410_dma_client s3c2412_dma_client_out = { | 45 | static struct s3c2410_dma_client s3c2412_dma_client_out = { |
55 | .name = "I2S PCM Stereo out" | 46 | .name = "I2S PCM Stereo out" |
@@ -73,431 +64,7 @@ static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = { | |||
73 | .dma_size = 4, | 64 | .dma_size = 4, |
74 | }; | 65 | }; |
75 | 66 | ||
76 | struct s3c2412_i2s_info { | 67 | static struct s3c_i2sv2_info s3c2412_i2s; |
77 | struct device *dev; | ||
78 | void __iomem *regs; | ||
79 | struct clk *iis_clk; | ||
80 | struct clk *iis_pclk; | ||
81 | struct clk *iis_cclk; | ||
82 | |||
83 | u32 suspend_iismod; | ||
84 | u32 suspend_iiscon; | ||
85 | u32 suspend_iispsr; | ||
86 | }; | ||
87 | |||
88 | static struct s3c2412_i2s_info s3c2412_i2s; | ||
89 | |||
90 | #define bit_set(v, b) (((v) & (b)) ? 1 : 0) | ||
91 | |||
92 | #if S3C2412_I2S_DEBUG_CON | ||
93 | static void dbg_showcon(const char *fn, u32 con) | ||
94 | { | ||
95 | printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn, | ||
96 | bit_set(con, S3C2412_IISCON_LRINDEX), | ||
97 | bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY), | ||
98 | bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY), | ||
99 | bit_set(con, S3C2412_IISCON_TXFIFO_FULL), | ||
100 | bit_set(con, S3C2412_IISCON_RXFIFO_FULL)); | ||
101 | |||
102 | printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n", | ||
103 | fn, | ||
104 | bit_set(con, S3C2412_IISCON_TXDMA_PAUSE), | ||
105 | bit_set(con, S3C2412_IISCON_RXDMA_PAUSE), | ||
106 | bit_set(con, S3C2412_IISCON_TXCH_PAUSE), | ||
107 | bit_set(con, S3C2412_IISCON_RXCH_PAUSE)); | ||
108 | printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn, | ||
109 | bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE), | ||
110 | bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE), | ||
111 | bit_set(con, S3C2412_IISCON_IIS_ACTIVE)); | ||
112 | } | ||
113 | #else | ||
114 | static inline void dbg_showcon(const char *fn, u32 con) | ||
115 | { | ||
116 | } | ||
117 | #endif | ||
118 | |||
119 | /* Turn on or off the transmission path. */ | ||
120 | static void s3c2412_snd_txctrl(int on) | ||
121 | { | ||
122 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
123 | void __iomem *regs = i2s->regs; | ||
124 | u32 fic, con, mod; | ||
125 | |||
126 | DBG("%s(%d)\n", __func__, on); | ||
127 | |||
128 | fic = readl(regs + S3C2412_IISFIC); | ||
129 | con = readl(regs + S3C2412_IISCON); | ||
130 | mod = readl(regs + S3C2412_IISMOD); | ||
131 | |||
132 | DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
133 | |||
134 | if (on) { | ||
135 | con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; | ||
136 | con &= ~S3C2412_IISCON_TXDMA_PAUSE; | ||
137 | con &= ~S3C2412_IISCON_TXCH_PAUSE; | ||
138 | |||
139 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
140 | case S3C2412_IISMOD_MODE_TXONLY: | ||
141 | case S3C2412_IISMOD_MODE_TXRX: | ||
142 | /* do nothing, we are in the right mode */ | ||
143 | break; | ||
144 | |||
145 | case S3C2412_IISMOD_MODE_RXONLY: | ||
146 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
147 | mod |= S3C2412_IISMOD_MODE_TXRX; | ||
148 | break; | ||
149 | |||
150 | default: | ||
151 | dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n"); | ||
152 | } | ||
153 | |||
154 | writel(con, regs + S3C2412_IISCON); | ||
155 | writel(mod, regs + S3C2412_IISMOD); | ||
156 | } else { | ||
157 | /* Note, we do not have any indication that the FIFO problems | ||
158 | * tha the S3C2410/2440 had apply here, so we should be able | ||
159 | * to disable the DMA and TX without resetting the FIFOS. | ||
160 | */ | ||
161 | |||
162 | con |= S3C2412_IISCON_TXDMA_PAUSE; | ||
163 | con |= S3C2412_IISCON_TXCH_PAUSE; | ||
164 | con &= ~S3C2412_IISCON_TXDMA_ACTIVE; | ||
165 | |||
166 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
167 | case S3C2412_IISMOD_MODE_TXRX: | ||
168 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
169 | mod |= S3C2412_IISMOD_MODE_RXONLY; | ||
170 | break; | ||
171 | |||
172 | case S3C2412_IISMOD_MODE_TXONLY: | ||
173 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
174 | con &= ~S3C2412_IISCON_IIS_ACTIVE; | ||
175 | break; | ||
176 | |||
177 | default: | ||
178 | dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n"); | ||
179 | } | ||
180 | |||
181 | writel(mod, regs + S3C2412_IISMOD); | ||
182 | writel(con, regs + S3C2412_IISCON); | ||
183 | } | ||
184 | |||
185 | fic = readl(regs + S3C2412_IISFIC); | ||
186 | dbg_showcon(__func__, con); | ||
187 | DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
188 | } | ||
189 | |||
190 | static void s3c2412_snd_rxctrl(int on) | ||
191 | { | ||
192 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
193 | void __iomem *regs = i2s->regs; | ||
194 | u32 fic, con, mod; | ||
195 | |||
196 | DBG("%s(%d)\n", __func__, on); | ||
197 | |||
198 | fic = readl(regs + S3C2412_IISFIC); | ||
199 | con = readl(regs + S3C2412_IISCON); | ||
200 | mod = readl(regs + S3C2412_IISMOD); | ||
201 | |||
202 | DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
203 | |||
204 | if (on) { | ||
205 | con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; | ||
206 | con &= ~S3C2412_IISCON_RXDMA_PAUSE; | ||
207 | con &= ~S3C2412_IISCON_RXCH_PAUSE; | ||
208 | |||
209 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
210 | case S3C2412_IISMOD_MODE_TXRX: | ||
211 | case S3C2412_IISMOD_MODE_RXONLY: | ||
212 | /* do nothing, we are in the right mode */ | ||
213 | break; | ||
214 | |||
215 | case S3C2412_IISMOD_MODE_TXONLY: | ||
216 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
217 | mod |= S3C2412_IISMOD_MODE_TXRX; | ||
218 | break; | ||
219 | |||
220 | default: | ||
221 | dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); | ||
222 | } | ||
223 | |||
224 | writel(mod, regs + S3C2412_IISMOD); | ||
225 | writel(con, regs + S3C2412_IISCON); | ||
226 | } else { | ||
227 | /* See txctrl notes on FIFOs. */ | ||
228 | |||
229 | con &= ~S3C2412_IISCON_RXDMA_ACTIVE; | ||
230 | con |= S3C2412_IISCON_RXDMA_PAUSE; | ||
231 | con |= S3C2412_IISCON_RXCH_PAUSE; | ||
232 | |||
233 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
234 | case S3C2412_IISMOD_MODE_RXONLY: | ||
235 | con &= ~S3C2412_IISCON_IIS_ACTIVE; | ||
236 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
237 | break; | ||
238 | |||
239 | case S3C2412_IISMOD_MODE_TXRX: | ||
240 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
241 | mod |= S3C2412_IISMOD_MODE_TXONLY; | ||
242 | break; | ||
243 | |||
244 | default: | ||
245 | dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); | ||
246 | } | ||
247 | |||
248 | writel(con, regs + S3C2412_IISCON); | ||
249 | writel(mod, regs + S3C2412_IISMOD); | ||
250 | } | ||
251 | |||
252 | fic = readl(regs + S3C2412_IISFIC); | ||
253 | DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
254 | } | ||
255 | |||
256 | |||
257 | /* | ||
258 | * Wait for the LR signal to allow synchronisation to the L/R clock | ||
259 | * from the codec. May only be needed for slave mode. | ||
260 | */ | ||
261 | static int s3c2412_snd_lrsync(void) | ||
262 | { | ||
263 | u32 iiscon; | ||
264 | unsigned long timeout = jiffies + msecs_to_jiffies(5); | ||
265 | |||
266 | DBG("Entered %s\n", __func__); | ||
267 | |||
268 | while (1) { | ||
269 | iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON); | ||
270 | if (iiscon & S3C2412_IISCON_LRINDEX) | ||
271 | break; | ||
272 | |||
273 | if (timeout < jiffies) { | ||
274 | printk(KERN_ERR "%s: timeout\n", __func__); | ||
275 | return -ETIMEDOUT; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * Check whether CPU is the master or slave | ||
284 | */ | ||
285 | static inline int s3c2412_snd_is_clkmaster(void) | ||
286 | { | ||
287 | u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | ||
288 | |||
289 | DBG("Entered %s\n", __func__); | ||
290 | |||
291 | iismod &= S3C2412_IISMOD_MASTER_MASK; | ||
292 | return !(iismod == S3C2412_IISMOD_SLAVE); | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * Set S3C2412 I2S DAI format | ||
297 | */ | ||
298 | static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, | ||
299 | unsigned int fmt) | ||
300 | { | ||
301 | u32 iismod; | ||
302 | |||
303 | |||
304 | DBG("Entered %s\n", __func__); | ||
305 | |||
306 | iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | ||
307 | DBG("hw_params r: IISMOD: %x \n", iismod); | ||
308 | |||
309 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
310 | case SND_SOC_DAIFMT_CBM_CFM: | ||
311 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; | ||
312 | iismod |= S3C2412_IISMOD_SLAVE; | ||
313 | break; | ||
314 | case SND_SOC_DAIFMT_CBS_CFS: | ||
315 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; | ||
316 | iismod |= S3C2412_IISMOD_MASTER_INTERNAL; | ||
317 | break; | ||
318 | default: | ||
319 | DBG("unknwon master/slave format\n"); | ||
320 | return -EINVAL; | ||
321 | } | ||
322 | |||
323 | iismod &= ~S3C2412_IISMOD_SDF_MASK; | ||
324 | |||
325 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
326 | case SND_SOC_DAIFMT_RIGHT_J: | ||
327 | iismod |= S3C2412_IISMOD_SDF_MSB; | ||
328 | break; | ||
329 | case SND_SOC_DAIFMT_LEFT_J: | ||
330 | iismod |= S3C2412_IISMOD_SDF_LSB; | ||
331 | break; | ||
332 | case SND_SOC_DAIFMT_I2S: | ||
333 | iismod |= S3C2412_IISMOD_SDF_IIS; | ||
334 | break; | ||
335 | default: | ||
336 | DBG("Unknown data format\n"); | ||
337 | return -EINVAL; | ||
338 | } | ||
339 | |||
340 | writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); | ||
341 | DBG("hw_params w: IISMOD: %x \n", iismod); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, | ||
346 | struct snd_pcm_hw_params *params, | ||
347 | struct snd_soc_dai *dai) | ||
348 | { | ||
349 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
350 | u32 iismod; | ||
351 | |||
352 | DBG("Entered %s\n", __func__); | ||
353 | |||
354 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
355 | rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out; | ||
356 | else | ||
357 | rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in; | ||
358 | |||
359 | /* Working copies of register */ | ||
360 | iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | ||
361 | DBG("%s: r: IISMOD: %x\n", __func__, iismod); | ||
362 | |||
363 | switch (params_format(params)) { | ||
364 | case SNDRV_PCM_FORMAT_S8: | ||
365 | iismod |= S3C2412_IISMOD_8BIT; | ||
366 | break; | ||
367 | case SNDRV_PCM_FORMAT_S16_LE: | ||
368 | iismod &= ~S3C2412_IISMOD_8BIT; | ||
369 | break; | ||
370 | } | ||
371 | |||
372 | writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); | ||
373 | DBG("%s: w: IISMOD: %x\n", __func__, iismod); | ||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | ||
378 | struct snd_soc_dai *dai) | ||
379 | { | ||
380 | int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); | ||
381 | unsigned long irqs; | ||
382 | int ret = 0; | ||
383 | |||
384 | DBG("Entered %s\n", __func__); | ||
385 | |||
386 | switch (cmd) { | ||
387 | case SNDRV_PCM_TRIGGER_START: | ||
388 | /* On start, ensure that the FIFOs are cleared and reset. */ | ||
389 | |||
390 | writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH, | ||
391 | s3c2412_i2s.regs + S3C2412_IISFIC); | ||
392 | |||
393 | /* clear again, just in case */ | ||
394 | writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC); | ||
395 | |||
396 | case SNDRV_PCM_TRIGGER_RESUME: | ||
397 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
398 | if (!s3c2412_snd_is_clkmaster()) { | ||
399 | ret = s3c2412_snd_lrsync(); | ||
400 | if (ret) | ||
401 | goto exit_err; | ||
402 | } | ||
403 | |||
404 | local_irq_save(irqs); | ||
405 | |||
406 | if (capture) | ||
407 | s3c2412_snd_rxctrl(1); | ||
408 | else | ||
409 | s3c2412_snd_txctrl(1); | ||
410 | |||
411 | local_irq_restore(irqs); | ||
412 | break; | ||
413 | |||
414 | case SNDRV_PCM_TRIGGER_STOP: | ||
415 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
416 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
417 | local_irq_save(irqs); | ||
418 | |||
419 | if (capture) | ||
420 | s3c2412_snd_rxctrl(0); | ||
421 | else | ||
422 | s3c2412_snd_txctrl(0); | ||
423 | |||
424 | local_irq_restore(irqs); | ||
425 | break; | ||
426 | default: | ||
427 | ret = -EINVAL; | ||
428 | break; | ||
429 | } | ||
430 | |||
431 | exit_err: | ||
432 | return ret; | ||
433 | } | ||
434 | |||
435 | /* default table of all avaialable root fs divisors */ | ||
436 | static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 }; | ||
437 | |||
438 | int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, | ||
439 | unsigned int *fstab, | ||
440 | unsigned int rate, struct clk *clk) | ||
441 | { | ||
442 | unsigned long clkrate = clk_get_rate(clk); | ||
443 | unsigned int div; | ||
444 | unsigned int fsclk; | ||
445 | unsigned int actual; | ||
446 | unsigned int fs; | ||
447 | unsigned int fsdiv; | ||
448 | signed int deviation = 0; | ||
449 | unsigned int best_fs = 0; | ||
450 | unsigned int best_div = 0; | ||
451 | unsigned int best_rate = 0; | ||
452 | unsigned int best_deviation = INT_MAX; | ||
453 | |||
454 | |||
455 | if (fstab == NULL) | ||
456 | fstab = s3c2412_iis_fs; | ||
457 | |||
458 | for (fs = 0;; fs++) { | ||
459 | fsdiv = s3c2412_iis_fs[fs]; | ||
460 | |||
461 | if (fsdiv == 0) | ||
462 | break; | ||
463 | |||
464 | fsclk = clkrate / fsdiv; | ||
465 | div = fsclk / rate; | ||
466 | |||
467 | if ((fsclk % rate) > (rate / 2)) | ||
468 | div++; | ||
469 | |||
470 | if (div <= 1) | ||
471 | continue; | ||
472 | |||
473 | actual = clkrate / (fsdiv * div); | ||
474 | deviation = actual - rate; | ||
475 | |||
476 | printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n", | ||
477 | fsdiv, div, actual, deviation); | ||
478 | |||
479 | deviation = abs(deviation); | ||
480 | |||
481 | if (deviation < best_deviation) { | ||
482 | best_fs = fsdiv; | ||
483 | best_div = div; | ||
484 | best_rate = actual; | ||
485 | best_deviation = deviation; | ||
486 | } | ||
487 | |||
488 | if (deviation == 0) | ||
489 | break; | ||
490 | } | ||
491 | |||
492 | printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n", | ||
493 | best_fs, best_div, best_rate); | ||
494 | |||
495 | info->fs_div = best_fs; | ||
496 | info->clk_div = best_div; | ||
497 | |||
498 | return 0; | ||
499 | } | ||
500 | EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate); | ||
501 | 68 | ||
502 | /* | 69 | /* |
503 | * Set S3C2412 Clock source | 70 | * Set S3C2412 Clock source |
@@ -507,15 +74,17 @@ static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | |||
507 | { | 74 | { |
508 | u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | 75 | u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); |
509 | 76 | ||
510 | DBG("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id, | 77 | pr_debug("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id, |
511 | freq, dir); | 78 | freq, dir); |
512 | 79 | ||
513 | switch (clk_id) { | 80 | switch (clk_id) { |
514 | case S3C2412_CLKSRC_PCLK: | 81 | case S3C2412_CLKSRC_PCLK: |
82 | s3c2412_i2s.master = 1; | ||
515 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; | 83 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; |
516 | iismod |= S3C2412_IISMOD_MASTER_INTERNAL; | 84 | iismod |= S3C2412_IISMOD_MASTER_INTERNAL; |
517 | break; | 85 | break; |
518 | case S3C2412_CLKSRC_I2SCLK: | 86 | case S3C2412_CLKSRC_I2SCLK: |
87 | s3c2412_i2s.master = 0; | ||
519 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; | 88 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; |
520 | iismod |= S3C2412_IISMOD_MASTER_EXTERNAL; | 89 | iismod |= S3C2412_IISMOD_MASTER_EXTERNAL; |
521 | break; | 90 | break; |
@@ -527,74 +96,6 @@ static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | |||
527 | return 0; | 96 | return 0; |
528 | } | 97 | } |
529 | 98 | ||
530 | /* | ||
531 | * Set S3C2412 Clock dividers | ||
532 | */ | ||
533 | static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, | ||
534 | int div_id, int div) | ||
535 | { | ||
536 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
537 | u32 reg; | ||
538 | |||
539 | DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); | ||
540 | |||
541 | switch (div_id) { | ||
542 | case S3C2412_DIV_BCLK: | ||
543 | reg = readl(i2s->regs + S3C2412_IISMOD); | ||
544 | reg &= ~S3C2412_IISMOD_BCLK_MASK; | ||
545 | writel(reg | div, i2s->regs + S3C2412_IISMOD); | ||
546 | |||
547 | DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); | ||
548 | break; | ||
549 | |||
550 | case S3C2412_DIV_RCLK: | ||
551 | if (div > 3) { | ||
552 | /* convert value to bit field */ | ||
553 | |||
554 | switch (div) { | ||
555 | case 256: | ||
556 | div = S3C2412_IISMOD_RCLK_256FS; | ||
557 | break; | ||
558 | |||
559 | case 384: | ||
560 | div = S3C2412_IISMOD_RCLK_384FS; | ||
561 | break; | ||
562 | |||
563 | case 512: | ||
564 | div = S3C2412_IISMOD_RCLK_512FS; | ||
565 | break; | ||
566 | |||
567 | case 768: | ||
568 | div = S3C2412_IISMOD_RCLK_768FS; | ||
569 | break; | ||
570 | |||
571 | default: | ||
572 | return -EINVAL; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | ||
577 | reg &= ~S3C2412_IISMOD_RCLK_MASK; | ||
578 | writel(reg | div, i2s->regs + S3C2412_IISMOD); | ||
579 | DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); | ||
580 | break; | ||
581 | |||
582 | case S3C2412_DIV_PRESCALER: | ||
583 | if (div >= 0) { | ||
584 | writel((div << 8) | S3C2412_IISPSR_PSREN, | ||
585 | i2s->regs + S3C2412_IISPSR); | ||
586 | } else { | ||
587 | writel(0x0, i2s->regs + S3C2412_IISPSR); | ||
588 | } | ||
589 | DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); | ||
590 | break; | ||
591 | |||
592 | default: | ||
593 | return -EINVAL; | ||
594 | } | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | 99 | ||
599 | struct clk *s3c2412_get_iisclk(void) | 100 | struct clk *s3c2412_get_iisclk(void) |
600 | { | 101 | { |
@@ -606,34 +107,30 @@ EXPORT_SYMBOL_GPL(s3c2412_get_iisclk); | |||
606 | static int s3c2412_i2s_probe(struct platform_device *pdev, | 107 | static int s3c2412_i2s_probe(struct platform_device *pdev, |
607 | struct snd_soc_dai *dai) | 108 | struct snd_soc_dai *dai) |
608 | { | 109 | { |
609 | DBG("Entered %s\n", __func__); | 110 | int ret; |
610 | 111 | ||
611 | s3c2412_i2s.dev = &pdev->dev; | 112 | pr_debug("Entered %s\n", __func__); |
612 | 113 | ||
613 | s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); | 114 | ret = s3c_i2sv2_probe(pdev, dai, &s3c2412_i2s, S3C2410_PA_IIS); |
614 | if (s3c2412_i2s.regs == NULL) | 115 | if (ret) |
615 | return -ENXIO; | 116 | return ret; |
616 | 117 | ||
617 | s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis"); | 118 | s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in; |
618 | if (s3c2412_i2s.iis_pclk == NULL) { | 119 | s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out; |
619 | DBG("failed to get iis_clock\n"); | ||
620 | iounmap(s3c2412_i2s.regs); | ||
621 | return -ENODEV; | ||
622 | } | ||
623 | 120 | ||
624 | s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk"); | 121 | s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk"); |
625 | if (s3c2412_i2s.iis_cclk == NULL) { | 122 | if (s3c2412_i2s.iis_cclk == NULL) { |
626 | DBG("failed to get i2sclk clock\n"); | 123 | pr_debug("failed to get i2sclk clock\n"); |
627 | iounmap(s3c2412_i2s.regs); | 124 | iounmap(s3c2412_i2s.regs); |
628 | return -ENODEV; | 125 | return -ENODEV; |
629 | } | 126 | } |
630 | 127 | ||
631 | clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll")); | 128 | /* Set MPLL as the source for IIS CLK */ |
632 | 129 | ||
633 | clk_enable(s3c2412_i2s.iis_pclk); | 130 | clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll")); |
634 | clk_enable(s3c2412_i2s.iis_cclk); | 131 | clk_enable(s3c2412_i2s.iis_cclk); |
635 | 132 | ||
636 | s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk; | 133 | s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk; |
637 | 134 | ||
638 | /* Configure the I2S pins in correct mode */ | 135 | /* Configure the I2S pins in correct mode */ |
639 | s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK); | 136 | s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK); |
@@ -642,66 +139,8 @@ static int s3c2412_i2s_probe(struct platform_device *pdev, | |||
642 | s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI); | 139 | s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI); |
643 | s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO); | 140 | s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO); |
644 | 141 | ||
645 | s3c2412_snd_txctrl(0); | ||
646 | s3c2412_snd_rxctrl(0); | ||
647 | |||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | #ifdef CONFIG_PM | ||
652 | static int s3c2412_i2s_suspend(struct snd_soc_dai *dai) | ||
653 | { | ||
654 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
655 | u32 iismod; | ||
656 | |||
657 | if (dai->active) { | ||
658 | i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD); | ||
659 | i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON); | ||
660 | i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR); | ||
661 | |||
662 | /* some basic suspend checks */ | ||
663 | |||
664 | iismod = readl(i2s->regs + S3C2412_IISMOD); | ||
665 | |||
666 | if (iismod & S3C2412_IISCON_RXDMA_ACTIVE) | ||
667 | pr_warning("%s: RXDMA active?\n", __func__); | ||
668 | |||
669 | if (iismod & S3C2412_IISCON_TXDMA_ACTIVE) | ||
670 | pr_warning("%s: TXDMA active?\n", __func__); | ||
671 | |||
672 | if (iismod & S3C2412_IISCON_IIS_ACTIVE) | ||
673 | pr_warning("%s: IIS active\n", __func__); | ||
674 | } | ||
675 | |||
676 | return 0; | ||
677 | } | ||
678 | |||
679 | static int s3c2412_i2s_resume(struct snd_soc_dai *dai) | ||
680 | { | ||
681 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
682 | |||
683 | pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n", | ||
684 | dai->active, i2s->suspend_iismod, i2s->suspend_iiscon); | ||
685 | |||
686 | if (dai->active) { | ||
687 | writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON); | ||
688 | writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD); | ||
689 | writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR); | ||
690 | |||
691 | writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH, | ||
692 | i2s->regs + S3C2412_IISFIC); | ||
693 | |||
694 | ndelay(250); | ||
695 | writel(0x0, i2s->regs + S3C2412_IISFIC); | ||
696 | |||
697 | } | ||
698 | |||
699 | return 0; | 142 | return 0; |
700 | } | 143 | } |
701 | #else | ||
702 | #define s3c2412_i2s_suspend NULL | ||
703 | #define s3c2412_i2s_resume NULL | ||
704 | #endif /* CONFIG_PM */ | ||
705 | 144 | ||
706 | #define S3C2412_I2S_RATES \ | 145 | #define S3C2412_I2S_RATES \ |
707 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ | 146 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ |
@@ -709,19 +148,13 @@ static int s3c2412_i2s_resume(struct snd_soc_dai *dai) | |||
709 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | 148 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) |
710 | 149 | ||
711 | static struct snd_soc_dai_ops s3c2412_i2s_dai_ops = { | 150 | static struct snd_soc_dai_ops s3c2412_i2s_dai_ops = { |
712 | .trigger = s3c2412_i2s_trigger, | ||
713 | .hw_params = s3c2412_i2s_hw_params, | ||
714 | .set_fmt = s3c2412_i2s_set_fmt, | ||
715 | .set_clkdiv = s3c2412_i2s_set_clkdiv, | ||
716 | .set_sysclk = s3c2412_i2s_set_sysclk, | 151 | .set_sysclk = s3c2412_i2s_set_sysclk, |
717 | }; | 152 | }; |
718 | 153 | ||
719 | struct snd_soc_dai s3c2412_i2s_dai = { | 154 | struct snd_soc_dai s3c2412_i2s_dai = { |
720 | .name = "s3c2412-i2s", | 155 | .name = "s3c2412-i2s", |
721 | .id = 0, | 156 | .id = 0, |
722 | .probe = s3c2412_i2s_probe, | 157 | .probe = s3c2412_i2s_probe, |
723 | .suspend = s3c2412_i2s_suspend, | ||
724 | .resume = s3c2412_i2s_resume, | ||
725 | .playback = { | 158 | .playback = { |
726 | .channels_min = 2, | 159 | .channels_min = 2, |
727 | .channels_max = 2, | 160 | .channels_max = 2, |
@@ -740,7 +173,7 @@ EXPORT_SYMBOL_GPL(s3c2412_i2s_dai); | |||
740 | 173 | ||
741 | static int __init s3c2412_i2s_init(void) | 174 | static int __init s3c2412_i2s_init(void) |
742 | { | 175 | { |
743 | return snd_soc_register_dai(&s3c2412_i2s_dai); | 176 | return s3c_i2sv2_register_dai(&s3c2412_i2s_dai); |
744 | } | 177 | } |
745 | module_init(s3c2412_i2s_init); | 178 | module_init(s3c2412_i2s_init); |
746 | 179 | ||
@@ -750,7 +183,6 @@ static void __exit s3c2412_i2s_exit(void) | |||
750 | } | 183 | } |
751 | module_exit(s3c2412_i2s_exit); | 184 | module_exit(s3c2412_i2s_exit); |
752 | 185 | ||
753 | |||
754 | /* Module information */ | 186 | /* Module information */ |
755 | MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); | 187 | MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); |
756 | MODULE_DESCRIPTION("S3C2412 I2S SoC Interface"); | 188 | MODULE_DESCRIPTION("S3C2412 I2S SoC Interface"); |
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h index aac08a25e541..92848e54be16 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.h +++ b/sound/soc/s3c24xx/s3c2412-i2s.h | |||
@@ -15,9 +15,11 @@ | |||
15 | #ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H | 15 | #ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H |
16 | #define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__ | 16 | #define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__ |
17 | 17 | ||
18 | #define S3C2412_DIV_BCLK (1) | 18 | #include "s3c-i2s-v2.h" |
19 | #define S3C2412_DIV_RCLK (2) | 19 | |
20 | #define S3C2412_DIV_PRESCALER (3) | 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 | ||
21 | 23 | ||
22 | #define S3C2412_CLKSRC_PCLK (0) | 24 | #define S3C2412_CLKSRC_PCLK (0) |
23 | #define S3C2412_CLKSRC_I2SCLK (1) | 25 | #define S3C2412_CLKSRC_I2SCLK (1) |
@@ -26,13 +28,4 @@ extern struct clk *s3c2412_get_iisclk(void); | |||
26 | 28 | ||
27 | extern struct snd_soc_dai s3c2412_i2s_dai; | 29 | extern struct snd_soc_dai s3c2412_i2s_dai; |
28 | 30 | ||
29 | struct s3c2412_rate_calc { | ||
30 | unsigned int clk_div; /* for prescaler */ | ||
31 | unsigned int fs_div; /* for root frame clock */ | ||
32 | }; | ||
33 | |||
34 | extern int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, | ||
35 | unsigned int *fstab, | ||
36 | unsigned int rate, struct clk *clk); | ||
37 | |||
38 | #endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */ | 31 | #endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */ |
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c index 83ea623234e7..3698f707c44d 100644 --- a/sound/soc/s3c24xx/s3c2443-ac97.c +++ b/sound/soc/s3c24xx/s3c2443-ac97.c | |||
@@ -31,7 +31,7 @@ | |||
31 | #include <plat/regs-ac97.h> | 31 | #include <plat/regs-ac97.h> |
32 | #include <mach/regs-gpio.h> | 32 | #include <mach/regs-gpio.h> |
33 | #include <mach/regs-clock.h> | 33 | #include <mach/regs-clock.h> |
34 | #include <mach/audio.h> | 34 | #include <plat/audio.h> |
35 | #include <asm/dma.h> | 35 | #include <asm/dma.h> |
36 | #include <mach/dma.h> | 36 | #include <mach/dma.h> |
37 | 37 | ||
@@ -360,6 +360,11 @@ static struct snd_soc_dai_ops s3c2443_ac97_dai_ops = { | |||
360 | .trigger = s3c2443_ac97_trigger, | 360 | .trigger = s3c2443_ac97_trigger, |
361 | }; | 361 | }; |
362 | 362 | ||
363 | static struct snd_soc_dai_ops s3c2443_ac97_mic_dai_ops = { | ||
364 | .hw_params = s3c2443_ac97_hw_mic_params, | ||
365 | .trigger = s3c2443_ac97_mic_trigger, | ||
366 | }; | ||
367 | |||
363 | struct snd_soc_dai s3c2443_ac97_dai[] = { | 368 | struct snd_soc_dai s3c2443_ac97_dai[] = { |
364 | { | 369 | { |
365 | .name = "s3c2443-ac97", | 370 | .name = "s3c2443-ac97", |
@@ -391,7 +396,7 @@ struct snd_soc_dai s3c2443_ac97_dai[] = { | |||
391 | .channels_max = 1, | 396 | .channels_max = 1, |
392 | .rates = s3c2443_AC97_RATES, | 397 | .rates = s3c2443_AC97_RATES, |
393 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | 398 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, |
394 | .ops = &s3c2443_ac97_dai_ops, | 399 | .ops = &s3c2443_ac97_mic_dai_ops, |
395 | }, | 400 | }, |
396 | }; | 401 | }; |
397 | EXPORT_SYMBOL_GPL(s3c2443_ac97_dai); | 402 | EXPORT_SYMBOL_GPL(s3c2443_ac97_dai); |
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index 4473fb584c4c..cc066964dad6 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c | |||
@@ -30,22 +30,15 @@ | |||
30 | #include <mach/hardware.h> | 30 | #include <mach/hardware.h> |
31 | #include <mach/regs-gpio.h> | 31 | #include <mach/regs-gpio.h> |
32 | #include <mach/regs-clock.h> | 32 | #include <mach/regs-clock.h> |
33 | #include <mach/audio.h> | 33 | #include <plat/audio.h> |
34 | #include <asm/dma.h> | 34 | #include <asm/dma.h> |
35 | #include <mach/dma.h> | 35 | #include <mach/dma.h> |
36 | 36 | ||
37 | #include <asm/plat-s3c24xx/regs-iis.h> | 37 | #include <plat/regs-iis.h> |
38 | 38 | ||
39 | #include "s3c24xx-pcm.h" | 39 | #include "s3c24xx-pcm.h" |
40 | #include "s3c24xx-i2s.h" | 40 | #include "s3c24xx-i2s.h" |
41 | 41 | ||
42 | #define S3C24XX_I2S_DEBUG 0 | ||
43 | #if S3C24XX_I2S_DEBUG | ||
44 | #define DBG(x...) printk(KERN_DEBUG "s3c24xx-i2s: " x) | ||
45 | #else | ||
46 | #define DBG(x...) | ||
47 | #endif | ||
48 | |||
49 | static struct s3c2410_dma_client s3c24xx_dma_client_out = { | 42 | static struct s3c2410_dma_client s3c24xx_dma_client_out = { |
50 | .name = "I2S PCM Stereo out" | 43 | .name = "I2S PCM Stereo out" |
51 | }; | 44 | }; |
@@ -84,13 +77,13 @@ static void s3c24xx_snd_txctrl(int on) | |||
84 | u32 iiscon; | 77 | u32 iiscon; |
85 | u32 iismod; | 78 | u32 iismod; |
86 | 79 | ||
87 | DBG("Entered %s\n", __func__); | 80 | pr_debug("Entered %s\n", __func__); |
88 | 81 | ||
89 | iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); | 82 | iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); |
90 | iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | 83 | iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); |
91 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 84 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); |
92 | 85 | ||
93 | DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon); | 86 | pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon); |
94 | 87 | ||
95 | if (on) { | 88 | if (on) { |
96 | iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE; | 89 | iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE; |
@@ -120,7 +113,7 @@ static void s3c24xx_snd_txctrl(int on) | |||
120 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); | 113 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); |
121 | } | 114 | } |
122 | 115 | ||
123 | DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon); | 116 | pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon); |
124 | } | 117 | } |
125 | 118 | ||
126 | static void s3c24xx_snd_rxctrl(int on) | 119 | static void s3c24xx_snd_rxctrl(int on) |
@@ -129,13 +122,13 @@ static void s3c24xx_snd_rxctrl(int on) | |||
129 | u32 iiscon; | 122 | u32 iiscon; |
130 | u32 iismod; | 123 | u32 iismod; |
131 | 124 | ||
132 | DBG("Entered %s\n", __func__); | 125 | pr_debug("Entered %s\n", __func__); |
133 | 126 | ||
134 | iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); | 127 | iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); |
135 | iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | 128 | iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); |
136 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 129 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); |
137 | 130 | ||
138 | DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon); | 131 | pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon); |
139 | 132 | ||
140 | if (on) { | 133 | if (on) { |
141 | iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE; | 134 | iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE; |
@@ -165,7 +158,7 @@ static void s3c24xx_snd_rxctrl(int on) | |||
165 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); | 158 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); |
166 | } | 159 | } |
167 | 160 | ||
168 | DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon); | 161 | pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon); |
169 | } | 162 | } |
170 | 163 | ||
171 | /* | 164 | /* |
@@ -177,7 +170,7 @@ static int s3c24xx_snd_lrsync(void) | |||
177 | u32 iiscon; | 170 | u32 iiscon; |
178 | int timeout = 50; /* 5ms */ | 171 | int timeout = 50; /* 5ms */ |
179 | 172 | ||
180 | DBG("Entered %s\n", __func__); | 173 | pr_debug("Entered %s\n", __func__); |
181 | 174 | ||
182 | while (1) { | 175 | while (1) { |
183 | iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | 176 | iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); |
@@ -197,7 +190,7 @@ static int s3c24xx_snd_lrsync(void) | |||
197 | */ | 190 | */ |
198 | static inline int s3c24xx_snd_is_clkmaster(void) | 191 | static inline int s3c24xx_snd_is_clkmaster(void) |
199 | { | 192 | { |
200 | DBG("Entered %s\n", __func__); | 193 | pr_debug("Entered %s\n", __func__); |
201 | 194 | ||
202 | return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1; | 195 | return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1; |
203 | } | 196 | } |
@@ -210,10 +203,10 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, | |||
210 | { | 203 | { |
211 | u32 iismod; | 204 | u32 iismod; |
212 | 205 | ||
213 | DBG("Entered %s\n", __func__); | 206 | pr_debug("Entered %s\n", __func__); |
214 | 207 | ||
215 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 208 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); |
216 | DBG("hw_params r: IISMOD: %lx \n", iismod); | 209 | pr_debug("hw_params r: IISMOD: %x \n", iismod); |
217 | 210 | ||
218 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 211 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
219 | case SND_SOC_DAIFMT_CBM_CFM: | 212 | case SND_SOC_DAIFMT_CBM_CFM: |
@@ -238,7 +231,7 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, | |||
238 | } | 231 | } |
239 | 232 | ||
240 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); | 233 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); |
241 | DBG("hw_params w: IISMOD: %lx \n", iismod); | 234 | pr_debug("hw_params w: IISMOD: %x \n", iismod); |
242 | return 0; | 235 | return 0; |
243 | } | 236 | } |
244 | 237 | ||
@@ -249,7 +242,7 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, | |||
249 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 242 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
250 | u32 iismod; | 243 | u32 iismod; |
251 | 244 | ||
252 | DBG("Entered %s\n", __func__); | 245 | pr_debug("Entered %s\n", __func__); |
253 | 246 | ||
254 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 247 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
255 | rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out; | 248 | rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out; |
@@ -258,7 +251,7 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, | |||
258 | 251 | ||
259 | /* Working copies of register */ | 252 | /* Working copies of register */ |
260 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 253 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); |
261 | DBG("hw_params r: IISMOD: %lx\n", iismod); | 254 | pr_debug("hw_params r: IISMOD: %x\n", iismod); |
262 | 255 | ||
263 | switch (params_format(params)) { | 256 | switch (params_format(params)) { |
264 | case SNDRV_PCM_FORMAT_S8: | 257 | case SNDRV_PCM_FORMAT_S8: |
@@ -276,7 +269,7 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, | |||
276 | } | 269 | } |
277 | 270 | ||
278 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); | 271 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); |
279 | DBG("hw_params w: IISMOD: %lx\n", iismod); | 272 | pr_debug("hw_params w: IISMOD: %x\n", iismod); |
280 | return 0; | 273 | return 0; |
281 | } | 274 | } |
282 | 275 | ||
@@ -285,7 +278,7 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |||
285 | { | 278 | { |
286 | int ret = 0; | 279 | int ret = 0; |
287 | 280 | ||
288 | DBG("Entered %s\n", __func__); | 281 | pr_debug("Entered %s\n", __func__); |
289 | 282 | ||
290 | switch (cmd) { | 283 | switch (cmd) { |
291 | case SNDRV_PCM_TRIGGER_START: | 284 | case SNDRV_PCM_TRIGGER_START: |
@@ -327,7 +320,7 @@ static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | |||
327 | { | 320 | { |
328 | u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 321 | u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); |
329 | 322 | ||
330 | DBG("Entered %s\n", __func__); | 323 | pr_debug("Entered %s\n", __func__); |
331 | 324 | ||
332 | iismod &= ~S3C2440_IISMOD_MPLL; | 325 | iismod &= ~S3C2440_IISMOD_MPLL; |
333 | 326 | ||
@@ -353,7 +346,7 @@ static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, | |||
353 | { | 346 | { |
354 | u32 reg; | 347 | u32 reg; |
355 | 348 | ||
356 | DBG("Entered %s\n", __func__); | 349 | pr_debug("Entered %s\n", __func__); |
357 | 350 | ||
358 | switch (div_id) { | 351 | switch (div_id) { |
359 | case S3C24XX_DIV_BCLK: | 352 | case S3C24XX_DIV_BCLK: |
@@ -389,7 +382,7 @@ EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate); | |||
389 | static int s3c24xx_i2s_probe(struct platform_device *pdev, | 382 | static int s3c24xx_i2s_probe(struct platform_device *pdev, |
390 | struct snd_soc_dai *dai) | 383 | struct snd_soc_dai *dai) |
391 | { | 384 | { |
392 | DBG("Entered %s\n", __func__); | 385 | pr_debug("Entered %s\n", __func__); |
393 | 386 | ||
394 | s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); | 387 | s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); |
395 | if (s3c24xx_i2s.regs == NULL) | 388 | if (s3c24xx_i2s.regs == NULL) |
@@ -397,7 +390,7 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev, | |||
397 | 390 | ||
398 | s3c24xx_i2s.iis_clk = clk_get(&pdev->dev, "iis"); | 391 | s3c24xx_i2s.iis_clk = clk_get(&pdev->dev, "iis"); |
399 | if (s3c24xx_i2s.iis_clk == NULL) { | 392 | if (s3c24xx_i2s.iis_clk == NULL) { |
400 | DBG("failed to get iis_clock\n"); | 393 | pr_err("failed to get iis_clock\n"); |
401 | iounmap(s3c24xx_i2s.regs); | 394 | iounmap(s3c24xx_i2s.regs); |
402 | return -ENODEV; | 395 | return -ENODEV; |
403 | } | 396 | } |
@@ -421,7 +414,7 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev, | |||
421 | #ifdef CONFIG_PM | 414 | #ifdef CONFIG_PM |
422 | static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai) | 415 | static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai) |
423 | { | 416 | { |
424 | DBG("Entered %s\n", __func__); | 417 | pr_debug("Entered %s\n", __func__); |
425 | 418 | ||
426 | s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | 419 | s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); |
427 | s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 420 | s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); |
@@ -435,7 +428,7 @@ static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai) | |||
435 | 428 | ||
436 | static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai) | 429 | static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai) |
437 | { | 430 | { |
438 | DBG("Entered %s\n", __func__); | 431 | pr_debug("Entered %s\n", __func__); |
439 | clk_enable(s3c24xx_i2s.iis_clk); | 432 | clk_enable(s3c24xx_i2s.iis_clk); |
440 | 433 | ||
441 | writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON); | 434 | writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON); |
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index ba1ae09dfaed..a9d68fa2b34a 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c | |||
@@ -29,17 +29,10 @@ | |||
29 | #include <asm/dma.h> | 29 | #include <asm/dma.h> |
30 | #include <mach/hardware.h> | 30 | #include <mach/hardware.h> |
31 | #include <mach/dma.h> | 31 | #include <mach/dma.h> |
32 | #include <mach/audio.h> | 32 | #include <plat/audio.h> |
33 | 33 | ||
34 | #include "s3c24xx-pcm.h" | 34 | #include "s3c24xx-pcm.h" |
35 | 35 | ||
36 | #define S3C24XX_PCM_DEBUG 0 | ||
37 | #if S3C24XX_PCM_DEBUG | ||
38 | #define DBG(x...) printk(KERN_DEBUG "s3c24xx-pcm: " x) | ||
39 | #else | ||
40 | #define DBG(x...) | ||
41 | #endif | ||
42 | |||
43 | static const struct snd_pcm_hardware s3c24xx_pcm_hardware = { | 36 | static const struct snd_pcm_hardware s3c24xx_pcm_hardware = { |
44 | .info = SNDRV_PCM_INFO_INTERLEAVED | | 37 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
45 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | 38 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
@@ -84,16 +77,16 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) | |||
84 | dma_addr_t pos = prtd->dma_pos; | 77 | dma_addr_t pos = prtd->dma_pos; |
85 | int ret; | 78 | int ret; |
86 | 79 | ||
87 | DBG("Entered %s\n", __func__); | 80 | pr_debug("Entered %s\n", __func__); |
88 | 81 | ||
89 | while (prtd->dma_loaded < prtd->dma_limit) { | 82 | while (prtd->dma_loaded < prtd->dma_limit) { |
90 | unsigned long len = prtd->dma_period; | 83 | unsigned long len = prtd->dma_period; |
91 | 84 | ||
92 | DBG("dma_loaded: %d\n", prtd->dma_loaded); | 85 | pr_debug("dma_loaded: %d\n", prtd->dma_loaded); |
93 | 86 | ||
94 | if ((pos + len) > prtd->dma_end) { | 87 | if ((pos + len) > prtd->dma_end) { |
95 | len = prtd->dma_end - pos; | 88 | len = prtd->dma_end - pos; |
96 | DBG(KERN_DEBUG "%s: corrected dma len %ld\n", | 89 | pr_debug(KERN_DEBUG "%s: corrected dma len %ld\n", |
97 | __func__, len); | 90 | __func__, len); |
98 | } | 91 | } |
99 | 92 | ||
@@ -119,7 +112,7 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, | |||
119 | struct snd_pcm_substream *substream = dev_id; | 112 | struct snd_pcm_substream *substream = dev_id; |
120 | struct s3c24xx_runtime_data *prtd; | 113 | struct s3c24xx_runtime_data *prtd; |
121 | 114 | ||
122 | DBG("Entered %s\n", __func__); | 115 | pr_debug("Entered %s\n", __func__); |
123 | 116 | ||
124 | if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) | 117 | if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) |
125 | return; | 118 | return; |
@@ -148,7 +141,7 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, | |||
148 | unsigned long totbytes = params_buffer_bytes(params); | 141 | unsigned long totbytes = params_buffer_bytes(params); |
149 | int ret = 0; | 142 | int ret = 0; |
150 | 143 | ||
151 | DBG("Entered %s\n", __func__); | 144 | pr_debug("Entered %s\n", __func__); |
152 | 145 | ||
153 | /* return if this is a bufferless transfer e.g. | 146 | /* return if this is a bufferless transfer e.g. |
154 | * codec <--> BT codec or GSM modem -- lg FIXME */ | 147 | * codec <--> BT codec or GSM modem -- lg FIXME */ |
@@ -161,14 +154,14 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, | |||
161 | /* prepare DMA */ | 154 | /* prepare DMA */ |
162 | prtd->params = dma; | 155 | prtd->params = dma; |
163 | 156 | ||
164 | DBG("params %p, client %p, channel %d\n", prtd->params, | 157 | pr_debug("params %p, client %p, channel %d\n", prtd->params, |
165 | prtd->params->client, prtd->params->channel); | 158 | prtd->params->client, prtd->params->channel); |
166 | 159 | ||
167 | ret = s3c2410_dma_request(prtd->params->channel, | 160 | ret = s3c2410_dma_request(prtd->params->channel, |
168 | prtd->params->client, NULL); | 161 | prtd->params->client, NULL); |
169 | 162 | ||
170 | if (ret < 0) { | 163 | if (ret < 0) { |
171 | DBG(KERN_ERR "failed to get dma channel\n"); | 164 | printk(KERN_ERR "failed to get dma channel\n"); |
172 | return ret; | 165 | return ret; |
173 | } | 166 | } |
174 | } | 167 | } |
@@ -196,7 +189,7 @@ static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream) | |||
196 | { | 189 | { |
197 | struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; | 190 | struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; |
198 | 191 | ||
199 | DBG("Entered %s\n", __func__); | 192 | pr_debug("Entered %s\n", __func__); |
200 | 193 | ||
201 | /* TODO - do we need to ensure DMA flushed */ | 194 | /* TODO - do we need to ensure DMA flushed */ |
202 | snd_pcm_set_runtime_buffer(substream, NULL); | 195 | snd_pcm_set_runtime_buffer(substream, NULL); |
@@ -214,7 +207,7 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) | |||
214 | struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; | 207 | struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; |
215 | int ret = 0; | 208 | int ret = 0; |
216 | 209 | ||
217 | DBG("Entered %s\n", __func__); | 210 | pr_debug("Entered %s\n", __func__); |
218 | 211 | ||
219 | /* return if this is a bufferless transfer e.g. | 212 | /* return if this is a bufferless transfer e.g. |
220 | * codec <--> BT codec or GSM modem -- lg FIXME */ | 213 | * codec <--> BT codec or GSM modem -- lg FIXME */ |
@@ -259,7 +252,7 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
259 | struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; | 252 | struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; |
260 | int ret = 0; | 253 | int ret = 0; |
261 | 254 | ||
262 | DBG("Entered %s\n", __func__); | 255 | pr_debug("Entered %s\n", __func__); |
263 | 256 | ||
264 | spin_lock(&prtd->lock); | 257 | spin_lock(&prtd->lock); |
265 | 258 | ||
@@ -297,7 +290,7 @@ s3c24xx_pcm_pointer(struct snd_pcm_substream *substream) | |||
297 | unsigned long res; | 290 | unsigned long res; |
298 | dma_addr_t src, dst; | 291 | dma_addr_t src, dst; |
299 | 292 | ||
300 | DBG("Entered %s\n", __func__); | 293 | pr_debug("Entered %s\n", __func__); |
301 | 294 | ||
302 | spin_lock(&prtd->lock); | 295 | spin_lock(&prtd->lock); |
303 | s3c2410_dma_getposition(prtd->params->channel, &src, &dst); | 296 | s3c2410_dma_getposition(prtd->params->channel, &src, &dst); |
@@ -309,7 +302,7 @@ s3c24xx_pcm_pointer(struct snd_pcm_substream *substream) | |||
309 | 302 | ||
310 | spin_unlock(&prtd->lock); | 303 | spin_unlock(&prtd->lock); |
311 | 304 | ||
312 | DBG("Pointer %x %x\n", src, dst); | 305 | pr_debug("Pointer %x %x\n", src, dst); |
313 | 306 | ||
314 | /* we seem to be getting the odd error from the pcm library due | 307 | /* we seem to be getting the odd error from the pcm library due |
315 | * to out-of-bounds pointers. this is maybe due to the dma engine | 308 | * to out-of-bounds pointers. this is maybe due to the dma engine |
@@ -330,7 +323,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) | |||
330 | struct snd_pcm_runtime *runtime = substream->runtime; | 323 | struct snd_pcm_runtime *runtime = substream->runtime; |
331 | struct s3c24xx_runtime_data *prtd; | 324 | struct s3c24xx_runtime_data *prtd; |
332 | 325 | ||
333 | DBG("Entered %s\n", __func__); | 326 | pr_debug("Entered %s\n", __func__); |
334 | 327 | ||
335 | snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware); | 328 | snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware); |
336 | 329 | ||
@@ -349,10 +342,10 @@ static int s3c24xx_pcm_close(struct snd_pcm_substream *substream) | |||
349 | struct snd_pcm_runtime *runtime = substream->runtime; | 342 | struct snd_pcm_runtime *runtime = substream->runtime; |
350 | struct s3c24xx_runtime_data *prtd = runtime->private_data; | 343 | struct s3c24xx_runtime_data *prtd = runtime->private_data; |
351 | 344 | ||
352 | DBG("Entered %s\n", __func__); | 345 | pr_debug("Entered %s\n", __func__); |
353 | 346 | ||
354 | if (!prtd) | 347 | if (!prtd) |
355 | DBG("s3c24xx_pcm_close called with prtd == NULL\n"); | 348 | pr_debug("s3c24xx_pcm_close called with prtd == NULL\n"); |
356 | 349 | ||
357 | kfree(prtd); | 350 | kfree(prtd); |
358 | 351 | ||
@@ -364,7 +357,7 @@ static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, | |||
364 | { | 357 | { |
365 | struct snd_pcm_runtime *runtime = substream->runtime; | 358 | struct snd_pcm_runtime *runtime = substream->runtime; |
366 | 359 | ||
367 | DBG("Entered %s\n", __func__); | 360 | pr_debug("Entered %s\n", __func__); |
368 | 361 | ||
369 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | 362 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, |
370 | runtime->dma_area, | 363 | runtime->dma_area, |
@@ -390,7 +383,7 @@ static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | |||
390 | struct snd_dma_buffer *buf = &substream->dma_buffer; | 383 | struct snd_dma_buffer *buf = &substream->dma_buffer; |
391 | size_t size = s3c24xx_pcm_hardware.buffer_bytes_max; | 384 | size_t size = s3c24xx_pcm_hardware.buffer_bytes_max; |
392 | 385 | ||
393 | DBG("Entered %s\n", __func__); | 386 | pr_debug("Entered %s\n", __func__); |
394 | 387 | ||
395 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | 388 | buf->dev.type = SNDRV_DMA_TYPE_DEV; |
396 | buf->dev.dev = pcm->card->dev; | 389 | buf->dev.dev = pcm->card->dev; |
@@ -409,7 +402,7 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | |||
409 | struct snd_dma_buffer *buf; | 402 | struct snd_dma_buffer *buf; |
410 | int stream; | 403 | int stream; |
411 | 404 | ||
412 | DBG("Entered %s\n", __func__); | 405 | pr_debug("Entered %s\n", __func__); |
413 | 406 | ||
414 | for (stream = 0; stream < 2; stream++) { | 407 | for (stream = 0; stream < 2; stream++) { |
415 | substream = pcm->streams[stream].substream; | 408 | substream = pcm->streams[stream].substream; |
@@ -433,7 +426,7 @@ static int s3c24xx_pcm_new(struct snd_card *card, | |||
433 | { | 426 | { |
434 | int ret = 0; | 427 | int ret = 0; |
435 | 428 | ||
436 | DBG("Entered %s\n", __func__); | 429 | pr_debug("Entered %s\n", __func__); |
437 | 430 | ||
438 | if (!card->dev->dma_mask) | 431 | if (!card->dev->dma_mask) |
439 | card->dev->dma_mask = &s3c24xx_pcm_dmamask; | 432 | card->dev->dma_mask = &s3c24xx_pcm_dmamask; |
diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c index a0a4d1832a14..8e79a416db57 100644 --- a/sound/soc/s3c24xx/s3c24xx_uda134x.c +++ b/sound/soc/s3c24xx/s3c24xx_uda134x.c | |||
@@ -22,7 +22,7 @@ | |||
22 | #include <sound/s3c24xx_uda134x.h> | 22 | #include <sound/s3c24xx_uda134x.h> |
23 | #include <sound/uda134x.h> | 23 | #include <sound/uda134x.h> |
24 | 24 | ||
25 | #include <asm/plat-s3c24xx/regs-iis.h> | 25 | #include <plat/regs-iis.h> |
26 | 26 | ||
27 | #include "s3c24xx-pcm.h" | 27 | #include "s3c24xx-pcm.h" |
28 | #include "s3c24xx-i2s.h" | 28 | #include "s3c24xx-i2s.h" |
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c new file mode 100644 index 000000000000..6e1e85dc1ff2 --- /dev/null +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c | |||
@@ -0,0 +1,220 @@ | |||
1 | /* sound/soc/s3c24xx/s3c64xx-i2s.c | ||
2 | * | ||
3 | * ALSA SoC Audio Layer - S3C64XX I2S driver | ||
4 | * | ||
5 | * Copyright 2008 Openmoko, Inc. | ||
6 | * Copyright 2008 Simtec Electronics | ||
7 | * Ben Dooks <ben@simtec.co.uk> | ||
8 | * http://armlinux.simtec.co.uk/ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/gpio.h> | ||
22 | #include <linux/io.h> | ||
23 | |||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/pcm_params.h> | ||
27 | #include <sound/initval.h> | ||
28 | #include <sound/soc.h> | ||
29 | |||
30 | #include <plat/regs-s3c2412-iis.h> | ||
31 | #include <plat/gpio-bank-d.h> | ||
32 | #include <plat/gpio-bank-e.h> | ||
33 | #include <plat/gpio-cfg.h> | ||
34 | #include <plat/audio.h> | ||
35 | |||
36 | #include <mach/map.h> | ||
37 | #include <mach/dma.h> | ||
38 | |||
39 | #include "s3c24xx-pcm.h" | ||
40 | #include "s3c64xx-i2s.h" | ||
41 | |||
42 | static struct s3c2410_dma_client s3c64xx_dma_client_out = { | ||
43 | .name = "I2S PCM Stereo out" | ||
44 | }; | ||
45 | |||
46 | static struct s3c2410_dma_client s3c64xx_dma_client_in = { | ||
47 | .name = "I2S PCM Stereo in" | ||
48 | }; | ||
49 | |||
50 | static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { | ||
51 | [0] = { | ||
52 | .channel = DMACH_I2S0_OUT, | ||
53 | .client = &s3c64xx_dma_client_out, | ||
54 | .dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISTXD, | ||
55 | .dma_size = 4, | ||
56 | }, | ||
57 | [1] = { | ||
58 | .channel = DMACH_I2S1_OUT, | ||
59 | .client = &s3c64xx_dma_client_out, | ||
60 | .dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISTXD, | ||
61 | .dma_size = 4, | ||
62 | }, | ||
63 | }; | ||
64 | |||
65 | static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_in[2] = { | ||
66 | [0] = { | ||
67 | .channel = DMACH_I2S0_IN, | ||
68 | .client = &s3c64xx_dma_client_in, | ||
69 | .dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISRXD, | ||
70 | .dma_size = 4, | ||
71 | }, | ||
72 | [1] = { | ||
73 | .channel = DMACH_I2S1_IN, | ||
74 | .client = &s3c64xx_dma_client_in, | ||
75 | .dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISRXD, | ||
76 | .dma_size = 4, | ||
77 | }, | ||
78 | }; | ||
79 | |||
80 | static struct s3c_i2sv2_info s3c64xx_i2s[2]; | ||
81 | |||
82 | static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) | ||
83 | { | ||
84 | return cpu_dai->private_data; | ||
85 | } | ||
86 | |||
87 | static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | ||
88 | int clk_id, unsigned int freq, int dir) | ||
89 | { | ||
90 | struct s3c_i2sv2_info *i2s = to_info(cpu_dai); | ||
91 | u32 iismod = readl(i2s->regs + S3C2412_IISMOD); | ||
92 | |||
93 | switch (clk_id) { | ||
94 | case S3C64XX_CLKSRC_PCLK: | ||
95 | iismod &= ~S3C64XX_IISMOD_IMS_SYSMUX; | ||
96 | break; | ||
97 | |||
98 | case S3C64XX_CLKSRC_MUX: | ||
99 | iismod |= S3C64XX_IISMOD_IMS_SYSMUX; | ||
100 | break; | ||
101 | |||
102 | default: | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | |||
106 | writel(iismod, i2s->regs + S3C2412_IISMOD); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | |||
112 | unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *dai) | ||
113 | { | ||
114 | struct s3c_i2sv2_info *i2s = to_info(dai); | ||
115 | |||
116 | return clk_get_rate(i2s->iis_cclk); | ||
117 | } | ||
118 | EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clockrate); | ||
119 | |||
120 | static int s3c64xx_i2s_probe(struct platform_device *pdev, | ||
121 | struct snd_soc_dai *dai) | ||
122 | { | ||
123 | struct device *dev = &pdev->dev; | ||
124 | struct s3c_i2sv2_info *i2s; | ||
125 | int ret; | ||
126 | |||
127 | dev_dbg(dev, "%s: probing dai %d\n", __func__, pdev->id); | ||
128 | |||
129 | if (pdev->id < 0 || pdev->id > ARRAY_SIZE(s3c64xx_i2s)) { | ||
130 | dev_err(dev, "id %d out of range\n", pdev->id); | ||
131 | return -EINVAL; | ||
132 | } | ||
133 | |||
134 | i2s = &s3c64xx_i2s[pdev->id]; | ||
135 | |||
136 | ret = s3c_i2sv2_probe(pdev, dai, i2s, | ||
137 | pdev->id ? S3C64XX_PA_IIS1 : S3C64XX_PA_IIS0); | ||
138 | if (ret) | ||
139 | return ret; | ||
140 | |||
141 | i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id]; | ||
142 | i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id]; | ||
143 | |||
144 | i2s->iis_cclk = clk_get(dev, "audio-bus"); | ||
145 | if (IS_ERR(i2s->iis_cclk)) { | ||
146 | dev_err(dev, "failed to get audio-bus"); | ||
147 | iounmap(i2s->regs); | ||
148 | return -ENODEV; | ||
149 | } | ||
150 | |||
151 | /* configure GPIO for i2s port */ | ||
152 | switch (pdev->id) { | ||
153 | case 0: | ||
154 | s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_I2S0_CLK); | ||
155 | s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_I2S0_CDCLK); | ||
156 | s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_I2S0_LRCLK); | ||
157 | s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_I2S0_DI); | ||
158 | s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_I2S0_D0); | ||
159 | break; | ||
160 | case 1: | ||
161 | s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_I2S1_CLK); | ||
162 | s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_I2S1_CDCLK); | ||
163 | s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_I2S1_LRCLK); | ||
164 | s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_I2S1_DI); | ||
165 | s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_I2S1_D0); | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | |||
172 | #define S3C64XX_I2S_RATES \ | ||
173 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ | ||
174 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | ||
175 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | ||
176 | |||
177 | #define S3C64XX_I2S_FMTS \ | ||
178 | (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE) | ||
179 | |||
180 | struct snd_soc_dai s3c64xx_i2s_dai = { | ||
181 | .name = "s3c64xx-i2s", | ||
182 | .id = 0, | ||
183 | .probe = s3c64xx_i2s_probe, | ||
184 | .playback = { | ||
185 | .channels_min = 2, | ||
186 | .channels_max = 2, | ||
187 | .rates = S3C64XX_I2S_RATES, | ||
188 | .formats = S3C64XX_I2S_FMTS, | ||
189 | }, | ||
190 | .capture = { | ||
191 | .channels_min = 2, | ||
192 | .channels_max = 2, | ||
193 | .rates = S3C64XX_I2S_RATES, | ||
194 | .formats = S3C64XX_I2S_FMTS, | ||
195 | }, | ||
196 | .ops = { | ||
197 | .set_sysclk = s3c64xx_i2s_set_sysclk, | ||
198 | }, | ||
199 | }; | ||
200 | EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai); | ||
201 | |||
202 | static int __init s3c64xx_i2s_init(void) | ||
203 | { | ||
204 | return s3c_i2sv2_register_dai(&s3c64xx_i2s_dai); | ||
205 | } | ||
206 | module_init(s3c64xx_i2s_init); | ||
207 | |||
208 | static void __exit s3c64xx_i2s_exit(void) | ||
209 | { | ||
210 | snd_soc_unregister_dai(&s3c64xx_i2s_dai); | ||
211 | } | ||
212 | module_exit(s3c64xx_i2s_exit); | ||
213 | |||
214 | /* Module information */ | ||
215 | MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); | ||
216 | MODULE_DESCRIPTION("S3C64XX I2S SoC Interface"); | ||
217 | MODULE_LICENSE("GPL"); | ||
218 | |||
219 | |||
220 | |||
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h new file mode 100644 index 000000000000..b7ffe3c38b66 --- /dev/null +++ b/sound/soc/s3c24xx/s3c64xx-i2s.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* sound/soc/s3c24xx/s3c64xx-i2s.h | ||
2 | * | ||
3 | * ALSA SoC Audio Layer - S3C64XX I2S driver | ||
4 | * | ||
5 | * Copyright 2008 Openmoko, Inc. | ||
6 | * Copyright 2008 Simtec Electronics | ||
7 | * Ben Dooks <ben@simtec.co.uk> | ||
8 | * http://armlinux.simtec.co.uk/ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef __SND_SOC_S3C24XX_S3C64XX_I2S_H | ||
16 | #define __SND_SOC_S3C24XX_S3C64XX_I2S_H __FILE__ | ||
17 | |||
18 | #include "s3c-i2s-v2.h" | ||
19 | |||
20 | #define S3C64XX_DIV_BCLK S3C_I2SV2_DIV_BCLK | ||
21 | #define S3C64XX_DIV_RCLK S3C_I2SV2_DIV_RCLK | ||
22 | #define S3C64XX_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER | ||
23 | |||
24 | #define S3C64XX_CLKSRC_PCLK (0) | ||
25 | #define S3C64XX_CLKSRC_MUX (1) | ||
26 | |||
27 | extern struct snd_soc_dai s3c64xx_i2s_dai; | ||
28 | |||
29 | extern unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *cpu_dai); | ||
30 | |||
31 | #endif /* __SND_SOC_S3C24XX_S3C64XX_I2S_H */ | ||
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4b8dbbfe2efb..735903a74675 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -332,7 +332,7 @@ static int dapm_new_mixer(struct snd_soc_codec *codec, | |||
332 | * kcontrol name. | 332 | * kcontrol name. |
333 | */ | 333 | */ |
334 | name_len = strlen(w->kcontrols[i].name) + 1; | 334 | name_len = strlen(w->kcontrols[i].name) + 1; |
335 | if (w->id == snd_soc_dapm_mixer) | 335 | if (w->id != snd_soc_dapm_mixer_named_ctl) |
336 | name_len += 1 + strlen(w->name); | 336 | name_len += 1 + strlen(w->name); |
337 | 337 | ||
338 | path->long_name = kmalloc(name_len, GFP_KERNEL); | 338 | path->long_name = kmalloc(name_len, GFP_KERNEL); |
@@ -341,15 +341,14 @@ static int dapm_new_mixer(struct snd_soc_codec *codec, | |||
341 | return -ENOMEM; | 341 | return -ENOMEM; |
342 | 342 | ||
343 | switch (w->id) { | 343 | switch (w->id) { |
344 | case snd_soc_dapm_mixer: | ||
345 | default: | 344 | default: |
346 | snprintf(path->long_name, name_len, "%s %s", | 345 | snprintf(path->long_name, name_len, "%s %s", |
347 | w->name, w->kcontrols[i].name); | 346 | w->name, w->kcontrols[i].name); |
348 | break; | 347 | break; |
349 | case snd_soc_dapm_mixer_named_ctl: | 348 | case snd_soc_dapm_mixer_named_ctl: |
350 | snprintf(path->long_name, name_len, "%s", | 349 | snprintf(path->long_name, name_len, "%s", |
351 | w->kcontrols[i].name); | 350 | w->kcontrols[i].name); |
352 | break; | 351 | break; |
353 | } | 352 | } |
354 | 353 | ||
355 | path->long_name[name_len - 1] = '\0'; | 354 | path->long_name[name_len - 1] = '\0'; |
@@ -523,6 +522,137 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, | |||
523 | EXPORT_SYMBOL_GPL(dapm_reg_event); | 522 | EXPORT_SYMBOL_GPL(dapm_reg_event); |
524 | 523 | ||
525 | /* | 524 | /* |
525 | * Scan a single DAPM widget for a complete audio path and update the | ||
526 | * power status appropriately. | ||
527 | */ | ||
528 | static int dapm_power_widget(struct snd_soc_codec *codec, int event, | ||
529 | struct snd_soc_dapm_widget *w) | ||
530 | { | ||
531 | int in, out, power_change, power, ret; | ||
532 | |||
533 | /* vmid - no action */ | ||
534 | if (w->id == snd_soc_dapm_vmid) | ||
535 | return 0; | ||
536 | |||
537 | /* active ADC */ | ||
538 | if (w->id == snd_soc_dapm_adc && w->active) { | ||
539 | in = is_connected_input_ep(w); | ||
540 | dapm_clear_walk(w->codec); | ||
541 | w->power = (in != 0) ? 1 : 0; | ||
542 | dapm_update_bits(w); | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | /* active DAC */ | ||
547 | if (w->id == snd_soc_dapm_dac && w->active) { | ||
548 | out = is_connected_output_ep(w); | ||
549 | dapm_clear_walk(w->codec); | ||
550 | w->power = (out != 0) ? 1 : 0; | ||
551 | dapm_update_bits(w); | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | /* pre and post event widgets */ | ||
556 | if (w->id == snd_soc_dapm_pre) { | ||
557 | if (!w->event) | ||
558 | return 0; | ||
559 | |||
560 | if (event == SND_SOC_DAPM_STREAM_START) { | ||
561 | ret = w->event(w, | ||
562 | NULL, SND_SOC_DAPM_PRE_PMU); | ||
563 | if (ret < 0) | ||
564 | return ret; | ||
565 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
566 | ret = w->event(w, | ||
567 | NULL, SND_SOC_DAPM_PRE_PMD); | ||
568 | if (ret < 0) | ||
569 | return ret; | ||
570 | } | ||
571 | return 0; | ||
572 | } | ||
573 | if (w->id == snd_soc_dapm_post) { | ||
574 | if (!w->event) | ||
575 | return 0; | ||
576 | |||
577 | if (event == SND_SOC_DAPM_STREAM_START) { | ||
578 | ret = w->event(w, | ||
579 | NULL, SND_SOC_DAPM_POST_PMU); | ||
580 | if (ret < 0) | ||
581 | return ret; | ||
582 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
583 | ret = w->event(w, | ||
584 | NULL, SND_SOC_DAPM_POST_PMD); | ||
585 | if (ret < 0) | ||
586 | return ret; | ||
587 | } | ||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | /* all other widgets */ | ||
592 | in = is_connected_input_ep(w); | ||
593 | dapm_clear_walk(w->codec); | ||
594 | out = is_connected_output_ep(w); | ||
595 | dapm_clear_walk(w->codec); | ||
596 | power = (out != 0 && in != 0) ? 1 : 0; | ||
597 | power_change = (w->power == power) ? 0 : 1; | ||
598 | w->power = power; | ||
599 | |||
600 | if (!power_change) | ||
601 | return 0; | ||
602 | |||
603 | /* call any power change event handlers */ | ||
604 | if (w->event) | ||
605 | pr_debug("power %s event for %s flags %x\n", | ||
606 | w->power ? "on" : "off", | ||
607 | w->name, w->event_flags); | ||
608 | |||
609 | /* power up pre event */ | ||
610 | if (power && w->event && | ||
611 | (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { | ||
612 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); | ||
613 | if (ret < 0) | ||
614 | return ret; | ||
615 | } | ||
616 | |||
617 | /* power down pre event */ | ||
618 | if (!power && w->event && | ||
619 | (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { | ||
620 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); | ||
621 | if (ret < 0) | ||
622 | return ret; | ||
623 | } | ||
624 | |||
625 | /* Lower PGA volume to reduce pops */ | ||
626 | if (w->id == snd_soc_dapm_pga && !power) | ||
627 | dapm_set_pga(w, power); | ||
628 | |||
629 | dapm_update_bits(w); | ||
630 | |||
631 | /* Raise PGA volume to reduce pops */ | ||
632 | if (w->id == snd_soc_dapm_pga && power) | ||
633 | dapm_set_pga(w, power); | ||
634 | |||
635 | /* power up post event */ | ||
636 | if (power && w->event && | ||
637 | (w->event_flags & SND_SOC_DAPM_POST_PMU)) { | ||
638 | ret = w->event(w, | ||
639 | NULL, SND_SOC_DAPM_POST_PMU); | ||
640 | if (ret < 0) | ||
641 | return ret; | ||
642 | } | ||
643 | |||
644 | /* power down post event */ | ||
645 | if (!power && w->event && | ||
646 | (w->event_flags & SND_SOC_DAPM_POST_PMD)) { | ||
647 | ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); | ||
648 | if (ret < 0) | ||
649 | return ret; | ||
650 | } | ||
651 | |||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | /* | ||
526 | * Scan each dapm widget for complete audio path. | 656 | * Scan each dapm widget for complete audio path. |
527 | * A complete path is a route that has valid endpoints i.e.:- | 657 | * A complete path is a route that has valid endpoints i.e.:- |
528 | * | 658 | * |
@@ -534,7 +664,7 @@ EXPORT_SYMBOL_GPL(dapm_reg_event); | |||
534 | static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | 664 | static int dapm_power_widgets(struct snd_soc_codec *codec, int event) |
535 | { | 665 | { |
536 | struct snd_soc_dapm_widget *w; | 666 | struct snd_soc_dapm_widget *w; |
537 | int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power; | 667 | int i, c = 1, *seq = NULL, ret = 0; |
538 | 668 | ||
539 | /* do we have a sequenced stream event */ | 669 | /* do we have a sequenced stream event */ |
540 | if (event == SND_SOC_DAPM_STREAM_START) { | 670 | if (event == SND_SOC_DAPM_STREAM_START) { |
@@ -545,135 +675,20 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
545 | seq = dapm_down_seq; | 675 | seq = dapm_down_seq; |
546 | } | 676 | } |
547 | 677 | ||
548 | for(i = 0; i < c; i++) { | 678 | for (i = 0; i < c; i++) { |
549 | list_for_each_entry(w, &codec->dapm_widgets, list) { | 679 | list_for_each_entry(w, &codec->dapm_widgets, list) { |
550 | 680 | ||
551 | /* is widget in stream order */ | 681 | /* is widget in stream order */ |
552 | if (seq && seq[i] && w->id != seq[i]) | 682 | if (seq && seq[i] && w->id != seq[i]) |
553 | continue; | 683 | continue; |
554 | 684 | ||
555 | /* vmid - no action */ | 685 | ret = dapm_power_widget(codec, event, w); |
556 | if (w->id == snd_soc_dapm_vmid) | 686 | if (ret != 0) |
557 | continue; | 687 | return ret; |
558 | |||
559 | /* active ADC */ | ||
560 | if (w->id == snd_soc_dapm_adc && w->active) { | ||
561 | in = is_connected_input_ep(w); | ||
562 | dapm_clear_walk(w->codec); | ||
563 | w->power = (in != 0) ? 1 : 0; | ||
564 | dapm_update_bits(w); | ||
565 | continue; | ||
566 | } | ||
567 | |||
568 | /* active DAC */ | ||
569 | if (w->id == snd_soc_dapm_dac && w->active) { | ||
570 | out = is_connected_output_ep(w); | ||
571 | dapm_clear_walk(w->codec); | ||
572 | w->power = (out != 0) ? 1 : 0; | ||
573 | dapm_update_bits(w); | ||
574 | continue; | ||
575 | } | ||
576 | |||
577 | /* pre and post event widgets */ | ||
578 | if (w->id == snd_soc_dapm_pre) { | ||
579 | if (!w->event) | ||
580 | continue; | ||
581 | |||
582 | if (event == SND_SOC_DAPM_STREAM_START) { | ||
583 | ret = w->event(w, | ||
584 | NULL, SND_SOC_DAPM_PRE_PMU); | ||
585 | if (ret < 0) | ||
586 | return ret; | ||
587 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
588 | ret = w->event(w, | ||
589 | NULL, SND_SOC_DAPM_PRE_PMD); | ||
590 | if (ret < 0) | ||
591 | return ret; | ||
592 | } | ||
593 | continue; | ||
594 | } | ||
595 | if (w->id == snd_soc_dapm_post) { | ||
596 | if (!w->event) | ||
597 | continue; | ||
598 | |||
599 | if (event == SND_SOC_DAPM_STREAM_START) { | ||
600 | ret = w->event(w, | ||
601 | NULL, SND_SOC_DAPM_POST_PMU); | ||
602 | if (ret < 0) | ||
603 | return ret; | ||
604 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
605 | ret = w->event(w, | ||
606 | NULL, SND_SOC_DAPM_POST_PMD); | ||
607 | if (ret < 0) | ||
608 | return ret; | ||
609 | } | ||
610 | continue; | ||
611 | } | ||
612 | |||
613 | /* all other widgets */ | ||
614 | in = is_connected_input_ep(w); | ||
615 | dapm_clear_walk(w->codec); | ||
616 | out = is_connected_output_ep(w); | ||
617 | dapm_clear_walk(w->codec); | ||
618 | power = (out != 0 && in != 0) ? 1 : 0; | ||
619 | power_change = (w->power == power) ? 0: 1; | ||
620 | w->power = power; | ||
621 | |||
622 | if (!power_change) | ||
623 | continue; | ||
624 | |||
625 | /* call any power change event handlers */ | ||
626 | if (w->event) | ||
627 | pr_debug("power %s event for %s flags %x\n", | ||
628 | w->power ? "on" : "off", | ||
629 | w->name, w->event_flags); | ||
630 | |||
631 | /* power up pre event */ | ||
632 | if (power && w->event && | ||
633 | (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { | ||
634 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); | ||
635 | if (ret < 0) | ||
636 | return ret; | ||
637 | } | ||
638 | |||
639 | /* power down pre event */ | ||
640 | if (!power && w->event && | ||
641 | (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { | ||
642 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); | ||
643 | if (ret < 0) | ||
644 | return ret; | ||
645 | } | ||
646 | |||
647 | /* Lower PGA volume to reduce pops */ | ||
648 | if (w->id == snd_soc_dapm_pga && !power) | ||
649 | dapm_set_pga(w, power); | ||
650 | |||
651 | dapm_update_bits(w); | ||
652 | |||
653 | /* Raise PGA volume to reduce pops */ | ||
654 | if (w->id == snd_soc_dapm_pga && power) | ||
655 | dapm_set_pga(w, power); | ||
656 | |||
657 | /* power up post event */ | ||
658 | if (power && w->event && | ||
659 | (w->event_flags & SND_SOC_DAPM_POST_PMU)) { | ||
660 | ret = w->event(w, | ||
661 | NULL, SND_SOC_DAPM_POST_PMU); | ||
662 | if (ret < 0) | ||
663 | return ret; | ||
664 | } | ||
665 | |||
666 | /* power down post event */ | ||
667 | if (!power && w->event && | ||
668 | (w->event_flags & SND_SOC_DAPM_POST_PMD)) { | ||
669 | ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); | ||
670 | if (ret < 0) | ||
671 | return ret; | ||
672 | } | ||
673 | } | 688 | } |
674 | } | 689 | } |
675 | 690 | ||
676 | return ret; | 691 | return 0; |
677 | } | 692 | } |
678 | 693 | ||
679 | #ifdef DEBUG | 694 | #ifdef DEBUG |
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ab64a30bedde..28346fb2e70c 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c | |||
@@ -14,6 +14,10 @@ | |||
14 | #include <sound/jack.h> | 14 | #include <sound/jack.h> |
15 | #include <sound/soc.h> | 15 | #include <sound/soc.h> |
16 | #include <sound/soc-dapm.h> | 16 | #include <sound/soc-dapm.h> |
17 | #include <linux/gpio.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/workqueue.h> | ||
20 | #include <linux/delay.h> | ||
17 | 21 | ||
18 | /** | 22 | /** |
19 | * snd_soc_jack_new - Create a new jack | 23 | * snd_soc_jack_new - Create a new jack |
@@ -136,3 +140,128 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, | |||
136 | return 0; | 140 | return 0; |
137 | } | 141 | } |
138 | EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins); | 142 | EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins); |
143 | |||
144 | #ifdef CONFIG_GPIOLIB | ||
145 | /* gpio detect */ | ||
146 | static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) | ||
147 | { | ||
148 | struct snd_soc_jack *jack = gpio->jack; | ||
149 | int enable; | ||
150 | int report; | ||
151 | |||
152 | if (gpio->debounce_time > 0) | ||
153 | mdelay(gpio->debounce_time); | ||
154 | |||
155 | enable = gpio_get_value(gpio->gpio); | ||
156 | if (gpio->invert) | ||
157 | enable = !enable; | ||
158 | |||
159 | if (enable) | ||
160 | report = gpio->report; | ||
161 | else | ||
162 | report = 0; | ||
163 | |||
164 | snd_soc_jack_report(jack, report, gpio->report); | ||
165 | } | ||
166 | |||
167 | /* irq handler for gpio pin */ | ||
168 | static irqreturn_t gpio_handler(int irq, void *data) | ||
169 | { | ||
170 | struct snd_soc_jack_gpio *gpio = data; | ||
171 | |||
172 | schedule_work(&gpio->work); | ||
173 | |||
174 | return IRQ_HANDLED; | ||
175 | } | ||
176 | |||
177 | /* gpio work */ | ||
178 | static void gpio_work(struct work_struct *work) | ||
179 | { | ||
180 | struct snd_soc_jack_gpio *gpio; | ||
181 | |||
182 | gpio = container_of(work, struct snd_soc_jack_gpio, work); | ||
183 | snd_soc_jack_gpio_detect(gpio); | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * snd_soc_jack_add_gpios - Associate GPIO pins with an ASoC jack | ||
188 | * | ||
189 | * @jack: ASoC jack | ||
190 | * @count: number of pins | ||
191 | * @gpios: array of gpio pins | ||
192 | * | ||
193 | * This function will request gpio, set data direction and request irq | ||
194 | * for each gpio in the array. | ||
195 | */ | ||
196 | int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, | ||
197 | struct snd_soc_jack_gpio *gpios) | ||
198 | { | ||
199 | int i, ret; | ||
200 | |||
201 | for (i = 0; i < count; i++) { | ||
202 | if (!gpio_is_valid(gpios[i].gpio)) { | ||
203 | printk(KERN_ERR "Invalid gpio %d\n", | ||
204 | gpios[i].gpio); | ||
205 | ret = -EINVAL; | ||
206 | goto undo; | ||
207 | } | ||
208 | if (!gpios[i].name) { | ||
209 | printk(KERN_ERR "No name for gpio %d\n", | ||
210 | gpios[i].gpio); | ||
211 | ret = -EINVAL; | ||
212 | goto undo; | ||
213 | } | ||
214 | |||
215 | ret = gpio_request(gpios[i].gpio, gpios[i].name); | ||
216 | if (ret) | ||
217 | goto undo; | ||
218 | |||
219 | ret = gpio_direction_input(gpios[i].gpio); | ||
220 | if (ret) | ||
221 | goto err; | ||
222 | |||
223 | ret = request_irq(gpio_to_irq(gpios[i].gpio), | ||
224 | gpio_handler, | ||
225 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
226 | jack->card->dev->driver->name, | ||
227 | &gpios[i]); | ||
228 | if (ret) | ||
229 | goto err; | ||
230 | |||
231 | INIT_WORK(&gpios[i].work, gpio_work); | ||
232 | gpios[i].jack = jack; | ||
233 | } | ||
234 | |||
235 | return 0; | ||
236 | |||
237 | err: | ||
238 | gpio_free(gpios[i].gpio); | ||
239 | undo: | ||
240 | snd_soc_jack_free_gpios(jack, i, gpios); | ||
241 | |||
242 | return ret; | ||
243 | } | ||
244 | EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios); | ||
245 | |||
246 | /** | ||
247 | * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack | ||
248 | * | ||
249 | * @jack: ASoC jack | ||
250 | * @count: number of pins | ||
251 | * @gpios: array of gpio pins | ||
252 | * | ||
253 | * Release gpio and irq resources for gpio pins associated with an ASoC jack. | ||
254 | */ | ||
255 | void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, | ||
256 | struct snd_soc_jack_gpio *gpios) | ||
257 | { | ||
258 | int i; | ||
259 | |||
260 | for (i = 0; i < count; i++) { | ||
261 | free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]); | ||
262 | gpio_free(gpios[i].gpio); | ||
263 | gpios[i].jack = NULL; | ||
264 | } | ||
265 | } | ||
266 | EXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios); | ||
267 | #endif /* CONFIG_GPIOLIB */ | ||