Blame view

sound/pci/hda/hda_jack.c 9.08 KB
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * Jack-detection handling for HD-audio
   *
   * Copyright (c) 2011 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/init.h>
  #include <linux/slab.h>
bf815bf0a   Takashi Iwai   ALSA: hda - Add m...
14
  #include <linux/export.h>
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
15
  #include <sound/core.h>
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
16
  #include <sound/control.h>
aad37dbd5   Takashi Iwai   ALSA: hda - Merge...
17
  #include <sound/jack.h>
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
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
  #include "hda_codec.h"
  #include "hda_local.h"
  #include "hda_jack.h"
  
  /* execute pin sense measurement */
  static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
  {
  	u32 pincap;
  
  	if (!codec->no_trigger_sense) {
  		pincap = snd_hda_query_pin_caps(codec, nid);
  		if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
  			snd_hda_codec_read(codec, nid, 0,
  					AC_VERB_SET_PIN_SENSE, 0);
  	}
  	return snd_hda_codec_read(codec, nid, 0,
  				  AC_VERB_GET_PIN_SENSE, 0);
  }
  
  /**
   * snd_hda_jack_tbl_get - query the jack-table entry for the given NID
   */
  struct hda_jack_tbl *
  snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
  {
  	struct hda_jack_tbl *jack = codec->jacktbl.list;
  	int i;
  
  	if (!nid || !jack)
  		return NULL;
  	for (i = 0; i < codec->jacktbl.used; i++, jack++)
  		if (jack->nid == nid)
  			return jack;
  	return NULL;
  }
  EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_get);
  
  /**
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
   * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
   */
  struct hda_jack_tbl *
  snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag)
  {
  	struct hda_jack_tbl *jack = codec->jacktbl.list;
  	int i;
  
  	if (!tag || !jack)
  		return NULL;
  	for (i = 0; i < codec->jacktbl.used; i++, jack++)
  		if (jack->tag == tag)
  			return jack;
  	return NULL;
  }
  EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_get_from_tag);
  
  /**
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
74
75
76
77
78
79
80
81
82
83
84
85
86
87
   * snd_hda_jack_tbl_new - create a jack-table entry for the given NID
   */
  struct hda_jack_tbl *
  snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid)
  {
  	struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
  	if (jack)
  		return jack;
  	snd_array_init(&codec->jacktbl, sizeof(*jack), 16);
  	jack = snd_array_new(&codec->jacktbl);
  	if (!jack)
  		return NULL;
  	jack->nid = nid;
  	jack->jack_dirty = 1;
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
88
  	jack->tag = codec->jacktbl.used;
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
89
90
  	return jack;
  }
d1cb62008   David Henningsson   ALSA: HDA: Jack: ...
91
  EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_new);
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
92
93
94
  
  void snd_hda_jack_tbl_clear(struct hda_codec *codec)
  {
31ef22579   Takashi Iwai   ALSA: hda - Integ...
95
96
97
98
99
100
101
102
103
104
105
  #ifdef CONFIG_SND_HDA_INPUT_JACK
  	/* free jack instances manually when clearing/reconfiguring */
  	if (!codec->bus->shutdown && codec->jacktbl.list) {
  		struct hda_jack_tbl *jack = codec->jacktbl.list;
  		int i;
  		for (i = 0; i < codec->jacktbl.used; i++, jack++) {
  			if (jack->jack)
  				snd_device_free(codec->bus->card, jack->jack);
  		}
  	}
  #endif
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
106
107
108
109
110
111
112
  	snd_array_free(&codec->jacktbl);
  }
  
  /* update the cached value and notification flag if needed */
  static void jack_detect_update(struct hda_codec *codec,
  			       struct hda_jack_tbl *jack)
  {
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
113
  	if (jack->jack_dirty || !jack->jack_detect) {
35be544af   Takashi Iwai   ALSA: Introduce c...
114
  		jack->pin_sense = read_pin_sense(codec, jack->nid);
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
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
  		jack->jack_dirty = 0;
  	}
  }
  
  /**
   * snd_hda_set_dirty_all - Mark all the cached as dirty
   *
   * This function sets the dirty flag to all entries of jack table.
   * It's called from the resume path in hda_codec.c.
   */
  void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
  {
  	struct hda_jack_tbl *jack = codec->jacktbl.list;
  	int i;
  
  	for (i = 0; i < codec->jacktbl.used; i++, jack++)
  		if (jack->nid)
  			jack->jack_dirty = 1;
  }
  EXPORT_SYMBOL_HDA(snd_hda_jack_set_dirty_all);
  
  /**
   * snd_hda_pin_sense - execute pin sense measurement
   * @codec: the CODEC to sense
   * @nid: the pin NID to sense
   *
   * Execute necessary pin sense measurement and return its Presence Detect,
   * Impedance, ELD Valid etc. status bits.
   */
  u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
  {
  	struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
  	if (jack) {
  		jack_detect_update(codec, jack);
  		return jack->pin_sense;
  	}
  	return read_pin_sense(codec, nid);
  }
  EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
