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

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
BIT5  EQU 0x00000020
BIT10 EQU 0x00000400

VID_VIA 	  equ 0x1106

CTRL_VT82C686	  equ 0x3058
CTRL_VT8233_5	  equ 0x3059


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


;VIA host controller registers set
;; common offsets
VIA_REG_OFFSET_STATUS	     equ   0x00    ;; byte - channel status
  VIA_REG_STAT_ACTIVE		   equ	 0x80	 ;; RO
  VIA_REG_STAT_PAUSED		   equ	 0x40	 ;; RO
  VIA_REG_STAT_TRIGGER_QUEUED	   equ	 0x08	 ;; RO
  VIA_REG_STAT_STOPPED		   equ	 0x04	 ;; RWC
  VIA_REG_STAT_EOL		   equ	 0x02	 ;; RWC
  VIA_REG_STAT_FLAG		   equ	 0x01	 ;; RWC
VIA_REG_OFFSET_CONTROL	     equ   0x01    ;; byte - channel control
  VIA_REG_CTRL_START		   equ	 0x80	 ;; WO
  VIA_REG_CTRL_TERMINATE	   equ	 0x40	 ;; WO
  VIA_REG_CTRL_AUTOSTART	   equ	 0x20
  VIA_REG_CTRL_PAUSE		   equ	 0x08	 ;; RW
  VIA_REG_CTRL_INT_STOP 	   equ	 0x04
  VIA_REG_CTRL_INT_EOL		   equ	 0x02
  VIA_REG_CTRL_INT_FLAG 	   equ	 0x01
  VIA_REG_CTRL_RESET		   equ	 0x01	 ;; RW - probably reset? undocumented
  VIA_REG_CTRL_INT		   equ	(VIA_REG_CTRL_INT_FLAG or \
					 VIA_REG_CTRL_INT_EOL or \
					 VIA_REG_CTRL_AUTOSTART)
VIA_REG_OFFSET_TYPE	     equ   0x02    ;; byte - channel type (686 only)
  VIA_REG_TYPE_AUTOSTART	   equ	 0x80	 ;; RW - autostart at EOL
  VIA_REG_TYPE_16BIT		   equ	 0x20	 ;; RW
  VIA_REG_TYPE_STEREO		   equ	 0x10	 ;; RW
  VIA_REG_TYPE_INT_LLINE	   equ	 0x00
  VIA_REG_TYPE_INT_LSAMPLE	   equ	 0x04
  VIA_REG_TYPE_INT_LESSONE	   equ	 0x08
  VIA_REG_TYPE_INT_MASK 	   equ	 0x0c
  VIA_REG_TYPE_INT_EOL		   equ	 0x02
  VIA_REG_TYPE_INT_FLAG 	   equ	 0x01
VIA_REG_OFFSET_TABLE_PTR     equ   0x04    ;; dword - channel table pointer
VIA_REG_OFFSET_CURR_PTR      equ   0x04    ;; dword - channel current pointer
VIA_REG_OFFSET_STOP_IDX      equ   0x08    ;; dword - stop index, channel type, sample rate
  VIA8233_REG_TYPE_16BIT	   equ	 0x00200000	 ;; RW
  VIA8233_REG_TYPE_STEREO	   equ	 0x00100000	 ;; RW
VIA_REG_OFFSET_CURR_COUNT    equ   0x0c    ;; dword - channel current count (24 bit)
VIA_REG_OFFSET_CURR_INDEX    equ   0x0f    ;; byte - channel current index (for via8233 only)


VIADEV_PLAYBACK 	equ   0x00
VIADEV_CAPTURE		equ   0x10
VIADEV_FM		equ   0x20

;; AC'97 ;;
VIA_REG_AC97		 equ   0x80    ; dword
  VIA_REG_AC97_CODEC_ID_MASK	   equ	0xC0000000 ;(3<<30)
  VIA_REG_AC97_CODEC_ID_SHIFT	   equ	30
  VIA_REG_AC97_CODEC_ID_PRIMARY    equ	0x00
  VIA_REG_AC97_CODEC_ID_SECONDARY  equ	0x01
  VIA_REG_AC97_SECONDARY_VALID	   equ	0x08000000 ;(1<<27)
  VIA_REG_AC97_PRIMARY_VALID	   equ	0x02000000 ;(1<<25)
  VIA_REG_AC97_BUSY		   equ	0x01000000 ;(1<<24)
  VIA_REG_AC97_READ		   equ	0x00800000 ;(1<<23)
  VIA_REG_AC97_CMD_SHIFT	   equ	16
  VIA_REG_AC97_CMD_MASK 	   equ	0x7E
  VIA_REG_AC97_DATA_SHIFT	   equ	0
  VIA_REG_AC97_DATA_MASK	   equ	0xFFFF

