;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2011. 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