源码路径:
kernel-3.18/arch/arm/boot/dts/mt6580.dtsi
kernel-3.18/drivers/misc/mediatek/video/common/mtkfb.c
kernel-3.18/drivers/misc/mediatek/video/mt6580/primary_display.c
kernel-3.18/drivers/misc/mediatek/video/mt6580/disp_lcm.c
kernel-3.18/drivers/misc/mediatek/lcm/mt65xx_lcm_list.c
kernel-3.18/drivers/misc/mediatek/lcm/s6d77a1a01_fwvga_dsi_vdo/s6d77a1a01_fwvga_dsi_vdo.c(这个因IC而异)
一、 添加DTS节点
mtkfb: mtkfb@0 {
compatible = "mediatek,mtkfb";
};
二、mtkfb驱动
/*
* 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 <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 <mt-plat/dma.h>
#include <linux/compat.h>
#include <linux/dma-mapping.h>
#if defined(COMMON_DISP_LOG)
#include "disp_log.h"
#include "disp_debug.h"
#include "disp_recorder.h"
#include "mtkfb_debug.h"
#else
#include "debug.h"
#include "ddp_log.h"
#include "disp_drv_log.h"
#include "display_recorder.h"
#endif
#include "disp_assert_layer.h"
#include "ddp_hal.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 "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"
#define ALIGN_TO(x, n) (((x) + ((n) - 1)) & ~((n) - 1))
/* xuecheng, remove this because we use session now */
/* mtk_dispif_info_t dispif_info[MTKFB_MAX_DISPLAY_COUNT]; */
struct notifier_block pm_nb;
unsigned int EnableVSyncLog = 0;
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 bool no_update;
#define MTK_FB_XRESV (ALIGN_TO(MTK_FB_XRES, MTK_FB_ALIGNMENT))
#define MTK_FB_YRESV (MTK_FB_YRES * 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 * MTK_FB_YRES)
#define MTK_FB_SIZEV (MTK_FB_LINE * MTK_FB_YRES * MTK_FB_PAGES)
#define CHECK_RET(expr) \
do { \
int ret = (expr); \
ASSERT(0 == ret); \
} 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) \
DISPMSG(fmt, ##arg); \
} while (0)
#define MTKFB_FUNC() \
do { \
if (mtkfb_log_on) \
DISPMSG("[Func]%s\n", __func__); \
} while (0)
#define PRNWARN(fmt, args...) pr_warn("DISP/MTKFB " fmt, ##args)
#define PRNERR(fmt, args...) pr_err("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 = 0;
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 = 0;
uint32_t dbg_backup = 0;
uint32_t dbb_backup = 0;
bool fblayer_dither_needed = false;
bool is_ipoh_bootup = false;
struct fb_info *mtkfb_fbi;
struct fb_overlay_layer fb_layer_context;
mtk_dispif_info_t dispif_info[MTKFB_MAX_DISPLAY_COUNT];
unsigned int FB_LAYER = 2;
bool is_early_suspended = false;
char mtkfb_lcm_name[256] = { 0 };
/**
* 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 = 0;
unsigned int PanDispSettingDirty = 0;
unsigned int PanDispSettingApplied = 0;
DECLARE_WAIT_QUEUE_HEAD(reg_update_wq);
unsigned int need_esd_check = 0;
DECLARE_WAIT_QUEUE_HEAD(esd_check_wq);
/* extern unsigned int disp_running; */
/* extern wait_queue_head_t disp_done_wq; */
DEFINE_MUTEX(ScreenCaptureMutex);
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;
/*
* ---------------------------------------------------------------------------
* 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();*/
DISPMSG("%s\n", __func__);
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();
DISPMSG("mtkfb_set_backlight_level:%d Start\n", level);
primary_display_setbacklight(level);
DISPMSG("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)) {
DISPERR("[FB Driver] can't get semaphore:%d\n", __LINE__);
return -ERESTARTSYS;
}
sem_flipping_cnt--;
if (down_interruptible(&sem_early_suspend)) {
DISPERR("[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)) {
DISPERR("[FB Driver] can't get semaphore:%d\n", __LINE__);
return -ERESTARTSYS;
}
sem_flipping_cnt--;
if (down_interruptible(&sem_early_suspend)) {
DISPERR("[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, 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;
case MTK_FB_FORMAT_BGRA8888:
dst->src_fmt = DISP_FORMAT_BGRA8888;
break;
case MTK_FB_FORMAT_RGBX8888:
dst->src_fmt = DISP_FORMAT_RGBX8888;
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;
DISPMSG("_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)
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
DISPMSG("_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);
DISPMSG("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);
DISPMSG("_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;
disp_session_input_config *session_input;
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;
}
DISPMSG("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_pa + offset;
vaStart = info->screen_base + offset;
vaEnd = vaStart + info->var.yres * info->fix.line_length;
session_input = kzalloc(sizeof(*session_input), GFP_KERNEL);
if (!session_input)
ASSERT(0);
/* 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 = (0 == var->blue.offset) ? DISP_FORMAT_BGRA8888 : DISP_FORMAT_RGBX8888;
br