msm: camera: Initial commit for VFE 4.0

Add Image Signal Processor support for 8974 chipset.

Change-Id: Id1f425e59d48e490d5e426f5d056bca7c54b54a2
Signed-off-by: Kevin Chan <ktchan@codeaurora.org>
This commit is contained in:
Kevin Chan
2012-07-22 02:23:43 -07:00
committed by Stephen Boyd
parent 1f05823c6b
commit a3ca4db7ad
5 changed files with 5727 additions and 0 deletions

View File

@@ -16,4 +16,5 @@ endif
obj-$(CONFIG_ARCH_MSM_ARM11) += msm_vfe7x.o
obj-$(CONFIG_ARCH_QSD8X50) += msm_vfe8x.o msm_vfe8x_proc.o
obj-$(CONFIG_ARCH_MSM8960) += msm_vfe32.o
obj-$(CONFIG_ARCH_MSM8974) += msm_vfe40.o msm_vfe40_axi.o
obj-$(CONFIG_MSM_CAMERA_V4L2) += msm_vfe_stats_buf.o

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,812 @@
/* Copyright (c) 2012, Code Aurora Forum. 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/module.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/atomic.h>
#include <linux/regulator/consumer.h>
#include <linux/clk.h>
#include <mach/irqs.h>
#include <mach/camera.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/msm_isp.h>
#include "msm.h"
#include "msm_vfe40.h"
static int msm_axi_subdev_s_crystal_freq(struct v4l2_subdev *sd,
u32 freq, u32 flags)
{
int rc = 0;
int round_rate;
struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
round_rate = clk_round_rate(axi_ctrl->vfe_clk[0], freq);
if (rc < 0) {
pr_err("%s: clk_round_rate failed %d\n",
__func__, rc);
return rc;
}
axi_ctrl->share_ctrl->vfe_clk_rate = round_rate;
rc = clk_set_rate(axi_ctrl->vfe_clk[0], round_rate);
if (rc < 0)
pr_err("%s: clk_set_rate failed %d\n",
__func__, rc);
return rc;
}
void axi_start(struct axi_ctrl_t *axi_ctrl)
{
switch (axi_ctrl->share_ctrl->operation_mode) {
case VFE_OUTPUTS_PREVIEW:
case VFE_OUTPUTS_PREVIEW_AND_VIDEO:
if (axi_ctrl->share_ctrl->outpath.output_mode &
VFE40_OUTPUT_MODE_PRIMARY) {
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out0.ch0]);
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out0.ch1]);
} else if (axi_ctrl->share_ctrl->outpath.output_mode &
VFE40_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out0.ch0]);
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out0.ch1]);
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out0.ch2]);
}
break;
default:
if (axi_ctrl->share_ctrl->outpath.output_mode &
VFE40_OUTPUT_MODE_SECONDARY) {
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out1.ch0]);
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out1.ch1]);
} else if (axi_ctrl->share_ctrl->outpath.output_mode &
VFE40_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out1.ch0]);
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out1.ch1]);
msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
vfe40_AXI_WM_CFG[axi_ctrl->
share_ctrl->outpath.out1.ch2]);
}
break;
}
}
void axi_stop(struct axi_ctrl_t *axi_ctrl)
{
uint8_t axiBusyFlag = true;
/* axi halt command. */
msm_camera_io_w(AXI_HALT,
axi_ctrl->share_ctrl->vfebase + VFE_AXI_CMD);
wmb();
while (axiBusyFlag) {
if (msm_camera_io_r(
axi_ctrl->share_ctrl->vfebase + VFE_AXI_STATUS) & 0x1)
axiBusyFlag = false;
}
/* Ensure the write order while writing
to the command register using the barrier */
msm_camera_io_w_mb(AXI_HALT_CLEAR,
axi_ctrl->share_ctrl->vfebase + VFE_AXI_CMD);
/* after axi halt, then ok to apply global reset. */
/* enable reset_ack and async timer interrupt only while
stopping the pipeline.*/
msm_camera_io_w(0xf0000000,
axi_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_0);
msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
axi_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_1);
/* Ensure the write order while writing
to the command register using the barrier */
msm_camera_io_w_mb(VFE_RESET_UPON_STOP_CMD,
axi_ctrl->share_ctrl->vfebase + VFE_GLOBAL_RESET);
}
static int vfe40_config_axi(
struct axi_ctrl_t *axi_ctrl, int mode, uint32_t *ao)
{
uint32_t *ch_info;
uint32_t *axi_cfg = ao;
/* Update the corresponding write masters for each output*/
ch_info = axi_cfg + V40_AXI_CFG_LEN;
axi_ctrl->share_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out0.ch1 =
0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out0.image_mode =
0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out1.ch1 =
0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out1.image_mode =
0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out2.ch1 =
0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out2.ch2 = 0x0000FFFF & *ch_info++;
switch (mode) {
case OUTPUT_PRIM:
axi_ctrl->share_ctrl->outpath.output_mode =
VFE40_OUTPUT_MODE_PRIMARY;
break;
case OUTPUT_PRIM_ALL_CHNLS:
axi_ctrl->share_ctrl->outpath.output_mode =
VFE40_OUTPUT_MODE_PRIMARY_ALL_CHNLS;
break;
case OUTPUT_PRIM|OUTPUT_SEC:
axi_ctrl->share_ctrl->outpath.output_mode =
VFE40_OUTPUT_MODE_PRIMARY;
axi_ctrl->share_ctrl->outpath.output_mode |=
VFE40_OUTPUT_MODE_SECONDARY;
break;
case OUTPUT_PRIM|OUTPUT_SEC_ALL_CHNLS:
axi_ctrl->share_ctrl->outpath.output_mode =
VFE40_OUTPUT_MODE_PRIMARY;
axi_ctrl->share_ctrl->outpath.output_mode |=
VFE40_OUTPUT_MODE_SECONDARY_ALL_CHNLS;
break;
case OUTPUT_PRIM_ALL_CHNLS|OUTPUT_SEC:
axi_ctrl->share_ctrl->outpath.output_mode =
VFE40_OUTPUT_MODE_PRIMARY_ALL_CHNLS;
axi_ctrl->share_ctrl->outpath.output_mode |=
VFE40_OUTPUT_MODE_SECONDARY;
break;
default:
pr_err("%s Invalid AXI mode %d ", __func__, mode);
return -EINVAL;
}
msm_camera_io_w(*ao, axi_ctrl->share_ctrl->vfebase +
VFE_BUS_IO_FORMAT_CFG);
msm_camera_io_memcpy(axi_ctrl->share_ctrl->vfebase +
vfe40_cmd[VFE_CMD_AXI_OUT_CFG].offset, axi_cfg,
vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length - V40_AXI_CH_INF_LEN);
return 0;
}
static int msm_axi_config(struct v4l2_subdev *sd, void __user *arg)
{
struct msm_vfe_cfg_cmd cfgcmd;
struct msm_isp_cmd vfecmd;
int rc = 0;
struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
if (!axi_ctrl->share_ctrl->vfebase) {
pr_err("%s: base address unmapped\n", __func__);
return -EFAULT;
}
if (NULL != arg) {
if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
ERR_COPY_FROM_USER();
return -EFAULT;
}
}
if (NULL != cfgcmd.value) {
if (copy_from_user(&vfecmd,
(void __user *)(cfgcmd.value),
sizeof(vfecmd))) {
pr_err("%s %d: copy_from_user failed\n", __func__,
__LINE__);
return -EFAULT;
}
}
switch (cfgcmd.cmd_type) {
case CMD_AXI_CFG_PRIM: {
uint32_t *axio = NULL;
axio = kmalloc(vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length,
GFP_ATOMIC);
if (!axio) {
rc = -ENOMEM;
break;
}
if (copy_from_user(axio, (void __user *)(vfecmd.value),
vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
kfree(axio);
rc = -EFAULT;
break;
}
vfe40_config_axi(axi_ctrl, OUTPUT_PRIM, axio);
kfree(axio);
}
break;
case CMD_AXI_CFG_PRIM_ALL_CHNLS: {
uint32_t *axio = NULL;
axio = kmalloc(vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length,
GFP_ATOMIC);
if (!axio) {
rc = -ENOMEM;
break;
}
if (copy_from_user(axio, (void __user *)(vfecmd.value),
vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
kfree(axio);
rc = -EFAULT;
break;
}
vfe40_config_axi(axi_ctrl, OUTPUT_PRIM_ALL_CHNLS, axio);
kfree(axio);
}
break;
case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC: {
uint32_t *axio = NULL;
axio = kmalloc(vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length,
GFP_ATOMIC);
if (!axio) {
rc = -ENOMEM;
break;
}
if (copy_from_user(axio, (void __user *)(vfecmd.value),
vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
kfree(axio);
rc = -EFAULT;
break;
}
vfe40_config_axi(axi_ctrl, OUTPUT_PRIM|OUTPUT_SEC, axio);
kfree(axio);
}
break;
case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC_ALL_CHNLS: {
uint32_t *axio = NULL;
axio = kmalloc(vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length,
GFP_ATOMIC);
if (!axio) {
rc = -ENOMEM;
break;
}
if (copy_from_user(axio, (void __user *)(vfecmd.value),
vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
kfree(axio);
rc = -EFAULT;
break;
}
vfe40_config_axi(axi_ctrl,
OUTPUT_PRIM|OUTPUT_SEC_ALL_CHNLS, axio);
kfree(axio);
}
break;
case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC: {
uint32_t *axio = NULL;
axio = kmalloc(vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length,
GFP_ATOMIC);
if (!axio) {
rc = -ENOMEM;
break;
}
if (copy_from_user(axio, (void __user *)(vfecmd.value),
vfe40_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
kfree(axio);
rc = -EFAULT;
break;
}
vfe40_config_axi(axi_ctrl,
OUTPUT_PRIM_ALL_CHNLS|OUTPUT_SEC, axio);
kfree(axio);
}
break;
case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC_ALL_CHNLS:
pr_err("%s Invalid/Unsupported AXI configuration %x",
__func__, cfgcmd.cmd_type);
break;
case CMD_AXI_START:
axi_start(axi_ctrl);
break;
case CMD_AXI_STOP:
axi_stop(axi_ctrl);
break;
default:
pr_err("%s Unsupported AXI configuration %x ", __func__,
cfgcmd.cmd_type);
break;
}
return rc;
}
static struct msm_free_buf *vfe40_check_free_buffer(
int id, int path, struct axi_ctrl_t *axi_ctrl)
{
struct vfe40_output_ch *outch = NULL;
struct msm_free_buf *b = NULL;
uint32_t image_mode = 0;
if (path == VFE_MSG_OUTPUT_PRIMARY)
image_mode = axi_ctrl->share_ctrl->outpath.out0.image_mode;
else
image_mode = axi_ctrl->share_ctrl->outpath.out1.image_mode;
vfe40_subdev_notify(id, path, image_mode,
&axi_ctrl->subdev, axi_ctrl->share_ctrl);
outch = vfe40_get_ch(path, axi_ctrl->share_ctrl);
if (outch->free_buf.ch_paddr[0])
b = &outch->free_buf;
return b;
}
static void vfe_send_outmsg(
struct axi_ctrl_t *axi_ctrl, uint8_t msgid,
uint32_t ch0_paddr, uint32_t ch1_paddr,
uint32_t ch2_paddr, uint32_t image_mode)
{
struct isp_msg_output msg;
msg.output_id = msgid;
msg.buf.image_mode = image_mode;
msg.buf.ch_paddr[0] = ch0_paddr;
msg.buf.ch_paddr[1] = ch1_paddr;
msg.buf.ch_paddr[2] = ch2_paddr;
msg.frameCounter = axi_ctrl->share_ctrl->vfeFrameId;
v4l2_subdev_notify(&axi_ctrl->subdev,
NOTIFY_VFE_MSG_OUT,
&msg);
return;
}
static void vfe40_process_output_path_irq_0(
struct axi_ctrl_t *axi_ctrl)
{
uint32_t ping_pong;
uint32_t ch0_paddr, ch1_paddr, ch2_paddr;
uint8_t out_bool = 0;
struct msm_free_buf *free_buf = NULL;
free_buf = vfe40_check_free_buffer(VFE_MSG_OUTPUT_IRQ,
VFE_MSG_OUTPUT_PRIMARY, axi_ctrl);
/* we render frames in the following conditions:
1. Continuous mode and the free buffer is avaialable.
2. In snapshot shot mode, free buffer is not always available.
when pending snapshot count is <=1, then no need to use
free buffer.
*/
out_bool = (
(axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_THUMB_AND_MAIN ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_MAIN_AND_THUMB ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_THUMB_AND_JPEG ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_JPEG_AND_THUMB ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_RAW ||
axi_ctrl->share_ctrl->liveshot_state ==
VFE_STATE_STARTED ||
axi_ctrl->share_ctrl->liveshot_state ==
VFE_STATE_STOP_REQUESTED ||
axi_ctrl->share_ctrl->liveshot_state ==
VFE_STATE_STOPPED) &&
(axi_ctrl->share_ctrl->vfe_capture_count <= 1)) ||
free_buf;
if (out_bool) {
ping_pong = msm_camera_io_r(axi_ctrl->share_ctrl->vfebase +
VFE_BUS_PING_PONG_STATUS);
/* Channel 0*/
ch0_paddr = vfe40_get_ch_addr(
ping_pong, axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out0.ch0);
/* Channel 1*/
ch1_paddr = vfe40_get_ch_addr(
ping_pong, axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out0.ch1);
/* Channel 2*/
ch2_paddr = vfe40_get_ch_addr(
ping_pong, axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out0.ch2);
CDBG("output path 0, ch0 = 0x%x, ch1 = 0x%x, ch2 = 0x%x\n",
ch0_paddr, ch1_paddr, ch2_paddr);
if (free_buf) {
/* Y channel */
vfe40_put_ch_addr(ping_pong,
axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out0.ch0,
free_buf->ch_paddr[0]);
/* Chroma channel */
vfe40_put_ch_addr(ping_pong,
axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out0.ch1,
free_buf->ch_paddr[1]);
if (free_buf->num_planes > 2)
vfe40_put_ch_addr(ping_pong,
axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out0.ch2,
free_buf->ch_paddr[2]);
}
if (axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_THUMB_AND_MAIN ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_MAIN_AND_THUMB ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_THUMB_AND_JPEG ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_JPEG_AND_THUMB ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_RAW ||
axi_ctrl->share_ctrl->liveshot_state ==
VFE_STATE_STOPPED)
axi_ctrl->share_ctrl->outpath.out0.capture_cnt--;
vfe_send_outmsg(axi_ctrl,
MSG_ID_OUTPUT_PRIMARY, ch0_paddr,
ch1_paddr, ch2_paddr,
axi_ctrl->share_ctrl->outpath.out0.image_mode);
if (axi_ctrl->share_ctrl->liveshot_state == VFE_STATE_STOPPED)
axi_ctrl->share_ctrl->liveshot_state = VFE_STATE_IDLE;
} else {
axi_ctrl->share_ctrl->outpath.out0.frame_drop_cnt++;
CDBG("path_irq_0 - no free buffer!\n");
}
}
static void vfe40_process_output_path_irq_1(
struct axi_ctrl_t *axi_ctrl)
{
uint32_t ping_pong;
uint32_t ch0_paddr, ch1_paddr, ch2_paddr;
/* this must be snapshot main image output. */
uint8_t out_bool = 0;
struct msm_free_buf *free_buf = NULL;
free_buf = vfe40_check_free_buffer(VFE_MSG_OUTPUT_IRQ,
VFE_MSG_OUTPUT_SECONDARY, axi_ctrl);
out_bool = ((axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_THUMB_AND_MAIN ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_MAIN_AND_THUMB ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_RAW ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_JPEG_AND_THUMB) &&
(axi_ctrl->share_ctrl->vfe_capture_count <= 1)) ||
free_buf;
if (out_bool) {
ping_pong = msm_camera_io_r(axi_ctrl->share_ctrl->vfebase +
VFE_BUS_PING_PONG_STATUS);
/* Y channel */
ch0_paddr = vfe40_get_ch_addr(ping_pong,
axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out1.ch0);
/* Chroma channel */
ch1_paddr = vfe40_get_ch_addr(ping_pong,
axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out1.ch1);
ch2_paddr = vfe40_get_ch_addr(ping_pong,
axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out1.ch2);
CDBG("%s ch0 = 0x%x, ch1 = 0x%x, ch2 = 0x%x\n",
__func__, ch0_paddr, ch1_paddr, ch2_paddr);
if (free_buf) {
/* Y channel */
vfe40_put_ch_addr(ping_pong,
axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out1.ch0,
free_buf->ch_paddr[0]);
/* Chroma channel */
vfe40_put_ch_addr(ping_pong,
axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out1.ch1,
free_buf->ch_paddr[1]);
if (free_buf->num_planes > 2)
vfe40_put_ch_addr(ping_pong,
axi_ctrl->share_ctrl->vfebase,
axi_ctrl->share_ctrl->outpath.out1.ch2,
free_buf->ch_paddr[2]);
}
if (axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_THUMB_AND_MAIN ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_MAIN_AND_THUMB ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_RAW ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_JPEG_AND_THUMB)
axi_ctrl->share_ctrl->outpath.out1.capture_cnt--;
vfe_send_outmsg(axi_ctrl,
MSG_ID_OUTPUT_SECONDARY, ch0_paddr,
ch1_paddr, ch2_paddr,
axi_ctrl->share_ctrl->outpath.out1.image_mode);
} else {
axi_ctrl->share_ctrl->outpath.out1.frame_drop_cnt++;
CDBG("path_irq_1 - no free buffer!\n");
}
}
static void msm_axi_process_irq(struct v4l2_subdev *sd, void *arg)
{
struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
uint32_t irqstatus = (uint32_t) arg;
if (!axi_ctrl->share_ctrl->vfebase) {
pr_err("%s: base address unmapped\n", __func__);
return;
}
/* next, check output path related interrupts. */
if (irqstatus &
VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK) {
CDBG("Image composite done 0 irq occured.\n");
vfe40_process_output_path_irq_0(axi_ctrl);
}
if (irqstatus &
VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK) {
CDBG("Image composite done 1 irq occured.\n");
vfe40_process_output_path_irq_1(axi_ctrl);
}
/* in snapshot mode if done then send
snapshot done message */
if (axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_THUMB_AND_MAIN ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_MAIN_AND_THUMB ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_THUMB_AND_JPEG ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_JPEG_AND_THUMB ||
axi_ctrl->share_ctrl->operation_mode ==
VFE_OUTPUTS_RAW) {
if ((axi_ctrl->share_ctrl->outpath.out0.capture_cnt == 0)
&& (axi_ctrl->share_ctrl->outpath.out1.
capture_cnt == 0)) {
msm_camera_io_w_mb(
CAMIF_COMMAND_STOP_IMMEDIATELY,
axi_ctrl->share_ctrl->vfebase +
VFE_CAMIF_COMMAND);
vfe40_send_isp_msg(&axi_ctrl->subdev,
axi_ctrl->share_ctrl->vfeFrameId,
MSG_ID_SNAPSHOT_DONE);
}
}
}
static int msm_axi_buf_cfg(struct v4l2_subdev *sd, void __user *arg)
{
struct msm_camvfe_params *vfe_params =
(struct msm_camvfe_params *)arg;
struct msm_vfe_cfg_cmd *cmd = vfe_params->vfe_cfg;
struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
void *data = vfe_params->data;
int rc = 0;
if (!axi_ctrl->share_ctrl->vfebase) {
pr_err("%s: base address unmapped\n", __func__);
return -EFAULT;
}
switch (cmd->cmd_type) {
case CMD_CONFIG_PING_ADDR: {
int path = *((int *)cmd->value);
struct vfe40_output_ch *outch =
vfe40_get_ch(path, axi_ctrl->share_ctrl);
outch->ping = *((struct msm_free_buf *)data);
}
break;
case CMD_CONFIG_PONG_ADDR: {
int path = *((int *)cmd->value);
struct vfe40_output_ch *outch =
vfe40_get_ch(path, axi_ctrl->share_ctrl);
outch->pong = *((struct msm_free_buf *)data);
}
break;
case CMD_CONFIG_FREE_BUF_ADDR: {
int path = *((int *)cmd->value);
struct vfe40_output_ch *outch =
vfe40_get_ch(path, axi_ctrl->share_ctrl);
outch->free_buf = *((struct msm_free_buf *)data);
}
break;
default:
pr_err("%s Unsupported AXI Buf config %x ", __func__,
cmd->cmd_type);
}
return rc;
};
static struct msm_cam_clk_info vfe40_clk_info[] = {
{"vfe_clk_src", 266670000},
{"camss_vfe_vfe_clk", -1},
{"camss_csi_vfe_clk", -1},
{"top_clk", -1},
{"iface_clk", -1},
{"bus_clk", -1},
};
int msm_axi_subdev_init(struct v4l2_subdev *sd,
struct msm_cam_media_controller *mctl)
{
int rc = 0;
struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
v4l2_set_subdev_hostdata(sd, mctl);
spin_lock_init(&axi_ctrl->tasklet_lock);
INIT_LIST_HEAD(&axi_ctrl->tasklet_q);
spin_lock_init(&axi_ctrl->share_ctrl->sd_notify_lock);
axi_ctrl->share_ctrl->vfebase = ioremap(axi_ctrl->vfemem->start,
resource_size(axi_ctrl->vfemem));
if (!axi_ctrl->share_ctrl->vfebase) {
rc = -ENOMEM;
pr_err("%s: vfe ioremap failed\n", __func__);
goto remap_failed;
}
if (axi_ctrl->fs_vfe == NULL) {
axi_ctrl->fs_vfe =
regulator_get(&axi_ctrl->pdev->dev, "vdd");
if (IS_ERR(axi_ctrl->fs_vfe)) {
pr_err("%s: Regulator FS_VFE get failed %ld\n",
__func__, PTR_ERR(axi_ctrl->fs_vfe));
axi_ctrl->fs_vfe = NULL;
goto fs_failed;
} else if (regulator_enable(axi_ctrl->fs_vfe)) {
pr_err("%s: Regulator FS_VFE enable failed\n",
__func__);
regulator_put(axi_ctrl->fs_vfe);
axi_ctrl->fs_vfe = NULL;
goto fs_failed;
}
}
rc = msm_cam_clk_enable(&axi_ctrl->pdev->dev, vfe40_clk_info,
axi_ctrl->vfe_clk, ARRAY_SIZE(vfe40_clk_info), 1);
if (rc < 0)
goto clk_enable_failed;
msm_camio_bus_scale_cfg(
mctl->sdata->pdata->cam_bus_scale_table, S_INIT);
msm_camio_bus_scale_cfg(
mctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
axi_ctrl->share_ctrl->register_total = VFE40_REGISTER_TOTAL;
enable_irq(axi_ctrl->vfeirq->start);
return rc;
clk_enable_failed:
regulator_disable(axi_ctrl->fs_vfe);
regulator_put(axi_ctrl->fs_vfe);
axi_ctrl->fs_vfe = NULL;
fs_failed:
iounmap(axi_ctrl->share_ctrl->vfebase);
axi_ctrl->share_ctrl->vfebase = NULL;
remap_failed:
disable_irq(axi_ctrl->vfeirq->start);
return rc;
}
void msm_axi_subdev_release(struct v4l2_subdev *sd)
{
struct msm_cam_media_controller *pmctl =
(struct msm_cam_media_controller *)v4l2_get_subdev_hostdata(sd);
struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
if (!axi_ctrl->share_ctrl->vfebase) {
pr_err("%s: base address unmapped\n", __func__);
return;
}
CDBG("%s, free_irq\n", __func__);
disable_irq(axi_ctrl->vfeirq->start);
tasklet_kill(&axi_ctrl->vfe40_tasklet);
msm_cam_clk_enable(&axi_ctrl->pdev->dev, vfe40_clk_info,
axi_ctrl->vfe_clk, ARRAY_SIZE(vfe40_clk_info), 0);
if (axi_ctrl->fs_vfe) {
regulator_disable(axi_ctrl->fs_vfe);
regulator_put(axi_ctrl->fs_vfe);
axi_ctrl->fs_vfe = NULL;
}
iounmap(axi_ctrl->share_ctrl->vfebase);
axi_ctrl->share_ctrl->vfebase = NULL;
if (atomic_read(&axi_ctrl->share_ctrl->irq_cnt))
pr_warning("%s, Warning IRQ Count not ZERO\n", __func__);
msm_camio_bus_scale_cfg(
pmctl->sdata->pdata->cam_bus_scale_table, S_EXIT);
}
static long msm_axi_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
int rc = -ENOIOCTLCMD;
switch (cmd) {
case VIDIOC_MSM_AXI_INIT:
rc = msm_axi_subdev_init(sd,
(struct msm_cam_media_controller *)arg);
break;
case VIDIOC_MSM_AXI_CFG:
rc = msm_axi_config(sd, arg);
break;
case VIDIOC_MSM_AXI_IRQ:
msm_axi_process_irq(sd, arg);
rc = 0;
break;
case VIDIOC_MSM_AXI_BUF_CFG:
msm_axi_buf_cfg(sd, arg);
rc = 0;
break;
case VIDIOC_MSM_AXI_RELEASE:
msm_axi_subdev_release(sd);
rc = 0;
break;
default:
pr_err("%s: command not found\n", __func__);
}
return rc;
}
static const struct v4l2_subdev_core_ops msm_axi_subdev_core_ops = {
.ioctl = msm_axi_subdev_ioctl,
};
static const struct v4l2_subdev_video_ops msm_axi_subdev_video_ops = {
.s_crystal_freq = msm_axi_subdev_s_crystal_freq,
};
static const struct v4l2_subdev_ops msm_axi_subdev_ops = {
.core = &msm_axi_subdev_core_ops,
.video = &msm_axi_subdev_video_ops,
};
static const struct v4l2_subdev_internal_ops msm_axi_internal_ops;
void vfe40_axi_probe(struct axi_ctrl_t *axi_ctrl)
{
struct msm_cam_subdev_info sd_info;
v4l2_subdev_init(&axi_ctrl->subdev, &msm_axi_subdev_ops);
axi_ctrl->subdev.internal_ops = &msm_axi_internal_ops;
axi_ctrl->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(axi_ctrl->subdev.name,
sizeof(axi_ctrl->subdev.name), "axi");
v4l2_set_subdevdata(&axi_ctrl->subdev, axi_ctrl);
sd_info.sdev_type = AXI_DEV;
sd_info.sd_index = axi_ctrl->pdev->id;
sd_info.irq_num = 0;
msm_cam_register_subdev_node(&axi_ctrl->subdev, &sd_info);
}

View File

@@ -220,6 +220,19 @@
#define VFE_CMD_STATS_BHIST_START 147
#define VFE_CMD_STATS_BHIST_STOP 148
#define VFE_CMD_RESET_2 149
#define VFE_CMD_FOV_ENC_CFG 150
#define VFE_CMD_FOV_VIEW_CFG 151
#define VFE_CMD_FOV_ENC_UPDATE 152
#define VFE_CMD_FOV_VIEW_UPDATE 153
#define VFE_CMD_SCALER_ENC_CFG 154
#define VFE_CMD_SCALER_VIEW_CFG 155
#define VFE_CMD_SCALER_ENC_UPDATE 156
#define VFE_CMD_SCALER_VIEW_UPDATE 157
#define VFE_CMD_COLORXFORM_ENC_CFG 158
#define VFE_CMD_COLORXFORM_VIEW_CFG 159
#define VFE_CMD_COLORXFORM_ENC_UPDATE 160
#define VFE_CMD_COLORXFORM_VIEW_UPDATE 161
#define VFE_CMD_TEST_GEN_CFG 162
struct msm_isp_cmd {
int32_t id;