/** * @file fbdev.c * */ /********************* * INCLUDES *********************/ #include "fbdev.h" #if USE_FBDEV || USE_BSD_FBDEV #include #include #include #include #include #include #include #if USE_BSD_FBDEV #include #include #include #include #else /* USE_BSD_FBDEV */ #include #endif /* USE_BSD_FBDEV */ /********************* * DEFINES *********************/ #ifndef FBDEV_PATH #define FBDEV_PATH "/dev/fb0" #endif #ifndef DIV_ROUND_UP #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #endif /********************** * TYPEDEFS **********************/ /********************** * STRUCTURES **********************/ struct bsd_fb_var_info{ uint32_t xoffset; uint32_t yoffset; uint32_t xres; uint32_t yres; int bits_per_pixel; }; struct bsd_fb_fix_info{ long int line_length; long int smem_len; }; /********************** * STATIC PROTOTYPES **********************/ /********************** * STATIC VARIABLES **********************/ #if USE_BSD_FBDEV static struct bsd_fb_var_info vinfo; static struct bsd_fb_fix_info finfo; #else static struct fb_var_screeninfo vinfo; static struct fb_fix_screeninfo finfo; #endif /* USE_BSD_FBDEV */ static char *fbp = 0; static long int screensize = 0; static int fbfd = 0; /********************** * MACROS **********************/ #if USE_BSD_FBDEV #define FBIOBLANK FBIO_BLANK #endif /* USE_BSD_FBDEV */ /********************** * GLOBAL FUNCTIONS **********************/ void fbdev_init(void) { // Open the file for reading and writing fbfd = open(FBDEV_PATH, O_RDWR); if(fbfd == -1) { perror("Error: cannot open framebuffer device"); return; } LV_LOG_INFO("The framebuffer device was opened successfully"); // Make sure that the display is on. if (ioctl(fbfd, FBIOBLANK, FB_BLANK_UNBLANK) != 0) { perror("ioctl(FBIOBLANK)"); // Don't return. Some framebuffer drivers like efifb or simplefb don't implement FBIOBLANK. } #if USE_BSD_FBDEV struct fbtype fb; unsigned line_length; //Get fb type if (ioctl(fbfd, FBIOGTYPE, &fb) != 0) { perror("ioctl(FBIOGTYPE)"); return; } //Get screen width if (ioctl(fbfd, FBIO_GETLINEWIDTH, &line_length) != 0) { perror("ioctl(FBIO_GETLINEWIDTH)"); return; } vinfo.xres = (unsigned) fb.fb_width; vinfo.yres = (unsigned) fb.fb_height; vinfo.bits_per_pixel = fb.fb_depth; vinfo.xoffset = 0; vinfo.yoffset = 0; finfo.line_length = line_length; finfo.smem_len = finfo.line_length * vinfo.yres; #else /* USE_BSD_FBDEV */ // Get fixed screen information if(ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) { perror("Error reading fixed information"); return; } // Get variable screen information if(ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) { perror("Error reading variable information"); return; } #endif /* USE_BSD_FBDEV */ LV_LOG_INFO("%dx%d, %dbpp", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel); // Figure out the size of the screen in bytes screensize = finfo.smem_len; //finfo.line_length * vinfo.yres; // Map the device to memory fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); if((intptr_t)fbp == -1) { perror("Error: failed to map framebuffer device to memory"); return; } // Don't initialise the memory to retain what's currently displayed / avoid clearing the screen. // This is important for applications that only draw to a subsection of the full framebuffer. LV_LOG_INFO("The framebuffer device was mapped to memory successfully"); } void fbdev_exit(void) { close(fbfd); } /** * Flush a buffer to the marked area * @param drv pointer to driver where this function belongs * @param area an area where to copy `color_p` * @param color_p an array of pixels to copy to the `area` part of the screen */ void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p) { if(fbp == NULL || area->x2 < 0 || area->y2 < 0 || area->x1 > (int32_t)vinfo.xres - 1 || area->y1 > (int32_t)vinfo.yres - 1) { lv_disp_flush_ready(drv); return; } /*Truncate the area to the screen*/ int32_t act_x1 = area->x1 < 0 ? 0 : area->x1; int32_t act_y1 = area->y1 < 0 ? 0 : area->y1; int32_t act_x2 = area->x2 > (int32_t)vinfo.xres - 1 ? (int32_t)vinfo.xres - 1 : area->x2; int32_t act_y2 = area->y2 > (int32_t)vinfo.yres - 1 ? (int32_t)vinfo.yres - 1 : area->y2; lv_coord_t w = (act_x2 - act_x1 + 1); long int location = 0; long int byte_location = 0; unsigned char bit_location = 0; /*32 bit per pixel*/ if(vinfo.bits_per_pixel == 32) { uint32_t * fbp32 = (uint32_t *)fbp; int32_t y; for(y = act_y1; y <= act_y2; y++) { location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length / 4; memcpy(&fbp32[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1) * 4); color_p += w; } } /*24 bit per pixel*/ else if(vinfo.bits_per_pixel == 24 && LV_COLOR_DEPTH == 32) { uint8_t * fbp8 = (uint8_t *)fbp; lv_coord_t x; int32_t y; uint8_t *pixel; for(y = act_y1; y <= act_y2; y++) { location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length / 3; for (x = 0; x < w; ++x) { pixel = (uint8_t *)(&color_p[x]); fbp8[3 * (location + x)] = pixel[0]; fbp8[3 * (location + x) + 1] = pixel[1]; fbp8[3 * (location + x) + 2] = pixel[2]; } color_p += w; } } /*16 bit per pixel*/ else if(vinfo.bits_per_pixel == 16) { uint16_t * fbp16 = (uint16_t *)fbp; int32_t y; for(y = act_y1; y <= act_y2; y++) { location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length / 2; memcpy(&fbp16[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1) * 2); color_p += w; } } /*8 bit per pixel*/ else if(vinfo.bits_per_pixel == 8) { uint8_t * fbp8 = (uint8_t *)fbp; int32_t y; for(y = act_y1; y <= act_y2; y++) { location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length; memcpy(&fbp8[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1)); color_p += w; } } /*1 bit per pixel*/ else if(vinfo.bits_per_pixel == 1) { uint8_t * fbp8 = (uint8_t *)fbp; int32_t x; int32_t y; for(y = act_y1; y <= act_y2; y++) { for(x = act_x1; x <= act_x2; x++) { location = (x + vinfo.xoffset) + (y + vinfo.yoffset) * vinfo.xres; byte_location = location / 8; /* find the byte we need to change */ bit_location = location % 8; /* inside the byte found, find the bit we need to change */ fbp8[byte_location] &= ~(((uint8_t)(1)) << bit_location); fbp8[byte_location] |= ((uint8_t)(color_p->full)) << bit_location; color_p++; } color_p += area->x2 - act_x2; } } else { /*Not supported bit per pixel*/ } //May be some direct update command is required //ret = ioctl(state->fd, FBIO_UPDATE, (unsigned long)((uintptr_t)rect)); lv_disp_flush_ready(drv); } void fbdev_get_sizes(uint32_t *width, uint32_t *height, uint32_t *dpi) { if (width) *width = vinfo.xres; if (height) *height = vinfo.yres; if (dpi && vinfo.height) *dpi = DIV_ROUND_UP(vinfo.xres * 254, vinfo.width * 10); } void fbdev_set_offset(uint32_t xoffset, uint32_t yoffset) { vinfo.xoffset = xoffset; vinfo.yoffset = yoffset; } /********************** * STATIC FUNCTIONS **********************/ #endif