;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

format MS COFF

DEBUG		equ 1

include 'proc32.inc'
include 'imports.inc'

API_VERSION	equ 0x01000100

DEBUG_IRQ	equ 0

USE_COM_IRQ	equ 0	 ;make irq 3 and irq 4 available for PCI devices
IRQ_REMAP	equ 0
IRQ_LINE	equ 0


;irq 0,1,2,8,12,13 íåäîñòóïíû
;                   FEDCBA9876543210
VALID_IRQ	equ 1100111011111000b
ATTCH_IRQ	equ 0000111010100000b

if USE_COM_IRQ
ATTCH_IRQ	equ 0000111010111000b
end if

CPU_FREQ	equ  2600d

BIT0  EQU 0x00000001
BIT1  EQU 0x00000002
BIT2  EQU 0x00000004
BIT3  EQU 0x00000008
BIT4  EQU 0x00000010
BIT5  EQU 0x00000020
BIT6  EQU 0x00000040
BIT7  EQU 0x00000080
BIT8  EQU 0x00000100
BIT9  EQU 0x00000200
BIT10 EQU 0x00000400
BIT11 EQU 0x00000800
BIT12 EQU 0x00001000
BIT13 EQU 0x00002000
BIT14 EQU 0x00004000
BIT15 EQU 0x00008000
BIT16 EQU 0x00010000
BIT17 EQU 0x00020000
BIT18 EQU 0x00040000
BIT19 EQU 0x00080000
BIT20 EQU 0x00100000
BIT21 EQU 0x00200000
BIT22 EQU 0x00400000
BIT23 EQU 0x00800000
BIT24 EQU 0x00100000
BIT25 EQU 0x02000000
BIT26 EQU 0x04000000
BIT27 EQU 0x08000000
BIT28 EQU 0x10000000
BIT29 EQU 0x20000000
BIT30 EQU 0x40000000
BIT31 EQU 0x80000000

PCM_4 equ BIT20
PCM_6 equ BIT21

VID_INTEL	  equ 0x8086
VID_NVIDIA	  equ 0x10DE

CTRL_ICH	  equ 0x2415
CTRL_ICH0	  equ 0x2425
CTRL_ICH2	  equ 0x2435
CTRL_ICH3	  equ 0x2445
CTRL_ICH4	  equ 0x24C5
CTRL_ICH5	  equ 0x24D5
CTRL_ICH6	  equ 0x266E
CTRL_ICH7	  equ 0x27DE

CTRL_NFORCE	  equ 0x01B1
CTRL_NFORCE2	  equ 0x006A
CTRL_NFORCE3	  equ 0x00DA
CTRL_MCP04	  equ 0x003A
CTRL_CK804	  equ 0x0059
CTRL_CK8	  equ 0x008A
CTRL_CK8S	  equ 0x00EA
CTRL_MCP51	  equ 0x026B


PCM_OUT_BDL	  equ  0x10  ; PCM out buffer descriptors list
PCM_OUT_CR_REG	  equ  0x1b  ; PCM out Control Register
PCM_OUT_LVI_REG   equ  0x15  ; PCM last valid index
PCM_OUT_SR_REG	  equ  0x16  ; PCM out Status register
PCM_OUT_PIV_REG   equ  0x1a
PCM_OUT_CIV_REG   equ  0x14  ; PCM out current index

PCM_IN_CR_REG	  equ  0x0b  ; PCM in Control Register
MC_IN_CR_REG	  equ  0x2b  ; MIC in Control Register
RR		  equ  BIT1  ; reset registers.  Nukes all regs

CODEC_MASTER_VOL_REG	     equ 0x02
CODEC_AUX_VOL		     equ 0x04 ;
CODEC_PCM_OUT_REG	     equ 0x18 ; PCM output volume
CODEC_EXT_AUDIO_REG	     equ 0x28 ; extended audio
CODEC_EXT_AUDIO_CTRL_REG     equ 0x2a ; extended audio control
CODEC_PCM_FRONT_DACRATE_REG  equ 0x2c ; PCM out sample rate
CODEC_PCM_SURND_DACRATE_REG  equ 0x2e ; surround sound sample rate
CODEC_PCM_LFE_DACRATE_REG    equ 0x30 ; LFE sample rate

GLOB_CTRL	  equ  0x2C  ;   Global Control
CTRL_STAT	  equ  0x30  ;   Global Status
CTRL_CAS	  equ  0x34  ;   Codec Access Semiphore

CAS_FLAG	  equ  0x01  ;   Codec Access Semiphore Bit

