Added
Link Here
|
1 |
/* |
2 |
* HD audio interface patch for Conexant HDA audio codec |
3 |
* |
4 |
* Copyright (c) 2006 Pototskiy Alex <alex.pototskiy@gmail.com> |
5 |
* |
6 |
* This driver is free software; you can redistribute it and/or modify |
7 |
* it under the terms of the GNU General Public License as published by |
8 |
* the Free Software Foundation; either version 2 of the License, or |
9 |
* (at your option) any later version. |
10 |
* |
11 |
* This driver is distributed in the hope that it will be useful, |
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU General Public License |
17 |
* along with this program; if not, write to the Free Software |
18 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
*/ |
20 |
|
21 |
#include <sound/driver.h> |
22 |
#include <linux/init.h> |
23 |
#include <linux/delay.h> |
24 |
#include <linux/slab.h> |
25 |
#include <linux/pci.h> |
26 |
#include <sound/core.h> |
27 |
#include "hda_codec.h" |
28 |
#include "hda_local.h" |
29 |
|
30 |
#define MAIN_MUX 0x1A |
31 |
#define DAC 0x10 |
32 |
#define ADC 0x12 |
33 |
#define SPDIF_OUT 0x11 |
34 |
|
35 |
struct conexant_spec { |
36 |
|
37 |
struct snd_kcontrol_new *mixers[5]; |
38 |
int num_mixers; |
39 |
|
40 |
const struct hda_verb *init_verbs[5]; /* initialization verbs |
41 |
* don't forget NULL termination! |
42 |
*/ |
43 |
unsigned int num_init_verbs; |
44 |
|
45 |
/* playback */ |
46 |
struct hda_multi_out multiout; /* playback set-up |
47 |
* max_channels, dacs must be set |
48 |
* dig_out_nid and hp_nid are optional |
49 |
*/ |
50 |
/* capture */ |
51 |
unsigned int num_adc_nids; |
52 |
hda_nid_t *adc_nids; |
53 |
hda_nid_t dig_in_nid; /* digital-in NID; optional */ |
54 |
|
55 |
/* capture source */ |
56 |
const struct hda_input_mux *input_mux; |
57 |
hda_nid_t *capsrc_nids; |
58 |
unsigned int cur_mux[3]; |
59 |
|
60 |
/* channel model */ |
61 |
const struct hda_channel_mode *channel_mode; |
62 |
int num_channel_mode; |
63 |
|
64 |
/* PCM information */ |
65 |
struct hda_pcm pcm_rec[2]; /* used in alc_build_pcms() */ |
66 |
|
67 |
struct mutex amp_mutex; /* PCM volume/mute control mutex */ |
68 |
unsigned int spdif_route; |
69 |
|
70 |
/* dynamic controls, init_verbs and input_mux */ |
71 |
struct auto_pin_cfg autocfg; |
72 |
unsigned int num_kctl_alloc, num_kctl_used; |
73 |
struct snd_kcontrol_new *kctl_alloc; |
74 |
struct hda_input_mux private_imux; |
75 |
hda_nid_t private_dac_nids[4]; |
76 |
|
77 |
}; |
78 |
|
79 |
static hda_nid_t conexant_dac_nids[1] = { DAC }; |
80 |
static hda_nid_t conexant_adc_nids[1] = { ADC }; |
81 |
static hda_nid_t conexant_capsrc_nids[1] = { 0x19 }; |
82 |
|
83 |
static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, |
84 |
struct hda_codec *codec, |
85 |
struct snd_pcm_substream *substream) |
86 |
{ |
87 |
struct conexant_spec *spec = codec->spec; |
88 |
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); |
89 |
} |
90 |
|
91 |
static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo, |
92 |
struct hda_codec *codec, |
93 |
unsigned int stream_tag, |
94 |
unsigned int format, |
95 |
struct snd_pcm_substream *substream) |
96 |
{ |
97 |
struct conexant_spec *spec = codec->spec; |
98 |
return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, |
99 |
format, substream); |
100 |
} |
101 |
|
102 |
static int conexant_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, |
103 |
struct hda_codec *codec, |
104 |
struct snd_pcm_substream *substream) |
105 |
{ |
106 |
struct conexant_spec *spec = codec->spec; |
107 |
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); |
108 |
} |
109 |
|
110 |
/* |
111 |
* Digital out |
112 |
*/ |
113 |
static int conexant_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, |
114 |
struct hda_codec *codec, |
115 |
struct snd_pcm_substream *substream) |
116 |
{ |
117 |
struct conexant_spec *spec = codec->spec; |
118 |
return snd_hda_multi_out_dig_open(codec, &spec->multiout); |
119 |
} |
120 |
|
121 |
static int conexant_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, |
122 |
struct hda_codec *codec, |
123 |
struct snd_pcm_substream *substream) |
124 |
{ |
125 |
struct conexant_spec *spec = codec->spec; |
126 |
return snd_hda_multi_out_dig_close(codec, &spec->multiout); |
127 |
} |
128 |
|
129 |
/* |
130 |
* Analog capture |
131 |
*/ |
132 |
static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo, |
133 |
struct hda_codec *codec, |
134 |
unsigned int stream_tag, |
135 |
unsigned int format, |
136 |
struct snd_pcm_substream *substream) |
137 |
{ |
138 |
struct conexant_spec *spec = codec->spec; |
139 |
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], |
140 |
stream_tag, 0, format); |
141 |
return 0; |
142 |
} |
143 |
|
144 |
static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, |
145 |
struct hda_codec *codec, |
146 |
struct snd_pcm_substream *substream) |
147 |
{ |
148 |
struct conexant_spec *spec = codec->spec; |
149 |
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], |
150 |
0, 0, 0); |
151 |
return 0; |
152 |
} |
153 |
|
154 |
|
155 |
|
156 |
static struct hda_pcm_stream conexant_pcm_analog_playback = { |
157 |
.substreams = 1, |
158 |
.channels_min = 2, |
159 |
.channels_max = 2, |
160 |
.nid = 0, /* fill later */ |
161 |
.ops = { |
162 |
.open = conexant_playback_pcm_open, |
163 |
.prepare = conexant_playback_pcm_prepare, |
164 |
.cleanup = conexant_playback_pcm_cleanup |
165 |
}, |
166 |
|
167 |
}; |
168 |
|
169 |
static struct hda_pcm_stream conexant_pcm_analog_capture = { |
170 |
.substreams = 1, |
171 |
.channels_min = 2, |
172 |
.channels_max = 2, |
173 |
.nid = 0, /* fill later */ |
174 |
.ops = { |
175 |
.prepare = conexant_capture_pcm_prepare, |
176 |
.cleanup = conexant_capture_pcm_cleanup |
177 |
}, |
178 |
|
179 |
}; |
180 |
|
181 |
|
182 |
static struct hda_pcm_stream conexant_pcm_digital_playback = { |
183 |
.substreams = 1, |
184 |
.channels_min = 2, |
185 |
.channels_max = 2, |
186 |
.nid = 0, /* fill later */ |
187 |
.ops = { |
188 |
.open = conexant_dig_playback_pcm_open, |
189 |
.close = conexant_dig_playback_pcm_close |
190 |
}, |
191 |
}; |
192 |
|
193 |
static struct hda_pcm_stream conexant_pcm_digital_capture = { |
194 |
.substreams = 1, |
195 |
.channels_min = 2, |
196 |
.channels_max = 2, |
197 |
/* NID is set in alc_build_pcms */ |
198 |
}; |
199 |
|
200 |
static int conexant_build_pcms(struct hda_codec *codec) { |
201 |
struct conexant_spec *spec = codec->spec; |
202 |
struct hda_pcm *info = spec->pcm_rec; |
203 |
|
204 |
codec->num_pcms = 1; |
205 |
codec->pcm_info = info; |
206 |
|
207 |
info->name = "CONEXANT Analog"; |
208 |
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_analog_playback; |
209 |
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; |
210 |
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; |
211 |
info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture; |
212 |
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids; |
213 |
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; |
214 |
|
215 |
if (spec->multiout.dig_out_nid) { |
216 |
info++; |
217 |
codec->num_pcms++; |
218 |
info->name = "Conexant Digital"; |
219 |
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_digital_playback; |
220 |
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; |
221 |
if (spec->dig_in_nid) { |
222 |
info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_digital_capture; |
223 |
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; |
224 |
} |
225 |
} |
226 |
|
227 |
return 0; |
228 |
} |
229 |
/* */ |
230 |
static struct hda_input_mux conexant_capture_source = { |
231 |
.num_items = 4, |
232 |
.items = { |
233 |
{ "ExtMic", 0x1 }, |
234 |
{ "IntMic", 0x2 }, |
235 |
{ "Line", 0x3 }, |
236 |
{ "CD", 0x4 } |
237 |
} |
238 |
}; |
239 |
|
240 |
static int conexant_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
241 |
{ |
242 |
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
243 |
struct conexant_spec *spec = codec->spec; |
244 |
|
245 |
return snd_hda_input_mux_info(spec->input_mux, uinfo); |
246 |
} |
247 |
|
248 |
static int conexant_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
249 |
{ |
250 |
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
251 |
struct conexant_spec *spec = codec->spec; |
252 |
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); |
253 |
|
254 |
ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; |
255 |
return 0; |
256 |
} |
257 |
|
258 |
static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
259 |
{ |
260 |
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
261 |
struct conexant_spec *spec = codec->spec; |
262 |
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); |
263 |
|
264 |
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, |
265 |
spec->capsrc_nids[adc_idx], |
266 |
&spec->cur_mux[adc_idx]); |
267 |
} |
268 |
|
269 |
|
270 |
static struct snd_kcontrol_new conexant_mixers[] = { |
271 |
{ |
272 |
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
273 |
.name = "Capture Source", |
274 |
.info = conexant_mux_enum_info, |
275 |
.get = conexant_mux_enum_get, |
276 |
.put = conexant_mux_enum_put |
277 |
}, |
278 |
HDA_CODEC_VOLUME("Mic Bypass Capture Volume",0x19,0x02,HDA_INPUT), |
279 |
HDA_CODEC_MUTE("Mic Bypass Capture Switch",0x19,0x02,HDA_INPUT), |
280 |
HDA_CODEC_VOLUME("Capture Volume",0x12,0x03,HDA_INPUT), |
281 |
HDA_CODEC_MUTE("Capture Switch",0x12,0x03,HDA_INPUT), |
282 |
HDA_CODEC_VOLUME("PCM Volume",0x10,0x00,HDA_OUTPUT), |
283 |
HDA_CODEC_MUTE("PCM Switch",0x10,0x00,HDA_OUTPUT), |
284 |
HDA_CODEC_VOLUME("Headphone Playback Volume",0x13,0x00,HDA_OUTPUT), |
285 |
HDA_CODEC_MUTE("Headphone Playback Switch",0x13,0x00,HDA_OUTPUT), |
286 |
{} |
287 |
}; |
288 |
|
289 |
static int conexant_build_controls(struct hda_codec *codec) { |
290 |
struct conexant_spec *spec = codec->spec; |
291 |
unsigned int i; |
292 |
int err; |
293 |
|
294 |
for (i = 0; i < spec->num_mixers; i++) { |
295 |
err = snd_hda_add_new_ctls(codec, spec->mixers[i]); |
296 |
if (err < 0) |
297 |
return err; |
298 |
} |
299 |
if (spec->multiout.dig_out_nid) { |
300 |
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); |
301 |
if (err < 0) |
302 |
return err; |
303 |
} |
304 |
if (spec->dig_in_nid) { |
305 |
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); |
306 |
if (err < 0) |
307 |
return err; |
308 |
} |
309 |
return 0; |
310 |
} |
311 |
|
312 |
static struct hda_verb conexant_init_verbs[] = { |
313 |
/* Line in, Mic, Built-in Mic */ |
314 |
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, |
315 |
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, |
316 |
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 }, |
317 |
/* HP, Amp */ |
318 |
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, |
319 |
{0x1A, AC_VERB_SET_CONNECT_SEL,0x01}, |
320 |
{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00}, |
321 |
{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03}, |
322 |
/* Record selector: Front mic */ |
323 |
{0x12, AC_VERB_SET_CONNECT_SEL,0x03}, |
324 |
{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, |
325 |
/* SPDIF route: PCM */ |
326 |
{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x0 }, |
327 |
{ } /* end */ |
328 |
}; |
329 |
|
330 |
static int conexant_init(struct hda_codec *codec) { |
331 |
struct conexant_spec *spec = codec->spec; |
332 |
int i; |
333 |
|
334 |
for (i = 0; i < spec->num_init_verbs; i++) |
335 |
snd_hda_sequence_write(codec, spec->init_verbs[i]); |
336 |
return 0; |
337 |
} |
338 |
|
339 |
static void conexant_free(struct hda_codec *codec) { |
340 |
struct conexant_spec *spec = codec->spec; |
341 |
unsigned int i; |
342 |
|
343 |
if (spec->kctl_alloc) { |
344 |
for (i = 0; i < spec->num_kctl_used; i++) |
345 |
kfree(spec->kctl_alloc[i].name); |
346 |
kfree(spec->kctl_alloc); |
347 |
} |
348 |
|
349 |
kfree(codec->spec); |
350 |
} |
351 |
|
352 |
#ifdef CONFIG_PM |
353 |
static int conexant_resume(struct hda_codec *codec) |
354 |
{ |
355 |
struct conexant_spec *spec = codec->spec; |
356 |
int i; |
357 |
|
358 |
codec->patch_ops.init(codec); |
359 |
for (i = 0; i < spec->num_mixers; i++) |
360 |
snd_hda_resume_ctls(codec, spec->mixers[i]); |
361 |
if (spec->multiout.dig_out_nid) |
362 |
snd_hda_resume_spdif_out(codec); |
363 |
if (spec->dig_in_nid) |
364 |
snd_hda_resume_spdif_in(codec); |
365 |
return 0; |
366 |
} |
367 |
#endif |
368 |
|
369 |
static struct hda_codec_ops conexant_patch_ops = { |
370 |
.build_controls = conexant_build_controls, |
371 |
.build_pcms = conexant_build_pcms, |
372 |
.init = conexant_init, |
373 |
.free = conexant_free, |
374 |
#ifdef CONFIG_PM |
375 |
.resume = conexant_resume, |
376 |
#endif |
377 |
}; |
378 |
|
379 |
enum { CONEXANT_LAPTOP }; |
380 |
|
381 |
static struct hda_board_config conexant_cfg_tbl[] = { |
382 |
{ .modelname = "laptop", .config = CONEXANT_LAPTOP }, |
383 |
{} |
384 |
}; |
385 |
|
386 |
static int patch_conexant(struct hda_codec *codec) { |
387 |
struct conexant_spec *spec; |
388 |
int board_config; |
389 |
|
390 |
spec = kzalloc(sizeof(*spec), GFP_KERNEL); |
391 |
if ( !spec ) |
392 |
return -ENOMEM; |
393 |
mutex_init(&spec->amp_mutex); |
394 |
codec->spec = spec; |
395 |
|
396 |
spec->multiout.max_channels = 2; |
397 |
spec->multiout.num_dacs = ARRAY_SIZE(conexant_dac_nids); |
398 |
spec->multiout.dac_nids = conexant_dac_nids; |
399 |
spec->multiout.dig_out_nid = SPDIF_OUT; |
400 |
spec->num_adc_nids = 1; |
401 |
spec->adc_nids = conexant_adc_nids; |
402 |
spec->capsrc_nids = conexant_capsrc_nids; |
403 |
spec->input_mux = &conexant_capture_source; |
404 |
spec->num_mixers = 1; |
405 |
spec->mixers[0] = conexant_mixers; |
406 |
spec->num_init_verbs = 1; |
407 |
spec->init_verbs[0] = conexant_init_verbs; |
408 |
spec->spdif_route = 0; |
409 |
|
410 |
codec->patch_ops = conexant_patch_ops; |
411 |
/* Place holder for different board configurations */ |
412 |
board_config = snd_hda_check_board_config(codec, conexant_cfg_tbl); |
413 |
|
414 |
return 0; |
415 |
} |
416 |
|
417 |
struct hda_codec_preset snd_hda_preset_conexant[] = { |
418 |
{ .id = 0x14f15047, .name = "CONEXANT", .patch = patch_conexant }, |
419 |
{} /* terminator */ |
420 |
}; |