/*
* Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file mx27_v4l2_capture.c
*
* @brief MX27 Video For Linux 2 driver
*
* @ingroup MXC_V4L2_CAPTURE
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
#include <linux/types.h>
#include <linux/fb.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <media/v4l2-dev.h>
#include <asm/io.h>
#include <asm/semaphore.h>
#include "mxc_v4l2_capture.h"
#include "mx27_prp.h"
#include "mx27_csi.h"
static int csi_mclk_flag_backup;
static int video_nr = -1;
extern cam_data *g_cam;
static int dq_intr_cnt = 0;
static int dq_timeout_cnt = 0;
static int empty_wq_cnt = 0;
/*!
* Free frame buffers
*
* @param cam Structure cam_data *
*
* @return status 0 success.
*/
static int mxc_free_frame_buf(cam_data * cam)
{
int i;
for (i = 0; i < FRAME_NUM; i++) {
if (cam->frame[i].vaddress != 0) {
dma_free_coherent(0,
cam->frame[i].buffer.length,
cam->frame[i].vaddress,
cam->frame[i].paddress);
cam->frame[i].vaddress = 0;
}
}
return 0;
}
/*!
* Allocate frame buffers
*
* @param cam Structure cam_data *
*
* @param count int number of buffer need to allocated
*
* @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
*/
static int mxc_allocate_frame_buf(cam_data * cam, int count)
{
int i;
for (i = 0; i < count; i++) {
cam->frame[i].vaddress = dma_alloc_coherent(0,
PAGE_ALIGN(cam->v2f.
fmt.pix.
sizeimage),
&cam->frame[i].
paddress,
GFP_DMA |
GFP_KERNEL);
if (cam->frame[i].vaddress == 0) {
pr_debug("mxc_allocate_frame_buf failed.\n");
mxc_free_frame_buf(cam);
return -ENOBUFS;
}
cam->frame[i].buffer.index = i;
cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cam->frame[i].buffer.length =
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;
cam->frame[i].buffer.m.offset = cam->frame[i].paddress;
cam->frame[i].index = i;
}
return 0;
}
/*!
* Free frame buffers status
*
* @param cam Structure cam_data *
*
* @return none
*/
static void mxc_free_frames(cam_data * cam)
{
int i;
for (i = 0; i < FRAME_NUM; i++) {
cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
}
cam->enc_counter = 0;
cam->skip_frame = 0;
INIT_LIST_HEAD(&cam->ready_q);
INIT_LIST_HEAD(&cam->working_q);
INIT_LIST_HEAD(&cam->done_q);
}
/*!
* Return the buffer status
*
* @param cam Structure cam_data *
* @param buf Structure v4l2_buffer *
*
* @return status 0 success, EINVAL failed.
*/
static int mxc_v4l2_buffer_status(cam_data * cam, struct v4l2_buffer *buf)
{
/* check range */
if (buf->index < 0 || buf->index >= FRAME_NUM) {
pr_debug("mxc_v4l2_buffer_status buffers not allocated\n");
return -EINVAL;
}
memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));
return 0;
}
/*!
* start the encoder job
*
* @param cam structure cam_data *
*
* @return status 0 Success
*/
static int mxc_streamon(cam_data * cam)
{
struct mxc_v4l_frame *frame;
int err = 0;
if (!cam)
return -EIO;
if (list_empty(&cam->ready_q)) {
printk(KERN_ERR "mxc_streamon buffer not been queued yet\n");
return -EINVAL;
}
cam->capture_pid = current->pid;
if (cam->enc_enable) {
err = cam->enc_enable(cam);
if (err != 0) {
return err;
}
}
cam->ping_pong_csi = 0;
if (cam->enc_update_eba) {
frame =
list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
list_del(cam->ready_q.next);
list_add_tail(&frame->queue, &cam->working_q);
err = cam->enc_update_eba(frame->paddress, &cam->ping_pong_csi);
frame =
list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
list_del(cam->ready_q.next);
list_add_tail(&frame->queue, &cam->working_q);
err |=
cam->enc_update_eba(frame->paddress, &cam->ping_pong_csi);
} else {
return -EINVAL;
}
return err;
}
/*!
* Shut down the encoder job
*
* @param cam structure cam_data *
*
* @return status 0 Success
*/
static int mxc_streamoff(cam_data * cam)
{
int err = 0;
if (!cam)
return -EIO;
if (cam->enc_disable) {
err = cam->enc_disable(cam);
}
mxc_free_frames(cam);
return err;
}
/*!
* Valid whether the palette is supported
*
* @param palette pixel format
*
* @return 0 if failed
*/
static inline int valid_mode(u32 palette)
{
/*
* MX27 PrP channel 2 supports YUV444, but YUV444 is not
* defined by V4L2 :(
*/
return ((palette == V4L2_PIX_FMT_YUYV) ||
(palette == V4L2_PIX_FMT_YUV420));
}
/*!
* Valid and adjust the overlay window size, position
*
* @param cam structure cam_data *
* @param win struct v4l2_window *
*
* @return 0
*/
static int verify_preview(cam_data * cam, struct v4l2_window *win)
{
if (cam->output >= num_registered_fb) {
pr_debug("verify_preview No matched.\n");
return -1;
}
cam->overlay_fb = (struct fb_info *)registered_fb[cam->output];
/* TODO: suppose 16bpp, 4 bytes alignment */
win->w.left &= ~0x1;
if (win->w.width + win->w.left > cam->overlay_fb->var.xres)
win->w.width = cam->overlay_fb->var.xres - win->w.left;
if (win->w.height + win->w.top > cam->overlay_fb->var.yres)
win->w.height = cam->overlay_fb->var.yres - win->w.top;
/*
* TODO: suppose 16bpp. Rounded down to a multiple of 2 pixels for
* width according to PrP limitations.
*/
if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT)
|| (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP)
|| (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP)
|| (cam->rotation == V4L2_MXC_ROTATE_90_LEFT))
win->w.height &= ~0x1;
else
win->w.width &= ~0x1;
return 0;
}
/*!
* start the viewfinder job
*
* @param cam structure cam_data *
*
* @return status 0 Success
*/
static int start_preview(cam_data * cam)
{
int err = 0;
err = prp_vf_select(cam);
if (err != 0)
return err;
cam->overlay_pid = current->pid;
err = cam->vf_start_sdc(cam);
return err;
}
/*!
* shut down the viewfinder job
*
* @param cam structure cam_data *
*
* @return status 0 Success
*/
static int stop_preview(cam_data * cam)
{
int err = 0;
err = prp_vf_deselect(cam);
return err;
}
/*!
* V4L2 - mxc_v4l2_g_fmt function
*
* @param cam structure cam_data *
*
* @param f structure v4l2_format *
*
* @return status 0 success, EINVAL failed
*/
static int mxc_v4l2_g_fmt(cam_data * cam, struct v4l2_format *f)
{
int retval = 0;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
f->fmt.pix.width = cam->v2f.fmt.pix.width;
f->fmt.pix.height = cam->v2f.fmt.pix.height;
f->fmt.pix.sizeimage = cam->v2f.fmt.pix.sizeimage;
f->fmt.pix.pixelformat = cam->v2f.fmt.pix.pixelformat;
f->fmt.pix.bytesperline = cam->v2f.fmt.pix.bytesperline;
f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
retval = 0;
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
f->fmt.win = cam->win;
break;
default:
retval = -EINVAL;
}
return retval;
}
/*!
* V4L2 - mxc_v4l2_s_fmt function
*
* @param cam structure cam_data *
*
* @param f structure v4l2_format *
*
* @return status 0 success, EINVAL failed
*/
static int mxc_v4l2_s_fmt(cam_data * cam, struct v4l2_format *f)
{
int retval = 0;
int size = 0;
int bytesperline = 0;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (!valid_mode(f->fmt.pix.pixelformat)) {
pr_debug("mxc_v4l2_s_fmt: format not supported\n");
retval = -EINVAL;