blob: 47d3536a6576564d282862e20d0d445907b7b154 [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
Matt2f2f4252005-04-13 14:45:30 +020027#include <linux/init.h>
28#include <linux/delay.h>
29#include <linux/slab.h>
30#include <linux/pci.h>
31#include <sound/core.h>
Mattc7d4b2f2005-06-27 14:59:41 +020032#include <sound/asoundef.h>
Matt2f2f4252005-04-13 14:45:30 +020033#include "hda_codec.h"
34#include "hda_local.h"
Harvey Harrison3c9a3202008-02-29 11:59:26 +010035#include "hda_patch.h"
Matt2f2f4252005-04-13 14:45:30 +020036
Matt4e550962005-07-04 17:51:39 +020037#define NUM_CONTROL_ALLOC 32
Matthew Ranostaya64135a2008-01-10 16:55:06 +010038#define STAC_PWR_EVENT 0x20
39#define STAC_HP_EVENT 0x30
Matt4e550962005-07-04 17:51:39 +020040
Takashi Iwaif5fcc132006-11-24 17:07:44 +010041enum {
42 STAC_REF,
Tobin Davisbf277782008-02-03 20:31:47 +010043 STAC_9200_OQO,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020044 STAC_9200_DELL_D21,
45 STAC_9200_DELL_D22,
46 STAC_9200_DELL_D23,
47 STAC_9200_DELL_M21,
48 STAC_9200_DELL_M22,
49 STAC_9200_DELL_M23,
50 STAC_9200_DELL_M24,
51 STAC_9200_DELL_M25,
52 STAC_9200_DELL_M26,
53 STAC_9200_DELL_M27,
Takashi Iwai1194b5b2007-10-10 10:04:26 +020054 STAC_9200_GATEWAY,
Takashi Iwaif5fcc132006-11-24 17:07:44 +010055 STAC_9200_MODELS
56};
57
58enum {
59 STAC_9205_REF,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020060 STAC_9205_DELL_M42,
Tobin Davisae0a8ed2007-08-13 15:50:29 +020061 STAC_9205_DELL_M43,
62 STAC_9205_DELL_M44,
Takashi Iwaif5fcc132006-11-24 17:07:44 +010063 STAC_9205_MODELS
64};
65
66enum {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +010067 STAC_92HD73XX_REF,
Matthew Ranostaya7662642008-02-21 07:51:14 +010068 STAC_DELL_M6,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +010069 STAC_92HD73XX_MODELS
70};
71
72enum {
Matthew Ranostaye035b842007-11-06 11:53:55 +010073 STAC_92HD71BXX_REF,
Matthew Ranostaya7662642008-02-21 07:51:14 +010074 STAC_DELL_M4_1,
75 STAC_DELL_M4_2,
Matthew Ranostaye035b842007-11-06 11:53:55 +010076 STAC_92HD71BXX_MODELS
77};
78
79enum {
Tobin Davis8e21c342007-01-08 11:04:17 +010080 STAC_925x_REF,
81 STAC_M2_2,
82 STAC_MA6,
Tobin Davis2c11f952007-05-17 09:36:34 +020083 STAC_PA6,
Tobin Davis8e21c342007-01-08 11:04:17 +010084 STAC_925x_MODELS
85};
86
87enum {
Takashi Iwaif5fcc132006-11-24 17:07:44 +010088 STAC_D945_REF,
89 STAC_D945GTP3,
90 STAC_D945GTP5,
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +020091 STAC_INTEL_MAC_V1,
92 STAC_INTEL_MAC_V2,
93 STAC_INTEL_MAC_V3,
94 STAC_INTEL_MAC_V4,
95 STAC_INTEL_MAC_V5,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020096 /* for backward compatibility */
Takashi Iwaif5fcc132006-11-24 17:07:44 +010097 STAC_MACMINI,
Takashi Iwai3fc24d82007-02-16 13:27:18 +010098 STAC_MACBOOK,
Nicolas Boichat6f0778d2007-03-15 12:38:15 +010099 STAC_MACBOOK_PRO_V1,
100 STAC_MACBOOK_PRO_V2,
Sylvain FORETf16928f2007-04-27 14:22:36 +0200101 STAC_IMAC_INTEL,
Takashi Iwai0dae0f82007-05-21 12:41:29 +0200102 STAC_IMAC_INTEL_20,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200103 STAC_922X_DELL_D81,
104 STAC_922X_DELL_D82,
105 STAC_922X_DELL_M81,
106 STAC_922X_DELL_M82,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100107 STAC_922X_MODELS
108};
109
110enum {
111 STAC_D965_REF,
112 STAC_D965_3ST,
113 STAC_D965_5ST,
Tobin Davis4ff076e2007-08-07 11:48:12 +0200114 STAC_DELL_3ST,
Matthew Ranostay8e9068b2007-12-17 11:58:13 +0100115 STAC_DELL_BIOS,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100116 STAC_927X_MODELS
117};
Matt Porter403d1942005-11-29 15:00:51 +0100118
Matt2f2f4252005-04-13 14:45:30 +0200119struct sigmatel_spec {
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100120 struct snd_kcontrol_new *mixers[4];
Mattc7d4b2f2005-06-27 14:59:41 +0200121 unsigned int num_mixers;
122
Matt Porter403d1942005-11-29 15:00:51 +0100123 int board_config;
Mattc7d4b2f2005-06-27 14:59:41 +0200124 unsigned int surr_switch: 1;
Matt Porter403d1942005-11-29 15:00:51 +0100125 unsigned int line_switch: 1;
126 unsigned int mic_switch: 1;
Matt Porter3cc08dc2006-01-23 15:27:49 +0100127 unsigned int alt_switch: 1;
Takashi Iwai82bc9552006-03-21 11:24:42 +0100128 unsigned int hp_detect: 1;
Mattc7d4b2f2005-06-27 14:59:41 +0200129
Matthew Ranostay4fe51952008-01-29 15:28:44 +0100130 /* gpio lines */
131 unsigned int gpio_mask;
132 unsigned int gpio_dir;
133 unsigned int gpio_data;
134 unsigned int gpio_mute;
135
136 /* analog loopback */
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100137 unsigned char aloopback_mask;
138 unsigned char aloopback_shift;
Takashi Iwai82599802007-07-31 15:56:24 +0200139
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100140 /* power management */
141 unsigned int num_pwrs;
142 hda_nid_t *pwr_nids;
Matthew Ranostayb76c8502008-02-06 14:49:44 +0100143 hda_nid_t *dac_list;
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100144
Matt2f2f4252005-04-13 14:45:30 +0200145 /* playback */
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100146 struct hda_input_mux *mono_mux;
147 unsigned int cur_mmux;
Matt2f2f4252005-04-13 14:45:30 +0200148 struct hda_multi_out multiout;
Matt Porter3cc08dc2006-01-23 15:27:49 +0100149 hda_nid_t dac_nids[5];
Matt2f2f4252005-04-13 14:45:30 +0200150
151 /* capture */
152 hda_nid_t *adc_nids;
Matt2f2f4252005-04-13 14:45:30 +0200153 unsigned int num_adcs;
Mattdabbed62005-06-14 10:19:34 +0200154 hda_nid_t *mux_nids;
155 unsigned int num_muxes;
Matt Porter8b657272006-10-26 17:12:59 +0200156 hda_nid_t *dmic_nids;
157 unsigned int num_dmics;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100158 hda_nid_t *dmux_nids;
Takashi Iwai16970552007-12-18 18:05:52 +0100159 unsigned int num_dmuxes;
Mattdabbed62005-06-14 10:19:34 +0200160 hda_nid_t dig_in_nid;
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100161 hda_nid_t mono_nid;
Matt2f2f4252005-04-13 14:45:30 +0200162
Matt2f2f4252005-04-13 14:45:30 +0200163 /* pin widgets */
164 hda_nid_t *pin_nids;
165 unsigned int num_pins;
Matt2f2f4252005-04-13 14:45:30 +0200166 unsigned int *pin_configs;
Richard Fish11b44bb2006-08-23 18:31:34 +0200167 unsigned int *bios_pin_configs;
Matt2f2f4252005-04-13 14:45:30 +0200168
169 /* codec specific stuff */
170 struct hda_verb *init;
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100171 struct snd_kcontrol_new *mixer;
Matt2f2f4252005-04-13 14:45:30 +0200172
173 /* capture source */
Matt Porter8b657272006-10-26 17:12:59 +0200174 struct hda_input_mux *dinput_mux;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100175 unsigned int cur_dmux[2];
Mattc7d4b2f2005-06-27 14:59:41 +0200176 struct hda_input_mux *input_mux;
Matt Porter3cc08dc2006-01-23 15:27:49 +0100177 unsigned int cur_mux[3];
Matt2f2f4252005-04-13 14:45:30 +0200178
Matt Porter403d1942005-11-29 15:00:51 +0100179 /* i/o switches */
180 unsigned int io_switch[2];
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +0200181 unsigned int clfe_swap;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200182 unsigned int aloopback;
Matt2f2f4252005-04-13 14:45:30 +0200183
Mattc7d4b2f2005-06-27 14:59:41 +0200184 struct hda_pcm pcm_rec[2]; /* PCM information */
185
186 /* dynamic controls and input_mux */
187 struct auto_pin_cfg autocfg;
188 unsigned int num_kctl_alloc, num_kctl_used;
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100189 struct snd_kcontrol_new *kctl_alloc;
Matt Porter8b657272006-10-26 17:12:59 +0200190 struct hda_input_mux private_dimux;
Mattc7d4b2f2005-06-27 14:59:41 +0200191 struct hda_input_mux private_imux;
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100192 struct hda_input_mux private_mono_mux;
Matt2f2f4252005-04-13 14:45:30 +0200193};
194
195static hda_nid_t stac9200_adc_nids[1] = {
196 0x03,
197};
198
199static hda_nid_t stac9200_mux_nids[1] = {
200 0x0c,
201};
202
203static hda_nid_t stac9200_dac_nids[1] = {
204 0x02,
205};
206
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100207static hda_nid_t stac92hd73xx_pwr_nids[8] = {
208 0x0a, 0x0b, 0x0c, 0xd, 0x0e,
209 0x0f, 0x10, 0x11
210};
211
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100212static hda_nid_t stac92hd73xx_adc_nids[2] = {
213 0x1a, 0x1b
214};
215
216#define STAC92HD73XX_NUM_DMICS 2
217static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
218 0x13, 0x14, 0
219};
220
221#define STAC92HD73_DAC_COUNT 5
222static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = {
223 0x15, 0x16, 0x17, 0x18, 0x19,
224};
225
226static hda_nid_t stac92hd73xx_mux_nids[4] = {
227 0x28, 0x29, 0x2a, 0x2b,
228};
229
230static hda_nid_t stac92hd73xx_dmux_nids[2] = {
231 0x20, 0x21,
232};
233
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100234static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
235 0x0a, 0x0d, 0x0f
236};
237
Matthew Ranostaye035b842007-11-06 11:53:55 +0100238static hda_nid_t stac92hd71bxx_adc_nids[2] = {
239 0x12, 0x13,
240};
241
242static hda_nid_t stac92hd71bxx_mux_nids[2] = {
243 0x1a, 0x1b
244};
245
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100246static hda_nid_t stac92hd71bxx_dmux_nids[1] = {
247 0x1c,
248};
249
Takashi Iwaiaea7bb02008-02-25 18:26:41 +0100250static hda_nid_t stac92hd71bxx_dac_nids[1] = {
Matthew Ranostaye035b842007-11-06 11:53:55 +0100251 0x10, /*0x11, */
252};
253
254#define STAC92HD71BXX_NUM_DMICS 2
255static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
256 0x18, 0x19, 0
257};
258
Tobin Davis8e21c342007-01-08 11:04:17 +0100259static hda_nid_t stac925x_adc_nids[1] = {
260 0x03,
261};
262
263static hda_nid_t stac925x_mux_nids[1] = {
264 0x0f,
265};
266
267static hda_nid_t stac925x_dac_nids[1] = {
268 0x02,
269};
270
Takashi Iwaif6e98522007-10-16 14:27:04 +0200271#define STAC925X_NUM_DMICS 1
272static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = {
273 0x15, 0
Tobin Davis2c11f952007-05-17 09:36:34 +0200274};
275
Takashi Iwai16970552007-12-18 18:05:52 +0100276static hda_nid_t stac925x_dmux_nids[1] = {
277 0x14,
278};
279
Matt2f2f4252005-04-13 14:45:30 +0200280static hda_nid_t stac922x_adc_nids[2] = {
281 0x06, 0x07,
282};
283
284static hda_nid_t stac922x_mux_nids[2] = {
285 0x12, 0x13,
286};
287
Matt Porter3cc08dc2006-01-23 15:27:49 +0100288static hda_nid_t stac927x_adc_nids[3] = {
289 0x07, 0x08, 0x09
290};
291
292static hda_nid_t stac927x_mux_nids[3] = {
293 0x15, 0x16, 0x17
294};
295
Matthew Ranostayb76c8502008-02-06 14:49:44 +0100296static hda_nid_t stac927x_dac_nids[6] = {
297 0x02, 0x03, 0x04, 0x05, 0x06, 0
298};
299
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100300static hda_nid_t stac927x_dmux_nids[1] = {
301 0x1b,
302};
303
Matthew Ranostay7f168592007-10-18 17:38:17 +0200304#define STAC927X_NUM_DMICS 2
305static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
306 0x13, 0x14, 0
307};
308
Matt Porterf3302a52006-07-31 12:49:34 +0200309static hda_nid_t stac9205_adc_nids[2] = {
310 0x12, 0x13
311};
312
313static hda_nid_t stac9205_mux_nids[2] = {
314 0x19, 0x1a
315};
316
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100317static hda_nid_t stac9205_dmux_nids[1] = {
Takashi Iwai16970552007-12-18 18:05:52 +0100318 0x1d,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100319};
320
Takashi Iwaif6e98522007-10-16 14:27:04 +0200321#define STAC9205_NUM_DMICS 2
322static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
323 0x17, 0x18, 0
Matt Porter8b657272006-10-26 17:12:59 +0200324};
325
Mattc7d4b2f2005-06-27 14:59:41 +0200326static hda_nid_t stac9200_pin_nids[8] = {
Tobin Davis93ed1502006-09-01 21:03:12 +0200327 0x08, 0x09, 0x0d, 0x0e,
328 0x0f, 0x10, 0x11, 0x12,
Matt2f2f4252005-04-13 14:45:30 +0200329};
330
Tobin Davis8e21c342007-01-08 11:04:17 +0100331static hda_nid_t stac925x_pin_nids[8] = {
332 0x07, 0x08, 0x0a, 0x0b,
333 0x0c, 0x0d, 0x10, 0x11,
334};
335
Matt2f2f4252005-04-13 14:45:30 +0200336static hda_nid_t stac922x_pin_nids[10] = {
337 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
338 0x0f, 0x10, 0x11, 0x15, 0x1b,
339};
340
Matthew Ranostaya7662642008-02-21 07:51:14 +0100341static hda_nid_t stac92hd73xx_pin_nids[13] = {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100342 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
343 0x0f, 0x10, 0x11, 0x12, 0x13,
Matthew Ranostaya7662642008-02-21 07:51:14 +0100344 0x14, 0x1e, 0x22
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100345};
346
Matthew Ranostaye035b842007-11-06 11:53:55 +0100347static hda_nid_t stac92hd71bxx_pin_nids[10] = {
348 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
349 0x0f, 0x14, 0x18, 0x19, 0x1e,
350};
351
Matt Porter3cc08dc2006-01-23 15:27:49 +0100352static hda_nid_t stac927x_pin_nids[14] = {
353 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
354 0x0f, 0x10, 0x11, 0x12, 0x13,
355 0x14, 0x21, 0x22, 0x23,
356};
357
Matt Porterf3302a52006-07-31 12:49:34 +0200358static hda_nid_t stac9205_pin_nids[12] = {
359 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
360 0x0f, 0x14, 0x16, 0x17, 0x18,
361 0x21, 0x22,
Matt Porterf3302a52006-07-31 12:49:34 +0200362};
363
Matt Porter8b657272006-10-26 17:12:59 +0200364static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
365 struct snd_ctl_elem_info *uinfo)
366{
367 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
368 struct sigmatel_spec *spec = codec->spec;
369 return snd_hda_input_mux_info(spec->dinput_mux, uinfo);
370}
371
372static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
373 struct snd_ctl_elem_value *ucontrol)
374{
375 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
376 struct sigmatel_spec *spec = codec->spec;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100377 unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Matt Porter8b657272006-10-26 17:12:59 +0200378
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100379 ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx];
Matt Porter8b657272006-10-26 17:12:59 +0200380 return 0;
381}
382
383static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
384 struct snd_ctl_elem_value *ucontrol)
385{
386 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
387 struct sigmatel_spec *spec = codec->spec;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100388 unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Matt Porter8b657272006-10-26 17:12:59 +0200389
390 return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100391 spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
Matt Porter8b657272006-10-26 17:12:59 +0200392}
393
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100394static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Matt2f2f4252005-04-13 14:45:30 +0200395{
396 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
397 struct sigmatel_spec *spec = codec->spec;
Mattc7d4b2f2005-06-27 14:59:41 +0200398 return snd_hda_input_mux_info(spec->input_mux, uinfo);
Matt2f2f4252005-04-13 14:45:30 +0200399}
400
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100401static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Matt2f2f4252005-04-13 14:45:30 +0200402{
403 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
404 struct sigmatel_spec *spec = codec->spec;
405 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
406
407 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
408 return 0;
409}
410
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100411static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Matt2f2f4252005-04-13 14:45:30 +0200412{
413 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
414 struct sigmatel_spec *spec = codec->spec;
415 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
416
Mattc7d4b2f2005-06-27 14:59:41 +0200417 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Matt2f2f4252005-04-13 14:45:30 +0200418 spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
419}
420
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100421static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
422 struct snd_ctl_elem_info *uinfo)
423{
424 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
425 struct sigmatel_spec *spec = codec->spec;
426 return snd_hda_input_mux_info(spec->mono_mux, uinfo);
427}
428
429static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
430 struct snd_ctl_elem_value *ucontrol)
431{
432 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
433 struct sigmatel_spec *spec = codec->spec;
434
435 ucontrol->value.enumerated.item[0] = spec->cur_mmux;
436 return 0;
437}
438
439static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
440 struct snd_ctl_elem_value *ucontrol)
441{
442 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
443 struct sigmatel_spec *spec = codec->spec;
444
445 return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
446 spec->mono_nid, &spec->cur_mmux);
447}
448
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200449#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
450
451static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
452 struct snd_ctl_elem_value *ucontrol)
453{
454 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100455 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200456 struct sigmatel_spec *spec = codec->spec;
457
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100458 ucontrol->value.integer.value[0] = !!(spec->aloopback &
459 (spec->aloopback_mask << idx));
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200460 return 0;
461}
462
463static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol,
464 struct snd_ctl_elem_value *ucontrol)
465{
466 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
467 struct sigmatel_spec *spec = codec->spec;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100468 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200469 unsigned int dac_mode;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100470 unsigned int val, idx_val;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200471
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100472 idx_val = spec->aloopback_mask << idx;
473 if (ucontrol->value.integer.value[0])
474 val = spec->aloopback | idx_val;
475 else
476 val = spec->aloopback & ~idx_val;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100477 if (spec->aloopback == val)
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200478 return 0;
479
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100480 spec->aloopback = val;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200481
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100482 /* Only return the bits defined by the shift value of the
483 * first two bytes of the mask
484 */
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200485 dac_mode = snd_hda_codec_read(codec, codec->afg, 0,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100486 kcontrol->private_value & 0xFFFF, 0x0);
487 dac_mode >>= spec->aloopback_shift;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200488
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100489 if (spec->aloopback & idx_val) {
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200490 snd_hda_power_up(codec);
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100491 dac_mode |= idx_val;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200492 } else {
493 snd_hda_power_down(codec);
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100494 dac_mode &= ~idx_val;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200495 }
496
497 snd_hda_codec_write_cache(codec, codec->afg, 0,
498 kcontrol->private_value >> 16, dac_mode);
499
500 return 1;
501}
502
Mattc7d4b2f2005-06-27 14:59:41 +0200503static struct hda_verb stac9200_core_init[] = {
Matt2f2f4252005-04-13 14:45:30 +0200504 /* set dac0mux for dac converter */
Mattc7d4b2f2005-06-27 14:59:41 +0200505 { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
Matt2f2f4252005-04-13 14:45:30 +0200506 {}
507};
508
Takashi Iwai1194b5b2007-10-10 10:04:26 +0200509static struct hda_verb stac9200_eapd_init[] = {
510 /* set dac0mux for dac converter */
511 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
512 {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
513 {}
514};
515
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100516static struct hda_verb stac92hd73xx_6ch_core_init[] = {
517 /* set master volume and direct control */
518 { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
519 /* setup audio connections */
520 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
521 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
522 { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
523 /* setup adcs to point to mixer */
524 { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
525 { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100526 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
527 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
528 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
529 /* setup import muxs */
530 { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
531 { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
532 { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
533 { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
534 {}
535};
536
537static struct hda_verb stac92hd73xx_8ch_core_init[] = {
538 /* set master volume and direct control */
539 { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
540 /* setup audio connections */
541 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
542 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
543 { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
544 /* connect hp ports to dac3 */
545 { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03},
546 { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03},
547 /* setup adcs to point to mixer */
548 { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
549 { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100550 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
551 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
552 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
553 /* setup import muxs */
554 { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
555 { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
556 { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
557 { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
558 {}
559};
560
561static struct hda_verb stac92hd73xx_10ch_core_init[] = {
562 /* set master volume and direct control */
563 { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
564 /* setup audio connections */
565 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
566 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
567 { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 },
568 /* dac3 is connected to import3 mux */
569 { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
570 /* connect hp ports to dac4 */
571 { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04},
572 { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04},
573 /* setup adcs to point to mixer */
574 { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
575 { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100576 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
577 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
578 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
579 /* setup import muxs */
580 { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
581 { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
582 { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
583 { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
584 {}
585};
586
Matthew Ranostaye035b842007-11-06 11:53:55 +0100587static struct hda_verb stac92hd71bxx_core_init[] = {
588 /* set master volume and direct control */
589 { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
590 /* connect headphone jack to dac1 */
591 { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
Matthew Ranostay541eee82007-12-14 12:08:04 +0100592 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
593 /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
594 { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
595 { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
596 { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Matthew Ranostay541eee82007-12-14 12:08:04 +0100597};
598
599static struct hda_verb stac92hd71bxx_analog_core_init[] = {
600 /* set master volume and direct control */
601 { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
602 /* connect headphone jack to dac1 */
603 { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
Matthew Ranostay9b359472007-11-07 13:03:12 +0100604 /* connect ports 0d and 0f to audio mixer */
605 { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
606 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100607 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
Matthew Ranostay9b359472007-11-07 13:03:12 +0100608 /* unmute dac0 input in audio mixer */
609 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
Matthew Ranostaye035b842007-11-06 11:53:55 +0100610 /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
611 { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
612 { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
613 { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Matthew Ranostaye035b842007-11-06 11:53:55 +0100614 {}
615};
616
Tobin Davis8e21c342007-01-08 11:04:17 +0100617static struct hda_verb stac925x_core_init[] = {
618 /* set dac0mux for dac converter */
619 { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
620 {}
621};
622
Mattc7d4b2f2005-06-27 14:59:41 +0200623static struct hda_verb stac922x_core_init[] = {
Matt2f2f4252005-04-13 14:45:30 +0200624 /* set master volume and direct control */
Mattc7d4b2f2005-06-27 14:59:41 +0200625 { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
Matt2f2f4252005-04-13 14:45:30 +0200626 {}
627};
628
Tobin Davis93ed1502006-09-01 21:03:12 +0200629static struct hda_verb d965_core_init[] = {
Takashi Iwai19039bd2006-06-28 15:52:16 +0200630 /* set master volume and direct control */
Tobin Davis93ed1502006-09-01 21:03:12 +0200631 { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
Takashi Iwai19039bd2006-06-28 15:52:16 +0200632 /* unmute node 0x1b */
633 { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
634 /* select node 0x03 as DAC */
635 { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
636 {}
637};
638
Matt Porter3cc08dc2006-01-23 15:27:49 +0100639static struct hda_verb stac927x_core_init[] = {
640 /* set master volume and direct control */
641 { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
642 {}
643};
644
Matt Porterf3302a52006-07-31 12:49:34 +0200645static struct hda_verb stac9205_core_init[] = {
646 /* set master volume and direct control */
647 { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
648 {}
649};
650
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100651#define STAC_MONO_MUX \
652 { \
653 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
654 .name = "Mono Mux", \
655 .count = 1, \
656 .info = stac92xx_mono_mux_enum_info, \
657 .get = stac92xx_mono_mux_enum_get, \
658 .put = stac92xx_mono_mux_enum_put, \
659 }
660
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200661#define STAC_INPUT_SOURCE(cnt) \
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200662 { \
663 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
664 .name = "Input Source", \
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200665 .count = cnt, \
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200666 .info = stac92xx_mux_enum_info, \
667 .get = stac92xx_mux_enum_get, \
668 .put = stac92xx_mux_enum_put, \
669 }
670
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100671#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200672 { \
673 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
674 .name = "Analog Loopback", \
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100675 .count = cnt, \
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200676 .info = stac92xx_aloopback_info, \
677 .get = stac92xx_aloopback_get, \
678 .put = stac92xx_aloopback_put, \
679 .private_value = verb_read | (verb_write << 16), \
680 }
681
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100682static struct snd_kcontrol_new stac9200_mixer[] = {
Matt2f2f4252005-04-13 14:45:30 +0200683 HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
684 HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200685 STAC_INPUT_SOURCE(1),
Matt2f2f4252005-04-13 14:45:30 +0200686 HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
687 HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
Mattc7d4b2f2005-06-27 14:59:41 +0200688 HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT),
Matt2f2f4252005-04-13 14:45:30 +0200689 { } /* end */
690};
691
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100692static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100693 STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
694
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100695 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
696 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
697
698 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
699 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
700
701 HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
702 HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
703
704 HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
705 HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
706
707 HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
708 HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
709
710 HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
711 HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
712
713 HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
714 HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
715 { } /* end */
716};
717
718static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100719 STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
720
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100721 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
722 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
723
724 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
725 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
726
727 HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
728 HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
729
730 HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
731 HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
732
733 HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
734 HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
735
736 HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
737 HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
738
739 HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
740 HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
741 { } /* end */
742};
743
744static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100745 STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
746
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100747 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
748 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
749
750 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
751 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
752
753 HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
754 HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
755
756 HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
757 HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
758
759 HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
760 HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
761
762 HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
763 HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
764
765 HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
766 HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
767 { } /* end */
768};
769
Matthew Ranostay541eee82007-12-14 12:08:04 +0100770static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
Matthew Ranostaye035b842007-11-06 11:53:55 +0100771 STAC_INPUT_SOURCE(2),
Matthew Ranostaye035b842007-11-06 11:53:55 +0100772
Matthew Ranostay9b359472007-11-07 13:03:12 +0100773 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
774 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
775 HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
776
777 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
778 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
779 HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
780
781 HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
782 HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
Matthew Ranostaye035b842007-11-06 11:53:55 +0100783 { } /* end */
784};
785
Matthew Ranostay541eee82007-12-14 12:08:04 +0100786static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
Matthew Ranostay541eee82007-12-14 12:08:04 +0100787 STAC_INPUT_SOURCE(2),
788 STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
789
Matthew Ranostay541eee82007-12-14 12:08:04 +0100790 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
791 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
792 HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
793
794 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
795 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
796 HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
797 { } /* end */
798};
799
Tobin Davis8e21c342007-01-08 11:04:17 +0100800static struct snd_kcontrol_new stac925x_mixer[] = {
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200801 STAC_INPUT_SOURCE(1),
Tobin Davis8e21c342007-01-08 11:04:17 +0100802 HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
803 HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_OUTPUT),
804 HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT),
805 { } /* end */
806};
807
Takashi Iwaid1d985f2006-11-23 19:27:12 +0100808static struct snd_kcontrol_new stac9205_mixer[] = {
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200809 STAC_INPUT_SOURCE(2),
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100810 STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200811
812 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
813 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
814 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x19, 0x0, HDA_OUTPUT),
815
816 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT),
817 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT),
818 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x1A, 0x0, HDA_OUTPUT),
819
820 { } /* end */
821};
822
823/* This needs to be generated dynamically based on sequence */
824static struct snd_kcontrol_new stac922x_mixer[] = {
825 STAC_INPUT_SOURCE(2),
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200826 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),
827 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT),
828 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x12, 0x0, HDA_OUTPUT),
829
830 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT),
831 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT),
832 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x13, 0x0, HDA_OUTPUT),
833 { } /* end */
834};
835
836
837static struct snd_kcontrol_new stac927x_mixer[] = {
838 STAC_INPUT_SOURCE(3),
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100839 STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200840
841 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
842 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
843 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x15, 0x0, HDA_OUTPUT),
844
845 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT),
846 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT),
847 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x16, 0x0, HDA_OUTPUT),
848
849 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT),
850 HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT),
851 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x2, 0x17, 0x0, HDA_OUTPUT),
Matt Porterf3302a52006-07-31 12:49:34 +0200852 { } /* end */
853};
854
Takashi Iwai16970552007-12-18 18:05:52 +0100855static struct snd_kcontrol_new stac_dmux_mixer = {
856 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
857 .name = "Digital Input Source",
858 /* count set later */
859 .info = stac92xx_dmux_enum_info,
860 .get = stac92xx_dmux_enum_get,
861 .put = stac92xx_dmux_enum_put,
862};
863
Takashi Iwai2134ea42008-01-10 16:53:55 +0100864static const char *slave_vols[] = {
865 "Front Playback Volume",
866 "Surround Playback Volume",
867 "Center Playback Volume",
868 "LFE Playback Volume",
869 "Side Playback Volume",
870 "Headphone Playback Volume",
871 "Headphone Playback Volume",
872 "Speaker Playback Volume",
873 "External Speaker Playback Volume",
874 "Speaker2 Playback Volume",
875 NULL
876};
877
878static const char *slave_sws[] = {
879 "Front Playback Switch",
880 "Surround Playback Switch",
881 "Center Playback Switch",
882 "LFE Playback Switch",
883 "Side Playback Switch",
884 "Headphone Playback Switch",
885 "Headphone Playback Switch",
886 "Speaker Playback Switch",
887 "External Speaker Playback Switch",
888 "Speaker2 Playback Switch",
Takashi Iwaiedb54a52008-01-29 12:47:02 +0100889 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100890 NULL
891};
892
Matt2f2f4252005-04-13 14:45:30 +0200893static int stac92xx_build_controls(struct hda_codec *codec)
894{
895 struct sigmatel_spec *spec = codec->spec;
896 int err;
Mattc7d4b2f2005-06-27 14:59:41 +0200897 int i;
Matt2f2f4252005-04-13 14:45:30 +0200898
899 err = snd_hda_add_new_ctls(codec, spec->mixer);
900 if (err < 0)
901 return err;
Mattc7d4b2f2005-06-27 14:59:41 +0200902
903 for (i = 0; i < spec->num_mixers; i++) {
904 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
905 if (err < 0)
906 return err;
907 }
Takashi Iwai16970552007-12-18 18:05:52 +0100908 if (spec->num_dmuxes > 0) {
909 stac_dmux_mixer.count = spec->num_dmuxes;
910 err = snd_ctl_add(codec->bus->card,
911 snd_ctl_new1(&stac_dmux_mixer, codec));
912 if (err < 0)
913 return err;
914 }
Mattc7d4b2f2005-06-27 14:59:41 +0200915
Mattdabbed62005-06-14 10:19:34 +0200916 if (spec->multiout.dig_out_nid) {
917 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
918 if (err < 0)
919 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100920 err = snd_hda_create_spdif_share_sw(codec,
921 &spec->multiout);
922 if (err < 0)
923 return err;
924 spec->multiout.share_spdif = 1;
Mattdabbed62005-06-14 10:19:34 +0200925 }
926 if (spec->dig_in_nid) {
927 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
928 if (err < 0)
929 return err;
930 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100931
932 /* if we have no master control, let's create it */
933 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100934 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100935 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100936 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100937 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100938 vmaster_tlv, slave_vols);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100939 if (err < 0)
940 return err;
941 }
942 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
943 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
944 NULL, slave_sws);
945 if (err < 0)
946 return err;
947 }
948
Mattdabbed62005-06-14 10:19:34 +0200949 return 0;
Matt2f2f4252005-04-13 14:45:30 +0200950}
951
Matt Porter403d1942005-11-29 15:00:51 +0100952static unsigned int ref9200_pin_configs[8] = {
Mattdabbed62005-06-14 10:19:34 +0200953 0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
Matt2f2f4252005-04-13 14:45:30 +0200954 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
955};
956
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200957/*
958 STAC 9200 pin configs for
959 102801A8
960 102801DE
961 102801E8
962*/
963static unsigned int dell9200_d21_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +0200964 0x400001f0, 0x400001f1, 0x02214030, 0x01014010,
965 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200966};
967
968/*
969 STAC 9200 pin configs for
970 102801C0
971 102801C1
972*/
973static unsigned int dell9200_d22_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +0200974 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
975 0x01813020, 0x02a19021, 0x90100140, 0x400001f2,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200976};
977
978/*
979 STAC 9200 pin configs for
980 102801C4 (Dell Dimension E310)
981 102801C5
982 102801C7
983 102801D9
984 102801DA
985 102801E3
986*/
987static unsigned int dell9200_d23_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +0200988 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
989 0x01813020, 0x01a19021, 0x90100140, 0x400001f2,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200990};
991
992
993/*
994 STAC 9200-32 pin configs for
995 102801B5 (Dell Inspiron 630m)
996 102801D8 (Dell Inspiron 640m)
997*/
998static unsigned int dell9200_m21_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +0200999 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310,
1000 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001001};
1002
1003/*
1004 STAC 9200-32 pin configs for
1005 102801C2 (Dell Latitude D620)
1006 102801C8
1007 102801CC (Dell Latitude D820)
1008 102801D4
1009 102801D6
1010*/
1011static unsigned int dell9200_m22_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001012 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310,
1013 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001014};
1015
1016/*
1017 STAC 9200-32 pin configs for
1018 102801CE (Dell XPS M1710)
1019 102801CF (Dell Precision M90)
1020*/
1021static unsigned int dell9200_m23_pin_configs[8] = {
1022 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310,
1023 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc,
1024};
1025
1026/*
1027 STAC 9200-32 pin configs for
1028 102801C9
1029 102801CA
1030 102801CB (Dell Latitude 120L)
1031 102801D3
1032*/
1033static unsigned int dell9200_m24_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001034 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310,
1035 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001036};
1037
1038/*
1039 STAC 9200-32 pin configs for
1040 102801BD (Dell Inspiron E1505n)
1041 102801EE
1042 102801EF
1043*/
1044static unsigned int dell9200_m25_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001045 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
1046 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001047};
1048
1049/*
1050 STAC 9200-32 pin configs for
1051 102801F5 (Dell Inspiron 1501)
1052 102801F6
1053*/
1054static unsigned int dell9200_m26_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001055 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310,
1056 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001057};
1058
1059/*
1060 STAC 9200-32
1061 102801CD (Dell Inspiron E1705/9400)
1062*/
1063static unsigned int dell9200_m27_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001064 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
1065 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001066};
1067
Tobin Davisbf277782008-02-03 20:31:47 +01001068static unsigned int oqo9200_pin_configs[8] = {
1069 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210,
1070 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3,
1071};
1072
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001073
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001074static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
1075 [STAC_REF] = ref9200_pin_configs,
Tobin Davisbf277782008-02-03 20:31:47 +01001076 [STAC_9200_OQO] = oqo9200_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001077 [STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
1078 [STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
1079 [STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
1080 [STAC_9200_DELL_M21] = dell9200_m21_pin_configs,
1081 [STAC_9200_DELL_M22] = dell9200_m22_pin_configs,
1082 [STAC_9200_DELL_M23] = dell9200_m23_pin_configs,
1083 [STAC_9200_DELL_M24] = dell9200_m24_pin_configs,
1084 [STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
1085 [STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
1086 [STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
Matt Porter403d1942005-11-29 15:00:51 +01001087};
1088
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001089static const char *stac9200_models[STAC_9200_MODELS] = {
1090 [STAC_REF] = "ref",
Tobin Davisbf277782008-02-03 20:31:47 +01001091 [STAC_9200_OQO] = "oqo",
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001092 [STAC_9200_DELL_D21] = "dell-d21",
1093 [STAC_9200_DELL_D22] = "dell-d22",
1094 [STAC_9200_DELL_D23] = "dell-d23",
1095 [STAC_9200_DELL_M21] = "dell-m21",
1096 [STAC_9200_DELL_M22] = "dell-m22",
1097 [STAC_9200_DELL_M23] = "dell-m23",
1098 [STAC_9200_DELL_M24] = "dell-m24",
1099 [STAC_9200_DELL_M25] = "dell-m25",
1100 [STAC_9200_DELL_M26] = "dell-m26",
1101 [STAC_9200_DELL_M27] = "dell-m27",
Takashi Iwai1194b5b2007-10-10 10:04:26 +02001102 [STAC_9200_GATEWAY] = "gateway",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001103};
1104
1105static struct snd_pci_quirk stac9200_cfg_tbl[] = {
1106 /* SigmaTel reference board */
1107 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1108 "DFI LanParty", STAC_REF),
Matt Portere7377072006-11-06 11:20:38 +01001109 /* Dell laptops have BIOS problem */
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001110 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8,
1111 "unknown Dell", STAC_9200_DELL_D21),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001112 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001113 "Dell Inspiron 630m", STAC_9200_DELL_M21),
1114 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bd,
1115 "Dell Inspiron E1505n", STAC_9200_DELL_M25),
1116 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c0,
1117 "unknown Dell", STAC_9200_DELL_D22),
1118 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c1,
1119 "unknown Dell", STAC_9200_DELL_D22),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001120 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001121 "Dell Latitude D620", STAC_9200_DELL_M22),
1122 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c5,
1123 "unknown Dell", STAC_9200_DELL_D23),
1124 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c7,
1125 "unknown Dell", STAC_9200_DELL_D23),
1126 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c8,
1127 "unknown Dell", STAC_9200_DELL_M22),
1128 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c9,
1129 "unknown Dell", STAC_9200_DELL_M24),
1130 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ca,
1131 "unknown Dell", STAC_9200_DELL_M24),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001132 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001133 "Dell Latitude 120L", STAC_9200_DELL_M24),
Cory T. Tusar877b8662007-01-30 17:30:55 +01001134 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001135 "Dell Latitude D820", STAC_9200_DELL_M22),
Mikael Nilsson46f02ca2007-02-13 12:46:16 +01001136 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cd,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001137 "Dell Inspiron E1705/9400", STAC_9200_DELL_M27),
Mikael Nilsson46f02ca2007-02-13 12:46:16 +01001138 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ce,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001139 "Dell XPS M1710", STAC_9200_DELL_M23),
Takashi Iwaif0f96742007-02-14 00:59:17 +01001140 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001141 "Dell Precision M90", STAC_9200_DELL_M23),
1142 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d3,
1143 "unknown Dell", STAC_9200_DELL_M22),
1144 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d4,
1145 "unknown Dell", STAC_9200_DELL_M22),
Daniel T Chen8286c532007-05-15 11:46:23 +02001146 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001147 "unknown Dell", STAC_9200_DELL_M22),
Tobin Davis49c605d2007-05-17 09:38:24 +02001148 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001149 "Dell Inspiron 640m", STAC_9200_DELL_M21),
1150 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d9,
1151 "unknown Dell", STAC_9200_DELL_D23),
1152 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01da,
1153 "unknown Dell", STAC_9200_DELL_D23),
1154 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01de,
1155 "unknown Dell", STAC_9200_DELL_D21),
1156 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e3,
1157 "unknown Dell", STAC_9200_DELL_D23),
1158 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e8,
1159 "unknown Dell", STAC_9200_DELL_D21),
1160 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ee,
1161 "unknown Dell", STAC_9200_DELL_M25),
1162 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ef,
1163 "unknown Dell", STAC_9200_DELL_M25),
Tobin Davis49c605d2007-05-17 09:38:24 +02001164 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001165 "Dell Inspiron 1501", STAC_9200_DELL_M26),
1166 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6,
1167 "unknown Dell", STAC_9200_DELL_M26),
Tobin Davis49c605d2007-05-17 09:38:24 +02001168 /* Panasonic */
1169 SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF),
Takashi Iwai1194b5b2007-10-10 10:04:26 +02001170 /* Gateway machines needs EAPD to be set on resume */
1171 SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_GATEWAY),
1172 SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*",
1173 STAC_9200_GATEWAY),
1174 SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707",
1175 STAC_9200_GATEWAY),
Tobin Davisbf277782008-02-03 20:31:47 +01001176 /* OQO Mobile */
1177 SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO),
Matt Porter403d1942005-11-29 15:00:51 +01001178 {} /* terminator */
1179};
1180
Tobin Davis8e21c342007-01-08 11:04:17 +01001181static unsigned int ref925x_pin_configs[8] = {
1182 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
Matthew Ranostay09a99952008-01-24 11:49:21 +01001183 0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
Tobin Davis8e21c342007-01-08 11:04:17 +01001184};
1185
1186static unsigned int stac925x_MA6_pin_configs[8] = {
1187 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
1188 0x90a70320, 0x90100211, 0x400003f1, 0x9033032e,
1189};
1190
Tobin Davis2c11f952007-05-17 09:36:34 +02001191static unsigned int stac925x_PA6_pin_configs[8] = {
1192 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
1193 0x50a103f0, 0x90100211, 0x400003f1, 0x9033032e,
1194};
1195
Tobin Davis8e21c342007-01-08 11:04:17 +01001196static unsigned int stac925xM2_2_pin_configs[8] = {
Steve Longerbeam7353e142007-05-29 14:36:17 +02001197 0x40c003f3, 0x424503f2, 0x04180011, 0x02a19020,
1198 0x50a103f0, 0x90100212, 0x400003f1, 0x9033032e,
Tobin Davis8e21c342007-01-08 11:04:17 +01001199};
1200
1201static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
1202 [STAC_REF] = ref925x_pin_configs,
1203 [STAC_M2_2] = stac925xM2_2_pin_configs,
1204 [STAC_MA6] = stac925x_MA6_pin_configs,
Tobin Davis2c11f952007-05-17 09:36:34 +02001205 [STAC_PA6] = stac925x_PA6_pin_configs,
Tobin Davis8e21c342007-01-08 11:04:17 +01001206};
1207
1208static const char *stac925x_models[STAC_925x_MODELS] = {
1209 [STAC_REF] = "ref",
1210 [STAC_M2_2] = "m2-2",
1211 [STAC_MA6] = "m6",
Tobin Davis2c11f952007-05-17 09:36:34 +02001212 [STAC_PA6] = "pa6",
Tobin Davis8e21c342007-01-08 11:04:17 +01001213};
1214
1215static struct snd_pci_quirk stac925x_cfg_tbl[] = {
1216 /* SigmaTel reference board */
1217 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
Tobin Davis2c11f952007-05-17 09:36:34 +02001218 SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
Tobin Davis8e21c342007-01-08 11:04:17 +01001219 SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_REF),
1220 SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_REF),
1221 SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_MA6),
Tobin Davis2c11f952007-05-17 09:36:34 +02001222 SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_PA6),
Tobin Davis8e21c342007-01-08 11:04:17 +01001223 SND_PCI_QUIRK(0x1002, 0x437b, "Gateway MX6453", STAC_M2_2),
1224 {} /* terminator */
1225};
1226
Matthew Ranostaya7662642008-02-21 07:51:14 +01001227static unsigned int ref92hd73xx_pin_configs[13] = {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01001228 0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
1229 0x0181302e, 0x01014010, 0x01014020, 0x01014030,
1230 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
Matthew Ranostaya7662642008-02-21 07:51:14 +01001231 0x01452050,
1232};
1233
1234static unsigned int dell_m6_pin_configs[13] = {
1235 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110,
1236 0x03a11020, 0x03011050, 0x4f0000f0, 0x4f0000f0,
1237 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
1238 0x4f0000f0,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01001239};
1240
1241static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
Matthew Ranostaya7662642008-02-21 07:51:14 +01001242 [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
1243 [STAC_DELL_M6] = dell_m6_pin_configs,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01001244};
1245
1246static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
1247 [STAC_92HD73XX_REF] = "ref",
Matthew Ranostaya7662642008-02-21 07:51:14 +01001248 [STAC_DELL_M6] = "dell-m6",
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01001249};
1250
1251static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
1252 /* SigmaTel reference board */
1253 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
Matthew Ranostaya7662642008-02-21 07:51:14 +01001254 "DFI LanParty", STAC_92HD73XX_REF),
1255 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
1256 "unknown Dell", STAC_DELL_M6),
1257 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
1258 "unknown Dell", STAC_DELL_M6),
1259 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256,
1260 "unknown Dell", STAC_DELL_M6),
1261 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257,
1262 "unknown Dell", STAC_DELL_M6),
1263 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e,
1264 "unknown Dell", STAC_DELL_M6),
1265 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f,
1266 "unknown Dell", STAC_DELL_M6),
1267 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271,
1268 "unknown Dell", STAC_DELL_M6),
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01001269 {} /* terminator */
1270};
1271
Matthew Ranostaye035b842007-11-06 11:53:55 +01001272static unsigned int ref92hd71bxx_pin_configs[10] = {
1273 0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
Matthew Ranostayb22b4822008-01-22 12:32:30 +01001274 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0,
Matthew Ranostaye035b842007-11-06 11:53:55 +01001275 0x90a000f0, 0x01452050,
1276};
1277
Matthew Ranostaya7662642008-02-21 07:51:14 +01001278static unsigned int dell_m4_1_pin_configs[13] = {
1279 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
1280 0x23a1902e, 0x23014250, 0x40f000f0, 0x4f0000f0,
1281 0x40f000f0, 0x4f0000f0,
1282};
1283
1284static unsigned int dell_m4_2_pin_configs[13] = {
1285 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
1286 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
1287 0x40f000f0, 0x044413b0,
1288};
1289
Matthew Ranostaye035b842007-11-06 11:53:55 +01001290static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
1291 [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
Matthew Ranostaya7662642008-02-21 07:51:14 +01001292 [STAC_DELL_M4_1] = dell_m4_1_pin_configs,
1293 [STAC_DELL_M4_2] = dell_m4_2_pin_configs,
Matthew Ranostaye035b842007-11-06 11:53:55 +01001294};
1295
1296static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
1297 [STAC_92HD71BXX_REF] = "ref",
Matthew Ranostaya7662642008-02-21 07:51:14 +01001298 [STAC_DELL_M4_1] = "dell-m4-1",
1299 [STAC_DELL_M4_2] = "dell-m4-2",
Matthew Ranostaye035b842007-11-06 11:53:55 +01001300};
1301
1302static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
1303 /* SigmaTel reference board */
1304 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1305 "DFI LanParty", STAC_92HD71BXX_REF),
Matthew Ranostaya7662642008-02-21 07:51:14 +01001306 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
1307 "unknown Dell", STAC_DELL_M4_1),
1308 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
1309 "unknown Dell", STAC_DELL_M4_1),
1310 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250,
1311 "unknown Dell", STAC_DELL_M4_1),
1312 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f,
1313 "unknown Dell", STAC_DELL_M4_1),
1314 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d,
1315 "unknown Dell", STAC_DELL_M4_1),
1316 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251,
1317 "unknown Dell", STAC_DELL_M4_1),
1318 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277,
1319 "unknown Dell", STAC_DELL_M4_1),
1320 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263,
1321 "unknown Dell", STAC_DELL_M4_2),
1322 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265,
1323 "unknown Dell", STAC_DELL_M4_2),
1324 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262,
1325 "unknown Dell", STAC_DELL_M4_2),
1326 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264,
1327 "unknown Dell", STAC_DELL_M4_2),
Matthew Ranostaye035b842007-11-06 11:53:55 +01001328 {} /* terminator */
1329};
1330
Matt Porter403d1942005-11-29 15:00:51 +01001331static unsigned int ref922x_pin_configs[10] = {
1332 0x01014010, 0x01016011, 0x01012012, 0x0221401f,
1333 0x01813122, 0x01011014, 0x01441030, 0x01c41030,
Matt2f2f4252005-04-13 14:45:30 +02001334 0x40000100, 0x40000100,
1335};
1336
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001337/*
1338 STAC 922X pin configs for
1339 102801A7
1340 102801AB
1341 102801A9
1342 102801D1
1343 102801D2
1344*/
1345static unsigned int dell_922x_d81_pin_configs[10] = {
1346 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
1347 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1,
1348 0x01813122, 0x400001f2,
1349};
1350
1351/*
1352 STAC 922X pin configs for
1353 102801AC
1354 102801D0
1355*/
1356static unsigned int dell_922x_d82_pin_configs[10] = {
1357 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
1358 0x02a19020, 0x01117011, 0x01451140, 0x400001f0,
1359 0x01813122, 0x400001f1,
1360};
1361
1362/*
1363 STAC 922X pin configs for
1364 102801BF
1365*/
1366static unsigned int dell_922x_m81_pin_configs[10] = {
1367 0x0321101f, 0x01112024, 0x01111222, 0x91174220,
1368 0x03a11050, 0x01116221, 0x90a70330, 0x01452340,
1369 0x40C003f1, 0x405003f0,
1370};
1371
1372/*
1373 STAC 9221 A1 pin configs for
1374 102801D7 (Dell XPS M1210)
1375*/
1376static unsigned int dell_922x_m82_pin_configs[10] = {
Jiang Zhe7f9310c2007-11-12 12:43:37 +01001377 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310,
1378 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001379 0x508003f3, 0x405003f4,
1380};
1381
Matt Porter403d1942005-11-29 15:00:51 +01001382static unsigned int d945gtp3_pin_configs[10] = {
Matt Porter869264c2006-01-25 19:20:50 +01001383 0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
Matt Porter403d1942005-11-29 15:00:51 +01001384 0x40000100, 0x40000100, 0x40000100, 0x40000100,
1385 0x02a19120, 0x40000100,
1386};
1387
1388static unsigned int d945gtp5_pin_configs[10] = {
Matt Porter869264c2006-01-25 19:20:50 +01001389 0x0221401f, 0x01011012, 0x01813024, 0x01014010,
1390 0x01a19021, 0x01016011, 0x01452130, 0x40000100,
Matt Porter403d1942005-11-29 15:00:51 +01001391 0x02a19320, 0x40000100,
1392};
1393
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001394static unsigned int intel_mac_v1_pin_configs[10] = {
1395 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
1396 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
Takashi Iwai3fc24d82007-02-16 13:27:18 +01001397 0x400000fc, 0x400000fb,
1398};
1399
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001400static unsigned int intel_mac_v2_pin_configs[10] = {
1401 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
1402 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
Sylvain FORETf16928f2007-04-27 14:22:36 +02001403 0x400000fc, 0x400000fb,
1404};
1405
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001406static unsigned int intel_mac_v3_pin_configs[10] = {
1407 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
1408 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
1409 0x400000fc, 0x400000fb,
1410};
1411
1412static unsigned int intel_mac_v4_pin_configs[10] = {
1413 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
1414 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
1415 0x400000fc, 0x400000fb,
1416};
1417
1418static unsigned int intel_mac_v5_pin_configs[10] = {
1419 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
1420 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
1421 0x400000fc, 0x400000fb,
Takashi Iwai0dae0f82007-05-21 12:41:29 +02001422};
1423
Takashi Iwai76c08822007-06-19 12:17:42 +02001424
Takashi Iwai19039bd2006-06-28 15:52:16 +02001425static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001426 [STAC_D945_REF] = ref922x_pin_configs,
Takashi Iwai19039bd2006-06-28 15:52:16 +02001427 [STAC_D945GTP3] = d945gtp3_pin_configs,
1428 [STAC_D945GTP5] = d945gtp5_pin_configs,
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001429 [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
1430 [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
1431 [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
1432 [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
1433 [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001434 /* for backward compatibility */
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001435 [STAC_MACMINI] = intel_mac_v3_pin_configs,
1436 [STAC_MACBOOK] = intel_mac_v5_pin_configs,
1437 [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
1438 [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
1439 [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
1440 [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001441 [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,
1442 [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,
1443 [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs,
1444 [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs,
Matt Porter403d1942005-11-29 15:00:51 +01001445};
1446
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001447static const char *stac922x_models[STAC_922X_MODELS] = {
1448 [STAC_D945_REF] = "ref",
1449 [STAC_D945GTP5] = "5stack",
1450 [STAC_D945GTP3] = "3stack",
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001451 [STAC_INTEL_MAC_V1] = "intel-mac-v1",
1452 [STAC_INTEL_MAC_V2] = "intel-mac-v2",
1453 [STAC_INTEL_MAC_V3] = "intel-mac-v3",
1454 [STAC_INTEL_MAC_V4] = "intel-mac-v4",
1455 [STAC_INTEL_MAC_V5] = "intel-mac-v5",
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001456 /* for backward compatibility */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001457 [STAC_MACMINI] = "macmini",
Takashi Iwai3fc24d82007-02-16 13:27:18 +01001458 [STAC_MACBOOK] = "macbook",
Nicolas Boichat6f0778d2007-03-15 12:38:15 +01001459 [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
1460 [STAC_MACBOOK_PRO_V2] = "macbook-pro",
Sylvain FORETf16928f2007-04-27 14:22:36 +02001461 [STAC_IMAC_INTEL] = "imac-intel",
Takashi Iwai0dae0f82007-05-21 12:41:29 +02001462 [STAC_IMAC_INTEL_20] = "imac-intel-20",
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001463 [STAC_922X_DELL_D81] = "dell-d81",
1464 [STAC_922X_DELL_D82] = "dell-d82",
1465 [STAC_922X_DELL_M81] = "dell-m81",
1466 [STAC_922X_DELL_M82] = "dell-m82",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001467};
1468
1469static struct snd_pci_quirk stac922x_cfg_tbl[] = {
1470 /* SigmaTel reference board */
1471 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1472 "DFI LanParty", STAC_D945_REF),
1473 /* Intel 945G based systems */
1474 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101,
1475 "Intel D945G", STAC_D945GTP3),
1476 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202,
1477 "Intel D945G", STAC_D945GTP3),
1478 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606,
1479 "Intel D945G", STAC_D945GTP3),
1480 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601,
1481 "Intel D945G", STAC_D945GTP3),
1482 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111,
1483 "Intel D945G", STAC_D945GTP3),
1484 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115,
1485 "Intel D945G", STAC_D945GTP3),
1486 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116,
1487 "Intel D945G", STAC_D945GTP3),
1488 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117,
1489 "Intel D945G", STAC_D945GTP3),
1490 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118,
1491 "Intel D945G", STAC_D945GTP3),
1492 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119,
1493 "Intel D945G", STAC_D945GTP3),
1494 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826,
1495 "Intel D945G", STAC_D945GTP3),
1496 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049,
1497 "Intel D945G", STAC_D945GTP3),
1498 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055,
1499 "Intel D945G", STAC_D945GTP3),
1500 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048,
1501 "Intel D945G", STAC_D945GTP3),
1502 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110,
1503 "Intel D945G", STAC_D945GTP3),
1504 /* Intel D945G 5-stack systems */
1505 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404,
1506 "Intel D945G", STAC_D945GTP5),
1507 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303,
1508 "Intel D945G", STAC_D945GTP5),
1509 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013,
1510 "Intel D945G", STAC_D945GTP5),
1511 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417,
1512 "Intel D945G", STAC_D945GTP5),
1513 /* Intel 945P based systems */
1514 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b,
1515 "Intel D945P", STAC_D945GTP3),
1516 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112,
1517 "Intel D945P", STAC_D945GTP3),
1518 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d,
1519 "Intel D945P", STAC_D945GTP3),
1520 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909,
1521 "Intel D945P", STAC_D945GTP3),
1522 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505,
1523 "Intel D945P", STAC_D945GTP3),
1524 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
1525 "Intel D945P", STAC_D945GTP5),
1526 /* other systems */
1527 /* Apple Mac Mini (early 2006) */
1528 SND_PCI_QUIRK(0x8384, 0x7680,
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001529 "Mac Mini", STAC_INTEL_MAC_V3),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001530 /* Dell systems */
1531 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
1532 "unknown Dell", STAC_922X_DELL_D81),
1533 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a9,
1534 "unknown Dell", STAC_922X_DELL_D81),
1535 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ab,
1536 "unknown Dell", STAC_922X_DELL_D81),
1537 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ac,
1538 "unknown Dell", STAC_922X_DELL_D82),
1539 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bf,
1540 "unknown Dell", STAC_922X_DELL_M81),
1541 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d0,
1542 "unknown Dell", STAC_922X_DELL_D82),
1543 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d1,
1544 "unknown Dell", STAC_922X_DELL_D81),
1545 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d2,
1546 "unknown Dell", STAC_922X_DELL_D81),
1547 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
1548 "Dell XPS M1210", STAC_922X_DELL_M82),
Matt Porter403d1942005-11-29 15:00:51 +01001549 {} /* terminator */
1550};
1551
Matt Porter3cc08dc2006-01-23 15:27:49 +01001552static unsigned int ref927x_pin_configs[14] = {
Tobin Davis93ed1502006-09-01 21:03:12 +02001553 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
1554 0x01a19040, 0x01011012, 0x01016011, 0x0101201f,
1555 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070,
1556 0x01c42190, 0x40000100,
Matt Porter3cc08dc2006-01-23 15:27:49 +01001557};
1558
Tobin Davis93ed1502006-09-01 21:03:12 +02001559static unsigned int d965_3st_pin_configs[14] = {
Tobin Davis81d3dbd2006-08-22 19:44:45 +02001560 0x0221401f, 0x02a19120, 0x40000100, 0x01014011,
1561 0x01a19021, 0x01813024, 0x40000100, 0x40000100,
1562 0x40000100, 0x40000100, 0x40000100, 0x40000100,
1563 0x40000100, 0x40000100
1564};
1565
Tobin Davis93ed1502006-09-01 21:03:12 +02001566static unsigned int d965_5st_pin_configs[14] = {
1567 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
1568 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
1569 0x40000100, 0x40000100, 0x40000100, 0x01442070,
1570 0x40000100, 0x40000100
1571};
1572
Tobin Davis4ff076e2007-08-07 11:48:12 +02001573static unsigned int dell_3st_pin_configs[14] = {
1574 0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
1575 0x01111212, 0x01116211, 0x01813050, 0x01112214,
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001576 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb,
Tobin Davis4ff076e2007-08-07 11:48:12 +02001577 0x40c003fc, 0x40000100
1578};
1579
Tobin Davis93ed1502006-09-01 21:03:12 +02001580static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001581 [STAC_D965_REF] = ref927x_pin_configs,
1582 [STAC_D965_3ST] = d965_3st_pin_configs,
1583 [STAC_D965_5ST] = d965_5st_pin_configs,
1584 [STAC_DELL_3ST] = dell_3st_pin_configs,
1585 [STAC_DELL_BIOS] = NULL,
Matt Porter3cc08dc2006-01-23 15:27:49 +01001586};
1587
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001588static const char *stac927x_models[STAC_927X_MODELS] = {
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001589 [STAC_D965_REF] = "ref",
1590 [STAC_D965_3ST] = "3stack",
1591 [STAC_D965_5ST] = "5stack",
1592 [STAC_DELL_3ST] = "dell-3stack",
1593 [STAC_DELL_BIOS] = "dell-bios",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001594};
1595
1596static struct snd_pci_quirk stac927x_cfg_tbl[] = {
1597 /* SigmaTel reference board */
1598 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1599 "DFI LanParty", STAC_D965_REF),
Tobin Davis81d3dbd2006-08-22 19:44:45 +02001600 /* Intel 946 based systems */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001601 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST),
1602 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST),
Tobin Davis93ed1502006-09-01 21:03:12 +02001603 /* 965 based 3 stack systems */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001604 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST),
1605 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST),
1606 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST),
1607 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST),
1608 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST),
1609 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST),
1610 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST),
1611 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST),
1612 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST),
1613 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST),
1614 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST),
1615 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST),
1616 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST),
1617 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
1618 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
1619 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
Tobin Davis4ff076e2007-08-07 11:48:12 +02001620 /* Dell 3 stack systems */
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001621 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_3ST),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001622 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
Tobin Davis4ff076e2007-08-07 11:48:12 +02001623 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST),
1624 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST),
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001625 /* Dell 3 stack systems with verb table in BIOS */
Matthew Ranostay2f32d902008-01-10 13:06:26 +01001626 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
1627 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001628 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell ", STAC_DELL_BIOS),
1629 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS),
1630 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
1631 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
1632 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
1633 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
Tobin Davis93ed1502006-09-01 21:03:12 +02001634 /* 965 based 5 stack systems */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001635 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
1636 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
1637 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
1638 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST),
1639 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST),
1640 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST),
1641 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST),
1642 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST),
1643 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST),
Matt Porter3cc08dc2006-01-23 15:27:49 +01001644 {} /* terminator */
1645};
1646
Matt Porterf3302a52006-07-31 12:49:34 +02001647static unsigned int ref9205_pin_configs[12] = {
1648 0x40000100, 0x40000100, 0x01016011, 0x01014010,
Matthew Ranostay09a99952008-01-24 11:49:21 +01001649 0x01813122, 0x01a19021, 0x01019020, 0x40000100,
Matt Porter8b657272006-10-26 17:12:59 +02001650 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
Matt Porterf3302a52006-07-31 12:49:34 +02001651};
1652
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001653/*
1654 STAC 9205 pin configs for
1655 102801F1
1656 102801F2
1657 102801FC
1658 102801FD
1659 10280204
1660 1028021F
Matthew Ranostay3fa2ef72008-01-11 11:39:06 +01001661 10280228 (Dell Vostro 1500)
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001662*/
1663static unsigned int dell_9205_m42_pin_configs[12] = {
1664 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
1665 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9,
1666 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE,
1667};
1668
1669/*
1670 STAC 9205 pin configs for
1671 102801F9
1672 102801FA
1673 102801FE
1674 102801FF (Dell Precision M4300)
1675 10280206
1676 10280200
1677 10280201
1678*/
1679static unsigned int dell_9205_m43_pin_configs[12] = {
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001680 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310,
1681 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9,
1682 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8,
1683};
1684
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001685static unsigned int dell_9205_m44_pin_configs[12] = {
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001686 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310,
1687 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9,
1688 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe,
1689};
1690
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001691static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001692 [STAC_9205_REF] = ref9205_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001693 [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
1694 [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
1695 [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
Matt Porterf3302a52006-07-31 12:49:34 +02001696};
1697
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001698static const char *stac9205_models[STAC_9205_MODELS] = {
1699 [STAC_9205_REF] = "ref",
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001700 [STAC_9205_DELL_M42] = "dell-m42",
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001701 [STAC_9205_DELL_M43] = "dell-m43",
1702 [STAC_9205_DELL_M44] = "dell-m44",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001703};
1704
1705static struct snd_pci_quirk stac9205_cfg_tbl[] = {
1706 /* SigmaTel reference board */
1707 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1708 "DFI LanParty", STAC_9205_REF),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001709 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
1710 "unknown Dell", STAC_9205_DELL_M42),
1711 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
1712 "unknown Dell", STAC_9205_DELL_M42),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001713 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
Matthew Ranostayb44ef2f2007-09-18 00:52:38 +02001714 "Dell Precision", STAC_9205_DELL_M43),
1715 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
1716 "Dell Precision", STAC_9205_DELL_M43),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001717 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
1718 "Dell Precision", STAC_9205_DELL_M43),
Matthew Ranostaye45e4592007-09-10 23:09:42 +02001719 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
1720 "Dell Precision", STAC_9205_DELL_M43),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001721 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
1722 "Dell Precision", STAC_9205_DELL_M43),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001723 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
1724 "unknown Dell", STAC_9205_DELL_M42),
1725 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
1726 "unknown Dell", STAC_9205_DELL_M42),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001727 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe,
1728 "Dell Precision", STAC_9205_DELL_M43),
1729 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001730 "Dell Precision M4300", STAC_9205_DELL_M43),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001731 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
1732 "Dell Precision", STAC_9205_DELL_M43),
1733 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
1734 "Dell Inspiron", STAC_9205_DELL_M44),
1735 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
1736 "Dell Inspiron", STAC_9205_DELL_M44),
1737 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
1738 "Dell Inspiron", STAC_9205_DELL_M44),
1739 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
1740 "Dell Inspiron", STAC_9205_DELL_M44),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001741 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
1742 "unknown Dell", STAC_9205_DELL_M42),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001743 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
1744 "Dell Inspiron", STAC_9205_DELL_M44),
Matthew Ranostay3fa2ef72008-01-11 11:39:06 +01001745 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
1746 "Dell Vostro 1500", STAC_9205_DELL_M42),
Matt Porterf3302a52006-07-31 12:49:34 +02001747 {} /* terminator */
1748};
1749
Richard Fish11b44bb2006-08-23 18:31:34 +02001750static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
1751{
1752 int i;
1753 struct sigmatel_spec *spec = codec->spec;
1754
1755 if (! spec->bios_pin_configs) {
1756 spec->bios_pin_configs = kcalloc(spec->num_pins,
1757 sizeof(*spec->bios_pin_configs), GFP_KERNEL);
1758 if (! spec->bios_pin_configs)
1759 return -ENOMEM;
1760 }
1761
1762 for (i = 0; i < spec->num_pins; i++) {
1763 hda_nid_t nid = spec->pin_nids[i];
1764 unsigned int pin_cfg;
1765
1766 pin_cfg = snd_hda_codec_read(codec, nid, 0,
1767 AC_VERB_GET_CONFIG_DEFAULT, 0x00);
1768 snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
1769 nid, pin_cfg);
1770 spec->bios_pin_configs[i] = pin_cfg;
1771 }
1772
1773 return 0;
1774}
1775
Matthew Ranostay87d48362007-07-17 11:52:24 +02001776static void stac92xx_set_config_reg(struct hda_codec *codec,
1777 hda_nid_t pin_nid, unsigned int pin_config)
1778{
1779 int i;
1780 snd_hda_codec_write(codec, pin_nid, 0,
1781 AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
1782 pin_config & 0x000000ff);
1783 snd_hda_codec_write(codec, pin_nid, 0,
1784 AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
1785 (pin_config & 0x0000ff00) >> 8);
1786 snd_hda_codec_write(codec, pin_nid, 0,
1787 AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
1788 (pin_config & 0x00ff0000) >> 16);
1789 snd_hda_codec_write(codec, pin_nid, 0,
1790 AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
1791 pin_config >> 24);
1792 i = snd_hda_codec_read(codec, pin_nid, 0,
1793 AC_VERB_GET_CONFIG_DEFAULT,
1794 0x00);
1795 snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
1796 pin_nid, i);
1797}
1798
Matt2f2f4252005-04-13 14:45:30 +02001799static void stac92xx_set_config_regs(struct hda_codec *codec)
1800{
1801 int i;
1802 struct sigmatel_spec *spec = codec->spec;
Matt2f2f4252005-04-13 14:45:30 +02001803
Matthew Ranostay87d48362007-07-17 11:52:24 +02001804 if (!spec->pin_configs)
1805 return;
Richard Fish11b44bb2006-08-23 18:31:34 +02001806
Matthew Ranostay87d48362007-07-17 11:52:24 +02001807 for (i = 0; i < spec->num_pins; i++)
1808 stac92xx_set_config_reg(codec, spec->pin_nids[i],
1809 spec->pin_configs[i]);
Matt2f2f4252005-04-13 14:45:30 +02001810}
Matt2f2f4252005-04-13 14:45:30 +02001811
Matt2f2f4252005-04-13 14:45:30 +02001812/*
1813 * Analog playback callbacks
1814 */
1815static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
1816 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001817 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001818{
1819 struct sigmatel_spec *spec = codec->spec;
Takashi Iwai9a081602008-02-12 18:37:26 +01001820 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1821 hinfo);
Matt2f2f4252005-04-13 14:45:30 +02001822}
1823
1824static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
1825 struct hda_codec *codec,
1826 unsigned int stream_tag,
1827 unsigned int format,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001828 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001829{
1830 struct sigmatel_spec *spec = codec->spec;
Matt Porter403d1942005-11-29 15:00:51 +01001831 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);
Matt2f2f4252005-04-13 14:45:30 +02001832}
1833
1834static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1835 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001836 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001837{
1838 struct sigmatel_spec *spec = codec->spec;
1839 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
1840}
1841
1842/*
Mattdabbed62005-06-14 10:19:34 +02001843 * Digital playback callbacks
1844 */
1845static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1846 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001847 struct snd_pcm_substream *substream)
Mattdabbed62005-06-14 10:19:34 +02001848{
1849 struct sigmatel_spec *spec = codec->spec;
1850 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1851}
1852
1853static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1854 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001855 struct snd_pcm_substream *substream)
Mattdabbed62005-06-14 10:19:34 +02001856{
1857 struct sigmatel_spec *spec = codec->spec;
1858 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1859}
1860
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001861static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
1862 struct hda_codec *codec,
1863 unsigned int stream_tag,
1864 unsigned int format,
1865 struct snd_pcm_substream *substream)
1866{
1867 struct sigmatel_spec *spec = codec->spec;
1868 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1869 stream_tag, format, substream);
1870}
1871
Mattdabbed62005-06-14 10:19:34 +02001872
1873/*
Matt2f2f4252005-04-13 14:45:30 +02001874 * Analog capture callbacks
1875 */
1876static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1877 struct hda_codec *codec,
1878 unsigned int stream_tag,
1879 unsigned int format,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001880 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001881{
1882 struct sigmatel_spec *spec = codec->spec;
1883
1884 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1885 stream_tag, 0, format);
1886 return 0;
1887}
1888
1889static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1890 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001891 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001892{
1893 struct sigmatel_spec *spec = codec->spec;
1894
1895 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
1896 return 0;
1897}
1898
Mattdabbed62005-06-14 10:19:34 +02001899static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
1900 .substreams = 1,
1901 .channels_min = 2,
1902 .channels_max = 2,
1903 /* NID is set in stac92xx_build_pcms */
1904 .ops = {
1905 .open = stac92xx_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001906 .close = stac92xx_dig_playback_pcm_close,
1907 .prepare = stac92xx_dig_playback_pcm_prepare
Mattdabbed62005-06-14 10:19:34 +02001908 },
1909};
1910
1911static struct hda_pcm_stream stac92xx_pcm_digital_capture = {
1912 .substreams = 1,
1913 .channels_min = 2,
1914 .channels_max = 2,
1915 /* NID is set in stac92xx_build_pcms */
1916};
1917
Matt2f2f4252005-04-13 14:45:30 +02001918static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
1919 .substreams = 1,
1920 .channels_min = 2,
Mattc7d4b2f2005-06-27 14:59:41 +02001921 .channels_max = 8,
Matt2f2f4252005-04-13 14:45:30 +02001922 .nid = 0x02, /* NID to query formats and rates */
1923 .ops = {
1924 .open = stac92xx_playback_pcm_open,
1925 .prepare = stac92xx_playback_pcm_prepare,
1926 .cleanup = stac92xx_playback_pcm_cleanup
1927 },
1928};
1929
Matt Porter3cc08dc2006-01-23 15:27:49 +01001930static struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = {
1931 .substreams = 1,
1932 .channels_min = 2,
1933 .channels_max = 2,
1934 .nid = 0x06, /* NID to query formats and rates */
1935 .ops = {
1936 .open = stac92xx_playback_pcm_open,
1937 .prepare = stac92xx_playback_pcm_prepare,
1938 .cleanup = stac92xx_playback_pcm_cleanup
1939 },
1940};
1941
Matt2f2f4252005-04-13 14:45:30 +02001942static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
Matt2f2f4252005-04-13 14:45:30 +02001943 .channels_min = 2,
1944 .channels_max = 2,
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02001945 /* NID + .substreams is set in stac92xx_build_pcms */
Matt2f2f4252005-04-13 14:45:30 +02001946 .ops = {
1947 .prepare = stac92xx_capture_pcm_prepare,
1948 .cleanup = stac92xx_capture_pcm_cleanup
1949 },
1950};
1951
1952static int stac92xx_build_pcms(struct hda_codec *codec)
1953{
1954 struct sigmatel_spec *spec = codec->spec;
1955 struct hda_pcm *info = spec->pcm_rec;
1956
1957 codec->num_pcms = 1;
1958 codec->pcm_info = info;
1959
Mattc7d4b2f2005-06-27 14:59:41 +02001960 info->name = "STAC92xx Analog";
Matt2f2f4252005-04-13 14:45:30 +02001961 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
Matt2f2f4252005-04-13 14:45:30 +02001962 info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
Matt Porter3cc08dc2006-01-23 15:27:49 +01001963 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02001964 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs;
Matt Porter3cc08dc2006-01-23 15:27:49 +01001965
1966 if (spec->alt_switch) {
1967 codec->num_pcms++;
1968 info++;
1969 info->name = "STAC92xx Analog Alt";
1970 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback;
1971 }
Matt2f2f4252005-04-13 14:45:30 +02001972
Mattdabbed62005-06-14 10:19:34 +02001973 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1974 codec->num_pcms++;
1975 info++;
1976 info->name = "STAC92xx Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001977 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Mattdabbed62005-06-14 10:19:34 +02001978 if (spec->multiout.dig_out_nid) {
1979 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
1980 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
1981 }
1982 if (spec->dig_in_nid) {
1983 info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture;
1984 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
1985 }
1986 }
1987
Matt2f2f4252005-04-13 14:45:30 +02001988 return 0;
1989}
1990
Takashi Iwaic960a032006-03-23 17:06:28 +01001991static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
1992{
1993 unsigned int pincap = snd_hda_param_read(codec, nid,
1994 AC_PAR_PIN_CAP);
1995 pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
1996 if (pincap & AC_PINCAP_VREF_100)
1997 return AC_PINCTL_VREF_100;
1998 if (pincap & AC_PINCAP_VREF_80)
1999 return AC_PINCTL_VREF_80;
2000 if (pincap & AC_PINCAP_VREF_50)
2001 return AC_PINCTL_VREF_50;
2002 if (pincap & AC_PINCAP_VREF_GRD)
2003 return AC_PINCTL_VREF_GRD;
2004 return 0;
2005}
2006
Matt Porter403d1942005-11-29 15:00:51 +01002007static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
2008
2009{
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002010 snd_hda_codec_write_cache(codec, nid, 0,
2011 AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
Matt Porter403d1942005-11-29 15:00:51 +01002012}
2013
Takashi Iwaia5ce8892007-07-23 15:42:26 +02002014#define stac92xx_io_switch_info snd_ctl_boolean_mono_info
Matt Porter403d1942005-11-29 15:00:51 +01002015
2016static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2017{
2018 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2019 struct sigmatel_spec *spec = codec->spec;
2020 int io_idx = kcontrol-> private_value & 0xff;
2021
2022 ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
2023 return 0;
2024}
2025
2026static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2027{
2028 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2029 struct sigmatel_spec *spec = codec->spec;
2030 hda_nid_t nid = kcontrol->private_value >> 8;
2031 int io_idx = kcontrol-> private_value & 0xff;
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002032 unsigned short val = !!ucontrol->value.integer.value[0];
Matt Porter403d1942005-11-29 15:00:51 +01002033
2034 spec->io_switch[io_idx] = val;
2035
2036 if (val)
2037 stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
Takashi Iwaic960a032006-03-23 17:06:28 +01002038 else {
2039 unsigned int pinctl = AC_PINCTL_IN_EN;
2040 if (io_idx) /* set VREF for mic */
2041 pinctl |= stac92xx_get_vref(codec, nid);
2042 stac92xx_auto_set_pinctl(codec, nid, pinctl);
2043 }
Jiang Zhe40c1d302007-11-12 13:05:16 +01002044
2045 /* check the auto-mute again: we need to mute/unmute the speaker
2046 * appropriately according to the pin direction
2047 */
2048 if (spec->hp_detect)
2049 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
2050
Matt Porter403d1942005-11-29 15:00:51 +01002051 return 1;
2052}
2053
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002054#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info
2055
2056static int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol,
2057 struct snd_ctl_elem_value *ucontrol)
2058{
2059 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2060 struct sigmatel_spec *spec = codec->spec;
2061
2062 ucontrol->value.integer.value[0] = spec->clfe_swap;
2063 return 0;
2064}
2065
2066static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
2067 struct snd_ctl_elem_value *ucontrol)
2068{
2069 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2070 struct sigmatel_spec *spec = codec->spec;
2071 hda_nid_t nid = kcontrol->private_value & 0xff;
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002072 unsigned int val = !!ucontrol->value.integer.value[0];
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002073
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002074 if (spec->clfe_swap == val)
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002075 return 0;
2076
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002077 spec->clfe_swap = val;
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002078
2079 snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
2080 spec->clfe_swap ? 0x4 : 0x0);
2081
2082 return 1;
2083}
2084
Matt Porter403d1942005-11-29 15:00:51 +01002085#define STAC_CODEC_IO_SWITCH(xname, xpval) \
2086 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2087 .name = xname, \
2088 .index = 0, \
2089 .info = stac92xx_io_switch_info, \
2090 .get = stac92xx_io_switch_get, \
2091 .put = stac92xx_io_switch_put, \
2092 .private_value = xpval, \
2093 }
2094
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002095#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \
2096 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2097 .name = xname, \
2098 .index = 0, \
2099 .info = stac92xx_clfe_switch_info, \
2100 .get = stac92xx_clfe_switch_get, \
2101 .put = stac92xx_clfe_switch_put, \
2102 .private_value = xpval, \
2103 }
Matt Porter403d1942005-11-29 15:00:51 +01002104
Mattc7d4b2f2005-06-27 14:59:41 +02002105enum {
2106 STAC_CTL_WIDGET_VOL,
2107 STAC_CTL_WIDGET_MUTE,
Matthew Ranostay09a99952008-01-24 11:49:21 +01002108 STAC_CTL_WIDGET_MONO_MUX,
Matt Porter403d1942005-11-29 15:00:51 +01002109 STAC_CTL_WIDGET_IO_SWITCH,
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002110 STAC_CTL_WIDGET_CLFE_SWITCH
Mattc7d4b2f2005-06-27 14:59:41 +02002111};
2112
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01002113static struct snd_kcontrol_new stac92xx_control_templates[] = {
Mattc7d4b2f2005-06-27 14:59:41 +02002114 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2115 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Matthew Ranostay09a99952008-01-24 11:49:21 +01002116 STAC_MONO_MUX,
Matt Porter403d1942005-11-29 15:00:51 +01002117 STAC_CODEC_IO_SWITCH(NULL, 0),
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002118 STAC_CODEC_CLFE_SWITCH(NULL, 0),
Mattc7d4b2f2005-06-27 14:59:41 +02002119};
2120
2121/* add dynamic controls */
2122static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val)
2123{
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01002124 struct snd_kcontrol_new *knew;
Mattc7d4b2f2005-06-27 14:59:41 +02002125
2126 if (spec->num_kctl_used >= spec->num_kctl_alloc) {
2127 int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
2128
2129 knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
2130 if (! knew)
2131 return -ENOMEM;
2132 if (spec->kctl_alloc) {
2133 memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
2134 kfree(spec->kctl_alloc);
2135 }
2136 spec->kctl_alloc = knew;
2137 spec->num_kctl_alloc = num;
2138 }
2139
2140 knew = &spec->kctl_alloc[spec->num_kctl_used];
2141 *knew = stac92xx_control_templates[type];
Takashi Iwai82fe0c52005-06-30 10:54:33 +02002142 knew->name = kstrdup(name, GFP_KERNEL);
Mattc7d4b2f2005-06-27 14:59:41 +02002143 if (! knew->name)
2144 return -ENOMEM;
2145 knew->private_value = val;
2146 spec->num_kctl_used++;
2147 return 0;
2148}
2149
Matt Porter403d1942005-11-29 15:00:51 +01002150/* flag inputs as additional dynamic lineouts */
2151static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
2152{
2153 struct sigmatel_spec *spec = codec->spec;
Steve Longerbeam7b043892007-05-03 20:50:03 +02002154 unsigned int wcaps, wtype;
2155 int i, num_dacs = 0;
2156
2157 /* use the wcaps cache to count all DACs available for line-outs */
2158 for (i = 0; i < codec->num_nodes; i++) {
2159 wcaps = codec->wcaps[i];
2160 wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01002161
Steve Longerbeam7b043892007-05-03 20:50:03 +02002162 if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
2163 num_dacs++;
2164 }
Matt Porter403d1942005-11-29 15:00:51 +01002165
Steve Longerbeam7b043892007-05-03 20:50:03 +02002166 snd_printdd("%s: total dac count=%d\n", __func__, num_dacs);
2167
Matt Porter403d1942005-11-29 15:00:51 +01002168 switch (cfg->line_outs) {
2169 case 3:
2170 /* add line-in as side */
Steve Longerbeam7b043892007-05-03 20:50:03 +02002171 if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002172 cfg->line_out_pins[cfg->line_outs] =
2173 cfg->input_pins[AUTO_PIN_LINE];
Matt Porter403d1942005-11-29 15:00:51 +01002174 spec->line_switch = 1;
2175 cfg->line_outs++;
2176 }
2177 break;
2178 case 2:
2179 /* add line-in as clfe and mic as side */
Steve Longerbeam7b043892007-05-03 20:50:03 +02002180 if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002181 cfg->line_out_pins[cfg->line_outs] =
2182 cfg->input_pins[AUTO_PIN_LINE];
Matt Porter403d1942005-11-29 15:00:51 +01002183 spec->line_switch = 1;
2184 cfg->line_outs++;
2185 }
Steve Longerbeam7b043892007-05-03 20:50:03 +02002186 if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002187 cfg->line_out_pins[cfg->line_outs] =
2188 cfg->input_pins[AUTO_PIN_MIC];
Matt Porter403d1942005-11-29 15:00:51 +01002189 spec->mic_switch = 1;
2190 cfg->line_outs++;
2191 }
2192 break;
2193 case 1:
2194 /* add line-in as surr and mic as clfe */
Steve Longerbeam7b043892007-05-03 20:50:03 +02002195 if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002196 cfg->line_out_pins[cfg->line_outs] =
2197 cfg->input_pins[AUTO_PIN_LINE];
Matt Porter403d1942005-11-29 15:00:51 +01002198 spec->line_switch = 1;
2199 cfg->line_outs++;
2200 }
Steve Longerbeam7b043892007-05-03 20:50:03 +02002201 if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002202 cfg->line_out_pins[cfg->line_outs] =
2203 cfg->input_pins[AUTO_PIN_MIC];
Matt Porter403d1942005-11-29 15:00:51 +01002204 spec->mic_switch = 1;
2205 cfg->line_outs++;
2206 }
2207 break;
2208 }
2209
2210 return 0;
2211}
2212
Steve Longerbeam7b043892007-05-03 20:50:03 +02002213
2214static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
2215{
2216 int i;
2217
2218 for (i = 0; i < spec->multiout.num_dacs; i++) {
2219 if (spec->multiout.dac_nids[i] == nid)
2220 return 1;
2221 }
2222
2223 return 0;
2224}
2225
Matt Porter3cc08dc2006-01-23 15:27:49 +01002226/*
Steve Longerbeam7b043892007-05-03 20:50:03 +02002227 * Fill in the dac_nids table from the parsed pin configuration
2228 * This function only works when every pin in line_out_pins[]
2229 * contains atleast one DAC in its connection list. Some 92xx
2230 * codecs are not connected directly to a DAC, such as the 9200
2231 * and 9202/925x. For those, dac_nids[] must be hard-coded.
Matt Porter3cc08dc2006-01-23 15:27:49 +01002232 */
Takashi Iwai19039bd2006-06-28 15:52:16 +02002233static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
Takashi Iwaidf802952007-07-02 19:18:00 +02002234 struct auto_pin_cfg *cfg)
Mattc7d4b2f2005-06-27 14:59:41 +02002235{
2236 struct sigmatel_spec *spec = codec->spec;
Steve Longerbeam7b043892007-05-03 20:50:03 +02002237 int i, j, conn_len = 0;
2238 hda_nid_t nid, conn[HDA_MAX_CONNECTIONS];
2239 unsigned int wcaps, wtype;
2240
Mattc7d4b2f2005-06-27 14:59:41 +02002241 for (i = 0; i < cfg->line_outs; i++) {
2242 nid = cfg->line_out_pins[i];
Steve Longerbeam7b043892007-05-03 20:50:03 +02002243 conn_len = snd_hda_get_connections(codec, nid, conn,
2244 HDA_MAX_CONNECTIONS);
2245 for (j = 0; j < conn_len; j++) {
2246 wcaps = snd_hda_param_read(codec, conn[j],
2247 AC_PAR_AUDIO_WIDGET_CAP);
2248 wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
Steve Longerbeam7b043892007-05-03 20:50:03 +02002249 if (wtype != AC_WID_AUD_OUT ||
2250 (wcaps & AC_WCAP_DIGITAL))
2251 continue;
2252 /* conn[j] is a DAC routed to this line-out */
2253 if (!is_in_dac_nids(spec, conn[j]))
2254 break;
2255 }
2256
2257 if (j == conn_len) {
Takashi Iwaidf802952007-07-02 19:18:00 +02002258 if (spec->multiout.num_dacs > 0) {
2259 /* we have already working output pins,
2260 * so let's drop the broken ones again
2261 */
2262 cfg->line_outs = spec->multiout.num_dacs;
2263 break;
2264 }
Steve Longerbeam7b043892007-05-03 20:50:03 +02002265 /* error out, no available DAC found */
2266 snd_printk(KERN_ERR
2267 "%s: No available DAC for pin 0x%x\n",
2268 __func__, nid);
2269 return -ENODEV;
2270 }
2271
2272 spec->multiout.dac_nids[i] = conn[j];
2273 spec->multiout.num_dacs++;
2274 if (conn_len > 1) {
2275 /* select this DAC in the pin's input mux */
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002276 snd_hda_codec_write_cache(codec, nid, 0,
2277 AC_VERB_SET_CONNECT_SEL, j);
Steve Longerbeam7b043892007-05-03 20:50:03 +02002278
2279 }
Mattc7d4b2f2005-06-27 14:59:41 +02002280 }
2281
Steve Longerbeam7b043892007-05-03 20:50:03 +02002282 snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
2283 spec->multiout.num_dacs,
2284 spec->multiout.dac_nids[0],
2285 spec->multiout.dac_nids[1],
2286 spec->multiout.dac_nids[2],
2287 spec->multiout.dac_nids[3],
2288 spec->multiout.dac_nids[4]);
Mattc7d4b2f2005-06-27 14:59:41 +02002289 return 0;
2290}
2291
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002292/* create volume control/switch for the given prefx type */
2293static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs)
2294{
2295 char name[32];
2296 int err;
2297
2298 sprintf(name, "%s Playback Volume", pfx);
2299 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
2300 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
2301 if (err < 0)
2302 return err;
2303 sprintf(name, "%s Playback Switch", pfx);
2304 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
2305 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
2306 if (err < 0)
2307 return err;
2308 return 0;
2309}
2310
Matthew Ranostayae0afd82008-02-22 17:55:05 +01002311static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
2312{
2313 if (!spec->multiout.hp_nid)
2314 spec->multiout.hp_nid = nid;
2315 else if (spec->multiout.num_dacs > 4) {
2316 printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
2317 return 1;
2318 } else {
2319 spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
2320 spec->multiout.num_dacs++;
2321 }
2322 return 0;
2323}
2324
2325static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
2326{
2327 if (is_in_dac_nids(spec, nid))
2328 return 1;
2329 if (spec->multiout.hp_nid == nid)
2330 return 1;
2331 return 0;
2332}
2333
Mattc7d4b2f2005-06-27 14:59:41 +02002334/* add playback controls from the parsed DAC table */
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002335static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
Takashi Iwai19039bd2006-06-28 15:52:16 +02002336 const struct auto_pin_cfg *cfg)
Mattc7d4b2f2005-06-27 14:59:41 +02002337{
Takashi Iwai19039bd2006-06-28 15:52:16 +02002338 static const char *chname[4] = {
2339 "Front", "Surround", NULL /*CLFE*/, "Side"
2340 };
Mattc7d4b2f2005-06-27 14:59:41 +02002341 hda_nid_t nid;
2342 int i, err;
2343
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002344 struct sigmatel_spec *spec = codec->spec;
Matthew Ranostayb5895dc2008-01-25 15:24:50 +01002345 unsigned int wid_caps, pincap;
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002346
2347
Mattc7d4b2f2005-06-27 14:59:41 +02002348 for (i = 0; i < cfg->line_outs; i++) {
Matt Porter403d1942005-11-29 15:00:51 +01002349 if (!spec->multiout.dac_nids[i])
Mattc7d4b2f2005-06-27 14:59:41 +02002350 continue;
2351
2352 nid = spec->multiout.dac_nids[i];
2353
2354 if (i == 2) {
2355 /* Center/LFE */
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002356 err = create_controls(spec, "Center", nid, 1);
2357 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002358 return err;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002359 err = create_controls(spec, "LFE", nid, 2);
2360 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002361 return err;
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002362
2363 wid_caps = get_wcaps(codec, nid);
2364
2365 if (wid_caps & AC_WCAP_LR_SWAP) {
2366 err = stac92xx_add_control(spec,
2367 STAC_CTL_WIDGET_CLFE_SWITCH,
2368 "Swap Center/LFE Playback Switch", nid);
2369
2370 if (err < 0)
2371 return err;
2372 }
2373
Mattc7d4b2f2005-06-27 14:59:41 +02002374 } else {
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002375 err = create_controls(spec, chname[i], nid, 3);
2376 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002377 return err;
2378 }
2379 }
2380
Matthew Ranostayb5895dc2008-01-25 15:24:50 +01002381 if (spec->line_switch) {
2382 nid = cfg->input_pins[AUTO_PIN_LINE];
2383 pincap = snd_hda_param_read(codec, nid,
2384 AC_PAR_PIN_CAP);
2385 if (pincap & AC_PINCAP_OUT) {
2386 err = stac92xx_add_control(spec,
2387 STAC_CTL_WIDGET_IO_SWITCH,
2388 "Line In as Output Switch", nid << 8);
2389 if (err < 0)
2390 return err;
2391 }
2392 }
Matt Porter403d1942005-11-29 15:00:51 +01002393
Matthew Ranostayb5895dc2008-01-25 15:24:50 +01002394 if (spec->mic_switch) {
Matthew Ranostaycace16f2008-01-30 14:58:38 +01002395 unsigned int def_conf;
Matthew Ranostayae0afd82008-02-22 17:55:05 +01002396 unsigned int mic_pin = AUTO_PIN_MIC;
2397again:
2398 nid = cfg->input_pins[mic_pin];
Matthew Ranostaycace16f2008-01-30 14:58:38 +01002399 def_conf = snd_hda_codec_read(codec, nid, 0,
2400 AC_VERB_GET_CONFIG_DEFAULT, 0);
Matthew Ranostaycace16f2008-01-30 14:58:38 +01002401 /* some laptops have an internal analog microphone
2402 * which can't be used as a output */
2403 if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
2404 pincap = snd_hda_param_read(codec, nid,
2405 AC_PAR_PIN_CAP);
2406 if (pincap & AC_PINCAP_OUT) {
2407 err = stac92xx_add_control(spec,
2408 STAC_CTL_WIDGET_IO_SWITCH,
2409 "Mic as Output Switch", (nid << 8) | 1);
Matthew Ranostayae0afd82008-02-22 17:55:05 +01002410 nid = snd_hda_codec_read(codec, nid, 0,
2411 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
2412 if (!check_in_dac_nids(spec, nid))
2413 add_spec_dacs(spec, nid);
Matthew Ranostaycace16f2008-01-30 14:58:38 +01002414 if (err < 0)
2415 return err;
2416 }
Matthew Ranostayae0afd82008-02-22 17:55:05 +01002417 } else if (mic_pin == AUTO_PIN_MIC) {
2418 mic_pin = AUTO_PIN_FRONT_MIC;
2419 goto again;
Matthew Ranostayb5895dc2008-01-25 15:24:50 +01002420 }
2421 }
Matt Porter403d1942005-11-29 15:00:51 +01002422
Mattc7d4b2f2005-06-27 14:59:41 +02002423 return 0;
2424}
2425
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002426/* add playback controls for Speaker and HP outputs */
2427static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
2428 struct auto_pin_cfg *cfg)
Mattc7d4b2f2005-06-27 14:59:41 +02002429{
2430 struct sigmatel_spec *spec = codec->spec;
Mattc7d4b2f2005-06-27 14:59:41 +02002431 hda_nid_t nid;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002432 int i, old_num_dacs, err;
Mattc7d4b2f2005-06-27 14:59:41 +02002433
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002434 old_num_dacs = spec->multiout.num_dacs;
2435 for (i = 0; i < cfg->hp_outs; i++) {
2436 unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
2437 if (wid_caps & AC_WCAP_UNSOL_CAP)
2438 spec->hp_detect = 1;
2439 nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
2440 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
2441 if (check_in_dac_nids(spec, nid))
2442 nid = 0;
2443 if (! nid)
Mattc7d4b2f2005-06-27 14:59:41 +02002444 continue;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002445 add_spec_dacs(spec, nid);
2446 }
2447 for (i = 0; i < cfg->speaker_outs; i++) {
Steve Longerbeam7b043892007-05-03 20:50:03 +02002448 nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0,
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002449 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
2450 if (check_in_dac_nids(spec, nid))
2451 nid = 0;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002452 if (! nid)
2453 continue;
2454 add_spec_dacs(spec, nid);
Mattc7d4b2f2005-06-27 14:59:41 +02002455 }
Matthew Ranostay1b290a52007-07-12 15:17:34 +02002456 for (i = 0; i < cfg->line_outs; i++) {
2457 nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
2458 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
2459 if (check_in_dac_nids(spec, nid))
2460 nid = 0;
2461 if (! nid)
2462 continue;
2463 add_spec_dacs(spec, nid);
2464 }
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002465 for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
2466 static const char *pfxs[] = {
2467 "Speaker", "External Speaker", "Speaker2",
2468 };
2469 err = create_controls(spec, pfxs[i - old_num_dacs],
2470 spec->multiout.dac_nids[i], 3);
2471 if (err < 0)
2472 return err;
2473 }
2474 if (spec->multiout.hp_nid) {
2475 const char *pfx;
Takashi Iwai6020c002007-11-19 11:56:26 +01002476 if (old_num_dacs == spec->multiout.num_dacs)
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002477 pfx = "Master";
2478 else
2479 pfx = "Headphone";
2480 err = create_controls(spec, pfx, spec->multiout.hp_nid, 3);
2481 if (err < 0)
2482 return err;
2483 }
Mattc7d4b2f2005-06-27 14:59:41 +02002484
2485 return 0;
2486}
2487
Matthew Ranostayb22b4822008-01-22 12:32:30 +01002488/* labels for mono mux outputs */
2489static const char *stac92xx_mono_labels[3] = {
2490 "DAC0", "DAC1", "Mixer"
2491};
2492
2493/* create mono mux for mono out on capable codecs */
2494static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
2495{
2496 struct sigmatel_spec *spec = codec->spec;
2497 struct hda_input_mux *mono_mux = &spec->private_mono_mux;
2498 int i, num_cons;
2499 hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)];
2500
2501 num_cons = snd_hda_get_connections(codec,
2502 spec->mono_nid,
2503 con_lst,
2504 HDA_MAX_NUM_INPUTS);
2505 if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
2506 return -EINVAL;
2507
2508 for (i = 0; i < num_cons; i++) {
2509 mono_mux->items[mono_mux->num_items].label =
2510 stac92xx_mono_labels[i];
2511 mono_mux->items[mono_mux->num_items].index = i;
2512 mono_mux->num_items++;
2513 }
Matthew Ranostay09a99952008-01-24 11:49:21 +01002514
2515 return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
2516 "Mono Mux", spec->mono_nid);
Matthew Ranostayb22b4822008-01-22 12:32:30 +01002517}
2518
Matt Porter8b657272006-10-26 17:12:59 +02002519/* labels for dmic mux inputs */
Adrian Bunkddc2cec2006-11-20 12:03:44 +01002520static const char *stac92xx_dmic_labels[5] = {
Matt Porter8b657272006-10-26 17:12:59 +02002521 "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
2522 "Digital Mic 3", "Digital Mic 4"
2523};
2524
2525/* create playback/capture controls for input pins on dmic capable codecs */
2526static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
2527 const struct auto_pin_cfg *cfg)
2528{
2529 struct sigmatel_spec *spec = codec->spec;
2530 struct hda_input_mux *dimux = &spec->private_dimux;
2531 hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002532 int err, i, j;
2533 char name[32];
Matt Porter8b657272006-10-26 17:12:59 +02002534
2535 dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
2536 dimux->items[dimux->num_items].index = 0;
2537 dimux->num_items++;
2538
2539 for (i = 0; i < spec->num_dmics; i++) {
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002540 hda_nid_t nid;
Matt Porter8b657272006-10-26 17:12:59 +02002541 int index;
2542 int num_cons;
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002543 unsigned int wcaps;
Matt Porter8b657272006-10-26 17:12:59 +02002544 unsigned int def_conf;
2545
2546 def_conf = snd_hda_codec_read(codec,
2547 spec->dmic_nids[i],
2548 0,
2549 AC_VERB_GET_CONFIG_DEFAULT,
2550 0);
2551 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
2552 continue;
2553
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002554 nid = spec->dmic_nids[i];
Matt Porter8b657272006-10-26 17:12:59 +02002555 num_cons = snd_hda_get_connections(codec,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01002556 spec->dmux_nids[0],
Matt Porter8b657272006-10-26 17:12:59 +02002557 con_lst,
2558 HDA_MAX_NUM_INPUTS);
2559 for (j = 0; j < num_cons; j++)
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002560 if (con_lst[j] == nid) {
Matt Porter8b657272006-10-26 17:12:59 +02002561 index = j;
2562 goto found;
2563 }
2564 continue;
2565found:
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002566 wcaps = get_wcaps(codec, nid);
2567
2568 if (wcaps & AC_WCAP_OUT_AMP) {
2569 sprintf(name, "%s Capture Volume",
2570 stac92xx_dmic_labels[dimux->num_items]);
2571
2572 err = stac92xx_add_control(spec,
2573 STAC_CTL_WIDGET_VOL,
2574 name,
2575 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2576 if (err < 0)
2577 return err;
2578 }
2579
Matt Porter8b657272006-10-26 17:12:59 +02002580 dimux->items[dimux->num_items].label =
2581 stac92xx_dmic_labels[dimux->num_items];
2582 dimux->items[dimux->num_items].index = index;
2583 dimux->num_items++;
2584 }
2585
2586 return 0;
2587}
2588
Mattc7d4b2f2005-06-27 14:59:41 +02002589/* create playback/capture controls for input pins */
2590static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
2591{
2592 struct sigmatel_spec *spec = codec->spec;
Mattc7d4b2f2005-06-27 14:59:41 +02002593 struct hda_input_mux *imux = &spec->private_imux;
2594 hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
2595 int i, j, k;
2596
2597 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai314634b2006-09-21 11:56:18 +02002598 int index;
Mattc7d4b2f2005-06-27 14:59:41 +02002599
Takashi Iwai314634b2006-09-21 11:56:18 +02002600 if (!cfg->input_pins[i])
2601 continue;
2602 index = -1;
2603 for (j = 0; j < spec->num_muxes; j++) {
2604 int num_cons;
2605 num_cons = snd_hda_get_connections(codec,
2606 spec->mux_nids[j],
2607 con_lst,
2608 HDA_MAX_NUM_INPUTS);
2609 for (k = 0; k < num_cons; k++)
2610 if (con_lst[k] == cfg->input_pins[i]) {
2611 index = k;
2612 goto found;
2613 }
Mattc7d4b2f2005-06-27 14:59:41 +02002614 }
Takashi Iwai314634b2006-09-21 11:56:18 +02002615 continue;
2616 found:
2617 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
2618 imux->items[imux->num_items].index = index;
2619 imux->num_items++;
Mattc7d4b2f2005-06-27 14:59:41 +02002620 }
2621
Steve Longerbeam7b043892007-05-03 20:50:03 +02002622 if (imux->num_items) {
Sam Revitch62fe78e2006-05-10 15:09:17 +02002623 /*
2624 * Set the current input for the muxes.
2625 * The STAC9221 has two input muxes with identical source
2626 * NID lists. Hopefully this won't get confused.
2627 */
2628 for (i = 0; i < spec->num_muxes; i++) {
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002629 snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0,
2630 AC_VERB_SET_CONNECT_SEL,
2631 imux->items[0].index);
Sam Revitch62fe78e2006-05-10 15:09:17 +02002632 }
2633 }
2634
Mattc7d4b2f2005-06-27 14:59:41 +02002635 return 0;
2636}
2637
Mattc7d4b2f2005-06-27 14:59:41 +02002638static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
2639{
2640 struct sigmatel_spec *spec = codec->spec;
2641 int i;
2642
2643 for (i = 0; i < spec->autocfg.line_outs; i++) {
2644 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2645 stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
2646 }
2647}
2648
2649static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
2650{
2651 struct sigmatel_spec *spec = codec->spec;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002652 int i;
Mattc7d4b2f2005-06-27 14:59:41 +02002653
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002654 for (i = 0; i < spec->autocfg.hp_outs; i++) {
2655 hda_nid_t pin;
2656 pin = spec->autocfg.hp_pins[i];
2657 if (pin) /* connect to front */
2658 stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
2659 }
2660 for (i = 0; i < spec->autocfg.speaker_outs; i++) {
2661 hda_nid_t pin;
2662 pin = spec->autocfg.speaker_pins[i];
2663 if (pin) /* connect to front */
2664 stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
2665 }
Mattc7d4b2f2005-06-27 14:59:41 +02002666}
2667
Matt Porter3cc08dc2006-01-23 15:27:49 +01002668static 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 +02002669{
2670 struct sigmatel_spec *spec = codec->spec;
2671 int err;
Jiang Zhebcecd9b2007-11-12 12:57:03 +01002672 int hp_speaker_swap = 0;
Mattc7d4b2f2005-06-27 14:59:41 +02002673
Matt Porter8b657272006-10-26 17:12:59 +02002674 if ((err = snd_hda_parse_pin_def_config(codec,
2675 &spec->autocfg,
2676 spec->dmic_nids)) < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002677 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002678 if (! spec->autocfg.line_outs)
Matt Porter869264c2006-01-25 19:20:50 +01002679 return 0; /* can't find valid pin config */
Takashi Iwai19039bd2006-06-28 15:52:16 +02002680
Jiang Zhebcecd9b2007-11-12 12:57:03 +01002681 /* If we have no real line-out pin and multiple hp-outs, HPs should
2682 * be set up as multi-channel outputs.
2683 */
2684 if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
2685 spec->autocfg.hp_outs > 1) {
2686 /* Copy hp_outs to line_outs, backup line_outs in
2687 * speaker_outs so that the following routines can handle
2688 * HP pins as primary outputs.
2689 */
2690 memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
2691 sizeof(spec->autocfg.line_out_pins));
2692 spec->autocfg.speaker_outs = spec->autocfg.line_outs;
2693 memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
2694 sizeof(spec->autocfg.hp_pins));
2695 spec->autocfg.line_outs = spec->autocfg.hp_outs;
2696 hp_speaker_swap = 1;
2697 }
Matthew Ranostay09a99952008-01-24 11:49:21 +01002698 if (spec->autocfg.mono_out_pin) {
2699 int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin)
2700 & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
2701 u32 caps = query_amp_caps(codec,
2702 spec->autocfg.mono_out_pin, dir);
2703 hda_nid_t conn_list[1];
2704
2705 /* get the mixer node and then the mono mux if it exists */
2706 if (snd_hda_get_connections(codec,
2707 spec->autocfg.mono_out_pin, conn_list, 1) &&
2708 snd_hda_get_connections(codec, conn_list[0],
2709 conn_list, 1)) {
2710
2711 int wcaps = get_wcaps(codec, conn_list[0]);
2712 int wid_type = (wcaps & AC_WCAP_TYPE)
2713 >> AC_WCAP_TYPE_SHIFT;
2714 /* LR swap check, some stac925x have a mux that
2715 * changes the DACs output path instead of the
2716 * mono-mux path.
2717 */
2718 if (wid_type == AC_WID_AUD_SEL &&
2719 !(wcaps & AC_WCAP_LR_SWAP))
2720 spec->mono_nid = conn_list[0];
2721 }
2722 /* all mono outs have a least a mute/unmute switch */
2723 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
2724 "Mono Playback Switch",
2725 HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
2726 1, 0, dir));
2727 if (err < 0)
2728 return err;
2729 /* check to see if there is volume support for the amp */
2730 if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
2731 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
2732 "Mono Playback Volume",
2733 HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
2734 1, 0, dir));
2735 if (err < 0)
2736 return err;
2737 }
2738
2739 stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
2740 AC_PINCTL_OUT_EN);
2741 }
Jiang Zhebcecd9b2007-11-12 12:57:03 +01002742
Matt Porter403d1942005-11-29 15:00:51 +01002743 if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
2744 return err;
Takashi Iwai19039bd2006-06-28 15:52:16 +02002745 if (spec->multiout.num_dacs == 0)
2746 if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2747 return err;
Mattc7d4b2f2005-06-27 14:59:41 +02002748
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002749 err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg);
2750
2751 if (err < 0)
2752 return err;
2753
Jiang Zhebcecd9b2007-11-12 12:57:03 +01002754 if (hp_speaker_swap == 1) {
2755 /* Restore the hp_outs and line_outs */
2756 memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
2757 sizeof(spec->autocfg.line_out_pins));
2758 spec->autocfg.hp_outs = spec->autocfg.line_outs;
2759 memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins,
2760 sizeof(spec->autocfg.speaker_pins));
2761 spec->autocfg.line_outs = spec->autocfg.speaker_outs;
2762 memset(spec->autocfg.speaker_pins, 0,
2763 sizeof(spec->autocfg.speaker_pins));
2764 spec->autocfg.speaker_outs = 0;
2765 }
2766
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002767 err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
2768
2769 if (err < 0)
2770 return err;
2771
2772 err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
2773
2774 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002775 return err;
2776
Matthew Ranostayb22b4822008-01-22 12:32:30 +01002777 if (spec->mono_nid > 0) {
2778 err = stac92xx_auto_create_mono_output_ctls(codec);
2779 if (err < 0)
2780 return err;
2781 }
2782
Matt Porter8b657272006-10-26 17:12:59 +02002783 if (spec->num_dmics > 0)
2784 if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
2785 &spec->autocfg)) < 0)
2786 return err;
2787
Mattc7d4b2f2005-06-27 14:59:41 +02002788 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Matt Porter403d1942005-11-29 15:00:51 +01002789 if (spec->multiout.max_channels > 2)
Mattc7d4b2f2005-06-27 14:59:41 +02002790 spec->surr_switch = 1;
Mattc7d4b2f2005-06-27 14:59:41 +02002791
Takashi Iwai82bc9552006-03-21 11:24:42 +01002792 if (spec->autocfg.dig_out_pin)
Matt Porter3cc08dc2006-01-23 15:27:49 +01002793 spec->multiout.dig_out_nid = dig_out;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002794 if (spec->autocfg.dig_in_pin)
Matt Porter3cc08dc2006-01-23 15:27:49 +01002795 spec->dig_in_nid = dig_in;
Mattc7d4b2f2005-06-27 14:59:41 +02002796
2797 if (spec->kctl_alloc)
2798 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2799
2800 spec->input_mux = &spec->private_imux;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01002801 if (!spec->dinput_mux)
2802 spec->dinput_mux = &spec->private_dimux;
Matthew Ranostayb22b4822008-01-22 12:32:30 +01002803 spec->mono_mux = &spec->private_mono_mux;
Mattc7d4b2f2005-06-27 14:59:41 +02002804
2805 return 1;
2806}
2807
Takashi Iwai82bc9552006-03-21 11:24:42 +01002808/* add playback controls for HP output */
2809static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
2810 struct auto_pin_cfg *cfg)
2811{
2812 struct sigmatel_spec *spec = codec->spec;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002813 hda_nid_t pin = cfg->hp_pins[0];
Takashi Iwai82bc9552006-03-21 11:24:42 +01002814 unsigned int wid_caps;
2815
2816 if (! pin)
2817 return 0;
2818
2819 wid_caps = get_wcaps(codec, pin);
Takashi Iwai505cb342006-03-27 12:51:52 +02002820 if (wid_caps & AC_WCAP_UNSOL_CAP)
Takashi Iwai82bc9552006-03-21 11:24:42 +01002821 spec->hp_detect = 1;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002822
2823 return 0;
2824}
2825
Richard Fish160ea0d2006-09-06 13:58:25 +02002826/* add playback controls for LFE output */
2827static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
2828 struct auto_pin_cfg *cfg)
2829{
2830 struct sigmatel_spec *spec = codec->spec;
2831 int err;
2832 hda_nid_t lfe_pin = 0x0;
2833 int i;
2834
2835 /*
2836 * search speaker outs and line outs for a mono speaker pin
2837 * with an amp. If one is found, add LFE controls
2838 * for it.
2839 */
2840 for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
2841 hda_nid_t pin = spec->autocfg.speaker_pins[i];
Takashi Iwai64ed0df2008-02-29 11:57:53 +01002842 unsigned int wcaps = get_wcaps(codec, pin);
Richard Fish160ea0d2006-09-06 13:58:25 +02002843 wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
2844 if (wcaps == AC_WCAP_OUT_AMP)
2845 /* found a mono speaker with an amp, must be lfe */
2846 lfe_pin = pin;
2847 }
2848
2849 /* if speaker_outs is 0, then speakers may be in line_outs */
2850 if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
2851 for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
2852 hda_nid_t pin = spec->autocfg.line_out_pins[i];
Takashi Iwai64ed0df2008-02-29 11:57:53 +01002853 unsigned int defcfg;
Harvey Harrison8b551782008-02-29 11:56:48 +01002854 defcfg = snd_hda_codec_read(codec, pin, 0,
Richard Fish160ea0d2006-09-06 13:58:25 +02002855 AC_VERB_GET_CONFIG_DEFAULT,
2856 0x00);
Harvey Harrison8b551782008-02-29 11:56:48 +01002857 if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
Takashi Iwai64ed0df2008-02-29 11:57:53 +01002858 unsigned int wcaps = get_wcaps(codec, pin);
Richard Fish160ea0d2006-09-06 13:58:25 +02002859 wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
2860 if (wcaps == AC_WCAP_OUT_AMP)
2861 /* found a mono speaker with an amp,
2862 must be lfe */
2863 lfe_pin = pin;
2864 }
2865 }
2866 }
2867
2868 if (lfe_pin) {
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002869 err = create_controls(spec, "LFE", lfe_pin, 1);
Richard Fish160ea0d2006-09-06 13:58:25 +02002870 if (err < 0)
2871 return err;
2872 }
2873
2874 return 0;
2875}
2876
Mattc7d4b2f2005-06-27 14:59:41 +02002877static int stac9200_parse_auto_config(struct hda_codec *codec)
2878{
2879 struct sigmatel_spec *spec = codec->spec;
2880 int err;
2881
Kailang Yangdf694da2005-12-05 19:42:22 +01002882 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002883 return err;
2884
2885 if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
2886 return err;
2887
Takashi Iwai82bc9552006-03-21 11:24:42 +01002888 if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
2889 return err;
2890
Richard Fish160ea0d2006-09-06 13:58:25 +02002891 if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0)
2892 return err;
2893
Takashi Iwai82bc9552006-03-21 11:24:42 +01002894 if (spec->autocfg.dig_out_pin)
Mattc7d4b2f2005-06-27 14:59:41 +02002895 spec->multiout.dig_out_nid = 0x05;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002896 if (spec->autocfg.dig_in_pin)
Mattc7d4b2f2005-06-27 14:59:41 +02002897 spec->dig_in_nid = 0x04;
Mattc7d4b2f2005-06-27 14:59:41 +02002898
2899 if (spec->kctl_alloc)
2900 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2901
2902 spec->input_mux = &spec->private_imux;
Matt Porter8b657272006-10-26 17:12:59 +02002903 spec->dinput_mux = &spec->private_dimux;
Mattc7d4b2f2005-06-27 14:59:41 +02002904
2905 return 1;
2906}
2907
Sam Revitch62fe78e2006-05-10 15:09:17 +02002908/*
2909 * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
2910 * funky external mute control using GPIO pins.
2911 */
2912
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002913static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
Matthew Ranostay4fe51952008-01-29 15:28:44 +01002914 unsigned int dir_mask, unsigned int data)
Sam Revitch62fe78e2006-05-10 15:09:17 +02002915{
2916 unsigned int gpiostate, gpiomask, gpiodir;
2917
2918 gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
2919 AC_VERB_GET_GPIO_DATA, 0);
Matthew Ranostay4fe51952008-01-29 15:28:44 +01002920 gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
Sam Revitch62fe78e2006-05-10 15:09:17 +02002921
2922 gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
2923 AC_VERB_GET_GPIO_MASK, 0);
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002924 gpiomask |= mask;
Sam Revitch62fe78e2006-05-10 15:09:17 +02002925
2926 gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
2927 AC_VERB_GET_GPIO_DIRECTION, 0);
Matthew Ranostay4fe51952008-01-29 15:28:44 +01002928 gpiodir |= dir_mask;
Sam Revitch62fe78e2006-05-10 15:09:17 +02002929
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002930 /* Configure GPIOx as CMOS */
Sam Revitch62fe78e2006-05-10 15:09:17 +02002931 snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
2932
2933 snd_hda_codec_write(codec, codec->afg, 0,
2934 AC_VERB_SET_GPIO_MASK, gpiomask);
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002935 snd_hda_codec_read(codec, codec->afg, 0,
2936 AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
Sam Revitch62fe78e2006-05-10 15:09:17 +02002937
2938 msleep(1);
2939
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002940 snd_hda_codec_read(codec, codec->afg, 0,
2941 AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
Sam Revitch62fe78e2006-05-10 15:09:17 +02002942}
2943
Takashi Iwai314634b2006-09-21 11:56:18 +02002944static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
2945 unsigned int event)
2946{
2947 if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
Takashi Iwaidc81bed2007-09-03 09:36:36 +02002948 snd_hda_codec_write_cache(codec, nid, 0,
2949 AC_VERB_SET_UNSOLICITED_ENABLE,
2950 (AC_USRSP_EN | event));
Takashi Iwai314634b2006-09-21 11:56:18 +02002951}
2952
Matthew Ranostaya64135a2008-01-10 16:55:06 +01002953static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
2954{
2955 int i;
2956 for (i = 0; i < cfg->hp_outs; i++)
2957 if (cfg->hp_pins[i] == nid)
2958 return 1; /* nid is a HP-Out */
2959
2960 return 0; /* nid is not a HP-Out */
2961};
2962
Matthew Ranostayb76c8502008-02-06 14:49:44 +01002963static void stac92xx_power_down(struct hda_codec *codec)
2964{
2965 struct sigmatel_spec *spec = codec->spec;
2966
2967 /* power down inactive DACs */
2968 hda_nid_t *dac;
2969 for (dac = spec->dac_list; *dac; dac++)
Matthew Ranostay44510892008-02-21 07:49:31 +01002970 if (!is_in_dac_nids(spec, *dac) &&
2971 spec->multiout.hp_nid != *dac)
Matthew Ranostayb76c8502008-02-06 14:49:44 +01002972 snd_hda_codec_write_cache(codec, *dac, 0,
2973 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2974}
2975
Mattc7d4b2f2005-06-27 14:59:41 +02002976static int stac92xx_init(struct hda_codec *codec)
2977{
2978 struct sigmatel_spec *spec = codec->spec;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002979 struct auto_pin_cfg *cfg = &spec->autocfg;
2980 int i;
Mattc7d4b2f2005-06-27 14:59:41 +02002981
Mattc7d4b2f2005-06-27 14:59:41 +02002982 snd_hda_sequence_write(codec, spec->init);
2983
Takashi Iwai82bc9552006-03-21 11:24:42 +01002984 /* set up pins */
2985 if (spec->hp_detect) {
Takashi Iwai505cb342006-03-27 12:51:52 +02002986 /* Enable unsolicited responses on the HP widget */
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002987 for (i = 0; i < cfg->hp_outs; i++)
Takashi Iwai314634b2006-09-21 11:56:18 +02002988 enable_pin_detect(codec, cfg->hp_pins[i],
2989 STAC_HP_EVENT);
Takashi Iwai0a07aca2007-03-13 10:40:23 +01002990 /* force to enable the first line-out; the others are set up
2991 * in unsol_event
2992 */
2993 stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
2994 AC_PINCTL_OUT_EN);
Takashi Iwaieb995a82006-09-21 14:28:21 +02002995 stac92xx_auto_init_hp_out(codec);
Takashi Iwai82bc9552006-03-21 11:24:42 +01002996 /* fake event to set up pins */
2997 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
2998 } else {
2999 stac92xx_auto_init_multi_out(codec);
3000 stac92xx_auto_init_hp_out(codec);
3001 }
3002 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwaic960a032006-03-23 17:06:28 +01003003 hda_nid_t nid = cfg->input_pins[i];
3004 if (nid) {
3005 unsigned int pinctl = AC_PINCTL_IN_EN;
3006 if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
3007 pinctl |= stac92xx_get_vref(codec, nid);
3008 stac92xx_auto_set_pinctl(codec, nid, pinctl);
3009 }
Takashi Iwai82bc9552006-03-21 11:24:42 +01003010 }
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003011 for (i = 0; i < spec->num_dmics; i++)
3012 stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
3013 AC_PINCTL_IN_EN);
3014 for (i = 0; i < spec->num_pwrs; i++) {
3015 int event = is_nid_hp_pin(cfg, spec->pwr_nids[i])
3016 ? STAC_HP_EVENT : STAC_PWR_EVENT;
3017 int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i],
3018 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
3019 /* outputs are only ports capable of power management
3020 * any attempts on powering down a input port cause the
3021 * referenced VREF to act quirky.
3022 */
3023 if (pinctl & AC_PINCTL_IN_EN)
3024 continue;
3025 enable_pin_detect(codec, spec->pwr_nids[i], event | i);
3026 codec->patch_ops.unsol_event(codec, (event | i) << 26);
3027 }
Matthew Ranostayb76c8502008-02-06 14:49:44 +01003028 if (spec->dac_list)
3029 stac92xx_power_down(codec);
Takashi Iwai82bc9552006-03-21 11:24:42 +01003030 if (cfg->dig_out_pin)
3031 stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
3032 AC_PINCTL_OUT_EN);
3033 if (cfg->dig_in_pin)
3034 stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
3035 AC_PINCTL_IN_EN);
3036
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003037 stac_gpio_set(codec, spec->gpio_mask,
3038 spec->gpio_dir, spec->gpio_data);
Sam Revitch62fe78e2006-05-10 15:09:17 +02003039
Mattc7d4b2f2005-06-27 14:59:41 +02003040 return 0;
3041}
3042
Matt2f2f4252005-04-13 14:45:30 +02003043static void stac92xx_free(struct hda_codec *codec)
3044{
Mattc7d4b2f2005-06-27 14:59:41 +02003045 struct sigmatel_spec *spec = codec->spec;
3046 int i;
3047
3048 if (! spec)
3049 return;
3050
3051 if (spec->kctl_alloc) {
3052 for (i = 0; i < spec->num_kctl_used; i++)
3053 kfree(spec->kctl_alloc[i].name);
3054 kfree(spec->kctl_alloc);
3055 }
3056
Richard Fish11b44bb2006-08-23 18:31:34 +02003057 if (spec->bios_pin_configs)
3058 kfree(spec->bios_pin_configs);
3059
Mattc7d4b2f2005-06-27 14:59:41 +02003060 kfree(spec);
Matt2f2f4252005-04-13 14:45:30 +02003061}
3062
Matt4e550962005-07-04 17:51:39 +02003063static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
3064 unsigned int flag)
3065{
3066 unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
3067 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
Steve Longerbeam7b043892007-05-03 20:50:03 +02003068
Takashi Iwaif9acba42007-05-29 18:01:06 +02003069 if (pin_ctl & AC_PINCTL_IN_EN) {
3070 /*
3071 * we need to check the current set-up direction of
3072 * shared input pins since they can be switched via
3073 * "xxx as Output" mixer switch
3074 */
3075 struct sigmatel_spec *spec = codec->spec;
3076 struct auto_pin_cfg *cfg = &spec->autocfg;
3077 if ((nid == cfg->input_pins[AUTO_PIN_LINE] &&
3078 spec->line_switch) ||
3079 (nid == cfg->input_pins[AUTO_PIN_MIC] &&
3080 spec->mic_switch))
3081 return;
3082 }
3083
Steve Longerbeam7b043892007-05-03 20:50:03 +02003084 /* if setting pin direction bits, clear the current
3085 direction bits first */
3086 if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
3087 pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
3088
Takashi Iwai82beb8f2007-08-10 17:09:26 +02003089 snd_hda_codec_write_cache(codec, nid, 0,
Matt4e550962005-07-04 17:51:39 +02003090 AC_VERB_SET_PIN_WIDGET_CONTROL,
3091 pin_ctl | flag);
3092}
3093
3094static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
3095 unsigned int flag)
3096{
3097 unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
3098 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
Takashi Iwai82beb8f2007-08-10 17:09:26 +02003099 snd_hda_codec_write_cache(codec, nid, 0,
Matt4e550962005-07-04 17:51:39 +02003100 AC_VERB_SET_PIN_WIDGET_CONTROL,
3101 pin_ctl & ~flag);
3102}
3103
Jiang Zhe40c1d302007-11-12 13:05:16 +01003104static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
Takashi Iwai314634b2006-09-21 11:56:18 +02003105{
3106 if (!nid)
3107 return 0;
3108 if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
Jiang Zhe40c1d302007-11-12 13:05:16 +01003109 & (1 << 31)) {
3110 unsigned int pinctl;
3111 pinctl = snd_hda_codec_read(codec, nid, 0,
3112 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
3113 if (pinctl & AC_PINCTL_IN_EN)
3114 return 0; /* mic- or line-input */
3115 else
3116 return 1; /* HP-output */
3117 }
Takashi Iwai314634b2006-09-21 11:56:18 +02003118 return 0;
3119}
3120
3121static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
Matt4e550962005-07-04 17:51:39 +02003122{
3123 struct sigmatel_spec *spec = codec->spec;
3124 struct auto_pin_cfg *cfg = &spec->autocfg;
3125 int i, presence;
3126
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003127 presence = 0;
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003128 if (spec->gpio_mute)
3129 presence = !(snd_hda_codec_read(codec, codec->afg, 0,
3130 AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
3131
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003132 for (i = 0; i < cfg->hp_outs; i++) {
Takashi Iwai314634b2006-09-21 11:56:18 +02003133 if (presence)
3134 break;
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003135 presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003136 }
Matt4e550962005-07-04 17:51:39 +02003137
3138 if (presence) {
3139 /* disable lineouts, enable hp */
3140 for (i = 0; i < cfg->line_outs; i++)
3141 stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
3142 AC_PINCTL_OUT_EN);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003143 for (i = 0; i < cfg->speaker_outs; i++)
3144 stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
3145 AC_PINCTL_OUT_EN);
Matt4e550962005-07-04 17:51:39 +02003146 } else {
3147 /* enable lineouts, disable hp */
3148 for (i = 0; i < cfg->line_outs; i++)
3149 stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
3150 AC_PINCTL_OUT_EN);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003151 for (i = 0; i < cfg->speaker_outs; i++)
3152 stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
3153 AC_PINCTL_OUT_EN);
Matt4e550962005-07-04 17:51:39 +02003154 }
3155}
3156
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003157static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
3158{
3159 struct sigmatel_spec *spec = codec->spec;
3160 hda_nid_t nid = spec->pwr_nids[idx];
3161 int presence, val;
3162 val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
3163 & 0x000000ff;
3164 presence = get_hp_pin_presence(codec, nid);
3165 idx = 1 << idx;
3166
3167 if (presence)
3168 val &= ~idx;
3169 else
3170 val |= idx;
3171
3172 /* power down unused output ports */
3173 snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
3174};
3175
Takashi Iwai314634b2006-09-21 11:56:18 +02003176static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
3177{
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003178 struct sigmatel_spec *spec = codec->spec;
3179 int idx = res >> 26 & 0x0f;
3180
3181 switch ((res >> 26) & 0x30) {
Takashi Iwai314634b2006-09-21 11:56:18 +02003182 case STAC_HP_EVENT:
3183 stac92xx_hp_detect(codec, res);
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003184 /* fallthru */
3185 case STAC_PWR_EVENT:
3186 if (spec->num_pwrs > 0)
3187 stac92xx_pin_sense(codec, idx);
Takashi Iwai314634b2006-09-21 11:56:18 +02003188 }
3189}
3190
Takashi Iwaicb53c622007-08-10 17:21:45 +02003191#ifdef SND_HDA_NEEDS_RESUME
Mattff6fdc32005-06-27 15:06:52 +02003192static int stac92xx_resume(struct hda_codec *codec)
3193{
Takashi Iwaidc81bed2007-09-03 09:36:36 +02003194 struct sigmatel_spec *spec = codec->spec;
3195
Richard Fish11b44bb2006-08-23 18:31:34 +02003196 stac92xx_set_config_regs(codec);
Takashi Iwaidc81bed2007-09-03 09:36:36 +02003197 snd_hda_sequence_write(codec, spec->init);
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003198 stac_gpio_set(codec, spec->gpio_mask,
3199 spec->gpio_dir, spec->gpio_data);
Takashi Iwai82beb8f2007-08-10 17:09:26 +02003200 snd_hda_codec_resume_amp(codec);
3201 snd_hda_codec_resume_cache(codec);
Matthew Ranostayb76c8502008-02-06 14:49:44 +01003202 /* power down inactive DACs */
3203 if (spec->dac_list)
3204 stac92xx_power_down(codec);
Takashi Iwaidc81bed2007-09-03 09:36:36 +02003205 /* invoke unsolicited event to reset the HP state */
3206 if (spec->hp_detect)
3207 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
Mattff6fdc32005-06-27 15:06:52 +02003208 return 0;
3209}
3210#endif
3211
Matt2f2f4252005-04-13 14:45:30 +02003212static struct hda_codec_ops stac92xx_patch_ops = {
3213 .build_controls = stac92xx_build_controls,
3214 .build_pcms = stac92xx_build_pcms,
3215 .init = stac92xx_init,
3216 .free = stac92xx_free,
Matt4e550962005-07-04 17:51:39 +02003217 .unsol_event = stac92xx_unsol_event,
Takashi Iwaicb53c622007-08-10 17:21:45 +02003218#ifdef SND_HDA_NEEDS_RESUME
Mattff6fdc32005-06-27 15:06:52 +02003219 .resume = stac92xx_resume,
3220#endif
Matt2f2f4252005-04-13 14:45:30 +02003221};
3222
3223static int patch_stac9200(struct hda_codec *codec)
3224{
3225 struct sigmatel_spec *spec;
Mattc7d4b2f2005-06-27 14:59:41 +02003226 int err;
Matt2f2f4252005-04-13 14:45:30 +02003227
Takashi Iwaie560d8d2005-09-09 14:21:46 +02003228 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Matt2f2f4252005-04-13 14:45:30 +02003229 if (spec == NULL)
3230 return -ENOMEM;
3231
3232 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003233 spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02003234 spec->pin_nids = stac9200_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003235 spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
3236 stac9200_models,
3237 stac9200_cfg_tbl);
Richard Fish11b44bb2006-08-23 18:31:34 +02003238 if (spec->board_config < 0) {
3239 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
3240 err = stac92xx_save_bios_config_regs(codec);
3241 if (err < 0) {
3242 stac92xx_free(codec);
3243 return err;
3244 }
3245 spec->pin_configs = spec->bios_pin_configs;
3246 } else {
Matt Porter403d1942005-11-29 15:00:51 +01003247 spec->pin_configs = stac9200_brd_tbl[spec->board_config];
3248 stac92xx_set_config_regs(codec);
3249 }
Matt2f2f4252005-04-13 14:45:30 +02003250
3251 spec->multiout.max_channels = 2;
3252 spec->multiout.num_dacs = 1;
3253 spec->multiout.dac_nids = stac9200_dac_nids;
3254 spec->adc_nids = stac9200_adc_nids;
3255 spec->mux_nids = stac9200_mux_nids;
Mattdabbed62005-06-14 10:19:34 +02003256 spec->num_muxes = 1;
Matt Porter8b657272006-10-26 17:12:59 +02003257 spec->num_dmics = 0;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003258 spec->num_adcs = 1;
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003259 spec->num_pwrs = 0;
Mattc7d4b2f2005-06-27 14:59:41 +02003260
Tobin Davisbf277782008-02-03 20:31:47 +01003261 if (spec->board_config == STAC_9200_GATEWAY ||
3262 spec->board_config == STAC_9200_OQO)
Takashi Iwai1194b5b2007-10-10 10:04:26 +02003263 spec->init = stac9200_eapd_init;
3264 else
3265 spec->init = stac9200_core_init;
Matt2f2f4252005-04-13 14:45:30 +02003266 spec->mixer = stac9200_mixer;
Mattc7d4b2f2005-06-27 14:59:41 +02003267
3268 err = stac9200_parse_auto_config(codec);
3269 if (err < 0) {
3270 stac92xx_free(codec);
3271 return err;
3272 }
Matt2f2f4252005-04-13 14:45:30 +02003273
3274 codec->patch_ops = stac92xx_patch_ops;
3275
3276 return 0;
3277}
3278
Tobin Davis8e21c342007-01-08 11:04:17 +01003279static int patch_stac925x(struct hda_codec *codec)
3280{
3281 struct sigmatel_spec *spec;
3282 int err;
3283
3284 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3285 if (spec == NULL)
3286 return -ENOMEM;
3287
3288 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003289 spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
Tobin Davis8e21c342007-01-08 11:04:17 +01003290 spec->pin_nids = stac925x_pin_nids;
3291 spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
3292 stac925x_models,
3293 stac925x_cfg_tbl);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003294 again:
Tobin Davis8e21c342007-01-08 11:04:17 +01003295 if (spec->board_config < 0) {
Tobin Davis2c11f952007-05-17 09:36:34 +02003296 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
3297 "using BIOS defaults\n");
Tobin Davis8e21c342007-01-08 11:04:17 +01003298 err = stac92xx_save_bios_config_regs(codec);
3299 if (err < 0) {
3300 stac92xx_free(codec);
3301 return err;
3302 }
3303 spec->pin_configs = spec->bios_pin_configs;
3304 } else if (stac925x_brd_tbl[spec->board_config] != NULL){
3305 spec->pin_configs = stac925x_brd_tbl[spec->board_config];
3306 stac92xx_set_config_regs(codec);
3307 }
3308
3309 spec->multiout.max_channels = 2;
3310 spec->multiout.num_dacs = 1;
3311 spec->multiout.dac_nids = stac925x_dac_nids;
3312 spec->adc_nids = stac925x_adc_nids;
3313 spec->mux_nids = stac925x_mux_nids;
3314 spec->num_muxes = 1;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003315 spec->num_adcs = 1;
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003316 spec->num_pwrs = 0;
Tobin Davis2c11f952007-05-17 09:36:34 +02003317 switch (codec->vendor_id) {
3318 case 0x83847632: /* STAC9202 */
3319 case 0x83847633: /* STAC9202D */
3320 case 0x83847636: /* STAC9251 */
3321 case 0x83847637: /* STAC9251D */
Takashi Iwaif6e98522007-10-16 14:27:04 +02003322 spec->num_dmics = STAC925X_NUM_DMICS;
Tobin Davis2c11f952007-05-17 09:36:34 +02003323 spec->dmic_nids = stac925x_dmic_nids;
Takashi Iwai16970552007-12-18 18:05:52 +01003324 spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids);
3325 spec->dmux_nids = stac925x_dmux_nids;
Tobin Davis2c11f952007-05-17 09:36:34 +02003326 break;
3327 default:
3328 spec->num_dmics = 0;
3329 break;
3330 }
Tobin Davis8e21c342007-01-08 11:04:17 +01003331
3332 spec->init = stac925x_core_init;
3333 spec->mixer = stac925x_mixer;
3334
3335 err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003336 if (!err) {
3337 if (spec->board_config < 0) {
3338 printk(KERN_WARNING "hda_codec: No auto-config is "
3339 "available, default to model=ref\n");
3340 spec->board_config = STAC_925x_REF;
3341 goto again;
3342 }
3343 err = -EINVAL;
3344 }
Tobin Davis8e21c342007-01-08 11:04:17 +01003345 if (err < 0) {
3346 stac92xx_free(codec);
3347 return err;
3348 }
3349
3350 codec->patch_ops = stac92xx_patch_ops;
3351
3352 return 0;
3353}
3354
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003355static struct hda_input_mux stac92hd73xx_dmux = {
3356 .num_items = 4,
3357 .items = {
3358 { "Analog Inputs", 0x0b },
3359 { "CD", 0x08 },
3360 { "Digital Mic 1", 0x09 },
3361 { "Digital Mic 2", 0x0a },
3362 }
3363};
3364
3365static int patch_stac92hd73xx(struct hda_codec *codec)
3366{
3367 struct sigmatel_spec *spec;
3368 hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
3369 int err = 0;
3370
3371 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3372 if (spec == NULL)
3373 return -ENOMEM;
3374
3375 codec->spec = spec;
3376 spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
3377 spec->pin_nids = stac92hd73xx_pin_nids;
3378 spec->board_config = snd_hda_check_board_config(codec,
3379 STAC_92HD73XX_MODELS,
3380 stac92hd73xx_models,
3381 stac92hd73xx_cfg_tbl);
3382again:
3383 if (spec->board_config < 0) {
3384 snd_printdd(KERN_INFO "hda_codec: Unknown model for"
3385 " STAC92HD73XX, using BIOS defaults\n");
3386 err = stac92xx_save_bios_config_regs(codec);
3387 if (err < 0) {
3388 stac92xx_free(codec);
3389 return err;
3390 }
3391 spec->pin_configs = spec->bios_pin_configs;
3392 } else {
3393 spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config];
3394 stac92xx_set_config_regs(codec);
3395 }
3396
3397 spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
3398 conn, STAC92HD73_DAC_COUNT + 2) - 1;
3399
3400 if (spec->multiout.num_dacs < 0) {
3401 printk(KERN_WARNING "hda_codec: Could not determine "
3402 "number of channels defaulting to DAC count\n");
3403 spec->multiout.num_dacs = STAC92HD73_DAC_COUNT;
3404 }
3405
3406 switch (spec->multiout.num_dacs) {
3407 case 0x3: /* 6 Channel */
3408 spec->mixer = stac92hd73xx_6ch_mixer;
3409 spec->init = stac92hd73xx_6ch_core_init;
3410 break;
3411 case 0x4: /* 8 Channel */
3412 spec->multiout.hp_nid = 0x18;
3413 spec->mixer = stac92hd73xx_8ch_mixer;
3414 spec->init = stac92hd73xx_8ch_core_init;
3415 break;
3416 case 0x5: /* 10 Channel */
3417 spec->multiout.hp_nid = 0x19;
3418 spec->mixer = stac92hd73xx_10ch_mixer;
3419 spec->init = stac92hd73xx_10ch_core_init;
3420 };
3421
3422 spec->multiout.dac_nids = stac92hd73xx_dac_nids;
3423 spec->aloopback_mask = 0x01;
3424 spec->aloopback_shift = 8;
3425
3426 spec->mux_nids = stac92hd73xx_mux_nids;
3427 spec->adc_nids = stac92hd73xx_adc_nids;
3428 spec->dmic_nids = stac92hd73xx_dmic_nids;
3429 spec->dmux_nids = stac92hd73xx_dmux_nids;
3430
3431 spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
3432 spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
Takashi Iwai16970552007-12-18 18:05:52 +01003433 spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003434 spec->dinput_mux = &stac92hd73xx_dmux;
3435 /* GPIO0 High = Enable EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003436 spec->gpio_mask = spec->gpio_dir = 0x1;
3437 spec->gpio_data = 0x01;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003438
Matthew Ranostaya7662642008-02-21 07:51:14 +01003439 switch (spec->board_config) {
3440 case STAC_DELL_M6:
3441 switch (codec->subsystem_id) {
3442 case 0x1028025e: /* Analog Mics */
3443 case 0x1028025f:
3444 stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
3445 spec->num_dmics = 0;
3446 break;
3447 case 0x10280254: /* Digital Mics */
3448 case 0x10280255:
3449 case 0x10280271:
3450 case 0x10280272:
3451 stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
3452 spec->num_dmics = 1;
3453 break;
3454 case 0x10280256: /* Both */
3455 case 0x10280057:
3456 stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
3457 stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
3458 spec->num_dmics = 1;
3459 break;
3460 }
3461 break;
3462 default:
3463 spec->num_dmics = STAC92HD73XX_NUM_DMICS;
3464 }
3465
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003466 spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
3467 spec->pwr_nids = stac92hd73xx_pwr_nids;
3468
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003469 err = stac92xx_parse_auto_config(codec, 0x22, 0x24);
3470
3471 if (!err) {
3472 if (spec->board_config < 0) {
3473 printk(KERN_WARNING "hda_codec: No auto-config is "
3474 "available, default to model=ref\n");
3475 spec->board_config = STAC_92HD73XX_REF;
3476 goto again;
3477 }
3478 err = -EINVAL;
3479 }
3480
3481 if (err < 0) {
3482 stac92xx_free(codec);
3483 return err;
3484 }
3485
3486 codec->patch_ops = stac92xx_patch_ops;
3487
3488 return 0;
3489}
3490
Matthew Ranostaye035b842007-11-06 11:53:55 +01003491static int patch_stac92hd71bxx(struct hda_codec *codec)
3492{
3493 struct sigmatel_spec *spec;
3494 int err = 0;
3495
3496 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3497 if (spec == NULL)
3498 return -ENOMEM;
3499
3500 codec->spec = spec;
3501 spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
3502 spec->pin_nids = stac92hd71bxx_pin_nids;
3503 spec->board_config = snd_hda_check_board_config(codec,
3504 STAC_92HD71BXX_MODELS,
3505 stac92hd71bxx_models,
3506 stac92hd71bxx_cfg_tbl);
3507again:
3508 if (spec->board_config < 0) {
3509 snd_printdd(KERN_INFO "hda_codec: Unknown model for"
3510 " STAC92HD71BXX, using BIOS defaults\n");
3511 err = stac92xx_save_bios_config_regs(codec);
3512 if (err < 0) {
3513 stac92xx_free(codec);
3514 return err;
3515 }
3516 spec->pin_configs = spec->bios_pin_configs;
3517 } else {
3518 spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config];
3519 stac92xx_set_config_regs(codec);
3520 }
3521
Matthew Ranostay541eee82007-12-14 12:08:04 +01003522 switch (codec->vendor_id) {
3523 case 0x111d76b6: /* 4 Port without Analog Mixer */
3524 case 0x111d76b7:
3525 case 0x111d76b4: /* 6 Port without Analog Mixer */
3526 case 0x111d76b5:
3527 spec->mixer = stac92hd71bxx_mixer;
3528 spec->init = stac92hd71bxx_core_init;
3529 break;
3530 default:
3531 spec->mixer = stac92hd71bxx_analog_mixer;
3532 spec->init = stac92hd71bxx_analog_core_init;
3533 }
3534
3535 spec->aloopback_mask = 0x20;
3536 spec->aloopback_shift = 0;
3537
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003538 /* GPIO0 High = EAPD */
3539 spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1;
Matthew Ranostaye035b842007-11-06 11:53:55 +01003540
Matthew Ranostaye035b842007-11-06 11:53:55 +01003541 spec->mux_nids = stac92hd71bxx_mux_nids;
3542 spec->adc_nids = stac92hd71bxx_adc_nids;
3543 spec->dmic_nids = stac92hd71bxx_dmic_nids;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003544 spec->dmux_nids = stac92hd71bxx_dmux_nids;
Matthew Ranostaye035b842007-11-06 11:53:55 +01003545
3546 spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
3547 spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
3548 spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
Takashi Iwai16970552007-12-18 18:05:52 +01003549 spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
Matthew Ranostaye035b842007-11-06 11:53:55 +01003550
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003551 spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
3552 spec->pwr_nids = stac92hd71bxx_pwr_nids;
3553
Takashi Iwaiaea7bb02008-02-25 18:26:41 +01003554 spec->multiout.num_dacs = 1;
Matthew Ranostaye035b842007-11-06 11:53:55 +01003555 spec->multiout.hp_nid = 0x11;
3556 spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
3557
3558 err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
3559 if (!err) {
3560 if (spec->board_config < 0) {
3561 printk(KERN_WARNING "hda_codec: No auto-config is "
3562 "available, default to model=ref\n");
3563 spec->board_config = STAC_92HD71BXX_REF;
3564 goto again;
3565 }
3566 err = -EINVAL;
3567 }
3568
3569 if (err < 0) {
3570 stac92xx_free(codec);
3571 return err;
3572 }
3573
3574 codec->patch_ops = stac92xx_patch_ops;
3575
3576 return 0;
3577};
3578
Matt2f2f4252005-04-13 14:45:30 +02003579static int patch_stac922x(struct hda_codec *codec)
3580{
3581 struct sigmatel_spec *spec;
Mattc7d4b2f2005-06-27 14:59:41 +02003582 int err;
Matt2f2f4252005-04-13 14:45:30 +02003583
Takashi Iwaie560d8d2005-09-09 14:21:46 +02003584 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Matt2f2f4252005-04-13 14:45:30 +02003585 if (spec == NULL)
3586 return -ENOMEM;
3587
3588 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003589 spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02003590 spec->pin_nids = stac922x_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003591 spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
3592 stac922x_models,
3593 stac922x_cfg_tbl);
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003594 if (spec->board_config == STAC_INTEL_MAC_V3) {
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003595 spec->gpio_mask = spec->gpio_dir = 0x03;
3596 spec->gpio_data = 0x03;
Takashi Iwai3fc24d82007-02-16 13:27:18 +01003597 /* Intel Macs have all same PCI SSID, so we need to check
3598 * codec SSID to distinguish the exact models
3599 */
Nicolas Boichat6f0778d2007-03-15 12:38:15 +01003600 printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
Takashi Iwai3fc24d82007-02-16 13:27:18 +01003601 switch (codec->subsystem_id) {
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003602
3603 case 0x106b0800:
3604 spec->board_config = STAC_INTEL_MAC_V1;
Abhijit Bhopatkarc45e20e2007-04-17 11:57:16 +02003605 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003606 case 0x106b0600:
3607 case 0x106b0700:
3608 spec->board_config = STAC_INTEL_MAC_V2;
Nicolas Boichat6f0778d2007-03-15 12:38:15 +01003609 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003610 case 0x106b0e00:
3611 case 0x106b0f00:
3612 case 0x106b1600:
3613 case 0x106b1700:
3614 case 0x106b0200:
3615 case 0x106b1e00:
3616 spec->board_config = STAC_INTEL_MAC_V3;
Takashi Iwai3fc24d82007-02-16 13:27:18 +01003617 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003618 case 0x106b1a00:
3619 case 0x00000100:
3620 spec->board_config = STAC_INTEL_MAC_V4;
Sylvain FORETf16928f2007-04-27 14:22:36 +02003621 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003622 case 0x106b0a00:
3623 case 0x106b2200:
3624 spec->board_config = STAC_INTEL_MAC_V5;
Takashi Iwai0dae0f82007-05-21 12:41:29 +02003625 break;
Takashi Iwai3fc24d82007-02-16 13:27:18 +01003626 }
3627 }
3628
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003629 again:
Richard Fish11b44bb2006-08-23 18:31:34 +02003630 if (spec->board_config < 0) {
3631 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
3632 "using BIOS defaults\n");
3633 err = stac92xx_save_bios_config_regs(codec);
3634 if (err < 0) {
3635 stac92xx_free(codec);
3636 return err;
3637 }
3638 spec->pin_configs = spec->bios_pin_configs;
3639 } else if (stac922x_brd_tbl[spec->board_config] != NULL) {
Matt Porter403d1942005-11-29 15:00:51 +01003640 spec->pin_configs = stac922x_brd_tbl[spec->board_config];
3641 stac92xx_set_config_regs(codec);
3642 }
Matt2f2f4252005-04-13 14:45:30 +02003643
Matt2f2f4252005-04-13 14:45:30 +02003644 spec->adc_nids = stac922x_adc_nids;
3645 spec->mux_nids = stac922x_mux_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01003646 spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003647 spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
Matt Porter8b657272006-10-26 17:12:59 +02003648 spec->num_dmics = 0;
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003649 spec->num_pwrs = 0;
Mattc7d4b2f2005-06-27 14:59:41 +02003650
3651 spec->init = stac922x_core_init;
Matt2f2f4252005-04-13 14:45:30 +02003652 spec->mixer = stac922x_mixer;
Mattc7d4b2f2005-06-27 14:59:41 +02003653
3654 spec->multiout.dac_nids = spec->dac_nids;
Takashi Iwai19039bd2006-06-28 15:52:16 +02003655
Matt Porter3cc08dc2006-01-23 15:27:49 +01003656 err = stac92xx_parse_auto_config(codec, 0x08, 0x09);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003657 if (!err) {
3658 if (spec->board_config < 0) {
3659 printk(KERN_WARNING "hda_codec: No auto-config is "
3660 "available, default to model=ref\n");
3661 spec->board_config = STAC_D945_REF;
3662 goto again;
3663 }
3664 err = -EINVAL;
3665 }
Matt Porter3cc08dc2006-01-23 15:27:49 +01003666 if (err < 0) {
3667 stac92xx_free(codec);
3668 return err;
3669 }
3670
3671 codec->patch_ops = stac92xx_patch_ops;
3672
Takashi Iwai807a46362007-05-29 19:01:37 +02003673 /* Fix Mux capture level; max to 2 */
3674 snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
3675 (0 << AC_AMPCAP_OFFSET_SHIFT) |
3676 (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3677 (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3678 (0 << AC_AMPCAP_MUTE_SHIFT));
3679
Matt Porter3cc08dc2006-01-23 15:27:49 +01003680 return 0;
3681}
3682
3683static int patch_stac927x(struct hda_codec *codec)
3684{
3685 struct sigmatel_spec *spec;
3686 int err;
3687
3688 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3689 if (spec == NULL)
3690 return -ENOMEM;
3691
3692 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003693 spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02003694 spec->pin_nids = stac927x_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003695 spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
3696 stac927x_models,
3697 stac927x_cfg_tbl);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003698 again:
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003699 if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) {
3700 if (spec->board_config < 0)
3701 snd_printdd(KERN_INFO "hda_codec: Unknown model for"
3702 "STAC927x, using BIOS defaults\n");
Richard Fish11b44bb2006-08-23 18:31:34 +02003703 err = stac92xx_save_bios_config_regs(codec);
3704 if (err < 0) {
3705 stac92xx_free(codec);
3706 return err;
3707 }
3708 spec->pin_configs = spec->bios_pin_configs;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003709 } else {
Matt Porter3cc08dc2006-01-23 15:27:49 +01003710 spec->pin_configs = stac927x_brd_tbl[spec->board_config];
3711 stac92xx_set_config_regs(codec);
3712 }
3713
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003714 spec->adc_nids = stac927x_adc_nids;
3715 spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
3716 spec->mux_nids = stac927x_mux_nids;
3717 spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
Matthew Ranostayb76c8502008-02-06 14:49:44 +01003718 spec->dac_list = stac927x_dac_nids;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003719 spec->multiout.dac_nids = spec->dac_nids;
3720
Tobin Davis81d3dbd2006-08-22 19:44:45 +02003721 switch (spec->board_config) {
Tobin Davis93ed1502006-09-01 21:03:12 +02003722 case STAC_D965_3ST:
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003723 case STAC_D965_5ST:
3724 /* GPIO0 High = Enable EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003725 spec->gpio_mask = spec->gpio_dir = 0x01;
3726 spec->gpio_data = 0x01;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003727 spec->num_dmics = 0;
3728
Tobin Davis93ed1502006-09-01 21:03:12 +02003729 spec->init = d965_core_init;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003730 spec->mixer = stac927x_mixer;
Tobin Davis81d3dbd2006-08-22 19:44:45 +02003731 break;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003732 case STAC_DELL_BIOS:
Matthew Ranostay03d7ca12008-02-21 07:51:46 +01003733 /* configure the analog microphone on some laptops */
3734 stac92xx_set_config_reg(codec, 0x0c, 0x90a79130);
Matthew Ranostay2f32d902008-01-10 13:06:26 +01003735 /* correct the front output jack as a hp out */
Matthew Ranostay7989fba2008-02-21 07:50:12 +01003736 stac92xx_set_config_reg(codec, 0x0f, 0x0227011f);
Matthew Ranostayc481fca2008-01-07 12:18:28 +01003737 /* correct the front input jack as a mic */
3738 stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
3739 /* fallthru */
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003740 case STAC_DELL_3ST:
3741 /* GPIO2 High = Enable EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003742 spec->gpio_mask = spec->gpio_dir = 0x04;
3743 spec->gpio_data = 0x04;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003744 spec->dmic_nids = stac927x_dmic_nids;
3745 spec->num_dmics = STAC927X_NUM_DMICS;
3746
Tobin Davis93ed1502006-09-01 21:03:12 +02003747 spec->init = d965_core_init;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003748 spec->mixer = stac927x_mixer;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003749 spec->dmux_nids = stac927x_dmux_nids;
Takashi Iwai16970552007-12-18 18:05:52 +01003750 spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
Tobin Davis81d3dbd2006-08-22 19:44:45 +02003751 break;
3752 default:
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003753 /* GPIO0 High = Enable EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003754 spec->gpio_mask = spec->gpio_dir = 0x1;
3755 spec->gpio_data = 0x01;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003756 spec->num_dmics = 0;
3757
Tobin Davis81d3dbd2006-08-22 19:44:45 +02003758 spec->init = stac927x_core_init;
3759 spec->mixer = stac927x_mixer;
3760 }
Matt Porter3cc08dc2006-01-23 15:27:49 +01003761
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003762 spec->num_pwrs = 0;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003763 spec->aloopback_mask = 0x40;
3764 spec->aloopback_shift = 0;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003765
Matt Porter3cc08dc2006-01-23 15:27:49 +01003766 err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003767 if (!err) {
3768 if (spec->board_config < 0) {
3769 printk(KERN_WARNING "hda_codec: No auto-config is "
3770 "available, default to model=ref\n");
3771 spec->board_config = STAC_D965_REF;
3772 goto again;
3773 }
3774 err = -EINVAL;
3775 }
Mattc7d4b2f2005-06-27 14:59:41 +02003776 if (err < 0) {
3777 stac92xx_free(codec);
3778 return err;
3779 }
Matt2f2f4252005-04-13 14:45:30 +02003780
3781 codec->patch_ops = stac92xx_patch_ops;
3782
Takashi Iwai52987652008-01-16 16:09:47 +01003783 /*
3784 * !!FIXME!!
3785 * The STAC927x seem to require fairly long delays for certain
3786 * command sequences. With too short delays (even if the answer
3787 * is set to RIRB properly), it results in the silence output
3788 * on some hardwares like Dell.
3789 *
3790 * The below flag enables the longer delay (see get_response
3791 * in hda_intel.c).
3792 */
3793 codec->bus->needs_damn_long_delay = 1;
3794
Matt2f2f4252005-04-13 14:45:30 +02003795 return 0;
3796}
3797
Matt Porterf3302a52006-07-31 12:49:34 +02003798static int patch_stac9205(struct hda_codec *codec)
3799{
3800 struct sigmatel_spec *spec;
Takashi Iwai82599802007-07-31 15:56:24 +02003801 int err;
Matt Porterf3302a52006-07-31 12:49:34 +02003802
3803 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3804 if (spec == NULL)
3805 return -ENOMEM;
3806
3807 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003808 spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02003809 spec->pin_nids = stac9205_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003810 spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
3811 stac9205_models,
3812 stac9205_cfg_tbl);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003813 again:
Richard Fish11b44bb2006-08-23 18:31:34 +02003814 if (spec->board_config < 0) {
3815 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
3816 err = stac92xx_save_bios_config_regs(codec);
3817 if (err < 0) {
3818 stac92xx_free(codec);
3819 return err;
3820 }
3821 spec->pin_configs = spec->bios_pin_configs;
3822 } else {
Matt Porterf3302a52006-07-31 12:49:34 +02003823 spec->pin_configs = stac9205_brd_tbl[spec->board_config];
3824 stac92xx_set_config_regs(codec);
3825 }
3826
3827 spec->adc_nids = stac9205_adc_nids;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003828 spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids);
Matt Porterf3302a52006-07-31 12:49:34 +02003829 spec->mux_nids = stac9205_mux_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01003830 spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
Matt Porter8b657272006-10-26 17:12:59 +02003831 spec->dmic_nids = stac9205_dmic_nids;
Takashi Iwaif6e98522007-10-16 14:27:04 +02003832 spec->num_dmics = STAC9205_NUM_DMICS;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003833 spec->dmux_nids = stac9205_dmux_nids;
Takashi Iwai16970552007-12-18 18:05:52 +01003834 spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003835 spec->num_pwrs = 0;
Matt Porterf3302a52006-07-31 12:49:34 +02003836
3837 spec->init = stac9205_core_init;
3838 spec->mixer = stac9205_mixer;
3839
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003840 spec->aloopback_mask = 0x40;
3841 spec->aloopback_shift = 0;
Matt Porterf3302a52006-07-31 12:49:34 +02003842 spec->multiout.dac_nids = spec->dac_nids;
Matthew Ranostay87d48362007-07-17 11:52:24 +02003843
Tobin Davisae0a8ed2007-08-13 15:50:29 +02003844 switch (spec->board_config){
Tobin Davisae0a8ed2007-08-13 15:50:29 +02003845 case STAC_9205_DELL_M43:
Matthew Ranostay87d48362007-07-17 11:52:24 +02003846 /* Enable SPDIF in/out */
3847 stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
3848 stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
Matt Porter33382402006-12-18 13:17:28 +01003849
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003850 /* Enable unsol response for GPIO4/Dock HP connection */
3851 snd_hda_codec_write(codec, codec->afg, 0,
3852 AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
3853 snd_hda_codec_write_cache(codec, codec->afg, 0,
3854 AC_VERB_SET_UNSOLICITED_ENABLE,
3855 (AC_USRSP_EN | STAC_HP_EVENT));
3856
3857 spec->gpio_dir = 0x0b;
3858 spec->gpio_mask = 0x1b;
3859 spec->gpio_mute = 0x10;
Matthew Ranostaye2e7d622008-01-24 15:32:15 +01003860 /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003861 * GPIO3 Low = DRM
Matthew Ranostay87d48362007-07-17 11:52:24 +02003862 */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003863 spec->gpio_data = 0x01;
Tobin Davisae0a8ed2007-08-13 15:50:29 +02003864 break;
3865 default:
3866 /* GPIO0 High = EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003867 spec->gpio_mask = spec->gpio_dir = 0x1;
3868 spec->gpio_data = 0x01;
Tobin Davisae0a8ed2007-08-13 15:50:29 +02003869 break;
3870 }
Matthew Ranostay87d48362007-07-17 11:52:24 +02003871
Matt Porterf3302a52006-07-31 12:49:34 +02003872 err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003873 if (!err) {
3874 if (spec->board_config < 0) {
3875 printk(KERN_WARNING "hda_codec: No auto-config is "
3876 "available, default to model=ref\n");
3877 spec->board_config = STAC_9205_REF;
3878 goto again;
3879 }
3880 err = -EINVAL;
3881 }
Matt Porterf3302a52006-07-31 12:49:34 +02003882 if (err < 0) {
3883 stac92xx_free(codec);
3884 return err;
3885 }
3886
3887 codec->patch_ops = stac92xx_patch_ops;
3888
3889 return 0;
3890}
3891
Matt2f2f4252005-04-13 14:45:30 +02003892/*
Guillaume Munch6d859062006-08-22 17:15:47 +02003893 * STAC9872 hack
Takashi Iwaidb064e52006-03-16 16:04:58 +01003894 */
3895
Guillaume Munch99ccc562006-08-16 19:35:12 +02003896/* static config for Sony VAIO FE550G and Sony VAIO AR */
Takashi Iwaidb064e52006-03-16 16:04:58 +01003897static hda_nid_t vaio_dacs[] = { 0x2 };
3898#define VAIO_HP_DAC 0x5
3899static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ };
3900static hda_nid_t vaio_mux_nids[] = { 0x15 };
3901
3902static struct hda_input_mux vaio_mux = {
Takashi Iwaia3a2f422007-10-11 11:21:21 +02003903 .num_items = 3,
Takashi Iwaidb064e52006-03-16 16:04:58 +01003904 .items = {
Takashi Iwaid7737812006-04-25 13:05:43 +02003905 /* { "HP", 0x0 }, */
Takashi Iwai1624cb92007-07-05 13:10:51 +02003906 { "Mic Jack", 0x1 },
3907 { "Internal Mic", 0x2 },
Takashi Iwaidb064e52006-03-16 16:04:58 +01003908 { "PCM", 0x3 },
3909 }
3910};
3911
3912static struct hda_verb vaio_init[] = {
3913 {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02003914 {0x0a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | STAC_HP_EVENT},
Takashi Iwaidb064e52006-03-16 16:04:58 +01003915 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
3916 {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
3917 {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
3918 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
Takashi Iwai1624cb92007-07-05 13:10:51 +02003919 {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
Takashi Iwaidb064e52006-03-16 16:04:58 +01003920 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
3921 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
3922 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
3923 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
3924 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
3925 {}
3926};
3927
Guillaume Munch6d859062006-08-22 17:15:47 +02003928static struct hda_verb vaio_ar_init[] = {
3929 {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
3930 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
3931 {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
3932 {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
3933/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
3934 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
Takashi Iwai1624cb92007-07-05 13:10:51 +02003935 {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
Guillaume Munch6d859062006-08-22 17:15:47 +02003936 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
3937 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
3938/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
3939 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
3940 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
3941 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
3942 {}
3943};
3944
Takashi Iwaidb064e52006-03-16 16:04:58 +01003945/* bind volumes of both NID 0x02 and 0x05 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02003946static struct hda_bind_ctls vaio_bind_master_vol = {
3947 .ops = &snd_hda_bind_vol,
3948 .values = {
3949 HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
3950 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
3951 0
3952 },
3953};
Takashi Iwaidb064e52006-03-16 16:04:58 +01003954
3955/* bind volumes of both NID 0x02 and 0x05 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02003956static struct hda_bind_ctls vaio_bind_master_sw = {
3957 .ops = &snd_hda_bind_sw,
3958 .values = {
3959 HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
3960 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
3961 0,
3962 },
3963};
Takashi Iwaidb064e52006-03-16 16:04:58 +01003964
3965static struct snd_kcontrol_new vaio_mixer[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02003966 HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
3967 HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
Takashi Iwaidb064e52006-03-16 16:04:58 +01003968 /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
3969 HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
3970 HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
3971 {
3972 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3973 .name = "Capture Source",
3974 .count = 1,
3975 .info = stac92xx_mux_enum_info,
3976 .get = stac92xx_mux_enum_get,
3977 .put = stac92xx_mux_enum_put,
3978 },
3979 {}
3980};
3981
Guillaume Munch6d859062006-08-22 17:15:47 +02003982static struct snd_kcontrol_new vaio_ar_mixer[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02003983 HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
3984 HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
Guillaume Munch6d859062006-08-22 17:15:47 +02003985 /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
3986 HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
3987 HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
3988 /*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT),
3989 HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/
3990 {
3991 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3992 .name = "Capture Source",
3993 .count = 1,
3994 .info = stac92xx_mux_enum_info,
3995 .get = stac92xx_mux_enum_get,
3996 .put = stac92xx_mux_enum_put,
3997 },
3998 {}
3999};
4000
4001static struct hda_codec_ops stac9872_patch_ops = {
Takashi Iwaidb064e52006-03-16 16:04:58 +01004002 .build_controls = stac92xx_build_controls,
4003 .build_pcms = stac92xx_build_pcms,
4004 .init = stac92xx_init,
4005 .free = stac92xx_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +02004006#ifdef SND_HDA_NEEDS_RESUME
Takashi Iwaidb064e52006-03-16 16:04:58 +01004007 .resume = stac92xx_resume,
4008#endif
4009};
4010
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02004011static int stac9872_vaio_init(struct hda_codec *codec)
4012{
4013 int err;
4014
4015 err = stac92xx_init(codec);
4016 if (err < 0)
4017 return err;
4018 if (codec->patch_ops.unsol_event)
4019 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
4020 return 0;
4021}
4022
4023static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
4024{
Jiang Zhe40c1d302007-11-12 13:05:16 +01004025 if (get_hp_pin_presence(codec, 0x0a)) {
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02004026 stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
4027 stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
4028 } else {
4029 stac92xx_reset_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
4030 stac92xx_set_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
4031 }
4032}
4033
4034static void stac9872_vaio_unsol_event(struct hda_codec *codec, unsigned int res)
4035{
4036 switch (res >> 26) {
4037 case STAC_HP_EVENT:
4038 stac9872_vaio_hp_detect(codec, res);
4039 break;
4040 }
4041}
4042
4043static struct hda_codec_ops stac9872_vaio_patch_ops = {
4044 .build_controls = stac92xx_build_controls,
4045 .build_pcms = stac92xx_build_pcms,
4046 .init = stac9872_vaio_init,
4047 .free = stac92xx_free,
4048 .unsol_event = stac9872_vaio_unsol_event,
4049#ifdef CONFIG_PM
4050 .resume = stac92xx_resume,
4051#endif
4052};
4053
Guillaume Munch6d859062006-08-22 17:15:47 +02004054enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */
4055 CXD9872RD_VAIO,
4056 /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */
4057 STAC9872AK_VAIO,
4058 /* Unknown. id=0x83847661 and subsys=0x104D1200. */
4059 STAC9872K_VAIO,
4060 /* AR Series. id=0x83847664 and subsys=104D1300 */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01004061 CXD9872AKD_VAIO,
4062 STAC_9872_MODELS,
4063};
Takashi Iwaidb064e52006-03-16 16:04:58 +01004064
Takashi Iwaif5fcc132006-11-24 17:07:44 +01004065static const char *stac9872_models[STAC_9872_MODELS] = {
4066 [CXD9872RD_VAIO] = "vaio",
4067 [CXD9872AKD_VAIO] = "vaio-ar",
4068};
4069
4070static struct snd_pci_quirk stac9872_cfg_tbl[] = {
4071 SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO),
4072 SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO),
4073 SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO),
Tobin Davis68e22542007-03-12 11:36:39 +01004074 SND_PCI_QUIRK(0x104d, 0x8205, "Sony VAIO AR", CXD9872AKD_VAIO),
Takashi Iwaidb064e52006-03-16 16:04:58 +01004075 {}
4076};
4077
Guillaume Munch6d859062006-08-22 17:15:47 +02004078static int patch_stac9872(struct hda_codec *codec)
Takashi Iwaidb064e52006-03-16 16:04:58 +01004079{
4080 struct sigmatel_spec *spec;
4081 int board_config;
4082
Takashi Iwaif5fcc132006-11-24 17:07:44 +01004083 board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
4084 stac9872_models,
4085 stac9872_cfg_tbl);
Takashi Iwaidb064e52006-03-16 16:04:58 +01004086 if (board_config < 0)
4087 /* unknown config, let generic-parser do its job... */
4088 return snd_hda_parse_generic_codec(codec);
4089
4090 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4091 if (spec == NULL)
4092 return -ENOMEM;
4093
4094 codec->spec = spec;
4095 switch (board_config) {
Guillaume Munch6d859062006-08-22 17:15:47 +02004096 case CXD9872RD_VAIO:
4097 case STAC9872AK_VAIO:
4098 case STAC9872K_VAIO:
Takashi Iwaidb064e52006-03-16 16:04:58 +01004099 spec->mixer = vaio_mixer;
4100 spec->init = vaio_init;
4101 spec->multiout.max_channels = 2;
4102 spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
4103 spec->multiout.dac_nids = vaio_dacs;
4104 spec->multiout.hp_nid = VAIO_HP_DAC;
4105 spec->num_adcs = ARRAY_SIZE(vaio_adcs);
4106 spec->adc_nids = vaio_adcs;
Matthew Ranostaya64135a2008-01-10 16:55:06 +01004107 spec->num_pwrs = 0;
Takashi Iwaidb064e52006-03-16 16:04:58 +01004108 spec->input_mux = &vaio_mux;
4109 spec->mux_nids = vaio_mux_nids;
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02004110 codec->patch_ops = stac9872_vaio_patch_ops;
Takashi Iwaidb064e52006-03-16 16:04:58 +01004111 break;
Guillaume Munch6d859062006-08-22 17:15:47 +02004112
4113 case CXD9872AKD_VAIO:
4114 spec->mixer = vaio_ar_mixer;
4115 spec->init = vaio_ar_init;
4116 spec->multiout.max_channels = 2;
4117 spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
4118 spec->multiout.dac_nids = vaio_dacs;
4119 spec->multiout.hp_nid = VAIO_HP_DAC;
4120 spec->num_adcs = ARRAY_SIZE(vaio_adcs);
Matthew Ranostaya64135a2008-01-10 16:55:06 +01004121 spec->num_pwrs = 0;
Guillaume Munch6d859062006-08-22 17:15:47 +02004122 spec->adc_nids = vaio_adcs;
4123 spec->input_mux = &vaio_mux;
4124 spec->mux_nids = vaio_mux_nids;
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02004125 codec->patch_ops = stac9872_patch_ops;
Guillaume Munch6d859062006-08-22 17:15:47 +02004126 break;
Takashi Iwaidb064e52006-03-16 16:04:58 +01004127 }
4128
Takashi Iwaidb064e52006-03-16 16:04:58 +01004129 return 0;
4130}
4131
4132
4133/*
Matt2f2f4252005-04-13 14:45:30 +02004134 * patch entries
4135 */
4136struct hda_codec_preset snd_hda_preset_sigmatel[] = {
4137 { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
4138 { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
4139 { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
4140 { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
4141 { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
4142 { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
4143 { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
Matt Porter22a27c72006-07-06 18:49:10 +02004144 { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
4145 { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
4146 { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
4147 { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
4148 { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
4149 { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
Matt Porter3cc08dc2006-01-23 15:27:49 +01004150 { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
4151 { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
4152 { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
4153 { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
4154 { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
4155 { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
4156 { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
4157 { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
4158 { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
4159 { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
Tobin Davis8e21c342007-01-08 11:04:17 +01004160 { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
4161 { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
4162 { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
4163 { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
4164 { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
4165 { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
Guillaume Munch6d859062006-08-22 17:15:47 +02004166 /* The following does not take into account .id=0x83847661 when subsys =
4167 * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
4168 * currently not fully supported.
4169 */
4170 { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
4171 { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
4172 { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
Matt Porterf3302a52006-07-31 12:49:34 +02004173 { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
4174 { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
4175 { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
4176 { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
4177 { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
4178 { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
4179 { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
4180 { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
Matthew Ranostay541eee82007-12-14 12:08:04 +01004181 { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
4182 { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01004183 { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
Matthew Ranostay541eee82007-12-14 12:08:04 +01004184 { .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx },
4185 { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
4186 { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
4187 { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
4188 { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
4189 { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
4190 { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
4191 { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
4192 { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
Matt2f2f4252005-04-13 14:45:30 +02004193 {} /* terminator */
4194};