Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/pci/hda/patch_ca0110.c
10817 views
1
/*
2
* HD audio interface patch for Creative X-Fi CA0110-IBG chip
3
*
4
* Copyright (c) 2008 Takashi Iwai <[email protected]>
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 <linux/init.h>
22
#include <linux/delay.h>
23
#include <linux/slab.h>
24
#include <linux/pci.h>
25
#include <sound/core.h>
26
#include "hda_codec.h"
27
#include "hda_local.h"
28
29
/*
30
*/
31
32
struct ca0110_spec {
33
struct auto_pin_cfg autocfg;
34
struct hda_multi_out multiout;
35
hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
36
hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
37
hda_nid_t hp_dac;
38
hda_nid_t input_pins[AUTO_PIN_LAST];
39
hda_nid_t adcs[AUTO_PIN_LAST];
40
hda_nid_t dig_out;
41
hda_nid_t dig_in;
42
unsigned int num_inputs;
43
const char *input_labels[AUTO_PIN_LAST];
44
struct hda_pcm pcm_rec[2]; /* PCM information */
45
};
46
47
/*
48
* PCM callbacks
49
*/
50
static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
51
struct hda_codec *codec,
52
struct snd_pcm_substream *substream)
53
{
54
struct ca0110_spec *spec = codec->spec;
55
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
56
hinfo);
57
}
58
59
static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
60
struct hda_codec *codec,
61
unsigned int stream_tag,
62
unsigned int format,
63
struct snd_pcm_substream *substream)
64
{
65
struct ca0110_spec *spec = codec->spec;
66
return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
67
stream_tag, format, substream);
68
}
69
70
static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
71
struct hda_codec *codec,
72
struct snd_pcm_substream *substream)
73
{
74
struct ca0110_spec *spec = codec->spec;
75
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
76
}
77
78
/*
79
* Digital out
80
*/
81
static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
82
struct hda_codec *codec,
83
struct snd_pcm_substream *substream)
84
{
85
struct ca0110_spec *spec = codec->spec;
86
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
87
}
88
89
static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
90
struct hda_codec *codec,
91
struct snd_pcm_substream *substream)
92
{
93
struct ca0110_spec *spec = codec->spec;
94
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
95
}
96
97
static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
98
struct hda_codec *codec,
99
unsigned int stream_tag,
100
unsigned int format,
101
struct snd_pcm_substream *substream)
102
{
103
struct ca0110_spec *spec = codec->spec;
104
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
105
format, substream);
106
}
107
108
/*
109
* Analog capture
110
*/
111
static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
112
struct hda_codec *codec,
113
unsigned int stream_tag,
114
unsigned int format,
115
struct snd_pcm_substream *substream)
116
{
117
struct ca0110_spec *spec = codec->spec;
118
119
snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
120
stream_tag, 0, format);
121
return 0;
122
}
123
124
static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
125
struct hda_codec *codec,
126
struct snd_pcm_substream *substream)
127
{
128
struct ca0110_spec *spec = codec->spec;
129
130
snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
131
return 0;
132
}
133
134
/*
135
*/
136
137
static const char * const dirstr[2] = { "Playback", "Capture" };
138
139
static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
140
int chan, int dir)
141
{
142
char namestr[44];
143
int type = dir ? HDA_INPUT : HDA_OUTPUT;
144
struct snd_kcontrol_new knew =
145
HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
146
sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
147
return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
148
}
149
150
static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
151
int chan, int dir)
152
{
153
char namestr[44];
154
int type = dir ? HDA_INPUT : HDA_OUTPUT;
155
struct snd_kcontrol_new knew =
156
HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
157
sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
158
return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
159
}
160
161
#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
162
#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
163
#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
164
#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
165
#define add_mono_switch(codec, nid, pfx, chan) \
166
_add_switch(codec, nid, pfx, chan, 0)
167
#define add_mono_volume(codec, nid, pfx, chan) \
168
_add_volume(codec, nid, pfx, chan, 0)
169
170
static int ca0110_build_controls(struct hda_codec *codec)
171
{
172
struct ca0110_spec *spec = codec->spec;
173
struct auto_pin_cfg *cfg = &spec->autocfg;
174
static const char * const prefix[AUTO_CFG_MAX_OUTS] = {
175
"Front", "Surround", NULL, "Side", "Multi"
176
};
177
hda_nid_t mutenid;
178
int i, err;
179
180
for (i = 0; i < spec->multiout.num_dacs; i++) {
181
if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
182
mutenid = spec->out_pins[i];
183
else
184
mutenid = spec->multiout.dac_nids[i];
185
if (!prefix[i]) {
186
err = add_mono_switch(codec, mutenid,
187
"Center", 1);
188
if (err < 0)
189
return err;
190
err = add_mono_switch(codec, mutenid,
191
"LFE", 1);
192
if (err < 0)
193
return err;
194
err = add_mono_volume(codec, spec->multiout.dac_nids[i],
195
"Center", 1);
196
if (err < 0)
197
return err;
198
err = add_mono_volume(codec, spec->multiout.dac_nids[i],
199
"LFE", 1);
200
if (err < 0)
201
return err;
202
} else {
203
err = add_out_switch(codec, mutenid,
204
prefix[i]);
205
if (err < 0)
206
return err;
207
err = add_out_volume(codec, spec->multiout.dac_nids[i],
208
prefix[i]);
209
if (err < 0)
210
return err;
211
}
212
}
213
if (cfg->hp_outs) {
214
if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
215
mutenid = cfg->hp_pins[0];
216
else
217
mutenid = spec->multiout.dac_nids[i];
218
219
err = add_out_switch(codec, mutenid, "Headphone");
220
if (err < 0)
221
return err;
222
if (spec->hp_dac) {
223
err = add_out_volume(codec, spec->hp_dac, "Headphone");
224
if (err < 0)
225
return err;
226
}
227
}
228
for (i = 0; i < spec->num_inputs; i++) {
229
const char *label = spec->input_labels[i];
230
if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
231
mutenid = spec->input_pins[i];
232
else
233
mutenid = spec->adcs[i];
234
err = add_in_switch(codec, mutenid, label);
235
if (err < 0)
236
return err;
237
err = add_in_volume(codec, spec->adcs[i], label);
238
if (err < 0)
239
return err;
240
}
241
242
if (spec->dig_out) {
243
err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
244
if (err < 0)
245
return err;
246
err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
247
if (err < 0)
248
return err;
249
spec->multiout.share_spdif = 1;
250
}
251
if (spec->dig_in) {
252
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
253
if (err < 0)
254
return err;
255
err = add_in_volume(codec, spec->dig_in, "IEC958");
256
}
257
return 0;
258
}
259
260
/*
261
*/
262
static const struct hda_pcm_stream ca0110_pcm_analog_playback = {
263
.substreams = 1,
264
.channels_min = 2,
265
.channels_max = 8,
266
.ops = {
267
.open = ca0110_playback_pcm_open,
268
.prepare = ca0110_playback_pcm_prepare,
269
.cleanup = ca0110_playback_pcm_cleanup
270
},
271
};
272
273
static const struct hda_pcm_stream ca0110_pcm_analog_capture = {
274
.substreams = 1,
275
.channels_min = 2,
276
.channels_max = 2,
277
.ops = {
278
.prepare = ca0110_capture_pcm_prepare,
279
.cleanup = ca0110_capture_pcm_cleanup
280
},
281
};
282
283
static const struct hda_pcm_stream ca0110_pcm_digital_playback = {
284
.substreams = 1,
285
.channels_min = 2,
286
.channels_max = 2,
287
.ops = {
288
.open = ca0110_dig_playback_pcm_open,
289
.close = ca0110_dig_playback_pcm_close,
290
.prepare = ca0110_dig_playback_pcm_prepare
291
},
292
};
293
294
static const struct hda_pcm_stream ca0110_pcm_digital_capture = {
295
.substreams = 1,
296
.channels_min = 2,
297
.channels_max = 2,
298
};
299
300
static int ca0110_build_pcms(struct hda_codec *codec)
301
{
302
struct ca0110_spec *spec = codec->spec;
303
struct hda_pcm *info = spec->pcm_rec;
304
305
codec->pcm_info = info;
306
codec->num_pcms = 0;
307
308
info->name = "CA0110 Analog";
309
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
310
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
311
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
312
spec->multiout.max_channels;
313
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
314
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
315
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
316
codec->num_pcms++;
317
318
if (!spec->dig_out && !spec->dig_in)
319
return 0;
320
321
info++;
322
info->name = "CA0110 Digital";
323
info->pcm_type = HDA_PCM_TYPE_SPDIF;
324
if (spec->dig_out) {
325
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
326
ca0110_pcm_digital_playback;
327
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
328
}
329
if (spec->dig_in) {
330
info->stream[SNDRV_PCM_STREAM_CAPTURE] =
331
ca0110_pcm_digital_capture;
332
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
333
}
334
codec->num_pcms++;
335
336
return 0;
337
}
338
339
static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
340
{
341
if (pin) {
342
snd_hda_codec_write(codec, pin, 0,
343
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
344
if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
345
snd_hda_codec_write(codec, pin, 0,
346
AC_VERB_SET_AMP_GAIN_MUTE,
347
AMP_OUT_UNMUTE);
348
}
349
if (dac)
350
snd_hda_codec_write(codec, dac, 0,
351
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
352
}
353
354
static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
355
{
356
if (pin) {
357
snd_hda_codec_write(codec, pin, 0,
358
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
359
if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
360
snd_hda_codec_write(codec, pin, 0,
361
AC_VERB_SET_AMP_GAIN_MUTE,
362
AMP_IN_UNMUTE(0));
363
}
364
if (adc)
365
snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
366
AMP_IN_UNMUTE(0));
367
}
368
369
static int ca0110_init(struct hda_codec *codec)
370
{
371
struct ca0110_spec *spec = codec->spec;
372
struct auto_pin_cfg *cfg = &spec->autocfg;
373
int i;
374
375
for (i = 0; i < spec->multiout.num_dacs; i++)
376
init_output(codec, spec->out_pins[i],
377
spec->multiout.dac_nids[i]);
378
init_output(codec, cfg->hp_pins[0], spec->hp_dac);
379
init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
380
381
for (i = 0; i < spec->num_inputs; i++)
382
init_input(codec, spec->input_pins[i], spec->adcs[i]);
383
init_input(codec, cfg->dig_in_pin, spec->dig_in);
384
return 0;
385
}
386
387
static void ca0110_free(struct hda_codec *codec)
388
{
389
kfree(codec->spec);
390
}
391
392
static const struct hda_codec_ops ca0110_patch_ops = {
393
.build_controls = ca0110_build_controls,
394
.build_pcms = ca0110_build_pcms,
395
.init = ca0110_init,
396
.free = ca0110_free,
397
};
398
399
400
static void parse_line_outs(struct hda_codec *codec)
401
{
402
struct ca0110_spec *spec = codec->spec;
403
struct auto_pin_cfg *cfg = &spec->autocfg;
404
int i, n;
405
unsigned int def_conf;
406
hda_nid_t nid;
407
408
n = 0;
409
for (i = 0; i < cfg->line_outs; i++) {
410
nid = cfg->line_out_pins[i];
411
def_conf = snd_hda_codec_get_pincfg(codec, nid);
412
if (!def_conf)
413
continue; /* invalid pin */
414
if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
415
continue;
416
spec->out_pins[n++] = nid;
417
}
418
spec->multiout.dac_nids = spec->dacs;
419
spec->multiout.num_dacs = n;
420
spec->multiout.max_channels = n * 2;
421
}
422
423
static void parse_hp_out(struct hda_codec *codec)
424
{
425
struct ca0110_spec *spec = codec->spec;
426
struct auto_pin_cfg *cfg = &spec->autocfg;
427
int i;
428
unsigned int def_conf;
429
hda_nid_t nid, dac;
430
431
if (!cfg->hp_outs)
432
return;
433
nid = cfg->hp_pins[0];
434
def_conf = snd_hda_codec_get_pincfg(codec, nid);
435
if (!def_conf) {
436
cfg->hp_outs = 0;
437
return;
438
}
439
if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
440
return;
441
442
for (i = 0; i < cfg->line_outs; i++)
443
if (dac == spec->dacs[i])
444
break;
445
if (i >= cfg->line_outs) {
446
spec->hp_dac = dac;
447
spec->multiout.hp_nid = dac;
448
}
449
}
450
451
static void parse_input(struct hda_codec *codec)
452
{
453
struct ca0110_spec *spec = codec->spec;
454
struct auto_pin_cfg *cfg = &spec->autocfg;
455
hda_nid_t nid, pin;
456
int n, i, j;
457
458
n = 0;
459
nid = codec->start_nid;
460
for (i = 0; i < codec->num_nodes; i++, nid++) {
461
unsigned int wcaps = get_wcaps(codec, nid);
462
unsigned int type = get_wcaps_type(wcaps);
463
if (type != AC_WID_AUD_IN)
464
continue;
465
if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
466
continue;
467
if (pin == cfg->dig_in_pin) {
468
spec->dig_in = nid;
469
continue;
470
}
471
for (j = 0; j < cfg->num_inputs; j++)
472
if (cfg->inputs[j].pin == pin)
473
break;
474
if (j >= cfg->num_inputs)
475
continue;
476
spec->input_pins[n] = pin;
477
spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
478
spec->adcs[n] = nid;
479
n++;
480
}
481
spec->num_inputs = n;
482
}
483
484
static void parse_digital(struct hda_codec *codec)
485
{
486
struct ca0110_spec *spec = codec->spec;
487
struct auto_pin_cfg *cfg = &spec->autocfg;
488
489
if (cfg->dig_outs &&
490
snd_hda_get_connections(codec, cfg->dig_out_pins[0],
491
&spec->dig_out, 1) == 1)
492
spec->multiout.dig_out_nid = spec->dig_out;
493
}
494
495
static int ca0110_parse_auto_config(struct hda_codec *codec)
496
{
497
struct ca0110_spec *spec = codec->spec;
498
int err;
499
500
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
501
if (err < 0)
502
return err;
503
504
parse_line_outs(codec);
505
parse_hp_out(codec);
506
parse_digital(codec);
507
parse_input(codec);
508
return 0;
509
}
510
511
512
static int patch_ca0110(struct hda_codec *codec)
513
{
514
struct ca0110_spec *spec;
515
int err;
516
517
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
518
if (!spec)
519
return -ENOMEM;
520
codec->spec = spec;
521
522
codec->bus->needs_damn_long_delay = 1;
523
524
err = ca0110_parse_auto_config(codec);
525
if (err < 0)
526
goto error;
527
528
codec->patch_ops = ca0110_patch_ops;
529
530
return 0;
531
532
error:
533
kfree(codec->spec);
534
codec->spec = NULL;
535
return err;
536
}
537
538
539
/*
540
* patch entries
541
*/
542
static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
543
{ .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
544
{ .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
545
{ .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
546
{} /* terminator */
547
};
548
549
MODULE_ALIAS("snd-hda-codec-id:1102000a");
550
MODULE_ALIAS("snd-hda-codec-id:1102000b");
551
MODULE_ALIAS("snd-hda-codec-id:1102000d");
552
553
MODULE_LICENSE("GPL");
554
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
555
556
static struct hda_codec_preset_list ca0110_list = {
557
.preset = snd_hda_preset_ca0110,
558
.owner = THIS_MODULE,
559
};
560
561
static int __init patch_ca0110_init(void)
562
{
563
return snd_hda_add_codec_preset(&ca0110_list);
564
}
565
566
static void __exit patch_ca0110_exit(void)
567
{
568
snd_hda_delete_codec_preset(&ca0110_list);
569
}
570
571
module_init(patch_ca0110_init)
572
module_exit(patch_ca0110_exit)
573
574