VIA_REG_SGD_SHADOW	 equ   0x84    ; dword

;; via8233-specific registers ;;
VIA_REG_OFS_PLAYBACK_VOLUME_L	equ  0x02    ;; byte
VIA_REG_OFS_PLAYBACK_VOLUME_R	equ  0x03    ;; byte
VIA_REG_OFS_MULTPLAY_FORMAT	equ  0x02    ;; byte - format and channels
  VIA_REG_MULTPLAY_FMT_8BIT	     equ  0x00
  VIA_REG_MULTPLAY_FMT_16BIT	     equ  0x80
  VIA_REG_MULTPLAY_FMT_CH_MASK	     equ  0x70	  ;; # channels << 4 (valid = 1,2,4,6)
VIA_REG_OFS_CAPTURE_FIFO	equ  0x02    ;; byte - bit 6 = fifo  enable
  VIA_REG_CAPTURE_FIFO_ENABLE	     equ  0x40

VIA_DXS_MAX_VOLUME		equ  31      ;; max. volume (attenuation) of reg 0x32/33

VIA_TBL_BIT_FLAG	  equ	0x40000000
VIA_TBL_BIT_EOL 	  equ	0x80000000

;; pci space ;;
VIA_ACLINK_STAT 	  equ	0x40
  ;...
  VIA_ACLINK_C00_READY		   equ	 0x01 ; primary codec ready
VIA_ACLINK_CTRL 	  equ	0x41
  VIA_ACLINK_CTRL_ENABLE	   equ	 0x80 ; 0: disable, 1: enable
  VIA_ACLINK_CTRL_RESET 	   equ	 0x40 ; 0: assert, 1: de-assert
  VIA_ACLINK_CTRL_SYNC		   equ	 0x20 ; 0: release SYNC, 1: force SYNC hi
  VIA_ACLINK_CTRL_SDO		   equ	 0x10 ; 0: release SDO, 1: force SDO hi
  VIA_ACLINK_CTRL_VRA		   equ	 0x08 ; 0: disable VRA, 1: enable VRA
  VIA_ACLINK_CTRL_PCM		   equ	 0x04 ; 0: disable PCM, 1: enable PCM
  VIA_ACLINK_CTRL_FM		   equ	 0x02 ; via686 only
  VIA_ACLINK_CTRL_SB		   equ	 0x01 ; via686 only
  VIA_ACLINK_CTRL_INIT		   equ	(VIA_ACLINK_CTRL_ENABLE or \
					 VIA_ACLINK_CTRL_RESET or \
					 VIA_ACLINK_CTRL_PCM or \
					 VIA_ACLINK_CTRL_VRA)
VIA_FUNC_ENABLE 	  equ	0x42
  VIA_FUNC_MIDI_PNP		   equ	 0x80 ; FIXME: it's 0x40 in the datasheet!
  VIA_FUNC_MIDI_IRQMASK 	   equ	 0x40 ; FIXME: not documented!
  VIA_FUNC_RX2C_WRITE		   equ	 0x20
  VIA_FUNC_SB_FIFO_EMPTY	   equ	 0x10
  VIA_FUNC_ENABLE_GAME		   equ	 0x08
  VIA_FUNC_ENABLE_FM		   equ	 0x04
  VIA_FUNC_ENABLE_MIDI		   equ	 0x02
  VIA_FUNC_ENABLE_SB		   equ	 0x01
VIA_PNP_CONTROL 	  equ	0x43
VIA_FM_NMI_CTRL 	  equ	0x48
VIA8233_VOLCHG_CTRL	  equ	0x48
VIA8233_SPDIF_CTRL	  equ	0x49
  VIA8233_SPDIF_DX3		   equ	 0x08
  VIA8233_SPDIF_SLOT_MASK	   equ	 0x03
  VIA8233_SPDIF_SLOT_1011	   equ	 0x00
  VIA8233_SPDIF_SLOT_34 	   equ	 0x01
  VIA8233_SPDIF_SLOT_78 	   equ	 0x02
  VIA8233_SPDIF_SLOT_69 	   equ	 0x03
