android_kernel_google_msm/drivers/char/msm_rotator.c
Naseer Ahmed f3ff9ea493 msm: rotator: Add proper checks for enabling Fast YUV
For rotator version 2.0 and above, there is support for fast
YUV which gives better rotator performance. However, there are
few constraints related to height under which fast YUV cannot be
enabled when 90 degree rotation is enabled. Make sure
to add proper checks before enabling fast YUV.

Change-Id: Iafd204a6e4ef09e1151d01d8fde35359b7c3b7a5
Signed-off-by: Padmanabhan Komanduru <pkomandu@codeaurora.org>
Signed-off-by: Naseer Ahmed <naseer@codeaurora.org>
Bug: 19043183
2015-01-27 19:51:26 -05:00

2594 lines
71 KiB
C

/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
#include <linux/android_pmem.h>
#include <linux/msm_rotator.h>
#include <linux/io.h>
#include <mach/msm_rotator_imem.h>
#include <linux/ktime.h>
#include <linux/workqueue.h>
#include <linux/file.h>
#include <linux/major.h>
#include <linux/regulator/consumer.h>
#include <linux/msm_ion.h>
#include <linux/sync.h>
#include <linux/sw_sync.h>
#ifdef CONFIG_MSM_BUS_SCALING
#include <mach/msm_bus.h>
#include <mach/msm_bus_board.h>
#endif
#include <mach/msm_subsystem_map.h>
#include <mach/iommu_domains.h>
#define DRIVER_NAME "msm_rotator"
#define MSM_ROTATOR_BASE (msm_rotator_dev->io_base)
#define MSM_ROTATOR_INTR_ENABLE (MSM_ROTATOR_BASE+0x0020)
#define MSM_ROTATOR_INTR_STATUS (MSM_ROTATOR_BASE+0x0024)
#define MSM_ROTATOR_INTR_CLEAR (MSM_ROTATOR_BASE+0x0028)
#define MSM_ROTATOR_START (MSM_ROTATOR_BASE+0x0030)
#define MSM_ROTATOR_MAX_BURST_SIZE (MSM_ROTATOR_BASE+0x0050)
#define MSM_ROTATOR_HW_VERSION (MSM_ROTATOR_BASE+0x0070)
#define MSM_ROTATOR_SW_RESET (MSM_ROTATOR_BASE+0x0074)
#define MSM_ROTATOR_SRC_SIZE (MSM_ROTATOR_BASE+0x1108)
#define MSM_ROTATOR_SRCP0_ADDR (MSM_ROTATOR_BASE+0x110c)
#define MSM_ROTATOR_SRCP1_ADDR (MSM_ROTATOR_BASE+0x1110)
#define MSM_ROTATOR_SRCP2_ADDR (MSM_ROTATOR_BASE+0x1114)
#define MSM_ROTATOR_SRC_YSTRIDE1 (MSM_ROTATOR_BASE+0x111c)
#define MSM_ROTATOR_SRC_YSTRIDE2 (MSM_ROTATOR_BASE+0x1120)
#define MSM_ROTATOR_SRC_FORMAT (MSM_ROTATOR_BASE+0x1124)
#define MSM_ROTATOR_SRC_UNPACK_PATTERN1 (MSM_ROTATOR_BASE+0x1128)
#define MSM_ROTATOR_SUB_BLOCK_CFG (MSM_ROTATOR_BASE+0x1138)
#define MSM_ROTATOR_OUT_PACK_PATTERN1 (MSM_ROTATOR_BASE+0x1154)
#define MSM_ROTATOR_OUTP0_ADDR (MSM_ROTATOR_BASE+0x1168)
#define MSM_ROTATOR_OUTP1_ADDR (MSM_ROTATOR_BASE+0x116c)
#define MSM_ROTATOR_OUTP2_ADDR (MSM_ROTATOR_BASE+0x1170)
#define MSM_ROTATOR_OUT_YSTRIDE1 (MSM_ROTATOR_BASE+0x1178)
#define MSM_ROTATOR_OUT_YSTRIDE2 (MSM_ROTATOR_BASE+0x117c)
#define MSM_ROTATOR_SRC_XY (MSM_ROTATOR_BASE+0x1200)
#define MSM_ROTATOR_SRC_IMAGE_SIZE (MSM_ROTATOR_BASE+0x1208)
#define MSM_ROTATOR_MAX_ROT 0x07
#define MSM_ROTATOR_MAX_H 0x1fff
#define MSM_ROTATOR_MAX_W 0x1fff
/* from lsb to msb */
#define GET_PACK_PATTERN(a, x, y, z, bit) \
(((a)<<((bit)*3))|((x)<<((bit)*2))|((y)<<(bit))|(z))
#define CLR_G 0x0
#define CLR_B 0x1
#define CLR_R 0x2
#define CLR_ALPHA 0x3
#define CLR_Y CLR_G
#define CLR_CB CLR_B
#define CLR_CR CLR_R
#define ROTATIONS_TO_BITMASK(r) ((((r) & MDP_ROT_90) ? 1 : 0) | \
(((r) & MDP_FLIP_LR) ? 2 : 0) | \
(((r) & MDP_FLIP_UD) ? 4 : 0))
#define IMEM_NO_OWNER -1;
#define MAX_SESSIONS 16
#define INVALID_SESSION -1
#define VERSION_KEY_MASK 0xFFFFFF00
#define MAX_DOWNSCALE_RATIO 3
#define MAX_COMMIT_QUEUE 4
#define WAIT_ROT_TIMEOUT 1000
#define MAX_TIMELINE_NAME_LEN 16
#define WAIT_FENCE_FIRST_TIMEOUT MSEC_PER_SEC
#define WAIT_FENCE_FINAL_TIMEOUT (10 * MSEC_PER_SEC)
#define ROTATOR_REVISION_V0 0
#define ROTATOR_REVISION_V1 1
#define ROTATOR_REVISION_V2 2
#define ROTATOR_REVISION_NONE 0xffffffff
#define BASE_ADDR(height, y_stride) ((height % 64) * y_stride)
#define HW_BASE_ADDR(height, y_stride) (((dstp0_ystride >> 5) << 11) - \
((dst_height & 0x3f) * dstp0_ystride))
uint32_t rotator_hw_revision;
static char rot_iommu_split_domain;
/*
* rotator_hw_revision:
* 0 == 7x30
* 1 == 8x60
* 2 == 8960
*
*/
struct tile_parm {
unsigned int width; /* tile's width */
unsigned int height; /* tile's height */
unsigned int row_tile_w; /* tiles per row's width */
unsigned int row_tile_h; /* tiles per row's height */
};
struct msm_rotator_mem_planes {
unsigned int num_planes;
unsigned int plane_size[4];
unsigned int total_size;
};
#define checkoffset(offset, size, max_size) \
((size) > (max_size) || (offset) > ((max_size) - (size)))
struct msm_rotator_fd_info {
int pid;
int ref_cnt;
struct list_head list;
};
struct rot_sync_info {
u32 initialized;
struct sync_fence *acq_fen;
struct sync_fence *rel_fen;
int rel_fen_fd;
struct sw_sync_timeline *timeline;
int timeline_value;
struct mutex sync_mutex;
atomic_t queue_buf_cnt;
};
struct msm_rotator_session {
struct msm_rotator_img_info img_info;
struct msm_rotator_fd_info fd_info;
int fast_yuv_enable;
};
struct msm_rotator_commit_info {
struct msm_rotator_data_info data_info;
struct msm_rotator_img_info img_info;
unsigned int format;
unsigned int in_paddr;
unsigned int out_paddr;
unsigned int in_chroma_paddr;
unsigned int out_chroma_paddr;
unsigned int in_chroma2_paddr;
unsigned int out_chroma2_paddr;
struct file *srcp0_file;
struct file *srcp1_file;
struct file *dstp0_file;
struct file *dstp1_file;
struct ion_handle *srcp0_ihdl;
struct ion_handle *srcp1_ihdl;
struct ion_handle *dstp0_ihdl;
struct ion_handle *dstp1_ihdl;
int ps0_need;
int session_index;
struct sync_fence *acq_fen;
int fast_yuv_en;
};
struct msm_rotator_dev {
void __iomem *io_base;
int irq;
struct clk *core_clk;
struct msm_rotator_session *rot_session[MAX_SESSIONS];
struct list_head fd_list;
struct clk *pclk;
int rot_clk_state;
struct regulator *regulator;
struct delayed_work rot_clk_work;
struct clk *imem_clk;
int imem_clk_state;
struct delayed_work imem_clk_work;
struct platform_device *pdev;
struct cdev cdev;
struct device *device;
struct class *class;
dev_t dev_num;
int processing;
int last_session_idx;
struct mutex rotator_lock;
struct mutex imem_lock;
int imem_owner;
wait_queue_head_t wq;
struct ion_client *client;
#ifdef CONFIG_MSM_BUS_SCALING
uint32_t bus_client_handle;
#endif
u32 sec_mapped;
u32 mmu_clk_on;
struct rot_sync_info sync_info[MAX_SESSIONS];
/* non blocking */
struct mutex commit_mutex;
struct mutex commit_wq_mutex;
struct completion commit_comp;
u32 commit_running;
struct work_struct commit_work;
struct msm_rotator_commit_info commit_info[MAX_COMMIT_QUEUE];
atomic_t commit_q_r;
atomic_t commit_q_w;
atomic_t commit_q_cnt;
};
#define COMPONENT_5BITS 1
#define COMPONENT_6BITS 2
#define COMPONENT_8BITS 3
static struct msm_rotator_dev *msm_rotator_dev;
#define mrd msm_rotator_dev
static void rot_wait_for_commit_queue(u32 is_all);
enum {
CLK_EN,
CLK_DIS,
CLK_SUSPEND,
};
struct res_mmu_clk {
char *mmu_clk_name;
struct clk *mmu_clk;
};
static struct res_mmu_clk rot_mmu_clks[] = {
{"mdp_iommu_clk"}, {"rot_iommu_clk"},
{"vcodec_iommu0_clk"}, {"vcodec_iommu1_clk"},
{"smmu_iface_clk"}
};
int msm_rotator_iommu_map_buf(int mem_id, int domain,
unsigned long *start, unsigned long *len,
struct ion_handle **pihdl, unsigned int secure)
{
if (!msm_rotator_dev->client)
return -EINVAL;
*pihdl = ion_import_dma_buf(msm_rotator_dev->client, mem_id);
if (IS_ERR_OR_NULL(*pihdl)) {
pr_err("ion_import_dma_buf() failed\n");
return PTR_ERR(*pihdl);
}
pr_debug("%s(): ion_hdl %p, ion_fd %d\n", __func__, *pihdl, mem_id);
if (rot_iommu_split_domain) {
if (secure) {
if (ion_phys(msm_rotator_dev->client,
*pihdl, start, (unsigned *)len)) {
pr_err("%s:%d: ion_phys map failed\n",
__func__, __LINE__);
return -ENOMEM;
}
} else {
if (ion_map_iommu(msm_rotator_dev->client,
*pihdl, domain, GEN_POOL,
SZ_4K, 0, start, len, 0,
ION_IOMMU_UNMAP_DELAYED)) {
pr_err("ion_map_iommu() failed\n");
return -EINVAL;
}
}
} else {
if (ion_map_iommu(msm_rotator_dev->client,
*pihdl, ROTATOR_SRC_DOMAIN, GEN_POOL,
SZ_4K, 0, start, len, 0, ION_IOMMU_UNMAP_DELAYED)) {
pr_err("ion_map_iommu() failed\n");
return -EINVAL;
}
}
pr_debug("%s(): mem_id %d, start 0x%lx, len 0x%lx\n",
__func__, mem_id, *start, *len);
return 0;
}
int msm_rotator_imem_allocate(int requestor)
{
int rc = 0;
#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
switch (requestor) {
case ROTATOR_REQUEST:
if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
msm_rotator_dev->imem_owner = ROTATOR_REQUEST;
rc = 1;
} else
rc = 0;
break;
case JPEG_REQUEST:
mutex_lock(&msm_rotator_dev->imem_lock);
msm_rotator_dev->imem_owner = JPEG_REQUEST;
rc = 1;
break;
default:
rc = 0;
}
#else
if (requestor == JPEG_REQUEST)
rc = 1;
#endif
if (rc == 1) {
cancel_delayed_work_sync(&msm_rotator_dev->imem_clk_work);
if (msm_rotator_dev->imem_clk_state != CLK_EN
&& msm_rotator_dev->imem_clk) {
clk_prepare_enable(msm_rotator_dev->imem_clk);
msm_rotator_dev->imem_clk_state = CLK_EN;
}
}
return rc;
}
EXPORT_SYMBOL(msm_rotator_imem_allocate);
void msm_rotator_imem_free(int requestor)
{
#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
if (msm_rotator_dev->imem_owner == requestor) {
schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
mutex_unlock(&msm_rotator_dev->imem_lock);
}
#else
if (requestor == JPEG_REQUEST)
schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
#endif
}
EXPORT_SYMBOL(msm_rotator_imem_free);
static void msm_rotator_imem_clk_work_f(struct work_struct *work)
{
#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
if (msm_rotator_dev->imem_clk_state == CLK_EN
&& msm_rotator_dev->imem_clk) {
clk_disable_unprepare(msm_rotator_dev->imem_clk);
msm_rotator_dev->imem_clk_state = CLK_DIS;
} else if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND)
msm_rotator_dev->imem_clk_state = CLK_DIS;
mutex_unlock(&msm_rotator_dev->imem_lock);
}
#endif
}
/* enable clocks needed by rotator block */
static void enable_rot_clks(void)
{
if (msm_rotator_dev->regulator)
regulator_enable(msm_rotator_dev->regulator);
if (msm_rotator_dev->core_clk != NULL)
clk_prepare_enable(msm_rotator_dev->core_clk);
if (msm_rotator_dev->pclk != NULL)
clk_prepare_enable(msm_rotator_dev->pclk);
}
/* disable clocks needed by rotator block */
static void disable_rot_clks(void)
{
if (msm_rotator_dev->core_clk != NULL)
clk_disable_unprepare(msm_rotator_dev->core_clk);
if (msm_rotator_dev->pclk != NULL)
clk_disable_unprepare(msm_rotator_dev->pclk);
if (msm_rotator_dev->regulator)
regulator_disable(msm_rotator_dev->regulator);
}
static void msm_rotator_rot_clk_work_f(struct work_struct *work)
{
if (mutex_trylock(&msm_rotator_dev->rotator_lock)) {
if (msm_rotator_dev->rot_clk_state == CLK_EN) {
disable_rot_clks();
msm_rotator_dev->rot_clk_state = CLK_DIS;
} else if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND)
msm_rotator_dev->rot_clk_state = CLK_DIS;
mutex_unlock(&msm_rotator_dev->rotator_lock);
}
}
static irqreturn_t msm_rotator_isr(int irq, void *dev_id)
{
if (msm_rotator_dev->processing) {
msm_rotator_dev->processing = 0;
wake_up(&msm_rotator_dev->wq);
} else
printk(KERN_WARNING "%s: unexpected interrupt\n", DRIVER_NAME);
return IRQ_HANDLED;
}
static void msm_rotator_signal_timeline(u32 session_index)
{
struct rot_sync_info *sync_info;
sync_info = &msm_rotator_dev->sync_info[session_index];
if ((!sync_info->timeline) || (!sync_info->initialized))
return;
mutex_lock(&sync_info->sync_mutex);
sw_sync_timeline_inc(sync_info->timeline, 1);
sync_info->timeline_value++;
mutex_unlock(&sync_info->sync_mutex);
}
static void msm_rotator_signal_timeline_done(u32 session_index)
{
struct rot_sync_info *sync_info;
sync_info = &msm_rotator_dev->sync_info[session_index];
if ((sync_info->timeline == NULL) ||
(sync_info->initialized == false))
return;
mutex_lock(&sync_info->sync_mutex);
sw_sync_timeline_inc(sync_info->timeline, 1);
sync_info->timeline_value++;
if (atomic_read(&sync_info->queue_buf_cnt) <= 0)
pr_err("%s queue_buf_cnt=%d", __func__,
atomic_read(&sync_info->queue_buf_cnt));
else
atomic_dec(&sync_info->queue_buf_cnt);
mutex_unlock(&sync_info->sync_mutex);
}
static void msm_rotator_release_acq_fence(u32 session_index)
{
struct rot_sync_info *sync_info;
sync_info = &msm_rotator_dev->sync_info[session_index];
if ((!sync_info->timeline) || (!sync_info->initialized))
return;
mutex_lock(&sync_info->sync_mutex);
sync_info->acq_fen = NULL;
mutex_unlock(&sync_info->sync_mutex);
}
static void msm_rotator_release_all_timeline(void)
{
int i;
struct rot_sync_info *sync_info;
for (i = 0; i < MAX_SESSIONS; i++) {
sync_info = &msm_rotator_dev->sync_info[i];
if (sync_info->initialized) {
msm_rotator_signal_timeline(i);
msm_rotator_release_acq_fence(i);
}
}
}
static void msm_rotator_wait_for_fence(struct sync_fence *acq_fen)
{
int ret;
if (acq_fen) {
ret = sync_fence_wait(acq_fen,
WAIT_FENCE_FIRST_TIMEOUT);
if (ret == -ETIME) {
pr_warn("%s: timeout, wait %ld more ms\n",
__func__, WAIT_FENCE_FINAL_TIMEOUT);
ret = sync_fence_wait(acq_fen,
WAIT_FENCE_FINAL_TIMEOUT);
}
if (ret < 0) {
pr_err("%s: sync_fence_wait failed! ret = %x\n",
__func__, ret);
}
sync_fence_put(acq_fen);
}
}
static int msm_rotator_buf_sync(unsigned long arg)
{
struct msm_rotator_buf_sync buf_sync;
int ret = 0;
struct sync_fence *fence = NULL;
struct rot_sync_info *sync_info;
struct sync_pt *rel_sync_pt;
struct sync_fence *rel_fence;
int rel_fen_fd;
u32 s;
if (copy_from_user(&buf_sync, (void __user *)arg, sizeof(buf_sync)))
return -EFAULT;
rot_wait_for_commit_queue(false);
for (s = 0; s < MAX_SESSIONS; s++)
if ((msm_rotator_dev->rot_session[s] != NULL) &&
(buf_sync.session_id ==
(unsigned int)msm_rotator_dev->rot_session[s]
))
break;
if (s == MAX_SESSIONS) {
pr_err("%s invalid session id %d", __func__,
buf_sync.session_id);
return -EINVAL;
}
sync_info = &msm_rotator_dev->sync_info[s];
if (sync_info->acq_fen)
pr_err("%s previous acq_fen will be overwritten", __func__);
if ((sync_info->timeline == NULL) ||
(sync_info->initialized == false))
return -EINVAL;
mutex_lock(&sync_info->sync_mutex);
if (buf_sync.acq_fen_fd >= 0)
fence = sync_fence_fdget(buf_sync.acq_fen_fd);
sync_info->acq_fen = fence;
if (sync_info->acq_fen &&
(buf_sync.flags & MDP_BUF_SYNC_FLAG_WAIT)) {
msm_rotator_wait_for_fence(sync_info->acq_fen);
sync_info->acq_fen = NULL;
}
rel_sync_pt = sw_sync_pt_create(sync_info->timeline,
sync_info->timeline_value +
atomic_read(&sync_info->queue_buf_cnt) + 1);
if (rel_sync_pt == NULL) {
pr_err("%s: cannot create sync point", __func__);
ret = -ENOMEM;
goto buf_sync_err_1;
}
/* create fence */
rel_fence = sync_fence_create("msm_rotator-fence",
rel_sync_pt);
if (rel_fence == NULL) {
sync_pt_free(rel_sync_pt);
pr_err("%s: cannot create fence", __func__);
ret = -ENOMEM;
goto buf_sync_err_1;
}
/* create fd */
rel_fen_fd = get_unused_fd_flags(0);
if (rel_fen_fd < 0) {
pr_err("%s: get_unused_fd_flags failed", __func__);
ret = -EIO;
goto buf_sync_err_2;
}
sync_fence_install(rel_fence, rel_fen_fd);
buf_sync.rel_fen_fd = rel_fen_fd;
sync_info->rel_fen = rel_fence;
sync_info->rel_fen_fd = rel_fen_fd;
ret = copy_to_user((void __user *)arg, &buf_sync, sizeof(buf_sync));
mutex_unlock(&sync_info->sync_mutex);
return ret;
buf_sync_err_2:
sync_fence_put(rel_fence);
buf_sync_err_1:
if (sync_info->acq_fen)
sync_fence_put(sync_info->acq_fen);
sync_info->acq_fen = NULL;
mutex_unlock(&sync_info->sync_mutex);
return ret;
}
static unsigned int tile_size(unsigned int src_width,
unsigned int src_height,
const struct tile_parm *tp)
{
unsigned int tile_w, tile_h;
unsigned int row_num_w, row_num_h;
tile_w = tp->width * tp->row_tile_w;
tile_h = tp->height * tp->row_tile_h;
row_num_w = (src_width + tile_w - 1) / tile_w;
row_num_h = (src_height + tile_h - 1) / tile_h;
return ((row_num_w * row_num_h * tile_w * tile_h) + 8191) & ~8191;
}
static int get_bpp(int format)
{
switch (format) {
case MDP_RGB_565:
case MDP_BGR_565:
return 2;
case MDP_XRGB_8888:
case MDP_ARGB_8888:
case MDP_RGBA_8888:
case MDP_BGRA_8888:
case MDP_RGBX_8888:
return 4;
case MDP_Y_CBCR_H2V2:
case MDP_Y_CRCB_H2V2:
case MDP_Y_CB_CR_H2V2:
case MDP_Y_CR_CB_H2V2:
case MDP_Y_CR_CB_GH2V2:
case MDP_Y_CRCB_H2V2_TILE:
case MDP_Y_CBCR_H2V2_TILE:
return 1;
case MDP_RGB_888:
case MDP_YCBCR_H1V1:
case MDP_YCRCB_H1V1:
return 3;
case MDP_YCRYCB_H2V1:
return 2;/* YCrYCb interleave */
case MDP_Y_CRCB_H2V1:
case MDP_Y_CBCR_H2V1:
return 1;
default:
return -1;
}
}
static int msm_rotator_get_plane_sizes(uint32_t format, uint32_t w, uint32_t h,
struct msm_rotator_mem_planes *p)
{
/*
* each row of samsung tile consists of two tiles in height
* and two tiles in width which means width should align to
* 64 x 2 bytes and height should align to 32 x 2 bytes.
* video decoder generate two tiles in width and one tile
* in height which ends up height align to 32 X 1 bytes.
*/
const struct tile_parm tile = {64, 32, 2, 1};
int i;
if (p == NULL)
return -EINVAL;
if ((w > MSM_ROTATOR_MAX_W) || (h > MSM_ROTATOR_MAX_H))
return -ERANGE;
memset(p, 0, sizeof(*p));
switch (format) {
case MDP_XRGB_8888:
case MDP_ARGB_8888:
case MDP_RGBA_8888:
case MDP_BGRA_8888:
case MDP_RGBX_8888:
case MDP_RGB_888:
case MDP_RGB_565:
case MDP_BGR_565:
case MDP_YCRYCB_H2V1:
case MDP_YCBCR_H1V1:
case MDP_YCRCB_H1V1:
p->num_planes = 1;
p->plane_size[0] = w * h * get_bpp(format);
break;
case MDP_Y_CRCB_H2V1:
case MDP_Y_CBCR_H2V1:
case MDP_Y_CRCB_H1V2:
p->num_planes = 2;
p->plane_size[0] = w * h;
p->plane_size[1] = w * h;
break;
case MDP_Y_CBCR_H2V2:
case MDP_Y_CRCB_H2V2:
p->num_planes = 2;
p->plane_size[0] = w * h;
p->plane_size[1] = w * h / 2;
break;
case MDP_Y_CRCB_H2V2_TILE:
case MDP_Y_CBCR_H2V2_TILE:
p->num_planes = 2;
p->plane_size[0] = tile_size(w, h, &tile);
p->plane_size[1] = tile_size(w, h/2, &tile);
break;
case MDP_Y_CB_CR_H2V2:
case MDP_Y_CR_CB_H2V2:
p->num_planes = 3;
p->plane_size[0] = w * h;
p->plane_size[1] = (w / 2) * (h / 2);
p->plane_size[2] = (w / 2) * (h / 2);
break;
case MDP_Y_CR_CB_GH2V2:
p->num_planes = 3;
p->plane_size[0] = ALIGN(w, 16) * h;
p->plane_size[1] = ALIGN(w / 2, 16) * (h / 2);
p->plane_size[2] = ALIGN(w / 2, 16) * (h / 2);
break;
default:
return -EINVAL;
}
for (i = 0; i < p->num_planes; i++)
p->total_size += p->plane_size[i];
return 0;
}
/* Checking invalid destination image size on FAST YUV for YUV420PP(NV12) with
* HW issue for rotation 90 + U/D filp + with/without flip operation
* (rotation 90 + U/D + L/R flip is rotation 270 degree option) and pix_rot
* block issue with tile line size is 4.
*
* Rotator structure is:
* if Fetch input image: W x H,
* Downscale: W` x H` = W/ScaleHor(2, 4 or 8) x H/ScaleVert(2, 4 or 8)
* Rotated output : W`` x H`` = (W` x H`) or (H` x W`) depends on "Rotation 90
* degree option"
*
* Pack: W`` x H``
*
* Rotator source ROI image width restriction is applied to W x H (case a,
* image resolution before downscaling)
*
* Packer source Image width/ height restriction are applied to W`` x H``
* (case c, image resolution after rotation)
*
* Supertile (64 x 8) and YUV (2 x 2) alignment restriction should be
* applied to the W x H (case a). Input image should be at least (2 x 2).
*
* "Support If packer source image height <= 256, multiple of 8", this
* restriction should be applied to the rotated image (W`` x H``)
*/
uint32_t fast_yuv_invalid_size_checker(unsigned char rot_mode,
uint32_t src_width,
uint32_t dst_width,
uint32_t src_height,
uint32_t dst_height,
uint32_t dstp0_ystride,
uint32_t is_planar420)
{
uint32_t hw_limit;
hw_limit = is_planar420 ? 512 : 256;
/* checking image constaints for missing EOT event from pix_rot block */
if ((src_width > hw_limit) && ((src_width % (hw_limit / 2)) == 8))
return -EINVAL;
if (rot_mode & MDP_ROT_90) {
if ((src_height % 128) == 8)
return -EINVAL;
/* if rotation 90 degree on fast yuv
* rotator image input width has to be multiple of 8
* rotator image input height has to be multiple of 8
*/
if (((dst_width % 8) != 0) || ((dst_height % 8) != 0))
return -EINVAL;
if ((rot_mode & MDP_FLIP_UD) ||
(rot_mode & (MDP_FLIP_UD | MDP_FLIP_LR))) {
/* image constraint checking for wrong address
* generation HW issue for Y plane checking
*/
if (((dst_height % 64) != 0) &&
((dst_height / 64) >= 4)) {
/* compare golden logic for second
* tile base address generation in row
* with actual HW implementation
*/
if (BASE_ADDR(dst_height, dstp0_ystride) !=
HW_BASE_ADDR(dst_height, dstp0_ystride))
return -EINVAL;
}
if (is_planar420) {
dst_width = dst_width / 2;
dstp0_ystride = dstp0_ystride / 2;
}
dst_height = dst_height / 2;
/* image constraint checking for wrong
* address generation HW issue. for
* U/V (P) or UV (PP) plane checking
*/
if (((dst_height % 64) != 0) && ((dst_height / 64) >=
(hw_limit / 128))) {
/* compare golden logic for
* second tile base address
* generation in row with
* actual HW implementation
*/
if (BASE_ADDR(dst_height, dstp0_ystride) !=
HW_BASE_ADDR(dst_height, dstp0_ystride))
return -EINVAL;
}
}
} else {
/* if NOT applying rotation 90 degree on fast yuv,
* rotator image input width has to be multiple of 8
* rotator image input height has to be multiple of 8
*/
if (((dst_width % 8) != 0) || ((dst_height % 8) != 0))
return -EINVAL;
}
return 0;
}
static int msm_rotator_ycxcx_h2v1(struct msm_rotator_img_info *info,
unsigned int in_paddr,
unsigned int out_paddr,
unsigned int use_imem,
int new_session,
unsigned int in_chroma_paddr,
unsigned int out_chroma_paddr)
{
int bpp;
if (info->src.format != info->dst.format)
return -EINVAL;
bpp = get_bpp(info->src.format);
if (bpp < 0)
return -ENOTTY;
iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
iowrite32(in_chroma_paddr, MSM_ROTATOR_SRCP1_ADDR);
iowrite32(out_paddr +
((info->dst_y * info->dst.width) + info->dst_x),
MSM_ROTATOR_OUTP0_ADDR);
iowrite32(out_chroma_paddr +
((info->dst_y * info->dst.width) + info->dst_x),
MSM_ROTATOR_OUTP1_ADDR);
if (new_session) {
iowrite32(info->src.width |
info->src.width << 16,
MSM_ROTATOR_SRC_YSTRIDE1);
if (info->rotations & MDP_ROT_90)
iowrite32(info->dst.width |
info->dst.width*2 << 16,
MSM_ROTATOR_OUT_YSTRIDE1);
else
iowrite32(info->dst.width |
info->dst.width << 16,
MSM_ROTATOR_OUT_YSTRIDE1);
if (info->src.format == MDP_Y_CBCR_H2V1) {
iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
} else {
iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
}
iowrite32((1 << 18) | /* chroma sampling 1=H2V1 */
(ROTATIONS_TO_BITMASK(info->rotations) << 9) |
1 << 8 | /* ROT_EN */
info->downscale_ratio << 2 | /* downscale v ratio */
info->downscale_ratio, /* downscale h ratio */
MSM_ROTATOR_SUB_BLOCK_CFG);
iowrite32(0 << 29 | /* frame format 0 = linear */
(use_imem ? 0 : 1) << 22 | /* tile size */
2 << 19 | /* fetch planes 2 = pseudo */
0 << 18 | /* unpack align */
1 << 17 | /* unpack tight */
1 << 13 | /* unpack count 0=1 component */
(bpp-1) << 9 | /* src Bpp 0=1 byte ... */
0 << 8 | /* has alpha */
0 << 6 | /* alpha bits 3=8bits */
3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
3 << 0, /* G/Y bits 1=5 2=6 3=8 */
MSM_ROTATOR_SRC_FORMAT);
}
return 0;
}
static int msm_rotator_ycxcx_h2v2(struct msm_rotator_img_info *info,
unsigned int in_paddr,
unsigned int out_paddr,
unsigned int use_imem,
int new_session,
unsigned int in_chroma_paddr,
unsigned int out_chroma_paddr,
unsigned int in_chroma2_paddr,
unsigned int out_chroma2_paddr,
int fast_yuv_en)
{
uint32_t dst_format;
int is_tile = 0;
switch (info->src.format) {
case MDP_Y_CRCB_H2V2_TILE:
is_tile = 1;
dst_format = MDP_Y_CRCB_H2V2;
break;
case MDP_Y_CR_CB_H2V2:
case MDP_Y_CR_CB_GH2V2:
if (fast_yuv_en) {
dst_format = info->src.format;
break;
}
case MDP_Y_CRCB_H2V2:
dst_format = MDP_Y_CRCB_H2V2;
break;
case MDP_Y_CB_CR_H2V2:
if (fast_yuv_en) {
dst_format = info->src.format;
break;
}
dst_format = MDP_Y_CBCR_H2V2;
break;
case MDP_Y_CBCR_H2V2_TILE:
is_tile = 1;
case MDP_Y_CBCR_H2V2:
dst_format = MDP_Y_CBCR_H2V2;
break;
default:
return -EINVAL;
}
if (info->dst.format != dst_format)
return -EINVAL;
/* rotator expects YCbCr for planar input format */
if ((info->src.format == MDP_Y_CR_CB_H2V2 ||
info->src.format == MDP_Y_CR_CB_GH2V2) &&
rotator_hw_revision < ROTATOR_REVISION_V2)
swap(in_chroma_paddr, in_chroma2_paddr);
iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
iowrite32(in_chroma_paddr, MSM_ROTATOR_SRCP1_ADDR);
iowrite32(in_chroma2_paddr, MSM_ROTATOR_SRCP2_ADDR);
iowrite32(out_paddr +
((info->dst_y * info->dst.width) + info->dst_x),
MSM_ROTATOR_OUTP0_ADDR);
iowrite32(out_chroma_paddr +
(((info->dst_y * info->dst.width)/2) + info->dst_x),
MSM_ROTATOR_OUTP1_ADDR);
if (out_chroma2_paddr)
iowrite32(out_chroma2_paddr +
(((info->dst_y * info->dst.width)/2) + info->dst_x),
MSM_ROTATOR_OUTP2_ADDR);
if (new_session) {
if (in_chroma2_paddr) {
if (info->src.format == MDP_Y_CR_CB_GH2V2) {
iowrite32(ALIGN(info->src.width, 16) |
ALIGN((info->src.width / 2), 16) << 16,
MSM_ROTATOR_SRC_YSTRIDE1);
iowrite32(ALIGN((info->src.width / 2), 16),
MSM_ROTATOR_SRC_YSTRIDE2);
} else {
iowrite32(info->src.width |
(info->src.width / 2) << 16,
MSM_ROTATOR_SRC_YSTRIDE1);
iowrite32((info->src.width / 2),
MSM_ROTATOR_SRC_YSTRIDE2);
}
} else {
iowrite32(info->src.width |
info->src.width << 16,
MSM_ROTATOR_SRC_YSTRIDE1);
}
if (out_chroma2_paddr) {
if (info->dst.format == MDP_Y_CR_CB_GH2V2) {
iowrite32(ALIGN(info->dst.width, 16) |
ALIGN((info->dst.width / 2), 16) << 16,
MSM_ROTATOR_OUT_YSTRIDE1);
iowrite32(ALIGN((info->dst.width / 2), 16),
MSM_ROTATOR_OUT_YSTRIDE2);
} else {
iowrite32(info->dst.width |
info->dst.width/2 << 16,
MSM_ROTATOR_OUT_YSTRIDE1);
iowrite32(info->dst.width/2,
MSM_ROTATOR_OUT_YSTRIDE2);
}
} else {
iowrite32(info->dst.width |
info->dst.width << 16,
MSM_ROTATOR_OUT_YSTRIDE1);
}
if (dst_format == MDP_Y_CBCR_H2V2 ||
dst_format == MDP_Y_CB_CR_H2V2) {
iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
} else {
iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
}
iowrite32((3 << 18) | /* chroma sampling 3=4:2:0 */
(ROTATIONS_TO_BITMASK(info->rotations) << 9) |
1 << 8 | /* ROT_EN */
fast_yuv_en << 4 | /*fast YUV*/
info->downscale_ratio << 2 | /* downscale v ratio */
info->downscale_ratio, /* downscale h ratio */
MSM_ROTATOR_SUB_BLOCK_CFG);
iowrite32((is_tile ? 2 : 0) << 29 | /* frame format */
(use_imem ? 0 : 1) << 22 | /* tile size */
(in_chroma2_paddr ? 1 : 2) << 19 | /* fetch planes */
0 << 18 | /* unpack align */
1 << 17 | /* unpack tight */
1 << 13 | /* unpack count 0=1 component */
0 << 9 | /* src Bpp 0=1 byte ... */
0 << 8 | /* has alpha */
0 << 6 | /* alpha bits 3=8bits */
3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
3 << 0, /* G/Y bits 1=5 2=6 3=8 */
MSM_ROTATOR_SRC_FORMAT);
}
return 0;
}
static int msm_rotator_ycrycb(struct msm_rotator_img_info *info,
unsigned int in_paddr,
unsigned int out_paddr,
unsigned int use_imem,
int new_session,
unsigned int out_chroma_paddr)
{
int bpp;
uint32_t dst_format;
if (info->src.format == MDP_YCRYCB_H2V1) {
if (info->rotations & MDP_ROT_90)
dst_format = MDP_Y_CRCB_H1V2;
else
dst_format = MDP_Y_CRCB_H2V1;
} else
return -EINVAL;
if (info->dst.format != dst_format)
return -EINVAL;
bpp = get_bpp(info->src.format);
if (bpp < 0)
return -ENOTTY;
iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
iowrite32(out_paddr +
((info->dst_y * info->dst.width) + info->dst_x),
MSM_ROTATOR_OUTP0_ADDR);
iowrite32(out_chroma_paddr +
((info->dst_y * info->dst.width)/2 + info->dst_x),
MSM_ROTATOR_OUTP1_ADDR);
if (new_session) {
iowrite32(info->src.width * bpp,
MSM_ROTATOR_SRC_YSTRIDE1);
if (info->rotations & MDP_ROT_90)
iowrite32(info->dst.width |
(info->dst.width*2) << 16,
MSM_ROTATOR_OUT_YSTRIDE1);
else
iowrite32(info->dst.width |
(info->dst.width) << 16,
MSM_ROTATOR_OUT_YSTRIDE1);
iowrite32(GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
iowrite32((1 << 18) | /* chroma sampling 1=H2V1 */
(ROTATIONS_TO_BITMASK(info->rotations) << 9) |
1 << 8 | /* ROT_EN */
info->downscale_ratio << 2 | /* downscale v ratio */
info->downscale_ratio, /* downscale h ratio */
MSM_ROTATOR_SUB_BLOCK_CFG);
iowrite32(0 << 29 | /* frame format 0 = linear */
(use_imem ? 0 : 1) << 22 | /* tile size */
0 << 19 | /* fetch planes 0=interleaved */
0 << 18 | /* unpack align */
1 << 17 | /* unpack tight */
3 << 13 | /* unpack count 0=1 component */
(bpp-1) << 9 | /* src Bpp 0=1 byte ... */
0 << 8 | /* has alpha */
0 << 6 | /* alpha bits 3=8bits */
3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
3 << 0, /* G/Y bits 1=5 2=6 3=8 */
MSM_ROTATOR_SRC_FORMAT);
}
return 0;
}
static int msm_rotator_rgb_types(struct msm_rotator_img_info *info,
unsigned int in_paddr,
unsigned int out_paddr,
unsigned int use_imem,
int new_session)
{
int bpp, abits, rbits, gbits, bbits;
if (info->src.format != info->dst.format)
return -EINVAL;
bpp = get_bpp(info->src.format);
if (bpp < 0)
return -ENOTTY;
iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
iowrite32(out_paddr +
((info->dst_y * info->dst.width) + info->dst_x) * bpp,
MSM_ROTATOR_OUTP0_ADDR);
if (new_session) {
iowrite32(info->src.width * bpp, MSM_ROTATOR_SRC_YSTRIDE1);
iowrite32(info->dst.width * bpp, MSM_ROTATOR_OUT_YSTRIDE1);
iowrite32((0 << 18) | /* chroma sampling 0=rgb */
(ROTATIONS_TO_BITMASK(info->rotations) << 9) |
1 << 8 | /* ROT_EN */
info->downscale_ratio << 2 | /* downscale v ratio */
info->downscale_ratio, /* downscale h ratio */
MSM_ROTATOR_SUB_BLOCK_CFG);
switch (info->src.format) {
case MDP_RGB_565:
iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
abits = 0;
rbits = COMPONENT_5BITS;
gbits = COMPONENT_6BITS;
bbits = COMPONENT_5BITS;
break;
case MDP_BGR_565:
iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
abits = 0;
rbits = COMPONENT_5BITS;
gbits = COMPONENT_6BITS;
bbits = COMPONENT_5BITS;
break;
case MDP_RGB_888:
case MDP_YCBCR_H1V1:
case MDP_YCRCB_H1V1:
iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
abits = 0;
rbits = COMPONENT_8BITS;
gbits = COMPONENT_8BITS;
bbits = COMPONENT_8BITS;
break;
case MDP_ARGB_8888:
case MDP_RGBA_8888:
case MDP_XRGB_8888:
case MDP_RGBX_8888:
iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
CLR_B, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
CLR_B, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
abits = COMPONENT_8BITS;
rbits = COMPONENT_8BITS;
gbits = COMPONENT_8BITS;
bbits = COMPONENT_8BITS;
break;
case MDP_BGRA_8888:
iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
CLR_R, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
CLR_R, 8),
MSM_ROTATOR_OUT_PACK_PATTERN1);
abits = COMPONENT_8BITS;
rbits = COMPONENT_8BITS;
gbits = COMPONENT_8BITS;
bbits = COMPONENT_8BITS;
break;
default:
return -EINVAL;
}
iowrite32(0 << 29 | /* frame format 0 = linear */
(use_imem ? 0 : 1) << 22 | /* tile size */
0 << 19 | /* fetch planes 0=interleaved */
0 << 18 | /* unpack align */
1 << 17 | /* unpack tight */
(abits ? 3 : 2) << 13 | /* unpack count 0=1 comp */
(bpp-1) << 9 | /* src Bpp 0=1 byte ... */
(abits ? 1 : 0) << 8 | /* has alpha */
abits << 6 | /* alpha bits 3=8bits */
rbits << 4 | /* R/Cr bits 1=5 2=6 3=8 */
bbits << 2 | /* B/Cb bits 1=5 2=6 3=8 */
gbits << 0, /* G/Y bits 1=5 2=6 3=8 */
MSM_ROTATOR_SRC_FORMAT);
}
return 0;
}
static int get_img(struct msmfb_data *fbd, int domain,
unsigned long *start, unsigned long *len, struct file **p_file,
int *p_need, struct ion_handle **p_ihdl, unsigned int secure)
{
int ret = 0;
#ifdef CONFIG_FB
struct file *file = NULL;
int put_needed, fb_num;
#endif
#ifdef CONFIG_ANDROID_PMEM
unsigned long vstart;
#endif
*p_need = 0;
#ifdef CONFIG_FB
if (fbd->flags & MDP_MEMORY_ID_TYPE_FB) {
file = fget_light(fbd->memory_id, &put_needed);
if (file == NULL) {
pr_err("fget_light returned NULL\n");
return -EINVAL;
}
if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
if (get_fb_phys_info(start, len, fb_num,
ROTATOR_SUBSYSTEM_ID)) {
pr_err("get_fb_phys_info() failed\n");
ret = -1;
} else {
*p_file = file;
*p_need = put_needed;
}
} else {
pr_err("invalid FB_MAJOR failed\n");
ret = -1;
}
if (ret)
fput_light(file, put_needed);
return ret;
}
#endif
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
return msm_rotator_iommu_map_buf(fbd->memory_id, domain, start,
len, p_ihdl, secure);
#endif
#ifdef CONFIG_ANDROID_PMEM
if (!get_pmem_file(fbd->memory_id, start, &vstart, len, p_file))
return 0;
else
return -ENOMEM;
#endif
}
static void put_img(struct file *p_file, struct ion_handle *p_ihdl,
int domain, unsigned int secure)
{
#ifdef CONFIG_ANDROID_PMEM
if (p_file != NULL)
put_pmem_file(p_file);
#endif
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
if (!IS_ERR_OR_NULL(p_ihdl)) {
pr_debug("%s(): p_ihdl %p\n", __func__, p_ihdl);
if (rot_iommu_split_domain) {
if (!secure)
ion_unmap_iommu(msm_rotator_dev->client,
p_ihdl, domain, GEN_POOL);
} else {
ion_unmap_iommu(msm_rotator_dev->client,
p_ihdl, ROTATOR_SRC_DOMAIN, GEN_POOL);
}
ion_free(msm_rotator_dev->client, p_ihdl);
}
#endif
}
static int msm_rotator_rotate_prepare(
struct msm_rotator_data_info *data_info,
struct msm_rotator_commit_info *commit_info)
{
unsigned int format;
struct msm_rotator_data_info info;
unsigned int in_paddr, out_paddr;
unsigned long src_len, dst_len;
int rc = 0, s;
struct file *srcp0_file = NULL, *dstp0_file = NULL;
struct file *srcp1_file = NULL, *dstp1_file = NULL;
struct ion_handle *srcp0_ihdl = NULL, *dstp0_ihdl = NULL;
struct ion_handle *srcp1_ihdl = NULL, *dstp1_ihdl = NULL;
int ps0_need, p_need;
unsigned int in_chroma_paddr = 0, out_chroma_paddr = 0;
unsigned int in_chroma2_paddr = 0, out_chroma2_paddr = 0;
struct msm_rotator_img_info *img_info;
struct msm_rotator_mem_planes src_planes, dst_planes;
mutex_lock(&msm_rotator_dev->rotator_lock);
info = *data_info;
for (s = 0; s < MAX_SESSIONS; s++)
if ((msm_rotator_dev->rot_session[s] != NULL) &&
(info.session_id ==
(unsigned int)msm_rotator_dev->rot_session[s]
))
break;
if (s == MAX_SESSIONS) {
pr_err("%s() : Attempt to use invalid session_id %d\n",
__func__, s);
rc = -EINVAL;
mutex_unlock(&msm_rotator_dev->rotator_lock);
return rc;
}
img_info = &(msm_rotator_dev->rot_session[s]->img_info);
if (img_info->enable == 0) {
dev_dbg(msm_rotator_dev->device,
"%s() : Session_id %d not enabled\n", __func__, s);
rc = -EINVAL;
mutex_unlock(&msm_rotator_dev->rotator_lock);
return rc;
}
if (msm_rotator_get_plane_sizes(img_info->src.format,
img_info->src.width,
img_info->src.height,
&src_planes)) {
pr_err("%s: invalid src format\n", __func__);
rc = -EINVAL;
mutex_unlock(&msm_rotator_dev->rotator_lock);
return rc;
}
if (msm_rotator_get_plane_sizes(img_info->dst.format,
img_info->dst.width,
img_info->dst.height,
&dst_planes)) {
pr_err("%s: invalid dst format\n", __func__);
rc = -EINVAL;
mutex_unlock(&msm_rotator_dev->rotator_lock);
return rc;
}
rc = get_img(&info.src, ROTATOR_SRC_DOMAIN, (unsigned long *)&in_paddr,
(unsigned long *)&src_len, &srcp0_file, &ps0_need,
&srcp0_ihdl, 0);
if (rc) {
pr_err("%s: in get_img() failed id=0x%08x\n",
DRIVER_NAME, info.src.memory_id);
goto rotate_prepare_error;
}
rc = get_img(&info.dst, ROTATOR_DST_DOMAIN, (unsigned long *)&out_paddr,
(unsigned long *)&dst_len, &dstp0_file, &p_need,
&dstp0_ihdl, img_info->secure);
if (rc) {
pr_err("%s: out get_img() failed id=0x%08x\n",
DRIVER_NAME, info.dst.memory_id);
goto rotate_prepare_error;
}
format = img_info->src.format;
if (((info.version_key & VERSION_KEY_MASK) == 0xA5B4C300) &&
((info.version_key & ~VERSION_KEY_MASK) > 0) &&
(src_planes.num_planes == 2)) {
if (checkoffset(info.src.offset,
src_planes.plane_size[0],
src_len)) {
pr_err("%s: invalid src buffer (len=%lu offset=%x)\n",
__func__, src_len, info.src.offset);
rc = -ERANGE;
goto rotate_prepare_error;
}
if (checkoffset(info.dst.offset,
dst_planes.plane_size[0],
dst_len)) {
pr_err("%s: invalid dst buffer (len=%lu offset=%x)\n",
__func__, dst_len, info.dst.offset);
rc = -ERANGE;
goto rotate_prepare_error;
}
rc = get_img(&info.src_chroma, ROTATOR_SRC_DOMAIN,
(unsigned long *)&in_chroma_paddr,
(unsigned long *)&src_len, &srcp1_file, &p_need,
&srcp1_ihdl, 0);
if (rc) {
pr_err("%s: in chroma get_img() failed id=0x%08x\n",
DRIVER_NAME, info.src_chroma.memory_id);
goto rotate_prepare_error;
}
rc = get_img(&info.dst_chroma, ROTATOR_DST_DOMAIN,
(unsigned long *)&out_chroma_paddr,
(unsigned long *)&dst_len, &dstp1_file, &p_need,
&dstp1_ihdl, img_info->secure);
if (rc) {
pr_err("%s: out chroma get_img() failed id=0x%08x\n",
DRIVER_NAME, info.dst_chroma.memory_id);
goto rotate_prepare_error;
}
if (checkoffset(info.src_chroma.offset,
src_planes.plane_size[1],
src_len)) {
pr_err("%s: invalid chr src buf len=%lu offset=%x\n",
__func__, src_len, info.src_chroma.offset);
rc = -ERANGE;
goto rotate_prepare_error;
}
if (checkoffset(info.dst_chroma.offset,
src_planes.plane_size[1],
dst_len)) {
pr_err("%s: invalid chr dst buf len=%lu offset=%x\n",
__func__, dst_len, info.dst_chroma.offset);
rc = -ERANGE;
goto rotate_prepare_error;
}
in_chroma_paddr += info.src_chroma.offset;
out_chroma_paddr += info.dst_chroma.offset;
} else {
if (checkoffset(info.src.offset,
src_planes.total_size,
src_len)) {
pr_err("%s: invalid src buffer (len=%lu offset=%x)\n",
__func__, src_len, info.src.offset);
rc = -ERANGE;
goto rotate_prepare_error;
}
if (checkoffset(info.dst.offset,
dst_planes.total_size,
dst_len)) {
pr_err("%s: invalid dst buffer (len=%lu offset=%x)\n",
__func__, dst_len, info.dst.offset);
rc = -ERANGE;
goto rotate_prepare_error;
}
}
in_paddr += info.src.offset;
out_paddr += info.dst.offset;
if (!in_chroma_paddr && src_planes.num_planes >= 2)
in_chroma_paddr = in_paddr + src_planes.plane_size[0];
if (!out_chroma_paddr && dst_planes.num_planes >= 2)
out_chroma_paddr = out_paddr + dst_planes.plane_size[0];
if (src_planes.num_planes >= 3)
in_chroma2_paddr = in_chroma_paddr + src_planes.plane_size[1];
if (dst_planes.num_planes >= 3)
out_chroma2_paddr = out_chroma_paddr + dst_planes.plane_size[1];
commit_info->data_info = info;
commit_info->img_info = *img_info;
commit_info->format = format;
commit_info->in_paddr = in_paddr;
commit_info->out_paddr = out_paddr;
commit_info->in_chroma_paddr = in_chroma_paddr;
commit_info->out_chroma_paddr = out_chroma_paddr;
commit_info->in_chroma2_paddr = in_chroma2_paddr;
commit_info->out_chroma2_paddr = out_chroma2_paddr;
commit_info->srcp0_file = srcp0_file;
commit_info->srcp1_file = srcp1_file;
commit_info->srcp0_ihdl = srcp0_ihdl;
commit_info->srcp1_ihdl = srcp1_ihdl;
commit_info->dstp0_file = dstp0_file;
commit_info->dstp0_ihdl = dstp0_ihdl;
commit_info->dstp1_file = dstp1_file;
commit_info->dstp1_ihdl = dstp1_ihdl;
commit_info->ps0_need = ps0_need;
commit_info->session_index = s;
commit_info->acq_fen = msm_rotator_dev->sync_info[s].acq_fen;
commit_info->fast_yuv_en = mrd->rot_session[s]->fast_yuv_enable;
mutex_unlock(&msm_rotator_dev->rotator_lock);
return 0;
rotate_prepare_error:
put_img(dstp1_file, dstp1_ihdl, ROTATOR_DST_DOMAIN,
msm_rotator_dev->rot_session[s]->img_info.secure);
put_img(srcp1_file, srcp1_ihdl, ROTATOR_SRC_DOMAIN, 0);
put_img(dstp0_file, dstp0_ihdl, ROTATOR_DST_DOMAIN,
msm_rotator_dev->rot_session[s]->img_info.secure);
/* only source may use frame buffer */
if (info.src.flags & MDP_MEMORY_ID_TYPE_FB)
fput_light(srcp0_file, ps0_need);
else
put_img(srcp0_file, srcp0_ihdl, ROTATOR_SRC_DOMAIN, 0);
msm_rotator_signal_timeline_done(s);
dev_dbg(msm_rotator_dev->device, "%s() returning rc = %d\n",
__func__, rc);
mutex_unlock(&msm_rotator_dev->rotator_lock);
return rc;
}
static int msm_rotator_do_rotate_sub(
struct msm_rotator_commit_info *commit_info)
{
unsigned int status, format;
struct msm_rotator_data_info info;
unsigned int in_paddr, out_paddr;
int use_imem = 0, rc = 0;
struct file *srcp0_file, *dstp0_file;
struct file *srcp1_file, *dstp1_file;
struct ion_handle *srcp0_ihdl, *dstp0_ihdl;
struct ion_handle *srcp1_ihdl, *dstp1_ihdl;
int s, ps0_need;
unsigned int in_chroma_paddr, out_chroma_paddr;
unsigned int in_chroma2_paddr, out_chroma2_paddr;
struct msm_rotator_img_info *img_info;
mutex_lock(&msm_rotator_dev->rotator_lock);
info = commit_info->data_info;
img_info = &commit_info->img_info;
format = commit_info->format;
in_paddr = commit_info->in_paddr;
out_paddr = commit_info->out_paddr;
in_chroma_paddr = commit_info->in_chroma_paddr;
out_chroma_paddr = commit_info->out_chroma_paddr;
in_chroma2_paddr = commit_info->in_chroma2_paddr;
out_chroma2_paddr = commit_info->out_chroma2_paddr;
srcp0_file = commit_info->srcp0_file;
srcp1_file = commit_info->srcp1_file;
srcp0_ihdl = commit_info->srcp0_ihdl;
srcp1_ihdl = commit_info->srcp1_ihdl;
dstp0_file = commit_info->dstp0_file;
dstp0_ihdl = commit_info->dstp0_ihdl;
dstp1_file = commit_info->dstp1_file;
dstp1_ihdl = commit_info->dstp1_ihdl;
ps0_need = commit_info->ps0_need;
s = commit_info->session_index;
msm_rotator_wait_for_fence(commit_info->acq_fen);
commit_info->acq_fen = NULL;
cancel_delayed_work_sync(&msm_rotator_dev->rot_clk_work);
if (msm_rotator_dev->rot_clk_state != CLK_EN) {
enable_rot_clks();
msm_rotator_dev->rot_clk_state = CLK_EN;
}
enable_irq(msm_rotator_dev->irq);
#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
use_imem = msm_rotator_imem_allocate(ROTATOR_REQUEST);
#else
use_imem = 0;
#endif
/*
* workaround for a hardware bug. rotator hardware hangs when we
* use write burst beat size 16 on 128X128 tile fetch mode. As a
* temporary fix use 0x42 for BURST_SIZE when imem used.
*/
if (use_imem)
iowrite32(0x42, MSM_ROTATOR_MAX_BURST_SIZE);
iowrite32(((img_info->src_rect.h & 0x1fff)
<< 16) |
(img_info->src_rect.w & 0x1fff),
MSM_ROTATOR_SRC_SIZE);
iowrite32(((img_info->src_rect.y & 0x1fff)
<< 16) |
(img_info->src_rect.x & 0x1fff),
MSM_ROTATOR_SRC_XY);
iowrite32(((img_info->src.height & 0x1fff)
<< 16) |
(img_info->src.width & 0x1fff),
MSM_ROTATOR_SRC_IMAGE_SIZE);
switch (format) {
case MDP_RGB_565:
case MDP_BGR_565:
case MDP_RGB_888:
case MDP_ARGB_8888:
case MDP_RGBA_8888:
case MDP_XRGB_8888:
case MDP_BGRA_8888:
case MDP_RGBX_8888:
case MDP_YCBCR_H1V1:
case MDP_YCRCB_H1V1:
rc = msm_rotator_rgb_types(img_info,
in_paddr, out_paddr,
use_imem,
msm_rotator_dev->last_session_idx
!= s);
break;
case MDP_Y_CBCR_H2V2:
case MDP_Y_CRCB_H2V2:
case MDP_Y_CB_CR_H2V2:
case MDP_Y_CR_CB_H2V2:
case MDP_Y_CR_CB_GH2V2:
case MDP_Y_CRCB_H2V2_TILE:
case MDP_Y_CBCR_H2V2_TILE:
rc = msm_rotator_ycxcx_h2v2(img_info,
in_paddr, out_paddr, use_imem,
msm_rotator_dev->last_session_idx
!= s,
in_chroma_paddr,
out_chroma_paddr,
in_chroma2_paddr,
out_chroma2_paddr,
commit_info->fast_yuv_en);
break;
case MDP_Y_CBCR_H2V1:
case MDP_Y_CRCB_H2V1:
rc = msm_rotator_ycxcx_h2v1(img_info,
in_paddr, out_paddr, use_imem,
msm_rotator_dev->last_session_idx
!= s,
in_chroma_paddr,
out_chroma_paddr);
break;
case MDP_YCRYCB_H2V1:
rc = msm_rotator_ycrycb(img_info,
in_paddr, out_paddr, use_imem,
msm_rotator_dev->last_session_idx != s,
out_chroma_paddr);
break;
default:
rc = -EINVAL;
pr_err("%s(): Unsupported format %u\n", __func__, format);
goto do_rotate_exit;
}
if (rc != 0) {
msm_rotator_dev->last_session_idx = INVALID_SESSION;
pr_err("%s(): Invalid session error\n", __func__);
goto do_rotate_exit;
}
iowrite32(3, MSM_ROTATOR_INTR_ENABLE);
msm_rotator_dev->processing = 1;
iowrite32(0x1, MSM_ROTATOR_START);
wait_event(msm_rotator_dev->wq,
(msm_rotator_dev->processing == 0));
status = (unsigned char)ioread32(MSM_ROTATOR_INTR_STATUS);
if ((status & 0x03) != 0x01) {
pr_err("%s(): AXI Bus Error, issuing SW_RESET\n", __func__);
iowrite32(0x1, MSM_ROTATOR_SW_RESET);
rc = -EFAULT;
}
iowrite32(0, MSM_ROTATOR_INTR_ENABLE);
iowrite32(3, MSM_ROTATOR_INTR_CLEAR);
do_rotate_exit:
disable_irq(msm_rotator_dev->irq);
#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
msm_rotator_imem_free(ROTATOR_REQUEST);
#endif
schedule_delayed_work(&msm_rotator_dev->rot_clk_work, HZ);
put_img(dstp1_file, dstp1_ihdl, ROTATOR_DST_DOMAIN,
img_info->secure);
put_img(srcp1_file, srcp1_ihdl, ROTATOR_SRC_DOMAIN, 0);
put_img(dstp0_file, dstp0_ihdl, ROTATOR_DST_DOMAIN,
img_info->secure);
/* only source may use frame buffer */
if (info.src.flags & MDP_MEMORY_ID_TYPE_FB)
fput_light(srcp0_file, ps0_need);
else
put_img(srcp0_file, srcp0_ihdl, ROTATOR_SRC_DOMAIN, 0);
msm_rotator_signal_timeline_done(s);
mutex_unlock(&msm_rotator_dev->rotator_lock);
dev_dbg(msm_rotator_dev->device, "%s() returning rc = %d\n",
__func__, rc);
return rc;
}
static void rot_wait_for_commit_queue(u32 is_all)
{
int ret = 0;
u32 loop_cnt = 0;
while (1) {
mutex_lock(&mrd->commit_mutex);
if (is_all && (atomic_read(&mrd->commit_q_cnt) == 0))
break;
if ((!is_all) &&
(atomic_read(&mrd->commit_q_cnt) < MAX_COMMIT_QUEUE))
break;
INIT_COMPLETION(mrd->commit_comp);
mutex_unlock(&mrd->commit_mutex);
ret = wait_for_completion_interruptible_timeout(
&mrd->commit_comp,
msecs_to_jiffies(WAIT_ROT_TIMEOUT));
if ((ret <= 0) ||
(atomic_read(&mrd->commit_q_cnt) >= MAX_COMMIT_QUEUE) ||
(loop_cnt > MAX_COMMIT_QUEUE)) {
pr_err("%s wait for commit queue failed ret=%d pointers:%d %d",
__func__, ret, atomic_read(&mrd->commit_q_r),
atomic_read(&mrd->commit_q_w));
mutex_lock(&mrd->commit_mutex);
ret = -ETIME;
break;
} else {
ret = 0;
}
loop_cnt++;
};
if (is_all || ret) {
atomic_set(&mrd->commit_q_r, 0);
atomic_set(&mrd->commit_q_cnt, 0);
atomic_set(&mrd->commit_q_w, 0);
}
mutex_unlock(&mrd->commit_mutex);
}
static int msm_rotator_do_rotate(unsigned long arg)
{
struct msm_rotator_data_info info;
struct rot_sync_info *sync_info;
int session_index, ret;
int commit_q_w;
if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
return -EFAULT;
rot_wait_for_commit_queue(false);
mutex_lock(&mrd->commit_mutex);
commit_q_w = atomic_read(&mrd->commit_q_w);
ret = msm_rotator_rotate_prepare(&info,
&mrd->commit_info[commit_q_w]);
if (ret) {
mutex_unlock(&mrd->commit_mutex);
return ret;
}
session_index = mrd->commit_info[commit_q_w].session_index;
sync_info = &msm_rotator_dev->sync_info[session_index];
mutex_lock(&sync_info->sync_mutex);
atomic_inc(&sync_info->queue_buf_cnt);
sync_info->acq_fen = NULL;
mutex_unlock(&sync_info->sync_mutex);
if (atomic_inc_return(&mrd->commit_q_w) >= MAX_COMMIT_QUEUE)
atomic_set(&mrd->commit_q_w, 0);
atomic_inc(&mrd->commit_q_cnt);
schedule_work(&mrd->commit_work);
mutex_unlock(&mrd->commit_mutex);
if (info.wait_for_finish)
rot_wait_for_commit_queue(true);
return 0;
}
static void rot_commit_wq_handler(struct work_struct *work)
{
mutex_lock(&mrd->commit_wq_mutex);
mutex_lock(&mrd->commit_mutex);
while (atomic_read(&mrd->commit_q_cnt) > 0) {
mrd->commit_running = true;
mutex_unlock(&mrd->commit_mutex);
msm_rotator_do_rotate_sub(
&mrd->commit_info[atomic_read(&mrd->commit_q_r)]);
mutex_lock(&mrd->commit_mutex);
if (atomic_read(&mrd->commit_q_cnt) > 0) {
atomic_dec(&mrd->commit_q_cnt);
if (atomic_inc_return(&mrd->commit_q_r) >=
MAX_COMMIT_QUEUE)
atomic_set(&mrd->commit_q_r, 0);
}
complete_all(&mrd->commit_comp);
}
mrd->commit_running = false;
if (atomic_read(&mrd->commit_q_r) != atomic_read(&mrd->commit_q_w))
pr_err("%s invalid state: r=%d w=%d cnt=%d", __func__,
atomic_read(&mrd->commit_q_r),
atomic_read(&mrd->commit_q_w),
atomic_read(&mrd->commit_q_cnt));
mutex_unlock(&mrd->commit_mutex);
mutex_unlock(&mrd->commit_wq_mutex);
}
static void msm_rotator_set_perf_level(u32 wh, u32 is_rgb)
{
u32 perf_level;
if (is_rgb)
perf_level = 1;
else if (wh <= (640 * 480))
perf_level = 2;
else if (wh <= (736 * 1280))
perf_level = 3;
else
perf_level = 4;
#ifdef CONFIG_MSM_BUS_SCALING
msm_bus_scale_client_update_request(msm_rotator_dev->bus_client_handle,
perf_level);
#endif
}
static int rot_enable_iommu_clocks(struct msm_rotator_dev *rot_dev)
{
int ret = 0, i;
if (rot_dev->mmu_clk_on)
return 0;
for (i = 0; i < ARRAY_SIZE(rot_mmu_clks); i++) {
rot_mmu_clks[i].mmu_clk = clk_get(&msm_rotator_dev->pdev->dev,
rot_mmu_clks[i].mmu_clk_name);
if (IS_ERR(rot_mmu_clks[i].mmu_clk)) {
pr_err(" %s: Get failed for clk %s", __func__,
rot_mmu_clks[i].mmu_clk_name);
ret = PTR_ERR(rot_mmu_clks[i].mmu_clk);
break;
}
ret = clk_prepare_enable(rot_mmu_clks[i].mmu_clk);
if (ret) {
clk_put(rot_mmu_clks[i].mmu_clk);
rot_mmu_clks[i].mmu_clk = NULL;
}
}
if (ret) {
for (i--; i >= 0; i--) {
clk_disable_unprepare(rot_mmu_clks[i].mmu_clk);
clk_put(rot_mmu_clks[i].mmu_clk);
rot_mmu_clks[i].mmu_clk = NULL;
}
} else {
rot_dev->mmu_clk_on = 1;
}
return ret;
}
static int rot_disable_iommu_clocks(struct msm_rotator_dev *rot_dev)
{
int i;
if (!rot_dev->mmu_clk_on)
return 0;
for (i = 0; i < ARRAY_SIZE(rot_mmu_clks); i++) {
clk_disable_unprepare(rot_mmu_clks[i].mmu_clk);
clk_put(rot_mmu_clks[i].mmu_clk);
rot_mmu_clks[i].mmu_clk = NULL;
}
rot_dev->mmu_clk_on = 0;
return 0;
}
static int map_sec_resource(struct msm_rotator_dev *rot_dev)
{
int ret = 0;
if (rot_dev->sec_mapped)
return 0;
ret = rot_enable_iommu_clocks(rot_dev);
if (ret) {
pr_err("IOMMU clock enabled failed while open");
return ret;
}
ret = msm_ion_secure_heap(ION_HEAP(ION_CP_MM_HEAP_ID));
if (ret)
pr_err("ION heap secure failed heap id %d ret %d\n",
ION_CP_MM_HEAP_ID, ret);
else
rot_dev->sec_mapped = 1;
rot_disable_iommu_clocks(rot_dev);
return ret;
}
static int unmap_sec_resource(struct msm_rotator_dev *rot_dev)
{
int ret = 0;
ret = rot_enable_iommu_clocks(rot_dev);
if (ret) {
pr_err("IOMMU clock enabled failed while close\n");
return ret;
}
msm_ion_unsecure_heap(ION_HEAP(ION_CP_MM_HEAP_ID));
rot_dev->sec_mapped = 0;
rot_disable_iommu_clocks(rot_dev);
return ret;
}
static int msm_rotator_start(unsigned long arg,
struct msm_rotator_fd_info *fd_info)
{
struct msm_rotator_img_info info;
struct msm_rotator_session *rot_session = NULL;
int rc = 0;
int s, is_rgb = 0;
int first_free_idx = INVALID_SESSION;
unsigned int dst_w, dst_h;
unsigned int is_planar420 = 0;
int fast_yuv_en = 0;
struct rot_sync_info *sync_info;
if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
return -EFAULT;
if ((info.rotations > MSM_ROTATOR_MAX_ROT) ||
(info.src.height > MSM_ROTATOR_MAX_H) ||
(info.src.width > MSM_ROTATOR_MAX_W) ||
(info.dst.height > MSM_ROTATOR_MAX_H) ||
(info.dst.width > MSM_ROTATOR_MAX_W) ||
(info.downscale_ratio > MAX_DOWNSCALE_RATIO)) {
pr_err("%s: Invalid parameters\n", __func__);
return -EINVAL;
}
if (info.rotations & MDP_ROT_90) {
dst_w = info.src_rect.h >> info.downscale_ratio;
dst_h = info.src_rect.w >> info.downscale_ratio;
} else {
dst_w = info.src_rect.w >> info.downscale_ratio;
dst_h = info.src_rect.h >> info.downscale_ratio;
}
if (checkoffset(info.src_rect.x, info.src_rect.w, info.src.width) ||
checkoffset(info.src_rect.y, info.src_rect.h, info.src.height) ||
checkoffset(info.dst_x, dst_w, info.dst.width) ||
checkoffset(info.dst_y, dst_h, info.dst.height)) {
pr_err("%s: Invalid src or dst rect\n", __func__);
return -ERANGE;
}
switch (info.src.format) {
case MDP_Y_CB_CR_H2V2:
case MDP_Y_CR_CB_H2V2:
case MDP_Y_CR_CB_GH2V2:
is_planar420 = 1;
case MDP_Y_CBCR_H2V2:
case MDP_Y_CRCB_H2V2:
case MDP_Y_CRCB_H2V2_TILE:
case MDP_Y_CBCR_H2V2_TILE:
if (rotator_hw_revision >= ROTATOR_REVISION_V2 &&
!(info.downscale_ratio &&
(info.rotations & MDP_ROT_90)))
fast_yuv_en = !fast_yuv_invalid_size_checker(
info.rotations,
info.src.width,
info.src.height,
dst_w,
dst_h,
dst_w,
is_planar420);
break;
default:
fast_yuv_en = 0;
}
switch (info.src.format) {
case MDP_RGB_565:
case MDP_BGR_565:
case MDP_RGB_888:
case MDP_ARGB_8888:
case MDP_RGBA_8888:
case MDP_XRGB_8888:
case MDP_RGBX_8888:
case MDP_BGRA_8888:
is_rgb = 1;
info.dst.format = info.src.format;
break;
case MDP_Y_CBCR_H2V2:
case MDP_Y_CRCB_H2V2:
case MDP_Y_CBCR_H2V1:
case MDP_Y_CRCB_H2V1:
case MDP_YCBCR_H1V1:
case MDP_YCRCB_H1V1:
info.dst.format = info.src.format;
break;
case MDP_YCRYCB_H2V1:
if (info.rotations & MDP_ROT_90)
info.dst.format = MDP_Y_CRCB_H1V2;
else
info.dst.format = MDP_Y_CRCB_H2V1;
break;
case MDP_Y_CB_CR_H2V2:
if (fast_yuv_en) {
info.dst.format = info.src.format;
break;
}
case MDP_Y_CBCR_H2V2_TILE:
info.dst.format = MDP_Y_CBCR_H2V2;
break;
case MDP_Y_CR_CB_H2V2:
case MDP_Y_CR_CB_GH2V2:
if (fast_yuv_en) {
info.dst.format = info.src.format;
break;
}
case MDP_Y_CRCB_H2V2_TILE:
info.dst.format = MDP_Y_CRCB_H2V2;
break;
default:
return -EINVAL;
}
mutex_lock(&msm_rotator_dev->rotator_lock);
msm_rotator_set_perf_level((info.src.width*info.src.height), is_rgb);
for (s = 0; s < MAX_SESSIONS; s++) {
if ((msm_rotator_dev->rot_session[s] != NULL) &&
(info.session_id ==
(unsigned int)msm_rotator_dev->rot_session[s]
)) {
rot_session = msm_rotator_dev->rot_session[s];
rot_session->img_info = info;
rot_session->fd_info = *fd_info;
rot_session->fast_yuv_enable = fast_yuv_en;
if (msm_rotator_dev->last_session_idx == s)
msm_rotator_dev->last_session_idx =
INVALID_SESSION;
break;
}
if ((msm_rotator_dev->rot_session[s] == NULL) &&
(first_free_idx == INVALID_SESSION))
first_free_idx = s;
}
if ((s == MAX_SESSIONS) && (first_free_idx != INVALID_SESSION)) {
/* allocate a session id */
msm_rotator_dev->rot_session[first_free_idx] =
kzalloc(sizeof(struct msm_rotator_session),
GFP_KERNEL);
if (!msm_rotator_dev->rot_session[first_free_idx]) {
printk(KERN_ERR "%s : unable to alloc mem\n",
__func__);
rc = -ENOMEM;
goto rotator_start_exit;
}
info.session_id = (unsigned int)
msm_rotator_dev->rot_session[first_free_idx];
rot_session = msm_rotator_dev->rot_session[first_free_idx];
rot_session->img_info = info;
rot_session->fd_info = *fd_info;
rot_session->fast_yuv_enable = fast_yuv_en;
s = first_free_idx;
} else if (s == MAX_SESSIONS) {
dev_dbg(msm_rotator_dev->device, "%s: all sessions in use\n",
__func__);
rc = -EBUSY;
}
if (rc == 0 && copy_to_user((void __user *)arg, &info, sizeof(info)))
rc = -EFAULT;
if ((rc == 0) && (info.secure))
map_sec_resource(msm_rotator_dev);
sync_info = &msm_rotator_dev->sync_info[s];
if ((rc == 0) && (sync_info->initialized == false)) {
char timeline_name[MAX_TIMELINE_NAME_LEN];
if (sync_info->timeline == NULL) {
snprintf(timeline_name, sizeof(timeline_name),
"msm_rot_%d", first_free_idx);
sync_info->timeline =
sw_sync_timeline_create(timeline_name);
if (sync_info->timeline == NULL)
pr_err("%s: cannot create %s time line",
__func__, timeline_name);
sync_info->timeline_value = 0;
}
mutex_init(&sync_info->sync_mutex);
sync_info->initialized = true;
}
sync_info->acq_fen = NULL;
atomic_set(&sync_info->queue_buf_cnt, 0);
rotator_start_exit:
mutex_unlock(&msm_rotator_dev->rotator_lock);
return rc;
}
static int msm_rotator_finish(unsigned long arg)
{
int rc = 0;
int s;
unsigned int session_id;
if (copy_from_user(&session_id, (void __user *)arg, sizeof(s)))
return -EFAULT;
rot_wait_for_commit_queue(true);
mutex_lock(&msm_rotator_dev->rotator_lock);
for (s = 0; s < MAX_SESSIONS; s++) {
if ((msm_rotator_dev->rot_session[s] != NULL) &&
(session_id ==
(unsigned int)msm_rotator_dev->rot_session[s])) {
if (msm_rotator_dev->last_session_idx == s)
msm_rotator_dev->last_session_idx =
INVALID_SESSION;
msm_rotator_signal_timeline(s);
msm_rotator_release_acq_fence(s);
kfree(msm_rotator_dev->rot_session[s]);
msm_rotator_dev->rot_session[s] = NULL;
break;
}
}
if (s == MAX_SESSIONS)
rc = -EINVAL;
#ifdef CONFIG_MSM_BUS_SCALING
msm_bus_scale_client_update_request(msm_rotator_dev->bus_client_handle,
0);
#endif
if (msm_rotator_dev->sec_mapped)
unmap_sec_resource(msm_rotator_dev);
mutex_unlock(&msm_rotator_dev->rotator_lock);
return rc;
}
static int
msm_rotator_open(struct inode *inode, struct file *filp)
{
struct msm_rotator_fd_info *tmp, *fd_info = NULL;
int i;
if (filp->private_data)
return -EBUSY;
mutex_lock(&msm_rotator_dev->rotator_lock);
for (i = 0; i < MAX_SESSIONS; i++) {
if (msm_rotator_dev->rot_session[i] == NULL)
break;
}
if (i == MAX_SESSIONS) {
mutex_unlock(&msm_rotator_dev->rotator_lock);
return -EBUSY;
}
list_for_each_entry(tmp, &msm_rotator_dev->fd_list, list) {
if (tmp->pid == current->pid) {
fd_info = tmp;
break;
}
}
if (!fd_info) {
fd_info = kzalloc(sizeof(*fd_info), GFP_KERNEL);
if (!fd_info) {
mutex_unlock(&msm_rotator_dev->rotator_lock);
pr_err("%s: insufficient memory to alloc resources\n",
__func__);
return -ENOMEM;
}
list_add(&fd_info->list, &msm_rotator_dev->fd_list);
fd_info->pid = current->pid;
}
fd_info->ref_cnt++;
mutex_unlock(&msm_rotator_dev->rotator_lock);
filp->private_data = fd_info;
return 0;
}
static int
msm_rotator_close(struct inode *inode, struct file *filp)
{
struct msm_rotator_fd_info *fd_info;
int s;
fd_info = (struct msm_rotator_fd_info *)filp->private_data;
mutex_lock(&msm_rotator_dev->rotator_lock);
if (--fd_info->ref_cnt > 0) {
mutex_unlock(&msm_rotator_dev->rotator_lock);
return 0;
}
for (s = 0; s < MAX_SESSIONS; s++) {
if (msm_rotator_dev->rot_session[s] != NULL &&
&(msm_rotator_dev->rot_session[s]->fd_info) == fd_info) {
pr_debug("%s: freeing rotator session %p (pid %d)\n",
__func__, msm_rotator_dev->rot_session[s],
fd_info->pid);
rot_wait_for_commit_queue(true);
msm_rotator_signal_timeline(s);
kfree(msm_rotator_dev->rot_session[s]);
msm_rotator_dev->rot_session[s] = NULL;
if (msm_rotator_dev->last_session_idx == s)
msm_rotator_dev->last_session_idx =
INVALID_SESSION;
}
}
list_del(&fd_info->list);
kfree(fd_info);
mutex_unlock(&msm_rotator_dev->rotator_lock);
return 0;
}
static long msm_rotator_ioctl(struct file *file, unsigned cmd,
unsigned long arg)
{
struct msm_rotator_fd_info *fd_info;
if (_IOC_TYPE(cmd) != MSM_ROTATOR_IOCTL_MAGIC)
return -ENOTTY;
fd_info = (struct msm_rotator_fd_info *)file->private_data;
switch (cmd) {
case MSM_ROTATOR_IOCTL_START:
return msm_rotator_start(arg, fd_info);
case MSM_ROTATOR_IOCTL_ROTATE:
return msm_rotator_do_rotate(arg);
case MSM_ROTATOR_IOCTL_FINISH:
return msm_rotator_finish(arg);
case MSM_ROTATOR_IOCTL_BUFFER_SYNC:
return msm_rotator_buf_sync(arg);
default:
dev_dbg(msm_rotator_dev->device,
"unexpected IOCTL %d\n", cmd);
return -ENOTTY;
}
}
static const struct file_operations msm_rotator_fops = {
.owner = THIS_MODULE,
.open = msm_rotator_open,
.release = msm_rotator_close,
.unlocked_ioctl = msm_rotator_ioctl,
};
static int __devinit msm_rotator_probe(struct platform_device *pdev)
{
int rc = 0;
struct resource *res;
struct msm_rotator_platform_data *pdata = NULL;
int i, number_of_clks;
uint32_t ver;
msm_rotator_dev = kzalloc(sizeof(struct msm_rotator_dev), GFP_KERNEL);
if (!msm_rotator_dev) {
printk(KERN_ERR "%s Unable to allocate memory for struct\n",
__func__);
return -ENOMEM;
}
for (i = 0; i < MAX_SESSIONS; i++)
msm_rotator_dev->rot_session[i] = NULL;
msm_rotator_dev->last_session_idx = INVALID_SESSION;
pdata = pdev->dev.platform_data;
number_of_clks = pdata->number_of_clocks;
rot_iommu_split_domain = pdata->rot_iommu_split_domain;
msm_rotator_dev->imem_owner = IMEM_NO_OWNER;
mutex_init(&msm_rotator_dev->imem_lock);
INIT_LIST_HEAD(&msm_rotator_dev->fd_list);
msm_rotator_dev->imem_clk_state = CLK_DIS;
INIT_DELAYED_WORK(&msm_rotator_dev->imem_clk_work,
msm_rotator_imem_clk_work_f);
msm_rotator_dev->imem_clk = NULL;
msm_rotator_dev->pdev = pdev;
msm_rotator_dev->core_clk = NULL;
msm_rotator_dev->pclk = NULL;
#ifdef CONFIG_MSM_BUS_SCALING
if (!msm_rotator_dev->bus_client_handle && pdata &&
pdata->bus_scale_table) {
msm_rotator_dev->bus_client_handle =
msm_bus_scale_register_client(
pdata->bus_scale_table);
if (!msm_rotator_dev->bus_client_handle) {
pr_err("%s not able to get bus scale handle\n",
__func__);
}
}
#endif
for (i = 0; i < number_of_clks; i++) {
if (pdata->rotator_clks[i].clk_type == ROTATOR_IMEM_CLK) {
msm_rotator_dev->imem_clk =
clk_get(&msm_rotator_dev->pdev->dev,
pdata->rotator_clks[i].clk_name);
if (IS_ERR(msm_rotator_dev->imem_clk)) {
rc = PTR_ERR(msm_rotator_dev->imem_clk);
msm_rotator_dev->imem_clk = NULL;
printk(KERN_ERR "%s: cannot get imem_clk "
"rc=%d\n", DRIVER_NAME, rc);
goto error_imem_clk;
}
if (pdata->rotator_clks[i].clk_rate)
clk_set_rate(msm_rotator_dev->imem_clk,
pdata->rotator_clks[i].clk_rate);
}
if (pdata->rotator_clks[i].clk_type == ROTATOR_PCLK) {
msm_rotator_dev->pclk =
clk_get(&msm_rotator_dev->pdev->dev,
pdata->rotator_clks[i].clk_name);
if (IS_ERR(msm_rotator_dev->pclk)) {
rc = PTR_ERR(msm_rotator_dev->pclk);
msm_rotator_dev->pclk = NULL;
printk(KERN_ERR "%s: cannot get pclk rc=%d\n",
DRIVER_NAME, rc);
goto error_pclk;
}
if (pdata->rotator_clks[i].clk_rate)
clk_set_rate(msm_rotator_dev->pclk,
pdata->rotator_clks[i].clk_rate);
}
if (pdata->rotator_clks[i].clk_type == ROTATOR_CORE_CLK) {
msm_rotator_dev->core_clk =
clk_get(&msm_rotator_dev->pdev->dev,
pdata->rotator_clks[i].clk_name);
if (IS_ERR(msm_rotator_dev->core_clk)) {
rc = PTR_ERR(msm_rotator_dev->core_clk);
msm_rotator_dev->core_clk = NULL;
printk(KERN_ERR "%s: cannot get core clk "
"rc=%d\n", DRIVER_NAME, rc);
goto error_core_clk;
}
if (pdata->rotator_clks[i].clk_rate)
clk_set_rate(msm_rotator_dev->core_clk,
pdata->rotator_clks[i].clk_rate);
}
}
msm_rotator_dev->regulator = regulator_get(&msm_rotator_dev->pdev->dev,
"vdd");
if (IS_ERR(msm_rotator_dev->regulator))
msm_rotator_dev->regulator = NULL;
msm_rotator_dev->rot_clk_state = CLK_DIS;
INIT_DELAYED_WORK(&msm_rotator_dev->rot_clk_work,
msm_rotator_rot_clk_work_f);
mutex_init(&msm_rotator_dev->rotator_lock);
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
msm_rotator_dev->client = msm_ion_client_create(-1, pdev->name);
#endif
platform_set_drvdata(pdev, msm_rotator_dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
printk(KERN_ALERT
"%s: could not get IORESOURCE_MEM\n", DRIVER_NAME);
rc = -ENODEV;
goto error_get_resource;
}
msm_rotator_dev->io_base = ioremap(res->start,
resource_size(res));
#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
if (msm_rotator_dev->imem_clk)
clk_prepare_enable(msm_rotator_dev->imem_clk);
#endif
enable_rot_clks();
ver = ioread32(MSM_ROTATOR_HW_VERSION);
disable_rot_clks();
#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
if (msm_rotator_dev->imem_clk)
clk_disable_unprepare(msm_rotator_dev->imem_clk);
#endif
if (ver != pdata->hardware_version_number)
pr_debug("%s: invalid HW version ver 0x%x\n",
DRIVER_NAME, ver);
rotator_hw_revision = ver;
rotator_hw_revision >>= 16; /* bit 31:16 */
rotator_hw_revision &= 0xff;
pr_info("%s: rotator_hw_revision=%x\n",
__func__, rotator_hw_revision);
msm_rotator_dev->irq = platform_get_irq(pdev, 0);
if (msm_rotator_dev->irq < 0) {
printk(KERN_ALERT "%s: could not get IORESOURCE_IRQ\n",
DRIVER_NAME);
rc = -ENODEV;
goto error_get_irq;
}
rc = request_irq(msm_rotator_dev->irq, msm_rotator_isr,
IRQF_TRIGGER_RISING, DRIVER_NAME, NULL);
if (rc) {
printk(KERN_ERR "%s: request_irq() failed\n", DRIVER_NAME);
goto error_get_irq;
}
/* we enable the IRQ when we need it in the ioctl */
disable_irq(msm_rotator_dev->irq);
rc = alloc_chrdev_region(&msm_rotator_dev->dev_num, 0, 1, DRIVER_NAME);
if (rc < 0) {
printk(KERN_ERR "%s: alloc_chrdev_region Failed rc = %d\n",
__func__, rc);
goto error_get_irq;
}
msm_rotator_dev->class = class_create(THIS_MODULE, DRIVER_NAME);
if (IS_ERR(msm_rotator_dev->class)) {
rc = PTR_ERR(msm_rotator_dev->class);
printk(KERN_ERR "%s: couldn't create class rc = %d\n",
DRIVER_NAME, rc);
goto error_class_create;
}
msm_rotator_dev->device = device_create(msm_rotator_dev->class, NULL,
msm_rotator_dev->dev_num, NULL,
DRIVER_NAME);
if (IS_ERR(msm_rotator_dev->device)) {
rc = PTR_ERR(msm_rotator_dev->device);
printk(KERN_ERR "%s: device_create failed %d\n",
DRIVER_NAME, rc);
goto error_class_device_create;
}
cdev_init(&msm_rotator_dev->cdev, &msm_rotator_fops);
rc = cdev_add(&msm_rotator_dev->cdev,
MKDEV(MAJOR(msm_rotator_dev->dev_num), 0),
1);
if (rc < 0) {
printk(KERN_ERR "%s: cdev_add failed %d\n", __func__, rc);
goto error_cdev_add;
}
init_waitqueue_head(&msm_rotator_dev->wq);
INIT_WORK(&msm_rotator_dev->commit_work, rot_commit_wq_handler);
init_completion(&msm_rotator_dev->commit_comp);
mutex_init(&msm_rotator_dev->commit_mutex);
mutex_init(&msm_rotator_dev->commit_wq_mutex);
atomic_set(&msm_rotator_dev->commit_q_w, 0);
atomic_set(&msm_rotator_dev->commit_q_r, 0);
atomic_set(&msm_rotator_dev->commit_q_cnt, 0);
dev_dbg(msm_rotator_dev->device, "probe successful\n");
return rc;
error_cdev_add:
device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
error_class_device_create:
class_destroy(msm_rotator_dev->class);
error_class_create:
unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
error_get_irq:
iounmap(msm_rotator_dev->io_base);
error_get_resource:
mutex_destroy(&msm_rotator_dev->rotator_lock);
if (msm_rotator_dev->regulator)
regulator_put(msm_rotator_dev->regulator);
clk_put(msm_rotator_dev->core_clk);
error_core_clk:
clk_put(msm_rotator_dev->pclk);
error_pclk:
if (msm_rotator_dev->imem_clk)
clk_put(msm_rotator_dev->imem_clk);
error_imem_clk:
mutex_destroy(&msm_rotator_dev->imem_lock);
kfree(msm_rotator_dev);
return rc;
}
static int __devexit msm_rotator_remove(struct platform_device *plat_dev)
{
int i;
rot_wait_for_commit_queue(true);
#ifdef CONFIG_MSM_BUS_SCALING
if (msm_rotator_dev->bus_client_handle) {
msm_bus_scale_unregister_client
(msm_rotator_dev->bus_client_handle);
msm_rotator_dev->bus_client_handle = 0;
}
#endif
free_irq(msm_rotator_dev->irq, NULL);
mutex_destroy(&msm_rotator_dev->rotator_lock);
cdev_del(&msm_rotator_dev->cdev);
device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
class_destroy(msm_rotator_dev->class);
unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
iounmap(msm_rotator_dev->io_base);
if (msm_rotator_dev->imem_clk) {
if (msm_rotator_dev->imem_clk_state == CLK_EN)
clk_disable_unprepare(msm_rotator_dev->imem_clk);
clk_put(msm_rotator_dev->imem_clk);
msm_rotator_dev->imem_clk = NULL;
}
if (msm_rotator_dev->rot_clk_state == CLK_EN)
disable_rot_clks();
clk_put(msm_rotator_dev->core_clk);
clk_put(msm_rotator_dev->pclk);
if (msm_rotator_dev->regulator)
regulator_put(msm_rotator_dev->regulator);
msm_rotator_dev->core_clk = NULL;
msm_rotator_dev->pclk = NULL;
mutex_destroy(&msm_rotator_dev->imem_lock);
for (i = 0; i < MAX_SESSIONS; i++)
if (msm_rotator_dev->rot_session[i] != NULL)
kfree(msm_rotator_dev->rot_session[i]);
kfree(msm_rotator_dev);
return 0;
}
#ifdef CONFIG_PM
static int msm_rotator_suspend(struct platform_device *dev, pm_message_t state)
{
rot_wait_for_commit_queue(true);
mutex_lock(&msm_rotator_dev->imem_lock);
if (msm_rotator_dev->imem_clk_state == CLK_EN
&& msm_rotator_dev->imem_clk) {
clk_disable_unprepare(msm_rotator_dev->imem_clk);
msm_rotator_dev->imem_clk_state = CLK_SUSPEND;
}
mutex_unlock(&msm_rotator_dev->imem_lock);
mutex_lock(&msm_rotator_dev->rotator_lock);
if (msm_rotator_dev->rot_clk_state == CLK_EN) {
disable_rot_clks();
msm_rotator_dev->rot_clk_state = CLK_SUSPEND;
}
msm_rotator_release_all_timeline();
mutex_unlock(&msm_rotator_dev->rotator_lock);
return 0;
}
static int msm_rotator_resume(struct platform_device *dev)
{
mutex_lock(&msm_rotator_dev->imem_lock);
if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND
&& msm_rotator_dev->imem_clk) {
clk_prepare_enable(msm_rotator_dev->imem_clk);
msm_rotator_dev->imem_clk_state = CLK_EN;
}
mutex_unlock(&msm_rotator_dev->imem_lock);
mutex_lock(&msm_rotator_dev->rotator_lock);
if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND) {
enable_rot_clks();
msm_rotator_dev->rot_clk_state = CLK_EN;
}
mutex_unlock(&msm_rotator_dev->rotator_lock);
return 0;
}
#endif
static struct platform_driver msm_rotator_platform_driver = {
.probe = msm_rotator_probe,
.remove = __devexit_p(msm_rotator_remove),
#ifdef CONFIG_PM
.suspend = msm_rotator_suspend,
.resume = msm_rotator_resume,
#endif
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME
}
};
static int __init msm_rotator_init(void)
{
return platform_driver_register(&msm_rotator_platform_driver);
}
static void __exit msm_rotator_exit(void)
{
return platform_driver_unregister(&msm_rotator_platform_driver);
}
module_init(msm_rotator_init);
module_exit(msm_rotator_exit);
MODULE_DESCRIPTION("MSM Offline Image Rotator driver");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL v2");