/*
* Analog Devices video capture driver
*
* Copyright (c) 2011 Analog Devices Inc.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/types.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
#include <asm/dma.h>
#include <media/blackfin/bfin_capture.h>
#include <media/blackfin/ppi.h>
#define CAPTURE_DRV_NAME "bfin_capture"
struct bcap_format {
char *desc;
u32 pixelformat;
u32 mbus_code;
int bpp; /* bits per pixel */
int dlen; /* data length for ppi in bits */
};
struct bcap_buffer {
struct vb2_buffer vb;
struct list_head list;
};
struct bcap_device {
/* capture device instance */
struct v4l2_device v4l2_dev;
/* v4l2 control handler */
struct v4l2_ctrl_handler ctrl_handler;
/* device node data */
struct video_device video_dev;
/* sub device instance */
struct v4l2_subdev *sd;
/* capture config */
struct bfin_capture_config *cfg;
/* ppi interface */
struct ppi_if *ppi;
/* current input */
unsigned int cur_input;
/* current selected standard */
v4l2_std_id std;
/* current selected dv_timings */
struct v4l2_dv_timings dv_timings;
/* used to store pixel format */
struct v4l2_pix_format fmt;
/* bits per pixel*/
int bpp;
/* data length for ppi in bits */
int dlen;
/* used to store sensor supported format */
struct bcap_format *sensor_formats;
/* number of sensor formats array */
int num_sensor_formats;
/* pointing to current video buffer */
struct bcap_buffer *cur_frm;
/* buffer queue used in videobuf2 */
struct vb2_queue buffer_queue;
/* allocator-specific contexts for each plane */
struct vb2_alloc_ctx *alloc_ctx;
/* queue of filled frames */
struct list_head dma_queue;
/* used in videobuf2 callback */
spinlock_t lock;
/* used to access capture device */
struct mutex mutex;
/* used to wait ppi to complete one transfer */
struct completion comp;
/* prepare to stop */
bool stop;
/* vb2 buffer sequence counter */
unsigned sequence;
};
static const struct bcap_format bcap_formats[] = {
{
.desc = "YCbCr 4:2:2 Interleaved UYVY",
.pixelformat = V4L2_PIX_FMT_UYVY,
.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.bpp = 16,
.dlen = 8,
},
{
.desc = "YCbCr 4:2:2 Interleaved YUYV",
.pixelformat = V4L2_PIX_FMT_YUYV,
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.bpp = 16,
.dlen = 8,
},
{
.desc = "YCbCr 4:2:2 Interleaved UYVY",
.pixelformat = V4L2_PIX_FMT_UYVY,
.mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
.bpp = 16,
.dlen = 16,
},
{
.desc = "RGB 565",
.pixelformat = V4L2_PIX_FMT_RGB565,
.mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.bpp = 16,
.dlen = 8,
},
{
.desc = "RGB 444",
.pixelformat = V4L2_PIX_FMT_RGB444,
.mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE,
.bpp = 16,
.dlen = 8,
},
};
#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
static irqreturn_t bcap_isr(int irq, void *dev_id);
static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb)
{
return container_of(vb, struct bcap_buffer, vb);
}
static int bcap_init_sensor_formats(struct bcap_device *bcap_dev)
{
struct v4l2_subdev_mbus_code_enum code = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
struct bcap_format *sf;
unsigned int num_formats = 0;
int i, j;
while (!v4l2_subdev_call(bcap_dev->sd, pad,
enum_mbus_code, NULL, &code)) {
num_formats++;
code.index++;
}
if (!num_formats)
return -ENXIO;
sf = kzalloc(num_formats * sizeof(*sf), GFP_KERNEL);