;] Asper


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     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

	   stdcall  PciWrite8, 0, 0xF8, 0x61, IRQ_LINE	;remap IRQ

	   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_VIA, 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_VIA
	   locals
	     status db 0
	   endl

	   mov	    edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_STATUS
	   call     [ctrl.ctrl_read8]
	   test     al, VIA_REG_STAT_ACTIVE
	   jz	    @f

	   and	    al, VIA_REG_STAT_EOL or VIA_REG_STAT_FLAG or VIA_REG_STAT_STOPPED
	   mov	    byte [status], al

	   mov	    ebx, dword [buff_list]
	   cmp	    [ctrl.user_callback], 0
	   je	    @f
	   stdcall  [ctrl.user_callback], ebx
       @@:
	   mov	    al, byte [status]		;; ack ;;
	   mov	    edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_STATUS
	   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	    edi, pcmout_bdl
	   stosd
	   mov	    eax, 0x80004000
	   stosd

	   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

	   stdcall  channel_reset, VIADEV_PLAYBACK
	   stdcall  codec_check_ready

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

	   mov	    edx, VIADEV_PLAYBACK  +VIA_REG_OFFSET_TABLE_PTR
	   call     [ctrl.ctrl_write32]

	   stdcall  codec_check_ready

	   mov	    edx, VIADEV_PLAYBACK  +VIA_REG_OFS_PLAYBACK_VOLUME_L
	   mov	    eax, 7;31
	   call     [ctrl.ctrl_write8]

	   mov	    edx, VIADEV_PLAYBACK  +VIA_REG_OFS_PLAYBACK_VOLUME_R
	   mov	    eax, 7;31
	   call     [ctrl.ctrl_write8]

	   mov	    edx, VIADEV_PLAYBACK  +VIA_REG_OFFSET_STOP_IDX
	   mov	    eax, VIA8233_REG_TYPE_16BIT or VIA8233_REG_TYPE_STEREO or 0xfffff or 0xff000000
	   mov	  [ctrl.lvi_reg], 16;0xF;eax
	   call     [ctrl.ctrl_write32]

	   stdcall  codec_check_ready
	   ret
endp


proc channel_reset channel:dword
	   mov	    esi, dword [channel]
	   mov	    edx, esi
	   add	    edx, VIA_REG_OFFSET_CONTROL
	   mov	    eax, VIA_REG_CTRL_PAUSE or VIA_REG_CTRL_TERMINATE or VIA_REG_CTRL_RESET
	   call     [ctrl.ctrl_write8]

	   mov	    edx, esi
	   add	    edx, VIA_REG_OFFSET_CONTROL
	   call     [ctrl.ctrl_read8]

	   mov	    eax, 50000	 ; wait 50 ms
	   call     StallExec
	   ; disable interrupts
	   mov	    edx, esi
	   add	    edx, VIA_REG_OFFSET_CONTROL
	   xor	    eax, eax
	   call     [ctrl.ctrl_write8]

	   ; clear interrupts
	   mov	    edx, esi
	   add	    edx, VIA_REG_OFFSET_STATUS
	   mov	    eax, 0x03
	   call     [ctrl.ctrl_write8]

	;outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */
	  ; mov      edx, esi                  ;; for via686
	  ; add      edx, VIA_REG_OFFSET_TYPE
	  ; mov      eax, 0x03
	  ; call     [ctrl.ctrl_write8]

	;; outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR));
	   ;mov      edx, esi
	   ;add      edx, VIA_REG_OFFSET_CURR_PTR
	   ;xor      eax, eax
	   ;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_VIA
	   jne @F
	   mov [ctrl.vendor_ids], msg_VIA
	   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, msgCtrlIsaIo
	   call     SysMsgBoardStr
	   stdcall  PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x10
	   call     dword2str
	   call     SysMsgBoardStr

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

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

	   ;stdcall  PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_FUNC_ENABLE ;0x42
	   ;mov      byte [old_legacy], al

	   ;stdcall  PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_PNP_CONTROL ;0x43
	   ;mov      byte [old_legacy_cfg], al

	   ;mov      al, VIA_FUNC_ENABLE_SB or VIA_FUNC_ENABLE_FM
	   ;xor      al, 0xFF
	   ;and      al, byte [old_legacy]
	   ;and      eax, 0xFF
	   ;stdcall  PciWrite8, [ctrl.bus], [ctrl.devfn], dword VIA_FUNC_ENABLE, eax ;0x42
	   ;mov      byte [old_legacy], al

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

