1
0
Fork 0

MLK-11508-4: V4L2 Capture: Porting MXC V4L2 Capture from 3.14.y

Initial port of the mxc V4L2 capture driver.
Baseline copied from imx_3.14.y branch:

Signed-off-by: Sandor Yu <R01008@freescale.com>

For 4.14 updated to fwnode interface because v4l-of is gone.

Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
pull/10/head
Sandor Yu 2015-08-31 18:15:43 +08:00 committed by Jason Liu
parent a2795de8bb
commit 89def645b3
38 changed files with 27928 additions and 3 deletions

View File

@ -221,9 +221,19 @@ CONFIG_MEDIA_USB_SUPPORT=y
CONFIG_USB_VIDEO_CLASS=m
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_VIDEO_MXC_OUTPUT=y
CONFIG_SOC_CAMERA=y
CONFIG_VIDEO_MXC_CAPTURE=m
CONFIG_MXC_CAMERA_OV5640=m
CONFIG_MXC_CAMERA_OV5642=m
CONFIG_MXC_CAMERA_OV5640_MIPI=m
CONFIG_MXC_TVIN_ADV7180=m
CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m
CONFIG_VIDEO_MXC_IPU_OUTPUT=y
CONFIG_VIDEO_MXC_PXP_V4L2=y
CONFIG_VIDEO_MXC_CSI_CAMERA=m
CONFIG_MXC_VADC=m
CONFIG_MXC_MIPI_CSI=m
CONFIG_MXC_CAMERA_OV5647_MIPI=m
CONFIG_SOC_CAMERA=y
CONFIG_V4L_MEM2MEM_DRIVERS=y
CONFIG_VIDEO_CODA=y
CONFIG_RADIO_SI476X=y
@ -304,6 +314,8 @@ CONFIG_MXC_IPU=y
CONFIG_MXC_MLB150=m
CONFIG_MXC_IPU_V3_PRE=y
CONFIG_MXC_SIM=y
CONFIG_MXC_MIPI_CSI2=y
CONFIG_MXC_HDMI_CEC=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y

View File

@ -151,6 +151,15 @@ config VIDEO_MXC_OUTPUT
---help---
This is the video4linux2 output driver based on MXC module.
config VIDEO_MXC_CAPTURE
tristate "MXC Video For Linux Video Capture"
depends on VIDEO_V4L2
---help---
This is the video4linux2 capture driver based on i.MX video-in module.
source "drivers/media/platform/mxc/capture/Kconfig"
source "drivers/media/platform/mxc/output/Kconfig"
source "drivers/media/platform/mxc/subdev/Kconfig"
source "drivers/media/platform/soc_camera/Kconfig"
source "drivers/media/platform/exynos4-is/Kconfig"
source "drivers/media/platform/am437x/Kconfig"
@ -172,7 +181,6 @@ config VIDEO_TI_CAL
In TI Technical Reference Manual this module is referred as
Camera Interface Subsystem (CAMSS).
source "drivers/media/platform/mxc/output/Kconfig"
endif # V4L_PLATFORM_DRIVERS

View File

@ -78,6 +78,10 @@ obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc/output/
obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/capture/
obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/subdev/
obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc/output/
ccflags-y += -I$(srctree)/drivers/media/i2c
obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/

View File

@ -0,0 +1,83 @@
if VIDEO_MXC_CAPTURE
config VIDEO_V4L2_MXC_INT_DEVICE
tristate
menu "MXC Camera/V4L2 PRP Features support"
config VIDEO_MXC_IPU_CAMERA
bool
select VIDEO_V4L2_MXC_INT_DEVICE
depends on VIDEO_MXC_CAPTURE && MXC_IPU
default y
config MXC_CAMERA_OV5640
tristate "OmniVision ov5640 camera support"
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
---help---
If you plan to use the ov5640 Camera with your MXC system, say Y here.
config MXC_CAMERA_OV5642
tristate "OmniVision ov5642 camera support"
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
---help---
If you plan to use the ov5642 Camera with your MXC system, say Y here.
config MXC_CAMERA_OV5640_MIPI
tristate "OmniVision ov5640 camera support using mipi"
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
---help---
If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.
config MXC_TVIN_ADV7180
tristate "Analog Device adv7180 TV Decoder Input support"
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
---help---
If you plan to use the adv7180 video decoder with your MXC system, say Y here.
choice
prompt "Select Overlay Rounting"
default MXC_IPU_DEVICE_QUEUE_SDC
depends on VIDEO_MXC_IPU_CAMERA && FB_MXC_SYNC_PANEL
config MXC_IPU_DEVICE_QUEUE_SDC
tristate "Queue ipu device for overlay library"
depends on VIDEO_MXC_IPU_CAMERA
---help---
Use case CSI->MEM->IPU DEVICE->SDC:
Images from sensor will be frist recieved in memory,then
queue to ipu device for processing if needed, and displaying
it on synchronous display with SDC use case.
config MXC_IPU_PRP_VF_SDC
bool "Pre-Processor VF SDC library"
depends on VIDEO_MXC_IPU_CAMERA
---help---
Use case PRP_VF_SDC:
Preprocessing image from smart sensor for viewfinder and
displaying it on synchronous display with SDC use case.
If SDC BG is selected, Rotation will not be supported.
CSI -> IC (PRP VF) -> MEM
MEM -> IC (ROT) -> MEM
MEM -> SDC (FG/BG)
endchoice
config MXC_IPU_PRP_ENC
tristate "Pre-processor Encoder library"
depends on VIDEO_MXC_IPU_CAMERA
default y
---help---
Use case PRP_ENC:
Preprocessing image from smart sensor for encoder.
CSI -> IC (PRP ENC) -> MEM
config MXC_IPU_CSI_ENC
tristate "IPU CSI Encoder library"
depends on VIDEO_MXC_IPU_CAMERA
default y
---help---
Use case IPU_CSI_ENC:
Get raw image with CSI from smart sensor for encoder.
CSI -> MEM
endmenu
endif

View File

