;; 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
    ;______________________________________
    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
    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!!!

     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 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 'LSI1039 (Agere Systems HDA Modem)',13,10,0
chip_LSI1040         db 'LSI1040 (Agere Systems HDA Modem)',13,10,0
chip_LSI3026         db 'LSI3026 (Agere Systems HDA Modem)',13,10,0
chip_LSI3055         db 'LSI3055 (Agere Systems HDA Modem)',13,10,0