/*
*
* *
* TODO : add support for VBI & HBI data service
* add static buffer allocation
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/string.h>
#include <linux/videodev2.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include "vpif_capture.h"
#include "vpif.h"
MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver");
MODULE_LICENSE("GPL");
#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
#define vpif_dbg(level, debug, fmt, arg...) \
v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)
static int debug = 1;
static u32 ch0_numbuffers = 3;
static u32 ch1_numbuffers = 3;
static u32 ch0_bufsize = 1920 * 1080 * 2;
static u32 ch1_bufsize = 720 * 576 * 2;
module_param(debug, int, 0644);
module_param(ch0_numbuffers, uint, S_IRUGO);
module_param(ch1_numbuffers, uint, S_IRUGO);
module_param(ch0_bufsize, uint, S_IRUGO);
module_param(ch1_bufsize, uint, S_IRUGO);
MODULE_PARM_DESC(debug, "Debug level 0-1");
MODULE_PARM_DESC(ch2_numbuffers, "Channel0 buffer count (default:3)");
MODULE_PARM_DESC(ch3_numbuffers, "Channel1 buffer count (default:3)");
MODULE_PARM_DESC(ch2_bufsize, "Channel0 buffer size (default:1920 x 1080 x 2)");
MODULE_PARM_DESC(ch3_bufsize, "Channel1 buffer size (default:720 x 576 x 2)");
static struct vpif_config_params config_params = {
.min_numbuffers = 3,
.numbuffers[0] = 3,
.numbuffers[1] = 3,
.min_bufsize[0] = 720 * 480 * 2,
.min_bufsize[1] = 720 * 480 * 2,
.channel_bufsize[0] = 1920 * 1080 * 2,
.channel_bufsize[1] = 720 * 576 * 2,
};
/* global variables */
static struct vpif_device vpif_obj = { {NULL} };
static struct device *vpif_dev;
/**
* ch_params: video standard configuration parameters for vpif
*/
static const struct vpif_channel_config_params ch_params[] = {
{
"NTSC_M", 720, 480, 30, 0, 1, 268, 1440, 1, 23, 263, 266,
286, 525, 525, 0, 1, 0, V4L2_STD_525_60,
},
{
"PAL_BDGHIK", 720, 576, 25, 0, 1, 280, 1440, 1, 23, 311, 313,
336, 624, 625, 0, 1, 0, V4L2_STD_625_50,
},
};
/**
* vpif_uservirt_to_phys : translate user/virtual address to phy address
* @virtp: user/virtual address
*
* This inline function is used to convert user space virtual address to
* physical address.
*/
static inline u32 vpif_uservirt_to_phys(u32 virtp)
{
unsigned long physp = 0;
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
vma = find_vma(mm, virtp);
/* For kernel direct-mapped memory, take the easy way */
if (virtp >= PAGE_OFFSET)
physp = virt_to_phys((void *)virtp);
else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff))
/**
* this will catch, kernel-allocated, mmaped-to-usermode
* addresses
*/
physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
else {
/* otherwise, use get_user_pages() for general userland pages */
int res, nr_pages = 1;
struct page *pages;
down_read(¤t->mm->mmap_sem);
res = get_user_pages(current, current->mm,
virtp, nr_pages, 1, 0, &pages, NULL);
up_read(¤t->mm->mmap_sem);
if (res == nr_pages)
physp = __pa(page_address(&pages[0]) +
(virtp & ~PAGE_MASK));
else {
vpif_err("get_user_pages failed\n");
return 0;
}
}
return physp;
}
/**
* buffer_prepare : callback function for buffer prepare
* @q : buffer queue ptr
* @vb: ptr to video buffer
* @field: field info
*
* This is the callback function for buffer prepare when videobuf_qbuf()
* function is called. The buffer is prepared and user space virtual address
* or user address is converted into physical address
*/
static int vpif_buffer_prepare(struct videobuf_queue *q,
struct videobuf_buffer *vb,
enum v4l2_field field)
{
/* Get the file handle object and channel object */
struct vpif_fh *fh = q->priv_data;
struct channel_obj *ch = fh->channel;
struct common_obj *common;
unsigned long addr;
vpif_dbg(2, debug, "vpif_buffer_prepare\n");
common = &ch->common[VPIF_VIDEO_INDEX];
/* If buffer is not initialized, initialize it */
if (VIDEOBUF_NEEDS_INIT == vb->state) {
vb->width = common->width;
vb->height = common->height;
vb->size = vb->width * vb->height;
vb->field = field;
}
vb->state = VIDEOBUF_PREPARED;
/**
* if user pointer memory mechanism is used, get the physical
* address of the buffer
*/
if (V4L2_MEMORY_USERPTR == common->memory) {
if (0 == vb->baddr) {
vpif_dbg(1, debug, "buffer address is 0\n");
return -EINVAL;
}
vb->boff = vpif_uservirt_to_phys(vb->baddr);
if (!IS_ALIGNED(vb->boff, 8))
goto exit;
}
addr = vb->boff;
if (q->streaming) {
if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
!IS_ALIGNED((addr + common->ybtm_off), 8) ||
!IS_ALIGNED((addr + common->ctop_off), 8) ||
!IS_ALIGNED((addr + common->cbtm_off), 8))
goto exit;
}
return 0;
exit:
vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n");
return -EINVAL;
}
/**
* vpif_buffer_setup : Callback function for buffer setup.
* @q: buffer queue ptr
* @count: number of buffers
* @size: size of the buffer
*
* This callback function is called when reqbuf() is called to adjust
* the buffer count and buffer size
*/
static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
unsigned int *size)
{
/* Get the file handle object and channel object */
struct vpif_fh *fh = q->priv_data;
struct channel_obj *ch = fh->channel;
struct common_obj *common;
common = &ch->common[VPIF_VIDEO_INDEX];
vpif_dbg(2, debug, "vpif_buffer_setup\n");
/* If memory type is not mmap, return */
if (V4L2_MEMORY_MMAP != common->memory)
return 0;
/* Calculate the size of the buffer */
*size = config_params.channel_bufsize[ch->channel_id];
if (*count < config_params.min_numbuffers)
*count = config_params.min_numbuffers;
return 0;
}
/**
* vpif_buffer_queue : Callback function to add buffer to DMA queue
* @q: ptr to videobuf_queue
* @vb: ptr to videobuf_buffer
*/
static void vpif_buffer_queue(struct videobuf_queue *q,
struct videobuf_buffer *vb)
{
/* Get the file handle object and channel object */
struct vpif_fh *fh = q->priv_data;
struct channel_obj *ch = fh->channel;
struct common_obj *common;
common = &ch->common[VPIF_VIDEO_INDEX];
vpif_dbg(2, debug, "vpif_buffer_queue\n");
/* add the buffer to the DMA queue */
list_add_tail(&vb->queue, &common->dma_queue);
/* Change state of the buffer */
vb->state = VIDEOBUF_QUEUED;
}
/**
* vpif_buffer_release : Callback function to free buffer
* @q: buffer queue ptr
* @vb: ptr to video buffer
*
* This function is called from the videobuf layer to free memory
* allocated to the buffers
*/
static void vpif_buffer_release(struct videobuf_queue *q,
struct videobuf_buffer *vb)
{
/* Get the file handle object and channel object */
struct vpif_fh *fh = q->priv_data;
struct channel_obj *ch = fh->channel;
struct common_obj *common;
common = &ch->common[VPIF_VIDEO_INDEX];
videobuf_dma_contig_free(q, vb);
vb->state = VIDEOBUF_NEEDS_INIT;
}
static struct videobuf_queue_ops video_qops = {
.buf_setup = vpif_buffer_setup,
.buf_prepare = vpif_buffer_prepare,
.buf_queue = vpif_buffer_queue,
.buf_release = vpif_buffer_release,
};
static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
{ {1, 1} };
/**
* vpif_process_buffer_complete: process a completed buffer
* @common: ptr to common channel object
*
* This function time stamp the buffer and mark it as DONE. It also
* wake up any process waiting on the QUEUE and set the next buffer
* as current
*/
static void vpif_process_buffer_complete(struct common_obj *common)
{
do_gettimeofday(&common->cur_frm->ts);
common->cu