@ -0,0 +1,21 @@
ifeq ($(CONFIG_VIDEO_MXC_IPU_CAMERA),y)
obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc_v4l2_capture.o
obj-$(CONFIG_MXC_IPU_PRP_VF_SDC) += ipu_prp_vf_sdc.o ipu_prp_vf_sdc_bg.o
obj-$(CONFIG_MXC_IPU_DEVICE_QUEUE_SDC) += ipu_fg_overlay_sdc.o ipu_bg_overlay_sdc.o
obj-$(CONFIG_MXC_IPU_PRP_ENC) += ipu_prp_enc.o ipu_still.o
obj-$(CONFIG_MXC_IPU_CSI_ENC) += ipu_csi_enc.o ipu_still.o
endif
ov5640_camera_int-objs := ov5640.o
obj-$(CONFIG_MXC_CAMERA_OV5640) += ov5640_camera_int.o
ov5642_camera-objs := ov5642.o
obj-$(CONFIG_MXC_CAMERA_OV5642) += ov5642_camera.o
ov5640_camera_mipi_int-objs := ov5640_mipi.o
obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi_int.o
adv7180_tvin-objs := adv7180.o
obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o
obj-$(CONFIG_VIDEO_V4L2_MXC_INT_DEVICE) += v4l2-int-device.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,552 @@
/*
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_bg_overlay_sdc_bg.c
*
* @brief IPU Use case for PRP-VF back-ground
*
* @ingroup IPU
*/
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/fb.h>
#include <linux/ipu.h>
#include <linux/mipi_csi2.h>
#include "mxc_v4l2_capture.h"
#include "ipu_prp_sw.h"
static int csi_buffer_num;
static u32 bpp, csi_mem_bufsize = 3;
static u32 out_format;
static struct ipu_soc *disp_ipu;
static u32 offset;
static void csi_buf_work_func(struct work_struct *work)
{
int err = 0;
cam_data *cam =
container_of(work, struct _cam_data, csi_work_struct);
struct ipu_task task;
memset(&task, 0, sizeof(task));
if (csi_buffer_num)
task.input.paddr = cam->vf_bufs[0];
else
task.input.paddr = cam->vf_bufs[1];
task.input.width = cam->crop_current.width;
task.input.height = cam->crop_current.height;
task.input.format = IPU_PIX_FMT_UYVY;
task.output.paddr = offset;
task.output.width = cam->overlay_fb->var.xres;
task.output.height = cam->overlay_fb->var.yres;
task.output.format = out_format;
task.output.rotate = cam->rotation;
task.output.crop.pos.x = cam->win.w.left;
task.output.crop.pos.y = cam->win.w.top;
if (cam->win.w.width > 1024 || cam->win.w.height > 1024) {
task.output.crop.w = cam->overlay_fb->var.xres;
task.output.crop.h = cam->overlay_fb->var.yres;
} else {
task.output.crop.w = cam->win.w.width;
task.output.crop.h = cam->win.w.height;
}
again:
err = ipu_check_task(&task);
if (err != IPU_CHECK_OK) {
if (err > IPU_CHECK_ERR_MIN) {
if (err == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) {
task.input.crop.w -= 8;
goto again;
}
if (err == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) {
task.input.crop.h -= 8;
goto again;
}
if (err == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) {
task.output.width -= 8;
task.output.crop.w = task.output.width;
goto again;
}
if (err == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) {
task.output.height -= 8;
task.output.crop.h = task.output.height;
goto again;
}
printk(KERN_ERR "check ipu taks fail\n");
return;
}
printk(KERN_ERR "check ipu taks fail\n");
return;
}
err = ipu_queue_task(&task);
if (err < 0)
printk(KERN_ERR "queue ipu task error\n");
}
static void get_disp_ipu(cam_data *cam)
{
if (cam->output > 2)
disp_ipu = ipu_get_soc(1); /* using DISP4 */
else
disp_ipu = ipu_get_soc(0);
}
/*!
* csi ENC callback function.
*
* @param irq int irq line
* @param dev_id void * device id
*
* @return status IRQ_HANDLED for handled
*/
static irqreturn_t csi_enc_callback(int irq, void *dev_id)
{
cam_data *cam = (cam_data *) dev_id;
ipu_channel_t chan = (irq == IPU_IRQ_CSI0_OUT_EOF) ?
CSI_MEM0 : CSI_MEM1;
ipu_select_buffer(cam->ipu, chan,
IPU_OUTPUT_BUFFER, csi_buffer_num);
schedule_work(&cam->csi_work_struct);
csi_buffer_num = (csi_buffer_num == 0) ? 1 : 0;
return IRQ_HANDLED;
}
static int csi_enc_setup(cam_data *cam)
{
ipu_channel_params_t params;
u32 pixel_fmt;
int err = 0, sensor_protocol = 0;
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
if (!cam) {
printk(KERN_ERR "cam private is NULL\n");
return -ENXIO;
}
memset(&params, 0, sizeof(ipu_channel_params_t));
params.csi_mem.csi = cam->csi;
sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi);
switch (sensor_protocol) {
case IPU_CSI_CLK_MODE_GATED_CLK:
case IPU_CSI_CLK_MODE_NONGATED_CLK:
case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
params.csi_mem.interlaced = false;
break;
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
params.csi_mem.interlaced = true;
break;
default:
printk(KERN_ERR "sensor protocol unsupported\n");
return -EINVAL;
}
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id) {
params.csi_mem.mipi_en = true;
params.csi_mem.mipi_vc =
mipi_csi2_get_virtual_channel(mipi_csi2_info);
params.csi_mem.mipi_id =
mipi_csi2_get_datatype(mipi_csi2_info);
mipi_csi2_pixelclk_enable(mipi_csi2_info);
} else {
params.csi_mem.mipi_en = false;
params.csi_mem.mipi_vc = 0;
params.csi_mem.mipi_id = 0;
}
} else {
params.csi_mem.mipi_en = false;
params.csi_mem.mipi_vc = 0;
params.csi_mem.mipi_id = 0;
}
}
#endif
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0],
(dma_addr_t) cam->vf_bufs[0]);
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1],
(dma_addr_t) cam->vf_bufs[1]);
}
csi_mem_bufsize =
cam->crop_current.width * cam->crop_current.height * 2;
cam->vf_bufs_size[0] = PAGE_ALIGN(csi_mem_bufsize);
cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
cam->vf_bufs_size[0],
(dma_addr_t *) &
cam->vf_bufs[0],
GFP_DMA |
GFP_KERNEL);
if (cam->vf_bufs_vaddr[0] == NULL) {
printk(KERN_ERR "Error to allocate vf buffer\n");
err = -ENOMEM;
goto out_2;
}
cam->vf_bufs_size[1] = PAGE_ALIGN(csi_mem_bufsize);
cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
cam->vf_bufs_size[1],
(dma_addr_t *) &
cam->vf_bufs[1],
GFP_DMA |
GFP_KERNEL);
if (cam->vf_bufs_vaddr[1] == NULL) {
printk(KERN_ERR "Error to allocate vf buffer\n");
err = -ENOMEM;
goto out_1;
}
pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
err = ipu_init_channel(cam->ipu, chan, &params);
if (err != 0) {
printk(KERN_ERR "ipu_init_channel %d\n", err);
goto out_1;
}
pixel_fmt = IPU_PIX_FMT_UYVY;
err = ipu_init_channel_buffer(
cam->ipu, chan, IPU_OUTPUT_BUFFER, pixel_fmt,
cam->crop_current.width, cam->crop_current.height,
cam->crop_current.width, IPU_ROTATE_NONE,
cam->vf_bufs[0], cam->vf_bufs[1], 0,
cam->offset.u_offset, cam->offset.u_offset);
if (err != 0) {
printk(KERN_ERR "CSI_MEM output buffer\n");
goto out_1;
}
err = ipu_enable_channel(cam->ipu, chan);
if (err < 0) {
printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
goto out_1;
}
csi_buffer_num = 0;
ipu_select_buffer(cam->ipu, chan, IPU_OUTPUT_BUFFER, 0);
ipu_select_buffer(cam->ipu, chan, IPU_OUTPUT_BUFFER, 1);
return err;
out_1:
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0],
(dma_addr_t) cam->vf_bufs[0]);
cam->vf_bufs_vaddr[0] = NULL;
cam->vf_bufs[0] = 0;
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1],
(dma_addr_t) cam->vf_bufs[1]);
cam->vf_bufs_vaddr[1] = NULL;
cam->vf_bufs[1] = 0;
}
out_2:
return err;
}
/*!
* Enable encoder task
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int csi_enc_enabling_tasks(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi);
err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi,
csi_enc_callback, 0, "Mxc Camera", cam);
if (err != 0) {
printk(KERN_ERR "Error registering CSI_OUT_EOF irq\n");
return err;
}
INIT_WORK(&cam->csi_work_struct, csi_buf_work_func);
err = csi_enc_setup(cam);
if (err != 0) {
printk(KERN_ERR "csi_enc_setup %d\n", err);
goto out1;
}
return err;
out1:
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi, cam);
return err;
}
/*!
* bg_overlay_start - start the overlay task
*
* @param private cam_data * mxc v4l2 main structure
*
*/
static int bg_overlay_start(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
if (!cam) {
printk(KERN_ERR "private is NULL\n");
return -EIO;
}
if (cam->overlay_active == true) {
pr_debug("already start.\n");
return 0;
}
get_disp_ipu(cam);
out_format = cam->v4l2_fb.fmt.pixelformat;
if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) {
bpp = 3, csi_mem_bufsize = 3;
pr_info("BGR24\n");
} else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) {
bpp = 2, csi_mem_bufsize = 2;
pr_info("RGB565\n");
} else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) {
bpp = 4, csi_mem_bufsize = 4;
pr_info("BGR32\n");
} else {
printk(KERN_ERR
"unsupported fix format from the framebuffer.\n");
return -EINVAL;
}
offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top +
csi_mem_bufsize * cam->win.w.left;
if (cam->v4l2_fb.base == 0)
printk(KERN_ERR "invalid frame buffer address.\n");
else
offset += (u32) cam->v4l2_fb.base;
csi_mem_bufsize = cam->win.w.width * cam->win.w.height
* csi_mem_bufsize;
err = csi_enc_enabling_tasks(cam);
if (err != 0) {
printk(KERN_ERR "Error csi enc enable fail\n");
return err;
}
cam->overlay_active = true;
return err;
}
/*!
* bg_overlay_stop - stop the overlay task
*
* @param private cam_data * mxc v4l2 main structure
*
*/
static int bg_overlay_stop(void *private)
{
int err = 0;
cam_data *cam = (cam_data *) private;
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
if (cam->overlay_active == false)
return 0;
err = ipu_disable_channel(cam->ipu, chan, true);
ipu_uninit_channel(cam->ipu, chan);
csi_buffer_num = 0;
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id)
mipi_csi2_pixelclk_disable(mipi_csi2_info);
}
}
#endif
flush_work(&cam->csi_work_struct);
cancel_work_sync(&cam->csi_work_struct);
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
cam->vf_bufs_vaddr[0] = NULL;
cam->vf_bufs[0] = 0;
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
cam->vf_bufs_vaddr[1] = NULL;
cam->vf_bufs[1] = 0;
}
if (cam->rot_vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->rot_vf_buf_size[0],
cam->rot_vf_bufs_vaddr[0],
cam->rot_vf_bufs[0]);
cam->rot_vf_bufs_vaddr[0] = NULL;
cam->rot_vf_bufs[0] = 0;
}
if (cam->rot_vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->rot_vf_buf_size[1],
cam->rot_vf_bufs_vaddr[1],
cam->rot_vf_bufs[1]);
cam->rot_vf_bufs_vaddr[1] = NULL;
cam->rot_vf_bufs[1] = 0;
}
cam->overlay_active = false;
return err;
}
/*!
* Enable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int bg_overlay_enable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
return ipu_enable_csi(cam->ipu, cam->csi);
}
/*!
* Disable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int bg_overlay_disable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
/* free csi eof irq firstly.
* when disable csi, wait for idmac eof.
* it requests eof irq again */
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi, cam);
return ipu_disable_csi(cam->ipu, cam->csi);
}
/*!
* function to select bg as the working path
*
* @param private cam_data * mxc v4l2 main structure
*
* @return status
*/
int bg_overlay_sdc_select(void *private)
{
cam_data *cam = (cam_data *) private;
if (cam) {
cam->vf_start_sdc = bg_overlay_start;
cam->vf_stop_sdc = bg_overlay_stop;
cam->vf_enable_csi = bg_overlay_enable_csi;
cam->vf_disable_csi = bg_overlay_disable_csi;
cam->overlay_active = false;
}
return 0;
}
EXPORT_SYMBOL(bg_overlay_sdc_select);
/*!
* function to de-select bg as the working path
*
* @param private cam_data * mxc v4l2 main structure
*
* @return status
*/
int bg_overlay_sdc_deselect(void *private)
{
cam_data *cam = (cam_data *) private;
if (cam) {
cam->vf_start_sdc = NULL;
cam->vf_stop_sdc = NULL;
cam->vf_enable_csi = NULL;
cam->vf_disable_csi = NULL;
}
return 0;
}
EXPORT_SYMBOL(bg_overlay_sdc_deselect);
/*!
* Init background overlay task.
*
* @return Error code indicating success or failure
*/
__init int bg_overlay_sdc_init(void)
{
return 0;
}
/*!
* Deinit background overlay task.
*
* @return Error code indicating success or failure
*/
void __exit bg_overlay_sdc_exit(void)
{
}
module_init(bg_overlay_sdc_init);
module_exit(bg_overlay_sdc_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,430 @@
/*
* Copyright 2009-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_csi_enc.c
*
* @brief CSI Use case for video capture
*
* @ingroup IPU
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/ipu.h>
#include <linux/mipi_csi2.h>
#include "mxc_v4l2_capture.h"
#include "ipu_prp_sw.h"
#ifdef CAMERA_DBG
#define CAMERA_TRACE(x) (printk)x
#else
#define CAMERA_TRACE(x)
#endif
/*
* Function definitions
*/
/*!
* csi ENC callback function.
*
* @param irq int irq line
* @param dev_id void * device id
*
* @return status IRQ_HANDLED for handled
*/
static irqreturn_t csi_enc_callback(int irq, void *dev_id)
{
cam_data *cam = (cam_data *) dev_id;
if (cam->enc_callback == NULL)
return IRQ_HANDLED;
cam->enc_callback(irq, dev_id);
return IRQ_HANDLED;
}
/*!
* CSI ENC enable channel setup function
*
* @param cam struct cam_data * mxc capture instance
*
* @return status
*/
static int csi_enc_setup(cam_data *cam)
{
ipu_channel_params_t params;
u32 pixel_fmt;
int err = 0, sensor_protocol = 0;
dma_addr_t dummy = cam->dummy_frame.buffer.m.offset;
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
CAMERA_TRACE("In csi_enc_setup\n");
if (!cam) {
printk(KERN_ERR "cam private is NULL\n");
return -ENXIO;
}
memset(&params, 0, sizeof(ipu_channel_params_t));
params.csi_mem.csi = cam->csi;
sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi);
switch (sensor_protocol) {
case IPU_CSI_CLK_MODE_GATED_CLK:
case IPU_CSI_CLK_MODE_NONGATED_CLK:
case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
params.csi_mem.interlaced = false;
break;
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
params.csi_mem.interlaced = true;
break;
default:
printk(KERN_ERR "sensor protocol unsupported\n");
return -EINVAL;
}
if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
pixel_fmt = IPU_PIX_FMT_YUV420P;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420)
pixel_fmt = IPU_PIX_FMT_YVU420P;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
pixel_fmt = IPU_PIX_FMT_YUV422P;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
pixel_fmt = IPU_PIX_FMT_UYVY;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pixel_fmt = IPU_PIX_FMT_YUYV;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)
pixel_fmt = IPU_PIX_FMT_NV12;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
pixel_fmt = IPU_PIX_FMT_BGR24;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
pixel_fmt = IPU_PIX_FMT_RGB24;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
pixel_fmt = IPU_PIX_FMT_RGB565;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
pixel_fmt = IPU_PIX_FMT_BGR32;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
pixel_fmt = IPU_PIX_FMT_RGB32;
else {
printk(KERN_ERR "format not supported\n");
return -EINVAL;
}
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id) {
params.csi_mem.mipi_en = true;
params.csi_mem.mipi_vc =
mipi_csi2_get_virtual_channel(mipi_csi2_info);
params.csi_mem.mipi_id =
mipi_csi2_get_datatype(mipi_csi2_info);
mipi_csi2_pixelclk_enable(mipi_csi2_info);
} else {
params.csi_mem.mipi_en = false;
params.csi_mem.mipi_vc = 0;
params.csi_mem.mipi_id = 0;
}
} else {
params.csi_mem.mipi_en = false;
params.csi_mem.mipi_vc = 0;
params.csi_mem.mipi_id = 0;
}
}
#endif
err = ipu_init_channel(cam->ipu, chan, &params);
if (err != 0) {
printk(KERN_ERR "ipu_init_channel %d\n", err);
return err;
}
err = ipu_init_channel_buffer(cam->ipu,
chan,
IPU_OUTPUT_BUFFER,
pixel_fmt, cam->v2f.fmt.pix.width,
cam->v2f.fmt.pix.height,
cam->v2f.fmt.pix.bytesperline,
IPU_ROTATE_NONE,
dummy, dummy, 0,
cam->offset.u_offset,
cam->offset.v_offset);
if (err != 0) {
printk(KERN_ERR "CSI_MEM output buffer\n");
return err;
}
err = ipu_enable_channel(cam->ipu, chan);
if (err < 0) {
printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
return err;
}
return err;
}
/*!
* function to update physical buffer address for encorder IDMA channel
*
* @param *private pointer to the cam_data structure
* @param eba physical buffer address for encorder IDMA channel
*
* @return status
*/
static int csi_enc_eba_update(void *private, dma_addr_t eba)
{
int err = 0;
cam_data *cam = (cam_data *) private;
struct ipu_soc *ipu = cam->ipu;
int *buffer_num = &cam->ping_pong_csi;
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
pr_debug("eba %x\n", eba);
err = ipu_update_channel_buffer(ipu, chan, IPU_OUTPUT_BUFFER,
*buffer_num, eba);
if (err != 0) {
ipu_clear_buffer_ready(ipu, chan, IPU_OUTPUT_BUFFER,
*buffer_num);
err = ipu_update_channel_buffer(ipu, chan,
IPU_OUTPUT_BUFFER, *buffer_num, eba);
if (err != 0) {
pr_err("ERROR: v4l2 capture: fail to update "
"buf%d\n", *buffer_num);
return err;
}
}
ipu_select_buffer(ipu, chan, IPU_OUTPUT_BUFFER, *buffer_num);
*buffer_num = (*buffer_num == 0) ? 1 : 0;
return 0;
}
/*!
* Enable encoder task
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int csi_enc_enabling_tasks(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
uint32_t irq = (cam->csi == 0) ?
IPU_IRQ_CSI0_OUT_EOF : IPU_IRQ_CSI1_OUT_EOF;
CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n");
cam->dummy_frame.vaddress = dma_alloc_coherent(0,
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
&cam->dummy_frame.paddress,
GFP_DMA | GFP_KERNEL);
if (cam->dummy_frame.vaddress == 0) {
pr_err("ERROR: v4l2 capture: Allocate dummy frame "
"failed.\n");
return -ENOBUFS;
}
cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE;
cam->dummy_frame.buffer.length =
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress;
ipu_clear_irq(cam->ipu, irq);
err = ipu_request_irq(
cam->ipu, irq, csi_enc_callback, 0, "Mxc Camera", cam);
if (err != 0) {
printk(KERN_ERR "Error registering rot irq\n");
return err;
}
err = csi_enc_setup(cam);
if (err != 0) {
printk(KERN_ERR "csi_enc_setup %d\n", err);
return err;
}
return err;
}
/*!
* Disable encoder task
* @param private struct cam_data * mxc capture instance
*
* @return int
*/
static int csi_enc_disabling_tasks(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
err = ipu_disable_channel(cam->ipu, chan, true);
ipu_uninit_channel(cam->ipu, chan);
if (cam->dummy_frame.vaddress != 0) {
dma_free_coherent(0, cam->dummy_frame.buffer.length,
cam->dummy_frame.vaddress,
cam->dummy_frame.paddress);
cam->dummy_frame.vaddress = 0;
}
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id)
mipi_csi2_pixelclk_disable(mipi_csi2_info);
}
}
#endif
return err;
}
/*!
* Enable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int csi_enc_enable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
return ipu_enable_csi(cam->ipu, cam->csi);
}
/*!
* Disable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int csi_enc_disable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
uint32_t irq = (cam->csi == 0) ?
IPU_IRQ_CSI0_OUT_EOF : IPU_IRQ_CSI1_OUT_EOF;
/* free csi eof irq firstly.
* when disable csi, wait for idmac eof.
* it requests eof irq again */
ipu_free_irq(cam->ipu, irq, cam);
return ipu_disable_csi(cam->ipu, cam->csi);
}
/*!
* function to select CSI ENC as the working path
*
* @param private struct cam_data * mxc capture instance
*
* @return int
*/
int csi_enc_select(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
if (cam) {
cam->enc_update_eba = csi_enc_eba_update;
cam->enc_enable = csi_enc_enabling_tasks;
cam->enc_disable = csi_enc_disabling_tasks;
cam->enc_enable_csi = csi_enc_enable_csi;
cam->enc_disable_csi = csi_enc_disable_csi;
} else {
err = -EIO;
}
return err;
}
EXPORT_SYMBOL(csi_enc_select);
/*!
* function to de-select CSI ENC as the working path
*
* @param private struct cam_data * mxc capture instance
*
* @return int
*/
int csi_enc_deselect(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
if (cam) {
cam->enc_update_eba = NULL;
cam->enc_enable = NULL;
cam->enc_disable = NULL;
cam->enc_enable_csi = NULL;
cam->enc_disable_csi = NULL;
}
return err;
}
EXPORT_SYMBOL(csi_enc_deselect);
/*!
* Init the Encorder channels
*
* @return Error code indicating success or failure
*/
__init int csi_enc_init(void)
{
return 0;
}
/*!
* Deinit the Encorder channels
*
*/
void __exit csi_enc_exit(void)
{
}
module_init(csi_enc_init);
module_exit(csi_enc_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("CSI ENC Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,638 @@
/*
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/* * The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_foreground_sdc.c
*
* @brief IPU Use case for PRP-VF
*
* @ingroup IPU
*/
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/console.h>
#include <linux/ipu.h>
#include <linux/mxcfb.h>
#include <linux/mipi_csi2.h>
#include "mxc_v4l2_capture.h"
#include "ipu_prp_sw.h"
#ifdef CAMERA_DBG
#define CAMERA_TRACE(x) (printk)x
#else
#define CAMERA_TRACE(x)
#endif
static int csi_buffer_num, buffer_num;
static u32 csi_mem_bufsize;
static struct ipu_soc *disp_ipu;
static struct fb_info *fbi;
static struct fb_var_screeninfo fbvar;
static u32 vf_out_format;
static void csi_buf_work_func(struct work_struct *work)
{
int err = 0;
cam_data *cam =
container_of(work, struct _cam_data, csi_work_struct);
struct ipu_task task;
memset(&task, 0, sizeof(task));
if (csi_buffer_num)
task.input.paddr = cam->vf_bufs[0];
else
task.input.paddr = cam->vf_bufs[1];
task.input.width = cam->crop_current.width;
task.input.height = cam->crop_current.height;
task.input.format = IPU_PIX_FMT_NV12;
if (buffer_num == 0)
task.output.paddr = fbi->fix.smem_start +
(fbi->fix.line_length * fbvar.yres);
else
task.output.paddr = fbi->fix.smem_start;
task.output.width = cam->win.w.width;
task.output.height = cam->win.w.height;
task.output.format = vf_out_format;
task.output.rotate = cam->rotation;
again:
err = ipu_check_task(&task);
if (err != IPU_CHECK_OK) {
if (err > IPU_CHECK_ERR_MIN) {
if (err == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) {
task.input.crop.w -= 8;
goto again;
}
if (err == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) {
task.input.crop.h -= 8;
goto again;
}
if (err == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) {
task.output.width -= 8;
task.output.crop.w = task.output.width;
goto again;
}
if (err == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) {
task.output.height -= 8;
task.output.crop.h = task.output.height;
goto again;
}
printk(KERN_ERR "check ipu taks fail\n");
return;
}
printk(KERN_ERR "check ipu taks fail\n");
return;
}
err = ipu_queue_task(&task);
if (err < 0)
printk(KERN_ERR "queue ipu task error\n");
ipu_select_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, buffer_num);
buffer_num = (buffer_num == 0) ? 1 : 0;
}
static void get_disp_ipu(cam_data *cam)
{
if (cam->output > 2)
disp_ipu = ipu_get_soc(1); /* using DISP4 */
else
disp_ipu = ipu_get_soc(0);
}
/*!
* csi ENC callback function.
*
* @param irq int irq line
* @param dev_id void * device id
*
* @return status IRQ_HANDLED for handled
*/
static irqreturn_t csi_enc_callback(int irq, void *dev_id)
{
cam_data *cam = (cam_data *) dev_id;
ipu_channel_t chan = (irq == IPU_IRQ_CSI0_OUT_EOF) ?
CSI_MEM0 : CSI_MEM1;
ipu_select_buffer(cam->ipu, chan,
IPU_OUTPUT_BUFFER, csi_buffer_num);
if ((cam->crop_current.width != cam->win.w.width) ||
(cam->crop_current.height != cam->win.w.height) ||
(vf_out_format != IPU_PIX_FMT_NV12) ||
(cam->rotation >= IPU_ROTATE_VERT_FLIP))
schedule_work(&cam->csi_work_struct);
csi_buffer_num = (csi_buffer_num == 0) ? 1 : 0;
return IRQ_HANDLED;
}
static int csi_enc_setup(cam_data *cam)
{
ipu_channel_params_t params;
int err = 0, sensor_protocol = 0;
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
CAMERA_TRACE("In csi_enc_setup\n");
if (!cam) {
printk(KERN_ERR "cam private is NULL\n");
return -ENXIO;
}
memset(&params, 0, sizeof(ipu_channel_params_t));
params.csi_mem.csi = cam->csi;
sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi);
switch (sensor_protocol) {
case IPU_CSI_CLK_MODE_GATED_CLK:
case IPU_CSI_CLK_MODE_NONGATED_CLK:
case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
params.csi_mem.interlaced = false;
break;
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
params.csi_mem.interlaced = true;
break;
default:
printk(KERN_ERR "sensor protocol unsupported\n");
return -EINVAL;
}
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id) {
params.csi_mem.mipi_en = true;
params.csi_mem.mipi_vc =
mipi_csi2_get_virtual_channel(mipi_csi2_info);
params.csi_mem.mipi_id =
mipi_csi2_get_datatype(mipi_csi2_info);
mipi_csi2_pixelclk_enable(mipi_csi2_info);
} else {
params.csi_mem.mipi_en = false;
params.csi_mem.mipi_vc = 0;
params.csi_mem.mipi_id = 0;
}
} else {
params.csi_mem.mipi_en = false;
params.csi_mem.mipi_vc = 0;
params.csi_mem.mipi_id = 0;
}
}
#endif
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0],
(dma_addr_t) cam->vf_bufs[0]);
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1],
(dma_addr_t) cam->vf_bufs[1]);
}
csi_mem_bufsize = cam->crop_current.width *
cam->crop_current.height * 3/2;
cam->vf_bufs_size[0] = PAGE_ALIGN(csi_mem_bufsize);
cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
cam->vf_bufs_size[0],
(dma_addr_t *) &
cam->vf_bufs[0],
GFP_DMA |
GFP_KERNEL);
if (cam->vf_bufs_vaddr[0] == NULL) {
printk(KERN_ERR "Error to allocate vf buffer\n");
err = -ENOMEM;
goto out_2;
}
cam->vf_bufs_size[1] = PAGE_ALIGN(csi_mem_bufsize);
cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
cam->vf_bufs_size[1],
(dma_addr_t *) &
cam->vf_bufs[1],
GFP_DMA |
GFP_KERNEL);
if (cam->vf_bufs_vaddr[1] == NULL) {
printk(KERN_ERR "Error to allocate vf buffer\n");
err = -ENOMEM;
goto out_1;
}
pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
err = ipu_init_channel(cam->ipu, chan, &params);
if (err != 0) {
printk(KERN_ERR "ipu_init_channel %d\n", err);
goto out_1;
}
if ((cam->crop_current.width == cam->win.w.width) &&
(cam->crop_current.height == cam->win.w.height) &&
(vf_out_format == IPU_PIX_FMT_NV12) &&
(cam->rotation < IPU_ROTATE_VERT_FLIP)) {
err = ipu_init_channel_buffer(cam->ipu, chan,
IPU_OUTPUT_BUFFER, IPU_PIX_FMT_NV12,
cam->crop_current.width,
cam->crop_current.height,
cam->crop_current.width, IPU_ROTATE_NONE,
fbi->fix.smem_start +
(fbi->fix.line_length * fbvar.yres),
fbi->fix.smem_start, 0,
cam->offset.u_offset, cam->offset.u_offset);
} else {
err = ipu_init_channel_buffer(cam->ipu, chan,
IPU_OUTPUT_BUFFER, IPU_PIX_FMT_NV12,
cam->crop_current.width,
cam->crop_current.height,
cam->crop_current.width, IPU_ROTATE_NONE,
cam->vf_bufs[0], cam->vf_bufs[1], 0,
cam->offset.u_offset, cam->offset.u_offset);
}
if (err != 0) {
printk(KERN_ERR "CSI_MEM output buffer\n");
goto out_1;
}
err = ipu_enable_channel(cam->ipu, chan);
if (err < 0) {
printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
goto out_1;
}
csi_buffer_num = 0;
ipu_select_buffer(cam->ipu, chan, IPU_OUTPUT_BUFFER, 0);
ipu_select_buffer(cam->ipu, chan, IPU_OUTPUT_BUFFER, 1);
return err;
out_1:
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0],
(dma_addr_t) cam->vf_bufs[0]);
cam->vf_bufs_vaddr[0] = NULL;
cam->vf_bufs[0] = 0;
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1],
(dma_addr_t) cam->vf_bufs[1]);
cam->vf_bufs_vaddr[1] = NULL;
cam->vf_bufs[1] = 0;
}
out_2:
return err;
}
/*!
* Enable encoder task
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int csi_enc_enabling_tasks(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n");
ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi);
err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi,
csi_enc_callback, 0, "Mxc Camera", cam);
if (err != 0) {
printk(KERN_ERR "Error registering CSI_OUT_EOF irq\n");
return err;
}
INIT_WORK(&cam->csi_work_struct, csi_buf_work_func);
err = csi_enc_setup(cam);
if (err != 0) {
printk(KERN_ERR "csi_enc_setup %d\n", err);
goto out1;
}
return err;
out1:
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi, cam);
return err;
}
/*
* Function definitions
*/
/*!
* foreground_start - start the vf task
*
* @param private cam_data * mxc v4l2 main structure
*
*/
static int foreground_start(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0, i = 0, screen_size;
char *base;
if (!cam) {
printk(KERN_ERR "private is NULL\n");
return -EIO;
}
if (cam->overlay_active == true) {
pr_debug("already started.\n");
return 0;
}
get_disp_ipu(cam);
for (i = 0; i < num_registered_fb; i++) {
char *idstr = registered_fb[i]->fix.id;
if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) ||
((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) {
fbi = registered_fb[i];
break;
}
}
if (fbi == NULL) {
printk(KERN_ERR "DISP FG fb not found\n");
return -EPERM;
}
fbvar = fbi->var;
/* Store the overlay frame buffer's original std */
cam->fb_origin_std = fbvar.nonstd;
if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) {
/* Use DP to do CSC so that we can get better performance */
vf_out_format = IPU_PIX_FMT_NV12;
fbvar.nonstd = vf_out_format;
} else {
vf_out_format = IPU_PIX_FMT_RGB565;
fbvar.nonstd = 0;
}
fbvar.bits_per_pixel = 16;
fbvar.xres = fbvar.xres_virtual = cam->win.w.width;
fbvar.yres = cam->win.w.height;
fbvar.yres_virtual = cam->win.w.height * 2;
fbvar.yoffset = 0;
fbvar.vmode &= ~FB_VMODE_YWRAP;
fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG;
fbvar.activate |= FB_ACTIVATE_FORCE;
fb_set_var(fbi, &fbvar);
ipu_disp_set_window_pos(disp_ipu, MEM_FG_SYNC, cam->win.w.left,
cam->win.w.top);
/* Fill black color for framebuffer */
base = (char *) fbi->screen_base;
screen_size = fbi->var.xres * fbi->var.yres;
if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) {
memset(base, 0, screen_size);
base += screen_size;
for (i = 0; i < screen_size / 2; i++, base++)
*base = 0x80;
} else {
for (i = 0; i < screen_size * 2; i++, base++)
*base = 0x00;
}
console_lock();
fb_blank(fbi, FB_BLANK_UNBLANK);
console_unlock();
/* correct display ch buffer address */
ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER,
0, fbi->fix.smem_start +
(fbi->fix.line_length * fbvar.yres));
ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER,
1, fbi->fix.smem_start);
err = csi_enc_enabling_tasks(cam);
if (err != 0) {
printk(KERN_ERR "Error csi enc enable fail\n");
return err;
}
cam->overlay_active = true;
return err;
}
/*!
* foreground_stop - stop the vf task
*
* @param private cam_data * mxc v4l2 main structure
*
*/
static int foreground_stop(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0, i = 0;
struct fb_info *fbi = NULL;
struct fb_var_screeninfo fbvar;
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
if (cam->overlay_active == false)
return 0;
err = ipu_disable_channel(cam->ipu, chan, true);
ipu_uninit_channel(cam->ipu, chan);
csi_buffer_num = 0;
buffer_num = 0;
for (i = 0; i < num_registered_fb; i++) {
char *idstr = registered_fb[i]->fix.id;
if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) ||
((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) {
fbi = registered_fb[i];
break;
}
}
if (fbi == NULL) {
printk(KERN_ERR "DISP FG fb not found\n");
return -EPERM;
}
console_lock();
fb_blank(fbi, FB_BLANK_POWERDOWN);
console_unlock();
/* Set the overlay frame buffer std to what it is used to be */
fbvar = fbi->var;
fbvar.accel_flags = FB_ACCEL_TRIPLE_FLAG;
fbvar.nonstd = cam->fb_origin_std;
fbvar.activate |= FB_ACTIVATE_FORCE;
fb_set_var(fbi, &fbvar);
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id)
mipi_csi2_pixelclk_disable(mipi_csi2_info);
}
}
#endif
flush_work(&cam->csi_work_struct);
cancel_work_sync(&cam->csi_work_struct);
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0],
(dma_addr_t) cam->vf_bufs[0]);
cam->vf_bufs_vaddr[0] = NULL;
cam->vf_bufs[0] = 0;
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1],
(dma_addr_t) cam->vf_bufs[1]);
cam->vf_bufs_vaddr[1] = NULL;
cam->vf_bufs[1] = 0;
}
cam->overlay_active = false;
return err;
}
/*!
* Enable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int foreground_enable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
return ipu_enable_csi(cam->ipu, cam->csi);
}
/*!
* Disable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int foreground_disable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
/* free csi eof irq firstly.
* when disable csi, wait for idmac eof.
* it requests eof irq again */
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi, cam);
return ipu_disable_csi(cam->ipu, cam->csi);
}
/*!
* function to select foreground as the working path
*
* @param private cam_data * mxc v4l2 main structure
*
* @return status
*/
int foreground_sdc_select(void *private)
{
cam_data *cam;
int err = 0;
if (private) {
cam = (cam_data *) private;
cam->vf_start_sdc = foreground_start;
cam->vf_stop_sdc = foreground_stop;
cam->vf_enable_csi = foreground_enable_csi;
cam->vf_disable_csi = foreground_disable_csi;
cam->overlay_active = false;
} else
err = -EIO;
return err;
}
EXPORT_SYMBOL(foreground_sdc_select);
/*!
* function to de-select foreground as the working path
*
* @param private cam_data * mxc v4l2 main structure
*
* @return int
*/
int foreground_sdc_deselect(void *private)
{
cam_data *cam;
if (private) {
cam = (cam_data *) private;
cam->vf_start_sdc = NULL;
cam->vf_stop_sdc = NULL;
cam->vf_enable_csi = NULL;
cam->vf_disable_csi = NULL;
}
return 0;
}
EXPORT_SYMBOL(foreground_sdc_deselect);
/*!
* Init viewfinder task.
*
* @return Error code indicating success or failure
*/
__init int foreground_sdc_init(void)
{
return 0;
}
/*!
* Deinit viewfinder task.
*
* @return Error code indicating success or failure
*/
void __exit foreground_sdc_exit(void)
{
}
module_init(foreground_sdc_init);
module_exit(foreground_sdc_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("IPU PRP VF SDC Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,597 @@
/*
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_prp_enc.c
*
* @brief IPU Use case for PRP-ENC
*
* @ingroup IPU
*/
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/ipu.h>
#include <linux/mipi_csi2.h>
#include "mxc_v4l2_capture.h"
#include "ipu_prp_sw.h"
#ifdef CAMERA_DBG
#define CAMERA_TRACE(x) (printk)x
#else
#define CAMERA_TRACE(x)
#endif
static ipu_rotate_mode_t grotation = IPU_ROTATE_NONE;
/*
* Function definitions
*/
/*!
* IPU ENC callback function.
*
* @param irq int irq line
* @param dev_id void * device id
*
* @return status IRQ_HANDLED for handled
*/
static irqreturn_t prp_enc_callback(int irq, void *dev_id)
{
cam_data *cam = (cam_data *) dev_id;
if (cam->enc_callback == NULL)
return IRQ_HANDLED;
cam->enc_callback(irq, dev_id);
return IRQ_HANDLED;
}
/*!
* PrpENC enable channel setup function
*
* @param cam struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_enc_setup(cam_data *cam)
{
ipu_channel_params_t enc;
int err = 0;
dma_addr_t dummy = cam->dummy_frame.buffer.m.offset;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
CAMERA_TRACE("In prp_enc_setup\n");
if (!cam) {
printk(KERN_ERR "cam private is NULL\n");
return -ENXIO;
}
memset(&enc, 0, sizeof(ipu_channel_params_t));
ipu_csi_get_window_size(cam->ipu, &enc.csi_prp_enc_mem.in_width,
&enc.csi_prp_enc_mem.in_height, cam->csi);
enc.csi_prp_enc_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.width;
enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.height;
enc.csi_prp_enc_mem.csi = cam->csi;
if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.height;
enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.width;
}
if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV420P;
pr_info("YUV420\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YVU420P;
pr_info("YVU420\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV422P;
pr_info("YUV422P\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUYV;
pr_info("YUYV\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_UYVY;
pr_info("UYVY\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_NV12;
pr_info("NV12\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR24;
pr_info("BGR24\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB24;
pr_info("RGB24\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB565;
pr_info("RGB565\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR32;
pr_info("BGR32\n");
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) {
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB32;
pr_info("RGB32\n");
} else {
printk(KERN_ERR "format not supported\n");
return -EINVAL;
}
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id) {
enc.csi_prp_enc_mem.mipi_en = true;
enc.csi_prp_enc_mem.mipi_vc =
mipi_csi2_get_virtual_channel(mipi_csi2_info);
enc.csi_prp_enc_mem.mipi_id =
mipi_csi2_get_datatype(mipi_csi2_info);
mipi_csi2_pixelclk_enable(mipi_csi2_info);
} else {
enc.csi_prp_enc_mem.mipi_en = false;
enc.csi_prp_enc_mem.mipi_vc = 0;
enc.csi_prp_enc_mem.mipi_id = 0;
}
} else {
enc.csi_prp_enc_mem.mipi_en = false;
enc.csi_prp_enc_mem.mipi_vc = 0;
enc.csi_prp_enc_mem.mipi_id = 0;
}
}
#endif
err = ipu_init_channel(cam->ipu, CSI_PRP_ENC_MEM, &enc);
if (err != 0) {
printk(KERN_ERR "ipu_init_channel %d\n", err);
return err;
}
grotation = cam->rotation;
if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
if (cam->rot_enc_bufs_vaddr[0]) {
dma_free_coherent(0, cam->rot_enc_buf_size[0],
cam->rot_enc_bufs_vaddr[0],
cam->rot_enc_bufs[0]);
}
if (cam->rot_enc_bufs_vaddr[1]) {
dma_free_coherent(0, cam->rot_enc_buf_size[1],
cam->rot_enc_bufs_vaddr[1],
cam->rot_enc_bufs[1]);
}
cam->rot_enc_buf_size[0] =
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
cam->rot_enc_bufs_vaddr[0] =
(void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[0],
&cam->rot_enc_bufs[0],
GFP_DMA | GFP_KERNEL);
if (!cam->rot_enc_bufs_vaddr[0]) {
printk(KERN_ERR "alloc enc_bufs0\n");
return -ENOMEM;
}
cam->rot_enc_buf_size[1] =
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
cam->rot_enc_bufs_vaddr[1] =
(void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[1],
&cam->rot_enc_bufs[1],
GFP_DMA | GFP_KERNEL);
if (!cam->rot_enc_bufs_vaddr[1]) {
dma_free_coherent(0, cam->rot_enc_buf_size[0],
cam->rot_enc_bufs_vaddr[0],
cam->rot_enc_bufs[0]);
cam->rot_enc_bufs_vaddr[0] = NULL;
cam->rot_enc_bufs[0] = 0;
printk(KERN_ERR "alloc enc_bufs1\n");
return -ENOMEM;
}
err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM,
IPU_OUTPUT_BUFFER,
enc.csi_prp_enc_mem.out_pixel_fmt,
enc.csi_prp_enc_mem.out_width,
enc.csi_prp_enc_mem.out_height,
enc.csi_prp_enc_mem.out_width,
IPU_ROTATE_NONE,
cam->rot_enc_bufs[0],
cam->rot_enc_bufs[1], 0, 0, 0);
if (err != 0) {
printk(KERN_ERR "CSI_PRP_ENC_MEM err\n");
return err;
}
err = ipu_init_channel(cam->ipu, MEM_ROT_ENC_MEM, NULL);
if (err != 0) {
printk(KERN_ERR "MEM_ROT_ENC_MEM channel err\n");
return err;
}
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM,
IPU_INPUT_BUFFER,
enc.csi_prp_enc_mem.out_pixel_fmt,
enc.csi_prp_enc_mem.out_width,
enc.csi_prp_enc_mem.out_height,
enc.csi_prp_enc_mem.out_width,
cam->rotation,
cam->rot_enc_bufs[0],
cam->rot_enc_bufs[1], 0, 0, 0);
if (err != 0) {
printk(KERN_ERR "MEM_ROT_ENC_MEM input buffer\n");
return err;
}
err =
ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM,
IPU_OUTPUT_BUFFER,
enc.csi_prp_enc_mem.out_pixel_fmt,
enc.csi_prp_enc_mem.out_height,
enc.csi_prp_enc_mem.out_width,
cam->v2f.fmt.pix.bytesperline /
bytes_per_pixel(enc.csi_prp_enc_mem.
out_pixel_fmt),
IPU_ROTATE_NONE,
dummy, dummy, 0,
cam->offset.u_offset,
cam->offset.v_offset);
if (err != 0) {
printk(KERN_ERR "MEM_ROT_ENC_MEM output buffer\n");
return err;
}
err = ipu_link_channels(cam->ipu,
CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
if (err < 0) {
printk(KERN_ERR
"link CSI_PRP_ENC_MEM-MEM_ROT_ENC_MEM\n");
return err;
}
err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM);
if (err < 0) {
printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
return err;
}
err = ipu_enable_channel(cam->ipu, MEM_ROT_ENC_MEM);
if (err < 0) {
printk(KERN_ERR "ipu_enable_channel MEM_ROT_ENC_MEM\n");
return err;
}
ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM,
IPU_OUTPUT_BUFFER, 0);
ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM,
IPU_OUTPUT_BUFFER, 1);
} else {
err =
ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM,
IPU_OUTPUT_BUFFER,
enc.csi_prp_enc_mem.out_pixel_fmt,
enc.csi_prp_enc_mem.out_width,
enc.csi_prp_enc_mem.out_height,
cam->v2f.fmt.pix.bytesperline /
bytes_per_pixel(enc.csi_prp_enc_mem.
out_pixel_fmt),
cam->rotation,
dummy, dummy, 0,
cam->offset.u_offset,
cam->offset.v_offset);
if (err != 0) {
printk(KERN_ERR "CSI_PRP_ENC_MEM output buffer\n");
return err;
}
err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM);
if (err < 0) {
printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
return err;
}
}
return err;
}
/*!
* function to update physical buffer address for encorder IDMA channel
*
* @param private pointer to cam_data structure
* @param eba physical buffer address for encorder IDMA channel
*
* @return status
*/
static int prp_enc_eba_update(void *private, dma_addr_t eba)
{
int err = 0;
cam_data *cam = (cam_data *) private;
struct ipu_soc *ipu = cam->ipu;
int *buffer_num = &cam->ping_pong_csi;
pr_debug("eba %x\n", eba);
if (grotation >= IPU_ROTATE_90_RIGHT) {
err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM,
IPU_OUTPUT_BUFFER, *buffer_num,
eba);
} else {
err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM,
IPU_OUTPUT_BUFFER, *buffer_num,
eba);
}
if (err != 0) {
if (grotation >= IPU_ROTATE_90_RIGHT) {
ipu_clear_buffer_ready(ipu, MEM_ROT_ENC_MEM,
IPU_OUTPUT_BUFFER,
*buffer_num);
err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM,
IPU_OUTPUT_BUFFER,
*buffer_num,
eba);
} else {
ipu_clear_buffer_ready(ipu, CSI_PRP_ENC_MEM,
IPU_OUTPUT_BUFFER,
*buffer_num);
err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM,
IPU_OUTPUT_BUFFER,
*buffer_num,
eba);
}
if (err != 0) {
pr_err("ERROR: v4l2 capture: fail to update "
"buf%d\n", *buffer_num);
return err;
}
}
if (grotation >= IPU_ROTATE_90_RIGHT) {
ipu_select_buffer(ipu, MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER,
*buffer_num);
} else {
ipu_select_buffer(ipu, CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER,
*buffer_num);
}
*buffer_num = (*buffer_num == 0) ? 1 : 0;
return 0;
}
/*!
* Enable encoder task
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_enc_enabling_tasks(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
CAMERA_TRACE("IPU:In prp_enc_enabling_tasks\n");
cam->dummy_frame.vaddress = dma_alloc_coherent(0,
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
&cam->dummy_frame.paddress,
GFP_DMA | GFP_KERNEL);
if (cam->dummy_frame.vaddress == 0) {
pr_err("ERROR: v4l2 capture: Allocate dummy frame "
"failed.\n");
return -ENOBUFS;
}
cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE;
cam->dummy_frame.buffer.length =
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress;
if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF,
prp_enc_callback, 0, "Mxc Camera", cam);
} else {
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF,
prp_enc_callback, 0, "Mxc Camera", cam);
}
if (err != 0) {
printk(KERN_ERR "Error registering rot irq\n");
return err;
}
err = prp_enc_setup(cam);
if (err != 0) {
printk(KERN_ERR "prp_enc_setup %d\n", err);
return err;
}
return err;
}
/*!
* Disable encoder task
* @param private struct cam_data * mxc capture instance
*
* @return int
*/
static int prp_enc_disabling_tasks(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF, cam);
ipu_unlink_channels(cam->ipu, CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
}
err = ipu_disable_channel(cam->ipu, CSI_PRP_ENC_MEM, true);
if (cam->rotation >= IPU_ROTATE_90_RIGHT)
err |= ipu_disable_channel(cam->ipu, MEM_ROT_ENC_MEM, true);
ipu_uninit_channel(cam->ipu, CSI_PRP_ENC_MEM);
if (cam->rotation >= IPU_ROTATE_90_RIGHT)
ipu_uninit_channel(cam->ipu, MEM_ROT_ENC_MEM);
if (cam->dummy_frame.vaddress != 0) {
dma_free_coherent(0, cam->dummy_frame.buffer.length,
cam->dummy_frame.vaddress,
cam->dummy_frame.paddress);
cam->dummy_frame.vaddress = 0;
}
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id)
mipi_csi2_pixelclk_disable(mipi_csi2_info);
}
}
#endif
return err;
}
/*!
* Enable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_enc_enable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
return ipu_enable_csi(cam->ipu, cam->csi);
}
/*!
* Disable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_enc_disable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
/* free csi eof irq firstly.
* when disable csi, wait for idmac eof.
* it requests eof irq again */
if (cam->rotation < IPU_ROTATE_90_RIGHT)
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF, cam);
return ipu_disable_csi(cam->ipu, cam->csi);
}
/*!
* function to select PRP-ENC as the working path
*
* @param private struct cam_data * mxc capture instance
*
* @return int
*/
int prp_enc_select(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
if (cam) {
cam->enc_update_eba = prp_enc_eba_update;
cam->enc_enable = prp_enc_enabling_tasks;
cam->enc_disable = prp_enc_disabling_tasks;
cam->enc_enable_csi = prp_enc_enable_csi;
cam->enc_disable_csi = prp_enc_disable_csi;
} else {
err = -EIO;
}
return err;
}
EXPORT_SYMBOL(prp_enc_select);
/*!
* function to de-select PRP-ENC as the working path
*
* @param private struct cam_data * mxc capture instance
*
* @return int
*/
int prp_enc_deselect(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
if (cam) {
cam->enc_update_eba = NULL;
cam->enc_enable = NULL;
cam->enc_disable = NULL;
cam->enc_enable_csi = NULL;
cam->enc_disable_csi = NULL;
if (cam->rot_enc_bufs_vaddr[0]) {
dma_free_coherent(0, cam->rot_enc_buf_size[0],
cam->rot_enc_bufs_vaddr[0],
cam->rot_enc_bufs[0]);
cam->rot_enc_bufs_vaddr[0] = NULL;
cam->rot_enc_bufs[0] = 0;
}
if (cam->rot_enc_bufs_vaddr[1]) {
dma_free_coherent(0, cam->rot_enc_buf_size[1],
cam->rot_enc_bufs_vaddr[1],
cam->rot_enc_bufs[1]);
cam->rot_enc_bufs_vaddr[1] = NULL;
cam->rot_enc_bufs[1] = 0;
}
}
return err;
}
EXPORT_SYMBOL(prp_enc_deselect);
/*!
* Init the Encorder channels
*
* @return Error code indicating success or failure
*/
__init int prp_enc_init(void)
{
return 0;
}
/*!
* Deinit the Encorder channels
*
*/
void __exit prp_enc_exit(void)
{
}
module_init(prp_enc_init);
module_exit(prp_enc_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("IPU PRP ENC Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,43 @@
/*
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_prp_sw.h
*
* @brief This file contains the IPU PRP use case driver header.
*
* @ingroup IPU
*/
#ifndef _INCLUDE_IPU__PRP_SW_H_
#define _INCLUDE_IPU__PRP_SW_H_
int csi_enc_select(void *private);
int csi_enc_deselect(void *private);
int prp_enc_select(void *private);
int prp_enc_deselect(void *private);
#ifdef CONFIG_MXC_IPU_PRP_VF_SDC
int prp_vf_sdc_select(void *private);
int prp_vf_sdc_deselect(void *private);
int prp_vf_sdc_select_bg(void *private);
int prp_vf_sdc_deselect_bg(void *private);
#else
int foreground_sdc_select(void *private);
int foreground_sdc_deselect(void *private);
int bg_overlay_sdc_select(void *private);
int bg_overlay_sdc_deselect(void *private);
#endif
int prp_still_select(void *private);
int prp_still_deselect(void *private);
#endif

View File

@ -0,0 +1,582 @@
/*
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/* * The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_prp_vf_sdc.c
*
* @brief IPU Use case for PRP-VF
*
* @ingroup IPU
*/
#include <linux/dma-mapping.h>
#include <linux/console.h>
#include <linux/ipu.h>
#include <linux/module.h>
#include <linux/mxcfb.h>
#include <mach/hardware.h>
#include <mach/mipi_csi2.h>
#include "mxc_v4l2_capture.h"
#include "ipu_prp_sw.h"
static int buffer_num;
static struct ipu_soc *disp_ipu;
static void get_disp_ipu(cam_data *cam)
{
if (cam->output > 2)
disp_ipu = ipu_get_soc(1); /* using DISP4 */
else
disp_ipu = ipu_get_soc(0);
}
static irqreturn_t prpvf_rot_eof_callback(int irq, void *dev_id)
{
cam_data *cam = dev_id;
pr_debug("buffer_num %d\n", buffer_num);
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
ipu_select_buffer(disp_ipu, MEM_FG_SYNC,
IPU_INPUT_BUFFER, buffer_num);
buffer_num = (buffer_num == 0) ? 1 : 0;
ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM,
IPU_OUTPUT_BUFFER, buffer_num);
} else {
ipu_select_buffer(disp_ipu, MEM_FG_SYNC,
IPU_INPUT_BUFFER, buffer_num);
buffer_num = (buffer_num == 0) ? 1 : 0;
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
IPU_OUTPUT_BUFFER, buffer_num);
}
return IRQ_HANDLED;
}
/*
* Function definitions
*/
/*!
* prpvf_start - start the vf task
*
* @param private cam_data * mxc v4l2 main structure
*
*/
static int prpvf_start(void *private)
{
struct fb_var_screeninfo fbvar;
struct fb_info *fbi = NULL;
cam_data *cam = (cam_data *) private;
ipu_channel_params_t vf;
u32 vf_out_format = 0;
u32 size = 2, temp = 0;
int err = 0, i = 0;
short *tmp, color;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
if (!cam) {
printk(KERN_ERR "private is NULL\n");
return -EIO;
}
if (cam->overlay_active == true) {
pr_debug("already started.\n");
return 0;
}
get_disp_ipu(cam);
for (i = 0; i < num_registered_fb; i++) {
char *idstr = registered_fb[i]->fix.id;
if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) ||
((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) {
fbi = registered_fb[i];
break;
}
}
if (fbi == NULL) {
printk(KERN_ERR "DISP FG fb not found\n");
return -EPERM;
}
fbvar = fbi->var;
/* Store the overlay frame buffer's original std */
cam->fb_origin_std = fbvar.nonstd;
if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) {
/* Use DP to do CSC so that we can get better performance */
vf_out_format = IPU_PIX_FMT_UYVY;
fbvar.nonstd = vf_out_format;
color = 0x80;
} else {
vf_out_format = IPU_PIX_FMT_RGB565;
fbvar.nonstd = 0;
color = 0x0;
}
fbvar.bits_per_pixel = 16;
fbvar.xres = fbvar.xres_virtual = cam->win.w.width;
fbvar.yres = cam->win.w.height;
fbvar.yres_virtual = cam->win.w.height * 2;
fbvar.yoffset = 0;
fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG;
fbvar.activate |= FB_ACTIVATE_FORCE;
fb_set_var(fbi, &fbvar);
ipu_disp_set_window_pos(disp_ipu, MEM_FG_SYNC, cam->win.w.left,
cam->win.w.top);
/* Fill black color for framebuffer */
tmp = (short *) fbi->screen_base;
for (i = 0; i < (fbi->fix.line_length * fbi->var.yres)/2;
i++, tmp++)
*tmp = color;
console_lock();
fb_blank(fbi, FB_BLANK_UNBLANK);
console_unlock();
/* correct display ch buffer address */
ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER,
0, fbi->fix.smem_start +
(fbi->fix.line_length * fbvar.yres));
ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER,
1, fbi->fix.smem_start);
memset(&vf, 0, sizeof(ipu_channel_params_t));
ipu_csi_get_window_size(cam->ipu, &vf.csi_prp_vf_mem.in_width,
&vf.csi_prp_vf_mem.in_height, cam->csi);
vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
vf.csi_prp_vf_mem.out_width = cam->win.w.width;
vf.csi_prp_vf_mem.out_height = cam->win.w.height;
vf.csi_prp_vf_mem.csi = cam->csi;
if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
vf.csi_prp_vf_mem.out_width = cam->win.w.height;
vf.csi_prp_vf_mem.out_height = cam->win.w.width;
}
vf.csi_prp_vf_mem.out_pixel_fmt = vf_out_format;
size = cam->win.w.width * cam->win.w.height * size;
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id) {
vf.csi_prp_vf_mem.mipi_en = true;
vf.csi_prp_vf_mem.mipi_vc =
mipi_csi2_get_virtual_channel(mipi_csi2_info);
vf.csi_prp_vf_mem.mipi_id =
mipi_csi2_get_datatype(mipi_csi2_info);
mipi_csi2_pixelclk_enable(mipi_csi2_info);
} else {
vf.csi_prp_vf_mem.mipi_en = false;
vf.csi_prp_vf_mem.mipi_vc = 0;
vf.csi_prp_vf_mem.mipi_id = 0;
}
} else {
vf.csi_prp_vf_mem.mipi_en = false;
vf.csi_prp_vf_mem.mipi_vc = 0;
vf.csi_prp_vf_mem.mipi_id = 0;
}
}
#endif
err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf);
if (err != 0)
goto out_5;
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0],
(dma_addr_t) cam->vf_bufs[0]);
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1],
(dma_addr_t) cam->vf_bufs[1]);
}
cam->vf_bufs_size[0] = PAGE_ALIGN(size);
cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
cam->vf_bufs_size[0],
(dma_addr_t *) &
cam->vf_bufs[0],
GFP_DMA |
GFP_KERNEL);
if (cam->vf_bufs_vaddr[0] == NULL) {
printk(KERN_ERR "Error to allocate vf buffer\n");
err = -ENOMEM;
goto out_4;
}
cam->vf_bufs_size[1] = PAGE_ALIGN(size);
cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
cam->vf_bufs_size[1],
(dma_addr_t *) &
cam->vf_bufs[1],
GFP_DMA |
GFP_KERNEL);
if (cam->vf_bufs_vaddr[1] == NULL) {
printk(KERN_ERR "Error to allocate vf buffer\n");
err = -ENOMEM;
goto out_3;
}
pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM,
IPU_OUTPUT_BUFFER,
vf_out_format,
vf.csi_prp_vf_mem.out_width,
vf.csi_prp_vf_mem.out_height,
vf.csi_prp_vf_mem.out_width,
IPU_ROTATE_NONE,
cam->vf_bufs[0], cam->vf_bufs[1],
0, 0, 0);
if (err != 0)
goto out_3;
err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL);
if (err != 0) {
printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
goto out_3;
}
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
IPU_INPUT_BUFFER,
vf_out_format,
vf.csi_prp_vf_mem.out_width,
vf.csi_prp_vf_mem.out_height,
vf.csi_prp_vf_mem.out_width,
cam->vf_rotation,
cam->vf_bufs[0],
cam->vf_bufs[1],
0, 0, 0);
if (err != 0) {
printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
goto out_2;
}
if (cam->vf_rotation < IPU_ROTATE_90_RIGHT) {
temp = vf.csi_prp_vf_mem.out_width;
vf.csi_prp_vf_mem.out_width =
vf.csi_prp_vf_mem.out_height;
vf.csi_prp_vf_mem.out_height = temp;
}
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
IPU_OUTPUT_BUFFER,
vf_out_format,
vf.csi_prp_vf_mem.out_height,
vf.csi_prp_vf_mem.out_width,
vf.csi_prp_vf_mem.out_height,
IPU_ROTATE_NONE,
fbi->fix.smem_start +
(fbi->fix.line_length *
fbi->var.yres),
fbi->fix.smem_start, 0, 0, 0);
if (err != 0) {
printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
goto out_2;
}
ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF);
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF,
prpvf_rot_eof_callback,
0, "Mxc Camera", cam);
if (err < 0) {
printk(KERN_ERR "Error request irq:IPU_IRQ_PRP_VF_ROT_OUT_EOF\n");
goto out_2;
}
err = ipu_link_channels(cam->ipu,
CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
if (err < 0) {
printk(KERN_ERR
"Error link CSI_PRP_VF_MEM-MEM_ROT_VF_MEM\n");
goto out_1;
}
ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM);
ipu_enable_channel(cam->ipu, MEM_ROT_VF_MEM);
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
IPU_OUTPUT_BUFFER, 0);
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
IPU_OUTPUT_BUFFER, 1);
ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM,
IPU_OUTPUT_BUFFER, 0);
} else {
err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM,
IPU_OUTPUT_BUFFER,
vf_out_format, cam->win.w.width,
cam->win.w.height,
cam->win.w.width,
cam->vf_rotation,
fbi->fix.smem_start +
(fbi->fix.line_length *
fbi->var.yres),
fbi->fix.smem_start, 0, 0, 0);
if (err != 0) {
printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
goto out_4;
}
ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF);
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF,
prpvf_rot_eof_callback,
0, "Mxc Camera", cam);
if (err < 0) {
printk(KERN_ERR "Error request irq:IPU_IRQ_PRP_VF_OUT_EOF\n");
goto out_4;
}
ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM);
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
IPU_OUTPUT_BUFFER, 0);
}
cam->overlay_active = true;
return err;
out_1:
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL);
out_2:
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP)
ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM);
out_3:
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0],
(dma_addr_t) cam->vf_bufs[0]);
cam->vf_bufs_vaddr[0] = NULL;
cam->vf_bufs[0] = 0;
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1],
(dma_addr_t) cam->vf_bufs[1]);
cam->vf_bufs_vaddr[1] = NULL;
cam->vf_bufs[1] = 0;
}
out_4:
ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM);
out_5:
return err;
}
/*!
* prpvf_stop - stop the vf task
*
* @param private cam_data * mxc v4l2 main structure
*
*/
static int prpvf_stop(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0, i = 0;
struct fb_info *fbi = NULL;
struct fb_var_screeninfo fbvar;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
if (cam->overlay_active == false)
return 0;
for (i = 0; i < num_registered_fb; i++) {
char *idstr = registered_fb[i]->fix.id;
if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) ||
((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) {
fbi = registered_fb[i];
break;
}
}
if (fbi == NULL) {
printk(KERN_ERR "DISP FG fb not found\n");
return -EPERM;
}
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
ipu_unlink_channels(cam->ipu, CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF, cam);
}
buffer_num = 0;
ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true);
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true);
ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM);
}
ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM);
console_lock();
fb_blank(fbi, FB_BLANK_POWERDOWN);
console_unlock();
/* Set the overlay frame buffer std to what it is used to be */
fbvar = fbi->var;
fbvar.accel_flags = FB_ACCEL_TRIPLE_FLAG;
fbvar.nonstd = cam->fb_origin_std;
fbvar.activate |= FB_ACTIVATE_FORCE;
fb_set_var(fbi, &fbvar);
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id)
mipi_csi2_pixelclk_disable(mipi_csi2_info);
}
}
#endif
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0],
(dma_addr_t) cam->vf_bufs[0]);
cam->vf_bufs_vaddr[0] = NULL;
cam->vf_bufs[0] = 0;
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1],
(dma_addr_t) cam->vf_bufs[1]);
cam->vf_bufs_vaddr[1] = NULL;
cam->vf_bufs[1] = 0;
}
cam->overlay_active = false;
return err;
}
/*!
* Enable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_vf_enable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
return ipu_enable_csi(cam->ipu, cam->csi);
}
/*!
* Disable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_vf_disable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
/* free csi eof irq firstly.
* when disable csi, wait for idmac eof.
* it requests eof irq again */
if (cam->vf_rotation < IPU_ROTATE_VERT_FLIP)
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam);
return ipu_disable_csi(cam->ipu, cam->csi);
}
/*!
* function to select PRP-VF as the working path
*
* @param private cam_data * mxc v4l2 main structure
*
* @return status
*/
int prp_vf_sdc_select(void *private)
{
cam_data *cam;
int err = 0;
if (private) {
cam = (cam_data *) private;
cam->vf_start_sdc = prpvf_start;
cam->vf_stop_sdc = prpvf_stop;
cam->vf_enable_csi = prp_vf_enable_csi;
cam->vf_disable_csi = prp_vf_disable_csi;
cam->overlay_active = false;
} else
err = -EIO;
return err;
}
EXPORT_SYMBOL(prp_vf_sdc_select);
/*!
* function to de-select PRP-VF as the working path
*
* @param private cam_data * mxc v4l2 main structure
*
* @return int
*/
int prp_vf_sdc_deselect(void *private)
{
cam_data *cam;
if (private) {
cam = (cam_data *) private;
cam->vf_start_sdc = NULL;
cam->vf_stop_sdc = NULL;
cam->vf_enable_csi = NULL;
cam->vf_disable_csi = NULL;
}
return 0;
}
EXPORT_SYMBOL(prp_vf_sdc_deselect);
/*!
* Init viewfinder task.
*
* @return Error code indicating success or failure
*/
__init int prp_vf_sdc_init(void)
{
return 0;
}
/*!
* Deinit viewfinder task.
*
* @return Error code indicating success or failure
*/
void __exit prp_vf_sdc_exit(void)
{
}
module_init(prp_vf_sdc_init);
module_exit(prp_vf_sdc_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("IPU PRP VF SDC Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,521 @@
/*
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_prp_vf_sdc_bg.c
*
* @brief IPU Use case for PRP-VF back-ground
*
* @ingroup IPU
*/
#include <linux/dma-mapping.h>
#include <linux/fb.h>
#include <linux/ipu.h>
#include <linux/module.h>
#include <mach/mipi_csi2.h>
#include "mxc_v4l2_capture.h"
#include "ipu_prp_sw.h"
static int buffer_num;
static int buffer_ready;
static struct ipu_soc *disp_ipu;
static void get_disp_ipu(cam_data *cam)
{
if (cam->output > 2)
disp_ipu = ipu_get_soc(1); /* using DISP4 */
else
disp_ipu = ipu_get_soc(0);
}
/*
* Function definitions
*/
/*!
* SDC V-Sync callback function.
*
* @param irq int irq line
* @param dev_id void * device id
*
* @return status IRQ_HANDLED for handled
*/
static irqreturn_t prpvf_sdc_vsync_callback(int irq, void *dev_id)
{
cam_data *cam = dev_id;
if (buffer_ready > 0) {
ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM,
IPU_OUTPUT_BUFFER, 0);
buffer_ready--;
}
return IRQ_HANDLED;
}
/*!
* VF EOF callback function.
*
* @param irq int irq line
* @param dev_id void * device id
*
* @return status IRQ_HANDLED for handled
*/
static irqreturn_t prpvf_vf_eof_callback(int irq, void *dev_id)
{
cam_data *cam = dev_id;
pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num);
ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM,
IPU_INPUT_BUFFER, buffer_num);
buffer_num = (buffer_num == 0) ? 1 : 0;
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
IPU_OUTPUT_BUFFER, buffer_num);
buffer_ready++;
return IRQ_HANDLED;
}
/*!
* prpvf_start - start the vf task
*
* @param private cam_data * mxc v4l2 main structure
*
*/
static int prpvf_start(void *private)
{
cam_data *cam = (cam_data *) private;
ipu_channel_params_t vf;
u32 format;
u32 offset;
u32 bpp, size = 3;
int err = 0;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
if (!cam) {
printk(KERN_ERR "private is NULL\n");
return -EIO;
}
if (cam->overlay_active == true) {
pr_debug("already start.\n");
return 0;
}
get_disp_ipu(cam);
format = cam->v4l2_fb.fmt.pixelformat;
if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) {
bpp = 3, size = 3;
pr_info("BGR24\n");
} else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) {
bpp = 2, size = 2;
pr_info("RGB565\n");
} else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) {
bpp = 4, size = 4;
pr_info("BGR32\n");
} else {
printk(KERN_ERR
"unsupported fix format from the framebuffer.\n");
return -EINVAL;
}
offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top +
size * cam->win.w.left;
if (cam->v4l2_fb.base == 0)
printk(KERN_ERR "invalid frame buffer address.\n");
else
offset += (u32) cam->v4l2_fb.base;
memset(&vf, 0, sizeof(ipu_channel_params_t));
ipu_csi_get_window_size(cam->ipu, &vf.csi_prp_vf_mem.in_width,
&vf.csi_prp_vf_mem.in_height, cam->csi);
vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
vf.csi_prp_vf_mem.out_width = cam->win.w.width;
vf.csi_prp_vf_mem.out_height = cam->win.w.height;
vf.csi_prp_vf_mem.csi = cam->csi;
if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
vf.csi_prp_vf_mem.out_width = cam->win.w.height;
vf.csi_prp_vf_mem.out_height = cam->win.w.width;
}
vf.csi_prp_vf_mem.out_pixel_fmt = format;
size = cam->win.w.width * cam->win.w.height * size;
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id) {
vf.csi_prp_vf_mem.mipi_en = true;
vf.csi_prp_vf_mem.mipi_vc =
mipi_csi2_get_virtual_channel(mipi_csi2_info);
vf.csi_prp_vf_mem.mipi_id =
mipi_csi2_get_datatype(mipi_csi2_info);
mipi_csi2_pixelclk_enable(mipi_csi2_info);
} else {
vf.csi_prp_vf_mem.mipi_en = false;
vf.csi_prp_vf_mem.mipi_vc = 0;
vf.csi_prp_vf_mem.mipi_id = 0;
}
} else {
vf.csi_prp_vf_mem.mipi_en = false;
vf.csi_prp_vf_mem.mipi_vc = 0;
vf.csi_prp_vf_mem.mipi_id = 0;
}
}
#endif
err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf);
if (err != 0)
goto out_4;
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
}
cam->vf_bufs_size[0] = PAGE_ALIGN(size);
cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
cam->vf_bufs_size[0],
&cam->vf_bufs[0],
GFP_DMA |
GFP_KERNEL);
if (cam->vf_bufs_vaddr[0] == NULL) {
printk(KERN_ERR "Error to allocate vf buffer\n");
err = -ENOMEM;
goto out_3;
}
cam->vf_bufs_size[1] = PAGE_ALIGN(size);
cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
cam->vf_bufs_size[1],
&cam->vf_bufs[1],
GFP_DMA |
GFP_KERNEL);
if (cam->vf_bufs_vaddr[1] == NULL) {
printk(KERN_ERR "Error to allocate vf buffer\n");
err = -ENOMEM;
goto out_3;
}
err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM,
IPU_OUTPUT_BUFFER,
format, vf.csi_prp_vf_mem.out_width,
vf.csi_prp_vf_mem.out_height,
vf.csi_prp_vf_mem.out_width,
IPU_ROTATE_NONE,
cam->vf_bufs[0],
cam->vf_bufs[1],
0, 0, 0);
if (err != 0) {
printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
goto out_3;
}
err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL);
if (err != 0) {
printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
goto out_3;
}
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
IPU_INPUT_BUFFER,
format, vf.csi_prp_vf_mem.out_width,
vf.csi_prp_vf_mem.out_height,
vf.csi_prp_vf_mem.out_width,
cam->vf_rotation,
cam->vf_bufs[0],
cam->vf_bufs[1],
0, 0, 0);
if (err != 0) {
printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
goto out_2;
}
if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
IPU_OUTPUT_BUFFER,
format,
vf.csi_prp_vf_mem.out_height,
vf.csi_prp_vf_mem.out_width,
cam->overlay_fb->var.xres * bpp,
IPU_ROTATE_NONE,
offset, 0, 0, 0, 0);
if (err != 0) {
printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
goto out_2;
}
} else {
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
IPU_OUTPUT_BUFFER,
format,
vf.csi_prp_vf_mem.out_width,
vf.csi_prp_vf_mem.out_height,
cam->overlay_fb->var.xres * bpp,
IPU_ROTATE_NONE,
offset, 0, 0, 0, 0);
if (err != 0) {
printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
goto out_2;
}
}
ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF);
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF,
prpvf_vf_eof_callback,
0, "Mxc Camera", cam);
if (err != 0) {
printk(KERN_ERR
"Error registering IPU_IRQ_PRP_VF_OUT_EOF irq.\n");
goto out_2;
}
ipu_clear_irq(disp_ipu, IPU_IRQ_BG_SF_END);
err = ipu_request_irq(disp_ipu, IPU_IRQ_BG_SF_END,
prpvf_sdc_vsync_callback,
0, "Mxc Camera", cam);
if (err != 0) {
printk(KERN_ERR "Error registering IPU_IRQ_BG_SF_END irq.\n");
goto out_1;
}
ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM);
ipu_enable_channel(cam->ipu, MEM_ROT_VF_MEM);
buffer_num = 0;
buffer_ready = 0;
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
cam->overlay_active = true;
return err;
out_1:
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL);
out_2:
ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM);
out_3:
ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM);
out_4:
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
cam->vf_bufs_vaddr[0] = NULL;
cam->vf_bufs[0] = 0;
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
cam->vf_bufs_vaddr[1] = NULL;
cam->vf_bufs[1] = 0;
}
if (cam->rot_vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->rot_vf_buf_size[0],
cam->rot_vf_bufs_vaddr[0],
cam->rot_vf_bufs[0]);
cam->rot_vf_bufs_vaddr[0] = NULL;
cam->rot_vf_bufs[0] = 0;
}
if (cam->rot_vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->rot_vf_buf_size[1],
cam->rot_vf_bufs_vaddr[1],
cam->rot_vf_bufs[1]);
cam->rot_vf_bufs_vaddr[1] = NULL;
cam->rot_vf_bufs[1] = 0;
}
return err;
}
/*!
* prpvf_stop - stop the vf task
*
* @param private cam_data * mxc v4l2 main structure
*
*/
static int prpvf_stop(void *private)
{
cam_data *cam = (cam_data *) private;
#ifdef CONFIG_MXC_MIPI_CSI2
void *mipi_csi2_info;
int ipu_id;
int csi_id;
#endif
if (cam->overlay_active == false)
return 0;
ipu_free_irq(disp_ipu, IPU_IRQ_BG_SF_END, cam);
ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true);
ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true);
ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM);
ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM);
#ifdef CONFIG_MXC_MIPI_CSI2
mipi_csi2_info = mipi_csi2_get_info();
if (mipi_csi2_info) {
if (mipi_csi2_get_status(mipi_csi2_info)) {
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
if (cam->ipu == ipu_get_soc(ipu_id)
&& cam->csi == csi_id)
mipi_csi2_pixelclk_disable(mipi_csi2_info);
}
}
#endif
if (cam->vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
cam->vf_bufs_vaddr[0] = NULL;
cam->vf_bufs[0] = 0;
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->vf_bufs_size[1],
cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
cam->vf_bufs_vaddr[1] = NULL;
cam->vf_bufs[1] = 0;
}
if (cam->rot_vf_bufs_vaddr[0]) {
dma_free_coherent(0, cam->rot_vf_buf_size[0],
cam->rot_vf_bufs_vaddr[0],
cam->rot_vf_bufs[0]);
cam->rot_vf_bufs_vaddr[0] = NULL;
cam->rot_vf_bufs[0] = 0;
}
if (cam->rot_vf_bufs_vaddr[1]) {
dma_free_coherent(0, cam->rot_vf_buf_size[1],
cam->rot_vf_bufs_vaddr[1],
cam->rot_vf_bufs[1]);
cam->rot_vf_bufs_vaddr[1] = NULL;
cam->rot_vf_bufs[1] = 0;
}
buffer_num = 0;
buffer_ready = 0;
cam->overlay_active = false;
return 0;
}
/*!
* Enable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_vf_enable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
return ipu_enable_csi(cam->ipu, cam->csi);
}
/*!
* Disable csi
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_vf_disable_csi(void *private)
{
cam_data *cam = (cam_data *) private;
/* free csi eof irq firstly.
* when disable csi, wait for idmac eof.
* it requests eof irq again */
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam);
return ipu_disable_csi(cam->ipu, cam->csi);
}
/*!
* function to select PRP-VF as the working path
*
* @param private cam_data * mxc v4l2 main structure
*
* @return status
*/
int prp_vf_sdc_select_bg(void *private)
{
cam_data *cam = (cam_data *) private;
if (cam) {
cam->vf_start_sdc = prpvf_start;
cam->vf_stop_sdc = prpvf_stop;
cam->vf_enable_csi = prp_vf_enable_csi;
cam->vf_disable_csi = prp_vf_disable_csi;
cam->overlay_active = false;
}
return 0;
}
EXPORT_SYMBOL(prp_vf_sdc_select_bg);
/*!
* function to de-select PRP-VF as the working path
*
* @param private cam_data * mxc v4l2 main structure
*
* @return status
*/
int prp_vf_sdc_deselect_bg(void *private)
{
cam_data *cam = (cam_data *) private;
if (cam) {
cam->vf_start_sdc = NULL;
cam->vf_stop_sdc = NULL;
cam->vf_enable_csi = NULL;
cam->vf_disable_csi = NULL;
}
return 0;
}
EXPORT_SYMBOL(prp_vf_sdc_deselect_bg);
/*!
* Init viewfinder task.
*
* @return Error code indicating success or failure
*/
__init int prp_vf_sdc_init_bg(void)
{
return 0;
}
/*!
* Deinit viewfinder task.
*
* @return Error code indicating success or failure
*/
void __exit prp_vf_sdc_exit_bg(void)
{
}
module_init(prp_vf_sdc_init_bg);
module_exit(prp_vf_sdc_exit_bg);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,268 @@
/*
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_still.c
*
* @brief IPU Use case for still image capture
*
* @ingroup IPU
*/
#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/sched.h>
#include <linux/ipu.h>
#include "mxc_v4l2_capture.h"
#include "ipu_prp_sw.h"
static int callback_eof_flag;
#ifndef CONFIG_MXC_IPU_V1
static int buffer_num;
#endif
#ifdef CONFIG_MXC_IPU_V1
static int callback_flag;
/*
* Function definitions
*/
/*!
* CSI EOF callback function.
*
* @param irq int irq line
* @param dev_id void * device id
*
* @return status IRQ_HANDLED for handled
*/
static irqreturn_t prp_csi_eof_callback(int irq, void *dev_id)
{
cam_data *cam = devid;
ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER,
callback_flag%2 ? 1 : 0);
if (callback_flag == 0)
ipu_enable_channel(cam->ipu, CSI_MEM);
callback_flag++;
return IRQ_HANDLED;
}
#endif
/*!
* CSI callback function.
*
* @param irq int irq line
* @param dev_id void * device id
*
* @return status IRQ_HANDLED for handled
*/
static irqreturn_t prp_still_callback(int irq, void *dev_id)
{
cam_data *cam = (cam_data *) dev_id;
callback_eof_flag++;
if (callback_eof_flag < 5) {
#ifndef CONFIG_MXC_IPU_V1
buffer_num = (buffer_num == 0) ? 1 : 0;
ipu_select_buffer(cam->ipu, CSI_MEM,
IPU_OUTPUT_BUFFER, buffer_num);
#endif
} else {
cam->still_counter++;
wake_up_interruptible(&cam->still_queue);
}
return IRQ_HANDLED;
}
/*!
* start csi->mem task
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_still_start(void *private)
{
cam_data *cam = (cam_data *) private;
u32 pixel_fmt;
int err;
ipu_channel_params_t params;
if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
pixel_fmt = IPU_PIX_FMT_YUV420P;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)
pixel_fmt = IPU_PIX_FMT_NV12;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
pixel_fmt = IPU_PIX_FMT_YUV422P;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
pixel_fmt = IPU_PIX_FMT_UYVY;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pixel_fmt = IPU_PIX_FMT_YUYV;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
pixel_fmt = IPU_PIX_FMT_BGR24;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
pixel_fmt = IPU_PIX_FMT_RGB24;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
pixel_fmt = IPU_PIX_FMT_RGB565;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
pixel_fmt = IPU_PIX_FMT_BGR32;
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
pixel_fmt = IPU_PIX_FMT_RGB32;
else {
printk(KERN_ERR "format not supported\n");
return -EINVAL;
}
memset(&params, 0, sizeof(params));
err = ipu_init_channel(cam->ipu, CSI_MEM, &params);
if (err != 0)
return err;
err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER,
pixel_fmt, cam->v2f.fmt.pix.width,
cam->v2f.fmt.pix.height,
cam->v2f.fmt.pix.width, IPU_ROTATE_NONE,
cam->still_buf[0], cam->still_buf[1], 0,
0, 0);
if (err != 0)
return err;
#ifdef CONFIG_MXC_IPU_V1
ipu_clear_irq(IPU_IRQ_SENSOR_OUT_EOF);
err = ipu_request_irq(IPU_IRQ_SENSOR_OUT_EOF, prp_still_callback,
0, "Mxc Camera", cam);
if (err != 0) {
printk(KERN_ERR "Error registering irq.\n");
return err;
}
callback_flag = 0;
callback_eof_flag = 0;
ipu_clear_irq(IPU_IRQ_SENSOR_EOF);
err = ipu_request_irq(IPU_IRQ_SENSOR_EOF, prp_csi_eof_callback,
0, "Mxc Camera", cam);
if (err != 0) {
printk(KERN_ERR "Error IPU_IRQ_SENSOR_EOF\n");
return err;
}
#else
callback_eof_flag = 0;
buffer_num = 0;
ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF);
err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF,
prp_still_callback,
0, "Mxc Camera", cam);
if (err != 0) {
printk(KERN_ERR "Error registering irq.\n");
return err;
}
ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0);
ipu_enable_channel(cam->ipu, CSI_MEM);
ipu_enable_csi(cam->ipu, cam->csi);
#endif
return err;
}
/*!
* stop csi->mem encoder task
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
static int prp_still_stop(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
#ifdef CONFIG_MXC_IPU_V1
ipu_free_irq(IPU_IRQ_SENSOR_EOF, NULL);
ipu_free_irq(IPU_IRQ_SENSOR_OUT_EOF, cam);
#else
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam);
#endif
ipu_disable_csi(cam->ipu, cam->csi);
ipu_disable_channel(cam->ipu, CSI_MEM, true);
ipu_uninit_channel(cam->ipu, CSI_MEM);
return err;
}
/*!
* function to select CSI_MEM as the working path
*
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
int prp_still_select(void *private)
{
cam_data *cam = (cam_data *) private;
if (cam) {
cam->csi_start = prp_still_start;
cam->csi_stop = prp_still_stop;
}
return 0;
}
EXPORT_SYMBOL(prp_still_select);
/*!
* function to de-select CSI_MEM as the working path
*
* @param private struct cam_data * mxc capture instance
*
* @return status
*/
int prp_still_deselect(void *private)
{
cam_data *cam = (cam_data *) private;
int err = 0;
err = prp_still_stop(cam);
if (cam) {
cam->csi_start = NULL;
cam->csi_stop = NULL;
}
return err;
}
EXPORT_SYMBOL(prp_still_deselect);
/*!
* Init the Encorder channels
*
* @return Error code indicating success or failure
*/
__init int prp_still_init(void)
{
return 0;
}
/*!
* Deinit the Encorder channels
*
*/
void __exit prp_still_exit(void)
{
}
module_init(prp_still_init);
module_exit(prp_still_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("IPU PRP STILL IMAGE Driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,262 @@
/*
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @defgroup MXC_V4L2_CAPTURE MXC V4L2 Video Capture Driver
*/
/*!
* @file mxc_v4l2_capture.h
*
* @brief mxc V4L2 capture device API Header file
*
* It include all the defines for frame operations, also three structure defines
* use case ops structure, common v4l2 driver structure and frame structure.
*
* @ingroup MXC_V4L2_CAPTURE
*/
#ifndef __MXC_V4L2_CAPTURE_H__
#define __MXC_V4L2_CAPTURE_H__
#include <linux/uaccess.h>
#include <linux/list.h>
#include <linux/mxc_v4l2.h>
#include <linux/completion.h>
#include <linux/dmaengine.h>
#include <linux/pxp_dma.h>
#include <linux/ipu-v3.h>
#include <linux/platform_data/dma-imx.h>
#include <media/v4l2-dev.h>
#include "v4l2-int-device.h"
#define FRAME_NUM 10
#define MXC_SENSOR_NUM 2
enum imx_v4l2_devtype {
IMX5_V4L2,
IMX6_V4L2,
};
/*!
* v4l2 frame structure.
*/
struct mxc_v4l_frame {
u32 paddress;
void *vaddress;
int count;
int width;
int height;
struct v4l2_buffer buffer;
struct list_head queue;
int index;
union {
int ipu_buf_num;
int csi_buf_num;
};
};
/* Only for old version. Will go away soon. */
typedef struct {
u8 clk_mode;
u8 ext_vsync;
u8 Vsync_pol;
u8 Hsync_pol;
u8 pixclk_pol;
u8 data_pol;
u8 data_width;
u8 pack_tight;
u8 force_eof;
u8 data_en_pol;
u16 width;
u16 height;
u32 pixel_fmt;
u32 mclk;
u16 active_width;
u16 active_height;
} sensor_interface;
/* Sensor control function */
/* Only for old version. Will go away soon. */
struct camera_sensor {
void (*set_color) (int bright, int saturation, int red, int green,
int blue);
void (*get_color) (int *bright, int *saturation, int *red, int *green,
int *blue);
void (*set_ae_mode) (int ae_mode);
void (*get_ae_mode) (int *ae_mode);
sensor_interface *(*config) (int *frame_rate, int high_quality);
sensor_interface *(*reset) (void);
void (*get_std) (v4l2_std_id *std);
void (*set_std) (v4l2_std_id std);
unsigned int csi;
};
/*!
* common v4l2 driver structure.
*/
typedef struct _cam_data {
struct video_device *video_dev;
int device_type;
/* semaphore guard against SMP multithreading */
struct semaphore busy_lock;
int open_count;
/* params lock for this camera */
struct semaphore param_lock;
/* Encoder */
struct list_head ready_q;
struct list_head done_q;
struct list_head working_q;
int ping_pong_csi;
spinlock_t queue_int_lock;
spinlock_t dqueue_int_lock;
struct mxc_v4l_frame frame[FRAME_NUM];
struct mxc_v4l_frame dummy_frame;
wait_queue_head_t enc_queue;
int enc_counter;
dma_addr_t rot_enc_bufs[2];
void *rot_enc_bufs_vaddr[2];
int rot_enc_buf_size[2];
enum v4l2_buf_type type;
/* still image capture */
wait_queue_head_t still_queue;
int still_counter;
dma_addr_t still_buf[2];
void *still_buf_vaddr;
/* overlay */
struct v4l2_window win;
struct v4l2_framebuffer v4l2_fb;
dma_addr_t vf_bufs[2];
void *vf_bufs_vaddr[2];
int vf_bufs_size[2];
dma_addr_t rot_vf_bufs[2];
void *rot_vf_bufs_vaddr[2];
int rot_vf_buf_size[2];
bool overlay_active;
int output;
struct fb_info *overlay_fb;
int fb_origin_std;
struct work_struct csi_work_struct;
/* v4l2 format */
struct v4l2_format v2f;
struct v4l2_format input_fmt; /* camera in */
bool bswapenable;
int rotation; /* for IPUv1 and IPUv3, this means encoder rotation */
int vf_rotation; /* viewfinder rotation only for IPUv1 and IPUv3 */
struct v4l2_mxc_offset offset;
/* V4l2 control bit */
int bright;
int hue;
int contrast;
int saturation;
int red;
int green;
int blue;
int ae_mode;
/* standard */
struct v4l2_streamparm streamparm;
struct v4l2_standard standard;
bool standard_autodetect;
/* crop */
struct v4l2_rect crop_bounds;
struct v4l2_rect crop_defrect;
struct v4l2_rect crop_current;
int (*enc_update_eba) (void *private, dma_addr_t eba);
int (*enc_enable) (void *private);
int (*enc_disable) (void *private);
int (*enc_enable_csi) (void *private);
int (*enc_disable_csi) (void *private);
void (*enc_callback) (u32 mask, void *dev);
int (*vf_start_adc) (void *private);
int (*vf_stop_adc) (void *private);
int (*vf_start_sdc) (void *private);
int (*vf_stop_sdc) (void *private);
int (*vf_enable_csi) (void *private);
int (*vf_disable_csi) (void *private);
int (*csi_start) (void *private);
int (*csi_stop) (void *private);
/* misc status flag */
bool overlay_on;
bool capture_on;
int overlay_pid;
int capture_pid;
bool low_power;
wait_queue_head_t power_queue;
unsigned int ipu_id;
unsigned int csi;
u8 mclk_source;
bool mclk_on[2]; /* two mclk sources at most now */
int current_input;
int local_buf_num;
/* camera sensor interface */
struct camera_sensor *cam_sensor; /* old version */
struct v4l2_int_device *all_sensors[MXC_SENSOR_NUM];
struct v4l2_int_device *sensor;
struct v4l2_int_device *self;
int sensor_index;
void *ipu;
void *csi_soc;
enum imx_v4l2_devtype devtype;
/* v4l2 buf elements related to PxP DMA */
struct completion pxp_tx_cmpl;
struct pxp_channel *pxp_chan;
struct pxp_config_data pxp_conf;
struct dma_async_tx_descriptor *txd;
dma_cookie_t cookie;
struct scatterlist sg[2];
} cam_data;
struct sensor_data {
const struct ov5642_platform_data *platform_data;
struct v4l2_int_device *v4l2_int_device;
struct i2c_client *i2c_client;
struct v4l2_pix_format pix;
struct v4l2_captureparm streamcap;
bool on;
/* control settings */
int brightness;
int hue;
int contrast;
int saturation;
int red;
int green;
int blue;
int ae_mode;
u32 mclk;
u8 mclk_source;
struct clk *sensor_clk;
int csi;
void (*io_init)(void);
};
void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi);
#endif /* __MXC_V4L2_CAPTURE_H__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,165 @@
/*
* drivers/media/video/v4l2-int-device.c
*
* V4L2 internal ioctl interface.
*
* Copyright 2005-2014 Freescale Semiconductor, Inc.
* Copyright (C) 2007 Nokia Corporation.
*
* Contact: Sakari Ailus <sakari.ailus@nokia.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/sort.h>
#include <linux/string.h>
#include <linux/module.h>
#include "v4l2-int-device.h"
static DEFINE_MUTEX(mutex);
static LIST_HEAD(int_list);
void v4l2_int_device_try_attach_all(void)
{
struct v4l2_int_device *m, *s;
list_for_each_entry(m, &int_list, head) {
if (m->type != v4l2_int_type_master)
continue;
list_for_each_entry(s, &int_list, head) {
if (s->type != v4l2_int_type_slave)
continue;
/* Slave is connected? */
if (s->u.slave->master)
continue;
/* Slave wants to attach to master? */
if (s->u.slave->attach_to[0] != 0
&& strncmp(m->name, s->u.slave->attach_to,
V4L2NAMESIZE))
continue;
if (!try_module_get(m->module))
continue;
s->u.slave->master = m;
if (m->u.master->attach(s)) {
s->u.slave->master = NULL;
module_put(m->module);
continue;
}
}
}
}
EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all);
static int ioctl_sort_cmp(const void *a, const void *b)
{
const struct v4l2_int_ioctl_desc *d1 = a, *d2 = b;
if (d1->num > d2->num)
return 1;
if (d1->num < d2->num)
return -1;
return 0;
}
int v4l2_int_device_register(struct v4l2_int_device *d)
{
if (d->type == v4l2_int_type_slave)
sort(d->u.slave->ioctls, d->u.slave->num_ioctls,
sizeof(struct v4l2_int_ioctl_desc),
&ioctl_sort_cmp, NULL);
mutex_lock(&mutex);
list_add(&d->head, &int_list);
v4l2_int_device_try_attach_all();
mutex_unlock(&mutex);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_int_device_register);
void v4l2_int_device_unregister(struct v4l2_int_device *d)
{
mutex_lock(&mutex);
list_del(&d->head);
if (d->type == v4l2_int_type_slave
&& d->u.slave->master != NULL) {
d->u.slave->master->u.master->detach(d);
module_put(d->u.slave->master->module);
d->u.slave->master = NULL;
}
mutex_unlock(&mutex);
}
EXPORT_SYMBOL_GPL(v4l2_int_device_unregister);
/* Adapted from search_extable in extable.c. */
static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd,
v4l2_int_ioctl_func *no_such_ioctl)
{
const struct v4l2_int_ioctl_desc *first = slave->ioctls;
const struct v4l2_int_ioctl_desc *last =
first + slave->num_ioctls - 1;
while (first <= last) {
const struct v4l2_int_ioctl_desc *mid;
mid = (last - first) / 2 + first;
if (mid->num < cmd)
first = mid + 1;
else if (mid->num > cmd)
last = mid - 1;
else
return mid->func;
}
return no_such_ioctl;
}
static int no_such_ioctl_0(struct v4l2_int_device *d)
{
return -ENOIOCTLCMD;
}
int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd)
{
return ((v4l2_int_ioctl_func_0 *)
find_ioctl(d->u.slave, cmd,
(v4l2_int_ioctl_func *)no_such_ioctl_0))(d);
}
EXPORT_SYMBOL_GPL(v4l2_int_ioctl_0);
static int no_such_ioctl_1(struct v4l2_int_device *d, void *arg)
{
return -ENOIOCTLCMD;
}
int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg)
{
return ((v4l2_int_ioctl_func_1 *)
find_ioctl(d->u.slave, cmd,
(v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg);
}
EXPORT_SYMBOL_GPL(v4l2_int_ioctl_1);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,309 @@
/*
* include/media/v4l2-int-device.h
*
* V4L2 internal ioctl interface.
*
* Copyright 2005-2014 Freescale Semiconductor, Inc.
* Copyright (C) 2007 Nokia Corporation.
*
* Contact: Sakari Ailus <sakari.ailus@nokia.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef V4L2_INT_DEVICE_H
#define V4L2_INT_DEVICE_H
#include <media/v4l2-common.h>
#define V4L2NAMESIZE 32
/*
*
* The internal V4L2 device interface core.
*
*/
enum v4l2_int_type {
v4l2_int_type_master = 1,
v4l2_int_type_slave
};
struct module;
struct v4l2_int_device;
struct v4l2_int_master {
int (*attach)(struct v4l2_int_device *slave);
void (*detach)(struct v4l2_int_device *slave);
};
typedef int (v4l2_int_ioctl_func)(struct v4l2_int_device *);
typedef int (v4l2_int_ioctl_func_0)(struct v4l2_int_device *);
typedef int (v4l2_int_ioctl_func_1)(struct v4l2_int_device *, void *);
struct v4l2_int_ioctl_desc {
int num;
v4l2_int_ioctl_func *func;
};
struct v4l2_int_slave {
/* Don't touch master. */
struct v4l2_int_device *master;
char attach_to[V4L2NAMESIZE];
int num_ioctls;
struct v4l2_int_ioctl_desc *ioctls;
};
struct v4l2_int_device {
/* Don't touch head. */
struct list_head head;
struct module *module;
char name[V4L2NAMESIZE];
enum v4l2_int_type type;
union {
struct v4l2_int_master *master;
struct v4l2_int_slave *slave;
} u;
void *priv;
};
void v4l2_int_device_try_attach_all(void);
int v4l2_int_device_register(struct v4l2_int_device *d);
void v4l2_int_device_unregister(struct v4l2_int_device *d);
int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd);
int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg);
/*
*
* Types and definitions for IOCTL commands.
*
*/
enum v4l2_power {
V4L2_POWER_OFF = 0,
V4L2_POWER_ON,
V4L2_POWER_STANDBY,
};
/* Slave interface type. */
enum v4l2_if_type {
/*
* Parallel 8-, 10- or 12-bit interface, used by for example
* on certain image sensors.
*/
V4L2_IF_TYPE_BT656,
};
enum v4l2_if_type_bt656_mode {
/*
* Modes without Bt synchronisation codes. Separate
* synchronisation signal lines are used.
*/
V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT,
V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT,
V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT,
/*
* Use Bt synchronisation codes. The vertical and horizontal
* synchronisation is done based on synchronisation codes.
*/
V4L2_IF_TYPE_BT656_MODE_BT_8BIT,
V4L2_IF_TYPE_BT656_MODE_BT_10BIT,
};
struct v4l2_if_type_bt656 {
/*
* 0: Frame begins when vsync is high.
* 1: Frame begins when vsync changes from low to high.
*/
unsigned frame_start_on_rising_vs:1;
/* Use Bt synchronisation codes for sync correction. */
unsigned bt_sync_correct:1;
/* Swap every two adjacent image data elements. */
unsigned swap:1;
/* Inverted latch clock polarity from slave. */
unsigned latch_clk_inv:1;
/* Hs polarity. 0 is active high, 1 active low. */
unsigned nobt_hs_inv:1;
/* Vs polarity. 0 is active high, 1 active low. */
unsigned nobt_vs_inv:1;
enum v4l2_if_type_bt656_mode mode;
/* Minimum accepted bus clock for slave (in Hz). */
u32 clock_min;
/* Maximum accepted bus clock for slave. */
u32 clock_max;
/*
* Current wish of the slave. May only change in response to
* ioctls that affect image capture.
*/
u32 clock_curr;
};
struct v4l2_ifparm {
enum v4l2_if_type if_type;
union {
struct v4l2_if_type_bt656 bt656;
} u;
};
/* IOCTL command numbers. */
enum v4l2_int_ioctl_num {
/*
*
* "Proper" V4L ioctls, as in struct video_device.
*
*/
vidioc_int_enum_fmt_cap_num = 1,
vidioc_int_g_fmt_cap_num,
vidioc_int_s_fmt_cap_num,
vidioc_int_try_fmt_cap_num,
vidioc_int_queryctrl_num,
vidioc_int_g_ctrl_num,
vidioc_int_s_ctrl_num,
vidioc_int_cropcap_num,
vidioc_int_g_crop_num,
vidioc_int_s_crop_num,
vidioc_int_g_parm_num,
vidioc_int_s_parm_num,
vidioc_int_querystd_num,
vidioc_int_s_std_num,
vidioc_int_s_video_routing_num,
/*
*
* Strictly internal ioctls.
*
*/
/* Initialise the device when slave attaches to the master. */
vidioc_int_dev_init_num = 1000,
/* Delinitialise the device at slave detach. */
vidioc_int_dev_exit_num,
/* Set device power state. */
vidioc_int_s_power_num,
/*
* Get slave private data, e.g. platform-specific slave
* configuration used by the master.
*/
vidioc_int_g_priv_num,
/* Get slave interface parameters. */
vidioc_int_g_ifparm_num,
/* Does the slave need to be reset after VIDIOC_DQBUF? */
vidioc_int_g_needs_reset_num,
vidioc_int_enum_framesizes_num,
vidioc_int_enum_frameintervals_num,
/*
*
* VIDIOC_INT_* ioctls.
*
*/
/* VIDIOC_INT_RESET */
vidioc_int_reset_num,
/* VIDIOC_INT_INIT */
vidioc_int_init_num,
/* VIDIOC_DBG_G_CHIP_IDENT */
vidioc_int_g_chip_ident_num,
/*
*
* Start of private ioctls.
*
*/
vidioc_int_priv_start_num = 2000,
};
/*
*
* IOCTL wrapper functions for better type checking.
*
*/
#define V4L2_INT_WRAPPER_0(name) \
static inline int vidioc_int_##name(struct v4l2_int_device *d) \
{ \
return v4l2_int_ioctl_0(d, vidioc_int_##name##_num); \
} \
\
static inline struct v4l2_int_ioctl_desc \
vidioc_int_##name##_cb(int (*func) \
(struct v4l2_int_device *)) \
{ \
struct v4l2_int_ioctl_desc desc; \
\
desc.num = vidioc_int_##name##_num; \
desc.func = (v4l2_int_ioctl_func *)func; \
\
return desc; \
}
#define V4L2_INT_WRAPPER_1(name, arg_type, asterisk) \
static inline int vidioc_int_##name(struct v4l2_int_device *d, \
arg_type asterisk arg) \
{ \
return v4l2_int_ioctl_1(d, vidioc_int_##name##_num, \
(void *)(unsigned long)arg); \
} \
\
static inline struct v4l2_int_ioctl_desc \
vidioc_int_##name##_cb(int (*func) \
(struct v4l2_int_device *, \
arg_type asterisk)) \
{ \
struct v4l2_int_ioctl_desc desc; \
\
desc.num = vidioc_int_##name##_num; \
desc.func = (v4l2_int_ioctl_func *)func; \
\
return desc; \
}
V4L2_INT_WRAPPER_1(enum_fmt_cap, struct v4l2_fmtdesc, *);
V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *);
V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *);
V4L2_INT_WRAPPER_1(try_fmt_cap, struct v4l2_format, *);
V4L2_INT_WRAPPER_1(queryctrl, struct v4l2_queryctrl, *);
V4L2_INT_WRAPPER_1(g_ctrl, struct v4l2_control, *);
V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *);
V4L2_INT_WRAPPER_1(cropcap, struct v4l2_cropcap, *);
V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *);
V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *);
V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *);
V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *);
V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *);
V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *);
V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *);
V4L2_INT_WRAPPER_0(dev_init);
V4L2_INT_WRAPPER_0(dev_exit);
V4L2_INT_WRAPPER_1(s_power, enum v4l2_power, /*dummy arg*/);
V4L2_INT_WRAPPER_1(g_priv, void, *);
V4L2_INT_WRAPPER_1(g_ifparm, struct v4l2_ifparm, *);
V4L2_INT_WRAPPER_1(g_needs_reset, void, *);
V4L2_INT_WRAPPER_1(enum_framesizes, struct v4l2_frmsizeenum, *);
V4L2_INT_WRAPPER_1(enum_frameintervals, struct v4l2_frmivalenum, *);
V4L2_INT_WRAPPER_0(reset);
V4L2_INT_WRAPPER_0(init);
V4L2_INT_WRAPPER_1(g_chip_ident, int, *);
#endif

