| /* |
| * Copyright (C) 2015 MediaTek Inc. |
| * |
| * This program is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| /*#include <generated/autoconf.h>*/ |
| #include <linux/module.h> |
| #include <linux/mm.h> |
| #include <linux/init.h> |
| #include <linux/fb.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/platform_device.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/kthread.h> |
| #include <linux/vmalloc.h> |
| #include <linux/semaphore.h> |
| #include <linux/mutex.h> |
| #include <linux/suspend.h> |
| #include <linux/of_fdt.h> |
| #include <linux/of.h> |
| #include <linux/of_address.h> |
| #include <linux/dma-buf.h> |
| #include <linux/uaccess.h> |
| #include <linux/atomic.h> |
| #include <asm/cacheflush.h> |
| #include <linux/io.h> |
| #include <linux/of_platform.h> |
| |
| #include <linux/compat.h> |
| #include <linux/dma-mapping.h> |
| |
| /* #include <linux/earlysuspend.h> */ |
| /* #include <linux/rtpm_prio.h> */ |
| /* #include <linux/leds-mt65xx.h> */ |
| /* #include <asm/mach-types.h> */ |
| /* #include "mach/mt_boot.h" */ |
| /* #include <mach/irqs.h> */ |
| |
| #include <mt-plat/dma.h> |
| #include <mt-plat/mtk_boot_common.h> |
| #include "disp_assert_layer.h" |
| |
| #include "debug.h" |
| |
| #include "ddp_hal.h" |
| #include "ddp_log.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 "ddp_manager.h" |
| |
| #include "mtk_ovl.h" |
| #include "ion_drv.h" |
| #include "ddp_drv.h" |
| |
| #ifdef DISP_GPIO_DTS |
| #include "disp_dts_gpio.h" /* set gpio via DTS */ |
| #endif |
| #include "disp_helper.h" |
| #include "mtk_disp_mgr.h" |
| |
| #ifdef CONFIG_MTK_LEDS |
| #include "mtk_leds_drv.h" |
| #endif |
| |
| #define ALIGN_TO(x, n) (((x) + ((n) - 1)) & ~((n) - 1)) |
| |
| |
| /* xuecheng, remove this because we use session now */ |
| /* struct mtk_dispif_info dispif_info[MTKFB_MAX_DISPLAY_COUNT]; */ |
| |
| struct notifier_block pm_nb; |
| unsigned int EnableVSyncLog; |
| |
| 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; |
| |
| #define MTK_FB_XRESV (ALIGN_TO(MTK_FB_XRES, MTK_FB_ALIGNMENT)) |
| #define MTK_FB_YRESV (ALIGN_TO(MTK_FB_YRES, MTK_FB_ALIGNMENT) * MTK_FB_PAGES) /* For page flipping */ |
| #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 CHECK_RET(expr) \ |
| do { \ |
| int ret = (expr); \ |
| ASSERT(ret == 0); \ |
| } while (0) |
| |
| |
| static size_t mtkfb_log_on = true; |
| #define MTKFB_LOG(fmt, arg...) \ |
| do { \ |
| if (mtkfb_log_on) \ |
| pr_debug("DISP/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) \ |
| pr_debug("DISP/MTKFB " fmt, ##arg); \ |
| } while (0) |
| |
| #define MTKFB_FUNC() \ |
| do { \ |
| if (mtkfb_log_on) \ |
| pr_debug("DISP/MTKFB [Func]%s\n", __func__); \ |
| } while (0) |
| |
| #define PRNERR(fmt, args...) pr_debug("DISP/MTKFB " fmt, ##args) |
| |
| void mtkfb_log_enable(int enable) |
| { |
| mtkfb_log_on = enable; |
| MTKFB_LOG("mtkfb log %s\n", enable ? "enabled" : "disabled"); |
| } |
| |
| /* --------------------------------------------------------------------------- */ |
| /* local variables */ |
| /* --------------------------------------------------------------------------- */ |
| |
| unsigned long fb_pa; |
| |
| static const struct timeval FRAME_INTERVAL = { 0, 30000 }; /* 33ms */ |
| |
| atomic_t has_pending_update = ATOMIC_INIT(0); |
| struct fb_overlay_layer video_layerInfo; |
| uint32_t dbr_backup; |
| uint32_t dbg_backup; |
| uint32_t 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]; |
| |
| /** |
| * This mutex is used to prevent tearing due to page flipping when adbd is |
| * reading the front buffer |
| */ |
| DEFINE_SEMAPHORE(sem_flipping); |
| DEFINE_SEMAPHORE(sem_early_suspend); |
| DEFINE_SEMAPHORE(sem_overlay_buffer); |
| |
| DEFINE_MUTEX(OverlaySettingMutex); |
| atomic_t OverlaySettingDirtyFlag = ATOMIC_INIT(0); |
| atomic_t OverlaySettingApplied = ATOMIC_INIT(0); |
| unsigned int PanDispSettingPending; |
| unsigned int PanDispSettingDirty; |
| unsigned int PanDispSettingApplied; |
| |
| DECLARE_WAIT_QUEUE_HEAD(reg_update_wq); |
| |
| unsigned int need_esd_check; |
| DECLARE_WAIT_QUEUE_HEAD(esd_check_wq); |
| |
| /* extern unsigned int disp_running; */ |
| /* extern wait_queue_head_t disp_done_wq; */ |
| |
| DEFINE_MUTEX(ScreenCaptureMutex); |
| |
| bool is_early_suspended; |
| static int sem_flipping_cnt = 1; |
| static int sem_early_suspend_cnt = 1; |
| static int vsync_cnt; |
| |
| /* extern BOOL is_engine_in_suspend_mode; */ |
| /* extern BOOL is_lcm_in_suspend_mode; */ |
| |
| /* --------------------------------------------------------------------------- */ |
| /* local function declarations */ |
| /* --------------------------------------------------------------------------- */ |
| |
| static int init_framebuffer(struct fb_info *info); |
| static int mtkfb_get_overlay_layer_info(struct fb_overlay_layer_info *layerInfo); |
| |
| |
| /* --------------------------------------------------------------------------- */ |
| /* Timer Routines */ |
| /* --------------------------------------------------------------------------- */ |
| unsigned int lcd_fps = 6000; |
| wait_queue_head_t screen_update_wq; |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * mtkfb_set_lcm_inited() will be called in mt6516_board_init() |
| * --------------------------------------------------------------------------- |
| */ |
| bool is_lcm_inited; |
| void mtkfb_set_lcm_inited(bool inited) |
| { |
| is_lcm_inited = inited; |
| } |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * 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; |
| } |
| |
| /* |
| * Store a single color palette entry into a pseudo palette or the hardware |
| * palette if one is available. For now we support only 16bpp and thus store |
| * the entry only to the pseudo palette. |
| */ |
| static int mtkfb_setcolreg(u_int regno, u_int red, u_int green, |
| u_int blue, u_int transp, struct fb_info *info) |
| { |
| int r = 0; |
| unsigned bpp, m; |
| |
| /* NOT_REFERENCED(transp); */ |
| |
| MSG_FUNC_ENTER(); |
| |
| bpp = info->var.bits_per_pixel; |
| m = 1 << bpp; |
| if (regno >= m) { |
| r = -EINVAL; |
| goto exit; |
| } |
| |
| switch (bpp) { |
| case 16: |
| /* RGB 565 */ |
| ((u32 *) (info->pseudo_palette))[regno] = |
| ((red & 0xF800) | ((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11)); |
| break; |
| case 32: |
| /* ARGB8888 */ |
| ((u32 *) (info->pseudo_palette))[regno] = |
| (0xff000000) | |
| ((red & 0xFF00) << 8) | ((green & 0xFF00)) | ((blue & 0xFF00) >> 8); |
| break; |
| |
| /* TODO: RGB888, BGR888, ABGR8888 */ |
| |
| default: |
| ASSERT(0); |
| } |
| |
| exit: |
| MSG_FUNC_LEAVE(); |
| return r; |
| } |
| |
| int mtkfb_set_backlight_level(unsigned int level) |
| { |
| MTKFB_FUNC(); |
| pr_debug("mtkfb_set_backlight_level:%d Start\n", level); |
| primary_display_setbacklight(level); |
| pr_debug("mtkfb_set_backlight_level End\n"); |
| return 0; |
| } |
| EXPORT_SYMBOL(mtkfb_set_backlight_level); |
| |
| int mtkfb_set_backlight_mode(unsigned int mode) |
| { |
| MTKFB_FUNC(); |
| if (down_interruptible(&sem_flipping)) { |
| pr_debug("[FB Driver] can't get semaphore:%d\n", __LINE__); |
| return -ERESTARTSYS; |
| } |
| sem_flipping_cnt--; |
| if (down_interruptible(&sem_early_suspend)) { |
| pr_debug("[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)) { |
| pr_debug("[FB Driver] can't get semaphore:%d\n", __LINE__); |
| return -ERESTARTSYS; |
| } |
| sem_flipping_cnt--; |
| if (down_interruptible(&sem_early_suspend)) { |
| pr_debug("[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()) { |
| pr_debug("[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 mtkfb_set_par(struct fb_info *fbi); |
| |
| static bool no_update; |
| |
| static int _convert_fb_layer_to_disp_input(struct fb_overlay_layer *src, struct disp_input_config *dst) |
| { |
| dst->layer_id = src->layer_id; |
| |
| 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_BGRA8888: |
| dst->src_fmt = DISP_FORMAT_BGRA8888; |
| break; |
| |
| case MTK_FB_FORMAT_RGBA8888: |
| dst->src_fmt = DISP_FORMAT_RGBA8888; |
| 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: |
| DISPERR("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("_convert_fb_layer_to_disp_input, dst->addr=0x%08lx\n", |
| (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 (MTK_FB_FORMAT_ARGB8888 == src->src_fmt || MTK_FB_FORMAT_ABGR8888 == src->src_fmt || |
| MTK_FB_FORMAT_RGBA8888 == src->src_fmt || MTK_FB_FORMAT_BGRA8888 == src->src_fmt) |
| 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; |
| |
| #if 1 |
| DISPDBG("_convert_fb_layer_to_disp_input():id=%u, en=%u, next_idx=%u, vaddr=%p, paddr=%p,\n", |
| dst->layer_id, dst->layer_enable, dst->next_buff_idx, dst->src_base_addr, dst->src_phy_addr); |
| DISPDBG("src fmt=%u, dst fmt=%u, pitch=%u, xoff=%u, yoff=%u, w=%u, h=%u\n", |
| src->src_fmt, dst->src_fmt, dst->src_pitch, dst->src_offset_x, |
| dst->src_offset_y, dst->src_width, dst->src_height); |
| DISPDBG("_convert_fb_layer_to_disp_input():target xoff=%u, target yoff=%u, target w=%u, target h=%u, aen=%u\n", |
| dst->tgt_offset_x, dst->tgt_offset_y, dst->tgt_width, dst->tgt_height, |
| dst->alpha_enable); |
| #endif |
| |
| return 0; |
| } |
| |
| static int mtkfb_pan_display_impl(struct fb_var_screeninfo *var, struct fb_info *info) |
| { |
| uint32_t offset = 0; |
| uint32_t paStart = 0; |
| char *vaStart = NULL, *vaEnd = NULL; |
| int ret = 0; |
| unsigned int src_pitch = 0; |
| static unsigned int pan_display_cnt; |
| struct disp_session_input_config *session_input = captured_session_input; |
| struct disp_input_config *input; |
| |
| DISPFUNC(); |
| |
| if (no_update) { |
| DISPMSG("FB_ACTIVATE_NO_UPDATE flag found, ignore mtkfb_pan_display_impl\n"); |
| no_update = false; |
| return ret; |
| } |
| |
| DDPMLOG("pan_display: offset(%u,%u), res(%u,%u), resv(%u,%u), cnt=%d.\n", |
| var->xoffset, var->yoffset, info->var.xres, info->var.yres, info->var.xres_virtual, |
| info->var.yres_virtual, pan_display_cnt++); |
| |
| info->var.yoffset = var->yoffset; |
| offset = var->yoffset * info->fix.line_length; |
| paStart = fb_pa + offset; |
| vaStart = info->screen_base + offset; |
| vaEnd = vaStart + info->var.yres * info->fix.line_length; |
| |
| /* pan display use layer 0 */ |
| input = &session_input->config[0]; |
| input->layer_id = 0; |
| input->src_phy_addr = (void *)((unsigned long)paStart); |
| input->src_base_addr = (void *)((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; |
| |
| 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: |
| DISPERR("Invalid color format bpp: %d\n", var->bits_per_pixel); |
| 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; |
| |
| session_input->config_layer_num = 1; |
| |
| 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 = 2; |
| } |
| |
| ret = primary_display_config_input_multiple(session_input); |
| ret = primary_display_trigger(true, NULL, 0); |
| /* primary_display_diagnose(); */ |
| |
| #ifdef XXXX_TODO |
| #error "need to wait rdma0 done here" |
| #error "aee dynamic switch, set overlay race condition protection" |
| #endif |
| |
| 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: |
| ASSERT(0); |
| } |
| |
| 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 = 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(); */ |
| |
| DISPDBG("mtkfb_check_var, xres=%u, yres=%u, xres_virtual=%u, yres_virtual=%u,\n", |
| var->xres, var->yres, var->xres_virtual, var->yres_virtual); |
| DISPDBG("xoffset=%u, yoffset=%u, bits_per_pixel=%u\n", 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("mtkfb_check_var, xres=%u, yres=%u, xres_virtual=%u, yres_virtual=%u,\n", |
| var->xres, var->yres, var->xres_virtual, var->yres_virtual); |
| DISPDBG("xoffset=%u, yoffset=%u, bits_per_pixel=%u\n", 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; |
| |
| DISPDBG("mtkfb_check_var, xres=%u, yres=%u, xres_virtual=%u, yres_virtual=%u,\n", |
| var->xres, var->yres, var->xres_virtual, var->yres_virtual); |
| DISPDBG("xoffset=%u, yoffset=%u, bits_per_pixel=%u\n", 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; |
| |
| /* Check if format is RGB565 or BGR565 */ |
| |
| 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; |
| |
| /* Check if format is ARGB565 or ABGR565 */ |
| |
| ASSERT(var->green.offset == 8 && var->transp.offset == 24); |
| ASSERT(var->red.offset + var->blue.offset == 16); |
| ASSERT(var->red.offset == 16 || var->red.offset == 0); |
| } |
| |
| var->red.msb_right = 0; |
| var->green.msb_right = 0; |
| var->blue.msb_right = 0; |
| 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; |
| } |
| |
| unsigned int FB_LAYER = 2; |
| |
| /* 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_RGBA8888 : MTK_FB_FORMAT_BGRA8888; |
| fb_layer.src_color_key = 0; |
| break; |
| |
| default: |
| fb_layer.src_fmt = MTK_FB_FORMAT_UNKNOWN; |
| DISPERR("[%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 = (void *)((unsigned long)fbdev->fb_va_base + var->yoffset * fbi->fix.line_length); |
| DISPDBG("fb_pa=0x%08lx, var->yoffset=0x%08x,fbi->fix.line_length=0x%08x\n", |
| fb_pa, var->yoffset, fbi->fix.line_length); |
| fb_layer.src_phy_addr = (void *)(fb_pa + 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_width = fb_layer.tgt_width = fb_layer.src_pitch = var->xres; */ |
| /* xuecheng, does HWGPU_SUPPORT still in use now? */ |
| #if defined(HWGPU_SUPPORT) |
| fb_layer.src_pitch = ALIGN_TO(var->xres, MTK_FB_ALIGNMENT); |
| #else |
| #ifndef DISP_NO_MT_BOOT |
| if (get_boot_mode() == META_BOOT || get_boot_mode() == FACTORY_BOOT || |
| get_boot_mode() == ADVMETA_BOOT || get_boot_mode() == RECOVERY_BOOT) |
| fb_layer.src_pitch = ALIGN_TO(var->xres, MTK_FB_ALIGNMENT); |
| else |
| fb_layer.src_pitch = ALIGN_TO(var->xres, MTK_FB_ALIGNMENT); |
| #else |
| fb_layer.src_pitch = ALIGN_TO(var->xres, MTK_FB_ALIGNMENT); |
| #endif |
| #endif |
| |
| 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("mtkfb_set_par, fb_layer.src_fmt=%x\n", fb_layer.src_fmt); |
| |
| session_input = kzalloc(sizeof(*session_input), GFP_KERNEL); |
| if (!session_input) |
| goto out; |
| |
| session_input->config_layer_num = 0; |
| |
| if (!isAEEEnabled) { |
| /* DISPCHECK("AEE is not enabled, will disable layer 3\n"); */ |
| input = &session_input->config[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); |
| 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) |
| { |
| #if 0 |
| struct DISP_LAYER_INFO layer; |
| |
| if (layerInfo->layer_id >= DDP_OVL_LAYER_MUN) |
| return 0; |
| |
| layer.id = layerInfo->layer_id; |
| /* DISP_GetLayerInfo(&layer); */ |
| int id = layerInfo->layer_id; |
| |
| layer.curr_en = captured_layer_config[id].layer_en; |
| layer.next_en = cached_layer_config[id].layer_en; |
| layer.hw_en = realtime_layer_config[id].layer_en; |
| layer.curr_idx = captured_layer_config[id].buff_idx; |
| layer.next_idx = cached_layer_config[id].buff_idx; |
| layer.hw_idx = realtime_layer_config[id].buff_idx; |
| layer.curr_identity = captured_layer_config[id].identity; |
| layer.next_identity = cached_layer_config[id].identity; |
| layer.hw_identity = realtime_layer_config[id].identity; |
| layer.curr_conn_type = captured_layer_config[id].connected_type; |
| layer.next_conn_type = cached_layer_config[id].connected_type; |
| layer.hw_conn_type = realtime_layer_config[id].connected_type; |
| layerInfo->layer_enabled = layer.hw_en; |
| layerInfo->curr_en = layer.curr_en; |
| layerInfo->next_en = layer.next_en; |
| layerInfo->hw_en = layer.hw_en; |
| layerInfo->curr_idx = layer.curr_idx; |
| layerInfo->next_idx = layer.next_idx; |
| layerInfo->hw_idx = layer.hw_idx; |
| layerInfo->curr_identity = layer.curr_identity; |
| layerInfo->next_identity = layer.next_identity; |
| layerInfo->hw_identity = layer.hw_identity; |
| layerInfo->curr_conn_type = layer.curr_conn_type; |
| layerInfo->next_conn_type = layer.next_conn_type; |
| layerInfo->hw_conn_type = layer.hw_conn_type; |
| #if 0 |
| MTKFB_LOG("[FB Driver] mtkfb_get_overlay_layer_info():id=%u, layer en=%u, next_en=%u,\n", |
| layerInfo->layer_id, layerInfo->layer_enabled, layerInfo->next_en); |
| MTKFB_LOG("curr_en=%u, hw_en=%u, next_idx=%u, curr_idx=%u, hw_idx=%u\n", |
| layerInfo->curr_en, layerInfo->hw_en, layerInfo->next_idx, layerInfo->curr_idx, layerInfo->hw_idx); |
| #endif |
| #endif |
| return 0; |
| } |
| |
| |
| #include <mt-plat/aee.h> |
| #define mtkfb_aee_print(string, args...) aee_kernel_warning_api(__FILE__, __LINE__, DB_OPT_MMPROFILE_BUFFER, \ |
| "sf-mtkfb blocked", string, ##args) |
| |
| void mtkfb_dump_layer_info(void) |
| { |
| #if 0 |
| unsigned int i; |
| |
| pr_debug("[mtkfb] start dump layer info, early_suspend=%d\n", primary_display_is_sleepd()); |
| pr_debug("[mtkfb] cache(next):\n"); |
| for (i = 0; i < 4; i++) { |
| pr_debug("[mtkfb] layer=%d, layer_en=%d, idx=%d, fmt=%d, addr=0x%x, %d, %d, %d\n ", |
| cached_layer_config[i].layer, /* layer */ |
| cached_layer_config[i].layer_en, cached_layer_config[i].buff_idx, |
| cached_layer_config[i].fmt, |
| cached_layer_config[i].addr, /* addr */ |
| cached_layer_config[i].identity, |
| cached_layer_config[i].connected_type, cached_layer_config[i].security); |
| } |
| |
| pr_debug("[mtkfb] captured(current):\n"); |
| for (i = 0; i < 4; i++) { |
| pr_debug("[mtkfb] layer=%d, layer_en=%d, idx=%d, fmt=%d, addr=0x%x, %d, %d, %d\n ", |
| captured_layer_config[i].layer, /* layer */ |
| captured_layer_config[i].layer_en, captured_layer_config[i].buff_idx, |
| captured_layer_config[i].fmt, |
| captured_layer_config[i].addr, /* addr */ |
| captured_layer_config[i].identity, |
| captured_layer_config[i].connected_type, |
| captured_layer_config[i].security); |
| } |
| pr_debug("[mtkfb] realtime(hw):\n"); |
| for (i = 0; i < 4; i++) { |
| pr_debug("[mtkfb] layer=%d, layer_en=%d, idx=%d, fmt=%d, addr=0x%x, %d, %d, %d\n ", |
| realtime_layer_config[i].layer, /* layer */ |
| realtime_layer_config[i].layer_en, realtime_layer_config[i].buff_idx, |
| realtime_layer_config[i].fmt, |
| realtime_layer_config[i].addr, /* addr */ |
| realtime_layer_config[i].identity, |
| realtime_layer_config[i].connected_type, |
| realtime_layer_config[i].security); |
| } |
| |
| /* dump mmp data */ |
| /* mtkfb_aee_print("surfaceflinger-mtkfb blocked"); */ |
| #endif |
| } |
| |
| static struct disp_session_input_config session_input; |
| static int mtkfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) |
| { |
| void __user *argp = (void __user *)arg; |
| enum DISP_STATUS ret = 0; |
| int r = 0; |
| |
| DISPFUNC(); |
| /* M: dump debug mmprofile log info */ |
| mmprofile_log_ex(MTKFB_MMP_Events.IOCtrl, MMPROFILE_FLAG_PULSE, _IOC_NR(cmd), arg); |
| MTKFB_LOG_DBG("mtkfb_ioctl, info=%p, cmd nr=0x%08x, cmd size=0x%08x\n", info, |
| (unsigned int)_IOC_NR(cmd), (unsigned int)_IOC_SIZE(cmd)); |
| |
| switch (cmd) { |
| case MTKFB_GET_FRAMEBUFFER_MVA: |
| return copy_to_user(argp, &fb_pa, sizeof(fb_pa)) ? -EFAULT : 0; |
| /* remain this for engineer mode dfo multiple resolution */ |
| #if 1 |
| 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 > MTKFB_MAX_DISPLAY_COUNT) { |
| DISPERR("[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 { |
| DISPERR("information for displayid: %d is not available now\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__); |
| r = -EFAULT; |
| } |
| |
| return r; |
| } |
| #endif |
| case MTKFB_POWEROFF: |
| { |
| MTKFB_FUNC(); |
| if (primary_display_is_sleepd()) { |
| pr_debug("[FB Driver] Still in MTKFB_POWEROFF!!!\n"); |
| return r; |
| } |
| |
| pr_debug("[FB Driver] enter MTKFB_POWEROFF\n"); |
| /* cci400_sel_for_ddp(); */ |
| ret = primary_display_suspend(); |
| if (ret < 0) |
| DISPERR("primary display suspend failed\n"); |
| |
| pr_debug("[FB Driver] leave MTKFB_POWEROFF\n"); |
| |
| is_early_suspended = true; /* no care */ |
| return r; |
| } |
| |
| case MTKFB_POWERON: |
| { |
| MTKFB_FUNC(); |
| if (primary_display_is_alive()) { |
| pr_debug("[FB Driver] Still in MTKFB_POWERON!!!\n"); |
| return r; |
| } |
| pr_debug("[FB Driver] enter MTKFB_POWERON\n"); |
| primary_display_resume(); |
| pr_debug("[FB Driver] leave MTKFB_POWERON\n"); |
| is_early_suspended = false; /* no care */ |
| return r; |
| } |
| 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_debug("[FB]: 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("[mtkfb_ioctl] can't get semaphore:%d\n", __LINE__); |
| return -ERESTARTSYS; |
| } |
| sem_early_suspend_cnt--; |
| /* DISP_WaitForLCDNotBusy(); */ |
| /* ret = DISP_ConfigImmediateUpdate((BOOL)arg); */ |
| /* sem_early_suspend_cnt++; */ |
| up(&sem_early_suspend); |
| return r; |
| } |
| |
| case MTKFB_CAPTURE_FRAMEBUFFER: |
| { |
| #ifdef CONFIG_MTK_M4U |
| unsigned long dst_pbuf = 0; |
| unsigned long *src_pbuf = 0; |
| unsigned int pixel_bpp = info->var.bits_per_pixel / 8; |
| unsigned int fbsize = DISP_GetScreenHeight() * DISP_GetScreenWidth() * pixel_bpp; |
| |
| if (copy_from_user(&dst_pbuf, (void __user *)arg, sizeof(dst_pbuf))) { |
| MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); |
| r = -EFAULT; |
| } else { |
| src_pbuf = vmalloc(fbsize); |
| if (!src_pbuf) { |
| MTKFB_LOG("[FB]: vmalloc capture src_pbuf failed! line:%d\n", __LINE__); |
| r = -EFAULT; |
| } else { |
| dprec_logger_start(DPREC_LOGGER_WDMA_DUMP, 0, 0); |
| primary_display_capture_framebuffer_ovl((unsigned long)src_pbuf, eBGRA8888); |
| dprec_logger_done(DPREC_LOGGER_WDMA_DUMP, 0, 0); |
| if (copy_to_user((unsigned long *)dst_pbuf, src_pbuf, fbsize)) { |
| MTKFB_LOG("[FB]: copy_to_user failed! line:%d\n", __LINE__); |
| r = -EFAULT; |
| } |
| vfree(src_pbuf); |
| } |
| } |
| #else |
| pr_warn("[FB]: do not support capture framebuffer with iommu, line:%d\n", __LINE__); |
| r = -EFAULT; |
| #endif |
| return r; |
| } |
| |
| case MTKFB_SLT_AUTO_CAPTURE: |
| { |
| #ifdef CONFIG_MTK_M4U |
| struct fb_slt_catpure capConfig; |
| char *dst_buffer; |
| unsigned int fb_size; |
| |
| if (copy_from_user(&capConfig, (void __user *)arg, sizeof(capConfig))) { |
| MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); |
| r = -EFAULT; |
| } else { |
| unsigned int format; |
| |
| switch (capConfig.format) { |
| case MTK_FB_FORMAT_RGB888: |
| format = eRGB888; |
| break; |
| case MTK_FB_FORMAT_BGR888: |
| format = eBGR888; |
| break; |
| case MTK_FB_FORMAT_ARGB8888: |
| format = eARGB8888; |
| break; |
| case MTK_FB_FORMAT_RGB565: |
| format = eRGB565; |
| break; |
| case MTK_FB_FORMAT_UYVY: |
| format = eYUV_420_2P_UYVY; |
| break; |
| case MTK_FB_FORMAT_ABGR8888: |
| default: |
| format = eABGR8888; |
| break; |
| } |
| |
| dst_buffer = (char *)capConfig.outputBuffer; |
| fb_size = DISP_GetScreenWidth() * DISP_GetScreenHeight() * 4; |
| if (!capConfig.outputBuffer) { |
| MTKFB_LOG("[FB]: vmalloc capture outputBuffer failed! line:%d\n", __LINE__); |
| r = -EFAULT; |
| } else { |
| capConfig.outputBuffer = vmalloc(fb_size); |
| primary_display_capture_framebuffer_ovl((unsigned long)capConfig.outputBuffer, format); |
| if (copy_to_user(dst_buffer, (char *)capConfig.outputBuffer, fb_size)) { |
| MTKFB_LOG("[FB]: copy_to_user failed! line:%d\n", __LINE__); |
| r = -EFAULT; |
| } |
| vfree((char *)capConfig.outputBuffer); |
| } |
| } |
| #else |
| pr_warn("[FB]: do not support capture framebuffer with iommu, line:%d\n", __LINE__); |
| r = -EFAULT; |
| #endif |
| |
| return r; |
| } |
| |
| case MTKFB_GET_OVERLAY_LAYER_INFO: |
| { |
| struct fb_overlay_layer_info layerInfo; |
| |
| MTKFB_LOG(" mtkfb_ioctl():MTKFB_GET_OVERLAY_LAYER_INFO\n"); |
| |
| 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__); |
| r = -EFAULT; |
| } |
| return r; |
| } |
| case MTKFB_SET_OVERLAY_LAYER: |
| { |
| struct fb_overlay_layer layerInfo; |
| |
| if (copy_from_user(&layerInfo, (void __user *)arg, sizeof(layerInfo))) { |
| MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); |
| r = -EFAULT; |
| } else { |
| struct disp_input_config *input; |
| |
| memset((void *)&session_input, 0, sizeof(session_input)); |
| if (layerInfo.layer_id >= TOTAL_OVL_LAYER_NUM) { |
| MTKFB_LOG |
| ("MTKFB_SET_OVERLAY_LAYER, layer_id invalid=%d\n", |
| layerInfo.layer_id); |
| } else { |
| input = &session_input.config[session_input.config_layer_num++]; |
| _convert_fb_layer_to_disp_input(&layerInfo, input); |
| } |
| |
| |
| primary_display_config_input_multiple(&session_input); |
| primary_display_trigger(1, NULL, 0); |
| } |
| |
| return r; |
| } |
| |
| case MTKFB_ERROR_INDEX_UPDATE_TIMEOUT: |
| { |
| pr_debug("[DDP] mtkfb_ioctl():MTKFB_ERROR_INDEX_UPDATE_TIMEOUT\n"); |
| /* call info dump function here */ |
| /* mtkfb_dump_layer_info(); */ |
| return r; |
| } |
| |
| case MTKFB_ERROR_INDEX_UPDATE_TIMEOUT_AEE: |
| { |
| pr_debug("[DDP] mtkfb_ioctl():MTKFB_ERROR_INDEX_UPDATE_TIMEOUT\n"); |
| /* call info dump function here */ |
| /* mtkfb_dump_layer_info(); */ |
| /* mtkfb_aee_print("surfaceflinger-mtkfb blocked"); */ |
| return r; |
| } |
| |
| 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[VIDEO_LAYER_COUNT]; |
| |
| MTKFB_LOG(" mtkfb_ioctl():MTKFB_SET_VIDEO_LAYERS\n"); |
| mmprofile_log(MTKFB_MMP_Events.SetOverlayLayers, MMPROFILE_FLAG_START); |
| |
| if (copy_from_user(&layerInfo, (void __user *)arg, sizeof(layerInfo))) { |
| MTKFB_LOG("[FB]: copy_from_user failed! line:%d\n", __LINE__); |
| mmprofile_log_meta_string(MTKFB_MMP_Events.SetOverlayLayers, |
| MMPROFILE_FLAG_END, "Copy_from_user failed!"); |
| r = -EFAULT; |
| } else { |
| int32_t i; |
| /* mutex_lock(&OverlaySettingMutex); */ |
| struct disp_input_config *input; |
| |
| memset((void *)&session_input, 0, sizeof(session_input)); |
| |
| for (i = 0; i < VIDEO_LAYER_COUNT; ++i) { |
| if (layerInfo[i].layer_id >= OVL_LAYER_NUM) { |
| DDPAEE |
| ("MTKFB_SET_VIDEO_LAYERS, layer_id invalid=%d\n", |
| layerInfo[i].layer_id); |
| continue; |
| } |
| |
| input = |
| &session_input.config[session_input.config_layer_num++]; |
| _convert_fb_layer_to_disp_input(&layerInfo[i], input); |
| } |
| /* is_ipoh_bootup = false; */ |
| /* atomic_set(&OverlaySettingDirtyFlag, 1); */ |
| /* atomic_set(&OverlaySettingApplied, 0); */ |
| /* mutex_unlock(&OverlaySettingMutex); */ |
| /* mmprofile_logStructure(MTKFB_MMP_Events.SetOverlayLayers, MMPROFILE_FLAG_END, */ |
| /* layerInfo, struct mmp_fb_overlay_layers); */ |
| primary_display_config_input_multiple(&session_input); |
| primary_display_trigger(1, NULL, 0); |
| } |
| |
| return r; |
| } |
| |
| case MTKFB_TRIG_OVERLAY_OUT: |
| { |
| MTKFB_LOG(" mtkfb_ioctl():MTKFB_TRIG_OVERLAY_OUT\n"); |
| mmprofile_log(MTKFB_MMP_Events.TrigOverlayOut, MMPROFILE_FLAG_PULSE); |
| 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; |
| if (info->var.yres + var.yoffset > info->var.yres_virtual) |
| return -EFAULT; |
| |
| info->var.yoffset = var.yoffset; |
| init_framebuffer(info); |
| |
| return mtkfb_pan_display_impl(&var, info); |
| } |
| |
| |
| case MTKFB_GET_DEFAULT_UPDATESPEED: |
| { |
| unsigned int speed; |
| |
| MTKFB_LOG("[MTKFB] get default update speed\n"); |
| /* DISP_Get_Default_UpdateSpeed(&speed); */ |
| |
| pr_debug("[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; |
| |
| MTKFB_LOG("[MTKFB] get current update speed\n"); |
| /* DISP_Get_Current_UpdateSpeed(&speed); */ |
| |
| pr_debug("[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; |
| |
| 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__); |
| r = -EFAULT; |
| } else { |
| /* DISP_Change_Update(speed); */ |
| |
| pr_debug("[MTKFB EM]MTKFB_CHANGE_UPDATESPEED is %d\n", speed); |
| } |
| return r; |
| } |
| |
| case MTKFB_AEE_LAYER_EXIST: |
| { |
| /* pr_debug("[MTKFB] isAEEEnabled=%d\n", isAEEEnabled); */ |
| return copy_to_user(argp, &isAEEEnabled, |
| sizeof(isAEEEnabled)) ? -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; |
| |
| pr_debug("factory mode: lcm auto test\n"); |
| result = mtkfb_fm_auto_test(); |
| return copy_to_user(argp, &result, sizeof(result)) ? -EFAULT : 0; |
| } |
| default: |
| pr_debug("mtkfb_ioctl Not support, info=%p, cmd=0x%08x, arg=0x%lx\n", info, |
| (unsigned int)cmd, arg); |
| return -EINVAL; |
| } |
| } |
| |
| #ifdef CONFIG_COMPAT |
| struct compat_fb_overlay_layer { |
| compat_uint_t layer_id; |
| compat_uint_t layer_enable; |
| |
| compat_uptr_t src_base_addr; |
| compat_uptr_t src_phy_addr; |
| compat_uint_t src_direct_link; |
| compat_int_t src_fmt; |
| compat_uint_t src_use_color_key; |
| compat_uint_t src_color_key; |
| compat_uint_t src_pitch; |
| compat_uint_t src_offset_x, src_offset_y; |
| compat_uint_t src_width, src_height; |
| |
| compat_uint_t tgt_offset_x, tgt_offset_y; |
| compat_uint_t tgt_width, tgt_height; |
| compat_int_t layer_rotation; |
| compat_int_t layer_type; |
| compat_int_t video_rotation; |
| |
| compat_uint_t isTdshp; /* set to 1, will go through tdshp first, then layer blending, then to color */ |
| |
| compat_int_t next_buff_idx; |
| compat_int_t identity; |
| compat_int_t connected_type; |
| compat_uint_t security; |
| compat_uint_t alpha_enable; |
| compat_uint_t alpha; |
| compat_int_t fence_fd; /* 8135 */ |
| compat_int_t ion_fd; /* 8135 CL 2340210 */ |
| }; |
| |
| #define COMPAT_MTKFB_SET_OVERLAY_LAYER MTK_IOW(0, struct compat_fb_overlay_layer) |
| #define COMPAT_MTKFB_TRIG_OVERLAY_OUT MTK_IO(1) |
| #define COMPAT_MTKFB_SET_VIDEO_LAYERS MTK_IOW(2, struct compat_fb_overlay_layer) |
| |
| #define COMPAT_MTKFB_CAPTURE_FRAMEBUFFER MTK_IOW(3, compat_ulong_t) |
| #define COMPAT_MTKFB_CONFIG_IMMEDIATE_UPDATE MTK_IOW(4, compat_ulong_t) |
| |
| #define COMPAT_MTKFB_GET_POWERSTATE MTK_IOR(21, compat_ulong_t) |
| #define COMPAT_MTKFB_META_RESTORE_SCREEN MTK_IOW(101, compat_ulong_t) |
| |
| 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; |
| |
| pr_debug("[FB Driver] mtkfb_compat_ioctl, cmd=0x%08x, cmd nr=0x%08x, cmd size=0x%08x\n", |
| cmd, (unsigned int)_IOC_NR(cmd), (unsigned int)_IOC_SIZE(cmd)); |
| |
| switch (cmd) { |
| 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_debug("MTKFB_GET_POWERSTATE failed\n"); |
| ret = -EFAULT; |
| } |
| pr_debug("MTKFB_GET_POWERSTATE success %d\n", power_state); |
| break; |
| } |
| case COMPAT_MTKFB_CAPTURE_FRAMEBUFFER: |
| { |
| #ifdef CONFIG_MTK_M4U |
| compat_ulong_t __user *data32; |
| unsigned long *pbuf; |
| unsigned int pixel_bpp = info->var.bits_per_pixel / 8; |
| unsigned int fbsize = DISP_GetScreenHeight() * DISP_GetScreenWidth() * pixel_bpp; |
| unsigned long dest; |
| |
| data32 = compat_ptr(arg); |
| pbuf = compat_alloc_user_space(fbsize); |
| |
| if (!pbuf) { |
| pr_warn("[FB]: vmalloc capture src_pbuf failed! line:%d\n", __LINE__); |
| ret = -EFAULT; |
| } else { |
| dprec_logger_start(DPREC_LOGGER_WDMA_DUMP, 0, 0); |
| primary_display_capture_framebuffer_ovl((unsigned long)pbuf, eBGRA8888); |
| dprec_logger_done(DPREC_LOGGER_WDMA_DUMP, 0, 0); |
| ret = get_user(dest, data32); |
| if (copy_in_user((unsigned long *)dest, pbuf, fbsize/2)) { |
| pr_warn("[FB]: copy_to_user failed! line:%d\n", __LINE__); |
| ret = -EFAULT; |
| } |
| } |
| #else |
| pr_warn("[FB]: do not support capture framebuffer with iommu, line:%d\n", __LINE__); |
| ret = -EFAULT; |
| #endif |
| 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; |
| |
| MTKFB_LOG(" mtkfb_compat_ioctl():MTKFB_SET_OVERLAY_LAYER\n"); |
| |
| 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; |
| |
| compat_convert(&compat_layerInfo, &layerInfo); |
| |
| /* in early suspend mode ,will not update buffer index, info SF by return value */ |
| if (primary_display_is_sleepd()) { |
| pr_debug("[FB Driver] error, set overlay in early suspend ,skip!\n"); |
| return MTKFB_ERROR_IS_EARLY_SUSPEND; |
| } |
| memset((void *)&session_input, 0, sizeof(session_input)); |
| if (layerInfo.layer_id >= TOTAL_OVL_LAYER_NUM) { |
| DDPAEE |
| ("COMPAT_MTKFB_SET_OVERLAY_LAYER, layer_id invalid=%d\n", |
| layerInfo.layer_id); |
| } else { |
| input = &session_input.config[session_input.config_layer_num++]; |
| _convert_fb_layer_to_disp_input(&layerInfo, input); |
| } |
| primary_display_config_input_multiple(&session_input); |
| /* primary_display_trigger(1, NULL, 0); */ |
| } |
| } |
| break; |
| |
| case COMPAT_MTKFB_SET_VIDEO_LAYERS: |
| { |
| struct compat_fb_overlay_layer compat_layerInfo[VIDEO_LAYER_COUNT]; |
| struct compat_fb_overlay_layer *info = compat_layerInfo; |
| |
| MTKFB_LOG(" mtkfb_compat_ioctl():MTKFB_SET_VIDEO_LAYERS\n"); |
| |
| 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 { |
| int32_t i; |
| /* mutex_lock(&OverlaySettingMutex); */ |
| struct disp_input_config *input; |
| |
| memset((void *)&session_input, 0, sizeof(session_input)); |
| |
| for (i = 0; i < VIDEO_LAYER_COUNT; ++i) { |
| compat_convert(info + i, &layerInfo); |
| if (layerInfo.layer_id >= OVL_LAYER_NUM) { |
| DDPAEE |
| ("COMPAT_MTKFB_SET_VIDEO_LAYERS, layer_id invalid=%d\n", |
| layerInfo.layer_id); |
| continue; |
| } |
| input = |
| &session_input.config[session_input.config_layer_num++]; |
| _convert_fb_layer_to_disp_input(&layerInfo, input); |
| } |
| /* is_ipoh_bootup = false; */ |
| /* atomic_set(&OverlaySettingDirtyFlag, 1); */ |
| /* atomic_set(&OverlaySettingApplied, 0); */ |
| /* mutex_unlock(&OverlaySettingMutex); */ |
| /* mmprofile_logStructure(MTKFB_MMP_Events.SetOverlayLayers, MMPROFILE_FLAG_END, */ |
| /* layerInfo, struct mmp_fb_overlay_layers); */ |
| primary_display_config_input_multiple(&session_input); |
| /* primary_display_trigger(1, NULL, 0); */ |
| } |
| } |
| 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 */ |
| pr_debug("AppLaunch mtkfb_pan_display_proxy.\n"); |
| #endif |
| return mtkfb_pan_display_impl(var, info); |
| } |
| |
| #if defined(CONFIG_PM_AUTOSLEEP) |
| static void mtkfb_blank_suspend(void); |
| static void mtkfb_blank_resume(void); |
| |
| |
| static int mtkfb_blank(int blank_mode, struct fb_info *info) |
| { |
| if (get_boot_mode() == RECOVERY_BOOT) |
| return 0; |
| |
| switch (blank_mode) { |
| case FB_BLANK_UNBLANK: |
| case FB_BLANK_NORMAL: |
| mtkfb_blank_resume(); |
| if (!lcd_fps) |
| msleep(30); |
| else |
| msleep(2 * 100000 / lcd_fps); /* Delay 2 frames. */ |
| break; |
| case FB_BLANK_VSYNC_SUSPEND: |
| case FB_BLANK_HSYNC_SUSPEND: |
| break; |
| case FB_BLANK_POWERDOWN: |
| mtkfb_blank_suspend(); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| #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_setcolreg = mtkfb_setcolreg, |
| .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_COMPAT |
| .fb_compat_ioctl = mtkfb_compat_ioctl, |
| #endif |
| #if defined(CONFIG_PM_AUTOSLEEP) |
| .fb_blank = mtkfb_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 r = 0; |
| |
| /*DISPFUNC(); */ |
| |
| WARN_ON(!fbdev->fb_va_base); |
| info->fbops = &mtkfb_ops; |
| info->flags = FBINFO_FLAG_DEFAULT; |
| info->screen_base = (char *)fbdev->fb_va_base; |
| info->screen_size = fbdev->fb_size_in_byte; |
| info->pseudo_palette = fbdev->pseudo_palette; |
| |
| r = fb_alloc_cmap(&info->cmap, 32, 0); |
| if (r != 0) |
| DISPERR("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("FB_XRES=%d, FB_YRES=%d, FB_XRES_V=%d, FB_YRES_V=%d, BPP=%d, FB_PAGES=%d, FB_LINE=%d, FB_SIZEV=%d\n", |
| MTK_FB_XRES, MTK_FB_YRES, MTK_FB_XRESV, MTK_FB_YRESV, MTK_FB_BPP, MTK_FB_PAGES, MTK_FB_LINE, |
| MTK_FB_SIZEV); |
| /* use 32 bit framebuffer as default */ |
| var.bits_per_pixel = 32; |
| |
| var.transp.offset = 24; |
| var.red.length = 8; |
| #if 0 |
| var.red.offset = 16; |
| var.red.length = 8; |
| var.green.offset = 8; |
| var.green.length = 8; |
| var.blue.offset = 0; |
| var.blue.length = 8; |
| #else |
| var.red.offset = 0; |
| var.red.length = 8; |
| var.green.offset = 8; |
| var.green.length = 8; |
| var.blue.offset = 16; |
| var.blue.length = 8; |
| #endif |
| var.width = DISP_GetActiveWidth(); |
| var.height = DISP_GetActiveHeight(); |
| var.activate = FB_ACTIVATE_NOW; |
| |
| r = mtkfb_check_var(&var, info); |
| if (r != 0) |
| DISPERR("failed to mtkfb_check_var\n"); |
| |
| info->var = var; |
| |
| r = mtkfb_set_par(info); |
| if (r != 0) |
| DISPERR("failed to mtkfb_set_par\n"); |
| |
| MSG_FUNC_LEAVE(); |
| return r; |
| } |
| |
| #ifdef MTKFB_UT |
| 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; |
| |
| DISPMSG("@(%d,%d)addr=0x%lx, MTK_FB_XRESV=%d, draw_block start addr=0x%lx, w=%d, h=%d\n", |
| x, y, addr, MTK_FB_XRESV, start_addr, w, h); |
| |
| for (j = 0; j < h; j++) { |
| for (i = 0; i < w; i++) |
| *(unsigned int *)(start_addr + i * 4 + j * MTK_FB_XRESV * 4) = color; |
| |
| } |
| |
| } |
| |
| static int _mtkfb_internal_test(unsigned long va, unsigned int w, unsigned int h, unsigned int fmt) |
| { |
| /* this is for debug, used in bring up day */ |
| unsigned int i = 0, j, num; |
| unsigned int color = 0; |
| int _internal_test_block_size = 120; |
| unsigned int *pAddr = (unsigned int *)va; |
| |
| DISPMSG("UT starts @ va=0x%lx, w=%d, h=%d\n", va, w, h); |
| |
| /* mtkfb_fbinfo_init default config as RGBA-->0xAABBGGRR */ |
| DISPMSG("R\n"); |
| for (i = 0; i < h; i++) { |
| for (j = 0; j < w; j++) { |
| *(pAddr + i * w + j) = |
| (fmt == 0) ? 0xFFFF0000U : 0xFF0000FFU; |
| } |
| } |
| primary_display_trigger(1, NULL, 0); |
| msleep(1000); |
| |
| DISPMSG("G\n"); |
| for (i = 0; i < h; i++) { |
| for (j = 0; j < w; j++) |
| *(pAddr + i * w + j) = 0xFF00FF00U; |
| } |
| primary_display_trigger(1, NULL, 0); |
| msleep(1000); |
| |
| DISPMSG("B\n"); |
| for (i = 0; i < h; i++) { |
| for (j = 0; j < w; j++) { |
| *(pAddr + i * w + j) = |
| (fmt == 0) ? 0xFF0000FFU : 0xFFFF0000U; |
| } |
| } |
| primary_display_trigger(1, NULL, 0); |
| msleep(1000); |
| |
| for (num = 160; num < 200; num += 40) { |
| _internal_test_block_size = num; |
| for (i = 0; i < w * h / _internal_test_block_size / _internal_test_block_size; i++) { |
| color = (i & 0x1) * 0xff; |
| color += ((i & 0x2) >> 1) * 0xff00; |
| color += ((i & 0x4) >> 2) * 0xff0000; |
| color += 0xff000000U; |
| _mtkfb_draw_block(va, |
| i % (w / _internal_test_block_size) * |
| _internal_test_block_size, |
| i / (w / _internal_test_block_size) * |
| _internal_test_block_size, _internal_test_block_size, |
| _internal_test_block_size, color); |
| } |
| primary_display_trigger(1, NULL, 0); |
| msleep(1000); |
| } |
| |
| return 0; |
| } |
| |
| static int mtkfb_fbinfo_modify(struct fb_info *info, unsigned int fmt) |
| { |
| struct fb_var_screeninfo var; |
| int r = 0; |
| |
| memcpy(&var, &(info->var), sizeof(var)); |
| var.activate = FB_ACTIVATE_NOW; |
| var.bits_per_pixel = 32; |
| var.transp.offset = 24; |
| |
| |
| if (fmt == 0) { |
| /* default as BGRA, GPU(default data is BGRA too) will not change this value at home screen */ |
| var.red.offset = 16; |
| var.red.length = 8; |
| var.green.offset = 8; |
| var.green.length = 8; |
| var.blue.offset = 0; |
| var.blue.length = 8; |
| } else { |
| /* 0xRGBA */ |
| 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.yoffset = var.yres; */ |
| |
| r = mtkfb_check_var(&var, info); |
| if (r != 0) |
| DISPERR("failed to mtkfb_check_var\n"); |
| |
| info->var = var; |
| |
| r = mtkfb_set_par(info); |
| if (r != 0) |
| DISPERR("failed to mtkfb_set_par\n"); |
| |
| return r; |
| } |
| #endif |
| |
| /* 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(); |
| } |
| |
| #define RGB565_TO_ARGB8888(x) \ |
| ((((x) & 0x1F) << 3) | \ |
| (((x) & 0x7E0) << 5) | \ |
| (((x) & 0xF800) << 8) | \ |
| (0xFF << 24)) /* opaque */ |
| |
| /* Init frame buffer content as 3 R/G/B color bars for debug */ |
| static int init_framebuffer(struct fb_info *info) |
| { |
| void *buffer = info->screen_base + info->var.yoffset * info->fix.line_length; |
| |
| /* clean whole frame buffer as black */ |
| int size = info->var.xres_virtual * info->var.yres * info->var.bits_per_pixel/8; |
| |
| /*memset_io(buffer, 0, info->screen_size)*/; |
| |
| 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 r = 0; |
| |
| switch (state) { |
| case MTKFB_ACTIVE: |
| r = unregister_framebuffer(fbdev->fb_info); |
| ASSERT(r == 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: |
| WARN_ON(1); |
| } |
| } |
| |
| char mtkfb_lcm_name[256] = { 0 }; |
| |
| void disp_get_fb_address(unsigned long *fbVirAddr, unsigned long *fbPhysAddr) |
| { |
| struct mtkfb_device *fbdev = (struct mtkfb_device *)mtkfb_fbi->par; |
| |
| *fbVirAddr = (unsigned long)fbdev->fb_va_base + mtkfb_fbi->var.yoffset * mtkfb_fbi->fix.line_length; |
| *fbPhysAddr = (unsigned long)fbdev->fb_pa_base + mtkfb_fbi->var.yoffset * mtkfb_fbi->fix.line_length; |
| } |
| |
| char *mtkfb_find_lcm_driver(void) |
| { |
| |
| #ifdef CONFIG_OF |
| if (_parse_tag_videolfb() == 1) { |
| pr_debug("[mtkfb] not found LCM driver, return NULL\n"); |
| return NULL; |
| } |
| #else |
| { |
| char *p, *q; |
| |
| p = strstr(saved_command_line, "lcm="); |
| /* we can't find lcm string in the command line, the uboot should be old version */ |
| if (p == NULL) |
| return NULL; |
| |
| p += 6; |
| if ((p - saved_command_line) > strlen(saved_command_line + 1)) |
| return NULL; |
| |
| pr_debug("%s, %s\n", __func__, p); |
| q = p; |
| while (*q != ' ' && *q != '\0') |
| q++; |
| |
| memset((void *)mtkfb_lcm_name, 0, sizeof(mtkfb_lcm_name)); |
| strncpy((char *)mtkfb_lcm_name, (const char *)p, (int)(q - p)); |
| mtkfb_lcm_name[q - p + 1] = '\0'; |
| } |
| #endif |
| /* printk("%s, %s\n", __func__, mtkfb_lcm_name); */ |
| return mtkfb_lcm_name; |
| } |
| |
| uint32_t color; |
| unsigned int mtkfb_fm_auto_test(void) |
| { |
| unsigned int result = 0; |
| unsigned int i = 0; |
| unsigned long fbVirAddr; |
| uint32_t fbsize; |
| int r = 0; |
| unsigned int *fb_buffer; |
| struct mtkfb_device *fbdev = (struct mtkfb_device *)mtkfb_fbi->par; |
| struct fb_var_screeninfo var; |
| |
| 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; |
| |
| r = mtkfb_check_var(&var, mtkfb_fbi); |
| if (r != 0) |
| PRNERR("failed to mtkfb_check_var\n"); |
| |
| mtkfb_fbi->var = var; |
| |
| #if 0 |
| r = mtkfb_set_par(mtkfb_fbi); |
| |
| if (r != 0) |
| PRNERR("failed to mtkfb_set_par\n"); |
| #endif |
| 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; |
| #if 0 |
| if (!primary_display_is_video_mode()) |
| primary_display_trigger(1, NULL, 0); |
| #endif |
| mtkfb_pan_display_impl(&mtkfb_fbi->var, mtkfb_fbi); |
| msleep(100); |
| |
| result = primary_display_lcm_ATA(); |
| |
| if (result == 0) |
| pr_debug("ATA LCM failed\n"); |
| else |
| pr_debug("ATA LCM passed\n"); |
| |
| return result; |
| } |
| |
| #ifdef CONFIG_OF |
| struct tag_videolfb { |
| u64 fb_base; |
| u32 islcmfound; |
| u32 fps; |
| u32 vram; |
| char lcmname[1]; /* this is the minimum size */ |
| }; |
| unsigned int islcmconnected; |
| unsigned int vramsize; |
| phys_addr_t fb_base; |
| static int is_videofb_parse_done; |
| |
| /* Retrun value: 0: success, 1: fail */ |
| int _parse_tag_videolfb(void) |
| { |
| struct tag_videolfb *videolfb_tag = NULL; |
| struct device_node *chosen_node; |
| #ifdef MTK_ONLY_KERNEL_DISP |
| const char* lcmname = NULL; |
| #endif |
| /* not necessary */ |
| /* DISPCHECK("[DT][videolfb]isvideofb_parse_done = %d\n",is_videofb_parse_done); */ |
| |
| if (is_videofb_parse_done) |
| return 0; |
| #ifdef MTK_NO_DISP_IN_LK |
| DISPCHECK("[DT][videolfb] zaikuo, workaround for LK not ready\n"); /* after LK ready, remove this code */ |
| return 1; |
| #endif |
| |
| chosen_node = of_find_node_by_path("/chosen"); |
| if (!chosen_node) |
| chosen_node = of_find_node_by_path("/chosen@0"); |
| |
| if (chosen_node) { |
| videolfb_tag = (struct tag_videolfb *)of_get_property(chosen_node, "atag,videolfb", NULL); |
| if (videolfb_tag) { |
| memset((void *)mtkfb_lcm_name, 0, sizeof(mtkfb_lcm_name)); |
| strcpy((char *)mtkfb_lcm_name, videolfb_tag->lcmname); |
| mtkfb_lcm_name[strlen(videolfb_tag->lcmname)] = '\0'; |
| |
| lcd_fps = videolfb_tag->fps; |
| DISPPRINT("lcd_fps = %d\n", lcd_fps); |
| if (lcd_fps == 0) |
| lcd_fps = 6000; |
| |
| islcmconnected = videolfb_tag->islcmfound; |
| vramsize = videolfb_tag->vram; |
| fb_base = videolfb_tag->fb_base; |
| is_videofb_parse_done = 1; |
| DISPPRINT("[DT][videolfb] lcmfound=%d, fps=%d, fb_base=%p, vram=%d, lcmname=%s\n", |
| islcmconnected, lcd_fps, (void *)fb_base, vramsize, mtkfb_lcm_name); |
| return 0; |
| } else { |
| #ifdef MTK_ONLY_KERNEL_DISP |
| if (!primary_display_init_lcm()) |
| DISPCHECK("[DT] init lcm failed\n"); |
| lcmname = primary_display_get_lcm_name(); |
| memset((void *)mtkfb_lcm_name, 0, sizeof(mtkfb_lcm_name)); |
| strcpy((char *)mtkfb_lcm_name, lcmname); |
| mtkfb_lcm_name[strlen(lcmname)] = '\0'; |
| lcd_fps = 0; |
| islcmconnected = 1; |
| vramsize = DISP_GetFBRamSize(); |
| vramsize += DAL_GetLayerSize(); |
| #ifdef CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE |
| vramsize = ALIGN_TO(vramsize, 0x400000); |
| #else |
| vramsize = ALIGN_TO(vramsize, 0x100000); |
| #endif |
| is_videofb_parse_done = 1; |
| DISPPRINT("[DT][videolfb] lcmfound=%d, fps=%d, fb_base=tobe, vram=%d, lcmname=%s\n", |
| islcmconnected, lcd_fps, vramsize, mtkfb_lcm_name); |
| return 0; |
| #endif |
| } |
| |
| DISPCHECK("[DT][videolfb] videolfb_tag not found\n"); |
| return 1; |
| } |
| |
| DISPCHECK("[DT][videolfb] of_chosen not found\n"); |
| return 1; |
| } |
| |
| 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 |
| |
| #ifdef FPGA_DEBUG_PAN |
| static int update_test_kthread(void *data) |
| { |
| /* struct sched_param param = { .sched_priority = RTPM_PRIO_SCRN_UPDATE }; */ |
| /* sched_setscheduler(current, SCHED_RR, ¶m); */ |
| 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 */ |
| pr_debug("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++; |
| } |
| |
| MTKFB_LOG("exit esd_recovery_kthread()\n"); |
| return 0; |
| } |
| #endif |
| |
| static int mtkfb_probe(struct platform_device *pdev) |
| { |
| struct mtkfb_device *fbdev = NULL; |
| struct fb_info *fbi; |
| int init_state; |
| int r = 0; |
| #ifdef DISP_GPIO_DTS |
| long dts_gpio_state = 0; |
| #endif |
| #ifdef CONFIG_MTK_IOMMU |
| struct device_node *larb_node; |
| struct platform_device *larb_pdev; |
| #endif |
| #ifdef MTK_ONLY_KERNEL_DISP |
| void *fb_virtual_address = NULL; |
| #endif |
| |
| DISPPRINT("mtkfb_probe name [%s] = [%s][%p]\n", |
| pdev->name, pdev->dev.init_name, (void *)&pdev->dev); |
| #ifndef CONFIG_MTK_IOMMU |
| if (!mtk_smi_larb_get_base(0)) |
| return -EPROBE_DEFER; |
| #else |
| larb_node = of_parse_phandle(pdev->dev.of_node, "mediatek,larb", 0); |
| if (!larb_node) |
| return -EINVAL; |
| |
| larb_pdev = of_find_device_by_node(larb_node); |
| of_node_put(larb_node); |
| if ((!larb_pdev) || (!larb_pdev->dev.driver)) { |
| pr_err("disp_probe is earlier than SMI\n"); |
| return -EPROBE_DEFER; |
| } |
| |
| #endif |
| #ifdef MTK_ONLY_KERNEL_DISP |
| if (IS_ERR_OR_NULL(g_ion_device)) |
| return -EPROBE_DEFER; |
| #endif |
| #ifdef CONFIG_OF |
| _parse_tag_videolfb(); |
| #else |
| { |
| char *p = NULL; |
| |
| pr_debug("%s, %s\n", __func__, saved_command_line); |
| p = strstr(saved_command_line, "fps="); |
| if (p == NULL) { |
| lcd_fps = 6000; |
| pr_debug("[FB driver]can not get fps from uboot\n"); |
| } else { |
| p += 4; |
| r = kstrtol(p, 10, &lcd_fps); |
| if (r) |
| pr_err("DISP/%s: errno %d\n", __func__, r); |
| if (lcd_fps == 0) |
| lcd_fps = 6000; |
| } |
| } |
| #endif |
| #ifdef MTK_ONLY_KERNEL_DISP |
| fb_virtual_address = primary_display_alloc_hw_buffer(vramsize, &fb_base); |
| if (fb_virtual_address == NULL) { |
| DISPERR("dma allocate framebuffer of size %u failed\n", (uint32_t)vramsize); |
| r = ENOMEM; |
| goto cleanup; |
| } |
| DISPPRINT("fb_base %p fb_virtual_address %p\n", (void *)fb_base, fb_virtual_address); |
| #endif |
| init_state = 0; |
| |
| #ifdef DISP_GPIO_DTS |
| /* repo call DTS gpio module, if not necessary, invoke nothing */ |
| dts_gpio_state = disp_dts_gpio_init_repo(pdev); |
| if (dts_gpio_state != 0) |
| dev_err(&pdev->dev, "retrieve GPIO DTS failed."); |
| #endif |
| |
| fbi = framebuffer_alloc(sizeof(struct mtkfb_device), &(pdev->dev)); |
| if (!fbi) { |
| DISPERR("unable to allocate memory for device info\n"); |
| r = -ENOMEM; |
| goto cleanup; |
| } |
| |
| fbdev = (struct mtkfb_device *)fbi->par; |
| fbdev->fb_info = fbi; |
| fbdev->dev = &(pdev->dev); |
| dev_set_drvdata(&(pdev->dev), fbdev); |
| |
| { |
| #ifndef MTK_NO_DISP_IN_LK |
| #ifdef CONFIG_OF |
| /* printk("mtkfb_probe:get FB MEM REG\n"); */ |
| _parse_tag_videolfb(); |
| /* printk("mtkfb_probe: fb_pa = %p\n",(void *)fb_base); */ |
| /* do not need to call attach device in arm64 */ |
| #if 0 /*def CONFIG_MTK_IOMMU; This supposed to be done by DMA framework.*/ |
| /* For IOMMU */ |
| np = of_parse_phandle(pdev->dev.of_node, "iommus", 0); |
| if (np) { |
| pimudev = of_find_device_by_node(np); |
| if (WARN_ON(pimudev)) |
| of_node_put(np); |
| |
| dma_mapping = pimudev->dev.archdata.iommu; |
| arm_iommu_attach_device(&pdev->dev, dma_mapping); |
| MTKFB_LOG("mtkfb iommu attach device\n"); |
| } |
| #endif |
| disp_hal_allocate_framebuffer(fb_base, (fb_base + vramsize - 1), |
| (unsigned long *)&fbdev->fb_va_base, &fb_pa); |
| fbdev->fb_pa_base = fb_base; |
| |
| #else |
| struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| |
| vramsize = res->end - res->start + 1; |
| /* ASSERT(DISP_GetVRamSize() <= (res->end - res->start + 1)); */ |
| disp_hal_allocate_framebuffer(res->start, res->end, |
| (unsigned int *)&fbdev->fb_va_base, &fb_pa); |
| fbdev->fb_pa_base = res->start; |
| #endif |
| #else |
| { |
| struct resource res; |
| |
| unsigned long fb_mem_addr_pa = 0; |
| unsigned long fb_mem_addr_va = 0; |
| |
| pr_debug("mtkfb_probe:get FB MEM REG\n"); |
| |
| if (of_address_to_resource(dev->of_node, 0, &res) != 0) { |
| r = -ENOMEM; |
| goto cleanup; |
| } |
| fb_mem_addr_pa = res.start; |
| |
| fb_mem_addr_va = (unsigned long)of_iomap(dev->of_node, 0); |
| |
| pr_debug("mtkfb_probe: fb_pa = 0x%lx, fb_va = 0x%lx\n", fb_mem_addr_pa, |
| fb_mem_addr_va); |
| |
| disp_hal_allocate_framebuffer(res.start, res.end, |
| (unsigned int *)&fbdev->fb_va_base, &fb_pa); |
| fbdev->fb_pa_base = res.start; |
| fbdev->fb_va_base = fb_mem_addr_va; |
| } |
| #endif |
| } |
| primary_display_set_frame_buffer_address((unsigned long)fbdev->fb_va_base, fb_pa); |
| |
| /* mtkfb should parse lcm name from kernel boot command line */ |
| primary_display_init(mtkfb_find_lcm_driver(), lcd_fps); |
| |
| init_state++; /* 1 */ |
| MTK_FB_XRES = primary_display_get_width(); |
| MTK_FB_YRES = primary_display_get_height(); |
| fb_xres_update = MTK_FB_XRES; |
| fb_yres_update = MTK_FB_YRES; |
| |
| MTK_FB_BPP = primary_display_get_bpp(); |
| MTK_FB_PAGES = primary_display_get_pages(); |
| /* DISPMSG("MTK_FB_XRES=%d, MTKFB_YRES=%d, MTKFB_BPP=%d, MTK_FB_PAGES=%d, MTKFB_LINE=%d, MTKFB_SIZEV=%d\n",*/ |
| /* MTK_FB_XRES, MTK_FB_YRES, MTK_FB_BPP, MTK_FB_PAGES, MTK_FB_LINE, MTK_FB_SIZEV); */ |
| fbdev->fb_size_in_byte = MTK_FB_SIZEV; |
| |
| /* Allocate and initialize video frame buffer */ |
| DISPPRINT("[FB Driver] fbdev->fb_pa_base = %p, fbdev->fb_va_base = %p\n", |
| &(fbdev->fb_pa_base), fbdev->fb_va_base); |
| |
| if (!fbdev->fb_va_base) { |
| DISPERR("unable to allocate memory for frame buffer\n"); |
| r = -ENOMEM; |
| goto cleanup; |
| } |
| init_state++; /* 2 */ |
| |
| /* Register to system */ |
| |
| r = mtkfb_fbinfo_init(fbi); |
| if (r) { |
| DISPERR("mtkfb_fbinfo_init fail, r = %d\n", r); |
| goto cleanup; |
| } |
| init_state++; /* 4 */ |
| mtkfb_fbi = fbi; |
| |
| 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 fbPA = fb_pa; |
| /* DAL init here */ |
| fbVA += DISP_GetFBRamSize(); |
| fbPA += DISP_GetFBRamSize(); |
| ret = DAL_Init(fbVA, fbPA); |
| } |
| |
| r = mtkfb_register_sysfs(fbdev); |
| if (r) { |
| DISPERR("mtkfb_register_sysfs fail, r = %d\n", r); |
| goto cleanup; |
| } |
| init_state++; /* 5 */ |
| |
| r = register_framebuffer(fbi); |
| if (r != 0) { |
| DISPERR("register_framebuffer failed\n"); |
| goto cleanup; |
| } |
| |
| if (disp_helper_get_stage() != DISP_HELPER_STAGE_NORMAL) |
| primary_display_diagnose(); |
| |
| #ifndef MTK_ONLY_KERNEL_DISP |
| /* this function will get fb_heap base address to ion for management frame buffer*/ |
| DISPPRINT("ion map framebuffer base=0x%p, fb total size=%d DAL size=%d\n", |
| (void *)mtkfb_get_fb_base(), |
| (unsigned int)mtkfb_get_fb_size(), (unsigned int)DAL_GetLayerSize()); |
| ion_drv_create_FB_heap(mtkfb_get_fb_base(), mtkfb_get_fb_size() - DAL_GetLayerSize()); |
| #endif |
| |
| fbdev->state = MTKFB_ACTIVE; |
| |
| #ifdef FPGA_DEBUG_PAN |
| #if 0 |
| { |
| unsigned int cnt = 0; |
| void *fb_va = (void *)fbdev->fb_va_base; |
| unsigned int fbsize = primary_display_get_height() * primary_display_get_width(); |
| #if 0 |
| for (cnt = 0; cnt < fbsize; cnt++) |
| *(fb_va++) = 0xFFFF0000; |
| for (cnt = 0; cnt < fbsize; cnt++) |
| *(fb_va++) = 0xFF00FF00; |
| #else |
| memset_io(fb_va, 0xFF, fbsize * 4); |
| memset_io(fb_va + fbsize * 4, 0x55, fbsize * 4); |
| #endif |
| } |
| pr_debug("memset_io done\n"); |
| #endif |
| { |
| struct task_struct *update_test_task = NULL; |
| |
| update_test_task = kthread_create(update_test_kthread, NULL, "update_test_kthread"); |
| |
| if (IS_ERR(update_test_task)) |
| MTKFB_LOG("update test task create fail\n"); |
| else |
| wake_up_process(update_test_task); |
| } |
| #endif |
| |
| MSG_FUNC_LEAVE(); |
| |
| #ifdef MTKFB_UT |
| /*1 for RGBA; 0 for BGRA*/ |
| /* check BGRA color format */ |
| _mtkfb_internal_test((unsigned long)fbdev->fb_va_base, MTK_FB_XRES, MTK_FB_YRES, 0); |
| /* check RGBA color format */ |
| mtkfb_fbinfo_modify(fbi, 1); |
| _mtkfb_internal_test((unsigned long)fbdev->fb_va_base, MTK_FB_XRES, MTK_FB_YRES, 1); |
| /* change back to BGRA as default */ |
| mtkfb_fbinfo_modify(fbi, 0); |
| #endif |
| |
| return 0; |
| |
| cleanup: |
| mtkfb_free_resources(fbdev, init_state); |
| |
| return r; |
| } |
| |
| /* 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) |
| { |
| /* NOT_REFERENCED(pdev); */ |
| MSG_FUNC_ENTER(); |
| MTKFB_LOG("[FB Driver] mtkfb_suspend(): 0x%x\n", 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_RESTORE_PREPARE: |
| primary_display_ipoh_restore(); |
| pr_debug("[FB Driver] mtkfb_ipoh_restore PM_RESTORE_PREPARE\n"); |
| return NOTIFY_DONE; |
| case PM_POST_RESTORE: |
| primary_display_ipoh_recover(); |
| pr_debug("[FB Driver] %s pm_event: %lu\n", __func__, val); |
| 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] mtkfb_shutdown()\n"); |
| #ifdef CONFIG_MTK_LEDS |
| mt65xx_leds_brightness_set(MT65XX_LED_TYPE_LCD, LED_OFF); |
| #endif |
| 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; |
| } |
| |
| MTKFB_LOG("[FB Driver] cci400_sel_for_ddp\n"); |
| /* cci400_sel_for_ddp(); //FIXME:need confirm with Zhihui */ |
| |
| primary_display_suspend(); |
| MTKFB_LOG("[FB Driver] leave mtkfb_shutdown\n"); |
| } |
| |
| void mtkfb_clear_lcm(void) |
| { |
| #if 0 |
| int i; |
| unsigned int layer_status[DDP_OVL_LAYER_MUN] = { 0 }; |
| |
| mutex_lock(&OverlaySettingMutex); |
| for (i = 0; i < DDP_OVL_LAYER_MUN; i++) { |
| layer_status[i] = cached_layer_config[i].layer_en; |
| cached_layer_config[i].layer_en = 0; |
| cached_layer_config[i].isDirty = 1; |
| } |
| atomic_set(&OverlaySettingDirtyFlag, 1); |
| atomic_set(&OverlaySettingApplied, 0); |
| mutex_unlock(&OverlaySettingMutex); |
| |
| /* DISP_CHECK_RET(DISP_UpdateScreen(0, 0, fb_xres_update, fb_yres_update)); */ |
| /* DISP_CHECK_RET(DISP_UpdateScreen(0, 0, fb_xres_update, fb_yres_update)); */ |
| /* DISP_WaitForLCDNotBusy(); */ |
| mutex_lock(&OverlaySettingMutex); |
| for (i = 0; i < DDP_OVL_LAYER_MUN; i++) { |
| cached_layer_config[i].layer_en = layer_status[i]; |
| cached_layer_config[i].isDirty = 1; |
| } |
| atomic_set(&OverlaySettingDirtyFlag, 1); |
| atomic_set(&OverlaySettingApplied, 0); |
| mutex_unlock(&OverlaySettingMutex); |
| #endif |
| } |
| |
| #if defined(CONFIG_PM_AUTOSLEEP) |
| static void mtkfb_blank_suspend(void) |
| { |
| int ret = 0; |
| |
| MSG_FUNC_ENTER(); |
| |
| if (disp_helper_get_stage() != DISP_HELPER_STAGE_NORMAL) |
| return; |
| |
| #ifdef CONFIG_SINGLE_PANEL_OUTPUT |
| is_early_suspended = true; |
| #endif |
| |
| pr_debug("[FB Driver] enter early_suspend\n"); |
| #ifdef CONFIG_MTK_LEDS |
| mt65xx_leds_brightness_set(MT65XX_LED_TYPE_LCD, LED_OFF); |
| #endif |
| msleep(30); |
| |
| ret = primary_display_suspend(); |
| |
| if (ret < 0) { |
| DISPERR("primary display suspend failed\n"); |
| return; |
| } |
| pr_debug("[FB Driver] leave early_suspend\n"); |
| } |
| #endif |
| /* PM resume */ |
| static int mtkfb_resume(struct platform_device *pdev) |
| { |
| /* NOT_REFERENCED(pdev); */ |
| MSG_FUNC_ENTER(); |
| MTKFB_LOG("[FB Driver] mtkfb_resume()\n"); |
| MSG_FUNC_LEAVE(); |
| return 0; |
| } |
| #if defined(CONFIG_PM_AUTOSLEEP) |
| static void mtkfb_blank_resume(void) |
| { |
| int ret = 0; |
| |
| MSG_FUNC_ENTER(); |
| |
| if (disp_helper_get_stage() != DISP_HELPER_STAGE_NORMAL) |
| return; |
| |
| pr_debug("[FB Driver] enter late_resume\n"); |
| |
| #ifdef CONFIG_SINGLE_PANEL_OUTPUT |
| is_early_suspended = false; |
| #endif |
| |
| ret = primary_display_resume(); |
| if (ret) { |
| DISPERR("primary display resume failed\n"); |
| return; |
| } |
| |
| if (get_boot_mode() == FACTORY_BOOT) |
| mtkfb_pan_display_impl(&mtkfb_fbi->var, mtkfb_fbi); |
| |
| pr_debug("[FB Driver] leave late_resume\n"); |
| } |
| #endif |
| /*---------------------------------------------------------------------------*/ |
| #ifdef CONFIG_PM |
| /*---------------------------------------------------------------------------*/ |
| int mtkfb_pm_suspend(struct device *device) |
| { |
| /* pr_debug("calling %s()\n", __func__); */ |
| struct platform_device *pdev = to_platform_device(device); |
| |
| WARN_ON(pdev == NULL); |
| |
| 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); |
| |
| WARN_ON(pdev == NULL); |
| |
| 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) |
| { |
| DISPCHECK("%s: %d\n", __func__, __LINE__); |
| |
| is_ipoh_bootup = true; |
| #ifndef CONFIG_MTK_CLKMGR |
| dpmgr_path_power_on(primary_get_dpmgr_handle(), CMDQ_DISABLE); |
| #endif |
| 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",}, |
| {} |
| }; |
| |
| 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, |
| .shutdown = mtkfb_shutdown, |
| .suspend = mtkfb_suspend, |
| .resume = mtkfb_resume, |
| .driver = { |
| .name = MTKFB_DRIVER, |
| #ifdef CONFIG_PM |
| .pm = &mtkfb_pm_ops, |
| #endif |
| .bus = &platform_bus_type, |
| .of_match_table = mtkfb_of_ids, |
| }, |
| }; |
| |
| #if 0 |
| #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 |
| #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 = fbdev->fb_pa_base; |
| unsigned int resv_size = vramsize; |
| |
| len += scnprintf(stringbuf + len, buf_len - len, |
| "|--------------------------------------------------------------------------------------|\n"); |
| /* len += scnprintf(stringbuf+len, buf_len - len, "********MTKFB Driver General Information********\n"); */ |
| len += scnprintf(stringbuf + len, buf_len - len, |
| "|Framebuffer VA:0x%lx, PA:0x%lx, MVA:0x%lx, Reserved Size:0x%08x|%d\n", va, |
| pa, mva, 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,total size=%d\n", MTK_FB_XRES, |
| MTK_FB_YRES, MTK_FB_BPP, MTK_FB_PAGES, MTK_FB_LINE, 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", |
| isAEEEnabled ? "enabled" : "disabled"); |
| |
| return len; |
| } |
| |
| |
| /* Register both the driver and the device */ |
| int __init mtkfb_init(void) |
| { |
| int r = 0; |
| |
| MSG_FUNC_ENTER(); |
| |
| if (platform_driver_register(&mtkfb_driver)) { |
| PRNERR("failed to register mtkfb driver\n"); |
| r = -ENODEV; |
| goto exit; |
| } |
| #if 0 |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| register_early_suspend(&mtkfb_early_suspend_handler); |
| #endif |
| #endif |
| /* FIXME: find definition */ |
| PanelMaster_Init(); |
| |
| DBG_Init(); |
| mtkfb_ipo_init(); |
| exit: |
| MSG_FUNC_LEAVE(); |
| return r; |
| } |
| |
| |
| static void __exit mtkfb_cleanup(void) |
| { |
| MSG_FUNC_ENTER(); |
| |
| platform_driver_unregister(&mtkfb_driver); |
| |
| #if 0 |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| unregister_early_suspend(&mtkfb_early_suspend_handler); |
| #endif |
| #endif |
| |
| /* FIXME: find definition of PanelMaster_Deinit */ |
| /* PanelMaster_Deinit(); */ |
| |
| DBG_Deinit(); |
| |
| MSG_FUNC_LEAVE(); |
| } |
| late_initcall(mtkfb_init); |
| module_exit(mtkfb_cleanup); |
| MODULE_DESCRIPTION("MEDIATEK framebuffer driver"); |
| MODULE_AUTHOR("Xuecheng Zhang <Xuecheng.Zhang@mediatek.com>"); |
| MODULE_LICENSE("GPL"); |