forked from KolibriOS/kolibrios
438 lines
14 KiB
NASM
438 lines
14 KiB
NASM
|
; Stub of videodriver for RDC Semiconductor Co. M2010/M2012 videocards (controller names: R3306/R3308).
|
||
|
; It is used in SoC produced by DMP Electronics Inc.:
|
||
|
; Vortex86MX (contains RDC M2010 graphics card, appears in eBox-3300MX)
|
||
|
; Vortex86MX+ (contains RDC M2012 graphics card, appears in eBox-3310MX)
|
||
|
; Link to manufacturers websites -
|
||
|
; RDC Semiconductor Co.: http://www.rdc.com.tw
|
||
|
; DM&P Electronics Inc.: http://www.dmp.com.tw and http://www.compactpc.com.tw
|
||
|
; Code stolen from vidintel.asm driver (c) by CleverMouse and adapted for RDC.
|
||
|
|
||
|
; When the start procedure gets control,
|
||
|
; it tries to detect preferred resolution,
|
||
|
; sets the detected resolution assuming 32-bpp VESA mode and exits
|
||
|
; (without registering a service).
|
||
|
; Detection can be overloaded with compile-time settings
|
||
|
; use_predefined_mode/predefined_width/predefined_height.
|
||
|
|
||
|
; set predefined resolution here
|
||
|
use_predefined_mode = 0;1
|
||
|
predefined_width = 0;1366
|
||
|
predefined_height = 0;768
|
||
|
|
||
|
; standard driver stuff
|
||
|
format MS COFF
|
||
|
|
||
|
DEBUG = 1
|
||
|
|
||
|
include 'proc32.inc'
|
||
|
include 'imports.inc'
|
||
|
|
||
|
public START
|
||
|
public version
|
||
|
|
||
|
section '.flat' code readable align 16
|
||
|
; the start procedure (see the description above)
|
||
|
START:
|
||
|
; 1. Detect device. Abort if not found.
|
||
|
push esi
|
||
|
call DetectDevice
|
||
|
test esi, esi
|
||
|
jz .return0
|
||
|
|
||
|
;{START}yogev_ezra: temporary exit after detection
|
||
|
pusha
|
||
|
mov esi, exitmsg
|
||
|
call SysMsgBoardStr
|
||
|
popa
|
||
|
jmp .return0
|
||
|
;{END}yogev_ezra: temporary exit after detection
|
||
|
|
||
|
; 2. Detect optimal mode unless the mode is given explicitly. Abort if failed.
|
||
|
if use_predefined_mode = 0
|
||
|
call DetectMode
|
||
|
end if
|
||
|
cmp [width], 0
|
||
|
jz .return0_cleanup
|
||
|
; 3. Set the detected mode.
|
||
|
call SetMode
|
||
|
; 4. Cleanup and return.
|
||
|
.return0_cleanup:
|
||
|
stdcall FreeKernelSpace, esi
|
||
|
.return0:
|
||
|
pop esi
|
||
|
xor eax, eax
|
||
|
ret 4
|
||
|
|
||
|
; check that there is RDC videocard
|
||
|
; if so, map MMIO registers and set internal variables
|
||
|
; esi points to MMIO block; NULL means no device
|
||
|
DetectDevice:
|
||
|
; 1. Sanity check: check that we are dealing with RDC videocard.
|
||
|
; Integrated video device for RDC is always at PCI:0:13:0 (bus:dev:fn=0:0d:0)
|
||
|
xor esi, esi ; initialize return value to NULL
|
||
|
; 1a. Get PCI VendorID and DeviceID.
|
||
|
push esi ; in: reg=0 (register) -> register 00 means return DeviceID (bits 16-31) + VendorID (bits 0-15)
|
||
|
push 68h ; in: devfn=13:0 | device:5bit (0Dh = 1101) + func:3bit (0 = 000) -> total:1byte (1101000b = 68h)
|
||
|
push esi ; in: bus=0
|
||
|
call PciRead32
|
||
|
; 1b. loword(eax) = ax = VendorID, hiword(eax) = DeviceID.
|
||
|
; Test whether we have RDC Semiconductor Co. chipset.
|
||
|
cmp ax, 17F3h ;VendorID 0x17F3, 'RDC Semiconductor Co.'
|
||
|
jnz .return
|
||
|
; 1c. Say hi including DeviceID.
|
||
|
shr eax, 10h ; now, ax = HIWORD(eax) = PCI DeviceID
|
||
|
push edi
|
||
|
pusha
|
||
|
mov edi, pciid_text ; edi='0000'
|
||
|
call WriteWord
|
||
|
mov esi, hellomsg
|
||
|
call SysMsgBoardStr
|
||
|
popa
|
||
|
; 1d. Test whether we know this DeviceID.
|
||
|
; If this is the case, remember the position of the device in line of RDC cards;
|
||
|
; this knowledge will be useful later.
|
||
|
; Tested on devices with id: 17F3:2010, 17F3:2012.
|
||
|
mov ecx, pciids_num
|
||
|
mov edi, pciids
|
||
|
repnz scasw
|
||
|
pop edi
|
||
|
jnz .return_unknown_pciid
|
||
|
sub ecx, pciids_num - 1
|
||
|
neg ecx
|
||
|
mov [deviceType], ecx
|
||
|
; 1e. Continue saying hi with positive intonation.
|
||
|
pusha
|
||
|
mov esi, knownmsg
|
||
|
call SysMsgBoardStr
|
||
|
popa
|
||
|
; 2. Prepare MMIO region to control the card.
|
||
|
; 2a. Read MMIO physical address from PCI config space.
|
||
|
; According to RDC M2010/M2012 registers manual, their memory-mapped I/O space is located at Base address #1
|
||
|
push 14h ; in: reg=14h (register) -> register 14h means Base address #1 (BAR1) in PCI configuration space
|
||
|
push 68h ; in: devfn=13:0 | device:5bit (0Dh = 1101) + func:3bit (0 = 000) -> total:1byte (1101000b = 68h)
|
||
|
push esi ; in: bus=0
|
||
|
call PciRead32
|
||
|
; 2b. Mask out PCI region type, lower 4 bits.
|
||
|
and al, not 0xF
|
||
|
; 2c. Create virtual mapping of the physical memory.
|
||
|
push 1Bh
|
||
|
push 100000h
|
||
|
push eax
|
||
|
call MapIoMem
|
||
|
; 3. Return.
|
||
|
xchg esi, eax
|
||
|
.return:
|
||
|
ret
|
||
|
; 1f. If we do not know DeviceID, continue saying hi with negative intonation.
|
||
|
.return_unknown_pciid:
|
||
|
pusha
|
||
|
mov esi, unknownmsg
|
||
|
call SysMsgBoardStr
|
||
|
popa
|
||
|
ret
|
||
|
|
||
|
; Convert word in ax to hexadecimal text in edi, advance edi.
|
||
|
WriteWord:
|
||
|
; 1. Convert high byte.
|
||
|
push eax
|
||
|
mov al, ah
|
||
|
call WriteByte
|
||
|
pop eax
|
||
|
; 2. Convert low byte.
|
||
|
; Fall through to WriteByte; ret from WriteByte is ret from WriteWord too.
|
||
|
|
||
|
; Convert byte in al to hexadecimal text in edi, advance edi.
|
||
|
WriteByte:
|
||
|
; 1. Convert high nibble.
|
||
|
push eax
|
||
|
shr al, 4
|
||
|
call WriteNibble
|
||
|
pop eax
|
||
|
; 2. Convert low nibble.
|
||
|
and al, 0xF
|
||
|
; Fall through to WriteNibble; ret from WriteNibble is ret from WriteByte too.
|
||
|
|
||
|
; Convert nibble in al to hexadecimal text in edi, advance edi.
|
||
|
WriteNibble:
|
||
|
; Obvious, isn't it?
|
||
|
cmp al, 10
|
||
|
sbb al, 69h
|
||
|
das
|
||
|
stosb ; This instruction uses EDI implicitly
|
||
|
ret
|
||
|
|
||
|
if use_predefined_mode = 0
|
||
|
; detect resolution of the flat panel
|
||
|
DetectMode:
|
||
|
push esi edi
|
||
|
; 1. Get the location of block of GMBUS* registers.
|
||
|
; Starting with Ironlake, GMBUS* registers were moved.
|
||
|
add esi, 5100h
|
||
|
cmp [deviceType], pciids_num ;ironlake_start
|
||
|
jb @f
|
||
|
add esi, 0xC0000
|
||
|
@@:
|
||
|
; 2. Initialize GMBUS engine.
|
||
|
mov edi, edid
|
||
|
mov ecx, 0x10000
|
||
|
@@:
|
||
|
test byte [esi+8+1], 80h
|
||
|
loopnz @b
|
||
|
jnz .fail
|
||
|
mov dword [esi], 3
|
||
|
test byte [esi+8+1], 4
|
||
|
jz .noreset
|
||
|
call ResetGMBus
|
||
|
jnz .fail
|
||
|
.noreset:
|
||
|
; 3. Send read command.
|
||
|
and dword [esi+20h], 0
|
||
|
mov dword [esi+4], 4E8000A1h
|
||
|
; 4. Wait for data, writing to the buffer as data arrive.
|
||
|
.getdata:
|
||
|
mov ecx, 0x10000
|
||
|
@@:
|
||
|
test byte [esi+8+1], 8
|
||
|
loopz @b
|
||
|
test byte [esi+8+1], 4
|
||
|
jz .dataok
|
||
|
call ResetGMBus
|
||
|
jmp .fail
|
||
|
.dataok:
|
||
|
mov eax, [esi+0Ch]
|
||
|
stosd
|
||
|
cmp edi, edid+80h
|
||
|
jb .getdata
|
||
|
; 5. Wait for bus idle.
|
||
|
mov ecx, 0x10000
|
||
|
@@:
|
||
|
test byte [esi+8+1], 2
|
||
|
loopnz @b
|
||
|
; 6. We got EDID; dump it if DEBUG.
|
||
|
if DEBUG
|
||
|
pusha
|
||
|
xor ecx, ecx
|
||
|
mov esi, edid
|
||
|
mov edi, edid_text
|
||
|
.dumploop:
|
||
|
lodsb
|
||
|
call WriteByte
|
||
|
mov al, ' '
|
||
|
stosb
|
||
|
inc cl
|
||
|
test cl, 15
|
||
|
jnz @f
|
||
|
mov byte [edi-1], 13
|
||
|
mov al, 10
|
||
|
stosb
|
||
|
@@:
|
||
|
test cl, cl
|
||
|
jns .dumploop
|
||
|
mov esi, edidmsg
|
||
|
call SysMsgBoardStr
|
||
|
popa
|
||
|
end if
|
||
|
; 7. Test whether EDID is good.
|
||
|
; 7a. Signature: 00 FF FF FF FF FF FF 00.
|
||
|
mov esi, edid
|
||
|
cmp dword [esi], 0xFFFFFF00
|
||
|
jnz .fail
|
||
|
cmp dword [esi+4], 0x00FFFFFF
|
||
|
jnz .fail
|
||
|
; 7b. Checksum must be zero.
|
||
|
xor edx, edx
|
||
|
mov ecx, 80h
|
||
|
@@:
|
||
|
lodsb
|
||
|
add dl, al
|
||
|
loop @b
|
||
|
jnz .fail
|
||
|
; 8. Get width and height from EDID.
|
||
|
xor eax, eax
|
||
|
mov ah, [esi-80h+3Ah]
|
||
|
shr ah, 4
|
||
|
mov al, [esi-80h+38h]
|
||
|
mov [width], eax
|
||
|
mov ah, [esi-80h+3Dh]
|
||
|
shr ah, 4
|
||
|
mov al, [esi-80h+3Bh]
|
||
|
mov [height], eax
|
||
|
; 9. Return.
|
||
|
.fail:
|
||
|
pop edi esi
|
||
|
ret
|
||
|
|
||
|
; reset bus, clear all errors
|
||
|
ResetGMBus:
|
||
|
; look into the PRM
|
||
|
mov dword [esi+4], 80000000h
|
||
|
mov dword [esi+4], 0
|
||
|
mov ecx, 0x10000
|
||
|
@@:
|
||
|
test byte [esi+8+1], 2
|
||
|
loopnz @b
|
||
|
ret
|
||
|
end if
|
||
|
|
||
|
; set resolution [width]*[height]
|
||
|
SetMode:
|
||
|
; 1. Program the registers of videocard.
|
||
|
; look into the PRM
|
||
|
cli
|
||
|
; or byte [esi+7000Ah], 0Ch ; PIPEACONF: disable Display+Cursor Planes
|
||
|
; or byte [esi+7100Ah], 0Ch ; PIPEBCONF: disable Display+Cursor Planes
|
||
|
xor eax, eax
|
||
|
xor edx, edx
|
||
|
cmp [deviceType], pciids_num ;i965_start
|
||
|
jb @f
|
||
|
mov dl, 9Ch - 84h
|
||
|
@@:
|
||
|
; or byte [esi+71403h], 80h ; VGACNTRL: VGA Display Disable
|
||
|
and byte [esi+70080h], not 27h ; CURACNTR: disable cursor A
|
||
|
mov dword [esi+70084h], eax ; CURABASE: force write to CURA* regs
|
||
|
and byte [esi+700C0h], not 27h ; CURBCNTR: disable cursor B
|
||
|
mov dword [esi+700C4h], eax ; CURBBASE: force write to CURB* regs
|
||
|
and byte [esi+70183h], not 80h ; DSPACNTR: disable Primary A Plane
|
||
|
mov dword [esi+edx+70184h], eax ; DSPALINOFF/DSPASURF: force write to DSPA* regs
|
||
|
and byte [esi+71183h], not 80h ; DSPBCNTR: disable Primary B Plane
|
||
|
mov dword [esi+edx+71184h], eax ; DSPBLINOFF/DSPBSURF: force write to DSPB* regs
|
||
|
if 1
|
||
|
cmp [deviceType], pciids_num ;ironlake_start
|
||
|
jae .disable_pipes
|
||
|
mov edx, 10000h
|
||
|
or byte [esi+70024h], 2 ; PIPEASTAT: clear VBLANK status
|
||
|
or byte [esi+71024h], 2 ; PIPEBSTAT: clear VBLANK status
|
||
|
.wait_vblank_preironlake1:
|
||
|
mov ecx, 1000h
|
||
|
loop $
|
||
|
test byte [esi+7000Bh], 80h ; PIPEACONF: pipe A active?
|
||
|
jz @f
|
||
|
test byte [esi+70024h], 2 ; PIPEASTAT: got VBLANK?
|
||
|
jz .wait_vblank_preironlake2
|
||
|
@@:
|
||
|
test byte [esi+7100Bh], 80h ; PIPEBCONF: pipe B active?
|
||
|
jz .disable_pipes
|
||
|
test byte [esi+71024h], 2 ; PIPEBSTAT: got VBLANK?
|
||
|
jnz .disable_pipes
|
||
|
.wait_vblank_preironlake2:
|
||
|
dec edx
|
||
|
jnz .wait_vblank_preironlake1
|
||
|
jmp .not_disabled
|
||
|
.disable_pipes:
|
||
|
end if
|
||
|
and byte [esi+7000Bh], not 80h ; PIPEACONF: disable pipe
|
||
|
and byte [esi+7100Bh], not 80h ; PIPEBCONF: disable pipe
|
||
|
cmp [deviceType], pciids_num ;gen4_start
|
||
|
jb .wait_watching_scanline
|
||
|
; g45 and later: use special flag from PIPE*CONF
|
||
|
mov edx, 10000h
|
||
|
@@:
|
||
|
mov ecx, 1000h
|
||
|
loop $
|
||
|
test byte [esi+7000Bh], 40h ; PIPEACONF: wait until pipe disabled
|
||
|
jz @f
|
||
|
dec edx
|
||
|
jnz @b
|
||
|
jmp .not_disabled
|
||
|
@@:
|
||
|
test byte [esi+7100Bh], 40h ; PIPEBCONF: wait until pipe disabled
|
||
|
jz .disabled
|
||
|
mov ecx, 1000h
|
||
|
loop $
|
||
|
dec edx
|
||
|
jnz @b
|
||
|
jmp .not_disabled
|
||
|
; pineview and before: wait while scanline still changes
|
||
|
.wait_watching_scanline:
|
||
|
mov edx, 1000h
|
||
|
.dis1:
|
||
|
push dword [esi+71000h]
|
||
|
push dword [esi+70000h]
|
||
|
mov ecx, 10000h
|
||
|
loop $
|
||
|
pop eax
|
||
|
xor eax, [esi+70000h]
|
||
|
and eax, 1FFFh
|
||
|
pop eax
|
||
|
jnz .notdis1
|
||
|
xor eax, [esi+71000h]
|
||
|
and eax, 1FFFh
|
||
|
jz .disabled
|
||
|
.notdis1:
|
||
|
dec edx
|
||
|
jnz .dis1
|
||
|
.not_disabled:
|
||
|
sti
|
||
|
jmp .return
|
||
|
.disabled:
|
||
|
lea eax, [esi+61183h]
|
||
|
cmp [deviceType], pciids_num ;ironlake_start
|
||
|
jb @f
|
||
|
add eax, 0xE0000 - 0x60000
|
||
|
@@:
|
||
|
lea edx, [esi+60000h]
|
||
|
test byte [eax], 40h
|
||
|
jz @f
|
||
|
add edx, 1000h
|
||
|
@@:
|
||
|
mov eax, [width]
|
||
|
dec eax
|
||
|
shl eax, 16
|
||
|
mov ax, word [height]
|
||
|
dec eax
|
||
|
mov dword [edx+1Ch], eax ; PIPEASRC: set source image size
|
||
|
ror eax, 16
|
||
|
mov dword [edx+10190h], eax ; for old cards
|
||
|
mov ecx, [width]
|
||
|
add ecx, 15
|
||
|
and ecx, not 15
|
||
|
shl ecx, 2
|
||
|
mov dword [edx+10188h], ecx ; DSPASTRIDE: set scanline length
|
||
|
mov dword [edx+10184h], 0 ; DSPALINOFF: force write to DSPA* registers
|
||
|
and byte [esi+61233h], not 80h ; PFIT_CONTROL: disable panel fitting
|
||
|
or byte [edx+1000Bh], 80h ; PIPEACONF: enable pipe
|
||
|
; and byte [edx+1000Ah], not 0Ch ; PIPEACONF: enable Display+Cursor Planes
|
||
|
or byte [edx+10183h], 80h ; DSPACNTR: enable Display Plane A
|
||
|
sti
|
||
|
; 2. Notify the kernel that resolution has changed.
|
||
|
call GetDisplay
|
||
|
mov edx, [width]
|
||
|
mov dword [eax+8], edx
|
||
|
mov edx, [height]
|
||
|
mov dword [eax+0Ch], edx
|
||
|
mov [eax+18h], ecx
|
||
|
mov eax, [width]
|
||
|
dec eax
|
||
|
dec edx
|
||
|
call SetScreen
|
||
|
.return:
|
||
|
ret
|
||
|
|
||
|
align 4
|
||
|
hellomsg db 'RDC videocard detected, PciId=17F3:' ;VendorID 0x17F3, 'RDC Semiconductor Co.'
|
||
|
pciid_text db '0000'
|
||
|
db ', which is ', 0
|
||
|
knownmsg db 'known',13,10,0
|
||
|
unknownmsg db 'unknown',13,10,0
|
||
|
exitmsg db 'Card detected successfully, exiting driver...',13,10,0
|
||
|
|
||
|
if DEBUG
|
||
|
edidmsg db 'EDID successfully read:',13,10
|
||
|
edid_text rb 8*(16*3+1)
|
||
|
db 0
|
||
|
end if
|
||
|
|
||
|
version:
|
||
|
dd 0x50005
|
||
|
|
||
|
width dd predefined_width
|
||
|
height dd predefined_height
|
||
|
|
||
|
pciids:
|
||
|
dw 0x2010 ; M2010 - appears in eBox-3300MX (Vortex86MX SoC)
|
||
|
dw 0x2012 ; M2012 - appears in eBox-3310MX (Vortex86MX+ SoC)
|
||
|
pciids_num = ($ - pciids) / 2
|
||
|
|
||
|
align 4
|
||
|
deviceType dd ?
|
||
|
edid rb 0x80
|