/* -------------------------------------------------------------------- */ /* LINUX FBCON FRAMEBUFFER UTILITY CODE */ /* makes setting up the framebuffer easy. Also makes it eays to port to */ /* some other system if needed. */ /* Copyright (c) 1999 - Carsten Haitzler (The Rasterman) */ /* -------------------------------------------------------------------- */ #include "evas_common.h" #include "evas_fb.h" #include #include #include #include #include #define FB_ACTIVE 0 #define FB_REL_REQ 1 #define FB_INACTIVE 2 #define FB_ACQ_REQ 3 /* -------------------------------------------------------------------- */ /* internal variables */ static struct fb_fix_screeninfo fb_fix; static int fb, tty; static int bpp, depth; //static int orig_vt_no = 0; static int kd_mode; static struct vt_mode vt_omode; static struct fb_var_screeninfo fb_ovar; static unsigned short ored[256], ogreen[256], oblue[256]; static unsigned short red[256], green[256], blue[256]; static struct fb_cmap ocmap = { 0, 256, ored, ogreen, oblue, NULL }; static struct fb_cmap cmap = { 0, 256, red, green, blue, NULL }; /* -------------------------------------------------------------------- */ /* internal function prototypes */ static void fb_cleanup(void); //static void fb_cleanup_fork(void); //static void fb_setvt(int vtno); static void fb_init_palette_332(FB_Mode *mode); static void fb_init_palette_linear(FB_Mode *mode); /* -------------------------------------------------------------------- */ /* palette setting */ static void fb_init_palette_332(FB_Mode *mode) { int r, g, b, i; if (mode->fb_var.bits_per_pixel != 8) return; i = 0; if (ioctl(fb, FBIOGETCMAP, &cmap) == -1) perror("ioctl FBIOGETCMAP"); /* generate the palette */ for (r = 0; r < 8; r++) { for (g = 0; g < 8; g++) { for (b = 0; b < 4; b++) { int val; val = (r << 5) | (r << 2) | (r >> 1); red[i] = (val << 8) | val; val = (g << 5) | (g << 2) | (g >> 1); green[i] = (val << 8) | val; val = (b << 6) | (b << 4) | (b << 2) | (b); blue[i] = (val << 8) | val; i++; } } } /* set colormap */ if (ioctl(fb, FBIOPUTCMAP, &cmap) == -1) perror("ioctl FBIOPUTCMAP"); } static void fb_init_palette_linear(FB_Mode *mode) { int i; if (mode->fb_var.bits_per_pixel != 8) return; if (ioctl(fb, FBIOGETCMAP, &cmap) == -1) perror("ioctl FBIOGETCMAP"); /* generate the palette */ for (i = 0; i < 256; i++) red[i] = (i << 8) | i; for (i = 0; i < 256; i++) green[i] = (i << 8) | i; for (i = 0; i < 256; i++) blue[i] = (i << 8) | i; /* set colormap */ if (ioctl(fb, FBIOPUTCMAP, &cmap) == -1) perror("ioctl FBIOPUTCMAP"); } /* -------------------------------------------------------------------- */ /* initialisation & cleanup */ FB_Mode * fb_list_modes(unsigned int *num_return) { FILE *f; char line[256], label[256], value[256]; FB_Mode *modes = NULL; int num; num = 0; f = fopen("/etc/fb.modes","r"); if (!f) { *num_return = 0; return NULL; } while (fgets(line, sizeof(line) - 1, f)) { if (sscanf(line, "mode \"%250[^\"]\"", label) == 1) { char f1[32], f2[32], f3[32], f4[32]; f1[0] = 0; f2[0] = 0; f3[0] = 0; f4[0] = 0; sscanf(label, "%30[^x]x%30[^-]-%30[^-]-%30s", f1, f2, f3, f4); if ((f1[0]) && (f2[0])) { int geometry = 0; int timings = 0; num++; modes = realloc(modes, num * sizeof(FB_Mode)); modes[num - 1].width = atoi(f1); modes[num - 1].height = atoi(f2); if (f3[0]) modes[num - 1].refresh = atoi(f3); else modes[num - 1].refresh = 0; modes[num - 1].fb_var.sync = 0; while ((fgets(line, sizeof(line) - 1, f)) && (!strstr(line, "endmode"))) { if (sscanf(line," geometry %i %i %i %i %i", &modes[num - 1].fb_var.xres, &modes[num - 1].fb_var.yres, &modes[num - 1].fb_var.xres_virtual, &modes[num - 1].fb_var.yres_virtual, &modes[num - 1].fb_var.bits_per_pixel) == 5) geometry = 1; if (sscanf(line," timings %i %i %i %i %i %i %i", &modes[num - 1].fb_var.pixclock, &modes[num - 1].fb_var.left_margin, &modes[num - 1].fb_var.right_margin, &modes[num - 1].fb_var.upper_margin, &modes[num - 1].fb_var.lower_margin, &modes[num - 1].fb_var.hsync_len, &modes[num - 1].fb_var.vsync_len) == 7) timings = 1; if ((sscanf(line, " hsync %15s", value) == 1) && (!strcmp(value,"high"))) modes[num - 1].fb_var.sync |= FB_SYNC_HOR_HIGH_ACT; if ((sscanf(line, " vsync %15s", value) == 1) && (!strcmp(value,"high"))) modes[num - 1].fb_var.sync |= FB_SYNC_VERT_HIGH_ACT; if ((sscanf(line, " csync %15s", value) == 1) && (!strcmp(value,"high"))) modes[num - 1].fb_var.sync |= FB_SYNC_COMP_HIGH_ACT; if ((sscanf(line, " extsync %15s", value) == 1) && (!strcmp(value,"true"))) modes[num - 1].fb_var.sync |= FB_SYNC_EXT; if ((sscanf(line, " laced %15s", value) == 1) && (!strcmp(value,"true"))) modes[num - 1].fb_var.vmode |= FB_VMODE_INTERLACED; if ((sscanf(line, " double %15s",value) == 1) && (!strcmp(value,"true"))) modes[num - 1].fb_var.vmode |= FB_VMODE_DOUBLE; } if ((!geometry) || (!timings)) { num--; if (num == 0) { free(modes); modes = NULL; } } else { modes[num - 1].fb_var.xoffset = 0; modes[num - 1].fb_var.yoffset = 0; } } } } fclose(f); *num_return = num; return modes; } FB_Mode * fb_setmode(unsigned int width, unsigned int height, unsigned int pdepth, unsigned int refresh) { FB_Mode *modes, *mode = NULL; unsigned int i, num_modes; modes = fb_list_modes(&num_modes); if (modes) { for (i = 0; i < num_modes; i++) { if ((modes[i].width == width) && (modes[i].height == height) && (!pdepth || modes[i].fb_var.bits_per_pixel == pdepth) && (modes[i].refresh == refresh)) { if (pdepth) modes[i].fb_var.bits_per_pixel = pdepth; if (ioctl(fb, FBIOPUT_VSCREENINFO, &modes[i].fb_var) == -1) perror("ioctl FBIOPUT_VSCREENINFO"); free(modes); return fb_getmode(); } } free(modes); } return mode; } FB_Mode * fb_changedepth(FB_Mode *cur_mode, unsigned int pdepth) { cur_mode->fb_var.bits_per_pixel = pdepth; if (ioctl(fb, FBIOPUT_VSCREENINFO, &cur_mode->fb_var) == -1) perror("ioctl FBIOPUT_VSCREENINFO"); free(cur_mode); return fb_getmode(); } FB_Mode * fb_changeres(FB_Mode *cur_mode, unsigned int width, unsigned int height, unsigned int refresh) { FB_Mode *modes; unsigned int i, num_modes; modes = fb_list_modes(&num_modes); if (modes) { for (i = 0; i < num_modes; i++) { if ((modes[i].width == width) && (modes[i].height == height) && (modes[i].refresh == refresh)) { modes[i].fb_var.bits_per_pixel = cur_mode->depth; if (ioctl(fb, FBIOPUT_VSCREENINFO, &modes[i].fb_var) == -1) perror("ioctl FBIOPUT_VSCREENINFO"); free(modes); free(cur_mode); return fb_getmode(); } } free(modes); } return cur_mode; } FB_Mode * fb_changemode(FB_Mode *cur_mode, unsigned int width, unsigned int height, unsigned int pdepth, unsigned int refresh) { FB_Mode *modes; unsigned int i, num_modes; modes = fb_list_modes(&num_modes); if (modes) { for (i = 0; i < num_modes; i++) { if ((modes[i].width == width) && (modes[i].height == height) && (!pdepth || modes[i].fb_var.bits_per_pixel == pdepth) && (modes[i].refresh == refresh)) { if (pdepth) modes[i].fb_var.bits_per_pixel = pdepth; if (ioctl(fb, FBIOPUT_VSCREENINFO, &modes[i].fb_var) == -1) perror("ioctl FBIOPUT_VSCREENINFO"); free(modes); free(cur_mode); return fb_getmode(); } } free(modes); } return cur_mode; } FB_Mode * fb_getmode(void) { FB_Mode *mode = NULL; int hpix, lines, clockrate; mode = malloc(sizeof(FB_Mode)); /* look what we have now ... */ if (ioctl(fb, FBIOGET_VSCREENINFO, &mode->fb_var) == -1) { perror("ioctl FBIOGET_VSCREENINFO"); return NULL; } mode->width = mode->fb_var.xres_virtual; mode->height = mode->fb_var.yres_virtual; hpix = mode->fb_var.left_margin + mode->fb_var.xres + mode->fb_var.right_margin + mode->fb_var.hsync_len; lines = mode->fb_var.upper_margin + mode->fb_var.yres + mode->fb_var.lower_margin + mode->fb_var.vsync_len; if (mode->fb_var.pixclock > 0) clockrate = 1000000 / mode->fb_var.pixclock; else clockrate = 0; if ((lines > 0) && (hpix > 0)) mode->refresh = clockrate * 1000000 / (lines * hpix); switch (mode->fb_var.bits_per_pixel) { case 1: bpp = 1; depth = 1; break; case 4: bpp = 1; depth = 4; break; case 8: bpp = 1; depth = 8; break; case 15: case 16: if (mode->fb_var.green.length == 6) depth = 16; else depth = 15; bpp = 2; break; case 24: depth = 24; bpp = mode->fb_var.bits_per_pixel / 8; break; case 32: depth = 32; bpp = mode->fb_var.bits_per_pixel / 8; break; default: ERR("Cannot handle framebuffer of depth %i", mode->fb_var.bits_per_pixel); fb_cleanup(); free(mode); return NULL; } mode->depth = depth; mode->bpp = bpp; if (mode->depth == 8) fb_init_palette_332(mode); else fb_init_palette_linear(mode); return mode; } /* XXX: unused static void fb_setvt(int vtno) { struct vt_stat vts; char vtname[32]; int vtfd; if (vtno < 0) { if ((ioctl(tty,VT_OPENQRY, &vtno) == -1)) { perror("ioctl VT_OPENQRY"); return; } if (vtno <= 0 ) { perror("ioctl VT_OPENQRY vtno <= 0"); return; } } vtno &= 0xff; sprintf(vtname, "/dev/tty%i", vtno); if (chown(vtname, getuid(), getgid()) != 0) { vtfd = 0; // do nothing - don't worry about chown } if (access(vtname,R_OK | W_OK) == -1) { CRIT("Access %s: %s",vtname,strerror(errno)); return; } vtfd = open(vtname,O_RDWR); if (ioctl(tty, VT_GETSTATE, &vts) == -1) { perror("ioctl VT_GETSTATE"); close(vtfd); return; } orig_vt_no = vts.v_active; close(vtfd); #if 0 if (ioctl(tty, VT_ACTIVATE, vtno) == -1) { perror("ioctl VT_ACTIVATE"); exit(1); } if (ioctl(tty, VT_WAITACTIVE, vtno) == -1) { perror("ioctl VT_WAITACTIVE"); exit(1); } #endif } */ void fb_init(int vt __UNUSED__, int device) { char dev[32]; tty = 0; #if 0 if (vt != 0) fb_setvt(vt); #endif if ( getenv("EVAS_FB_DEV") ) { fb = open(getenv("EVAS_FB_DEV"), O_RDWR); } else { sprintf(dev, "/dev/fb/%i", device); fb = open(dev, O_RDWR); if ( fb == -1 ) { sprintf(dev, "/dev/fb%i", device); fb = open(dev, O_RDWR); } } if (fb == -1) { CRIT("open %s: %s", dev, strerror(errno)); fb_cleanup(); return; } if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) == -1) { perror("ioctl FBIOGET_VSCREENINFO"); return; } if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) == -1) { perror("ioctl FBIOGET_FSCREENINFO"); return; } if ((fb_ovar.bits_per_pixel == 8) || (fb_fix.visual == FB_VISUAL_DIRECTCOLOR)) { if (ioctl(fb,FBIOGETCMAP , &ocmap) == -1) { perror("ioctl FBIOGETCMAP"); return; } } #if 0 if (isatty(0)) tty = 0; else if ((tty = open("/dev/tty",O_RDWR)) == -1) { CITICAL("open %s: %s", "/dev/tty", strerror(errno)); return; } if (tty) { if (ioctl(tty, KDGETMODE, &kd_mode) == -1) { perror("ioctl KDGETMODE"); return; } if (ioctl(tty, VT_GETMODE, &vt_omode) == -1) { perror("ioctl VT_GETMODE"); return; } } #endif } int fb_postinit(FB_Mode *mode) { if (ioctl(fb,FBIOGET_FSCREENINFO, &fb_fix) == -1) { perror("ioctl FBIOGET_FSCREENINFO"); fb_cleanup(); return 0; } if (fb_fix.type != FB_TYPE_PACKED_PIXELS) { CRIT("can handle only packed pixel frame buffers"); fb_cleanup(); return 0; } mode->mem_offset = (unsigned)(fb_fix.smem_start) & (getpagesize()-1); mode->mem = (unsigned char *)mmap(NULL, fb_fix.smem_len + mode->mem_offset, PROT_WRITE | PROT_READ, MAP_SHARED, fb, 0); if (mode->mem == MAP_FAILED) { perror("mmap"); fb_cleanup(); } /* move viewport to upper left corner */ if ((mode->fb_var.xoffset != 0) || (mode->fb_var.yoffset != 0)) { mode->fb_var.xoffset = 0; mode->fb_var.yoffset = 0; if (ioctl(fb, FBIOPAN_DISPLAY, &(mode->fb_var)) == -1) { perror("ioctl FBIOPAN_DISPLAY"); fb_cleanup(); } } #if 0 if (tty) { if (ioctl(tty,KDSETMODE, KD_GRAPHICS) == -1) { perror("ioctl KDSETMODE"); fb_cleanup(); } } #endif mode->fb_fd = fb; return fb; } static void fb_cleanup(void) { /* restore console */ if (ioctl(fb, FBIOPUT_VSCREENINFO, &fb_ovar) == -1) perror("ioctl FBIOPUT_VSCREENINFO"); if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) == -1) perror("ioctl FBIOGET_FSCREENINFO"); if ((fb_ovar.bits_per_pixel == 8) || (fb_fix.visual == FB_VISUAL_DIRECTCOLOR)) { if (ioctl(fb, FBIOPUTCMAP, &ocmap) == -1) perror("ioctl FBIOPUTCMAP"); } close(fb); if (tty) { if (ioctl(tty, KDSETMODE, kd_mode) == -1) perror("ioctl KDSETMODE"); if (ioctl(tty, VT_SETMODE, &vt_omode) == -1) perror("ioctl VT_SETMODE"); #if 0 if ((ioctl(tty, VT_ACTIVATE, orig_vt_no) == -1) && (orig_vt_no)) perror("ioctl VT_ACTIVATE"); #endif } close(tty); }