CTRL_ST_CREADY	  equ  BIT8+BIT9+BIT28 ;   Primary Codec Ready

CTRL_ST_RCS	  equ  0x00008000  ;   Read Completion Status

CTRL_CNT_CRIE	  equ  BIT4+BIT5+BIT6  ;   Codecs Resume Interrupt Enable
CTRL_CNT_AC_OFF   equ  0x00000008  ;   ACLINK Off
CTRL_CNT_WARM	  equ  0x00000004  ;   AC97 Warm Reset
CTRL_CNT_COLD	  equ  0x00000002  ;   AC97 Cold Reset
CTRL_CNT_GIE	  equ  0x00000001  ;   GPI Interrupt Enable

CODEC_REG_POWERDOWN   equ 0x26
CODEC_REG_ST	      equ 0x26

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

struc AC_CNTRL		    ;AC controller base class
{ .bus		      dd ?
  .devfn	      dd ?

  .vendor	      dd ?
  .dev_id	      dd ?
  .pci_cmd	      dd ?
  .pci_stat	      dd ?

  .codec_io_base      dd ?
  .codec_mem_base     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 ?
  .ctrl_setup	      dd ?
  .user_callback      dd ?
  .codec_read16       dd ?
  .codec_write16      dd ?

  .ctrl_read8	      dd ?
  .ctrl_read16	      dd ?
  .ctrl_read32	      dd ?

  .ctrl_write8	      dd ?
  .ctrl_write16       dd ?
  .ctrl_write32       dd ?
}

struc CODEC		   ;Audio Chip base class
{
  .chip_id	      dd ?
  .flags	      dd ?
  .status	      dd ?

  .ac_vendor_ids      dd ?    ;ac vendor id string
  .chip_ids	      dd ?    ;chip model string

  .shadow_flag	      dd ?
		      dd ?

  .regs 	      dw ?     ; codec registers
  .reg_master_vol     dw ?     ;0x02
  .reg_aux_out_vol    dw ?     ;0x04
  .reg_mone_vol       dw ?     ;0x06
  .reg_master_tone    dw ?     ;0x08
  .reg_beep_vol       dw ?     ;0x0A
  .reg_phone_vol      dw ?     ;0x0C
  .reg_mic_vol	      dw ?     ;0x0E
  .reg_line_in_vol    dw ?     ;0x10
  .reg_cd_vol	      dw ?     ;0x12
  .reg_video_vol      dw ?     ;0x14
  .reg_aux_in_vol     dw ?     ;0x16
  .reg_pcm_out_vol    dw ?     ;0x18
  .reg_rec_select     dw ?     ;0x1A
  .reg_rec_gain       dw ?     ;0x1C
  .reg_rec_gain_mic   dw ?     ;0x1E
  .reg_gen	      dw ?     ;0x20
  .reg_3d_ctrl	      dw ?     ;0X22
  .reg_page	      dw ?     ;0X24
  .reg_powerdown      dw ?     ;0x26
  .reg_ext_audio      dw ?     ;0x28
  .reg_ext_st	      dw ?     ;0x2a
  .reg_pcm_front_rate dw ?     ;0x2c
  .reg_pcm_surr_rate  dw ?     ;0x2e
  .reg_lfe_rate       dw ?     ;0x30
  .reg_pcm_in_rate    dw ?     ;0x32
		      dw ?     ;0x34
  .reg_cent_lfe_vol   dw ?     ;0x36
  .reg_surr_vol       dw ?     ;0x38
  .reg_spdif_ctrl     dw ?     ;0x3A
		      dw ?     ;0x3C
		      dw ?     ;0x3E
		      dw ?     ;0x40
		      dw ?     ;0x42
		      dw ?     ;0x44
		      dw ?     ;0x46
		      dw ?     ;0x48
		      dw ?     ;0x4A
		      dw ?     ;0x4C
		      dw ?     ;0x4E
		      dw ?     ;0x50
		      dw ?     ;0x52
		      dw ?     ;0x54
		      dw ?     ;0x56
		      dw ?     ;0x58
		      dw ?     ;0x5A
		      dw ?     ;0x5C
		      dw ?     ;0x5E
  .reg_page_0	      dw ?     ;0x60
  .reg_page_1	      dw ?     ;0x62
  .reg_page_2	      dw ?     ;0x64
  .reg_page_3	      dw ?     ;0x66
  .reg_page_4	      dw ?     ;0x68
  .reg_page_5	      dw ?     ;0x6A
  .reg_page_6	      dw ?     ;0x6C
  .reg_page_7	      dw ?     ;0x6E
		      dw ?     ;0x70
		      dw ?     ;0x72
		      dw ?     ;0x74
		      dw ?     ;0x76
		      dw ?     ;0x78
		      dw ?     ;0x7A
  .reg_vendor_id_1    dw ?     ;0x7C
  .reg_vendor_id_2    dw ?     ;0x7E


  .reset	      dd ?    ;virual
  .set_master_vol     dd ?
}

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, msgInit
	   call SysMsgBoardStr
     end if

	   call detect_controller
	   test eax, eax
	   jz .fail

     if DEBUG
	   mov esi,[ctrl.vendor_ids]
	   call SysMsgBoardStr
	   mov esi, [ctrl.ctrl_ids]
	   call SysMsgBoardStr

     end if

	   call init_controller
	   test eax, eax
	   jz .fail

	   call init_codec
	   test eax, eax
	   jz .fail

	   call reset_controller
	   call setup_codec

	   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 eax, VALID_IRQ
	   mov ebx, [ctrl.int_line]
	   mov esi, msgInvIRQ
	   bt eax, ebx
	   jnc .fail_msg
	   mov eax, ATTCH_IRQ
	   mov esi, msgAttchIRQ
	   bt eax, ebx
	   jnc .fail_msg

	   stdcall AttachIntHandler, ebx, ac97_irq, dword 0
