1. 程式人生 > >MTK LCM驅動移植

MTK LCM驅動移植

對於LCM驅動移植,一般分為三部曲

1、硬體IO口配置;

2、確保LCM背光能夠正常點亮;

3、LCM驅動移植;

硬體電路:





1、GPIO配置

開啟 mediatek\dct\DrvGen.exe 

選擇 mediatek\custom\xiaoxi\kernel\dct\dct\codegen.dws 配置檔案

配置LCM PWM引腳、RST復位引腳、DISP_PWM引腳和LCM電源控制引腳




2、背光燈

編譯燒錄後啟動系統,驗證LCM背光是否能正常點亮,否則無法繼續除錯LCD;

3、LCM驅動移植(以ili9806e為例)

(1)、在mediatek\custom\common\kernel\lcm目錄下建立ili9806目錄,將驅動檔案拷貝到驅動檔案ili9806.c到新建立的目錄中;程式碼自動將lcm軟連結到mediatek\custom\common\lk和mediatek\custom\common\uboot目錄,因此無需拷貝驅動檔案到lk和uboot中;

(2)、修改\mediatek\custom\common\kernel\lcm\mt65xx_lcm_list.c, 在lcm_driver_list 陣列中增加:
    extern LCM_DRIVER ili9806e_lcm_drv;
    #if defined(ILI9806)   
         &ili9806e_lcm_drv,  //就是ili9488.c中的LCM_DRIVER結構
    #endif

(3)、開啟mediatek\config\prj\ProjectConfig.mk修改:
    BUILD_LK=yes 
    CUSTOM_KERNEL_LCM=ili9806   //對應lcm目錄驅動的子目錄名
    CUSTOM_LK_LCM=ili9806           //對應lcm目錄驅動的子目錄名


    CUSTOM_UBOOT_LCM=ili9806   //對應lcm目錄驅動的子目錄名
    LCM_WIDTH=480
    LCM_HEIGHT=800

系統編譯的時候,編譯器會根據CUSTOM_KERNEL_LCM、CUSTOM_LK_LCM、CUSTOM_UBOOT_LCM找到mediatek\custom\common\kernel\lcm\ili9806目錄,拷貝mediatek\custom\out\pro\kernel\lcm目錄,參與系統的編譯,所以對於驅動檔名有沒命名要求;lk和uboot同理;

注:系統此時也會產生ILI9806的環境變數,這就是mt65xx_lcm_list.c中的 #if defined(ILI9806) 可以進行預編譯處理;

4、LCM驅動簡要解析

LCM_DRIVER結構表示一個LCM物件,裡邊包含LCM各項引數;

LCM_DRIVER ili9806e_drv = 
{
	.name          = "ili9806e_txd_dsi_cmd_sp13_lcm_drv",   //裝置名
	.set_util_funcs = lcm_set_util_funcs,    //獲取LCM_DRIVER結構
	.get_params     = lcm_get_params,  //獲取lcm引數
	.init           = lcm_init,      //lcm初始化函式
	.suspend        = lcm_suspend,   //lcm掛起
	.resume         = lcm_resume,    //lcm恢復
	.compare_id     = lcm_compare_id,  //裝置id匹配
};

以上函式介面是為MTK框架中的幾個重要介面;

/* 獲取裝置的LCM_DRIVER結構 */

static void lcm_set_util_funcs(const LCM_UTIL_FUNCS *util)
{
	memcpy(&lcm_util, util, sizeof(LCM_UTIL_FUNCS));
}

