;; 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
	push	eax esi
	mov	esi, msgVerbQuery
	invoke	SysMsgBoardStr
	mov	eax, ebx
	stdcall fdword2str, 2
	invoke	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
	invoke	SysMsgBoardStr
	mov	eax, edx
	stdcall fdword2str, 2
	invoke	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
	invoke	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
	;______________________________________
	cmp	dword [esi], 0
	je	.out
	movzx	ecx, word [esi] ; NID
	movzx	ebx, word [esi+2] ; verb
	and	bx, 0x0FFF
	movzx	eax, word [esi + 4] ; sizeof(param) = 4 bytes
	stdcall snd_hda_codec_write, ecx, 0, ebx, eax
	add	esi, 6
	jmp	@b
.out:
	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
	invoke	SysMsgBoardStr
	mov	eax, [nid]
	stdcall fdword2str, 3
	invoke	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
	invoke	SysMsgBoardStr
	mov	eax, [nid]
	stdcall fdword2str, 1
	invoke	SysMsgBoardStr

	mov	esi, strSemicolon
	invoke	SysMsgBoardStr
	mov	eax, ecx
	stdcall fdword2str, 0
	invoke	SysMsgBoardStr

	mov	esi, strSemicolon
	invoke	SysMsgBoardStr
	mov	eax, [parm]
	stdcall fdword2str, 2
	invoke	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
	invoke	SysMsgBoardStr
	pop	esi
	push	eax
	mov	eax, esi
	stdcall fdword2str, 0
	invoke	SysMsgBoardStr

	mov	esi, strSemicolon
	invoke	SysMsgBoardStr
	pop	eax
	stdcall fdword2str, 2
	invoke	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
	invoke	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
	invoke	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
	;Asper+: try to use another codec if possible [
if DEBUG
	push	esi
	mov	esi, msgNoAFGFound
	invoke	SysMsgBoardStr
	pop	esi
end  if
	push	ecx
	inc	eax
	mov	ecx, [codec.addr]
	shl	eax, cl
	pop	ecx
	cmp	eax, [ctrl.codec_mask]
	jl	.skip_codec
	;Asper+]

	mov	eax, [codec.mfg]
	test	eax, eax
	jnz	@f
if DEBUG
	push	esi
	mov	esi, emsgNoAFGorMFGFound
	invoke	SysMsgBoardStr
	pop	esi
end  if
.skip_codec:
	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
	invoke	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

	xor	eax, eax
	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!!!

	test	eax, eax
	jz	@f
if DEBUG
	push	esi
	mov	esi, emsgNoParserAvailable
	invoke	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
	invoke	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
	invoke	SysMsgBoardStr
	movzx	eax, [codec.chip_id]
	stdcall fdword2str, 2
	invoke	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
	invoke	SysMsgBoardStr
	pop	esi  edi  ebx  eax
	ret
.next:
	add	edi, 8
	jmp	@b
.unknown:
	mov	[codec.chip_ids], chip_unknown
	mov	esi, chip_unknown
	invoke	SysMsgBoardStr
	movzx	eax, [codec.chip_id]
	stdcall fdword2str, 2
	invoke	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
	push	eax esi
	mov	esi, msgSETUP_FG_NODES
	invoke	SysMsgBoardStr
	mov	eax, ebx
	stdcall fdword2str, 1
	invoke	SysMsgBoardStr

	mov	esi, strSemicolon
	invoke	SysMsgBoardStr
	mov	eax, ecx
	stdcall fdword2str, 3
	invoke	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
	push	eax esi
	mov	esi, msgFG_TYPE
	invoke	SysMsgBoardStr
	stdcall fdword2str, 3
	invoke	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
	push	eax esi
	mov	esi, msgSETUP_FG_NODES
	invoke	SysMsgBoardStr
	mov	eax, ebx
	stdcall fdword2str, 1
	invoke	SysMsgBoardStr

	mov	esi, strSemicolon
	invoke	SysMsgBoardStr
	mov	eax, ecx
	stdcall fdword2str, 3
	invoke	SysMsgBoardStr
	pop	esi eax
end if

if FDEBUG
	push	esi
	mov	esi, msgWCaps
	invoke	SysMsgBoardStr
	pop	esi
end if

	mov	eax, ecx
	shl	eax, 2
	push	ebx ecx
	invoke	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
	invoke	Kmalloc
	pop	ecx ebx
	test	eax, eax
	jz	.err_out
	mov	[codec.init_pins], eax
	mov	[codec.num_pins], 0
	mov	edi, eax
	;Asper ]

if FDEBUG
	push	eax esi
	mov	esi, msgPinCfgs
	invoke	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
	add	edi, HDA_PINCFG.sizeof
	inc	[codec.num_pins]

.continue:
	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



; 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
	invoke	SysMsgBoardStr
	stdcall fdword2str, 3
	invoke	SysMsgBoardStr

	mov	esi, msgStream
	invoke	SysMsgBoardStr
	mov	eax, [stream_tag]
	stdcall fdword2str, 3
	invoke	SysMsgBoardStr

	mov	esi, msgChannel
	invoke	SysMsgBoardStr
	mov	eax, [channel_id]
	stdcall fdword2str, 3
	invoke	SysMsgBoardStr

	mov	esi, msgFormat
	invoke	SysMsgBoardStr
	mov	eax, [format]
	stdcall fdword2str, 3
	invoke	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
	invoke	SysMsgBoardStr
	stdcall fdword2str, 3
	invoke	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

; execute pin sense measurement
proc snd_hda_read_pin_sense stdcall, nid:dword, trigger_sense:dword
	mov	eax, [trigger_sense]
	test	eax, eax
	jz	.no_trigger_sense

	stdcall read_pin_cap, [nid]
	test	eax, AC_PINCAP_TRIG_REQ ;need trigger?
	jz	.no_trigger_sense

	stdcall snd_hda_codec_read, [nid], 0, AC_VERB_SET_PIN_SENSE, 0
.no_trigger_sense:
	stdcall snd_hda_codec_read, [nid], 0, AC_VERB_GET_PIN_SENSE, 0
	ret
endp

proc is_jack_detectable stdcall, nid:dword
	stdcall read_pin_cap, [nid]
	test	eax, AC_PINCAP_PRES_DETECT
	jz	.no

	stdcall get_wcaps, [nid]
	test	eax, AC_WCAP_UNSOL_CAP
	jz	.no
.yes:
	xor	eax, eax
	inc	eax
	ret
.no:
	xor	eax, eax
	ret
endp

proc snd_hda_enable_pin_sense stdcall nid:dword, jacktag:dword
	push	eax
	stdcall is_jack_detectable, [nid]
	test	eax, eax
	jz	@f
	mov	eax, [jacktag]
	or	eax, AC_USRSP_EN
	stdcall snd_hda_codec_write, [nid], 0, AC_VERB_SET_UNSOLICITED_ENABLE, eax
@@:
	pop	eax
	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 0x1039, chip_LSI1039
		     dd 0x1040, chip_LSI1040
		     dd 0x3026, chip_LSI3026
		     dd 0x3055, chip_LSI3055
		     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

;LSI
chip_LSI1039	     db '1039 (Agere Systems HDA Modem)',13,10,0
chip_LSI1040	     db '1040 (Agere Systems HDA Modem)',13,10,0
chip_LSI3026	     db '3026 (Agere Systems HDA Modem)',13,10,0
chip_LSI3055	     db '3055 (Agere Systems HDA Modem)',13,10,0