1
0
Fork 0

Merge remote-tracking branch 'origin/capture/mxc' into capture/next

* origin/capture/mxc: (11 commits)
  media: v4l2: vadc: Enable vadc driver
  media: mx6s_capture: accommodate the driver to framework's change
  media: v4l2 capture: add v4l2 capture driver based on csi
  media: mipi csi: add IPU CSI driver
  media: add adv7180 video decoder driver
  ...
5.4-rM2-2.2.x-imx-squashed
Dong Aisheng 2019-12-02 18:00:51 +08:00
commit b8cb1caf1e
29 changed files with 12930 additions and 0 deletions

View File

@ -147,7 +147,14 @@ config VIDEO_MX8_CAPTURE
---help---
This is the video4linux2 capture driver based on i.MX8 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/imx8/Kconfig"
source "drivers/media/platform/mxc/capture/Kconfig"
source "drivers/media/platform/exynos4-is/Kconfig"
source "drivers/media/platform/am437x/Kconfig"
source "drivers/media/platform/xilinx/Kconfig"

View File

@ -85,6 +85,7 @@ obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/
obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
obj-$(CONFIG_VIDEO_MX8_CAPTURE) += imx8/
obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/capture/
obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/

View File

@ -0,0 +1,124 @@
if VIDEO_MXC_CAPTURE
config VIDEO_V4L2_MXC_INT_DEVICE
tristate
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_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.
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
depends on VIDEO_V4L2_MXC_INT_DEVICE
help
If you plan to use the ov5640 Camera with your MXC system, say Y here.
config MXC_CAMERA_OV5640_V2
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_CAMERA_OV5642
tristate "OmniVision ov5642 camera support"
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
depends on VIDEO_V4L2_MXC_INT_DEVICE
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 && MXC_MIPI_CSI2
depends on VIDEO_V4L2_MXC_INT_DEVICE
help
If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.
config MXC_CAMERA_OV5640_MIPI_V2
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.
config MXC_TVIN_ADV7180
tristate "Analog Device adv7180 TV Decoder Input support"
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
depends on VIDEO_V4L2_MXC_INT_DEVICE
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,38 @@
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
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
# Used for iMX 6QDL
ov5640_camera_int-objs := ov5640.o
obj-$(CONFIG_MXC_CAMERA_OV5640) += ov5640_camera_int.o
# Used for iMX 6UL/ULL/SX/SL/SLL
ov5640_camera_v2-objs := ov5640_v2.o
obj-$(CONFIG_MXC_CAMERA_OV5640_V2) += ov5640_camera_v2.o
ov5642_camera-objs := ov5642.o
obj-$(CONFIG_MXC_CAMERA_OV5642) += ov5642_camera.o
# Used for iMX 6QDL/DQSCM
ov5640_camera_mipi_int-objs := ov5640_mipi.o
obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi_int.o
# Used for iMX 7D
ov5640_camera_mipi_v2-objs := ov5640_mipi_v2.o
obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI_V2) += ov5640_camera_mipi_v2.o
ov5647_camera_mipi-objs := ov5647_mipi.o
obj-$(CONFIG_MXC_CAMERA_OV5647_MIPI) += ov5647_camera_mipi.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,544 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
/*!
* @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(cam->dev, 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(cam->dev, 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(cam->dev,
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(cam->dev,
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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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,423 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2009-2015 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
/*!
* @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(cam->dev,
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(cam->dev, 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,633 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
/*!
* @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(cam->dev, 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(cam->dev, 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(cam->dev,
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(cam->dev,
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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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,590 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
/*!
* @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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev,
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(cam->dev, 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(cam->dev, 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(cam->dev, 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,36 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
/*!
* @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,576 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*!
* @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(cam->dev, 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(cam->dev, 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(cam->dev,
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(cam->dev,
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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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,514 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
/*!
* @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(cam->dev, cam->vf_bufs_size[0],
cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
}
if (cam->vf_bufs_vaddr[1]) {
dma_free_coherent(cam->dev, 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(cam->dev,
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(cam->dev,
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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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(cam->dev, 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,261 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
/*!
* @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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,256 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
/*!
* @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 device *dev;
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__ */

View File

@ -0,0 +1,830 @@
/*
* 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_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
return vadc_get_fmt(sd, cfg, format);
}
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,
.set_fmt = vadc_set_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

View File

@ -0,0 +1,152 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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>
*/
#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 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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_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 @@
source "drivers/mxc/mipi/Kconfig"

View File

