summaryrefslogtreecommitdiff
path: root/linux/drivers/media/dvb/av7110/saa7146_v4l.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/dvb/av7110/saa7146_v4l.c')
-rw-r--r--linux/drivers/media/dvb/av7110/saa7146_v4l.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/linux/drivers/media/dvb/av7110/saa7146_v4l.c b/linux/drivers/media/dvb/av7110/saa7146_v4l.c
new file mode 100644
index 000000000..387d0fe4b
--- /dev/null
+++ b/linux/drivers/media/dvb/av7110/saa7146_v4l.c
@@ -0,0 +1,502 @@
+/*
+ video4linux-parts of the saa7146 device driver
+
+ Copyright (C) 1998,1999 Michael Hunold <michael@mihu.de>
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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/module.h> /* for module-version */
+#include <linux/string.h>
+#include <linux/slab.h> /* for kmalloc/kfree */
+#include <linux/delay.h> /* for delay-stuff */
+#include <asm/uaccess.h> /* for copy_to/from_user */
+#include <linux/wrapper.h> /* for mem_map_reserve */
+#include <linux/vmalloc.h>
+#include <linux/videodev.h>
+
+#include "saa7146_defs.h"
+#include "saa7146_core.h"
+#include "saa7146_v4l.h"
+
+
+static int saa7146_v4l_debug = 0;
+
+#define dprintk if (saa7146_v4l_debug) printk
+#define hprintk if (saa7146_v4l_debug >= 2) printk
+#define gprintk if (saa7146_v4l_debug >= 3) printk
+
+#define __COMPILE_SAA7146__
+#include "saa7146.c"
+
+/* transform video4linux-cliplist to plain arrays -- we assume that the arrays
+ are big enough -- if not: your fault! */
+int saa7146_v4lclip2plain(struct video_clip *clips, u16 clipcount, int x[], int y[], int width[], int height[])
+{
+ int i = 0;
+ struct video_clip* vc = NULL;
+
+ dprintk("saa7146_v4l.o: ==> saa7146_v4lclip2plain, cc:%d\n",clipcount);
+
+ /* anything to do here? */
+ if( 0 == clipcount )
+ return 0;
+
+ /* copy to kernel-space */
+ vc = vmalloc(sizeof(struct video_clip)*(clipcount));
+ if( NULL == vc ) {
+ printk("saa7146_v4l.o: ==> v4lclip2saa7146_v4l.o: no memory #2!\n");
+ return -ENOMEM;
+ }
+ if(copy_from_user(vc,clips,sizeof(struct video_clip)*clipcount)) {
+ printk("saa7146_v4l.o: ==> v4lclip2saa7146_v4l.o: could not copy from user-space!\n");
+ return -EFAULT;
+ }
+
+ /* copy the clip-list to the arrays
+ note: the video_clip-struct may contain negative values to indicate that a window
+ doesn't lay completly over the video window. Thus, we correct the values right here */
+ for(i = 0; i < clipcount; i++) {
+
+ if( vc[i].width < 0) {
+ vc[i].x += vc[i].width; vc[i].width = -vc[i].width;
+ }
+ if( vc[i].height < 0) {
+ vc[i].y += vc[i].height; vc[i].height = -vc[i].height;
+ }
+
+ if( vc[i].x < 0) {
+ vc[i].width += vc[i].x; vc[i].x = 0;
+ }
+ if( vc[i].y < 0) {
+ vc[i].height += vc[i].y; vc[i].y = 0;
+ }
+
+ if(vc[i].width <= 0 || vc[i].height <= 0) {
+ vfree(vc);
+ return -EINVAL;
+ }
+
+ x[i] = vc[i].x;
+ y[i] = vc[i].y;
+ width[i] = vc[i].width;
+ height[i] = vc[i].height;
+ }
+
+ /* free memory used for temporary clips */
+ vfree(vc);
+
+ return 0;
+}
+
+struct saa7146_v4l_struct {
+ struct video_buffer buffer;
+ struct video_mbuf mbuf;
+ struct video_window window;
+ struct video_picture picture;
+};
+
+static int saa7146_v4l_command(struct saa7146* saa, void *p, unsigned int cmd, void *arg)
+{
+ struct saa7146_v4l_struct* data = (struct saa7146_v4l_struct*)p;
+
+ hprintk("saa7146_v4l.o: ==> saa7146_v4l_command\n");
+
+ if( NULL == saa)
+ return -EINVAL;
+
+ switch(cmd) {
+ case SAA7146_V4L_GPICT:
+ {
+ struct video_picture *p = arg;
+
+ hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_GPICT\n");
+
+ memcpy(p, &data->picture, sizeof(struct video_picture));
+
+ }
+ break;
+
+ case SAA7146_V4L_SPICT:
+ {
+ struct video_picture *p = arg;
+
+ hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_SPICT\n");
+
+ memcpy(&data->picture, p, sizeof(struct video_picture));
+ set_picture_prop(saa, (u32)(data->picture.brightness>>8),(u32)(data->picture.contrast>>9),(u32)(data->picture.colour>>9));
+
+ }
+ break;
+
+ case SAA7146_V4L_SWIN:
+ {
+ struct video_window *vw = arg;
+ int *x = NULL, *y = NULL, *w = NULL, *h = NULL;
+
+ u32 palette = 0;
+
+ hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_SWIN\n");
+
+ video_setmode(saa, 0);
+ saa7146_write(saa->mem, MC1, (MASK_21));
+
+ set_window(saa, vw->width, vw->height,0,0,0);
+ //saa->port, saa->sync);
+ if (move_to(saa, vw->x, vw->y, vw->height, data->buffer.width,
+ data->buffer.depth, data->buffer.bytesperline,
+ (u32)data->buffer.base, 0)<0)
+ return -1;
+
+ switch( data->picture.palette ) {
+
+ case VIDEO_PALETTE_RGB555:
+ palette = RGB15_COMPOSED;
+ break;
+
+ case VIDEO_PALETTE_RGB24:
+ palette = RGB24_COMPOSED;
+ break;
+
+ case VIDEO_PALETTE_RGB32:
+ palette = RGB32_COMPOSED;
+ break;
+
+ case VIDEO_PALETTE_UYVY:
+ palette = YUV422_COMPOSED;
+ break;
+
+ case VIDEO_PALETTE_YUV422P:
+ palette = YUV422_DECOMPOSED;
+ break;
+
+ case VIDEO_PALETTE_YUV420P:
+ palette = YUV420_DECOMPOSED;
+ break;
+
+ case VIDEO_PALETTE_YUV411P:
+ palette = YUV411_DECOMPOSED;
+ break;
+
+ default:
+ /*case VIDEO_PALETTE_RGB565:*/
+ palette = RGB16_COMPOSED;
+ break;
+ }
+
+ set_output_format(saa, palette);
+
+ if (vw->flags==VIDEO_CLIP_BITMAP) {
+ clip_windows(saa, SAA7146_CLIPPING_MASK, vw->width, vw->height,
+ (u32 *) vw->clips, 1, 0, 0, 0, 0);
+ } else {
+
+
+ /* this is tricky, but helps us saving kmalloc/kfree-calls
+ and boring if/else-constructs ... */
+ x = (int*)kmalloc(sizeof(int)*vw->clipcount*4,GFP_KERNEL);
+ if( NULL == x ) {
+ hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_SWIN: out of kernel-memory.\n");
+ return -ENOMEM;
+ }
+ y = x+(1*vw->clipcount);
+ w = x+(2*vw->clipcount);
+ h = x+(3*vw->clipcount);
+
+ /* transform clipping-windows */
+ if (0 != saa7146_v4lclip2plain(vw->clips, vw->clipcount,x,y,w,h))
+ break;
+ clip_windows(saa, SAA7146_CLIPPING_RECT, vw->width, vw->height,
+ NULL, vw->clipcount, x, y, w, h);
+ kfree(x);
+
+ memcpy(&data->window, arg, sizeof(struct video_window));
+ }
+ video_setmode(saa, 1);
+ break;
+ }
+
+ case SAA7146_V4L_CCAPTURE:
+ {
+ int* i = arg;
+
+ hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_CCAPTURE\n");
+
+ if ( 0 == *i ) {
+ video_setmode(saa, 0);
+ }
+ else {
+ video_setmode(saa, 1);
+ }
+
+ break;
+ }
+
+ case SAA7146_V4L_GFBUF:
+ {
+ struct video_buffer *b = arg;
+
+ hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_GFBUF\n");
+
+ memcpy(b, &data->buffer, sizeof(struct video_buffer));
+
+ break;
+ }
+
+ case SAA7146_V4L_SFBUF:
+ {
+ struct video_buffer *b = arg;
+
+ memcpy(&data->buffer, b, sizeof(struct video_buffer));
+ hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_SFBUF: b:0x%08x, h:%d, w:%d, d:%d\n", (u32)data->buffer.base, data->buffer.height, data->buffer.width, data->buffer.depth);
+
+ break;
+ }
+
+
+ case SAA7146_V4L_CSYNC:
+ {
+ int i = *((int*)arg);
+
+ int count = 0, k = 0;
+ unsigned char* grabbfr;
+ unsigned char y, uv;
+
+ /* sanity checks */
+ if ( i >= saa->buffers || i < 0) {
+ gprintk("saa7146_v4l.o: SAA7146_V4L_CSYNC, invalid buffer %d\n",i);
+ return -EINVAL;
+ }
+
+ /* get the state */
+ switch ( saa->frame_stat[i] ) {
+ case GBUFFER_UNUSED:
+ {
+ /* there was no grab to this buffer */
+ gprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_CSYNC, invalid frame (fr:%d)\n",i);
+ return -EINVAL;
+ }
+ case GBUFFER_GRABBING:
+ {
+ /* wait to be woken up by the irq-handler */
+ interruptible_sleep_on(&saa->rps0_wq);
+ break;
+ }
+ case GBUFFER_DONE:
+ {
+ gprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_CSYNC, frame done! (fr:%d)\n",i);
+ break;
+ }
+ }
+
+ /* all saa7146´s below chip-revision 3 are not capable of
+ doing byte-swaps with video-dma1. for rgb-grabbing this
+ does not matter, but yuv422-grabbing has the wrong
+ byte-order, so we have to swap in software */
+ if ( ( saa->revision<3) &&
+ (saa->grab_format[i] == YUV422_COMPOSED)) {
+ /* swap UYVY to YUYV */
+ count = saa->grab_height[i]*saa->grab_width[i]*2;
+ grabbfr = ((unsigned char*)(saa->grabbing))+i*GRABBING_MEM_SIZE;
+ for (k=0; k<count; k=k+2) {
+ y = grabbfr[k+1];
+ uv = grabbfr[k];
+ grabbfr[k] = y;
+ grabbfr[k+1] = uv;
+ }
+ }
+
+ /* set corresponding buffer to ´unused´ */
+ saa->frame_stat[i] = GBUFFER_UNUSED;
+printk ("saa7146_v4l.o: SAA7146_V4L_CSYNC, frame %i done.\n", i);
+
+ break;
+ }
+ case SAA7146_V4L_CMCAPTURE:
+ {
+ struct video_mmap *vm = arg;
+
+ gprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_CMCAPTURE, trying buffer:%d\n", vm->frame);
+
+ /* check status for wanted frame */
+ if ( GBUFFER_GRABBING == saa->frame_stat[vm->frame] ) {
+ gprintk("saa7146_v4l.o: frame #%d still grabbing!\n",vm->frame);
+ return -EBUSY;
+ }
+
+ /* do necessary transformations from the videodev-structure to our own format. */
+
+ /* sanity checks */
+ if ( vm->width <= 0 || vm->height <= 0 ) {
+ gprintk("saa7146_v4l.o: set_up_grabbing, invalid dimension for wanted buffer %d\n",vm->frame);
+ return -EINVAL;
+ }
+
+ /* set corresponding buffer to ´grabbing´ */
+ saa->frame_stat[vm->frame] = GBUFFER_GRABBING;
+
+ /* copy grabbing informtaion for the buffer */
+ saa->grab_height[vm->frame] = vm->height;
+ saa->grab_width[vm->frame] = vm->width;
+ /* fixme: setting of grabbing port ?!*/
+ saa->grab_port[vm->frame] = 0;
+
+ switch( vm->format ) {
+
+ case VIDEO_PALETTE_RGB555:
+ saa->grab_format[vm->frame] = RGB15_COMPOSED;
+ break;
+
+ case VIDEO_PALETTE_RGB24:
+ saa->grab_format[vm->frame] = RGB24_COMPOSED;
+ break;
+
+ case VIDEO_PALETTE_RGB32:
+ saa->grab_format[vm->frame] = RGB32_COMPOSED;
+ break;
+
+ case VIDEO_PALETTE_YUV420P:
+ return -EINVAL;
+
+ case VIDEO_PALETTE_YUV422:
+ saa->grab_format[vm->frame] = YUV422_COMPOSED;
+ break;
+
+ case VIDEO_PALETTE_YUV422P:
+ saa->grab_format[vm->frame] = YUV422_DECOMPOSED;
+ break;
+
+ case VIDEO_PALETTE_YUV411P:
+ saa->grab_format[vm->frame] = YUV411_DECOMPOSED;
+ break;
+
+ default:
+ /*case VIDEO_PALETTE_RGB565:*/
+ saa->grab_format[vm->frame] = RGB16_COMPOSED;
+ break;
+ }
+
+ set_up_grabbing(saa,vm->frame);
+ break;
+ }
+ case SAA7146_V4L_GMBUF:
+ {
+ struct video_mbuf *m = arg;
+ int i = 0;
+
+ m->size = saa->buffers * GRABBING_MEM_SIZE;
+ m->frames = saa->buffers;
+
+ for(i = 0; i < saa->buffers; i++)
+ m->offsets[i]=(i*GRABBING_MEM_SIZE);
+
+ gprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_GMBUF, providing %d buffers.\n", saa->buffers);
+
+ break;
+ }
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+int saa7146_v4l_attach(struct saa7146* adap, void** p)
+{
+ struct saa7146_v4l_struct* data;
+
+ hprintk("saa7146_v4l.o: ==> saa7146_v4l_inc_use_attach\n");
+
+ if (!(data = kmalloc(sizeof(struct saa7146_v4l_struct), GFP_KERNEL))) {
+ printk (KERN_ERR "%s: out of memory!\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+ *(struct saa7146_v4l_struct**)p = data;
+
+ memset(&data->buffer, 0x0, sizeof(struct video_buffer));
+ memset(&data->mbuf, 0x0, sizeof(struct video_mbuf));
+ memset(&data->window, 0x0, sizeof(struct video_window));
+ memset(&data->picture,0x0, sizeof(struct video_picture));
+
+ data->picture.brightness = 32768;
+ data->picture.contrast = 32768;
+ data->picture.colour = 32768; /* saturation */
+ data->picture.depth = 16;
+ data->picture.palette = VIDEO_PALETTE_RGB565;
+
+ return 0;
+}
+
+
+void saa7146_v4l_inc_use(struct saa7146* adap)
+{
+ MOD_INC_USE_COUNT;
+}
+
+
+int saa7146_v4l_detach(struct saa7146* adap, void** p)
+{
+ struct saa7146_v4l_struct** data = (struct saa7146_v4l_struct**)p;
+
+ kfree(*data);
+ *data = NULL;
+
+ return 0;
+}
+
+
+void saa7146_v4l_dec_use(struct saa7146* adap)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+
+static struct saa7146_extension saa7146_v4l_extension = {
+ "v4l extension\0",
+ MASK_27, /* handles rps0 irqs */
+ saa7146_std_grab_irq_callback_rps0,
+ saa7146_v4l_command,
+ saa7146_v4l_attach,
+ saa7146_v4l_detach,
+ saa7146_v4l_inc_use,
+ saa7146_v4l_dec_use
+};
+
+
+int saa7146_v4l_init (void)
+{
+ int res = 0;
+
+ if((res = saa7146_add_extension(&saa7146_v4l_extension))) {
+ printk("saa7146_v4l.o: extension registration failed, module not inserted.\n");
+ return res;
+ }
+
+ return 0;
+}
+
+
+void saa7146_v4l_exit (void)
+{
+ int res = 0;
+ if ((res = saa7146_del_extension(&saa7146_v4l_extension))) {
+ printk("saa7146_v4l.o: extension deregistration failed, module not removed.\n");
+ }
+}
+
+MODULE_PARM(saa7146_v4l_debug, "i");
+MODULE_PARM_DESC(saa7146_v4l_debug, "set saa7146_v4l.c in debug mode");
+