/* 獲取lcm各個引數 */
static void lcm_get_params(LCM_PARAMS *params)
{
	memset(params, 0, sizeof(LCM_PARAMS));  //先將LCM_PARAMS結構清空
	
	params->type = LCM_TYPE_DSI;   //lcm介面型別
	params->width = FRAME_WIDTH;   //lcm顯示寬度
	params->height = FRAME_HEIGHT; //lcm顯示高度
        /* 設定通訊模式 */
	// enable tearing-free
	params->dbi.te_mode = LCM_DBI_TE_MODE_DISABLED;
	params->dbi.te_edge_polarity = LCM_POLARITY_RISING;
        /* dsi分兩種模式,一種是cmd模式,一種是video模式 */
#if (LCM_DSI_CMD_MODE)
	params->dsi.mode = CMD_MODE;
#else
	params->dsi.mode   = SYNC_PULSE_VDO_MODE;
#endif
        /* 設定資料格式 */
	// DSI
	/* Command mode setting */
	params->dsi.LANE_NUM				= LCM_TWO_LANE;   //兩通道MIPI
	//The following defined the fomat for data coming from LCD engine.
	params->dsi.data_format.color_order = LCM_COLOR_ORDER_RGB;   
	params->dsi.data_format.trans_seq   = LCM_DSI_TRANS_SEQ_MSB_FIRST;
	params->dsi.data_format.padding     = LCM_DSI_PADDING_ON_LSB;
	params->dsi.data_format.format      = LCM_DSI_FORMAT_RGB888; 

	// Highly depends on LCD driver capability.
	// Not support in MT6573
	params->dsi.packet_size = 256;

	// Video mode setting
	params->dsi.intermediat_buffer_num = 0;

	params->dsi.PS = LCM_PACKED_PS_24BIT_RGB888;
	params->dsi.word_count = 480 * 3;
        /* 垂直引數設定 */
	params->dsi.vertical_sync_active = 4;      //垂直同步訊號的寬度
	params->dsi.vertical_backporch = 16;//10   //垂直同步訊號的後沿
	params->dsi.vertical_frontporch = 20;//8   //垂直同步訊號的前沿
	params->dsi.vertical_active_line = FRAME_HEIGHT;
        /* 水平引數設定 */
	params->dsi.horizontal_sync_active = 10;   //水平同步訊號的寬度
	params->dsi.horizontal_backporch = 50;     //水平同步訊號的後沿
	params->dsi.horizontal_frontporch = 60;    //水平同步訊號的前沿
	params->dsi.horizontal_active_pixel = FRAME_WIDTH;
        /* 時鐘頻率 */
	params->dsi.PLL_CLOCK= 200;
}

//復位引腳
#define SET_RESET_PIN(v) (lcm_util.set_reset_pin((v)))     //這裡就會直接使用GPIO_LCD_RST硬引腳
//延時函式   
#define UDELAY(n) (lcm_util.udelay(n))  
#define MDELAY(n) (lcm_util.mdelay(n))  

/* 資料傳輸介面 */
//long packet 操作介面
#define dsi_set_cmdq_V3(para_tbl, size, force_update)       lcm_util.dsi_set_cmdq_V3(para_tbl, size, force_update)     //para_tbl:LCM_setting_table結構, size:大小, force_update:強制更新標誌
#define dsi_set_cmdq_V2(cmd, count, ppara, force_update)     lcm_util.dsi_set_cmdq_V2(cmd, count, ppare, force_update) //cmd:命令, count:大小, ppara:引數,force_update:強制更新標誌
//short packet 操作介面
#define dsi_set_cmdq(pdata, queue_size, force_update)       lcm_util.dsi_set_cmdq(pdata, queue_size, force_update)  


//讀寫暫存器等操作
#define write_cmd(cmd)                      lcm_util.dsi_write_cmd(cmd)  
#define write_regs(addr, pdata, byte_nums)          lcm_util.dsi_write_regs(addr, pdata, bytes_nums)  
#define read_reg(cmd)                       lcm_util.dsi_dcs_read_lcm_reg(cmd)  
#define read_reg_v2(cmd, buffer, buffer_size)           lcm_util.dsi_dcs_read_lcm_reg_v2(cmd, buffer, buffer_size)  


/* 初始化引數及函式介面 */

static struct LCM_setting_table lcm_initialization_setting[] = {
        /* 資料格式:命令,資料個數,資料 */  //命令一般是對應暫存器地址
	{0xFF, 5,{0xFF,0x98,0x06,0x04,0x01}},	
	{0x08, 1, {0x10}},
	{0x21, 1, {0x01}},
	{0x30, 1, {0x02}},
	{0x31, 1, {0x02}},
	{0x40, 1, {0x16}},	
	{0x41, 1, {0x22}},	
	 ......
	{0x53, 1, {0x1A}}, //10   
	{0xFF, 5,{0xFF,0x98,0x06,0x04,0x07}},
	{0x17, 1, {0x12}}, //22
	{0x02, 1, {0x77}},
	{0xFF, 5,{0xFF,0x98,0x06,0x04,0x00}},
 	{0x35,1,	{0x00}},
 	{0x36,1,	{0x03}},  //翻轉180度
	{0x11,	1,	{0x00}},
	{REGFLAG_DELAY, 120, {}},
	{0x29,	1,	{0x00}},
	{REGFLAG_DELAY, 50, {}},
	{REGFLAG_END_OF_TABLE, 0x00, {}}  //資料結束必須使用REGFLAG_END_OF_TABLE
};

