Skip to content

Commit

Permalink
rpi framebuffer fixes from pssc - raspberrypi/firmware#354
Browse files Browse the repository at this point in the history
he writes:
number of fixes for the frame buffer and HW double buffering specifically around the Pi, making it more robust in general and coping with extended key codes better.
On a mode change on the Pi the FIXSCREEN info can change, including the base address of the bufferer and offsets stuffing up the Flips etc

Having reread all the docs and code I am more inclined to believe this is an incorrect assumption in the original SDL fbcon driver. As 'unchangeable information about a frame buffer device and
a specific video mode' had been interpreted as this info will never change, which Is a trap I also fell into on my first reading and is the assumption the original code makes.
  • Loading branch information
joolswills committed Feb 12, 2015
1 parent 184efb7 commit 18831ad
Showing 1 changed file with 79 additions and 27 deletions.
106 changes: 79 additions & 27 deletions src/video/fbcon/SDL_fbvideo.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,22 +65,29 @@ static inline void outb (unsigned char value, unsigned short port)
#endif /* FB_TYPE_VGA_PLANES */

/* A list of video resolutions that we query for (sorted largest to smallest) */
/* http://en.wikipedia.org/wiki/Graphics_display_resolution */
static const SDL_Rect checkres[] = {
{ 0, 0, 1600, 1200 }, /* 16 bpp: 0x11E, or 286 */
{ 0, 0, 1408, 1056 }, /* 16 bpp: 0x19A, or 410 */
{ 0, 0, 1280, 1024 }, /* 16 bpp: 0x11A, or 282 */
{ 0, 0, 1152, 864 }, /* 16 bpp: 0x192, or 402 */
{ 0, 0, 1024, 768 }, /* 16 bpp: 0x117, or 279 */
{ 0, 0, 1920, 1200 }, // WUXGA
{ 0, 0, 1920, 1080 }, // 1080p FHD 16:9 = 1.7
{ 0, 0, 1600, 1200 }, /* 16 bpp: 0x11E, or 286 / UXGA */
{ 0, 0, 1408, 1056 }, /* 16 bpp: 0x19A, or 410 */
{ 0, 0, 1280, 1024 }, /* 16 bpp: 0x11A, or 282 / SXGA */
{ 0, 0, 1280, 720 }, // 720p HD/WXGA 16:9 = 1.7
{ 0, 0, 1152, 864 }, /* 16 bpp: 0x192, or 402 / XGA+ */
{ 0, 0, 1024, 768 }, /* 16 bpp: 0x117, or 279 / XGA */
{ 0, 0, 960, 720 }, /* 16 bpp: 0x18A, or 394 */
{ 0, 0, 800, 600 }, /* 16 bpp: 0x114, or 276 */
{ 0, 0, 800, 600 }, /* 16 bpp: 0x114, or 276 / SVGA */
{ 0, 0, 800, 480 }, // WVGA 5:3 = 1.6
{ 0, 0, 768, 576 }, /* 16 bpp: 0x182, or 386 */
{ 0, 0, 720, 576 }, /* PAL */
{ 0, 0, 720, 480 }, /* NTSC */
{ 0, 0, 640, 480 }, /* 16 bpp: 0x111, or 273 */
{ 0, 0, 640, 400 }, /* 8 bpp: 0x100, or 256 */
{ 0, 0, 512, 384 },
{ 0, 0, 320, 240 },
{ 0, 0, 320, 200 }
{ 0, 0, 480, 320 }, // HVGA 3:2 = 1.5
{ 0, 0, 480, 272 }, // WQVGA?
{ 0, 0, 320, 240 }, // QVGA 4:3 = 1.3
{ 0, 0, 320, 200 } // CGA 4:3 = 1.3
};
static const struct {
int xres;
Expand Down Expand Up @@ -177,6 +184,8 @@ static int SDL_getpagesize(void)
#endif
}

static void print_finfo(struct fb_fix_screeninfo *finfo);


/* Small wrapper for mmap() so we can play nicely with no-mmu hosts
* (non-mmu hosts disallow the MAP_SHARED flag) */
Expand Down Expand Up @@ -329,6 +338,8 @@ static int read_fbmodes_mode(FILE *f, struct fb_var_screeninfo *vinfo)
}
while(1);

