Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/hda/codecs/senarytech.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* HD audio codec driver for Senary HDA audio codec
4
*
5
* Initially based on conexant.c
6
*/
7
8
#include <linux/init.h>
9
#include <linux/delay.h>
10
#include <linux/slab.h>
11
#include <linux/module.h>
12
#include <sound/core.h>
13
#include <sound/jack.h>
14
15
#include <sound/hda_codec.h>
16
#include "hda_local.h"
17
#include "hda_auto_parser.h"
18
#include "hda_beep.h"
19
#include "hda_jack.h"
20
#include "generic.h"
21
22
struct senary_spec {
23
struct hda_gen_spec gen;
24
25
/* extra EAPD pins */
26
unsigned int num_eapds;
27
hda_nid_t eapds[4];
28
hda_nid_t mute_led_eapd;
29
30
unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
31
32
int mute_led_polarity;
33
unsigned int gpio_led;
34
unsigned int gpio_mute_led_mask;
35
unsigned int gpio_mic_led_mask;
36
};
37
38
#ifdef CONFIG_SND_HDA_INPUT_BEEP
39
/* additional beep mixers; private_value will be overwritten */
40
static const struct snd_kcontrol_new senary_beep_mixer[] = {
41
HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
42
HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
43
};
44
45
static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid,
46
int idx, int dir)
47
{
48
struct snd_kcontrol_new *knew;
49
unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
50
int i;
51
52
spec->gen.beep_nid = nid;
53
for (i = 0; i < ARRAY_SIZE(senary_beep_mixer); i++) {
54
knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
55
&senary_beep_mixer[i]);
56
if (!knew)
57
return -ENOMEM;
58
knew->private_value = beep_amp;
59
}
60
return 0;
61
}
62
63
static int senary_auto_parse_beep(struct hda_codec *codec)
64
{
65
struct senary_spec *spec = codec->spec;
66
hda_nid_t nid;
67
68
for_each_hda_codec_node(nid, codec)
69
if ((get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) &&
70
(get_wcaps(codec, nid) & (AC_WCAP_OUT_AMP | AC_WCAP_AMP_OVRD)))
71
return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
72
return 0;
73
}
74
#else
75
#define senary_auto_parse_beep(codec) 0
76
#endif
77
78
/* parse EAPDs */
79
static void senary_auto_parse_eapd(struct hda_codec *codec)
80
{
81
struct senary_spec *spec = codec->spec;
82
hda_nid_t nid;
83
84
for_each_hda_codec_node(nid, codec) {
85
if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
86
continue;
87
if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
88
continue;
89
spec->eapds[spec->num_eapds++] = nid;
90
if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
91
break;
92
}
93
}
94
95
static void senary_auto_turn_eapd(struct hda_codec *codec, int num_pins,
96
const hda_nid_t *pins, bool on)
97
{
98
int i;
99
100
for (i = 0; i < num_pins; i++) {
101
if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
102
snd_hda_codec_write(codec, pins[i], 0,
103
AC_VERB_SET_EAPD_BTLENABLE,
104
on ? 0x02 : 0);
105
}
106
}
107
108
/* turn on/off EAPD according to Master switch */
109
static void senary_auto_vmaster_hook(void *private_data, int enabled)
110
{
111
struct hda_codec *codec = private_data;
112
struct senary_spec *spec = codec->spec;
113
114
senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
115
}
116
117
static void senary_init_gpio_led(struct hda_codec *codec)
118
{
119
struct senary_spec *spec = codec->spec;
120
unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask;
121
122
if (mask) {
123
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
124
mask);
125
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
126
mask);
127
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
128
spec->gpio_led);
129
}
130
}
131
132
static int senary_init(struct hda_codec *codec)
133
{
134
snd_hda_gen_init(codec);
135
senary_init_gpio_led(codec);
136
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
137
138
return 0;
139
}
140
141
static void senary_shutdown(struct hda_codec *codec)
142
{
143
struct senary_spec *spec = codec->spec;
144
145
/* Turn the problematic codec into D3 to avoid spurious noises
146
* from the internal speaker during (and after) reboot
147
*/
148
senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
149
}
150
151
static void senary_remove(struct hda_codec *codec)
152
{
153
senary_shutdown(codec);
154
snd_hda_gen_remove(codec);
155
}
156
157
static int senary_suspend(struct hda_codec *codec)
158
{
159
senary_shutdown(codec);
160
return 0;
161
}
162
163
static int senary_probe(struct hda_codec *codec, const struct hda_device_id *id)
164
{
165
struct senary_spec *spec;
166
int err;
167
168
codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name);
169
170
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
171
if (!spec)
172
return -ENOMEM;
173
snd_hda_gen_spec_init(&spec->gen);
174
codec->spec = spec;
175
176
senary_auto_parse_eapd(codec);
177
spec->gen.own_eapd_ctl = 1;
178
179
if (!spec->gen.vmaster_mute.hook)
180
spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook;
181
182
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
183
184
err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
185
spec->parse_flags);
186
if (err < 0)
187
goto error;
188
189
err = senary_auto_parse_beep(codec);
190
if (err < 0)
191
goto error;
192
193
err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
194
if (err < 0)
195
goto error;
196
197
/* Some laptops with Senary chips show stalls in S3 resume,
198
* which falls into the single-cmd mode.
199
* Better to make reset, then.
200
*/
201
if (!codec->bus->core.sync_write) {
202
codec_info(codec,
203
"Enable sync_write for stable communication\n");
204
codec->bus->core.sync_write = 1;
205
codec->bus->allow_bus_reset = 1;
206
}
207
208
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
209
210
return 0;
211
212
error:
213
senary_remove(codec);
214
return err;
215
}
216
217
static const struct hda_codec_ops senary_codec_ops = {
218
.probe = senary_probe,
219
.remove = senary_remove,
220
.build_controls = snd_hda_gen_build_controls,
221
.build_pcms = snd_hda_gen_build_pcms,
222
.init = senary_init,
223
.unsol_event = snd_hda_jack_unsol_event,
224
.suspend = senary_suspend,
225
.check_power_status = snd_hda_gen_check_power_status,
226
.stream_pm = snd_hda_gen_stream_pm,
227
};
228
229
/*
230
*/
231
232
static const struct hda_device_id snd_hda_id_senary[] = {
233
HDA_CODEC_ID(0x1fa86186, "SN6186"),
234
{} /* terminator */
235
};
236
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_senary);
237
238
MODULE_LICENSE("GPL");
239
MODULE_DESCRIPTION("Senarytech HD-audio codec");
240
241
static struct hda_codec_driver senary_driver = {
242
.id = snd_hda_id_senary,
243
.ops = &senary_codec_ops,
244
};
245
246
module_hda_codec_driver(senary_driver);
247
248