static void lcm_init(void)
{
        /* 復位 */
	SET_RESET_PIN(1);	
	MDELAY(10);
	SET_RESET_PIN(0);
	/* Third change Lava */
	MDELAY(10);//10 
	SET_RESET_PIN(1);
	MDELAY(120);	// 150
        /* 初始化資料 */
	push_table(lcm_initialization_setting, sizeof(lcm_initialization_setting) / sizeof(struct LCM_setting_table), 1);
}

/* 裝置掛起 */
static void lcm_suspend(void)
{
#ifdef BUILD_LK   
	printf("%s, ALS/PS bbbbbbbbbbbbbbb \n", __func__); 
#else
	printk("%s, ALS/PS bbbbbbbbbbbbbb  \n", __func__);   
#endif	

	push_table(lcm_deep_sleep_mode_in_setting, sizeof(lcm_deep_sleep_mode_in_setting) / sizeof(struct LCM_setting_table), 1);
	SET_RESET_PIN(0);
	MDELAY(20);//10 
	SET_RESET_PIN(1);
	MDELAY(50);
}

 因為lcm驅動被對映到lk層,在lk層只能使用printf進行除錯,但在kernel層中只能printk進行列印除錯,所以可以使用巨集進行BUILD_LK區分;

掛起的機制一般有兩種:簡單睡眠或深度睡眠;

簡單睡眠:裝置還處於工作狀態,可以被喚醒,但是此時也會存在待機功耗等問題;

深度睡眠:裝置處於休眠狀態,基本處於不工作狀態,因此無法被喚醒;

一般程式設計都是使用深度睡眠,在喚醒時進行重新初始化;


/* 裝置恢復 */
static void lcm_resume(void)
{
	lcm_init();
	//push_table(lcm_sleep_out_setting, sizeof(lcm_sleep_out_setting) / sizeof(struct LCM_setting_table), 1);
}
重新初始化裝置

/* 裝置id匹配 */

static unsigned int lcm_compare_id()
{
    unsigned int array[4];
    unsigned char buffer[4] = {0,0,0,0};
    unsigned char id_high=0;
    unsigned char id_low=0;
    unsigned char id_low0=0;
    unsigned int id=0;

    /* 先進行復位操作 */
    SET_RESET_PIN(1);
    MDELAY(10);
    SET_RESET_PIN(0);
    MDELAY(10);
    SET_RESET_PIN(1);
    MDELAY(200);
    
//*************Enable CMD2 Page1  *******************//
    array[0]=0x00063902;
    array[1]=0x0698ffff;
    array[2]=0x00000104;
    dsi_set_cmdq(array, 3, 1);

    array[0] = 0x00043700;
    dsi_set_cmdq(array, 1, 1);
    MDELAY(10);
    read_reg_v2(0x00, buffer, 4);
    id_high = buffer[0];     //98

    array[0] = 0x00043700;
    dsi_set_cmdq(array, 1, 1);
    MDELAY(10);
    read_reg_v2(0x01, buffer, 4);
    id_low = buffer[0];     //06

    array[0] = 0x00043700;
    dsi_set_cmdq(array, 1, 1);
    MDELAY(10);
    read_reg_v2(0x02, buffer, 4);
    id_low0 = buffer[0];   //04

    id = (id_high<<16) | (id_low<<8)|id_low0;

#ifdef BUILD_LK
    printf("ILI9806e:id2=%x.\n",id);
    printf("ILI9806e:id4=%x.\n",id_high);
    printf("ILI9806e:id5=%x.\n",id_low);
    printf("ILI9806e:id5=%x.\n",id_low0);
#else
    printk("ILI9806e:id=%x.\n",id);
    printk("ILI9806e:id_high=%x.\n",id_high);
    printk("ILI9806e:id_low=%x.\n",id_low);
    printk("ILI9806e:id_low=%x.\n",id_low0);
#endif
		   
    return (0x980604 == id) ? 1 : 0;
}
資料編寫格式是遵循MIPI協議進行編寫的

注:如果系統只配置一個lcm裝置,lcm_compare_id介面不會呼叫,只有系統存在多個裝置的是才會呼叫該介面進行匹配;