@ -0,0 +1 @@
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_IMX6 || SOC_IMX7D)
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,528 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
#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,34 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
#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,77 @@
/*
* Copyright 2012-2016 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 version 2 as
* published by the Free Software Foundation.
*/
#ifndef __ASM_ARCH_MXC_BUSFREQ_H__
#define __ASM_ARCH_MXC_BUSFREQ_H__
#include <linux/notifier.h>
#include <linux/regulator/consumer.h>
/*
* This enumerates busfreq low power mode entry and exit.
*/
enum busfreq_event {
LOW_BUSFREQ_ENTER,
LOW_BUSFREQ_EXIT,
};
/*
* This enumerates the system bus and ddr frequencies in various modes.
* BUS_FREQ_HIGH - DDR @ 528MHz, AHB @ 132MHz.
* BUS_FREQ_MED - DDR @ 400MHz, AHB @ 132MHz
* BUS_FREQ_AUDIO - DDR @ 50MHz/100MHz, AHB @ 24MHz.
* BUS_FREQ_LOW - DDR @ 24MHz, AHB @ 24MHz.
* BUS_FREQ_ULTRA_LOW - DDR @ 1MHz, AHB - 3MHz.
*
* Drivers need to request/release the bus/ddr frequencies based on
* their performance requirements. Drivers cannot request/release
* BUS_FREQ_ULTRA_LOW mode as this mode is automatically entered from
* either BUS_FREQ_AUDIO or BUS_FREQ_LOW
* modes.
*/
enum bus_freq_mode {
BUS_FREQ_HIGH,
BUS_FREQ_MED,
BUS_FREQ_AUDIO,
BUS_FREQ_LOW,
BUS_FREQ_ULTRA_LOW,
};
#if defined(CONFIG_HAVE_IMX_BUSFREQ) && !defined(CONFIG_ARM64)
extern struct regulator *arm_reg;
extern struct regulator *soc_reg;
void request_bus_freq(enum bus_freq_mode mode);
void release_bus_freq(enum bus_freq_mode mode);
int register_busfreq_notifier(struct notifier_block *nb);
int unregister_busfreq_notifier(struct notifier_block *nb);
int get_bus_freq_mode(void);
#elif defined(CONFIG_HAVE_IMX_BUSFREQ)
void request_bus_freq(enum bus_freq_mode mode);
void release_bus_freq(enum bus_freq_mode mode);
int get_bus_freq_mode(void);
#else
static inline void request_bus_freq(enum bus_freq_mode mode)
{
}
static inline void release_bus_freq(enum bus_freq_mode mode)
{
}
static inline int register_busfreq_notifier(struct notifier_block *nb)
{
return 0;
}
static inline int unregister_busfreq_notifier(struct notifier_block *nb)
{
return 0;
}
static inline int get_bus_freq_mode(void)
{
return BUS_FREQ_HIGH;
}
#endif
#endif

View File

@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2019 NXP
*/
#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

@ -0,0 +1,73 @@
/*
* Copyright (C) 2013-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.
*/
/*!
* @file uapi/linux/mxc_v4l2.h
*
* @brief MXC V4L2 private header file
*
* @ingroup MXC V4L2
*/
#ifndef __ASM_ARCH_MXC_V4L2_H__
#define __ASM_ARCH_MXC_V4L2_H__
/*
* For IPUv1 and IPUv3, V4L2_CID_MXC_ROT means encoder ioctl ID.
* And V4L2_CID_MXC_VF_ROT is viewfinder ioctl ID only for IPUv1 and IPUv3.
*/
#define V4L2_CID_MXC_ROT (V4L2_CID_PRIVATE_BASE + 0)
#define V4L2_CID_MXC_FLASH (V4L2_CID_PRIVATE_BASE + 1)
#define V4L2_CID_MXC_VF_ROT (V4L2_CID_PRIVATE_BASE + 2)
#define V4L2_CID_MXC_MOTION (V4L2_CID_PRIVATE_BASE + 3)
#define V4L2_CID_MXC_SWITCH_CAM (V4L2_CID_PRIVATE_BASE + 6)
#define V4L2_MXC_ROTATE_NONE 0
#define V4L2_MXC_ROTATE_VERT_FLIP 1
#define V4L2_MXC_ROTATE_HORIZ_FLIP 2
#define V4L2_MXC_ROTATE_180 3
#define V4L2_MXC_ROTATE_90_RIGHT 4
#define V4L2_MXC_ROTATE_90_RIGHT_VFLIP 5
#define V4L2_MXC_ROTATE_90_RIGHT_HFLIP 6
#define V4L2_MXC_ROTATE_90_LEFT 7
struct v4l2_mxc_offset {
uint32_t u_offset;
uint32_t v_offset;
};
struct v4l2_mxc_dest_crop {
__u32 type; /* enum v4l2_buf_type */
struct v4l2_mxc_offset offset;
};
/*
* Private IOCTLs
*
* VIDIOC_S_INOUT_CROP: Set input stream crop size
* VIDIOC_G_INOUT_CROP: Get input stream crop size
*/
#define VIDIOC_S_INPUT_CROP \
_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