.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
	   ret
@@:
	   cmp eax, DEV_STOP
	   jne @F
     if DEBUG
	   mov esi, msgStop
	   call SysMsgBoardStr
     end if
	   call stop
	   ret
@@:
	   cmp eax, DEV_CALLBACK
	   jne @F
	   mov ebx, [edi+input]
	   stdcall set_callback, [ebx]
	   ret
@@:
	   cmp eax, DEV_SET_MASTERVOL
	   jne @F
	   mov eax, [edi+input]
	   mov eax, [eax]
	   call set_master_vol	    ;eax= vol
	   ret
@@:
	   cmp eax, DEV_GET_MASTERVOL
	   jne @F
	   mov ebx, [edi+output]
	   stdcall get_master_vol, ebx
	   ret
;@@:
;           cmp eax, DEV_GET_INFO
;           jne @F
;           mov ebx, [edi+output]
;           stdcall get_dev_info, ebx
;           ret
@@:
.fail:
	   or eax, -1
	   ret
endp

restore   handle
restore   io_code
restore   input
restore   inp_size
restore   output
restore   out_size

align 4
proc ac97_irq

     if DEBUG_IRQ
	   mov esi, msgIRQ
	   call SysMsgBoardStr
     end if

	   mov edx, PCM_OUT_CR_REG
	   mov al, 0x10;               0x10
	   call [ctrl.ctrl_write8]

	   mov ax, 0x1c
	   mov edx, PCM_OUT_SR_REG
	   call [ctrl.ctrl_write16]

	   mov edx, PCM_OUT_CIV_REG
	   call [ctrl.ctrl_read8]

	   and eax, 0x1F
	   cmp eax, [civ_val]
	   je .skip

	   mov [civ_val], eax
	   dec eax
	   and eax, 0x1F
	   mov [ctrl.lvi_reg], eax

	   mov edx, PCM_OUT_LVI_REG
	   call [ctrl.ctrl_write8]

	   mov edx, PCM_OUT_CR_REG
	   mov ax, 0x11 	     ;0x1D
	   call [ctrl.ctrl_write8]

	   mov eax, [civ_val]
	   add eax, 1
	   and eax, 31
	   mov ebx, dword [buff_list+eax*4]

	   cmp [ctrl.user_callback], 0
	   je @f

	   stdcall [ctrl.user_callback], ebx
@@:
	   ret

.skip:
	   mov edx, PCM_OUT_CR_REG
	   mov ax, 0x11 	      ;0x1D
	   call [ctrl.ctrl_write8]
	   ret
endp

align 4
proc create_primary_buff

	   stdcall KernelAlloc, 0x10000
	   mov [ctrl.buffer], eax

	   mov edi, eax
	   mov ecx, 0x10000/4
	   xor eax, eax
	   cld
	   rep stosd

	   mov eax, [ctrl.buffer]
	   call GetPgAddr

	   mov ebx, 0xC0002000
	   mov ecx, 4
	   mov edi, pcmout_bdl
