aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_sigmatel.c
diff options
context:
space:
mode:
authorMatt <matt@embeddedalley.com>2005-04-13 08:45:30 -0400
committerJaroslav Kysela <perex@suse.cz>2005-05-29 04:00:54 -0400
commit2f2f4251c9b123b2ab04da9e78ab6158535c2e38 (patch)
tree5b551aa143facbf53ce295ea5a71f03a4b6b7552 /sound/pci/hda/patch_sigmatel.c
parent1a12de1edf234b54ce94971d5604f85809c391b9 (diff)
[ALSA] add sigmatel codec support
HDA generic driver,HDA Codec driver Add initial SigmaTel codec support for 9200 and 922x. Note that this hda patch relies on the configuration default registers to be set correctly (normally by BIOS/firmware) in order for it to set up pin widgets properly. There's a test switch in the patch so it will work with the SigmaTel reference boards that are usually plugged into a system that doesn't set the configuration default registers. It supports 2 channel analog out and line/mic in. I plan to add >2 channel support and spdif support shortly. Please apply. Signed-off-by: Matt <matt@embeddedalley.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_sigmatel.c')
-rw-r--r--sound/pci/hda/patch_sigmatel.c560
1 files changed, 560 insertions, 0 deletions
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
new file mode 100644
index 000000000000..1534e20af63d
--- /dev/null
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -0,0 +1,560 @@
1/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
4 * HD audio interface patch for SigmaTel STAC92xx
5 *
6 * Copyright (c) 2005 Embedded Alley Solutions, Inc.
7 * <matt@embeddedalley.com>
8 *
9 * Based on patch_cmedia.c and patch_realtek.c
10 * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
11 *
12 * This driver is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This driver is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include <sound/driver.h>
28#include <linux/init.h>
29#include <linux/delay.h>
30#include <linux/slab.h>
31#include <linux/pci.h>
32#include <sound/core.h>
33#include "hda_codec.h"
34#include "hda_local.h"
35
36#undef STAC_TEST
37
38struct sigmatel_spec {
39 /* playback */
40 struct hda_multi_out multiout;
41 hda_nid_t playback_nid;
42
43 /* capture */
44 hda_nid_t *adc_nids;
45 hda_nid_t *mux_nids;
46 unsigned int num_adcs;
47 hda_nid_t capture_nid;
48
49 /* power management*/
50 hda_nid_t *pstate_nids;
51 unsigned int num_pstates;
52
53 /* pin widgets */
54 hda_nid_t *pin_nids;
55 unsigned int num_pins;
56#ifdef STAC_TEST
57 unsigned int *pin_configs;
58#endif
59
60 /* codec specific stuff */
61 struct hda_verb *init;
62 snd_kcontrol_new_t *mixer;
63
64 /* capture source */
65 const struct hda_input_mux *input_mux;
66 unsigned int cur_mux[2];
67
68 /* channel mode */
69 unsigned int num_ch_modes;
70 unsigned int cur_ch_mode;
71 const struct sigmatel_channel_mode *channel_modes;
72
73 struct hda_pcm pcm_rec[1]; /* PCM information */
74};
75
76static hda_nid_t stac9200_adc_nids[1] = {
77 0x03,
78};
79
80static hda_nid_t stac9200_mux_nids[1] = {
81 0x0c,
82};
83
84static hda_nid_t stac9200_dac_nids[1] = {
85 0x02,
86};
87
88static hda_nid_t stac9200_pstate_nids[3] = {
89 0x01, 0x02, 0x03,
90};
91
92static hda_nid_t stac9200_pin_nids[8] = {
93 0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
94};
95
96static hda_nid_t stac922x_adc_nids[2] = {
97 0x06, 0x07,
98};
99
100static hda_nid_t stac922x_mux_nids[2] = {
101 0x12, 0x13,
102};
103
104static hda_nid_t stac922x_dac_nids[4] = {
105 0x02, 0x03, 0x04, 0x05,
106};
107
108static hda_nid_t stac922x_pstate_nids[7] = {
109 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
110};
111
112static hda_nid_t stac922x_pin_nids[10] = {
113 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
114 0x0f, 0x10, 0x11, 0x15, 0x1b,
115};
116
117static int stac92xx_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
118{
119 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
120 struct sigmatel_spec *spec = codec->spec;
121 return snd_hda_input_mux_info(spec->input_mux, uinfo);
122}
123
124static int stac92xx_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
125{
126 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
127 struct sigmatel_spec *spec = codec->spec;
128 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
129
130 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
131 return 0;
132}
133
134static int stac92xx_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
135{
136 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
137 struct sigmatel_spec *spec = codec->spec;
138 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
139
140 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
141 spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
142}
143
144static struct hda_verb stac9200_ch2_init[] = {
145 /* set dac0mux for dac converter */
146 { 0x07, 0x701, 0x00},
147 {}
148};
149
150static struct hda_verb stac922x_ch2_init[] = {
151 /* set master volume and direct control */
152 { 0x16, 0x70f, 0xff},
153 {}
154};
155
156struct sigmatel_channel_mode {
157 unsigned int channels;
158 const struct hda_verb *sequence;
159};
160
161static snd_kcontrol_new_t stac9200_mixer[] = {
162 HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
163 HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
164 {
165 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
166 .name = "Input Source",
167 .count = 1,
168 .info = stac92xx_mux_enum_info,
169 .get = stac92xx_mux_enum_get,
170 .put = stac92xx_mux_enum_put,
171 },
172 HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
173 HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
174 HDA_CODEC_VOLUME("Input Mux Volume", 0x0c, 0, HDA_OUTPUT),
175 { } /* end */
176};
177
178static snd_kcontrol_new_t stac922x_mixer[] = {
179 HDA_CODEC_VOLUME("PCM Playback Volume", 0x2, 0x0, HDA_OUTPUT),
180 HDA_CODEC_MUTE("PCM Playback Switch", 0x2, 0x0, HDA_OUTPUT),
181 {
182 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
183 .name = "Input Source",
184 .count = 1,
185 .info = stac92xx_mux_enum_info,
186 .get = stac92xx_mux_enum_get,
187 .put = stac92xx_mux_enum_put,
188 },
189 HDA_CODEC_VOLUME("Capture Volume", 0x17, 0x0, HDA_INPUT),
190 HDA_CODEC_MUTE("Capture Switch", 0x17, 0x0, HDA_INPUT),
191 HDA_CODEC_VOLUME("Mux Capture Volume", 0x12, 0x0, HDA_OUTPUT),
192 { } /* end */
193};
194
195static struct hda_input_mux stac9200_input_mux = {
196 .num_items = 5,
197 .items = {
198 { "Port B", 0x0 },
199 { "Port C", 0x1 },
200 { "Port D", 0x2 },
201 { "Port A", 0x3 },
202 { "CD", 0x4 },
203 }
204};
205
206static struct hda_input_mux stac922x_input_mux = {
207 .num_items = 7,
208 .items = {
209 { "Port E", 0x0 },
210 { "CD", 0x1 },
211 { "Port F", 0x2 },
212 { "Port B", 0x3 },
213 { "Port C", 0x4 },
214 { "Port D", 0x5 },
215 { "Port A", 0x6 },
216 }
217};
218
219static int stac92xx_build_controls(struct hda_codec *codec)
220{
221 struct sigmatel_spec *spec = codec->spec;
222 int err;
223
224 err = snd_hda_add_new_ctls(codec, spec->mixer);
225 if (err < 0)
226 return err;
227
228 return 0;
229}
230
231#ifdef STAC_TEST
232static unsigned int stac9200_pin_configs[8] = {
233 0x40000100, 0x40000100, 0x0221401f, 0x01114010,
234 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
235};
236
237static unsigned int stac922x_pin_configs[14] = {
238 0x40000100, 0x40000100, 0x40000100, 0x01114010,
239 0x01813122, 0x40000100, 0x40000100, 0x40000100,
240 0x40000100, 0x40000100,
241};
242
243static void stac92xx_set_config_regs(struct hda_codec *codec)
244{
245 int i;
246 struct sigmatel_spec *spec = codec->spec;
247 unsigned int pin_cfg;
248
249 for (i=0; i < spec->num_pins; i++) {
250 snd_hda_codec_write(codec, spec->pin_nids[i], 0,
251 AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
252 spec->pin_configs[i] & 0x000000ff);
253 snd_hda_codec_write(codec, spec->pin_nids[i], 0,
254 AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
255 (spec->pin_configs[i] & 0x0000ff00) >> 8);
256 snd_hda_codec_write(codec, spec->pin_nids[i], 0,
257 AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
258 (spec->pin_configs[i] & 0x00ff0000) >> 16);
259 snd_hda_codec_write(codec, spec->pin_nids[i], 0,
260 AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
261 spec->pin_configs[i] >> 24);
262 pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
263 AC_VERB_GET_CONFIG_DEFAULT,
264 0x00);
265 printk("pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
266 }
267}
268#endif
269
270static int stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, unsigned int value)
271{
272 unsigned int pin_ctl;
273
274 pin_ctl = snd_hda_codec_read(codec, nid, 0,
275 AC_VERB_GET_PIN_WIDGET_CONTROL,
276 0x00);
277 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
278 pin_ctl | value);
279
280 return 0;
281}
282
283static int stac92xx_set_vref(struct hda_codec *codec, hda_nid_t nid)
284{
285 unsigned int vref_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) >> AC_PINCAP_VREF_SHIFT;
286 unsigned int vref_ctl = AC_PINCTL_VREF_HIZ;
287
288 if (vref_caps & AC_PINCAP_VREF_100)
289 vref_ctl = AC_PINCTL_VREF_100;
290 else if (vref_caps & AC_PINCAP_VREF_80)
291 vref_ctl = AC_PINCTL_VREF_80;
292 else if (vref_caps & AC_PINCAP_VREF_50)
293 vref_ctl = AC_PINCTL_VREF_50;
294 else if (vref_caps & AC_PINCAP_VREF_GRD)
295 vref_ctl = AC_PINCTL_VREF_GRD;
296
297 stac92xx_set_pinctl(codec, nid, vref_ctl);
298
299 return 0;
300}
301
302static int stac92xx_config_pin(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_cfg)
303{
304 switch((pin_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) {
305 case AC_JACK_HP_OUT:
306 /* Enable HP amp */
307 stac92xx_set_pinctl(codec, nid, AC_PINCTL_HP_EN);
308 /* Fall through */
309 case AC_JACK_LINE_OUT:
310 case AC_JACK_SPEAKER:
311 /* Enable output */
312 stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
313 break;
314 case AC_JACK_MIC_IN:
315 /* Set vref */
316 stac92xx_set_vref(codec, nid);
317 case AC_JACK_CD:
318 case AC_JACK_LINE_IN:
319 case AC_JACK_AUX:
320 /* Enable input */
321 stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
322 break;
323 }
324
325 return 0;
326}
327
328static int stac92xx_config_pins(struct hda_codec *codec)
329{
330 struct sigmatel_spec *spec = codec->spec;
331 int i;
332 unsigned int pin_cfg;
333
334 for (i=0; i < spec->num_pins; i++) {
335 /* Default to disabled */
336 snd_hda_codec_write(codec, spec->pin_nids[i], 0,
337 AC_VERB_SET_PIN_WIDGET_CONTROL,
338 0x00);
339
340 pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
341 AC_VERB_GET_CONFIG_DEFAULT,
342 0x00);
343 if (((pin_cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) == AC_JACK_PORT_NONE)
344 continue; /* Move on */
345
346 stac92xx_config_pin(codec, spec->pin_nids[i], pin_cfg);
347 }
348
349 return 0;
350}
351
352static int stac92xx_init(struct hda_codec *codec)
353{
354 struct sigmatel_spec *spec = codec->spec;
355 int i;
356
357 for (i=0; i < spec->num_pstates; i++)
358 snd_hda_codec_write(codec, spec->pstate_nids[i], 0,
359 AC_VERB_SET_POWER_STATE, 0x00);
360
361 mdelay(100);
362
363 snd_hda_sequence_write(codec, spec->init);
364
365#ifdef STAC_TEST
366 stac92xx_set_config_regs(codec);
367#endif
368
369 stac92xx_config_pins(codec);
370
371 return 0;
372}
373
374/*
375 * Analog playback callbacks
376 */
377static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
378 struct hda_codec *codec,
379 snd_pcm_substream_t *substream)
380{
381 struct sigmatel_spec *spec = codec->spec;
382 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
383}
384
385static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
386 struct hda_codec *codec,
387 unsigned int stream_tag,
388 unsigned int format,
389 snd_pcm_substream_t *substream)
390{
391 struct sigmatel_spec *spec = codec->spec;
392 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
393 format, substream);
394}
395
396static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
397 struct hda_codec *codec,
398 snd_pcm_substream_t *substream)
399{
400 struct sigmatel_spec *spec = codec->spec;
401 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
402}
403
404/*
405 * Analog capture callbacks
406 */
407static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
408 struct hda_codec *codec,
409 unsigned int stream_tag,
410 unsigned int format,
411 snd_pcm_substream_t *substream)
412{
413 struct sigmatel_spec *spec = codec->spec;
414
415 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
416 stream_tag, 0, format);
417 return 0;
418}
419
420static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
421 struct hda_codec *codec,
422 snd_pcm_substream_t *substream)
423{
424 struct sigmatel_spec *spec = codec->spec;
425
426 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
427 return 0;
428}
429
430static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
431 .substreams = 1,
432 .channels_min = 2,
433 .channels_max = 2,
434 .nid = 0x02, /* NID to query formats and rates */
435 .ops = {
436 .open = stac92xx_playback_pcm_open,
437 .prepare = stac92xx_playback_pcm_prepare,
438 .cleanup = stac92xx_playback_pcm_cleanup
439 },
440};
441
442static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
443 .substreams = 2,
444 .channels_min = 2,
445 .channels_max = 2,
446 .nid = 0x06, /* NID to query formats and rates */
447 .ops = {
448 .prepare = stac92xx_capture_pcm_prepare,
449 .cleanup = stac92xx_capture_pcm_cleanup
450 },
451};
452
453static int stac92xx_build_pcms(struct hda_codec *codec)
454{
455 struct sigmatel_spec *spec = codec->spec;
456 struct hda_pcm *info = spec->pcm_rec;
457
458 codec->num_pcms = 1;
459 codec->pcm_info = info;
460
461 info->name = "STAC92xx";
462 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
463 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->playback_nid;
464 info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
465 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->capture_nid;
466
467 return 0;
468}
469
470static void stac92xx_free(struct hda_codec *codec)
471{
472 kfree(codec->spec);
473}
474
475static struct hda_codec_ops stac92xx_patch_ops = {
476 .build_controls = stac92xx_build_controls,
477 .build_pcms = stac92xx_build_pcms,
478 .init = stac92xx_init,
479 .free = stac92xx_free,
480};
481
482static int patch_stac9200(struct hda_codec *codec)
483{
484 struct sigmatel_spec *spec;
485
486 spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
487 if (spec == NULL)
488 return -ENOMEM;
489
490 codec->spec = spec;
491
492 spec->multiout.max_channels = 2;
493 spec->multiout.num_dacs = 1;
494 spec->multiout.dac_nids = stac9200_dac_nids;
495 spec->adc_nids = stac9200_adc_nids;
496 spec->mux_nids = stac9200_mux_nids;
497 spec->input_mux = &stac9200_input_mux;
498 spec->pstate_nids = stac9200_pstate_nids;
499 spec->num_pstates = 3;
500 spec->pin_nids = stac9200_pin_nids;
501#ifdef STAC_TEST
502 spec->pin_configs = stac9200_pin_configs;
503#endif
504 spec->num_pins = 8;
505 spec->init = stac9200_ch2_init;
506 spec->mixer = stac9200_mixer;
507 spec->playback_nid = 0x02;
508 spec->capture_nid = 0x03;
509
510 codec->patch_ops = stac92xx_patch_ops;
511
512 return 0;
513}
514
515static int patch_stac922x(struct hda_codec *codec)
516{
517 struct sigmatel_spec *spec;
518
519 spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
520 if (spec == NULL)
521 return -ENOMEM;
522
523 codec->spec = spec;
524
525 spec->multiout.max_channels = 2;
526 spec->multiout.num_dacs = 4;
527 spec->multiout.dac_nids = stac922x_dac_nids;
528 spec->adc_nids = stac922x_adc_nids;
529 spec->mux_nids = stac922x_mux_nids;
530 spec->input_mux = &stac922x_input_mux;
531 spec->pstate_nids = stac922x_pstate_nids;
532 spec->num_pstates = 7;
533 spec->pin_nids = stac922x_pin_nids;
534#ifdef STAC_TEST
535 spec->pin_configs = stac922x_pin_configs;
536#endif
537 spec->num_pins = 10;
538 spec->init = stac922x_ch2_init;
539 spec->mixer = stac922x_mixer;
540 spec->playback_nid = 0x02;
541 spec->capture_nid = 0x06;
542
543 codec->patch_ops = stac92xx_patch_ops;
544
545 return 0;
546}
547
548/*
549 * patch entries
550 */
551struct hda_codec_preset snd_hda_preset_sigmatel[] = {
552 { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
553 { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
554 { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
555 { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
556 { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
557 { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
558 { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
559 {} /* terminator */
560};