android_kernel_samsung_msm8976/drivers/video/msm/mdss/samsung/ss_dsi_panel_common.c

6819 lines
204 KiB
C

/*
* =================================================================
*
*
* Description: samsung display common file
*
* Author: jb09.kim
* Company: Samsung Electronics
*
* ================================================================
*/
/*
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 2015, Samsung Electronics. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
#include "ss_dsi_panel_common.h"
#include "../mdss_debug.h"
static void mdss_samsung_event_osc_te_fitting(struct mdss_panel_data *pdata, int event, void *arg);
static irqreturn_t samsung_te_check_handler(int irq, void *handle);
static void samsung_te_check_done_work(struct work_struct *work);
static void mdss_samsung_event_esd_recovery_init(struct mdss_panel_data *pdata, int event, void *arg);
static void mdss_samsung_panel_lpm_ctrl(struct mdss_panel_data *pdata, int event);
static void mdss_samsung_panel_lpm_hz_ctrl(struct mdss_panel_data *pdata, int aod_ctrl);
static void mdss_samsung_panel_lpm_mode_store(struct mdss_panel_data *pdata, u8 mode);
struct samsung_display_driver_data vdd_data;
struct dsi_cmd_desc default_cmd = {{DTYPE_DCS_LWRITE, 1, 0, 0, 0, 0}, NULL};
#if defined(CONFIG_LCD_CLASS_DEVICE)
/* SYSFS RELATED VALUE */
#define MAX_FILE_NAME 128
#define TUNING_FILE_PATH "/sdcard/"
static char tuning_file[MAX_FILE_NAME];
#endif
u8 csc_update = 1;
u8 csc_change = 0;
void __iomem *virt_mmss_gp_base;
struct samsung_display_driver_data *check_valid_ctrl(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct samsung_display_driver_data *vdd = NULL;
if (IS_ERR_OR_NULL(ctrl)) {
LCD_ERR("Invalid data ctrl : 0x%zx\n", (size_t)ctrl);
return NULL;
}
vdd = (struct samsung_display_driver_data *)ctrl->panel_data.panel_private;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid vdd_data : 0x%zx\n", (size_t)vdd);
return NULL;
}
return vdd;
}
char mdss_panel_id0_get(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return -ERANGE;
}
return (vdd->manufacture_id_dsi[display_ndx_check(ctrl)] & 0xFF0000) >> 16;
}
char mdss_panel_id1_get(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return -ERANGE;
}
return (vdd->manufacture_id_dsi[display_ndx_check(ctrl)] & 0xFF00) >> 8;
}
char mdss_panel_id2_get(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return -ERANGE;
}
return vdd->manufacture_id_dsi[display_ndx_check(ctrl)] & 0xFF;
}
char mdss_panel_rev_get(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return -ERANGE;
}
return vdd->manufacture_id_dsi[display_ndx_check(ctrl)] & 0x0F;
}
int mdss_panel_attach_get(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return -ERANGE;
}
return (vdd->panel_attach_status & (0x01 << ctrl->ndx)) > 0 ? true : false;
}
int mdss_panel_attach_set(struct mdss_dsi_ctrl_pdata *ctrl, int status)
{
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return -ERANGE;
}
/* 0bit->DSI0 1bit->DSI1 */
/* check the lcd id for DISPLAY_1 and DISPLAY_2 */
if (likely(mdss_panel_attached(DISPLAY_1) || mdss_panel_attached(DISPLAY_2)) && status) {
if (ctrl->cmd_sync_wait_broadcast)
vdd->panel_attach_status |= (BIT(1) | BIT(0));
else {
/* One more time check dual dsi */
if (!IS_ERR_OR_NULL(vdd->ctrl_dsi[DSI_CTRL_0]) &&
!IS_ERR_OR_NULL(vdd->ctrl_dsi[DSI_CTRL_1]))
vdd->panel_attach_status |= (BIT(1) | BIT(0));
else
vdd->panel_attach_status |= BIT(0) << ctrl->ndx;
}
} else {
if (ctrl->cmd_sync_wait_broadcast)
vdd->panel_attach_status &= ~(BIT(1) | BIT(0));
else {
/* One more time check dual dsi */
if (!IS_ERR_OR_NULL(vdd->ctrl_dsi[DSI_CTRL_0]) &&
!IS_ERR_OR_NULL(vdd->ctrl_dsi[DSI_CTRL_1]))
vdd->panel_attach_status &= ~(BIT(1) | BIT(0));
else
vdd->panel_attach_status &= ~(BIT(0) << ctrl->ndx);
}
}
LCD_INFO("panel_attach_status : %d\n", vdd->panel_attach_status);
return vdd->panel_attach_status;
}
/*
* Check the lcd id for DISPLAY_1 and DISPLAY_2 using the ndx
*/
int mdss_panel_attached(int ndx)
{
int lcd_id = 0;
/*
* ndx 0 means DISPLAY_1 and ndx 1 means DISPLAY_2
*/
if (ndx == 0)
lcd_id = get_lcd_attached("GET");
else if (ndx == 1)
lcd_id = get_lcd_attached_secondary("GET");
/*
* The 0xFFFFFF is the id for PBA booting
* if the id is same with 0xFFFFFF, this function
* will return 0
*/
return !(lcd_id == PBA_ID);
}
static int mdss_samsung_parse_panel_id(char *panel_id)
{
char *pt;
int lcd_id = 0;
if (!IS_ERR_OR_NULL(panel_id))
pt = panel_id;
else
return lcd_id;
for (pt = panel_id; *pt != 0; pt++) {
lcd_id <<= 4;
switch (*pt) {
case '0' ... '9':
lcd_id += *pt - '0';
break;
case 'a' ... 'f':
lcd_id += 10 + *pt - 'a';
break;
case 'A' ... 'F':
lcd_id += 10 + *pt - 'A';
break;
}
}
LCD_ERR("LCD_ID = 0x%X\n", lcd_id);
return lcd_id;
}
int get_lcd_attached(char *mode)
{
static int lcd_id = 0;
LCD_DEBUG("%s", mode);
if (mode == NULL)
return true;
if (!strncmp(mode, "GET", 3)) {
goto end;
} else {
lcd_id = 0;
lcd_id = mdss_samsung_parse_panel_id(mode);
}
end:
return lcd_id;
}
EXPORT_SYMBOL(get_lcd_attached);
__setup("lcd_id=0x", get_lcd_attached);
int get_lcd_attached_secondary(char *mode)
{
static int lcd_id = 0;
LCD_DEBUG("%s", mode);
if (mode == NULL)
return true;
if (!strncmp(mode, "GET", 3))
goto end;
else
lcd_id = mdss_samsung_parse_panel_id(mode);
end:
return lcd_id;
}
EXPORT_SYMBOL(get_lcd_attached_secondary);
__setup("lcd_id2=0x", get_lcd_attached_secondary);
int get_hall_ic_status(char *mode)
{
if (mode == NULL)
return true;
if (*mode - '0')
vdd_data.display_status_dsi[DISPLAY_1].hall_ic_status = HALL_IC_CLOSE;
else
vdd_data.display_status_dsi[DISPLAY_1].hall_ic_status = HALL_IC_OPEN;
LCD_ERR("hall_ic : %s\n", vdd_data.display_status_dsi[DISPLAY_1].hall_ic_status ? "CLOSE" : "OPEN");
return true;
}
EXPORT_SYMBOL(get_hall_ic_status);
__setup("hall_ic=0x", get_hall_ic_status);
static void mdss_samsung_event_frame_update(struct mdss_panel_data *pdata, int event, void *arg)
{
int ndx;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)pdata->panel_private;
struct panel_func *panel_func = NULL;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
panel_func = &vdd->panel_func;
ndx = display_ndx_check(ctrl);
if (ctrl->cmd_sync_wait_broadcast) {
if (ctrl->cmd_sync_wait_trigger) {
if (vdd->display_status_dsi[ndx].wait_disp_on) {
ATRACE_BEGIN(__func__);
mdss_samsung_send_cmd(ctrl, PANEL_DISPLAY_ON);
vdd->display_status_dsi[ndx].wait_disp_on = 0;
if (vdd->panel_func.samsung_backlight_late_on)
vdd->panel_func.samsung_backlight_late_on(ctrl);
if (vdd->dtsi_data[0].hmt_enabled &&
vdd->vdd_blank_mode[0] != FB_BLANK_NORMAL) {
if (vdd->hmt_stat.hmt_on) {
LCD_INFO("hmt reset ..\n");
vdd->hmt_stat.hmt_enable(ctrl,vdd);
vdd->hmt_stat.hmt_reverse_update(ctrl,1);
vdd->hmt_stat.hmt_bright_update(ctrl);
}
}
LCD_INFO("DISPLAY_ON\n");
ATRACE_END(__func__);
}
} else
vdd->display_status_dsi[ndx].wait_disp_on = 0;
} else {
/* Check TE duration when the panel turned on */
/*
if (vdd->display_status_dsi[ctrl->ndx].wait_disp_on) {
vdd->te_fitting_info.status &= ~TE_FITTING_DONE;
vdd->te_fitting_info.te_duration = 0;
}
*/
if (vdd->dtsi_data[ctrl->ndx].samsung_osc_te_fitting &&
!(vdd->te_fitting_info.status & TE_FITTING_DONE)) {
if (panel_func->mdss_samsung_event_osc_te_fitting)
panel_func->mdss_samsung_event_osc_te_fitting(pdata, event, arg);
}
if (vdd->display_status_dsi[ndx].wait_disp_on) {
MDSS_XLOG(ndx);
ATRACE_BEGIN(__func__);
if (!IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].display_on_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(ctrl, PANEL_DISPLAY_ON);
vdd->display_status_dsi[ndx].wait_disp_on = 0;
if (vdd->panel_func.samsung_backlight_late_on)
vdd->panel_func.samsung_backlight_late_on(ctrl);
if (vdd->dtsi_data[0].hmt_enabled &&
vdd->vdd_blank_mode[0] != FB_BLANK_NORMAL) {
if (vdd->hmt_stat.hmt_on) {
LCD_INFO("hmt reset ..\n");
vdd->hmt_stat.hmt_enable(ctrl,vdd);
vdd->hmt_stat.hmt_reverse_update(ctrl,1);
vdd->hmt_stat.hmt_bright_update(ctrl);
}
}
MDSS_XLOG(ndx);
LCD_INFO("DISPLAY_ON\n");
ATRACE_END(__func__);
}
}
}
static void mdss_samsung_send_esd_recovery_cmd( struct mdss_dsi_ctrl_pdata *ctrl)
{
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
static bool toggle;
/* do not send esd recovery cmd when mipi data pck is sending */
if (mutex_is_locked(&vdd->vdd_lock))
return;
if (toggle)
mdss_samsung_send_cmd(ctrl, PANEL_ESD_RECOVERY_1);
else
mdss_samsung_send_cmd(ctrl, PANEL_ESD_RECOVERY_2);
toggle = !toggle;
}
static void mdss_samsung_event_fb_event_callback(struct mdss_panel_data *pdata, int event, void *arg)
{
struct samsung_display_driver_data *vdd = NULL;
struct panel_func *panel_func = NULL;
if (IS_ERR_OR_NULL(pdata)) {
LCD_ERR("Invalid data pdata : 0x%zx\n",
(size_t)pdata);
return;
}
vdd = (struct samsung_display_driver_data *)pdata->panel_private;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd);
return;
}
panel_func = &vdd->panel_func;
if (IS_ERR_OR_NULL(panel_func)) {
LCD_ERR("Invalid data panel_func : 0x%zx\n",
(size_t)panel_func);
return;
}
if (panel_func->mdss_samsung_event_esd_recovery_init)
panel_func->mdss_samsung_event_esd_recovery_init(pdata, event, arg);
}
static int mdss_samsung_dsi_panel_event_handler(
struct mdss_panel_data *pdata, int event, void *arg)
{
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)pdata->panel_private;
struct panel_func *panel_func = NULL;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n",
(size_t)ctrl, (size_t)vdd);
return -EINVAL;
}
if (event < MDSS_SAMSUNG_EVENT_START) {
LCD_DEBUG("Unknown event(%d)\n", event);
goto end;
}
/*
* The MDSS_SAMSUNG_EVENT_MAX value added
* for FB_BLANK_ related events
*/
if (event >= MDSS_SAMSUNG_EVENT_MAX)
event -= MDSS_SAMSUNG_EVENT_MAX;
LCD_DEBUG("%d\n", event);
panel_func = &vdd->panel_func;
if (IS_ERR_OR_NULL(panel_func)) {
LCD_ERR("Invalid data panel_func : 0x%zx\n", (size_t)panel_func);
return -EINVAL;
}
switch (event) {
case MDSS_SAMSUNG_EVENT_FRAME_UPDATE:
if (!IS_ERR_OR_NULL(panel_func->mdss_samsung_event_frame_update))
panel_func->mdss_samsung_event_frame_update(pdata, event, arg);
break;
case MDSS_SAMSUNG_EVENT_FB_EVENT_CALLBACK:
if (!IS_ERR_OR_NULL(panel_func->mdss_samsung_event_fb_event_callback))
panel_func->mdss_samsung_event_fb_event_callback(pdata, event, arg);
break;
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_NORMAL:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_POWERDOWN:
if (likely(!vdd->is_factory_mode))
mdss_samsung_panel_lpm_ctrl(pdata, event);
break;
case MDSS_SAMSUNG_EVENT_PANEL_ESD_RECOVERY:
if (vdd->send_esd_recovery)
mdss_samsung_send_esd_recovery_cmd(ctrl);
break;
default:
LCD_DEBUG("unhandled event=%d\n", event);
break;
}
end:
return 0;
}
/*
* Debugfs related functions
*/
static ssize_t mdss_samsung_panel_lpm_ctrl_debug(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct samsung_display_driver_data *vdd = file->private_data;
struct mdss_panel_data *pdata = NULL;
struct mdss_dsi_ctrl_pdata *ctrl;
int mode, ret = count;
char buf[10];
if (IS_ERR_OR_NULL(vdd)) {
ret = -EFAULT;
goto end;
}
if (copy_from_user(buf, user_buf, count)) {
ret = -EFAULT;
goto end;
}
if (sscanf(buf, "%d", &mode) != 1) {
ret = -EFAULT;
goto end;
}
ctrl = vdd->ctrl_dsi[DISPLAY_1];
pdata = &ctrl->panel_data;
if (!vdd->dtsi_data[DISPLAY_1].panel_lpm_enable) {
LCD_INFO("[Panel LPM DEBUG] panel_lpm is not supported \n");
goto end;
}
LCD_INFO("[Panel LPM DEBUG] Mode : %d\n", mode);
mdss_samsung_panel_lpm_mode_store(pdata, (u8)(mode + MAX_LPM_MODE));
if ((vdd->panel_lpm.hz == PANEL_LPM_2HZ) ||
(vdd->panel_lpm.hz == PANEL_LPM_1HZ))
mdss_samsung_panel_lpm_hz_ctrl(pdata, PANEL_LPM_AOD_OFF);
if (vdd->panel_lpm.mode != MODE_OFF) {
mdss_samsung_send_cmd(ctrl, PANEL_LPM_ON);
LCD_INFO("[Panel LPM] Send panel LPM cmds\n");
mdss_samsung_panel_lpm_hz_ctrl(pdata, PANEL_LPM_HZ_NONE);
vdd->panel_lpm.param_changed = false;
vdd->display_status_dsi[ctrl->ndx].wait_disp_on = true;
LCD_DEBUG("[Panel LPM] Set wait_disp_on to true\n");
} else if (vdd->panel_lpm.mode == MODE_OFF) {
/* Turn Off ALPM Mode */
mdss_samsung_send_cmd(ctrl, PANEL_LPM_OFF);
LCD_INFO("[Panel LPM] Send panel LPM off cmds\n");
vdd->panel_lpm.param_changed = true;
LCD_DEBUG("[Panel LPM] Restore brightness level\n");
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
pdata->set_backlight(pdata, vdd->mfd_dsi[DISPLAY_1]->bl_level);
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
vdd->display_status_dsi[ctrl->ndx].wait_disp_on = true;
}
if ((vdd->panel_lpm.hz == PANEL_LPM_2HZ) ||
(vdd->panel_lpm.hz == PANEL_LPM_1HZ))
mdss_samsung_panel_lpm_hz_ctrl(pdata, PANEL_LPM_AOD_ON);
end:
return ret;
}
static struct file_operations panel_lpm_ops = {
.open = simple_open,
.write = mdss_samsung_panel_lpm_ctrl_debug,
};
static ssize_t mdss_samsung_dump_regs_debug(struct file *file,
char __user *buff, size_t count, loff_t *ppos)
{
struct samsung_display_driver_data *vdd = file->private_data;
if (IS_ERR_OR_NULL(vdd))
return -EFAULT;
mdss_samsung_read_rddpm();
mdss_samsung_dump_regs();
mdss_samsung_dsi_dump_regs(0);
mdss_samsung_dsi_dump_regs(1);
mdss_samsung_dsi_te_check();
mdss_mdp_underrun_dump_info();
return 0;
}
static struct file_operations panel_dump_ops = {
.open = simple_open,
.read = mdss_samsung_dump_regs_debug,
};
static void mdss_sasmung_panel_debug_create(struct samsung_display_driver_data *vdd)
{
struct samsung_display_debug_data *debug_data;
debug_data = vdd->debug_data;
/* Create file on debugfs of display_driver */
/* Create file on debugfs on dump */
debugfs_create_file("reg_dump", 0600, debug_data->dump,
vdd, &panel_dump_ops);
debugfs_create_bool("print_cmds", 0600, debug_data->dump,
(u32 *)&debug_data->print_cmds);
debugfs_create_bool("panic_on_pptimeout", 0600, debug_data->dump,
(u32 *)&debug_data->panic_on_pptimeout);
/* Create file on debugfs on display_status */
debugfs_create_u32("panel_attach_status", 0600, debug_data->display_status,
(u32 *)&vdd->panel_attach_status);
debugfs_create_file("panel_lpm", 0600, debug_data->display_status,
vdd, &panel_lpm_ops);
if (!IS_ERR_OR_NULL(debug_data->is_factory_mode))
debugfs_create_bool("is_factory_mode", 0600, debug_data->root,
(u32 *)debug_data->is_factory_mode);
/* Create file on debugfs on hw_info */
/* TBD */
}
static int mdss_sasmung_panel_debug_init(struct samsung_display_driver_data *vdd)
{
struct samsung_display_debug_data *debug_data;
static bool debugfs_init;
int ret = 0;
debug_data = kzalloc(sizeof(struct samsung_display_debug_data),
GFP_KERNEL);
if (IS_ERR_OR_NULL(vdd)) {
ret = -ENODEV;
goto end;
}
/*
* The debugfs must be init one time
* in case of dual dsi, this function will be called twice
*/
if (debugfs_init)
goto end;
debugfs_init = true;
vdd->debug_data = debug_data;
if (IS_ERR_OR_NULL(debug_data)) {
LCD_ERR("no memory to create display debug data\n");
ret = -ENOMEM;
goto end;
}
/* INIT debug data */
debug_data->is_factory_mode = &vdd->is_factory_mode;
/*
* panic_on_pptimeout default value is false
* if you want to enable panic for specific project
* please change the value on your panel file.
* if you want to enable panic for all project
* please change the value here.
*/
debug_data->panic_on_pptimeout = false;
/* Root directory for display driver */
debug_data->root = debugfs_create_dir("display_driver", NULL);
if (IS_ERR_OR_NULL(debug_data->root)) {
LCD_ERR("debugfs_create_dir failed, error %ld(line:%d)\n",
PTR_ERR(debug_data->root), __LINE__);
ret = -ENODEV;
goto end;
}
/* Directory for dump */
debug_data->dump = debugfs_create_dir("dump", debug_data->root);
if (IS_ERR_OR_NULL(debug_data->dump)) {
LCD_ERR("debugfs_create_dir failed, error %ld(line:%d)\n",
PTR_ERR(debug_data->dump), __LINE__);
ret = -ENODEV;
goto end;
}
/* Directory for hw_info */
debug_data->hw_info = debugfs_create_dir("hw_info", debug_data->root);
if (IS_ERR_OR_NULL(debug_data->root)) {
LCD_ERR("debugfs_create_dir failed, error %ld(line:%d)\n",
PTR_ERR(debug_data->root), __LINE__);
ret = -ENODEV;
goto end;
}
/* Directory for hw_info */
debug_data->display_status = debugfs_create_dir("display_status", debug_data->root);
if (IS_ERR_OR_NULL(debug_data->display_status)) {
LCD_ERR("debugfs_create_dir failed, error %ld(line:%d)\n",
PTR_ERR(debug_data->root), __LINE__);
ret = -ENODEV;
goto end;
}
mdss_sasmung_panel_debug_create(vdd);
if (ret)
LCD_ERR("Fail to create files for debugfs\n");
end:
if (ret && !IS_ERR_OR_NULL(debug_data->root))
debugfs_remove_recursive(debug_data->root);
return ret;
}
void mdss_samsung_panel_init(struct device_node *np,
struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
/* At this time ctrl_pdata->ndx is not set */
struct mdss_panel_info *pinfo = NULL;
int ndx = ctrl_pdata->panel_data.panel_info.pdest;
int loop, loop2;
ctrl_pdata->panel_data.panel_private = &vdd_data;
if (mdss_sasmung_panel_debug_init(&vdd_data))
LCD_ERR("Fail to create debugfs\n");
mutex_init(&vdd_data.vdd_lock);
/* To guarantee BLANK & UNBLANK mode change operation*/
mutex_init(&vdd_data.vdd_blank_unblank_lock);
/* To guarantee ALPM ON or OFF mode change operation*/
mutex_init(&vdd_data.vdd_panel_lpm_lock);
vdd_data.ctrl_dsi[ndx] = ctrl_pdata;
pinfo = &ctrl_pdata->panel_data.panel_info;
/* Set default link_state of brightness command */
for (loop = 0; loop < SUPPORT_PANEL_COUNT; loop++)
vdd_data.brightness[loop].brightness_packet_tx_cmds_dsi.link_state = DSI_HS_MODE;
if (pinfo && pinfo->mipi.mode == DSI_CMD_MODE) {
vdd_data.panel_func.mdss_samsung_event_osc_te_fitting =
mdss_samsung_event_osc_te_fitting;
}
vdd_data.panel_func.mdss_samsung_event_frame_update =
mdss_samsung_event_frame_update;
vdd_data.panel_func.mdss_samsung_event_fb_event_callback =
mdss_samsung_event_fb_event_callback;
vdd_data.panel_func.mdss_samsung_event_esd_recovery_init =
mdss_samsung_event_esd_recovery_init;
vdd_data.manufacture_id_dsi[0] = PBA_ID;
if (IS_ERR_OR_NULL(vdd_data.panel_func.samsung_panel_init))
LCD_ERR("no samsung_panel_init fucn");
else
vdd_data.panel_func.samsung_panel_init(&vdd_data);
vdd_data.ctrl_dsi[ndx]->event_handler = mdss_samsung_dsi_panel_event_handler;
if (vdd_data.support_mdnie_lite || vdd_data.support_cabc)
/* check the lcd id for DISPLAY_1 and DISPLAY_2 */
if (((ndx == 0) && mdss_panel_attached(ndx)) ||
((ndx == 1) && mdss_panel_attached(ndx)))
vdd_data.mdnie_tune_state_dsi[ndx] = init_dsi_tcon_mdnie_class(ndx, &vdd_data);
/*
Below for loop are same as initializing sturct brightenss_pasket_dsi.
vdd_data.brightness[ndx].brightness_packet_dsi[0] = {{DTYPE_DCS_LWRITE, 1, 0, 0, 0, 0}, NULL};
vdd_data.brightness[ndx].brightness_packet_dsi[1] = {{DTYPE_DCS_LWRITE, 1, 0, 0, 0, 0}, NULL};
...
vdd_data.brightness[ndx].brightness_packet_dsi[BRIGHTNESS_MAX_PACKET - 2] = {{DTYPE_DCS_LWRITE, 1, 0, 0, 0, 0}, NULL};
vdd_data.brightness[ndx].brightness_packet_dsi[BRIGHTNESS_MAX_PACKET - 1 ] = {{DTYPE_DCS_LWRITE, 1, 0, 0, 0, 0}, NULL};
*/
for (loop = 0; loop < SUPPORT_PANEL_COUNT; loop++)
for (loop2 = 0; loop2 < BRIGHTNESS_MAX_PACKET; loop2++)
vdd_data.brightness[loop].brightness_packet_dsi[loop2] = default_cmd;
for (loop = 0; loop < SUPPORT_PANEL_COUNT; loop++) {
vdd_data.brightness[loop].brightness_packet_tx_cmds_dsi.cmds = &vdd_data.brightness[loop].brightness_packet_dsi[0];
vdd_data.brightness[loop].brightness_packet_tx_cmds_dsi.cmd_cnt = 0;
}
spin_lock_init(&vdd_data.esd_recovery.irq_lock);
vdd_data.hmt_stat.hmt_enable = hmt_enable;
vdd_data.hmt_stat.hmt_reverse_update = hmt_reverse_update;
vdd_data.hmt_stat.hmt_bright_update = hmt_bright_update;
mdss_panel_attach_set(ctrl_pdata, true);
/* Set init brightness level */
vdd_data.bl_level = DEFAULT_BRIGHTNESS;
/* Init blank_mode */
for (loop = 0; loop < SUPPORT_PANEL_COUNT; loop++)
vdd_data.vdd_blank_mode[loop] = FB_BLANK_POWERDOWN;
/* Init Hall ic related things */
mutex_init(&vdd_data.vdd_hall_ic_lock); /* To guarantee HALL IC switching */
mutex_init(&vdd_data.vdd_hall_ic_blank_unblank_lock); /* To guarantee HALL IC operation(PMS BLANK & UNBLAMK) */
/* Init Other line panel support */
if (!IS_ERR_OR_NULL(vdd_data.panel_func.parsing_otherline_pdata) && mdss_panel_attached(DISPLAY_1) ) {
if (!IS_ERR_OR_NULL(vdd_data.panel_func.get_panel_fab_type)){
if (vdd_data.panel_func.get_panel_fab_type() == NEW_FB_PANLE_TYPE){
LCD_ERR("parsing_otherline_pdata (%d)\n", vdd_data.panel_func.get_panel_fab_type());
INIT_DELAYED_WORK(&vdd_data.other_line_panel_support_work, (work_func_t)read_panel_data_work_fn);
vdd_data.other_line_panel_support_workq = create_singlethread_workqueue("other_line_panel_support_wq");
if (vdd_data.other_line_panel_support_workq) {
vdd_data.other_line_panel_work_cnt = OTHERLINE_WORKQ_CNT;
queue_delayed_work(vdd_data.other_line_panel_support_workq,
&vdd_data.other_line_panel_support_work, msecs_to_jiffies(OTHERLINE_WORKQ_DEALY));
}
}
}
}
/* Init panel LPM info */
vdd_data.panel_lpm.param_changed = true;
#if defined(CONFIG_SEC_FACTORY)
vdd_data.is_factory_mode = true;
#endif
}
void mdss_samsung_dsi_panel_registered(struct mdss_panel_data *pdata)
{
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)pdata->panel_private;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return;
}
if ((struct msm_fb_data_type *)registered_fb[ctrl->ndx]) {
vdd->mfd_dsi[ctrl->ndx] = (struct msm_fb_data_type *)registered_fb[ctrl->ndx]->par;
} else {
LCD_ERR("no registered_fb[%d]\n", ctrl->ndx);
return;
}
mdss_samsung_create_sysfs(vdd);
LCD_INFO("DSI%d success", ctrl->ndx);
}
int mdss_samsung_parse_candella_lux_mapping_table(struct device_node *np,
struct candella_lux_map *table, char *keystring)
{
const __be32 *data;
int data_offset, len = 0 , i = 0;
data = of_get_property(np, keystring, &len);
if (!data) {
LCD_DEBUG("%d, Unable to read table %s ", __LINE__, keystring);
return -EINVAL;
} else
LCD_ERR("Success to read table %s\n", keystring);
if ((len % 4) != 0) {
LCD_ERR("%d, Incorrect table entries for %s",
__LINE__, keystring);
return -EINVAL;
}
table->lux_tab_size = len / (sizeof(int)*4);
table->lux_tab = kzalloc((sizeof(int) * table->lux_tab_size), GFP_KERNEL);
if (!table->lux_tab)
return -ENOMEM;
table->cmd_idx = kzalloc((sizeof(int) * table->lux_tab_size), GFP_KERNEL);
if (!table->cmd_idx)
goto error;
data_offset = 0;
for (i = 0 ; i < table->lux_tab_size; i++) {
table->cmd_idx[i] = be32_to_cpup(&data[data_offset++]); /* 1rst field => <idx> */
table->from[i] = be32_to_cpup(&data[data_offset++]); /* 2nd field => <from> */
table->end[i] = be32_to_cpup(&data[data_offset++]); /* 3rd field => <till> */
table->lux_tab[i] = be32_to_cpup(&data[data_offset++]); /* 4th field => <candella> */
/* Fill the backlight level to lux mapping array */
do {
table->bkl[table->from[i]++] = i;
} while (table->from[i] <= table->end[i]);
}
return 0;
error:
kfree(table->lux_tab);
return -ENOMEM;
}
int mdss_samsung_parse_hbm_candella_lux_mapping_table(struct device_node *np,
struct hbm_candella_lux_map *table, char *keystring)
{
const __be32 *data;
int data_offset, len = 0 , i = 0;
data = of_get_property(np, keystring, &len);
if (!data) {
LCD_ERR("%d, Unable to read table %s ", __LINE__, keystring);
return -EINVAL;
} else
LCD_ERR("Success to read table %s\n", keystring);
if ((len % 4) != 0) {
LCD_ERR("%d, Incorrect table entries for %s",
__LINE__, keystring);
return -EINVAL;
}
table->lux_tab_size = len / (sizeof(int)*5);
table->lux_tab = kzalloc((sizeof(int) * table->lux_tab_size), GFP_KERNEL);
if (!table->lux_tab)
return -ENOMEM;
table->cmd_idx = kzalloc((sizeof(int) * table->lux_tab_size), GFP_KERNEL);
if (!table->cmd_idx)
goto error;
table->from = kzalloc((sizeof(int) * table->lux_tab_size), GFP_KERNEL);
if (!table->from)
goto error;
table->end = kzalloc((sizeof(int) * table->lux_tab_size), GFP_KERNEL);
if (!table->end)
goto error;
table->auto_level = kzalloc((sizeof(int) * table->lux_tab_size), GFP_KERNEL);
if (!table->auto_level)
goto error;
data_offset = 0;
for (i = 0 ; i < table->lux_tab_size; i++) {
table->cmd_idx[i] = be32_to_cpup(&data[data_offset++]); /* 1st field => <idx> */
table->from[i] = be32_to_cpup(&data[data_offset++]); /* 2nd field => <from> */
table->end[i] = be32_to_cpup(&data[data_offset++]); /* 3rd field => <till> */
table->lux_tab[i] = be32_to_cpup(&data[data_offset++]); /* 4th field => <candella> */
table->auto_level[i] = be32_to_cpup(&data[data_offset++]); /* 5th field => <auto brightness level> */
}
table->hbm_min_lv = table->from[0];
pr_err("tab_size (%d) hbm_min_lv (%d)\n", table->lux_tab_size, table->hbm_min_lv);
return 0;
error:
kfree(table->lux_tab);
kfree(table->cmd_idx);
kfree(table->from);
kfree(table->end);
kfree(table->auto_level);
return -ENOMEM;
}
int mdss_samsung_parse_panel_table(struct device_node *np,
struct cmd_map *table, char *keystring)
{
const __be32 *data;
int data_offset, len = 0 , i = 0;
data = of_get_property(np, keystring, &len);
if (!data) {
LCD_DEBUG("%d, Unable to read table %s\n", __LINE__, keystring);
return -EINVAL;
} else
LCD_ERR("Success to read table %s\n", keystring);
if ((len % 2) != 0) {
LCD_ERR("%d, Incorrect table entries for %s",
__LINE__, keystring);
return -EINVAL;
}
table->size = len / (sizeof(int)*2);
table->bl_level = kzalloc((sizeof(int) * table->size), GFP_KERNEL);
if (!table->bl_level)
return -ENOMEM;
table->cmd_idx = kzalloc((sizeof(int) * table->size), GFP_KERNEL);
if (!table->cmd_idx)
goto error;
data_offset = 0;
for (i = 0 ; i < table->size; i++) {
table->bl_level[i] = be32_to_cpup(&data[data_offset++]);
table->cmd_idx[i] = be32_to_cpup(&data[data_offset++]);
}
return 0;
error:
kfree(table->cmd_idx);
return -ENOMEM;
}
static int samsung_nv_read(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *cmds, unsigned char *destBuffer)
{
int loop_limit = 0;
int read_pos = 0;
int read_count = 0;
int show_cnt;
int i, j;
char show_buffer[256] = {0,};
int show_buffer_pos = 0;
int read_size = 0;
int srcLength = 0;
int startoffset = 0;
struct samsung_display_driver_data *vdd = NULL;
int ndx = 0;
int packet_size = 10; /* mipi limitation */
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(cmds)) {
LCD_ERR("Invalid ctrl data\n");
return -EINVAL;
}
vdd = (struct samsung_display_driver_data *)ctrl->panel_data.panel_private;
ndx = display_ndx_check(ctrl);
srcLength = cmds->read_size[0];
startoffset = read_pos = cmds->read_startoffset[0];
show_buffer_pos += snprintf(show_buffer, sizeof(show_buffer), "read_reg : %X[%d] : ", cmds->cmds->payload[0], srcLength);
loop_limit = (srcLength + packet_size - 1) / packet_size;
show_cnt = 0;
for (j = 0; j < loop_limit; j++) {
vdd->dtsi_data[ndx].reg_read_pos_tx_cmds[vdd->panel_revision].cmds->payload[1] = read_pos;
read_size = ((srcLength - read_pos + startoffset) < packet_size) ?
(srcLength - read_pos + startoffset) : packet_size;
mdss_samsung_send_cmd(ctrl, PANEL_REG_READ_POS);
mutex_lock(&vdd->vdd_lock);
read_count = mdss_samsung_panel_cmd_read(ctrl, cmds, read_size);
mutex_unlock(&vdd->vdd_lock);
for (i = 0; i < read_count; i++, show_cnt++) {
show_buffer_pos += snprintf(show_buffer + show_buffer_pos, sizeof(show_buffer)-show_buffer_pos, "%02x ",
ctrl->rx_buf.data[i]);
if (destBuffer != NULL && show_cnt < srcLength) {
destBuffer[show_cnt] =
ctrl->rx_buf.data[i];
}
}
show_buffer_pos += snprintf(show_buffer + show_buffer_pos, sizeof(show_buffer)-show_buffer_pos, ".");
read_pos += read_count;
if (read_pos-startoffset >= srcLength)
break;
}
LCD_ERR("mdss DSI%d %s\n", ndx, show_buffer);
return read_pos-startoffset;
}
struct dsi_panel_cmds *mdss_samsung_cmds_select(struct mdss_dsi_ctrl_pdata *ctrl, enum mipi_samsung_cmd_list cmd,
u32 *flags)
{
int ndx;
struct dsi_panel_cmds *cmds = NULL;
struct mdss_panel_info *pinfo = NULL;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)ctrl->panel_data.panel_private;
u32 cmd_flags = CMD_REQ_COMMIT;
if (IS_ERR_OR_NULL(vdd))
return NULL;
ndx = display_ndx_check(ctrl);
pinfo = &(ctrl->panel_data.panel_info);
/*
* Skip commands that are not related to ALPM
*/
if (pinfo->blank_state == MDSS_PANEL_BLANK_LOW_POWER) {
switch (cmd) {
case PANEL_LPM_ON:
case PANEL_LPM_OFF:
case PANEL_LPM_HZ_NONE:
case PANEL_LPM_1HZ:
case PANEL_LPM_2HZ:
case PANEL_LPM_30HZ:
case PANEL_LPM_AOD_ON:
case PANEL_LPM_AOD_OFF:
case PANEL_DISPLAY_ON:
case PANEL_DISPLAY_OFF:
case PANEL_LEVE1_KEY_ENABLE:
case PANEL_LEVE1_KEY_DISABLE:
case PANEL_LEVE2_KEY_ENABLE:
case PANEL_LEVE2_KEY_DISABLE:
case PANEL_LEVE1_KEY:
case PANEL_LEVE2_KEY:
case PANEL_UPI_CLK_CHANGE:
case PANEL_ESD_RECOVERY_1:
case PANEL_ESD_RECOVERY_2:
break;
default:
LCD_INFO("Skip commands that are not related to ALPM\n");
goto end;
}
}
switch (cmd) {
case PANEL_DISPLAY_ON:
cmds = &vdd->dtsi_data[ndx].display_on_tx_cmds[vdd->panel_revision];
break;
case PANEL_DISPLAY_OFF:
cmds = &vdd->dtsi_data[ndx].display_off_tx_cmds[vdd->panel_revision];
break;
case PANEL_BRIGHT_CTRL:
cmds = &vdd->brightness[ndx].brightness_packet_tx_cmds_dsi;
break;
case PANEL_LEVE1_KEY_ENABLE:
cmds = &vdd->dtsi_data[ndx].level1_key_enable_tx_cmds[vdd->panel_revision];
break;
case PANEL_LEVE1_KEY_DISABLE:
cmds = &vdd->dtsi_data[ndx].level1_key_disable_tx_cmds[vdd->panel_revision];
break;
case PANEL_LEVE2_KEY_ENABLE:
cmds = &vdd->dtsi_data[ndx].level2_key_enable_tx_cmds[vdd->panel_revision];
break;
case PANEL_LEVE2_KEY_DISABLE:
cmds = &vdd->dtsi_data[ndx].level2_key_disable_tx_cmds[vdd->panel_revision];
break;
case PANEL_PACKET_SIZE:
cmds = &vdd->dtsi_data[ndx].packet_size_tx_cmds[vdd->panel_revision];
break;
case PANEL_REG_READ_POS:
cmds = &vdd->dtsi_data[ndx].reg_read_pos_tx_cmds[vdd->panel_revision];
break;
case PANEL_MDNIE_TUNE:
cmds = &vdd->mdnie_tune_data[ndx].mdnie_tune_packet_tx_cmds_dsi;
break;
case PANEL_OSC_TE_FITTING:
cmds = &vdd->dtsi_data[ndx].osc_te_fitting_tx_cmds[vdd->panel_revision];
break;
case PANEL_AVC_ON:
cmds = &vdd->dtsi_data[ndx].avc_on_tx_cmds[vdd->panel_revision];
break;
case PANEL_LDI_FPS_CHANGE:
cmds = &vdd->dtsi_data[ndx].ldi_fps_change_tx_cmds[vdd->panel_revision];
break;
case PANEL_HMT_ENABLE:
cmds = &vdd->dtsi_data[ndx].hmt_enable_tx_cmds[vdd->panel_revision];
break;
case PANEL_HMT_DISABLE:
cmds = &vdd->dtsi_data[ndx].hmt_disable_tx_cmds[vdd->panel_revision];
break;
case PANEL_HMT_REVERSE:
cmds = &vdd->dtsi_data[ndx].hmt_reverse_tx_cmds[vdd->panel_revision];
break;
case PANEL_HMT_FORWARD:
cmds = &vdd->dtsi_data[ndx].hmt_forward_tx_cmds[vdd->panel_revision];
break;
case PANEL_LDI_SET_VDD_OFFSET:
cmds = &vdd->dtsi_data[ndx].write_vdd_offset_cmds[vdd->panel_revision];
break;
case PANEL_LDI_SET_VDDM_OFFSET:
cmds = &vdd->dtsi_data[ndx].write_vddm_offset_cmds[vdd->panel_revision];
break;
case PANEL_LPM_ON:
cmds = &vdd->dtsi_data[ndx].alpm_on_tx_cmds[vdd->panel_revision];
break;
case PANEL_LPM_OFF:
cmds = &vdd->dtsi_data[ndx].alpm_off_tx_cmds[vdd->panel_revision];
break;
case PANEL_LPM_HZ_NONE:
cmds = &vdd->dtsi_data[ndx].lpm_hz_none_tx_cmds[vdd->panel_revision];
break;
case PANEL_LPM_1HZ:
cmds = &vdd->dtsi_data[ndx].lpm_1hz_tx_cmds[vdd->panel_revision];
break;
case PANEL_LPM_2HZ:
cmds = &vdd->dtsi_data[ndx].lpm_2hz_tx_cmds[vdd->panel_revision];
break;
case PANEL_LPM_30HZ:
cmds = &vdd->dtsi_data[ndx].lpm_hz_none_tx_cmds[vdd->panel_revision];
break;
case PANEL_LPM_AOD_ON:
cmds = &vdd->dtsi_data[ndx].lpm_aod_on_tx_cmds[vdd->panel_revision];
break;
case PANEL_LPM_AOD_OFF:
cmds = &vdd->dtsi_data[ndx].lpm_aod_off_tx_cmds[vdd->panel_revision];
break;
case PANEL_HSYNC_ON:
cmds = &vdd->dtsi_data[ndx].hsync_on_tx_cmds[vdd->panel_revision];
break;
case PANEL_CABC_ON:
cmds = &vdd->dtsi_data[ndx].cabc_on_tx_cmds[vdd->panel_revision];
break;
case PANEL_CABC_OFF:
cmds = &vdd->dtsi_data[ndx].cabc_off_tx_cmds[vdd->panel_revision];
break;
case PANEL_BLIC_DIMMING:
cmds = &vdd->dtsi_data[ndx].blic_dimming_cmds[vdd->panel_revision];
break;
case PANEL_CABC_ON_DUTY:
cmds = &vdd->dtsi_data[ndx].cabc_on_duty_tx_cmds[vdd->panel_revision];
break;
case PANEL_CABC_OFF_DUTY:
cmds = &vdd->dtsi_data[ndx].cabc_off_duty_tx_cmds[vdd->panel_revision];
break;
case PANEL_SPI_ENABLE:
cmds = &vdd->dtsi_data[ndx].spi_enable_tx_cmds[vdd->panel_revision];
break;
case PANEL_COLOR_WEAKNESS_ENABLE:
cmds = &vdd->dtsi_data[ndx].ccb_on_tx_cmds[vdd->panel_revision];
break;
case PANEL_COLOR_WEAKNESS_DISABLE:
cmds = &vdd->dtsi_data[ndx].ccb_off_tx_cmds[vdd->panel_revision];
break;
case PANEL_UPI_CLK_CHANGE:
cmds = &vdd->dtsi_data[ndx].upi_clk_change_tx_cmds[vdd->panel_revision];
cmds->link_state = DSI_LP_MODE;
break;
case PANEL_MCD_ON:
cmds = &vdd->dtsi_data[ndx].mcd_on_tx_cmds[vdd->panel_revision];
break;
case PANEL_MCD_OFF:
cmds = &vdd->dtsi_data[ndx].mcd_off_tx_cmds[vdd->panel_revision];
break;
case PANEL_GRADUAL_ACL:
cmds = &vdd->dtsi_data[ndx].gradual_acl_tx_cmds[vdd->panel_revision];
break;
case PANEL_HW_CURSOR:
cmds = &vdd->dtsi_data[ndx].hw_cursor_tx_cmds[vdd->panel_revision];
break;
default:
LCD_ERR("unknown_command(%d)..\n", cmd);
break;
}
if (!IS_ERR_OR_NULL(flags))
*flags |= cmd_flags;
end:
return cmds;
}
int mdss_samsung_send_cmd(struct mdss_dsi_ctrl_pdata *ctrl, enum mipi_samsung_cmd_list cmd)
{
struct mdss_panel_info *pinfo = NULL;
struct samsung_display_driver_data *vdd = NULL;
struct dsi_panel_cmds *pcmds = NULL;
u32 flags = CMD_REQ_COMMIT;
if (!mdss_panel_attach_get(ctrl)) {
LCD_ERR("mdss_panel_attach_get(%d) : %d\n",
ctrl->ndx, mdss_panel_attach_get(ctrl));
return -EAGAIN;
}
vdd = (struct samsung_display_driver_data *)ctrl->panel_data.panel_private;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("vdd is null\n");
return 0;
}
pinfo = &(ctrl->panel_data.panel_info);
if ((pinfo->blank_state == MDSS_PANEL_BLANK_BLANK) || (vdd->panel_func.samsung_lvds_write_reg)) {
LCD_ERR("blank stste is (%d)\n", pinfo->blank_state);
return 0;
}
mutex_lock(&vdd->vdd_blank_unblank_lock); /* To block blank & unblank operation while sending cmds */
/* To check registered FB */
if (IS_ERR_OR_NULL(vdd->mfd_dsi[ctrl->ndx])) {
/* Do not send any CMD data under FB_BLANK_POWERDOWN condition*/
if (vdd->vdd_blank_mode[DISPLAY_1] == FB_BLANK_POWERDOWN) {
mutex_unlock(&vdd->vdd_blank_unblank_lock); /* To block blank & unblank operation while sending cmds */
LCD_ERR("fb blank is POWERDOWN mode\n");
return 0;
}
} else {
/* Do not send any CMD data under FB_BLANK_POWERDOWN condition*/
if (vdd->vdd_blank_mode[ctrl->ndx] == FB_BLANK_POWERDOWN) {
mutex_unlock(&vdd->vdd_blank_unblank_lock); /* To block blank & unblank operation while sending cmds*/
LCD_ERR("fb blank is POWERDOWN mode.\n");
return 0;
}
}
mutex_lock(&vdd->vdd_lock);
pcmds = mdss_samsung_cmds_select(ctrl, cmd, &flags);
if (!IS_ERR_OR_NULL(pcmds) && !IS_ERR_OR_NULL(pcmds->cmds))
mdss_dsi_panel_cmds_send(ctrl, pcmds);
else
LCD_INFO("no panel_cmds..\n");
mutex_unlock(&vdd->vdd_lock);
mutex_unlock(&vdd->vdd_blank_unblank_lock); /* To block blank & unblank operation while sending cmds */
return 0;
}
static int mdss_samsung_read_nv_mem(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *cmds, unsigned char *buffer, int level_key)
{
int nv_read_cnt = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)ctrl->panel_data.panel_private;
if (IS_ERR_OR_NULL(vdd)) {
pr_err("%s: Invalid data ctrl : 0x%zx vdd : 0x%zx\n", __func__, (size_t)ctrl, (size_t)vdd);
return 0;
}
if ((level_key & PANEL_LEVE1_KEY) && !IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level1_key_enable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(ctrl, PANEL_LEVE1_KEY_ENABLE);
if ((level_key & PANEL_LEVE2_KEY) && !IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level2_key_enable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(ctrl, PANEL_LEVE2_KEY_ENABLE);
nv_read_cnt = samsung_nv_read(ctrl, cmds, buffer);
if ((level_key & PANEL_LEVE1_KEY) && !IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level1_key_disable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(ctrl, PANEL_LEVE1_KEY_DISABLE);
if ((level_key & PANEL_LEVE2_KEY) && !IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level2_key_disable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(ctrl, PANEL_LEVE2_KEY_DISABLE);
return nv_read_cnt;
}
void mdss_samsung_panel_data_read(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *cmds, char *buffer, int level_key)
{
if (!ctrl) {
LCD_ERR("Invalid ctrl data\n");
return;
}
if (!mdss_panel_attach_get(ctrl)) {
LCD_ERR("mdss_panel_attach_get(%d) : %d\n",
ctrl->ndx, mdss_panel_attach_get(ctrl));
return;
}
if (!cmds->cmd_cnt) {
LCD_ERR("cmds_count is zero..\n");
return;
}
mdss_samsung_read_nv_mem(ctrl, cmds, buffer, level_key);
}
static unsigned char mdss_samsung_manufacture_id(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *cmds)
{
char manufacture_id = 0;
mdss_samsung_panel_data_read(ctrl, cmds, &manufacture_id, PANEL_LEVE1_KEY);
return manufacture_id;
}
static int mdss_samsung_manufacture_id_full(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *cmds)
{
char manufacture_id[3];
int ret = 0;
mdss_samsung_panel_data_read(ctrl, cmds, manufacture_id, PANEL_LEVE1_KEY);
ret |= (manufacture_id[0] << 16);
ret |= (manufacture_id[1] << 8);
ret |= (manufacture_id[2]);
return ret;
}
int mdss_samsung_panel_on_pre(struct mdss_panel_data *pdata)
{
int ndx;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)pdata->panel_private;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return false;
}
ndx = display_ndx_check(ctrl);
vdd->display_status_dsi[ndx].disp_on_pre = 1;
if (vdd_data.other_line_panel_work_cnt)
vdd_data.other_line_panel_work_cnt = 0; /*stop open otherline dat file*/
#if !defined(CONFIG_SEC_FACTORY)
/* LCD ID read every wake_up time incase of factory binary */
if (vdd->dtsi_data[ndx].tft_common_support)
return false;
#endif
if (!mdss_panel_attach_get(ctrl)) {
LCD_ERR("mdss_panel_attach_get(%d) : %d\n",
ndx, mdss_panel_attach_get(ctrl));
return false;
}
LCD_INFO("+: ndx=%d\n", ndx);
if (unlikely(vdd->is_factory_mode) &&
vdd->dtsi_data[ndx].samsung_support_factory_panel_swap) {
/* LCD ID read every wake_up time incase of factory binary */
vdd->manufacture_id_dsi[ndx] = PBA_ID;
/* Factory Panel Swap*/
vdd->manufacture_date_loaded_dsi[ndx] = 0;
vdd->ddi_id_loaded_dsi[ndx] = 0;
vdd->hbm_loaded_dsi[ndx] = 0;
vdd->mdnie_loaded_dsi[ndx] = 0;
vdd->smart_dimming_loaded_dsi[ndx] = 0;
vdd->smart_dimming_hmt_loaded_dsi[ndx] = 0;
}
if (vdd->manufacture_id_dsi[ndx] == PBA_ID) {
/*
* At this time, panel revision it not selected.
* So last index(SUPPORT_PANEL_REVISION-1) used.
*/
vdd->panel_revision = SUPPORT_PANEL_REVISION-1;
/*
* Some panel needs to update register at init time to read ID & MTP
* Such as, dual-dsi-control or sleep-out so on.
*/
if (!IS_ERR_OR_NULL(vdd->dtsi_data[ndx].manufacture_id_rx_cmds[vdd->panel_revision].cmds)) {
vdd->manufacture_id_dsi[ndx] = mdss_samsung_manufacture_id_full(ctrl, &vdd->dtsi_data[ndx].manufacture_id_rx_cmds[SUPPORT_PANEL_REVISION-1]);
LCD_INFO("DSI%d manufacture_id(04h)=0x%x\n", ndx, vdd->manufacture_id_dsi[ndx]);
} else {
if (!IS_ERR_OR_NULL(vdd->dtsi_data[ndx].manufacture_read_pre_tx_cmds[vdd->panel_revision].cmds)) {
mutex_lock(&vdd->vdd_lock);
mdss_dsi_panel_cmds_send(ctrl, &vdd->dtsi_data[ndx].manufacture_read_pre_tx_cmds[vdd->panel_revision]);
mutex_unlock(&vdd->vdd_lock);
LCD_ERR("DSI%d manufacture_read_pre_tx_cmds ", ndx);
}
if (!IS_ERR_OR_NULL(vdd->dtsi_data[ndx].manufacture_id0_rx_cmds[vdd->panel_revision].cmds)) {
vdd->manufacture_id_dsi[ndx] = mdss_samsung_manufacture_id(ctrl, &vdd->dtsi_data[ndx].manufacture_id0_rx_cmds[SUPPORT_PANEL_REVISION-1]);
vdd->manufacture_id_dsi[ndx] <<= 8;
} else
LCD_ERR("DSI%d manufacture_id0_rx_cmds NULL", ndx);
if (!IS_ERR_OR_NULL(vdd->dtsi_data[ndx].manufacture_id1_rx_cmds[vdd->panel_revision].cmds)) {
vdd->manufacture_id_dsi[ndx] |= mdss_samsung_manufacture_id(ctrl, &vdd->dtsi_data[ndx].manufacture_id1_rx_cmds[SUPPORT_PANEL_REVISION-1]);
vdd->manufacture_id_dsi[ndx] <<= 8;
} else
LCD_ERR("DSI%d manufacture_id1_rx_cmds NULL", ndx);
if (!IS_ERR_OR_NULL(vdd->dtsi_data[ndx].manufacture_id2_rx_cmds[vdd->panel_revision].cmds))
vdd->manufacture_id_dsi[ndx] |= mdss_samsung_manufacture_id(ctrl, &vdd->dtsi_data[ndx].manufacture_id2_rx_cmds[SUPPORT_PANEL_REVISION-1]);
else
LCD_ERR("DSI%d manufacture_id2_rx_cmds NULL", ndx);
LCD_INFO("DSI%d manufacture_id=0x%x\n", ndx, vdd->manufacture_id_dsi[ndx]);
}
/* Panel revision selection */
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_revision))
LCD_ERR("DSI%d no panel_revision_selection_error fucntion\n", ndx);
else
vdd->panel_func.samsung_panel_revision(ctrl);
LCD_INFO("DSI%d Panel_Revision = %c %d\n", ndx, vdd->panel_revision + 'A', vdd->panel_revision);
}
/* Manufacture date */
if (!vdd->manufacture_date_loaded_dsi[ndx]) {
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_manufacture_date_read))
LCD_ERR("DSI%d no samsung_manufacture_date_read function\n", ndx);
else
vdd->manufacture_date_loaded_dsi[ndx] = vdd->panel_func.samsung_manufacture_date_read(ctrl);
}
/* DDI ID */
if (!vdd->ddi_id_loaded_dsi[ndx]) {
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_ddi_id_read))
LCD_ERR("DSI%d no samsung_ddi_id_read function\n", ndx);
else
vdd->ddi_id_loaded_dsi[ndx] = vdd->panel_func.samsung_ddi_id_read(ctrl);
}
/* Panel Unique Cell ID */
if (!vdd->cell_id_loaded_dsi[ndx]) {
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_cell_id_read))
LCD_ERR("DSI%d no samsung_cell_id_read function\n", ndx);
else
vdd->cell_id_loaded_dsi[ndx] = vdd->panel_func.samsung_cell_id_read(ctrl);
}
/* ELVSS read */
if (!vdd->elvss_loaded_dsi[ndx]) {
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_elvss_read))
LCD_ERR("DSI%d no samsung_elvss_read function\n", ndx);
else
vdd->elvss_loaded_dsi[ndx] = vdd->panel_func.samsung_elvss_read(ctrl);
}
/* HBM */
if (!vdd->hbm_loaded_dsi[ndx]) {
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_hbm_read))
LCD_ERR("DSI%d no samsung_hbm_read function\n", ndx);
else
vdd->hbm_loaded_dsi[ndx] = vdd->panel_func.samsung_hbm_read(ctrl);
}
/* MDNIE X,Y */
if (!vdd->mdnie_loaded_dsi[ndx]) {
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_mdnie_read))
LCD_ERR("DSI%d no samsung_mdnie_read function\n", ndx);
else
vdd->mdnie_loaded_dsi[ndx] = vdd->panel_func.samsung_mdnie_read(ctrl);
}
/* Panel Unique Cell ID */
if (!vdd->cell_id_loaded_dsi[ndx]) {
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_cell_id_read))
LCD_ERR("DSI%d no samsung_cell_id_read function\n", ndx);
else
vdd->cell_id_loaded_dsi[ndx] = vdd->panel_func.samsung_cell_id_read(ctrl);
}
/* Smart dimming*/
if (!vdd->smart_dimming_loaded_dsi[ndx]) {
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_smart_dimming_init))
LCD_ERR("DSI%d no samsung_smart_dimming_init function\n", ndx);
else
vdd->smart_dimming_loaded_dsi[ndx] = vdd->panel_func.samsung_smart_dimming_init(ctrl);
}
/* Smart dimming for hmt */
if (vdd->dtsi_data[0].hmt_enabled) {
if (!vdd->smart_dimming_hmt_loaded_dsi[ndx]) {
if (IS_ERR_OR_NULL(vdd->panel_func.samsung_smart_dimming_hmt_init))
LCD_ERR("DSI%d no samsung_smart_dimming_hmt_init function\n", ndx);
else
vdd->smart_dimming_hmt_loaded_dsi[ndx] = vdd->panel_func.samsung_smart_dimming_hmt_init(ctrl);
}
}
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_on_pre))
vdd->panel_func.samsung_panel_on_pre(ctrl);
LCD_INFO("-: ndx=%d\n", ndx);
return true;
}
int mdss_samsung_panel_on_post(struct mdss_panel_data *pdata)
{
int ndx;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd = NULL;
struct mdss_panel_info *pinfo = NULL;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(ctrl)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return false;
}
if (!mdss_panel_attach_get(ctrl)) {
LCD_ERR("mdss_panel_attach_get(%d) : %d\n",
ctrl->ndx, mdss_panel_attach_get(ctrl));
return false;
}
pinfo = &(ctrl->panel_data.panel_info);
if (IS_ERR_OR_NULL(pinfo)) {
LCD_ERR("Invalid data pinfo : 0x%zx\n", (size_t)pinfo);
return false;
}
ndx = display_ndx_check(ctrl);
LCD_INFO("+: ndx=%d\n", ndx);
MDSS_XLOG(ndx);
if (vdd->support_cabc && !vdd->auto_brightness)
mdss_samsung_cabc_update();
else if (vdd->mdss_panel_tft_outdoormode_update && vdd->auto_brightness)
vdd->mdss_panel_tft_outdoormode_update(ctrl);
else if (vdd->support_cabc && vdd->auto_brightness)
mdss_tft_autobrightness_cabc_update(ctrl);
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_on_post))
vdd->panel_func.samsung_panel_on_post(ctrl);
/* Recovery Mode : Set some default brightness */
if (vdd->recovery_boot_mode)
vdd->bl_level = DEFAULT_BRIGHTNESS;
if((vdd->panel_func.color_weakness_ccb_on_off)&& vdd->color_weakness_mode)
vdd->panel_func.color_weakness_ccb_on_off(vdd, vdd->color_weakness_mode);
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
if (vdd->support_hall_ic) {
/*
* Brightenss cmds sent by samsung_display_hall_ic_status() at panel switching operation
*/
if ((vdd->ctrl_dsi[DISPLAY_1]->bklt_ctrl == BL_DCS_CMD) &&
(vdd->display_status_dsi[DISPLAY_1].hall_ic_mode_change_trigger == false))
mdss_samsung_brightness_dcs(ctrl, vdd->bl_level);
} else {
if ((vdd->ctrl_dsi[DISPLAY_1]->bklt_ctrl == BL_DCS_CMD))
mdss_samsung_brightness_dcs(ctrl, vdd->bl_level);
}
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
if (vdd->support_mdnie_lite){
update_dsi_tcon_mdnie_register(vdd);
if(vdd->support_mdnie_trans_dimming)
vdd->mdnie_disable_trans_dimming = false;
}
vdd->display_status_dsi[ndx].wait_disp_on = true;
if (pinfo->esd_check_enabled) {
vdd->esd_recovery.esd_irq_enable(true, true, (void *)vdd);
vdd->esd_recovery.is_enabled_esd_recovery = true;
}
LCD_INFO("-: ndx=%d\n", ndx);
MDSS_XLOG(ndx);
return true;
}
int mdss_samsung_panel_off_pre(struct mdss_panel_data *pdata)
{
int ret = 0;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)pdata->panel_private;
struct mdss_panel_info *pinfo = NULL;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n",
(size_t)ctrl, (size_t)vdd);
return false;
}
pinfo = &(ctrl->panel_data.panel_info);
if (IS_ERR_OR_NULL(pinfo)) {
LCD_ERR("Invalid data pinfo : 0x%zx\n", (size_t)pinfo);
return false;
}
if (pinfo->esd_check_enabled) {
vdd->esd_recovery.is_wakeup_source = false;
vdd->esd_recovery.esd_irq_enable(false, true, (void *)vdd);
vdd->esd_recovery.is_enabled_esd_recovery = false;
}
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_off_pre))
vdd->panel_func.samsung_panel_off_pre(ctrl);
return ret;
}
int mdss_samsung_panel_off_post(struct mdss_panel_data *pdata)
{
int ret = 0;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)pdata->panel_private;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n",
(size_t)ctrl, (size_t)vdd);
return false;
}
if(vdd->support_mdnie_trans_dimming)
vdd->mdnie_disable_trans_dimming = true;
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_panel_off_post))
vdd->panel_func.samsung_panel_off_post(ctrl);
/* gradual acl on/off */
vdd->gradual_pre_acl_on = GRADUAL_ACL_UNSTABLE;
return ret;
}
/*************************************************************
*
* EXTRA POWER RELATED FUNCTION BELOW.
*
**************************************************************/
static int mdss_dsi_extra_power_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl)
{
int rc = 0, i;
/*
* gpio_name[] named as gpio_name + num(recomend as 0)
* because of the num will increase depend on number of gpio
*/
static const char gpio_name[] = "panel_extra_power";
static u8 gpio_request_status = -EINVAL;
struct samsung_display_driver_data *vdd = NULL;
if (!gpio_request_status)
goto end;
if (!IS_ERR_OR_NULL(ctrl))
vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data pinfo : 0x%zx\n", (size_t)vdd);
goto end;
}
for (i = 0; i < MAX_EXTRA_POWER_GPIO; i++) {
if (gpio_is_valid(vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i]) && mdss_panel_attach_get(ctrl)) {
rc = gpio_request(vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i],
gpio_name);
if (rc) {
LCD_ERR("request %s failed, rc=%d\n", gpio_name, rc);
goto extra_power_gpio_err;
}
}
}
gpio_request_status = rc;
end:
return rc;
extra_power_gpio_err:
if (i) {
do {
if (gpio_is_valid(vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i]))
gpio_free(vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i--]);
LCD_ERR("i = %d\n", i);
} while (i > 0);
}
return rc;
}
int mdss_samsung_panel_extra_power(struct mdss_panel_data *pdata, int enable)
{
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
int ret = 0, i = 0, add_value = 1;
struct samsung_display_driver_data *vdd = NULL;
if (IS_ERR_OR_NULL(pdata)) {
LCD_ERR("Invalid pdata : 0x%zx\n", (size_t)pdata);
ret = -EINVAL;
goto end;
}
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
if (!IS_ERR_OR_NULL(ctrl))
vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data pinfo : 0x%zx\n", (size_t)vdd);
goto end;
}
LCD_INFO("++ enable(%d) ndx(%d)\n",
enable, ctrl->ndx);
if (ctrl->ndx == DSI_CTRL_1)
goto end;
if (mdss_dsi_extra_power_request_gpios(ctrl)) {
LCD_ERR("fail to request extra power gpios");
goto end;
}
LCD_DEBUG("%s extra power gpios\n", enable ? "enable" : "disable");
/*
* The order of extra_power_gpio enable/disable
* 1. Enable : panel_extra_power_gpio[0], [1], ... [MAX_EXTRA_POWER_GPIO - 1]
* 2. Disable : panel_extra_power_gpio[MAX_EXTRA_POWER_GPIO - 1], ... [1], [0]
*/
if (!enable) {
add_value = -1;
i = MAX_EXTRA_POWER_GPIO - 1;
}
do {
if (gpio_is_valid(vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i]) && mdss_panel_attach_get(ctrl)) {
gpio_set_value(vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i], enable);
LCD_DEBUG("set extra power gpio[%d] to %s\n",
vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i],
enable ? "high" : "low");
usleep_range(1500, 1500);
}
} while (((i += add_value) < MAX_EXTRA_POWER_GPIO) && (i >= 0));
end:
LCD_INFO("--\n");
return ret;
}
/*************************************************************
*
* TFT BACKLIGHT GPIO FUNCTION BELOW.
*
**************************************************************/
int mdss_backlight_tft_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl)
{
int rc = 0, i;
/*
* gpio_name[] named as gpio_name + num(recomend as 0)
* because of the num will increase depend on number of gpio
*/
char gpio_name[17] = "disp_bcklt_gpio0";
static u8 gpio_request_status = -EINVAL;
struct samsung_display_driver_data *vdd = NULL;
if (!gpio_request_status)
goto end;
if (!IS_ERR_OR_NULL(ctrl))
vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data pinfo : 0x%zx\n", (size_t)vdd);
goto end;
}
for (i = 0; i < MAX_BACKLIGHT_TFT_GPIO; i++) {
if (gpio_is_valid(vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i])) {
rc = gpio_request(vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i],
gpio_name);
if (rc) {
LCD_ERR("request %s failed, rc=%d\n", gpio_name, rc);
goto tft_backlight_gpio_err;
}
}
}
gpio_request_status = rc;
end:
return rc;
tft_backlight_gpio_err:
if (i) {
do {
if (gpio_is_valid(vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i]))
gpio_free(vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i--]);
LCD_ERR("i = %d\n", i);
} while (i > 0);
}
return rc;
}
int mdss_backlight_tft_gpio_config(struct mdss_panel_data *pdata, int enable)
{
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
int ret = 0, i = 0, add_value = 1;
struct samsung_display_driver_data *vdd = NULL;
if (IS_ERR_OR_NULL(pdata)) {
LCD_ERR("Invalid pdata : 0x%zx\n", (size_t)pdata);
ret = -EINVAL;
goto end;
}
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
if (!IS_ERR_OR_NULL(ctrl))
vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data pinfo : 0x%zx\n", (size_t)vdd);
goto end;
}
LCD_INFO("++ enable(%d) ndx(%d)\n",
enable, ctrl->ndx);
if (ctrl->ndx == DSI_CTRL_1)
goto end;
if (mdss_backlight_tft_request_gpios(ctrl)) {
LCD_ERR("fail to request tft backlight gpios");
goto end;
}
LCD_DEBUG("%s tft backlight gpios\n", enable ? "enable" : "disable");
/*
* The order of backlight_tft_gpio enable/disable
* 1. Enable : backlight_tft_gpio[0], [1], ... [MAX_BACKLIGHT_TFT_GPIO - 1]
* 2. Disable : backlight_tft_gpio[MAX_BACKLIGHT_TFT_GPIO - 1], ... [1], [0]
*/
if (!enable) {
add_value = -1;
i = MAX_BACKLIGHT_TFT_GPIO - 1;
}
do {
if (gpio_is_valid(vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i])) {
gpio_set_value(vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i], enable);
LCD_DEBUG("set backlight tft gpio[%d] to %s\n",
vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i],
enable ? "high" : "low");
usleep_range(500, 500);
}
} while (((i += add_value) < MAX_BACKLIGHT_TFT_GPIO) && (i >= 0));
end:
LCD_INFO("--\n");
return ret;
}
/*************************************************************
*
* ESD RECOVERY RELATED FUNCTION BELOW.
*
**************************************************************/
static int mdss_dsi_esd_irq_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
LCD_INFO("lcd esd recovery\n");
return !ctrl->status_value;
}
/*
* esd_irq_enable() - Enable or disable esd irq.
*
* @enable : flag for enable or disabled
* @nosync : flag for disable irq with nosync
* @data : point ot struct mdss_panel_info
*/
static void esd_irq_enable(bool enable, bool nosync, void *data)
{
/* The irq will enabled when do the request_threaded_irq() */
static bool is_enabled = true;
static bool is_wakeup_source = false;
int gpio;
unsigned long flags;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)data;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd);
return;
}
spin_lock_irqsave(&vdd->esd_recovery.irq_lock, flags);
if (enable == is_enabled) {
LCD_INFO("ESD irq already %s\n",
enable ? "enabled" : "disabled");
goto config_wakeup_source;
}
gpio = vdd->esd_recovery.esd_gpio;
if (enable) {
is_enabled = true;
enable_irq(gpio_to_irq(gpio));
} else {
if (nosync)
disable_irq_nosync(gpio_to_irq(gpio));
else
disable_irq(gpio_to_irq(gpio));
is_enabled = false;
}
/* TODO: Disable log if the esd function stable */
LCD_DEBUG("ESD irq %s with %s\n",
enable ? "enabled" : "disabled",
nosync ? "nosync" : "sync");
config_wakeup_source:
if (vdd->esd_recovery.is_wakeup_source == is_wakeup_source) {
LCD_DEBUG("[ESD] IRQs are already irq_wake %s\n",
is_wakeup_source ? "enabled" : "disabled");
goto end;
}
gpio = vdd->esd_recovery.esd_gpio;
if (!gpio_is_valid(gpio)) {
LCD_ERR("Invalid ESD_GPIO : %d\n", gpio);
}
is_wakeup_source =
vdd->esd_recovery.is_wakeup_source;
if (is_wakeup_source)
enable_irq_wake(gpio_to_irq(gpio));
else
disable_irq_wake(gpio_to_irq(gpio));
LCD_DEBUG("[ESD] IRQs are set to irq_wake %s\n",
is_wakeup_source ? "enabled" : "disabled");
end:
spin_unlock_irqrestore(&vdd->esd_recovery.irq_lock, flags);
}
static irqreturn_t esd_irq_handler(int irq, void *handle)
{
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd = NULL;
struct mdss_panel_info *pinfo;
if (!handle) {
LCD_INFO("handle is null\n");
return IRQ_HANDLED;
}
ctrl = (struct mdss_dsi_ctrl_pdata *)handle;
if (!IS_ERR_OR_NULL(ctrl))
vdd = check_valid_ctrl(ctrl);
pinfo = &ctrl->panel_data.panel_info;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd);
goto end;
}
if (!vdd->esd_recovery.is_enabled_esd_recovery) {
LCD_DEBUG("esd recovery is not enabled yet");
goto end;
}
LCD_INFO("++\n");
esd_irq_enable(false, true, (void *)vdd);
vdd->panel_lpm.esd_recovery = true;
if (!(pinfo->blank_state == MDSS_PANEL_BLANK_LOW_POWER)) {
schedule_work(&pstatus_data->check_status.work);
} else {
/* schedule_work(&pstatus_data->check_status.work); */
mdss_fb_report_panel_dead(pstatus_data->mfd);
}
LCD_INFO("--\n");
end:
return IRQ_HANDLED;
}
static void mdss_samsung_event_esd_recovery_init(struct mdss_panel_data *pdata, int event, void *arg)
{
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd = NULL;
int ret;
uint32_t *interval;
uint32_t interval_ms_for_irq = 500;
struct mdss_panel_info *pinfo;
struct esd_recovery *esd = NULL;
if (IS_ERR_OR_NULL(pdata)) {
LCD_ERR("Invalid pdata : 0x%zx\n", (size_t)pdata);
return;
}
vdd = (struct samsung_display_driver_data *)pdata->panel_private;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
esd = &vdd->esd_recovery;
interval = arg;
pinfo = &ctrl->panel_data.panel_info;
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n",
(size_t)ctrl, (size_t)vdd);
return;
}
if (unlikely(!esd->esd_recovery_init)) {
esd->esd_recovery_init = true;
esd->esd_irq_enable = esd_irq_enable;
if (ctrl->status_mode == ESD_REG_IRQ) {
if (gpio_is_valid(vdd->esd_recovery.esd_gpio)) {
gpio_request(vdd->esd_recovery.esd_gpio, "esd_recovery");
ret = request_threaded_irq(
gpio_to_irq(vdd->esd_recovery.esd_gpio),
NULL,
esd_irq_handler,
vdd->esd_recovery.irqflags,
"esd_recovery",
(void *)ctrl);
if (ret)
LCD_ERR("Failed to request_irq, ret=%d\n",
ret);
else
esd_irq_enable(false, true, (void *)vdd);
*interval = interval_ms_for_irq;
}
}
}
}
/*************************************************************
*
* BRIGHTNESS RELATED FUNCTION BELOW.
*
**************************************************************/
int get_cmd_index(struct samsung_display_driver_data *vdd, int ndx)
{
int index;
index = vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].bkl[vdd->bl_level];
if (vdd->dtsi_data[0].hmt_enabled && vdd->hmt_stat.hmt_on)
index = vdd->cmd_idx;
return vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].cmd_idx[index];
}
int get_candela_value(struct samsung_display_driver_data *vdd, int ndx)
{
int index;
index = vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].bkl[vdd->bl_level];
if (vdd->dtsi_data[0].hmt_enabled && vdd->hmt_stat.hmt_on)
index = vdd->cmd_idx;
return vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].lux_tab[index];
}
void set_auto_brightness_value(struct samsung_display_driver_data *vdd, int ndx)
{
int i, from, end;
int size;
size = vdd->dtsi_data[ndx].hbm_candela_map_table[vdd->panel_revision].lux_tab_size;
if (size == 0){
pr_info("%s: HBM candela map table is not present\n", __func__);
return;
}
for (i=0; i<size; i++) {
from = vdd->dtsi_data[ndx].hbm_candela_map_table[vdd->panel_revision].from[i];
end = vdd->dtsi_data[ndx].hbm_candela_map_table[vdd->panel_revision].end[i];
if (vdd->bl_level >= from && vdd->bl_level <= end)
break;
}
if (i == size) {
LCD_ERR("can not find auto brightness value !!(for %d / size %d)\n", vdd->bl_level, size);
i = size-1;
}
vdd->candela_level = vdd->dtsi_data[ndx].hbm_candela_map_table[vdd->panel_revision].lux_tab[i];
vdd->auto_brightness = vdd->dtsi_data[ndx].hbm_candela_map_table[vdd->panel_revision].auto_level[i];
return;
}
int get_bl_level_from_cd(struct samsung_display_driver_data *vdd, int ndx, int cd)
{
int i, idx = 0;
for (i=0; i<vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].lux_tab_size; i++) {
if (vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].lux_tab[i] == cd) {
idx = i;
break;
}
if (vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].lux_tab[i] > cd) {
if ((cd - vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].lux_tab[i-1]) >=
(vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].lux_tab[i] - cd)) {
idx = i;
break;
} else {
idx = i-1;
break;
}
}
}
LCD_ERR("idx (%d)\n", idx);
return vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].end[idx];
}
int get_bl_level(struct samsung_display_driver_data *vdd, int ndx, int idx)
{
return vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].end[idx];
}
int get_scaled_level(struct samsung_display_driver_data *vdd, int ndx)
{
int index = vdd->dtsi_data[ndx].scaled_level_map_table[vdd->panel_revision].bkl[vdd->bl_level];
return vdd->dtsi_data[ndx].scaled_level_map_table[vdd->panel_revision].lux_tab[index];
}
static void mdss_samsung_update_brightness_packet(struct dsi_cmd_desc *packet, int *count, struct dsi_panel_cmds *tx_cmd)
{
int loop = 0;
if (IS_ERR_OR_NULL(packet)) {
LCD_ERR("%ps no packet\n", __builtin_return_address(0));
return;
}
if (IS_ERR_OR_NULL(tx_cmd)) {
LCD_ERR("%ps no tx_cmd\n", __builtin_return_address(0));
return;
}
if (*count > (BRIGHTNESS_MAX_PACKET - 1))/*cmd_count is index, if cmd_count >13 then panic*/
panic("over max brightness_packet size(%d).. !!", BRIGHTNESS_MAX_PACKET);
for (loop = 0; loop < tx_cmd->cmd_cnt; loop++) {
packet[*count].dchdr.dtype = tx_cmd->cmds[loop].dchdr.dtype;
packet[*count].dchdr.wait = tx_cmd->cmds[loop].dchdr.wait;
packet[*count].dchdr.dlen = tx_cmd->cmds[loop].dchdr.dlen;
packet[*count].payload = tx_cmd->cmds[loop].payload;
(*count)++;
}
}
static void update_packet_level_key_enable(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_cmd_desc *packet, int *cmd_cnt, int level_key)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)ctrl->panel_data.panel_private;
if (IS_ERR_OR_NULL(vdd)) {
pr_err("%s: Invalid data ctrl : 0x%zx vdd : 0x%zx\n", __func__, (size_t)ctrl, (size_t)vdd);
return;
}
if (!level_key)
return;
else {
if ((level_key & PANEL_LEVE1_KEY) && !IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level1_key_enable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_update_brightness_packet(packet, cmd_cnt, mdss_samsung_cmds_select(ctrl, PANEL_LEVE1_KEY_ENABLE, NULL));
if ((level_key & PANEL_LEVE2_KEY) && !IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level2_key_enable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_update_brightness_packet(packet, cmd_cnt, mdss_samsung_cmds_select(ctrl, PANEL_LEVE2_KEY_ENABLE, NULL));
}
}
static void update_packet_level_key_disable(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_cmd_desc *packet, int *cmd_cnt, int level_key)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)ctrl->panel_data.panel_private;
if (IS_ERR_OR_NULL(vdd)) {
pr_err("%s: Invalid data ctrl : 0x%zx vdd : 0x%zx\n", __func__, (size_t)ctrl, (size_t)vdd);
return;
};
if (!level_key)
return;
else {
if ((level_key & PANEL_LEVE1_KEY) && !IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level1_key_disable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_update_brightness_packet(packet, cmd_cnt, mdss_samsung_cmds_select(ctrl, PANEL_LEVE1_KEY_DISABLE, NULL));
if ((level_key & PANEL_LEVE2_KEY) && !IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level2_key_disable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_update_brightness_packet(packet, cmd_cnt, mdss_samsung_cmds_select(ctrl, PANEL_LEVE2_KEY_DISABLE, NULL));
}
}
int mdss_samsung_hbm_brightenss_packet_set(struct mdss_dsi_ctrl_pdata *ctrl)
{
int ndx;
int cmd_cnt = 0;
int level_key = 0;
struct dsi_cmd_desc *packet = NULL;
struct dsi_panel_cmds *tx_cmd = NULL;
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return 0;
}
ndx = display_ndx_check(ctrl);
/* init packet */
packet = &vdd->brightness[ndx].brightness_packet_dsi[0];
/*
* HBM doesn't need calculated cmds. So Just use previously fixed data.
*/
/* To check supporting HBM mdoe by hbm_gamma_tx_cmds */
// candela_level and auto_brightness
set_auto_brightness_value(vdd, ctrl->ndx);
/* aid/aor */
if (vdd->color_weakness_mode)
{
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_aid)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_aid(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
}
/* IRC */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_hbm_irc)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_hbm_irc(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
/* Gamma */
if (vdd->dtsi_data[ndx].hbm_gamma_tx_cmds[vdd->panel_revision].cmd_cnt) {
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_hbm_gamma)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_hbm_gamma(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
}
/* hbm etc */
if (vdd->dtsi_data[ndx].hbm_etc_tx_cmds[vdd->panel_revision].cmd_cnt) {
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_hbm_etc)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_hbm_etc(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
}
return cmd_cnt;
}
int mdss_samsung_normal_brightenss_packet_set(struct mdss_dsi_ctrl_pdata *ctrl)
{
int ndx;
int cmd_cnt = 0;
int level_key = 0;
struct dsi_cmd_desc *packet = NULL;
struct dsi_panel_cmds *tx_cmd = NULL;
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return 0;
}
ndx = display_ndx_check(ctrl);
vdd->auto_brightness = 0;
/* consider 256 ~ 281 level for gallery ACL off */
if (vdd->bl_level > 255)
vdd->bl_level = 255;
vdd->candela_level = get_candela_value(vdd, ctrl->ndx);
packet = &vdd->brightness[ndx].brightness_packet_dsi[0]; /* init packet */
if (vdd->smart_dimming_loaded_dsi[ndx]) { /* OCTA PANEL */
/* hbm off */
if (vdd->display_status_dsi[ndx].hbm_mode) {
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_hbm_off)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_hbm_off(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
}
/* aid/aor */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_aid)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_aid(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
/* acl */
if (vdd->acl_status || vdd->siop_status) {
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_acl_on)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_acl_on(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_acl_percent)) {
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_pre_acl_percent)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_pre_acl_percent(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_acl_percent(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
} else {
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_acl_off)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_acl_off(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
}
/* elvss */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_elvss)) {
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_pre_elvss)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_pre_elvss(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_elvss(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
/* temperature elvss */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_elvss_temperature1)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_elvss_temperature1(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_elvss_temperature2)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_elvss_temperature2(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
/* caps*/
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_caps)) {
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_pre_caps)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_pre_caps(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_caps(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
/* IRC */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_irc)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_irc(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
/* gamma */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_gamma)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_gamma(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
} else { /* TFT PANEL */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_tft_pwm_ldi)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_tft_pwm_ldi(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
}
return cmd_cnt;
}
static int mdss_samsung_single_transmission_packet(struct samsung_brightenss_data *tx_packet)
{
int loop;
struct dsi_cmd_desc *packet = tx_packet->brightness_packet_dsi;
int packet_cnt = tx_packet->brightness_packet_tx_cmds_dsi.cmd_cnt;
for (loop = 0; (loop < packet_cnt) && (loop < BRIGHTNESS_MAX_PACKET); loop++) {
if (packet[loop].dchdr.dtype == DTYPE_DCS_LWRITE ||
packet[loop].dchdr.dtype == DTYPE_GEN_LWRITE)
packet[loop].dchdr.last = 0;
else {
if (loop > 0)
packet[loop - 1].dchdr.last = 1; /*To ensure previous single tx packet */
packet[loop].dchdr.last = 1;
}
}
if (loop == BRIGHTNESS_MAX_PACKET)
return false;
else {
packet[loop - 1].dchdr.last = 1; /* To make last packet flag */
return true;
}
}
int mdss_samsung_brightness_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level)
{
struct samsung_display_driver_data *vdd = NULL;
int cmd_cnt = 0;
int ret = 0;
int ndx;
int hbm_min_level;
vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return false;
}
vdd->bl_level = level;
/* check the lcd id for DISPLAY_1 or DISPLAY_2 */
if (!mdss_panel_attached(ctrl->ndx))
return false;
if (vdd->dtsi_data[0].hmt_enabled && vdd->hmt_stat.hmt_on) {
LCD_ERR("HMT is on. do not set normal brightness..(%d)\n", level);
return false;
}
if (vdd->mfd_dsi[DISPLAY_1]->panel_info->cont_splash_enabled) {
LCD_ERR("splash is not done..\n");
return 0;
}
ndx = display_ndx_check(ctrl);
hbm_min_level = vdd->dtsi_data[ndx].hbm_candela_map_table[vdd->panel_revision].hbm_min_lv;
if (vdd->bl_level >= hbm_min_level && !vdd->dtsi_data[ndx].tft_common_support) {
cmd_cnt = mdss_samsung_hbm_brightenss_packet_set(ctrl);
cmd_cnt > 0 ? vdd->display_status_dsi[ndx].hbm_mode = true : false;
} else {
cmd_cnt = mdss_samsung_normal_brightenss_packet_set(ctrl);
cmd_cnt > 0 ? vdd->display_status_dsi[ndx].hbm_mode = false : false;
}
if (cmd_cnt) {
/* setting tx cmds cmt */
vdd->brightness[ndx].brightness_packet_tx_cmds_dsi.cmd_cnt = cmd_cnt;
/* generate single tx packet */
ret = mdss_samsung_single_transmission_packet(&vdd->brightness[ndx]);
/* sending tx cmds */
if (ret) {
mdss_samsung_send_cmd(ctrl, PANEL_BRIGHT_CTRL);
if (!IS_ERR_OR_NULL(vdd->dtsi_data[ndx].blic_dimming_cmds[vdd->panel_revision].cmds)) {
if (vdd->bl_level == 0)
vdd->dtsi_data[ndx].blic_dimming_cmds->cmds->payload[1] = 0x24;
else
vdd->dtsi_data[ndx].blic_dimming_cmds->cmds->payload[1] = 0x2C;
mdss_samsung_send_cmd(ctrl, PANEL_BLIC_DIMMING);
}
LCD_INFO("DSI%d level : %d candela : %dCD hbm : %d (%d)\n",
ndx, vdd->bl_level, vdd->candela_level, vdd->display_status_dsi[ndx].hbm_mode, vdd->auto_brightness);
} else
LCD_INFO("DSDI%d single_transmission_fail error\n", ndx);
} else
LCD_INFO("DSI%d level : %d skip\n", ndx, vdd->bl_level);
if (vdd->auto_brightness >= HBM_CE_MODE &&
vdd->bl_level >= hbm_min_level &&
!vdd->dtsi_data[ndx].tft_common_support &&
vdd->support_mdnie_lite)
update_dsi_tcon_mdnie_register(vdd);
return cmd_cnt;
}
void mdss_samsung_brightness_tft_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level)
{
struct samsung_display_driver_data *vdd = NULL;
struct mdss_panel_info *pinfo;
vdd = check_valid_ctrl(ctrl);
if (vdd == NULL) {
LCD_ERR("no PWM\n");
return;
}
pinfo = &(ctrl->panel_data.panel_info);
if (pinfo->blank_state == MDSS_PANEL_BLANK_BLANK)
return;
vdd->bl_level = level;
if (vdd->panel_func.samsung_brightness_tft_pwm)
vdd->panel_func.samsung_brightness_tft_pwm(ctrl, level);
}
void mdss_tft_autobrightness_cabc_update(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx", (size_t)ctrl, (size_t)vdd);
return;
}
LCD_INFO("\n");
switch (vdd->auto_brightness) {
case 0:
mdss_samsung_cabc_update();
break;
case 1:
case 2:
case 3:
case 4:
mdss_samsung_send_cmd(ctrl, PANEL_CABC_ON);
break;
case 5:
case 6:
mdss_samsung_send_cmd(ctrl, PANEL_CABC_OFF);
break;
}
}
/*************************************************************
*
* OSC TE FITTING RELATED FUNCTION BELOW.
*
**************************************************************/
static void mdss_samsung_event_osc_te_fitting(struct mdss_panel_data *pdata, int event, void *arg)
{
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd = NULL;
struct osc_te_fitting_info *te_info = NULL;
struct mdss_mdp_ctl *ctl = NULL;
int ret, i, lut_count;
if (IS_ERR_OR_NULL(pdata)) {
LCD_ERR("Invalid pdata : 0x%zx\n", (size_t)pdata);
return;
}
vdd = (struct samsung_display_driver_data *)pdata->panel_private;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
ctl = arg;
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(ctl)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx ctl : 0x%zx\n",
(size_t)ctrl, (size_t)vdd, (size_t)ctl);
return;
}
te_info = &vdd->te_fitting_info;
if (IS_ERR_OR_NULL(te_info)) {
LCD_ERR("Invalid te data : 0x%zx\n",
(size_t)te_info);
return;
}
if (pdata->panel_info.cont_splash_enabled) {
LCD_ERR("cont splash enabled\n");
return;
}
if (!ctl->vsync_handler.enabled) {
LCD_DEBUG("vsync handler does not enabled yet\n");
return;
}
te_info->status |= TE_FITTING_DONE;
LCD_DEBUG("++\n");
if (!(te_info->status & TE_FITTING_REQUEST_IRQ)) {
te_info->status |= TE_FITTING_REQUEST_IRQ;
ret = request_threaded_irq(
gpio_to_irq(ctrl->disp_te_gpio),
samsung_te_check_handler,
NULL,
IRQF_TRIGGER_FALLING,
"VSYNC_GPIO",
(void *)ctrl);
if (ret)
LCD_ERR("Failed to request_irq, ret=%d\n",
ret);
else
disable_irq(gpio_to_irq(ctrl->disp_te_gpio));
te_info->te_time =
kzalloc(sizeof(long long) * te_info->sampling_rate, GFP_KERNEL);
INIT_WORK(&te_info->work, samsung_te_check_done_work);
}
for (lut_count = 0; lut_count < OSC_TE_FITTING_LUT_MAX; lut_count++) {
init_completion(&te_info->te_check_comp);
te_info->status |= TE_CHECK_ENABLE;
te_info->te_duration = 0;
LCD_DEBUG("osc_te_fitting _irq : %d\n",
gpio_to_irq(ctrl->disp_te_gpio));
enable_irq(gpio_to_irq(ctrl->disp_te_gpio));
ret = wait_for_completion_timeout(
&te_info->te_check_comp, 1000);
if (ret <= 0)
LCD_ERR("timeout\n");
for (i = 0; i < te_info->sampling_rate; i++) {
te_info->te_duration +=
(i != 0 ? (te_info->te_time[i] - te_info->te_time[i-1]) : 0);
LCD_DEBUG("vsync time : %lld, sum : %lld\n",
te_info->te_time[i], te_info->te_duration);
}
do_div(te_info->te_duration, te_info->sampling_rate - 1);
LCD_INFO("ave vsync time : %lld\n",
te_info->te_duration);
te_info->status &= ~TE_CHECK_ENABLE;
if (vdd->panel_func.samsung_osc_te_fitting)
ret = vdd->panel_func.samsung_osc_te_fitting(ctrl);
if (!ret)
mdss_samsung_send_cmd(ctrl, PANEL_OSC_TE_FITTING);
else
break;
}
LCD_DEBUG("--\n");
}
static void samsung_te_check_done_work(struct work_struct *work)
{
struct osc_te_fitting_info *te_info = NULL;
te_info = container_of(work, struct osc_te_fitting_info, work);
if (IS_ERR_OR_NULL(te_info)) {
LCD_ERR("Invalid TE tuning data\n");
return;
}
complete_all(&te_info->te_check_comp);
}
static irqreturn_t samsung_te_check_handler(int irq, void *handle)
{
struct samsung_display_driver_data *vdd = NULL;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct osc_te_fitting_info *te_info = NULL;
static bool skip_first_te = true;
static u8 count;
if (skip_first_te) {
skip_first_te = false;
goto end;
}
if (IS_ERR_OR_NULL(handle)) {
LCD_ERR("handle is null\n");
goto end;
}
ctrl = (struct mdss_dsi_ctrl_pdata *)handle;
vdd = (struct samsung_display_driver_data *)ctrl->panel_data.panel_private;
te_info = &vdd->te_fitting_info;
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n",
(size_t)ctrl, (size_t)vdd);
return -EINVAL;
}
if (!(te_info->status & TE_CHECK_ENABLE))
goto end;
if (count < te_info->sampling_rate) {
te_info->te_time[count++] =
ktime_to_us(ktime_get());
} else {
disable_irq_nosync(gpio_to_irq(ctrl->disp_te_gpio));
schedule_work(&te_info->work);
skip_first_te = true;
count = 0;
}
end:
return IRQ_HANDLED;
}
/*************************************************************
*
* LDI FPS RELATED FUNCTION BELOW.
*
**************************************************************/
int ldi_fps(unsigned int input_fps)
{
int rc = 0;
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd = samsung_get_vdd();
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd);
return 0;
}
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
if (IS_ERR_OR_NULL(ctrl)) {
LCD_ERR("no ctrl");
return 0;
}
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_change_ldi_fps))
rc = vdd->panel_func.samsung_change_ldi_fps(ctrl, input_fps);
if (rc)
mdss_samsung_send_cmd(ctrl, PANEL_LDI_FPS_CHANGE);
return rc;
}
EXPORT_SYMBOL(ldi_fps);
/*************************************************************
*
* HMT RELATED FUNCTION BELOW.
*
**************************************************************/
static int get_candela_value_hmt(struct samsung_display_driver_data *vdd, int ndx)
{
int index = vdd->dtsi_data[ndx].hmt_candela_map_table[vdd->panel_revision].bkl[vdd->hmt_stat.hmt_bl_level];
return vdd->dtsi_data[ndx].hmt_candela_map_table[vdd->panel_revision].lux_tab[index];
}
static int get_cmd_idx_hmt(struct samsung_display_driver_data *vdd, int ndx)
{
int index = vdd->dtsi_data[ndx].hmt_candela_map_table[vdd->panel_revision].bkl[vdd->hmt_stat.hmt_bl_level];
return vdd->dtsi_data[ndx].hmt_candela_map_table[vdd->panel_revision].cmd_idx[index];
}
int mdss_samsung_hmt_brightenss_packet_set(struct mdss_dsi_ctrl_pdata *ctrl)
{
int cmd_cnt = 0;
int level_key = 0;
struct dsi_cmd_desc *packet = NULL;
struct dsi_panel_cmds *tx_cmd = NULL;
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
LCD_INFO("++\n");
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return 0;
}
/* init packet */
packet = &vdd->brightness[ctrl->ndx].brightness_packet_dsi[0];
if (vdd->smart_dimming_hmt_loaded_dsi[ctrl->ndx]) {
/* aid/aor B2 */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_aid_hmt)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_aid_hmt(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
/* elvss B5 */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_elvss_hmt)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_elvss_hmt(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
/* vint F4 */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_vint_hmt)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_vint_hmt(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
/* gamma CA */
if (!IS_ERR_OR_NULL(vdd->panel_func.samsung_brightness_gamma_hmt)) {
level_key = false;
tx_cmd = vdd->panel_func.samsung_brightness_gamma_hmt(ctrl, &level_key);
update_packet_level_key_enable(ctrl, packet, &cmd_cnt, level_key);
mdss_samsung_update_brightness_packet(packet, &cmd_cnt, tx_cmd);
update_packet_level_key_disable(ctrl, packet, &cmd_cnt, level_key);
}
}
LCD_INFO("--\n");
return cmd_cnt;
}
int mdss_samsung_brightness_dcs_hmt(struct mdss_dsi_ctrl_pdata *ctrl, int level)
{
struct samsung_display_driver_data *vdd = NULL;
int cmd_cnt;
int ret = 0;
vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n", (size_t)ctrl, (size_t)vdd);
return false;
}
vdd->hmt_stat.hmt_bl_level = level;
LCD_ERR("[HMT] hmt_bl_level(%d)\n", vdd->hmt_stat.hmt_bl_level);
if (level < 0 || level > 255) {
LCD_ERR("[HMT] hmt_bl_level(%d) is out of range!\n", level);
return 0;
}
vdd->hmt_stat.cmd_idx_hmt = get_cmd_idx_hmt(vdd, ctrl->ndx);
vdd->hmt_stat.candela_level_hmt = get_candela_value_hmt(vdd, ctrl->ndx);
cmd_cnt = mdss_samsung_hmt_brightenss_packet_set(ctrl);
/* sending tx cmds */
if (cmd_cnt) {
/* setting tx cmds cmt */
vdd->brightness[ctrl->ndx].brightness_packet_tx_cmds_dsi.cmd_cnt = cmd_cnt;
/* generate single tx packet */
ret = mdss_samsung_single_transmission_packet(vdd->brightness);
if (ret) {
mdss_samsung_send_cmd(ctrl, PANEL_BRIGHT_CTRL);
LCD_INFO("DSI(%d) cmd_idx(%d), candela_level(%d), hmt_bl_level(%d)",
ctrl->ndx, vdd->hmt_stat.cmd_idx_hmt, vdd->hmt_stat.candela_level_hmt, vdd->hmt_stat.hmt_bl_level);
} else
LCD_DEBUG("DSDI%d single_transmission_fail error\n", ctrl->ndx);
} else
LCD_INFO("DSI%d level : %d skip\n", ctrl->ndx, vdd->bl_level);
return cmd_cnt;
}
/************************************************************
PANEL DTSI PARSE FUNCTION.
--- NEVER DELETE OR CHANGE ORDER ---
---JUST ADD ITEMS AT LAST---
"samsung,display_on_tx_cmds_rev%c"
"samsung,display_off_tx_cmds_rev%c"
"samsung,level1_key_enable_tx_cmds_rev%c"
"samsung,level1_key_disable_tx_cmds_rev%c"
"samsung,hsync_on_tx_cmds_rev%c"
"samsung,level2_key_enable_tx_cmds_rev%c"
"samsung,level2_key_disable_tx_cmds_rev%c"
"samsung,smart_dimming_mtp_rx_cmds_rev%c"
"samsung,manufacture_read_pre_tx_cmds_rev%c"
"samsung,manufacture_id0_rx_cmds_rev%c"
"samsung,manufacture_id1_rx_cmds_rev%c"
"samsung,manufacture_id2_rx_cmds_rev%c"
"samsung,manufacture_date_rx_cmds_rev%c"
"samsung,ddi_id_rx_cmds_rev%c"
"samsung,rddpm_rx_cmds_rev%c"
"samsung,mtp_read_sysfs_rx_cmds_rev%c"
"samsung,vint_tx_cmds_rev%c"
"samsung,vint_map_table_rev%c"
"samsung,acl_off_tx_cmds_rev%c"
"samsung,acl_map_table_rev%c"
"samsung,candela_map_table_rev%c"
"samsung,acl_percent_tx_cmds_rev%c"
"samsung,acl_pre_percent_tx_cmds_rev%c"
"samsung,acl_on_tx_cmds_rev%c"
"samsung,gamma_tx_cmds_rev%c"
"samsung,elvss_rx_cmds_rev%c"
"samsung,elvss_tx_cmds_rev%c"
"samsung,elvss_map_table_rev%c"
"samsung,elvss_pre_tx_cmds_rev%c"
"samsung,aid_map_table_rev%c"
"samsung,aid_tx_cmds_rev%c"
"samsung,hbm_rx_cmds_rev%c"
"samsung,hbm2_rx_cmds_rev%c"
"samsung,hbm_gamma_tx_cmds_rev%c"
"samsung,hbm_etc_tx_cmds_rev%c"
"samsung,hbm_etc2_tx_cmds_rev%c"
"samsung,hbm_off_tx_cmds_rev%c"
"samsung,mdnie_read_rx_cmds_rev%c"
"samsung,ldi_debug0_rx_cmds_rev%c"
"samsung,ldi_debug1_rx_cmds_rev%c"
"samsung,ldi_debug2_rx_cmds_rev%c"
"samsung,elvss_lowtemp_tx_cmds_rev%c"
"samsung,elvss_lowtemp2_tx_cmds_rev%c"
"samsung,smart_acl_elvss_lowtemp_tx_cmds_rev%c"
"samsung,smart_acl_elvss_tx_cmds_rev%c"
"samsung,smart_acl_elvss_map_table_rev%c"
"samsung,partial_display_on_tx_cmds_rev%c"
"samsung,partial_display_off_tx_cmds_rev%c"
"samsung,partial_display_column_row_tx_cmds_rev%c"
"samsung,alpm_on_tx_cmds_rev%c"
"samsung,alpm_off_tx_cmds_rev%c"
"samsung,lpm_2nit_tx_cmds_rev%c"
"samsung,lpm_40nit_tx_cmds_rev%c"
"samsung,lpm_ctrl_alpm_tx_cmds_rev%c"
"samsung,lpm_ctrl_hlpm_tx_cmds_rev%c"
"samsung,lpm_ctrl_alpm_2nit_tx_cmds_rev%c"
"samsung,lpm_ctrl_alpm_40nit_tx_cmds_rev%c"
"samsung,lpm_ctrl_hlpm_2nit_tx_cmds_rev%c"
"samsung,lpm_ctrl_hlpm_40nit_tx_cmds_rev%c"
"samsung,lpm_1hz_tx_cmds_rev%c"
"samsung,lpm_2hz_tx_cmds_rev%c"
"samsung,lpm_hz_none_tx_cmds_rev%c"
"samsung,hmt_gamma_tx_cmds_rev%c"
"samsung,hmt_elvss_tx_cmds_rev%c"
"samsung,hmt_vint_tx_cmds_rev%c"
"samsung,hmt_enable_tx_cmds_rev%c"
"samsung,hmt_disable_tx_cmds_rev%c"
"samsung,hmt_reverse_enable_tx_cmds_rev%c"
"samsung,hmt_reverse_disable_tx_cmds_rev%c"
"samsung,hmt_aid_tx_cmds_rev%c"
"samsung,hmt_reverse_aid_map_table_rev%c"
"samsung,hmt_150cd_rx_cmds_rev%c"
"samsung,hmt_candela_map_table_rev%c"
"samsung,ldi_fps_change_tx_cmds_rev%c"
"samsung,ldi_fps_rx_cmds_rev%c"
"samsung,tft_pwm_tx_cmds_rev%c"
"samsung,scaled_level_map_table_rev%c"
"samsung,packet_size_tx_cmds_rev%c"
"samsung,reg_read_pos_tx_cmds_rev%c"
"samsung,osc_te_fitting_tx_cmds_rev%c"
"samsung,panel_ldi_vdd_read_cmds_rev%c"
"samsung,panel_ldi_vddm_read_cmds_rev%c"
"samsung,panel_ldi_vdd_offset_write_cmds_rev%c"
"samsung,panel_ldi_vddm_offset_write_cmds_rev%c"
"samsung,cabc_on_tx_cmds_rev%c"
"samsung,cabc_off_tx_cmds_rev%c"
"samsung,cabc_on_duty_tx_cmds_rev%c"
"samsung,cabc_off_duty_tx_cmds_rev%c"
"samsung,pre_caps_setting_tx_cmds_revA%c"
"samsung,caps_setting_tx_cmds_rev%c"
"samsung,caps_map_table_rev%c"
"samsung,upi_clk_change_tx_cmds_rev%c"
*************************************************************/
static void parse_dt_data(struct device_node *np,
void *data, char *cmd_string, char panel_rev, void *fnc)
{
char string[PARSE_STRING];
int ret = 0;
if (IS_ERR_OR_NULL(np) || IS_ERR_OR_NULL(data) || IS_ERR_OR_NULL(cmd_string)) {
LCD_ERR("Invalid data, np : 0x%zx data : 0x%zx cmd_string : 0x%zx\n",
(size_t)np, (size_t)data, (size_t)cmd_string);
return;
}
/* Generate string to parsing from DT */
snprintf(string, PARSE_STRING, "%s%c", cmd_string, 'A' + panel_rev);
if (fnc == mdss_samsung_parse_panel_table) {
ret = mdss_samsung_parse_panel_table(np,
(struct cmd_map *)data, string);
if (ret && (panel_rev > 0))
memcpy(data, (struct cmd_map *)data - 1, sizeof(struct cmd_map));
} else if (fnc == mdss_samsung_parse_candella_lux_mapping_table) {
ret = mdss_samsung_parse_candella_lux_mapping_table(np,
(struct candella_lux_map *)data, string);
if (ret && (panel_rev > 0))
memcpy(data, (struct candella_lux_map *)data - 1, sizeof(struct candella_lux_map));
} else if (fnc == mdss_samsung_parse_hbm_candella_lux_mapping_table) {
ret = mdss_samsung_parse_hbm_candella_lux_mapping_table(np,
(struct hbm_candella_lux_map *)data, string);
if (ret && (panel_rev > 0))
memcpy(data, (struct hbm_candella_lux_map *)data - 1, sizeof(struct hbm_candella_lux_map));
} else if ((fnc == mdss_samsung_parse_dcs_cmds) || (fnc == NULL)) {
ret = mdss_samsung_parse_dcs_cmds(np,
(struct dsi_panel_cmds *)data, string, NULL);
if (ret && (panel_rev > 0))
memcpy(data, (struct dsi_panel_cmds *)data - 1, sizeof(struct dsi_panel_cmds));
}
}
void mdss_samsung_panel_parse_dt_cmds(struct device_node *np,
struct mdss_dsi_ctrl_pdata *ctrl)
{
int panel_rev;
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
struct samsung_display_dtsi_data *dtsi_data = NULL;
/* At this time ctrl->ndx is not set */
int ndx = ctrl->panel_data.panel_info.pdest;
static int parse_dt_cmds_cnt = 0;
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx\n",
(size_t)ctrl, (size_t)vdd);
return;
}
LCD_INFO("DSI%d", ndx);
if (vdd->support_hall_ic) {
if (!parse_dt_cmds_cnt) {
ndx = DSI_CTRL_0;
parse_dt_cmds_cnt = 1;
} else
ndx = DSI_CTRL_1;
}
dtsi_data = &vdd->dtsi_data[ndx];
for (panel_rev = 0; panel_rev < SUPPORT_PANEL_REVISION; panel_rev++) {
parse_dt_data(np, &dtsi_data->display_on_tx_cmds[panel_rev],
"samsung,display_on_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->display_off_tx_cmds[panel_rev],
"samsung,display_off_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->esd_recovery_1_tx_cmds[panel_rev],
"samsung,esd_recovery_1_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->esd_recovery_2_tx_cmds[panel_rev],
"samsung,esd_recovery_2_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->level1_key_enable_tx_cmds[panel_rev],
"samsung,level1_key_enable_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->level1_key_disable_tx_cmds[panel_rev],
"samsung,level1_key_disable_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hsync_on_tx_cmds[panel_rev],
"samsung,hsync_on_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->level2_key_enable_tx_cmds[panel_rev],
"samsung,level2_key_enable_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->level2_key_disable_tx_cmds[panel_rev],
"samsung,level2_key_disable_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->smart_dimming_mtp_rx_cmds[panel_rev],
"samsung,smart_dimming_mtp_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->manufacture_read_pre_tx_cmds[panel_rev],
"samsung,manufacture_read_pre_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->manufacture_id_rx_cmds[panel_rev],
"samsung,manufacture_id_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->manufacture_id0_rx_cmds[panel_rev],
"samsung,manufacture_id0_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->manufacture_id1_rx_cmds[panel_rev],
"samsung,manufacture_id1_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->manufacture_id2_rx_cmds[panel_rev],
"samsung,manufacture_id2_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->manufacture_date_rx_cmds[panel_rev],
"samsung,manufacture_date_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->ddi_id_rx_cmds[panel_rev],
"samsung,ddi_id_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->cell_id_rx_cmds[panel_rev],
"samsung,cell_id_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->rddpm_rx_cmds[panel_rev],
"samsung,rddpm_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->mtp_read_sysfs_rx_cmds[panel_rev],
"samsung,mtp_read_sysfs_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->vint_tx_cmds[panel_rev],
"samsung,vint_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->vint_map_table[panel_rev],
"samsung,vint_map_table_rev", panel_rev,
mdss_samsung_parse_panel_table); /* VINT TABLE */
parse_dt_data(np, &dtsi_data->acl_off_tx_cmds[panel_rev],
"samsung,acl_off_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->acl_map_table[panel_rev],
"samsung,acl_map_table_rev", panel_rev,
mdss_samsung_parse_panel_table); /* ACL TABLE */
parse_dt_data(np, &dtsi_data->candela_map_table[panel_rev],
"samsung,candela_map_table_rev", panel_rev,
mdss_samsung_parse_candella_lux_mapping_table);
parse_dt_data(np, &dtsi_data->hbm_candela_map_table[panel_rev],
"samsung,hbm_candela_map_table_rev", panel_rev,
mdss_samsung_parse_hbm_candella_lux_mapping_table);
parse_dt_data(np, &dtsi_data->acl_percent_tx_cmds[panel_rev],
"samsung,acl_percent_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->acl_on_tx_cmds[panel_rev],
"samsung,acl_on_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->acl_pre_percent_tx_cmds[panel_rev],
"samsung,acl_pre_percent_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->gamma_tx_cmds[panel_rev],
"samsung,gamma_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->elvss_rx_cmds[panel_rev],
"samsung,elvss_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->elvss_tx_cmds[panel_rev],
"samsung,elvss_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->elvss_map_table[panel_rev],
"samsung,elvss_map_table_rev", panel_rev,
mdss_samsung_parse_panel_table); /* ELVSS TABLE */
parse_dt_data(np, &dtsi_data->elvss_pre_tx_cmds[panel_rev],
"samsung,elvss_pre_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->aid_map_table[panel_rev],
"samsung,aid_map_table_rev", panel_rev,
mdss_samsung_parse_panel_table); /* AID TABLE */
parse_dt_data(np, &dtsi_data->aid_tx_cmds[panel_rev],
"samsung,aid_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->aid_subdivision_tx_cmds[panel_rev],
"samsung,aid_subdivision_tx_cmds_rev", panel_rev, NULL);
/* CONFIG_HBM_RE */
parse_dt_data(np, &dtsi_data->hbm_rx_cmds[panel_rev],
"samsung,hbm_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hbm2_rx_cmds[panel_rev],
"samsung,hbm2_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hbm_gamma_tx_cmds[panel_rev],
"samsung,hbm_gamma_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hbm_etc_tx_cmds[panel_rev],
"samsung,hbm_etc_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hbm_off_tx_cmds[panel_rev],
"samsung,hbm_off_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hbm_etc2_tx_cmds[panel_rev],
"samsung,hbm_etc2_tx_cmds_rev", panel_rev, NULL);
/* CCB */
parse_dt_data(np, &dtsi_data->ccb_on_tx_cmds[panel_rev],
"samsung,ccb_on_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->ccb_off_tx_cmds[panel_rev],
"samsung,ccb_off_tx_cmds_rev", panel_rev, NULL);
/* UPI CLK */
parse_dt_data(np, &dtsi_data->upi_clk_change_tx_cmds[panel_rev],
"samsung,upi_clk_change_tx_cmds_rev", panel_rev, NULL);
/* IRC */
parse_dt_data(np, &dtsi_data->irc_tx_cmds[panel_rev],
"samsung,irc_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hbm_irc_tx_cmds[panel_rev],
"samsung,hbm_irc_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->irc_off_tx_cmds[panel_rev],
"samsung,irc_off_tx_cmds_rev", panel_rev, NULL);
/* MCD */
parse_dt_data(np, &dtsi_data->mcd_on_tx_cmds[panel_rev],
"samsung,mcd_on_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->mcd_off_tx_cmds[panel_rev],
"samsung,mcd_off_tx_cmds_rev", panel_rev, NULL);
/* CONFIG_TCON_MDNIE_LITE */
parse_dt_data(np, &dtsi_data->mdnie_read_rx_cmds[panel_rev],
"samsung,mdnie_read_rx_cmds_rev", panel_rev, NULL);
/* CONFIG_DEBUG_LDI_STATUS */
parse_dt_data(np, &dtsi_data->ldi_debug0_rx_cmds[panel_rev],
"samsung,ldi_debug0_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->ldi_debug1_rx_cmds[panel_rev],
"samsung,ldi_debug1_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->ldi_debug2_rx_cmds[panel_rev],
"samsung,ldi_debug2_rx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->ldi_loading_det_rx_cmds[panel_rev],
"samsung,ldi_loading_det_rx_cmds_rev", panel_rev, NULL);
/* CONFIG_TEMPERATURE_ELVSS */
parse_dt_data(np, &dtsi_data->elvss_lowtemp_tx_cmds[panel_rev],
"samsung,elvss_lowtemp_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->elvss_lowtemp2_tx_cmds[panel_rev],
"samsung,elvss_lowtemp2_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->smart_acl_elvss_lowtemp_tx_cmds[panel_rev],
"samsung,smart_acl_elvss_lowtemp_tx_cmds_rev", panel_rev, NULL);
/* CONFIG_SMART_ACL */
parse_dt_data(np, &dtsi_data->smart_acl_elvss_tx_cmds[panel_rev],
"samsung,smart_acl_elvss_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->smart_acl_elvss_map_table[panel_rev],
"samsung,smart_acl_elvss_map_table_rev", panel_rev,
mdss_samsung_parse_panel_table); /* TABLE */
/* CONFIG_CAPS*/
parse_dt_data(np, &dtsi_data->pre_caps_setting_tx_cmds[panel_rev],
"samsung,pre_caps_setting_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->caps_setting_tx_cmds[panel_rev],
"samsung,caps_setting_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->caps_map_table[panel_rev],
"samsung,caps_map_table_rev", panel_rev,
mdss_samsung_parse_panel_table); /* TABLE */
/* CONFIG_PARTIAL_UPDATE */
parse_dt_data(np, &dtsi_data->partial_display_on_tx_cmds[panel_rev],
"samsung,partial_display_on_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->partial_display_off_tx_cmds[panel_rev],
"samsung,partial_display_off_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->partial_display_column_row_tx_cmds[panel_rev],
"samsung,partial_display_column_row_tx_cmds_rev", panel_rev, NULL);
/* CONFIG_ALPM_MODE */
parse_dt_data(np, &dtsi_data->alpm_on_tx_cmds[panel_rev],
"samsung,alpm_on_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->alpm_off_tx_cmds[panel_rev],
"samsung,alpm_off_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_2nit_tx_cmds[panel_rev],
"samsung,lpm_2nit_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_40nit_tx_cmds[panel_rev],
"samsung,lpm_40nit_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_ctrl_alpm_2nit_tx_cmds[panel_rev],
"samsung,lpm_ctrl_alpm_2nit_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_ctrl_alpm_40nit_tx_cmds[panel_rev],
"samsung,lpm_ctrl_alpm_40nit_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_ctrl_hlpm_2nit_tx_cmds[panel_rev],
"samsung,lpm_ctrl_hlpm_2nit_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_ctrl_hlpm_40nit_tx_cmds[panel_rev],
"samsung,lpm_ctrl_hlpm_40nit_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_ctrl_alpm_tx_cmds[panel_rev],
"samsung,lpm_ctrl_alpm_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_ctrl_hlpm_tx_cmds[panel_rev],
"samsung,lpm_ctrl_hlpm_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_1hz_tx_cmds[panel_rev],
"samsung,lpm_1hz_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_2hz_tx_cmds[panel_rev],
"samsung,lpm_2hz_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_hz_none_tx_cmds[panel_rev],
"samsung,lpm_hz_none_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_aod_on_tx_cmds[panel_rev],
"samsung,lpm_ctrl_alpm_aod_on_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->lpm_aod_off_tx_cmds[panel_rev],
"samsung,lpm_ctrl_alpm_aod_off_tx_cmds_rev", panel_rev, NULL);
/* HMT */
parse_dt_data(np, &dtsi_data->hmt_gamma_tx_cmds[panel_rev],
"samsung,hmt_gamma_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hmt_elvss_tx_cmds[panel_rev],
"samsung,hmt_elvss_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hmt_vint_tx_cmds[panel_rev],
"samsung,hmt_vint_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hmt_enable_tx_cmds[panel_rev],
"samsung,hmt_enable_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hmt_disable_tx_cmds[panel_rev],
"samsung,hmt_disable_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hmt_reverse_tx_cmds[panel_rev],
"samsung,hmt_reverse_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hmt_forward_tx_cmds[panel_rev],
"samsung,hmt_forward_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hmt_aid_tx_cmds[panel_rev],
"samsung,hmt_aid_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->hmt_reverse_aid_map_table[panel_rev],
"samsung,hmt_reverse_aid_map_table_rev", panel_rev,
mdss_samsung_parse_panel_table); /* TABLE */
parse_dt_data(np, &dtsi_data->hmt_candela_map_table[panel_rev],
"samsung,hmt_candela_map_table_rev", panel_rev,
mdss_samsung_parse_candella_lux_mapping_table);
/* CONFIG_FPS_CHANGE */
parse_dt_data(np, &dtsi_data->ldi_fps_change_tx_cmds[panel_rev],
"samsung,ldi_fps_change_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->ldi_fps_rx_cmds[panel_rev],
"samsung,ldi_fps_rx_cmds_rev", panel_rev, NULL);
/* TFT PWM CONTROL */
parse_dt_data(np, &dtsi_data->tft_pwm_tx_cmds[panel_rev],
"samsung,tft_pwm_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->blic_dimming_cmds[panel_rev],
"samsung,blic_dimming_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->scaled_level_map_table[panel_rev],
"samsung,scaled_level_map_table_rev", panel_rev,
mdss_samsung_parse_candella_lux_mapping_table);
/* Additional Command */
parse_dt_data(np, &dtsi_data->packet_size_tx_cmds[panel_rev],
"samsung,packet_size_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->reg_read_pos_tx_cmds[panel_rev],
"samsung,reg_read_pos_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->osc_te_fitting_tx_cmds[panel_rev],
"samsung,osc_te_fitting_tx_cmds_rev", panel_rev, NULL);
/* samsung,avc_on */
parse_dt_data(np, &dtsi_data->avc_on_tx_cmds[panel_rev],
"samsung,avc_on_rev", panel_rev, NULL);
/* VDDM OFFSET */
parse_dt_data(np, &dtsi_data->read_vdd_ref_cmds[panel_rev],
"samsung,panel_ldi_vdd_read_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->read_vddm_ref_cmds[panel_rev],
"samsung,panel_ldi_vddm_read_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->write_vdd_offset_cmds[panel_rev],
"samsung,panel_ldi_vdd_offset_write_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->write_vddm_offset_cmds[panel_rev],
"samsung,panel_ldi_vddm_offset_write_cmds_rev", panel_rev, NULL);
/* TFT CABC CONTROL */
parse_dt_data(np, &dtsi_data->cabc_on_tx_cmds[panel_rev],
"samsung,cabc_on_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->cabc_off_tx_cmds[panel_rev],
"samsung,cabc_off_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->cabc_on_duty_tx_cmds[panel_rev],
"samsung,cabc_on_duty_tx_cmds_rev", panel_rev, NULL);
parse_dt_data(np, &dtsi_data->cabc_off_duty_tx_cmds[panel_rev],
"samsung,cabc_off_duty_tx_cmds_rev", panel_rev, NULL);
/* SPI ENABLE */
parse_dt_data(np, &dtsi_data->spi_enable_tx_cmds[panel_rev],
"samsung,spi_enable_tx_cmds_rev", panel_rev, NULL);
/* GRADUAL ACL ON/OFF */
parse_dt_data(np, &dtsi_data->gradual_acl_tx_cmds[panel_rev],
"samsung,gradual_acl_tx_cmds_rev", panel_rev, NULL);
/* H/W CURSOR */
parse_dt_data(np, &dtsi_data->hw_cursor_tx_cmds[panel_rev],
"samsung,hw_cursor_tx_cmds_rev", panel_rev, NULL);
}
}
void mdss_samsung_check_hw_config(struct platform_device *pdev)
{
struct mdss_dsi_data *dsi_res = platform_get_drvdata(pdev);
struct dsi_shared_data *sdata;
struct samsung_display_driver_data *vdd;
if (!dsi_res)
return;
vdd = check_valid_ctrl(dsi_res->ctrl_pdata[DSI_CTRL_0]);
sdata = dsi_res->shared_data;
if (!vdd || sdata)
return;
sdata->hw_config = vdd->samsung_hw_config;
LCD_INFO("DSI h/w configuration is %d\n",
sdata->hw_config);
}
void mdss_samsung_panel_pbaboot_config(struct device_node *np,
struct mdss_dsi_ctrl_pdata *ctrl)
{
struct mdss_panel_info *pinfo = NULL;
struct mdss_debug_data *mdd =
(struct mdss_debug_data *)((mdss_mdp_get_mdata())->debug_inf.debug_data);
struct samsung_display_driver_data *vdd = NULL;
bool need_to_force_vidoe_mode = false;
if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(mdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx, mdd : 9x%zx\n",
(size_t)ctrl, (size_t)mdd);
return;
}
pinfo = &ctrl->panel_data.panel_info;
vdd = check_valid_ctrl(ctrl);
if (vdd->support_hall_ic) {
/* check the lcd id for DISPLAY_1 and DISPLAY_2 */
if (!mdss_panel_attached(DISPLAY_1) && !mdss_panel_attached(DISPLAY_2))
need_to_force_vidoe_mode = true;
} else {
/* check the lcd id for DISPLAY_1 */
if (!mdss_panel_attached(DISPLAY_1))
need_to_force_vidoe_mode = true;
}
/* Support PBA boot without lcd */
if (need_to_force_vidoe_mode &&
!IS_ERR_OR_NULL(pinfo) &&
!IS_ERR_OR_NULL(vdd) &&
(pinfo->mipi.mode == DSI_CMD_MODE)) {
LCD_ERR("force VIDEO_MODE : %d\n", ctrl->ndx);
pinfo->type = MIPI_VIDEO_PANEL;
pinfo->mipi.mode = DSI_VIDEO_MODE;
pinfo->mipi.traffic_mode = DSI_BURST_MODE;
pinfo->mipi.bllp_power_stop = true;
pinfo->mipi.te_sel = 0;
pinfo->mipi.vsync_enable = 0;
pinfo->mipi.hw_vsync_mode = 0;
pinfo->mipi.force_clk_lane_hs = true;
pinfo->mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
pinfo->cont_splash_enabled = false;
pinfo->mipi.lp11_init = false;
vdd->support_mdnie_lite = false;
vdd->support_mdnie_trans_dimming = false;
vdd->mdnie_disable_trans_dimming = false;
if (!IS_ERR_OR_NULL(vdd->panel_func.parsing_otherline_pdata) && mdss_panel_attached(DISPLAY_1)) {
vdd->panel_func.parsing_otherline_pdata = NULL;
destroy_workqueue(vdd_data.other_line_panel_support_workq);
}
pinfo->esd_check_enabled = false;
ctrl->on_cmds.link_state = DSI_LP_MODE;
ctrl->off_cmds.link_state = DSI_LP_MODE;
/* To avoid underrun panic*/
mdd->logd.xlog_enable = 0;
vdd->dtsi_data[ctrl->ndx].samsung_osc_te_fitting = false;
}
}
void mdss_samsung_panel_parse_dt_esd(struct device_node *np,
struct mdss_dsi_ctrl_pdata *ctrl)
{
int rc = 0;
const char *data;
struct samsung_display_driver_data *vdd = NULL;
if (!ctrl) {
LCD_ERR("ctrl is null\n");
return;
}
vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data vdd : 0x%zx\n", (size_t)vdd);
return;
}
vdd->esd_recovery.esd_gpio = of_get_named_gpio(np, "qcom,esd_irq_gpio", 0);
if (gpio_is_valid(vdd->esd_recovery.esd_gpio)) {
LCD_INFO("esd gpio : %d, irq : %d\n",
vdd->esd_recovery.esd_gpio,
gpio_to_irq(vdd->esd_recovery.esd_gpio));
}
rc = of_property_read_string(np, "qcom,mdss-dsi-panel-status-check-mode", &data);
if (!rc) {
if (!strcmp(data, "reg_read_irq")) {
ctrl->status_mode = ESD_REG_IRQ;
ctrl->status_cmds_rlen = 0;
ctrl->check_read_status = mdss_dsi_esd_irq_status;
}
}
rc = of_property_read_string(np, "qcom,mdss-dsi-panel-status-irq-trigger", &data);
if (!rc) {
vdd->esd_recovery.irqflags = IRQF_ONESHOT | IRQF_NO_SUSPEND;;
if (!strcmp(data, "rising"))
vdd->esd_recovery.irqflags |= IRQF_TRIGGER_RISING;
else if (!strcmp(data, "falling"))
vdd->esd_recovery.irqflags |= IRQF_TRIGGER_FALLING;
else if (!strcmp(data, "high"))
vdd->esd_recovery.irqflags |= IRQF_TRIGGER_HIGH;
else if (!strcmp(data, "low"))
vdd->esd_recovery.irqflags |= IRQF_TRIGGER_LOW;
}
}
void mdss_samsung_panel_parse_dt(struct device_node *np,
struct mdss_dsi_ctrl_pdata *ctrl)
{
int rc, i;
u32 tmp[2];
char panel_extra_power_gpio[] = "samsung,panel-extra-power-gpio1";
char backlight_tft_gpio[] = "samsung,panel-backlight-tft-gpio1";
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
const char *data;
const __be32 *data_32;
rc = of_property_read_u32(np, "samsung,support_panel_max", tmp);
vdd->support_panel_max = !rc ? tmp[0] : 1;
/* Set LP11 init flag */
vdd->dtsi_data[ctrl->ndx].samsung_lp11_init = of_property_read_bool(np, "samsung,dsi-lp11-init");
LCD_ERR("LP11 init %s\n",
vdd->dtsi_data[ctrl->ndx].samsung_lp11_init ? "enabled" : "disabled");
rc = of_property_read_u32(np, "samsung,mdss-power-on-reset-delay-us", tmp);
vdd->dtsi_data[ctrl->ndx].samsung_power_on_reset_delay = (!rc ? tmp[0] : 0);
rc = of_property_read_u32(np, "samsung,mdss-power-on-delay-us", tmp);
vdd->dtsi_data[ctrl->ndx].samsung_power_on_delay = (!rc ? tmp[0] : 0);
rc = of_property_read_u32(np, "samsung,mdss-power-off-delay-us", tmp);
vdd->dtsi_data[ctrl->ndx].samsung_power_off_delay = (!rc ? tmp[0] : 0);
rc = of_property_read_u32(np, "samsung,mdss-dsi-off-reset-delay-us", tmp);
vdd->dtsi_data[ctrl->ndx].samsung_dsi_off_reset_delay = (!rc ? tmp[0] : 0);
/* Set esc clk 128M */
vdd->dtsi_data[ctrl->ndx].samsung_esc_clk_128M = of_property_read_bool(np, "samsung,esc-clk-128M");
LCD_ERR("ESC CLK 128M %s\n",
vdd->dtsi_data[ctrl->ndx].samsung_esc_clk_128M ? "enabled" : "disabled");
vdd->dtsi_data[ctrl->ndx].panel_lpm_enable = of_property_read_bool(np, "samsung,panel-lpm-enable");
LCD_ERR("alpm enable %s\n",
vdd->dtsi_data[ctrl->ndx].panel_lpm_enable ? "enabled" : "disabled");
/* Set HALL IC */
vdd->support_hall_ic = of_property_read_bool(np, "samsung,mdss_dsi_hall_ic_enable");
LCD_ERR("hall_ic %s\n", vdd->support_hall_ic ? "enabled" : "disabled");
/*Set OSC TE fitting flag */
vdd->dtsi_data[ctrl->ndx].samsung_osc_te_fitting =
of_property_read_bool(np, "samsung,osc-te-fitting-enable");
if (vdd->dtsi_data[ctrl->ndx].samsung_osc_te_fitting) {
rc = of_property_read_u32_array(np, "samsung,osc-te-fitting-cmd-index", tmp, 2);
if (!rc) {
vdd->dtsi_data[ctrl->ndx].samsung_osc_te_fitting_cmd_index[0] =
tmp[0];
vdd->dtsi_data[ctrl->ndx].samsung_osc_te_fitting_cmd_index[1] =
tmp[1];
}
rc = of_property_read_u32(np, "samsung,osc-te-fitting-sampling-rate", tmp);
vdd->te_fitting_info.sampling_rate = !rc ? tmp[0] : 2;
}
LCD_INFO("OSC TE fitting %s\n",
vdd->dtsi_data[0].samsung_osc_te_fitting ? "enabled" : "disabled");
/* Set HMT flag */
vdd->dtsi_data[0].hmt_enabled = of_property_read_bool(np, "samsung,hmt_enabled");
if (vdd->dtsi_data[0].hmt_enabled)
for (i = 1; i < vdd->support_panel_max; i++)
vdd->dtsi_data[i].hmt_enabled = true;
LCD_INFO("hmt %s\n",
vdd->dtsi_data[0].hmt_enabled ? "enabled" : "disabled");
/* TCON Clk On Support */
vdd->dtsi_data[ctrl->ndx].samsung_tcon_clk_on_support = of_property_read_bool(np,
"samsung,tcon-clk-on-support");
LCD_INFO("Tcon Clk On Suppor %s\n",
vdd->dtsi_data[ctrl->ndx].samsung_tcon_clk_on_support ? "enabled" : "disabled");
/* Set TFT flag */
vdd->mdnie_tuning_enable_tft = of_property_read_bool(np,
"samsung,mdnie-tuning-enable-tft");
vdd->dtsi_data[ctrl->ndx].tft_common_support = of_property_read_bool(np,
"samsung,tft-common-support");
LCD_INFO("tft_common_support %s\n",
vdd->dtsi_data[ctrl->ndx].tft_common_support ? "enabled" : "disabled");
vdd->dtsi_data[ctrl->ndx].tft_module_name = of_get_property(np,
"samsung,tft-module-name", NULL); /* for tft tablet */
vdd->dtsi_data[ctrl->ndx].panel_vendor = of_get_property(np,
"samsung,panel-vendor", NULL);
vdd->dtsi_data[ctrl->ndx].backlight_gpio_config = of_property_read_bool(np,
"samsung,backlight-gpio-config");
LCD_INFO("backlight_gpio_config %s\n",
vdd->dtsi_data[ctrl->ndx].backlight_gpio_config ? "enabled" : "disabled");
/* Factory Panel Swap*/
vdd->dtsi_data[ctrl->ndx].samsung_support_factory_panel_swap = of_property_read_bool(np,
"samsung,support_factory_panel_swap");
/* Set extra power gpio */
for (i = 0; i < MAX_EXTRA_POWER_GPIO; i++) {
panel_extra_power_gpio[strlen(panel_extra_power_gpio) - 1] = '1' + i;
vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i] =
of_get_named_gpio(np,
panel_extra_power_gpio, 0);
if (!gpio_is_valid(vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i]))
LCD_ERR("%d, panel_extra_power gpio%d not specified\n",
__LINE__, i+1);
else
LCD_ERR("extra gpio num : %d\n", vdd->dtsi_data[ctrl->ndx].panel_extra_power_gpio[i]);
}
/* Set tft backlight gpio */
for (i = 0; i < MAX_BACKLIGHT_TFT_GPIO; i++) {
backlight_tft_gpio[strlen(backlight_tft_gpio) - 1] = '1' + i;
vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i] =
of_get_named_gpio(np,
backlight_tft_gpio, 0);
if (!gpio_is_valid(vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i]))
LCD_ERR("%d, backlight_tft_gpio gpio%d not specified\n",
__LINE__, i+1);
else
LCD_ERR("tft gpio num : %d\n", vdd->dtsi_data[ctrl->ndx].backlight_tft_gpio[i]);
}
/* Set Mdnie lite HBM_CE_TEXT_MDNIE mode used */
vdd->dtsi_data[ctrl->ndx].hbm_ce_text_mode_support = of_property_read_bool(np, "samsung,hbm_ce_text_mode_support");
/* Set Backlight IC discharge time */
rc = of_property_read_u32(np, "samsung,blic-discharging-delay-us", tmp);
vdd->dtsi_data[ctrl->ndx].blic_discharging_delay_tft = (!rc ? tmp[0] : 6);
/* Set cabc delay time */
rc = of_property_read_u32(np, "samsung,cabc-delay-us", tmp);
vdd->dtsi_data[ctrl->ndx].cabc_delay = (!rc ? tmp[0] : 6);
/* Change hw_config to support single, dual and split dsi on runtime */
data = of_get_property(np, "samsung,hw-config", NULL);
if (data) {
if (!strcmp(data, "dual_dsi"))
vdd->samsung_hw_config = DUAL_DSI;
else if (!strcmp(data, "split_dsi"))
vdd->samsung_hw_config = SPLIT_DSI;
else if (!strcmp(data, "single_dsi"))
vdd->samsung_hw_config = SINGLE_DSI;
}
/* Set IRC */
vdd->samsung_support_irc = of_property_read_bool(np, "samsung,support_irc");
/* Set elvss_interpolation_temperature */
data_32 = of_get_property(np, "samsung,elvss_interpolation_temperature", NULL);
if (data_32)
vdd->elvss_interpolation_temperature = (int)(be32_to_cpup(data_32));
else
vdd->elvss_interpolation_temperature = ELVSS_INTERPOLATION_TEMPERATURE;
/* Set lux value for mdnie HBM */
data_32 = of_get_property(np, "samsung,enter_hbm_lux", NULL);
if (data_32)
vdd->enter_hbm_lux = (int)(be32_to_cpup(data_32));
else
vdd->enter_hbm_lux = ENTER_HBM_LUX;
mdss_samsung_panel_parse_dt_cmds(np, ctrl);
if (vdd->support_hall_ic) {
vdd->hall_ic_notifier_display.priority = 0; /* Tsp is 1, Touch key is 2 */
vdd->hall_ic_notifier_display.notifier_call = samsung_display_hall_ic_status;
/* hall_ic_register_notify(&vdd->hall_ic_notifier_display); */
mdss_samsung_panel_parse_dt_cmds(np, ctrl);
vdd->mdnie_tune_state_dsi[DISPLAY_2] = init_dsi_tcon_mdnie_class(DISPLAY_2, vdd);
}
mdss_samsung_panel_parse_dt_esd(np, ctrl);
mdss_samsung_panel_pbaboot_config(np, ctrl);
}
/************************************************************
*
* SYSFS RELATED FUNCTION
*
**************************************************************/
#if defined(CONFIG_LCD_CLASS_DEVICE)
static char char_to_dec(char data1, char data2)
{
char dec;
dec = 0;
if (data1 >= 'a') {
data1 -= 'a';
data1 += 10;
} else if (data1 >= 'A') {
data1 -= 'A';
data1 += 10;
} else
data1 -= '0';
dec = data1 << 4;
if (data2 >= 'a') {
data2 -= 'a';
data2 += 10;
} else if (data2 >= 'A') {
data2 -= 'A';
data2 += 10;
} else
data2 -= '0';
dec |= data2;
return dec;
}
static void sending_tune_cmd(struct device *dev, char *src, int len)
{
int data_pos;
int cmd_step;
int cmd_pos;
char *mdnie_tuning1;
char *mdnie_tuning2;
char *mdnie_tuning3;
char *mdnie_tuning4;
char *mdnie_tuning5;
char *mdnie_tuning6;
struct dsi_cmd_desc *mdnie_tune_cmd;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd\n");
return;
}
if (!vdd->mdnie_tune_size1 || !vdd->mdnie_tune_size2 || !vdd->mdnie_tune_size3) {
LCD_ERR("mdnie_tune_size is zero 1(%d) 2(%d) 3(%d)\n",
vdd->mdnie_tune_size1, vdd->mdnie_tune_size2, vdd->mdnie_tune_size3);
return;
}
if (vdd->mdnie_tuning_enable_tft) {
mdnie_tune_cmd = kzalloc(7 * sizeof(struct dsi_cmd_desc), GFP_KERNEL);
mdnie_tuning1 = kzalloc(sizeof(char) * vdd->mdnie_tune_size1, GFP_KERNEL);
mdnie_tuning2 = kzalloc(sizeof(char) * vdd->mdnie_tune_size2, GFP_KERNEL);
mdnie_tuning3 = kzalloc(sizeof(char) * vdd->mdnie_tune_size3, GFP_KERNEL);
mdnie_tuning4 = kzalloc(sizeof(char) * vdd->mdnie_tune_size4, GFP_KERNEL);
mdnie_tuning5 = kzalloc(sizeof(char) * vdd->mdnie_tune_size5, GFP_KERNEL);
mdnie_tuning6 = kzalloc(sizeof(char) * vdd->mdnie_tune_size6, GFP_KERNEL);
} else {
mdnie_tune_cmd = kzalloc(3 * sizeof(struct dsi_cmd_desc), GFP_KERNEL);
mdnie_tuning1 = kzalloc(sizeof(char) * vdd->mdnie_tune_size1, GFP_KERNEL);
mdnie_tuning2 = kzalloc(sizeof(char) * vdd->mdnie_tune_size2, GFP_KERNEL);
mdnie_tuning3 = kzalloc(sizeof(char) * vdd->mdnie_tune_size3, GFP_KERNEL);
}
cmd_step = 0;
cmd_pos = 0;
for (data_pos = 0; data_pos < len;) {
if (*(src + data_pos) == '0') {
if (*(src + data_pos + 1) == 'x') {
if (!cmd_step)
mdnie_tuning1[cmd_pos] = char_to_dec(*(src + data_pos + 2), *(src + data_pos + 3));
else if (cmd_step == 1)
mdnie_tuning2[cmd_pos] = char_to_dec(*(src + data_pos + 2), *(src + data_pos + 3));
else if (cmd_step == 2 && vdd->mdnie_tuning_enable_tft)
mdnie_tuning3[cmd_pos] = char_to_dec(*(src + data_pos + 2), *(src + data_pos + 3));
else if (cmd_step == 3 && vdd->mdnie_tuning_enable_tft)
mdnie_tuning4[cmd_pos] = char_to_dec(*(src + data_pos + 2), *(src + data_pos + 3));
else if (cmd_step == 4 && vdd->mdnie_tuning_enable_tft)
mdnie_tuning5[cmd_pos] = char_to_dec(*(src + data_pos + 2), *(src + data_pos + 3));
else if (cmd_step == 5 && vdd->mdnie_tuning_enable_tft)
mdnie_tuning6[cmd_pos] = char_to_dec(*(src + data_pos + 2), *(src + data_pos + 3));
else
mdnie_tuning3[cmd_pos] = char_to_dec(*(src + data_pos + 2), *(src + data_pos + 3));
data_pos += 3;
cmd_pos++;
if (cmd_pos == vdd->mdnie_tune_size1 && !cmd_step) {
cmd_pos = 0;
cmd_step = 1;
} else if ((cmd_pos == vdd->mdnie_tune_size2) && (cmd_step == 1)) {
cmd_pos = 0;
cmd_step = 2;
} else if ((cmd_pos == vdd->mdnie_tune_size3) && (cmd_step == 2) && vdd->mdnie_tuning_enable_tft) {
cmd_pos = 0;
cmd_step = 3;
} else if ((cmd_pos == vdd->mdnie_tune_size4) && (cmd_step == 3) && vdd->mdnie_tuning_enable_tft) {
cmd_pos = 0;
cmd_step = 4;
} else if ((cmd_pos == vdd->mdnie_tune_size5) && (cmd_step == 4) && vdd->mdnie_tuning_enable_tft) {
cmd_pos = 0;
cmd_step = 5;
}
} else
data_pos++;
} else {
data_pos++;
}
}
mdnie_tune_cmd[0].dchdr.dtype = DTYPE_DCS_LWRITE;
mdnie_tune_cmd[0].dchdr.last = 1;
mdnie_tune_cmd[0].dchdr.dlen = vdd->mdnie_tune_size1;
mdnie_tune_cmd[0].payload = mdnie_tuning1;
mdnie_tune_cmd[1].dchdr.dtype = DTYPE_DCS_LWRITE;
mdnie_tune_cmd[1].dchdr.last = 1;
mdnie_tune_cmd[1].dchdr.dlen = vdd->mdnie_tune_size2;
mdnie_tune_cmd[1].payload = mdnie_tuning2;
mdnie_tune_cmd[2].dchdr.dtype = DTYPE_DCS_LWRITE;
mdnie_tune_cmd[2].dchdr.last = 1;
mdnie_tune_cmd[2].dchdr.dlen = vdd->mdnie_tune_size3;
mdnie_tune_cmd[2].payload = mdnie_tuning3;
printk(KERN_ERR "mdnie_tuning1 (%d)\n", vdd->mdnie_tune_size1);
for (data_pos = 0; data_pos < vdd->mdnie_tune_size1 ; data_pos++)
printk(KERN_ERR "0x%x \n", mdnie_tuning1[data_pos]);
printk(KERN_ERR "mdnie_tuning2 (%d)\n", vdd->mdnie_tune_size2);
for (data_pos = 0; data_pos < vdd->mdnie_tune_size2 ; data_pos++)
printk(KERN_ERR "0x%x \n", mdnie_tuning2[data_pos]);
printk(KERN_ERR "mdnie_tuning3 (%d)\n", vdd->mdnie_tune_size3);
for (data_pos = 0; data_pos < vdd->mdnie_tune_size3 ; data_pos++)
printk(KERN_ERR "0x%x \n", mdnie_tuning3[data_pos]);
if (vdd->mdnie_tuning_enable_tft) {
mdnie_tune_cmd[3].dchdr.dtype = DTYPE_DCS_LWRITE;
mdnie_tune_cmd[3].dchdr.last = 1;
mdnie_tune_cmd[3].dchdr.dlen = vdd->mdnie_tune_size4;
mdnie_tune_cmd[3].payload = mdnie_tuning4;
mdnie_tune_cmd[4].dchdr.dtype = DTYPE_DCS_LWRITE;
mdnie_tune_cmd[4].dchdr.last = 1;
mdnie_tune_cmd[4].dchdr.dlen = vdd->mdnie_tune_size5;
mdnie_tune_cmd[4].payload = mdnie_tuning5;
mdnie_tune_cmd[5].dchdr.dtype = DTYPE_DCS_LWRITE;
mdnie_tune_cmd[5].dchdr.last = 1;
mdnie_tune_cmd[5].dchdr.dlen = vdd->mdnie_tune_size6;
mdnie_tune_cmd[5].payload = mdnie_tuning6;
printk(KERN_ERR "\n");
for (data_pos = 0; data_pos < vdd->mdnie_tune_size3 ; data_pos++)
printk(KERN_ERR "0x%x ", mdnie_tuning3[data_pos]);
printk(KERN_ERR "\n");
for (data_pos = 0; data_pos < vdd->mdnie_tune_size4 ; data_pos++)
printk(KERN_ERR "0x%x ", mdnie_tuning4[data_pos]);
printk(KERN_ERR "\n");
for (data_pos = 0; data_pos < vdd->mdnie_tune_size5 ; data_pos++)
printk(KERN_ERR "0x%x ", mdnie_tuning5[data_pos]);
printk(KERN_ERR "\n");
for (data_pos = 0; data_pos < vdd->mdnie_tune_size6 ; data_pos++)
printk(KERN_ERR "0x%x ", mdnie_tuning6[data_pos]);
printk(KERN_ERR "\n");
}
if (IS_ERR_OR_NULL(vdd))
LCD_ERR("no vdd");
else {
if ((vdd->ctrl_dsi[DSI_CTRL_0]->cmd_sync_wait_broadcast)
&& (vdd->ctrl_dsi[DSI_CTRL_1]->cmd_sync_wait_trigger)) { /* Dual DSI & dsi 1 trigger */
if (!IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level1_key_enable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(vdd->ctrl_dsi[DSI_CTRL_1], PANEL_LEVE1_KEY_ENABLE);
if (!IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level2_key_enable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(vdd->ctrl_dsi[DSI_CTRL_1], PANEL_LEVE2_KEY_ENABLE);
/* Set default link_stats as DSI_HS_MODE for mdnie tune data */
vdd->mdnie_tune_data[DSI_CTRL_1].mdnie_tune_packet_tx_cmds_dsi.link_state = DSI_HS_MODE;
vdd->mdnie_tune_data[DSI_CTRL_1].mdnie_tune_packet_tx_cmds_dsi.cmds = mdnie_tune_cmd;
vdd->mdnie_tune_data[DSI_CTRL_1].mdnie_tune_packet_tx_cmds_dsi.cmd_cnt = 3;
mdss_samsung_send_cmd(vdd->ctrl_dsi[DSI_CTRL_1], PANEL_MDNIE_TUNE);
if (!IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level1_key_disable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(vdd->ctrl_dsi[DSI_CTRL_1], PANEL_LEVE1_KEY_DISABLE);
if (!IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level2_key_disable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(vdd->ctrl_dsi[DSI_CTRL_1], PANEL_LEVE2_KEY_DISABLE);
} else { /* Single DSI, dsi 0 trigger */
if (!IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level1_key_enable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(vdd->ctrl_dsi[DSI_CTRL_0], PANEL_LEVE1_KEY_ENABLE);
/* Set default link_stats as DSI_HS_MODE for mdnie tune data */
vdd->mdnie_tune_data[DSI_CTRL_0].mdnie_tune_packet_tx_cmds_dsi.link_state = DSI_HS_MODE;
vdd->mdnie_tune_data[DSI_CTRL_0].mdnie_tune_packet_tx_cmds_dsi.cmds = mdnie_tune_cmd;
vdd->mdnie_tune_data[DSI_CTRL_0].mdnie_tune_packet_tx_cmds_dsi.cmd_cnt = vdd->mdnie_tuning_enable_tft ? 6 : 3;
mdss_samsung_send_cmd(vdd->ctrl_dsi[DSI_CTRL_0], PANEL_MDNIE_TUNE);
if (!IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].level1_key_disable_tx_cmds[vdd->panel_revision].cmds))
mdss_samsung_send_cmd(vdd->ctrl_dsi[DSI_CTRL_0], PANEL_LEVE1_KEY_DISABLE);
}
}
if (vdd->mdnie_tuning_enable_tft) {
kfree(mdnie_tune_cmd);
kfree(mdnie_tuning1);
kfree(mdnie_tuning2);
kfree(mdnie_tuning3);
kfree(mdnie_tuning4);
kfree(mdnie_tuning5);
kfree(mdnie_tuning6);
} else {
kfree(mdnie_tune_cmd);
kfree(mdnie_tuning1);
kfree(mdnie_tuning2);
kfree(mdnie_tuning3);
}
}
static void load_tuning_file(struct device *dev, char *filename)
{
struct file *filp;
char *dp;
long l;
loff_t pos;
int ret;
mm_segment_t fs;
LCD_INFO("called loading file name : [%s]\n",
filename);
fs = get_fs();
set_fs(get_ds());
filp = filp_open(filename, O_RDONLY, 0);
if (IS_ERR(filp)) {
printk(KERN_ERR "%s File open failed\n", __func__);
goto err;
}
l = filp->f_path.dentry->d_inode->i_size;
LCD_INFO("Loading File Size : %ld(bytes)", l);
dp = kmalloc(l + 10, GFP_KERNEL);
if (dp == NULL) {
LCD_INFO("Can't not alloc memory for tuning file load\n");
filp_close(filp, current->files);
goto err;
}
pos = 0;
memset(dp, 0, l);
LCD_INFO("before vfs_read()\n");
ret = vfs_read(filp, (char __user *)dp, l, &pos);
LCD_INFO("after vfs_read()\n");
if (ret != l) {
LCD_INFO("vfs_read() filed ret : %d\n", ret);
kfree(dp);
filp_close(filp, current->files);
goto err;
}
filp_close(filp, current->files);
set_fs(fs);
sending_tune_cmd(dev, dp, l);
kfree(dp);
return;
err:
set_fs(fs);
}
static ssize_t tuning_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret = 0;
ret = snprintf(buf, MAX_FILE_NAME, "Tunned File Name : %s\n", tuning_file);
return ret;
}
static ssize_t tuning_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t size)
{
char *pt;
memset(tuning_file, 0, sizeof(tuning_file));
snprintf(tuning_file, MAX_FILE_NAME, "%s%s", TUNING_FILE_PATH, buf);
pt = tuning_file;
while (*pt) {
if (*pt == '\r' || *pt == '\n') {
*pt = 0;
break;
}
pt++;
}
LCD_INFO("%s\n", tuning_file);
load_tuning_file(dev, tuning_file);
return size;
}
static DEVICE_ATTR(tuning, 0664, tuning_show, tuning_store);
#endif
#define OTHER_PANEL_FILE "/efs/FactoryApp/a2_line.dat"
int read_line(char *src, char *buf, int *pos, int len)
{
int idx = 0;
LCD_DEBUG("(%d) ++\n", *pos);
while (*(src + *pos) != 10 && *(src + *pos) != 13) {
buf[idx] = *(src + *pos);
#if 0
LCD_ERR("%c (%3d) idx(%d)\n", buf[idx], buf[idx], idx);
#endif
idx++;
(*pos)++;
if (idx > MAX_READ_LINE_SIZE) {
LCD_ERR("overflow!\n");
return idx;
}
if (*pos >= len) {
LCD_ERR("End of File (%d) / (%d)\n", *pos, len);
return idx;
}
}
while (*(src + *pos) == 10 || *(src + *pos) == 13)
(*pos)++;
LCD_DEBUG("--\n");
return idx;
}
int mdss_samsung_read_otherline_panel_data(struct samsung_display_driver_data *vdd)
{
struct file *filp;
char *dp;
long l;
loff_t pos;
int ret = 0;
mm_segment_t fs;
fs = get_fs();
set_fs(get_ds());
filp = filp_open(OTHER_PANEL_FILE, O_RDONLY, 0);
if (IS_ERR(filp)) {
printk(KERN_ERR "%s File open failed\n", __func__);
if (!IS_ERR_OR_NULL(vdd->panel_func.set_panel_fab_type))
vdd->panel_func.set_panel_fab_type(BASIC_FB_PANLE_TYPE);/*to work as original line panel*/
ret = -ENOENT;
goto err;
}
l = filp->f_path.dentry->d_inode->i_size;
LCD_INFO("Loading File Size : %ld(bytes)", l);
dp = kmalloc(l + 10, GFP_KERNEL);
if (dp == NULL) {
LCD_INFO("Can't not alloc memory for tuning file load\n");
filp_close(filp, current->files);
ret = -1;
goto err;
}
pos = 0;
memset(dp, 0, l);
LCD_INFO("before vfs_read()\n");
ret = vfs_read(filp, (char __user *)dp, l, &pos);
LCD_INFO("after vfs_read()\n");
if (ret != l) {
LCD_INFO("vfs_read() filed ret : %d\n", ret);
kfree(dp);
filp_close(filp, current->files);
ret = -1;
goto err;
}
if (!IS_ERR_OR_NULL(vdd->panel_func.parsing_otherline_pdata))
ret = vdd->panel_func.parsing_otherline_pdata(filp, vdd, dp, l);
filp_close(filp, current->files);
set_fs(fs);
kfree(dp);
return ret;
err:
set_fs(fs);
return ret;
}
void read_panel_data_work_fn(struct delayed_work *work)
{
int ret = 1;
ret = mdss_samsung_read_otherline_panel_data(&vdd_data);
if (ret && vdd_data.other_line_panel_work_cnt) {
queue_delayed_work(vdd_data.other_line_panel_support_workq,
&vdd_data.other_line_panel_support_work, msecs_to_jiffies(OTHERLINE_WORKQ_DEALY));
vdd_data.other_line_panel_work_cnt--;
} else
destroy_workqueue(vdd_data.other_line_panel_support_workq);
if (vdd_data.other_line_panel_work_cnt == 0)
LCD_ERR(" cnt (%d)\n", vdd_data.other_line_panel_work_cnt);
}
static ssize_t mdss_samsung_disp_cell_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
static int string_size = 50;
char temp[string_size];
int *cell_id;
int ndx;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return strnlen(buf, string_size);
}
ndx = display_ndx_check(vdd->ctrl_dsi[DSI_CTRL_0]);
cell_id = &vdd->cell_id_dsi[ndx][0];
/*
* STANDARD FORMAT (Total is 11Byte)
* MAX_CELL_ID : 11Byte
* 7byte(cell_id) + 2byte(Mdnie x_postion) + 2byte(Mdnie y_postion)
*/
snprintf((char *)temp, sizeof(temp),
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
cell_id[0], cell_id[1], cell_id[2], cell_id[3], cell_id[4],
cell_id[5], cell_id[6],
(vdd->mdnie_x[ndx] & 0xFF00) >> 8,
vdd->mdnie_x[ndx] & 0xFF,
(vdd->mdnie_y[ndx] & 0xFF00) >> 8,
vdd->mdnie_y[ndx] & 0xFF);
strlcat(buf, temp, string_size);
LCD_INFO("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
cell_id[0], cell_id[1], cell_id[2], cell_id[3], cell_id[4],
cell_id[5], cell_id[6],
(vdd->mdnie_x[ndx] & 0xFF00) >> 8,
vdd->mdnie_x[ndx] & 0xFF,
(vdd->mdnie_y[ndx] & 0xFF00) >> 8,
vdd->mdnie_y[ndx] & 0xFF);
return strnlen(buf, string_size);
}
static ssize_t mdss_samsung_disp_lcdtype_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
static int string_size = 100;
char temp[string_size];
int ndx;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return strnlen(buf, string_size);
}
ndx = display_ndx_check(vdd->ctrl_dsi[DSI_CTRL_0]);
if (vdd->dtsi_data[ndx].tft_common_support && vdd->dtsi_data[ndx].tft_module_name) {
if (vdd->dtsi_data[ndx].panel_vendor)
snprintf(temp, 20, "%s_%s\n", vdd->dtsi_data[ndx].panel_vendor, vdd->dtsi_data[ndx].tft_module_name);
else
snprintf(temp, 20, "SDC_%s\n", vdd->dtsi_data[ndx].tft_module_name);
} else if (mdss_panel_attached(ndx)) {
if (vdd->dtsi_data[ndx].panel_vendor)
snprintf(temp, 20, "%s_%06x\n", vdd->dtsi_data[ndx].panel_vendor, vdd->manufacture_id_dsi[ndx]);
else
snprintf(temp, 20, "SDC_%06x\n", vdd->manufacture_id_dsi[ndx]);
} else {
LCD_INFO("no manufacture id\n");
snprintf(temp, 20, "SDC_000000\n");
}
strlcat(buf, temp, string_size);
return strnlen(buf, string_size);
}
static ssize_t mdss_samsung_disp_windowtype_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
static int string_size = 15;
char temp[string_size];
int id, id1, id2, id3;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return strnlen(buf, string_size);
}
id = vdd->manufacture_id_dsi[display_ndx_check(vdd->ctrl_dsi[DSI_CTRL_0])];
id1 = (id & 0x00FF0000) >> 16;
id2 = (id & 0x0000FF00) >> 8;
id3 = id & 0xFF;
LCD_INFO("ndx : %d %02x %02x %02x\n",
display_ndx_check(vdd->ctrl_dsi[DSI_CTRL_0]), id1, id2, id3);
snprintf(temp, sizeof(temp), "%02x %02x %02x\n", id1, id2, id3);
strlcat(buf, temp, string_size);
return strnlen(buf, string_size);
}
static ssize_t mdss_samsung_disp_manufacture_date_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
static int string_size = 30;
char temp[string_size];
int date;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return strnlen(buf, string_size);
}
date = vdd->manufacture_date_dsi[display_ndx_check(vdd->ctrl_dsi[DSI_CTRL_0])];
snprintf((char *)temp, sizeof(temp), "manufacture date : %d\n", date);
strlcat(buf, temp, string_size);
LCD_INFO("manufacture date : %d\n", date);
return strnlen(buf, string_size);
}
static ssize_t mdss_samsung_disp_manufacture_code_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
static int string_size = 30;
char temp[string_size];
int *ddi_id;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return strnlen(buf, string_size);
}
ddi_id = &vdd->ddi_id_dsi[display_ndx_check(vdd->ctrl_dsi[DSI_CTRL_0])][0];
snprintf((char *)temp, sizeof(temp), "%02x%02x%02x%02x%02x\n",
ddi_id[0], ddi_id[1], ddi_id[2], ddi_id[3], ddi_id[4]);
strlcat(buf, temp, string_size);
LCD_INFO("%02x %02x %02x %02x %02x\n",
ddi_id[0], ddi_id[1], ddi_id[2], ddi_id[3], ddi_id[4]);
return strnlen(buf, string_size);
}
static ssize_t mdss_samsung_disp_acl_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return rc;
}
rc = snprintf((char *)buf, sizeof(vdd->acl_status), "%d\n", vdd->acl_status);
LCD_INFO("acl status: %d\n", *buf);
return rc;
}
static ssize_t mdss_samsung_disp_acl_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int acl_set = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
struct mdss_panel_data *pdata;
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
return size;
}
if (sysfs_streq(buf, "1"))
acl_set = true;
else if (sysfs_streq(buf, "0"))
acl_set = false;
else
LCD_INFO("Invalid argument!!");
LCD_INFO("(%d)\n", acl_set);
pdata = &vdd->ctrl_dsi[DISPLAY_1]->panel_data;
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
if (acl_set && !vdd->acl_status) {
vdd->acl_status = acl_set;
pdata->set_backlight(pdata, vdd->bl_level);
} else if (!acl_set && vdd->acl_status) {
vdd->acl_status = acl_set;
pdata->set_backlight(pdata, vdd->bl_level);
} else {
vdd->acl_status = acl_set;
LCD_INFO("skip acl update!! acl %d", vdd->acl_status);
}
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
return size;
}
static ssize_t mdss_samsung_disp_siop_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return rc;
}
rc = snprintf((char *)buf, sizeof(vdd->siop_status), "%d\n", vdd->siop_status);
LCD_INFO("siop status: %d\n", *buf);
return rc;
}
static ssize_t mdss_samsung_disp_siop_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int siop_set = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
struct mdss_panel_data *pdata;
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
return size;
}
pdata = &vdd->ctrl_dsi[DISPLAY_1]->panel_data;
if (sysfs_streq(buf, "1"))
siop_set = true;
else if (sysfs_streq(buf, "0"))
siop_set = false;
else
LCD_INFO("Invalid argument!!");
LCD_INFO("(%d)\n", siop_set);
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
if (siop_set && !vdd->siop_status) {
vdd->siop_status = siop_set;
pdata->set_backlight(pdata, vdd->bl_level);
} else if (!siop_set && vdd->siop_status) {
vdd->siop_status = siop_set;
pdata->set_backlight(pdata, vdd->bl_level);
} else {
vdd->siop_status = siop_set;
LCD_INFO("skip siop update!! acl %d", vdd->acl_status);
}
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
return size;
}
static ssize_t mdss_samsung_aid_log_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = 0;
int loop = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return rc;
}
for (loop = 0; loop < vdd->support_panel_max; loop++) {
if (vdd->smart_dimming_dsi[loop] && vdd->smart_dimming_dsi[loop]->print_aid_log)
vdd->smart_dimming_dsi[loop]->print_aid_log(vdd->smart_dimming_dsi[loop]);
else
LCD_ERR("DSI%d smart dimming is not loaded\n", loop);
}
if (vdd->dtsi_data[0].hmt_enabled) {
for (loop = 0; loop < vdd->support_panel_max; loop++) {
if (vdd->smart_dimming_dsi_hmt[loop] && vdd->smart_dimming_dsi_hmt[loop]->print_aid_log)
vdd->smart_dimming_dsi_hmt[loop]->print_aid_log(vdd->smart_dimming_dsi_hmt[loop]);
else
LCD_ERR("DSI%d smart dimming hmt is not loaded\n", loop);
}
}
return rc;
}
#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE)
static ssize_t mdss_samsung_disp_brightness_step(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = 0;
int ndx;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return rc;
}
ndx = display_ndx_check(vdd->ctrl_dsi[DSI_CTRL_0]);
rc = snprintf((char *)buf, 20, "%d\n", vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].lux_tab_size);
LCD_INFO("brightness_step : %d", vdd->dtsi_data[ndx].candela_map_table[vdd->panel_revision].lux_tab_size);
return rc;
}
static ssize_t mdss_samsung_disp_color_weakness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = 0;
int ndx;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return rc;
}
ndx = display_ndx_check(vdd->ctrl_dsi[DSI_CTRL_0]);
rc = snprintf((char *)buf, 20, "%d %d\n", vdd->color_weakness_mode, vdd->color_weakness_level);
LCD_INFO("color weakness : %d %d", vdd->color_weakness_mode, vdd->color_weakness_level);
return rc;
}
static ssize_t mdss_samsung_disp_color_weakness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
struct mdss_panel_data *pdata;
unsigned int mode, level, value = 0;
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
goto end;
}
pdata = &vdd->ctrl_dsi[DISPLAY_1]->panel_data;
sscanf(buf, "%x %x" , &mode, &level);
if (mode >= 0 && mode <= 9)
vdd->color_weakness_mode = mode;
else
LCD_ERR("mode (%x) is not correct !\n", mode);
if (level >= 0 && level <= 9)
vdd->color_weakness_level = level;
else
LCD_ERR("level (%x) is not correct !\n", level);
value = level << 4 | mode;
LCD_ERR("level (%x) mode (%x) value (%x)\n", level, mode, value);
vdd->dtsi_data[DISPLAY_1].ccb_on_tx_cmds[vdd->panel_revision].cmds[1].payload[1] = value;
if (mode)
mdss_samsung_send_cmd(vdd->ctrl_dsi[DISPLAY_1], PANEL_COLOR_WEAKNESS_ENABLE);
else
mdss_samsung_send_cmd(vdd->ctrl_dsi[DISPLAY_1], PANEL_COLOR_WEAKNESS_DISABLE);
end:
return size;
}
#endif
static ssize_t mdss_samsung_read_mtp_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int addr, len, start;
char *read_buf = NULL;
char read_size, read_startoffset;
struct dsi_panel_cmds *rx_cmds;
struct mdss_dsi_ctrl_pdata *ctrl;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
return size;
}
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
if (IS_ERR_OR_NULL(ctrl)) {
LCD_ERR("no ctrl");
return size;
}
sscanf(buf, "%x %d %x" , &addr, &len, &start);
read_buf = kmalloc(len * sizeof(char), GFP_KERNEL);
LCD_INFO("%x %d %x\n", addr, len, start);
rx_cmds = &(vdd->dtsi_data[display_ndx_check(ctrl)].mtp_read_sysfs_rx_cmds[vdd->panel_revision]);
rx_cmds->cmds[0].payload[0] = addr;
rx_cmds->cmds[0].payload[1] = len;
rx_cmds->cmds[0].payload[2] = start;
read_size = len;
read_startoffset = start;
rx_cmds->read_size = &read_size;
rx_cmds->read_startoffset = &read_startoffset;
LCD_INFO("%x %x %x %x %x %x %x %x %x\n",
rx_cmds->cmds[0].dchdr.dtype,
rx_cmds->cmds[0].dchdr.last,
rx_cmds->cmds[0].dchdr.vc,
rx_cmds->cmds[0].dchdr.ack,
rx_cmds->cmds[0].dchdr.wait,
rx_cmds->cmds[0].dchdr.dlen,
rx_cmds->cmds[0].payload[0],
rx_cmds->cmds[0].payload[1],
rx_cmds->cmds[0].payload[2]);
mdss_samsung_panel_data_read(ctrl, rx_cmds, read_buf, PANEL_LEVE1_KEY | PANEL_LEVE2_KEY);
kfree(read_buf);
return size;
}
static ssize_t mdss_samsung_temperature_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return rc;
}
if (vdd->elvss_interpolation_temperature == -15)
rc = snprintf((char *)buf, 40, "-15, -14, 0, 1, 30, 40\n");
else
rc = snprintf((char *)buf, 40, "-20, -19, 0, 1, 30, 40\n");
LCD_INFO("temperature : %d elvss_interpolation_temperature : %d\n", vdd->temperature, vdd->elvss_interpolation_temperature);
return rc;
}
static ssize_t mdss_samsung_temperature_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
struct mdss_panel_data *pdata;
int pre_temp = 0;
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
return size;
}
pdata = &vdd->ctrl_dsi[DISPLAY_1]->panel_data;
pre_temp = vdd->temperature;
sscanf(buf, "%d" , &vdd->temperature);
/* When temperature changed, hbm_mode must setted 0 for EA8061 hbm setting. */
if (pre_temp != vdd->temperature && vdd->display_status_dsi[DISPLAY_1].hbm_mode == 1)
vdd->display_status_dsi[DISPLAY_1].hbm_mode = 0;
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
pdata->set_backlight(pdata, vdd->bl_level);
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
LCD_INFO("temperature : %d", vdd->temperature);
return size;
}
static ssize_t mdss_samsung_lux_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return rc;
}
rc = snprintf((char *)buf, 40, "%d\n", vdd->lux);
LCD_INFO("lux : %d\n", vdd->lux);
return rc;
}
static ssize_t mdss_samsung_lux_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
struct mdss_panel_data *pdata;
int pre_lux = 0;
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
return size;
}
pdata = &vdd->ctrl_dsi[DISPLAY_1]->panel_data;
pre_lux = vdd->lux;
sscanf(buf, "%d" , &vdd->lux);
if (vdd->support_mdnie_lite && pre_lux != vdd->lux)
update_dsi_tcon_mdnie_register(vdd);
LCD_INFO("lux : %d", vdd->lux);
return size;
}
static ssize_t mdss_samsung_read_copr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
static int string_size = 70;
char temp[string_size];
char copr[10] = {0};
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return strnlen(buf, string_size);
}
mdss_samsung_send_cmd(vdd->ctrl_dsi[DISPLAY_1], PANEL_SPI_ENABLE);
if (vdd->panel_func.samsung_spi_read_reg) {
mutex_lock(&vdd->vdd_lock);
memcpy(copr, vdd->panel_func.samsung_spi_read_reg(), sizeof(copr));
mutex_unlock(&vdd->vdd_lock);
}
LCD_INFO("%x %x %x %x %x %x %x %x %x %x\n",
copr[0], copr[1], copr[2], copr[3], copr[4],
copr[5], copr[6], copr[7], copr[8], copr[9]);
snprintf((char *)temp, sizeof(temp), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
copr[0], copr[1], copr[2], copr[3], copr[4], copr[5], copr[6], copr[7], copr[8], copr[9]);
strlcat(buf, temp, string_size);
return strnlen(buf, string_size);
}
static ssize_t mdss_samsung_disp_partial_disp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
LCD_DEBUG("TDB");
return 0;
}
static ssize_t mdss_samsung_disp_partial_disp_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
LCD_DEBUG("TDB");
return size;
}
/*
* Panel LPM related functions
*/
static ssize_t mdss_samsung_panel_lpm_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
struct mdss_dsi_ctrl_pdata *ctrl;
struct panel_func *pfunc;
u8 current_status = 0;
pfunc = &vdd->panel_func;
if (IS_ERR_OR_NULL(pfunc)) {
LCD_ERR("no pfunc");
return rc;
}
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
if (vdd->dtsi_data[ctrl->ndx].panel_lpm_enable)
current_status = vdd->panel_lpm.mode;
rc = snprintf((char *)buf, 30, "%d\n", current_status);
LCD_INFO("[Panel LPM] Current status : %d\n", (int)current_status);
return rc;
}
void mdss_samsung_panel_low_power_config(struct mdss_panel_data *pdata, int enable)
{
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd = NULL;
struct mdss_panel_info *pinfo = NULL;
if (IS_ERR_OR_NULL(pdata)) {
LCD_ERR("Invalid input data\n");
return;
}
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
vdd = check_valid_ctrl(ctrl);
pinfo = &pdata->panel_info;
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
if (!vdd->dtsi_data[ctrl->ndx].panel_lpm_enable) {
LCD_INFO("[Panel LPM] LPM(ALPM/HLPM) is not supported\n");
return;
}
mdss_samsung_panel_lpm_ctrl(pdata, FB_BLANK_NORMAL);
if (enable) {
vdd->esd_recovery.is_wakeup_source = true;
} else {
vdd->esd_recovery.is_wakeup_source = false;
}
vdd->esd_recovery.esd_irq_enable(true, true, (void *)vdd);
}
/*
* mdss_init_panel_lpm_reg_offset()
* This function find offset for reg value
* reg_list[X][0] is reg value
* reg_list[X][1] is offset for reg value
* cmd_list is the target cmds for searching reg value
*/
int mdss_init_panel_lpm_reg_offset(struct mdss_dsi_ctrl_pdata *ctrl,
int (*reg_list)[2],
struct dsi_panel_cmds *cmd_list[], int list_size)
{
struct samsung_display_driver_data *vdd = NULL;
struct dsi_panel_cmds *lpm_cmds = NULL;
int i = 0, j = 0, max_cmd_cnt;
static bool offset_init;
if (IS_ERR_OR_NULL(ctrl))
goto end;
if (IS_ERR_OR_NULL(reg_list) || IS_ERR_OR_NULL(cmd_list))
goto end;
if (offset_init)
goto end;
LCD_INFO("init done\n");
offset_init = true;
vdd = check_valid_ctrl(ctrl);
for (i = 0; i < list_size; i++) {
lpm_cmds = cmd_list[i];
max_cmd_cnt = lpm_cmds->cmd_cnt;
for (j = 0; j < max_cmd_cnt; j++) {
if (lpm_cmds->cmds[j].payload &&
lpm_cmds->cmds[j].payload[0] == reg_list[i][0]) {
reg_list[i][1] = j;
break;
}
}
}
end:
for (i = 0; i < list_size; i++)
LCD_DEBUG("[Panel LPM] offset[%d] : %d\n", i, reg_list[i][1]);
return 0;
}
static void mdss_samsung_panel_lpm_hz_ctrl(struct mdss_panel_data *pdata, int aod_ctrl)
{
struct mdss_dsi_ctrl_pdata *ctrl = NULL;
struct samsung_display_driver_data *vdd = NULL;
struct mdss_panel_info *pinfo = NULL;
if (IS_ERR_OR_NULL(pdata)) {
LCD_ERR("Invalid input data\n");
return;
}
pinfo = &pdata->panel_info;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
vdd = check_valid_ctrl(ctrl);
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
if (!vdd->dtsi_data[ctrl->ndx].panel_lpm_enable) {
LCD_INFO("[Panel LPM] LPM(ALPM/HLPM) is not supported\n");
return;
}
if (pinfo->blank_state == MDSS_PANEL_BLANK_BLANK) {
LCD_INFO("[Panel LPM] Do not change Hz\n");
/*return;*/
}
switch (aod_ctrl) {
case PANEL_LPM_HZ_NONE:
mdss_samsung_send_cmd(ctrl, vdd->panel_lpm.hz);
break;
case PANEL_LPM_AOD_ON:
case PANEL_LPM_AOD_OFF:
mdss_samsung_send_cmd(ctrl, aod_ctrl);
break;
default:
break;
}
LCD_DEBUG("[Panel LPM] Update Hz: %s, aod_ctrl : %s\n",
vdd->panel_lpm.hz == PANEL_LPM_HZ_NONE ? "NONE" :
vdd->panel_lpm.hz == PANEL_LPM_1HZ ? "1HZ" :
vdd->panel_lpm.hz == PANEL_LPM_2HZ ? "2HZ" :
vdd->panel_lpm.hz == PANEL_LPM_30HZ ? "30HZ" : "Default",
aod_ctrl == PANEL_LPM_HZ_NONE ? "NONE" :
aod_ctrl == PANEL_LPM_AOD_ON ? "AOD_ON" :
aod_ctrl == PANEL_LPM_AOD_OFF ? "AOD_OFF" : "UNKNOWN");
}
static void mdss_samsung_panel_lpm_ctrl(struct mdss_panel_data *pdata, int event)
{
struct mdss_dsi_ctrl_pdata *ctrl= NULL;
struct samsung_display_driver_data *vdd = NULL;
struct mdss_panel_info *pinfo = NULL;
static u8 prev_blank_state = MDSS_PANEL_BLANK_UNBLANK;
static int stored_bl_level; /* Used for factory mode only */
int current_bl_level = 0;
static u8 log_counter;
if (IS_ERR_OR_NULL(pdata)) {
LCD_ERR("Invalid input data\n");
return;
}
LCD_DEBUG("[Panel LPM] ++\n");
pinfo = &pdata->panel_info;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
vdd = check_valid_ctrl(ctrl);
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
if (!vdd->dtsi_data[ctrl->ndx].panel_lpm_enable) {
LCD_INFO("[Panel LPM] LPM(ALPM/HLPM) is not supported\n");
return;
}
if (event == FB_BLANK_HSYNC_SUSPEND ||
event == FB_BLANK_POWERDOWN) {
LCD_DEBUG("[Panel LPM] Blank for double tab scenario\n");
vdd->panel_lpm.param_changed = true;
goto end;
}
if (pinfo->blank_state == MDSS_PANEL_BLANK_BLANK) {
LCD_INFO("[Panel LPM] Do not change mode\n");
goto end;
}
mutex_lock(&vdd->vdd_panel_lpm_lock);
/* send_cmd is invalid factory binary */
if ((pinfo->blank_state == MDSS_PANEL_BLANK_UNBLANK) || /* MODE OFF */
vdd->panel_lpm.param_changed || /* MODE ON */
!(++log_counter % 10))
LCD_INFO("[Panel LPM] param_changed : %s, mode : %s, "
"sned_cmd : %s, Hz : %s, bl_level : %s, "
"blank_state : %s(%d), prev_blank_state : %s(%d), event : %s(%d)\n",
vdd->panel_lpm.param_changed ? "True" : "False",
/* Check LPM mode */
vdd->panel_lpm.mode == ALPM_MODE_ON ? "ALPM" :
vdd->panel_lpm.mode == HLPM_MODE_ON ? "HLPM" :
vdd->panel_lpm.mode == MODE_OFF ? "MODE_OFF" : "UNKNOWN",
/* Check the sending command */
(pinfo->blank_state == MDSS_PANEL_BLANK_LOW_POWER) &&
vdd->panel_lpm.param_changed ? "SEND_LPM_CMD" :
(prev_blank_state == MDSS_PANEL_BLANK_LOW_POWER) &&
pinfo->blank_state == MDSS_PANEL_BLANK_UNBLANK ? "SEND_LPM_OFF_CMD" : "NONE",
/* Check the currnet Hz */
vdd->panel_lpm.hz == PANEL_LPM_1HZ ? "1Hz" :
vdd->panel_lpm.hz == PANEL_LPM_2HZ ? "2Hz" :
vdd->panel_lpm.hz == PANEL_LPM_30HZ ? "30Hz" :
vdd->panel_lpm.hz == PANEL_LPM_HZ_NONE ? "NONE" : "UNKNOWN",
/* Check current brightness level */
vdd->panel_lpm.lpm_bl_level == PANEL_LPM_2NIT ? "2NIT" :
vdd->panel_lpm.lpm_bl_level == PANEL_LPM_40NIT ? "40NIT" :
vdd->panel_lpm.lpm_bl_level == PANEL_LPM_60NIT ? "60NIT" : "UNKNOWN",
/* Check current blank_state */
pinfo->blank_state == MDSS_PANEL_BLANK_BLANK ? "BLANK" :
pinfo->blank_state == MDSS_PANEL_BLANK_UNBLANK ? "UNBLANK" :
pinfo->blank_state == MDSS_PANEL_BLANK_LOW_POWER ? "LOW_POWER" :
pinfo->blank_state == MDSS_PANEL_BLANK_READY_TO_UNBLANK ? "READY_TO_UNBLANK" : "UNKNOWN",
pinfo->blank_state,
/* Check previous blank_state */
prev_blank_state == MDSS_PANEL_BLANK_BLANK ? "BLANK" :
prev_blank_state == MDSS_PANEL_BLANK_UNBLANK ? "UNBLANK" :
prev_blank_state == MDSS_PANEL_BLANK_LOW_POWER ? "LOW_POWER" :
prev_blank_state == MDSS_PANEL_BLANK_READY_TO_UNBLANK ? "READY_TO_UNBLANK" : "UNKNOWN",
prev_blank_state,
/* Check events */
event == FB_BLANK_VSYNC_SUSPEND ? "ULP" :
event == FB_BLANK_NORMAL ? "LP" : "UNKNOWN",
event);
/*
* AOD ON/OFF
* ON : AOD will be on if the blank_state is FB_BLANK_VSYNC_SUSPEND
* OFF : AOD will be off if the blank_state is FB_BLANK_NORMAL
*/
if (((vdd->panel_lpm.hz == PANEL_LPM_2HZ) ||
(vdd->panel_lpm.hz == PANEL_LPM_1HZ)) &&
pinfo->blank_state != MDSS_PANEL_BLANK_UNBLANK) {
if (event == FB_BLANK_VSYNC_SUSPEND)
mdss_samsung_panel_lpm_hz_ctrl(pdata, PANEL_LPM_AOD_ON);
else if (event == FB_BLANK_NORMAL) {
mdss_samsung_panel_lpm_hz_ctrl(pdata, PANEL_LPM_AOD_OFF);
}
}
if (((pinfo->blank_state == MDSS_PANEL_BLANK_LOW_POWER) &&
vdd->panel_lpm.param_changed) ||
unlikely((vdd->is_factory_mode &&
vdd->panel_lpm.mode != MODE_OFF) &&
vdd->panel_lpm.param_changed)) {
if (unlikely(vdd->is_factory_mode)) {
LCD_INFO("[Panel LPM] Set low brightness for factory mode\n");
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
stored_bl_level = vdd->mfd_dsi[ctrl->ndx]->bl_level;
pdata->set_backlight(pdata, 0);
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
}
mdss_samsung_send_cmd(ctrl, PANEL_DISPLAY_OFF);
LCD_DEBUG("[Panel LPM] Send panel DISPLAY_OFF cmds\n");
mdss_samsung_send_cmd(ctrl, PANEL_LPM_ON);
LCD_DEBUG("[Panel LPM] Send panel LPM cmds\n");
mdss_samsung_panel_lpm_hz_ctrl(pdata, PANEL_LPM_HZ_NONE);
vdd->panel_lpm.param_changed = false;
if (unlikely(vdd->is_factory_mode) ||
(event == FB_BLANK_VSYNC_SUSPEND)) {
/*
* Send display_on cmd.
* 1. Factory mode
* 2. When the mode changed Doze Suspend
*/
mdss_samsung_send_cmd(ctrl, PANEL_DISPLAY_ON);
} else {
/* The display_on cmd will be sent on next commit */
vdd->display_status_dsi[ctrl->ndx].wait_disp_on = true;
LCD_DEBUG("[Panel LPM] Set wait_disp_on to true\n");
}
} else if (((prev_blank_state == MDSS_PANEL_BLANK_LOW_POWER) &&
(pinfo->blank_state == MDSS_PANEL_BLANK_UNBLANK)) ||
unlikely(vdd->is_factory_mode &&
vdd->panel_lpm.mode == MODE_OFF)) {
/* Turn Off ALPM Mode */
mdss_samsung_send_cmd(ctrl, PANEL_LPM_OFF);
LCD_DEBUG("[Panel LPM] Send panel LPM off cmds\n");
vdd->panel_lpm.param_changed = true;
LCD_DEBUG("[Panel LPM] Restore brightness level\n");
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
if (unlikely(vdd->is_factory_mode &&
vdd->mfd_dsi[ctrl->ndx]->unset_bl_level == 0)) {
LCD_INFO("[Panel LPM] restore bl_level for factory\n");
current_bl_level = stored_bl_level;
} else {
current_bl_level = vdd->mfd_dsi[DISPLAY_1]->bl_level;
}
pdata->set_backlight(pdata, current_bl_level);
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
if (unlikely(vdd->is_factory_mode)) {
mdss_samsung_send_cmd(ctrl, PANEL_DISPLAY_ON);
} else {
/* The display_on cmd will be sent on next commit */
vdd->display_status_dsi[ctrl->ndx].wait_disp_on = true;
LCD_DEBUG("[Panel LPM] Set wait_disp_on to true\n");
}
} else
LCD_DEBUG("[Panel LPM] Do not change mode, line : %d\n", __LINE__);
end:
prev_blank_state = pinfo->blank_state;
LCD_DEBUG("[Panel LPM] --\n");
mutex_unlock(&vdd->vdd_panel_lpm_lock);
}
static void mdss_samsung_panel_lpm_mode_store(struct mdss_panel_data *pdata, u8 mode)
{
struct mdss_dsi_ctrl_pdata *ctrl= NULL;
struct samsung_display_driver_data *vdd = NULL;
struct mdss_panel_info *pinfo = NULL;
u8 req_mode = mode;
if (IS_ERR_OR_NULL(pdata)) {
LCD_ERR("Invalid input data\n");
return;
}
LCD_DEBUG("[Panel LPM] ++\n");
pinfo = &pdata->panel_info;
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
vdd = check_valid_ctrl(ctrl);
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
if (!vdd->dtsi_data[ctrl->ndx].panel_lpm_enable) {
LCD_INFO("[Panel LPM] LPM(ALPM/HLPM) is not supported\n");
return;
}
mutex_lock(&vdd->vdd_panel_lpm_lock);
/*
* Get Panel LPM mode and update brightness and Hz setting
*/
if (vdd->panel_func.samsung_get_panel_lpm_mode)
vdd->panel_func.samsung_get_panel_lpm_mode(ctrl, &req_mode);
if ((req_mode >= MAX_LPM_MODE)) {
LCD_INFO("[Panel LPM] Set req_mode to ALPM_MODE_ON\n");
req_mode = ALPM_MODE_ON;
}
vdd->panel_lpm.mode = req_mode;
mutex_unlock(&vdd->vdd_panel_lpm_lock);
if (unlikely(vdd->is_factory_mode))
mdss_samsung_panel_lpm_ctrl(pdata, FB_BLANK_UNBLANK);
LCD_DEBUG("[Panel LPM] --\n");
}
static ssize_t mdss_samsung_panel_lpm_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int mode = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
struct mdss_panel_data *pdata;
struct mdss_panel_info *pinfo;
pdata = &vdd->ctrl_dsi[DISPLAY_1]->panel_data;
pinfo = &pdata->panel_info;
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
return size;
}
sscanf(buf, "%d" , &mode);
LCD_INFO("[Panel LPM] Mode : %d\n", mode);
mdss_samsung_panel_lpm_mode_store(pdata, mode + MAX_LPM_MODE);
return size;
}
int hmt_bright_update(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct mdss_panel_data *pdata;
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
pdata = &vdd->ctrl_dsi[DISPLAY_1]->panel_data;
if (vdd->hmt_stat.hmt_on) {
mdss_samsung_brightness_dcs_hmt(ctrl, vdd->hmt_stat.hmt_bl_level);
} else {
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
pdata->set_backlight(pdata, vdd->bl_level);
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
LCD_INFO("hmt off state!\n");
}
return 0;
}
int hmt_enable(struct mdss_dsi_ctrl_pdata *ctrl, struct samsung_display_driver_data *vdd)
{
LCD_INFO("[HMT] HMT %s\n", vdd->hmt_stat.hmt_on ? "ON" : "OFF");
if (vdd->hmt_stat.hmt_on) {
mdss_samsung_send_cmd(ctrl, PANEL_HMT_ENABLE);
} else {
mdss_samsung_send_cmd(ctrl, PANEL_HMT_DISABLE);
}
return 0;
}
int hmt_reverse_update(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
{
LCD_INFO("[HMT] HMT %s\n", enable ? "REVERSE" : "FORWARD");
if (enable)
mdss_samsung_send_cmd(ctrl, PANEL_HMT_REVERSE);
else
mdss_samsung_send_cmd(ctrl, PANEL_HMT_FORWARD);
return 0;
}
static ssize_t mipi_samsung_hmt_bright_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return -ENODEV;
}
if (!vdd->dtsi_data[0].hmt_enabled) {
LCD_ERR("hmt is not supported..\n");
return -ENODEV;
}
rc = snprintf((char *)buf, 30, "%d\n", vdd->hmt_stat.hmt_bl_level);
LCD_INFO("[HMT] hmt bright : %d\n", *buf);
return rc;
}
static ssize_t mipi_samsung_hmt_bright_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int input;
struct mdss_panel_info *pinfo = NULL;
struct mdss_dsi_ctrl_pdata *ctrl;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return -ENODEV;
}
if (!vdd->dtsi_data[0].hmt_enabled) {
LCD_ERR("hmt is not supported..\n");
return -ENODEV;
}
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
pinfo = &ctrl->panel_data.panel_info;
sscanf(buf, "%d" , &input);
LCD_INFO("[HMT] input (%d) ++\n", input);
if (!vdd->hmt_stat.hmt_on) {
LCD_INFO("[HMT] hmt is off!\n");
return size;
}
if (!pinfo->blank_state) {
LCD_ERR("[HMT] panel is off!\n");
vdd->hmt_stat.hmt_bl_level = input;
return size;
}
if (vdd->hmt_stat.hmt_bl_level == input) {
LCD_ERR("[HMT] hmt bright already %d!\n", vdd->hmt_stat.hmt_bl_level);
return size;
}
vdd->hmt_stat.hmt_bl_level = input;
hmt_bright_update(ctrl);
LCD_INFO("[HMT] input (%d) --\n", input);
return size;
}
static ssize_t mipi_samsung_hmt_on_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return -ENODEV;
}
if (!vdd->dtsi_data[0].hmt_enabled) {
LCD_ERR("hmt is not supported..\n");
return -ENODEV;
}
rc = snprintf((char *)buf, 30, "%d\n", vdd->hmt_stat.hmt_on);
LCD_INFO("[HMT] hmt on input : %d\n", *buf);
return rc;
}
static ssize_t mipi_samsung_hmt_on_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int input;
struct mdss_panel_info *pinfo = NULL;
struct mdss_dsi_ctrl_pdata *ctrl;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return -ENODEV;
}
if (!vdd->dtsi_data[0].hmt_enabled) {
LCD_ERR("hmt is not supported..\n");
return -ENODEV;
}
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
pinfo = &ctrl->panel_data.panel_info;
sscanf(buf, "%d" , &input);
LCD_INFO("[HMT] input (%d) ++\n", input);
if (!pinfo->blank_state) {
LCD_ERR("[HMT] panel is off!\n");
vdd->hmt_stat.hmt_on = input;
return size;
}
if (vdd->hmt_stat.hmt_on == input) {
LCD_INFO("[HMT] hmt already %s !\n", vdd->hmt_stat.hmt_on?"ON":"OFF");
return size;
}
vdd->hmt_stat.hmt_on = input;
hmt_enable(ctrl, vdd);
hmt_reverse_update(ctrl, vdd->hmt_stat.hmt_on);
hmt_bright_update(ctrl);
LCD_INFO("[HMT] input hmt (%d) --\n",
vdd->hmt_stat.hmt_on);
return size;
}
void mdss_samsung_cabc_update(void)
{
struct samsung_display_driver_data *vdd = samsung_get_vdd();
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return;
}
if (vdd->auto_brightness) {
LCD_INFO("auto brightness is on , cabc cmds are already sent--\n");
return;
}
if (vdd->siop_status) {
if (vdd->panel_func.samsung_lvds_write_reg)
vdd->panel_func.samsung_brightness_tft_pwm(vdd->ctrl_dsi[DISPLAY_1], vdd->bl_level);
else {
mdss_samsung_send_cmd(vdd->ctrl_dsi[DISPLAY_1], PANEL_CABC_OFF_DUTY);
mdss_samsung_send_cmd(vdd->ctrl_dsi[DISPLAY_1], PANEL_CABC_ON);
if (vdd->dtsi_data[0].cabc_delay && !vdd->display_status_dsi[0].disp_on_pre)
usleep_range(vdd->dtsi_data[0].cabc_delay, vdd->dtsi_data[0].cabc_delay);
mdss_samsung_send_cmd(vdd->ctrl_dsi[DISPLAY_1], PANEL_CABC_ON_DUTY);
}
} else {
if (vdd->panel_func.samsung_lvds_write_reg)
vdd->panel_func.samsung_brightness_tft_pwm(vdd->ctrl_dsi[DISPLAY_1], vdd->bl_level);
else {
mdss_samsung_send_cmd(vdd->ctrl_dsi[DISPLAY_1], PANEL_CABC_OFF_DUTY);
mdss_samsung_send_cmd(vdd->ctrl_dsi[DISPLAY_1], PANEL_CABC_OFF);
}
}
}
int config_cabc(int value)
{
struct samsung_display_driver_data *vdd = samsung_get_vdd();
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return value;
}
if (vdd->siop_status == value) {
LCD_INFO("No change in cabc state, update not needed--\n");
return value;
}
vdd->siop_status = value;
mdss_samsung_cabc_update();
return 0;
}
static ssize_t mipi_samsung_mcd_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
struct mdss_dsi_ctrl_pdata *ctrl;
int input;
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
goto end;
}
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
sscanf(buf, "%d" , &input);
LCD_INFO("(%d)\n", input);
if (input)
mdss_samsung_send_cmd(ctrl, PANEL_MCD_ON);
else
mdss_samsung_send_cmd(ctrl, PANEL_MCD_OFF);
end:
return size;
}
static ssize_t mdss_samsung_irc_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = 0;
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("no vdd");
return rc;
}
rc = snprintf((char *)buf, 50, "irc_enable_status : %d irc_mode : %d\n",
vdd->irc_info.irc_enable_status, vdd->irc_info.irc_mode);
LCD_INFO("irc_enable_status : %d irc_mode : %d\n",
vdd->irc_info.irc_enable_status, vdd->irc_info.irc_mode);
return rc;
}
static ssize_t mdss_samsung_irc_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
int input_enable, input_mode;
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
goto end;
}
sscanf(buf, "%d %x" , &input_enable, &input_mode);
if (input_enable != 0 && input_enable != 1) {
LCD_INFO("Invalid argument %d 0x%x !!\n", input_enable, input_mode);
return size;
}
if (input_mode >= IRC_MAX_MODE) {
LCD_INFO("Invalid argument %d 0x%x !!\n", input_enable, input_mode);
return size;
}
if (vdd->irc_info.irc_enable_status != input_enable ||
vdd->irc_info.irc_mode != input_mode) {
vdd->irc_info.irc_enable_status = input_enable;
vdd->irc_info.irc_mode = input_mode;
LCD_INFO("%d 0x%x\n", input_enable, input_mode);
if (!vdd->dtsi_data[DISPLAY_1].tft_common_support) {
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
mdss_samsung_brightness_dcs(vdd->ctrl_dsi[DISPLAY_1], vdd->bl_level);
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
}
}
end:
return size;
}
static ssize_t mdss_samsung_ldu_correction_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return strlen(buf);
}
static ssize_t mdss_samsung_ldu_correction_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
int value;
sscanf(buf, "%d" , &value);
if ((value < 0) || (value > 7)) {
LCD_ERR("out of range %d\n", value);
return -EINVAL;
}
vdd->ldu_correction_state = value;
LCD_INFO("ldu_correction_state : %d\n", vdd->ldu_correction_state);
return size;
}
static ssize_t mipi_samsung_hw_cursor_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
struct mdss_dsi_ctrl_pdata *ctrl;
int input[10];
if (IS_ERR_OR_NULL(vdd) || IS_ERR_OR_NULL(vdd->mfd_dsi[DISPLAY_1])) {
LCD_ERR("no vdd");
goto end;
}
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_broadcast) {
if (vdd->ctrl_dsi[DISPLAY_1]->cmd_sync_wait_trigger)
ctrl = vdd->ctrl_dsi[DISPLAY_1];
else
ctrl = vdd->ctrl_dsi[DISPLAY_2];
} else
ctrl = vdd->ctrl_dsi[DISPLAY_1];
sscanf(buf, "%d %d %d %d %d %d %d %x %x %x" , &input[0], &input[1],
&input[2], &input[3], &input[4], &input[5], &input[6],
&input[7], &input[8], &input[9]);
if (!IS_ERR_OR_NULL(vdd->panel_func.ddi_hw_cursor))
vdd->panel_func.ddi_hw_cursor(ctrl, input);
else
LCD_ERR("ddi_hw_cursor function is NULL\n");
end:
return size;
}
static ssize_t mdss_samsung_adaptive_control_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct samsung_display_driver_data *vdd =
(struct samsung_display_driver_data *)dev_get_drvdata(dev);
int value;
sscanf(buf, "%d" , &value);
LCD_INFO("ACL value : %x\n", value);
vdd->gradual_acl_val = value;
if (!vdd->gradual_acl_val)
vdd->acl_status = 0;
else
vdd->acl_status = 1;
if (!vdd->dtsi_data[DISPLAY_1].tft_common_support) {
mutex_lock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
mdss_samsung_brightness_dcs(vdd->ctrl_dsi[DISPLAY_1], vdd->bl_level);
mutex_unlock(&vdd->mfd_dsi[DISPLAY_1]->bl_lock);
}
return size;
}
static ssize_t mipi_samsung_esd_check_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc;
int ndx;
struct samsung_display_driver_data *vdd = samsung_get_vdd();
if (IS_ERR_OR_NULL(vdd)) {
pr_err("%s: Invalid data vdd : 0x%zx\n", __func__, (size_t)vdd);
return 0;
}
ndx = display_ndx_check(vdd->ctrl_dsi[DSI_CTRL_0]);
pr_info("%s++ ndx(%d) \n", __func__, ndx);
if (vdd->ctrl_dsi[ndx]->panel_data.panel_info.esd_check_enabled){
rc = snprintf((char *)buf, 20, "esd_irq_handler\n");
/*esd enabled only on dsi 0*/
esd_irq_handler(0, vdd->ctrl_dsi[DISPLAY_1]);
}else
rc = snprintf((char *)buf, 20, "no esd_irq_handler %d\n", 0);
return rc;
}
static DEVICE_ATTR(lcd_type, S_IRUGO, mdss_samsung_disp_lcdtype_show, NULL);
static DEVICE_ATTR(cell_id, S_IRUGO, mdss_samsung_disp_cell_id_show, NULL);
static DEVICE_ATTR(window_type, S_IRUGO, mdss_samsung_disp_windowtype_show, NULL);
static DEVICE_ATTR(manufacture_date, S_IRUGO, mdss_samsung_disp_manufacture_date_show, NULL);
static DEVICE_ATTR(manufacture_code, S_IRUGO, mdss_samsung_disp_manufacture_code_show, NULL);
static DEVICE_ATTR(power_reduce, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_disp_acl_show, mdss_samsung_disp_acl_store);
static DEVICE_ATTR(siop_enable, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_disp_siop_show, mdss_samsung_disp_siop_store);
static DEVICE_ATTR(read_mtp, S_IRUGO | S_IWUSR | S_IWGRP, NULL, mdss_samsung_read_mtp_store);
static DEVICE_ATTR(temperature, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_temperature_show, mdss_samsung_temperature_store);
static DEVICE_ATTR(lux, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_lux_show, mdss_samsung_lux_store);
static DEVICE_ATTR(read_copr, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_read_copr_show, NULL);
static DEVICE_ATTR(aid_log, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_aid_log_show, NULL);
static DEVICE_ATTR(partial_disp, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_disp_partial_disp_show, mdss_samsung_disp_partial_disp_store);
static DEVICE_ATTR(alpm, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_panel_lpm_show, mdss_samsung_panel_lpm_store);
static DEVICE_ATTR(hmt_bright, S_IRUGO | S_IWUSR | S_IWGRP, mipi_samsung_hmt_bright_show, mipi_samsung_hmt_bright_store);
static DEVICE_ATTR(hmt_on, S_IRUGO | S_IWUSR | S_IWGRP, mipi_samsung_hmt_on_show, mipi_samsung_hmt_on_store);
static DEVICE_ATTR(mcd_mode, S_IRUGO | S_IWUSR | S_IWGRP, NULL, mipi_samsung_mcd_store);
static DEVICE_ATTR(irc, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_irc_show, mdss_samsung_irc_store);
static DEVICE_ATTR(ldu_correction, S_IRUGO | S_IWUSR | S_IWGRP, mdss_samsung_ldu_correction_show, mdss_samsung_ldu_correction_store);
static DEVICE_ATTR(adaptive_control, S_IRUGO | S_IWUSR | S_IWGRP, NULL, mdss_samsung_adaptive_control_store);
static DEVICE_ATTR(hw_cursor, S_IRUGO | S_IWUSR | S_IWGRP, NULL, mipi_samsung_hw_cursor_store);
static DEVICE_ATTR(esd_check, S_IRUGO , mipi_samsung_esd_check_show, NULL);
static struct attribute *panel_sysfs_attributes[] = {
&dev_attr_lcd_type.attr,
&dev_attr_cell_id.attr,
&dev_attr_window_type.attr,
&dev_attr_manufacture_date.attr,
&dev_attr_manufacture_code.attr,
&dev_attr_power_reduce.attr,
&dev_attr_siop_enable.attr,
&dev_attr_aid_log.attr,
&dev_attr_read_mtp.attr,
&dev_attr_read_copr.attr,
&dev_attr_temperature.attr,
&dev_attr_lux.attr,
&dev_attr_partial_disp.attr,
&dev_attr_alpm.attr,
&dev_attr_hmt_bright.attr,
&dev_attr_hmt_on.attr,
&dev_attr_mcd_mode.attr,
&dev_attr_irc.attr,
&dev_attr_ldu_correction.attr,
&dev_attr_adaptive_control.attr,
&dev_attr_hw_cursor.attr,
&dev_attr_esd_check.attr,
NULL
};
static const struct attribute_group panel_sysfs_group = {
.attrs = panel_sysfs_attributes,
};
#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE)
static DEVICE_ATTR(brightness_step, S_IRUGO | S_IWUSR | S_IWGRP,
mdss_samsung_disp_brightness_step,
NULL);
static DEVICE_ATTR(weakness_ccb, S_IRUGO | S_IWUSR | S_IWGRP,
mdss_samsung_disp_color_weakness_show,
mdss_samsung_disp_color_weakness_store);
static struct attribute *bl_sysfs_attributes[] = {
&dev_attr_brightness_step.attr,
&dev_attr_weakness_ccb.attr,
NULL
};
static const struct attribute_group bl_sysfs_group = {
.attrs = bl_sysfs_attributes,
};
#endif /* END CONFIG_LCD_CLASS_DEVICE*/
static ssize_t csc_read_cfg(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t ret = 0;
ret = snprintf(buf, PAGE_SIZE, "%d\n", csc_update);
return ret;
}
static ssize_t csc_write_cfg(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
ssize_t ret = strnlen(buf, PAGE_SIZE);
int err;
int mode;
err = kstrtoint(buf, 0, &mode);
if (err)
return ret;
csc_update = (u8)mode;
csc_change = 1;
LCD_INFO("csc ctrl set to csc_update(%d)\n", csc_update);
return ret;
}
static DEVICE_ATTR(csc_cfg, S_IRUGO | S_IWUSR, csc_read_cfg, csc_write_cfg);
int mdss_samsung_create_sysfs(void *data)
{
static int sysfs_enable = 0;
int rc = 0;
#if defined(CONFIG_LCD_CLASS_DEVICE)
struct lcd_device *lcd_device;
#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE)
struct backlight_device *bd = NULL;
#endif
#endif
struct device *csc_dev = vdd_data.mfd_dsi[0]->fbi->dev;
/* sysfs creat func should be called one time in dual dsi mode */
if (sysfs_enable)
return 0;
#if defined(CONFIG_LCD_CLASS_DEVICE)
lcd_device = lcd_device_register("panel", NULL, data, NULL);
if (IS_ERR_OR_NULL(lcd_device)) {
rc = PTR_ERR(lcd_device);
LCD_ERR("Failed to register lcd device..\n");
return rc;
}
rc = sysfs_create_group(&lcd_device->dev.kobj, &panel_sysfs_group);
if (rc) {
LCD_ERR("Failed to create panel sysfs group..\n");
sysfs_remove_group(&lcd_device->dev.kobj, &panel_sysfs_group);
return rc;
}
#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE)
bd = backlight_device_register("panel", &lcd_device->dev,
data, NULL, NULL);
if (IS_ERR(bd)) {
rc = PTR_ERR(bd);
LCD_ERR("backlight : failed to register device\n");
return rc;
}
rc = sysfs_create_group(&bd->dev.kobj, &bl_sysfs_group);
if (rc) {
LCD_ERR("Failed to create backlight sysfs group..\n");
sysfs_remove_group(&bd->dev.kobj, &bl_sysfs_group);
return rc;
}
#endif
rc = sysfs_create_file(&lcd_device->dev.kobj, &dev_attr_tuning.attr);
if (rc) {
LCD_ERR("sysfs create fail-%s\n", dev_attr_tuning.attr.name);
return rc;
}
#endif
rc = sysfs_create_file(&csc_dev->kobj, &dev_attr_csc_cfg.attr);
if (rc) {
LCD_ERR("sysfs create fail-%s\n", dev_attr_csc_cfg.attr.name);
return rc;
}
sysfs_enable = 1;
LCD_INFO("done!!\n");
return rc;
}
struct samsung_display_driver_data *samsung_get_vdd(void)
{
return &vdd_data;
}
int display_ndx_check(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct samsung_display_driver_data *vdd = check_valid_ctrl(ctrl);
if (IS_ERR_OR_NULL(vdd)) {
LCD_ERR("Invalid data ctrl : 0x%zx vdd : 0x%zx", (size_t)ctrl, (size_t)vdd);
return DSI_CTRL_0;
}
if (vdd->support_hall_ic) {
if (vdd->display_status_dsi[DISPLAY_1].hall_ic_status == HALL_IC_OPEN)
return DSI_CTRL_0; /*OPEN : Internal PANEL */
else
return DSI_CTRL_1; /*CLOSE : External PANEL */
} else
return ctrl->ndx;
}
int samsung_display_hall_ic_status(struct notifier_block *nb,
unsigned long hall_ic, void *data)
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = vdd_data.ctrl_dsi[DISPLAY_1];
/*
previou panel off -> current panel on
foder open : 0, close : 1
*/
if (!vdd_data.support_hall_ic)
return 0;
mutex_lock(&vdd_data.vdd_hall_ic_blank_unblank_lock); /*HALL IC blank mode change */
mutex_lock(&vdd_data.vdd_hall_ic_lock); /* HALL IC switching */
LCD_ERR("mdss hall_ic : %s, blank_status: %d start\n", hall_ic ? "CLOSE" : "OPEN", vdd_data.vdd_blank_mode[DISPLAY_1]);
/* check the lcd id for DISPLAY_1 and DISPLAY_2 */
if (mdss_panel_attached(DISPLAY_1) && mdss_panel_attached(DISPLAY_2)) {
/* To check current blank mode */
if (vdd_data.vdd_blank_mode[DISPLAY_1] == FB_BLANK_UNBLANK &&
ctrl_pdata->panel_data.panel_info.panel_state &&
vdd_data.display_status_dsi[DISPLAY_1].hall_ic_status != hall_ic) {
/* set flag */
vdd_data.display_status_dsi[DISPLAY_1].hall_ic_mode_change_trigger = true;
/* panel off */
ctrl_pdata->off(&ctrl_pdata->panel_data);
/* set status */
vdd_data.display_status_dsi[DISPLAY_1].hall_ic_status = hall_ic;
/* panel on */
ctrl_pdata->on(&ctrl_pdata->panel_data);
/* clear flag */
vdd_data.display_status_dsi[DISPLAY_1].hall_ic_mode_change_trigger = false;
/* Brightness setting */
if (vdd_data.ctrl_dsi[DISPLAY_1]->bklt_ctrl == BL_DCS_CMD) {
mutex_lock(&vdd_data.mfd_dsi[DISPLAY_1]->bl_lock);
mdss_samsung_brightness_dcs(ctrl_pdata, vdd_data.bl_level);
mutex_unlock(&vdd_data.mfd_dsi[DISPLAY_1]->bl_lock);
}
/* display on */
mdss_samsung_send_cmd(ctrl_pdata, PANEL_DISPLAY_ON);
} else {
vdd_data.display_status_dsi[DISPLAY_1].hall_ic_status = hall_ic;
LCD_ERR("mdss skip display changing\n");
}
} else {
/* check the lcd id for DISPLAY_1 */
if (mdss_panel_attached(DISPLAY_1))
vdd_data.display_status_dsi[DISPLAY_1].hall_ic_status = HALL_IC_OPEN;
/* check the lcd id for DISPLAY_2 */
if (mdss_panel_attached(DISPLAY_2))
vdd_data.display_status_dsi[DISPLAY_1].hall_ic_status = HALL_IC_CLOSE;
}
mutex_unlock(&vdd_data.vdd_hall_ic_lock); /* HALL IC switching */
mutex_unlock(&vdd_data.vdd_hall_ic_blank_unblank_lock); /*HALL IC blank mode change */
LCD_ERR("mdss hall_ic : %s, blank_status: %d end\n", hall_ic ? "CLOSE" : "OPEN", vdd_data.vdd_blank_mode[DISPLAY_1]);
return 0;
}
/************************************************************
*
* MDSS & DSI REGISTER DUMP FUNCTION
*
**************************************************************/
size_t kvaddr_to_paddr(unsigned long vaddr)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
size_t paddr;
pgd = pgd_offset_k(vaddr);
if (unlikely(pgd_none(*pgd) || pgd_bad(*pgd)))
return 0;
pud = pud_offset(pgd, vaddr);
if (unlikely(pud_none(*pud) || pud_bad(*pud)))
return 0;
pmd = pmd_offset(pud, vaddr);
if (unlikely(pmd_none(*pmd) || pmd_bad(*pmd)))
return 0;
pte = pte_offset_kernel(pmd, vaddr);
if (!pte_present(*pte))
return 0;
paddr = (unsigned long)pte_pfn(*pte) << PAGE_SHIFT;
paddr += (vaddr & (PAGE_SIZE - 1));
return paddr;
}
static void dump_reg(char *addr, int len)
{
if (IS_ERR_OR_NULL(addr))
return;
#if defined(CONFIG_ARCH_MSM8976) || defined(CONFIG_ARCH_MSM8952)
mdss_dump_reg(MDSS_REG_DUMP_IN_LOG, addr, len, NULL, false);
#else
mdss_dump_reg(addr, len);
#endif
}
void mdss_samsung_dump_regs(void)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
struct mdss_mdp_ctl *ctl;
char name[32];
int loop;
if (IS_ERR_OR_NULL(mdata))
return;
else
ctl = mdata->ctl_off;
snprintf(name, sizeof(name), "MDP BASE");
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->mdss_io.base));
dump_reg(mdata->mdss_io.base, 0x100);
snprintf(name, sizeof(name), "MDP REG");
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->mdp_base));
dump_reg(mdata->mdp_base, 0x500);
for (loop = 0; loop < mdata->nctl ; loop++) {
snprintf(name, sizeof(name), "CTRL%d", loop);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->ctl_off[loop].base));
dump_reg(mdata->ctl_off[loop].base, 0x100);
}
for (loop = 0; loop < mdata->nvig_pipes ; loop++) {
snprintf(name, sizeof(name), "VG%d", loop);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->vig_pipes[loop].base));
/* dump_reg(mdata->vig_pipes[loop].base, 0x100); */
dump_reg(mdata->vig_pipes[loop].base, 0x400);
}
for (loop = 0; loop < mdata->nrgb_pipes ; loop++) {
snprintf(name, sizeof(name), "RGB%d", loop);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->rgb_pipes[loop].base));
/* dump_reg(mdata->rgb_pipes[loop].base, 0x100); */
dump_reg(mdata->rgb_pipes[loop].base, 0x400);
}
for (loop = 0; loop < mdata->ndma_pipes ; loop++) {
snprintf(name, sizeof(name), "DMA%d", loop);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->dma_pipes[loop].base));
/* dump_reg(mdata->dma_pipes[loop].base, 0x100); */
dump_reg(mdata->dma_pipes[loop].base, 0x400);
}
for (loop = 0; loop < mdata->nmixers_intf ; loop++) {
snprintf(name, sizeof(name), "MIXER_INTF_%d", loop);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->mixer_intf[loop].base));
dump_reg(mdata->mixer_intf[loop].base, 0x100);
}
for (loop = 0; loop < mdata->nmixers_wb ; loop++) {
snprintf(name, sizeof(name), "MIXER_WB_%d", loop);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->mixer_wb[loop].base));
dump_reg(mdata->mixer_wb[loop].base, 0x100);
}
if (ctl->is_video_mode) {
for (loop = 0; loop < mdata->nintf; loop++) {
snprintf(name, sizeof(name), "VIDEO_INTF%d", loop);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdss_mdp_get_intf_base_addr(mdata, loop)));
dump_reg(mdss_mdp_get_intf_base_addr(mdata, loop), 0x40);
}
}
for (loop = 0; loop < mdata->nmixers_intf ; loop++) {
snprintf(name, sizeof(name), "PING_PONG%d", loop);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->mixer_intf[loop].pingpong_base));
/* dump_reg(mdata->mixer_intf[loop].pingpong_base, 0x40); */
dump_reg(mdata->mixer_intf[loop].pingpong_base, 0x100);
}
if (ctl->mfd->panel_info->compression_mode == COMPRESSION_DSC) {
for (loop = 0; loop < mdata->ndsc ; loop++) {
snprintf(name, sizeof(name), "DSC%d", loop);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->dsc_off[loop].base));
dump_reg(mdata->dsc_off[loop].base, 0x200);
}
}
#if defined(CONFIG_ARCH_MSM8992)
/* To dump ping-pong slave register for ping-pong split supporting chipset */
snprintf(name, sizeof(name), "PING_PONG SLAVE");
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->slave_pingpong_base));
dump_reg(mdata->slave_pingpong_base, 0x40);
#endif
snprintf(name, sizeof(name), "VBIF");
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->vbif_io.base));
dump_reg(mdata->vbif_io.base, 0x270);
snprintf(name, sizeof(name), "VBIF NRT");
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)mdata->vbif_nrt_io.base));
dump_reg(mdata->vbif_nrt_io.base, 0x270);
}
void mdss_samsung_dsi_dump_regs(int dsi_num)
{
struct mdss_dsi_ctrl_pdata *dsi_ctrl = mdss_dsi_get_ctrl(dsi_num);
char name[32];
if (!dsi_ctrl) {
LCD_ERR("dsi_ctrl is null.. (%d)\n", dsi_num);
return;
}
if (vdd_data.panel_attach_status & BIT(dsi_num)) {
snprintf(name, sizeof(name), "DSI%d CTL", dsi_num);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)dsi_ctrl->ctrl_io.base));
dump_reg((char *)dsi_ctrl->ctrl_io.base, dsi_ctrl->ctrl_io.len);
snprintf(name, sizeof(name), "DSI%d PHY", dsi_num);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)dsi_ctrl->phy_io.base));
dump_reg((char *)dsi_ctrl->phy_io.base, (size_t)dsi_ctrl->phy_io.len);
snprintf(name, sizeof(name), "DSI%d PLL", dsi_num);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)vdd_data.dump_info[dsi_num].dsi_pll.virtual_addr));
dump_reg((char *)vdd_data.dump_info[dsi_num].dsi_pll.virtual_addr, 0x200);
#if defined(CONFIG_ARCH_MSM8992) || defined(CONFIG_ARCH_MSM8994)
if (dsi_ctrl->shared_ctrl_data->phy_regulator_io.base) {
snprintf(name, sizeof(name), "DSI%d REGULATOR", dsi_num);
LCD_ERR("=============%s 0x%08zx ==============\n", name,
kvaddr_to_paddr((unsigned long)dsi_ctrl->shared_ctrl_data->phy_regulator_io.base));
dump_reg((char *)dsi_ctrl->shared_ctrl_data->phy_regulator_io.base, (size_t)dsi_ctrl->shared_ctrl_data->phy_regulator_io.len);
}
#endif
}
}
int mdss_samsung_read_rddpm(void)
{
struct mdss_dsi_ctrl_pdata *dsi_ctrl = mdss_dsi_get_ctrl(0);
struct samsung_display_driver_data *vdd = samsung_get_vdd();
char rddpm_reg = 0;
MDSS_XLOG(dsi_ctrl->ndx, 0x0A);
if (!IS_ERR_OR_NULL(vdd->dtsi_data[DISPLAY_1].ldi_debug0_rx_cmds[vdd->panel_revision].cmds)) {
mdss_samsung_read_nv_mem(&dsi_ctrl[DISPLAY_1], &vdd->dtsi_data[DISPLAY_1].ldi_debug0_rx_cmds[vdd->panel_revision], &rddpm_reg, 0);
LCD_ERR("rddpm 0x(%x)\n", rddpm_reg);
if (rddpm_reg == 0x08) {
LCD_ERR("ddi reset status (%x)\n", rddpm_reg);
schedule_work(&pstatus_data->check_status.work);
return 1;
}
} else
LCD_ERR("no rddpm read cmds..\n");
return 0;
}
int mdss_samsung_dsi_te_check(void)
{
struct mdss_dsi_ctrl_pdata *dsi_ctrl_0 = mdss_dsi_get_ctrl(0);
struct mdss_dsi_ctrl_pdata *dsi_ctrl_1 = mdss_dsi_get_ctrl(1);
int rc, te_count = 0;
int te_max = 20000; /*sampling 200ms */
if (dsi_ctrl_0[DISPLAY_1].panel_mode == DSI_VIDEO_MODE)
return 0;
if (gpio_is_valid(dsi_ctrl_0[DISPLAY_1].disp_te_gpio) || gpio_is_valid(dsi_ctrl_1[DISPLAY_1].disp_te_gpio)) {
LCD_ERR("============ start waiting for TE ============\n");
for (te_count = 0; te_count < te_max; te_count++) {
if (gpio_is_valid(dsi_ctrl_0[DISPLAY_1].disp_te_gpio))
rc = gpio_get_value(dsi_ctrl_0[DISPLAY_1].disp_te_gpio);
else
rc = gpio_get_value(dsi_ctrl_1[DISPLAY_1].disp_te_gpio);
if (rc == 1) {
LCD_ERR("gpio_get_value(disp_te_gpio) = %d ",
rc);
LCD_ERR("te_count = %d\n", te_count);
break;
}
/* usleep suspends the calling thread whereas udelay is a
* busy wait. Here the value of te_gpio is checked in a loop of
* max count = 250. If this loop has to iterate multiple
* times before the te_gpio is 1, the calling thread will end
* up in suspend/wakeup sequence multiple times if usleep is
* used, which is an overhead. So use udelay instead of usleep.
*/
udelay(10);
}
if (te_count == te_max) {
LCD_ERR("LDI doesn't generate TE, ddi recovery start.");
return 1;
} else
LCD_ERR("LDI generate TE\n");
LCD_ERR("============ finish waiting for TE ============\n");
} else
LCD_ERR("disp_te_gpio is not valid\n");
return 0;
}
void mdss_mdp_underrun_dump_info(void)
{
struct mdss_mdp_pipe *pipe;
/* struct mdss_data_type *mdss_res = mdss_mdp_get_mdata(); */
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(vdd_data.mfd_dsi[0]);
int pcount = mdp5_data->mdata->nrgb_pipes + mdp5_data->mdata->nvig_pipes + mdp5_data->mdata->ndma_pipes;
LCD_ERR(" ============ start ===========\n");
list_for_each_entry(pipe, &mdp5_data->pipes_used, list) {
if (pipe && pipe->src_fmt)
LCD_ERR("[%4d, %4d, %4d, %4d] -> [%4d, %4d, %4d, %4d]"
"|flags = %8d|src_format = %2d|bpp = %2d|ndx = %3d|\n",
pipe->src.x, pipe->src.y, pipe->src.w, pipe->src.h,
pipe->dst.x, pipe->dst.y, pipe->dst.w, pipe->dst.h,
pipe->flags, pipe->src_fmt->format, pipe->src_fmt->bpp,
pipe->ndx);
LCD_ERR("pipe addr : %p\n", pipe);
pcount--;
if (!pcount)
break;
}
/*
LCD_ERR("mdp_clk = %ld, bus_ab = %llu, bus_ib = %llu\n", mdss_mdp_get_clk_rate(MDSS_CLK_MDP_SRC),
mdss_res->bus_scale_table->usecase[mdss_res->curr_bw_uc_idx].vectors[0].ab,
mdss_res->bus_scale_table->usecase[mdss_res->curr_bw_uc_idx].vectors[0].ib);
*/
LCD_ERR("============ end ===========\n");
}
#if 0
DEFINE_MUTEX(FENCE_LOCK);
static const char *sync_status_str(int status)
{
if (status > 0)
return "signaled";
else if (status == 0)
return "active";
else
return "error";
}
static void sync_pt_log(struct sync_pt *pt, bool pt_callback)
{
int status = pt->status;
pr_cont("[mdss DEBUG_FENCE] %s_pt %s",
pt->parent->name,
sync_status_str(status));
if (pt->status) {
struct timeval tv = ktime_to_timeval(pt->timestamp);
pr_cont("@%ld.%06ld", tv.tv_sec, tv.tv_usec);
}
if (pt->parent->ops->timeline_value_str &&
pt->parent->ops->pt_value_str) {
char value[64];
pt->parent->ops->pt_value_str(pt, value, sizeof(value));
pr_cont(": %s", value);
pt->parent->ops->timeline_value_str(pt->parent, value,
sizeof(value));
pr_cont(" / %s", value);
}
pr_cont("\n");
/* Show additional details for active fences */
if (pt->status == 0 && pt->parent->ops->pt_log && pt_callback)
pt->parent->ops->pt_log(pt);
}
void mdss_samsung_fence_dump(char *intf, struct sync_fence *fence)
{
struct sync_pt *pt;
struct list_head *pos;
mutex_lock(&FENCE_LOCK);
LCD_ERR("[mdss DEBUG_FENCE] %s : %s start\n", intf, fence->name);
list_for_each(pos, &fence->pt_list_head) {
pt = container_of(pos, struct sync_pt, pt_list);
sync_pt_log(pt, true);
}
LCD_ERR("[mdss DEBUG_FENCE] %s : %s end\n", intf, fence->name);
mutex_unlock(&FENCE_LOCK);
}
#endif
#include "../../../../staging/android/ion/ion_priv.h"
#include "../../../../staging/android/ion/ion.h"
struct ion_handle {
struct kref ref;
struct ion_client *client;
struct ion_buffer *buffer;
struct rb_node node;
unsigned int kmap_cnt;
int id;
};
static int WIDTH_COMPRESS_RATIO = 1;
static int HEIGHT_COMPRESS_RATIO = 1;
static inline char samsung_drain_image_888(struct mdss_mdp_pipe *pipe, char *drain_pos, int drain_x, int drain_y, int RGB)
{
char (*drain_pos_RGB888)[pipe->img_width][3] = (void *)drain_pos;
return drain_pos_RGB888[drain_y][drain_x][RGB];
}
static inline char samsung_drain_image_8888(struct mdss_mdp_pipe *pipe, char *drain_pos, int drain_x, int drain_y, int RGB)
{
char (*drain_pos_RGB8888)[pipe->img_width][4] = (void *)drain_pos;
return drain_pos_RGB8888[drain_y][drain_x][RGB];
}
void samsung_image_dump(void)
{
struct mdss_panel_info *panel_info = &vdd_data.ctrl_dsi[DISPLAY_1]->panel_data.panel_info;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(vdd_data.mfd_dsi[0]);
struct mdss_mdp_pipe *pipe;
struct mdss_mdp_data *buf;
int z_order;
int x, y;
int dst_x, dst_y;
int drain_x, drain_y, drain_max_x, drain_max_y;
int panel_x_res, panel_y_res;
char *dst_fb_pos = NULL;
char *drain_pos = NULL;
char (*dst_fb_pos_none_split)[panel_info->xres / WIDTH_COMPRESS_RATIO][3];
char (*dst_fb_pos_split)[(panel_info->xres * 2) / WIDTH_COMPRESS_RATIO][3];
struct ion_buffer *ion_buffers;
int split_mode, acquire_lock;
/*
cont_splash_mem: cont_splash_mem@0 {
linux,reserve-contiguous-region;
linux,reserve-region;
reg = <0 0x83000000 0 0x01C00000>;
label = "cont_splash_mem";
};
QC reserves tripple buffer for WQHD size at kernel. 0x2200000 is 34Mbyte.
So we uses second buffer to image dump.
*/
unsigned int buffer_offset, buffer_size;
struct BITMAPFILEHEADER bitmap;
int bitmap_y_reverse;
LCD_ERR("start\n");
if (vdd_data.mfd_dsi[0]->split_mode == MDP_DUAL_LM_DUAL_DISPLAY ||
vdd_data.mfd_dsi[0]->split_mode == MDP_PINGPONG_SPLIT) {
split_mode = true;
buffer_offset = ((panel_info->xres * 2) * panel_info->yres * 3);
panel_x_res = (panel_info->xres * 2) / WIDTH_COMPRESS_RATIO;
panel_y_res = panel_info->yres / HEIGHT_COMPRESS_RATIO;
buffer_size = panel_x_res * panel_y_res * 3;
} else {
split_mode = false;
buffer_offset = (panel_info->xres * panel_info->yres * 3);
panel_x_res = panel_info->xres / WIDTH_COMPRESS_RATIO;
panel_y_res = panel_info->yres / HEIGHT_COMPRESS_RATIO;
buffer_size = panel_x_res * panel_y_res * 3;
}
acquire_lock = mutex_trylock(&mdp5_data->list_lock);
/*
We use 2 frame to dump image.
First, dump image to First fb frame address
Second, copy to reversed data to Second fb Frame address from Frist fb frame address.
Because BMP format use upside-down & flipped format
*/
/* BMP HEADER ADDRESS */
dst_fb_pos = phys_to_virt(mdp5_data->splash_mem_addr + buffer_offset);
if (IS_ERR_OR_NULL(dst_fb_pos))
return;
/* Generate BMP format */
bitmap.bfType = BF_TYPE;
bitmap.bfSize = buffer_size + sizeof(struct BITMAPFILEHEADER);
bitmap.bfReserved1 = 0x00;
bitmap.bfReserved2 = 0x00;
bitmap.bfOffBits = sizeof(struct BITMAPFILEHEADER);
bitmap.biSize = 0x28;
bitmap.biWidth = panel_x_res;
bitmap.biHeight = panel_y_res;
bitmap.biPlanes = 0x01;
bitmap.biBitCount = 0x18;
bitmap.biCompression = 0x00;
bitmap.biSizeImage = buffer_size;
bitmap.biXPelsPerMeter = (1000 * panel_info->xres) / panel_info->physical_width;
bitmap.biYPelsPerMeter = (1000 * panel_info->yres) / panel_info->physical_height;
bitmap.biClrUsed = 0x00;
bitmap.biClrImportant = 0x00;
memcpy(dst_fb_pos, &bitmap, sizeof(struct BITMAPFILEHEADER));
/* fb frameaddress */
dst_fb_pos = phys_to_virt(mdp5_data->splash_mem_addr + buffer_offset + sizeof(struct BITMAPFILEHEADER));
if (IS_ERR_OR_NULL(dst_fb_pos))
return;
dst_fb_pos_none_split = (void *)dst_fb_pos;
dst_fb_pos_split = (void *)dst_fb_pos;
memset(dst_fb_pos, 0x00, buffer_size);
LCD_ERR("dst_pos : 0x%p split_mode : %s xres :%d yres :%d\n", dst_fb_pos,
vdd_data.mfd_dsi[0]->split_mode == 0 ? "MDP_SPLIT_MODE_NONE" :
vdd_data.mfd_dsi[0]->split_mode == 1 ? "MDP_DUAL_LM_SINGLE_DISPLAY" :
vdd_data.mfd_dsi[0]->split_mode == 2 ? "MDP_DUAL_LM_DUAL_DISPLAY" : "MDP_SPLIT_MODE_NONE",
panel_info->xres, panel_info->yres);
for (z_order = MDSS_MDP_STAGE_0; z_order < MDSS_MDP_MAX_STAGE; z_order++) {
list_for_each_entry(pipe, &mdp5_data->pipes_used, list) {
/* Check z-order */
if (pipe->mixer_stage != z_order)
continue;
LCD_ERR("L_mixer:%d R_mixer:%d %s z:%d format:%d %s flag:0x%x src.x:%d y:%d w:%d h:%d "
"des_rect.x:%d y:%d w:%d h:%d\n",
pipe->mixer_left ? pipe->mixer_left->num : -1,
pipe->mixer_right ? pipe->mixer_right->num : -1,
pipe->ndx == BIT(0) ? "VG0" : pipe->ndx == BIT(1) ? "VG1" :
pipe->ndx == BIT(2) ? "VG2" : pipe->ndx == BIT(3) ? "RGB0" :
pipe->ndx == BIT(4) ? "RGB1" : pipe->ndx == BIT(5) ? "RGB2" :
pipe->ndx == BIT(6) ? "DMA0" : pipe->ndx == BIT(7) ? "DMA1" :
pipe->ndx == BIT(8) ? "VG3" : pipe->ndx == BIT(9) ? "RGB3" :
pipe->ndx == BIT(10) ? "CURSOR0" : pipe->ndx == BIT(11) ? "CURSOR1" : "MAX_SSPP",
pipe->mixer_stage - MDSS_MDP_STAGE_0,
pipe->src_fmt->format,
pipe->src_fmt->format == MDP_RGB_888 ? "MDP_RGB_888" :
pipe->src_fmt->format == MDP_RGBX_8888 ? "MDP_RGBX_8888" :
pipe->src_fmt->format == MDP_Y_CRCB_H2V2 ? "MDP_Y_CRCB_H2V2" :
pipe->src_fmt->format == MDP_RGBA_8888 ? "MDP_RGBA_8888" :
pipe->src_fmt->format == MDP_Y_CBCR_H2V2_VENUS ? "MDP_Y_CBCR_H2V2_VENUS" : "NONE",
pipe->flags,
pipe->src.x, pipe->src.y, pipe->src.w, pipe->src.h,
pipe->dst.x, pipe->dst.y, pipe->dst.w, pipe->dst.h);
/* Check format */
if (pipe->src_fmt->format != MDP_RGB_888 &&
pipe->src_fmt->format != MDP_RGBA_8888 &&
pipe->src_fmt->format != MDP_RGBX_8888)
continue;
buf = list_first_entry_or_null(&pipe->buf_queue,
struct mdss_mdp_data, pipe_list);
if (IS_ERR_OR_NULL(buf))
continue;
#if defined(CONFIG_ARCH_MSM8996)
ion_buffers = (struct ion_buffer *)(buf->p[0].srcp_dma_buf->priv);
#else
ion_buffers = buf->p[0].srcp_ihdl->buffer;
#endif
if (!IS_ERR_OR_NULL(ion_buffers)) {
/* To avoid buffer free */
kref_get(&ion_buffers->ref);
drain_pos = ion_heap_map_kernel(NULL, ion_buffers);
if (!IS_ERR_OR_NULL(drain_pos)) {
dst_x = pipe->dst.x / WIDTH_COMPRESS_RATIO;
dst_y = pipe->dst.y / HEIGHT_COMPRESS_RATIO;
drain_x = pipe->src.x;
drain_y = pipe->src.y;
drain_max_x = dst_x + (pipe->dst.w / WIDTH_COMPRESS_RATIO);
drain_max_y = dst_y + (pipe->dst.h / HEIGHT_COMPRESS_RATIO);
/*
Because of BMP format, we save RGB to BGR & Flip(up -> donw) image
*/
if (pipe->src_fmt->format == MDP_RGB_888) {
if (split_mode) {
for (y = dst_y; y < drain_max_y; y++, drain_y += HEIGHT_COMPRESS_RATIO) {
bitmap_y_reverse = panel_y_res - y;
drain_x = pipe->src.x;
for (x = dst_x; x < drain_max_x; x++, drain_x += WIDTH_COMPRESS_RATIO) {
dst_fb_pos_split[bitmap_y_reverse][x][0] |= samsung_drain_image_888(pipe, drain_pos, drain_x, drain_y, 2); /* B */
dst_fb_pos_split[bitmap_y_reverse][x][1] |= samsung_drain_image_888(pipe, drain_pos, drain_x, drain_y, 1); /* G */
dst_fb_pos_split[bitmap_y_reverse][x][2] |= samsung_drain_image_888(pipe, drain_pos, drain_x, drain_y, 0); /* R */
}
}
} else {
for (y = dst_y; y < drain_max_y; y++, drain_y += HEIGHT_COMPRESS_RATIO) {
bitmap_y_reverse = panel_y_res - y;
drain_x = pipe->src.x;
for (x = dst_x; x < drain_max_x; x++, drain_x += WIDTH_COMPRESS_RATIO) {
dst_fb_pos_none_split[bitmap_y_reverse][x][0] |= samsung_drain_image_888(pipe, drain_pos, drain_x, drain_y, 2); /* B */
dst_fb_pos_none_split[bitmap_y_reverse][x][1] |= samsung_drain_image_888(pipe, drain_pos, drain_x, drain_y, 1); /* G */
dst_fb_pos_none_split[bitmap_y_reverse][x][2] |= samsung_drain_image_888(pipe, drain_pos, drain_x, drain_y, 0); /* R */
}
}
}
} else {
if (split_mode) {
for (y = dst_y; y < drain_max_y; y++, drain_y += HEIGHT_COMPRESS_RATIO) {
bitmap_y_reverse = panel_y_res - y;
drain_x = pipe->src.x;
for (x = dst_x; x < drain_max_x; x++, drain_x += WIDTH_COMPRESS_RATIO) {
dst_fb_pos_split[bitmap_y_reverse][x][0] |= samsung_drain_image_8888(pipe, drain_pos, drain_x, drain_y, 2); /* B */
dst_fb_pos_split[bitmap_y_reverse][x][1] |= samsung_drain_image_8888(pipe, drain_pos, drain_x, drain_y, 1); /* G */
dst_fb_pos_split[bitmap_y_reverse][x][2] |= samsung_drain_image_8888(pipe, drain_pos, drain_x, drain_y, 0); /* R */
}
}
} else {
for (y = dst_y; y < drain_max_y; y++, drain_y += HEIGHT_COMPRESS_RATIO) {
bitmap_y_reverse = panel_y_res - y;
drain_x = pipe->src.x;
for (x = dst_x; x < drain_max_x; x++, drain_x += WIDTH_COMPRESS_RATIO) {
dst_fb_pos_none_split[bitmap_y_reverse][x][0] |= samsung_drain_image_8888(pipe, drain_pos, drain_x, drain_y, 2); /* B */
dst_fb_pos_none_split[bitmap_y_reverse][x][1] |= samsung_drain_image_8888(pipe, drain_pos, drain_x, drain_y, 1); /* G */
dst_fb_pos_none_split[bitmap_y_reverse][x][2] |= samsung_drain_image_8888(pipe, drain_pos, drain_x, drain_y, 0); /* R */
}
}
}
}
ion_heap_unmap_kernel(NULL, ion_buffers);
} else
LCD_ERR("vmap fail");
} else
LCD_ERR("ion_buffer is NULL\n");
}
}
if (acquire_lock)
mutex_unlock(&mdp5_data->list_lock);
LCD_ERR("end\n");
}
void samsung_image_dump_worker(struct work_struct *work)
{
struct mdss_panel_info *panel_info = &vdd_data.ctrl_dsi[DISPLAY_1]->panel_data.panel_info;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(vdd_data.mfd_dsi[0]);
unsigned int image_size;
/* update compress ratio */
if (vdd_data.mfd_dsi[0]->split_mode == MDP_DUAL_LM_DUAL_DISPLAY ||
vdd_data.mfd_dsi[0]->split_mode == MDP_PINGPONG_SPLIT)
image_size = (panel_info->xres * 2) * panel_info->yres * 3;
else
image_size = panel_info->xres * panel_info->yres * 3;
if (image_size <= SZ_2M) {
WIDTH_COMPRESS_RATIO = 1;
HEIGHT_COMPRESS_RATIO = 1;
} else if (image_size <= SZ_8M) {
WIDTH_COMPRESS_RATIO = 2;
HEIGHT_COMPRESS_RATIO = 2;
} else {
WIDTH_COMPRESS_RATIO = 4;
HEIGHT_COMPRESS_RATIO = 4;
}
/* Clear image dump BMP format */
memset(phys_to_virt(mdp5_data->splash_mem_addr + image_size), 0x00, sizeof(struct BITMAPFILEHEADER));
if (in_interrupt()) {
LCD_ERR("in_interrupt()\n");
return;
}
/* real dump */
samsung_image_dump();
}
void samsung_mdss_image_dump(void)
{
static int dump_done = false;
#if defined(CONFIG_SEC_DEBUG)
if (!sec_debug_is_enabled())
return;
#endif
if (dump_done)
return;
else
dump_done = true;
if (in_interrupt()) {
if (!IS_ERR_OR_NULL(vdd_data.image_dump_workqueue)) {
queue_work(vdd_data.image_dump_workqueue, &vdd_data.image_dump_work);
LCD_ERR("dump_work queued\n");
}
} else
samsung_image_dump_worker(NULL);
}