QMK/lib/ugfx/boards/base/Linux-Framebuffer/board_framebuffer.h

223 lines
6.6 KiB
C

/*
* This file is subject to the terms of the GFX License. If a copy of
* the license was not distributed with this file, you can obtain one at:
*
* http://ugfx.io/license.html
*/
// Set this to your frame buffer pixel format. Note Linux frame buffer only supports RGB modes (no BGR modes).
#ifndef GDISP_LLD_PIXELFORMAT
#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB888
#endif
#ifdef GDISP_DRIVER_VMT
#define FBDEV_PATH1 "/dev/fb0"
#define FBDEV_PATH2 "/dev/fb/0" // Optional - comment this out to only try the one device
#define USE_SET_MODE // Optional - comment this out to not to try to set the color mode we want
//#define VTDEV_PATH "/dev/tty0" // Optional - if defined use this tty to switch from text to graphics mode
#define _GNU_SOURCE 1
#include <fcntl.h>
#include <limits.h>
#include <linux/fb.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#if VTDEV_PATH
static void board_revert2textmode(void) {
int tty;
// Go back to text mode
if ((tty = open(VTDEV_PATH, O_RDWR)) >= 0) {
ioctl(tty, KDSETMODE, KD_TEXT);
close(tty);
}
}
static void board_switch2graphicsmode(void) {
int tty;
// Open the tty
if ((tty = open (VTDEV_PATH, O_RDWR)) < 0) {
fprintf(stderr, "GDISP Framebuffer: Cannot open tty %s\n", VTDEV_PATH);
exit(-1);
}
if (ioctl (tty, KDSETMODE, KD_GRAPHICS) == -1) {
fprintf(stderr, "GDISP Framebuffer: Cannot set to graphics mode\n");
exit(-1);
}
close(tty);
// Make sure we clean up properly
atexit(board_revert2textmode)
}
#endif
static void board_init(GDisplay *g, fbInfo *fbi) {
int fb;
char * env;
size_t fblen;
struct fb_fix_screeninfo fb_fix;
struct fb_var_screeninfo fb_var;
// Open the frame buffer device
if((env = getenv("FRAMEBUFFER")) != 0)
fb = open(env, O_RDWR);
else {
fb = open(FBDEV_PATH1, O_RDWR);
#ifdef FBDEV_PATH2
if (fb < 0) fb = open(FBDEV_PATH2, O_RDWR);
#endif
}
if(fb < 0) {
fprintf(stderr, "GDISP Framebuffer: Error opening the framebuffer device\n");
exit(-1);
}
// Get screen info
if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) == -1 || ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) == -1) {
fprintf(stderr, "GDISP Framebuffer: Error getting screen info\n");
exit(-1);
}
#ifdef USE_SET_MODE
fb_var.reserved[0] = 0;
fb_var.reserved[1] = 0;
fb_var.reserved[2] = 0;
fb_var.xoffset = 0;
fb_var.yoffset = 0;
#if LLDCOLOR_BITS == 15
fb_var.bits_per_pixel = LLDCOLOR_BITS; // Handle RGB555 & BGR555
#else
fb_var.bits_per_pixel = sizeof(LLDCOLOR_TYPE)*8;
#endif
fb_var.grayscale = 0;
fb_var.activate = FB_ACTIVATE_NOW;
if (ioctl(fb, FBIOPUT_VSCREENINFO, &fb_var) == -1 || ioctl (fb, FBIOGET_VSCREENINFO, &fb_var) == -1) {
fprintf(stderr, "GDISP Framebuffer: Failed to set video mode\n");
exit(-1);
}
#endif
// Check things are as they should be
if (fb_fix.type != FB_TYPE_PACKED_PIXELS) {
fprintf(stderr, "GDISP Framebuffer: The display is not in a single plane graphics mode\n");
exit(-1);
}
if (fb_fix.visual != FB_VISUAL_TRUECOLOR) {
fprintf(stderr, "GDISP Framebuffer: The display is not in TRUECOLOR mode\n");
exit(-1);
}
if (fb_var.bits_per_pixel != sizeof(LLDCOLOR_TYPE)*8) {
fprintf(stderr, "GDISP Framebuffer: The display is %u not %u bits per pixel\n", fb_var.bits_per_pixel, LLDCOLOR_TYPE_BITS);
exit(-1);
}
if (fb_var.red.length != LLDCOLOR_BITS_R || fb_var.green.length != LLDCOLOR_BITS_G || fb_var.blue.length != LLDCOLOR_BITS_B) {
fprintf(stderr, "GDISP Framebuffer: The display pixel format is not %d%d%d\n", LLDCOLOR_BITS_R, LLDCOLOR_BITS_G, LLDCOLOR_BITS_B);
exit(-1);
}
if (fb_var.red.offset != LLDCOLOR_SHIFT_R || fb_var.green.offset != LLDCOLOR_SHIFT_G || fb_var.blue.offset != LLDCOLOR_SHIFT_B) {
#if LLDCOLOR_SHIFT_B == 0
fprintf(stderr, "GDISP Framebuffer: The display pixel format is not RGB\n");
#else
fprintf(stderr, "GDISP Framebuffer: The display pixel format is not BGR\n");
#endif
exit(-1);
}
// Ensure we are at the origin of the virtual display area
if (fb_var.xoffset || fb_var.yoffset) {
fb_var.xoffset = 0;
fb_var.yoffset = 0;
ioctl(fb, FBIOPAN_DISPLAY, &fb_var);
}
// Switch to graphics mode (if required)
#ifdef VTDEV_PATH
board_switch2graphicsmode();
#endif
// Calculate the frame buffer length
fblen = fb_var.yres * fb_fix.line_length;
// Different systems need mapping in slightly different ways - Yuck!
#ifdef ARCH_LINUX_SPARC
#define CG3_MMAP_OFFSET 0x4000000
#define CG6_RAM 0x70016000
#define TCX_RAM8BIT 0x00000000
#define TCX_RAM24BIT 0x01000000
switch (fb_fix.accel) {
case FB_ACCEL_SUN_CGTHREE:
fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_SHARED, fb, CG3_MMAP_OFFSET);
break;
case FB_ACCEL_SUN_CGSIX:
fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_SHARED, fb, CG6_RAM);
break;
case FB_ACCEL_SUN_TCX:
fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_SHARED, fb, TCX_RAM24BIT);
break;
default:
fprintf(stderr, "GDISP Framebuffer: Don't know how to mmap with accel %d\n", fb_fix.accel);
exit(-1);
}
#elif defined(BLACKFIN)
fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FILE, fb, 0);
#elif defined(__uClinux__)
fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, 0, fb, 0);
#else
fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);
#endif
if(!fbi->pixels || fbi->pixels == (void *)-1) {
fprintf(stderr, "GDISP Framebuffer: mmap of display buffer failed\n");
exit(-1);
}
// If this program gets children they should not inherit this file descriptor
fcntl(fb, F_SETFD, FD_CLOEXEC);
// We are finished with the file descriptor
close(fb);
// Set the rest of the details of the frame buffer
g->g.Width = fb_var.xres;
g->g.Height = fb_var.yres;
g->g.Backlight = 100;
g->g.Contrast = 50;
fbi->linelen = fb_fix.line_length;
}
#if GDISP_HARDWARE_FLUSH
static void board_flush(GDisplay *g) {
(void) g;
}
#endif
#if GDISP_NEED_CONTROL
static void board_backlight(GDisplay *g, gU8 percent) {
(void) g;
(void) percent;
}
static void board_contrast(GDisplay *g, gU8 percent) {
(void) g;
(void) percent;
}
static void board_power(GDisplay *g, gPowermode pwr) {
(void) g;
(void) pwr;
}
#endif
#endif /* GDISP_LLD_BOARD_IMPLEMENTATION */