*
* @ingroup IPU
*/
-
#include <linux/types.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/wait.h>
+#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/ipu.h>
+#include <linux/kthread.h>
#include <asm/cacheflush.h>
-#include "ipu_prv.h"
-#include "ipu_regs.h"
-#include "ipu_param_mem.h"
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+/* Strucutures and variables for exporting MXC IPU as device*/
+typedef enum {
+ RGB_CS,
+ YUV_CS,
+ NULL_CS
+} cs_t;
+
+typedef enum {
+ STATE_OK = 0,
+ STATE_NO_IPU,
+ STATE_NO_IRQ,
+ STATE_IRQ_FAIL,
+ STATE_IRQ_TIMEOUT,
+ STATE_INIT_CHAN_FAIL,
+ STATE_LINK_CHAN_FAIL,
+ STATE_INIT_CHAN_BUF_FAIL,
+} ipu_state_t;
+
+struct ipu_state_msg {
+ int state;
+ char *msg;
+} state_msg[] = {
+ {STATE_OK, "ok"},
+ {STATE_NO_IPU, "no ipu found"},
+ {STATE_NO_IRQ, "no irq found for task"},
+ {STATE_IRQ_FAIL, "request irq failed"},
+ {STATE_IRQ_TIMEOUT, "wait for irq timeout"},
+ {STATE_INIT_CHAN_FAIL, "ipu init channel fail"},
+ {STATE_LINK_CHAN_FAIL, "ipu link channel fail"},
+ {STATE_INIT_CHAN_BUF_FAIL, "ipu init channel buffer fail"},
+};
+
+struct stripe_setting {
+ u32 iw;
+ u32 ih;
+ u32 ow;
+ u32 oh;
+ u32 outh_resize_ratio;
+ u32 outv_resize_ratio;
+ u32 i_left_pos;
+ u32 i_right_pos;
+ u32 i_top_pos;
+ u32 i_bottom_pos;
+ u32 o_left_pos;
+ u32 o_right_pos;
+ u32 o_top_pos;
+ u32 o_bottom_pos;
+};
+
+struct task_set {
+#define NULL_MODE 0x0
+#define IC_MODE 0x1
+#define ROT_MODE 0x2
+#define VDI_MODE 0x4
+ u8 mode;
+#define IC_VF 0x1
+#define IC_PP 0x2
+#define ROT_VF 0x4
+#define ROT_PP 0x8
+#define VDI_VF 0x10
+ u8 task;
+
+ ipu_channel_t ic_chan;
+ ipu_channel_t rot_chan;
+ ipu_channel_t vdi_ic_p_chan;
+ ipu_channel_t vdi_ic_n_chan;
+
+ u32 i_off;
+ u32 i_uoff;
+ u32 i_voff;
+ u32 istride;
+
+ u32 ov_off;
+ u32 ov_uoff;
+ u32 ov_voff;
+ u32 ovstride;
+
+ u32 ov_alpha_off;
+ u32 ov_alpha_stride;
+
+ u32 o_off;
+ u32 o_uoff;
+ u32 o_voff;
+ u32 ostride;
+
+ u32 r_fmt;
+ u32 r_width;
+ u32 r_height;
+ u32 r_stride;
+ dma_addr_t r_paddr;
+
+#define NO_SPLIT 0x0
+#define RL_SPLIT 0x1
+#define UD_SPLIT 0x2
+#define LEFT_STRIPE 0x1
+#define RIGHT_STRIPE 0x2
+#define UP_STRIPE 0x4
+#define DOWN_STRIPE 0x8
+ u8 split_mode;
+ struct stripe_setting sp_setting;
+};
+
+struct ipu_split_task {
+ struct ipu_task task;
+ struct ipu_task_entry *parent_task;
+ struct task_struct *thread;
+ bool could_finish;
+ wait_queue_head_t waitq;
+ int ret;
+};
+
+struct ipu_task_entry {
+ struct ipu_input input;
+ struct ipu_output output;
+
+ bool overlay_en;
+ struct ipu_overlay overlay;
+
+ u8 priority;
+ u8 task_id;
+#define DEF_TIMEOUT_MS 1000
+ int timeout;
+
+ struct list_head node;
+ struct device *dev;
+ struct task_set set;
+ struct completion comp;
+ ipu_state_t state;
+};
+
+struct ipu_alloc_list {
+ struct list_head list;
+ dma_addr_t phy_addr;
+ void *cpu_addr;
+ u32 size;
+};
+LIST_HEAD(ipu_alloc_list);
+
+static int major;
+static struct class *ipu_class;
+static struct device *ipu_dev;
+
+int ipu_queue_sp_task(struct ipu_split_task *sp_task);
+
+static bool deinterlace_3_field(struct ipu_task_entry *t)
+{
+ return ((t->set.mode & VDI_MODE) &&
+ (t->input.deinterlace.motion != HIGH_MOTION));
+}
+
+static u32 fmt_to_bpp(u32 pixelformat)
+{
+ u32 bpp;
+
+ switch (pixelformat) {
+ case IPU_PIX_FMT_RGB565:
+ /*interleaved 422*/
+ case IPU_PIX_FMT_YUYV:
+ case IPU_PIX_FMT_UYVY:
+ /*non-interleaved 422*/
+ case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YVU422P:
+ bpp = 16;
+ break;
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ case IPU_PIX_FMT_YUV444:
+ bpp = 24;
+ break;
+ case IPU_PIX_FMT_BGR32:
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_RGB32:
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_ABGR32:
+ bpp = 32;
+ break;
+ /*non-interleaved 420*/
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YVU420P:
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_NV12:
+ bpp = 12;
+ break;
+ default:
+ bpp = 8;
+ break;
+ }
+ return bpp;
+}
+
+static cs_t colorspaceofpixel(int fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_BGR32:
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_RGB32:
+ case IPU_PIX_FMT_ABGR32:
+ return RGB_CS;
+ break;
+ case IPU_PIX_FMT_UYVY:
+ case IPU_PIX_FMT_YUYV:
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YVU420P:
+ case IPU_PIX_FMT_YVU422P:
+ case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YUV444:
+ case IPU_PIX_FMT_NV12:
+ return YUV_CS;
+ break;
+ default:
+ return NULL_CS;
+ }
+}
+
+static int need_csc(int ifmt, int ofmt)
+{
+ cs_t ics, ocs;
+
+ ics = colorspaceofpixel(ifmt);
+ ocs = colorspaceofpixel(ofmt);
+
+ if ((ics == NULL_CS) || (ocs == NULL_CS))
+ return -1;
+ else if (ics != ocs)
+ return 1;
+
+ return 0;
+}
+
+static int soc_max_in_width(void)
+{
+ return 4096;
+}
+
+static int soc_max_in_height(void)
+{
+ return 4096;
+}
+
+static int soc_max_out_width(void)
+{
+ /* mx51/mx53/mx6q is 1024*/
+ return 1024;
+}
+
+static int soc_max_out_height(void)
+{
+ /* mx51/mx53/mx6q is 1024*/
+ return 1024;
+}
+
+static int list_size(struct list_head *head)
+{
+ struct list_head *p, *n;
+ int size = 0;
+
+ list_for_each_safe(p, n, head)
+ size++;
+
+ return size;
+}
+
+static int get_task_size(struct ipu_soc *ipu, int id)
+{
+ struct list_head *task_list;
+
+ if (id == IPU_TASK_ID_VF)
+ task_list = &ipu->task_list[0];
+ else if (id == IPU_TASK_ID_PP)
+ task_list = &ipu->task_list[1];
+ else {
+ printk(KERN_ERR "query error task id\n");
+ return -EINVAL;
+ }
+
+ return list_size(task_list);
+}
+
+static struct ipu_soc *most_free_ipu_task(struct ipu_task_entry *t)
+{
+ unsigned int task_num[2][2] = {
+ {0xffffffff, 0xffffffff},
+ {0xffffffff, 0xffffffff} };
+ struct ipu_soc *ipu;
+ int ipu_idx, task_id;
+ int i;
+
+ /* decide task_id */
+ if (t->task_id >= IPU_TASK_ID_MAX)
+ t->task_id %= IPU_TASK_ID_MAX;
+ /* must use task_id VF for VDI task*/
+ if ((t->set.mode & VDI_MODE) &&
+ (t->task_id != IPU_TASK_ID_VF))
+ t->task_id = IPU_TASK_ID_VF;
+
+ for (i = 0; i < MXC_IPU_MAX_NUM; i++) {
+ ipu = ipu_get_soc(i);
+ if (!IS_ERR(ipu)) {
+ task_num[i][0] = get_task_size(ipu, IPU_TASK_ID_VF);
+ task_num[i][1] = get_task_size(ipu, IPU_TASK_ID_PP);
+ }
+ }
+
+ task_id = t->task_id;
+ if (t->task_id == IPU_TASK_ID_VF) {
+ if (task_num[0][0] < task_num[1][0])
+ ipu_idx = 0;
+ else
+ ipu_idx = 1;
+ } else if (t->task_id == IPU_TASK_ID_PP) {
+ if (task_num[0][1] < task_num[1][1])
+ ipu_idx = 0;
+ else
+ ipu_idx = 1;
+ } else {
+ unsigned int min;
+ ipu_idx = 0;
+ task_id = IPU_TASK_ID_VF;
+ min = task_num[0][0];
+ if (task_num[0][1] < min) {
+ min = task_num[0][1];
+ task_id = IPU_TASK_ID_PP;
+ }
+ if (task_num[1][0] < min) {
+ min = task_num[1][0];
+ ipu_idx = 1;
+ task_id = IPU_TASK_ID_VF;
+ }
+ if (task_num[1][1] < min) {
+ ipu_idx = 1;
+ task_id = IPU_TASK_ID_PP;
+ }
+ }
+
+ t->task_id = task_id;
+ ipu = ipu_get_soc(ipu_idx);
+
+ return ipu;
+}
+
+static void dump_task_info(struct ipu_task_entry *t)
+{
+ dev_dbg(t->dev, "[0x%p]input:\n", (void *)t);
+ dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->input.format);
+ dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->input.width);
+ dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->input.height);
+ dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", (void *)t, t->input.crop.w);
+ dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", (void *)t, t->input.crop.h);
+ dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n",
+ (void *)t, t->input.crop.pos.x);
+ dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n",
+ (void *)t, t->input.crop.pos.y);
+ dev_dbg(t->dev, "[0x%p]input buffer:\n", (void *)t);
+ dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->input.paddr);
+ dev_dbg(t->dev, "[0x%p]\ti_off = 0x%x\n", (void *)t, t->set.i_off);
+ dev_dbg(t->dev, "[0x%p]\ti_uoff = 0x%x\n", (void *)t, t->set.i_uoff);
+ dev_dbg(t->dev, "[0x%p]\ti_voff = 0x%x\n", (void *)t, t->set.i_voff);
+ dev_dbg(t->dev, "[0x%p]\tistride = %d\n", (void *)t, t->set.istride);
+ if (t->input.deinterlace.enable) {
+ dev_dbg(t->dev, "[0x%p]deinterlace enabled with:\n", (void *)t);
+ if (t->input.deinterlace.motion != HIGH_MOTION) {
+ dev_dbg(t->dev, "[0x%p]\tlow/medium motion\n", (void *)t);
+ dev_dbg(t->dev, "[0x%p]\tpaddr_n = 0x%x\n",
+ (void *)t, t->input.paddr_n);
+ } else
+ dev_dbg(t->dev, "[0x%p]\thigh motion\n", (void *)t);
+ }
+
+ dev_dbg(t->dev, "[0x%p]output:\n", (void *)t);
+ dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->output.format);
+ dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->output.width);
+ dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->output.height);
+ dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", (void *)t, t->output.crop.w);
+ dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", (void *)t, t->output.crop.h);
+ dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n",
+ (void *)t, t->output.crop.pos.x);
+ dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n",
+ (void *)t, t->output.crop.pos.y);
+ dev_dbg(t->dev, "[0x%p]\trotate = %d\n", (void *)t, t->output.rotate);
+ dev_dbg(t->dev, "[0x%p]output buffer:\n", (void *)t);
+ dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->output.paddr);
+ dev_dbg(t->dev, "[0x%p]\to_off = 0x%x\n", (void *)t, t->set.o_off);
+ dev_dbg(t->dev, "[0x%p]\to_uoff = 0x%x\n", (void *)t, t->set.o_uoff);
+ dev_dbg(t->dev, "[0x%p]\to_voff = 0x%x\n", (void *)t, t->set.o_voff);
+ dev_dbg(t->dev, "[0x%p]\tostride = %d\n", (void *)t, t->set.ostride);
+
+ if (t->overlay_en) {
+ dev_dbg(t->dev, "[0x%p]overlay:\n", (void *)t);
+ dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n",
+ (void *)t, t->overlay.format);
+ dev_dbg(t->dev, "[0x%p]\twidth = %d\n",
+ (void *)t, t->overlay.width);
+ dev_dbg(t->dev, "[0x%p]\theight = %d\n",
+ (void *)t, t->overlay.height);
+ dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n",
+ (void *)t, t->overlay.crop.w);
+ dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n",
+ (void *)t, t->overlay.crop.h);
+ dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n",
+ (void *)t, t->overlay.crop.pos.x);
+ dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n",
+ (void *)t, t->overlay.crop.pos.y);
+ dev_dbg(t->dev, "[0x%p]overlay buffer:\n", (void *)t);
+ dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n",
+ (void *)t, t->overlay.paddr);
+ dev_dbg(t->dev, "[0x%p]\tov_off = 0x%x\n",
+ (void *)t, t->set.ov_off);
+ dev_dbg(t->dev, "[0x%p]\tov_uoff = 0x%x\n",
+ (void *)t, t->set.ov_uoff);
+ dev_dbg(t->dev, "[0x%p]\tov_voff = 0x%x\n",
+ (void *)t, t->set.ov_voff);
+ dev_dbg(t->dev, "[0x%p]\tovstride = %d\n",
+ (void *)t, t->set.ovstride);
+ if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
+ dev_dbg(t->dev, "[0x%p]local alpha enabled with:\n",
+ (void *)t);
+ dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n",
+ (void *)t, t->overlay.alpha.loc_alp_paddr);
+ dev_dbg(t->dev, "[0x%p]\tov_alpha_off = 0x%x\n",
+ (void *)t, t->set.ov_alpha_off);
+ dev_dbg(t->dev, "[0x%p]\tov_alpha_stride = %d\n",
+ (void *)t, t->set.ov_alpha_stride);
+ } else
+ dev_dbg(t->dev, "[0x%p]globle alpha enabled with value 0x%x\n",
+ (void *)t, t->overlay.alpha.gvalue);
+ if (t->overlay.colorkey.enable)
+ dev_dbg(t->dev, "[0x%p]colorkey enabled with value 0x%x\n",
+ (void *)t, t->overlay.colorkey.value);
+ }
+
+ dev_dbg(t->dev, "[0x%p]want task_id = %d\n", (void *)t, t->task_id);
+ dev_dbg(t->dev, "[0x%p]want task mode is 0x%x\n",
+ (void *)t, t->set.mode);
+ dev_dbg(t->dev, "[0x%p]\tIC_MODE = 0x%x\n", (void *)t, IC_MODE);
+ dev_dbg(t->dev, "[0x%p]\tROT_MODE = 0x%x\n", (void *)t, ROT_MODE);
+ dev_dbg(t->dev, "[0x%p]\tVDI_MODE = 0x%x\n", (void *)t, VDI_MODE);
+}
+
+static void dump_check_err(struct device *dev, int err)
+{
+ switch (err) {
+ case IPU_CHECK_ERR_INPUT_CROP:
+ dev_err(dev, "input crop setting error\n");
+ break;
+ case IPU_CHECK_ERR_OUTPUT_CROP:
+ dev_err(dev, "output crop setting error\n");
+ break;
+ case IPU_CHECK_ERR_OVERLAY_CROP:
+ dev_err(dev, "overlay crop setting error\n");
+ break;
+ case IPU_CHECK_ERR_INPUT_OVER_LIMIT:
+ dev_err(dev, "input over limitation\n");
+ break;
+ case IPU_CHECK_ERR_OVERLAY_WITH_VDI:
+ dev_err(dev, "do not support overlay with deinterlace\n");
+ break;
+ case IPU_CHECK_ERR_OV_OUT_NO_FIT:
+ dev_err(dev,
+ "width/height of overlay and ic output should be same\n");
+ break;
+ case IPU_CHECK_ERR_PROC_NO_NEED:
+ dev_err(dev, "no ipu processing need\n");
+ break;
+ case IPU_CHECK_ERR_SPLIT_INPUTW_OVER:
+ dev_err(dev, "split mode input width overflow\n");
+ break;
+ case IPU_CHECK_ERR_SPLIT_INPUTH_OVER:
+ dev_err(dev, "split mode input height overflow\n");
+ break;
+ case IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER:
+ dev_err(dev, "split mode output width overflow\n");
+ break;
+ case IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER:
+ dev_err(dev, "split mode output height overflow\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static void dump_check_warn(struct device *dev, int warn)
+{
+ if (warn & IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN)
+ dev_warn(dev, "input u/v offset not 8 align\n");
+ if (warn & IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN)
+ dev_warn(dev, "output u/v offset not 8 align\n");
+ if (warn & IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN)
+ dev_warn(dev, "overlay u/v offset not 8 align\n");
+}
+
+static int set_crop(struct ipu_crop *crop, int width, int height)
+{
+ if (crop->w || crop->h) {
+ if (((crop->w + crop->pos.x) > width)
+ || ((crop->h + crop->pos.y) > height))
+ return -EINVAL;
+ } else {
+ crop->pos.x = 0;
+ crop->pos.y = 0;
+ crop->w = width;
+ crop->h = height;
+ }
+ crop->w -= crop->w%8;
+ crop->h -= crop->h%8;
+
+ return 0;
+}
+
+static void update_offset(unsigned int fmt,
+ unsigned int width, unsigned int height,
+ unsigned int pos_x, unsigned int pos_y,
+ int *off, int *uoff, int *voff, int *stride)
+{
+ /* NOTE: u v offset should based on start point of off*/
+ switch (fmt) {
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ *off = pos_y * width + pos_x;
+ *uoff = (width * (height - pos_y) - pos_x)
+ + ((width/2 * pos_y/2) + pos_x/2);
+ *voff = *uoff + (width/2 * height/2);
+ break;
+ case IPU_PIX_FMT_YVU420P:
+ *off = pos_y * width + pos_x;
+ *voff = (width * (height - pos_y) - pos_x)
+ + ((width/2 * pos_y/2) + pos_x/2);
+ *uoff = *voff + (width/2 * height/2);
+ break;
+ case IPU_PIX_FMT_YVU422P:
+ *off = pos_y * width + pos_x;
+ *voff = (width * (height - pos_y) - pos_x)
+ + ((width * pos_y)/2 + pos_x/2);
+ *uoff = *voff + (width * height)/2;
+ break;
+ case IPU_PIX_FMT_YUV422P:
+ *off = pos_y * width + pos_x;
+ *uoff = (width * (height - pos_y) - pos_x)
+ + (width * pos_y)/2 + pos_x/2;
+ *voff = *uoff + (width * height)/2;
+ break;
+ case IPU_PIX_FMT_NV12:
+ *off = pos_y * width + pos_x;
+ *uoff = (width * (height - pos_y) - pos_x)
+ + width * pos_y/2 + pos_x;
+ break;
+ default:
+ *off = (pos_y * width + pos_x) * fmt_to_bpp(fmt)/8;
+ break;
+ }
+ *stride = width * bytes_per_pixel(fmt);
+}
+
+static int update_split_setting(struct ipu_task_entry *t)
+{
+ struct stripe_param left_stripe;
+ struct stripe_param right_stripe;
+ struct stripe_param up_stripe;
+ struct stripe_param down_stripe;
+ u32 iw, ih, ow, oh;
+
+ iw = t->input.crop.w;
+ ih = t->input.crop.h;
+ if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+ ow = t->output.crop.h;
+ oh = t->output.crop.w;
+ } else {
+ ow = t->output.crop.w;
+ oh = t->output.crop.h;
+ }
+
+ if (t->set.split_mode & RL_SPLIT) {
+ ipu_calc_stripes_sizes(iw,
+ ow,
+ soc_max_out_width(),
+ (((unsigned long long)1) << 32), /* 32bit for fractional*/
+ 1, /* equal stripes */
+ t->input.format,
+ t->output.format,
+ &left_stripe,
+ &right_stripe);
+ t->set.sp_setting.iw = left_stripe.input_width;
+ t->set.sp_setting.ow = left_stripe.output_width;
+ t->set.sp_setting.outh_resize_ratio = left_stripe.irr;
+ t->set.sp_setting.i_left_pos = left_stripe.input_column;
+ t->set.sp_setting.o_left_pos = left_stripe.output_column;
+ t->set.sp_setting.i_right_pos = right_stripe.input_column;
+ t->set.sp_setting.o_right_pos = right_stripe.output_column;
+ } else {
+ t->set.sp_setting.iw = iw;
+ t->set.sp_setting.ow = ow;
+ t->set.sp_setting.outh_resize_ratio = 0;
+ t->set.sp_setting.i_left_pos = 0;
+ t->set.sp_setting.o_left_pos = 0;
+ t->set.sp_setting.i_right_pos = 0;
+ t->set.sp_setting.o_right_pos = 0;
+ }
+ if ((t->set.sp_setting.iw + t->set.sp_setting.i_right_pos) > iw)
+ return IPU_CHECK_ERR_SPLIT_INPUTW_OVER;
+ if (((t->set.sp_setting.ow + t->set.sp_setting.o_right_pos) > ow)
+ || (t->set.sp_setting.ow > soc_max_out_width()))
+ return IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER;
+
+ if (t->set.split_mode & UD_SPLIT) {
+ ipu_calc_stripes_sizes(ih,
+ oh,
+ soc_max_out_height(),
+ (((unsigned long long)1) << 32), /* 32bit for fractional*/
+ 1, /* equal stripes */
+ t->input.format,
+ t->output.format,
+ &up_stripe,
+ &down_stripe);
+ t->set.sp_setting.ih = up_stripe.input_width;
+ t->set.sp_setting.oh = up_stripe.output_width;
+ t->set.sp_setting.outv_resize_ratio = up_stripe.irr;
+ t->set.sp_setting.i_top_pos = up_stripe.input_column;
+ t->set.sp_setting.o_top_pos = up_stripe.output_column;
+ t->set.sp_setting.i_bottom_pos = down_stripe.input_column;
+ t->set.sp_setting.o_bottom_pos = down_stripe.output_column;
+ } else {
+ t->set.sp_setting.ih = ih;
+ t->set.sp_setting.oh = oh;
+ t->set.sp_setting.outv_resize_ratio = 0;
+ t->set.sp_setting.i_top_pos = 0;
+ t->set.sp_setting.o_top_pos = 0;
+ t->set.sp_setting.i_bottom_pos = 0;
+ t->set.sp_setting.o_bottom_pos = 0;
+ }
+ if ((t->set.sp_setting.ih + t->set.sp_setting.i_bottom_pos) > ih)
+ return IPU_CHECK_ERR_SPLIT_INPUTH_OVER;
+ if (((t->set.sp_setting.oh + t->set.sp_setting.o_bottom_pos) > oh)
+ || (t->set.sp_setting.oh > soc_max_out_height()))
+ return IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER;
+
+ return IPU_CHECK_OK;
+}
+
+static int check_task(struct ipu_task_entry *t)
+{
+ int tmp;
+ int ret = IPU_CHECK_OK;
+
+ /* check input */
+ ret = set_crop(&t->input.crop, t->input.width, t->input.height);
+ if (ret < 0) {
+ ret = IPU_CHECK_ERR_INPUT_CROP;
+ goto done;
+ } else
+ update_offset(t->input.format, t->input.width, t->input.height,
+ t->input.crop.pos.x, t->input.crop.pos.y,
+ &t->set.i_off, &t->set.i_uoff,
+ &t->set.i_voff, &t->set.istride);
+
+ /* check output */
+ ret = set_crop(&t->output.crop, t->output.width, t->output.height);
+ if (ret < 0) {
+ ret = IPU_CHECK_ERR_OUTPUT_CROP;
+ goto done;
+ } else
+ update_offset(t->output.format,
+ t->output.width, t->output.height,
+ t->output.crop.pos.x, t->output.crop.pos.y,
+ &t->set.o_off, &t->set.o_uoff,
+ &t->set.o_voff, &t->set.ostride);
+
+ /* check overlay if there is */
+ if (t->overlay_en) {
+ if (t->input.deinterlace.enable) {
+ ret = IPU_CHECK_ERR_OVERLAY_WITH_VDI;
+ goto done;
+ }
+ ret = set_crop(&t->overlay.crop, t->overlay.width, t->overlay.height);
+ if (ret < 0) {
+ ret = IPU_CHECK_ERR_OVERLAY_CROP;
+ goto done;
+ } else {
+ int ow = t->output.crop.w;
+ int oh = t->output.crop.h;
+
+ if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+ ow = t->output.crop.h;
+ oh = t->output.crop.w;
+ }
+ if ((t->overlay.crop.w != ow) || (t->overlay.crop.h != oh)) {
+ ret = IPU_CHECK_ERR_OV_OUT_NO_FIT;
+ goto done;
+ }
+
+ update_offset(t->overlay.format,
+ t->overlay.width, t->overlay.height,
+ t->overlay.crop.pos.x, t->overlay.crop.pos.y,
+ &t->set.ov_off, &t->set.ov_uoff,
+ &t->set.ov_voff, &t->set.ovstride);
+ if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
+ t->set.ov_alpha_stride = t->overlay.width;
+ t->set.ov_alpha_off = t->overlay.crop.pos.y *
+ t->overlay.width + t->overlay.crop.pos.x;
+ }
+ }
+ }
+
+ /* input overflow? */
+ if ((t->input.crop.w > soc_max_in_width()) ||
+ (t->input.crop.h > soc_max_in_height())) {
+ ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT;
+ goto done;
+ }
+
+ /* check task mode */
+ t->set.mode = NULL_MODE;
+ t->set.split_mode = NO_SPLIT;
+
+ if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+ /*output swap*/
+ tmp = t->output.crop.w;
+ t->output.crop.w = t->output.crop.h;
+ t->output.crop.h = tmp;
+ }
+
+ if (t->output.rotate >= IPU_ROTATE_90_RIGHT)
+ t->set.mode |= ROT_MODE;
+
+ /*need resize or CSC?*/
+ if ((t->input.crop.w != t->output.crop.w) ||
+ (t->input.crop.h != t->output.crop.h) ||
+ need_csc(t->input.format, t->output.format))
+ t->set.mode |= IC_MODE;
+
+ /*need flip?*/
+ if ((t->set.mode == NULL_MODE) && (t->output.rotate > IPU_ROTATE_NONE))
+ t->set.mode |= IC_MODE;
+
+ /*need IDMAC do format(same color space)?*/
+ if ((t->set.mode == NULL_MODE) && (t->input.format != t->output.format))
+ t->set.mode |= IC_MODE;
+
+ /*overlay support*/
+ if (t->overlay_en)
+ t->set.mode |= IC_MODE;
+
+ /*deinterlace*/
+ if (t->input.deinterlace.enable) {
+ t->set.mode &= ~IC_MODE;
+ t->set.mode |= VDI_MODE;
+ }
+
+ if (t->set.mode & (IC_MODE | VDI_MODE)) {
+ if (t->output.crop.w > soc_max_out_width())
+ t->set.split_mode |= RL_SPLIT;
+ if (t->output.crop.h > soc_max_out_height())
+ t->set.split_mode |= UD_SPLIT;
+ ret = update_split_setting(t);
+ if (ret > IPU_CHECK_ERR_MIN)
+ goto done;
+ }
+
+ if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+ /*output swap*/
+ tmp = t->output.crop.w;
+ t->output.crop.w = t->output.crop.h;
+ t->output.crop.h = tmp;
+ }
+
+ if (t->set.mode == NULL_MODE) {
+ ret = IPU_CHECK_ERR_PROC_NO_NEED;
+ goto done;
+ }
+
+ if ((t->set.i_uoff % 8) || (t->set.i_voff % 8))
+ ret |= IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN;
+ if ((t->set.o_uoff % 8) || (t->set.o_voff % 8))
+ ret |= IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN;
+ if (t->overlay_en && ((t->set.ov_uoff % 8) || (t->set.ov_voff % 8)))
+ ret |= IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN;
+
+done:
+ /* dump msg */
+ if (ret > IPU_CHECK_ERR_MIN)
+ dump_check_err(t->dev, ret);
+ else if (ret != IPU_CHECK_OK)
+ dump_check_warn(t->dev, ret);
+
+ return ret;
+}
+
+static int prepare_task(struct ipu_task_entry *t)
+{
+ int ret = 0;
+
+ ret = check_task(t);
+ if (ret > IPU_CHECK_ERR_MIN)
+ return -EINVAL;
+
+ dump_task_info(t);
+
+ return ret;
+}
+
+/* should call from a process context */
+static int queue_task(struct ipu_task_entry *t)
+{
+ int ret = 0;
+ struct ipu_soc *ipu;
+ struct list_head *task_list = NULL;
+ struct mutex *task_lock = NULL;
+ wait_queue_head_t *waitq = NULL;
+
+ ipu = most_free_ipu_task(t);
+ t->dev = ipu->dev;
+
+ dev_dbg(t->dev, "[0x%p]Queue task: id %d\n", (void *)t, t->task_id);
+
+ init_completion(&t->comp);
+
+ t->set.task = 0;
+ switch (t->task_id) {
+ case IPU_TASK_ID_VF:
+ task_list = &ipu->task_list[0];
+ task_lock = &ipu->task_lock[0];
+ waitq = &ipu->waitq[0];
+ if (t->set.mode & IC_MODE)
+ t->set.task |= IC_VF;
+ else if (t->set.mode & VDI_MODE)
+ t->set.task |= VDI_VF;
+ if (t->set.mode & ROT_MODE)
+ t->set.task |= ROT_VF;
+ break;
+ case IPU_TASK_ID_PP:
+ task_list = &ipu->task_list[1];
+ task_lock = &ipu->task_lock[1];
+ waitq = &ipu->waitq[1];
+ if (t->set.mode & IC_MODE)
+ t->set.task |= IC_PP;
+ if (t->set.mode & ROT_MODE)
+ t->set.task |= ROT_PP;
+ break;
+ default:
+ dev_err(t->dev, "[0x%p]should never come here\n", (void *)t);
+ }
+
+ dev_dbg(t->dev, "[0x%p]choose task_id[%d] mode[0x%x]\n",
+ (void *)t, t->task_id, t->set.task);
+ dev_dbg(t->dev, "[0x%p]\tIPU_TASK_ID_VF = %d\n",
+ (void *)t, IPU_TASK_ID_VF);
+ dev_dbg(t->dev, "[0x%p]\tIPU_TASK_ID_PP = %d\n",
+ (void *)t, IPU_TASK_ID_PP);
+ dev_dbg(t->dev, "[0x%p]\tIC_VF = 0x%x\n", (void *)t, IC_VF);
+ dev_dbg(t->dev, "[0x%p]\tIC_PP = 0x%x\n", (void *)t, IC_PP);
+ dev_dbg(t->dev, "[0x%p]\tROT_VF = 0x%x\n", (void *)t, ROT_VF);
+ dev_dbg(t->dev, "[0x%p]\tROT_PP = 0x%x\n", (void *)t, ROT_PP);
+ dev_dbg(t->dev, "[0x%p]\tVDI_VF = 0x%x\n", (void *)t, VDI_VF);
+
+ /* add and wait task */
+ mutex_lock(task_lock);
+ list_add_tail(&t->node, task_list);
+ mutex_unlock(task_lock);
+
+ wake_up_interruptible(waitq);
+
+ wait_for_completion(&t->comp);
+
+ dev_dbg(t->dev, "[0x%p]Queue task finished\n", (void *)t);
+
+ if (t->state != STATE_OK) {
+ dev_err(t->dev, "[0x%p]state %d: %s\n",
+ (void *)t, t->state, state_msg[t->state].msg);
+ ret = -ECANCELED;
+ }
+
+ return ret;
+}
+
+static bool need_split(struct ipu_task_entry *t)
+{
+ return (t->set.split_mode != NO_SPLIT);
+}
+
+static int split_task_thread(void *data)
+{
+ struct ipu_split_task *t = data;
+
+ t->ret = ipu_queue_sp_task(t);
+
+ while (!kthread_should_stop())
+ wait_event_interruptible(t->waitq, t->could_finish);
+
+ return 0;
+}
+
+static int create_split_task(
+ int stripe,
+ struct ipu_split_task *sp_task)
+{
+ struct ipu_task *task = &(sp_task->task);
+ struct ipu_task_entry *t = sp_task->parent_task;
+
+ task->input = t->input;
+ task->output = t->output;
+ task->overlay_en = t->overlay_en;
+ if (task->overlay_en)
+ task->overlay = t->overlay;
+ task->priority = t->priority;
+ task->task_id = t->task_id;
+ task->timeout = t->timeout;
+
+ task->input.crop.w = t->set.sp_setting.iw;
+ task->input.crop.h = t->set.sp_setting.ih;
+ if (task->overlay_en) {
+ task->overlay.crop.w = t->set.sp_setting.ow;
+ task->overlay.crop.h = t->set.sp_setting.oh;
+ }
+ if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+ task->output.crop.w = t->set.sp_setting.oh;
+ task->output.crop.h = t->set.sp_setting.ow;
+ } else {
+ task->output.crop.w = t->set.sp_setting.ow;
+ task->output.crop.h = t->set.sp_setting.oh;
+ }
+
+ if (stripe & LEFT_STRIPE)
+ task->input.crop.pos.x += t->set.sp_setting.i_left_pos;
+ else if (stripe & RIGHT_STRIPE)
+ task->input.crop.pos.x += t->set.sp_setting.i_right_pos;
+ if (stripe & UP_STRIPE)
+ task->input.crop.pos.y += t->set.sp_setting.i_top_pos;
+ else if (stripe & DOWN_STRIPE)
+ task->input.crop.pos.y += t->set.sp_setting.i_bottom_pos;
+
+ if (task->overlay_en) {
+ if (stripe & LEFT_STRIPE)
+ task->overlay.crop.pos.x += t->set.sp_setting.o_left_pos;
+ else if (stripe & RIGHT_STRIPE)
+ task->overlay.crop.pos.x += t->set.sp_setting.o_right_pos;
+ if (stripe & UP_STRIPE)
+ task->overlay.crop.pos.y += t->set.sp_setting.o_top_pos;
+ else if (stripe & DOWN_STRIPE)
+ task->overlay.crop.pos.y += t->set.sp_setting.o_bottom_pos;
+ }
+
+ switch (t->output.rotate) {
+ case IPU_ROTATE_NONE:
+ if (stripe & LEFT_STRIPE)
+ task->output.crop.pos.x += t->set.sp_setting.o_left_pos;
+ else if (stripe & RIGHT_STRIPE)
+ task->output.crop.pos.x += t->set.sp_setting.o_right_pos;
+ if (stripe & UP_STRIPE)
+ task->output.crop.pos.y += t->set.sp_setting.o_top_pos;
+ else if (stripe & DOWN_STRIPE)
+ task->output.crop.pos.y += t->set.sp_setting.o_bottom_pos;
+ break;
+ case IPU_ROTATE_VERT_FLIP:
+ if (stripe & LEFT_STRIPE)
+ task->output.crop.pos.x += t->set.sp_setting.o_left_pos;
+ else if (stripe & RIGHT_STRIPE)
+ task->output.crop.pos.x += t->set.sp_setting.o_right_pos;
+ if (stripe & UP_STRIPE)
+ task->output.crop.pos.y =
+ t->output.crop.pos.y + t->output.crop.h
+ - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh;
+ else if (stripe & DOWN_STRIPE)
+ task->output.crop.pos.y =
+ t->output.crop.pos.y + t->output.crop.h
+ - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh;
+ break;
+ case IPU_ROTATE_HORIZ_FLIP:
+ if (stripe & LEFT_STRIPE)
+ task->output.crop.pos.x =
+ t->output.crop.pos.x + t->output.crop.w
+ - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow;
+ else if (stripe & RIGHT_STRIPE)
+ task->output.crop.pos.x =
+ t->output.crop.pos.x + t->output.crop.w
+ - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow;
+ if (stripe & UP_STRIPE)
+ task->output.crop.pos.y += t->set.sp_setting.o_top_pos;
+ else if (stripe & DOWN_STRIPE)
+ task->output.crop.pos.y += t->set.sp_setting.o_bottom_pos;
+ break;
+ case IPU_ROTATE_180:
+ if (stripe & LEFT_STRIPE)
+ task->output.crop.pos.x =
+ t->output.crop.pos.x + t->output.crop.w
+ - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow;
+ else if (stripe & RIGHT_STRIPE)
+ task->output.crop.pos.x =
+ t->output.crop.pos.x + t->output.crop.w
+ - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow;
+ if (stripe & UP_STRIPE)
+ task->output.crop.pos.y =
+ t->output.crop.pos.y + t->output.crop.h
+ - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh;
+ else if (stripe & DOWN_STRIPE)
+ task->output.crop.pos.y =
+ t->output.crop.pos.y + t->output.crop.h
+ - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh;
+ break;
+ case IPU_ROTATE_90_RIGHT:
+ if (stripe & UP_STRIPE)
+ task->output.crop.pos.x =
+ t->output.crop.pos.x + t->output.crop.w
+ - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh;
+ else if (stripe & DOWN_STRIPE)
+ task->output.crop.pos.x =
+ t->output.crop.pos.x + t->output.crop.w
+ - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh;
+ if (stripe & LEFT_STRIPE)
+ task->output.crop.pos.y += t->set.sp_setting.o_left_pos;
+ else if (stripe & RIGHT_STRIPE)
+ task->output.crop.pos.y += t->set.sp_setting.o_right_pos;
+ break;
+ case IPU_ROTATE_90_RIGHT_HFLIP:
+ if (stripe & UP_STRIPE)
+ task->output.crop.pos.x += t->set.sp_setting.o_top_pos;
+ else if (stripe & DOWN_STRIPE)
+ task->output.crop.pos.x += t->set.sp_setting.o_bottom_pos;
+ if (stripe & LEFT_STRIPE)
+ task->output.crop.pos.y += t->set.sp_setting.o_left_pos;
+ else if (stripe & RIGHT_STRIPE)
+ task->output.crop.pos.y += t->set.sp_setting.o_right_pos;
+ break;
+ case IPU_ROTATE_90_RIGHT_VFLIP:
+ if (stripe & UP_STRIPE)
+ task->output.crop.pos.x =
+ t->output.crop.pos.x + t->output.crop.w
+ - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh;
+ else if (stripe & DOWN_STRIPE)
+ task->output.crop.pos.x =
+ t->output.crop.pos.x + t->output.crop.w
+ - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh;
+ if (stripe & LEFT_STRIPE)
+ task->output.crop.pos.y =
+ t->output.crop.pos.y + t->output.crop.h
+ - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow;
+ else if (stripe & RIGHT_STRIPE)
+ task->output.crop.pos.y =
+ t->output.crop.pos.y + t->output.crop.h
+ - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow;
+ break;
+ case IPU_ROTATE_90_LEFT:
+ if (stripe & UP_STRIPE)
+ task->output.crop.pos.x += t->set.sp_setting.o_top_pos;
+ else if (stripe & DOWN_STRIPE)
+ task->output.crop.pos.x += t->set.sp_setting.o_bottom_pos;
+ if (stripe & LEFT_STRIPE)
+ task->output.crop.pos.y =
+ t->output.crop.pos.y + t->output.crop.h
+ - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow;
+ else if (stripe & RIGHT_STRIPE)
+ task->output.crop.pos.y =
+ t->output.crop.pos.y + t->output.crop.h
+ - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow;
+ break;
+ default:
+ dev_err(t->dev, "should not be here\n");
+ break;
+ }
+
+ sp_task->thread = kthread_run(split_task_thread, sp_task,
+ "ipu_split_task");
+ if (IS_ERR(sp_task->thread)) {
+ dev_err(t->dev, "split thread can not create\n");
+ return PTR_ERR(sp_task->thread);
+ }
+
+ return 0;
+}
+
+static int queue_split_task(struct ipu_task_entry *t)
+{
+ struct ipu_split_task sp_task[4];
+ int i, ret = 0, size;
+
+ dev_dbg(t->dev, "Split task 0x%p\n", (void *)t);
+
+ if ((t->set.split_mode == RL_SPLIT) || (t->set.split_mode == UD_SPLIT))
+ size = 2;
+ else
+ size = 4;
+
+ for (i = 0; i < size; i++) {
+ memset(&sp_task[i], 0, sizeof(struct ipu_split_task));
+ init_waitqueue_head(&(sp_task[i].waitq));
+ sp_task[i].could_finish = false;
+ sp_task[i].parent_task = t;
+ }
+
+ if (t->set.split_mode == RL_SPLIT) {
+ create_split_task(LEFT_STRIPE, &sp_task[0]);
+ create_split_task(RIGHT_STRIPE, &sp_task[1]);
+ } else if (t->set.split_mode == UD_SPLIT) {
+ create_split_task(UP_STRIPE, &sp_task[0]);
+ create_split_task(DOWN_STRIPE, &sp_task[1]);
+ } else {
+ create_split_task(LEFT_STRIPE | UP_STRIPE, &sp_task[0]);
+ create_split_task(RIGHT_STRIPE | UP_STRIPE, &sp_task[1]);
+ create_split_task(LEFT_STRIPE | DOWN_STRIPE, &sp_task[2]);
+ create_split_task(RIGHT_STRIPE | DOWN_STRIPE, &sp_task[3]);
+ }
+
+ for (i = 0; i < size; i++) {
+ sp_task[i].could_finish = true;
+ wake_up_interruptible(&sp_task[i].waitq);
+ kthread_stop(sp_task[i].thread);
+ if (sp_task[i].ret < 0) {
+ ret = sp_task[i].ret;
+ dev_err(t->dev,
+ "split task %d fail with ret %d\n",
+ i, ret);
+ }
+ }
+
+ return ret;
+}
+
+static struct ipu_task_entry *create_task_entry(struct ipu_task *task)
+{
+ struct ipu_task_entry *tsk;
+
+ tsk = kzalloc(sizeof(struct ipu_task_entry), GFP_KERNEL);
+ if (!tsk)
+ return ERR_PTR(-ENOMEM);
+
+ tsk->dev = ipu_dev;
+ tsk->input = task->input;
+ tsk->output = task->output;
+ tsk->overlay_en = task->overlay_en;
+ if (tsk->overlay_en)
+ tsk->overlay = task->overlay;
+ tsk->priority = task->priority;
+ tsk->task_id = task->task_id;
+ if (task->timeout && (task->timeout > DEF_TIMEOUT_MS))
+ tsk->timeout = task->timeout;
+ else
+ tsk->timeout = DEF_TIMEOUT_MS;
+
+ return tsk;
+}
+
+int ipu_check_task(struct ipu_task *task)
+{
+ struct ipu_task_entry *tsk;
+ int ret = 0;
+
+ tsk = create_task_entry(task);
+ if (IS_ERR(tsk))
+ return PTR_ERR(tsk);
+
+ ret = check_task(tsk);
+
+ task->input = tsk->input;
+ task->output = tsk->output;
+ task->overlay = tsk->overlay;
-/* Strucutures and variables for exporting MXC IPU as device*/
+ kfree(tsk);
-static int mxc_ipu_major;
-static struct class *mxc_ipu_class;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_check_task);
-DEFINE_SPINLOCK(event_lock);
+int ipu_queue_sp_task(struct ipu_split_task *sp_task)
+{
+ struct ipu_task_entry *tsk;
+ int ret;
-struct ipu_dev_irq_info {
- wait_queue_head_t waitq;
- int irq_pending;
-} irq_info[480];
+ tsk = create_task_entry(&sp_task->task);
+ if (IS_ERR(tsk))
+ return PTR_ERR(tsk);
-int register_ipu_device(void);
+ ret = prepare_task(tsk);
+ if (ret < 0)
+ goto done;
-/* Static functions */
+ tsk->set.sp_setting = sp_task->parent_task->set.sp_setting;
+ tsk->set.sp_setting = sp_task->parent_task->set.sp_setting;
-int get_events(ipu_event_info *p)
+ ret = queue_task(tsk);
+done:
+ kfree(tsk);
+ return ret;
+}
+
+int ipu_queue_task(struct ipu_task *task)
{
- unsigned long flags;
- int ret = 0;
+ struct ipu_task_entry *tsk;
+ int ret;
- spin_lock_irqsave(&event_lock, flags);
- if (irq_info[p->irq].irq_pending > 0)
- irq_info[p->irq].irq_pending--;
- else
- ret = -1;
- spin_unlock_irqrestore(&event_lock, flags);
+ tsk = create_task_entry(task);
+ if (IS_ERR(tsk))
+ return PTR_ERR(tsk);
+
+ ret = prepare_task(tsk);
+ if (ret < 0)
+ goto done;
+ if (need_split(tsk))
+ ret = queue_split_task(tsk);
+ else
+ ret = queue_task(tsk);
+done:
+ kfree(tsk);
return ret;
}
+EXPORT_SYMBOL_GPL(ipu_queue_task);
-static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
+static bool only_ic(u8 mode)
{
- irq_info[irq].irq_pending++;
+ return ((mode == IC_MODE) || (mode == VDI_MODE));
+}
- /* Wakeup any blocking user context */
- wake_up_interruptible(&(irq_info[irq].waitq));
- return IRQ_HANDLED;
+static bool only_rot(u8 mode)
+{
+ return (mode == ROT_MODE);
}
-static int mxc_ipu_open(struct inode *inode, struct file *file)
+static bool ic_and_rot(u8 mode)
{
- int ret = 0;
- return ret;
+ return ((mode == (IC_MODE | ROT_MODE)) ||
+ (mode == (VDI_MODE | ROT_MODE)));
}
-static long mxc_ipu_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+static int init_ic(struct ipu_soc *ipu, struct ipu_task_entry *t)
{
int ret = 0;
+ ipu_channel_params_t params;
+ dma_addr_t inbuf = 0, ovbuf = 0, ov_alp_buf = 0;
+ dma_addr_t inbuf_p = 0, inbuf_n = 0;
+ dma_addr_t outbuf = 0;
+ int out_uoff = 0, out_voff = 0, out_rot;
+ int out_w = 0, out_h = 0, out_stride;
+ int out_fmt;
- switch (cmd) {
- case IPU_INIT_CHANNEL:
- {
- ipu_channel_parm parm;
+ memset(¶ms, 0, sizeof(params));
- if (copy_from_user
- (&parm, (ipu_channel_parm *) arg,
- sizeof(ipu_channel_parm)))
- return -EFAULT;
+ /* is it need link a rot channel */
+ if (ic_and_rot(t->set.mode)) {
+ outbuf = t->set.r_paddr;
+ out_w = t->set.r_width;
+ out_h = t->set.r_height;
+ out_stride = t->set.r_stride;
+ out_fmt = t->set.r_fmt;
+ out_uoff = 0;
+ out_voff = 0;
+ out_rot = IPU_ROTATE_NONE;
+ } else {
+ outbuf = t->output.paddr + t->set.o_off;
+ out_w = t->output.crop.w;
+ out_h = t->output.crop.h;
+ out_stride = t->set.ostride;
+ out_fmt = t->output.format;
+ out_uoff = t->set.o_uoff;
+ out_voff = t->set.o_voff;
+ out_rot = t->output.rotate;
+ }
- if (!parm.flag) {
- ret =
- ipu_init_channel(parm.channel,
- &parm.params);
- } else {
- ret = ipu_init_channel(parm.channel, NULL);
- }
- }
- break;
- case IPU_UNINIT_CHANNEL:
- {
- ipu_channel_t ch;
- int __user *argp = (void __user *)arg;
- if (get_user(ch, argp))
- return -EFAULT;
- ipu_uninit_channel(ch);
- }
- break;
- case IPU_INIT_CHANNEL_BUFFER:
- {
- ipu_channel_buf_parm parm;
- if (copy_from_user
- (&parm, (ipu_channel_buf_parm *) arg,
- sizeof(ipu_channel_buf_parm)))
- return -EFAULT;
+ /* settings */
+ params.mem_prp_vf_mem.in_width = t->input.crop.w;
+ params.mem_prp_vf_mem.out_width = out_w;
+ params.mem_prp_vf_mem.in_height = t->input.crop.h;
+ params.mem_prp_vf_mem.out_height = out_h;
+ params.mem_prp_vf_mem.in_pixel_fmt = t->input.format;
+ params.mem_prp_vf_mem.out_pixel_fmt = out_fmt;
+ params.mem_prp_vf_mem.motion_sel = t->input.deinterlace.motion;
- ret =
- ipu_init_channel_buffer(
- parm.channel, parm.type,
- parm.pixel_fmt,
- parm.width, parm.height,
- parm.stride,
- parm.rot_mode,
- parm.phyaddr_0,
- parm.phyaddr_1,
- parm.phyaddr_2,
- parm.u_offset,
- parm.v_offset);
+ params.mem_prp_vf_mem.outh_resize_ratio =
+ t->set.sp_setting.outh_resize_ratio;
+ params.mem_prp_vf_mem.outv_resize_ratio =
+ t->set.sp_setting.outv_resize_ratio;
+ if (t->overlay_en) {
+ params.mem_prp_vf_mem.in_g_pixel_fmt = t->overlay.format;
+ params.mem_prp_vf_mem.graphics_combine_en = 1;
+ if (t->overlay.alpha.mode == IPU_ALPHA_MODE_GLOBAL)
+ params.mem_prp_vf_mem.global_alpha_en = 1;
+ else
+ params.mem_prp_vf_mem.alpha_chan_en = 1;
+ params.mem_prp_vf_mem.alpha = t->overlay.alpha.gvalue;
+ if (t->overlay.colorkey.enable) {
+ params.mem_prp_vf_mem.key_color_en = 1;
+ params.mem_prp_vf_mem.key_color = t->overlay.colorkey.value;
}
- break;
- case IPU_UPDATE_CHANNEL_BUFFER:
- {
- ipu_channel_buf_parm parm;
- if (copy_from_user
- (&parm, (ipu_channel_buf_parm *) arg,
- sizeof(ipu_channel_buf_parm)))
- return -EFAULT;
+ }
- if ((parm.phyaddr_0 != (dma_addr_t) NULL)
- && (parm.phyaddr_1 == (dma_addr_t) NULL)) {
- ret =
- ipu_update_channel_buffer(
- parm.channel,
- parm.type,
- parm.bufNum,
- parm.phyaddr_0);
- } else if ((parm.phyaddr_0 == (dma_addr_t) NULL)
- && (parm.phyaddr_1 != (dma_addr_t) NULL)) {
- ret =
- ipu_update_channel_buffer(
- parm.channel,
- parm.type,
- parm.bufNum,
- parm.phyaddr_1);
- } else {
- ret = -1;
- }
+ /* init channels */
+ ret = ipu_init_channel(ipu, t->set.ic_chan, ¶ms);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_FAIL;
+ goto done;
+ }
+ if (deinterlace_3_field(t)) {
+ ret = ipu_init_channel(ipu, t->set.vdi_ic_p_chan, ¶ms);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_FAIL;
+ goto done;
}
- break;
- case IPU_SELECT_CHANNEL_BUFFER:
- {
- ipu_channel_buf_parm parm;
- if (copy_from_user
- (&parm, (ipu_channel_buf_parm *) arg,
- sizeof(ipu_channel_buf_parm)))
- return -EFAULT;
+ ret = ipu_init_channel(ipu, t->set.vdi_ic_n_chan, ¶ms);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_FAIL;
+ goto done;
+ }
+ }
+
+ /* init channel bufs */
+ if (deinterlace_3_field(t)) {
+ inbuf_p = t->input.paddr + t->set.istride + t->set.i_off;
+ inbuf = t->input.paddr_n + t->set.i_off;
+ inbuf_n = t->input.paddr_n + t->set.istride + t->set.i_off;
+ } else
+ inbuf = t->input.paddr + t->set.i_off;
- ret =
- ipu_select_buffer(parm.channel,
- parm.type, parm.bufNum);
+ if (t->overlay_en) {
+ ovbuf = t->overlay.paddr + t->set.ov_off;
+ if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL)
+ ov_alp_buf = t->overlay.alpha.loc_alp_paddr
+ + t->set.ov_alpha_off;
+ }
+
+ ret = ipu_init_channel_buffer(ipu,
+ t->set.ic_chan,
+ IPU_INPUT_BUFFER,
+ t->input.format,
+ t->input.crop.w,
+ t->input.crop.h,
+ t->set.istride,
+ IPU_ROTATE_NONE,
+ inbuf,
+ 0,
+ 0,
+ t->set.i_uoff,
+ t->set.i_voff);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_BUF_FAIL;
+ goto done;
+ }
+ if (deinterlace_3_field(t)) {
+ ret = ipu_init_channel_buffer(ipu,
+ t->set.vdi_ic_p_chan,
+ IPU_INPUT_BUFFER,
+ t->input.format,
+ t->input.crop.w,
+ t->input.crop.h,
+ t->set.istride,
+ IPU_ROTATE_NONE,
+ inbuf_p,
+ 0,
+ 0,
+ t->set.i_uoff,
+ t->set.i_voff);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_BUF_FAIL;
+ goto done;
}
- break;
- case IPU_SELECT_MULTI_VDI_BUFFER:
- {
- uint32_t parm;
- if (copy_from_user
- (&parm, (uint32_t *) arg,
- sizeof(uint32_t)))
- return -EFAULT;
- ret = ipu_select_multi_vdi_buffer(parm);
+ ret = ipu_init_channel_buffer(ipu,
+ t->set.vdi_ic_n_chan,
+ IPU_INPUT_BUFFER,
+ t->input.format,
+ t->input.crop.w,
+ t->input.crop.h,
+ t->set.istride,
+ IPU_ROTATE_NONE,
+ inbuf_n,
+ 0,
+ 0,
+ t->set.i_uoff,
+ t->set.i_voff);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_BUF_FAIL;
+ goto done;
}
- break;
- case IPU_LINK_CHANNELS:
- {
- ipu_channel_link link;
- if (copy_from_user
- (&link, (ipu_channel_link *) arg,
- sizeof(ipu_channel_link)))
- return -EFAULT;
+ }
- ret = ipu_link_channels(link.src_ch,
- link.dest_ch);
+ if (t->overlay_en) {
+ ret = ipu_init_channel_buffer(ipu,
+ t->set.ic_chan,
+ IPU_GRAPH_IN_BUFFER,
+ t->overlay.format,
+ t->overlay.crop.w,
+ t->overlay.crop.h,
+ t->set.ovstride,
+ IPU_ROTATE_NONE,
+ ovbuf,
+ 0,
+ 0,
+ t->set.ov_uoff,
+ t->set.ov_voff);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_BUF_FAIL;
+ goto done;
+ }
+ if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
+ ret = ipu_init_channel_buffer(ipu,
+ t->set.ic_chan,
+ IPU_ALPHA_IN_BUFFER,
+ IPU_PIX_FMT_GENERIC,
+ t->overlay.crop.w,
+ t->overlay.crop.h,
+ t->set.ov_alpha_stride,
+ IPU_ROTATE_NONE,
+ ov_alp_buf,
+ 0,
+ 0,
+ 0, 0);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_BUF_FAIL;
+ goto done;
+ }
}
- break;
- case IPU_UNLINK_CHANNELS:
- {
- ipu_channel_link link;
- if (copy_from_user
- (&link, (ipu_channel_link *) arg,
- sizeof(ipu_channel_link)))
- return -EFAULT;
+ }
+
+ ret = ipu_init_channel_buffer(ipu,
+ t->set.ic_chan,
+ IPU_OUTPUT_BUFFER,
+ out_fmt,
+ out_w,
+ out_h,
+ out_stride,
+ out_rot,
+ outbuf,
+ 0,
+ 0,
+ out_uoff,
+ out_voff);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_BUF_FAIL;
+ goto done;
+ }
- ret = ipu_unlink_channels(link.src_ch,
- link.dest_ch);
+done:
+ return ret;
+}
- }
+static void uninit_ic(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+ ipu_uninit_channel(ipu, t->set.ic_chan);
+ if (deinterlace_3_field(t)) {
+ ipu_uninit_channel(ipu, t->set.vdi_ic_p_chan);
+ ipu_uninit_channel(ipu, t->set.vdi_ic_n_chan);
+ }
+}
+
+static int init_rot(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+ int ret = 0;
+ dma_addr_t inbuf = 0, outbuf = 0;
+ int in_uoff = 0, in_voff = 0;
+ int in_fmt, in_width, in_height, in_stride;
+
+ /* init channel */
+ ret = ipu_init_channel(ipu, t->set.rot_chan, NULL);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_FAIL;
+ goto done;
+ }
+
+ /* init channel buf */
+ /* is it need link to a ic channel */
+ if (ic_and_rot(t->set.mode)) {
+ in_fmt = t->set.r_fmt;
+ in_width = t->set.r_width;
+ in_height = t->set.r_height;
+ in_stride = t->set.r_stride;
+ inbuf = t->set.r_paddr;
+ in_uoff = 0;
+ in_voff = 0;
+ } else {
+ in_fmt = t->input.format;
+ in_width = t->input.crop.w;
+ in_height = t->input.crop.h;
+ in_stride = t->set.istride;
+ inbuf = t->input.paddr + t->set.i_off;
+ in_uoff = t->set.i_uoff;
+ in_voff = t->set.i_voff;
+ }
+ outbuf = t->output.paddr + t->set.o_off;
+
+ ret = ipu_init_channel_buffer(ipu,
+ t->set.rot_chan,
+ IPU_INPUT_BUFFER,
+ in_fmt,
+ in_width,
+ in_height,
+ t->set.istride,
+ t->output.rotate,
+ inbuf,
+ 0,
+ 0,
+ in_uoff,
+ in_voff);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_BUF_FAIL;
+ goto done;
+ }
+
+ ret = ipu_init_channel_buffer(ipu,
+ t->set.rot_chan,
+ IPU_OUTPUT_BUFFER,
+ t->output.format,
+ t->output.crop.w,
+ t->output.crop.h,
+ t->set.ostride,
+ IPU_ROTATE_NONE,
+ outbuf,
+ 0,
+ 0,
+ t->set.o_uoff,
+ t->set.o_voff);
+ if (ret < 0) {
+ t->state = STATE_INIT_CHAN_BUF_FAIL;
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static void uninit_rot(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+ ipu_uninit_channel(ipu, t->set.rot_chan);
+}
+
+static int get_irq(struct ipu_task_entry *t)
+{
+ int irq;
+ ipu_channel_t chan;
+
+ if (only_ic(t->set.mode))
+ chan = t->set.ic_chan;
+ else
+ chan = t->set.rot_chan;
+
+ switch (chan) {
+ case MEM_ROT_VF_MEM:
+ irq = IPU_IRQ_PRP_VF_ROT_OUT_EOF;
break;
- case IPU_ENABLE_CHANNEL:
- {
- ipu_channel_t ch;
- int __user *argp = (void __user *)arg;
- if (get_user(ch, argp))
- return -EFAULT;
- ipu_enable_channel(ch);
- }
+ case MEM_ROT_PP_MEM:
+ irq = IPU_IRQ_PP_ROT_OUT_EOF;
break;
- case IPU_DISABLE_CHANNEL:
- {
- ipu_channel_info info;
- if (copy_from_user
- (&info, (ipu_channel_info *) arg,
- sizeof(ipu_channel_info)))
- return -EFAULT;
+ case MEM_VDI_PRP_VF_MEM:
+ case MEM_PRP_VF_MEM:
+ irq = IPU_IRQ_PRP_VF_OUT_EOF;
+ break;
+ case MEM_PP_MEM:
+ irq = IPU_IRQ_PP_OUT_EOF;
+ break;
+ default:
+ irq = -EINVAL;
+ }
+
+ return irq;
+}
+
+static irqreturn_t task_irq_handler(int irq, void *dev_id)
+{
+ struct completion *comp = dev_id;
+ complete(comp);
+ return IRQ_HANDLED;
+}
+
+static void do_task(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+ struct completion comp;
+ void *r_vaddr;
+ int r_size;
+ int irq;
+ int ret;
+
+ if (!ipu) {
+ t->state = STATE_NO_IPU;
+ return;
+ }
+
+ dev_dbg(ipu->dev, "[0x%p]Do task: id %d\n", (void *)t, t->task_id);
+ dump_task_info(t);
- ret = ipu_disable_channel(info.channel,
- info.stop);
+ if (t->set.task & IC_PP) {
+ t->set.ic_chan = MEM_PP_MEM;
+ dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PP_MEM\n", (void *)t);
+ } else if (t->set.task & IC_VF) {
+ t->set.ic_chan = MEM_PRP_VF_MEM;
+ dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PRP_VF_MEM\n", (void *)t);
+ } else if (t->set.task & VDI_VF) {
+ t->set.ic_chan = MEM_VDI_PRP_VF_MEM;
+ if (deinterlace_3_field(t)) {
+ t->set.vdi_ic_p_chan = MEM_VDI_PRP_VF_MEM_P;
+ t->set.vdi_ic_n_chan = MEM_VDI_PRP_VF_MEM_N;
}
- break;
- case IPU_ENABLE_IRQ:
- {
- uint32_t irq;
- int __user *argp = (void __user *)arg;
- if (get_user(irq, argp))
- return -EFAULT;
- ipu_enable_irq(irq);
+ dev_dbg(ipu->dev, "[0x%p]ic channel MEM_VDI_PRP_VF_MEM\n", (void *)t);
+ }
+
+ if (t->set.task & ROT_PP) {
+ t->set.rot_chan = MEM_ROT_PP_MEM;
+ dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_PP_MEM\n", (void *)t);
+ } else if (t->set.task & ROT_VF) {
+ t->set.rot_chan = MEM_ROT_VF_MEM;
+ dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_VF_MEM\n", (void *)t);
+ }
+
+ /* channel setup */
+ if (only_ic(t->set.mode)) {
+ dev_dbg(t->dev, "[0x%p]only ic mode\n", (void *)t);
+ ret = init_ic(ipu, t);
+ if (ret < 0)
+ goto chan_done;
+ } else if (only_rot(t->set.mode)) {
+ dev_dbg(t->dev, "[0x%p]only rot mode\n", (void *)t);
+ ret = init_rot(ipu, t);
+ if (ret < 0)
+ goto chan_done;
+ } else if (ic_and_rot(t->set.mode)) {
+ dev_dbg(t->dev, "[0x%p]ic + rot mode\n", (void *)t);
+ t->set.r_fmt = t->output.format;
+ if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+ t->set.r_width = t->output.crop.h;
+ t->set.r_height = t->output.crop.w;
+ } else {
+ t->set.r_width = t->output.crop.w;
+ t->set.r_height = t->output.crop.h;
}
- break;
- case IPU_DISABLE_IRQ:
- {
- uint32_t irq;
- int __user *argp = (void __user *)arg;
- if (get_user(irq, argp))
- return -EFAULT;
- ipu_disable_irq(irq);
+ t->set.r_stride = t->set.r_width *
+ bytes_per_pixel(t->set.r_fmt);
+ r_size = t->set.r_width * t->set.r_height
+ * fmt_to_bpp(t->set.r_fmt)/8;
+ r_vaddr = dma_alloc_coherent(t->dev,
+ r_size,
+ &t->set.r_paddr,
+ GFP_DMA | GFP_KERNEL);
+ if (r_vaddr == NULL) {
+ ret = -ENOMEM;
+ goto chan_done;
}
- break;
- case IPU_CLEAR_IRQ:
- {
- uint32_t irq;
- int __user *argp = (void __user *)arg;
- if (get_user(irq, argp))
- return -EFAULT;
- ipu_clear_irq(irq);
+
+ dev_dbg(t->dev, "[0x%p]rotation:\n", (void *)t);
+ dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->set.r_fmt);
+ dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->set.r_width);
+ dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->set.r_height);
+ dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->set.r_paddr);
+ dev_dbg(t->dev, "[0x%p]\trstride = %d\n", (void *)t, t->set.r_stride);
+
+ ret = init_ic(ipu, t);
+ if (ret < 0)
+ goto chan_done;
+ ret = init_rot(ipu, t);
+ if (ret < 0)
+ goto chan_done;
+ ret = ipu_link_channels(ipu, t->set.ic_chan,
+ t->set.rot_chan);
+ if (ret < 0) {
+ t->state = STATE_LINK_CHAN_FAIL;
+ goto chan_done;
}
- break;
- case IPU_FREE_IRQ:
- {
- ipu_irq_info info;
+ } else {
+ dev_err(t->dev, "[0x%p]do_task: should not be here\n", (void *)t);
+ return;
+ }
- if (copy_from_user
- (&info, (ipu_irq_info *) arg,
- sizeof(ipu_irq_info)))
- return -EFAULT;
+ /* irq setup */
+ irq = get_irq(t);
+ if (irq < 0) {
+ t->state = STATE_NO_IRQ;
+ goto chan_done;
+ }
+
+ dev_dbg(t->dev, "[0x%p]task irq is %d\n", (void *)t, irq);
- ipu_free_irq(info.irq, info.dev_id);
- irq_info[info.irq].irq_pending = 0;
+ init_completion(&comp);
+ ipu_clear_irq(ipu, irq);
+ ret = ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, &comp);
+ if (ret < 0) {
+ t->state = STATE_IRQ_FAIL;
+ goto chan_done;
+ }
+
+ /* enable/start channel */
+ if (only_ic(t->set.mode)) {
+ ipu_enable_channel(ipu, t->set.ic_chan);
+ if (deinterlace_3_field(t)) {
+ ipu_enable_channel(ipu, t->set.vdi_ic_p_chan);
+ ipu_enable_channel(ipu, t->set.vdi_ic_n_chan);
}
- break;
- case IPU_REQUEST_IRQ_STATUS:
- {
- uint32_t irq;
- int __user *argp = (void __user *)arg;
- if (get_user(irq, argp))
- return -EFAULT;
- ret = ipu_get_irq_status(irq);
+
+ ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER, 0);
+ if (t->overlay_en) {
+ ipu_select_buffer(ipu, t->set.ic_chan, IPU_GRAPH_IN_BUFFER, 0);
+ if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL)
+ ipu_select_buffer(ipu, t->set.ic_chan, IPU_ALPHA_IN_BUFFER, 0);
+ }
+ if (deinterlace_3_field(t))
+ ipu_select_multi_vdi_buffer(ipu, 0);
+ else
+ ipu_select_buffer(ipu, t->set.ic_chan, IPU_INPUT_BUFFER, 0);
+ } else if (only_rot(t->set.mode)) {
+ ipu_enable_channel(ipu, t->set.rot_chan);
+ ipu_select_buffer(ipu, t->set.rot_chan, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(ipu, t->set.rot_chan, IPU_INPUT_BUFFER, 0);
+ } else if (ic_and_rot(t->set.mode)) {
+ ipu_enable_channel(ipu, t->set.rot_chan);
+ ipu_enable_channel(ipu, t->set.ic_chan);
+ if (deinterlace_3_field(t)) {
+ ipu_enable_channel(ipu, t->set.vdi_ic_p_chan);
+ ipu_enable_channel(ipu, t->set.vdi_ic_n_chan);
}
- break;
- case IPU_REGISTER_GENERIC_ISR:
- {
- ipu_event_info info;
- if (copy_from_user
- (&info, (ipu_event_info *) arg,
- sizeof(ipu_event_info)))
- return -EFAULT;
- ret =
- ipu_request_irq(info.irq,
- mxc_ipu_generic_handler,
- 0, "video_sink", info.dev);
- if (ret == 0)
- init_waitqueue_head(&(irq_info[info.irq].waitq));
+ ipu_select_buffer(ipu, t->set.rot_chan, IPU_OUTPUT_BUFFER, 0);
+ if (t->overlay_en) {
+ ipu_select_buffer(ipu, t->set.ic_chan, IPU_GRAPH_IN_BUFFER, 0);
+ if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL)
+ ipu_select_buffer(ipu, t->set.ic_chan, IPU_ALPHA_IN_BUFFER, 0);
}
- break;
- case IPU_GET_EVENT:
- /* User will have to allocate event_type
- structure and pass the pointer in arg */
- {
- ipu_event_info info;
- int r = -1;
+ if (deinterlace_3_field(t))
+ ipu_select_multi_vdi_buffer(ipu, 0);
+ else
+ ipu_select_buffer(ipu, t->set.ic_chan, IPU_INPUT_BUFFER, 0);
+ ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER, 0);
+ }
- if (copy_from_user
- (&info, (ipu_event_info *) arg,
- sizeof(ipu_event_info)))
- return -EFAULT;
+ ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(t->timeout));
+ if (ret == 0)
+ t->state = STATE_IRQ_TIMEOUT;
- r = get_events(&info);
- if (r == -1) {
- if ((file->f_flags & O_NONBLOCK) &&
- (irq_info[info.irq].irq_pending == 0))
- return -EAGAIN;
- wait_event_interruptible_timeout(irq_info[info.irq].waitq,
- (irq_info[info.irq].irq_pending != 0), 2 * HZ);
- r = get_events(&info);
- }
- ret = -1;
- if (r == 0) {
- if (!copy_to_user((ipu_event_info *) arg,
- &info, sizeof(ipu_event_info)))
- ret = 0;
- }
+ ipu_free_irq(ipu, irq, &comp);
+ if (t->set.task & IC_VF) {
+ ipu_clear_irq(ipu, IPU_IRQ_PRP_IN_EOF);
+ ipu_clear_irq(ipu, IPU_IRQ_PRP_VF_OUT_EOF);
+ } else if (t->set.task & IC_PP) {
+ ipu_clear_irq(ipu, IPU_IRQ_PP_IN_EOF);
+ ipu_clear_irq(ipu, IPU_IRQ_PP_OUT_EOF);
+ } else if (t->set.task & VDI_VF) {
+ ipu_clear_irq(ipu, IPU_IRQ_VDI_C_IN_EOF);
+ if (deinterlace_3_field(t)) {
+ ipu_clear_irq(ipu, IPU_IRQ_VDI_P_IN_EOF);
+ ipu_clear_irq(ipu, IPU_IRQ_VDI_N_IN_EOF);
}
- break;
- case IPU_ALOC_MEM:
- {
- ipu_mem_info info;
- if (copy_from_user
- (&info, (ipu_mem_info *) arg,
- sizeof(ipu_mem_info)))
- return -EFAULT;
+ ipu_clear_irq(ipu, IPU_IRQ_PRP_VF_OUT_EOF);
+ }
+ if (t->set.task & ROT_VF) {
+ ipu_clear_irq(ipu, IPU_IRQ_PRP_VF_ROT_IN_EOF);
+ ipu_clear_irq(ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF);
+ } else if (t->set.task & ROT_PP) {
+ ipu_clear_irq(ipu, IPU_IRQ_PP_ROT_IN_EOF);
+ ipu_clear_irq(ipu, IPU_IRQ_PP_ROT_OUT_EOF);
+ }
+
+ if (only_ic(t->set.mode)) {
+ ipu_disable_channel(ipu, t->set.ic_chan, true);
+ if (deinterlace_3_field(t)) {
+ ipu_disable_channel(ipu, t->set.vdi_ic_p_chan, true);
+ ipu_disable_channel(ipu, t->set.vdi_ic_n_chan, true);
+ }
+ } else if (only_rot(t->set.mode))
+ ipu_disable_channel(ipu, t->set.rot_chan, true);
+ else if (ic_and_rot(t->set.mode)) {
+ ipu_disable_channel(ipu, t->set.ic_chan, true);
+ if (deinterlace_3_field(t)) {
+ ipu_disable_channel(ipu, t->set.vdi_ic_p_chan, true);
+ ipu_disable_channel(ipu, t->set.vdi_ic_n_chan, true);
+ }
+ ipu_disable_channel(ipu, t->set.rot_chan, true);
+ }
+
+chan_done:
+ if (only_ic(t->set.mode))
+ uninit_ic(ipu, t);
+ else if (only_rot(t->set.mode))
+ uninit_rot(ipu, t);
+ else if (ic_and_rot(t->set.mode)) {
+ ipu_unlink_channels(ipu, t->set.ic_chan,
+ t->set.rot_chan);
+ uninit_ic(ipu, t);
+ uninit_rot(ipu, t);
+ if (r_vaddr)
+ dma_free_coherent(t->dev,
+ r_size,
+ r_vaddr,
+ t->set.r_paddr);
+ }
+ return;
+}
- info.vaddr = dma_alloc_coherent(0,
- PAGE_ALIGN(info.size),
- &info.paddr,
- GFP_DMA | GFP_KERNEL);
- if (info.vaddr == 0) {
- printk(KERN_ERR "dma alloc failed!\n");
- return -ENOBUFS;
+static int thread_loop(struct ipu_soc *ipu, int id)
+{
+ struct ipu_task_entry *tsk;
+ struct list_head *task_list = &ipu->task_list[id];
+ struct mutex *task_lock = &ipu->task_lock[id];
+ int ret;
+
+ while (!kthread_should_stop()) {
+ int found = 0;
+
+ ret = wait_event_interruptible(ipu->waitq[id], !list_empty(task_list));
+ if (0 != ret)
+ continue;
+
+ mutex_lock(task_lock);
+
+ list_for_each_entry(tsk, task_list, node) {
+ if (tsk->priority == IPU_TASK_PRIORITY_HIGH) {
+ found = 1;
+ break;
}
- if (copy_to_user((ipu_mem_info *) arg, &info,
- sizeof(ipu_mem_info)) > 0)
- return -EFAULT;
}
- break;
- case IPU_FREE_MEM:
+
+ if (!found)
+ tsk = list_first_entry(task_list, struct ipu_task_entry, node);
+
+ mutex_unlock(task_lock);
+
+ do_task(ipu, tsk);
+
+ mutex_lock(task_lock);
+ list_del(&tsk->node);
+ mutex_unlock(task_lock);
+
+ complete(&tsk->comp);
+ }
+
+ return 0;
+}
+
+static int task_vf_thread(void *data)
+{
+ struct ipu_soc *ipu = data;
+
+ thread_loop(ipu, 0);
+
+ return 0;
+}
+
+static int task_pp_thread(void *data)
+{
+ struct ipu_soc *ipu = data;
+
+ thread_loop(ipu, 1);
+
+ return 0;
+}
+
+static int mxc_ipu_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static long mxc_ipu_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int __user *argp = (void __user *)arg;
+ int ret = 0;
+
+ switch (cmd) {
+ case IPU_CHECK_TASK:
{
- ipu_mem_info info;
+ struct ipu_task task;
+
if (copy_from_user
- (&info, (ipu_mem_info *) arg,
- sizeof(ipu_mem_info)))
+ (&task, (struct ipu_task *) arg,
+ sizeof(struct ipu_task)))
return -EFAULT;
-
- if (info.vaddr)
- dma_free_coherent(0, PAGE_ALIGN(info.size),
- info.vaddr, info.paddr);
- else
+ ret = ipu_check_task(&task);
+ if (copy_to_user((struct ipu_task *) arg,
+ &task, sizeof(struct ipu_task)))
return -EFAULT;
+ break;
}
- break;
- case IPU_IS_CHAN_BUSY:
+ case IPU_QUEUE_TASK:
{
- ipu_channel_t chan;
+ struct ipu_task task;
+
if (copy_from_user
- (&chan, (ipu_channel_t *)arg,
- sizeof(ipu_channel_t)))
+ (&task, (struct ipu_task *) arg,
+ sizeof(struct ipu_task)))
return -EFAULT;
-
- if (ipu_is_channel_busy(chan))
- ret = 1;
- else
- ret = 0;
+ ret = ipu_queue_task(&task);
+ break;
}
- break;
- case IPU_CALC_STRIPES_SIZE:
+ case IPU_ALLOC:
{
- ipu_stripe_parm stripe_parm;
+ int size;
+ struct ipu_alloc_list *mem;
- if (copy_from_user (&stripe_parm, (ipu_stripe_parm *)arg,
- sizeof(ipu_stripe_parm)))
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (mem == NULL)
+ return -ENOMEM;
+
+ if (get_user(size, argp))
return -EFAULT;
- ipu_calc_stripes_sizes(stripe_parm.input_width,
- stripe_parm.output_width,
- stripe_parm.maximal_stripe_width,
- stripe_parm.cirr,
- stripe_parm.equal_stripes,
- stripe_parm.input_pixelformat,
- stripe_parm.output_pixelformat,
- &stripe_parm.left,
- &stripe_parm.right);
- if (copy_to_user((ipu_stripe_parm *) arg, &stripe_parm,
- sizeof(ipu_stripe_parm)) > 0)
+
+ mem->size = PAGE_ALIGN(size);
+
+ mem->cpu_addr = dma_alloc_coherent(ipu_dev, size,
+ &mem->phy_addr,
+ GFP_DMA);
+ if (mem->cpu_addr == NULL) {
+ kfree(mem);
+ return -ENOMEM;
+ }
+
+ list_add(&mem->list, &ipu_alloc_list);
+
+ dev_dbg(ipu_dev, "allocated %d bytes @ 0x%08X\n",
+ mem->size, mem->phy_addr);
+
+ if (put_user(mem->phy_addr, argp))
return -EFAULT;
+
+ break;
}
- break;
- case IPU_UPDATE_BUF_OFFSET:
+ case IPU_FREE:
{
- ipu_buf_offset_parm offset_parm;
+ unsigned long offset;
+ struct ipu_alloc_list *mem;
- if (copy_from_user (&offset_parm, (ipu_buf_offset_parm *)arg,
- sizeof(ipu_buf_offset_parm)))
- return -EFAULT;
- ret = ipu_update_channel_offset(offset_parm.channel,
- offset_parm.type,
- offset_parm.pixel_fmt,
- offset_parm.width,
- offset_parm.height,
- offset_parm.stride,
- offset_parm.u_offset,
- offset_parm.v_offset,
- offset_parm.vertical_offset,
- offset_parm.horizontal_offset);
- }
- break;
- case IPU_CSC_UPDATE:
- {
- int param[5][3];
- ipu_csc_update csc;
- if (copy_from_user(&csc, (void *) arg,
- sizeof(ipu_csc_update)))
- return -EFAULT;
- if (copy_from_user(¶m[0][0], (void *) csc.param,
- sizeof(param)))
+ if (get_user(offset, argp))
return -EFAULT;
- ipu_set_csc_coefficients(csc.channel, param);
+
+ ret = -EINVAL;
+ list_for_each_entry(mem, &ipu_alloc_list, list) {
+ if (mem->phy_addr == offset) {
+ list_del(&mem->list);
+ dma_free_coherent(ipu_dev,
+ mem->size,
+ mem->cpu_addr,
+ mem->phy_addr);
+ kfree(mem);
+ ret = 0;
+ break;
+ }
+ }
+
+ break;
}
- break;
default:
break;
}
static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma)
{
+ bool found = false;
+ u32 len;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct ipu_alloc_list *mem;
+
+ list_for_each_entry(mem, &ipu_alloc_list, list) {
+ if (offset == mem->phy_addr) {
+ found = true;
+ len = mem->size;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+
+ if (vma->vm_end - vma->vm_start > len)
+ return -EINVAL;
+
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
.unlocked_ioctl = mxc_ipu_ioctl,
};
-int register_ipu_device()
+int register_ipu_device(struct ipu_soc *ipu, int id)
{
int ret = 0;
- struct device *temp;
- mxc_ipu_major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);
- if (mxc_ipu_major < 0) {
- printk(KERN_ERR
- "Unable to register Mxc Ipu as a char device\n");
- return mxc_ipu_major;
- }
- mxc_ipu_class = class_create(THIS_MODULE, "mxc_ipu");
- if (IS_ERR(mxc_ipu_class)) {
- printk(KERN_ERR "Unable to create class for Mxc Ipu\n");
- ret = PTR_ERR(mxc_ipu_class);
- goto err1;
+ if (!major) {
+ major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);
+ if (major < 0) {
+ printk(KERN_ERR "Unable to register mxc_ipu as a char device\n");
+ ret = major;
+ goto register_cdev_fail;
+ }
+
+ ipu_class = class_create(THIS_MODULE, "mxc_ipu");
+ if (IS_ERR(ipu_class)) {
+ ret = PTR_ERR(ipu_class);
+ goto ipu_class_fail;
+ }
+
+ ipu_dev = device_create(ipu_class, NULL, MKDEV(major, 0),
+ NULL, "mxc_ipu");
+ if (IS_ERR(ipu_dev)) {
+ ret = PTR_ERR(ipu_dev);
+ goto dev_create_fail;
+ }
+ ipu_dev->dma_mask = kmalloc(sizeof(*ipu_dev->dma_mask), GFP_KERNEL);
+ *ipu_dev->dma_mask = DMA_BIT_MASK(32);
+ ipu_dev->coherent_dma_mask = DMA_BIT_MASK(32);
}
- temp = device_create(mxc_ipu_class, NULL, MKDEV(mxc_ipu_major, 0),
- NULL, "mxc_ipu");
+ INIT_LIST_HEAD(&ipu->task_list[0]);
+ INIT_LIST_HEAD(&ipu->task_list[1]);
+ init_waitqueue_head(&ipu->waitq[0]);
+ init_waitqueue_head(&ipu->waitq[1]);
+ mutex_init(&ipu->task_lock[0]);
+ mutex_init(&ipu->task_lock[1]);
+ ipu->thread[0] = kthread_run(task_vf_thread, ipu,
+ "ipu%d_process-vf", id);
+ if (IS_ERR(ipu->thread[0])) {
+ ret = PTR_ERR(ipu->thread[0]);
+ goto kthread0_fail;
+ }
- if (IS_ERR(temp)) {
- printk(KERN_ERR "Unable to create class device for Mxc Ipu\n");
- ret = PTR_ERR(temp);
- goto err2;
+ ipu->thread[1] = kthread_run(task_pp_thread, ipu,
+ "ipu%d_process-pp", id);
+ if (IS_ERR(ipu->thread[1])) {
+ ret = PTR_ERR(ipu->thread[1]);
+ goto kthread1_fail;
}
- spin_lock_init(&event_lock);
return ret;
-err2:
- class_destroy(mxc_ipu_class);
-err1:
- unregister_chrdev(mxc_ipu_major, "mxc_ipu");
+kthread1_fail:
+ kthread_stop(ipu->thread[0]);
+kthread0_fail:
+ if (id == 0)
+ device_destroy(ipu_class, MKDEV(major, 0));
+dev_create_fail:
+ if (id == 0) {
+ class_destroy(ipu_class);
+ unregister_chrdev(major, "mxc_ipu");
+ }
+ipu_class_fail:
+ if (id == 0)
+ unregister_chrdev(major, "mxc_ipu");
+register_cdev_fail:
return ret;
+}
+void unregister_ipu_device(struct ipu_soc *ipu, int id)
+{
+ kthread_stop(ipu->thread[0]);
+ kthread_stop(ipu->thread[1]);
+ if (major) {
+ device_destroy(ipu_class, MKDEV(major, 0));
+ class_destroy(ipu_class);
+ unregister_chrdev(major, "mxc_ipu");
+ major = 0;
+ }
}