diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/patch_intelhdmi.c | 120 |
1 files changed, 119 insertions, 1 deletions
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 3c68aa9742d7..1c374f11ed07 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c | |||
@@ -213,6 +213,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { | |||
213 | }; | 213 | }; |
214 | 214 | ||
215 | 215 | ||
216 | /* | ||
217 | * HDA/HDMI auto parsing | ||
218 | */ | ||
219 | |||
216 | static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) | 220 | static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) |
217 | { | 221 | { |
218 | int i; | 222 | int i; |
@@ -225,6 +229,113 @@ static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) | |||
225 | return -EINVAL; | 229 | return -EINVAL; |
226 | } | 230 | } |
227 | 231 | ||
232 | static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) | ||
233 | { | ||
234 | struct intel_hdmi_spec *spec = codec->spec; | ||
235 | hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; | ||
236 | int conn_len, curr; | ||
237 | int index; | ||
238 | |||
239 | if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { | ||
240 | snd_printk(KERN_WARNING | ||
241 | "HDMI: pin %d wcaps %#x " | ||
242 | "does not support connection list\n", | ||
243 | pin_nid, get_wcaps(codec, pin_nid)); | ||
244 | return -EINVAL; | ||
245 | } | ||
246 | |||
247 | conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, | ||
248 | HDA_MAX_CONNECTIONS); | ||
249 | if (conn_len > 1) | ||
250 | curr = snd_hda_codec_read(codec, pin_nid, 0, | ||
251 | AC_VERB_GET_CONNECT_SEL, 0); | ||
252 | else | ||
253 | curr = 0; | ||
254 | |||
255 | index = hda_node_index(spec->pin, pin_nid); | ||
256 | if (index < 0) | ||
257 | return -EINVAL; | ||
258 | |||
259 | spec->pin_cvt[index] = conn_list[curr]; | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) | ||
265 | { | ||
266 | struct intel_hdmi_spec *spec = codec->spec; | ||
267 | |||
268 | if (spec->num_pins >= INTEL_HDMI_PINS) { | ||
269 | snd_printk(KERN_WARNING | ||
270 | "HDMI: no space for pin %d \n", pin_nid); | ||
271 | return -EINVAL; | ||
272 | } | ||
273 | |||
274 | spec->pin[spec->num_pins] = pin_nid; | ||
275 | spec->num_pins++; | ||
276 | |||
277 | /* | ||
278 | * It is assumed that converter nodes come first in the node list and | ||
279 | * hence have been registered and usable now. | ||
280 | */ | ||
281 | return intel_hdmi_read_pin_conn(codec, pin_nid); | ||
282 | } | ||
283 | |||
284 | static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) | ||
285 | { | ||
286 | struct intel_hdmi_spec *spec = codec->spec; | ||
287 | |||
288 | if (spec->num_cvts >= INTEL_HDMI_CVTS) { | ||
289 | snd_printk(KERN_WARNING | ||
290 | "HDMI: no space for converter %d \n", nid); | ||
291 | return -EINVAL; | ||
292 | } | ||
293 | |||
294 | spec->cvt[spec->num_cvts] = nid; | ||
295 | spec->num_cvts++; | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int intel_hdmi_parse_codec(struct hda_codec *codec) | ||
301 | { | ||
302 | hda_nid_t nid; | ||
303 | int i, nodes; | ||
304 | |||
305 | nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); | ||
306 | if (!nid || nodes < 0) { | ||
307 | snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); | ||
308 | return -EINVAL; | ||
309 | } | ||
310 | |||
311 | for (i = 0; i < nodes; i++, nid++) { | ||
312 | unsigned int caps; | ||
313 | unsigned int type; | ||
314 | |||
315 | caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); | ||
316 | type = get_wcaps_type(caps); | ||
317 | |||
318 | if (!(caps & AC_WCAP_DIGITAL)) | ||
319 | continue; | ||
320 | |||
321 | switch (type) { | ||
322 | case AC_WID_AUD_OUT: | ||
323 | if (intel_hdmi_add_cvt(codec, nid) < 0) | ||
324 | return -EINVAL; | ||
325 | break; | ||
326 | case AC_WID_PIN: | ||
327 | caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); | ||
328 | if (!(caps & AC_PINCAP_HDMI)) | ||
329 | continue; | ||
330 | if (intel_hdmi_add_pin(codec, nid) < 0) | ||
331 | return -EINVAL; | ||
332 | break; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | return 0; | ||
337 | } | ||
338 | |||
228 | /* | 339 | /* |
229 | * HDMI routines | 340 | * HDMI routines |
230 | */ | 341 | */ |
@@ -756,8 +867,15 @@ static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) | |||
756 | if (spec == NULL) | 867 | if (spec == NULL) |
757 | return -ENOMEM; | 868 | return -ENOMEM; |
758 | 869 | ||
759 | *spec = static_specs[spec_id]; | ||
760 | codec->spec = spec; | 870 | codec->spec = spec; |
871 | if (intel_hdmi_parse_codec(codec) < 0) { | ||
872 | codec->spec = NULL; | ||
873 | kfree(spec); | ||
874 | return -EINVAL; | ||
875 | } | ||
876 | if (memcmp(spec, static_specs + spec_id, sizeof(*spec))) | ||
877 | snd_printk(KERN_WARNING | ||
878 | "HDMI: auto parse disagree with known config\n"); | ||
761 | codec->patch_ops = intel_hdmi_patch_ops; | 879 | codec->patch_ops = intel_hdmi_patch_ops; |
762 | 880 | ||
763 | for (i = 0; i < spec->num_pins; i++) | 881 | for (i = 0; i < spec->num_pins; i++) |