937 lines
21 KiB
PHP
937 lines
21 KiB
PHP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; Universal Interface for Intel High Definition Audio Codec ;
|
|
; ;
|
|
; Generic widget tree parser ;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; widget node for parsing
|
|
struc HDA_GNODE
|
|
{
|
|
.nid dw ? ;NID of this widget
|
|
.nconns dw ? ;number of input connections
|
|
.conn_list dd ?
|
|
.slist dw ? ;temporary list
|
|
dw ?
|
|
|
|
.wid_caps dd ? ;widget capabilities
|
|
.type db ? ;widget type
|
|
.pin_ctl db ? ;pin controls
|
|
.checked db ? ;the flag indicates that the node is already parsed
|
|
.pin_caps dd ? ;pin widget capabilities
|
|
.def_cfg dd ? ;default configuration
|
|
.amp_out_caps dd ? ;AMP out capabilities
|
|
.amp_in_caps dd ? ;AMP in capabilities
|
|
.next dd ? ; struct list_head list
|
|
.sizeof:
|
|
}
|
|
|
|
virtual at 0
|
|
HDA_GNODE HDA_GNODE
|
|
end virtual
|
|
|
|
struc HDA_GSPEC
|
|
{
|
|
.dac_node dd ? ;DAC node
|
|
dd ?
|
|
.out_pin_node dd ? ;Output pin (Line-Out) node
|
|
dd ?
|
|
|
|
.def_amp_in_caps dd ?
|
|
.def_amp_out_caps dd ?
|
|
|
|
; .pcm_rec dd ? ;PCM information
|
|
.nid_list dd 0 ;list of widgets
|
|
}
|
|
|
|
struc VOLUME_CTL
|
|
{
|
|
.out_amp_node dd 0 ;Asper+ : To get/set volume
|
|
.num_steps db ? ; num_steps=NumSteps+1
|
|
.step_size db ? ; step_size=StepSize+1
|
|
.maxDb dd ? ; Max volume in Db. maxDb=(num_steps*step_size/4*100)
|
|
}
|
|
|
|
; retrieve the default device type from the default config value
|
|
|
|
proc defcfg_type stdcall, node:dword
|
|
push edx
|
|
mov edx, [node]
|
|
mov eax, [edx + HDA_GNODE.def_cfg]
|
|
and eax, AC_DEFCFG_DEVICE
|
|
shr eax, AC_DEFCFG_DEVICE_SHIFT
|
|
pop edx
|
|
ret
|
|
endp
|
|
|
|
proc defcfg_location stdcall, node:dword
|
|
push edx
|
|
mov edx, [node]
|
|
mov eax, [edx + HDA_GNODE.def_cfg]
|
|
and eax, AC_DEFCFG_LOCATION
|
|
shr eax, AC_DEFCFG_LOCATION_SHIFT
|
|
pop edx
|
|
ret
|
|
endp
|
|
|
|
proc defcfg_port_conn stdcall, node:dword
|
|
push edx
|
|
mov edx, [node]
|
|
mov eax, [edx + HDA_GNODE.def_cfg]
|
|
and eax, AC_DEFCFG_PORT_CONN
|
|
shr eax, AC_DEFCFG_PORT_CONN_SHIFT
|
|
pop edx
|
|
ret
|
|
endp
|
|
|
|
proc defcfg_color stdcall, node:dword
|
|
push edx
|
|
mov edx, [node]
|
|
mov eax, [edx + HDA_GNODE.def_cfg]
|
|
and eax, AC_DEFCFG_COLOR
|
|
shr eax, AC_DEFCFG_COLOR_SHIFT
|
|
pop edx
|
|
ret
|
|
endp
|
|
|
|
|
|
; destructor
|
|
proc snd_hda_generic_free
|
|
push eax ebx edx edi
|
|
; free all widgets
|
|
mov ebx, [spec.nid_list] ; ebx = 1st node address
|
|
test ebx, ebx
|
|
jz .out
|
|
mov edx, [ebx + HDA_GNODE.next] ;edx = 2nd node address
|
|
|
|
.next:
|
|
test edx, edx
|
|
jz .free_head
|
|
|
|
mov eax, [edx + HDA_GNODE.conn_list]
|
|
lea edi, [edx + HDA_GNODE.slist]
|
|
cmp eax, edi
|
|
je @f
|
|
pusha
|
|
call Kfree ;free conn_list
|
|
popa
|
|
@@:
|
|
mov eax, edx
|
|
mov edx, [edx + HDA_GNODE.next]
|
|
pusha
|
|
call Kfree ;free node
|
|
popa
|
|
jmp .next
|
|
.free_head:
|
|
mov eax, [spec.nid_list]
|
|
pusha
|
|
call Kfree ;free the very 1st node in the list
|
|
popa
|
|
mov [spec.nid_list], 0
|
|
.out:
|
|
pop edi edx ebx eax
|
|
ret
|
|
endp
|
|
|
|
|
|
; add a new widget node and read its attributes
|
|
proc add_new_node stdcall, nid:dword
|
|
push ebx ecx edx edi esi
|
|
|
|
mov eax, HDA_GNODE.sizeof
|
|
call Kmalloc
|
|
test eax, eax
|
|
jz .err_out ; Not enough memory
|
|
|
|
mov edx, eax
|
|
;Asper+ [
|
|
mov edi, edx
|
|
xor eax, eax
|
|
mov ecx, HDA_GNODE.sizeof
|
|
rep stosb
|
|
;Asper+ ]
|
|
|
|
mov eax, [nid]
|
|
mov word [edx + HDA_GNODE.nid], ax
|
|
stdcall get_wcaps, eax
|
|
mov [edx + HDA_GNODE.wid_caps], eax
|
|
mov ebx, eax
|
|
stdcall get_wcaps_type, eax
|
|
mov byte [edx + HDA_GNODE.type], al
|
|
|
|
mov eax, HDA_MAX_CONNECTIONS*2 ;HDA_MAX_CONNECTIONS * sizeof(word)
|
|
push ebx ecx edx
|
|
call Kmalloc ;malloc temporary conn_list
|
|
pop edx ecx ebx
|
|
mov edi, eax
|
|
|
|
test ebx, AC_WCAP_CONN_LIST
|
|
jz .no_conn_list
|
|
|
|
stdcall snd_hda_get_connections, [nid], edi, HDA_MAX_CONNECTIONS
|
|
mov ecx, eax
|
|
cmp ecx, 0
|
|
jge @f
|
|
|
|
mov eax, edx
|
|
pusha
|
|
call Kfree ;free node
|
|
popa
|
|
mov eax, ecx
|
|
jmp .out
|
|
.no_conn_list:
|
|
|
|
xor ecx, ecx
|
|
@@:
|
|
cmp ecx, 2 ;nconns <= ARRAY_SIZE(node->slist) ?
|
|
jg @f
|
|
|
|
lea eax, [edx + HDA_GNODE.slist]
|
|
mov [edx + HDA_GNODE.conn_list], eax
|
|
jmp .set_conn_list
|
|
@@:
|
|
mov eax, ecx
|
|
shl ecx, 1
|
|
push ebx ecx edx edi
|
|
call Kmalloc ;malloc conn_list
|
|
pop edi edx ecx ebx
|
|
shr ecx, 1
|
|
test eax, eax
|
|
jnz @f
|
|
|
|
mov eax, edi
|
|
pusha
|
|
call Kfree ;free temporary conn_list
|
|
popa
|
|
jmp .err_out
|
|
@@:
|
|
mov [edx + HDA_GNODE.conn_list], eax
|
|
.set_conn_list:
|
|
mov [edx + HDA_GNODE.nconns], cx
|
|
push edi
|
|
mov esi, edi
|
|
mov edi, eax
|
|
rep movsw
|
|
pop edi
|
|
|
|
|
|
mov al, byte [edx + HDA_GNODE.type]
|
|
test al, AC_WID_PIN
|
|
jz @f
|
|
;Asper+ [
|
|
cmp al, AC_WID_VENDOR
|
|
je @f
|
|
;Asper+ ]
|
|
|
|
|
|
stdcall read_pin_cap, [nid]
|
|
mov [edx + HDA_GNODE.pin_caps], eax
|
|
stdcall snd_hda_codec_read, [nid], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0
|
|
mov byte [edx + HDA_GNODE.pin_ctl], al
|
|
stdcall snd_hda_codec_get_pincfg, [nid]
|
|
mov [edx + HDA_GNODE.def_cfg], eax
|
|
@@:
|
|
|
|
xor eax, eax
|
|
test ebx, AC_WCAP_OUT_AMP
|
|
jz .no_out_amp
|
|
test ebx, AC_WCAP_AMP_OVRD
|
|
jz @f
|
|
snd_hda_param_read [nid], AC_PAR_AMP_OUT_CAP
|
|
@@:
|
|
test eax, eax
|
|
jnz @f
|
|
mov eax, [spec.def_amp_out_caps]
|
|
@@:
|
|
mov [edx + HDA_GNODE.amp_out_caps], eax
|
|
.no_out_amp:
|
|
|
|
;;Asper+: Beeper [
|
|
; pusha
|
|
; mov bl, byte [edx + HDA_GNODE.type]
|
|
; cmp bl, AC_WID_BEEP
|
|
; jne .not_beeper
|
|
;
|
|
; mov ebx, [nid]
|
|
; mov [codec.beeper_nid], bx
|
|
;
|
|
; test eax, eax
|
|
; jz .no_beeper_amp
|
|
; ;set beep amplifier here
|
|
; stdcall unmute_output, edx
|
|
; .no_beeper_amp:
|
|
; ;try to beep here
|
|
; stdcall snd_hda_codec_read, [nid], 0, AC_VERB_GET_BEEP_CONTROL, 0 ;eax
|
|
; if DEBUG
|
|
; push eax esi
|
|
; mov esi, msgBeeperNid
|
|
; call SysMsgBoardStr
|
|
; push eax
|
|
; mov eax, [nid]
|
|
; stdcall fdword2str, 2
|
|
; call SysMsgBoardStr
|
|
;
|
|
; mov esi, msgBeeperValue
|
|
; call SysMsgBoardStr
|
|
; pop eax
|
|
; stdcall fdword2str, 2
|
|
; call SysMsgBoardStr
|
|
;
|
|
; mov esi, msgBeepNow
|
|
; call SysMsgBoardStr
|
|
; pop esi eax
|
|
; end if
|
|
; mov ecx, 256*1
|
|
; .next_tone:
|
|
; dec ecx
|
|
; movzx ebx, [esi + HDA_GNODE.nid]
|
|
; stdcall snd_hda_codec_write, [nid], 0, AC_VERB_SET_BEEP_CONTROL, ecx
|
|
; ;mov eax, 0x8000
|
|
; ;stdcall StallExec
|
|
; test ecx, ecx
|
|
; jnz .next_tone
|
|
; .end_beep:
|
|
; stdcall snd_hda_codec_read, [nid], 0, AC_VERB_GET_BEEP_CONTROL, 0 ;eax
|
|
; if DEBUG
|
|
; ;push eax esi
|
|
; mov esi, msgBeeperValue
|
|
; call SysMsgBoardStr
|
|
; stdcall fdword2str, 2
|
|
; call SysMsgBoardStr
|
|
; ;pop esi eax
|
|
; end if
|
|
; .not_beeper:
|
|
; popa
|
|
;;Asper+: Beeper ]
|
|
|
|
xor eax, eax
|
|
test ebx, AC_WCAP_IN_AMP
|
|
jz .no_in_amp
|
|
test ebx, AC_WCAP_AMP_OVRD
|
|
jz @f
|
|
snd_hda_param_read [nid], AC_PAR_AMP_IN_CAP
|
|
@@:
|
|
test eax, eax
|
|
jnz @f
|
|
mov eax, [spec.def_amp_in_caps]
|
|
@@:
|
|
mov [edx + HDA_GNODE.amp_in_caps], eax
|
|
.no_in_amp:
|
|
|
|
mov esi, [spec.nid_list]
|
|
test esi, esi
|
|
jnz @f
|
|
mov [spec.nid_list], edx
|
|
jmp .out
|
|
@@:
|
|
|
|
;Asper+: Sort pins by DA:Sequence during tree building [
|
|
mov ecx, esi
|
|
movzx ebx, byte [edx + HDA_GNODE.def_cfg]
|
|
push edi
|
|
.next_node:
|
|
cmp [esi + HDA_GNODE.type], AC_WID_PIN
|
|
jne @f
|
|
cmp [edx + HDA_GNODE.type], AC_WID_PIN
|
|
je .pin
|
|
|
|
mov edi, [spec.nid_list]
|
|
cmp [edi + HDA_GNODE.type], AC_WID_PIN
|
|
jne .not_pin
|
|
mov [edx + HDA_GNODE.next], edi
|
|
.head: ;CleverMouse+
|
|
mov [spec.nid_list], edx
|
|
pop edi
|
|
jmp .out
|
|
.pin:
|
|
movzx edi, byte [esi + HDA_GNODE.def_cfg]
|
|
cmp edi, ebx
|
|
jle @f
|
|
.not_pin:
|
|
mov [edx + HDA_GNODE.next], esi
|
|
cmp esi, [spec.nid_list] ;CleverMouse+
|
|
jz .head ;CleverMouse+
|
|
mov esi, ecx
|
|
jmp .insert
|
|
@@:
|
|
mov eax, [esi + HDA_GNODE.next]
|
|
test eax, eax
|
|
jz .insert
|
|
mov ecx, esi
|
|
mov esi, eax
|
|
jmp .next_node
|
|
.insert:
|
|
mov [esi + HDA_GNODE.next], edx
|
|
pop edi
|
|
;Asper+ ]
|
|
|
|
.out:
|
|
mov eax, edi
|
|
pusha
|
|
call Kfree ;free temporary conn_list
|
|
popa
|
|
xor eax, eax
|
|
pop esi edi edx ecx ebx
|
|
ret
|
|
|
|
.err_out:
|
|
mov eax, edx
|
|
pusha
|
|
call Kfree ;free node
|
|
popa
|
|
xor eax, eax
|
|
dec eax
|
|
pop esi edi edx ecx ebx
|
|
ret
|
|
endp
|
|
|
|
|
|
|
|
; build the AFG subtree
|
|
proc build_afg_tree
|
|
push ebx ecx edx
|
|
|
|
mov ebx, [codec.afg]
|
|
snd_hda_param_read ebx, AC_PAR_AMP_OUT_CAP
|
|
|
|
mov [spec.def_amp_out_caps], eax
|
|
snd_hda_param_read ebx, AC_PAR_AMP_IN_CAP
|
|
mov [spec.def_amp_in_caps], eax
|
|
|
|
stdcall snd_hda_get_sub_nodes, ebx
|
|
mov ecx, eax
|
|
and ecx, 0xFFFF ;ecx = nodes number
|
|
mov edx, eax
|
|
shr edx, 16 ;eax = address of the first nid
|
|
|
|
test edx, edx
|
|
jz @f
|
|
cmp ecx, 0
|
|
jge .nid_ok
|
|
@@:
|
|
if FDEBUG
|
|
push esi
|
|
mov esi, emsgInvalidAFGSubtree
|
|
call SysMsgBoardStr
|
|
pop esi
|
|
end if
|
|
xor eax, eax
|
|
dec eax
|
|
jmp .out
|
|
.nid_ok:
|
|
|
|
; parse all nodes belonging to the AFG
|
|
.next_node:
|
|
test ecx, ecx
|
|
jz .build_done
|
|
|
|
stdcall add_new_node, edx
|
|
test eax, eax
|
|
jnz .out
|
|
inc edx
|
|
dec ecx
|
|
jmp .next_node
|
|
.build_done:
|
|
xor eax, eax
|
|
.out:
|
|
pop edx ecx ebx
|
|
ret
|
|
endp
|
|
|
|
|
|
; look for the node record for the given NID
|
|
proc hda_get_node stdcall, nid:dword
|
|
push ebx edx esi
|
|
movzx ebx, word [nid]
|
|
mov esi, [spec.nid_list]
|
|
test esi, esi
|
|
jz .out
|
|
|
|
.next_node:
|
|
mov edx, [esi + HDA_GNODE.next]
|
|
test edx, edx ;Asper+
|
|
jz .not_found ;Asper+
|
|
mov ax, word [esi + HDA_GNODE.nid]
|
|
cmp ax, bx
|
|
je .out
|
|
mov esi, edx
|
|
jmp .next_node
|
|
|
|
.not_found: ;Asper+
|
|
xor esi, esi
|
|
.out:
|
|
mov eax, esi
|
|
pop esi edx ebx
|
|
ret
|
|
endp
|
|
|
|
;Asper+[
|
|
proc set_eapd stdcall, node:dword ;nid:dword, on:dword
|
|
push eax ebx esi
|
|
mov esi, [node]
|
|
cmp [esi + HDA_GNODE.type], AC_WID_PIN
|
|
jne .out
|
|
; eapd capable?
|
|
test [esi + HDA_GNODE.pin_caps], AC_PINCAP_EAPD
|
|
jz .out
|
|
;stdcall snd_hda_codec_read, ebx, 0, AC_VERB_GET_EAPD_BTLENABLE, AC_EAPDBTL_EAPD
|
|
;or eax, AC_EAPDBTL_EAPD
|
|
movzx ebx, [esi + HDA_GNODE.nid]
|
|
stdcall snd_hda_codec_write, ebx, 0, AC_VERB_SET_EAPD_BTLENABLE, AC_EAPDBTL_EAPD ;eax
|
|
if DEBUG
|
|
push eax esi
|
|
mov esi, msgEnableEAPD
|
|
call SysMsgBoardStr
|
|
mov eax, ebx
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
pop esi eax
|
|
end if
|
|
.out:
|
|
pop esi ebx eax
|
|
ret
|
|
endp
|
|
;Asper+]
|
|
|
|
; unmute (and set max vol) the output amplifier
|
|
proc unmute_output stdcall, node:dword
|
|
|
|
push ebx ecx edx esi
|
|
mov esi, [node]
|
|
test [esi + HDA_GNODE.wid_caps], AC_WCAP_OUT_AMP
|
|
jz .out
|
|
movzx eax, word [esi + HDA_GNODE.nid]
|
|
if DEBUG
|
|
push esi
|
|
mov esi, msgUnmuteOut
|
|
call SysMsgBoardStr
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
pop esi
|
|
end if
|
|
|
|
stdcall set_eapd, esi ;Asper+: set EAPD if exist
|
|
|
|
mov ebx, eax
|
|
mov eax, [esi + HDA_GNODE.amp_out_caps]
|
|
mov ecx, eax
|
|
|
|
and eax, AC_AMPCAP_NUM_STEPS
|
|
shr eax, AC_AMPCAP_NUM_STEPS_SHIFT
|
|
|
|
stdcall snd_hda_codec_amp_stereo, ebx, HDA_OUTPUT, 0, 0xFF, eax
|
|
|
|
and ecx, AC_AMPCAP_STEP_SIZE
|
|
shr ecx, AC_AMPCAP_STEP_SIZE_SHIFT
|
|
|
|
test al, al
|
|
jz .out
|
|
if DEBUG
|
|
push eax esi
|
|
mov esi, msgAmpVal
|
|
call SysMsgBoardStr
|
|
stdcall fdword2str, 1
|
|
call SysMsgBoardStr
|
|
|
|
mov esi, strSemicolon
|
|
call SysMsgBoardStr
|
|
mov eax, ecx
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
pop esi eax
|
|
end if
|
|
mov [volume.out_amp_node], esi
|
|
inc al
|
|
mov [volume.num_steps], al
|
|
inc cl
|
|
mov [volume.step_size], cl
|
|
mul cl
|
|
shr eax, 2
|
|
imul eax, 100
|
|
mov [volume.maxDb], eax
|
|
|
|
.out:
|
|
xor eax, eax
|
|
pop esi edx ecx ebx
|
|
ret
|
|
endp
|
|
|
|
; unmute (and set max vol) the input amplifier
|
|
proc unmute_input stdcall, node:dword, index:dword
|
|
push ecx edx esi
|
|
test [esi + HDA_GNODE.wid_caps], AC_WCAP_IN_AMP
|
|
jz .out
|
|
and [index], 0xF ;Asper+ : Ranger
|
|
mov esi, [node]
|
|
movzx eax, word [esi + HDA_GNODE.nid]
|
|
if DEBUG
|
|
push eax esi
|
|
mov esi, msgUnmuteIn
|
|
call SysMsgBoardStr
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
mov esi, msgIdx
|
|
call SysMsgBoardStr
|
|
mov eax, [index]
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
pop esi eax
|
|
end if
|
|
|
|
mov edx, [esi + HDA_GNODE.amp_in_caps]
|
|
mov ecx, edx
|
|
|
|
and edx, AC_AMPCAP_NUM_STEPS
|
|
shr edx, AC_AMPCAP_NUM_STEPS_SHIFT
|
|
|
|
stdcall snd_hda_codec_amp_stereo, eax, HDA_INPUT, [index], 0xFF, edx
|
|
.out:
|
|
xor eax, eax
|
|
pop esi edx ecx
|
|
ret
|
|
endp
|
|
|
|
|
|
; select the input connection of the given node.
|
|
proc select_input_connection stdcall, node:dword, index:dword
|
|
push ebx esi
|
|
mov esi, [node]
|
|
movzx eax, word [esi + HDA_GNODE.nid]
|
|
mov ebx, [index]
|
|
if DEBUG
|
|
mov esi, msgConnect
|
|
call SysMsgBoardStr
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
|
|
mov esi, msgIdx
|
|
call SysMsgBoardStr
|
|
push eax
|
|
mov eax, ebx
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
pop eax
|
|
end if
|
|
stdcall snd_hda_codec_write, eax, 0, AC_VERB_SET_CONNECT_SEL, ebx
|
|
pop esi ebx
|
|
ret
|
|
endp
|
|
|
|
|
|
; clear checked flag of each node in the node list
|
|
proc clear_check_flags
|
|
push eax esi
|
|
mov esi, [spec.nid_list]
|
|
test esi, esi
|
|
jz .out
|
|
.next_node:
|
|
mov byte [esi + HDA_GNODE.checked], 0
|
|
mov eax, [esi + HDA_GNODE.next]
|
|
test eax, eax
|
|
jz .out
|
|
mov esi, eax
|
|
jmp .next_node
|
|
|
|
.out:
|
|
pop esi eax
|
|
ret
|
|
endp
|
|
|
|
;
|
|
; parse the output path recursively until reach to an audio output widget
|
|
;
|
|
; returns 0 if not found, 1 if found, or a negative error code.
|
|
;
|
|
proc parse_output_path stdcall, node:dword, dac_idx:dword
|
|
push ebx ecx edx esi
|
|
mov esi, [node]
|
|
mov al, byte [esi + HDA_GNODE.checked]
|
|
test al, al
|
|
jnz .ret_zero
|
|
|
|
mov byte [esi + HDA_GNODE.checked], 1
|
|
|
|
mov al, byte [esi + HDA_GNODE.type]
|
|
cmp al, AC_WID_AUD_OUT
|
|
jne .not_wid_aud_out
|
|
|
|
movzx eax, word [esi + HDA_GNODE.nid]
|
|
mov ebx, [esi + HDA_GNODE.wid_caps]
|
|
test ebx, AC_WCAP_DIGITAL
|
|
jz @f
|
|
if DEBUG
|
|
push esi
|
|
mov esi, msgSkipDigitalOutNode
|
|
call SysMsgBoardStr
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
pop esi
|
|
end if
|
|
jmp .ret_zero
|
|
@@:
|
|
if DEBUG
|
|
push eax esi
|
|
mov esi, msgAudOutFound
|
|
call SysMsgBoardStr
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
pop esi eax
|
|
end if
|
|
|
|
push eax
|
|
stdcall unmute_output, esi ;Asper+
|
|
pop eax
|
|
mov ecx, [dac_idx]
|
|
shl ecx, 2
|
|
push eax
|
|
mov eax, [spec.dac_node+ecx]
|
|
test eax, eax
|
|
pop eax
|
|
jz @f
|
|
; already DAC node is assigned, just unmute & connect
|
|
cmp eax, [node]
|
|
je .ret_one
|
|
jmp .ret_zero
|
|
@@:
|
|
mov ecx, [dac_idx]
|
|
shl ecx, 2
|
|
mov [spec.dac_node+ecx], eax
|
|
jmp .ret_one ;found
|
|
.not_wid_aud_out:
|
|
movzx ebx, [esi + HDA_GNODE.nconns]
|
|
xor ecx, ecx
|
|
mov edx, [esi + HDA_GNODE.conn_list]
|
|
test ebx, ebx
|
|
jz .ret_zero
|
|
.next_node:
|
|
stdcall hda_get_node, [edx]
|
|
test eax, eax
|
|
jz .continue
|
|
|
|
stdcall parse_output_path, eax, [dac_idx]
|
|
|
|
cmp [esi + HDA_GNODE.nconns], 1
|
|
jle @f
|
|
stdcall select_input_connection, esi, ecx
|
|
@@:
|
|
;UNSUPPORTED YET! stdcall unmute_input, esi, ecx
|
|
stdcall unmute_output, esi
|
|
jmp .ret_one
|
|
|
|
.continue:
|
|
add edx, 2
|
|
inc ecx
|
|
cmp ecx, ebx
|
|
jl .next_node
|
|
.ret_zero:
|
|
xor eax, eax
|
|
pop esi edx ecx ebx
|
|
ret
|
|
.ret_one:
|
|
xor eax, eax
|
|
inc eax
|
|
.ret: ;Asper+
|
|
pop esi edx ecx ebx
|
|
ret
|
|
endp
|
|
|
|
; Look for the output PIN widget with the given jack type
|
|
; and parse the output path to that PIN.
|
|
;
|
|
; Returns the PIN node when the path to DAC is established.
|
|
proc parse_output_jack stdcall, jack_type:dword
|
|
push edx esi
|
|
|
|
mov esi, [spec.nid_list]
|
|
test esi, esi
|
|
jz .ret_zero
|
|
.next_pin:
|
|
cmp [esi + HDA_GNODE.type], AC_WID_PIN
|
|
jne .continue
|
|
|
|
; output capable?
|
|
mov eax, [esi + HDA_GNODE.pin_caps]
|
|
test eax, AC_PINCAP_OUT
|
|
jz .continue
|
|
|
|
stdcall defcfg_port_conn, esi
|
|
cmp eax, AC_JACK_PORT_NONE
|
|
je .continue ;unconnected
|
|
|
|
mov edx, [jack_type]
|
|
cmp edx, 0
|
|
jl @f
|
|
|
|
stdcall defcfg_type, esi
|
|
cmp edx, eax
|
|
jne .continue
|
|
|
|
test [esi + HDA_GNODE.wid_caps], AC_WCAP_DIGITAL
|
|
jnz .continue ; skip SPDIF
|
|
@@:
|
|
; output as default?
|
|
if DEBUG
|
|
pusha
|
|
; push esi
|
|
; mov esi, msgPin_Nid
|
|
; call SysMsgBoardStr
|
|
; pop esi
|
|
movzx eax, [esi + HDA_GNODE.nid]
|
|
movzx ebx, [esi + HDA_GNODE.pin_ctl]
|
|
mov ecx, [esi + HDA_GNODE.pin_caps]
|
|
mov edx, [esi + HDA_GNODE.def_cfg]
|
|
mov edi, [esi + HDA_GNODE.amp_out_caps]
|
|
mov esi, msgPin_Nid
|
|
call SysMsgBoardStr
|
|
stdcall fdword2str, 3
|
|
call SysMsgBoardStr
|
|
|
|
mov esi, msgPin_Ctl
|
|
call SysMsgBoardStr
|
|
mov eax, ebx
|
|
stdcall fdword2str, 2
|
|
call SysMsgBoardStr
|
|
|
|
mov esi, msgPin_Caps
|
|
call SysMsgBoardStr
|
|
mov eax, ecx
|
|
stdcall fdword2str, 2
|
|
call SysMsgBoardStr
|
|
|
|
mov esi, msgDef_Cfg
|
|
call SysMsgBoardStr
|
|
mov eax, edx
|
|
stdcall fdword2str, 2
|
|
call SysMsgBoardStr
|
|
|
|
mov esi, msgAmp_Out_Caps
|
|
call SysMsgBoardStr
|
|
mov eax, edi
|
|
stdcall fdword2str, 2
|
|
call SysMsgBoardStr
|
|
|
|
popa
|
|
end if
|
|
; test [esi + HDA_GNODE.pin_ctl], AC_PINCTL_OUT_EN
|
|
; jz .continue
|
|
stdcall clear_check_flags
|
|
stdcall parse_output_path, esi, 0
|
|
|
|
test eax, eax
|
|
jnz @f
|
|
mov edx, [spec.out_pin_node]
|
|
test edx, edx
|
|
jz @f
|
|
stdcall clear_check_flags
|
|
stdcall parse_output_path, esi, 1
|
|
@@:
|
|
cmp eax, 0
|
|
jle .l1
|
|
|
|
; unmute the PIN output
|
|
stdcall unmute_output, esi
|
|
; set PIN-Out enable
|
|
xor edx, edx
|
|
test [esi + HDA_GNODE.pin_caps], AC_PINCAP_HP_DRV
|
|
jz @f
|
|
mov edx, AC_PINCTL_HP_EN
|
|
@@:
|
|
or edx, AC_PINCTL_OUT_EN
|
|
movzx eax, [esi + HDA_GNODE.nid]
|
|
stdcall snd_hda_codec_write, eax, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, edx
|
|
mov eax, esi
|
|
jmp .out
|
|
.l1:
|
|
.continue:
|
|
mov edx, [esi + HDA_GNODE.next]
|
|
test edx, edx
|
|
jz .ret_zero
|
|
mov esi, edx
|
|
jmp .next_pin
|
|
.ret_zero:
|
|
xor eax, eax
|
|
.out:
|
|
pop esi edx
|
|
ret
|
|
endp
|
|
|
|
|
|
; parse outputs
|
|
proc parse_output
|
|
push edx
|
|
; Look for the output PIN widget
|
|
;
|
|
; first, look for the line-out pin
|
|
stdcall parse_output_jack, AC_JACK_LINE_OUT
|
|
test eax, eax
|
|
jz @f
|
|
mov [spec.out_pin_node], eax ; found, remember the PIN node
|
|
jmp .l1
|
|
@@:
|
|
; if no line-out is found, try speaker out
|
|
stdcall parse_output_jack, AC_JACK_SPEAKER
|
|
test eax, eax
|
|
jz .l1
|
|
mov [spec.out_pin_node], eax ; found, remember the PIN node
|
|
.l1:
|
|
; look for the HP-out pin
|
|
stdcall parse_output_jack, AC_JACK_HP_OUT
|
|
test eax, eax
|
|
jz .l2
|
|
|
|
mov edx, [spec.out_pin_node]
|
|
test edx, edx
|
|
jnz @f
|
|
mov [spec.out_pin_node], eax
|
|
jmp .l2
|
|
@@:
|
|
mov [spec.out_pin_node+4], eax
|
|
.l2:
|
|
mov edx, [spec.out_pin_node]
|
|
test edx, edx
|
|
jnz @f
|
|
; no line-out or HP pins found,
|
|
; then choose for the first output pin
|
|
stdcall parse_output_jack, -1
|
|
|
|
mov [spec.out_pin_node], eax
|
|
test eax, eax
|
|
jnz @f
|
|
if DEBUG
|
|
push esi
|
|
mov esi, emsgNoProperOutputPathFound
|
|
call SysMsgBoardStr
|
|
pop esi
|
|
end if
|
|
@@:
|
|
pop edx
|
|
xor eax, eax
|
|
ret
|
|
endp
|
|
|
|
|
|
;(...) Skip functions for the input (capture is not supported).
|
|
|
|
; the generic parser
|
|
proc snd_hda_parse_generic_codec
|
|
mov eax, [codec.afg]
|
|
test eax, eax
|
|
jz .out
|
|
|
|
stdcall build_afg_tree
|
|
cmp eax, 0
|
|
jl .error
|
|
|
|
stdcall parse_output
|
|
xor eax, eax
|
|
.out:
|
|
ret
|
|
.error:
|
|
stdcall snd_hda_generic_free
|
|
ret
|
|
endp
|
|
|
|
|
|
; some data
|
|
spec HDA_GSPEC
|
|
volume VOLUME_CTL
|