// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) Samsung Electronics Co., Ltd. * JiHoon Kim * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "panel.h" #include "panel_drv.h" #include "panel_vrr.h" #include "panel_debug.h" #include "panel_bl.h" #ifdef CONFIG_EXYNOS_DECON_LCD_COPR #include "copr.h" #endif #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) #include "mdnie.h" #endif #ifdef CONFIG_PANEL_AID_DIMMING #include "dimming.h" #endif #ifdef CONFIG_SUPPORT_DDI_FLASH #include "panel_poc.h" #endif #ifdef CONFIG_EXTEND_LIVE_CLOCK #include "./aod/aod_drv.h" #endif #ifdef CONFIG_SUPPORT_POC_SPI #include "panel_spi.h" #endif #ifdef CONFIG_DISPLAY_USE_INFO #include "dpui.h" #endif #define INVALID_CELL_ID_STR ("0000000000") static DEFINE_MUTEX(sysfs_lock); char *mcd_rs_name[MAX_MCD_RS] = { "MCD1_R", "MCD1_L", "MCD2_R", "MCD2_L", }; #ifdef CONFIG_EXYNOS_LCD_ENG #ifdef CONFIG_SUPPORT_ISC_TUNE_TEST static const char *str_stm_fied[STM_FIELD_MAX] = { "stm_ctrl_en=", "stm_max_opt=", "stm_default_opt=", "stm_dim_step=", "stm_frame_period=", "stm_min_sect=", "stm_pixel_period=", "stm_line_period=", "stm_min_move=", "stm_m_thres=", "stm_v_thres=" }; static ssize_t isc_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.isc_threshold); return strlen(buf); } static ssize_t isc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc, ret; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (panel_data->props.isc_threshold == value) return size; mutex_lock(&panel->op_lock); panel_data->props.isc_threshold = value; mutex_unlock(&panel->op_lock); ret = panel_do_seqtbl_by_index(panel, PANEL_ISC_THRESHOLD_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write isc threshold seq\n"); return ret; } panel_info("isc N %d\n", panel_data->props.isc_threshold); return size; } int print_stm_info(u8 *stm_field, char *buf) { snprintf(buf, PAGE_SIZE, "CTRL EN=%d, MAX_OPT=%d, DEFAULT_OPT=%d, DIM_STEP=%d, FRAME_PERIOD=%d, MIN_SECT=%d, PIXEL_PERIOD=%d, LINE_PERIOD=%d, MIN_MOVE=%d, M_THRES=%d, V_THRES=%d\n", stm_field[STM_CTRL_EN], stm_field[STM_MAX_OPT], stm_field[STM_DEFAULT_OPT], stm_field[STM_DIM_STEP], stm_field[STM_FRAME_PERIOD], stm_field[STM_MIN_SECT], stm_field[STM_PIXEL_PERIOD], stm_field[STM_LINE_PERIOD], stm_field[STM_MIN_MOVE], stm_field[STM_M_THRES], stm_field[STM_V_THRES]); return strlen(buf); } static ssize_t stm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; return print_stm_info(panel_data->props.stm_field_info, buf); } int set_stm_info(char *user_set, u8 *stm_field) { int i; int val = 0, ret; for (i = STM_CTRL_EN; i < STM_FIELD_MAX; i++) { if (strncmp(user_set, str_stm_fied[i], strlen(str_stm_fied[i])) == 0) { ret = sscanf(user_set + strlen(str_stm_fied[i]), "%d", &val); stm_field[i] = val; return 0; } } return -EINVAL; } static ssize_t stm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); char *recv_buf; char *ptr = NULL; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; recv_buf = (char *)buf; while ((ptr = strsep(&recv_buf, " \t")) != NULL) { if (*ptr) { ret = set_stm_info(ptr, panel_data->props.stm_field_info); if (ret < 0) panel_info("invalid input %s\n", ptr); } } ret = panel_do_seqtbl_by_index(panel, PANEL_STM_TUNE_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write stm_tune\n"); return ret; } panel_info("n"); return size; } #endif unsigned char readbuf[256] = { 0xff, }; unsigned int readreg, readpos, readlen; static ssize_t read_mtp_show(struct device *dev, struct device_attribute *attr, char *buf) { int i, len; mutex_lock(&sysfs_lock); if (readreg <= 0 || readreg > 0xFF || readlen <= 0 || readlen > 0xFF || readpos > 0xFFFF) { mutex_unlock(&sysfs_lock); return -EINVAL; } len = snprintf(buf, PAGE_SIZE, "addr:0x%02X pos:%d size:%d\n", readreg, readpos, readlen); for (i = 0; i < readlen; i++) len += snprintf(buf + len, PAGE_SIZE - len, "0x%02X%s", readbuf[i], (((i + 1) % 16) == 0) || (i == readlen - 1) ? "\n" : " "); readreg = 0; readpos = 0; readlen = 0; mutex_unlock(&sysfs_lock); return len; } static ssize_t read_mtp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_device *panel = dev_get_drvdata(dev); int ret, i; if (!IS_PANEL_ACTIVE(panel)) return -EIO; mutex_lock(&sysfs_lock); ret = sscanf(buf, "%x %d %d", &readreg, &readpos, &readlen); if (ret != 3 || readreg <= 0 || readreg > 0xFF || readlen <= 0 || readlen > 0xFF || readpos > 0xFFFF) { ret = -EINVAL; panel_info("%x %d %d\n", readreg, readpos, readlen); goto store_err; } mutex_lock(&panel->op_lock); panel_set_key(panel, 3, true); ret = panel_rx_nbytes(panel, DSI_PKT_TYPE_RD, readbuf, readreg, readpos, readlen); panel_set_key(panel, 3, false); mutex_unlock(&panel->op_lock); if (unlikely(ret != readlen)) { panel_err("failed to read reg %02Xh pos %d len %d\n", readreg, readpos, readlen); ret = -EIO; goto store_err; } panel_info("READ_Reg addr: %02x, pos : %d len : %d\n", readreg, readpos, readlen); for (i = 0; i < readlen; i++) panel_info("READ_Reg %dth : %02x\n", i + 1, readbuf[i]); mutex_unlock(&sysfs_lock); return size; store_err: readreg = 0; readpos = 0; readlen = 0; mutex_unlock(&sysfs_lock); return ret; } static u8 WRITE_MTP_TX_DATA[512]; static DEFINE_STATIC_PACKET(write_mtp_tx_data, DSI_PKT_TYPE_WR, WRITE_MTP_TX_DATA, 0); static void *write_mtp_cmdtbl[] = { &PKTINFO(write_mtp_tx_data), }; struct seqinfo write_mtp_seqtbl[] = { SEQINFO_INIT("write-mtp-seq", write_mtp_cmdtbl), }; static ssize_t write_mtp_show(struct device *dev, struct device_attribute *attr, char *buf) { int i, len = 0, size, sz_buf = PAGE_SIZE; char *data = WRITE_MTP_TX_DATA; bool newline = true; mutex_lock(&sysfs_lock); size = PKTINFO(write_mtp_tx_data).dlen; for (i = 0; i < size; i++) { if (newline) len += snprintf(buf + len, sz_buf - len, "[%02Xh] ", i); len += snprintf(buf + len, sz_buf - len, "%02X", data[i] & 0xFF); if (!((i + 1) % 32) || (i + 1 == size)) { len += snprintf(buf + len, sz_buf - len, "\n"); newline = true; } else { len += snprintf(buf + len, sz_buf - len, " "); newline = false; } } mutex_unlock(&sysfs_lock); return len; } static ssize_t write_mtp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_device *panel = dev_get_drvdata(dev); int tx_len = 0, ret = 0; u32 val; char *p, *arg = (char *)buf; if (!IS_PANEL_ACTIVE(panel)) return -EIO; mutex_lock(&sysfs_lock); while ((p = strsep(&arg, " \t\n=")) != NULL) { if (!*p) continue; ret = sscanf(p, "%02x", &val); if (ret != 1) { panel_err("failed to scan payload %d\n", tx_len); ret = -EINVAL; goto err_write_mtp_store; } WRITE_MTP_TX_DATA[tx_len++] = val & 0xFF; if (tx_len >= ARRAY_SIZE(WRITE_MTP_TX_DATA)) break; } PKTINFO(write_mtp_tx_data).dlen = tx_len; mutex_lock(&panel->op_lock); ret = excute_seqtbl_nolock(panel, write_mtp_seqtbl, 0); mutex_unlock(&panel->op_lock); if (ret < 0) { panel_err("failed to excute write-mtp-seq(ret:%d)\n", ret); goto err_write_mtp_store; } panel_info("%d byte(s) sent.\n", tx_len); mutex_unlock(&sysfs_lock); return size; err_write_mtp_store: PKTINFO(write_mtp_tx_data).dlen = 0; memset(WRITE_MTP_TX_DATA, 0, sizeof(WRITE_MTP_TX_DATA)); mutex_unlock(&sysfs_lock); panel_info("failed to write_mtp\n"); return ret; } #endif //void g_tracing_mark_write( char id, char* str1, int value ); int fingerprint_value = -1; static ssize_t fingerprint_show(struct device *dev, struct device_attribute *attr, char *buf) { snprintf(buf, PAGE_SIZE, "%u\n", fingerprint_value); return strlen(buf); } static ssize_t fingerprint_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int rc; rc = kstrtouint(buf, 0, &fingerprint_value); if (rc < 0) return rc; //g_tracing_mark_write( 'C', "BCDS_hbm", fingerprint_value & 4); //g_tracing_mark_write( 'C', "BCDS_alpha", fingerprint_value & 2); panel_info("%d\n", fingerprint_value); return size; } static ssize_t lcd_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%c%c%c_%02X%02X%02X\n", panel_data->vendor[0], panel_data->vendor[1], panel_data->vendor[2], panel_data->id[0], panel_data->id[1], panel_data->id[2]); return strlen(buf); } #ifdef CONFIG_SUPPORT_MAFPC static ssize_t mafpc_time_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } snprintf(buf, PAGE_SIZE, "MAC : %lld usec", panel->mafpc_write_time); return strlen(buf); } static int mafpc_get_target_crc(struct panel_device *panel, u8 *crc) { struct mafpc_device *mafpc = NULL; v4l2_subdev_call(panel->mafpc_sd, core, ioctl, V4L2_IOCTL_MAFPC_GET_INFO, NULL); mafpc = (struct mafpc_device *)v4l2_get_subdev_hostdata(panel->mafpc_sd); if (mafpc == NULL) { panel_err("failed to get mafpc info\n"); return -EINVAL; } if (mafpc->comp_crc_len < MAFPC_CRC_LEN) { panel_err("crc len must be %d\n", MAFPC_CRC_LEN); return -EINVAL; } memcpy(crc, mafpc->comp_crc_buf, MAFPC_CRC_LEN); return 0; } #define MAFPC_CRC_LEN 2 static void prepare_mafpc_check_mode(struct panel_device *panel) { int ret; panel_dsi_set_bypass(panel, true); usleep_range(90000, 100000); ret = panel_disable_disp_det_irq(panel); if (ret < 0) panel_err("failed to disable disp_det irq\n"); ret = panel_disable_pcd_irq(panel); if (ret < 0) panel_err("failed to disable pcd irq\n"); ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_EXIT_SEQ); if (ret < 0) panel_err("failed exit-seq\n"); ret = __set_panel_power(panel, PANEL_POWER_OFF); if (ret < 0) panel_err("failed to set power off\n"); ret = __set_panel_power(panel, PANEL_POWER_ON); if (ret < 0) panel_err("failed to set power on\n"); ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_INIT_SEQ); if (ret < 0) panel_err("failed init-seq\n"); #ifdef CONFIG_EXTEND_LIVE_CLOCK ret = panel_aod_init_panel(panel, INIT_WITHOUT_LOCK); if (ret) panel_err("failed to aod init_panel\n"); #endif } static void clear_mafpc_check_mode(struct panel_device *panel) { int ret; panel->state.cur_state = PANEL_STATE_NORMAL; panel->state.disp_on = PANEL_DISPLAY_OFF; panel_dsi_set_bypass(panel, false); ret = panel_enable_gpio_irq(panel, PANEL_GPIO_DISP_DET); if (ret < 0) panel_warn("do not support irq\n"); ret = panel_enable_gpio_irq(panel, PANEL_GPIO_PCD); if (ret < 0) panel_warn("do not support irq\n"); msleep(20); } static ssize_t mafpc_check_show(struct device *dev, struct device_attribute *attr, char *buf) { int size = 0; int ret = 0; u8 target_crc[MAFPC_CRC_LEN] = {0, }; u8 origin_crc[MAFPC_CRC_LEN] = {1, }; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); panel_info("+\n"); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; mutex_lock(&panel->io_lock); if (!IS_PANEL_ACTIVE(panel)) { panel_err("panel is not active\n"); goto exit; } if (panel->state.cur_state == PANEL_STATE_ALPM) { panel_err("gct not supported on LPM\n"); goto exit; } #ifdef CONFIG_EXYNOS_DECON_LCD_COPR copr_disable(&panel->copr); #endif #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) mdnie_disable(&panel->mdnie); mutex_lock(&panel->mdnie.lock); #endif mutex_lock(&panel->op_lock); prepare_mafpc_check_mode(panel); ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_MAFPC_CHECKSUM_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write panel_mafpc_crc seq\n"); goto out; } ret = resource_copy_n_clear_by_name(panel_data, target_crc, "mafpc_crc"); if (unlikely(ret < 0)) { panel_err("failed to read mafpc crc\n"); goto out; } ret = mafpc_get_target_crc(panel, origin_crc); if (ret) panel_err("failed to get target mAFPC crc value\n"); panel_info("target crc : %x :%x\n", target_crc[0], target_crc[1]); panel_info("origin crc : %x :%x\n", origin_crc[0], origin_crc[1]); out: clear_mafpc_check_mode(panel); mutex_unlock(&panel->op_lock); #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) mutex_unlock(&panel->mdnie.lock); #endif exit: size = snprintf(buf, PAGE_SIZE, "%01d %02x %02x\n", memcmp(target_crc, origin_crc, MAFPC_CRC_LEN) == 0 ? 1 : 0, target_crc[0], target_crc[1]); mutex_unlock(&panel->io_lock); return size; } #endif static ssize_t window_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%02x %02x %02x\n", panel_data->id[0], panel_data->id[1], panel_data->id[2]); panel_info("%02x %02x %02x\n", panel_data->id[0], panel_data->id[1], panel_data->id[2]); return strlen(buf); } static ssize_t manufacture_code_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 code[5] = { 0, }; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; ret = resource_copy_by_name(panel_data, code, "code"); if (ret < 0) { panel_err("failed to copy resources\n"); snprintf(buf, PAGE_SIZE, "%s\n", INVALID_CELL_ID_STR); return strlen(buf); } snprintf(buf, PAGE_SIZE, "%02X%02X%02X%02X%02X\n", code[0], code[1], code[2], code[3], code[4]); return strlen(buf); } static ssize_t SVC_OCTA_DDI_CHIPID_show(struct device *dev, struct device_attribute *attr, char *buf) { return manufacture_code_show(dev, attr, buf); } static ssize_t cell_id_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 date[PANEL_DATE_LEN] = { 0, }, coordinate[4] = { 0, }; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; ret = resource_copy_by_name(panel_data, date, "date"); ret |= resource_copy_by_name(panel_data, coordinate, "coordinate"); if (ret < 0) { panel_err("failed to copy resources\n"); snprintf(buf, PAGE_SIZE, "%s\n", INVALID_CELL_ID_STR); return strlen(buf); } snprintf(buf, PAGE_SIZE, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", date[0], date[1], date[2], date[3], date[4], date[5], date[6], coordinate[0], coordinate[1], coordinate[2], coordinate[3]); return strlen(buf); } static ssize_t SVC_OCTA_show(struct device *dev, struct device_attribute *attr, char *buf) { return cell_id_show(dev, attr, buf); } static ssize_t octa_id_show(struct device *dev, struct device_attribute *attr, char *buf) { int i, site, rework, poc; u8 cell_id[16], octa_id[PANEL_OCTA_ID_LEN] = { 0xFF, }; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); int len = 0; bool cell_id_exist = true; int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; ret = resource_copy_by_name(panel_data, octa_id, "octa_id"); if (ret < 0) { panel_err("failed to copy resources\n"); snprintf(buf, PAGE_SIZE, "%s\n", INVALID_CELL_ID_STR); return strlen(buf); } site = (octa_id[0] >> 4) & 0x0F; rework = octa_id[0] & 0x0F; poc = octa_id[1] & 0x0F; panel_dbg("site (%d), rework (%d), poc (%d)\n", site, rework, poc); panel_dbg("\n"); for (i = 0; i < 16; i++) { cell_id[i] = isalnum(octa_id[i + 4]) ? octa_id[i + 4] : '\0'; panel_dbg("%x -> %c\n", octa_id[i + 4], cell_id[i]); if (cell_id[i] == '\0') { cell_id_exist = false; break; } } len += snprintf(buf + len, PAGE_SIZE - len, "%d%d%d%02x%02x", site, rework, poc, octa_id[2], octa_id[3]); if (cell_id_exist) { for (i = 0; i < 16; i++) len += snprintf(buf + len, PAGE_SIZE - len, "%c", cell_id[i]); } len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return strlen(buf); } static ssize_t SVC_OCTA_CHIPID_show(struct device *dev, struct device_attribute *attr, char *buf) { return octa_id_show(dev, attr, buf); } static ssize_t color_coordinate_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 coordinate[4] = { 0, }; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; resource_copy_by_name(panel_data, coordinate, "coordinate"); snprintf(buf, PAGE_SIZE, "%u, %u\n", /* X, Y */ coordinate[0] << 8 | coordinate[1], coordinate[2] << 8 | coordinate[3]); return strlen(buf); } static ssize_t manufacture_date_show(struct device *dev, struct device_attribute *attr, char *buf) { u16 year; u8 month, day, hour, min, date[PANEL_DATE_LEN] = { 0, }; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; resource_copy_by_name(panel_data, date, "date"); year = ((date[0] & 0xF0) >> 4) + 2011; month = date[0] & 0xF; day = date[1] & 0x1F; hour = date[2] & 0x1F; min = date[3] & 0x3F; snprintf(buf, PAGE_SIZE, "%d, %d, %d, %d:%d\n", year, month, day, hour, min); return strlen(buf); } static ssize_t brightness_table_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct panel_bl_device *panel_bl; int br, len = 0, recv_len = 0, prev_br = 0, temp = 0; int actual_brightness = 0, prev_actual_brightness = 0; char recv_buf[50] = {0, }; int recv_buf_len = ARRAY_SIZE(recv_buf); int max_brightness = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_bl = &panel->panel_bl; max_brightness = get_max_brightness(panel_bl); mutex_lock(&panel_bl->lock); for (br = 0; br <= max_brightness; br++) { actual_brightness = get_actual_brightness(panel_bl, br); if (recv_len == 0) { recv_len += snprintf(recv_buf, recv_buf_len, "%5d", prev_br); prev_actual_brightness = actual_brightness; } if ((prev_actual_brightness != actual_brightness) || (br == max_brightness)) { if (recv_len < recv_buf_len) { temp += snprintf(recv_buf + recv_len, recv_buf_len - recv_len, "~%5d %3d\n", prev_br, prev_actual_brightness); len += snprintf(buf + len, PAGE_SIZE - len, "%s", recv_buf); } recv_len = 0; memset(recv_buf, 0x00, sizeof(recv_buf)); } prev_actual_brightness = actual_brightness; prev_br = br; if (len >= PAGE_SIZE) { panel_info("print buffer overflow %d\n", len); len = PAGE_SIZE - 1; goto exit; } } len += snprintf(buf + len, PAGE_SIZE - len, "%s", recv_buf); exit: mutex_unlock(&panel_bl->lock); return len; } static ssize_t adaptive_control_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%d\n", panel_data->props.adaptive_control); return strlen(buf); } static ssize_t adaptive_control_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); struct panel_bl_device *panel_bl; int rc; u32 value; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; panel_bl = &panel->panel_bl; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (value > 100) { panel_err("invalid adaptive_control %d\n", value); return -EINVAL; } if (panel_data->props.adaptive_control == value) return size; mutex_lock(&panel_bl->lock); panel_data->props.adaptive_control = value; mutex_unlock(&panel_bl->lock); panel_update_brightness(panel); panel_info("adaptive_control %d\n", panel_data->props.adaptive_control); return size; } static ssize_t siop_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.siop_enable); return strlen(buf); } static ssize_t siop_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); int value, rc; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (panel_data->props.siop_enable == value) return size; mutex_lock(&panel->op_lock); panel_data->props.siop_enable = value; mutex_unlock(&panel->op_lock); panel_update_brightness(panel); panel_info("siop_enable %d\n", panel_data->props.siop_enable); return size; } static ssize_t temperature_show(struct device *dev, struct device_attribute *attr, char *buf) { char temp[] = "-15, -14, 0, 1\n"; strcat(buf, temp); return strlen(buf); } static ssize_t temperature_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); int value, rc; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtoint(buf, 10, &value); if (rc < 0) return rc; mutex_lock(&panel->op_lock); panel_data->props.temperature = value; mutex_unlock(&panel->op_lock); panel_update_brightness(panel); panel_info("temperature %d\n", panel_data->props.temperature); return size; } static ssize_t mcd_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.mcd_on); return strlen(buf); } static ssize_t mcd_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc, ret; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (panel_data->props.mcd_on == value) return size; mutex_lock(&panel->io_lock); #if 0 // temporary #ifdef CONFIG_PANEL_VRR_BRIDGE if ((value) && ((panel_data->props.vrr_fps != 60) || (panel_data->props.vrr_mode != VRR_NORMAL_MODE))) { // "mcd on" is only 60 Normal panel_info("request mcd on, but current %d %s mode\n", panel_data->props.vrr_fps, panel_data->props.vrr_mode ? "HS" : "Normal"); mutex_unlock(&panel->io_lock); return size; } #endif #endif mutex_lock(&panel->op_lock); panel_data->props.mcd_on = value; mutex_unlock(&panel->op_lock); ret = panel_do_seqtbl_by_index(panel, value ? PANEL_MCD_ON_SEQ : PANEL_MCD_OFF_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write mcd seq\n"); mutex_unlock(&panel->io_lock); return ret; } panel_info("mcd %s (%d %s mode)\n", panel_data->props.mcd_on ? "on" : "off", panel_data->props.vrr_fps, panel_data->props.vrr_mode ? "HS" : "Normal"); mutex_unlock(&panel->io_lock); return size; } static void print_mcd_resistance(u8 *mcd_nok, int size) { int code, len; char buf[1024]; len = snprintf(buf, sizeof(buf), "MCD CHECK [b7:MCD1_R, b6:MCD2_R, b3:MCD1_L, b2:MCD2_L]\n"); for (code = 0; code < size; code++) { if (!(code % 0x10)) len += snprintf(buf + len, sizeof(buf) - len, "[%02X] ", code); len += snprintf(buf + len, sizeof(buf) - len, "%02X%s", mcd_nok[code], (!((code + 1) % 0x10)) ? "\n" : " "); } panel_info("%s\n", buf); } static int read_mcd_resistance(struct panel_device *panel) { int i, ret, code; u8 mcd_nok[128]; struct panel_info *panel_data = &panel->panel_data; int stt, end; u8 mcd_rs_mask[MAX_MCD_RS] = { (1U << 7), (1U << 3), (1U << 6), (1U << 2), }; s64 elapsed_usec; struct timespec64 cur_ts, last_ts, delta_ts; ktime_get_ts64(&last_ts); ret = panel_disable_disp_det_irq(panel); if (ret < 0) panel_err("failed to disable disp_det irq\n"); ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_MCD_RS_ON_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write mcd_3_0_on seq\n"); goto out; } memset(mcd_nok, 0, sizeof(mcd_nok)); for (code = 0; code < 0x80; code++) { panel_data->props.mcd_resistance = code; ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_MCD_RS_READ_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write mcd_rs_read seq\n"); goto out; } ret = resource_copy_n_clear_by_name(panel_data, &mcd_nok[code], "mcd_resistance"); if (unlikely(ret < 0)) { panel_err("failed to copy resource(mcd_resistance) (ret %d)\n", ret); goto out; } panel_dbg("%02X : %02X\n", code, mcd_nok[code]); } print_mcd_resistance(mcd_nok, ARRAY_SIZE(mcd_nok)); for (i = 0; i < MAX_MCD_RS; i++) { for (code = 0, stt = -1, end = -1; code < 0x80; code++) { if (mcd_nok[code] & mcd_rs_mask[i]) { if (stt == -1) stt = code; end = code; } } panel_data->props.mcd_rs_range[i][0] = stt; panel_data->props.mcd_rs_range[i][1] = end; } ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_MCD_RS_OFF_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write mcd_3_0_off seq\n"); goto out; } out: ret = panel_enable_gpio_irq(panel, PANEL_GPIO_DISP_DET); if (ret < 0) panel_warn("do not support irq\n"); ktime_get_ts64(&cur_ts); delta_ts = timespec64_sub(cur_ts, last_ts); elapsed_usec = timespec64_to_ns(&delta_ts) / 1000; panel_info("done (elapsed %2lld.%03lld msec)\n", elapsed_usec / 1000, elapsed_usec % 1000); return ret; } static ssize_t mcd_resistance_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); int i, len = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; mutex_lock(&panel->op_lock); for (i = 0; i < MAX_MCD_RS; i++) len += snprintf(buf + len, PAGE_SIZE - len, "SDC_%s:%d%s", mcd_rs_name[i], panel_data->props.mcd_rs_flash_range[i][1], (i != MAX_MCD_RS - 1) ? " " : "\n"); for (i = 0; i < MAX_MCD_RS; i++) len += snprintf(buf + len, PAGE_SIZE - len, "%s:%d%s", mcd_rs_name[i], panel_data->props.mcd_rs_range[i][1], (i != MAX_MCD_RS - 1) ? " " : "\n"); mutex_unlock(&panel->op_lock); return len; } static ssize_t mcd_resistance_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int i, value, rc, ret; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); #ifdef CONFIG_SUPPORT_DDI_FLASH u8 flash_mcd[8]; #endif if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (!!value) { mutex_lock(&panel->op_lock); /* clear variable */ memset(panel_data->props.mcd_rs_range, -1, sizeof(panel_data->props.mcd_rs_range)); ret = read_mcd_resistance(panel); mutex_unlock(&panel->op_lock); if (unlikely(ret < 0)) { panel_err("failed to check mcd resistance\n"); return ret; } for (i = 0; i < MAX_MCD_RS; i++) panel_info("%s:(%d, %d)\n", mcd_rs_name[i], panel_data->props.mcd_rs_range[i][0], panel_data->props.mcd_rs_range[i][1]); #ifdef CONFIG_SUPPORT_DDI_FLASH ret = set_panel_poc(&panel->poc_dev, POC_OP_MCD_READ, NULL); if (unlikely(ret)) { panel_err("failed to read mcd(ret %d)\n", ret); return ret; } ret = panel_resource_update_by_name(panel, "flash_mcd"); if (unlikely(ret < 0)) { panel_err("failed to update flash_mcd res (ret %d)\n", ret); return ret; } ret = resource_copy_by_name(&panel->panel_data, flash_mcd, "flash_mcd"); if (unlikely(ret < 0)) { panel_err("failed to copy flash_mcd res (ret %d)\n", ret); return ret; } panel_data->props.mcd_rs_flash_range[MCD_RS_1_RIGHT][1] = flash_mcd[0]; panel_data->props.mcd_rs_flash_range[MCD_RS_2_RIGHT][1] = flash_mcd[1]; panel_data->props.mcd_rs_flash_range[MCD_RS_1_LEFT][1] = flash_mcd[4]; panel_data->props.mcd_rs_flash_range[MCD_RS_2_LEFT][1] = flash_mcd[5]; for (i = 0; i < MAX_MCD_RS; i++) panel_info("SDC_%s:(%d, %d)\n", mcd_rs_name[i], panel_data->props.mcd_rs_flash_range[i][0], panel_data->props.mcd_rs_flash_range[i][1]); #endif } return size; } static ssize_t irc_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.irc_mode); return strlen(buf); } static ssize_t irc_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); panel_dbg(" ++\n"); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; mutex_lock(&panel->op_lock); panel_data->props.irc_mode = !!value; mutex_unlock(&panel->op_lock); panel_update_brightness(panel); #ifdef CONFIG_PANEL_NOTIFY panel_send_screen_mode_notify(panel->id, panel_data->props.irc_mode); #endif panel_info("irc_mode %s\n", panel_data->props.irc_mode ? "on" : "off"); panel_dbg(" --\n"); return size; } static ssize_t dia_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.dia_mode); return strlen(buf); } static ssize_t dia_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc, ret; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; mutex_lock(&panel->op_lock); panel_data->props.dia_mode = value; mutex_unlock(&panel->op_lock); ret = panel_do_seqtbl_by_index(panel, PANEL_DIA_ONOFF_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write mcd seq\n"); return ret; } panel_info("set %s\n", panel_data->props.dia_mode ? "on" : "off"); return size; } static ssize_t partial_disp_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.panel_partial_disp); return strlen(buf); } static ssize_t partial_disp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc, ret; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (value != panel_data->props.panel_partial_disp) { mutex_lock(&panel->op_lock); panel_data->props.panel_partial_disp = value; mutex_unlock(&panel->op_lock); ret = panel_do_seqtbl_by_index(panel, value ? PANEL_PARTIAL_DISP_ON_SEQ : PANEL_PARTIAL_DISP_OFF_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write mcd seq\n"); return ret; } panel_info("set %s\n", panel_data->props.panel_partial_disp ? "on" : "off"); } else { panel_info("already set %s, so skip\n", panel_data->props.panel_partial_disp ? "on" : "off"); } return size; } #ifdef CONFIG_SUPPORT_MST ssize_t mst_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.mst_on); return strlen(buf); } ssize_t mst_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc, ret; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (panel_data->props.mst_on == value) return size; mutex_lock(&panel->op_lock); panel_data->props.mst_on = value; mutex_unlock(&panel->op_lock); ret = panel_do_seqtbl_by_index(panel, value ? PANEL_MST_ON_SEQ : PANEL_MST_OFF_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write mst seq\n"); return ret; } panel_info("mst %s\n", panel_data->props.mst_on ? "on" : "off"); return size; } #endif #if defined(CONFIG_SUPPORT_GRAM_CHECKSUM) || defined(CONFIG_SUPPORT_PANEL_DECODER_TEST) static void prepare_gct_mode(struct panel_device *panel) { int ret; panel_dsi_set_commit_retry(panel, true); panel_dsi_set_bypass(panel, true); usleep_range(90000, 100000); ret = panel_disable_disp_det_irq(panel); if (ret < 0) panel_err("failed to disable disp_det irq\n"); ret = panel_disable_pcd_irq(panel); if (ret < 0) panel_err("failed to disable pcd irq\n"); } static void clear_gct_mode(struct panel_device *panel) { struct panel_info *panel_data = &panel->panel_data; int ret; ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_EXIT_SEQ); if (ret < 0) panel_err("failed exit-seq\n"); ret = __set_panel_power(panel, PANEL_POWER_OFF); if (ret < 0) panel_err("failed to set power off\n"); ret = __set_panel_power(panel, PANEL_POWER_ON); if (ret < 0) panel_err("failed to set power on\n"); ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_INIT_SEQ); if (ret < 0) panel_err("failed init-seq\n"); #ifdef CONFIG_EXTEND_LIVE_CLOCK ret = panel_aod_init_panel(panel, INIT_WITHOUT_LOCK); if (ret) panel_err("failed to aod init_panel\n"); #endif ret = panel_enable_gpio_irq(panel, PANEL_GPIO_DISP_DET); if (ret < 0) panel_warn("do not support irq\n"); ret = panel_enable_gpio_irq(panel, PANEL_GPIO_PCD); if (ret < 0) panel_warn("do not support irq\n"); panel_data->props.gct_on = GRAM_TEST_OFF; panel->state.cur_state = PANEL_STATE_NORMAL; panel->state.disp_on = PANEL_DISPLAY_OFF; panel_dsi_set_bypass(panel, false); panel_dsi_set_commit_retry(panel, false); msleep(20); } #endif #ifdef CONFIG_SUPPORT_GRAM_CHECKSUM u8 checksum[4] = { 0x12, 0x34, 0x56, 0x78 }; static bool gct_chksum_is_valid(struct panel_device *panel) { int i; struct panel_info *panel_data = &panel->panel_data; for (i = 0; i < 4; i++) if (checksum[i] != panel_data->props.gct_valid_chksum[i]) return false; return true; } static ssize_t gct_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u 0x%08x\n", panel_data->props.gct_on == GRAM_TEST_SKIPPED || gct_chksum_is_valid(panel) ? 1 : 0, ntohl(*(u32 *)checksum)); panel_info("%s", buf); return strlen(buf); } static ssize_t gct_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc, ret, result = 0; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); struct seqinfo *seqtbl; int i, index = 0, vddm = 0, pattern = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (value != GRAM_TEST_ON) return -EINVAL; if (!check_seqtbl_exist(panel_data, PANEL_GCT_ENTER_SEQ)) { panel_warn("cannot found gct seq. skip\n"); panel_data->props.gct_on = GRAM_TEST_SKIPPED; return -EINVAL; } panel_info("++"); /* clear checksum buffer */ checksum[0] = 0x12; checksum[1] = 0x34; checksum[2] = 0x56; checksum[3] = 0x78; mutex_lock(&panel->io_lock); if (!IS_PANEL_ACTIVE(panel)) { panel_err("panel is not active\n"); mutex_unlock(&panel->io_lock); return -EAGAIN; } if (panel->state.cur_state == PANEL_STATE_ALPM) { panel_warn("gct not supported on LPM\n"); mutex_unlock(&panel->io_lock); return -EINVAL; } #ifdef CONFIG_EXYNOS_DECON_LCD_COPR copr_disable(&panel->copr); #endif #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) mdnie_disable(&panel->mdnie); mutex_lock(&panel->mdnie.lock); #endif mutex_lock(&panel->op_lock); prepare_gct_mode(panel); panel_data->props.gct_on = value; #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) #ifdef CONFIG_SUPPORT_AFC if (panel->mdnie.props.afc_on && panel->mdnie.nr_seqtbl > MDNIE_AFC_OFF_SEQ) { panel_info("afc off\n"); ret = panel_do_seqtbl(panel, &panel->mdnie.seqtbl[MDNIE_AFC_OFF_SEQ]); if (unlikely(ret < 0)) panel_err("failed to write afc off seqtbl\n"); } #endif #endif ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_GCT_ENTER_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write gram-checksum-test-enter seq\n"); result = ret; goto out; } for (vddm = VDDM_LV; vddm < MAX_VDDM; vddm++) { panel_data->props.gct_vddm = vddm; ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_GCT_VDDM_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write gram-checksum-on seq\n"); result = ret; goto out; } for (pattern = GCT_PATTERN_1; pattern < MAX_GCT_PATTERN; pattern++) { panel_data->props.gct_pattern = pattern; seqtbl = find_index_seqtbl(&panel->panel_data, PANEL_GCT_IMG_UPDATE_SEQ); ret = panel_do_seqtbl_by_index_nolock(panel, (seqtbl && seqtbl->cmdtbl) ? PANEL_GCT_IMG_UPDATE_SEQ : ((pattern == GCT_PATTERN_1) ? PANEL_GCT_IMG_0_UPDATE_SEQ : PANEL_GCT_IMG_1_UPDATE_SEQ)); if (unlikely(ret < 0)) { panel_err("failed to write gram-img-update seq\n"); result = ret; goto out; } ret = resource_copy_n_clear_by_name(panel_data, &checksum[index], "gram_checksum"); if (unlikely(ret < 0)) { panel_err("failed to copy gram_checksum[%d] (ret %d)\n", index, ret); result = ret; goto out; } panel_info("gram_checksum[%d] 0x%x\n", index, checksum[index]); index++; } } ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_GCT_EXIT_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write gram-checksum-off seq\n"); result = ret; } out: clear_gct_mode(panel); mutex_unlock(&panel->op_lock); #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) mutex_unlock(&panel->mdnie.lock); #endif mutex_unlock(&panel->io_lock); if (panel_data->ddi_props.support_avoid_sandstorm) { panel_info("display on\n"); ret = panel_display_on(panel); if (ret < 0) panel_err("failed to display on\n"); } else { panel_info("wait display on\n"); for (i = 0; i < 20; i++) { if (panel->state.disp_on == PANEL_DISPLAY_ON) break; msleep(50); } if (i == 20) { panel_info("display on\n"); ret = panel_display_on(panel); if (ret < 0) panel_err("failed to display on\n"); } } if (result < 0) return result; panel_info("-- chksum %s 0x%08x\n", gct_chksum_is_valid(panel) ? "ok" : "nok", ntohl(*(u32 *)checksum)); if (!gct_chksum_is_valid(panel)) panel_dsi_print_dpu_event_log(panel); return size; } #endif #ifdef CONFIG_SUPPORT_PANEL_DECODER_TEST int decoder_test_result = 0; char decoder_test_result_str[32] = { 0, }; static ssize_t dsc_crc_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%d %s\n", decoder_test_result, decoder_test_result_str); panel_info("%s", buf); return strlen(buf); } static ssize_t dsc_crc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc, ret = 0, i; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); ret = -EINVAL; goto exit; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (value != DECODER_TEST_ON) { ret = -EINVAL; goto exit; } memset(decoder_test_result_str, 0, ARRAY_SIZE(decoder_test_result_str)); if (!check_panel_decoder_test_exists(panel)) { panel_warn("cannot found dsc_crc test seq. skip\n"); decoder_test_result = 0; snprintf(decoder_test_result_str, ARRAY_SIZE(decoder_test_result_str), "0"); ret = -EINVAL; goto exit; } panel_info("++"); /* clear checksum buffer */ mutex_lock(&panel->io_lock); if (!IS_PANEL_ACTIVE(panel)) { panel_err("panel is not active\n"); mutex_unlock(&panel->io_lock); ret = -EAGAIN; goto exit; } if (panel->state.cur_state == PANEL_STATE_ALPM) { panel_warn("dsc_crc not supported on LPM\n"); mutex_unlock(&panel->io_lock); ret = -EINVAL; goto exit; } #ifdef CONFIG_EXYNOS_DECON_LCD_COPR copr_disable(&panel->copr); #endif #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) mdnie_disable(&panel->mdnie); mutex_lock(&panel->mdnie.lock); #endif mutex_lock(&panel->op_lock); prepare_gct_mode(panel); #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) #ifdef CONFIG_SUPPORT_AFC if (panel->mdnie.props.afc_on && panel->mdnie.nr_seqtbl > MDNIE_AFC_OFF_SEQ) { panel_info("afc off\n"); ret = panel_do_seqtbl(panel, &panel->mdnie.seqtbl[MDNIE_AFC_OFF_SEQ]); if (unlikely(ret < 0)) panel_err("failed to write afc off seqtbl\n"); } #endif #endif decoder_test_result = panel_decoder_test(panel, decoder_test_result_str, ARRAY_SIZE(decoder_test_result_str)); panel_info("-- chksum %s %s\n", (decoder_test_result > 0) ? "ok" : "nok", decoder_test_result_str); clear_gct_mode(panel); mutex_unlock(&panel->op_lock); #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) mutex_unlock(&panel->mdnie.lock); #endif mutex_unlock(&panel->io_lock); if (panel_data->ddi_props.support_avoid_sandstorm) { panel_info("display on\n"); ret = panel_display_on(panel); if (ret < 0) panel_err("failed to display on\n"); } else { panel_info("wait display on\n"); for (i = 0; i < 20; i++) { if (panel->state.disp_on == PANEL_DISPLAY_ON) break; msleep(50); } if (i == 20) { panel_info("display on\n"); ret = panel_display_on(panel); if (ret < 0) panel_err("failed to display on\n"); } } if (decoder_test_result < 0) { panel_info("ret %d\n", decoder_test_result); panel_dsi_print_dpu_event_log(panel); } exit: if (ret < 0) return ret; return size; } #endif #ifdef CONFIG_SUPPORT_XTALK_MODE static ssize_t xtalk_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.xtalk_mode); return strlen(buf); } static ssize_t xtalk_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc; struct panel_info *panel_data; struct panel_bl_device *panel_bl; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; panel_bl = &panel->panel_bl; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (panel_data->props.xtalk_mode == value) return size; mutex_lock(&panel_bl->lock); panel_data->props.xtalk_mode = value; mutex_unlock(&panel_bl->lock); panel_update_brightness(panel); panel_info("xtalk_mode %d\n", panel_data->props.xtalk_mode); return size; } #endif #ifdef CONFIG_SUPPORT_POC_FLASH static ssize_t poc_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct panel_poc_device *poc_dev; struct panel_poc_info *poc_info; int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } if (!IS_PANEL_ACTIVE(panel)) { panel_err("panel is not active\n"); return -EAGAIN; } poc_dev = &panel->poc_dev; poc_info = &poc_dev->poc_info; ret = set_panel_poc(poc_dev, POC_OP_CHECKPOC, NULL); if (unlikely(ret < 0)) { panel_err("failed to chkpoc (ret %d)\n", ret); return ret; } ret = set_panel_poc(poc_dev, POC_OP_CHECKSUM, NULL); if (unlikely(ret < 0)) { panel_err("failed to chksum (ret %d)\n", ret); return ret; } snprintf(buf, PAGE_SIZE, "%d %d %02x\n", poc_info->poc, poc_info->poc_chksum[4], poc_info->poc_ctrl[3]); panel_info("poc:%d chk:%d gray:%02x\n", poc_info->poc, poc_info->poc_chksum[4], poc_info->poc_ctrl[3]); return strlen(buf); } static ssize_t poc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_device *panel = dev_get_drvdata(dev); struct panel_info *panel_data; struct panel_poc_device *poc_dev; struct panel_poc_info *poc_info; #ifdef CONFIG_SUPPORT_POC_SPI struct panel_spi_dev *spi_dev = &panel->panel_spi_dev; #endif int rc, ret; unsigned int value; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; poc_dev = &panel->poc_dev; poc_info = &poc_dev->poc_info; rc = sscanf(buf, "%u", &value); if (rc < 1) { panel_err("poc_op required\n"); return -EINVAL; } if (!IS_VALID_POC_OP(value)) { panel_warn("invalid poc_op %d\n", value); return -EINVAL; } if (value == POC_OP_WRITE || value == POC_OP_READ) { panel_warn("unsupported poc_op %d\n", value); return size; } #ifdef CONFIG_SUPPORT_POC_SPI if (value == POC_OP_SET_SPI_SPEED) { rc = sscanf(buf, "%*u %u", &value); if (rc < 1) { panel_warn("SET_SPI_SPEED need 2 params\n"); return -EINVAL; } spi_dev->spi_info.speed_hz = value; value = POC_OP_SET_SPI_SPEED; return size; } #endif if (value == POC_OP_CANCEL) { atomic_set(&poc_dev->cancel, 1); } else { mutex_lock(&panel->io_lock); ret = set_panel_poc(poc_dev, value, (void *)buf); if (unlikely(ret < 0)) { panel_err("failed to poc_op %d(ret %d)\n", value, ret); mutex_unlock(&panel->io_lock); return -EINVAL; } mutex_unlock(&panel->io_lock); } mutex_lock(&panel->op_lock); panel_data->props.poc_op = value; mutex_unlock(&panel->op_lock); panel_info("poc_op %d\n", panel_data->props.poc_op); return size; } static ssize_t poc_mca_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); int ret; u8 chksum_data[256]; int i, len, ofs = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } if (!IS_PANEL_ACTIVE(panel)) { panel_err("panel is not active\n"); return -EAGAIN; } panel_set_key(panel, 2, true); ret = panel_resource_update_by_name(panel, "poc_mca_chksum"); if (unlikely(ret < 0)) { panel_err("failed to update poc_mca_chksum res (ret %d)\n", ret); return ret; } panel_set_key(panel, 2, false); ret = resource_copy_by_name(&panel->panel_data, chksum_data, "poc_mca_chksum"); if (unlikely(ret < 0)) { panel_err("failed to copy poc_mca_chksum res (ret %d)\n", ret); return ret; } len = get_resource_size_by_name(&panel->panel_data, "poc_mca_chksum"); for (i = 0; i < len; i++) ofs += snprintf(buf + ofs, PAGE_SIZE - ofs, "%02X ", chksum_data[i]); ofs += snprintf(buf + ofs, PAGE_SIZE - ofs, "\n"); panel_info("poc_mca_checksum: %s", buf); return ofs; } static ssize_t poc_info_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct panel_poc_device *poc_dev; struct panel_poc_info *poc_info; int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } poc_dev = &panel->poc_dev; poc_info = &poc_dev->poc_info; ret = get_poc_partition_size(poc_dev, POC_IMG_PARTITION); if (unlikely(ret < 0)) { panel_err("failed to get poc partition size (ret %d)\n", ret); return ret; } snprintf(buf, PAGE_SIZE, "poc_mca_image_size %d\n", ret); panel_info("%s\n", buf); return strlen(buf); } #endif static ssize_t gamma_check_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } ret = panel_mtp_gamma_check(panel); panel_info("ret %d\n", ret); snprintf(buf, PAGE_SIZE, "%d\n", ret); return strlen(buf); } #ifdef CONFIG_SUPPORT_SSR_TEST static ssize_t ssr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } ret = panel_ssr_test(panel); panel_info("ret %d\n", ret); snprintf(buf, PAGE_SIZE, "%d\n", ret); return strlen(buf); } #endif #ifdef CONFIG_SUPPORT_ECC_TEST static ssize_t ecc_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } ret = panel_ecc_test(panel); panel_info("ret %d\n", ret); snprintf(buf, PAGE_SIZE, "%d\n", ret); return strlen(buf); } #endif /* * gamma_flash check function for gamma-mode2 data */ static ssize_t gamma_flash_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct dim_flash_result *result; int size = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } result = &panel->flash_checksum_result; panel_info("%s\n", result->result); size += snprintf(buf + size, PAGE_SIZE - size, "%s\n", result->result); return size; } static ssize_t gamma_flash_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_device *panel = dev_get_drvdata(dev); int rc; unsigned int value; if (!IS_PANEL_ACTIVE(panel)) return -ENODEV; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; panel_info("+ %u\n", value); if (value == 1) { rc = panel_flash_checksum_calc(panel); if (rc < 0) { panel_info("failed %d\n", rc); return rc; } } return size; } #ifdef CONFIG_SUPPORT_GRAYSPOT_TEST static ssize_t grayspot_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.grayspot); return strlen(buf); } static ssize_t grayspot_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc, ret; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (panel_data->props.grayspot == value) return size; mutex_lock(&panel->op_lock); panel_data->props.grayspot = value; mutex_unlock(&panel->op_lock); ret = panel_do_seqtbl_by_index(panel, value ? PANEL_GRAYSPOT_ON_SEQ : PANEL_GRAYSPOT_OFF_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write grayspot on/off seq\n"); return ret; } panel_info("grayspot %s\n", panel_data->props.grayspot ? "on" : "off"); return size; } #endif #ifdef CONFIG_SUPPORT_HMD static ssize_t hmt_bright_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_bl_device *panel_bl; struct panel_device *panel = dev_get_drvdata(dev); int size; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_bl = &panel->panel_bl; mutex_lock(&panel_bl->lock); if (panel_bl->props.id == PANEL_BL_SUBDEV_TYPE_DISP) { size = snprintf(buf, 30, "HMD off state\n"); } else { size = snprintf(buf, PAGE_SIZE, "index : %d, brightenss : %d\n", panel_bl->props.actual_brightness_index, BRT_USER(panel_bl->props.brightness)); } mutex_unlock(&panel_bl->lock); return size; } static ssize_t hmt_bright_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret; int value, rc; struct panel_bl_device *panel_bl; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_bl = &panel->panel_bl; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; panel_info("brightness : %d\n", value); mutex_lock(&panel_bl->lock); mutex_lock(&panel->op_lock); if (panel_bl->subdev[PANEL_BL_SUBDEV_TYPE_HMD].brightness != BRT(value)) panel_bl->subdev[PANEL_BL_SUBDEV_TYPE_HMD].brightness = BRT(value); if (panel->state.hmd_on != PANEL_HMD_ON) { panel_info("hmd off\n"); goto exit_store; } ret = panel_bl_set_brightness(panel_bl, PANEL_BL_SUBDEV_TYPE_HMD, SEND_CMD); if (ret) { panel_err("fail to set brightness\n"); goto exit_store; } exit_store: mutex_unlock(&panel->op_lock); mutex_unlock(&panel_bl->lock); return size; } static ssize_t hmt_on_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_state *state; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } state = &panel->state; snprintf(buf, PAGE_SIZE, "%u\n", state->hmd_on); return strlen(buf); } static ssize_t hmt_on_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret; int value, rc; struct backlight_device *bd; struct panel_state *state; struct panel_device *panel = dev_get_drvdata(dev); struct panel_bl_device *panel_bl; struct panel_vrr *vrr; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_bl = &panel->panel_bl; bd = panel_bl->bd; state = &panel->state; rc = kstrtoint(buf, 0, &value); if (rc < 0) return rc; if (value != PANEL_HMD_OFF && value != PANEL_HMD_ON) { panel_err("invalid parameter %d\n", value); return -EINVAL; } panel_info("hmd set to: %s\n", (value == PANEL_HMD_ON) ? "on" : "off"); panel_flush_image(panel); mutex_lock(&panel_bl->lock); mutex_lock(&panel->op_lock); if (value == state->hmd_on) { panel_warn("already set : %d\n", value); ret = -EBUSY; goto exit; } vrr = get_panel_vrr(panel); if (vrr != NULL && value == PANEL_HMD_ON) { if (vrr->fps != 60 || vrr->mode != 0) { panel_err("failed to set hmd %s: fps %d mode %d\n", (value == PANEL_HMD_ON) ? "on" : "off", vrr->fps, vrr->mode); ret = -EINVAL; goto exit; } } ret = panel_do_seqtbl_by_index_nolock(panel, (value == PANEL_HMD_ON) ? PANEL_HMD_ON_SEQ : PANEL_HMD_OFF_SEQ); if (ret < 0) panel_err("failed to set hmd %s seq\n", (value == PANEL_HMD_ON) ? "on" : "off"); ret = panel_bl_set_brightness(panel_bl, (value == PANEL_HMD_ON) ? PANEL_BL_SUBDEV_TYPE_HMD : PANEL_BL_SUBDEV_TYPE_DISP, SEND_CMD); if (ret < 0) panel_err("failed to set %s brightness\n", (value == PANEL_HMD_ON) ? "hmd" : "normal"); panel_info("hmd_on %d -> %d\n", state->hmd_on, value); state->hmd_on = value; exit: mutex_unlock(&panel->op_lock); mutex_unlock(&panel_bl->lock); if (ret < 0) return ret; return size; } #endif /* CONFIG_SUPPORT_HMD */ #ifdef CONFIG_MCD_PANEL_LPM static int set_alpm_mode(struct panel_device *panel, int mode) { int ret = 0; #ifdef CONFIG_SUPPORT_AOD_BL int lpm_ver = (mode & 0x00FF0000) >> 16; #endif int lpm_mode = (mode & 0xFF); #ifdef CONFIG_MCD_PANEL_FACTORY static int backup_br; #endif struct panel_info *panel_data = &panel->panel_data; #ifdef CONFIG_SUPPORT_AOD_BL struct panel_bl_device *panel_bl = &panel->panel_bl; #endif #if defined(CONFIG_MCD_PANEL_FACTORY) || defined(CONFIG_SUPPORT_AOD_BL) struct backlight_device *bd = panel_bl->bd; #endif switch (lpm_mode) { case ALPM_OFF: #ifdef CONFIG_MCD_PANEL_FACTORY ret = panel_seq_exit_alpm(panel); if (ret) panel_err("failed to write set_alpm\n"); if (backup_br) bd->props.brightness = backup_br; #endif panel_data->props.alpm_mode = lpm_mode; panel_update_brightness(panel); #ifdef CONFIG_MCD_PANEL_FACTORY msleep(34); #endif break; case ALPM_LOW_BR: case HLPM_LOW_BR: case ALPM_HIGH_BR: case HLPM_HIGH_BR: panel_data->props.alpm_mode = lpm_mode; #ifndef CONFIG_MCD_PANEL_FACTORY if (panel->state.cur_state != PANEL_STATE_ALPM) { panel_info("panel state(%d) is not lpm mode\n", panel->state.cur_state); return ret; } #endif #ifdef CONFIG_MCD_PANEL_FACTORY backup_br = bd->props.brightness; #endif #ifdef CONFIG_SUPPORT_AOD_BL if (lpm_ver == 0) { bd->props.brightness = (lpm_mode <= HLPM_LOW_BR) ? BRT(0) : BRT(94); panel_bl->subdev[PANEL_BL_SUBDEV_TYPE_AOD].brightness = (lpm_mode <= HLPM_LOW_BR) ? BRT(0) : BRT(94); } #endif #ifdef CONFIG_MCD_PANEL_FACTORY ret = panel_seq_set_alpm(panel); if (ret) panel_err("failed to set_alpm\n"); #endif break; default: panel_err("invalid alpm_mode: %d\n", lpm_mode); break; } return ret; } #endif static ssize_t alpm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc; struct panel_device *panel = dev_get_drvdata(dev); struct panel_info *panel_data = &panel->panel_data; panel_info("++\n"); mutex_lock(&panel->io_lock); rc = kstrtoint(buf, 0, &value); if (rc < 0) { panel_warn("invalid param (ret %d)\n", rc); mutex_unlock(&panel->io_lock); return rc; } #ifdef CONFIG_MCD_PANEL_LPM rc = set_alpm_mode(panel, value); if (rc) panel_err("failed to set alpm (value %d, ret %d)\n", value, rc); #endif mutex_unlock(&panel->io_lock); panel_info("value %d, alpm_mode %d\n", value, panel_data->props.alpm_mode); return size; } static ssize_t alpm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%d\n", panel_data->props.alpm_mode); panel_dbg("%d\n", panel_data->props.alpm_mode); return strlen(buf); } static ssize_t lpm_opr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc; struct panel_device *panel = dev_get_drvdata(dev); struct panel_info *panel_data = &panel->panel_data; mutex_lock(&panel->io_lock); rc = kstrtoint(buf, 0, &value); if (rc < 0) { panel_warn("invalid param (ret %d)\n", rc); mutex_unlock(&panel->io_lock); return rc; } mutex_lock(&panel->op_lock); panel_data->props.lpm_opr = value; mutex_unlock(&panel->op_lock); panel_update_brightness(panel); panel_info("value %d, lpm_opr %d\n", value, panel_data->props.lpm_opr); mutex_unlock(&panel->io_lock); return size; } static ssize_t lpm_opr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%d\n", panel_data->props.lpm_opr); return strlen(buf); } static ssize_t conn_det_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc; struct panel_device *panel = dev_get_drvdata(dev); rc = kstrtoint(buf, 0, &value); if (rc < 0) { panel_warn("invalid param (ret %d)\n", rc); return rc; } if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } if (!panel_is_gpio_valid(panel, PANEL_GPIO_CONN_DET)) { panel_err("conn det is unsupported\n"); return -EINVAL; } if (panel->panel_data.props.conn_det_enable != value) { panel->panel_data.props.conn_det_enable = value; panel_info("set %d %s\n", panel->panel_data.props.conn_det_enable, panel->panel_data.props.conn_det_enable ? "enable" : "disable"); if (panel->panel_data.props.conn_det_enable) { if (panel_disconnected(panel)) panel_send_ubconn_uevent(panel); } } else { panel_info("already set %d %s\n", panel->panel_data.props.conn_det_enable, panel->panel_data.props.conn_det_enable ? "enable" : "disable"); } return size; } static ssize_t conn_det_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } if (panel_is_gpio_valid(panel, PANEL_GPIO_CONN_DET)) snprintf(buf, PAGE_SIZE, "%s\n", panel_disconnected(panel) ? "disconnected" : "connected"); else snprintf(buf, PAGE_SIZE, "%d\n", -1); panel_info("%s", buf); return strlen(buf); } #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) static ssize_t lux_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%d\n", panel_data->props.lux); return strlen(buf); } static ssize_t lux_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int rc; int value; struct mdnie_info *mdnie; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } mdnie = &panel->mdnie; panel_data = &panel->panel_data; rc = kstrtoint(buf, 0, &value); if (rc < 0) return rc; if (panel_data->props.lux != value) { mutex_lock(&panel->op_lock); panel_data->props.lux = value; mutex_unlock(&panel->op_lock); attr_store_for_each(mdnie->class, attr->attr.name, buf, size); } return size; } #endif /* CONFIG_EXYNOS_DECON_MDNIE_LITE */ #ifdef CONFIG_EXYNOS_DECON_LCD_COPR static ssize_t copr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct copr_info *copr = &panel->copr; return copr_reg_show(copr, buf); } static ssize_t copr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_device *panel = dev_get_drvdata(dev); struct copr_info *copr = &panel->copr; char *p, *arg = (char *)buf; const char *name; int index, rc; u32 value; mutex_lock(&copr->lock); while ((p = strsep(&arg, " \t")) != NULL) { if (!*p) continue; index = find_copr_reg_by_name(copr->props.version, p); if (index < 0) { panel_err("arg(%s) not found\n", p); continue; } name = get_copr_reg_name(copr->props.version, index); if (name == NULL) { panel_err("arg(%s) not found\n", p); continue; } rc = sscanf(p + strlen(name), "%i", &value); if (rc != 1) { panel_err("invalid argument name:%s ret:%d\n", name, rc); continue; } rc = copr_reg_store(copr, index, value); if (rc < 0) { panel_err("failed to store to copr_reg (index %d, value %d)\n", index, value); continue; } } copr->props.state = COPR_UNINITIALIZED; copr_update_average(copr); panel_info("copr %s\n", get_copr_reg_copr_en(copr) ? "enable" : "disable"); mutex_unlock(&copr->lock); copr_update_start(&panel->copr, 1); return size; } static ssize_t read_copr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct copr_info *copr = &panel->copr; int cur_copr; if (!IS_PANEL_ACTIVE(panel)) { panel_err("panel is not active\n"); return snprintf(buf, PAGE_SIZE, "-1\n"); } if (!copr_is_enabled(copr)) { panel_err("copr is off state\n"); return snprintf(buf, PAGE_SIZE, "-1\n"); } cur_copr = copr_get_value(copr); if (cur_copr < 0) { panel_err("failed to get copr\n"); return snprintf(buf, PAGE_SIZE, "-1\n"); } return snprintf(buf, PAGE_SIZE, "%d\n", cur_copr); } static ssize_t copr_roi_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_device *panel = dev_get_drvdata(dev); struct copr_info *copr = &panel->copr; struct copr_roi roi[6]; int nr_roi, rc = 0, i; memset(roi, -1, sizeof(roi)); if (copr->props.version == COPR_VER_1 || copr->props.version == COPR_VER_2) { rc = sscanf(buf, "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", &roi[0].roi_xs, &roi[0].roi_ys, &roi[0].roi_xe, &roi[0].roi_ye, &roi[1].roi_xs, &roi[1].roi_ys, &roi[1].roi_xe, &roi[1].roi_ye, &roi[2].roi_xs, &roi[2].roi_ys, &roi[2].roi_xe, &roi[2].roi_ye, &roi[3].roi_xs, &roi[3].roi_ys, &roi[3].roi_xe, &roi[3].roi_ye); if (rc < 0) { panel_err("invalid roi input(rc %d)\n", rc); return -EINVAL; } nr_roi = rc / 4; } else if (copr->props.version == COPR_VER_3) { rc = sscanf(buf, "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", &roi[0].roi_xs, &roi[0].roi_ys, &roi[0].roi_xe, &roi[0].roi_ye, &roi[1].roi_xs, &roi[1].roi_ys, &roi[1].roi_xe, &roi[1].roi_ye, &roi[2].roi_xs, &roi[2].roi_ys, &roi[2].roi_xe, &roi[2].roi_ye, &roi[3].roi_xs, &roi[3].roi_ys, &roi[3].roi_xe, &roi[3].roi_ye, &roi[4].roi_xs, &roi[4].roi_ys, &roi[4].roi_xe, &roi[4].roi_ye); if (rc < 0) { panel_err("invalid roi input(rc %d)\n", rc); return -EINVAL; } if (rc == 20) { /* roi5&6 must be same in copr ver3.0 */ memcpy(&roi[5], &roi[4], sizeof(struct copr_roi)); } nr_roi = rc / 4; } else if (copr->props.version == COPR_VER_5) { rc = sscanf(buf, "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", &roi[0].roi_xs, &roi[0].roi_ys, &roi[0].roi_xe, &roi[0].roi_ye, &roi[1].roi_xs, &roi[1].roi_ys, &roi[1].roi_xe, &roi[1].roi_ye, &roi[2].roi_xs, &roi[2].roi_ys, &roi[2].roi_xe, &roi[2].roi_ye, &roi[3].roi_xs, &roi[3].roi_ys, &roi[3].roi_xe, &roi[3].roi_ye, &roi[4].roi_xs, &roi[4].roi_ys, &roi[4].roi_xe, &roi[4].roi_ye); if (rc < 0) { panel_err("invalid roi input(rc %d)\n", rc); return -EINVAL; } nr_roi = rc / 4; } else if (copr->props.version == COPR_VER_6) { rc = sscanf(buf, "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", &roi[0].roi_er, &roi[0].roi_eg, &roi[0].roi_eb, &roi[0].roi_xs, &roi[0].roi_ys, &roi[0].roi_xe, &roi[0].roi_ye, &roi[1].roi_er, &roi[1].roi_eg, &roi[1].roi_eb, &roi[1].roi_xs, &roi[1].roi_ys, &roi[1].roi_xe, &roi[1].roi_ye, &roi[2].roi_er, &roi[2].roi_eg, &roi[2].roi_eb, &roi[2].roi_xs, &roi[2].roi_ys, &roi[2].roi_xe, &roi[2].roi_ye, &roi[3].roi_er, &roi[3].roi_eg, &roi[3].roi_eb, &roi[3].roi_xs, &roi[3].roi_ys, &roi[3].roi_xe, &roi[3].roi_ye, &roi[4].roi_er, &roi[4].roi_eg, &roi[4].roi_eb, &roi[4].roi_xs, &roi[4].roi_ys, &roi[4].roi_xe, &roi[4].roi_ye); if (rc < 0) { panel_err("invalid roi input(rc %d)\n", rc); return -EINVAL; } nr_roi = rc / 7; } else { panel_err("roi is unsupported in copr ver%d\n", copr->props.version); return -EINVAL; } mutex_lock(&copr->lock); for (i = 0; i < nr_roi; i++) { if ((int)roi[i].roi_xs == -1 || (int)roi[i].roi_ys == -1 || (int)roi[i].roi_xe == -1 || (int)roi[i].roi_ye == -1) continue; else memcpy(&copr->props.roi[i], &roi[i], sizeof(struct copr_roi)); } if (copr->props.version == COPR_VER_2 || copr->props.version == COPR_VER_1) copr->props.nr_roi = nr_roi; mutex_unlock(&copr->lock); if (copr->props.version == COPR_VER_3 || copr->props.version == COPR_VER_5 || copr->props.version == COPR_VER_6) { /* apply roi at once in copr ver3.0 ~ ver6.0 */ copr_roi_set_value(copr, copr->props.roi, copr->props.nr_roi); } if (copr->props.version == COPR_VER_6) { for (i = 0; i < nr_roi; i++) panel_info("set roi[%d] %d %d %d %d %d %d %d\n", i, roi[i].roi_er, roi[i].roi_eg, roi[i].roi_eb, roi[i].roi_xs, roi[i].roi_ys, roi[i].roi_xe, roi[i].roi_ye); for (i = 0; i < copr->props.nr_roi; i++) panel_info("cur roi[%d] %d %d %d %d %d %d %d\n", i, copr->props.roi[i].roi_er, copr->props.roi[i].roi_eg, copr->props.roi[i].roi_eb, copr->props.roi[i].roi_xs, copr->props.roi[i].roi_ys, copr->props.roi[i].roi_xe, copr->props.roi[i].roi_ye); } else { for (i = 0; i < nr_roi; i++) panel_info("set roi[%d] %d %d %d %d\n", i, roi[i].roi_xs, roi[i].roi_ys, roi[i].roi_xe, roi[i].roi_ye); for (i = 0; i < copr->props.nr_roi; i++) panel_info("cur roi[%d] %d %d %d %d\n", i, copr->props.roi[i].roi_xs, copr->props.roi[i].roi_ys, copr->props.roi[i].roi_xe, copr->props.roi[i].roi_ye); } return size; } static ssize_t copr_roi_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct copr_info *copr = &panel->copr; int i, c, ret, len = 0; int max_color = (copr->props.version == COPR_VER_6) ? MAX_RGBW_COLOR : MAX_COLOR; u32 out[5 * 4] = { 0, }; if (copr->props.nr_roi == 0) { panel_warn("copr roi disabled\n"); return -ENODEV; } if (!copr_is_enabled(copr)) { panel_err("copr is off state\n"); return snprintf(buf, PAGE_SIZE, "-1\n"); } if (!IS_PANEL_ACTIVE(panel)) { panel_err("panel is not active\n"); return snprintf(buf, PAGE_SIZE, "-1\n"); } ret = copr_roi_get_value(copr, copr->props.roi, copr->props.nr_roi, out); if (ret < 0) { panel_err("failed to get copr\n"); return snprintf(buf, PAGE_SIZE, "-1\n"); } for (i = 0; i < copr->props.nr_roi; i++) { for (c = 0; c < max_color; c++) { len += snprintf(buf + len, PAGE_SIZE - len, "%d%s", out[i * max_color + c], ((i == copr->props.nr_roi - 1) && c == max_color - 1) ? "\n" : " "); } } return len; } static ssize_t brt_avg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct panel_bl_device *panel_bl = &panel->panel_bl; int brt_avg; if (!IS_PANEL_ACTIVE(panel)) { panel_err("panel is not active\n"); return snprintf(buf, PAGE_SIZE, "-1\n"); } brt_avg = panel_bl_get_average_and_clear(panel_bl, 1); if (brt_avg < 0) { panel_err("failed to get average brt1\n"); return snprintf(buf, PAGE_SIZE, "-1\n"); } return snprintf(buf, PAGE_SIZE, "%d\n", brt_avg); } #endif #ifdef CONFIG_DISPLAY_USE_INFO /* * HW PARAM LOGGING SYSFS NODE */ static ssize_t dpui_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; update_dpui_log(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL); ret = get_dpui_log(buf, DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL); if (ret < 0) { panel_err("failed to get log %d\n", ret); return ret; } panel_info("%s\n", buf); return ret; } static ssize_t dpui_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { if (buf[0] == 'C' || buf[0] == 'c') clear_dpui_log(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_PANEL); return size; } /* * [DEV ONLY] * HW PARAM LOGGING SYSFS NODE */ static ssize_t dpui_dbg_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; update_dpui_log(DPUI_LOG_LEVEL_DEBUG, DPUI_TYPE_PANEL); ret = get_dpui_log(buf, DPUI_LOG_LEVEL_DEBUG, DPUI_TYPE_PANEL); if (ret < 0) { panel_err("failed to get log %d\n", ret); return ret; } panel_info("%s\n", buf); return ret; } static ssize_t dpui_dbg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { if (buf[0] == 'C' || buf[0] == 'c') clear_dpui_log(DPUI_LOG_LEVEL_DEBUG, DPUI_TYPE_PANEL); return size; } /* * [AP DEPENDENT ONLY] * HW PARAM LOGGING SYSFS NODE */ static ssize_t dpci_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; update_dpui_log(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL); ret = get_dpui_log(buf, DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL); if (ret < 0) { panel_err("failed to get log %d\n", ret); return ret; } panel_info("%s\n", buf); return ret; } static ssize_t dpci_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { if (buf[0] == 'C' || buf[0] == 'c') clear_dpui_log(DPUI_LOG_LEVEL_INFO, DPUI_TYPE_CTRL); return size; } /* * [AP DEPENDENT DEV ONLY] * HW PARAM LOGGING SYSFS NODE */ static ssize_t dpci_dbg_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; update_dpui_log(DPUI_LOG_LEVEL_DEBUG, DPUI_TYPE_CTRL); ret = get_dpui_log(buf, DPUI_LOG_LEVEL_DEBUG, DPUI_TYPE_CTRL); if (ret < 0) { panel_err("failed to get log %d\n", ret); return ret; } panel_info("%s\n", buf); return ret; } static ssize_t dpci_dbg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { if (buf[0] == 'C' || buf[0] == 'c') clear_dpui_log(DPUI_LOG_LEVEL_DEBUG, DPUI_TYPE_CTRL); return size; } #endif static ssize_t poc_onoff_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%d\n", panel_data->props.poc_onoff); return strlen(buf); } static ssize_t poc_onoff_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int rc; int value; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtoint(buf, 0, &value); if (rc < 0) return rc; panel_info("%d -> %d\n", panel_data->props.poc_onoff, value); if (panel_data->props.poc_onoff != value) { mutex_lock(&panel->panel_bl.lock); panel_data->props.poc_onoff = value; mutex_unlock(&panel->panel_bl.lock); panel_update_brightness(panel); } return size; } #ifdef CONFIG_EXTEND_LIVE_CLOCK static ssize_t self_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { struct aod_dev_info *aod; struct aod_ioctl_props *props; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } aod = &panel->aod; props = &aod->props; snprintf(buf, PAGE_SIZE, "%d\n", props->self_mask_en); return strlen(buf); } static ssize_t self_mask_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int rc, ret; int value; struct aod_dev_info *aod; struct aod_ioctl_props *props; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } aod = &panel->aod; props = &aod->props; if ((aod == NULL) || (props == NULL)) { panel_err("aod is null\n"); return -EINVAL; } rc = kstrtoint(buf, 0, &value); if (rc < 0) return rc; panel_info("%d -> %d\n", props->self_mask_en, value); if (props->self_mask_en != value) { if (value == 0) { ret = panel_do_aod_seqtbl_by_index(aod, SELF_MASK_DIS_SEQ); if (unlikely(ret < 0)) panel_err("failed to disable self mask\n"); } else { ret = panel_do_aod_seqtbl_by_index(aod, SELF_MASK_IMG_SEQ); if (unlikely(ret < 0)) panel_err("failed to write self mask image\n"); ret = panel_do_aod_seqtbl_by_index(aod, SELF_MASK_ENA_SEQ); if (unlikely(ret < 0)) panel_err("failed to enable self mask\n"); } props->self_mask_en = value; } return size; } static void prepare_self_mask_check(struct panel_device *panel) { int ret = 0; panel_dsi_set_bypass(panel, true); ret = panel_disable_disp_det_irq(panel); if (ret < 0) panel_err("failed to disable disp_det irq\n"); ret = panel_disable_pcd_irq(panel); if (ret < 0) panel_err("failed to disable pcd irq\n"); } static void clear_self_mask_check(struct panel_device *panel) { int ret = 0; ret = panel_enable_gpio_irq(panel, PANEL_GPIO_DISP_DET); if (ret < 0) panel_warn("do not support irq\n"); ret = panel_enable_gpio_irq(panel, PANEL_GPIO_PCD); if (ret < 0) panel_warn("do not support irq\n"); panel_dsi_set_bypass(panel, false); } static ssize_t self_mask_check_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct aod_dev_info *aod; struct panel_info *panel_data; u8 success_check = 1; u8 *recv_checksum = NULL; int ret = 0, i = 0; int len = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } aod = &panel->aod; panel_data = &panel->panel_data; if (aod->props.self_mask_checksum_len) { recv_checksum = kmalloc_array(aod->props.self_mask_checksum_len, sizeof(u8), GFP_KERNEL); if (!recv_checksum) return -ENOMEM; prepare_self_mask_check(panel); ret = panel_do_aod_seqtbl_by_index(aod, SELF_MASK_CHECKSUM_SEQ); if (unlikely(ret < 0)) { panel_err("failed to send cmd selfmask checksum\n"); kfree(recv_checksum); return ret; } ret = resource_copy_n_clear_by_name(panel_data, recv_checksum, "self_mask_checksum"); if (unlikely(ret < 0)) { panel_err("failed to get selfmask checksum\n"); kfree(recv_checksum); return ret; } clear_self_mask_check(panel); for (i = 0; i < aod->props.self_mask_checksum_len; i++) { if (aod->props.self_mask_checksum[i] != recv_checksum[i]) { success_check = 0; break; } } len = snprintf(buf, PAGE_SIZE, "%d", success_check); for (i = 0; i < aod->props.self_mask_checksum_len; i++) len += snprintf(buf + len, PAGE_SIZE - len, " %02x", recv_checksum[i]); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); kfree(recv_checksum); } else { snprintf(buf, PAGE_SIZE, "-1\n"); } return strlen(buf); } #endif #ifdef SUPPORT_NORMAL_SELF_MOVE static ssize_t self_move_show(struct device *dev, struct device_attribute *attr, char *buf) { struct aod_dev_info *aod; struct aod_ioctl_props *props; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } aod = &panel->aod; props = &aod->props; snprintf(buf, PAGE_SIZE, "%d\n", props->self_move_pattern); return strlen(buf); } static ssize_t self_move_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret; int pattern; struct aod_dev_info *aod; struct aod_ioctl_props *props; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } aod = &panel->aod; props = &aod->props; if ((aod == NULL) || (props == NULL)) { panel_err("aod is null\n"); return -EINVAL; } ret = kstrtoint(buf, 0, &pattern); if (ret < 0) return ret; if (pattern < NORMAL_SELF_MOVE_PATTERN_OFF || pattern >= MAX_NORMAL_SELF_MOVE_PATTERN) { panel_err("out of range(%d)\n", pattern); return -EINVAL; } panel_info("pattern : %d\n", pattern); mutex_lock(&panel->io_lock); props->self_move_pattern = pattern; ret = panel_self_move_pattern_update(panel); if (ret < 0) panel_info("failed to set self move pattern\n"); mutex_unlock(&panel->io_lock); return size; } #endif #ifdef CONFIG_SUPPORT_ISC_DEFECT static ssize_t isc_defect_show(struct device *dev, struct device_attribute *attr, char *buf) { snprintf(buf, PAGE_SIZE, "support isc defect checkd\n"); return 0; } static ssize_t isc_defect_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int rc, ret; int value; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } rc = kstrtoint(buf, 0, &value); if (rc < 0) return rc; panel_info("%d\n", value); mutex_lock(&panel->op_lock); if (value) { ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_CHECK_ISC_DEFECT_SEQ); if (unlikely(ret < 0)) panel_err("failed to write ics defect seq\n"); } mutex_unlock(&panel->op_lock); return size; } #endif #ifdef CONFIG_SUPPORT_BRIGHTDOT_TEST static ssize_t brightdot_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct panel_info *panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.brightdot_test_enable); return strlen(buf); } static ssize_t brightdot_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int rc, ret; u32 value = 0; struct panel_device *panel = dev_get_drvdata(dev); struct panel_info *panel_data; panel_data = &panel->panel_data; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; mutex_lock(&panel->op_lock); panel_info("%u -> %u\n", panel_data->props.brightdot_test_enable, value); panel_data->props.brightdot_test_enable = value; ret = panel_do_seqtbl_by_index_nolock(panel, PANEL_BRIGHTDOT_TEST_SEQ); if (unlikely(ret < 0)) panel_err("failed to write brightdot seq\n"); mutex_unlock(&panel->op_lock); return size; } #endif #ifdef CONFIG_SUPPORT_SPI_IF_SEL static ssize_t spi_if_sel_show(struct device *dev, struct device_attribute *attr, char *buf) { return 0; } static ssize_t spi_if_sel_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int rc, ret; int value; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } rc = kstrtoint(buf, 0, &value); if (rc < 0) return rc; if (check_seqtbl_exist(&panel->panel_data, value ? PANEL_SPI_IF_ON_SEQ : PANEL_SPI_IF_OFF_SEQ) <= 0) { panel_info("spi if on/off unsupported\n"); return size; } panel_info("%d\n", value); mutex_lock(&panel->op_lock); ret = panel_do_seqtbl_by_index_nolock(panel, value ? PANEL_SPI_IF_ON_SEQ : PANEL_SPI_IF_OFF_SEQ); if (unlikely(ret < 0)) panel_err("failed to write spi-if-%s seq\n", value ? "on" : "off"); mutex_unlock(&panel->op_lock); return size; } #endif #ifdef CONFIG_SUPPORT_CCD_TEST #define CCD_STATE_SIZE 4 static ssize_t ccd_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); struct resinfo *info; int ret = 0, retVal = 0, ccd_size, i; size_t ires; enum { CCD_CHKSUM_PASS, CCD_CHKSUM_FAIL, CCD_CHKSUM_PASS_LIST }; static const char * const ccd_resource_name[] = { [CCD_CHKSUM_PASS] = "ccd_chksum_pass", [CCD_CHKSUM_FAIL] = "ccd_chksum_fail", [CCD_CHKSUM_PASS_LIST] = "ccd_chksum_pass_list", }; u8 ccd_state[CCD_STATE_SIZE] = { 0x12, 0x34, 0x56, 0x78 }; u8 ccd_compare[CCD_STATE_SIZE] = { 0x87, 0x65, 0x43, 0x21 }; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; info = find_panel_resource(panel_data, "ccd_state"); if (unlikely(info == NULL)) { panel_err("failed to get ccd resource\n"); return -EINVAL; } ccd_size = info->dlen; if (ccd_size < 1 || ccd_size > CCD_STATE_SIZE) { panel_err("invalid ccd size %d %d\n", ccd_size, CCD_STATE_SIZE); return -EINVAL; } ret = panel_do_seqtbl_by_index(panel, PANEL_CCD_TEST_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write ccd seq\n"); return ret; } ret = resource_copy_n_clear_by_name(panel_data, ccd_state, "ccd_state"); if (unlikely(ret < 0)) { panel_err("failed to read ccd_state \n"); return ret; } for (ires = 0; ires < ARRAY_SIZE(ccd_resource_name); ires++) { info = find_panel_resource(panel_data, (char *)ccd_resource_name[ires]); if (info) { panel_info("find ccd compare resource.(%s)\n", (char *)ccd_resource_name[ires]); break; } } if (ires != ARRAY_SIZE(ccd_resource_name)) { if (ires < CCD_CHKSUM_PASS_LIST) { if (info->dlen != ccd_size) { panel_err("%s: size mismatch %d %d\n", ccd_resource_name[ires], info->dlen, ccd_size); return -EINVAL; } if (rescpy(ccd_compare, info, 0, ccd_size) < 0) return -EINVAL; if (ires == CCD_CHKSUM_PASS) { retVal = memcmp(ccd_state, ccd_compare, ccd_size) == 0 ? 1 : 0; panel_info("p_comp %s\n", (retVal == 1) ? "Pass" : "Fail"); } else { retVal = memcmp(ccd_state, ccd_compare, ccd_size) == 0 ? 0 : 1; panel_info("f_comp %s\n", (retVal == 1) ? "Pass" : "Fail"); } for (i = 0; i < ccd_size; i++) panel_info("[%d] 0x%02x 0x%02x\n", i, ccd_state[i], ccd_compare[i]); } else if (ires == CCD_CHKSUM_PASS_LIST) { retVal = 0; if (rescpy(ccd_compare, info, 0, info->dlen) < 0) return -EINVAL; for (i = 0 ; i < info->dlen; i++) { panel_info("p_list_comp read:0x%02x pass:0x%02x\n", ccd_state[0], ccd_compare[i]); if (ccd_state[0] == ccd_compare[i]) retVal = 1; } panel_info("p_list_comp %s\n", (retVal == 1) ? "Pass" : "Fail"); } } else { /* support previous panel, compare with first 1byte: 0x00(pass) */ retVal = (ccd_state[0] == 0x00) ? 1 : 0; panel_info("comp %s 0x%02x %d\n", (retVal == 1) ? "Pass" : "Fail", ccd_state[0], retVal); } snprintf(buf, PAGE_SIZE, "%d\n", retVal); return strlen(buf); } #endif #ifdef CONFIG_SUPPORT_DYNAMIC_HLPM static ssize_t dynamic_hlpm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.dynamic_hlpm); return strlen(buf); } static ssize_t dynamic_hlpm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int value, rc, ret; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if ((panel_data->props.alpm_mode != HLPM_HIGH_BR) && (panel_data->props.alpm_mode != HLPM_LOW_BR)) { panel_info("please set HLPM mode %d\n", panel_data->props.alpm_mode); return size; } if (panel_data->props.dynamic_hlpm == value) return size; mutex_lock(&panel->op_lock); panel_data->props.dynamic_hlpm = value; mutex_unlock(&panel->op_lock); ret = panel_do_seqtbl_by_index(panel, value ? PANEL_DYNAMIC_HLPM_ON_SEQ : PANEL_DYNAMIC_HLPM_OFF_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write dynamic_hlpm on/off seq\n"); return ret; } panel_info("dynamic hlpm %s\n", panel_data->props.dynamic_hlpm ? "on" : "off"); return size; } #endif static ssize_t vrr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); struct panel_properties *props; struct panel_vrr *vrr; int vrr_fps, vrr_mode; int div_count; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; props = &panel->panel_data.props; if (!panel_vrr_is_supported(panel)) return snprintf(buf, PAGE_SIZE, "60 0\n"); vrr = get_panel_vrr(panel); if (vrr == NULL) return -EINVAL; div_count = max(TE_SKIP_TO_DIV(vrr->te_sw_skip_count, vrr->te_hw_skip_count), MIN_VRR_DIV_COUNT); vrr_fps = vrr->fps; vrr_mode = vrr->mode; panel_info("display(%d%s) panel(%d%s)\n", vrr_fps / div_count, REFRESH_MODE_STR(vrr_mode), vrr_fps, REFRESH_MODE_STR(vrr_mode)); snprintf(buf, PAGE_SIZE, "%d %d\n", vrr_fps / div_count, vrr_mode); return strlen(buf); } #if defined(CONFIG_PANEL_DISPLAY_MODE) static ssize_t display_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); struct panel_display_modes *panel_modes; struct common_panel_display_modes *common_panel_modes; struct panel_properties *props; int i, len = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } props = &panel->panel_data.props; panel_modes = panel->panel_modes; if (!panel->panel_modes) { len += snprintf(buf + len, PAGE_SIZE - len, "panel_display_modes empty!!\n"); } else { for (i = 0; i < panel_modes->num_modes; i++) { if (!panel_modes->modes[i]) continue; len += snprintf(buf + len, PAGE_SIZE - len, "pdm:%d %s\n", i, panel_modes->modes[i]->name); } } common_panel_modes = panel->panel_data.common_panel_modes; if (!common_panel_modes) { len += snprintf(buf + len, PAGE_SIZE - len, "common_panel_display_modes empty!!\n"); } else { for (i = 0; i < common_panel_modes->num_modes; i++) { if (!common_panel_modes->modes[i]) continue; len += snprintf(buf + len, PAGE_SIZE - len, "cpdm:%d %s\n", i, common_panel_modes->modes[i]->name); } } len += snprintf(buf + len, PAGE_SIZE - len, "panel_mode:%d\n", props->panel_mode); return len; } static ssize_t display_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_device *panel = dev_get_drvdata(dev); struct common_panel_display_modes *common_panel_modes; struct panel_properties *props; int rc, panel_mode = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } props = &panel->panel_data.props; common_panel_modes = panel->panel_data.common_panel_modes; rc = kstrtoint(buf, 0, &panel_mode); if (rc < 0) return -EINVAL; mutex_lock(&panel->op_lock); if (panel_mode < 0 || panel_mode >= common_panel_modes->num_modes) { panel_err("panel_mode(%d) exceeded num_modes(%d)\n", panel_mode, common_panel_modes->num_modes); mutex_unlock(&panel->op_lock); return -EINVAL; } props->panel_mode = panel_mode; mutex_unlock(&panel->op_lock); rc = panel_update_display_mode(panel); if (rc < 0) panel_err("failed to panel_update_display_mode\n"); return size; } #endif #ifdef CONFIG_PANEL_VRR_BRIDGE static ssize_t vrr_bridge_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; return snprintf(buf, PAGE_SIZE, "%d\n", panel_data->props.vrr_bridge_enable); } static ssize_t vrr_bridge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); int rc, enable = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtoint(buf, 0, &enable); if (rc < 0) return -EINVAL; mutex_lock(&panel->io_lock); panel_data->props.vrr_bridge_enable = !!enable; panel_info("vrr_bridge_enable %d\n", enable); mutex_unlock(&panel->io_lock); return size; } #endif ssize_t snprint_vrr_lfd(struct panel_device *panel, char *buf, ssize_t size) { struct panel_properties *props = &panel->panel_data.props; ssize_t len = 0; int i = 0, scope = 0; bool updated; const char *client_name = NULL; const char *scope_name = NULL; len += snprintf(buf + len, size - len, "[req]\n"); updated = false; for (i = 0; i < MAX_VRR_LFD_CLIENT; i++) { for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) { client_name = get_vrr_lfd_client_name(i); scope_name = get_vrr_lfd_scope_name(scope); if (client_name == NULL || scope_name == NULL) continue; if (props->vrr_lfd_info.req[i][scope].fix == VRR_LFD_FREQ_NONE && props->vrr_lfd_info.req[i][scope].scalability == VRR_LFD_SCALABILITY_NONE && props->vrr_lfd_info.req[i][scope].min == 0 && props->vrr_lfd_info.req[i][scope].max == 0) continue; len += snprintf(buf + len, size - len, "client=%s scope=%s fix=%d scalability=%d min=%d max=%d\n", client_name, scope_name, props->vrr_lfd_info.req[i][scope].fix, props->vrr_lfd_info.req[i][scope].scalability, props->vrr_lfd_info.req[i][scope].min, props->vrr_lfd_info.req[i][scope].max); updated = true; } } if (updated == false) len += snprintf(buf + len, size - len, "none\n"); len += snprintf(buf + len, size - len, "[cur]\n"); for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) { scope_name = get_vrr_lfd_scope_name(scope); if (scope_name == NULL) continue; len += snprintf(buf + len, size - len, "scope=%s lfd:%d~%dHz div:%d~%d(fix=%d scalability=%d min=%d max=%d)\n", scope_name, props->vrr_lfd_info.status[scope].lfd_min_freq, props->vrr_lfd_info.status[scope].lfd_max_freq, props->vrr_lfd_info.status[scope].lfd_min_freq_div, props->vrr_lfd_info.status[scope].lfd_max_freq_div, props->vrr_lfd_info.cur[scope].fix, props->vrr_lfd_info.cur[scope].scalability, props->vrr_lfd_info.cur[scope].min, props->vrr_lfd_info.cur[scope].max); } return len; } static ssize_t vrr_lfd_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } if (!panel_vrr_is_supported(panel)) { panel_warn("vrr is not supported\n"); return -EINVAL; } panel_data = &panel->panel_data; return snprint_vrr_lfd(panel, buf, PAGE_SIZE); } static ssize_t vrr_lfd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_info *panel_data; struct panel_properties *props; struct panel_device *panel = dev_get_drvdata(dev); int index, scope; int argc = 0, client_index = -1, scope_mask = 0; struct sysfs_arg vrr_lfd_arglist[] = { /* old argument : to be removed */ { .name = "0", .nargs = 0, .type = SYSFS_ARG_TYPE_NONE, }, { .name = "1", .nargs = 0, .type = SYSFS_ARG_TYPE_NONE, }, { .name = "2", .nargs = 0, .type = SYSFS_ARG_TYPE_NONE, }, { .name = "tsp_lpm", .nargs = 1, .type = SYSFS_ARG_TYPE_S32, }, { .name = "FIX", .nargs = 1, .type = SYSFS_ARG_TYPE_S32, }, { .name = "SCAN", .nargs = 1, .type = SYSFS_ARG_TYPE_S32, }, /* new argument */ { .name = VRR_LFD_ARG_KEY_CLIENT, .nargs = 1, .type = SYSFS_ARG_TYPE_STR, }, { .name = VRR_LFD_ARG_KEY_SCOPE, .nargs = 1, .type = SYSFS_ARG_TYPE_STR, }, { .name = VRR_LFD_ARG_KEY_FIX, .nargs = 1, .type = SYSFS_ARG_TYPE_S32, }, { .name = VRR_LFD_ARG_KEY_SCALABILITY, .nargs = 1, .type = SYSFS_ARG_TYPE_S32, }, { .name = VRR_LFD_ARG_KEY_MIN, .nargs = 1, .type = SYSFS_ARG_TYPE_S32, }, { .name = VRR_LFD_ARG_KEY_MAX, .nargs = 1, .type = SYSFS_ARG_TYPE_S32, }, }; struct sysfs_arg_out out; char *p, *arg = (char *)buf, *arg1; int len = 0; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } if (!panel_vrr_is_supported(panel)) { panel_warn("vrr is not supported\n"); return -EINVAL; } memset(&out, 0, sizeof(struct sysfs_arg_out)); panel_data = &panel->panel_data; props = &panel_data->props; mutex_lock(&panel->panel_bl.lock); while ((p = strsep(&arg, " \t\n=")) != NULL) { if (!*p) continue; index = find_sysfs_arg_by_name(vrr_lfd_arglist, ARRAY_SIZE(vrr_lfd_arglist), p); if (index < 0) { panel_err("arg(%s) not found\n", p); mutex_unlock(&panel->panel_bl.lock); return -EINVAL; } if (vrr_lfd_arglist[index].nargs > 0) { len = parse_sysfs_arg(vrr_lfd_arglist[index].nargs, vrr_lfd_arglist[index].type, arg, &out); if (len < 0) { panel_err("failed to parse sysfs arg(%s)\n", arg); mutex_unlock(&panel->panel_bl.lock); return -EINVAL; } arg += len; } if (argc == 0) { /* madatory: 1st argument should start with "client=" */ if (!strcmp(vrr_lfd_arglist[index].name, VRR_LFD_ARG_KEY_CLIENT)) { client_index = find_vrr_lfd_client_name(out.d[0].val_str); if (client_index < 0) { panel_err("client(%s) not found\n", out.d[0].val_str); mutex_unlock(&panel->panel_bl.lock); return -EINVAL; } argc++; continue; } } /* old argument : to be removed */ if (client_index == -1) { if (!strcmp("0", vrr_lfd_arglist[index].name)) { for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) props->vrr_lfd_info.req[VRR_LFD_CLIENT_FAC][scope].fix = VRR_LFD_FREQ_NONE; } else if (!strcmp("1", vrr_lfd_arglist[index].name)) { for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) props->vrr_lfd_info.req[VRR_LFD_CLIENT_FAC][scope].fix = VRR_LFD_FREQ_HIGH; } else if (!strcmp("2", vrr_lfd_arglist[index].name)) { for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) props->vrr_lfd_info.req[VRR_LFD_CLIENT_FAC][scope].fix = VRR_LFD_FREQ_LOW; } else if (!strcmp("tsp_lpm", vrr_lfd_arglist[index].name)) { props->vrr_lfd_info.req[VRR_LFD_CLIENT_AOD][VRR_LFD_SCOPE_LPM].fix = out.d[0].val_s32; } else if (!strcmp("FIX", vrr_lfd_arglist[index].name)) { for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) props->vrr_lfd_info.req[VRR_LFD_CLIENT_DISP][scope].fix = out.d[0].val_s32; } else if (!strcmp("SCAN", vrr_lfd_arglist[index].name)) { props->vrr_lfd_info.req[VRR_LFD_CLIENT_VID][VRR_LFD_SCOPE_NORMAL].max = out.d[0].val_s32; } else { panel_err("undefined argument(%s)\n", vrr_lfd_arglist[index].name); } break; } /* madatory: 2nd argument should start with "scope=" */ if (!strcmp(vrr_lfd_arglist[index].name, VRR_LFD_ARG_KEY_SCOPE)) { arg1 = out.d[0].val_str; while ((p = strsep(&arg1, " ,|")) != NULL) { if (!*p) continue; scope = find_vrr_lfd_scope_name(p); if (scope < 0) { panel_err("scope(%s) not found\n", p); mutex_unlock(&panel->panel_bl.lock); return -EINVAL; } scope_mask |= VRR_LFD_SCOPE_MASK(scope); } argc++; continue; } /* * this is temporay w/a code for old argument. * it will be removed. */ if (scope_mask == 0) { if (client_index == VRR_LFD_CLIENT_AOD) scope_mask = VRR_LFD_SCOPE_LPM_MASK; else scope_mask = VRR_LFD_SCOPE_NORMAL_MASK; } if (scope_mask == 0) { panel_err("argument(scope=) not found\n"); mutex_unlock(&panel->panel_bl.lock); return -EINVAL; } if (!strcmp(VRR_LFD_ARG_KEY_FIX, vrr_lfd_arglist[index].name)) { for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) if (scope_mask & VRR_LFD_SCOPE_MASK(scope)) props->vrr_lfd_info.req[client_index][scope].fix = out.d[0].val_s32; } else if (!strcmp(VRR_LFD_ARG_KEY_SCALABILITY, vrr_lfd_arglist[index].name)) { for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) if (scope_mask & VRR_LFD_SCOPE_MASK(scope)) props->vrr_lfd_info.req[client_index][scope].scalability = out.d[0].val_s32; } else if (!strcmp(VRR_LFD_ARG_KEY_MIN, vrr_lfd_arglist[index].name)) { for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) if (scope_mask & VRR_LFD_SCOPE_MASK(scope)) props->vrr_lfd_info.req[client_index][scope].min = out.d[0].val_s32; } else if (!strcmp(VRR_LFD_ARG_KEY_MAX, vrr_lfd_arglist[index].name)) { for (scope = 0; scope < MAX_VRR_LFD_SCOPE; scope++) if (scope_mask & VRR_LFD_SCOPE_MASK(scope)) props->vrr_lfd_info.req[client_index][scope].max = out.d[0].val_s32; } else { panel_err("undefined argument(%s)\n", vrr_lfd_arglist[index].name); } argc++; } queue_delayed_work(panel->work[PANEL_WORK_UPDATE].wq, &panel->work[PANEL_WORK_UPDATE].dwork, msecs_to_jiffies(0)); mutex_unlock(&panel->panel_bl.lock); panel_info("done %d\n", argc); return size; } #ifdef CONFIG_SUPPORT_POC_SPI #define SPI_BUF_LEN 2048 u8 spi_flash_readbuf[SPI_BUF_LEN]; u32 spi_flash_readlen; u8 spi_flash_writebuf[SPI_BUF_LEN]; u32 spi_flash_writelen; static ssize_t spi_flash_ctrl_show(struct device *dev, struct device_attribute *attr, char *buf) { int i, len; mutex_lock(&sysfs_lock); if (spi_flash_writelen <= 0) { mutex_unlock(&sysfs_lock); return -EINVAL; } len = snprintf(buf, PAGE_SIZE, "send %d byte(s):\n", spi_flash_writelen); for (i = 0; i < spi_flash_writelen; i++) len += snprintf(buf + len, PAGE_SIZE - len, "0x%02X%s", spi_flash_writebuf[i], (((i + 1) % 16) == 0) || (i == spi_flash_writelen - 1) ? "\n" : " "); len += snprintf(buf + len, PAGE_SIZE - len, "receive %d byte(s):\n", spi_flash_readlen); for (i = 0; i < spi_flash_readlen; i++) len += snprintf(buf + len, PAGE_SIZE - len, "0x%02X%s", spi_flash_readbuf[i], (((i + 1) % 16) == 0) || (i == spi_flash_readlen - 1) ? "\n" : " "); spi_flash_writelen = 0; spi_flash_readlen = 0; mutex_unlock(&sysfs_lock); return len; } static ssize_t spi_flash_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_device *panel = dev_get_drvdata(dev); struct panel_spi_dev *spi_dev = &panel->panel_spi_dev; int ret, cmd_scanned, parse, cmd_input; mutex_lock(&sysfs_lock); mutex_lock(&panel->op_lock); memset(spi_flash_readbuf, 0, SPI_BUF_LEN); memset(spi_flash_writebuf, 0, SPI_BUF_LEN); spi_flash_readlen = spi_flash_writelen = 0; ret = sscanf(buf, "%d%n", &spi_flash_readlen, &parse); if (ret != 1 || parse <= 0 || spi_flash_readlen > SPI_BUF_LEN) { ret = -EINVAL; goto store_err; } cmd_scanned = parse; while (cmd_scanned < size && spi_flash_writelen < SPI_BUF_LEN) { ret = sscanf(buf + cmd_scanned, " %x%n", &cmd_input, &parse); panel_dbg("readed %d %d ret %d target str %s\n", cmd_input, parse, ret, buf + cmd_scanned); if (parse <= 0 || ret <= 0) break; spi_flash_writebuf[spi_flash_writelen++] = cmd_input & 0xFF; cmd_scanned += parse; } panel_info("send %d byte(s), receive %d byte(s)\n", spi_flash_writelen, spi_flash_readlen); print_hex_dump(KERN_ERR, __func__, DUMP_PREFIX_OFFSET, 16, 1, spi_flash_writebuf, spi_flash_writelen, false); ret = spi_dev->ops->cmd(spi_dev, spi_flash_writebuf, spi_flash_writelen, spi_flash_readbuf, spi_flash_readlen); if (ret < 0) { panel_err("failed to spi cmd 0x%0x, ret %d\n", spi_flash_writebuf[0], ret); ret = -EIO; goto store_err; } panel_info("received %d byte(s)\n", ret); print_hex_dump(KERN_ERR, __func__, DUMP_PREFIX_OFFSET, 16, 1, spi_flash_readbuf, spi_flash_readlen, false); mutex_unlock(&panel->op_lock); mutex_unlock(&sysfs_lock); return size; store_err: spi_flash_readlen = spi_flash_writelen = 0; mutex_unlock(&panel->op_lock); mutex_unlock(&sysfs_lock); return ret; } #endif #if defined(CONFIG_SUPPORT_FAST_DISCHARGE) static ssize_t enable_fd_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; snprintf(buf, PAGE_SIZE, "%u\n", panel_data->props.enable_fd); return strlen(buf); } static ssize_t enable_fd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int rc, ret; u32 prev_value, value; struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (panel_data->props.enable_fd == value) { panel_info("fast discharge is already %s\n", panel_data->props.enable_fd ? "on" : "off"); return size; } mutex_lock(&panel->op_lock); prev_value = panel_data->props.enable_fd; panel_data->props.enable_fd = value; mutex_unlock(&panel->op_lock); ret = panel_disable_disp_det_irq(panel); if (ret < 0) panel_err("failed to disable disp_det irq\n"); ret = panel_fast_discharge_set(panel); if (unlikely(ret < 0)) { panel_err("failed to write fast discharge set\n"); mutex_lock(&panel->op_lock); panel_data->props.enable_fd = prev_value; mutex_unlock(&panel->op_lock); return ret; } #ifdef CONFIG_EVASION_DISP_DET queue_delayed_work(panel->work[PANEL_WORK_EVASION_DISP_DET].wq, &panel->work[PANEL_WORK_EVASION_DISP_DET].dwork, msecs_to_jiffies(34)); #endif panel_info("fast discharge set to %s\n", panel_data->props.enable_fd ? "on" : "off"); return size; } #endif #ifdef CONFIG_SUPPORT_MASK_LAYER static ssize_t mask_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); struct panel_bl_device *panel_bl; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; panel_bl = &panel->panel_bl; sprintf(buf, "%d\n", panel_bl->props.mask_layer_br_target); return strlen(buf); } static ssize_t mask_brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); struct panel_bl_device *panel_bl; int value, rc; rc = kstrtouint(buf, 0, &value); if (rc < 0) return rc; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; panel_bl = &panel->panel_bl; if (value > panel_bl->bd->props.max_brightness) { panel_err("input:%d bd max:%d\n", value, panel_bl->bd->props.max_brightness); return -EINVAL; } panel_info("%d->%d target br updated.\n", panel_bl->props.mask_layer_br_target, value); panel_bl->props.mask_layer_br_target = value; return size; } static ssize_t actual_mask_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_info *panel_data; struct panel_device *panel = dev_get_drvdata(dev); struct panel_bl_device *panel_bl; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } panel_data = &panel->panel_data; panel_bl = &panel->panel_bl; sprintf(buf, "%d\n", panel_bl->props.mask_layer_br_actual); return strlen(buf); } #endif #define DISP_TE_POLL_SLEEP_USEC (10UL) #define DISP_TE_POLL_TIMEOUT_USEC (400 * 1000UL) static ssize_t te_check_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } /* expected gpio level : high */ ret = panel_poll_gpio(panel, PANEL_GPIO_DISP_TE, 1, DISP_TE_POLL_SLEEP_USEC, DISP_TE_POLL_TIMEOUT_USEC); if (ret == -EINVAL) { panel_err("gpio('disp-te') is not supported\n"); return -EINVAL; } if (ret == -ETIMEDOUT) panel_info("polling gpio('disp-te') timeout\n"); return sprintf(buf, "%d\n", (ret == 0) ? 1 : 0); } #ifdef CONFIG_SUPPORT_PANEL_VCOM_TRIM_TEST static ssize_t vcom_trim_show(struct device *dev, struct device_attribute *attr, char *buf) { struct panel_device *panel = dev_get_drvdata(dev); char result[SZ_32] = { 0, }; int ret; if (panel == NULL) { panel_err("panel is null\n"); return -EINVAL; } ret = panel_vcom_trim_test(panel, result, SZ_32); panel_info("ret %d\n", ret); if (ret == -ENOENT) { ret = PANEL_VCOM_TRIM_TEST_PASS; } if (ret == PANEL_VCOM_TRIM_TEST_PASS) { snprintf(buf, PAGE_SIZE, "%d\n", ret); } else { snprintf(buf, PAGE_SIZE, "%d %s\n", ret, result); } return strlen(buf); } #endif struct device_attribute panel_attrs[] = { __PANEL_ATTR_RO(lcd_type, 0444), __PANEL_ATTR_RO(window_type, 0444), __PANEL_ATTR_RO(manufacture_code, 0444), __PANEL_ATTR_RO(cell_id, 0444), __PANEL_ATTR_RO(octa_id, 0444), __PANEL_ATTR_RO(SVC_OCTA, 0444), __PANEL_ATTR_RO(SVC_OCTA_CHIPID, 0444), __PANEL_ATTR_RO(SVC_OCTA_DDI_CHIPID, 0444), #ifdef CONFIG_SUPPORT_XTALK_MODE __PANEL_ATTR_RW(xtalk_mode, 0664), #endif #ifdef CONFIG_SUPPORT_MST __PANEL_ATTR_RW(mst, 0664), #endif #ifdef CONFIG_SUPPORT_POC_FLASH __PANEL_ATTR_RW(poc, 0660), __PANEL_ATTR_RO(poc_mca, 0440), __PANEL_ATTR_RO(poc_info, 0440), #endif __PANEL_ATTR_RW(gamma_flash, 0660), __PANEL_ATTR_RO(gamma_check, 0440), #ifdef CONFIG_SUPPORT_SSR_TEST __PANEL_ATTR_RO(ssr, 0440), #endif #ifdef CONFIG_SUPPORT_ECC_TEST __PANEL_ATTR_RO(ecc, 0440), #endif #ifdef CONFIG_SUPPORT_GRAM_CHECKSUM __PANEL_ATTR_RW(gct, 0664), #endif #ifdef CONFIG_SUPPORT_PANEL_DECODER_TEST __PANEL_ATTR_RW(dsc_crc, 0664), #endif #ifdef CONFIG_SUPPORT_GRAYSPOT_TEST __PANEL_ATTR_RW(grayspot, 0664), #endif __PANEL_ATTR_RW(irc_mode, 0664), __PANEL_ATTR_RW(dia, 0664), __PANEL_ATTR_RO(color_coordinate, 0444), __PANEL_ATTR_RO(manufacture_date, 0444), __PANEL_ATTR_RO(brightness_table, 0444), __PANEL_ATTR_RW(adaptive_control, 0664), __PANEL_ATTR_RW(siop_enable, 0664), __PANEL_ATTR_RW(temperature, 0664), #ifdef CONFIG_EXYNOS_LCD_ENG __PANEL_ATTR_RW(read_mtp, 0644), __PANEL_ATTR_RW(write_mtp, 0644), #ifdef CONFIG_SUPPORT_ISC_TUNE_TEST __PANEL_ATTR_RW(stm, 0664), __PANEL_ATTR_RW(isc, 0664), #endif #endif __PANEL_ATTR_RW(mcd_mode, 0664), __PANEL_ATTR_RW(partial_disp, 0664), __PANEL_ATTR_RW(mcd_resistance, 0664), #if defined(CONFIG_EXYNOS_DECON_MDNIE_LITE) __PANEL_ATTR_RW(lux, 0644), #endif #if defined(CONFIG_EXYNOS_DECON_LCD_COPR) __PANEL_ATTR_RW(copr, 0600), __PANEL_ATTR_RO(read_copr, 0440), __PANEL_ATTR_RW(copr_roi, 0600), __PANEL_ATTR_RO(brt_avg, 0440), #endif __PANEL_ATTR_RW(alpm, 0664), __PANEL_ATTR_RW(lpm_opr, 0664), __PANEL_ATTR_RW(fingerprint, 0644), #ifdef CONFIG_SUPPORT_HMD __PANEL_ATTR_RW(hmt_bright, 0664), __PANEL_ATTR_RW(hmt_on, 0664), #endif #ifdef CONFIG_DISPLAY_USE_INFO __PANEL_ATTR_RW(dpui, 0660), __PANEL_ATTR_RW(dpui_dbg, 0660), __PANEL_ATTR_RW(dpci, 0660), __PANEL_ATTR_RW(dpci_dbg, 0660), #endif __PANEL_ATTR_RW(poc_onoff, 0664), #ifdef CONFIG_EXTEND_LIVE_CLOCK __PANEL_ATTR_RW(self_mask, 0664), __PANEL_ATTR_RO(self_mask_check, 0444), #endif #ifdef SUPPORT_NORMAL_SELF_MOVE __PANEL_ATTR_RW(self_move, 0664), #endif #ifdef CONFIG_SUPPORT_ISC_DEFECT __PANEL_ATTR_RW(isc_defect, 0664), #endif #ifdef CONFIG_SUPPORT_SPI_IF_SEL __PANEL_ATTR_RW(spi_if_sel, 0664), #endif #ifdef CONFIG_SUPPORT_CCD_TEST __PANEL_ATTR_RO(ccd_state, 0444), #endif #ifdef CONFIG_SUPPORT_DYNAMIC_HLPM __PANEL_ATTR_RW(dynamic_hlpm, 0664), #endif #ifdef CONFIG_SUPPORT_POC_SPI __PANEL_ATTR_RW(spi_flash_ctrl, 0660), #endif __PANEL_ATTR_RO(vrr, 0444), #if defined(CONFIG_PANEL_DISPLAY_MODE) __PANEL_ATTR_RW(display_mode, 0664), #endif #ifdef CONFIG_PANEL_VRR_BRIDGE __PANEL_ATTR_RW(vrr_bridge, 0664), #endif __PANEL_ATTR_RW(conn_det, 0664), #ifdef CONFIG_SUPPORT_MAFPC __PANEL_ATTR_RO(mafpc_time, 0444), __PANEL_ATTR_RO(mafpc_check, 0440), #endif __PANEL_ATTR_RW(vrr_lfd, 0664), #if defined(CONFIG_SUPPORT_FAST_DISCHARGE) __PANEL_ATTR_RW(enable_fd, 0664), #endif #ifdef CONFIG_SUPPORT_MASK_LAYER __PANEL_ATTR_RW(mask_brightness, 0664), __PANEL_ATTR_RO(actual_mask_brightness, 0444), #endif #ifdef CONFIG_SUPPORT_BRIGHTDOT_TEST __PANEL_ATTR_RW(brightdot, 0664), #endif #ifdef CONFIG_SUPPORT_PANEL_VCOM_TRIM_TEST __PANEL_ATTR_RO(vcom_trim, 0440), #endif __PANEL_ATTR_RO(te_check, 0440), }; static int attr_find_and_store(struct device *dev, void *data) { struct kernfs_node *kn; struct attribute *attr; struct attr_store_args *args = data; if (!dev || !args || !args->name || !args->buf) return -EINVAL; kn = kernfs_find_and_get(dev->kobj.sd, args->name); if (!kn) return 0; attr = kn->priv; if (attr) { struct device_attribute *dev_attr = container_of(attr, struct device_attribute, attr); if (dev_attr->store) dev_attr->store(dev, dev_attr, args->buf, args->size); } kernfs_put(kn); return 0; } ssize_t attr_store_for_each(struct class *class, const char *name, const char *buf, size_t size) { struct attr_store_args args = { .name = name, .buf = buf, .size = size, }; if (!class || !name || !buf) return -EINVAL; return class_for_each_device(class, NULL, &args, attr_find_and_store); } EXPORT_SYMBOL(attr_store_for_each); static int attr_find_and_show(struct device *dev, void *data) { struct kernfs_node *kn; struct attribute *attr; struct attr_show_args *args = data; if (!dev || !args || !args->name || !args->buf) return -EINVAL; kn = kernfs_find_and_get(dev->kobj.sd, args->name); if (!kn) return 0; attr = kn->priv; if (attr) { struct device_attribute *dev_attr = container_of(attr, struct device_attribute, attr); if (dev_attr->show) dev_attr->show(dev, dev_attr, args->buf); } kernfs_put(kn); return 0; } ssize_t attr_show_for_each(struct class *class, const char *name, char *buf) { struct attr_show_args args = { .name = name, .buf = buf, }; if (!class || !name || !buf) return -EINVAL; return class_for_each_device(class, NULL, &args, attr_find_and_show); } EXPORT_SYMBOL(attr_show_for_each); static int attr_exist(struct device *dev, void *data) { struct kernfs_node *kn; struct attr_exist_args *args = data; if (!dev || !args || !args->name) return -EINVAL; kn = kernfs_find_and_get(dev->kobj.sd, args->name); if (!kn) return 0; kernfs_put(kn); return 1; } ssize_t attr_exist_for_each(struct class *class, const char *name) { struct attr_exist_args args = { .name = name, }; if (!class || !name) return -EINVAL; return class_for_each_device(class, NULL, &args, attr_exist); } EXPORT_SYMBOL(attr_exist_for_each); int panel_remove_svc_octa(struct panel_device *panel) { struct device *lcd_dev; struct kobject *top_kobj = NULL; struct kernfs_node *kn = NULL; struct kobject *svc_kobj = NULL; int ret = 0; if (!panel) return -EINVAL; lcd_dev = panel->lcd_dev; if (unlikely(!lcd_dev)) { panel_err("lcd device not exist\n"); return -ENODEV; } if (panel->panel_bl.bd) { top_kobj = &panel->panel_bl.bd->dev.kobj.kset->kobj; kn = kernfs_find_and_get(top_kobj->sd, "svc"); } if (kn) { svc_kobj = kn->priv; if (!IS_ERR_OR_NULL(svc_kobj)) { sysfs_remove_link(svc_kobj, "OCTA"); kobject_put(svc_kobj); } else { panel_err("fail to find svc\n"); ret = -ENODEV; goto exit; } panel_info("succeed to remove svc/OCTA\n"); } exit: if (kn) kernfs_put(kn); return ret; } int panel_create_svc_octa(struct panel_device *panel) { struct device *lcd_dev; struct kobject *top_kobj = NULL; struct kernfs_node *kn = NULL; struct kobject *svc_kobj = NULL; int ret = 0; if (!panel) return -EINVAL; lcd_dev = panel->lcd_dev; if (unlikely(!lcd_dev)) { panel_err("lcd device not exist\n"); return -ENODEV; } if (panel->panel_bl.bd) { top_kobj = &panel->panel_bl.bd->dev.kobj.kset->kobj; kn = kernfs_find_and_get(top_kobj->sd, "svc"); } svc_kobj = kn ? kn->priv : kobject_create_and_add("svc", top_kobj); if (!svc_kobj) { panel_err("%s: fail to create svc\n", __func__); ret = -ENODEV; goto exit; } if (!IS_ERR_OR_NULL(svc_kobj)) { ret = sysfs_create_link(svc_kobj, &lcd_dev->kobj, "OCTA"); if (ret) panel_err("failed to create svc/OCTA/\n"); else panel_info("succeeded to create svc/OCTA/\n"); } else { panel_err("failed to find svc kobject\n"); } exit: if (kn) kernfs_put(kn); return ret; } int panel_remove_sysfs(struct panel_device *panel) { struct device *lcd_dev; size_t i; if (!panel) return -EINVAL; lcd_dev = panel->lcd_dev; if (unlikely(!lcd_dev)) { panel_err("lcd device not exist\n"); return -ENODEV; } for (i = 0; i < ARRAY_SIZE(panel_attrs); i++) device_remove_file(panel->lcd_dev, &panel_attrs[i]); panel_info("remove sysfs done\n"); return 0; } int panel_create_sysfs(struct panel_device *panel) { struct device *lcd_dev; size_t i; int ret = 0; if (!panel) return -EINVAL; lcd_dev = panel->lcd_dev; if (unlikely(!lcd_dev)) { panel_err("lcd device not exist\n"); return -ENODEV; } for (i = 0; i < ARRAY_SIZE(panel_attrs); i++) { ret = device_create_file(panel->lcd_dev, &panel_attrs[i]); if (ret < 0) { panel_err("failed to add sysfs(%s) entries, %d\n", panel_attrs[i].attr.name, ret); goto err; } } return 0; err: panel_remove_sysfs(panel); return ret; } int panel_sysfs_probe(struct panel_device *panel) { int ret; if (!panel) return -EINVAL; ret = panel_create_svc_octa(panel); if (ret < 0) return ret; ret = panel_create_sysfs(panel); if (ret < 0) goto err; return 0; err: panel_remove_svc_octa(panel); return ret; } int panel_sysfs_remove(struct panel_device *panel) { int ret = 0; if (!panel) return -EINVAL; ret = panel_remove_sysfs(panel); if (ret < 0) return ret; panel_remove_svc_octa(panel); return 0; }