msm: gemini: Support upto 64mp encoding

Redesign the output ping pong mechanism to support
upto 64mp encoding using gemini hardware.

Remove the MSM_GMN_IOCTL_TEST_DUMP_REGION ioctl,
because it's unsafe.

Change-Id: Id7801861e0fe119f61a02fb3409155f312a85d21
Signed-off-by: Sunid Wilson <sunidw@codeaurora.org>
This commit is contained in:
Sunid Wilson
2013-04-23 00:59:56 -07:00
committed by Kamen Kirov
parent b0b2953308
commit 82e25d5040
7 changed files with 309 additions and 77 deletions
@@ -1,4 +1,4 @@
/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-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
@@ -142,7 +142,7 @@ void *msm_gemini_core_framedone_irq(int gemini_irq_status, void *context)
buf_p = msm_gemini_hw_pingpong_active_buffer(&we_pingpong_buf);
if (buf_p) {
buf_p->framedone_len = msm_gemini_hw_encode_output_size();
GMN_DBG("%s:%d] framedone_len %d\n", __func__, __LINE__,
pr_debug("%s:%d] framedone_len %d\n", __func__, __LINE__,
buf_p->framedone_len);
}
@@ -1,4 +1,4 @@
/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010,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
@@ -297,6 +297,8 @@ void msm_gemini_hw_we_buffer_update(struct msm_gemini_hw_buf *p_input,
struct msm_gemini_hw_cmd *hw_cmd_p;
pr_debug("%s:%d] pingpong index %d", __func__, __LINE__,
pingpong_index);
if (pingpong_index == 0) {
hw_cmd_p = &hw_cmd_we_ping_update[0];
@@ -486,40 +488,38 @@ int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds)
return is_copy_to_user;
}
void msm_gemini_hw_region_dump(int size)
#ifdef MSM_GMN_DBG_DUMP
void msm_gemini_io_dump(int size)
{
uint32_t *p;
uint8_t *p8;
if (size > gemini_region_size)
GMN_PR_ERR("%s:%d] wrong region dump size\n",
__func__, __LINE__);
p = (uint32_t *) gemini_region_base;
while (size >= 16) {
GMN_DBG("0x%08X] %08X %08X %08X %08X\n",
gemini_region_size - size,
readl(p), readl(p+1), readl(p+2), readl(p+3));
p += 4;
size -= 16;
}
if (size > 0) {
uint32_t d;
GMN_DBG("0x%08X] ", gemini_region_size - size);
while (size >= 4) {
GMN_DBG("%08X ", readl(p++));
size -= 4;
char line_str[128], *p_str;
void __iomem *addr = gemini_region_base;
int i;
u32 *p = (u32 *) addr;
u32 data;
pr_info("%s: %p %d reg_size %d\n", __func__, addr, size,
gemini_region_size);
line_str[0] = '\0';
p_str = line_str;
for (i = 0; i < size/4; i++) {
if (i % 4 == 0) {
snprintf(p_str, 12, "%08x: ", (u32) p);
p_str += 10;
}
d = readl(p);
p8 = (uint8_t *) &d;
while (size) {
GMN_DBG("%02X", *p8++);
size--;
data = readl_relaxed(p++);
snprintf(p_str, 12, "%08x ", data);
p_str += 9;
if ((i + 1) % 4 == 0) {
pr_info("%s\n", line_str);
line_str[0] = '\0';
p_str = line_str;
}
GMN_DBG("\n");
}
if (line_str[0] != '\0')
pr_info("%s\n", line_str);
}
#else
void msm_gemini_io_dump(int size)
{
}
#endif
@@ -1,4 +1,4 @@
/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-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
@@ -95,7 +95,7 @@ void msm_gemini_hw_write(struct msm_gemini_hw_cmd *hw_cmd_p);
int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds);
void msm_gemini_hw_region_dump(int size);
void msm_gemini_io_dump(int size);
#define MSM_GEMINI_PIPELINE_CLK_128MHZ 128 /* 8MP 128MHz */
#define MSM_GEMINI_PIPELINE_CLK_140MHZ 140 /* 9MP 140MHz */
@@ -1,4 +1,4 @@
/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010, 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
@@ -171,6 +171,6 @@
#define HWIO_JPEG_IRQ_STATUS_RMSK 0xffffffff
#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR (GEMINI_REG_BASE + 0x00000034)
#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK 0xffffff
#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK 0xffffffff
#endif /* MSM_GEMINI_HW_REG_H */
@@ -1,4 +1,4 @@
/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-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
@@ -16,6 +16,8 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <media/msm_gemini.h>
#include <mach/msm_bus.h>
#include <mach/msm_bus_board.h>
#include "msm_gemini_sync.h"
#include "msm_gemini_core.h"
#include "msm_gemini_platform.h"
@@ -23,6 +25,9 @@
static int release_buf;
/* size is based on 4k page size */
static const int g_max_out_size = 0x7ff000;
/*************** queue helper ****************/
inline void msm_gemini_q_init(char const *name, struct msm_gemini_q *q_p)
{
@@ -180,7 +185,7 @@ int msm_gemini_framedone_irq(struct msm_gemini_device *pgmn_dev,
{
int rc = 0;
GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
pr_debug("%s:%d] buf_in %p", __func__, __LINE__, buf_in);
if (buf_in) {
buf_in->vbuf.framedone_len = buf_in->framedone_len;
@@ -266,19 +271,88 @@ void msm_gemini_err_irq(struct msm_gemini_device *pgmn_dev,
/*************** output queue ****************/
int msm_gemini_get_out_buffer(struct msm_gemini_device *pgmn_dev,
struct msm_gemini_hw_buf *p_outbuf)
{
int buf_size = 0;
int bytes_remaining = 0;
if (pgmn_dev->out_offset >= pgmn_dev->out_buf.y_len) {
GMN_PR_ERR("%s:%d] no more buffers", __func__, __LINE__);
return -EINVAL;
}
bytes_remaining = pgmn_dev->out_buf.y_len - pgmn_dev->out_offset;
buf_size = min(bytes_remaining, pgmn_dev->max_out_size);
pgmn_dev->out_frag_cnt++;
pr_debug("%s:%d] buf_size[%d] %d", __func__, __LINE__,
pgmn_dev->out_frag_cnt, buf_size);
p_outbuf->y_len = buf_size;
p_outbuf->y_buffer_addr = pgmn_dev->out_buf.y_buffer_addr +
pgmn_dev->out_offset;
pgmn_dev->out_offset += buf_size;
return 0;
}
int msm_gemini_outmode_single_we_pingpong_irq(
struct msm_gemini_device *pgmn_dev,
struct msm_gemini_core_buf *buf_in)
{
int rc = 0;
struct msm_gemini_core_buf out_buf;
int frame_done = buf_in &&
buf_in->vbuf.type == MSM_GEMINI_EVT_FRAMEDONE;
pr_debug("%s:%d] framedone %d", __func__, __LINE__, frame_done);
if (!pgmn_dev->out_buf_set) {
pr_err("%s:%d] output buffer not set",
__func__, __LINE__);
return -EFAULT;
}
if (frame_done) {
/* send the buffer back */
pgmn_dev->out_buf.vbuf.framedone_len = buf_in->framedone_len;
pgmn_dev->out_buf.vbuf.type = MSM_GEMINI_EVT_FRAMEDONE;
rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q,
&pgmn_dev->out_buf);
if (rc) {
pr_err("%s:%d] cannot queue the output buffer",
__func__, __LINE__);
return -EFAULT;
}
rc = msm_gemini_q_wakeup(&pgmn_dev->output_rtn_q);
/* reset the output buffer since the ownership is
transferred to the rtn queue */
if (!rc)
pgmn_dev->out_buf_set = 0;
} else {
/* configure ping/pong */
rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
if (rc)
msm_gemini_core_we_buf_reset(&out_buf);
else
msm_gemini_core_we_buf_update(&out_buf);
}
return rc;
}
int msm_gemini_we_pingpong_irq(struct msm_gemini_device *pgmn_dev,
struct msm_gemini_core_buf *buf_in)
{
int rc = 0;
struct msm_gemini_core_buf *buf_out;
GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
pr_debug("%s:%d] Enter mode %d", __func__, __LINE__,
pgmn_dev->out_mode);
if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_SINGLE)
return msm_gemini_outmode_single_we_pingpong_irq(pgmn_dev,
buf_in);
if (buf_in) {
GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
pr_debug("%s:%d] 0x%08x %d\n", __func__, __LINE__,
(int) buf_in->y_buffer_addr, buf_in->y_len);
rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q, buf_in);
} else {
GMN_DBG("%s:%d] no output return buffer\n", __func__,
pr_debug("%s:%d] no output return buffer\n", __func__,
__LINE__);
rc = -1;
return rc;
@@ -291,7 +365,7 @@ int msm_gemini_we_pingpong_irq(struct msm_gemini_device *pgmn_dev,
kfree(buf_out);
} else {
msm_gemini_core_we_buf_reset(buf_in);
GMN_DBG("%s:%d] no output buffer\n", __func__, __LINE__);
pr_debug("%s:%d] no output buffer\n", __func__, __LINE__);
rc = -2;
}
@@ -339,6 +413,43 @@ int msm_gemini_output_get_unblock(struct msm_gemini_device *pgmn_dev)
return 0;
}
int msm_gemini_set_output_buf(struct msm_gemini_device *pgmn_dev,
void __user *arg)
{
struct msm_gemini_buf buf_cmd;
if (pgmn_dev->out_buf_set) {
pr_err("%s:%d] outbuffer buffer already provided",
__func__, __LINE__);
return -EINVAL;
}
if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
pr_err("%s:%d] failed\n", __func__, __LINE__);
return -EFAULT;
}
GMN_DBG("%s:%d] output addr 0x%08x len %d", __func__, __LINE__,
(int) buf_cmd.vaddr,
buf_cmd.y_len);
pgmn_dev->out_buf.y_buffer_addr = msm_gemini_platform_v2p(
buf_cmd.fd,
buf_cmd.y_len,
&pgmn_dev->out_buf.file,
&pgmn_dev->out_buf.handle);
if (!pgmn_dev->out_buf.y_buffer_addr) {
pr_err("%s:%d] cannot map the output address",
__func__, __LINE__);
return -EFAULT;
}
pgmn_dev->out_buf.y_len = buf_cmd.y_len;
pgmn_dev->out_buf.vbuf = buf_cmd;
pgmn_dev->out_buf_set = 1;
return 0;
}
int msm_gemini_output_buf_enqueue(struct msm_gemini_device *pgmn_dev,
void __user *arg)
{
@@ -456,6 +567,7 @@ int msm_gemini_input_buf_enqueue(struct msm_gemini_device *pgmn_dev,
struct msm_gemini_core_buf *buf_p;
struct msm_gemini_buf buf_cmd;
int rc = 0;
struct msm_bus_scale_pdata *p_bus_scale_data = NULL;
if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
@@ -484,9 +596,9 @@ int msm_gemini_input_buf_enqueue(struct msm_gemini_device *pgmn_dev,
return rc;
}
} else {
buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
&buf_p->handle) + buf_cmd.offset + buf_cmd.y_off;
buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
&buf_p->handle) + buf_cmd.offset + buf_cmd.y_off;
}
buf_p->y_len = buf_cmd.y_len;
@@ -504,6 +616,30 @@ int msm_gemini_input_buf_enqueue(struct msm_gemini_device *pgmn_dev,
return -1;
}
buf_p->vbuf = buf_cmd;
buf_p->vbuf.type = MSM_GEMINI_EVT_RESET;
/* Set bus vectors */
p_bus_scale_data = (struct msm_bus_scale_pdata *)
pgmn_dev->pdev->dev.platform_data;
if (pgmn_dev->bus_perf_client &&
(MSM_GMN_OUTMODE_SINGLE == pgmn_dev->out_mode)) {
int rc;
struct msm_bus_paths *path = &(p_bus_scale_data->usecase[1]);
GMN_DBG("%s:%d] Update bus bandwidth", __func__, __LINE__);
if (pgmn_dev->op_mode & MSM_GEMINI_MODE_OFFLINE_ENCODE) {
path->vectors[0].ab = (buf_p->y_len + buf_p->cbcr_len) *
15 * 2;
path->vectors[0].ib = path->vectors[0].ab;
path->vectors[1].ab = 0;
path->vectors[1].ib = 0;
}
rc = msm_bus_scale_client_update_request(
pgmn_dev->bus_perf_client, 1);
if (rc < 0) {
GMN_PR_ERR("%s:%d] update_request fails %d",
__func__, __LINE__, rc);
}
}
msm_gemini_q_in(&pgmn_dev->input_buf_q, buf_p);
@@ -545,6 +681,9 @@ int msm_gemini_irq(int event, void *context, void *data)
int __msm_gemini_open(struct msm_gemini_device *pgmn_dev)
{
int rc;
struct msm_bus_scale_pdata *p_bus_scale_data =
(struct msm_bus_scale_pdata *)pgmn_dev->pdev->dev.
platform_data;
mutex_lock(&pgmn_dev->lock);
if (pgmn_dev->open_count) {
@@ -576,7 +715,23 @@ int __msm_gemini_open(struct msm_gemini_device *pgmn_dev)
msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
msm_gemini_q_cleanup(&pgmn_dev->input_buf_q);
msm_gemini_core_init();
pgmn_dev->out_mode = MSM_GMN_OUTMODE_FRAGMENTED;
pgmn_dev->out_buf_set = 0;
pgmn_dev->out_offset = 0;
pgmn_dev->max_out_size = g_max_out_size;
pgmn_dev->out_frag_cnt = 0;
pgmn_dev->bus_perf_client = 0;
if (p_bus_scale_data) {
GMN_DBG("%s:%d] register bus client", __func__, __LINE__);
pgmn_dev->bus_perf_client =
msm_bus_scale_register_client(p_bus_scale_data);
if (!pgmn_dev->bus_perf_client) {
GMN_PR_ERR("%s:%d] bus client register failed",
__func__, __LINE__);
return -EINVAL;
}
}
GMN_DBG("%s:%d] success\n", __func__, __LINE__);
return rc;
}
@@ -593,13 +748,23 @@ int __msm_gemini_release(struct msm_gemini_device *pgmn_dev)
pgmn_dev->open_count--;
mutex_unlock(&pgmn_dev->lock);
msm_gemini_core_release(release_buf);
if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED) {
msm_gemini_core_release(release_buf);
} else if (pgmn_dev->out_buf_set) {
msm_gemini_platform_p2v(pgmn_dev->out_buf.file,
&pgmn_dev->out_buf.handle);
}
msm_gemini_q_cleanup(&pgmn_dev->evt_q);
msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
msm_gemini_outbuf_q_cleanup(&pgmn_dev->input_buf_q);
if (pgmn_dev->bus_perf_client) {
msm_bus_scale_unregister_client(pgmn_dev->bus_perf_client);
pgmn_dev->bus_perf_client = 0;
}
if (pgmn_dev->open_count)
GMN_PR_ERR(KERN_ERR "%s: multiple opens\n", __func__);
@@ -699,29 +864,63 @@ int msm_gemini_start(struct msm_gemini_device *pgmn_dev, void * __user arg)
}
}
for (i = 0; i < 2; i++) {
buf_out_free[i] = msm_gemini_q_out(&pgmn_dev->output_buf_q);
if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED) {
for (i = 0; i < 2; i++) {
buf_out_free[i] =
msm_gemini_q_out(&pgmn_dev->output_buf_q);
if (buf_out_free[i]) {
msm_gemini_core_we_buf_update(buf_out_free[i]);
} else if (i == 1) {
/* set the pong to same address as ping */
buf_out_free[0]->y_len >>= 1;
buf_out_free[0]->y_buffer_addr +=
buf_out_free[0]->y_len;
msm_gemini_core_we_buf_update(buf_out_free[0]);
/* since ping and pong are same buf release only once*/
release_buf = 0;
} else {
GMN_DBG("%s:%d] no output buffer\n",
__func__, __LINE__);
break;
if (buf_out_free[i]) {
msm_gemini_core_we_buf_update(buf_out_free[i]);
} else if (i == 1) {
/* set the pong to same address as ping */
buf_out_free[0]->y_len >>= 1;
buf_out_free[0]->y_buffer_addr +=
buf_out_free[0]->y_len;
msm_gemini_core_we_buf_update(buf_out_free[0]);
/*
* since ping and pong are same buf
* release only once
*/
release_buf = 0;
} else {
GMN_DBG("%s:%d] no output buffer\n",
__func__, __LINE__);
break;
}
}
for (i = 0; i < 2; i++)
kfree(buf_out_free[i]);
} else {
struct msm_gemini_core_buf out_buf;
/*
* Since the same buffer is fragmented, p2v need not be
* called for all the buffers
*/
release_buf = 0;
if (!pgmn_dev->out_buf_set) {
GMN_PR_ERR("%s:%d] output buffer not set",
__func__, __LINE__);
return -EFAULT;
}
/* configure ping */
rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
if (rc) {
GMN_PR_ERR("%s:%d] no output buffer for ping",
__func__, __LINE__);
return rc;
}
msm_gemini_core_we_buf_update(&out_buf);
/* configure pong */
rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
if (rc) {
GMN_DBG("%s:%d] no output buffer for pong",
__func__, __LINE__);
/* fall through to configure same buffer */
}
msm_gemini_core_we_buf_update(&out_buf);
msm_gemini_io_dump(0x150);
}
for (i = 0; i < 2; i++)
kfree(buf_out_free[i]);
rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, arg);
GMN_DBG("%s:%d]\n", __func__, __LINE__);
return rc;
@@ -746,12 +945,22 @@ int msm_gemini_ioctl_reset(struct msm_gemini_device *pgmn_dev,
return rc;
}
int msm_gemini_ioctl_test_dump_region(struct msm_gemini_device *pgmn_dev,
unsigned long arg)
int msm_gemini_ioctl_set_outmode(struct msm_gemini_device *pgmn_dev,
void * __user arg)
{
GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
msm_gemini_hw_region_dump(arg);
return 0;
int rc = 0;
enum msm_gmn_out_mode mode;
if (copy_from_user(&mode, arg, sizeof(mode))) {
GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
return -EFAULT;
}
GMN_DBG("%s:%d] mode %d", __func__, __LINE__, mode);
if ((mode == MSM_GMN_OUTMODE_FRAGMENTED)
|| (mode == MSM_GMN_OUTMODE_SINGLE))
pgmn_dev->out_mode = mode;
return rc;
}
long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
@@ -790,8 +999,12 @@ long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
break;
case MSM_GMN_IOCTL_OUTPUT_BUF_ENQUEUE:
rc = msm_gemini_output_buf_enqueue(pgmn_dev,
(void __user *) arg);
if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED)
rc = msm_gemini_output_buf_enqueue(pgmn_dev,
(void __user *) arg);
else
rc = msm_gemini_set_output_buf(pgmn_dev,
(void __user *) arg);
break;
case MSM_GMN_IOCTL_OUTPUT_GET:
@@ -818,8 +1031,8 @@ long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
break;
case MSM_GMN_IOCTL_TEST_DUMP_REGION:
rc = msm_gemini_ioctl_test_dump_region(pgmn_dev, arg);
case MSM_GMN_IOCTL_SET_MODE:
rc = msm_gemini_ioctl_set_outmode(pgmn_dev, (void __user *)arg);
break;
default:
@@ -1,4 +1,4 @@
/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010,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
@@ -74,6 +74,16 @@ struct msm_gemini_device {
struct msm_gemini_q input_buf_q;
struct v4l2_subdev subdev;
enum msm_gmn_out_mode out_mode;
/* single out mode parameters */
struct msm_gemini_hw_buf out_buf;
int out_offset;
int out_buf_set;
int max_out_size;
int out_frag_cnt;
uint32_t bus_perf_client;
};
int __msm_gemini_open(struct msm_gemini_device *pgmn_dev);
+9
View File
@@ -51,10 +51,19 @@
#define MSM_GMN_IOCTL_TEST_DUMP_REGION \
_IOW(MSM_GMN_IOCTL_MAGIC, 15, unsigned long)
#define MSM_GMN_IOCTL_SET_MODE \
_IOW(MSM_GMN_IOCTL_MAGIC, 16, enum msm_gmn_out_mode)
#define MSM_GEMINI_MODE_REALTIME_ENCODE 0
#define MSM_GEMINI_MODE_OFFLINE_ENCODE 1
#define MSM_GEMINI_MODE_REALTIME_ROTATION 2
#define MSM_GEMINI_MODE_OFFLINE_ROTATION 3
enum msm_gmn_out_mode {
MSM_GMN_OUTMODE_FRAGMENTED,
MSM_GMN_OUTMODE_SINGLE
};
struct msm_gemini_ctrl_cmd {
uint32_t type;
uint32_t len;