// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include "disp_assert_layer.h" #include #include /* #include */ #include #include #include #include #include #include #include /* #include */ #include #include #include #if defined(CONFIG_MTK_ION) #include "ion_drv.h" #endif #include "ion.h" /* #include "mt-plat/dma.h" */ /* #include */ #include #include #ifdef CONFIG_MTK_AEE_FEATURE # include "mt-plat/aee.h" #endif /* #include "mt-plat/mtk_boot.h" */ #include "debug.h" #include "ddp_hal.h" #include "disp_drv_log.h" #include "disp_lcm.h" #include "mtkfb.h" #include "mtkfb_console.h" #include "mtkfb_fence.h" #include "mtkfb_info.h" #include "ddp_ovl.h" #include "disp_drv_platform.h" #include "primary_display.h" #include "ddp_dump.h" #include "display_recorder.h" #include "fbconfig_kdebug.h" #include "mtk_ovl.h" /* #include "mtk_boot.h" */ #include "disp_helper.h" #include "compat_mtkfb.h" #include "disp_dts_gpio.h" #include "disp_recovery.h" #include "ddp_clkmgr.h" #include "ddp_log.h" #include "ddp_m4u.h" #include "disp_lowpower.h" #include "mtk_disp_mgr.h" #ifdef MTKFB_SUPPORT_SECOND_DISP # include "extd_multi_control.h" #endif #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) # include "external_display.h" #endif #ifndef _UAPI__ASMARM_SETUP_H #define _UAPI__ASMARM_SETUP_H #endif #ifdef CONFIG_MTK_SMI_EXT #include "smi_public.h" #endif /* include for mmpath auto gen */ #ifndef CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS #endif #include "mmpath.h" /* static variable */ static u32 MTK_FB_XRES; static u32 MTK_FB_YRES; static u32 MTK_FB_BPP; static u32 MTK_FB_PAGES; static u32 fb_xres_update; static u32 fb_yres_update; static size_t mtkfb_log_on = true; static int sem_flipping_cnt = 1; static int sem_early_suspend_cnt = 1; static int vsync_cnt; static const struct timeval FRAME_INTERVAL = { 0, 30000 }; /* 33ms */ static bool no_update; static struct disp_session_input_config session_input; /* macro definiton */ #define ALIGN_TO(x, n) (((x) + ((n) - 1)) & ~((n) - 1)) #define MTK_FB_XRESV (ALIGN_TO(MTK_FB_XRES, MTK_FB_ALIGNMENT)) /* For page flipping */ #define MTK_FB_YRESV (ALIGN_TO(MTK_FB_YRES, MTK_FB_ALIGNMENT) * MTK_FB_PAGES) #define MTK_FB_BYPP ((MTK_FB_BPP + 7) >> 3) #define MTK_FB_LINE (ALIGN_TO(MTK_FB_XRES, MTK_FB_ALIGNMENT) * MTK_FB_BYPP) #define MTK_FB_SIZE (MTK_FB_LINE * ALIGN_TO(MTK_FB_YRES, MTK_FB_ALIGNMENT)) #define MTK_FB_SIZEV (MTK_FB_LINE * ALIGN_TO(MTK_FB_YRES, MTK_FB_ALIGNMENT) * \ MTK_FB_PAGES) #define ASSERT_LAYER (DDP_OVL_LAYER_MUN-1) #define DISP_DEFAULT_UI_LAYER_ID (DDP_OVL_LAYER_MUN-1) #define DISP_CHANGED_UI_LAYER_ID (DDP_OVL_LAYER_MUN-2) #define NOT_REFERENCED(x) { (x) = (x); } #ifdef CONFIG_MTK_AEE_FEATURE # define CHECK_RET(expr) \ do { \ int ret = (expr); \ aee_kernel_exception("mtkfb", "[DISP]error:%s,%d", \ __FILE__, __LINE__); \ } while (0) #else # define CHECK_RET(expr) #endif #define MTKFB_LOG(fmt, arg...) \ do { \ if (mtkfb_log_on) \ DISP_LOG_PRINT(ANDROID_LOG_WARN, "MTKFB", fmt, ##arg); \ } while (0) /* always show this debug info while the global debug log is off */ #define MTKFB_LOG_DBG(fmt, arg...) \ do { \ if (!mtkfb_log_on) \ DISP_LOG_PRINT(ANDROID_LOG_WARN, "MTKFB", fmt, ##arg); \ } while (0) #define MTKFB_FUNC() \ do { \ if (mtkfb_log_on) \ DISP_LOG_PRINT(ANDROID_LOG_INFO, "MTKFB", \ "[Func]%s\n", __func__); \ } while (0) #define PRNERR(fmt, args...) \ DISP_LOG_PRINT(ANDROID_LOG_INFO, "MTKFB", fmt, ## args) /* ------------------------------------------------------------ */ /* local variables */ /* ------------------------------------------------------------ */ struct notifier_block pm_nb; unsigned int EnableVSyncLog; unsigned long fb_mva; dma_addr_t fb_iova; atomic_t has_pending_update = ATOMIC_INIT(0); struct fb_overlay_layer video_layerInfo; UINT32 dbr_backup; UINT32 dbg_backup; UINT32 dbb_backup; bool fblayer_dither_needed; bool is_ipoh_bootup; struct fb_info *mtkfb_fbi; struct fb_overlay_layer fb_layer_context; struct mtk_dispif_info dispif_info[MTKFB_MAX_DISPLAY_COUNT]; unsigned int FB_LAYER = 2; bool is_early_suspended = FALSE; atomic_t OverlaySettingDirtyFlag = ATOMIC_INIT(0); atomic_t OverlaySettingApplied = ATOMIC_INIT(0); unsigned int PanDispSettingPending; unsigned int PanDispSettingDirty; unsigned int PanDispSettingApplied; unsigned int need_esd_check; unsigned int lcd_fps = 6000; wait_queue_head_t screen_update_wq; char mtkfb_lcm_name[256] = { 0 }; #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) struct fb_info *ext_mtkfb_fb; unsigned long ext_fb_pa; unsigned int ext_lcd_fps = 6000; char ext_mtkfb_lcm_name[256] = { 0 }; #endif DEFINE_SEMAPHORE(sem_flipping); DEFINE_SEMAPHORE(sem_early_suspend); DEFINE_SEMAPHORE(sem_overlay_buffer); /* ----------------------------------------------------------- */ /* local function declarations */ /* ----------------------------------------------------------- */ static int mtkfb_set_par(struct fb_info *fbi); static int init_framebuffer(struct fb_info *info); static int mtkfb_get_overlay_layer_info( struct fb_overlay_layer_info *layerInfo); #ifdef CONFIG_OF static int _parse_tag_videolfb(void); #endif static void mtkfb_late_resume(void); static void mtkfb_early_suspend(void); void mtkfb_log_enable(int enable) { mtkfb_log_on = enable; MTKFB_LOG("mtkfb log %s\n", enable ? "enabled" : "disabled"); } /* * ---------------------------------------------------------- * fbdev framework callbacks and the ioctl interface * ---------------------------------------------------------- */ /* Called each time the mtkfb device is opened */ static int mtkfb_open(struct fb_info *info, int user) { NOT_REFERENCED(info); NOT_REFERENCED(user); DISPFUNC(); MSG_FUNC_ENTER(); MSG_FUNC_LEAVE(); return 0; } /* Called when the mtkfb device is closed. We make sure that any pending*/ /* gfx DMA operations are ended, before we return. */ static int mtkfb_release(struct fb_info *info, int user) { NOT_REFERENCED(info); NOT_REFERENCED(user); DISPFUNC(); MSG_FUNC_ENTER(); MSG_FUNC_LEAVE(); return 0; } #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) static int mtkfb1_blank(int blank_mode, struct fb_info *info) { pr_debug("%s blank mode :%d\n", __func__, blank_mode); switch (blank_mode) { case FB_BLANK_UNBLANK: case FB_BLANK_NORMAL: /* * parameter 0 is to all external display should be resumed, * 0x20003 is dual lcm session */ external_display_resume(0x20003); break; case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: break; case FB_BLANK_POWERDOWN: /* * parameter 0 is to all external display should be suspend, * 0x20003 is dual lcm session */ external_display_suspend(0x20003); break; default: return -EINVAL; } return 0; } #endif static int mtkfb_blank(int blank_mode, struct fb_info *info) { enum mtkfb_power_mode prev_pm = primary_display_get_power_mode(); switch (blank_mode) { case FB_BLANK_UNBLANK: case FB_BLANK_NORMAL: DISPDBG("%s mtkfb_late_resume\n", __func__); if (bypass_blank) { DISP_PR_INFO("FB_BLANK_UNBLANK bypass_blank %d\n", bypass_blank); break; } primary_display_set_power_mode(FB_RESUME); mtkfb_late_resume(); debug_print_power_mode_check(prev_pm, FB_RESUME); break; case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: break; case FB_BLANK_POWERDOWN: DISPDBG("%s mtkfb_early_suspend\n", __func__); if (bypass_blank) { DISP_PR_INFO("FB_BLANK_POWERDOWN bypass_blank %d\n", bypass_blank); break; } primary_display_set_power_mode(FB_SUSPEND); mtkfb_early_suspend(); debug_print_power_mode_check(prev_pm, FB_SUSPEND); break; default: return -EINVAL; } return 0; } int mtkfb_set_backlight_level(unsigned int level) { bool aal_is_support = disp_aal_is_support(); MTKFB_FUNC(); DISPDBG("%s:%d Start\n", __func__, level); if (aal_is_support) primary_display_setbacklight_nolock(level); else primary_display_setbacklight(level); DISPDBG("%s End\n", __func__); return 0; } EXPORT_SYMBOL(mtkfb_set_backlight_level); #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) int mtkfb1_set_backlight_level(unsigned int level) { MTKFB_FUNC(); /* DISPDBG("mtkfb1_set_backlight_level:%d Start\n", level); */ external_display_setbacklight(level); /* DISPDBG("mtkfb1_set_backlight_level End\n"); */ return 0; } EXPORT_SYMBOL(mtkfb1_set_backlight_level); #endif int mtkfb_set_backlight_mode(unsigned int mode) { MTKFB_FUNC(); if (down_interruptible(&sem_flipping)) { DISP_PR_ERR("[FB Driver] can't get semaphore:%d\n", __LINE__); return -ERESTARTSYS; } sem_flipping_cnt--; if (down_interruptible(&sem_early_suspend)) { DISP_PR_ERR("[FB Driver] can't get semaphore:%d\n", __LINE__); sem_flipping_cnt++; up(&sem_flipping); return -ERESTARTSYS; } sem_early_suspend_cnt--; if (primary_display_is_sleepd()) goto end; /* DISP_SetBacklight_mode(mode); */ end: sem_flipping_cnt++; sem_early_suspend_cnt++; up(&sem_early_suspend); up(&sem_flipping); return 0; } EXPORT_SYMBOL(mtkfb_set_backlight_mode); int mtkfb_set_backlight_pwm(int div) { MTKFB_FUNC(); if (down_interruptible(&sem_flipping)) { DISP_PR_ERR("[FB Driver] can't get semaphore:%d\n", __LINE__); return -ERESTARTSYS; } sem_flipping_cnt--; if (down_interruptible(&sem_early_suspend)) { DISP_PR_ERR("[FB Driver] can't get semaphore:%d\n", __LINE__); sem_flipping_cnt++; up(&sem_flipping); return -ERESTARTSYS; } sem_early_suspend_cnt--; if (primary_display_is_sleepd()) goto end; /* DISP_SetPWM(div); */ end: sem_flipping_cnt++; sem_early_suspend_cnt++; up(&sem_early_suspend); up(&sem_flipping); return 0; } EXPORT_SYMBOL(mtkfb_set_backlight_pwm); int mtkfb_get_backlight_pwm(int div, unsigned int *freq) { /* DISP_GetPWM(div, freq); */ return 0; } EXPORT_SYMBOL(mtkfb_get_backlight_pwm); void mtkfb_waitVsync(void) { if (primary_display_is_sleepd()) { DISPMSG("[MTKFB_VSYNC]:mtkfb has suspend, return directly\n"); msleep(20); return; } vsync_cnt++; #ifdef CONFIG_FPGA_EARLY_PORTING msleep(20); #else primary_display_wait_for_vsync(NULL); #endif vsync_cnt--; } EXPORT_SYMBOL(mtkfb_waitVsync); static int _convert_fb_layer_to_disp_input(struct fb_overlay_layer *src, struct disp_input_config *dst) { dst->layer_id = src->layer_id; dst->dirty_roi_num = 0; if (!src->layer_enable) { dst->layer_enable = 0; return 0; } switch (src->src_fmt) { case MTK_FB_FORMAT_YUV422: dst->src_fmt = DISP_FORMAT_YUV422; break; case MTK_FB_FORMAT_RGB565: dst->src_fmt = DISP_FORMAT_RGB565; break; case MTK_FB_FORMAT_RGB888: dst->src_fmt = DISP_FORMAT_RGB888; break; case MTK_FB_FORMAT_BGR888: dst->src_fmt = DISP_FORMAT_BGR888; break; case MTK_FB_FORMAT_ARGB8888: dst->src_fmt = DISP_FORMAT_ARGB8888; break; case MTK_FB_FORMAT_ABGR8888: dst->src_fmt = DISP_FORMAT_ABGR8888; break; case MTK_FB_FORMAT_XRGB8888: dst->src_fmt = DISP_FORMAT_XRGB8888; break; case MTK_FB_FORMAT_XBGR8888: dst->src_fmt = DISP_FORMAT_XBGR8888; break; case MTK_FB_FORMAT_UYVY: dst->src_fmt = DISP_FORMAT_UYVY; break; default: DISP_PR_INFO("Invalid color format: 0x%x\n", src->src_fmt); return -1; } dst->src_base_addr = src->src_base_addr; dst->security = src->security; dst->src_phy_addr = src->src_phy_addr; DISPDBG("%s, dst->addr=0x%lx\n", __func__, (unsigned long)dst->src_phy_addr); dst->isTdshp = src->isTdshp; dst->next_buff_idx = src->next_buff_idx; dst->identity = src->identity; dst->connected_type = src->connected_type; /* set Alpha blending */ dst->alpha = src->alpha; if (src->src_fmt == MTK_FB_FORMAT_ARGB8888 || src->src_fmt == MTK_FB_FORMAT_ABGR8888) dst->alpha_enable = TRUE; else dst->alpha_enable = FALSE; /* set src width, src height */ dst->src_offset_x = src->src_offset_x; dst->src_offset_y = src->src_offset_y; dst->src_width = src->src_width; dst->src_height = src->src_height; dst->tgt_offset_x = src->tgt_offset_x; dst->tgt_offset_y = src->tgt_offset_y; dst->tgt_width = src->tgt_width; dst->tgt_height = src->tgt_height; if (dst->tgt_width > dst->src_width) dst->tgt_width = dst->src_width; if (dst->tgt_height > dst->src_height) dst->tgt_height = dst->src_height; dst->src_pitch = src->src_pitch; /* set color key */ dst->src_color_key = src->src_color_key; dst->src_use_color_key = src->src_use_color_key; /* data transferring is triggerred in MTKFB_TRIG_OVERLAY_OUT */ dst->layer_enable = src->layer_enable; dst->ext_sel_layer = -1; DISPDBG("%s:id=%u,en=%u,next_idx=%u,va=%lx,pa=%lx,src,dstfmt=%u,%u\n", __func__, dst->layer_id, dst->layer_enable, dst->next_buff_idx, (unsigned long)dst->src_base_addr, (unsigned long)dst->src_phy_addr, src->src_fmt, dst->src_fmt); DISPDBG("%s:pitch=%u,src(%u,%u,%ux%u),target(%u,%u,%ux%u),aen=%u\n", __func__, dst->src_pitch, dst->src_offset_x, dst->src_offset_y, dst->src_width, dst->src_height, dst->tgt_offset_x, dst->tgt_offset_y, dst->tgt_width, dst->tgt_height, dst->alpha_enable); return 0; } static int mtkfb_pan_display_impl(struct fb_var_screeninfo *var, struct fb_info *info) { UINT32 offset = 0; UINT32 paStart = 0; char *vaStart = NULL, *vaEnd = NULL; int ret = 0; /* int wait_ret = 0; */ /* unsigned int layerpitch = 0; */ unsigned int src_pitch = 0; struct disp_session_input_config *session_input; struct disp_input_config *input; /* DISPFUNC(); */ if (no_update) { DISPMSG("the first time of %s will be ignored\n", __func__); return ret; } DISPCHECK("pan_display: offset(%u,%u), res(%u,%u), resv(%u,%u)\n", var->xoffset, var->yoffset, info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual); info->var.yoffset = var->yoffset; offset = var->yoffset * info->fix.line_length; paStart = fb_mva + offset; vaStart = (unsigned long)info->screen_base + offset; vaEnd = vaStart + info->var.yres * info->fix.line_length; DISPCHECK("%s:%d, va=0x%lx, pa=0x%lx\n", __func__, __LINE__, (unsigned long)vaStart, (unsigned long)paStart); DISPCHECK("fb dump: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", *(unsigned int *)vaStart, *(unsigned int *)(vaStart+4), *(unsigned int *)(vaStart+8), *(unsigned int *)(vaStart+0xC)); session_input = kzalloc(sizeof(*session_input), GFP_KERNEL); if (!session_input) { DISP_PR_ERR("session input allocate fail\n"); ASSERT(0); return -1; } /* pan display use layer 0 */ input = &session_input->config[0]; input->layer_id = 0; input->src_phy_addr = (unsigned long)paStart; input->src_base_addr = (unsigned long)vaStart; input->layer_id = primary_display_get_option("FB_LAYER"); input->layer_enable = 1; input->src_offset_x = 0; input->src_offset_y = 0; input->src_width = var->xres; input->src_height = var->yres; input->tgt_offset_x = 0; input->tgt_offset_y = 0; input->tgt_width = var->xres; input->tgt_height = var->yres; input->ext_sel_layer = -1; switch (var->bits_per_pixel) { case 16: input->src_fmt = DISP_FORMAT_RGB565; break; case 24: input->src_fmt = DISP_FORMAT_RGB888; break; case 32: input->src_fmt = (var->blue.offset == 0) ? DISP_FORMAT_BGRA8888 : DISP_FORMAT_RGBX8888; break; default: DISP_PR_INFO("Invalid color format bpp: %d\n", var->bits_per_pixel); kfree(session_input); return -1; } input->alpha_enable = FALSE; input->alpha = 0xFF; input->next_buff_idx = -1; src_pitch = ALIGN_TO(var->xres, MTK_FB_ALIGNMENT); input->src_pitch = src_pitch; input->ext_sel_layer = -1; session_input->config_layer_num++; session_input->setter = SESSION_USER_PANDISP; if (!is_DAL_Enabled()) { /* disable font layer(layer3) drawed in lk */ session_input->config[1].layer_id = primary_display_get_option("ASSERT_LAYER"); session_input->config[1].next_buff_idx = -1; session_input->config[1].layer_enable = 0; session_input->config_layer_num++; } ret = primary_display_config_input_multiple(session_input); ret = primary_display_trigger(TRUE, NULL, 0); kfree(session_input); return ret; } /** * Set fb_info.fix fields and also updates fbdev. * When calling this fb_info.var must be set up already. */ static void set_fb_fix(struct mtkfb_device *fbdev) { struct fb_info *fbi = fbdev->fb_info; struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_var_screeninfo *var = &fbi->var; struct fb_ops *fbops = fbi->fbops; strncpy(fix->id, MTKFB_DRIVER, sizeof(fix->id)); fix->type = FB_TYPE_PACKED_PIXELS; switch (var->bits_per_pixel) { case 16: case 24: case 32: fix->visual = FB_VISUAL_TRUECOLOR; break; case 1: case 2: case 4: case 8: fix->visual = FB_VISUAL_PSEUDOCOLOR; break; default: DISP_PR_ERR("set fb fix fail,bits per pixel=%d\n", var->bits_per_pixel); return; } fix->accel = FB_ACCEL_NONE; fix->line_length = ALIGN_TO(var->xres_virtual, MTK_FB_ALIGNMENT) * var->bits_per_pixel / 8; fix->smem_len = fbdev->fb_size_in_byte; fix->smem_start = (unsigned long)fbdev->fb_pa_base; fix->xpanstep = 0; fix->ypanstep = 1; fbops->fb_fillrect = cfb_fillrect; fbops->fb_copyarea = cfb_copyarea; fbops->fb_imageblit = cfb_imageblit; } /** * Check values in var, try to adjust them in case of out of bound values if * possible, or return error. */ static int mtkfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) { unsigned int bpp; unsigned long max_frame_size; unsigned long line_size; struct mtkfb_device *fbdev = (struct mtkfb_device *)fbi->par; /* DISPFUNC(); */ DISPCHECK("%s:x,yres=%u,%u,x,y_virt=%u,%u,x,yoffset=%u,%u,bppixel=%u\n", __func__, var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->xoffset, var->yoffset, var->bits_per_pixel); bpp = var->bits_per_pixel; if (bpp != 16 && bpp != 24 && bpp != 32) { MTKFB_LOG("[%s]unsupported bpp: %d", __func__, bpp); return -1; } switch (var->rotate) { case 0: case 180: var->xres = MTK_FB_XRES; var->yres = MTK_FB_YRES; break; case 90: case 270: var->xres = MTK_FB_YRES; var->yres = MTK_FB_XRES; break; default: return -1; } if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; max_frame_size = fbdev->fb_size_in_byte; DISPDBG("fbdev->fb_size_in_byte=0x%08lx\n", fbdev->fb_size_in_byte); line_size = var->xres_virtual * bpp / 8; if (line_size * var->yres_virtual > max_frame_size) { /* Try to keep yres_virtual first */ line_size = max_frame_size / var->yres_virtual; var->xres_virtual = line_size * 8 / bpp; if (var->xres_virtual < var->xres) { /* Still doesn't fit. Shrink yres_virtual too */ var->xres_virtual = var->xres; line_size = var->xres * bpp / 8; var->yres_virtual = max_frame_size / line_size; } } DISPDBG("%s:x,yres=%u,%u,x,y_virt=%u,%u,x,yoffset=%u,%u,bppixel=%u\n", __func__, var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->xoffset, var->yoffset, var->bits_per_pixel); if (var->xres + var->xoffset > var->xres_virtual) var->xoffset = var->xres_virtual - var->xres; if (var->yres + var->yoffset > var->yres_virtual) var->yoffset = var->yres_virtual - var->yres; DISPMSG("%s:x,yres=%u,%u,x,y_virt=%u,%u,x,yoffset=%u,%u,bppixel=%u\n", __func__, var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->xoffset, var->yoffset, var->bits_per_pixel); if (bpp == 16) { var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; var->transp.offset = 0; var->transp.length = 0; } else if (bpp == 24) { var->red.length = var->green.length = var->blue.length = 8; var->transp.length = 0; ASSERT(var->green.offset == 8); ASSERT((var->red.offset + var->blue.offset) == 16); ASSERT((var->red.offset == 16 || var->red.offset == 0)); } else if (bpp == 32) { var->red.length = var->green.length = var->blue.length = var->transp.length = 8; ASSERT(var->red.offset + var->blue.offset == 16); ASSERT((var->red.offset == 16 || var->red.offset == 0)); } var->red.msb_right = var->green.msb_right = var->blue.msb_right = var->transp.msb_right = 0; if (var->activate & FB_ACTIVATE_NO_UPDATE) no_update = true; else no_update = false; var->activate = FB_ACTIVATE_NOW; var->height = UINT_MAX; var->width = UINT_MAX; var->grayscale = 0; var->nonstd = 0; var->pixclock = UINT_MAX; var->left_margin = UINT_MAX; var->right_margin = UINT_MAX; var->upper_margin = UINT_MAX; var->lower_margin = UINT_MAX; var->hsync_len = UINT_MAX; var->vsync_len = UINT_MAX; var->vmode = FB_VMODE_NONINTERLACED; var->sync = 0; MSG_FUNC_LEAVE(); return 0; } /** * Switch to a new mode. The parameters for it has been check already by * mtkfb_check_var. */ static int mtkfb_set_par(struct fb_info *fbi) { struct fb_var_screeninfo *var = &fbi->var; struct mtkfb_device *fbdev = (struct mtkfb_device *)fbi->par; struct fb_overlay_layer fb_layer; u32 bpp = var->bits_per_pixel; struct disp_session_input_config *session_input; struct disp_input_config *input; /* DISPFUNC(); */ memset(&fb_layer, 0, sizeof(struct fb_overlay_layer)); switch (bpp) { case 16: fb_layer.src_fmt = MTK_FB_FORMAT_RGB565; fb_layer.src_use_color_key = 1; fb_layer.src_color_key = 0xFF000000; break; case 24: fb_layer.src_use_color_key = 1; fb_layer.src_fmt = (var->blue.offset == 0) ? MTK_FB_FORMAT_RGB888 : MTK_FB_FORMAT_BGR888; fb_layer.src_color_key = 0xFF000000; break; case 32: fb_layer.src_use_color_key = 0; DISPDBG("set_par,var->blue.offset=%d\n", var->blue.offset); fb_layer.src_fmt = (var->blue.offset == 0) ? MTK_FB_FORMAT_ARGB8888 : MTK_FB_FORMAT_ABGR8888; fb_layer.src_color_key = 0; break; default: fb_layer.src_fmt = MTK_FB_FORMAT_UNKNOWN; DISP_PR_INFO("[%s]unsupported bpp: %d", __func__, bpp); return -1; } set_fb_fix(fbdev); fb_layer.layer_id = primary_display_get_option("FB_LAYER"); fb_layer.layer_enable = 1; fb_layer.src_base_addr = (unsigned long)fbdev->fb_va_base + var->yoffset * fbi->fix.line_length; DISPDBG("fb_pa=0x%08lx,var->yoffset=%u,fbi->fix.line_length=%u\n", fb_mva, var->yoffset, fbi->fix.line_length); fb_layer.src_phy_addr = fb_mva + var->yoffset * fbi->fix.line_length; fb_layer.src_direct_link = 0; fb_layer.src_offset_x = fb_layer.src_offset_y = 0; fb_layer.src_pitch = ALIGN_TO(var->xres, MTK_FB_ALIGNMENT); fb_layer.src_width = fb_layer.tgt_width = var->xres; fb_layer.src_height = fb_layer.tgt_height = var->yres; fb_layer.tgt_offset_x = fb_layer.tgt_offset_y = 0; fb_layer.alpha = 0xff; /* fb_layer.src_color_key = 0; */ fb_layer.layer_rotation = MTK_FB_ORIENTATION_0; fb_layer.layer_type = LAYER_2D; DISPDBG("%s, fb_layer.src_fmt=%x\n", __func__, fb_layer.src_fmt); session_input = kzalloc(sizeof(*session_input), GFP_KERNEL); if (!session_input) goto out; session_input->config_layer_num = 0; if (!is_DAL_Enabled()) { int layer_num; DISPCHECK("AEE is not enabled, will disable layer 3\n"); layer_num = session_input->config_layer_num; input = &session_input->config[layer_num]; session_input->config_layer_num++; input->layer_id = primary_display_get_option("ASSERT_LAYER"); input->layer_enable = 0; } else { DISPCHECK("AEE is enabled, should not disable layer 3\n"); } input = &session_input->config[session_input->config_layer_num++]; _convert_fb_layer_to_disp_input(&fb_layer, input); session_input->setter = SESSION_USER_INVALID; primary_display_config_input_multiple(session_input); kfree(session_input); out: /* backup fb_layer information. */ memcpy(&fb_layer_context, &fb_layer, sizeof(fb_layer)); MSG_FUNC_LEAVE(); return 0; } static int mtkfb_soft_cursor(struct fb_info *info, struct fb_cursor *cursor) { NOT_REFERENCED(info); NOT_REFERENCED(cursor); return 0; } static int mtkfb_get_overlay_layer_info(struct fb_overlay_layer_info *layerInfo) { return 0; } void mtkfb_dump_layer_info(void) { } UINT32 color; unsigned int mtkfb_fm_auto_test(void) { unsigned int result = 0; unsigned int i = 0; unsigned long fbVirAddr; UINT32 fbsize; int ret = 0; unsigned int *fb_buffer; struct mtkfb_device *fbdev = (struct mtkfb_device *)mtkfb_fbi->par; struct fb_var_screeninfo var; int idle_state_backup = disp_helper_get_option(DISP_OPT_IDLE_MGR); if (primary_display_is_sleepd()) { DISP_PR_INFO("primary display path is already sleep, skip\n"); return 0; } if (idle_state_backup) { primary_display_idlemgr_kick(__func__, 1); enable_idlemgr(0); } fbVirAddr = (unsigned long)fbdev->fb_va_base; fb_buffer = (unsigned int *)fbVirAddr; memcpy(&var, &(mtkfb_fbi->var), sizeof(var)); var.activate = FB_ACTIVATE_NOW; var.bits_per_pixel = 32; var.transp.offset = 24; var.transp.length = 8; var.red.offset = 16; var.red.length = 8; var.green.offset = 8; var.green.length = 8; var.blue.offset = 0; var.blue.length = 8; ret = mtkfb_check_var(&var, mtkfb_fbi); if (ret) PRNERR("failed to mtkfb_check_var\n"); mtkfb_fbi->var = var; if (color == 0) color = 0xFF00FF00; fbsize = ALIGN_TO(DISP_GetScreenWidth(), MTK_FB_ALIGNMENT) * DISP_GetScreenHeight() * MTK_FB_PAGES; for (i = 0; i < fbsize; i++) *fb_buffer++ = color; mtkfb_pan_display_impl(&mtkfb_fbi->var, mtkfb_fbi); msleep(100); result = primary_display_lcm_ATA(); if (idle_state_backup) enable_idlemgr(1); if (result == 0) DISP_PR_ERR("ATA LCM failed\n"); else DISPMSG("ATA LCM passed\n"); return result; } int mtkfb_aod_mode_switch(enum mtkfb_aod_power_mode aod_pm) { int ret = 0; enum mtkfb_power_mode prev_pm = primary_display_get_power_mode(); if (!primary_is_aod_supported()) { DISPCHECK("AOD: feature not support\n"); return ret; } DISPCHECK("%s: AOD ioctl: %d in\n", __func__, aod_pm); if (aod_pm == MTKFB_AOD_DOZE_SUSPEND) { /* * First DOZE to power on dispsys and LCM(low power mode); * then DOZE_SUSPEND to power off dispsys. */ if (primary_display_is_sleepd() && primary_display_get_lcm_power_state()) { primary_display_set_power_mode(DOZE); primary_display_resume(); debug_print_power_mode_check(prev_pm, DOZE); } primary_display_set_power_mode(DOZE_SUSPEND); ret = primary_display_suspend(); debug_print_power_mode_check(prev_pm, DOZE_SUSPEND); } else if (aod_pm == MTKFB_AOD_DOZE) { primary_display_set_power_mode(DOZE); ret = primary_display_resume(); debug_print_power_mode_check(prev_pm, DOZE); } else { DISP_PR_ERR("AOD: error: unknown AOD power mode %d\n", aod_pm); } if (ret < 0) DISP_PR_INFO("%s: AOD set %d failed\n", __func__, aod_pm); return ret; } static int mtkfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; enum DISP_STATUS stat = 0; int ret = 0; DISPFUNC(); /* M: dump debug mmprofile log info */ DISPDBG("%s: info=%p, cmd nr=0x%08x, cmd size=0x%08x\n", __func__, info, (unsigned int)_IOC_NR(cmd), (unsigned int)_IOC_SIZE(cmd)); switch (cmd) { case MTKFB_GET_FRAMEBUFFER_MVA: return copy_to_user(argp, &fb_mva, sizeof(fb_mva)) ? -EFAULT : 0; case MTKFB_GET_DISPLAY_IF_INFORMATION: { int displayid = 0; if (copy_from_user(&displayid, (void __user *)arg, sizeof(displayid))) { MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); return -EFAULT; } if (displayid == 0) { dispif_info[displayid].displayWidth = primary_display_get_width(); dispif_info[displayid].displayHeight = primary_display_get_height(); dispif_info[displayid].lcmOriginalWidth = primary_display_get_original_width(); dispif_info[displayid].lcmOriginalHeight = primary_display_get_original_height(); dispif_info[displayid].displayMode = primary_display_is_video_mode() ? 0 : 1; } else { DISP_PR_ERR("info for displayid:%d is not available\n", displayid); return -EFAULT; } if (copy_to_user((void __user *)arg, &(dispif_info[displayid]), sizeof(struct mtk_dispif_info))) { MTKFB_LOG("[FB]: copy_to_user failed! line:%d\n", __LINE__); ret = -EFAULT; } return ret; } case MTKFB_SET_AOD_POWER_MODE: { enum mtkfb_aod_power_mode aod_pm = MTKFB_AOD_POWER_MODE_ERROR; aod_pm = (enum mtkfb_aod_power_mode)arg; ret = mtkfb_aod_mode_switch(arg); break; } case MTKFB_POWEROFF: { MTKFB_FUNC(); if (primary_display_is_sleepd()) { DISP_PR_INFO("[FB Driver]still in MTKFB_POWEROFF!\n"); return ret; } DISPDBG("[FB Driver] enter MTKFB_POWEROFF\n"); /* TODO: remove unnessecary IOCTL * It will call SurfaceFlinger blank before this that * do the same thing, so it is trivial now. */ /*ret = primary_display_suspend();*/ if (stat < 0) DISP_PR_ERR("primary display suspend failed\n"); DISPDBG("[FB Driver] leave MTKFB_POWEROFF\n"); is_early_suspended = TRUE; /* no care */ return ret; } case MTKFB_POWERON: { MTKFB_FUNC(); if (primary_display_is_alive()) { DISP_PR_INFO("[FB Driver]still in MTKFB_POWERON!\n"); return ret; } DISPDBG("[FB Driver] enter MTKFB_POWERON\n"); /* TODO: remove unnessecary IOCTL * It will call SurfaceFlinger unblank after this that * do the same thing, so it is trivial now. */ /*primary_display_resume();*/ DISPDBG("[FB Driver] leave MTKFB_POWERON\n"); is_early_suspended = FALSE; /* no care */ return ret; } case MTKFB_GET_POWERSTATE: { int power_state; if (primary_display_is_sleepd()) power_state = 0; else power_state = 1; if (copy_to_user(argp, &power_state, sizeof(power_state))) { pr_err("MTKFB_GET_POWERSTATE failed\n"); return -EFAULT; } return 0; } case MTKFB_CONFIG_IMMEDIATE_UPDATE: { MTKFB_LOG("[%s] MTKFB_CONFIG_IMMEDIATE_UPDATE, enable = %lu\n", __func__, arg); if (down_interruptible(&sem_early_suspend)) { MTKFB_LOG("[%s] can't get semaphore:%d\n", __func__, __LINE__); return -ERESTARTSYS; } sem_early_suspend_cnt--; up(&sem_early_suspend); return ret; } case MTKFB_CAPTURE_FRAMEBUFFER: { #if defined(DISP_CAPTURE_FB) /* comment this for iofuzzer security issue */ unsigned long *src_pbuf = 0; unsigned int pixel_bpp = primary_display_get_bpp() / 8; unsigned int fbsize = DISP_GetScreenHeight() * DISP_GetScreenWidth() * pixel_bpp; src_pbuf = vmalloc(fbsize); if (!src_pbuf) { MTKFB_LOG("[FB]: vmalloc src_pbuf failed! line:%d\n", __LINE__); return -EFAULT; } dprec_logger_start(DPREC_LOGGER_WDMA_DUMP, 0, 0); ret = primary_display_capture_framebuffer_ovl( (unsigned long)src_pbuf, fbsize, UFMT_BGRA8888); if (ret < 0) DISP_PR_ERR("primary capture framebuffer failed\n"); dprec_logger_done(DPREC_LOGGER_WDMA_DUMP, 0, 0); if (copy_to_user((void __user *)arg, src_pbuf, fbsize)) { MTKFB_LOG("[FB]: copy_to_user failed! line:%d\n", __LINE__); ret = -EFAULT; } vfree(src_pbuf); #endif return ret; } case MTKFB_SLT_AUTO_CAPTURE: { #if defined(DISP_FOR_SLT)/* please open this when need SLT */ struct fb_slt_catpure capConfig; unsigned long *src_pbuf = 0; unsigned int format; unsigned int pixel_bpp = primary_display_get_bpp() / 8; unsigned int fbsize = DISP_GetScreenHeight() * DISP_GetScreenWidth() * pixel_bpp; if (copy_from_user(&capConfig, (void __user *)arg, sizeof(capConfig))) { MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); return -EFAULT; } switch (capConfig.format) { case MTK_FB_FORMAT_RGB888: format = UFMT_RGB888; break; case MTK_FB_FORMAT_BGR888: format = UFMT_BGR888; break; case MTK_FB_FORMAT_ARGB8888: format = UFMT_ARGB8888; break; case MTK_FB_FORMAT_RGB565: format = UFMT_RGB565; break; case MTK_FB_FORMAT_UYVY: format = UFMT_UYVY; break; case MTK_FB_FORMAT_ABGR8888: default: format = UFMT_ABGR8888; break; } src_pbuf = vmalloc(fbsize); if (!src_pbuf) { MTKFB_LOG("[FB]vmalloc src_pbuf failed!line:%d\n", __LINE__); return -EFAULT; } ret = primary_display_capture_framebuffer_ovl( (unsigned long)src_pbuf, fbsize, format); if (ret < 0) DISP_PR_ERR("primary capture framebuffer failed\n"); if (copy_to_user((unsigned long *)capConfig.outputBuffer, src_pbuf, fbsize)) { MTKFB_LOG("[FB]: copy_to_user failed! line:%d\n", __LINE__); ret = -EFAULT; } vfree(src_pbuf); #endif return ret; } case MTKFB_GET_OVERLAY_LAYER_INFO: { struct fb_overlay_layer_info layerInfo; MTKFB_LOG(" %s():MTKFB_GET_OVERLAY_LAYER_INFO\n", __func__); if (copy_from_user(&layerInfo, (void __user *)arg, sizeof(layerInfo))) { MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); return -EFAULT; } if (mtkfb_get_overlay_layer_info(&layerInfo) < 0) { MTKFB_LOG("[FB]: Failed to get overlay layer info\n"); return -EFAULT; } if (copy_to_user((void __user *)arg, &layerInfo, sizeof(layerInfo))) { MTKFB_LOG("[FB]: copy_to_user failed! line:%d\n", __LINE__); ret = -EFAULT; } return ret; } case MTKFB_SET_OVERLAY_LAYER: { /* no function */ struct fb_overlay_layer *layerInfo; struct disp_input_config *input; int layer_num; DISPMSG(" %s():MTKFB_SET_OVERLAY_LAYER\n", __func__); layerInfo = kmalloc(sizeof(*layerInfo), GFP_KERNEL); if (!layerInfo) return -ENOMEM; if (copy_from_user(layerInfo, (void __user *)arg, sizeof(*layerInfo))) { MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); kfree(layerInfo); return -EFAULT; } /* * in early suspend mode, will not update buffer index, * info SF by return value */ if (primary_display_is_sleepd()) { DISP_PR_INFO("[FB]set overlay in early suspend,skip\n"); kfree(layerInfo); return MTKFB_ERROR_IS_EARLY_SUSPEND; } memset((void *)&session_input, 0, sizeof(session_input)); layer_num = session_input.config_layer_num; input = &session_input.config[layer_num]; session_input.config_layer_num++; session_input.setter = SESSION_USER_PANDISP; _convert_fb_layer_to_disp_input(layerInfo, input); primary_display_config_input_multiple(&session_input); primary_display_trigger(1, NULL, 0); kfree(layerInfo); return ret; } case MTKFB_ERROR_INDEX_UPDATE_TIMEOUT: { pr_info("[DDP] %s():MTKFB_ERROR_INDEX_UPDATE_TIMEOUT\n", __func__); /* call info dump function here */ /* mtkfb_dump_layer_info(); */ return ret; } case MTKFB_ERROR_INDEX_UPDATE_TIMEOUT_AEE: { pr_info("[DDP] %s():MTKFB_ERROR_INDEX_UPDATE_TIMEOUT\n", __func__); /* call info dump function here */ /* mtkfb_dump_layer_info(); */ return ret; } case MTKFB_SET_VIDEO_LAYERS: { struct mmp_fb_overlay_layers { struct fb_overlay_layer Layer0; struct fb_overlay_layer Layer1; struct fb_overlay_layer Layer2; struct fb_overlay_layer Layer3; }; struct fb_overlay_layer *layerInfo; int layerInfo_size = sizeof(struct fb_overlay_layer) * VIDEO_LAYER_COUNT; int32_t i; struct disp_input_config *input; int layer_num; DISPMSG(" %s():MTKFB_SET_VIDEO_LAYERS\n", __func__); layerInfo = kmalloc(layerInfo_size, GFP_KERNEL); if (!layerInfo) return -ENOMEM; if (copy_from_user(layerInfo, (void __user *)arg, layerInfo_size)) { MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); kfree(layerInfo); return -EFAULT; } memset((void *)&session_input, 0, sizeof(session_input)); for (i = 0; i < VIDEO_LAYER_COUNT; ++i) { if (layerInfo[i].layer_id >= TOTAL_OVL_LAYER_NUM) { DISP_PR_INFO("cmd(0x%08x:layer_id invalid=%d\n", cmd, layerInfo[i].layer_id); continue; } layer_num = session_input.config_layer_num; input = &session_input.config[layer_num]; session_input.config_layer_num++; _convert_fb_layer_to_disp_input(&layerInfo[i], input); } session_input.setter = SESSION_USER_PANDISP; primary_display_config_input_multiple(&session_input); primary_display_trigger(1, NULL, 0); kfree(layerInfo); return ret; } case MTKFB_TRIG_OVERLAY_OUT: { DISPMSG(" %s():MTKFB_TRIG_OVERLAY_OUT\n", __func__); primary_display_trigger(1, NULL, 0); return 0; } case MTKFB_META_RESTORE_SCREEN: { struct fb_var_screeninfo var; if (copy_from_user(&var, argp, sizeof(var))) return -EFAULT; /* invalidate params from userspace */ if (var.xres > MTK_FB_XRES || var.yres > MTK_FB_YRES || var.xres_virtual > MTK_FB_XRESV || var.yres_virtual > MTK_FB_YRESV || var.xoffset > MTK_FB_XRES || var.yoffset > MTK_FB_YRESV * (MTK_FB_PAGES - 1)) return -EFAULT; info->var.yoffset = var.yoffset; /* check var.yoffset passed by user space */ if (info->var.yres + info->var.yoffset > info->var.yres_virtual) info->var.yoffset = info->var.yres_virtual - info->var.yres; init_framebuffer(info); return mtkfb_pan_display_impl(&var, info); } case MTKFB_GET_DEFAULT_UPDATESPEED: { unsigned int speed = 0; MTKFB_LOG("[MTKFB] get default update speed\n"); DISPMSG("[MTKFB EM]MTKFB_GET_DEFAULT_UPDATESPEED is %d\n", speed); return copy_to_user(argp, &speed, sizeof(speed)) ? -EFAULT : 0; } case MTKFB_GET_CURR_UPDATESPEED: { unsigned int speed = 0; MTKFB_LOG("[MTKFB] get current update speed\n"); DISPMSG("[MTKFB EM]MTKFB_GET_CURR_UPDATESPEED is %d\n", speed); return copy_to_user(argp, &speed, sizeof(speed)) ? -EFAULT : 0; } case MTKFB_CHANGE_UPDATESPEED: { unsigned int speed = 0; MTKFB_LOG("[MTKFB] change update speed\n"); if (copy_from_user(&speed, (void __user *)arg, sizeof(speed))) { MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); ret = -EFAULT; } else { DISPMSG("[MTKFB EM]MTKFB_CHANGE_UPDATESPEED is %d\n", speed); } return ret; } case MTKFB_AEE_LAYER_EXIST: { int dal_en = is_DAL_Enabled(); return copy_to_user(argp, &dal_en, sizeof(dal_en)) ? -EFAULT : 0; } case MTKFB_LOCK_FRONT_BUFFER: return 0; case MTKFB_UNLOCK_FRONT_BUFFER: return 0; case MTKFB_FACTORY_AUTO_TEST: { unsigned int result = 0; DISPMSG("factory mode: lcm auto test\n"); result = mtkfb_fm_auto_test(); return copy_to_user(argp, &result, sizeof(result)) ? -EFAULT : 0; } case MTKFB_META_SHOW_BOOTLOGO: { int i, layer_num; struct mtkfb_device *fbdev = (struct mtkfb_device *) mtkfb_fbi->par; struct disp_input_config *input; DISPMSG("MTKFB_META_SHOW_BOOTLOGO\n"); memset((void *)&session_input, 0, sizeof(session_input)); for (i = 0; i < 2; i++) { layer_num = session_input.config_layer_num; input = &session_input.config[layer_num]; session_input.config_layer_num++; input->layer_enable = 1; input->src_fmt = DISP_FORMAT_RGBA8888; input->src_offset_x = 0; input->src_offset_y = 0; input->src_width = MTK_FB_XRES; input->src_height = MTK_FB_YRES; input->tgt_offset_x = 0; input->tgt_offset_y = 0; input->tgt_width = MTK_FB_XRES; input->tgt_height = MTK_FB_YRES; input->src_pitch = ALIGN_TO(MTK_FB_XRES, MTK_FB_ALIGNMENT) * 4; input->alpha_enable = 1; input->alpha = 0xff; input->next_buff_idx = -1; } input = &session_input.config[0]; input->layer_id = 0; input->src_phy_addr = (unsigned long) fbdev->fb_pa_base; input = &session_input.config[1]; input->layer_id = 3; input->src_phy_addr = (unsigned long)fbdev->fb_pa_base + (ALIGN_TO(MTK_FB_XRES, MTK_FB_ALIGNMENT) * ALIGN_TO(MTK_FB_YRES, MTK_FB_ALIGNMENT) * 4); session_input.setter = SESSION_USER_PANDISP; primary_display_config_input_multiple(&session_input); primary_display_trigger(1, NULL, 0); return 0; } default: DISP_PR_INFO("%s:Not support:info=%p,cmd=0x%08x,arg=0x%08lx\n", __func__, info, (unsigned int)cmd, arg); return -EINVAL; } return ret; } #ifdef CONFIG_COMPAT static void compat_convert(struct compat_fb_overlay_layer *compat_info, struct fb_overlay_layer *info) { info->layer_id = compat_info->layer_id; info->layer_enable = compat_info->layer_enable; info->src_base_addr = (void *)((unsigned long) (compat_info->src_base_addr)); info->src_phy_addr = (void *)((unsigned long) (compat_info->src_phy_addr)); info->src_direct_link = compat_info->src_direct_link; info->src_fmt = compat_info->src_fmt; info->src_use_color_key = compat_info->src_use_color_key; info->src_color_key = compat_info->src_color_key; info->src_pitch = compat_info->src_pitch; info->src_offset_x = compat_info->src_offset_x; info->src_offset_y = compat_info->src_offset_y; info->src_width = compat_info->src_width; info->src_height = compat_info->src_height; info->tgt_offset_x = compat_info->tgt_offset_x; info->tgt_offset_y = compat_info->tgt_offset_y; info->tgt_width = compat_info->tgt_width; info->tgt_height = compat_info->tgt_height; info->layer_rotation = compat_info->layer_rotation; info->layer_type = compat_info->layer_type; info->video_rotation = compat_info->video_rotation; info->isTdshp = compat_info->isTdshp; info->next_buff_idx = compat_info->next_buff_idx; info->identity = compat_info->identity; info->connected_type = compat_info->connected_type; info->security = compat_info->security; info->alpha_enable = compat_info->alpha_enable; info->alpha = compat_info->alpha; info->fence_fd = compat_info->fence_fd; info->ion_fd = compat_info->ion_fd; } static int mtkfb_compat_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { struct fb_overlay_layer layerInfo; long ret = 0; DISPDBG("[FB Driver] %s: cmd=0x%08x, cmd nr=0x%08x, cmd size=0x%08x\n", __func__, cmd, (unsigned int)_IOC_NR(cmd), (unsigned int)_IOC_SIZE(cmd)); switch (cmd) { case COMPAT_MTKFB_GET_FRAMEBUFFER_MVA: { compat_uint_t __user *data32; __u32 data; data32 = compat_ptr(arg); data = (__u32)fb_mva; if (put_user(data, data32)) { pr_err("MTKFB_FRAMEBUFFER_MVA failed\n"); ret = -EFAULT; } DISPDBG("MTKFB_FRAMEBUFFER_MVA success 0x%lx\n", fb_mva); return ret; } case COMPAT_MTKFB_GET_DISPLAY_IF_INFORMATION: { compat_uint_t __user *data32; compat_uint_t displayid = 0; data32 = compat_ptr(arg); if (get_user(displayid, data32)) { pr_err("cmd(0x%08x) failed\n", cmd); return -EFAULT; } if (displayid >= MTKFB_MAX_DISPLAY_COUNT) { pr_err("[FB]: invalid display id:%d\n", displayid); return -EFAULT; } if (displayid == 0) { dispif_info[displayid].displayWidth = primary_display_get_width(); dispif_info[displayid].displayHeight = primary_display_get_height(); dispif_info[displayid].lcmOriginalWidth = primary_display_get_original_width(); dispif_info[displayid].lcmOriginalHeight = primary_display_get_original_height(); dispif_info[displayid].displayMode = primary_display_is_video_mode() ? 0 : 1; } else { DISP_PR_INFO("info for displayid:%d is not available\n", displayid); } if (copy_to_user((void __user *)arg, &(dispif_info[displayid]), sizeof(struct compat_mtk_dispif_info))) { pr_err("[FB]: copy_to_user failed! line:%d\n", __LINE__); return -EFAULT; } break; } case COMPAT_MTKFB_POWEROFF: { ret = mtkfb_ioctl(info, MTKFB_POWEROFF, arg); break; } case COMPAT_MTKFB_POWERON: { ret = mtkfb_ioctl(info, MTKFB_POWERON, arg); break; } case COMPAT_MTKFB_GET_POWERSTATE: { compat_uint_t __user *data32; int power_state = 0; data32 = compat_ptr(arg); if (primary_display_is_sleepd()) power_state = 0; else power_state = 1; if (put_user(power_state, data32)) { pr_err("MTKFB_GET_POWERSTATE failed\n"); ret = -EFAULT; } DISPDBG("MTKFB_GET_POWERSTATE success %d\n", power_state); break; } case COMPAT_MTKFB_CAPTURE_FRAMEBUFFER: { compat_ulong_t __user *data32; unsigned long *pbuf; compat_ulong_t l; data32 = compat_ptr(arg); pbuf = compat_alloc_user_space(sizeof(unsigned long)); ret = get_user(l, data32); ret |= put_user(l, pbuf); ret = mtkfb_ioctl(info, MTKFB_CAPTURE_FRAMEBUFFER, (unsigned long)pbuf); break; } case COMPAT_MTKFB_TRIG_OVERLAY_OUT: { arg = (unsigned long)compat_ptr(arg); ret = mtkfb_ioctl(info, MTKFB_TRIG_OVERLAY_OUT, arg); break; } case COMPAT_MTKFB_META_RESTORE_SCREEN: { arg = (unsigned long)compat_ptr(arg); ret = mtkfb_ioctl(info, MTKFB_META_RESTORE_SCREEN, arg); break; } case COMPAT_MTKFB_SET_OVERLAY_LAYER: { struct compat_fb_overlay_layer *compat_layerInfo; compat_layerInfo = kmalloc(sizeof(*compat_layerInfo), GFP_KERNEL); if (!compat_layerInfo) return -ENOMEM; MTKFB_LOG(" %s():MTKFB_SET_OVERLAY_LAYER\n", __func__); arg = (unsigned long)compat_ptr(arg); if (copy_from_user(compat_layerInfo, (void __user *)arg, sizeof(*compat_layerInfo))) { MTKFB_LOG("[FB Driver]:copy_from_user failed!line:%d\n", __LINE__); ret = -EFAULT; } else { struct disp_input_config *input; int layer_num; compat_convert(compat_layerInfo, &layerInfo); /* * in early suspend mode ,will not update buffer index, * info SF by return value */ if (primary_display_is_sleepd()) { DISP_PR_INFO("set OVL in early suspend,skip\n"); kfree(compat_layerInfo); return MTKFB_ERROR_IS_EARLY_SUSPEND; } memset((void *)&session_input, 0, sizeof(session_input)); layer_num = session_input.config_layer_num; input = &session_input.config[layer_num]; session_input.config_layer_num++; session_input.setter = SESSION_USER_PANDISP; _convert_fb_layer_to_disp_input(&layerInfo, input); primary_display_config_input_multiple(&session_input); /* primary_display_trigger(1, NULL, 0); */ } kfree(compat_layerInfo); break; } case COMPAT_MTKFB_SET_VIDEO_LAYERS: { int32_t i; struct disp_input_config *input; struct compat_fb_overlay_layer *compat_layerInfo; int layer_num; int l_info_size = sizeof(struct compat_fb_overlay_layer) * VIDEO_LAYER_COUNT; compat_layerInfo = kmalloc(l_info_size, GFP_KERNEL); if (!compat_layerInfo) return -ENOMEM; MTKFB_LOG(" %s():MTKFB_SET_VIDEO_LAYERS\n", __func__); if (copy_from_user(compat_layerInfo, (void __user *)arg, l_info_size)) { MTKFB_LOG("[FB Driver]copy_from_user failed! line:%d\n", __LINE__); kfree(compat_layerInfo); return -EFAULT; } memset((void *)&session_input, 0, sizeof(session_input)); for (i = 0; i < VIDEO_LAYER_COUNT; ++i) { compat_convert(&compat_layerInfo[i], &layerInfo); layer_num = session_input.config_layer_num; input = &session_input.config[layer_num]; session_input.config_layer_num++; _convert_fb_layer_to_disp_input(&layerInfo, input); } session_input.setter = SESSION_USER_PANDISP; primary_display_config_input_multiple(&session_input); kfree(compat_layerInfo); break; } case COMPAT_MTKFB_AEE_LAYER_EXIST: { int dal_en = is_DAL_Enabled(); compat_ulong_t __user *data32; data32 = compat_ptr(arg); if (put_user(dal_en, data32)) { pr_err("MTKFB_GET_POWERSTATE failed\n"); ret = -EFAULT; } break; } case COMPAT_MTKFB_FACTORY_AUTO_TEST: { unsigned long result = 0; compat_ulong_t __user *data32; DISPMSG("factory mode: lcm auto test\n"); result = mtkfb_fm_auto_test(); data32 = compat_ptr(arg); if (put_user(result, data32)) { pr_err("MTKFB_GET_POWERSTATE failed\n"); ret = -EFAULT; } break; } case COMPAT_MTKFB_META_SHOW_BOOTLOGO: { arg = (unsigned long)compat_ptr(arg); ret = mtkfb_ioctl(info, MTKFB_META_SHOW_BOOTLOGO, arg); break; } default: /* NOTHING DIFFERENCE with standard ioctl calling */ arg = (unsigned long)compat_ptr(arg); ret = mtkfb_ioctl(info, cmd, arg); break; } return ret; } #endif static int mtkfb_pan_display_proxy(struct fb_var_screeninfo *var, struct fb_info *info) { #ifdef CONFIG_MTPROF_APPLAUNCH /* eng enable, user disable */ LOG_PRINT(ANDROID_LOG_INFO, "AppLaunch", "mtkfb_pan_display_proxy.\n"); #endif return mtkfb_pan_display_impl(var, info); } #ifdef CONFIG_MTK_IOMMU_V2 static int mtkfb_mmap(struct fb_info *info, struct vm_area_struct *vma); #endif /* * Callback table for the frame buffer framework. Some of these pointers * will be changed according to the current setting of fb_info->accel_flags. */ static struct fb_ops mtkfb_ops = { .owner = THIS_MODULE, .fb_open = mtkfb_open, .fb_release = mtkfb_release, .fb_pan_display = mtkfb_pan_display_proxy, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, .fb_cursor = mtkfb_soft_cursor, .fb_check_var = mtkfb_check_var, .fb_set_par = mtkfb_set_par, .fb_ioctl = mtkfb_ioctl, #ifdef CONFIG_MTK_IOMMU_V2 .fb_mmap = mtkfb_mmap, #endif #ifdef CONFIG_COMPAT .fb_compat_ioctl = mtkfb_compat_ioctl, #endif .fb_blank = mtkfb_blank, }; #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) /* external_display fb */ static struct fb_ops mtkfb1_ops = { .owner = THIS_MODULE, .fb_open = NULL, .fb_release = NULL, .fb_setcolreg = NULL, .fb_pan_display = NULL, .fb_fillrect = NULL, .fb_copyarea = NULL, .fb_imageblit = NULL, .fb_cursor = NULL, .fb_check_var = NULL, .fb_set_par = NULL, .fb_ioctl = NULL, #ifdef CONFIG_COMPAT .fb_compat_ioctl = NULL, #endif .fb_blank = mtkfb1_blank, }; #endif /* * --------------------------------------------------------------- * Sysfs interface * --------------------------------------------------------------- */ static int mtkfb_register_sysfs(struct mtkfb_device *fbdev) { NOT_REFERENCED(fbdev); return 0; } static void mtkfb_unregister_sysfs(struct mtkfb_device *fbdev) { NOT_REFERENCED(fbdev); } /* * ---------------------------------------------------------------- * LDM callbacks * ---------------------------------------------------------------- */ /* Initialize system fb_info object and set the default video mode. * The frame buffer memory already allocated by lcddma_init */ static int mtkfb_fbinfo_init(struct fb_info *info) { struct mtkfb_device *fbdev = (struct mtkfb_device *)info->par; struct fb_var_screeninfo var; int ret = 0; DISPFUNC(); ASSERT(fbdev->fb_va_base); info->fbops = &mtkfb_ops; info->flags = FBINFO_FLAG_DEFAULT; info->screen_base = (unsigned long)(fbdev->fb_va_base); info->screen_size = fbdev->fb_size_in_byte; info->pseudo_palette = fbdev->pseudo_palette; ret = fb_alloc_cmap(&info->cmap, 32, 0); if (ret) DISP_PR_ERR("unable to allocate color map memory\n"); /* setup the initial video mode (RGB565) */ memset(&var, 0, sizeof(var)); var.xres = MTK_FB_XRES; var.yres = MTK_FB_YRES; var.xres_virtual = MTK_FB_XRESV; var.yres_virtual = MTK_FB_YRESV; DISPMSG("%s var.(x,y)res=%d,%d,var.(x,y)res_virtual=%d,%d\n", __func__, var.xres, var.yres, var.xres_virtual, var.yres_virtual); /* use 32 bit framebuffer as default */ var.bits_per_pixel = 32; var.transp.offset = 24; var.red.length = 8; var.red.offset = 0; var.red.length = 8; var.green.offset = 8; var.green.length = 8; var.blue.offset = 16; var.blue.length = 8; var.width = DISP_GetActiveWidth(); var.height = DISP_GetActiveHeight(); var.activate = FB_ACTIVATE_NOW; ret = mtkfb_check_var(&var, info); if (ret) DISP_PR_ERR("failed to mtkfb_check_var\n"); info->var = var; ret = mtkfb_set_par(info); if (ret) DISP_PR_ERR("failed to mtkfb_set_par\n"); MSG_FUNC_LEAVE(); return ret; } /* Release the fb_info object */ static void mtkfb_fbinfo_cleanup(struct mtkfb_device *fbdev) { MSG_FUNC_ENTER(); fb_dealloc_cmap(&fbdev->fb_info->cmap); MSG_FUNC_LEAVE(); } /* Init frame buffer content as 3 R/G/B color bars for debug */ static int init_framebuffer(struct fb_info *info) { void *buffer; int size; struct fb_var_screeninfo *var = &info->var; buffer = (unsigned long)info->screen_base + var->yoffset * info->fix.line_length; size = var->xres_virtual * var->yres * var->bits_per_pixel / 8; memset_io(buffer, 0, size); return 0; } /** * Free driver resources. Can be called to rollback an aborted initialization * sequence. */ static void mtkfb_free_resources(struct mtkfb_device *fbdev, int state) { int ret = 0; switch (state) { case MTKFB_ACTIVE: ret = unregister_framebuffer(fbdev->fb_info); ASSERT(ret == 0); /* lint -fallthrough */ case 5: mtkfb_unregister_sysfs(fbdev); /* lint -fallthrough */ case 4: mtkfb_fbinfo_cleanup(fbdev); /* lint -fallthrough */ case 3: /* DISP_CHECK_RET(DISP_Deinit()); */ /* lint -fallthrough */ case 2: #ifndef CONFIG_FPGA_EARLY_PORTING dma_free_coherent(0, fbdev->fb_size_in_byte, fbdev->fb_va_base, fbdev->fb_pa_base); #endif /* lint -fallthrough */ case 1: dev_set_drvdata(fbdev->dev, NULL); framebuffer_release(fbdev->fb_info); /* lint -fallthrough */ case 0: /* nothing to free */ break; default: DISP_PR_ERR("free resources fail, state=%d\n", state); break; } } void disp_get_fb_address(unsigned long *fbVirAddr, unsigned long *fbPhysAddr) { struct mtkfb_device *fbdev = (struct mtkfb_device *)mtkfb_fbi->par; int fb_size; fb_size = mtkfb_fbi->var.yoffset * mtkfb_fbi->fix.line_length; *fbVirAddr = (unsigned long)fbdev->fb_va_base + fb_size; *fbPhysAddr = (unsigned long)fbdev->fb_pa_base + fb_size; } static void _mtkfb_draw_block(unsigned long addr, unsigned int x, unsigned int y, unsigned int w, unsigned int h, unsigned int color) { int i = 0; int j = 0; unsigned long start_addr = addr + MTK_FB_XRESV * 4 * y + x * 4; for (j = 0; j < h; j++) { for (i = 0; i < w; i++) writel(color, (start_addr + i * 4 + j * MTK_FB_XRESV * 4)); } } char *mtkfb_find_lcm_driver(void) { _parse_tag_videolfb(); DISPMSG("%s, %s\n", __func__, mtkfb_lcm_name); return mtkfb_lcm_name; } int _mtkfb_internal_test(unsigned long va, unsigned int w, unsigned int h) { /* this is for debug, used in bring up day */ unsigned int i = 0; unsigned int color = 0; int block_sz = 120; for (i = 0; i < w * h / block_sz / block_sz; i++) { color = (i & 0x1) * 0xff; #if defined(DISP_TEST_PATTERN) color += ((i & 0x2) >> 1) * 0xff00; color += ((i & 0x4) >> 2) * 0xff0000; #endif color += 0xff000000U; /* color = 0xff000000U; */ _mtkfb_draw_block(va, i % (w / block_sz) * block_sz, i / (w / block_sz) * block_sz, block_sz, block_sz, color); } primary_display_trigger(1, NULL, 0); return 0; } #ifdef CONFIG_OF struct tag_videolfb { u64 fb_base; u32 islcmfound; u32 fps; u32 vram; char lcmname[1]; /* this is the minimum size */ }; #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) struct tag_ext_videolfb { u64 ext_fb_base; u32 ext_islcmfound; u32 ext_fps; u32 ext_vram; char ext_lcmname[1]; /* this is the minimum size */ }; #endif unsigned int islcmconnected; unsigned int is_lcm_inited; unsigned int vramsize; phys_addr_t fb_base; static int is_videofb_parse_done; #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) unsigned int ext_islcmconnected; unsigned int ext_is_lcm_inited; unsigned int ext_vramsize; phys_addr_t ext_fb_base; #endif static int __parse_tag_videolfb_extra(struct device_node *node) { void *prop; unsigned long size = 0; u32 fb_base_h, fb_base_l; prop = (void *)of_get_property(node, "atag,videolfb-fb_base_h", NULL); if (!prop) return -1; fb_base_h = of_read_number(prop, 1); prop = (void *)of_get_property(node, "atag,videolfb-fb_base_l", NULL); if (!prop) return -1; fb_base_l = of_read_number(prop, 1); fb_base = ((u64) fb_base_h << 32) | (u64) fb_base_l; prop = (void *)of_get_property(node, "atag,videolfb-islcmfound", NULL); if (!prop) return -1; islcmconnected = of_read_number(prop, 1); prop = (void *)of_get_property(node, "atag,videolfb-islcm_inited", NULL); if (!prop) is_lcm_inited = 1; else is_lcm_inited = of_read_number(prop, 1); prop = (void *)of_get_property(node, "atag,videolfb-fps", NULL); if (!prop) return -1; lcd_fps = of_read_number(prop, 1); if (lcd_fps == 0) lcd_fps = 6000; prop = (void *)of_get_property(node, "atag,videolfb-vramSize", NULL); if (!prop) return -1; vramsize = of_read_number(prop, 1); prop = (void *)of_get_property(node, "atag,videolfb-fb_base_l", NULL); if (!prop) return -1; fb_base_l = of_read_number(prop, 1); prop = (void *)of_get_property(node, "atag,videolfb-lcmname", (int *)&size); if (!prop) return -1; if (size >= sizeof(mtkfb_lcm_name)) { DISPCHECK("%s: error to get lcmname size=%ld\n", __func__, size); return -1; } memset((void *)mtkfb_lcm_name, 0, sizeof(mtkfb_lcm_name)); strncpy((char *)mtkfb_lcm_name, prop, sizeof(mtkfb_lcm_name)); mtkfb_lcm_name[size] = '\0'; DISPMSG("%s done\n", __func__); return 0; } int __parse_tag_videolfb(struct device_node *node) { struct tag_videolfb *videolfb_tag = NULL; unsigned long size = 0; videolfb_tag = (struct tag_videolfb *) of_get_property(node, "atag,videolfb", (int *)&size); if (videolfb_tag) { memset((void *)mtkfb_lcm_name, 0, sizeof(mtkfb_lcm_name)); strncpy((char *)mtkfb_lcm_name, videolfb_tag->lcmname, sizeof(mtkfb_lcm_name)); mtkfb_lcm_name[strlen(videolfb_tag->lcmname)] = '\0'; lcd_fps = videolfb_tag->fps; if (lcd_fps == 0) lcd_fps = 6000; islcmconnected = videolfb_tag->islcmfound; vramsize = videolfb_tag->vram; fb_base = videolfb_tag->fb_base; is_lcm_inited = 1; return 0; } DISPCHECK("[DT][videolfb] videolfb_tag not found\n"); return -1; } #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) int __init __parse_tag_ext_videolfb(unsigned long node) { struct tag_ext_videolfb *tag_ext_videolfb = NULL; unsigned long size = 0; char *lcm_name; tag_ext_videolfb = (struct tag_ext_videolfb *) of_get_flat_dt_prop(node, "atag,ext_videolfb", (int *)&size); if (tag_ext_videolfb) { lcm_name = tag_ext_videolfb->ext_lcmname; memset((void *)ext_mtkfb_lcm_name, 0, sizeof(ext_mtkfb_lcm_name)); strcpy((char *)ext_mtkfb_lcm_name, lcm_name); ext_mtkfb_lcm_name[strlen(lcm_name)] = '\0'; ext_lcd_fps = tag_ext_videolfb->ext_fps; if (ext_lcd_fps == 0) ext_lcd_fps = 6000; ext_islcmconnected = tag_ext_videolfb->ext_islcmfound; ext_vramsize = tag_ext_videolfb->ext_vram; ext_fb_base = tag_ext_videolfb->ext_fb_base; ext_is_lcm_inited = 1; return 0; } DISPCHECK("[DT][ext_videolfb] tag_ext_videolfb not found\n"); return -1; } #endif static int _parse_tag_videolfb(void) { int ret; struct device_node *chosen_node; DISPCHECK("[DT][videolfb]isvideofb_parse_done = %d\n", is_videofb_parse_done); if (is_videofb_parse_done) return 0; chosen_node = of_find_node_by_path("/chosen"); if (!chosen_node) chosen_node = of_find_node_by_path("/chosen@0"); if (chosen_node) { ret = __parse_tag_videolfb(chosen_node); #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) ret = __parse_tag_ext_videolfb(chosen_node); #endif if (!ret) goto found; ret = __parse_tag_videolfb_extra(chosen_node); if (!ret) goto found; } else { DISPCHECK("[DT][videolfb] of_chosen not found\n"); } return -1; found: is_videofb_parse_done = 1; DISPCHECK("[DT][videolfb] islcmfound = %d\n", islcmconnected); DISPCHECK("[DT][videolfb] is_lcm_inited = %d\n", is_lcm_inited); DISPCHECK("[DT][videolfb] fps = %d\n", lcd_fps); DISPCHECK("[DT][videolfb] fb_base = 0x%lx\n", (unsigned long)fb_base); DISPCHECK("[DT][videolfb] vram = 0x%x (%d)\n", vramsize, vramsize); DISPCHECK("[DT][videolfb] lcmname = %s\n", mtkfb_lcm_name); #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) DISPCHECK("[DT][videolfb] ext_islcmfound = %d\n", ext_islcmconnected); DISPCHECK("[DT][videolfb] ext_is_lcm_inited = %d\n", ext_is_lcm_inited); DISPCHECK("[DT][videolfb] ext_fps = %d\n", ext_lcd_fps); DISPCHECK("[DT][videolfb] ext_fb_base = 0x%lx\n", (unsigned long)ext_fb_base); DISPCHECK("[DT][videolfb] ext_vram = 0x%x (%d)\n", ext_vramsize, ext_vramsize); DISPCHECK("[DT][videolfb] ext_lcmname = %s\n", ext_mtkfb_lcm_name); #endif return 0; } phys_addr_t mtkfb_get_fb_base(void) { _parse_tag_videolfb(); return fb_base; } EXPORT_SYMBOL(mtkfb_get_fb_base); size_t mtkfb_get_fb_size(void) { _parse_tag_videolfb(); return vramsize; } EXPORT_SYMBOL(mtkfb_get_fb_size); #endif /* used when early porting, test pan display */ int pan_display_test(int frame_num, int bpp) { int i, j; int Bpp = bpp / 8; unsigned char *fb_va; unsigned long fb_pa; unsigned int fb_size; int w, h, fb_h; int yoffset_max; int yoffset; mtkfb_fbi->var.yoffset = 0; disp_get_fb_address((unsigned long *)&fb_va, &fb_pa); fb_size = mtkfb_fbi->fix.smem_len; w = mtkfb_fbi->var.xres; h = mtkfb_fbi->var.yres; fb_h = fb_size / (w * Bpp) - 10; DISPMSG("%s: frame_num=%d,bpp=%d, w=%d,h=%d,fb_h=%d\n", __func__, frame_num, bpp, w, h, fb_h); for (i = 0; i < fb_h; i++) for (j = 0; j < w; j++) { int x = (i * w + j) * Bpp; fb_va[x++] = (i + j) % 256; fb_va[x++] = (i + j) % 256; fb_va[x++] = (i + j) % 256; if (Bpp == 4) fb_va[x++] = 255; } mtkfb_fbi->var.bits_per_pixel = bpp; yoffset_max = fb_h - h; yoffset = 0; for (i = 0; i < frame_num; i++, yoffset += 10) { if (yoffset >= yoffset_max) yoffset = 0; mtkfb_fbi->var.xoffset = 0; mtkfb_fbi->var.yoffset = yoffset; mtkfb_pan_display_impl(&mtkfb_fbi->var, mtkfb_fbi); } return 0; } /* #define FPGA_DEBUG_PAN */ #ifdef FPGA_DEBUG_PAN static struct task_struct *test_task; static int update_test_kthread(void *data) { unsigned int i = 0, j = 0; unsigned long fb_va; unsigned long fb_pa; unsigned int *fb_start; unsigned int fbsize = primary_display_get_height() * primary_display_get_width(); mtkfb_fbi->var.yoffset = 0; disp_get_fb_address(&fb_va, &fb_pa); for (;;) { if (kthread_should_stop()) break; msleep(1000); /* 2s */ DISPMSG("update test thread work,offset = %d\n", i); mtkfb_fbi->var.yoffset = 0; disp_get_fb_address(&fb_va, &fb_pa); fb_start = (unsigned int *)fb_va; for (j = 0; j < fbsize; j++) { *fb_start = (0x55) << ((i % 4) * 8); fb_start++; } mtkfb_pan_display_impl(&mtkfb_fbi->var, mtkfb_fbi); i++; } DISPMSG("exit %s()\n", __func__); return 0; } #endif #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) static struct fb_info *allocate_fb_by_index(struct device *dev) { struct mtkfb_device *fbdev = NULL; struct fb_info *fb_dev = NULL; fb_dev = framebuffer_alloc(sizeof(struct mtkfb_device), dev); if (!fb_dev) { DISP_PR_ERR("unable to allocate memory for device info\n"); return NULL; } fbdev = (struct mtkfb_device *)fb_dev->par; fbdev->fb_info = fb_dev; fbdev->dev = dev; /* dev_set_drvdata(dev, fbdev); */ fb_dev->fbops = &mtkfb1_ops; fb_dev->flags = FBINFO_FLAG_DEFAULT; return fb_dev; } #endif static int mtkfb_probe(struct platform_device *pdev) { struct mtkfb_device *fbdev = NULL; struct fb_info *fbi; #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) /* external display fb */ struct fb_info *fb1; #endif int init_state; int ret = 0; size_t temp_va = 0; /* struct platform_device *pdev; */ long dts_gpio_state = 0; DISPMSG("%s name [%s] = [%s][%p]\n", __func__, pdev->name, pdev->dev.init_name, (void *)&pdev->dev); #ifdef CONFIG_MTK_SMI_EXT if (!smi_mm_first_get()) { DISPMSG("SMI not start probe\n"); return -EPROBE_DEFER; } #endif _parse_tag_videolfb(); init_state = 0; /* pdev = to_platform_device(dev); */ /* repo call DTS gpio module, if not necessary, invoke nothing */ dts_gpio_state = disp_dts_gpio_init_repo(pdev); if (dts_gpio_state) dev_err(&pdev->dev, "retrieve GPIO DTS failed."); fbi = framebuffer_alloc(sizeof(struct mtkfb_device), &(pdev->dev)); if (!fbi) { DISP_PR_ERR("unable to allocate memory for device info\n"); ret = -ENOMEM; goto cleanup; } mtkfb_fbi = fbi; fbdev = (struct mtkfb_device *)fbi->par; fbdev->fb_info = fbi; fbdev->dev = &(pdev->dev); dev_set_drvdata(&(pdev->dev), fbdev); mtk_disp_mgr_set_dev(&(pdev->dev)); #ifdef CONFIG_MTK_IOMMU_V2 disp_aosp_set_dev(&(pdev->dev)); #endif DISPMSG("%s: fb_pa = %pa\n", __func__, &fb_base); temp_va = (size_t)ioremap_nocache(fb_base, vramsize); fbdev->fb_va_base = (void *)temp_va; disp_hal_allocate_framebuffer(fb_base, (fb_base + vramsize - 1), (unsigned long *)(&fbdev->fb_va_base), &fb_mva); fbdev->fb_pa_base = fb_base; primary_display_set_frame_buffer_address((unsigned long) (fbdev->fb_va_base), fb_mva, fb_base); primary_display_init(mtkfb_find_lcm_driver(), lcd_fps, is_lcm_inited); init_state++; /* 1 */ MTK_FB_XRES = DISP_GetScreenWidth(); MTK_FB_YRES = DISP_GetScreenHeight(); fb_xres_update = MTK_FB_XRES; fb_yres_update = MTK_FB_YRES; MTK_FB_BPP = DISP_GetScreenBpp(); MTK_FB_PAGES = DISP_GetPages(); DISPCHECK("MTK_FB_XRES=%d, MTKFB_YRES=%d, MTKFB_BPP=%d,\n", MTK_FB_XRES, MTK_FB_YRES, MTK_FB_BPP); DISPCHECK("MTK_FB_PAGES=%d, MTKFB_LINE=%d, MTKFB_SIZEV=%d\n", MTK_FB_PAGES, MTK_FB_LINE, MTK_FB_SIZEV); fbdev->fb_size_in_byte = MTK_FB_SIZEV; /* allocate and initialize video frame buffer */ DISPCHECK("[FB Driver] fb_pa_base=0x%lx, fb_va_base=0x%lx\n", (unsigned long)fbdev->fb_pa_base, (unsigned long)fbdev->fb_va_base); if (!fbdev->fb_va_base) { DISP_PR_ERR("unable to allocate memory for frame buffer\n"); ret = -ENOMEM; goto cleanup; } init_state++; /* 2 */ ret = mtkfb_fbinfo_init(fbi); if (ret) { DISP_PR_ERR("mtkfb_fbinfo_init fail, ret = %d\n", ret); goto cleanup; } init_state++; /* 4 */ DISPMSG("mtkfb_fbinfo_init done\n"); if (disp_helper_get_stage() == DISP_HELPER_STAGE_NORMAL) { /* * dal_init should after mtkfb_fbinfo_init, otherwise layer * 3 will show dal background color */ enum DAL_STATUS ret; unsigned long fbVA = (unsigned long)(fbdev->fb_va_base); unsigned long fbMVA = fb_mva; /* DAL init here */ fbVA += DISP_GetFBRamSize(); fbMVA += DISP_GetFBRamSize(); ret = DAL_Init(fbVA, fbMVA); DISPMSG("DAL_Init done\n"); } if (disp_helper_get_stage() != DISP_HELPER_STAGE_NORMAL) _mtkfb_internal_test((unsigned long)(fbdev->fb_va_base), MTK_FB_XRES, MTK_FB_YRES); ret = mtkfb_register_sysfs(fbdev); if (ret) { DISP_PR_ERR("mtkfb_register_sysfs fail, ret = %d\n", ret); goto cleanup; } init_state++; /* 5 */ DISPMSG("register_framebuffer start...\n"); ret = register_framebuffer(fbi); if (ret) { DISP_PR_ERR("register_framebuffer failed\n"); goto cleanup; } DISPMSG("register_framebuffer done\n"); #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) DISPDBG("%s register fb1\n", __func__); /* register external display fb */ fb1 = allocate_fb_by_index(&pdev->dev); register_framebuffer(fb1); DISPMSG("register_ext_framebuffer done\n"); #endif #ifdef FPGA_DEBUG_PAN test_task = kthread_create(update_test_kthread, NULL, "update_test_kthread"); wake_up_process(test_task); #endif if (disp_helper_get_stage() != DISP_HELPER_STAGE_NORMAL) primary_display_diagnose(); fbdev->state = MTKFB_ACTIVE; MSG_FUNC_LEAVE(); pr_info("disp driver(2) %s end\n", __func__); return 0; cleanup: mtkfb_free_resources(fbdev, init_state); pr_info("disp driver(3) %s end\n", __func__); return ret; } #ifdef CONFIG_MTK_IOMMU_V2 static int mtkfb_mmap(struct fb_info *info, struct vm_area_struct *vma) { int ret = -1; struct mtkfb_device *fbdev = (struct mtkfb_device *)mtkfb_fbi->par; ret = disp_aosp_mmap(vma, (unsigned long)fbdev->fb_va_base, fb_mva, vramsize); return ret; } #endif /* Called when the device is being detached from the driver */ static int mtkfb_remove(struct platform_device *pdev) { struct mtkfb_device *fbdev = dev_get_drvdata(&pdev->dev); enum mtkfb_state saved_state = fbdev->state; MSG_FUNC_ENTER(); /* FIXME: wait till completion of pending events */ fbdev->state = MTKFB_DISABLED; mtkfb_free_resources(fbdev, saved_state); MSG_FUNC_LEAVE(); return 0; } /* PM suspend */ static int mtkfb_suspend(struct platform_device *pdev, pm_message_t mesg) { DISPFUNC(); NOT_REFERENCED(pdev); MSG_FUNC_ENTER(); MTKFB_LOG("[FB Driver] %s(): 0x%x\n", __func__, mesg.event); ovl2mem_wait_done(); MSG_FUNC_LEAVE(); return 0; } bool mtkfb_is_suspend(void) { return primary_display_is_sleepd(); } EXPORT_SYMBOL(mtkfb_is_suspend); int mtkfb_ipoh_restore(struct notifier_block *nb, unsigned long val, void *ign) { switch (val) { case PM_HIBERNATION_PREPARE: DISPDBG("[FB Driver] %s PM_HIBERNATION_PREPARE\n", __func__); return NOTIFY_DONE; case PM_RESTORE_PREPARE: primary_display_ipoh_restore(); DISPDBG("[FB Driver] %s PM_RESTORE_PREPARE\n", __func__); return NOTIFY_DONE; case PM_POST_HIBERNATION: DISPDBG("[FB Driver] %s PM_POST_HIBERNATION\n", __func__); return NOTIFY_DONE; } return NOTIFY_OK; } int mtkfb_ipo_init(void) { pm_nb.notifier_call = mtkfb_ipoh_restore; pm_nb.priority = 0; register_pm_notifier(&pm_nb); return 0; } static void mtkfb_shutdown(struct platform_device *pdev) { MTKFB_LOG("[FB Driver] %s\n", __func__); /* mt65xx_leds_brightness_set(MT65XX_LED_TYPE_LCD, LED_OFF); */ if (!lcd_fps) msleep(30); else msleep(2 * 100000 / lcd_fps); /* Delay 2 frames. */ if (primary_display_is_sleepd()) { MTKFB_LOG("mtkfb has been power off\n"); return; } primary_display_set_power_mode(FB_SUSPEND); primary_display_suspend(); MTKFB_LOG("[FB Driver] leave %s\n", __func__); } void mtkfb_clear_lcm(void) { } static void mtkfb_early_suspend(void) { int ret = 0; if (disp_helper_get_stage() != DISP_HELPER_STAGE_NORMAL) return; DISPMSG("%s+\n", __func__); ret = primary_display_suspend(); if (ret < 0) { DISP_PR_ERR("primary display suspend failed\n"); return; } DISPMSG("%s-\n", __func__); } /* PM resume */ static int mtkfb_resume(struct platform_device *pdev) { NOT_REFERENCED(pdev); MSG_FUNC_ENTER(); MTKFB_LOG("[FB Driver] %s()\n", __func__); MSG_FUNC_LEAVE(); return 0; } static void mtkfb_late_resume(void) { int ret = 0; if (disp_helper_get_stage() != DISP_HELPER_STAGE_NORMAL) return; DISPMSG("%s+\n", __func__); ret = primary_display_resume(); if (ret) { DISP_PR_ERR("primary display resume failed\n"); return; } DISPMSG("%s-\n", __func__); } #ifdef CONFIG_PM int mtkfb_pm_suspend(struct device *device) { /* pr_debug("calling %s()\n", __func__); */ struct platform_device *pdev = to_platform_device(device); if (pdev == NULL) { disp_aee_db_print("pdev is NULL\n"); return -1; } return mtkfb_suspend(pdev, PMSG_SUSPEND); } int mtkfb_pm_resume(struct device *device) { /* pr_debug("calling %s()\n", __func__); */ struct platform_device *pdev = to_platform_device(device); if (pdev == NULL) { disp_aee_db_print("pdev is NULL\n"); return -1; } return mtkfb_resume(pdev); } int mtkfb_pm_freeze(struct device *device) { primary_display_esd_check_enable(0); return 0; } int mtkfb_pm_restore_noirq(struct device *device) { /* disphal_pm_restore_noirq(device); */ DISPDBG("%s: %d\n", __func__, __LINE__); is_ipoh_bootup = true; dpmgr_path_power_on(primary_get_dpmgr_handle(), CMDQ_DISABLE); DISPDBG("%s: %d\n", __func__, __LINE__); return 0; } #else /* !CONFIG_PM */ #define mtkfb_pm_suspend NULL #define mtkfb_pm_resume NULL #define mtkfb_pm_restore_noirq NULL #define mtkfb_pm_freeze NULL #endif /* CONFIG_PM */ static const struct of_device_id mtkfb_of_ids[] = { {.compatible = "mediatek,MTKFB",}, {} }; static const struct dev_pm_ops mtkfb_pm_ops = { .suspend = mtkfb_pm_suspend, .resume = mtkfb_pm_resume, .freeze = mtkfb_pm_freeze, .thaw = mtkfb_pm_resume, .poweroff = mtkfb_pm_suspend, .restore = mtkfb_pm_resume, .restore_noirq = mtkfb_pm_restore_noirq, }; static struct platform_driver mtkfb_driver = { .probe = mtkfb_probe, .remove = mtkfb_remove, .suspend = mtkfb_suspend, .resume = mtkfb_resume, .shutdown = mtkfb_shutdown, .driver = { .name = MTKFB_DRIVER, #ifdef CONFIG_PM .pm = &mtkfb_pm_ops, #endif .bus = &platform_bus_type, .of_match_table = mtkfb_of_ids, }, }; #ifdef CONFIG_HAS_EARLYSUSPEND static struct early_suspend mtkfb_early_suspend_handler = { .level = EARLY_SUSPEND_LEVEL_DISABLE_FB, .suspend = mtkfb_early_suspend, .resume = mtkfb_late_resume, }; #endif int mtkfb_get_debug_state(char *stringbuf, int buf_len) { int len = 0; struct mtkfb_device *fbdev = (struct mtkfb_device *)mtkfb_fbi->par; unsigned long va = (unsigned long)fbdev->fb_va_base; unsigned long mva = (unsigned long)fbdev->fb_pa_base; unsigned long pa = (unsigned long)fbdev->fb_pa_base; unsigned int resv_size = vramsize; len += scnprintf(stringbuf + len, buf_len - len, "|-----------------------------"); len += scnprintf(stringbuf + len, buf_len - len, "-------------------------------------|\n"); len += scnprintf(stringbuf + len, buf_len - len, "|Framebuffer VA:0x%lx, PA:0x%lx, MVA:0x%lx, ", va, pa, mva); len += scnprintf(stringbuf + len, buf_len - len, "Reserved Size:0x%08x|%d\n", resv_size, resv_size); len += scnprintf(stringbuf + len, buf_len - len, "|xoffset=%d, yoffset=%d\n", mtkfb_fbi->var.xoffset, mtkfb_fbi->var.yoffset); len += scnprintf(stringbuf + len, buf_len - len, "|framebuffer line alignment(for gpu)=%d\n", MTK_FB_ALIGNMENT); len += scnprintf(stringbuf + len, buf_len - len, "|xres=%d, yres=%d,bpp=%d,pages=%d,linebytes=%d,", MTK_FB_XRES, MTK_FB_YRES, MTK_FB_BPP, MTK_FB_PAGES, MTK_FB_LINE); len += scnprintf(stringbuf + len, buf_len - len, "total size=%d\n", MTK_FB_SIZEV); /* use extern in case DAL_LOCK is hold, then can't get any debug info */ len += scnprintf(stringbuf + len, buf_len - len, "|AEE Layer is %s\n", is_DAL_Enabled() ? "enabled" : "disabled"); return len; } /* Register both the driver and the device */ int __init mtkfb_init(void) { int ret = 0; MSG_FUNC_ENTER(); DISPCHECK("%s Enter\n", __func__); if (platform_driver_register(&mtkfb_driver)) { PRNERR("failed to register mtkfb driver\n"); ret = -ENODEV; goto exit; } #ifdef CONFIG_HAS_EARLYSUSPEND register_early_suspend(&mtkfb_early_suspend_handler); #endif PanelMaster_Init(); DBG_Init(); mtkfb_ipo_init(); exit: MSG_FUNC_LEAVE(); DISPCHECK("%s LEAVE\n", __func__); return ret; } static void __exit mtkfb_cleanup(void) { MSG_FUNC_ENTER(); platform_driver_unregister(&mtkfb_driver); #ifdef CONFIG_HAS_EARLYSUSPEND unregister_early_suspend(&mtkfb_early_suspend_handler); #endif PanelMaster_Deinit(); DBG_Deinit(); MSG_FUNC_LEAVE(); } module_init(mtkfb_init); module_exit(mtkfb_cleanup); MODULE_DESCRIPTION("MEDIATEK framebuffer driver"); MODULE_AUTHOR("Xuecheng Zhang "); MODULE_LICENSE("GPL");