// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) Samsung Electronics Co., Ltd. * Gwanghui Lee * * 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 #include #include #include #include "panel.h" #include "panel_bl.h" #include "panel_drv.h" #include "panel_debug.h" #include "panel_poc.h" #ifdef CONFIG_SUPPORT_POC_SPI #include "panel_spi.h" #endif static u8 *poc_wr_img; static u8 *poc_rd_img; const char * const poc_op[MAX_POC_OP] = { [POC_OP_NONE] = "POC_OP_NONE", [POC_OP_ERASE] = "POC_OP_ERASE", [POC_OP_WRITE] = "POC_OP_WRITE", [POC_OP_READ] = "POC_OP_READ", [POC_OP_CANCEL] = "POC_OP_CANCEL", [POC_OP_CHECKSUM] = "POC_OP_CHECKSUM", [POC_OP_CHECKPOC] = "POC_OP_CHECKPOC", [POC_OP_SECTOR_ERASE] = "POC_OP_SECTOR_ERASE", [POC_OP_BACKUP] = "POC_OP_BACKUP", [POC_OP_IMG_WRITE] = "POC_OP_IMG_WRITE", [POC_OP_IMG_READ] = "POC_OP_IMG_READ", [POC_OP_IMG_READ_TEST] = "POC_OP_IMG_READ_TEST", [POC_OP_DIM_WRITE] = "POC_OP_DIM_WRITE", [POC_OP_DIM_READ] = "POC_OP_DIM_READ", [POC_OP_DIM_CHKSUM] = "POC_OP_DIM_CHKSUM", [POC_OP_DIM_VALID] = "POC_OP_DIM_VALID", [POC_OP_DIM_READ_TEST] = "POC_OP_DIM_READ_TEST", [POC_OP_SELF_TEST] = "POC_OP_SELF_TEST", [POC_OP_READ_TEST] = "POC_OP_READ_TEST", [POC_OP_WRITE_TEST] = "POC_OP_WRITE_TEST", [POC_OP_DIM_READ_FROM_FILE] = "POC_OP_DIM_READ_FROM_FILE", [POC_OP_MTP_READ] = "POC_OP_MTP_READ", [POC_OP_MCD_READ] = "POC_OP_MCD_READ", #ifdef CONFIG_SUPPORT_POC_SPI [POC_OP_SET_CONN_SRC] = "POC_OP_SET_CONN_SRC", [POC_OP_SET_SPI_SPEED] = "POC_OP_SET_SPI_SPEED", [POC_OP_READ_SPI_STATUS_REG] = "POC_OP_READ_SPI_STATUS_REG", #endif [POC_OP_INITIALIZE] = "POC_OP_INITIALIZE", [POC_OP_UNINITIALIZE] = "POC_OP_UNINITIALIZE", [POC_OP_GM2_READ] = "POC_OP_GM2_READ", }; const char * const str_partition_write_check[] = { [PARTITION_WRITE_CHECK_NONE] = "none", [PARTITION_WRITE_CHECK_NOK] = "nok", [PARTITION_WRITE_CHECK_OK] = "ok", }; static int panel_do_poc_seqtbl_by_index_nolock(struct panel_poc_device *poc_dev, int index) { struct panel_device *panel = to_panel_device(poc_dev); if (panel->poc_dev.seqtbl == NULL) { panel_err("invalid seqtbl\n"); return -EINVAL; } if (unlikely(index < 0 || index >= MAX_POC_SEQ)) { panel_err("invalid parameter (panel %p, index %d)\n", panel, index); return -EINVAL; } return excute_seqtbl_nolock(panel, poc_dev->seqtbl, index); } int panel_do_poc_seqtbl_by_index(struct panel_poc_device *poc_dev, int index) { struct panel_device *panel = to_panel_device(poc_dev); int ret; mutex_lock(&panel->op_lock); ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, index); mutex_unlock(&panel->op_lock); return ret; } static struct seqinfo *find_poc_seqtbl_by_index(struct panel_poc_device *poc_dev, u32 index) { struct seqinfo *tbl; if (unlikely(!poc_dev->seqtbl)) { panel_err("seqtbl not exist\n"); return NULL; } if (unlikely(index >= MAX_POC_SEQ)) { panel_err("invalid parameter (index %d)\n", index); return NULL; } tbl = &poc_dev->seqtbl[index]; if (tbl != NULL) panel_dbg("found %s panel seqtbl\n", tbl->name); return tbl; } static bool panel_poc_seq_exist(struct panel_poc_device *poc_dev, u32 index) { struct seqinfo *tbl; tbl = find_poc_seqtbl_by_index(poc_dev, index); if (tbl == NULL || tbl->cmdtbl == NULL || tbl->size == 0) return false; return true; } static int poc_get_poc_chksum(struct panel_device *panel) { struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_info *panel_data = &panel->panel_data; int ret; if (sizeof(poc_info->poc_chksum) != PANEL_POC_CHKSUM_LEN) { panel_err("invalid poc control length\n"); return -EINVAL; } ret = resource_copy_by_name(panel_data, poc_info->poc_chksum, "poc_chksum"); if (unlikely(ret < 0)) { panel_err("failed to copy resource(poc_chksum)\n"); return ret; } panel_info("poc_chksum 0x%02X 0x%02X 0x%02X 0x%02X, result %d\n", poc_info->poc_chksum[0], poc_info->poc_chksum[1], poc_info->poc_chksum[2], poc_info->poc_chksum[3], poc_info->poc_chksum[4]); return 0; } #ifdef CONFIG_SUPPORT_POC_SPI int _spi_poc_erase(struct panel_device *panel, int addr, int len) { struct panel_spi_dev *spi_dev = &panel->panel_spi_dev; struct spi_data_packet data_packet = { 0, }; int ret = 0; if (unlikely(!SPI_IS_READY(spi_dev))) { panel_err("spi dev is not ready\n"); return -EINVAL; } if (addr % POC_PAGE > 0 || addr < 0) { panel_err("failed to start erase. invalid addr\n"); return -EINVAL; } if (len <= 0) { panel_err("failed to start erase. invalid len %d\n", len); return -EINVAL; } panel_info("++ addr 0x%06X(%d), 0x%X(%d) bytes\n", addr, addr, len, len); data_packet.addr = addr; data_packet.size = len; ret = spi_dev->ops->erase(spi_dev, &data_packet); if (ret != 0) { panel_err("failed to erase addr: 0x%06x size %d ret %d\n", data_packet.addr, data_packet.size, ret); ret = -EIO; } panel_info("-- ret %d\n", ret); return ret; } #endif int _dsi_poc_erase(struct panel_device *panel, int addr, int len) { struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; int ret, sz_block = 0, erased_size = 0, erase_seq_index; if (addr % POC_PAGE > 0) { panel_err("failed to start erase. invalid addr\n"); return -EINVAL; } if (len < 0 || addr + len > get_poc_partition_size(poc_dev, POC_IMG_PARTITION)) { panel_err("failed to start erase. range exceeded\n"); return -EINVAL; } len = ALIGN(len, SZ_4K); panel_info("poc erase +++, 0x%x, %d\n", addr, len); mutex_lock(&panel->op_lock); ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_ERASE_ENTER_SEQ); if (unlikely(ret < 0)) { panel_err("failed to poc-erase-enter-seq\n"); goto out_poc_erase; } while (len > erased_size) { if ((len >= erased_size + SZ_64K) && panel_poc_seq_exist(poc_dev, POC_ERASE_64K_SEQ)) { erase_seq_index = POC_ERASE_64K_SEQ; sz_block = SZ_64K; } else if ((len >= erased_size + SZ_32K) && panel_poc_seq_exist(poc_dev, POC_ERASE_32K_SEQ)) { erase_seq_index = POC_ERASE_32K_SEQ; sz_block = SZ_32K; } else { erase_seq_index = POC_ERASE_4K_SEQ; sz_block = SZ_4K; } poc_info->waddr = addr + erased_size; ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, erase_seq_index); if (unlikely(ret < 0)) { panel_err("failed to poc-erase-seq 0x%x\n", addr + erased_size); goto out_poc_erase; } panel_info("erased addr %06X, sz_block %06X\n", addr + erased_size, sz_block); if (atomic_read(&poc_dev->cancel)) { panel_err("stopped by user at erase 0x%x\n", erased_size); goto cancel_poc_erase; } erased_size += sz_block; } ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_ERASE_EXIT_SEQ); if (unlikely(ret < 0)) { panel_err("failed to poc-erase-exit-seq\n"); goto out_poc_erase; } mutex_unlock(&panel->op_lock); panel_info("poc erase ---\n"); return 0; cancel_poc_erase: ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_ERASE_EXIT_SEQ); if (unlikely(ret < 0)) panel_err("failed to poc-erase-exit-seq\n"); ret = -EIO; atomic_set(&poc_dev->cancel, 0); out_poc_erase: mutex_unlock(&panel->op_lock); return ret; } int poc_erase(struct panel_device *panel, int addr, int len) { #ifdef CONFIG_SUPPORT_POC_SPI struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; if (poc_info->conn_src == POC_CONN_SRC_SPI) return _spi_poc_erase(panel, addr, len); #endif return _dsi_poc_erase(panel, addr, len); } #ifdef CONFIG_SUPPORT_POC_SPI static int _spi_poc_read_data(struct panel_device *panel, u8 *buf, u32 addr, u32 len) { struct panel_spi_dev *spi_dev = &panel->panel_spi_dev; struct spi_data_packet data_packet = { 0, }; int i, ret = 0; int spi_buffer_size; u8 *spi_buffer; int read_len; if (unlikely(!SPI_IS_READY(spi_dev))) { panel_err("spi dev is not ready\n"); return -EINVAL; } if (len <= 0) { panel_err("failed to start read. invalid len %d\n", len); return -EINVAL; } panel_info("++ addr 0x%06X(%d), 0x%X(%d) bytes\n", addr, addr, len, len); spi_buffer_size = spi_dev->ops->get_buf_size(spi_dev, PANEL_SPI_GET_READ_SIZE); if (spi_buffer_size < 1) { panel_err("got invalid buffer size %d\n", spi_buffer_size); return -EINVAL; } if (spi_buffer_size > SZ_4K) { panel_warn("buffer size set to %d->%d\n", spi_buffer_size, SZ_4K); spi_buffer_size = SZ_4K; } spi_buffer = (u8 *)devm_kzalloc(panel->dev, spi_buffer_size * sizeof(u8), GFP_KERNEL); if (!spi_buffer) { panel_err("memory allocation failed\n"); return -ENOMEM; } for (i = 0; i < len;) { memset(spi_buffer, 0, sizeof(spi_buffer_size)); read_len = spi_buffer_size; if (read_len > len - i) read_len = len - i; data_packet.addr = addr + i; data_packet.buf = spi_buffer; data_packet.size = read_len; ret = spi_dev->ops->read(spi_dev, &data_packet); if (ret != 0) { panel_err("failed to read addr: 0x%06x size %d ret %d\n", data_packet.addr, data_packet.size, ret); ret = -EIO; goto error_exit; } memcpy(&buf[i], spi_buffer, read_len); panel_dbg("[%06X] 0x%02X, len %d\n", data_packet.addr, buf[i], data_packet.size); i += read_len; } error_exit: panel_info("-- ret %d\n", ret); devm_kfree(panel->dev, spi_buffer); return ret; } #endif static int _dsi_poc_read_data(struct panel_device *panel, u8 *buf, u32 addr, u32 len) { struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_info *panel_data = &panel->panel_data; struct panel_poc_info *poc_info = &poc_dev->poc_info; int i, ret = 0; u32 poc_addr; panel_info("poc read addr 0x%06X, %d(0x%X) bytes +++\n", addr, len, len); ret = poc_get_poc_chksum(panel); if (unlikely(ret < 0)) { panel_err("failed to read poc cheksum seq\n"); goto exit; } mutex_lock(&panel->op_lock); poc_info->state = POC_STATE_RD_PROGRESS; if (poc_info->poc_chksum[0] != 0x00 || poc_info->poc_chksum[1] != 0x00) { ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_READ_PRE_ENTER_SEQ); if (unlikely(ret < 0)) { panel_err("failed to read poc-rd-pre-enter seq\n"); goto out_poc_read; } } ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_READ_ENTER_SEQ); if (unlikely(ret < 0)) { panel_err("failed to read poc-rd-enter seq\n"); goto out_poc_read; } for (i = 0; i < len; i++) { if (atomic_read(&poc_dev->cancel)) { panel_err("stopped by user at %d bytes\n", i); goto cancel_poc_read; } poc_addr = addr + i; poc_info->raddr = poc_addr; ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_READ_DAT_SEQ); if (unlikely(ret < 0)) { panel_err("failed to read poc-rd-dat seq\n"); goto out_poc_read; } ret = resource_copy_by_name(panel_data, &buf[i], "poc_data"); if (unlikely(ret < 0)) { panel_err("failed to copy resource(poc_data)\n"); goto out_poc_read; } if ((i % 4096) == 0) panel_info("[%04d] addr %06X %02X\n", i, poc_addr, buf[i]); } ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_READ_EXIT_SEQ); if (unlikely(ret < 0)) { panel_err("failed to read poc-rd-exit seq\n"); goto out_poc_read; } panel_info("poc read addr 0x%06X, %d(0x%X) bytes ---\n", addr, len, len); poc_info->state = POC_STATE_RD_COMPLETE; mutex_unlock(&panel->op_lock); return 0; cancel_poc_read: ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_READ_EXIT_SEQ); if (unlikely(ret < 0)) panel_err("failed to read poc-rd-exit seq\n"); ret = -EIO; atomic_set(&poc_dev->cancel, 0); out_poc_read: poc_info->state = POC_STATE_RD_FAILED; mutex_unlock(&panel->op_lock); exit: return ret; } int poc_read_data(struct panel_device *panel, u8 *buf, u32 addr, u32 len) { #ifdef CONFIG_SUPPORT_POC_SPI struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; if (poc_info->conn_src == POC_CONN_SRC_SPI) return _spi_poc_read_data(panel, buf, addr, len); #endif return _dsi_poc_read_data(panel, buf, addr, len); } #ifdef CONFIG_SUPPORT_POC_SPI static int _spi_poc_write_data(struct panel_device *panel, u8 *data, u32 addr, u32 size) { struct panel_spi_dev *spi_dev = &panel->panel_spi_dev; struct spi_data_packet data_packet = { 0, }; int i, ret = 0; int spi_buffer_size; int copy_len; if (unlikely(!SPI_IS_READY(spi_dev))) { panel_err("spi dev is not ready\n"); return -EINVAL; } if (addr % POC_PAGE > 0) { panel_err("failed to start write. invalid addr\n"); return -EINVAL; } panel_info("++ addr 0x%06X(%d), 0x%X(%d) bytes\n", addr, addr, size, size); spi_buffer_size = spi_dev->ops->get_buf_size(spi_dev, PANEL_SPI_GET_WRITE_SIZE); for (i = 0; i < size;) { copy_len = size - i; if (copy_len > spi_buffer_size) copy_len = spi_buffer_size; data_packet.addr = addr + i; data_packet.buf = data + i; data_packet.size = copy_len; ret = spi_dev->ops->write(spi_dev, &data_packet); if (ret != 0) { panel_err("failed to write addr: 0x%06x size %d ret %d\n", data_packet.addr, data_packet.size, ret); ret = -EIO; break; } panel_dbg("[%06X] 0x%02X, len %d\n", data_packet.addr, data_packet.buf, data_packet.size); i += copy_len; } panel_info("-- ret %d\n", ret); return ret; } #endif static int _dsi_poc_write_data(struct panel_device *panel, u8 *data, u32 addr, u32 size) { struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; int i, ret = 0; int copy_len; u32 poc_addr; bool write_stt_seq_exist; bool write_end_seq_exist; write_stt_seq_exist = panel_poc_seq_exist(poc_dev, POC_WRITE_STT_SEQ); write_end_seq_exist = panel_poc_seq_exist(poc_dev, POC_WRITE_END_SEQ); poc_info->wdata = (u8 *)devm_kzalloc(panel->dev, poc_info->wdata_len * sizeof(u8), GFP_KERNEL); if (!poc_info->wdata) { panel_err("memory allocation failed\n"); return -ENOMEM; } mutex_lock(&panel->op_lock); ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_WRITE_ENTER_SEQ); if (unlikely(ret < 0)) { panel_err("failed to read poc-wr-enter-seq\n"); goto out_poc_write; } for (i = 0; i < size;) { if (atomic_read(&poc_dev->cancel)) { panel_err("stopped by user at %d bytes\n", i); goto cancel_poc_write; } poc_addr = addr + i; poc_info->waddr = poc_addr; if (write_stt_seq_exist && (i == 0 || (poc_addr & 0xFF) == 0)) { ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_WRITE_STT_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write poc-wr-stt seq\n"); goto out_poc_write; } } memset(poc_info->wdata, 0x00, poc_info->wdata_len); copy_len = size - i; if (copy_len > poc_info->wdata_len) copy_len = poc_info->wdata_len; memcpy(poc_info->wdata, data + i, copy_len); ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_WRITE_DAT_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write poc-wr-img seq\n"); goto out_poc_write; } if ((i % 4096) == 0) panel_info("addr %06X %02X\n", poc_addr, data[i]); if (write_end_seq_exist && ((poc_addr & 0xFF) == 0xFF || i == (size - 1))) { ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_WRITE_END_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write poc-wr-exit seq\n"); goto out_poc_write; } } i += copy_len; } ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_WRITE_EXIT_SEQ); if (unlikely(ret < 0)) { panel_err("failed to write poc-wr-exit seq\n"); goto out_poc_write; } panel_info("poc write addr 0x%06X, %d(0x%X) bytes\n", addr, size, size); mutex_unlock(&panel->op_lock); if (poc_info->wdata) devm_kfree(panel->dev, poc_info->wdata); return 0; cancel_poc_write: ret = panel_do_poc_seqtbl_by_index_nolock(poc_dev, POC_WRITE_EXIT_SEQ); if (unlikely(ret < 0)) panel_err("failed to read poc-wr-exit seq\n"); ret = -EIO; atomic_set(&poc_dev->cancel, 0); out_poc_write: mutex_unlock(&panel->op_lock); if (poc_info->wdata) devm_kfree(panel->dev, poc_info->wdata); return ret; } int poc_write_data(struct panel_device *panel, u8 *data, u32 addr, u32 size) { #ifdef CONFIG_SUPPORT_POC_SPI struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; if (poc_info->conn_src == POC_CONN_SRC_SPI) return _spi_poc_write_data(panel, data, addr, size); #endif return _dsi_poc_write_data(panel, data, addr, size); } int poc_memory_initialize(struct panel_device *panel) { int ret = 0; #ifdef CONFIG_SUPPORT_POC_SPI struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_spi_dev *spi_dev = &panel->panel_spi_dev; if (poc_info->conn_src != POC_CONN_SRC_SPI) return ret; ret = spi_dev->ops->init(spi_dev); if (ret != 0) { panel_err("failed to initialize memory %d\n", ret); return -EIO; } #endif return ret; } int poc_memory_uninitialize(struct panel_device *panel) { int ret = 0; #ifdef CONFIG_SUPPORT_POC_SPI struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_spi_dev *spi_dev = &panel->panel_spi_dev; if (poc_info->conn_src != POC_CONN_SRC_SPI) return ret; ret = spi_dev->ops->exit(spi_dev); if (ret != 0) { panel_err("failed to uninitialize memory %d\n", ret); return -EIO; } #endif return ret; } static int poc_get_octa_poc(struct panel_device *panel) { struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_info *panel_data = &panel->panel_data; u8 octa_id[PANEL_OCTA_ID_LEN] = { 0, }; int ret; ret = resource_copy_by_name(panel_data, octa_id, "octa_id"); if (unlikely(ret < 0)) { panel_err("failed to copy resource(octa_id) (ret %d)\n", ret); return ret; } poc_info->poc = octa_id[1] & 0x0F; panel_info("poc %d\n", poc_info->poc); return 0; } static int poc_get_poc_ctrl(struct panel_device *panel) { struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_info *panel_data = &panel->panel_data; int ret; if (sizeof(poc_info->poc_ctrl) != PANEL_POC_CTRL_LEN) { panel_err("invalid poc control length\n"); return -EINVAL; } mutex_lock(&panel->op_lock); panel_set_key(panel, 3, true); ret = panel_resource_update_by_name(panel, "poc_ctrl"); panel_set_key(panel, 3, false); mutex_unlock(&panel->op_lock); if (unlikely(ret < 0)) { panel_err("failed to update resource(poc_ctrl)\n"); return ret; } ret = resource_copy_by_name(panel_data, poc_info->poc_ctrl, "poc_ctrl"); if (unlikely(ret < 0)) { panel_err("failed to copy resource(poc_ctrl)\n"); return ret; } panel_info("poc_ctrl 0x%02X 0x%02X 0x%02X 0x%02X\n", poc_info->poc_ctrl[0], poc_info->poc_ctrl[1], poc_info->poc_ctrl[2], poc_info->poc_ctrl[3]); return 0; } static int poc_data_backup(struct panel_device *panel, u8 *buf, int size, char *filename) { #if 0 struct file *fp; mm_segment_t old_fs; old_fs = get_fs(); set_fs(KERNEL_DS); panel_info("size %d\n", size); fp = filp_open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_SYNC, 0660); if (IS_ERR(fp)) { panel_err("fail to open log file\n"); goto open_err; } vfs_write(fp, (u8 __user *)buf, size, &fp->f_pos); panel_info("write %d bytes done!!\n", size); filp_close(fp, current->files); set_fs(old_fs); return 0; open_err: set_fs(old_fs); #endif return -1; } static int read_poc_partition_magicnum(struct panel_poc_device *poc_dev, int index) { struct panel_device *panel = to_panel_device(poc_dev); u32 value = 0; int ret; if (!IS_PANEL_ACTIVE(panel)) return -EAGAIN; if (unlikely(index >= poc_dev->nr_partition)) { panel_err("invalid partition index %d\n", index); return -EINVAL; } if (poc_dev->partition[index].magicnum_size != 0) { ret = poc_read_data(panel, (u8 *)&value, poc_dev->partition[index].magicnum_addr, poc_dev->partition[index].magicnum_size); if (unlikely(ret < 0)) { panel_err("failed to read poc data\n"); return ret; } poc_dev->partition[index].magicnum_by_read = value; poc_dev->partition[index].write_check = (poc_dev->partition[index].magicnum == poc_dev->partition[index].magicnum_by_read) ? PARTITION_WRITE_CHECK_OK : PARTITION_WRITE_CHECK_NOK; poc_dev->partition[index].cache[PARTITION_REGION_MAGIC] = true; } else { panel_info("partition[%d] has no magicnum. write_check set to true\n", index); poc_dev->partition[index].write_check = PARTITION_WRITE_CHECK_OK; poc_dev->partition[index].cache[PARTITION_REGION_MAGIC] = true; } return 0; } static int read_poc_partition_chksum(struct panel_poc_device *poc_dev, int index) { struct panel_device *panel = to_panel_device(poc_dev); int ret; if (!IS_PANEL_ACTIVE(panel)) return -EAGAIN; if (unlikely(index >= poc_dev->nr_partition)) { panel_err("invalid partition index %d\n", index); return -EINVAL; } if (poc_dev->partition[index].checksum_size != 0) { ret = poc_read_data(panel, &poc_rd_img[poc_dev->partition[index].checksum_addr], poc_dev->partition[index].checksum_addr, poc_dev->partition[index].checksum_size); if (unlikely(ret < 0)) { panel_err("failed to read poc data\n"); return ret; } poc_dev->partition[index].chksum_by_read = ntohs(*(u16 *)&poc_rd_img[poc_dev->partition[index].checksum_addr]); poc_dev->partition[index].cache[PARTITION_REGION_CHKSUM] = true; } return 0; } static int read_poc_partition_data(struct panel_poc_device *poc_dev, int index) { struct panel_device *panel = to_panel_device(poc_dev); u16 chksum = 0; int i, k, ret; if (!IS_PANEL_ACTIVE(panel)) return -EAGAIN; if (unlikely(index >= poc_dev->nr_partition)) { panel_err("invalid partition index %d\n", index); return -EINVAL; } for (k = 0; k < MAX_POC_PARTITION_DATA; k++) { if (poc_dev->partition[index].data[k].data_size != 0) { ret = poc_read_data(panel, &poc_rd_img[poc_dev->partition[index].data[k].data_addr], poc_dev->partition[index].data[k].data_addr, poc_dev->partition[index].data[k].data_size); if (unlikely(ret < 0)) { panel_err("failed to read poc data\n"); return ret; } panel_info("partition %d data %d addr 0x%06x size %d\n", index, k, poc_dev->partition[index].data[k].data_addr, poc_dev->partition[index].data[k].data_size); for (i = 0; i < poc_dev->partition[index].data[k].data_size; i++) chksum += poc_rd_img[poc_dev->partition[index].data[k].data_addr + i]; } } poc_dev->partition[index].chksum_by_calc = chksum; poc_dev->partition[index].cache[PARTITION_REGION_DATA] = true; return 0; } int read_poc_partition_region(struct panel_poc_device *poc_dev, int index, int region, bool force) { int ret = 0; if (unlikely(index >= poc_dev->nr_partition)) { panel_err("invalid partition index %d\n", index); return -EINVAL; } if (unlikely(region >= MAX_PARTITION_REGION)) { panel_err("invalid partition region %d\n", region); return -EINVAL; } if (force || poc_dev->partition[index].cache[region] == false) { if (region == PARTITION_REGION_MAGIC) ret = read_poc_partition_magicnum(poc_dev, index); else if (region == PARTITION_REGION_CHKSUM) ret = read_poc_partition_chksum(poc_dev, index); else if (region == PARTITION_REGION_DATA) ret = read_poc_partition_data(poc_dev, index); if (unlikely(ret < 0)) { panel_err("failed to read data (partition:%d, region:%d, ret:%d)\n", index, region, ret); return ret; } } return 0; } int read_poc_partition(struct panel_poc_device *poc_dev, int index) { struct panel_device *panel = to_panel_device(poc_dev); int ret, region; if (!IS_PANEL_ACTIVE(panel)) return -EAGAIN; if (unlikely(index >= poc_dev->nr_partition)) { panel_err("invalid partition index %d\n", index); return -EINVAL; } poc_dev->partition[index].preload_done = false; poc_dev->partition[index].chksum_ok = false; poc_dev->partition[index].chksum_by_calc = 0; poc_dev->partition[index].chksum_by_read = 0; poc_dev->partition[index].write_check = PARTITION_WRITE_CHECK_NONE; memset(poc_dev->partition[index].cache, 0, sizeof(poc_dev->partition[index].cache)); for (region = 0; region < MAX_PARTITION_REGION; region++) { ret = read_poc_partition_region(poc_dev, index, region, true); if (unlikely(ret < 0)) { panel_err("failed to read data (partition:%d, region:%d, ret:%d)\n", index, region, ret); goto err_read; } } // print_hex_dump(KERN_ERR, "read_poc_partition ", DUMP_PREFIX_ADDRESS, 16, 1, poc_rd_img + poc_dev->partition[index].addr, poc_dev->partition[index].size, false); poc_dev->partition[index].preload_done = true; poc_dev->partition[index].chksum_ok = (poc_dev->partition[index].chksum_by_calc == poc_dev->partition[index].chksum_by_read); panel_info("read partition[%d] chksum:%s(%04X,%04X), magic:%s(%X,%X)\n", index, poc_dev->partition[index].chksum_ok ? "OK" : "NOK", poc_dev->partition[index].chksum_by_calc, poc_dev->partition[index].chksum_by_read, str_partition_write_check[poc_dev->partition[index].write_check], poc_dev->partition[index].magicnum, poc_dev->partition[index].magicnum_by_read); return 0; err_read: return ret; } int get_poc_partition_addr(struct panel_poc_device *poc_dev, int index) { if (unlikely(index >= poc_dev->nr_partition)) { panel_err("invalid partition index %d\n", index); return -EINVAL; } return poc_dev->partition[index].addr; } int get_poc_partition_size(struct panel_poc_device *poc_dev, int index) { if (unlikely(index >= poc_dev->nr_partition)) { panel_err("invalid partition index %d\n", index); return -EINVAL; } return poc_dev->partition[index].size; } int check_poc_partition_exists(struct panel_poc_device *poc_dev, int index) { int ret; ret = read_poc_partition_region(poc_dev, index, PARTITION_REGION_MAGIC, false); if (unlikely(ret < 0)) { panel_err("failed to read magic (partition:%d, ret:%d)\n", index, ret); return ret; } return poc_dev->partition[index].write_check; } EXPORT_SYMBOL(check_poc_partition_exists); int get_poc_partition_chksum(struct panel_poc_device *poc_dev, int index, u32 *chksum_ok, u32 *chksum_by_calc, u32 *chksum_by_read) { int ret; ret = read_poc_partition_region(poc_dev, index, PARTITION_REGION_DATA, false); if (unlikely(ret < 0)) { panel_err("failed to read data (partition:%d, ret:%d)\n", index, ret); return ret; } ret = read_poc_partition_region(poc_dev, index, PARTITION_REGION_CHKSUM, false); if (unlikely(ret < 0)) { panel_err("failed to read chksum (partition:%d, ret:%d)\n", index, ret); return ret; } *chksum_ok = poc_dev->partition[index].chksum_ok; *chksum_by_read = poc_dev->partition[index].chksum_by_read; *chksum_by_calc = poc_dev->partition[index].chksum_by_calc; return 0; } EXPORT_SYMBOL(get_poc_partition_chksum); int check_poc_partition_chksum(struct panel_poc_device *poc_dev, int index) { int ret; int chksum_ok; int chksum_by_read; int chksum_by_calc; ret = get_poc_partition_chksum(poc_dev, index, &chksum_ok, &chksum_by_calc, &chksum_by_read); if (unlikely(ret < 0)) { panel_err("failed to get chksum (partition:%d, ret:%d)\n", index, ret); return ret; } return chksum_ok; } int cmp_poc_partition_data(struct panel_poc_device *poc_dev, int partition_index, int data_area_index, u8 *buf, u32 size) { int ret = 0; if (unlikely(partition_index >= poc_dev->nr_partition)) { panel_err("invalid partition index %d\n", partition_index); return -EINVAL; } if (unlikely(data_area_index >= MAX_POC_PARTITION_DATA)) { panel_err("invalid partition data area index %d\n", data_area_index); return -EINVAL; } ret = read_poc_partition_region(poc_dev, partition_index, PARTITION_REGION_DATA, false); if (unlikely(ret < 0)) { panel_err("failed to read data (partition:%d, data_area %d, ret:%d)\n", partition_index, data_area_index, ret); return ret; } return (size != poc_dev->partition[partition_index].data[data_area_index].data_size) || memcmp(&poc_rd_img[poc_dev->partition[partition_index].data[data_area_index].data_addr], buf, size); } int copy_poc_partition(struct panel_poc_device *poc_dev, u8 *dst, int index, int offset, int size) { if (unlikely(index >= poc_dev->nr_partition)) { panel_err("invalid partition index %d\n", index); return -EINVAL; } if (unlikely(offset + size > poc_dev->partition[index].size)) { panel_err("invalid offset %d size %d\n", offset, size); return -EINVAL; } if (!poc_dev->partition[index].preload_done) { panel_err("partition(%d) is not loaded\n", index); return -EINVAL; } if (!poc_dev->partition[index].chksum_ok) { panel_err("partition(%d) checksum error(calc:%04X read:%04X)\n", index, poc_dev->partition[index].chksum_by_calc, poc_dev->partition[index].chksum_by_read); } memcpy(dst, poc_rd_img + poc_dev->partition[index].addr + offset, size); return size; } int set_panel_poc(struct panel_poc_device *poc_dev, u32 cmd, void *arg) { struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_device *panel = to_panel_device(poc_dev); int ret = 0; struct timespec64 cur_ts, last_ts, delta_ts; s64 elapsed_msec; int addr = -1, len = -1, index; int partition_size, partition_addr; if (cmd >= MAX_POC_OP) { panel_err("invalid poc_op %d\n", cmd); return -EINVAL; } panel_info("%s +\n", poc_op[cmd]); ktime_get_ts64(&last_ts); switch (cmd) { case POC_OP_ERASE: break; case POC_OP_WRITE: ret = poc_write_data(panel, &poc_info->wbuf[poc_info->wpos], poc_info->wpos, poc_info->wsize); if (unlikely(ret < 0)) { panel_err("failed to write poc-write-seq\n"); return ret; } break; case POC_OP_READ: ret = poc_read_data(panel, &poc_info->rbuf[poc_info->rpos], poc_info->rpos, poc_info->rsize); if (unlikely(ret < 0)) { panel_err("failed to write poc-read-seq\n"); return ret; } break; case POC_OP_CHECKSUM: ret = poc_get_poc_chksum(panel); if (unlikely(ret < 0)) { panel_err("failed to get poc checksum\n"); return ret; } ret = poc_get_poc_ctrl(panel); if (unlikely(ret < 0)) { panel_err("failed to get poc ctrl\n"); return ret; } break; case POC_OP_CHECKPOC: ret = poc_get_octa_poc(panel); if (unlikely(ret < 0)) { panel_err("failed to get_octa_poc\n"); return ret; } break; case POC_OP_SECTOR_ERASE: ret = sscanf((char *)arg, "%*d %d %d", &addr, &len); if (unlikely(ret < 2)) { panel_err("failed to get poc erase params\n"); return -EINVAL; } if (unlikely(addr < 0) || unlikely((addr % SZ_4K) > 0) || unlikely(len < 0)) { panel_err("invalid poc erase params\n"); return -EINVAL; } partition_addr = get_poc_partition_addr(poc_dev, POC_IMG_PARTITION); if (partition_addr < 0 || (partition_addr % SZ_4K) > 0) { panel_err("invalid partition addr %d\n", partition_addr); return -EINVAL; } partition_size = get_poc_partition_size(poc_dev, POC_IMG_PARTITION); if (partition_size <= 0 || (partition_size < addr + len)) { panel_err("invalid partition size %d %d %d\n", partition_size, addr, len); return -EINVAL; } addr = partition_addr + addr; ret = poc_memory_initialize(panel); if (unlikely(ret < 0)) { panel_err("failed to initialize memory\n"); return ret; } #ifdef CONFIG_DISPLAY_USE_INFO poc_info->erase_trycount++; #endif ret = poc_erase(panel, addr, len); if (unlikely(ret < 0)) { panel_err("failed to write poc-erase-seq\n"); #ifdef CONFIG_DISPLAY_USE_INFO poc_info->erase_failcount++; #endif poc_info->erased = false; } else { poc_info->erased = true; } ret = poc_memory_uninitialize(panel); if (unlikely(ret < 0)) { panel_err("failed to uninitialize memory\n"); return ret; } break; case POC_OP_IMG_READ: ret = read_poc_partition(poc_dev, POC_IMG_PARTITION); if (unlikely(ret < 0)) { panel_err("failed to read img partition\n"); return ret; } ret = poc_data_backup(panel, poc_rd_img + poc_dev->partition[POC_IMG_PARTITION].addr, poc_dev->partition[POC_IMG_PARTITION].size, POC_IMG_PATH); if (unlikely(ret < 0)) { panel_err("failed to backup poc img\n"); return ret; } break; case POC_OP_DIM_READ: if (arg == NULL) return -EINVAL; index = POC_DIM_PARTITION + *(int *)arg; if (index < POC_DIM_PARTITION || index > POC_DIM_PARTITION_END) { panel_err("invalid index of dim partition:%d\n", index); return -EINVAL; } ret = read_poc_partition(poc_dev, index); if (unlikely(ret < 0)) { panel_err("failed to read partition:%d\n", index); return ret; } break; case POC_OP_DIM_CHKSUM: break; case POC_OP_DIM_VALID: break; case POC_OP_MTP_READ: if (arg == NULL) return -EINVAL; index = POC_MTP_PARTITION + *(int *)arg; if (index < POC_MTP_PARTITION || index > POC_MTP_PARTITION_END) { panel_err("invalid index of mtp partition:%d\n", index); return -EINVAL; } ret = read_poc_partition(poc_dev, index); if (unlikely(ret < 0)) { panel_err("failed to read partition:%d\n", index); return ret; } break; case POC_OP_MCD_READ: ret = read_poc_partition(poc_dev, POC_MCD_PARTITION); if (unlikely(ret < 0)) { panel_err("failed to read MCD partition\n"); return ret; } break; case POC_OP_DIM_READ_TEST: if (arg == NULL) return -EINVAL; index = POC_DIM_PARTITION + *(int *)arg; if (index < POC_DIM_PARTITION || index > POC_DIM_PARTITION_END) { panel_err("invalid index of mtp partition:%d\n", index); return -EINVAL; } ret = read_poc_partition(poc_dev, index); if (unlikely(ret < 0)) { panel_err("failed to read partition:%d\n", index); return ret; } ret = poc_data_backup(panel, poc_rd_img + poc_dev->partition[index].addr, poc_dev->partition[index].size, POC_DATA_PATH); if (unlikely(ret < 0)) { panel_err("failed to backup gamma flash\n"); return ret; } break; #ifdef CONFIG_SUPPORT_POC_SPI case POC_OP_SET_CONN_SRC: ret = sscanf((char *)arg, "%*d %d", &addr); if (unlikely(ret < 1)) { panel_err("failed to get poc set conn params\n"); return -EINVAL; } if (unlikely(addr < 0) || addr >= MAX_POC_CONN_SRC) { panel_err("invalid poc set conn params\n"); return -EINVAL; } poc_info->conn_src = addr; break; #if 0 case POC_OP_READ_SPI_STATUS_REG: ret = _spi_poc_get_status(panel); if (unlikely(ret < 0)) { panel_err("failed to get status reg\n"); return ret; } panel_info("spi_status_reg 0x%04X\n", ret); break; #endif #endif case POC_OP_INITIALIZE: ret = poc_memory_initialize(panel); if (unlikely(ret < 0)) { panel_err("failed to initialize memory\n"); return ret; } break; case POC_OP_UNINITIALIZE: ret = poc_memory_uninitialize(panel); if (unlikely(ret < 0)) { panel_err("failed to uninitialize memory\n"); return ret; } break; case POC_OP_GM2_READ: if (arg == NULL) return -EINVAL; index = *(int *)arg; if (index < POC_GM2_PARTITION || index > POC_GM2_PARTITION_END) { panel_err("invalid index of dim partition:%d\n", index); return -EINVAL; } ret = read_poc_partition(poc_dev, index); if (unlikely(ret < 0)) { panel_err("failed to read partition:%d\n", index); return ret; } break; case POC_OP_NONE: panel_info("none operation\n"); break; default: panel_err("invalid poc op\n"); break; } ktime_get_ts64(&cur_ts); delta_ts = timespec64_sub(cur_ts, last_ts); elapsed_msec = timespec64_to_ns(&delta_ts) / 1000000; panel_info("%s (elapsed %lld.%03lld sec) -\n", poc_op[cmd], elapsed_msec / 1000, elapsed_msec % 1000); return 0; }; EXPORT_SYMBOL(set_panel_poc); #ifdef CONFIG_SUPPORT_POC_FLASH static long panel_poc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct panel_poc_device *poc_dev = file->private_data; struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_device *panel = to_panel_device(poc_dev); int ret; if (unlikely(!poc_dev->opened)) { panel_err("poc device not opened\n"); return -EIO; } panel_wake_lock(panel); if (!IS_PANEL_ACTIVE(panel)) { panel_wake_unlock(panel); return -EAGAIN; } mutex_lock(&panel->io_lock); switch (cmd) { case IOC_GET_POC_STATUS: if (copy_to_user((u32 __user *)arg, &poc_info->state, sizeof(poc_info->state))) { ret = -EFAULT; break; } break; case IOC_GET_POC_CHKSUM: ret = set_panel_poc(poc_dev, POC_OP_CHECKSUM, NULL); if (ret) { panel_err("error set_panel_poc\n"); ret = -EFAULT; break; } if (copy_to_user((u8 __user *)arg, &poc_info->poc_chksum[4], sizeof(poc_info->poc_chksum[4]))) { ret = -EFAULT; break; } break; case IOC_GET_POC_CSDATA: ret = set_panel_poc(poc_dev, POC_OP_CHECKSUM, NULL); if (ret) { panel_err("error set_panel_poc\n"); ret = -EFAULT; break; } if (copy_to_user((u8 __user *)arg, poc_info->poc_chksum, sizeof(poc_info->poc_chksum))) { ret = -EFAULT; break; } break; case IOC_GET_POC_ERASED: if (copy_to_user((u8 __user *)arg, &poc_info->erased, sizeof(poc_info->erased))) { ret = -EFAULT; break; } break; case IOC_GET_POC_FLASHED: ret = set_panel_poc(poc_dev, POC_OP_CHECKPOC, NULL); if (ret) { panel_err("error set_panel_poc\n"); ret = -EFAULT; break; } if (copy_to_user((u8 __user *)arg, &poc_info->poc, sizeof(poc_info->poc))) { ret = -EFAULT; break; } break; case IOC_SET_POC_ERASE: ret = set_panel_poc(poc_dev, POC_OP_ERASE, NULL); if (ret) { panel_err("error set_panel_poc\n"); ret = -EFAULT; break; } break; default: break; }; mutex_unlock(&panel->io_lock); panel_wake_unlock(panel); return 0; } static int panel_poc_open(struct inode *inode, struct file *file) { struct miscdevice *dev = file->private_data; struct panel_poc_device *poc_dev = container_of(dev, struct panel_poc_device, dev); struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_device *panel = to_panel_device(poc_dev); int ret; panel_info("was called\n"); if (poc_dev->opened) { panel_err("already opend\n"); return -EBUSY; } panel_wake_lock(panel); if (!IS_PANEL_ACTIVE(panel)) { panel_wake_unlock(panel); return -EAGAIN; } mutex_lock(&panel->io_lock); ret = set_panel_poc(poc_dev, POC_OP_INITIALIZE, NULL); if (ret < 0) goto err_open; mutex_lock(&panel->op_lock); poc_info->state = 0; mutex_unlock(&panel->op_lock); memset(poc_info->poc_chksum, 0, sizeof(poc_info->poc_chksum)); memset(poc_info->poc_ctrl, 0, sizeof(poc_info->poc_ctrl)); poc_info->wbuf = poc_wr_img; poc_info->wpos = 0; poc_info->wsize = 0; poc_info->rbuf = poc_rd_img; poc_info->rpos = 0; poc_info->rsize = 0; file->private_data = poc_dev; poc_dev->opened = 1; atomic_set(&poc_dev->cancel, 0); mutex_unlock(&panel->io_lock); panel_wake_unlock(panel); return 0; err_open: panel_err("failed to initialize %d\n", ret); mutex_unlock(&panel->io_lock); panel_wake_unlock(panel); return ret; } static int panel_poc_release(struct inode *inode, struct file *file) { int ret = 0; struct panel_poc_device *poc_dev = file->private_data; struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_device *panel = to_panel_device(poc_dev); panel_info("was called\n"); panel_wake_lock(panel); mutex_lock(&panel->io_lock); mutex_lock(&panel->op_lock); poc_info->state = 0; mutex_unlock(&panel->op_lock); memset(poc_info->poc_chksum, 0, sizeof(poc_info->poc_chksum)); memset(poc_info->poc_ctrl, 0, sizeof(poc_info->poc_ctrl)); poc_info->wbuf = NULL; poc_info->wpos = 0; poc_info->wsize = 0; poc_info->rbuf = NULL; poc_info->rpos = 0; poc_info->rsize = 0; poc_dev->opened = 0; atomic_set(&poc_dev->cancel, 0); ret = set_panel_poc(poc_dev, POC_OP_UNINITIALIZE, NULL); if (ret < 0) panel_err("failed to uninitialize %d\n", ret); mutex_unlock(&panel->io_lock); panel_wake_unlock(panel); return ret; } static ssize_t panel_poc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct panel_poc_device *poc_dev = file->private_data; struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_device *panel = to_panel_device(poc_dev); ssize_t res; int partition_addr, partition_size; panel_info("size:%d ppos:%d\n", (int)count, (int)*ppos); poc_info->read_trycount++; if (unlikely(!poc_dev->opened)) { panel_err("poc device not opened\n"); poc_info->read_failcount++; return -EIO; } if (unlikely(!buf)) { panel_err("invalid read buffer\n"); poc_info->read_failcount++; return -EINVAL; } partition_addr = get_poc_partition_addr(poc_dev, POC_IMG_PARTITION); if (partition_addr < 0 || (partition_addr % SZ_4K) > 0) { panel_err("invalid partition addr %d\n", partition_addr); return -EINVAL; } partition_size = get_poc_partition_size(poc_dev, POC_IMG_PARTITION); if (partition_size < 0) { poc_info->read_failcount++; return -EINVAL; } if (unlikely(*ppos < 0 || *ppos >= partition_size)) { panel_err("invalid read pos %d\n", (int)*ppos); poc_info->read_failcount++; return -EINVAL; } panel_wake_lock(panel); if (!IS_PANEL_ACTIVE(panel)) { poc_info->read_failcount++; panel_wake_unlock(panel); return -EAGAIN; } mutex_lock(&panel->io_lock); poc_info->rbuf = poc_rd_img; poc_info->rpos = partition_addr + *ppos; if (count > partition_size - *ppos) { panel_warn("adjust count %d -> %d\n", (int)count, (int)(partition_size - *ppos)); count = partition_size - *ppos; } poc_info->rsize = (u32)count; res = set_panel_poc(poc_dev, POC_OP_READ, NULL); if (res < 0) goto err_read; res = simple_read_from_buffer(buf, poc_info->rsize, ppos, poc_info->rbuf + partition_addr, partition_size); if (res < 0) goto err_read; panel_info("read %ld bytes (count %ld)\n", res, count); mutex_unlock(&panel->io_lock); panel_wake_unlock(panel); return res; err_read: mutex_unlock(&panel->io_lock); poc_info->read_failcount++; panel_wake_unlock(panel); return res; } static ssize_t panel_poc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct panel_poc_device *poc_dev = file->private_data; struct panel_poc_info *poc_info = &poc_dev->poc_info; struct panel_device *panel = to_panel_device(poc_dev); ssize_t res; int partition_addr, partition_size; panel_info("size:%d, ppos:%d\n", (int)count, (int)*ppos); poc_info->write_trycount++; if (unlikely(!poc_dev->opened)) { panel_err("poc device not opened\n"); poc_info->write_failcount++; return -EIO; } if (unlikely(!buf)) { panel_err("invalid write buffer\n"); poc_info->write_failcount++; return -EINVAL; } partition_addr = get_poc_partition_addr(poc_dev, POC_IMG_PARTITION); if (partition_addr < 0 || (partition_addr % SZ_4K) > 0) { panel_err("invalid partition addr %d\n", partition_addr); return -EINVAL; } partition_size = get_poc_partition_size(poc_dev, POC_IMG_PARTITION); if (partition_size < 0) { poc_info->write_failcount++; return -EINVAL; } if (unlikely(*ppos < 0 || *ppos >= partition_size)) { panel_err("invalid write size pos %d, size %d\n", (int)*ppos, (int)count); poc_info->write_failcount++; return -EINVAL; } panel_wake_lock(panel); if (!IS_PANEL_ACTIVE(panel)) { poc_info->write_failcount++; panel_wake_unlock(panel); return -EAGAIN; } mutex_lock(&panel->io_lock); poc_info->wbuf = poc_wr_img; poc_info->wpos = partition_addr + *ppos; if (count > partition_size - *ppos) { panel_warn("adjust count %d -> %d\n", (int)count, (int)(partition_size - *ppos)); count = partition_size - *ppos; } poc_info->wsize = (u32)count; res = simple_write_to_buffer(poc_info->wbuf + partition_addr, partition_size, ppos, buf, poc_info->wsize); if (res < 0) goto err_write; panel_info("write %ld bytes (count %ld)\n", res, count); res = set_panel_poc(poc_dev, POC_OP_WRITE, NULL); if (res < 0) goto err_write; mutex_unlock(&panel->io_lock); panel_wake_unlock(panel); return count; err_write: poc_info->write_failcount++; mutex_unlock(&panel->io_lock); panel_wake_unlock(panel); return res; } loff_t panel_poc_llseek(struct file *file, loff_t offset, int whence) { struct inode *inode = file->f_mapping->host; panel_info("s_maxbytes %d, i_size_read %d\n", (int)inode->i_sb->s_maxbytes, (int)i_size_read(inode)); return generic_file_llseek_size(file, offset, whence, inode->i_sb->s_maxbytes, i_size_read(inode)); } static const struct file_operations panel_poc_fops = { .owner = THIS_MODULE, .read = panel_poc_read, .write = panel_poc_write, .unlocked_ioctl = panel_poc_ioctl, .open = panel_poc_open, .release = panel_poc_release, .llseek = generic_file_llseek, }; #endif /* CONFIG_SUPPORT_POC_FLASH */ #ifdef CONFIG_DISPLAY_USE_INFO #ifdef CONFIG_SUPPORT_POC_FLASH #define EPOCEFS_IMGIDX (100) enum { EPOCEFS_NOENT = 1, /* No such file or directory */ EPOCEFS_EMPTY = 2, /* Empty file */ EPOCEFS_READ = 3, /* Read failed */ MAX_EPOCEFS, }; static int poc_get_efs_count(char *filename, int *value) { mm_segment_t old_fs; struct file *filp = NULL; int fsize = 0, nread, rc, ret = 0; int count; u8 buf[128]; if (!filename || !value) { panel_err("invalid parameter\n"); return -EINVAL; } old_fs = get_fs(); set_fs(KERNEL_DS); filp = filp_open(filename, O_RDONLY, 0440); if (IS_ERR(filp)) { ret = PTR_ERR(filp); if (ret == -ENOENT) panel_err("file(%s) not exist\n", filename); else panel_info("file(%s) open error(ret %d)\n", filename, ret); set_fs(old_fs); return -EPOCEFS_NOENT; } if (filp->f_path.dentry && filp->f_path.dentry->d_inode) fsize = filp->f_path.dentry->d_inode->i_size; if (fsize == 0 || fsize > ARRAY_SIZE(buf)) { panel_err("invalid file(%s) size %d\n", filename, fsize); ret = -EPOCEFS_EMPTY; goto exit; } memset(buf, 0, sizeof(buf)); nread = vfs_read(filp, (char __user *)buf, fsize, &filp->f_pos); if (nread != fsize) { panel_err("failed to read (ret %d)\n", nread); ret = -EPOCEFS_READ; goto exit; } rc = sscanf(buf, "%d", &count); if (rc != 1) { panel_err("failed to sscanf %d\n", rc); ret = -EINVAL; goto exit; } panel_info("%s(size %d) : %d\n", filename, fsize, count); *value = count; exit: filp_close(filp, current->files); set_fs(old_fs); return ret; } static int poc_get_efs_image_index_org(char *filename, int *value) { mm_segment_t old_fs; struct file *filp = NULL; int fsize = 0, nread, rc, ret = 0; char binary; int image_index, chksum; u8 buf[128]; if (!filename || !value) { panel_err("invalid parameter\n"); return -EINVAL; } old_fs = get_fs(); set_fs(KERNEL_DS); filp = filp_open(filename, O_RDONLY, 0440); if (IS_ERR(filp)) { ret = PTR_ERR(filp); if (ret == -ENOENT) panel_err("file(%s) not exist\n", filename); else panel_info("file(%s) open error(ret %d)\n", filename, ret); set_fs(old_fs); return -EPOCEFS_NOENT; } if (filp->f_path.dentry && filp->f_path.dentry->d_inode) fsize = filp->f_path.dentry->d_inode->i_size; if (fsize == 0 || fsize > ARRAY_SIZE(buf)) { panel_err("invalid file(%s) size %d\n", filename, fsize); ret = -EPOCEFS_EMPTY; goto exit; } memset(buf, 0, sizeof(buf)); nread = vfs_read(filp, (char __user *)buf, fsize, &filp->f_pos); if (nread != fsize) { panel_err("failed to read (ret %d)\n", nread); ret = -EPOCEFS_READ; goto exit; } rc = sscanf(buf, "%c %d %d", &binary, &image_index, &chksum); if (rc != 3) { panel_err("failed to sscanf %d\n", rc); ret = -EINVAL; goto exit; } panel_info("%s(size %d) : %c %d %d\n", filename, fsize, binary, image_index, chksum); *value = image_index; exit: filp_close(filp, current->files); set_fs(old_fs); return ret; } static int poc_get_efs_image_index(char *filename, int *value) { mm_segment_t old_fs; struct file *filp = NULL; int fsize = 0, nread, rc, ret = 0; int image_index, seek; u8 buf[128]; if (!filename || !value) { panel_err("invalid parameter\n"); return -EINVAL; } old_fs = get_fs(); set_fs(KERNEL_DS); filp = filp_open(filename, O_RDONLY, 0440); if (IS_ERR(filp)) { ret = PTR_ERR(filp); if (ret == -ENOENT) panel_err("file(%s) not exist\n", filename); else panel_info("file(%s) open error(ret %d)\n", filename, ret); set_fs(old_fs); return -EPOCEFS_NOENT; } if (filp->f_path.dentry && filp->f_path.dentry->d_inode) fsize = filp->f_path.dentry->d_inode->i_size; if (fsize == 0 || fsize > ARRAY_SIZE(buf)) { panel_err("invalid file(%s) size %d\n", filename, fsize); ret = -EPOCEFS_EMPTY; goto exit; } memset(buf, 0, sizeof(buf)); nread = vfs_read(filp, (char __user *)buf, fsize, &filp->f_pos); if (nread != fsize) { panel_err("failed to read (ret %d)\n", nread); ret = -EPOCEFS_READ; goto exit; } rc = sscanf(buf, "%d,%d", &image_index, &seek); if (rc != 2) { panel_err("failed to sscanf %d\n", rc); ret = -EINVAL; goto exit; } panel_info("%s(size %d) : %d %d\n", filename, fsize, image_index, seek); *value = image_index; exit: filp_close(filp, current->files); set_fs(old_fs); return ret; } static int poc_dpui_callback(struct panel_poc_device *poc_dev) { struct panel_poc_info *poc_info; char tbuf[MAX_DPUI_VAL_LEN]; int size, ret, poci, poci_org; poc_info = &poc_dev->poc_info; ret = poc_get_efs_count(POC_TOTAL_TRY_COUNT_FILE_PATH, &poc_info->total_trycount); if (ret < 0) poc_info->total_trycount = (ret > -MAX_EPOCEFS) ? ret : -1; size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", poc_info->total_trycount); set_dpui_field(DPUI_KEY_PNPOCT, tbuf, size); ret = poc_get_efs_count(POC_TOTAL_FAIL_COUNT_FILE_PATH, &poc_info->total_failcount); if (ret < 0) poc_info->total_failcount = (ret > -MAX_EPOCEFS) ? ret : -1; size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", poc_info->total_failcount); set_dpui_field(DPUI_KEY_PNPOCF, tbuf, size); ret = poc_get_efs_image_index_org(POC_INFO_FILE_PATH, &poci_org); if (ret < 0) poci_org = -EPOCEFS_IMGIDX + ret; size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", poci_org); set_dpui_field(DPUI_KEY_PNPOCI_ORG, tbuf, size); ret = poc_get_efs_image_index(POC_USER_FILE_PATH, &poci); if (ret < 0) poci = -EPOCEFS_IMGIDX + ret; size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", poci); set_dpui_field(DPUI_KEY_PNPOCI, tbuf, size); inc_dpui_u32_field(DPUI_KEY_PNPOC_ER_TRY, poc_info->erase_trycount); poc_info->erase_trycount = 0; inc_dpui_u32_field(DPUI_KEY_PNPOC_ER_FAIL, poc_info->erase_failcount); poc_info->erase_failcount = 0; inc_dpui_u32_field(DPUI_KEY_PNPOC_WR_TRY, poc_info->write_trycount); poc_info->write_trycount = 0; inc_dpui_u32_field(DPUI_KEY_PNPOC_WR_FAIL, poc_info->write_failcount); poc_info->write_failcount = 0; inc_dpui_u32_field(DPUI_KEY_PNPOC_RD_TRY, poc_info->read_trycount); poc_info->read_trycount = 0; inc_dpui_u32_field(DPUI_KEY_PNPOC_RD_FAIL, poc_info->read_failcount); poc_info->read_failcount = 0; return 0; } #else static int poc_dpui_callback(struct panel_poc_device *poc_dev) { return 0; } #endif /* CONFIG_SUPPORT_POC_FLASH */ static int poc_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct panel_poc_device *poc_dev; struct dpui_info *dpui = data; if (dpui == NULL) { panel_err("dpui is null\n"); return 0; } poc_dev = container_of(self, struct panel_poc_device, poc_notif); poc_dpui_callback(poc_dev); return 0; } #endif /* CONFIG_DISPLAY_USE_INFO */ int panel_poc_probe(struct panel_device *panel, struct panel_poc_data *poc_data) { struct panel_poc_device *poc_dev = &panel->poc_dev; struct panel_poc_info *poc_info = &poc_dev->poc_info; int ret = 0, i, exists; static bool initialized; if (!poc_data) { panel_warn("poc_data is null\n"); return -EINVAL; } if (!initialized) { #ifdef CONFIG_SUPPORT_POC_FLASH poc_dev->dev.minor = MISC_DYNAMIC_MINOR; poc_dev->dev.name = "poc"; poc_dev->dev.fops = &panel_poc_fops; poc_dev->dev.parent = NULL; ret = misc_register(&poc_dev->dev); if (ret) { panel_err("failed to register panel misc driver (ret %d)\n", ret); goto exit_probe; } #endif } poc_info->version = poc_data->version; #ifdef CONFIG_SUPPORT_POC_SPI poc_info->conn_src = poc_data->conn_src; #endif poc_info->wdata_len = poc_data->wdata_len; poc_dev->seqtbl = poc_data->seqtbl; poc_dev->nr_seqtbl = poc_data->nr_seqtbl; poc_dev->maptbl = poc_data->maptbl; poc_dev->nr_maptbl = poc_data->nr_maptbl; poc_dev->partition = poc_data->partition; poc_dev->nr_partition = poc_data->nr_partition; for (i = 0; i < poc_dev->nr_maptbl; i++) poc_dev->maptbl[i].pdata = poc_dev; poc_info->erased = false; poc_info->poc = 1; /* default enabled */ poc_dev->opened = 0; for (i = 0; i < poc_dev->nr_maptbl; i++) maptbl_init(&poc_dev->maptbl[i]); for (i = 0; i < poc_dev->nr_partition; i++) { poc_dev->partition[i].preload_done = false; poc_dev->partition[i].chksum_ok = false; panel_info("%s addr:0x%x size:%d\n", poc_dev->partition[i].name, poc_dev->partition[i].addr, poc_dev->partition[i].size); if (poc_info->total_size < poc_dev->partition[i].addr + poc_dev->partition[i].size) poc_info->total_size = poc_dev->partition[i].addr + poc_dev->partition[i].size; } if (poc_wr_img) devm_kfree(panel->dev, poc_wr_img); poc_wr_img = (u8 *)devm_kzalloc(panel->dev, poc_info->total_size * sizeof(u8), GFP_KERNEL); if (poc_rd_img) devm_kfree(panel->dev, poc_rd_img); poc_rd_img = (u8 *)devm_kzalloc(panel->dev, poc_info->total_size * sizeof(u8), GFP_KERNEL); #ifdef CONFIG_DISPLAY_USE_INFO poc_info->total_trycount = -1; poc_info->total_failcount = -1; if (!initialized) { poc_dev->poc_notif.notifier_call = poc_notifier_callback; ret = dpui_logging_register(&poc_dev->poc_notif, DPUI_TYPE_PANEL); if (ret) { panel_err("failed to register dpui notifier callback\n"); goto exit_probe; } } #endif for (i = 0; i < poc_dev->nr_partition; i++) { if (poc_dev->partition[i].need_preload) { exists = check_poc_partition_exists(poc_dev, i); if (exists < 0) { panel_err("failed to check partition(%d)\n", i); ret = exists; goto exit_probe; } if (!exists) { panel_warn("partition(%d) not exist\n", i); continue; } ret = read_poc_partition(poc_dev, i); if (unlikely(ret < 0)) { panel_err("failed to read partition(%d)\n", i); goto exit_probe; } } } initialized = true; panel_info("total_size:%d registered successfully\n", poc_info->total_size); exit_probe: return ret; } int panel_poc_remove(struct panel_device *panel) { return 0; } void copy_poc_wr_addr_maptbl(struct maptbl *tbl, u8 *dst) { struct panel_poc_device *poc_dev; struct panel_poc_info *poc_info; if (!tbl || !dst) return; poc_dev = (struct panel_poc_device *)tbl->pdata; if (unlikely(!poc_dev)) return; poc_info = &poc_dev->poc_info; dst[0] = (poc_info->waddr & 0xFF0000) >> 16; dst[1] = (poc_info->waddr & 0x00FF00) >> 8; dst[2] = (poc_info->waddr & 0x0000FF); } void copy_poc_wr_data_maptbl(struct maptbl *tbl, u8 *dst) { struct panel_poc_device *poc_dev; struct panel_poc_info *poc_info; if (!tbl || !dst) return; poc_dev = (struct panel_poc_device *)tbl->pdata; if (unlikely(!poc_dev)) return; poc_info = &poc_dev->poc_info; memcpy(dst, poc_info->wdata, poc_info->wdata_len); } void copy_poc_rd_addr_maptbl(struct maptbl *tbl, u8 *dst) { struct panel_poc_device *poc_dev; struct panel_poc_info *poc_info; if (!tbl || !dst) return; poc_dev = (struct panel_poc_device *)tbl->pdata; if (unlikely(!poc_dev)) return; poc_info = &poc_dev->poc_info; dst[0] = (poc_info->raddr & 0xFF0000) >> 16; dst[1] = (poc_info->raddr & 0x00FF00) >> 8; dst[2] = (poc_info->raddr & 0x0000FF); } void copy_poc_er_addr_maptbl(struct maptbl *tbl, u8 *dst) { struct panel_poc_device *poc_dev; struct panel_poc_info *poc_info; if (!tbl || !dst) return; poc_dev = (struct panel_poc_device *)tbl->pdata; if (unlikely(!poc_dev)) return; poc_info = &poc_dev->poc_info; dst[0] = (poc_info->waddr & 0xFF0000) >> 16; dst[1] = (poc_info->waddr & 0x00FF00) >> 8; dst[2] = (poc_info->waddr & 0x0000FF); }