/* * Allwinner SoCs g2d 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. */ #include"g2d_driver_i.h" /* alloc based on 4K byte */ #define G2D_BYTE_ALIGN(x) (((x + (4*1024-1)) >> 12) << 12) static struct info_mem g2d_mem[MAX_G2D_MEM_INDEX]; static int g2d_mem_sel; 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; __g2d_drv_t g2d_ext_hd; __g2d_info_t para; #if !defined(CONFIG_OF) static struct resource g2d_resource[2] = { [0] = { .start = SUNXI_MP_PBASE, .end = SUNXI_MP_PBASE + 0x000fffff, .flags = IORESOURCE_MEM, }, [1] = { .start = INTC_IRQNO_DE_MIX, .end = INTC_IRQNO_DE_MIX, .flags = IORESOURCE_IRQ, }, }; #endif __s32 drv_g2d_init(void) { g2d_init_para init_para; DBG("drv_g2d_init\n"); init_para.g2d_base = (__u32) para.io; memset(&g2d_ext_hd, 0, sizeof(__g2d_drv_t)); init_waitqueue_head(&g2d_ext_hd.queue); g2d_init(&init_para); return 0; } 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 = PAGE_ALIGN(bytes_num); address = dma_alloc_coherent(para.dev, actual_bytes, (dma_addr_t *) phy_addr, GFP_KERNEL); if (address) { DBG("dma_alloc_coherent ok, address=0x%p, size=0x%x\n", (void *)(*(unsigned long *)phy_addr), bytes_num); return address; } else { ERR("dma_alloc_coherent fail, size=0x%x\n", bytes_num); return NULL; } } else { ERR("%s size is zero\n", __func__); } #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)); ERR("page_address fail!\n"); return NULL; } *phy_addr = virt_to_phys(address); return address; } else { ERR("alloc_pages fail!\n"); return NULL; } } else { ERR("%s size is zero\n", __func__); } #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 } __s32 g2d_get_free_mem_index(void) { __u32 i = 0; for (i = 0; i < MAX_G2D_MEM_INDEX; i++) { if (g2d_mem[i].b_used == 0) return i; } return -1; } int g2d_mem_request(__u32 size) { __s32 sel; __u32 ret = 0; __u32 phy_addr; sel = g2d_get_free_mem_index(); if (sel < 0) { ERR("g2d_get_free_mem_index fail!\n"); return -EINVAL; } ret = (__u32) g2d_malloc(size, &phy_addr); if (ret != 0) { g2d_mem[sel].virt_addr = (void *)ret; memset(g2d_mem[sel].virt_addr, 0, size); g2d_mem[sel].phy_addr = phy_addr; g2d_mem[sel].mem_len = size; g2d_mem[sel].b_used = 1; INFO("map_g2d_memory[%d]: pa=%08lx va=%p size:%x\n", sel, g2d_mem[sel].phy_addr, g2d_mem[sel].virt_addr, size); return sel; } else { ERR("fail to alloc reserved memory!\n"); return -ENOMEM; } } int g2d_mem_release(__u32 sel) { if (g2d_mem[sel].b_used == 0) { ERR("mem not used in g2d_mem_release,%d\n", sel); return -EINVAL; } g2d_free((void *)g2d_mem[sel].virt_addr, (void *)g2d_mem[sel].phy_addr, g2d_mem[sel].mem_len); memset(&g2d_mem[sel], 0, sizeof(struct info_mem)); return 0; } 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; } static int g2d_open(struct inode *inode, struct file *file) { mutex_lock(¶.mutex); if (!para.opened) { if (para.clk) clk_prepare_enable(para.clk); para.opened = true; } mutex_unlock(¶.mutex); return 0; } static int g2d_release(struct inode *inode, struct file *file) { mutex_lock(¶.mutex); if (para.opened) { if (para.clk) clk_disable(para.clk); para.opened = false; } mutex_unlock(¶.mutex); mutex_lock(&global_lock); scan_order = G2D_SM_TDLR; mutex_unlock(&global_lock); return 0; } irqreturn_t g2d_handle_irq(int irq, void *dev_id) { #ifdef G2D_V2X_SUPPORT __u32 mixer_irq_flag, rot_irq_flag; mixer_irq_flag = g2d_irq_query(); rot_irq_flag = rot_irq_query(); if (mixer_irq_flag == 0 || rot_irq_flag == 0) { g2d_bsp_close(); g2d_ext_hd.finish_flag = 1; wake_up(&g2d_ext_hd.queue); } #else __u32 mod_irq_flag, cmd_irq_flag; mod_irq_flag = mixer_get_irq(); cmd_irq_flag = mixer_get_irq0(); if (mod_irq_flag & G2D_FINISH_IRQ) { mixer_clear_init(); g2d_ext_hd.finish_flag = 1; wake_up(&g2d_ext_hd.queue); } else if (cmd_irq_flag & G2D_FINISH_IRQ) { mixer_clear_init0(); g2d_ext_hd.finish_flag = 1; wake_up(&g2d_ext_hd.queue); } #endif return IRQ_HANDLED; } int g2d_init(g2d_init_para *para) { mixer_set_reg_base(para->g2d_base); return 0; } int g2d_exit(void) { __u8 err = 0; return err; } int g2d_wait_cmd_finish(void) { long timeout = 100; /* 100ms */ timeout = wait_event_timeout(g2d_ext_hd.queue, g2d_ext_hd.finish_flag == 1, msecs_to_jiffies(timeout)); if (timeout == 0) { #ifdef G2D_V2X_SUPPORT g2d_bsp_close(); #else mixer_clear_init(); mixer_clear_init0(); #endif pr_warn("wait g2d irq pending flag timeout\n"); g2d_ext_hd.finish_flag = 1; wake_up(&g2d_ext_hd.queue); return -1; } return 0; } int g2d_blit(g2d_blt *para) { __s32 err = 0; __u32 tmp_w, tmp_h; if ((para->flag & G2D_BLT_ROTATE90) || (para->flag & G2D_BLT_ROTATE270)) { tmp_w = para->src_rect.h; tmp_h = para->src_rect.w; } else { tmp_w = para->src_rect.w; tmp_h = para->src_rect.h; } /* check the parameter valid */ if (((para->src_rect.x < 0) && ((-para->src_rect.x) > para->src_rect.w)) || ((para->src_rect.y < 0) && ((-para->src_rect.y) > para->src_rect.h)) || ((para->dst_x < 0) && ((-para->dst_x) > tmp_w)) || ((para->dst_y < 0) && ((-para->dst_y) > tmp_h)) || ((para->src_rect.x > 0) && (para->src_rect.x > para->src_image.w - 1)) || ((para->src_rect.y > 0) && (para->src_rect.y > para->src_image.h - 1)) || ((para->dst_x > 0) && (para->dst_x > para->dst_image.w - 1)) || ((para->dst_y > 0) && (para->dst_y > para->dst_image.h - 1))) { printk("invalid blit parameter setting"); return -EINVAL; } else { if (((para->src_rect.x < 0) && ((-para->src_rect.x) < para->src_rect.w))) { para->src_rect.w = para->src_rect.w + para->src_rect.x; para->src_rect.x = 0; } else if ((para->src_rect.x + para->src_rect.w) > para->src_image.w) { para->src_rect.w = para->src_image.w - para->src_rect.x; } if (((para->src_rect.y < 0) && ((-para->src_rect.y) < para->src_rect.h))) { para->src_rect.h = para->src_rect.h + para->src_rect.y; para->src_rect.y = 0; } else if ((para->src_rect.y + para->src_rect.h) > para->src_image.h) { para->src_rect.h = para->src_image.h - para->src_rect.y; } if (((para->dst_x < 0) && ((-para->dst_x) < tmp_w))) { para->src_rect.w = tmp_w + para->dst_x; para->src_rect.x = (-para->dst_x); para->dst_x = 0; } else if ((para->dst_x + tmp_w) > para->dst_image.w) { para->src_rect.w = para->dst_image.w - para->dst_x; } if (((para->dst_y < 0) && ((-para->dst_y) < tmp_h))) { para->src_rect.h = tmp_h + para->dst_y; para->src_rect.y = (-para->dst_y); para->dst_y = 0; } else if ((para->dst_y + tmp_h) > para->dst_image.h) { para->src_rect.h = para->dst_image.h - para->dst_y; } } g2d_ext_hd.finish_flag = 0; /* Add support inverted order copy, however, * hardware have a bug when reciving y coordinate, * it use (y + height) rather than (y) on inverted * order mode, so here adjust it before pass it to hardware. */ mutex_lock(&global_lock); if (scan_order > G2D_SM_TDRL) para->dst_y += para->src_rect.h; mutex_unlock(&global_lock); err = mixer_blt(para, scan_order); return err; } int g2d_fill(g2d_fillrect *para) { __s32 err = 0; /* check the parameter valid */ if (((para->dst_rect.x < 0) && ((-para->dst_rect.x) > para->dst_rect.w)) || ((para->dst_rect.y < 0) && ((-para->dst_rect.y) > para->dst_rect.h)) || ((para->dst_rect.x > 0) && (para->dst_rect.x > para->dst_image.w - 1)) || ((para->dst_rect.y > 0) && (para->dst_rect.y > para->dst_image.h - 1))) { printk("invalid fillrect parameter setting"); return -EINVAL; } else { if (((para->dst_rect.x < 0) && ((-para->dst_rect.x) < para->dst_rect.w))) { para->dst_rect.w = para->dst_rect.w + para->dst_rect.x; para->dst_rect.x = 0; } else if ((para->dst_rect.x + para->dst_rect.w) > para->dst_image.w) { para->dst_rect.w = para->dst_image.w - para->dst_rect.x; } if (((para->dst_rect.y < 0) && ((-para->dst_rect.y) < para->dst_rect.h))) { para->dst_rect.h = para->dst_rect.h + para->dst_rect.y; para->dst_rect.y = 0; } else if ((para->dst_rect.y + para->dst_rect.h) > para->dst_image.h) { para->dst_rect.h = para->dst_image.h - para->dst_rect.y; } } g2d_ext_hd.finish_flag = 0; err = mixer_fillrectangle(para); return err; } int g2d_stretchblit(g2d_stretchblt *para) { __s32 err = 0; /* check the parameter valid */ if (((para->src_rect.x < 0) && ((-para->src_rect.x) > para->src_rect.w)) || ((para->src_rect.y < 0) && ((-para->src_rect.y) > para->src_rect.h)) || ((para->dst_rect.x < 0) && ((-para->dst_rect.x) > para->dst_rect.w)) || ((para->dst_rect.y < 0) && ((-para->dst_rect.y) > para->dst_rect.h)) || ((para->src_rect.x > 0) && (para->src_rect.x > para->src_image.w - 1)) || ((para->src_rect.y > 0) && (para->src_rect.y > para->src_image.h - 1)) || ((para->dst_rect.x > 0) && (para->dst_rect.x > para->dst_image.w - 1)) || ((para->dst_rect.y > 0) && (para->dst_rect.y > para->dst_image.h - 1))) { printk("invalid stretchblit parameter setting"); return -EINVAL; } else { if (((para->src_rect.x < 0) && ((-para->src_rect.x) < para->src_rect.w))) { para->src_rect.w = para->src_rect.w + para->src_rect.x; para->src_rect.x = 0; } else if ((para->src_rect.x + para->src_rect.w) > para->src_image.w) { para->src_rect.w = para->src_image.w - para->src_rect.x; } if (((para->src_rect.y < 0) && ((-para->src_rect.y) < para->src_rect.h))) { para->src_rect.h = para->src_rect.h + para->src_rect.y; para->src_rect.y = 0; } else if ((para->src_rect.y + para->src_rect.h) > para->src_image.h) { para->src_rect.h = para->src_image.h - para->src_rect.y; } if (((para->dst_rect.x < 0) && ((-para->dst_rect.x) < para->dst_rect.w))) { para->dst_rect.w = para->dst_rect.w + para->dst_rect.x; para->dst_rect.x = 0; } else if ((para->dst_rect.x + para->dst_rect.w) > para->dst_image.w) { para->dst_rect.w = para->dst_image.w - para->dst_rect.x; } if (((para->dst_rect.y < 0) && ((-para->dst_rect.y) < para->dst_rect.h))) { para->dst_rect.h = para->dst_rect.h + para->dst_rect.y; para->dst_rect.y = 0; } else if ((para->dst_rect.y + para->dst_rect.h) > para->dst_image.h) { para->dst_rect.h = para->dst_image.h - para->dst_rect.y; } } g2d_ext_hd.finish_flag = 0; /* Add support inverted order copy, however, * hardware have a bug when reciving y coordinate, * it use (y + height) rather than (y) on inverted * order mode, so here adjust it before pass it to hardware. */ mutex_lock(&global_lock); if (scan_order > G2D_SM_TDRL) para->dst_rect.y += para->src_rect.h; mutex_unlock(&global_lock); err = mixer_stretchblt(para, scan_order); return err; } #ifdef G2D_V2X_SUPPORT int g2d_fill_h(g2d_fillrect_h *para) { __s32 err = 0; g2d_image_enh dst_tmp; g2d_image_enh *dst = &dst_tmp; /* check the parameter valid */ if (((para->dst_image_h.clip_rect.x < 0) && ((-para->dst_image_h.clip_rect.x) > para->dst_image_h.clip_rect.w)) || ((para->dst_image_h.clip_rect.y < 0) && ((-para->dst_image_h.clip_rect.y) > para->dst_image_h.clip_rect.h)) || ((para->dst_image_h.clip_rect.x > 0) && (para->dst_image_h.clip_rect.x > para->dst_image_h.width - 1)) || ((para->dst_image_h.clip_rect.y > 0) && (para->dst_image_h.clip_rect.y > para->dst_image_h.height - 1))) { pr_err("invalid fillrect parameter setting\n"); return -EINVAL; } else { if (((para->dst_image_h.clip_rect.x < 0) && ((-para->dst_image_h.clip_rect.x) < para->dst_image_h.clip_rect.w))) { para->dst_image_h.clip_rect.w = para->dst_image_h.clip_rect.w + para->dst_image_h.clip_rect.x; para->dst_image_h.clip_rect.x = 0; } else if ((para->dst_image_h.clip_rect.x + para->dst_image_h.clip_rect.w) > para->dst_image_h.width) { para->dst_image_h.clip_rect.w = para->dst_image_h.width - para->dst_image_h.clip_rect.x; } if (((para->dst_image_h.clip_rect.y < 0) && ((-para->dst_image_h.clip_rect.y) < para->dst_image_h.clip_rect.h))) { para->dst_image_h.clip_rect.h = para->dst_image_h.clip_rect.h + para->dst_image_h.clip_rect.y; para->dst_image_h.clip_rect.y = 0; } else if ((para->dst_image_h.clip_rect.y + para->dst_image_h.clip_rect.h) > para->dst_image_h.height) { para->dst_image_h.clip_rect.h = para->dst_image_h.height - para->dst_image_h.clip_rect.y; } } dst->bbuff = 1; dst->gamut = G2D_BT709; dst->mode = 0; dst->alpha = para->dst_image_h.alpha; dst->color = para->dst_image_h.color; dst->format = para->dst_image_h.format; dst->laddr[0] = para->dst_image_h.laddr[0]; dst->laddr[1] = para->dst_image_h.laddr[1]; dst->laddr[2] = para->dst_image_h.laddr[2]; dst->width = para->dst_image_h.width; dst->height = para->dst_image_h.height; dst->clip_rect.x = para->dst_image_h.clip_rect.x; dst->clip_rect.y = para->dst_image_h.clip_rect.y; dst->clip_rect.w = para->dst_image_h.clip_rect.w; dst->clip_rect.h = para->dst_image_h.clip_rect.h; g2d_ext_hd.finish_flag = 0; err = g2d_fillrectangle(dst, dst->color); return err; } int g2d_blit_h(g2d_blt_h *para) { __s32 err = 0; g2d_image_enh src_tmp, dst_tmp; g2d_image_enh *src = &src_tmp; g2d_image_enh *dst = &dst_tmp; memset(src, 0, sizeof(g2d_image_enh)); memset(dst, 0, sizeof(g2d_image_enh)); /* check the parameter valid */ if (((para->src_image_h.clip_rect.x < 0) && ((-para->src_image_h.clip_rect.x) > para->src_image_h.clip_rect.w)) || ((para->src_image_h.clip_rect.y < 0) && ((-para->src_image_h.clip_rect.y) > para->src_image_h.clip_rect.h)) || ((para->src_image_h.clip_rect.x > 0) && (para->src_image_h.clip_rect.x > para->src_image_h.width - 1)) || ((para->src_image_h.clip_rect.y > 0) && (para->src_image_h.clip_rect.y > para->src_image_h.height - 1)) || ((para->dst_image_h.clip_rect.x > 0) && (para->dst_image_h.clip_rect.x > para->dst_image_h.width - 1)) || ((para->dst_image_h.clip_rect.y > 0) && (para->dst_image_h.clip_rect.y > para->dst_image_h.height - 1))) { pr_err("invalid bitblit parameter setting\n"); return -EINVAL; } else { if (((para->src_image_h.clip_rect.x < 0) && ((-para->src_image_h.clip_rect.x) < para->src_image_h.clip_rect.w))) { para->src_image_h.clip_rect.w = para->src_image_h.clip_rect.w + para->src_image_h.clip_rect.x; para->src_image_h.clip_rect.x = 0; } else if ((para->src_image_h.clip_rect.x + para->src_image_h.clip_rect.w) > para->src_image_h.width) { para->src_image_h.clip_rect.w = para->src_image_h.width - para->src_image_h.clip_rect.x; } if (((para->src_image_h.clip_rect.y < 0) && ((-para->src_image_h.clip_rect.y) < para->src_image_h.clip_rect.h))) { para->src_image_h.clip_rect.h = para->src_image_h.clip_rect.h + para->src_image_h.clip_rect.y; para->src_image_h.clip_rect.y = 0; } else if ((para->src_image_h.clip_rect.y + para->src_image_h.clip_rect.h) > para->src_image_h.height) { para->src_image_h.clip_rect.h = para->src_image_h.height - para->src_image_h.clip_rect.y; } if (((para->dst_image_h.clip_rect.x < 0) && ((-para->dst_image_h.clip_rect.x) < para->dst_image_h.clip_rect.w))) { para->dst_image_h.clip_rect.w = para->dst_image_h.clip_rect.w + para->dst_image_h.clip_rect.x; para->dst_image_h.clip_rect.x = 0; } else if ((para->dst_image_h.clip_rect.x + para->dst_image_h.clip_rect.w) > para->dst_image_h.width) { para->dst_image_h.clip_rect.w = para->dst_image_h.width - para->dst_image_h.clip_rect.x; } if (((para->dst_image_h.clip_rect.y < 0) && ((-para->dst_image_h.clip_rect.y) < para->dst_image_h.clip_rect.h))) { para->dst_image_h.clip_rect.h = para->dst_image_h.clip_rect.h + para->dst_image_h.clip_rect.y; para->dst_image_h.clip_rect.y = 0; } else if ((para->dst_image_h.clip_rect.y + para->dst_image_h.clip_rect.h) > para->dst_image_h.height) { para->dst_image_h.clip_rect.h = para->dst_image_h.height - para->dst_image_h.clip_rect.y; } } g2d_ext_hd.finish_flag = 0; /* Add support inverted order copy, however, * hardware have a bug when reciving y coordinate, * it use (y + height) rather than (y) on inverted * order mode, so here adjust it before pass it to hardware. */ src->bpremul = 0; src->bbuff = 1; src->color = para->color; src->alpha = para->src_image_h.alpha; src->format = para->src_image_h.format; src->mode = para->src_image_h.mode; src->laddr[0] = para->src_image_h.laddr[0]; src->laddr[1] = para->src_image_h.laddr[1]; src->laddr[2] = para->src_image_h.laddr[2]; src->width = para->src_image_h.width; src->height = para->src_image_h.height; src->clip_rect.x = para->src_image_h.clip_rect.x; src->clip_rect.y = para->src_image_h.clip_rect.y; src->clip_rect.w = para->src_image_h.clip_rect.w; src->clip_rect.h = para->src_image_h.clip_rect.h; src->gamut = G2D_BT709; dst->bbuff = 1; dst->alpha = para->dst_image_h.alpha; dst->format = para->dst_image_h.format; dst->color = para->color; dst->mode = para->dst_image_h.mode; dst->laddr[0] = para->dst_image_h.laddr[0]; dst->laddr[1] = para->dst_image_h.laddr[1]; dst->laddr[2] = para->dst_image_h.laddr[2]; dst->width = para->dst_image_h.width; dst->height = para->dst_image_h.height; dst->clip_rect.x = para->dst_image_h.clip_rect.x; dst->clip_rect.y = para->dst_image_h.clip_rect.y; dst->clip_rect.w = para->dst_image_h.clip_rect.w; dst->clip_rect.h = para->dst_image_h.clip_rect.h; dst->gamut = G2D_BT709; err = g2d_bsp_bitblt(src, dst, para->flag_h); return err; } int g2d_bld_h(g2d_bld *para) { __s32 err = 0; g2d_image_enh src_tmp, dst_tmp; g2d_image_enh *src = &src_tmp; g2d_image_enh *dst = &dst_tmp; g2d_ck ck_para_tmp; g2d_ck *ck_para = &ck_para_tmp; memset(src, 0, sizeof(g2d_image_enh)); memset(dst, 0, sizeof(g2d_image_enh)); memset(ck_para, 0, sizeof(g2d_ck)); /* check the parameter valid */ if (((para->src_image_h.clip_rect.x < 0) && ((-para->src_image_h.clip_rect.x) > para->src_image_h.clip_rect.w)) || ((para->src_image_h.clip_rect.y < 0) && ((-para->src_image_h.clip_rect.y) > para->src_image_h.clip_rect.h)) || ((para->src_image_h.clip_rect.x > 0) && (para->src_image_h.clip_rect.x > para->src_image_h.width - 1)) || ((para->src_image_h.clip_rect.y > 0) && (para->src_image_h.clip_rect.y > para->src_image_h.height - 1)) || ((para->dst_image_h.clip_rect.x > 0) && (para->dst_image_h.clip_rect.x > para->dst_image_h.width - 1)) || ((para->dst_image_h.clip_rect.y > 0) && (para->dst_image_h.clip_rect.y > para->dst_image_h.height - 1))) { pr_err("invalid blit parameter setting\n"); return -EINVAL; } else { if (((para->src_image_h.clip_rect.x < 0) && ((-para->src_image_h.clip_rect.x) < para->src_image_h.clip_rect.w))) { para->src_image_h.clip_rect.w = para->src_image_h.clip_rect.w + para->src_image_h.clip_rect.x; para->src_image_h.clip_rect.x = 0; } else if ((para->src_image_h.clip_rect.x + para->src_image_h.clip_rect.w) > para->src_image_h.width) { para->src_image_h.clip_rect.w = para->src_image_h.width - para->src_image_h.clip_rect.x; } if (((para->src_image_h.clip_rect.y < 0) && ((-para->src_image_h.clip_rect.y) < para->src_image_h.clip_rect.h))) { para->src_image_h.clip_rect.h = para->src_image_h.clip_rect.h + para->src_image_h.clip_rect.y; para->src_image_h.clip_rect.y = 0; } else if ((para->src_image_h.clip_rect.y + para->src_image_h.clip_rect.h) > para->src_image_h.height) { para->src_image_h.clip_rect.h = para->src_image_h.height - para->src_image_h.clip_rect.y; } } src->bbuff = 1; src->gamut = G2D_BT709; src->alpha = para->src_image_h.alpha; src->mode = para->src_image_h.mode; src->color = para->src_image_h.color; src->format = para->src_image_h.format; src->laddr[0] = para->src_image_h.laddr[0]; src->laddr[1] = para->src_image_h.laddr[1]; src->laddr[2] = para->src_image_h.laddr[2]; src->width = para->src_image_h.width; src->height = para->src_image_h.height; src->clip_rect.x = para->src_image_h.clip_rect.x; src->clip_rect.y = para->src_image_h.clip_rect.y; src->clip_rect.w = para->src_image_h.clip_rect.w; src->clip_rect.h = para->src_image_h.clip_rect.h; dst->bbuff = 1; dst->gamut = G2D_BT709; dst->alpha = para->dst_image_h.alpha; dst->mode = para->dst_image_h.mode; dst->format = para->dst_image_h.format; dst->laddr[0] = para->dst_image_h.laddr[0]; dst->laddr[1] = para->dst_image_h.laddr[1]; dst->laddr[2] = para->dst_image_h.laddr[2]; dst->width = para->dst_image_h.width; dst->height = para->dst_image_h.height; dst->clip_rect.x = para->dst_image_h.clip_rect.x; dst->clip_rect.y = para->dst_image_h.clip_rect.y; dst->clip_rect.w = para->dst_image_h.clip_rect.w; dst->clip_rect.h = para->dst_image_h.clip_rect.h; ck_para->match_rule = para->ck_para.match_rule; ck_para->max_color = para->ck_para.max_color; ck_para->min_color = para->ck_para.min_color; g2d_ext_hd.finish_flag = 0; err = g2d_bsp_bld(src, dst, para->bld_cmd, ck_para); return err; } int g2d_maskblt_h(g2d_maskblt *para) { __s32 err = 0; g2d_image_enh src_tmp, dst_tmp, ptn_tmp, mask_tmp; g2d_image_enh *src = &src_tmp; g2d_image_enh *dst = &dst_tmp; g2d_image_enh *ptn = &ptn_tmp; g2d_image_enh *mask = &mask_tmp; memset(src, 0, sizeof(g2d_image_enh)); memset(dst, 0, sizeof(g2d_image_enh)); memset(ptn, 0, sizeof(g2d_image_enh)); memset(mask, 0, sizeof(g2d_image_enh)); /* check the parameter valid */ if (((para->dst_image_h.clip_rect.x < 0) && ((-para->dst_image_h.clip_rect.x) > para->dst_image_h.clip_rect.w)) || ((para->dst_image_h.clip_rect.y < 0) && ((-para->dst_image_h.clip_rect.y) > para->dst_image_h.clip_rect.h)) || ((para->dst_image_h.clip_rect.x > 0) && (para->dst_image_h.clip_rect.x > para->dst_image_h.width - 1)) || ((para->dst_image_h.clip_rect.y > 0) && (para->dst_image_h.clip_rect.y > para->dst_image_h.height - 1))) { pr_err("invalid maskblt parameter setting\n"); return -EINVAL; } else { if (((para->dst_image_h.clip_rect.x < 0) && ((-para->dst_image_h.clip_rect.x) < para->dst_image_h.clip_rect.w))) { para->dst_image_h.clip_rect.w = para->dst_image_h.clip_rect.w + para->dst_image_h.clip_rect.x; para->dst_image_h.clip_rect.x = 0; } else if ((para->dst_image_h.clip_rect.x + para->dst_image_h.clip_rect.w) > para->dst_image_h.width) { para->dst_image_h.clip_rect.w = para->dst_image_h.width - para->dst_image_h.clip_rect.x; } if (((para->dst_image_h.clip_rect.y < 0) && ((-para->dst_image_h.clip_rect.y) < para->dst_image_h.clip_rect.h))) { para->dst_image_h.clip_rect.h = para->dst_image_h.clip_rect.h + para->dst_image_h.clip_rect.y; para->dst_image_h.clip_rect.y = 0; } else if ((para->dst_image_h.clip_rect.y + para->dst_image_h.clip_rect.h) > para->dst_image_h.height) { para->dst_image_h.clip_rect.h = para->dst_image_h.height - para->dst_image_h.clip_rect.y; } } src->bbuff = 1; src->gamut = G2D_BT709; src->alpha = para->src_image_h.alpha; src->color = para->src_image_h.color; src->format = para->src_image_h.format; src->mode = para->src_image_h.mode; src->laddr[0] = para->src_image_h.laddr[0]; src->laddr[1] = para->src_image_h.laddr[1]; src->laddr[2] = para->src_image_h.laddr[2]; src->width = para->src_image_h.width; src->height = para->src_image_h.height; src->clip_rect.x = para->src_image_h.clip_rect.x; src->clip_rect.y = para->src_image_h.clip_rect.y; src->clip_rect.w = para->dst_image_h.clip_rect.w; src->clip_rect.h = para->dst_image_h.clip_rect.h; ptn->bbuff = 1; ptn->gamut = G2D_BT709; ptn->alpha = para->ptn_image_h.alpha; ptn->color = para->ptn_image_h.color; ptn->format = para->ptn_image_h.format; ptn->mode = para->ptn_image_h.mode; ptn->laddr[0] = para->ptn_image_h.laddr[0]; ptn->laddr[1] = para->ptn_image_h.laddr[1]; ptn->laddr[2] = para->ptn_image_h.laddr[2]; ptn->width = para->ptn_image_h.width; ptn->height = para->ptn_image_h.height; ptn->clip_rect.x = para->ptn_image_h.clip_rect.x; ptn->clip_rect.y = para->ptn_image_h.clip_rect.y; ptn->clip_rect.w = para->dst_image_h.clip_rect.w; ptn->clip_rect.h = para->dst_image_h.clip_rect.h; mask->bbuff = 1; mask->gamut = G2D_BT709; mask->alpha = para->mask_image_h.alpha; mask->color = para->mask_image_h.color; mask->format = para->mask_image_h.format; mask->laddr[0] = para->mask_image_h.laddr[0]; mask->laddr[1] = para->mask_image_h.laddr[1]; mask->laddr[2] = para->mask_image_h.laddr[2]; mask->width = para->mask_image_h.width; mask->height = para->mask_image_h.height; mask->clip_rect.x = para->mask_image_h.clip_rect.x; mask->clip_rect.y = para->mask_image_h.clip_rect.y; mask->clip_rect.w = para->dst_image_h.clip_rect.w; mask->clip_rect.h = para->dst_image_h.clip_rect.h; dst->bbuff = 1; dst->gamut = G2D_BT709; dst->alpha = para->dst_image_h.alpha; dst->color = para->dst_image_h.color; dst->format = para->dst_image_h.format; dst->mode = para->dst_image_h.mode; dst->laddr[0] = para->dst_image_h.laddr[0]; dst->laddr[1] = para->dst_image_h.laddr[1]; dst->laddr[2] = para->dst_image_h.laddr[2]; dst->width = para->dst_image_h.width; dst->height = para->dst_image_h.height; dst->clip_rect.x = para->dst_image_h.clip_rect.x; dst->clip_rect.y = para->dst_image_h.clip_rect.y; dst->clip_rect.w = para->dst_image_h.clip_rect.w; dst->clip_rect.h = para->dst_image_h.clip_rect.h; g2d_ext_hd.finish_flag = 0; err = g2d_bsp_maskblt(src, ptn, mask, dst, para->back_flag, para->fore_flag); return err; } #endif /*int g2d_set_palette_table(g2d_palette *para) { if ((para->pbuffer == NULL) || (para->size < 0) || (para->size > 1024)) { printk("para invalid in mixer_set_palette\n"); return -1; } mixer_set_palette(para); return 0; } */ /*int g2d_cmdq(unsigned int para) { __s32 err = 0; g2d_ext_hd.finish_flag = 0; err = mixer_cmdq(para); return err; } */ long g2d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { __s32 ret = 0; if (!mutex_trylock(¶.mutex)) mutex_lock(¶.mutex); switch (cmd) { /* Proceed to the operation */ case G2D_CMD_BITBLT:{ g2d_blt blit_para; if (copy_from_user(&blit_para, (g2d_blt *) arg, sizeof(g2d_blt))) { kfree(&blit_para); ret = -EFAULT; goto err_noput; } ret = g2d_blit(&blit_para); break; } case G2D_CMD_FILLRECT:{ g2d_fillrect fill_para; if (copy_from_user(&fill_para, (g2d_fillrect *) arg, sizeof(g2d_fillrect))) { kfree(&fill_para); ret = -EFAULT; goto err_noput; } ret = g2d_fill(&fill_para); break; } case G2D_CMD_STRETCHBLT:{ g2d_stretchblt stre_para; if (copy_from_user(&stre_para, (g2d_stretchblt *) arg, sizeof(g2d_stretchblt))) { kfree(&stre_para); ret = -EFAULT; goto err_noput; } ret = g2d_stretchblit(&stre_para); break; } /* case G2D_CMD_PALETTE_TBL:{ g2d_palette pale_para; if (copy_from_user(&pale_para, (g2d_palette *)arg, sizeof(g2d_palette))) { kfree(&pale_para); ret = -EFAULT; goto err_noput; } ret = g2d_set_palette_table(&pale_para); break; } case G2D_CMD_QUEUE:{ unsigned int cmdq_addr; if (copy_from_user(&cmdq_addr, (unsigned int *)arg, sizeof(unsigned int))) { kfree(&cmdq_addr); ret = -EFAULT; goto err_noput; } ret = g2d_cmdq(cmdq_addr); break; } */ #ifdef G2D_V2X_SUPPORT case G2D_CMD_BITBLT_H:{ g2d_blt_h blit_para; if (copy_from_user(&blit_para, (g2d_blt_h *) arg, sizeof(g2d_blt_h))) { kfree(&blit_para); ret = -EFAULT; goto err_noput; } ret = g2d_blit_h(&blit_para); break; } case G2D_CMD_FILLRECT_H:{ g2d_fillrect_h fill_para; if (copy_from_user(&fill_para, (g2d_fillrect_h *) arg, sizeof(g2d_fillrect_h))) { kfree(&fill_para); ret = -EFAULT; goto err_noput; } ret = g2d_fill_h(&fill_para); break; } case G2D_CMD_BLD_H:{ g2d_bld bld_para; if (copy_from_user(&bld_para, (g2d_bld *) arg, sizeof(g2d_bld))) { kfree(&bld_para); ret = -EFAULT; goto err_noput; } ret = g2d_bld_h(&bld_para); break; } case G2D_CMD_MASK_H:{ g2d_maskblt mask_para; if (copy_from_user(&mask_para, (g2d_maskblt *) arg, sizeof(g2d_maskblt))) { kfree(&mask_para); ret = -EFAULT; goto err_noput; } ret = g2d_maskblt_h(&mask_para); break; } #endif /* just management memory for test */ case G2D_CMD_MEM_REQUEST: ret = g2d_mem_request(arg); break; case G2D_CMD_MEM_RELEASE: ret = g2d_mem_release(arg); break; case G2D_CMD_MEM_SELIDX: g2d_mem_sel = arg; break; case G2D_CMD_MEM_GETADR: if (g2d_mem[arg].b_used) { ret = g2d_mem[arg].phy_addr; } else { ERR("mem not used in G2D_CMD_MEM_GETADR\n"); ret = -1; } break; case G2D_CMD_INVERTED_ORDER: { if (arg > G2D_SM_DTRL) { ERR("scan mode is err.\n"); ret = -EINVAL; goto err_noput; } mutex_lock(&global_lock); scan_order = arg; mutex_unlock(&global_lock); break; } /* Invalid IOCTL call */ default: return -EINVAL; } err_noput: mutex_unlock(¶.mutex); return ret; } static struct file_operations g2d_fops = { .owner = THIS_MODULE, .open = g2d_open, .release = g2d_release, .unlocked_ioctl = g2d_ioctl, .mmap = g2d_mmap, }; static int g2d_probe(struct platform_device *pdev) { #if !defined(CONFIG_OF) int size; struct resource *res; #endif int ret = 0; __g2d_info_t *info = NULL; info = ¶ info->dev = &pdev->dev; platform_set_drvdata(pdev, info); #if !defined(CONFIG_OF) /* get the memory region */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { ERR("failed to get memory register\n"); ret = -ENXIO; goto dealloc_fb; } size = (res->end - res->start) + 1; /* map the memory */ info->io = ioremap(res->start, size); if (info->io == NULL) { ERR("iorGmap() of register failed\n"); ret = -ENXIO; goto dealloc_fb; } #else info->io = of_iomap(pdev->dev.of_node, 0); if (info->io == NULL) { ERR("iormap() of register failed\n"); ret = -ENXIO; goto dealloc_fb; } #endif #if !defined(CONFIG_OF) /* get the irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { ERR("failed to get irq resource\n"); ret = -ENXIO; goto release_regs; } info->irq = res->start; #else info->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (!info->irq) { ERR("irq_of_parse_and_map irq fail for transform\n"); ret = -ENXIO; goto release_regs; } #endif /* request the irq */ ret = request_irq(info->irq, g2d_handle_irq, 0, dev_name(&pdev->dev), NULL); if (ret) { ERR("failed to install irq resource\n"); goto release_regs; } #if defined(CONFIG_OF) /* clk init */ info->clk = of_clk_get(pdev->dev.of_node, 0); if (IS_ERR(info->clk)) ERR("fail to get clk\n"); #endif drv_g2d_init(); mutex_init(&info->mutex); mutex_init(&global_lock); return 0; release_regs: #if !defined(CONFIG_OF) iounmap(info->io); #endif dealloc_fb: platform_set_drvdata(pdev, NULL); return ret; } static int g2d_remove(struct platform_device *pdev) { __g2d_info_t *info = platform_get_drvdata(pdev); free_irq(info->irq, NULL); #if !defined(CONFIG_OF) iounmap(info->io); #endif platform_set_drvdata(pdev, NULL); 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); } 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) clk_prepare_enable(para.clk); } mutex_unlock(¶.mutex); INFO("g2d_resume succesfully.\n"); return 0; } #if !defined(CONFIG_OF) struct platform_device g2d_device = { .name = "g2d", .id = -1, .num_resources = ARRAY_SIZE(g2d_resource), .resource = g2d_resource, .dev = { }, }; #else static const struct of_device_id sunxi_g2d_match[] = { {.compatible = "allwinner,sunxi-g2d",}, {}, }; #endif static struct platform_driver g2d_driver = { .probe = g2d_probe, .remove = g2d_remove, .suspend = g2d_suspend, .resume = g2d_resume, .suspend = NULL, .resume = NULL, .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) { ERR("I was assigned major number %d.\n", MAJOR(devid)); return -1; } g2d_class = class_create(THIS_MODULE, "g2d_class"); if (IS_ERR(g2d_class)) { ERR("create class error\n"); return -1; } device_create(g2d_class, NULL, devid, NULL, "g2d"); #if !defined(CONFIG_OF) ret = platform_device_register(&g2d_device); #endif if (ret == 0) ret = platform_driver_register(&g2d_driver); INFO("Module initialized.major:%d\n", MAJOR(devid)); return ret; } static void __exit g2d_module_exit(void) { INFO("g2d_module_exit\n"); kfree(g2d_ext_hd.g2d_finished_sem); platform_driver_unregister(&g2d_driver); #if !defined(CONFIG_OF) platform_device_unregister(&g2d_device); #endif device_destroy(g2d_class, devid); class_destroy(g2d_class); cdev_del(g2d_cdev); } module_init(g2d_module_init); module_exit(g2d_module_exit); MODULE_AUTHOR("yupu_tang"); MODULE_AUTHOR("tyle "); MODULE_DESCRIPTION("g2d driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:g2d");