diff options
Diffstat (limited to 'drivers/media/sprd_gsp/gsp_drv.c')
| -rw-r--r-- | drivers/media/sprd_gsp/gsp_drv.c | 1253 |
1 files changed, 1253 insertions, 0 deletions
diff --git a/drivers/media/sprd_gsp/gsp_drv.c b/drivers/media/sprd_gsp/gsp_drv.c new file mode 100644 index 00000000..8606f668 --- /dev/null +++ b/drivers/media/sprd_gsp/gsp_drv.c @@ -0,0 +1,1253 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/platform_device.h> +#include <linux/proc_fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <asm/uaccess.h> +#include <linux/math64.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/irq.h> +#include <linux/kthread.h> +#include <linux/io.h>//for ioremap +#include <linux/pid.h> + +#ifdef CONFIG_OF +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/device.h> +#endif + +#include <soc/sprd/arch_misc.h>// get chip id +#include <video/ion_sprd.h> +//#include "gsp_drv.h" +#include "gsp_config_if.h" +#include "scaler_coef_cal.h" + +#include "gsp_debug.h" + + + +#ifdef CONFIG_OF +static struct device *s_gsp_of_dev = NULL; +ulong g_gsp_base_addr = 0; +ulong g_gsp_mmu_ctrl_addr = 0; +#endif +gsp_context_t *g_gspCtx = NULL;// only used to debug when kernel crash, not used to pass parameters + +static gsp_user* gsp_get_user(pid_t user_pid, gsp_context_t *gspCtx) +{ + gsp_user* ret_user = NULL; + int i; + + for (i = 0; i < GSP_MAX_USER; i ++) { + if ((gspCtx->gsp_user_array + i)->pid == user_pid) { + ret_user = gspCtx->gsp_user_array + i; + break; + } + } + + if (ret_user == NULL) { + for (i = 0; i < GSP_MAX_USER; i ++) { + if ((gspCtx->gsp_user_array + i)->pid == INVALID_USER_ID) { + ret_user = gspCtx->gsp_user_array + i; + ret_user->pid = user_pid; + break; + } + } + } + + return ret_user; +} + + +static int32_t gsp_drv_open(struct inode *node, struct file *file) +{ + int32_t ret = GSP_NO_ERR; + gsp_user *pUserdata = NULL; + struct miscdevice *miscdev = NULL; + gsp_context_t *gspCtx = NULL; + GSP_TRACE("gsp_drv_open:pid:0x%08x enter.\n",current->pid); + + miscdev = file->private_data; + if (NULL == miscdev) { + return -EINVAL; + } + gspCtx = container_of(miscdev, gsp_context_t , dev); + if (NULL == gspCtx) { + return -EINVAL; + } + + pUserdata = (gsp_user *)gsp_get_user(current->pid, gspCtx); + + if (NULL == pUserdata) { + printk("gsp_drv_open:pid:0x%08x user cnt full.\n",current->pid); + ret = GSP_KERNEL_FULL; + goto exit; + } + GSP_TRACE("gsp_drv_open:pid:0x%08x bf wait open sema.\n",current->pid); + ret = down_interruptible(&pUserdata->sem_open); + if(!ret) { //ret==0, wait success + GSP_TRACE("gsp_drv_open:pid:0x%08x wait open sema success.\n",current->pid); + pUserdata->priv = (void*)gspCtx; + file->private_data = pUserdata; + } else { //ret == -EINTR + ret = GSP_KERNEL_OPEN_INTR; + printk("gsp_drv_open:pid:0x%08x wait open sema failed.\n",current->pid); + } + +exit: + return ret; +} + + +static int32_t gsp_drv_release(struct inode *node, struct file *file) +{ + gsp_user* pUserdata = file->private_data; + + GSP_TRACE("gsp_drv_release:pid:0x%08x.\n\n",current->pid); + if(pUserdata == NULL) { + printk("gsp_drv_release:error--pUserdata is null!, pid-0x%08x \n\n",current->pid); + return -ENODEV; + } + pUserdata->pid = INVALID_USER_ID; + sema_init(&pUserdata->sem_open, 1); + /* + //if caller thread hold gsp_hw_resource_sem,but was terminated,we release hw semaphore here + if(gsp_cur_client_pid == current->pid) + { + GSP_Wait_Finish();//wait busy-bit down + GSP_Deinit(); + //pUserdata->own_gsp_flag = 0; + gsp_cur_client_pid = INVALID_USER_ID; + sema_init(&gsp_wait_interrupt_sem,0); + GSP_TRACE("%s:pid:0x%08x, release gsp-hw sema, L%d \n",__func__,pUserdata->pid,__LINE__); + up(&gsp_hw_resource_sem); + } + */ + file->private_data = NULL; + + return GSP_NO_ERR; +} + + +ssize_t gsp_drv_write(struct file *file, const char __user * u_data, size_t cnt, loff_t *cnt_ret) +{ + gsp_user* pUserdata = file->private_data; + + if(pUserdata == NULL) { + printk("%s:error--pUserdata is null!, pid-0x%08x \n\n",__func__,current->pid); + return -ENODEV; + } + + GSP_TRACE("gsp_drv_write:pid:0x%08x.\n",current->pid); + + pUserdata->is_exit_force = 1; + /* + nomater the target thread "pUserdata->pid" wait on done-sema or hw resource sema, + send a signal to resume it and make it return from ioctl(),we does not up a sema to make target + thread can distinguish GSP done and signal interrupt. + */ + //send_sig(SIGABRT, (struct task_struct *)pUserdata->pid, 0); + + return 1; +} + +ssize_t gsp_drv_read(struct file *file, char __user *u_data, size_t cnt, loff_t *cnt_ret) +{ + char rt_word[32]= {0}; + gsp_user* pUserdata = file->private_data; + + if(pUserdata == NULL) { + printk("%s:error--pUserdata is null!, pid-0x%08x \n\n",__func__,current->pid); + return -ENODEV; + } + + *cnt_ret = 0; + *cnt_ret += sprintf(rt_word + *cnt_ret, "gsp read %zd\n",cnt); + return copy_to_user(u_data, (void*)rt_word, (ulong)*cnt_ret); +} + + +static void GSP_Coef_Tap_Convert(uint8_t h_tap,uint8_t v_tap, gsp_context_t *gspCtx) +{ + switch(h_tap) { + case 8: + gspCtx->gsp_cfg.layer0_info.row_tap_mode = 0; + break; + + case 6: + gspCtx->gsp_cfg.layer0_info.row_tap_mode = 1; + break; + + case 4: + gspCtx->gsp_cfg.layer0_info.row_tap_mode = 2; + break; + + case 2: + gspCtx->gsp_cfg.layer0_info.row_tap_mode = 3; + break; + + default: + gspCtx->gsp_cfg.layer0_info.row_tap_mode = 0; + break; + } + + switch(v_tap) { + case 8: + gspCtx->gsp_cfg.layer0_info.col_tap_mode = 0; + break; + + case 6: + gspCtx->gsp_cfg.layer0_info.col_tap_mode = 1; + break; + + case 4: + gspCtx->gsp_cfg.layer0_info.col_tap_mode = 2; + break; + + case 2: + gspCtx->gsp_cfg.layer0_info.col_tap_mode = 3; + break; + + default: + gspCtx->gsp_cfg.layer0_info.col_tap_mode = 0; + break; + } + gspCtx->gsp_cfg.layer0_info.row_tap_mode &= 0x3; + gspCtx->gsp_cfg.layer0_info.col_tap_mode &= 0x3; +} + + +static int32_t GSP_Scaling_Coef_Gen_And_Config(ulong* force_calc, gsp_context_t *gspCtx) +{ + uint8_t h_tap = 8; + uint8_t v_tap = 8; + uint32_t *tmp_buf = NULL; + uint32_t *h_coeff = NULL; + uint32_t *v_coeff = NULL; + uint32_t coef_factor_w = 0; + uint32_t coef_factor_h = 0; + uint32_t after_rotate_w = 0; + uint32_t after_rotate_h = 0; + uint32_t coef_in_w = 0; + uint32_t coef_in_h = 0; + uint32_t coef_out_w = 0; + uint32_t coef_out_h = 0; + static volatile uint32_t coef_in_w_last = 0;//if the new in w h out w h equal last params, don't need calc again + static volatile uint32_t coef_in_h_last = 0; + static volatile uint32_t coef_out_w_last = 0; + static volatile uint32_t coef_out_h_last = 0; + static volatile uint32_t coef_h_tap_last = 0; + static volatile uint32_t coef_v_tap_last = 0; + + if(gspCtx->gsp_cfg.layer0_info.scaling_en == 1) { + if(gspCtx->gsp_cfg.layer0_info.des_rect.rect_w < 4 + ||gspCtx->gsp_cfg.layer0_info.des_rect.rect_h < 4) { + return GSP_KERNEL_GEN_OUT_RANG; + } + + if(gspCtx->gsp_cfg.layer0_info.rot_angle == GSP_ROT_ANGLE_0 + ||gspCtx->gsp_cfg.layer0_info.rot_angle == GSP_ROT_ANGLE_180 + ||gspCtx->gsp_cfg.layer0_info.rot_angle == GSP_ROT_ANGLE_0_M + ||gspCtx->gsp_cfg.layer0_info.rot_angle == GSP_ROT_ANGLE_180_M) { + after_rotate_w = gspCtx->gsp_cfg.layer0_info.clip_rect.rect_w; + after_rotate_h = gspCtx->gsp_cfg.layer0_info.clip_rect.rect_h; + } else if(gspCtx->gsp_cfg.layer0_info.rot_angle == GSP_ROT_ANGLE_90 + ||gspCtx->gsp_cfg.layer0_info.rot_angle == GSP_ROT_ANGLE_270 + ||gspCtx->gsp_cfg.layer0_info.rot_angle == GSP_ROT_ANGLE_90_M + ||gspCtx->gsp_cfg.layer0_info.rot_angle == GSP_ROT_ANGLE_270_M) { + after_rotate_w = gspCtx->gsp_cfg.layer0_info.clip_rect.rect_h; + after_rotate_h = gspCtx->gsp_cfg.layer0_info.clip_rect.rect_w; + } + + coef_factor_w = CEIL(after_rotate_w,gspCtx->gsp_cfg.layer0_info.des_rect.rect_w); + coef_factor_h = CEIL(after_rotate_h,gspCtx->gsp_cfg.layer0_info.des_rect.rect_h); + + if(coef_factor_w > 16 || coef_factor_h > 16) { + return GSP_KERNEL_GEN_OUT_RANG; + } + + if(coef_factor_w > 8) { + coef_factor_w = 4; + } else if(coef_factor_w > 4) { + coef_factor_w = 2; + } else { + coef_factor_w = 1; + } + + if(coef_factor_h > 8) { + coef_factor_h = 4; + } else if(coef_factor_h > 4) { + coef_factor_h = 2; + } else { + coef_factor_h= 1; + } + + coef_in_w = CEIL(after_rotate_w,coef_factor_w); + coef_in_h = CEIL(after_rotate_h,coef_factor_h); + coef_out_w = gspCtx->gsp_cfg.layer0_info.des_rect.rect_w; + coef_out_h = gspCtx->gsp_cfg.layer0_info.des_rect.rect_h; + + if(GSP_SRC_FMT_RGB565 < gspCtx->gsp_cfg.layer0_info.img_format + && gspCtx->gsp_cfg.layer0_info.img_format < GSP_SRC_FMT_8BPP + && (coef_in_w>coef_out_w||coef_in_h>coef_out_h)) { //video scaling down + h_tap = 2; + v_tap = 2; + if(coef_in_w*3 <= coef_in_h*2) { // height is larger than 1.5*width + v_tap = 4; + } + if(coef_in_h*3 <= coef_in_w*2) { // width is larger than 1.5*height + h_tap = 4; + } + //GSP_TRACE("GSP, for video scaling down, we change tap to 2.\n"); + } + + //give hal a chance to set tap number + if((gspCtx->gsp_cfg.layer0_info.row_tap_mode>0) || (gspCtx->gsp_cfg.layer0_info.col_tap_mode>0)) { + //GSP_TRACE("GSP, hwc set tap: %dx%d-> ",h_tap,v_tap); + h_tap = (gspCtx->gsp_cfg.layer0_info.row_tap_mode>0)?gspCtx->gsp_cfg.layer0_info.row_tap_mode:h_tap; + v_tap = (gspCtx->gsp_cfg.layer0_info.col_tap_mode>0)?gspCtx->gsp_cfg.layer0_info.col_tap_mode:v_tap; + //GSP_TRACE("%dx%d\n",h_tap,v_tap); + } + + if(*force_calc == 1 + ||coef_in_w_last != coef_in_w + || coef_in_h_last != coef_in_h + || coef_out_w_last != coef_out_w + || coef_out_h_last != coef_out_h + || coef_h_tap_last != h_tap + || coef_v_tap_last != v_tap) { + tmp_buf = (uint32_t *)kmalloc(GSP_COEFF_BUF_SIZE, GFP_KERNEL); + if (NULL == tmp_buf) { + printk("SCALE DRV: No mem to alloc coeff buffer! \n"); + return GSP_KERNEL_GEN_ALLOC_ERR; + } + h_coeff = tmp_buf; + v_coeff = tmp_buf + (GSP_COEFF_COEF_SIZE/4); + + if (!(GSP_Gen_Block_Ccaler_Coef(coef_in_w, + coef_in_h, + coef_out_w, + coef_out_h, + h_tap, + v_tap, + h_coeff, + v_coeff, + tmp_buf + (GSP_COEFF_COEF_SIZE/2), + GSP_COEFF_POOL_SIZE, gspCtx))) { + kfree(tmp_buf); + printk("GSP DRV: GSP_Gen_Block_Ccaler_Coef error! \n"); + return GSP_KERNEL_GEN_COMMON_ERR; + } + GSP_Scale_Coef_Tab_Config(h_coeff,v_coeff);//write coef-metrix to register + coef_in_w_last = coef_in_w; + coef_in_h_last = coef_in_h; + coef_out_w_last = coef_out_w; + coef_out_h_last = coef_out_h; + coef_h_tap_last = h_tap; + coef_v_tap_last = v_tap; + *force_calc = 0; + } + + GSP_Coef_Tap_Convert(coef_h_tap_last,coef_v_tap_last, gspCtx); + GSP_L0_SCALETAPMODE_SET(gspCtx->gsp_cfg.layer0_info.row_tap_mode,gspCtx->gsp_cfg.layer0_info.col_tap_mode); + GSP_TRACE("GSP DRV: GSP_Gen_Block_Ccaler_Coef, register: r_tap%d,c_tap %d \n", + ((volatile GSP_REG_T*)GSP_REG_BASE)->gsp_layer0_cfg_u.mBits.row_tap_mod, + ((volatile GSP_REG_T*)GSP_REG_BASE)->gsp_layer0_cfg_u.mBits.col_tap_mod);// + kfree(tmp_buf); + } + return GSP_NO_ERR; +} + +/* +func:GSP_Info_Config +desc:config info to register +*/ +static uint32_t GSP_Info_Config(gsp_context_t *gspCtx) +{ + GSP_ConfigLayer(GSP_MODULE_LAYER0,gspCtx); + GSP_ConfigLayer(GSP_MODULE_LAYER1,gspCtx); + GSP_ConfigLayer(GSP_MODULE_ID_MAX,gspCtx); + GSP_ConfigLayer(GSP_MODULE_DST,gspCtx); + return GSP_ERRCODE_GET(); +} + +/* +func:GSP_Cache_Flush +desc: +*/ +static void GSP_Cache_Flush(void) +{ + +} + +/* +func:GSP_Cache_Invalidate +desc: +*/ +static void GSP_Cache_Invalidate(void) +{ + +} + +/* +func:GSP_Release_HWSema +desc: +*/ +static void GSP_Release_HWSema(gsp_context_t *gspCtx) +{ + gsp_user *pTempUserdata = NULL; + + GSP_TRACE("%s:pid:0x%08x, was killed without release GSP hw semaphore, L%d \n",__func__,gspCtx->gsp_cur_client_pid,__LINE__); + + pTempUserdata = (gsp_user *)gsp_get_user(gspCtx->gsp_cur_client_pid, gspCtx); + pTempUserdata->pid = INVALID_USER_ID; + sema_init(&pTempUserdata->sem_open, 1); + + GSP_Wait_Finish();//wait busy-bit down + GSP_Deinit(gspCtx); + gspCtx->gsp_cur_client_pid = INVALID_USER_ID; + sema_init(&gspCtx->gsp_wait_interrupt_sem,0); + up(&gspCtx->gsp_hw_resource_sem); +} + +static int GSP_Map(gsp_context_t *gspCtx) +{ + struct ion_addr_data data; + + if(gspCtx->gsp_cfg.layer0_info.src_addr.addr_y == 0 + &&gspCtx->gsp_cfg.layer0_info.mem_info.share_fd) { + data.fd_buffer = gspCtx->gsp_cfg.layer0_info.mem_info.share_fd; + if(sprd_ion_get_gsp_addr(&data)) { + printk("%s, L%d, error!\n",__func__,__LINE__); + return -1; + } + if(data.iova_enabled) + gspCtx->gsp_cfg.layer0_info.src_addr.addr_y = data.iova_addr; + else + gspCtx->gsp_cfg.layer0_info.src_addr.addr_y = data.phys_addr; + gspCtx->gsp_cfg.layer0_info.src_addr.addr_uv = gspCtx->gsp_cfg.layer0_info.src_addr.addr_y + gspCtx->gsp_cfg.layer0_info.mem_info.uv_offset; + gspCtx->gsp_cfg.layer0_info.src_addr.addr_v = gspCtx->gsp_cfg.layer0_info.src_addr.addr_y + gspCtx->gsp_cfg.layer0_info.mem_info.v_offset; + } + + if(gspCtx->gsp_cfg.layer1_info.src_addr.addr_y == 0 + &&gspCtx->gsp_cfg.layer1_info.mem_info.share_fd) { + data.fd_buffer = gspCtx->gsp_cfg.layer1_info.mem_info.share_fd; + if(sprd_ion_get_gsp_addr(&data)) { + printk("%s, L%d, error!\n",__func__,__LINE__); + return -1; + } + if(data.iova_enabled) + gspCtx->gsp_cfg.layer1_info.src_addr.addr_y = data.iova_addr; + else + gspCtx->gsp_cfg.layer1_info.src_addr.addr_y = data.phys_addr; + gspCtx->gsp_cfg.layer1_info.src_addr.addr_uv = gspCtx->gsp_cfg.layer1_info.src_addr.addr_y + gspCtx->gsp_cfg.layer1_info.mem_info.uv_offset; + gspCtx->gsp_cfg.layer1_info.src_addr.addr_v = gspCtx->gsp_cfg.layer1_info.src_addr.addr_y + gspCtx->gsp_cfg.layer1_info.mem_info.v_offset; + + } + + if(gspCtx->gsp_cfg.layer_des_info.src_addr.addr_y == 0 + &&gspCtx->gsp_cfg.layer_des_info.mem_info.share_fd) { + data.fd_buffer = gspCtx->gsp_cfg.layer_des_info.mem_info.share_fd; + if(sprd_ion_get_gsp_addr(&data)) { + printk("%s, L%d, error!\n",__func__,__LINE__); + return -1; + } + if(data.iova_enabled) + gspCtx->gsp_cfg.layer_des_info.src_addr.addr_y = data.iova_addr; + else + gspCtx->gsp_cfg.layer_des_info.src_addr.addr_y = data.phys_addr; + gspCtx->gsp_cfg.layer_des_info.src_addr.addr_uv = gspCtx->gsp_cfg.layer_des_info.src_addr.addr_y + gspCtx->gsp_cfg.layer_des_info.mem_info.uv_offset; + gspCtx->gsp_cfg.layer_des_info.src_addr.addr_v = gspCtx->gsp_cfg.layer_des_info.src_addr.addr_y + gspCtx->gsp_cfg.layer_des_info.mem_info.v_offset; + + } + + return 0; +} + +static int GSP_Unmap(gsp_context_t *gspCtx) +{ + if(gspCtx->gsp_cfg.layer0_info.mem_info.share_fd) + sprd_ion_free_gsp_addr(gspCtx->gsp_cfg.layer0_info.mem_info.share_fd); + + if(gspCtx->gsp_cfg.layer1_info.mem_info.share_fd) + sprd_ion_free_gsp_addr(gspCtx->gsp_cfg.layer1_info.mem_info.share_fd); + + if(gspCtx->gsp_cfg.layer_des_info.mem_info.share_fd) + sprd_ion_free_gsp_addr(gspCtx->gsp_cfg.layer_des_info.mem_info.share_fd); + + return 0; +} + +uint32_t __attribute__((weak)) sci_get_chip_id(void) +{ + printk("GSP local read chip id, *(%p) == %x \n",(void*)(SPRD_AONAPB_BASE+0xFC),GSP_REG_READ(SPRD_AONAPB_BASE+0xFC)); + return GSP_REG_READ(SPRD_AONAPB_BASE+0xFC); +} + +uint32_t gsp_get_chip_id(void) +{ + uint32_t adie_chip_id = sci_get_chip_id(); + if((adie_chip_id & 0xffff0000) < 0x50000000) { + printk("%s[%d]:warning, chip id 0x%08x is invalidate, try to get it by reading reg directly!\n", __func__, __LINE__ , adie_chip_id); + adie_chip_id = GSP_REG_READ(SPRD_AONAPB_BASE+0xFC); + if((adie_chip_id & 0xffff0000) < 0x50000000) { + printk("%s[%d]:warning, chip id 0x%08x from reg is invalidate too!\n", __func__, __LINE__ , adie_chip_id); + } + } + printk("%s[%d] return chip id 0x%08x \n", __func__, __LINE__, adie_chip_id); + return adie_chip_id; +} + +static GSP_ADDR_TYPE_E GSP_Get_Addr_Type(void) +{ + static volatile GSP_ADDR_TYPE_E s_gsp_addr_type = GSP_ADDR_TYPE_INVALUE; + static uint32_t s_iommuCtlBugChipList[]= {0x7715a000,0x7715a001,0x8815a000}; //bug chip list + //0x8730b000 tshark + //0x8300a001 shark + + +#ifndef CONFIG_SPRD_IOMMU // shark or (dolphin/tshark not define IOMMU) + s_gsp_addr_type = GSP_ADDR_TYPE_PHYSICAL; +#else // (dolphin/tshark defined IOMMU) + if(s_gsp_addr_type == GSP_ADDR_TYPE_INVALUE) { + uint32_t adie_chip_id = 0; + int i = 0; + /*set s_gsp_addr_type according to the chip id*/ + //adie_chip_id = sci_get_ana_chip_id(); + //printk("GSPa : get chip id :0x%08x \n", adie_chip_id); + adie_chip_id = gsp_get_chip_id(); + + if((adie_chip_id & 0xffff0000) > 0x50000000) { + printk("GSP : get chip id :%08x is validate, scan bugchip list.\n", adie_chip_id); + for (i=0; i<ARRAY_SIZE(s_iommuCtlBugChipList); i++) { + if(s_iommuCtlBugChipList[i] == adie_chip_id) { + printk("GSP : match bug chip id :%08x == [%d]\n", adie_chip_id,i); +#ifdef GSP_IOMMU_WORKAROUND1 + s_gsp_addr_type = GSP_ADDR_TYPE_IOVIRTUAL; +#else + s_gsp_addr_type = GSP_ADDR_TYPE_PHYSICAL; +#endif + break; + } + } + if(s_gsp_addr_type == GSP_ADDR_TYPE_INVALUE) { + printk("GSP : mismatch bug chip id.\n"); + s_gsp_addr_type = GSP_ADDR_TYPE_IOVIRTUAL; + printk("dolphin tshark GSP : gsp address type :%d \n", s_gsp_addr_type); + } + } else { + printk("GSP : get chip id :%08x is invalidate,set address type as physical.\n", adie_chip_id); + s_gsp_addr_type = GSP_ADDR_TYPE_PHYSICAL; + } + } +#endif + printk("GSP [%d]: gsp address type :%d ,\n", __LINE__, s_gsp_addr_type); + return s_gsp_addr_type; +} + + + + +static GSP_CAPABILITY_T* GSP_Config_Capability(void) +{ + uint32_t adie_chip_id = 0; + static GSP_CAPABILITY_T s_gsp_capability; + + if(s_gsp_capability.magic != CAPABILITY_MAGIC_NUMBER) { // not initialized + memset((void*)&s_gsp_capability,0,sizeof(s_gsp_capability)); + s_gsp_capability.max_layer_cnt = 1; + s_gsp_capability.blend_video_with_OSD=0; + s_gsp_capability.max_videoLayer_cnt = 1; + s_gsp_capability.max_layer_cnt_with_video = 1; + s_gsp_capability.scale_range_up=64; + s_gsp_capability.scale_range_down=1; + s_gsp_capability.scale_updown_sametime=0; + s_gsp_capability.OSD_scaling=0; + + adie_chip_id = gsp_get_chip_id(); + switch(adie_chip_id&0xFFFF0000) { + case 0x83000000:/*shark-0x8300a001 & 9620*/ + s_gsp_capability.version = 0x00; + s_gsp_capability.scale_range_up=256; + break; + case 0x77150000: + case 0x88150000: + if(adie_chip_id == 0x7715a000 + ||adie_chip_id == 0x7715a001 + ||adie_chip_id == 0x8815a000) { /*dolphin iommu ctl reg access err*/ + s_gsp_capability.version = 0x01; + s_gsp_capability.video_need_copy = 1; + s_gsp_capability.max_video_size = 1; + } else if(adie_chip_id == 0x7715a002 + ||adie_chip_id == 0x7715a003 + ||adie_chip_id == 0x8815a001 + ||adie_chip_id == 0x8815a002) { /*dolphin iommu ctl reg access ok, but with black line bug*/ + s_gsp_capability.version = 0x02; + s_gsp_capability.video_need_copy = 1; + s_gsp_capability.max_video_size = 1; + } else { /*adie_chip_id > 0x7715a003 || adie_chip_id > 0x8815a002, dolphin black line bug fixed*/ + s_gsp_capability.version = 0x03; + s_gsp_capability.max_video_size = 1; + s_gsp_capability.scale_range_up=256; + printk("%s[%d]: info:a new chip id, treated as newest dolphin that without any bugs!\n",__func__,__LINE__); + } + break; + case 0x87300000: + if(adie_chip_id == 0x8730b000) { /*tshark, with black line bug*/ + s_gsp_capability.version = 0x04; + s_gsp_capability.video_need_copy = 1; + } else { /*tshark-0x8730b001 & tshark2-? & pike-?, black line bug fixed*/ + s_gsp_capability.version = 0x05; + s_gsp_capability.max_layer_cnt = 2; + s_gsp_capability.scale_range_up=256; + } + break; + #ifdef CONFIG_ARCH_SCX20 + case 0x88600000:/*pike, same with tshark*/ + s_gsp_capability.version = 0x05; + s_gsp_capability.max_layer_cnt = 2; + s_gsp_capability.scale_range_up=256; + break; + #endif + default: + if(adie_chip_id != 0x96300000/*SharkL, with YCbCr->RGB888*/ + && adie_chip_id != 0x96310000/*SharkL64*/) { + /* + pikeL/sharkLT8 ... + after sharkL, gsp will not update any more,so these late-comers are same with sharkL. + */ + printk("%s[%d]: info:a new chip id, be treated as sharkL!\n",__func__,__LINE__); + } + s_gsp_capability.version = 0x06; + s_gsp_capability.blend_video_with_OSD=1; + s_gsp_capability.max_layer_cnt_with_video = 3; + s_gsp_capability.max_layer_cnt = 2; + s_gsp_capability.scale_range_up=256; + break; + } + s_gsp_capability.buf_type_support=GSP_Get_Addr_Type(); + s_gsp_capability.yuv_xywh_even = 1; + s_gsp_capability.crop_min.w=s_gsp_capability.crop_min.h=4; + s_gsp_capability.out_min.w=s_gsp_capability.out_min.h=4; + s_gsp_capability.crop_max.w=s_gsp_capability.crop_max.h=4095; + s_gsp_capability.out_max.w=s_gsp_capability.out_max.h=4095; + s_gsp_capability.magic = CAPABILITY_MAGIC_NUMBER; + } + return &s_gsp_capability; +} +static long gsp_drv_ioctl(struct file *file, + uint32_t cmd, + unsigned long arg) +{ + int32_t ret = -GSP_NO_ERR; + size_t param_size = _IOC_SIZE(cmd); + gsp_user* pUserdata = file->private_data; + gsp_context_t *gspCtx = NULL; + struct timespec start_time; + struct timespec end_time; + //long long cost=0; + memset(&start_time,0,sizeof(start_time)); + memset(&end_time,0,sizeof(end_time)); + + GSP_TRACE("%s:pid:0x%08x,cmd:0x%08x, io number 0x%x, param_size %zu \n", + __func__, + pUserdata->pid, + cmd, + _IOC_NR(cmd), + param_size); + + if (NULL == pUserdata || NULL == pUserdata->priv) { + return -EINVAL; + } + gspCtx = (gsp_context_t*)pUserdata->priv; + if(gspCtx->suspend_resume_flag==1) { + printk("%s[%d]: in suspend, ioctl just return!\n",__func__,__LINE__); + return ret; + } + + switch (cmd) { + case GSP_IO_GET_CAPABILITY: + if (param_size>=sizeof(GSP_CAPABILITY_T)) { + GSP_CAPABILITY_T *cap=GSP_Config_Capability(); + if(arg & MEM_OPS_ADDR_ALIGN_MASK || (ulong)cap & MEM_OPS_ADDR_ALIGN_MASK) { + GSP_TRACE("%s[%d] copy_to_user use none 8B alignment address!",__func__,__LINE__); + } + ret=copy_to_user((void __user *)arg,(const void*)cap,sizeof(GSP_CAPABILITY_T)); + if(ret) { + printk("%s[%d] err:get gsp capability failed in copy_to_user !\n",__func__,__LINE__); + ret = GSP_KERNEL_COPY_ERR; + goto exit; + } + GSP_TRACE("%s[%d]: get gsp capability success in copy_to_user \n",__func__,__LINE__); + } else { + printk("%s[%d] err:get gsp capability, buffer is too small,come:%zu,need:%zu!",__func__, __LINE__, + param_size,sizeof(GSP_CAPABILITY_T)); + ret = GSP_KERNEL_COPY_ERR; + goto exit; + } + break; + + case GSP_IO_SET_PARAM: { + if (param_size) { + GSP_TRACE("%s:pid:0x%08x, bf wait gsp-hw sema, L%d \n",__func__,pUserdata->pid,__LINE__); + + // the caller thread was killed without release GSP hw semaphore + if( gspCtx->gsp_cur_client_pid != INVALID_USER_ID) { + struct pid * __pid = NULL; + struct task_struct *__task = NULL; + pid_t temp_pid = INVALID_USER_ID; + + GSP_TRACE("%sL%d current:%08x store_pid:0x%08x, \n",__func__,__LINE__,current->pid, gspCtx->gsp_cur_client_pid); + //barrier(); + temp_pid = gspCtx->gsp_cur_client_pid; + __pid = find_get_pid(temp_pid); + if(__pid != NULL) { + __task = get_pid_task(__pid,PIDTYPE_PID); + //barrier(); + + if(__task != NULL) { + if(__task->pid != gspCtx->gsp_cur_client_pid) { + GSP_Release_HWSema(gspCtx); + } + } else { + GSP_Release_HWSema(gspCtx); + } + } else { + GSP_Release_HWSema(gspCtx); + } + //barrier(); + } + + + ret = down_interruptible(&gspCtx->gsp_hw_resource_sem); + if(ret) { + printk("%s:pid:0x%08x, wait gsp-hw sema interrupt by signal,return, L%d \n",__func__,pUserdata->pid,__LINE__); + //receive a signal + ret = GSP_KERNEL_CFG_INTR; + goto exit; + } + GSP_TRACE("%s:pid:0x%08x, wait gsp-hw sema success, L%d \n",__func__,pUserdata->pid,__LINE__); + gspCtx->gsp_cur_client_pid = pUserdata->pid; + if(arg & MEM_OPS_ADDR_ALIGN_MASK || (ulong)&gspCtx->gsp_cfg & MEM_OPS_ADDR_ALIGN_MASK) { + GSP_TRACE("%s[%d] copy_from_user use none 8B alignment address!",__func__,__LINE__); + } + ret=copy_from_user((void*)&gspCtx->gsp_cfg, (void*)arg, param_size); + if(ret) { + printk("%s:pid:0x%08x, copy_params_from_user failed! \n",__func__,pUserdata->pid); + ret = GSP_KERNEL_COPY_ERR; + goto exit1; + } else { + GSP_TRACE("%s:pid:0x%08x, copy_params_from_user success!, L%d \n",__func__,pUserdata->pid,__LINE__); + ret = GSP_Init(gspCtx); + if(ret) { + goto exit1; + } + // if the y u v address is virtual, should be converted to phy address here!!! + if(gspCtx->gsp_cfg.layer0_info.layer_en == 1) { + if(gspCtx->gsp_cfg.layer0_info.rot_angle & 0x1) { //90 270 + if((gspCtx->gsp_cfg.layer0_info.clip_rect.rect_w != gspCtx->gsp_cfg.layer0_info.des_rect.rect_h) || + (gspCtx->gsp_cfg.layer0_info.clip_rect.rect_h != gspCtx->gsp_cfg.layer0_info.des_rect.rect_w)) { + gspCtx->gsp_cfg.layer0_info.scaling_en = 1; + } + } else { // 0 + if((gspCtx->gsp_cfg.layer0_info.clip_rect.rect_w != gspCtx->gsp_cfg.layer0_info.des_rect.rect_w) || + (gspCtx->gsp_cfg.layer0_info.clip_rect.rect_h != gspCtx->gsp_cfg.layer0_info.des_rect.rect_h)) { + gspCtx->gsp_cfg.layer0_info.scaling_en = 1; + } + } + } + + if(GSP_Map(gspCtx)) { + ret = GSP_KERNEL_ADDR_MAP_ERR; + goto exit3; + } + + gspCtx->gsp_cfg.misc_info.gsp_clock = GSP_CLOCK; + if(GSP_GAP & 0x100) { + gspCtx->gsp_cfg.misc_info.gsp_gap = (GSP_GAP & 0xff); + } + ret = GSP_Info_Config(gspCtx); + GSP_TRACE("%s:pid:0x%08x, config hw %s!, L%d \n",__func__,pUserdata->pid,(ret>0)?"failed":"success",__LINE__); + if(ret) { + printk("%s%d:pid:0x%08x, gsp config err:%d, release hw sema.\n",__func__,__LINE__,pUserdata->pid,ret); + goto exit3; + } + } + } + + GSP_TRACE("%s:pid:0x%08x, in trigger to run , L%d \n",__func__,pUserdata->pid,__LINE__); + if(gspCtx->gsp_cur_client_pid == pUserdata->pid) { + GSP_TRACE("%s:pid:0x%08x, calc coef and trigger to run , L%d \n",__func__,pUserdata->pid,__LINE__); + GSP_Cache_Flush(); + ret = GSP_Scaling_Coef_Gen_And_Config(&gspCtx->gsp_coef_force_calc, gspCtx); + if(ret) { + goto exit3; + } + + ret = GSP_Trigger(); + GSP_TRACE("%sL%d:pid:0x%08x, trigger %s!\n",__func__,__LINE__,pUserdata->pid,(ret)?"failed":"success"); + if(ret) { + printk("%s%d:pid:0x%08x, trigger failed!! err_code:%d \n",__func__,__LINE__,pUserdata->pid,ret); + printCfgInfo(gspCtx); + printGPSReg(); + ERR_RECORD_ADD(*(GSP_REG_T *)GSP_REG_BASE); + ERR_RECORD_INDEX_ADD_WP(); + if (ERR_RECORD_FULL()) { + ERR_RECORD_INDEX_ADD_RP(); + } + GSP_TRACE("%s:pid:0x%08x, release hw sema, L%d \n",__func__,pUserdata->pid,__LINE__); + goto exit3; + } + } else { + GSP_TRACE("%s:pid:0x%08x,exit L%d \n",__func__,pUserdata->pid,__LINE__); + ret = GSP_KERNEL_CALLER_NOT_OWN_HW; + goto exit3; + } + + if(gspCtx->gsp_cur_client_pid == pUserdata->pid) { + GSP_TRACE("%s:pid:0x%08x, bf wait done sema, L%d \n",__func__,pUserdata->pid,__LINE__); + //ret = down_interruptible(&gsp_wait_interrupt_sem);//interrupt lose + ret = down_timeout(&gspCtx->gsp_wait_interrupt_sem,msecs_to_jiffies(500));//for interrupt lose, timeout return -ETIME, + if (ret == 0) { //gsp process over + GSP_TRACE("%s:pid:0x%08x, wait done sema success, L%d \n",__func__,pUserdata->pid,__LINE__); + } else if (ret == -ETIME) { + printk("%s%d:pid:0x%08x, wait done sema 500-ms-timeout,it's abnormal!!!!!!!! \n",__func__,__LINE__,pUserdata->pid); + GPSTimeoutPrint(gspCtx); + ret = GSP_KERNEL_WAITDONE_TIMEOUT; + } else if (ret) { // == -EINTR + printk("%s:pid:0x%08x, wait done sema interrupted by a signal, L%d \n",__func__,pUserdata->pid,__LINE__); + ret = GSP_KERNEL_WAITDONE_INTR; + } + + if (pUserdata->is_exit_force) { + pUserdata->is_exit_force = 0; + ret = GSP_KERNEL_FORCE_EXIT; + } + + GSP_Wait_Finish();//wait busy-bit down + GSP_Unmap(gspCtx); + GSP_Cache_Invalidate(); + GSP_Deinit(gspCtx); + gspCtx->gsp_cur_client_pid = INVALID_USER_ID; + sema_init(&gspCtx->gsp_wait_interrupt_sem,0); + GSP_TRACE("%s:pid:0x%08x, release gsp-hw sema, L%d \n",__func__,pUserdata->pid,__LINE__); + up(&gspCtx->gsp_hw_resource_sem); + } + } + break; + + default: + ret = GSP_KERNEL_CTL_CMD_ERR; + break; + } + + return ret; + +exit3: + GSP_Deinit(gspCtx); +//exit2: + GSP_Unmap(gspCtx); +exit1: + gspCtx->gsp_cur_client_pid = INVALID_USER_ID; + up(&gspCtx->gsp_hw_resource_sem); +exit: + if (ret) { + printk("%s:pid:0x%08x, error code 0x%x \n", __func__,pUserdata->pid,ret); + } + return ret; + +} + +static struct file_operations gsp_drv_fops = { + .owner = THIS_MODULE, + .open = gsp_drv_open, + .write = gsp_drv_write, + .read = gsp_drv_read, + .unlocked_ioctl = gsp_drv_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = gsp_drv_ioctl, +#endif + .release = gsp_drv_release +}; + +static irqreturn_t gsp_irq_handler(int32_t irq, void *dev_id) +{ + gsp_context_t *gspCtx = (gsp_context_t *)dev_id; + + GSP_TRACE("%s enter!\n",__func__); + + if (NULL == gspCtx) { + return IRQ_NONE; + } + GSP_IRQSTATUS_CLEAR(); + GSP_IRQENABLE_SET(GSP_IRQ_TYPE_DISABLE); + up(&gspCtx->gsp_wait_interrupt_sem); + + return IRQ_HANDLED; +} + + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void gsp_early_suspend(struct early_suspend* es) +{ + gsp_context_t *gspCtx = NULL; + int32_t ret = -GSP_NO_ERR; + + printk("%s%d\n",__func__,__LINE__); + + gspCtx = container_of(es, gsp_context_t, earlysuspend); + if (NULL == gspCtx) { + return; + } + + gspCtx->suspend_resume_flag = 1; + + //in case of GSP is processing now, wait it finish and then disable + ret = down_timeout(&gspCtx->gsp_hw_resource_sem,msecs_to_jiffies(500)); + if(ret) { + printk("%s[%d]: wait gsp-hw sema failed, ret: %d\n",__func__,__LINE__,ret); + GPSTimeoutPrint(gspCtx); + } else { + printk("%s[%d]: wait gsp-hw sema success. \n",__func__,__LINE__); + up(&gspCtx->gsp_hw_resource_sem); + //GSP_module_disable(gspCtx); + } +} + +static void gsp_late_resume(struct early_suspend* es) +{ + gsp_context_t *gspCtx = NULL; + + printk("%s%d\n",__func__,__LINE__); + + gspCtx = container_of(es, gsp_context_t, earlysuspend); + if (NULL == gspCtx) { + return; + } + + gspCtx->gsp_coef_force_calc = 1; + //GSP_module_enable(gspCtx);// + + GSP_AUTO_GATE_ENABLE();//bug 198152 + gspCtx->suspend_resume_flag = 0; +} + +#else +static int gsp_suspend(struct platform_device *pdev,pm_message_t state) +{ + gsp_context_t *gspCtx = NULL; + int32_t ret = -GSP_NO_ERR; + printk("%s%d\n",__func__,__LINE__); + + gspCtx = platform_get_drvdata(pdev); + if (NULL == gspCtx) { + return -EINVAL; + } + + gspCtx->suspend_resume_flag = 1; + + //in case of GSP is processing now, wait it finish and then disable + ret = down_timeout(&gspCtx->gsp_hw_resource_sem,msecs_to_jiffies(500)); + if(ret) { + printk("%s[%d]: wait gsp-hw sema failed, ret: %d\n",__func__,__LINE__,ret); + GPSTimeoutPrint(gspCtx); + return ret; + } else { + printk("%s[%d]: wait gsp-hw sema success. \n",__func__,__LINE__); + up(&gspCtx->gsp_hw_resource_sem); + //GSP_module_disable(gspCtx); + } + return 0; +} + +static int gsp_resume(struct platform_device *pdev) +{ + gsp_context_t *gspCtx = NULL; + + gspCtx = platform_get_drvdata(pdev); + if (NULL == gspCtx) { + return -EINVAL; + } + + printk("%s%d\n",__func__,__LINE__); + gspCtx->gsp_coef_force_calc = 1; + //GSP_module_enable(gspCtx);// + + GSP_AUTO_GATE_ENABLE();//bug 198152 + gspCtx->suspend_resume_flag = 0; + return 0; +} + +#endif + + +static int32_t gsp_clock_init(gsp_context_t *gspCtx) +{ + struct clk *emc_clk_parent = NULL; + struct clk *gsp_clk_parent = NULL; + int ret = 0; + +#ifdef CONFIG_OF + emc_clk_parent = of_clk_get_by_name(s_gsp_of_dev->of_node, GSP_EMC_CLOCK_PARENT_NAME); +#else + emc_clk_parent = clk_get(NULL, GSP_EMC_CLOCK_PARENT_NAME); +#endif + if (IS_ERR(emc_clk_parent)) { + printk(KERN_ERR "gsp: get emc clk_parent failed!\n"); + return -1; + } else { + printk(KERN_INFO "gsp: get emc clk_parent ok!\n");//pr_debug + } + +#ifdef CONFIG_OF + gspCtx->gsp_emc_clk = of_clk_get_by_name(s_gsp_of_dev->of_node, GSP_EMC_CLOCK_NAME); +#else + gspCtx->gsp_emc_clk = clk_get(NULL, GSP_EMC_CLOCK_NAME); +#endif + if (IS_ERR(gspCtx->gsp_emc_clk)) { + printk(KERN_ERR "gsp: get emc clk failed!\n"); + return -1; + } else { + printk(KERN_INFO "gsp: get emc clk ok!\n");//pr_debug + } + + ret = clk_set_parent(gspCtx->gsp_emc_clk, emc_clk_parent); + if(ret) { + printk(KERN_ERR "gsp: gsp set emc clk parent failed!\n"); + return -1; + } else { + printk(KERN_INFO "gsp: gsp set emc clk parent ok!\n");//pr_debug + } + +#ifdef CONFIG_OF + gsp_clk_parent = of_clk_get_by_name(s_gsp_of_dev->of_node, GSP_CLOCK_PARENT3); +#else + gsp_clk_parent = clk_get(NULL, GSP_CLOCK_PARENT3); +#endif + if (IS_ERR(gsp_clk_parent)) { + printk(KERN_ERR "gsp: get clk_parent failed!\n"); + return -1; + } else { + printk(KERN_INFO "gsp: get clk_parent ok!\n"); + } + +#ifdef CONFIG_OF + gspCtx->gsp_clk = of_clk_get_by_name(s_gsp_of_dev->of_node, GSP_CLOCK_NAME); +#else + gspCtx->gsp_clk = clk_get(NULL, GSP_CLOCK_NAME); +#endif + if (IS_ERR(gspCtx->gsp_clk)) { + printk(KERN_ERR "gsp: get clk failed!\n"); + return -1; + } else { + printk(KERN_INFO "gsp: get clk ok!\n"); + } + + ret = clk_set_parent(gspCtx->gsp_clk, gsp_clk_parent); + if(ret) { + printk(KERN_ERR "gsp: gsp set clk parent failed!\n"); + return -1; + } else { + printk(KERN_INFO "gsp: gsp set clk parent ok!\n"); + } + return ret; +} + + +int32_t gsp_drv_probe(struct platform_device *pdev) +{ + int32_t ret = 0; + int32_t i = 0; + gsp_context_t *gspCtx; + +#ifdef CONFIG_OF + struct resource r; +#endif + + gspCtx =kzalloc(sizeof(gsp_context_t), GFP_KERNEL); + + if (NULL == gspCtx) { + dev_err(&pdev->dev, "Can't alloc memory for module data.\n"); + ret = -ENOMEM; + } + + GSP_TRACE("gsp_probe enter .\n"); + printk("%s,AHB clock :%d\n", __func__,GSP_AHB_CLOCK_GET()); + +#ifdef CONFIG_OF + s_gsp_of_dev = &(pdev->dev); + + gspCtx->gsp_irq_num = irq_of_parse_and_map(s_gsp_of_dev->of_node, 0); + + if(0 != of_address_to_resource(s_gsp_of_dev->of_node, 0, &r)) { + printk(KERN_ERR "gsp probe fail. (can't get register base address)\n"); + goto exit; + } + g_gsp_base_addr = (unsigned long)ioremap_nocache(r.start, resource_size(&r)); + if(!g_gsp_base_addr) + BUG(); + +#ifndef GSP_IOMMU_WORKAROUND1 +#if defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L) + ret = of_property_read_u32(s_gsp_of_dev->of_node, "gsp_mmu_ctrl_base", (u32*)&g_gsp_mmu_ctrl_addr); + + if(0 != ret) { + printk("%s: read gsp_mmu_ctrl_addr fail (%d)\n", __func__, ret); + return ret; + } + g_gsp_mmu_ctrl_addr = (ulong)ioremap_nocache(g_gsp_mmu_ctrl_addr,sizeof(uint32_t)); + if(!g_gsp_mmu_ctrl_addr) + BUG(); +#endif +#endif + + printk("gsp: irq = %d, g_gsp_base_addr = 0x%lx,\n", gspCtx->gsp_irq_num, g_gsp_base_addr); +#else + gspCtx->gsp_irq_num = TB_GSP_INT; +#endif + + GSP_AUTO_GATE_ENABLE(); + ret = gsp_clock_init(gspCtx); + if (ret) { + printk(KERN_ERR "gsp emc clock init failed. \n"); + goto exit; + } + //GSP_module_enable(gspCtx); + + gspCtx->dev.minor = MISC_DYNAMIC_MINOR; + gspCtx->dev.name = "sprd_gsp"; + gspCtx->dev.fops = &gsp_drv_fops; + ret = misc_register(&gspCtx->dev); + if (ret) { + printk(KERN_ERR "gsp cannot register miscdev (%d)\n", ret); + goto exit; + } + + ret = request_irq(gspCtx->gsp_irq_num,// + gsp_irq_handler, + 0,//IRQF_SHARED + "GSP", + gspCtx); + + if (ret) { + printk("could not request irq %d\n", gspCtx->gsp_irq_num); + goto exit1; + } + + gspCtx->gsp_cur_client_pid = INVALID_USER_ID; + for (i=0; i<GSP_MAX_USER; i++) { + gspCtx->gsp_user_array[i].pid = INVALID_USER_ID; + gspCtx->gsp_user_array[i].is_exit_force = 0; + sema_init(&(gspCtx->gsp_user_array[i].sem_open), 1); + } + + /* initialize locks*/ + memset(&gspCtx->gsp_cfg,0,sizeof(gspCtx->gsp_cfg)); + sema_init(&gspCtx->gsp_hw_resource_sem, 1); + sema_init(&gspCtx->gsp_wait_interrupt_sem, 0); + /*initialize gsp_perf*/ + + gspCtx->cache_coef_init_flag = 0; + +#ifdef CONFIG_HAS_EARLYSUSPEND + memset(& gspCtx->earlysuspend,0,sizeof( gspCtx->earlysuspend)); + gspCtx->earlysuspend.suspend = gsp_early_suspend; + gspCtx->earlysuspend.resume = gsp_late_resume; + gspCtx->earlysuspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING; + register_early_suspend(&gspCtx->earlysuspend); +#endif + + platform_set_drvdata(pdev, gspCtx); + g_gspCtx = gspCtx; + return ret; + +exit1: + misc_deregister(&gspCtx->dev); +exit: + return ret; +} + +static int32_t gsp_drv_remove(struct platform_device *dev) +{ + gsp_context_t *gspCtx; + GSP_TRACE( "gsp_remove called !\n"); + + gspCtx = platform_get_drvdata(dev); + if (NULL == gspCtx) { + return -EINVAL; + } + free_irq(gspCtx->gsp_irq_num, gsp_irq_handler); + misc_deregister(&gspCtx->dev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sprdgsp_dt_ids[] = { + { .compatible = "sprd,gsp", }, + {} +}; +#endif + +static struct platform_driver gsp_driver = { + .probe = gsp_drv_probe, + .remove = gsp_drv_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = gsp_suspend, + .resume = gsp_resume, +#endif + .driver = + { + .owner = THIS_MODULE, + .name = "sprd_gsp", +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(sprdgsp_dt_ids), +#endif + } +}; + +int32_t __init gsp_drv_init(void) +{ + printk("gsp_drv_init enter! \n"); + + if (platform_driver_register(&gsp_driver) != 0) { + printk("gsp platform driver register Failed! \n"); + return -1; + } else { + GSP_TRACE("gsp platform driver registered successful! \n"); + } + return 0; +} + + +void gsp_drv_exit(void) +{ + platform_driver_unregister(&gsp_driver); + GSP_TRACE("gsp platform driver unregistered! \n"); +} + +module_init(gsp_drv_init); +module_exit(gsp_drv_exit); + +MODULE_DESCRIPTION("GSP Driver"); +MODULE_LICENSE("GPL"); + |