35be544af   Takashi Iwai   ALSA: Introduce c...
154
  #define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
155
156
157
158
159
160
161
162
163
164
  /**
   * snd_hda_jack_detect - query pin Presence Detect status
   * @codec: the CODEC to sense
   * @nid: the pin NID to sense
   *
   * Query and return the pin's Presence Detect status.
   */
  int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
  {
  	u32 sense = snd_hda_pin_sense(codec, nid);
35be544af   Takashi Iwai   ALSA: Introduce c...
165
  	return get_jack_plug_state(sense);
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
166
167
168
169
170
171
172
  }
  EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
  
  /**
   * snd_hda_jack_detect_enable - enable the jack-detection
   */
  int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
173
  			       unsigned char action)
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
174
175
176
177
  {
  	struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid);
  	if (!jack)
  		return -ENOMEM;
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
178
  	if (jack->jack_detect)
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
179
  		return 0; /* already registered */
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
180
181
182
  	jack->jack_detect = 1;
  	if (action)
  		jack->action = action;
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
183
184
  	return snd_hda_codec_write_cache(codec, nid, 0,
  					 AC_VERB_SET_UNSOLICITED_ENABLE,
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
185
  					 AC_USRSP_EN | jack->tag);
1835a0f9a   Takashi Iwai   ALSA: hda - Cache...
186
187
  }
  EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
188

01a61e12b   Takashi Iwai   ALSA: hda - Creat...
189
  /**
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
190
191
192
193
194
   * snd_hda_jack_report_sync - sync the states of all jacks and report if changed
   */
  void snd_hda_jack_report_sync(struct hda_codec *codec)
  {
  	struct hda_jack_tbl *jack = codec->jacktbl.list;
35be544af   Takashi Iwai   ALSA: Introduce c...
195
  	int i, state;
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
196
197
198
199
  
  	for (i = 0; i < codec->jacktbl.used; i++, jack++)
  		if (jack->nid) {
  			jack_detect_update(codec, jack);
cfc7c9d30   Takashi Iwai   ALSA: hda/jack - ...
200
201
  			if (!jack->kctl)
  				continue;
35be544af   Takashi Iwai   ALSA: Introduce c...
202
  			state = get_jack_plug_state(jack->pin_sense);
aad37dbd5   Takashi Iwai   ALSA: hda - Merge...
203
  			snd_kctl_jack_report(codec->bus->card, jack->kctl, state);
31ef22579   Takashi Iwai   ALSA: hda - Integ...
204
205
206
207
208
  #ifdef CONFIG_SND_HDA_INPUT_JACK
  			if (jack->jack)
  				snd_jack_report(jack->jack,
  						state ? jack->type : 0);
  #endif
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
209
210
211
  		}
  }
  EXPORT_SYMBOL_HDA(snd_hda_jack_report_sync);
31ef22579   Takashi Iwai   ALSA: hda - Integ...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
  #ifdef CONFIG_SND_HDA_INPUT_JACK
  /* guess the jack type from the pin-config */
  static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid)
  {
  	unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
  	switch (get_defcfg_device(def_conf)) {
  	case AC_JACK_LINE_OUT:
  	case AC_JACK_SPEAKER:
  		return SND_JACK_LINEOUT;
  	case AC_JACK_HP_OUT:
  		return SND_JACK_HEADPHONE;
  	case AC_JACK_SPDIF_OUT:
  	case AC_JACK_DIG_OTHER_OUT:
  		return SND_JACK_AVOUT;
  	case AC_JACK_MIC_IN:
  		return SND_JACK_MICROPHONE;
  	default:
  		return SND_JACK_LINEIN;
  	}
  }
  
  static void hda_free_jack_priv(struct snd_jack *jack)
  {
  	struct hda_jack_tbl *jacks = jack->private_data;
  	jacks->nid = 0;
  	jacks->jack = NULL;
  }
  #endif
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
240
241
242
243
244
245
246
247
248
249
250
  /**
   * snd_hda_jack_add_kctl - Add a kctl for the given pin
   *
   * This assigns a jack-detection kctl to the given pin.  The kcontrol
   * will have the given name and index.
   */
  int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
  			  const char *name, int idx)
  {
  	struct hda_jack_tbl *jack;
  	struct snd_kcontrol *kctl;
31ef22579   Takashi Iwai   ALSA: hda - Integ...
251
  	int err, state;
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
252

3a93897ea   Takashi Iwai   ALSA: hda - Manag...
253
  	jack = snd_hda_jack_tbl_new(codec, nid);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
254
255
256
257
  	if (!jack)
  		return 0;
  	if (jack->kctl)
  		return 0; /* already created */
35be544af   Takashi Iwai   ALSA: Introduce c...
258
  	kctl = snd_kctl_jack_new(name, idx, codec);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
259
260
  	if (!kctl)
  		return -ENOMEM;
31ef22579   Takashi Iwai   ALSA: hda - Integ...
261
262
263
  	err = snd_hda_ctl_add(codec, nid, kctl);
  	if (err < 0)
  		return err;
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
264
  	jack->kctl = kctl;
31ef22579   Takashi Iwai   ALSA: hda - Integ...
265
266
267
268
269
270
271
272
273
274
275
  	state = snd_hda_jack_detect(codec, nid);
  	snd_kctl_jack_report(codec->bus->card, kctl, state);
  #ifdef CONFIG_SND_HDA_INPUT_JACK
  	jack->type = get_input_jack_type(codec, nid);
  	err = snd_jack_new(codec->bus->card, name, jack->type, &jack->jack);
  	if (err < 0)
  		return err;
  	jack->jack->private_data = jack;
  	jack->jack->private_free = hda_free_jack_priv;
  	snd_jack_report(jack->jack, state ? jack->type : 0);
  #endif
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
276
277
  	return 0;
  }
