3756 lines
100 KiB
C
3756 lines
100 KiB
C
/*
|
|
* Copyright 2005-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
|
* Copyright 2019 NXP
|
|
*/
|
|
|
|
/*
|
|
* The code contained herein is licensed under the GNU General Public
|
|
* License. You may obtain a copy of the GNU General Public License
|
|
* Version 2 or later at the following locations:
|
|
*
|
|
* http://www.opensource.org/licenses/gpl-license.html
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
|
|
/*!
|
|
* @file ipu_device.c
|
|
*
|
|
* @brief This file contains the IPUv3 driver device interface and fops functions.
|
|
*
|
|
* @ingroup IPU
|
|
*/
|
|
#include <linux/clk.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/err.h>
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
#include <linux/ipu-v3.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/types.h>
|
|
#include <linux/sched/rt.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/time.h>
|
|
#include <linux/types.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/wait.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/outercache.h>
|
|
|
|
#include "ipu_param_mem.h"
|
|
#include "ipu_regs.h"
|
|
#include "vdoa.h"
|
|
|
|
#define CHECK_RETCODE(cont, str, err, label, ret) \
|
|
do { \
|
|
if (cont) { \
|
|
dev_err(t->dev, "ERR:[0x%p]-no:0x%x "#str" ret:%d," \
|
|
"line:%d\n", t, t->task_no, ret, __LINE__);\
|
|
if (ret != -EACCES) { \
|
|
t->state = err; \
|
|
goto label; \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CHECK_RETCODE_CONT(cont, str, err, ret) \
|
|
do { \
|
|
if (cont) { \
|
|
dev_err(t->dev, "ERR:[0x%p]-no:0x%x"#str" ret:%d," \
|
|
"line:%d\n", t, t->task_no, ret, __LINE__);\
|
|
if (ret != -EACCES) { \
|
|
if (t->state == STATE_OK) \
|
|
t->state = err; \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#undef DBG_IPU_PERF
|
|
#ifdef DBG_IPU_PERF
|
|
#define CHECK_PERF(ts) \
|
|
do { \
|
|
getnstimeofday(ts); \
|
|
} while (0)
|
|
|
|
#define DECLARE_PERF_VAR \
|
|
struct timespec ts_queue; \
|
|
struct timespec ts_dotask; \
|
|
struct timespec ts_waitirq; \
|
|
struct timespec ts_sche; \
|
|
struct timespec ts_rel; \
|
|
struct timespec ts_frame
|
|
|
|
#define PRINT_TASK_STATISTICS \
|
|
do { \
|
|
ts_queue = timespec_sub(tsk->ts_dotask, tsk->ts_queue); \
|
|
ts_dotask = timespec_sub(tsk->ts_waitirq, tsk->ts_dotask); \
|
|
ts_waitirq = timespec_sub(tsk->ts_inirq, tsk->ts_waitirq); \
|
|
ts_sche = timespec_sub(tsk->ts_wakeup, tsk->ts_inirq); \
|
|
ts_rel = timespec_sub(tsk->ts_rel, tsk->ts_wakeup); \
|
|
ts_frame = timespec_sub(tsk->ts_rel, tsk->ts_queue); \
|
|
dev_dbg(tsk->dev, "[0x%p] no-0x%x, ts_q:%ldus, ts_do:%ldus," \
|
|
"ts_waitirq:%ldus,ts_sche:%ldus, ts_rel:%ldus," \
|
|
"ts_frame: %ldus\n", tsk, tsk->task_no, \
|
|
ts_queue.tv_nsec / NSEC_PER_USEC + ts_queue.tv_sec * USEC_PER_SEC,\
|
|
ts_dotask.tv_nsec / NSEC_PER_USEC + ts_dotask.tv_sec * USEC_PER_SEC,\
|
|
ts_waitirq.tv_nsec / NSEC_PER_USEC + ts_waitirq.tv_sec * USEC_PER_SEC,\
|
|
ts_sche.tv_nsec / NSEC_PER_USEC + ts_sche.tv_sec * USEC_PER_SEC,\
|
|
ts_rel.tv_nsec / NSEC_PER_USEC + ts_rel.tv_sec * USEC_PER_SEC,\
|
|
ts_frame.tv_nsec / NSEC_PER_USEC + ts_frame.tv_sec * USEC_PER_SEC); \
|
|
if ((ts_frame.tv_nsec/NSEC_PER_USEC + ts_frame.tv_sec*USEC_PER_SEC) > \
|
|
80000) \
|
|
dev_dbg(tsk->dev, "ts_frame larger than 80ms [0x%p] no-0x%x.\n"\
|
|
, tsk, tsk->task_no); \
|
|
} while (0)
|
|
#else
|
|
#define CHECK_PERF(ts)
|
|
#define DECLARE_PERF_VAR
|
|
#define PRINT_TASK_STATISTICS
|
|
#endif
|
|
|
|
#define IPU_PP_CH_VF (IPU_TASK_ID_VF - 1)
|
|
#define IPU_PP_CH_PP (IPU_TASK_ID_PP - 1)
|
|
#define MAX_PP_CH (IPU_TASK_ID_MAX - 1)
|
|
#define VDOA_DEF_TIMEOUT_MS (HZ/2)
|
|
|
|
/* Strucutures and variables for exporting MXC IPU as device*/
|
|
typedef enum {
|
|
STATE_OK = 0,
|
|
STATE_QUEUE,
|
|
STATE_IN_PROGRESS,
|
|
STATE_ERR,
|
|
STATE_TIMEOUT,
|
|
STATE_RES_TIMEOUT,
|
|
STATE_NO_IPU,
|
|
STATE_NO_IRQ,
|
|
STATE_IPU_BUSY,
|
|
STATE_IRQ_FAIL,
|
|
STATE_IRQ_TIMEOUT,
|
|
STATE_ENABLE_CHAN_FAIL,
|
|
STATE_DISABLE_CHAN_FAIL,
|
|
STATE_SEL_BUF_FAIL,
|
|
STATE_INIT_CHAN_FAIL,
|
|
STATE_LINK_CHAN_FAIL,
|
|
STATE_UNLINK_CHAN_FAIL,
|
|
STATE_INIT_CHAN_BUF_FAIL,
|
|
STATE_INIT_CHAN_BAND_FAIL,
|
|
STATE_SYS_NO_MEM,
|
|
STATE_VDOA_IRQ_TIMEOUT,
|
|
STATE_VDOA_IRQ_FAIL,
|
|
STATE_VDOA_TASK_FAIL,
|
|
} ipu_state_t;
|
|
|
|
enum {
|
|
INPUT_CHAN_VDI_P = 1,
|
|
INPUT_CHAN,
|
|
INPUT_CHAN_VDI_N,
|
|
};
|
|
|
|
struct ipu_state_msg {
|
|
int state;
|
|
char *msg;
|
|
} state_msg[] = {
|
|
{STATE_OK, "ok"},
|
|
{STATE_QUEUE, "split queue"},
|
|
{STATE_IN_PROGRESS, "split in progress"},
|
|
{STATE_ERR, "error"},
|
|
{STATE_TIMEOUT, "split task timeout"},
|
|
{STATE_RES_TIMEOUT, "wait resource timeout"},
|
|
{STATE_NO_IPU, "no ipu found"},
|
|
{STATE_NO_IRQ, "no irq found for task"},
|
|
{STATE_IPU_BUSY, "ipu busy"},
|
|
{STATE_IRQ_FAIL, "request irq failed"},
|
|
{STATE_IRQ_TIMEOUT, "wait for irq timeout"},
|
|
{STATE_ENABLE_CHAN_FAIL, "ipu enable channel fail"},
|
|
{STATE_DISABLE_CHAN_FAIL, "ipu disable channel fail"},
|
|
{STATE_SEL_BUF_FAIL, "ipu select buf fail"},
|
|
{STATE_INIT_CHAN_FAIL, "ipu init channel fail"},
|
|
{STATE_LINK_CHAN_FAIL, "ipu link channel fail"},
|
|
{STATE_UNLINK_CHAN_FAIL, "ipu unlink channel fail"},
|
|
{STATE_INIT_CHAN_BUF_FAIL, "ipu init channel buffer fail"},
|
|
{STATE_INIT_CHAN_BAND_FAIL, "ipu init channel band mode fail"},
|
|
{STATE_SYS_NO_MEM, "sys no mem: -ENOMEM"},
|
|
{STATE_VDOA_IRQ_TIMEOUT, "wait for vdoa irq timeout"},
|
|
{STATE_VDOA_IRQ_FAIL, "vdoa irq fail"},
|
|
{STATE_VDOA_TASK_FAIL, "vdoa task 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;
|
|
u32 rl_split_line;
|
|
u32 ud_split_line;
|
|
};
|
|
|
|
struct task_set {
|
|
#define NULL_MODE 0x0
|
|
#define IC_MODE 0x1
|
|
#define ROT_MODE 0x2
|
|
#define VDI_MODE 0x4
|
|
#define IPU_PREPROCESS_MODE_MASK (IC_MODE | ROT_MODE | VDI_MODE)
|
|
/* VDOA_MODE means this task use vdoa, and VDOA has two modes:
|
|
* BAND MODE and non-BAND MODE. Non-band mode will do transfer data
|
|
* to memory. BAND mode needs hareware sync with IPU, it is used default
|
|
* if connected to VDIC.
|
|
*/
|
|
#define VDOA_MODE 0x8
|
|
#define VDOA_BAND_MODE 0x10
|
|
u8 mode;
|
|
#define IC_VF 0x1
|
|
#define IC_PP 0x2
|
|
#define ROT_VF 0x4
|
|
#define ROT_PP 0x8
|
|
#define VDI_VF 0x10
|
|
#define VDOA_ONLY 0x20
|
|
u8 task;
|
|
#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
|
|
#define SPLIT_MASK 0xF
|
|
u8 split_mode;
|
|
u8 band_lines;
|
|
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;
|
|
|
|
struct stripe_setting sp_setting;
|
|
};
|
|
|
|
struct ipu_split_task {
|
|
struct ipu_task task;
|
|
struct ipu_task_entry *parent_task;
|
|
struct ipu_task_entry *child_task;
|
|
u32 task_no;
|
|
};
|
|
|
|
struct ipu_task_entry {
|
|
struct ipu_input input;
|
|
struct ipu_output output;
|
|
|
|
bool overlay_en;
|
|
struct ipu_overlay overlay;
|
|
#define DEF_TIMEOUT_MS 1000
|
|
#define DEF_DELAY_MS 20
|
|
int timeout;
|
|
int irq;
|
|
|
|
u8 task_id;
|
|
u8 ipu_id;
|
|
u8 task_in_list;
|
|
u8 split_done;
|
|
struct mutex split_lock;
|
|
struct mutex vdic_lock;
|
|
wait_queue_head_t split_waitq;
|
|
|
|
struct list_head node;
|
|
struct list_head split_list;
|
|
struct ipu_soc *ipu;
|
|
struct device *dev;
|
|
struct task_set set;
|
|
wait_queue_head_t task_waitq;
|
|
struct completion irq_comp;
|
|
struct kref refcount;
|
|
ipu_state_t state;
|
|
u32 task_no;
|
|
atomic_t done;
|
|
atomic_t res_free;
|
|
atomic_t res_get;
|
|
|
|
struct ipu_task_entry *parent;
|
|
char *vditmpbuf[2];
|
|
u32 old_save_lines;
|
|
u32 old_size;
|
|
bool buf1filled;
|
|
bool buf0filled;
|
|
|
|
vdoa_handle_t vdoa_handle;
|
|
struct vdoa_output_mem {
|
|
void *vaddr;
|
|
dma_addr_t paddr;
|
|
int size;
|
|
} vdoa_dma;
|
|
|
|
#ifdef DBG_IPU_PERF
|
|
struct timespec ts_queue;
|
|
struct timespec ts_dotask;
|
|
struct timespec ts_waitirq;
|
|
struct timespec ts_inirq;
|
|
struct timespec ts_wakeup;
|
|
struct timespec ts_rel;
|
|
#endif
|
|
};
|
|
|
|
struct ipu_channel_tabel {
|
|
struct mutex lock;
|
|
u8 used[MXC_IPU_MAX_NUM][MAX_PP_CH];
|
|
u8 vdoa_used;
|
|
};
|
|
|
|
struct ipu_thread_data {
|
|
struct ipu_soc *ipu;
|
|
u32 id;
|
|
u32 is_vdoa;
|
|
};
|
|
|
|
struct ipu_alloc_list {
|
|
struct list_head list;
|
|
dma_addr_t phy_addr;
|
|
void *cpu_addr;
|
|
u32 size;
|
|
void *file_index;
|
|
};
|
|
|
|
static LIST_HEAD(ipu_alloc_list);
|
|
static DEFINE_MUTEX(ipu_alloc_lock);
|
|
static struct ipu_channel_tabel ipu_ch_tbl;
|
|
static LIST_HEAD(ipu_task_list);
|
|
static DEFINE_SPINLOCK(ipu_task_list_lock);
|
|
static DECLARE_WAIT_QUEUE_HEAD(thread_waitq);
|
|
static DECLARE_WAIT_QUEUE_HEAD(res_waitq);
|
|
static atomic_t req_cnt;
|
|
static atomic_t file_index = ATOMIC_INIT(1);
|
|
static int major;
|
|
static int max_ipu_no;
|
|
static int thread_id;
|
|
static atomic_t frame_no;
|
|
static struct class *ipu_class;
|
|
static struct device *ipu_dev;
|
|
static int debug;
|
|
module_param(debug, int, 0600);
|
|
#ifdef DBG_IPU_PERF
|
|
static struct timespec ts_frame_max;
|
|
static u32 ts_frame_avg;
|
|
static atomic_t frame_cnt;
|
|
#endif
|
|
|
|
static bool deinterlace_3_field(struct ipu_task_entry *t)
|
|
{
|
|
return ((t->set.mode & VDI_MODE) &&
|
|
(t->input.deinterlace.motion != HIGH_MOTION));
|
|
}
|
|
|
|
static u32 tiled_filed_size(struct ipu_task_entry *t)
|
|
{
|
|
u32 field_size;
|
|
|
|
/* note: page_align is required by VPU hw ouput buffer */
|
|
field_size = TILED_NV12_FRAME_SIZE(t->input.width, t->input.height/2);
|
|
return field_size;
|
|
}
|
|
|
|
static bool only_ic(u8 mode)
|
|
{
|
|
mode = mode & IPU_PREPROCESS_MODE_MASK;
|
|
return ((mode == IC_MODE) || (mode == VDI_MODE));
|
|
}
|
|
|
|
static bool only_rot(u8 mode)
|
|
{
|
|
mode = mode & IPU_PREPROCESS_MODE_MASK;
|
|
return (mode == ROT_MODE);
|
|
}
|
|
|
|
static bool ic_and_rot(u8 mode)
|
|
{
|
|
mode = mode & IPU_PREPROCESS_MODE_MASK;
|
|
return ((mode == (IC_MODE | ROT_MODE)) ||
|
|
(mode == (VDI_MODE | ROT_MODE)));
|
|
}
|
|
|
|
static bool need_split(struct ipu_task_entry *t)
|
|
{
|
|
return ((t->set.split_mode != NO_SPLIT) || (t->task_no & SPLIT_MASK));
|
|
}
|
|
|
|
unsigned int fmt_to_bpp(unsigned int 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:
|
|
case IPU_PIX_FMT_YUV444P:
|
|
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;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fmt_to_bpp);
|
|
|
|
cs_t colorspaceofpixel(int fmt)
|
|
{
|
|
switch (fmt) {
|
|
case IPU_PIX_FMT_RGB565:
|
|
case IPU_PIX_FMT_RGB666:
|
|
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_YUV444P:
|
|
case IPU_PIX_FMT_NV12:
|
|
case IPU_PIX_FMT_TILED_NV12:
|
|
case IPU_PIX_FMT_TILED_NV12F:
|
|
return YUV_CS;
|
|
break;
|
|
default:
|
|
return NULL_CS;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(colorspaceofpixel);
|
|
|
|
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;
|
|
}
|
|
EXPORT_SYMBOL_GPL(need_csc);
|
|
|
|
static int soc_max_in_width(u32 is_vdoa)
|
|
{
|
|
return is_vdoa ? 8192 : 4096;
|
|
}
|
|
|
|
static int soc_max_vdi_in_width(struct ipu_soc *ipu)
|
|
{
|
|
int i;
|
|
|
|
if (!ipu) {
|
|
for (i = 0; i < max_ipu_no; i++) {
|
|
ipu = ipu_get_soc(i);
|
|
if (!IS_ERR_OR_NULL(ipu))
|
|
break;
|
|
}
|
|
|
|
if (i == max_ipu_no)
|
|
return 720;
|
|
}
|
|
return IPU_MAX_VDI_IN_WIDTH(ipu->devtype);
|
|
}
|
|
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 void dump_task_info(struct ipu_task_entry *t)
|
|
{
|
|
if (!debug)
|
|
return;
|
|
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);
|
|
dev_dbg(t->dev, "[0x%p]\tTask_no = 0x%x\n\n\n", (void *)t, t->task_no);
|
|
}
|
|
|
|
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;
|
|
case IPU_CHECK_ERR_SPLIT_WITH_ROT:
|
|
dev_err(dev, "not support split mode with rotation\n");
|
|
break;
|
|
case IPU_CHECK_ERR_W_DOWNSIZE_OVER:
|
|
dev_err(dev, "horizontal downsizing ratio overflow\n");
|
|
break;
|
|
case IPU_CHECK_ERR_H_DOWNSIZE_OVER:
|
|
dev_err(dev, "vertical downsizing ratio 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, int fmt)
|
|
{
|
|
if ((width == 0) || (height == 0)) {
|
|
pr_err("Invalid param: width=%d, height=%d\n", width, height);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((IPU_PIX_FMT_TILED_NV12 == fmt) ||
|
|
(IPU_PIX_FMT_TILED_NV12F == fmt)) {
|
|
if (crop->w || crop->h) {
|
|
if (((crop->w + crop->pos.x) > width)
|
|
|| ((crop->h + crop->pos.y) > height)
|
|
|| (0 != (crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN))
|
|
|| (0 != (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN))
|
|
|| (0 != (crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN))
|
|
|| (0 != (crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN))
|
|
) {
|
|
pr_err("set_crop error MB align.\n");
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
crop->pos.x = 0;
|
|
crop->pos.y = 0;
|
|
crop->w = width;
|
|
crop->h = height;
|
|
if ((0 != (crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN))
|
|
|| (0 != (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN))) {
|
|
pr_err("set_crop error w/h MB align.\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
} else {
|
|
if (crop->w || crop->h) {
|
|
if (((crop->w + crop->pos.x) > (width + 16))
|
|
|| ((crop->h + crop->pos.y) > height + 16)) {
|
|
pr_err("set_crop error exceeds width/height.\n");
|
|
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;
|
|
}
|
|
|
|
if ((crop->w == 0) || (crop->h == 0)) {
|
|
pr_err("Invalid crop param: crop.w=%d, crop.h=%d\n",
|
|
crop->w, crop->h);
|
|
return -EINVAL;
|
|
}
|
|
|
|
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;
|
|
/* In case height is odd, round up to even */
|
|
*voff = *uoff + (width/2) * ((height+1)/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;
|
|
/* In case height is odd, round up to even */
|
|
*uoff = *voff + (width/2) * ((height+1)/2);
|
|
break;
|
|
case IPU_PIX_FMT_YVU422P:
|
|
*off = pos_y * width + pos_x;
|
|
*voff = (width * (height - pos_y) - pos_x)
|
|
+ (width/2) * pos_y + pos_x/2;
|
|
*uoff = *voff + (width/2) * height;
|
|
break;
|
|
case IPU_PIX_FMT_YUV422P:
|
|
*off = pos_y * width + pos_x;
|
|
*uoff = (width * (height - pos_y) - pos_x)
|
|
+ (width/2) * pos_y + pos_x/2;
|
|
*voff = *uoff + (width/2) * height;
|
|
break;
|
|
case IPU_PIX_FMT_YUV444P:
|
|
*off = pos_y * width + pos_x;
|
|
*uoff = width * height;
|
|
*voff = 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;
|
|
case IPU_PIX_FMT_TILED_NV12:
|
|
/*
|
|
* tiled format, progressive:
|
|
* assuming that line is aligned with MB height (aligned to 16)
|
|
* offset = line * stride + (pixel / MB_width) * pixels_in_MB
|
|
* = line * stride + (pixel / 16) * 256
|
|
* = line * stride + pixel * 16
|
|
*/
|
|
*off = pos_y * width + (pos_x << 4);
|
|
*uoff = ALIGN(width * height, SZ_4K) + (*off >> 1) - *off;
|
|
break;
|
|
case IPU_PIX_FMT_TILED_NV12F:
|
|
/*
|
|
* tiled format, interlaced:
|
|
* same as above, only number of pixels in MB is 128,
|
|
* instead of 256
|
|
*/
|
|
*off = (pos_y >> 1) * width + (pos_x << 3);
|
|
*uoff = ALIGN(width * height/2, SZ_4K) + (*off >> 1) - *off;
|
|
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, bool vdi_split)
|
|
{
|
|
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;
|
|
u32 max_width;
|
|
int ret;
|
|
|
|
if (t->output.rotate >= IPU_ROTATE_90_RIGHT)
|
|
return IPU_CHECK_ERR_SPLIT_WITH_ROT;
|
|
|
|
iw = t->input.crop.w;
|
|
ih = t->input.crop.h;
|
|
|
|
ow = t->output.crop.w;
|
|
oh = t->output.crop.h;
|
|
|
|
memset(&left_stripe, 0, sizeof(left_stripe));
|
|
memset(&right_stripe, 0, sizeof(right_stripe));
|
|
memset(&up_stripe, 0, sizeof(up_stripe));
|
|
memset(&down_stripe, 0, sizeof(down_stripe));
|
|
|
|
if (t->set.split_mode & RL_SPLIT) {
|
|
/*
|
|
* We do want equal strips: initialize stripes in case
|
|
* calc_stripes returns before actually doing the calculation
|
|
*/
|
|
left_stripe.input_width = iw / 2;
|
|
left_stripe.output_width = ow / 2;
|
|
right_stripe.input_column = iw / 2;
|
|
right_stripe.output_column = ow / 2;
|
|
|
|
if (vdi_split)
|
|
max_width = soc_max_vdi_in_width(t->ipu);
|
|
else
|
|
max_width = soc_max_out_width();
|
|
ret = ipu_calc_stripes_sizes(iw,
|
|
ow,
|
|
max_width,
|
|
(((unsigned long long)1) << 32), /* 32bit for fractional*/
|
|
1, /* equal stripes */
|
|
t->input.format,
|
|
t->output.format,
|
|
&left_stripe,
|
|
&right_stripe);
|
|
if (ret < 0)
|
|
return IPU_CHECK_ERR_W_DOWNSIZE_OVER;
|
|
else if (ret)
|
|
dev_dbg(t->dev, "Warn: no:0x%x,calc_stripes ret:%d\n",
|
|
t->task_no, ret);
|
|
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+16))
|
|
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 (rounddown(t->set.sp_setting.ow, 8) * 8 <=
|
|
rounddown(t->set.sp_setting.iw, 8))
|
|
return IPU_CHECK_ERR_W_DOWNSIZE_OVER;
|
|
|
|
if (t->set.split_mode & UD_SPLIT) {
|
|
/*
|
|
* We do want equal strips: initialize stripes in case
|
|
* calc_stripes returns before actually doing the calculation
|
|
*/
|
|
up_stripe.input_width = ih / 2;
|
|
up_stripe.output_width = oh / 2;
|
|
down_stripe.input_column = ih / 2;
|
|
down_stripe.output_column = oh / 2;
|
|
ret = ipu_calc_stripes_sizes(ih,
|
|
oh,
|
|
soc_max_out_height(),
|
|
(((unsigned long long)1) << 32), /* 32bit for fractional*/
|
|
0x1 | 0x2, /* equal stripes and vertical */
|
|
t->input.format,
|
|
t->output.format,
|
|
&up_stripe,
|
|
&down_stripe);
|
|
if (ret < 0)
|
|
return IPU_CHECK_ERR_H_DOWNSIZE_OVER;
|
|
else if (ret)
|
|
dev_err(t->dev, "Warn: no:0x%x,calc_stripes ret:%d\n",
|
|
t->task_no, ret);
|
|
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;
|
|
}
|
|
|
|
/* downscale case: enforce limits */
|
|
if (((t->set.sp_setting.ih + t->set.sp_setting.i_bottom_pos) > (ih))
|
|
&& (t->set.sp_setting.ih >= t->set.sp_setting.oh))
|
|
return IPU_CHECK_ERR_SPLIT_INPUTH_OVER;
|
|
/* upscale case: relax limits because ipu_calc_stripes_sizes() may
|
|
create input stripe that falls just outside of the input window */
|
|
else if ((t->set.sp_setting.ih + t->set.sp_setting.i_bottom_pos)
|
|
> (ih+16))
|
|
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;
|
|
if (rounddown(t->set.sp_setting.oh, 8) * 8 <=
|
|
rounddown(t->set.sp_setting.ih, 8))
|
|
return IPU_CHECK_ERR_H_DOWNSIZE_OVER;
|
|
|
|
return IPU_CHECK_OK;
|
|
}
|
|
|
|
static int check_task(struct ipu_task_entry *t)
|
|
{
|
|
int tmp;
|
|
int ret = IPU_CHECK_OK;
|
|
int timeout;
|
|
bool vdi_split = false;
|
|
int ocw, och;
|
|
|
|
if ((IPU_PIX_FMT_TILED_NV12 == t->overlay.format) ||
|
|
(IPU_PIX_FMT_TILED_NV12F == t->overlay.format) ||
|
|
(IPU_PIX_FMT_TILED_NV12 == t->output.format) ||
|
|
(IPU_PIX_FMT_TILED_NV12F == t->output.format) ||
|
|
((IPU_PIX_FMT_TILED_NV12F == t->input.format) &&
|
|
!t->input.deinterlace.enable)) {
|
|
ret = IPU_CHECK_ERR_NOT_SUPPORT;
|
|
goto done;
|
|
}
|
|
|
|
/* check input */
|
|
ret = set_crop(&t->input.crop, t->input.width, t->input.height,
|
|
t->input.format);
|
|
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,
|
|
t->output.format);
|
|
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);
|
|
|
|
if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
|
|
/*
|
|
* Cache output width and height and
|
|
* swap them so that we may check
|
|
* downsize overflow correctly.
|
|
*/
|
|
ocw = t->output.crop.h;
|
|
och = t->output.crop.w;
|
|
} else {
|
|
ocw = t->output.crop.w;
|
|
och = t->output.crop.h;
|
|
}
|
|
|
|
if (ocw * 8 <= t->input.crop.w) {
|
|
ret = IPU_CHECK_ERR_W_DOWNSIZE_OVER;
|
|
goto done;
|
|
}
|
|
|
|
if (och * 8 <= t->input.crop.h) {
|
|
ret = IPU_CHECK_ERR_H_DOWNSIZE_OVER;
|
|
goto done;
|
|
}
|
|
|
|
if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) ||
|
|
(IPU_PIX_FMT_TILED_NV12F == t->input.format)) {
|
|
if ((t->input.crop.w > soc_max_in_width(1)) ||
|
|
(t->input.crop.h > soc_max_in_height())) {
|
|
ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT;
|
|
goto done;
|
|
}
|
|
/* output fmt: NV12 and YUYV, now don't support resize */
|
|
if (((IPU_PIX_FMT_NV12 != t->output.format) &&
|
|
(IPU_PIX_FMT_YUYV != t->output.format)) ||
|
|
(t->input.crop.w != t->output.crop.w) ||
|
|
(t->input.crop.h != t->output.crop.h)) {
|
|
ret = IPU_CHECK_ERR_NOT_SUPPORT;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* 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, t->overlay.format);
|
|
if (ret < 0) {
|
|
ret = IPU_CHECK_ERR_OVERLAY_CROP;
|
|
goto done;
|
|
} else {
|
|
ocw = t->output.crop.w;
|
|
och = t->output.crop.h;
|
|
|
|
if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
|
|
ocw = t->output.crop.h;
|
|
och = t->output.crop.w;
|
|
}
|
|
if ((t->overlay.crop.w != ocw) ||
|
|
(t->overlay.crop.h != och)) {
|
|
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 (!((IPU_PIX_FMT_TILED_NV12 == t->input.format) ||
|
|
(IPU_PIX_FMT_TILED_NV12F == t->input.format))) {
|
|
if ((t->input.crop.w > soc_max_in_width(0)) ||
|
|
(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 cropping?*/
|
|
if ((t->input.crop.w != t->input.width) ||
|
|
(t->input.crop.h != t->input.height) ||
|
|
(t->output.crop.w != t->output.width) ||
|
|
(t->output.crop.h != t->output.height))
|
|
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 ((IPU_PIX_FMT_TILED_NV12 == t->input.format) ||
|
|
(IPU_PIX_FMT_TILED_NV12F == t->input.format)) {
|
|
if (t->set.mode & ROT_MODE) {
|
|
ret = IPU_CHECK_ERR_NOT_SUPPORT;
|
|
goto done;
|
|
}
|
|
t->set.mode |= VDOA_MODE;
|
|
if (IPU_PIX_FMT_TILED_NV12F == t->input.format)
|
|
t->set.mode |= VDOA_BAND_MODE;
|
|
t->set.mode &= ~IC_MODE;
|
|
}
|
|
|
|
if ((t->set.mode & (IC_MODE | VDI_MODE)) &&
|
|
(IPU_PIX_FMT_TILED_NV12F != t->input.format)) {
|
|
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;
|
|
if (!t->set.split_mode && (t->set.mode & VDI_MODE) &&
|
|
(t->input.crop.w >
|
|
soc_max_vdi_in_width(t->ipu))) {
|
|
t->set.split_mode |= RL_SPLIT;
|
|
vdi_split = true;
|
|
}
|
|
if (t->set.split_mode) {
|
|
if ((t->set.split_mode == RL_SPLIT) ||
|
|
(t->set.split_mode == UD_SPLIT))
|
|
timeout = DEF_TIMEOUT_MS * 2 + DEF_DELAY_MS;
|
|
else
|
|
timeout = DEF_TIMEOUT_MS * 4 + DEF_DELAY_MS;
|
|
if (t->timeout < timeout)
|
|
t->timeout = timeout;
|
|
|
|
ret = update_split_setting(t, vdi_split);
|
|
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 (debug) {
|
|
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;
|
|
|
|
if (t->set.mode & VDI_MODE) {
|
|
t->task_id = IPU_TASK_ID_VF;
|
|
t->set.task = VDI_VF;
|
|
if (t->set.mode & ROT_MODE)
|
|
t->set.task |= ROT_VF;
|
|
}
|
|
|
|
if (VDOA_MODE == t->set.mode) {
|
|
if (t->set.task != 0) {
|
|
dev_err(t->dev, "ERR: vdoa only task:0x%x, [0x%p].\n",
|
|
t->set.task, t);
|
|
return -EINVAL;
|
|
}
|
|
t->set.task |= VDOA_ONLY;
|
|
}
|
|
|
|
if (VDOA_BAND_MODE & t->set.mode) {
|
|
/* to save band size: 1<<3 = 8 lines */
|
|
t->set.band_lines = 3;
|
|
}
|
|
|
|
dump_task_info(t);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t ic_vf_pp_is_busy(struct ipu_soc *ipu, bool is_vf)
|
|
{
|
|
uint32_t status;
|
|
uint32_t status_vf;
|
|
uint32_t status_rot;
|
|
|
|
if (is_vf) {
|
|
status = ipu_channel_status(ipu, MEM_VDI_PRP_VF_MEM);
|
|
status_vf = ipu_channel_status(ipu, MEM_PRP_VF_MEM);
|
|
status_rot = ipu_channel_status(ipu, MEM_ROT_VF_MEM);
|
|
return status || status_vf || status_rot;
|
|
} else {
|
|
status = ipu_channel_status(ipu, MEM_PP_MEM);
|
|
status_rot = ipu_channel_status(ipu, MEM_ROT_PP_MEM);
|
|
return status || status_rot;
|
|
}
|
|
}
|
|
|
|
static int _get_vdoa_ipu_res(struct ipu_task_entry *t)
|
|
{
|
|
int i;
|
|
struct ipu_soc *ipu;
|
|
u8 *used;
|
|
uint32_t found_ipu = 0;
|
|
uint32_t found_vdoa = 0;
|
|
struct ipu_channel_tabel *tbl = &ipu_ch_tbl;
|
|
|
|
mutex_lock(&tbl->lock);
|
|
if (t->set.mode & VDOA_MODE) {
|
|
if (NULL != t->vdoa_handle)
|
|
found_vdoa = 1;
|
|
else {
|
|
found_vdoa = tbl->vdoa_used ? 0 : 1;
|
|
if (found_vdoa) {
|
|
tbl->vdoa_used = 1;
|
|
vdoa_get_handle(&t->vdoa_handle);
|
|
} else
|
|
/* first get vdoa->ipu resource sequence */
|
|
goto out;
|
|
if (t->set.task & VDOA_ONLY)
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < max_ipu_no; i++) {
|
|
ipu = ipu_get_soc(i);
|
|
if (IS_ERR(ipu))
|
|
dev_err(t->dev, "no:0x%x,found_vdoa:%d, ipu:%d\n",
|
|
t->task_no, found_vdoa, i);
|
|
|
|
used = &tbl->used[i][IPU_PP_CH_VF];
|
|
if (t->set.mode & VDI_MODE) {
|
|
if (0 == *used) {
|
|
*used = 1;
|
|
found_ipu = 1;
|
|
break;
|
|
}
|
|
} else if ((t->set.mode & IC_MODE) || only_rot(t->set.mode)) {
|
|
if (0 == *used) {
|
|
t->task_id = IPU_TASK_ID_VF;
|
|
if (t->set.mode & IC_MODE)
|
|
t->set.task |= IC_VF;
|
|
if (t->set.mode & ROT_MODE)
|
|
t->set.task |= ROT_VF;
|
|
*used = 1;
|
|
found_ipu = 1;
|
|
break;
|
|
}
|
|
} else
|
|
dev_err(t->dev, "no:0x%x,found_vdoa:%d, mode:0x%x\n",
|
|
t->task_no, found_vdoa, t->set.mode);
|
|
}
|
|
if (found_ipu)
|
|
goto next;
|
|
|
|
for (i = 0; i < max_ipu_no; i++) {
|
|
ipu = ipu_get_soc(i);
|
|
if (IS_ERR(ipu))
|
|
dev_err(t->dev, "no:0x%x,found_vdoa:%d, ipu:%d\n",
|
|
t->task_no, found_vdoa, i);
|
|
|
|
if ((t->set.mode & IC_MODE) || only_rot(t->set.mode)) {
|
|
used = &tbl->used[i][IPU_PP_CH_PP];
|
|
if (0 == *used) {
|
|
t->task_id = IPU_TASK_ID_PP;
|
|
if (t->set.mode & IC_MODE)
|
|
t->set.task |= IC_PP;
|
|
if (t->set.mode & ROT_MODE)
|
|
t->set.task |= ROT_PP;
|
|
*used = 1;
|
|
found_ipu = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
next:
|
|
if (found_ipu) {
|
|
t->ipu = ipu;
|
|
t->ipu_id = i;
|
|
t->dev = ipu->dev;
|
|
if (atomic_inc_return(&t->res_get) == 2)
|
|
dev_err(t->dev,
|
|
"ERR no:0x%x,found_vdoa:%d,get ipu twice\n",
|
|
t->task_no, found_vdoa);
|
|
}
|
|
out:
|
|
dev_dbg(t->dev,
|
|
"%s:no:0x%x,found_vdoa:%d, found_ipu:%d\n",
|
|
__func__, t->task_no, found_vdoa, found_ipu);
|
|
mutex_unlock(&tbl->lock);
|
|
if (t->set.task & VDOA_ONLY)
|
|
return found_vdoa;
|
|
else if (t->set.mode & VDOA_MODE)
|
|
return found_vdoa && found_ipu;
|
|
else
|
|
return found_ipu;
|
|
}
|
|
|
|
static void put_vdoa_ipu_res(struct ipu_task_entry *tsk, int vdoa_only)
|
|
{
|
|
int ret;
|
|
int rel_vdoa = 0, rel_ipu = 0;
|
|
struct ipu_channel_tabel *tbl = &ipu_ch_tbl;
|
|
|
|
mutex_lock(&tbl->lock);
|
|
if (tsk->set.mode & VDOA_MODE) {
|
|
if (!tbl->vdoa_used && tsk->vdoa_handle)
|
|
dev_err(tsk->dev,
|
|
"ERR no:0x%x,vdoa not used,mode:0x%x\n",
|
|
tsk->task_no, tsk->set.mode);
|
|
if (tbl->vdoa_used && tsk->vdoa_handle) {
|
|
tbl->vdoa_used = 0;
|
|
vdoa_put_handle(&tsk->vdoa_handle);
|
|
if (tsk->ipu)
|
|
tsk->ipu->vdoa_en = 0;
|
|
rel_vdoa = 1;
|
|
if (vdoa_only || (tsk->set.task & VDOA_ONLY))
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (tsk->ipu_id != 0 && tsk->ipu_id != 1) {
|
|
dev_err(tsk->dev,
|
|
"%s:invalid ipu id, no:0x%x, rel_vdoa:%d, rel_ipu:%d\n",
|
|
__func__, tsk->task_no, rel_vdoa, rel_ipu);
|
|
goto out;
|
|
}
|
|
|
|
tbl->used[tsk->ipu_id][tsk->task_id - 1] = 0;
|
|
rel_ipu = 1;
|
|
ret = atomic_inc_return(&tsk->res_free);
|
|
if (ret == 2)
|
|
dev_err(tsk->dev,
|
|
"ERR no:0x%x,rel_vdoa:%d,put ipu twice\n",
|
|
tsk->task_no, rel_vdoa);
|
|
out:
|
|
dev_dbg(tsk->dev,
|
|
"%s:no:0x%x,rel_vdoa:%d, rel_ipu:%d\n",
|
|
__func__, tsk->task_no, rel_vdoa, rel_ipu);
|
|
mutex_unlock(&tbl->lock);
|
|
}
|
|
|
|
static int get_vdoa_ipu_res(struct ipu_task_entry *t)
|
|
{
|
|
int ret;
|
|
uint32_t found = 0;
|
|
|
|
found = _get_vdoa_ipu_res(t);
|
|
if (!found) {
|
|
t->ipu_id = -1;
|
|
t->ipu = NULL;
|
|
/* blocking to get resource */
|
|
ret = atomic_inc_return(&req_cnt);
|
|
dev_dbg(t->dev,
|
|
"wait_res:no:0x%x,req_cnt:%d\n", t->task_no, ret);
|
|
ret = wait_event_timeout(res_waitq, _get_vdoa_ipu_res(t),
|
|
msecs_to_jiffies(t->timeout - DEF_DELAY_MS));
|
|
if (ret == 0) {
|
|
dev_err(t->dev, "ERR[0x%p,no-0x%x] wait_res timeout:%dms!\n",
|
|
t, t->task_no, t->timeout - DEF_DELAY_MS);
|
|
ret = -ETIMEDOUT;
|
|
t->state = STATE_RES_TIMEOUT;
|
|
goto out;
|
|
} else {
|
|
if (!(t->set.task & VDOA_ONLY) && (!t->ipu))
|
|
dev_err(t->dev,
|
|
"ERR[no-0x%x] can not get ipu!\n",
|
|
t->task_no);
|
|
ret = atomic_read(&req_cnt);
|
|
if (ret > 0)
|
|
ret = atomic_dec_return(&req_cnt);
|
|
else
|
|
dev_err(t->dev,
|
|
"ERR[no-0x%x] req_cnt:%d mismatch!\n",
|
|
t->task_no, ret);
|
|
dev_dbg(t->dev, "no-0x%x,[0x%p],req_cnt:%d, got_res!\n",
|
|
t->task_no, t, ret);
|
|
found = 1;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return found;
|
|
}
|
|
|
|
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);
|
|
kref_init(&tsk->refcount);
|
|
tsk->state = -EINVAL;
|
|
tsk->ipu_id = -1;
|
|
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;
|
|
if (task->timeout > DEF_TIMEOUT_MS)
|
|
tsk->timeout = task->timeout;
|
|
else
|
|
tsk->timeout = DEF_TIMEOUT_MS;
|
|
|
|
return tsk;
|
|
}
|
|
|
|
static void task_mem_free(struct kref *ref)
|
|
{
|
|
struct ipu_task_entry *tsk =
|
|
container_of(ref, struct ipu_task_entry, refcount);
|
|
kfree(tsk);
|
|
}
|
|
|
|
int create_split_child_task(struct ipu_split_task *sp_task)
|
|
{
|
|
int ret = 0;
|
|
struct ipu_task_entry *tsk;
|
|
|
|
tsk = create_task_entry(&sp_task->task);
|
|
if (IS_ERR(tsk))
|
|
return PTR_ERR(tsk);
|
|
|
|
sp_task->child_task = tsk;
|
|
tsk->task_no = sp_task->task_no;
|
|
|
|
ret = prepare_task(tsk);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
tsk->parent = sp_task->parent_task;
|
|
tsk->set.sp_setting = sp_task->parent_task->set.sp_setting;
|
|
|
|
list_add(&tsk->node, &tsk->parent->split_list);
|
|
dev_dbg(tsk->dev, "[0x%p] sp_tsk Q list,no-0x%x\n", tsk, tsk->task_no);
|
|
tsk->state = STATE_QUEUE;
|
|
CHECK_PERF(&tsk->ts_queue);
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
static inline int sp_task_check_done(struct ipu_split_task *sp_task,
|
|
struct ipu_task_entry *parent, int num, int *idx)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
struct ipu_task_entry *tsk;
|
|
struct mutex *lock = &parent->split_lock;
|
|
|
|
*idx = -EINVAL;
|
|
mutex_lock(lock);
|
|
for (i = 0; i < num; i++) {
|
|
tsk = sp_task[i].child_task;
|
|
if (tsk && tsk->split_done) {
|
|
*idx = i;
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(lock);
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
int ret;
|
|
|
|
sp_task->task_no |= stripe;
|
|
|
|
task->input = t->input;
|
|
task->output = t->output;
|
|
task->overlay_en = t->overlay_en;
|
|
if (task->overlay_en)
|
|
task->overlay = t->overlay;
|
|
task->task_id = t->task_id;
|
|
if ((t->set.split_mode == RL_SPLIT) ||
|
|
(t->set.split_mode == UD_SPLIT))
|
|
task->timeout = t->timeout / 2;
|
|
else
|
|
task->timeout = t->timeout / 4;
|
|
|
|
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;
|
|
t->set.sp_setting.rl_split_line = t->set.sp_setting.o_bottom_pos;
|
|
t->set.sp_setting.ud_split_line = t->set.sp_setting.o_right_pos;
|
|
|
|
} else {
|
|
task->output.crop.w = t->set.sp_setting.ow;
|
|
task->output.crop.h = t->set.sp_setting.oh;
|
|
t->set.sp_setting.rl_split_line = t->set.sp_setting.o_right_pos;
|
|
t->set.sp_setting.ud_split_line = t->set.sp_setting.o_bottom_pos;
|
|
}
|
|
|
|
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, "ERR:should not be here\n");
|
|
break;
|
|
}
|
|
|
|
ret = create_split_child_task(sp_task);
|
|
if (ret < 0)
|
|
dev_err(t->dev, "ERR:create_split_child_task() ret:%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int queue_split_task(struct ipu_task_entry *t,
|
|
struct ipu_split_task *sp_task, uint32_t size)
|
|
{
|
|
int err[4];
|
|
int ret = 0;
|
|
int i, j;
|
|
struct ipu_task_entry *tsk = NULL;
|
|
struct mutex *lock = &t->split_lock;
|
|
struct mutex *vdic_lock = &t->vdic_lock;
|
|
|
|
dev_dbg(t->dev, "Split task 0x%p, no-0x%x, size:%d\n",
|
|
t, t->task_no, size);
|
|
mutex_init(lock);
|
|
mutex_init(vdic_lock);
|
|
init_waitqueue_head(&t->split_waitq);
|
|
INIT_LIST_HEAD(&t->split_list);
|
|
for (j = 0; j < size; j++) {
|
|
memset(&sp_task[j], 0, sizeof(*sp_task));
|
|
sp_task[j].parent_task = t;
|
|
sp_task[j].task_no = t->task_no;
|
|
}
|
|
|
|
if (t->set.split_mode == RL_SPLIT) {
|
|
i = 0;
|
|
err[i] = create_split_task(RIGHT_STRIPE, &sp_task[i]);
|
|
if (err[i] < 0)
|
|
goto err_start;
|
|
i = 1;
|
|
err[i] = create_split_task(LEFT_STRIPE, &sp_task[i]);
|
|
} else if (t->set.split_mode == UD_SPLIT) {
|
|
i = 0;
|
|
err[i] = create_split_task(DOWN_STRIPE, &sp_task[i]);
|
|
if (err[i] < 0)
|
|
goto err_start;
|
|
i = 1;
|
|
err[i] = create_split_task(UP_STRIPE, &sp_task[i]);
|
|
} else {
|
|
i = 0;
|
|
err[i] = create_split_task(RIGHT_STRIPE | DOWN_STRIPE, &sp_task[i]);
|
|
if (err[i] < 0)
|
|
goto err_start;
|
|
i = 1;
|
|
err[i] = create_split_task(LEFT_STRIPE | DOWN_STRIPE, &sp_task[i]);
|
|
if (err[i] < 0)
|
|
goto err_start;
|
|
i = 2;
|
|
err[i] = create_split_task(RIGHT_STRIPE | UP_STRIPE, &sp_task[i]);
|
|
if (err[i] < 0)
|
|
goto err_start;
|
|
i = 3;
|
|
err[i] = create_split_task(LEFT_STRIPE | UP_STRIPE, &sp_task[i]);
|
|
}
|
|
|
|
err_start:
|
|
for (j = 0; j < (i + 1); j++) {
|
|
if (err[j] < 0) {
|
|
if (sp_task[j].child_task)
|
|
dev_err(t->dev,
|
|
"sp_task[%d],no-0x%x fail state:%d, queue err:%d.\n",
|
|
j, sp_task[j].child_task->task_no,
|
|
sp_task[j].child_task->state, err[j]);
|
|
goto err_exit;
|
|
}
|
|
dev_dbg(t->dev, "[0x%p] sp_task[%d], no-0x%x state:%s, queue ret:%d.\n",
|
|
sp_task[j].child_task, j, sp_task[j].child_task->task_no,
|
|
state_msg[sp_task[j].child_task->state].msg, err[j]);
|
|
}
|
|
|
|
return ret;
|
|
|
|
err_exit:
|
|
for (j = 0; j < (i + 1); j++) {
|
|
if (err[j] < 0 && !ret)
|
|
ret = err[j];
|
|
tsk = sp_task[j].child_task;
|
|
if (!tsk)
|
|
continue;
|
|
kfree(tsk);
|
|
}
|
|
t->state = STATE_ERR;
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int init_tiled_buf(struct ipu_soc *ipu, struct ipu_task_entry *t,
|
|
ipu_channel_t channel, uint32_t ch_type)
|
|
{
|
|
int ret = 0;
|
|
int i;
|
|
uint32_t ipu_fmt;
|
|
dma_addr_t inbuf_base = 0;
|
|
u32 field_size;
|
|
struct vdoa_params param;
|
|
struct vdoa_ipu_buf buf;
|
|
struct ipu_soc *ipu_idx;
|
|
u32 ipu_stride, obuf_size;
|
|
u32 height, width;
|
|
ipu_buffer_t type;
|
|
|
|
if ((IPU_PIX_FMT_YUYV != t->output.format) &&
|
|
(IPU_PIX_FMT_NV12 != t->output.format)) {
|
|
dev_err(t->dev, "ERR:[0x%d] output format\n", t->task_no);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(¶m, 0, sizeof(param));
|
|
/* init channel tiled bufs */
|
|
if (deinterlace_3_field(t) &&
|
|
(IPU_PIX_FMT_TILED_NV12F == t->input.format)) {
|
|
field_size = tiled_filed_size(t);
|
|
if (INPUT_CHAN_VDI_P == ch_type) {
|
|
inbuf_base = t->input.paddr + field_size;
|
|
param.vfield_buf.prev_veba = inbuf_base + t->set.i_off;
|
|
} else if (INPUT_CHAN == ch_type) {
|
|
inbuf_base = t->input.paddr_n;
|
|
param.vfield_buf.cur_veba = inbuf_base + t->set.i_off;
|
|
} else if (INPUT_CHAN_VDI_N == ch_type) {
|
|
inbuf_base = t->input.paddr_n + field_size;
|
|
param.vfield_buf.next_veba = inbuf_base + t->set.i_off;
|
|
} else
|
|
return -EINVAL;
|
|
height = t->input.crop.h >> 1; /* field format for vdoa */
|
|
width = t->input.crop.w;
|
|
param.vfield_buf.vubo = t->set.i_uoff;
|
|
param.interlaced = 1;
|
|
param.scan_order = 1;
|
|
type = IPU_INPUT_BUFFER;
|
|
} else if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) &&
|
|
(INPUT_CHAN == ch_type)) {
|
|
height = t->input.crop.h;
|
|
width = t->input.crop.w;
|
|
param.vframe_buf.veba = t->input.paddr + t->set.i_off;
|
|
param.vframe_buf.vubo = t->set.i_uoff;
|
|
type = IPU_INPUT_BUFFER;
|
|
} else
|
|
return -EINVAL;
|
|
|
|
param.band_mode = (t->set.mode & VDOA_BAND_MODE) ? 1 : 0;
|
|
if (param.band_mode && (t->set.band_lines != 3) &&
|
|
(t->set.band_lines != 4) && (t->set.band_lines != 5))
|
|
return -EINVAL;
|
|
else if (param.band_mode)
|
|
param.band_lines = (1 << t->set.band_lines);
|
|
for (i = 0; i < max_ipu_no; i++) {
|
|
ipu_idx = ipu_get_soc(i);
|
|
if (!IS_ERR(ipu_idx) && ipu_idx == ipu)
|
|
break;
|
|
}
|
|
if (t->set.task & VDOA_ONLY)
|
|
/* dummy, didn't need ipu res */
|
|
i = 0;
|
|
if (max_ipu_no == i) {
|
|
dev_err(t->dev, "ERR:[0x%p] get ipu num\n", t);
|
|
return -EINVAL;
|
|
}
|
|
|
|
param.ipu_num = i;
|
|
param.vpu_stride = t->input.width;
|
|
param.height = height;
|
|
param.width = width;
|
|
if (IPU_PIX_FMT_NV12 == t->output.format)
|
|
param.pfs = VDOA_PFS_NV12;
|
|
else
|
|
param.pfs = VDOA_PFS_YUYV;
|
|
ipu_fmt = (param.pfs == VDOA_PFS_YUYV) ? IPU_PIX_FMT_YUYV :
|
|
IPU_PIX_FMT_NV12;
|
|
ipu_stride = param.width * bytes_per_pixel(ipu_fmt);
|
|
obuf_size = PAGE_ALIGN(param.width * param.height *
|
|
fmt_to_bpp(ipu_fmt)/8);
|
|
dev_dbg(t->dev, "band_mode:%d, band_lines:%d\n",
|
|
param.band_mode, param.band_lines);
|
|
if (!param.band_mode) {
|
|
/* note: if only for tiled -> raster convert and
|
|
no other post-processing, we don't need alloc buf
|
|
and use output buffer directly.
|
|
*/
|
|
if (t->set.task & VDOA_ONLY)
|
|
param.ieba0 = t->output.paddr;
|
|
else {
|
|
dev_err(t->dev, "ERR:[0x%d] vdoa task\n", t->task_no);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
if (IPU_PIX_FMT_TILED_NV12F != t->input.format) {
|
|
dev_err(t->dev, "ERR [0x%d] vdoa task\n", t->task_no);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
ret = vdoa_setup(t->vdoa_handle, ¶m);
|
|
if (ret)
|
|
goto done;
|
|
vdoa_get_output_buf(t->vdoa_handle, &buf);
|
|
if (t->set.task & VDOA_ONLY)
|
|
goto done;
|
|
|
|
ret = ipu_init_channel_buffer(ipu,
|
|
channel,
|
|
type,
|
|
ipu_fmt,
|
|
width,
|
|
height,
|
|
ipu_stride,
|
|
IPU_ROTATE_NONE,
|
|
buf.ieba0,
|
|
buf.ieba1,
|
|
0,
|
|
buf.iubo,
|
|
0);
|
|
if (ret < 0) {
|
|
t->state = STATE_INIT_CHAN_BUF_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
if (param.band_mode) {
|
|
ret = ipu_set_channel_bandmode(ipu, channel,
|
|
type, t->set.band_lines);
|
|
if (ret < 0) {
|
|
t->state = STATE_INIT_CHAN_BAND_FAIL;
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
static int init_tiled_ch_bufs(struct ipu_soc *ipu, struct ipu_task_entry *t)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (IPU_PIX_FMT_TILED_NV12 == t->input.format) {
|
|
ret = init_tiled_buf(ipu, t, t->set.ic_chan, INPUT_CHAN);
|
|
CHECK_RETCODE(ret < 0, "init tiled_ch", t->state, done, ret);
|
|
} else if (IPU_PIX_FMT_TILED_NV12F == t->input.format) {
|
|
ret = init_tiled_buf(ipu, t, t->set.ic_chan, INPUT_CHAN);
|
|
CHECK_RETCODE(ret < 0, "init tiled_ch-c", t->state, done, ret);
|
|
ret = init_tiled_buf(ipu, t, t->set.vdi_ic_p_chan,
|
|
INPUT_CHAN_VDI_P);
|
|
CHECK_RETCODE(ret < 0, "init tiled_ch-p", t->state, done, ret);
|
|
ret = init_tiled_buf(ipu, t, t->set.vdi_ic_n_chan,
|
|
INPUT_CHAN_VDI_N);
|
|
CHECK_RETCODE(ret < 0, "init tiled_ch-n", t->state, done, ret);
|
|
} else {
|
|
ret = -EINVAL;
|
|
dev_err(t->dev, "ERR[no-0x%x] invalid fmt:0x%x!\n",
|
|
t->task_no, t->input.format);
|
|
}
|
|
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
u32 vdi_frame_idx = 0;
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
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 if (t->overlay.alpha.loc_alp_paddr)
|
|
params.mem_prp_vf_mem.alpha_chan_en = 1;
|
|
/* otherwise, alpha bending per pixel is used. */
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (t->input.deinterlace.enable) {
|
|
if (t->input.deinterlace.field_fmt & IPU_DEINTERLACE_FIELD_MASK)
|
|
params.mem_prp_vf_mem.field_fmt =
|
|
IPU_DEINTERLACE_FIELD_BOTTOM;
|
|
else
|
|
params.mem_prp_vf_mem.field_fmt =
|
|
IPU_DEINTERLACE_FIELD_TOP;
|
|
|
|
if (t->input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN)
|
|
vdi_frame_idx = t->input.deinterlace.field_fmt &
|
|
IPU_DEINTERLACE_RATE_FRAME1;
|
|
}
|
|
|
|
if (t->set.mode & VDOA_MODE)
|
|
ipu->vdoa_en = 1;
|
|
|
|
/* init channels */
|
|
if (!(t->set.task & VDOA_ONLY)) {
|
|
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;
|
|
}
|
|
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 ((IPU_PIX_FMT_TILED_NV12 == t->input.format) ||
|
|
(IPU_PIX_FMT_TILED_NV12F == t->input.format)) {
|
|
ret = init_tiled_ch_bufs(ipu, t);
|
|
if (ret < 0)
|
|
goto done;
|
|
} else {
|
|
if ((deinterlace_3_field(t)) &&
|
|
(IPU_PIX_FMT_TILED_NV12F != t->input.format)) {
|
|
if (params.mem_prp_vf_mem.field_fmt ==
|
|
IPU_DEINTERLACE_FIELD_TOP) {
|
|
if (vdi_frame_idx) {
|
|
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;
|
|
params.mem_prp_vf_mem.field_fmt =
|
|
IPU_DEINTERLACE_FIELD_BOTTOM;
|
|
} else {
|
|
inbuf_p = t->input.paddr + t->set.i_off;
|
|
inbuf = t->input.paddr + t->set.istride + t->set.i_off;
|
|
inbuf_n = t->input.paddr_n + t->set.i_off;
|
|
}
|
|
} else {
|
|
if (vdi_frame_idx) {
|
|
inbuf_p = t->input.paddr + t->set.i_off;
|
|
inbuf = t->input.paddr_n + t->set.istride + t->set.i_off;
|
|
inbuf_n = t->input.paddr_n + t->set.i_off;
|
|
params.mem_prp_vf_mem.field_fmt =
|
|
IPU_DEINTERLACE_FIELD_TOP;
|
|
} else {
|
|
inbuf_p = t->input.paddr + t->set.istride +
|
|
t->set.i_off;
|
|
inbuf = t->input.paddr + t->set.i_off;
|
|
inbuf_n = t->input.paddr_n + t->set.istride +
|
|
t->set.i_off;
|
|
}
|
|
}
|
|
} else {
|
|
if (t->input.deinterlace.enable) {
|
|
if (params.mem_prp_vf_mem.field_fmt ==
|
|
IPU_DEINTERLACE_FIELD_TOP) {
|
|
if (vdi_frame_idx) {
|
|
inbuf = t->input.paddr + t->set.istride + t->set.i_off;
|
|
params.mem_prp_vf_mem.field_fmt =
|
|
IPU_DEINTERLACE_FIELD_BOTTOM;
|
|
} else
|
|
inbuf = t->input.paddr + t->set.i_off;
|
|
} else {
|
|
if (vdi_frame_idx) {
|
|
inbuf = t->input.paddr + t->set.i_off;
|
|
params.mem_prp_vf_mem.field_fmt =
|
|
IPU_DEINTERLACE_FIELD_TOP;
|
|
} else
|
|
inbuf = t->input.paddr + t->set.istride + t->set.i_off;
|
|
}
|
|
} else
|
|
inbuf = t->input.paddr + t->set.i_off;
|
|
}
|
|
|
|
if (t->overlay_en)
|
|
ovbuf = t->overlay.paddr + t->set.ov_off;
|
|
}
|
|
if (t->overlay_en && (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL))
|
|
ov_alp_buf = t->overlay.alpha.loc_alp_paddr
|
|
+ t->set.ov_alpha_off;
|
|
|
|
if ((IPU_PIX_FMT_TILED_NV12 != t->input.format) &&
|
|
(IPU_PIX_FMT_TILED_NV12F != t->input.format)) {
|
|
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) &&
|
|
(IPU_PIX_FMT_TILED_NV12F != t->input.format)) {
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (!(t->set.task & VDOA_ONLY)) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
if ((t->set.mode & VDOA_BAND_MODE) && (t->set.task & VDI_VF)) {
|
|
ret = ipu_link_channels(ipu, MEM_VDOA_MEM, t->set.ic_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_link_ch vdoa_ic",
|
|
STATE_LINK_CHAN_FAIL, done, ret);
|
|
}
|
|
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
static void uninit_ic(struct ipu_soc *ipu, struct ipu_task_entry *t)
|
|
{
|
|
int ret;
|
|
|
|
if ((t->set.mode & VDOA_BAND_MODE) && (t->set.task & VDI_VF)) {
|
|
ret = ipu_unlink_channels(ipu, MEM_VDOA_MEM, t->set.ic_chan);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_unlink_ch vdoa_ic",
|
|
STATE_UNLINK_CHAN_FAIL, ret);
|
|
}
|
|
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,
|
|
in_stride,
|
|
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 MEM_ROT_PP_MEM:
|
|
irq = IPU_IRQ_PP_ROT_OUT_EOF;
|
|
break;
|
|
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;
|
|
case MEM_VDI_MEM:
|
|
irq = IPU_IRQ_VDIC_OUT_EOF;
|
|
break;
|
|
default:
|
|
irq = -EINVAL;
|
|
}
|
|
|
|
return irq;
|
|
}
|
|
|
|
static irqreturn_t task_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct ipu_task_entry *prev_tsk = dev_id;
|
|
|
|
CHECK_PERF(&prev_tsk->ts_inirq);
|
|
complete(&prev_tsk->irq_comp);
|
|
dev_dbg(prev_tsk->dev, "[0x%p] no-0x%x in-irq!",
|
|
prev_tsk, prev_tsk->task_no);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/* Fix deinterlace up&down split mode medium line */
|
|
static void vdi_split_process(struct ipu_soc *ipu, struct ipu_task_entry *t)
|
|
{
|
|
u32 vdi_size;
|
|
u32 vdi_save_lines;
|
|
u32 stripe_mode;
|
|
u32 task_no;
|
|
u32 i, offset_addr;
|
|
u32 line_size;
|
|
unsigned char *base_off;
|
|
struct ipu_task_entry *parent = t->parent;
|
|
struct mutex *lock = &parent->vdic_lock;
|
|
|
|
if (!parent) {
|
|
dev_err(t->dev, "ERR[0x%x]invalid parent\n", t->task_no);
|
|
return;
|
|
}
|
|
mutex_lock(lock);
|
|
stripe_mode = t->task_no & 0xf;
|
|
task_no = t->task_no >> 4;
|
|
|
|
/* Save both luma and chroma part for interleaved YUV(e.g. YUYV).
|
|
* Save luma part for non-interleaved and partial-interleaved
|
|
* YUV format (e.g NV12 and YV12). */
|
|
if (t->output.format == IPU_PIX_FMT_YUYV ||
|
|
t->output.format == IPU_PIX_FMT_UYVY)
|
|
line_size = t->output.crop.w * fmt_to_bpp(t->output.format)/8;
|
|
else
|
|
line_size = t->output.crop.w;
|
|
|
|
vdi_save_lines = (t->output.crop.h - t->set.sp_setting.ud_split_line)/2;
|
|
vdi_size = vdi_save_lines * line_size;
|
|
if (vdi_save_lines <= 0) {
|
|
dev_err(t->dev, "[0x%p] vdi_save_line error\n", (void *)t);
|
|
mutex_unlock(lock);
|
|
return;
|
|
}
|
|
|
|
/*check vditmpbuf buffer have alloced or buffer size is changed */
|
|
if ((vdi_save_lines != parent->old_save_lines) ||
|
|
(vdi_size != parent->old_size)) {
|
|
if (parent->vditmpbuf[0] != NULL)
|
|
kfree(parent->vditmpbuf[0]);
|
|
if (parent->vditmpbuf[1] != NULL)
|
|
kfree(parent->vditmpbuf[1]);
|
|
|
|
parent->vditmpbuf[0] = kmalloc(vdi_size, GFP_KERNEL);
|
|
if (parent->vditmpbuf[0] == NULL) {
|
|
dev_err(t->dev,
|
|
"[0x%p]Falied Alloc vditmpbuf[0]\n", (void *)t);
|
|
mutex_unlock(lock);
|
|
return;
|
|
}
|
|
memset(parent->vditmpbuf[0], 0, vdi_size);
|
|
|
|
parent->vditmpbuf[1] = kmalloc(vdi_size, GFP_KERNEL);
|
|
if (parent->vditmpbuf[1] == NULL) {
|
|
dev_err(t->dev,
|
|
"[0x%p]Falied Alloc vditmpbuf[1]\n", (void *)t);
|
|
mutex_unlock(lock);
|
|
return;
|
|
}
|
|
memset(parent->vditmpbuf[1], 0, vdi_size);
|
|
|
|
parent->old_save_lines = vdi_save_lines;
|
|
parent->old_size = vdi_size;
|
|
}
|
|
|
|
if (pfn_valid(t->output.paddr >> PAGE_SHIFT)) {
|
|
base_off = page_address(pfn_to_page(t->output.paddr >> PAGE_SHIFT));
|
|
base_off += t->output.paddr & ((1 << PAGE_SHIFT) - 1);
|
|
} else {
|
|
base_off = (char *)ioremap_nocache(t->output.paddr,
|
|
t->output.width * t->output.height *
|
|
fmt_to_bpp(t->output.format)/8);
|
|
}
|
|
if (base_off == NULL) {
|
|
dev_err(t->dev, "ERR[0x%p]Failed get virtual address\n", t);
|
|
mutex_unlock(lock);
|
|
return;
|
|
}
|
|
|
|
/* UP stripe or UP&LEFT stripe */
|
|
if ((stripe_mode == UP_STRIPE) ||
|
|
(stripe_mode == (UP_STRIPE | LEFT_STRIPE))) {
|
|
if (!parent->buf0filled) {
|
|
offset_addr = t->set.o_off +
|
|
t->set.sp_setting.ud_split_line*t->set.ostride;
|
|
dmac_flush_range(base_off + offset_addr,
|
|
base_off + offset_addr + vdi_size);
|
|
outer_flush_range(t->output.paddr + offset_addr,
|
|
t->output.paddr + offset_addr + vdi_size);
|
|
|
|
for (i = 0; i < vdi_save_lines; i++)
|
|
memcpy(parent->vditmpbuf[0] + i*line_size,
|
|
base_off + offset_addr +
|
|
i*t->set.ostride, line_size);
|
|
parent->buf0filled = true;
|
|
} else {
|
|
offset_addr = t->set.o_off + (t->output.crop.h -
|
|
vdi_save_lines) * t->set.ostride;
|
|
for (i = 0; i < vdi_save_lines; i++)
|
|
memcpy(base_off + offset_addr + i*t->set.ostride,
|
|
parent->vditmpbuf[0] + i*line_size, line_size);
|
|
|
|
dmac_flush_range(base_off + offset_addr,
|
|
base_off + offset_addr + i*t->set.ostride);
|
|
outer_flush_range(t->output.paddr + offset_addr,
|
|
t->output.paddr + offset_addr + i*t->set.ostride);
|
|
parent->buf0filled = false;
|
|
}
|
|
}
|
|
/*Down stripe or Down&Left stripe*/
|
|
else if ((stripe_mode == DOWN_STRIPE) ||
|
|
(stripe_mode == (DOWN_STRIPE | LEFT_STRIPE))) {
|
|
if (!parent->buf0filled) {
|
|
offset_addr = t->set.o_off + vdi_save_lines*t->set.ostride;
|
|
dmac_flush_range(base_off + offset_addr,
|
|
base_off + offset_addr + vdi_size);
|
|
outer_flush_range(t->output.paddr + offset_addr,
|
|
t->output.paddr + offset_addr + vdi_size);
|
|
|
|
for (i = 0; i < vdi_save_lines; i++)
|
|
memcpy(parent->vditmpbuf[0] + i*line_size,
|
|
base_off + offset_addr + i*t->set.ostride,
|
|
line_size);
|
|
parent->buf0filled = true;
|
|
} else {
|
|
offset_addr = t->set.o_off;
|
|
for (i = 0; i < vdi_save_lines; i++)
|
|
memcpy(base_off + offset_addr + i*t->set.ostride,
|
|
parent->vditmpbuf[0] + i*line_size,
|
|
line_size);
|
|
|
|
dmac_flush_range(base_off + offset_addr,
|
|
base_off + offset_addr + i*t->set.ostride);
|
|
outer_flush_range(t->output.paddr + offset_addr,
|
|
t->output.paddr + offset_addr + i*t->set.ostride);
|
|
parent->buf0filled = false;
|
|
}
|
|
}
|
|
/*Up&Right stripe*/
|
|
else if (stripe_mode == (UP_STRIPE | RIGHT_STRIPE)) {
|
|
if (!parent->buf1filled) {
|
|
offset_addr = t->set.o_off +
|
|
t->set.sp_setting.ud_split_line*t->set.ostride;
|
|
dmac_flush_range(base_off + offset_addr,
|
|
base_off + offset_addr + vdi_size);
|
|
outer_flush_range(t->output.paddr + offset_addr,
|
|
t->output.paddr + offset_addr + vdi_size);
|
|
|
|
for (i = 0; i < vdi_save_lines; i++)
|
|
memcpy(parent->vditmpbuf[1] + i*line_size,
|
|
base_off + offset_addr + i*t->set.ostride,
|
|
line_size);
|
|
parent->buf1filled = true;
|
|
} else {
|
|
offset_addr = t->set.o_off +
|
|
(t->output.crop.h - vdi_save_lines)*t->set.ostride;
|
|
for (i = 0; i < vdi_save_lines; i++)
|
|
memcpy(base_off + offset_addr + i*t->set.ostride,
|
|
parent->vditmpbuf[1] + i*line_size,
|
|
line_size);
|
|
|
|
dmac_flush_range(base_off + offset_addr,
|
|
base_off + offset_addr + i*t->set.ostride);
|
|
outer_flush_range(t->output.paddr + offset_addr,
|
|
t->output.paddr + offset_addr + i*t->set.ostride);
|
|
parent->buf1filled = false;
|
|
}
|
|
}
|
|
/*Down stripe or Down&Right stript*/
|
|
else if (stripe_mode == (DOWN_STRIPE | RIGHT_STRIPE)) {
|
|
if (!parent->buf1filled) {
|
|
offset_addr = t->set.o_off + vdi_save_lines*t->set.ostride;
|
|
dmac_flush_range(base_off + offset_addr,
|
|
base_off + offset_addr + vdi_save_lines*t->set.ostride);
|
|
outer_flush_range(t->output.paddr + offset_addr,
|
|
t->output.paddr + offset_addr + vdi_save_lines*t->set.ostride);
|
|
|
|
for (i = 0; i < vdi_save_lines; i++)
|
|
memcpy(parent->vditmpbuf[1] + i*line_size,
|
|
base_off + offset_addr + i*t->set.ostride,
|
|
line_size);
|
|
parent->buf1filled = true;
|
|
} else {
|
|
offset_addr = t->set.o_off;
|
|
for (i = 0; i < vdi_save_lines; i++)
|
|
memcpy(base_off + offset_addr + i*t->set.ostride,
|
|
parent->vditmpbuf[1] + i*line_size,
|
|
line_size);
|
|
|
|
dmac_flush_range(base_off + offset_addr,
|
|
base_off + offset_addr + vdi_save_lines*t->set.ostride);
|
|
outer_flush_range(t->output.paddr + offset_addr,
|
|
t->output.paddr + offset_addr + vdi_save_lines*t->set.ostride);
|
|
parent->buf1filled = false;
|
|
}
|
|
}
|
|
if (!pfn_valid(t->output.paddr >> PAGE_SHIFT))
|
|
iounmap(base_off);
|
|
mutex_unlock(lock);
|
|
}
|
|
|
|
static void do_task_release(struct ipu_task_entry *t, int fail)
|
|
{
|
|
int ret;
|
|
struct ipu_soc *ipu = t->ipu;
|
|
|
|
if (t->input.deinterlace.enable && !fail &&
|
|
(t->task_no & (UP_STRIPE | DOWN_STRIPE)))
|
|
vdi_split_process(ipu, t);
|
|
|
|
ipu_free_irq(ipu, t->irq, t);
|
|
|
|
if (t->vdoa_dma.vaddr)
|
|
dma_free_coherent(t->dev,
|
|
t->vdoa_dma.size,
|
|
t->vdoa_dma.vaddr,
|
|
t->vdoa_dma.paddr);
|
|
|
|
if (only_ic(t->set.mode)) {
|
|
ret = ipu_disable_channel(ipu, t->set.ic_chan, true);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_ic",
|
|
STATE_DISABLE_CHAN_FAIL, ret);
|
|
if (deinterlace_3_field(t)) {
|
|
ret = ipu_disable_channel(ipu, t->set.vdi_ic_p_chan,
|
|
true);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_ic_p",
|
|
STATE_DISABLE_CHAN_FAIL, ret);
|
|
ret = ipu_disable_channel(ipu, t->set.vdi_ic_n_chan,
|
|
true);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_ic_n",
|
|
STATE_DISABLE_CHAN_FAIL, ret);
|
|
}
|
|
} else if (only_rot(t->set.mode)) {
|
|
ret = ipu_disable_channel(ipu, t->set.rot_chan, true);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_rot",
|
|
STATE_DISABLE_CHAN_FAIL, ret);
|
|
} else if (ic_and_rot(t->set.mode)) {
|
|
ret = ipu_unlink_channels(ipu, t->set.ic_chan, t->set.rot_chan);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_unlink_ch",
|
|
STATE_UNLINK_CHAN_FAIL, ret);
|
|
ret = ipu_disable_channel(ipu, t->set.rot_chan, true);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch ic_and_rot-rot",
|
|
STATE_DISABLE_CHAN_FAIL, ret);
|
|
ret = ipu_disable_channel(ipu, t->set.ic_chan, true);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch ic_and_rot-ic",
|
|
STATE_DISABLE_CHAN_FAIL, ret);
|
|
if (deinterlace_3_field(t)) {
|
|
ret = ipu_disable_channel(ipu, t->set.vdi_ic_p_chan,
|
|
true);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch icrot-ic-p",
|
|
STATE_DISABLE_CHAN_FAIL, ret);
|
|
ret = ipu_disable_channel(ipu, t->set.vdi_ic_n_chan,
|
|
true);
|
|
CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch icrot-ic-n",
|
|
STATE_DISABLE_CHAN_FAIL, ret);
|
|
}
|
|
}
|
|
|
|
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)) {
|
|
uninit_ic(ipu, t);
|
|
uninit_rot(ipu, t);
|
|
}
|
|
|
|
t->state = STATE_OK;
|
|
CHECK_PERF(&t->ts_rel);
|
|
return;
|
|
}
|
|
|
|
static void do_task_vdoa_only(struct ipu_task_entry *t)
|
|
{
|
|
int ret;
|
|
|
|
ret = init_tiled_ch_bufs(NULL, t);
|
|
CHECK_RETCODE(ret < 0, "do_vdoa_only", STATE_ERR, out, ret);
|
|
ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS);
|
|
vdoa_stop(t->vdoa_handle);
|
|
CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_only",
|
|
STATE_VDOA_IRQ_TIMEOUT, out, ret);
|
|
|
|
t->state = STATE_OK;
|
|
out:
|
|
return;
|
|
}
|
|
|
|
static void do_task(struct ipu_task_entry *t)
|
|
{
|
|
int r_size;
|
|
int irq;
|
|
int ret;
|
|
uint32_t busy;
|
|
struct ipu_soc *ipu = t->ipu;
|
|
|
|
CHECK_PERF(&t->ts_dotask);
|
|
|
|
if (!ipu) {
|
|
t->state = STATE_NO_IPU;
|
|
return;
|
|
}
|
|
|
|
init_completion(&t->irq_comp);
|
|
dev_dbg(ipu->dev, "[0x%p]Do task no:0x%x: id %d\n", (void *)t,
|
|
t->task_no, t->task_id);
|
|
dump_task_info(t);
|
|
|
|
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) {
|
|
if (t->set.mode & VDOA_BAND_MODE) {
|
|
t->set.ic_chan = MEM_VDI_MEM;
|
|
if (deinterlace_3_field(t)) {
|
|
t->set.vdi_ic_p_chan = MEM_VDI_MEM_P;
|
|
t->set.vdi_ic_n_chan = MEM_VDI_MEM_N;
|
|
}
|
|
dev_dbg(ipu->dev, "[0x%p]ic ch MEM_VDI_MEM\n",
|
|
(void *)t);
|
|
} else {
|
|
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;
|
|
}
|
|
dev_dbg(ipu->dev,
|
|
"[0x%p]ic ch MEM_VDI_PRP_VF_MEM\n", 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);
|
|
}
|
|
|
|
if (t->task_id == IPU_TASK_ID_VF)
|
|
busy = ic_vf_pp_is_busy(ipu, true);
|
|
else if (t->task_id == IPU_TASK_ID_PP)
|
|
busy = ic_vf_pp_is_busy(ipu, false);
|
|
else {
|
|
dev_err(ipu->dev, "ERR[no:0x%x]ipu task_id:%d invalid!\n",
|
|
t->task_no, t->task_id);
|
|
return;
|
|
}
|
|
if (busy) {
|
|
dev_err(ipu->dev, "ERR[0x%p-no:0x%x]ipu task_id:%d busy!\n",
|
|
(void *)t, t->task_no, t->task_id);
|
|
t->state = STATE_IPU_BUSY;
|
|
return;
|
|
}
|
|
|
|
irq = get_irq(t);
|
|
if (irq < 0) {
|
|
t->state = STATE_NO_IRQ;
|
|
return;
|
|
}
|
|
t->irq = irq;
|
|
|
|
/* 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);
|
|
CHECK_RETCODE(ret < 0, "init_ic only_ic",
|
|
t->state, chan_setup, ret);
|
|
} else if (only_rot(t->set.mode)) {
|
|
dev_dbg(t->dev, "[0x%p]only rot mode\n", (void *)t);
|
|
ret = init_rot(ipu, t);
|
|
CHECK_RETCODE(ret < 0, "init_rot only_rot",
|
|
t->state, chan_setup, ret);
|
|
} else if (ic_and_rot(t->set.mode)) {
|
|
int rot_idx = (t->task_id == IPU_TASK_ID_VF) ? 0 : 1;
|
|
|
|
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;
|
|
}
|
|
t->set.r_stride = t->set.r_width *
|
|
bytes_per_pixel(t->set.r_fmt);
|
|
r_size = PAGE_ALIGN(t->set.r_width * t->set.r_height
|
|
* fmt_to_bpp(t->set.r_fmt)/8);
|
|
|
|
if (r_size > ipu->rot_dma[rot_idx].size) {
|
|
dev_dbg(t->dev, "[0x%p]realloc rot buffer\n", (void *)t);
|
|
|
|
if (ipu->rot_dma[rot_idx].vaddr)
|
|
dma_free_coherent(t->dev,
|
|
ipu->rot_dma[rot_idx].size,
|
|
ipu->rot_dma[rot_idx].vaddr,
|
|
ipu->rot_dma[rot_idx].paddr);
|
|
|
|
ipu->rot_dma[rot_idx].size = r_size;
|
|
ipu->rot_dma[rot_idx].vaddr = dma_alloc_coherent(t->dev,
|
|
r_size,
|
|
&ipu->rot_dma[rot_idx].paddr,
|
|
GFP_DMA | GFP_KERNEL);
|
|
CHECK_RETCODE(ipu->rot_dma[rot_idx].vaddr == NULL,
|
|
"ic_and_rot", STATE_SYS_NO_MEM,
|
|
chan_setup, -ENOMEM);
|
|
}
|
|
t->set.r_paddr = ipu->rot_dma[rot_idx].paddr;
|
|
|
|
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);
|
|
CHECK_RETCODE(ret < 0, "init_ic ic_and_rot",
|
|
t->state, chan_setup, ret);
|
|
ret = init_rot(ipu, t);
|
|
CHECK_RETCODE(ret < 0, "init_rot ic_and_rot",
|
|
t->state, chan_setup, ret);
|
|
ret = ipu_link_channels(ipu, t->set.ic_chan,
|
|
t->set.rot_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_link_ch ic_and_rot",
|
|
STATE_LINK_CHAN_FAIL, chan_setup, ret);
|
|
} else {
|
|
dev_err(t->dev, "ERR [0x%p]do task: should not be here\n", t);
|
|
t->state = STATE_ERR;
|
|
return;
|
|
}
|
|
|
|
ret = ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, t);
|
|
CHECK_RETCODE(ret < 0, "ipu_req_irq",
|
|
STATE_IRQ_FAIL, chan_setup, ret);
|
|
|
|
/* enable/start channel */
|
|
if (only_ic(t->set.mode)) {
|
|
ret = ipu_enable_channel(ipu, t->set.ic_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic",
|
|
STATE_ENABLE_CHAN_FAIL, chan_en, ret);
|
|
if (deinterlace_3_field(t)) {
|
|
ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_p",
|
|
STATE_ENABLE_CHAN_FAIL, chan_en, ret);
|
|
ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_n",
|
|
STATE_ENABLE_CHAN_FAIL, chan_en, ret);
|
|
}
|
|
|
|
ret = ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER,
|
|
0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic",
|
|
STATE_SEL_BUF_FAIL, chan_buf, ret);
|
|
if (t->overlay_en) {
|
|
ret = ipu_select_buffer(ipu, t->set.ic_chan,
|
|
IPU_GRAPH_IN_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_g",
|
|
STATE_SEL_BUF_FAIL, chan_buf, ret);
|
|
if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
|
|
ret = ipu_select_buffer(ipu, t->set.ic_chan,
|
|
IPU_ALPHA_IN_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_a",
|
|
STATE_SEL_BUF_FAIL, chan_buf,
|
|
ret);
|
|
}
|
|
}
|
|
if (!(t->set.mode & VDOA_BAND_MODE)) {
|
|
if (deinterlace_3_field(t))
|
|
ipu_select_multi_vdi_buffer(ipu, 0);
|
|
else {
|
|
ret = ipu_select_buffer(ipu, t->set.ic_chan,
|
|
IPU_INPUT_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_i",
|
|
STATE_SEL_BUF_FAIL, chan_buf, ret);
|
|
}
|
|
}
|
|
} else if (only_rot(t->set.mode)) {
|
|
ret = ipu_enable_channel(ipu, t->set.rot_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_enable_ch only_rot",
|
|
STATE_ENABLE_CHAN_FAIL, chan_en, ret);
|
|
ret = ipu_select_buffer(ipu, t->set.rot_chan,
|
|
IPU_OUTPUT_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_o",
|
|
STATE_SEL_BUF_FAIL, chan_buf, ret);
|
|
ret = ipu_select_buffer(ipu, t->set.rot_chan,
|
|
IPU_INPUT_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_i",
|
|
STATE_SEL_BUF_FAIL, chan_buf, ret);
|
|
} else if (ic_and_rot(t->set.mode)) {
|
|
ret = ipu_enable_channel(ipu, t->set.rot_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-rot",
|
|
STATE_ENABLE_CHAN_FAIL, chan_en, ret);
|
|
ret = ipu_enable_channel(ipu, t->set.ic_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-ic",
|
|
STATE_ENABLE_CHAN_FAIL, chan_en, ret);
|
|
if (deinterlace_3_field(t)) {
|
|
ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-p",
|
|
STATE_ENABLE_CHAN_FAIL, chan_en, ret);
|
|
ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan);
|
|
CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-n",
|
|
STATE_ENABLE_CHAN_FAIL, chan_en, ret);
|
|
}
|
|
|
|
ret = ipu_select_buffer(ipu, t->set.rot_chan,
|
|
IPU_OUTPUT_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-rot-o",
|
|
STATE_SEL_BUF_FAIL, chan_buf, ret);
|
|
if (t->overlay_en) {
|
|
ret = ipu_select_buffer(ipu, t->set.ic_chan,
|
|
IPU_GRAPH_IN_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-g",
|
|
STATE_SEL_BUF_FAIL, chan_buf, ret);
|
|
if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
|
|
ret = ipu_select_buffer(ipu, t->set.ic_chan,
|
|
IPU_ALPHA_IN_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf icrot-ic-a",
|
|
STATE_SEL_BUF_FAIL,
|
|
chan_buf, ret);
|
|
}
|
|
}
|
|
ret = ipu_select_buffer(ipu, t->set.ic_chan,
|
|
IPU_OUTPUT_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-o",
|
|
STATE_SEL_BUF_FAIL, chan_buf, ret);
|
|
if (deinterlace_3_field(t))
|
|
ipu_select_multi_vdi_buffer(ipu, 0);
|
|
else {
|
|
ret = ipu_select_buffer(ipu, t->set.ic_chan,
|
|
IPU_INPUT_BUFFER, 0);
|
|
CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-i",
|
|
STATE_SEL_BUF_FAIL, chan_buf, ret);
|
|
}
|
|
}
|
|
|
|
if (need_split(t))
|
|
t->state = STATE_IN_PROGRESS;
|
|
|
|
if (t->set.mode & VDOA_BAND_MODE) {
|
|
ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS);
|
|
CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_band",
|
|
STATE_VDOA_IRQ_TIMEOUT, chan_rel, ret);
|
|
}
|
|
|
|
CHECK_PERF(&t->ts_waitirq);
|
|
ret = wait_for_completion_timeout(&t->irq_comp,
|
|
msecs_to_jiffies(t->timeout - DEF_DELAY_MS));
|
|
CHECK_PERF(&t->ts_wakeup);
|
|
CHECK_RETCODE(ret == 0, "wait_for_comp_timeout",
|
|
STATE_IRQ_TIMEOUT, chan_rel, ret);
|
|
dev_dbg(t->dev, "[0x%p] no-0x%x ipu irq done!", t, t->task_no);
|
|
|
|
chan_rel:
|
|
chan_buf:
|
|
chan_en:
|
|
chan_setup:
|
|
if (t->set.mode & VDOA_BAND_MODE)
|
|
vdoa_stop(t->vdoa_handle);
|
|
do_task_release(t, t->state >= STATE_ERR);
|
|
return;
|
|
}
|
|
|
|
static void do_task_vdoa_vdi(struct ipu_task_entry *t)
|
|
{
|
|
int i;
|
|
int ret;
|
|
u32 stripe_width;
|
|
|
|
/* FIXME: crop mode not support now */
|
|
stripe_width = t->input.width >> 1;
|
|
t->input.crop.pos.x = 0;
|
|
t->input.crop.pos.y = 0;
|
|
t->input.crop.w = stripe_width;
|
|
t->input.crop.h = t->input.height;
|
|
t->output.crop.w = stripe_width;
|
|
t->output.crop.h = t->input.height;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
t->input.crop.pos.x = t->input.crop.pos.x + i * stripe_width;
|
|
t->output.crop.pos.x = t->output.crop.pos.x + i * stripe_width;
|
|
/* check input */
|
|
ret = set_crop(&t->input.crop, t->input.width, t->input.height,
|
|
t->input.format);
|
|
if (ret < 0) {
|
|
ret = STATE_ERR;
|
|
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);
|
|
dev_dbg(t->dev, "i_off:0x%x, i_uoff:0x%x, istride:%d.\n",
|
|
t->set.i_off, t->set.i_uoff, t->set.istride);
|
|
/* check output */
|
|
ret = set_crop(&t->output.crop, t->input.width,
|
|
t->output.height, t->output.format);
|
|
if (ret < 0) {
|
|
ret = STATE_ERR;
|
|
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);
|
|
|
|
dev_dbg(t->dev, "o_off:0x%x, o_uoff:0x%x, ostride:%d.\n",
|
|
t->set.o_off, t->set.o_uoff, t->set.ostride);
|
|
|
|
do_task(t);
|
|
}
|
|
|
|
return;
|
|
done:
|
|
dev_err(t->dev, "ERR %s set_crop.\n", __func__);
|
|
t->state = ret;
|
|
return;
|
|
}
|
|
|
|
static void get_res_do_task(struct ipu_task_entry *t)
|
|
{
|
|
uint32_t found;
|
|
uint32_t split_child;
|
|
struct mutex *lock;
|
|
|
|
found = get_vdoa_ipu_res(t);
|
|
if (!found) {
|
|
dev_err(t->dev, "ERR:[0x%p] no-0x%x can not get res\n",
|
|
t, t->task_no);
|
|
return;
|
|
} else {
|
|
if (t->set.task & VDOA_ONLY)
|
|
do_task_vdoa_only(t);
|
|
else if ((IPU_PIX_FMT_TILED_NV12F == t->input.format) &&
|
|
(t->set.mode & VDOA_BAND_MODE) &&
|
|
(t->input.crop.w >
|
|
soc_max_vdi_in_width(t->ipu)))
|
|
do_task_vdoa_vdi(t);
|
|
else
|
|
do_task(t);
|
|
put_vdoa_ipu_res(t, 0);
|
|
}
|
|
if (t->state != STATE_OK) {
|
|
dev_err(t->dev, "ERR:[0x%p] no-0x%x state: %s\n",
|
|
t, t->task_no, state_msg[t->state].msg);
|
|
}
|
|
|
|
split_child = need_split(t) && t->parent;
|
|
if (split_child) {
|
|
lock = &t->parent->split_lock;
|
|
mutex_lock(lock);
|
|
t->split_done = 1;
|
|
mutex_unlock(lock);
|
|
wake_up(&t->parent->split_waitq);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void wait_split_task_complete(struct ipu_task_entry *parent,
|
|
struct ipu_split_task *sp_task, uint32_t size)
|
|
{
|
|
struct ipu_task_entry *tsk = NULL;
|
|
int ret = 0, rc;
|
|
int j, idx = -1;
|
|
unsigned long flags;
|
|
struct mutex *lock = &parent->split_lock;
|
|
int k, busy_vf, busy_pp;
|
|
struct ipu_soc *ipu;
|
|
DECLARE_PERF_VAR;
|
|
|
|
for (j = 0; j < size; j++) {
|
|
rc = wait_event_timeout(
|
|
parent->split_waitq,
|
|
sp_task_check_done(sp_task, parent, size, &idx),
|
|
msecs_to_jiffies(parent->timeout - DEF_DELAY_MS));
|
|
if (!rc) {
|
|
dev_err(parent->dev,
|
|
"ERR:[0x%p] no-0x%x, split_task timeout,j:%d,"
|
|
"size:%d.\n",
|
|
parent, parent->task_no, j, size);
|
|
ret = -ETIMEDOUT;
|
|
goto out;
|
|
} else {
|
|
if (idx < 0) {
|
|
dev_err(parent->dev,
|
|
"ERR:[0x%p] no-0x%x, invalid task idx:%d\n",
|
|
parent, parent->task_no, idx);
|
|
continue;
|
|
}
|
|
tsk = sp_task[idx].child_task;
|
|
mutex_lock(lock);
|
|
if (!tsk->split_done || !tsk->ipu)
|
|
dev_err(tsk->dev,
|
|
"ERR:no-0x%x,split not done:%d/null ipu:0x%p\n",
|
|
tsk->task_no, tsk->split_done, tsk->ipu);
|
|
tsk->split_done = 0;
|
|
mutex_unlock(lock);
|
|
|
|
dev_dbg(tsk->dev,
|
|
"[0x%p] no-0x%x sp_tsk[%d] done,state:%d.\n",
|
|
tsk, tsk->task_no, idx, tsk->state);
|
|
#ifdef DBG_IPU_PERF
|
|
CHECK_PERF(&tsk->ts_rel);
|
|
PRINT_TASK_STATISTICS;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (ret == -ETIMEDOUT) {
|
|
/* debug */
|
|
for (k = 0; k < max_ipu_no; k++) {
|
|
ipu = ipu_get_soc(k);
|
|
if (IS_ERR(ipu)) {
|
|
dev_err(parent->dev, "no:0x%x, null ipu:%d\n",
|
|
parent->task_no, k);
|
|
} else {
|
|
busy_vf = ic_vf_pp_is_busy(ipu, true);
|
|
busy_pp = ic_vf_pp_is_busy(ipu, false);
|
|
dev_err(parent->dev,
|
|
"ERR:ipu[%d] busy_vf:%d, busy_pp:%d.\n",
|
|
k, busy_vf, busy_pp);
|
|
}
|
|
}
|
|
for (k = 0; k < size; k++) {
|
|
tsk = sp_task[k].child_task;
|
|
if (!tsk)
|
|
continue;
|
|
dev_err(parent->dev,
|
|
"ERR: sp_task[%d][0x%p] no-0x%x done:%d,"
|
|
"state:%s,on_list:%d, ipu:0x%p,timeout!\n",
|
|
k, tsk, tsk->task_no, tsk->split_done,
|
|
state_msg[tsk->state].msg, tsk->task_in_list,
|
|
tsk->ipu);
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < size; j++) {
|
|
tsk = sp_task[j].child_task;
|
|
if (!tsk)
|
|
continue;
|
|
spin_lock_irqsave(&ipu_task_list_lock, flags);
|
|
if (tsk->task_in_list) {
|
|
list_del(&tsk->node);
|
|
tsk->task_in_list = 0;
|
|
dev_dbg(tsk->dev,
|
|
"[0x%p] no-0x%x,id:%d sp_tsk timeout list_del.\n",
|
|
tsk, tsk->task_no, tsk->task_id);
|
|
}
|
|
spin_unlock_irqrestore(&ipu_task_list_lock, flags);
|
|
if (!tsk->ipu)
|
|
continue;
|
|
if (tsk->state != STATE_OK) {
|
|
dev_err(tsk->dev,
|
|
"ERR:[0x%p] no-0x%x,id:%d, sp_tsk state: %s\n",
|
|
tsk, tsk->task_no, tsk->task_id,
|
|
state_msg[tsk->state].msg);
|
|
}
|
|
kref_put(&tsk->refcount, task_mem_free);
|
|
}
|
|
|
|
kfree(parent->vditmpbuf[0]);
|
|
kfree(parent->vditmpbuf[1]);
|
|
|
|
if (ret < 0)
|
|
parent->state = STATE_TIMEOUT;
|
|
else
|
|
parent->state = STATE_OK;
|
|
return;
|
|
}
|
|
|
|
static inline int find_task(struct ipu_task_entry **t, int thread_id)
|
|
{
|
|
int found;
|
|
unsigned long flags;
|
|
struct ipu_task_entry *tsk;
|
|
struct list_head *task_list = &ipu_task_list;
|
|
|
|
*t = NULL;
|
|
spin_lock_irqsave(&ipu_task_list_lock, flags);
|
|
found = !list_empty(task_list);
|
|
if (found) {
|
|
tsk = list_first_entry(task_list, struct ipu_task_entry, node);
|
|
if (tsk->task_in_list) {
|
|
list_del(&tsk->node);
|
|
tsk->task_in_list = 0;
|
|
*t = tsk;
|
|
kref_get(&tsk->refcount);
|
|
dev_dbg(tsk->dev,
|
|
"thread_id:%d,[0x%p] task_no:0x%x,mode:0x%x list_del\n",
|
|
thread_id, tsk, tsk->task_no, tsk->set.mode);
|
|
} else
|
|
dev_err(tsk->dev,
|
|
"thread_id:%d,task_no:0x%x,mode:0x%x not on list_del\n",
|
|
thread_id, tsk->task_no, tsk->set.mode);
|
|
}
|
|
spin_unlock_irqrestore(&ipu_task_list_lock, flags);
|
|
|
|
return found;
|
|
}
|
|
|
|
static int ipu_task_thread(void *argv)
|
|
{
|
|
struct ipu_task_entry *tsk;
|
|
struct ipu_task_entry *sp_tsk0;
|
|
struct ipu_split_task sp_task[4];
|
|
/* priority lower than irq_thread */
|
|
const struct sched_param param = {
|
|
.sched_priority = MAX_USER_RT_PRIO/2 - 1,
|
|
};
|
|
int ret;
|
|
int curr_thread_id;
|
|
uint32_t size;
|
|
unsigned long flags;
|
|
unsigned int cpu;
|
|
struct cpumask cpu_mask;
|
|
struct ipu_thread_data *data = (struct ipu_thread_data *)argv;
|
|
|
|
thread_id++;
|
|
curr_thread_id = thread_id;
|
|
sched_setscheduler(current, SCHED_FIFO, ¶m);
|
|
|
|
if (!data->is_vdoa) {
|
|
cpu = cpumask_first(cpu_online_mask);
|
|
cpumask_set_cpu(cpu, &cpu_mask);
|
|
ret = sched_setaffinity(data->ipu->thread[data->id]->pid,
|
|
&cpu_mask);
|
|
if (ret < 0) {
|
|
pr_err("%s: sched_setaffinity fail:%d.\n", __func__, ret);
|
|
}
|
|
pr_debug("%s: sched_setaffinity cpu:%d.\n", __func__, cpu);
|
|
}
|
|
|
|
while (!kthread_should_stop()) {
|
|
int split_fail = 0;
|
|
int split_parent;
|
|
int split_child;
|
|
|
|
wait_event_interruptible(thread_waitq, find_task(&tsk, curr_thread_id));
|
|
|
|
if (!tsk) {
|
|
pr_err("thread:%d can not find task.\n",
|
|
curr_thread_id);
|
|
continue;
|
|
}
|
|
|
|
/* note: other threads run split child task */
|
|
split_parent = need_split(tsk) && !tsk->parent;
|
|
split_child = need_split(tsk) && tsk->parent;
|
|
if (split_parent) {
|
|
if ((tsk->set.split_mode == RL_SPLIT) ||
|
|
(tsk->set.split_mode == UD_SPLIT))
|
|
size = 2;
|
|
else
|
|
size = 4;
|
|
ret = queue_split_task(tsk, sp_task, size);
|
|
if (ret < 0) {
|
|
split_fail = 1;
|
|
} else {
|
|
struct list_head *pos;
|
|
|
|
spin_lock_irqsave(&ipu_task_list_lock, flags);
|
|
|
|
sp_tsk0 = list_first_entry(&tsk->split_list,
|
|
struct ipu_task_entry, node);
|
|
list_del(&sp_tsk0->node);
|
|
|
|
list_for_each(pos, &tsk->split_list) {
|
|
struct ipu_task_entry *tmp;
|
|
|
|
tmp = list_entry(pos,
|
|
struct ipu_task_entry, node);
|
|
tmp->task_in_list = 1;
|
|
dev_dbg(tmp->dev,
|
|
"[0x%p] no-0x%x,id:%d sp_tsk "
|
|
"add_to_list.\n", tmp,
|
|
tmp->task_no, tmp->task_id);
|
|
}
|
|
/* add to global list */
|
|
list_splice(&tsk->split_list, &ipu_task_list);
|
|
|
|
spin_unlock_irqrestore(&ipu_task_list_lock,
|
|
flags);
|
|
/* let the parent thread do the first sp_task */
|
|
/* FIXME: ensure the correct sequence for split
|
|
4size: 5/6->9/a*/
|
|
wake_up_interruptible(&thread_waitq);
|
|
get_res_do_task(sp_tsk0);
|
|
dev_dbg(sp_tsk0->dev,
|
|
"thread:%d complete tsk no:0x%x.\n",
|
|
curr_thread_id, sp_tsk0->task_no);
|
|
ret = atomic_read(&req_cnt);
|
|
if (ret > 0) {
|
|
wake_up(&res_waitq);
|
|
dev_dbg(sp_tsk0->dev,
|
|
"sp_tsk0 sche thread:%d no:0x%x,"
|
|
"req_cnt:%d\n", curr_thread_id,
|
|
sp_tsk0->task_no, ret);
|
|
/* For other threads to get_res */
|
|
schedule();
|
|
}
|
|
}
|
|
} else
|
|
get_res_do_task(tsk);
|
|
|
|
/* wait for all 4 sp_task finished here or timeout
|
|
and then release all resources */
|
|
if (split_parent && !split_fail)
|
|
wait_split_task_complete(tsk, sp_task, size);
|
|
|
|
if (!split_child) {
|
|
atomic_inc(&tsk->done);
|
|
wake_up(&tsk->task_waitq);
|
|
}
|
|
|
|
dev_dbg(tsk->dev, "thread:%d complete tsk no:0x%x-[0x%p].\n",
|
|
curr_thread_id, tsk->task_no, tsk);
|
|
ret = atomic_read(&req_cnt);
|
|
if (ret > 0) {
|
|
wake_up(&res_waitq);
|
|
dev_dbg(tsk->dev, "sche thread:%d no:0x%x,req_cnt:%d\n",
|
|
curr_thread_id, tsk->task_no, ret);
|
|
/* note: give cpu to other threads to get_res */
|
|
schedule();
|
|
}
|
|
|
|
kref_put(&tsk->refcount, task_mem_free);
|
|
}
|
|
|
|
pr_info("ERR %s exit.\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
dump_task_info(tsk);
|
|
|
|
kref_put(&tsk->refcount, task_mem_free);
|
|
if (ret != 0)
|
|
pr_debug("%s ret:%d.\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ipu_check_task);
|
|
|
|
int ipu_queue_task(struct ipu_task *task)
|
|
{
|
|
struct ipu_task_entry *tsk;
|
|
unsigned long flags;
|
|
int ret;
|
|
u32 tmp_task_no;
|
|
DECLARE_PERF_VAR;
|
|
|
|
tsk = create_task_entry(task);
|
|
if (IS_ERR(tsk))
|
|
return PTR_ERR(tsk);
|
|
|
|
CHECK_PERF(&tsk->ts_queue);
|
|
ret = prepare_task(tsk);
|
|
if (ret < 0)
|
|
goto done;
|
|
|
|
if (need_split(tsk)) {
|
|
CHECK_PERF(&tsk->ts_dotask);
|
|
CHECK_PERF(&tsk->ts_waitirq);
|
|
CHECK_PERF(&tsk->ts_inirq);
|
|
CHECK_PERF(&tsk->ts_wakeup);
|
|
}
|
|
|
|
/* task_no last four bits for split task type*/
|
|
tmp_task_no = atomic_inc_return(&frame_no);
|
|
tsk->task_no = tmp_task_no << 4;
|
|
init_waitqueue_head(&tsk->task_waitq);
|
|
|
|
spin_lock_irqsave(&ipu_task_list_lock, flags);
|
|
list_add_tail(&tsk->node, &ipu_task_list);
|
|
tsk->task_in_list = 1;
|
|
dev_dbg(tsk->dev, "[0x%p,no-0x%x] list_add_tail\n", tsk, tsk->task_no);
|
|
spin_unlock_irqrestore(&ipu_task_list_lock, flags);
|
|
wake_up_interruptible(&thread_waitq);
|
|
|
|
ret = wait_event_timeout(tsk->task_waitq, atomic_read(&tsk->done),
|
|
msecs_to_jiffies(tsk->timeout));
|
|
if (0 == ret) {
|
|
/* note: the timeout should larger than the internal timeout!*/
|
|
ret = -ETIMEDOUT;
|
|
dev_err(tsk->dev, "ERR: [0x%p] no-0x%x, timeout:%dms!\n",
|
|
tsk, tsk->task_no, tsk->timeout);
|
|
} else {
|
|
if (STATE_OK != tsk->state) {
|
|
dev_err(tsk->dev, "ERR: [0x%p] no-0x%x,state %d: %s\n",
|
|
tsk, tsk->task_no, tsk->state,
|
|
state_msg[tsk->state].msg);
|
|
ret = -ECANCELED;
|
|
} else
|
|
ret = 0;
|
|
}
|
|
|
|
spin_lock_irqsave(&ipu_task_list_lock, flags);
|
|
if (tsk->task_in_list) {
|
|
list_del(&tsk->node);
|
|
tsk->task_in_list = 0;
|
|
dev_dbg(tsk->dev, "[0x%p] no:0x%x list_del\n",
|
|
tsk, tsk->task_no);
|
|
}
|
|
spin_unlock_irqrestore(&ipu_task_list_lock, flags);
|
|
|
|
#ifdef DBG_IPU_PERF
|
|
CHECK_PERF(&tsk->ts_rel);
|
|
PRINT_TASK_STATISTICS;
|
|
if (ts_frame_avg == 0)
|
|
ts_frame_avg = ts_frame.tv_nsec / NSEC_PER_USEC +
|
|
ts_frame.tv_sec * USEC_PER_SEC;
|
|
else
|
|
ts_frame_avg = (ts_frame_avg + ts_frame.tv_nsec / NSEC_PER_USEC
|
|
+ ts_frame.tv_sec * USEC_PER_SEC)/2;
|
|
if (timespec_compare(&ts_frame, &ts_frame_max) > 0)
|
|
ts_frame_max = ts_frame;
|
|
|
|
atomic_inc(&frame_cnt);
|
|
|
|
if ((atomic_read(&frame_cnt) % 1000) == 0)
|
|
pr_debug("ipu_dev: max frame time:%ldus, avg frame time:%dus,"
|
|
"frame_cnt:%d\n", ts_frame_max.tv_nsec / NSEC_PER_USEC
|
|
+ ts_frame_max.tv_sec * USEC_PER_SEC,
|
|
ts_frame_avg, atomic_read(&frame_cnt));
|
|
#endif
|
|
done:
|
|
if (ret < 0)
|
|
dev_err(tsk->dev, "ERR: no-0x%x,ipu_queue_task err:%d\n",
|
|
tsk->task_no, ret);
|
|
|
|
kref_put(&tsk->refcount, task_mem_free);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ipu_queue_task);
|
|
|
|
static int mxc_ipu_open(struct inode *inode, struct file *file)
|
|
{
|
|
file->private_data = (void *)atomic_inc_return(&file_index);
|
|
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:
|
|
{
|
|
struct ipu_task task;
|
|
|
|
if (copy_from_user
|
|
(&task, (struct ipu_task *) arg,
|
|
sizeof(struct ipu_task)))
|
|
return -EFAULT;
|
|
ret = ipu_check_task(&task);
|
|
if (copy_to_user((struct ipu_task *) arg,
|
|
&task, sizeof(struct ipu_task)))
|
|
return -EFAULT;
|
|
break;
|
|
}
|
|
case IPU_QUEUE_TASK:
|
|
{
|
|
struct ipu_task task;
|
|
|
|
if (copy_from_user
|
|
(&task, (struct ipu_task *) arg,
|
|
sizeof(struct ipu_task)))
|
|
return -EFAULT;
|
|
ret = ipu_queue_task(&task);
|
|
break;
|
|
}
|
|
case IPU_ALLOC:
|
|
{
|
|
int size;
|
|
struct ipu_alloc_list *mem;
|
|
|
|
mem = kzalloc(sizeof(*mem), GFP_KERNEL);
|
|
if (mem == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (get_user(size, argp)) {
|
|
kfree(mem);
|
|
return -EFAULT;
|
|
}
|
|
|
|
mem->size = PAGE_ALIGN(size);
|
|
|
|
mem->cpu_addr = dma_alloc_coherent(ipu_dev, size,
|
|
&mem->phy_addr,
|
|
GFP_DMA | GFP_KERNEL);
|
|
if (mem->cpu_addr == NULL) {
|
|
kfree(mem);
|
|
return -ENOMEM;
|
|
}
|
|
mem->file_index = file->private_data;
|
|
mutex_lock(&ipu_alloc_lock);
|
|
list_add(&mem->list, &ipu_alloc_list);
|
|
mutex_unlock(&ipu_alloc_lock);
|
|
|
|
if (put_user(mem->phy_addr, argp)) {
|
|
mutex_lock(&ipu_alloc_lock);
|
|
list_del(&mem->list);
|
|
mutex_unlock(&ipu_alloc_lock);
|
|
dma_free_coherent(ipu_dev,
|
|
mem->size,
|
|
mem->cpu_addr,
|
|
mem->phy_addr);
|
|
kfree(mem);
|
|
return -EFAULT;
|
|
}
|
|
|
|
dev_dbg(ipu_dev, "allocated %d bytes @ 0x%08X\n",
|
|
mem->size, mem->phy_addr);
|
|
|
|
break;
|
|
}
|
|
case IPU_FREE:
|
|
{
|
|
unsigned long offset;
|
|
struct ipu_alloc_list *mem;
|
|
|
|
if (get_user(offset, argp))
|
|
return -EFAULT;
|
|
|
|
ret = -EINVAL;
|
|
mutex_lock(&ipu_alloc_lock);
|
|
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;
|
|
}
|
|
}
|
|
mutex_unlock(&ipu_alloc_lock);
|
|
if (0 == ret)
|
|
dev_dbg(ipu_dev, "free %d bytes @ 0x%08X\n",
|
|
mem->size, mem->phy_addr);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
|
|
mutex_lock(&ipu_alloc_lock);
|
|
list_for_each_entry(mem, &ipu_alloc_list, list) {
|
|
if (offset == mem->phy_addr) {
|
|
found = true;
|
|
len = mem->size;
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&ipu_alloc_lock);
|
|
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,
|
|
vma->vm_end - vma->vm_start,
|
|
vma->vm_page_prot)) {
|
|
printk(KERN_ERR
|
|
"mmap failed!\n");
|
|
return -ENOBUFS;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mxc_ipu_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct ipu_alloc_list *mem;
|
|
struct ipu_alloc_list *n;
|
|
|
|
mutex_lock(&ipu_alloc_lock);
|
|
list_for_each_entry_safe(mem, n, &ipu_alloc_list, list) {
|
|
if ((mem->cpu_addr != 0) &&
|
|
(file->private_data == mem->file_index)) {
|
|
list_del(&mem->list);
|
|
dma_free_coherent(ipu_dev,
|
|
mem->size,
|
|
mem->cpu_addr,
|
|
mem->phy_addr);
|
|
dev_dbg(ipu_dev, "rel-free %d bytes @ 0x%08X\n",
|
|
mem->size, mem->phy_addr);
|
|
kfree(mem);
|
|
}
|
|
}
|
|
mutex_unlock(&ipu_alloc_lock);
|
|
atomic_dec(&file_index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct file_operations mxc_ipu_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = mxc_ipu_open,
|
|
.mmap = mxc_ipu_mmap,
|
|
.release = mxc_ipu_release,
|
|
.unlocked_ioctl = mxc_ipu_ioctl,
|
|
};
|
|
|
|
int register_ipu_device(struct ipu_soc *ipu, int id)
|
|
{
|
|
int ret = 0;
|
|
static int idx;
|
|
static struct ipu_thread_data thread_data[5];
|
|
|
|
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);
|
|
|
|
mutex_init(&ipu_ch_tbl.lock);
|
|
}
|
|
max_ipu_no = ++id;
|
|
ipu->rot_dma[0].size = 0;
|
|
ipu->rot_dma[1].size = 0;
|
|
|
|
thread_data[idx].ipu = ipu;
|
|
thread_data[idx].id = 0;
|
|
thread_data[idx].is_vdoa = 0;
|
|
ipu->thread[0] = kthread_run(ipu_task_thread, &thread_data[idx++],
|
|
"ipu%d_task", id);
|
|
if (IS_ERR(ipu->thread[0])) {
|
|
ret = PTR_ERR(ipu->thread[0]);
|
|
goto kthread0_fail;
|
|
}
|
|
|
|
thread_data[idx].ipu = ipu;
|
|
thread_data[idx].id = 1;
|
|
thread_data[idx].is_vdoa = 0;
|
|
ipu->thread[1] = kthread_run(ipu_task_thread, &thread_data[idx++],
|
|
"ipu%d_task", id);
|
|
if (IS_ERR(ipu->thread[1])) {
|
|
ret = PTR_ERR(ipu->thread[1]);
|
|
goto kthread1_fail;
|
|
}
|
|
|
|
|
|
return ret;
|
|
|
|
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);
|
|
}
|
|
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)
|
|
{
|
|
int i;
|
|
|
|
kthread_stop(ipu->thread[0]);
|
|
kthread_stop(ipu->thread[1]);
|
|
for (i = 0; i < 2; i++) {
|
|
if (ipu->rot_dma[i].vaddr)
|
|
dma_free_coherent(ipu_dev,
|
|
ipu->rot_dma[i].size,
|
|
ipu->rot_dma[i].vaddr,
|
|
ipu->rot_dma[i].paddr);
|
|
}
|
|
|
|
if (major) {
|
|
device_destroy(ipu_class, MKDEV(major, 0));
|
|
class_destroy(ipu_class);
|
|
unregister_chrdev(major, "mxc_ipu");
|
|
major = 0;
|
|
}
|
|
}
|