@@:
	   mov [edi], eax
	   mov [edi+4], ebx

	   mov [edi+32], eax
	   mov [edi+4+32], ebx

	   mov [edi+64], eax
	   mov [edi+4+64], ebx

	   mov [edi+96], eax
	   mov [edi+4+96], ebx

	   mov [edi+128], eax
	   mov [edi+4+128], ebx

	   mov [edi+160], eax
	   mov [edi+4+160], ebx

	   mov [edi+192], eax
	   mov [edi+4+192], ebx

	   mov [edi+224], eax
	   mov [edi+4+224], ebx

	   add eax, 0x4000
	   add edi, 8
	   loop @B

	   mov edi, buff_list
	   mov eax, [ctrl.buffer]
	   mov ecx, 4
@@:
	   mov [edi], eax
	   mov [edi+16], eax
	   mov [edi+32], eax
	   mov [edi+48], eax
	   mov [edi+64], eax
	   mov [edi+80], eax
	   mov [edi+96], eax
	   mov [edi+112], eax

	   add eax, 0x4000
	   add edi, 4
	   loop @B

	   mov eax, pcmout_bdl
	   mov ebx, eax
	   call GetPgAddr     ;eax
	   and ebx, 0xFFF
	   add eax, ebx

	   mov edx, PCM_OUT_BDL
	   call [ctrl.ctrl_write32]

	   mov eax, 16
	   mov [ctrl.lvi_reg], eax
	   mov edx, PCM_OUT_LVI_REG
	   call [ctrl.ctrl_write8]
	   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], edx
	   shr eax, 16
	   mov [ctrl.dev_id], eax

	   mov ebx, [edi+4]
	   mov [ctrl.ctrl_ids], ebx
	   mov esi, [edi+8]
	   mov [ctrl.ctrl_setup], esi

	   cmp edx, VID_INTEL
	   jne @F
	   mov [ctrl.vendor_ids], msg_Intel
	   ret
@@:
	   cmp edx, VID_NVIDIA
	   jne @F
	   mov [ctrl.vendor_ids], msg_NVidia
	   ret
@@:
.err:
	   xor eax, eax
	   mov [ctrl.vendor_ids], eax	  ;something  wrong ?
	   ret
endp

align 4
proc init_controller

	   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
	   call dword2str
	   call SysMsgBoardStr

	   mov esi, msgPciStat
	   call SysMsgBoardStr
	   mov eax, [ctrl.pci_stat]
	   call dword2str
	   call SysMsgBoardStr

	   mov esi, msgMixIsaIo
	   call SysMsgBoardStr

	   stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x10

	   call dword2str
	   call SysMsgBoardStr

	   and eax,0xFFFE
	   mov [ctrl.codec_io_base], eax

	   mov esi, msgCtrlIsaIo
	   call SysMsgBoardStr

	   stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x14

	   call dword2str
	   call SysMsgBoardStr

	   and eax, 0xFFC0
	   mov [ctrl.ctrl_io_base], eax

	   mov esi, msgMixMMIo
	   call SysMsgBoardStr

	   stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x18
	   mov [ctrl.codec_mem_base], eax

	   call dword2str
	   call SysMsgBoardStr

	   mov esi, msgCtrlMMIo
	   call SysMsgBoardStr

	   stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x1C
	   mov [ctrl.ctrl_mem_base], eax

	   call dword2str
	   call SysMsgBoardStr

if 0

;;patch for some ugly BIOS  ICH-ICH5 compatible
	   cmp [ctrl.vendor], VID_INTEL
	   jne .default

	   mov esi, msgIrqMap
	   call SysMsgBoardStr
	   stdcall PciRead8, 0, 0xF8, 0x61
	   and eax, 0xFF
	   call dword2str
	   call SysMsgBoardStr
	   btr eax, 7		      ;when bit 7 set remap disabled
	   jnc @F
	   xor eax, eax
	   jmp @F
end if

.default:
	   stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x3C
	   and eax, 0xFF
@@:
	   mov [ctrl.int_line], eax

	   stdcall PciRead8, [ctrl.bus], [ctrl.devfn], dword 0x41
	   and eax, 0xFF
	   mov [ctrl.cfg_reg], eax

	   call [ctrl.ctrl_setup]
	   xor eax, eax
	   inc eax
	   ret
endp

align 4
proc set_ICH
	   mov [ctrl.codec_read16],  codec_io_r16    ;virtual
	   mov [ctrl.codec_write16], codec_io_w16    ;virtual

	   mov [ctrl.ctrl_read8 ],  ctrl_io_r8	     ;virtual
	   mov [ctrl.ctrl_read16],  ctrl_io_r16      ;virtual
	   mov [ctrl.ctrl_read32],  ctrl_io_r32      ;virtual

	   mov [ctrl.ctrl_write8 ], ctrl_io_w8	     ;virtual
	   mov [ctrl.ctrl_write16], ctrl_io_w16      ;virtual
	   mov [ctrl.ctrl_write32], ctrl_io_w32      ;virtual
	   ret
