summaryrefslogtreecommitdiff
path: root/sound/pci/hda/hda_auto_parser.c
blob: 7cc3a16882409255fb44760755bc1a5db29d4d7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
 * BIOS auto-parser helper functions for HD-audio
 *
 * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
 *
 * This driver is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/slab.h>
#include <linux/export.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_auto_parser.h"

#define SFX	"hda_codec: "

int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
			  const struct hda_verb *list)
{
	const struct hda_verb **v;
	snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
	v = snd_array_new(&spec->verbs);
	if (!v)
		return -ENOMEM;
	*v = list;
	return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs);

void snd_hda_gen_apply_verbs(struct hda_codec *codec)
{
	struct hda_gen_spec *spec = codec->spec;
	int i;
	for (i = 0; i < spec->verbs.used; i++) {
		struct hda_verb **v = snd_array_elem(&spec->verbs, i);
		snd_hda_sequence_write(codec, *v);
	}
}
EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs);

void snd_hda_apply_pincfgs(struct hda_codec *codec,
			   const struct hda_pintbl *cfg)
{
	for (; cfg->nid; cfg++)
		snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
}
EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs);

void snd_hda_apply_fixup(struct hda_codec *codec, int action)
{
	struct hda_gen_spec *spec = codec->spec;
	int id = spec->fixup_id;
#ifdef CONFIG_SND_DEBUG_VERBOSE
	const char *modelname = spec->fixup_name;
#endif
	int depth = 0;

	if (!spec->fixup_list)
		return;

	while (id >= 0) {
		const struct hda_fixup *fix = spec->fixup_list + id;

		switch (fix->type) {
		case HDA_FIXUP_PINS:
			if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
				break;
			snd_printdd(KERN_INFO SFX
				    "%s: Apply pincfg for %s\n",
				    codec->chip_name, modelname);
			snd_hda_apply_pincfgs(codec, fix->v.pins);
			break;
		case HDA_FIXUP_VERBS:
			if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs)
				break;
			snd_printdd(KERN_INFO SFX
				    "%s: Apply fix-verbs for %s\n",
				    codec->chip_name, modelname);
			snd_hda_gen_add_verbs(codec->spec, fix->v.verbs);
			break;
		case HDA_FIXUP_FUNC:
			if (!fix->v.func)
				break;
			snd_printdd(KERN_INFO SFX
				    "%s: Apply fix-func for %s\n",
				    codec->chip_name, modelname);
			fix->v.func(codec, fix, action);
			break;
		default:
			snd_printk(KERN_ERR SFX
				   "%s: Invalid fixup type %d\n",
				   codec->chip_name, fix->type);
			break;
		}
		if (!fix->chained)
			break;
		if (++depth > 10)
			break;
		id = fix->chain_id;
	}
}
EXPORT_SYMBOL_HDA(snd_hda_apply_fixup);

void snd_hda_pick_fixup(struct hda_codec *codec,
			const struct hda_model_fixup *models,
			const struct snd_pci_quirk *quirk,
			const struct hda_fixup *fixlist)
{
	struct hda_gen_spec *spec = codec->spec;
	const struct snd_pci_quirk *q;
	int id = -1;
	const char *name = NULL;

	/* when model=nofixup is given, don't pick up any fixups */
	if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
		spec->fixup_list = NULL;
		spec->fixup_id = -1;
		return;
	}

	if (codec->modelname && models) {
		while (models->name) {
			if (!strcmp(codec->modelname, models->name)) {
				id = models->id;
				name = models->name;
				break;
			}
			models++;
		}
	}
	if (id < 0) {
		q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
		if (q) {
			id = q->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE
			name = q->name;
#endif
		}
	}
	if (id < 0) {
		for (q = quirk; q->subvendor; q++) {
			unsigned int vendorid =
				q->subdevice | (q->subvendor << 16);
			if (vendorid == codec->subsystem_id) {
				id = q->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE
				name = q->name;
#endif
				break;
			}
		}
	}

	spec->fixup_id = id;
	if (id >= 0) {
		spec->fixup_list = fixlist;
		spec->fixup_name = name;
	}
}
EXPORT_SYMBOL_HDA(snd_hda_pick_fixup);