blob: 70c56945975869580ba2cdfd0c1275f7e7ca16be [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"
35
Matt4e550962005-07-04 17:51:39 +020036#define NUM_CONTROL_ALLOC 32
Matthew Ranostaya64135a2008-01-10 16:55:06 +010037#define STAC_PWR_EVENT 0x20
38#define STAC_HP_EVENT 0x30
Matt4e550962005-07-04 17:51:39 +020039
Takashi Iwaif5fcc132006-11-24 17:07:44 +010040enum {
41 STAC_REF,
Tobin Davisbf277782008-02-03 20:31:47 +010042 STAC_9200_OQO,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020043 STAC_9200_DELL_D21,
44 STAC_9200_DELL_D22,
45 STAC_9200_DELL_D23,
46 STAC_9200_DELL_M21,
47 STAC_9200_DELL_M22,
48 STAC_9200_DELL_M23,
49 STAC_9200_DELL_M24,
50 STAC_9200_DELL_M25,
51 STAC_9200_DELL_M26,
52 STAC_9200_DELL_M27,
Takashi Iwai1194b5b2007-10-10 10:04:26 +020053 STAC_9200_GATEWAY,
Takashi Iwaif5fcc132006-11-24 17:07:44 +010054 STAC_9200_MODELS
55};
56
57enum {
58 STAC_9205_REF,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020059 STAC_9205_DELL_M42,
Tobin Davisae0a8ed2007-08-13 15:50:29 +020060 STAC_9205_DELL_M43,
61 STAC_9205_DELL_M44,
Takashi Iwaif5fcc132006-11-24 17:07:44 +010062 STAC_9205_MODELS
63};
64
65enum {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +010066 STAC_92HD73XX_REF,
67 STAC_92HD73XX_MODELS
68};
69
70enum {
Matthew Ranostaye035b842007-11-06 11:53:55 +010071 STAC_92HD71BXX_REF,
72 STAC_92HD71BXX_MODELS
73};
74
75enum {
Tobin Davis8e21c342007-01-08 11:04:17 +010076 STAC_925x_REF,
77 STAC_M2_2,
78 STAC_MA6,
Tobin Davis2c11f952007-05-17 09:36:34 +020079 STAC_PA6,
Tobin Davis8e21c342007-01-08 11:04:17 +010080 STAC_925x_MODELS
81};
82
83enum {
Takashi Iwaif5fcc132006-11-24 17:07:44 +010084 STAC_D945_REF,
85 STAC_D945GTP3,
86 STAC_D945GTP5,
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +020087 STAC_INTEL_MAC_V1,
88 STAC_INTEL_MAC_V2,
89 STAC_INTEL_MAC_V3,
90 STAC_INTEL_MAC_V4,
91 STAC_INTEL_MAC_V5,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020092 /* for backward compatibility */
Takashi Iwaif5fcc132006-11-24 17:07:44 +010093 STAC_MACMINI,
Takashi Iwai3fc24d82007-02-16 13:27:18 +010094 STAC_MACBOOK,
Nicolas Boichat6f0778d2007-03-15 12:38:15 +010095 STAC_MACBOOK_PRO_V1,
96 STAC_MACBOOK_PRO_V2,
Sylvain FORETf16928f2007-04-27 14:22:36 +020097 STAC_IMAC_INTEL,
Takashi Iwai0dae0f82007-05-21 12:41:29 +020098 STAC_IMAC_INTEL_20,
Takashi Iwaidfe495d2007-08-23 19:04:28 +020099 STAC_922X_DELL_D81,
100 STAC_922X_DELL_D82,
101 STAC_922X_DELL_M81,
102 STAC_922X_DELL_M82,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100103 STAC_922X_MODELS
104};
105
106enum {
107 STAC_D965_REF,
108 STAC_D965_3ST,
109 STAC_D965_5ST,
Tobin Davis4ff076e2007-08-07 11:48:12 +0200110 STAC_DELL_3ST,
Matthew Ranostay8e9068b2007-12-17 11:58:13 +0100111 STAC_DELL_BIOS,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100112 STAC_927X_MODELS
113};
Matt Porter403d1942005-11-29 15:00:51 +0100114
Matt2f2f4252005-04-13 14:45:30 +0200115struct sigmatel_spec {
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100116 struct snd_kcontrol_new *mixers[4];
Mattc7d4b2f2005-06-27 14:59:41 +0200117 unsigned int num_mixers;
118
Matt Porter403d1942005-11-29 15:00:51 +0100119 int board_config;
Mattc7d4b2f2005-06-27 14:59:41 +0200120 unsigned int surr_switch: 1;
Matt Porter403d1942005-11-29 15:00:51 +0100121 unsigned int line_switch: 1;
122 unsigned int mic_switch: 1;
Matt Porter3cc08dc2006-01-23 15:27:49 +0100123 unsigned int alt_switch: 1;
Takashi Iwai82bc9552006-03-21 11:24:42 +0100124 unsigned int hp_detect: 1;
Mattc7d4b2f2005-06-27 14:59:41 +0200125
Matthew Ranostay4fe51952008-01-29 15:28:44 +0100126 /* gpio lines */
127 unsigned int gpio_mask;
128 unsigned int gpio_dir;
129 unsigned int gpio_data;
130 unsigned int gpio_mute;
131
132 /* analog loopback */
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100133 unsigned char aloopback_mask;
134 unsigned char aloopback_shift;
Takashi Iwai82599802007-07-31 15:56:24 +0200135
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100136 /* power management */
137 unsigned int num_pwrs;
138 hda_nid_t *pwr_nids;
Matthew Ranostayb76c8502008-02-06 14:49:44 +0100139 hda_nid_t *dac_list;
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100140
Matt2f2f4252005-04-13 14:45:30 +0200141 /* playback */
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100142 struct hda_input_mux *mono_mux;
143 unsigned int cur_mmux;
Matt2f2f4252005-04-13 14:45:30 +0200144 struct hda_multi_out multiout;
Matt Porter3cc08dc2006-01-23 15:27:49 +0100145 hda_nid_t dac_nids[5];
Matt2f2f4252005-04-13 14:45:30 +0200146
147 /* capture */
148 hda_nid_t *adc_nids;
Matt2f2f4252005-04-13 14:45:30 +0200149 unsigned int num_adcs;
Mattdabbed62005-06-14 10:19:34 +0200150 hda_nid_t *mux_nids;
151 unsigned int num_muxes;
Matt Porter8b657272006-10-26 17:12:59 +0200152 hda_nid_t *dmic_nids;
153 unsigned int num_dmics;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100154 hda_nid_t *dmux_nids;
Takashi Iwai1697055e2007-12-18 18:05:52 +0100155 unsigned int num_dmuxes;
Mattdabbed62005-06-14 10:19:34 +0200156 hda_nid_t dig_in_nid;
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100157 hda_nid_t mono_nid;
Matt2f2f4252005-04-13 14:45:30 +0200158
Matt2f2f4252005-04-13 14:45:30 +0200159 /* pin widgets */
160 hda_nid_t *pin_nids;
161 unsigned int num_pins;
Matt2f2f4252005-04-13 14:45:30 +0200162 unsigned int *pin_configs;
Richard Fish11b44bb2006-08-23 18:31:34 +0200163 unsigned int *bios_pin_configs;
Matt2f2f4252005-04-13 14:45:30 +0200164
165 /* codec specific stuff */
166 struct hda_verb *init;
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100167 struct snd_kcontrol_new *mixer;
Matt2f2f4252005-04-13 14:45:30 +0200168
169 /* capture source */
Matt Porter8b657272006-10-26 17:12:59 +0200170 struct hda_input_mux *dinput_mux;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100171 unsigned int cur_dmux[2];
Mattc7d4b2f2005-06-27 14:59:41 +0200172 struct hda_input_mux *input_mux;
Matt Porter3cc08dc2006-01-23 15:27:49 +0100173 unsigned int cur_mux[3];
Matt2f2f4252005-04-13 14:45:30 +0200174
Matt Porter403d1942005-11-29 15:00:51 +0100175 /* i/o switches */
176 unsigned int io_switch[2];
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +0200177 unsigned int clfe_swap;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200178 unsigned int aloopback;
Matt2f2f4252005-04-13 14:45:30 +0200179
Mattc7d4b2f2005-06-27 14:59:41 +0200180 struct hda_pcm pcm_rec[2]; /* PCM information */
181
182 /* dynamic controls and input_mux */
183 struct auto_pin_cfg autocfg;
184 unsigned int num_kctl_alloc, num_kctl_used;
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100185 struct snd_kcontrol_new *kctl_alloc;
Matt Porter8b657272006-10-26 17:12:59 +0200186 struct hda_input_mux private_dimux;
Mattc7d4b2f2005-06-27 14:59:41 +0200187 struct hda_input_mux private_imux;
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100188 struct hda_input_mux private_mono_mux;
Matt2f2f4252005-04-13 14:45:30 +0200189};
190
191static hda_nid_t stac9200_adc_nids[1] = {
192 0x03,
193};
194
195static hda_nid_t stac9200_mux_nids[1] = {
196 0x0c,
197};
198
199static hda_nid_t stac9200_dac_nids[1] = {
200 0x02,
201};
202
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100203static hda_nid_t stac92hd73xx_pwr_nids[8] = {
204 0x0a, 0x0b, 0x0c, 0xd, 0x0e,
205 0x0f, 0x10, 0x11
206};
207
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100208static hda_nid_t stac92hd73xx_adc_nids[2] = {
209 0x1a, 0x1b
210};
211
212#define STAC92HD73XX_NUM_DMICS 2
213static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
214 0x13, 0x14, 0
215};
216
217#define STAC92HD73_DAC_COUNT 5
218static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = {
219 0x15, 0x16, 0x17, 0x18, 0x19,
220};
221
222static hda_nid_t stac92hd73xx_mux_nids[4] = {
223 0x28, 0x29, 0x2a, 0x2b,
224};
225
226static hda_nid_t stac92hd73xx_dmux_nids[2] = {
227 0x20, 0x21,
228};
229
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100230static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
231 0x0a, 0x0d, 0x0f
232};
233
Matthew Ranostaye035b842007-11-06 11:53:55 +0100234static hda_nid_t stac92hd71bxx_adc_nids[2] = {
235 0x12, 0x13,
236};
237
238static hda_nid_t stac92hd71bxx_mux_nids[2] = {
239 0x1a, 0x1b
240};
241
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100242static hda_nid_t stac92hd71bxx_dmux_nids[1] = {
243 0x1c,
244};
245
Matthew Ranostaye035b842007-11-06 11:53:55 +0100246static hda_nid_t stac92hd71bxx_dac_nids[2] = {
247 0x10, /*0x11, */
248};
249
250#define STAC92HD71BXX_NUM_DMICS 2
251static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
252 0x18, 0x19, 0
253};
254
Tobin Davis8e21c342007-01-08 11:04:17 +0100255static hda_nid_t stac925x_adc_nids[1] = {
256 0x03,
257};
258
259static hda_nid_t stac925x_mux_nids[1] = {
260 0x0f,
261};
262
263static hda_nid_t stac925x_dac_nids[1] = {
264 0x02,
265};
266
Takashi Iwaif6e98522007-10-16 14:27:04 +0200267#define STAC925X_NUM_DMICS 1
268static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = {
269 0x15, 0
Tobin Davis2c11f952007-05-17 09:36:34 +0200270};
271
Takashi Iwai1697055e2007-12-18 18:05:52 +0100272static hda_nid_t stac925x_dmux_nids[1] = {
273 0x14,
274};
275
Matt2f2f4252005-04-13 14:45:30 +0200276static hda_nid_t stac922x_adc_nids[2] = {
277 0x06, 0x07,
278};
279
280static hda_nid_t stac922x_mux_nids[2] = {
281 0x12, 0x13,
282};
283
Matt Porter3cc08dc2006-01-23 15:27:49 +0100284static hda_nid_t stac927x_adc_nids[3] = {
285 0x07, 0x08, 0x09
286};
287
288static hda_nid_t stac927x_mux_nids[3] = {
289 0x15, 0x16, 0x17
290};
291
Matthew Ranostayb76c8502008-02-06 14:49:44 +0100292static hda_nid_t stac927x_dac_nids[6] = {
293 0x02, 0x03, 0x04, 0x05, 0x06, 0
294};
295
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100296static hda_nid_t stac927x_dmux_nids[1] = {
297 0x1b,
298};
299
Matthew Ranostay7f168592007-10-18 17:38:17 +0200300#define STAC927X_NUM_DMICS 2
301static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
302 0x13, 0x14, 0
303};
304
Matt Porterf3302a52006-07-31 12:49:34 +0200305static hda_nid_t stac9205_adc_nids[2] = {
306 0x12, 0x13
307};
308
309static hda_nid_t stac9205_mux_nids[2] = {
310 0x19, 0x1a
311};
312
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100313static hda_nid_t stac9205_dmux_nids[1] = {
Takashi Iwai1697055e2007-12-18 18:05:52 +0100314 0x1d,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100315};
316
Takashi Iwaif6e98522007-10-16 14:27:04 +0200317#define STAC9205_NUM_DMICS 2
318static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
319 0x17, 0x18, 0
Matt Porter8b657272006-10-26 17:12:59 +0200320};
321
Mattc7d4b2f2005-06-27 14:59:41 +0200322static hda_nid_t stac9200_pin_nids[8] = {
Tobin Davis93ed1502006-09-01 21:03:12 +0200323 0x08, 0x09, 0x0d, 0x0e,
324 0x0f, 0x10, 0x11, 0x12,
Matt2f2f4252005-04-13 14:45:30 +0200325};
326
Tobin Davis8e21c342007-01-08 11:04:17 +0100327static hda_nid_t stac925x_pin_nids[8] = {
328 0x07, 0x08, 0x0a, 0x0b,
329 0x0c, 0x0d, 0x10, 0x11,
330};
331
Matt2f2f4252005-04-13 14:45:30 +0200332static hda_nid_t stac922x_pin_nids[10] = {
333 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
334 0x0f, 0x10, 0x11, 0x15, 0x1b,
335};
336
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100337static hda_nid_t stac92hd73xx_pin_nids[12] = {
338 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
339 0x0f, 0x10, 0x11, 0x12, 0x13,
340 0x14, 0x22
341};
342
Matthew Ranostaye035b842007-11-06 11:53:55 +0100343static hda_nid_t stac92hd71bxx_pin_nids[10] = {
344 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
345 0x0f, 0x14, 0x18, 0x19, 0x1e,
346};
347
Matt Porter3cc08dc2006-01-23 15:27:49 +0100348static hda_nid_t stac927x_pin_nids[14] = {
349 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
350 0x0f, 0x10, 0x11, 0x12, 0x13,
351 0x14, 0x21, 0x22, 0x23,
352};
353
Matt Porterf3302a52006-07-31 12:49:34 +0200354static hda_nid_t stac9205_pin_nids[12] = {
355 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
356 0x0f, 0x14, 0x16, 0x17, 0x18,
357 0x21, 0x22,
Matt Porterf3302a52006-07-31 12:49:34 +0200358};
359
Matt Porter8b657272006-10-26 17:12:59 +0200360static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
361 struct snd_ctl_elem_info *uinfo)
362{
363 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
364 struct sigmatel_spec *spec = codec->spec;
365 return snd_hda_input_mux_info(spec->dinput_mux, uinfo);
366}
367
368static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
369 struct snd_ctl_elem_value *ucontrol)
370{
371 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
372 struct sigmatel_spec *spec = codec->spec;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100373 unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Matt Porter8b657272006-10-26 17:12:59 +0200374
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100375 ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx];
Matt Porter8b657272006-10-26 17:12:59 +0200376 return 0;
377}
378
379static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
380 struct snd_ctl_elem_value *ucontrol)
381{
382 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
383 struct sigmatel_spec *spec = codec->spec;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100384 unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Matt Porter8b657272006-10-26 17:12:59 +0200385
386 return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100387 spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
Matt Porter8b657272006-10-26 17:12:59 +0200388}
389
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100390static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Matt2f2f4252005-04-13 14:45:30 +0200391{
392 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
393 struct sigmatel_spec *spec = codec->spec;
Mattc7d4b2f2005-06-27 14:59:41 +0200394 return snd_hda_input_mux_info(spec->input_mux, uinfo);
Matt2f2f4252005-04-13 14:45:30 +0200395}
396
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100397static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Matt2f2f4252005-04-13 14:45:30 +0200398{
399 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
400 struct sigmatel_spec *spec = codec->spec;
401 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
402
403 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
404 return 0;
405}
406
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100407static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Matt2f2f4252005-04-13 14:45:30 +0200408{
409 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
410 struct sigmatel_spec *spec = codec->spec;
411 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
412
Mattc7d4b2f2005-06-27 14:59:41 +0200413 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Matt2f2f4252005-04-13 14:45:30 +0200414 spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
415}
416
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100417static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
418 struct snd_ctl_elem_info *uinfo)
419{
420 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
421 struct sigmatel_spec *spec = codec->spec;
422 return snd_hda_input_mux_info(spec->mono_mux, uinfo);
423}
424
425static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
426 struct snd_ctl_elem_value *ucontrol)
427{
428 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
429 struct sigmatel_spec *spec = codec->spec;
430
431 ucontrol->value.enumerated.item[0] = spec->cur_mmux;
432 return 0;
433}
434
435static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
436 struct snd_ctl_elem_value *ucontrol)
437{
438 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
439 struct sigmatel_spec *spec = codec->spec;
440
441 return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
442 spec->mono_nid, &spec->cur_mmux);
443}
444
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200445#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
446
447static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
448 struct snd_ctl_elem_value *ucontrol)
449{
450 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100451 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200452 struct sigmatel_spec *spec = codec->spec;
453
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100454 ucontrol->value.integer.value[0] = !!(spec->aloopback &
455 (spec->aloopback_mask << idx));
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200456 return 0;
457}
458
459static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol,
460 struct snd_ctl_elem_value *ucontrol)
461{
462 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
463 struct sigmatel_spec *spec = codec->spec;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100464 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200465 unsigned int dac_mode;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100466 unsigned int val, idx_val;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200467
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100468 idx_val = spec->aloopback_mask << idx;
469 if (ucontrol->value.integer.value[0])
470 val = spec->aloopback | idx_val;
471 else
472 val = spec->aloopback & ~idx_val;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100473 if (spec->aloopback == val)
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200474 return 0;
475
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100476 spec->aloopback = val;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200477
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100478 /* Only return the bits defined by the shift value of the
479 * first two bytes of the mask
480 */
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200481 dac_mode = snd_hda_codec_read(codec, codec->afg, 0,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100482 kcontrol->private_value & 0xFFFF, 0x0);
483 dac_mode >>= spec->aloopback_shift;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200484
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100485 if (spec->aloopback & idx_val) {
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200486 snd_hda_power_up(codec);
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100487 dac_mode |= idx_val;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200488 } else {
489 snd_hda_power_down(codec);
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100490 dac_mode &= ~idx_val;
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200491 }
492
493 snd_hda_codec_write_cache(codec, codec->afg, 0,
494 kcontrol->private_value >> 16, dac_mode);
495
496 return 1;
497}
498
Mattc7d4b2f2005-06-27 14:59:41 +0200499static struct hda_verb stac9200_core_init[] = {
Matt2f2f4252005-04-13 14:45:30 +0200500 /* set dac0mux for dac converter */
Mattc7d4b2f2005-06-27 14:59:41 +0200501 { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
Matt2f2f4252005-04-13 14:45:30 +0200502 {}
503};
504
Takashi Iwai1194b5b2007-10-10 10:04:26 +0200505static struct hda_verb stac9200_eapd_init[] = {
506 /* set dac0mux for dac converter */
507 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
508 {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
509 {}
510};
511
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100512static struct hda_verb stac92hd73xx_6ch_core_init[] = {
513 /* set master volume and direct control */
514 { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
515 /* setup audio connections */
516 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
517 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
518 { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
519 /* setup adcs to point to mixer */
520 { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
521 { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100522 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
523 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
524 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
525 /* setup import muxs */
526 { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
527 { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
528 { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
529 { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
530 {}
531};
532
533static struct hda_verb stac92hd73xx_8ch_core_init[] = {
534 /* set master volume and direct control */
535 { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
536 /* setup audio connections */
537 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
538 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
539 { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
540 /* connect hp ports to dac3 */
541 { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03},
542 { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03},
543 /* setup adcs to point to mixer */
544 { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
545 { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100546 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
547 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
548 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
549 /* setup import muxs */
550 { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
551 { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
552 { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
553 { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
554 {}
555};
556
557static struct hda_verb stac92hd73xx_10ch_core_init[] = {
558 /* set master volume and direct control */
559 { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
560 /* setup audio connections */
561 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
562 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
563 { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 },
564 /* dac3 is connected to import3 mux */
565 { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
566 /* connect hp ports to dac4 */
567 { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04},
568 { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04},
569 /* setup adcs to point to mixer */
570 { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
571 { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100572 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
573 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
574 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
575 /* setup import muxs */
576 { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
577 { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
578 { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
579 { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
580 {}
581};
582
Matthew Ranostaye035b842007-11-06 11:53:55 +0100583static struct hda_verb stac92hd71bxx_core_init[] = {
584 /* set master volume and direct control */
585 { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
586 /* connect headphone jack to dac1 */
587 { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
Matthew Ranostay541eee82007-12-14 12:08:04 +0100588 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
589 /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
590 { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
591 { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
592 { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Matthew Ranostay541eee82007-12-14 12:08:04 +0100593};
594
595static struct hda_verb stac92hd71bxx_analog_core_init[] = {
596 /* set master volume and direct control */
597 { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
598 /* connect headphone jack to dac1 */
599 { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
Matthew Ranostay9b359472007-11-07 13:03:12 +0100600 /* connect ports 0d and 0f to audio mixer */
601 { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
602 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
Matthew Ranostaya64135a2008-01-10 16:55:06 +0100603 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
Matthew Ranostay9b359472007-11-07 13:03:12 +0100604 /* unmute dac0 input in audio mixer */
605 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
Matthew Ranostaye035b842007-11-06 11:53:55 +0100606 /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
607 { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
608 { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
609 { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Matthew Ranostaye035b842007-11-06 11:53:55 +0100610 {}
611};
612
Tobin Davis8e21c342007-01-08 11:04:17 +0100613static struct hda_verb stac925x_core_init[] = {
614 /* set dac0mux for dac converter */
615 { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
616 {}
617};
618
Mattc7d4b2f2005-06-27 14:59:41 +0200619static struct hda_verb stac922x_core_init[] = {
Matt2f2f4252005-04-13 14:45:30 +0200620 /* set master volume and direct control */
Mattc7d4b2f2005-06-27 14:59:41 +0200621 { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
Matt2f2f4252005-04-13 14:45:30 +0200622 {}
623};
624
Tobin Davis93ed1502006-09-01 21:03:12 +0200625static struct hda_verb d965_core_init[] = {
Takashi Iwai19039bd2006-06-28 15:52:16 +0200626 /* set master volume and direct control */
Tobin Davis93ed1502006-09-01 21:03:12 +0200627 { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
Takashi Iwai19039bd2006-06-28 15:52:16 +0200628 /* unmute node 0x1b */
629 { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
630 /* select node 0x03 as DAC */
631 { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
632 {}
633};
634
Matt Porter3cc08dc2006-01-23 15:27:49 +0100635static struct hda_verb stac927x_core_init[] = {
636 /* set master volume and direct control */
637 { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
638 {}
639};
640
Matt Porterf3302a52006-07-31 12:49:34 +0200641static struct hda_verb stac9205_core_init[] = {
642 /* set master volume and direct control */
643 { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
644 {}
645};
646
Matthew Ranostayb22b4822008-01-22 12:32:30 +0100647#define STAC_MONO_MUX \
648 { \
649 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
650 .name = "Mono Mux", \
651 .count = 1, \
652 .info = stac92xx_mono_mux_enum_info, \
653 .get = stac92xx_mono_mux_enum_get, \
654 .put = stac92xx_mono_mux_enum_put, \
655 }
656
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200657#define STAC_INPUT_SOURCE(cnt) \
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200658 { \
659 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
660 .name = "Input Source", \
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200661 .count = cnt, \
Maxim Levitskyca7c5a82007-08-31 12:52:19 +0200662 .info = stac92xx_mux_enum_info, \
663 .get = stac92xx_mux_enum_get, \
664 .put = stac92xx_mux_enum_put, \
665 }
666
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100667#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200668 { \
669 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
670 .name = "Analog Loopback", \
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100671 .count = cnt, \
Maxim Levitsky5f10c4a2007-09-03 15:29:37 +0200672 .info = stac92xx_aloopback_info, \
673 .get = stac92xx_aloopback_get, \
674 .put = stac92xx_aloopback_put, \
675 .private_value = verb_read | (verb_write << 16), \
676 }
677
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100678static struct snd_kcontrol_new stac9200_mixer[] = {
Matt2f2f4252005-04-13 14:45:30 +0200679 HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
680 HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200681 STAC_INPUT_SOURCE(1),
Matt2f2f4252005-04-13 14:45:30 +0200682 HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
683 HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
Mattc7d4b2f2005-06-27 14:59:41 +0200684 HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT),
Matt2f2f4252005-04-13 14:45:30 +0200685 { } /* end */
686};
687
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100688static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100689 STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
690
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100691 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
692 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
693
694 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
695 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
696
697 HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
698 HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
699
700 HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
701 HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
702
703 HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
704 HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
705
706 HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
707 HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
708
709 HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
710 HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
711 { } /* end */
712};
713
714static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100715 STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
716
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100717 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
718 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
719
720 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
721 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
722
723 HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
724 HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
725
726 HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
727 HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
728
729 HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
730 HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
731
732 HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
733 HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
734
735 HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
736 HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
737 { } /* end */
738};
739
740static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100741 STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
742
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100743 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
744 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
745
746 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
747 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
748
749 HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
750 HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
751
752 HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
753 HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
754
755 HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
756 HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
757
758 HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
759 HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
760
761 HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
762 HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
763 { } /* end */
764};
765
Matthew Ranostay541eee82007-12-14 12:08:04 +0100766static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
Matthew Ranostaye035b842007-11-06 11:53:55 +0100767 STAC_INPUT_SOURCE(2),
Matthew Ranostaye035b842007-11-06 11:53:55 +0100768
Matthew Ranostay9b359472007-11-07 13:03:12 +0100769 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
770 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
771 HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
772
773 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
774 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
775 HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
776
777 HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
778 HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
Matthew Ranostaye035b842007-11-06 11:53:55 +0100779 { } /* end */
780};
781
Matthew Ranostay541eee82007-12-14 12:08:04 +0100782static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
Matthew Ranostay541eee82007-12-14 12:08:04 +0100783 STAC_INPUT_SOURCE(2),
784 STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
785
Matthew Ranostay541eee82007-12-14 12:08:04 +0100786 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
787 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
788 HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
789
790 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
791 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
792 HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
793 { } /* end */
794};
795
Tobin Davis8e21c342007-01-08 11:04:17 +0100796static struct snd_kcontrol_new stac925x_mixer[] = {
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200797 STAC_INPUT_SOURCE(1),
Tobin Davis8e21c342007-01-08 11:04:17 +0100798 HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
799 HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_OUTPUT),
800 HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT),
801 { } /* end */
802};
803
Takashi Iwaid1d985f2006-11-23 19:27:12 +0100804static struct snd_kcontrol_new stac9205_mixer[] = {
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200805 STAC_INPUT_SOURCE(2),
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100806 STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200807
808 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
809 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
810 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x19, 0x0, HDA_OUTPUT),
811
812 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT),
813 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT),
814 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x1A, 0x0, HDA_OUTPUT),
815
816 { } /* end */
817};
818
819/* This needs to be generated dynamically based on sequence */
820static struct snd_kcontrol_new stac922x_mixer[] = {
821 STAC_INPUT_SOURCE(2),
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200822 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),
823 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT),
824 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x12, 0x0, HDA_OUTPUT),
825
826 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT),
827 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT),
828 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x13, 0x0, HDA_OUTPUT),
829 { } /* end */
830};
831
832
833static struct snd_kcontrol_new stac927x_mixer[] = {
834 STAC_INPUT_SOURCE(3),
Matthew Ranostaye1f0d662007-12-13 17:47:21 +0100835 STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +0200836
837 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
838 HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
839 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x15, 0x0, HDA_OUTPUT),
840
841 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT),
842 HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT),
843 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x16, 0x0, HDA_OUTPUT),
844
845 HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT),
846 HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT),
847 HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x2, 0x17, 0x0, HDA_OUTPUT),
Matt Porterf3302a52006-07-31 12:49:34 +0200848 { } /* end */
849};
850
Takashi Iwai1697055e2007-12-18 18:05:52 +0100851static struct snd_kcontrol_new stac_dmux_mixer = {
852 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
853 .name = "Digital Input Source",
854 /* count set later */
855 .info = stac92xx_dmux_enum_info,
856 .get = stac92xx_dmux_enum_get,
857 .put = stac92xx_dmux_enum_put,
858};
859
Takashi Iwai2134ea42008-01-10 16:53:55 +0100860static const char *slave_vols[] = {
861 "Front Playback Volume",
862 "Surround Playback Volume",
863 "Center Playback Volume",
864 "LFE Playback Volume",
865 "Side Playback Volume",
866 "Headphone Playback Volume",
867 "Headphone Playback Volume",
868 "Speaker Playback Volume",
869 "External Speaker Playback Volume",
870 "Speaker2 Playback Volume",
871 NULL
872};
873
874static const char *slave_sws[] = {
875 "Front Playback Switch",
876 "Surround Playback Switch",
877 "Center Playback Switch",
878 "LFE Playback Switch",
879 "Side Playback Switch",
880 "Headphone Playback Switch",
881 "Headphone Playback Switch",
882 "Speaker Playback Switch",
883 "External Speaker Playback Switch",
884 "Speaker2 Playback Switch",
Takashi Iwaiedb54a52008-01-29 12:47:02 +0100885 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100886 NULL
887};
888
Matt2f2f4252005-04-13 14:45:30 +0200889static int stac92xx_build_controls(struct hda_codec *codec)
890{
891 struct sigmatel_spec *spec = codec->spec;
892 int err;
Mattc7d4b2f2005-06-27 14:59:41 +0200893 int i;
Matt2f2f4252005-04-13 14:45:30 +0200894
895 err = snd_hda_add_new_ctls(codec, spec->mixer);
896 if (err < 0)
897 return err;
Mattc7d4b2f2005-06-27 14:59:41 +0200898
899 for (i = 0; i < spec->num_mixers; i++) {
900 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
901 if (err < 0)
902 return err;
903 }
Takashi Iwai1697055e2007-12-18 18:05:52 +0100904 if (spec->num_dmuxes > 0) {
905 stac_dmux_mixer.count = spec->num_dmuxes;
906 err = snd_ctl_add(codec->bus->card,
907 snd_ctl_new1(&stac_dmux_mixer, codec));
908 if (err < 0)
909 return err;
910 }
Mattc7d4b2f2005-06-27 14:59:41 +0200911
Mattdabbed62005-06-14 10:19:34 +0200912 if (spec->multiout.dig_out_nid) {
913 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
914 if (err < 0)
915 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100916 err = snd_hda_create_spdif_share_sw(codec,
917 &spec->multiout);
918 if (err < 0)
919 return err;
920 spec->multiout.share_spdif = 1;
Mattdabbed62005-06-14 10:19:34 +0200921 }
922 if (spec->dig_in_nid) {
923 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
924 if (err < 0)
925 return err;
926 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100927
928 /* if we have no master control, let's create it */
929 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100930 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100931 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100932 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100933 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100934 vmaster_tlv, slave_vols);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100935 if (err < 0)
936 return err;
937 }
938 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
939 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
940 NULL, slave_sws);
941 if (err < 0)
942 return err;
943 }
944
Mattdabbed62005-06-14 10:19:34 +0200945 return 0;
Matt2f2f4252005-04-13 14:45:30 +0200946}
947
Matt Porter403d1942005-11-29 15:00:51 +0100948static unsigned int ref9200_pin_configs[8] = {
Mattdabbed62005-06-14 10:19:34 +0200949 0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
Matt2f2f4252005-04-13 14:45:30 +0200950 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
951};
952
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200953/*
954 STAC 9200 pin configs for
955 102801A8
956 102801DE
957 102801E8
958*/
959static unsigned int dell9200_d21_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +0200960 0x400001f0, 0x400001f1, 0x02214030, 0x01014010,
961 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200962};
963
964/*
965 STAC 9200 pin configs for
966 102801C0
967 102801C1
968*/
969static unsigned int dell9200_d22_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +0200970 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
971 0x01813020, 0x02a19021, 0x90100140, 0x400001f2,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200972};
973
974/*
975 STAC 9200 pin configs for
976 102801C4 (Dell Dimension E310)
977 102801C5
978 102801C7
979 102801D9
980 102801DA
981 102801E3
982*/
983static unsigned int dell9200_d23_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +0200984 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
985 0x01813020, 0x01a19021, 0x90100140, 0x400001f2,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200986};
987
988
989/*
990 STAC 9200-32 pin configs for
991 102801B5 (Dell Inspiron 630m)
992 102801D8 (Dell Inspiron 640m)
993*/
994static unsigned int dell9200_m21_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +0200995 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310,
996 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd,
Takashi Iwaidfe495d2007-08-23 19:04:28 +0200997};
998
999/*
1000 STAC 9200-32 pin configs for
1001 102801C2 (Dell Latitude D620)
1002 102801C8
1003 102801CC (Dell Latitude D820)
1004 102801D4
1005 102801D6
1006*/
1007static unsigned int dell9200_m22_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001008 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310,
1009 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001010};
1011
1012/*
1013 STAC 9200-32 pin configs for
1014 102801CE (Dell XPS M1710)
1015 102801CF (Dell Precision M90)
1016*/
1017static unsigned int dell9200_m23_pin_configs[8] = {
1018 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310,
1019 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc,
1020};
1021
1022/*
1023 STAC 9200-32 pin configs for
1024 102801C9
1025 102801CA
1026 102801CB (Dell Latitude 120L)
1027 102801D3
1028*/
1029static unsigned int dell9200_m24_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001030 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310,
1031 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001032};
1033
1034/*
1035 STAC 9200-32 pin configs for
1036 102801BD (Dell Inspiron E1505n)
1037 102801EE
1038 102801EF
1039*/
1040static unsigned int dell9200_m25_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001041 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
1042 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001043};
1044
1045/*
1046 STAC 9200-32 pin configs for
1047 102801F5 (Dell Inspiron 1501)
1048 102801F6
1049*/
1050static unsigned int dell9200_m26_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001051 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310,
1052 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001053};
1054
1055/*
1056 STAC 9200-32
1057 102801CD (Dell Inspiron E1705/9400)
1058*/
1059static unsigned int dell9200_m27_pin_configs[8] = {
Takashi Iwaiaf6c0162007-09-05 23:46:03 +02001060 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
1061 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001062};
1063
Tobin Davisbf277782008-02-03 20:31:47 +01001064static unsigned int oqo9200_pin_configs[8] = {
1065 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210,
1066 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3,
1067};
1068
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001069
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001070static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
1071 [STAC_REF] = ref9200_pin_configs,
Tobin Davisbf277782008-02-03 20:31:47 +01001072 [STAC_9200_OQO] = oqo9200_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001073 [STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
1074 [STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
1075 [STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
1076 [STAC_9200_DELL_M21] = dell9200_m21_pin_configs,
1077 [STAC_9200_DELL_M22] = dell9200_m22_pin_configs,
1078 [STAC_9200_DELL_M23] = dell9200_m23_pin_configs,
1079 [STAC_9200_DELL_M24] = dell9200_m24_pin_configs,
1080 [STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
1081 [STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
1082 [STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
Matt Porter403d1942005-11-29 15:00:51 +01001083};
1084
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001085static const char *stac9200_models[STAC_9200_MODELS] = {
1086 [STAC_REF] = "ref",
Tobin Davisbf277782008-02-03 20:31:47 +01001087 [STAC_9200_OQO] = "oqo",
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001088 [STAC_9200_DELL_D21] = "dell-d21",
1089 [STAC_9200_DELL_D22] = "dell-d22",
1090 [STAC_9200_DELL_D23] = "dell-d23",
1091 [STAC_9200_DELL_M21] = "dell-m21",
1092 [STAC_9200_DELL_M22] = "dell-m22",
1093 [STAC_9200_DELL_M23] = "dell-m23",
1094 [STAC_9200_DELL_M24] = "dell-m24",
1095 [STAC_9200_DELL_M25] = "dell-m25",
1096 [STAC_9200_DELL_M26] = "dell-m26",
1097 [STAC_9200_DELL_M27] = "dell-m27",
Takashi Iwai1194b5b2007-10-10 10:04:26 +02001098 [STAC_9200_GATEWAY] = "gateway",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001099};
1100
1101static struct snd_pci_quirk stac9200_cfg_tbl[] = {
1102 /* SigmaTel reference board */
1103 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1104 "DFI LanParty", STAC_REF),
Matt Portere7377072006-11-06 11:20:38 +01001105 /* Dell laptops have BIOS problem */
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001106 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8,
1107 "unknown Dell", STAC_9200_DELL_D21),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001108 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001109 "Dell Inspiron 630m", STAC_9200_DELL_M21),
1110 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bd,
1111 "Dell Inspiron E1505n", STAC_9200_DELL_M25),
1112 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c0,
1113 "unknown Dell", STAC_9200_DELL_D22),
1114 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c1,
1115 "unknown Dell", STAC_9200_DELL_D22),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001116 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001117 "Dell Latitude D620", STAC_9200_DELL_M22),
1118 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c5,
1119 "unknown Dell", STAC_9200_DELL_D23),
1120 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c7,
1121 "unknown Dell", STAC_9200_DELL_D23),
1122 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c8,
1123 "unknown Dell", STAC_9200_DELL_M22),
1124 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c9,
1125 "unknown Dell", STAC_9200_DELL_M24),
1126 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ca,
1127 "unknown Dell", STAC_9200_DELL_M24),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001128 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001129 "Dell Latitude 120L", STAC_9200_DELL_M24),
Cory T. Tusar877b8662007-01-30 17:30:55 +01001130 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001131 "Dell Latitude D820", STAC_9200_DELL_M22),
Mikael Nilsson46f02ca2007-02-13 12:46:16 +01001132 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cd,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001133 "Dell Inspiron E1705/9400", STAC_9200_DELL_M27),
Mikael Nilsson46f02ca2007-02-13 12:46:16 +01001134 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ce,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001135 "Dell XPS M1710", STAC_9200_DELL_M23),
Takashi Iwaif0f96742007-02-14 00:59:17 +01001136 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001137 "Dell Precision M90", STAC_9200_DELL_M23),
1138 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d3,
1139 "unknown Dell", STAC_9200_DELL_M22),
1140 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d4,
1141 "unknown Dell", STAC_9200_DELL_M22),
Daniel T Chen8286c532007-05-15 11:46:23 +02001142 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001143 "unknown Dell", STAC_9200_DELL_M22),
Tobin Davis49c605d2007-05-17 09:38:24 +02001144 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001145 "Dell Inspiron 640m", STAC_9200_DELL_M21),
1146 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d9,
1147 "unknown Dell", STAC_9200_DELL_D23),
1148 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01da,
1149 "unknown Dell", STAC_9200_DELL_D23),
1150 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01de,
1151 "unknown Dell", STAC_9200_DELL_D21),
1152 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e3,
1153 "unknown Dell", STAC_9200_DELL_D23),
1154 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e8,
1155 "unknown Dell", STAC_9200_DELL_D21),
1156 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ee,
1157 "unknown Dell", STAC_9200_DELL_M25),
1158 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ef,
1159 "unknown Dell", STAC_9200_DELL_M25),
Tobin Davis49c605d2007-05-17 09:38:24 +02001160 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001161 "Dell Inspiron 1501", STAC_9200_DELL_M26),
1162 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6,
1163 "unknown Dell", STAC_9200_DELL_M26),
Tobin Davis49c605d2007-05-17 09:38:24 +02001164 /* Panasonic */
1165 SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF),
Takashi Iwai1194b5b2007-10-10 10:04:26 +02001166 /* Gateway machines needs EAPD to be set on resume */
1167 SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_GATEWAY),
1168 SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*",
1169 STAC_9200_GATEWAY),
1170 SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707",
1171 STAC_9200_GATEWAY),
Tobin Davisbf277782008-02-03 20:31:47 +01001172 /* OQO Mobile */
1173 SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO),
Matt Porter403d1942005-11-29 15:00:51 +01001174 {} /* terminator */
1175};
1176
Tobin Davis8e21c342007-01-08 11:04:17 +01001177static unsigned int ref925x_pin_configs[8] = {
1178 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
Matthew Ranostay09a99952008-01-24 11:49:21 +01001179 0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
Tobin Davis8e21c342007-01-08 11:04:17 +01001180};
1181
1182static unsigned int stac925x_MA6_pin_configs[8] = {
1183 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
1184 0x90a70320, 0x90100211, 0x400003f1, 0x9033032e,
1185};
1186
Tobin Davis2c11f952007-05-17 09:36:34 +02001187static unsigned int stac925x_PA6_pin_configs[8] = {
1188 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
1189 0x50a103f0, 0x90100211, 0x400003f1, 0x9033032e,
1190};
1191
Tobin Davis8e21c342007-01-08 11:04:17 +01001192static unsigned int stac925xM2_2_pin_configs[8] = {
Steve Longerbeam7353e142007-05-29 14:36:17 +02001193 0x40c003f3, 0x424503f2, 0x04180011, 0x02a19020,
1194 0x50a103f0, 0x90100212, 0x400003f1, 0x9033032e,
Tobin Davis8e21c342007-01-08 11:04:17 +01001195};
1196
1197static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
1198 [STAC_REF] = ref925x_pin_configs,
1199 [STAC_M2_2] = stac925xM2_2_pin_configs,
1200 [STAC_MA6] = stac925x_MA6_pin_configs,
Tobin Davis2c11f952007-05-17 09:36:34 +02001201 [STAC_PA6] = stac925x_PA6_pin_configs,
Tobin Davis8e21c342007-01-08 11:04:17 +01001202};
1203
1204static const char *stac925x_models[STAC_925x_MODELS] = {
1205 [STAC_REF] = "ref",
1206 [STAC_M2_2] = "m2-2",
1207 [STAC_MA6] = "m6",
Tobin Davis2c11f952007-05-17 09:36:34 +02001208 [STAC_PA6] = "pa6",
Tobin Davis8e21c342007-01-08 11:04:17 +01001209};
1210
1211static struct snd_pci_quirk stac925x_cfg_tbl[] = {
1212 /* SigmaTel reference board */
1213 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
Tobin Davis2c11f952007-05-17 09:36:34 +02001214 SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
Tobin Davis8e21c342007-01-08 11:04:17 +01001215 SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_REF),
1216 SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_REF),
1217 SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_MA6),
Tobin Davis2c11f952007-05-17 09:36:34 +02001218 SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_PA6),
Tobin Davis8e21c342007-01-08 11:04:17 +01001219 SND_PCI_QUIRK(0x1002, 0x437b, "Gateway MX6453", STAC_M2_2),
1220 {} /* terminator */
1221};
1222
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01001223static unsigned int ref92hd73xx_pin_configs[12] = {
1224 0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
1225 0x0181302e, 0x01014010, 0x01014020, 0x01014030,
1226 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
1227};
1228
1229static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
1230 [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
1231};
1232
1233static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
1234 [STAC_92HD73XX_REF] = "ref",
1235};
1236
1237static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
1238 /* SigmaTel reference board */
1239 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1240 "DFI LanParty", STAC_92HD73XX_REF),
1241 {} /* terminator */
1242};
1243
Matthew Ranostaye035b842007-11-06 11:53:55 +01001244static unsigned int ref92hd71bxx_pin_configs[10] = {
1245 0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
Matthew Ranostayb22b4822008-01-22 12:32:30 +01001246 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0,
Matthew Ranostaye035b842007-11-06 11:53:55 +01001247 0x90a000f0, 0x01452050,
1248};
1249
1250static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
1251 [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
1252};
1253
1254static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
1255 [STAC_92HD71BXX_REF] = "ref",
1256};
1257
1258static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
1259 /* SigmaTel reference board */
1260 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1261 "DFI LanParty", STAC_92HD71BXX_REF),
1262 {} /* terminator */
1263};
1264
Matt Porter403d1942005-11-29 15:00:51 +01001265static unsigned int ref922x_pin_configs[10] = {
1266 0x01014010, 0x01016011, 0x01012012, 0x0221401f,
1267 0x01813122, 0x01011014, 0x01441030, 0x01c41030,
Matt2f2f4252005-04-13 14:45:30 +02001268 0x40000100, 0x40000100,
1269};
1270
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001271/*
1272 STAC 922X pin configs for
1273 102801A7
1274 102801AB
1275 102801A9
1276 102801D1
1277 102801D2
1278*/
1279static unsigned int dell_922x_d81_pin_configs[10] = {
1280 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
1281 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1,
1282 0x01813122, 0x400001f2,
1283};
1284
1285/*
1286 STAC 922X pin configs for
1287 102801AC
1288 102801D0
1289*/
1290static unsigned int dell_922x_d82_pin_configs[10] = {
1291 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
1292 0x02a19020, 0x01117011, 0x01451140, 0x400001f0,
1293 0x01813122, 0x400001f1,
1294};
1295
1296/*
1297 STAC 922X pin configs for
1298 102801BF
1299*/
1300static unsigned int dell_922x_m81_pin_configs[10] = {
1301 0x0321101f, 0x01112024, 0x01111222, 0x91174220,
1302 0x03a11050, 0x01116221, 0x90a70330, 0x01452340,
1303 0x40C003f1, 0x405003f0,
1304};
1305
1306/*
1307 STAC 9221 A1 pin configs for
1308 102801D7 (Dell XPS M1210)
1309*/
1310static unsigned int dell_922x_m82_pin_configs[10] = {
Jiang Zhe7f9310c2007-11-12 12:43:37 +01001311 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310,
1312 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001313 0x508003f3, 0x405003f4,
1314};
1315
Matt Porter403d1942005-11-29 15:00:51 +01001316static unsigned int d945gtp3_pin_configs[10] = {
Matt Porter869264c2006-01-25 19:20:50 +01001317 0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
Matt Porter403d1942005-11-29 15:00:51 +01001318 0x40000100, 0x40000100, 0x40000100, 0x40000100,
1319 0x02a19120, 0x40000100,
1320};
1321
1322static unsigned int d945gtp5_pin_configs[10] = {
Matt Porter869264c2006-01-25 19:20:50 +01001323 0x0221401f, 0x01011012, 0x01813024, 0x01014010,
1324 0x01a19021, 0x01016011, 0x01452130, 0x40000100,
Matt Porter403d1942005-11-29 15:00:51 +01001325 0x02a19320, 0x40000100,
1326};
1327
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001328static unsigned int intel_mac_v1_pin_configs[10] = {
1329 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
1330 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
Takashi Iwai3fc24d82007-02-16 13:27:18 +01001331 0x400000fc, 0x400000fb,
1332};
1333
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001334static unsigned int intel_mac_v2_pin_configs[10] = {
1335 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
1336 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
Sylvain FORETf16928f2007-04-27 14:22:36 +02001337 0x400000fc, 0x400000fb,
1338};
1339
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001340static unsigned int intel_mac_v3_pin_configs[10] = {
1341 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
1342 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
1343 0x400000fc, 0x400000fb,
1344};
1345
1346static unsigned int intel_mac_v4_pin_configs[10] = {
1347 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
1348 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
1349 0x400000fc, 0x400000fb,
1350};
1351
1352static unsigned int intel_mac_v5_pin_configs[10] = {
1353 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
1354 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
1355 0x400000fc, 0x400000fb,
Takashi Iwai0dae0f82007-05-21 12:41:29 +02001356};
1357
Takashi Iwai76c08822007-06-19 12:17:42 +02001358
Takashi Iwai19039bd2006-06-28 15:52:16 +02001359static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001360 [STAC_D945_REF] = ref922x_pin_configs,
Takashi Iwai19039bd2006-06-28 15:52:16 +02001361 [STAC_D945GTP3] = d945gtp3_pin_configs,
1362 [STAC_D945GTP5] = d945gtp5_pin_configs,
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001363 [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
1364 [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
1365 [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
1366 [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
1367 [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001368 /* for backward compatibility */
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001369 [STAC_MACMINI] = intel_mac_v3_pin_configs,
1370 [STAC_MACBOOK] = intel_mac_v5_pin_configs,
1371 [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
1372 [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
1373 [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
1374 [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001375 [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,
1376 [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,
1377 [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs,
1378 [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs,
Matt Porter403d1942005-11-29 15:00:51 +01001379};
1380
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001381static const char *stac922x_models[STAC_922X_MODELS] = {
1382 [STAC_D945_REF] = "ref",
1383 [STAC_D945GTP5] = "5stack",
1384 [STAC_D945GTP3] = "3stack",
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001385 [STAC_INTEL_MAC_V1] = "intel-mac-v1",
1386 [STAC_INTEL_MAC_V2] = "intel-mac-v2",
1387 [STAC_INTEL_MAC_V3] = "intel-mac-v3",
1388 [STAC_INTEL_MAC_V4] = "intel-mac-v4",
1389 [STAC_INTEL_MAC_V5] = "intel-mac-v5",
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001390 /* for backward compatibility */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001391 [STAC_MACMINI] = "macmini",
Takashi Iwai3fc24d82007-02-16 13:27:18 +01001392 [STAC_MACBOOK] = "macbook",
Nicolas Boichat6f0778d2007-03-15 12:38:15 +01001393 [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
1394 [STAC_MACBOOK_PRO_V2] = "macbook-pro",
Sylvain FORETf16928f2007-04-27 14:22:36 +02001395 [STAC_IMAC_INTEL] = "imac-intel",
Takashi Iwai0dae0f82007-05-21 12:41:29 +02001396 [STAC_IMAC_INTEL_20] = "imac-intel-20",
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001397 [STAC_922X_DELL_D81] = "dell-d81",
1398 [STAC_922X_DELL_D82] = "dell-d82",
1399 [STAC_922X_DELL_M81] = "dell-m81",
1400 [STAC_922X_DELL_M82] = "dell-m82",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001401};
1402
1403static struct snd_pci_quirk stac922x_cfg_tbl[] = {
1404 /* SigmaTel reference board */
1405 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1406 "DFI LanParty", STAC_D945_REF),
1407 /* Intel 945G based systems */
1408 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101,
1409 "Intel D945G", STAC_D945GTP3),
1410 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202,
1411 "Intel D945G", STAC_D945GTP3),
1412 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606,
1413 "Intel D945G", STAC_D945GTP3),
1414 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601,
1415 "Intel D945G", STAC_D945GTP3),
1416 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111,
1417 "Intel D945G", STAC_D945GTP3),
1418 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115,
1419 "Intel D945G", STAC_D945GTP3),
1420 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116,
1421 "Intel D945G", STAC_D945GTP3),
1422 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117,
1423 "Intel D945G", STAC_D945GTP3),
1424 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118,
1425 "Intel D945G", STAC_D945GTP3),
1426 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119,
1427 "Intel D945G", STAC_D945GTP3),
1428 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826,
1429 "Intel D945G", STAC_D945GTP3),
1430 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049,
1431 "Intel D945G", STAC_D945GTP3),
1432 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055,
1433 "Intel D945G", STAC_D945GTP3),
1434 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048,
1435 "Intel D945G", STAC_D945GTP3),
1436 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110,
1437 "Intel D945G", STAC_D945GTP3),
1438 /* Intel D945G 5-stack systems */
1439 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404,
1440 "Intel D945G", STAC_D945GTP5),
1441 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303,
1442 "Intel D945G", STAC_D945GTP5),
1443 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013,
1444 "Intel D945G", STAC_D945GTP5),
1445 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417,
1446 "Intel D945G", STAC_D945GTP5),
1447 /* Intel 945P based systems */
1448 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b,
1449 "Intel D945P", STAC_D945GTP3),
1450 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112,
1451 "Intel D945P", STAC_D945GTP3),
1452 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d,
1453 "Intel D945P", STAC_D945GTP3),
1454 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909,
1455 "Intel D945P", STAC_D945GTP3),
1456 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505,
1457 "Intel D945P", STAC_D945GTP3),
1458 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
1459 "Intel D945P", STAC_D945GTP5),
1460 /* other systems */
1461 /* Apple Mac Mini (early 2006) */
1462 SND_PCI_QUIRK(0x8384, 0x7680,
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02001463 "Mac Mini", STAC_INTEL_MAC_V3),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001464 /* Dell systems */
1465 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
1466 "unknown Dell", STAC_922X_DELL_D81),
1467 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a9,
1468 "unknown Dell", STAC_922X_DELL_D81),
1469 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ab,
1470 "unknown Dell", STAC_922X_DELL_D81),
1471 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ac,
1472 "unknown Dell", STAC_922X_DELL_D82),
1473 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bf,
1474 "unknown Dell", STAC_922X_DELL_M81),
1475 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d0,
1476 "unknown Dell", STAC_922X_DELL_D82),
1477 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d1,
1478 "unknown Dell", STAC_922X_DELL_D81),
1479 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d2,
1480 "unknown Dell", STAC_922X_DELL_D81),
1481 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
1482 "Dell XPS M1210", STAC_922X_DELL_M82),
Matt Porter403d1942005-11-29 15:00:51 +01001483 {} /* terminator */
1484};
1485
Matt Porter3cc08dc2006-01-23 15:27:49 +01001486static unsigned int ref927x_pin_configs[14] = {
Tobin Davis93ed1502006-09-01 21:03:12 +02001487 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
1488 0x01a19040, 0x01011012, 0x01016011, 0x0101201f,
1489 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070,
1490 0x01c42190, 0x40000100,
Matt Porter3cc08dc2006-01-23 15:27:49 +01001491};
1492
Tobin Davis93ed1502006-09-01 21:03:12 +02001493static unsigned int d965_3st_pin_configs[14] = {
Tobin Davis81d3dbd2006-08-22 19:44:45 +02001494 0x0221401f, 0x02a19120, 0x40000100, 0x01014011,
1495 0x01a19021, 0x01813024, 0x40000100, 0x40000100,
1496 0x40000100, 0x40000100, 0x40000100, 0x40000100,
1497 0x40000100, 0x40000100
1498};
1499
Tobin Davis93ed1502006-09-01 21:03:12 +02001500static unsigned int d965_5st_pin_configs[14] = {
1501 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
1502 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
1503 0x40000100, 0x40000100, 0x40000100, 0x01442070,
1504 0x40000100, 0x40000100
1505};
1506
Tobin Davis4ff076e2007-08-07 11:48:12 +02001507static unsigned int dell_3st_pin_configs[14] = {
1508 0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
1509 0x01111212, 0x01116211, 0x01813050, 0x01112214,
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001510 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb,
Tobin Davis4ff076e2007-08-07 11:48:12 +02001511 0x40c003fc, 0x40000100
1512};
1513
Tobin Davis93ed1502006-09-01 21:03:12 +02001514static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001515 [STAC_D965_REF] = ref927x_pin_configs,
1516 [STAC_D965_3ST] = d965_3st_pin_configs,
1517 [STAC_D965_5ST] = d965_5st_pin_configs,
1518 [STAC_DELL_3ST] = dell_3st_pin_configs,
1519 [STAC_DELL_BIOS] = NULL,
Matt Porter3cc08dc2006-01-23 15:27:49 +01001520};
1521
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001522static const char *stac927x_models[STAC_927X_MODELS] = {
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001523 [STAC_D965_REF] = "ref",
1524 [STAC_D965_3ST] = "3stack",
1525 [STAC_D965_5ST] = "5stack",
1526 [STAC_DELL_3ST] = "dell-3stack",
1527 [STAC_DELL_BIOS] = "dell-bios",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001528};
1529
1530static struct snd_pci_quirk stac927x_cfg_tbl[] = {
1531 /* SigmaTel reference board */
1532 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1533 "DFI LanParty", STAC_D965_REF),
Tobin Davis81d3dbd2006-08-22 19:44:45 +02001534 /* Intel 946 based systems */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001535 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST),
1536 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST),
Tobin Davis93ed1502006-09-01 21:03:12 +02001537 /* 965 based 3 stack systems */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001538 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST),
1539 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST),
1540 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST),
1541 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST),
1542 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST),
1543 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST),
1544 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST),
1545 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST),
1546 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST),
1547 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST),
1548 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST),
1549 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST),
1550 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST),
1551 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
1552 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
1553 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
Tobin Davis4ff076e2007-08-07 11:48:12 +02001554 /* Dell 3 stack systems */
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001555 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_3ST),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001556 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
Tobin Davis4ff076e2007-08-07 11:48:12 +02001557 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST),
1558 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST),
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001559 /* Dell 3 stack systems with verb table in BIOS */
Matthew Ranostay2f32d902008-01-10 13:06:26 +01001560 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
1561 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01001562 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell ", STAC_DELL_BIOS),
1563 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS),
1564 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
1565 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
1566 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
1567 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
Tobin Davis93ed1502006-09-01 21:03:12 +02001568 /* 965 based 5 stack systems */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001569 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
1570 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
1571 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
1572 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST),
1573 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST),
1574 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST),
1575 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST),
1576 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST),
1577 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST),
Matt Porter3cc08dc2006-01-23 15:27:49 +01001578 {} /* terminator */
1579};
1580
Matt Porterf3302a52006-07-31 12:49:34 +02001581static unsigned int ref9205_pin_configs[12] = {
1582 0x40000100, 0x40000100, 0x01016011, 0x01014010,
Matthew Ranostay09a99952008-01-24 11:49:21 +01001583 0x01813122, 0x01a19021, 0x01019020, 0x40000100,
Matt Porter8b657272006-10-26 17:12:59 +02001584 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
Matt Porterf3302a52006-07-31 12:49:34 +02001585};
1586
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001587/*
1588 STAC 9205 pin configs for
1589 102801F1
1590 102801F2
1591 102801FC
1592 102801FD
1593 10280204
1594 1028021F
Matthew Ranostay3fa2ef72008-01-11 11:39:06 +01001595 10280228 (Dell Vostro 1500)
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001596*/
1597static unsigned int dell_9205_m42_pin_configs[12] = {
1598 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
1599 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9,
1600 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE,
1601};
1602
1603/*
1604 STAC 9205 pin configs for
1605 102801F9
1606 102801FA
1607 102801FE
1608 102801FF (Dell Precision M4300)
1609 10280206
1610 10280200
1611 10280201
1612*/
1613static unsigned int dell_9205_m43_pin_configs[12] = {
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001614 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310,
1615 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9,
1616 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8,
1617};
1618
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001619static unsigned int dell_9205_m44_pin_configs[12] = {
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001620 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310,
1621 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9,
1622 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe,
1623};
1624
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001625static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001626 [STAC_9205_REF] = ref9205_pin_configs,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001627 [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
1628 [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
1629 [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
Matt Porterf3302a52006-07-31 12:49:34 +02001630};
1631
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001632static const char *stac9205_models[STAC_9205_MODELS] = {
1633 [STAC_9205_REF] = "ref",
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001634 [STAC_9205_DELL_M42] = "dell-m42",
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001635 [STAC_9205_DELL_M43] = "dell-m43",
1636 [STAC_9205_DELL_M44] = "dell-m44",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001637};
1638
1639static struct snd_pci_quirk stac9205_cfg_tbl[] = {
1640 /* SigmaTel reference board */
1641 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
1642 "DFI LanParty", STAC_9205_REF),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001643 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
1644 "unknown Dell", STAC_9205_DELL_M42),
1645 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
1646 "unknown Dell", STAC_9205_DELL_M42),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001647 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
Matthew Ranostayb44ef2f2007-09-18 00:52:38 +02001648 "Dell Precision", STAC_9205_DELL_M43),
1649 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
1650 "Dell Precision", STAC_9205_DELL_M43),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001651 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
1652 "Dell Precision", STAC_9205_DELL_M43),
Matthew Ranostaye45e4592007-09-10 23:09:42 +02001653 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
1654 "Dell Precision", STAC_9205_DELL_M43),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001655 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
1656 "Dell Precision", STAC_9205_DELL_M43),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001657 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
1658 "unknown Dell", STAC_9205_DELL_M42),
1659 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
1660 "unknown Dell", STAC_9205_DELL_M42),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001661 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe,
1662 "Dell Precision", STAC_9205_DELL_M43),
1663 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001664 "Dell Precision M4300", STAC_9205_DELL_M43),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001665 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
1666 "Dell Precision", STAC_9205_DELL_M43),
1667 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
1668 "Dell Inspiron", STAC_9205_DELL_M44),
1669 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
1670 "Dell Inspiron", STAC_9205_DELL_M44),
1671 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
1672 "Dell Inspiron", STAC_9205_DELL_M44),
1673 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
1674 "Dell Inspiron", STAC_9205_DELL_M44),
Takashi Iwaidfe495d2007-08-23 19:04:28 +02001675 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
1676 "unknown Dell", STAC_9205_DELL_M42),
Tobin Davisae0a8ed2007-08-13 15:50:29 +02001677 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
1678 "Dell Inspiron", STAC_9205_DELL_M44),
Matthew Ranostay3fa2ef72008-01-11 11:39:06 +01001679 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
1680 "Dell Vostro 1500", STAC_9205_DELL_M42),
Matt Porterf3302a52006-07-31 12:49:34 +02001681 {} /* terminator */
1682};
1683
Richard Fish11b44bb2006-08-23 18:31:34 +02001684static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
1685{
1686 int i;
1687 struct sigmatel_spec *spec = codec->spec;
1688
1689 if (! spec->bios_pin_configs) {
1690 spec->bios_pin_configs = kcalloc(spec->num_pins,
1691 sizeof(*spec->bios_pin_configs), GFP_KERNEL);
1692 if (! spec->bios_pin_configs)
1693 return -ENOMEM;
1694 }
1695
1696 for (i = 0; i < spec->num_pins; i++) {
1697 hda_nid_t nid = spec->pin_nids[i];
1698 unsigned int pin_cfg;
1699
1700 pin_cfg = snd_hda_codec_read(codec, nid, 0,
1701 AC_VERB_GET_CONFIG_DEFAULT, 0x00);
1702 snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
1703 nid, pin_cfg);
1704 spec->bios_pin_configs[i] = pin_cfg;
1705 }
1706
1707 return 0;
1708}
1709
Matthew Ranostay87d48362007-07-17 11:52:24 +02001710static void stac92xx_set_config_reg(struct hda_codec *codec,
1711 hda_nid_t pin_nid, unsigned int pin_config)
1712{
1713 int i;
1714 snd_hda_codec_write(codec, pin_nid, 0,
1715 AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
1716 pin_config & 0x000000ff);
1717 snd_hda_codec_write(codec, pin_nid, 0,
1718 AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
1719 (pin_config & 0x0000ff00) >> 8);
1720 snd_hda_codec_write(codec, pin_nid, 0,
1721 AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
1722 (pin_config & 0x00ff0000) >> 16);
1723 snd_hda_codec_write(codec, pin_nid, 0,
1724 AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
1725 pin_config >> 24);
1726 i = snd_hda_codec_read(codec, pin_nid, 0,
1727 AC_VERB_GET_CONFIG_DEFAULT,
1728 0x00);
1729 snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
1730 pin_nid, i);
1731}
1732
Matt2f2f4252005-04-13 14:45:30 +02001733static void stac92xx_set_config_regs(struct hda_codec *codec)
1734{
1735 int i;
1736 struct sigmatel_spec *spec = codec->spec;
Matt2f2f4252005-04-13 14:45:30 +02001737
Matthew Ranostay87d48362007-07-17 11:52:24 +02001738 if (!spec->pin_configs)
1739 return;
Richard Fish11b44bb2006-08-23 18:31:34 +02001740
Matthew Ranostay87d48362007-07-17 11:52:24 +02001741 for (i = 0; i < spec->num_pins; i++)
1742 stac92xx_set_config_reg(codec, spec->pin_nids[i],
1743 spec->pin_configs[i]);
Matt2f2f4252005-04-13 14:45:30 +02001744}
Matt2f2f4252005-04-13 14:45:30 +02001745
Matt2f2f4252005-04-13 14:45:30 +02001746/*
1747 * Analog playback callbacks
1748 */
1749static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
1750 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001751 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001752{
1753 struct sigmatel_spec *spec = codec->spec;
Takashi Iwai9a081602008-02-12 18:37:26 +01001754 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1755 hinfo);
Matt2f2f4252005-04-13 14:45:30 +02001756}
1757
1758static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
1759 struct hda_codec *codec,
1760 unsigned int stream_tag,
1761 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001762 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001763{
1764 struct sigmatel_spec *spec = codec->spec;
Matt Porter403d1942005-11-29 15:00:51 +01001765 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);
Matt2f2f4252005-04-13 14:45:30 +02001766}
1767
1768static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1769 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001770 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001771{
1772 struct sigmatel_spec *spec = codec->spec;
1773 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
1774}
1775
1776/*
Mattdabbed62005-06-14 10:19:34 +02001777 * Digital playback callbacks
1778 */
1779static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1780 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001781 struct snd_pcm_substream *substream)
Mattdabbed62005-06-14 10:19:34 +02001782{
1783 struct sigmatel_spec *spec = codec->spec;
1784 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1785}
1786
1787static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1788 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001789 struct snd_pcm_substream *substream)
Mattdabbed62005-06-14 10:19:34 +02001790{
1791 struct sigmatel_spec *spec = codec->spec;
1792 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1793}
1794
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001795static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
1796 struct hda_codec *codec,
1797 unsigned int stream_tag,
1798 unsigned int format,
1799 struct snd_pcm_substream *substream)
1800{
1801 struct sigmatel_spec *spec = codec->spec;
1802 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1803 stream_tag, format, substream);
1804}
1805
Mattdabbed62005-06-14 10:19:34 +02001806
1807/*
Matt2f2f4252005-04-13 14:45:30 +02001808 * Analog capture callbacks
1809 */
1810static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1811 struct hda_codec *codec,
1812 unsigned int stream_tag,
1813 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001814 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001815{
1816 struct sigmatel_spec *spec = codec->spec;
1817
1818 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1819 stream_tag, 0, format);
1820 return 0;
1821}
1822
1823static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1824 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001825 struct snd_pcm_substream *substream)
Matt2f2f4252005-04-13 14:45:30 +02001826{
1827 struct sigmatel_spec *spec = codec->spec;
1828
1829 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
1830 return 0;
1831}
1832
Mattdabbed62005-06-14 10:19:34 +02001833static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
1834 .substreams = 1,
1835 .channels_min = 2,
1836 .channels_max = 2,
1837 /* NID is set in stac92xx_build_pcms */
1838 .ops = {
1839 .open = stac92xx_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001840 .close = stac92xx_dig_playback_pcm_close,
1841 .prepare = stac92xx_dig_playback_pcm_prepare
Mattdabbed62005-06-14 10:19:34 +02001842 },
1843};
1844
1845static struct hda_pcm_stream stac92xx_pcm_digital_capture = {
1846 .substreams = 1,
1847 .channels_min = 2,
1848 .channels_max = 2,
1849 /* NID is set in stac92xx_build_pcms */
1850};
1851
Matt2f2f4252005-04-13 14:45:30 +02001852static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
1853 .substreams = 1,
1854 .channels_min = 2,
Mattc7d4b2f2005-06-27 14:59:41 +02001855 .channels_max = 8,
Matt2f2f4252005-04-13 14:45:30 +02001856 .nid = 0x02, /* NID to query formats and rates */
1857 .ops = {
1858 .open = stac92xx_playback_pcm_open,
1859 .prepare = stac92xx_playback_pcm_prepare,
1860 .cleanup = stac92xx_playback_pcm_cleanup
1861 },
1862};
1863
Matt Porter3cc08dc2006-01-23 15:27:49 +01001864static struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = {
1865 .substreams = 1,
1866 .channels_min = 2,
1867 .channels_max = 2,
1868 .nid = 0x06, /* NID to query formats and rates */
1869 .ops = {
1870 .open = stac92xx_playback_pcm_open,
1871 .prepare = stac92xx_playback_pcm_prepare,
1872 .cleanup = stac92xx_playback_pcm_cleanup
1873 },
1874};
1875
Matt2f2f4252005-04-13 14:45:30 +02001876static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
Matt2f2f4252005-04-13 14:45:30 +02001877 .channels_min = 2,
1878 .channels_max = 2,
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02001879 /* NID + .substreams is set in stac92xx_build_pcms */
Matt2f2f4252005-04-13 14:45:30 +02001880 .ops = {
1881 .prepare = stac92xx_capture_pcm_prepare,
1882 .cleanup = stac92xx_capture_pcm_cleanup
1883 },
1884};
1885
1886static int stac92xx_build_pcms(struct hda_codec *codec)
1887{
1888 struct sigmatel_spec *spec = codec->spec;
1889 struct hda_pcm *info = spec->pcm_rec;
1890
1891 codec->num_pcms = 1;
1892 codec->pcm_info = info;
1893
Mattc7d4b2f2005-06-27 14:59:41 +02001894 info->name = "STAC92xx Analog";
Matt2f2f4252005-04-13 14:45:30 +02001895 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
Matt2f2f4252005-04-13 14:45:30 +02001896 info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
Matt Porter3cc08dc2006-01-23 15:27:49 +01001897 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02001898 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs;
Matt Porter3cc08dc2006-01-23 15:27:49 +01001899
1900 if (spec->alt_switch) {
1901 codec->num_pcms++;
1902 info++;
1903 info->name = "STAC92xx Analog Alt";
1904 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback;
1905 }
Matt2f2f4252005-04-13 14:45:30 +02001906
Mattdabbed62005-06-14 10:19:34 +02001907 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1908 codec->num_pcms++;
1909 info++;
1910 info->name = "STAC92xx Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001911 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Mattdabbed62005-06-14 10:19:34 +02001912 if (spec->multiout.dig_out_nid) {
1913 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
1914 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
1915 }
1916 if (spec->dig_in_nid) {
1917 info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture;
1918 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
1919 }
1920 }
1921
Matt2f2f4252005-04-13 14:45:30 +02001922 return 0;
1923}
1924
Takashi Iwaic960a032006-03-23 17:06:28 +01001925static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
1926{
1927 unsigned int pincap = snd_hda_param_read(codec, nid,
1928 AC_PAR_PIN_CAP);
1929 pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
1930 if (pincap & AC_PINCAP_VREF_100)
1931 return AC_PINCTL_VREF_100;
1932 if (pincap & AC_PINCAP_VREF_80)
1933 return AC_PINCTL_VREF_80;
1934 if (pincap & AC_PINCAP_VREF_50)
1935 return AC_PINCTL_VREF_50;
1936 if (pincap & AC_PINCAP_VREF_GRD)
1937 return AC_PINCTL_VREF_GRD;
1938 return 0;
1939}
1940
Matt Porter403d1942005-11-29 15:00:51 +01001941static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
1942
1943{
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001944 snd_hda_codec_write_cache(codec, nid, 0,
1945 AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
Matt Porter403d1942005-11-29 15:00:51 +01001946}
1947
Takashi Iwaia5ce8892007-07-23 15:42:26 +02001948#define stac92xx_io_switch_info snd_ctl_boolean_mono_info
Matt Porter403d1942005-11-29 15:00:51 +01001949
1950static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1951{
1952 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1953 struct sigmatel_spec *spec = codec->spec;
1954 int io_idx = kcontrol-> private_value & 0xff;
1955
1956 ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
1957 return 0;
1958}
1959
1960static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1961{
1962 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1963 struct sigmatel_spec *spec = codec->spec;
1964 hda_nid_t nid = kcontrol->private_value >> 8;
1965 int io_idx = kcontrol-> private_value & 0xff;
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001966 unsigned short val = !!ucontrol->value.integer.value[0];
Matt Porter403d1942005-11-29 15:00:51 +01001967
1968 spec->io_switch[io_idx] = val;
1969
1970 if (val)
1971 stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
Takashi Iwaic960a032006-03-23 17:06:28 +01001972 else {
1973 unsigned int pinctl = AC_PINCTL_IN_EN;
1974 if (io_idx) /* set VREF for mic */
1975 pinctl |= stac92xx_get_vref(codec, nid);
1976 stac92xx_auto_set_pinctl(codec, nid, pinctl);
1977 }
Jiang Zhe40c1d302007-11-12 13:05:16 +01001978
1979 /* check the auto-mute again: we need to mute/unmute the speaker
1980 * appropriately according to the pin direction
1981 */
1982 if (spec->hp_detect)
1983 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
1984
Matt Porter403d1942005-11-29 15:00:51 +01001985 return 1;
1986}
1987
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02001988#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info
1989
1990static int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol,
1991 struct snd_ctl_elem_value *ucontrol)
1992{
1993 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1994 struct sigmatel_spec *spec = codec->spec;
1995
1996 ucontrol->value.integer.value[0] = spec->clfe_swap;
1997 return 0;
1998}
1999
2000static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
2001 struct snd_ctl_elem_value *ucontrol)
2002{
2003 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2004 struct sigmatel_spec *spec = codec->spec;
2005 hda_nid_t nid = kcontrol->private_value & 0xff;
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002006 unsigned int val = !!ucontrol->value.integer.value[0];
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002007
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002008 if (spec->clfe_swap == val)
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002009 return 0;
2010
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002011 spec->clfe_swap = val;
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002012
2013 snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
2014 spec->clfe_swap ? 0x4 : 0x0);
2015
2016 return 1;
2017}
2018
Matt Porter403d1942005-11-29 15:00:51 +01002019#define STAC_CODEC_IO_SWITCH(xname, xpval) \
2020 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2021 .name = xname, \
2022 .index = 0, \
2023 .info = stac92xx_io_switch_info, \
2024 .get = stac92xx_io_switch_get, \
2025 .put = stac92xx_io_switch_put, \
2026 .private_value = xpval, \
2027 }
2028
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002029#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \
2030 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2031 .name = xname, \
2032 .index = 0, \
2033 .info = stac92xx_clfe_switch_info, \
2034 .get = stac92xx_clfe_switch_get, \
2035 .put = stac92xx_clfe_switch_put, \
2036 .private_value = xpval, \
2037 }
Matt Porter403d1942005-11-29 15:00:51 +01002038
Mattc7d4b2f2005-06-27 14:59:41 +02002039enum {
2040 STAC_CTL_WIDGET_VOL,
2041 STAC_CTL_WIDGET_MUTE,
Matthew Ranostay09a99952008-01-24 11:49:21 +01002042 STAC_CTL_WIDGET_MONO_MUX,
Matt Porter403d1942005-11-29 15:00:51 +01002043 STAC_CTL_WIDGET_IO_SWITCH,
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002044 STAC_CTL_WIDGET_CLFE_SWITCH
Mattc7d4b2f2005-06-27 14:59:41 +02002045};
2046
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01002047static struct snd_kcontrol_new stac92xx_control_templates[] = {
Mattc7d4b2f2005-06-27 14:59:41 +02002048 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2049 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Matthew Ranostay09a99952008-01-24 11:49:21 +01002050 STAC_MONO_MUX,
Matt Porter403d1942005-11-29 15:00:51 +01002051 STAC_CODEC_IO_SWITCH(NULL, 0),
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002052 STAC_CODEC_CLFE_SWITCH(NULL, 0),
Mattc7d4b2f2005-06-27 14:59:41 +02002053};
2054
2055/* add dynamic controls */
2056static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val)
2057{
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01002058 struct snd_kcontrol_new *knew;
Mattc7d4b2f2005-06-27 14:59:41 +02002059
2060 if (spec->num_kctl_used >= spec->num_kctl_alloc) {
2061 int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
2062
2063 knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
2064 if (! knew)
2065 return -ENOMEM;
2066 if (spec->kctl_alloc) {
2067 memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
2068 kfree(spec->kctl_alloc);
2069 }
2070 spec->kctl_alloc = knew;
2071 spec->num_kctl_alloc = num;
2072 }
2073
2074 knew = &spec->kctl_alloc[spec->num_kctl_used];
2075 *knew = stac92xx_control_templates[type];
Takashi Iwai82fe0c52005-06-30 10:54:33 +02002076 knew->name = kstrdup(name, GFP_KERNEL);
Mattc7d4b2f2005-06-27 14:59:41 +02002077 if (! knew->name)
2078 return -ENOMEM;
2079 knew->private_value = val;
2080 spec->num_kctl_used++;
2081 return 0;
2082}
2083
Matt Porter403d1942005-11-29 15:00:51 +01002084/* flag inputs as additional dynamic lineouts */
2085static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
2086{
2087 struct sigmatel_spec *spec = codec->spec;
Steve Longerbeam7b043892007-05-03 20:50:03 +02002088 unsigned int wcaps, wtype;
2089 int i, num_dacs = 0;
2090
2091 /* use the wcaps cache to count all DACs available for line-outs */
2092 for (i = 0; i < codec->num_nodes; i++) {
2093 wcaps = codec->wcaps[i];
2094 wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01002095
Steve Longerbeam7b043892007-05-03 20:50:03 +02002096 if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
2097 num_dacs++;
2098 }
Matt Porter403d1942005-11-29 15:00:51 +01002099
Steve Longerbeam7b043892007-05-03 20:50:03 +02002100 snd_printdd("%s: total dac count=%d\n", __func__, num_dacs);
2101
Matt Porter403d1942005-11-29 15:00:51 +01002102 switch (cfg->line_outs) {
2103 case 3:
2104 /* add line-in as side */
Steve Longerbeam7b043892007-05-03 20:50:03 +02002105 if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002106 cfg->line_out_pins[cfg->line_outs] =
2107 cfg->input_pins[AUTO_PIN_LINE];
Matt Porter403d1942005-11-29 15:00:51 +01002108 spec->line_switch = 1;
2109 cfg->line_outs++;
2110 }
2111 break;
2112 case 2:
2113 /* add line-in as clfe and mic as side */
Steve Longerbeam7b043892007-05-03 20:50:03 +02002114 if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002115 cfg->line_out_pins[cfg->line_outs] =
2116 cfg->input_pins[AUTO_PIN_LINE];
Matt Porter403d1942005-11-29 15:00:51 +01002117 spec->line_switch = 1;
2118 cfg->line_outs++;
2119 }
Steve Longerbeam7b043892007-05-03 20:50:03 +02002120 if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002121 cfg->line_out_pins[cfg->line_outs] =
2122 cfg->input_pins[AUTO_PIN_MIC];
Matt Porter403d1942005-11-29 15:00:51 +01002123 spec->mic_switch = 1;
2124 cfg->line_outs++;
2125 }
2126 break;
2127 case 1:
2128 /* add line-in as surr and mic as clfe */
Steve Longerbeam7b043892007-05-03 20:50:03 +02002129 if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002130 cfg->line_out_pins[cfg->line_outs] =
2131 cfg->input_pins[AUTO_PIN_LINE];
Matt Porter403d1942005-11-29 15:00:51 +01002132 spec->line_switch = 1;
2133 cfg->line_outs++;
2134 }
Steve Longerbeam7b043892007-05-03 20:50:03 +02002135 if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) {
Takashi Iwaic480f792007-09-03 09:43:38 +02002136 cfg->line_out_pins[cfg->line_outs] =
2137 cfg->input_pins[AUTO_PIN_MIC];
Matt Porter403d1942005-11-29 15:00:51 +01002138 spec->mic_switch = 1;
2139 cfg->line_outs++;
2140 }
2141 break;
2142 }
2143
2144 return 0;
2145}
2146
Steve Longerbeam7b043892007-05-03 20:50:03 +02002147
2148static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
2149{
2150 int i;
2151
2152 for (i = 0; i < spec->multiout.num_dacs; i++) {
2153 if (spec->multiout.dac_nids[i] == nid)
2154 return 1;
2155 }
2156
2157 return 0;
2158}
2159
Matt Porter3cc08dc2006-01-23 15:27:49 +01002160/*
Steve Longerbeam7b043892007-05-03 20:50:03 +02002161 * Fill in the dac_nids table from the parsed pin configuration
2162 * This function only works when every pin in line_out_pins[]
2163 * contains atleast one DAC in its connection list. Some 92xx
2164 * codecs are not connected directly to a DAC, such as the 9200
2165 * and 9202/925x. For those, dac_nids[] must be hard-coded.
Matt Porter3cc08dc2006-01-23 15:27:49 +01002166 */
Takashi Iwai19039bd2006-06-28 15:52:16 +02002167static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
Takashi Iwaidf802952007-07-02 19:18:00 +02002168 struct auto_pin_cfg *cfg)
Mattc7d4b2f2005-06-27 14:59:41 +02002169{
2170 struct sigmatel_spec *spec = codec->spec;
Steve Longerbeam7b043892007-05-03 20:50:03 +02002171 int i, j, conn_len = 0;
2172 hda_nid_t nid, conn[HDA_MAX_CONNECTIONS];
2173 unsigned int wcaps, wtype;
2174
Mattc7d4b2f2005-06-27 14:59:41 +02002175 for (i = 0; i < cfg->line_outs; i++) {
2176 nid = cfg->line_out_pins[i];
Steve Longerbeam7b043892007-05-03 20:50:03 +02002177 conn_len = snd_hda_get_connections(codec, nid, conn,
2178 HDA_MAX_CONNECTIONS);
2179 for (j = 0; j < conn_len; j++) {
2180 wcaps = snd_hda_param_read(codec, conn[j],
2181 AC_PAR_AUDIO_WIDGET_CAP);
2182 wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
Steve Longerbeam7b043892007-05-03 20:50:03 +02002183 if (wtype != AC_WID_AUD_OUT ||
2184 (wcaps & AC_WCAP_DIGITAL))
2185 continue;
2186 /* conn[j] is a DAC routed to this line-out */
2187 if (!is_in_dac_nids(spec, conn[j]))
2188 break;
2189 }
2190
2191 if (j == conn_len) {
Takashi Iwaidf802952007-07-02 19:18:00 +02002192 if (spec->multiout.num_dacs > 0) {
2193 /* we have already working output pins,
2194 * so let's drop the broken ones again
2195 */
2196 cfg->line_outs = spec->multiout.num_dacs;
2197 break;
2198 }
Steve Longerbeam7b043892007-05-03 20:50:03 +02002199 /* error out, no available DAC found */
2200 snd_printk(KERN_ERR
2201 "%s: No available DAC for pin 0x%x\n",
2202 __func__, nid);
2203 return -ENODEV;
2204 }
2205
2206 spec->multiout.dac_nids[i] = conn[j];
2207 spec->multiout.num_dacs++;
2208 if (conn_len > 1) {
2209 /* select this DAC in the pin's input mux */
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002210 snd_hda_codec_write_cache(codec, nid, 0,
2211 AC_VERB_SET_CONNECT_SEL, j);
Steve Longerbeam7b043892007-05-03 20:50:03 +02002212
2213 }
Mattc7d4b2f2005-06-27 14:59:41 +02002214 }
2215
Steve Longerbeam7b043892007-05-03 20:50:03 +02002216 snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
2217 spec->multiout.num_dacs,
2218 spec->multiout.dac_nids[0],
2219 spec->multiout.dac_nids[1],
2220 spec->multiout.dac_nids[2],
2221 spec->multiout.dac_nids[3],
2222 spec->multiout.dac_nids[4]);
Mattc7d4b2f2005-06-27 14:59:41 +02002223 return 0;
2224}
2225
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002226/* create volume control/switch for the given prefx type */
2227static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs)
2228{
2229 char name[32];
2230 int err;
2231
2232 sprintf(name, "%s Playback Volume", pfx);
2233 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
2234 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
2235 if (err < 0)
2236 return err;
2237 sprintf(name, "%s Playback Switch", pfx);
2238 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
2239 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
2240 if (err < 0)
2241 return err;
2242 return 0;
2243}
2244
Mattc7d4b2f2005-06-27 14:59:41 +02002245/* add playback controls from the parsed DAC table */
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002246static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
Takashi Iwai19039bd2006-06-28 15:52:16 +02002247 const struct auto_pin_cfg *cfg)
Mattc7d4b2f2005-06-27 14:59:41 +02002248{
Takashi Iwai19039bd2006-06-28 15:52:16 +02002249 static const char *chname[4] = {
2250 "Front", "Surround", NULL /*CLFE*/, "Side"
2251 };
Mattc7d4b2f2005-06-27 14:59:41 +02002252 hda_nid_t nid;
2253 int i, err;
2254
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002255 struct sigmatel_spec *spec = codec->spec;
Matthew Ranostayb5895dc2008-01-25 15:24:50 +01002256 unsigned int wid_caps, pincap;
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002257
2258
Mattc7d4b2f2005-06-27 14:59:41 +02002259 for (i = 0; i < cfg->line_outs; i++) {
Matt Porter403d1942005-11-29 15:00:51 +01002260 if (!spec->multiout.dac_nids[i])
Mattc7d4b2f2005-06-27 14:59:41 +02002261 continue;
2262
2263 nid = spec->multiout.dac_nids[i];
2264
2265 if (i == 2) {
2266 /* Center/LFE */
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002267 err = create_controls(spec, "Center", nid, 1);
2268 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002269 return err;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002270 err = create_controls(spec, "LFE", nid, 2);
2271 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002272 return err;
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002273
2274 wid_caps = get_wcaps(codec, nid);
2275
2276 if (wid_caps & AC_WCAP_LR_SWAP) {
2277 err = stac92xx_add_control(spec,
2278 STAC_CTL_WIDGET_CLFE_SWITCH,
2279 "Swap Center/LFE Playback Switch", nid);
2280
2281 if (err < 0)
2282 return err;
2283 }
2284
Mattc7d4b2f2005-06-27 14:59:41 +02002285 } else {
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002286 err = create_controls(spec, chname[i], nid, 3);
2287 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002288 return err;
2289 }
2290 }
2291
Matthew Ranostayb5895dc2008-01-25 15:24:50 +01002292 if (spec->line_switch) {
2293 nid = cfg->input_pins[AUTO_PIN_LINE];
2294 pincap = snd_hda_param_read(codec, nid,
2295 AC_PAR_PIN_CAP);
2296 if (pincap & AC_PINCAP_OUT) {
2297 err = stac92xx_add_control(spec,
2298 STAC_CTL_WIDGET_IO_SWITCH,
2299 "Line In as Output Switch", nid << 8);
2300 if (err < 0)
2301 return err;
2302 }
2303 }
Matt Porter403d1942005-11-29 15:00:51 +01002304
Matthew Ranostayb5895dc2008-01-25 15:24:50 +01002305 if (spec->mic_switch) {
Matthew Ranostaycace16f2008-01-30 14:58:38 +01002306 unsigned int def_conf;
Matthew Ranostayb5895dc2008-01-25 15:24:50 +01002307 nid = cfg->input_pins[AUTO_PIN_MIC];
Matthew Ranostaycace16f2008-01-30 14:58:38 +01002308 def_conf = snd_hda_codec_read(codec, nid, 0,
2309 AC_VERB_GET_CONFIG_DEFAULT, 0);
2310
2311 /* some laptops have an internal analog microphone
2312 * which can't be used as a output */
2313 if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
2314 pincap = snd_hda_param_read(codec, nid,
2315 AC_PAR_PIN_CAP);
2316 if (pincap & AC_PINCAP_OUT) {
2317 err = stac92xx_add_control(spec,
2318 STAC_CTL_WIDGET_IO_SWITCH,
2319 "Mic as Output Switch", (nid << 8) | 1);
2320 if (err < 0)
2321 return err;
2322 }
Matthew Ranostayb5895dc2008-01-25 15:24:50 +01002323 }
2324 }
Matt Porter403d1942005-11-29 15:00:51 +01002325
Mattc7d4b2f2005-06-27 14:59:41 +02002326 return 0;
2327}
2328
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002329static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
2330{
Steve Longerbeam7b043892007-05-03 20:50:03 +02002331 if (is_in_dac_nids(spec, nid))
2332 return 1;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002333 if (spec->multiout.hp_nid == nid)
2334 return 1;
2335 return 0;
2336}
2337
2338static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
2339{
2340 if (!spec->multiout.hp_nid)
2341 spec->multiout.hp_nid = nid;
2342 else if (spec->multiout.num_dacs > 4) {
2343 printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
2344 return 1;
2345 } else {
2346 spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
2347 spec->multiout.num_dacs++;
2348 }
2349 return 0;
2350}
2351
2352/* add playback controls for Speaker and HP outputs */
2353static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
2354 struct auto_pin_cfg *cfg)
Mattc7d4b2f2005-06-27 14:59:41 +02002355{
2356 struct sigmatel_spec *spec = codec->spec;
Mattc7d4b2f2005-06-27 14:59:41 +02002357 hda_nid_t nid;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002358 int i, old_num_dacs, err;
Mattc7d4b2f2005-06-27 14:59:41 +02002359
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002360 old_num_dacs = spec->multiout.num_dacs;
2361 for (i = 0; i < cfg->hp_outs; i++) {
2362 unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
2363 if (wid_caps & AC_WCAP_UNSOL_CAP)
2364 spec->hp_detect = 1;
2365 nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
2366 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
2367 if (check_in_dac_nids(spec, nid))
2368 nid = 0;
2369 if (! nid)
Mattc7d4b2f2005-06-27 14:59:41 +02002370 continue;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002371 add_spec_dacs(spec, nid);
2372 }
2373 for (i = 0; i < cfg->speaker_outs; i++) {
Steve Longerbeam7b043892007-05-03 20:50:03 +02002374 nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0,
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002375 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
2376 if (check_in_dac_nids(spec, nid))
2377 nid = 0;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002378 if (! nid)
2379 continue;
2380 add_spec_dacs(spec, nid);
Mattc7d4b2f2005-06-27 14:59:41 +02002381 }
Matthew Ranostay1b290a52007-07-12 15:17:34 +02002382 for (i = 0; i < cfg->line_outs; i++) {
2383 nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
2384 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
2385 if (check_in_dac_nids(spec, nid))
2386 nid = 0;
2387 if (! nid)
2388 continue;
2389 add_spec_dacs(spec, nid);
2390 }
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002391 for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
2392 static const char *pfxs[] = {
2393 "Speaker", "External Speaker", "Speaker2",
2394 };
2395 err = create_controls(spec, pfxs[i - old_num_dacs],
2396 spec->multiout.dac_nids[i], 3);
2397 if (err < 0)
2398 return err;
2399 }
2400 if (spec->multiout.hp_nid) {
2401 const char *pfx;
Takashi Iwai6020c002007-11-19 11:56:26 +01002402 if (old_num_dacs == spec->multiout.num_dacs)
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002403 pfx = "Master";
2404 else
2405 pfx = "Headphone";
2406 err = create_controls(spec, pfx, spec->multiout.hp_nid, 3);
2407 if (err < 0)
2408 return err;
2409 }
Mattc7d4b2f2005-06-27 14:59:41 +02002410
2411 return 0;
2412}
2413
Matthew Ranostayb22b4822008-01-22 12:32:30 +01002414/* labels for mono mux outputs */
2415static const char *stac92xx_mono_labels[3] = {
2416 "DAC0", "DAC1", "Mixer"
2417};
2418
2419/* create mono mux for mono out on capable codecs */
2420static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
2421{
2422 struct sigmatel_spec *spec = codec->spec;
2423 struct hda_input_mux *mono_mux = &spec->private_mono_mux;
2424 int i, num_cons;
2425 hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)];
2426
2427 num_cons = snd_hda_get_connections(codec,
2428 spec->mono_nid,
2429 con_lst,
2430 HDA_MAX_NUM_INPUTS);
2431 if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
2432 return -EINVAL;
2433
2434 for (i = 0; i < num_cons; i++) {
2435 mono_mux->items[mono_mux->num_items].label =
2436 stac92xx_mono_labels[i];
2437 mono_mux->items[mono_mux->num_items].index = i;
2438 mono_mux->num_items++;
2439 }
Matthew Ranostay09a99952008-01-24 11:49:21 +01002440
2441 return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
2442 "Mono Mux", spec->mono_nid);
Matthew Ranostayb22b4822008-01-22 12:32:30 +01002443}
2444
Matt Porter8b657272006-10-26 17:12:59 +02002445/* labels for dmic mux inputs */
Adrian Bunkddc2cec2006-11-20 12:03:44 +01002446static const char *stac92xx_dmic_labels[5] = {
Matt Porter8b657272006-10-26 17:12:59 +02002447 "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
2448 "Digital Mic 3", "Digital Mic 4"
2449};
2450
2451/* create playback/capture controls for input pins on dmic capable codecs */
2452static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
2453 const struct auto_pin_cfg *cfg)
2454{
2455 struct sigmatel_spec *spec = codec->spec;
2456 struct hda_input_mux *dimux = &spec->private_dimux;
2457 hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002458 int err, i, j;
2459 char name[32];
Matt Porter8b657272006-10-26 17:12:59 +02002460
2461 dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
2462 dimux->items[dimux->num_items].index = 0;
2463 dimux->num_items++;
2464
2465 for (i = 0; i < spec->num_dmics; i++) {
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002466 hda_nid_t nid;
Matt Porter8b657272006-10-26 17:12:59 +02002467 int index;
2468 int num_cons;
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002469 unsigned int wcaps;
Matt Porter8b657272006-10-26 17:12:59 +02002470 unsigned int def_conf;
2471
2472 def_conf = snd_hda_codec_read(codec,
2473 spec->dmic_nids[i],
2474 0,
2475 AC_VERB_GET_CONFIG_DEFAULT,
2476 0);
2477 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
2478 continue;
2479
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002480 nid = spec->dmic_nids[i];
Matt Porter8b657272006-10-26 17:12:59 +02002481 num_cons = snd_hda_get_connections(codec,
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01002482 spec->dmux_nids[0],
Matt Porter8b657272006-10-26 17:12:59 +02002483 con_lst,
2484 HDA_MAX_NUM_INPUTS);
2485 for (j = 0; j < num_cons; j++)
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002486 if (con_lst[j] == nid) {
Matt Porter8b657272006-10-26 17:12:59 +02002487 index = j;
2488 goto found;
2489 }
2490 continue;
2491found:
Matthew Ranostay0678acc2008-01-08 12:10:50 +01002492 wcaps = get_wcaps(codec, nid);
2493
2494 if (wcaps & AC_WCAP_OUT_AMP) {
2495 sprintf(name, "%s Capture Volume",
2496 stac92xx_dmic_labels[dimux->num_items]);
2497
2498 err = stac92xx_add_control(spec,
2499 STAC_CTL_WIDGET_VOL,
2500 name,
2501 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2502 if (err < 0)
2503 return err;
2504 }
2505
Matt Porter8b657272006-10-26 17:12:59 +02002506 dimux->items[dimux->num_items].label =
2507 stac92xx_dmic_labels[dimux->num_items];
2508 dimux->items[dimux->num_items].index = index;
2509 dimux->num_items++;
2510 }
2511
2512 return 0;
2513}
2514
Mattc7d4b2f2005-06-27 14:59:41 +02002515/* create playback/capture controls for input pins */
2516static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
2517{
2518 struct sigmatel_spec *spec = codec->spec;
Mattc7d4b2f2005-06-27 14:59:41 +02002519 struct hda_input_mux *imux = &spec->private_imux;
2520 hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
2521 int i, j, k;
2522
2523 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai314634b2006-09-21 11:56:18 +02002524 int index;
Mattc7d4b2f2005-06-27 14:59:41 +02002525
Takashi Iwai314634b2006-09-21 11:56:18 +02002526 if (!cfg->input_pins[i])
2527 continue;
2528 index = -1;
2529 for (j = 0; j < spec->num_muxes; j++) {
2530 int num_cons;
2531 num_cons = snd_hda_get_connections(codec,
2532 spec->mux_nids[j],
2533 con_lst,
2534 HDA_MAX_NUM_INPUTS);
2535 for (k = 0; k < num_cons; k++)
2536 if (con_lst[k] == cfg->input_pins[i]) {
2537 index = k;
2538 goto found;
2539 }
Mattc7d4b2f2005-06-27 14:59:41 +02002540 }
Takashi Iwai314634b2006-09-21 11:56:18 +02002541 continue;
2542 found:
2543 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
2544 imux->items[imux->num_items].index = index;
2545 imux->num_items++;
Mattc7d4b2f2005-06-27 14:59:41 +02002546 }
2547
Steve Longerbeam7b043892007-05-03 20:50:03 +02002548 if (imux->num_items) {
Sam Revitch62fe78e2006-05-10 15:09:17 +02002549 /*
2550 * Set the current input for the muxes.
2551 * The STAC9221 has two input muxes with identical source
2552 * NID lists. Hopefully this won't get confused.
2553 */
2554 for (i = 0; i < spec->num_muxes; i++) {
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002555 snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0,
2556 AC_VERB_SET_CONNECT_SEL,
2557 imux->items[0].index);
Sam Revitch62fe78e2006-05-10 15:09:17 +02002558 }
2559 }
2560
Mattc7d4b2f2005-06-27 14:59:41 +02002561 return 0;
2562}
2563
Mattc7d4b2f2005-06-27 14:59:41 +02002564static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
2565{
2566 struct sigmatel_spec *spec = codec->spec;
2567 int i;
2568
2569 for (i = 0; i < spec->autocfg.line_outs; i++) {
2570 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2571 stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
2572 }
2573}
2574
2575static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
2576{
2577 struct sigmatel_spec *spec = codec->spec;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002578 int i;
Mattc7d4b2f2005-06-27 14:59:41 +02002579
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002580 for (i = 0; i < spec->autocfg.hp_outs; i++) {
2581 hda_nid_t pin;
2582 pin = spec->autocfg.hp_pins[i];
2583 if (pin) /* connect to front */
2584 stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
2585 }
2586 for (i = 0; i < spec->autocfg.speaker_outs; i++) {
2587 hda_nid_t pin;
2588 pin = spec->autocfg.speaker_pins[i];
2589 if (pin) /* connect to front */
2590 stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
2591 }
Mattc7d4b2f2005-06-27 14:59:41 +02002592}
2593
Matt Porter3cc08dc2006-01-23 15:27:49 +01002594static 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 +02002595{
2596 struct sigmatel_spec *spec = codec->spec;
2597 int err;
Jiang Zhebcecd9b2007-11-12 12:57:03 +01002598 int hp_speaker_swap = 0;
Mattc7d4b2f2005-06-27 14:59:41 +02002599
Matt Porter8b657272006-10-26 17:12:59 +02002600 if ((err = snd_hda_parse_pin_def_config(codec,
2601 &spec->autocfg,
2602 spec->dmic_nids)) < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002603 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002604 if (! spec->autocfg.line_outs)
Matt Porter869264c2006-01-25 19:20:50 +01002605 return 0; /* can't find valid pin config */
Takashi Iwai19039bd2006-06-28 15:52:16 +02002606
Jiang Zhebcecd9b2007-11-12 12:57:03 +01002607 /* If we have no real line-out pin and multiple hp-outs, HPs should
2608 * be set up as multi-channel outputs.
2609 */
2610 if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
2611 spec->autocfg.hp_outs > 1) {
2612 /* Copy hp_outs to line_outs, backup line_outs in
2613 * speaker_outs so that the following routines can handle
2614 * HP pins as primary outputs.
2615 */
2616 memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
2617 sizeof(spec->autocfg.line_out_pins));
2618 spec->autocfg.speaker_outs = spec->autocfg.line_outs;
2619 memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
2620 sizeof(spec->autocfg.hp_pins));
2621 spec->autocfg.line_outs = spec->autocfg.hp_outs;
2622 hp_speaker_swap = 1;
2623 }
Matthew Ranostay09a99952008-01-24 11:49:21 +01002624 if (spec->autocfg.mono_out_pin) {
2625 int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin)
2626 & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
2627 u32 caps = query_amp_caps(codec,
2628 spec->autocfg.mono_out_pin, dir);
2629 hda_nid_t conn_list[1];
2630
2631 /* get the mixer node and then the mono mux if it exists */
2632 if (snd_hda_get_connections(codec,
2633 spec->autocfg.mono_out_pin, conn_list, 1) &&
2634 snd_hda_get_connections(codec, conn_list[0],
2635 conn_list, 1)) {
2636
2637 int wcaps = get_wcaps(codec, conn_list[0]);
2638 int wid_type = (wcaps & AC_WCAP_TYPE)
2639 >> AC_WCAP_TYPE_SHIFT;
2640 /* LR swap check, some stac925x have a mux that
2641 * changes the DACs output path instead of the
2642 * mono-mux path.
2643 */
2644 if (wid_type == AC_WID_AUD_SEL &&
2645 !(wcaps & AC_WCAP_LR_SWAP))
2646 spec->mono_nid = conn_list[0];
2647 }
2648 /* all mono outs have a least a mute/unmute switch */
2649 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
2650 "Mono Playback Switch",
2651 HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
2652 1, 0, dir));
2653 if (err < 0)
2654 return err;
2655 /* check to see if there is volume support for the amp */
2656 if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
2657 err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
2658 "Mono Playback Volume",
2659 HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
2660 1, 0, dir));
2661 if (err < 0)
2662 return err;
2663 }
2664
2665 stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
2666 AC_PINCTL_OUT_EN);
2667 }
Jiang Zhebcecd9b2007-11-12 12:57:03 +01002668
Matt Porter403d1942005-11-29 15:00:51 +01002669 if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
2670 return err;
Takashi Iwai19039bd2006-06-28 15:52:16 +02002671 if (spec->multiout.num_dacs == 0)
2672 if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2673 return err;
Mattc7d4b2f2005-06-27 14:59:41 +02002674
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002675 err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg);
2676
2677 if (err < 0)
2678 return err;
2679
Jiang Zhebcecd9b2007-11-12 12:57:03 +01002680 if (hp_speaker_swap == 1) {
2681 /* Restore the hp_outs and line_outs */
2682 memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
2683 sizeof(spec->autocfg.line_out_pins));
2684 spec->autocfg.hp_outs = spec->autocfg.line_outs;
2685 memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins,
2686 sizeof(spec->autocfg.speaker_pins));
2687 spec->autocfg.line_outs = spec->autocfg.speaker_outs;
2688 memset(spec->autocfg.speaker_pins, 0,
2689 sizeof(spec->autocfg.speaker_pins));
2690 spec->autocfg.speaker_outs = 0;
2691 }
2692
Maxim Levitsky0fb87bb2007-09-03 15:29:04 +02002693 err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
2694
2695 if (err < 0)
2696 return err;
2697
2698 err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
2699
2700 if (err < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002701 return err;
2702
Matthew Ranostayb22b4822008-01-22 12:32:30 +01002703 if (spec->mono_nid > 0) {
2704 err = stac92xx_auto_create_mono_output_ctls(codec);
2705 if (err < 0)
2706 return err;
2707 }
2708
Matt Porter8b657272006-10-26 17:12:59 +02002709 if (spec->num_dmics > 0)
2710 if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
2711 &spec->autocfg)) < 0)
2712 return err;
2713
Mattc7d4b2f2005-06-27 14:59:41 +02002714 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Matt Porter403d1942005-11-29 15:00:51 +01002715 if (spec->multiout.max_channels > 2)
Mattc7d4b2f2005-06-27 14:59:41 +02002716 spec->surr_switch = 1;
Mattc7d4b2f2005-06-27 14:59:41 +02002717
Takashi Iwai82bc9552006-03-21 11:24:42 +01002718 if (spec->autocfg.dig_out_pin)
Matt Porter3cc08dc2006-01-23 15:27:49 +01002719 spec->multiout.dig_out_nid = dig_out;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002720 if (spec->autocfg.dig_in_pin)
Matt Porter3cc08dc2006-01-23 15:27:49 +01002721 spec->dig_in_nid = dig_in;
Mattc7d4b2f2005-06-27 14:59:41 +02002722
2723 if (spec->kctl_alloc)
2724 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2725
2726 spec->input_mux = &spec->private_imux;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01002727 if (!spec->dinput_mux)
2728 spec->dinput_mux = &spec->private_dimux;
Matthew Ranostayb22b4822008-01-22 12:32:30 +01002729 spec->mono_mux = &spec->private_mono_mux;
Mattc7d4b2f2005-06-27 14:59:41 +02002730
2731 return 1;
2732}
2733
Takashi Iwai82bc9552006-03-21 11:24:42 +01002734/* add playback controls for HP output */
2735static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
2736 struct auto_pin_cfg *cfg)
2737{
2738 struct sigmatel_spec *spec = codec->spec;
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002739 hda_nid_t pin = cfg->hp_pins[0];
Takashi Iwai82bc9552006-03-21 11:24:42 +01002740 unsigned int wid_caps;
2741
2742 if (! pin)
2743 return 0;
2744
2745 wid_caps = get_wcaps(codec, pin);
Takashi Iwai505cb342006-03-27 12:51:52 +02002746 if (wid_caps & AC_WCAP_UNSOL_CAP)
Takashi Iwai82bc9552006-03-21 11:24:42 +01002747 spec->hp_detect = 1;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002748
2749 return 0;
2750}
2751
Richard Fish160ea0d2006-09-06 13:58:25 +02002752/* add playback controls for LFE output */
2753static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
2754 struct auto_pin_cfg *cfg)
2755{
2756 struct sigmatel_spec *spec = codec->spec;
2757 int err;
2758 hda_nid_t lfe_pin = 0x0;
2759 int i;
2760
2761 /*
2762 * search speaker outs and line outs for a mono speaker pin
2763 * with an amp. If one is found, add LFE controls
2764 * for it.
2765 */
2766 for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
2767 hda_nid_t pin = spec->autocfg.speaker_pins[i];
2768 unsigned long wcaps = get_wcaps(codec, pin);
2769 wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
2770 if (wcaps == AC_WCAP_OUT_AMP)
2771 /* found a mono speaker with an amp, must be lfe */
2772 lfe_pin = pin;
2773 }
2774
2775 /* if speaker_outs is 0, then speakers may be in line_outs */
2776 if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
2777 for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
2778 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2779 unsigned long cfg;
2780 cfg = snd_hda_codec_read(codec, pin, 0,
2781 AC_VERB_GET_CONFIG_DEFAULT,
2782 0x00);
2783 if (get_defcfg_device(cfg) == AC_JACK_SPEAKER) {
2784 unsigned long wcaps = get_wcaps(codec, pin);
2785 wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
2786 if (wcaps == AC_WCAP_OUT_AMP)
2787 /* found a mono speaker with an amp,
2788 must be lfe */
2789 lfe_pin = pin;
2790 }
2791 }
2792 }
2793
2794 if (lfe_pin) {
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002795 err = create_controls(spec, "LFE", lfe_pin, 1);
Richard Fish160ea0d2006-09-06 13:58:25 +02002796 if (err < 0)
2797 return err;
2798 }
2799
2800 return 0;
2801}
2802
Mattc7d4b2f2005-06-27 14:59:41 +02002803static int stac9200_parse_auto_config(struct hda_codec *codec)
2804{
2805 struct sigmatel_spec *spec = codec->spec;
2806 int err;
2807
Kailang Yangdf694da2005-12-05 19:42:22 +01002808 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Mattc7d4b2f2005-06-27 14:59:41 +02002809 return err;
2810
2811 if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
2812 return err;
2813
Takashi Iwai82bc9552006-03-21 11:24:42 +01002814 if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
2815 return err;
2816
Richard Fish160ea0d2006-09-06 13:58:25 +02002817 if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0)
2818 return err;
2819
Takashi Iwai82bc9552006-03-21 11:24:42 +01002820 if (spec->autocfg.dig_out_pin)
Mattc7d4b2f2005-06-27 14:59:41 +02002821 spec->multiout.dig_out_nid = 0x05;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002822 if (spec->autocfg.dig_in_pin)
Mattc7d4b2f2005-06-27 14:59:41 +02002823 spec->dig_in_nid = 0x04;
Mattc7d4b2f2005-06-27 14:59:41 +02002824
2825 if (spec->kctl_alloc)
2826 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2827
2828 spec->input_mux = &spec->private_imux;
Matt Porter8b657272006-10-26 17:12:59 +02002829 spec->dinput_mux = &spec->private_dimux;
Mattc7d4b2f2005-06-27 14:59:41 +02002830
2831 return 1;
2832}
2833
Sam Revitch62fe78e2006-05-10 15:09:17 +02002834/*
2835 * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
2836 * funky external mute control using GPIO pins.
2837 */
2838
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002839static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
Matthew Ranostay4fe51952008-01-29 15:28:44 +01002840 unsigned int dir_mask, unsigned int data)
Sam Revitch62fe78e2006-05-10 15:09:17 +02002841{
2842 unsigned int gpiostate, gpiomask, gpiodir;
2843
2844 gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
2845 AC_VERB_GET_GPIO_DATA, 0);
Matthew Ranostay4fe51952008-01-29 15:28:44 +01002846 gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
Sam Revitch62fe78e2006-05-10 15:09:17 +02002847
2848 gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
2849 AC_VERB_GET_GPIO_MASK, 0);
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002850 gpiomask |= mask;
Sam Revitch62fe78e2006-05-10 15:09:17 +02002851
2852 gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
2853 AC_VERB_GET_GPIO_DIRECTION, 0);
Matthew Ranostay4fe51952008-01-29 15:28:44 +01002854 gpiodir |= dir_mask;
Sam Revitch62fe78e2006-05-10 15:09:17 +02002855
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002856 /* Configure GPIOx as CMOS */
Sam Revitch62fe78e2006-05-10 15:09:17 +02002857 snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
2858
2859 snd_hda_codec_write(codec, codec->afg, 0,
2860 AC_VERB_SET_GPIO_MASK, gpiomask);
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002861 snd_hda_codec_read(codec, codec->afg, 0,
2862 AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
Sam Revitch62fe78e2006-05-10 15:09:17 +02002863
2864 msleep(1);
2865
Takashi Iwai76e1ddf2008-01-15 11:39:08 +01002866 snd_hda_codec_read(codec, codec->afg, 0,
2867 AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
Sam Revitch62fe78e2006-05-10 15:09:17 +02002868}
2869
Takashi Iwai314634b2006-09-21 11:56:18 +02002870static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
2871 unsigned int event)
2872{
2873 if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
Takashi Iwaidc81bed2007-09-03 09:36:36 +02002874 snd_hda_codec_write_cache(codec, nid, 0,
2875 AC_VERB_SET_UNSOLICITED_ENABLE,
2876 (AC_USRSP_EN | event));
Takashi Iwai314634b2006-09-21 11:56:18 +02002877}
2878
Matthew Ranostaya64135a2008-01-10 16:55:06 +01002879static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
2880{
2881 int i;
2882 for (i = 0; i < cfg->hp_outs; i++)
2883 if (cfg->hp_pins[i] == nid)
2884 return 1; /* nid is a HP-Out */
2885
2886 return 0; /* nid is not a HP-Out */
2887};
2888
Matthew Ranostayb76c8502008-02-06 14:49:44 +01002889static void stac92xx_power_down(struct hda_codec *codec)
2890{
2891 struct sigmatel_spec *spec = codec->spec;
2892
2893 /* power down inactive DACs */
2894 hda_nid_t *dac;
2895 for (dac = spec->dac_list; *dac; dac++)
Matthew Ranostay44510892008-02-21 07:49:31 +01002896 if (!is_in_dac_nids(spec, *dac) &&
2897 spec->multiout.hp_nid != *dac)
Matthew Ranostayb76c8502008-02-06 14:49:44 +01002898 snd_hda_codec_write_cache(codec, *dac, 0,
2899 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2900}
2901
Mattc7d4b2f2005-06-27 14:59:41 +02002902static int stac92xx_init(struct hda_codec *codec)
2903{
2904 struct sigmatel_spec *spec = codec->spec;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002905 struct auto_pin_cfg *cfg = &spec->autocfg;
2906 int i;
Mattc7d4b2f2005-06-27 14:59:41 +02002907
Mattc7d4b2f2005-06-27 14:59:41 +02002908 snd_hda_sequence_write(codec, spec->init);
2909
Takashi Iwai82bc9552006-03-21 11:24:42 +01002910 /* set up pins */
2911 if (spec->hp_detect) {
Takashi Iwai505cb342006-03-27 12:51:52 +02002912 /* Enable unsolicited responses on the HP widget */
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002913 for (i = 0; i < cfg->hp_outs; i++)
Takashi Iwai314634b2006-09-21 11:56:18 +02002914 enable_pin_detect(codec, cfg->hp_pins[i],
2915 STAC_HP_EVENT);
Takashi Iwai0a07acaf2007-03-13 10:40:23 +01002916 /* force to enable the first line-out; the others are set up
2917 * in unsol_event
2918 */
2919 stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
2920 AC_PINCTL_OUT_EN);
Takashi Iwaieb995a82006-09-21 14:28:21 +02002921 stac92xx_auto_init_hp_out(codec);
Takashi Iwai82bc9552006-03-21 11:24:42 +01002922 /* fake event to set up pins */
2923 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
2924 } else {
2925 stac92xx_auto_init_multi_out(codec);
2926 stac92xx_auto_init_hp_out(codec);
2927 }
2928 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwaic960a032006-03-23 17:06:28 +01002929 hda_nid_t nid = cfg->input_pins[i];
2930 if (nid) {
2931 unsigned int pinctl = AC_PINCTL_IN_EN;
2932 if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
2933 pinctl |= stac92xx_get_vref(codec, nid);
2934 stac92xx_auto_set_pinctl(codec, nid, pinctl);
2935 }
Takashi Iwai82bc9552006-03-21 11:24:42 +01002936 }
Matthew Ranostaya64135a2008-01-10 16:55:06 +01002937 for (i = 0; i < spec->num_dmics; i++)
2938 stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
2939 AC_PINCTL_IN_EN);
2940 for (i = 0; i < spec->num_pwrs; i++) {
2941 int event = is_nid_hp_pin(cfg, spec->pwr_nids[i])
2942 ? STAC_HP_EVENT : STAC_PWR_EVENT;
2943 int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i],
2944 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
2945 /* outputs are only ports capable of power management
2946 * any attempts on powering down a input port cause the
2947 * referenced VREF to act quirky.
2948 */
2949 if (pinctl & AC_PINCTL_IN_EN)
2950 continue;
2951 enable_pin_detect(codec, spec->pwr_nids[i], event | i);
2952 codec->patch_ops.unsol_event(codec, (event | i) << 26);
2953 }
Matthew Ranostayb76c8502008-02-06 14:49:44 +01002954 if (spec->dac_list)
2955 stac92xx_power_down(codec);
Takashi Iwai82bc9552006-03-21 11:24:42 +01002956 if (cfg->dig_out_pin)
2957 stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
2958 AC_PINCTL_OUT_EN);
2959 if (cfg->dig_in_pin)
2960 stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
2961 AC_PINCTL_IN_EN);
2962
Matthew Ranostay4fe51952008-01-29 15:28:44 +01002963 stac_gpio_set(codec, spec->gpio_mask,
2964 spec->gpio_dir, spec->gpio_data);
Sam Revitch62fe78e2006-05-10 15:09:17 +02002965
Mattc7d4b2f2005-06-27 14:59:41 +02002966 return 0;
2967}
2968
Matt2f2f4252005-04-13 14:45:30 +02002969static void stac92xx_free(struct hda_codec *codec)
2970{
Mattc7d4b2f2005-06-27 14:59:41 +02002971 struct sigmatel_spec *spec = codec->spec;
2972 int i;
2973
2974 if (! spec)
2975 return;
2976
2977 if (spec->kctl_alloc) {
2978 for (i = 0; i < spec->num_kctl_used; i++)
2979 kfree(spec->kctl_alloc[i].name);
2980 kfree(spec->kctl_alloc);
2981 }
2982
Richard Fish11b44bb2006-08-23 18:31:34 +02002983 if (spec->bios_pin_configs)
2984 kfree(spec->bios_pin_configs);
2985
Mattc7d4b2f2005-06-27 14:59:41 +02002986 kfree(spec);
Matt2f2f4252005-04-13 14:45:30 +02002987}
2988
Matt4e550962005-07-04 17:51:39 +02002989static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
2990 unsigned int flag)
2991{
2992 unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
2993 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
Steve Longerbeam7b043892007-05-03 20:50:03 +02002994
Takashi Iwaif9acba42007-05-29 18:01:06 +02002995 if (pin_ctl & AC_PINCTL_IN_EN) {
2996 /*
2997 * we need to check the current set-up direction of
2998 * shared input pins since they can be switched via
2999 * "xxx as Output" mixer switch
3000 */
3001 struct sigmatel_spec *spec = codec->spec;
3002 struct auto_pin_cfg *cfg = &spec->autocfg;
3003 if ((nid == cfg->input_pins[AUTO_PIN_LINE] &&
3004 spec->line_switch) ||
3005 (nid == cfg->input_pins[AUTO_PIN_MIC] &&
3006 spec->mic_switch))
3007 return;
3008 }
3009
Steve Longerbeam7b043892007-05-03 20:50:03 +02003010 /* if setting pin direction bits, clear the current
3011 direction bits first */
3012 if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
3013 pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
3014
Takashi Iwai82beb8f2007-08-10 17:09:26 +02003015 snd_hda_codec_write_cache(codec, nid, 0,
Matt4e550962005-07-04 17:51:39 +02003016 AC_VERB_SET_PIN_WIDGET_CONTROL,
3017 pin_ctl | flag);
3018}
3019
3020static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
3021 unsigned int flag)
3022{
3023 unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
3024 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
Takashi Iwai82beb8f2007-08-10 17:09:26 +02003025 snd_hda_codec_write_cache(codec, nid, 0,
Matt4e550962005-07-04 17:51:39 +02003026 AC_VERB_SET_PIN_WIDGET_CONTROL,
3027 pin_ctl & ~flag);
3028}
3029
Jiang Zhe40c1d302007-11-12 13:05:16 +01003030static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
Takashi Iwai314634b2006-09-21 11:56:18 +02003031{
3032 if (!nid)
3033 return 0;
3034 if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
Jiang Zhe40c1d302007-11-12 13:05:16 +01003035 & (1 << 31)) {
3036 unsigned int pinctl;
3037 pinctl = snd_hda_codec_read(codec, nid, 0,
3038 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
3039 if (pinctl & AC_PINCTL_IN_EN)
3040 return 0; /* mic- or line-input */
3041 else
3042 return 1; /* HP-output */
3043 }
Takashi Iwai314634b2006-09-21 11:56:18 +02003044 return 0;
3045}
3046
3047static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
Matt4e550962005-07-04 17:51:39 +02003048{
3049 struct sigmatel_spec *spec = codec->spec;
3050 struct auto_pin_cfg *cfg = &spec->autocfg;
3051 int i, presence;
3052
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003053 presence = 0;
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003054 if (spec->gpio_mute)
3055 presence = !(snd_hda_codec_read(codec, codec->afg, 0,
3056 AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
3057
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003058 for (i = 0; i < cfg->hp_outs; i++) {
Takashi Iwai314634b2006-09-21 11:56:18 +02003059 if (presence)
3060 break;
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003061 presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003062 }
Matt4e550962005-07-04 17:51:39 +02003063
3064 if (presence) {
3065 /* disable lineouts, enable hp */
3066 for (i = 0; i < cfg->line_outs; i++)
3067 stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
3068 AC_PINCTL_OUT_EN);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003069 for (i = 0; i < cfg->speaker_outs; i++)
3070 stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
3071 AC_PINCTL_OUT_EN);
Matt4e550962005-07-04 17:51:39 +02003072 } else {
3073 /* enable lineouts, disable hp */
3074 for (i = 0; i < cfg->line_outs; i++)
3075 stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
3076 AC_PINCTL_OUT_EN);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003077 for (i = 0; i < cfg->speaker_outs; i++)
3078 stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
3079 AC_PINCTL_OUT_EN);
Matt4e550962005-07-04 17:51:39 +02003080 }
3081}
3082
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003083static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
3084{
3085 struct sigmatel_spec *spec = codec->spec;
3086 hda_nid_t nid = spec->pwr_nids[idx];
3087 int presence, val;
3088 val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
3089 & 0x000000ff;
3090 presence = get_hp_pin_presence(codec, nid);
3091 idx = 1 << idx;
3092
3093 if (presence)
3094 val &= ~idx;
3095 else
3096 val |= idx;
3097
3098 /* power down unused output ports */
3099 snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
3100};
3101
Takashi Iwai314634b2006-09-21 11:56:18 +02003102static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
3103{
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003104 struct sigmatel_spec *spec = codec->spec;
3105 int idx = res >> 26 & 0x0f;
3106
3107 switch ((res >> 26) & 0x30) {
Takashi Iwai314634b2006-09-21 11:56:18 +02003108 case STAC_HP_EVENT:
3109 stac92xx_hp_detect(codec, res);
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003110 /* fallthru */
3111 case STAC_PWR_EVENT:
3112 if (spec->num_pwrs > 0)
3113 stac92xx_pin_sense(codec, idx);
Takashi Iwai314634b2006-09-21 11:56:18 +02003114 }
3115}
3116
Takashi Iwaicb53c622007-08-10 17:21:45 +02003117#ifdef SND_HDA_NEEDS_RESUME
Mattff6fdc32005-06-27 15:06:52 +02003118static int stac92xx_resume(struct hda_codec *codec)
3119{
Takashi Iwaidc81bed2007-09-03 09:36:36 +02003120 struct sigmatel_spec *spec = codec->spec;
3121
Richard Fish11b44bb2006-08-23 18:31:34 +02003122 stac92xx_set_config_regs(codec);
Takashi Iwaidc81bed2007-09-03 09:36:36 +02003123 snd_hda_sequence_write(codec, spec->init);
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003124 stac_gpio_set(codec, spec->gpio_mask,
3125 spec->gpio_dir, spec->gpio_data);
Takashi Iwai82beb8f2007-08-10 17:09:26 +02003126 snd_hda_codec_resume_amp(codec);
3127 snd_hda_codec_resume_cache(codec);
Matthew Ranostayb76c8502008-02-06 14:49:44 +01003128 /* power down inactive DACs */
3129 if (spec->dac_list)
3130 stac92xx_power_down(codec);
Takashi Iwaidc81bed2007-09-03 09:36:36 +02003131 /* invoke unsolicited event to reset the HP state */
3132 if (spec->hp_detect)
3133 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
Mattff6fdc32005-06-27 15:06:52 +02003134 return 0;
3135}
3136#endif
3137
Matt2f2f4252005-04-13 14:45:30 +02003138static struct hda_codec_ops stac92xx_patch_ops = {
3139 .build_controls = stac92xx_build_controls,
3140 .build_pcms = stac92xx_build_pcms,
3141 .init = stac92xx_init,
3142 .free = stac92xx_free,
Matt4e550962005-07-04 17:51:39 +02003143 .unsol_event = stac92xx_unsol_event,
Takashi Iwaicb53c622007-08-10 17:21:45 +02003144#ifdef SND_HDA_NEEDS_RESUME
Mattff6fdc32005-06-27 15:06:52 +02003145 .resume = stac92xx_resume,
3146#endif
Matt2f2f4252005-04-13 14:45:30 +02003147};
3148
3149static int patch_stac9200(struct hda_codec *codec)
3150{
3151 struct sigmatel_spec *spec;
Mattc7d4b2f2005-06-27 14:59:41 +02003152 int err;
Matt2f2f4252005-04-13 14:45:30 +02003153
Takashi Iwaie560d8d2005-09-09 14:21:46 +02003154 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Matt2f2f4252005-04-13 14:45:30 +02003155 if (spec == NULL)
3156 return -ENOMEM;
3157
3158 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003159 spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02003160 spec->pin_nids = stac9200_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003161 spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
3162 stac9200_models,
3163 stac9200_cfg_tbl);
Richard Fish11b44bb2006-08-23 18:31:34 +02003164 if (spec->board_config < 0) {
3165 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
3166 err = stac92xx_save_bios_config_regs(codec);
3167 if (err < 0) {
3168 stac92xx_free(codec);
3169 return err;
3170 }
3171 spec->pin_configs = spec->bios_pin_configs;
3172 } else {
Matt Porter403d1942005-11-29 15:00:51 +01003173 spec->pin_configs = stac9200_brd_tbl[spec->board_config];
3174 stac92xx_set_config_regs(codec);
3175 }
Matt2f2f4252005-04-13 14:45:30 +02003176
3177 spec->multiout.max_channels = 2;
3178 spec->multiout.num_dacs = 1;
3179 spec->multiout.dac_nids = stac9200_dac_nids;
3180 spec->adc_nids = stac9200_adc_nids;
3181 spec->mux_nids = stac9200_mux_nids;
Mattdabbed62005-06-14 10:19:34 +02003182 spec->num_muxes = 1;
Matt Porter8b657272006-10-26 17:12:59 +02003183 spec->num_dmics = 0;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003184 spec->num_adcs = 1;
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003185 spec->num_pwrs = 0;
Mattc7d4b2f2005-06-27 14:59:41 +02003186
Tobin Davisbf277782008-02-03 20:31:47 +01003187 if (spec->board_config == STAC_9200_GATEWAY ||
3188 spec->board_config == STAC_9200_OQO)
Takashi Iwai1194b5b2007-10-10 10:04:26 +02003189 spec->init = stac9200_eapd_init;
3190 else
3191 spec->init = stac9200_core_init;
Matt2f2f4252005-04-13 14:45:30 +02003192 spec->mixer = stac9200_mixer;
Mattc7d4b2f2005-06-27 14:59:41 +02003193
3194 err = stac9200_parse_auto_config(codec);
3195 if (err < 0) {
3196 stac92xx_free(codec);
3197 return err;
3198 }
Matt2f2f4252005-04-13 14:45:30 +02003199
3200 codec->patch_ops = stac92xx_patch_ops;
3201
3202 return 0;
3203}
3204
Tobin Davis8e21c342007-01-08 11:04:17 +01003205static int patch_stac925x(struct hda_codec *codec)
3206{
3207 struct sigmatel_spec *spec;
3208 int err;
3209
3210 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3211 if (spec == NULL)
3212 return -ENOMEM;
3213
3214 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003215 spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
Tobin Davis8e21c342007-01-08 11:04:17 +01003216 spec->pin_nids = stac925x_pin_nids;
3217 spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
3218 stac925x_models,
3219 stac925x_cfg_tbl);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003220 again:
Tobin Davis8e21c342007-01-08 11:04:17 +01003221 if (spec->board_config < 0) {
Tobin Davis2c11f952007-05-17 09:36:34 +02003222 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
3223 "using BIOS defaults\n");
Tobin Davis8e21c342007-01-08 11:04:17 +01003224 err = stac92xx_save_bios_config_regs(codec);
3225 if (err < 0) {
3226 stac92xx_free(codec);
3227 return err;
3228 }
3229 spec->pin_configs = spec->bios_pin_configs;
3230 } else if (stac925x_brd_tbl[spec->board_config] != NULL){
3231 spec->pin_configs = stac925x_brd_tbl[spec->board_config];
3232 stac92xx_set_config_regs(codec);
3233 }
3234
3235 spec->multiout.max_channels = 2;
3236 spec->multiout.num_dacs = 1;
3237 spec->multiout.dac_nids = stac925x_dac_nids;
3238 spec->adc_nids = stac925x_adc_nids;
3239 spec->mux_nids = stac925x_mux_nids;
3240 spec->num_muxes = 1;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003241 spec->num_adcs = 1;
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003242 spec->num_pwrs = 0;
Tobin Davis2c11f952007-05-17 09:36:34 +02003243 switch (codec->vendor_id) {
3244 case 0x83847632: /* STAC9202 */
3245 case 0x83847633: /* STAC9202D */
3246 case 0x83847636: /* STAC9251 */
3247 case 0x83847637: /* STAC9251D */
Takashi Iwaif6e98522007-10-16 14:27:04 +02003248 spec->num_dmics = STAC925X_NUM_DMICS;
Tobin Davis2c11f952007-05-17 09:36:34 +02003249 spec->dmic_nids = stac925x_dmic_nids;
Takashi Iwai1697055e2007-12-18 18:05:52 +01003250 spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids);
3251 spec->dmux_nids = stac925x_dmux_nids;
Tobin Davis2c11f952007-05-17 09:36:34 +02003252 break;
3253 default:
3254 spec->num_dmics = 0;
3255 break;
3256 }
Tobin Davis8e21c342007-01-08 11:04:17 +01003257
3258 spec->init = stac925x_core_init;
3259 spec->mixer = stac925x_mixer;
3260
3261 err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003262 if (!err) {
3263 if (spec->board_config < 0) {
3264 printk(KERN_WARNING "hda_codec: No auto-config is "
3265 "available, default to model=ref\n");
3266 spec->board_config = STAC_925x_REF;
3267 goto again;
3268 }
3269 err = -EINVAL;
3270 }
Tobin Davis8e21c342007-01-08 11:04:17 +01003271 if (err < 0) {
3272 stac92xx_free(codec);
3273 return err;
3274 }
3275
3276 codec->patch_ops = stac92xx_patch_ops;
3277
3278 return 0;
3279}
3280
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003281static struct hda_input_mux stac92hd73xx_dmux = {
3282 .num_items = 4,
3283 .items = {
3284 { "Analog Inputs", 0x0b },
3285 { "CD", 0x08 },
3286 { "Digital Mic 1", 0x09 },
3287 { "Digital Mic 2", 0x0a },
3288 }
3289};
3290
3291static int patch_stac92hd73xx(struct hda_codec *codec)
3292{
3293 struct sigmatel_spec *spec;
3294 hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
3295 int err = 0;
3296
3297 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3298 if (spec == NULL)
3299 return -ENOMEM;
3300
3301 codec->spec = spec;
3302 spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
3303 spec->pin_nids = stac92hd73xx_pin_nids;
3304 spec->board_config = snd_hda_check_board_config(codec,
3305 STAC_92HD73XX_MODELS,
3306 stac92hd73xx_models,
3307 stac92hd73xx_cfg_tbl);
3308again:
3309 if (spec->board_config < 0) {
3310 snd_printdd(KERN_INFO "hda_codec: Unknown model for"
3311 " STAC92HD73XX, using BIOS defaults\n");
3312 err = stac92xx_save_bios_config_regs(codec);
3313 if (err < 0) {
3314 stac92xx_free(codec);
3315 return err;
3316 }
3317 spec->pin_configs = spec->bios_pin_configs;
3318 } else {
3319 spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config];
3320 stac92xx_set_config_regs(codec);
3321 }
3322
3323 spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
3324 conn, STAC92HD73_DAC_COUNT + 2) - 1;
3325
3326 if (spec->multiout.num_dacs < 0) {
3327 printk(KERN_WARNING "hda_codec: Could not determine "
3328 "number of channels defaulting to DAC count\n");
3329 spec->multiout.num_dacs = STAC92HD73_DAC_COUNT;
3330 }
3331
3332 switch (spec->multiout.num_dacs) {
3333 case 0x3: /* 6 Channel */
3334 spec->mixer = stac92hd73xx_6ch_mixer;
3335 spec->init = stac92hd73xx_6ch_core_init;
3336 break;
3337 case 0x4: /* 8 Channel */
3338 spec->multiout.hp_nid = 0x18;
3339 spec->mixer = stac92hd73xx_8ch_mixer;
3340 spec->init = stac92hd73xx_8ch_core_init;
3341 break;
3342 case 0x5: /* 10 Channel */
3343 spec->multiout.hp_nid = 0x19;
3344 spec->mixer = stac92hd73xx_10ch_mixer;
3345 spec->init = stac92hd73xx_10ch_core_init;
3346 };
3347
3348 spec->multiout.dac_nids = stac92hd73xx_dac_nids;
3349 spec->aloopback_mask = 0x01;
3350 spec->aloopback_shift = 8;
3351
3352 spec->mux_nids = stac92hd73xx_mux_nids;
3353 spec->adc_nids = stac92hd73xx_adc_nids;
3354 spec->dmic_nids = stac92hd73xx_dmic_nids;
3355 spec->dmux_nids = stac92hd73xx_dmux_nids;
3356
3357 spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
3358 spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
3359 spec->num_dmics = STAC92HD73XX_NUM_DMICS;
Takashi Iwai1697055e2007-12-18 18:05:52 +01003360 spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003361 spec->dinput_mux = &stac92hd73xx_dmux;
3362 /* GPIO0 High = Enable EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003363 spec->gpio_mask = spec->gpio_dir = 0x1;
3364 spec->gpio_data = 0x01;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003365
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003366 spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
3367 spec->pwr_nids = stac92hd73xx_pwr_nids;
3368
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003369 err = stac92xx_parse_auto_config(codec, 0x22, 0x24);
3370
3371 if (!err) {
3372 if (spec->board_config < 0) {
3373 printk(KERN_WARNING "hda_codec: No auto-config is "
3374 "available, default to model=ref\n");
3375 spec->board_config = STAC_92HD73XX_REF;
3376 goto again;
3377 }
3378 err = -EINVAL;
3379 }
3380
3381 if (err < 0) {
3382 stac92xx_free(codec);
3383 return err;
3384 }
3385
3386 codec->patch_ops = stac92xx_patch_ops;
3387
3388 return 0;
3389}
3390
Matthew Ranostaye035b842007-11-06 11:53:55 +01003391static int patch_stac92hd71bxx(struct hda_codec *codec)
3392{
3393 struct sigmatel_spec *spec;
3394 int err = 0;
3395
3396 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3397 if (spec == NULL)
3398 return -ENOMEM;
3399
3400 codec->spec = spec;
3401 spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
3402 spec->pin_nids = stac92hd71bxx_pin_nids;
3403 spec->board_config = snd_hda_check_board_config(codec,
3404 STAC_92HD71BXX_MODELS,
3405 stac92hd71bxx_models,
3406 stac92hd71bxx_cfg_tbl);
3407again:
3408 if (spec->board_config < 0) {
3409 snd_printdd(KERN_INFO "hda_codec: Unknown model for"
3410 " STAC92HD71BXX, using BIOS defaults\n");
3411 err = stac92xx_save_bios_config_regs(codec);
3412 if (err < 0) {
3413 stac92xx_free(codec);
3414 return err;
3415 }
3416 spec->pin_configs = spec->bios_pin_configs;
3417 } else {
3418 spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config];
3419 stac92xx_set_config_regs(codec);
3420 }
3421
Matthew Ranostay541eee82007-12-14 12:08:04 +01003422 switch (codec->vendor_id) {
3423 case 0x111d76b6: /* 4 Port without Analog Mixer */
3424 case 0x111d76b7:
3425 case 0x111d76b4: /* 6 Port without Analog Mixer */
3426 case 0x111d76b5:
3427 spec->mixer = stac92hd71bxx_mixer;
3428 spec->init = stac92hd71bxx_core_init;
3429 break;
3430 default:
3431 spec->mixer = stac92hd71bxx_analog_mixer;
3432 spec->init = stac92hd71bxx_analog_core_init;
3433 }
3434
3435 spec->aloopback_mask = 0x20;
3436 spec->aloopback_shift = 0;
3437
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003438 /* GPIO0 High = EAPD */
3439 spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1;
Matthew Ranostaye035b842007-11-06 11:53:55 +01003440
Matthew Ranostaye035b842007-11-06 11:53:55 +01003441 spec->mux_nids = stac92hd71bxx_mux_nids;
3442 spec->adc_nids = stac92hd71bxx_adc_nids;
3443 spec->dmic_nids = stac92hd71bxx_dmic_nids;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003444 spec->dmux_nids = stac92hd71bxx_dmux_nids;
Matthew Ranostaye035b842007-11-06 11:53:55 +01003445
3446 spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
3447 spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
3448 spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
Takashi Iwai1697055e2007-12-18 18:05:52 +01003449 spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
Matthew Ranostaye035b842007-11-06 11:53:55 +01003450
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003451 spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
3452 spec->pwr_nids = stac92hd71bxx_pwr_nids;
3453
Matthew Ranostaye035b842007-11-06 11:53:55 +01003454 spec->multiout.num_dacs = 2;
3455 spec->multiout.hp_nid = 0x11;
3456 spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
3457
3458 err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
3459 if (!err) {
3460 if (spec->board_config < 0) {
3461 printk(KERN_WARNING "hda_codec: No auto-config is "
3462 "available, default to model=ref\n");
3463 spec->board_config = STAC_92HD71BXX_REF;
3464 goto again;
3465 }
3466 err = -EINVAL;
3467 }
3468
3469 if (err < 0) {
3470 stac92xx_free(codec);
3471 return err;
3472 }
3473
3474 codec->patch_ops = stac92xx_patch_ops;
3475
3476 return 0;
3477};
3478
Matt2f2f4252005-04-13 14:45:30 +02003479static int patch_stac922x(struct hda_codec *codec)
3480{
3481 struct sigmatel_spec *spec;
Mattc7d4b2f2005-06-27 14:59:41 +02003482 int err;
Matt2f2f4252005-04-13 14:45:30 +02003483
Takashi Iwaie560d8d2005-09-09 14:21:46 +02003484 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Matt2f2f4252005-04-13 14:45:30 +02003485 if (spec == NULL)
3486 return -ENOMEM;
3487
3488 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003489 spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02003490 spec->pin_nids = stac922x_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003491 spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
3492 stac922x_models,
3493 stac922x_cfg_tbl);
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003494 if (spec->board_config == STAC_INTEL_MAC_V3) {
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003495 spec->gpio_mask = spec->gpio_dir = 0x03;
3496 spec->gpio_data = 0x03;
Takashi Iwai3fc24d82007-02-16 13:27:18 +01003497 /* Intel Macs have all same PCI SSID, so we need to check
3498 * codec SSID to distinguish the exact models
3499 */
Nicolas Boichat6f0778d2007-03-15 12:38:15 +01003500 printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
Takashi Iwai3fc24d82007-02-16 13:27:18 +01003501 switch (codec->subsystem_id) {
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003502
3503 case 0x106b0800:
3504 spec->board_config = STAC_INTEL_MAC_V1;
Abhijit Bhopatkarc45e20e2007-04-17 11:57:16 +02003505 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003506 case 0x106b0600:
3507 case 0x106b0700:
3508 spec->board_config = STAC_INTEL_MAC_V2;
Nicolas Boichat6f0778d2007-03-15 12:38:15 +01003509 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003510 case 0x106b0e00:
3511 case 0x106b0f00:
3512 case 0x106b1600:
3513 case 0x106b1700:
3514 case 0x106b0200:
3515 case 0x106b1e00:
3516 spec->board_config = STAC_INTEL_MAC_V3;
Takashi Iwai3fc24d82007-02-16 13:27:18 +01003517 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003518 case 0x106b1a00:
3519 case 0x00000100:
3520 spec->board_config = STAC_INTEL_MAC_V4;
Sylvain FORETf16928f2007-04-27 14:22:36 +02003521 break;
Ivan N. Zlatev5d5d3bc2007-05-29 16:03:00 +02003522 case 0x106b0a00:
3523 case 0x106b2200:
3524 spec->board_config = STAC_INTEL_MAC_V5;
Takashi Iwai0dae0f82007-05-21 12:41:29 +02003525 break;
Takashi Iwai3fc24d82007-02-16 13:27:18 +01003526 }
3527 }
3528
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003529 again:
Richard Fish11b44bb2006-08-23 18:31:34 +02003530 if (spec->board_config < 0) {
3531 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
3532 "using BIOS defaults\n");
3533 err = stac92xx_save_bios_config_regs(codec);
3534 if (err < 0) {
3535 stac92xx_free(codec);
3536 return err;
3537 }
3538 spec->pin_configs = spec->bios_pin_configs;
3539 } else if (stac922x_brd_tbl[spec->board_config] != NULL) {
Matt Porter403d1942005-11-29 15:00:51 +01003540 spec->pin_configs = stac922x_brd_tbl[spec->board_config];
3541 stac92xx_set_config_regs(codec);
3542 }
Matt2f2f4252005-04-13 14:45:30 +02003543
Matt2f2f4252005-04-13 14:45:30 +02003544 spec->adc_nids = stac922x_adc_nids;
3545 spec->mux_nids = stac922x_mux_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01003546 spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003547 spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
Matt Porter8b657272006-10-26 17:12:59 +02003548 spec->num_dmics = 0;
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003549 spec->num_pwrs = 0;
Mattc7d4b2f2005-06-27 14:59:41 +02003550
3551 spec->init = stac922x_core_init;
Matt2f2f4252005-04-13 14:45:30 +02003552 spec->mixer = stac922x_mixer;
Mattc7d4b2f2005-06-27 14:59:41 +02003553
3554 spec->multiout.dac_nids = spec->dac_nids;
Takashi Iwai19039bd2006-06-28 15:52:16 +02003555
Matt Porter3cc08dc2006-01-23 15:27:49 +01003556 err = stac92xx_parse_auto_config(codec, 0x08, 0x09);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003557 if (!err) {
3558 if (spec->board_config < 0) {
3559 printk(KERN_WARNING "hda_codec: No auto-config is "
3560 "available, default to model=ref\n");
3561 spec->board_config = STAC_D945_REF;
3562 goto again;
3563 }
3564 err = -EINVAL;
3565 }
Matt Porter3cc08dc2006-01-23 15:27:49 +01003566 if (err < 0) {
3567 stac92xx_free(codec);
3568 return err;
3569 }
3570
3571 codec->patch_ops = stac92xx_patch_ops;
3572
Takashi Iwai807a46362007-05-29 19:01:37 +02003573 /* Fix Mux capture level; max to 2 */
3574 snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
3575 (0 << AC_AMPCAP_OFFSET_SHIFT) |
3576 (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3577 (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3578 (0 << AC_AMPCAP_MUTE_SHIFT));
3579
Matt Porter3cc08dc2006-01-23 15:27:49 +01003580 return 0;
3581}
3582
3583static int patch_stac927x(struct hda_codec *codec)
3584{
3585 struct sigmatel_spec *spec;
3586 int err;
3587
3588 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3589 if (spec == NULL)
3590 return -ENOMEM;
3591
3592 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003593 spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02003594 spec->pin_nids = stac927x_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003595 spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
3596 stac927x_models,
3597 stac927x_cfg_tbl);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003598 again:
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003599 if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) {
3600 if (spec->board_config < 0)
3601 snd_printdd(KERN_INFO "hda_codec: Unknown model for"
3602 "STAC927x, using BIOS defaults\n");
Richard Fish11b44bb2006-08-23 18:31:34 +02003603 err = stac92xx_save_bios_config_regs(codec);
3604 if (err < 0) {
3605 stac92xx_free(codec);
3606 return err;
3607 }
3608 spec->pin_configs = spec->bios_pin_configs;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003609 } else {
Matt Porter3cc08dc2006-01-23 15:27:49 +01003610 spec->pin_configs = stac927x_brd_tbl[spec->board_config];
3611 stac92xx_set_config_regs(codec);
3612 }
3613
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003614 spec->adc_nids = stac927x_adc_nids;
3615 spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
3616 spec->mux_nids = stac927x_mux_nids;
3617 spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
Matthew Ranostayb76c8502008-02-06 14:49:44 +01003618 spec->dac_list = stac927x_dac_nids;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003619 spec->multiout.dac_nids = spec->dac_nids;
3620
Tobin Davis81d3dbd2006-08-22 19:44:45 +02003621 switch (spec->board_config) {
Tobin Davis93ed1502006-09-01 21:03:12 +02003622 case STAC_D965_3ST:
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003623 case STAC_D965_5ST:
3624 /* GPIO0 High = Enable EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003625 spec->gpio_mask = spec->gpio_dir = 0x01;
3626 spec->gpio_data = 0x01;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003627 spec->num_dmics = 0;
3628
Tobin Davis93ed1502006-09-01 21:03:12 +02003629 spec->init = d965_core_init;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003630 spec->mixer = stac927x_mixer;
Tobin Davis81d3dbd2006-08-22 19:44:45 +02003631 break;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003632 case STAC_DELL_BIOS:
Matthew Ranostay2f32d902008-01-10 13:06:26 +01003633 /* correct the front output jack as a hp out */
Matthew Ranostay7989fba2008-02-21 07:50:12 +01003634 stac92xx_set_config_reg(codec, 0x0f, 0x0227011f);
Matthew Ranostayc481fca2008-01-07 12:18:28 +01003635 /* correct the front input jack as a mic */
3636 stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
3637 /* fallthru */
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003638 case STAC_DELL_3ST:
3639 /* GPIO2 High = Enable EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003640 spec->gpio_mask = spec->gpio_dir = 0x04;
3641 spec->gpio_data = 0x04;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003642 spec->dmic_nids = stac927x_dmic_nids;
3643 spec->num_dmics = STAC927X_NUM_DMICS;
3644
Tobin Davis93ed1502006-09-01 21:03:12 +02003645 spec->init = d965_core_init;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003646 spec->mixer = stac927x_mixer;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003647 spec->dmux_nids = stac927x_dmux_nids;
Takashi Iwai1697055e2007-12-18 18:05:52 +01003648 spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
Tobin Davis81d3dbd2006-08-22 19:44:45 +02003649 break;
3650 default:
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003651 /* GPIO0 High = Enable EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003652 spec->gpio_mask = spec->gpio_dir = 0x1;
3653 spec->gpio_data = 0x01;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003654 spec->num_dmics = 0;
3655
Tobin Davis81d3dbd2006-08-22 19:44:45 +02003656 spec->init = stac927x_core_init;
3657 spec->mixer = stac927x_mixer;
3658 }
Matt Porter3cc08dc2006-01-23 15:27:49 +01003659
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003660 spec->num_pwrs = 0;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003661 spec->aloopback_mask = 0x40;
3662 spec->aloopback_shift = 0;
Matthew Ranostay8e9068b2007-12-17 11:58:13 +01003663
Matt Porter3cc08dc2006-01-23 15:27:49 +01003664 err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003665 if (!err) {
3666 if (spec->board_config < 0) {
3667 printk(KERN_WARNING "hda_codec: No auto-config is "
3668 "available, default to model=ref\n");
3669 spec->board_config = STAC_D965_REF;
3670 goto again;
3671 }
3672 err = -EINVAL;
3673 }
Mattc7d4b2f2005-06-27 14:59:41 +02003674 if (err < 0) {
3675 stac92xx_free(codec);
3676 return err;
3677 }
Matt2f2f4252005-04-13 14:45:30 +02003678
3679 codec->patch_ops = stac92xx_patch_ops;
3680
Takashi Iwai52987652008-01-16 16:09:47 +01003681 /*
3682 * !!FIXME!!
3683 * The STAC927x seem to require fairly long delays for certain
3684 * command sequences. With too short delays (even if the answer
3685 * is set to RIRB properly), it results in the silence output
3686 * on some hardwares like Dell.
3687 *
3688 * The below flag enables the longer delay (see get_response
3689 * in hda_intel.c).
3690 */
3691 codec->bus->needs_damn_long_delay = 1;
3692
Matt2f2f4252005-04-13 14:45:30 +02003693 return 0;
3694}
3695
Matt Porterf3302a52006-07-31 12:49:34 +02003696static int patch_stac9205(struct hda_codec *codec)
3697{
3698 struct sigmatel_spec *spec;
Takashi Iwai82599802007-07-31 15:56:24 +02003699 int err;
Matt Porterf3302a52006-07-31 12:49:34 +02003700
3701 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3702 if (spec == NULL)
3703 return -ENOMEM;
3704
3705 codec->spec = spec;
Takashi Iwaia4eed132007-07-06 18:17:04 +02003706 spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
Richard Fish11b44bb2006-08-23 18:31:34 +02003707 spec->pin_nids = stac9205_pin_nids;
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003708 spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
3709 stac9205_models,
3710 stac9205_cfg_tbl);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003711 again:
Richard Fish11b44bb2006-08-23 18:31:34 +02003712 if (spec->board_config < 0) {
3713 snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
3714 err = stac92xx_save_bios_config_regs(codec);
3715 if (err < 0) {
3716 stac92xx_free(codec);
3717 return err;
3718 }
3719 spec->pin_configs = spec->bios_pin_configs;
3720 } else {
Matt Porterf3302a52006-07-31 12:49:34 +02003721 spec->pin_configs = stac9205_brd_tbl[spec->board_config];
3722 stac92xx_set_config_regs(codec);
3723 }
3724
3725 spec->adc_nids = stac9205_adc_nids;
Maxim Levitsky9e05b7a2007-09-03 15:31:02 +02003726 spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids);
Matt Porterf3302a52006-07-31 12:49:34 +02003727 spec->mux_nids = stac9205_mux_nids;
Takashi Iwai25494132007-03-12 12:36:16 +01003728 spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
Matt Porter8b657272006-10-26 17:12:59 +02003729 spec->dmic_nids = stac9205_dmic_nids;
Takashi Iwaif6e98522007-10-16 14:27:04 +02003730 spec->num_dmics = STAC9205_NUM_DMICS;
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003731 spec->dmux_nids = stac9205_dmux_nids;
Takashi Iwai1697055e2007-12-18 18:05:52 +01003732 spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
Matthew Ranostaya64135a2008-01-10 16:55:06 +01003733 spec->num_pwrs = 0;
Matt Porterf3302a52006-07-31 12:49:34 +02003734
3735 spec->init = stac9205_core_init;
3736 spec->mixer = stac9205_mixer;
3737
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01003738 spec->aloopback_mask = 0x40;
3739 spec->aloopback_shift = 0;
Matt Porterf3302a52006-07-31 12:49:34 +02003740 spec->multiout.dac_nids = spec->dac_nids;
Matthew Ranostay87d48362007-07-17 11:52:24 +02003741
Tobin Davisae0a8ed2007-08-13 15:50:29 +02003742 switch (spec->board_config){
Tobin Davisae0a8ed2007-08-13 15:50:29 +02003743 case STAC_9205_DELL_M43:
Matthew Ranostay87d48362007-07-17 11:52:24 +02003744 /* Enable SPDIF in/out */
3745 stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
3746 stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
Matt Porter33382402006-12-18 13:17:28 +01003747
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003748 /* Enable unsol response for GPIO4/Dock HP connection */
3749 snd_hda_codec_write(codec, codec->afg, 0,
3750 AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
3751 snd_hda_codec_write_cache(codec, codec->afg, 0,
3752 AC_VERB_SET_UNSOLICITED_ENABLE,
3753 (AC_USRSP_EN | STAC_HP_EVENT));
3754
3755 spec->gpio_dir = 0x0b;
3756 spec->gpio_mask = 0x1b;
3757 spec->gpio_mute = 0x10;
Matthew Ranostaye2e7d622008-01-24 15:32:15 +01003758 /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003759 * GPIO3 Low = DRM
Matthew Ranostay87d48362007-07-17 11:52:24 +02003760 */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003761 spec->gpio_data = 0x01;
Tobin Davisae0a8ed2007-08-13 15:50:29 +02003762 break;
3763 default:
3764 /* GPIO0 High = EAPD */
Matthew Ranostay4fe51952008-01-29 15:28:44 +01003765 spec->gpio_mask = spec->gpio_dir = 0x1;
3766 spec->gpio_data = 0x01;
Tobin Davisae0a8ed2007-08-13 15:50:29 +02003767 break;
3768 }
Matthew Ranostay87d48362007-07-17 11:52:24 +02003769
Matt Porterf3302a52006-07-31 12:49:34 +02003770 err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
Takashi Iwai9e507ab2007-02-08 17:50:10 +01003771 if (!err) {
3772 if (spec->board_config < 0) {
3773 printk(KERN_WARNING "hda_codec: No auto-config is "
3774 "available, default to model=ref\n");
3775 spec->board_config = STAC_9205_REF;
3776 goto again;
3777 }
3778 err = -EINVAL;
3779 }
Matt Porterf3302a52006-07-31 12:49:34 +02003780 if (err < 0) {
3781 stac92xx_free(codec);
3782 return err;
3783 }
3784
3785 codec->patch_ops = stac92xx_patch_ops;
3786
3787 return 0;
3788}
3789
Matt2f2f4252005-04-13 14:45:30 +02003790/*
Guillaume Munch6d859062006-08-22 17:15:47 +02003791 * STAC9872 hack
Takashi Iwaidb064e52006-03-16 16:04:58 +01003792 */
3793
Guillaume Munch99ccc562006-08-16 19:35:12 +02003794/* static config for Sony VAIO FE550G and Sony VAIO AR */
Takashi Iwaidb064e52006-03-16 16:04:58 +01003795static hda_nid_t vaio_dacs[] = { 0x2 };
3796#define VAIO_HP_DAC 0x5
3797static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ };
3798static hda_nid_t vaio_mux_nids[] = { 0x15 };
3799
3800static struct hda_input_mux vaio_mux = {
Takashi Iwaia3a2f422007-10-11 11:21:21 +02003801 .num_items = 3,
Takashi Iwaidb064e52006-03-16 16:04:58 +01003802 .items = {
Takashi Iwaid7737812006-04-25 13:05:43 +02003803 /* { "HP", 0x0 }, */
Takashi Iwai1624cb92007-07-05 13:10:51 +02003804 { "Mic Jack", 0x1 },
3805 { "Internal Mic", 0x2 },
Takashi Iwaidb064e52006-03-16 16:04:58 +01003806 { "PCM", 0x3 },
3807 }
3808};
3809
3810static struct hda_verb vaio_init[] = {
3811 {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02003812 {0x0a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | STAC_HP_EVENT},
Takashi Iwaidb064e52006-03-16 16:04:58 +01003813 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
3814 {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
3815 {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
3816 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
Takashi Iwai1624cb92007-07-05 13:10:51 +02003817 {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
Takashi Iwaidb064e52006-03-16 16:04:58 +01003818 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
3819 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
3820 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
3821 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
3822 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
3823 {}
3824};
3825
Guillaume Munch6d859062006-08-22 17:15:47 +02003826static struct hda_verb vaio_ar_init[] = {
3827 {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
3828 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
3829 {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
3830 {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
3831/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
3832 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
Takashi Iwai1624cb92007-07-05 13:10:51 +02003833 {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
Guillaume Munch6d859062006-08-22 17:15:47 +02003834 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
3835 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
3836/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
3837 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
3838 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
3839 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
3840 {}
3841};
3842
Takashi Iwaidb064e52006-03-16 16:04:58 +01003843/* bind volumes of both NID 0x02 and 0x05 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02003844static struct hda_bind_ctls vaio_bind_master_vol = {
3845 .ops = &snd_hda_bind_vol,
3846 .values = {
3847 HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
3848 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
3849 0
3850 },
3851};
Takashi Iwaidb064e52006-03-16 16:04:58 +01003852
3853/* bind volumes of both NID 0x02 and 0x05 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02003854static struct hda_bind_ctls vaio_bind_master_sw = {
3855 .ops = &snd_hda_bind_sw,
3856 .values = {
3857 HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
3858 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
3859 0,
3860 },
3861};
Takashi Iwaidb064e52006-03-16 16:04:58 +01003862
3863static struct snd_kcontrol_new vaio_mixer[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02003864 HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
3865 HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
Takashi Iwaidb064e52006-03-16 16:04:58 +01003866 /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
3867 HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
3868 HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
3869 {
3870 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3871 .name = "Capture Source",
3872 .count = 1,
3873 .info = stac92xx_mux_enum_info,
3874 .get = stac92xx_mux_enum_get,
3875 .put = stac92xx_mux_enum_put,
3876 },
3877 {}
3878};
3879
Guillaume Munch6d859062006-08-22 17:15:47 +02003880static struct snd_kcontrol_new vaio_ar_mixer[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02003881 HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
3882 HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
Guillaume Munch6d859062006-08-22 17:15:47 +02003883 /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
3884 HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
3885 HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
3886 /*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT),
3887 HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/
3888 {
3889 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3890 .name = "Capture Source",
3891 .count = 1,
3892 .info = stac92xx_mux_enum_info,
3893 .get = stac92xx_mux_enum_get,
3894 .put = stac92xx_mux_enum_put,
3895 },
3896 {}
3897};
3898
3899static struct hda_codec_ops stac9872_patch_ops = {
Takashi Iwaidb064e52006-03-16 16:04:58 +01003900 .build_controls = stac92xx_build_controls,
3901 .build_pcms = stac92xx_build_pcms,
3902 .init = stac92xx_init,
3903 .free = stac92xx_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +02003904#ifdef SND_HDA_NEEDS_RESUME
Takashi Iwaidb064e52006-03-16 16:04:58 +01003905 .resume = stac92xx_resume,
3906#endif
3907};
3908
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02003909static int stac9872_vaio_init(struct hda_codec *codec)
3910{
3911 int err;
3912
3913 err = stac92xx_init(codec);
3914 if (err < 0)
3915 return err;
3916 if (codec->patch_ops.unsol_event)
3917 codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
3918 return 0;
3919}
3920
3921static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
3922{
Jiang Zhe40c1d302007-11-12 13:05:16 +01003923 if (get_hp_pin_presence(codec, 0x0a)) {
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02003924 stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
3925 stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
3926 } else {
3927 stac92xx_reset_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
3928 stac92xx_set_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
3929 }
3930}
3931
3932static void stac9872_vaio_unsol_event(struct hda_codec *codec, unsigned int res)
3933{
3934 switch (res >> 26) {
3935 case STAC_HP_EVENT:
3936 stac9872_vaio_hp_detect(codec, res);
3937 break;
3938 }
3939}
3940
3941static struct hda_codec_ops stac9872_vaio_patch_ops = {
3942 .build_controls = stac92xx_build_controls,
3943 .build_pcms = stac92xx_build_pcms,
3944 .init = stac9872_vaio_init,
3945 .free = stac92xx_free,
3946 .unsol_event = stac9872_vaio_unsol_event,
3947#ifdef CONFIG_PM
3948 .resume = stac92xx_resume,
3949#endif
3950};
3951
Guillaume Munch6d859062006-08-22 17:15:47 +02003952enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */
3953 CXD9872RD_VAIO,
3954 /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */
3955 STAC9872AK_VAIO,
3956 /* Unknown. id=0x83847661 and subsys=0x104D1200. */
3957 STAC9872K_VAIO,
3958 /* AR Series. id=0x83847664 and subsys=104D1300 */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003959 CXD9872AKD_VAIO,
3960 STAC_9872_MODELS,
3961};
Takashi Iwaidb064e52006-03-16 16:04:58 +01003962
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003963static const char *stac9872_models[STAC_9872_MODELS] = {
3964 [CXD9872RD_VAIO] = "vaio",
3965 [CXD9872AKD_VAIO] = "vaio-ar",
3966};
3967
3968static struct snd_pci_quirk stac9872_cfg_tbl[] = {
3969 SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO),
3970 SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO),
3971 SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO),
Tobin Davis68e22542007-03-12 11:36:39 +01003972 SND_PCI_QUIRK(0x104d, 0x8205, "Sony VAIO AR", CXD9872AKD_VAIO),
Takashi Iwaidb064e52006-03-16 16:04:58 +01003973 {}
3974};
3975
Guillaume Munch6d859062006-08-22 17:15:47 +02003976static int patch_stac9872(struct hda_codec *codec)
Takashi Iwaidb064e52006-03-16 16:04:58 +01003977{
3978 struct sigmatel_spec *spec;
3979 int board_config;
3980
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003981 board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
3982 stac9872_models,
3983 stac9872_cfg_tbl);
Takashi Iwaidb064e52006-03-16 16:04:58 +01003984 if (board_config < 0)
3985 /* unknown config, let generic-parser do its job... */
3986 return snd_hda_parse_generic_codec(codec);
3987
3988 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3989 if (spec == NULL)
3990 return -ENOMEM;
3991
3992 codec->spec = spec;
3993 switch (board_config) {
Guillaume Munch6d859062006-08-22 17:15:47 +02003994 case CXD9872RD_VAIO:
3995 case STAC9872AK_VAIO:
3996 case STAC9872K_VAIO:
Takashi Iwaidb064e52006-03-16 16:04:58 +01003997 spec->mixer = vaio_mixer;
3998 spec->init = vaio_init;
3999 spec->multiout.max_channels = 2;
4000 spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
4001 spec->multiout.dac_nids = vaio_dacs;
4002 spec->multiout.hp_nid = VAIO_HP_DAC;
4003 spec->num_adcs = ARRAY_SIZE(vaio_adcs);
4004 spec->adc_nids = vaio_adcs;
Matthew Ranostaya64135a2008-01-10 16:55:06 +01004005 spec->num_pwrs = 0;
Takashi Iwaidb064e52006-03-16 16:04:58 +01004006 spec->input_mux = &vaio_mux;
4007 spec->mux_nids = vaio_mux_nids;
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02004008 codec->patch_ops = stac9872_vaio_patch_ops;
Takashi Iwaidb064e52006-03-16 16:04:58 +01004009 break;
Guillaume Munch6d859062006-08-22 17:15:47 +02004010
4011 case CXD9872AKD_VAIO:
4012 spec->mixer = vaio_ar_mixer;
4013 spec->init = vaio_ar_init;
4014 spec->multiout.max_channels = 2;
4015 spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
4016 spec->multiout.dac_nids = vaio_dacs;
4017 spec->multiout.hp_nid = VAIO_HP_DAC;
4018 spec->num_adcs = ARRAY_SIZE(vaio_adcs);
Matthew Ranostaya64135a2008-01-10 16:55:06 +01004019 spec->num_pwrs = 0;
Guillaume Munch6d859062006-08-22 17:15:47 +02004020 spec->adc_nids = vaio_adcs;
4021 spec->input_mux = &vaio_mux;
4022 spec->mux_nids = vaio_mux_nids;
Takashi Iwai72e7b0d2007-08-16 17:33:55 +02004023 codec->patch_ops = stac9872_patch_ops;
Guillaume Munch6d859062006-08-22 17:15:47 +02004024 break;
Takashi Iwaidb064e52006-03-16 16:04:58 +01004025 }
4026
Takashi Iwaidb064e52006-03-16 16:04:58 +01004027 return 0;
4028}
4029
4030
4031/*
Matt2f2f4252005-04-13 14:45:30 +02004032 * patch entries
4033 */
4034struct hda_codec_preset snd_hda_preset_sigmatel[] = {
4035 { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
4036 { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
4037 { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
4038 { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
4039 { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
4040 { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
4041 { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
Matt Porter22a27c72006-07-06 18:49:10 +02004042 { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
4043 { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
4044 { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
4045 { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
4046 { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
4047 { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
Matt Porter3cc08dc2006-01-23 15:27:49 +01004048 { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
4049 { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
4050 { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
4051 { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
4052 { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
4053 { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
4054 { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
4055 { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
4056 { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
4057 { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
Tobin Davis8e21c342007-01-08 11:04:17 +01004058 { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
4059 { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
4060 { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
4061 { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
4062 { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
4063 { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
Guillaume Munch6d859062006-08-22 17:15:47 +02004064 /* The following does not take into account .id=0x83847661 when subsys =
4065 * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
4066 * currently not fully supported.
4067 */
4068 { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
4069 { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
4070 { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
Matt Porterf3302a52006-07-31 12:49:34 +02004071 { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
4072 { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
4073 { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
4074 { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
4075 { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
4076 { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
4077 { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
4078 { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
Matthew Ranostay541eee82007-12-14 12:08:04 +01004079 { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
4080 { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
Matthew Ranostaye1f0d662007-12-13 17:47:21 +01004081 { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
Matthew Ranostay541eee82007-12-14 12:08:04 +01004082 { .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx },
4083 { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
4084 { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
4085 { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
4086 { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
4087 { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
4088 { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
4089 { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
4090 { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
Matt2f2f4252005-04-13 14:45:30 +02004091 {} /* terminator */
4092};