From 7a6b2a11adc7efc12ce14f46d2a592daa08e336c Mon Sep 17 00:00:00 2001 From: "Kirill Lipatov (Leency)" Date: Fri, 7 Dec 2012 02:56:49 +0000 Subject: [PATCH] intel hda from Asper 0.18b git-svn-id: svn://kolibrios.org@3083 a494cfbc-eb01-0410-851d-a64ba20cac60 --- drivers/audio/intel_hda/CODEC.INC | 1336 +++++++++++ drivers/audio/intel_hda/CODEC_H.INC | 648 +++++ drivers/audio/intel_hda/IMPORTS.INC | 89 + drivers/audio/intel_hda/PROC32.INC | 268 +++ drivers/audio/intel_hda/hda_generic.inc | 936 ++++++++ drivers/audio/intel_hda/intel_hda.asm | 2924 +++++++++++++++++++++++ 6 files changed, 6201 insertions(+) create mode 100644 drivers/audio/intel_hda/CODEC.INC create mode 100644 drivers/audio/intel_hda/CODEC_H.INC create mode 100644 drivers/audio/intel_hda/IMPORTS.INC create mode 100644 drivers/audio/intel_hda/PROC32.INC create mode 100644 drivers/audio/intel_hda/hda_generic.inc create mode 100644 drivers/audio/intel_hda/intel_hda.asm diff --git a/drivers/audio/intel_hda/CODEC.INC b/drivers/audio/intel_hda/CODEC.INC new file mode 100644 index 0000000000..630266cc73 --- /dev/null +++ b/drivers/audio/intel_hda/CODEC.INC @@ -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 + diff --git a/drivers/audio/intel_hda/CODEC_H.INC b/drivers/audio/intel_hda/CODEC_H.INC new file mode 100644 index 0000000000..8a8e68f660 --- /dev/null +++ b/drivers/audio/intel_hda/CODEC_H.INC @@ -0,0 +1,648 @@ +; Universal Interface for Intel High Definition Audio Codec + +; nodes +AC_NODE_ROOT equ 0x00 + +; function group types +AC_GRP_AUDIO_FUNCTION equ 0x01 +AC_GRP_MODEM_FUNCTION equ 0x02 + +; widget types +AC_WID_AUD_OUT equ 0x0 ; Audio Out +AC_WID_AUD_IN equ 0x1 ; Audio In +AC_WID_AUD_MIX equ 0x2 ; Audio Mixer +AC_WID_AUD_SEL equ 0x3 ; Audio Selector +AC_WID_PIN equ 0x4 ; Pin Complex +AC_WID_POWER equ 0x5 ; Power +AC_WID_VOL_KNB equ 0x6 ; Volume Knob +AC_WID_BEEP equ 0x7 ; Beep Generator +AC_WID_VENDOR equ 0x0F ; Vendor specific + +; GET verbs +AC_VERB_GET_STREAM_FORMAT equ 0x0A00 +AC_VERB_GET_AMP_GAIN_MUTE equ 0x0B00 +AC_VERB_GET_PROC_COEF equ 0x0C00 +AC_VERB_GET_COEF_INDEX equ 0x0D00 +AC_VERB_PARAMETERS equ 0x0F00 +AC_VERB_GET_CONNECT_SEL equ 0x0F01 +AC_VERB_GET_CONNECT_LIST equ 0x0F02 +AC_VERB_GET_PROC_STATE equ 0x0F03 +AC_VERB_GET_SDI_SELECT equ 0x0F04 +AC_VERB_GET_POWER_STATE equ 0x0F05 +AC_VERB_GET_CONV equ 0x0F06 +AC_VERB_GET_PIN_WIDGET_CONTROL equ 0x0F07 +AC_VERB_GET_UNSOLICITED_RESPONSE equ 0x0F08 +AC_VERB_GET_PIN_SENSE equ 0x0F09 +AC_VERB_GET_BEEP_CONTROL equ 0x0F0A +AC_VERB_GET_EAPD_BTLENABLE equ 0x0F0C +AC_VERB_GET_DIGI_CONVERT_1 equ 0x0F0D +AC_VERB_GET_DIGI_CONVERT_2 equ 0x0F0E ; unused +AC_VERB_GET_VOLUME_KNOB_CONTROL equ 0x0F0F +; f10-f1a: GPIO +AC_VERB_GET_GPIO_DATA equ 0x0F15 +AC_VERB_GET_GPIO_MASK equ 0x0F16 +AC_VERB_GET_GPIO_DIRECTION equ 0x0F17 +AC_VERB_GET_GPIO_WAKE_MASK equ 0x0F18 +AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK equ 0x0F19 +AC_VERB_GET_GPIO_STICKY_MASK equ 0x0F1A +AC_VERB_GET_CONFIG_DEFAULT equ 0x0F1C +; f20: AFG/MFG +AC_VERB_GET_SUBSYSTEM_ID equ 0x0F20 +AC_VERB_GET_CVT_CHAN_COUNT equ 0x0F2D +AC_VERB_GET_HDMI_DIP_SIZE equ 0x0F2E +AC_VERB_GET_HDMI_ELDD equ 0x0F2F +AC_VERB_GET_HDMI_DIP_INDEX equ 0x0F30 +AC_VERB_GET_HDMI_DIP_DATA equ 0x0F31 +AC_VERB_GET_HDMI_DIP_XMIT equ 0x0F32 +AC_VERB_GET_HDMI_CP_CTRL equ 0x0F33 +AC_VERB_GET_HDMI_CHAN_SLOT equ 0x0F34 + +; SET verbs + +AC_VERB_SET_STREAM_FORMAT equ 0x200 +AC_VERB_SET_AMP_GAIN_MUTE equ 0x300 +AC_VERB_SET_PROC_COEF equ 0x400 +AC_VERB_SET_COEF_INDEX equ 0x500 +AC_VERB_SET_CONNECT_SEL equ 0x701 +AC_VERB_SET_PROC_STATE equ 0x703 +AC_VERB_SET_SDI_SELECT equ 0x704 +AC_VERB_SET_POWER_STATE equ 0x705 +AC_VERB_SET_CHANNEL_STREAMID equ 0x706 +AC_VERB_SET_PIN_WIDGET_CONTROL equ 0x707 +AC_VERB_SET_UNSOLICITED_ENABLE equ 0x708 +AC_VERB_SET_PIN_SENSE equ 0x709 +AC_VERB_SET_BEEP_CONTROL equ 0x70A +AC_VERB_SET_EAPD_BTLENABLE equ 0x70C +AC_VERB_SET_DIGI_CONVERT_1 equ 0x70D +AC_VERB_SET_DIGI_CONVERT_2 equ 0x70E +AC_VERB_SET_VOLUME_KNOB_CONTROL equ 0x70F +AC_VERB_SET_GPIO_DATA equ 0x715 +AC_VERB_SET_GPIO_MASK equ 0x716 +AC_VERB_SET_GPIO_DIRECTION equ 0x717 +AC_VERB_SET_GPIO_WAKE_MASK equ 0x718 +AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK equ 0x719 +AC_VERB_SET_GPIO_STICKY_MASK equ 0x71A +AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 equ 0x71C +AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 equ 0x71D +AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 equ 0x71E +AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 equ 0x71F +AC_VERB_SET_EAPD equ 0x788 +AC_VERB_SET_CODEC_RESET equ 0x7FF +AC_VERB_SET_CVT_CHAN_COUNT equ 0x72D +AC_VERB_SET_HDMI_DIP_INDEX equ 0x730 +AC_VERB_SET_HDMI_DIP_DATA equ 0x731 +AC_VERB_SET_HDMI_DIP_XMIT equ 0x732 +AC_VERB_SET_HDMI_CP_CTRL equ 0x733 +AC_VERB_SET_HDMI_CHAN_SLOT equ 0x734 + +; Parameter IDs + +AC_PAR_VENDOR_ID equ 0x00 +AC_PAR_SUBSYSTEM_ID equ 0x01 +AC_PAR_REV_ID equ 0x02 +AC_PAR_NODE_COUNT equ 0x04 +AC_PAR_FUNCTION_TYPE equ 0x05 +AC_PAR_AUDIO_FG_CAP equ 0x08 +AC_PAR_AUDIO_WIDGET_CAP equ 0x09 +AC_PAR_PCM equ 0x0A +AC_PAR_STREAM equ 0x0B +AC_PAR_PIN_CAP equ 0x0C +AC_PAR_AMP_IN_CAP equ 0x0D +AC_PAR_CONNLIST_LEN equ 0x0E +AC_PAR_POWER_STATE equ 0x0F +AC_PAR_PROC_CAP equ 0x10 +AC_PAR_GPIO_CAP equ 0x11 +AC_PAR_AMP_OUT_CAP equ 0x12 +AC_PAR_VOL_KNB_CAP equ 0x13 +AC_PAR_HDMI_LPCM_CAP equ 0x20 + +; AC_VERB_PARAMETERS results (32bit) + + +; Function Group Type +AC_FGT_TYPE equ (0xFF shl 0) +AC_FGT_TYPE_SHIFT equ 0 +AC_FGT_UNSOL_CAP equ (1 shl 8) + +; Audio Function Group Capabilities +AC_AFG_OUT_DELAY equ (0xF shl 0) +AC_AFG_IN_DELAY equ (0xF shl 8) +AC_AFG_BEEP_GEN equ (1 shl 16) + +; Audio Widget Capabilities +AC_WCAP_STEREO equ (1 shl 0) ; stereo I/O +AC_WCAP_IN_AMP equ (1 shl 1) ; AMP-in present +AC_WCAP_OUT_AMP equ (1 shl 2) ; AMP-out present +AC_WCAP_AMP_OVRD equ (1 shl 3) ; AMP-parameter override +AC_WCAP_FORMAT_OVRD equ (1 shl 4) ; format override +AC_WCAP_STRIPE equ (1 shl 5) ; stripe +AC_WCAP_PROC_WID equ (1 shl 6) ; Proc Widget +AC_WCAP_UNSOL_CAP equ (1 shl 7) ; Unsol capable +AC_WCAP_CONN_LIST equ (1 shl 8) ; connection list +AC_WCAP_DIGITAL equ (1 shl 9) ; digital I/O +AC_WCAP_POWER equ (1 shl 10) ; power control +AC_WCAP_LR_SWAP equ (1 shl 11) ; L/R swap +AC_WCAP_CP_CAPS equ (1 shl 12) ; content protection +AC_WCAP_CHAN_CNT_EXT equ (7 shl 13) ; channel count ext +AC_WCAP_DELAY equ (0xF shl 16) +AC_WCAP_DELAY_SHIFT equ 16 +AC_WCAP_TYPE equ (0xF shl 20) +AC_WCAP_TYPE_SHIFT equ 20 + +; supported PCM rates and bits +AC_SUPPCM_RATES equ (0xFFF shl 0) +AC_SUPPCM_BITS_8 equ (1 shl 16) +AC_SUPPCM_BITS_16 equ (1 shl 17) +AC_SUPPCM_BITS_20 equ (1 shl 18) +AC_SUPPCM_BITS_24 equ (1 shl 19) +AC_SUPPCM_BITS_32 equ (1 shl 20) + +; supported PCM stream format +AC_SUPFMT_PCM equ (1 shl 0) +AC_SUPFMT_FLOAT32 equ (1 shl 1) +AC_SUPFMT_AC3 equ (1 shl 2) + +; GP I/O count +AC_GPIO_IO_COUNT equ (0xFF shl 0) +AC_GPIO_O_COUNT equ (0xFF shl 8) +AC_GPIO_O_COUNT_SHIFT equ 8 +AC_GPIO_I_COUNT equ (0xFF shl 16) +AC_GPIO_I_COUNT_SHIFT equ 16 +AC_GPIO_UNSOLICITED equ (1 shl 30) +AC_GPIO_WAKE equ (1 shl 31) + +; Converter stream, channel +AC_CONV_CHANNEL equ (0xF shl 0) +AC_CONV_STREAM equ (0xF shl 4) +AC_CONV_STREAM_SHIFT equ 4 + +; Input converter SDI select +AC_SDI_SELECT equ (0xF shl 0) + +; Unsolicited response control +AC_UNSOL_TAG equ (0x3F shl 0) +AC_UNSOL_ENABLED equ (1 shl 7) +AC_USRSP_EN equ AC_UNSOL_ENABLED + +; Unsolicited responses +AC_UNSOL_RES_TAG equ (0x3F shl 26) +AC_UNSOL_RES_TAG_SHIFT equ 26 +AC_UNSOL_RES_SUBTAG equ (0x1F shl 21) +AC_UNSOL_RES_SUBTAG_SHIFT equ 21 +AC_UNSOL_RES_ELDV equ (1 shl 1) ; ELD Data valid (for HDMI) +AC_UNSOL_RES_PD equ (1 shl 0) ; pinsense detect +AC_UNSOL_RES_CP_STATE equ (1 shl 1) ; content protection +AC_UNSOL_RES_CP_READY equ (1 shl 0) ; content protection + +; Pin widget capabilies +AC_PINCAP_IMP_SENSE equ (1 shl 0) ; impedance sense capable +AC_PINCAP_TRIG_REQ equ (1 shl 1) ; trigger required +AC_PINCAP_PRES_DETECT equ (1 shl 2) ; presence detect capable +AC_PINCAP_HP_DRV equ (1 shl 3) ; headphone drive capable +AC_PINCAP_OUT equ (1 shl 4) ; output capable +AC_PINCAP_IN equ (1 shl 5) ; input capable +AC_PINCAP_BALANCE equ (1 shl 6) ; balanced I/O capable +; Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification, +; but is marked reserved in the Intel HDA specification. + +AC_PINCAP_LR_SWAP equ (1 shl 7) ; L/R swap +; Note: The same bit as LR_SWAP is newly defined as HDMI capability +; in HD-audio specification + +AC_PINCAP_HDMI equ (1 shl 7) ; HDMI pin +AC_PINCAP_DP equ (1 shl 24) ; DisplayPort pin, can + ; coexist with AC_PINCAP_HDMI + +AC_PINCAP_VREF equ (0x37 shl 8) +AC_PINCAP_VREF_SHIFT equ 8 +AC_PINCAP_EAPD equ (1 shl 16) ; EAPD capable +AC_PINCAP_HBR equ (1 shl 27) ; High Bit Rate +; Vref status (used in pin cap) +AC_PINCAP_VREF_HIZ equ (1 shl 0) ; Hi-Z +AC_PINCAP_VREF_50 equ (1 shl 1) ; 50% +AC_PINCAP_VREF_GRD equ (1 shl 2) ; ground +AC_PINCAP_VREF_80 equ (1 shl 4) ; 80% +AC_PINCAP_VREF_100 equ (1 shl 5) ; 100% + +; Amplifier capabilities +AC_AMPCAP_OFFSET equ (0x7F shl 0) ; 0dB offset +AC_AMPCAP_OFFSET_SHIFT equ 0 +AC_AMPCAP_NUM_STEPS equ (0x7F shl 8) ; number of steps +AC_AMPCAP_NUM_STEPS_SHIFT equ 8 +AC_AMPCAP_STEP_SIZE equ (0x7F shl 16) ; step size 0-32dB + ; in 0.25dB +AC_AMPCAP_STEP_SIZE_SHIFT equ 16 +AC_AMPCAP_MUTE equ (1 shl 31) ; mute capable +AC_AMPCAP_MUTE_SHIFT equ 31 + +; Connection list +AC_CLIST_LENGTH equ (0x7F shl 0) +AC_CLIST_LONG equ (1 shl 7) + +; Supported power status +AC_PWRST_D0SUP equ (1 shl 0) +AC_PWRST_D1SUP equ (1 shl 1) +AC_PWRST_D2SUP equ (1 shl 2) +AC_PWRST_D3SUP equ (1 shl 3) +AC_PWRST_D3COLDSUP equ (1 shl 4) +AC_PWRST_S3D3COLDSUP equ (1 shl 29) +AC_PWRST_CLKSTOP equ (1 shl 30) +AC_PWRST_EPSS equ (1 shl 31) + +; Power state values +AC_PWRST_SETTING equ (0xF shl 0) +AC_PWRST_ACTUAL equ (0xF shl 4) +AC_PWRST_ACTUAL_SHIFT equ 4 +AC_PWRST_D0 equ 0x00 +AC_PWRST_D1 equ 0x01 +AC_PWRST_D2 equ 0x02 +AC_PWRST_D3 equ 0x03 + +; Processing capabilies +AC_PCAP_BENIGN equ (1 shl 0) +AC_PCAP_NUM_COEF equ (0xFF shl 8) +AC_PCAP_NUM_COEF_SHIFT equ 8 + +; Volume knobs capabilities +AC_KNBCAP_NUM_STEPS equ (0x7F shl 0) +AC_KNBCAP_DELTA equ (1 shl 7) + +; HDMI LPCM capabilities +AC_LPCMCAP_48K_CP_CHNS equ (0x0F shl 0) ; max channels w/ CP-on +AC_LPCMCAP_48K_NO_CHNS equ (0x0F shl 4) ; max channels w/o CP-on +AC_LPCMCAP_48K_20BIT equ (1 shl 8) ; 20b bitrate supported +AC_LPCMCAP_48K_24BIT equ (1 shl 9) ; 24b bitrate supported +AC_LPCMCAP_96K_CP_CHNS equ (0x0F shl 10) ; max channels w/ CP-on +AC_LPCMCAP_96K_NO_CHNS equ (0x0F shl 14) ; max channels w/o CP-on +AC_LPCMCAP_96K_20BIT equ (1 shl 18) ; 20b bitrate supported +AC_LPCMCAP_96K_24BIT equ (1 shl 19) ; 24b bitrate supported +AC_LPCMCAP_192K_CP_CHNS equ (0x0F shl 20) ; max channels w/ CP-on +AC_LPCMCAP_192K_NO_CHNS equ (0x0F shl 24) ; max channels w/o CP-on +AC_LPCMCAP_192K_20BIT equ (1 shl 28) ; 20b bitrate supported +AC_LPCMCAP_192K_24BIT equ (1 shl 29) ; 24b bitrate supported +AC_LPCMCAP_44K equ (1 shl 30) ; 44.1kHz support +AC_LPCMCAP_44K_MS equ (1 shl 31) ; 44.1kHz-multiplies support + + +; Control Parameters + +; Amp gain/mute +AC_AMP_MUTE equ (1 shl 7) +AC_AMP_GAIN equ (0x7F) +AC_AMP_GET_INDEX equ (0xF shl 0) + +AC_AMP_GET_LEFT equ (1 shl 13) +AC_AMP_GET_RIGHT equ (0 shl 13) +AC_AMP_GET_OUTPUT equ (1 shl 15) +AC_AMP_GET_INPUT equ (0 shl 15) + +AC_AMP_SET_INDEX equ (0xF shl 8) +AC_AMP_SET_INDEX_SHIFT equ 8 +AC_AMP_SET_RIGHT equ (1 shl 12) +AC_AMP_SET_LEFT equ (1 shl 13) +AC_AMP_SET_INPUT equ (1 shl 14) +AC_AMP_SET_OUTPUT equ (1 shl 15) + +; DIGITAL1 bits +AC_DIG1_ENABLE equ (1 shl 0) +AC_DIG1_V equ (1 shl 1) +AC_DIG1_VCFG equ (1 shl 2) +AC_DIG1_EMPHASIS equ (1 shl 3) +AC_DIG1_COPYRIGHT equ (1 shl 4) +AC_DIG1_NONAUDIO equ (1 shl 5) +AC_DIG1_PROFESSIONAL equ (1 shl 6) +AC_DIG1_LEVEL equ (1 shl 7) + +; DIGITAL2 bits +AC_DIG2_CC equ (0x7F shl 0) + +; Pin widget control - 8bit +AC_PINCTL_VREFEN equ (0x7 shl 0) +AC_PINCTL_VREF_HIZ equ 0 ; Hi-Z +AC_PINCTL_VREF_50 equ 1 ; 50% +AC_PINCTL_VREF_GRD equ 2 ; ground +AC_PINCTL_VREF_80 equ 4 ; 80% +AC_PINCTL_VREF_100 equ 5 ; 100% +AC_PINCTL_IN_EN equ (1 shl 5) +AC_PINCTL_OUT_EN equ (1 shl 6) +AC_PINCTL_HP_EN equ (1 shl 7) + +; Pin sense - 32bit +AC_PINSENSE_IMPEDANCE_MASK equ (0x7FFFFFFF) +AC_PINSENSE_PRESENCE equ (1 shl 31) +AC_PINSENSE_ELDV equ (1 shl 30) ; ELD valid (HDMI) + +; EAPD/BTL enable - 32bit +AC_EAPDBTL_BALANCED equ (1 shl 0) +AC_EAPDBTL_EAPD equ (1 shl 1) +AC_EAPDBTL_LR_SWAP equ (1 shl 2) + +; HDMI ELD data +AC_ELDD_ELD_VALID equ (1 shl 31) +AC_ELDD_ELD_DATA equ 0xFF + +; HDMI DIP size +AC_DIPSIZE_ELD_BUF equ (1 shl 3) ; ELD buf size of packet size +AC_DIPSIZE_PACK_IDX equ (0x07 shl 0) ; packet index + +; HDMI DIP index +AC_DIPIDX_PACK_IDX equ (0x07 shl 5) ; packet idnex +AC_DIPIDX_BYTE_IDX equ (0x1F shl 0) ; byte index + +; HDMI DIP xmit (transmit) control +AC_DIPXMIT_MASK equ (0x3 shl 6) +AC_DIPXMIT_DISABLE equ (0x0 shl 6) ; disable xmit +AC_DIPXMIT_ONCE equ (0x2 shl 6) ; xmit once then disable +AC_DIPXMIT_BEST equ (0x3 shl 6) ; best effort + +; HDMI content protection (CP) control +AC_CPCTRL_CES equ (1 shl 9) ; current encryption state +AC_CPCTRL_READY equ (1 shl 8) ; ready bit +AC_CPCTRL_SUBTAG equ (0x1F shl 3) ; subtag for unsol-resp +AC_CPCTRL_STATE equ (3 shl 0) ; current CP request state + +; Converter channel <-> HDMI slot mapping +AC_CVTMAP_HDMI_SLOT equ (0xF shl 0) ; HDMI slot number +AC_CVTMAP_CHAN equ (0xF shl 4) ; converter channel number + +; configuration default - 32bit +AC_DEFCFG_SEQUENCE equ (0xF shl 0) +AC_DEFCFG_DEF_ASSOC equ (0xF shl 4) +AC_DEFCFG_ASSOC_SHIFT equ 4 +AC_DEFCFG_MISC equ (0xF shl 8) +AC_DEFCFG_MISC_SHIFT equ 8 +AC_DEFCFG_MISC_NO_PRESENCE equ (1 shl 0) +AC_DEFCFG_COLOR equ (0xF shl 12) +AC_DEFCFG_COLOR_SHIFT equ 12 +AC_DEFCFG_CONN_TYPE equ (0xF shl 16) +AC_DEFCFG_CONN_TYPE_SHIFT equ 16 +AC_DEFCFG_DEVICE equ (0xF shl 20) +AC_DEFCFG_DEVICE_SHIFT equ 20 +AC_DEFCFG_LOCATION equ (0x3F shl 24) +AC_DEFCFG_LOCATION_SHIFT equ 24 +AC_DEFCFG_PORT_CONN equ (0x3 shl 30) +AC_DEFCFG_PORT_CONN_SHIFT equ 30 + +; device device types (0x0-0xf) +AC_JACK_LINE_OUT equ 0x0 +AC_JACK_SPEAKER equ 0x1 +AC_JACK_HP_OUT equ 0x2 +AC_JACK_CD equ 0x3 +AC_JACK_SPDIF_OUT equ 0x4 +AC_JACK_DIG_OTHER_OUT equ 0x5 +AC_JACK_MODEM_LINE_SIDE equ 0x6 +AC_JACK_MODEM_HAND_SIDE equ 0x7 +AC_JACK_LINE_IN equ 0x8 +AC_JACK_AUX equ 0x9 +AC_JACK_MIC_IN equ 0xA +AC_JACK_TELEPHONY equ 0xB +AC_JACK_SPDIF_IN equ 0xC +AC_JACK_DIG_OTHER_IN equ 0xD +AC_JACK_OTHER equ 0xF + +; jack connection types (0x0-0xf) +AC_JACK_CONN_UNKNOWN equ 0x0 +AC_JACK_CONN_1_8 equ 0x1 +AC_JACK_CONN_1_4 equ 0x2 +AC_JACK_CONN_ATAPI equ 0x3 +AC_JACK_CONN_RCA equ 0x4 +AC_JACK_CONN_OPTICAL equ 0x5 +AC_JACK_CONN_OTHER_DIGITAL equ 0x6 +AC_JACK_CONN_OTHER_ANALOG equ 0x7 +AC_JACK_CONN_DIN equ 0x8 +AC_JACK_CONN_XLR equ 0x9 +AC_JACK_CONN_RJ11 equ 0xA +AC_JACK_CONN_COMB equ 0xB +AC_JACK_CONN_OTHER equ 0xF + +; jack colors (0x0-0xf) +AC_JACK_COLOR_UNKNOWN equ 0x0 +AC_JACK_COLOR_BLACK equ 0x1 +AC_JACK_COLOR_GREY equ 0x2 +AC_JACK_COLOR_BLUE equ 0x3 +AC_JACK_COLOR_GREEN equ 0x4 +AC_JACK_COLOR_RED equ 0x5 +AC_JACK_COLOR_ORANGE equ 0x6 +AC_JACK_COLOR_YELLOW equ 0x7 +AC_JACK_COLOR_PURPLE equ 0x8 +AC_JACK_COLOR_PINK equ 0x9 +AC_JACK_COLOR_WHITE equ 0xE +AC_JACK_COLOR_OTHER equ 0xF + +; Jack location (0x0-0x3f) +; common case +AC_JACK_LOC_NONE equ 0 +AC_JACK_LOC_REAR equ 1 +AC_JACK_LOC_FRONT equ 2 +AC_JACK_LOC_LEFT equ 3 +AC_JACK_LOC_RIGHT equ 4 +AC_JACK_LOC_TOP equ 5 +AC_JACK_LOC_BOTTOM equ 6 + +; bits 4-5 +AC_JACK_LOC_EXTERNAL equ 0x00 +AC_JACK_LOC_INTERNAL equ 0x10 +AC_JACK_LOC_SEPARATE equ 0x20 +AC_JACK_LOC_OTHER equ 0x30 + +; external on primary chasis +AC_JACK_LOC_REAR_PANEL equ 0x07 +AC_JACK_LOC_DRIVE_BAY equ 0x08 +; internal +AC_JACK_LOC_RISER equ 0x17 +AC_JACK_LOC_HDMI equ 0x18 +AC_JACK_LOC_ATAPI equ 0x19 +; others +AC_JACK_LOC_MOBILE_IN equ 0x37 +AC_JACK_LOC_MOBILE_OUT equ 0x38 + +; Port connectivity (0-3) +AC_JACK_PORT_COMPLEX equ 0 +AC_JACK_PORT_NONE equ 1 +AC_JACK_PORT_FIXED equ 2 +AC_JACK_PORT_BOTH equ 3 + +; max. connections to a widget +HDA_MAX_CONNECTIONS equ 32 + +; max. codec address +HDA_MAX_CODEC_ADDRESS equ 0x0f + +; max number of PCM devics per card +HDA_MAX_PCMS equ 10 + + +; Structures + +; direction +HDA_INPUT equ 0x0 +HDA_OUTPUT equ 0x1 + + +struc HDA_VERB +{ + .nid dw ? + .verb dd ? + .param dd ? +} + +virtual at 0 + HDA_VERB HDA_VERB +end virtual + +; the struct for codec->pin_configs +struc HDA_PINCFG +{ + .nid dw ? + .reserved dw ? ;Asper + for align purposes + .cfg dd ? + .sizeof: +} + +virtual at 0 + HDA_PINCFG HDA_PINCFG +end virtual + + +;Asper [ this part is from "hda_local.h" + +;(...) +; amp value bits +HDA_AMP_MUTE equ 0x80 +HDA_AMP_UNMUTE equ 0x00 +HDA_AMP_VOLMASK equ 0x7F +;(...) + + +; unsolicited event handler +HDA_UNSOL_QUEUE_SIZE equ 64 + +;struc HDA_BUS_UNSOLICITED +;{ +; ; ring buffer +; .queue: +; times HDA_UNSOL_QUEUE_SIZE*2 dd ? +; .rp dd ? +; .wp dd ? +; +; ; workqueue +; .work dd ?;struct work_struct work; +; .bus dd ? ;struct hda_bus ;bus +;}; + +; Helper for automatic ping configuration +AUTO_PIN_MIC equ 0 +AUTO_PIN_FRONT_MIC equ 1 +AUTO_PIN_LINE equ 2 +AUTO_PIN_FRONT_LINE equ 3 +AUTO_PIN_CD equ 4 +AUTO_PIN_AUX equ 5 +AUTO_PIN_LAST equ 6 + + +AUTO_PIN_LINE_OUT equ 0 +AUTO_PIN_SPEAKER_OUT equ 1 +AUTO_PIN_HP_OUT equ 2 + + +;extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST]; + +AUTO_CFG_MAX_OUTS equ 5 + +;struc AUTO_PIN_CFG +;{ +; .line_outs dd ? +; ; sorted in the order of FrontSurrCLFESide +; .line_out_pins times AUTO_CFG_MAX_OUTS dw ? +; .speaker_outs dd ? +; .speaker_pins times AUTO_CFG_MAX_OUTS dw ? +; .hp_outs dd ? +; .line_out_type dd ? ; AUTO_PIN_XXX_OUT +; .hp_pins times AUTO_CFG_MAX_OUTS dw ? +; .input_pins times AUTO_PIN_LAST dw ? +; .dig_outs dd ? +; .dig_out_pins times 2 dd ? +; .dig_in_pin dw ? +; .mono_out_pin dw ? +; .dig_out_type times 2 dd ? ; HDA_PCM_TYPE_XXX +; .dig_in_type dd ? ; HDA_PCM_TYPE_XXX +;} + +;#define get_defcfg_connect(cfg) \ +; ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) +;#define get_defcfg_association(cfg) \ +; ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT) +;#define get_defcfg_location(cfg) \ +; ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) +;#define get_defcfg_sequence(cfg) \ +; (cfg & AC_DEFCFG_SEQUENCE) +;#define get_defcfg_device(cfg) \ +; ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) + +; amp values +;AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8)) +;AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8)) +AMP_OUT_MUTE equ 0xb080 +AMP_OUT_UNMUTE equ 0xb000 +AMP_OUT_ZERO equ 0xb000 +; pinctl values +PIN_IN equ (AC_PINCTL_IN_EN) +PIN_VREFHIZ equ (AC_PINCTL_IN_EN or AC_PINCTL_VREF_HIZ) +PIN_VREF50 equ (AC_PINCTL_IN_EN or AC_PINCTL_VREF_50) +PIN_VREFGRD equ (AC_PINCTL_IN_EN or AC_PINCTL_VREF_GRD) +PIN_VREF80 equ (AC_PINCTL_IN_EN or AC_PINCTL_VREF_80) +PIN_VREF100 equ (AC_PINCTL_IN_EN or AC_PINCTL_VREF_100) +PIN_OUT equ (AC_PINCTL_OUT_EN) +PIN_HP equ (AC_PINCTL_OUT_EN or AC_PINCTL_HP_EN) +PIN_HP_AMP equ (AC_PINCTL_HP_EN) + + +; get widget capabilities +;static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) +proc get_wcaps stdcall, nid:dword + push ebx ecx edx + xor eax, eax + movzx ebx, [codec.start_nid] + movzx ecx, [codec.num_nodes] + mov edx, [nid] + + cmp edx, ebx + jl .out + + add ecx, ebx + cmp edx, ecx + jge .out + + sub edx, ebx + shl edx, 2 + add edx, [codec.wcaps] + mov eax, [edx] + .out: + pop edx ecx ebx + ret +endp + +; get the widget type from widget capability bits +;#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT) +proc get_wcaps_type stdcall, wcaps:dword + mov eax, [wcaps] + and eax, AC_WCAP_TYPE + shr eax, AC_WCAP_TYPE_SHIFT + ret +endp + +;static inline unsigned int get_wcaps_channels(u32 wcaps) +proc get_wcaps_channels stdcall, wcaps:dword +; chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; +; chans = ((chans << 1) | 1) + 1; + mov eax, [wcaps] + and eax, AC_WCAP_CHAN_CNT_EXT + shr eax, 13 + shl eax, 1 + or eax, 1 + inc eax + ret +endp + + +;Asper ] diff --git a/drivers/audio/intel_hda/IMPORTS.INC b/drivers/audio/intel_hda/IMPORTS.INC new file mode 100644 index 0000000000..df1ee70f0c --- /dev/null +++ b/drivers/audio/intel_hda/IMPORTS.INC @@ -0,0 +1,89 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +macro kernel_export [name]{ +forward + if used name + if DEBUG + display 'uses: ',`name,#13,#10 + end if + extrn name + end if +} +; all exported kernel functions and data + + +kernel_export \ + RegService,\ + GetService,\ + ServiceHandler,\ + AttachIntHandler,\ + GetIntHandler,\ + FpuSave,\ + FpuRestore,\ + ReservePortArea,\ + Boot_Log,\ +\ + PciApi,\ + PciRead32,\ + PciRead16,\ + PciRead8,\ + PciWrite8,\ + PciWrite16,\ + PciWrite32,\ +\ + AllocPage,\ + AllocPages,\ + FreePage,\ + MapPage,\ + MapSpace,\ + MapIoMem,\ + GetPgAddr,\ + CommitPages,\ + ReleasePages,\ +\ + AllocKernelSpace,\ + FreeKernelSpace,\ + KernelAlloc,\ + KernelFree,\ + UserAlloc,\ + UserFree,\ + Kmalloc,\ + Kfree,\ + CreateRingBuffer,\ +\ + GetPid,\ + CreateObject,\ + DestroyObject,\ + CreateEvent,\ + RaiseEvent,\ + WaitEvent,\ + DestroyEvent,\ + ClearEvent,\ +\ + LoadCursor,\ + SelectHwCursor,\ + SetHwCursor,\ + HwCursorRestore,\ + HwCursorCreate,\ +\ + SysMsgBoardStr,\ + SysMsgBoardChar,\ + GetCurrentTask,\ + LoadFile,\ + SendEvent,\ + SetMouseData,\ + Sleep,\ + GetTimerTicks,\ +\ + strncat,\ + strncpy,\ + strncmp,\ + strnlen,\ + strchr,\ + strrchr,\ +\ + LFBAddress diff --git a/drivers/audio/intel_hda/PROC32.INC b/drivers/audio/intel_hda/PROC32.INC new file mode 100644 index 0000000000..23c56b03c1 --- /dev/null +++ b/drivers/audio/intel_hda/PROC32.INC @@ -0,0 +1,268 @@ + +; Macroinstructions for defining and calling procedures + +macro stdcall proc,[arg] ; directly call STDCALL procedure + { common + if ~ arg eq + reverse + pushd arg + common + end if + call proc } + +macro invoke proc,[arg] ; indirectly call STDCALL procedure + { common + if ~ arg eq + reverse + pushd arg + common + end if + call [proc] } + +macro ccall proc,[arg] ; directly call CDECL procedure + { common + size@ccall = 0 + if ~ arg eq + reverse + pushd arg + size@ccall = size@ccall+4 + common + end if + call proc + if size@ccall + add esp,size@ccall + end if } + +macro cinvoke proc,[arg] ; indirectly call CDECL procedure + { common + size@ccall = 0 + if ~ arg eq + reverse + pushd arg + size@ccall = size@ccall+4 + common + end if + call [proc] + if size@ccall + add esp,size@ccall + end if } + +macro proc [args] ; define procedure + { common + match name params, args> + \{ define@proc name, \{ prologue name,flag,parmbytes,localbytes,reglist \} + macro locals + \{ virtual at ebp-localbytes+current + macro label . \\{ deflocal@proc .,:, \\} + struc db [val] \\{ \common deflocal@proc .,db,val \\} + struc dw [val] \\{ \common deflocal@proc .,dw,val \\} + struc dp [val] \\{ \common deflocal@proc .,dp,val \\} + struc dd [val] \\{ \common deflocal@proc .,dd,val \\} + struc dt [val] \\{ \common deflocal@proc .,dt,val \\} + struc dq [val] \\{ \common deflocal@proc .,dq,val \\} + struc rb cnt \\{ deflocal@proc .,rb cnt, \\} + struc rw cnt \\{ deflocal@proc .,rw cnt, \\} + struc rp cnt \\{ deflocal@proc .,rp cnt, \\} + struc rd cnt \\{ deflocal@proc .,rd cnt, \\} + struc rt cnt \\{ deflocal@proc .,rt cnt, \\} + struc rq cnt \\{ deflocal@proc .,rq cnt, \\} \} + macro endl + \{ purge label + restruc db,dw,dp,dd,dt,dq + restruc rb,rw,rp,rd,rt,rq + restruc byte,word,dword,pword,tword,qword + current = $-(ebp-localbytes) + end virtual \} + macro ret operand + \{ match any, operand \\{ retn operand \\} + match , operand \\{ match epilogue:reglist, epilogue@proc: + \\\{ epilogue name,flag,parmbytes,localbytes,reglist \\\} \\} \} + macro finish@proc \{ localbytes = (((current-1) shr 2)+1) shl 2 + end if \} } + +macro defargs@proc [arg] + { common + if ~ arg eq + forward + local ..arg,current@arg + match argname:type, arg + \{ current@arg equ argname + label ..arg type + argname equ ..arg + if dqword eq type + dd ?,?,?,? + else if tbyte eq type + dd ?,?,? + else if qword eq type | pword eq type + dd ?,? + else + dd ? + end if \} + match =current@arg,current@arg + \{ current@arg equ arg + arg equ ..arg + ..arg dd ? \} + common + args@proc equ current@arg + forward + restore current@arg + common + end if } + +macro deflocal@proc name,def,[val] + { common + match vars, all@vars \{ all@vars equ all@vars, \} + all@vars equ all@vars name + forward + local ..var,..tmp + ..var def val + match =?, val \{ ..tmp equ \} + match any =dup (=?), val \{ ..tmp equ \} + match tmp : value, ..tmp : val + \{ tmp: end virtual + initlocal@proc ..var,def value + virtual at tmp\} + common + match first rest, ..var, \{ name equ first \} } + +macro initlocal@proc name,def + { virtual at name + def + size@initlocal = $ - name + end virtual + position@initlocal = 0 + while size@initlocal > position@initlocal + virtual at name + def + if size@initlocal - position@initlocal < 2 + current@initlocal = 1 + load byte@initlocal byte from name+position@initlocal + else if size@initlocal - position@initlocal < 4 + current@initlocal = 2 + load word@initlocal word from name+position@initlocal + else + current@initlocal = 4 + load dword@initlocal dword from name+position@initlocal + end if + end virtual + if current@initlocal = 1 + mov byte [name+position@initlocal],byte@initlocal + else if current@initlocal = 2 + mov word [name+position@initlocal],word@initlocal + else + mov dword [name+position@initlocal],dword@initlocal + end if + position@initlocal = position@initlocal + current@initlocal + end while } + +macro endp + { purge ret,locals,endl + finish@proc + purge finish@proc + restore regs@proc + match all,args@proc \{ restore all \} + restore args@proc + match all,all@vars \{ restore all \} } + +macro local [var] + { common + locals + forward done@local equ + match varname[count]:vartype, var + \{ match =BYTE, vartype \\{ varname rb count + restore done@local \\} + match =WORD, vartype \\{ varname rw count + restore done@local \\} + match =DWORD, vartype \\{ varname rd count + restore done@local \\} + match =PWORD, vartype \\{ varname rp count + restore done@local \\} + match =QWORD, vartype \\{ varname rq count + restore done@local \\} + match =TBYTE, vartype \\{ varname rt count + restore done@local \\} + match =DQWORD, vartype \\{ label varname dqword + rq count+count + restore done@local \\} + match , done@local \\{ virtual + varname vartype + end virtual + rb count*sizeof.\#vartype + restore done@local \\} \} + match :varname:vartype, done@local:var + \{ match =BYTE, vartype \\{ varname db ? + restore done@local \\} + match =WORD, vartype \\{ varname dw ? + restore done@local \\} + match =DWORD, vartype \\{ varname dd ? + restore done@local \\} + match =PWORD, vartype \\{ varname dp ? + restore done@local \\} + match =QWORD, vartype \\{ varname dq ? + restore done@local \\} + match =TBYTE, vartype \\{ varname dt ? + restore done@local \\} + match =DQWORD, vartype \\{ label varname dqword + dq ?,? + restore done@local \\} + match , done@local \\{ varname vartype + restore done@local \\} \} + match ,done@local + \{ var + restore done@local \} + common + endl } diff --git a/drivers/audio/intel_hda/hda_generic.inc b/drivers/audio/intel_hda/hda_generic.inc new file mode 100644 index 0000000000..2c2f8d6181 --- /dev/null +++ b/drivers/audio/intel_hda/hda_generic.inc @@ -0,0 +1,936 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 diff --git a/drivers/audio/intel_hda/intel_hda.asm b/drivers/audio/intel_hda/intel_hda.asm new file mode 100644 index 0000000000..92fe2678fa --- /dev/null +++ b/drivers/audio/intel_hda/intel_hda.asm @@ -0,0 +1,2924 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +format MS COFF + +DEBUG equ 1 +FDEBUG equ 1 +DEBUG_IRQ equ 0 + +USE_SINGLE_MODE equ 0 ; 1 = Single mode; 0 = Normal mode. + +TEST_VERSION_NUMBER equ '018b' + +;Asper+ [ +SDO_TAG equ 1 ;Asper: Output stream tag id (any number except 0) +SDO_IDX equ 4 ;Asper: Output stream index +;According to "Intel® I/O Controller Hub 6 (ICH6) High Definition Audio / AC ’97 Programmer’s Reference Manual (PRM) May 2005 Document" +;and "Intel® I/O Controller Hub 6 (ICH6) Family Datasheet" SDO0=4, +;but according to "High Definition Audio Specification Revision 1.0a June 17, 2010" SDO0 depends on the number of SDIs. + +SDO_INT equ 1 shl SDO_IDX ;Asper: Output stream interrupt (must be power of 2) +SDO_OFS equ 0x80+(SDO_IDX*0x20) ;Asper: Output stream offset +;Asper+ ] + +include 'proc32.inc' +include 'imports.inc' +include 'codec_h.inc' + + +CURRENT_API equ 0x0100 ;1.00 +COMPATIBLE_API equ 0x0101 ;1.01 +API_VERSION equ (COMPATIBLE_API shl 16) or CURRENT_API + +IRQ_REMAP equ 0 +IRQ_LINE equ 0 + +CPU_FREQ equ 2600d + +; Vendors +VID_INTEL equ 0x8086 +VID_NVIDIA equ 0x10DE +VID_ATI equ 0x1002 +VID_AMD equ 0x1022 +VID_VIA equ 0x1006 +VID_SIS equ 0x1039 +VID_ULI equ 0x10B9 +VID_CREATIVE equ 0x1102 +VID_TERA equ 0x6549 +VID_RDC equ 0x17F3 +VID_VMWARE equ 0x15AD + +; Devices +; Intel +CTRL_INTEL_SCH2 equ 0x080a +CTRL_INTEL_HPT equ 0x0c0c +CTRL_INTEL_CPT equ 0x1c20 +CTRL_INTEL_PGB equ 0x1d20 +CTRL_INTEL_PPT1 equ 0x1e20 +CTRL_INTEL_82801F equ 0x2668 +CTRL_INTEL_63XXESB equ 0x269a +CTRL_INTEL_82801G equ 0x27d8 +CTRL_INTEL_82801H equ 0x284b +CTRL_INTEL_82801_UNK1 equ 0x2911 +CTRL_INTEL_82801I equ 0x293e +CTRL_INTEL_82801_UNK2 equ 0x293f +CTRL_INTEL_82801JI equ 0x3a3e +CTRL_INTEL_82801JD equ 0x3a6e +CTRL_INTEL_PCH equ 0x3b56 +CTRL_INTEL_PCH2 equ 0x3b57 +CTRL_INTEL_SCH equ 0x811b +CTRL_INTEL_LPT equ 0x8c20 +; Nvidia +CTRL_NVIDIA_MCP51 equ 0x026c +CTRL_NVIDIA_MCP55 equ 0x0371 +CTRL_NVIDIA_MCP61_1 equ 0x03e4 +CTRL_NVIDIA_MCP61_2 equ 0x03f0 +CTRL_NVIDIA_MCP65_1 equ 0x044a +CTRL_NVIDIA_MCP65_2 equ 0x044b +CTRL_NVIDIA_MCP67_1 equ 0x055c +CTRL_NVIDIA_MCP67_2 equ 0x055d +CTRL_NVIDIA_MCP78_1 equ 0x0774 +CTRL_NVIDIA_MCP78_2 equ 0x0775 +CTRL_NVIDIA_MCP78_3 equ 0x0776 +CTRL_NVIDIA_MCP78_4 equ 0x0777 +CTRL_NVIDIA_MCP73_1 equ 0x07fc +CTRL_NVIDIA_MCP73_2 equ 0x07fd +CTRL_NVIDIA_MCP79_1 equ 0x0ac0 +CTRL_NVIDIA_MCP79_2 equ 0x0ac1 +CTRL_NVIDIA_MCP79_3 equ 0x0ac2 +CTRL_NVIDIA_MCP79_4 equ 0x0ac3 +CTRL_NVIDIA_0BE2 equ 0x0be2 +CTRL_NVIDIA_0BE3 equ 0x0be3 +CTRL_NVIDIA_0BE4 equ 0x0be4 +CTRL_NVIDIA_GT100 equ 0x0be5 +CTRL_NVIDIA_GT106 equ 0x0be9 +CTRL_NVIDIA_GT108 equ 0x0bea +CTRL_NVIDIA_GT104 equ 0x0beb +CTRL_NVIDIA_GT116 equ 0x0bee +CTRL_NVIDIA_MCP89_1 equ 0x0d94 +CTRL_NVIDIA_MCP89_2 equ 0x0d95 +CTRL_NVIDIA_MCP89_3 equ 0x0d96 +CTRL_NVIDIA_MCP89_4 equ 0x0d97 +CTRL_NVIDIA_GF119 equ 0x0e08 +CTRL_NVIDIA_GF110_1 equ 0x0e09 +CTRL_NVIDIA_GF110_2 equ 0x0e0c +; ATI +CTRL_ATI_SB450 equ 0x437b +CTRL_ATI_SB600 equ 0x4383 +; ATI HDMI +CTRL_ATI_RS600 equ 0x793b +CTRL_ATI_RS690 equ 0x7919 +CTRL_ATI_RS780 equ 0x960f +CTRL_ATI_RS_UNK1 equ 0x970f +CTRL_ATI_R600 equ 0xaa00 +CTRL_ATI_RV630 equ 0xaa08 +CTRL_ATI_RV610 equ 0xaa10 +CTRL_ATI_RV670 equ 0xaa18 +CTRL_ATI_RV635 equ 0xaa20 +CTRL_ATI_RV620 equ 0xaa28 +CTRL_ATI_RV770 equ 0xaa30 +CTRL_ATI_RV730 equ 0xaa38 +CTRL_ATI_RV710 equ 0xaa40 +CTRL_ATI_RV740 equ 0xaa48 +; AMD +CTRL_AMD_HUDSON equ 0x780d +; VIA +CTRL_VIA_VT82XX equ 0x3288 +CTRL_VIA_VT61XX equ 0x9140 +CTRL_VIA_VT71XX equ 0x9170 +; SiS +CTRL_SIS_966 equ 0x7502 +; ULI +CTRL_ULI_M5461 equ 0x5461 +; Creative +CTRL_CREATIVE_CA0110_IBG equ 0x0009 +CTRL_CREATIVE_SOUND_CORE3D_1 equ 0x0010 +CTRL_CREATIVE_SOUND_CORE3D_2 equ 0x0012 +; Teradici +CTRL_TERA_UNK1 equ 0x1200 +; RDC Semiconductor +CTRL_RDC_R3010 equ 0x3010 +;VMware +CTRL_VMWARE_UNK1 equ 0x1977 + + +; driver types +AZX_DRIVER_ICH equ 0 +AZX_DRIVER_PCH equ 1 +AZX_DRIVER_SCH equ 2 +AZX_DRIVER_ATI equ 3 +AZX_DRIVER_ATIHDMI equ 4 +AZX_DRIVER_VIA equ 5 +AZX_DRIVER_SIS equ 6 +AZX_DRIVER_ULI equ 7 +AZX_DRIVER_NVIDIA equ 8 +AZX_DRIVER_TERA equ 9 +AZX_DRIVER_CTX equ 10 +AZX_DRIVER_GENERIC equ 11 +AZX_NUM_DRIVERS equ 12 + + +; registers + +ICH6_REG_GCAP equ 0x00 +ICH6_REG_VMIN equ 0x02 +ICH6_REG_VMAJ equ 0x03 +ICH6_REG_OUTPAY equ 0x04 +ICH6_REG_INPAY equ 0x06 +ICH6_REG_GCTL equ 0x08 + ICH6_GCTL_RESET equ (1 shl 0) ; controller reset + ICH6_GCTL_FCNTRL equ (1 shl 1) ; flush control + ICH6_GCTL_UNSOL equ (1 shl 8) ; accept unsol. response enable +ICH6_REG_WAKEEN equ 0x0c +ICH6_REG_STATESTS equ 0x0e +ICH6_REG_GSTS equ 0x10 + ICH6_GSTS_FSTS equ (1 shl 1) ; flush status +ICH6_REG_INTCTL equ 0x20 +ICH6_REG_INTSTS equ 0x24 +ICH6_REG_WALLCLK equ 0x30 ; 24Mhz source +ICH6_REG_OLD_SSYNC equ 0x34 ; SSYNC for old ICH +ICH6_REG_SSYNC equ 0x38 +ICH6_REG_CORBLBASE equ 0x40 +ICH6_REG_CORBUBASE equ 0x44 +ICH6_REG_CORBWP equ 0x48 +ICH6_REG_CORBRP equ 0x4A + ICH6_CORBRP_RST equ (1 shl 15) ; read pointer reset +ICH6_REG_CORBCTL equ 0x4c + ICH6_CORBCTL_RUN equ (1 shl 1) ; enable DMA + ICH6_CORBCTL_CMEIE equ (1 shl 0) ; enable memory error irq +ICH6_REG_CORBSTS equ 0x4d + ICH6_CORBSTS_CMEI equ (1 shl 0) ; memory error indication +ICH6_REG_CORBSIZE equ 0x4e + +ICH6_REG_RIRBLBASE equ 0x50 +ICH6_REG_RIRBUBASE equ 0x54 +ICH6_REG_RIRBWP equ 0x58 + ICH6_RIRBWP_RST equ (1 shl 15) ; write pointer reset +ICH6_REG_RINTCNT equ 0x5a +ICH6_REG_RIRBCTL equ 0x5c + ICH6_RBCTL_IRQ_EN equ (1 shl 0) ; enable IRQ + ICH6_RBCTL_DMA_EN equ (1 shl 1) ; enable DMA + ICH6_RBCTL_OVERRUN_EN equ (1 shl 2) ; enable overrun irq +ICH6_REG_RIRBSTS equ 0x5d + ICH6_RBSTS_IRQ equ (1 shl 0) ; response irq + ICH6_RBSTS_OVERRUN equ (1 shl 2) ; overrun irq +ICH6_REG_RIRBSIZE equ 0x5e + +ICH6_REG_IC equ 0x60 +ICH6_REG_IR equ 0x64 +ICH6_REG_IRS equ 0x68 + ICH6_IRS_VALID equ 2 + ICH6_IRS_BUSY equ 1 + +ICH6_REG_DPLBASE equ 0x70 +ICH6_REG_DPUBASE equ 0x74 + ICH6_DPLBASE_ENABLE equ 1 ; Enable position buffer + +; SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ +SDI0_SD_OFFSET equ 0x80 +SDI1_SD_OFFSET equ 0xA0 +SDI2_SD_OFFSET equ 0xC0 +SDI3_SD_OFFSET equ 0xE0 +SDO0_SD_OFFSET equ 0x100 +SDO1_SD_OFFSET equ 0x120 +SDO2_SD_OFFSET equ 0X140 +SDO3_SD_OFFSET equ 0x160 + +; stream register offsets from stream base +ICH6_REG_SD_CTL equ 0x00 +ICH6_REG_SD_STS equ 0x03 +ICH6_REG_SD_LPIB equ 0x04 +ICH6_REG_SD_CBL equ 0x08 +ICH6_REG_SD_LVI equ 0x0c +ICH6_REG_SD_FIFOW equ 0x0e +ICH6_REG_SD_FIFOSIZE equ 0x10 +ICH6_REG_SD_FORMAT equ 0x12 +ICH6_REG_SD_BDLPL equ 0x18 +ICH6_REG_SD_BDLPU equ 0x1c + +; PCI space +ICH6_PCIREG_TCSEL equ 0x44 + +; other constants +ICH6_RIRB_EX_UNSOL_EV equ (1 shl 4) + +; max number of SDs +MAX_ICH6_DEV equ 8 +; max number of fragments - we may use more if allocating more pages for BDL +AZX_MAX_FRAG equ (4096 / (MAX_ICH6_DEV * 16)) +; max buffer size - no h/w limit, you can increase as you like +AZX_MAX_BUF_SIZE equ (1024*1024*1024) +; max number of PCM devices per card +AZX_MAX_PCMS equ 8 + +; RIRB int mask: overrun[2], response[0] +RIRB_INT_RESPONSE equ 0x01 +RIRB_INT_OVERRUN equ 0x04 +RIRB_INT_MASK equ 0x05 + +; STATESTS int mask: SD2,SD1,SD0 +STATESTS_INT_MASK equ 0x07 +AZX_MAX_CODECS equ 4 + +; SD_CTL bits +SD_CTL_STREAM_RESET equ 0x01 ; stream reset bit +SD_CTL_DMA_START equ 0x02 ; stream DMA start bit +SD_CTL_STREAM_TAG_MASK equ (0xf shl 20) +SD_CTL_STREAM_TAG_SHIFT equ 20 + +; SD_CTL and SD_STS +SD_INT_DESC_ERR equ 0x10 ; descriptor error interrupt +SD_INT_FIFO_ERR equ 0x08 ; FIFO error interrupt +SD_INT_COMPLETE equ 0x04 ; completion interrupt +SD_INT_MASK equ (SD_INT_DESC_ERR or SD_INT_FIFO_ERR or SD_INT_COMPLETE) + +; SD_STS +SD_STS_FIFO_READY equ 0x20 ; FIFO ready + +; INTCTL and INTSTS +ICH6_INT_ALL_STREAM equ 0xff ; all stream interrupts +ICH6_INT_CTRL_EN equ 0x40000000 ; controller interrupt enable bit +ICH6_INT_GLOBAL_EN equ 0x80000000 ; global interrupt enable bit + +; GCTL reset bit +ICH6_GCTL_RESET equ 1 + +; CORB/RIRB control, read/write pointer +ICH6_RBCTL_DMA_EN equ 0x02 ; enable DMA +ICH6_RBCTL_IRQ_EN equ 0x01 ; enable IRQ +ICH6_RBRWP_CLR equ 0x8000 ; read/write pointer clear +; below are so far hardcoded - should read registers in future +ICH6_MAX_CORB_ENTRIES equ 256 +ICH6_MAX_RIRB_ENTRIES equ 256 + +; position fix mode + POS_FIX_AUTO equ 0 + POS_FIX_LPIB equ 1 + POS_FIX_POSBUF equ 2 + POS_FIX_VIACOMBO equ 4 + POS_FIX_COMBO equ 8 + +; Defines for ATI HD Audio support in SB450 south bridge +ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR equ 0x42 +ATI_SB450_HDAUDIO_ENABLE_SNOOP equ 0x02 + +; Defines for Nvidia HDA support +NVIDIA_HDA_TRANSREG_ADDR equ 0x4e +NVIDIA_HDA_ENABLE_COHBITS equ 0x0f +NVIDIA_HDA_ISTRM_COH equ 0x4d +NVIDIA_HDA_OSTRM_COH equ 0x4c +NVIDIA_HDA_ENABLE_COHBIT equ 0x01 + +; Defines for Intel SCH HDA snoop control +INTEL_SCH_HDA_DEVC equ 0x78 +INTEL_SCH_HDA_DEVC_NOSNOOP equ (0x1 shl 11) + +; Define IN stream 0 FIFO size offset in VIA controller +VIA_IN_STREAM0_FIFO_SIZE_OFFSET equ 0x90 +; Define VIA HD Audio Device ID +VIA_HDAC_DEVICE_ID equ 0x3288 + +; HD Audio class code +PCI_CLASS_MULTIMEDIA_HD_AUDIO equ 0x0403 + + +SRV_GETVERSION equ 0 +DEV_PLAY equ 1 +DEV_STOP equ 2 +DEV_CALLBACK equ 3 +DEV_SET_BUFF equ 4 +DEV_NOTIFY equ 5 +DEV_SET_MASTERVOL equ 6 +DEV_GET_MASTERVOL equ 7 +DEV_GET_INFO equ 8 +DEV_GET_POS equ 9 +DEV_SET_CHANNEL_VOLUME equ 10 +DEV_GET_CHANNEL_VOLUME equ 11 +;Asper: Non standard system service. For the tests only! [ +DEV_EXEC_CODEC_CMD equ 100 +;Asper: Non standard system service. For the tests only! ] + +struc AC_CNTRL ;AC controller base class +{ .bus dd ? + .devfn dd ? + + .vendor dw ? + .dev_id dw ? + .pci_cmd dd ? + .pci_stat dd ? + + .ctrl_io_base dd ? + .ctrl_mem_base dd ? + .cfg_reg dd ? + .int_line dd ? + + .vendor_ids dd ? ;vendor id string + .ctrl_ids dd ? ;hub id string + + .buffer dd ? + + .notify_pos dd ? + .notify_task dd ? + + .lvi_reg dd ? + .civ_val dd 1 + .user_callback dd ? + + .ctrl_read8 dd ? + .ctrl_read16 dd ? + .ctrl_read32 dd ? + + .ctrl_write8 dd ? + .ctrl_write16 dd ? + .ctrl_write32 dd ? + +;Asper+ [ + .codec_mask dd ? + .rb dd ? + .rirb_rp dw 0 + .rirb_wp dw 0 + .corb_rp dw 0 + .corb_wp dw 0 + .rirb_cmd dd 0 + .rirb_res dd 0 + .rirb_error dd 0 + .response_reset dd 0 + .polling_mode db 0 + .poll_count db 0 + .posbuf dd ? + .start_wallclk dd ? ; start + minimum wallclk + .period_wallclk dd ? ; wallclk for period + .position_fix db ? +;Asper+ ] +} + +struc CODEC ;Audio Chip base class +{ +;Asper+ [ + .addr dd ? ; codec slot index (codec address) + .afg dd ? ; AFG node id + .mfg dd ? ; MFG node id + + .function_id dd ? + .subsystem_id dd ? + .revision_id dd ? + .chip_id dw ? + .vendor_id dw ? + + ; widget capabilities cache + .num_nodes dw ? + .start_nid dw ? + .wcaps dd ? + + .init_pins dd ? ; initial (BIOS) pin configurations + .num_pins dd ? ;Asper + : word is enough, but for align... + .beeper_nid dw ? + .pad dw ? +;Asper+ ] + + .ac_vendor_ids dd ? ;ac vendor id string + .chip_ids dd ? ;chip model string +} + +struc CTRL_INFO +{ .pci_cmd dd ? + .irq dd ? + .glob_cntrl dd ? + .glob_sta dd ? + .codec_io_base dd ? + .ctrl_io_base dd ? + .codec_mem_base dd ? + .ctrl_mem_base dd ? + .codec_id dd ? +} + +struc IOCTL +{ .handle dd ? + .io_code dd ? + .input dd ? + .inp_size dd ? + .output dd ? + .out_size dd ? +} + +virtual at 0 + IOCTL IOCTL +end virtual + +EVENT_NOTIFY equ 0x00000200 + +public START +public service_proc +public version + +section '.flat' code readable align 16 + +proc START stdcall, state:dword + + cmp [state], 1 + jne .stop + + if DEBUG + mov esi, msgTV + call SysMsgBoardStr + + mov esi, msgInit + call SysMsgBoardStr + end if + + call detect_controller + test eax, eax + jz .fail + + mov esi,[ctrl.vendor_ids] + call SysMsgBoardStr + mov esi, [ctrl.ctrl_ids] + call SysMsgBoardStr + + call init_controller + test eax, eax + jz .fail + +;Asper This part is from "azx_create" proc. [ + ;(...) + mov [ctrl.position_fix], POS_FIX_LPIB + cmp [driver_type], AZX_DRIVER_VIA + je .set_via_patch + cmp [driver_type], AZX_DRIVER_ATI + jne .no_via_patch + .set_via_patch: + or [ctrl.position_fix], POS_FIX_VIACOMBO + .no_via_patch: + ; codec detection + mov eax, [ctrl.codec_mask] + test eax, eax + jnz @f + if DEBUG + mov esi, msgNoCodecsFound + jmp .fail_msg + else + jmp .fail + end if + @@: +;Asper ] + + mov esi, msgPrimBuff + call SysMsgBoardStr + call create_primary_buff + mov esi, msgDone + call SysMsgBoardStr + + if IRQ_REMAP + pushf + cli + + mov ebx, [ctrl.int_line] + in al, 0xA1 + mov ah, al + in al, 0x21 + test ebx, ebx + jz .skip + bts ax, bx ;mask old line +.skip + bts ax, IRQ_LINE ;mask new ine + out 0x21, al + mov al, ah + out 0xA1, al + ;remap IRQ + stdcall PciWrite8, 0, 0xF8, 0x61, IRQ_LINE + + mov dx, 0x4d0 ;8259 ELCR1 + in al, dx + bts ax, IRQ_LINE + out dx, al ;set level-triggered mode + mov [ctrl.int_line], IRQ_LINE + popf + mov esi, msgRemap + call SysMsgBoardStr + end if + + mov ebx, [ctrl.int_line] + stdcall AttachIntHandler, ebx, hda_irq, dword 0 + +;Asper This part is from "azx_probe" proc. [ + call azx_codec_create + cmp eax, 0 + jl .fail + + call azx_codec_configure + cmp eax, 0 + jl .fail +;] Asper + + ; create PCM streams +;Asper+ [ + mov eax, [spec.dac_node] + if DEBUG + push eax esi + mov esi, msgVal + call SysMsgBoardStr + stdcall fdword2str, 3 + call SysMsgBoardStr + pop esi eax + end if + + test eax, eax + jz .fail + mov ebx, [spec.dac_node+4] + if DEBUG + push eax esi + mov esi, msgVal + call SysMsgBoardStr + mov eax, [spec.dac_node+4] + stdcall fdword2str, 3 + call SysMsgBoardStr + pop esi eax + end if + + test ebx, ebx + jz @f + cmp eax, ebx + je @f + stdcall hda_codec_setup_stream, ebx, SDO_TAG, 0, 0x11 ; Left & Right channels (Front panel) + @@: + stdcall hda_codec_setup_stream, eax, SDO_TAG, 0, 0x11 ; Left & Right channels (Back panel) +;Asper+ ] + + if USE_SINGLE_MODE + mov esi, msgSingleMode + call SysMsgBoardStr + else + mov esi, msgNormalMode + call SysMsgBoardStr + end if + + +.reg: + stdcall RegService, sz_sound_srv, service_proc + ret +.fail: + if DEBUG + mov esi, msgFail + call SysMsgBoardStr + end if + xor eax, eax + ret +.fail_msg: + call SysMsgBoardStr + xor eax, eax + ret +.stop: + call stop + xor eax, eax + ret +endp + +handle equ IOCTL.handle +io_code equ IOCTL.io_code +input equ IOCTL.input +inp_size equ IOCTL.inp_size +output equ IOCTL.output +out_size equ IOCTL.out_size + +align 4 +proc service_proc stdcall, ioctl:dword + mov edi, [ioctl] + mov eax, [edi+io_code] + + cmp eax, SRV_GETVERSION + jne @F + + mov eax, [edi+output] + cmp [edi+out_size], 4 + jne .fail + + mov [eax], dword API_VERSION + xor eax, eax + ret +@@: + cmp eax, DEV_PLAY + jne @F + if DEBUG + mov esi, msgPlay + call SysMsgBoardStr + end if + call play + xor eax, eax + ret +@@: + cmp eax, DEV_STOP + jne @F + if DEBUG + mov esi, msgStop + call SysMsgBoardStr + end if + call stop + xor eax, eax + ret +@@: + cmp eax, DEV_CALLBACK + jne @F + mov ebx, [edi+input] + stdcall set_callback, [ebx] + xor eax, eax + ret +@@: + cmp eax, DEV_SET_MASTERVOL + jne @F + mov eax, [edi+input] + mov eax, [eax] + call set_master_vol + xor eax, eax + ret +@@: + cmp eax, DEV_GET_MASTERVOL + jne @F + mov ebx, [edi+output] + stdcall get_master_vol, ebx + xor eax, eax + ret +;@@: +; cmp eax, DEV_GET_INFO +; jne @F +; mov ebx, [edi+output] +; stdcall get_dev_info, ebx +; xor eax, eax +; ret +@@: + cmp eax, DEV_GET_POS + jne @F + stdcall azx_get_position + shr eax, 2 + mov ebx, [edi+output] + mov [ebx], eax + xor eax, eax + ret +@@: +; cmp eax, DEV_SET_CHANNEL_VOLUME +; jne @F +; if DEBUG +; mov esi, msgSetChannelVolume +; call SysMsgBoardStr +; end if +; mov ebx, [edi+input] +; mov cl, byte [ebx] ; cl=channel +; mov eax, dword [ebx+1] ; eax=volume in Db +; if DEBUG +; push eax esi +; mov esi, msgYAHOO1 +; call SysMsgBoardStr +; stdcall fdword2str, 1 +; call SysMsgBoardStr +; mov esi, strSemicolon +; call SysMsgBoardStr +; movzx eax, cl +; stdcall fdword2str, 3 +; call SysMsgBoardStr +; pop esi eax +; end if +; +; call set_channel_volume +; xor eax, eax +; ret +;@@: +; cmp eax, DEV_GET_CHANNEL_VOLUME +; jne @F +; mov cl, byte [edi+input] ; cl=channel +; call get_channel_volume +; mov ebx, [edi+output] +; mov [ebx], eax +; xor eax, eax +; ret +;@@: + +;Asper: Non standard system service. For the tests only! [ +@@: + cmp eax, DEV_EXEC_CODEC_CMD + jne @f + + mov eax, [edi+input] + mov eax, [eax] + stdcall codec_exec_verb, eax + xor eax, eax + ret +@@: +;Asper: Non standard system service. For the tests only! ] + + +.fail: + or eax, -1 + ret +endp + +restore handle +restore io_code +restore input +restore inp_size +restore output +restore out_size + + +align 4 +proc hda_irq ;+ + if DEBUG_IRQ + push eax esi + ;mov esi, msgIRQ + ;call SysMsgBoardStr + call GetTimerTicks + stdcall fdword2str, 2 + call SysMsgBoardStr + pop esi eax + end if + mov edx, ICH6_REG_INTSTS + call azx_readl + test eax, eax + jnz @f + ret + @@: + mov ebx, eax ; status + mov eax, SDO_INT + test ebx, eax + jz @f + + mov edx, ICH6_REG_SD_STS + SDO_OFS + call azx_readb + mov bl, al + + mov al, SD_INT_MASK + mov edx, ICH6_REG_SD_STS + SDO_OFS + call azx_writeb + + test bl, SD_INT_COMPLETE + jz @f + + mov eax, [ctrl.civ_val] + inc eax + and eax, 4-1 ;2-1 + mov [ctrl.civ_val], eax + + mov ebx, dword [buff_list+eax*4] + cmp [ctrl.user_callback], 0 + je @f + stdcall [ctrl.user_callback], ebx + + @@: + + ; clear rirb int + mov edx, ICH6_REG_RIRBSTS + call azx_readb + + test al, RIRB_INT_MASK + jz .l1 + test al, RIRB_INT_RESPONSE + jz .l2 + + cmp byte [driver_type], AZX_DRIVER_CTX + jne @f + mov eax, 80 ; wait 80 us + call StallExec + @@: + + call azx_update_rirb + .l2: + mov al, RIRB_INT_MASK + mov edx, ICH6_REG_RIRBSTS + call azx_writeb + .l1: + +;if 0 + ; clear state status int + mov edx, ICH6_REG_STATESTS + call azx_readb + test al, 0x04 + jz @f + + mov al, 0x04 + mov edx, ICH6_REG_STATESTS + call azx_writeb + @@: +;end if + or eax, 1 + ret +endp + + +align 4 +proc create_primary_buff + + stdcall KernelAlloc, 4096 + mov [ctrl.posbuf], eax + + stdcall KernelAlloc, 0x10000 ;0x8000 + mov [ctrl.buffer], eax + + mov edi, eax + mov ecx, 0x10000/4 ;0x8000/4 + xor eax, eax + cld + rep stosd + + + stdcall KernelAlloc, 4096 + mov [pcmout_bdl], eax + + mov edi, eax + mov ecx, 4096/4 + xor eax, eax + cld + rep stosd + + + ; reset BDL address + xor eax, eax + mov edx, ICH6_REG_SD_BDLPL + SDO_OFS + call azx_writel + xor eax, eax + mov edx, ICH6_REG_SD_BDLPU + SDO_OFS + call azx_writel + + ; program the initial BDL entries + mov eax, [ctrl.buffer] + mov ebx, eax + call GetPgAddr + and ebx, 0xFFF + add eax, ebx + + + mov ebx, 0x4000 ;buffer size + mov ecx, 8 ;number of periods + mov edi, [pcmout_bdl] ;pcmout_bdl + .next_period: + push eax ecx + mov ecx, 4 ;2 ;number of bdl in a period + .next_bdl: + ; program the address field of the BDL entry + mov dword [edi], eax + mov dword [edi+4], 0 + ; program the size field of the BDL entry + mov dword [edi+8], ebx + ; program the IOC to enable interrupt when buffer completes + mov dword [edi+12], 0x01 + + + add eax, ebx + add edi, 16 + dec ecx + jnz .next_bdl + + pop ecx eax + dec ecx + jnz .next_period + + + mov edi, buff_list + mov eax, [ctrl.buffer] + mov ecx, 4 ;2 +@@: + mov [edi], eax + mov [edi+8], eax + mov [edi+16], eax + mov [edi+24], eax + mov [edi+32], eax + mov [edi+40], eax + mov [edi+48], eax + mov [edi+56], eax + + add eax, ebx + add edi, 4 + loop @B + + ; wallclk has 24Mhz clock source + mov [ctrl.period_wallclk], ((0x4000 * 24000) / 48000) * 1000 + + ;-call azx_stream_reset + call azx_setup_controller + ret +endp + +align 4 +proc detect_controller + locals + last_bus dd ? + bus dd ? + devfn dd ? + endl + + xor eax, eax + mov [bus], eax + inc eax + call PciApi + cmp eax, -1 + je .err + + mov [last_bus], eax + + .next_bus: + and [devfn], 0 + .next_dev: + stdcall PciRead32, [bus], [devfn], dword 0 + test eax, eax + jz .next + cmp eax, -1 + je .next + + mov edi, devices + @@: + mov ebx, [edi] + test ebx, ebx + jz .next + + cmp eax, ebx + je .found + add edi, 12 + jmp @B + .next: + inc [devfn] + cmp [devfn], 256 + jb .next_dev + mov eax, [bus] + inc eax + mov [bus], eax + cmp eax, [last_bus] + jna .next_bus + xor eax, eax + ret + .found: + mov ebx, [bus] + mov [ctrl.bus], ebx + + mov ecx, [devfn] + mov [ctrl.devfn], ecx + + mov edx, eax + and edx, 0xFFFF + mov [ctrl.vendor], dx + shr eax, 16 + mov [ctrl.dev_id], ax + + mov ebx, [edi+4] + mov [ctrl.ctrl_ids], ebx + + cmp edx, VID_INTEL + jne @F + mov [ctrl.vendor_ids], msg_Intel + jmp .ok + @@: + cmp edx, VID_NVIDIA + jne @F + mov [ctrl.vendor_ids], msg_NVidia + jmp .ok + @@: + cmp edx, VID_ATI + jne @F + cmp eax, 0x4383 + jg .ati_hdmi + mov [ctrl.vendor_ids], msg_ATI + jmp .ok + .ati_hdmi: + mov [ctrl.vendor_ids], msg_ATI_HDMI + jmp .ok + @@: + cmp edx, VID_AMD + jne @F + mov [ctrl.vendor_ids], msg_AMD + jmp .ok + @@: + cmp edx, VID_VIA + jne @F + mov [ctrl.vendor_ids], msg_VIA + jmp .ok + @@: + cmp edx, VID_SIS + jne @F + mov [ctrl.vendor_ids], msg_SIS + jmp .ok + @@: + cmp edx, VID_ULI + jne @F + mov [ctrl.vendor_ids], msg_ULI + jmp .ok + @@: + cmp edx, VID_TERA + jne @F + mov [ctrl.vendor_ids], msg_TERA + jmp .ok + @@: + cmp edx, VID_CREATIVE + jne @F + mov [ctrl.vendor_ids], msg_CREATIVE + jmp .ok + @@: + cmp edx, VID_RDC + jne @F + mov [ctrl.vendor_ids], msg_RDC + jmp .ok + @@: + cmp edx, VID_VMWARE + jne @F + mov [ctrl.vendor_ids], msg_VMWARE + jmp .ok + @@: + .err: + xor eax, eax + mov [ctrl.vendor_ids], eax ;something wrong ? + mov [driver_type], -1 + ret + .ok: + mov ebx, [edi+8] + mov [driver_type], ebx + ret +endp + +align 4 +proc init_controller + + stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 4 + test eax, 0x4 ; Test Master bit + jnz @f + or eax, 0x4 ; Set Master bit + stdcall PciWrite32, [ctrl.bus], [ctrl.devfn], dword 4, eax + stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 4 + @@: + + mov ebx, eax + and eax, 0xFFFF + mov [ctrl.pci_cmd], eax + shr ebx, 16 + mov [ctrl.pci_stat], ebx + + mov esi, msgPciCmd + call SysMsgBoardStr + stdcall fdword2str, 2 + call SysMsgBoardStr + + mov esi, msgPciStat + call SysMsgBoardStr + mov eax, [ctrl.pci_stat] + stdcall fdword2str, 2 + call SysMsgBoardStr + + mov esi, msgHDALowMMIo + call SysMsgBoardStr + stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x10 + stdcall fdword2str, 2 + call SysMsgBoardStr + + and eax, 0xFFFFC000 + mov [ctrl.ctrl_mem_base], eax + + mov esi, msgHDAUpMMIo + call SysMsgBoardStr + stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x14 + ;-mov [ctrl.hda_upper_mem_base], eax + stdcall fdword2str, 2 + call SysMsgBoardStr + + .default: + stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x3C + and eax, 0xFF + @@: + mov [ctrl.int_line], eax + mov [ctrl.user_callback], 0 + + call set_HDA +;Asper This is from "azx_create" proc. [ + xor eax, eax + mov edx, ICH6_REG_GCAP + call azx_readw + if DEBUG + mov esi, msgGCap + call SysMsgBoardStr + stdcall fdword2str, 2 + call SysMsgBoardStr + end if + + ; allocate CORB/RIRB + call azx_alloc_cmd_io + + ; initialize chip + call azx_init_pci + + xor eax, eax + call azx_init_chip +;] Asper + + xor eax, eax + inc eax + ret +endp + + + + +PG_SW equ 0x003 +PG_NOCACHE equ 0x018 + +align 4 +proc set_HDA + + stdcall MapIoMem,[ctrl.ctrl_mem_base],0x1000,PG_SW+PG_NOCACHE + mov [ctrl.ctrl_mem_base], eax + ret +endp + + +; in: eax - fullreset_flag +; +; reset codec link +align 4 +proc reset_controller + locals + counter dd ? + endl + + test eax, eax + jz .skip + + ; clear STATESTS + mov eax, STATESTS_INT_MASK + mov edx, ICH6_REG_STATESTS + call azx_writeb + + ; reset controller + mov edx, ICH6_REG_GCTL + call azx_readl + mov ebx, ICH6_GCTL_RESET + xor ebx, -1 + and eax, ebx + mov edx, ICH6_REG_GCTL + call azx_writel + + mov [counter], 50 ; total 50*100 us = 0.5s + .wait0: + + mov edx, ICH6_REG_GCTL + call azx_readb + test eax, eax + jz @f + + mov eax, 100 ; wait 100 us + call StallExec + + dec [counter] + jnz .wait0 + @@: + ; delay for >= 100us for codec PLL to settle per spec + ; Rev 0.9 section 5.5.1 + mov eax, 100 ; wait 100 us + call StallExec + + ; Bring controller out of reset + mov edx, ICH6_REG_GCTL + call azx_readb + or eax, ICH6_GCTL_RESET + mov edx, ICH6_REG_GCTL + call azx_writeb + + mov [counter], 50 ; total 50*100 us = 0.5s + .wait1: + + mov edx, ICH6_REG_GCTL + call azx_readb + test eax, eax + jnz @f + + mov eax, 100 ; wait 100 us + call StallExec + + dec [counter] + jnz .wait1 + @@: + + ; Brent Chartrand said to wait >= 540us for codecs to intialize + mov eax, 540 ; wait 540 us + call StallExec + + .skip: + ; check to see if controller is ready + mov edx, ICH6_REG_GCTL + call azx_readb + test eax, eax + jz .fail + + ; Accept unsolicited responses + if USE_SINGLE_MODE + else +;UNSUPPORTED YET! [ +; mov edx, ICH6_REG_GCTL +; call azx_readl +; or eax, ICH6_GCTL_UNSOL +; mov edx, ICH6_REG_GCTL +; call azx_writel +;UNSUPPORTED YET! ] + end if + + ; detect codecs + mov eax, [ctrl.codec_mask] + test ax, ax + jnz @f + + mov edx, ICH6_REG_STATESTS + call azx_readw + mov [ctrl.codec_mask], eax + + if DEBUG + mov esi, msgCodecMask + call SysMsgBoardStr + stdcall fdword2str, 2 + call SysMsgBoardStr + end if + + @@: + + .ok: + clc + ret + .fail: + if DEBUG + mov esi, msgHDARFail + call SysMsgBoardStr + end if + stc + ret +endp + + +align 4 +play: + mov edx, ICH6_REG_WALLCLK + call azx_readl + mov [ctrl.start_wallclk], eax + + call azx_stream_start + xor eax, eax + ret + +align 4 +stop: +;* call azx_stream_stop ;Asper: Hangs system +;R push ebx ecx edx +;R ; stop DMA +;R mov edx, ICH6_REG_SD_CTL +;R call azx_sd_readb +;R mov bl, SD_CTL_DMA_START or SD_INT_MASK +;R xor bl, -1 +;R and al, bl +;R mov edx, ICH6_REG_SD_CTL +;R call azx_sd_writeb +;R mov edx, ICH6_REG_SD_STS +;R mov al, SD_INT_MASK +;R call azx_sd_writeb ; to be sure + ; disable SIE +;N mov edx, ICH6_REG_INTCTL +;N call azx_readb +;N mov bl, SDO_INT ;shl azx_dev->index +;N xor bl, -1 +;N and al, bl +;N mov edx, ICH6_REG_INTCTL +;N call azx_writeb + + ; int timeout = 5000; + ; while (azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START && --timeout) ; +;Asper: Hangs system [ +;* mov ecx, 5000 +;* .l1: +;* mov edx, ICH6_REG_SD_CTL +;* call azx_sd_readb +;* test al, SD_CTL_DMA_START +;* jz @f +;* dec ecx +;* jnz .l1 +;* @@: +;* +;* pop edx ecx ebx +;Asper ] + + xor eax, eax + ret + +;align 4 +;proc get_dev_info stdcall, p_info:dword +; virtual at esi +; CTRL_INFO CTRL_INFO +; end virtual +; +; mov esi, [p_info] +; mov eax, [ctrl.int_line] +; mov bx, [ctrl.dev_id] +; shl ebx, 16 +; and bx, [ctrl.vendor] +; mov ecx, [ctrl.pci_cmd] +; mov edx, [ctrl.codec_mem_base] ;[ctrl.hda_lower_mem_base] +; mov edi, [ctrl.ctrl_mem_base] ;[ctrl.hda_upper_mem_base] +; +; mov [CTRL_INFO.irq], eax +; mov [CTRL_INFO.codec_id], ebx +; mov [CTRL_INFO.pci_cmd], ecx +; mov [CTRL_INFO.codec_mem_base], edx +; mov [CTRL_INFO.ctrl_mem_base], edi +; +; xor eax, eax +; mov [CTRL_INFO.codec_io_base], eax +; mov [CTRL_INFO.ctrl_io_base], eax +; mov [CTRL_INFO.glob_cntrl], eax +; mov [CTRL_INFO.glob_sta], eax +; ret +;endp + +align 4 +proc set_callback stdcall, handler:dword + mov eax, [handler] + mov [ctrl.user_callback], eax + ret +endp + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Interface for HD codec ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; CORB / RIRB interface ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;; + +proc azx_alloc_cmd_io + push eax ecx edx + ; single page (at least 4096 bytes) must suffice for both ringbuffers + stdcall KernelAlloc, 4096 + mov [ctrl.rb], eax + + mov edi, eax + mov ecx, 4096/4 + xor eax, eax + cld + rep stosd + + pop edx ecx eax + ret +endp + +proc azx_init_cmd_io + pusha + ; CORB set up + mov eax, [ctrl.rb] + mov ebx, eax + call GetPgAddr + and ebx, 0xFFF + add eax, ebx + push eax ; save corb address + mov edx, ICH6_REG_CORBLBASE + call azx_writel + xor eax, eax + mov edx, ICH6_REG_CORBUBASE + call azx_writel + + ; set the corb size to 256 entries (ULI requires explicitly) + mov al, 0x02 + mov edx, ICH6_REG_CORBSIZE + call azx_writeb + ; set the corb write pointer to 0 + xor ax, ax + mov edx, ICH6_REG_CORBWP + call azx_writew + ; reset the corb hw read pointer + mov ax, ICH6_CORBRP_RST + mov edx, ICH6_REG_CORBRP + call azx_writew + ; enable corb dma + mov al, ICH6_CORBCTL_RUN + mov edx, ICH6_REG_CORBCTL + call azx_writeb + + ; RIRB set up + mov [ctrl.rirb_rp], 0 + mov [ctrl.rirb_wp], 0 + mov [ctrl.rirb_cmd], 0 + + pop eax ; restore corb address + add eax, 2048 + mov edx, ICH6_REG_RIRBLBASE + call azx_writel + xor eax, eax + mov edx, ICH6_REG_RIRBUBASE + call azx_writel + + ; set the rirb size to 256 entries (ULI requires explicitly) + mov al, 0x02 + mov edx, ICH6_REG_RIRBSIZE + call azx_writeb + ; reset the rirb hw write pointer + mov ax, ICH6_RIRBWP_RST + mov edx, ICH6_REG_RIRBWP + call azx_writew + ; set N=1, get RIRB response interrupt for new entry + xor ax, ax + cmp byte [driver_type], AZX_DRIVER_CTX + jne @f + mov ax, 0xC0-1 + @@: + inc ax + mov edx, ICH6_REG_RINTCNT + call azx_writew + ; enable rirb dma and response irq + mov al, ICH6_RBCTL_DMA_EN or ICH6_RBCTL_IRQ_EN + mov edx, ICH6_REG_RIRBCTL + call azx_writeb + + popa + ret +endp + +proc azx_free_cmd_io + push eax edx + ; disable ringbuffer DMAs + xor al, al + mov edx, ICH6_REG_RIRBCTL + call azx_writeb + mov edx, ICH6_REG_CORBCTL + call azx_writeb + pop edx eax + ret +endp + + +; send a command +proc azx_corb_send_cmd stdcall, val:dword + push edx edi + xor eax, eax + ; add command to corb + mov edx, ICH6_REG_CORBWP + call azx_readb + inc al + inc dword [ctrl.rirb_cmd] + mov edi, dword [ctrl.rb] + + push eax + shl eax, 2 ;wp=wp*sizeof(corb entry)=wp*4 + add edi, eax + mov eax, dword [val] + stosd + pop eax + mov edx, ICH6_REG_CORBWP + call azx_writel + + pop edi edx + xor eax, eax ;Asper+ + ret +endp + + +; retrieve RIRB entry - called from interrupt handler +proc azx_update_rirb + pusha + xor eax, eax + mov edx, ICH6_REG_RIRBWP + call azx_readb ;call azx_readw + + cmp ax, [ctrl.rirb_wp] + je .done + mov [ctrl.rirb_wp], ax + mov bx, [ctrl.rirb_rp] + + .l1: + cmp bx, [ctrl.rirb_wp] + je .l3 + + inc bl + .l2: + cmp bx, ICH6_MAX_RIRB_ENTRIES + jl @f + sub bx, ICH6_MAX_RIRB_ENTRIES + jmp .l2 + @@: + + movzx edx, bx + shl edx, 1 + 2 ; an RIRB entry is 8-bytes + mov esi, dword [ctrl.rb] + add esi, 2048 + add esi, edx + lodsd ; res + mov edx, eax + lodsd ; res_ex + + test eax, ICH6_RIRB_EX_UNSOL_EV + jz @f + stdcall snd_hda_queue_unsol_event, edx, eax + jmp .l1 + @@: + mov ecx, [ctrl.rirb_cmd] + test ecx, ecx + jz @f + mov [ctrl.rirb_res], edx + dec dword [ctrl.rirb_cmd] + jmp .l1 + @@: + if DEBUG + push esi + mov esi, msgSpuriousResponce + call SysMsgBoardStr + pop esi + end if + jmp .l1 + .l3: + mov [ctrl.rirb_rp], bx + .done: + popa + ret +endp + +; receive a response +proc azx_rirb_get_response + locals + do_poll db 0 + endl + + push ebx ecx edx + .again: + mov ecx, 1000;+1000 + .next_try: + mov al, [ctrl.polling_mode] + test al, al + jnz .poll + mov ah, [do_poll] + test ah, ah + jz @f + .poll: + call azx_update_rirb + @@: + mov eax, [ctrl.rirb_cmd] + test eax, eax + jnz .l1 + mov [ctrl.rirb_error], 0 + mov al, [do_poll] + test al, al + jnz @f + mov [ctrl.poll_count], 0 + @@: + mov eax, [ctrl.rirb_res] ; the last value + jmp .out + .l1: + push eax + mov eax, 2000 ; temporary workaround + call StallExec + pop eax + dec ecx + jnz .next_try + .no_next_try: + mov al, [ctrl.polling_mode] + test al, al + jnz .no_poll + + mov al, [ctrl.poll_count] + cmp al, 2 + jge .poll_count_overflow + if DEBUG + push eax esi + mov esi, msgGetResponceTimeout + call SysMsgBoardStr + mov esi, msgPollingCodecOnce + call SysMsgBoardStr + pop esi eax + end if + mov [do_poll], 1 + inc [ctrl.poll_count] + jmp .again + + .poll_count_overflow: + if DEBUG + push eax esi + mov esi, msgGetResponceTimeout + call SysMsgBoardStr + mov esi, msgSwitchToPollMode + call SysMsgBoardStr + pop esi eax + end if + mov [ctrl.polling_mode], 1 + jmp .again + + .no_poll: + + mov al, [ctrl.polling_mode] + test al, al + jz @f + mov eax, -1 + jmp .out + @@: + + + ; a fatal communication error; need either to reset or to fallback + ; to the single_cmd mode + mov [ctrl.rirb_error], 1 + ;Asper~ -? [ + mov [ctrl.response_reset], 1 + mov eax, -1 ; give a chance to retry + jmp .out + ;Asper~ -? ] + + ;-? mov [ctrl.single_cmd], 1 + mov [ctrl.response_reset], 0 + + ; release CORB/RIRB + call azx_free_cmd_io + ; disable unsolicited responses + mov edx, ICH6_REG_GCTL + call azx_readl + mov ebx, ICH6_GCTL_UNSOL + xor ebx, -1 + and eax, ebx + mov edx, ICH6_REG_GCTL + call azx_writel + mov eax, -1 + .out: + pop edx ecx ebx + ret +endp + +; +; Use the single immediate command instead of CORB/RIRB for simplicity +; +; Note: according to Intel, this is not preferred use. The command was +; intended for the BIOS only, and may get confused with unsolicited +; responses. So, we shouldn't use it for normal operation from the +; driver. +; I left the codes, however, for debugging/testing purposes. +; + +; receive a response +proc azx_single_wait_for_response + push ecx edx esi + + mov ecx, 50 + .l1: + test ecx, ecx + jz .timeout + + ; check IRV busy bit + mov edx, ICH6_REG_IRS + call azx_readw + test ax, ICH6_IRS_VALID + jz @f + ; reuse rirb.res as the response return value + mov edx, ICH6_REG_IR + call azx_readl + mov [ctrl.rirb_res], eax + + pop esi edx ecx + xor eax, eax + ret + @@: + xor eax, eax + inc eax + call StallExec + + dec ecx + jmp .l1 + .timeout: + if DEBUG + xor eax, eax + mov edx, ICH6_REG_IRS + call azx_readw + mov esi, msgGetResponceTimeout + call SysMsgBoardStr + mov esi, msgIRS + call SysMsgBoardStr + stdcall fdword2str, 2 + call SysMsgBoardStr + end if + + pop esi edx ecx + mov eax, -1 + mov [ctrl.rirb_res], eax + ret +endp + +; send a command +proc azx_single_send_cmd stdcall, val:dword + push ecx edx esi + + mov ecx, 50 + .l1: + test ecx, ecx + jz .timeout + + ; check ICB busy bit + mov edx, ICH6_REG_IRS + call azx_readw + test ax, ICH6_IRS_BUSY + jnz @f + ; Clear IRV valid bit + mov edx, ICH6_REG_IRS + call azx_readw + or ax, ICH6_IRS_VALID + mov edx, ICH6_REG_IRS + call azx_writew + + mov eax, dword [val] + mov edx, ICH6_REG_IC + call azx_writel + + mov edx, ICH6_REG_IRS + call azx_readw + or ax, ICH6_IRS_BUSY + mov edx, ICH6_REG_IRS + call azx_writew + + stdcall azx_single_wait_for_response + pop esi edx ecx + ret + @@: + dec ecx + jmp .l1 + .timeout: + if DEBUG + xor eax, eax + mov edx, ICH6_REG_IRS + call azx_readw + mov esi, msgSendCmdTimeout + call SysMsgBoardStr + stdcall fdword2str, 2 + call SysMsgBoardStr + mov esi, msgVal + call SysMsgBoardStr + mov eax, dword [val] + stdcall fdword2str, 2 + call SysMsgBoardStr + end if + + pop esi edx ecx + mov eax, -1 + ret +endp + +; receive a response +proc azx_single_get_response + mov eax, [ctrl.rirb_res] + ret +endp + +; +; The below are the main callbacks from hda_codec. +; +; They are just the skeleton to call sub-callbacks according to the +; current setting of chip->single_cmd. +; + +; send a command +proc azx_send_cmd stdcall, val:dword + if USE_SINGLE_MODE + stdcall azx_single_send_cmd, [val] + else + stdcall azx_corb_send_cmd, [val] + end if + ret +endp + +; get a response +proc azx_get_response + if USE_SINGLE_MODE + call azx_single_get_response + else + call azx_rirb_get_response + end if + ret +endp + + +;;;;;;;;;;;;;;;;;;;;;;;; +;; Lowlevel interface ;; +;;;;;;;;;;;;;;;;;;;;;;;; + +; enable interrupts +proc azx_int_enable + push eax edx + ; enable controller CIE and GIE + mov edx, ICH6_REG_INTCTL + call azx_readl + or eax, ICH6_INT_CTRL_EN or ICH6_INT_GLOBAL_EN + mov edx, ICH6_REG_INTCTL + call azx_writel + pop edx eax + ret +endp + +; disable interrupts +proc azx_int_disable + push eax ebx edx + + ; disable interrupts in stream descriptor + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_readb + mov bl, SD_INT_MASK + xor bl, -1 + and al, bl + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_writeb + + ; disable SIE for all streams + xor al, al + mov edx, ICH6_REG_INTCTL + call azx_writeb + + ; disable controller CIE and GIE + mov edx, ICH6_REG_INTCTL + call azx_readl + mov ebx, ICH6_INT_CTRL_EN or ICH6_INT_GLOBAL_EN + xor ebx, -1 + and eax, ebx + call azx_writel + pop edx ebx eax + ret +endp + +; clear interrupts +proc azx_int_clear + push eax edx + + ; clear stream status + mov al, SD_INT_MASK + mov edx, ICH6_REG_SD_STS + SDO_OFS + call azx_writeb + + ; clear STATESTS + mov al, STATESTS_INT_MASK + mov edx, ICH6_REG_STATESTS + call azx_writeb + + ; clear rirb status + mov al, RIRB_INT_MASK + mov edx, ICH6_REG_RIRBSTS + call azx_writeb + + ; clear int status + mov eax, ICH6_INT_CTRL_EN or ICH6_INT_ALL_STREAM + mov edx, ICH6_REG_INTSTS + call azx_writel + pop edx eax + ret +endp + + +; start a stream +proc azx_stream_start + push eax edx + + ; enable SIE + mov edx, ICH6_REG_INTCTL + call azx_readl + + or eax, 0xC0000000 ;Asper+ + or eax, SDO_INT ; Asper: output stream interrupt index + mov edx, ICH6_REG_INTCTL + call azx_writel + ; set DMA start and interrupt mask + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_readb + + or al, SD_CTL_DMA_START or SD_INT_MASK + + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_writeb + + pop edx eax + ret +endp + +; stop DMA +proc azx_stream_clear + push eax ebx edx + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_readb + mov bl, SD_CTL_DMA_START or SD_INT_MASK + xor bl, -1 + and al, bl + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_writeb + mov al, SD_INT_MASK + mov edx, ICH6_REG_SD_STS + SDO_OFS + call azx_writeb + pop edx ebx eax + ret +endp + +; stop a stream +proc azx_stream_stop + push eax ebx edx + call azx_stream_clear + ; disable SIE + mov edx, ICH6_REG_INTCTL + call azx_readl + mov ebx, (SDO_INT) + xor ebx, -1 + and eax, ebx + mov edx, ICH6_REG_INTCTL + call azx_writel + pop edx ebx eax + ret +endp + +; +;in: eax = full_reset +; +; initialize the chip +proc azx_init_chip + push eax + + ; reset controller + mov eax, 1 ;full reset + call reset_controller + + ; initialize interrupts + call azx_int_clear + call azx_int_enable + + ; initialize the codec command I/O + if USE_SINGLE_MODE + else + call azx_init_cmd_io + end if + + ; program the position buffer + mov eax, dword [ctrl.posbuf] + mov ebx, eax + call GetPgAddr + and ebx, 0xFFF + add eax, ebx + mov edx, ICH6_REG_DPLBASE + call azx_writel + xor eax, eax + mov edx, ICH6_REG_DPUBASE + call azx_writel + + pop eax + ret +endp + + +; initialize the PCI registers + +; update bits in a PCI register byte +proc update_pci_byte stdcall, reg:dword, mask:dword, val:dword + push ax bx + stdcall PciRead8, [ctrl.bus], [ctrl.devfn], [reg] + mov bl, byte [mask] + mov bh, bl + xor bl, -1 + and al, bl + shr bx, 8 + and bl, byte [val] + or al, bl + stdcall PciWrite8, [ctrl.bus], [ctrl.devfn], [reg], eax + pop bx ax + ret +endp + + +proc azx_init_pci + ; Clear bits 0-2 of PCI register TCSEL (at offset 0x44) + ; TCSEL == Traffic Class Select Register, which sets PCI express QOS + ; Ensuring these bits are 0 clears playback static on some HD Audio + ; codecs + push eax + stdcall update_pci_byte, ICH6_PCIREG_TCSEL, 0x07, 0 + + mov eax, [driver_type] + cmp eax, AZX_DRIVER_ATI + jne @f + ; For ATI SB450 azalia HD audio, we need to enable snoop + stdcall update_pci_byte, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP + jmp .done + @@: + cmp eax, AZX_DRIVER_NVIDIA + jne @f + ; For NVIDIA HDA, enable snoop + stdcall update_pci_byte, NVIDIA_HDA_TRANSREG_ADDR, 0x0f, NVIDIA_HDA_ENABLE_COHBITS + stdcall update_pci_byte, NVIDIA_HDA_ISTRM_COH, 0x01, NVIDIA_HDA_ENABLE_COHBIT + stdcall update_pci_byte, NVIDIA_HDA_OSTRM_COH, 0x01, NVIDIA_HDA_ENABLE_COHBIT + jmp .done + @@: + cmp eax, AZX_DRIVER_SCH + je .l1 + cmp eax, AZX_DRIVER_PCH + jne @f + .l1: + stdcall PciRead16, [ctrl.bus], [ctrl.devfn], dword INTEL_SCH_HDA_DEVC + test ax, INTEL_SCH_HDA_DEVC_NOSNOOP + jz @f + push ebx + mov ebx, INTEL_SCH_HDA_DEVC_NOSNOOP + xor ebx, -1 + and eax, ebx + pop ebx + stdcall PciWrite16, [ctrl.bus], [ctrl.devfn], dword INTEL_SCH_HDA_DEVC, eax + stdcall PciRead16, [ctrl.bus], [ctrl.devfn], dword INTEL_SCH_HDA_DEVC + + if DEBUG + push esi + mov esi, msgHDASnoopDisabled + call SysMsgBoardStr + mov esi, msg_OK + test ax, INTEL_SCH_HDA_DEVC_NOSNOOP + jz .snoop_ok + mov esi, msg_Fail + .snoop_ok: + call SysMsgBoardStr + pop esi + end if + @@: + .done: + pop eax + ret +endp + + +; reset stream +proc azx_stream_reset + push eax ebx ecx edx + + call azx_stream_clear + + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_readb + or al, SD_CTL_STREAM_RESET + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_writeb + + mov eax, 3 + call StallExec + + mov ecx, 300 + .l1: + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_readb + test al, SD_CTL_STREAM_RESET + jnz @f + dec ecx + jnz .l1 + @@: + mov bl, SD_CTL_STREAM_RESET + xor bl, -1 + and al, bl + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_writeb + + mov eax, 3 + call StallExec + + mov ecx, 300 + ; waiting for hardware to report that the stream is out of reset + .l2: + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_readb + test al, SD_CTL_STREAM_RESET + jnz @f + dec ecx + jnz .l2 + @@: + ; reset first position - may not be synced with hw at this time + mov edx, [ctrl.posbuf] + mov dword [edx], 0 + pop edx ecx ebx eax + ret +endp + + +; set up the SD for streaming +proc azx_setup_controller + push eax ebx ecx edx + ; make sure the run bit is zero for SD + call azx_stream_clear + + ; program the stream_tag + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_readl + mov ecx, SD_CTL_STREAM_TAG_MASK + xor ecx, -1 + and eax, ecx + mov ecx, SDO_TAG + shl ecx, SD_CTL_STREAM_TAG_SHIFT + or eax, ecx + ; Asper stream_tag = SDO_TAG + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_writel + + ; program the length of samples in cyclic buffer + mov eax, 0x4000*32 + mov edx, ICH6_REG_SD_CBL + SDO_OFS + call azx_writel + + ; program the stream format + ; this value needs to be the same as the one programmed + mov ax, 0x11 + mov edx, ICH6_REG_SD_FORMAT + SDO_OFS + call azx_writew + + ; program the stream LVI (last valid index) of the BDL + mov eax, 32-1 ;4-1 ;2-1 + mov [ctrl.lvi_reg], eax + mov edx, ICH6_REG_SD_LVI + SDO_OFS + call azx_writew + + ; program the BDL address + ; lower BDL address + mov eax, [pcmout_bdl] + mov ebx, eax + call GetPgAddr + and ebx, 0xFFF + add eax, ebx + mov edx, ICH6_REG_SD_BDLPL + SDO_OFS + call azx_writel + ; upper BDL address + xor eax, eax ;upper_32bit(azx_dev->bdl_addr) + mov edx, ICH6_REG_SD_BDLPU + SDO_OFS + call azx_writel + + ; enable the position buffer + cmp [ctrl.position_fix], POS_FIX_LPIB + jz @f + mov edx, ICH6_REG_DPLBASE + call azx_readl + and eax, ICH6_DPLBASE_ENABLE + jnz @f + mov eax, dword [ctrl.posbuf] + mov ebx, eax + call GetPgAddr + and ebx, 0xFFF + add eax, ebx + or eax, ICH6_DPLBASE_ENABLE + mov edx, ICH6_REG_DPLBASE + call azx_writel + @@: + + ; set the interrupt enable bits in the descriptor control register + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_readl + or eax, SD_INT_MASK + mov edx, ICH6_REG_SD_CTL + SDO_OFS + call azx_writel + + pop edx ecx ebx eax + ret +endp + + +;(...) + +; Probe the given codec address +proc probe_codec, addr:dword + push edx + mov eax, [addr] + shl eax, 28 + mov edx, (AC_NODE_ROOT shl 20) or (AC_VERB_PARAMETERS shl 8) or AC_PAR_VENDOR_ID + or eax, edx + stdcall azx_send_cmd, eax + stdcall azx_get_response + + cmp eax, -1 + je .out + mov eax, [addr] + mov [codec.addr], eax ;Asper+ + if DEBUG + push esi + mov esi, msgCodecOK + call SysMsgBoardStr + mov esi, msgCAd + call SysMsgBoardStr + stdcall fdword2str, 3 + call SysMsgBoardStr + pop esi + end if + xor eax, eax + .out: + pop edx + ret +endp + + +proc azx_bus_reset + call azx_stop_chip + call azx_init_chip +endp + + +; Codec initialization +proc azx_codec_create + push ebx ecx edx + ;(...) + ; First try to probe all given codec slots + ; Asper: We asume for now that max slots for codecs = 4 + xor ecx, ecx + xor edx, edx + inc edx + .next_slot: + test edx, [ctrl.codec_mask] + jz @f + stdcall probe_codec, ecx + test eax, eax + jz .init ;@f + ; Some BIOSen give you wrong codec addresses that don't exist + if DEBUG + mov esi, msgCodecError + call SysMsgBoardStr + end if + mov ebx, edx + xor ebx, -1 + and [ctrl.codec_mask], ebx + + ; More badly, accessing to a non-existing + ; codec often screws up the controller chip, + ; and disturbs the further communications. + ; Thus if an error occurs during probing, + ; better to reset the controller chip to + ; get back to the sanity state. + ;call azx_bus_reset + @@: + shl edx, 1 + inc ecx +; if USE_FIRST_CODEC +; cmp ecx, 1 +; else + cmp ecx, 3 +; end if + jl .next_slot + mov eax, -1 + jmp .out + .init: + stdcall snd_hda_codec_init + xor eax, eax + .out: + pop edx ecx ebx + ret +endp + + +proc azx_codec_configure + ;(...) + call snd_hda_codec_configure + ret +endp + + +proc azx_get_position + test [ctrl.position_fix], POS_FIX_LPIB + jz @f + ; read LPIB + mov edx, ICH6_REG_SD_LPIB + SDO_OFS + call azx_readl + jmp .out + @@: + test [ctrl.position_fix], POS_FIX_VIACOMBO + jz @f +; call azx_get_via_position +; jmp .out + @@: + ; use the position buffer + push edx + mov edx, dword [ctrl.posbuf] + mov eax, dword [edx] + pop edx + .out: + cmp eax, 0x4000 ; bufsize + jl @f + xor eax, eax + @@: + ret +endp + + +proc azx_stop_chip + push eax edx + + ; disable interrupts + call azx_int_disable + call azx_int_clear + ; disable CORB/RIRB + call azx_free_cmd_io + ; disable position buffer + xor eax, eax + mov edx, ICH6_REG_DPLBASE + call azx_writel + mov edx, ICH6_REG_DPUBASE + call azx_writel + + pop edx eax + ret +endp + + +; in: eax = volume (-10000 - 0) +align 4 +set_master_vol: + mov ecx, 3 + call set_channel_volume + ret + + +; out: [pvol] = volume (-10000 - 0) +align 4 +proc get_master_vol stdcall, pvol:dword + xor ecx, ecx + call get_channel_volume + mov ebx, [pvol] + mov [ebx], eax + xor eax, eax + ret +endp + + +; in: ecx = channel mask (1 - Left; 2 - Right; 3-Both) +; eax = volume (-10000 - 0) +align 4 +set_channel_volume: + push eax ebx ecx edx + mov ebx, [volume.maxDb] + neg eax + if 0;DEBUG ;YAHOO + push eax esi + mov esi, msgNewVolume + call SysMsgBoardStr + stdcall fdword2str, 2 + call SysMsgBoardStr + + mov esi, msgMaxVolume + call SysMsgBoardStr + mov eax, ebx + stdcall fdword2str, 2 + call SysMsgBoardStr + pop esi eax + end if + test ebx, ebx + jz .err_out + + cmp eax, 0 + jg @f + xor eax, eax + jmp .set + @@: + cmp eax, ebx + jl .set + mov eax, ebx + .set: + ;cdq + xor edx, edx + shl eax, 2 + mov ebx, 100 + div bx + mov bl, [volume.step_size] + div bl + + mov edx, [volume.out_amp_node] + test edx, edx + jz .out + movzx ebx, [edx+HDA_GNODE.nid] + + test ecx, 1 ; Left channel ? + jz @f + stdcall put_volume_mute, ebx, 0, HDA_OUTPUT, 0, eax + @@: + test ecx, 2 ; Right channel ? + jz .out + stdcall put_volume_mute, ebx, 1, HDA_OUTPUT, 0, eax + .out: + pop edx ecx ebx eax + ret + .err_out: + if 0;DEBUG ;YAHOO + push esi + mov esi, emsgNoVolCtrl + call SysMsgBoardStr + pop esi + end if + jmp .out + +; in: ecx = channel (1 - Left; 2 - Right) +; out: eax = volume (-10000 - 0) +align 4 +get_channel_volume: + push ebx ecx edx + cmp ecx, 2 + jg .out + dec cl + xor eax, eax + mov edx, [volume.out_amp_node] + test edx, edx + jz .out + movzx ebx, [edx+HDA_GNODE.nid] + stdcall get_volume_mute, ebx, ecx, HDA_OUTPUT, 0 + mov cl, [volume.step_size] + mul cl + + mov cx, 100 + mul cx + shr eax, 2 ; *0.25 + neg eax + .out: + pop edx ecx ebx + ret + + +; in: ecx = delay +udelay: + push eax ecx edx + test ecx, ecx + jnz @f + inc ecx + @@: + mov eax, ecx + mov cx, 500 + mul cl + mov ecx, edx + shl ecx, 16 + or ecx, eax + @@: + xor eax, eax + cpuid + dec ecx + jz @b + pop edx ecx eax + ret + +align 4 +proc StallExec + push ecx + push edx + push ebx + push eax + + mov ecx, CPU_FREQ + mul ecx + mov ebx, eax ;low + mov ecx, edx ;high + rdtsc + add ebx, eax + adc ecx,edx +@@: + rdtsc + sub eax, ebx + sbb edx, ecx + js @B + + pop eax + pop ebx + pop edx + pop ecx + ret +endp + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MEMORY MAPPED IO (os depended) ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +align 4 +proc azx_readb + add edx, [ctrl.ctrl_mem_base] + mov al, [edx] + ret +endp + +align 4 +proc azx_readw + add edx, [ctrl.ctrl_mem_base] + mov ax, [edx] + ret +endp + +align 4 +proc azx_readl + add edx, [ctrl.ctrl_mem_base] + mov eax, [edx] + ret +endp + +align 4 +proc azx_writeb + add edx, [ctrl.ctrl_mem_base] + mov [edx], al + ret +endp + +align 4 +proc azx_writew + add edx, [ctrl.ctrl_mem_base] + mov [edx], ax + ret +endp + +align 4 +proc azx_writel + add edx, [ctrl.ctrl_mem_base] + mov [edx], eax + ret +endp + +;_______ + + +;Asper remember to add this functions: +proc snd_hda_queue_unsol_event stdcall, par1:dword, par2:dword + if DEBUG + push esi + mov esi, msgUnsolEvent + call SysMsgBoardStr + pop esi + end if + ret +endp +;... + + +align 4 +proc fdword2str stdcall, flags:dword ; bit 0 - skipLeadZeroes; bit 1 - newLine; other bits undefined + push eax ebx ecx + mov esi, hex_buff + mov ecx, -8 + push eax + @@: + rol eax, 4 + mov ebx, eax + and ebx, 0x0F + mov bl, [ebx+hexletters] + mov [8+esi+ecx], bl + inc ecx + jnz @B + pop eax + + mov dword [esi+8], 0 + test [flags], 0x2 ; new line ? + jz .no_newline + mov dword [esi+8], 0x00000A0D + .no_newline: + + push eax + test [flags], 0x1 ; skip zero bits ? + jz .no_skipz + mov ecx, 8 + @@: + test eax, 0xF0000000 + jnz .skipz_done + rol eax, 4 + inc esi + dec ecx + jnz @b + dec esi + .skipz_done: + .no_skipz: + pop eax + + pop ecx ebx eax + ret +endp + +hexletters db '0123456789ABCDEF' +hex_buff db 8 dup(0),13,10,0,0 + + +include "codec.inc" +include "hda_generic.inc" + +align 4 +devices: +; Intel + dd (CTRL_INTEL_SCH2 shl 16)+VID_INTEL,msg_INTEL_SCH2, AZX_DRIVER_SCH + dd (CTRL_INTEL_HPT shl 16)+VID_INTEL,msg_INTEL_HPT, AZX_DRIVER_SCH + dd (CTRL_INTEL_CPT shl 16)+VID_INTEL,msg_INTEL_CPT, AZX_DRIVER_PCH + dd (CTRL_INTEL_PGB shl 16)+VID_INTEL,msg_INTEL_PGB, AZX_DRIVER_PCH + dd (CTRL_INTEL_PPT1 shl 16)+VID_INTEL,msg_INTEL_PPT1, AZX_DRIVER_PCH + dd (CTRL_INTEL_82801F shl 16)+VID_INTEL,msg_INTEL_82801F, AZX_DRIVER_ICH + dd (CTRL_INTEL_63XXESB shl 16)+VID_INTEL,msg_INTEL_63XXESB, AZX_DRIVER_ICH + dd (CTRL_INTEL_82801G shl 16)+VID_INTEL,msg_INTEL_82801G, AZX_DRIVER_ICH + dd (CTRL_INTEL_82801H shl 16)+VID_INTEL,msg_INTEL_82801H, AZX_DRIVER_ICH + dd (CTRL_INTEL_82801_UNK1 shl 16)+VID_INTEL,msg_INTEL_82801_UNK1, AZX_DRIVER_ICH + dd (CTRL_INTEL_82801I shl 16)+VID_INTEL,msg_INTEL_82801I, AZX_DRIVER_ICH + dd (CTRL_INTEL_82801_UNK2 shl 16)+VID_INTEL,msg_INTEL_82801_UNK2, AZX_DRIVER_ICH + dd (CTRL_INTEL_82801JI shl 16)+VID_INTEL,msg_INTEL_82801JI, AZX_DRIVER_ICH + dd (CTRL_INTEL_82801JD shl 16)+VID_INTEL,msg_INTEL_82801JD, AZX_DRIVER_ICH + dd (CTRL_INTEL_PCH shl 16)+VID_INTEL,msg_INTEL_PCH, AZX_DRIVER_PCH + dd (CTRL_INTEL_PCH2 shl 16)+VID_INTEL,msg_INTEL_PCH2, AZX_DRIVER_PCH + dd (CTRL_INTEL_SCH shl 16)+VID_INTEL,msg_INTEL_SCH, AZX_DRIVER_SCH + dd (CTRL_INTEL_LPT shl 16)+VID_INTEL,msg_INTEL_LPT, AZX_DRIVER_PCH +; Nvidia + dd (CTRL_NVIDIA_MCP51 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP51, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP55 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP55, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP61_1 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP61, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP61_2 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP61, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP65_1 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP65, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP65_2 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP65, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP67_1 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP67, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP67_2 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP67, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP73_1 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP73, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP73_2 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP73, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP78_1 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP78, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP78_2 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP78, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP78_3 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP78, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP78_4 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP78, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP79_1 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP79, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP79_2 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP79, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP79_3 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP79, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP79_4 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP79, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_0BE2 shl 16)+VID_NVIDIA,msg_NVIDIA_0BE2, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_0BE3 shl 16)+VID_NVIDIA,msg_NVIDIA_0BE3, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_0BE4 shl 16)+VID_NVIDIA,msg_NVIDIA_0BE4, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_GT100 shl 16)+VID_NVIDIA,msg_NVIDIA_GT100, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_GT106 shl 16)+VID_NVIDIA,msg_NVIDIA_GT106, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_GT108 shl 16)+VID_NVIDIA,msg_NVIDIA_GT108, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_GT104 shl 16)+VID_NVIDIA,msg_NVIDIA_GT104, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_GT116 shl 16)+VID_NVIDIA,msg_NVIDIA_GT116, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP89_1 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP89, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP89_2 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP89, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP89_3 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP89, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_MCP89_4 shl 16)+VID_NVIDIA,msg_NVIDIA_MCP89, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_GF119 shl 16)+VID_NVIDIA,msg_NVIDIA_GF119, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_GF110_1 shl 16)+VID_NVIDIA,msg_NVIDIA_GF110, AZX_DRIVER_NVIDIA + dd (CTRL_NVIDIA_GF110_2 shl 16)+VID_NVIDIA,msg_NVIDIA_GF110, AZX_DRIVER_NVIDIA +; ATI + dd (CTRL_ATI_SB450 shl 16)+VID_ATI,msg_ATI_SB450, AZX_DRIVER_ATI + dd (CTRL_ATI_SB600 shl 16)+VID_ATI,msg_ATI_SB600, AZX_DRIVER_ATI + dd (CTRL_ATI_RS600 shl 16)+VID_ATI,msg_ATI_RS600, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RS690 shl 16)+VID_ATI,msg_ATI_RS690, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RS780 shl 16)+VID_ATI,msg_ATI_RS780, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RS_UNK1 shl 16)+VID_ATI,msg_ATI_RS_UNK1, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_R600 shl 16)+VID_ATI,msg_ATI_R600, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RV610 shl 16)+VID_ATI,msg_ATI_RV610, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RV620 shl 16)+VID_ATI,msg_ATI_RV620, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RV630 shl 16)+VID_ATI,msg_ATI_RV630, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RV635 shl 16)+VID_ATI,msg_ATI_RV635, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RV670 shl 16)+VID_ATI,msg_ATI_RV670, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RV710 shl 16)+VID_ATI,msg_ATI_RV710, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RV730 shl 16)+VID_ATI,msg_ATI_RV730, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RV740 shl 16)+VID_ATI,msg_ATI_RV740, AZX_DRIVER_ATIHDMI + dd (CTRL_ATI_RV770 shl 16)+VID_ATI,msg_ATI_RV770, AZX_DRIVER_ATIHDMI +; AMD + dd (CTRL_AMD_HUDSON shl 16)+VID_AMD,msg_AMD_HUDSON, AZX_DRIVER_GENERIC +; VIA + dd (CTRL_VIA_VT82XX shl 16)+VID_VIA,msg_VIA_VT82XX, AZX_DRIVER_VIA + dd (CTRL_VIA_VT61XX shl 16)+VID_VIA,msg_VIA_VT61XX, AZX_DRIVER_GENERIC + dd (CTRL_VIA_VT71XX shl 16)+VID_VIA,msg_VIA_VT71XX, AZX_DRIVER_GENERIC +; SiS + dd (CTRL_SIS_966 shl 16)+VID_SIS,msg_SIS_966, AZX_DRIVER_SIS +; ULI + dd (CTRL_ULI_M5461 shl 16)+VID_ULI,msg_ULI_M5461, AZX_DRIVER_ULI +; Teradici + dd (CTRL_TERA_UNK1 shl 16)+VID_ULI,msg_TERA_UNK1, AZX_DRIVER_TERA +; Creative + dd (CTRL_CREATIVE_CA0110_IBG shl 16)+VID_CREATIVE,msg_CREATIVE_CA0110_IBG, AZX_DRIVER_CTX + dd (CTRL_CREATIVE_SOUND_CORE3D_1 shl 16)+VID_CREATIVE,msg_CREATIVE_SOUND_CORE3D, AZX_DRIVER_GENERIC + dd (CTRL_CREATIVE_SOUND_CORE3D_2 shl 16)+VID_CREATIVE,msg_CREATIVE_SOUND_CORE3D, AZX_DRIVER_GENERIC +; RDC Semiconductor + dd (CTRL_RDC_R3010 shl 16)+VID_RDC,msg_RDC_R3010, AZX_DRIVER_GENERIC +; VMware + dd (CTRL_VMWARE_UNK1 shl 16)+VID_VMWARE,msg_VMWARE_UNK1, AZX_DRIVER_GENERIC + + dd 0 ;terminator + + +version dd (5 shl 16) or (API_VERSION and 0xFFFF) + +msg_Intel db 'Intel ',0 +msg_INTEL_CPT db 'Cougar Point',13,10,0 +msg_INTEL_PGB db 'Patsburg',13,10,0 +msg_INTEL_PPT1 db 'Panther Point',13,10,0 +msg_INTEL_LPT db 'Lynx Point',13,10,0 +msg_INTEL_HPT db 'Haswell',13,10,0 +msg_INTEL_82801F db '82801F',13,10,0 +msg_INTEL_63XXESB db '631x/632xESB',13,10,0 +msg_INTEL_82801G db '82801G', 13,10,0 +msg_INTEL_82801H db '82801H', 13,10,0 +msg_INTEL_82801I db '82801I', 13,10,0 +msg_INTEL_82801JI db '82801JI',13,10,0 +msg_INTEL_82801JD db '82801JD',13,10,0 +msg_INTEL_PCH db 'PCH',13,10,0 +msg_INTEL_PCH2 db 'PCH2',13,10,0 +msg_INTEL_SCH db 'Poulsbo',13,10,0 +msg_INTEL_SCH2 db 'Oaktrail',13,10,0 +msg_INTEL_82801_UNK1 db '82801_UNK1', 13,10,0 +msg_INTEL_82801_UNK2 db '82801_UNK2', 13,10,0 + +msg_NVidia db 'NVidia ',0 +msg_NVIDIA_MCP51 db 'MCP51', 13,10,0 +msg_NVIDIA_MCP55 db 'MCP55', 13,10,0 +msg_NVIDIA_MCP61 db 'MCP61', 13,10,0 +msg_NVIDIA_MCP65 db 'MCP65', 13,10,0 +msg_NVIDIA_MCP67 db 'MCP67', 13,10,0 +msg_NVIDIA_MCP73 db 'MCP73', 13,10,0 +msg_NVIDIA_MCP78 db 'MCP78', 13,10,0 +msg_NVIDIA_MCP79 db 'MCP79', 13,10,0 +msg_NVIDIA_MCP89 db 'MCP89', 13,10,0 +msg_NVIDIA_0BE2 db '(0x0be2)', 13,10,0 +msg_NVIDIA_0BE3 db '(0x0be3)', 13,10,0 +msg_NVIDIA_0BE4 db '(0x0be4)', 13,10,0 +msg_NVIDIA_GT100 db 'GT100', 13,10,0 +msg_NVIDIA_GT104 db 'GT104', 13,10,0 +msg_NVIDIA_GT106 db 'GT106', 13,10,0 +msg_NVIDIA_GT108 db 'GT108', 13,10,0 +msg_NVIDIA_GT116 db 'GT116', 13,10,0 +msg_NVIDIA_GF119 db 'GF119', 13,10,0 +msg_NVIDIA_GF110 db 'GF110', 13,10,0 + +msg_ATI db 'ATI ',0 +msg_ATI_SB450 db 'SB450', 13,10,0 +msg_ATI_SB600 db 'SB600', 13,10,0 + +msg_ATI_HDMI db 'ATI HDMI ',0 +msg_ATI_RS600 db 'RS600', 13,10,0 +msg_ATI_RS690 db 'RS690', 13,10,0 +msg_ATI_RS780 db 'RS780', 13,10,0 +msg_ATI_RS_UNK1 db 'RS_UNK1', 13,10,0 +msg_ATI_R600 db 'R600', 13,10,0 +msg_ATI_RV610 db 'RV610', 13,10,0 +msg_ATI_RV620 db 'RV620', 13,10,0 +msg_ATI_RV630 db 'RV630', 13,10,0 +msg_ATI_RV635 db 'RV635', 13,10,0 +msg_ATI_RV670 db 'RV670', 13,10,0 +msg_ATI_RV710 db 'RV710', 13,10,0 +msg_ATI_RV730 db 'RV730', 13,10,0 +msg_ATI_RV740 db 'RV740', 13,10,0 +msg_ATI_RV770 db 'RV770', 13,10,0 + +msg_AMD db 'AMD ',0 +msg_AMD_HUDSON db 'Hudson', 13,10,0 + +msg_VIA db 'VIA ',0 +msg_VIA_VT82XX db 'VT8251/8237A', 13,10,0 +msg_VIA_VT61XX db 'GFX VT6122/VX11', 13,10,0 +msg_VIA_VT71XX db 'GFX VT7122/VX900', 13,10,0 + +msg_SIS db 'SIS ',0 +msg_SIS_966 db '966', 13,10,0 + +msg_ULI db 'ULI ',0 +msg_ULI_M5461 db 'M5461', 13,10,0 + +msg_TERA db 'Teradici ',0 +msg_TERA_UNK1 db 'UNK1', 13,10,0 + +msg_CREATIVE db 'Creative ',0 +msg_CREATIVE_CA0110_IBG db 'CA0110-IBG',13,10,0 ;SB X-Fi Xtreme Audio +msg_CREATIVE_SOUND_CORE3D db 'Sound Core3D' + +msg_RDC db 'RDC ',0 +msg_RDC_R3010 db 'R3010', 13,10,0 + +msg_VMWARE db 'VMware ',0 +msg_VMWARE_UNK1 db 'UNK1', 13,10,0 + +szKernel db 'KERNEL',0 +sz_sound_srv db 'SOUND',0 + +msgInit db 'detect hardware...',13,10,0 +msgFail db 'device not found',13,10,0 +msgAttchIRQ db 'IRQ line not supported', 13,10,0 +msgInvIRQ db 'IRQ line not assigned or invalid', 13,10,0 +msgPlay db 'start play', 13,10,0 +msgStop db 'stop play', 13,10,0 +msgSetChannelVolume db 'Set Channel Volume', 13,10,0 +msgIRQ db 'HDA IRQ', 13,10,0 +msgInitCtrl db 'init controller',13,10,0 +msgPrimBuff db 'create primary buffer ...',0 +msgDone db 'done',13,10,0 +msgRemap db 'Remap IRQ',13,10,0 +msgOk db 'service installed',13,10,0 +msgCold db 'cold reset',13,10,0 + msgHDARFail db 'controller not ready',13,10,0 +msgCFail db 'codec not ready',13,10,0 +msgResetOk db 'reset complete',13,10,0 +msgPciCmd db 'PCI command ',0 +msgPciStat db 'PCI status ',0 + msgHDALowMMIo db 'lower mmio base ',0 + msgHDAUpMMIo db 'upper mmio base ',0 +msgIrqMap db 'HDA irq map as ',0 + +;Asper [ +if DEBUG + msgCodecMask db 'codec_mask = ',0 + msgNoCodecsFound db 'no codecs found!',13,10,0 + msgHDASnoopDisabled db 'HDA snoop disabled, enabling ... ',0 + msg_OK db 'OK',13,10,0 + msg_Fail db 'Failed',13,10,0 + msgSpuriousResponce db 'spurious responce ',0 + emsgInvalidAFGSubtree db 'Invalid AFG subtree',13,10,0 + emsgConnListNotAvailable db 'connection list not available for ',0 + msgUnmuteOut db 'UNMUTE OUT: NID=',0 + msgUnmuteIn db 'UNMUTE IN: NID=',0 + msgGetResponceTimeout db 'get_response timeout: ',0 + msgVal db ' val=',0 + emsgBusResetFatalComm db 'resetting BUS due to fatal communication error',13,10,0 + msgCodecOK db 'codec probed OK',13,10,0 + msgCodecError db 'codec probe error disabling it...',13,10,0 + emsgNoAFGorMFGFound db 'no AFG or MFG node found',13,10,0 + emsgNoMem db 'hda_codec: cannot malloc',13,10,0 + msgConnect db 'CONNECT: NID=',0 + msgIdx db ' IDX=',0 + msgSkipDigitalOutNode db 'Skip Digital OUT node ',0 + msgAudOutFound db 'AUD_OUT found ',0 + emsgNoParserAvailable db 'No codec parser is available',13,10,0 + emsgNoProperOutputPathFound db 'hda_generic: no proper output path found',13,10,0 + emsgInvConnList db 'hda_codec: invalid CONNECT_LIST verb ',0 + emsgInvDepRangeVal db 'hda_codec: invalid dep_range_val ',0 + emsgTooManyConns db 'Too many connections',13,10,0 + emsgNoVolCtrl db 'No volume control',13,10,0 + msgHDACodecSetupStream db 'hda_codec_setup_stream: NID=',0 + msgStream db 'stream=',0 + msgChannel db 'channel=',0 + msgFormat db 'format=',0 + + msgPollingCodecOnce db 'polling the codec once',13,10,0 ;Asper~ + msgSwitchToPollMode db 'switching to polling mode',13,10,0 ;Asper~ + + msgUnsolEvent db 'Unsolicited event!',13,10,0 + strSemicolon db ':',0 + msgSETUP_FG_NODES db 'Setup FG nodes = start_nid:total_nodes = ',0 + msgFG_TYPE db 'FG type = ',0 + msgPinCfgs db 'Pin configurations:',13,10,0 + msgWCaps db 'Widget capabilities:',13,10,0 + msgCAd db 'CAd = ',0 + msgTCSEL db 'PCI TCSEL ',0 + msgTV db 'HDA test version ',TEST_VERSION_NUMBER,13,10,0 + msgGCap db 'GCAP = ',0 +end if + +if USE_SINGLE_MODE + msgSingleMode db 'Single mode !',13,10,0 + msgIRS db 'IRS=',0 + msgSendCmdTimeout db 'send_cmd timeout: IRS=',0 +else + msgNormalMode db 'Normal mode !',13,10,0 +end if + +if DEBUG + msgYAHOO2 db 'YAHOO2: ',0 + msgMaxVolume db 'MaxVolume: ',0 + msgNewVolume db 'NewVolume: ',0 + + msgVerbQuery db 'Q: ',0 + msgVerbAnswer db 'A: ',0 + msgPin_Nid db 'Pin Nid = ',0 + msgPin_Ctl db 'Pin Control = ',0 + msgPin_Caps db 'Pin Capabilities = ',0 + msgDef_Cfg db 'Pin def_cfg = ',0 + msgAmp_Out_Caps db 'Pin Amp Out caps = ',0 + msgAmpVal db 'Amp val = ',0 + msgEnableEAPD db 'Enable EAPD: NID=',0 + msgBeeperNid db 'Beeper found: NID=',0 + msgBeeperValue db 'Beeper initial value: ',0 + msgBeepNow db 'Beep!',13,10,0 +end if + +;] Asper + + +section '.data' data readable writable align 16 + + +codec CODEC +ctrl AC_CNTRL + +;Asper: BDL must be aligned to 128 according to HDA specification. +pcmout_bdl rd 1 +buff_list rd 32 + +driver_type rd 1