View File

@ -0,0 +1,38 @@
if VIDEO_MXC_CAPTURE
config VIDEO_MXC_CSI_CAMERA
tristate "CSI camera support"
depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
---help---
This is the video4linux2 capture driver based on CSI module.
config MXC_CAMERA_OV5640
tristate "OmniVision ov5640 camera support"
depends on VIDEO_MXC_CAPTURE && I2C
---help---
If you plan to use the ov5640 Camera with your MXC system, say Y here.
config MXC_VADC
tristate "mxc VADC support"
depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
---help---
If you plan to use the VADC with your MXC system, say Y here.
config MXC_MIPI_CSI
tristate "mxc mipi csi driver"
depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
---help---
This is a V4L2 driver for i.MX7D SoC MIPI-CSI2 receiver devices.
config MXC_CAMERA_OV5640_MIPI
tristate "OmniVision ov5640 camera support using mipi"
depends on MXC_MIPI_CSI && I2C
---help---
If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.
config MXC_CAMERA_OV5647_MIPI
tristate "OmniVision ov5647 camera support using mipi"
depends on MXC_MIPI_CSI && I2C
---help---
If you plan to use the ov5647 Camera with mipi interface in your MXC system, say Y here.
endif

View File

@ -0,0 +1,14 @@
#Makefile for mxc csi driver
obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += mx6s_capture.o
obj-$(CONFIG_MXC_VADC) += mxc_vadc.o
obj-$(CONFIG_MXC_MIPI_CSI) += mxc_mipi_csi.o
ov5640_camera-objs := ov5640.o
obj-$(CONFIG_MXC_CAMERA_OV5640) += ov5640_camera.o
ov5640_camera_mipi-objs := ov5640_mipi.o
obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi.o
ov5647_camera_mipi-objs := ov5647_mipi.o
obj-$(CONFIG_MXC_CAMERA_OV5647_MIPI) += ov5647_camera_mipi.o

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,822 @@
/*
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/media-bus-format.h>
#include <media/v4l2-ioctl.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include "mxc_vadc.h"
/* Resource names for the VADC driver. */
#define VAFE_REGS_ADDR_RES_NAME "vadc-vafe"
#define VDEC_REGS_ADDR_RES_NAME "vadc-vdec"
#define reg32_write(addr, val) __raw_writel(val, addr)
#define reg32_read(addr) __raw_readl(addr)
#define reg32setbit(addr, bitpos) \
reg32_write((addr), (reg32_read((addr)) | (1<<(bitpos))))
#define reg32clrbit(addr, bitpos) \
reg32_write((addr), (reg32_read((addr)) & (0xFFFFFFFF ^ (1<<(bitpos)))))
#define GPC_CNTR 0x00
#define IMX6SX_GPC_CNTR_VADC_ANALOG_OFF_MASK BIT(17)
#define IMX6SX_GPC_CNTR_VADC_POWER_DOWN_MASK BIT(18)
void __iomem *vafe_regbase;
void __iomem *vdec_regbase;
/* List of input video formats supported. The video formats is corresponding
* with v4l2 id in video_fmt
*/
enum video_fmt_idx {
VADC_NTSC = 0, /* Locked on (M) NTSC video signal. */
VADC_PAL, /* (B, G, H, I, N)PAL video signal. */
};
/* Number of video standards supported (including 'not locked' signal). */
#define VADC_STD_MAX (VADC_PAL + 1)
/* Video format structure. */
struct video_fmt{
v4l2_std_id v4l2_std; /* Video for linux ID. */
char name[16]; /* Name (e.g., "NTSC", "PAL", etc.) */
u16 raw_width; /* Raw width. */
u16 raw_height; /* Raw height. */
u16 active_width; /* Active width. */
u16 active_height; /* Active height. */
u16 framerates;
};
/*
* Maintains the information on the current state of the sensor.
*/
struct vadc_state {
struct v4l2_device v4l2_dev;
struct v4l2_subdev sd;
struct video_fmt *fmt;
struct clk *vadc_clk;
struct clk *csi_clk;
struct regmap *gpr;
void __iomem *gpc_reg;
u32 vadc_in;
u32 csi_id;
};
static int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std);
/* Description of video formats supported.
*
* PAL: raw=720x625, active=720x576.
* NTSC: raw=720x525, active=720x480.
*/
static struct video_fmt video_fmts[] = {
/* NTSC */
{
.v4l2_std = V4L2_STD_NTSC,
.name = "NTSC",
.raw_width = 720,
.raw_height = 525,
.active_width = 720,
.active_height = 480,
.framerates = 30,
},
/* (B, G, H, I, N) PAL */
{
.v4l2_std = V4L2_STD_PAL,
.name = "PAL",
.raw_width = 720,
.raw_height = 625,
.active_width = 720,
.active_height = 576,
.framerates = 25,
},
};
static void afe_voltage_clampingmode(void)
{
reg32_write(AFE_CLAMP, 0x07);
reg32_write(AFE_CLMPAMP, 0x60);
reg32_write(AFE_CLMPDAT, 0xF0);
}
static void afe_alwayson_clampingmode(void)
{
reg32_write(AFE_CLAMP, 0x15);
reg32_write(AFE_CLMPDAT, 0x08);
reg32_write(AFE_CLMPAMP, 0x00);
}
static void afe_init(void)
{
pr_debug("%s\n", __func__);
reg32_write(AFE_PDBUF, 0x1f);
reg32_write(AFE_PDADC, 0x0f);
reg32_write(AFE_PDSARH, 0x01);
reg32_write(AFE_PDSARL, 0xff);
reg32_write(AFE_PDADCRFH, 0x01);
reg32_write(AFE_PDADCRFL, 0xff);
reg32_write(AFE_ICTRL, 0x3a);
reg32_write(AFE_ICTLSTG, 0x1e);
reg32_write(AFE_RCTRLSTG, 0x1e);
reg32_write(AFE_INPBUF, 0x035);
reg32_write(AFE_INPFLT, 0x02);
reg32_write(AFE_ADCDGN, 0x40);
reg32_write(AFE_TSTSEL, 0x10);
reg32_write(AFE_ACCTST, 0x07);
reg32_write(AFE_BGREG, 0x08);
reg32_write(AFE_ADCGN, 0x09);
/* set current controlled clamping
* always on, low current */
reg32_write(AFE_CLAMP, 0x11);
reg32_write(AFE_CLMPAMP, 0x08);
}
static void vdec_mode_timing_init(int std)
{
if (std == V4L2_STD_NTSC) {
/* NTSC 720x480 */
reg32_write(VDEC_HACTS, 0x66);
reg32_write(VDEC_HACTE, 0x24);
reg32_write(VDEC_VACTS, 0x29);
reg32_write(VDEC_VACTE, 0x04);
/* set V Position */
reg32_write(VDEC_VRTPOS, 0x2);
} else if (std == V4L2_STD_PAL) {
/* PAL 720x576 */
reg32_write(VDEC_HACTS, 0x66);
reg32_write(VDEC_HACTE, 0x24);
reg32_write(VDEC_VACTS, 0x29);
reg32_write(VDEC_VACTE, 0x04);
/* set V Position */
reg32_write(VDEC_VRTPOS, 0x6);
} else
pr_debug("Error not support video mode\n");
/* set H Position */
reg32_write(VDEC_HZPOS, 0x60);
/* set H ignore start */
reg32_write(VDEC_HSIGS, 0xf8);
/* set H ignore end */
reg32_write(VDEC_HSIGE, 0x18);
}
/*
* vdec_init()
* Initialises the VDEC registers
* Returns: nothing
*/
static void vdec_init(struct vadc_state *vadc)
{
v4l2_std_id std;
pr_debug("%s\n", __func__);
/* Get work mode PAL or NTSC */
vadc_querystd(&vadc->sd, &std);
vdec_mode_timing_init(std);
/* vcr detect threshold high, automatic detections */
reg32_write(VDEC_VSCON2, 0);
reg32_write(VDEC_BASE + 0x110, 0x01);
/* set the noramp mode on the Hloop PLL. */
reg32_write(VDEC_BASE+(0x14*4), 0x10);
/* set the YC relative delay.*/
reg32_write(VDEC_YCDEL, 0x90);
/* setup the Hpll */
reg32_write(VDEC_BASE+(0x13*4), 0x13);
/* setup the 2d comb */
/* set the gain of the Hdetail output to 3
* set the notch alpha gain to 1 */
reg32_write(VDEC_CFC2, 0x34);
/* setup various 2d comb bits.*/
reg32_write(VDEC_BASE+(0x02*4), 0x01);
reg32_write(VDEC_BASE+(0x03*4), 0x18);
reg32_write(VDEC_BASE+(0x04*4), 0x34);
/* set the start of the burst gate */
reg32_write(VDEC_BRSTGT, 0x30);
/* set 1f motion gain */
reg32_write(VDEC_BASE+(0x0f*4), 0x20);
/* set the 1F chroma motion detector thresh
* for colour reverse detection */
reg32_write(VDEC_THSH1, 0x02);
reg32_write(VDEC_BASE+(0x4a*4), 0x20);
reg32_write(VDEC_BASE+(0x4b*4), 0x08);
reg32_write(VDEC_BASE+(0x4c*4), 0x08);
/* set the threshold for the narrow/wide adaptive chroma BW */
reg32_write(VDEC_BASE+(0x20*4), 0x20);
/* turn up the colour with the new colour gain reg */
/* hue: */
reg32_write(VDEC_HUE, 0x00);
/* cbgain: 22 B4 */
reg32_write(VDEC_CBGN, 0xb4);
/* cr gain 80 */
reg32_write(VDEC_CRGN, 0x80);
/* luma gain (contrast) */
reg32_write(VDEC_CNTR, 0x80);
/* setup the signed black level register, brightness */
reg32_write(VDEC_BRT, 0x00);
/* filter the standard detection
* enable the comb for the ntsc443 */
reg32_write(VDEC_STDDBG, 0x20);
/* setup chroma kill thresh for no chroma */
reg32_write(VDEC_CHBTH, 0x0);
/* set chroma loop to wider BW
* no set it to normal BW. i fixed the bw problem.*/
reg32_write(VDEC_YCDEL, 0x00);
/* set the compensation in the chroma loop for the Hloop
* set the ratio for the nonarithmetic 3d comb modes.*/
reg32_write(VDEC_BASE + (0x1d*4), 0x90);
/* set the threshold for the nonarithmetic mode for the 2d comb
* the higher the value the more Fc Fh offset
* we will tolerate before turning off the comb. */
reg32_write(VDEC_BASE + (0x33*4), 0xa0);
/* setup the bluescreen output colour */
reg32_write(VDEC_BASE + (0x3d*4), 35);
reg32_write(VDEC_BLSCRCR, 114);
reg32_write(VDEC_BLSCRCB, 212);
/* disable the active blanking */
reg32_write(VDEC_BASE + (0x15*4), 0x02);
/* setup the luma agc for automatic gain. */
reg32_write(VDEC_LMAGC2, 0x5e);
reg32_write(VDEC_LMAGC1, 0x81);
/* setup chroma agc */
reg32_write(VDEC_CHAGC2, 0xa0);
reg32_write(VDEC_CHAGC1, 0x01);
/* setup the MV thresh lower nibble
* setup the sync top cap, upper nibble */
reg32_write(VDEC_BASE + (0x3a*4), 0x80);
reg32_write(VDEC_SHPIMP, 0x00);
/* setup the vsync block */
reg32_write(VDEC_VSCON1, 0x87);
/* set the nosignal threshold
* set the vsync threshold */
reg32_write(VDEC_VSSGTH, 0x35);
/* set length for min hphase filter
* (or saturate limit if saturate is chosen) */
reg32_write(VDEC_BASE + (0x45*4), 0x40);
/* enable the internal resampler,
* select min filter not saturate for
* hphase noise filter for vcr detect.
* enable vcr pause mode different field lengths */
reg32_write(VDEC_BASE + (0x46*4), 0x90);
/* disable VCR detection, lock to the Hsync rather than the Vsync */
reg32_write(VDEC_VSCON2, 0x04);
/* set tiplevel goal for dc clamp. */
reg32_write(VDEC_BASE + (0x3c*4), 0xB0);
/* override SECAM detection and force SECAM off */
reg32_write(VDEC_BASE + (0x2f*4), 0x20);
/* Set r3d_hardblend in 3D control2 reg */
reg32_write(VDEC_BASE + (0x0c*4), 0x04);
}
/* set Input selector & input pull-downs */
static void vadc_s_routing(int vadc_in)
{
switch (vadc_in) {
case 0:
reg32_write(AFE_INPFLT, 0x02);
reg32_write(AFE_OFFDRV, 0x00);
reg32_write(AFE_INPCONFIG, 0x1e);
break;
case 1:
reg32_write(AFE_INPFLT, 0x02);
reg32_write(AFE_OFFDRV, 0x00);
reg32_write(AFE_INPCONFIG, 0x2d);
break;
case 2:
reg32_write(AFE_INPFLT, 0x02);
reg32_write(AFE_OFFDRV, 0x00);
reg32_write(AFE_INPCONFIG, 0x4b);
break;
case 3:
reg32_write(AFE_INPFLT, 0x02);
reg32_write(AFE_OFFDRV, 0x00);
reg32_write(AFE_INPCONFIG, 0x87);
break;
default:
pr_debug("error video input %d\n", vadc_in);
}
}
static void vadc_power_up(struct vadc_state *state)
{
/* Power on vadc analog */
reg32clrbit(state->gpc_reg + GPC_CNTR, 17);
/* Power down vadc ext power */
reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
/* software reset afe */
regmap_update_bits(state->gpr, IOMUXC_GPR1,
IMX6SX_GPR1_VADC_SW_RST_MASK,
IMX6SX_GPR1_VADC_SW_RST_RESET);
msleep(10);
/* clock config for vadc */
reg32_write(VDEC_BASE + 0x320, 0xe3);
reg32_write(VDEC_BASE + 0x324, 0x38);
reg32_write(VDEC_BASE + 0x328, 0x8e);
reg32_write(VDEC_BASE + 0x32c, 0x23);
/* Release reset bit */
regmap_update_bits(state->gpr, IOMUXC_GPR1,
IMX6SX_GPR1_VADC_SW_RST_MASK,
IMX6SX_GPR1_VADC_SW_RST_RELEASE);
/* Power on vadc ext power */
reg32setbit(state->gpc_reg + GPC_CNTR, 18);
}
static void vadc_power_down(struct vadc_state *state)
{
/* Power down vadc analog */
reg32setbit(state->gpc_reg + GPC_CNTR, 17);
/* Power down vadc ext power */
reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
}
static void vadc_init(struct vadc_state *vadc)
{
pr_debug("%s\n", __func__);
vadc_power_up(vadc);
afe_init();
/* select Video Input 0-3 */
vadc_s_routing(vadc->vadc_in);
afe_voltage_clampingmode();
vdec_init(vadc);
/*
* current control loop will move sinewave input off below
* the bottom of the signal range visible
* when the testbus is viewed as magnitude,
* so have to break before this point while capturing ENOB data:
*/
afe_alwayson_clampingmode();
}
static inline struct vadc_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct vadc_state, sd);
}
static int vadc_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
{
struct vadc_state *state = to_state(sd);
*std = state->fmt->v4l2_std;
return 0;
}
/*!
* Return attributes of current video standard.
* Since this device autodetects the current standard, this function also
* sets the values that need to be changed if the standard changes.
* There is no set std equivalent function.
*
* @return None.
*/
static int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
struct vadc_state *state = to_state(sd);
int mod;
int idx;
int i;
/* Read auto mode detected result */
printk(KERN_INFO"wait vadc auto detect video mode....\n");
for (i = 0; i < 10; i++) {
msleep(200);
mod = reg32_read(VDEC_VIDMOD);
/* Check video signal states */
if ((mod & VDEC_VIDMOD_SIGNAL_MASK)
== VDEC_VIDMOD_SIGNAL_DETECT)
break;
}
if (i == 10)
printk(KERN_INFO"Timeout detect video signal mod=0x%x\n", mod);
if ((mod & VDEC_VIDMOD_PAL_MASK) || (mod & VDEC_VIDMOD_M625_MASK))
idx = VADC_PAL;
else
idx = VADC_NTSC;
*std = video_fmts[idx].v4l2_std;
state->fmt = &video_fmts[idx];
printk(KERN_INFO"video mode %s\n", video_fmts[idx].name);
return 0;
}
static int vadc_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
/* support only one format */
if (code->pad || code->index >= 1)
return -EINVAL;
code->code = MEDIA_BUS_FMT_AYUV8_1X32;
return 0;
}
static int vadc_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
struct vadc_state *state = to_state(sd);
struct v4l2_mbus_framefmt *fmt = &format->format;
if (format->pad)
return -EINVAL;
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
fmt->field = V4L2_FIELD_INTERLACED;
fmt->width = 720;
fmt->height = state->fmt->v4l2_std & V4L2_STD_NTSC ? 480 : 576;
return 0;
}
static int vadc_enum_framesizes(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vadc_state *state = to_state(sd);
if (fse->index >= 1)
return -EINVAL;
fse->min_width = state->fmt->active_width;
fse->max_width = state->fmt->active_width;
fse->min_height = state->fmt->active_height;
fse->max_height = state->fmt->active_height;
return 0;
}
static int vadc_enum_frameintervals(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_interval_enum *fie)
{
struct vadc_state *state = to_state(sd);
if (fie->index < 0 || fie->index >= 1)
return -EINVAL;
fie->interval.numerator = 1;
fie->interval.denominator = state->fmt->framerates;
return 0;
}
static int vadc_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
{
struct vadc_state *state = to_state(sd);
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (parms->parm.capture.timeperframe.denominator
!= state->fmt->framerates)
parms->parm.capture.timeperframe.denominator
= state->fmt->framerates;
return 0;
}
static const struct v4l2_subdev_video_ops vadc_video_ops = {
.querystd = vadc_querystd,
.s_parm = vadc_s_parm,
.g_std = vadc_g_std,
};
static const struct v4l2_subdev_pad_ops vadc_pad_ops = {
.get_fmt = vadc_get_fmt,
.enum_mbus_code = vadc_enum_mbus_code,
.enum_frame_size = vadc_enum_framesizes,
.enum_frame_interval = vadc_enum_frameintervals,
};
static const struct v4l2_subdev_ops vadc_ops = {
.video = &vadc_video_ops,
.pad = &vadc_pad_ops,
};
static const struct of_device_id fsl_vadc_dt_ids[] = {
{ .compatible = "fsl,imx6sx-vadc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_vadc_dt_ids);
static int vadc_of_init(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *gpc_np;
struct vadc_state *state = platform_get_drvdata(pdev);
int csi_id;
int ret;
/* Get csi_id to setting vadc to csi mux in gpr */
ret = of_property_read_u32(np, "csi_id", &csi_id);
if (ret) {
dev_err(&pdev->dev, "failed to read of property csi_id\n");
return ret;
}
state->csi_id = csi_id;
/* remap GPR register */
state->gpr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"gpr");
if (IS_ERR(state->gpr)) {
dev_dbg(&pdev->dev, "can not get gpr\n");
return -ENOMEM;
}
/* Configuration vadc-to-csi 0 or 1 */
if (csi_id) {
regmap_update_bits(state->gpr, IOMUXC_GPR5,
IMX6SX_GPR5_CSI2_MUX_CTRL_MASK,
IMX6SX_GPR5_CSI2_MUX_CTRL_CVD);
} else {
regmap_update_bits(state->gpr, IOMUXC_GPR5,
IMX6SX_GPR5_CSI1_MUX_CTRL_MASK,
IMX6SX_GPR5_CSI1_MUX_CTRL_CVD);
}
/* Get default vadc_in number */
ret = of_property_read_u32(np, "vadc_in", &state->vadc_in);
if (ret) {
dev_err(&pdev->dev, "failed to read of property vadc_in\n");
return ret;
}
/* map GPC register */
gpc_np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc");
state->gpc_reg = of_iomap(gpc_np, 0);
if (!state->gpc_reg) {
dev_err(&pdev->dev, "ioremap failed with gpc base\n");
goto error;
}
return ret;
error:
iounmap(state->gpc_reg);
return ret;
}
static void vadc_v4l2_subdev_init(struct v4l2_subdev *sd,
struct platform_device *pdev,
const struct v4l2_subdev_ops *ops)
{
struct vadc_state *state = platform_get_drvdata(pdev);
int ret = 0;
v4l2_subdev_init(sd, ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
sd->owner = pdev->dev.driver->owner;
sd->dev = &pdev->dev;
/* initialize name */
snprintf(sd->name, sizeof(sd->name), "%s",
pdev->dev.driver->name);
v4l2_set_subdevdata(sd, state);
ret = v4l2_async_register_subdev(sd);
if (ret < 0)
dev_err(&pdev->dev, "%s--Async register faialed, ret=%d\n", __func__, ret);
}
static int vadc_probe(struct platform_device *pdev)
{
struct vadc_state *state;
struct v4l2_subdev *sd;
struct resource *res;
int ret = 0;
state = devm_kzalloc(&pdev->dev, sizeof(struct vadc_state), GFP_KERNEL);
if (!state) {
dev_err(&pdev->dev, "Cannot allocate device data\n");
return -ENOMEM;
}
/* Set initial values for the sensor struct. */
state->fmt = &video_fmts[VADC_NTSC];
sd = &state->sd;
/* map vafe address */
res = platform_get_resource_byname(pdev,
IORESOURCE_MEM, VAFE_REGS_ADDR_RES_NAME);
if (!res) {
dev_err(&pdev->dev, "No vafe base address found.\n");
return -ENOMEM;
}
vafe_regbase = devm_ioremap_resource(&pdev->dev, res);
if (!vafe_regbase) {
dev_err(&pdev->dev, "ioremap failed with vafe base\n");
return -ENOMEM;
}
/* map vdec address */
res = platform_get_resource_byname(pdev,
IORESOURCE_MEM, VDEC_REGS_ADDR_RES_NAME);
if (!res) {
dev_err(&pdev->dev, "No vdec base address found.\n");
return -ENODEV;
}
vdec_regbase = devm_ioremap_resource(&pdev->dev, res);
if (!vdec_regbase) {
dev_err(&pdev->dev, "ioremap failed with vdec base\n");
return -ENOMEM;
}
/* Get clock */
state->vadc_clk = devm_clk_get(&pdev->dev, "vadc");
if (IS_ERR(state->vadc_clk)) {
ret = PTR_ERR(state->vadc_clk);
return ret;
}
state->csi_clk = devm_clk_get(&pdev->dev, "csi");
if (IS_ERR(state->csi_clk)) {
ret = PTR_ERR(state->csi_clk);
return ret;
}
/* clock */
clk_prepare_enable(state->csi_clk);
clk_prepare_enable(state->vadc_clk);
platform_set_drvdata(pdev, state);
vadc_v4l2_subdev_init(sd, pdev, &vadc_ops);
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
/* Init VADC */
ret = vadc_of_init(pdev);
if (ret < 0)
goto err;
vadc_init(state);
pr_info("vadc driver loaded\n");
return 0;
err:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
v4l2_async_unregister_subdev(&state->sd);
clk_disable_unprepare(state->csi_clk);
clk_disable_unprepare(state->vadc_clk);
return ret;
}
static int vadc_remove(struct platform_device *pdev)
{
struct vadc_state *state = platform_get_drvdata(pdev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
v4l2_async_unregister_subdev(&state->sd);
clk_disable_unprepare(state->csi_clk);
clk_disable_unprepare(state->vadc_clk);
vadc_power_down(state);
return true;
}
static int vadc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct vadc_state *state = platform_get_drvdata(pdev);
clk_disable(state->csi_clk);
clk_disable(state->vadc_clk);
vadc_power_down(state);
return 0;
}
static int vadc_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct vadc_state *state = platform_get_drvdata(pdev);
clk_enable(state->csi_clk);
clk_enable(state->vadc_clk);
vadc_init(state);
return 0;
}
static const struct dev_pm_ops vadc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(vadc_suspend, vadc_resume)
};
static struct platform_driver vadc_driver = {
.driver = {
.name = "fsl_vadc",
.of_match_table = of_match_ptr(fsl_vadc_dt_ids),
.pm = &vadc_pm_ops,
},
.probe = vadc_probe,
.remove = vadc_remove,
};
module_platform_driver(vadc_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("fsl VADC/VDEC driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,239 @@
/*
* Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef MXC_VDEC_H
#define MXC_VDEC_H
/*** define base address ***/
#define VDEC_BASE vdec_regbase
#define AFE_BASE vafe_regbase
/* AFE - Register offsets */
#define AFE_BLOCK_ID_OFFSET 0x00000000
#define AFE_PDBUF_OFFSET 0x00000004
#define AFE_SWRST_OFFSET 0x00000008
#define AFE_TSTSEL_OFFSET 0x0000000c
#define AFE_TSTMSC_OFFSET 0x00000010
#define AFE_ENPADIO_OFFSET 0x00000014
#define AFE_BGREG_OFFSET 0x00000018
#define AFE_ACCESSAR_ID_OFFSET 0x00000400
#define AFE_PDADC_OFFSET 0x00000404
#define AFE_PDSARH_OFFSET 0x00000408
#define AFE_PDSARL_OFFSET 0x0000040C
#define AFE_PDADCRFH_OFFSET 0x00000410
#define AFE_PDADCRFL_OFFSET 0x00000414
#define AFE_ACCTST_OFFSET 0x00000418
#define AFE_ADCGN_OFFSET 0x0000041C
#define AFE_ICTRL_OFFSET 0x00000420
#define AFE_ICTLSTG_OFFSET 0x00000424
#define AFE_RCTRLSTG_OFFSET 0x00000428
#define AFE_TCTRLSTG_OFFSET 0x0000042c
#define AFE_REFMOD_OFFSET 0x00000430
#define AFE_REFTRIML_OFFSET 0x00000434
#define AFE_REFTRIMH_OFFSET 0x00000438
#define AFE_ADCR_OFFSET 0x0000043c
#define AFE_DUMMY0_OFFSET 0x00000440
#define AFE_DUMMY1_OFFSET 0x00000444
#define AFE_DUMMY2_OFFSET 0x00000448
#define AFE_DACAMP_OFFSET 0x0000044c
#define AFE_CLMPTST_OFFSET 0x00000450
#define AFE_CLMPDAT_OFFSET 0x00000454
#define AFE_CLMPAMP_OFFSET 0x00000458
#define AFE_CLAMP_OFFSET 0x0000045c
#define AFE_INPBUF_OFFSET 0x00000460
#define AFE_INPFLT_OFFSET 0x00000464
#define AFE_ADCDGN_OFFSET 0x00000468
#define AFE_OFFDRV_OFFSET 0x0000046c
#define AFE_INPCONFIG_OFFSET 0x00000470
#define AFE_PROGDELAY_OFFSET 0x00000474
#define AFE_ADCOMT_OFFSET 0x00000478
#define AFE_ALGDELAY_OFFSET 0x0000047c
#define AFE_ACC_ID_OFFSET 0x00000800
#define AFE_ACCSTA_OFFSET 0x00000804
#define AFE_ACCNOSLI_OFFSET 0x00000808
#define AFE_ACCCALCON_OFFSET 0x0000080c
#define AFE_BWEWRICTRL_OFFSET 0x00000810
#define AFE_SELSLI_OFFSET 0x00000814
#define AFE_SELBYT_OFFSET 0x00000818
#define AFE_REDVAL_OFFSET 0x00000820
#define AFE_WRIBYT_OFFSET 0x00000824
/* AFE Register per module */
#define AFE_BLOCK_ID (AFE_BASE + AFE_BLOCK_ID_OFFSET)
#define AFE_PDBUF (AFE_BASE + AFE_PDBUF_OFFSET)
#define AFE_SWRST (AFE_BASE + AFE_SWRST_OFFSET)
#define AFE_TSTSEL (AFE_BASE + AFE_TSTSEL_OFFSET)
#define AFE_TSTMSC (AFE_BASE + AFE_TSTMSC_OFFSET)
#define AFE_ENPADIO (AFE_BASE + AFE_ENPADIO_OFFSET)
#define AFE_BGREG (AFE_BASE + AFE_BGREG_OFFSET)
#define AFE_ACCESSAR_ID (AFE_BASE + AFE_ACCESSAR_ID_OFFSET)
#define AFE_PDADC (AFE_BASE + AFE_PDADC_OFFSET)
#define AFE_PDSARH (AFE_BASE + AFE_PDSARH_OFFSET)
#define AFE_PDSARL (AFE_BASE + AFE_PDSARL_OFFSET)
#define AFE_PDADCRFH (AFE_BASE + AFE_PDADCRFH_OFFSET)
#define AFE_PDADCRFL (AFE_BASE + AFE_PDADCRFL_OFFSET)
#define AFE_ACCTST (AFE_BASE + AFE_ACCTST_OFFSET)
#define AFE_ADCGN (AFE_BASE + AFE_ADCGN_OFFSET)
#define AFE_ICTRL (AFE_BASE + AFE_ICTRL_OFFSET)
#define AFE_ICTLSTG (AFE_BASE + AFE_ICTLSTG_OFFSET)
#define AFE_RCTRLSTG (AFE_BASE + AFE_RCTRLSTG_OFFSET)
#define AFE_TCTRLSTG (AFE_BASE + AFE_TCTRLSTG_OFFSET)
#define AFE_REFMOD (AFE_BASE + AFE_REFMOD_OFFSET)
#define AFE_REFTRIML (AFE_BASE + AFE_REFTRIML_OFFSET)
#define AFE_REFTRIMH (AFE_BASE + AFE_REFTRIMH_OFFSET)
#define AFE_ADCR (AFE_BASE + AFE_ADCR_OFFSET)
#define AFE_DUMMY0 (AFE_BASE + AFE_DUMMY0_OFFSET)
#define AFE_DUMMY1 (AFE_BASE + AFE_DUMMY1_OFFSET)
#define AFE_DUMMY2 (AFE_BASE + AFE_DUMMY2_OFFSET)
#define AFE_DACAMP (AFE_BASE + AFE_DACAMP_OFFSET)
#define AFE_CLMPTST (AFE_BASE + AFE_CLMPTST_OFFSET)
#define AFE_CLMPDAT (AFE_BASE + AFE_CLMPDAT_OFFSET)
#define AFE_CLMPAMP (AFE_BASE + AFE_CLMPAMP_OFFSET)
#define AFE_CLAMP (AFE_BASE + AFE_CLAMP_OFFSET)
#define AFE_INPBUF (AFE_BASE + AFE_INPBUF_OFFSET)
#define AFE_INPFLT (AFE_BASE + AFE_INPFLT_OFFSET)
#define AFE_ADCDGN (AFE_BASE + AFE_ADCDGN_OFFSET)
#define AFE_OFFDRV (AFE_BASE + AFE_OFFDRV_OFFSET)
#define AFE_INPCONFIG (AFE_BASE + AFE_INPCONFIG_OFFSET)
#define AFE_PROGDELAY (AFE_BASE + AFE_PROGDELAY_OFFSET)
#define AFE_ADCOMT (AFE_BASE + AFE_ADCOMT_OFFSET)
#define AFE_ALGDELAY (AFE_BASE + AFE_ALGDELAY_OFFSET)
#define AFE_ACC_ID (AFE_BASE + AFE_ACC_ID_OFFSET)
#define AFE_ACCSTA (AFE_BASE + AFE_ACCSTA_OFFSET)
#define AFE_ACCNOSLI (AFE_BASE + AFE_ACCNOSLI_OFFSET)
#define AFE_ACCCALCON (AFE_BASE + AFE_ACCCALCON_OFFSET)
#define AFE_BWEWRICTRL (AFE_BASE + AFE_BWEWRICTRL_OFFSET)
#define AFE_SELSLI (AFE_BASE + AFE_SELSLI_OFFSET)
#define AFE_SELBYT (AFE_BASE + AFE_SELBYT_OFFSET)
#define AFE_REDVAL (AFE_BASE + AFE_REDVAL_OFFSET)
#define AFE_WRIBYT (AFE_BASE + AFE_WRIBYT_OFFSET)
/* VDEC - Register offsets */
#define VDEC_CFC1_OFFSET 0x00000000
#define VDEC_CFC2_OFFSET 0x00000004
#define VDEC_BRSTGT_OFFSET 0x00000024
#define VDEC_HZPOS_OFFSET 0x00000040
#define VDEC_VRTPOS_OFFSET 0x00000044
#define VDEC_HVSHIFT_OFFSET 0x00000054
#define VDEC_HSIGS_OFFSET 0x00000058
#define VDEC_HSIGE_OFFSET 0x0000005C
#define VDEC_VSCON1_OFFSET 0x00000060
#define VDEC_VSCON2_OFFSET 0x00000064
#define VDEC_YCDEL_OFFSET 0x0000006C
#define VDEC_AFTCLP_OFFSET 0x00000070
#define VDEC_DCOFF_OFFSET 0x00000078
#define VDEC_CSID_OFFSET 0x00000084
#define VDEC_CBGN_OFFSET 0x00000088
#define VDEC_CRGN_OFFSET 0x0000008C
#define VDEC_CNTR_OFFSET 0x00000090
#define VDEC_BRT_OFFSET 0x00000094
#define VDEC_HUE_OFFSET 0x00000098
#define VDEC_CHBTH_OFFSET 0x0000009C
#define VDEC_SHPIMP_OFFSET 0x000000A4
#define VDEC_CHPLLIM_OFFSET 0x000000A8
#define VDEC_VIDMOD_OFFSET 0x000000AC
#define VDEC_VIDSTS_OFFSET 0x000000B0
#define VDEC_NOISE_OFFSET 0x000000B4
#define VDEC_STDDBG_OFFSET 0x000000B8
#define VDEC_MANOVR_OFFSET 0x000000BC
#define VDEC_VSSGTH_OFFSET 0x000000C8
#define VDEC_DBGFBH_OFFSET 0x000000D0
#define VDEC_DBGFBL_OFFSET 0x000000D4
#define VDEC_HACTS_OFFSET 0x000000D8
#define VDEC_HACTE_OFFSET 0x000000DC
#define VDEC_VACTS_OFFSET 0x000000E0
#define VDEC_VACTE_OFFSET 0x000000E4
#define VDEC_HSTIP_OFFSET 0x000000EC
#define VDEC_BLSCRY_OFFSET 0x000000F4
#define VDEC_BLSCRCR_OFFSET 0x000000F8
#define VDEC_BLSCRCB_OFFSET 0x000000FC
#define VDEC_LMAGC1_OFFSET 0x00000100
#define VDEC_LMAGC2_OFFSET 0x00000104
#define VDEC_CHAGC1_OFFSET 0x00000108
#define VDEC_CHAGC2_OFFSET 0x0000010C
#define VDEC_MINTH_OFFSET 0x00000114
#define VDEC_VFRQOH_OFFSET 0x0000011C
#define VDEC_VFRQOL_OFFSET 0x00000120
#define VDEC_THSH1_OFFSET 0x00000124
#define VDEC_THSH2_OFFSET 0x00000128
#define VDEC_NCHTH_OFFSET 0x0000012C
#define VDEC_TH1F_OFFSET 0x00000130
/* VDEC Register per module */
#define VDEC_CFC1 (VDEC_BASE + VDEC_CFC1_OFFSET)
#define VDEC_CFC2 (VDEC_BASE + VDEC_CFC2_OFFSET)
#define VDEC_BRSTGT (VDEC_BASE + VDEC_BRSTGT_OFFSET)
#define VDEC_HZPOS (VDEC_BASE + VDEC_HZPOS_OFFSET)
#define VDEC_VRTPOS (VDEC_BASE + VDEC_VRTPOS_OFFSET)
#define VDEC_HVSHIFT (VDEC_BASE + VDEC_HVSHIFT_OFFSET)
#define VDEC_HSIGS (VDEC_BASE + VDEC_HSIGS_OFFSET)
#define VDEC_HSIGE (VDEC_BASE + VDEC_HSIGE_OFFSET)
#define VDEC_VSCON1 (VDEC_BASE + VDEC_VSCON1_OFFSET)
#define VDEC_VSCON2 (VDEC_BASE + VDEC_VSCON2_OFFSET)
#define VDEC_YCDEL (VDEC_BASE + VDEC_YCDEL_OFFSET)
#define VDEC_AFTCLP (VDEC_BASE + VDEC_AFTCLP_OFFSET)
#define VDEC_DCOFF (VDEC_BASE + VDEC_DCOFF_OFFSET)
#define VDEC_CSID (VDEC_BASE + VDEC_CSID_OFFSET)
#define VDEC_CBGN (VDEC_BASE + VDEC_CBGN_OFFSET)
#define VDEC_CRGN (VDEC_BASE + VDEC_CRGN_OFFSET)
#define VDEC_CNTR (VDEC_BASE + VDEC_CNTR_OFFSET)
#define VDEC_BRT (VDEC_BASE + VDEC_BRT_OFFSET)
#define VDEC_HUE (VDEC_BASE + VDEC_HUE_OFFSET)
#define VDEC_CHBTH (VDEC_BASE + VDEC_CHBTH_OFFSET)
#define VDEC_SHPIMP (VDEC_BASE + VDEC_SHPIMP_OFFSET)
#define VDEC_CHPLLIM (VDEC_BASE + VDEC_CHPLLIM_OFFSET)
#define VDEC_VIDMOD (VDEC_BASE + VDEC_VIDMOD_OFFSET)
#define VDEC_VIDSTS (VDEC_BASE + VDEC_VIDSTS_OFFSET)
#define VDEC_NOISE (VDEC_BASE + VDEC_NOISE_OFFSET)
#define VDEC_STDDBG (VDEC_BASE + VDEC_STDDBG_OFFSET)
#define VDEC_MANOVR (VDEC_BASE + VDEC_MANOVR_OFFSET)
#define VDEC_VSSGTH (VDEC_BASE + VDEC_VSSGTH_OFFSET)
#define VDEC_DBGFBH (VDEC_BASE + VDEC_DBGFBH_OFFSET)
#define VDEC_DBGFBL (VDEC_BASE + VDEC_DBGFBL_OFFSET)
#define VDEC_HACTS (VDEC_BASE + VDEC_HACTS_OFFSET)
#define VDEC_HACTE (VDEC_BASE + VDEC_HACTE_OFFSET)
#define VDEC_VACTS (VDEC_BASE + VDEC_VACTS_OFFSET)
#define VDEC_VACTE (VDEC_BASE + VDEC_VACTE_OFFSET)
#define VDEC_HSTIP (VDEC_BASE + VDEC_HSTIP_OFFSET)
#define VDEC_BLSCRY (VDEC_BASE + VDEC_BLSCRY_OFFSET)
#define VDEC_BLSCRCR (VDEC_BASE + VDEC_BLSCRCR_OFFSET)
#define VDEC_BLSCRCB (VDEC_BASE + VDEC_BLSCRCB_OFFSET)
#define VDEC_LMAGC1 (VDEC_BASE + VDEC_LMAGC1_OFFSET)
#define VDEC_LMAGC2 (VDEC_BASE + VDEC_LMAGC2_OFFSET)
#define VDEC_CHAGC1 (VDEC_BASE + VDEC_CHAGC1_OFFSET)
#define VDEC_CHAGC2 (VDEC_BASE + VDEC_CHAGC2_OFFSET)
#define VDEC_MINTH (VDEC_BASE + VDEC_MINTH_OFFSET)
#define VDEC_VFRQOH (VDEC_BASE + VDEC_VFRQOH_OFFSET)
#define VDEC_VFRQOL (VDEC_BASE + VDEC_VFRQOL_OFFSET)
#define VDEC_THSH1 (VDEC_BASE + VDEC_THSH1_OFFSET)
#define VDEC_THSH2 (VDEC_BASE + VDEC_THSH2_OFFSET)
#define VDEC_NCHTH (VDEC_BASE + VDEC_NCHTH_OFFSET)
#define VDEC_TH1F (VDEC_BASE + VDEC_TH1F_OFFSET)
#define VDEC_VIDMOD_SIGNAL_MASK 0x0F
#define VDEC_VIDMOD_SIGNAL_DETECT 0x0F
#define VDEC_VIDMOD_M625_SHIFT 4
#define VDEC_VIDMOD_M625_MASK (1 << VDEC_VIDMOD_M625_SHIFT)
#define VDEC_VIDMOD_PAL_SHIFT 7
#define VDEC_VIDMOD_PAL_MASK (1 << VDEC_VIDMOD_PAL_SHIFT)
/*** define base address ***/
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ config MXC_IPU
source "drivers/mxc/mlb/Kconfig"
source "drivers/mxc/ipu3/Kconfig"
source "drivers/mxc/sim/Kconfig"
source "drivers/mxc/mipi/Kconfig"
source "drivers/mxc/vpu/Kconfig"
source "drivers/mxc/hdmi-cec/Kconfig"

View File

@ -3,3 +3,4 @@ obj-$(CONFIG_MXC_SIM) += sim/
obj-$(CONFIG_MXC_VPU) += vpu/
obj-$(CONFIG_MXC_IPU_V3) += ipu3/
obj-$(CONFIG_MXC_HDMI_CEC) += hdmi-cec/
obj-$(CONFIG_MXC_MIPI_CSI2) += mipi/

View File

@ -0,0 +1,14 @@
#
# MIPI configuration
#
menu "MXC MIPI Support"
config MXC_MIPI_CSI2
tristate "MIPI CSI2 support"
depends on SOC_IMX6Q
default n
---help---
Say Y to get the MIPI CSI2 support.
endmenu

View File

@ -0,0 +1,4 @@
#
# Makefile for the mipi interface driver
#
obj-$(CONFIG_MXC_MIPI_CSI2) += mxc_mipi_csi2.o

View File

@ -0,0 +1,540 @@
/*
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/fsl_devices.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/mipi_csi2.h>
#include "mxc_mipi_csi2.h"
static struct mipi_csi2_info *gmipi_csi2;
void _mipi_csi2_lock(struct mipi_csi2_info *info)
{
if (!in_irq() && !in_softirq())
mutex_lock(&info->mutex_lock);
}
void _mipi_csi2_unlock(struct mipi_csi2_info *info)
{
if (!in_irq() && !in_softirq())
mutex_unlock(&info->mutex_lock);
}
static inline void mipi_csi2_write(struct mipi_csi2_info *info,
unsigned value, unsigned offset)
{
writel(value, info->mipi_csi2_base + offset);
}
static inline unsigned int mipi_csi2_read(struct mipi_csi2_info *info,
unsigned offset)
{
return readl(info->mipi_csi2_base + offset);
}
/*!
* This function is called to enable the mipi csi2 interface.
*
* @param info mipi csi2 hander
* @return Returns setted value
*/
bool mipi_csi2_enable(struct mipi_csi2_info *info)
{
bool status;
_mipi_csi2_lock(info);
if (!info->mipi_en) {
info->mipi_en = true;
clk_prepare_enable(info->cfg_clk);
clk_prepare_enable(info->dphy_clk);
} else
mipi_dbg("mipi csi2 already enabled!\n");
status = info->mipi_en;
_mipi_csi2_unlock(info);
return status;
}
EXPORT_SYMBOL(mipi_csi2_enable);
/*!
* This function is called to disable the mipi csi2 interface.
*
* @param info mipi csi2 hander
* @return Returns setted value
*/
bool mipi_csi2_disable(struct mipi_csi2_info *info)
{
bool status;
_mipi_csi2_lock(info);
if (info->mipi_en) {
info->mipi_en = false;
clk_disable_unprepare(info->dphy_clk);
clk_disable_unprepare(info->cfg_clk);
} else
mipi_dbg("mipi csi2 already disabled!\n");
status = info->mipi_en;
_mipi_csi2_unlock(info);
return status;
}
EXPORT_SYMBOL(mipi_csi2_disable);
/*!
* This function is called to get mipi csi2 disable/enable status.
*
* @param info mipi csi2 hander
* @return Returns mipi csi2 status
*/
bool mipi_csi2_get_status(struct mipi_csi2_info *info)
{
bool status;
_mipi_csi2_lock(info);
status = info->mipi_en;
_mipi_csi2_unlock(info);
return status;
}
EXPORT_SYMBOL(mipi_csi2_get_status);
/*!
* This function is called to set mipi lanes.
*
* @param info mipi csi2 hander
* @return Returns setted value
*/
unsigned int mipi_csi2_set_lanes(struct mipi_csi2_info *info)
{
unsigned int lanes;
_mipi_csi2_lock(info);
mipi_csi2_write(info, info->lanes - 1, MIPI_CSI2_N_LANES);
lanes = mipi_csi2_read(info, MIPI_CSI2_N_LANES);
_mipi_csi2_unlock(info);
return lanes;
}
EXPORT_SYMBOL(mipi_csi2_set_lanes);
/*!
* This function is called to set mipi data type.
*
* @param info mipi csi2 hander
* @return Returns setted value
*/
unsigned int mipi_csi2_set_datatype(struct mipi_csi2_info *info,
unsigned int datatype)
{
unsigned int dtype;
_mipi_csi2_lock(info);
info->datatype = datatype;
dtype = info->datatype;
_mipi_csi2_unlock(info);
return dtype;
}
EXPORT_SYMBOL(mipi_csi2_set_datatype);
/*!
* This function is called to get mipi data type.
*
* @param info mipi csi2 hander
* @return Returns mipi data type
*/
unsigned int mipi_csi2_get_datatype(struct mipi_csi2_info *info)
{
unsigned int dtype;
_mipi_csi2_lock(info);
dtype = info->datatype;
_mipi_csi2_unlock(info);
return dtype;
}
EXPORT_SYMBOL(mipi_csi2_get_datatype);
/*!
* This function is called to get mipi csi2 dphy status.
*
* @param info mipi csi2 hander
* @return Returns dphy status
*/
unsigned int mipi_csi2_dphy_status(struct mipi_csi2_info *info)
{
unsigned int status;
_mipi_csi2_lock(info);
status = mipi_csi2_read(info, MIPI_CSI2_PHY_STATE);
_mipi_csi2_unlock(info);
return status;
}
EXPORT_SYMBOL(mipi_csi2_dphy_status);
/*!
* This function is called to get mipi csi2 error1 status.
*
* @param info mipi csi2 hander
* @return Returns error1 value
*/
unsigned int mipi_csi2_get_error1(struct mipi_csi2_info *info)
{
unsigned int err1;
_mipi_csi2_lock(info);
err1 = mipi_csi2_read(info, MIPI_CSI2_ERR1);
_mipi_csi2_unlock(info);
return err1;
}
EXPORT_SYMBOL(mipi_csi2_get_error1);
/*!
* This function is called to get mipi csi2 error1 status.
*
* @param info mipi csi2 hander
* @return Returns error1 value
*/
unsigned int mipi_csi2_get_error2(struct mipi_csi2_info *info)
{
unsigned int err2;
_mipi_csi2_lock(info);
err2 = mipi_csi2_read(info, MIPI_CSI2_ERR2);
_mipi_csi2_unlock(info);
return err2;
}
EXPORT_SYMBOL(mipi_csi2_get_error2);
/*!
* This function is called to enable mipi to ipu pixel clock.
*
* @param info mipi csi2 hander
* @return Returns 0 on success or negative error code on fail
*/
int mipi_csi2_pixelclk_enable(struct mipi_csi2_info *info)
{
return clk_prepare_enable(info->pixel_clk);
}
EXPORT_SYMBOL(mipi_csi2_pixelclk_enable);
/*!
* This function is called to disable mipi to ipu pixel clock.
*
* @param info mipi csi2 hander
* @return Returns 0 on success or negative error code on fail
*/
void mipi_csi2_pixelclk_disable(struct mipi_csi2_info *info)
{
clk_disable_unprepare(info->pixel_clk);
}
EXPORT_SYMBOL(mipi_csi2_pixelclk_disable);
/*!
* This function is called to power on mipi csi2.
*
* @param info mipi csi2 hander
* @return Returns 0 on success or negative error code on fail
*/
int mipi_csi2_reset(struct mipi_csi2_info *info)
{
_mipi_csi2_lock(info);
mipi_csi2_write(info, 0x0, MIPI_CSI2_PHY_SHUTDOWNZ);
mipi_csi2_write(info, 0x0, MIPI_CSI2_DPHY_RSTZ);
mipi_csi2_write(info, 0x0, MIPI_CSI2_CSI2_RESETN);
mipi_csi2_write(info, 0x00000001, MIPI_CSI2_PHY_TST_CTRL0);
mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL1);
mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0);
mipi_csi2_write(info, 0x00000002, MIPI_CSI2_PHY_TST_CTRL0);
mipi_csi2_write(info, 0x00010044, MIPI_CSI2_PHY_TST_CTRL1);
mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0);
mipi_csi2_write(info, 0x00000014, MIPI_CSI2_PHY_TST_CTRL1);
mipi_csi2_write(info, 0x00000002, MIPI_CSI2_PHY_TST_CTRL0);
mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0);
mipi_csi2_write(info, 0xffffffff, MIPI_CSI2_PHY_SHUTDOWNZ);
mipi_csi2_write(info, 0xffffffff, MIPI_CSI2_DPHY_RSTZ);
mipi_csi2_write(info, 0xffffffff, MIPI_CSI2_CSI2_RESETN);
_mipi_csi2_unlock(info);
return 0;
}
EXPORT_SYMBOL(mipi_csi2_reset);
/*!
* This function is called to get mipi csi2 info.
*
* @return Returns mipi csi2 info struct pointor
*/
struct mipi_csi2_info *mipi_csi2_get_info(void)
{
return gmipi_csi2;
}
EXPORT_SYMBOL(mipi_csi2_get_info);
/*!
* This function is called to get mipi csi2 bind ipu num.
*
* @return Returns mipi csi2 bind ipu num
*/
int mipi_csi2_get_bind_ipu(struct mipi_csi2_info *info)
{
int ipu_id;
_mipi_csi2_lock(info);
ipu_id = info->ipu_id;
_mipi_csi2_unlock(info);
return ipu_id;
}
EXPORT_SYMBOL(mipi_csi2_get_bind_ipu);
/*!
* This function is called to get mipi csi2 bind csi num.
*
* @return Returns mipi csi2 bind csi num
*/
unsigned int mipi_csi2_get_bind_csi(struct mipi_csi2_info *info)
{
unsigned int csi_id;
_mipi_csi2_lock(info);
csi_id = info->csi_id;
_mipi_csi2_unlock(info);
return csi_id;
}
EXPORT_SYMBOL(mipi_csi2_get_bind_csi);
/*!
* This function is called to get mipi csi2 virtual channel.
*
* @return Returns mipi csi2 virtual channel num
*/
unsigned int mipi_csi2_get_virtual_channel(struct mipi_csi2_info *info)
{
unsigned int v_channel;
_mipi_csi2_lock(info);
v_channel = info->v_channel;
_mipi_csi2_unlock(info);
return v_channel;
}
EXPORT_SYMBOL(mipi_csi2_get_virtual_channel);
/**
* This function is called by the driver framework to initialize the MIPI CSI2
* device.
*
* @param pdev The device structure for the MIPI CSI2 passed in by the
* driver framework.
*
* @return Returns 0 on success or negative error code on error
*/
static int mipi_csi2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct resource *res;
u32 mipi_csi2_dphy_ver;
int ret;
gmipi_csi2 = kmalloc(sizeof(struct mipi_csi2_info), GFP_KERNEL);
if (!gmipi_csi2) {
ret = -ENOMEM;
goto alloc_failed;
}
ret = of_property_read_u32(np, "ipu_id", &(gmipi_csi2->ipu_id));
if (ret) {
dev_err(&pdev->dev, "ipu_id missing or invalid\n");
goto err;
}
ret = of_property_read_u32(np, "csi_id", &(gmipi_csi2->csi_id));
if (ret) {
dev_err(&pdev->dev, "csi_id missing or invalid\n");
goto err;
}
ret = of_property_read_u32(np, "v_channel", &(gmipi_csi2->v_channel));
if (ret) {
dev_err(&pdev->dev, "v_channel missing or invalid\n");
goto err;
}
ret = of_property_read_u32(np, "lanes", &(gmipi_csi2->lanes));
if (ret) {
dev_err(&pdev->dev, "lanes missing or invalid\n");
goto err;
}
if ((gmipi_csi2->ipu_id < 0) || (gmipi_csi2->ipu_id > 1) ||
(gmipi_csi2->csi_id > 1) || (gmipi_csi2->v_channel > 3) ||
(gmipi_csi2->lanes > 4)) {
dev_err(&pdev->dev, "invalid param for mipi csi2!\n");
ret = -EINVAL;
goto err;
}
/* initialize mutex */
mutex_init(&gmipi_csi2->mutex_lock);
/* get mipi csi2 informaiton */
gmipi_csi2->pdev = pdev;
gmipi_csi2->mipi_en = false;
gmipi_csi2->cfg_clk = devm_clk_get(dev, "cfg_clk");
if (IS_ERR(gmipi_csi2->cfg_clk)) {
dev_err(&pdev->dev, "failed to get cfg_clk\n");
ret = PTR_ERR(gmipi_csi2->cfg_clk);
goto err;
}
/* get mipi dphy clk */
gmipi_csi2->dphy_clk = devm_clk_get(dev, "dphy_clk");
if (IS_ERR(gmipi_csi2->dphy_clk)) {
dev_err(&pdev->dev, "failed to get dphy pll_ref_clk\n");
ret = PTR_ERR(gmipi_csi2->dphy_clk);
goto err;
}
/* get mipi to ipu pixel clk */
gmipi_csi2->pixel_clk = devm_clk_get(dev, "pixel_clk");
if (IS_ERR(gmipi_csi2->pixel_clk)) {
dev_err(&pdev->dev, "failed to get mipi pixel clk\n");
ret = PTR_ERR(gmipi_csi2->pixel_clk);
goto err;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
goto err;
}
/* mipi register mapping */
gmipi_csi2->mipi_csi2_base = ioremap(res->start, PAGE_SIZE);
if (!gmipi_csi2->mipi_csi2_base) {
ret = -ENOMEM;
goto err;
}
/* mipi dphy clk enable for register access */
clk_prepare_enable(gmipi_csi2->dphy_clk);
/* get mipi csi2 dphy version */
mipi_csi2_dphy_ver = mipi_csi2_read(gmipi_csi2, MIPI_CSI2_VERSION);
clk_disable_unprepare(gmipi_csi2->dphy_clk);
platform_set_drvdata(pdev, gmipi_csi2);
dev_info(&pdev->dev, "i.MX MIPI CSI2 driver probed\n");
dev_info(&pdev->dev, "i.MX MIPI CSI2 dphy version is 0x%x\n",
mipi_csi2_dphy_ver);
return 0;
err:
kfree(gmipi_csi2);
alloc_failed:
dev_err(&pdev->dev, "i.MX MIPI CSI2 driver probed - error\n");
return ret;
}
static int mipi_csi2_remove(struct platform_device *pdev)
{
/* unmapping mipi register */
iounmap(gmipi_csi2->mipi_csi2_base);
kfree(gmipi_csi2);
dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
static const struct of_device_id imx_mipi_csi2_dt_ids[] = {
{ .compatible = "fsl,imx6q-mipi-csi2", },
{ /* sentinel */ }
};
static struct platform_driver mipi_csi2_driver = {
.driver = {
.name = "mxc_mipi_csi2",
.of_match_table = imx_mipi_csi2_dt_ids,
},
.probe = mipi_csi2_probe,
.remove = mipi_csi2_remove,
};
static int __init mipi_csi2_init(void)
{
int err;
err = platform_driver_register(&mipi_csi2_driver);
if (err) {
pr_err("mipi_csi2_driver register failed\n");
return -ENODEV;
}
pr_info("MIPI CSI2 driver module loaded\n");
return 0;
}
static void __exit mipi_csi2_cleanup(void)
{
platform_driver_unregister(&mipi_csi2_driver);
}
subsys_initcall(mipi_csi2_init);
module_exit(mipi_csi2_cleanup);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("i.MX MIPI CSI2 driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __MXC_MIPI_CSI2_H__
#define __MXC_MIPI_CSI2_H__
#ifdef DEBUG
#define mipi_dbg(fmt, ...) \
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define mipi_dbg(fmt, ...)
#endif
/* driver private data */
struct mipi_csi2_info {
bool mipi_en;
int ipu_id;
unsigned int csi_id;
unsigned int v_channel;
unsigned int lanes;
unsigned int datatype;
struct clk *cfg_clk;
struct clk *dphy_clk;
struct clk *pixel_clk;
void __iomem *mipi_csi2_base;
struct platform_device *pdev;
struct mutex mutex_lock;
};
#endif

View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __INCLUDE_MIPI_CSI2_H
#define __INCLUDE_MIPI_CSI2_H
/* MIPI CSI2 registers */
#define MIPI_CSI2_REG(offset) (offset)
#define MIPI_CSI2_VERSION MIPI_CSI2_REG(0x000)
#define MIPI_CSI2_N_LANES MIPI_CSI2_REG(0x004)
#define MIPI_CSI2_PHY_SHUTDOWNZ MIPI_CSI2_REG(0x008)
#define MIPI_CSI2_DPHY_RSTZ MIPI_CSI2_REG(0x00c)
#define MIPI_CSI2_CSI2_RESETN MIPI_CSI2_REG(0x010)
#define MIPI_CSI2_PHY_STATE MIPI_CSI2_REG(0x014)
#define MIPI_CSI2_DATA_IDS_1 MIPI_CSI2_REG(0x018)
#define MIPI_CSI2_DATA_IDS_2 MIPI_CSI2_REG(0x01c)
#define MIPI_CSI2_ERR1 MIPI_CSI2_REG(0x020)
#define MIPI_CSI2_ERR2 MIPI_CSI2_REG(0x024)
#define MIPI_CSI2_MASK1 MIPI_CSI2_REG(0x028)
#define MIPI_CSI2_MASK2 MIPI_CSI2_REG(0x02c)
#define MIPI_CSI2_PHY_TST_CTRL0 MIPI_CSI2_REG(0x030)
#define MIPI_CSI2_PHY_TST_CTRL1 MIPI_CSI2_REG(0x034)
#define MIPI_CSI2_SFT_RESET MIPI_CSI2_REG(0xf00)
/* mipi data type */
#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */
#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */
#define MIPI_DT_YUV422 0x1e /* UYVY... */
#define MIPI_DT_RGB444 0x20
#define MIPI_DT_RGB555 0x21
#define MIPI_DT_RGB565 0x22
#define MIPI_DT_RGB666 0x23
#define MIPI_DT_RGB888 0x24
#define MIPI_DT_RAW6 0x28
#define MIPI_DT_RAW7 0x29
#define MIPI_DT_RAW8 0x2a
#define MIPI_DT_RAW10 0x2b
#define MIPI_DT_RAW12 0x2c
#define MIPI_DT_RAW14 0x2d
struct mipi_csi2_info;
/* mipi csi2 API */
struct mipi_csi2_info *mipi_csi2_get_info(void);
bool mipi_csi2_enable(struct mipi_csi2_info *info);
bool mipi_csi2_disable(struct mipi_csi2_info *info);
bool mipi_csi2_get_status(struct mipi_csi2_info *info);
int mipi_csi2_get_bind_ipu(struct mipi_csi2_info *info);
unsigned int mipi_csi2_get_bind_csi(struct mipi_csi2_info *info);
unsigned int mipi_csi2_get_virtual_channel(struct mipi_csi2_info *info);
unsigned int mipi_csi2_set_lanes(struct mipi_csi2_info *info);
unsigned int mipi_csi2_set_datatype(struct mipi_csi2_info *info,
unsigned int datatype);
unsigned int mipi_csi2_get_datatype(struct mipi_csi2_info *info);
unsigned int mipi_csi2_dphy_status(struct mipi_csi2_info *info);
unsigned int mipi_csi2_get_error1(struct mipi_csi2_info *info);
unsigned int mipi_csi2_get_error2(struct mipi_csi2_info *info);
int mipi_csi2_pixelclk_enable(struct mipi_csi2_info *info);
void mipi_csi2_pixelclk_disable(struct mipi_csi2_info *info);
int mipi_csi2_reset(struct mipi_csi2_info *info);
#endif

View File

@ -53,6 +53,11 @@ struct v4l2_mxc_offset {
uint32_t v_offset;
};
struct v4l2_mxc_dest_crop {
__u32 type; /* enum v4l2_buf_type */
struct v4l2_mxc_offset offset;
};
/*
* Private IOCTLs
*
@ -63,5 +68,6 @@ struct v4l2_mxc_offset {
_IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct v4l2_crop)
#define VIDIOC_G_INPUT_CROP \
_IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct v4l2_crop)
#define VIDIOC_S_DEST_CROP \
_IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct v4l2_mxc_dest_crop)
#endif