/*
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
*
* 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 version 2.
*
* This program is distributed WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <asm/pgtable.h>
#include <mach/cputype.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
#include <media/davinci/vpbe_display.h>
#include <media/davinci/vpbe_types.h>
#include <media/davinci/vpbe.h>
#include <media/davinci/vpbe_venc.h>
#include <media/davinci/vpbe_osd.h>
#include "vpbe_venc_regs.h"
#define VPBE_DISPLAY_DRIVER "vpbe-v4l2"
static int debug;
#define VPBE_DEFAULT_NUM_BUFS 3
module_param(debug, int, 0644);
static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
struct vpbe_layer *layer);
static int venc_is_second_field(struct vpbe_display *disp_dev)
{
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
int ret;
int val;
ret = v4l2_subdev_call(vpbe_dev->venc,
core,
ioctl,
VENC_GET_FLD,
&val);
if (ret < 0) {
v4l2_err(&vpbe_dev->v4l2_dev,
"Error in getting Field ID 0\n");
}
return val;
}
static void vpbe_isr_even_field(struct vpbe_display *disp_obj,
struct vpbe_layer *layer)
{
if (layer->cur_frm == layer->next_frm)
return;
layer->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
/* Make cur_frm pointing to next_frm */
layer->cur_frm = layer->next_frm;
}
static void vpbe_isr_odd_field(struct vpbe_display *disp_obj,
struct vpbe_layer *layer)
{
struct osd_state *osd_device = disp_obj->osd_device;
unsigned long addr;
spin_lock(&disp_obj->dma_queue_lock);
if (list_empty(&layer->dma_queue) ||
(layer->cur_frm != layer->next_frm)) {
spin_unlock(&disp_obj->dma_queue_lock);
return;
}
/*
* one field is displayed configure
* the next frame if it is available
* otherwise hold on current frame
* Get next from the buffer queue
*/
layer->next_frm = list_entry(layer->dma_queue.next,
struct vpbe_disp_buffer, list);
/* Remove that from the buffer queue */
list_del(&layer->next_frm->list);
spin_unlock(&disp_obj->dma_queue_lock);
/* Mark state of the frame to active */
layer->next_frm->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE;
addr = vb2_dma_contig_plane_dma_addr(&layer->next_frm->vb.vb2_buf, 0);
osd_device->ops.start_layer(osd_device,
layer->layer_info.id,
addr,
disp_obj->cbcr_ofst);
}
/* interrupt service routine */
static irqreturn_t venc_isr(int irq, void *arg)
{
struct vpbe_display *disp_dev = (struct vpbe_display *)arg;
struct vpbe_layer *layer;
static unsigned last_event;
unsigned event = 0;
int fid;
int i;
if ((NULL == arg) || (NULL == disp_dev->dev[0]))
return IRQ_HANDLED;
if (venc_is_second_field(disp_dev))
event |= VENC_SECOND_FIELD;
else
event |= VENC_FIRST_FIELD;
if (event == (last_event & ~VENC_END_OF_FRAME)) {
/*
* If the display is non-interlaced, then we need to flag the
* end-of-frame event at every interrupt regardless of the
* value of the FIDST bit. We can conclude that the display is
* non-interlaced if the value of the FIDST bit is unchanged
* from the previous interrupt.
*/
event |= VENC_END_OF_FRAME;
} else if (event == VENC_SECOND_FIELD) {
/* end-of-frame for interlaced display */
event |= VENC_END_OF_FRAME;
}
last_event = event;
for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
layer = disp_dev->dev[i];
if (!vb2_start_streaming_called(&layer->buffer_queue))
continue;
if (layer->layer_first_int) {
layer->layer_first_int = 0;
continue;
}
/* Check the field format */
if ((V4L2_FIELD_NONE == layer->pix_fmt.field) &&
(event & VENC_END_OF_FRAME)) {
/* Progressive mode */
vpbe_isr_even_field(disp_dev, layer);
vpbe_isr_odd_field(disp_dev, layer);
} else {
/* Interlaced mode */
layer->field_id ^= 1;
if (event & VENC_FIRST_FIELD)
fid = 0;
else
fid = 1;
/*
* If field id does not match with store
* field id
*/
if (fid != layer->field_id) {
/* Make them in sync */
layer->field_id = fid;
continue;
}
/*
* device field id and local field id are
* in sync. If this is even field
*/
if (0 == fid)
vpbe_isr_even_field(disp_dev, layer);
else /* odd field */
vpbe_isr_odd_field(disp_dev, layer);
}
}
return IRQ_HANDLED;
}
/*
* vpbe_buffer_prepare()
* This is the callback function called from vb2_qbuf() function
* the buffer is prepared and user space virtual address is converted into
* physical address
*/
static int vpbe_buffer_prepare(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
struct vpbe_layer *layer = vb2_get_drv_priv(q);
struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
unsigned long addr;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
"vpbe_buffer_prepare\n");
vb2_set_plane_payload(vb, 0, layer->pix_fmt.sizeimage);
if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
return -EINVAL;
addr = vb2_dma_contig_plane_dma_addr(vb, 0);
if (!IS_ALIGNED(addr, 8)) {
v4l2_err(&vpbe_dev->v4l2_dev,
"buffer_prepare:offset is not aligned to 32 bytes\n");
return -EINVAL;
}
return 0;
}
/*
* vpbe_buffer_setup()
* This function allocates memory for the buffers
*/
static int
vpbe_buffer_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
/* Get the file handle object and layer object */
struct vpbe_layer *layer = vb2_get_drv_priv(vq);
struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
/* Store number of buffers allocated in numbuffer member */
if (v
|