Platform: IMX6Q
OS: Android 4.4
本文只討論lvds接口的是lcd參數匹配的過程,mipi dsi以及其他接口部分會有一點差異。
核心函數fb_find_mode(),在分析之前先了解下幾個參數。
重要參數說明:
1. ldb.c中的 ldb_modedb
[cpp] view plain copystatic struct fb_videomode ldb_modedb[] = { { "LDB-WXGA", 60, 1280, 800, 14065, 40, 40, 10, 3, 80, 10, 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_DETAILED,}, { "LDB-XGA", 60, 1024, 768, 15385, 220, 40, 21, 7, 60, 10, 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_DETAILED,}, { "LDB-1080P60", 60, 1920, 1080, 7692, 100, 40, 30, 3, 10, 2, 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_DETAILED,}, { "LDB-WSVGA", //Name 60, //refresh 1024, //xres 600, //yres 19531,//pixclock 220, 40,//left/right margin 21, 7,//upper/lower margin 60, 7,//hsync_len,vsync_len 0,//sync FB_VMODE_NONINTERLACED,//vmode FB_MODE_IS_DETAILED,}, }; static int ldb_modedb_sz = ARRAY_SIZE(ldb_modedb); 系統lvds接口支持的lcd時序參數都在此了。2. board-mx6-xxx.c中的ipuv3_fb_platform_data結構
本例是:
[cpp] view plain copystatic struct ipuv3_fb_platform_data tek_fb_data[] = { { /*fb0*/ .disp_dev = "ldb", .interface_pix_fmt = IPU_PIX_FMT_RGB24, .mode_str = "LDB-WSVGA", .default_bpp = 32, .int_clk = false, .late_init = false, }, { .disp_dev = "hdmi", .interface_pix_fmt = IPU_PIX_FMT_RGB24, .mode_str = "1920x1080M@60", .default_bpp = 32, .int_clk = false, .late_init = false, }, { .disp_dev = "lcd", .interface_pix_fmt = IPU_PIX_FMT_RGB565, .mode_str = "CLAA-WVGA", .default_bpp = 16, .int_clk = false, .late_init = false, }, { .disp_dev = "ldb", .interface_pix_fmt = IPU_PIX_FMT_RGB666, .mode_str = "LDB-VGA", .default_bpp = 16, .int_clk = false, .late_init = false, }, }; fb_find_mode()用到其mode_str里的值去ldb_modedb查找有沒有對應lcd時序參數。此mode_str其實就是后面會提到的mode_options, 格式如下:
<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or <name>[-<bpp>][@<refresh>]
所以有兩種類型:
1. 字符規則形, 如 “LDB-WXVGA”
2. 數字規則形,如"1920*1080"
具體各個參數意義可參照fb_find_mode()函數注釋。
3. kernel cmdline里設置的
本例是:
video=mxcfb0:dev=ldb,LDB-WSVGA,if=RGB24,bpp=32
它會覆蓋 tek_fb_data中的值,覆蓋規則根據mxcfb后面的值。
比如mxcfb0覆蓋tek_fb_data[0], 以此類推。
了解了參數意義之后下面就方便理解了.系統有如下調用:
[cpp] view plain copystatic int ldb_disp_init(struct mxc_dispdrv_handle *disp, struct mxc_dispdrv_setting *setting) { ...... ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str, ldb_modedb, ldb_modedb_sz, NULL, setting->default_bpp); ...... } 本例:setting->dft_mode_str為: "LDB-WSVGA"
setting->default_bpp為: 32
下面看代碼執行流程:
[cpp] view plain copy/** * fb_find_mode - finds a valid video mode * @var: frame buffer user defined part of display * @info: frame buffer info structure * @mode_option: string video mode to find * @db: video mode database * @dbsize: size of @db * @default_mode: default video mode to fall back to * @default_bpp: default color depth in bits per pixel * * Finds a suitable video mode, starting with the specified mode * in @mode_option with fallback to @default_mode. If * @default_mode fails, all modes in the video mode database will * be tried. * * Valid mode specifiers for @mode_option: * * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or * <name>[-<bpp>][@<refresh>] * * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and * <name> a string. * * If 'M' is 其實本例中fb_try_mode返回的都是0,看代碼,這里的作用基本上看成是得到當前對應的db值然后放再var中供后面的framebuffer driver使用。
kernel_imx/drivers/video/modedb.c
[cpp] view plain copystatic int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info, const struct fb_videomode *mode, unsigned int bpp) { int err = 0; DPRINTK("Trying mode %s %dx%d-%d@%d/n", mode->name ? mode->name : "noname", mode->xres, mode->yres, bpp, mode->refresh); var->xres = mode->xres; var->yres = mode->yres; var->xres_virtual = mode->xres; var->yres_virtual = mode->yres; var->xoffset = 0; var->yoffset = 0; var->bits_per_pixel = bpp; var->activate |= FB_ACTIVATE_TEST; var->pixclock = mode->pixclock; var->left_margin = mode->left_margin; var->right_margin = mode->right_margin; var->upper_margin = mode->upper_margin; var->lower_margin = mode->lower_margin; var->hsync_len = mode->hsync_len; var->vsync_len = mode->vsync_len; var->sync = mode->sync; var->vmode = mode->vmode; if (info->fbops->fb_check_var) err = info->fbops->fb_check_var(var, info); var->activate &= ~FB_ACTIVATE_TEST; return err; } kernel_imx/drivers/video/mxc/mxc_ipuv3_fb.c[cpp] view plain copystatic int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { u32 vtotal; u32 htotal; struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par; if (var->xres == 0 || var->yres == 0) { return 0; } /* fg should not bigger than bg */ if (mxc_fbi->ipu_ch == MEM_FG_SYNC) { struct fb_info *fbi_tmp; int bg_xres = 0, bg_yres = 0; int16_t pos_x, pos_y; bg_xres = var->xres; bg_yres = var->yres; fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id); if (fbi_tmp) { bg_xres = fbi_tmp->var.xres; bg_yres = fbi_tmp->var.yres; } ipu_disp_get_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch, &pos_x, &pos_y); if ((var->xres + pos_x) > bg_xres) var->xres = bg_xres - pos_x; if ((var->yres + pos_y) > bg_yres) var->yres = bg_yres - pos_y; } if (var->rotate > IPU_ROTATE_VERT_FLIP) var->rotate = IPU_ROTATE_NONE; if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) var->yres_virtual = var->yres * 3; if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && (var->bits_per_pixel != 16) && (var->bits_per_pixel != 12) && (var->bits_per_pixel != 8)) var->bits_per_pixel = 16; if (check_var_pixfmt(var)) /* Fall back to default */ bpp_to_var(var->bits_per_pixel, var); if (var->pixclock < 1000) { htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin; vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin; var->pixclock = (vtotal * htotal * 6UL) / 100UL; var->pixclock = KHZ2PICOS(var->pixclock); dev_dbg(info->device, "pixclock set for 60Hz refresh = %u ps/n", var->pixclock); } if (var->height == 0 && mxc_fbi->panel_height_mm) var->height = mxc_fbi->panel_height_mm; else if (var->height == 0) var->height = -1; if (var->width == 0 && mxc_fbi->panel_width_mm) var->width = mxc_fbi->panel_width_mm; else if (var->width == 0) var->width = -1; var->grayscale = 0; return 0; }新聞熱點
疑難解答