endp

PG_SW		 equ 0x003
PG_NOCACHE	 equ 0x018

align 4
proc set_ICH4

	   stdcall MapIoMem,[ctrl.codec_mem_base],0x1000,PG_SW+PG_NOCACHE
	   mov [ctrl.codec_mem_base], eax

	   stdcall MapIoMem,[ctrl.ctrl_mem_base],0x1000,PG_SW+PG_NOCACHE
	   mov [ctrl.ctrl_mem_base], eax

	   mov [ctrl.codec_read16],  codec_mem_r16    ;virtual
	   mov [ctrl.codec_write16], codec_mem_w16    ;virtual

	   mov [ctrl.ctrl_read8 ],  ctrl_mem_r8       ;virtual
	   mov [ctrl.ctrl_read16],  ctrl_mem_r16      ;virtual
	   mov [ctrl.ctrl_read32],  ctrl_mem_r32      ;virtual

	   mov [ctrl.ctrl_write8 ], ctrl_mem_w8       ;virtual
	   mov [ctrl.ctrl_write16], ctrl_mem_w16      ;virtual
	   mov [ctrl.ctrl_write32], ctrl_mem_w32      ;virtual
	   ret
endp

align 4
proc reset_controller

	   xor eax, eax
	   mov edx, PCM_IN_CR_REG
	   call [ctrl.ctrl_write8]

	   mov edx, PCM_OUT_CR_REG
	   call [ctrl.ctrl_write8]

	   mov edx, MC_IN_CR_REG
	   call [ctrl.ctrl_write8]

	   mov eax, RR
	   mov edx, PCM_IN_CR_REG
	   call [ctrl.ctrl_write8]

	   mov edx, PCM_OUT_CR_REG
	   call [ctrl.ctrl_write8]

	   mov edx, MC_IN_CR_REG
	   call [ctrl.ctrl_write8]
	   ret
endp

align 4
proc init_codec
	   locals
	     counter dd ?
	   endl

	   mov esi, msgControl
	   call SysMsgBoardStr

	   mov edx, GLOB_CTRL
	   call [ctrl.ctrl_read32]
	   call dword2str
	   call SysMsgBoardStr

	   mov esi, msgStatus
	   call SysMsgBoardStr

	   mov edx, CTRL_STAT
	   call [ctrl.ctrl_read32]
	   push eax
	   call dword2str
	   call SysMsgBoardStr
	   pop eax
	   cmp eax, 0xFFFFFFFF
	   je .err

	   test eax, CTRL_ST_CREADY
	   jnz .ready

	   call reset_codec
	   test eax, eax
	   jz .err

.ready:
	   xor edx, edx     ;ac_reg_0
	   call [ctrl.codec_write16]

	   xor eax, eax
	   mov edx, CODEC_REG_POWERDOWN
	   call [ctrl.codec_write16]

	   mov [counter], 200	  ; total 200*5 ms = 1s
.wait:
	   mov eax, 5000   ; wait 5 ms
	   call StallExec

	   mov edx, CODEC_REG_POWERDOWN
	   call [ctrl.codec_read16]
	   and eax, 0x0F
	   cmp eax, 0x0F
	   jz .done

	   sub [counter] , 1
	   jnz .wait
.err:
	   xor eax, eax        ; timeout error
	   ret
.done:
	   mov eax, 2	   ;force set 16-bit 2-channel PCM
	   mov edx, GLOB_CTRL
	   call [ctrl.ctrl_write32]
	   mov eax, 5000   ; wait 5 ms
	   call StallExec

	   call detect_codec

	   xor eax, eax
	   inc eax
	   ret
endp

align 4
proc reset_codec
	   mov edx, GLOB_CTRL
	   call [ctrl.ctrl_read32]

	   test eax, 0x02
	   jz .cold

	   call warm_reset
	   jnc .ok
.cold:
	   call cold_reset
	   jnc .ok

     if DEBUG
	   mov esi, msgCFail
	   call SysMsgBoardStr
	   end if
	   xor eax, eax     ; timeout error
	   ret
.ok:
     if DEBUG
	   mov esi, msgResetOk
	   call SysMsgBoardStr
     end if

	   xor eax, eax
	   inc eax
	   ret