SDL_memset(vinfo, 0, sizeof(struct fb_var_screeninfo)); // prevent random junk

SDL_sscanf(line, "geometry %d %d %d %d %d", &vinfo->xres, &vinfo->yres,
&vinfo->xres_virtual, &vinfo->yres_virtual, &vinfo->bits_per_pixel);
if (read_fbmodes_line(f, line, sizeof(line))==0)
Expand Down Expand Up @@ -495,7 +506,6 @@ static void FB_SortModes(_THIS)

static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat)
{
const int pagesize = SDL_getpagesize();
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
int i, j;
Expand Down Expand Up @@ -533,6 +543,10 @@ static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat)
FB_VideoQuit(this);
return(-1);
}
#ifdef FBCON_DEBUG
print_finfo(&finfo);
#endif

switch (finfo.type) {
case FB_TYPE_PACKED_PIXELS:
/* Supported, no worries.. */
Expand Down Expand Up @@ -578,7 +592,7 @@ static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat)

/* Memory map the device, compensating for buggy PPC mmap() */
mapped_offset = (((long)finfo.smem_start) -
(((long)finfo.smem_start)&~(pagesize-1)));
(((long)finfo.smem_start)&~(SDL_getpagesize()-1)));
mapped_memlen = finfo.smem_len+mapped_offset;
mapped_mem = do_mmap(NULL, mapped_memlen,
PROT_READ|PROT_WRITE, MAP_SHARED, console_fd, 0);
Expand Down Expand Up @@ -885,6 +899,10 @@ static int choose_fbmodes_mode(struct fb_var_screeninfo *vinfo)
while ( read_fbmodes_mode(modesdb, &cinfo) ) {
if ( (vinfo->xres == cinfo.xres && vinfo->yres == cinfo.yres) &&
(!matched || (vinfo->bits_per_pixel == cinfo.bits_per_pixel)) ) {
#ifdef FBCON_DEBUG
fprintf(stderr, "Using FBModes timings for %dx%d\n",
vinfo->xres, vinfo->yres);
#endif
vinfo->pixclock = cinfo.pixclock;
vinfo->left_margin = cinfo.left_margin;
vinfo->right_margin = cinfo.right_margin;
Expand Down Expand Up @@ -1015,13 +1033,20 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
/* Restore the original palette */
FB_RestorePalette(this);

SDL_memset(&vinfo, 0, sizeof(vinfo));
/* Set the video mode and get the final screen format */
if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
SDL_SetError("Couldn't get console screen info");
return(NULL);
}
/* Get the type of video hardware */
if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
SDL_SetError("Couldn't get console hardware info");
return(NULL);
}
#ifdef FBCON_DEBUG
fprintf(stderr, "Printing original vinfo:\n");
fprintf(stderr, "Printing original info:\n");
print_finfo(&finfo);
print_vinfo(&vinfo);
#endif
/* Do not use double buffering with shadow buffer */
Expand All @@ -1031,6 +1056,10 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,

if ( (vinfo.xres != width) || (vinfo.yres != height) ||
(vinfo.bits_per_pixel != bpp) || (flags & SDL_DOUBLEBUF) ) {
#ifdef FBCON_DEBUG
fprintf(stderr, "Request %dx%d %d Actual %dx%d %d %s flags %x current %dx%d\n",width,height,bpp,vinfo.xres,vinfo.yres,vinfo.bits_per_pixel,(flags & SDL_DOUBLEBUF) ? "SDL_DOUBLEBUF" : "" ,flags , current->w,current->h);
#endif
SDL_memset(&vinfo, 0, sizeof(vinfo));
vinfo.activate = FB_ACTIVATE_NOW;
vinfo.accel_flags = 0;
vinfo.bits_per_pixel = bpp;
Expand All @@ -1048,6 +1077,9 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
vinfo.green.length = vinfo.green.offset = 0;
vinfo.blue.length = vinfo.blue.offset = 0;
vinfo.transp.length = vinfo.transp.offset = 0;
// vinfo.height = 0;
// vinfo.width = 0;
// vinfo.vmode |= FB_VMODE_CONUPDATE;
if ( ! choose_fbmodes_mode(&vinfo) ) {
choose_vesa_mode(&vinfo);
}
Expand Down Expand Up @@ -1076,11 +1108,20 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
vinfo.yres_virtual = maxheight;
}
}
cache_vinfo = vinfo;
/* Get the fixed information about the console hardware.
This is necessary since finfo.line_length changes.
and in case RPI the frame buffer offsets and length change
*/
if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
SDL_SetError("Couldn't get console hardware info");
return(NULL);
}
#ifdef FBCON_DEBUG
fprintf(stderr, "Printing actual vinfo:\n");
fprintf(stderr, "Printing actual info:\n");
print_finfo(&finfo);
print_vinfo(&vinfo);
#endif
cache_vinfo = vinfo;
Rmask = 0;
for ( i=0; i<vinfo.red.length; ++i ) {
Rmask <<= 1;
Expand All @@ -1100,15 +1141,6 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
Rmask, Gmask, Bmask, 0) ) {
return(NULL);
}

