diff options
Diffstat (limited to 'drivers/media/video/samsung/fimg2d4x-exynos4')
7 files changed, 239 insertions, 24 deletions
diff --git a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d.h b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d.h index 2d8f3b7..3bbb194 100644 --- a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d.h +++ b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d.h @@ -41,6 +41,15 @@ #define FIMG2D_BITBLT_SYNC _IOW(FIMG2D_IOCTL_MAGIC, 1, int) #define FIMG2D_BITBLT_VERSION _IOR(FIMG2D_IOCTL_MAGIC, 2, struct fimg2d_version) #define FIMG2D_BITBLT_SECURE _IOW(FIMG2D_IOCTL_MAGIC, 3, unsigned int) +#define FIMG2D_BITBLT_DBUFFER _IOW(FIMG2D_IOCTL_MAGIC, 4, unsigned long) + +#define SEQ_NO_BLT_SKIA 0x00000001 +#define SEQ_NO_BLT_HWC_SEC 0x00000012 +#define SEQ_NO_BLT_HWC_NOSEC 0x00000002 +#define SEQ_NO_BLT_HDMI 0x00000003 +#define SEQ_NO_CMD_SECURE_ON 0x10000001 +#define SEQ_NO_CMD_SECURE_OFF 0x10000002 +#define SEQ_NO_CMD_SET_DBUFFER 0x10000003 struct fimg2d_version { unsigned int hw; @@ -429,6 +438,7 @@ struct fimg2d_context { atomic_t ncmd; wait_queue_head_t wait_q; struct fimg2d_perf perf[MAX_PERF_DESCS]; + unsigned long *pgd_clone; }; /** @@ -486,6 +496,8 @@ struct fimg2d_control { int irq; unsigned int secure; + unsigned int dbuffer_addr; + unsigned long fault_addr; atomic_t nctx; atomic_t busy; atomic_t active; diff --git a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d4x_blt.c b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d4x_blt.c index fc6016c..1eb8d63 100644 --- a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d4x_blt.c +++ b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d4x_blt.c @@ -83,7 +83,12 @@ void fimg2d4x_bitblt(struct fimg2d_control *info) goto blitend; if (cmd->image[IDST].addr.type != ADDR_PHYS) { - pgd = (unsigned long *)ctx->mm->pgd; + if ((cmd->image[IDST].addr.type == ADDR_USER_CONTIG) || + (cmd->image[ISRC].addr.type == ADDR_USER_CONTIG)) + pgd = (unsigned long *)ctx->pgd_clone; + else + pgd = (unsigned long *)ctx->mm->pgd; + s5p_sysmmu_enable(info->dev, (unsigned long)virt_to_phys(pgd)); fimg2d_debug("sysmmu enable: pgd %p ctx %p seq_no(%u)\n", pgd, ctx, cmd->seq_no); @@ -98,6 +103,8 @@ void fimg2d4x_bitblt(struct fimg2d_control *info) info->run(info); fimg2d4x_blit_wait(info, cmd); + if (info->fault_addr) + fimg2d_mmutable_value_replace(cmd, info->fault_addr, 0); #ifdef PERF_PROFILE perf_end(cmd->ctx, PERF_BLIT); #endif diff --git a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d4x_hw.c b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d4x_hw.c index 4eb4d04..7835dee 100644 --- a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d4x_hw.c +++ b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d4x_hw.c @@ -16,6 +16,7 @@ #include "fimg2d.h" #include "fimg2d4x.h" #include "fimg2d_clk.h" +#include "fimg2d_cache.h" #define wr(d, a) writel((d), info->regs + (a)) #define rd(a) readl(info->regs + (a)) @@ -124,7 +125,13 @@ void fimg2d4x_set_src_image(struct fimg2d_control *info, struct fimg2d_image *s) { unsigned long cfg; - wr(FIMG2D_ADDR(s->addr.start), FIMG2D_SRC_BASE_ADDR_REG); + if ((s->addr.type == ADDR_USER_CONTIG) && (s->order < ARGB_ORDER_END)) { + wr(FIMG2D_ADDR(GET_MVA(s->addr.start, s->plane2.start)), + FIMG2D_SRC_BASE_ADDR_REG); + } else { + wr(FIMG2D_ADDR(s->addr.start), FIMG2D_SRC_BASE_ADDR_REG); + } + wr(FIMG2D_STRIDE(s->stride), FIMG2D_SRC_STRIDE_REG); if (s->order < ARGB_ORDER_END) { /* argb */ @@ -173,7 +180,13 @@ void fimg2d4x_set_dst_image(struct fimg2d_control *info, struct fimg2d_image *d) { unsigned long cfg; - wr(FIMG2D_ADDR(d->addr.start), FIMG2D_DST_BASE_ADDR_REG); + if ((d->addr.type == ADDR_USER_CONTIG) && (d->order < ARGB_ORDER_END)) { + wr(FIMG2D_ADDR(GET_MVA(d->addr.start, d->plane2.start)), + FIMG2D_DST_BASE_ADDR_REG); + } else { + wr(FIMG2D_ADDR(d->addr.start), FIMG2D_DST_BASE_ADDR_REG); + } + wr(FIMG2D_STRIDE(d->stride), FIMG2D_DST_STRIDE_REG); if (d->order < ARGB_ORDER_END) { diff --git a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_cache.c b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_cache.c index 43489e4..f5486b1 100644 --- a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_cache.c +++ b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_cache.c @@ -114,8 +114,48 @@ void fimg2d_clean_outer_pagetable(struct mm_struct *mm, unsigned long vaddr, lv1++; } while (lv1 != lv1end); } + +void fimg2d_clean_outer_pagetable_clone(unsigned long *pgd_clone, + unsigned long vaddr, size_t size) +{ + unsigned long *pgd; + unsigned long *lv1, *lv1end; + unsigned long lv2pa; + + pgd = (unsigned long *)pgd_clone; + + lv1 = pgd + (vaddr >> LV1_SHIFT); + lv1end = pgd + ((vaddr + size + LV1_PT_SIZE-1) >> LV1_SHIFT); + + /* clean level1 page table */ + outer_clean_range(virt_to_phys(lv1), virt_to_phys(lv1end)); + + do { + /* if page size is 4KB, clean level2 page table entry */ + if ((*lv1 & 0x3) == 0x1) { + lv2pa = *lv1 & ~LV2_BASE_MASK; /* lv2 pt base */ + /* clean level2 page table */ + outer_clean_range(lv2pa, lv2pa + LV2_PT_SIZE); + } + lv1++; + } while (lv1 != lv1end); +} #endif /* CONFIG_OUTER_CACHE */ +void fimg2d_clean_inner_pagetable_clone(unsigned long *pgd_clone, + unsigned long vaddr, size_t size) +{ + unsigned long *pgd; + unsigned long *lv1, *lv1end; + + pgd = (unsigned long *)pgd_clone; + + lv1 = pgd + (vaddr >> LV1_SHIFT); + lv1end = pgd + ((vaddr + size + LV1_PT_SIZE-1) >> LV1_SHIFT); + fimg2d_dma_sync_inner((unsigned long)lv1, + (unsigned int)lv1end - (unsigned int)lv1, DMA_TO_DEVICE); +} + enum pt_status fimg2d_check_pagetable(struct mm_struct *mm, unsigned long vaddr, size_t size) { @@ -166,3 +206,50 @@ enum pt_status fimg2d_check_pagetable(struct mm_struct *mm, unsigned long vaddr, return PT_NORMAL; } + +#define PT_NS 0x0 // Non Secure +#define PT_AP 0x8c00 // Access permission +#define PT_ENTRY 0x2 // 1MB Page + +enum pt_status fimg2d_migrate_pagetable(unsigned long *pgd_clone, + unsigned long vaddr, unsigned long paddr, size_t size) +{ + unsigned long *pgd; + unsigned long *lv1d; + + pgd = (unsigned long *)pgd_clone; + + size += vaddr & (SZ_1M - 1); + size = ALIGN(size, SZ_1M); + + while ((long)size > 0) { + lv1d = pgd + (vaddr >> LV1_SHIFT); + + *lv1d = (paddr & 0xfff00000) | PT_NS | PT_AP | PT_ENTRY; + + vaddr += SZ_1M; + paddr += SZ_1M; + size -= SZ_1M; + } + return PT_NORMAL; +} + +void fimg2d_mmutable_value_replace(struct fimg2d_bltcmd *cmd, + unsigned long fault_addr, unsigned long l2d_value) +{ + unsigned long *pgd; + unsigned long *lv1d, *lv2d; + + pgd = (unsigned long *)cmd->ctx->mm->pgd; + lv1d = pgd + (fault_addr >> LV1_SHIFT); + + lv2d = (unsigned long *)phys_to_virt(*lv1d & ~LV2_BASE_MASK) + + ((fault_addr & LV2_PT_MASK) >> LV2_SHIFT); + + *lv2d = l2d_value; + + flush_all_cpu_caches(); + fimg2d_clean_outer_pagetable(cmd->ctx->mm, fault_addr, 4); + + printk(KERN_INFO "MMU Level2 value replaced [0x%lx]", l2d_value); +} diff --git a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_cache.h b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_cache.h index f337ea5..7699a37 100644 --- a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_cache.h +++ b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_cache.h @@ -18,6 +18,10 @@ #define L1_CACHE_SIZE SZ_64K #define L2_CACHE_SIZE SZ_1M #define LINE_FLUSH_THRESHOLD SZ_1K +#define L1_DESCRIPTOR_SIZE SZ_16K + +/* Get Modified virtual address to use 1MB page */ +#define GET_MVA(V, P) ((V & 0xfff00000) | (P & 0x000fffff)) /** * cache_opr - [kernel] cache operation mode @@ -92,5 +96,11 @@ static inline void fimg2d_dma_unsync_inner(unsigned long addr, size_t size, int } void fimg2d_clean_outer_pagetable(struct mm_struct *mm, unsigned long addr, size_t size); +void fimg2d_clean_outer_pagetable_clone(unsigned long *pgd_clone, unsigned long addr, size_t size); +void fimg2d_clean_inner_pagetable_clone(unsigned long *pgd_clone, unsigned long addr, size_t size); void fimg2d_dma_sync_outer(struct mm_struct *mm, unsigned long addr, size_t size, enum cache_opr opr); enum pt_status fimg2d_check_pagetable(struct mm_struct *mm, unsigned long addr, size_t size); +enum pt_status fimg2d_migrate_pagetable(unsigned long *pgd_clone, + unsigned long vaddr, unsigned long paddr, size_t size); +void fimg2d_mmutable_value_replace(struct fimg2d_bltcmd *cmd, + unsigned long fault_addr, unsigned long l2d_value); diff --git a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_ctx.c b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_ctx.c index eaa722c..0c6c590 100644 --- a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_ctx.c +++ b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_ctx.c @@ -130,6 +130,7 @@ static int fimg2d_check_dma_sync(struct fimg2d_bltcmd *cmd) enum pt_status pt; int clip_x, clip_w, clip_h, y, dir, i; unsigned long clip_start; + unsigned long modified_addr; clp = &p->clipping; @@ -155,6 +156,13 @@ static int fimg2d_check_dma_sync(struct fimg2d_bltcmd *cmd) pt = fimg2d_check_pagetable(mm, c->addr, c->size); if (pt == PT_FAULT) return -1; + } else if (img->addr.type == ADDR_USER_CONTIG) { + modified_addr = GET_MVA(img->addr.start, img->plane2.start); + pt = fimg2d_migrate_pagetable(cmd->ctx->pgd_clone, + modified_addr, img->plane2.start, img->height * img->stride); + if (pt != PT_NORMAL) { + return -1; + } } if (img->need_cacheopr && i != IMAGE_TMP) { @@ -175,7 +183,25 @@ static int fimg2d_check_dma_sync(struct fimg2d_bltcmd *cmd) c = &cmd->dma[i]; r = &img->rect; - if (!img->addr.type || !c->cached) + if (!img->addr.type) + continue; + + if ((cmd->image[IMAGE_SRC].addr.type == ADDR_USER_CONTIG) || + (cmd->image[IMAGE_DST].addr.type == ADDR_USER_CONTIG)) { + if (img->addr.type == ADDR_USER_CONTIG) { + if (i == IMAGE_DST && clp->enable) + modified_addr = GET_MVA(img->addr.start, img->plane2.start) + + (img->stride * clp->y1); + else + modified_addr = GET_MVA(img->addr.start, img->plane2.start) + + (img->stride * r->y1); + } else { + modified_addr = c->addr; + } + fimg2d_clean_inner_pagetable_clone(cmd->ctx->pgd_clone, modified_addr, c->size); + } + + if ( !c->cached) continue; if (i == IMAGE_DST) @@ -226,8 +252,22 @@ static int fimg2d_check_dma_sync(struct fimg2d_bltcmd *cmd) continue; /* clean pagetable */ - if (img->addr.type == ADDR_USER) + if ((cmd->image[IMAGE_SRC].addr.type == ADDR_USER_CONTIG) || + (cmd->image[IMAGE_DST].addr.type == ADDR_USER_CONTIG)) { + if (img->addr.type == ADDR_USER_CONTIG) { + if (i == IMAGE_DST && clp->enable) + modified_addr = GET_MVA(img->addr.start, img->plane2.start) + + (img->stride * clp->y1); + else + modified_addr = GET_MVA(img->addr.start, img->plane2.start) + + (img->stride * r->y1); + } else { + modified_addr = c->addr; + } + fimg2d_clean_outer_pagetable_clone(cmd->ctx->pgd_clone, modified_addr, c->size); + } else { fimg2d_clean_outer_pagetable(mm, c->addr, c->size); + } if (!c->cached) continue; @@ -275,14 +315,17 @@ int fimg2d_add_command(struct fimg2d_control *info, struct fimg2d_context *ctx, int i, ret; struct fimg2d_image *buf[MAX_IMAGES] = image_table(blit); struct fimg2d_bltcmd *cmd; + struct fimg2d_image *img; - if ((blit->dst) && (type == ADDR_USER)) + if ((blit->dst) && (type == ADDR_USER) + && (blit->seq_no == SEQ_NO_BLT_SKIA)) up_write(&page_alloc_slow_rwsem); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { - if ((blit->dst) && (type == ADDR_USER)) + if ((blit->dst) && (type == ADDR_USER) + && (blit->seq_no == SEQ_NO_BLT_SKIA)) if (!down_write_trylock(&page_alloc_slow_rwsem)) return -EAGAIN; return -ENOMEM; @@ -294,7 +337,8 @@ int fimg2d_add_command(struct fimg2d_control *info, struct fimg2d_context *ctx, if (copy_from_user(&cmd->image[i], buf[i], sizeof(struct fimg2d_image))) { - if ((blit->dst) && (type == ADDR_USER)) + if ((blit->dst) && (type == ADDR_USER) + && (blit->seq_no == SEQ_NO_BLT_SKIA)) if (!down_write_trylock(&page_alloc_slow_rwsem)) { ret = -EAGAIN; goto err_user; @@ -304,7 +348,8 @@ int fimg2d_add_command(struct fimg2d_control *info, struct fimg2d_context *ctx, } } - if ((blit->dst) && (type == ADDR_USER)) + if ((blit->dst) && (type == ADDR_USER) + && (blit->seq_no == SEQ_NO_BLT_SKIA)) if (!down_write_trylock(&page_alloc_slow_rwsem)) { ret = -EAGAIN; goto err_user; @@ -329,6 +374,13 @@ int fimg2d_add_command(struct fimg2d_control *info, struct fimg2d_context *ctx, fimg2d_fixup_params(cmd); + for (i = 0; i < MAX_IMAGES; i++) { + img = &cmd->image[i]; + if (img->addr.type == ADDR_USER_CONTIG) { + memcpy(cmd->ctx->pgd_clone, cmd->ctx->mm->pgd, L1_DESCRIPTOR_SIZE); + } + } + if (fimg2d_check_dma_sync(cmd)) { ret = -EFAULT; goto err_user; diff --git a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_drv.c b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_drv.c index ed14901..84c7db5 100644 --- a/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_drv.c +++ b/drivers/media/video/samsung/fimg2d4x-exynos4/fimg2d_drv.c @@ -46,6 +46,8 @@ #define LV2_PT_MASK 0xff000 #define LV2_SHIFT 12 #define LV1_DESC_MASK 0x3 +#define LV2_VALUE_META 0xc7f +#define LV2_VALUE_BASE_MASK 0xfff static struct fimg2d_control *info; @@ -116,7 +118,12 @@ static int fimg2d_sysmmu_fault_handler(enum S5P_SYSMMU_INTERRUPT_TYPE itype, lv2d = (unsigned long *)phys_to_virt(*lv1d & ~LV2_BASE_MASK) + ((fault_addr & LV2_PT_MASK) >> LV2_SHIFT); printk(KERN_ERR " Level 2 descriptor(0x%lx)\n", *lv2d); - fimg2d_clean_outer_pagetable(cmd->ctx->mm, fault_addr, 4); + if (*lv2d == 0) { + fimg2d_mmutable_value_replace(cmd, fault_addr, + (info->dbuffer_addr & ~LV2_VALUE_BASE_MASK) | LV2_VALUE_META); + info->fault_addr = fault_addr; + } else + fimg2d_clean_outer_pagetable(cmd->ctx->mm, fault_addr, 4); next: return 0; @@ -161,6 +168,8 @@ static int fimg2d_open(struct inode *inode, struct file *file) ctx, (unsigned long *)ctx->mm->pgd, (unsigned long *)init_mm.pgd); + ctx->pgd_clone = kzalloc(L1_DESCRIPTOR_SIZE, GFP_KERNEL); + fimg2d_add_context(info, ctx); return 0; } @@ -178,6 +187,7 @@ static int fimg2d_release(struct inode *inode, struct file *file) } fimg2d_del_context(info, ctx); + kfree(ctx->pgd_clone); kfree(ctx); return 0; } @@ -223,7 +233,8 @@ static long fimg2d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) dev_lock(info->bus_dev, info->dev, 160160); #endif #endif - if ((blit.dst) && (dst.addr.type == ADDR_USER)) + if ((blit.dst) && (dst.addr.type == ADDR_USER) + && (blit.seq_no == SEQ_NO_BLT_SKIA)) if (!down_write_trylock(&page_alloc_slow_rwsem)) ret = -EAGAIN; @@ -238,7 +249,9 @@ static long fimg2d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) perf_print(ctx, blit.seq_no); perf_clear(ctx); #endif - if ((blit.dst) && (dst.addr.type == ADDR_USER) && ret != -EAGAIN) + if ((blit.dst) && (dst.addr.type == ADDR_USER) + && (blit.seq_no == SEQ_NO_BLT_SKIA) + && ret != -EAGAIN) up_write(&page_alloc_slow_rwsem); #ifdef CONFIG_BUSFREQ_OPP @@ -246,6 +259,13 @@ static long fimg2d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) dev_unlock(info->bus_dev, info->dev); #endif #endif + + if (info->fault_addr) { + printk(KERN_INFO "Return by G2D fault handler"); + info->fault_addr = 0; + ret = -EFAULT; + } + break; case FIMG2D_BITBLT_SYNC: @@ -281,6 +301,17 @@ static long fimg2d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; + case FIMG2D_BITBLT_DBUFFER: + if (copy_from_user(&info->dbuffer_addr, + (unsigned long *)arg, + sizeof(unsigned long))) { + printk(KERN_ERR + "[%s] failed to FIMG2D_BITBLT_DBUFFER: copy_from_user error\n\n", + __func__); + return -EFAULT; + } + break; + default: printk(KERN_ERR "[%s] unknown ioctl\n", __func__); ret = -EFAULT; @@ -315,6 +346,7 @@ static int fimg2d_setup_controller(struct fimg2d_control *info) atomic_set(&info->nctx, 0); atomic_set(&info->active, 0); info->secure = 0; + info->fault_addr = 0; spin_lock_init(&info->bltlock); @@ -384,12 +416,20 @@ static int fimg2d_probe(struct platform_device *pdev) fimg2d_debug("device name: %s base address: 0x%lx\n", pdev->name, (unsigned long)res->start); + /* Clock setup */ + ret = fimg2d_clk_setup(info); + if (ret) { + printk(KERN_ERR "FIMG2D failed to setup clk\n"); + ret = -ENOENT; + goto err_clk; + } + /* irq */ info->irq = platform_get_irq(pdev, 0); if (!info->irq) { printk(KERN_ERR "FIMG2D failed to get irq resource\n"); ret = -ENOENT; - goto err_map; + goto err_irq; } fimg2d_debug("irq: %d\n", info->irq); @@ -400,13 +440,6 @@ static int fimg2d_probe(struct platform_device *pdev) goto err_irq; } - ret = fimg2d_clk_setup(info); - if (ret) { - printk(KERN_ERR "FIMG2D failed to setup clk\n"); - ret = -ENOENT; - goto err_clk; - } - #ifdef CONFIG_PM_RUNTIME pm_runtime_enable(info->dev); fimg2d_debug("enable runtime pm\n"); @@ -432,19 +465,20 @@ static int fimg2d_probe(struct platform_device *pdev) return 0; err_reg: - fimg2d_clk_release(info); - -err_clk: free_irq(info->irq, NULL); err_irq: + fimg2d_clk_release(info); + +err_clk: iounmap(info->regs); err_map: + release_mem_region(res->start, resource_size(res)); kfree(info->mem); err_region: - release_resource(info->mem); + release_resource(res); err_res: destroy_workqueue(info->work_q); |