endp

align 4
proc warm_reset
	   locals
	     counter dd ?
	   endl

	   mov eax, 0x06
	   mov edx, GLOB_CTRL
	   call [ctrl.ctrl_write32]

     if DEBUG
	   mov esi, msgWarm
	   call SysMsgBoardStr
     end if

	   mov [counter], 10	; total 10*100 ms = 1s
.wait:
	   mov eax, 100000    ; wait 100 ms
	   call StallExec

	   mov edx, CTRL_STAT
	   call [ctrl.ctrl_read32]
	   test eax, CTRL_ST_CREADY
	   jnz .ok

	   dec [counter]
	   jnz .wait

     if DEBUG
	   mov esi, msgWRFail
	   call SysMsgBoardStr
     end if
.fail:
	   stc
	   ret
.ok:
	   clc
	   ret
endp

align 4
proc cold_reset
	   locals
	     counter dd ?
	   endl

	   mov eax, 0x02
	   mov edx, GLOB_CTRL
	   call [ctrl.ctrl_write32]

     if DEBUG
	   mov esi, msgCold
	   call SysMsgBoardStr
     end if

	   mov eax, 400000     ; wait 400 ms
	   call StallExec

	   mov [counter], 16	; total 20*100 ms = 2s
.wait:

	   mov edx, CTRL_STAT
	   call [ctrl.ctrl_read32]
	   test eax, CTRL_ST_CREADY
	   jnz .ok

	   mov eax, 100000    ; wait 100 ms
	   call StallExec

	   dec [counter]
	   jnz .wait

     if DEBUG
	   mov esi, msgCRFail
	   call SysMsgBoardStr
     end if

.fail:
	   stc
	   ret
.ok:
	   mov esi, msgControl
	   call SysMsgBoardStr

	   mov edx, GLOB_CTRL
	   call [ctrl.ctrl_read32]
	   call dword2str
	   call SysMsgBoardStr

	   mov esi, msgStatus
	   call SysMsgBoardStr

	   mov edx, CTRL_STAT
	   call [ctrl.ctrl_read32]
	   push eax
	   call dword2str
	   call SysMsgBoardStr
	   pop eax

	   test eax, CTRL_ST_CREADY
	   jz .fail
	   clc
	   ret
endp

align 4
play:
	   mov eax, 16
	   mov [ctrl.lvi_reg], eax
	   mov edx, PCM_OUT_LVI_REG
	   call [ctrl.ctrl_write8]

	   mov edx, PCM_OUT_CR_REG
	   mov ax, 0x1D
	   call [ctrl.ctrl_write8]
	   xor eax, eax
	   ret

align 4
stop:
	   mov edx, PCM_OUT_CR_REG
	   mov ax, 0x0
	   call [ctrl.ctrl_write8]

	   mov ax, 0x1c
	   mov edx, PCM_OUT_SR_REG
	   call [ctrl.ctrl_write16]
	   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 ebx, [ctrl.codec_io_base]
	   mov ecx, [ctrl.ctrl_io_base]
	   mov edx, [ctrl.codec_mem_base]
	   mov edi, [ctrl.ctrl_mem_base]

	   mov [CTRL_INFO.irq], eax
	   mov [CTRL_INFO.codec_io_base], ebx
	   mov [CTRL_INFO.ctrl_io_base], ecx
	   mov [CTRL_INFO.codec_mem_base], edx
	   mov [CTRL_INFO.ctrl_mem_base], edi

	   mov eax, [codec.chip_id]
	   mov [CTRL_INFO.codec_id], eax

	   mov edx, GLOB_CTRL
	   call [ctrl.ctrl_read32]
	   mov [CTRL_INFO.glob_cntrl], eax

	   mov edx, CTRL_STAT
	   call [ctrl.ctrl_read32]
	   mov [CTRL_INFO.glob_sta], eax

	   mov ebx, [ctrl.pci_cmd]
	   mov [CTRL_INFO.pci_cmd], ebx
	   ret
endp

align 4
proc set_callback stdcall, handler:dword
	   mov eax, [handler]
	   mov [ctrl.user_callback], eax
	   ret
endp

align 4
proc codec_read stdcall, ac_reg:dword	   ; reg = edx, reval = eax

	   mov edx, [ac_reg]

	   mov ebx, edx
	   shr ebx, 1
	   bt [codec.shadow_flag], ebx
	   jc .use_shadow

	   call [ctrl.codec_read16]  ;change edx !!!
	   mov ecx, eax

	   mov edx, CTRL_STAT
	   call [ctrl.ctrl_read32]
	   test eax, CTRL_ST_RCS
	   jz .read_ok

	   mov edx, CTRL_STAT
	   call [ctrl.ctrl_write32]
	   xor eax,eax
	   not eax  ;timeout
	   ret
