--- /dev/null
+/* drivers/staging/rk29/ipp/rk29-ipp.c\r
+ *\r
+ * Copyright (C) 2010 ROCKCHIP, Inc.\r
+ *\r
+ * This software is licensed under the terms of the GNU General Public\r
+ * License version 2, as published by the Free Software Foundation, and\r
+ * may be copied, distributed, and modified under those terms.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ */\r
+\r
+#include <linux/kernel.h>\r
+#include <linux/init.h>\r
+#include <linux/module.h>\r
+#include <linux/platform_device.h>\r
+#include <linux/sched.h>\r
+#include <linux/mutex.h>\r
+#include <linux/err.h>\r
+#include <linux/clk.h>\r
+#include <asm/delay.h>\r
+#include <linux/dma-mapping.h>\r
+#include <linux/delay.h>\r
+#include <asm/io.h>\r
+#include <linux/irq.h>\r
+#include <linux/interrupt.h>\r
+#include <mach/rk29_iomap.h>\r
+#include <mach/irqs.h>\r
+#include <linux/fs.h>\r
+#include <asm/uaccess.h>\r
+#include <linux/firmware.h>\r
+#include <linux/miscdevice.h>\r
+#include <linux/poll.h>\r
+#include <linux/delay.h>\r
+#include <linux/wait.h>\r
+#include <linux/syscalls.h>\r
+#include <linux/timer.h>\r
+#include "rk29-ipp.h"\r
+\r
+\r
+struct ipp_drvdata {\r
+ struct miscdevice miscdev;\r
+ struct device dev;\r
+ void *ipp_base;\r
+ struct ipp_regs regs;\r
+ int irq0;\r
+ struct clk *axi_clk;\r
+ struct clk *ahb_clk;\r
+ struct mutex mutex; // mutex\r
+};\r
+\r
+static struct ipp_drvdata *drvdata = NULL;\r
+\r
+static DECLARE_WAIT_QUEUE_HEAD(wait_queue);\r
+static int wq_condition = 0;\r
+\r
+/* Context data (unique) */\r
+struct ipp_context\r
+{\r
+ struct ipp_drvdata *data; // driver data\r
+ struct rk29_ipp_req *blit; // blit request\r
+ uint32_t rot; // rotation mode\r
+ struct file *srcf; // source file (for PMEM)\r
+ struct file *dstf; // destination file (for PMEM)\r
+};\r
+\r
+#define IPP_MAJOR 232\r
+#define RK_IPP_BLIT 0x5017\r
+#define RK_IPP_SYNC 0x5018\r
+\r
+#define RK29_IPP_PHYS 0x10110000\r
+#define RK29_IPP_SIZE SZ_16K\r
+#define IPP_RESET_TIMEOUT 1000\r
+\r
+/* Driver information */\r
+#define DRIVER_DESC "RK29 IPP Device Driver"\r
+#define DRIVER_NAME "rk29-ipp"\r
+\r
+/* Logging */\r
+#define IPP_DEBUG 0\r
+#ifdef IPP_DEBUG\r
+#define DBG(format, args...) printk(KERN_DEBUG "%s: " format, DRIVER_NAME, ## args)\r
+#define ERR(format, args...) printk(KERN_DEBUG "%s: " format, DRIVER_NAME, ## args)\r
+#define WARNING(format, args...) printk(KERN_DEBUG "%s: " format, DRIVER_NAME, ## args)\r
+#define INFO(format, args...) printk(KERN_DEBUG "%s: " format, DRIVER_NAME, ## args)\r
+#else\r
+#define DBG(format, args...)\r
+#define ERR(format, args...)\r
+#define WARNING(format, args...)\r
+#define INFO(format, args...)\r
+#endif\r
+\r
+static inline void ipp_write( uint32_t b, uint32_t r)\r
+{\r
+ __raw_writel(b, drvdata->ipp_base + r);\r
+}\r
+\r
+static inline uint32_t ipp_read( uint32_t r)\r
+{\r
+ return __raw_readl(drvdata->ipp_base + r);\r
+}\r
+\r
+static void ipp_soft_reset(void)\r
+{\r
+ uint32_t i;\r
+ uint32_t reg;\r
+\r
+ ipp_write(1, IPP_SRESET);\r
+\r
+ for(i = 0; i < IPP_RESET_TIMEOUT; i++) {\r
+ reg = ipp_read( IPP_SRESET) & 1;\r
+\r
+ if(reg == 0)\r
+ break;\r
+\r
+ udelay(1);\r
+ }\r
+\r
+ if(i == IPP_RESET_TIMEOUT)\r
+ ERR("soft reset timeout.\n");\r
+}\r
+\r
+static int ipp_do_blit(struct rk29_ipp_req *req)\r
+{\r
+ uint32_t rotate;\r
+ uint32_t pre_scale = 0;\r
+ uint32_t post_scale = 0;\r
+ uint32_t pre_scale_w, pre_scale_h;\r
+ uint32_t post_scale_w = 0x1000;\r
+ uint32_t post_scale_h = 0x1000;\r
+ uint32_t pre_scale_target_w, pre_scale_target_h;\r
+ uint32_t post_scale_target_w, post_scale_target_h;\r
+ uint32_t dst0_YrgbMst,dst0_CbrMst;\r
+ uint32_t ret = 0;\r
+ rotate = req->flag;\r
+ switch (rotate) {\r
+ case IPP_ROT_90:\r
+ //for rotation 90 degree\r
+ DBG("rotate %d, src0.fmt %d",rotate,req->src0.fmt);\r
+ switch(req->src0.fmt)\r
+ {\r
+ case IPP_XRGB_8888:\r
+ dst0_YrgbMst = req->dst0.YrgbMst + req->dst0.w*4;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_RGB_565:\r
+ dst0_YrgbMst = req->dst0.YrgbMst + req->dst0.w*2;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H1V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst + req->dst0.w;\r
+ dst0_CbrMst = req->dst0.CbrMst + req->dst0.w*2;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst + req->dst0.w;\r
+ dst0_CbrMst = req->dst0.CbrMst + req->dst0.w*2;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V2:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+ req->dst0.w;\r
+ dst0_CbrMst = req->dst0.CbrMst + req->dst0.w;\r
+ break;\r
+\r
+ default:\r
+\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case IPP_ROT_180:\r
+ //for rotation 180 degree\r
+ DBG("rotate %d, src0.fmt %d",rotate,req->src0.fmt);\r
+ switch(req->src0.fmt)\r
+ {\r
+ case IPP_XRGB_8888:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w*4+req->dst0.w*4;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_RGB_565:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w*2+req->dst0.w*2;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H1V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w+req->dst0.w;\r
+ dst0_CbrMst = req->dst0.CbrMst+(req->dst0.h-1)*req->dst_vir_w*2+req->dst0.w*2;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w+req->dst0.w;\r
+ dst0_CbrMst = req->dst0.CbrMst+(req->dst0.h-1)*req->dst_vir_w+req->dst0.w;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V2:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w+req->dst0.w;\r
+ dst0_CbrMst = req->dst0.CbrMst+((req->dst0.h/2)-1)*req->dst_vir_w+req->dst0.w;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case IPP_ROT_270:\r
+ DBG("rotate %d, src0.fmt %d \n",rotate,req->src0.fmt);\r
+ switch(req->src0.fmt)\r
+ {\r
+ case IPP_XRGB_8888:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w*4;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_RGB_565:\r
+ dst0_YrgbMst = req->dst0.YrgbMst +(req->dst0.h-1)*req->dst_vir_w*2;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H1V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w;\r
+ dst0_CbrMst = req->dst0.CbrMst+(req->dst0.h-1)*req->dst_vir_w*2;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w;\r
+ dst0_CbrMst = req->dst0.CbrMst+(req->dst0.h-1)*req->dst_vir_w*2;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V2:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w;\r
+ dst0_CbrMst = req->dst0.CbrMst+((req->dst0.h/2)-1)*req->dst_vir_w;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case IPP_ROT_X_FLIP:\r
+ DBG("rotate %d, src0.fmt %d",rotate,req->src0.fmt);\r
+ switch(req->src0.fmt)\r
+ {\r
+ case IPP_XRGB_8888:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+req->dst0.w*4;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_RGB_565:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+req->dst0.w*2;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H1V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+req->dst0.w;\r
+ dst0_CbrMst = req->dst0.CbrMst+req->dst0.w*2;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+req->dst0.w;\r
+ dst0_CbrMst = req->dst0.CbrMst+req->dst0.w;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V2:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+req->dst0.w;\r
+ dst0_CbrMst = req->dst0.CbrMst+req->dst0.w;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ break;\r
+\r
+ case IPP_ROT_Y_FLIP:\r
+ DBG("rotate %d, src0.fmt %d",rotate,req->src0.fmt);\r
+ switch(req->src0.fmt)\r
+ {\r
+ case IPP_XRGB_8888:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w*4;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_RGB_565:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w*2;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H1V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w;\r
+ dst0_CbrMst = req->dst0.CbrMst+(req->dst0.h-1)*req->dst_vir_w*2;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V1:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w;\r
+ dst0_CbrMst = req->dst0.CbrMst+(req->dst0.h-1)*req->dst_vir_w;\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V2:\r
+ dst0_YrgbMst = req->dst0.YrgbMst+(req->dst0.h-1)*req->dst_vir_w;\r
+ dst0_CbrMst = req->dst0.CbrMst+((req->dst0.h/2)-1)*req->dst_vir_w;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ break;\r
+ case IPP_ROT_0:\r
+ //for 0 degree\r
+ DBG("rotate %d, src0.fmt %d",rotate,req->src0.fmt);\r
+ dst0_YrgbMst = req->dst0.YrgbMst;\r
+ dst0_CbrMst = req->dst0.CbrMst;\r
+ break;\r
+\r
+ default:\r
+ ERR("ipp is not surpport degree!!\n" );\r
+ break;\r
+ }\r
+\r
+ if(drvdata->axi_clk)\r
+ {\r
+ clk_enable(drvdata->axi_clk);\r
+ }\r
+ else\r
+ {\r
+ printk("axi_clk is null \n");\r
+ ret = -EINVAL;\r
+ goto error_axi_clk;\r
+ }\r
+\r
+ if(drvdata->ahb_clk)\r
+ {\r
+ clk_enable(drvdata->ahb_clk);\r
+ }\r
+ else\r
+ {\r
+ printk("ahb_clk is null \n");\r
+ ret = -EINVAL;\r
+ goto error_ahb_clk;\r
+ }\r
+\r
+ /* Configure source image */\r
+ DBG("src YrgbMst 0x%x , CbrMst0x%x, %dx%d, fmt = %d\n", req->src0.YrgbMst,req->src0.CbrMst,\r
+ req->src0.w, req->src0.h, req->src0.fmt);\r
+\r
+ ipp_write(req->src0.YrgbMst, IPP_SRC0_Y_MST);\r
+ if(IS_YCRCB(req->src0.fmt))\r
+ {\r
+ ipp_write(req->src0.CbrMst, IPP_SRC0_CBR_MST);\r
+ }\r
+ ipp_write(req->src0.h<<16|req->src0.w, IPP_SRC_IMG_INFO);\r
+ ipp_write(ipp_read(IPP_CONFIG)|req->src0.fmt, IPP_CONFIG);\r
+\r
+ /* Configure destination image */\r
+ DBG("dst YrgbMst 0x%x , CbrMst0x%x, %dx%d\n", dst0_YrgbMst,dst0_CbrMst,\r
+ req->dst0.w, req->dst0.h);\r
+\r
+ ipp_write(dst0_YrgbMst, IPP_DST0_Y_MST);\r
+\r
+ if(IS_YCRCB(req->src0.fmt))\r
+ {\r
+ ipp_write(dst0_CbrMst, IPP_DST0_CBR_MST);\r
+ }\r
+ ipp_write(req->dst0.h<<16|req->dst0.w, IPP_DST_IMG_INFO);\r
+\r
+ /*Configure Pre_scale*/\r
+ if((IPP_ROT_90 == rotate) || (IPP_ROT_270 == rotate))\r
+ {\r
+ pre_scale = ((req->dst0.w < req->src0.h) ? 1 : 0)||((req->dst0.h < req->src0.w) ? 1 : 0);\r
+ }\r
+ else //other degree\r
+ {\r
+ pre_scale = ((req->dst0.w < req->src0.w) ? 1 : 0)||((req->dst0.h < req->src0.h) ? 1 : 0);\r
+ }\r
+\r
+ if(pre_scale)\r
+ {\r
+ if(((IPP_ROT_90 == rotate) || (IPP_ROT_270 == rotate)))\r
+ {\r
+\r
+ if((req->src0.w>req->dst0.h))\r
+ {\r
+ pre_scale_w = (uint32_t)( req->src0.w/req->dst0.h);//floor\r
+ }\r
+ else\r
+ {\r
+ pre_scale_w = 1;\r
+ }\r
+ if((req->src0.h>req->dst0.w))\r
+ {\r
+ pre_scale_h = (uint32_t)( req->src0.h/req->dst0.w);//floor\r
+ }\r
+ else\r
+ {\r
+ pre_scale_h = 1;\r
+ }\r
+\r
+ DBG("!!!!!pre_scale_h %d,pre_scale_w %d \n",pre_scale_h,pre_scale_w);\r
+\r
+ ipp_write(ipp_read(IPP_CONFIG)|PRE_SCALE, IPP_CONFIG); //enable pre_scale\r
+ ipp_write(ipp_read(IPP_PRE_SCL_PARA)|(pre_scale_h-1)<<3|(pre_scale_w-1),IPP_PRE_SCL_PARA);\r
+\r
+ if((req->src0.w%pre_scale_w)!=0) //ÏòÉÏÈ¡Õû ceil\r
+ {\r
+ pre_scale_target_w = req->src0.w/pre_scale_w+1;\r
+ }\r
+ else\r
+ {\r
+ pre_scale_target_w = req->src0.w/pre_scale_w;\r
+ }\r
+\r
+ if((req->src0.h%pre_scale_h)!=0)//ÏòÉÏÈ¡Õû ceil\r
+ {\r
+ pre_scale_target_h = req->src0.h/pre_scale_h +1;\r
+ }\r
+ else\r
+ {\r
+ pre_scale_target_h = req->src0.h/pre_scale_h;\r
+ }\r
+\r
+ ipp_write(((pre_scale_target_h)<<16)|(pre_scale_target_w), IPP_PRE_IMG_INFO);\r
+\r
+ }\r
+ else//0 180 x ,y\r
+ {\r
+\r
+ if((req->src0.w>req->dst0.w))\r
+ {\r
+ pre_scale_w = (uint32_t)( req->src0.w/req->dst0.w);//floor\r
+ }\r
+ else\r
+ {\r
+ pre_scale_w = 1;\r
+ }\r
+\r
+ if((req->src0.h>req->dst0.h))\r
+ {\r
+ pre_scale_h = (uint32_t)( req->src0.h/req->dst0.h);//floor\r
+ }\r
+ else\r
+ {\r
+ pre_scale_h = 1;\r
+ }\r
+\r
+ DBG("!!!!!pre_scale_h %d,pre_scale_w %d \n",pre_scale_h,pre_scale_w);\r
+\r
+ ipp_write(ipp_read(IPP_CONFIG)|PRE_SCALE, IPP_CONFIG); //enable pre_scale\r
+ ipp_write(ipp_read(IPP_PRE_SCL_PARA)|(pre_scale_h-1)<<3|(pre_scale_w-1),IPP_PRE_SCL_PARA);\r
+\r
+ if((req->src0.w%pre_scale_w)!=0) //ÏòÉÏÈ¡Õû ceil\r
+ {\r
+ pre_scale_target_w = req->src0.w/pre_scale_w+1;\r
+ }\r
+ else\r
+ {\r
+ pre_scale_target_w = req->src0.w/pre_scale_w;\r
+ }\r
+\r
+ if((req->src0.h%pre_scale_h)!=0)//ÏòÉÏÈ¡Õû ceil\r
+ {\r
+ pre_scale_target_h = req->src0.h/pre_scale_h +1;\r
+ }\r
+ else\r
+ {\r
+ pre_scale_target_h = req->src0.h/pre_scale_h;\r
+ }\r
+ }\r
+ ipp_write(((pre_scale_target_h)<<16)|(pre_scale_target_w), IPP_PRE_IMG_INFO);\r
+\r
+ }\r
+ else//no pre_scale\r
+ {\r
+ ipp_write(0,IPP_PRE_SCL_PARA);\r
+ ipp_write((req->src0.h<<16)|req->src0.w, IPP_PRE_IMG_INFO);\r
+ }\r
+\r
+ /*Configure Post_scale*/\r
+ if((IPP_ROT_90 == rotate) || (IPP_ROT_270 == rotate))\r
+ {\r
+ if (( (req->src0.h%req->dst0.w)!=0)||( (req->src0.w%req->dst0.h)!= 0)//СÊý±¶ËõС\r
+ ||(req->dst0.w > req->src0.h) ||(req->dst0.h > req->src0.w))\r
+ {\r
+ post_scale = 1;\r
+ }\r
+ else\r
+ {\r
+ post_scale = 0;\r
+ }\r
+ }\r
+ else //0 180 x-flip y-flip\r
+ {\r
+ if (( (req->src0.w%req->dst0.w)!=0)||( (req->src0.h%req->dst0.h)!= 0)//СÊý±¶ËõС\r
+ ||(req->dst0.w > req->src0.w) ||(req->dst0.h > req->src0.h))\r
+ {\r
+ post_scale = 1;\r
+ }\r
+ else\r
+ {\r
+ post_scale = 0;\r
+ }\r
+ }\r
+\r
+ if(post_scale)\r
+ {\r
+ if((IPP_ROT_90 == rotate) || (IPP_ROT_270 == rotate))\r
+ {\r
+ if(pre_scale)\r
+ {\r
+ post_scale_target_w = pre_scale_target_w;\r
+ post_scale_target_h = pre_scale_target_h;\r
+ }\r
+ else\r
+ {\r
+ post_scale_target_w = req->src0.w;\r
+ post_scale_target_h = req->src0.h;\r
+ }\r
+\r
+ DBG("post_scale_target_w %d ,post_scale_target_h %d !!!\n",post_scale_target_w,post_scale_target_h);\r
+\r
+ switch(req->src0.fmt)\r
+ {\r
+ case IPP_XRGB_8888:\r
+ case IPP_RGB_565:\r
+ case IPP_Y_CBCR_H1V1:\r
+ post_scale_w = (uint32_t)(4096*(post_scale_target_w-1)/(req->dst0.h-1));\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V1:\r
+ case IPP_Y_CBCR_H2V2:\r
+ post_scale_w = (uint32_t)(4096*(post_scale_target_w/2-1)/(req->dst0.h/2-1));\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ post_scale_h = (uint32_t)(4096*(post_scale_target_h -1)/(req->dst0.w-1));\r
+\r
+ DBG("1111 post_scale_w %x,post_scale_h %x!!! \n",post_scale_w,post_scale_h);\r
+ }\r
+ else// 0 180 x-flip y-flip\r
+ {\r
+\r
+ if(pre_scale)\r
+ {\r
+ post_scale_target_w = pre_scale_target_w;\r
+ post_scale_target_h = pre_scale_target_h;\r
+ }\r
+ else\r
+ {\r
+ post_scale_target_w = req->src0.w;\r
+ post_scale_target_h = req->src0.h;\r
+ }\r
+\r
+ switch(req->src0.fmt)\r
+ {\r
+ case IPP_XRGB_8888:\r
+ case IPP_RGB_565:\r
+ case IPP_Y_CBCR_H1V1:\r
+ post_scale_w = (uint32_t)(4096*(post_scale_target_w-1)/(req->dst0.w-1));\r
+ break;\r
+\r
+ case IPP_Y_CBCR_H2V1:\r
+ case IPP_Y_CBCR_H2V2:\r
+ post_scale_w = (uint32_t)(4096*(post_scale_target_w/2-1)/(req->dst0.w/2-1));\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ post_scale_h = (uint32_t)(4096*(post_scale_target_h -1)/(req->dst0.h-1));\r
+\r
+ }\r
+ ipp_write(ipp_read(IPP_CONFIG)|POST_SCALE, IPP_CONFIG); //enable post_scale\r
+ ipp_write((post_scale_h<<16)|post_scale_w, IPP_POST_SCL_PARA);\r
+ }\r
+ else //no post_scale\r
+ {\r
+ DBG("no post_scale !!!!!! \n");\r
+ ipp_write(ipp_read(IPP_CONFIG)&(~POST_SCALE), IPP_CONFIG); //disable post_scale\r
+ ipp_write((post_scale_h<<16)|post_scale_w, IPP_POST_SCL_PARA);\r
+ }\r
+\r
+ /* Configure rotation */\r
+\r
+ if(IPP_ROT_0 == req->flag)\r
+ {\r
+ ipp_write(ipp_read(IPP_CONFIG)&(~ROT_ENABLE), IPP_CONFIG);\r
+ }\r
+ else if(req->flag > IPP_ROT_LIMIT)\r
+ {\r
+ printk("rk29_ipp is not surpport rot degree!!!!\n");\r
+ ret = -1;\r
+ goto error_rot_limit;\r
+ }\r
+ else\r
+ {\r
+ ipp_write(ipp_read(IPP_CONFIG)|ROT_ENABLE, IPP_CONFIG);\r
+ ipp_write(ipp_read(IPP_CONFIG)|rotate<<5, IPP_CONFIG);\r
+ }\r
+\r
+ /*Configure other*/\r
+ ipp_write((req->dst_vir_w<<16)|req->src_vir_w, IPP_IMG_VIR);\r
+\r
+ if((req->src0.w%4) !=0)\r
+ ipp_write(ipp_read(IPP_CONFIG)|(1<<26), IPP_CONFIG);//store clip mode\r
+\r
+ /* Start the operation */\r
+\r
+ ipp_write(8, IPP_INT);//\r
+\r
+ //msleep(5000);//debug use\r
+\r
+ ipp_write(1, IPP_PROCESS_ST);\r
+\r
+ if(!wait_event_timeout(wait_queue, wq_condition, msecs_to_jiffies(req->timeout)))\r
+ {\r
+ printk("wait_event_timeout \n");\r
+ wq_condition = 0;\r
+ if(!drvdata)\r
+ {\r
+ printk("close clk 0! \n");\r
+ ret = -EINVAL;\r
+ goto error_null;\r
+ }\r
+ ret = -EAGAIN;\r
+ goto errot_timeout;\r
+ }\r
+\r
+ wq_condition = 0;\r
+\r
+ if(!drvdata)\r
+ {\r
+ printk("close clk! \n");\r
+ ret = -EINVAL;\r
+ goto error_null;\r
+ }\r
+\r
+ if(((ipp_read(IPP_INT)>>6)&0x3) ==0)// idle\r
+ {\r
+ clk_disable(drvdata->axi_clk);\r
+ clk_disable(drvdata->ahb_clk);\r
+ }\r
+ else\r
+ {\r
+ printk("rk29 ipp status is error!!!\n");\r
+ ret = -EINVAL;\r
+ goto errot_timeout;\r
+ }\r
+ return 0;\r
+\r
+error_null:\r
+errot_timeout:\r
+error_rot_limit:\r
+error_ahb_clk:\r
+ clk_disable(drvdata->ahb_clk);\r
+error_axi_clk:\r
+ clk_disable(drvdata->axi_clk);\r
+ return ret;\r
+}\r
+\r
+static int stretch_blit(struct ipp_context *ctx, unsigned long arg )\r
+{\r
+ struct ipp_drvdata *data = ctx->data;\r
+ int ret = 0;\r
+ struct rk29_ipp_req req;\r
+\r
+ mutex_lock(&data->mutex);\r
+\r
+ if (unlikely(copy_from_user(&req, (struct rk29_ipp_req*)arg,\r
+ sizeof(struct rk29_ipp_req)))) {\r
+ ERR("copy_from_user failed\n");\r
+ ret = -EFAULT;\r
+ goto err_noput;\r
+ }\r
+\r
+ if (unlikely((req.src0.w <= 1) || (req.src0.h <= 1))) {\r
+ ERR("invalid source resolution\n");\r
+ ret = -EINVAL;\r
+ goto err_noput;\r
+ }\r
+\r
+ if (unlikely((req.dst0.w <= 1) || (req.dst0.h <= 1))) {\r
+ ERR("invalid destination resolution\n");\r
+ ret = -EINVAL;\r
+ goto err_noput;\r
+ }\r
+\r
+ if (unlikely(req.src0.YrgbMst== 0) )\r
+ {\r
+ ERR("could not retrieve src image from memory\n");\r
+ ret = -EINVAL;\r
+ goto err_noput;\r
+ }\r
+\r
+ if (unlikely(req.dst0.YrgbMst== 0) )\r
+ {\r
+ ERR("could not retrieve dst image from memory\n");\r
+ ret = -EINVAL;\r
+ goto err_noput;\r
+ }\r
+\r
+ ret = ipp_do_blit(&req);\r
+ if(ret != 0) {\r
+ ERR("Failed to start IPP operation (%d)\n", ret);\r
+ ipp_soft_reset();\r
+ goto err_noput;\r
+ }\r
+\r
+err_noput:\r
+\r
+ mutex_unlock(&data->mutex);\r
+\r
+ return ret;\r
+}\r
+\r
+static int ipp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)\r
+{\r
+\r
+ struct ipp_context *ctx = (struct ipp_context *)file->private_data;\r
+ int ret = 0;\r
+ switch (cmd)\r
+ {\r
+ case RK_IPP_BLIT:\r
+ ret = stretch_blit(ctx, arg);\r
+ break;\r
+\r
+ default:\r
+ ERR("unknown ioctl cmd!\n");\r
+ ret = -EINVAL;\r
+ break;\r
+ }\r
+ return ret;\r
+}\r
+\r
+static int rk29_ipp_open(struct inode *inode, struct file *file)\r
+{\r
+ struct ipp_context *ctx;\r
+\r
+ ctx = kmalloc(sizeof(struct ipp_context), GFP_KERNEL);\r
+ if (ctx == NULL) {\r
+ ERR("Context allocation failed\n");\r
+ return -ENOMEM;\r
+ }\r
+\r
+ memset(ctx, 0, sizeof(struct ipp_context));\r
+ ctx->data = drvdata;\r
+ ctx->rot = IPP_ROT_0;\r
+ file->private_data = ctx;\r
+\r
+ DBG("device opened\n");\r
+\r
+ return 0;\r
+}\r
+\r
+static int ipp_release(struct inode *inode, struct file *file)\r
+{\r
+ struct ipp_context *ctx = (struct ipp_context *)file->private_data;\r
+ kfree(ctx);\r
+\r
+ DBG("device released\n");\r
+ return 0;\r
+}\r
+\r
+static irqreturn_t rk29_ipp_irq(int irq, void *dev_id)\r
+{\r
+ uint32_t stat;\r
+\r
+ DBG("rk29_ipp_irq %d \n",irq);\r
+ stat = ipp_read(IPP_INT);\r
+\r
+ while(!(stat & 0x1))\r
+ {\r
+ DBG("stat %d \n",stat);\r
+ }\r
+ wq_condition = 1;\r
+ wake_up(&wait_queue);\r
+\r
+ ipp_write(ipp_read(IPP_INT)|0xc, IPP_INT);\r
+ ipp_write(ipp_read(IPP_INT)|0x30, IPP_INT);\r
+ return IRQ_HANDLED;\r
+}\r
+\r
+struct file_operations ipp_fops = {\r
+ .owner = THIS_MODULE,\r
+ .open = rk29_ipp_open,\r
+ .release = ipp_release,\r
+ .ioctl = ipp_ioctl,\r
+};\r
+\r
+static struct miscdevice ipp_dev ={\r
+ .minor = IPP_MAJOR,\r
+ .name = "rk29-ipp",\r
+ .fops = &ipp_fops,\r
+};\r
+\r
+static int __init ipp_drv_probe(struct platform_device *pdev)\r
+{\r
+ struct ipp_drvdata *data;\r
+ int ret = 0;\r
+\r
+ data = kmalloc(sizeof(struct ipp_drvdata), GFP_KERNEL);\r
+ if(NULL==data)\r
+ {\r
+ ERR("failed to allocate driver data.\n");\r
+ return -ENOMEM;\r
+ }\r
+\r
+ /* get the clock */\r
+ data->axi_clk = clk_get(&pdev->dev, "ipp_axi");\r
+ if(NULL == data->axi_clk)\r
+ {\r
+ ERR("failed to find ipp axi clock source\n");\r
+ ret = -ENOENT;\r
+ goto err_clock;\r
+ }\r
+\r
+ data->ahb_clk = clk_get(&pdev->dev, "ipp_ahb");\r
+ if(NULL == data->ahb_clk)\r
+ {\r
+ ERR("failed to find ipp ahb clock source\n");\r
+ ret = -ENOENT;\r
+ goto err_clock;\r
+ }\r
+\r
+ /* map the memory */\r
+ data->ipp_base = (void*)ioremap( RK29_IPP_PHYS, SZ_16K);//ipp size 16k\r
+ if (data->ipp_base == NULL)\r
+ {\r
+ ERR("ioremap failed\n");\r
+ ret = -ENOENT;\r
+ goto err_ioremap;\r
+ }\r
+\r
+ /* get the IRQ */\r
+ data->irq0 = pdev->resource[1].start;\r
+ printk("ipp irq %d\n",data->irq0);\r
+ if (data->irq0 <= 0)\r
+ {\r
+ ERR("failed to get irq resource (%d).\n", data->irq0);\r
+ ret = data->irq0;\r
+ goto err_irq;\r
+ }\r
+\r
+ /* request the IRQ */\r
+ ret = request_irq(data->irq0, rk29_ipp_irq, IRQF_SHARED, "rk29-ipp", pdev);\r
+ if (ret)\r
+ {\r
+ ERR("request_irq failed (%d).\n", ret);\r
+ goto err_irq;\r
+ }\r
+\r
+ mutex_init(&data->mutex);\r
+\r
+ platform_set_drvdata(pdev, data);\r
+ drvdata = data;\r
+\r
+ ret = misc_register(&ipp_dev);\r
+ if(ret)\r
+ {\r
+ ERR("cannot register miscdev (%d)\n", ret);\r
+ goto err_misc_register;\r
+ }\r
+ DBG("Driver loaded succesfully\n");\r
+\r
+ return 0;\r
+\r
+err_misc_register:\r
+ free_irq(data->irq0, pdev);\r
+err_irq:\r
+ iounmap(data->ipp_base);\r
+err_ioremap:\r
+err_clock:\r
+ kfree(data);\r
+\r
+ return ret;\r
+}\r
+\r
+static int ipp_drv_remove(struct platform_device *pdev)\r
+{\r
+ struct ipp_drvdata *data = platform_get_drvdata(pdev);\r
+ DBG("%s [%d]\n",__FUNCTION__,__LINE__);\r
+\r
+ misc_deregister(&(data->miscdev));\r
+ free_irq(data->irq0, &data->miscdev);\r
+ iounmap((void __iomem *)(data->ipp_base));\r
+\r
+ if(data->axi_clk) {\r
+ clk_put(data->axi_clk);\r
+ }\r
+ if(data->ahb_clk) {\r
+ clk_put(data->ahb_clk);\r
+ }\r
+\r
+ kfree(data);\r
+ return 0;\r
+}\r
+\r
+static struct platform_driver rk29_ipp_driver = {\r
+ .probe = ipp_drv_probe,\r
+ .remove = ipp_drv_remove,\r
+ .driver = {\r
+ .owner = THIS_MODULE,\r
+ .name = "rk29-ipp",\r
+ },\r
+};\r
+\r
+static int __init rk29_ipp_init(void)\r
+{\r
+ int ret;\r
+\r
+ if ((ret = platform_driver_register(&rk29_ipp_driver)) != 0)\r
+ {\r
+ ERR("Platform device register failed (%d).\n", ret);\r
+ return ret;\r
+ }\r
+ INFO("Module initialized.\n");\r
+ return 0;\r
+}\r
+\r
+static void __exit rk29_ipp_exit(void)\r
+{\r
+ platform_driver_unregister(&rk29_ipp_driver);\r
+}\r
+\r
+module_init(rk29_ipp_init);\r
+module_exit(rk29_ipp_exit);\r
+\r
+/* Module information */\r
+MODULE_AUTHOR("wy@rock-chips.com");\r
+MODULE_DESCRIPTION("Driver for rk29 ipp device");\r
+MODULE_LICENSE("GPL");\r
+\r
+\r