/* linux/drivers/media/video/samsung/g2d/s3c_fimg2d2x.c * * Driver file for Samsung 2D Accelerator(FIMG-2D) * * Jonghun Han, Copyright (c) 2009 Samsung Electronics * http://www.samsungsemi.com/ * * add some notes by tommy.hong@2009.05.09 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation.*/
由于时间不多,部分用了C++的注释风格,请原谅。#include <linux/module.h> module_init,module_exit#include <linux/kernel.h>#include <linux/errno.h>ENOENT and etc#include <linux/clk.h>//clk_get#include <linux/poll.h>//poll_table and etc#include <linux/fs.h>//more imporant file structure ,such as file_operation operation and etc#include <linux/irq.h>//IRQ_DISABLED and etc#include <linux/mm.h>//vm_area_struct#include <linux/interrupt.h>//IRQ and etc#include <linux/platform_device.h>//platform_device#include <linux/miscdevice.h>//misc_register#include <asm/io.h>//ioremap ,__raw_readl and __raw_writl and etc#include <mach/map.h>//FIMG2D Physical address definition#include <plat/regs-g2d.h>#include "s3c_fimg2d2x.h"static struct clk *s3c_g2d_clock;//for s3c_g2d_clk use by clk_getstatic struct clk *h_clk;//for s3c6410 HCLK,because fimg2d clk generate from devider of HCLKstatic int s3c_g2d_irq_num = NO_IRQ;//fimg2d irq ,initializestatic struct resource *s3c_g2d_mem;//resource for memory regionstatic void __iomem *s3c_g2d_base;//for ioremap to set FIMG2D registerstatic wait_queue_head_t waitq_g2d;//wait_queue_head_t use for kernel process schedulestatic struct mutex *h_rot_mutex;//Kernel lockstatic u16 s3c_g2d_poll_flag = 0;//use for Poll during processing,device driver need this/* * Here is S3C6410 FIMG2D command FIFO check or monitor ,must do it by user; * If FIFO Entries reach FIFO FULL ,and full interrupt occurs. * FIFO full number is 32. * */void s3c_g2d_check_fifo(int empty_fifo){ u32 val; val = __raw_readl(s3c_g2d_base + S3C_G2D_FIFO_STAT_REG); while( S3C_G2D_FIFO_USED(val) > (FIFO_NUM - empty_fifo));}/* * FIMG2D Register Initialize,when user use FIMG2D to accerate,must do this. * user open FIMG2D device node and ioctl,copy_from_user by params(such BPP,src_add,src_h,src_w, * dest_h,dest_w,and rotation mode ,and etc) let FIMG2D driver initialize,FIMG2D initalize FIMG2D whole * register,take S3C6410 data sheet as refer * * @Todo:s3c_g2d_params ===>copy_from_user transfer GUI application funtion param to Driver * * */static int s3c_g2d_init_regs(s3c_g2d_params *params){ u32 bpp_mode; u32 tmp_reg; s3c_g2d_check_fifo(25); switch(params->bpp) { case ARGB8: bpp_mode = S3C_G2D_COLOR_MODE_REG_C0_15BPP; break; case RGB16: bpp_mode = S3C_G2D_COLOR_RGB_565; break; case RGB18: bpp_mode = S3C_G2D_COLOR_MODE_REG_C2_18BPP; break; case RGB24: bpp_mode = S3C_G2D_COLOR_XRGB_8888; break; default: bpp_mode = S3C_G2D_COLOR_MODE_REG_C3_24BPP; break; } /*set register for soruce image ===============================*/ __raw_writel(params->src_base_addr, s3c_g2d_base + S3C_G2D_SRC_BASE_ADDR); __raw_writel(params->src_full_width, s3c_g2d_base + S3C_G2D_HORI_RES_REG); __raw_writel(params->src_full_height, s3c_g2d_base + S3C_G2D_VERT_RES_REG); __raw_writel((S3C_G2D_FULL_V(params->src_full_height) | S3C_G2D_FULL_H(params->src_full_width)), s3c_g2d_base+S3C_G2D_SRC_RES_REG); __raw_writel(bpp_mode, s3c_g2d_base + S3C_G2D_SRC_COLOR_MODE); /*set register for destination image =============================*/ __raw_writel(params->dst_base_addr, s3c_g2d_base + S3C_G2D_DST_BASE_ADDR); __raw_writel(params->dst_full_width, s3c_g2d_base + S3C_G2D_SC_HORI_REG); __raw_writel(params->dst_full_height, s3c_g2d_base + S3C_G2D_SC_VERT_REG); __raw_writel((S3C_G2D_FULL_V(params->dst_full_height) | S3C_G2D_FULL_H(params->dst_full_width)), s3c_g2d_base+S3C_G2D_SC_RES_REG); __raw_writel(bpp_mode, s3c_g2d_base + S3C_G2D_DST_COLOR_MODE); /*set register for clipping window===============================*/ __raw_writel(params->cw_x1, s3c_g2d_base + S3C_G2D_CW_LT_X_REG); __raw_writel(params->cw_y1, s3c_g2d_base + S3C_G2D_CW_LT_Y_REG); __raw_writel(params->cw_x2, s3c_g2d_base + S3C_G2D_CW_RB_X_REG); __raw_writel(params->cw_y2, s3c_g2d_base + S3C_G2D_CW_RB_Y_REG); /*set register for color=======================================*/ __raw_writel(params->color_val[G2D_WHITE], s3c_g2d_base + S3C_G2D_FG_COLOR_REG); /* set color to both font and foreground color */ __raw_writel(params->color_val[G2D_BLACK], s3c_g2d_base + S3C_G2D_BG_COLOR_REG); __raw_writel(params->color_val[G2D_BLUE], s3c_g2d_base + S3C_G2D_BS_COLOR_REG); /* Set blue color to blue screen color */ /*set register for ROP & Alpha==================================*/ if(params->alpha_mode == TRUE) { if(params->alpha_val > ALPHA_VALUE_MAX) { printk(KERN_ALERT "s3c g2d dirver error: exceed alpha value range 0~255/n"); return -ENOENT; } __raw_writel(S3C_G2D_ROP_REG_OS_FG_COLOR | S3C_G2D_ROP_REG_ABM_REGISTER | S3C_G2D_ROP_REG_T_OPAQUE_MODE | G2D_ROP_SRC_ONLY, s3c_g2d_base + S3C_G2D_ROP_REG); __raw_writel(S3C_G2D_ALPHA(params->alpha_val), s3c_g2d_base + S3C_G2D_ALPHA_REG); } else { __raw_writel(S3C_G2D_ROP_REG_OS_FG_COLOR | S3C_G2D_ROP_REG_ABM_NO_BLENDING | S3C_G2D_ROP_REG_T_OPAQUE_MODE | G2D_ROP_SRC_ONLY, s3c_g2d_base + S3C_G2D_ROP_REG); __raw_writel(S3C_G2D_ALPHA(0x00), s3c_g2d_base + S3C_G2D_ALPHA_REG); } /*set register for color key====================================*/ if(params->color_key_mode == TRUE) { tmp_reg = __raw_readl(s3c_g2d_base + S3C_G2D_ROP_REG); tmp_reg |= S3C_G2D_ROP_REG_T_TRANSP_MODE ; __raw_writel(tmp_reg , s3c_g2d_base + S3C_G2D_ROP_REG); __raw_writel(params->color_key_val, s3c_g2d_base + S3C_G2D_BS_COLOR_REG); } /*set register for rotation=====================================*/ __raw_writel(S3C_G2D_ROTATRE_REG_R0_0, s3c_g2d_base + S3C_G2D_ROT_OC_X_REG); __raw_writel(S3C_G2D_ROTATRE_REG_R0_0, s3c_g2d_base + S3C_G2D_ROT_OC_Y_REG); __raw_writel(S3C_G2D_ROTATRE_REG_R0_0, s3c_g2d_base + S3C_G2D_ROTATE_REG); return 0;}/* * This funtion is very important,because user usually need Bitblt function to accerate; * eg,Xfbdev Kdrive need this accerate GUI performance. * * nothing to note ,just writel * * send address of SRC and DEST first,send BITBLT Command to FIFO ,begin bitblt * */void s3c_g2d_bitblt(u16 src_x1, u16 src_y1, u16 src_x2, u16 src_y2, u16 dst_x1, u16 dst_y1, u16 dst_x2, u16 dst_y2){ u32 cmd_reg_val; s3c_g2d_check_fifo(25); __raw_writel(src_x1, s3c_g2d_base + S3C_G2D_COORD0_X_REG); __raw_writel(src_y1, s3c_g2d_base + S3C_G2D_COORD0_Y_REG); __raw_writel(src_x2, s3c_g2d_base + S3C_G2D_COORD1_X_REG); __raw_writel(src_y2, s3c_g2d_base + S3C_G2D_COORD1_Y_REG); __raw_writel(dst_x1, s3c_g2d_base + S3C_G2D_COORD2_X_REG); __raw_writel(dst_y1, s3c_g2d_base + S3C_G2D_COORD2_Y_REG); __raw_writel(dst_x2, s3c_g2d_base + S3C_G2D_COORD3_X_REG); __raw_writel(dst_y2, s3c_g2d_base + S3C_G2D_COORD3_Y_REG); cmd_reg_val = readl(s3c_g2d_base + S3C_G2D_CMD1_REG); cmd_reg_val = ~(S3C_G2D_CMD1_REG_S|S3C_G2D_CMD1_REG_N); cmd_reg_val |= S3C_G2D_CMD1_REG_N; __raw_writel(cmd_reg_val, s3c_g2d_base + S3C_G2D_CMD1_REG);}static void s3c_g2d_rotate_with_bitblt(s3c_g2d_params *params, ROT_DEG rot_degree){ u16 org_x=0, org_y=0; u32 rot_reg_val = 0; u32 src_x1, src_y1, src_x2, src_y2, dst_x1, dst_y1, dst_x2, dst_y2; src_x1 = params->src_start_x; src_y1 = params->src_start_y; src_x2 = params->src_start_x + params->src_work_width; src_y2 = params->src_start_y + params->src_work_height; dst_x1 = params->dst_start_x; dst_y1 = params->dst_start_y; dst_x2 = params->dst_start_x + params->dst_work_width; dst_y2 = params->dst_start_y + params->dst_work_height; s3c_g2d_check_fifo(25); __raw_writel(S3C_G2D_INTEN_REG_CCF, s3c_g2d_base + S3C_G2D_INTEN_REG); s3c_g2d_get_rotation_origin(src_x1, src_y1, src_x2, src_y2, dst_x1, dst_y1, rot_degree, &org_x, &org_y); if(rot_degree != (ROT_0||ROT_X_FLIP||ROT_Y_FLIP)){ org_x += 1; org_y += 1; } __raw_writel(org_x, s3c_g2d_base + S3C_G2D_ROT_OC_X_REG); __raw_writel(org_y, s3c_g2d_base + S3C_G2D_ROT_OC_Y_REG); switch(rot_degree) { case ROT_0: rot_reg_val = S3C_G2D_ROTATRE_REG_R0_0; break; case ROT_90: rot_reg_val = S3C_G2D_ROTATRE_REG_R1_90; break; case ROT_180: rot_reg_val = S3C_G2D_ROTATRE_REG_R2_180; break; case ROT_270: rot_reg_val = S3C_G2D_ROTATRE_REG_R3_270; break; case ROT_X_FLIP: rot_reg_val = S3C_G2D_ROTATRE_REG_FX; break; case ROT_Y_FLIP: rot_reg_val = S3C_G2D_ROTATRE_REG_FY; break; default: printk(KERN_ERR "[%s : %d] Wrong rotation degree/n", __FUNCTION__, __LINE__); break; } __raw_writel(rot_reg_val, s3c_g2d_base + S3C_G2D_ROTATE_REG); switch(rot_degree) { case ROT_0: /* fall through */ case ROT_X_FLIP: /* fall through */ case ROT_Y_FLIP: s3c_g2d_bitblt(src_x1, src_y1, src_x2, src_y2, dst_x1, dst_y1, dst_x2, dst_y2); break; case ROT_90: s3c_g2d_bitblt(src_x1, src_y1, src_x2, src_y2, src_x1, src_y1+1, src_x2, src_y2+1); break; case ROT_180: s3c_g2d_bitblt(src_x1, src_y1, src_x2, src_y2, src_x1+1, src_y1+1, src_x2+1, src_y2+1); break; case ROT_270: s3c_g2d_bitblt(src_x1, src_y1, src_x2, src_y2, src_x1+1, src_y1, src_x2+1, src_y2); break; default: break; }}/*s3c_g2d_get_rotation_origin Get Memory region for rotation from source image,just memcopy*/static void s3c_g2d_get_rotation_origin(u16 src_x1, u16 src_y1, u16 src_x2, u16 src_y2, u16 dst_x1, u16 dst_y1, ROT_DEG rot_degree, u16* org_x, u16* org_y){ s3c_g2d_check_fifo(25); switch(rot_degree) { case ROT_90: *org_x = ((dst_x1 - dst_y1 + src_x1 + src_y2)>>1); *org_y = ((dst_x1 + dst_y1 - src_x1 + src_y2)>>1); break; case ROT_180: /* fall through */ case ROT_X_FLIP: /* fall through */ case ROT_Y_FLIP: *org_x = ((dst_x1 + src_x2)>>1); *org_y = ((dst_y1 + src_y2)>>1); break; case ROT_270: *org_x = ((dst_x1 + dst_y1 + src_x2 - src_y1)>>1); *org_y = ((dst_y1 - dst_x1 + src_x2 + src_y1)>>1); break; case ROT_0: /* fall through */ default: break; }}static void s3c_g2d_rotator_start(s3c_g2d_params *params, ROT_DEG rot_degree){ s3c_g2d_init_regs(params); s3c_g2d_rotate_with_bitblt(params, rot_degree);}/*s3c_g2d_irq */irqreturn_t s3c_g2d_irq(int irq, void *dev_id){ if(__raw_readl(s3c_g2d_base + S3C_G2D_INTC_PEND_REG) & S3C_G2D_PEND_REG_INTP_CMD_FIN) { __raw_writel ( S3C_G2D_PEND_REG_INTP_CMD_FIN, s3c_g2d_base + S3C_G2D_INTC_PEND_REG ); wake_up_interruptible(&waitq_g2d); s3c_g2d_poll_flag = 1; } return IRQ_HANDLED;}int s3c_g2d_open(struct inode *inode, struct file *file){ s3c_g2d_params *params; params = (s3c_g2d_params *)kmalloc(sizeof(s3c_g2d_params), GFP_KERNEL); if(params == NULL) { printk(KERN_ERR "Instance memory allocation was failed/n"); return -1; } memset(params, 0, sizeof(s3c_g2d_params)); file->private_data = (s3c_g2d_params *)params; printk("s3c_g2d_open() /n"); return 0;}int s3c_g2d_release(struct inode *inode, struct file *file){ s3c_g2d_params *params; params = (s3c_g2d_params *)file->private_data; if (params == NULL) { printk(KERN_ERR "Can't release s3c_rotator!!/n"); return -1; } kfree(params);//params's memory region is kmalloc ,so here kfree for memory safe printk("s3c_g2d_release() /n"); return 0;}int s3c_g2d_mmap(struct file* filp, struct vm_area_struct *vma) { unsigned long pageFrameNo = 0; unsigned long size; size = vma->vm_end - vma->vm_start; // page frame number of the address for a source G2D_SFR_SIZE to be stored at. pageFrameNo = __phys_to_pfn(s3c_g2d_mem->start); if(size > G2D_SFR_SIZE) { printk("The size of G2D_SFR_SIZE mapping is too big!/n"); return -EINVAL; } vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); if((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) { printk("Writable G2D_SFR_SIZE mapping must be shared !/n"); return -EINVAL; } if(remap_pfn_range(vma, vma->vm_start, pageFrameNo, size, vma->vm_page_prot)) return -EINVAL; return 0;}//Interface Of UserSpace and Kernel Driver Spacestatic int s3c_g2d_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ s3c_g2d_params *params; ROT_DEG rot_degree; params = (s3c_g2d_params*)file->private_data; if (copy_from_user(params, (s3c_g2d_params*)arg, sizeof(s3c_g2d_params))) return -EFAULT; mutex_lock(h_rot_mutex); switch(cmd) { case S3C_G2D_ROTATOR_0: rot_degree = ROT_0; s3c_g2d_rotator_start(params, rot_degree); break; case S3C_G2D_ROTATOR_90: rot_degree = ROT_90; s3c_g2d_rotator_start(params, rot_degree); break; case S3C_G2D_ROTATOR_180: rot_degree = ROT_180; s3c_g2d_rotator_start(params, rot_degree); break; case S3C_G2D_ROTATOR_270: rot_degree = ROT_270; s3c_g2d_rotator_start(params, rot_degree); break; case S3C_G2D_ROTATOR_X_FLIP: rot_degree = ROT_X_FLIP; s3c_g2d_rotator_start(params, rot_degree); break; case S3C_G2D_ROTATOR_Y_FLIP: rot_degree = ROT_Y_FLIP; s3c_g2d_rotator_start(params, rot_degree); break; default: mutex_unlock(h_rot_mutex); return -EINVAL; } if(!(file->f_flags & O_NONBLOCK)) { if (interruptible_sleep_on_timeout(&waitq_g2d, G2D_TIMEOUT) == 0) { printk(KERN_ERR "/n%s: Waiting for interrupt is timeout/n", __FUNCTION__); } } mutex_unlock(h_rot_mutex); return 0; }static unsigned int s3c_g2d_poll(struct file *file, poll_table *wait){ unsigned int mask = 0; poll_wait(file, &waitq_g2d, wait); if(s3c_g2d_poll_flag == 1) { mask = POLLOUT|POLLWRNORM; s3c_g2d_poll_flag = 0; } return mask;} //Kernel VFS ,GTK open ===>Kernel VFS==>s3c_g2d_fops==s3c_g2d_openstruct file_operations s3c_g2d_fops = { .owner = THIS_MODULE, .open = s3c_g2d_open, .release = s3c_g2d_release, .mmap = s3c_g2d_mmap, .ioctl = s3c_g2d_ioctl, .poll = s3c_g2d_poll,};//User for misc_registerstatic struct miscdevice s3c_g2d_dev = { .minor = G2D_MINOR, .name = "s3c-g2d", .fops = &s3c_g2d_fops,};int s3c_g2d_probe(struct platform_device *pdev){ struct resource *res; int ret; printk(KERN_ALERT"s3c_g2d_probe called/n"); /* find the IRQs */ s3c_g2d_irq_num = platform_get_irq(pdev, 0); if(s3c_g2d_irq_num <= 0) { printk(KERN_ERR "failed to get irq resouce/n"); return -ENOENT; } ret = request_irq(s3c_g2d_irq_num, s3c_g2d_irq, IRQF_DISABLED, pdev->name, NULL); if (ret) { printk("request_irq(g2d) failed./n"); return ret; } /* get the memory region */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if(res == NULL) { printk(KERN_ERR "failed to get memory region resouce/n"); return -ENOENT; } s3c_g2d_mem = request_mem_region(res->start, res->end-res->start+1, pdev->name); if(s3c_g2d_mem == NULL) { printk(KERN_ERR "failed to reserve memory region/n"); return -ENOENT; } s3c_g2d_base = ioremap(s3c_g2d_mem->start, s3c_g2d_mem->end - res->start + 1); if(s3c_g2d_base == NULL) { printk(KERN_ERR "failed ioremap/n"); return -ENOENT; } s3c_g2d_clock = clk_get(&pdev->dev, "g2d"); if(s3c_g2d_clock == NULL) { printk(KERN_ERR "failed to find g2d clock source/n"); return -ENOENT; } clk_enable(s3c_g2d_clock); h_clk = clk_get(&pdev->dev, "hclk"); if(h_clk == NULL) { printk(KERN_ERR "failed to find h_clk clock source/n"); return -ENOENT; } init_waitqueue_head(&waitq_g2d); ret = misc_register(&s3c_g2d_dev); if (ret) { printk (KERN_ERR "cannot register miscdev on minor=%d (%d)/n", G2D_MINOR, ret); return ret; } h_rot_mutex = (struct mutex *)kmalloc(sizeof(struct mutex), GFP_KERNEL); if (h_rot_mutex == NULL) return -1; mutex_init(h_rot_mutex); printk(KERN_ALERT" s3c_g2d_probe Success/n"); return 0;}static int s3c_g2d_remove(struct platform_device *dev){ printk(KERN_INFO "s3c_g2d_remove called !/n"); free_irq(s3c_g2d_irq_num, NULL); if (s3c_g2d_mem != NULL) { printk(KERN_INFO "S3C Rotator Driver, releasing resource/n"); iounmap(s3c_g2d_base); release_resource(s3c_g2d_mem); kfree(s3c_g2d_mem); } misc_deregister(&s3c_g2d_dev); printk(KERN_INFO "s3c_g2d_remove Success !/n"); return 0;}//the following use for FIMG2D Powermagement,just use clk_disable and clk_enable to achievestatic int s3c_g2d_suspend(struct platform_device *dev, pm_message_t state){ clk_disable(s3c_g2d_clock); return 0;}static int s3c_g2d_resume(struct platform_device *pdev){ clk_enable(s3c_g2d_clock); return 0;}static struct platform_driver s3c_g2d_driver = { .probe = s3c_g2d_probe, .remove = s3c_g2d_remove, .suspend = s3c_g2d_suspend, .resume = s3c_g2d_resume, .driver = { .owner = THIS_MODULE, .name = "s3c-g2d", },};int __init s3c_g2d_init(void){ if(platform_driver_register(&s3c_g2d_driver) != 0) { printk("platform device register Failed /n"); return -1; } printk(" S3C G2D Init : Done/n"); return 0;}void s3c_g2d_exit(void){ platform_driver_unregister(&s3c_g2d_driver); mutex_destroy(h_rot_mutex);}module_init(s3c_g2d_init);module_exit(s3c_g2d_exit);MODULE_AUTHOR("Jonghun Han <jonghun.han@samsung.com>");MODULE_DESCRIPTION("S3C FIMG-2D Device Driver");MODULE_LICENSE("GPL");
