/*
*
* Driver name : VPFE Capture driver
* VPFE Capture driver allows applications to capture and stream video
* frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as
* TVP5146 or Raw Bayer RGB image data from an image sensor
* such as Microns' MT9T001, MT9T031 etc.
*
* These SoCs have, in common, a Video Processing Subsystem (VPSS) that
* consists of a Video Processing Front End (VPFE) for capturing
* video/raw image data and Video Processing Back End (VPBE) for displaying
* YUV data through an in-built analog encoder or Digital LCD port. This
* driver is for capture through VPFE. A typical EVM using these SoCs have
* following high level configuration.
*
*
* decoder(TVP5146/ YUV/
* MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF)
* data input | |
* V |
* SDRAM |
* V
* Image Processor
* |
* V
* SDRAM
* The data flow happens from a decoder connected to the VPFE over a
* YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface
* and to the input of VPFE through an optional MUX (if more inputs are
* to be interfaced on the EVM). The input data is first passed through
* CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC
* does very little or no processing on YUV data and does pre-process Raw
* Bayer RGB data through modules such as Defect Pixel Correction (DFC)
* Color Space Conversion (CSC), data gain/offset etc. After this, data
* can be written to SDRAM or can be connected to the image processing
* block such as IPIPE (on DM355 only).
*
* Features supported
* - MMAP IO
* - Capture using TVP5146 over BT.656
* - support for interfacing decoders using sub device model
* - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV
* data capture to SDRAM.
* TODO list
* - Support multiple REQBUF after open
* - Support for de-allocating buffers through REQBUF
* - Support for Raw Bayer RGB capture
* - Support for chaining Image Processor
* - Support for static allocation of buffers
* - Support for USERPTR IO
* - Support for STREAMON before QBUF
* - Support for control ioctls
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <media/v4l2-common.h>
#include <linux/io.h>
#include <media/davinci/vpfe_capture.h>
#include "ccdc_hw_device.h"
static int debug;
static u32 numbuffers = 3;
static u32 bufsize = (720 * 576 * 2);
module_param(numbuffers, uint, S_IRUGO);
module_param(bufsize, uint, S_IRUGO);
module_param(debug, int, 0644);
MODULE_PARM_DESC(numbuffers, "buffer count (default:3)");
MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)");
MODULE_PARM_DESC(debug, "Debug level 0-1");
MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Texas Instruments");
/* standard information */
struct vpfe_standard {
v4l2_std_id std_id;
unsigned int width;
unsigned int height;
struct v4l2_fract pixelaspect;
/* 0 - progressive, 1 - interlaced */
int frame_format;
};
/* ccdc configuration */
struct ccdc_config {
/* This make sure vpfe is probed and ready to go */
int vpfe_probed;
/* name of ccdc device */
char name[32];
};
/* data structures */
static struct vpfe_config_params config_params = {
.min_numbuffers = 3,
.numbuffers = 3,
.min_bufsize = 720 * 480 * 2,
.device_bufsize = 720 * 576 * 2,
};
/* ccdc device registered */
static struct ccdc_hw_device *ccdc_dev;
/* lock for accessing ccdc information */
static DEFINE_MUTEX(ccdc_lock);
/* ccdc configuration */
static struct ccdc_config *ccdc_cfg;
const struct vpfe_standard vpfe_standards[] = {
{V4L2_STD_525_60, 720, 480, {11, 10}, 1},
{V4L2_STD_625_50, 720, 576, {54, 59}, 1},
};
/* Used when raw Bayer image from ccdc is directly captured to SDRAM */
static const struct vpfe_pixel_format vpfe_pix_fmts[] = {
{
.fmtdesc = {
.index = 0,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.description = "Bayer GrRBGb 8bit A-Law compr.",
.pixelformat = V4L2_PIX_FMT_SBGGR8,
},
.bpp = 1,
},
{
.fmtdesc = {
.index = 1,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.description = "Bayer GrRBGb - 16bit",
.pixelformat = V4L2_PIX_FMT_SBGGR16,
},
.bpp = 2,
},
{
.fmtdesc = {
.index = 2,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.description = "Bayer GrRBGb 8bit DPCM compr.",
.pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
},
.bpp = 1,
},
{
.fmtdesc = {
.index = 3,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.description = "YCbCr 4:2:2 Interleaved UYVY",
.pixelformat = V4L2_PIX_FMT_UYVY,
},
.bpp = 2,
},
{
.fmtdesc = {
.index = 4,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.description = "YCbCr 4:2:2 Interleaved YUYV",
.pixelformat = V4L2_PIX_FMT_YUYV,
},
.bpp = 2,
},
{
.fmtdesc = {
.index = 5,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.description = "Y/CbCr 4:2:0 - Semi planar",
.pixelformat = V4L2_PIX_FMT_NV12,
},
.bpp = 1,
},
};
/*
* vpfe_lookup_pix_format()
* lookup an entry in the vpfe pix format table based on pix_format
*/
static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format)
{
int i;
for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) {
if (pix_format == vpfe_pix_fmts[i].fmtdesc.pixelformat)
return &vpfe_pix_fmts[i];
}
return NULL;
}
/*
* vpfe_register_ccdc_device. CCDC module calls this to
* register with vpfe capture
*/
int vpfe_register_ccdc_device(struct ccdc_hw_device *dev)
{
int ret = 0;
printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name);
BUG_ON(!dev->hw_ops.open);
BUG_ON(!dev->hw_ops.enable);
BUG_ON(!dev->hw_ops.set_hw_if_params);
BUG_ON(!dev->hw_ops.configure);
BUG_ON(!dev->hw_ops.set_buftype);
BUG_ON(!dev->hw_ops.get_buftype);
BUG_ON(!dev->hw_ops.enum_pix);
BUG_ON(!dev->hw_ops.set_frame_format);
BUG_ON(!dev->hw_ops.get_frame_format);
BUG_ON(!dev->hw_ops.get_pixel_format);
BUG_ON(!dev->hw_ops.set_pixel_format);
BUG_ON(!dev->hw_ops.set_image_window);
BUG_ON(!dev->hw_ops.get_image_window);
BUG_ON(!dev->hw_ops.get_line_length);
BUG_ON(!dev->hw_ops.getfid);
mutex_lock(&ccdc_lock);
if (NULL == ccdc_cfg) {
/*
* TODO. Will this ever happen? if so, we need to fix it.
* Proabably we need to add the request to a linked list and
* walk through it during vpfe probe
*/
printk(KERN_ERR "vpfe capture not initialized\n");
ret = -EFAULT;
goto unlock;
}
if (strcmp(dev->name, ccdc_cfg->name)) {
/* ignore this ccdc */
ret = -EINVAL;
goto unlock;
}
if (ccdc_dev) {
printk(KERN_ERR "ccdc already registered\n");
ret = -EINVAL;
goto unlock;
}
ccdc_dev = dev;
unlock:
mutex_unlock(&ccdc_lock);
return ret;
}
EXPORT_SYMBOL(vpfe_register_ccdc_device);
/*
* vpfe_unregister_ccdc_device. CCDC module calls this to
* unregister with vpfe capture
*/
void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev)
{
if (NULL == dev) {
printk(KERN_ERR "invalid ccdc device ptr\n");
return;
}
printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n",
dev->name);
if (strcmp(dev->name, ccdc_cfg->name)) {
/* ignore this ccdc */
return;
}
mutex_lock(&ccdc_lock);
ccdc_dev = NULL;
mutex_unlock(&ccdc_lock);
return;
}
EXPORT_SYMBOL(vpfe_unregister_ccdc_device);
/*
* vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings
*/
static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev,
struct v4l2_format *f)
{
struct v4l2_rect image_win;
enum ccdc_buftype buf_type;
enum ccdc_frmfmt frm_fmt;
memset(f, 0, sizeof(*f));
f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
ccdc_dev->hw_ops.get_image_window(&image_win);
f->fmt.pix.width = image_win.width;
f->fmt.pix.height = image_win.height;
f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length()