blob: 297f740192798f058a9c4ead92f2e3bcebbecea8 [file] [log] [blame]
Matt2f2f4252005-04-13 14:45:30 +02001/*
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.
Matt Porter403d1942005-11-29 15:00:51 +01007 * Matt Porter <mporter@embeddedalley.com>
Matt2f2f4252005-04-13 14:45:30 +02008 *
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>
Mattc7d4b2f2005-06-27 14:59:41 +020033#include <sound/asoundef.h>
Matt2f2f4252005-04-13 14:45:30 +020034#include "hda_codec.h"
35#include "hda_local.h"
36
Matt4e550962005-07-04 17:51:39 +020037#define NUM_CONTROL_ALLOC 32
38#define STAC_HP_EVENT 0x37
Matt4e550962005-07-04 17:51:39 +020039
Takashi Iwaif5fcc132006-11-24 17:07:44 +010040enum {
41 STAC_REF,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020042 STAC_9200_DELL_D21,
43 STAC_9200_DELL_D22,
44 STAC_9200_DELL_D23,
45 STAC_9200_DELL_M21,
46 STAC_9200_DELL_M22,
47 STAC_9200_DELL_M23,
48 STAC_9200_DELL_M24,
49 STAC_9200_DELL_M25,
50 STAC_9200_DELL_M26,
51 STAC_9200_DELL_M27,
Takashi Iwaif5fcc132006-11-24 17:07:44 +010052 STAC_9200_MODELS
53};
54
55enum {
56 STAC_9205_REF,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020057 STAC_9205_DELL_M42,
Tobin Davisae0a8ed2007-08-13 15:50:29 +020058 STAC_9205_DELL_M43,
59 STAC_9205_DELL_M44,
60 STAC_9205_M43xx,
Takashi Iwaif5fcc132006-11-24 17:07:44 +010061 STAC_9205_MODELS
62};
63
64enum {
Tobin Davis8e21c342007-01-08 11:04:17 +010065 STAC_925x_REF,
66 STAC_M2_2,
67 STAC_MA6,
Tobin Davis2c11f952007-05-17 09:36:34 +020068 STAC_PA6,
Tobin Davis8e21c342007-01-08 11:04:17 +010069 STAC_925x_MODELS
70};
71
72enum {
Takashi Iwaif5fcc132006-11-24 17:07:44 +010073 STAC_D945_REF,
74 STAC_D945GTP3,
75 STAC_D945GTP5,
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +020076 STAC_INTEL_MAC_V1,
77 STAC_INTEL_MAC_V2,
78 STAC_INTEL_MAC_V3,
79 STAC_INTEL_MAC_V4,
80 STAC_INTEL_MAC_V5,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020081 /* for backward compatibility */
Takashi Iwaif5fcc132006-11-24 17:07:44 +010082 STAC_MACMINI,
Takashi Iwai3fc24d82007-02-16 13:27:18 +010083 STAC_MACBOOK,
Nicolas Boichat6f0778d2007-03-15 12:38:15 +010084 STAC_MACBOOK_PRO_V1,
85 STAC_MACBOOK_PRO_V2,
Sylvain FORETf16928f2007-04-27 14:22:36 +020086 STAC_IMAC_INTEL,
Takashi Iwai0dae0f82007-05-21 12:41:29 +020087 STAC_IMAC_INTEL_20,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020088 STAC_922X_DELL_D81,
89 STAC_922X_DELL_D82,
90 STAC_922X_DELL_M81,
91 STAC_922X_DELL_M82,
Takashi Iwaif5fcc132006-11-24 17:07:44 +010092 STAC_922X_MODELS
93};
94
95enum {
96 STAC_D965_REF,
97 STAC_D965_3ST,
98 STAC_D965_5ST,
Tobin Davis4ff076e2007-08-07 11:48:12 +020099 STAC_DELL_3ST,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100100 STAC_927X_MODELS
101};
Matt Porter403d1942005-11-29 15:00:51 +0100102
Matt2f2f4252005-04-13 14:45:30 +0200103struct sigmatel_spec {
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100104 struct snd_kcontrol_new *mixers[4];
Mattc7d4b2f2005-06-27 14:59:41 +0200105 unsigned int num_mixers;
106
Matt Porter403d1942005-11-29 15:00:51 +0100107 int board_config;
Mattc7d4b2f2005-06-27 14:59:41 +0200108 unsigned int surr_switch: 1;
Matt Porter403d1942005-11-29 15:00:51 +0100109 unsigned int line_switch: 1;
110 unsigned int mic_switch: 1;
Matt Porter3cc08dc2006-01-23 15:27:49 +0100111 unsigned int alt_switch: 1;
Takashi Iwai82bc9552006-03-21 11:24:42 +0100112 unsigned int hp_detect: 1;
Sam Revitch62fe78e2006-05-10 15:09:17 +0200113 unsigned int gpio_mute: 1;
Mattc7d4b2f2005-06-27 14:59:41 +0200114
Takashi Iwai82599802007-07-31 15:56:24 +0200115 unsigned int gpio_mask, gpio_data;
116
Matt2f2f4252005-04-13 14:45:30 +0200117 /* playback */
118 struct hda_multi_out multiout;
Matt Porter3cc08dc2006-01-23 15:27:49 +0100119 hda_nid_t dac_nids[5];
Matt2f2f4252005-04-13 14:45:30 +0200120
121 /* capture */
122 hda_nid_t *adc_nids;
Matt2f2f4252005-04-13 14:45:30 +0200123 unsigned int num_adcs;
Mattdabbed62005-06-14 10:19:34 +0200124 hda_nid_t *mux_nids;
125 unsigned int num_muxes;
Matt Porter8b657272006-10-26 17:12:59 +0200126 hda_nid_t *dmic_nids;
127 unsigned int num_dmics;
128 hda_nid_t dmux_nid;
Mattdabbed62005-06-14 10:19:34 +0200129 hda_nid_t dig_in_nid;
Matt2f2f4252005-04-13 14:45:30 +0200130
Matt2f2f4252005-04-13 14:45:30 +0200131 /* pin widgets */
132 hda_nid_t *pin_nids;
133 unsigned int num_pins;
Matt2f2f4252005-04-13 14:45:30 +0200134 unsigned int *pin_configs;
Richard Fish11b44bb2006-08-23 18:31:34 +0200135 unsigned int *bios_pin_configs;
Matt2f2f4252005-04-13 14:45:30 +0200136
137 /* codec specific stuff */
138 struct hda_verb *init;
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100139 struct snd_kcontrol_new *mixer;
Matt2f2f4252005-04-13 14:45:30 +0200140
141 /* capture source */
Matt Porter8b657272006-10-26 17:12:59 +0200142 struct hda_input_mux *dinput_mux;
143 unsigned int cur_dmux;
Mattc7d4b2f2005-06-27 14:59:41 +0200144 struct hda_input_mux *input_mux;
Matt Porter3cc08dc2006-01-23 15:27:49 +0100145 unsigned int cur_mux[3];
Matt2f2f4252005-04-13 14:45:30 +0200146
Matt Porter403d1942005-11-29 15:00:51 +0100147 /* i/o switches */
148 unsigned int io_switch[2];
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +0200149 unsigned int clfe_swap;
Matt2f2f4252005-04-13 14:45:30 +0200150
Mattc7d4b2f2005-06-27 14:59:41 +0200151 struct hda_pcm pcm_rec[2]; /* PCM information */
152
153 /* dynamic controls and input_mux */
154 struct auto_pin_cfg autocfg;
155 unsigned int num_kctl_alloc, num_kctl_used;
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100156 struct snd_kcontrol_new *kctl_alloc;
Matt Porter8b657272006-10-26 17:12:59 +0200157 struct hda_input_mux private_dimux;
Mattc7d4b2f2005-06-27 14:59:41 +0200158 struct hda_input_mux private_imux;
Matt2f2f4252005-04-13 14:45:30 +0200159};
160
161static hda_nid_t stac9200_adc_nids[1] = {
162 0x03,
163};
164
165static hda_nid_t stac9200_mux_nids[1] = {
166 0x0c,
167};
168
169static hda_nid_t stac9200_dac_nids[1] = {
170 0x02,
171};
172
Tobin Davis8e21c342007-01-08 11:04:17 +0100173static hda_nid_t stac925x_adc_nids[1] = {
174 0x03,
175};
176
177static hda_nid_t stac925x_mux_nids[1] = {
178 0x0f,
179};
180
181static hda_nid_t stac925x_dac_nids[1] = {
182 0x02,
183};
184
Tobin Davis2c11f952007-05-17 09:36:34 +0200185static hda_nid_t stac925x_dmic_nids[1] = {
186 0x15,
187};
188
Matt2f2f4252005-04-13 14:45:30 +0200189static hda_nid_t stac922x_adc_nids[2] = {
190 0x06, 0x07,
191};
192
193static hda_nid_t stac922x_mux_nids[2] = {
194 0x12, 0x13,
195};
196
Matt Porter3cc08dc2006-01-23 15:27:49 +0100197static hda_nid_t stac927x_adc_nids[3] = {
198 0x07, 0x08, 0x09
199};
200
201static hda_nid_t stac927x_mux_nids[3] = {
202 0x15, 0x16, 0x17
203};
204
Matt Porterf3302a52006-07-31 12:49:34 +0200205static hda_nid_t stac9205_adc_nids[2] = {
206 0x12, 0x13
207};
208
209static hda_nid_t stac9205_mux_nids[2] = {
210 0x19, 0x1a
211};
212
Takashi Iwai25494132007-03-12 12:36:16 +0100213static hda_nid_t stac9205_dmic_nids[2] = {
214 0x17, 0x18,
Matt Porter8b657272006-10-26 17:12:59 +0200215};
216
Mattc7d4b2f2005-06-27 14:59:41 +0200217static hda_nid_t stac9200_pin_nids[8] = {
Tobin Davis93ed1502006-09-01 21:03:12 +0200218 0x08, 0x09, 0x0d, 0x0e,
219 0x0f, 0x10, 0x11, 0x12,
Matt2f2f4252005-04-13 14:45:30 +0200220};
221
Tobin Davis8e21c342007-01-08 11:04:17 +0100222static hda_nid_t stac925x_pin_nids[8] = {
223 0x07, 0x08, 0x0a, 0x0b,
224 0x0c, 0x0d, 0x10, 0x11,
225};
226
Matt2f2f4252005-04-13 14:45:30 +0200227static hda_nid_t stac922x_pin_nids[10] = {
228 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
229 0x0f, 0x10, 0x11, 0x15, 0x1b,
230};
231
Matt Porter3cc08dc2006-01-23 15:27:49 +0100232static hda_nid_t stac927x_pin_nids[14] = {
233 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
234 0x0f, 0x10, 0x11, 0x12, 0x13,
235 0x14, 0x21, 0x22, 0x23,
236};
237
Matt Porterf3302a52006-07-31 12:49:34 +0200238static hda_nid_t stac9205_pin_nids[12] = {
239 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
240 0x0f, 0x14, 0x16, 0x17, 0x18,
241 0x21, 0x22,
Matt Porterf3302a52006-07-31 12:49:34 +0200242};
243
Matt Porter8b657272006-10-26 17:12:59 +0200244static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
245 struct snd_ctl_elem_info *uinfo)
246{
247 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
248 struct sigmatel_spec *spec = codec->spec;
249 return snd_hda_input_mux_info(spec->dinput_mux, uinfo);
250}
251
252static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
253 struct snd_ctl_elem_value *ucontrol)
254{
255 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
256 struct sigmatel_spec *spec = codec->spec;
257
258 ucontrol->value.enumerated.item[0] = spec->cur_dmux;
259 return 0;
260}
261
262static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
263 struct snd_ctl_elem_value *ucontrol)
264{
265 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
266 struct sigmatel_spec *spec = codec->spec;
267
268 return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
269 spec->dmux_nid, &spec->cur_dmux);
270}
271
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100272static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Matt2f2f4252005-04-13 14:45:30 +0200273{
274 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
275 struct sigmatel_spec *spec = codec->spec;
Mattc7d4b2f2005-06-27 14:59:41 +0200276 return snd_hda_input_mux_info(spec->input_mux, uinfo);
Matt2f2f4252005-04-13 14:45:30 +0200277}
278
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100279static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Matt2f2f4252005-04-13 14:45:30 +0200280{
281 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
282 struct sigmatel_spec *spec = codec->spec;
283 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
284
285 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
286 return 0;
287}
288
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100289static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Matt2f2f4252005-04-13 14:45:30 +0200290{
291 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
292 struct sigmatel_spec *spec = codec->spec;
293 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
294
Mattc7d4b2f2005-06-27 14:59:41 +0200295 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Matt2f2f4252005-04-13 14:45:30 +0200296 spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
297}
298
Mattc7d4b2f2005-06-27 14:59:41 +0200299static struct hda_verb stac9200_core_init[] = {
Matt2f2f4252005-04-13 14:45:30 +0200300 /* set dac0mux for dac converter */
Mattc7d4b2f2005-06-27 14:59:41 +0200301 { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
Matt2f2f4252005-04-13 14:45:30 +0200302 {}
303};
304
Tobin Davis8e21c342007-01-08 11:04:17 +0100305static struct hda_verb stac925x_core_init[] = {
306 /* set dac0mux for dac converter */
307 { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
308 {}
309};
310
Mattc7d4b2f2005-06-27 14:59:41 +0200311static struct hda_verb stac922x_core_init[] = {
Matt2f2f4252005-04-13 14:45:30 +0200312 /* set master volume and direct control */
Mattc7d4b2f2005-06-27 14:59:41 +0200313 { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
Matt2f2f4252005-04-13 14:45:30 +0200314 {}
315};
316
Tobin Davis93ed1502006-09-01 21:03:12 +0200317static struct hda_verb d965_core_init[] = {
Takashi Iwai19039bd2006-06-28 15:52:16 +0200318 /* set master volume and direct control */
Tobin Davis93ed1502006-09-01 21:03:12 +0200319 { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
Takashi Iwai19039bd2006-06-28 15:52:16 +0200320 /* unmute node 0x1b */
321 { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
322 /* select node 0x03 as DAC */
323 { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
324 {}
325};
326
Matt Porter3cc08dc2006-01-23 15:27:49 +0100327static struct hda_verb stac927x_core_init[] = {
328 /* set master volume and direct control */
329 { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
330 {}
331};
332
Matt Porterf3302a52006-07-31 12:49:34 +0200333static struct hda_verb stac9205_core_init[] = {
334 /* set master volume and direct control */
335 { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
336 {}
337};
338
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200339#define STAC_INPUT_SOURCE \
340 { \
341 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
342 .name = "Input Source", \
343 .count = 1, \
344 .info = stac92xx_mux_enum_info, \
345 .get = stac92xx_mux_enum_get, \
346 .put = stac92xx_mux_enum_put, \
347 }
348
349
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100350static struct snd_kcontrol_new stac9200_mixer[] = {
Matt2f2f4252005-04-13 14:45:30 +0200351 HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
352 HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200353 STAC_INPUT_SOURCE,
Matt2f2f4252005-04-13 14:45:30 +0200354 HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
355 HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
Mattc7d4b2f2005-06-27 14:59:41 +0200356 HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT),
Matt2f2f4252005-04-13 14:45:30 +0200357 { } /* end */
358};
359
Tobin Davis8e21c342007-01-08 11:04:17 +0100360static struct snd_kcontrol_new stac925x_mixer[] = {
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200361 STAC_INPUT_SOURCE,
Tobin Davis8e21c342007-01-08 11:04:17 +0100362 HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
363 HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_OUTPUT),
364 HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT),
365 { } /* end */
366};
367
Mattc7d4b2f2005-06-27 14:59:41 +0200368/* This needs to be generated dynamically based on sequence */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100369static struct snd_kcontrol_new stac922x_mixer[] = {
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200370 STAC_INPUT_SOURCE,
Matt2f2f4252005-04-13 14:45:30 +0200371 HDA_CODEC_VOLUME("Capture Volume", 0x17, 0x0, HDA_INPUT),
Takashi Iwai0fd17082006-01-13 18:46:21 +0100372 HDA_CODEC_MUTE("Capture Switch", 0x17, 0x0, HDA_INPUT),
Matt2f2f4252005-04-13 14:45:30 +0200373 HDA_CODEC_VOLUME("Mux Capture Volume", 0x12, 0x0, HDA_OUTPUT),
374 { } /* end */
375};
376
Takashi Iwai19039bd2006-06-28 15:52:16 +0200377/* This needs to be generated dynamically based on sequence */
378static struct snd_kcontrol_new stac9227_mixer[] = {
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200379 STAC_INPUT_SOURCE,
Takashi Iwai19039bd2006-06-28 15:52:16 +0200380 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
381 HDA_CODEC_MUTE("Capture Switch", 0x1b, 0x0, HDA_OUTPUT),
382 { } /* end */
383};
384
Takashi Iwaid1d985f2006-11-23 19:27:12 +0100385static struct snd_kcontrol_new stac927x_mixer[] = {
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200386 STAC_INPUT_SOURCE,
Matt Porter3cc08dc2006-01-23 15:27:49 +0100387 HDA_CODEC_VOLUME("InMux Capture Volume", 0x15, 0x0, HDA_OUTPUT),
388 HDA_CODEC_VOLUME("InVol Capture Volume", 0x18, 0x0, HDA_INPUT),
389 HDA_CODEC_MUTE("ADCMux Capture Switch", 0x1b, 0x0, HDA_OUTPUT),
390 { } /* end */
391};
392
Takashi Iwaid1d985f2006-11-23 19:27:12 +0100393static struct snd_kcontrol_new stac9205_mixer[] = {
Matt Porterf3302a52006-07-31 12:49:34 +0200394 {
395 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Matt Porter8b657272006-10-26 17:12:59 +0200396 .name = "Digital Input Source",
397 .count = 1,
398 .info = stac92xx_dmux_enum_info,
399 .get = stac92xx_dmux_enum_get,
400 .put = stac92xx_dmux_enum_put,
401 },
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200402 STAC_INPUT_SOURCE,
Matt Porterf3302a52006-07-31 12:49:34 +0200403 HDA_CODEC_VOLUME("InMux Capture Volume", 0x19, 0x0, HDA_OUTPUT),
404 HDA_CODEC_VOLUME("InVol Capture Volume", 0x1b, 0x0, HDA_INPUT),
405 HDA_CODEC_MUTE("ADCMux Capture Switch", 0x1d, 0x0, HDA_OUTPUT),
406 { } /* end */
407};
408
Matt2f2f4252005-04-13 14:45:30 +0200409static int stac92xx_build_controls(struct hda_codec *codec)
410{
411 struct sigmatel_spec *spec = codec->spec;
412 int err;
Mattc7d4b2f2005-06-27 14:59:41 +0200413 int i;
Matt2f2f4252005-04-13 14:45:30 +0200414
415 err = snd_hda_add_new_ctls(codec, spec->mixer);
416 if (err < 0)
417 return err;
Mattc7d4b2f2005-06-27 14:59:41 +0200418
419 for (i = 0; i < spec->num_mixers; i++) {
420 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
421 if (err < 0)
422 return err;
423 }
424
Mattdabbed62005-06-14 10:19:34 +0200425 if (spec->multiout.dig_out_nid) {
426 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
427 if (err < 0)
428 return err;
429 }
430 if (spec->dig_in_nid) {
431 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
432 if (err < 0)
433 return err;
434 }
435 return 0;
Matt2f2f4252005-04-13 14:45:30 +0200436}
437
Matt Porter403d1942005-11-29 15:00:51 +0100438static unsigned int ref9200_pin_configs[8] = {
Mattdabbed62005-06-14 10:19:34 +0200439 0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
Matt2f2f4252005-04-13 14:45:30 +0200440 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
441};
442
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200443/*
444 STAC 9200 pin configs for
445 102801A8
446 102801DE
447 102801E8
448*/
449static unsigned int dell9200_d21_pin_configs[8] = {
450 0x400001f0, 0x400001f1, 0x01a19021, 0x90100140,
451 0x01813122, 0x02214030, 0x01014010, 0x02a19020,
452};
453
454/*
455 STAC 9200 pin configs for
456 102801C0
457 102801C1
458*/
459static unsigned int dell9200_d22_pin_configs[8] = {
460 0x400001f0, 0x400001f1, 0x02a19021, 0x90100140,
461 0x400001f2, 0x0221401f, 0x01014010, 0x01813020,
462};
463
464/*
465 STAC 9200 pin configs for
466 102801C4 (Dell Dimension E310)
467 102801C5
468 102801C7
469 102801D9
470 102801DA
471 102801E3
472*/
473static unsigned int dell9200_d23_pin_configs[8] = {
474 0x400001f0, 0x400001f1, 0x01a19021, 0x90100140,
475 0x400001f2, 0x0221401f, 0x01014010, 0x01813020,
476};
477
478
479/*
480 STAC 9200-32 pin configs for
481 102801B5 (Dell Inspiron 630m)
482 102801D8 (Dell Inspiron 640m)
483*/
484static unsigned int dell9200_m21_pin_configs[8] = {
485 0x40c003fa, 0x03441340, 0x03a11020, 0x401003fc,
486 0x403003fd, 0x0321121f, 0x0321121f, 0x408003fb,
487};
488
489/*
490 STAC 9200-32 pin configs for
491 102801C2 (Dell Latitude D620)
492 102801C8
493 102801CC (Dell Latitude D820)
494 102801D4
495 102801D6
496*/
497static unsigned int dell9200_m22_pin_configs[8] = {
498 0x40c003fa, 0x0144131f, 0x03A11020, 0x401003fb,
499 0x40f000fc, 0x0321121f, 0x90170310, 0x90a70321,
500};
501
502/*
503 STAC 9200-32 pin configs for
504 102801CE (Dell XPS M1710)
505 102801CF (Dell Precision M90)
506*/
507static unsigned int dell9200_m23_pin_configs[8] = {
508 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310,
509 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc,
510};
511
512/*
513 STAC 9200-32 pin configs for
514 102801C9
515 102801CA
516 102801CB (Dell Latitude 120L)
517 102801D3
518*/
519static unsigned int dell9200_m24_pin_configs[8] = {
520 0x40c003fa, 0x404003fb, 0x03a11020, 0x401003fd,
521 0x403003fe, 0x0321121f, 0x90170310, 0x408003fc,
522};
523
524/*
525 STAC 9200-32 pin configs for
526 102801BD (Dell Inspiron E1505n)
527 102801EE
528 102801EF
529*/
530static unsigned int dell9200_m25_pin_configs[8] = {
531 0x40c003fa, 0x01441340, 0x04a11020, 0x401003fc,
532 0x403003fd, 0x0421121f, 0x90170310, 0x408003fb,
533};
534
535/*
536 STAC 9200-32 pin configs for
537 102801F5 (Dell Inspiron 1501)
538 102801F6
539*/
540static unsigned int dell9200_m26_pin_configs[8] = {
541 0x40c003fa, 0x404003fb, 0x04a11020, 0x401003fd,
542 0x403003fe, 0x0421121f, 0x90170310, 0x408003fc,
543};
544
545/*
546 STAC 9200-32
547 102801CD (Dell Inspiron E1705/9400)
548*/
549static unsigned int dell9200_m27_pin_configs[8] = {
550 0x40c003fa, 0x01441340, 0x04a11020, 0x90170310,
551 0x40f003fc, 0x0421121f, 0x90170310, 0x408003fb,
552};
553
554
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100555static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
556 [STAC_REF] = ref9200_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200557 [STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
558 [STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
559 [STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
560 [STAC_9200_DELL_M21] = dell9200_m21_pin_configs,
561 [STAC_9200_DELL_M22] = dell9200_m22_pin_configs,
562 [STAC_9200_DELL_M23] = dell9200_m23_pin_configs,
563 [STAC_9200_DELL_M24] = dell9200_m24_pin_configs,
564 [STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
565 [STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
566 [STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
Matt Porter403d1942005-11-29 15:00:51 +0100567};
568
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100569static const char *stac9200_models[STAC_9200_MODELS] = {
570 [STAC_REF] = "ref",
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200571 [STAC_9200_DELL_D21] = "dell-d21",
572 [STAC_9200_DELL_D22] = "dell-d22",
573 [STAC_9200_DELL_D23] = "dell-d23",
574 [STAC_9200_DELL_M21] = "dell-m21",
575 [STAC_9200_DELL_M22] = "dell-m22",
576 [STAC_9200_DELL_M23] = "dell-m23",
577 [STAC_9200_DELL_M24] = "dell-m24",
578 [STAC_9200_DELL_M25] = "dell-m25",
579 [STAC_9200_DELL_M26] = "dell-m26",
580 [STAC_9200_DELL_M27] = "dell-m27",
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100581};
582
583static struct snd_pci_quirk stac9200_cfg_tbl[] = {
584 /* SigmaTel reference board */
585 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
586 "DFI LanParty", STAC_REF),
Matt Portere7377072006-11-06 11:20:38 +0100587 /* Dell laptops have BIOS problem */
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200588 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8,
589 "unknown Dell", STAC_9200_DELL_D21),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100590 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200591 "Dell Inspiron 630m", STAC_9200_DELL_M21),
592 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bd,
593 "Dell Inspiron E1505n", STAC_9200_DELL_M25),
594 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c0,
595 "unknown Dell", STAC_9200_DELL_D22),
596 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c1,
597 "unknown Dell", STAC_9200_DELL_D22),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100598 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200599 "Dell Latitude D620", STAC_9200_DELL_M22),
600 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c5,
601 "unknown Dell", STAC_9200_DELL_D23),
602 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c7,
603 "unknown Dell", STAC_9200_DELL_D23),
604 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c8,
605 "unknown Dell", STAC_9200_DELL_M22),
606 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c9,
607 "unknown Dell", STAC_9200_DELL_M24),
608 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ca,
609 "unknown Dell", STAC_9200_DELL_M24),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100610 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200611 "Dell Latitude 120L", STAC_9200_DELL_M24),
Cory T. Tusar877b8662007-01-30 17:30:55 +0100612 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200613 "Dell Latitude D820", STAC_9200_DELL_M22),
Mikael Nilsson46f02ca2007-02-13 12:46:16 +0100614 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cd,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200615 "Dell Inspiron E1705/9400", STAC_9200_DELL_M27),
Mikael Nilsson46f02ca2007-02-13 12:46:16 +0100616 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ce,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200617 "Dell XPS M1710", STAC_9200_DELL_M23),
Takashi Iwaif0f96742007-02-14 00:59:17 +0100618 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200619 "Dell Precision M90", STAC_9200_DELL_M23),
620 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d3,
621 "unknown Dell", STAC_9200_DELL_M22),
622 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d4,
623 "unknown Dell", STAC_9200_DELL_M22),
Daniel T Chen8286c532007-05-15 11:46:23 +0200624 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200625 "unknown Dell", STAC_9200_DELL_M22),
Tobin Davis49c605d2007-05-17 09:38:24 +0200626 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200627 "Dell Inspiron 640m", STAC_9200_DELL_M21),
628 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d9,
629 "unknown Dell", STAC_9200_DELL_D23),
630 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01da,
631 "unknown Dell", STAC_9200_DELL_D23),
632 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01de,
633 "unknown Dell", STAC_9200_DELL_D21),
634 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e3,
635 "unknown Dell", STAC_9200_DELL_D23),
636 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e8,
637 "unknown Dell", STAC_9200_DELL_D21),
638 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ee,
639 "unknown Dell", STAC_9200_DELL_M25),
640 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ef,
641 "unknown Dell", STAC_9200_DELL_M25),
Tobin Davis49c605d2007-05-17 09:38:24 +0200642 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200643 "Dell Inspiron 1501", STAC_9200_DELL_M26),
644 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6,
645 "unknown Dell", STAC_9200_DELL_M26),
Tobin Davis49c605d2007-05-17 09:38:24 +0200646 /* Panasonic */
647 SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF),
648
Matt Porter403d1942005-11-29 15:00:51 +0100649 {} /* terminator */
650};
651
Tobin Davis8e21c342007-01-08 11:04:17 +0100652static unsigned int ref925x_pin_configs[8] = {
653 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
654 0x90a70320, 0x02214210, 0x400003f1, 0x9033032e,
655};
656
657static unsigned int stac925x_MA6_pin_configs[8] = {
658 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
659 0x90a70320, 0x90100211, 0x400003f1, 0x9033032e,
660};
661
Tobin Davis2c11f952007-05-17 09:36:34 +0200662static unsigned int stac925x_PA6_pin_configs[8] = {
663 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
664 0x50a103f0, 0x90100211, 0x400003f1, 0x9033032e,
665};
666
Tobin Davis8e21c342007-01-08 11:04:17 +0100667static unsigned int stac925xM2_2_pin_configs[8] = {
Steve Longerbeam7353e142007-05-29 14:36:17 +0200668 0x40c003f3, 0x424503f2, 0x04180011, 0x02a19020,
669 0x50a103f0, 0x90100212, 0x400003f1, 0x9033032e,
Tobin Davis8e21c342007-01-08 11:04:17 +0100670};
671
672static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
673 [STAC_REF] = ref925x_pin_configs,
674 [STAC_M2_2] = stac925xM2_2_pin_configs,
675 [STAC_MA6] = stac925x_MA6_pin_configs,
Tobin Davis2c11f952007-05-17 09:36:34 +0200676 [STAC_PA6] = stac925x_PA6_pin_configs,
Tobin Davis8e21c342007-01-08 11:04:17 +0100677};
678
679static const char *stac925x_models[STAC_925x_MODELS] = {
680 [STAC_REF] = "ref",
681 [STAC_M2_2] = "m2-2",
682 [STAC_MA6] = "m6",
Tobin Davis2c11f952007-05-17 09:36:34 +0200683 [STAC_PA6] = "pa6",
Tobin Davis8e21c342007-01-08 11:04:17 +0100684};
685
686static struct snd_pci_quirk stac925x_cfg_tbl[] = {
687 /* SigmaTel reference board */
688 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
Tobin Davis2c11f952007-05-17 09:36:34 +0200689 SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
Tobin Davis8e21c342007-01-08 11:04:17 +0100690 SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_REF),
691 SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_REF),
692 SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_MA6),
Tobin Davis2c11f952007-05-17 09:36:34 +0200693 SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_PA6),
Tobin Davis8e21c342007-01-08 11:04:17 +0100694 SND_PCI_QUIRK(0x1002, 0x437b, "Gateway MX6453", STAC_M2_2),
695 {} /* terminator */
696};
697
Matt Porter403d1942005-11-29 15:00:51 +0100698static unsigned int ref922x_pin_configs[10] = {
699 0x01014010, 0x01016011, 0x01012012, 0x0221401f,
700 0x01813122, 0x01011014, 0x01441030, 0x01c41030,
Matt2f2f4252005-04-13 14:45:30 +0200701 0x40000100, 0x40000100,
702};
703
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200704/*
705 STAC 922X pin configs for
706 102801A7
707 102801AB
708 102801A9
709 102801D1
710 102801D2
711*/
712static unsigned int dell_922x_d81_pin_configs[10] = {
713 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
714 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1,
715 0x01813122, 0x400001f2,
716};
717
718/*
719 STAC 922X pin configs for
720 102801AC
721 102801D0
722*/
723static unsigned int dell_922x_d82_pin_configs[10] = {
724 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
725 0x02a19020, 0x01117011, 0x01451140, 0x400001f0,
726 0x01813122, 0x400001f1,
727};
728
729/*
730 STAC 922X pin configs for
731 102801BF
732*/
733static unsigned int dell_922x_m81_pin_configs[10] = {
734 0x0321101f, 0x01112024, 0x01111222, 0x91174220,
735 0x03a11050, 0x01116221, 0x90a70330, 0x01452340,
736 0x40C003f1, 0x405003f0,
737};
738
739/*
740 STAC 9221 A1 pin configs for
741 102801D7 (Dell XPS M1210)
742*/
743static unsigned int dell_922x_m82_pin_configs[10] = {
744 0x0221121f, 0x408103ff, 0x02111212, 0x90100310,
745 0x408003f1, 0x02111211, 0x03451340, 0x40c003f2,
746 0x508003f3, 0x405003f4,
747};
748
Matt Porter403d1942005-11-29 15:00:51 +0100749static unsigned int d945gtp3_pin_configs[10] = {
Matt Porter869264c2006-01-25 19:20:50 +0100750 0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
Matt Porter403d1942005-11-29 15:00:51 +0100751 0x40000100, 0x40000100, 0x40000100, 0x40000100,
752 0x02a19120, 0x40000100,
753};
754
755static unsigned int d945gtp5_pin_configs[10] = {
Matt Porter869264c2006-01-25 19:20:50 +0100756 0x0221401f, 0x01011012, 0x01813024, 0x01014010,
757 0x01a19021, 0x01016011, 0x01452130, 0x40000100,
Matt Porter403d1942005-11-29 15:00:51 +0100758 0x02a19320, 0x40000100,
759};
760
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +0200761static unsigned int intel_mac_v1_pin_configs[10] = {
762 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
763 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
Takashi Iwai3fc24d82007-02-16 13:27:18 +0100764 0x400000fc, 0x400000fb,
765};
766
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +0200767static unsigned int intel_mac_v2_pin_configs[10] = {
768 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
769 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
Sylvain FORETf16928f2007-04-27 14:22:36 +0200770 0x400000fc, 0x400000fb,
771};
772
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +0200773static unsigned int intel_mac_v3_pin_configs[10] = {
774 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
775 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
776 0x400000fc, 0x400000fb,
777};
778
779static unsigned int intel_mac_v4_pin_configs[10] = {
780 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
781 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
782 0x400000fc, 0x400000fb,
783};
784
785static unsigned int intel_mac_v5_pin_configs[10] = {
786 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
787 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
788 0x400000fc, 0x400000fb,
Takashi Iwai0dae0f82007-05-21 12:41:29 +0200789};
790
Takashi Iwai76c08822007-06-19 12:17:42 +0200791
Takashi Iwai19039bd2006-06-28 15:52:16 +0200792static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100793 [STAC_D945_REF] = ref922x_pin_configs,
Takashi Iwai19039bd2006-06-28 15:52:16 +0200794 [STAC_D945GTP3] = d945gtp3_pin_configs,
795 [STAC_D945GTP5] = d945gtp5_pin_configs,
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +0200796 [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
797 [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
798 [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
799 [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
800 [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200801 /* for backward compatibility */
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +0200802 [STAC_MACMINI] = intel_mac_v3_pin_configs,
803 [STAC_MACBOOK] = intel_mac_v5_pin_configs,
804 [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
805 [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
806 [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
807 [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200808 [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,
809 [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,
810 [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs,
811 [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs,
Matt Porter403d1942005-11-29 15:00:51 +0100812};
813
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100814static const char *stac922x_models[STAC_922X_MODELS] = {
815 [STAC_D945_REF] = "ref",
816 [STAC_D945GTP5] = "5stack",
817 [STAC_D945GTP3] = "3stack",
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +0200818 [STAC_INTEL_MAC_V1] = "intel-mac-v1",
819 [STAC_INTEL_MAC_V2] = "intel-mac-v2",
820 [STAC_INTEL_MAC_V3] = "intel-mac-v3",
821 [STAC_INTEL_MAC_V4] = "intel-mac-v4",
822 [STAC_INTEL_MAC_V5] = "intel-mac-v5",
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200823 /* for backward compatibility */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100824 [STAC_MACMINI] = "macmini",
Takashi Iwai3fc24d82007-02-16 13:27:18 +0100825 [STAC_MACBOOK] = "macbook",
Nicolas Boichat6f0778d2007-03-15 12:38:15 +0100826 [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
827 [STAC_MACBOOK_PRO_V2] = "macbook-pro",
Sylvain FORETf16928f2007-04-27 14:22:36 +0200828 [STAC_IMAC_INTEL] = "imac-intel",
Takashi Iwai0dae0f82007-05-21 12:41:29 +0200829 [STAC_IMAC_INTEL_20] = "imac-intel-20",
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200830 [STAC_922X_DELL_D81] = "dell-d81",
831 [STAC_922X_DELL_D82] = "dell-d82",
832 [STAC_922X_DELL_M81] = "dell-m81",
833 [STAC_922X_DELL_M82] = "dell-m82",
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100834};
835
836static struct snd_pci_quirk stac922x_cfg_tbl[] = {
837 /* SigmaTel reference board */
838 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
839 "DFI LanParty", STAC_D945_REF),
840 /* Intel 945G based systems */
841 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101,
842 "Intel D945G", STAC_D945GTP3),
843 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202,
844 "Intel D945G", STAC_D945GTP3),
845 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606,
846 "Intel D945G", STAC_D945GTP3),
847 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601,
848 "Intel D945G", STAC_D945GTP3),
849 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111,
850 "Intel D945G", STAC_D945GTP3),
851 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115,
852 "Intel D945G", STAC_D945GTP3),
853 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116,
854 "Intel D945G", STAC_D945GTP3),
855 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117,
856 "Intel D945G", STAC_D945GTP3),
857 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118,
858 "Intel D945G", STAC_D945GTP3),
859 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119,
860 "Intel D945G", STAC_D945GTP3),
861 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826,
862 "Intel D945G", STAC_D945GTP3),
863 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049,
864 "Intel D945G", STAC_D945GTP3),
865 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055,
866 "Intel D945G", STAC_D945GTP3),
867 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048,
868 "Intel D945G", STAC_D945GTP3),
869 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110,
870 "Intel D945G", STAC_D945GTP3),
871 /* Intel D945G 5-stack systems */
872 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404,
873 "Intel D945G", STAC_D945GTP5),
874 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303,
875 "Intel D945G", STAC_D945GTP5),
876 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013,
877 "Intel D945G", STAC_D945GTP5),
878 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417,
879 "Intel D945G", STAC_D945GTP5),
880 /* Intel 945P based systems */
881 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b,
882 "Intel D945P", STAC_D945GTP3),
883 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112,
884 "Intel D945P", STAC_D945GTP3),
885 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d,
886 "Intel D945P", STAC_D945GTP3),
887 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909,
888 "Intel D945P", STAC_D945GTP3),
889 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505,
890 "Intel D945P", STAC_D945GTP3),
891 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
892 "Intel D945P", STAC_D945GTP5),
893 /* other systems */
894 /* Apple Mac Mini (early 2006) */
895 SND_PCI_QUIRK(0x8384, 0x7680,
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +0200896 "Mac Mini", STAC_INTEL_MAC_V3),
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200897 /* Dell systems */
898 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
899 "unknown Dell", STAC_922X_DELL_D81),
900 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a9,
901 "unknown Dell", STAC_922X_DELL_D81),
902 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ab,
903 "unknown Dell", STAC_922X_DELL_D81),
904 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ac,
905 "unknown Dell", STAC_922X_DELL_D82),
906 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bf,
907 "unknown Dell", STAC_922X_DELL_M81),
908 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d0,
909 "unknown Dell", STAC_922X_DELL_D82),
910 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d1,
911 "unknown Dell", STAC_922X_DELL_D81),
912 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d2,
913 "unknown Dell", STAC_922X_DELL_D81),
914 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
915 "Dell XPS M1210", STAC_922X_DELL_M82),
Matt Porter403d1942005-11-29 15:00:51 +0100916 {} /* terminator */
917};
918
Matt Porter3cc08dc2006-01-23 15:27:49 +0100919static unsigned int ref927x_pin_configs[14] = {
Tobin Davis93ed1502006-09-01 21:03:12 +0200920 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
921 0x01a19040, 0x01011012, 0x01016011, 0x0101201f,
922 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070,
923 0x01c42190, 0x40000100,
Matt Porter3cc08dc2006-01-23 15:27:49 +0100924};
925
Tobin Davis93ed1502006-09-01 21:03:12 +0200926static unsigned int d965_3st_pin_configs[14] = {
Tobin Davis81d3dbd2006-08-22 19:44:45 +0200927 0x0221401f, 0x02a19120, 0x40000100, 0x01014011,
928 0x01a19021, 0x01813024, 0x40000100, 0x40000100,
929 0x40000100, 0x40000100, 0x40000100, 0x40000100,
930 0x40000100, 0x40000100
931};
932
Tobin Davis93ed1502006-09-01 21:03:12 +0200933static unsigned int d965_5st_pin_configs[14] = {
934 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
935 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
936 0x40000100, 0x40000100, 0x40000100, 0x01442070,
937 0x40000100, 0x40000100
938};
939
Tobin Davis4ff076e2007-08-07 11:48:12 +0200940static unsigned int dell_3st_pin_configs[14] = {
941 0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
942 0x01111212, 0x01116211, 0x01813050, 0x01112214,
943 0x403003fa, 0x40000100, 0x40000100, 0x404003fb,
944 0x40c003fc, 0x40000100
945};
946
Tobin Davis93ed1502006-09-01 21:03:12 +0200947static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100948 [STAC_D965_REF] = ref927x_pin_configs,
Tobin Davis93ed1502006-09-01 21:03:12 +0200949 [STAC_D965_3ST] = d965_3st_pin_configs,
950 [STAC_D965_5ST] = d965_5st_pin_configs,
Tobin Davis4ff076e2007-08-07 11:48:12 +0200951 [STAC_DELL_3ST] = dell_3st_pin_configs,
Matt Porter3cc08dc2006-01-23 15:27:49 +0100952};
953
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100954static const char *stac927x_models[STAC_927X_MODELS] = {
955 [STAC_D965_REF] = "ref",
956 [STAC_D965_3ST] = "3stack",
957 [STAC_D965_5ST] = "5stack",
Tobin Davis4ff076e2007-08-07 11:48:12 +0200958 [STAC_DELL_3ST] = "dell-3stack",
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100959};
960
961static struct snd_pci_quirk stac927x_cfg_tbl[] = {
962 /* SigmaTel reference board */
963 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
964 "DFI LanParty", STAC_D965_REF),
Tobin Davis81d3dbd2006-08-22 19:44:45 +0200965 /* Intel 946 based systems */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100966 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST),
967 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST),
Tobin Davis93ed1502006-09-01 21:03:12 +0200968 /* 965 based 3 stack systems */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100969 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST),
970 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST),
971 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST),
972 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST),
973 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST),
974 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST),
975 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST),
976 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST),
977 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST),
978 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST),
979 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST),
980 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST),
981 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST),
982 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
983 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
984 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
Tobin Davis4ff076e2007-08-07 11:48:12 +0200985 /* Dell 3 stack systems */
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200986 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
Tobin Davis4ff076e2007-08-07 11:48:12 +0200987 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST),
988 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST),
Tobin Davis93ed1502006-09-01 21:03:12 +0200989 /* 965 based 5 stack systems */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100990 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
991 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
992 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
993 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST),
994 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST),
995 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST),
996 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST),
997 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST),
998 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST),
Matt Porter3cc08dc2006-01-23 15:27:49 +0100999 {} /* terminator */
1000};
1001
Matt Porterf3302a52006-07-31 12:49:34 +02001002static unsigned int ref9205_pin_configs[12] = {
1003 0x40000100, 0x40000100, 0x01016011, 0x01014010,
Matt Porter8b657272006-10-26 17:12:59 +02001004 0x01813122, 0x01a19021, 0x40000100, 0x40000100,
1005 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
Matt Porterf3302a52006-07-31 12:49:34 +02001006};
1007
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001008/*
1009 STAC 9205 pin configs for
1010 102801F1
1011 102801F2
1012 102801FC
1013 102801FD
1014 10280204
1015 1028021F
1016*/
1017static unsigned int dell_9205_m42_pin_configs[12] = {
1018 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
1019 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9,
1020 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE,
1021};
1022
1023/*
1024 STAC 9205 pin configs for
1025 102801F9
1026 102801FA
1027 102801FE
1028 102801FF (Dell Precision M4300)
1029 10280206
1030 10280200
1031 10280201
1032*/
1033static unsigned int dell_9205_m43_pin_configs[12] = {
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001034 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310,
1035 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9,
1036 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8,
1037};
1038
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001039static unsigned int dell_9205_m44_pin_configs[12] = {
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001040 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310,
1041 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9,
1042 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe,
1043};
1044
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001045static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001046 [STAC_9205_REF] = ref9205_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001047 [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
1048 [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
1049 [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001050 [STAC_9205_M43xx] = NULL,
Matt Porterf3302a52006-07-31 12:49:34 +02001051};
1052
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001053static const char *stac9205_models[STAC_9205_MODELS] = {
1054 [STAC_9205_REF] = "ref",
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001055 [STAC_9205_DELL_M42] = "dell-m42",
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001056 [STAC_9205_DELL_M43] = "dell-m43",
1057 [STAC_9205_DELL_M44] = "dell-m44",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001058};
1059
1060static struct snd_pci_quirk stac9205_cfg_tbl[] = {
1061 /* SigmaTel reference board */
1062 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1063 "DFI LanParty", STAC_9205_REF),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001064 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
1065 "unknown Dell", STAC_9205_DELL_M42),
1066 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
1067 "unknown Dell", STAC_9205_DELL_M42),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001068 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
1069 "Dell Precision", STAC_9205_M43xx),
1070 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
1071 "Dell Precision", STAC_9205_DELL_M43),
1072 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
1073 "Dell Precision", STAC_9205_DELL_M43),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001074 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
1075 "unknown Dell", STAC_9205_DELL_M42),
1076 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
1077 "unknown Dell", STAC_9205_DELL_M42),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001078 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe,
1079 "Dell Precision", STAC_9205_DELL_M43),
1080 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001081 "Dell Precision M4300", STAC_9205_DELL_M43),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001082 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
1083 "Dell Precision", STAC_9205_DELL_M43),
1084 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
1085 "Dell Inspiron", STAC_9205_DELL_M44),
1086 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
1087 "Dell Inspiron", STAC_9205_DELL_M44),
1088 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
1089 "Dell Inspiron", STAC_9205_DELL_M44),
1090 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
1091 "Dell Inspiron", STAC_9205_DELL_M44),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001092 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
1093 "unknown Dell", STAC_9205_DELL_M42),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001094 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
1095 "Dell Inspiron", STAC_9205_DELL_M44),
Matt Porterf3302a52006-07-31 12:49:34 +02001096 {} /* terminator */
1097};
1098
Richard Fish11b44bb2006-08-23 18:31:34 +02001099static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
1100{
1101 int i;
1102 struct sigmatel_spec *spec = codec->spec;
1103
1104 if (! spec->bios_pin_configs) {
1105 spec->bios_pin_configs = kcalloc(spec->num_pins,
1106 sizeof(*spec->bios_pin_configs), GFP_KERNEL);
1107 if (! spec->bios_pin_configs)
1108 return -ENOMEM;
1109 }
1110
1111 for (i = 0; i < spec->num_pins; i++) {
1112 hda_nid_t nid = spec->pin_nids[i];
1113 unsigned int pin_cfg;
1114
1115 pin_cfg = snd_hda_codec_read(codec, nid, 0,
1116 AC_VERB_GET_CONFIG_DEFAULT, 0x00);
1117 snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
1118 nid, pin_cfg);
1119 spec->bios_pin_configs[i] = pin_cfg;
1120 }
1121
1122 return 0;
1123}
1124
Matthew Ranostay87d48362007-07-17 11:52:24 +02001125static void stac92xx_set_config_reg(struct hda_codec *codec,
1126 hda_nid_t pin_nid, unsigned int pin_config)
1127{
1128 int i;
1129 snd_hda_codec_write(codec, pin_nid, 0,
1130 AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
1131 pin_config & 0x000000ff);
1132 snd_hda_codec_write(codec, pin_nid, 0,
1133 AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
1134 (pin_config & 0x0000ff00) >> 8);
1135 snd_hda_codec_write(codec, pin_nid, 0,
1136 AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
1137 (pin_config & 0x00ff0000) >> 16);
1138 snd_hda_codec_write(codec, pin_nid, 0,
1139 AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
1140 pin_config >> 24);
1141 i = snd_hda_codec_read(codec, pin_nid, 0,
1142 AC_VERB_GET_CONFIG_DEFAULT,
1143 0x00);
1144 snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
1145 pin_nid, i);
1146}
1147
Matt2f2f4252005-04-13 14:45:30 +02001148static void stac92xx_set_config_regs(struct hda_codec *codec)
1149{
1150 int i;
1151 struct sigmatel_spec *spec = codec->spec;
Matt2f2f4252005-04-13 14:45:30 +02001152
Matthew Ranostay87d48362007-07-17 11:52:24 +02001153 if (!spec->pin_configs)
1154 return;
Richard Fish11b44bb2006-08-23 18:31:34 +02001155
Matthew Ranostay87d48362007-07-17 11:52:24 +02001156 for (i = 0; i < spec->num_pins; i++)
1157 stac92xx_set_config_reg(codec, spec->pin_nids[i],
1158 spec->pin_configs[i]);
Matt2f2f4252005-04-13 14:45:30 +02001159}
Matt2f2f4252005-04-13 14:45:30 +02001160
Takashi Iwai82599802007-07-31 15:56:24 +02001161static void stac92xx_enable_gpio_mask(struct hda_codec *codec)
Matthew Ranostay92a22be2007-06-19 16:48:28 +02001162{
Takashi Iwai82599802007-07-31 15:56:24 +02001163 struct sigmatel_spec *spec = codec->spec;
Matthew Ranostay87d48362007-07-17 11:52:24 +02001164 /* Configure GPIOx as output */
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001165 snd_hda_codec_write_cache(codec, codec->afg, 0,
1166 AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask);
Matthew Ranostay87d48362007-07-17 11:52:24 +02001167 /* Configure GPIOx as CMOS */
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001168 snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000);
Matthew Ranostay87d48362007-07-17 11:52:24 +02001169 /* Assert GPIOx */
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001170 snd_hda_codec_write_cache(codec, codec->afg, 0,
1171 AC_VERB_SET_GPIO_DATA, spec->gpio_data);
Matthew Ranostay87d48362007-07-17 11:52:24 +02001172 /* Enable GPIOx */
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001173 snd_hda_codec_write_cache(codec, codec->afg, 0,
1174 AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
Matthew Ranostay92a22be2007-06-19 16:48:28 +02001175}
1176
Matt2f2f4252005-04-13 14:45:30 +02001177/*
1178 * Analog playback callbacks
1179 */
1180static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
1181 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001182 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001183{
1184 struct sigmatel_spec *spec = codec->spec;
1185 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
1186}
1187
1188static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
1189 struct hda_codec *codec,
1190 unsigned int stream_tag,
1191 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001192 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001193{
1194 struct sigmatel_spec *spec = codec->spec;
Matt Porter403d1942005-11-29 15:00:51 +01001195 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);
Matt2f2f4252005-04-13 14:45:30 +02001196}
1197
1198static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1199 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001200 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001201{
1202 struct sigmatel_spec *spec = codec->spec;
1203 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
1204}
1205
1206/*
Mattdabbed62005-06-14 10:19:34 +02001207 * Digital playback callbacks
1208 */
1209static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1210 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001211 struct snd_pcm_substream *substream)
Mattdabbed62005-06-14 10:19:34 +02001212{
1213 struct sigmatel_spec *spec = codec->spec;
1214 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1215}
1216
1217static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1218 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001219 struct snd_pcm_substream *substream)
Mattdabbed62005-06-14 10:19:34 +02001220{
1221 struct sigmatel_spec *spec = codec->spec;
1222 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1223}
1224
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001225static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
1226 struct hda_codec *codec,
1227 unsigned int stream_tag,
1228 unsigned int format,
1229 struct snd_pcm_substream *substream)
1230{
1231 struct sigmatel_spec *spec = codec->spec;
1232 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1233 stream_tag, format, substream);
1234}
1235
Mattdabbed62005-06-14 10:19:34 +02001236
1237/*
Matt2f2f4252005-04-13 14:45:30 +02001238 * Analog capture callbacks
1239 */
1240static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1241 struct hda_codec *codec,
1242 unsigned int stream_tag,
1243 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001244 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001245{
1246 struct sigmatel_spec *spec = codec->spec;
1247
1248 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1249 stream_tag, 0, format);
1250 return 0;
1251}
1252
1253static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1254 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001255 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001256{
1257 struct sigmatel_spec *spec = codec->spec;
1258
1259 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
1260 return 0;
1261}
1262
Mattdabbed62005-06-14 10:19:34 +02001263static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
1264 .substreams = 1,
1265 .channels_min = 2,
1266 .channels_max = 2,
1267 /* NID is set in stac92xx_build_pcms */
1268 .ops = {
1269 .open = stac92xx_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001270 .close = stac92xx_dig_playback_pcm_close,
1271 .prepare = stac92xx_dig_playback_pcm_prepare
Mattdabbed62005-06-14 10:19:34 +02001272 },
1273};
1274
1275static struct hda_pcm_stream stac92xx_pcm_digital_capture = {
1276 .substreams = 1,
1277 .channels_min = 2,
1278 .channels_max = 2,
1279 /* NID is set in stac92xx_build_pcms */
1280};
1281
Matt2f2f4252005-04-13 14:45:30 +02001282static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
1283 .substreams = 1,
1284 .channels_min = 2,
Mattc7d4b2f2005-06-27 14:59:41 +02001285 .channels_max = 8,
Matt2f2f4252005-04-13 14:45:30 +02001286 .nid = 0x02, /* NID to query formats and rates */
1287 .ops = {
1288 .open = stac92xx_playback_pcm_open,
1289 .prepare = stac92xx_playback_pcm_prepare,
1290 .cleanup = stac92xx_playback_pcm_cleanup
1291 },
1292};
1293
Matt Porter3cc08dc2006-01-23 15:27:49 +01001294static struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = {
1295 .substreams = 1,
1296 .channels_min = 2,
1297 .channels_max = 2,
1298 .nid = 0x06, /* NID to query formats and rates */
1299 .ops = {
1300 .open = stac92xx_playback_pcm_open,
1301 .prepare = stac92xx_playback_pcm_prepare,
1302 .cleanup = stac92xx_playback_pcm_cleanup
1303 },
1304};
1305
Matt2f2f4252005-04-13 14:45:30 +02001306static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
1307 .substreams = 2,
1308 .channels_min = 2,
1309 .channels_max = 2,
Matt Porter3cc08dc2006-01-23 15:27:49 +01001310 /* NID is set in stac92xx_build_pcms */
Matt2f2f4252005-04-13 14:45:30 +02001311 .ops = {
1312 .prepare = stac92xx_capture_pcm_prepare,
1313 .cleanup = stac92xx_capture_pcm_cleanup
1314 },
1315};
1316
1317static int stac92xx_build_pcms(struct hda_codec *codec)
1318{
1319 struct sigmatel_spec *spec = codec->spec;
1320 struct hda_pcm *info = spec->pcm_rec;
1321
1322 codec->num_pcms = 1;
1323 codec->pcm_info = info;
1324
Mattc7d4b2f2005-06-27 14:59:41 +02001325 info->name = "STAC92xx Analog";
Matt2f2f4252005-04-13 14:45:30 +02001326 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
Matt2f2f4252005-04-13 14:45:30 +02001327 info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
Matt Porter3cc08dc2006-01-23 15:27:49 +01001328 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1329
1330 if (spec->alt_switch) {
1331 codec->num_pcms++;
1332 info++;
1333 info->name = "STAC92xx Analog Alt";
1334 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback;
1335 }
Matt2f2f4252005-04-13 14:45:30 +02001336
Mattdabbed62005-06-14 10:19:34 +02001337 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1338 codec->num_pcms++;
1339 info++;
1340 info->name = "STAC92xx Digital";
1341 if (spec->multiout.dig_out_nid) {
1342 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
1343 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
1344 }
1345 if (spec->dig_in_nid) {
1346 info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture;
1347 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
1348 }
1349 }
1350
Matt2f2f4252005-04-13 14:45:30 +02001351 return 0;
1352}
1353
Takashi Iwaic960a032006-03-23 17:06:28 +01001354static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
1355{
1356 unsigned int pincap = snd_hda_param_read(codec, nid,
1357 AC_PAR_PIN_CAP);
1358 pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
1359 if (pincap & AC_PINCAP_VREF_100)
1360 return AC_PINCTL_VREF_100;
1361 if (pincap & AC_PINCAP_VREF_80)
1362 return AC_PINCTL_VREF_80;
1363 if (pincap & AC_PINCAP_VREF_50)
1364 return AC_PINCTL_VREF_50;
1365 if (pincap & AC_PINCAP_VREF_GRD)
1366 return AC_PINCTL_VREF_GRD;
1367 return 0;
1368}
1369
Matt Porter403d1942005-11-29 15:00:51 +01001370static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
1371
1372{
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001373 snd_hda_codec_write_cache(codec, nid, 0,
1374 AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
Matt Porter403d1942005-11-29 15:00:51 +01001375}
1376
Takashi Iwaia5ce8892007-07-23 15:42:26 +02001377#define stac92xx_io_switch_info snd_ctl_boolean_mono_info
Matt Porter403d1942005-11-29 15:00:51 +01001378
1379static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1380{
1381 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1382 struct sigmatel_spec *spec = codec->spec;
1383 int io_idx = kcontrol-> private_value & 0xff;
1384
1385 ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
1386 return 0;
1387}
1388
1389static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1390{
1391 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1392 struct sigmatel_spec *spec = codec->spec;
1393 hda_nid_t nid = kcontrol->private_value >> 8;
1394 int io_idx = kcontrol-> private_value & 0xff;
1395 unsigned short val = ucontrol->value.integer.value[0];
1396
1397 spec->io_switch[io_idx] = val;
1398
1399 if (val)
1400 stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
Takashi Iwaic960a032006-03-23 17:06:28 +01001401 else {
1402 unsigned int pinctl = AC_PINCTL_IN_EN;
1403 if (io_idx) /* set VREF for mic */
1404 pinctl |= stac92xx_get_vref(codec, nid);
1405 stac92xx_auto_set_pinctl(codec, nid, pinctl);
1406 }
Matt Porter403d1942005-11-29 15:00:51 +01001407 return 1;
1408}
1409
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02001410#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info
1411
1412static int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol,
1413 struct snd_ctl_elem_value *ucontrol)
1414{
1415 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1416 struct sigmatel_spec *spec = codec->spec;
1417
1418 ucontrol->value.integer.value[0] = spec->clfe_swap;
1419 return 0;
1420}
1421
1422static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
1423 struct snd_ctl_elem_value *ucontrol)
1424{
1425 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1426 struct sigmatel_spec *spec = codec->spec;
1427 hda_nid_t nid = kcontrol->private_value & 0xff;
1428
1429 if (spec->clfe_swap == ucontrol->value.integer.value[0])
1430 return 0;
1431
1432 spec->clfe_swap = ucontrol->value.integer.value[0];
1433
1434 snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
1435 spec->clfe_swap ? 0x4 : 0x0);
1436
1437 return 1;
1438}
1439
Matt Porter403d1942005-11-29 15:00:51 +01001440#define STAC_CODEC_IO_SWITCH(xname, xpval) \
1441 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1442 .name = xname, \
1443 .index = 0, \
1444 .info = stac92xx_io_switch_info, \
1445 .get = stac92xx_io_switch_get, \
1446 .put = stac92xx_io_switch_put, \
1447 .private_value = xpval, \
1448 }
1449
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02001450#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \
1451 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1452 .name = xname, \
1453 .index = 0, \
1454 .info = stac92xx_clfe_switch_info, \
1455 .get = stac92xx_clfe_switch_get, \
1456 .put = stac92xx_clfe_switch_put, \
1457 .private_value = xpval, \
1458 }
Matt Porter403d1942005-11-29 15:00:51 +01001459
Mattc7d4b2f2005-06-27 14:59:41 +02001460enum {
1461 STAC_CTL_WIDGET_VOL,
1462 STAC_CTL_WIDGET_MUTE,
Matt Porter403d1942005-11-29 15:00:51 +01001463 STAC_CTL_WIDGET_IO_SWITCH,
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02001464 STAC_CTL_WIDGET_CLFE_SWITCH
Mattc7d4b2f2005-06-27 14:59:41 +02001465};
1466
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001467static struct snd_kcontrol_new stac92xx_control_templates[] = {
Mattc7d4b2f2005-06-27 14:59:41 +02001468 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
1469 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Matt Porter403d1942005-11-29 15:00:51 +01001470 STAC_CODEC_IO_SWITCH(NULL, 0),
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02001471 STAC_CODEC_CLFE_SWITCH(NULL, 0),
Mattc7d4b2f2005-06-27 14:59:41 +02001472};
1473
1474/* add dynamic controls */
1475static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val)
1476{
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001477 struct snd_kcontrol_new *knew;
Mattc7d4b2f2005-06-27 14:59:41 +02001478
1479 if (spec->num_kctl_used >= spec->num_kctl_alloc) {
1480 int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
1481
1482 knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
1483 if (! knew)
1484 return -ENOMEM;
1485 if (spec->kctl_alloc) {
1486 memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
1487 kfree(spec->kctl_alloc);
1488 }
1489 spec->kctl_alloc = knew;
1490 spec->num_kctl_alloc = num;
1491 }
1492
1493 knew = &spec->kctl_alloc[spec->num_kctl_used];
1494 *knew = stac92xx_control_templates[type];
Takashi Iwai82fe0c52005-06-30 10:54:33 +02001495 knew->name = kstrdup(name, GFP_KERNEL);
Mattc7d4b2f2005-06-27 14:59:41 +02001496 if (! knew->name)
1497 return -ENOMEM;
1498 knew->private_value = val;
1499 spec->num_kctl_used++;
1500 return 0;
1501}
1502
Matt Porter403d1942005-11-29 15:00:51 +01001503/* flag inputs as additional dynamic lineouts */
1504static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
1505{
1506 struct sigmatel_spec *spec = codec->spec;
Steve Longerbeam7b043892007-05-03 20:50:03 +02001507 unsigned int wcaps, wtype;
1508 int i, num_dacs = 0;
1509
1510 /* use the wcaps cache to count all DACs available for line-outs */
1511 for (i = 0; i < codec->num_nodes; i++) {
1512 wcaps = codec->wcaps[i];
1513 wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
1514 if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
1515 num_dacs++;
1516 }
Matt Porter403d1942005-11-29 15:00:51 +01001517
Steve Longerbeam7b043892007-05-03 20:50:03 +02001518 snd_printdd("%s: total dac count=%d\n", __func__, num_dacs);
1519
Matt Porter403d1942005-11-29 15:00:51 +01001520 switch (cfg->line_outs) {
1521 case 3:
1522 /* add line-in as side */
Steve Longerbeam7b043892007-05-03 20:50:03 +02001523 if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) {
Takashi Iwaic480f792007-09-03 09:43:38 +02001524 cfg->line_out_pins[cfg->line_outs] =
1525 cfg->input_pins[AUTO_PIN_LINE];
Matt Porter403d1942005-11-29 15:00:51 +01001526 spec->line_switch = 1;
1527 cfg->line_outs++;
1528 }
1529 break;
1530 case 2:
1531 /* add line-in as clfe and mic as side */
Steve Longerbeam7b043892007-05-03 20:50:03 +02001532 if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) {
Takashi Iwaic480f792007-09-03 09:43:38 +02001533 cfg->line_out_pins[cfg->line_outs] =
1534 cfg->input_pins[AUTO_PIN_LINE];
Matt Porter403d1942005-11-29 15:00:51 +01001535 spec->line_switch = 1;
1536 cfg->line_outs++;
1537 }
Steve Longerbeam7b043892007-05-03 20:50:03 +02001538 if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) {
Takashi Iwaic480f792007-09-03 09:43:38 +02001539 cfg->line_out_pins[cfg->line_outs] =
1540 cfg->input_pins[AUTO_PIN_MIC];
Matt Porter403d1942005-11-29 15:00:51 +01001541 spec->mic_switch = 1;
1542 cfg->line_outs++;
1543 }
1544 break;
1545 case 1:
1546 /* add line-in as surr and mic as clfe */
Steve Longerbeam7b043892007-05-03 20:50:03 +02001547 if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) {
Takashi Iwaic480f792007-09-03 09:43:38 +02001548 cfg->line_out_pins[cfg->line_outs] =
1549 cfg->input_pins[AUTO_PIN_LINE];
Matt Porter403d1942005-11-29 15:00:51 +01001550 spec->line_switch = 1;
1551 cfg->line_outs++;
1552 }
Steve Longerbeam7b043892007-05-03 20:50:03 +02001553 if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) {
Takashi Iwaic480f792007-09-03 09:43:38 +02001554 cfg->line_out_pins[cfg->line_outs] =
1555 cfg->input_pins[AUTO_PIN_MIC];
Matt Porter403d1942005-11-29 15:00:51 +01001556 spec->mic_switch = 1;
1557 cfg->line_outs++;
1558 }
1559 break;
1560 }
1561
1562 return 0;
1563}
1564
Steve Longerbeam7b043892007-05-03 20:50:03 +02001565
1566static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
1567{
1568 int i;
1569
1570 for (i = 0; i < spec->multiout.num_dacs; i++) {
1571 if (spec->multiout.dac_nids[i] == nid)
1572 return 1;
1573 }
1574
1575 return 0;
1576}
1577
Matt Porter3cc08dc2006-01-23 15:27:49 +01001578/*
Steve Longerbeam7b043892007-05-03 20:50:03 +02001579 * Fill in the dac_nids table from the parsed pin configuration
1580 * This function only works when every pin in line_out_pins[]
1581 * contains atleast one DAC in its connection list. Some 92xx
1582 * codecs are not connected directly to a DAC, such as the 9200
1583 * and 9202/925x. For those, dac_nids[] must be hard-coded.
Matt Porter3cc08dc2006-01-23 15:27:49 +01001584 */
Takashi Iwai19039bd2006-06-28 15:52:16 +02001585static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
Takashi Iwaidf802952007-07-02 19:18:00 +02001586 struct auto_pin_cfg *cfg)
Mattc7d4b2f2005-06-27 14:59:41 +02001587{
1588 struct sigmatel_spec *spec = codec->spec;
Steve Longerbeam7b043892007-05-03 20:50:03 +02001589 int i, j, conn_len = 0;
1590 hda_nid_t nid, conn[HDA_MAX_CONNECTIONS];
1591 unsigned int wcaps, wtype;
1592
Mattc7d4b2f2005-06-27 14:59:41 +02001593 for (i = 0; i < cfg->line_outs; i++) {
1594 nid = cfg->line_out_pins[i];
Steve Longerbeam7b043892007-05-03 20:50:03 +02001595 conn_len = snd_hda_get_connections(codec, nid, conn,
1596 HDA_MAX_CONNECTIONS);
1597 for (j = 0; j < conn_len; j++) {
1598 wcaps = snd_hda_param_read(codec, conn[j],
1599 AC_PAR_AUDIO_WIDGET_CAP);
1600 wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
1601
1602 if (wtype != AC_WID_AUD_OUT ||
1603 (wcaps & AC_WCAP_DIGITAL))
1604 continue;
1605 /* conn[j] is a DAC routed to this line-out */
1606 if (!is_in_dac_nids(spec, conn[j]))
1607 break;
1608 }
1609
1610 if (j == conn_len) {
Takashi Iwaidf802952007-07-02 19:18:00 +02001611 if (spec->multiout.num_dacs > 0) {
1612 /* we have already working output pins,
1613 * so let's drop the broken ones again
1614 */
1615 cfg->line_outs = spec->multiout.num_dacs;
1616 break;
1617 }
Steve Longerbeam7b043892007-05-03 20:50:03 +02001618 /* error out, no available DAC found */
1619 snd_printk(KERN_ERR
1620 "%s: No available DAC for pin 0x%x\n",
1621 __func__, nid);
1622 return -ENODEV;
1623 }
1624
1625 spec->multiout.dac_nids[i] = conn[j];
1626 spec->multiout.num_dacs++;
1627 if (conn_len > 1) {
1628 /* select this DAC in the pin's input mux */
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001629 snd_hda_codec_write_cache(codec, nid, 0,
1630 AC_VERB_SET_CONNECT_SEL, j);
Steve Longerbeam7b043892007-05-03 20:50:03 +02001631
1632 }
Mattc7d4b2f2005-06-27 14:59:41 +02001633 }
1634
Steve Longerbeam7b043892007-05-03 20:50:03 +02001635 snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
1636 spec->multiout.num_dacs,
1637 spec->multiout.dac_nids[0],
1638 spec->multiout.dac_nids[1],
1639 spec->multiout.dac_nids[2],
1640 spec->multiout.dac_nids[3],
1641 spec->multiout.dac_nids[4]);
Mattc7d4b2f2005-06-27 14:59:41 +02001642 return 0;
1643}
1644
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001645/* create volume control/switch for the given prefx type */
1646static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs)
1647{
1648 char name[32];
1649 int err;
1650
1651 sprintf(name, "%s Playback Volume", pfx);
1652 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
1653 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1654 if (err < 0)
1655 return err;
1656 sprintf(name, "%s Playback Switch", pfx);
1657 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
1658 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1659 if (err < 0)
1660 return err;
1661 return 0;
1662}
1663
Mattc7d4b2f2005-06-27 14:59:41 +02001664/* add playback controls from the parsed DAC table */
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02001665static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
Takashi Iwai19039bd2006-06-28 15:52:16 +02001666 const struct auto_pin_cfg *cfg)
Mattc7d4b2f2005-06-27 14:59:41 +02001667{
Takashi Iwai19039bd2006-06-28 15:52:16 +02001668 static const char *chname[4] = {
1669 "Front", "Surround", NULL /*CLFE*/, "Side"
1670 };
Mattc7d4b2f2005-06-27 14:59:41 +02001671 hda_nid_t nid;
1672 int i, err;
1673
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02001674 struct sigmatel_spec *spec = codec->spec;
1675 unsigned int wid_caps;
1676
1677
Mattc7d4b2f2005-06-27 14:59:41 +02001678 for (i = 0; i < cfg->line_outs; i++) {
Matt Porter403d1942005-11-29 15:00:51 +01001679 if (!spec->multiout.dac_nids[i])
Mattc7d4b2f2005-06-27 14:59:41 +02001680 continue;
1681
1682 nid = spec->multiout.dac_nids[i];
1683
1684 if (i == 2) {
1685 /* Center/LFE */
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001686 err = create_controls(spec, "Center", nid, 1);
1687 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02001688 return err;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001689 err = create_controls(spec, "LFE", nid, 2);
1690 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02001691 return err;
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02001692
1693 wid_caps = get_wcaps(codec, nid);
1694
1695 if (wid_caps & AC_WCAP_LR_SWAP) {
1696 err = stac92xx_add_control(spec,
1697 STAC_CTL_WIDGET_CLFE_SWITCH,
1698 "Swap Center/LFE Playback Switch", nid);
1699
1700 if (err < 0)
1701 return err;
1702 }
1703
Mattc7d4b2f2005-06-27 14:59:41 +02001704 } else {
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001705 err = create_controls(spec, chname[i], nid, 3);
1706 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02001707 return err;
1708 }
1709 }
1710
Matt Porter403d1942005-11-29 15:00:51 +01001711 if (spec->line_switch)
1712 if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0)
1713 return err;
1714
1715 if (spec->mic_switch)
1716 if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0)
1717 return err;
1718
Mattc7d4b2f2005-06-27 14:59:41 +02001719 return 0;
1720}
1721
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001722static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
1723{
Steve Longerbeam7b043892007-05-03 20:50:03 +02001724 if (is_in_dac_nids(spec, nid))
1725 return 1;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001726 if (spec->multiout.hp_nid == nid)
1727 return 1;
1728 return 0;
1729}
1730
1731static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
1732{
1733 if (!spec->multiout.hp_nid)
1734 spec->multiout.hp_nid = nid;
1735 else if (spec->multiout.num_dacs > 4) {
1736 printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
1737 return 1;
1738 } else {
1739 spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
1740 spec->multiout.num_dacs++;
1741 }
1742 return 0;
1743}
1744
1745/* add playback controls for Speaker and HP outputs */
1746static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
1747 struct auto_pin_cfg *cfg)
Mattc7d4b2f2005-06-27 14:59:41 +02001748{
1749 struct sigmatel_spec *spec = codec->spec;
Mattc7d4b2f2005-06-27 14:59:41 +02001750 hda_nid_t nid;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001751 int i, old_num_dacs, err;
Mattc7d4b2f2005-06-27 14:59:41 +02001752
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001753 old_num_dacs = spec->multiout.num_dacs;
1754 for (i = 0; i < cfg->hp_outs; i++) {
1755 unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
1756 if (wid_caps & AC_WCAP_UNSOL_CAP)
1757 spec->hp_detect = 1;
1758 nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
1759 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
1760 if (check_in_dac_nids(spec, nid))
1761 nid = 0;
1762 if (! nid)
Mattc7d4b2f2005-06-27 14:59:41 +02001763 continue;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001764 add_spec_dacs(spec, nid);
1765 }
1766 for (i = 0; i < cfg->speaker_outs; i++) {
Steve Longerbeam7b043892007-05-03 20:50:03 +02001767 nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0,
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001768 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
1769 if (check_in_dac_nids(spec, nid))
1770 nid = 0;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001771 if (! nid)
1772 continue;
1773 add_spec_dacs(spec, nid);
Mattc7d4b2f2005-06-27 14:59:41 +02001774 }
Matthew Ranostay1b290a52007-07-12 15:17:34 +02001775 for (i = 0; i < cfg->line_outs; i++) {
1776 nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
1777 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
1778 if (check_in_dac_nids(spec, nid))
1779 nid = 0;
1780 if (! nid)
1781 continue;
1782 add_spec_dacs(spec, nid);
1783 }
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001784 for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
1785 static const char *pfxs[] = {
1786 "Speaker", "External Speaker", "Speaker2",
1787 };
1788 err = create_controls(spec, pfxs[i - old_num_dacs],
1789 spec->multiout.dac_nids[i], 3);
1790 if (err < 0)
1791 return err;
1792 }
1793 if (spec->multiout.hp_nid) {
1794 const char *pfx;
1795 if (old_num_dacs == spec->multiout.num_dacs)
1796 pfx = "Master";
1797 else
1798 pfx = "Headphone";
1799 err = create_controls(spec, pfx, spec->multiout.hp_nid, 3);
1800 if (err < 0)
1801 return err;
1802 }
Mattc7d4b2f2005-06-27 14:59:41 +02001803
1804 return 0;
1805}
1806
Matt Porter8b657272006-10-26 17:12:59 +02001807/* labels for dmic mux inputs */
Adrian Bunkddc2cec2006-11-20 12:03:44 +01001808static const char *stac92xx_dmic_labels[5] = {
Matt Porter8b657272006-10-26 17:12:59 +02001809 "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
1810 "Digital Mic 3", "Digital Mic 4"
1811};
1812
1813/* create playback/capture controls for input pins on dmic capable codecs */
1814static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
1815 const struct auto_pin_cfg *cfg)
1816{
1817 struct sigmatel_spec *spec = codec->spec;
1818 struct hda_input_mux *dimux = &spec->private_dimux;
1819 hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
1820 int i, j;
1821
1822 dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
1823 dimux->items[dimux->num_items].index = 0;
1824 dimux->num_items++;
1825
1826 for (i = 0; i < spec->num_dmics; i++) {
1827 int index;
1828 int num_cons;
1829 unsigned int def_conf;
1830
1831 def_conf = snd_hda_codec_read(codec,
1832 spec->dmic_nids[i],
1833 0,
1834 AC_VERB_GET_CONFIG_DEFAULT,
1835 0);
1836 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
1837 continue;
1838
1839 num_cons = snd_hda_get_connections(codec,
1840 spec->dmux_nid,
1841 con_lst,
1842 HDA_MAX_NUM_INPUTS);
1843 for (j = 0; j < num_cons; j++)
1844 if (con_lst[j] == spec->dmic_nids[i]) {
1845 index = j;
1846 goto found;
1847 }
1848 continue;
1849found:
1850 dimux->items[dimux->num_items].label =
1851 stac92xx_dmic_labels[dimux->num_items];
1852 dimux->items[dimux->num_items].index = index;
1853 dimux->num_items++;
1854 }
1855
1856 return 0;
1857}
1858
Mattc7d4b2f2005-06-27 14:59:41 +02001859/* create playback/capture controls for input pins */
1860static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
1861{
1862 struct sigmatel_spec *spec = codec->spec;
Mattc7d4b2f2005-06-27 14:59:41 +02001863 struct hda_input_mux *imux = &spec->private_imux;
1864 hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
1865 int i, j, k;
1866
1867 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai314634b2006-09-21 11:56:18 +02001868 int index;
Mattc7d4b2f2005-06-27 14:59:41 +02001869
Takashi Iwai314634b2006-09-21 11:56:18 +02001870 if (!cfg->input_pins[i])
1871 continue;
1872 index = -1;
1873 for (j = 0; j < spec->num_muxes; j++) {
1874 int num_cons;
1875 num_cons = snd_hda_get_connections(codec,
1876 spec->mux_nids[j],
1877 con_lst,
1878 HDA_MAX_NUM_INPUTS);
1879 for (k = 0; k < num_cons; k++)
1880 if (con_lst[k] == cfg->input_pins[i]) {
1881 index = k;
1882 goto found;
1883 }
Mattc7d4b2f2005-06-27 14:59:41 +02001884 }
Takashi Iwai314634b2006-09-21 11:56:18 +02001885 continue;
1886 found:
1887 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
1888 imux->items[imux->num_items].index = index;
1889 imux->num_items++;
Mattc7d4b2f2005-06-27 14:59:41 +02001890 }
1891
Steve Longerbeam7b043892007-05-03 20:50:03 +02001892 if (imux->num_items) {
Sam Revitch62fe78e2006-05-10 15:09:17 +02001893 /*
1894 * Set the current input for the muxes.
1895 * The STAC9221 has two input muxes with identical source
1896 * NID lists. Hopefully this won't get confused.
1897 */
1898 for (i = 0; i < spec->num_muxes; i++) {
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001899 snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0,
1900 AC_VERB_SET_CONNECT_SEL,
1901 imux->items[0].index);
Sam Revitch62fe78e2006-05-10 15:09:17 +02001902 }
1903 }
1904
Mattc7d4b2f2005-06-27 14:59:41 +02001905 return 0;
1906}
1907
Mattc7d4b2f2005-06-27 14:59:41 +02001908static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
1909{
1910 struct sigmatel_spec *spec = codec->spec;
1911 int i;
1912
1913 for (i = 0; i < spec->autocfg.line_outs; i++) {
1914 hda_nid_t nid = spec->autocfg.line_out_pins[i];
1915 stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
1916 }
1917}
1918
1919static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
1920{
1921 struct sigmatel_spec *spec = codec->spec;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001922 int i;
Mattc7d4b2f2005-06-27 14:59:41 +02001923
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001924 for (i = 0; i < spec->autocfg.hp_outs; i++) {
1925 hda_nid_t pin;
1926 pin = spec->autocfg.hp_pins[i];
1927 if (pin) /* connect to front */
1928 stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
1929 }
1930 for (i = 0; i < spec->autocfg.speaker_outs; i++) {
1931 hda_nid_t pin;
1932 pin = spec->autocfg.speaker_pins[i];
1933 if (pin) /* connect to front */
1934 stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
1935 }
Mattc7d4b2f2005-06-27 14:59:41 +02001936}
1937
Matt Porter3cc08dc2006-01-23 15:27:49 +01001938static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
Mattc7d4b2f2005-06-27 14:59:41 +02001939{
1940 struct sigmatel_spec *spec = codec->spec;
1941 int err;
1942
Matt Porter8b657272006-10-26 17:12:59 +02001943 if ((err = snd_hda_parse_pin_def_config(codec,
1944 &spec->autocfg,
1945 spec->dmic_nids)) < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02001946 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01001947 if (! spec->autocfg.line_outs)
Matt Porter869264c2006-01-25 19:20:50 +01001948 return 0; /* can't find valid pin config */
Takashi Iwai19039bd2006-06-28 15:52:16 +02001949
Matt Porter403d1942005-11-29 15:00:51 +01001950 if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
1951 return err;
Takashi Iwai19039bd2006-06-28 15:52:16 +02001952 if (spec->multiout.num_dacs == 0)
1953 if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
1954 return err;
Mattc7d4b2f2005-06-27 14:59:41 +02001955
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02001956 err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg);
1957
1958 if (err < 0)
1959 return err;
1960
1961 err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
1962
1963 if (err < 0)
1964 return err;
1965
1966 err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
1967
1968 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02001969 return err;
1970
Matt Porter8b657272006-10-26 17:12:59 +02001971 if (spec->num_dmics > 0)
1972 if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
1973 &spec->autocfg)) < 0)
1974 return err;
1975
Mattc7d4b2f2005-06-27 14:59:41 +02001976 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Matt Porter403d1942005-11-29 15:00:51 +01001977 if (spec->multiout.max_channels > 2)
Mattc7d4b2f2005-06-27 14:59:41 +02001978 spec->surr_switch = 1;
Mattc7d4b2f2005-06-27 14:59:41 +02001979
Takashi Iwai82bc9552006-03-21 11:24:42 +01001980 if (spec->autocfg.dig_out_pin)
Matt Porter3cc08dc2006-01-23 15:27:49 +01001981 spec->multiout.dig_out_nid = dig_out;
Takashi Iwai82bc9552006-03-21 11:24:42 +01001982 if (spec->autocfg.dig_in_pin)
Matt Porter3cc08dc2006-01-23 15:27:49 +01001983 spec->dig_in_nid = dig_in;
Mattc7d4b2f2005-06-27 14:59:41 +02001984
1985 if (spec->kctl_alloc)
1986 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
1987
1988 spec->input_mux = &spec->private_imux;
Matt Porter8b657272006-10-26 17:12:59 +02001989 spec->dinput_mux = &spec->private_dimux;
Mattc7d4b2f2005-06-27 14:59:41 +02001990
1991 return 1;
1992}
1993
Takashi Iwai82bc9552006-03-21 11:24:42 +01001994/* add playback controls for HP output */
1995static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
1996 struct auto_pin_cfg *cfg)
1997{
1998 struct sigmatel_spec *spec = codec->spec;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02001999 hda_nid_t pin = cfg->hp_pins[0];
Takashi Iwai82bc9552006-03-21 11:24:42 +01002000 unsigned int wid_caps;
2001
2002 if (! pin)
2003 return 0;
2004
2005 wid_caps = get_wcaps(codec, pin);
Takashi Iwai505cb342006-03-27 12:51:52 +02002006 if (wid_caps & AC_WCAP_UNSOL_CAP)
Takashi Iwai82bc9552006-03-21 11:24:42 +01002007 spec->hp_detect = 1;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002008
2009 return 0;
2010}
2011
Richard Fish160ea0d2006-09-06 13:58:25 +02002012/* add playback controls for LFE output */
2013static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
2014 struct auto_pin_cfg *cfg)
2015{
2016 struct sigmatel_spec *spec = codec->spec;
2017 int err;
2018 hda_nid_t lfe_pin = 0x0;
2019 int i;
2020
2021 /*
2022 * search speaker outs and line outs for a mono speaker pin
2023 * with an amp. If one is found, add LFE controls
2024 * for it.
2025 */
2026 for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
2027 hda_nid_t pin = spec->autocfg.speaker_pins[i];
2028 unsigned long wcaps = get_wcaps(codec, pin);
2029 wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
2030 if (wcaps == AC_WCAP_OUT_AMP)
2031 /* found a mono speaker with an amp, must be lfe */
2032 lfe_pin = pin;
2033 }
2034
2035 /* if speaker_outs is 0, then speakers may be in line_outs */
2036 if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
2037 for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
2038 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2039 unsigned long cfg;
2040 cfg = snd_hda_codec_read(codec, pin, 0,
2041 AC_VERB_GET_CONFIG_DEFAULT,
2042 0x00);
2043 if (get_defcfg_device(cfg) == AC_JACK_SPEAKER) {
2044 unsigned long wcaps = get_wcaps(codec, pin);
2045 wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
2046 if (wcaps == AC_WCAP_OUT_AMP)
2047 /* found a mono speaker with an amp,
2048 must be lfe */
2049 lfe_pin = pin;
2050 }
2051 }
2052 }
2053
2054 if (lfe_pin) {
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002055 err = create_controls(spec, "LFE", lfe_pin, 1);
Richard Fish160ea0d2006-09-06 13:58:25 +02002056 if (err < 0)
2057 return err;
2058 }
2059
2060 return 0;
2061}
2062
Mattc7d4b2f2005-06-27 14:59:41 +02002063static int stac9200_parse_auto_config(struct hda_codec *codec)
2064{
2065 struct sigmatel_spec *spec = codec->spec;
2066 int err;
2067
Kailang Yangdf694da2005-12-05 19:42:22 +01002068 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002069 return err;
2070
2071 if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
2072 return err;
2073
Takashi Iwai82bc9552006-03-21 11:24:42 +01002074 if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
2075 return err;
2076
Richard Fish160ea0d2006-09-06 13:58:25 +02002077 if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0)
2078 return err;
2079
Takashi Iwai82bc9552006-03-21 11:24:42 +01002080 if (spec->autocfg.dig_out_pin)
Mattc7d4b2f2005-06-27 14:59:41 +02002081 spec->multiout.dig_out_nid = 0x05;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002082 if (spec->autocfg.dig_in_pin)
Mattc7d4b2f2005-06-27 14:59:41 +02002083 spec->dig_in_nid = 0x04;
Mattc7d4b2f2005-06-27 14:59:41 +02002084
2085 if (spec->kctl_alloc)
2086 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2087
2088 spec->input_mux = &spec->private_imux;
Matt Porter8b657272006-10-26 17:12:59 +02002089 spec->dinput_mux = &spec->private_dimux;
Mattc7d4b2f2005-06-27 14:59:41 +02002090
2091 return 1;
2092}
2093
Sam Revitch62fe78e2006-05-10 15:09:17 +02002094/*
2095 * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
2096 * funky external mute control using GPIO pins.
2097 */
2098
2099static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted)
2100{
2101 unsigned int gpiostate, gpiomask, gpiodir;
2102
2103 gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
2104 AC_VERB_GET_GPIO_DATA, 0);
2105
2106 if (!muted)
2107 gpiostate |= (1 << pin);
2108 else
2109 gpiostate &= ~(1 << pin);
2110
2111 gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
2112 AC_VERB_GET_GPIO_MASK, 0);
2113 gpiomask |= (1 << pin);
2114
2115 gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
2116 AC_VERB_GET_GPIO_DIRECTION, 0);
2117 gpiodir |= (1 << pin);
2118
2119 /* AppleHDA seems to do this -- WTF is this verb?? */
2120 snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
2121
2122 snd_hda_codec_write(codec, codec->afg, 0,
2123 AC_VERB_SET_GPIO_MASK, gpiomask);
2124 snd_hda_codec_write(codec, codec->afg, 0,
2125 AC_VERB_SET_GPIO_DIRECTION, gpiodir);
2126
2127 msleep(1);
2128
2129 snd_hda_codec_write(codec, codec->afg, 0,
2130 AC_VERB_SET_GPIO_DATA, gpiostate);
2131}
2132
Takashi Iwai314634b2006-09-21 11:56:18 +02002133static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
2134 unsigned int event)
2135{
2136 if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
Takashi Iwaidc81bed2007-09-03 09:36:36 +02002137 snd_hda_codec_write_cache(codec, nid, 0,
2138 AC_VERB_SET_UNSOLICITED_ENABLE,
2139 (AC_USRSP_EN | event));
Takashi Iwai314634b2006-09-21 11:56:18 +02002140}
2141
Mattc7d4b2f2005-06-27 14:59:41 +02002142static int stac92xx_init(struct hda_codec *codec)
2143{
2144 struct sigmatel_spec *spec = codec->spec;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002145 struct auto_pin_cfg *cfg = &spec->autocfg;
2146 int i;
Mattc7d4b2f2005-06-27 14:59:41 +02002147
Mattc7d4b2f2005-06-27 14:59:41 +02002148 snd_hda_sequence_write(codec, spec->init);
2149
Takashi Iwai82bc9552006-03-21 11:24:42 +01002150 /* set up pins */
2151 if (spec->hp_detect) {
Takashi Iwai505cb342006-03-27 12:51:52 +02002152 /* Enable unsolicited responses on the HP widget */
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002153 for (i = 0; i < cfg->hp_outs; i++)
Takashi Iwai314634b2006-09-21 11:56:18 +02002154 enable_pin_detect(codec, cfg->hp_pins[i],
2155 STAC_HP_EVENT);
Takashi Iwai0a07acaf2007-03-13 10:40:23 +01002156 /* force to enable the first line-out; the others are set up
2157 * in unsol_event
2158 */
2159 stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
2160 AC_PINCTL_OUT_EN);
Takashi Iwaieb995a82006-09-21 14:28:21 +02002161 stac92xx_auto_init_hp_out(codec);
Takashi Iwai82bc9552006-03-21 11:24:42 +01002162 /* fake event to set up pins */
2163 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
2164 } else {
2165 stac92xx_auto_init_multi_out(codec);
2166 stac92xx_auto_init_hp_out(codec);
2167 }
2168 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwaic960a032006-03-23 17:06:28 +01002169 hda_nid_t nid = cfg->input_pins[i];
2170 if (nid) {
2171 unsigned int pinctl = AC_PINCTL_IN_EN;
2172 if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
2173 pinctl |= stac92xx_get_vref(codec, nid);
2174 stac92xx_auto_set_pinctl(codec, nid, pinctl);
2175 }
Takashi Iwai82bc9552006-03-21 11:24:42 +01002176 }
Matt Porter8b657272006-10-26 17:12:59 +02002177 if (spec->num_dmics > 0)
2178 for (i = 0; i < spec->num_dmics; i++)
2179 stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
2180 AC_PINCTL_IN_EN);
2181
Takashi Iwai82bc9552006-03-21 11:24:42 +01002182 if (cfg->dig_out_pin)
2183 stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
2184 AC_PINCTL_OUT_EN);
2185 if (cfg->dig_in_pin)
2186 stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
2187 AC_PINCTL_IN_EN);
2188
Sam Revitch62fe78e2006-05-10 15:09:17 +02002189 if (spec->gpio_mute) {
2190 stac922x_gpio_mute(codec, 0, 0);
2191 stac922x_gpio_mute(codec, 1, 0);
2192 }
2193
Mattc7d4b2f2005-06-27 14:59:41 +02002194 return 0;
2195}
2196
Matt2f2f4252005-04-13 14:45:30 +02002197static void stac92xx_free(struct hda_codec *codec)
2198{
Mattc7d4b2f2005-06-27 14:59:41 +02002199 struct sigmatel_spec *spec = codec->spec;
2200 int i;
2201
2202 if (! spec)
2203 return;
2204
2205 if (spec->kctl_alloc) {
2206 for (i = 0; i < spec->num_kctl_used; i++)
2207 kfree(spec->kctl_alloc[i].name);
2208 kfree(spec->kctl_alloc);
2209 }
2210
Richard Fish11b44bb2006-08-23 18:31:34 +02002211 if (spec->bios_pin_configs)
2212 kfree(spec->bios_pin_configs);
2213
Mattc7d4b2f2005-06-27 14:59:41 +02002214 kfree(spec);
Matt2f2f4252005-04-13 14:45:30 +02002215}
2216
Matt4e550962005-07-04 17:51:39 +02002217static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
2218 unsigned int flag)
2219{
2220 unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
2221 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
Steve Longerbeam7b043892007-05-03 20:50:03 +02002222
Takashi Iwaif9acba42007-05-29 18:01:06 +02002223 if (pin_ctl & AC_PINCTL_IN_EN) {
2224 /*
2225 * we need to check the current set-up direction of
2226 * shared input pins since they can be switched via
2227 * "xxx as Output" mixer switch
2228 */
2229 struct sigmatel_spec *spec = codec->spec;
2230 struct auto_pin_cfg *cfg = &spec->autocfg;
2231 if ((nid == cfg->input_pins[AUTO_PIN_LINE] &&
2232 spec->line_switch) ||
2233 (nid == cfg->input_pins[AUTO_PIN_MIC] &&
2234 spec->mic_switch))
2235 return;
2236 }
2237
Steve Longerbeam7b043892007-05-03 20:50:03 +02002238 /* if setting pin direction bits, clear the current
2239 direction bits first */
2240 if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
2241 pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
2242
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002243 snd_hda_codec_write_cache(codec, nid, 0,
Matt4e550962005-07-04 17:51:39 +02002244 AC_VERB_SET_PIN_WIDGET_CONTROL,
2245 pin_ctl | flag);
2246}
2247
2248static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
2249 unsigned int flag)
2250{
2251 unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
2252 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002253 snd_hda_codec_write_cache(codec, nid, 0,
Matt4e550962005-07-04 17:51:39 +02002254 AC_VERB_SET_PIN_WIDGET_CONTROL,
2255 pin_ctl & ~flag);
2256}
2257
Takashi Iwai314634b2006-09-21 11:56:18 +02002258static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
2259{
2260 if (!nid)
2261 return 0;
2262 if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
2263 & (1 << 31))
2264 return 1;
2265 return 0;
2266}
2267
2268static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
Matt4e550962005-07-04 17:51:39 +02002269{
2270 struct sigmatel_spec *spec = codec->spec;
2271 struct auto_pin_cfg *cfg = &spec->autocfg;
2272 int i, presence;
2273
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002274 presence = 0;
2275 for (i = 0; i < cfg->hp_outs; i++) {
Takashi Iwai314634b2006-09-21 11:56:18 +02002276 presence = get_pin_presence(codec, cfg->hp_pins[i]);
2277 if (presence)
2278 break;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002279 }
Matt4e550962005-07-04 17:51:39 +02002280
2281 if (presence) {
2282 /* disable lineouts, enable hp */
2283 for (i = 0; i < cfg->line_outs; i++)
2284 stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
2285 AC_PINCTL_OUT_EN);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002286 for (i = 0; i < cfg->speaker_outs; i++)
2287 stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
2288 AC_PINCTL_OUT_EN);
Matt4e550962005-07-04 17:51:39 +02002289 } else {
2290 /* enable lineouts, disable hp */
2291 for (i = 0; i < cfg->line_outs; i++)
2292 stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
2293 AC_PINCTL_OUT_EN);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002294 for (i = 0; i < cfg->speaker_outs; i++)
2295 stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
2296 AC_PINCTL_OUT_EN);
Matt4e550962005-07-04 17:51:39 +02002297 }
2298}
2299
Takashi Iwai314634b2006-09-21 11:56:18 +02002300static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
2301{
2302 switch (res >> 26) {
2303 case STAC_HP_EVENT:
2304 stac92xx_hp_detect(codec, res);
2305 break;
2306 }
2307}
2308
Takashi Iwaicb53c622007-08-10 17:21:45 +02002309#ifdef SND_HDA_NEEDS_RESUME
Mattff6fdc32005-06-27 15:06:52 +02002310static int stac92xx_resume(struct hda_codec *codec)
2311{
Takashi Iwaidc81bed2007-09-03 09:36:36 +02002312 struct sigmatel_spec *spec = codec->spec;
2313
Richard Fish11b44bb2006-08-23 18:31:34 +02002314 stac92xx_set_config_regs(codec);
Takashi Iwaidc81bed2007-09-03 09:36:36 +02002315 snd_hda_sequence_write(codec, spec->init);
2316 if (spec->gpio_mute) {
2317 stac922x_gpio_mute(codec, 0, 0);
2318 stac922x_gpio_mute(codec, 1, 0);
2319 }
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002320 snd_hda_codec_resume_amp(codec);
2321 snd_hda_codec_resume_cache(codec);
Takashi Iwaidc81bed2007-09-03 09:36:36 +02002322 /* invoke unsolicited event to reset the HP state */
2323 if (spec->hp_detect)
2324 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
Mattff6fdc32005-06-27 15:06:52 +02002325 return 0;
2326}
2327#endif
2328
Matt2f2f4252005-04-13 14:45:30 +02002329static struct hda_codec_ops stac92xx_patch_ops = {
2330 .build_controls = stac92xx_build_controls,
2331 .build_pcms = stac92xx_build_pcms,
2332 .init = stac92xx_init,
2333 .free = stac92xx_free,
Matt4e550962005-07-04 17:51:39 +02002334 .unsol_event = stac92xx_unsol_event,
Takashi Iwaicb53c622007-08-10 17:21:45 +02002335#ifdef SND_HDA_NEEDS_RESUME
Mattff6fdc32005-06-27 15:06:52 +02002336 .resume = stac92xx_resume,
2337#endif
Matt2f2f4252005-04-13 14:45:30 +02002338};
2339
2340static int patch_stac9200(struct hda_codec *codec)
2341{
2342 struct sigmatel_spec *spec;
Mattc7d4b2f2005-06-27 14:59:41 +02002343 int err;
Matt2f2f4252005-04-13 14:45:30 +02002344
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002345 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Matt2f2f4252005-04-13 14:45:30 +02002346 if (spec == NULL)
2347 return -ENOMEM;
2348
2349 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02002350 spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02002351 spec->pin_nids = stac9200_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002352 spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
2353 stac9200_models,
2354 stac9200_cfg_tbl);
Richard Fish11b44bb2006-08-23 18:31:34 +02002355 if (spec->board_config < 0) {
2356 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
2357 err = stac92xx_save_bios_config_regs(codec);
2358 if (err < 0) {
2359 stac92xx_free(codec);
2360 return err;
2361 }
2362 spec->pin_configs = spec->bios_pin_configs;
2363 } else {
Matt Porter403d1942005-11-29 15:00:51 +01002364 spec->pin_configs = stac9200_brd_tbl[spec->board_config];
2365 stac92xx_set_config_regs(codec);
2366 }
Matt2f2f4252005-04-13 14:45:30 +02002367
2368 spec->multiout.max_channels = 2;
2369 spec->multiout.num_dacs = 1;
2370 spec->multiout.dac_nids = stac9200_dac_nids;
2371 spec->adc_nids = stac9200_adc_nids;
2372 spec->mux_nids = stac9200_mux_nids;
Mattdabbed62005-06-14 10:19:34 +02002373 spec->num_muxes = 1;
Matt Porter8b657272006-10-26 17:12:59 +02002374 spec->num_dmics = 0;
Mattc7d4b2f2005-06-27 14:59:41 +02002375
2376 spec->init = stac9200_core_init;
Matt2f2f4252005-04-13 14:45:30 +02002377 spec->mixer = stac9200_mixer;
Mattc7d4b2f2005-06-27 14:59:41 +02002378
2379 err = stac9200_parse_auto_config(codec);
2380 if (err < 0) {
2381 stac92xx_free(codec);
2382 return err;
2383 }
Matt2f2f4252005-04-13 14:45:30 +02002384
2385 codec->patch_ops = stac92xx_patch_ops;
2386
2387 return 0;
2388}
2389
Tobin Davis8e21c342007-01-08 11:04:17 +01002390static int patch_stac925x(struct hda_codec *codec)
2391{
2392 struct sigmatel_spec *spec;
2393 int err;
2394
2395 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2396 if (spec == NULL)
2397 return -ENOMEM;
2398
2399 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02002400 spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
Tobin Davis8e21c342007-01-08 11:04:17 +01002401 spec->pin_nids = stac925x_pin_nids;
2402 spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
2403 stac925x_models,
2404 stac925x_cfg_tbl);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01002405 again:
Tobin Davis8e21c342007-01-08 11:04:17 +01002406 if (spec->board_config < 0) {
Tobin Davis2c11f952007-05-17 09:36:34 +02002407 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
2408 "using BIOS defaults\n");
Tobin Davis8e21c342007-01-08 11:04:17 +01002409 err = stac92xx_save_bios_config_regs(codec);
2410 if (err < 0) {
2411 stac92xx_free(codec);
2412 return err;
2413 }
2414 spec->pin_configs = spec->bios_pin_configs;
2415 } else if (stac925x_brd_tbl[spec->board_config] != NULL){
2416 spec->pin_configs = stac925x_brd_tbl[spec->board_config];
2417 stac92xx_set_config_regs(codec);
2418 }
2419
2420 spec->multiout.max_channels = 2;
2421 spec->multiout.num_dacs = 1;
2422 spec->multiout.dac_nids = stac925x_dac_nids;
2423 spec->adc_nids = stac925x_adc_nids;
2424 spec->mux_nids = stac925x_mux_nids;
2425 spec->num_muxes = 1;
Tobin Davis2c11f952007-05-17 09:36:34 +02002426 switch (codec->vendor_id) {
2427 case 0x83847632: /* STAC9202 */
2428 case 0x83847633: /* STAC9202D */
2429 case 0x83847636: /* STAC9251 */
2430 case 0x83847637: /* STAC9251D */
2431 spec->num_dmics = 1;
2432 spec->dmic_nids = stac925x_dmic_nids;
2433 break;
2434 default:
2435 spec->num_dmics = 0;
2436 break;
2437 }
Tobin Davis8e21c342007-01-08 11:04:17 +01002438
2439 spec->init = stac925x_core_init;
2440 spec->mixer = stac925x_mixer;
2441
2442 err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01002443 if (!err) {
2444 if (spec->board_config < 0) {
2445 printk(KERN_WARNING "hda_codec: No auto-config is "
2446 "available, default to model=ref\n");
2447 spec->board_config = STAC_925x_REF;
2448 goto again;
2449 }
2450 err = -EINVAL;
2451 }
Tobin Davis8e21c342007-01-08 11:04:17 +01002452 if (err < 0) {
2453 stac92xx_free(codec);
2454 return err;
2455 }
2456
2457 codec->patch_ops = stac92xx_patch_ops;
2458
2459 return 0;
2460}
2461
Matt2f2f4252005-04-13 14:45:30 +02002462static int patch_stac922x(struct hda_codec *codec)
2463{
2464 struct sigmatel_spec *spec;
Mattc7d4b2f2005-06-27 14:59:41 +02002465 int err;
Matt2f2f4252005-04-13 14:45:30 +02002466
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002467 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Matt2f2f4252005-04-13 14:45:30 +02002468 if (spec == NULL)
2469 return -ENOMEM;
2470
2471 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02002472 spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02002473 spec->pin_nids = stac922x_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002474 spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
2475 stac922x_models,
2476 stac922x_cfg_tbl);
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02002477 if (spec->board_config == STAC_INTEL_MAC_V3) {
Takashi Iwai3fc24d82007-02-16 13:27:18 +01002478 spec->gpio_mute = 1;
2479 /* Intel Macs have all same PCI SSID, so we need to check
2480 * codec SSID to distinguish the exact models
2481 */
Nicolas Boichat6f0778d2007-03-15 12:38:15 +01002482 printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
Takashi Iwai3fc24d82007-02-16 13:27:18 +01002483 switch (codec->subsystem_id) {
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02002484
2485 case 0x106b0800:
2486 spec->board_config = STAC_INTEL_MAC_V1;
Abhijit Bhopatkarc45e20e2007-04-17 11:57:16 +02002487 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02002488 case 0x106b0600:
2489 case 0x106b0700:
2490 spec->board_config = STAC_INTEL_MAC_V2;
Nicolas Boichat6f0778d2007-03-15 12:38:15 +01002491 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02002492 case 0x106b0e00:
2493 case 0x106b0f00:
2494 case 0x106b1600:
2495 case 0x106b1700:
2496 case 0x106b0200:
2497 case 0x106b1e00:
2498 spec->board_config = STAC_INTEL_MAC_V3;
Takashi Iwai3fc24d82007-02-16 13:27:18 +01002499 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02002500 case 0x106b1a00:
2501 case 0x00000100:
2502 spec->board_config = STAC_INTEL_MAC_V4;
Sylvain FORETf16928f2007-04-27 14:22:36 +02002503 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02002504 case 0x106b0a00:
2505 case 0x106b2200:
2506 spec->board_config = STAC_INTEL_MAC_V5;
Takashi Iwai0dae0f82007-05-21 12:41:29 +02002507 break;
Takashi Iwai3fc24d82007-02-16 13:27:18 +01002508 }
2509 }
2510
Takashi Iwai9e507ab2007-02-08 17:50:10 +01002511 again:
Richard Fish11b44bb2006-08-23 18:31:34 +02002512 if (spec->board_config < 0) {
2513 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
2514 "using BIOS defaults\n");
2515 err = stac92xx_save_bios_config_regs(codec);
2516 if (err < 0) {
2517 stac92xx_free(codec);
2518 return err;
2519 }
2520 spec->pin_configs = spec->bios_pin_configs;
2521 } else if (stac922x_brd_tbl[spec->board_config] != NULL) {
Matt Porter403d1942005-11-29 15:00:51 +01002522 spec->pin_configs = stac922x_brd_tbl[spec->board_config];
2523 stac92xx_set_config_regs(codec);
2524 }
Matt2f2f4252005-04-13 14:45:30 +02002525
Matt2f2f4252005-04-13 14:45:30 +02002526 spec->adc_nids = stac922x_adc_nids;
2527 spec->mux_nids = stac922x_mux_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01002528 spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
Matt Porter8b657272006-10-26 17:12:59 +02002529 spec->num_dmics = 0;
Mattc7d4b2f2005-06-27 14:59:41 +02002530
2531 spec->init = stac922x_core_init;
Matt2f2f4252005-04-13 14:45:30 +02002532 spec->mixer = stac922x_mixer;
Mattc7d4b2f2005-06-27 14:59:41 +02002533
2534 spec->multiout.dac_nids = spec->dac_nids;
Takashi Iwai19039bd2006-06-28 15:52:16 +02002535
Matt Porter3cc08dc2006-01-23 15:27:49 +01002536 err = stac92xx_parse_auto_config(codec, 0x08, 0x09);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01002537 if (!err) {
2538 if (spec->board_config < 0) {
2539 printk(KERN_WARNING "hda_codec: No auto-config is "
2540 "available, default to model=ref\n");
2541 spec->board_config = STAC_D945_REF;
2542 goto again;
2543 }
2544 err = -EINVAL;
2545 }
Matt Porter3cc08dc2006-01-23 15:27:49 +01002546 if (err < 0) {
2547 stac92xx_free(codec);
2548 return err;
2549 }
2550
2551 codec->patch_ops = stac92xx_patch_ops;
2552
Takashi Iwai807a46362007-05-29 19:01:37 +02002553 /* Fix Mux capture level; max to 2 */
2554 snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
2555 (0 << AC_AMPCAP_OFFSET_SHIFT) |
2556 (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2557 (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2558 (0 << AC_AMPCAP_MUTE_SHIFT));
2559
Matt Porter3cc08dc2006-01-23 15:27:49 +01002560 return 0;
2561}
2562
2563static int patch_stac927x(struct hda_codec *codec)
2564{
2565 struct sigmatel_spec *spec;
2566 int err;
2567
2568 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2569 if (spec == NULL)
2570 return -ENOMEM;
2571
2572 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02002573 spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02002574 spec->pin_nids = stac927x_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002575 spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
2576 stac927x_models,
2577 stac927x_cfg_tbl);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01002578 again:
Richard Fish11b44bb2006-08-23 18:31:34 +02002579 if (spec->board_config < 0) {
Matt Porter3cc08dc2006-01-23 15:27:49 +01002580 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n");
Richard Fish11b44bb2006-08-23 18:31:34 +02002581 err = stac92xx_save_bios_config_regs(codec);
2582 if (err < 0) {
2583 stac92xx_free(codec);
2584 return err;
2585 }
2586 spec->pin_configs = spec->bios_pin_configs;
2587 } else if (stac927x_brd_tbl[spec->board_config] != NULL) {
Matt Porter3cc08dc2006-01-23 15:27:49 +01002588 spec->pin_configs = stac927x_brd_tbl[spec->board_config];
2589 stac92xx_set_config_regs(codec);
2590 }
2591
Tobin Davis81d3dbd2006-08-22 19:44:45 +02002592 switch (spec->board_config) {
Tobin Davis93ed1502006-09-01 21:03:12 +02002593 case STAC_D965_3ST:
Tobin Davis81d3dbd2006-08-22 19:44:45 +02002594 spec->adc_nids = stac927x_adc_nids;
2595 spec->mux_nids = stac927x_mux_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01002596 spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
Matt Porter8b657272006-10-26 17:12:59 +02002597 spec->num_dmics = 0;
Tobin Davis93ed1502006-09-01 21:03:12 +02002598 spec->init = d965_core_init;
Tobin Davis81d3dbd2006-08-22 19:44:45 +02002599 spec->mixer = stac9227_mixer;
2600 break;
Tobin Davis93ed1502006-09-01 21:03:12 +02002601 case STAC_D965_5ST:
2602 spec->adc_nids = stac927x_adc_nids;
2603 spec->mux_nids = stac927x_mux_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01002604 spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
Matt Porter8b657272006-10-26 17:12:59 +02002605 spec->num_dmics = 0;
Tobin Davis93ed1502006-09-01 21:03:12 +02002606 spec->init = d965_core_init;
Tobin Davis81d3dbd2006-08-22 19:44:45 +02002607 spec->mixer = stac9227_mixer;
2608 break;
2609 default:
2610 spec->adc_nids = stac927x_adc_nids;
2611 spec->mux_nids = stac927x_mux_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01002612 spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
Matt Porter8b657272006-10-26 17:12:59 +02002613 spec->num_dmics = 0;
Tobin Davis81d3dbd2006-08-22 19:44:45 +02002614 spec->init = stac927x_core_init;
2615 spec->mixer = stac927x_mixer;
2616 }
Matt Porter3cc08dc2006-01-23 15:27:49 +01002617
2618 spec->multiout.dac_nids = spec->dac_nids;
Matthew Ranostay87d48362007-07-17 11:52:24 +02002619 /* GPIO0 High = Enable EAPD */
Takashi Iwai82599802007-07-31 15:56:24 +02002620 spec->gpio_mask = spec->gpio_data = 0x00000001;
2621 stac92xx_enable_gpio_mask(codec);
Matthew Ranostay92a22be2007-06-19 16:48:28 +02002622
Matt Porter3cc08dc2006-01-23 15:27:49 +01002623 err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01002624 if (!err) {
2625 if (spec->board_config < 0) {
2626 printk(KERN_WARNING "hda_codec: No auto-config is "
2627 "available, default to model=ref\n");
2628 spec->board_config = STAC_D965_REF;
2629 goto again;
2630 }
2631 err = -EINVAL;
2632 }
Mattc7d4b2f2005-06-27 14:59:41 +02002633 if (err < 0) {
2634 stac92xx_free(codec);
2635 return err;
2636 }
Matt2f2f4252005-04-13 14:45:30 +02002637
2638 codec->patch_ops = stac92xx_patch_ops;
2639
2640 return 0;
2641}
2642
Matt Porterf3302a52006-07-31 12:49:34 +02002643static int patch_stac9205(struct hda_codec *codec)
2644{
2645 struct sigmatel_spec *spec;
Takashi Iwai82599802007-07-31 15:56:24 +02002646 int err;
Matt Porterf3302a52006-07-31 12:49:34 +02002647
2648 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2649 if (spec == NULL)
2650 return -ENOMEM;
2651
2652 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02002653 spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02002654 spec->pin_nids = stac9205_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002655 spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
2656 stac9205_models,
2657 stac9205_cfg_tbl);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01002658 again:
Richard Fish11b44bb2006-08-23 18:31:34 +02002659 if (spec->board_config < 0) {
2660 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
2661 err = stac92xx_save_bios_config_regs(codec);
2662 if (err < 0) {
2663 stac92xx_free(codec);
2664 return err;
2665 }
2666 spec->pin_configs = spec->bios_pin_configs;
2667 } else {
Matt Porterf3302a52006-07-31 12:49:34 +02002668 spec->pin_configs = stac9205_brd_tbl[spec->board_config];
2669 stac92xx_set_config_regs(codec);
2670 }
2671
2672 spec->adc_nids = stac9205_adc_nids;
2673 spec->mux_nids = stac9205_mux_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01002674 spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
Matt Porter8b657272006-10-26 17:12:59 +02002675 spec->dmic_nids = stac9205_dmic_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01002676 spec->num_dmics = ARRAY_SIZE(stac9205_dmic_nids);
Matt Porter8b657272006-10-26 17:12:59 +02002677 spec->dmux_nid = 0x1d;
Matt Porterf3302a52006-07-31 12:49:34 +02002678
2679 spec->init = stac9205_core_init;
2680 spec->mixer = stac9205_mixer;
2681
2682 spec->multiout.dac_nids = spec->dac_nids;
Matthew Ranostay87d48362007-07-17 11:52:24 +02002683
Tobin Davisae0a8ed2007-08-13 15:50:29 +02002684 switch (spec->board_config){
2685 case STAC_9205_M43xx:
2686 case STAC_9205_DELL_M43:
Matthew Ranostay87d48362007-07-17 11:52:24 +02002687 /* Enable SPDIF in/out */
2688 stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
2689 stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
Matt Porter33382402006-12-18 13:17:28 +01002690
Takashi Iwai82599802007-07-31 15:56:24 +02002691 spec->gpio_mask = 0x00000007; /* GPIO0-2 */
Matthew Ranostay87d48362007-07-17 11:52:24 +02002692 /* GPIO0 High = EAPD, GPIO1 Low = DRM,
2693 * GPIO2 High = Headphone Mute
2694 */
Takashi Iwai82599802007-07-31 15:56:24 +02002695 spec->gpio_data = 0x00000005;
Tobin Davisae0a8ed2007-08-13 15:50:29 +02002696 break;
2697 default:
2698 /* GPIO0 High = EAPD */
2699 spec->gpio_mask = spec->gpio_data = 0x00000001;
2700 break;
2701 }
Matthew Ranostay87d48362007-07-17 11:52:24 +02002702
Takashi Iwai82599802007-07-31 15:56:24 +02002703 stac92xx_enable_gpio_mask(codec);
Matt Porterf3302a52006-07-31 12:49:34 +02002704 err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01002705 if (!err) {
2706 if (spec->board_config < 0) {
2707 printk(KERN_WARNING "hda_codec: No auto-config is "
2708 "available, default to model=ref\n");
2709 spec->board_config = STAC_9205_REF;
2710 goto again;
2711 }
2712 err = -EINVAL;
2713 }
Matt Porterf3302a52006-07-31 12:49:34 +02002714 if (err < 0) {
2715 stac92xx_free(codec);
2716 return err;
2717 }
2718
2719 codec->patch_ops = stac92xx_patch_ops;
2720
2721 return 0;
2722}
2723
Matt2f2f4252005-04-13 14:45:30 +02002724/*
Guillaume Munch6d859062006-08-22 17:15:47 +02002725 * STAC9872 hack
Takashi Iwaidb064e52006-03-16 16:04:58 +01002726 */
2727
Guillaume Munch99ccc562006-08-16 19:35:12 +02002728/* static config for Sony VAIO FE550G and Sony VAIO AR */
Takashi Iwaidb064e52006-03-16 16:04:58 +01002729static hda_nid_t vaio_dacs[] = { 0x2 };
2730#define VAIO_HP_DAC 0x5
2731static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ };
2732static hda_nid_t vaio_mux_nids[] = { 0x15 };
2733
2734static struct hda_input_mux vaio_mux = {
2735 .num_items = 2,
2736 .items = {
Takashi Iwaid7737812006-04-25 13:05:43 +02002737 /* { "HP", 0x0 }, */
Takashi Iwai1624cb92007-07-05 13:10:51 +02002738 { "Mic Jack", 0x1 },
2739 { "Internal Mic", 0x2 },
Takashi Iwaidb064e52006-03-16 16:04:58 +01002740 { "PCM", 0x3 },
2741 }
2742};
2743
2744static struct hda_verb vaio_init[] = {
2745 {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02002746 {0x0a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | STAC_HP_EVENT},
Takashi Iwaidb064e52006-03-16 16:04:58 +01002747 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
2748 {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
2749 {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
2750 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
Takashi Iwai1624cb92007-07-05 13:10:51 +02002751 {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
Takashi Iwaidb064e52006-03-16 16:04:58 +01002752 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
2753 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
2754 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
2755 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
2756 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
2757 {}
2758};
2759
Guillaume Munch6d859062006-08-22 17:15:47 +02002760static struct hda_verb vaio_ar_init[] = {
2761 {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
2762 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
2763 {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
2764 {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
2765/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
2766 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
Takashi Iwai1624cb92007-07-05 13:10:51 +02002767 {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
Guillaume Munch6d859062006-08-22 17:15:47 +02002768 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
2769 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
2770/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
2771 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
2772 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
2773 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
2774 {}
2775};
2776
Takashi Iwaidb064e52006-03-16 16:04:58 +01002777/* bind volumes of both NID 0x02 and 0x05 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02002778static struct hda_bind_ctls vaio_bind_master_vol = {
2779 .ops = &snd_hda_bind_vol,
2780 .values = {
2781 HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
2782 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
2783 0
2784 },
2785};
Takashi Iwaidb064e52006-03-16 16:04:58 +01002786
2787/* bind volumes of both NID 0x02 and 0x05 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02002788static struct hda_bind_ctls vaio_bind_master_sw = {
2789 .ops = &snd_hda_bind_sw,
2790 .values = {
2791 HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
2792 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
2793 0,
2794 },
2795};
Takashi Iwaidb064e52006-03-16 16:04:58 +01002796
2797static struct snd_kcontrol_new vaio_mixer[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02002798 HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
2799 HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
Takashi Iwaidb064e52006-03-16 16:04:58 +01002800 /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
2801 HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
2802 HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
2803 {
2804 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2805 .name = "Capture Source",
2806 .count = 1,
2807 .info = stac92xx_mux_enum_info,
2808 .get = stac92xx_mux_enum_get,
2809 .put = stac92xx_mux_enum_put,
2810 },
2811 {}
2812};
2813
Guillaume Munch6d859062006-08-22 17:15:47 +02002814static struct snd_kcontrol_new vaio_ar_mixer[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02002815 HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
2816 HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
Guillaume Munch6d859062006-08-22 17:15:47 +02002817 /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
2818 HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
2819 HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
2820 /*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT),
2821 HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/
2822 {
2823 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2824 .name = "Capture Source",
2825 .count = 1,
2826 .info = stac92xx_mux_enum_info,
2827 .get = stac92xx_mux_enum_get,
2828 .put = stac92xx_mux_enum_put,
2829 },
2830 {}
2831};
2832
2833static struct hda_codec_ops stac9872_patch_ops = {
Takashi Iwaidb064e52006-03-16 16:04:58 +01002834 .build_controls = stac92xx_build_controls,
2835 .build_pcms = stac92xx_build_pcms,
2836 .init = stac92xx_init,
2837 .free = stac92xx_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +02002838#ifdef SND_HDA_NEEDS_RESUME
Takashi Iwaidb064e52006-03-16 16:04:58 +01002839 .resume = stac92xx_resume,
2840#endif
2841};
2842
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02002843static int stac9872_vaio_init(struct hda_codec *codec)
2844{
2845 int err;
2846
2847 err = stac92xx_init(codec);
2848 if (err < 0)
2849 return err;
2850 if (codec->patch_ops.unsol_event)
2851 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
2852 return 0;
2853}
2854
2855static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
2856{
2857 if (get_pin_presence(codec, 0x0a)) {
2858 stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
2859 stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
2860 } else {
2861 stac92xx_reset_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
2862 stac92xx_set_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
2863 }
2864}
2865
2866static void stac9872_vaio_unsol_event(struct hda_codec *codec, unsigned int res)
2867{
2868 switch (res >> 26) {
2869 case STAC_HP_EVENT:
2870 stac9872_vaio_hp_detect(codec, res);
2871 break;
2872 }
2873}
2874
2875static struct hda_codec_ops stac9872_vaio_patch_ops = {
2876 .build_controls = stac92xx_build_controls,
2877 .build_pcms = stac92xx_build_pcms,
2878 .init = stac9872_vaio_init,
2879 .free = stac92xx_free,
2880 .unsol_event = stac9872_vaio_unsol_event,
2881#ifdef CONFIG_PM
2882 .resume = stac92xx_resume,
2883#endif
2884};
2885
Guillaume Munch6d859062006-08-22 17:15:47 +02002886enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */
2887 CXD9872RD_VAIO,
2888 /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */
2889 STAC9872AK_VAIO,
2890 /* Unknown. id=0x83847661 and subsys=0x104D1200. */
2891 STAC9872K_VAIO,
2892 /* AR Series. id=0x83847664 and subsys=104D1300 */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002893 CXD9872AKD_VAIO,
2894 STAC_9872_MODELS,
2895};
Takashi Iwaidb064e52006-03-16 16:04:58 +01002896
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002897static const char *stac9872_models[STAC_9872_MODELS] = {
2898 [CXD9872RD_VAIO] = "vaio",
2899 [CXD9872AKD_VAIO] = "vaio-ar",
2900};
2901
2902static struct snd_pci_quirk stac9872_cfg_tbl[] = {
2903 SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO),
2904 SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO),
2905 SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO),
Tobin Davis68e22542007-03-12 11:36:39 +01002906 SND_PCI_QUIRK(0x104d, 0x8205, "Sony VAIO AR", CXD9872AKD_VAIO),
Takashi Iwaidb064e52006-03-16 16:04:58 +01002907 {}
2908};
2909
Guillaume Munch6d859062006-08-22 17:15:47 +02002910static int patch_stac9872(struct hda_codec *codec)
Takashi Iwaidb064e52006-03-16 16:04:58 +01002911{
2912 struct sigmatel_spec *spec;
2913 int board_config;
2914
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002915 board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
2916 stac9872_models,
2917 stac9872_cfg_tbl);
Takashi Iwaidb064e52006-03-16 16:04:58 +01002918 if (board_config < 0)
2919 /* unknown config, let generic-parser do its job... */
2920 return snd_hda_parse_generic_codec(codec);
2921
2922 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2923 if (spec == NULL)
2924 return -ENOMEM;
2925
2926 codec->spec = spec;
2927 switch (board_config) {
Guillaume Munch6d859062006-08-22 17:15:47 +02002928 case CXD9872RD_VAIO:
2929 case STAC9872AK_VAIO:
2930 case STAC9872K_VAIO:
Takashi Iwaidb064e52006-03-16 16:04:58 +01002931 spec->mixer = vaio_mixer;
2932 spec->init = vaio_init;
2933 spec->multiout.max_channels = 2;
2934 spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
2935 spec->multiout.dac_nids = vaio_dacs;
2936 spec->multiout.hp_nid = VAIO_HP_DAC;
2937 spec->num_adcs = ARRAY_SIZE(vaio_adcs);
2938 spec->adc_nids = vaio_adcs;
2939 spec->input_mux = &vaio_mux;
2940 spec->mux_nids = vaio_mux_nids;
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02002941 codec->patch_ops = stac9872_vaio_patch_ops;
Takashi Iwaidb064e52006-03-16 16:04:58 +01002942 break;
Guillaume Munch6d859062006-08-22 17:15:47 +02002943
2944 case CXD9872AKD_VAIO:
2945 spec->mixer = vaio_ar_mixer;
2946 spec->init = vaio_ar_init;
2947 spec->multiout.max_channels = 2;
2948 spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
2949 spec->multiout.dac_nids = vaio_dacs;
2950 spec->multiout.hp_nid = VAIO_HP_DAC;
2951 spec->num_adcs = ARRAY_SIZE(vaio_adcs);
2952 spec->adc_nids = vaio_adcs;
2953 spec->input_mux = &vaio_mux;
2954 spec->mux_nids = vaio_mux_nids;
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02002955 codec->patch_ops = stac9872_patch_ops;
Guillaume Munch6d859062006-08-22 17:15:47 +02002956 break;
Takashi Iwaidb064e52006-03-16 16:04:58 +01002957 }
2958
Takashi Iwaidb064e52006-03-16 16:04:58 +01002959 return 0;
2960}
2961
2962
2963/*
Matt2f2f4252005-04-13 14:45:30 +02002964 * patch entries
2965 */
2966struct hda_codec_preset snd_hda_preset_sigmatel[] = {
2967 { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
2968 { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
2969 { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
2970 { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
2971 { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
2972 { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
2973 { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
Matt Porter22a27c72006-07-06 18:49:10 +02002974 { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
2975 { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
2976 { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
2977 { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
2978 { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
2979 { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
Matt Porter3cc08dc2006-01-23 15:27:49 +01002980 { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
2981 { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
2982 { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
2983 { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
2984 { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
2985 { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
2986 { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
2987 { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
2988 { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
2989 { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
Tobin Davis8e21c342007-01-08 11:04:17 +01002990 { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
2991 { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
2992 { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
2993 { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
2994 { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
2995 { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
Guillaume Munch6d859062006-08-22 17:15:47 +02002996 /* The following does not take into account .id=0x83847661 when subsys =
2997 * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
2998 * currently not fully supported.
2999 */
3000 { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
3001 { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
3002 { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
Matt Porterf3302a52006-07-31 12:49:34 +02003003 { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
3004 { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
3005 { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
3006 { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
3007 { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
3008 { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
3009 { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
3010 { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
Matt2f2f4252005-04-13 14:45:30 +02003011 {} /* terminator */
3012};