From 8454f182c590e888016f1198d6988aefb58ee333 Mon Sep 17 00:00:00 2001 From: CleverMouse Date: Tue, 14 Jan 2014 20:50:19 +0000 Subject: [PATCH] hardware cursors for RDC M2010/M2012 git-svn-id: svn://kolibrios.org@4453 a494cfbc-eb01-0410-851d-a64ba20cac60 --- drivers/imports.inc | 4 +- drivers/macros.inc | 9 + drivers/peimport.inc | 4 +- drivers/video/rdc.asm | 429 +++++++++++++++++++++++++++++++++ kernel/trunk/bus/pci/pci32.inc | 6 + kernel/trunk/core/exports.inc | 2 + 6 files changed, 452 insertions(+), 2 deletions(-) create mode 100644 drivers/video/rdc.asm diff --git a/drivers/imports.inc b/drivers/imports.inc index 8d8c109ed4..716c990b1c 100644 --- a/drivers/imports.inc +++ b/drivers/imports.inc @@ -99,4 +99,6 @@ kernel_export \ NetPtrToNum,\ NetLinkChanged,\ Eth_input,\ - IPv4_input + IPv4_input,\ +\ + GetPCIList diff --git a/drivers/macros.inc b/drivers/macros.inc index 454a5ed65f..0f0e330442 100644 --- a/drivers/macros.inc +++ b/drivers/macros.inc @@ -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 diff --git a/drivers/peimport.inc b/drivers/peimport.inc index ddc28cc9b6..796f1ab1e5 100644 --- a/drivers/peimport.inc +++ b/drivers/peimport.inc @@ -148,5 +148,7 @@ import core,\ DiskDel,\ \ TimerHS,\ - CancelTimerHS + CancelTimerHS,\ +\ + GetPCIList end data diff --git a/drivers/video/rdc.asm b/drivers/video/rdc.asm new file mode 100644 index 0000000000..7bc294aa98 --- /dev/null +++ b/drivers/video/rdc.asm @@ -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 diff --git a/kernel/trunk/bus/pci/pci32.inc b/kernel/trunk/bus/pci/pci32.inc index e82e8e83ec..1e77ab90b5 100644 --- a/kernel/trunk/bus/pci/pci32.inc +++ b/kernel/trunk/bus/pci/pci32.inc @@ -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 diff --git a/kernel/trunk/core/exports.inc b/kernel/trunk/core/exports.inc index 1321edcac7..c8b4b502a4 100644 --- a/kernel/trunk/core/exports.inc +++ b/kernel/trunk/core/exports.inc @@ -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