aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorJon Smirl <jonsmirl@gmail.com>2009-05-23 19:13:07 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-05-24 08:15:21 -0400
commit3c166c7f1828f226c7f478758bf6c8ce8be1623f (patch)
tree6cbede14c240f7c4f91959761b93b5d17b13f6a9 /sound
parent0154724d487586241c1ad57cfd348ed2ff2274e2 (diff)
ASoC: Codec for STAC9766 used on the Efika
Datasheet: http://www.idt.com/products/getDoc.cfm?docID=13134007 Signed-off-by: Jon Smirl <jonsmirl@gmail.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/stac9766.c470
-rw-r--r--sound/soc/codecs/stac9766.h21
4 files changed, 497 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 7f78b65fc4e3..cb07d9b51b61 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -19,6 +19,7 @@ config SND_SOC_ALL_CODECS
19 select SND_SOC_CS4270 if I2C 19 select SND_SOC_CS4270 if I2C
20 select SND_SOC_PCM3008 20 select SND_SOC_PCM3008
21 select SND_SOC_SSM2602 if I2C 21 select SND_SOC_SSM2602 if I2C
22 select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
22 select SND_SOC_TLV320AIC23 if I2C 23 select SND_SOC_TLV320AIC23 if I2C
23 select SND_SOC_TLV320AIC26 if SPI_MASTER 24 select SND_SOC_TLV320AIC26 if SPI_MASTER
24 select SND_SOC_TLV320AIC3X if I2C 25 select SND_SOC_TLV320AIC3X if I2C
@@ -93,6 +94,9 @@ config SND_SOC_PCM3008
93config SND_SOC_SSM2602 94config SND_SOC_SSM2602
94 tristate 95 tristate
95 96
97config SND_SOC_STAC9766
98 tristate
99
96config SND_SOC_TLV320AIC23 100config SND_SOC_TLV320AIC23
97 tristate 101 tristate
98 102
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 70c55fa2c436..46c007cb5625 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -7,6 +7,7 @@ snd-soc-cs4270-objs := cs4270.o
7snd-soc-l3-objs := l3.o 7snd-soc-l3-objs := l3.o
8snd-soc-pcm3008-objs := pcm3008.o 8snd-soc-pcm3008-objs := pcm3008.o
9snd-soc-ssm2602-objs := ssm2602.o 9snd-soc-ssm2602-objs := ssm2602.o
10snd-soc-stac9766-objs := stac9766.o
10snd-soc-tlv320aic23-objs := tlv320aic23.o 11snd-soc-tlv320aic23-objs := tlv320aic23.o
11snd-soc-tlv320aic26-objs := tlv320aic26.o 12snd-soc-tlv320aic26-objs := tlv320aic26.o
12snd-soc-tlv320aic3x-objs := tlv320aic3x.o 13snd-soc-tlv320aic3x-objs := tlv320aic3x.o
@@ -42,6 +43,7 @@ obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
42obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o 43obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
43obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o 44obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
44obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o 45obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
46obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
45obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o 47obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
46obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o 48obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
47obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o 49obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
new file mode 100644
index 000000000000..7740cd5a7604
--- /dev/null
+++ b/sound/soc/codecs/stac9766.c
@@ -0,0 +1,470 @@
1/*
2 * stac9766.c -- ALSA SoC STAC9766 codec support
3 *
4 * Copyright 2009 Jon Smirl, Digispeaker
5 * Author: Jon Smirl <jonsmirl@gmail.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 * Features:-
13 *
14 * o Support for AC97 Codec, S/PDIF
15 */
16
17#include <linux/init.h>
18#include <linux/module.h>
19#include <linux/device.h>
20#include <sound/core.h>
21#include <sound/pcm.h>
22#include <sound/ac97_codec.h>
23#include <sound/initval.h>
24#include <sound/pcm_params.h>
25#include <sound/soc.h>
26#include <sound/tlv.h>
27#include <sound/soc-of-simple.h>
28
29#include "stac9766.h"
30
31#define STAC9766_VERSION "0.10"
32
33/*
34 * STAC9766 register cache
35 */
36static const u16 stac9766_reg[] = {
37 0x6A90, 0x8000, 0x8000, 0x8000, /* 6 */
38 0x0000, 0x0000, 0x8008, 0x8008, /* e */
39 0x8808, 0x8808, 0x8808, 0x8808, /* 16 */
40 0x8808, 0x0000, 0x8000, 0x0000, /* 1e */
41 0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
42 0x0a05, 0x0400, 0xbb80, 0x0000, /* 2e */
43 0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
44 0x0000, 0x2000, 0x0000, 0x0100, /* 3e */
45 0x0000, 0x0000, 0x0080, 0x0000, /* 46 */
46 0x0000, 0x0000, 0x0003, 0xffff, /* 4e */
47 0x0000, 0x0000, 0x0000, 0x0000, /* 56 */
48 0x4000, 0x0000, 0x0000, 0x0000, /* 5e */
49 0x1201, 0xFFFF, 0xFFFF, 0x0000, /* 66 */
50 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
51 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
52 0x0000, 0x0000, 0x0000, 0x0000, /* 7e */
53};
54
55static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX", "Line", "Stereo Mix", "Mono Mix", "Phone"};
56static const char *stac9766_mono_mux[] = {"Mix", "Mic"};
57static const char *stac9766_mic_mux[] = {"Mic1", "Mic2"};
58static const char *stac9766_SPDIF_mux[] = {"PCM", "ADC Record"};
59static const char *stac9766_popbypass_mux[] = {"Normal", "Bypass Mixer"};
60static const char *stac9766_record_all_mux[] = {"All analog", "Analog plus DAC"};
61static const char *stac9766_boost1[] = {"0dB", "10dB"};
62static const char *stac9766_boost2[] = {"0dB", "20dB"};
63static const char *stac9766_stereo_mic[] = {"Off", "On"};
64
65static const struct soc_enum stac9766_record_enum =
66 SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, stac9766_record_mux);
67static const struct soc_enum stac9766_mono_enum =
68 SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, stac9766_mono_mux);
69static const struct soc_enum stac9766_mic_enum =
70 SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, stac9766_mic_mux);
71static const struct soc_enum stac9766_SPDIF_enum =
72 SOC_ENUM_SINGLE(AC97_STAC_DA_CONTROL, 1, 2, stac9766_SPDIF_mux);
73static const struct soc_enum stac9766_popbypass_enum =
74 SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, stac9766_popbypass_mux);
75static const struct soc_enum stac9766_record_all_enum =
76 SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 12, 2, stac9766_record_all_mux);
77static const struct soc_enum stac9766_boost1_enum =
78 SOC_ENUM_SINGLE(AC97_MIC, 6, 2, stac9766_boost1); /* 0/10dB */
79static const struct soc_enum stac9766_boost2_enum =
80 SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 2, 2, stac9766_boost2); /* 0/20dB */
81static const struct soc_enum stac9766_stereo_mic_enum =
82 SOC_ENUM_SINGLE(AC97_STAC_STEREO_MIC, 2, 1, stac9766_stereo_mic);
83
84static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0);
85static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250);
86static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0);
87static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200);
88
89static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
90 SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv),
91 SOC_SINGLE("Speaker Switch", AC97_MASTER, 15, 1, 1),
92 SOC_DOUBLE_TLV("Headphone Volume", AC97_HEADPHONE, 8, 0, 31, 1, master_tlv),
93 SOC_SINGLE("Headphone Switch", AC97_HEADPHONE, 15, 1, 1),
94 SOC_SINGLE_TLV("Mono Out Volume", AC97_MASTER_MONO, 0, 31, 1, master_tlv),
95 SOC_SINGLE("Mono Out Switch", AC97_MASTER_MONO, 15, 1, 1),
96
97 SOC_DOUBLE_TLV("Record Volume", AC97_REC_GAIN, 8, 0, 15, 0, record_tlv),
98 SOC_SINGLE("Record Switch", AC97_REC_GAIN, 15, 1, 1),
99
100
101 SOC_SINGLE_TLV("Beep Volume", AC97_PC_BEEP, 1, 15, 1, beep_tlv),
102 SOC_SINGLE("Beep Switch", AC97_PC_BEEP, 15, 1, 1),
103 SOC_SINGLE("Beep Frequency", AC97_PC_BEEP, 5, 127, 1),
104 SOC_SINGLE_TLV("Phone Volume", AC97_PHONE, 0, 31, 1, mix_tlv),
105 SOC_SINGLE("Phone Switch", AC97_PHONE, 15, 1, 1),
106
107 SOC_ENUM("Mic Boost1", stac9766_boost1_enum),
108 SOC_ENUM("Mic Boost2", stac9766_boost2_enum),
109 SOC_SINGLE_TLV("Mic Volume", AC97_MIC, 0, 31, 1, mix_tlv),
110 SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1),
111 SOC_ENUM("Stereo Mic", stac9766_stereo_mic_enum),
112
113 SOC_DOUBLE_TLV("Line Volume", AC97_LINE, 8, 0, 31, 1, mix_tlv),
114 SOC_SINGLE("Line Switch", AC97_LINE, 15, 1, 1),
115 SOC_DOUBLE_TLV("CD Volume", AC97_CD, 8, 0, 31, 1, mix_tlv),
116 SOC_SINGLE("CD Switch", AC97_CD, 15, 1, 1),
117 SOC_DOUBLE_TLV("AUX Volume", AC97_AUX, 8, 0, 31, 1, mix_tlv),
118 SOC_SINGLE("AUX Switch", AC97_AUX, 15, 1, 1),
119 SOC_DOUBLE_TLV("Video Volume", AC97_VIDEO, 8, 0, 31, 1, mix_tlv),
120 SOC_SINGLE("Video Switch", AC97_VIDEO, 15, 1, 1),
121
122 SOC_DOUBLE_TLV("DAC Volume", AC97_PCM, 8, 0, 31, 1, mix_tlv),
123 SOC_SINGLE("DAC Switch", AC97_PCM, 15, 1, 1),
124 SOC_SINGLE("Loopback Test Switch", AC97_GENERAL_PURPOSE, 7, 1, 0),
125 SOC_SINGLE("3D Volume", AC97_3D_CONTROL, 3, 2, 1),
126 SOC_SINGLE("3D Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
127
128 SOC_ENUM("SPDIF Mux", stac9766_SPDIF_enum),
129 SOC_ENUM("Mic1/2 Mux", stac9766_mic_enum),
130 SOC_ENUM("Record All Mux", stac9766_record_all_enum),
131 SOC_ENUM("Record Mux", stac9766_record_enum),
132 SOC_ENUM("Mono Mux", stac9766_mono_enum),
133 SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum),
134};
135
136int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
137 unsigned int val)
138{
139 u16 *cache = codec->reg_cache;
140
141 if (reg > AC97_STAC_PAGE0) {
142 stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
143 soc_ac97_ops.write(codec->ac97, reg, val);
144 stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
145 return 0;
146 }
147 if (reg / 2 > ARRAY_SIZE(stac9766_reg))
148 return -EIO;
149
150 soc_ac97_ops.write(codec->ac97, reg, val);
151 cache[reg / 2] = val;
152 return 0;
153}
154
155unsigned int stac9766_ac97_read(struct snd_soc_codec *codec, unsigned int reg)
156{
157 u16 val = 0, *cache = codec->reg_cache;
158
159 if (reg > AC97_STAC_PAGE0) {
160 stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
161 val = soc_ac97_ops.read(codec->ac97, reg - AC97_STAC_PAGE0);
162 stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
163 return val;
164 }
165 if (reg / 2 > ARRAY_SIZE(stac9766_reg))
166 return -EIO;
167
168 if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
169 reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
170 reg == AC97_VENDOR_ID2) {
171
172 val = soc_ac97_ops.read(codec->ac97, reg);
173 return val;
174 }
175 return cache[reg / 2];
176}
177
178static int ac97_analog_prepare(struct snd_pcm_substream *substream,
179 struct snd_soc_dai *dai)
180{
181 struct snd_soc_codec *codec = dai->codec;
182 struct snd_pcm_runtime *runtime = substream->runtime;
183 unsigned short reg, vra;
184
185 vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
186
187 vra |= 0x1; /* enable variable rate audio */
188
189 stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
190
191 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
192 reg = AC97_PCM_FRONT_DAC_RATE;
193 else
194 reg = AC97_PCM_LR_ADC_RATE;
195
196 return stac9766_ac97_write(codec, reg, runtime->rate);
197}
198
199static int ac97_digital_prepare(struct snd_pcm_substream *substream,
200 struct snd_soc_dai *dai)
201{
202 struct snd_soc_codec *codec = dai->codec;
203 struct snd_pcm_runtime *runtime = substream->runtime;
204 unsigned short reg, vra;
205
206 stac9766_ac97_write(codec, AC97_SPDIF, 0x2002);
207
208 vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
209 vra |= 0x5; /* Enable VRA and SPDIF out */
210
211 stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
212
213 reg = AC97_PCM_FRONT_DAC_RATE;
214
215 return stac9766_ac97_write(codec, reg, runtime->rate);
216}
217
218static int ac97_digital_trigger(struct snd_pcm_substream *substream,
219 int cmd, struct snd_soc_dai *dai)
220{
221 struct snd_soc_codec *codec = dai->codec;
222 unsigned short vra;
223
224 switch (cmd) {
225 case SNDRV_PCM_TRIGGER_STOP:
226 vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
227 vra &= !0x04;
228 stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
229 break;
230 }
231 return 0;
232}
233
234static int stac9766_set_bias_level(struct snd_soc_codec *codec,
235 enum snd_soc_bias_level level)
236{
237 switch (level) {
238 case SND_SOC_BIAS_ON: /* full On */
239 case SND_SOC_BIAS_PREPARE: /* partial On */
240 case SND_SOC_BIAS_STANDBY: /* Off, with power */
241 stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000);
242 break;
243 case SND_SOC_BIAS_OFF: /* Off, without power */
244 /* disable everything including AC link */
245 stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff);
246 break;
247 }
248 codec->bias_level = level;
249 return 0;
250}
251
252int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
253{
254 if (try_warm && soc_ac97_ops.warm_reset) {
255 soc_ac97_ops.warm_reset(codec->ac97);
256 if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
257 return 1;
258 }
259
260 soc_ac97_ops.reset(codec->ac97);
261 if (soc_ac97_ops.warm_reset)
262 soc_ac97_ops.warm_reset(codec->ac97);
263 if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
264 return -EIO;
265 return 0;
266}
267
268static int stac9766_codec_suspend(struct platform_device *pdev,
269 pm_message_t state)
270{
271 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
272 struct snd_soc_codec *codec = socdev->card->codec;
273
274 stac9766_set_bias_level(codec, SND_SOC_BIAS_OFF);
275 return 0;
276}
277
278static int stac9766_codec_resume(struct platform_device *pdev)
279{
280 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
281 struct snd_soc_codec *codec = socdev->card->codec;
282 u16 id, reset;
283
284 reset = 0;
285 /* give the codec an AC97 warm reset to start the link */
286reset:
287 if (reset > 5) {
288 printk(KERN_ERR "stac9766 failed to resume");
289 return -EIO;
290 }
291 codec->ac97->bus->ops->warm_reset(codec->ac97);
292 id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
293 if (id != 0x4c13) {
294 stac9766_reset(codec, 0);
295 reset++;
296 goto reset;
297 }
298 stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
299
300 if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
301 stac9766_set_bias_level(codec, SND_SOC_BIAS_ON);
302
303 return 0;
304}
305
306static struct snd_soc_dai_ops stac9766_dai_ops_analog =
307{
308 .prepare = ac97_analog_prepare,
309};
310
311static struct snd_soc_dai_ops stac9766_dai_ops_digital =
312{
313 .prepare = ac97_digital_prepare,
314 .trigger = ac97_digital_trigger,
315};
316
317struct snd_soc_dai stac9766_dai[] = {
318{
319 .name = "stac9766 analog",
320 .id = 0,
321 .ac97_control = 1,
322
323 /* stream cababilities */
324 .playback = {
325 .stream_name = "stac9766 analog",
326 .channels_min = 1,
327 .channels_max = 2,
328 .rates = SNDRV_PCM_RATE_8000_48000,
329 .formats = SND_SOC_STD_AC97_FMTS,
330 },
331 .capture = {
332 .stream_name = "stac9766 analog",
333 .channels_min = 1,
334 .channels_max = 2,
335 .rates = SNDRV_PCM_RATE_8000_48000,
336 .formats = SND_SOC_STD_AC97_FMTS,
337 },
338 /* alsa ops */
339 .ops = &stac9766_dai_ops_analog,
340},
341{
342 .name = "stac9766 IEC958",
343 .id = 1,
344 .ac97_control = 1,
345
346 /* stream cababilities */
347 .playback = {
348 .stream_name = "stac9766 IEC958",
349 .channels_min = 1,
350 .channels_max = 2,
351 .rates = SNDRV_PCM_RATE_32000 | \
352 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
353 .formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,
354 },
355 /* alsa ops */
356 .ops = &stac9766_dai_ops_digital,
357}};
358EXPORT_SYMBOL_GPL(stac9766_dai);
359
360static int stac9766_codec_probe(struct platform_device *pdev)
361{
362 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
363 struct snd_soc_codec *codec;
364 int ret = 0;
365
366 printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
367
368 socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
369 if (socdev->card->codec == NULL)
370 return -ENOMEM;
371 codec = socdev->card->codec;
372 mutex_init(&codec->mutex);
373
374 codec->reg_cache = kmemdup(stac9766_reg, sizeof(stac9766_reg), GFP_KERNEL);
375 if (codec->reg_cache == NULL) {
376 ret = -ENOMEM;
377 goto cache_err;
378 }
379 codec->reg_cache_size = sizeof(stac9766_reg);
380 codec->reg_cache_step = 2;
381
382 codec->name = "STAC9766";
383 codec->owner = THIS_MODULE;
384 codec->dai = stac9766_dai;
385 codec->num_dai = ARRAY_SIZE(stac9766_dai);
386 codec->write = stac9766_ac97_write;
387 codec->read = stac9766_ac97_read;
388 codec->set_bias_level = stac9766_set_bias_level;
389 INIT_LIST_HEAD(&codec->dapm_widgets);
390 INIT_LIST_HEAD(&codec->dapm_paths);
391
392 ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
393 if (ret < 0)
394 goto codec_err;
395
396 /* register pcms */
397 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
398 if (ret < 0)
399 goto pcm_err;
400
401 /* do a cold reset for the controller and then try
402 * a warm reset followed by an optional cold reset for codec */
403 stac9766_reset(codec, 0);
404 ret = stac9766_reset(codec, 1);
405 if (ret < 0) {
406 printk(KERN_ERR "Failed to reset STAC9766: AC97 link error\n");
407 goto reset_err;
408 }
409
410 stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
411
412 snd_soc_add_controls(codec, stac9766_snd_ac97_controls, ARRAY_SIZE(
413 stac9766_snd_ac97_controls));
414
415 ret = snd_soc_init_card(socdev);
416 if (ret < 0)
417 goto reset_err;
418 return 0;
419
420reset_err:
421 snd_soc_free_pcms(socdev);
422pcm_err:
423 snd_soc_free_ac97_codec(codec);
424codec_err:
425 kfree(codec->private_data);
426cache_err:
427 kfree(socdev->card->codec);
428 socdev->card->codec = NULL;
429 return ret;
430}
431
432static int stac9766_codec_remove(struct platform_device *pdev)
433{
434 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
435 struct snd_soc_codec *codec = socdev->card->codec;
436
437 if (codec == NULL)
438 return 0;
439
440 snd_soc_free_pcms(socdev);
441 snd_soc_free_ac97_codec(codec);
442 kfree(codec->reg_cache);
443 kfree(codec);
444 return 0;
445}
446
447struct snd_soc_codec_device soc_codec_dev_stac9766 =
448{
449 .probe = stac9766_codec_probe,
450 .remove = stac9766_codec_remove,
451 .suspend = stac9766_codec_suspend,
452 .resume = stac9766_codec_resume,
453};
454EXPORT_SYMBOL_GPL(soc_codec_dev_stac9766);
455
456static int __init stac9766_modinit(void)
457{
458 return snd_soc_register_dais(stac9766_dai, ARRAY_SIZE(stac9766_dai));
459}
460module_init(stac9766_modinit);
461
462static void __exit stac9766_exit(void)
463{
464 snd_soc_unregister_dais(stac9766_dai, ARRAY_SIZE(stac9766_dai));
465}
466module_exit(stac9766_exit);
467
468MODULE_DESCRIPTION("ASoC stac9766 driver");
469MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
470MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/stac9766.h b/sound/soc/codecs/stac9766.h
new file mode 100644
index 000000000000..65642eb8393e
--- /dev/null
+++ b/sound/soc/codecs/stac9766.h
@@ -0,0 +1,21 @@
1/*
2 * stac9766.h -- STAC9766 Soc Audio driver
3 */
4
5#ifndef _STAC9766_H
6#define _STAC9766_H
7
8#define AC97_STAC_PAGE0 0x1000
9#define AC97_STAC_DA_CONTROL (AC97_STAC_PAGE0 | 0x6A)
10#define AC97_STAC_ANALOG_SPECIAL (AC97_STAC_PAGE0 | 0x6E)
11#define AC97_STAC_STEREO_MIC 0x78
12
13/* STAC9766 DAI ID's */
14#define STAC9766_DAI_AC97_ANALOG 0
15#define STAC9766_DAI_AC97_DIGITAL 1
16
17extern struct snd_soc_dai stac9766_dai[];
18extern struct snd_soc_codec_device soc_codec_dev_stac9766;
19
20
21#endif