.read_ok:
	   mov edx, [ac_reg]
	   mov [codec.regs+edx], cx
	   bts [codec.shadow_flag], ebx
	   mov eax, ecx
	   ret
.use_shadow:
	   movzx eax, word [codec.regs+edx]
	   ret
endp

align 4
proc codec_write stdcall, ac_reg:dword
	   push eax
	   call check_semafore
	   and eax, eax
	   jz .err
	   pop eax

	   mov esi, [ac_reg]
	   mov edx, esi
	   call [ctrl.codec_write16]
	   mov [codec.regs+esi], ax
	   shr esi, 1
	   bts [codec.shadow_flag], esi
	   ret
.err:
	   pop eax
	   ret
endp

align 4
proc codec_check_ready

	   mov edx, CTRL_ST
	   call [ctrl.ctrl_read32]
	   and eax, CTRL_ST_CREADY
	   jz .not_ready

	   xor eax, wax
	   inc eax
	   ret
.not_ready:
	   xor eax, eax
	   ret
endp

align 4
proc check_semafore
	   local counter:DWORD

	   mov [counter], 100
.l1:
	   mov edx, CTRL_CAS
	   call [ctrl.ctrl_read8]
	   and eax, CAS_FLAG
	   jz .ok

	   mov eax, 1
	   call StallExec
	   sub [counter], 1
	   jnz .l1
	   xor eax, eax
	   ret
align 4
.ok:
	   xor eax,eax
	   inc eax
	   ret
endp

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

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;          CONTROLLER IO functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

align 4
proc codec_io_r16
	   add edx, [ctrl.codec_io_base]
	   in  ax, dx
	   ret
endp

align 4
proc codec_io_w16
	   add edx, [ctrl.codec_io_base]
	   out dx, ax
	   ret
endp

align 4
proc ctrl_io_r8
	   add edx, [ctrl.ctrl_io_base]
	   in  al, dx
	   ret
endp

align 4
proc ctrl_io_r16
	   add edx, [ctrl.ctrl_io_base]
	   in  ax, dx
	   ret
endp

align 4
proc ctrl_io_r32
	   add edx, [ctrl.ctrl_io_base]
	   in  eax, dx
	   ret
endp

align 4
proc ctrl_io_w8
	   add edx, [ctrl.ctrl_io_base]
	   out dx, al
	   ret
endp

align 4
proc ctrl_io_w16
	   add edx, [ctrl.ctrl_io_base]
	   out dx, ax
	   ret
endp

align 4
proc ctrl_io_w32
	   add edx, [ctrl.ctrl_io_base]
	   out dx, eax
	   ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;         MEMORY MAPPED IO    (os depended)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

align 4
proc codec_mem_r16
	   add edx, [ctrl.codec_mem_base]
	   mov ax, word [edx]
	   ret
endp

align 4
proc codec_mem_w16
	   add edx, [ctrl.codec_mem_base]
	   mov word [edx], ax
	   ret
endp

align 4
proc ctrl_mem_r8
	   add edx, [ctrl.ctrl_mem_base]
	   mov al, [edx]
	   ret
endp

align 4
proc ctrl_mem_r16
	   add edx, [ctrl.ctrl_mem_base]
	   mov ax, [edx]
	   ret
endp

align 4
proc ctrl_mem_r32
	   add edx, [ctrl.ctrl_mem_base]
	   mov eax, [edx]
	   ret
endp

align 4
proc ctrl_mem_w8
	   add edx, [ctrl.ctrl_mem_base]
	   mov [edx], al
	   ret
endp

align 4
proc ctrl_mem_w16
	   add edx, [ctrl.ctrl_mem_base]
	   mov [edx], ax
	   ret
endp

align 4
proc ctrl_mem_w32
	   add edx, [ctrl.ctrl_mem_base]
	   mov [edx], eax
	   ret
endp

align 4
dword2str:
      mov  esi, hex_buff
      mov ecx, -8
@@:
      rol eax, 4
      mov ebx, eax
      and ebx, 0x0F
      mov bl, [ebx+hexletters]
      mov [8+esi+ecx], bl
      inc ecx
      jnz @B
      ret

hexletters   db '0123456789ABCDEF'
hex_buff     db 8 dup(0),13,10,0


