/* * g2d_rcq/g2d_driver/g2d.c * * Copyright (c) 2007-2019 Allwinnertech Co., Ltd. * Author: zhengxiaobin * * * 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 "g2d_driver_i.h" #include "g2d_top.h" #if defined(CONFIG_SUNXI_G2D_MIXER) #include "g2d_mixer.h" #endif #if defined(CONFIG_SUNXI_G2D_ROTATE) #include "g2d_rotate.h" #endif /* alloc based on 4K byte */ #define G2D_BYTE_ALIGN(x) (((x + (4*1024-1)) >> 12) << 12) static enum g2d_scan_order scan_order; static struct mutex global_lock; static struct class *g2d_class; static struct cdev *g2d_cdev; static dev_t devid; static struct device *g2d_dev; static struct device *dmabuf_dev; u32 g_time_info; u32 g_func_runtime; u32 g2d_timeout_flag; unsigned int __iomem *ccmu; uint32_t data_ccmu; __g2d_drv_t g2d_ext_hd; __g2d_info_t para; __u32 dbg_info; #if ((defined CONFIG_ARCH_SUN8IW21P1) || (defined CONFIG_ARCH_SUN20IW2P1) \ || (defined CONFIG_ARCH_SUN20IW3P1)) void g2d_reset(void) { data_ccmu = readl(ccmu); writel(data_ccmu & (~(1 << 16)), ccmu); udelay(1); writel(data_ccmu, ccmu); g2d_bsp_open(); } #endif static struct g2d_format_attr fmt_attr_tbl[] = { /* format bits hor_rsample(u,v) ver_rsample(u,v) uvc interleave factor div */ { G2D_FORMAT_ARGB8888, 8, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_ABGR8888, 8, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_RGBA8888, 8, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_BGRA8888, 8, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_XRGB8888, 8, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_XBGR8888, 8, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_RGBX8888, 8, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_BGRX8888, 8, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_RGB888, 8, 1, 1, 1, 1, 0, 1, 3, 1}, { G2D_FORMAT_BGR888, 8, 1, 1, 1, 1, 0, 1, 3, 1}, { G2D_FORMAT_RGB565, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_BGR565, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_ARGB4444, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_ABGR4444, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_RGBA4444, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_BGRA4444, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_ARGB1555, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_ABGR1555, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_RGBA5551, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_BGRA5551, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_ARGB2101010, 10, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_ABGR2101010, 10, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_RGBA1010102, 10, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_BGRA1010102, 10, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_IYUV422_V0Y1U0Y0, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_IYUV422_Y1V0Y0U0, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_IYUV422_U0Y1V0Y0, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_IYUV422_Y1U0Y0V0, 8, 1, 1, 1, 1, 0, 1, 2, 1}, { G2D_FORMAT_YUV422_PLANAR, 8, 2, 2, 1, 1, 0, 0, 2, 1}, { G2D_FORMAT_YUV420_PLANAR, 8, 2, 2, 2, 2, 0, 0, 3, 2}, { G2D_FORMAT_YUV411_PLANAR, 8, 4, 4, 1, 1, 0, 0, 3, 2}, { G2D_FORMAT_YUV422UVC_U1V1U0V0, 8, 2, 2, 1, 1, 1, 0, 2, 1}, { G2D_FORMAT_YUV422UVC_V1U1V0U0, 8, 2, 2, 1, 1, 1, 0, 2, 1}, { G2D_FORMAT_YUV420UVC_U1V1U0V0, 8, 2, 2, 2, 2, 1, 0, 3, 2}, { G2D_FORMAT_YUV420UVC_V1U1V0U0, 8, 2, 2, 2, 2, 1, 0, 3, 2}, { G2D_FORMAT_YUV411UVC_U1V1U0V0, 8, 4, 4, 1, 1, 1, 0, 3, 2}, { G2D_FORMAT_YUV411UVC_V1U1V0U0, 8, 4, 4, 1, 1, 1, 0, 3, 2}, { G2D_FORMAT_Y8, 8, 1, 1, 1, 1, 0, 0, 1, 1}, { G2D_FORMAT_YVU10_444, 10, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_YUV10_444, 10, 1, 1, 1, 1, 0, 1, 4, 1}, { G2D_FORMAT_YVU10_P210, 10, 2, 2, 1, 1, 0, 0, 4, 1}, { G2D_FORMAT_YVU10_P010, 10, 2, 2, 2, 2, 0, 0, 3, 1}, }; void *g2d_malloc(__u32 bytes_num, __u32 *phy_addr) { void *address = NULL; #if defined(CONFIG_ION_SUNXI) u32 actual_bytes; if (bytes_num != 0) { actual_bytes = G2D_BYTE_ALIGN(bytes_num); address = dma_alloc_coherent(para.dev, actual_bytes, (dma_addr_t *) phy_addr, GFP_KERNEL); if (address) { return address; } G2D_ERR_MSG("dma_alloc_coherent fail, size=0x%x\n", bytes_num); return NULL; } G2D_ERR_MSG("size is zero\n"); #else unsigned map_size = 0; struct page *page; if (bytes_num != 0) { map_size = PAGE_ALIGN(bytes_num); page = alloc_pages(GFP_KERNEL, get_order(map_size)); if (page != NULL) { address = page_address(page); if (address == NULL) { free_pages((unsigned long)(page), get_order(map_size)); G2D_ERR_MSG("page_address fail!\n"); return NULL; } *phy_addr = virt_to_phys(address); return address; } G2D_ERR_MSG("alloc_pages fail!\n"); return NULL; } G2D_ERR_MSG("size is zero\n"); #endif return NULL; } void g2d_free(void *virt_addr, void *phy_addr, unsigned int size) { #if defined(CONFIG_ION_SUNXI) u32 actual_bytes; actual_bytes = PAGE_ALIGN(size); if (phy_addr && virt_addr) dma_free_coherent(para.dev, actual_bytes, virt_addr, (dma_addr_t) phy_addr); #else unsigned map_size = PAGE_ALIGN(size); unsigned page_size = map_size; if (virt_addr == NULL) return; free_pages((unsigned long)virt_addr, get_order(page_size)); #endif } int g2d_dma_map(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) { G2D_ERR_MSG("dma_buf_id(%d) is invalid\n", fd); goto exit; } dmabuf = dma_buf_get(fd); if (IS_ERR(dmabuf)) { G2D_ERR_MSG("dma_buf_get failed, fd=%d\n", fd); goto exit; } attachment = dma_buf_attach(dmabuf, dmabuf_dev); if (IS_ERR(attachment)) { G2D_ERR_MSG("dma_buf_attach failed\n"); goto err_buf_put; } sgt = dma_buf_map_attachment(attachment, DMA_FROM_DEVICE); if (IS_ERR_OR_NULL(sgt)) { G2D_ERR_MSG("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) { G2D_ERR_MSG("alloc sgt fail\n"); goto err_buf_unmap; } ret = sg_alloc_table(sgt_bak, sgt->nents, GFP_KERNEL); if (ret != 0) { G2D_ERR_MSG("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(dmabuf_dev, sgt_bak->sgl, sgt_bak->nents, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); if (sg_count != 1) { G2D_ERR_MSG("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 g2d_dma_unmap(struct dmabuf_item *item) { dma_unmap_sg_attrs(dmabuf_dev, 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); } __s32 g2d_set_info(g2d_image_enh *g2d_img, struct dmabuf_item *item) { __s32 ret = -1; __u32 i = 0; __u32 len = ARRAY_SIZE(fmt_attr_tbl); __u32 y_width, y_height, u_width, u_height; __u32 y_pitch, u_pitch; __u32 y_size, u_size; g2d_img->laddr[0] = item->dma_addr; if (g2d_img->format >= G2D_FORMAT_MAX) { G2D_ERR_MSG("format 0x%x is out of range\n", g2d_img->format); goto exit; } for (i = 0; i < len; ++i) { if (fmt_attr_tbl[i].format == g2d_img->format) { y_width = g2d_img->width; y_height = g2d_img->height; u_width = y_width/fmt_attr_tbl[i].hor_rsample_u; u_height = y_height/fmt_attr_tbl[i].ver_rsample_u; y_pitch = G2DALIGN(y_width, g2d_img->align[0]); u_pitch = G2DALIGN(u_width * (fmt_attr_tbl[i].uvc + 1), g2d_img->align[1]); y_size = y_pitch * y_height; u_size = u_pitch * u_height; g2d_img->laddr[1] = g2d_img->laddr[0] + y_size; g2d_img->laddr[2] = g2d_img->laddr[0] + y_size + u_size; if (g2d_img->format == G2D_FORMAT_YUV420_PLANAR) { /* v */ g2d_img->laddr[1] = g2d_img->laddr[0] + y_size + u_size; g2d_img->laddr[2] = g2d_img->laddr[0] + y_size; /* u */ } ret = 0; break; } } if (ret != 0) G2D_ERR_MSG("format 0x%x is invalid\n", g2d_img->format); exit: return ret; } __s32 g2d_byte_cal(__u32 format, __u32 *ycnt, __u32 *ucnt, __u32 *vcnt) { *ycnt = 0; *ucnt = 0; *vcnt = 0; if (format <= G2D_FORMAT_BGRX8888) *ycnt = 4; else if (format <= G2D_FORMAT_BGR888) *ycnt = 3; else if (format <= G2D_FORMAT_BGRA5551) *ycnt = 2; else if (format <= G2D_FORMAT_BGRA1010102) *ycnt = 4; else if (format <= 0x23) { *ycnt = 2; } else if (format <= 0x25) { *ycnt = 1; *ucnt = 2; } else if (format == 0x26) { *ycnt = 1; *ucnt = 1; *vcnt = 1; } else if (format <= 0x29) { *ycnt = 1; *ucnt = 2; } else if (format == 0x2a) { *ycnt = 1; *ucnt = 1; *vcnt = 1; } else if (format <= 0x2d) { *ycnt = 1; *ucnt = 2; } else if (format == 0x2e) { *ycnt = 1; *ucnt = 1; *vcnt = 1; } else if (format == 0x30) *ycnt = 1; else if (format <= 0x36) { *ycnt = 2; *ucnt = 4; } else if (format <= 0x39) *ycnt = 6; return 0; } /** */ __u32 cal_align(__u32 width, __u32 align) { switch (align) { case 0: return width; case 4: return (width + 3) >> 1 << 1; case 8: return (width + 7) >> 3 << 3; case 16: return (width + 15) >> 4 << 4; case 32: return (width + 31) >> 5 << 5; case 64: return (width + 63) >> 6 << 6; case 128: return (width + 127) >> 7 << 7; default: return (width + 31) >> 5 << 5; } } __s32 g2d_image_check(g2d_image_enh *p_image) { __s32 ret = -EINVAL; if (!p_image) { G2D_ERR_MSG("NUll pointer!\n"); goto OUT; } if (((p_image->clip_rect.x < 0) && ((-p_image->clip_rect.x) > p_image->clip_rect.w)) || ((p_image->clip_rect.y < 0) && ((-p_image->clip_rect.y) > p_image->clip_rect.h)) || ((p_image->clip_rect.x > 0) && (p_image->clip_rect.x > p_image->width - 1)) || ((p_image->clip_rect.y > 0) && (p_image->clip_rect.y > p_image->height - 1))) { G2D_ERR_MSG("Invalid imager parameter setting\n"); goto OUT; } if (((p_image->clip_rect.x < 0) && ((-p_image->clip_rect.x) < p_image->clip_rect.w))) { p_image->clip_rect.w = p_image->clip_rect.w + p_image->clip_rect.x; p_image->clip_rect.x = 0; } else if ((p_image->clip_rect.x + p_image->clip_rect.w) > p_image->width) { p_image->clip_rect.w = p_image->width - p_image->clip_rect.x; } if (((p_image->clip_rect.y < 0) && ((-p_image->clip_rect.y) < p_image->clip_rect.h))) { p_image->clip_rect.h = p_image->clip_rect.h + p_image->clip_rect.y; p_image->clip_rect.y = 0; } else if ((p_image->clip_rect.y + p_image->clip_rect.h) > p_image->height) { p_image->clip_rect.h = p_image->height - p_image->clip_rect.y; } p_image->bpremul = 0; p_image->bbuff = 1; ret = 0; OUT: return ret; } int g2d_open(struct inode *inode, struct file *file) { mutex_lock(¶.mutex); para.user_cnt++; if (para.user_cnt == 1) { if (para.clk) { if (para.clk_parent) clk_set_parent(para.clk, para.clk_parent); clk_prepare_enable(para.clk); } para.opened = true; g2d_bsp_open(); #if ((defined CONFIG_ARCH_SUN8IW21P1) || (defined CONFIG_ARCH_SUN20IW2P1) \ || (defined CONFIG_ARCH_SUN20IW3P1)) ccmu = ioremap(0x0200163C, 4); #endif } mutex_unlock(¶.mutex); return 0; } EXPORT_SYMBOL_GPL(g2d_open); int g2d_release(struct inode *inode, struct file *file) { mutex_lock(¶.mutex); para.user_cnt--; if (para.user_cnt == 0) { if (para.clk) clk_disable(para.clk); para.opened = false; g2d_bsp_close(); #if ((defined CONFIG_ARCH_SUN8IW21P1) || (defined CONFIG_ARCH_SUN20IW2P1) \ || (defined CONFIG_ARCH_SUN20IW3P1)) iounmap(ccmu); #endif } mutex_unlock(¶.mutex); mutex_lock(&global_lock); scan_order = G2D_SM_TDLR; mutex_unlock(&global_lock); return 0; } EXPORT_SYMBOL_GPL(g2d_release); int g2d_blit_h(g2d_blt_h *para) { int ret = -1; #if defined(CONFIG_SUNXI_G2D_ROTATE) ret = g2d_rotate_set_para(¶->src_image_h, ¶->dst_image_h, para->flag_h); #else G2D_ERR_MSG("Please enable CONFIG_SUNXI_G2D_ROTATE\n"); #endif return ret; } EXPORT_SYMBOL_GPL(g2d_blit_h); int g2d_mmap(struct file *file, struct vm_area_struct *vma) { unsigned long mypfn = vma->vm_pgoff; unsigned long vmsize = vma->vm_end - vma->vm_start; vma->vm_pgoff = 0; vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); if (remap_pfn_range(vma, vma->vm_start, mypfn, vmsize, vma->vm_page_prot)) return -EAGAIN; return 0; } int g2d_wait_cmd_finish(unsigned int timeout) { timeout = wait_event_timeout(g2d_ext_hd.queue, g2d_ext_hd.finish_flag == 1, msecs_to_jiffies(timeout)); if (timeout == 0) { #if ((defined CONFIG_ARCH_SUN8IW21P1) || (defined CONFIG_ARCH_SUN20IW2P1) \ || (defined CONFIG_ARCH_SUN20IW3P1)) g2d_reset(); g2d_ext_hd.finish_flag = 1; wake_up(&g2d_ext_hd.queue); g2d_timeout_flag++; G2D_ERR_MSG("G2D irq pending flag timeout\n"); if (g2d_timeout_flag >= 3) { G2D_ERR_MSG("G2D irq pending flag timeout over 3 times!\n"); } return -1; #else g2d_bsp_reset(); G2D_ERR_MSG("G2D irq pending flag timeout\n"); g2d_ext_hd.finish_flag = 1; wake_up(&g2d_ext_hd.queue); return -1; #endif } g2d_ext_hd.finish_flag = 0; return 0; } irqreturn_t g2d_handle_irq(int irq, void *dev_id) { #if ((defined CONFIG_ARCH_SUN8IW21P1) || (defined CONFIG_ARCH_SUN20IW2P1) \ || (defined CONFIG_ARCH_SUN20IW3P1)) g2d_timeout_flag = 0; #endif #if defined(CONFIG_SUNXI_G2D_MIXER) #if G2D_MIXER_RCQ_USED == 1 if (g2d_top_rcq_task_irq_query()) { g2d_top_mixer_reset(); g2d_ext_hd.finish_flag = 1; wake_up(&g2d_ext_hd.queue); return IRQ_HANDLED; } #else if (g2d_mixer_irq_query()) { g2d_top_mixer_reset(); g2d_ext_hd.finish_flag = 1; wake_up(&g2d_ext_hd.queue); return IRQ_HANDLED; } #endif #endif #if defined(CONFIG_SUNXI_G2D_ROTATE) if (g2d_rot_irq_query()) { g2d_top_rot_reset(); g2d_ext_hd.finish_flag = 1; wake_up(&g2d_ext_hd.queue); return IRQ_HANDLED; } #endif return IRQ_HANDLED; } void g2d_ioctl_mutex_lock(void) { if (!mutex_trylock(¶.mutex)) mutex_lock(¶.mutex); } EXPORT_SYMBOL_GPL(g2d_ioctl_mutex_lock); void g2d_ioctl_mutex_unlock(void) { mutex_unlock(¶.mutex); } EXPORT_SYMBOL_GPL(g2d_ioctl_mutex_unlock); long g2d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = -1; struct timeval test_start, test_end; if (g_time_info == 1) do_gettimeofday(&test_start); if (!mutex_trylock(¶.mutex)) mutex_lock(¶.mutex); g2d_ext_hd.finish_flag = 0; switch (cmd) { case G2D_CMD_MIXER_TASK: { #if defined(CONFIG_SUNXI_G2D_MIXER) struct mixer_para *p_mixer_para = NULL; unsigned long karg[2]; unsigned long ubuffer[2] = { 0 }; if (copy_from_user((void *)karg, (void __user *)arg, sizeof(unsigned long) * 2)) { ret = -EFAULT; goto err_noput; } ubuffer[0] = *(unsigned long *)karg; ubuffer[1] = (*(unsigned long *)(karg + 1)); p_mixer_para = kmalloc(sizeof(struct mixer_para) * ubuffer[1], GFP_KERNEL | __GFP_ZERO); if (!p_mixer_para) goto err_noput; if (copy_from_user(p_mixer_para, (void __user *)ubuffer[0], sizeof(struct mixer_para) * ubuffer[1])) { ret = -EFAULT; goto err_noput; } ret = mixer_task_process(¶, p_mixer_para, ubuffer[1]); kfree(p_mixer_para); #endif break; } case G2D_CMD_CREATE_TASK: { #if defined(CONFIG_SUNXI_G2D_MIXER) struct mixer_para *p_mixer_para = NULL; unsigned long karg[2]; unsigned long ubuffer[2] = { 0 }; if (copy_from_user((void *)karg, (void __user *)arg, sizeof(unsigned long) * 2)) { ret = -EFAULT; goto err_noput; } ubuffer[0] = *(unsigned long *)karg; ubuffer[1] = (*(unsigned long *)(karg + 1)); p_mixer_para = kmalloc(sizeof(struct mixer_para) * ubuffer[1], GFP_KERNEL | __GFP_ZERO); if (!p_mixer_para) goto err_noput; if (copy_from_user(p_mixer_para, (void __user *)ubuffer[0], sizeof(struct mixer_para) * ubuffer[1])) { ret = -EFAULT; goto err_noput; } ret = create_mixer_task(¶, p_mixer_para, ubuffer[1]); if (copy_to_user((void __user *)ubuffer[0], p_mixer_para, sizeof(struct mixer_para) * ubuffer[1])) { G2D_ERR_MSG("copy_to_user fail\n"); return -EFAULT; } kfree(p_mixer_para); #endif break; } case G2D_CMD_TASK_APPLY: { #if defined(CONFIG_SUNXI_G2D_MIXER) unsigned long karg[1]; unsigned long ubuffer[1] = { 0 }; struct g2d_mixer_task *p_task = NULL; if (copy_from_user((void *)karg, (void __user *)arg, sizeof(unsigned long))) { ret = -EFAULT; goto err_noput; } ubuffer[0] = *(unsigned long *)karg; p_task = g2d_mixer_get_inst(ubuffer[0]); if (!p_task) { ret = -EFAULT; goto err_noput; } ret = p_task->apply(p_task); #endif break; } case G2D_CMD_TASK_DESTROY: { #if defined(CONFIG_SUNXI_G2D_MIXER) unsigned long karg[1]; unsigned long ubuffer[1] = { 0 }; struct g2d_mixer_task *p_task = NULL; if (copy_from_user((void *)karg, (void __user *)arg, sizeof(unsigned long))) { ret = -EFAULT; goto err_noput; } ubuffer[0] = *(unsigned long *)karg; p_task = g2d_mixer_get_inst(ubuffer[0]); if (!p_task) { ret = -EFAULT; G2D_ERR_MSG("Fail to find mixer task inst:%lu\n", ubuffer[0]); goto err_noput; } ret = p_task->destory(p_task); #endif break; } case G2D_CMD_TASK_GET_PARA: { #if defined(CONFIG_SUNXI_G2D_MIXER) struct g2d_mixer_task *p_task = NULL; unsigned long karg[2]; unsigned long ubuffer[2] = { 0 }; if (copy_from_user((void *)karg, (void __user *)arg, sizeof(unsigned long) * 2)) { ret = -EFAULT; goto err_noput; } ubuffer[0] = *(unsigned long *)karg; ubuffer[1] = (*(unsigned long *)(karg + 1)); p_task = g2d_mixer_get_inst(ubuffer[0]); if (!p_task || !ubuffer[1]) { ret = -EFAULT; goto err_noput; } ret = copy_to_user( (void __user *)ubuffer[1], p_task->p_para, sizeof(struct mixer_para) * p_task->frame_cnt); #endif break; } case G2D_CMD_BITBLT_H: { g2d_blt_h blit_para; if (copy_from_user(&blit_para, (g2d_blt_h *) arg, sizeof(g2d_blt_h))) { ret = -EFAULT; goto err_noput; } if (blit_para.flag_h & 0xff00) { ret = g2d_blit_h(&blit_para); } #if defined(CONFIG_SUNXI_G2D_MIXER) else { struct mixer_para mixer_blit_para; /*mixer module*/ memset(&mixer_blit_para, 0, sizeof(struct mixer_para)); memcpy(&mixer_blit_para.dst_image_h, &blit_para.dst_image_h, sizeof(g2d_image_enh)); memcpy(&mixer_blit_para.src_image_h, &blit_para.src_image_h, sizeof(g2d_image_enh)); mixer_blit_para.flag_h = blit_para.flag_h; mixer_blit_para.op_flag = OP_BITBLT; ret = mixer_task_process(¶, &mixer_blit_para, 1); } #endif break; } case G2D_CMD_BLD_H:{ #if defined(CONFIG_SUNXI_G2D_MIXER) g2d_bld bld_para; struct mixer_para mixer_bld_para; if (copy_from_user(&bld_para, (g2d_bld *) arg, sizeof(g2d_bld))) { ret = -EFAULT; goto err_noput; } memset(&mixer_bld_para, 0, sizeof(struct mixer_para)); memcpy(&mixer_bld_para.dst_image_h, &bld_para.dst_image_h, sizeof(g2d_image_enh)); memcpy(&mixer_bld_para.src_image_h, &bld_para.src_image_h, sizeof(g2d_image_enh)); memcpy(&mixer_bld_para.ck_para, &bld_para.ck_para, sizeof(g2d_ck)); mixer_bld_para.bld_cmd = bld_para.bld_cmd; mixer_bld_para.op_flag = OP_BLEND; ret = mixer_task_process(¶, &mixer_bld_para, 1); #endif break; } case G2D_CMD_FILLRECT_H:{ #if defined(CONFIG_SUNXI_G2D_MIXER) g2d_fillrect_h fill_para; struct mixer_para mixer_fill_para; if (copy_from_user(&fill_para, (g2d_fillrect_h *) arg, sizeof(g2d_fillrect_h))) { ret = -EFAULT; goto err_noput; } memset(&mixer_fill_para, 0, sizeof(struct mixer_para)); memcpy(&mixer_fill_para.dst_image_h, &fill_para.dst_image_h, sizeof(g2d_image_enh)); mixer_fill_para.op_flag = OP_FILLRECT; ret = mixer_task_process(¶, &mixer_fill_para, 1); #endif break; } case G2D_CMD_MASK_H:{ #if defined(CONFIG_SUNXI_G2D_MIXER) g2d_maskblt mask_para; struct mixer_para mixer_mask_para; if (copy_from_user(&mask_para, (g2d_maskblt *) arg, sizeof(g2d_maskblt))) { ret = -EFAULT; goto err_noput; } memset(&mixer_mask_para, 0, sizeof(struct mixer_para)); memcpy(&mixer_mask_para.ptn_image_h, &mask_para.ptn_image_h, sizeof(g2d_image_enh)); memcpy(&mixer_mask_para.mask_image_h, &mask_para.mask_image_h, sizeof(g2d_image_enh)); memcpy(&mixer_mask_para.dst_image_h, &mask_para.dst_image_h, sizeof(g2d_image_enh)); memcpy(&mixer_mask_para.src_image_h, &mask_para.src_image_h, sizeof(g2d_image_enh)); mixer_mask_para.back_flag = mask_para.back_flag; mixer_mask_para.fore_flag = mask_para.fore_flag; mixer_mask_para.op_flag = OP_MASK; ret = mixer_task_process(¶, &mixer_mask_para, 1); #endif break; } case G2D_CMD_INVERTED_ORDER: { if (arg > G2D_SM_DTRL) { G2D_ERR_MSG("scan mode is err.\n"); ret = -EINVAL; goto err_noput; } mutex_lock(&global_lock); scan_order = arg; mutex_unlock(&global_lock); break; } default: goto err_noput; break; } err_noput: mutex_unlock(¶.mutex); if (g_time_info == 1) { do_gettimeofday(&test_end); g_func_runtime += (test_end.tv_sec - test_start.tv_sec) * 1000000 + (test_end.tv_usec - test_start.tv_usec); } return ret; } __s32 drv_g2d_init(void) { memset(&g2d_ext_hd, 0, sizeof(__g2d_drv_t)); init_waitqueue_head(&g2d_ext_hd.queue); g2d_top_set_base((unsigned long) para.io); #if defined(CONFIG_SUNXI_G2D_ROTATE) g2d_rot_set_base((unsigned long) para.io); #endif return 0; } static ssize_t g2d_debug_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "debug=%d\n", dbg_info); } static ssize_t g2d_debug_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { if (strncasecmp(buf, "1", 1) == 0) dbg_info = 1; else if (strncasecmp(buf, "0", 1) == 0) dbg_info = 0; else if (strncasecmp(buf, "2", 1) == 0) dbg_info = 2; else G2D_ERR_MSG("Error input!\n"); return count; } static DEVICE_ATTR(debug, 0660, g2d_debug_show, g2d_debug_store); static ssize_t g2d_func_runtime_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "func_runtime = %d us\n", g_func_runtime); } static ssize_t g2d_func_runtime_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { g_func_runtime = 0; if (strncasecmp(buf, "1", 1) == 0) g_time_info = 1; else if (strncasecmp(buf, "0", 1) == 0) g_time_info = 0; else G2D_ERR_MSG("Error input!\n"); return count; } static DEVICE_ATTR(func_runtime, 0660, g2d_func_runtime_show, g2d_func_runtime_store); static struct attribute *g2d_attributes[] = { &dev_attr_debug.attr, &dev_attr_func_runtime.attr, NULL }; static struct attribute_group g2d_attribute_group = { .name = "attr", .attrs = g2d_attributes }; static u64 sunxi_g2d_dma_mask = DMA_BIT_MASK(32); static int g2d_probe(struct platform_device *pdev) { int ret = 0; __g2d_info_t *info = NULL; info = ¶ info->dev = &pdev->dev; dmabuf_dev = &pdev->dev; dmabuf_dev->dma_mask = &sunxi_g2d_dma_mask; dmabuf_dev->coherent_dma_mask = DMA_BIT_MASK(32); platform_set_drvdata(pdev, info); info->io = of_iomap(pdev->dev.of_node, 0); if (info->io == NULL) { G2D_ERR_MSG("iormap() of register failed\n"); ret = -ENXIO; goto dealloc_fb; } info->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (!info->irq) { G2D_ERR_MSG("irq_of_parse_and_map irq fail for transform\n"); ret = -ENXIO; goto release_regs; } /* request the irq */ ret = request_irq(info->irq, g2d_handle_irq, 0, dev_name(&pdev->dev), NULL); if (ret) { G2D_ERR_MSG("failed to install irq resource\n"); goto release_regs; } /* clk init */ info->clk = of_clk_get(pdev->dev.of_node, 0); if (IS_ERR(info->clk)) G2D_ERR_MSG("fail to get clk\n"); else info->clk_parent = clk_get_parent(info->clk); drv_g2d_init(); mutex_init(&info->mutex); mutex_init(&global_lock); ret = sysfs_create_group(&g2d_dev->kobj, &g2d_attribute_group); if (ret < 0) G2D_ERR_MSG("sysfs_create_file fail!\n"); return 0; release_regs: dealloc_fb: platform_set_drvdata(pdev, NULL); return ret; } static const struct file_operations g2d_fops = { .owner = THIS_MODULE, .open = g2d_open, .release = g2d_release, .unlocked_ioctl = g2d_ioctl, .mmap = g2d_mmap, }; static int g2d_remove(struct platform_device *pdev) { __g2d_info_t *info = platform_get_drvdata(pdev); free_irq(info->irq, NULL); platform_set_drvdata(pdev, NULL); sysfs_remove_group(&g2d_dev->kobj, &g2d_attribute_group); INFO("Driver unloaded succesfully.\n"); return 0; } static int g2d_suspend(struct platform_device *pdev, pm_message_t state) { INFO("%s.\n", __func__); mutex_lock(¶.mutex); if (para.opened) { if (para.clk) clk_disable(para.clk); g2d_bsp_close(); } mutex_unlock(¶.mutex); INFO("g2d_suspend succesfully.\n"); return 0; } static int g2d_resume(struct platform_device *pdev) { INFO("%s.\n", __func__); mutex_lock(¶.mutex); if (para.opened) { if (para.clk) { if (para.clk_parent) clk_set_parent(para.clk, para.clk_parent); clk_prepare_enable(para.clk); } g2d_bsp_open(); } mutex_unlock(¶.mutex); INFO("g2d_resume succesfully.\n"); return 0; } static const struct of_device_id sunxi_g2d_match[] = { {.compatible = "allwinner,sunxi-g2d",}, {}, }; static struct platform_driver g2d_driver = { .probe = g2d_probe, .remove = g2d_remove, .suspend = g2d_suspend, .resume = g2d_resume, .driver = { .owner = THIS_MODULE, .name = "g2d", .of_match_table = sunxi_g2d_match, }, }; int __init g2d_module_init(void) { int ret = 0, err; alloc_chrdev_region(&devid, 0, 1, "g2d_chrdev"); g2d_cdev = cdev_alloc(); cdev_init(g2d_cdev, &g2d_fops); g2d_cdev->owner = THIS_MODULE; err = cdev_add(g2d_cdev, devid, 1); if (err) { G2D_ERR_MSG("I was assigned major number %d.\n", MAJOR(devid)); return -1; } g2d_class = class_create(THIS_MODULE, "g2d"); if (IS_ERR(g2d_class)) { G2D_ERR_MSG("create class error\n"); return -1; } g2d_dev = device_create(g2d_class, NULL, devid, NULL, "g2d"); if (ret == 0) ret = platform_driver_register(&g2d_driver); INFO("rcq version initialized.major:%d\n", MAJOR(devid)); return ret; } static void __exit g2d_module_exit(void) { INFO("g2d_module_exit\n"); platform_driver_unregister(&g2d_driver); device_destroy(g2d_class, devid); class_destroy(g2d_class); cdev_del(g2d_cdev); } subsys_initcall_sync(g2d_module_init); module_exit(g2d_module_exit); MODULE_AUTHOR("zxb "); MODULE_DESCRIPTION("g2d(rcq) driver"); MODULE_LICENSE("GPL");