| From: |
| "Syed Mohammed, Khasim" <x0khasim@ti.com> |
| To: |
| <video4linux-list@redhat.com> |
| Subject: |
| [Linux V4L] [RFC] OMAP Video out Driver |
| Date: |
| Wed, 7 Mar 2007 22:57:42 -0600 |
| Archive-link: |
| Article,
Thread
|
Hello,
I am here by presenting a Video driver (preliminary version) for TI's
OMAP Display Sub System (Video pipeline). We implemented this driver in
2.6.10, I am in the process of migrating it to 2.6.20 with new V4L2
changes. For now, I am presenting only part of the actual functionality
to help others to review and provide necessary suggestions and comments.
The OMAP part of this is being discussed on the respective list.
It would be very kind of you if you can review this piece of code and
provide your valuable comments for the same.
Some info regarding this driver: OMAP2 (&3) Display controller has two
Identical Video pipelines to render Video of RGB565/YUYV/UYVY/RGB24
(packed/unpacked) format onto LCD or TV. Hardware also supports Scaling,
Mirroring, Rotation, Windowing, Cropping, Transparency color Key, etc.
For now, this code provides some these functionalities others will be
added later.
Thanks in advance,
Khasim
=============================================================
/*
* drivers/media/video/omap_videout.c
*
* Copyright (C) 2005-2006 Texas Instruments.
*
* This file is licensed under the terms of the GNU General Public
License
* version 2. This program is licensed "as is" without any warranty of
any
* kind, whether express or implied.
*
* Initial code was leveraged from OMAP2 camera driver
* by Andy Lowe (source@mvista.com)
*
* Copyright (C) 2004 MontaVista Software, Inc.
* Copyright (C) 2004 Texas Instruments.
*
* Code re-written for OMAP Video pipelines
* Jian Zhang (jzhang@ti.com)
* Syed Mohammed Khasim (x0khasim@ti.com)
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/videodev.h>
#include <media/video-buf.h>
#include <linux/input.h>
#include <linux/dma-mapping.h>
#include <media/v4l2-dev.h>
#include <asm/arch/display.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/semaphore.h>
#include <asm/processor.h>
#include <asm/arch/bus.h>
#include <asm/arch/dma.h>
#include "omap24xxlib.h"
#include "omap24xxvoutdef.h"
extern const char *global_mode_option;
/*
* Uncomment this if debugging support needs to be enabled
*/
/* #define DEBUG */
#undef DEBUG
#ifdef DEBUG
#define DPRINTK(ARGS...) printk("<%s>: ",__FUNCTION__);printk(ARGS)
#else
#define DPRINTK( x... )
#endif
/* configuration macros */
#define VOUT_NAME "omap24xxvout"
#define V1OUT_NAME "omap24xxvout1"
#define V2OUT_NAME "omap24xxvout2"
#define VGA_WIDTH 640
#define VGA_HEIGHT 480
#define QQVGA_WIDTH 160
#define QQVGA_HEIGHT 120
#define BYTESPERPIXEL 2
#define NUM_OF_VIDEO_CHANNELS 2
static struct omap24xxvout_device *saved_v1out, *saved_v2out;
static spinlock_t vout_link_lock;
static struct videobuf_queue_ops dummy_vbq_ops;
/* module parameters */
/*
* Maximum amount of memory to use for rendering buffers.
* Default is enough to four (RGB24) VGA buffers.
*/
#define MAX_ALLOWED_VIDBUFFERS 4
static int render_mem = VGA_WIDTH * VGA_HEIGHT * 4 *
MAX_ALLOWED_VIDBUFFERS;
/* list of image formats supported by OMAP2 video pipelines */
const static struct v4l2_fmtdesc omap2_formats[] = {
{
.description = "RGB565, le",
.pixelformat = V4L2_PIX_FMT_RGB565,
},
{
.description = "RGB565, be",
.pixelformat = V4L2_PIX_FMT_RGB565X,
},
{
.description = "RGB32, le",
.pixelformat = V4L2_PIX_FMT_RGB32,
},
{
.description = "RGB24, le",
.pixelformat = V4L2_PIX_FMT_RGB24,
},
{
.description = "YUYV (YUV 4:2:2), packed",
.pixelformat = V4L2_PIX_FMT_YUYV,
},
{
.description = "UYVY, packed",
.pixelformat = V4L2_PIX_FMT_UYVY,
},
};
#define NUM_OUTPUT_FORMATS
(sizeof(omap2_formats)/sizeof(omap2_formats[0]))
/* ---------------------------------------------------------------------
*/
static int
try_format (struct v4l2_pix_format *pix)
{
int ifmt,bpp =0;
if (pix->width > VGA_WIDTH)
pix->width = VGA_WIDTH;
if (pix->height > VGA_HEIGHT)
pix->height = VGA_HEIGHT;
for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++){
if (pix->pixelformat == omap2_formats[ifmt].pixelformat)
break;
}
if (ifmt == NUM_OUTPUT_FORMATS)
ifmt = 0;
pix->pixelformat = omap2_formats[ifmt].pixelformat;
pix->field = V4L2_FIELD_NONE;
pix->priv = 0;
switch (pix->pixelformat){
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
default:
pix->colorspace = V4L2_COLORSPACE_JPEG;
bpp = YUYV_BPP;
break;
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_RGB565X:
pix->colorspace = V4L2_COLORSPACE_SRGB;
bpp = RGB565_BPP;
break;
case V4L2_PIX_FMT_RGB24:
pix->colorspace = V4L2_COLORSPACE_SRGB;
bpp = RGB24_BPP;
break;
case V4L2_PIX_FMT_RGB32:
pix->colorspace = V4L2_COLORSPACE_SRGB;
bpp = RGB32_BPP;
break;
}
pix->bytesperline = pix->width * bpp;
pix->sizeimage = pix->bytesperline * pix->height;
return(bpp);
}
/*
* NOTE: THIS IOCTL WILL BE CHANGED TO NEW STYLE
*/
static int
omap24xxvout_do_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
struct omap24xxvout_fh *fh = (
struct omap24xxvout_fh *)file->private_data;
struct omap24xxvout_device *vout = fh->vout;
int err;
switch (cmd){
case VIDIOC_ENUMOUTPUT:
{
struct v4l2_output *output = (struct v4l2_output *) arg;
int index = output->index;
if (index > 0)
return -EINVAL;
memset (output, 0, sizeof (*output));
output->index = index;
strncpy (output->name, "video out", sizeof
(output->name));
output->type = V4L2_OUTPUT_TYPE_MODULATOR;
return 0;
}
case VIDIOC_G_OUTPUT:
{
unsigned int *output = arg;
*output = 0;
return 0;
}
case VIDIOC_S_OUTPUT:
{
unsigned int *output = arg;
if (*output > 0)
return -EINVAL;
return 0;
}
case VIDIOC_QUERYCAP:
{
struct v4l2_capability *cap = (struct v4l2_capability *)
arg;
memset (cap, 0, sizeof (*cap));
strncpy (cap->driver, VOUT_NAME, sizeof (cap->driver));
strncpy (cap->card, vout->vfd->name, sizeof
(cap->card));
cap->bus_info[0] = '\0';
cap->capabilities = V4L2_CAP_STREAMING |
V4L2_CAP_VIDEO_OUTPUT;
return 0;
}
case VIDIOC_ENUM_FMT:
{
struct v4l2_fmtdesc *fmt = arg;
int index = fmt->index;
enum v4l2_buf_type type = fmt->type;
memset (fmt, 0, sizeof (*fmt));
fmt->index = index;
fmt->type = type;
switch (fmt->type){
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
if (index >= NUM_OUTPUT_FORMATS)
return -EINVAL;
break;
default:
return -EINVAL;
}
fmt->flags = omap2_formats[index].flags;
strncpy (fmt->description,
omap2_formats[index].description,
sizeof (fmt->description));
fmt->pixelformat = omap2_formats[index].pixelformat;
return 0;
}
case VIDIOC_G_FMT:
{
struct v4l2_format *f = (struct v4l2_format *) arg;
switch (f->type){
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
{
struct v4l2_pix_format *pix = &f->fmt.pix;
memset (pix, 0, sizeof (*pix));
*pix = vout->pix;
return 0;
}
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
{
struct v4l2_window *win = &f->fmt.win;
memset (win, 0, sizeof (*win));
/*
* The API has a bit of a problem here.
* We're returning a v4l2_window
* structure, but that structure
* contains pointers to variable-sized
* objects for clipping rectangles and
* clipping bitmaps. We will just
* return NULLs for those pointers.
*/
win->w = vout->win.w;
win->field = vout->win.field;
win->chromakey = vout->win.chromakey;
return 0;
}
default:
return -EINVAL;
}
}
case VIDIOC_TRY_FMT:
{
struct v4l2_format *f = (struct v4l2_format *) arg;
if (vout->streaming)
return -EBUSY;
switch (f->type){
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
{
struct v4l2_window *win = &f->fmt.win;
err = omap24xxvout_try_window (&vout->fbuf,
win);
return err;
}
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
{
/* don't allow to change img for the linked
layer */
if (vout->vid == vout_linked)
return -EINVAL;
try_format (&f->fmt.pix);
return 0;
}
default:
return -EINVAL;
}
}
case VIDIOC_S_FMT:
{
struct v4l2_format *f = (struct v4l2_format *) arg;
if (vout->streaming)
return -EBUSY;
omap2_disp_get_panel_size (
omap2_disp_get_output_dev (vout->vid),
&(vout->fbuf.fmt.width),
&(vout->fbuf.fmt.height));
switch (f->type){
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
{
struct v4l2_window *win = &f->fmt.win;
err = omap24xxvout_new_window (&vout->crop,
&vout->win,
&vout->fbuf, win);
return err;
}
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
{
int bpp;
/* change to samller size is OK */
bpp = try_format (&f->fmt.pix);
f->fmt.pix.sizeimage =
f->fmt.pix.width * f->fmt.pix.height
*bpp;
/* try & set the new output format */
vout->bpp = bpp;
vout->pix = f->fmt.pix;
vout->vrfb_bpp = 1;
/* set default crop and win */
omap24xxvout_new_format
(&vout->pix, &vout->fbuf, &vout->crop,
&vout->win);
return 0;
}
default:
return -EINVAL;
}
}
case VIDIOC_CROPCAP:
{
struct v4l2_cropcap *cropcap = (struct v4l2_cropcap *)
arg;
enum v4l2_buf_type type = cropcap->type;
memset (cropcap, 0, sizeof (*cropcap));
cropcap->type = type;
switch (type){
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
{
struct v4l2_pix_format *pix = &vout->pix;
cropcap->bounds.width = pix->width & ~1;
cropcap->bounds.height = pix->height & ~1;
omap24xxvout_default_crop (&vout->pix,
&vout->fbuf,
&cropcap->defrect);
cropcap->pixelaspect.numerator = 1;
cropcap->pixelaspect.denominator = 1;
return 0;
}
default:
return -EINVAL;
}
}
case VIDIOC_G_CROP:
{
struct v4l2_crop *crop = (struct v4l2_crop *) arg;
switch (crop->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
{
crop->c = vout->crop;
return 0;
}
default:
return -EINVAL;
}
}
case VIDIOC_S_CROP:
{
struct v4l2_crop *crop = (struct v4l2_crop *) arg;
if (vout->streaming)
return -EBUSY;
omap2_disp_get_panel_size
(omap2_disp_get_output_dev (vout->vid),
&(vout->fbuf.fmt.width),
&(vout->fbuf.fmt.height));
switch (crop->type){
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
{
err =
omap24xxvout_new_crop
(&vout->pix, &vout->crop, &vout->win,
&vout->fbuf, &crop->c);
return err;
}
default:
return -EINVAL;
}
}
case VIDIOC_REQBUFS:
{
struct v4l2_requestbuffers *req =
(struct v4l2_requestbuffers *) arg;
struct videobuf_queue *q = &fh->vbq;
unsigned int phy_addr, virt_addr;
unsigned int count, i;
/* don't allow to buffer request for the linked layer */
if (vout->vid == vout_linked)
return -EINVAL;
if (req->count > VIDEO_MAX_FRAME)
req->count = VIDEO_MAX_FRAME;
/* we allow req->count == 0 */
if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
|| (req->count <
0))
return -EINVAL;
if (req->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
if (vout->streaming)
return -EBUSY;
/* reuse old buffers */
if (vout->buffer_allocated && vout->buffer_size >=
vout->pix.sizeimage){
if (req->count) {
req->count = vout->buffer_allocated;
return 0;
}
}
/*
* We allow re-request as long as old buffers are not
mmaped.
* We will need to free old buffers first.
*/
/* either no buffer allocated or too small */
if (vout->buffer_allocated){
if (vout->mmap_count)
return -EBUSY;
mutex_lock(&q->lock);
for (i = 0; i < vout->buffer_allocated; i++){
dma_free_coherent(NULL,
vout->buffer_size,
(void *)
q->bufs[i]->dma.vmalloc,
q->bufs[i]->dma.bus_addr);
q->bufs[i] = NULL;
}
vout->buffer_allocated = 0;
vout->buffer_size = 0;
mutex_unlock(&q->lock);
videobuf_mmap_free(q);
}
/* user intends to free buffer */
if (!req->count)
return 0;
mutex_lock(&q->lock);
count = req->count;
while (PAGE_ALIGN(vout->pix.sizeimage) * count >
render_mem)
count--;
vout->buffer_size = PAGE_ALIGN(vout->pix.sizeimage);
for (i = 0; i < count; i++) {
virt_addr =
(unsigned int)dma_alloc_coherent(NULL,
vout->buffer_size,
(dma_addr_t *) & phy_addr,
GFP_KERNEL | GFP_DMA);
if (!virt_addr)
break;
memset((void *) virt_addr, 0,
vout->buffer_size);
DPRINTK("REQBUFS: buffer %d: virt=0x%x,
phy=0x%x,
size=0x%x\n", i, virt_addr, phy_addr,
vout->buffer_size);
q->bufs[i] = videobuf_alloc (q->msize);
if (q->bufs[i] == NULL){
dma_free_coherent(NULL,
vout->buffer_size,
(void *) virt_addr,
(dma_addr_t) phy_addr);
break;
}
q->bufs[i]->i = i;
q->bufs[i]->input = UNSET;
q->bufs[i]->memory = req->memory;
q->bufs[i]->bsize = vout->buffer_size;
q->bufs[i]->boff = vout->buffer_size * i;
q->bufs[i]->dma.vmalloc = (void *) (virt_addr);
q->bufs[i]->dma.bus_addr = phy_addr;
q->bufs[i]->state = STATE_PREPARED;
vout->buf_virt_addr[i] = (unsigned long)
q->bufs[i]->dma.vmalloc;
vout->buf_phy_addr[i] =
q->bufs[i]->dma.bus_addr;
vout->buffer_allocated++;
}
vout->buf_memory_type = req->memory;
/* return -ENOMEM if we are not able to allocate a
single buffer
* we have to seperate the case which user requests 0
buffer
* means user wants to free the already allocated
buffers
*/
if (vout->buffer_allocated == 0 && req->count != 0){
mutex_unlock(&q->lock);
vout->buffer_size = 0;
return -ENOMEM;
}
req->count = vout->buffer_allocated;
mutex_unlock(&q->lock);
return 0;
}
case VIDIOC_QUERYBUF:
return videobuf_querybuf (&fh->vbq, arg);
case VIDIOC_QBUF:
{
struct v4l2_buffer *buffer = (struct v4l2_buffer *) arg;
struct videobuf_queue *q = &fh->vbq;
if (buffer->index >= vout->buffer_allocated)
return -EINVAL;
DPRINTK ("queuing buffer %d ...\n", buffer->index);
if (!vout->streaming)
return -EINVAL;
mutex_lock(&q->lock);
omap2_disp_start_vlayer (vout->vid, &vout->pix,
&vout->crop,
q->bufs[buffer->index]->dma.bus_addr,-1,0);
mutex_unlock(&q->lock);
return 0;
}
case VIDIOC_DQBUF:
{
/*
* At present we want user to DQ based on the Audio rate
*/
return 0;
}
case VIDIOC_STREAMON:
{
if (vout->streaming)
return -EBUSY;
/* set flag here. Next QBUF will start DMA */
vout->streaming = fh;
/* DMA will be started by QBUF */
omap2_disp_config_vlayer (vout->vid, &vout->pix,
&vout->crop,
&vout->win, -1, 0);
return 0;
}
case VIDIOC_STREAMOFF:
{
/*
* only allow the file handler that started streaming to
* stop streaming
*/
if (vout->streaming == fh){
omap2_disp_disable_layer (vout->vid);
vout->streaming = NULL;
return 0;
}
else
return -EINVAL;
}
}
return 0;
}
static void
omap24xxvout_vm_open (struct vm_area_struct *vma)
{
struct omap24xxvout_device *vout = vma->vm_private_data;
DPRINTK ("vm_open [vma=%08lx-%08lx]\n", vma->vm_start,
vma->vm_end);
vout->mmap_count++;
}
static void
omap24xxvout_vm_close (struct vm_area_struct *vma)
{
struct omap24xxvout_device *vout = vma->vm_private_data;
DPRINTK ("vm_close [vma=%08lx-%08lx]\n", vma->vm_start,
vma->vm_end);
vout->mmap_count--;
}
static struct vm_operations_struct omap24xxvout_vm_ops = {
.open = omap24xxvout_vm_open,
.close = omap24xxvout_vm_close,
};
static int
omap24xxvout_mmap (struct file *file, struct vm_area_struct *vma)
{
struct omap24xxvout_fh *fh = file->private_data;
struct omap24xxvout_device *vout = fh->vout;
struct videobuf_queue *q = &fh->vbq;
int size, i;
DPRINTK ("pgoff=0x%x, start=0x%x, end=0x%x\n", vma->vm_pgoff,
vma->vm_start, vma->vm_end);
size = vma->vm_end - vma->vm_start;
mutex_lock(&q->lock);
/* look for the buffer to map */
for (i = 0; i < VIDEO_MAX_FRAME; i++){
if (NULL == q->bufs[i]) continue;
if (V4L2_MEMORY_MMAP != q->bufs[i]->memory)
continue;
if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT))
break;
}
if (VIDEO_MAX_FRAME == i){
DPRINTK ("offset invalid [offset=0x%lx]\n",
(vma->vm_pgoff << PAGE_SHIFT));
mutex_unlock(&q->lock);
return -EINVAL;
}
vma->vm_flags |= VM_RESERVED;
vma->vm_page_prot = pgprot_writecombine (vma->vm_page_prot);
vma->vm_ops = &omap24xxvout_vm_ops;
vma->vm_private_data = (void *) vout;
if (remap_pfn_range(vma, vma->vm_start,
q->bufs[i]->dma.bus_addr>>PAGE_SHIFT, size,
vma->vm_page_prot)){
mutex_unlock(&q->lock);
return -EAGAIN;
}
vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
//vma->vm_flags &= ~VM_PFNMAP;
mutex_unlock(&q->lock);
vout->mmap_count++;
return 0;
}
static int
omap24xxvout_ioctl (struct inode *inode, struct file *file, unsigned int
cmd,
unsigned long arg)
{
return video_usercopy (inode, file, cmd, arg,
omap24xxvout_do_ioctl);
}
static int
omap24xxvout_release (struct inode *inode, struct file *file)
{
struct omap24xxvout_fh *fh = file->private_data;
struct omap24xxvout_device *vout;
struct videobuf_queue *q;
DPRINTK ("Entering release\n");
if (fh == 0)
return 0;
if ((vout = fh->vout) == 0)
return 0;
q = &fh->vbq;
omap2_disp_disable_layer (vout->vid);
if (vout->streaming == fh) vout->streaming = NULL;
if (vout->mmap_count != 0){
vout->mmap_count = 0;
printk("mmap count is not zero!\n");
}
omap2_disp_release_layer (vout->vid);
omap2_disp_put_dss ();
vout->opened -= 1;
file->private_data = NULL;
if (vout->buffer_allocated)
videobuf_mmap_free(q);
kfree (fh);
/* need to remove the link when the either slave or master is
gone */
spin_lock (&vout_link_lock);
spin_unlock (&vout_link_lock);
return 0;
}
/* define this to allow videobuf_mmap_free to work */
static void dummy_vbq_release(struct videobuf_queue *q, struct
videobuf_buffer *vb)
{
}
static int
omap24xxvout_open (struct inode *inode, struct file *file)
{
int minor = MINOR (file->f_dentry->d_inode->i_rdev);
struct omap24xxvout_device *vout = NULL;
struct omap24xxvout_fh *fh;
struct videobuf_queue *q;
int i;
DPRINTK ("entering\n");
if (saved_v1out && saved_v1out->vfd &&
(saved_v1out->vfd->minor == minor)){
vout = saved_v1out;
}
if (vout == NULL){
if (saved_v2out && saved_v2out->vfd
&& (saved_v2out->vfd->minor == minor)){
vout = saved_v2out;
}
}
if (vout == NULL)
return -ENODEV;
/* for now, we only support single open */
if (vout->opened)
return -EBUSY;
vout->opened += 1;
if (!omap2_disp_request_layer (vout->vid)){
vout->opened -= 1;
return -ENODEV;
}
omap2_disp_get_dss ();
/* allocate per-filehandle data */
fh = kmalloc (sizeof (*fh), GFP_KERNEL);
if (NULL == fh){
omap2_disp_release_layer (vout->vid);
omap2_disp_put_dss ();
vout->opened -= 1;
return -ENOMEM;
}
file->private_data = fh;
fh->vout = vout;
fh->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
q = &fh->vbq;
dummy_vbq_ops.buf_release = dummy_vbq_release;
videobuf_queue_init (q, &dummy_vbq_ops, NULL, &vout->vbq_lock,
fh->type, V4L2_FIELD_NONE, sizeof (struct
videobuf_buffer), fh);
/* restore info for mmap */
for (i = 0; i < vout->buffer_allocated; i++) {
q->bufs[i] = videobuf_alloc (q->msize);
if (q->bufs[i] == NULL){
dma_free_coherent(NULL, vout->buffer_size,
(void *) vout->buf_virt_addr[i],
(dma_addr_t)
vout->buf_phy_addr[i]);
/* we have to give up some old buffers */
vout->buffer_allocated = i;
break;
}
q->bufs[i]->i = i;
q->bufs[i]->input = UNSET;
q->bufs[i]->memory = vout->buf_memory_type;
q->bufs[i]->bsize = vout->buffer_size;
q->bufs[i]->boff = vout->buffer_size * i;
q->bufs[i]->dma.vmalloc = (void *)
vout->buf_virt_addr[i];
q->bufs[i]->dma.bus_addr = vout->buf_phy_addr[i];
q->bufs[i]->state = STATE_PREPARED;
}
return 0;
}
static struct file_operations omap24xxvout_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.ioctl = omap24xxvout_ioctl,
.mmap = omap24xxvout_mmap,
.open = omap24xxvout_open,
.release = omap24xxvout_release,
};
/*
----------------------------------------------------------------------
*/
static void
cleanup_vout_device (int vid)
{
struct video_device *vfd;
struct omap24xxvout_device *vout;
vout = (vid == OMAP2_VIDEO1) ? saved_v1out : saved_v2out;
if (!vout)
return;
vfd = vout->vfd;
if (vfd){
if (vfd->minor == -1){
/*
* The device was never registered, so release
the
* video_device struct directly.
*/
video_device_release (vfd);
}
else{
/*
* The unregister function will release the
video_device
* struct as well as unregistering it.
*/
video_unregister_device (vfd);
}
}
kfree (vout);
if (vid == OMAP2_VIDEO1)
saved_v1out = NULL;
else
saved_v2out = NULL;
}
static struct omap24xxvout_device *
init_vout_device (int vid)
{
struct omap24xxvout_device *vout;
struct video_device *vfd;
struct v4l2_pix_format *pix;
vout = kmalloc (sizeof (struct omap24xxvout_device),
GFP_KERNEL);
if (!vout){
printk (KERN_ERR VOUT_NAME ": could not allocate
memory\n");
return NULL;
}
memset (vout, 0, sizeof (struct omap24xxvout_device));
vout->vid = vid;
/* set the default pix */
pix = &vout->pix;
pix->width = QQVGA_WIDTH;
pix->height = QQVGA_HEIGHT;
pix->pixelformat = V4L2_PIX_FMT_RGB565;
pix->field = V4L2_FIELD_NONE;
pix->bytesperline = pix->width * 2;
pix->sizeimage = pix->bytesperline * pix->height;
pix->priv = 0;
pix->colorspace = V4L2_COLORSPACE_JPEG;
vout->bpp = RGB565_BPP;
vout->vrfb_bpp = 1;
/* get the screen parameters */
omap2_disp_get_panel_size (omap2_disp_get_output_dev
(vout->vid),
&(vout->fbuf.fmt.width),&(vout->fbuf.fmt.height));
/* set default crop and win */
omap24xxvout_new_format (pix, &vout->fbuf, &vout->crop,
&vout->win);
/* initialize the video_device struct */
vfd = vout->vfd = video_device_alloc ();
if (!vfd){
printk (KERN_ERR VOUT_NAME": could not allocate video
device struct\n");
kfree (vout);
return NULL;
}
vfd->release = video_device_release;
strncpy (vfd->name, VOUT_NAME, sizeof (vfd->name));
vfd->type = VID_TYPE_OVERLAY | VID_TYPE_CHROMAKEY;
/* need to register for a VID_HARDWARE_* ID in videodev.h */
vfd->hardware = 0;
vfd->fops = &omap24xxvout_fops;
video_set_drvdata (vfd, vout);
vfd->minor = -1;
if (video_register_device (vfd, VFL_TYPE_GRABBER, vid) < 0){
printk (KERN_ERR VOUT_NAME": could not register Video
for Linux device\n");
goto init_error;
}
printk (KERN_INFO VOUT_NAME ": registered device video%d
[v4l2]\n",
vfd->minor);
return vout;
init_error:
video_device_release (vfd);
kfree (vout);
return NULL;
}
static int __init
omap24xxvout_init (void)
{
saved_v1out = init_vout_device (OMAP2_VIDEO1);
if (saved_v1out == NULL)
return -ENODEV;
omap2_disp_save_initstate(OMAP2_VIDEO1);
saved_v2out = init_vout_device (OMAP2_VIDEO2);
if (saved_v2out == NULL){
cleanup_vout_device (OMAP2_VIDEO1);
return -ENODEV;
}
omap2_disp_save_initstate(OMAP2_VIDEO2);
spin_lock_init (&vout_link_lock);
return 0;
}
static void
omap24xxvout_cleanup (void)
{
cleanup_vout_device (OMAP2_VIDEO1);
cleanup_vout_device (OMAP2_VIDEO2);
}
MODULE_AUTHOR ("Texas Instruments.");
MODULE_DESCRIPTION ("OMAP Video for Linux Video out driver");
MODULE_LICENSE ("GPL");
module_init (omap24xxvout_init);
module_exit (omap24xxvout_cleanup);
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubs...
https://www.redhat.com/mailman/listinfo/video4linux-list