d1cb62008   David Henningsson   ALSA: HDA: Jack: ...
278
  EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
279

201e06ffa   Takashi Iwai   ALSA: hda - Give ...
280
  static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
281
282
  			 const struct auto_pin_cfg *cfg)
  {
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
283
  	unsigned int def_conf, conn;
201e06ffa   Takashi Iwai   ALSA: hda - Give ...
284
285
  	char name[44];
  	int idx, err;
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
286

01a61e12b   Takashi Iwai   ALSA: hda - Creat...
287
288
289
290
  	if (!nid)
  		return 0;
  	if (!is_jack_detectable(codec, nid))
  		return 0;
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
291
292
293
294
  	def_conf = snd_hda_codec_get_pincfg(codec, nid);
  	conn = get_defcfg_connect(def_conf);
  	if (conn != AC_JACK_PORT_COMPLEX)
  		return 0;
201e06ffa   Takashi Iwai   ALSA: hda - Give ...
295
296
  	snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx);
  	err = snd_hda_jack_add_kctl(codec, nid, name, idx);
3a93897ea   Takashi Iwai   ALSA: hda - Manag...
297
298
299
  	if (err < 0)
  		return err;
  	return snd_hda_jack_detect_enable(codec, nid, 0);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
300
301
302
303
  }
  
  /**
   * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
304
305
306
307
308
309
310
311
   */
  int snd_hda_jack_add_kctls(struct hda_codec *codec,
  			   const struct auto_pin_cfg *cfg)
  {
  	const hda_nid_t *p;
  	int i, err;
  
  	for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
201e06ffa   Takashi Iwai   ALSA: hda - Give ...
312
  		err = add_jack_kctl(codec, *p, cfg);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
313
314
315
316
317
318
  		if (err < 0)
  			return err;
  	}
  	for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
  		if (*p == *cfg->line_out_pins) /* might be duplicated */
  			break;
201e06ffa   Takashi Iwai   ALSA: hda - Give ...
319
  		err = add_jack_kctl(codec, *p, cfg);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
320
321
322
323
324
325
  		if (err < 0)
  			return err;
  	}
  	for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
  		if (*p == *cfg->line_out_pins) /* might be duplicated */
  			break;
201e06ffa   Takashi Iwai   ALSA: hda - Give ...
326
  		err = add_jack_kctl(codec, *p, cfg);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
327
328
329
330
  		if (err < 0)
  			return err;
  	}
  	for (i = 0; i < cfg->num_inputs; i++) {
201e06ffa   Takashi Iwai   ALSA: hda - Give ...
331
  		err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
332
333
334
335
  		if (err < 0)
  			return err;
  	}
  	for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
201e06ffa   Takashi Iwai   ALSA: hda - Give ...
336
  		err = add_jack_kctl(codec, *p, cfg);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
337
338
339
  		if (err < 0)
  			return err;
  	}
201e06ffa   Takashi Iwai   ALSA: hda - Give ...
340
  	err = add_jack_kctl(codec, cfg->dig_in_pin, cfg);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
341
342
  	if (err < 0)
  		return err;
201e06ffa   Takashi Iwai   ALSA: hda - Give ...
343
  	err = add_jack_kctl(codec, cfg->mono_out_pin, cfg);
01a61e12b   Takashi Iwai   ALSA: hda - Creat...
344
345
346
347
348
  	if (err < 0)
  		return err;
  	return 0;
  }
  EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls);