diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/media/video/samsung/fimc/fimc_capture.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'drivers/media/video/samsung/fimc/fimc_capture.c')
-rw-r--r-- | drivers/media/video/samsung/fimc/fimc_capture.c | 3171 |
1 files changed, 3171 insertions, 0 deletions
diff --git a/drivers/media/video/samsung/fimc/fimc_capture.c b/drivers/media/video/samsung/fimc/fimc_capture.c new file mode 100644 index 0000000..fe0878a --- /dev/null +++ b/drivers/media/video/samsung/fimc/fimc_capture.c @@ -0,0 +1,3171 @@ +/* linux/drivers/media/video/samsung/fimc_capture.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * V4L2 Capture device support file for Samsung Camera Interface (FIMC) driver + * + * 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/slab.h> +#include <linux/bootmem.h> +#include <linux/string.h> +#include <linux/platform_device.h> +#include <linux/videodev2.h> +#include <linux/videodev2_exynos_media.h> +#include <linux/videodev2_exynos_camera.h> +#include <linux/clk.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <plat/media.h> +#include <plat/clock.h> +#include <plat/fimc.h> +#include <linux/delay.h> + +#include <asm/cacheflush.h> +#include <linux/pm_qos_params.h> + +#include "fimc.h" + +static struct pm_qos_request_list bus_qos_pm_qos_req; + +static const struct v4l2_fmtdesc capture_fmts[] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PACKED, + .description = "RGB-5-6-5", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, { + .index = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PACKED, + .description = "RGB-8-8-8, unpacked 24 bpp", + .pixelformat = V4L2_PIX_FMT_RGB32, + }, { + .index = 2, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PACKED, + .description = "YUV 4:2:2 packed, YCbYCr", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, { + .index = 3, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PACKED, + .description = "YUV 4:2:2 packed, CbYCrY", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, { + .index = 4, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PACKED, + .description = "YUV 4:2:2 packed, CrYCbY", + .pixelformat = V4L2_PIX_FMT_VYUY, + }, { + .index = 5, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PACKED, + .description = "YUV 4:2:2 packed, YCrYCb", + .pixelformat = V4L2_PIX_FMT_YVYU, + }, { + .index = 6, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PLANAR, + .description = "YUV 4:2:2 planar, Y/Cb/Cr", + .pixelformat = V4L2_PIX_FMT_YUV422P, + }, { + .index = 7, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PLANAR, + .description = "YUV 4:2:0 planar, Y/CbCr", + .pixelformat = V4L2_PIX_FMT_NV12, + }, { + .index = 8, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PLANAR, + .description = "YUV 4:2:0 planar, Y/CbCr, Tiled", + .pixelformat = V4L2_PIX_FMT_NV12T, + }, { + .index = 9, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PLANAR, + .description = "YUV 4:2:0 planar, Y/CrCb", + .pixelformat = V4L2_PIX_FMT_NV21, + }, { + .index = 10, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PLANAR, + .description = "YUV 4:2:2 planar, Y/CbCr", + .pixelformat = V4L2_PIX_FMT_NV16, + }, { + .index = 11, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PLANAR, + .description = "YUV 4:2:2 planar, Y/CrCb", + .pixelformat = V4L2_PIX_FMT_NV61, + }, { + .index = 12, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PLANAR, + .description = "YUV 4:2:0 planar, Y/Cb/Cr", + .pixelformat = V4L2_PIX_FMT_YUV420, + }, { + .index = 13, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = FORMAT_FLAGS_PLANAR, + .description = "YUV 4:2:0 planar, Y/Cr/Cb", + .pixelformat = V4L2_PIX_FMT_YVU420, + }, { + .index = 14, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "JPEG encoded data", + .pixelformat = V4L2_PIX_FMT_JPEG, + }, { + .index = 15, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Interleaved data", + .pixelformat = V4L2_PIX_FMT_INTERLEAVED, + }, +}; + +static const struct v4l2_queryctrl fimc_controls[] = { + { + .id = V4L2_CID_ROTATION, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Roataion", + .minimum = 0, + .maximum = 270, + .step = 90, + .default_value = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_PADDR_Y, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Physical address Y", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + }, { + .id = V4L2_CID_PADDR_CB, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Physical address Cb", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + }, { + .id = V4L2_CID_PADDR_CR, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Physical address Cr", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + }, { + .id = V4L2_CID_PADDR_CBCR, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Physical address CbCr", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + }, { + .id = V4L2_CID_CACHEABLE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Cacheable", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + +#ifndef CONFIG_VIDEO_FIMC_MIPI +void s3c_csis_start(int csis_id, int lanes, int settle, \ + int align, int width, int height, int pixel_format) {} +void s3c_csis_stop(int csis_id) {} +void s3c_csis_enable_pktdata(int csis_id, bool enable) {} +#endif + +static int fimc_init_camera(struct fimc_control *ctrl) +{ + struct fimc_global *fimc = get_fimc_dev(); + struct s3c_platform_fimc *pdata; + struct s3c_platform_camera *cam; + int ret = 0, retry_cnt = 0; + +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + struct platform_device *pdev = to_platform_device(ctrl->dev); +#endif + pdata = to_fimc_plat(ctrl->dev); + + cam = ctrl->cam; + + /* do nothing if already initialized */ + if (ctrl->cam->initialized) + return 0; + +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + if (ctrl->power_status == FIMC_POWER_OFF) { + pm_runtime_get_sync(&pdev->dev); + } +#endif + /* + * WriteBack mode doesn't need to set clock and power, + * but it needs to set source width, height depend on LCD resolution. + */ + if ((cam->id == CAMERA_WB) || (cam->id == CAMERA_WB_B)) { + ret = s3cfb_direct_ioctl(0, S3CFB_GET_LCD_WIDTH, + (unsigned long)&cam->width); + if (ret) { + fimc_err("fail to get LCD size\n"); +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + pm_runtime_put_sync(&pdev->dev); +#endif + return ret; + } + + ret = s3cfb_direct_ioctl(0, S3CFB_GET_LCD_HEIGHT, + (unsigned long)&cam->height); + if (ret) { + fimc_err("fail to get LCD size\n"); +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + pm_runtime_put_sync(&pdev->dev); +#endif + return ret; + } + + cam->window.width = cam->width; + cam->window.height = cam->height; + cam->initialized = 1; + + return ret; + } + +retry: + /* set rate for mclk */ + if ((clk_get_rate(cam->clk)) && (fimc->mclk_status == CAM_MCLK_OFF)) { + clk_set_rate(cam->clk, cam->clk_rate); + clk_enable(cam->clk); + fimc->mclk_status = CAM_MCLK_ON; + fimc_info1("clock for camera: %d\n", cam->clk_rate); + } + + /* enable camera power if needed */ + if (cam->cam_power) { + ret = cam->cam_power(1); + if (unlikely(ret < 0)) { + fimc_err("\nfail to power on\n"); + if (fimc->mclk_status == CAM_MCLK_ON) { + clk_disable(ctrl->cam->clk); + fimc->mclk_status = CAM_MCLK_OFF; + } +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + pm_runtime_put_sync(&pdev->dev); +#endif + return ret; + } + } + + /* "0" argument means preview init for s5k4ea */ + ret = v4l2_subdev_call(cam->sd, core, init, 0); + + /* Retry camera power-up if first i2c fails. */ + if (unlikely(ret < 0)) { + if (cam->cam_power) + cam->cam_power(0); + + if (fimc->mclk_status == CAM_MCLK_ON) { + clk_disable(ctrl->cam->clk); + fimc->mclk_status = CAM_MCLK_OFF; + } + if (retry_cnt++ < 3) { + msleep(100); + fimc_err("Retry power on(%d/3)\n\n", retry_cnt); + goto retry; + } else { + fimc_err("Camera power/init failed!!!!\n\n"); +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + if (ctrl->power_status == FIMC_POWER_ON) { + pm_runtime_put_sync(&pdev->dev); + } +#endif + } + } else { + /* Apply things to interface register */ + fimc_hwset_reset(ctrl); + cam->initialized = 1; + } + + return ret; +} + +static int fimc_camera_get_jpeg_memsize(struct fimc_control *ctrl) +{ + int ret = 0; + struct v4l2_control cam_ctrl; + cam_ctrl.id = V4L2_CID_CAM_JPEG_MEMSIZE; + + ret = v4l2_subdev_call(ctrl->cam->sd, core, g_ctrl, &cam_ctrl); + if (ret < 0) { + fimc_err("%s: Subdev doesn't support JEPG encoding.\n", \ + __func__); + return 0; + } + + return cam_ctrl.value; +} + + +static int fimc_capture_scaler_info(struct fimc_control *ctrl) +{ + struct fimc_scaler *sc = &ctrl->sc; + struct v4l2_rect *window = &ctrl->cam->window; + int tx, ty, sx, sy; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + int rot = 0; + + if (!ctrl->cam->use_isp) { + sx = window->width; + sy = window->height; + } else { + sx = ctrl->is.fmt.width; + sy = ctrl->is.fmt.height; + } + + sc->real_width = sx; + sc->real_height = sy; + + rot = fimc_mapping_rot_flip(ctrl->cap->rotate, ctrl->cap->flip); + + if (rot & FIMC_ROT) { + tx = ctrl->cap->fmt.height; + ty = ctrl->cap->fmt.width; + } else { + tx = ctrl->cap->fmt.width; + ty = ctrl->cap->fmt.height; + } + + fimc_dbg("%s: CamOut (%d, %d), TargetOut (%d, %d)\n", + __func__, sx, sy, tx, ty); + + if (sx <= 0 || sy <= 0) { + fimc_err("%s: invalid source size\n", __func__); + return -EINVAL; + } + + if (tx <= 0 || ty <= 0) { + fimc_err("%s: invalid target size\n", __func__); + return -EINVAL; + } + + fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor); + fimc_get_scaler_factor(sy, ty, &sc->pre_vratio, &sc->vfactor); + + if (sx == sy) { + if (sx*10/tx >= 15 && sx*10/tx < 20) { + sc->pre_hratio = 2; + sc->hfactor = 1; + } + if (sy*10/ty >= 15 && sy*10/ty < 20) { + sc->pre_vratio = 2; + sc->vfactor = 1; + } + } + + + sc->pre_dst_width = sx / sc->pre_hratio; + sc->pre_dst_height = sy / sc->pre_vratio; + + if (pdata->hw_ver >= 0x50) { + sc->main_hratio = (sx << 14) / (tx << sc->hfactor); + sc->main_vratio = (sy << 14) / (ty << sc->vfactor); + } else { + sc->main_hratio = (sx << 8) / (tx << sc->hfactor); + sc->main_vratio = (sy << 8) / (ty << sc->vfactor); + } + + sc->scaleup_h = (tx >= sx) ? 1 : 0; + sc->scaleup_v = (ty >= sy) ? 1 : 0; + + return 0; +} + +static int fimc_capture_change_scaler_info(struct fimc_control *ctrl) +{ + struct fimc_scaler *sc = &ctrl->sc; + struct v4l2_rect *window = &ctrl->cam->window; + int tx, ty, sx, sy; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + int rot = 0; + + if (!ctrl->cam->use_isp) { + sx = window->width; + sy = window->height; + } else { + sx = ctrl->is.zoom_in_width; + sy = ctrl->is.zoom_in_height; + } + + sc->real_width = sx; + sc->real_height = sy; + + rot = fimc_mapping_rot_flip(ctrl->cap->rotate, ctrl->cap->flip); + + if (rot & FIMC_ROT) { + tx = ctrl->cap->fmt.height; + ty = ctrl->cap->fmt.width; + } else { + tx = ctrl->cap->fmt.width; + ty = ctrl->cap->fmt.height; + } + + fimc_dbg("%s: CamOut (%d, %d), TargetOut (%d, %d)\n", + __func__, sx, sy, tx, ty); + + if (sx <= 0 || sy <= 0) { + fimc_err("%s: invalid source size\n", __func__); + return -EINVAL; + } + + if (tx <= 0 || ty <= 0) { + fimc_err("%s: invalid target size\n", __func__); + return -EINVAL; + } + + fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor); + fimc_get_scaler_factor(sy, ty, &sc->pre_vratio, &sc->vfactor); + + sc->pre_dst_width = sx / sc->pre_hratio; + sc->pre_dst_height = sy / sc->pre_vratio; + + if (pdata->hw_ver >= 0x50) { + sc->main_hratio = (sx << 14) / (tx << sc->hfactor); + sc->main_vratio = (sy << 14) / (ty << sc->vfactor); + } else { + sc->main_hratio = (sx << 8) / (tx << sc->hfactor); + sc->main_vratio = (sy << 8) / (ty << sc->vfactor); + } + + sc->scaleup_h = (tx >= sx) ? 1 : 0; + sc->scaleup_v = (ty >= sy) ? 1 : 0; + + return 0; +} + +int fimc_start_zoom_capture(struct fimc_control *ctrl) +{ + fimc_dbg("%s\n", __func__); + + fimc_hwset_start_scaler(ctrl); + + fimc_hwset_enable_capture(ctrl, ctrl->sc.bypass); + fimc_hwset_disable_frame_end_irq(ctrl); + + return 0; +} + +int fimc_stop_zoom_capture(struct fimc_control *ctrl) +{ + fimc_dbg("%s\n", __func__); + if (!ctrl->cam) { + fimc_err("%s: No capture device.\n", __func__); + return -ENODEV; + } + + if (!ctrl->cap) { + fimc_err("%s: No cappure format.\n", __func__); + return -ENODEV; + } + + if (ctrl->cap->lastirq) { + fimc_hwset_enable_lastirq(ctrl); + fimc_hwset_disable_capture(ctrl); + fimc_hwset_disable_lastirq(ctrl); + } else { + fimc_hwset_disable_capture(ctrl); + fimc_hwset_enable_frame_end_irq(ctrl); + } + + fimc_hwset_stop_scaler(ctrl); + return 0; +} + +static int fimc_add_inqueue(struct fimc_control *ctrl, int i) +{ + struct fimc_capinfo *cap = ctrl->cap; + struct fimc_buf_set *tmp_buf; + struct list_head *count; + + /* PINGPONG_2ADDR_MODE Only */ + list_for_each(count, &cap->inq) { + tmp_buf = list_entry(count, struct fimc_buf_set, list); + /* skip list_add_tail if already buffer is in cap->inq list*/ + if (tmp_buf->id == i) + return 0; + } + list_add_tail(&cap->bufs[i].list, &cap->inq); + + return 0; +} + +static int fimc_add_outqueue(struct fimc_control *ctrl, int i) +{ + struct fimc_capinfo *cap = ctrl->cap; + struct fimc_buf_set *buf; + unsigned int mask = 0x2; + + /* PINGPONG_2ADDR_MODE Only */ + /* pair_buf_index stands for pair index of i. (0<->2) (1<->3) */ + int pair_buf_index = (i^mask); + + /* FIMC have 4 h/w registers */ + if (i < 0 || i >= FIMC_PHYBUFS) { + fimc_err("%s: invalid queue index : %d\n", __func__, i); + return -ENOENT; + } + + if (list_empty(&cap->inq)) + return -ENOENT; + + buf = list_first_entry(&cap->inq, struct fimc_buf_set, list); + + /* pair index buffer should be allocated first */ + cap->outq[pair_buf_index] = buf->id; + fimc_hwset_output_address(ctrl, buf, pair_buf_index); + + cap->outq[i] = buf->id; + fimc_hwset_output_address(ctrl, buf, i); + + list_del(&buf->list); + + return 0; +} + +int fimc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + int ret = 0; + + fimc_dbg("%s\n", __func__); + + /* WriteBack doesn't have subdev_call */ + + if ((ctrl->cam->id == CAMERA_WB) || (ctrl->cam->id == CAMERA_WB_B)) + return 0; + + mutex_lock(&ctrl->v4l2_lock); + ret = v4l2_subdev_call(ctrl->cam->sd, video, g_parm, a); + mutex_unlock(&ctrl->v4l2_lock); + + return ret; +} + +int fimc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + int ret = 0; + int new_fps = a->parm.capture.timeperframe.denominator / + a->parm.capture.timeperframe.numerator; + + fimc_info2("%s fimc%d, %d\n", __func__, ctrl->id, new_fps); + + /* WriteBack doesn't have subdev_call */ + if ((ctrl->cam->id == CAMERA_WB) || (ctrl->cam->id == CAMERA_WB_B)) + return 0; + + mutex_lock(&ctrl->v4l2_lock); + + if (ctrl->cam->sd && fimc_cam_use) + ret = v4l2_subdev_call(ctrl->cam->sd, video, s_parm, a); + else if (ctrl->is.sd && fimc_cam_use) + ret = v4l2_subdev_call(ctrl->is.sd, video, s_parm, a); + + mutex_unlock(&ctrl->v4l2_lock); + + return ret; +} + +/* Enumerate controls */ +int fimc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + int i, ret; + + fimc_dbg("%s\n", __func__); + + for (i = 0; i < ARRAY_SIZE(fimc_controls); i++) { + if (fimc_controls[i].id == qc->id) { + memcpy(qc, &fimc_controls[i], sizeof(struct v4l2_queryctrl)); + return 0; + } + } + + mutex_lock(&ctrl->v4l2_lock); + ret = v4l2_subdev_call(ctrl->cam->sd, core, queryctrl, qc); + mutex_unlock(&ctrl->v4l2_lock); + + return ret; +} + +/* Menu control items */ +int fimc_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qm) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + int ret = 0; + + fimc_dbg("%s\n", __func__); + + mutex_lock(&ctrl->v4l2_lock); + ret = v4l2_subdev_call(ctrl->cam->sd, core, querymenu, qm); + mutex_unlock(&ctrl->v4l2_lock); + + return ret; +} + +int fimc_enum_input(struct file *file, void *fh, struct v4l2_input *inp) +{ + struct fimc_global *fimc = get_fimc_dev(); + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + + fimc_dbg("%s: index %d\n", __func__, inp->index); + + if (inp->index >= FIMC_MAXCAMS) { + fimc_err("%s: invalid input index, received = %d\n", + __func__, inp->index); + return -EINVAL; + } + + if (!fimc->camera_isvalid[inp->index]) + return -EINVAL; + mutex_lock(&ctrl->v4l2_lock); + + if (fimc->camera[inp->index]->use_isp && !(fimc->camera[inp->index]->info)) + strcpy(inp->name, "ISP Camera"); + else + strcpy(inp->name, fimc->camera[inp->index]->info->type); + + inp->type = V4L2_INPUT_TYPE_CAMERA; + + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +int fimc_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + struct fimc_global *fimc = get_fimc_dev(); + + /* In case of isueing g_input before s_input */ + if (!ctrl->cam) { + fimc_err("no camera device selected yet. do VIDIOC_S_INPUT first\n"); + return -ENODEV; + } + mutex_lock(&ctrl->v4l2_lock); + + *i = (unsigned int) fimc->active_camera; + + mutex_unlock(&ctrl->v4l2_lock); + + fimc_dbg("%s: index %d\n", __func__, *i); + + return 0; +} + +int fimc_release_subdev(struct fimc_control *ctrl) +{ + struct fimc_global *fimc = get_fimc_dev(); + struct i2c_client *client; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + int ret; + + if (ctrl->cam->sd && fimc_cam_use) { + fimc_dbg("%s called\n", __func__); + + /* WriteBack doesn't need clock setting */ + if ((ctrl->cam->id == CAMERA_WB) || + (ctrl->cam->id == CAMERA_WB_B)) { + ctrl->cam->initialized = 0; + ctrl->cam = NULL; + fimc->active_camera = -1; + return 0; + } + + client = v4l2_get_subdevdata(ctrl->cam->sd); + i2c_unregister_device(client); + ctrl->cam->sd = NULL; + if (ctrl->cam->cam_power) + ctrl->cam->cam_power(0); + + /* shutdown the MCLK */ + if (fimc->mclk_status == CAM_MCLK_ON) { + clk_disable(ctrl->cam->clk); + fimc->mclk_status = CAM_MCLK_OFF; + } + + ctrl->cam->initialized = 0; + ctrl->cam = NULL; + fimc->active_camera = -1; + } else if (ctrl->cam->sd) { + ctrl->cam->initialized = 0; + ctrl->cam = NULL; + fimc->active_camera = -1; + } + + if (ctrl->flite_sd && fimc_cam_use) { + ret = v4l2_subdev_call(ctrl->flite_sd, core, s_power, 0); + if (ret) + fimc_err("s_power failed: %d", ret); + + ctrl->flite_sd = NULL; + } + + return 0; +} + +static int fimc_configure_subdev(struct fimc_control *ctrl) +{ + struct i2c_adapter *i2c_adap; + struct i2c_board_info *i2c_info; + struct i2c_client *client; + struct v4l2_subdev *sd; + unsigned short addr; + char *name; + int ret = 0; + + i2c_adap = i2c_get_adapter(ctrl->cam->get_i2c_busnum()); + if (!i2c_adap) { + fimc_err("subdev i2c_adapter missing-skip registration\n"); + return -ENODEV; + } + + i2c_info = ctrl->cam->info; + if (!i2c_info) { + fimc_err("%s: subdev i2c board info missing\n", __func__); + return -ENODEV; + } + + name = i2c_info->type; + if (!name) { + fimc_err("subdev i2c driver name missing-skip registration\n"); + return -ENODEV; + } + + addr = i2c_info->addr; + if (!addr) { + fimc_err("subdev i2c address missing-skip registration\n"); + return -ENODEV; + } + /* + * NOTE: first time subdev being registered, + * s_config is called and try to initialize subdev device + * but in this point, we are not giving MCLK and power to subdev + * so nothing happens but pass platform data through + */ + sd = v4l2_i2c_new_subdev_board(&ctrl->v4l2_dev, i2c_adap, + i2c_info, &addr); + if (!sd) { + fimc_err("%s: v4l2 subdev board registering failed\n", + __func__); + } + /* Assign subdev to proper camera device pointer */ + ctrl->cam->sd = sd; + + if (!ctrl->cam->initialized) { + ret = fimc_init_camera(ctrl); + if (ret < 0) { + fimc_err("%s: fail to initialize subdev\n", __func__); + client = v4l2_get_subdevdata(sd); + i2c_unregister_device(client); + ctrl->cam->sd = NULL; + return ret; + } + } + + return 0; +} + +static int flite_register_callback(struct device *dev, void *p) +{ + struct v4l2_subdev **sd_list = p; + struct v4l2_subdev *sd = NULL; + + sd = dev_get_drvdata(dev); + if (sd) { + struct platform_device *pdev = v4l2_get_subdev_hostdata(sd); + *(sd_list + pdev->id) = sd; + } + + return 0; /* non-zero value stops iteration */ +} + +static struct v4l2_subdev *exynos_flite_get_subdev(int id) +{ + const char *module_name = "exynos-fimc-lite"; + struct device_driver *drv; + struct v4l2_subdev *sd[FLITE_MAX_NUM] = {NULL,}; + int ret; + + drv = driver_find(module_name, &platform_bus_type); + if (!drv) { + request_module(module_name); + drv = driver_find(module_name, &platform_bus_type); + } + if (!drv) + return ERR_PTR(-ENODEV); + + ret = driver_for_each_device(drv, NULL, &sd[0], + flite_register_callback); + put_driver(drv); + + return ret ? NULL : sd[id]; +} + +int fimc_subdev_attatch(struct fimc_control *ctrl) +{ + int ret = 0; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + + ctrl->flite_sd = exynos_flite_get_subdev(ctrl->cam->flite_id); + if (IS_ERR_OR_NULL(ctrl->flite_sd)) { + ctrl->flite_sd = NULL; + return PTR_ERR(ctrl->flite_sd); + } else { + if (fimc_cam_use) { + ret = v4l2_subdev_call(ctrl->flite_sd, core, s_power, 1); + if (ret) + fimc_err("s_power failed: %d", ret); + } + + } + + return 0; +} + +static int fimc_is_register_callback(struct device *dev, void *p) +{ + struct v4l2_subdev **sd = p; + + *sd = dev_get_drvdata(dev); + + if (!*sd) + return -EINVAL; + + return 0; /* non-zero value stops iteration */ +} + +int fimc_is_release_subdev(struct fimc_control *ctrl) +{ + int ret; + struct fimc_global *fimc = get_fimc_dev(); + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + + if (ctrl->is.sd && ctrl->cam && fimc_cam_use) { + if (ctrl->cam->cam_power) + ctrl->cam->cam_power(0); + /* shutdown the MCLK */ + if (fimc->mclk_status == CAM_MCLK_ON) { + clk_disable(ctrl->cam->clk); + fimc->mclk_status = CAM_MCLK_OFF; + } + + ret = v4l2_subdev_call(ctrl->is.sd, core, s_power, 0); + if (ret < 0) { + fimc_dbg("FIMC-IS init failed"); + return -ENODEV; + } + + v4l2_device_unregister_subdev(ctrl->is.sd); + ctrl->is.sd = NULL; + ctrl->cam->initialized = 0; + ctrl->cam = NULL; + fimc->active_camera = -1; + } else if (ctrl->is.sd && ctrl->cam) { + ctrl->is.sd = NULL; + ctrl->cam->initialized = 0; + ctrl->cam = NULL; + fimc->active_camera = -1; + } + + return 0; +} + +static struct v4l2_subdev *fimc_is_get_subdev(int id) +{ + const char *module_name = "exynos4-fimc-is"; + struct device_driver *drv; + struct v4l2_subdev *sd = NULL; + int ret; + + drv = driver_find(module_name, &platform_bus_type); + if (!drv) { + request_module(module_name); + drv = driver_find(module_name, &platform_bus_type); + } + if (!drv) + return ERR_PTR(-ENODEV); + + ret = driver_for_each_device(drv, NULL, &sd, + fimc_is_register_callback); + put_driver(drv); + return ret ? NULL : sd; +} + +static int fimc_is_init_cam(struct fimc_control *ctrl) +{ + struct fimc_global *fimc = get_fimc_dev(); + struct s3c_platform_camera *cam; + int ret = 0; + +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + struct platform_device *pdev = to_platform_device(ctrl->dev); +#endif + + cam = ctrl->cam; + /* Do noting if already initialized */ + if (ctrl->cam->initialized) + return 0; + +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + if (ctrl->power_status == FIMC_POWER_OFF) + pm_runtime_get_sync(&pdev->dev); +#endif + + /* set rate for mclk */ + if ((clk_get_rate(cam->clk)) && (fimc->mclk_status == CAM_MCLK_OFF)) { + clk_set_rate(cam->clk, cam->clk_rate); + clk_enable(cam->clk); + fimc->mclk_status = CAM_MCLK_ON; + fimc_info1("clock for camera (FIMC-IS): %d\n", cam->clk_rate); + } + + /* enable camera power if needed */ + if (cam->cam_power) { + ret = cam->cam_power(1); + if (unlikely(ret < 0)) + fimc_err("\nfail to power on\n"); + } + + + /* Retry camera power-up if first i2c fails. */ + if (unlikely(ret < 0)) { + if (cam->cam_power) + cam->cam_power(0); + + if (fimc->mclk_status == CAM_MCLK_ON) { + clk_disable(ctrl->cam->clk); + fimc->mclk_status = CAM_MCLK_OFF; + } + + fimc_err("Camera power/init failed!!!!\n\n"); +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + if (ctrl->power_status == FIMC_POWER_ON) + pm_runtime_put_sync(&pdev->dev); +#endif + } else { + /* Apply things to interface register */ + fimc_hwset_reset(ctrl); + cam->initialized = 1; + } + + return ret; +} + +int fimc_s_input(struct file *file, void *fh, unsigned int i) +{ + struct fimc_global *fimc = get_fimc_dev(); + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + struct fimc_capinfo *cap = ctrl->cap; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + int ret = 0; +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + struct platform_device *pdev = to_platform_device(ctrl->dev); +#endif + + fimc_dbg("%s: index %d\n", __func__, i); + + if (i >= FIMC_MAXCAMS) { + fimc_err("%s: invalid input index\n", __func__); + return -EINVAL; + } + + if (!fimc->camera_isvalid[i]) + return -EINVAL; + + if (fimc->camera[i]->sd && fimc_cam_use) { + fimc_err("%s: Camera already in use.\n", __func__); + return -EBUSY; + } + mutex_lock(&ctrl->v4l2_lock); + + /* If ctrl->cam is not NULL, there is one subdev already registered. + * We need to unregister that subdev first. */ + if (i != fimc->active_camera) { + fimc_info1("\n\nfimc_s_input activating subdev\n"); + if (ctrl->cam && (ctrl->cam->sd || ctrl->flite_sd)) + fimc_release_subdev(ctrl); + else if (ctrl->is.sd) + fimc_is_release_subdev(ctrl); + ctrl->cam = fimc->camera[i]; + + if ((ctrl->cam->id != CAMERA_WB) && (ctrl->cam->id != + CAMERA_WB_B) && (!ctrl->cam->use_isp) && fimc_cam_use) { + ret = fimc_configure_subdev(ctrl); + if (ret < 0) { +#ifdef CONFIG_MACH_GC1 + if (ret == -ENOSYS) { + /* return no error If firmware is bad. + Because F/W update app should access the sensor through HAL instance */ + fimc_err("%s: please update the F/W\n", __func__); + } else { + ctrl->cam = NULL; + mutex_unlock(&ctrl->v4l2_lock); + fimc_err("%s: Could not register camera" \ + " sensor with V4L2.\n", __func__); + return -ENODEV; + } +#else + ctrl->cam = NULL; +#ifdef CONFIG_MACH_P4NOTE + fimc_release_subdev(ctrl); +#endif /* CONFIG_MACH_P4NOTE */ + mutex_unlock(&ctrl->v4l2_lock); + fimc_err("%s: Could not register camera" \ + " sensor with V4L2.\n", __func__); + return -ENODEV; +#endif + } + } + fimc->active_camera = i; + fimc_info2("fimc_s_input activated subdev = %d\n", i); + } + + if (!fimc_cam_use) { + if (i == fimc->active_camera) { + ctrl->cam = fimc->camera[i]; + fimc_info2("fimc_s_input activating subdev FIMC%d\n", + ctrl->id); + } else { + mutex_unlock(&ctrl->v4l2_lock); + return -EINVAL; + } +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) +#ifdef CONFIG_DRM_EXYNOS_FIMD_WB + if ((ctrl->cam->id != CAMERA_WB) && + (ctrl->cam->id != CAMERA_WB_B) && + (ctrl->power_status == FIMC_POWER_OFF)) { +#else + if (ctrl->power_status == FIMC_POWER_OFF) { +#endif + pm_runtime_get_sync(&pdev->dev); + } + fimc_hwset_reset(ctrl); +#endif + } + + if (ctrl->cam->use_isp) { + /* fimc-lite attatch */ + ret = fimc_subdev_attatch(ctrl); + if (ret) { + fimc_err("subdev_attatch failed\n"); + mutex_unlock(&ctrl->v4l2_lock); + return -ENODEV; + } + /* fimc-is attatch */ + ctrl->is.sd = fimc_is_get_subdev(i); + if (IS_ERR_OR_NULL(ctrl->is.sd)) { + fimc_err("fimc-is subdev_attatch failed\n"); + mutex_unlock(&ctrl->v4l2_lock); + return -ENODEV; + } + + ctrl->is.fmt.width = ctrl->cam->width; + ctrl->is.fmt.height = ctrl->cam->height; + ctrl->is.frame_count = 0; + if (fimc_cam_use) { + ret = fimc_is_init_cam(ctrl); + if (ret < 0) { + fimc_dbg("FIMC-IS init clock failed"); + mutex_unlock(&ctrl->v4l2_lock); + return -ENODEV; + } + ret = v4l2_subdev_call(ctrl->is.sd, core, s_power, 1); + if (ret < 0) { + fimc_dbg("FIMC-IS init failed"); + mutex_unlock(&ctrl->v4l2_lock); + return -ENODEV; + } + ret = v4l2_subdev_call(ctrl->is.sd, core, load_fw); + if (ret < 0) { + fimc_dbg("FIMC-IS init failed"); + mutex_unlock(&ctrl->v4l2_lock); + return -ENODEV; + } + ret = v4l2_subdev_call(ctrl->is.sd, core, init, ctrl->cam->sensor_index); + if (ret < 0) { + fimc_dbg("FIMC-IS init failed"); + mutex_unlock(&ctrl->v4l2_lock); + return -ENODEV; + } + } + } + + + /* + * The first time alloc for struct cap_info, and will be + * released at the file close. + * Anyone has better idea to do this? + */ + if (!cap) { + cap = kzalloc(sizeof(*cap), GFP_KERNEL); + if (!cap) { + fimc_err("%s: no memory for " + "capture device info\n", __func__); + return -ENOMEM; + } + + /* assign to ctrl */ + ctrl->cap = cap; +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + if (ctrl->power_status == FIMC_POWER_OFF) + pm_runtime_get_sync(&pdev->dev); +#endif + } else { + memset(cap, 0, sizeof(*cap)); + } + + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +int fimc_enum_fmt_vid_capture(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + int i = f->index; + + fimc_dbg("%s\n", __func__); + + if (i >= ARRAY_SIZE(capture_fmts)) { + fimc_err("%s: There is no support format index %d\n", __func__, i); + return -EINVAL; + } + + mutex_lock(&ctrl->v4l2_lock); + + memset(f, 0, sizeof(*f)); + memcpy(f, &capture_fmts[i], sizeof(*f)); + + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +int fimc_g_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + + fimc_dbg("%s\n", __func__); + + if (!ctrl->cap) { + fimc_err("%s: no capture device info\n", __func__); + return -EINVAL; + } + + mutex_lock(&ctrl->v4l2_lock); + + memset(&f->fmt.pix, 0, sizeof(f->fmt.pix)); + memcpy(&f->fmt.pix, &ctrl->cap->fmt, sizeof(f->fmt.pix)); + + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +/* + * Check for whether the requested format + * can be streamed out from FIMC + * depends on FIMC node + */ +static int fimc_fmt_avail(struct fimc_control *ctrl, + struct v4l2_pix_format *f) +{ + int i; + + /* + * TODO: check for which FIMC is used. + * Available fmt should be varied for each FIMC + */ + + for (i = 0; i < ARRAY_SIZE(capture_fmts); i++) { + if (capture_fmts[i].pixelformat == f->pixelformat) + return 0; + } + + fimc_info1("Not supported pixelformat requested\n"); + + return -1; +} + +/* + * figures out the depth of requested format + */ +static int fimc_fmt_depth(struct fimc_control *ctrl, struct v4l2_pix_format *f) +{ + int err, depth = 0; + + /* First check for available format or not */ + err = fimc_fmt_avail(ctrl, f); + if (err < 0) + return -1; + + /* handles only supported pixelformats */ + switch (f->pixelformat) { + case V4L2_PIX_FMT_RGB32: + depth = 32; + fimc_dbg("32bpp\n"); + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + depth = 16; + fimc_dbg("16bpp\n"); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12T: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + depth = 12; + fimc_dbg("12bpp\n"); + break; + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_INTERLEAVED: + depth = -1; + fimc_dbg("Compressed format.\n"); + break; + default: + fimc_dbg("why am I here?\n"); + break; + } + + return depth; +} + +int fimc_s_fmt_vid_private(struct file *file, void *fh, struct v4l2_format *f) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + struct v4l2_mbus_framefmt *mbus_fmt; + int ret = 0; + + fimc_dbg("%s\n", __func__); + if (ctrl->cam->sd) { + struct v4l2_pix_format *pix = &f->fmt.pix; + int depth; + + fimc_info1("%s %d:\n", __func__, __LINE__); + + mbus_fmt = &ctrl->cap->mbus_fmt; + mbus_fmt->width = pix->width; + mbus_fmt->height = pix->height; +#ifdef CONFIG_MACH_P4NOTE +/* Unfortuntely, we have to use pix->field (not pix->priv) since + * pix.field is already used in the below else condtion statement + * (in case that sub-devices are not registered) + */ + mbus_fmt->field = pix->field; +#endif +#if (defined(CONFIG_MACH_S2PLUS) || defined(CONFIG_MACH_GC1)) + mbus_fmt->field = pix->priv; +#endif + printk(KERN_INFO "%s mbus_fmt->width = %d, height = %d,\n", + __func__,mbus_fmt->width ,mbus_fmt->height); + + depth = fimc_fmt_depth(ctrl, pix); + if (depth == 0) { + fimc_err("%s: Invalid pixel format\n", __func__); + return -EINVAL; + } else if (depth < 0) { /* JPEG */ + mbus_fmt->code = V4L2_MBUS_FMT_JPEG_1X8; + mbus_fmt->colorspace = V4L2_COLORSPACE_JPEG; + } else { + mbus_fmt->code = V4L2_MBUS_FMT_VYUY8_2X8; + } + + if (fimc_cam_use) { + ret = v4l2_subdev_call(ctrl->cam->sd, video, + s_mbus_fmt, mbus_fmt); + if (ret) { + fimc_err("%s: fail to s_mbus_fmt\n", __func__); + return ret; + } + } + + return 0; + } else { + mbus_fmt = kzalloc(sizeof(*mbus_fmt), GFP_KERNEL); + if (!mbus_fmt) { + fimc_err("%s: no memory for " + "mbus_fmt\n", __func__); + return -ENOMEM; + } + ctrl->is.fmt.width = f->fmt.pix.width; + ctrl->is.fmt.height = f->fmt.pix.height; + ctrl->is.fmt.pixelformat = f->fmt.pix.pixelformat; + + mbus_fmt->width = f->fmt.pix.width; + mbus_fmt->height = f->fmt.pix.height; + mbus_fmt->code = V4L2_MBUS_FMT_YUYV8_2X8; /*dummy*/ + mbus_fmt->field = f->fmt.pix.field; + mbus_fmt->colorspace = V4L2_COLORSPACE_SRGB; + + printk(KERN_INFO "%s mbus_fmt->width = %d, height = %d, \n", + __func__,mbus_fmt->width ,mbus_fmt->height); + if (fimc_cam_use) + ret = v4l2_subdev_call(ctrl->is.sd, video, + s_mbus_fmt, mbus_fmt); + kfree(mbus_fmt); + return ret; + } + + return -EINVAL; +} + +int fimc_s_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + struct fimc_capinfo *cap = ctrl->cap; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + + int ret = 0; + int depth; + struct v4l2_control is_ctrl; + + is_ctrl.id = 0; + is_ctrl.value = 0; + + printk(KERN_INFO "%s\n", __func__); + + if (!ctrl->cap) { + fimc_err("%s: No capture structure." \ + "you have to call s_input first.\n", __func__); + return -ENODEV; + } + + /* rotaton, flip, dtp_mode, movie_mode and vt_mode, + * sensor_output_width,height should be maintained.(by TN) */ + memset(cap, 0, sizeof(*cap) - sizeof(u32) * 7); + + mutex_lock(&ctrl->v4l2_lock); + + memset(&cap->fmt, 0, sizeof(cap->fmt)); + memcpy(&cap->fmt, &f->fmt.pix, sizeof(cap->fmt)); + + /* + * Note that expecting format only can be with + * available output format from FIMC + * Following items should be handled in driver + * bytesperline = width * depth / 8 + * sizeimage = bytesperline * height + */ + /* This function may return 0 or -1 in case of error, + * hence need to check here. + */ + + depth = fimc_fmt_depth(ctrl, &cap->fmt); + if (depth == 0) { + mutex_unlock(&ctrl->v4l2_lock); + fimc_err("%s: Invalid pixel format\n", __func__); + return -EINVAL; + } else if (depth < 0) { + /* + * When the pixelformat is JPEG, + * the application is requesting for data + * in JPEG compressed format + */ + cap->fmt.colorspace = V4L2_COLORSPACE_JPEG; + } else { + cap->fmt.bytesperline = (cap->fmt.width * depth) >> 3; + cap->fmt.sizeimage = (cap->fmt.bytesperline * cap->fmt.height); + } + + + if (cap->fmt.pixelformat == V4L2_PIX_FMT_JPEG || + cap->fmt.pixelformat == V4L2_PIX_FMT_INTERLEAVED) { + ctrl->sc.bypass = 1; + cap->lastirq = 0; + fimc_info1("fimc_s_fmt_vid_capture V4L2_COLORSPACE_JPEG or INTERLEAVED\n"); + } else { +#ifdef CONFIG_MACH_GC1 + /* + Fimc scaler input Hsize is restricted to 4224 pixels. + So, GC1 has to bypass fimc scaler to use more than 12M YUV. + */ + ctrl->sc.bypass = 1; +#else + ctrl->sc.bypass = 0; +#endif + cap->lastirq = 0; + } + + printk(KERN_INFO "fimc%d s_fmt width = %d, height = %d\n", ctrl->id, \ + cap->fmt.width, cap->fmt.height); + + /* WriteBack doesn't have subdev_call */ + if (ctrl->cam->id == CAMERA_WB || ctrl->cam->id == CAMERA_WB_B) { + mutex_unlock(&ctrl->v4l2_lock); + return 0; + } + + if (ctrl->is.sd && fimc_cam_use) { + ctrl->is.mbus_fmt.code = V4L2_MBUS_FMT_SGRBG10_1X10; + is_ctrl.id = V4L2_CID_IS_GET_SENSOR_WIDTH; + is_ctrl.value = 0; + v4l2_subdev_call(ctrl->is.sd, core, g_ctrl, &is_ctrl); + ctrl->is.fmt.width = ctrl->is.mbus_fmt.width = is_ctrl.value; + + is_ctrl.id = V4L2_CID_IS_GET_SENSOR_HEIGHT; + is_ctrl.value = 0; + v4l2_subdev_call(ctrl->is.sd, core, g_ctrl, &is_ctrl); + ctrl->is.fmt.height = ctrl->is.mbus_fmt.height = is_ctrl.value; + /* default offset values */ + ctrl->is.offset_x = 16; + ctrl->is.offset_y = 12; + } + + fimc_hwset_reset(ctrl); + + mutex_unlock(&ctrl->v4l2_lock); + printk(KERN_INFO "%s -- FIMC%d\n", __func__, ctrl->id); + + return ret; +} + +int fimc_try_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f) +{ + /* Not implement */ + return -ENOTTY; +} + +static int fimc_alloc_buffers(struct fimc_control *ctrl, + int plane, int size, int align, int bpp, int use_paddingbuf, int pad_size) +{ + struct fimc_capinfo *cap = ctrl->cap; + int i, j; + int plane_length[4] = {0, }; +#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM + int alloc_size, err; + struct cma_info mem_info; +#endif + + switch (plane) { + case 1: + if (align) { + plane_length[0] = PAGE_ALIGN((size*bpp) >> 3); + plane_length[1] = 0; + plane_length[2] = 0; + } else { + plane_length[0] = (size*bpp) >> 3; + plane_length[1] = 0; + plane_length[2] = 0; + } + break; + /* In case of 2, only NV12 and NV12T is supported. */ + case 2: + if (align) { + plane_length[0] = PAGE_ALIGN((size*8) >> 3); + plane_length[1] = PAGE_ALIGN((size*(bpp-8)) >> 3); + plane_length[2] = 0; + fimc_info2("plane_length[0] = %d, plane_length[1] = %d\n" \ + , plane_length[0], plane_length[1]); + } else { + plane_length[0] = ((size*8) >> 3); + plane_length[1] = ((size*(bpp-8)) >> 3); + plane_length[2] = 0; + fimc_info2("plane_length[0] = %d, plane_length[1] = %d\n" \ + , plane_length[0], plane_length[1]); + } + + break; + /* In case of 3 + * YUV422 : 8 / 4 / 4 (bits) + * YUV420 : 8 / 2 / 2 (bits) + * 3rd plane have to consider page align for mmap */ + case 3: + if (align) { + plane_length[0] = (size*8) >> 3; + plane_length[1] = (size*((bpp-8)/2)) >> 3; + plane_length[2] = PAGE_ALIGN((size*bpp)>>3) - plane_length[0] + - plane_length[1]; + } else { + plane_length[0] = (size*8) >> 3; + plane_length[1] = (size*((bpp-8)/2)) >> 3; + plane_length[2] = ((size*bpp)>>3) - plane_length[0] + - plane_length[1]; + } + break; + default: + fimc_err("impossible!\n"); + return -ENOMEM; + } + + if (use_paddingbuf) { + plane_length[plane] = pad_size; + cap->pktdata_plane = plane; + } else + plane_length[plane] = 0; + +#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM + if (align) { + alloc_size = (ALIGN(plane_length[0], align) + + ALIGN(plane_length[1], align) + + ALIGN(plane_length[2], align)) + * cap->nr_bufs; + } else { + alloc_size = (plane_length[0] + plane_length[1] + + plane_length[2]) * cap->nr_bufs; + } + + err = cma_info(&mem_info, ctrl->dev, 0); + printk(KERN_DEBUG "%s : [cma_info] start_addr : 0x%x, end_addr : 0x%x, " + "total_size : 0x%x, free_size : 0x%x req_size : 0x%x\n", + __func__, mem_info.lower_bound, mem_info.upper_bound, + mem_info.total_size, mem_info.free_size, alloc_size); + + if (err || (mem_info.free_size < alloc_size)) { + fimc_err("%s: get cma info failed\n", __func__); + ctrl->mem.size = 0; + ctrl->mem.base = 0; + return -ENOMEM; + } else { + ctrl->mem.size = alloc_size; + ctrl->mem.base = (dma_addr_t)cma_alloc + (ctrl->dev, ctrl->cma_name, (size_t) alloc_size, align); + } + + ctrl->mem.curr = ctrl->mem.base; +#endif + for (i = 0; i < cap->nr_bufs; i++) { + for (j = 0; j < plane; j++) { + cap->bufs[i].length[j] = plane_length[j]; + fimc_dma_alloc(ctrl, &cap->bufs[i], j, align); + + if (!cap->bufs[i].base[j]) + goto err_alloc; + } + if (use_paddingbuf) { + cap->bufs[i].length[plane] = plane_length[plane]; + fimc_dma_alloc(ctrl, &cap->bufs[i], plane, align); + + cap->bufs[i].vaddr_pktdata = phys_to_virt(cap->bufs[i].base[plane]); + /* printk(KERN_INFO "pktdata address = 0x%x, 0x%x\n" + ,cap->bufs[i].base[1], cap->bufs[i].vaddr_pktdata ); */ + + if (!cap->bufs[i].base[plane]) + goto err_alloc; + } + cap->bufs[i].state = VIDEOBUF_PREPARED; + } + + return 0; + +err_alloc: + for (i = 0; i < cap->nr_bufs; i++) { + for (j = 0; j < plane; j++) { + if (cap->bufs[i].base[j]) + fimc_dma_free(ctrl, &cap->bufs[i], j); + } + if (use_paddingbuf) { + if (cap->bufs[i].base[plane]) + fimc_dma_free(ctrl, &cap->bufs[i], plane); + } + memset(&cap->bufs[i], 0, sizeof(cap->bufs[i])); + } + + return -ENOMEM; +} + +static void fimc_free_buffers(struct fimc_control *ctrl) +{ + struct fimc_capinfo *cap; + int i; + + if (ctrl && ctrl->cap) + cap = ctrl->cap; + else + return; + + for (i = 0; i < FIMC_PHYBUFS; i++) { + memset(&cap->bufs[i], 0, sizeof(cap->bufs[i])); + cap->bufs[i].state = VIDEOBUF_NEEDS_INIT; + } + + ctrl->mem.curr = ctrl->mem.base; +} + +int fimc_reqbufs_capture_mmap(void *fh, struct v4l2_requestbuffers *b) +{ + struct fimc_control *ctrl = fh; + struct fimc_capinfo *cap = ctrl->cap; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + struct platform_device *pdev = to_platform_device(ctrl->dev); +#endif + int ret = 0, i; + int bpp = 0; + int size = 0; + + if (!cap) { + fimc_err("%s: no capture device info\n", __func__); + return -ENODEV; + } + + mutex_lock(&ctrl->v4l2_lock); + + /* A count value of zero frees all buffers */ + if ((b->count == 0) || (b->count >= FIMC_CAPBUFS)) { + /* aborting or finishing any DMA in progress */ + if (ctrl->status == FIMC_STREAMON) + fimc_streamoff_capture(fh); + for (i = 0; i < FIMC_CAPBUFS; i++) { + fimc_dma_free(ctrl, &ctrl->cap->bufs[i], 0); + fimc_dma_free(ctrl, &ctrl->cap->bufs[i], 1); + fimc_dma_free(ctrl, &ctrl->cap->bufs[i], 2); + } +#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM + if (ctrl->mem.base) { + cma_free(ctrl->mem.base); + ctrl->mem.base = 0; + ctrl->mem.size = 0; + } +#endif + + mutex_unlock(&ctrl->v4l2_lock); + return 0; + } + /* free previous buffers */ + if ((cap->nr_bufs >= 0) && (cap->nr_bufs < FIMC_CAPBUFS)) { + fimc_info1("%s : remained previous buffer count is %d\n", __func__, + cap->nr_bufs); + for (i = 0; i < cap->nr_bufs; i++) { + fimc_dma_free(ctrl, &cap->bufs[i], 0); + fimc_dma_free(ctrl, &cap->bufs[i], 1); + fimc_dma_free(ctrl, &cap->bufs[i], 2); + fimc_dma_free(ctrl, &cap->bufs[i], 3); + } +#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM + if (ctrl->mem.base) { + cma_free(ctrl->mem.base); + ctrl->mem.base = 0; + ctrl->mem.size = 0; + } +#endif + } + fimc_free_buffers(ctrl); + + cap->nr_bufs = b->count; + if (pdata->hw_ver >= 0x51) { +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + if (ctrl->power_status == FIMC_POWER_OFF) { + pm_runtime_get_sync(&pdev->dev); + } +#endif + fimc_hw_reset_output_buf_sequence(ctrl); + for (i = 0; i < cap->nr_bufs; i++) { + fimc_hwset_output_buf_sequence(ctrl, i, 1); + cap->bufs[i].id = i; + cap->bufs[i].state = VIDEOBUF_NEEDS_INIT; + + /* initialize list */ + INIT_LIST_HEAD(&cap->bufs[i].list); + } + fimc_info1("%s: requested %d buffers\n", __func__, b->count); + fimc_info1("%s: sequence[%d]\n", __func__, + fimc_hwget_output_buf_sequence(ctrl)); + INIT_LIST_HEAD(&cap->outgoing_q); + } + if (pdata->hw_ver < 0x51) { + INIT_LIST_HEAD(&cap->inq); + for (i = 0; i < cap->nr_bufs; i++) { + cap->bufs[i].id = i; + cap->bufs[i].state = VIDEOBUF_NEEDS_INIT; + + /* initialize list */ + INIT_LIST_HEAD(&cap->bufs[i].list); + } + } + + if (cap->pktdata_enable) + cap->pktdata_size = 0x1000; + + bpp = fimc_fmt_depth(ctrl, &cap->fmt); + + switch (cap->fmt.pixelformat) { + case V4L2_PIX_FMT_RGB32: /* fall through */ + case V4L2_PIX_FMT_RGB565: /* fall through */ + case V4L2_PIX_FMT_YUYV: /* fall through */ + case V4L2_PIX_FMT_UYVY: /* fall through */ + case V4L2_PIX_FMT_VYUY: /* fall through */ + case V4L2_PIX_FMT_YVYU: /* fall through */ + case V4L2_PIX_FMT_NV16: /* fall through */ + case V4L2_PIX_FMT_NV61: /* fall through */ + fimc_info1("%s : 1plane\n", __func__); + ret = fimc_alloc_buffers(ctrl, 1, + cap->fmt.width * cap->fmt.height, SZ_4K, bpp, cap->pktdata_enable, cap->pktdata_size); + break; + + case V4L2_PIX_FMT_NV21: + fimc_info1("%s : 2plane for NV21 w %d h %d\n", __func__, + cap->fmt.width, cap->fmt.height); + ret = fimc_alloc_buffers(ctrl, 2, + cap->fmt.width * cap->fmt.height, 0, bpp, cap->pktdata_enable, cap->pktdata_size); + break; + + case V4L2_PIX_FMT_NV12: + fimc_info1("%s : 2plane for NV12\n", __func__); + ret = fimc_alloc_buffers(ctrl, 2, + cap->fmt.width * cap->fmt.height, SZ_64K, bpp, cap->pktdata_enable, cap->pktdata_size); + break; + + case V4L2_PIX_FMT_NV12T: + fimc_info1("%s : 2plane for NV12T\n", __func__); + ret = fimc_alloc_buffers(ctrl, 2, + ALIGN(cap->fmt.width, 128) * ALIGN(cap->fmt.height, 32), + SZ_64K, bpp, cap->pktdata_enable, cap->pktdata_size); + break; + + case V4L2_PIX_FMT_YUV422P: /* fall through */ + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + fimc_info1("%s : 3plane\n", __func__); + ret = fimc_alloc_buffers(ctrl, 3, + cap->fmt.width * cap->fmt.height, 0, bpp, cap->pktdata_enable, cap->pktdata_size); + break; + + case V4L2_PIX_FMT_JPEG: + fimc_info1("%s : JPEG 1plane\n", __func__); + size = fimc_camera_get_jpeg_memsize(ctrl); + fimc_info2("%s : JPEG 1plane size = %x\n", __func__, size); + ret = fimc_alloc_buffers(ctrl, 1, size, 0, 8, cap->pktdata_enable, cap->pktdata_size); + break; + case V4L2_PIX_FMT_INTERLEAVED: + fimc_info1("%s : Interleaved Format\n", __func__); + size = fimc_camera_get_jpeg_memsize(ctrl); /*0xA00000*/ + fimc_info2("%s : Interleaved size = %x\n", __func__, size); + ret = fimc_alloc_buffers(ctrl, 1, size, 0, 8, cap->pktdata_enable, cap->pktdata_size); + break; + default: + break; + } + + if (ret) { + fimc_err("%s: no memory for capture buffer\n", __func__); + mutex_unlock(&ctrl->v4l2_lock); + return -ENOMEM; + } + + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +int fimc_reqbufs_capture_userptr(void *fh, struct v4l2_requestbuffers *b) +{ + struct fimc_control *ctrl = fh; + struct fimc_capinfo *cap = ctrl->cap; +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + struct platform_device *pdev = to_platform_device(ctrl->dev); +#endif + int i; + + if (!cap) { + fimc_err("%s: no capture device info\n", __func__); + return -ENODEV; + } + + mutex_lock(&ctrl->v4l2_lock); + + /* A count value of zero frees all buffers */ + if ((b->count == 0) || (b->count >= FIMC_CAPBUFS)) { + /* aborting or finishing any DMA in progress */ + if (ctrl->status == FIMC_STREAMON) + fimc_streamoff_capture(fh); + + fimc_free_buffers(ctrl); + + mutex_unlock(&ctrl->v4l2_lock); + return 0; + } + + /* free previous buffers */ + if ((cap->nr_bufs >= 0) && (cap->nr_bufs < FIMC_CAPBUFS)) { + fimc_info1("%s: prev buf cnt(%d)\n", __func__, cap->nr_bufs); + fimc_free_buffers(ctrl); + } + + cap->nr_bufs = b->count; + +#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) + if (ctrl->power_status == FIMC_POWER_OFF) { + pm_runtime_get_sync(&pdev->dev); + } +#endif + fimc_hw_reset_output_buf_sequence(ctrl); + for (i = 0; i < cap->nr_bufs; i++) { + fimc_hwset_output_buf_sequence(ctrl, i, 1); + cap->bufs[i].id = i; + cap->bufs[i].state = VIDEOBUF_IDLE; + + /* initialize list */ + INIT_LIST_HEAD(&cap->bufs[i].list); + } + fimc_info1("%s: requested %d buffers\n", __func__, b->count); + fimc_info1("%s: sequence[%d]\n", __func__, + fimc_hwget_output_buf_sequence(ctrl)); + INIT_LIST_HEAD(&cap->outgoing_q); + + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +int fimc_reqbufs_capture(void *fh, struct v4l2_requestbuffers *b) +{ + int ret = 0; + + if (b->memory == V4L2_MEMORY_MMAP) + ret = fimc_reqbufs_capture_mmap(fh, b); + else + ret = fimc_reqbufs_capture_userptr(fh, b); + + return ret; +} + +int fimc_querybuf_capture(void *fh, struct v4l2_buffer *b) +{ + struct fimc_control *ctrl = fh; + struct fimc_capinfo *cap = ctrl->cap; + + if (ctrl->status != FIMC_STREAMOFF) { + fimc_err("fimc is running\n"); + return -EBUSY; + } + + mutex_lock(&ctrl->v4l2_lock); + + switch (cap->fmt.pixelformat) { + case V4L2_PIX_FMT_JPEG: /* fall through */ + case V4L2_PIX_FMT_RGB32: /* fall through */ + case V4L2_PIX_FMT_RGB565: /* fall through */ + case V4L2_PIX_FMT_YUYV: /* fall through */ + case V4L2_PIX_FMT_UYVY: /* fall through */ + case V4L2_PIX_FMT_VYUY: /* fall through */ + case V4L2_PIX_FMT_YVYU: /* fall through */ + case V4L2_PIX_FMT_NV16: /* fall through */ + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_INTERLEAVED: + b->length = cap->bufs[b->index].length[0]; + break; + + case V4L2_PIX_FMT_NV21: + b->length = ctrl->cap->bufs[b->index].length[0] + + ctrl->cap->bufs[b->index].length[1]; + break; + case V4L2_PIX_FMT_NV12: /* fall through */ + case V4L2_PIX_FMT_NV12T: + b->length = ALIGN(ctrl->cap->bufs[b->index].length[0], SZ_64K) + + ALIGN(ctrl->cap->bufs[b->index].length[1], SZ_64K); + break; + case V4L2_PIX_FMT_YUV422P: /* fall through */ + case V4L2_PIX_FMT_YUV420: /* fall through */ + case V4L2_PIX_FMT_YVU420: + b->length = ctrl->cap->bufs[b->index].length[0] + + ctrl->cap->bufs[b->index].length[1] + + ctrl->cap->bufs[b->index].length[2]; + break; + + default: + b->length = cap->bufs[b->index].length[0]; + break; + } + + if (cap->pktdata_enable) + b->length += ctrl->cap->bufs[b->index].length[cap->pktdata_plane]; + + b->m.offset = b->index * PAGE_SIZE; + /* memory field should filled V4L2_MEMORY_MMAP */ + b->memory = V4L2_MEMORY_MMAP; + + ctrl->cap->bufs[b->index].state = VIDEOBUF_IDLE; + + fimc_dbg("%s: %d bytes with offset: %d\n", + __func__, b->length, b->m.offset); + + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +int fimc_g_ctrl_capture(void *fh, struct v4l2_control *c) +{ + struct fimc_control *ctrl = fh; + int ret = 0; + + fimc_dbg("%s\n", __func__); + + switch (c->id) { + case V4L2_CID_ROTATION: + c->value = ctrl->cap->rotate; + break; + + case V4L2_CID_HFLIP: + c->value = (ctrl->cap->flip & FIMC_XFLIP) ? 1 : 0; + break; + + case V4L2_CID_VFLIP: + c->value = (ctrl->cap->flip & FIMC_YFLIP) ? 1 : 0; + break; + + case V4L2_CID_CACHEABLE: + c->value = ctrl->cap->cacheable; + break; + + default: + /* get ctrl supported by subdev */ + /* WriteBack doesn't have subdev_call */ + if ((ctrl->cam->id == CAMERA_WB) || (ctrl->cam->id == CAMERA_WB_B)) + break; + if (ctrl->cam->sd) + ret = v4l2_subdev_call(ctrl->cam->sd, core, g_ctrl, c); + if (ctrl->is.sd) + ret = v4l2_subdev_call(ctrl->is.sd, core, g_ctrl, c); + break; + } + + return ret; +} + +int fimc_s_ctrl_capture(void *fh, struct v4l2_control *c) +{ + struct fimc_control *ctrl = fh; + struct fimc_global *fimc = get_fimc_dev(); + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + int ret = 0; + + fimc_dbg("%s\n", __func__); + + if (!ctrl->cam || !ctrl->cap || + ((ctrl->cam->id != CAMERA_WB && ctrl->cam->id != CAMERA_WB_B) && + (!ctrl->cam->sd) && (!ctrl->is.sd))) { + fimc_err("%s: No capture device.\n", __func__); + return -ENODEV; + } + + switch (c->id) { +#ifdef CONFIG_MACH_GC1 + case V4L2_CID_CAM_UPDATE_FW: + if (fimc->mclk_status == CAM_MCLK_ON) { + if (ctrl->cam->cam_power) + ctrl->cam->cam_power(0); + + /* shutdown the MCLK */ + clk_disable(ctrl->cam->clk); + fimc->mclk_status = CAM_MCLK_OFF; + + mdelay(5); + } + + if ((clk_get_rate(ctrl->cam->clk)) && (fimc->mclk_status == CAM_MCLK_OFF)) { + clk_set_rate(ctrl->cam->clk, ctrl->cam->clk_rate); + clk_enable(ctrl->cam->clk); + fimc->mclk_status = CAM_MCLK_ON; + fimc_info1("clock for camera: %d\n", ctrl->cam->clk_rate); + + if (ctrl->cam->cam_power) + ctrl->cam->cam_power(1); + } + + if (c->value == FW_MODE_UPDATE) + ret = v4l2_subdev_call(ctrl->cam->sd, core, load_fw); + + else + ret = v4l2_subdev_call(ctrl->cam->sd, core, s_ctrl, c); + break; +#endif + case V4L2_CID_CAMERA_RESET: + fimc_warn("ESD: reset the camera sensor\n"); + if (ctrl->cam->initialized) { + if (ctrl->cam->cam_power) + ctrl->cam->cam_power(0); + + /* shutdown the MCLK */ + clk_disable(ctrl->cam->clk); + fimc->mclk_status = CAM_MCLK_OFF; + ctrl->cam->initialized = 0; +#ifdef CONFIG_MACH_P4NOTE + /* 100ms: increase delay. + * There are cases that sensor doesn't get revived + * inspite of doing power reset.*/ + msleep(100); +#else + msleep(5); +#endif + } + if (ctrl->cam->sd) { + fimc_warn("ESD: init external sensor\n"); + ret = fimc_init_camera(ctrl); + } + if (ctrl->is.sd && ctrl->cam->use_isp) { + fimc_warn("ESD: init FIMC-IS\n"); + ret = v4l2_subdev_call(ctrl->is.sd, core, s_power, 0); + if (ret < 0) { + fimc_err("ESD : FIMC-IS power off failed"); + return -EINVAL; + } + ret = fimc_is_init_cam(ctrl); + if (ret < 0) { + fimc_err("ESD : FIMC-IS init clock failed"); + return -EINVAL; + } + ret = v4l2_subdev_call(ctrl->is.sd, core, s_power, 1); + if (ret < 0) { + fimc_err("ESD : FIMC-IS power on failed"); + return -EINVAL; + } + ret = v4l2_subdev_call(ctrl->is.sd, core, load_fw); + if (ret < 0) { + fimc_dbg("ESD : FIMC-IS load FW failed"); + return -EINVAL; + } + ret = v4l2_subdev_call(ctrl->is.sd, core, + init, ctrl->cam->sensor_index); + if (ret < 0) { + fimc_err("ESD : FIMC-IS init failed"); + return -EINVAL; + } + } + break; + case V4L2_CID_ROTATION: + ctrl->cap->rotate = c->value; + break; + + case V4L2_CID_HFLIP: + if (c->value) + ctrl->cap->flip |= FIMC_YFLIP; + else + ctrl->cap->flip &= ~FIMC_YFLIP; + break; + + case V4L2_CID_VFLIP: + if (c->value) + ctrl->cap->flip |= FIMC_XFLIP; + else + ctrl->cap->flip &= ~FIMC_XFLIP; + break; + + case V4L2_CID_PADDR_Y: + if (ctrl->cap->bufs[c->value].length[FIMC_ADDR_Y]) + c->value = ctrl->cap->bufs[c->value].base[FIMC_ADDR_Y]; + break; + + case V4L2_CID_PADDR_CB: /* fall through */ + case V4L2_CID_PADDR_CBCR: + if (ctrl->cap->bufs[c->value].length[FIMC_ADDR_CB]) + c->value = ctrl->cap->bufs[c->value].base[FIMC_ADDR_CB]; + break; + + case V4L2_CID_PADDR_CR: + if (ctrl->cap->bufs[c->value].length[FIMC_ADDR_CR]) + c->value = ctrl->cap->bufs[c->value].base[FIMC_ADDR_CR]; + break; + /* Implementation as per C100 FIMC driver */ + case V4L2_CID_STREAM_PAUSE: + fimc_hwset_stop_processing(ctrl); + break; + + case V4L2_CID_IMAGE_EFFECT_APPLY: + ctrl->fe.ie_on = c->value ? 1 : 0; + ctrl->fe.ie_after_sc = 0; + ret = fimc_hwset_image_effect(ctrl); + break; + + case V4L2_CID_IMAGE_EFFECT_FN: + if (c->value < 0 || c->value > FIMC_EFFECT_FIN_SILHOUETTE) + return -EINVAL; + ctrl->fe.fin = c->value; + ret = 0; + break; + + case V4L2_CID_IMAGE_EFFECT_CB: + ctrl->fe.pat_cb = c->value & 0xFF; + ret = 0; + break; + + case V4L2_CID_IMAGE_EFFECT_CR: + ctrl->fe.pat_cr = c->value & 0xFF; + ret = 0; + break; + + case V4L2_CID_IS_LOAD_FW: + if (ctrl->is.sd && fimc_cam_use) + ret = v4l2_subdev_call(ctrl->is.sd, core, s_power, c->value); + break; + case V4L2_CID_IS_RESET: + if (ctrl->is.sd && fimc_cam_use) + ret = v4l2_subdev_call(ctrl->is.sd, core, reset, c->value); + break; + case V4L2_CID_IS_S_POWER: + if (ctrl->is.sd && fimc_cam_use) + ret = v4l2_subdev_call(ctrl->is.sd, core, s_power, c->value); + break; + case V4L2_CID_IS_S_STREAM: + if (ctrl->is.sd && fimc_cam_use) + ret = v4l2_subdev_call(ctrl->is.sd, video, s_stream, c->value); + break; + case V4L2_CID_CACHEABLE: + ctrl->cap->cacheable = c->value; + ret = 0; + break; + + case V4L2_CID_EMBEDDEDDATA_ENABLE: + ctrl->cap->pktdata_enable = c->value; + ret = 0; + break; + + case V4L2_CID_IS_ZOOM: + fimc_is_set_zoom(ctrl, c); + break; + + case V4L2_CID_CAMERA_SENSOR_MODE: + ctrl->cap->movie_mode = c->value; + if (ctrl->cam->sd && fimc_cam_use) + ret = v4l2_subdev_call(ctrl->cam->sd, core, s_ctrl, c); + break; + + case V4L2_CID_CAMERA_VT_MODE: + ctrl->cap->vt_mode = c->value; + if (fimc_cam_use) { + if (ctrl->cam->sd) + ret = v4l2_subdev_call(ctrl->cam->sd, + core, s_ctrl, c); + if (ctrl->is.sd && ctrl->cam->use_isp) + ret = v4l2_subdev_call(ctrl->is.sd, + core, s_ctrl, c); + } + break; + + case V4L2_CID_CAMERA_SENSOR_OUTPUT_SIZE: + ctrl->cap->sensor_output_width = (u32)c->value >> 16; + ctrl->cap->sensor_output_height = (u32)c->value & 0x0FFFF; + printk(KERN_DEBUG "sensor output size: %dx%d\n", + ctrl->cap->sensor_output_width, + ctrl->cap->sensor_output_height); + break; + +#if defined(CONFIG_BUSFREQ_OPP) || defined(CONFIG_BUSFREQ_LOCK_WRAPPER) + case V4L2_CID_CAMERA_BUSFREQ_LOCK: + /* lock bus frequency */ + dev_lock(ctrl->bus_dev, ctrl->dev, (unsigned long)c->value); + break; + case V4L2_CID_CAMERA_BUSFREQ_UNLOCK: + /* unlock bus frequency */ + dev_unlock(ctrl->bus_dev, ctrl->dev); + break; +#endif + + case V4L2_CID_IS_CAMERA_FLASH_MODE: + case V4L2_CID_CAMERA_SCENE_MODE: + default: + /* try on subdev */ + /* WriteBack doesn't have subdev_call */ + + if ((ctrl->cam->id == CAMERA_WB) || \ + (ctrl->cam->id == CAMERA_WB_B)) + break; + if (fimc_cam_use) { + if (ctrl->cam->sd) + ret = v4l2_subdev_call(ctrl->cam->sd, + core, s_ctrl, c); + if (ctrl->is.sd && ctrl->cam->use_isp) + ret = v4l2_subdev_call(ctrl->is.sd, + core, s_ctrl, c); + } else + ret = 0; + break; + } + + return ret; +} + +int fimc_g_ext_ctrls_capture(void *fh, struct v4l2_ext_controls *c) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + int ret = 0; + mutex_lock(&ctrl->v4l2_lock); + + if (ctrl->cam->sd) + /* try on subdev */ + ret = v4l2_subdev_call(ctrl->cam->sd, core, g_ext_ctrls, c); + if (ctrl->is.sd) + /* try on subdev */ + ret = v4l2_subdev_call(ctrl->is.sd, core, g_ext_ctrls, c); + + mutex_unlock(&ctrl->v4l2_lock); + + return ret; +} + +int fimc_s_ext_ctrls_capture(void *fh, struct v4l2_ext_controls *c) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + int ret = 0; + mutex_lock(&ctrl->v4l2_lock); + + if (ctrl->cam->sd) + /* try on subdev */ + ret = v4l2_subdev_call(ctrl->cam->sd, core, s_ext_ctrls, c); + else if (ctrl->is.sd) + ret = v4l2_subdev_call(ctrl->is.sd, core, s_ext_ctrls, c); + + mutex_unlock(&ctrl->v4l2_lock); + + return ret; +} + +int fimc_cropcap_capture(void *fh, struct v4l2_cropcap *a) +{ + struct fimc_control *ctrl = fh; + struct fimc_capinfo *cap = ctrl->cap; + struct fimc_global *fimc = get_fimc_dev(); + struct s3c_platform_fimc *pdata; + + fimc_dbg("%s\n", __func__); + + if (!ctrl->cam || !ctrl->cam->sd || !ctrl->cap) { + fimc_err("%s: No capture device.\n", __func__); + return -ENODEV; + } + mutex_lock(&ctrl->v4l2_lock); + + pdata = to_fimc_plat(ctrl->dev); + if (!ctrl->cam) + ctrl->cam = fimc->camera[pdata->default_cam]; + + if (!cap) { + cap = kzalloc(sizeof(*cap), GFP_KERNEL); + if (!cap) { + fimc_err("%s: no memory for " + "capture device info\n", __func__); + return -ENOMEM; + } + + /* assign to ctrl */ + ctrl->cap = cap; + } + + /* crop limitations */ + cap->cropcap.bounds.left = 0; + cap->cropcap.bounds.top = 0; + cap->cropcap.bounds.width = ctrl->cam->width; + cap->cropcap.bounds.height = ctrl->cam->height; + + /* crop default values */ + cap->cropcap.defrect.left = 0; + cap->cropcap.defrect.top = 0; + cap->cropcap.defrect.width = ctrl->cam->width; + cap->cropcap.defrect.height = ctrl->cam->height; + + a->bounds = cap->cropcap.bounds; + a->defrect = cap->cropcap.defrect; + + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +int fimc_g_crop_capture(void *fh, struct v4l2_crop *a) +{ + struct fimc_control *ctrl = fh; + + fimc_dbg("%s\n", __func__); + + if (!ctrl->cap) { + fimc_err("%s: No capture device.\n", __func__); + return -ENODEV; + } + + mutex_lock(&ctrl->v4l2_lock); + a->c = ctrl->cap->crop; + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +int fimc_s_crop_capture(void *fh, struct v4l2_crop *a) +{ + struct fimc_control *ctrl = fh; + + fimc_dbg("%s\n", __func__); + + mutex_lock(&ctrl->v4l2_lock); + ctrl->cap->crop = a->c; + mutex_unlock(&ctrl->v4l2_lock); + + return 0; +} + +int fimc_start_capture(struct fimc_control *ctrl) +{ + fimc_dbg("%s\n", __func__); + + fimc_reset_status_reg(ctrl); + + if (!ctrl->sc.bypass) + fimc_hwset_start_scaler(ctrl); + + fimc_hwset_enable_capture(ctrl, ctrl->sc.bypass); + fimc_hwset_disable_frame_end_irq(ctrl); + + return 0; +} + +int fimc_stop_capture(struct fimc_control *ctrl) +{ + fimc_dbg("%s\n", __func__); + if (!ctrl->cam) { + fimc_err("%s: No capture device.\n", __func__); + return -ENODEV; + } + + if (!ctrl->cap) { + fimc_err("%s: No cappure format.\n", __func__); + return -ENODEV; + } + + if (ctrl->cap->lastirq) { + fimc_hwset_enable_lastirq(ctrl); + fimc_hwset_disable_capture(ctrl); + fimc_hwset_disable_lastirq(ctrl); + } else { + fimc_hwset_disable_capture(ctrl); + fimc_hwset_enable_frame_end_irq(ctrl); + } + + fimc_hwset_stop_scaler(ctrl); + + return 0; +} + +static int fimc_check_capture_source(struct fimc_control *ctrl) +{ + if(!ctrl->cam) + return -ENODEV; + + if (ctrl->cam->sd || ctrl->is.sd || !ctrl->flite_sd) + return 0; + + if (ctrl->cam->id == CAMERA_WB || ctrl->cam->id == CAMERA_WB_B) + return 0; + + return -ENODEV; +} + +static int is_scale_up(struct fimc_control *ctrl) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &ctrl->cap->mbus_fmt; + struct v4l2_pix_format *pix = &ctrl->cap->fmt; + + if (!mbus_fmt->width) { + fimc_err("%s: sensor resolution isn't selected.\n", __func__); + return -EINVAL; + } + + if (ctrl->cap->rotate == 90 || ctrl->cap->rotate == 270) { + if (pix->width > mbus_fmt->height || + pix->height > mbus_fmt->width) { + fimc_err("%s: ScaleUp isn't supported.\n", __func__); + return -EINVAL; + } + } else { + if (pix->width > mbus_fmt->width || + pix->height > mbus_fmt->height) { + fimc_err("%s: ScaleUp isn't supported.\n", __func__); + return -EINVAL; + } + } + + return 0; +} + +int fimc_streamon_capture(void *fh) +{ + struct fimc_control *ctrl = fh; + struct fimc_capinfo *cap = ctrl->cap; + struct v4l2_frmsizeenum cam_frmsize; + struct v4l2_control is_ctrl; + void __iomem *qos_regs; + + int rot = 0, i; + int ret = 0; + struct s3c_platform_camera *cam = NULL; + + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + + printk(KERN_INFO "%s++ fimc%d\n", __func__, ctrl->id); + cam_frmsize.discrete.width = 0; + cam_frmsize.discrete.height = 0; + is_ctrl.id = 0; + is_ctrl.value = 0; + + if (!ctrl->cam) { + fimc_err("%s: ctrl->cam is null\n", __func__); + return -EINVAL; + } else { + cam = ctrl->cam; + } + + if (fimc_check_capture_source(ctrl)) { + fimc_err("%s: No capture device.\n", __func__); + return -ENODEV; + } + + if (cam->sd) { + if (is_scale_up(ctrl)) + return -EINVAL; + } + + if (pdata->hw_ver < 0x51) + fimc_hw_reset_camera(ctrl); +#if (!defined(CONFIG_EXYNOS_DEV_PD) && !defined(CONFIG_PM_RUNTIME)) + ctrl->status = FIMC_READY_ON; +#endif + cap->irq = 0; + + fimc_hwset_enable_irq(ctrl, 0, 1); + + if ((cam->id != CAMERA_WB) && (cam->id != CAMERA_WB_B)) { + if (fimc_cam_use && cam->sd) { + ret = v4l2_subdev_call(cam->sd, video, enum_framesizes, + &cam_frmsize); + if (ret < 0) { + dev_err(ctrl->dev, "%s: enum_framesizes failed\n", + __func__); + if (ret != -ENOIOCTLCMD) + return ret; + } else { + if (cam_frmsize.discrete.width > 0 + && cam_frmsize.discrete.height > 0) { + cam->window.left = 0; + cam->window.top = 0; + cam->width = cam->window.width + = cam_frmsize.discrete.width; + cam->height + = cam->window.height + = cam_frmsize.discrete.height; + printk(KERN_INFO "%s cam real size width = %d," + "height = %d\n",__func__, ctrl->cam->width, + ctrl->cam->height); + } + } + +#ifdef CONFIG_MACH_P4NOTE +#ifdef CONFIG_VIDEO_IMPROVE_STREAMOFF + v4l2_subdev_call(cam->sd, video, s_stream, + STREAM_MODE_WAIT_OFF); +#endif /* CONFIG_VIDEO_IMPROVE_STREAMOFF */ +#else /* !CONFIG_MACH_P4NOTE */ + if (cap->fmt.priv == V4L2_PIX_FMT_MODE_CAPTURE) { + ret = v4l2_subdev_call(cam->sd, video, s_stream, 1); + if (ret < 0) { + dev_err(ctrl->dev, "%s: s_stream failed\n", + __func__); + return ret; + } + } +#endif + if (cam->type == CAM_TYPE_MIPI) { + if (cam->id == CAMERA_CSI_C) { + s3c_csis_enable_pktdata(CSI_CH_0, cap->pktdata_enable); + s3c_csis_start(CSI_CH_0, cam->mipi_lanes, + cam->mipi_settle, cam->mipi_align, + cam->width, cam->height, + cap->fmt.pixelformat); + } else { + s3c_csis_enable_pktdata(CSI_CH_1, cap->pktdata_enable); + s3c_csis_start(CSI_CH_1, cam->mipi_lanes, + cam->mipi_settle, cam->mipi_align, + cam->width, cam->height, + cap->fmt.pixelformat); + } + } +#ifdef CONFIG_MACH_P4NOTE + if (1) { +#else + if (cap->fmt.priv != V4L2_PIX_FMT_MODE_CAPTURE) { +#endif + ret = v4l2_subdev_call(cam->sd, video, s_stream, 1); + if (ret < 0) { + dev_err(ctrl->dev, "%s: s_stream failed\n", + __func__); + if (cam->id == CAMERA_CSI_C) + s3c_csis_stop(CSI_CH_0); + else + s3c_csis_stop(CSI_CH_1); + + return ret; + } + } + } + } + /* Set FIMD to write back */ + if ((cam->id == CAMERA_WB) || (cam->id == CAMERA_WB_B)) { + if (cam->id == CAMERA_WB) + fimc_hwset_sysreg_camblk_fimd0_wb(ctrl); + else + fimc_hwset_sysreg_camblk_fimd1_wb(ctrl); + + ret = s3cfb_direct_ioctl(0, S3CFB_SET_WRITEBACK, 1); + if (ret) { + fimc_err("failed set writeback\n"); + return ret; + } + + } + + if (ctrl->is.sd && fimc_cam_use) { + struct platform_device *pdev = to_platform_device(ctrl->dev); + struct clk *pxl_async = NULL; + is_ctrl.id = V4L2_CID_IS_GET_SENSOR_OFFSET_X; + is_ctrl.value = 0; + v4l2_subdev_call(ctrl->is.sd, core, g_ctrl, &is_ctrl); + ctrl->is.offset_x = is_ctrl.value; + is_ctrl.id = V4L2_CID_IS_GET_SENSOR_OFFSET_Y; + is_ctrl.value = 0; + v4l2_subdev_call(ctrl->is.sd, core, g_ctrl, &is_ctrl); + ctrl->is.offset_y = is_ctrl.value; + fimc_dbg("CSI setting width = %d, height = %d\n", + ctrl->is.fmt.width + ctrl->is.offset_x, + ctrl->is.fmt.height + ctrl->is.offset_y); + + if (ctrl->flite_sd && fimc_cam_use) { + ctrl->is.mbus_fmt.width += ctrl->is.offset_x; + ctrl->is.mbus_fmt.height += ctrl->is.offset_y; + ret = v4l2_subdev_call(ctrl->flite_sd, video, + s_mbus_fmt, &ctrl->is.mbus_fmt); + } + + if (cam->id == CAMERA_CSI_C) + s3c_csis_start(CSI_CH_0, cam->mipi_lanes, + cam->mipi_settle, cam->mipi_align, + ctrl->is.fmt.width + ctrl->is.offset_x, + ctrl->is.fmt.height + ctrl->is.offset_y, + V4L2_PIX_FMT_SGRBG10); + else if (cam->id == CAMERA_CSI_D) + s3c_csis_start(CSI_CH_1, cam->mipi_lanes, + cam->mipi_settle, cam->mipi_align, + ctrl->is.fmt.width + ctrl->is.offset_x, + ctrl->is.fmt.height + ctrl->is.offset_y, + V4L2_PIX_FMT_SGRBG10); + + pxl_async = clk_get(&pdev->dev, "pxl_async1"); + if (IS_ERR(pxl_async)) { + dev_err(&pdev->dev, "failed to get pxl_async\n"); + return -ENODEV; + } + + clk_enable(pxl_async); + clk_put(pxl_async); + fimc_hwset_sysreg_camblk_isp_wb(ctrl); + } + + if (ctrl->flite_sd && fimc_cam_use) + v4l2_subdev_call(ctrl->flite_sd, video, s_stream, 1); + + if (!ctrl->is.sd && cap->movie_mode && + !((cam->width == 880 && cam->height == 720))) { + printk(KERN_INFO "\n\n\n%s pm_qos_req is called..\n", __func__ ); + dev_lock(ctrl->bus_dev, ctrl->dev, (unsigned long)400200); + pm_qos_add_request(&bus_qos_pm_qos_req, PM_QOS_BUS_QOS, 1); + + /* ioremap for register block */ + qos_regs = ioremap(0x11a00400, 0x10); + if (!qos_regs) { + fimc_err("%s: failed to remap io region\n", __func__); + return -1; + } + writel(0x3, qos_regs + 0x0); + writel(0x1, qos_regs + 0x4); + fimc_err("0x11a00400 = 0x%x , 0x11a00404 = 0x%x \n", readl(qos_regs + 0), readl(qos_regs + 4)); + + iounmap(qos_regs); + } + + fimc_hwset_camera_type(ctrl); + fimc_hwset_camera_polarity(ctrl); + fimc_hwset_enable_lastend(ctrl); + + if (cap->fmt.pixelformat != V4L2_PIX_FMT_JPEG && + cap->fmt.pixelformat != V4L2_PIX_FMT_INTERLEAVED) { + fimc_hwset_camera_source(ctrl); + fimc_hwset_camera_offset(ctrl); + + fimc_capture_scaler_info(ctrl); + fimc_hwset_prescaler(ctrl, &ctrl->sc); + fimc_hwset_scaler(ctrl, &ctrl->sc); + fimc_hwset_output_colorspace(ctrl, cap->fmt.pixelformat); + fimc_hwset_output_addr_style(ctrl, cap->fmt.pixelformat); + + if (cap->fmt.pixelformat == V4L2_PIX_FMT_RGB32 || + cap->fmt.pixelformat == V4L2_PIX_FMT_RGB565) + fimc_hwset_output_rgb(ctrl, cap->fmt.pixelformat); + else + fimc_hwset_output_yuv(ctrl, cap->fmt.pixelformat); + + fimc_hwset_output_area(ctrl, cap->fmt.width, cap->fmt.height); + fimc_hwset_output_scan(ctrl, &cap->fmt); + + fimc_hwset_output_rot_flip(ctrl, cap->rotate, cap->flip); + rot = fimc_mapping_rot_flip(cap->rotate, cap->flip); + + if (rot & FIMC_ROT) { + fimc_hwset_org_output_size(ctrl, cap->fmt.width, + cap->fmt.height); + fimc_hwset_output_size(ctrl, cap->fmt.height, + cap->fmt.width); + } else { + fimc_hwset_org_output_size(ctrl, cap->fmt.width, + cap->fmt.height); + fimc_hwset_output_size(ctrl, cap->fmt.width, + cap->fmt.height); + } + + fimc_hwset_jpeg_mode(ctrl, false); + } else { + fimc_hwset_output_size(ctrl, + cap->fmt.width, cap->fmt.height); + if (rot & FIMC_ROT) + fimc_hwset_org_output_size(ctrl, + cap->fmt.height, cap->fmt.width); + else + fimc_hwset_org_output_size(ctrl, + cap->fmt.width, cap->fmt.height); + if (cap->fmt.pixelformat == V4L2_PIX_FMT_JPEG) + fimc_hwset_output_area_size(ctrl, + fimc_camera_get_jpeg_memsize(ctrl)); + else if (cap->fmt.pixelformat == V4L2_PIX_FMT_INTERLEAVED) + fimc_hwset_output_area_size(ctrl, + 0xc00000); + fimc_hwset_jpeg_mode(ctrl, true); + } + + if (pdata->hw_ver >= 0x51) { + for (i = 0; i < cap->nr_bufs; i++) + fimc_hwset_output_address(ctrl, &cap->bufs[i], i); + } else { + for (i = 0; i < FIMC_PINGPONG; i++) + fimc_add_outqueue(ctrl, i); + } + + if (cap->fmt.pixelformat == V4L2_PIX_FMT_JPEG || + cap->fmt.pixelformat == V4L2_PIX_FMT_INTERLEAVED) { + fimc_hwset_scaler_bypass(ctrl); + } + + ctrl->cap->cnt = 0; + fimc_start_capture(ctrl); + ctrl->status = FIMC_STREAMON; + + if (ctrl->is.sd && fimc_cam_use) + ret = v4l2_subdev_call(ctrl->is.sd, video, s_stream, 1); + printk(KERN_INFO "%s-- fimc%d\n", __func__, ctrl->id); + + /* if available buffer did not remained */ + return 0; +} + +int fimc_streamoff_capture(void *fh) +{ + struct fimc_control *ctrl = fh; + struct fimc_capinfo *cap = ctrl->cap; + + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + int ret = 0; + void __iomem *qos_regs; + + printk(KERN_INFO "%s++ fimc%d\n", __func__, ctrl->id); + + if (ctrl->status == FIMC_STREAMOFF) { + fimc_err("%s: fimc%d already stopped.\n", __func__, ctrl->id); + return -ENODEV; + } + + if (fimc_check_capture_source(ctrl)) { + fimc_err("%s: No capture device.\n", __func__); + return -ENODEV; + } + + ctrl->status = FIMC_READY_OFF; + + fimc_stop_capture(ctrl); +#ifdef CONFIG_VIDEO_IMPROVE_STREAMOFF + if ((get_fimc_dev()->active_camera == 0) && + fimc_cam_use && ctrl->cam->sd) + v4l2_subdev_call(ctrl->cam->sd, video, s_stream, 0); +#endif + + /* wait for stop hardware */ + fimc_wait_disable_capture(ctrl); + + fimc_hwset_disable_irq(ctrl); + if (pdata->hw_ver < 0x51) + INIT_LIST_HEAD(&cap->inq); + + ctrl->status = FIMC_STREAMOFF; + if (fimc_cam_use) { + if (ctrl->is.sd) + v4l2_subdev_call(ctrl->is.sd, video, s_stream, 0); + + if (ctrl->flite_sd) + v4l2_subdev_call(ctrl->flite_sd, video, s_stream, 0); + + if (ctrl->cam->type == CAM_TYPE_MIPI) { + if (ctrl->cam->id == CAMERA_CSI_C) + s3c_csis_stop(CSI_CH_0); + else + s3c_csis_stop(CSI_CH_1); + } + fimc_hwset_reset(ctrl); + +#ifdef CONFIG_VIDEO_IMPROVE_STREAMOFF + if (ctrl->cam->sd && (get_fimc_dev()->active_camera != 0)) +#else + if (ctrl->cam->sd) +#endif + v4l2_subdev_call(ctrl->cam->sd, video, s_stream, 0); + } else { + fimc_hwset_reset(ctrl); + } + + if (!ctrl->is.sd && cap->movie_mode && + !(ctrl->cam->width == 880 && ctrl->cam->height == 720)) { + printk(KERN_INFO "\n\n\n%s pm_qos_req is removed..\n", __func__ ); + pm_qos_remove_request(&bus_qos_pm_qos_req); + dev_unlock(ctrl->bus_dev, ctrl->dev); + + /* ioremap for register block */ + qos_regs = ioremap(0x11a00400, 0x10); + if (!qos_regs) { + fimc_err("%s: failed to remap io region\n", __func__); + return -1; + } + writel(0x0, qos_regs + 0x0); + writel(0x0, qos_regs + 0x4); + fimc_err("0x11a00400 = 0x%x , 0x11a00404 = 0x%x \n", readl(qos_regs + 0), readl(qos_regs + 4)); + + iounmap(qos_regs); + } + + /* Set FIMD to write back */ + if ((ctrl->cam->id == CAMERA_WB) || (ctrl->cam->id == CAMERA_WB_B)) { + ret = s3cfb_direct_ioctl(0, S3CFB_SET_WRITEBACK, 0); + if (ret) { + fimc_err("failed set writeback\n"); + return ret; + } + } + /* disable camera power */ + /* cam power off should call in the subdev release function */ + if (fimc_cam_use) { + if (ctrl->cam->reset_camera) { + if (ctrl->cam->cam_power) + ctrl->cam->cam_power(0); + if (ctrl->power_status != FIMC_POWER_SUSPEND) + ctrl->cam->initialized = 0; + } + } + printk(KERN_INFO "%s-- fimc%d\n", __func__, ctrl->id); + return 0; +} + +int fimc_is_set_zoom(struct fimc_control *ctrl, struct v4l2_control *c) +{ + struct v4l2_control is_ctrl; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + struct s3c_platform_camera *cam = NULL; + int ret = 0; + + is_ctrl.id = 0; + is_ctrl.value = 0; + + if (ctrl->cam) + cam = ctrl->cam; + else + return -ENODEV; + + /* 0. Check zoom width and height */ + if (!c->value) { + ctrl->is.zoom_in_width = ctrl->is.fmt.width; + ctrl->is.zoom_in_height = ctrl->is.fmt.height; + } else { + ctrl->is.zoom_in_width = ctrl->is.fmt.width - (16 * c->value); + ctrl->is.zoom_in_height = + (ctrl->is.zoom_in_width * ctrl->is.fmt.height) + / ctrl->is.fmt.width; + /* bayer crop contraint */ + switch (ctrl->is.zoom_in_height%4) { + case 1: + ctrl->is.zoom_in_height--; + break; + case 2: + ctrl->is.zoom_in_height += 2; + break; + case 3: + ctrl->is.zoom_in_height++; + break; + } + if ((ctrl->is.zoom_in_width < (ctrl->is.fmt.width/4)) + || (ctrl->is.zoom_in_height < (ctrl->is.fmt.height/4))) { + ctrl->is.zoom_in_width = ctrl->is.fmt.width/4; + ctrl->is.zoom_in_height = ctrl->is.fmt.height/4; + } + } + /* 1. fimc stop */ + fimc_stop_zoom_capture(ctrl); + /* 2. Set zoom and calculate new width and height */ + if (ctrl->is.sd && fimc_cam_use) { + ret = v4l2_subdev_call(ctrl->is.sd, core, s_ctrl, c); + /* 2. Set zoom */ + is_ctrl.id = V4L2_CID_IS_ZOOM_STATE; + is_ctrl.value = 0; + while (!is_ctrl.value) { + v4l2_subdev_call(ctrl->is.sd, core, g_ctrl, &is_ctrl); + fimc_dbg("V4L2_CID_IS_ZOOM_STATE - %d", is_ctrl.value); + } + } + /* 2. Change soruce size of FIMC */ + fimc_hwset_camera_change_source(ctrl); + fimc_capture_change_scaler_info(ctrl); + fimc_hwset_prescaler(ctrl, &ctrl->sc); + fimc_hwset_scaler(ctrl, &ctrl->sc); + /* 4. Start FIMC */ + fimc_start_zoom_capture(ctrl); + /* 5. FIMC-IS stream on */ + if (ctrl->is.sd && fimc_cam_use) + ret = v4l2_subdev_call(ctrl->is.sd, video, s_stream, 1); + + return 0; +} + +static void fimc_buf2bs(struct fimc_buf_set *bs, struct fimc_buf *buf) +{ + bs->base[FIMC_ADDR_Y] = buf->base[FIMC_ADDR_Y]; + bs->length[FIMC_ADDR_Y] = buf->length[FIMC_ADDR_Y]; + + bs->base[FIMC_ADDR_CB] = buf->base[FIMC_ADDR_CB]; + bs->length[FIMC_ADDR_CB] = buf->length[FIMC_ADDR_CB]; + + bs->base[FIMC_ADDR_CR] = buf->base[FIMC_ADDR_CR]; + bs->length[FIMC_ADDR_CR] = buf->length[FIMC_ADDR_CR]; +} + +int fimc_qbuf_capture(void *fh, struct v4l2_buffer *b) +{ + struct fimc_control *ctrl = fh; + struct fimc_buf *buf = (struct fimc_buf *)b->m.userptr; + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + struct fimc_capinfo *cap = ctrl->cap; + int idx = b->index; + int framecnt_seq; + int available_bufnum; + size_t length = 0; + int i; + + if (!cap || !ctrl->cam) { + fimc_err("%s: No capture device.\n", __func__); + return -ENODEV; + } + + mutex_lock(&ctrl->v4l2_lock); + if (pdata->hw_ver >= 0x51) { + if (cap->bufs[idx].state != VIDEOBUF_IDLE) { + fimc_err("%s: invalid state idx : %d\n", __func__, idx); + mutex_unlock(&ctrl->v4l2_lock); + return -EINVAL; + } else { + if (b->memory == V4L2_MEMORY_USERPTR) { + fimc_buf2bs(&cap->bufs[idx], buf); + fimc_hwset_output_address(ctrl, &cap->bufs[idx], idx); + } + + fimc_hwset_output_buf_sequence(ctrl, idx, FIMC_FRAMECNT_SEQ_ENABLE); + cap->bufs[idx].state = VIDEOBUF_QUEUED; + if (ctrl->status == FIMC_BUFFER_STOP) { + framecnt_seq = fimc_hwget_output_buf_sequence(ctrl); + available_bufnum = + fimc_hwget_number_of_bits(framecnt_seq); + if (available_bufnum >= 2) { + printk(KERN_INFO "fimc_qbuf_capture start again\n"); + cap->cnt = 0; + fimc_start_capture(ctrl); + ctrl->status = FIMC_STREAMON; + ctrl->restart = true; + } + } + } + } else { + fimc_add_inqueue(ctrl, b->index); + } + + mutex_unlock(&ctrl->v4l2_lock); + + if (!cap->cacheable) + return 0; + + for (i = 0; i < 3; i++) { + if (cap->bufs[b->index].base[i]) + length += cap->bufs[b->index].length[i]; + else + break; + } + + if (length > (unsigned long) L2_FLUSH_ALL) { + flush_cache_all(); /* L1 */ + smp_call_function((smp_call_func_t)__cpuc_flush_kern_all, NULL, 1); + outer_flush_all(); /* L2 */ + } else if (length > (unsigned long) L1_FLUSH_ALL) { + flush_cache_all(); /* L1 */ + smp_call_function((smp_call_func_t)__cpuc_flush_kern_all, NULL, 1); + + for (i = 0; i < 3; i++) { + phys_addr_t start = cap->bufs[b->index].base[i]; + phys_addr_t end = cap->bufs[b->index].base[i] + + cap->bufs[b->index].length[i] - 1; + + if (!start) + break; + + outer_flush_range(start, end); /* L2 */ + } + } else { + for (i = 0; i < 3; i++) { + phys_addr_t start = cap->bufs[b->index].base[i]; + phys_addr_t end = cap->bufs[b->index].base[i] + + cap->bufs[b->index].length[i] - 1; + + if (!start) + break; + + dmac_flush_range(phys_to_virt(start), phys_to_virt(end)); + outer_flush_range(start, end); /* L2 */ + } + } + + return 0; +} + +static void fimc_bs2buf(struct fimc_buf *buf, struct fimc_buf_set *bs) +{ + buf->base[FIMC_ADDR_Y] = bs->base[FIMC_ADDR_Y]; + buf->length[FIMC_ADDR_Y] = bs->length[FIMC_ADDR_Y]; + + buf->base[FIMC_ADDR_CB] = bs->base[FIMC_ADDR_CB]; + buf->length[FIMC_ADDR_CB] = bs->length[FIMC_ADDR_CB]; + + buf->base[FIMC_ADDR_CR] = bs->base[FIMC_ADDR_CR]; + buf->length[FIMC_ADDR_CR] = bs->length[FIMC_ADDR_CR]; +} + +int fimc_dqbuf_capture(void *fh, struct v4l2_buffer *b) +{ + unsigned long spin_flags; + struct fimc_control *ctrl = fh; + struct fimc_capinfo *cap = ctrl->cap; + struct fimc_buf_set *bs; + struct fimc_buf *buf = (struct fimc_buf *)b->m.userptr; + size_t length = 0; + int i, pp, ret = 0; + phys_addr_t start, end; + + struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); + + if (!cap || !ctrl->cam) { + fimc_err("%s: No capture device.\n", __func__); + return -ENODEV; + } + + if (pdata->hw_ver >= 0x51) { + spin_lock_irqsave(&ctrl->outq_lock, spin_flags); + + if (list_empty(&cap->outgoing_q)) { + fimc_info2("%s: outgoing_q is empty\n", __func__); + spin_unlock_irqrestore(&ctrl->outq_lock, spin_flags); + return -EAGAIN; + } else { + bs = list_first_entry(&cap->outgoing_q, struct fimc_buf_set, + list); + fimc_info2("%s[%d]: bs->id : %d\n", __func__, ctrl->id, bs->id); + b->index = bs->id; + bs->state = VIDEOBUF_IDLE; + + if (b->memory == V4L2_MEMORY_USERPTR) + fimc_bs2buf(buf, bs); + + list_del(&bs->list); + } + + spin_unlock_irqrestore(&ctrl->outq_lock, spin_flags); + } else { + pp = ((fimc_hwget_frame_count(ctrl) + 2) % 4); + if (cap->fmt.field == V4L2_FIELD_INTERLACED_TB) + pp &= ~0x1; + b->index = cap->outq[pp]; + fimc_info2("%s: buffer(%d) outq[%d]\n", __func__, b->index, pp); + ret = fimc_add_outqueue(ctrl, pp); + if (ret) { + b->index = -1; + fimc_err("%s: no inqueue buffer\n", __func__); + } + } + + if (cap->pktdata_enable) { + flush_cache_all(); /* L1 */ + smp_call_function((smp_call_func_t)__cpuc_flush_kern_all, NULL, 1); + + start = cap->bufs[b->index].base[cap->pktdata_plane]; + end = cap->bufs[b->index].base[cap->pktdata_plane] + + cap->bufs[b->index].length[cap->pktdata_plane] - 1; + fimc_info2("fimc_dqbuf interleaved mode cache flush... start 0x%x, size 0x%x\n", + start, cap->bufs[b->index].length[cap->pktdata_plane] ); + + outer_flush_range(start, end); /* L2 */ + } + + if (!cap->cacheable) + return ret; + + for (i = 0; i < 3; i++) { + if (cap->bufs[b->index].base[i]) + length += cap->bufs[b->index].length[i]; + else + break; + } + + if (length > (unsigned long) L2_FLUSH_ALL) { + flush_cache_all(); /* L1 */ + smp_call_function((smp_call_func_t)__cpuc_flush_kern_all, NULL, 1); + outer_flush_all(); /* L2 */ + } else if (length > (unsigned long) L1_FLUSH_ALL) { + flush_cache_all(); /* L1 */ + smp_call_function((smp_call_func_t)__cpuc_flush_kern_all, NULL, 1); + + for (i = 0; i < 3; i++) { + phys_addr_t start = cap->bufs[b->index].base[i]; + phys_addr_t end = cap->bufs[b->index].base[i] + + cap->bufs[b->index].length[i] - 1; + + if (!start) + break; + + outer_flush_range(start, end); /* L2 */ + } + } else { + for (i = 0; i < 3; i++) { + phys_addr_t start = cap->bufs[b->index].base[i]; + phys_addr_t end = cap->bufs[b->index].base[i] + + cap->bufs[b->index].length[i] - 1; + + if (!start) + break; + + dmac_flush_range(phys_to_virt(start), phys_to_virt(end)); + outer_flush_range(start, end); /* L2 */ + } + } + + return ret; +} + +int fimc_enum_framesizes(struct file *filp, void *fh, struct v4l2_frmsizeenum *fsize) +{ + struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; + int i; + u32 index = 0; + for (i = 0; i < ARRAY_SIZE(capture_fmts); i++) { + if (fsize->pixel_format != capture_fmts[i].pixelformat) + continue; + if (fsize->index == index) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + /* this is camera sensor's width, height. + * originally this should be filled each file format + */ + fsize->discrete.width = ctrl->cam->width; + fsize->discrete.height = ctrl->cam->height; + + return 0; + } + index++; + } + + return -EINVAL; +} +int fimc_enum_frameintervals(struct file *filp, void *fh, + struct v4l2_frmivalenum *fival) +{ + if (fival->index > 0) + return -EINVAL; + /* temporary only support 30fps */ + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1000; + fival->discrete.denominator = 30000; + + return 0; +} + +/* + * only used at mipi power func. + */ +struct device *fimc_get_active_device(void) +{ + struct fimc_global *fimc = get_fimc_dev(); + struct fimc_control *ctrl; + + if (!fimc || (fimc->active_camera < 0)) + return NULL; + + ctrl = get_fimc_ctrl(fimc->active_camera); + + return ctrl->dev; +} |