include "codec.inc"

align 4
devices dd (CTRL_ICH  shl 16)+VID_INTEL,msg_ICH, set_ICH
	dd (CTRL_ICH0 shl 16)+VID_INTEL,msg_ICH0,set_ICH
	dd (CTRL_ICH2 shl 16)+VID_INTEL,msg_ICH2,set_ICH
	dd (CTRL_ICH3 shl 16)+VID_INTEL,msg_ICH3,set_ICH
	dd (CTRL_ICH4 shl 16)+VID_INTEL,msg_ICH4,set_ICH4
	dd (CTRL_ICH5 shl 16)+VID_INTEL,msg_ICH5,set_ICH4
	dd (CTRL_ICH6 shl 16)+VID_INTEL,msg_ICH6,set_ICH4
	dd (CTRL_ICH7 shl 16)+VID_INTEL,msg_ICH7,set_ICH4

	dd (CTRL_NFORCE  shl 16)+VID_NVIDIA,msg_NForce, set_ICH
	dd (CTRL_NFORCE2 shl 16)+VID_NVIDIA,msg_NForce2,set_ICH
	dd (CTRL_NFORCE3 shl 16)+VID_NVIDIA,msg_NForce3,set_ICH
	dd (CTRL_MCP04	 shl 16)+VID_NVIDIA,msg_MCP04,set_ICH
	dd (CTRL_CK804	 shl 16)+VID_NVIDIA,msg_CK804,set_ICH
	dd (CTRL_CK8	 shl 16)+VID_NVIDIA,msg_CK8,set_ICH
	dd (CTRL_CK8S	 shl 16)+VID_NVIDIA,msg_CK8S,set_ICH
	dd (CTRL_MCP51	 shl 16)+VID_NVIDIA,msg_MCP51,set_ICH

	dd 0	;terminator


version      dd (5 shl 16) or (API_VERSION and 0xFFFF)

msg_ICH      db '802801AA (ICH)',  13,10, 0
msg_ICH0     db '802801AB (ICH0)', 13,10, 0
msg_ICH2     db '802801BA (ICH2)', 13,10, 0
msg_ICH3     db '802801CA (ICH3)', 13,10, 0
msg_ICH4     db '802801DB (ICH4)', 13,10, 0
msg_ICH5     db '802801EB (ICH5)', 13,10, 0
msg_ICH6     db '802801FB (ICH6)', 13,10, 0
msg_ICH7     db '802801GB (ICH7)', 13,10, 0
msg_Intel    db 'Intel ', 0

msg_NForce   db 'NForce',      13,10, 0
msg_NForce2  db 'NForce 2',    13,10, 0
msg_NForce3  db 'NForce 3',    13,10, 0
msg_MCP04    db 'NForce MCP04',13,10, 0
msg_CK804    db 'NForce CK804',13,10, 0
msg_CK8      db 'NForce CK8',  13,10, 0
msg_CK8S     db 'NForce CK8S', 13,10, 0
msg_MCP51    db 'NForce MCP51',13,10, 0

msg_NVidia   db 'NVidia', 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
;msgNotify    db 'call notify',13,10,0
msgIRQ	     db 'AC97 IRQ', 13,10,0
msgInitCtrl  db 'init controller',13,10,0
;msgInitCodec db 'init codec',13,10,0
msgPrimBuff  db 'create primary buffer ...',0
msgDone      db 'done',13,10,0
msgRemap     db 'Remap IRQ',13,10,0
;msgReg       db 'set service handler',13,10,0
msgOk	     db 'service installed',13,10,0
msgCold      db 'cold reset',13,10,0
msgWarm      db 'warm reset',13,10,0
msgWRFail    db 'warm reset failed',13,10,0
msgCRFail    db 'cold reset failed',13,10,0
msgCFail     db 'codec not ready',13,10,0
msgResetOk   db 'reset complete',13,10,0
msgStatus    db 'global status   ',0
msgControl   db 'global control  ',0
msgPciCmd    db 'PCI command     ',0
msgPciStat   db 'PCI status      ',0
msgCtrlIsaIo db 'controller io base   ',0
msgMixIsaIo  db 'codec io base        ',0
msgCtrlMMIo  db 'controller mmio base ',0
msgMixMMIo   db 'codec mmio base      ',0
msgIrqMap    db 'AC97 irq map as      ',0

section '.data' data readable writable align 16

pcmout_bdl	 rq 32
buff_list	 rd 32

codec CODEC
ctrl AC_CNTRL

lpc_bus  rd 1
civ_val  rd 1