/* * Allwinner SoCs eink driver. * * Copyright (C) 2019 Allwinner. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include "asm/cacheflush.h" #include #include #include "include/eink_sys_source.h" #include "include/eink_driver.h" #define BYTE_ALIGN(x) (((x + (4 * 1024 - 1)) >> 12) << 12) extern struct eink_driver_info g_eink_drvdata; s32 eink_set_print_level(u32 level) { EINK_INFO_MSG("print level = 0x%x\n", level); g_eink_drvdata.eink_dbg_info = level; return 0; } /* 0 -- no log * 8 -- print timer info * 7 -- capture DE WB BUF * 6 -- capture wavfile * 5 -- decode debug * 4 -- dump register * 3 -- print pipe and buf list * 2 -- reserve * 1 -- base debug info */ s32 eink_get_print_level(void) { return g_eink_drvdata.eink_dbg_info; } s32 get_delt_ms_timer(struct timeval start_timer, struct timeval end_timer) { return ((end_timer.tv_sec - start_timer.tv_sec) * 1000 + (end_timer.tv_usec - start_timer.tv_usec)/1000); } s32 get_delt_us_timer(struct timeval start_timer, struct timeval end_timer) { return ((end_timer.tv_sec - start_timer.tv_sec) * 1000000 + (end_timer.tv_usec - start_timer.tv_usec)); } bool is_upd_win_zero(struct upd_win update_area) { if ((update_area.left == 0) && (update_area.right == 0) && (update_area.top == 0) && (update_area.bottom == 0)) return true; else return false; } int atoi_float(char *buf) { int num = 0; int i = 0; int point_pos = -1; int pow = 0; while (buf[i] != '\0') { if (buf[i] >= '0' && buf[i] <= '9') { if ((point_pos < 0) || ((point_pos >= 0) && ((point_pos + 4) > i))) { num = num * 10 + (buf[i] - '0'); } else { //drop the left char break; } } if (buf[i] == '.') { point_pos = i; } i++; } //auto *1000 if ((point_pos >= 0) && ((point_pos + 4) > i)) { pow = (point_pos + 4 - i); while (pow > 0) { num = num * 10; pow--; } } if (buf[0] == '-') num = num * (-1); EINK_INFO_MSG("input str=%s, num=%d\n", buf, num); return num; } int eink_sys_pin_set_state(char *dev_name, char *name) { char compat[32]; u32 len = 0; struct device_node *node = NULL; struct platform_device *pdev = NULL; struct pinctrl *pctl = NULL; struct pinctrl_state *state = NULL; int ret = -1; len = sprintf(compat, "allwinner,sunxi-%s", dev_name); if (len > 32) pr_warn("size of mian_name is out of range\n"); node = of_find_compatible_node(NULL, NULL, compat); if (!node) { pr_err("of_find_compatible_node %s fail\n", compat); goto exit; } pdev = of_find_device_by_node(node); if (!node) { pr_err("of_find_device_by_node for %s fail\n", compat); goto exit; } pctl = pinctrl_get(&pdev->dev); if (IS_ERR(pctl)) { /*not every eink need pin config*/ pr_err("%s:pinctrl_get for %s fail\n", __func__, compat); ret = PTR_ERR(pctl); goto exit; } state = pinctrl_lookup_state(pctl, name); if (IS_ERR(state)) { pr_err("%s:pinctrl_lookup_state for %s fail\n", __func__, compat); ret = PTR_ERR(state); goto exit; } ret = pinctrl_select_state(pctl, state); if (ret < 0) { pr_err("%s:pinctrl_select_state(%s) for %s fail\n", __func__, name, compat); goto exit; } ret = 0; exit: return ret; } EXPORT_SYMBOL(eink_sys_pin_set_state); int eink_sys_power_enable(char *name) { int ret = 0; #if defined(CONFIG_AW_AXP) || defined(CONFIG_REGULATOR) struct regulator *regu = NULL; #ifdef CONFIG_SUNXI_REGULATOR_DT regu = regulator_get(g_eink_drvdata.device, name); #else regu = regulator_get(NULL, name); #endif if (IS_ERR(regu)) { pr_err("some error happen, fail to get regulator %s\n", name); goto exit; } /* enalbe regulator */ ret = regulator_enable(regu); if (ret != 0) { pr_err("some err happen, fail to enable regulator %s!\n", name); goto exit1; } else { pr_info("suceess to enable regulator %s!\n", name); } exit1: /* put regulater, when module exit */ regulator_put(regu); exit: #endif return ret; } EXPORT_SYMBOL(eink_sys_power_enable); int eink_sys_power_disable(char *name) { int ret = 0; #if defined(CONFIG_AW_AXP) || defined(CONFIG_REGULATOR) struct regulator *regu = NULL; #ifdef CONFIG_SUNXI_REGULATOR_DT regu = regulator_get(g_eink_drvdata.device, name); #else regu = regulator_get(NULL, name); #endif if (IS_ERR(regu)) { pr_err("some error happen, fail to get regulator %s\n", name); goto exit; } /* disalbe regulator */ ret = regulator_disable(regu); if (ret != 0) { pr_err("some err happen, fail to disable regulator %s!\n", name); goto exit1; } else { pr_info("suceess to disable regulator %s!\n", name); } exit1: /* put regulater, when module exit */ regulator_put(regu); exit: #endif return ret; } EXPORT_SYMBOL(eink_sys_power_disable); s32 eink_panel_pin_cfg(u32 en) { struct eink_manager *eink_mgr = get_eink_manager(); char dev_name[25]; if (eink_mgr == NULL) { DE_WRN("NULL hdl!\n"); return -1; } EINK_INFO_MSG("eink pin config, state %s, %d\n", (en) ? "on" : "off", en); /* io-pad */ if (en == 1) { if (!((!strcmp(eink_mgr->eink_pin_power, "")) || (!strcmp(eink_mgr->eink_pin_power, "none")))) eink_sys_power_enable(eink_mgr->eink_pin_power); } sprintf(dev_name, "eink"); eink_sys_pin_set_state(dev_name, (en == 1) ? EINK_PIN_STATE_ACTIVE : EINK_PIN_STATE_SLEEP); if (en == 0) { if (!((!strcmp(eink_mgr->eink_pin_power, "")) || (!strcmp(eink_mgr->eink_pin_power, "none")))) eink_sys_power_disable(eink_mgr->eink_pin_power); } return 0; } int eink_sys_gpio_request(struct eink_gpio_cfg *gpio_list, u32 group_count_max) { int ret = 0; struct gpio_config pin_cfg; char pin_name[32]; u32 config; char pintype[50] = SUNXI_PINCTRL; if (gpio_list == NULL) { pr_err("%s: gpio list is null\n", __func__); return 0; } pin_cfg.gpio = gpio_list->gpio; pin_cfg.mul_sel = gpio_list->mul_sel; pin_cfg.pull = gpio_list->pull; pin_cfg.drv_level = gpio_list->drv_level; pin_cfg.data = gpio_list->data; ret = gpio_request(pin_cfg.gpio, NULL); if (ret != 0) { pr_err("%s failed, gpio_name=%s, gpio=%d, ret=%d\n", __func__, gpio_list->gpio_name, gpio_list->gpio, ret); return 0; } EINK_DEBUG_MSG("gpio_name=%s, gpio=%3d, <%d,%d,%d,%d> ret=%d\n", gpio_list->gpio_name, gpio_list->gpio, gpio_list->mul_sel, gpio_list->pull, gpio_list->drv_level, gpio_list->data, ret); ret = pin_cfg.gpio; if (!IS_AXP_PIN(pin_cfg.gpio)) { /* * valid pin of sunxi-pinctrl, * config pin attributes individually. */ sunxi_gpio_to_name(pin_cfg.gpio, pin_name); config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, pin_cfg.mul_sel); ret = pin_config_set(pintype, pin_name, config); if (ret == -EINVAL) { /*try r_pio*/ snprintf(pintype, 50, SUNXI_R_PINCTRL); ret = pin_config_set(pintype, pin_name, config); } if (pin_cfg.pull != GPIO_PULL_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, pin_cfg.pull); pin_config_set(pintype, pin_name, config); } if (pin_cfg.drv_level != GPIO_DRVLVL_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DRV, pin_cfg.drv_level); pin_config_set(pintype, pin_name, config); } if (pin_cfg.data != GPIO_DATA_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, pin_cfg.data); pin_config_set(pintype, pin_name, config); } } else if (IS_AXP_PIN(pin_cfg.gpio)) { /* valid pin of axp-pinctrl, * config pin attributes individually. */ sunxi_gpio_to_name(pin_cfg.gpio, pin_name); if (pin_cfg.data != GPIO_DATA_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, pin_cfg.data); pin_config_set(AXP_PINCTRL, pin_name, config); } } else { pr_warn("invalid pin [%d] from sys-config\n", pin_cfg.gpio); } return pin_cfg.gpio; } EXPORT_SYMBOL(eink_sys_gpio_request); int eink_sys_gpio_release(int p_handler) { if (p_handler) gpio_free(p_handler); else pr_warn("%s: hdl is NULL\n", __func__); return 0; } EXPORT_SYMBOL(eink_sys_gpio_release); int eink_sys_gpio_set_value(u32 p_handler, u32 value_to_gpio, const char *gpio_name) { if (p_handler) __gpio_set_value(p_handler, value_to_gpio); else pr_warn("EINK_SET_GPIO, hdl is NULL\n"); return 0; } /* type: 0:invalid, 1: int; 2:str, 3: gpio */ int eink_sys_script_get_item(char *main_name, char *sub_name, int value[], int type) { char compat[32]; u32 len = 0; struct device_node *node; int ret = 0; struct gpio_config config; len = sprintf(compat, "allwinner,sunxi-%s", main_name); if (len > 32) pr_err("size of mian_name is out of range\n"); node = of_find_compatible_node(NULL, NULL, compat); if (!node) { pr_err("of_find_compatible_node %s fail\n", compat); return ret; } if (type == 1) { if (of_property_read_u32_array(node, sub_name, value, 1)) pr_info("of_property_read_u32_array %s.%s fail\n", main_name, sub_name); else ret = type; } else if (type == 2) { const char *str; if (of_property_read_string(node, sub_name, &str)) pr_info("of_property_read_string %s.%s fail\n", main_name, sub_name); else { ret = type; memcpy((void *)value, str, strlen(str) + 1); } } else if (type == 3) { struct eink_gpio_cfg *gpio_info = (struct eink_gpio_cfg *)value; int gpio; gpio = of_get_named_gpio_flags(node, sub_name, 0, (enum of_gpio_flags *)&config); if (!gpio_is_valid(gpio)) goto exit; gpio_info->gpio = config.gpio; gpio_info->mul_sel = config.mul_sel; gpio_info->pull = config.pull; gpio_info->drv_level = config.drv_level; gpio_info->data = config.data; memcpy(gpio_info->gpio_name, sub_name, strlen(sub_name) + 1); __inf("%s.%s gpio=%d,mul_sel=%d,data:%d\n", main_name, sub_name, gpio_info->gpio, gpio_info->mul_sel, gpio_info->data); ret = type; } exit: return ret; } EXPORT_SYMBOL(eink_sys_script_get_item); #if defined(CONFIG_ION_SUNXI) && defined(EINK_CACHE_MEM) static int __eink_ion_alloc_coherent(struct ion_client *client, struct eink_ion_mem *mem) { unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC; struct scatterlist *sgl = NULL; int ret = -1; if (IS_ERR_OR_NULL(client) || (mem == NULL)) { pr_err("input param is null\n"); return -1; } #if defined(CONFIG_SUNXI_IOMMU) mem->handle = ion_alloc(client, mem->size, 0, (1 << ION_HEAP_TYPE_SYSTEM), flags); #else mem->handle = ion_alloc(client, mem->size, PAGE_SIZE, ((1<handle)) { pr_err("ion_alloc failed, size=%lu!\n", (unsigned long)mem->size); return -2; } mem->vaddr = ion_map_kernel(client, mem->handle); if (IS_ERR_OR_NULL(mem->vaddr)) { pr_err("ion_map_kernel failed!!\n"); goto err_map_kernel; } #ifndef CONFIG_IOMMU_SUPPORT if (ion_phys(client, mem->handle, (ion_phys_addr_t *)&mem->paddr, &mem->size)) { pr_err("ion_phys failed!!\n"); goto err_phys; } #else sgl = mem->handle->buffer->sg_table->sgl; ret = dma_map_sg(g_eink_drvdata.device, sgl, 1, DMA_BIDIRECTIONAL); if (ret != 1) { pr_err("dma map sg fail, ret=%d\n", ret); goto err_phys; } mem->paddr = sg_dma_address(sgl); #endif EINK_INFO_MSG("ion malloc size=0x%x, vaddr=%p, paddr=0x%08x\n", (unsigned int)mem->size, mem->vaddr, (unsigned int)mem->paddr); return 0; err_phys: ion_unmap_kernel(client, mem->handle); err_map_kernel: ion_free(client, mem->handle); return -ENOMEM; } static void __eink_ion_free_coherent(struct ion_client *client, struct eink_ion_mem *mem) { if (IS_ERR_OR_NULL(client) || IS_ERR_OR_NULL(mem->handle) || IS_ERR_OR_NULL(mem->vaddr)) { pr_err("input param is null\n"); return; } ion_unmap_kernel(client, mem->handle); ion_free(client, mem->handle); return; } static void __eink_ion_dump(void) { #ifdef ION_DEBUG_DUMP struct eink_ion_mgr *ion_mgr = &(g_eink_drvdata.ion_mgr); struct ion_client *client = NULL; struct eink_ion_list_node *ion_node = NULL, *tmp_ion_node = NULL; struct eink_ion_mem *mem = NULL; unsigned int i = 0; if (ion_mgr == NULL) { pr_err("eink ion mgr is not initial yet\n"); return; } client = ion_mgr->client; mutex_lock(&(ion_mgr->mlock)); list_for_each_entry_safe(ion_node, tmp_ion_node, &ion_mgr->ion_list, node) { if (ion_node != NULL) { mem = &ion_node->mem; EINK_INFO_MSG("ion node %d: vaddr=0x%08x, paddr=0x%08x, size=%d\n", i, (unsigned int)mem->vaddr, (unsigned int)mem->paddr, mem->size); i++; } } mutex_unlock(&(ion_mgr->mlock)); #endif return; } static void *eink_ion_malloc(u32 num_bytes, void *phys_addr) { struct eink_ion_mgr *ion_mgr = &(g_eink_drvdata.ion_mgr); struct ion_client *client = NULL; struct eink_ion_list_node *ion_node = NULL; struct eink_ion_mem *mem = NULL; u32 *paddr = NULL; int ret = -1; if (ion_mgr == NULL) { pr_err("eink ion manager has not initial yet\n"); return NULL; } ion_node = kmalloc(sizeof(*ion_node), GFP_KERNEL); if (ion_node == NULL) { pr_err("fail to alloc ion node, size=%u\n", (unsigned int)sizeof(*ion_node)); return NULL; } mutex_lock(&(ion_mgr->mlock)); client = ion_mgr->client; mem = &ion_node->mem; mem->size = BYTE_ALIGN(num_bytes); ret = __eink_ion_alloc_coherent(client, mem); if (ret != 0) { pr_err("fail to alloc ion, ret=%d\n", ret); goto err_hdl; } paddr = (u32 *)phys_addr; *paddr = (u32)mem->paddr; list_add_tail(&(ion_node->node), &(ion_mgr->ion_list)); mutex_unlock(&(ion_mgr->mlock)); __eink_ion_dump(); return mem->vaddr; err_hdl: kfree(ion_node); mutex_unlock(&(ion_mgr->mlock)); return NULL; } static void eink_ion_free(void *virt_addr, void *phys_addr, u32 num_bytes) { struct eink_ion_mgr *ion_mgr = &(g_eink_drvdata.ion_mgr); struct ion_client *client = NULL; struct eink_ion_list_node *ion_node = NULL, *tmp_ion_node = NULL; struct eink_ion_mem *mem = NULL; bool found = false; if (ion_mgr == NULL) { pr_err("eink ion manager has not initial yet\n"); return; } client = ion_mgr->client; mutex_lock(&(ion_mgr->mlock)); list_for_each_entry_safe(ion_node, tmp_ion_node, &ion_mgr->ion_list, node) { if (ion_node != NULL) { mem = &ion_node->mem; if ((((unsigned long)mem->paddr) == ((unsigned long)phys_addr)) && (((unsigned long)mem->vaddr) == ((unsigned long)virt_addr))) { __eink_ion_free_coherent(client, mem); __list_del_entry(&(ion_node->node)); found = true; break; } } } mutex_unlock(&(ion_mgr->mlock)); if (false == found) { pr_err("vaddr=0x%08x, paddr=0x%08x is not found in ion\n", *((unsigned int *)virt_addr), *((unsigned int *)phys_addr)); } return; } int init_eink_ion_mgr(struct eink_ion_mgr *ion_mgr) { if (ion_mgr == NULL) { pr_err("[%s]:input param is null\n", __func__); return -EINVAL; } mutex_init(&(ion_mgr->mlock)); mutex_lock(&(ion_mgr->mlock)); INIT_LIST_HEAD(&(ion_mgr->ion_list)); ion_mgr->client = sunxi_ion_client_create("ion_eink"); if (IS_ERR_OR_NULL(ion_mgr->client)) { mutex_unlock(&(ion_mgr->mlock)); pr_err("[%s]:eink_ion client create failed!!", __func__); return -ENOMEM; } else { EINK_INFO_MSG("Init ion manager for EINK ok\n"); } mutex_unlock(&(ion_mgr->mlock)); return 0; } void deinit_eink_ion_mgr(struct eink_ion_mgr *ion_mgr) { struct eink_ion_list_node *ion_node = NULL, *tmp_ion_node = NULL; struct eink_ion_mem *mem = NULL; struct ion_client *client = NULL; if (ion_mgr == NULL) { pr_err("[%s]:input param is null\n", __func__); return; } client = ion_mgr->client; mutex_lock(&(ion_mgr->mlock)); list_for_each_entry_safe(ion_node, tmp_ion_node, &ion_mgr->ion_list, node) { if (ion_node != NULL) { // free all ion node mem = &ion_node->mem; __eink_ion_free_coherent(client, mem); __list_del_entry(&(ion_node->node)); kfree(ion_node); } } ion_client_destroy(client); mutex_unlock(&(ion_mgr->mlock)); } #else void *eink_mem_malloc(u32 num_bytes, void *phys_addr) { u32 actual_bytes; void *address = NULL; EINK_DEBUG_MSG("input!\n"); if (num_bytes != 0) { actual_bytes = ALIGN(num_bytes, PAGE_SIZE); address = dma_alloc_coherent(g_eink_drvdata.device, actual_bytes, (dma_addr_t *) phys_addr, GFP_KERNEL); if (address) { EINK_INFO_MSG ("dma_alloc_coherent ok, address=0x%p, size=0x%x\n", (void *)(*(unsigned long *)phys_addr), num_bytes); return address; } pr_err("dma_alloc_coherent fail, size=0x%x\n", num_bytes); return NULL; } pr_warn("%s size is zero\n", __func__); return NULL; } void eink_mem_free(void *virt_addr, void *phys_addr, u32 num_bytes) { u32 actual_bytes; actual_bytes = BYTE_ALIGN(num_bytes); if (phys_addr && virt_addr) dma_free_coherent(g_eink_drvdata.device, actual_bytes, virt_addr, (dma_addr_t)phys_addr); } #endif void *eink_malloc(u32 num_bytes, void *phys_addr) { #if defined(CONFIG_ION_SUNXI) && defined(EINK_CACHE_MEM) return eink_ion_malloc(num_bytes, phys_addr); /* cache */ #else return eink_mem_malloc(num_bytes, phys_addr); /* skip cache */ #endif } EXPORT_SYMBOL(eink_malloc); void eink_free(void *virt_addr, void *phys_addr, u32 num_bytes) { #if defined(CONFIG_ION_SUNXI) && defined(EINK_CACHE_MEM) return eink_ion_free(virt_addr, phys_addr, num_bytes); #else return eink_mem_free(virt_addr, phys_addr, num_bytes); #endif } EXPORT_SYMBOL(eink_free); void eink_cache_sync(void *startAddr, int size) { #if defined(CONFIG_ION_SUNXI) && defined(EINK_CACHE_MEM) struct sunxi_cache_range range; range.start = (unsigned long)startAddr; range.end = (unsigned long)startAddr + size; #ifdef CONFIG_ARM64 __dma_flush_range((void *)range.start, range.end - range.start); #else dmac_flush_range((void *)range.start, (void *)range.end); #endif return; #else return; #endif } EXPORT_SYMBOL(eink_cache_sync); int eink_dma_map_core(int fd, struct dmabuf_item *item) { struct dma_buf *dmabuf; struct dma_buf_attachment *attachment; struct sg_table *sgt, *sgt_bak; struct scatterlist *sgl, *sgl_bak; s32 sg_count = 0; int ret = -1; int i; if (fd < 0) { pr_err("[EINK]dma_buf_id(%d) is invalid\n", fd); goto exit; } dmabuf = dma_buf_get(fd); if (IS_ERR(dmabuf)) { pr_err("[EINK]dma_buf_get failed, fd=%d\n", fd); goto exit; } attachment = dma_buf_attach(dmabuf, g_eink_drvdata.device); if (IS_ERR(attachment)) { pr_err("[EINK]dma_buf_attach failed\n"); goto err_buf_put; } sgt = dma_buf_map_attachment(attachment, DMA_FROM_DEVICE); if (IS_ERR_OR_NULL(sgt)) { pr_err("[EINK]dma_buf_map_attachment failed\n"); goto err_buf_detach; } /* create a private sgtable base on the given dmabuf */ sgt_bak = kmalloc(sizeof(struct sg_table), GFP_KERNEL | __GFP_ZERO); if (sgt_bak == NULL) { pr_err("[EINK]alloc sgt fail\n"); goto err_buf_unmap; } ret = sg_alloc_table(sgt_bak, sgt->nents, GFP_KERNEL); if (ret != 0) { pr_err("[EINK]alloc sgt fail\n"); goto err_kfree; } sgl_bak = sgt_bak->sgl; for_each_sg(sgt->sgl, sgl, sgt->nents, i) { sg_set_page(sgl_bak, sg_page(sgl), sgl->length, sgl->offset); sgl_bak = sg_next(sgl_bak); } sg_count = dma_map_sg_attrs(g_eink_drvdata.device, sgt_bak->sgl, sgt_bak->nents, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); if (sg_count != 1) { pr_err("[EINK]dma_map_sg failed:%d\n", sg_count); goto err_sgt_free; } item->fd = fd; item->buf = dmabuf; item->sgt = sgt_bak; item->attachment = attachment; item->dma_addr = sg_dma_address(sgt_bak->sgl); ret = 0; goto exit; err_sgt_free: sg_free_table(sgt_bak); err_kfree: kfree(sgt_bak); err_buf_unmap: /* unmap attachment sgt, not sgt_bak, because it's not alloc yet! */ dma_buf_unmap_attachment(attachment, sgt, DMA_FROM_DEVICE); err_buf_detach: dma_buf_detach(dmabuf, attachment); err_buf_put: dma_buf_put(dmabuf); exit: return ret; } void eink_dma_unmap_core(struct dmabuf_item *item) { dma_unmap_sg_attrs(g_eink_drvdata.device, item->sgt->sgl, item->sgt->nents, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); dma_buf_unmap_attachment(item->attachment, item->sgt, DMA_FROM_DEVICE); sg_free_table(item->sgt); kfree(item->sgt); dma_buf_detach(item->buf, item->attachment); dma_buf_put(item->buf); } struct dmabuf_item *eink_dma_map(int fd) { struct dmabuf_item *item = NULL; EINK_INFO_MSG("Func intput!\n"); item = kmalloc(sizeof(struct dmabuf_item), GFP_KERNEL | __GFP_ZERO); if (item == NULL) { pr_err("malloc memory of size %d fail!\n", (unsigned int)sizeof(struct dmabuf_item)); goto exit; } if (eink_dma_map_core(fd, item) != 0) { kfree(item); item = NULL; } exit: return item; } void eink_dma_unmap(struct dmabuf_item *item) { eink_dma_unmap_core(item); kfree(item); } void eink_print_register(unsigned long start_addr, unsigned long end_addr) { unsigned long start_addr_align = ALIGN(start_addr, 4); unsigned long end_addr_align = ALIGN(end_addr, 4); unsigned long addr = 0; unsigned char tmp_buf[50] = {0}; unsigned char buf[256] = {0}; pr_info("\n"); pr_info("print reg: [0x%08x ~ 0x%08x]\n", (unsigned int)start_addr_align, (unsigned int)end_addr_align); for (addr = start_addr_align; addr <= end_addr_align; addr += 4) { if (0 == (addr & 0xf)) { memset(tmp_buf, 0, sizeof(tmp_buf)); memset(buf, 0, sizeof(buf)); snprintf(tmp_buf, sizeof(tmp_buf), "0x%08x: ", (unsigned int)addr); strncat(buf, tmp_buf, sizeof(tmp_buf)); } memset(tmp_buf, 0, sizeof(tmp_buf)); //snprintf(tmp_buf, sizeof(tmp_buf), "0x%08x ", readl((void __iomem *)(addr | 0xf0000000))); snprintf(tmp_buf, sizeof(tmp_buf), "0x%08x ", readl((void __iomem *)addr)); strncat(buf, tmp_buf, sizeof(tmp_buf)); if (0 == ((addr + 4) & 0xf)) { pr_info("%s\n", buf); msleep(5); } } } void print_free_pipe_list(struct pipe_manager *mgr) { struct pipe_info_node *pipe, *tpipe; unsigned long flags = 0; unsigned int count = 0; spin_lock_irqsave(&mgr->list_lock, flags); if (list_empty(&mgr->pipe_free_list)) { EINK_INFO_MSG("FREE_LIST is empty\n"); spin_unlock_irqrestore(&mgr->list_lock, flags); return; } list_for_each_entry_safe(pipe, tpipe, &mgr->pipe_free_list, node) { #ifdef PIPELINE_DEBUG EINK_INFO_MSG("FREE_LIST: pipe %02d is free\n", pipe->pipe_id); #endif count++; } EINK_INFO_MSG("[+++] %d Pipes are Free!\n", count); spin_unlock_irqrestore(&mgr->list_lock, flags); } void print_used_pipe_list(struct pipe_manager *mgr) { struct pipe_info_node *pipe, *tpipe; unsigned long flags = 0; unsigned int count = 0; spin_lock_irqsave(&mgr->list_lock, flags); if (list_empty(&mgr->pipe_used_list)) { EINK_INFO_MSG("USED_LIST is empty\n"); spin_unlock_irqrestore(&mgr->list_lock, flags); return; } list_for_each_entry_safe(pipe, tpipe, &mgr->pipe_used_list, node) { #ifdef PIPELINE_DEBUG EINK_INFO_MSG("USED_LIST: pipe %02d is used\n", pipe->pipe_id); #endif count++; } EINK_INFO_MSG("[+++] %d Pipes are Used!\n", count); spin_unlock_irqrestore(&mgr->list_lock, flags); } void print_coll_img_list(struct buf_manager *mgr) { struct img_node *curnode = NULL, *tmpnode = NULL; mutex_lock(&mgr->mlock); if (list_empty(&mgr->img_collision_list)) { EINK_INFO_MSG("COLL_LIST is empty\n"); mutex_unlock(&mgr->mlock); return; } list_for_each_entry_safe(curnode, tmpnode, &mgr->img_collision_list, node) { EINK_INFO_MSG("COLL_LIST: img [buf_id, order, state] [%d, %d, %d] is collision\n", curnode->buf_id, curnode->upd_order, curnode->img->state); } mutex_unlock(&mgr->mlock); } void print_used_img_list(struct buf_manager *mgr) { struct img_node *curnode = NULL, *tmpnode = NULL; mutex_lock(&mgr->mlock); if (list_empty(&mgr->img_used_list)) { EINK_INFO_MSG("USED_LIST is empty\n"); mutex_unlock(&mgr->mlock); return; } list_for_each_entry_safe(curnode, tmpnode, &mgr->img_used_list, node) { EINK_INFO_MSG("IMG_USED_LIST: img [buf_id, order, state] [%d, %d, %d] is used\n", curnode->buf_id, curnode->upd_order, curnode->img->state); } mutex_unlock(&mgr->mlock); } void print_free_img_list(struct buf_manager *mgr) { struct img_node *curnode = NULL, *tmpnode = NULL; mutex_lock(&mgr->mlock); if (list_empty(&mgr->img_free_list)) { EINK_INFO_MSG("IMG_FREE_LIST is empty\n"); mutex_unlock(&mgr->mlock); return; } list_for_each_entry_safe(curnode, tmpnode, &mgr->img_free_list, node) { EINK_INFO_MSG("FREE_LIST: img [buf_id, order, state] [%d, %d, %d] is free\n", curnode->buf_id, curnode->upd_order, curnode->img->state); } mutex_unlock(&mgr->mlock); } int save_as_bin_file(__u8 *buf, char *file_name, __u32 length, loff_t pos) { struct file *fp = NULL; mm_segment_t old_fs; ssize_t ret = 0; if ((buf == NULL) || (file_name == NULL)) { pr_err("%s: buf or file_name is null\n", __func__); return -1; } fp = filp_open(file_name, O_RDWR | O_CREAT, 0644); if (IS_ERR(fp)) { pr_err("%s: fail to open file(%s), ret=%d\n", __func__, file_name, *((u32 *)fp)); return -1; } old_fs = get_fs(); set_fs(KERNEL_DS); ret = vfs_write(fp, buf, length, &pos); pr_info("%s: save %s done, len=%u, pos=%lld, ret=%d\n", __func__, file_name, length, pos, (unsigned int)ret); set_fs(old_fs); filp_close(fp, NULL); return ret; } s32 save_gray_image_as_bmp(u8 *buf, char *file_name, u32 scn_w, u32 scn_h) { int ret = -1; ES_FILE *fd = NULL; mm_segment_t old_fs; loff_t pos = 0; BMP_FILE_HEADER st_file_header; BMP_INFO_HEADER st_info_header; char *src_buf = buf; char *path = file_name; BMP_INFO dest_info; u32 bit_count = 0; ST_RGB *dest_buf = NULL; ST_ARGB color_table[256]; u32 i = 0; if ((path == NULL) || (src_buf == NULL)) { pr_err("%s: input param is null\n", __func__); return -1; } fd = filp_open(path, O_RDWR | O_CREAT, 0644); if (IS_ERR(fd)) { pr_err("%s: create bmp file(%s) error\n", __func__, path); return -2; } bit_count = 8; memset(&dest_info, 0, sizeof(BMP_INFO)); dest_info.width = scn_w; dest_info.height = scn_h; dest_info.bit_count = 8; dest_info.color_tbl_size = 0; dest_info.image_size = (dest_info.width * dest_info.height * dest_info.bit_count)/8; st_file_header.bfType[0] = 'B'; st_file_header.bfType[1] = 'M'; st_file_header.bfOffBits = BMP_IMAGE_DATA_OFFSET; st_file_header.bfSize = st_file_header.bfOffBits + dest_info.image_size; st_file_header.bfReserved1 = 0; st_file_header.bfReserved2 = 0; st_info_header.biSize = sizeof(BMP_INFO_HEADER); st_info_header.biWidth = dest_info.width; st_info_header.biHeight = dest_info.height; st_info_header.biPlanes = 1; st_info_header.biBitCount = dest_info.bit_count; st_info_header.biCompress = 0; st_info_header.biSizeImage = dest_info.image_size; st_info_header.biXPelsPerMeter = 0; st_info_header.biYPelsPerMeter = 0; st_info_header.biClrUsed = 0; st_info_header.biClrImportant = 0; for (i = 0; i < 256; i++) { color_table[i].reserved = 0; color_table[i].r = i; color_table[i].g = i; color_table[i].b = i; } dest_buf = (ST_RGB *)src_buf; old_fs = get_fs(); set_fs(KERNEL_DS); ret = vfs_write(fd, (u8 *)(&st_file_header), BMP_FILE_HEADER_SIZE, &pos); EINK_INFO_MSG("save file header, len=%u, pos=%d, ret=%d\n", (unsigned int)BMP_FILE_HEADER_SIZE, (int)pos, ret); ret = vfs_write(fd, (u8 *)(&st_info_header), BMP_INFO_HEADER_SIZE, &pos); EINK_INFO_MSG("save file header, len=%u, pos=%d, ret=%d\n", (unsigned int)BMP_INFO_HEADER_SIZE, (int)pos, ret); ret = vfs_write(fd, (u8 *)(color_table), BMP_COLOR_SIZE, &pos); EINK_INFO_MSG("save file header, len=%u, pos=%d, ret=%d\n", (unsigned int)BMP_COLOR_SIZE, (int)pos, ret); ret = vfs_write(fd, (u8 *)(dest_buf), dest_info.image_size, &pos); EINK_INFO_MSG("save file header, len=%u, pos=%d, ret=%d\n", dest_info.image_size, (int)pos, ret); set_fs(old_fs); ret = 0; if (!IS_ERR(fd)) { filp_close(fd, NULL); } return ret; } void save_upd_rmi_buffer(u32 order, u8 *buf, u32 len) { char file_name[256] = {0}; if (buf == NULL) { pr_err("%s:input param is null\n", __func__); return; } memset(file_name, 0, sizeof(file_name)); snprintf(file_name, sizeof(file_name), "/tmp/rmi_buf%d.bin", order); save_as_bin_file(buf, file_name, len, 0); } void eink_put_gray_to_mem(u32 order, char *buf, u32 width, u32 height) { char file_name[256] = {0}; if (buf == NULL) { pr_err("input param is null\n"); return; } memset(file_name, 0, sizeof(file_name)); snprintf(file_name, sizeof(file_name), "/tmp/eink_image%d.bmp", order); save_gray_image_as_bmp((u8 *)buf, file_name, width, height); } void eink_save_last_img(char *buf, u32 width, u32 height) { char file_name[256] = {0}; static int order; if (buf == NULL) { pr_err("input param is null\n"); return; } pr_info("[%s]: ", __func__); memset(file_name, 0, sizeof(file_name)); snprintf(file_name, sizeof(file_name), "/tmp/last_img%d.bmp", order); save_gray_image_as_bmp((u8 *)buf, file_name, width, height); order++; } void eink_save_current_img(char *buf, u32 width, u32 height) { char file_name[256] = {0}; static int order; if (buf == NULL) { pr_err("input param is null\n"); return; } pr_info("[%s]: ", __func__); memset(file_name, 0, sizeof(file_name)); snprintf(file_name, sizeof(file_name), "/tmp/cur_img%d.bmp", order); save_gray_image_as_bmp((u8 *)buf, file_name, width, height); order++; } int eink_get_default_file_from_mem(__u8 *buf, char *file_name, __u32 length, loff_t pos) { struct file *fp = NULL; mm_segment_t fs; __s32 read_len = 0; ssize_t ret = 0; if ((buf == NULL) || (file_name == NULL)) { pr_err("%s: buf or file_name is null\n", __func__); return -1; } EINK_INFO_MSG("\n"); fp = filp_open(file_name, O_RDONLY, 0); if (IS_ERR(fp)) { pr_err("%s: fail to open file(%s), ret=0x%p\n", __func__, file_name, fp); return -1; } fs = get_fs(); set_fs(KERNEL_DS); read_len = vfs_read(fp, buf, length, &pos); if (read_len != length) { pr_err("maybe miss some data(read=%d byte, file=%d byte)\n", read_len, length); ret = -EAGAIN; } set_fs(fs); filp_close(fp, NULL); return ret; } void save_waveform_to_mem(u32 order, u8 *buf, u32 frames, u32 bit_num) { char file_name[256] = {0}; u32 len = 0, per_size = 0; if (buf == NULL) { pr_err("%s:input param is null\n", __func__); return; } if (bit_num == 5) per_size = 1024; else per_size = 256; len = frames * per_size; pr_info("[%s] size = (%d x %d) = %d\n", __func__, frames, bit_num, len); memset(file_name, 0, sizeof(file_name)); snprintf(file_name, sizeof(file_name), "/tmp/waveform%d.bin", order); save_as_bin_file(buf, file_name, len, 0); } void save_rearray_waveform_to_mem(u8 *buf, u32 len) { char file_name[256] = {0}; if (buf == NULL) { pr_err("%s:input param is null\n", __func__); return; } pr_info("%s:len is %d\n", __func__, len); memset(file_name, 0, sizeof(file_name)); snprintf(file_name, sizeof(file_name), "/tmp/waveform_rearray.bin"); save_as_bin_file(buf, file_name, len, 0); } #ifdef OFFLINE_SINGLE_MODE void save_one_wavedata_buffer(u8 *buf, bool is_edma) { struct wavedata_queue *queue = NULL; /* u8 *buf = NULL; */ char file_name[256] = {0}; static u32 dec_id = 0, edma_id; u32 data_len = 0; u32 vsync = 0, hsync = 0; struct eink_manager *mgr = get_eink_manager(); struct pipe_manager *pipe_mgr = get_pipeline_manager(); if (pipe_mgr == NULL) { pr_err("%s;pipe mgr is NULL\n", __func__); return; } hsync = mgr->timing_info.lbl + mgr->timing_info.lsl + mgr->timing_info.ldl + mgr->timing_info.lel; vsync = mgr->timing_info.fbl + mgr->timing_info.fsl + mgr->timing_info.fdl + mgr->timing_info.fel; if (mgr->panel_info.data_len == 8) { data_len = hsync * vsync; } else { data_len = hsync * vsync * 2; } queue = &pipe_mgr->wavedata_ring_buffer; memset(file_name, 0, sizeof(file_name)); if (is_edma) { snprintf(file_name, sizeof(file_name), "/tmp/edma_wf_data%d.bin", edma_id); /* buf = (void*)queue->wavedata_vaddr[0]; */ edma_id++; } else { snprintf(file_name, sizeof(file_name), "/tmp/dc_wf_data%d.bin", dec_id); /* buf = (void*)queue->wavedata_vaddr[0]; */ dec_id++; } save_as_bin_file(buf, file_name, data_len, 0); } #if 0 static void dump_wavedata_buffer(struct wavedata_queue *queue) { int i = 0; unsigned long flags = 0; unsigned int head = 0, tail = 0, tmp_tail = 0, tmp_head = 0; enum wv_buffer_state buffer_state[WAVE_DATA_BUF_NUM]; spin_lock_irqsave(&queue->slock, flags); head = queue->head; tail = queue->tail; tmp_tail = queue->tmp_tail; tmp_head = queue->tmp_head; for (i = 0; i < WAVE_DATA_BUF_NUM; i++) buffer_state[i] = queue->buffer_state[i]; spin_unlock_irqrestore(&queue->slock, flags); EINK_INFO_MSG("head=%d, tail=%d, tmp_head=%d, tmp_tail=%d\n", head, tail, tmp_head, tmp_tail); for (i = 0; i < WAVE_DATA_BUF_NUM; i++) { if (buffer_state[i] != WV_INIT_STATE) { printk("miss buffer %d, state=%d\n", i, buffer_state[i]); } } } #endif void save_all_wavedata_buffer(struct eink_manager *mgr, u32 all_total_frames) { struct wavedata_queue *queue = NULL; u8 *buf = NULL; char file_name[256] = {0}; u32 id = 0; u32 data_len = 0; u32 vsync = 0, hsync = 0; struct pipe_manager *pipe_mgr = get_pipeline_manager(); if (pipe_mgr == NULL) { pr_err("%s;pipe mgr is NULL\n", __func__); return; } if (all_total_frames > WAVE_DATA_BUF_NUM) { pr_err("too many frames(%d), not to save\n ", all_total_frames); return; } hsync = mgr->timing_info.lbl + mgr->timing_info.lsl + mgr->timing_info.ldl + mgr->timing_info.lel; vsync = mgr->timing_info.fbl + mgr->timing_info.fsl + mgr->timing_info.fdl + mgr->timing_info.fel; if (mgr->panel_info.data_len == 8) { data_len = hsync * vsync; } else { data_len = hsync * vsync * 2; } EINK_INFO_MSG("save wavedata, total_frames=%d\n", all_total_frames); queue = &pipe_mgr->wavedata_ring_buffer; for (id = 0; id < all_total_frames; id++) { memset(file_name, 0, sizeof(file_name)); snprintf(file_name, sizeof(file_name), "/tmp/wvdata_%d.bin", id); buf = (void *)queue->wavedata_vaddr[id]; save_as_bin_file(buf, file_name, data_len, 0); } } #endif