/* * Allwinner SoCs display driver. * * Copyright (C) 2016 Allwinner. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include "disp_lcd.h" struct disp_lcd_private_data { struct disp_lcd_flow open_flow; struct disp_lcd_flow close_flow; struct disp_panel_para panel_info; struct panel_extend_para panel_extend_info; struct panel_extend_para panel_extend_info_set; u32 panel_extend_dirty; struct disp_lcd_cfg lcd_cfg; struct disp_lcd_panel_fun lcd_panel_fun; bool is_bridge; bool enabling; bool disabling; bool bl_enabled; u32 irq_no; u32 irq_no_dsi; u32 irq_no_edp; u32 enabled; u32 power_enabled; u32 bl_need_enabled; u32 frame_per_sec; u32 usec_per_line; u32 judge_line; u32 tri_finish_fail; s32 color_temperature; u32 color_inverse; /*0:reset all module, 1:reset panel only*/ u32 esd_reset_level; struct { uintptr_t dev; u32 channel; u32 polarity; u32 period_ns; u32 duty_ns; u32 enabled; } pwm_info; struct clk *clk; struct clk *lvds_clk; struct clk *dsi_clk[CLK_DSI_NUM]; struct clk *clk_parent; struct clk *dsi_clk_parent[CLK_DSI_NUM]; struct clk *lvds_clk_parent; /*0:no reset process;1:reset request;2:resetting*/ atomic_t lcd_resetting; struct work_struct reflush_work; struct disp_lcd_esd_info esd_inf; struct mutex enable_lock; struct plist_head timing_head; struct disp_bridge_fun *bridge; enum disp_tv_mode mode_using; }; static spinlock_t lcd_data_lock; static struct disp_device *lcds; static struct disp_lcd_private_data *lcd_private; 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 (lcd == NULL) { DE_WRN("param is NULL!\n"); return NULL; } return (struct disp_lcd_private_data *)lcd->priv_data; } 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 ((lcd == NULL) || (lcdp == NULL)) ret = 0; else ret = (s32)lcdp->lcd_cfg.lcd_used; return ret; } static s32 lcd_parse_panel_para(u32 disp, struct disp_panel_para *info) { s32 ret = 0; char primary_key[25]; s32 value = 0; sprintf(primary_key, "lcd%d", disp); memset(info, 0, sizeof(struct 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_tcon_div", &value, 1); if (ret == 1) info->lcd_tcon_div = 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_cpu_mode", &value, 1); if (ret == 1) info->lcd_cpu_mode = 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_dsi_port_num", &value, 1); if (ret == 1) info->lcd_dsi_port_num = value; ret = disp_sys_script_get_item(primary_key, "lcd_tcon_mode", &value, 1); if (ret == 1) info->lcd_tcon_mode = value; ret = disp_sys_script_get_item(primary_key, "lcd_slave_tcon_num", &value, 1); if (ret == 1) info->lcd_slave_tcon_num = value; ret = disp_sys_script_get_item(primary_key, "lcd_slave_stop_pos", &value, 1); if (ret == 1) info->lcd_slave_stop_pos = value; ret = disp_sys_script_get_item(primary_key, "lcd_sync_pixel_num", &value, 1); if (ret == 1) info->lcd_sync_pixel_num = value; ret = disp_sys_script_get_item(primary_key, "lcd_sync_line_num", &value, 1); if (ret == 1) info->lcd_sync_line_num = value; ret = disp_sys_script_get_item(primary_key, "lcd_tcon_en_odd_even_div", &value, 1); if (ret == 1) info->lcd_tcon_en_odd_even = value; ret = disp_sys_script_get_item(primary_key, "lcd_fsync_en", &value, 1); if (ret == 1) info->lcd_fsync_en = value; ret = disp_sys_script_get_item(primary_key, "lcd_fsync_act_time", &value, 1); if (ret == 1) info->lcd_fsync_act_time = value; ret = disp_sys_script_get_item(primary_key, "lcd_fsync_dis_time", &value, 1); if (ret == 1) info->lcd_fsync_dis_time = value; ret = disp_sys_script_get_item(primary_key, "lcd_fsync_pol", &value, 1); if (ret == 1) info->lcd_fsync_pol = 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; } static void lcd_get_sys_config(u32 disp, struct disp_lcd_cfg *lcd_cfg) { struct 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) 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 < 6; 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 (i == 0) 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); } sprintf(sub_name, "lcd_pwm_max_limit"); ret = disp_sys_script_get_item(primary_key, sub_name, &value, 1); if (ret == 1) { lcd_cfg->lcd_pwm_max_limit = value; } else { lcd_cfg->lcd_pwm_max_limit = 255; } /* 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] = lcd_cfg->lcd_pwm_max_limit; ret = disp_sys_script_get_item(primary_key, sub_name, &value, 1); if (ret == 1) { value = (value > 100) ? 100 : value; value = value * lcd_cfg->lcd_pwm_max_limit / 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 > lcd_cfg->lcd_pwm_max_limit) ? lcd_cfg->lcd_pwm_max_limit : value; lcd_cfg->backlight_bright = value; } else { lcd_cfg->backlight_bright = 197; } } static s32 lcd_clk_init(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); s32 i = 0; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return -1; } DE_INF("lcd %d clk init\n", lcd->disp); lcdp->clk_parent = clk_get_parent(lcdp->clk); lcdp->lvds_clk_parent = clk_get_parent(lcdp->lvds_clk); for (i = 0; i < CLK_DSI_NUM; ++i) { lcdp->dsi_clk_parent[i] = clk_get_parent(lcdp->dsi_clk[i]); } return DIS_SUCCESS; } static s32 lcd_clk_exit(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return -1; } if (lcdp->clk_parent) clk_put(lcdp->clk_parent); return DIS_SUCCESS; } /** * @name :cal_real_frame_period * @brief :set lcd->timings.frame_period (nsec) * ,lcd->timings.start_delay (line) * and lcd->timings.dclk_rate_set(hz) * @param :lcd:pointer of disp_device * @return :0 if success, other fail */ static s32 cal_real_frame_period(struct disp_device *lcd) { s32 ret = -1; struct disp_lcd_private_data *lcdp; struct lcd_clk_info clk_info; unsigned long long temp = 0; if (!lcd) { DE_WRN("NULL hdl!\n"); goto OUT; } lcdp = disp_lcd_get_priv(lcd); if (!lcdp) { DE_WRN("NULL hdl!\n"); goto OUT; } memset(&clk_info, 0, sizeof(struct lcd_clk_info)); disp_al_lcd_get_clk_info(lcd->hwdev_index, &clk_info, &lcdp->panel_info); if (!lcdp->clk || !(&lcdp->panel_info)) { DE_WRN("NULL clk or panel_info!\n"); goto OUT; } if (lcdp->panel_info.lcd_if == LCD_IF_DSI) lcd->timings.dclk_rate_set = clk_get_rate(lcdp->clk) / clk_info.dsi_div; else lcd->timings.dclk_rate_set = clk_get_rate(lcdp->clk) / clk_info.tcon_div; if (lcd->timings.dclk_rate_set == 0) { DE_WRN("lcd dclk_rate_set is 0!\n"); ret = -2; goto OUT; } temp = ONE_SEC * lcdp->panel_info.lcd_ht * lcdp->panel_info.lcd_vt; do_div(temp, lcd->timings.dclk_rate_set); lcd->timings.frame_period = temp; lcd->timings.start_delay = disp_al_lcd_get_start_delay(lcd->hwdev_index, &lcdp->panel_info); DE_INF("lcd frame period:%llu\n", lcd->timings.frame_period); ret = 0; OUT: return ret; } 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 = 297000000, lcd_rate = 33000000; unsigned long dclk_rate = 33000000, dsi_rate = 0; /* hz */ unsigned long pll_rate_set = 297000000, lcd_rate_set = 33000000; unsigned long dclk_rate_set = 33000000, dsi_rate_set = 0; /* hz */ u32 i = 0, j = 0; u32 dsi_num = 0; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return -1; } memset(&clk_info, 0, sizeof(struct lcd_clk_info)); if (lcdp->panel_info.lcd_tcon_div != 0) clk_info.tcon_div = lcdp->panel_info.lcd_tcon_div; disp_al_lcd_get_clk_info(lcd->hwdev_index, &clk_info, &lcdp->panel_info); dclk_rate = lcdp->panel_info.lcd_dclk_freq * 1000000; /* Mhz -> hz */ if (lcdp->panel_info.lcd_if == LCD_IF_DSI) { 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 && lcdp->clk) { clk_set_parent(lcdp->clk, lcdp->clk_parent); clk_set_rate(lcdp->clk_parent, pll_rate); pll_rate_set = clk_get_rate(lcdp->clk_parent); } if (lcdp->lvds_clk_parent && lcdp->lvds_clk) clk_set_parent(lcdp->lvds_clk, lcdp->lvds_clk_parent); for (i = 0; i < CLK_DSI_NUM; ++i) { if (lcdp->dsi_clk_parent[i] && lcdp->dsi_clk[i]) clk_set_parent(lcdp->dsi_clk[i], lcdp->dsi_clk_parent[i]); } if (clk_info.lcd_div) lcd_rate_set = pll_rate_set / clk_info.lcd_div; else lcd_rate_set = pll_rate_set; clk_set_rate(lcdp->clk, lcd_rate_set); lcd_rate_set = clk_get_rate(lcdp->clk); if (lcdp->panel_info.lcd_if == LCD_IF_DSI) { if (lcdp->panel_info.lcd_dsi_if == LCD_DSI_IF_COMMAND_MODE) dsi_rate_set = pll_rate_set; else dsi_rate_set = pll_rate_set / clk_info.dsi_div; dsi_rate_set = (clk_info.dsi_rate == 0) ? dsi_rate_set : clk_info.dsi_rate; dsi_num = (lcdp->panel_info.lcd_tcon_mode == DISP_TCON_DUAL_DSI) ? 2 : 1; dsi_rate_set /= dsi_num; /* total number of dsi clk for current disp device*/ dsi_num *= CLK_NUM_PER_DSI; /*In the case of CLK_NUM_PER_DSI equal to 2*/ /*even index mean hs clk which need to be seted*/ /*odd index mean lp clk which no need to be seted*/ for (i = dsi_num * lcd->disp, j = 0; i < CLK_DSI_NUM; i += CLK_NUM_PER_DSI) { if (lcdp->dsi_clk[i]) { clk_set_rate(lcdp->dsi_clk[i], dsi_rate_set); DE_INF( "clk_set_rate:dsi's %d th clk with %ld\n", i, dsi_rate_set); j += CLK_NUM_PER_DSI; } if (j == dsi_num) break; } /*FIXME, dsi clk0 = dsi clk1(rate)*/ /*disp_sys_clk_set_rate(lcdp->dsi_clk[1], dsi_rate_set);*/ } 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 = 0; u32 i = 0, j = 0; u32 dsi_num = 0; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return -1; } lcd_clk_config(lcd); if (!__clk_get_enable_count(lcdp->clk)) { ret = clk_prepare_enable(lcdp->clk); if (ret != 0) { DE_WRN("fail enable lcd's clock!\n"); goto exit; } } if (lcdp->panel_info.lcd_if == LCD_IF_LVDS) { if (lcdp->lvds_clk) ret = clk_prepare_enable(lcdp->lvds_clk); if (ret != 0) { DE_WRN("fail enable lvds's clock!\n"); goto exit; } #ifdef SUPPORT_COMBO_DPHY if (lcd->hwdev_index == 0) clk_prepare_enable(lcdp->dsi_clk[0]); #endif } else if (lcdp->panel_info.lcd_if == LCD_IF_DSI) { dsi_num = (lcdp->panel_info.lcd_tcon_mode == DISP_TCON_DUAL_DSI) ? 2 : 1; /*the num of dsi clk to use for current disp*/ dsi_num *= CLK_NUM_PER_DSI; for (i = dsi_num * lcd->disp, j = 0; i < CLK_DSI_NUM; ++i) { if (lcdp->dsi_clk[i]) { ret = clk_prepare_enable(lcdp->dsi_clk[i]); if (ret != 0) { DE_WRN("fail enable dsi's clock%d!\n", i); goto exit; } ++j; } else { DE_WRN("dsi's clock%d is NULL!\n", i); goto exit; } if (j == dsi_num) break; } } exit: return ret; } static s32 lcd_clk_disable(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); u32 i = 0, j = 0; u32 dsi_num = 0; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return -1; } if (lcdp->panel_info.lcd_if == LCD_IF_LVDS) { clk_disable(lcdp->lvds_clk); #ifdef SUPPORT_COMBO_DPHY if (lcd->hwdev_index == 0) clk_disable(lcdp->dsi_clk[0]); #endif } else if (lcdp->panel_info.lcd_if == LCD_IF_DSI) { dsi_num = (lcdp->panel_info.lcd_tcon_mode == DISP_TCON_DUAL_DSI) ? 2 : 1; /*the num of dsi clk to use for current disp*/ dsi_num *= CLK_NUM_PER_DSI; for (i = dsi_num * lcd->disp, j = 0; i < CLK_DSI_NUM; ++i) { if (lcdp->dsi_clk[i]) clk_disable(lcdp->dsi_clk[i]); ++j; if (j == dsi_num) break; } } if (__clk_get_enable_count(lcdp->clk)) clk_disable(lcdp->clk); return DIS_SUCCESS; } static int lcd_calc_judge_line(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return -1; } if (lcdp->usec_per_line == 0) { struct disp_panel_para *panel_info = &lcdp->panel_info; /* * usec_per_line = 1 / fps / vt * 1000000 * = 1 / (dclk * 1000000 / vt / ht) / vt * 1000000 * = ht / dclk(Mhz) */ lcdp->usec_per_line = panel_info->lcd_ht / panel_info->lcd_dclk_freq; } if (lcdp->judge_line == 0) { int start_delay = disp_al_lcd_get_start_delay(lcd->hwdev_index, &lcdp-> panel_info); int usec_start_delay = start_delay * lcdp->usec_per_line; int usec_judge_point; if (usec_start_delay <= 200) usec_judge_point = usec_start_delay * 3 / 7; else if (usec_start_delay <= 400) usec_judge_point = usec_start_delay / 2; else usec_judge_point = 200; lcdp->judge_line = usec_judge_point / lcdp->usec_per_line; } return 0; } #ifdef EINK_FLUSH_TIME_TEST struct timeval lcd_start, lcd_mid, lcd_mid1, lcd_mid2, lcd_end, t5_b, t5_e; struct timeval pin_b, pin_e, po_b, po_e, tocn_b, tcon_e; unsigned int lcd_t1 = 0, lcd_t2 = 0, lcd_t3 = 0, lcd_t4 = 0, lcd_t5 = 0; unsigned int lcd_pin, lcd_po, lcd_tcon; #endif extern int sunxi_get_soc_chipid_str(char *serial); static s32 disp_lcd_speed_limit(struct disp_panel_para *panel, u32 *min_dclk, u32 *max_dclk) { char id[17] = ""; unsigned long value = 0; unsigned int qa_val = 0; unsigned int ic_ver = 0, display_cfg_flag = 0; /*init unlimit*/ *min_dclk = 0; *max_dclk = 9999; #if defined(CONFIG_ARCH_SUN50IW10) qa_val = readl(ioremap(0x0300621c, 4)); qa_val = (qa_val >> 28) & 0x00000003; ic_ver = readl(ioremap(0x03000024, 4)) & 0x00000007; display_cfg_flag = (readl(ioremap(0x03006218, 4)) >> 12) & 0x00000001; sunxi_get_soc_chipid_str(id); if (qa_val >= 2 && panel->lcd_if == LCD_IF_DSI) { /*bad IC not support DSI*/ *min_dclk = 9999; *max_dclk = 0; goto OUT; } if (!kstrtoul(id, 16, &value)) { switch (value) { case 0x3c00: /*A53*/ { if (panel->lcd_if == LCD_IF_DSI) { if (qa_val == 0) *min_dclk = 40; /*1024*600@60*/ /* normal qa and B/C ic */ if (qa_val == 1 && ic_ver == 0) { *min_dclk = 64; /*1280*720@60*/ } /*ic version after e*/ if (ic_ver >= 4) *min_dclk = 0; } *max_dclk = 200; /*1280*720@60*/ } break; case 0x0400: /*A100*/ { if (panel->lcd_if == LCD_IF_DSI) { if (qa_val == 0) *min_dclk = 40; /*1024*600@60*/ /* normal qa and B/C ic */ if (qa_val == 1 && ic_ver == 0) { *min_dclk = 64; /*1280*720@60*/ } /*ic version after e*/ if (ic_ver >= 4) *min_dclk = 0; } *max_dclk = 200; /*1280*720@60*/ } break; case 0x1400: /*A133*/ { if (panel->lcd_if == LCD_IF_DSI) { if ((qa_val == 0 && ic_ver == 0) || (qa_val == 1 && ic_ver == 3)) { /*D 01 or B/C 00*/ *min_dclk = 40; } else if (qa_val == 1 && ic_ver == 0) { /*B/C 01*/ *min_dclk = 64; /*1280*720@60*/ } /*ic version after e*/ if (ic_ver >= 4) *min_dclk = 0; } *max_dclk = 200; /*1920*1200@60*/ } break; case 0x1000: /*R818/MR813*/ case 0x2000: { /*not bad ic*/ *max_dclk = 200; /*1920*1200@60*/ } break; case 0x0800: { /*T509*/ if (panel->lcd_if == LCD_IF_DSI) { if (qa_val == 0 && ic_ver == 0) *min_dclk = 40;/*1024*600@60*/ else *min_dclk = 0; } *max_dclk = 200; /*1920*1200@60*/ } break; case 0x4000: { /*not support*/ *min_dclk = 9999; *max_dclk = 0; } break; default: break; } } #endif OUT: /*unlimit */ return 0; } static s32 disp_lcd_tcon_enable(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); u32 min_dclk = 0, max_dclk = 0; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return -1; } disp_lcd_speed_limit(&lcdp->panel_info, &min_dclk, &max_dclk); if (lcdp->panel_info.lcd_dclk_freq < min_dclk || lcdp->panel_info.lcd_dclk_freq > max_dclk) { return 0; } return disp_al_lcd_enable(lcd->hwdev_index, &lcdp->panel_info); } s32 disp_lcd_tcon_disable(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return -1; } return disp_al_lcd_disable(lcd->hwdev_index, &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 ((lcd == NULL) || (lcdp == NULL)) { 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, (bon == 1) ? DISP_PIN_STATE_ACTIVE : DISP_PIN_STATE_SLEEP); disp_al_lcd_io_cfg(lcd->hwdev_index, 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; } #if defined(CONFIG_FPGA_V4_PLATFORM) && defined(SUPPORT_EINK) static s32 disp_lcd_pwm_enable(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long reg = 0; unsigned long val = 0; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } if (disp_lcd_is_used(lcd) && lcdp->pwm_info.dev) { reg = ((unsigned long)0xf1c20878); val = readl(reg); val = val & 0xfff0ffff; val = val | 0x10000; writel(val, reg); reg = ((unsigned long)0xf1c2087C); val = readl(reg); val = val & 0xefffffff; val = val | 0x10000000; writel(val, reg); return 0; } 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); unsigned long reg = 0; unsigned long val = 0; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } if (disp_lcd_is_used(lcd) && lcdp->pwm_info.dev) { reg = ((unsigned long)0xf1c20878); val = readl(reg); val = val & 0xfff0ffff; val = val | 0x10000; writel(val, reg); reg = ((unsigned long)0xf1c2087C); val = readl(reg); val = val & 0xefffffff; val = val | 0x00000000; writel(val, reg); return 0; } DE_WRN("pwm device hdl is NULL\n"); return DIS_FAIL; } #else static s32 disp_lcd_pwm_enable(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } if (disp_lcd_is_used(lcd) && lcdp->pwm_info.dev) 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); s32 ret = -1; struct pwm_device *pwm_dev; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } if (disp_lcd_is_used(lcd) && lcdp->pwm_info.dev) { ret = disp_sys_pwm_disable(lcdp->pwm_info.dev); pwm_dev = (struct pwm_device *)lcdp->pwm_info.dev; /*following is for reset pwm state purpose*/ disp_sys_pwm_config(lcdp->pwm_info.dev, pwm_dev->state.duty_cycle - 1, pwm_dev->state.period); disp_sys_pwm_set_polarity(lcdp->pwm_info.dev, !lcdp->pwm_info.polarity); return ret; } DE_WRN("pwm device hdl is NULL\n"); return DIS_FAIL; } #endif static s32 disp_lcd_backlight_enable(struct disp_device *lcd) { struct disp_gpio_set_t gpio_info[1]; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long flags; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->bl_enabled) { spin_unlock_irqrestore(&lcd_data_lock, flags); return -EBUSY; } lcdp->bl_need_enabled = 1; lcdp->bl_enabled = true; 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(struct disp_gpio_set_t)); lcdp->lcd_cfg.lcd_bl_gpio_hdl = disp_sys_gpio_request(gpio_info, 1); } bl = disp_lcd_get_bright(lcd); disp_lcd_set_bright(lcd, bl); } return 0; } static s32 disp_lcd_backlight_disable(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long flags; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } spin_lock_irqsave(&lcd_data_lock, flags); if (!lcdp->bl_enabled) { spin_unlock_irqrestore(&lcd_data_lock, flags); return -EBUSY; } lcdp->bl_enabled = false; spin_unlock_irqrestore(&lcd_data_lock, flags); if (disp_lcd_is_used(lcd)) { if (lcdp->lcd_cfg.lcd_bl_en_used) { disp_sys_gpio_release(lcdp->lcd_cfg.lcd_bl_gpio_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; } #if defined(CONFIG_FPGA_V4_PLATFORM) && defined(SUPPORT_EINK) static s32 disp_lcd_power_enable(struct disp_device *lcd, u32 power_id) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long reg = 0; unsigned long val = 0; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } if (disp_lcd_is_used(lcd)) { reg = ((unsigned long)0xf1c20878); val = readl(reg); val = val & 0x000fffff; val = val | 0x11100000; writel(val, reg); reg = ((unsigned long)0xf1c2087C); val = readl(reg); val = val & 0x1fffffff; val = val | 0xe0000000; writel(val, reg); return 0; } return DIS_FAIL; } static s32 disp_lcd_power_disable(struct disp_device *lcd, u32 power_id) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long reg = 0; unsigned long val = 0; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } if (disp_lcd_is_used(lcd)) { reg = ((unsigned long)0xf1c20878); val = readl(reg); val = val & 0x000fffff; val = val | 0x77700000; writel(val, reg); return 0; } return DIS_FAIL; } #else 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 ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } if (disp_lcd_is_used(lcd)) { if (lcdp->lcd_cfg.lcd_power_used[power_id] == 1) { /* 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 ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } if (disp_lcd_is_used(lcd)) { if (lcdp->lcd_cfg.lcd_power_used[power_id] == 1) { /* regulator type */ disp_sys_power_disable(lcdp->lcd_cfg. lcd_power[power_id]); } } return 0; } #endif 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 ((lcd == NULL) || (lcdp == NULL)) { 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 ((lcd == NULL) || (lcdp == NULL)) { 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 ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } mgr = lcd->manager; if (mgr == NULL) { 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 = (lcdp->lcd_cfg.backlight_dimming == 0) ? 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 ((lcd == NULL) || (lcdp == NULL)) { 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 ((lcd == NULL) || (lcdp == NULL)) { 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, struct disp_panel_para *info) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } memcpy(info, (struct disp_panel_para *) (&(lcdp->panel_info)), sizeof(struct disp_panel_para)); return 0; } #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; #if defined(SUPPORT_EINK) && defined(CONFIG_EINK_PANEL_USED) struct disp_eink_manager *eink_manager = NULL; #endif u32 hwdev_index; u32 irq_flag = 0; unsigned int panel_extend_dirty; unsigned long flags; if (lcd == NULL) return DISP_IRQ_RETURN; hwdev_index = lcd->hwdev_index; lcdp = disp_lcd_get_priv(lcd); if (lcdp == NULL) return DISP_IRQ_RETURN; #if defined(SUPPORT_EINK) && defined(CONFIG_EINK_PANEL_USED) eink_manager = disp_get_eink_manager(0); if (eink_manager == NULL) return DISP_IRQ_RETURN; #endif if (disp_al_lcd_query_irq (hwdev_index, LCD_IRQ_TCON0_VBLK, &lcdp->panel_info)) { #if defined(SUPPORT_EINK) && defined(CONFIG_EINK_PANEL_USED) eink_display_one_frame(eink_manager); #else int cur_line = disp_al_lcd_get_cur_line(hwdev_index, &lcdp->panel_info); int start_delay = disp_al_lcd_get_start_delay(hwdev_index, &lcdp->panel_info); #if defined(CONFIG_DISP2_LCD_ESD_DETECT) if (lcdp->lcd_panel_fun.esd_check && lcdp->lcd_panel_fun.reset_panel) { ++lcdp->esd_inf.cnt; if (cur_line < 2 && !atomic_read(&lcdp->lcd_resetting) && lcdp->esd_inf.cnt >= lcdp->esd_inf.freq) { if (!lcdp->esd_inf.esd_check_func_pos || lcdp->lcd_panel_fun.esd_check(lcd->disp)) { /*request reset*/ atomic_set(&lcdp->lcd_resetting, 1); schedule_work(&lcdp->reflush_work); } lcdp->esd_inf.cnt = 0; } } #endif mgr = lcd->manager; if (mgr == NULL) return DISP_IRQ_RETURN; if (cur_line <= (start_delay - lcdp->judge_line)) sync_event_proc(mgr->disp, false); else sync_event_proc(mgr->disp, true); #endif } else { irq_flag = disp_al_lcd_query_irq(hwdev_index, LCD_IRQ_TCON0_CNTR, &lcdp->panel_info); irq_flag |= disp_al_lcd_query_irq(hwdev_index, LCD_IRQ_TCON0_TRIF, &lcdp->panel_info); if (irq_flag == 0) goto exit; if (disp_al_lcd_tri_busy(hwdev_index, &lcdp->panel_info)) { /* if lcd is still busy when tri/cnt irq coming, * take it as failture, record failture times, * when it reach 2 times, clear counter */ lcdp->tri_finish_fail++; lcdp->tri_finish_fail = (lcdp->tri_finish_fail == 2) ? 0 : lcdp->tri_finish_fail; } else lcdp->tri_finish_fail = 0; mgr = lcd->manager; if (mgr == NULL) return DISP_IRQ_RETURN; #if defined(CONFIG_DISP2_LCD_ESD_DETECT) if (lcdp->lcd_panel_fun.esd_check && lcdp->lcd_panel_fun.reset_panel) { ++lcdp->esd_inf.cnt; if (!atomic_read(&lcdp->lcd_resetting) && lcdp->esd_inf.cnt >= lcdp->esd_inf.freq) { if (!lcdp->esd_inf.esd_check_func_pos || lcdp->lcd_panel_fun.esd_check(lcd->disp)) { /*request reset*/ atomic_set(&lcdp->lcd_resetting, 1); schedule_work(&lcdp->reflush_work); } lcdp->esd_inf.cnt = 0; } } #endif if (lcdp->tri_finish_fail == 0) { sync_event_proc(mgr->disp, false); disp_al_lcd_tri_start(hwdev_index, &lcdp->panel_info); } else sync_event_proc(mgr->disp, true); } spin_lock_irqsave(&lcd_data_lock, flags); panel_extend_dirty = lcdp->panel_extend_dirty; lcdp->panel_extend_dirty = 0; spin_unlock_irqrestore(&lcd_data_lock, flags); if (panel_extend_dirty == 1) disp_al_lcd_cfg_ext(lcd->disp, &lcdp->panel_extend_info_set); exit: return DISP_IRQ_RETURN; } #if defined(CONFIG_DISP2_LCD_ESD_DETECT) static void disp_lcd_reflush_work(struct work_struct *work) { struct disp_lcd_private_data *lcdp = container_of(work, struct disp_lcd_private_data, reflush_work); struct disp_device *lcd = disp_device_get_from_priv((void *)lcdp); unsigned long flags; if (!lcdp || !lcd) { DE_WRN("lcdp is null\n"); return; } /*lcd is not enabled or is enabling*/ if (disp_lcd_is_enabled(lcd) == 0 || lcdp->enabling == 1) return; /*lcd is resetting*/ if (atomic_read(&lcdp->lcd_resetting) == 2) return; if (!lcdp->esd_inf.esd_check_func_pos) if (lcdp->lcd_panel_fun.esd_check) if (!lcdp->lcd_panel_fun.esd_check(lcd->disp)) { atomic_set(&lcdp->lcd_resetting, 0); return; /*everything is just fine*/ } if (lcdp->esd_inf.level == 1) { spin_lock_irqsave(&lcd_data_lock, flags); lcdp->enabled = 0; lcdp->enabling = 1; spin_unlock_irqrestore(&lcd_data_lock, flags); atomic_set(&lcdp->lcd_resetting, 2); disp_lcd_tcon_disable(lcd); disp_al_lcd_cfg(lcd->hwdev_index, &lcdp->panel_info, &lcdp->panel_extend_info_set); } else atomic_set(&lcdp->lcd_resetting, 2); ++lcdp->esd_inf.rst_cnt; if (lcdp->lcd_panel_fun.reset_panel) lcdp->lcd_panel_fun.reset_panel(lcd->disp); if (lcdp->esd_inf.level == 1) { disp_lcd_tcon_enable(lcd); spin_lock_irqsave(&lcd_data_lock, flags); lcdp->enabled = 1; lcdp->enabling = 0; spin_unlock_irqrestore(&lcd_data_lock, flags); } disp_delay_ms(300); /*lcd reset finish*/ atomic_set(&lcdp->lcd_resetting, 0); } #endif static s32 disp_lcd_cal_fps(struct disp_device *lcd) { s32 ret = -1; struct disp_lcd_private_data *lcdp = NULL; struct disp_panel_para *panel_info = NULL; if (!lcd) goto OUT; lcdp = disp_lcd_get_priv(lcd); if (!lcdp) goto OUT; panel_info = &lcdp->panel_info; lcdp->frame_per_sec = DIV_ROUND_CLOSEST(panel_info->lcd_dclk_freq * 1000000 * (panel_info->lcd_interlace + 1), panel_info->lcd_ht * panel_info->lcd_vt); ret = 0; OUT: return ret; } static s32 disp_lcd_panel_open(struct disp_device *lcd, bool skip_open_backlight) { int i; int ret = -1; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return ret; } 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%d_panel_fun.cfg_open_flow is NULL\n", lcd->disp); if (skip_open_backlight) { lcdp->open_flow.func_num = lcdp->open_flow.func_num - 1; } 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 (lcdp->open_flow.func[i].delay != 0) disp_delay_ms(lcdp->open_flow.func[i].delay); } } if (lcdp->is_bridge && lcdp->bridge->enable_bridge) ret = lcdp->bridge->enable_bridge(); if (ret) DE_WRN("lcd%d enable_bridge failed\n", lcd->disp); return 0; } static s32 disp_lcd_panel_close(struct disp_device *lcd) { int i; int ret = -1; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return ret; } 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); if (lcdp->is_bridge && lcdp->bridge->disable_bridge) { ret = lcdp->bridge->disable_bridge(); if (ret) DE_WRN("lcd%d disable_bridge failed\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 (lcdp->close_flow.func[i].delay != 0) disp_delay_ms(lcdp->close_flow.func[i].delay); } } return 0; } /* lcd enable except for backlight */ static s32 disp_lcd_fake_enable(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); int i, ret = DIS_FAIL; struct disp_manager *mgr = NULL; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return ret; } mutex_lock(&lcdp->enable_lock); DE_INF("lcd %d\n", lcd->disp); mgr = lcd->manager; if (mgr == NULL) { DE_WRN("mgr is NULL!\n"); goto OUT; } if (disp_lcd_is_enabled(lcd) == 1) { ret = 0; goto OUT; } disp_lcd_cal_fps(lcd); if (mgr->enable) mgr->enable(mgr); /* init fix power */ for (i = 0; i < LCD_POWER_NUM; i++) { if (lcdp->lcd_cfg.lcd_fix_power_used[i] == 1) disp_sys_power_enable(lcdp->lcd_cfg.lcd_fix_power[i]); } #if defined(SUPPORT_DSI) && defined(DSI_VERSION_40) if ((lcdp->panel_info.lcd_if == LCD_IF_DSI) && (lcdp->irq_no_dsi != 0)) { if (lcdp->panel_info.lcd_dsi_if == LCD_DSI_IF_COMMAND_MODE) { disp_sys_register_irq(lcdp->irq_no, 0, disp_lcd_event_proc, (void *)lcd, 0, 0); disp_sys_enable_irq(lcdp->irq_no); } else { 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 #endif { disp_sys_register_irq(lcdp->irq_no, 0, disp_lcd_event_proc, (void *)lcd, 0, 0); disp_sys_enable_irq(lcdp->irq_no); } lcdp->enabling = 1; lcdp->bl_need_enabled = 0; disp_lcd_gpio_init(lcd); lcd_clk_enable(lcd); ret = cal_real_frame_period(lcd); if (ret) DE_WRN("cal_real_frame_period fail:%d\n", ret); disp_sys_pwm_set_polarity(lcdp->pwm_info.dev, lcdp->pwm_info.polarity); disp_al_lcd_cfg(lcd->hwdev_index, &lcdp->panel_info, &lcdp->panel_extend_info_set); disp_lcd_panel_open(lcd, true); lcdp->enabled = 1; lcdp->enabling = 0; #if defined(CONFIG_DISP2_LCD_ESD_DETECT) atomic_set(&lcdp->lcd_resetting, 0); #endif ret = 0; OUT: mutex_unlock(&lcdp->enable_lock); return ret; } #if defined(SUPPORT_EINK) && defined(CONFIG_EINK_PANEL_USED) static s32 disp_lcd_enable(struct disp_device *lcd) { unsigned long flags; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); struct disp_manager *mgr = NULL; int ret; int i; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } flush_work(&lcd->close_eink_panel_work); DE_INF("lcd %d\n", lcd->disp); mgr = lcd->manager; if (mgr == NULL) { DE_WRN("mgr is NULL!\n"); return DIS_FAIL; } if (disp_lcd_is_enabled(lcd) == 1) return 0; 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); disp_lcd_gpio_init(lcd); ret = lcd_clk_enable(lcd); if (ret != 0) return DIS_FAIL; ret = cal_real_frame_period(lcd); if (ret) DE_WRN("cal_real_frame_period fail:%d\n", ret); disp_al_lcd_cfg(lcd->hwdev_index, &lcdp->panel_info, &lcdp->panel_extend_info);/* init tcon_lcd regs */ disp_lcd_panel_open(lcd, false); 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 ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } DE_INF("lcd %d\n", lcd->disp); mgr = lcd->manager; if (mgr == NULL) { DE_WRN("mgr is NULL!\n"); return DIS_FAIL; } if (disp_lcd_is_enabled(lcd) == 0) return 0; spin_lock_irqsave(&lcd_data_lock, flags); lcdp->enabled = 0; spin_unlock_irqrestore(&lcd_data_lock, flags); lcdp->bl_need_enabled = 0; disp_lcd_panel_close(lcd); lcd_clk_disable(lcd); disp_lcd_gpio_exit(lcd); disp_sys_disable_irq(lcdp->irq_no); disp_sys_unregister_irq(lcdp->irq_no, disp_lcd_event_proc, (void *)lcd); return 0; } #else static s32 disp_lcd_enable(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); int i; struct disp_manager *mgr = NULL; unsigned bl; int ret = DIS_FAIL; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return ret; } mutex_lock(&lcdp->enable_lock); __inf("lcd %d\n", lcd->disp); mgr = lcd->manager; if (mgr == NULL) { DE_WRN("mgr is NULL!\n"); goto OUT; } if (disp_lcd_is_enabled(lcd) == 1) { ret = 0; goto OUT; } disp_lcd_cal_fps(lcd); if (mgr->enable) mgr->enable(mgr); /* init fix power */ for (i = 0; i < LCD_POWER_NUM; i++) { if (lcdp->lcd_cfg.lcd_fix_power_used[i] == 1) disp_sys_power_enable(lcdp->lcd_cfg.lcd_fix_power[i]); } #if defined(SUPPORT_DSI) && defined(DSI_VERSION_40) if ((lcdp->panel_info.lcd_if == LCD_IF_DSI) && (lcdp->irq_no_dsi != 0)) { if (lcdp->panel_info.lcd_dsi_if == LCD_DSI_IF_COMMAND_MODE) { disp_sys_register_irq(lcdp->irq_no, 0, disp_lcd_event_proc, (void *)lcd, 0, 0); disp_sys_enable_irq(lcdp->irq_no); } else { 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 #endif { disp_sys_register_irq(lcdp->irq_no, 0, disp_lcd_event_proc, (void *)lcd, 0, 0); disp_sys_enable_irq(lcdp->irq_no); } lcdp->enabling = 1; lcdp->bl_need_enabled = 0; lcdp->panel_extend_info.lcd_gamma_en = lcdp->panel_info.lcd_gamma_en; disp_lcd_gpio_init(lcd); ret = lcd_clk_enable(lcd); if (ret != 0) goto OUT; ret = cal_real_frame_period(lcd); if (ret) DE_WRN("cal_real_frame_period fail:%d\n", ret); disp_sys_pwm_set_polarity(lcdp->pwm_info.dev, lcdp->pwm_info.polarity); disp_al_lcd_cfg(lcd->hwdev_index, &lcdp->panel_info, &lcdp->panel_extend_info_set); lcd_calc_judge_line(lcd); #if defined(CONFIG_DISP2_LCD_ESD_DETECT) if (lcdp->lcd_panel_fun.set_esd_info) { lcdp->lcd_panel_fun.set_esd_info(&lcdp->esd_inf); } else { /*default value*/ lcdp->esd_inf.level = 0; lcdp->esd_inf.freq = 60; lcdp->esd_inf.esd_check_func_pos = 0; lcdp->esd_inf.cnt = 0; } #endif disp_lcd_panel_open(lcd, false); lcdp->enabled = 1; lcdp->enabling = 0; bl = disp_lcd_get_bright(lcd); disp_lcd_set_bright(lcd, bl); #if defined(CONFIG_DISP2_LCD_ESD_DETECT) atomic_set(&lcdp->lcd_resetting, 0); #endif ret = 0; OUT: mutex_unlock(&lcdp->enable_lock); return ret; } static s32 disp_lcd_disable(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); struct disp_manager *mgr = NULL; int i = 0, ret = DIS_FAIL; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return ret; } mutex_lock(&lcdp->enable_lock); DE_INF("lcd %d\n", lcd->disp); mgr = lcd->manager; if (mgr == NULL) { DE_WRN("mgr is NULL!\n"); goto OUT; } if (disp_lcd_is_enabled(lcd) == 0) { ret = 0; goto OUT; } #if defined(CONFIG_DISP2_LCD_ESD_DETECT) atomic_set(&lcdp->lcd_resetting, 2); #endif lcdp->enabled = 0; lcdp->bl_need_enabled = 0; disp_lcd_panel_close(lcd); disp_lcd_gpio_exit(lcd); lcd_clk_disable(lcd); #if defined(SUPPORT_DSI) && defined(DSI_VERSION_40) if ((lcdp->panel_info.lcd_if == LCD_IF_DSI) && (lcdp->irq_no_dsi != 0)) { if (lcdp->panel_info.lcd_dsi_if == LCD_DSI_IF_COMMAND_MODE) { disp_sys_disable_irq(lcdp->irq_no); disp_sys_unregister_irq( lcdp->irq_no, disp_lcd_event_proc, (void *)lcd); } else { disp_sys_disable_irq(lcdp->irq_no_dsi); disp_sys_unregister_irq( lcdp->irq_no_dsi, disp_lcd_event_proc, (void *)lcd); } } else #endif { 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 (lcdp->lcd_cfg.lcd_fix_power_used[i] == 1) disp_sys_power_disable(lcdp->lcd_cfg.lcd_fix_power[i]); } if (mgr->disable) mgr->disable(mgr); ret = 0; OUT: mutex_unlock(&lcdp->enable_lock); return ret; } #endif 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, ret; struct disp_manager *mgr = NULL; struct disp_gpio_set_t gpio_info[1]; struct pwm_device *pwm_dev; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } mgr = lcd->manager; if (mgr == NULL) { DE_WRN("mgr is NULL!\n"); return DIS_FAIL; } disp_lcd_cal_fps(lcd); lcd_calc_judge_line(lcd); if (mgr->sw_enable) mgr->sw_enable(mgr); #if !defined(CONFIG_COMMON_CLK_ENABLE_SYNCBOOT) if (lcd_clk_enable(lcd) != 0) return DIS_FAIL; #endif ret = cal_real_frame_period(lcd); if (ret) DE_WRN("cal_real_frame_period fail:%d\n", ret); /* init fix power */ for (i = 0; i < LCD_POWER_NUM; i++) { if (lcdp->lcd_cfg.lcd_fix_power_used[i] == 1) disp_sys_power_enable(lcdp->lcd_cfg.lcd_fix_power[i]); } /* init lcd power */ for (i = 0; i < LCD_POWER_NUM; i++) { if (lcdp->lcd_cfg.lcd_power_used[i] == 1) 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(struct disp_gpio_set_t)); lcdp->lcd_cfg.gpio_hdl[i] = disp_sys_gpio_request(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); lcdp->lcd_cfg.lcd_bl_gpio_hdl = disp_sys_gpio_request(&lcdp->lcd_cfg.lcd_bl_en, 1); } if (lcdp->panel_info.lcd_pwm_used && lcdp->pwm_info.dev) { pwm_dev = (struct pwm_device *)lcdp->pwm_info.dev; pwm_dev->state.period = lcdp->pwm_info.period_ns; pwm_dev->state.duty_cycle = lcdp->pwm_info.duty_ns; disp_sys_pwm_set_polarity(lcdp->pwm_info.dev, lcdp->pwm_info.polarity); pwm_dev->state.enabled = true; } spin_lock_irqsave(&lcd_data_lock, flags); lcdp->enabled = 1; lcdp->enabling = 0; lcdp->bl_need_enabled = 1; lcdp->bl_enabled = true; spin_unlock_irqrestore(&lcd_data_lock, flags); #if defined(CONFIG_DISP2_LCD_ESD_DETECT) if (lcdp->lcd_panel_fun.set_esd_info) { lcdp->lcd_panel_fun.set_esd_info(&lcdp->esd_inf); } else { /*default value*/ lcdp->esd_inf.level = 0; lcdp->esd_inf.freq = 60; lcdp->esd_inf.esd_check_func_pos = 0; lcdp->esd_inf.cnt = 0; } #endif disp_al_lcd_disable_irq(lcd->hwdev_index, LCD_IRQ_TCON0_VBLK, &lcdp->panel_info); #if defined(SUPPORT_DSI) && defined(DSI_VERSION_40) if ((lcdp->panel_info.lcd_if == LCD_IF_DSI) && (lcdp->irq_no_dsi != 0)) { if (lcdp->panel_info.lcd_dsi_if == LCD_DSI_IF_COMMAND_MODE) { disp_sys_register_irq(lcdp->irq_no, 0, disp_lcd_event_proc, (void *)lcd, 0, 0); disp_sys_enable_irq(lcdp->irq_no); } else { 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 #endif { 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->hwdev_index, 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 ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } return (s32)lcdp->enabled; } /** * disp_lcd_check_if_enabled - check lcd if be enabled status * * this function only be used by bsp_disp_sync_with_hw to check * the device enabled status when driver init */ s32 disp_lcd_check_if_enabled(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); int ret = 1; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } #if !defined(CONFIG_COMMON_CLK_ENABLE_SYNCBOOT) if (lcdp->clk && (__clk_get_enable_count(lcdp->clk) == 0)) ret = 0; #endif return ret; } 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 ((lcd == NULL) || (lcdp == NULL)) { 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 ((lcd == NULL) || (lcdp == NULL)) { 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, struct 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 ((lcd == NULL) || (lcdp == NULL)) { 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 ((ret == 2) && !strcmp(drv_name, name)) { memset(&lcdp->lcd_panel_fun, 0, sizeof(struct 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.esd_check = lcd_cfg->esd_check; lcdp->lcd_panel_fun.reset_panel = lcd_cfg->reset_panel; lcdp->lcd_panel_fun.set_esd_info = lcd_cfg->set_esd_info; lcdp->lcd_panel_fun.lcd_user_defined_func = lcd_cfg->lcd_user_defined_func; lcdp->lcd_panel_fun.set_bright = lcd_cfg->set_bright; if (lcdp->lcd_panel_fun.cfg_panel_info) { lcdp->lcd_panel_fun.cfg_panel_info(&lcdp->panel_extend_info); memcpy(&lcdp->panel_extend_info_set, &lcdp->panel_extend_info, sizeof(struct panel_extend_para)); } else { DE_WRN("lcd_panel_fun[%d].cfg_panel_info is NULL\n", lcd->disp); } 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 ((lcd == NULL) || (lcdp == NULL)) { 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]) { struct disp_gpio_set_t gpio_info[1]; memcpy(gpio_info, &(lcdp->lcd_cfg.lcd_gpio[i]), sizeof(struct 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 ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } for (i = 0; i < LCD_GPIO_NUM; i++) { if (lcdp->lcd_cfg.gpio_hdl[i]) { struct 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(struct 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 ((lcd == NULL) || (lcdp == NULL)) { 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 ((lcd == NULL) || (lcdp == NULL)) { 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 ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } if (io_index >= LCD_GPIO_NUM) { DE_WRN("gpio num out of range\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 ((lcd == NULL) || (lcdp == NULL)) { 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 ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return 0; } return disp_al_lcd_get_status(lcd->hwdev_index, &lcdp->panel_info); } static bool disp_lcd_is_in_safe_period(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); int start_delay; int cur_line; bool ret = true; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); goto exit; } start_delay = disp_al_lcd_get_start_delay(lcd->hwdev_index, &lcdp->panel_info); cur_line = disp_al_lcd_get_cur_line(lcd->hwdev_index, &lcdp->panel_info); if (cur_line >= start_delay) ret = false; exit: return ret; } static s32 disp_lcd_update_gamma_tbl_set(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); int i; unsigned int *gamma, *gamma_set; unsigned int r, g, b; s32 color_temperature; u32 color_inverse; color_temperature = lcdp->color_temperature; color_inverse = lcdp->color_inverse; memcpy(&lcdp->panel_extend_info_set, &lcdp->panel_extend_info, sizeof(struct panel_extend_para)); gamma = lcdp->panel_extend_info.lcd_gamma_tbl; gamma_set = lcdp->panel_extend_info_set.lcd_gamma_tbl; if (color_temperature > 0) { /* warm color */ for (i = 0; i < 256; i++) { r = (gamma[i] >> 16) & 0xff; g = (gamma[i] >> 8) & 0xff; b = gamma[i] & 0xff; g = g * (512 - color_temperature) / 512; b = b * (256 - color_temperature) / 256; r = r << 16; g = g << 8; gamma_set[i] = r | g | b; } } else if (color_temperature < 0) { /* cool color */ for (i = 0; i < 256; i++) { r = (gamma[i] >> 16) & 0xff; g = (gamma[i] >> 8) & 0xff; b = gamma[i] & 0xff; r = r * (256 + color_temperature) / 256; g = g * (512 + color_temperature) / 512; r = r << 16; g = g << 8; gamma_set[i] = r | g | b; } } if (color_inverse == 1) { for (i = 0; i < 256; i++) gamma_set[i] = 0xffffffff - gamma_set[i]; } if (color_inverse != 0) lcdp->panel_extend_info_set.lcd_gamma_en = 1; if (color_temperature != 0) lcdp->panel_extend_info_set.lcd_gamma_en = 1; return 0; } static s32 disp_lcd_set_gamma_tbl(struct disp_device *lcd, unsigned int *gamma_table, unsigned int size) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long flags; if ((lcd == NULL) || (lcdp == NULL) || (gamma_table == NULL)) { DE_WRN("NULL hdl!\n"); return 0; } size = (size > LCD_GAMMA_TABLE_SIZE) ? LCD_GAMMA_TABLE_SIZE : size; spin_lock_irqsave(&lcd_data_lock, flags); memcpy(lcdp->panel_extend_info.lcd_gamma_tbl, gamma_table, size); disp_lcd_update_gamma_tbl_set(lcd); lcdp->panel_extend_dirty = 1; spin_unlock_irqrestore(&lcd_data_lock, flags); return 0; } static s32 disp_lcd_enable_gamma(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long flags; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return 0; } spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->panel_extend_info.lcd_gamma_en == 0) { lcdp->panel_extend_info.lcd_gamma_en = 1; disp_lcd_update_gamma_tbl_set(lcd); lcdp->panel_extend_dirty = 1; } spin_unlock_irqrestore(&lcd_data_lock, flags); return 0; } static s32 disp_lcd_disable_gamma(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); int ret; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return 0; } if (lcdp->panel_extend_info.lcd_gamma_en == 1) { lcdp->panel_extend_info.lcd_gamma_en = 0; ret = disp_al_lcd_cfg_ext(lcd->disp, &lcdp->panel_extend_info); } else { ret = 0; } return ret; } static s32 disp_lcd_get_fps(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return 0; } return lcdp->frame_per_sec; } static s32 disp_lcd_set_color_temperature(struct disp_device *lcd, s32 color_temperature) { 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 -1; } spin_lock_irqsave(&lcd_data_lock, flags); lcdp->color_temperature = color_temperature; disp_lcd_update_gamma_tbl_set(lcd); lcdp->panel_extend_dirty = 1; spin_unlock_irqrestore(&lcd_data_lock, flags); return 0; } static s32 disp_lcd_get_color_temperature(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long flags; s32 color_temperature = 0; if ((NULL == lcd) || (NULL == lcdp)) { DE_WRN("NULL hdl!\n"); return 0; } spin_lock_irqsave(&lcd_data_lock, flags); color_temperature = lcdp->color_temperature; spin_unlock_irqrestore(&lcd_data_lock, flags); return color_temperature; } static s32 disp_lcd_init(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); int i; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } DE_INF("lcd %d\n", lcd->disp); mutex_init(&lcdp->enable_lock); lcd_get_sys_config(lcd->disp, &lcdp->lcd_cfg); if (disp_lcd_is_used(lcd)) { struct disp_video_timings *timmings; struct disp_panel_para *panel_info; lcd_parse_panel_para(lcd->disp, &lcdp->panel_info); lcdp->panel_extend_info.lcd_cmap_en = lcdp->panel_info.lcd_cmap_en; lcdp->panel_extend_info.lcd_gamma_en = lcdp->panel_info.lcd_gamma_en; if (lcdp->panel_extend_info.lcd_gamma_en || lcdp->panel_extend_info.lcd_cmap_en) lcdp->panel_extend_dirty = 1; if (lcdp->panel_info.lcd_if == LCD_IF_DSI && lcdp->panel_info.lcd_tcon_mode == DISP_TCON_DUAL_DSI && lcdp->panel_info.lcd_dsi_port_num == DISP_LCD_DSI_SINGLE_PORT) { lcdp->panel_info.lcd_ht *= 2; lcdp->panel_info.lcd_hspw *= 2; lcdp->panel_info.lcd_x *= 2; lcdp->panel_info.lcd_hbp *= 2; lcdp->panel_info.lcd_dclk_freq *= 2; } 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); /* default 1khz */ period_ns = 1000 * 1000 * 1000 / 1000; } backlight_bright = lcdp->lcd_cfg.backlight_bright; duty_ns = (backlight_bright * period_ns) / 256; 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; disp_sys_pwm_config(lcdp->pwm_info.dev, duty_ns, period_ns); } lcd_clk_init(lcd); for (i = 0; i < 256; i++) { lcdp->panel_extend_info.lcd_gamma_tbl[i] = (i << 24) | (i << 16) | (i << 8) | (i); } } /* lcd_panel_parameter_check(lcd->disp, lcd); */ return 0; } #if defined(SUPPORT_EINK) && defined(CONFIG_EINK_PANEL_USED) static void disp_close_eink_panel_task(struct work_struct *work) { /* (unsigned long parg) */ struct disp_device *plcd = NULL; plcd = disp_device_find(0, DISP_OUTPUT_TYPE_LCD); plcd->disable(plcd); display_finish_flag = 1; } #endif static disp_config_update_t disp_lcd_check_config_dirty(struct disp_device *lcd, struct disp_device_config *config) { disp_config_update_t ret = DISP_NOT_UPDATE; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long flags; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); goto exit; } spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->enabled == 0 || (lcdp->is_bridge && lcdp->mode_using != config->mode)) ret = DISP_NORMAL_UPDATE; spin_unlock_irqrestore(&lcd_data_lock, flags); exit: return ret; } static int disp_lcd_remove_video_timing(struct disp_device *dispdev, enum disp_tv_mode mode) { unsigned long flags; struct lcd_timing_private *timing; struct lcd_timing_private *timing_tmp; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(dispdev); int ret = -1; if (!dispdev || !lcdp) { DE_WRN("NULL hdl!\n"); ret = -1; return ret; } spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->is_bridge == false) { DE_WRN("error: lcd%d is not register as bridge!\n", dispdev->disp); spin_unlock_irqrestore(&lcd_data_lock, flags); return -1; } plist_for_each_entry_safe(timing, timing_tmp, &lcd_private[dispdev->disp].timing_head, list) { if (timing->timing.mode == mode) { plist_del(&timing->list, &lcdp->timing_head); kfree(timing); ret = 0; } } spin_unlock_irqrestore(&lcd_data_lock, flags); if (ret == -1) DE_WRN("remove mode for lcd%d failed, mode not found!\n", dispdev->disp); return ret; } static int disp_lcd_get_video_timing(struct disp_device *dispdev, struct disp_lcd_timing *timings, int *num) { unsigned long flags; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(dispdev); struct lcd_timing_private *timing_list; int ret = 0; if (!dispdev || !lcdp) { DE_WRN("NULL hdl!\n"); ret = -1; return ret; } spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->is_bridge == false) { DE_WRN("error: lcd%d is not register as bridge!\n", dispdev->disp); spin_unlock_irqrestore(&lcd_data_lock, flags); return -1; } *num = 0; plist_for_each_entry(timing_list, &lcd_private[dispdev->disp].timing_head, list) { if (timings != NULL) memcpy(&timings[*num], &timing_list->timing, sizeof(struct disp_lcd_timing)); (*num)++; } spin_unlock_irqrestore(&lcd_data_lock, flags); return ret; } static int disp_lcd_add_video_timing(struct disp_device *dispdev, struct disp_lcd_timing *timings) { struct lcd_timing_private *timing; struct lcd_timing_private *timing_list; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(dispdev); unsigned long flags; bool is_replace = false; int ret = 0; int i = 0; if (!dispdev || !lcdp) { DE_WRN("NULL hdl!\n"); ret = -1; return ret; } spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->is_bridge == false) { DE_WRN("error: lcd%d is not register as bridge!\n", dispdev->disp); spin_unlock_irqrestore(&lcd_data_lock, flags); return -1; } plist_for_each_entry(timing_list, &lcd_private[dispdev->disp].timing_head, list) { if (timing_list->timing.mode == timings[0].mode) { /*already exist, replace with new one*/ timing_list->timing.mode = timings[0].mode; timing_list->timing.lcd_x = timings[0].lcd_x; timing_list->timing.lcd_y = timings[0].lcd_y; timing_list->timing.lcd_dclk_freq = timings[0].lcd_dclk_freq; timing_list->timing.lcd_hbp = timings[0].lcd_hbp; timing_list->timing.lcd_ht = timings[0].lcd_ht; timing_list->timing.lcd_vbp = timings[0].lcd_vbp; timing_list->timing.lcd_vt = timings[0].lcd_vt; timing_list->timing.lcd_vspw = timings[0].lcd_vspw; timing_list->timing.lcd_hspw = timings[0].lcd_hspw; is_replace = true; DE_WRN("find video mode:0x%x already exist for lcd%d, replace with new one!\n", timing_list->timing.mode, dispdev->disp); break; } } if (is_replace == false) {/*add new one*/ timing = kmalloc(sizeof(struct lcd_timing_private), GFP_ATOMIC | __GFP_ZERO); timing->timing.mode = timings[i].mode; timing->timing.lcd_x = timings[i].lcd_x; timing->timing.lcd_y = timings[i].lcd_y; timing->timing.lcd_dclk_freq = timings[i].lcd_dclk_freq; timing->timing.lcd_hbp = timings[i].lcd_hbp; timing->timing.lcd_ht = timings[i].lcd_ht; timing->timing.lcd_vbp = timings[i].lcd_vbp; timing->timing.lcd_vt = timings[i].lcd_vt; timing->timing.lcd_vspw = timings[i].lcd_vspw; timing->timing.lcd_hspw = timings[i].lcd_hspw; plist_node_init(&timing->list, timings[i].mode); plist_add(&(timing->list), &lcdp->timing_head); } spin_unlock_irqrestore(&lcd_data_lock, flags); return ret; } static int disp_lcd_register_bridge(struct disp_device *dispdev, struct disp_bridge_fun *bridge) { int ret = 0; unsigned long flags; struct lcd_timing_private timing; char primary_key[25]; s32 value = 0; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(dispdev); if (!dispdev || !lcdp) { DE_WRN("NULL hdl!\n"); ret = -1; goto exit; } if (bridge->enable_bridge != NULL && bridge->disable_bridge != NULL && bridge->set_video_mode != NULL) { spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->is_bridge) { ret = -1; DE_WRN("error: lcd%d is already register as bridge!\n", dispdev->disp); } else { lcdp->bridge = bridge; lcdp->is_bridge = true; } spin_unlock_irqrestore(&lcd_data_lock, flags); if (ret) goto exit; sprintf(primary_key, "lcd%d", dispdev->disp); ret = 1; ret &= disp_sys_script_get_item(primary_key, "lcd_x", &value, 1); if (ret == 1) timing.timing.lcd_x = value; ret &= disp_sys_script_get_item(primary_key, "lcd_y", &value, 1); if (ret == 1) timing.timing.lcd_y = value; ret &= disp_sys_script_get_item(primary_key, "lcd_dclk_freq", &value, 1); if (ret == 1) timing.timing.lcd_dclk_freq = value; ret &= disp_sys_script_get_item(primary_key, "lcd_hbp", &value, 1); if (ret == 1) timing.timing.lcd_hbp = value; ret &= disp_sys_script_get_item(primary_key, "lcd_ht", &value, 1); if (ret == 1) timing.timing.lcd_ht = value; ret &= disp_sys_script_get_item(primary_key, "lcd_hspw", &value, 1); if (ret == 1) timing.timing.lcd_hspw = value; ret &= disp_sys_script_get_item(primary_key, "lcd_vbp", &value, 1); if (ret == 1) timing.timing.lcd_vbp = value; ret &= disp_sys_script_get_item(primary_key, "lcd_vt", &value, 1); if (ret == 1) timing.timing.lcd_vt = value; ret &= disp_sys_script_get_item(primary_key, "lcd_vspw", &value, 1); if (ret == 1) timing.timing.lcd_vspw = value; if (ret == 1) { timing.timing.mode = DISP_TV_MODE_DEFAULT; ret = disp_lcd_add_video_timing(dispdev, &timing.timing); spin_lock_irqsave(&lcd_data_lock, flags); lcdp->mode_using = DISP_TV_MODE_DEFAULT; spin_unlock_irqrestore(&lcd_data_lock, flags); ret = 0; } else { ret = -1; DE_WRN("fail to get lcd defalut timing!\n"); goto exit; } } else { DE_WRN("register_bridge failed, bridge ops NULL!\n"); ret = -1; } exit: return ret; } static int disp_lcd_unregister_bridge(struct disp_device *dispdev) { int ret = 0; unsigned long flags; struct lcd_timing_private *timing; struct lcd_timing_private *timing_tmp; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(dispdev); if (!dispdev || !lcdp) { DE_WRN("NULL hdl!\n"); ret = -1; goto exit; } spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->is_bridge == true) { plist_for_each_entry_safe(timing, timing_tmp, &lcd_private[dispdev->disp].timing_head, list) { plist_del(&timing->list, &lcdp->timing_head); kfree(timing); } lcdp->mode_using = DISP_TV_MODE_DEFAULT; lcdp->is_bridge = false; } spin_unlock_irqrestore(&lcd_data_lock, flags); exit: return ret; } static s32 lcd_get_bridge_timing(struct disp_lcd_private_data *lcdp, struct disp_panel_para *info, enum disp_tv_mode mode) { struct lcd_timing_private *timing; bool find = false; plist_for_each_entry(timing, &lcdp->timing_head, list) { if (timing->timing.mode == mode) { find = true; break; } } if (find) { info->lcd_x = timing->timing.lcd_x; info->lcd_y = timing->timing.lcd_y; info->lcd_dclk_freq = timing->timing.lcd_dclk_freq; info->lcd_hbp = timing->timing.lcd_hbp; info->lcd_ht = timing->timing.lcd_ht; info->lcd_vbp = timing->timing.lcd_vbp; info->lcd_vt = timing->timing.lcd_vt; info->lcd_vspw = timing->timing.lcd_vspw; info->lcd_hspw = timing->timing.lcd_hspw; } else { DE_WRN("video mode not found!\n"); return -1; } return 0; } static s32 disp_lcd_set_static_config(struct disp_device *lcd, struct disp_device_config *config) { struct disp_video_timings *timmings; struct disp_panel_para *panel_info; unsigned long flags; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("ERROR hdl!\n"); return DIS_FAIL; } mutex_lock(&lcdp->enable_lock); spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->is_bridge) { if (lcd_get_bridge_timing(lcdp, &lcdp->panel_info, config->mode)) { spin_unlock_irqrestore(&lcd_data_lock, flags); mutex_unlock(&lcdp->enable_lock); return DIS_FAIL; } lcdp->mode_using = config->mode; if (lcdp->panel_info.lcd_if == LCD_IF_DSI && lcdp->panel_info.lcd_tcon_mode == DISP_TCON_DUAL_DSI && lcdp->panel_info.lcd_dsi_port_num == DISP_LCD_DSI_SINGLE_PORT) { lcdp->panel_info.lcd_ht *= 2; lcdp->panel_info.lcd_hspw *= 2; lcdp->panel_info.lcd_x *= 2; lcdp->panel_info.lcd_hbp *= 2; lcdp->panel_info.lcd_dclk_freq *= 2; } 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; spin_unlock_irqrestore(&lcd_data_lock, flags); if (lcdp->bridge->set_video_mode(config->mode)) { DE_WRN("lcd%d bridge set_video_mode failed!\n", lcd->disp); } } else { spin_unlock_irqrestore(&lcd_data_lock, flags); } mutex_unlock(&lcdp->enable_lock); return 0; } static s32 disp_lcd_get_static_config(struct disp_device *lcd, struct disp_device_config *config) { int ret = 0; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); unsigned long flags; if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); ret = -1; goto exit; } config->type = lcd->type; spin_lock_irqsave(&lcd_data_lock, flags); if (lcdp->is_bridge == true) config->mode = lcdp->mode_using; config->format = DISP_CSC_TYPE_RGB; spin_unlock_irqrestore(&lcd_data_lock, flags); exit: return ret; } static s32 disp_lcd_exit(struct disp_device *lcd) { struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(lcd); if ((lcd == NULL) || (lcdp == NULL)) { DE_WRN("NULL hdl!\n"); return DIS_FAIL; } lcd_clk_exit(lcd); return 0; } #if defined(CONFIG_DISP2_LCD_ESD_DETECT) static s32 disp_lcd_get_esd_info(struct disp_device *dispdev, struct disp_lcd_esd_info *p_esd_info) { s32 ret = -1; struct disp_lcd_private_data *lcdp = NULL; if (!dispdev || !p_esd_info) goto OUT; lcdp = disp_lcd_get_priv(dispdev); if (!lcdp) goto OUT; memcpy(p_esd_info, &lcdp->esd_inf, sizeof(struct disp_lcd_esd_info)); ret = 0; OUT: return ret; } #endif static u32 disp_lcd_usec_before_vblank(struct disp_device *dispdev) { int cur_line; int start_delay; u32 usec = 0; struct disp_lcd_private_data *lcdp = disp_lcd_get_priv(dispdev); if (!dispdev || !lcdp) { DE_WRN("NULL hdl!\n"); goto exit; } start_delay = disp_al_lcd_get_start_delay(dispdev->hwdev_index, &lcdp->panel_info); cur_line = disp_al_lcd_get_cur_line(dispdev->hwdev_index, &lcdp->panel_info); if (cur_line > (start_delay - lcdp->judge_line)) { usec = (lcdp->panel_info.lcd_vt - cur_line + 1) * lcdp->usec_per_line; } exit: return usec; } s32 disp_init_lcd(struct disp_bsp_init_para *para) { u32 num_devices; u32 disp = 0; struct disp_device *lcd; struct disp_lcd_private_data *lcdp; u32 hwdev_index = 0; u32 num_devices_support_lcd = 0; #if defined(SUPPORT_DSI) u32 i = 0; #endif char primary_key[20]; int ret = 0, value = 1; #if defined(CONFIG_ARCH_SUN8IW17P1) s32 use_dsi_flag = 0; #endif DE_INF("disp_init_lcd\n"); spin_lock_init(&lcd_data_lock); num_devices = bsp_disp_feat_get_num_devices(); for (hwdev_index = 0; hwdev_index < num_devices; hwdev_index++) { if (bsp_disp_feat_is_supported_output_types (hwdev_index, DISP_OUTPUT_TYPE_LCD)) num_devices_support_lcd++; } lcds = kmalloc_array(num_devices_support_lcd, sizeof(struct disp_device), GFP_KERNEL | __GFP_ZERO); if (lcds == NULL) { DE_WRN("malloc memory(%d bytes) fail!\n", (unsigned int)sizeof(struct disp_device) * num_devices_support_lcd); goto malloc_err; } lcd_private = (struct disp_lcd_private_data *) kmalloc(sizeof(struct disp_lcd_private_data) * num_devices_support_lcd, GFP_KERNEL | __GFP_ZERO); if (lcd_private == NULL) { DE_WRN("malloc memory(%d bytes) fail!\n", (unsigned int)sizeof(struct disp_lcd_private_data) * num_devices_support_lcd); goto malloc_err; } disp = 0; for (hwdev_index = 0; hwdev_index < num_devices; hwdev_index++) { if (!bsp_disp_feat_is_supported_output_types (hwdev_index, DISP_OUTPUT_TYPE_LCD)) { continue; } sprintf(primary_key, "lcd%d", disp); ret = disp_sys_script_get_item(primary_key, "lcd_used", &value, 1); if (ret != 1 || value != 1) continue; lcd = &lcds[disp]; lcdp = &lcd_private[disp]; lcd->priv_data = (void *)lcdp; sprintf(lcd->name, "lcd%d", disp); lcd->disp = disp; #if defined(CONFIG_ARCH_SUN8IW17P1) value = 0; ret = disp_sys_script_get_item(primary_key, "lcd_if", &value, 1); if (value == 4) { lcd->hwdev_index = 1; use_dsi_flag = 1; } else lcd->hwdev_index = (use_dsi_flag == 1) ? 0 : hwdev_index; #else lcd->hwdev_index = hwdev_index; #endif lcd->type = DISP_OUTPUT_TYPE_LCD; plist_head_init(&lcdp->timing_head); lcdp->bridge = NULL; lcdp->mode_using = DISP_TV_MODE_NUM; lcdp->irq_no = para->irq_no[DISP_MOD_LCD0 + lcd->hwdev_index]; lcdp->clk = para->mclk[DISP_MOD_LCD0 + lcd->hwdev_index]; #if DEVICE_LVDS_NUM == 1 lcdp->lvds_clk = para->mclk[DISP_MOD_LVDS]; #else lcdp->lvds_clk = para->mclk[DISP_MOD_LVDS + lcd->hwdev_index]; #endif #if defined(SUPPORT_DSI) lcdp->irq_no_dsi = para->irq_no[DISP_MOD_DSI0 + disp]; for (i = 0; i < CLK_DSI_NUM; ++i) lcdp->dsi_clk[i] = para->mclk[DISP_MOD_DSI0 + i]; DE_INF("total number of clk in dsi:%d\n", CLK_DSI_NUM); #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->check_if_enabled = disp_lcd_check_if_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_static_config = disp_lcd_set_static_config; lcd->get_static_config = disp_lcd_get_static_config; lcd->check_config_dirty = disp_lcd_check_config_dirty; 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->is_in_safe_period = disp_lcd_is_in_safe_period; lcd->usec_before_vblank = disp_lcd_usec_before_vblank; lcd->set_gamma_tbl = disp_lcd_set_gamma_tbl; lcd->enable_gamma = disp_lcd_enable_gamma; lcd->disable_gamma = disp_lcd_disable_gamma; lcd->get_fps = disp_lcd_get_fps; lcd->set_color_temperature = disp_lcd_set_color_temperature; lcd->get_color_temperature = disp_lcd_get_color_temperature; lcd->show_builtin_patten = disp_device_show_builtin_patten; #if defined(CONFIG_DISP2_LCD_ESD_DETECT) lcd->get_esd_info = disp_lcd_get_esd_info; #endif lcd->init = disp_lcd_init; lcd->exit = disp_lcd_exit; lcd->add_video_timing = disp_lcd_add_video_timing; lcd->remove_video_timing = disp_lcd_remove_video_timing; lcd->get_video_timing = disp_lcd_get_video_timing; lcd->register_lcd_bridge = disp_lcd_register_bridge; lcd->unregister_lcd_bridge = disp_lcd_unregister_bridge; #if defined(SUPPORT_EINK) && defined(CONFIG_EINK_PANEL_USED) INIT_WORK(&lcd->close_eink_panel_work, disp_close_eink_panel_task); #endif lcd->init(lcd); disp_device_register(lcd); #if defined(CONFIG_DISP2_LCD_ESD_DETECT) INIT_WORK(&lcdp->reflush_work, disp_lcd_reflush_work); atomic_set(&lcdp->lcd_resetting, 0); #endif disp++; } return 0; malloc_err: kfree(lcds); kfree(lcd_private); lcds = NULL; lcd_private = NULL; return -1; } s32 disp_exit_lcd(void) { u32 num_devices; u32 disp = 0; struct disp_device *lcd; u32 hwdev_index = 0; struct lcd_timing_private *timing; struct lcd_timing_private *timing_tmp; if (!lcds) return 0; num_devices = bsp_disp_feat_get_num_devices(); disp = 0; for (hwdev_index = 0; hwdev_index < num_devices; hwdev_index++) { if (!bsp_disp_feat_is_supported_output_types (hwdev_index, DISP_OUTPUT_TYPE_LCD)) { continue; } plist_for_each_entry_safe(timing, timing_tmp, &lcd_private[disp].timing_head, list) { plist_del(&timing->list, &lcd_private[disp].timing_head); kfree(timing); } lcd = &lcds[disp]; disp_device_unregister(lcd); lcd->exit(lcd); disp++; } kfree(lcds); kfree(lcd_private); lcds = NULL; lcd_private = NULL; return 0; }