forked from KolibriOS/kolibrios
hardware cursors for RDC M2010/M2012
git-svn-id: svn://kolibrios.org@4453 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
c1a8d1cc24
commit
8454f182c5
@ -99,4 +99,6 @@ kernel_export \
|
||||
NetPtrToNum,\
|
||||
NetLinkChanged,\
|
||||
Eth_input,\
|
||||
IPv4_input
|
||||
IPv4_input,\
|
||||
\
|
||||
GetPCIList
|
||||
|
@ -34,6 +34,15 @@ struct PCIDEV
|
||||
owner dd ? ; pointer to SRV or 0
|
||||
ends
|
||||
|
||||
struct IOCTL
|
||||
handle dd ?
|
||||
io_code dd ?
|
||||
input dd ?
|
||||
inp_size dd ?
|
||||
output dd ?
|
||||
out_size dd ?
|
||||
ends
|
||||
|
||||
; The following macro assume that we are on uniprocessor machine.
|
||||
; Serious work is needed for multiprocessor machines.
|
||||
macro spin_lock_irqsave spinlock
|
||||
|
@ -148,5 +148,7 @@ import core,\
|
||||
DiskDel,\
|
||||
\
|
||||
TimerHS,\
|
||||
CancelTimerHS
|
||||
CancelTimerHS,\
|
||||
\
|
||||
GetPCIList
|
||||
end data
|
||||
|
429
drivers/video/rdc.asm
Normal file
429
drivers/video/rdc.asm
Normal file
@ -0,0 +1,429 @@
|
||||
; RDC M2010/M2012 video driver.
|
||||
|
||||
; Standard driver stuff
|
||||
format PE DLL native at 0
|
||||
entry start
|
||||
__DEBUG__ equ 1
|
||||
__DEBUG_LEVEL__ equ 1
|
||||
section '.flat' readable writable executable
|
||||
include '../proc32.inc'
|
||||
include '../struct.inc'
|
||||
include '../macros.inc'
|
||||
include '../fdo.inc'
|
||||
|
||||
; Display-specific driver stuff
|
||||
; Kernel passes to init_cursor cursors with fixed size 32x32
|
||||
KERNEL_CURSOR_WIDTH = 32
|
||||
KERNEL_CURSOR_HEIGHT = 32
|
||||
|
||||
; Constants for IOCTL codes
|
||||
SRV_GETVERSION = 0
|
||||
SRV_ENUM_MODES = 1
|
||||
SRV_SET_MODE = 2
|
||||
|
||||
; Constants for SRV_GETVERSION result
|
||||
CURRENT_API = 0x0200
|
||||
COMPATIBLE_API = 0x0100
|
||||
API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API
|
||||
|
||||
; Some structures
|
||||
struct display_t
|
||||
x dd ?
|
||||
y dd ?
|
||||
width dd ?
|
||||
height dd ?
|
||||
bpp dd ?
|
||||
vrefresh dd ?
|
||||
pitch dd ?
|
||||
lfb dd ?
|
||||
|
||||
modes dd ?
|
||||
ddev dd ?
|
||||
connector dd ?
|
||||
crtc dd ?
|
||||
|
||||
cr_list.next dd ?
|
||||
cr_list.prev dd ?
|
||||
|
||||
cursor dd ?
|
||||
|
||||
init_cursor dd ?
|
||||
select_cursor dd ?
|
||||
show_cursor dd ?
|
||||
move_cursor dd ?
|
||||
restore_cursor dd ?
|
||||
disable_mouse dd ?
|
||||
mask_seqno dd ?
|
||||
check_mouse dd ?
|
||||
check_m_pixel dd ?
|
||||
ends
|
||||
|
||||
struct APPOBJ ; common object header
|
||||
magic dd ? ;
|
||||
destroy dd ? ; internal destructor
|
||||
fd dd ? ; next object in list
|
||||
bk dd ? ; prev object in list
|
||||
pid dd ? ; owner id
|
||||
ends
|
||||
|
||||
struct CURSOR APPOBJ
|
||||
base dd ? ;allocated memory
|
||||
hot_x dd ? ;hotspot coords
|
||||
hot_y dd ?
|
||||
|
||||
list_next dd ? ;next cursor in cursor list
|
||||
list_prev dd ? ;prev cursor in cursor list
|
||||
dev_obj dd ? ;device depended data
|
||||
ends
|
||||
|
||||
; Constants specific to our drivers
|
||||
; We are handling two videocards: M2010 and M2012
|
||||
PCI_VENDOR_RDC = 0x17F3
|
||||
; I like this approach to select device IDs!
|
||||
PCI_CHIP_M2010 = 0x2010
|
||||
PCI_CHIP_M2012 = 0x2012
|
||||
|
||||
; I/O ports for CRT registers
|
||||
COLOR_CRTC_INDEX = 0x3D4
|
||||
COLOR_CRTC_DATA = 0x3D5
|
||||
; The value that unlocks extended CRT registers:
|
||||
; index 0x80, value 0xA8
|
||||
ENABLE_EXTENDED_REGS = 0xA880
|
||||
|
||||
; Hardware cursors have size 64x64
|
||||
HW_CURSOR_WIDTH = 64
|
||||
HW_CURSOR_HEIGHT = 64
|
||||
; Multiplication to powers of two can be replaced with shifts,
|
||||
; x*HW_CURSOR_WIDTH = x shl HW_CURSOR_WIDTH_SHIFT
|
||||
HW_CURSOR_WIDTH_SHIFT = 6
|
||||
HW_CURSOR_HEIGHT_SHIFT = 6
|
||||
|
||||
; MMIO registers responsible for hardware cursor, see PRM
|
||||
HWC_MMIO_CTRL = 0x580
|
||||
HWC_MMIO_OFFSET = 0x584
|
||||
HWC_MMIO_POSITION = 0x588
|
||||
HWC_MMIO_ADDRESS = 0x58C
|
||||
|
||||
; Data for hardware cursors must be stored in videomemory,
|
||||
; so we need an allocator for objects inside videomemory.
|
||||
; Currently, we just reserve a fixed amount of memory for cursors
|
||||
; (cursor size is the same for all cursors) and keep a bitfield
|
||||
; that describes free blocks. 32 bits fit nicely in one dword.
|
||||
MAX_CURSORS = 32
|
||||
|
||||
; === Entry points for external code ===
|
||||
|
||||
; Called once when driver is loading and once at shutdown.
|
||||
; When loading, must initialize itself, register itself in the system
|
||||
; and return eax = value obtained when registering.
|
||||
; Cdecl with two parameters.
|
||||
proc start
|
||||
push ebx esi ; save used registers to be stdcall
|
||||
virtual at esp
|
||||
rd 2 ; saved registers
|
||||
dd ? ; return address
|
||||
.reason dd ? ; DRV_ENTRY or DRV_EXIT
|
||||
.cmdline dd ? ; normally NULL
|
||||
end virtual
|
||||
; 1. Check the reason for the call, do nothing unless initializing.
|
||||
cmp [.reason], DRV_ENTRY
|
||||
jnz .nothing
|
||||
; 2. Find the PCI device for our videocard.
|
||||
; If not found, just return zero.
|
||||
invoke GetPCIList
|
||||
mov ebx, eax
|
||||
.look_pcidev_loop:
|
||||
mov ebx, [ebx+PCIDEV.fd]
|
||||
cmp ebx, eax
|
||||
jz .pcidev_notfound
|
||||
cmp [ebx+PCIDEV.vendor_device_id], PCI_VENDOR_RDC + (PCI_CHIP_M2010 shl 16)
|
||||
jz .pcidev_found
|
||||
cmp [ebx+PCIDEV.vendor_device_id], PCI_VENDOR_RDC + (PCI_CHIP_M2012 shl 16)
|
||||
jnz .look_pcidev_loop
|
||||
.pcidev_found:
|
||||
; 3. Get addresses, sizes and pointers from the hardware.
|
||||
; 3a. Create mapping for MMIO.
|
||||
invoke PciRead32, dword [ebx+PCIDEV.bus], dword [ebx+PCIDEV.devfn], 14h
|
||||
and al, not 0xF
|
||||
invoke MapIoMem, eax, 10000h, PG_NOCACHE+PG_SW
|
||||
test eax, eax
|
||||
jz .nothing
|
||||
mov [mmio], eax
|
||||
; 3b. Get videomemory size. It is stored in 3 lower bits of 0xAA extended register
|
||||
; logarithmically started with 8Mb.
|
||||
mov dx, COLOR_CRTC_INDEX
|
||||
mov ax, ENABLE_EXTENDED_REGS
|
||||
out dx, ax
|
||||
mov al, 0xAA
|
||||
out dx, al
|
||||
inc edx
|
||||
in al, dx
|
||||
and eax, 7
|
||||
mov ecx, eax
|
||||
mov eax, 8 shl 20
|
||||
shl eax, cl
|
||||
mov [video_mem_size], eax
|
||||
; 3c. Reserve area for cursors in the last part of videomemory.
|
||||
sub eax, MAX_CURSORS * HW_CURSOR_WIDTH * HW_CURSOR_HEIGHT * 4
|
||||
mov [cursors_base_offset], eax
|
||||
; 3d. Create mapping for part of videomemory that we have reserved for cursors.
|
||||
; Note: we can't just use system-wide mapping at 0xFE000000, it is too short.
|
||||
invoke PciRead32, dword [ebx+PCIDEV.bus], dword [ebx+PCIDEV.devfn], 10h
|
||||
and al, not 0xF
|
||||
add eax, [cursors_base_offset]
|
||||
invoke MapIoMem, eax, MAX_CURSORS * HW_CURSOR_WIDTH * HW_CURSOR_HEIGHT * 4, PG_SW
|
||||
test eax, eax
|
||||
jz .nothing
|
||||
mov [cursors_base_va], eax
|
||||
; 4. Install cursor handlers.
|
||||
; 4a. Get pointer to the structure that keeps everything display-related.
|
||||
invoke GetDisplay
|
||||
mov ebx, eax
|
||||
; 4b. Make sure that no one tries to use partially-changed structure.
|
||||
pushf
|
||||
cli
|
||||
; 4c. Ask the previous handler to restore image hidden beyond cursor.
|
||||
stdcall [ebx+display_t.restore_cursor], 0, 0
|
||||
; 4d. Store pointers to our functions.
|
||||
mov [ebx+display_t.init_cursor], init_cursor
|
||||
mov [ebx+display_t.select_cursor], select_cursor
|
||||
mov [ebx+display_t.show_cursor], 0
|
||||
mov [ebx+display_t.move_cursor], move_cursor
|
||||
mov [ebx+display_t.restore_cursor], restore_cursor
|
||||
mov [ebx+display_t.disable_mouse], disable_mouse
|
||||
; 4e. The kernel will pass all new cursors to our init_cursor,
|
||||
; but we must process already created cursors ourselves to be able to
|
||||
; select_cursor them when requested. Do it now: pass every cursor
|
||||
; in the list display_t.cr_list to init_cursor.
|
||||
add ebx, display_t.cr_list.next
|
||||
mov esi, ebx
|
||||
.init_old_cursors_loop:
|
||||
mov esi, [esi]
|
||||
cmp esi, ebx
|
||||
jz .init_old_cursors_done
|
||||
lea eax, [esi-CURSOR.list_next]
|
||||
push eax
|
||||
call init_cursor
|
||||
pop eax
|
||||
jmp .init_old_cursors_loop
|
||||
.init_old_cursors_done:
|
||||
; 4f. Setup the current cursor.
|
||||
stdcall move_cursor, [ebx+display_t.cursor-display_t.cr_list.next], 0, 0
|
||||
stdcall select_cursor, [ebx+display_t.cursor-display_t.cr_list.next]
|
||||
; 4g. It is safe now to work with display structure; restore after 4b.
|
||||
popf
|
||||
; 5. Say something happy to the (curious) user.
|
||||
mov esi, success_msg
|
||||
invoke SysMsgBoardStr
|
||||
; 6. Register ourselves as a service.
|
||||
; Note: not really needed currently as we don't do any useful in ioctl_handler,
|
||||
; but do it nevertheless for future expansion.
|
||||
invoke RegService, rdc_name, ioctl_handler
|
||||
.nothing:
|
||||
pop esi ebx ; restore used registers to be stdcall
|
||||
ret
|
||||
.pcidev_notfound:
|
||||
xor eax, eax
|
||||
jmp .nothing
|
||||
endp
|
||||
|
||||
; Service procedure for the driver - handle all IOCTL requests for the driver.
|
||||
; Stdcall with one parameter.
|
||||
proc ioctl_handler
|
||||
virtual at esp
|
||||
dd ? ; return address
|
||||
.ioctl dd ?
|
||||
end virtual
|
||||
; Not very useful currently - just return API_VERSION as a response to SRV_GETVERSION = 0.
|
||||
mov edx, [.ioctl]
|
||||
mov eax, [edx+IOCTL.io_code]
|
||||
test eax, eax
|
||||
jz .getversion
|
||||
.error:
|
||||
or eax, -1 ; fail everything unknown
|
||||
retn 4
|
||||
.getversion:
|
||||
cmp [edx+IOCTL.out_size], 4
|
||||
jnz .error
|
||||
mov eax, [edx+IOCTL.output]
|
||||
mov dword [eax], API_VERSION
|
||||
xor eax, eax
|
||||
retn 4
|
||||
endp
|
||||
|
||||
; === Cursors ===
|
||||
|
||||
; This function is called when an application registers a new cursor.
|
||||
; Cdecl with one parameter, return value ignored.
|
||||
proc init_cursor
|
||||
push esi edi
|
||||
virtual at esp
|
||||
rd 2 ; saved registers
|
||||
dd ? ; return address
|
||||
.cursor dd ?
|
||||
end virtual
|
||||
; We store one specific dword in CURSOR.dev_obj,
|
||||
; index in cursors area from 0 to NUM_CURSORS-1, or -1 for error.
|
||||
; 1. Prepare: store -1 to CURSOR.dev_obj and pointer to destroy function.
|
||||
mov edx, [.cursor]
|
||||
mov [edx+CURSOR.dev_obj], -1
|
||||
mov [edx+CURSOR.destroy], destroy_cursor
|
||||
; 2. Allocate videomemory.
|
||||
bsr edi, [free_cursors]
|
||||
jz .nocopy
|
||||
btr [free_cursors], edi
|
||||
; 3. Store the allocated item to CURSOR.dev_obj.
|
||||
mov [edx+CURSOR.dev_obj], edi
|
||||
; 4. Copy data from kernel-provided cursor to videomemory,
|
||||
; transforming KERNEL_CURSOR_WIDTH*KERNEL_CURSOR_HEIGHT*(4 bytes RGBA) to
|
||||
; HW_CURSOR_WIDTH*HW_CURSOR_HEIGHT*(4 bytes RGBA).
|
||||
shl edi, HW_CURSOR_WIDTH_SHIFT + HW_CURSOR_HEIGHT_SHIFT + 2
|
||||
add edi, [cursors_base_va]
|
||||
mov esi, [edx+CURSOR.base]
|
||||
push KERNEL_CURSOR_HEIGHT
|
||||
xor eax, eax
|
||||
@@:
|
||||
mov ecx, KERNEL_CURSOR_WIDTH
|
||||
rep movsd
|
||||
mov ecx, HW_CURSOR_WIDTH - KERNEL_CURSOR_WIDTH
|
||||
rep stosd
|
||||
dec dword [esp]
|
||||
jnz @b
|
||||
pop ecx
|
||||
mov ecx, HW_CURSOR_WIDTH * (HW_CURSOR_HEIGHT - KERNEL_CURSOR_HEIGHT)
|
||||
rep stosd
|
||||
.nocopy:
|
||||
; 5. We don't need kernel-provided data anymore; free it now.
|
||||
invoke KernelFree, [edx+CURSOR.base]
|
||||
pop edi esi
|
||||
ret
|
||||
endp
|
||||
|
||||
; This function is called when a thread that has created a cursor
|
||||
; is terminating and we need to free the cursor.
|
||||
proc destroy_cursor
|
||||
; 1. Free allocated videomemory.
|
||||
mov edx, [eax+CURSOR.dev_obj]
|
||||
test edx, edx
|
||||
js .nofree
|
||||
bts [free_cursors], edx
|
||||
.nofree:
|
||||
; 2. Remove the cursor from the overall list at display_t.cr_list.
|
||||
pushf
|
||||
cli
|
||||
mov ecx, [eax+CURSOR.list_next]
|
||||
mov edx, [eax+CURSOR.list_prev]
|
||||
mov [ecx+4], edx
|
||||
mov [edx], ecx
|
||||
popf
|
||||
; 3. Free memory allocated for kernel object.
|
||||
jmp [DestroyObject]
|
||||
endp
|
||||
|
||||
; This function is called when cursor shape needs to be changed,
|
||||
; either due to explicit request from application
|
||||
; or due to moving from one window to another.
|
||||
; Stdcall with one parameter, return value ignored.
|
||||
proc select_cursor
|
||||
virtual at esp
|
||||
dd ? ; return address
|
||||
.cursor dd ?
|
||||
end virtual
|
||||
mov eax, [.cursor]
|
||||
mov eax, [eax+CURSOR.dev_obj]
|
||||
cmp eax, -1
|
||||
jz .nothing
|
||||
; Setup base address of cursor, relative to videomemory,
|
||||
; and enable hardware cursor.
|
||||
; See PRM for details.
|
||||
shl eax, HW_CURSOR_WIDTH_SHIFT + HW_CURSOR_HEIGHT_SHIFT + 2
|
||||
add eax, [cursors_base_offset]
|
||||
shr eax, 3
|
||||
mov edx, [mmio]
|
||||
mov [edx+HWC_MMIO_ADDRESS], eax
|
||||
mov dword [edx+HWC_MMIO_CTRL], (1 shl 31) + (1 shl 1) + (1 shl 0)
|
||||
.nothing:
|
||||
ret 4
|
||||
endp
|
||||
|
||||
; This function is called when cursor is moved to a new place.
|
||||
; Stdcall with three parameters, return value ignored.
|
||||
proc move_cursor
|
||||
virtual at esp
|
||||
dd ? ; return address
|
||||
.cursor dd ?
|
||||
.x dd ?
|
||||
.y dd ?
|
||||
.xoffset dd ?
|
||||
.yoffset dd ?
|
||||
end virtual
|
||||
; If cursor x is smaller than hotspot x,
|
||||
; only part of cursor is shown at position x=0
|
||||
; with x offset = (hotspot x) - (cursor x).
|
||||
; Otherwise, the entire cursor is shown (x offset = 0)
|
||||
; at position x = (cursor x) - (hotspot x).
|
||||
; Similar for y. Refer to PRM for details.
|
||||
xor ecx, ecx
|
||||
mov edx, [.cursor]
|
||||
mov eax, [.x]
|
||||
sub eax, [edx+CURSOR.hot_x]
|
||||
jae @f
|
||||
sub ecx, eax
|
||||
xor eax, eax
|
||||
@@:
|
||||
mov [.x], eax
|
||||
mov eax, [.y]
|
||||
sub eax, [edx+CURSOR.hot_y]
|
||||
jae @f
|
||||
shl eax, 8
|
||||
sub ecx, eax
|
||||
xor eax, eax
|
||||
@@:
|
||||
mov [.y], eax
|
||||
mov edx, [mmio]
|
||||
mov eax, [.y]
|
||||
add eax, HW_CURSOR_HEIGHT - 1 ; no scaling
|
||||
shl eax, 16
|
||||
add eax, ecx
|
||||
mov [edx+HWC_MMIO_OFFSET], eax
|
||||
mov eax, [.y]
|
||||
shl eax, 16
|
||||
add eax, [.x]
|
||||
mov [edx+HWC_MMIO_POSITION], eax
|
||||
mov eax, [edx+HWC_MMIO_CTRL]
|
||||
mov [edx+HWC_MMIO_CTRL], eax
|
||||
ret 12
|
||||
endp
|
||||
|
||||
; Stdcall with two parameters, return value ignored.
|
||||
proc restore_cursor
|
||||
; No-operation for hardware cursors.
|
||||
ret 8
|
||||
endp
|
||||
|
||||
; No parameters, return value ignored.
|
||||
proc disable_mouse
|
||||
; No-operation for hardware cursors.
|
||||
ret
|
||||
endp
|
||||
|
||||
; === Data ===
|
||||
rdc_name db 'DISPLAY',0
|
||||
success_msg db 'RDC: using hardware cursors',13,10,0
|
||||
|
||||
align 4
|
||||
; Look at the comment before definition of NUM_CURSORS.
|
||||
free_cursors dd 0xFFFFFFFF
|
||||
|
||||
data fixups
|
||||
end data
|
||||
|
||||
include '../peimport.inc'
|
||||
;include_debug_strings
|
||||
IncludeIGlobals
|
||||
IncludeUGlobals
|
||||
align 4
|
||||
mmio dd ? ; virtual address of MMIO for our device
|
||||
video_mem_size dd ? ; total size of video memory, in bytes
|
||||
cursors_base_offset dd ? ; base of cursor data, relative to video memory start
|
||||
cursors_base_va dd ? ; mapped virtual address of cursor data
|
@ -722,3 +722,9 @@ end virtual
|
||||
leave
|
||||
ret
|
||||
endp
|
||||
|
||||
; Export for drivers. Just returns the pointer to the pci-devices list.
|
||||
proc get_pcidev_list
|
||||
mov eax, pcidev_list
|
||||
ret
|
||||
endp
|
||||
|
@ -121,6 +121,8 @@ __exports:
|
||||
NET_ptr_to_num, 'NetPtrToNum', \
|
||||
NET_link_changed, 'NetLinkChanged', \
|
||||
ETH_input, 'Eth_input', \
|
||||
\
|
||||
get_pcidev_list, 'GetPCIList', \
|
||||
\
|
||||
0, 'LFBAddress' ; must be the last one
|
||||
load kernel_exports_count dword from __exports + 24
|
||||
|
Loading…
Reference in New Issue
Block a user