/* * Allwinner SoCs display driver. * * Copyright (C) 2016 Allwinner. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ /* Description: awf waveform file decoder Version : v0.1 Author : oujunxi Date : 2015/07/10 */ #include "disp_waveform.h" #define DEBUG_WAVEFILE #ifdef DEBUG_WAVEFILE #define WF_DBG(msg, fmt...) printk(KERN_WARNING msg, ##fmt) #define WF_INFO(msg, fmt...) printk(KERN_WARNING msg, ##fmt) #define WF_WRN(msg, fmt...) printk(KERN_WARNING msg, ##fmt) #define WF_ERR(msg, fmt...) printk(KERN_ERR msg, ##fmt) #else #define WF_DBG(msg, fmt...) #define WF_INFO(msg, fmt...) #define WF_WRN(msg, fmt...) printk(KERN_WARNING msg, ##fmt) #define WF_ERR(msg, fmt...) printk(KERN_ERR msg, ##fmt) #endif #define C_HEADER_INFO_OFFSET 0 #define C_HEADER_TYPE_ID_OFFSET 0 /* eink type(eink id) */ #define C_HEADER_VERSION_STR_OFFSET 1 #define C_HEADER_INFO_SIZE 128 /* size of awf file header */ #define C_TEMP_TBL_OFFSET (C_HEADER_INFO_OFFSET+C_HEADER_INFO_SIZE) /* temperature table offset from the beginning of the awf file */ #define C_TEMP_TBL_SIZE 32 /* temperature table size */ #define C_MODE_ADDR_TBL_OFFSET (C_TEMP_TBL_OFFSET+C_TEMP_TBL_SIZE) /* mode address table offset */ #define C_MODE_ADDR_TBL_SIZE 64 #define C_INIT_MODE_ADDR_OFFSET C_MODE_ADDR_TBL_OFFSET #define C_GC16_MODE_ADDR_OFFSET (C_MODE_ADDR_TBL_OFFSET+4) #define C_GC4_MODE_ADDR_OFFSET (C_MODE_ADDR_TBL_OFFSET+8) #define C_DU_MODE_ADDR_OFFSET (C_MODE_ADDR_TBL_OFFSET+12) #define C_A2_MODE_ADDR_OFFSET (C_MODE_ADDR_TBL_OFFSET+16) #define C_GC16_LOCAL_MODE_ADDR_OFFSET (C_MODE_ADDR_TBL_OFFSET+20) #define C_GC4_LOCAL_MODE_ADDR_OFFSET (C_MODE_ADDR_TBL_OFFSET+24) #define C_A2_IN_MODE_ADDR_OFFSET (C_MODE_ADDR_TBL_OFFSET+28) #define C_A2_OUT_MODE_ADDR_OFFSET (C_MODE_ADDR_TBL_OFFSET+32) #define C_INIT_MODE_OFFSET (C_MODE_ADDR_TBL_OFFSET+C_MODE_ADDR_TBL_SIZE) #define C_REAL_TEMP_AREA_NUM 15 /* max temperature range number */ #define WF_MAX_COL 256 /* GC16, 16*16 = 256 */ typedef struct { u8 load_flag; /* when awf has been loaded, init_flag = 1 */ char *p_wf_vaddr; /* virtual address of waveform file */ u32 p_wf_paddr; /* phy address of waveform file */ EINK_PANEL_TYPE eink_panel_type;/*eink type, example PVI, OED and etc*/ u8 wf_temp_area_tbl[C_TEMP_TBL_SIZE]; /* temperature table */ /*phy address*/ u32 p_init_wf; /* init mode address (include temperature table address) */ u32 p_gc16_wf; /* gc16 mode address (include temperature table address) */ u32 p_gc4_wf; /* gc4 mode address (include temperature table address) */ u32 p_du_wf; /* du mode address (include temperature table address) */ u32 p_A2_wf; /* A2 mode address (include temperature table address) */ u32 p_gc16_local_wf; /* gc16 local mode address (include temperature table address) */ u32 p_gc4_local_wf; /* gc4 local mode address (include temperature table address) */ u32 p_A2_in_wf; /* A2 in mode address (include temperature table address) */ u32 p_A2_out_wf; /* A2 out mode address (include temperature table address) */ } AWF_WAVEFILE; static AWF_WAVEFILE g_waveform_file; /* *Description : get temperature range index from temperature table, * if temperature match TBL[id] <= temperature < TBL[id + 1] * condition, then temp range index = id *Input : temperature -- temperature of eink panel *Output : None *Return : 0 & positive -- temperature range index, negative -- fail */ static __s32 get_temp_range_index(int temperature) { __s32 index = -EINVAL; for (index = 0; index < C_TEMP_TBL_SIZE; index++) { if ((g_waveform_file.wf_temp_area_tbl[index] == 0) && (index > 0)) break; if (temperature < g_waveform_file.wf_temp_area_tbl[index]) break; } if (index > 0) index -= 1; return index; } /* Description : get mode address according to flush mode Input : mode -- flush mode Output : None Return : NULL -- get mode address fail, others -- get mode address successfully */ static u8 *get_mode_virt_address(enum eink_update_mode mode) { u8 *p_wf_file = NULL; u32 offset = 0; switch (mode) { case EINK_INIT_MODE: { offset = (u32)g_waveform_file.p_init_wf - g_waveform_file.p_wf_paddr; p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset; break; } case EINK_DU_MODE: case EINK_DU_RECT_MODE: { offset = (u32)g_waveform_file.p_du_wf - g_waveform_file.p_wf_paddr; p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset; break; } case EINK_GC16_MODE: case EINK_GC16_RECT_MODE: { offset = (u32)g_waveform_file.p_gc16_wf - g_waveform_file.p_wf_paddr; p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset; break; } case EINK_A2_MODE: case EINK_A2_RECT_MODE: { offset = (u32)g_waveform_file.p_A2_wf - g_waveform_file.p_wf_paddr; p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset; break; } case EINK_GC16_LOCAL_MODE: case EINK_GC16_LOCAL_RECT_MODE: { offset = (u32)g_waveform_file.p_gc16_local_wf - g_waveform_file.p_wf_paddr; p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset; break; } default: { WF_WRN("unkown mode(0x%x)\n", mode); p_wf_file = NULL; break; } } return p_wf_file; } /* *Description : get mode address according to flush mode *Input : mode -- flush mode *Output : None *Return : NULL -- get mode address fail, * others -- get mode address successfully */ static u32 get_mode_phy_address(enum eink_update_mode mode) { u32 phy_addr = 0; switch (mode) { case EINK_INIT_MODE: { phy_addr = g_waveform_file.p_init_wf; break; } case EINK_DU_MODE: case EINK_DU_RECT_MODE: { phy_addr = g_waveform_file.p_du_wf; break; } case EINK_GC16_MODE: case EINK_GC16_RECT_MODE: { phy_addr = g_waveform_file.p_gc16_wf; break; } case EINK_A2_MODE: case EINK_A2_RECT_MODE: { phy_addr = g_waveform_file.p_A2_wf; break; } case EINK_GC16_LOCAL_MODE: case EINK_GC16_LOCAL_RECT_MODE: { phy_addr = g_waveform_file.p_gc16_local_wf; break; } default: { WF_WRN("unkown mode(0x%x)\n", mode); phy_addr = 0; break; } } return phy_addr; } /* Description : load waveform file to memory, and save each flush mode of address. This function should be called before waveform data has been used. Input : path -- path of waveform file Output : None Return : 0 -- load waveform ok, others -- fail */ extern void *disp_malloc(u32 num_bytes, void *phy_addr); __s32 init_waveform(const char *path) { struct file *fp = NULL; __s32 file_len = 0; __s32 read_len = 0; mm_segment_t fs; loff_t pos; __u32 wf_buffer_len = 0; /* the len of waveform file */ u32 *pAddr = NULL; __s32 ret = -EINVAL; if (path == NULL) { WF_ERR("path is null\n"); return -EINVAL; } WF_DBG("starting to load awf waveform file(%s)\n", path); fp = filp_open(path, O_RDONLY, 0); if (IS_ERR(fp)) { WF_ERR("fail to open waveform file(%s)", path); return -EBADF; } memset(&g_waveform_file, 0, sizeof(g_waveform_file)); fs = get_fs(); set_fs(KERNEL_DS); pos = 0; file_len = fp->f_path.dentry->d_inode->i_size; wf_buffer_len = file_len+1023; g_waveform_file.p_wf_vaddr = (char *)disp_malloc(wf_buffer_len, (u32 *)(&g_waveform_file.p_wf_paddr)); /* waveform vaddr */ if (g_waveform_file.p_wf_vaddr == NULL) { WF_ERR("fail to alloc memory for waveform file\n"); ret = -ENOMEM; goto error; } read_len = vfs_read(fp, (char *)g_waveform_file.p_wf_vaddr, file_len, &pos); if (read_len != file_len) { WF_ERR("miss data(read=%d byte, file=%d byte) when read wavefile\n", read_len, file_len); ret = -EAGAIN; goto error; } g_waveform_file.eink_panel_type = *((u8 *)g_waveform_file.p_wf_vaddr); WF_DBG("eink type=0x%x\n", g_waveform_file.eink_panel_type); /* starting to load data */ memcpy(g_waveform_file.wf_temp_area_tbl, (g_waveform_file.p_wf_vaddr+C_TEMP_TBL_OFFSET), C_TEMP_TBL_SIZE); pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_INIT_MODE_ADDR_OFFSET); g_waveform_file.p_init_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr); pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_GC16_MODE_ADDR_OFFSET); g_waveform_file.p_gc16_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr); pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_GC4_MODE_ADDR_OFFSET); g_waveform_file.p_gc4_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr); pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_DU_MODE_ADDR_OFFSET); g_waveform_file.p_du_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr); pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_A2_MODE_ADDR_OFFSET); g_waveform_file.p_A2_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr); pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_GC16_LOCAL_MODE_ADDR_OFFSET); g_waveform_file.p_gc16_local_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr); pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_GC4_LOCAL_MODE_ADDR_OFFSET); g_waveform_file.p_gc4_local_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr); pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_A2_IN_MODE_ADDR_OFFSET); g_waveform_file.p_A2_in_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr); pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_A2_OUT_MODE_ADDR_OFFSET); g_waveform_file.p_A2_out_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr); if (fp) { filp_close(fp, NULL); set_fs(fs); } g_waveform_file.load_flag = 1; WF_DBG("load waveform file(%s) successfully\n", path); return 0; error: if (g_waveform_file.p_wf_vaddr != NULL) { vfree(g_waveform_file.p_wf_vaddr); g_waveform_file.p_wf_vaddr = NULL; } if (fp) { filp_close(fp, NULL); set_fs(fs); } return ret; } /* Description: get eink panel type, for example, 3 stands for ED060XC3(4-bit), 4 stands for ED060XD4(5-bit) Input: None Output: type -- save the type of eink panel Return: 0 -- get eink panel type successfully, others -- fail */ int get_eink_panel_type(EINK_PANEL_TYPE *type) { if (g_waveform_file.load_flag != 1) { WF_ERR("waveform hasn't init yet, pls init first\n"); return -EAGAIN; } if (type == NULL) { WF_ERR("input param is null\n"); return -EINVAL; } *type = g_waveform_file.eink_panel_type; return 0; } /* Description: get bit number of eink panel, 4-bit stands for 16 grayscale, 5-bit stands for 32 grayscale Input: None Output: bit_num -- save the bit number of eink panel Return: 0 -- get eink panel type successfully, others -- fail */ int get_eink_panel_bit_num(enum eink_bit_num *bit_num) { if (g_waveform_file.load_flag != 1) { WF_ERR("waveform hasn't init yet, pls init first\n"); return -EAGAIN; } if (bit_num == NULL) { WF_ERR("input param is null\n"); return -EINVAL; } if (g_waveform_file.eink_panel_type == ED060XD4) *bit_num = EINK_BIT_5; else *bit_num = EINK_BIT_4; return 0; } /* Description: get waveform data address according to mode and temperature Input: None Output: type -- save the type of eink panel Return: 0 -- get eink panel type successfully, others -- fail */ int get_waveform_data(enum eink_update_mode mode, u32 temp, u32 *total_frames, u32 *wf_buf) { u32 p_mode_phy_addr = 0; u8 *p_mode_virt_addr = NULL; u32 mode_temp_offset = 0; u8 *p_mode_temp_addr = NULL; /* u16 *p_pointer = NULL; */ u32 temp_range_id = 0; if (g_waveform_file.load_flag != 1) { WF_ERR("waveform hasn't init yet, pls init first\n"); return -EAGAIN; } if ((total_frames == NULL) || (wf_buf == NULL)) { WF_ERR("input param is null\n"); return -EINVAL; } p_mode_virt_addr = get_mode_virt_address(mode); if (p_mode_virt_addr == NULL) { WF_ERR("get mode virturl address fail, mode=0x%x\n", mode); return -EINVAL; } p_mode_phy_addr = get_mode_phy_address(mode); if (p_mode_phy_addr == 0) { WF_ERR("get mode phy address fail, mode=0x%x\n", mode); return -EINVAL; } temp_range_id = get_temp_range_index(temp); if (temp_range_id < 0) { WF_ERR("get temp range index fail, temp=0x%x\n", temp); return -EINVAL; } mode_temp_offset = *((u32 *)p_mode_virt_addr + temp_range_id); p_mode_temp_addr = (u8 *)p_mode_virt_addr + mode_temp_offset; *total_frames = *((u16 *)p_mode_temp_addr); mode_temp_offset = mode_temp_offset + 4; /* skip total frame(2 Byte) and dividor(2 Byte) */ *wf_buf = p_mode_phy_addr + mode_temp_offset; return 0; } /* Description : free memory that used by waveform file, after that, waveform data cannot be used until init_waveform function has been called. This function should be called when unload module. Input : None Output : None Return : None */ void free_waveform(void) { if (g_waveform_file.p_wf_vaddr != NULL) { vfree(g_waveform_file.p_wf_vaddr); g_waveform_file.p_wf_vaddr = NULL; } g_waveform_file.load_flag = 0; return; }