0,0 → 1,1336 |
|
;; Compose a 32bit command word to be sent to the HD-audio controller |
proc make_codec_cmd stdcall, nid:dword, direct:dword, verb:dword, parm:dword |
push ebx |
|
and dword [codec.addr], 0xF |
and dword [direct], 1 |
and dword [nid], 0x7F |
and dword [verb], 0xFFF |
and dword [parm], 0xFFFF |
|
mov eax, [codec.addr] |
shl eax, 28 |
mov ebx, [direct] |
shl ebx, 27 |
or eax, ebx |
mov ebx, [nid] |
shl ebx, 20 |
or eax, ebx |
mov ebx, [verb] |
shl ebx, 8 |
or eax, ebx |
mov ebx, [parm] |
or eax, ebx |
pop ebx |
ret |
.err: |
pop ebx |
mov eax, -1 |
ret |
endp |
|
;; Send and receive a verb |
proc codec_exec_verb stdcall, cmd:dword;, res:dword <- returned in eax |
push ebx edx |
mov ebx, [cmd] |
cmp ebx, -1 |
jne @f |
pop edx ebx |
mov eax, -1 |
ret |
@@: |
if FDEBUG ;YAHOO |
push eax esi |
mov esi, msgVerbQuery |
call SysMsgBoardStr |
mov eax, ebx |
stdcall fdword2str, 2 |
call SysMsgBoardStr |
pop esi eax |
end if |
|
mov edx, -1 |
.again: |
; call snd_hda_power_up |
stdcall azx_send_cmd, ebx |
mov ebx, eax |
test ebx, ebx |
jnz @f |
call azx_get_response |
mov edx, eax |
if FDEBUG |
test edx, edx |
jz .end_debug |
push eax esi |
mov esi, msgVerbAnswer |
call SysMsgBoardStr |
mov eax, edx |
stdcall fdword2str, 2 |
call SysMsgBoardStr |
pop esi eax |
.end_debug: |
end if |
|
@@: |
|
; call snd_hda_power_down |
cmp edx, -1 |
jne .l1 |
|
mov eax, [ctrl.rirb_error] |
test eax, eax |
jz .l1 |
|
mov eax, [ctrl.response_reset] |
jz @f |
|
if DEBUG |
push esi |
mov esi, emsgBusResetFatalComm |
call SysMsgBoardStr |
pop esi |
end if |
call azx_bus_reset |
@@: |
.l1: |
;; clear reset-flag when the communication gets recovered |
test ebx, ebx |
jnz @f |
mov [ctrl.response_reset], 0 |
@@: |
mov eax, edx |
|
pop edx ebx |
ret |
endp |
|
|
;; |
;; snd_hda_codec_read - send a command and get the response |
;; @nid: NID to send the command |
;; @direct: direct flag |
;; @verb: the verb to send |
;; @parm: the parameter for the verb |
;; |
;; Send a single command and read the corresponding response. |
;; |
;; Returns the obtained response value, or -1 for an error. |
;; |
proc snd_hda_codec_read stdcall, nid:dword, direct:dword, verb:dword, parm:dword |
stdcall make_codec_cmd, [nid], [direct], [verb], [parm] |
stdcall codec_exec_verb, eax |
ret |
endp |
|
|
;; |
;; snd_hda_codec_write - send a single command without waiting for response |
;; @nid: NID to send the command |
;; @direct: direct flag |
;; @verb: the verb to send |
;; @parm: the parameter for the verb |
;; |
;; Send a single command without waiting for response. |
;; |
;; Returns 0 if successful, or a negative error code. |
;; |
proc snd_hda_codec_write stdcall, nid:dword, direct:dword, verb:dword, parm:dword |
; Do we need to support a sync write? |
stdcall make_codec_cmd, [nid], [direct], [verb], [parm] |
stdcall codec_exec_verb, eax |
ret |
endp |
|
|
;; |
;; snd_hda_sequence_write - sequence writes |
;; @seq: VERB array to send |
;; |
;; Send the commands sequentially from the given array. |
;; The array must be terminated with NID=0. |
;; |
proc snd_hda_sequence_write stdcall, seq:dword |
push eax ebx ecx esi |
mov esi, [seq] |
@@: |
mov ecx, [esi + hda_verb.nid] |
mov ebx, [esi + hda_verb.verb] |
mov eax, [esi + hda_verb.param] |
stdcall snd_hda_codec_write, ecx, 0, ebx, eax |
add esi, hda_verb.sizeof |
test ecx, ecx |
jnz @b |
pop esi ecx ebx eax |
ret |
endp |
|
|
macro snd_hda_param_read nid, param |
{ |
stdcall snd_hda_codec_read, nid, 0, AC_VERB_PARAMETERS, param |
} |
|
;; |
;; snd_hda_get_sub_nodes - get the range of sub nodes |
;; @codec: the HDA codec |
;; @nid: NID to parse |
;; @start_id: the pointer to store the start NID |
;; |
;; Parse the NID and store the start NID of its sub-nodes. |
;; Returns the number of sub-nodes. |
;; |
proc snd_hda_get_sub_nodes stdcall, nid:dword;, start_id:dword <- returned in upper word of eax |
snd_hda_param_read [nid], AC_PAR_NODE_COUNT |
|
cmp eax, -1 |
jne @f |
inc eax |
@@: |
and eax, 0x7FFF7FFF |
|
ret |
endp |
|
;; |
;; snd_hda_get_connections - get connection list |
;; @codec: the HDA codec |
;; @nid: NID to parse |
;; @conn_list: connection list array |
;; @max_conns: max. number of connections to store |
;; |
;; Parses the connection list of the given widget and stores the list |
;; of NIDs. |
;; |
;; Returns the number of connections, or a negative error code. |
;; |
proc snd_hda_get_connections stdcall, nid:dword, conn_list:dword, max_conns:dword ;Asper: Complete translation! |
locals |
parm dd ? |
conn_len dd ? |
conns dd 0 |
shift db 8 |
num_elements dd 4 |
mask dd 0x7F |
wcaps dd ? |
prev_nid dw 1 ;Asper: Hmm.. Probably ALSA bug that it isn't initialized. I suppose to init it with 1. |
endl |
|
push ebx ecx edx edi esi |
mov edi, [conn_list] |
test edi, edi |
jz .err_out |
mov ecx, [max_conns] |
cmp ecx, 0 |
jle .err_out |
|
|
stdcall get_wcaps, [nid] |
mov ebx, eax |
mov [wcaps], eax |
stdcall get_wcaps_type, ebx |
cmp eax, AC_WID_VOL_KNB |
je .conn_list_ok |
test ebx, AC_WCAP_CONN_LIST |
jnz .conn_list_ok |
if DEBUG |
mov esi, emsgConnListNotAvailable |
call SysMsgBoardStr |
mov eax, [nid] |
stdcall fdword2str, 3 |
call SysMsgBoardStr |
end if |
xor eax, eax |
dec eax |
jmp .out |
.conn_list_ok: |
|
snd_hda_param_read [nid], AC_PAR_CONNLIST_LEN |
mov [parm], eax |
|
test eax, AC_CLIST_LONG |
jz @f |
; long form |
mov [shift], 16 |
mov [num_elements], 2 |
mov [mask], 0x7FFF ;Asper+ |
@@: |
and eax, AC_CLIST_LENGTH |
test eax, eax |
jz .out ; no connection |
|
mov [conn_len], eax |
cmp eax, 1 |
jne .multi_conns |
; single connection |
stdcall snd_hda_codec_read, [nid], 0, AC_VERB_GET_CONNECT_LIST, 0 |
mov [parm], eax |
cmp [parm], -1 |
jne @f |
cmp [ctrl.rirb_error], 0 |
jne @f |
xor eax, eax |
dec eax |
jmp .out |
@@: |
|
mov eax, [parm] |
and eax, [mask] |
stosd |
xor eax, eax |
inc eax |
jmp .out |
.multi_conns: |
|
; multi connection |
xor ecx, ecx |
mov edx, [num_elements] |
.next_conn: |
mov eax, ecx |
.mod: |
cmp eax, edx |
jl .mod_counted |
sub eax, edx |
jmp .mod |
.mod_counted: |
|
test eax, eax |
jnz .l1 |
stdcall snd_hda_codec_read, [nid], 0, AC_VERB_GET_CONNECT_LIST, ecx |
mov [parm], eax |
|
cmp eax, -1 |
jne .l1 |
cmp [ctrl.rirb_error], 0 |
jne .err_out |
.l1: |
|
mov eax, 1 |
push ecx |
mov cl, [shift] |
dec cl |
shl eax, cl |
and eax, [parm] |
pop ecx |
mov ebx, eax ;ranges |
|
mov eax, [parm] |
and eax, [mask] ;val |
|
test eax, eax |
jnz @f |
if DEBUG |
push eax esi |
mov esi, emsgInvConnList |
call SysMsgBoardStr |
mov eax, [nid] |
stdcall fdword2str, 1 |
call SysMsgBoardStr |
|
mov esi, strSemicolon |
call SysMsgBoardStr |
mov eax, ecx |
stdcall fdword2str, 0 |
call SysMsgBoardStr |
|
mov esi, strSemicolon |
call SysMsgBoardStr |
mov eax, [parm] |
stdcall fdword2str, 2 |
call SysMsgBoardStr |
pop esi eax |
end if |
xor eax, eax |
jmp .out |
@@: |
push ecx |
mov cl, [shift] |
shr [parm], cl |
pop ecx |
|
test ebx, ebx |
jz .range_zero |
; ranges between the previous and this one |
movzx esi, word [prev_nid] |
test esi, esi |
jz .l2 |
cmp esi, eax |
jl @f |
.l2: |
if DEBUG |
push eax esi |
push esi |
mov esi, emsgInvDepRangeVal |
call SysMsgBoardStr |
pop esi |
push eax |
mov eax, esi |
stdcall fdword2str, 0 |
call SysMsgBoardStr |
|
mov esi, strSemicolon |
call SysMsgBoardStr |
pop eax |
stdcall fdword2str, 2 |
call SysMsgBoardStr |
pop esi eax |
end if |
jmp .continue |
@@: |
push ecx |
mov ecx, esi |
inc ecx |
mov ebx, [conns] |
.next_conn2: |
cmp ebx, [max_conns] |
jl @f |
if DEBUG |
push esi |
mov esi, emsgTooManyConns |
call SysMsgBoardStr |
pop esi |
end if |
pop ecx |
jmp .err_out |
@@: |
shl ebx, 1 |
push edi |
add edi, ebx |
mov word [edi], cx |
pop edi |
shr ebx, 1 |
inc ebx |
inc ecx |
cmp ecx, eax |
jle .next_conn2 |
|
mov [conns], ebx |
pop ecx |
jmp .end_range_test |
.range_zero: |
|
mov ebx, [conns] |
cmp ebx, [max_conns] |
jl @f |
if DEBUG |
push esi |
mov esi, emsgTooManyConns |
call SysMsgBoardStr |
pop esi |
end if |
jmp .err_out |
@@: |
shl ebx, 1 |
push edi |
add edi, ebx |
mov word [edi], ax |
pop edi |
shr ebx, 1 |
inc ebx |
mov [conns], ebx |
.end_range_test: |
mov [prev_nid], ax |
.continue: |
inc ecx |
cmp ecx, [conn_len] |
jl .next_conn |
|
mov eax, [conns] |
.out: |
pop esi edi edx ecx ebx |
ret |
.err_out: |
pop esi edi edx ecx ebx |
mov eax, -1 |
ret |
endp |
|
|
; Asper: Have to be realized later, when we will work with such events, but not NOW! |
;proc snd_hda_queue_unsol_events stdcall, res:dword, res_ex:dword |
; push ebx edi esi |
; ... |
; pop esi edi ebx |
; ret |
;endp |
|
; This functions also will be later realized. |
;proc process_unsol_events stdcall, work:dword |
;proc init_usol_queue stdcall, bus:dword |
|
;; |
;; snd_hda_bus_new - create a HDA bus |
;; @card: the card entry |
;; @temp: the template for hda_bus information |
;; @busp: the pointer to store the created bus instance |
;; |
;; Returns 0 if successful, or a negative error code. |
;; |
;proc snd_hda_bus_new |
; if we want to support unsolicited events, we have to solve this |
; bus->workq = create_singlethread_workqueue(bus->workq_name); |
; (...) |
; xor eax, eax |
; ret |
;endp |
|
;; |
;; snd_hda_codec_init - initialize a HDA codec |
;; |
;; Returns 0 if successful, or a negative error code. |
;; |
proc snd_hda_codec_init ; We use just one codec (the first found) |
snd_hda_param_read AC_NODE_ROOT, AC_PAR_VENDOR_ID |
cmp eax, -1 |
jne @f |
snd_hda_param_read AC_NODE_ROOT, AC_PAR_VENDOR_ID |
@@: |
mov [codec.chip_id], ax |
shr eax, 16 |
mov [codec.vendor_id], ax |
|
snd_hda_param_read AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID |
mov [codec.subsystem_id], eax |
|
snd_hda_param_read AC_NODE_ROOT, AC_PAR_REV_ID |
mov [codec.revision_id], eax |
|
stdcall setup_fg_nodes |
|
mov eax, [codec.afg] |
test eax, eax |
jnz @f |
|
mov eax, [codec.mfg] |
test eax, eax |
jnz @f |
if DEBUG |
push esi |
mov esi, emsgNoAFGorMFGFound |
call SysMsgBoardStr |
pop esi |
end if |
mov eax, -1 |
ret |
@@: |
|
mov ebx, eax |
push ebx |
stdcall read_widget_caps, eax |
|
cmp eax, 0 |
jge @f |
if DEBUG |
push esi |
mov esi, emsgNoMem |
call SysMsgBoardStr |
pop esi |
end if |
pop ebx |
mov eax, -1 |
ret |
@@: |
|
call read_pin_defaults |
|
cmp eax, 0 |
jge @f |
pop ebx |
mov eax, -1 |
ret |
@@: |
mov eax, [codec.subsystem_id] |
test eax, eax |
jnz @f |
stdcall snd_hda_codec_read, ebx, 0, AC_VERB_GET_SUBSYSTEM_ID, 0 |
|
@@: |
|
; power up all before initialization |
; stdcall snd_hda_set_power_state, ebx, AC_PWRST_D0 |
|
pop ebx |
ret |
endp |
|
|
;; |
;; snd_hda_codec_configure - (Re-)configure the HD-audio codec |
;; |
;; Start parsing of the given codec tree and (re-)initialize the whole |
;; patch instance. |
;; |
;; Returns 0 if successful or a negative error code. |
;; |
proc snd_hda_codec_configure |
call get_codec_name |
@@: |
; call the default parser |
stdcall snd_hda_parse_generic_codec ;entry point to generic tree parser!!! |
;Asper+:patch for HP Elitebook 8730w [ |
; push eax ebx |
; mov ebx, [codec.afg] |
; stdcall snd_hda_codec_write, ebx, 0, AC_VERB_SET_GPIO_MASK, 0x02 |
; stdcall snd_hda_codec_write, ebx, 0, AC_VERB_SET_GPIO_DIRECTION, 0x02 |
; stdcall snd_hda_codec_write, ebx, 0, AC_VERB_SET_GPIO_DATA, 0x02 ; first muted |
; pop ebx eax |
;Asper+ ] |
test eax, eax |
jz @f |
if DEBUG |
push esi |
mov esi, emsgNoParserAvailable |
call SysMsgBoardStr |
pop esi |
end if |
@@: |
.out: |
ret |
endp |
|
|
; get_codec_name - store the codec name |
proc get_codec_name |
push eax ebx edi esi |
mov eax, [codec.ac_vendor_ids] |
test eax, eax |
jnz .get_chip_name |
mov ax, [codec.vendor_id] |
mov edi, hda_vendor_ids |
|
@@: |
mov ebx, [edi] |
test ebx, ebx |
jz .unknown |
|
cmp ax, bx |
jne .next |
mov eax, [edi+4] |
mov [codec.ac_vendor_ids], eax |
mov esi, eax |
call SysMsgBoardStr |
.get_chip_name: |
stdcall detect_chip, [edi+8] |
pop esi edi ebx eax |
ret |
.next: |
add edi, 12 |
jmp @b |
.unknown: |
mov [codec.ac_vendor_ids], ac_unknown |
mov [codec.chip_ids], chip_unknown |
|
mov esi, chip_unknown |
call SysMsgBoardStr |
movzx eax, [codec.chip_id] |
stdcall fdword2str, 2 |
call SysMsgBoardStr |
pop esi edi ebx eax |
ret |
endp |
|
|
align 4 |
proc detect_chip stdcall, chip_tab:dword |
|
push eax ebx edi esi |
mov ax, [codec.chip_id] |
|
mov edi, [chip_tab] |
@@: |
mov ebx, [edi] |
cmp ebx, 0xFF |
je .unknown |
|
cmp ax, bx |
jne .next |
mov eax, [edi+4] |
mov [codec.chip_ids], eax |
mov esi, eax |
call SysMsgBoardStr |
pop esi edi ebx eax |
ret |
.next: |
add edi, 8 |
jmp @b |
.unknown: |
mov [codec.chip_ids], chip_unknown |
mov esi, chip_unknown |
call SysMsgBoardStr |
movzx eax, [codec.chip_id] |
stdcall fdword2str, 2 |
call SysMsgBoardStr |
pop esi edi ebx eax |
ret |
endp |
|
|
;; look for an AFG and MFG nodes |
proc setup_fg_nodes |
push eax ebx ecx |
stdcall snd_hda_get_sub_nodes, AC_NODE_ROOT |
mov ecx, eax |
and ecx, 0x7FFF ; total_nodes |
mov ebx, eax |
shr ebx, 16 |
and ebx, 0x7FFF ; nid |
|
if DEBUG ;YAHOO |
push eax esi |
mov esi, msgSETUP_FG_NODES |
call SysMsgBoardStr |
mov eax, ebx |
stdcall fdword2str, 1 |
call SysMsgBoardStr |
|
mov esi, strSemicolon |
call SysMsgBoardStr |
mov eax, ecx |
stdcall fdword2str, 3 |
call SysMsgBoardStr |
pop esi eax |
end if |
|
.next: |
test ecx, ecx |
jz .l1 |
snd_hda_param_read ebx, AC_PAR_FUNCTION_TYPE |
and eax, 0xFF |
|
if DEBUG ;YAHOO |
push eax esi |
mov esi, msgFG_TYPE |
call SysMsgBoardStr |
stdcall fdword2str, 3 |
call SysMsgBoardStr |
pop esi eax |
end if |
|
cmp eax, AC_GRP_AUDIO_FUNCTION |
jne @f |
|
mov [codec.afg], ebx |
mov [codec.function_id], eax |
jmp .continue |
@@: |
cmp eax, AC_GRP_MODEM_FUNCTION |
jne @f |
|
mov [codec.mfg], ebx |
mov [codec.function_id], eax |
jmp .continue |
@@: |
.continue: |
inc ebx |
dec ecx |
jnz .next |
.l1: |
pop ecx ebx eax |
ret |
endp |
|
|
;====================================================================================== |
; read widget caps for each widget and store in cache |
proc read_widget_caps stdcall, fg_node:dword |
push ebx ecx edx edi |
|
stdcall snd_hda_get_sub_nodes, [fg_node] |
mov ecx, eax |
and ecx, 0x7FFF ; total_nodes |
mov [codec.num_nodes], cx |
mov ebx, eax |
shr ebx, 16 |
and ebx, 0x7FFF ; nid |
mov [codec.start_nid], bx |
|
if DEBUG ;YAHOO |
push eax esi |
mov esi, msgSETUP_FG_NODES |
call SysMsgBoardStr |
mov eax, ebx |
stdcall fdword2str, 1 |
call SysMsgBoardStr |
|
mov esi, strSemicolon |
call SysMsgBoardStr |
mov eax, ecx |
stdcall fdword2str, 3 |
call SysMsgBoardStr |
pop esi eax |
end if |
|
if FDEBUG ;YAHOO |
push esi |
mov esi, msgWCaps |
call SysMsgBoardStr |
pop esi |
end if |
|
mov eax, ecx |
shl eax, 2 |
push ebx ecx |
call Kmalloc |
pop ecx ebx |
test eax, eax |
jz .err_out |
mov [codec.wcaps], eax |
|
mov edi, eax |
.next_node: |
|
snd_hda_param_read ebx, AC_PAR_AUDIO_WIDGET_CAP |
stosd |
inc ebx |
dec ecx |
jnz .next_node |
pop edi edx ecx ebx |
xor eax, eax |
ret |
.err_out: |
pop edi edx ecx ebx |
xor eax, eax |
dec eax |
ret |
endp |
|
|
; read all pin default configurations and save codec->init_pins |
proc read_pin_defaults |
push ebx ecx edx edi |
|
movzx ebx, [codec.start_nid] |
movzx ecx, [codec.num_nodes] |
|
;Asper [ |
mov eax, HDA_PINCFG.sizeof |
mul cl |
push ebx ecx |
call Kmalloc |
pop ecx ebx |
test eax, eax |
jz .err_out |
mov [codec.init_pins], eax |
mov edi, eax |
;Asper ] |
|
if FDEBUG |
push eax esi |
mov esi, msgPinCfgs |
call SysMsgBoardStr |
pop esi eax |
end if |
|
|
.next_node: |
stdcall get_wcaps, ebx |
and eax, AC_WCAP_TYPE |
shr eax, AC_WCAP_TYPE_SHIFT |
|
cmp eax, AC_WID_PIN |
jne .continue |
|
mov [edi + HDA_PINCFG.nid], bx |
stdcall snd_hda_codec_read, ebx, 0, AC_VERB_GET_CONFIG_DEFAULT, 0 |
mov [edi + HDA_PINCFG.cfg], eax |
|
.continue: |
add edi, HDA_PINCFG.sizeof |
inc ebx |
dec ecx |
jnz .next_node |
|
;Asper [ |
and ebx, 0xFFFF |
sub bx, [codec.start_nid] |
mov [codec.num_pins], ebx |
;Asper ] |
|
pop edi edx ecx ebx |
xor eax, eax |
ret |
.err_out: |
pop edi edx ecx ebx |
xor eax, eax |
dec eax |
ret |
endp |
|
|
|
; look up the given pin config list and return the item matching with NID |
proc look_up_pincfg stdcall, array:dword, nid:dword |
push ebx ecx edx |
mov ecx, [codec.num_pins] |
mov eax, [array] |
mov ebx, [nid] |
.next_pin: |
mov dx, [eax + HDA_PINCFG.nid] |
cmp dx, bx |
je .out |
.continue: |
add eax, HDA_PINCFG.sizeof |
dec ecx |
jnz .next_pin |
|
xor eax, eax |
.out: |
pop edx ecx ebx |
ret |
endp |
|
; write a config value for the given NID |
proc set_pincfg stdcall, nid:dword, cfg:dword |
push eax ebx ecx edx |
mov eax, [cfg] |
xor ebx, ebx |
mov edx, AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 |
mov ecx, 4 |
@@: |
mov bl, al |
stdcall snd_hda_codec_write, [nid], 0, edx, ebx |
shr eax, 8 |
inc edx |
dec ecx |
jnz @b |
.l1: |
pop edx ecx ebx eax |
ret |
endp |
|
|
;; |
;; snd_hda_codec_get_pincfg - Obtain a pin-default configuration |
;; @codec: the HDA codec |
;; @nid: NID to get the pin config |
;; |
;; Get the current pin config value of the given pin NID. |
;; If the pincfg value is cached or overridden via sysfs or driver, |
;; returns the cached value. |
;; |
proc snd_hda_codec_get_pincfg stdcall, nid:dword |
push edi |
stdcall look_up_pincfg, [codec.init_pins], [nid] |
test eax, eax |
jz @f |
mov edi, eax |
mov eax, [edi + HDA_PINCFG.cfg] |
@@: |
pop edi |
ret |
endp |
|
;====================================================================================== |
|
;; |
;; snd_hda_codec_setup_stream - set up the codec for streaming |
;; @nid: the NID to set up |
;; @stream_tag: stream tag to pass, it's between 0x1 and 0xf. |
;; @channel_id: channel id to pass, zero based. |
;; @format: stream format. |
;; |
proc hda_codec_setup_stream stdcall, nid:dword, stream_tag:dword, channel_id:dword, format:dword |
push eax |
mov eax, [nid] |
test eax, eax |
jnz @f |
pop eax |
ret |
@@: |
if DEBUG |
push esi |
mov esi, msgHDACodecSetupStream |
call SysMsgBoardStr |
stdcall fdword2str, 3 |
call SysMsgBoardStr |
|
mov esi, msgStream |
call SysMsgBoardStr |
mov eax, [stream_tag] |
stdcall fdword2str, 3 |
call SysMsgBoardStr |
|
mov esi, msgChannel |
call SysMsgBoardStr |
mov eax, [channel_id] |
stdcall fdword2str, 3 |
call SysMsgBoardStr |
|
mov esi, msgFormat |
call SysMsgBoardStr |
mov eax, [format] |
stdcall fdword2str, 3 |
call SysMsgBoardStr |
pop esi |
end if |
mov eax, [stream_tag] |
shl eax, 4 |
or eax, [channel_id] |
stdcall snd_hda_codec_write, [nid], 0, AC_VERB_SET_CHANNEL_STREAMID, eax |
|
mov eax, 1000 ; wait 1 ms |
call StallExec |
|
stdcall snd_hda_codec_write, [nid], 0, AC_VERB_SET_STREAM_FORMAT, [format] |
pop eax |
ret |
endp |
|
|
proc snd_hda_codec_cleanup_stream stdcall, nid:dword |
push eax |
mov eax, [nid] |
test eax, eax |
jz @f |
pop eax |
ret |
@@: |
if DEBUG |
push esi |
mov esi, msgHDACodecCleanupStream |
call SysMsgBoardStr |
stdcall fdword2str, 3 |
call SysMsgBoardStr |
pop esi |
end if |
stdcall snd_hda_codec_write, [nid], 0, AC_VERB_SET_CHANNEL_STREAMID, 0 |
if 0 ; keep the format |
mov eax, 1000000 ; wait 100 ms |
call StallExec |
stdcall snd_hda_codec_write, [nid], 0, AC_VERB_SET_STREAM_FORMAT, 0 |
end if |
pop eax |
ret |
endp |
|
|
proc read_pin_cap, nid:dword |
snd_hda_param_read [nid], AC_PAR_PIN_CAP |
ret |
endp |
|
|
;; read the current volume |
proc get_volume_mute stdcall, nid:dword, ch:dword, direction:dword, index:dword |
push ebx |
mov ebx, AC_AMP_GET_LEFT |
mov eax, [ch] |
test eax, eax |
jz @f |
mov ebx, AC_AMP_GET_RIGHT |
@@: |
mov eax, [direction] |
cmp eax, HDA_OUTPUT |
jne @f |
or ebx, AC_AMP_GET_OUTPUT |
jmp .l1 |
@@: |
or ebx, AC_AMP_GET_INPUT |
.l1: |
or ebx, [index] |
stdcall snd_hda_codec_read, [nid], 0, AC_VERB_GET_AMP_GAIN_MUTE, ebx |
and eax, 0xFF |
pop ebx |
ret |
endp |
|
|
;; write the current volume in info to the h/w |
proc put_volume_mute stdcall, nid:dword, ch:dword, direction:dword, index:dword, val:dword |
push eax ebx |
mov ebx, AC_AMP_SET_LEFT |
mov eax, [ch] |
test eax, eax |
jz @f |
mov ebx, AC_AMP_SET_RIGHT |
@@: |
mov eax, [direction] |
cmp eax, HDA_OUTPUT |
jne @f |
or ebx, AC_AMP_SET_OUTPUT |
jmp .l1 |
@@: |
or ebx, AC_AMP_SET_INPUT |
.l1: |
mov eax, [index] |
shl eax, AC_AMP_SET_INDEX_SHIFT |
or ebx, eax |
or ebx, [val] |
stdcall snd_hda_codec_write, [nid], 0, AC_VERB_SET_AMP_GAIN_MUTE, ebx |
pop ebx eax |
ret |
endp |
|
|
;; |
;; snd_hda_codec_amp_update - update the AMP value |
;; @nid: NID to read the AMP value |
;; @ch: channel (left=0 or right=1) |
;; @direction: #HDA_INPUT or #HDA_OUTPUT |
;; @idx: the index value (only for input direction) |
;; @mask: bit mask to set |
;; @val: the bits value to set |
;; |
;; Update the AMP value with a bit mask. |
;; Returns 0 if the value is unchanged, 1 if changed. |
;; |
;-proc snd_hda_codec_amp_update stdcall, nid:dword, ch:dword, direction:dword, idx:dword, mask:dword, val:dword |
;- push ebx edx |
;- mov eax, [mask] |
;- mov ebx, [val] |
;- and ebx, eax |
;- xor eax, -1 |
;- mov edx, eax |
;- stdcall get_volume_mute, [nid], [ch], [direction], [idx] |
;- and eax, edx |
;- or ebx, eax |
;- |
;- stdcall put_volume_mute, [nid], [ch], [direction], [idx], ebx |
;- xor eax, eax |
;- inc eax |
;- pop edx ebx |
;- ret |
;-endp |
|
|
;; |
;; snd_hda_codec_amp_stereo - update the AMP stereo values |
;; @nid: NID to read the AMP value |
;; @direction: #HDA_INPUT or #HDA_OUTPUT |
;; @idx: the index value (only for input direction) |
;; @mask: bit mask to set |
;; @val: the bits value to set |
;; |
;; Update the AMP values like snd_hda_codec_amp_update(), but for a |
;; stereo widget with the same mask and value. |
;; |
proc snd_hda_codec_amp_stereo stdcall, nid:dword, direction:dword, idx:dword, mask:dword, val:dword |
push ebx edx |
mov ebx, [val] |
mov edx, [mask] |
and ebx, edx |
stdcall put_volume_mute, [nid], 0, [direction], [idx], ebx |
stdcall put_volume_mute, [nid], 1, [direction], [idx], ebx |
pop edx ebx |
ret |
endp |
|
|
;; set power state of the codec |
proc snd_hda_set_power_state stdcall, fg:dword, power_state:dword |
push eax ebx ecx edx |
; this delay seems necessary to avoid click noise at power down |
mov ebx, [power_state] |
cmp ebx, AC_PWRST_D3 |
jne @f |
mov eax, 100000 |
call StallExec |
@@: |
stdcall snd_hda_codec_read, [fg], 0, AC_VERB_SET_POWER_STATE, ebx |
;partial workaround for "azx_get_response timeout" |
cmp ebx, AC_PWRST_D0 |
jne @f |
|
mov dx, [codec.vendor_id] |
cmp dx, 0x14F1 |
|
jne @f |
mov eax, 10000 |
call StallExec |
@@: |
movzx ecx, [codec.num_nodes] |
movzx edx, [codec.start_nid] |
.next_nid: |
stdcall get_wcaps, edx |
test eax, AC_WCAP_POWER |
jz .skip_nid |
|
stdcall get_wcaps_type, eax |
cmp ebx, AC_PWRST_D3 |
jne .l1 |
cmp eax, AC_WID_PIN |
jne .l1 |
;don't power down the widget if it controls |
;eapd and EAPD_BTLENABLE is set. |
stdcall read_pin_cap, edx |
test eax, AC_PINCAP_EAPD |
jz .l2 |
|
stdcall snd_hda_codec_read, edx, 0, AC_VERB_GET_EAPD_BTLENABLE, 0 |
and eax, 0x02 |
test eax, eax |
jnz .skip_nid |
.l2: |
.l1: |
stdcall snd_hda_codec_write, edx, 0, AC_VERB_SET_POWER_STATE, ebx |
.skip_nid: |
inc edx |
dec ecx |
jnz .next_nid |
|
cmp ebx, AC_PWRST_D0 |
jne .out |
;wait until codec reaches to D0 |
mov ecx, 500 |
.wait_D0: |
stdcall snd_hda_codec_read, [fg], 0, AC_VERB_GET_POWER_STATE, 0 |
cmp eax, ebx |
je .out |
mov eax, 1000 ; msleep(1); |
call StallExec |
dec ecx |
jnz .wait_D0 |
.out: |
pop edx ecx ebx eax |
ret |
endp |
|
|
;data |
|
; codec vendors |
align 16 |
msg_Cirrus db 'Cirrus Logic ',0 |
msg_Motorola db 'Motorola ',0 |
msg_SiliconImage db 'Silicon Image ',0 |
msg_Realtek db 'Realtek ',0 |
msg_Creative db 'Creative ',0 |
msg_IDT db 'IDT ',0 |
msg_LSI db 'LSI ',0 |
msg_AnalogDevices db 'Analog Devices ',0 |
msg_CMedia db 'C-Media ',0 |
msg_Conexant db 'Conexant ',0 |
msg_Chrontel db 'Chrontel ',0 |
msg_LG db 'LG ',0 |
msg_Wolfson db 'Wolfson Microelectronics ',0 |
msg_Qumranet db 'Qumranet ',0 |
msg_SigmaTel db 'SigmaTel ',0 |
ac_unknown db 'unknown manufacturer ',0 |
|
chip_unknown db 'unknown codec id ', 0 |
|
|
; codec vendor labels |
align 4 |
hda_vendor_ids: |
dd 0x1002, msg_ATI, chips_ATI |
dd 0x1013, msg_Cirrus, chips_Cirrus |
dd 0x1057, msg_Motorola, chips_Motorola |
dd 0x1095, msg_SiliconImage, chips_SiliconImage |
dd 0x10de, msg_NVidia, chips_NVidia |
dd 0x10ec, msg_Realtek, chips_Realtek |
dd 0x1102, msg_Creative, chips_Creative |
dd 0x1106, msg_VIA, chips_VIA |
dd 0x111d, msg_IDT, chips_IDT |
dd 0x11c1, msg_LSI, chips_LSI |
dd 0x11d4, msg_AnalogDevices, chips_Analog |
dd 0x13f6, msg_CMedia, chips_CMedia |
dd 0x14f1, msg_Conexant, chips_Conexant |
dd 0x17e8, msg_Chrontel, chips_Chrontel |
dd 0x1854, msg_LG, chips_LG |
dd 0x1aec, msg_Wolfson, chips_Wolfson |
dd 0x1af4, msg_Qumranet, chips_Qumranet ; Qemu 0.14 |
dd 0x434d, msg_CMedia, chips_CMedia |
dd 0x8086, msg_Intel, chips_Intel |
dd 0x8384, msg_SigmaTel, chips_SigmaTel |
dd 0 ; terminator |
|
align 16 ;known codecs |
chips_ATI dd 0xAA01, chip_ATIR6XX |
dd 0xFF |
|
chips_Cirrus dd 0xFF |
chips_Motorola dd 0xFF |
|
chips_SiliconImage dd 0x1392, chip_SI1392 |
dd 0xFF |
|
chips_NVidia dd 0x0002, chip_MCP78 |
dd 0xFF |
|
chips_Realtek dd 0x0262, chip_ALC262 |
dd 0x0268, chip_ALC268 |
dd 0x0269, chip_ALC269 |
dd 0x0272, chip_ALC272 |
dd 0x0662, chip_ALC662 |
dd 0x0663, chip_ALC663 |
dd 0x0883, chip_ALC883 |
dd 0x0887, chip_ALC887 |
dd 0x0888, chip_ALC888 |
dd 0x0889, chip_ALC889 |
dd 0xFF |
|
chips_Creative dd 0xFF |
|
chips_VIA dd 0xE721, chip_VT1708B_1 |
dd 0x0397, chip_VT17085_0 |
dd 0xFF |
|
chips_IDT dd 0xFF |
chips_LSI dd 0xFF |
|
chips_Analog dd 0x1986, chip_AD1986A |
dd 0x198B, chip_AD198B |
dd 0xFF |
|
chips_CMedia dd 0xFF |
|
chips_Conexant dd 0x5045, chip_CX20549 |
dd 0x5051, chip_CX20561 |
dd 0xFF |
|
chips_Chrontel dd 0xFF |
chips_LG dd 0xFF |
chips_Wolfson dd 0xFF |
chips_Intel dd 0xFF |
|
chips_Qumranet dd 0x0010, chip_HDA_OUTPUT |
dd 0x0020, chip_HDA_DUPLEX |
dd 0xFF |
|
chips_SigmaTel dd 0x7680, chip_STAC9221 |
dd 0x7682, chip_STAC9221_A2 |
dd 0xFF |
|
align 16 |
;AnalogDevices |
chip_AD1986A db 'AD1986A',13,10,0 |
chip_AD198B db 'AD198B',13,10,0 |
|
;ATI |
chip_ATIR6XX db 'ATIR6XX',13,10,0 |
|
;Silicon Image |
chip_SI1392 db 'SI1392',13,10,0 |
|
;NVidia |
chip_MCP78 db 'MCP78',13,10,0 |
|
;Realtek |
chip_ALC262 db 'ALC262',13,10,0 |
chip_ALC268 db 'ALC268',13,10,0 |
chip_ALC269 db 'ALC269',13,10,0 |
chip_ALC272 db 'ALC272',13,10,0 |
chip_ALC662 db 'ALC662',13,10,0 |
chip_ALC663 db 'ALC663',13,10,0 |
chip_ALC883 db 'ALC883',13,10,0 |
chip_ALC887 db 'ALC887',13,10,0 |
chip_ALC888 db 'ALC888',13,10,0 |
chip_ALC889 db 'ALC889',13,10,0 |
|
;Sigmatel |
chip_STAC9221 db 'STAC9221',13,10,0 |
chip_STAC9221_A2 db 'STAC9221_A2',13,10,0 |
|
;VIA |
chip_VT1708B_1 db 'VT1708B_1',13,10,0 |
chip_VT17085_0 db 'VT17085_0',13,10,0 |
|
;Conexant |
chip_CX20549 db 'CX20549',13,10,0 |
chip_CX20561 db 'CX20561',13,10,0 |
|
;Qumranet |
chip_HDA_OUTPUT db 'HDA-OUTPUT',13,10,0 |
chip_HDA_DUPLEX db 'HDA-DUPLEX',13,10,0 |
|