/* Get the fixed information about the console hardware.
This is necessary since finfo.line_length changes.
*/
if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
SDL_SetError("Couldn't get console hardware info");
return(NULL);
}

/* Save hardware palette, if needed */
FB_SavePalette(this, &finfo, &vinfo);

Expand All @@ -1129,6 +1161,20 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
}
}

munmap(mapped_mem, mapped_memlen);
/* Memory map the device, compensating for buggy PPC mmap() */
mapped_offset = (((long)finfo.smem_start) -
(((long)finfo.smem_start)&~(SDL_getpagesize()-1)));
mapped_memlen = finfo.smem_len+mapped_offset;
mapped_mem = do_mmap(NULL, mapped_memlen,
PROT_READ|PROT_WRITE, MAP_SHARED, console_fd, 0);
if ( mapped_mem == (char *)-1 ) {
SDL_SetError("Unable to memory map the video hardware");
mapped_mem = NULL;
FB_VideoQuit(this);
return(NULL);
}

/* Set up the new mode framebuffer */
current->flags &= SDL_FULLSCREEN;
if (shadow_fb) {
Expand Down Expand Up @@ -1167,7 +1213,7 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,

/* Update for double-buffering, if we can */
if ( flags & SDL_DOUBLEBUF ) {
if ( vinfo.yres_virtual == (height*2) ) {
if ( vinfo.yres_virtual >= (vinfo.yres*2) ) {
current->flags |= SDL_DOUBLEBUF;
flip_page = 0;
flip_address[0] = (char *)current->pixels;
Expand All @@ -1176,6 +1222,10 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
this->screen = current;
FB_FlipHWSurface(this, current);
this->screen = NULL;
#ifdef FBCON_DEBUG
fprintf(stderr, "SDL_DOUBLEBUF 0:%x 1:%x pitch %x\n",(unsigned int)flip_address[0],(unsigned int) flip_address[1],current->pitch);
#endif

}
}

Expand Down Expand Up @@ -1409,9 +1459,7 @@ static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface)

static void FB_WaitVBL(_THIS)
{
#ifdef FBIOWAITRETRACE /* Heheh, this didn't make it into the main kernel */
ioctl(console_fd, FBIOWAITRETRACE, 0);
#endif
ioctl(console_fd, FBIO_WAITFORVSYNC, 0);
return;
}

Expand All @@ -1426,8 +1474,12 @@ static int FB_FlipHWSurface(_THIS, SDL_Surface *surface)
return -2; /* no hardware access */
}

#ifdef FBCON_DEBUG
fprintf(stderr, "Flip vinfo offset changing to %d current:\n",flip_page*cache_vinfo.yres);
print_vinfo(&cache_vinfo);
#endif
/* Wait for vertical retrace and then flip display */
cache_vinfo.yoffset = flip_page*surface->h;
cache_vinfo.yoffset = flip_page*cache_vinfo.yres;
if ( FB_IsSurfaceBusy(this->screen) ) {
FB_WaitBusySurfaces(this);
}
Expand Down

0 comments on commit 18831ad

Please sign in to comment.