align 4
proc set_VIA
	   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


align 4
proc init_codec
	   locals
	     counter dd ?
	   endl

	   mov	    esi, msgControl
	   call     SysMsgBoardStr
	   stdcall  PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL
	   and	    eax, 0xFF
	   call     dword2str
	   call     SysMsgBoardStr

	   mov	    esi, msgStatus
	   call     SysMsgBoardStr
	   stdcall  PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_STAT
	   and	    eax, 0xFF
	   push     eax
	   call     dword2str
	   call     SysMsgBoardStr
	   pop	    eax

	   test     eax, VIA_ACLINK_C00_READY
	   jz	    .ready

	   call     reset_codec
	   test     eax, eax
	   jz	    .err

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

.err:
	   xor	    eax, eax	      ; timeout error
	   ret

.done:
	   call     detect_codec

	   xor	    eax, eax
	   inc	    eax
	   ret
endp

align 4
proc reset_codec
	   stdcall  PciWrite8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL, \
			       VIA_ACLINK_CTRL_ENABLE or VIA_ACLINK_CTRL_RESET or VIA_ACLINK_CTRL_SYNC
	   mov	    eax, 100000    ; wait 100 ms
	   call     StallExec
.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 cold_reset
	   locals
	     counter dd ?
	   endl

	   stdcall  PciWrite8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL, dword 0

     if DEBUG
	   mov	 esi, msgCold
	   call  SysMsgBoardStr
     end if

	   mov	    eax, 100000     ; wait 100 ms ;400000     ; wait 400 ms
	   call     StallExec

	   ;; ACLink on, deassert ACLink reset, VSR, SGD data out
	   ;; note - FM data out has trouble with non VRA codecs !!
	   stdcall  PciWrite8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL, dword VIA_ACLINK_CTRL_INIT

	   mov	    [counter], 16    ; total 20*100 ms = 2s
.wait:
	   stdcall  PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_STAT
	   test     eax, VIA_ACLINK_C00_READY
	   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
	   stdcall  PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL
	   call     dword2str
	   call     SysMsgBoardStr

	   mov	    esi, msgStatus
	   call     SysMsgBoardStr
	   stdcall  PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_STAT
	   and	    eax, 0xFF
	   push     eax
	   call     dword2str
	   call     SysMsgBoardStr
	   pop	    eax

	   test     eax, VIA_ACLINK_C00_READY ;CTRL_ST_CREADY
	   jz	    .fail
	   clc
	   ret
endp

align 4
play:
	   mov	    edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_STOP_IDX
	   mov	    eax, VIA8233_REG_TYPE_16BIT or VIA8233_REG_TYPE_STEREO or 0xfffff or 0xff000000
	   mov	 [ctrl.lvi_reg], 16
	   call     [ctrl.ctrl_write32]

	   mov	    eax, VIA_REG_CTRL_INT
	   or	    eax, VIA_REG_CTRL_START
	   mov	    edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_CONTROL
	   call     [ctrl.ctrl_write8]

	   xor	    eax, eax
	   ret

align 4
stop:
	   mov	    eax, VIA_REG_CTRL_INT
	   or	    eax, VIA_REG_CTRL_TERMINATE
	   mov	    edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_CONTROL
	   call     [ctrl.ctrl_write8]

	   stdcall  channel_reset, VIADEV_PLAYBACK
	   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	    ecx, [ctrl.ctrl_io_base]
	   mov	    [CTRL_INFO.irq], eax
	   mov	    [CTRL_INFO.ctrl_io_base], ecx

	   xor	    eax, eax
	   ;mov      edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_TABLE_PTR
	   ;call     [ctrl.ctrl_read32]
	   mov	    [CTRL_INFO.codec_io_base], eax
	   ;mov      edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_STOP_IDX
	   ;call     [ctrl.ctrl_read32]
	   mov	    [CTRL_INFO.codec_mem_base], eax
	   ;mov      edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_CURR_COUNT
	   ;call     [ctrl.ctrl_read32]
	   mov	    [CTRL_INFO.ctrl_mem_base], eax

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

	   mov	    edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_CONTROL
	   call     [ctrl.ctrl_read8]
	   and	    eax, 0xFF
	   mov	    [CTRL_INFO.glob_cntrl], eax

	   mov	    edx, VIADEV_PLAYBACK   +VIA_REG_OFFSET_STATUS
	   call     [ctrl.ctrl_read8]
	   and	    eax, 0xFF
	   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_check_ready stdcall
	   locals
	     counter dd ?
	   endl

	   mov	    [counter], 1000	; total 1000*1 ms = 1s
