blob: c3f20ef59679cd48a7a2383cec85ab0837ff1fb5 [file] [log] [blame]
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is core functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <mach/videonode.h>
#if defined(CONFIG_BUSFREQ_OPP) && defined(CONFIG_CPU_EXYNOS5250)
#include <mach/dev.h>
#endif
#include <media/exynos_mc.h>
#include <linux/cma.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/videodev2_exynos_camera.h>
#include "fimc-is-core.h"
#include "fimc-is-helper.h"
#include "fimc-is-param.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-misc.h"
#if defined(CONFIG_VIDEOBUF2_ION)
static void *is_vb_cookie;
void *buf_start;
#endif
static struct fimc_is_dev *to_fimc_is_dev_from_front_dev
(struct fimc_is_front_dev *front_dev)
{
return container_of(front_dev, struct fimc_is_dev, front);
}
static struct fimc_is_sensor_dev *to_fimc_is_sensor_dev
(struct v4l2_subdev *sdev)
{
return container_of(sdev, struct fimc_is_sensor_dev, sd);
}
static struct fimc_is_front_dev *to_fimc_is_front_dev(struct v4l2_subdev *sdev)
{
return container_of(sdev, struct fimc_is_front_dev, sd);
}
static struct fimc_is_back_dev *to_fimc_is_back_dev(struct v4l2_subdev *sdev)
{
return container_of(sdev, struct fimc_is_back_dev, sd);
}
static int fimc_is_sensor_s_stream(struct v4l2_subdev *sd, int enable)
{
dbg("%s\n", __func__);
return 0;
}
#if defined(CONFIG_VIDEOBUF2_CMA_PHYS)
void fimc_is_mem_cache_clean(const void *start_addr,
unsigned long size)
{
unsigned long paddr;
dmac_map_area(start_addr, size, DMA_TO_DEVICE);
/*
* virtual & phsical addrees mapped directly, so we can convert
* the address just using offset
*/
paddr = __pa((unsigned long)start_addr);
outer_clean_range(paddr, paddr + size);
}
void fimc_is_mem_cache_inv(const void *start_addr, unsigned long size)
{
unsigned long paddr;
paddr = __pa((unsigned long)start_addr);
outer_inv_range(paddr, paddr + size);
dmac_unmap_area(start_addr, size, DMA_FROM_DEVICE);
}
int fimc_is_init_mem(struct fimc_is_dev *dev)
{
struct cma_info mem_info;
char cma_name[16];
int err;
dbg("fimc_is_init_mem - ION\n");
sprintf(cma_name, "%s%d", "fimc_is", 0);
err = cma_info(&mem_info, &dev->pdev->dev, 0);
dbg("%s : [cma_info] start_addr : 0x%x, end_addr : 0x%x\n",
__func__, mem_info.lower_bound, mem_info.upper_bound);
dbg("total_size : 0x%x, free_size : 0x%x\n",
mem_info.total_size, mem_info.free_size);
if (err) {
dev_err(&dev->pdev->dev, "%s: get cma info failed\n", __func__);
return -EINVAL;
}
dev->mem.size = FIMC_IS_A5_MEM_SIZE;
dev->mem.base = (dma_addr_t)cma_alloc
(&dev->pdev->dev, cma_name, (size_t)dev->mem.size, 0);
dev->is_p_region =
(struct is_region *)(phys_to_virt(dev->mem.base +
FIMC_IS_A5_MEM_SIZE - FIMC_IS_REGION_SIZE));
memset((void *)dev->is_p_region, 0,
(unsigned long)sizeof(struct is_region));
fimc_is_mem_cache_clean((void *)dev->is_p_region,
FIMC_IS_REGION_SIZE+1);
dbg("ctrl->mem.size = 0x%x\n", dev->mem.size);
dbg("ctrl->mem.base = 0x%x\n", dev->mem.base);
return 0;
}
#elif defined(CONFIG_VIDEOBUF2_ION)
void fimc_is_mem_init_mem_cleanup(void *alloc_ctxes)
{
vb2_ion_destroy_context(alloc_ctxes);
}
void fimc_is_mem_resume(void *alloc_ctxes)
{
vb2_ion_attach_iommu(alloc_ctxes);
}
void fimc_is_mem_suspend(void *alloc_ctxes)
{
vb2_ion_detach_iommu(alloc_ctxes);
}
int fimc_is_cache_flush(struct fimc_is_dev *dev,
const void *start_addr, unsigned long size)
{
vb2_ion_sync_for_device(dev->mem.fw_cookie,
(unsigned long)start_addr - (unsigned long)dev->mem.kvaddr,
size, DMA_BIDIRECTIONAL);
return 0;
}
/* Allocate firmware */
int fimc_is_alloc_firmware(struct fimc_is_dev *dev)
{
void *fimc_is_bitproc_buf;
int ret;
dbg("Allocating memory for FIMC-IS firmware.\n");
fimc_is_bitproc_buf = vb2_ion_private_alloc(dev->alloc_ctx,
FIMC_IS_A5_MEM_SIZE +
SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF +
SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF +
SIZE_3DNR_INTERNAL_BUF * NUM_3DNR_INTERNAL_BUF +
SIZE_ISP_INTERNAL_BUF * NUM_ISP_INTERNAL_BUF);
if (IS_ERR(fimc_is_bitproc_buf)) {
fimc_is_bitproc_buf = 0;
err("Allocating bitprocessor buffer failed\n");
return -ENOMEM;
}
ret = vb2_ion_dma_address(fimc_is_bitproc_buf, &dev->mem.dvaddr);
if ((ret < 0) || (dev->mem.dvaddr & FIMC_IS_FW_BASE_MASK)) {
err("The base memory is not aligned to 64MB.\n");
vb2_ion_private_free(fimc_is_bitproc_buf);
dev->mem.dvaddr = 0;
fimc_is_bitproc_buf = 0;
return -EIO;
}
dbg("Device vaddr = %08x , size = %08x\n",
dev->mem.dvaddr, FIMC_IS_A5_MEM_SIZE);
dev->mem.kvaddr = vb2_ion_private_vaddr(fimc_is_bitproc_buf);
if (IS_ERR(dev->mem.kvaddr)) {
err("Bitprocessor memory remap failed\n");
vb2_ion_private_free(fimc_is_bitproc_buf);
dev->mem.dvaddr = 0;
fimc_is_bitproc_buf = 0;
return -EIO;
}
dbg("Virtual address for FW: %08lx\n",
(long unsigned int)dev->mem.kvaddr);
dev->mem.bitproc_buf = fimc_is_bitproc_buf;
dev->mem.fw_cookie = fimc_is_bitproc_buf;
is_vb_cookie = dev->mem.fw_cookie;
buf_start = dev->mem.kvaddr;
return 0;
}
void fimc_is_mem_cache_clean(const void *start_addr,
unsigned long size)
{
off_t offset;
if (start_addr < buf_start) {
err("Start address error\n");
return;
}
size--;
offset = start_addr - buf_start;
vb2_ion_sync_for_device(is_vb_cookie, offset, size, DMA_TO_DEVICE);
}
void fimc_is_mem_cache_inv(const void *start_addr, unsigned long size)
{
off_t offset;
if (start_addr < buf_start) {
err("Start address error\n");
return;
}
offset = start_addr - buf_start;
vb2_ion_sync_for_device(is_vb_cookie, offset, size, DMA_FROM_DEVICE);
}
int fimc_is_init_mem(struct fimc_is_dev *dev)
{
int ret;
dbg("fimc_is_init_mem - ION\n");
ret = fimc_is_alloc_firmware(dev);
if (ret) {
err("Couldn't alloc for FIMC-IS firmware\n");
return -EINVAL;
}
memset(dev->mem.kvaddr, 0,
FIMC_IS_A5_MEM_SIZE +
SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF +
SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF +
SIZE_3DNR_INTERNAL_BUF * NUM_3DNR_INTERNAL_BUF +
SIZE_ISP_INTERNAL_BUF * NUM_ISP_INTERNAL_BUF);
dev->is_p_region =
(struct is_region *)(dev->mem.kvaddr +
FIMC_IS_A5_MEM_SIZE - FIMC_IS_REGION_SIZE);
dev->is_shared_region =
(struct is_share_region *)(dev->mem.kvaddr +
FIMC_IS_SHARED_REGION_ADDR);
dev->mem.dvaddr_odc = (unsigned char *)(dev->mem.dvaddr +
FIMC_IS_A5_MEM_SIZE);
dev->mem.kvaddr_odc = dev->mem.kvaddr + FIMC_IS_A5_MEM_SIZE;
dev->mem.dvaddr_dis = (unsigned char *)
(dev->mem.dvaddr +
FIMC_IS_A5_MEM_SIZE +
SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF);
dev->mem.kvaddr_dis = dev->mem.kvaddr +
FIMC_IS_A5_MEM_SIZE +
SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF;
dev->mem.dvaddr_3dnr = (unsigned char *)
(dev->mem.dvaddr +
FIMC_IS_A5_MEM_SIZE +
SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF +
SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF);
dev->mem.kvaddr_3dnr = dev->mem.kvaddr +
FIMC_IS_A5_MEM_SIZE +
SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF +
SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF;
dev->mem.dvaddr_isp = (unsigned char *)
(dev->mem.dvaddr +
FIMC_IS_A5_MEM_SIZE +
SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF +
SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF +
SIZE_3DNR_INTERNAL_BUF * NUM_3DNR_INTERNAL_BUF);
dev->mem.kvaddr_isp = dev->mem.kvaddr +
FIMC_IS_A5_MEM_SIZE +
SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF +
SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF +
SIZE_3DNR_INTERNAL_BUF * NUM_3DNR_INTERNAL_BUF;
dev->mem.dvaddr_shared = (unsigned char *)dev->mem.dvaddr +
((unsigned char *)&dev->is_p_region->shared[0] -
dev->mem.kvaddr);
dev->mem.kvaddr_shared = dev->mem.kvaddr +
((unsigned char *)&dev->is_p_region->shared[0] -
dev->mem.kvaddr);
if (fimc_is_cache_flush(dev, (void *)dev->is_p_region, IS_PARAM_SIZE)) {
err("fimc_is_cache_flush-Err\n");
return -EINVAL;
}
dbg("fimc_is_init_mem3\n");
return 0;
}
#endif
static int fimc_is_request_firmware(struct fimc_is_dev *dev)
{
int ret;
struct firmware *fw_blob;
ret = request_firmware((const struct firmware **)&fw_blob,
FIMC_IS_FW, &dev->pdev->dev);
if (ret) {
dev_err(&dev->pdev->dev,
"could not load firmware (err=%d)\n", ret);
return -EINVAL;
}
#if defined(CONFIG_VIDEOBUF2_CMA_PHYS)
memcpy((void *)phys_to_virt(dev->mem.base),
fw_blob->data, fw_blob->size);
fimc_is_mem_cache_clean((void *)phys_to_virt(dev->mem.base),
fw_blob->size + 1);
#elif defined(CONFIG_VIDEOBUF2_ION)
if (dev->mem.bitproc_buf == 0) {
err("failed to load FIMC-IS F/W\n");
} else {
memcpy(dev->mem.kvaddr, fw_blob->data, fw_blob->size);
fimc_is_mem_cache_clean(
(void *)dev->mem.kvaddr, fw_blob->size + 1);
}
#endif
dbg("FIMC_IS F/W loaded successfully - size:%d\n",
fw_blob->size);
release_firmware(fw_blob);
return ret;
}
int fimc_is_load_fw(struct fimc_is_dev *dev)
{
int ret;
dbg("%s\n", __func__);
if (test_bit(IS_ST_IDLE, &dev->state)) {
/* 1. Load IS firmware */
ret = fimc_is_request_firmware(dev);
if (ret) {
err("failed to fimc_is_request_firmware (%d)\n", ret);
return -EINVAL;
}
set_bit(IS_ST_PWR_ON, &dev->state);
/* 3. A5 power on */
clear_bit(IS_ST_FW_DOWNLOADED, &dev->state);
fimc_is_hw_a5_power(dev, 1);
ret = wait_event_timeout(dev->irq_queue,
test_bit(IS_ST_FW_DOWNLOADED, &dev->state),
FIMC_IS_SHUTDOWN_TIMEOUT);
if (!ret) {
err("wait timeout A5 power on: %s\n", __func__);
return -EINVAL;
}
dbg("fimc_is_load_fw end\n");
} else {
dbg("IS FW was loaded before\n");
}
return 0;
}
static int fimc_is_load_setfile(struct fimc_is_dev *dev)
{
int ret;
struct firmware *fw_blob;
ret = request_firmware((const struct firmware **)&fw_blob,
FIMC_IS_SETFILE, &dev->pdev->dev);
if (ret) {
dev_err(&dev->pdev->dev,
"could not load firmware (err=%d)\n", ret);
return -EINVAL;
}
#if defined(CONFIG_VIDEOBUF2_CMA_PHYS)
memcpy((void *)phys_to_virt(dev->mem.base + dev->setfile.base),
fw_blob->data, fw_blob->size);
fimc_is_mem_cache_clean(
(void *)phys_to_virt(dev->mem.base + dev->setfile.base),
fw_blob->size + 1);
#elif defined(CONFIG_VIDEOBUF2_ION)
if (dev->mem.bitproc_buf == 0) {
err("failed to load setfile\n");
} else {
memcpy((dev->mem.kvaddr + dev->setfile.base),
fw_blob->data, fw_blob->size);
fimc_is_mem_cache_clean(
(void *)(dev->mem.kvaddr + dev->setfile.base),
fw_blob->size + 1);
}
#endif
dev->setfile.state = 1;
dbg("FIMC_IS setfile loaded successfully - size:%d\n",
fw_blob->size);
release_firmware(fw_blob);
dbg("A5 mem base = 0x%08x\n", dev->mem.base);
dbg("Setfile base = 0x%08x\n", dev->setfile.base);
return ret;
}
int fimc_is_init_set(struct fimc_is_dev *dev , u32 val)
{
int ret;
struct flite_frame f_frame;
#ifdef FIMC_IS_A5_DEBUG_ON
unsigned long debug_device = 0;
#endif
fimc_is_hw_diable_wdt(dev);
dev->sensor.sensor_type = val;
dev->sensor.id_dual = 0;
dev->setfile.sub_index = 0;
dbg("fimc_is_init\n");
if (test_bit(IS_ST_FW_DOWNLOADED, &dev->state)) {
/* Init sequence 1: Open sensor */
dbg("v4l2 : open sensor : %d\n", val);
/* set mipi & fimclite */
f_frame.o_width =
dev->video[FIMC_IS_VIDEO_NUM_SCALERC].frame.width + 16;
f_frame.o_height =
dev->video[FIMC_IS_VIDEO_NUM_SCALERC].frame.height + 12;
f_frame.offs_h = 0;
f_frame.offs_v = 0;
f_frame.width =
dev->video[FIMC_IS_VIDEO_NUM_SCALERC].frame.width + 16;
f_frame.height =
dev->video[FIMC_IS_VIDEO_NUM_SCALERC].frame.height + 12;
/*start mipi & fimclite*/
dbg("start fimclite(pos:%d) (port:%d)\n",
dev->sensor.id_position,
dev->pdata->sensor_info[dev->sensor.id_position]->flite_id);
start_fimc_lite(dev->pdata->
sensor_info[dev->sensor.id_position]->
flite_id, &f_frame);
mdelay(10);
dbg("start mipi (pos:%d) (port:%d)\n",
dev->sensor.id_position,
dev->pdata->
sensor_info[dev->sensor.id_position]->csi_id);
start_mipi_csi(dev->pdata->
sensor_info[dev->sensor.id_position]->
csi_id, &f_frame);
clear_bit(IS_ST_OPEN_SENSOR, &dev->state);
fimc_is_hw_open_sensor(dev, dev->sensor.id_dual, val);
ret = wait_event_timeout(dev->irq_queue,
test_bit(IS_ST_OPEN_SENSOR, &dev->state),
FIMC_IS_SHUTDOWN_TIMEOUT);
if (!ret) {
dev_err(&dev->pdev->dev,
"wait timeout:%s\n", __func__);
return -EBUSY;
}
if (dev->is_p_region->shared[MAX_SHARED_COUNT-1]
!= MAGIC_NUMBER)
dev_err(&dev->pdev->dev, "MAGIC NUMBER error\n");
dbg("v4l2 : setfile address\n");
fimc_is_hw_get_setfile_addr(dev);
ret = wait_event_timeout(dev->irq_queue,
test_bit(IS_ST_SETFILE_LOADED, &dev->state),
FIMC_IS_SHUTDOWN_TIMEOUT);
if (!ret) {
err("wait timeout - get setfile address\n");
/*fimc_is_hw_set_low_poweroff(dev, true);*/
return -EINVAL;
}
fimc_is_load_setfile(dev);
dbg(" fimc_is_load_setfile end\n");
clear_bit(IS_ST_SETFILE_LOADED, &dev->state);
fimc_is_hw_load_setfile(dev);
ret = wait_event_timeout(dev->irq_queue,
test_bit(IS_ST_SETFILE_LOADED, &dev->state),
FIMC_IS_SHUTDOWN_TIMEOUT);
if (!ret) {
err("wait timeout - get setfile address\n");
/*fimc_is_hw_set_low_poweroff(dev, true);*/
return -EINVAL;
}
dbg("v4l2 : Load set file end\n");
/* Debug only */
dbg("Parameter region addr = 0x%08x\n",
(unsigned int)(dev->is_p_region));
dev->frame_count = 0;
dbg("Stream Off\n");
clear_bit(IS_ST_STREAM_OFF, &dev->state);
fimc_is_hw_set_stream(dev, 0); /*stream off */
ret = wait_event_timeout(dev->irq_queue,
test_bit(IS_ST_STREAM_OFF, &dev->state),
FIMC_IS_SHUTDOWN_TIMEOUT);
if (!ret) {
dev_err(&dev->pdev->dev,
"wait timeout : %s\n", __func__);
return -EBUSY;
}
clear_bit(IS_ST_RUN, &dev->state);
/* 1. */
dbg("Default setting : preview_still\n");
dev->scenario_id = ISS_PREVIEW_STILL;
fimc_is_hw_set_init(dev);
fimc_is_hw_change_size(dev);
fimc_is_mem_cache_clean((void *)dev->is_p_region,
IS_PARAM_SIZE);
/* 1 */
set_bit(IS_ST_INIT_PREVIEW_STILL, &dev->state);
fimc_is_hw_set_param(dev);
ret = wait_event_timeout(dev->irq_queue,
test_bit(IS_ST_INIT_PREVIEW_VIDEO, &dev->state),
FIMC_IS_SHUTDOWN_TIMEOUT);
if (!ret) {
dev_err(&dev->pdev->dev,
"wait timeout : %s\n", __func__);
return -EBUSY;
}
#ifdef FIMC_IS_A5_DEBUG_ON
/*set_bit(FIMC_IS_DEBUG_MAIN, &debug_device);
set_bit(FIMC_IS_DEBUG_EC, &debug_device);
set_bit(FIMC_IS_DEBUG_SENSOR, &debug_device);
set_bit(FIMC_IS_DEBUG_ISP, &debug_device);
set_bit(FIMC_IS_DEBUG_DRC, &debug_device);
set_bit(FIMC_IS_DEBUG_FD, &debug_device);
set_bit(FIMC_IS_DEBUG_SDK, &debug_device);
set_bit(FIMC_IS_DEBUG_SCALERC, &debug_device);
set_bit(FIMC_IS_DEBUG_ODC, &debug_device);
set_bit(FIMC_IS_DEBUG_TDNR, &debug_device);
set_bit(FIMC_IS_DEBUG_SCALERP, &debug_device);
set_bit(FIMC_IS_DEBUG_DIS, &debug_device);
*/
fimc_is_hw_set_debug_level(dev, FIMC_IS_DEBUG_UART,
debug_device, FIMC_IS_A5_DEBUG_LEVEL);
#endif
clear_bit(IS_ST_STREAM_OFF, &dev->state);
set_bit(IS_ST_RUN, &dev->state);
dbg("Init sequence completed!! Ready to use\n");
}
return 0;
}
static int fimc_is_front_s_stream(struct v4l2_subdev *sd, int enable)
{
struct fimc_is_front_dev *front_dev = to_fimc_is_front_dev(sd);
struct fimc_is_dev *isp = to_fimc_is_dev_from_front_dev(front_dev);
int ret;
if (enable) {
printk(KERN_INFO "fimc_is_front_s_stream : ON\n");
} else {
printk(KERN_INFO "fimc_is_front_s_stream : OFF\n");
fimc_is_hw_subip_poweroff(isp);
ret = wait_event_timeout(isp->irq_queue,
!test_bit(FIMC_IS_PWR_ST_POWER_ON_OFF, &isp->power),
FIMC_IS_SHUTDOWN_TIMEOUT);
if (!ret) {
dev_err(&isp->pdev->dev,
"wait timeout : %s\n", __func__);
return -EBUSY;
}
fimc_is_hw_a5_power(isp, 0);
isp->state = 0;
isp->pipe_state = 0;
stop_mipi_csi(isp->pdata->sensor_info[0]->csi_id);
stop_mipi_csi(isp->pdata->sensor_info[1]->csi_id);
stop_fimc_lite(isp->pdata->sensor_info[0]->flite_id);
stop_fimc_lite(isp->pdata->sensor_info[1]->flite_id);
if (isp->pdata->clk_off) {
/* isp->pdata->clk_off(isp->pdev); */
} else {
err("#### failed to Clock On ####\n");
return -EINVAL;
}
set_bit(IS_ST_IDLE , &isp->state);
dbg("state(%d), pipe_state(%d)\n",
(int)isp->state, (int)isp->pipe_state);
}
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_back_s_stream(struct v4l2_subdev *sd, int enable)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_sensor_subdev_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_sensor_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_sensor_subdev_get_crop(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_sensor_subdev_set_crop(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_front_subdev_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_front_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_front_subdev_get_crop(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_front_subdev_set_crop(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_back_subdev_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_back_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_back_subdev_get_crop(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_back_subdev_set_crop(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_sensor_s_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_sensor_g_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_front_s_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_front_g_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_back_s_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_back_g_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
dbg("%s\n", __func__);
return 0;
}
static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = {
.get_fmt = fimc_is_sensor_subdev_get_fmt,
.set_fmt = fimc_is_sensor_subdev_set_fmt,
.get_crop = fimc_is_sensor_subdev_get_crop,
.set_crop = fimc_is_sensor_subdev_set_crop,
};
static struct v4l2_subdev_pad_ops fimc_is_front_pad_ops = {
.get_fmt = fimc_is_front_subdev_get_fmt,
.set_fmt = fimc_is_front_subdev_set_fmt,
.get_crop = fimc_is_front_subdev_get_crop,
.set_crop = fimc_is_front_subdev_set_crop,
};
static struct v4l2_subdev_pad_ops fimc_is_back_pad_ops = {
.get_fmt = fimc_is_back_subdev_get_fmt,
.set_fmt = fimc_is_back_subdev_set_fmt,
.get_crop = fimc_is_back_subdev_get_crop,
.set_crop = fimc_is_back_subdev_set_crop,
};
static struct v4l2_subdev_video_ops fimc_is_sensor_video_ops = {
.s_stream = fimc_is_sensor_s_stream,
};
static struct v4l2_subdev_video_ops fimc_is_front_video_ops = {
.s_stream = fimc_is_front_s_stream,
};
static struct v4l2_subdev_video_ops fimc_is_back_video_ops = {
.s_stream = fimc_is_back_s_stream,
};
static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = {
.s_ctrl = fimc_is_sensor_s_ctrl,
.g_ctrl = fimc_is_sensor_g_ctrl,
};
static struct v4l2_subdev_core_ops fimc_is_front_core_ops = {
.s_ctrl = fimc_is_front_s_ctrl,
.g_ctrl = fimc_is_front_g_ctrl,
};
static struct v4l2_subdev_core_ops fimc_is_back_core_ops = {
.s_ctrl = fimc_is_back_s_ctrl,
.g_ctrl = fimc_is_back_g_ctrl,
};
static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = {
.pad = &fimc_is_sensor_pad_ops,
.video = &fimc_is_sensor_video_ops,
.core = &fimc_is_sensor_core_ops,
};
static struct v4l2_subdev_ops fimc_is_front_subdev_ops = {
.pad = &fimc_is_front_pad_ops,
.video = &fimc_is_front_video_ops,
.core = &fimc_is_front_core_ops,
};
static struct v4l2_subdev_ops fimc_is_back_subdev_ops = {
.pad = &fimc_is_back_pad_ops,
.video = &fimc_is_back_video_ops,
.core = &fimc_is_back_core_ops,
};
static int fimc_is_sensor_init_formats(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_sensor_subdev_close(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_sensor_subdev_registered(struct v4l2_subdev *sd)
{
dbg("%s\n", __func__);
return 0;
}
static void fimc_is_sensor_subdev_unregistered(struct v4l2_subdev *sd)
{
dbg("%s\n", __func__);
}
static int fimc_is_front_init_formats(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_front_subdev_close(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_front_subdev_registered(struct v4l2_subdev *sd)
{
dbg("%s\n", __func__);
return 0;
}
static void fimc_is_front_subdev_unregistered(struct v4l2_subdev *sd)
{
dbg("%s\n", __func__);
}
static int fimc_is_back_init_formats(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_back_subdev_close(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_back_subdev_registered(struct v4l2_subdev *sd)
{
dbg("%s\n", __func__);
return 0;
}
static void fimc_is_back_subdev_unregistered(struct v4l2_subdev *sd)
{
dbg("%s\n", __func__);
}
static const struct v4l2_subdev_internal_ops
fimc_is_sensor_v4l2_internal_ops = {
.open = fimc_is_sensor_init_formats,
.close = fimc_is_sensor_subdev_close,
.registered = fimc_is_sensor_subdev_registered,
.unregistered = fimc_is_sensor_subdev_unregistered,
};
static const struct v4l2_subdev_internal_ops fimc_is_front_v4l2_internal_ops = {
.open = fimc_is_front_init_formats,
.close = fimc_is_front_subdev_close,
.registered = fimc_is_front_subdev_registered,
.unregistered = fimc_is_front_subdev_unregistered,
};
static const struct v4l2_subdev_internal_ops fimc_is_back_v4l2_internal_ops = {
.open = fimc_is_back_init_formats,
.close = fimc_is_back_subdev_close,
.registered = fimc_is_back_subdev_registered,
.unregistered = fimc_is_back_subdev_unregistered,
};
static int fimc_is_sensor_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct fimc_is_sensor_dev *fimc_is_sensor = to_fimc_is_sensor_dev(sd);
dbg("++%s\n", __func__);
dbg("local->index : %d\n", local->index);
dbg("media_entity_type(remote->entity) : %d\n",
media_entity_type(remote->entity));
switch (local->index | media_entity_type(remote->entity)) {
case FIMC_IS_SENSOR_PAD_SOURCE_FRONT | MEDIA_ENT_T_V4L2_SUBDEV:
if (flags & MEDIA_LNK_FL_ENABLED)
fimc_is_sensor->output = FIMC_IS_SENSOR_OUTPUT_FRONT;
else
fimc_is_sensor->output = FIMC_IS_SENSOR_OUTPUT_NONE;
break;
default:
v4l2_err(sd, "%s : ERR link\n", __func__);
return -EINVAL;
}
dbg("--%s\n", __func__);
return 0;
}
static int fimc_is_front_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct fimc_is_front_dev *fimc_is_front = to_fimc_is_front_dev(sd);
dbg("++%s\n", __func__);
dbg("local->index : %d\n", local->index);
dbg("media_entity_type(remote->entity) : %d\n",
media_entity_type(remote->entity));
switch (local->index | media_entity_type(remote->entity)) {
case FIMC_IS_FRONT_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
dbg("fimc_is_front sink pad\n");
if (flags & MEDIA_LNK_FL_ENABLED) {
if (fimc_is_front->input
!= FIMC_IS_FRONT_INPUT_NONE) {
dbg("BUSY\n");
return -EBUSY;
}
if (remote->index == FIMC_IS_SENSOR_PAD_SOURCE_FRONT)
fimc_is_front->input
= FIMC_IS_FRONT_INPUT_SENSOR;
} else {
fimc_is_front->input = FIMC_IS_FRONT_INPUT_NONE;
}
break;
case FIMC_IS_FRONT_PAD_SOURCE_BACK | MEDIA_ENT_T_V4L2_SUBDEV:
if (flags & MEDIA_LNK_FL_ENABLED)
fimc_is_front->output |= FIMC_IS_FRONT_OUTPUT_BACK;
else
fimc_is_front->output = FIMC_IS_FRONT_OUTPUT_NONE;
break;
case FIMC_IS_FRONT_PAD_SOURCE_BAYER | MEDIA_ENT_T_DEVNODE:
if (flags & MEDIA_LNK_FL_ENABLED)
fimc_is_front->output |= FIMC_IS_FRONT_OUTPUT_BAYER;
else
fimc_is_front->output = FIMC_IS_FRONT_OUTPUT_NONE;
break;
case FIMC_IS_FRONT_PAD_SOURCE_SCALERC | MEDIA_ENT_T_DEVNODE:
if (flags & MEDIA_LNK_FL_ENABLED)
fimc_is_front->output |= FIMC_IS_FRONT_OUTPUT_SCALERC;
else
fimc_is_front->output = FIMC_IS_FRONT_OUTPUT_NONE;
break;
default:
v4l2_err(sd, "%s : ERR link\n", __func__);
return -EINVAL;
}
dbg("--%s\n", __func__);
return 0;
}
static int fimc_is_back_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct fimc_is_back_dev *fimc_is_back = to_fimc_is_back_dev(sd);
dbg("++%s\n", __func__);
switch (local->index | media_entity_type(remote->entity)) {
case FIMC_IS_BACK_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
dbg("fimc_is_back sink pad\n");
if (flags & MEDIA_LNK_FL_ENABLED) {
if (fimc_is_back->input != FIMC_IS_BACK_INPUT_NONE) {
dbg("BUSY\n");
return -EBUSY;
}
if (remote->index == FIMC_IS_FRONT_PAD_SOURCE_BACK)
fimc_is_back->input = FIMC_IS_BACK_INPUT_FRONT;
} else {
fimc_is_back->input = FIMC_IS_FRONT_INPUT_NONE;
}
break;
case FIMC_IS_BACK_PAD_SOURCE_3DNR | MEDIA_ENT_T_DEVNODE:
if (flags & MEDIA_LNK_FL_ENABLED)
fimc_is_back->output |= FIMC_IS_BACK_OUTPUT_3DNR;
else
fimc_is_back->output = FIMC_IS_FRONT_OUTPUT_NONE;
break;
case FIMC_IS_BACK_PAD_SOURCE_SCALERP | MEDIA_ENT_T_DEVNODE:
if (flags & MEDIA_LNK_FL_ENABLED)
fimc_is_back->output |= FIMC_IS_BACK_OUTPUT_SCALERP;
else
fimc_is_back->output = FIMC_IS_FRONT_OUTPUT_NONE;
break;
default:
v4l2_err(sd, "%s : ERR link\n", __func__);
return -EINVAL;
}
dbg("--%s\n", __func__);
return 0;
}
static const struct media_entity_operations fimc_is_sensor_media_ops = {
.link_setup = fimc_is_sensor_link_setup,
};
static const struct media_entity_operations fimc_is_front_media_ops = {
.link_setup = fimc_is_front_link_setup,
};
static const struct media_entity_operations fimc_is_back_media_ops = {
.link_setup = fimc_is_back_link_setup,
};
int fimc_is_pipeline_s_stream_preview(struct media_entity *start_entity, int on)
{
struct media_pad *pad = &start_entity->pads[0];
struct v4l2_subdev *back_sd;
struct v4l2_subdev *front_sd;
struct v4l2_subdev *sensor_sd;
int ret;
dbg("--%s\n", __func__);
pad = media_entity_remote_source(pad);
if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV
|| pad == NULL)
dbg("cannot find back entity\n");
back_sd = media_entity_to_v4l2_subdev(pad->entity);
pad = &pad->entity->pads[0];
pad = media_entity_remote_source(pad);
if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV
|| pad == NULL)
dbg("cannot find front entity\n");
front_sd = media_entity_to_v4l2_subdev(pad->entity);
pad = &pad->entity->pads[0];
pad = media_entity_remote_source(pad);
if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV
|| pad == NULL)
dbg("cannot find sensor entity\n");
sensor_sd = media_entity_to_v4l2_subdev(pad->entity);
if (on) {
ret = v4l2_subdev_call(sensor_sd, video, s_stream, 1);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
ret = v4l2_subdev_call(front_sd, video, s_stream, 1);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
ret = v4l2_subdev_call(back_sd, video, s_stream, 1);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
} else {
ret = v4l2_subdev_call(back_sd, video, s_stream, 0);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
ret = v4l2_subdev_call(front_sd, video, s_stream, 0);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
ret = v4l2_subdev_call(sensor_sd, video, s_stream, 0);
}
return ret == -ENOIOCTLCMD ? 0 : ret;
}
static int fimc_is_suspend(struct device *dev)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_resume(struct device *dev)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct fimc_is_dev *isp
= (struct fimc_is_dev *)platform_get_drvdata(pdev);
return fimc_is_hw_a5_power_off(isp);
}
static int fimc_is_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct fimc_is_dev *isp
= (struct fimc_is_dev *)platform_get_drvdata(pdev);
return fimc_is_hw_a5_power_on(isp);
}
static int fimc_is_get_md_callback(struct device *dev, void *p)
{
struct exynos_md **md_list = p;
struct exynos_md *md = NULL;
md = dev_get_drvdata(dev);
if (md)
*(md_list + md->id) = md;
return 0; /* non-zero value stops iteration */
}
static struct exynos_md *fimc_is_get_md(enum mdev_node node)
{
struct device_driver *drv;
struct exynos_md *md[MDEV_MAX_NUM] = {NULL,};
int ret;
drv = driver_find(MDEV_MODULE_NAME, &platform_bus_type);
if (!drv)
return ERR_PTR(-ENODEV);
ret = driver_for_each_device(drv, NULL, &md[0],
fimc_is_get_md_callback);
return ret ? NULL : md[node];
}
static unsigned int fimc_is_get_intr_position(unsigned int intr_status)
{
int i;
for (i = 0; i < 5; i++)
if (intr_status & (1<<i))
return i;
return 0;
}
static irqreturn_t fimc_is_irq_handler(int irq, void *dev_id)
{
struct fimc_is_dev *dev = dev_id;
int buf_index;
unsigned int intr_status, intr_pos;
intr_status = readl(dev->regs + INTSR1);
intr_pos = fimc_is_get_intr_position(intr_status);
if (intr_pos == INTR_GENERAL) {
dev->i2h_cmd.cmd = readl(dev->regs + ISSR10);
/* Read ISSR10 ~ ISSR15 */
switch (dev->i2h_cmd.cmd) {
case IHC_GET_SENSOR_NUMBER:
dbg("IHC_GET_SENSOR_NUMBER\n");
fimc_is_hw_get_param(dev, 1);
dbg("ISP - FW version - %d\n", dev->i2h_cmd.arg[0]);
dev->fw.ver = dev->i2h_cmd.arg[0];
fimc_is_hw_wait_intmsr0_intmsd0(dev);
fimc_is_hw_set_sensor_num(dev);
break;
case IHC_SET_SHOT_MARK:
fimc_is_hw_get_param(dev, 3);
break;
case IHC_SET_FACE_MARK:
fimc_is_hw_get_param(dev, 2);
break;
case IHC_NOT_READY:
break;
case IHC_AA_DONE:
fimc_is_hw_get_param(dev, 3);
break;
case ISR_DONE:
fimc_is_hw_get_param(dev, 3);
break;
case ISR_NDONE:
fimc_is_hw_get_param(dev, 4);
/* fimc_is_fw_clear_insr1(dev); */
break;
}
/* Just clear the interrupt pending bits. */
fimc_is_fw_clear_irq1(dev, intr_pos);
switch (dev->i2h_cmd.cmd) {
case IHC_GET_SENSOR_NUMBER:
fimc_is_hw_set_intgr0_gd0(dev);
set_bit(IS_ST_FW_DOWNLOADED, &dev->state);
break;
case IHC_SET_SHOT_MARK:
break;
case IHC_SET_FACE_MARK:
dbg("IHC_SET_FACE_MARK - %d, %d\n",
dev->i2h_cmd.arg[0],
dev->i2h_cmd.arg[1]);
dev->fd_header.count = dev->i2h_cmd.arg[0];
dev->fd_header.index = dev->i2h_cmd.arg[1];
/* Implementation of AF with face */
if (dev->af.mode == IS_FOCUS_MODE_CONTINUOUS &&
dev->af.af_state == FIMC_IS_AF_LOCK) {
fimc_is_af_face(dev);
} else if (dev->af.mode == IS_FOCUS_MODE_FACEDETECT) {
/* Using face information once only */
fimc_is_af_face(dev);
dev->af.mode = IS_FOCUS_MODE_IDLE;
}
break;
case IHC_AA_DONE:
switch (dev->i2h_cmd.arg[0]) {
/* SEARCH: Occurs when search is
* requested at continuous AF */
case 2:
break;
/* INFOCUS: Occurs when focus is found. */
case 3:
if (dev->af.af_state == FIMC_IS_AF_RUNNING)
dev->af.af_state = FIMC_IS_AF_LOCK;
dev->af.af_lock_state = 0x2;
break;
/* OUTOFFOCUS: Occurs when focus is not found. */
case 4:
if (dev->af.af_state == FIMC_IS_AF_RUNNING)
dev->af.af_state = FIMC_IS_AF_LOCK;
dev->af.af_lock_state = 0x1;
break;
}
break;
case IHC_NOT_READY:
err("Init Sequnce Error- IS will be turned off!!");
break;
case ISR_DONE:
dbg("ISR_DONE - %d\n", dev->i2h_cmd.arg[0]);
switch (dev->i2h_cmd.arg[0]) {
case HIC_PREVIEW_STILL:
case HIC_PREVIEW_VIDEO:
case HIC_CAPTURE_STILL:
case HIC_CAPTURE_VIDEO:
if (test_and_clear_bit(IS_ST_CHANGE_MODE,
&dev->state)) {
dev->sensor.offset_x
= dev->i2h_cmd.arg[1];
dev->sensor.offset_y
= dev->i2h_cmd.arg[2];
set_bit(IS_ST_CHANGE_MODE_DONE,
&dev->state);
}
break;
case HIC_STREAM_ON:
clear_bit(IS_ST_CHANGE_MODE_DONE, &dev->state);
set_bit(IS_ST_STREAM_ON, &dev->state);
break;
case HIC_STREAM_OFF:
set_bit(IS_ST_STREAM_OFF, &dev->state);
set_bit(IS_ST_RUN, &dev->state);
break;
case HIC_SET_PARAMETER:
dev->p_region_index1 = 0;
dev->p_region_index2 = 0;
atomic_set(&dev->p_region_num, 0);
/* FW bug - should be removed*/
if (dev->i2h_cmd.arg[1] == 0 &&
dev->i2h_cmd.arg[2] == 0)
break;
set_bit(IS_ST_BLOCK_CMD_CLEARED, &dev->state);
if (test_bit(PARAM_ISP_AA,
(void *)&dev->i2h_cmd.arg[1]) &&
(dev->af.af_state
== FIMC_IS_AF_SETCONFIG)) {
dev->af.af_state = FIMC_IS_AF_RUNNING;
} else if (test_bit(PARAM_ISP_AA,
(void *)&dev->i2h_cmd.arg[1]) &&
dev->af.af_state == FIMC_IS_AF_ABORT) {
dev->af.af_state = FIMC_IS_AF_IDLE;
}
if (test_bit(IS_ST_INIT_PREVIEW_STILL,
&dev->state))
set_bit(IS_ST_INIT_PREVIEW_VIDEO,
&dev->state);
else {
clear_bit(IS_ST_SET_PARAM, &dev->state);
set_bit(IS_ST_RUN, &dev->state);
}
break;
case HIC_GET_PARAMETER:
break;
case HIC_SET_TUNE:
break;
case HIC_GET_STATUS:
break;
case HIC_OPEN_SENSOR:
set_bit(IS_ST_OPEN_SENSOR, &dev->state);
dbg("reply HIC_OPEN_SENSOR");
break;
case HIC_CLOSE_SENSOR:
clear_bit(IS_ST_OPEN_SENSOR, &dev->state);
dev->sensor.id_dual = 0;
break;
case HIC_POWER_DOWN:
clear_bit(FIMC_IS_PWR_ST_POWER_ON_OFF,
&dev->power);
break;
case HIC_GET_SET_FILE_ADDR:
dev->setfile.base = dev->i2h_cmd.arg[1];
set_bit(IS_ST_SETFILE_LOADED, &dev->state);
break;
case HIC_LOAD_SET_FILE:
set_bit(IS_ST_SETFILE_LOADED, &dev->state);
}
break;
case ISR_NDONE:
err("ISR_NDONE - %d: %d\n", dev->i2h_cmd.arg[0],
dev->i2h_cmd.arg[1]);
switch (dev->i2h_cmd.arg[1]) {
case IS_ERROR_SET_PARAMETER:
fimc_is_mem_cache_inv((void *)dev->is_p_region,
IS_PARAM_SIZE);
break;
}
break;
}
} else if (intr_pos == INTR_FRAME_DONE_ISP) {
dev->i2h_cmd.arg[0] = readl(dev->regs + ISSR20);
dev->i2h_cmd.arg[1] = readl(dev->regs + ISSR21);
dev->i2h_cmd.arg[2] = readl(dev->regs + ISSR22);
fimc_is_fw_clear_irq1(dev, intr_pos);
buf_index = dev->i2h_cmd.arg[2];
dbg("Bayer returned buf index : %d\n", buf_index);
vb2_buffer_done(dev->video[FIMC_IS_VIDEO_NUM_BAYER].
vbq.bufs[buf_index], VB2_BUF_STATE_DONE);
fimc_is_hw_update_bufmask(dev, FIMC_IS_VIDEO_NUM_BAYER);
} else if (intr_pos == INTR_FRAME_DONE_SCALERC) {
dev->i2h_cmd.arg[0] = readl(dev->regs + ISSR28);
dev->i2h_cmd.arg[1] = readl(dev->regs + ISSR29);
dev->i2h_cmd.arg[2] = readl(dev->regs + ISSR30);
fimc_is_fw_clear_irq1(dev, intr_pos);
buf_index = dev->i2h_cmd.arg[2];
dbg("ScalerC returned buf index : %d\n", buf_index);
vb2_buffer_done(dev->video[FIMC_IS_VIDEO_NUM_SCALERC].
vbq.bufs[buf_index], VB2_BUF_STATE_DONE);
fimc_is_hw_update_bufmask(dev, FIMC_IS_VIDEO_NUM_SCALERC);
} else if (intr_pos == INTR_FRAME_DONE_TDNR) {
dev->i2h_cmd.arg[0] = readl(dev->regs + ISSR36);
dev->i2h_cmd.arg[1] = readl(dev->regs + ISSR37);
dev->i2h_cmd.arg[2] = readl(dev->regs + ISSR38);
fimc_is_fw_clear_irq1(dev, intr_pos);
buf_index = dev->i2h_cmd.arg[2];
dbg("3DNR returned buf index : %d\n", buf_index);
vb2_buffer_done(dev->video[FIMC_IS_VIDEO_NUM_3DNR].
vbq.bufs[buf_index], VB2_BUF_STATE_DONE);
fimc_is_hw_update_bufmask(dev, FIMC_IS_VIDEO_NUM_3DNR);
} else if (intr_pos == INTR_FRAME_DONE_SCALERP) {
dev->i2h_cmd.arg[0] = readl(dev->regs + ISSR44);
dev->i2h_cmd.arg[1] = readl(dev->regs + ISSR45);
dev->i2h_cmd.arg[2] = readl(dev->regs + ISSR46);
fimc_is_fw_clear_irq1(dev, intr_pos);
#ifdef DZOOM_EVT0
set_bit(IS_ST_SCALERP_FRAME_DONE, &dev->state);
#endif
buf_index = dev->i2h_cmd.arg[2];
dbg("ScalerP returned buf index : %d\n", buf_index);
vb2_buffer_done(dev->video[FIMC_IS_VIDEO_NUM_SCALERP].
vbq.bufs[buf_index], VB2_BUF_STATE_DONE);
fimc_is_hw_update_bufmask(dev, FIMC_IS_VIDEO_NUM_SCALERP);
}
wake_up(&dev->irq_queue);
return IRQ_HANDLED;
}
static int fimc_is_probe(struct platform_device *pdev)
{
struct resource *mem_res;
struct resource *regs_res;
struct fimc_is_dev *isp;
int ret = -ENODEV;
struct vb2_queue *scalerc_q;
struct vb2_queue *scalerp_q;
struct vb2_queue *dnr_q;
struct vb2_queue *bayer_q;
dbg("fimc_is_front_probe\n");
isp = kzalloc(sizeof(struct fimc_is_dev), GFP_KERNEL);
if (!isp)
return -ENOMEM;
isp->pdev = pdev;
isp->pdata = pdev->dev.platform_data;
isp->id = pdev->id;
isp->pipe_state = 0;
set_bit(FIMC_IS_STATE_IDLE, &isp->pipe_state);
set_bit(IS_ST_IDLE , &isp->state);
init_waitqueue_head(&isp->irq_queue);
spin_lock_init(&isp->slock);
mutex_init(&isp->vb_lock);
mutex_init(&isp->lock);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
dev_err(&pdev->dev, "Failed to get io memory region\n");
goto p_err1;
}
regs_res = request_mem_region(mem_res->start, resource_size(mem_res),
pdev->name);
if (!regs_res) {
dev_err(&pdev->dev, "Failed to request io memory region\n");
goto p_err1;
}
isp->regs_res = regs_res;
isp->regs = ioremap(mem_res->start, resource_size(mem_res));
if (!isp->regs) {
dev_err(&pdev->dev, "Failed to remap io region\n");
goto p_err2;
}
isp->mdev = fimc_is_get_md(MDEV_ISP);
if (IS_ERR_OR_NULL(isp->mdev))
goto p_err3;
dbg("fimc_is_front->mdev : 0x%p\n", isp->mdev);
#if defined(CONFIG_VIDEOBUF2_CMA_PHYS)
isp->vb2 = &fimc_is_vb2_cma;
#elif defined(CONFIG_VIDEOBUF2_ION)
isp->vb2 = &fimc_is_vb2_ion;
#endif
isp->irq = platform_get_irq(pdev, 0);
if (isp->irq < 0) {
dev_err(&pdev->dev, "Failed to get irq\n");
goto p_err3;
}
ret = request_irq(isp->irq, fimc_is_irq_handler,
0, dev_name(&pdev->dev), isp);
if (ret) {
dev_err(&pdev->dev, "request_irq failed\n");
goto p_err3;
}
/*sensor entity*/
v4l2_subdev_init(&isp->sensor.sd, &fimc_is_sensor_subdev_ops);
isp->sensor.sd.owner = THIS_MODULE;
isp->sensor.sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(isp->sensor.sd.name, sizeof(isp->sensor.sd.name), "%s\n",
FIMC_IS_SENSOR_ENTITY_NAME);
isp->sensor.pads.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_init(&isp->sensor.sd.entity, 1,
&isp->sensor.pads, 0);
if (ret < 0)
goto p_err3;
fimc_is_sensor_init_formats(&isp->sensor.sd, NULL);
isp->sensor.sd.internal_ops = &fimc_is_sensor_v4l2_internal_ops;
isp->sensor.sd.entity.ops = &fimc_is_sensor_media_ops;
ret = v4l2_device_register_subdev(&isp->mdev->v4l2_dev,
&isp->sensor.sd);
if (ret)
goto p_err3;
/* This allows to retrieve the platform device id by the host driver */
v4l2_set_subdevdata(&isp->sensor.sd, pdev);
/*front entity*/
v4l2_subdev_init(&isp->front.sd, &fimc_is_front_subdev_ops);
isp->front.sd.owner = THIS_MODULE;
isp->front.sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(isp->front.sd.name, sizeof(isp->front.sd.name), "%s\n",
FIMC_IS_FRONT_ENTITY_NAME);
isp->front.pads[FIMC_IS_FRONT_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
isp->front.pads[FIMC_IS_FRONT_PAD_SOURCE_BACK].flags
= MEDIA_PAD_FL_SOURCE;
isp->front.pads[FIMC_IS_FRONT_PAD_SOURCE_BAYER].flags
= MEDIA_PAD_FL_SOURCE;
isp->front.pads[FIMC_IS_FRONT_PAD_SOURCE_SCALERC].flags
= MEDIA_PAD_FL_SOURCE;
ret = media_entity_init(&isp->front.sd.entity, FIMC_IS_FRONT_PADS_NUM,
isp->front.pads, 0);
if (ret < 0)
goto p_err3;
fimc_is_front_init_formats(&isp->front.sd, NULL);
isp->front.sd.internal_ops = &fimc_is_front_v4l2_internal_ops;
isp->front.sd.entity.ops = &fimc_is_front_media_ops;
ret = v4l2_device_register_subdev(&isp->mdev->v4l2_dev,
&isp->front.sd);
if (ret)
goto p_err3;
/* This allows to retrieve the platform device id by the host driver */
v4l2_set_subdevdata(&isp->front.sd, pdev);
/*back entity*/
v4l2_subdev_init(&isp->back.sd, &fimc_is_back_subdev_ops);
isp->back.sd.owner = THIS_MODULE;
isp->back.sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(isp->back.sd.name, sizeof(isp->back.sd.name), "%s\n",
FIMC_IS_BACK_ENTITY_NAME);
isp->back.pads[FIMC_IS_BACK_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
isp->back.pads[FIMC_IS_BACK_PAD_SOURCE_3DNR].flags
= MEDIA_PAD_FL_SOURCE;
isp->back.pads[FIMC_IS_BACK_PAD_SOURCE_SCALERP].flags
= MEDIA_PAD_FL_SOURCE;
ret = media_entity_init(&isp->back.sd.entity, FIMC_IS_BACK_PADS_NUM,
isp->back.pads, 0);
if (ret < 0)
goto p_err3;
fimc_is_front_init_formats(&isp->back.sd, NULL);
isp->back.sd.internal_ops = &fimc_is_back_v4l2_internal_ops;
isp->back.sd.entity.ops = &fimc_is_back_media_ops;
ret = v4l2_device_register_subdev(&isp->mdev->v4l2_dev, &isp->back.sd);
if (ret)
goto p_err3;
v4l2_set_subdevdata(&isp->back.sd, pdev);
/*front video entity - scalerC */
snprintf(isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.name,
sizeof(isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.name),
"%s", FIMC_IS_VIDEO_SCALERC_NAME);
isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.fops
= &fimc_is_scalerc_video_fops;
isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.ioctl_ops
= &fimc_is_scalerc_video_ioctl_ops;
isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.v4l2_dev
= &isp->mdev->v4l2_dev;
isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.minor = -1;
isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.release
= video_device_release;
isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.lock = &isp->vb_lock;
video_set_drvdata(&isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd, isp);
isp->video[FIMC_IS_VIDEO_NUM_SCALERC].dev = isp;
scalerc_q = &isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vbq;
memset(scalerc_q, 0, sizeof(*scalerc_q));
scalerc_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
scalerc_q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
scalerc_q->drv_priv = &isp->video[FIMC_IS_VIDEO_NUM_SCALERC];
scalerc_q->ops = &fimc_is_scalerc_qops;
scalerc_q->mem_ops = isp->vb2->ops;
vb2_queue_init(scalerc_q);
ret = video_register_device(&isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd,
VFL_TYPE_GRABBER,
FIMC_IS_VIDEO_NUM_SCALERC+EXYNOS_VIDEONODE_FIMC_IS);
dbg("scalerC minor : %d\n",
isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.minor);
if (ret) {
err("Failed to register ScalerC video device\n");
goto p_err3;
}
isp->video[FIMC_IS_VIDEO_NUM_SCALERC].pads.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_init(&isp->video[FIMC_IS_VIDEO_NUM_SCALERC].
vd.entity, 1,
&isp->video[FIMC_IS_VIDEO_NUM_SCALERC].pads, 0);
if (ret) {
err("Failed to media_entity_init ScalerC video device\n");
goto p_err3;
}
/* back video entity - scalerP*/
snprintf(isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.name,
sizeof(isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.name),
"%s", FIMC_IS_VIDEO_SCALERP_NAME);
isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.fops
= &fimc_is_scalerp_video_fops;
isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.ioctl_ops
= &fimc_is_scalerp_video_ioctl_ops;
isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.v4l2_dev
= &isp->mdev->v4l2_dev;
isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.minor = -1;
isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.release = video_device_release;
isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.lock = &isp->vb_lock;
video_set_drvdata(&isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd, isp);
isp->video[FIMC_IS_VIDEO_NUM_SCALERP].dev = isp;
scalerp_q = &isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vbq;
memset(scalerp_q, 0, sizeof(*scalerp_q));
scalerp_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
scalerp_q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
scalerp_q->drv_priv = &isp->video[FIMC_IS_VIDEO_NUM_SCALERP];
scalerp_q->ops = &fimc_is_scalerp_qops;
scalerp_q->mem_ops = isp->vb2->ops;
vb2_queue_init(scalerp_q);
ret = video_register_device(&isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd,
VFL_TYPE_GRABBER,
FIMC_IS_VIDEO_NUM_SCALERP
+ EXYNOS_VIDEONODE_FIMC_IS);
dbg("scalerP minor : %d\n",
isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.minor);
if (ret) {
err("Failed to register ScalerP video device\n");
goto p_err3;
}
isp->video[FIMC_IS_VIDEO_NUM_SCALERP].pads.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_init(&isp->video[FIMC_IS_VIDEO_NUM_SCALERP].
vd.entity, 1,
&isp->video[FIMC_IS_VIDEO_NUM_SCALERP].pads, 0);
if (ret) {
err("Failed to media_entity_init ScalerP video device\n");
goto p_err3;
}
/*back video entity - 3DNR */
snprintf(isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.name,
sizeof(isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.name),
"%s", FIMC_IS_VIDEO_3DNR_NAME);
isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.fops
= &fimc_is_3dnr_video_fops;
isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.ioctl_ops
= &fimc_is_3dnr_video_ioctl_ops;
isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.v4l2_dev
= &isp->mdev->v4l2_dev;
isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.minor = -1;
isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.release
= video_device_release;
isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.lock = &isp->vb_lock;
video_set_drvdata(&isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd, isp);
isp->video[FIMC_IS_VIDEO_NUM_3DNR].dev = isp;
dnr_q = &isp->video[FIMC_IS_VIDEO_NUM_3DNR].vbq;
memset(dnr_q, 0, sizeof(*dnr_q));
dnr_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
dnr_q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
dnr_q->drv_priv = &isp->video[FIMC_IS_VIDEO_NUM_3DNR];
dnr_q->ops = &fimc_is_3dnr_qops;
dnr_q->mem_ops = isp->vb2->ops;
vb2_queue_init(dnr_q);
ret = video_register_device(&isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd,
VFL_TYPE_GRABBER,
FIMC_IS_VIDEO_NUM_3DNR
+ EXYNOS_VIDEONODE_FIMC_IS);
dbg("3DNR minor : %d\n", isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.minor);
if (ret) {
err("Failed to register 3DNR video device\n");
goto p_err3;
}
isp->video[FIMC_IS_VIDEO_NUM_3DNR].pads.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_init(&isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.entity,
1, &isp->video[FIMC_IS_VIDEO_NUM_3DNR].pads, 0);
if (ret) {
err("Failed to media_entity_init 3DNR video device\n");
goto p_err3;
}
/* back video entity - bayer*/
snprintf(isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.name,
sizeof(isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.name),
"%s", FIMC_IS_VIDEO_BAYER_NAME);
isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.fops
= &fimc_is_bayer_video_fops;
isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.ioctl_ops
= &fimc_is_bayer_video_ioctl_ops;
isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.v4l2_dev = &isp->mdev->v4l2_dev;
isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.minor = -1;
isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.release = video_device_release;
isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.lock = &isp->vb_lock;
video_set_drvdata(&isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd, isp);
isp->video[FIMC_IS_VIDEO_NUM_BAYER].dev = isp;
bayer_q = &isp->video[FIMC_IS_VIDEO_NUM_BAYER].vbq;
memset(bayer_q, 0, sizeof(*bayer_q));
bayer_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
bayer_q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
bayer_q->drv_priv = &isp->video[FIMC_IS_VIDEO_NUM_BAYER];
bayer_q->ops = &fimc_is_bayer_qops;
bayer_q->mem_ops = isp->vb2->ops;
vb2_queue_init(bayer_q);
ret = video_register_device(&isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd,
VFL_TYPE_GRABBER,
FIMC_IS_VIDEO_NUM_BAYER
+ EXYNOS_VIDEONODE_FIMC_IS);
dbg("scalerP minor : %d\n",
isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.minor);
if (ret) {
err("Failed to register ScalerP video device\n");
goto p_err3;
}
isp->video[FIMC_IS_VIDEO_NUM_BAYER].pads.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_init(&isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.entity,
1, &isp->video[FIMC_IS_VIDEO_NUM_BAYER].pads, 0);
if (ret) {
err("Failed to media_entity_init ScalerP video device\n");
goto p_err3;
}
platform_set_drvdata(pdev, isp);
/* create link */
ret = media_entity_create_link(
&isp->sensor.sd.entity, FIMC_IS_SENSOR_PAD_SOURCE_FRONT,
&isp->front.sd.entity, FIMC_IS_FRONT_PAD_SINK, 0);
if (ret < 0) {
err("failed link creation from sensor to front\n");
goto p_err3;
}
ret = media_entity_create_link(
&isp->front.sd.entity, FIMC_IS_FRONT_PAD_SOURCE_BACK,
&isp->back.sd.entity, FIMC_IS_BACK_PAD_SINK, 0);
if (ret < 0) {
err("failed link creation from front to back\n");
goto p_err3;
}
ret = media_entity_create_link(
&isp->front.sd.entity, FIMC_IS_FRONT_PAD_SOURCE_SCALERC,
&isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.entity, 0, 0);
if (ret < 0) {
err("failed link creation from front to scalerC video\n");
goto p_err3;
}
ret = media_entity_create_link(
&isp->back.sd.entity, FIMC_IS_BACK_PAD_SOURCE_SCALERP,
&isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.entity, 0, 0);
if (ret < 0) {
err("failed link creation from back to scalerP video\n");
goto p_err3;
}
ret = media_entity_create_link(
&isp->back.sd.entity, FIMC_IS_BACK_PAD_SOURCE_3DNR,
&isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.entity, 0, 0);
if (ret < 0) {
err("failed link creation from back to 3DNR video\n");
goto p_err3;
}
/* register subdev nodes*/
ret = v4l2_device_register_subdev_nodes(&isp->mdev->v4l2_dev);
if (ret)
err("v4l2_device_register_subdev_nodes failed\n");
/* init vb2*/
isp->alloc_ctx = isp->vb2->init(isp);
if (IS_ERR(isp->alloc_ctx)) {
ret = PTR_ERR(isp->alloc_ctx);
goto p_err1;
}
/* init memory*/
ret = fimc_is_init_mem(isp);
if (ret) {
dbg("failed to fimc_is_init_mem (%d)\n", ret);
goto p_err3;
}
#if defined(CONFIG_BUSFREQ_OPP) && defined(CONFIG_CPU_EXYNOS5250)
isp->bus_dev = dev_get("exynos-busfreq");
mutex_init(&isp->busfreq_lock);
isp->busfreq_num = 0;
#endif
/*init gpio : should be moved to stream_on */
if (isp->pdata->cfg_gpio) {
isp->pdata->cfg_gpio(isp->pdev);
} else {
dev_err(&isp->pdev->dev, "failed to init GPIO config\n");
goto p_err3;
}
#if defined(CONFIG_PM_RUNTIME)
pm_runtime_enable(&pdev->dev);
#endif
printk(KERN_INFO "fimc-is-mc probe success\n");
return 0;
p_err3:
iounmap(isp->regs);
p_err2:
release_mem_region(regs_res->start, resource_size(regs_res));
p_err1:
kfree(isp);
return ret;
}
static int fimc_is_remove(struct platform_device *pdev)
{
dbg("%s\n", __func__);
return 0;
}
static const struct dev_pm_ops fimc_is_pm_ops = {
.suspend = fimc_is_suspend,
.resume = fimc_is_resume,
.runtime_suspend = fimc_is_runtime_suspend,
.runtime_resume = fimc_is_runtime_resume,
};
static struct platform_driver fimc_is_driver = {
.probe = fimc_is_probe,
.remove = __devexit_p(fimc_is_remove),
.driver = {
.name = FIMC_IS_MODULE_NAME,
.owner = THIS_MODULE,
.pm = &fimc_is_pm_ops,
}
};
static int __init fimc_is_init(void)
{
int ret = platform_driver_register(&fimc_is_driver);
if (ret)
err("platform_driver_register failed: %d\n", ret);
return ret;
}
static void __exit fimc_is_exit(void)
{
platform_driver_unregister(&fimc_is_driver);
}
module_init(fimc_is_init);
module_exit(fimc_is_exit);
MODULE_AUTHOR("Jiyoung Shin<idon.shin@samsung.com>");
MODULE_DESCRIPTION("Exynos FIMC_IS driver");
MODULE_LICENSE("GPL");