#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* DC8000 hw reg*/ #define FRAMEBUFFER_CONFIG 0x1518 #define FRAMEBUFFER_ADDRESS 0x1400 #define FRAMEBUFFER_STRIDE 0x1408 #define HDISPLAY 0x1430 #define HSYNC 0x1438 #define VDISPLAY 0x1440 #define VSYNC 0x1448 #define PANEL_CONFIG 0x1418 #define DPI_CONFIG 0x14B8 #define CURSOR_ADDRESS 0x146C #define CURSOR_CONFIG 0x1468 #define CURSOR_LOCATION 0x1470 #define CURSOR_BACKGROUND 0x1474 #define CURSOR_FOREGROUND 0x1478 #define FRAMEBUFFER_UPLANAR_ADDRESS 0x1530 #define FRAMEBUFFER_VPLANAR_ADDRESS 0x1538 #define FRAMEBUFFER_USTRIDE 0x1800 #define FRAMEBUFFER_VSTRIDE 0x1808 #define INDEXCOLOR_TABLEINDEX 0x1818 #define INDEXCOLOR_TABLEDATA 0x1820 #define FRAMEBUFFER_SIZE 0x1810 #define FRAMEBUFFER_SCALEFACTORX 0x1828 #define FRAMEBUFFER_SCALEFACTORY 0x1830 #define FRAMEBUFFER_SCALEFCONFIG 0x1520 #define HORIFILTER_KERNELINDEX 0x1838 #define HORIFILTER_KERNEL 0x1A00 #define VERTIFILTER_KERNELINDEX 0x1A08 #define VERTIFILTER_KERNEL 0x1A10 #define FRAMEBUFFER_INITIALOFFSET 0x1A20 #define FRAMEBUFFER_COLORKEY 0x1508 #define FRAMEBUFFER_COLORHIGHKEY 0x1510 #define FRAMEBUFFER_BGCOLOR 0x1528 #define FRAMEBUFFER_CLEARVALUE 0x1A18 #define DISPLAY_INTRENABLE 0x1480 #define INT_STATE 0x147C #define PANEL_DEST_ADDRESS 0x14F0 #define MEM_DEST_ADDRESS 0x14E8 #define DEST_CONFIG 0x14F8 #define DEST_STRIDE 0x1500 #define DBI_CONFIG 0x1488 #define AQHICLOCKCONTROL 0x0000 #define OVERLAY_CONFIG 0x1540 #define OVERLAY_STRIDE 0x1600 #define OVERLAY_USTRIDE 0x18C0 #define OVERLAY_VSTRIDE 0x1900 #define OVERLAY_TL 0x1640 #define OVERLAY_BR 0x1680 #define OVERLAY_ALPHA_BLEND_CONFIG 0x1580 #define OVERLAY_SRC_GLOBAL_COLOR 0x16C0 #define OVERLAY_DST_GLOBAL_COLOR 0x1700 #define OVERLAY_CLEAR_VALUE 0x1940 #define OVERLAY_SIZE 0x17C0 #define OVERLAY_COLOR_KEY 0x1740 #define OVERLAY_COLOR_KEY_HIGH 0x1780 #define OVERLAY_ADDRESS 0x15C0 #define OVERLAY_UPLANAR_ADDRESS 0x1840 #define OVERLAY_VPLANAR_ADDRESS 0x1880 #define OVERLAY_SCALE_CONFIG 0x1C00 #define OVERLAY_SCALE_FACTOR_X 0x1A40 #define OVERLAY_SCALE_FACTOR_Y 0x1A80 #define OVERLAY_HORI_FILTER_KERNEL_INDEX 0x1AC0 #define OVERLAY_HORI_FILTER_KERNEL 0x1B00 #define OVERLAY_VERTI_FILTER_KERNEL_INDEX 0x1B40 #define OVERLAY_VERTI_FILTER_KERNEL 0x1B80 #define OVERLAY_INITIAL_OFFSET 0x1BC0 #define GAMMA_EX_INDEX 0x1CF0 #define GAMMA_EX_DATA 0x1CF8 #define GAMMA_EX_ONE_DATA 0x1D80 #define GAMMA_INDEX 0x1458 #define GAMMA_DATA 0x1460 #define DISPLAY_DITHER_TABLE_LOW 0x1420 #define DISPLAY_DITHER_TABLE_HIGH 0x1428 #define DISPLAY_DITHER_CONFIG 0x1410 /* ultrafb.h */ #define ULTRAFBIO_CURSOR 0x46A0 #define ULTRAFBIO_BLENDING_MODE 0x46A4 #define ULTRAFBIO_GLOBAL_MODE_VALUE 0x46A5 #define ULTRAFBIO_BUFFER_SIZE 0x46A7 #define ULTRAFBIO_OVERLAY_RECT 0x46A8 #define ULTRAFBIO_SCALE_FILTER_TAP 0x46A9 #define ULTRAFBIO_SYNC_TABLE 0x46AA #define ULTRAFBIO_GAMMA 0x46BD #define ULTRAFBIO_DITHER 0x46BE #define ULTRAFBIO_ROTATION 0x46BF #define ULTRAFBIO_TILEMODE 0x46C1 #define ULTRAFBIO_COLORKEY 0x46C2 #define CURSOR_SIZE 32 #define GAMMA_INDEX_MAX 256 typedef struct _dc_frame_info { unsigned int width; unsigned int height; unsigned int stride; } dc_frame_info; typedef struct _dc_overlay_rect { unsigned int tlx; unsigned int tly; unsigned int brx; unsigned int bry; } dc_overlay_rect; typedef enum _dc_alpha_blending_mode { DC_BLEND_MODE_CLEAR = 0x0, DC_BLEND_MODE_SRC, DC_BLEND_MODE_DST, DC_BLEND_MODE_SRC_OVER, DC_BLEND_MODE_DST_OVER, DC_BLEND_MODE_SRC_IN, DC_BLEND_MODE_DST_IN, DC_BLEND_MODE_SRC_OUT, } dc_alpha_blending_mode; typedef enum _dc_rot_angle { DC_ROT_ANGLE_ROT0 = 0x0, DC_ROT_ANGLE_FLIP_X, DC_ROT_ANGLE_FLIP_Y, DC_ROT_ANGLE_FLIP_XY, DC_ROT_ANGLE_ROT90, DC_ROT_ANGLE_ROT180, DC_ROT_ANGLE_ROT270, } dc_rot_angle; typedef enum _dc_tile_mode { DC_TILE_MODE_LINEAR = 0x0, DC_TILE_MODE_TILED4X4, DC_TILE_MODE_SUPER_TILED_XMAJOR, DC_TILE_MODE_SUPER_TILED_YMAJOR, DC_TILE_MODE_TILE_MODE8X8, DC_TILE_MODE_TILE_MODE8X4, DC_TILE_MODE_SUPER_TILED_XMAJOR8X4, DC_TILE_MODE_SUPER_TILED_YMAJOR4X8, DC_TILE_MODE_TILE_Y, } dc_tile_mode; typedef struct _dc_global_alpha { unsigned int global_alpha_mode; unsigned int global_alpha_value; } dc_global_alpha; typedef struct _dc_filter_tap { unsigned int vertical_filter_tap; unsigned int horizontal_filter_tap; } dc_filter_tap; typedef struct _dc_sync_table { unsigned int horkernel[128]; unsigned int verkernel[128]; } dc_sync_table; typedef struct _dc_gamma_table { bool gamma_enable; unsigned int gamma[GAMMA_INDEX_MAX][3]; } dc_gamma_table; typedef struct _dc_color_key { unsigned char enable; unsigned int colorkey_low; unsigned int colorkey_high; /* background color only available for video, not available for overlay*/ unsigned int bg_color; } dc_color_key; #define DEBUG 0 // #define DEFAULT_DC_ROT_ANGLE DC_ROT_ANGLE_ROT0; #define DEFAULT_DC_ROT_ANGLE DC_ROT_ANGLE_ROT180; // for ICE EVB C910 board HW #define DEFAULT_WIDTH 720 #define DEFAULT_HEIGHT 1280 #define DEFAULT_FB_SIZE (DEFAULT_WIDTH * DEFAULT_HEIGHT * 4 * 3) #define DEFAULT_OVERLAY_SIZE (DEFAULT_WIDTH * DEFAULT_HEIGHT * 4 * 3) #define PIX_FMT_NONE 0 #define PIX_FMT_RGB444 1 #define PIX_FMT_RGB555 3 #define PIX_FMT_RGB565 4 #define PIX_FMT_RGB888 6 #define CURSOR_FMT_DISALBE 0 #define CURSOR_FMT_MASKED 1 #define CURSOR_FMT_A8R8G8B8 2 #define CURSOR_SIZE 32 /*module param*/ static uint irq = 0; module_param(irq, uint, 0644); static ulong reg_base = 0; module_param(reg_base, ulong, 0644); static uint reg_size = 64 << 10; module_param(reg_size, uint, 0644); static void __iomem *reg_virtual; static dma_addr_t framebuffer_phys = 0; static dma_addr_t overlay_phys = 0; static wait_queue_head_t vsync_wait; static struct fb_info *overlay_info = NULL; static u32 dst_global_alpha_mode = 0; static u32 dst_global_alpha_value = 0; static void *cursor_data = NULL; #if DEBUG static char __iomem *dest_virtual; static dma_addr_t dest_phys = 0; #endif /* ToDo */ typedef struct _dc_last_overlay_scale_info { unsigned int lastverfiltertap; unsigned int lasthorfiltertap; unsigned int lastheight; unsigned int lastwidth; unsigned int lastrectx; unsigned int lastrecty; } dc_last_overlay_scale_info; struct ultrafb_info { struct device *dev; struct clk *clk; struct fb_info *info; void __iomem *reg_virtual; dma_addr_t fb_start_dma; dc_tile_mode tile_mode; int format; u32 pseudo_palette[16]; /* cursor info. */ u32 cursor_format; int cursor_x; int cursor_y; int cursor_hot_x; int cursor_hot_y; dma_addr_t cursor_start_dma; void *cursor_virtual; /* frame buffer info. */ dma_addr_t fb_current_dma; dc_frame_info framebuffersize; /* overlay info. */ dma_addr_t overlay_current_dma; dc_overlay_rect overlayrect; dc_frame_info overlaysize; /* alpha blending info. */ dc_global_alpha blending; dc_alpha_blending_mode mode; /* scale info. */ dc_filter_tap filtertap; dc_sync_table synctable; /* gamma info. */ dc_gamma_table gamma_table; /* dither info. */ bool dither_enable; /* rotation info. */ dc_rot_angle rotangle; /* colorkey info. */ dc_color_key colorkey; u32 vblank_count; }; struct ultrafb_mach_info { struct fb_videomode *modes; int num_modes; unsigned int pix_fmt; }; struct fb_videomode video_modes[] = { [0] = { .refresh = 60, .xres = 1920, .yres = 1080, .hsync_len = 32, .left_margin = 120, .right_margin = 128, .vsync_len = 14, .upper_margin = 21, .lower_margin = 10, }, [1] = { .refresh = 60, .xres = 640, .yres = 480, .hsync_len = 96, .left_margin = 48, .right_margin = 16, .vsync_len = 2, .upper_margin = 33, .lower_margin = 10, }, [2] = { .refresh = 30, .xres = 1280, .yres = 720, .hsync_len = 43, .left_margin = 1917, .right_margin = 60, .vsync_len = 3, .upper_margin = 17, .lower_margin = 10, }, [3] = { .refresh = 60, .xres = 720, .yres = 1280, .hsync_len = 10, .left_margin = 45, .right_margin = 40, .vsync_len = 4, .upper_margin = 10, .lower_margin = 11, }, [4] = { .refresh = 50, .xres = 1280, .yres = 720, .hsync_len = 43, .left_margin = 597, .right_margin = 60, .vsync_len = 3, .upper_margin = 17, .lower_margin = 10, }, [5] = { .refresh = 60, .xres = 800, .yres = 480, .hsync_len = 3, .left_margin = 43, .right_margin = 82, .vsync_len = 3, .upper_margin = 20, .lower_margin = 22, }, }; struct ultrafb_mach_info default_mach_info = { // .modes = &video_modes[5], /* for RGB 7" LCD */ .modes = &video_modes[3], /* for MIPI LCD */ .num_modes = ARRAY_SIZE(video_modes), .pix_fmt = PIX_FMT_RGB888, }; struct pix_fmt_info { int pix_fmt; int8_t bits_per_pixel; int8_t transp_offset; int8_t transp_length; int8_t red_offset; int8_t red_length; int8_t green_offset; int8_t green_length; int8_t blue_offset; int8_t blue_length; }; static struct pix_fmt_info pix_fmt_xlate[] = { {PIX_FMT_RGB444, 16, 12, 4, 8, 4, 4, 4, 0, 4}, {PIX_FMT_RGB444, 16, 0, 0, 8, 4, 4, 4, 0, 4}, {PIX_FMT_RGB565, 16, 0, 0, 11, 5, 5, 6, 0, 5}, {PIX_FMT_RGB555, 16, 15, 1, 10, 5, 5, 5, 0, 5}, {PIX_FMT_RGB555, 16, 0, 0, 10, 5, 5, 5, 0, 5}, {PIX_FMT_RGB888, 32, 24, 8, 16, 8, 8, 8, 0, 8}, {PIX_FMT_RGB888, 32, 0, 0, 16, 8, 8, 8, 0, 8}, }; /* Now, dma_alloc_writecombine is not supported in riscv systm yet, * using kmalloc instead. */ static inline void *thead_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_addr, gfp_t gfp) { #ifdef __riscv void *vaddr = kmalloc(size, GFP_KERNEL); if (vaddr == NULL) { printk("thead_dma_alloc(size=%zu) failed\n", size); return NULL; } *dma_addr = virt_to_phys(vaddr); return vaddr; #else return dma_alloc_writecombine(dev, size, dma_addr, gfp); #endif } static inline void teahd_dma_free(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_addr) { #ifdef __riscv kfree(cpu_addr); #else dma_free_writecombine(dev, size, cpu_addr, dma_addr); #endif } void write_register(uint32_t data, uint32_t addr) { writel(data, reg_virtual + addr); #if DEBUG printk("Write [0x%08x] 0x%08x\n", addr, data); #endif } uint32_t read_register(uint32_t addr) { uint32_t data = 0; data = readl(reg_virtual + addr); #if DEBUG if (addr != INT_STATE) { printk("Read [0x%08x] 0x%08x\n", addr, data); } #endif return data; } static int determine_best_pix_fmt(struct fb_var_screeninfo *var) { size_t i; for (i = 0; i < ARRAY_SIZE(pix_fmt_xlate); i++) { struct pix_fmt_info *info = &pix_fmt_xlate[i]; if ((var->bits_per_pixel == info->bits_per_pixel) && (var->transp.offset == info->transp_offset) && (var->transp.length == info->transp_length) && (var->red.offset == info->red_offset) && (var->red.length == info->red_length) && (var->green.offset == info->green_offset) && (var->green.length == info->green_length) && (var->blue.offset == info->blue_offset) && (var->blue.length == info->blue_length)) { /* found format. */ return info->pix_fmt; } } return 0; } static void set_pix_fmt(struct fb_var_screeninfo *var, int pix_fmt) { size_t i; struct pix_fmt_info *info = NULL; for (i = 0; i < ARRAY_SIZE(pix_fmt_xlate); i++) { if (pix_fmt_xlate[i].pix_fmt == pix_fmt) { info = &pix_fmt_xlate[i]; break; } } var->bits_per_pixel = info->bits_per_pixel; var->transp.offset = info->transp_offset; var->transp.length = info->transp_length; var->red.offset = info->red_offset; var->red.length = info->red_length; var->green.offset = info->green_offset; var->green.length = info->green_length; var->blue.offset = info->blue_offset; var->blue.length = info->blue_length; } static int get_bpp_by_format(int pix_fmt) { size_t i; struct pix_fmt_info *info = NULL; int bpp = 0; for (i = 0; i < ARRAY_SIZE(pix_fmt_xlate); i++) { if (pix_fmt_xlate[i].pix_fmt == pix_fmt) { info = &pix_fmt_xlate[i]; break; } } bpp = info ->bits_per_pixel / 8; return bpp; } static void set_mode(struct fb_var_screeninfo *var, struct fb_videomode *mode, int pix_fmt) { set_pix_fmt(var, pix_fmt); fb_videomode_to_var(var, mode); var->grayscale = 0; } static void set_graphics_start(struct fb_info *info, int xoffset, int yoffset) { struct ultrafb_info *fbi = info->par; struct fb_var_screeninfo *var = &info->var; int pixel_offset; pixel_offset = (yoffset * var->xres_virtual) + xoffset; fbi->fb_current_dma = info->fix.smem_start + (pixel_offset * (var->bits_per_pixel >> 3)); } static void set_overlay_graphics_start(struct fb_info *info, int xoffset, int yoffset) { struct ultrafb_info *fbi = info->par; struct fb_var_screeninfo *var = &info->var; int pixel_offset; pixel_offset = (yoffset * var->xres_virtual) + xoffset; fbi->overlay_current_dma = info->fix.smem_start + (pixel_offset * (var->bits_per_pixel >> 3)); } static int ultrafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct ultrafb_info *fbi = info->par; struct device *dev = fbi->dev; int pix_fmt; pix_fmt = determine_best_pix_fmt(var); if (!pix_fmt) { dev_err(dev, "unsupport pixel format\n"); return -EINVAL; } fbi->format = pix_fmt; /* * Basic geometry sanity checks. */ if (var->xres_virtual > var->xres) { dev_err(dev, "unsupport stride\n"); return -EINVAL; } if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; /* * Check size of framebuffer. */ if (var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3) > info->fix.smem_len) { dev_err(dev, "invalid framebuffer size requirement\n"); return -EINVAL; } set_graphics_start(info, var->xoffset, var->yoffset); return 0; } static int ultrafb_set_par(struct fb_info *info) { /* config dc register */ uint32_t h_end, h_total, hsync_start, hsync_end; uint32_t v_end, v_total, vsync_start, vsync_end; struct fb_var_screeninfo *var = &info->var; struct ultrafb_info *fbi = info->par; uint32_t data; int j = 0; info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; info->fix.ypanstep = var->yres; /* Get the timing from var info */ h_end = var->xres; h_total = var->xres + var->left_margin + var->right_margin + var->hsync_len; hsync_start = var->xres + var->right_margin; hsync_end = hsync_start + var->hsync_len; v_end = var->yres; v_total = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; vsync_start = var->yres + var->lower_margin; vsync_end = vsync_start + var->vsync_len; data = (h_total << 16) | h_end; write_register(data, HDISPLAY); data = (v_total << 16) | v_end; write_register(data, VDISPLAY); data = (0 << 31) | (1 << 30) | (hsync_end << 15) | (hsync_start); write_register(data, HSYNC); data = (0 << 31) | (1 << 30) | (vsync_end << 15) | (vsync_start); write_register(data, VSYNC); write_register(0x00000080, DBI_CONFIG); write_register(0x00000005, DPI_CONFIG); data = (1 << 0) | (0 << 1) | (1 << 4) | (0 << 5) | (1 << 8) | (0 << 9); write_register(data, PANEL_CONFIG); data = 0x0; write_register(data, FRAMEBUFFER_UPLANAR_ADDRESS); data = 0x0; write_register(data, FRAMEBUFFER_VPLANAR_ADDRESS); data = 0x0; write_register(data, FRAMEBUFFER_USTRIDE); data = 0x0; write_register(data, FRAMEBUFFER_VSTRIDE); write_register(0, INDEXCOLOR_TABLEINDEX); data = (fbi->framebuffersize.height << 15) | (fbi->framebuffersize.width); write_register(data, FRAMEBUFFER_SIZE); if ((var->xres != fbi->framebuffersize.width) || (var->yres != fbi->framebuffersize.height)) { data = ((fbi->framebuffersize.width - 1) << 16) / (var->xres - 1); write_register(data , FRAMEBUFFER_SCALEFACTORX); data = ((fbi->framebuffersize.height - 1) << 16) / (var->yres - 1); if (data > (3 << 16)) { data = (3 << 16); } write_register(data , FRAMEBUFFER_SCALEFACTORY); write_register(0, HORIFILTER_KERNELINDEX); for (j = 0; j < 128; j++) { data = fbi->synctable.horkernel[j]; write_register(data , HORIFILTER_KERNEL); } write_register(0, VERTIFILTER_KERNELINDEX); for (j = 0; j < 128; j++) { data = fbi->synctable.verkernel[j]; write_register(data , VERTIFILTER_KERNEL); } data = (0x8000 << 0) | (0x8000 << 16); write_register(data , FRAMEBUFFER_INITIALOFFSET); data = (fbi->filtertap.vertical_filter_tap << 0) | (fbi->filtertap.horizontal_filter_tap << 4); write_register(data , FRAMEBUFFER_SCALEFCONFIG); } data = fbi->colorkey.colorkey_low; write_register(data, FRAMEBUFFER_COLORKEY); data = fbi->colorkey.colorkey_high; write_register(data, FRAMEBUFFER_COLORHIGHKEY); data = fbi->colorkey.bg_color; write_register(data, FRAMEBUFFER_BGCOLOR); data = 0x0; write_register(data, FRAMEBUFFER_CLEARVALUE); data = 1 << 0; write_register(data, DISPLAY_INTRENABLE); #if DEBUG /* Set dest related function */ write_register(dest_phys, PANEL_DEST_ADDRESS); write_register(dest_phys, MEM_DEST_ADDRESS); data = (1 << 16) | (1 << 17); write_register(data, DEST_CONFIG); write_register(fbi->framebuffersize.stride, DEST_STRIDE); #endif write_register(fbi->framebuffersize.stride, FRAMEBUFFER_STRIDE); return 0; } static int ultrafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { uint32_t data; struct ultrafb_info *fbi = info->par; int pixel_byte = 0; pixel_byte = get_bpp_by_format(fbi->format); set_graphics_start(info, var->xoffset, var->yoffset); write_register(fbi->fb_current_dma, FRAMEBUFFER_ADDRESS); if (((fbi->rotangle == DC_ROT_ANGLE_ROT90) || (fbi->rotangle == DC_ROT_ANGLE_ROT270)) && (fbi->tile_mode == DC_TILE_MODE_LINEAR)) { return -EINVAL; } else { if ((var->xres != fbi->framebuffersize.width) || (var->yres != fbi->framebuffersize.height)) { data = (1 << 0) | (fbi->gamma_table.gamma_enable << 2) |(1 << 4) | (0 << 8) | (fbi->colorkey.enable << 9) | (fbi->rotangle << 11) | (fbi->tile_mode << 17) | (1 << 22) | (fbi->format << 26); } else { data = (1 << 0) | (fbi->gamma_table.gamma_enable << 2) |(1 << 4) | (0 << 8) | (fbi->colorkey.enable << 9) | (fbi->rotangle << 11) | (fbi->tile_mode << 17) | (0 << 22) | (fbi->format << 26); } write_register(data, FRAMEBUFFER_CONFIG); } var->yoffset = var->yoffset + var->yres; if (var->yoffset > info->var.yres_virtual - var->yres) { var->yoffset = 0; } fbi->gamma_table.gamma_enable = 0; fbi->colorkey.enable = 0; fbi->rotangle = DEFAULT_DC_ROT_ANGLE; fbi->framebuffersize.width = DEFAULT_WIDTH; fbi->framebuffersize.height = DEFAULT_HEIGHT; fbi->framebuffersize.stride = DEFAULT_WIDTH * pixel_byte; return 0; } static int ultrafb_cursor(struct fb_info *info, struct fb_cursor *cursor) { uint32_t x, y, format; uint32_t data; int set = cursor->set; struct ultrafb_info *fbi = info->par; if (cursor->image.width > CURSOR_SIZE || cursor->image.height > CURSOR_SIZE) return -EINVAL; write_register(fbi->cursor_start_dma, CURSOR_ADDRESS); if (set & FB_CUR_SETPOS) { y = cursor->image.dy; x = cursor->image.dx; data = (y << 16) | x; write_register(data, CURSOR_LOCATION); } /* * FB_CUR_SETIMAGE - the cursor image has changed * FB_CUR_SETCMAP - the cursor colors has changed * FB_CUR_SETSHAPE - the cursor bitmask has changed */ if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) { uint32_t image_size; uint32_t bg, fg; if (info->state != FBINFO_STATE_RUNNING) return 0; bg = cursor->image.bg_color; fg = cursor->image.fg_color; write_register(bg, CURSOR_BACKGROUND); write_register(fg, CURSOR_FOREGROUND); /* Use 32-bit operations on the data to improve performance */ image_size = cursor->image.width * cursor->image.height; memcpy(fbi->cursor_virtual, (uint32_t *)cursor->image.data, image_size * 4); } if (cursor->enable) { /* Default ARGB8888 format */ format = CURSOR_FMT_A8R8G8B8; data = format | (1 << 2) | (0 << 4) | (0 << 8) | (0 << 16); write_register(data, CURSOR_CONFIG); } else { data = (0 << 0) | (1 << 2) | (0 << 4) | (0 << 8) | (0 << 16); write_register(data, CURSOR_CONFIG); } return 0; } static int ultrafb_gamma(struct ultrafb_info *fbi) { int i = 0; u32 data; for (i = 0; i < GAMMA_INDEX_MAX; i++) { data = i; write_register(data, GAMMA_INDEX); data = (fbi->gamma_table.gamma[i][2] << 0) | (fbi->gamma_table.gamma[i][1] << 10) | (fbi->gamma_table.gamma[i][0] << 20); write_register(data, GAMMA_DATA); } return 0; } static int ultrafb_dither(struct ultrafb_info *fbi) { u32 data; if (fbi->dither_enable) { write_register(0x7B48F3C0, DISPLAY_DITHER_TABLE_LOW); write_register(0x596AD1E2, DISPLAY_DITHER_TABLE_HIGH); data = fbi->dither_enable << 31; write_register(data, DISPLAY_DITHER_CONFIG); } else { write_register(0, DISPLAY_DITHER_TABLE_LOW); write_register(0, DISPLAY_DITHER_TABLE_HIGH); write_register(0, DISPLAY_DITHER_CONFIG); } return 0; } static int ultrafb_blank(int blank, struct fb_info *info) { u32 data, _new; data = read_register(FRAMEBUFFER_CONFIG); if (blank) _new = data & ~(1 << 0); else _new = data | (1 << 0); if (_new == data) return 0; write_register(_new, FRAMEBUFFER_CONFIG); return 0; } static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) { return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset; } static int ultrafb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int trans, struct fb_info *info) { struct ultrafb_info *fbi = info->par; u32 val; if (info->var.grayscale) red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16; if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) { val = chan_to_field(red, &info->var.red); val |= chan_to_field(green, &info->var.green); val |= chan_to_field(blue , &info->var.blue); fbi->pseudo_palette[regno] = val; } return 0; } static int ultrafb_mmap(struct fb_info *info, struct vm_area_struct * vma) { unsigned long mmio_pgoff; unsigned long start; u32 len; start = info->fix.smem_start; len = info->fix.smem_len; mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT; if (vma->vm_pgoff >= mmio_pgoff) { if (info->var.accel_flags) { mutex_unlock(&info->mm_lock); return -EINVAL; } vma->vm_pgoff -= mmio_pgoff; start = info->fix.mmio_start; len = info->fix.mmio_len; } vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); return vm_iomap_memory(vma, start, len); } static int ultrafb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long argp) { int ret, j; u32 vblank_count; dc_frame_info size; dc_filter_tap filtertap; dc_sync_table table; struct fb_cursor cursor; static dc_gamma_table gammatable; bool ditherenable; dc_rot_angle rotangle; dc_tile_mode tilemode; dc_color_key colorkey; struct ultrafb_info *fbi = info->par; switch (cmd) { case ULTRAFBIO_CURSOR: if (copy_from_user(&cursor, (void *)argp, sizeof(cursor))) return -EFAULT; cursor_data = kmalloc(CURSOR_SIZE * CURSOR_SIZE * 4, GFP_KERNEL); if (copy_from_user(cursor_data, (void *)cursor.image.data, CURSOR_SIZE * CURSOR_SIZE * 4)) return -EFAULT; cursor.image.data = (char *)cursor_data; ret = ultrafb_cursor(info, &cursor); /* free buffer */ kfree(cursor_data); return ret; case FBIO_WAITFORVSYNC: vblank_count = fbi->vblank_count; ret = wait_event_interruptible_timeout(vsync_wait, vblank_count != fbi->vblank_count, HZ /10); if(ret == 0) return -ETIMEDOUT; return 0; case ULTRAFBIO_BUFFER_SIZE: if (copy_from_user(&size, (void *)argp, sizeof(size))) return -EFAULT; fbi->framebuffersize.width = size.width; fbi->framebuffersize.height = size.height; fbi->framebuffersize.stride = size.stride; return 0; case ULTRAFBIO_SCALE_FILTER_TAP: if (copy_from_user(&filtertap, (void *)argp, sizeof(filtertap))) return -EFAULT; fbi->filtertap.horizontal_filter_tap = filtertap.horizontal_filter_tap; fbi->filtertap.vertical_filter_tap = filtertap.vertical_filter_tap; return 0; case ULTRAFBIO_SYNC_TABLE: if (copy_from_user(&table, (void *)argp, sizeof(table))) return -EFAULT; for (j = 0; j < 128; j++) { fbi->synctable.horkernel[j] = table.horkernel[j]; fbi->synctable.verkernel[j] = table.verkernel[j]; } return 0; case ULTRAFBIO_GAMMA: if (copy_from_user(&gammatable, (void *)argp, sizeof(gammatable))) return -EFAULT; fbi->gamma_table.gamma_enable = gammatable.gamma_enable; for (j = 0; j < GAMMA_INDEX_MAX; j++) { fbi->gamma_table.gamma[j][0] = gammatable.gamma[j][0]; fbi->gamma_table.gamma[j][1] = gammatable.gamma[j][1]; fbi->gamma_table.gamma[j][2] = gammatable.gamma[j][2]; } ret = ultrafb_gamma(fbi); return ret; case ULTRAFBIO_DITHER: if (copy_from_user(&ditherenable, (void *)argp, sizeof(ditherenable))) return -EFAULT; fbi->dither_enable = ditherenable; ret = ultrafb_dither(fbi); return ret; case ULTRAFBIO_ROTATION: if (copy_from_user(&rotangle, (void *)argp, sizeof(rotangle))) return -EFAULT; fbi->rotangle = rotangle; return 0; case ULTRAFBIO_TILEMODE: if (copy_from_user(&tilemode, (void *)argp, sizeof(tilemode))) return -EFAULT; fbi->tile_mode = tilemode; return 0; case ULTRAFBIO_COLORKEY: if (copy_from_user(&colorkey, (void *)argp, sizeof(colorkey))) return -EFAULT; if (colorkey.enable) fbi->colorkey.enable = 0x2; else fbi->colorkey.enable = 0x0; fbi->colorkey.colorkey_low = colorkey.colorkey_low; fbi->colorkey.colorkey_high = colorkey.colorkey_high; fbi->colorkey.bg_color = colorkey.bg_color; return 0; default: ret = -EINVAL; break; } return ret; } static int ultrafb_release(struct fb_info *info, int user) { uint32_t data; data = 0x0; write_register(data, FRAMEBUFFER_CONFIG); return 0; } static struct fb_ops ultrafb_ops = { .owner = THIS_MODULE, .fb_check_var = ultrafb_check_var, .fb_set_par = ultrafb_set_par, .fb_pan_display = ultrafb_pan_display, .fb_blank = ultrafb_blank, .fb_setcolreg = ultrafb_setcolreg, .fb_mmap = ultrafb_mmap, .fb_ioctl = ultrafb_ioctl, .fb_release = ultrafb_release, }; static irqreturn_t ultrafb_handle_irq(int irq, void *dev_id) { struct ultrafb_info *fbi = dev_id; uint32_t data; data = read_register(INT_STATE); fbi->vblank_count ++; if (!(data & 0x1)) return IRQ_NONE; /* TODO: set fb cursor here. */ return IRQ_HANDLED; } static void set_overlay_var(struct fb_var_screeninfo *var, struct fb_videomode *mode, int pix_fmt) { set_pix_fmt(var, pix_fmt); var->xres = mode ->xres; var->yres = mode ->yres; var->xres_virtual = mode ->xres; var->yres_virtual = mode ->yres; var->xoffset = 0; var->yoffset = 0; var->height = -1; var->width = -1; var->vmode = FB_VMODE_NONINTERLACED; var->activate = FB_ACTIVATE_FORCE; } static int overlay_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct ultrafb_info *fbi = info->par; struct device *dev = fbi->dev; int pix_fmt; pix_fmt = determine_best_pix_fmt(var); if (!pix_fmt) { dev_err(dev, "unsupport pixel format\n"); return -EINVAL; } fbi->format = pix_fmt; /* * Basic geometry sanity checks. */ if (var->xres_virtual > var->xres) { dev_err(dev, "unsupport stride\n"); return -EINVAL; } if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; /* * Check size of framebuffer. */ if (var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3) > info->fix.smem_len) { dev_err(dev, "invalid framebuffer size requirement\n"); return -EINVAL; } set_overlay_graphics_start(info, var->xoffset, var->yoffset); return 0; } static int overlay_set_par(struct fb_info *info) { /* config overlay register */ struct ultrafb_info *fbi = info->par; uint32_t data; uint32_t rectx, recty; int j = 0; rectx = fbi->overlayrect.brx - fbi->overlayrect.tlx; recty = fbi->overlayrect.bry - fbi->overlayrect.tly; data = fbi->overlaysize.stride; write_register(data, OVERLAY_STRIDE); data = 0; write_register(data, OVERLAY_USTRIDE); data = 0; write_register(data, OVERLAY_VSTRIDE); data = (fbi->overlayrect.tlx << 0) | (fbi->overlayrect.tly << 15); write_register(data, OVERLAY_TL); data = (fbi->overlayrect.brx << 0) | (fbi->overlayrect.bry << 15); write_register(data, OVERLAY_BR); switch(fbi->mode) { case DC_BLEND_MODE_CLEAR: data = (0 << 0) | (fbi->blending.global_alpha_mode << 3) | (0 << 5) | (0 << 8) | (0 << 9) | (dst_global_alpha_mode << 10) | (0 << 12) | (0 << 15); break; case DC_BLEND_MODE_SRC: data = (0 << 0) | (fbi->blending.global_alpha_mode << 3) | (1 << 5) | (0 << 8) | (0 << 9) | (dst_global_alpha_mode << 10) | (0 << 12) | (0 << 15); break; case DC_BLEND_MODE_DST: data = (0 << 0) | (fbi->blending.global_alpha_mode << 3) | (0 << 5) | (0 << 8) | (0 << 9) | (dst_global_alpha_mode << 10) | (1 << 12) | (0 << 15); break; case DC_BLEND_MODE_SRC_OVER: data = (0 << 0) | (fbi->blending.global_alpha_mode << 3) | (1 << 5) | (0 << 8) | (0 << 9) | (dst_global_alpha_mode << 10) | (3 << 12) | (0 << 15); break; case DC_BLEND_MODE_DST_OVER: data = (0 << 0) | (fbi->blending.global_alpha_mode << 3) | (3 << 5) | (0 << 8) | (0 << 9) | (dst_global_alpha_mode << 10) | (1 << 12) | (0 << 15); break; case DC_BLEND_MODE_SRC_IN: data = (0 << 0) | (fbi->blending.global_alpha_mode << 3) | (2 << 5) | (0 << 8) | (0 << 9) | (dst_global_alpha_mode << 10) | (0 << 12) | (0 << 15); break; case DC_BLEND_MODE_DST_IN: data = (0 << 0) | (fbi->blending.global_alpha_mode << 3) | (0 << 5) | (0 << 8) | (0 << 9) | (dst_global_alpha_mode << 10) | (2 << 12) | (0 << 15); break; case DC_BLEND_MODE_SRC_OUT: data = (0 << 0) | (fbi->blending.global_alpha_mode << 3) | (3 << 5) | (0 << 8) | (0 << 9) | (dst_global_alpha_mode << 10) | (0 << 12) | (0 << 15); break; } write_register(data, OVERLAY_ALPHA_BLEND_CONFIG); data = fbi->blending.global_alpha_value; write_register(data , OVERLAY_SRC_GLOBAL_COLOR); data = dst_global_alpha_value; write_register(data , OVERLAY_DST_GLOBAL_COLOR); write_register(0, OVERLAY_CLEAR_VALUE); data = (fbi->overlaysize.height << 15) | (fbi->overlaysize.width); write_register(data, OVERLAY_SIZE); data = fbi->colorkey.colorkey_low; write_register(data, OVERLAY_COLOR_KEY); data = fbi->colorkey.colorkey_high; write_register(data, OVERLAY_COLOR_KEY_HIGH); write_register(0, OVERLAY_UPLANAR_ADDRESS); write_register(0, OVERLAY_VPLANAR_ADDRESS); if ((rectx != fbi->overlaysize.width) || (recty != fbi->overlaysize.height)) { data = ((fbi->overlaysize.width - 1) << 16) / (rectx - 1); write_register(data , OVERLAY_SCALE_FACTOR_X); data = ((fbi->overlaysize.height - 1) << 16) / (recty - 1); if (data > (3 << 16)) { data = (3 << 16); } write_register(data , OVERLAY_SCALE_FACTOR_Y); write_register(0, OVERLAY_HORI_FILTER_KERNEL_INDEX); for (j = 0; j < 128; j++) { data = fbi->synctable.horkernel[j]; write_register(data , OVERLAY_HORI_FILTER_KERNEL); } write_register(0, OVERLAY_VERTI_FILTER_KERNEL_INDEX); for (j = 0; j < 128; j++) { data = fbi->synctable.verkernel[j]; write_register(data , OVERLAY_VERTI_FILTER_KERNEL); } data = (0x8000 << 0) | (0x8000 << 16); write_register(data , OVERLAY_INITIAL_OFFSET); data = (fbi->filtertap.vertical_filter_tap << 0) | (fbi->filtertap.horizontal_filter_tap << 4) | (1 << 8); write_register(data , OVERLAY_SCALE_CONFIG); } else { write_register(0, OVERLAY_SCALE_CONFIG); } return 0; } static int overlay_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { uint32_t data; struct ultrafb_info *fbi = info->par; set_overlay_graphics_start(info, var->xoffset, var->yoffset); write_register(fbi->overlay_current_dma, OVERLAY_ADDRESS); if (((fbi->rotangle == DC_ROT_ANGLE_ROT90) || (fbi->rotangle == DC_ROT_ANGLE_ROT270)) && (fbi->tile_mode != DC_TILE_MODE_LINEAR)) { return -EINVAL; } else { data = (fbi->colorkey.enable << 0) | (6 << 16) | (1 << 24) | (fbi->rotangle << 2) | (0 << 13) | (0 << 0) | (0 << 25) | (fbi->tile_mode << 8); write_register(data, OVERLAY_CONFIG); } var->yoffset = var->yoffset + var->yres; if (var->yoffset > info->var.yres_virtual - var->yres) { var->yoffset = 0; } fbi->colorkey.enable = 0; fbi->rotangle = DEFAULT_DC_ROT_ANGLE; fbi->mode = DC_BLEND_MODE_SRC; fbi->overlayrect.tlx = 0; fbi->overlayrect.tly = 0; fbi->overlayrect.brx = 320; fbi->overlayrect.bry = 240; fbi->overlaysize.width = 320; fbi->overlaysize.height = 240; return 0; } static int overlay_ioctl(struct fb_info *info, unsigned int cmd, unsigned long argp) { int ret, j; dc_global_alpha alpha; dc_frame_info size; dc_overlay_rect coordinate; struct ultrafb_info *fbi = info->par; dc_alpha_blending_mode mode; dc_filter_tap filtertap; dc_sync_table table; dc_rot_angle rotangle; dc_tile_mode tilemode; dc_color_key colorkey; switch (cmd) { case ULTRAFBIO_BUFFER_SIZE: if (copy_from_user(&size, (void *)argp, sizeof(size))) return -EFAULT; fbi->overlaysize.width = size.width; fbi->overlaysize.height = size.height; fbi->overlaysize.stride = size.stride; return 0; case ULTRAFBIO_OVERLAY_RECT: if (copy_from_user(&coordinate, (void *)argp, sizeof(coordinate))) return -EFAULT; fbi->overlayrect.tlx = coordinate.tlx; fbi->overlayrect.tly = coordinate.tly; fbi->overlayrect.brx = coordinate.brx; fbi->overlayrect.bry = coordinate.bry; return 0; case ULTRAFBIO_BLENDING_MODE: if (copy_from_user(&mode, (void *)argp, sizeof(mode))) return -EFAULT; fbi->mode = mode; return 0; case ULTRAFBIO_GLOBAL_MODE_VALUE: if (copy_from_user(&alpha, (void *)argp, sizeof(alpha))) return -EFAULT; fbi->blending.global_alpha_mode = alpha.global_alpha_mode; fbi->blending.global_alpha_value = alpha.global_alpha_value; return 0; case ULTRAFBIO_SCALE_FILTER_TAP: if (copy_from_user(&filtertap, (void *)argp, sizeof(filtertap))) return -EFAULT; fbi->filtertap.horizontal_filter_tap = filtertap.horizontal_filter_tap; fbi->filtertap.vertical_filter_tap = filtertap.vertical_filter_tap; return 0; case ULTRAFBIO_SYNC_TABLE: if (copy_from_user(&table, (void *)argp, sizeof(table))) return -EFAULT; for (j = 0; j < 128; j++) { fbi->synctable.horkernel[j] = table.horkernel[j]; fbi->synctable.verkernel[j] = table.verkernel[j]; } return 0; case ULTRAFBIO_ROTATION: if (copy_from_user(&rotangle, (void *)argp, sizeof(rotangle))) return -EFAULT; fbi->rotangle = rotangle; return 0; case ULTRAFBIO_TILEMODE: if (copy_from_user(&tilemode, (void *)argp, sizeof(tilemode))) return -EFAULT; fbi->tile_mode = tilemode; return 0; case ULTRAFBIO_COLORKEY: if (copy_from_user(&colorkey, (void *)argp, sizeof(colorkey))) return -EFAULT; if (colorkey.enable) fbi->colorkey.enable = 0x2; else fbi->colorkey.enable = 0x0; fbi->colorkey.colorkey_low = colorkey.colorkey_low; fbi->colorkey.colorkey_high = colorkey.colorkey_high; return 0; default: ret = -EINVAL; break; } return ret; } static int overlay_release(struct fb_info *info, int user) { uint32_t data; data = 0x0; write_register(data, OVERLAY_CONFIG); return 0; } static struct fb_ops overlay_ops = { .owner = THIS_MODULE, .fb_check_var = overlay_check_var, .fb_set_par = overlay_set_par, .fb_pan_display = overlay_pan_display, .fb_ioctl = overlay_ioctl, .fb_release = overlay_release, }; static int dcultra_overlay_init(struct platform_device *pdev) { struct fb_info *info; struct ultrafb_info *fbi; struct ultrafb_mach_info *mi; int ret = 0; mi = &default_mach_info; info = framebuffer_alloc(sizeof(struct ultrafb_info), &pdev->dev); if (info == NULL) { dev_err(&pdev->dev, "alloc overlay fb_info failed\n"); return -ENOMEM; } fbi = info->par; fbi->info = info; fbi->dev = info->dev = &pdev->dev; fbi->format = mi->pix_fmt; fbi->mode = DC_BLEND_MODE_SRC; fbi->blending.global_alpha_mode = 0; fbi->blending.global_alpha_value = 0; fbi->filtertap.horizontal_filter_tap = 5; fbi->filtertap.vertical_filter_tap = 3; fbi->overlayrect.brx = 320; fbi->overlayrect.bry = 240; fbi->overlayrect.tlx = 0; fbi->overlayrect.tly = 0; fbi->overlaysize.height = 160; fbi->overlaysize.width = 120; fbi->rotangle = 0; fbi->tile_mode = 0; fbi->colorkey.enable = 0; /* set other parameter*/ info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; info->node = -1; info->pseudo_palette = NULL; info->fbops = &overlay_ops; /* set var parameter*/ set_overlay_var(&info->var, mi->modes, mi->pix_fmt); /* set multibuffer. */ info->var.xres_virtual = info->var.xres; info->var.yres_virtual = info->fix.smem_len / (info->var.xres_virtual * (info->var.bits_per_pixel >> 3)) / info->var.yres * info->var.yres; if (info->var.yres_virtual > 3 * info->var.yres) info->var.yres_virtual = 3 * info->var.yres; /* set fix parameter*/ strlcpy(info->fix.id, "dcultra overlay", 16); info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.type_aux = 0; info->fix.xpanstep = 0; info->fix.ypanstep = info->var.yres; info->fix.ywrapstep = 0; info->fix.smem_len = PAGE_ALIGN(DEFAULT_OVERLAY_SIZE); info->screen_base = thead_dma_alloc(NULL, info->fix.smem_len, &overlay_phys, GFP_KERNEL); info->fix.smem_start = (unsigned long)overlay_phys; overlay_info = info; overlay_check_var(&info->var, info); overlay_set_par(info); ret = register_framebuffer(info); if (ret < 0) { dev_err(&pdev->dev, "Failed to register overlay fb_info\n"); ret = -ENOMEM; goto on_error; } return 0; on_error: teahd_dma_free(NULL, info->fix.smem_len, info->screen_base, overlay_phys); framebuffer_release(overlay_info); return ret; } static int ultrafb_probe(struct platform_device *pdev) { struct fb_info *info; struct ultrafb_info *fbi; struct ultrafb_mach_info *mi; int ret = 0; int pixel_byte = 0; _Static_assert(PAGE_ALIGN(DEFAULT_FB_SIZE) + 32 * 32 * 4 < 0x2000000, "error"); printk("%s probe.\n", __func__); mi = &default_mach_info; if (!reg_base) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "no IO memory defined\n"); return -ENOENT; } reg_base = res->start; reg_size = resource_size(res); } if (irq < 0) { dev_err(&pdev->dev, "no IRQ defined\n"); return -ENOENT; } info = framebuffer_alloc(sizeof(struct ultrafb_info), &pdev->dev); if (info == NULL) { dev_err(&pdev->dev, "alloc framebuffer failed\n"); return -ENOMEM; } fbi = info->par; fbi->info = info; fbi->dev = info->dev = &pdev->dev; fbi->format = mi->pix_fmt; fbi->vblank_count = 0; fbi->rotangle = 0; fbi->tile_mode = 0; fbi->colorkey.enable = 0; pixel_byte = get_bpp_by_format(fbi->format); fbi->framebuffersize.width = DEFAULT_WIDTH; fbi->framebuffersize.height = DEFAULT_HEIGHT; fbi->framebuffersize.stride = DEFAULT_WIDTH * pixel_byte; /* * Initialise static fb parameters. */ info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; info->node = -1; strlcpy(info->fix.id, "Vivante dcultra", 16); info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.type_aux = 0; info->fix.xpanstep = 0; info->fix.ypanstep = 0; info->fix.ywrapstep = 0; info->fix.mmio_start = reg_base; info->fix.mmio_len = reg_size; info->fix.accel = FB_ACCEL_NONE; info->fbops = &ultrafb_ops; info->pseudo_palette = fbi->pseudo_palette; info->fix.visual = FB_VISUAL_TRUECOLOR; info->fix.smem_len = PAGE_ALIGN(DEFAULT_FB_SIZE); ret = request_irq(irq, ultrafb_handle_irq, IRQF_SHARED, "dc_ultra", fbi); init_waitqueue_head(&vsync_wait); request_mem_region(reg_base, reg_size, "dc ultra region"); info->screen_base = thead_dma_alloc(NULL, info->fix.smem_len, &framebuffer_phys, GFP_KERNEL); info->fix.smem_start = (unsigned long)framebuffer_phys; fbi->reg_virtual = (void __iomem *)ioremap(reg_base, reg_size); reg_virtual = fbi->reg_virtual; #if DEBUG dest_virtual = thead_dma_alloc(NULL, info->fix.smem_len, &dest_phys, GFP_KERNEL); #endif fbi->cursor_start_dma = PAGE_ALIGN(info->fix.smem_start + info->fix.smem_len); fbi->cursor_virtual = thead_dma_alloc(NULL, CURSOR_SIZE * CURSOR_SIZE * 4, &fbi->cursor_start_dma, GFP_KERNEL); set_mode(&info->var, mi->modes, mi->pix_fmt); /* set multibuffer. */ info->var.xres_virtual = info->var.xres; info->var.yres_virtual = info->fix.smem_len / (info->var.xres_virtual * (info->var.bits_per_pixel >> 3)) / info->var.yres * info->var.yres; if (info->var.yres_virtual > 3 * info->var.yres) info->var.yres_virtual = 3 * info->var.yres; info->var.activate = FB_ACTIVATE_FORCE; ultrafb_check_var(&info->var, info); write_register(0x0, FRAMEBUFFER_CONFIG); write_register(0x00071900, AQHICLOCKCONTROL); write_register(0x00070900, AQHICLOCKCONTROL); ultrafb_set_par(info); ret = fb_alloc_cmap(&info->cmap, 16, 0); if (ret) { dev_err(&pdev->dev, "Fail to alloc cmap: %d\n", ret); goto on_error; } ret = register_framebuffer(info); if (ret < 0) { dev_err(&pdev->dev, "Failed to register dc-fb: %d\n", ret); ret = -ENXIO; goto on_error; } platform_set_drvdata(pdev, fbi); ret = dcultra_overlay_init(pdev); if (ret < 0) { dev_err(&pdev->dev, "Failed to init overlay\n"); return ret; } return 0; on_error: if (&info->cmap) fb_dealloc_cmap(&info->cmap); teahd_dma_free(NULL, CURSOR_SIZE * CURSOR_SIZE * 4, fbi->cursor_virtual, fbi->cursor_start_dma); #if DEBUG teahd_dma_free(NULL, info->fix.smem_len, dest_virtual, dest_phys); #endif iounmap(fbi->reg_virtual); teahd_dma_free(NULL, info->fix.smem_len, info->screen_base, framebuffer_phys); framebuffer_release(info); dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret); return ret; } static int ultrafb_remove(struct platform_device *pdev) { struct ultrafb_info *fbi = platform_get_drvdata(pdev); struct fb_info *info = fbi->info; uint32_t data; /* Disable DE */ data = read_register(PANEL_CONFIG); data = data & ~0x1; write_register(data, PANEL_CONFIG); /* Reset framebuffer */ write_register(0, FRAMEBUFFER_CONFIG); unregister_framebuffer(overlay_info); unregister_framebuffer(info); if (&info->cmap) fb_dealloc_cmap(&info->cmap); teahd_dma_free(NULL, info->fix.smem_len, info->screen_base, overlay_phys); teahd_dma_free(NULL, CURSOR_SIZE * CURSOR_SIZE * 4, fbi->cursor_virtual, fbi->cursor_start_dma); #if DEBUG teahd_dma_free(NULL, info->fix.smem_len, dest_virtual, dest_phys); #endif iounmap(fbi->reg_virtual); teahd_dma_free(NULL, info->fix.smem_len, info->screen_base, framebuffer_phys); release_mem_region(info->fix.mmio_start, info->fix.mmio_len); framebuffer_release(overlay_info); framebuffer_release(info); /* release irq */ free_irq(irq, fbi); return 0; } static const struct of_device_id ultrafb_of_dev_id[] = { { .compatible = "verisilicon,dc8000-fb", }, { } }; static struct platform_driver ultrafb_driver = { .driver = { .name = "viv-dc", .of_match_table = ultrafb_of_dev_id, }, .probe = ultrafb_probe, .remove = ultrafb_remove, }; module_platform_driver(ultrafb_driver); MODULE_DESCRIPTION("Vivante dcultra Driver"); MODULE_LICENSE("GPL");