.wait:
	   call     [ctrl.codec_read16]
	   test     eax, VIA_REG_AC97_BUSY
	   jz	    .ok

	   mov	    eax, 1000	; wait 1 ms
	   call     StallExec

	   sub	    [counter] , 1
	   jnz	    .wait
.err:
	   mov	    eax, -1
	   ret
.ok:
	   and	    eax, 0xFFFF
	   ret
endp


align 4
proc codec_valid stdcall
	   stdcall  codec_check_ready
	   ret
endp

align 4
proc codec_read stdcall, ac_reg:dword	   ; reg = edx, reval = eax
	   locals
	     counter dd ?
	   endl

	   ;Use only primary codec.
	   mov	    eax, [ac_reg]
	   and	    eax, 0x7F
	   shl	    eax, VIA_REG_AC97_CMD_SHIFT
	   or	    eax, VIA_REG_AC97_PRIMARY_VALID or VIA_REG_AC97_READ

	   mov	    [counter], 3     ; total 3*20 ms = 60ms
.wait:
	   push     eax
	   call     [ctrl.codec_write16]

	   mov	    eax, 20000	 ; wait 20 ms
	   call     StallExec

	   stdcall  codec_valid,
	   cmp	    eax, 0
	   pop	    eax
	   jge	    .ok

	   sub	    [counter] , 1
	   jnz	    .wait
	   jmp	    .err

.ok:
	   mov	    eax, 25000	 ; wait 25 ms
	   call     StallExec

	   call     [ctrl.codec_read16]  ;change edx !!!
	   and	    eax, 0xFFFF
	   ret
.err:
     if DEBUG
	   mov	 esi, msgCInvalid
	   call  SysMsgBoardStr
     end if
	   mov	    eax, -1	   ; invalid codec error
	   ret
endp

align 4
proc codec_write stdcall, ac_reg:dword
	   ;Use only primary codec.
	   mov	    esi, [ac_reg]
	   mov	    edx, esi
	   shl	    edx, VIA_REG_AC97_CMD_SHIFT

	   shl	    eax, VIA_REG_AC97_DATA_SHIFT
	   or	    edx, eax

	   mov	    eax, VIA_REG_AC97_CODEC_ID_PRIMARY ;not VIA_REG_AC97_CODEC_ID_PRIMARY
	   shl	    eax, VIA_REG_AC97_CODEC_ID_SHIFT
	   or	    edx, eax

	   mov	    eax, edx
	   mov	    edx, esi
	   call     [ctrl.codec_write16]
	   mov	    [codec.regs+esi], ax

	   stdcall  codec_check_ready
	   cmp	    eax, 0
	   jl	    .err
.ok:
	   ret
.err:
     if DEBUG
	   mov	  esi, msgCFail
	   call   SysMsgBoardStr
     end if
	   ;mov      eax, -1        ; codec not ready error
	   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 ;r32
	   mov	    edx, [ctrl.ctrl_io_base]
	   add	    edx, VIA_REG_AC97
	   in	    eax, dx
	   ret
endp

align 4
proc codec_io_w16 ;w32
	   mov	    edx, [ctrl.ctrl_io_base]
	   add	    edx, VIA_REG_AC97
	   out	    dx,  eax
	   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


align 4
dword2str:
	   push     eax ebx ecx
	   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
	   pop	    ecx ebx eax
	   ret

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


include "codec.inc"

align 4
devices dd (CTRL_VT82C686  shl 16)+VID_VIA,msg_VT82C686,set_VIA
	dd (CTRL_VT8233_5  shl 16)+VID_VIA,msg_VT8233,set_VIA
	dd 0	;terminator


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

msg_VT82C686 db 'VT82C686', 13,10, 0
msg_VT8233   db 'VT8233',   13,10, 0
msg_VIA      db 'VIA'	,   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
;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
msgCInvalid  db 'codec is not valid',13,10,0 ;Asper
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

chip_type	 rb 1