Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 2006 lines (1685 sloc) 52.9 KB
#include "disp_lcd.h"
#include "../dev_disp.h"
#include <linux/backlight.h>
struct disp_lcd_private_data
{
disp_lcd_flow open_flow;
disp_lcd_flow close_flow;
disp_panel_para panel_info;
panel_extend_para panel_extend_info;
disp_lcd_cfg lcd_cfg;
disp_lcd_panel_fun lcd_panel_fun;
bool enabling;
bool disabling;
u32 irq_no;
u32 irq_no_dsi;
u32 irq_no_edp;
u32 enabled;
u32 power_enabled;
u32 bl_need_enabled;
struct {
uintptr_t dev;
u32 channel;
u32 polarity;
u32 period_ns;
u32 duty_ns;
u32 enabled;
}pwm_info;
struct backlight_device *backlight;
struct clk *clk;
struct clk *lvds_clk;
struct clk *dsi_clk0;
struct clk *dsi_clk1;
struct clk *edp_clk;
struct clk *clk_parent;
};
#if defined(__LINUX_PLAT__)
static spinlock_t lcd_data_lock;
#else
static int lcd_data_lock;
#endif
static struct disp_device *lcds = NULL;
static struct disp_lcd_private_data *lcd_private;
s32 disp_lcd_set_bright(struct disp_device *lcd, u32 bright);
s32 disp_lcd_get_bright(struct disp_device *lcd);
struct disp_device* disp_get_lcd(u32 disp)
{
u32 num_screens;
num_screens = bsp_disp_feat_get_num_screens();
if (disp >= num_screens || !bsp_disp_feat_is_supported_output_types(disp, DISP_OUTPUT_TYPE_LCD)) {
DE_INF("disp %d not support lcd output\n", disp);
return NULL;
}
return &lcds[disp];
}
static struct disp_lcd_private_data *disp_lcd_get_priv(struct disp_device *lcd)
{
if (NULL == lcd) {
DE_WRN("param is NULL!\n");
return NULL;
}
if (!bsp_disp_feat_is_supported_output_types(lcd->disp, DISP_OUTPUT_TYPE_LCD)) {
DE_INF("disp %d not support lcd output\n", lcd->disp);
return NULL;
}
return &lcd_private[lcd->disp];
}
static s32 disp_lcd_is_used(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
s32 ret = 0;
if ((NULL == lcd) || (NULL == lcdp)) {
ret = 0;
} else {
if (bsp_disp_feat_is_supported_output_types(lcd->disp, DISP_OUTPUT_TYPE_LCD))
ret = (s32)lcdp->lcd_cfg.lcd_used;
}
return ret;
}
static s32 lcd_parse_panel_para(u32 disp, disp_panel_para * info)
{
s32 ret = 0;
char primary_key[25];
s32 value = 0;
sprintf(primary_key, "lcd%d", disp);
memset(info, 0, sizeof(disp_panel_para));
ret = disp_sys_script_get_item(primary_key, "lcd_x", &value, 1);
if (ret == 1)
{
info->lcd_x = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_y", &value, 1);
if (ret == 1)
{
info->lcd_y = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_width", &value, 1);
if (ret == 1)
{
info->lcd_width = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_height", &value, 1);
if (ret == 1)
{
info->lcd_height = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_dclk_freq", &value, 1);
if (ret == 1)
{
info->lcd_dclk_freq = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_pwm_used", &value, 1);
if (ret == 1)
{
info->lcd_pwm_used = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_pwm_ch", &value, 1);
if (ret == 1)
{
info->lcd_pwm_ch = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_pwm_freq", &value, 1);
if (ret == 1)
{
info->lcd_pwm_freq = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_pwm_pol", &value, 1);
if (ret == 1)
{
info->lcd_pwm_pol = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_if", &value, 1);
if (ret == 1)
{
info->lcd_if = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_hbp", &value, 1);
if (ret == 1)
{
info->lcd_hbp = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_ht", &value, 1);
if (ret == 1)
{
info->lcd_ht = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_vbp", &value, 1);
if (ret == 1)
{
info->lcd_vbp = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_vt", &value, 1);
if (ret == 1)
{
info->lcd_vt = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_hv_if", &value, 1);
if (ret == 1)
{
info->lcd_hv_if = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_vspw", &value, 1);
if (ret == 1)
{
info->lcd_vspw = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_hspw", &value, 1);
if (ret == 1)
{
info->lcd_hspw = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_lvds_if", &value, 1);
if (ret == 1)
{
info->lcd_lvds_if = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_lvds_mode", &value, 1);
if (ret == 1)
{
info->lcd_lvds_mode = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_lvds_colordepth", &value, 1);
if (ret == 1)
{
info->lcd_lvds_colordepth= value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_lvds_io_polarity", &value, 1);
if (ret == 1)
{
info->lcd_lvds_io_polarity = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_cpu_if", &value, 1);
if (ret == 1)
{
info->lcd_cpu_if = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_cpu_te", &value, 1);
if (ret == 1)
{
info->lcd_cpu_te = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_frm", &value, 1);
if (ret == 1)
{
info->lcd_frm = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_dsi_if", &value, 1);
if (ret == 1)
{
info->lcd_dsi_if = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_dsi_lane", &value, 1);
if (ret == 1)
{
info->lcd_dsi_lane = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_dsi_format", &value, 1);
if (ret == 1)
{
info->lcd_dsi_format = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_dsi_eotp", &value, 1);
if (ret == 1)
{
info->lcd_dsi_eotp = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_dsi_te", &value, 1);
if (ret == 1)
{
info->lcd_dsi_te = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_edp_rate", &value, 1);
if (ret == 1)
{
info->lcd_edp_rate = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_edp_lane", &value, 1);
if (ret == 1)
{
info->lcd_edp_lane= value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_edp_colordepth", &value, 1);
if (ret == 1)
{
info->lcd_edp_colordepth = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_edp_fps", &value, 1);
if (ret == 1)
{
info->lcd_edp_fps = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_hv_clk_phase", &value, 1);
if (ret == 1)
{
info->lcd_hv_clk_phase = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_hv_sync_polarity", &value, 1);
if (ret == 1)
{
info->lcd_hv_sync_polarity = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_hv_srgb_seq", &value, 1);
if (ret == 1)
{
info->lcd_hv_srgb_seq = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_rb_swap", &value, 1);
if (ret == 1)
{
info->lcd_rb_swap = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_hv_syuv_seq", &value, 1);
if (ret == 1)
{
info->lcd_hv_syuv_seq = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_hv_syuv_fdly", &value, 1);
if (ret == 1)
{
info->lcd_hv_syuv_fdly = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_gamma_en", &value, 1);
if (ret == 1)
{
info->lcd_gamma_en = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_cmap_en", &value, 1);
if (ret == 1)
{
info->lcd_cmap_en = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_xtal_freq", &value, 1);
if (ret == 1)
{
info->lcd_xtal_freq = value;
}
ret = disp_sys_script_get_item(primary_key, "lcd_size", (int*)info->lcd_size, 2);
ret = disp_sys_script_get_item(primary_key, "lcd_model_name", (int*)info->lcd_model_name, 2);
return 0;
}
#if 0
static void lcd_panel_parameter_check(u32 disp, struct disp_device* lcd)
{
disp_panel_para* info;
u32 cycle_num = 1;
u32 Lcd_Panel_Err_Flag = 0;
u32 Lcd_Panel_Wrn_Flag = 0;
u32 Disp_Driver_Bug_Flag = 0;
u32 lcd_fclk_frq;
u32 lcd_clk_div;
s32 ret = 0;
char primary_key[20];
s32 value = 0;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return ;
}
if (!disp_al_query_lcd_mod(lcd->disp))
return;
sprintf(primary_key, "lcd%d", lcd->disp);
ret = disp_sys_script_get_item(primary_key, "lcd_used", &value, 1);
if (ret != 0 ) {
DE_WRN("get lcd%dpara lcd_used fail\n", lcd->disp);
return;
} else {
if (value != 1) {
DE_WRN("lcd%dpara is not used\n", lcd->disp);
return;
}
}
info = &(lcdp->panel_info);
if (NULL == info) {
DE_WRN("NULL hdl!\n");
return;
}
if (info->lcd_if == 0 && info->lcd_hv_if == 8)
cycle_num = 3;
else if (info->lcd_if == 0 && info->lcd_hv_if == 10)
cycle_num = 3;
else if (info->lcd_if == 0 && info->lcd_hv_if == 11)
cycle_num = 4;
else if (info->lcd_if == 0 && info->lcd_hv_if == 12)
cycle_num = 4;
else if (info->lcd_if == 1 && info->lcd_cpu_if == 2)
cycle_num = 3;
else if (info->lcd_if == 1 && info->lcd_cpu_if == 4)
cycle_num = 2;
else if (info->lcd_if == 1 && info->lcd_cpu_if == 6)
cycle_num = 2;
else if (info->lcd_if == 1 && info->lcd_cpu_if == 10)
cycle_num = 2;
else if (info->lcd_if == 1 && info->lcd_cpu_if == 12)
cycle_num = 3;
else if (info->lcd_if == 1 && info->lcd_cpu_if == 14)
cycle_num = 2;
else
cycle_num = 1;
if (info->lcd_hbp > info->lcd_hspw)
{
;
}
else
{
Lcd_Panel_Err_Flag |= BIT0;
}
if (info->lcd_vbp > info->lcd_vspw)
{
;
}
else
{
Lcd_Panel_Err_Flag |= BIT1;
}
if (info->lcd_ht >= (info->lcd_hbp+info->lcd_x*cycle_num+4))
{
;
}
else
{
Lcd_Panel_Err_Flag |= BIT2;
}
if ((info->lcd_vt) >= (info->lcd_vbp+info->lcd_y + 2))
{
;
}
else
{
Lcd_Panel_Err_Flag |= BIT3;
}
lcd_clk_div = disp_al_lcd_get_clk_div(disp);
if (lcd_clk_div >= 6)
{
;
}
else if (lcd_clk_div >=2)
{
if ((info->lcd_hv_clk_phase == 1) && (info->lcd_hv_clk_phase == 3))
{
Lcd_Panel_Err_Flag |= BIT10;
}
}
else
{
Disp_Driver_Bug_Flag |= 1;
}
if ((info->lcd_if == 1 && info->lcd_cpu_if == 0) || (info->lcd_if == 1 && info->lcd_cpu_if == 10)
|| (info->lcd_if == 1 && info->lcd_cpu_if == 12) ||(info->lcd_if == 3 && info->lcd_lvds_colordepth == 1))
{
if (info->lcd_frm != 1)
Lcd_Panel_Wrn_Flag |= BIT0;
}
else if (info->lcd_if == 1 && ((info->lcd_cpu_if == 2) || (info->lcd_cpu_if == 4) || (info->lcd_cpu_if == 6)
|| (info->lcd_cpu_if == 8) || (info->lcd_cpu_if == 14)))
{
if (info->lcd_frm != 2)
Lcd_Panel_Wrn_Flag |= BIT1;
}
lcd_fclk_frq = (info->lcd_dclk_freq * 1000 * 1000) / ((info->lcd_vt) * info->lcd_ht);
if (lcd_fclk_frq < 50 || lcd_fclk_frq > 70)
{
Lcd_Panel_Wrn_Flag |= BIT2;
}
if (Lcd_Panel_Err_Flag != 0 || Lcd_Panel_Wrn_Flag != 0)
{
if (Lcd_Panel_Err_Flag != 0)
{
u32 i;
for (i = 0; i < 200; i++)
{
//OSAL_PRINTF("*** Lcd in danger...\n");
}
}
OSAL_PRINTF("*****************************************************************\n");
OSAL_PRINTF("***\n");
OSAL_PRINTF("*** LCD Panel Parameter Check\n");
OSAL_PRINTF("***\n");
OSAL_PRINTF("*** by guozhenjie\n");
OSAL_PRINTF("***\n");
OSAL_PRINTF("*****************************************************************\n");
OSAL_PRINTF("*** \n");
OSAL_PRINTF("*** Interface:");
if (info->lcd_if == 0 && info->lcd_hv_if == 0)
{OSAL_PRINTF("*** Parallel HV Panel\n");}
else if (info->lcd_if == 0 && info->lcd_hv_if == 8)
{OSAL_PRINTF("*** Serial HV Panel\n");}
else if (info->lcd_if == 0 && info->lcd_hv_if == 10)
{OSAL_PRINTF("*** Dummy RGB HV Panel\n");}
else if (info->lcd_if == 0 && info->lcd_hv_if == 11)
{OSAL_PRINTF("*** RGB Dummy HV Panel\n");}
else if (info->lcd_if == 0 && info->lcd_hv_if == 12)
{OSAL_PRINTF("*** Serial YUV Panel\n");}
else if (info->lcd_if == 3 && info->lcd_lvds_colordepth== 0)
{OSAL_PRINTF("*** 24Bit LVDS Panel\n");}
else if (info->lcd_if == 3 && info->lcd_lvds_colordepth== 1)
{OSAL_PRINTF("*** 18Bit LVDS Panel\n");}
else if ((info->lcd_if == 1) && (info->lcd_cpu_if == 0 || info->lcd_cpu_if == 10 || info->lcd_cpu_if == 12))
{OSAL_PRINTF("*** 18Bit CPU Panel\n");}
else if ((info->lcd_if == 1) && (info->lcd_cpu_if == 2 || info->lcd_cpu_if == 4 ||
info->lcd_cpu_if == 6 || info->lcd_cpu_if == 8 || info->lcd_cpu_if == 14))
{OSAL_PRINTF("*** 16Bit CPU Panel\n");}
else
{
OSAL_PRINTF("\n");
OSAL_PRINTF("*** lcd_if: %d\n",info->lcd_if);
OSAL_PRINTF("*** lcd_hv_if: %d\n",info->lcd_hv_if);
OSAL_PRINTF("*** lcd_cpu_if: %d\n",info->lcd_cpu_if);
}
if (info->lcd_frm == 0)
{OSAL_PRINTF("*** Lcd Frm Disable\n");}
else if (info->lcd_frm == 1)
{OSAL_PRINTF("*** Lcd Frm to RGB666\n");}
else if (info->lcd_frm == 2)
{OSAL_PRINTF("*** Lcd Frm to RGB565\n");}
OSAL_PRINTF("*** \n");
OSAL_PRINTF("*** Timing:\n");
OSAL_PRINTF("*** lcd_x: %d\n", info->lcd_x);
OSAL_PRINTF("*** lcd_y: %d\n", info->lcd_y);
OSAL_PRINTF("*** lcd_ht: %d\n", info->lcd_ht);
OSAL_PRINTF("*** lcd_hbp: %d\n", info->lcd_hbp);
OSAL_PRINTF("*** lcd_vt: %d\n", info->lcd_vt);
OSAL_PRINTF("*** lcd_vbp: %d\n", info->lcd_vbp);
OSAL_PRINTF("*** lcd_hspw: %d\n", info->lcd_hspw);
OSAL_PRINTF("*** lcd_vspw: %d\n", info->lcd_vspw);
OSAL_PRINTF("*** lcd_frame_frq: %dHz\n", lcd_fclk_frq);
OSAL_PRINTF("*** \n");
if (Lcd_Panel_Err_Flag & BIT0)
{OSAL_PRINTF("*** Err01: Violate \"lcd_hbp > lcd_hspw\"\n");}
if (Lcd_Panel_Err_Flag & BIT1)
{OSAL_PRINTF("*** Err02: Violate \"lcd_vbp > lcd_vspw\"\n");}
if (Lcd_Panel_Err_Flag & BIT2)
{OSAL_PRINTF("*** Err03: Violate \"lcd_ht >= (lcd_hbp+lcd_x*%d+4)\"\n", cycle_num);}
if (Lcd_Panel_Err_Flag & BIT3)
{OSAL_PRINTF("*** Err04: Violate \"(lcd_vt) >= (lcd_vbp+lcd_y+2)\"\n");}
if (Lcd_Panel_Err_Flag & BIT10)
{OSAL_PRINTF("*** Err10: Violate \"lcd_hv_clk_phase\",use \"0\" or \"2\"");}
if (Lcd_Panel_Wrn_Flag & BIT0)
{OSAL_PRINTF("*** WRN01: Recommend \"lcd_frm = 1\"\n");}
if (Lcd_Panel_Wrn_Flag & BIT1)
{OSAL_PRINTF("*** WRN02: Recommend \"lcd_frm = 2\"\n");}
if (Lcd_Panel_Wrn_Flag & BIT2)
{OSAL_PRINTF("*** WRN03: Recommend \"lcd_dclk_frq = %d\"\n",
((info->lcd_vt) * info->lcd_ht) * 60 / (1000 * 1000));}
OSAL_PRINTF("*** \n");
if (Lcd_Panel_Err_Flag != 0)
{
u32 image_base_addr;
u32 reg_value = 0;
image_base_addr = DE_Get_Reg_Base(disp);
sys_put_wvalue(image_base_addr+0x804, 0xffff00ff);//set background color
reg_value = sys_get_wvalue(image_base_addr + 0x800);
sys_put_wvalue(image_base_addr+0x800, reg_value & 0xfffff0ff);//close all layer
mdelay(2000);
sys_put_wvalue(image_base_addr + 0x804, 0x00000000);//set background color
sys_put_wvalue(image_base_addr + 0x800, reg_value);//open layer
OSAL_PRINTF("*** Try new parameters,you can make it pass!\n");
}
OSAL_PRINTF("*** LCD Panel Parameter Check End\n");
OSAL_PRINTF("*****************************************************************\n");
}
}
#endif
static void lcd_get_sys_config(u32 disp, disp_lcd_cfg *lcd_cfg)
{
disp_gpio_set_t *gpio_info;
int value = 1;
char primary_key[20], sub_name[25];
int i = 0;
int ret;
sprintf(primary_key, "lcd%d", disp);
//lcd_used
ret = disp_sys_script_get_item(primary_key, "lcd_used", &value, 1);
if (ret == 1)
{
lcd_cfg->lcd_used = value;
}
if (lcd_cfg->lcd_used == 0) //no need to get lcd config if lcd_used eq 0
return ;
//lcd_bl_en
lcd_cfg->lcd_bl_en_used = 0;
gpio_info = &(lcd_cfg->lcd_bl_en);
ret = disp_sys_script_get_item(primary_key,"lcd_bl_en", (int *)gpio_info, 3);
if (ret == 3)
{
lcd_cfg->lcd_bl_en_used = 1;
}
sprintf(sub_name, "lcd_bl_en_power");
ret = disp_sys_script_get_item(primary_key, sub_name, (int *)lcd_cfg->lcd_bl_en_power, 2);
//lcd fix power
for (i=0; i<LCD_POWER_NUM; i++)
{
if (i==0)
sprintf(sub_name, "lcd_fix_power");
else
sprintf(sub_name, "lcd_fix_power%d", i);
lcd_cfg->lcd_power_used[i] = 0;
ret = disp_sys_script_get_item(primary_key,sub_name, (int *)(lcd_cfg->lcd_fix_power[i]), 2);
if (ret == 2) {
/* str */
lcd_cfg->lcd_fix_power_used[i] = 1;
}
}
//lcd_power
for (i=0; i<LCD_POWER_NUM; i++)
{
if (i==0)
sprintf(sub_name, "lcd_power");
else
sprintf(sub_name, "lcd_power%d", i);
lcd_cfg->lcd_power_used[i] = 0;
ret = disp_sys_script_get_item(primary_key,sub_name, (int *)(lcd_cfg->lcd_power[i]), 2);
if (ret == 2) {
/* str */
lcd_cfg->lcd_power_used[i] = 1;
}
}
//lcd_gpio
for (i=0; i<4; i++)
{
sprintf(sub_name, "lcd_gpio_%d", i);
gpio_info = &(lcd_cfg->lcd_gpio[i]);
ret = disp_sys_script_get_item(primary_key,sub_name, (int *)gpio_info, 3);
if (ret == 3)
{
lcd_cfg->lcd_gpio_used[i]= 1;
}
}
//lcd_gpio_scl,lcd_gpio_sda
gpio_info = &(lcd_cfg->lcd_gpio[LCD_GPIO_SCL]);
ret = disp_sys_script_get_item(primary_key,"lcd_gpio_scl", (int *)gpio_info, 3);
if (ret == 3)
{
lcd_cfg->lcd_gpio_used[LCD_GPIO_SCL]= 1;
}
gpio_info = &(lcd_cfg->lcd_gpio[LCD_GPIO_SDA]);
ret = disp_sys_script_get_item(primary_key,"lcd_gpio_sda", (int *)gpio_info, 3);
if (ret == 3)
{
lcd_cfg->lcd_gpio_used[LCD_GPIO_SDA]= 1;
}
for (i = 0; i < LCD_GPIO_REGU_NUM; i++)
{
sprintf(sub_name, "lcd_gpio_power%d", i);
ret = disp_sys_script_get_item(primary_key, sub_name, (int *)lcd_cfg->lcd_gpio_power[i], 2);
}
for (i=0; i<LCD_GPIO_REGU_NUM; i++) {
if (0==i)
sprintf(sub_name, "lcd_pin_power");
else
sprintf(sub_name, "lcd_pin_power%d", i);
ret = disp_sys_script_get_item(primary_key, sub_name, (int *)lcd_cfg->lcd_pin_power[i], 2);
}
//backlight adjust
for (i = 0; i < 101; i++) {
sprintf(sub_name, "lcd_bl_%d_percent", i);
lcd_cfg->backlight_curve_adjust[i] = 0;
if (i == 100)
lcd_cfg->backlight_curve_adjust[i] = 255;
ret = disp_sys_script_get_item(primary_key, sub_name, &value, 1);
if (ret == 1) {
value = (value > 100)? 100:value;
value = value * 255 / 100;
lcd_cfg->backlight_curve_adjust[i] = value;
}
}
sprintf(sub_name, "lcd_backlight");
ret = disp_sys_script_get_item(primary_key, sub_name, &value, 1);
if (ret == 1) {
value = (value > 256)? 256:value;
lcd_cfg->backlight_bright = value;
} else {
lcd_cfg->backlight_bright = 197;
}
}
#include <linux/clk-provider.h>
static s32 lcd_clk_init(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return -1;
}
DE_INF("lcd %d clk init\n", lcd->disp);
lcdp->clk_parent = clk_get_parent(lcdp->clk);
return DIS_SUCCESS;
}
static s32 lcd_clk_exit(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return -1;
}
if (lcdp->clk_parent)
clk_put(lcdp->clk_parent);
return DIS_SUCCESS;
}
static s32 lcd_clk_config(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
struct lcd_clk_info clk_info;
unsigned long pll_rate, lcd_rate, dclk_rate, dsi_rate = 0;//hz
unsigned long pll_rate_set, lcd_rate_set, dclk_rate_set, dsi_rate_set = 0;//hz
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return -1;
}
memset(&clk_info, 0, sizeof(struct lcd_clk_info));
disp_al_lcd_get_clk_info(lcd->disp, &clk_info, &lcdp->panel_info);
dclk_rate = lcdp->panel_info.lcd_dclk_freq * 1000000;//Mhz -> hz
if (LCD_IF_DSI == lcdp->panel_info.lcd_if) {
lcd_rate = dclk_rate * clk_info.dsi_div;
pll_rate = lcd_rate * clk_info.lcd_div;
} else {
lcd_rate = dclk_rate * clk_info.tcon_div;
pll_rate = lcd_rate * clk_info.lcd_div;
}
dsi_rate = pll_rate / clk_info.dsi_div;
if (lcdp->clk_parent) {
clk_set_rate(lcdp->clk_parent, pll_rate);
pll_rate_set = clk_get_rate(lcdp->clk_parent);
}
if (clk_info.lcd_div)
lcd_rate_set = pll_rate_set / clk_info.lcd_div;
clk_set_rate(lcdp->clk, lcd_rate_set);
lcd_rate_set = clk_get_rate(lcdp->clk);
if (LCD_IF_DSI == lcdp->panel_info.lcd_if) {
dsi_rate_set = pll_rate_set / clk_info.dsi_div;
dsi_rate_set = (0 == clk_info.dsi_rate)? dsi_rate_set:clk_info.dsi_rate;
clk_set_rate(lcdp->dsi_clk0, dsi_rate_set);
//disp_sys_clk_set_rate(lcdp->dsi_clk1, dsi_rate_set);//FIXME, dsi clk0 = dsi clk1(rate)
}
dclk_rate_set = lcd_rate_set / clk_info.tcon_div;
if ((pll_rate_set != pll_rate) || (lcd_rate_set != lcd_rate)
|| (dclk_rate_set != dclk_rate)) {
DE_WRN("disp %d, clk: pll(%ld),clk(%ld),dclk(%ld) dsi_rate(%ld)\n clk real:pll(%ld),clk(%ld),dclk(%ld) dsi_rate(%ld)\n",
lcd->disp, pll_rate, lcd_rate, dclk_rate, dsi_rate, pll_rate_set, lcd_rate_set, dclk_rate_set, dsi_rate_set);
}
return 0;
}
static s32 lcd_clk_enable(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
int ret;
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return -1;
}
lcd_clk_config(lcd);
ret = clk_prepare_enable(lcdp->clk);
if (0 != ret) {
DE_WRN("fail enable lcd's clock!\n");
goto exit;
}
if (LCD_IF_LVDS == lcdp->panel_info.lcd_if) {
ret = clk_prepare_enable(lcdp->lvds_clk);
if (0 != ret) {
DE_WRN("fail enable lvds's clock!\n");
goto exit;
}
} else if (LCD_IF_DSI == lcdp->panel_info.lcd_if) {
ret = clk_prepare_enable(lcdp->dsi_clk0);
if (0 != ret) {
DE_WRN("fail enable dsi's clock0!\n");
goto exit;
}
ret = clk_prepare_enable(lcdp->dsi_clk1);
if (0 != ret) {
DE_WRN("fail enable dsi's clock1!\n");
goto exit;
}
} else if (LCD_IF_EDP == lcdp->panel_info.lcd_if) {
ret = clk_prepare_enable(lcdp->edp_clk);
if (0 != ret) {
DE_WRN("fail enable edp's clock!\n");
goto exit;
}
}
exit:
return ret;
}
static s32 lcd_clk_disable(struct disp_device* lcd)
{ struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return -1;
}
if (LCD_IF_LVDS == lcdp->panel_info.lcd_if) {
clk_disable(lcdp->lvds_clk);
} else if (LCD_IF_DSI == lcdp->panel_info.lcd_if) {
clk_disable(lcdp->dsi_clk1);
clk_disable(lcdp->dsi_clk0);
} else if (LCD_IF_EDP == lcdp->panel_info.lcd_if) {
clk_disable(lcdp->edp_clk);
}
clk_disable(lcdp->clk);
return DIS_SUCCESS;
}
static s32 disp_lcd_tcon_enable(struct disp_device *lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return -1;
}
return disp_al_lcd_enable(lcd->disp, &lcdp->panel_info);
}
static s32 disp_lcd_tcon_disable(struct disp_device *lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return -1;
}
return disp_al_lcd_disable(lcd->disp, &lcdp->panel_info);
}
static s32 disp_lcd_pin_cfg(struct disp_device *lcd, u32 bon)
{
int i;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
char dev_name[25];
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
DE_INF("lcd %d pin config, state %s, %d\n", lcd->disp, (bon)? "on":"off", bon);
//io-pad
if (bon == 1) {
for (i=0; i<LCD_GPIO_REGU_NUM; i++) {
if (!((!strcmp(lcdp->lcd_cfg.lcd_pin_power[i], "")) || (!strcmp(lcdp->lcd_cfg.lcd_pin_power[i], "none"))))
disp_sys_power_enable(lcdp->lcd_cfg.lcd_pin_power[i]);
}
}
sprintf(dev_name, "lcd%d", lcd->disp);
disp_sys_pin_set_state(dev_name, (1==bon)? DISP_PIN_STATE_ACTIVE:DISP_PIN_STATE_SLEEP);
disp_al_lcd_io_cfg(lcd->disp, bon, &lcdp->panel_info);
if (bon == 0) {
for (i=LCD_GPIO_REGU_NUM-1; i>=0; i--) {
if (!((!strcmp(lcdp->lcd_cfg.lcd_pin_power[i], "")) || (!strcmp(lcdp->lcd_cfg.lcd_pin_power[i], "none"))))
disp_sys_power_disable(lcdp->lcd_cfg.lcd_pin_power[i]);
}
}
return DIS_SUCCESS;
}
static s32 disp_lcd_pwm_enable(struct disp_device *lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
if (disp_lcd_is_used(lcd) && lcdp->pwm_info.dev) {
disp_sys_pwm_set_polarity(lcdp->pwm_info.dev, lcdp->pwm_info.polarity);
return disp_sys_pwm_enable(lcdp->pwm_info.dev);
}
DE_WRN("pwm device hdl is NULL\n");
return DIS_FAIL;
}
static s32 disp_lcd_pwm_disable(struct disp_device *lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
if (disp_lcd_is_used(lcd) && lcdp->pwm_info.dev) {
return disp_sys_pwm_disable(lcdp->pwm_info.dev);
}
DE_WRN("pwm device hdl is NULL\n");
return DIS_FAIL;
}
static s32 disp_lcd_backlight_enable(struct disp_device *lcd)
{
disp_gpio_set_t gpio_info[1];
int hdl;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
unsigned long flags;
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
spin_lock_irqsave(&lcd_data_lock, flags);
if (lcdp->backlight) {
lcdp->backlight->props.power = FB_BLANK_UNBLANK;
}
lcdp->bl_need_enabled = 1;
spin_unlock_irqrestore(&lcd_data_lock, flags);
if (disp_lcd_is_used(lcd)) {
unsigned bl;
if (lcdp->lcd_cfg.lcd_bl_en_used) {
//io-pad
if (!((!strcmp(lcdp->lcd_cfg.lcd_bl_en_power, "")) || (!strcmp(lcdp->lcd_cfg.lcd_bl_en_power, "none"))))
disp_sys_power_enable(lcdp->lcd_cfg.lcd_bl_en_power);
memcpy(gpio_info, &(lcdp->lcd_cfg.lcd_bl_en), sizeof(disp_gpio_set_t));
hdl = disp_sys_gpio_request(gpio_info, 1);
disp_sys_gpio_release(hdl, 2);
}
bl = disp_lcd_get_bright(lcd);
disp_lcd_set_bright(lcd, bl);
}
return 0;
}
static s32 disp_lcd_backlight_disable(struct disp_device *lcd)
{
disp_gpio_set_t gpio_info[1];
int hdl;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
if (lcdp->backlight) {
lcdp->backlight->props.power = FB_BLANK_POWERDOWN;
}
if (disp_lcd_is_used(lcd)) {
if (lcdp->lcd_cfg.lcd_bl_en_used) {
memcpy(gpio_info, &(lcdp->lcd_cfg.lcd_bl_en), sizeof(disp_gpio_set_t));
gpio_info->data = (gpio_info->data==0)?1:0;
gpio_info->mul_sel = 7;
hdl = disp_sys_gpio_request(gpio_info, 1);
disp_sys_gpio_release(hdl, 2);
//io-pad
if (!((!strcmp(lcdp->lcd_cfg.lcd_bl_en_power, "")) || (!strcmp(lcdp->lcd_cfg.lcd_bl_en_power, "none"))))
disp_sys_power_disable(lcdp->lcd_cfg.lcd_bl_en_power);
}
}
return 0;
}
static s32 disp_lcd_power_enable(struct disp_device *lcd, u32 power_id)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
if (disp_lcd_is_used(lcd)) {
if (1 == lcdp->lcd_cfg.lcd_power_used[power_id]) {
/* regulator type */
disp_sys_power_enable(lcdp->lcd_cfg.lcd_power[power_id]);
}
}
return 0;
}
static s32 disp_lcd_power_disable(struct disp_device *lcd, u32 power_id)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
if (disp_lcd_is_used(lcd)) {
if (1 == lcdp->lcd_cfg.lcd_power_used[power_id]) {
/* regulator type */
disp_sys_power_disable(lcdp->lcd_cfg.lcd_power[power_id]);
}
}
return 0;
}
static s32 disp_lcd_bright_get_adjust_value(struct disp_device *lcd, u32 bright)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
bright = (bright > 255)? 255:bright;
return lcdp->panel_extend_info.lcd_bright_curve_tbl[bright];
}
static s32 disp_lcd_bright_curve_init(struct disp_device *lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
u32 i = 0, j=0;
u32 items = 0;
u32 lcd_bright_curve_tbl[101][2];
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
for (i = 0; i < 101; i++) {
if (lcdp->lcd_cfg.backlight_curve_adjust[i] == 0) {
if (i == 0) {
lcd_bright_curve_tbl[items][0] = 0;
lcd_bright_curve_tbl[items][1] = 0;
items++;
}
} else {
lcd_bright_curve_tbl[items][0] = 255 * i / 100;
lcd_bright_curve_tbl[items][1] = lcdp->lcd_cfg.backlight_curve_adjust[i];
items++;
}
}
for (i=0; i<items-1; i++) {
u32 num = lcd_bright_curve_tbl[i+1][0] - lcd_bright_curve_tbl[i][0];
for (j=0; j<num; j++) {
u32 value = 0;
value = lcd_bright_curve_tbl[i][1] + ((lcd_bright_curve_tbl[i+1][1] - lcd_bright_curve_tbl[i][1]) * j)/num;
lcdp->panel_extend_info.lcd_bright_curve_tbl[lcd_bright_curve_tbl[i][0] + j] = value;
}
}
lcdp->panel_extend_info.lcd_bright_curve_tbl[255] = lcd_bright_curve_tbl[items-1][1];
return 0;
}
s32 disp_lcd_set_bright(struct disp_device *lcd, u32 bright)
{
u32 duty_ns;
__u64 backlight_bright = bright;
__u64 backlight_dimming;
__u64 period_ns;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
unsigned long flags;
bool bright_update = false;
struct disp_manager *mgr = NULL;
struct disp_smbl *smbl = NULL;
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
mgr = lcd->manager;
if (NULL == mgr) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
smbl = mgr->smbl;
spin_lock_irqsave(&lcd_data_lock, flags);
backlight_bright = (backlight_bright > 255)? 255:backlight_bright;
if (lcdp->lcd_cfg.backlight_bright != backlight_bright) {
bright_update = true;
lcdp->lcd_cfg.backlight_bright = backlight_bright;
}
spin_unlock_irqrestore(&lcd_data_lock, flags);
if (bright_update && smbl)
smbl->update_backlight(smbl, backlight_bright);
if (lcdp->pwm_info.dev) {
if (backlight_bright != 0) {
backlight_bright += 1;
}
backlight_bright = disp_lcd_bright_get_adjust_value(lcd, backlight_bright);
lcdp->lcd_cfg.backlight_dimming = (0 == lcdp->lcd_cfg.backlight_dimming)? 256:lcdp->lcd_cfg.backlight_dimming;
backlight_dimming = lcdp->lcd_cfg.backlight_dimming;
period_ns = lcdp->pwm_info.period_ns;
duty_ns = (backlight_bright * backlight_dimming * period_ns/256 + 128) / 256;
lcdp->pwm_info.duty_ns = duty_ns;
disp_sys_pwm_config(lcdp->pwm_info.dev, duty_ns, period_ns);
}
if (lcdp->lcd_panel_fun.set_bright && lcdp->enabled) {
lcdp->lcd_panel_fun.set_bright(lcd->disp,disp_lcd_bright_get_adjust_value(lcd,bright));
}
return DIS_SUCCESS;
}
s32 disp_lcd_get_bright(struct disp_device *lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
return lcdp->lcd_cfg.backlight_bright;
}
static s32 disp_lcd_set_bright_dimming(struct disp_device *lcd, u32 dimming)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
u32 bl = 0;
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
dimming = dimming > 256?256:dimming;
lcdp->lcd_cfg.backlight_dimming = dimming;
bl = disp_lcd_get_bright(lcd);
disp_lcd_set_bright(lcd, bl);
return DIS_SUCCESS;
}
static s32 disp_lcd_get_panel_info(struct disp_device *lcd, disp_panel_para* info)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
memcpy(info, (disp_panel_para*)(&(lcdp->panel_info)), sizeof(disp_panel_para));
return 0;
}
//FIXME
extern void sync_event_proc(u32 disp, bool timeout);
#if defined(__LINUX_PLAT__)
static s32 disp_lcd_event_proc(int irq, void *parg)
#else
static s32 disp_lcd_event_proc(void *parg)
#endif
{
struct disp_device *lcd = (struct disp_device*)parg;
struct disp_lcd_private_data *lcdp = NULL;
struct disp_manager *mgr = NULL;
u32 disp;
if (NULL == lcd)
return DISP_IRQ_RETURN;
disp = lcd->disp;
lcdp = disp_lcd_get_priv(lcd);
if (NULL == lcdp)
return DISP_IRQ_RETURN;
if (disp_al_lcd_query_irq(disp, LCD_IRQ_TCON0_VBLK, &lcdp->panel_info)) {
int cur_line = disp_al_lcd_get_cur_line(disp, &lcdp->panel_info);
int start_delay = disp_al_lcd_get_start_delay(disp, &lcdp->panel_info);
mgr = lcd->manager;
if (NULL == mgr)
return DISP_IRQ_RETURN;
if (cur_line <= (start_delay-4)) {
sync_event_proc(mgr->disp, false);
} else {
sync_event_proc(mgr->disp, true);
}
}
return DISP_IRQ_RETURN;
}
static s32 disp_lcd_enable(struct disp_device* lcd)
{
unsigned long flags;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
int i;
struct disp_manager *mgr = NULL;
unsigned bl;
int ret;
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
DE_INF("lcd %d\n", lcd->disp);
mgr = lcd->manager;
if ((NULL == mgr)) {
DE_WRN("mgr is NULL!\n");
return DIS_FAIL;
}
if (1 == disp_lcd_is_enabled(lcd))
return 0;
if (mgr->enable)
mgr->enable(mgr);
/* init fix power */
for (i=0; i<LCD_POWER_NUM; i++) {
if (1 == lcdp->lcd_cfg.lcd_fix_power_used[i]) {
disp_sys_power_enable(lcdp->lcd_cfg.lcd_fix_power[i]);
}
}
if ((LCD_IF_DSI == lcdp->panel_info.lcd_if) && (0 != lcdp->irq_no_dsi)) {
disp_sys_register_irq(lcdp->irq_no_dsi,0,disp_lcd_event_proc,(void*)lcd,0,0);
disp_sys_enable_irq(lcdp->irq_no_dsi);
} else {
disp_sys_register_irq(lcdp->irq_no,0,disp_lcd_event_proc,(void*)lcd,0,0);
disp_sys_enable_irq(lcdp->irq_no);
}
spin_lock_irqsave(&lcd_data_lock, flags);
lcdp->enabling = 1;
lcdp->bl_need_enabled = 0;
spin_unlock_irqrestore(&lcd_data_lock, flags);
if (lcdp->lcd_panel_fun.cfg_panel_info)
lcdp->lcd_panel_fun.cfg_panel_info(&lcdp->panel_extend_info);
else
DE_WRN("lcd_panel_fun[%d].cfg_panel_info is NULL\n", lcd->disp);
lcdp->panel_extend_info.lcd_gamma_en = lcdp->panel_info.lcd_gamma_en;
disp_lcd_gpio_init(lcd);
ret = lcd_clk_enable(lcd);
if (0 != ret)
return DIS_FAIL;
disp_al_lcd_cfg(lcd->disp, &lcdp->panel_info, &lcdp->panel_extend_info);
lcdp->open_flow.func_num = 0;
if (lcdp->lcd_panel_fun.cfg_open_flow) {
lcdp->lcd_panel_fun.cfg_open_flow(lcd->disp);
} else {
DE_WRN("lcd_panel_fun[%d].cfg_open_flow is NULL\n", lcd->disp);
}
for (i=0; i<lcdp->open_flow.func_num; i++) {
if (lcdp->open_flow.func[i].func) {
lcdp->open_flow.func[i].func(lcd->disp);
DE_INF("open flow:step %d finish, to delay %d\n", i, lcdp->open_flow.func[i].delay);
if (0 != lcdp->open_flow.func[i].delay)
disp_delay_ms(lcdp->open_flow.func[i].delay);
}
}
spin_lock_irqsave(&lcd_data_lock, flags);
lcdp->enabled = 1;
lcdp->enabling = 0;
spin_unlock_irqrestore(&lcd_data_lock, flags);
bl = disp_lcd_get_bright(lcd);
disp_lcd_set_bright(lcd, bl);
return 0;
}
/* lcd enable except for backlight */
static s32 disp_lcd_fake_enable(struct disp_device* lcd)
{
unsigned long flags;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
int i;
struct disp_manager *mgr = NULL;
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
DE_INF("lcd %d\n", lcd->disp);
mgr = lcd->manager;
if ((NULL == mgr)) {
DE_WRN("mgr is NULL!\n");
return DIS_FAIL;
}
if (1 == disp_lcd_is_enabled(lcd))
return 0;
if (mgr->enable)
mgr->enable(mgr);
/* init fix power */
for (i=0; i<LCD_POWER_NUM; i++) {
if (1 == lcdp->lcd_cfg.lcd_fix_power_used[i]) {
disp_sys_power_enable(lcdp->lcd_cfg.lcd_fix_power[i]);
}
}
if ((LCD_IF_DSI == lcdp->panel_info.lcd_if) && (0 != lcdp->irq_no_dsi)) {
disp_sys_register_irq(lcdp->irq_no_dsi,0,disp_lcd_event_proc,(void*)lcd,0,0);
disp_sys_enable_irq(lcdp->irq_no_dsi);
} else {
disp_sys_register_irq(lcdp->irq_no,0,disp_lcd_event_proc,(void*)lcd,0,0);
disp_sys_enable_irq(lcdp->irq_no);
}
spin_lock_irqsave(&lcd_data_lock, flags);
lcdp->enabling = 1;
lcdp->bl_need_enabled = 0;
spin_unlock_irqrestore(&lcd_data_lock, flags);
disp_lcd_gpio_init(lcd);
lcd_clk_enable(lcd);
disp_al_lcd_cfg(lcd->disp, &lcdp->panel_info, &lcdp->panel_extend_info);
lcdp->open_flow.func_num = 0;
if (lcdp->lcd_panel_fun.cfg_open_flow) {
lcdp->lcd_panel_fun.cfg_open_flow(lcd->disp);
} else {
DE_WRN("lcd_panel_fun[%d].cfg_open_flow is NULL\n", lcd->disp);
}
for (i=0; i<lcdp->open_flow.func_num - 1; i++) {
if (lcdp->open_flow.func[i].func) {
lcdp->open_flow.func[i].func(lcd->disp);
DE_INF("open flow:step %d finish, to delay %d\n", i, lcdp->open_flow.func[i].delay);
if (0 != lcdp->open_flow.func[i].delay)
disp_delay_ms(lcdp->open_flow.func[i].delay);
}
}
spin_lock_irqsave(&lcd_data_lock, flags);
lcdp->enabled = 1;
lcdp->enabling = 0;
spin_unlock_irqrestore(&lcd_data_lock, flags);
return 0;
}
static s32 disp_lcd_disable(struct disp_device* lcd)
{
unsigned long flags;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
struct disp_manager *mgr = NULL;
int i;
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
DE_INF("lcd %d\n", lcd->disp);
mgr = lcd->manager;
if ((NULL == mgr)) {
DE_WRN("mgr is NULL!\n");
return DIS_FAIL;
}
if (0 == disp_lcd_is_enabled(lcd))
return 0;
spin_lock_irqsave(&lcd_data_lock, flags);
lcdp->enabled = 0;
spin_unlock_irqrestore(&lcd_data_lock, flags);
lcdp->bl_need_enabled = 0;
lcdp->close_flow.func_num = 0;
if (lcdp->lcd_panel_fun.cfg_close_flow) {
lcdp->lcd_panel_fun.cfg_close_flow(lcd->disp);
} else {
DE_WRN("lcd_panel_fun[%d].cfg_close_flow is NULL\n", lcd->disp);
}
for (i=0; i<lcdp->close_flow.func_num; i++) {
if (lcdp->close_flow.func[i].func) {
lcdp->close_flow.func[i].func(lcd->disp);
DE_INF("close flow:step %d finish, to delay %d\n", i, lcdp->close_flow.func[i].delay);
if (0 != lcdp->close_flow.func[i].delay)
disp_delay_ms(lcdp->close_flow.func[i].delay);
}
}
disp_lcd_gpio_exit(lcd);
lcd_clk_disable(lcd);
if ((LCD_IF_DSI == lcdp->panel_info.lcd_if) && (0 != lcdp->irq_no_dsi)) {
disp_sys_disable_irq(lcdp->irq_no_dsi);
disp_sys_unregister_irq(lcdp->irq_no_dsi, disp_lcd_event_proc,(void*)lcd);
} else {
disp_sys_disable_irq(lcdp->irq_no);
disp_sys_unregister_irq(lcdp->irq_no, disp_lcd_event_proc,(void*)lcd);
}
/* disable fix power */
for (i=LCD_POWER_NUM -1; i>=0; i--) {
if (1 == lcdp->lcd_cfg.lcd_fix_power_used[i]) {
disp_sys_power_disable(lcdp->lcd_cfg.lcd_fix_power[i]);
}
}
if (mgr->disable)
mgr->disable(mgr);
return 0;
}
static s32 disp_lcd_sw_enable(struct disp_device* lcd)
{
unsigned long flags;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
int i;
struct disp_manager *mgr = NULL;
disp_gpio_set_t gpio_info[1];
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
mgr = lcd->manager;
if ((NULL == mgr)) {
DE_WRN("mgr is NULL!\n");
return DIS_FAIL;
}
if (mgr->sw_enable)
mgr->sw_enable(mgr);
#if !defined(CONFIG_COMMON_CLK_ENABLE_SYNCBOOT)
if (0 != lcd_clk_enable(lcd))
return DIS_FAIL;
#endif
/* init fix power */
for (i=0; i<LCD_POWER_NUM; i++) {
if (1 == lcdp->lcd_cfg.lcd_fix_power_used[i]) {
disp_sys_power_enable(lcdp->lcd_cfg.lcd_fix_power[i]);
}
}
/* init lcd power */
for (i=0; i<LCD_POWER_NUM; i++) {
if (1 == lcdp->lcd_cfg.lcd_power_used[i]) {
disp_sys_power_enable(lcdp->lcd_cfg.lcd_power[i]);
}
}
/* init gpio */
for (i = 0; i < LCD_GPIO_REGU_NUM; i++)
{
if (!((!strcmp(lcdp->lcd_cfg.lcd_gpio_power[i], "")) || (!strcmp(lcdp->lcd_cfg.lcd_gpio_power[i], "none"))))
disp_sys_power_enable(lcdp->lcd_cfg.lcd_gpio_power[i]);
}
for (i=0; i<LCD_GPIO_NUM; i++) {
lcdp->lcd_cfg.gpio_hdl[i] = 0;
if (lcdp->lcd_cfg.lcd_gpio_used[i]) {
memcpy(gpio_info, &(lcdp->lcd_cfg.lcd_gpio[i]), sizeof(disp_gpio_set_t));
lcdp->lcd_cfg.gpio_hdl[i] = disp_sys_gpio_request_simple(gpio_info, 1);
}
}
/* init lcd pin */
for (i=0; i<LCD_GPIO_REGU_NUM; i++) {
if (!((!strcmp(lcdp->lcd_cfg.lcd_pin_power[i], "")) || (!strcmp(lcdp->lcd_cfg.lcd_pin_power[i], "none"))))
disp_sys_power_enable(lcdp->lcd_cfg.lcd_pin_power[i]);
}
/* init bl */
if (lcdp->lcd_cfg.lcd_bl_en_used) {
//io-pad
if (!((!strcmp(lcdp->lcd_cfg.lcd_bl_en_power, "")) || (!strcmp(lcdp->lcd_cfg.lcd_bl_en_power, "none"))))
disp_sys_power_enable(lcdp->lcd_cfg.lcd_bl_en_power);
}
spin_lock_irqsave(&lcd_data_lock, flags);
lcdp->enabled = 1;
lcdp->enabling = 0;
lcdp->bl_need_enabled = 1;
spin_unlock_irqrestore(&lcd_data_lock, flags);
disp_al_lcd_disable_irq(lcd->disp, LCD_IRQ_TCON0_VBLK, &lcdp->panel_info);
if ((LCD_IF_DSI == lcdp->panel_info.lcd_if) && (0 != lcdp->irq_no_dsi)) {
disp_sys_register_irq(lcdp->irq_no_dsi,0,disp_lcd_event_proc,(void*)lcd,0,0);
disp_sys_enable_irq(lcdp->irq_no_dsi);
} else {
disp_sys_register_irq(lcdp->irq_no,0,disp_lcd_event_proc,(void*)lcd,0,0);
disp_sys_enable_irq(lcdp->irq_no);
}
disp_al_lcd_enable_irq(lcd->disp, LCD_IRQ_TCON0_VBLK, &lcdp->panel_info);
return 0;
}
s32 disp_lcd_is_enabled(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
return (s32)lcdp->enabled;
}
static s32 disp_lcd_set_open_func(struct disp_device* lcd, LCD_FUNC func, u32 delay)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return -1;
}
if (func) {
lcdp->open_flow.func[lcdp->open_flow.func_num].func = func;
lcdp->open_flow.func[lcdp->open_flow.func_num].delay = delay;
lcdp->open_flow.func_num ++;
}
return DIS_SUCCESS;
}
static s32 disp_lcd_set_close_func(struct disp_device* lcd, LCD_FUNC func, u32 delay)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return -1;
}
if (func) {
lcdp->close_flow.func[lcdp->close_flow.func_num].func = func;
lcdp->close_flow.func[lcdp->close_flow.func_num].delay = delay;
lcdp->close_flow.func_num ++;
}
return DIS_SUCCESS;
}
static s32 disp_lcd_set_panel_funs(struct disp_device* lcd, char *name, disp_lcd_panel_fun * lcd_cfg)
{
char primary_key[20], drv_name[32];
s32 ret;
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
sprintf(primary_key, "lcd%d", lcd->disp);
ret = disp_sys_script_get_item(primary_key, "lcd_driver_name", (int*)drv_name, 2);
DE_INF("lcd %d, driver_name %s, panel_name %s\n", lcd->disp, drv_name, name);
if ((2==ret) && !strcmp(drv_name, name)) {
memset(&lcdp->lcd_panel_fun, 0, sizeof(disp_lcd_panel_fun));
lcdp->lcd_panel_fun.cfg_panel_info= lcd_cfg->cfg_panel_info;
lcdp->lcd_panel_fun.cfg_open_flow = lcd_cfg->cfg_open_flow;
lcdp->lcd_panel_fun.cfg_close_flow = lcd_cfg->cfg_close_flow;
lcdp->lcd_panel_fun.lcd_user_defined_func = lcd_cfg->lcd_user_defined_func;
lcdp->lcd_panel_fun.set_bright = lcd_cfg->set_bright;
return 0;
}
return -1;
}
s32 disp_lcd_gpio_init(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
int i = 0;
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
//io-pad
for (i = 0; i < LCD_GPIO_REGU_NUM; i++)
{
if (!((!strcmp(lcdp->lcd_cfg.lcd_gpio_power[i], "")) || (!strcmp(lcdp->lcd_cfg.lcd_gpio_power[i], "none"))))
disp_sys_power_enable(lcdp->lcd_cfg.lcd_gpio_power[i]);
}
for (i=0; i<LCD_GPIO_NUM; i++) {
lcdp->lcd_cfg.gpio_hdl[i] = 0;
if (lcdp->lcd_cfg.lcd_gpio_used[i]) {
disp_gpio_set_t gpio_info[1];
memcpy(gpio_info, &(lcdp->lcd_cfg.lcd_gpio[i]), sizeof(disp_gpio_set_t));
lcdp->lcd_cfg.gpio_hdl[i] = disp_sys_gpio_request(gpio_info, 1);
}
}
return 0;
}
s32 disp_lcd_gpio_exit(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
int i = 0;
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
for (i=0; i<LCD_GPIO_NUM; i++) {
if (lcdp->lcd_cfg.gpio_hdl[i]) {
disp_gpio_set_t gpio_info[1];
disp_sys_gpio_release(lcdp->lcd_cfg.gpio_hdl[i], 2);
memcpy(gpio_info, &(lcdp->lcd_cfg.lcd_gpio[i]), sizeof(disp_gpio_set_t));
gpio_info->mul_sel = 7;
lcdp->lcd_cfg.gpio_hdl[i] = disp_sys_gpio_request(gpio_info, 1);
disp_sys_gpio_release(lcdp->lcd_cfg.gpio_hdl[i], 2);
lcdp->lcd_cfg.gpio_hdl[i] = 0;
}
}
//io-pad
for (i = LCD_GPIO_REGU_NUM-1; i>=0; i--)
{
if (!((!strcmp(lcdp->lcd_cfg.lcd_gpio_power[i], "")) || (!strcmp(lcdp->lcd_cfg.lcd_gpio_power[i], "none"))))
disp_sys_power_disable(lcdp->lcd_cfg.lcd_gpio_power[i]);
}
return 0;
}
//direction: input(0), output(1)
s32 disp_lcd_gpio_set_direction(struct disp_device* lcd, u32 io_index, u32 direction)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
char gpio_name[20];
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
sprintf(gpio_name, "lcd_gpio_%d", io_index);
return disp_sys_gpio_set_direction(lcdp->lcd_cfg.gpio_hdl[io_index], direction, gpio_name);
}
s32 disp_lcd_gpio_get_value(struct disp_device* lcd,u32 io_index)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
char gpio_name[20];
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
sprintf(gpio_name, "lcd_gpio_%d", io_index);
return disp_sys_gpio_get_value(lcdp->lcd_cfg.gpio_hdl[io_index], gpio_name);
}
s32 disp_lcd_gpio_set_value(struct disp_device* lcd, u32 io_index, u32 data)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
char gpio_name[20];
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
sprintf(gpio_name, "lcd_gpio_%d", io_index);
return disp_sys_gpio_set_value(lcdp->lcd_cfg.gpio_hdl[io_index], data, gpio_name);
}
static s32 disp_lcd_get_dimensions(struct disp_device *lcd, u32 *width, u32 *height)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
*width = lcdp->panel_info.lcd_width;
*height = lcdp->panel_info.lcd_height;
return 0;
}
static s32 disp_lcd_get_status(struct disp_device *lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return 0;
}
return disp_al_device_get_status(lcd->disp);
}
static int sunxi_update_bl(struct backlight_device *bdev)
{
struct disp_device *disp = bl_get_data(bdev);
int brightness = bdev->props.brightness;
if (bdev->props.power != FB_BLANK_UNBLANK ||
bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
brightness = 0;
int ret = disp_lcd_set_bright(disp, brightness);
if (bdev->props.power != FB_BLANK_UNBLANK)
disp_lcd_backlight_disable(disp);
else
disp_lcd_backlight_enable(disp);
return ret;
}
static int sunxi_get_brightness(struct backlight_device *bdev)
{
struct disp_device *disp = bl_get_data(bdev);
return disp_lcd_get_bright(disp);
}
static int sunxi_check_fb(struct backlight_device *bdev,
struct fb_info *info)
{
// return (info->bl_dev == bdev);
return 1;
}
static struct backlight_ops sunxi_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = sunxi_update_bl,
.get_brightness = sunxi_get_brightness,
.check_fb = sunxi_check_fb,
};
static s32 disp_lcd_init(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
DE_INF("lcd %d\n", lcd->disp);
lcd_get_sys_config(lcd->disp, &lcdp->lcd_cfg);
if (disp_lcd_is_used(lcd)) {
struct disp_video_timings *timmings;
disp_panel_para *panel_info;
lcd_parse_panel_para(lcd->disp, &lcdp->panel_info);
timmings = &lcd->timings;
panel_info = &lcdp->panel_info;
timmings->pixel_clk = panel_info->lcd_dclk_freq * 1000;
timmings->x_res = panel_info->lcd_x;
timmings->y_res = panel_info->lcd_y;
timmings->hor_total_time= panel_info->lcd_ht;
timmings->hor_sync_time= panel_info->lcd_hspw;
timmings->hor_back_porch= panel_info->lcd_hbp-panel_info->lcd_hspw;
timmings->hor_front_porch= panel_info->lcd_ht-panel_info->lcd_hbp - panel_info->lcd_x;
timmings->ver_total_time= panel_info->lcd_vt;
timmings->ver_sync_time= panel_info->lcd_vspw;
timmings->ver_back_porch= panel_info->lcd_vbp-panel_info->lcd_vspw;
timmings->ver_front_porch= panel_info->lcd_vt-panel_info->lcd_vbp -panel_info->lcd_y;
}
disp_lcd_bright_curve_init(lcd);
if (disp_lcd_is_used(lcd)) {
__u64 backlight_bright;
__u64 period_ns, duty_ns;
if (lcdp->panel_info.lcd_pwm_used) {
lcdp->pwm_info.channel = lcdp->panel_info.lcd_pwm_ch;
lcdp->pwm_info.polarity = lcdp->panel_info.lcd_pwm_pol;
lcdp->pwm_info.dev = disp_sys_pwm_request(lcdp->panel_info.lcd_pwm_ch);
if (lcdp->panel_info.lcd_pwm_freq != 0) {
period_ns = 1000*1000*1000 / lcdp->panel_info.lcd_pwm_freq;
} else {
DE_WRN("lcd%d.lcd_pwm_freq is ZERO\n", lcd->disp);
period_ns = 1000*1000*1000 / 1000; //default 1khz
}
backlight_bright = lcdp->lcd_cfg.backlight_bright;
duty_ns = (backlight_bright * period_ns) / 256;
//DE_DBG("[PWM]backlight_bright=%d,period_ns=%d,duty_ns=%d\n",(u32)backlight_bright,(u32)period_ns, (u32)duty_ns);
disp_sys_pwm_set_polarity(lcdp->pwm_info.dev, lcdp->pwm_info.polarity);
lcdp->pwm_info.duty_ns = duty_ns;
lcdp->pwm_info.period_ns = period_ns;
lcdp->backlight = backlight_device_register(lcd->name, g_disp_drv.dev, lcd,
&sunxi_bl_ops, NULL);
if (IS_ERR(lcdp->backlight)) {
dev_err(g_disp_drv.dev, "unable to register backlight device: %ld\n",
PTR_ERR(lcdp->backlight));
} else {
lcdp->backlight->props.max_brightness = 255;
lcdp->backlight->props.brightness = disp_lcd_get_bright(lcd);
backlight_update_status(lcdp->backlight);
}
}
lcd_clk_init(lcd);
}
//lcd_panel_parameter_check(lcd->disp, lcd);
return 0;
}
static s32 disp_lcd_exit(struct disp_device* lcd)
{
struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd);
if ((NULL == lcd) || (NULL == lcdp)) {
DE_WRN("NULL hdl!\n");
return DIS_FAIL;
}
if (lcdp->backlight) {
backlight_device_unregister(lcdp->backlight);
lcdp->backlight = NULL;
}
lcd_clk_exit(lcd);
return 0;
}
s32 disp_init_lcd(disp_bsp_init_para * para)
{
u32 num_screens;
u32 disp;
struct disp_device *lcd;
struct disp_lcd_private_data *lcdp;
DE_INF("disp_init_lcd\n");
#if defined(__LINUX_PLAT__)
spin_lock_init(&lcd_data_lock);
#endif
num_screens = bsp_disp_feat_get_num_screens();
lcds = (struct disp_device *)kmalloc(sizeof(struct disp_device) * num_screens,
GFP_KERNEL | __GFP_ZERO);
if (NULL == lcds) {
DE_WRN("malloc memory(%d bytes) fail!\n",
(unsigned int)sizeof(struct disp_device) * num_screens);
return DIS_FAIL;
}
lcd_private = (struct disp_lcd_private_data *)kmalloc(sizeof(struct disp_lcd_private_data)\
* num_screens, GFP_KERNEL | __GFP_ZERO);
if (NULL == lcd_private) {
DE_WRN("malloc memory(%d bytes) fail!\n",
(unsigned int)sizeof(struct disp_lcd_private_data) * num_screens);
return DIS_FAIL;
}
for (disp=0; disp<num_screens; disp++) {
lcd = &lcds[disp];
lcdp = &lcd_private[disp];
sprintf(lcd->name, "lcd%d", disp);
lcd->disp = disp;
lcd->type = DISP_OUTPUT_TYPE_LCD;
lcdp->irq_no = para->irq_no[DISP_MOD_LCD0 + disp];
lcdp->clk = para->mclk[DISP_MOD_LCD0 + disp];
lcdp->lvds_clk = para->mclk[DISP_MOD_LVDS];
#if defined(SUPPORT_DSI)
lcdp->irq_no_dsi = para->irq_no[DISP_MOD_DSI0 + disp];
lcdp->dsi_clk0 = para->mclk[DISP_MOD_DSI0];
lcdp->dsi_clk1 = para->mclk[DISP_MOD_DSI1];
#endif
DE_INF("lcd %d, irq_no=%d, irq_no_dsi=%d\n", disp, lcdp->irq_no, lcdp->irq_no_dsi);
lcd->set_manager = disp_device_set_manager;
lcd->unset_manager = disp_device_unset_manager;
lcd->get_resolution = disp_device_get_resolution;
lcd->get_timings = disp_device_get_timings;
lcd->enable = disp_lcd_enable;
lcd->sw_enable = disp_lcd_sw_enable;
lcd->fake_enable = disp_lcd_fake_enable;
lcd->disable = disp_lcd_disable;
lcd->is_enabled = disp_lcd_is_enabled;
lcd->set_bright = disp_lcd_set_bright;
lcd->get_bright = disp_lcd_get_bright;
lcd->set_bright_dimming = disp_lcd_set_bright_dimming;
lcd->get_panel_info = disp_lcd_get_panel_info;
lcd->set_panel_func = disp_lcd_set_panel_funs;
lcd->set_open_func = disp_lcd_set_open_func;
lcd->set_close_func = disp_lcd_set_close_func;
lcd->backlight_enable = disp_lcd_backlight_enable;
lcd->backlight_disable = disp_lcd_backlight_disable;
lcd->pwm_enable = disp_lcd_pwm_enable;
lcd->pwm_disable = disp_lcd_pwm_disable;
lcd->power_enable = disp_lcd_power_enable;
lcd->power_disable = disp_lcd_power_disable;
lcd->pin_cfg = disp_lcd_pin_cfg;
lcd->tcon_enable = disp_lcd_tcon_enable;
lcd->tcon_disable = disp_lcd_tcon_disable;
lcd->gpio_set_value = disp_lcd_gpio_set_value;
lcd->gpio_set_direction = disp_lcd_gpio_set_direction;
lcd->get_dimensions = disp_lcd_get_dimensions;
lcd->get_status = disp_lcd_get_status;
lcd->init = disp_lcd_init;
lcd->exit = disp_lcd_exit;
if (bsp_disp_feat_is_supported_output_types(disp, DISP_OUTPUT_TYPE_LCD)) {
lcd->init(lcd);
disp_device_register(lcd);
}
}
return 0;
}
You can’t perform that action at this time.