From 721e4b481a301692d40f231aa18dd247b6675a92 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 16 Oct 2011 22:28:41 +0800 Subject: [PATCH] Modules/PL110 - Added ARM PL110 CLCD driver - Added more to DrvUtils (video) - Working on an API to parse Key/Value pairs (header only) --- Kernel/drvutil.c | 185 ++++++++++++++++++++- Kernel/include/api_drv_video.h | 56 ++++++- Kernel/include/lib/keyvalue.h | 43 +++++ Modules/Display/PL110/Makefile | 7 + Modules/Display/PL110/main.c | 294 +++++++++++++++++++++++++++++++++ Modules/Display/VESA/main.c | 5 +- 6 files changed, 574 insertions(+), 16 deletions(-) create mode 100644 Kernel/include/lib/keyvalue.h create mode 100644 Modules/Display/PL110/Makefile create mode 100644 Modules/Display/PL110/main.c diff --git a/Kernel/drvutil.c b/Kernel/drvutil.c index e86f9a87..3835f0ce 100644 --- a/Kernel/drvutil.c +++ b/Kernel/drvutil.c @@ -7,9 +7,24 @@ #include #include +// === TYPES === + +// === PROTOTYPES === +//int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers); +//size_t DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Src); +void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour); +void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H); + +// === GLOBALS === +tDrvUtil_Video_2DHandlers gDrvUtil_Stub_2DFunctions = { + NULL, + DrvUtil_Video_2D_Fill, + DrvUtil_Video_2D_Blit +}; + // === CODE === // --- Video Driver Helpers --- -Uint64 DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, +int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers) { void *stream = Buffer; @@ -22,13 +37,16 @@ Uint64 DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, stream = (void*)((tVAddr)stream + 1); if(op > NUM_VIDEO_2DOPS) { - Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Unknown" - " operation %i", op); + Log_Warning("DrvUtil", + "DrvUtil_Video_2DStream: Unknown operation %i", + op); + return Length-rem; } if(op*sizeof(void*) > SizeofHandlers) { - Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver does" - " not support op %i", op); + Log_Warning("DrvUtil", + "DrvUtil_Video_2DStream: Driver does not support op %i", + op); return Length-rem; } @@ -81,6 +99,163 @@ Uint64 DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, return 0; } +int DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Buffer) +{ + Uint8 *dest; + ENTER("iMode pFBInfo xOffset xLength pBuffer", + Mode, FBInfo, Offset, Length, Buffer); + switch( Mode ) + { + case VIDEO_BUFFMT_TEXT: + { + tVT_Char *chars = Buffer; + int bytes_per_px = FBInfo->Depth / 8; + int widthInChars = FBInfo->Width/giVT_CharWidth; + int heightInChars = FBInfo->Height/giVT_CharHeight; + int x, y, i; + + Length /= sizeof(tVT_Char); Offset /= sizeof(tVT_Char); + + x = Offset % widthInChars; y = Offset / widthInChars; + + // Sanity Check + if(Offset > heightInChars * widthInChars) LEAVE_RET('i', 0); + if(y >= heightInChars) LEAVE_RET('i', 0); + + if( Offset + Length > heightInChars*widthInChars ) + { + Length = heightInChars*widthInChars - Offset; + } + + dest = FBInfo->Framebuffer; + dest += y * giVT_CharHeight * FBInfo->Pitch; + + for( i = 0; i < Length; i++ ) + { + if( y >= heightInChars ) + { + Log_Notice("DrvUtil", "Stopped at %i", i); + break; + } + + VT_Font_Render( + chars->Ch, + dest + x*giVT_CharWidth*bytes_per_px, FBInfo->Depth, FBInfo->Pitch, + VT_Colour12toN(chars->BGCol, FBInfo->Depth), + VT_Colour12toN(chars->FGCol, FBInfo->Depth) + ); + + chars ++; + x ++; + if( x >= widthInChars ) + { + x = 0; + y ++; + dest += FBInfo->Pitch*giVT_CharHeight; + } + } + Length = i * sizeof(tVT_Char); + } + break; + + case VIDEO_BUFFMT_FRAMEBUFFER: + if(FBInfo->Width*FBInfo->Height*4 < Offset+Length) + { + Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Framebuffer Overflow"); + return 0; + } + + //TODO: Handle non 32-bpp framebuffer modes + if( FBInfo->Depth != 32 ) { + Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Don't support non 32-bpp FB mode"); + return 0; + } + + + //TODO: Handle pitch != Width*BytesPerPixel + // Copy to Frambuffer + dest = (Uint8 *)FBInfo->Framebuffer + Offset; + memcpy(dest, Buffer, Length); + break; + + case VIDEO_BUFFMT_2DSTREAM: + Length = DrvUtil_Video_2DStream( + FBInfo, Buffer, Length, + &gDrvUtil_Stub_2DFunctions, sizeof(gDrvUtil_Stub_2DFunctions) + ); + break; + + default: + LEAVE('i', -1); + return -1; + } + LEAVE('x', Length); + return Length; +} + +void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour) +{ + tDrvUtil_Video_BufInfo *FBInfo = Ent; + + // TODO: Handle non-32bit modes + if( FBInfo->Depth != 32 ) return; + + // TODO: Be less hacky + int pitch = FBInfo->Pitch/4; + Uint32 *buf = (Uint32*)FBInfo->Framebuffer + Y*pitch + X; + while( H -- ) { + Uint32 *tmp; + int i; + tmp = buf; + for(i=W;i--;tmp++) *tmp = Colour; + buf += pitch; + } +} + +void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H) +{ + tDrvUtil_Video_BufInfo *FBInfo = Ent; + int scrnpitch = FBInfo->Pitch; + int bytes_per_px = (FBInfo->Depth + 7) / 8; + int dst = DstY*scrnpitch + DstX; + int src = SrcY*scrnpitch + SrcX; + int tmp; + + //Log("Vesa_2D_Blit: (Ent=%p, DstX=%i, DstY=%i, SrcX=%i, SrcY=%i, W=%i, H=%i)", + // Ent, DstX, DstY, SrcX, SrcY, W, H); + + if(SrcX + W > FBInfo->Width) W = FBInfo->Width - SrcX; + if(DstX + W > FBInfo->Width) W = FBInfo->Width - DstX; + if(SrcY + H > FBInfo->Height) H = FBInfo->Height - SrcY; + if(DstY + H > FBInfo->Height) H = FBInfo->Height - DstY; + + //Debug("W = %i, H = %i", W, H); + + if( dst > src ) { + // Reverse copy + dst += H*scrnpitch; + src += H*scrnpitch; + while( H -- ) { + dst -= scrnpitch; + src -= scrnpitch; + tmp = W*bytes_per_px; + for( tmp = W; tmp --; ) { + *((Uint8*)FBInfo->Framebuffer + dst + tmp) = *((Uint8*)FBInfo->Framebuffer + src + tmp); + } + } + } + else { + // Normal copy is OK + while( H -- ) { + memcpy((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, W*bytes_per_px); + dst += scrnpitch; + src += scrnpitch; + } + } + //Log("Vesa_2D_Blit: RETURN"); +} + + // --- Disk Driver Helpers --- Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer, tDrvUtil_Callback ReadBlocks, Uint64 BlockSize, Uint Argument) diff --git a/Kernel/include/api_drv_video.h b/Kernel/include/api_drv_video.h index a4eb46c0..28b98542 100644 --- a/Kernel/include/api_drv_video.h +++ b/Kernel/include/api_drv_video.h @@ -51,8 +51,8 @@ enum eTplVideo_IOCtl { * \return 1 if a mode was found, 0 otherwise * * Using avaliable modes matching the \a bpp and \a flags fields - * set the \a id field to the mode id of the mode with the closest - * \a width and \a height. + * set the \a id, \a width and \a heights fields to the closest + * matching mode. */ VIDEO_IOCTL_FINDMODE, @@ -111,8 +111,8 @@ typedef struct sVideo_IOCtl_Mode short id; //!< Mode ID Uint16 width; //!< Width Uint16 height; //!< Height - Uint8 bpp; //!< Bits per Unit (Character or Pixel, depending on \a flags) - Uint8 flags; //!< Mode Flags + Uint8 bpp; //!< Bits per pixel + Uint8 flags; //!< Mode Flags (none defined, should be zero) } tVideo_IOCtl_Mode; /** @@ -292,10 +292,40 @@ extern Uint16 VT_Colour12to15(Uint16 Col12); */ extern Uint32 VT_Colour12toN(Uint16 Col12, int Depth); +typedef struct sDrvUtil_Video_BufInfo tDrvUtil_Video_BufInfo; +typedef struct sDrvUtil_Video_2DHandlers tDrvUtil_Video_2DHandlers; + +/** + * \brief Framebuffer information used by all DrvUtil_Video functions + */ +struct sDrvUtil_Video_BufInfo +{ + /** + * \brief Framebuffer virtual address + */ + void *Framebuffer; + /** + * \brief Bytes between the start of each line + */ + int Pitch; + /** + * \brief Number of pixels in each line + */ + int Width; + /** + * \brief Total number of lines + */ + int Height; + /** + * \brief Bit depth of the framebuffer + */ + int Depth; +}; + /** * \brief Handlers for eTplVideo_2DCommands */ -typedef struct sDrvUtil_Video_2DHandlers +struct sDrvUtil_Video_2DHandlers { /** * \brief No Operation, Ignored @@ -323,7 +353,7 @@ typedef struct sDrvUtil_Video_2DHandlers * \see VIDEO_2DOP_BLIT */ void (*Blit)(void *Ent, Uint16 DestX, Uint16 DestY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H); -} tDrvUtil_Video_2DHandlers; +}; /** * \brief Handle a 2D operation stream for a driver @@ -334,7 +364,19 @@ typedef struct sDrvUtil_Video_2DHandlers * \param SizeofHandlers Size of \a tDrvUtil_Video_2DHandlers according * to the driver. Used as version control and error avoidence. */ -extern Uint64 DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, +extern int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers); +/** + * \brief Perform write operations to a LFB + * \param Mode Buffer mode (see eTplVideo_BufFormats) + * \param FBInfo Framebuffer descriptor, see type for details + * \param Offset Offset provided by VFS call + * \param Length Length provided by VFS call + * \param Src Data from VFS call + * \return Number of bytes written + * + * Handles all write modes in software, using the VT font calls for rendering. + */ +extern int DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Src); #endif diff --git a/Kernel/include/lib/keyvalue.h b/Kernel/include/lib/keyvalue.h new file mode 100644 index 00000000..ef37d5b3 --- /dev/null +++ b/Kernel/include/lib/keyvalue.h @@ -0,0 +1,43 @@ +/* + * Acess2 Kernel + * - By John Hodge + * + * include/keyvalue.h + * - Key/Value pair parsing + */ +#ifndef _ACESS_KEYVALUE_H_ +#define _ACESS_KEYVALUE_H_ + +typedef struct sKeyVal_ParseRules tKeyVal_ParseRules; +typedef struct sKeyVal_int_Rule tKeyVal_int_Rule; +typedef void (*tKeyVal_UnkCb)(char *String); +typedef void (*tKeyVal_KeyCb)(const char *Key, char *Value); + +/** + * \brief Handling rule for a key + */ +struct sKeyVal_int_Rule +{ + const char *Key; + const char *Type; // Acess printf format, with 'F' being a tKeyVal_KeyCb + void *Data; +}; + +struct sKeyVal_ParseRules +{ + /** + * \brief Function to call when no match is found + */ + tKeyVal_UnkCb Unknown; + tKeyVal_int_Rule Rules[]; +}; + +/** + * \brief Parse a NULL terminated list of strings as Key/Value pairs + * \param Rules Parsing rules + * \param Strings Input string list + */ +extern int KeyVal_ParseNull(tKeyVal_ParseRules *Rules, char **Strings); + +#endif + diff --git a/Modules/Display/PL110/Makefile b/Modules/Display/PL110/Makefile new file mode 100644 index 00000000..0f90347b --- /dev/null +++ b/Modules/Display/PL110/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = main.o +NAME = PL110 + +-include ../Makefile.tpl diff --git a/Modules/Display/PL110/main.c b/Modules/Display/PL110/main.c new file mode 100644 index 00000000..f1604c61 --- /dev/null +++ b/Modules/Display/PL110/main.c @@ -0,0 +1,294 @@ +/** + * Acess2 ARM PrimeCell Colour LCD Controller (PL110) Driver + * - By John Hodge (thePowersGang) + * + * main.c + * - Driver core + * + * + * NOTE: The PL110 is set to 24bpp, but these are stored as 32-bit words. + * This corresponds to the Acess 32bpp mode, as the Acess 24bpp is packed + */ +#define DEBUG 0 +#define VERSION ((0<<8)|10) +#include +#include +#include +#include +#include +#include +#include +#include + +#define ABS(a) ((a)>0?(a):-(a)) + +// === TYPEDEFS === +typedef struct sPL110 tPL110; + +struct sPL110 +{ + Uint32 LCDTiming0; + Uint32 LCDTiming1; + Uint32 LCDTiming2; + Uint32 LCDTiming3; + + Uint32 LCDUPBase; + Uint32 LCDLPBase; + Uint32 LCDIMSC; + Uint32 LCDControl; + Uint32 LCDRIS; + Uint32 LCDMIS; + Uint32 LCDICR; + Uint32 LCDUPCurr; + Uint32 LCDLPCurr; +}; + +// === CONSTANTS === +#define PL110_BASE 0x10020000 +const struct { + short W, H; +} caPL110_Modes[] = { + {640,480}, + {800,600}, + {1024,768} // MAX +}; +const int ciPL110_ModeCount = sizeof(caPL110_Modes)/sizeof(caPL110_Modes[0]); + +// === PROTOTYPES === +// Driver + int PL110_Install(char **Arguments); +void PL110_Uninstall(); +// Internal +// Filesystem +Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); +Uint64 PL110_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); + int PL110_IOCtl(tVFS_Node *node, int id, void *data); +// -- Internals + int PL110_int_SetResolution(int W, int H); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, PL110, PL110_Install, NULL, NULL); +tDevFS_Driver gPL110_DriverStruct = { + NULL, "PL110", + { + .Read = PL110_Read, + .Write = PL110_Write, + .IOCtl = PL110_IOCtl + } +}; +// -- Options +tPAddr gPL110_PhysBase = PL110_BASE; +// -- KeyVal parse rules +const tKeyVal_ParseRules gPL110_KeyValueParser = { + NULL, + { + {"Base", "P", &gPL110_PhysBase}, + {NULL, NULL, NULL} + } +}; +// -- Driver state + int giPL110_CurrentMode = 0; + int giPL110_BufferMode; + int giPL110_Width = 640; + int giPL110_Height = 480; +size_t giPL110_FramebufferSize; +tPL110 *gpPL110_IOMem; +tPAddr gPL110_FramebufferPhys; +void *gpPL110_Framebuffer; +// -- Misc +tDrvUtil_Video_BufInfo gPL110_DrvUtil_BufInfo; + +// === CODE === +/** + */ +int PL110_Install(char **Arguments) +{ +// KeyVal_Parse(&gPL110_KeyValueParser, Arguments); + + gpPL110_IOMem = (void*)MM_MapHWPages(gPL110_PhysBase, 1); + + PL110_int_SetResolution(caPL110_Modes[0].W, caPL110_Modes[0].H); + + DevFS_AddDevice( &gPL110_DriverStruct ); + + return 0; +} + +/** + * \brief Clean up resources for driver unloading + */ +void PL110_Uninstall() +{ +} + +/** + * \brief Read from the framebuffer + */ +Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) +{ + return 0; +} + +/** + * \brief Write to the framebuffer + */ +Uint64 PL110_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + return DrvUtil_Video_WriteLFB(giPL110_BufferMode, &gPL110_DrvUtil_BufInfo, Offset, Length, Buffer); +} + +const char *csaPL110_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL}; + +/** + * \brief Handle messages to the device + */ +int PL110_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + int ret = -2; + ENTER("pNode iID pData", Node, ID, Data); + + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaPL110_IOCtls); + + case VIDEO_IOCTL_SETBUFFORMAT: + ret = giPL110_BufferMode; + if(Data) giPL110_BufferMode = *(int*)Data; + break; + + case VIDEO_IOCTL_GETSETMODE: + if(Data) + { + int newMode; + + if( !CheckMem(Data, sizeof(int)) ) + LEAVE_RET('i', -1); + + newMode = *(int*)Data; + + if(newMode < 0 || newMode >= ciPL110_ModeCount) + LEAVE_RET('i', -1); + + if(newMode != giPL110_CurrentMode) + { + giPL110_CurrentMode = newMode; + PL110_int_SetResolution( caPL110_Modes[newMode].W, caPL110_Modes[newMode].H ); + } + } + ret = giPL110_CurrentMode; + break; + + case VIDEO_IOCTL_FINDMODE: + { + tVideo_IOCtl_Mode *mode = Data; + int closest, closestArea, reqArea = 0; + if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) + LEAVE_RET('i', -1); + if( mode->bpp != 32 ) + LEAVE_RET('i', 0); + if( mode->flags != 0 ) + LEAVE_RET('i', 0); + + ret = 0; + + for( int i = 0; i < ciPL110_ModeCount; i ++ ) + { + int area; + if(mode->width == caPL110_Modes[i].W && mode->height == caPL110_Modes[i].H) { + mode->id = i; + ret = 1; + break; + } + + area = caPL110_Modes[i].W * caPL110_Modes[i].H; + if(!reqArea) { + reqArea = mode->width * mode->height; + closest = i; + closestArea = area; + } + else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) { + closest = i; + closestArea = area; + } + } + + if( ret == 0 ) + { + mode->id = closest; + ret = 1; + } + mode->width = caPL110_Modes[mode->id].W; + mode->height = caPL110_Modes[mode->id].H; + break; + } + + case VIDEO_IOCTL_MODEINFO: + { + tVideo_IOCtl_Mode *mode = Data; + if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) + LEAVE_RET('i', -1); + if(mode->id < 0 || mode->id >= ciPL110_ModeCount) + LEAVE_RET('i', 0); + + + mode->bpp = 32; + mode->flags = 0; + mode->width = caPL110_Modes[mode->id].W; + mode->height = caPL110_Modes[mode->id].H; + + ret = 1; + break; + } + + case VIDEO_IOCTL_SETCURSOR: break; + + default: + LEAVE('i', -2); + return -2; + } + + LEAVE('i', ret); + return ret; +} + +// +// +// + +/** + */ +int PL110_int_SetResolution(int W, int H) +{ + W = (W + 15)/16; + if(W > 1024) W = 1024; + if(H > 768) H = 768; + + gpPL110_IOMem->LCDTiming0 = ((W/16)-1) << 2; + gpPL110_IOMem->LCDTiming1 = H-1; + gpPL110_IOMem->LCDTiming2 = (14 << 27); + gpPL110_IOMem->LCDTiming3 = 0; + + if( gpPL110_Framebuffer ) { + MM_UnmapHWPages((tVAddr)gpPL110_Framebuffer, (giPL110_FramebufferSize+0xFFF)>>12); + } + giPL110_FramebufferSize = W*H*4; + + gpPL110_Framebuffer = (void*)MM_AllocDMA( (giPL110_FramebufferSize+0xFFF)>>12, 32, &gPL110_FramebufferPhys ); + gpPL110_IOMem->LCDUPBase = gPL110_FramebufferPhys; + gpPL110_IOMem->LCDLPBase = 0; + + gpPL110_IOMem->LCDIMSC = 0; + gpPL110_IOMem->LCDControl = (1 << 11)|(1 << 5)|(5<<1)|1; + + giPL110_Width = W; + giPL110_Height = H; + + // Update the DrvUtil buffer info + gPL110_DrvUtil_BufInfo.Framebuffer = gpPL110_Framebuffer; + gPL110_DrvUtil_BufInfo.Pitch = W * 4; + gPL110_DrvUtil_BufInfo.Width = W; + gPL110_DrvUtil_BufInfo.Height = H; + gPL110_DrvUtil_BufInfo.Depth = 32; + + return 0; +} diff --git a/Modules/Display/VESA/main.c b/Modules/Display/VESA/main.c index c18891f4..a227f179 100644 --- a/Modules/Display/VESA/main.c +++ b/Modules/Display/VESA/main.c @@ -390,10 +390,7 @@ int Vesa_Ioctl(tVFS_Node *Node, int ID, void *Data) case VIDEO_IOCTL_SETBUFFORMAT: ret = giVesaCurrentFormat; - if(Data) { - //Log_Log("VESA", "Buffer mode to %i", *(int*)Data); - giVesaCurrentFormat = *(int*)Data; - } + if(Data) giVesaCurrentFormat = *(int*)Data; return ret; case VIDEO_IOCTL_SETCURSOR: // Set cursor position -- 2.20.1