441 строка
9.9 KiB
C
441 строка
9.9 KiB
C
/*
|
|
* (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "vesabios.h"
|
|
|
|
static void
|
|
vga_enter(disp_adapter_t *adapter)
|
|
{
|
|
disp_acquire_vga_resources(adapter);
|
|
disp_device_active(adapter, 1, 1);
|
|
}
|
|
|
|
static void
|
|
vga_leave(disp_adapter_t *adapter)
|
|
{
|
|
vb_context_t *vb = adapter->ms_ctx;
|
|
|
|
disp_device_active(adapter, 0, vb->mode_set);
|
|
disp_release_vga_resources(adapter);
|
|
}
|
|
|
|
static int
|
|
setstride(vb_context_t *ctx)
|
|
{
|
|
vbios_regs_t regs;
|
|
|
|
memset(®s, 0, sizeof regs);
|
|
regs.eax = 0x4f06;
|
|
regs.ebx = 2;
|
|
regs.ecx = ctx->vidstride; /* set stride to specific width */
|
|
|
|
if (vbios_int(ctx->adapter->vbios, 0x10, ®s, 0) != -1)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
setmode(vb_context_t *ctx, unsigned mode, CRTCInfoBlock_t *crtc_params)
|
|
{
|
|
vbios_regs_t regs;
|
|
int xfer_size = 0;
|
|
struct vbios_context *vbios = ctx->adapter->vbios;
|
|
|
|
memset(®s, 0, sizeof regs);
|
|
if (mode < 0x100) {
|
|
/* VGA bios set mode */
|
|
regs.eax = mode;
|
|
} else {
|
|
/* VESA bios set mode */
|
|
regs.eax = 0x4f02;
|
|
regs.ebx = mode;
|
|
if (crtc_params != NULL) {
|
|
/* VBE 3.0 refresh rate support */
|
|
regs.ebx |= 1<<11;
|
|
regs.es = vbios->xfer_area_seg;
|
|
regs.edi = vbios->xfer_area_off;
|
|
xfer_size = sizeof (*crtc_params);
|
|
}
|
|
}
|
|
|
|
if (vbios_int(ctx->adapter->vbios, 0x10, ®s, xfer_size) != -1 &&
|
|
!(mode >= 0x100 && (regs.eax & 0xffff) != 0x004f))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/* Put the chip back into standard VGA mode */
|
|
void
|
|
vesa_restore_vga(disp_adapter_t *ctx)
|
|
{
|
|
vb_context_t *vb = ctx->ms_ctx;
|
|
|
|
vga_enter(ctx);
|
|
if (setmode(ctx->ms_ctx, 3, NULL) == -1)
|
|
disp_perror(ctx, "vesa_restore_vga");
|
|
vga_leave(ctx);
|
|
|
|
vb->mode_set = 0;
|
|
}
|
|
|
|
static void
|
|
vesa_get_crtc_params(disp_adapter_t *adapter,
|
|
int refresh, int xres, int yres, CRTCInfoBlock_t *crtc_params)
|
|
{
|
|
disp_crtc_settings_t crtc;
|
|
int h_gran;
|
|
|
|
if (disp_mode_get_entry(adapter, &crtc,
|
|
"/etc/system/config/crtc-settings", xres, yres, refresh) != 0) {
|
|
crtc.xres = xres;
|
|
crtc.yres = yres;
|
|
crtc.refresh = refresh;
|
|
crtc.h_granularity = h_gran = 8;
|
|
crtc.v_granularity = 1;
|
|
|
|
disp_crtc_calc(&crtc);
|
|
} else
|
|
h_gran = 1;
|
|
|
|
memset(crtc_params, 0, sizeof (*crtc_params));
|
|
|
|
crtc_params->HorizontalTotal = crtc.h_total * h_gran;
|
|
crtc_params->HorizontalSyncStart = crtc.h_sync_start * h_gran;
|
|
crtc_params->HorizontalSyncEnd =
|
|
(crtc.h_sync_start+crtc.h_sync_len) * h_gran;
|
|
crtc_params->VerticalTotal = crtc.v_total;
|
|
crtc_params->VerticalSyncStart = crtc.v_sync_start;
|
|
crtc_params->VerticalSyncEnd =
|
|
(crtc.v_sync_start+crtc.v_sync_len);
|
|
crtc_params->Flags = 0;
|
|
crtc_params->PixelClock = crtc.pixel_clock * 1000;
|
|
crtc_params->RefreshRate = crtc.refresh * 100;
|
|
}
|
|
/* Place details on `mode' into `info' struct */
|
|
int
|
|
vesa_get_modeinfo(disp_adapter_t *ctx,
|
|
int dispno, disp_mode_t mode, disp_mode_info_t *info)
|
|
{
|
|
VESAModeInfoStruct vmode;
|
|
VESAInfoBlockStruct VIB;
|
|
int rc = -1;
|
|
|
|
DISP_ASSERT(dispno == 0);
|
|
|
|
memset(info, 0, sizeof (*info));
|
|
|
|
vga_enter(ctx);
|
|
|
|
if (vesa_ModeInfo(ctx->vbios, mode & ~0x4000, &vmode) == NULL)
|
|
goto done;
|
|
if (vesa_InfoBlock(ctx->vbios, &VIB) == NULL)
|
|
goto done;
|
|
|
|
info->size = sizeof (*info);
|
|
info->mode = mode;
|
|
info->xres = vmode.XResolution;
|
|
info->yres = vmode.YResolution;
|
|
|
|
/* Linear frame buffer properties */
|
|
if (vmode.PhysBasePtr == 0)
|
|
goto done;
|
|
else
|
|
info->fb_addr = vmode.PhysBasePtr;
|
|
|
|
if (info->fb_addr == 0) {
|
|
errno = EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
info->fb_stride = vmode.BytesPerScanLine;
|
|
info->fb_size = VIB.TotalMemory * 0x10000;
|
|
|
|
switch (vmode.BitsPerPixel) {
|
|
case 8:
|
|
info->pixel_format = DISP_SURFACE_FORMAT_PAL8;
|
|
break;
|
|
case 15:
|
|
case 16:
|
|
if (vmode.GreenMaskSize == 5)
|
|
info->pixel_format = DISP_SURFACE_FORMAT_ARGB1555;
|
|
else
|
|
info->pixel_format = DISP_SURFACE_FORMAT_RGB565;
|
|
break;
|
|
case 24:
|
|
info->pixel_format = DISP_SURFACE_FORMAT_RGB888;
|
|
break;
|
|
case 32:
|
|
info->pixel_format = DISP_SURFACE_FORMAT_ARGB8888;
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
if (vesa_HaveSetDisplayOffset(ctx->vbios))
|
|
info->caps |= DISP_MCAP_SET_DISPLAY_OFFSET;
|
|
#endif
|
|
if (vesa_HaveDPMS(ctx->vbios))
|
|
info->caps |= DISP_MCAP_DPMS_SUPPORTED;
|
|
|
|
info->crtc_start_gran = DISP_BITS_PER_PIXEL(info->pixel_format);
|
|
|
|
// Panning supported if not 24bpp (24bpp screwed up strides on a lot of cards)
|
|
if(info->pixel_format != DISP_SURFACE_FORMAT_RGB888) {
|
|
info->caps |= DISP_MCAP_VIRTUAL_PANNING;
|
|
info->crtc_pitch_gran = 16;
|
|
info->max_virtual_width = 3200;
|
|
info->max_virtual_height = 2400;
|
|
}
|
|
|
|
if (VIB.VESAVersion >= 0x300) {
|
|
info->u.fixed.refresh[0] = 60;
|
|
info->u.fixed.refresh[1] = 70;
|
|
info->u.fixed.refresh[2] = 72;
|
|
info->u.fixed.refresh[3] = 75;
|
|
info->u.fixed.refresh[4] = 85;
|
|
info->u.fixed.refresh[5] = 0;
|
|
} else {
|
|
info->u.fixed.refresh[0] = 60; /* More than likely if not VESA 3 */
|
|
info->u.fixed.refresh[1] = 0;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
done:
|
|
vga_leave(ctx);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
vesa_set_mode(disp_adapter_t *ctx, int dispno, disp_mode_t mode,
|
|
disp_crtc_settings_t *settings, disp_surface_t *surf, unsigned flags)
|
|
{
|
|
vb_context_t *vb = ctx->ms_ctx;
|
|
disp_mode_info_t mi;
|
|
int rc;
|
|
CRTCInfoBlock_t *crtc_params;
|
|
|
|
DISP_ASSERT(dispno == 0);
|
|
|
|
if (vesa_get_modeinfo(ctx, 0, mode, &mi) == -1)
|
|
return -1;
|
|
|
|
if (vb->vidptr) {
|
|
disp_munmap_device_memory(vb->vidptr, mi.fb_size);
|
|
vb->vidptr = NULL;
|
|
}
|
|
|
|
if (mi.fb_addr == 0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
|
|
vb->vidptr = disp_mmap_device_memory(mi.fb_addr, mi.fb_size,
|
|
DISP_PROT_READ | DISP_PROT_WRITE, DISP_MAP_LAZY);
|
|
|
|
if (vb->vidptr == NULL)
|
|
return -1;
|
|
|
|
vb->vidbase = mi.fb_addr;
|
|
|
|
ctx->adapter_ram = vb->vidsize = mi.fb_size;
|
|
if(surf->stride > mi.fb_stride && mi.caps & DISP_MCAP_VIRTUAL_PANNING)
|
|
vb->vidstride = surf->stride;
|
|
else
|
|
vb->vidstride = mi.fb_stride;
|
|
vb->format = mi.pixel_format;
|
|
|
|
/* Fill in info about where to draw */
|
|
surf->vidptr = vb->vidptr;
|
|
surf->paddr = mi.fb_addr;
|
|
surf->offset = 0;
|
|
surf->stride = vb->vidstride;
|
|
surf->pixel_format = mi.pixel_format;
|
|
surf->flags = DISP_SURFACE_DISPLAYABLE | DISP_SURFACE_PAGE_ALIGNED |
|
|
DISP_SURFACE_CPU_LINEAR_READABLE |
|
|
DISP_SURFACE_CPU_LINEAR_WRITEABLE | DISP_SURFACE_2D_TARGETABLE;
|
|
|
|
if (!(mi.caps & DISP_MCAP_SET_DISPLAY_OFFSET)) {
|
|
/*
|
|
* There can only be a single displayable surface, and
|
|
* it must begin at offset 0
|
|
*/
|
|
memcpy(&vb->visible, surf, sizeof (*surf));
|
|
vb->visible.height = surf->height;
|
|
} else
|
|
vb->visible.height = 0;
|
|
|
|
vb->visible.offset = 0;
|
|
vb->current_mode_caps = mi.caps;
|
|
|
|
if (settings->refresh > 60) {
|
|
/* VBE 3.0 refresh rate support */
|
|
crtc_params = (void *)ctx->vbios->xfer_area_ptr;
|
|
vesa_get_crtc_params(ctx, settings->refresh, mi.xres, mi.yres,
|
|
crtc_params);
|
|
} else
|
|
crtc_params = NULL;
|
|
|
|
vga_enter(ctx);
|
|
rc = setmode(ctx->ms_ctx, mode, crtc_params);
|
|
|
|
if(surf->stride > mi.fb_stride && mi.caps & DISP_MCAP_VIRTUAL_PANNING)
|
|
if (setstride(ctx->ms_ctx) == -1)
|
|
disp_printf(vb->adapter,"Set Stride failed");
|
|
|
|
if (rc == 0)
|
|
vb->mode_set = 1;
|
|
else
|
|
errno = ENOSYS;
|
|
|
|
vga_leave(ctx);
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* Place a list of mode ID's in `list' */
|
|
int
|
|
vesa_get_modelist(disp_adapter_t *ctx, int dispno, unsigned short *list,
|
|
int index, int size)
|
|
{
|
|
uint16_t VModes[DISP_MAX_MODES + 1];
|
|
int i, j = 0, rc = -1;
|
|
VESAInfoBlockStruct VIB;
|
|
VESAModeInfoStruct VMI;
|
|
|
|
if (dispno != 0)
|
|
return -1;
|
|
|
|
list[0] = DISP_MODE_LISTEND;
|
|
|
|
vga_enter(ctx);
|
|
|
|
if (vesa_InfoBlock(ctx->vbios, &VIB) != NULL) {
|
|
/* Get the list of VESA modes */
|
|
if (vesa_ModeList(ctx->vbios, &VIB, VModes) == NULL)
|
|
goto done;
|
|
|
|
for (i = 0; i < DISP_MAX_MODES; i++) {
|
|
if (i < index)
|
|
continue;
|
|
if (j >= size-1)
|
|
break;
|
|
if (VModes[i] == 0xffff)
|
|
break;
|
|
(void)vesa_ModeInfo(ctx->vbios, VModes[i], &VMI);
|
|
if (VMI.MemoryModel != VESA_MM_DirectColor &&
|
|
!(VMI.MemoryModel == VESA_MM_VGA8Packed &&
|
|
(VModes[i] & 0xf00)))
|
|
continue;
|
|
/* Don't support any of this tripe yet */
|
|
if (VMI.NumberOfPlanes > 1 || VMI.NumberOfBanks > 1
|
|
|| VMI.BitsPerPixel < 8)
|
|
continue;
|
|
if (!(VMI.ModeAttributes & 1))
|
|
continue;
|
|
|
|
/* Check that mode fits into memory */
|
|
if ((VMI.BytesPerScanLine * VMI.YResolution) > (VIB.TotalMemory * 0x10000))
|
|
continue;
|
|
|
|
if (VModes[i] >= 0x100 && (VMI.ModeAttributes & 0x80) &&
|
|
VIB.VESAVersion >= 0x200) {
|
|
/* A linear VESA mode */
|
|
list[j++] = VModes[i] | 0x4000;
|
|
}
|
|
rc = 0;
|
|
}
|
|
}
|
|
|
|
list[j] = DISP_MODE_LISTEND;
|
|
|
|
done:
|
|
vga_leave(ctx);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
vesa_set_display_offset(disp_adapter_t *ctx,
|
|
int dispno, unsigned offset, int wait_vsync)
|
|
{
|
|
vb_context_t *vb = ctx->ms_ctx;
|
|
int rc;
|
|
|
|
DISP_ASSERT(dispno == 0);
|
|
|
|
vga_enter(ctx);
|
|
|
|
rc = vesa_SetDisplayOffset(ctx->vbios,
|
|
offset%vb->vidstride, offset/vb->vidstride, wait_vsync);
|
|
|
|
vga_leave(ctx);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
vesa_set_scroll_pos(disp_adapter_t *adapter, int dispno,
|
|
unsigned xoff, unsigned yoff)
|
|
{
|
|
vb_context_t *vb = adapter->ms_ctx;
|
|
int rc;
|
|
unsigned offset;
|
|
|
|
offset = (yoff*vb->vidstride) +
|
|
((xoff*DISP_BITS_PER_PIXEL(vb->format)+ 7)>>3);
|
|
|
|
DISP_ASSERT(dispno == 0);
|
|
|
|
vga_enter(adapter);
|
|
|
|
rc = vesa_SetDisplayOffset(adapter->vbios,
|
|
(offset/DISP_BYTES_PER_PIXEL(vb->format))%vb->vidstride,
|
|
offset/vb->vidstride, 0);
|
|
|
|
vga_leave(adapter);
|
|
}
|
|
|
|
int
|
|
vesa_set_palette(disp_adapter_t *ctx, int dispno,
|
|
int index, int count, disp_color_t *pal)
|
|
{
|
|
int rc;
|
|
|
|
DISP_ASSERT(dispno == 0);
|
|
|
|
vga_enter(ctx);
|
|
|
|
rc = vesa_BIOSSetPalette(ctx->vbios, pal, index, count);
|
|
|
|
vga_leave(ctx);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
vesa_set_dpms_mode(disp_adapter_t *ctx, int dispno, int dpms_mode)
|
|
{
|
|
int rc;
|
|
|
|
DISP_ASSERT(dispno == 0);
|
|
|
|
vga_enter(ctx);
|
|
|
|
rc = vesa_SetDPMSMode(ctx->vbios, dpms_mode);
|
|
|
|
vga_leave(ctx);
|
|
|
|
return rc;
|
|
}
|