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介面不會呼叫,只有系統存在多個裝置的是才會呼叫該介面進行匹配;