diff options
author | merge <null@invalid> | 2009-01-22 13:55:32 +0000 |
---|---|---|
committer | Andy Green <agreen@octopus.localdomain> | 2009-01-22 13:55:32 +0000 |
commit | aa6f5ffbdba45aa8e19e5048648fc6c7b25376d3 (patch) | |
tree | fbb786d0ac6f8a774fd834e9ce951197e60fbffa /drivers/staging/go7007 | |
parent | f2d78193eae5dccd3d588d2c8ea0866efc368332 (diff) |
MERGE-via-pending-tracking-hist-MERGE-via-stable-tracking-MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040-1232632141
pending-tracking-hist top was MERGE-via-stable-tracking-MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040-1232632141 / fdf777a63bcb59e0dfd78bfe2c6242e01f6d4eb9 ... parent commitmessage:
From: merge <null@invalid>
MERGE-via-stable-tracking-hist-MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040
stable-tracking-hist top was MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040 / 90463bfd2d5a3c8b52f6e6d71024a00e052b0ced ... parent commitmessage:
From: merge <null@invalid>
MERGE-via-mokopatches-tracking-hist-fix-stray-endmenu-patch
mokopatches-tracking-hist top was fix-stray-endmenu-patch / 3630e0be570de8057e7f8d2fe501ed353cdf34e6 ... parent commitmessage:
From: Andy Green <andy@openmoko.com>
fix-stray-endmenu.patch
Signed-off-by: Andy Green <andy@openmoko.com>
Diffstat (limited to 'drivers/staging/go7007')
-rw-r--r-- | drivers/staging/go7007/Kconfig | 10 | ||||
-rw-r--r-- | drivers/staging/go7007/Makefile | 15 | ||||
-rw-r--r-- | drivers/staging/go7007/go7007-driver.c | 5 | ||||
-rw-r--r-- | drivers/staging/go7007/go7007-fw.c | 30 | ||||
-rw-r--r-- | drivers/staging/go7007/go7007-priv.h | 2 | ||||
-rw-r--r-- | drivers/staging/go7007/go7007-usb.c | 95 | ||||
-rw-r--r-- | drivers/staging/go7007/go7007-v4l2.c | 1706 | ||||
-rw-r--r-- | drivers/staging/go7007/go7007.txt | 481 | ||||
-rw-r--r-- | drivers/staging/go7007/s2250-board.c | 630 | ||||
-rw-r--r-- | drivers/staging/go7007/s2250-loader.c | 188 | ||||
-rw-r--r-- | drivers/staging/go7007/saa7134-go7007.c | 52 | ||||
-rw-r--r-- | drivers/staging/go7007/wis-i2c.h | 1 | ||||
-rw-r--r-- | drivers/staging/go7007/wis-sony-tuner.c | 2 |
13 files changed, 2509 insertions, 708 deletions
diff --git a/drivers/staging/go7007/Kconfig b/drivers/staging/go7007/Kconfig index 593fdb767aa..f2cf7f66ae0 100644 --- a/drivers/staging/go7007/Kconfig +++ b/drivers/staging/go7007/Kconfig @@ -25,3 +25,13 @@ config VIDEO_GO7007_USB To compile this driver as a module, choose M here: the module will be called go7007-usb +config VIDEO_GO7007_USB_S2250_BOARD + tristate "Sensoray 2250/2251 support" + depends on VIDEO_GO7007_USB && DVB_USB + default N + ---help--- + This is a video4linux driver for the Sensoray 2250/2251 device + + To compile this driver as a module, choose M here: the + module will be called s2250-board + diff --git a/drivers/staging/go7007/Makefile b/drivers/staging/go7007/Makefile index 9b9310cae1c..e514b4af6d0 100644 --- a/drivers/staging/go7007/Makefile +++ b/drivers/staging/go7007/Makefile @@ -5,14 +5,23 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007.o obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o +obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o -go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o snd-go7007.o +go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ + snd-go7007.o wis-saa7113.o +s2250-objs += s2250-board.o s2250-loader.o -#ifneq ($(SAA7134_BUILD),) -#obj-m += saa7134-go7007.o +# Uncompile when the saa7134 patches get into upstream +#ifneq ($(CONFIG_VIDEO_SAA7134),) +#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o +#EXTRA_CFLAGS += -Idrivers/media/video/saa7134 #endif +ifneq ($(CONFIG_VIDEO_GO7007_USB_S2250_BOARD),) +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-usb +endif + EXTRA_CFLAGS += -Idrivers/staging/saa7134 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/staging/go7007/go7007-driver.c b/drivers/staging/go7007/go7007-driver.c index e4ead96679c..58bfc8d81b3 100644 --- a/drivers/staging/go7007/go7007-driver.c +++ b/drivers/staging/go7007/go7007-driver.c @@ -217,6 +217,9 @@ static int init_i2c_module(struct i2c_adapter *adapter, int id, int addr) case I2C_DRIVERID_WIS_OV7640: modname = "wis-ov7640"; break; + case I2C_DRIVERID_S2250: + modname = "s2250-board"; + break; default: modname = NULL; break; @@ -227,7 +230,7 @@ static int init_i2c_module(struct i2c_adapter *adapter, int id, int addr) return 0; if (modname != NULL) printk(KERN_INFO - "go7007: probing for module %s failed", modname); + "go7007: probing for module %s failed\n", modname); else printk(KERN_INFO "go7007: sensor %u seems to be unsupported!\n", id); diff --git a/drivers/staging/go7007/go7007-fw.c b/drivers/staging/go7007/go7007-fw.c index a0e17b0e0ce..871ed43e4e0 100644 --- a/drivers/staging/go7007/go7007-fw.c +++ b/drivers/staging/go7007/go7007-fw.c @@ -284,7 +284,7 @@ static const int zz[64] = { 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; -static int copy_packages(u16 *dest, u16 *src, int pkg_cnt, int space) +static int copy_packages(__le16 *dest, u16 *src, int pkg_cnt, int space) { int i, cnt = pkg_cnt * 32; @@ -292,7 +292,7 @@ static int copy_packages(u16 *dest, u16 *src, int pkg_cnt, int space) return -1; for (i = 0; i < cnt; ++i) - dest[i] = __cpu_to_le16(src[i]); + dest[i] = cpu_to_le16p(src + i); return cnt; } @@ -372,7 +372,7 @@ static int mjpeg_frame_header(struct go7007 *go, unsigned char *buf, int q) return p; } -static int gen_mjpeghdr_to_package(struct go7007 *go, u16 *code, int space) +static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space) { u8 *buf; u16 mem = 0x3e00; @@ -643,7 +643,7 @@ static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext) } static int gen_mpeg1hdr_to_package(struct go7007 *go, - u16 *code, int space, int *framelen) + __le16 *code, int space, int *framelen) { u8 *buf; u16 mem = 0x3e00; @@ -831,7 +831,7 @@ static int mpeg4_sequence_header(struct go7007 *go, unsigned char *buf, int ext) } static int gen_mpeg4hdr_to_package(struct go7007 *go, - u16 *code, int space, int *framelen) + __le16 *code, int space, int *framelen) { u8 *buf; u16 mem = 0x3e00; @@ -936,7 +936,7 @@ done: } static int brctrl_to_package(struct go7007 *go, - u16 *code, int space, int *framelen) + __le16 *code, int space, int *framelen) { int converge_speed = 0; int lambda = (go->format == GO7007_FORMAT_MJPEG || go->dvd_mode) ? @@ -1091,7 +1091,7 @@ static int brctrl_to_package(struct go7007 *go, return copy_packages(code, pack, 6, space); } -static int config_package(struct go7007 *go, u16 *code, int space) +static int config_package(struct go7007 *go, __le16 *code, int space) { int fps = go->sensor_framerate / go->fps_scale / 1000; int rows = go->interlace_coding ? go->height / 32 : go->height / 16; @@ -1213,7 +1213,7 @@ static int config_package(struct go7007 *go, u16 *code, int space) return copy_packages(code, pack, 5, space); } -static int seqhead_to_package(struct go7007 *go, u16 *code, int space, +static int seqhead_to_package(struct go7007 *go, __le16 *code, int space, int (*sequence_header_func)(struct go7007 *go, unsigned char *buf, int ext)) { @@ -1292,7 +1292,7 @@ static int relative_prime(int big, int little) return big; } -static int avsync_to_package(struct go7007 *go, u16 *code, int space) +static int avsync_to_package(struct go7007 *go, __le16 *code, int space) { int arate = go->board_info->audio_rate * 1001 * go->fps_scale; int ratio = arate / go->sensor_framerate; @@ -1323,7 +1323,7 @@ static int avsync_to_package(struct go7007 *go, u16 *code, int space) return copy_packages(code, pack, 1, space); } -static int final_package(struct go7007 *go, u16 *code, int space) +static int final_package(struct go7007 *go, __le16 *code, int space) { int rows = go->interlace_coding ? go->height / 32 : go->height / 16; u16 pack[] = { @@ -1386,7 +1386,7 @@ static int final_package(struct go7007 *go, u16 *code, int space) return copy_packages(code, pack, 1, space); } -static int audio_to_package(struct go7007 *go, u16 *code, int space) +static int audio_to_package(struct go7007 *go, __le16 *code, int space) { int clock_config = ((go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER ? 1 : 0) << 11) | @@ -1436,7 +1436,7 @@ static int audio_to_package(struct go7007 *go, u16 *code, int space) return copy_packages(code, pack, 2, space); } -static int modet_to_package(struct go7007 *go, u16 *code, int space) +static int modet_to_package(struct go7007 *go, __le16 *code, int space) { int ret, mb, i, addr, cnt = 0; u16 pack[32]; @@ -1505,7 +1505,7 @@ static int modet_to_package(struct go7007 *go, u16 *code, int space) return cnt; } -static int do_special(struct go7007 *go, u16 type, u16 *code, int space, +static int do_special(struct go7007 *go, u16 type, __le16 *code, int space, int *framelen) { switch (type) { @@ -1555,7 +1555,7 @@ static int do_special(struct go7007 *go, u16 type, u16 *code, int space, int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) { const struct firmware *fw_entry; - u16 *code, *src; + __le16 *code, *src; int framelen[8] = { }; /* holds the lengths of empty frame templates */ int codespace = 64 * 1024, i = 0, srclen, chunk_len, chunk_flags; int mode_flag; @@ -1590,7 +1590,7 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) goto fw_failed; } memset(code, 0, codespace * 2); - src = (u16 *)fw_entry->data; + src = (__le16 *)fw_entry->data; srclen = fw_entry->size / 2; while (srclen >= 2) { chunk_flags = __le16_to_cpu(src[0]); diff --git a/drivers/staging/go7007/go7007-priv.h b/drivers/staging/go7007/go7007-priv.h index 005542d16a5..372f1f1c09b 100644 --- a/drivers/staging/go7007/go7007-priv.h +++ b/drivers/staging/go7007/go7007-priv.h @@ -40,6 +40,7 @@ struct go7007; #define GO7007_BOARDID_LIFEVIEW_LR192 21 /* TV Walker Ultra */ #define GO7007_BOARDID_ENDURA 22 #define GO7007_BOARDID_ADLINK_MPG24 23 +#define GO7007_BOARDID_SENSORAY_2250 24 /* Sensoray 2250/2251 */ /* Various characteristics of each board */ #define GO7007_BOARD_HAS_AUDIO (1<<0) @@ -104,6 +105,7 @@ struct go7007_hpi_ops { int (*stream_start)(struct go7007 *go); int (*stream_stop)(struct go7007 *go); int (*send_firmware)(struct go7007 *go, u8 *data, int len); + int (*send_command)(struct go7007 *go, unsigned int cmd, void *arg); }; /* The video buffer size must be a multiple of PAGE_SIZE */ diff --git a/drivers/staging/go7007/go7007-usb.c b/drivers/staging/go7007/go7007-usb.c index 3f5ee3424e7..83eec920c7d 100644 --- a/drivers/staging/go7007/go7007-usb.c +++ b/drivers/staging/go7007/go7007-usb.c @@ -225,7 +225,7 @@ static struct go7007_usb_board board_px_tv402u = { .inputs = { { .video_input = 1, - .audio_input = TVAUDIO_INPUT_EXTERN, + .audio_input = TVAUDIO_INPUT_EXTERN, .name = "Composite", }, { @@ -398,6 +398,41 @@ static struct go7007_usb_board board_adlink_mpg24 = { }, }; +static struct go7007_usb_board board_sensoray_2250 = { + .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, + .main_info = { + .firmware = "go7007tv.bin", + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .flags = GO7007_BOARD_HAS_AUDIO, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV, + .num_i2c_devs = 1, + .i2c_devs = { + { + .id = I2C_DRIVERID_S2250, + .addr = 0x34, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 1, + .name = "S-Video", + }, + }, + }, +}; + static struct usb_device_id go7007_usb_id_table[] = { { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | @@ -491,6 +526,14 @@ static struct usb_device_id go7007_usb_id_table[] = { .bcdDevice_hi = 0x1, .driver_info = (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192, }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x1943, /* Vendor ID Sensoray */ + .idProduct = 0x2250, /* Product ID of 2250/2251 */ + .bcdDevice_lo = 0x1, + .bcdDevice_hi = 0x1, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250, + }, { } /* Terminating entry */ }; @@ -637,9 +680,10 @@ static void go7007_usb_readinterrupt_complete(struct urb *urb) { struct go7007 *go = (struct go7007 *)urb->context; u16 *regs = (u16 *)urb->transfer_buffer; + int status = urb->status; - if (urb->status != 0) { - if (urb->status != -ESHUTDOWN && + if (status) { + if (status != -ESHUTDOWN && go->status != STATUS_SHUTDOWN) { printk(KERN_ERR "go7007-usb: error in read interrupt: %d\n", @@ -680,15 +724,14 @@ static int go7007_usb_read_interrupt(struct go7007 *go) static void go7007_usb_read_video_pipe_complete(struct urb *urb) { struct go7007 *go = (struct go7007 *)urb->context; - int r; + int r, status = urb-> status; if (!go->streaming) { wake_up_interruptible(&go->frame_waitq); return; } - if (urb->status != 0) { - printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", - urb->status); + if (status) { + printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", status); return; } if (urb->actual_length != urb->transfer_buffer_length) { @@ -704,13 +747,12 @@ static void go7007_usb_read_video_pipe_complete(struct urb *urb) static void go7007_usb_read_audio_pipe_complete(struct urb *urb) { struct go7007 *go = (struct go7007 *)urb->context; - int r; + int r, status = urb->status; if (!go->streaming) return; - if (urb->status != 0) { - printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", - urb->status); + if (status) { + printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", status); return; } if (urb->actual_length != urb->transfer_buffer_length) { @@ -751,7 +793,7 @@ static int go7007_usb_stream_start(struct go7007 *go) return 0; audio_submit_failed: - for (i = 0; i < 8; ++i) + for (i = 0; i < 7; ++i) usb_kill_urb(usb->audio_urbs[i]); video_submit_failed: for (i = 0; i < 8; ++i) @@ -965,16 +1007,20 @@ static int go7007_usb_probe(struct usb_interface *intf, name = "Lifeview TV Walker Ultra"; board = &board_lifeview_lr192; break; + case GO7007_BOARDID_SENSORAY_2250: + printk(KERN_INFO "Sensoray 2250 found\n"); + name = "Sensoray 2250/2251\n"; + board = &board_sensoray_2250; + break; default: printk(KERN_ERR "go7007-usb: unknown board ID %d!\n", (unsigned int)id->driver_info); return 0; } - usb = kmalloc(sizeof(struct go7007_usb), GFP_KERNEL); + usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL); if (usb == NULL) return -ENOMEM; - memset(usb, 0, sizeof(struct go7007_usb)); /* Allocate the URB and buffer for receiving incoming interrupts */ usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -1179,6 +1225,7 @@ static void go7007_usb_disconnect(struct usb_interface *intf) { struct go7007 *go = usb_get_intfdata(intf); struct go7007_usb *usb = go->hpi_context; + struct urb *vurb, *aurb; int i; go->status = STATUS_SHUTDOWN; @@ -1186,15 +1233,19 @@ static void go7007_usb_disconnect(struct usb_interface *intf) /* Free USB-related structs */ for (i = 0; i < 8; ++i) { - if (usb->video_urbs[i] != NULL) { - if (usb->video_urbs[i]->transfer_buffer != NULL) - kfree(usb->video_urbs[i]->transfer_buffer); - usb_free_urb(usb->video_urbs[i]); + vurb = usb->video_urbs[i]; + if (vurb) { + usb_kill_urb(vurb); + if (vurb->transfer_buffer) + kfree(vurb->transfer_buffer); + usb_free_urb(vurb); } - if (usb->audio_urbs[i] != NULL) { - if (usb->audio_urbs[i]->transfer_buffer != NULL) - kfree(usb->audio_urbs[i]->transfer_buffer); - usb_free_urb(usb->audio_urbs[i]); + aurb = usb->audio_urbs[i]; + if (aurb) { + usb_kill_urb(aurb); + if (aurb->transfer_buffer) + kfree(aurb->transfer_buffer); + usb_free_urb(aurb); } } kfree(usb->intr_urb->transfer_buffer); diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/go7007/go7007-v4l2.c index 94e1141a1fc..868edb65e7b 100644 --- a/drivers/staging/go7007/go7007-v4l2.c +++ b/drivers/staging/go7007/go7007-v4l2.c @@ -38,6 +38,14 @@ #include "go7007-priv.h" #include "wis-i2c.h" +/* Temporary defines until accepted in v4l-dvb */ +#ifndef V4L2_MPEG_STREAM_TYPE_MPEG_ELEM +#define V4L2_MPEG_STREAM_TYPE_MPEG_ELEM 6 /* MPEG elementary stream */ +#endif +#ifndef V4L2_MPEG_VIDEO_ENCODING_MPEG_4 +#define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3 +#endif + static void deactivate_buffer(struct go7007_buffer *gobuf) { int i; @@ -81,7 +89,7 @@ static int go7007_streamoff(struct go7007 *go) return 0; } -static int go7007_open(struct inode *inode, struct file *file) +static int go7007_open(struct file *file) { struct go7007 *go = video_get_drvdata(video_devdata(file)); struct go7007_file *gofh; @@ -99,7 +107,7 @@ static int go7007_open(struct inode *inode, struct file *file) return 0; } -static int go7007_release(struct inode *inode, struct file *file) +static int go7007_release(struct file *file) { struct go7007_file *gofh = file->private_data; struct go7007 *go = gofh->go; @@ -319,6 +327,7 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) return 0; } +#if 0 static int clip_to_modet_map(struct go7007 *go, int region, struct v4l2_clip *clip_list) { @@ -375,499 +384,801 @@ static int clip_to_modet_map(struct go7007 *go, int region, return 0; } -static int go7007_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int mpeg_queryctrl(u32 id, struct v4l2_queryctrl *ctrl) { - struct go7007_file *gofh = file->private_data; - struct go7007 *go = gofh->go; - unsigned long flags; - int retval = 0; - - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap, 0, sizeof(*cap)); - strcpy(cap->driver, "go7007"); - strncpy(cap->card, go->name, sizeof(cap->card)); - cap->version = KERNEL_VERSION(0, 9, 8); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING; /* | V4L2_CAP_AUDIO; */ - if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) - cap->capabilities |= V4L2_CAP_TUNER; + static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + 0 + }; + static const u32 mpeg_ctrls[] = { + V4L2_CID_MPEG_CLASS, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0 + }; + static const u32 *ctrl_classes[] = { + user_ctrls, + mpeg_ctrls, + NULL + }; + + /* The ctrl may already contain the queried i2c controls, + * query the mpeg controls if the existing ctrl id is + * greater than the next mpeg ctrl id. + */ + id = v4l2_ctrl_next(ctrl_classes, id); + if (id >= ctrl->id && ctrl->name[0]) return 0; + + memset(ctrl, 0, sizeof(*ctrl)); + ctrl->id = id; + + switch (ctrl->id) { + case V4L2_CID_USER_CLASS: + case V4L2_CID_MPEG_CLASS: + return v4l2_ctrl_query_fill_std(ctrl); + case V4L2_CID_MPEG_STREAM_TYPE: + return v4l2_ctrl_query_fill(ctrl, + V4L2_MPEG_STREAM_TYPE_MPEG2_DVD, + V4L2_MPEG_STREAM_TYPE_MPEG_ELEM, 1, + V4L2_MPEG_STREAM_TYPE_MPEG_ELEM); + case V4L2_CID_MPEG_VIDEO_ENCODING: + return v4l2_ctrl_query_fill(ctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + case V4L2_CID_MPEG_VIDEO_ASPECT: + return v4l2_ctrl_query_fill(ctrl, + V4L2_MPEG_VIDEO_ASPECT_1x1, + V4L2_MPEG_VIDEO_ASPECT_16x9, 1, + V4L2_MPEG_VIDEO_ASPECT_1x1); + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return v4l2_ctrl_query_fill_std(ctrl); + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(ctrl, + 64000, + 10000000, 1, + 9800000); + default: + break; } - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *fmt = arg; - unsigned int index; - char *desc; - u32 pixelformat; + return -EINVAL; +} + +static int mpeg_s_control(struct v4l2_control *ctrl, struct go7007 *go) +{ + /* pretty sure we can't change any of these while streaming */ + if (go->streaming) + return -EBUSY; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + switch (ctrl->value) { + case V4L2_MPEG_STREAM_TYPE_MPEG2_DVD: + go->format = GO7007_FORMAT_MPEG2; + go->bitrate = 9800000; + go->gop_size = 15; + go->pali = 0x48; + go->closed_gop = 1; + go->repeat_seqhead = 0; + go->seq_header_enable = 1; + go->gop_header_enable = 1; + go->dvd_mode = 1; + break; + case V4L2_MPEG_STREAM_TYPE_MPEG_ELEM: + /* todo: */ + break; + default: return -EINVAL; - switch (fmt->index) { - case 0: - pixelformat = V4L2_PIX_FMT_MJPEG; - desc = "Motion-JPEG"; + } + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + switch (ctrl->value) { + case V4L2_MPEG_VIDEO_ENCODING_MPEG_1: + go->format = GO7007_FORMAT_MPEG1; + go->pali = 0; break; - case 1: - pixelformat = V4L2_PIX_FMT_MPEG; - desc = "MPEG1/MPEG2/MPEG4"; + case V4L2_MPEG_VIDEO_ENCODING_MPEG_2: + go->format = GO7007_FORMAT_MPEG2; + /*if (mpeg->pali >> 24 == 2) + go->pali = mpeg->pali & 0xff; + else*/ + go->pali = 0x48; + break; + case V4L2_MPEG_VIDEO_ENCODING_MPEG_4: + go->format = GO7007_FORMAT_MPEG4; + /*if (mpeg->pali >> 24 == 4) + go->pali = mpeg->pali & 0xff; + else*/ + go->pali = 0xf5; break; default: return -EINVAL; } - index = fmt->index; - memset(fmt, 0, sizeof(*fmt)); - fmt->index = index; - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt->flags = V4L2_FMT_FLAG_COMPRESSED; - strncpy(fmt->description, desc, sizeof(fmt->description)); - fmt->pixelformat = pixelformat; - - return 0; - } - case VIDIOC_TRY_FMT: - { - struct v4l2_format *fmt = arg; - - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + go->gop_header_enable = + /*mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER + ? 0 :*/ 1; + /*if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER) + go->repeat_seqhead = 1; + else*/ + go->repeat_seqhead = 0; + go->dvd_mode = 0; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + if (go->format == GO7007_FORMAT_MJPEG) return -EINVAL; - return set_capture_size(go, fmt, 1); - } - case VIDIOC_G_FMT: - { - struct v4l2_format *fmt = arg; - - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + switch (ctrl->value) { + case V4L2_MPEG_VIDEO_ASPECT_1x1: + go->aspect_ratio = GO7007_RATIO_1_1; + break; + case V4L2_MPEG_VIDEO_ASPECT_4x3: + go->aspect_ratio = GO7007_RATIO_4_3; + break; + case V4L2_MPEG_VIDEO_ASPECT_16x9: + go->aspect_ratio = GO7007_RATIO_16_9; + break; + case V4L2_MPEG_VIDEO_ASPECT_221x100: + default: return -EINVAL; - memset(fmt, 0, sizeof(*fmt)); - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt->fmt.pix.width = go->width; - fmt->fmt.pix.height = go->height; - fmt->fmt.pix.pixelformat = go->format == GO7007_FORMAT_MJPEG ? - V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_MPEG; - fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */ - return 0; - } - case VIDIOC_S_FMT: - { - struct v4l2_format *fmt = arg; - - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + } + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + go->gop_size = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + if (ctrl->value != 0 && ctrl->value != 1) return -EINVAL; - if (go->streaming) - return -EBUSY; - return set_capture_size(go, fmt, 0); - } - case VIDIOC_G_FBUF: - case VIDIOC_S_FBUF: - return -EINVAL; - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *req = arg; - unsigned int count, i; - - if (go->streaming) - return -EBUSY; - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - req->memory != V4L2_MEMORY_MMAP) + go->closed_gop = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + /* Upper bound is kind of arbitrary here */ + if (ctrl->value < 64000 || ctrl->value > 10000000) return -EINVAL; - - down(&gofh->lock); - retval = -EBUSY; - for (i = 0; i < gofh->buf_count; ++i) - if (gofh->bufs[i].mapped > 0) - goto unlock_and_return; - down(&go->hw_lock); - if (go->in_use > 0 && gofh->buf_count == 0) { - up(&go->hw_lock); - goto unlock_and_return; - } - if (gofh->buf_count > 0) - kfree(gofh->bufs); - retval = -ENOMEM; - count = req->count; - if (count > 0) { - if (count < 2) - count = 2; - if (count > 32) - count = 32; - gofh->bufs = kmalloc(count * - sizeof(struct go7007_buffer), - GFP_KERNEL); - if (gofh->bufs == NULL) { - up(&go->hw_lock); - goto unlock_and_return; - } - memset(gofh->bufs, 0, - count * sizeof(struct go7007_buffer)); - for (i = 0; i < count; ++i) { - gofh->bufs[i].go = go; - gofh->bufs[i].index = i; - gofh->bufs[i].state = BUF_STATE_IDLE; - gofh->bufs[i].mapped = 0; - } - go->in_use = 1; - } else { - go->in_use = 0; - } - gofh->buf_count = count; - up(&go->hw_lock); - up(&gofh->lock); - memset(req, 0, sizeof(*req)); - req->count = count; - req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req->memory = V4L2_MEMORY_MMAP; - return 0; + go->bitrate = ctrl->value; + break; + default: + return -EINVAL; } - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *buf = arg; - unsigned int index; + return 0; +} - retval = -EINVAL; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) +static int mpeg_g_control(struct v4l2_control *ctrl, struct go7007 *go) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + if (go->dvd_mode) + ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_DVD; + else + ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG_ELEM; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + switch (go->format) { + case GO7007_FORMAT_MPEG1: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + break; + case GO7007_FORMAT_MPEG2: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + break; + case GO7007_FORMAT_MPEG4: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4; + break; + default: return -EINVAL; - index = buf->index; - down(&gofh->lock); - if (index >= gofh->buf_count) - goto unlock_and_return; - memset(buf, 0, sizeof(*buf)); - buf->index = index; - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - switch (gofh->bufs[index].state) { - case BUF_STATE_QUEUED: - buf->flags = V4L2_BUF_FLAG_QUEUED; + } + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + switch (go->aspect_ratio) { + case GO7007_RATIO_1_1: + ctrl->value = V4L2_MPEG_VIDEO_ASPECT_1x1; break; - case BUF_STATE_DONE: - buf->flags = V4L2_BUF_FLAG_DONE; + case GO7007_RATIO_4_3: + ctrl->value = V4L2_MPEG_VIDEO_ASPECT_4x3; + break; + case GO7007_RATIO_16_9: + ctrl->value = V4L2_MPEG_VIDEO_ASPECT_16x9; break; default: - buf->flags = 0; + return -EINVAL; } - if (gofh->bufs[index].mapped) - buf->flags |= V4L2_BUF_FLAG_MAPPED; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = index * GO7007_BUF_SIZE; - buf->length = GO7007_BUF_SIZE; - up(&gofh->lock); + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctrl->value = go->gop_size; + break; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + ctrl->value = go->closed_gop; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = go->bitrate; + break; + default: + return -EINVAL; + } + return 0; +} +#endif - return 0; +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + strlcpy(cap->driver, "go7007", sizeof(cap->driver)); + strlcpy(cap->card, go->name, sizeof(cap->card)); +#if 0 + strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); +#endif + + cap->version = KERNEL_VERSION(0, 9, 8); + + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; /* | V4L2_CAP_AUDIO; */ + + if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + char *desc = NULL; + + switch (fmt->index) { + case 0: + fmt->pixelformat = V4L2_PIX_FMT_MJPEG; + desc = "Motion-JPEG"; + break; + case 1: + fmt->pixelformat = V4L2_PIX_FMT_MPEG; + desc = "MPEG1/MPEG2/MPEG4"; + break; + default: + return -EINVAL; } - case VIDIOC_QBUF: - { - struct v4l2_buffer *buf = arg; - struct go7007_buffer *gobuf; - int ret; + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt->flags = V4L2_FMT_FLAG_COMPRESSED; - retval = -EINVAL; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - down(&gofh->lock); - if (buf->index < 0 || buf->index >= gofh->buf_count) - goto unlock_and_return; - gobuf = &gofh->bufs[buf->index]; - if (gobuf->mapped == 0) - goto unlock_and_return; - retval = -EBUSY; - if (gobuf->state != BUF_STATE_IDLE) - goto unlock_and_return; - /* offset will be 0 until we really support USERPTR streaming */ - gobuf->offset = gobuf->user_addr & ~PAGE_MASK; - gobuf->bytesused = 0; - gobuf->frame_offset = 0; - gobuf->modet_active = 0; - if (gobuf->offset > 0) - gobuf->page_count = GO7007_BUF_PAGES + 1; - else - gobuf->page_count = GO7007_BUF_PAGES; - retval = -ENOMEM; - down_read(¤t->mm->mmap_sem); - ret = get_user_pages(current, current->mm, - gobuf->user_addr & PAGE_MASK, gobuf->page_count, - 1, 1, gobuf->pages, NULL); - up_read(¤t->mm->mmap_sem); - if (ret != gobuf->page_count) { - int i; - for (i = 0; i < ret; ++i) - page_cache_release(gobuf->pages[i]); - gobuf->page_count = 0; + strncpy(fmt->description, desc, sizeof(fmt->description)); + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt->fmt.pix.width = go->width; + fmt->fmt.pix.height = go->height; + fmt->fmt.pix.pixelformat = (go->format == GO7007_FORMAT_MJPEG) ? + V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + return set_capture_size(go, fmt, 1); +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (go->streaming) + return -EBUSY; + + return set_capture_size(go, fmt, 0); +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + int retval = -EBUSY; + unsigned int count, i; + + if (go->streaming) + return retval; + + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + down(&gofh->lock); + for (i = 0; i < gofh->buf_count; ++i) + if (gofh->bufs[i].mapped > 0) goto unlock_and_return; - } - gobuf->state = BUF_STATE_QUEUED; - spin_lock_irqsave(&go->spinlock, flags); - list_add_tail(&gobuf->stream, &go->stream); - spin_unlock_irqrestore(&go->spinlock, flags); - up(&gofh->lock); - return 0; + + down(&go->hw_lock); + if (go->in_use > 0 && gofh->buf_count == 0) { + up(&go->hw_lock); + goto unlock_and_return; } - case VIDIOC_DQBUF: - { - struct v4l2_buffer *buf = arg; - struct go7007_buffer *gobuf; - u32 frame_type_flag; - DEFINE_WAIT(wait); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - down(&gofh->lock); - retval = -EINVAL; - if (list_empty(&go->stream)) + if (gofh->buf_count > 0) + kfree(gofh->bufs); + + retval = -ENOMEM; + count = req->count; + if (count > 0) { + if (count < 2) + count = 2; + if (count > 32) + count = 32; + + gofh->bufs = kmalloc(count * sizeof(struct go7007_buffer), + GFP_KERNEL); + + if (!gofh->bufs) { + up(&go->hw_lock); goto unlock_and_return; - gobuf = list_entry(go->stream.next, - struct go7007_buffer, stream); - retval = -EAGAIN; - if (gobuf->state != BUF_STATE_DONE && - !(file->f_flags & O_NONBLOCK)) { - for (;;) { - prepare_to_wait(&go->frame_waitq, &wait, - TASK_INTERRUPTIBLE); - if (gobuf->state == BUF_STATE_DONE) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - finish_wait(&go->frame_waitq, &wait); } - if (gobuf->state != BUF_STATE_DONE) - goto unlock_and_return; - spin_lock_irqsave(&go->spinlock, flags); - deactivate_buffer(gobuf); - spin_unlock_irqrestore(&go->spinlock, flags); - frame_type_flag = get_frame_type_flag(gobuf, go->format); - gobuf->state = BUF_STATE_IDLE; - memset(buf, 0, sizeof(*buf)); - buf->index = gobuf->index; - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->bytesused = gobuf->bytesused; - buf->flags = V4L2_BUF_FLAG_MAPPED | frame_type_flag; - buf->field = V4L2_FIELD_NONE; - buf->timestamp = gobuf->timestamp; - buf->sequence = gobuf->seq; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = gobuf->index * GO7007_BUF_SIZE; - buf->length = GO7007_BUF_SIZE; - buf->reserved = gobuf->modet_active; - up(&gofh->lock); - return 0; - } - case VIDIOC_STREAMON: - { - unsigned int *type = arg; - if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - down(&gofh->lock); - down(&go->hw_lock); - if (!go->streaming) { - go->streaming = 1; - go->next_seq = 0; - go->active_buf = NULL; - if (go7007_start_encoder(go) < 0) - retval = -EIO; - else - retval = 0; + memset(gofh->bufs, 0, count * sizeof(struct go7007_buffer)); + + for (i = 0; i < count; ++i) { + gofh->bufs[i].go = go; + gofh->bufs[i].index = i; + gofh->bufs[i].state = BUF_STATE_IDLE; + gofh->bufs[i].mapped = 0; } - up(&go->hw_lock); - up(&gofh->lock); - return retval; - } - case VIDIOC_STREAMOFF: - { - unsigned int *type = arg; - if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - down(&gofh->lock); - go7007_streamoff(go); - up(&gofh->lock); - return 0; + go->in_use = 1; + } else { + go->in_use = 0; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - u32 id; - if (!go->i2c_adapter_online) - return -EIO; - id = ctrl->id; - memset(ctrl, 0, sizeof(*ctrl)); - ctrl->id = id; - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, arg); - return ctrl->name[0] == 0 ? -EINVAL : 0; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - struct v4l2_queryctrl query; + gofh->buf_count = count; + up(&go->hw_lock); + up(&gofh->lock); - if (!go->i2c_adapter_online) - return -EIO; - memset(&query, 0, sizeof(query)); - query.id = ctrl->id; - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); - if (query.name[0] == 0) - return -EINVAL; - i2c_clients_command(&go->i2c_adapter, VIDIOC_G_CTRL, arg); - return 0; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - struct v4l2_queryctrl query; + memset(req, 0, sizeof(*req)); - if (!go->i2c_adapter_online) - return -EIO; - memset(&query, 0, sizeof(query)); - query.id = ctrl->id; - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); - if (query.name[0] == 0) - return -EINVAL; - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_CTRL, arg); - return 0; - } - case VIDIOC_G_PARM: - { - struct v4l2_streamparm *parm = arg; - struct v4l2_fract timeperframe = { - .numerator = 1001 * go->fps_scale, - .denominator = go->sensor_framerate, - }; + req->count = count; + req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req->memory = V4L2_MEMORY_MMAP; - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(parm, 0, sizeof(*parm)); - parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; - parm->parm.capture.timeperframe = timeperframe; - return 0; + return 0; + +unlock_and_return: + up(&gofh->lock); + return retval; +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct go7007_file *gofh = priv; + int retval = -EINVAL; + unsigned int index; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return retval; + + index = buf->index; + + down(&gofh->lock); + if (index >= gofh->buf_count) + goto unlock_and_return; + + memset(buf, 0, sizeof(*buf)); + buf->index = index; + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + switch (gofh->bufs[index].state) { + case BUF_STATE_QUEUED: + buf->flags = V4L2_BUF_FLAG_QUEUED; + break; + case BUF_STATE_DONE: + buf->flags = V4L2_BUF_FLAG_DONE; + break; + default: + buf->flags = 0; } - case VIDIOC_S_PARM: - { - struct v4l2_streamparm *parm = arg; - unsigned int n, d; - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (parm->parm.capture.capturemode != 0) - return -EINVAL; - n = go->sensor_framerate * - parm->parm.capture.timeperframe.numerator; - d = 1001 * parm->parm.capture.timeperframe.denominator; - if (n != 0 && d != 0 && n > d) - go->fps_scale = (n + d/2) / d; - else - go->fps_scale = 1; - return 0; + if (gofh->bufs[index].mapped) + buf->flags |= V4L2_BUF_FLAG_MAPPED; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = index * GO7007_BUF_SIZE; + buf->length = GO7007_BUF_SIZE; + up(&gofh->lock); + + return 0; + +unlock_and_return: + up(&gofh->lock); + return retval; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + struct go7007_buffer *gobuf; + unsigned long flags; + int retval = -EINVAL; + int ret; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->memory != V4L2_MEMORY_MMAP) + return retval; + + down(&gofh->lock); + if (buf->index < 0 || buf->index >= gofh->buf_count) + goto unlock_and_return; + + gobuf = &gofh->bufs[buf->index]; + if (!gobuf->mapped) + goto unlock_and_return; + + retval = -EBUSY; + if (gobuf->state != BUF_STATE_IDLE) + goto unlock_and_return; + + /* offset will be 0 until we really support USERPTR streaming */ + gobuf->offset = gobuf->user_addr & ~PAGE_MASK; + gobuf->bytesused = 0; + gobuf->frame_offset = 0; + gobuf->modet_active = 0; + if (gobuf->offset > 0) + gobuf->page_count = GO7007_BUF_PAGES + 1; + else + gobuf->page_count = GO7007_BUF_PAGES; + + retval = -ENOMEM; + down_read(¤t->mm->mmap_sem); + ret = get_user_pages(current, current->mm, + gobuf->user_addr & PAGE_MASK, gobuf->page_count, + 1, 1, gobuf->pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (ret != gobuf->page_count) { + int i; + for (i = 0; i < ret; ++i) + page_cache_release(gobuf->pages[i]); + gobuf->page_count = 0; + goto unlock_and_return; } - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *std = arg; - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - go->input == go->board_info->num_inputs - 1) { - if (!go->i2c_adapter_online) - return -EIO; - i2c_clients_command(&go->i2c_adapter, - VIDIOC_ENUMSTD, arg); - if (!std->id) /* hack to indicate EINVAL from tuner */ - return -EINVAL; - } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) { - switch (std->index) { - case 0: - v4l2_video_std_construct(std, - V4L2_STD_NTSC, "NTSC"); + gobuf->state = BUF_STATE_QUEUED; + spin_lock_irqsave(&go->spinlock, flags); + list_add_tail(&gobuf->stream, &go->stream); + spin_unlock_irqrestore(&go->spinlock, flags); + up(&gofh->lock); + + return 0; + +unlock_and_return: + up(&gofh->lock); + return retval; +} + + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + struct go7007_buffer *gobuf; + int retval = -EINVAL; + unsigned long flags; + u32 frame_type_flag; + DEFINE_WAIT(wait); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return retval; + if (buf->memory != V4L2_MEMORY_MMAP) + return retval; + + down(&gofh->lock); + if (list_empty(&go->stream)) + goto unlock_and_return; + gobuf = list_entry(go->stream.next, + struct go7007_buffer, stream); + + retval = -EAGAIN; + if (gobuf->state != BUF_STATE_DONE && + !(file->f_flags & O_NONBLOCK)) { + for (;;) { + prepare_to_wait(&go->frame_waitq, &wait, + TASK_INTERRUPTIBLE); + if (gobuf->state == BUF_STATE_DONE) break; - case 1: - v4l2_video_std_construct(std, - V4L2_STD_PAL | V4L2_STD_SECAM, - "PAL/SECAM"); + if (signal_pending(current)) { + retval = -ERESTARTSYS; break; - default: - return -EINVAL; } - } else { - if (std->index != 0) - return -EINVAL; - memset(std, 0, sizeof(*std)); - snprintf(std->name, sizeof(std->name), "%dx%d, %dfps", - go->board_info->sensor_width, - go->board_info->sensor_height, - go->board_info->sensor_framerate / 1000); - std->frameperiod.numerator = 1001; - std->frameperiod.denominator = - go->board_info->sensor_framerate; + schedule(); } - return 0; + finish_wait(&go->frame_waitq, &wait); } - case VIDIOC_G_STD: - { - v4l2_std_id *std = arg; + if (gobuf->state != BUF_STATE_DONE) + goto unlock_and_return; + + spin_lock_irqsave(&go->spinlock, flags); + deactivate_buffer(gobuf); + spin_unlock_irqrestore(&go->spinlock, flags); + frame_type_flag = get_frame_type_flag(gobuf, go->format); + gobuf->state = BUF_STATE_IDLE; + + memset(buf, 0, sizeof(*buf)); + buf->index = gobuf->index; + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->bytesused = gobuf->bytesused; + buf->flags = V4L2_BUF_FLAG_MAPPED | frame_type_flag; + buf->field = V4L2_FIELD_NONE; + buf->timestamp = gobuf->timestamp; + buf->sequence = gobuf->seq; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = gobuf->index * GO7007_BUF_SIZE; + buf->length = GO7007_BUF_SIZE; + buf->reserved = gobuf->modet_active; - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - go->input == go->board_info->num_inputs - 1) { - if (!go->i2c_adapter_online) - return -EIO; - i2c_clients_command(&go->i2c_adapter, - VIDIOC_G_STD, arg); - } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) { - if (go->standard == GO7007_STD_NTSC) - *std = V4L2_STD_NTSC; - else - *std = V4L2_STD_PAL | V4L2_STD_SECAM; - } else - *std = 0; - return 0; + up(&gofh->lock); + return 0; + +unlock_and_return: + up(&gofh->lock); + return retval; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + int retval = 0; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + down(&gofh->lock); + down(&go->hw_lock); + + if (!go->streaming) { + go->streaming = 1; + go->next_seq = 0; + go->active_buf = NULL; + if (go7007_start_encoder(go) < 0) + retval = -EIO; + else + retval = 0; } - case VIDIOC_S_STD: - { - v4l2_std_id *std = arg; + up(&go->hw_lock); + up(&gofh->lock); - if (go->streaming) - return -EBUSY; - if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && - *std != 0) - return -EINVAL; - if (*std == 0) - return -EINVAL; - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - go->input == go->board_info->num_inputs - 1) { - if (!go->i2c_adapter_online) - return -EIO; - i2c_clients_command(&go->i2c_adapter, - VIDIOC_S_STD, arg); - if (!*std) /* hack to indicate EINVAL from tuner */ - return -EINVAL; - } - if (*std & V4L2_STD_NTSC) { - go->standard = GO7007_STD_NTSC; - go->sensor_framerate = 30000; - } else if (*std & V4L2_STD_PAL) { - go->standard = GO7007_STD_PAL; - go->sensor_framerate = 25025; - } else if (*std & V4L2_STD_SECAM) { - go->standard = GO7007_STD_PAL; - go->sensor_framerate = 25025; - } else + return retval; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + down(&gofh->lock); + go7007_streamoff(go); + up(&gofh->lock); + + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *query) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (!go->i2c_adapter_online) + return -EIO; + + i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, query); + + return (!query->name[0]) ? -EINVAL : 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + struct v4l2_queryctrl query; + + if (!go->i2c_adapter_online) + return -EIO; + + memset(&query, 0, sizeof(query)); + query.id = ctrl->id; + i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); + if (query.name[0] == 0) + return -EINVAL; + i2c_clients_command(&go->i2c_adapter, VIDIOC_G_CTRL, ctrl); + + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + struct v4l2_queryctrl query; + + if (!go->i2c_adapter_online) + return -EIO; + + memset(&query, 0, sizeof(query)); + query.id = ctrl->id; + i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); + if (query.name[0] == 0) + return -EINVAL; + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_CTRL, ctrl); + + return 0; +} + +static int vidioc_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + struct v4l2_fract timeperframe = { + .numerator = 1001 * go->fps_scale, + .denominator = go->sensor_framerate, + }; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe = timeperframe; + + return 0; +} + +static int vidioc_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + unsigned int n, d; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (parm->parm.capture.capturemode != 0) + return -EINVAL; + + n = go->sensor_framerate * + parm->parm.capture.timeperframe.numerator; + d = 1001 * parm->parm.capture.timeperframe.denominator; + if (n != 0 && d != 0 && n > d) + go->fps_scale = (n + d/2) / d; + else + go->fps_scale = 1; + + return 0; +} + +/* VIDIOC_ENUMSTD on go7007 were used for enumberating the supported fps and + its resolution, when the device is not connected to TV. + This were an API abuse, probably used by the lack of specific IOCTL's to + enumberate it, by the time the driver were written. + + However, since kernel 2.6.19, two new ioctls (VIDIOC_ENUM_FRAMEINTERVALS + and VIDIOC_ENUM_FRAMESIZES) were added for this purpose. + + The two functions bellow implements the newer ioctls +*/ +static int vidioc_enum_framesizes(struct file *filp, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + /* Return -EINVAL, if it is a TV board */ + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) || + (go->board_info->sensor_flags & GO7007_SENSOR_TV)) + return -EINVAL; + + if (fsize->index > 0) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = go->board_info->sensor_width; + fsize->discrete.height = go->board_info->sensor_height; + + return 0; +} + +static int vidioc_enum_frameintervals(struct file *filp, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + /* Return -EINVAL, if it is a TV board */ + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) || + (go->board_info->sensor_flags & GO7007_SENSOR_TV)) + return -EINVAL; + + if (fival->index > 0) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1001; + fival->discrete.denominator = go->board_info->sensor_framerate; + + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (go->streaming) + return -EBUSY; + + if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && + *std != 0) + return -EINVAL; + + if (*std == 0) + return -EINVAL; + + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && + go->input == go->board_info->num_inputs - 1) { + if (!go->i2c_adapter_online) + return -EIO; + i2c_clients_command(&go->i2c_adapter, + VIDIOC_S_STD, std); + if (!*std) /* hack to indicate EINVAL from tuner */ return -EINVAL; - if (go->i2c_adapter_online) - i2c_clients_command(&go->i2c_adapter, - VIDIOC_S_STD, std); - set_capture_size(go, NULL, 0); - return 0; } + + if (*std & V4L2_STD_NTSC) { + go->standard = GO7007_STD_NTSC; + go->sensor_framerate = 30000; + } else if (*std & V4L2_STD_PAL) { + go->standard = GO7007_STD_PAL; + go->sensor_framerate = 25025; + } else if (*std & V4L2_STD_SECAM) { + go->standard = GO7007_STD_PAL; + go->sensor_framerate = 25025; + } else + return -EINVAL; + + if (go->i2c_adapter_online) + i2c_clients_command(&go->i2c_adapter, + VIDIOC_S_STD, std); + set_capture_size(go, NULL, 0); + + return 0; +} + +#if 0 case VIDIOC_QUERYSTD: { v4l2_std_id *std = arg; @@ -884,219 +1195,269 @@ static int go7007_do_ioctl(struct inode *inode, struct file *file, *std = 0; return 0; } - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *inp = arg; - int index; +#endif - if (inp->index >= go->board_info->num_inputs) - return -EINVAL; - index = inp->index; - memset(inp, 0, sizeof(*inp)); - inp->index = index; - strncpy(inp->name, go->board_info->inputs[index].name, - sizeof(inp->name)); - /* If this board has a tuner, it will be the last input */ - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - index == go->board_info->num_inputs - 1) - inp->type = V4L2_INPUT_TYPE_TUNER; - else - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->audioset = 0; - inp->tuner = 0; - if (go->board_info->sensor_flags & GO7007_SENSOR_TV) - inp->std = V4L2_STD_NTSC | V4L2_STD_PAL | - V4L2_STD_SECAM; - else - inp->std = 0; - return 0; - } - case VIDIOC_G_INPUT: - { - int *input = arg; +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; - *input = go->input; - return 0; - } - case VIDIOC_S_INPUT: - { - int *input = arg; + if (inp->index >= go->board_info->num_inputs) + return -EINVAL; - if (*input >= go->board_info->num_inputs) - return -EINVAL; - if (go->streaming) - return -EBUSY; - go->input = *input; - if (go->i2c_adapter_online) { - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_INPUT, - &go->board_info->inputs[*input].video_input); - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_AUDIO, - &go->board_info->inputs[*input].audio_input); - } - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; + strncpy(inp->name, go->board_info->inputs[inp->index].name, + sizeof(inp->name)); - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (t->index != 0) - return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - i2c_clients_command(&go->i2c_adapter, VIDIOC_G_TUNER, arg); - t->index = 0; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; + /* If this board has a tuner, it will be the last input */ + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && + inp->index == go->board_info->num_inputs - 1) + inp->type = V4L2_INPUT_TYPE_TUNER; + else + inp->type = V4L2_INPUT_TYPE_CAMERA; - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (t->index != 0) - return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - switch (go->board_id) { - case GO7007_BOARDID_PX_TV402U_NA: - case GO7007_BOARDID_PX_TV402U_JP: - /* No selectable options currently */ - if (t->audmode != V4L2_TUNER_MODE_STEREO) - return -EINVAL; - break; - } - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_TUNER, arg); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + inp->audioset = 0; + inp->tuner = 0; + if (go->board_info->sensor_flags & GO7007_SENSOR_TV) + inp->std = V4L2_STD_NTSC | V4L2_STD_PAL | + V4L2_STD_SECAM; + else + inp->std = 0; - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - memset(f, 0, sizeof(*f)); - f->type = V4L2_TUNER_ANALOG_TV; - i2c_clients_command(&go->i2c_adapter, VIDIOC_G_FREQUENCY, arg); - return 0; - } - case VIDIOC_S_FREQUENCY: - { - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_FREQUENCY, arg); - return 0; - } - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *cropcap = arg; + return 0; +} - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(cropcap, 0, sizeof(*cropcap)); - cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - /* These specify the raw input of the sensor */ - switch (go->standard) { - case GO7007_STD_NTSC: - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = 720; - cropcap->bounds.height = 480; - cropcap->defrect.top = 0; - cropcap->defrect.left = 0; - cropcap->defrect.width = 720; - cropcap->defrect.height = 480; - break; - case GO7007_STD_PAL: - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = 720; - cropcap->bounds.height = 576; - cropcap->defrect.top = 0; - cropcap->defrect.left = 0; - cropcap->defrect.width = 720; - cropcap->defrect.height = 576; - break; - case GO7007_STD_OTHER: - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = go->board_info->sensor_width; - cropcap->bounds.height = go->board_info->sensor_height; - cropcap->defrect.top = 0; - cropcap->defrect.left = 0; - cropcap->defrect.width = go->board_info->sensor_width; - cropcap->defrect.height = go->board_info->sensor_height; - break; - } - return 0; - } - case VIDIOC_G_CROP: - { - struct v4l2_crop *crop = arg; +static int vidioc_g_input(struct file *file, void *priv, unsigned int *input) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(crop, 0, sizeof(*crop)); - crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - /* These specify the raw input of the sensor */ - switch (go->standard) { - case GO7007_STD_NTSC: - crop->c.top = 0; - crop->c.left = 0; - crop->c.width = 720; - crop->c.height = 480; - break; - case GO7007_STD_PAL: - crop->c.top = 0; - crop->c.left = 0; - crop->c.width = 720; - crop->c.height = 576; - break; - case GO7007_STD_OTHER: - crop->c.top = 0; - crop->c.left = 0; - crop->c.width = go->board_info->sensor_width; - crop->c.height = go->board_info->sensor_height; - break; - } + *input = go->input; - return 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int input) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (input >= go->board_info->num_inputs) + return -EINVAL; + if (go->streaming) + return -EBUSY; + + go->input = input; + if (go->i2c_adapter_online) { + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_INPUT, + &go->board_info->inputs[input].video_input); + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_AUDIO, + &go->board_info->inputs[input].audio_input); } - case VIDIOC_S_CROP: - { - struct v4l2_crop *crop = arg; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + return -EINVAL; + if (t->index != 0) + return -EINVAL; + if (!go->i2c_adapter_online) + return -EIO; + + i2c_clients_command(&go->i2c_adapter, VIDIOC_G_TUNER, t); + + t->index = 0; + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + return -EINVAL; + if (t->index != 0) + return -EINVAL; + if (!go->i2c_adapter_online) + return -EIO; + + switch (go->board_id) { + case GO7007_BOARDID_PX_TV402U_NA: + case GO7007_BOARDID_PX_TV402U_JP: + /* No selectable options currently */ + if (t->audmode != V4L2_TUNER_MODE_STEREO) return -EINVAL; - return 0; + break; } - case VIDIOC_G_JPEGCOMP: - { - struct v4l2_jpegcompression *params = arg; - memset(params, 0, sizeof(*params)); - params->quality = 50; /* ?? */ - params->jpeg_markers = V4L2_JPEG_MARKER_DHT | - V4L2_JPEG_MARKER_DQT; + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_TUNER, t); - return 0; + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + return -EINVAL; + if (!go->i2c_adapter_online) + return -EIO; + + f->type = V4L2_TUNER_ANALOG_TV; + i2c_clients_command(&go->i2c_adapter, VIDIOC_G_FREQUENCY, f); + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + return -EINVAL; + if (!go->i2c_adapter_online) + return -EIO; + + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_FREQUENCY, f); + + return 0; +} + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* These specify the raw input of the sensor */ + switch (go->standard) { + case GO7007_STD_NTSC: + cropcap->bounds.top = 0; + cropcap->bounds.left = 0; + cropcap->bounds.width = 720; + cropcap->bounds.height = 480; + cropcap->defrect.top = 0; + cropcap->defrect.left = 0; + cropcap->defrect.width = 720; + cropcap->defrect.height = 480; + break; + case GO7007_STD_PAL: + cropcap->bounds.top = 0; + cropcap->bounds.left = 0; + cropcap->bounds.width = 720; + cropcap->bounds.height = 576; + cropcap->defrect.top = 0; + cropcap->defrect.left = 0; + cropcap->defrect.width = 720; + cropcap->defrect.height = 576; + break; + case GO7007_STD_OTHER: + cropcap->bounds.top = 0; + cropcap->bounds.left = 0; + cropcap->bounds.width = go->board_info->sensor_width; + cropcap->bounds.height = go->board_info->sensor_height; + cropcap->defrect.top = 0; + cropcap->defrect.left = 0; + cropcap->defrect.width = go->board_info->sensor_width; + cropcap->defrect.height = go->board_info->sensor_height; + break; } - case VIDIOC_S_JPEGCOMP: - { - struct v4l2_jpegcompression *params = arg; - if (params->quality != 50 || - params->jpeg_markers != (V4L2_JPEG_MARKER_DHT | - V4L2_JPEG_MARKER_DQT)) - return -EINVAL; - return 0; + return 0; +} + +static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* These specify the raw input of the sensor */ + switch (go->standard) { + case GO7007_STD_NTSC: + crop->c.top = 0; + crop->c.left = 0; + crop->c.width = 720; + crop->c.height = 480; + break; + case GO7007_STD_PAL: + crop->c.top = 0; + crop->c.left = 0; + crop->c.width = 720; + crop->c.height = 576; + break; + case GO7007_STD_OTHER: + crop->c.top = 0; + crop->c.left = 0; + crop->c.width = go->board_info->sensor_width; + crop->c.height = go->board_info->sensor_height; + break; } + + return 0; +} + +/* FIXME: vidioc_s_crop is not really implemented!!! + */ +static int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return 0; +} + +static int vidioc_g_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *params) +{ + memset(params, 0, sizeof(*params)); + params->quality = 50; /* ?? */ + params->jpeg_markers = V4L2_JPEG_MARKER_DHT | + V4L2_JPEG_MARKER_DQT; + + return 0; +} + +static int vidioc_s_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *params) +{ + if (params->quality != 50 || + params->jpeg_markers != (V4L2_JPEG_MARKER_DHT | + V4L2_JPEG_MARKER_DQT)) + return -EINVAL; + + return 0; +} + +/* FIXME: + Those ioctls are private, and not needed, since several standard + extended controls already provide streaming control. + So, those ioctls should be converted into vidioc_g_ext_ctrls() + and vidioc_s_ext_ctrls() + */ + +#if 0 /* Temporary ioctls for controlling compression characteristics */ case GO7007IOC_S_BITRATE: { @@ -1316,27 +1677,7 @@ static int go7007_do_ioctl(struct inode *inode, struct file *file, return -EINVAL; return clip_to_modet_map(go, region->region, region->clips); } - default: - printk(KERN_DEBUG "go7007: unsupported ioctl %d\n", cmd); - return -ENOIOCTLCMD; - } - return 0; - -unlock_and_return: - up(&gofh->lock); - return retval; -} - -static int go7007_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct go7007_file *gofh = file->private_data; - - if (gofh->go->status != STATUS_ONLINE) - return -EIO; - - return video_usercopy(inode, file, cmd, arg, go7007_do_ioctl); -} +#endif static ssize_t go7007_read(struct file *file, char __user *data, size_t count, loff_t *ppos) @@ -1371,8 +1712,7 @@ static int go7007_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) page = alloc_page(GFP_USER | __GFP_DMA32); if (!page) return VM_FAULT_OOM; - clear_user_page(page_address(page), (unsigned long)vmf->virtual_address, - page); + clear_user_highpage(page, (unsigned long)vmf->virtual_address); vmf->page = page; return 0; } @@ -1441,23 +1781,59 @@ static void go7007_vfl_release(struct video_device *vfd) kfree(go); } -static struct file_operations go7007_fops = { +static struct v4l2_file_operations go7007_fops = { .owner = THIS_MODULE, .open = go7007_open, .release = go7007_release, - .ioctl = go7007_ioctl, - .llseek = no_llseek, + .ioctl = video_ioctl2, .read = go7007_read, .mmap = go7007_mmap, .poll = go7007_poll, }; +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_jpegcomp = vidioc_g_jpegcomp, + .vidioc_s_jpegcomp = vidioc_s_jpegcomp, +}; + static struct video_device go7007_template = { .name = "go7007", .vfl_type = VID_TYPE_CAPTURE, .fops = &go7007_fops, .minor = -1, .release = go7007_vfl_release, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = V4L2_STD_ALL, + .current_norm = V4L2_STD_NTSC, }; int go7007_v4l2_init(struct go7007 *go) @@ -1477,6 +1853,8 @@ int go7007_v4l2_init(struct go7007 *go) } video_set_drvdata(go->video_dev, go); ++go->ref_count; + printk(KERN_INFO "%s: registered device video%d [v4l2]\n", + go->video_dev->name, go->video_dev->num); return 0; } diff --git a/drivers/staging/go7007/go7007.txt b/drivers/staging/go7007/go7007.txt new file mode 100644 index 00000000000..9f6772bc68c --- /dev/null +++ b/drivers/staging/go7007/go7007.txt @@ -0,0 +1,481 @@ +This is a driver for the WIS GO7007SB multi-format video encoder. + +Pete Eberlein <pete@sensoray.com> + +The driver was orignally released under the GPL and is currently hosted at: +http://nikosapi.org/wiki/index.php/WIS_Go7007_Linux_driver +The go7007 firmware can be acquired from the package on the site above. + +I've modified the driver to support the following Video4Linux2 MPEG +controls, with acceptable values: + +V4L2_CID_MPEG_STREAM_TYPE V4L2_MPEG_STREAM_TYPE_MPEG2_DVD + V4L2_MPEG_STREAM_TYPE_MPEG_ELEM +V4L2_CID_MPEG_VIDEO_ENCODING V4L2_MPEG_VIDEO_ENCODING_MPEG_1 + V4L2_MPEG_VIDEO_ENCODING_MPEG_2 + V4L2_MPEG_VIDEO_ENCODING_MPEG_4 +V4L2_CID_MPEG_VIDEO_ASPECT V4L2_MPEG_VIDEO_ASPECT_1x1 + V4L2_MPEG_VIDEO_ASPECT_4x3 + V4L2_MPEG_VIDEO_ASPECT_16x9 +V4L2_CID_MPEG_VIDEO_GOP_SIZE integer +V4L2_CID_MPEG_VIDEO_BITRATE 64000 .. 10000000 + +These should be used instead of the non-standard GO7007 ioctls described +below. + + +The README files from the orignal package appear below: + +--------------------------------------------------------------------------- + WIS GO7007SB Public Linux Driver +--------------------------------------------------------------------------- + + +*** Please see the file RELEASE-NOTES for important last-minute updates *** + + + 0. OVERVIEW AND LICENSING/DISCLAIMER + + +This driver kit contains Linux drivers for the WIS GO7007SB multi-format +video encoder. Only kernel version 2.6.x is supported. The video stream +is available through the Video4Linux2 API and the audio stream is available +through the ALSA API (or the OSS emulation layer of the ALSA system). + +The files in kernel/ and hotplug/ are licensed under the GNU General Public +License Version 2 from the Free Software Foundation. A copy of the license +is included in the file COPYING. + +The example applications in apps/ and C header files in include/ are +licensed under a permissive license included in the source files which +allows copying, modification and redistribution for any purpose without +attribution. + +The firmware files included in the firmware/ directory may be freely +redistributed only in conjunction with this document; but modification, +tampering and reverse engineering are prohibited. + +MICRONAS USA, INC., MAKES NO WARRANTIES TO ANY PERSON OR ENTITY WITH +RESPECT TO THE SOFTWARE OR ANY DERIVATIVES THEREOF OR ANY SERVICES OR +LICENSES AND DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATION +WARRANTIES OF MERCHANTABILITY, SUPPORT, AND FITNESS FOR A PARTICULAR +PURPOSE AND NON-INFRINGEMENT. + + + 1. SYSTEM REQUIREMENTS + + +This driver requires Linux kernel 2.6. Kernel 2.4 is not supported. Using +kernel 2.6.10 or later is recommended, as earlier kernels are known to have +unstable USB 2.0 support. + +A fully built kernel source tree must be available. Typically this will be +linked from "/lib/modules/<KERNEL VERSION>/build" for convenience. If this +link does not exist, an extra parameter will need to be passed to the +`make` command. + +All vendor-built kernels should already be configured properly. However, +for custom-built kernels, the following options need to be enabled in the +kernel as built-in or modules: + + CONFIG_HOTPLUG - Support for hot-pluggable devices + CONFIG_MODULES - Enable loadable module support + CONFIG_KMOD - Automatic kernel module loading + CONFIG_FW_LOADER - Hotplug firmware loading support + CONFIG_I2C - I2C support + CONFIG_VIDEO_DEV - Video For Linux + CONFIG_SOUND - Sound card support + CONFIG_SND - Advanced Linux Sound Architecture + CONFIG_USB - Support for Host-side USB + CONFIG_USB_DEVICEFS - USB device filesystem + CONFIG_USB_EHCI_HCD - EHCI HCD (USB 2.0) support + +Additionally, to use the example application, the following options need to +be enabled in the ALSA section: + + CONFIG_SND_MIXER_OSS - OSS Mixer API + CONFIG_SND_PCM_OSS - OSS PCM (digital audio) API + +The hotplug scripts, along with the fxload utility, must also be installed. +These scripts can be obtained from <http://linux-hotplug.sourceforge.net/>. +Hotplugging is used for loading firmware into the Cypruss EZ-USB chip using +fxload and for loading firmware into the driver using the firmware agent. + + + 2. COMPILING AND INSTALLING THE DRIVER + + +Most users should be able to compile the driver by simply running: + + $ make + +in the top-level directory of the driver kit. First the kernel modules +will be built, followed by the example applications. + +If the build system is unable to locate the kernel source tree for the +currently-running kernel, or if the module should be built for a kernel +other than the currently-running kernel, an additional parameter will need +to be passed to make to specify the appropriate kernel source directory: + + $ make KERNELSRC=/usr/src/linux-2.6.10-custom3 + +Once the compile completes, the driver and firmware files should be +installed by running: + + $ make install + +The kernel modules will be placed in "/lib/modules/<KERNEL VERSION>/extra" +and the firmware files will be placed in the appropriate hotplug firmware +directory, usually /lib/firmware. In addition, USB maps and scripts will +be placed in /etc/hotplug/usb to enable fxload to initialize the EZ-USB +control chip when the device is connected. + + + 3. PAL/SECAM TUNER CONFIGURATION (TV402U-EU only) + + +The PAL model of the Plextor ConvertX TV402U may require additional +configuration to correctly select the appropriate TV frequency band and +audio subchannel. + +Users with a device other than the Plextor ConvertX TV402U-EU should skip +this section. + +The wide variety of PAL TV systems used in Europe requires that additional +information about the local TV standards be passed to the driver in order +to properly tune TV channels. The two necessary parameters are (a) the PAL +TV band, and (b) the audio subchannel format in use. + +In many cases, the appropriate TV band selection is passed to the driver +from applications. However, in some cases, the application only specifies +that the driver should use PAL but not the specific information about the +appropriate TV band. To work around this issue, the correct TV band may be +specified in the "force_band" parameter to the wis-sony-tuner module: + + TV band force_band + ------- ---------- + PAL B/G B + PAL I I + PAL D/K D + SECAM L L + +If the "force_band" parameter is specified, the driver will ignore any TV +band specified by applications and will always use the band provided in the +module parameter. + +The other parameter that can be specified is the audio subchannel format. +There are several stereo audio carrier systems in use, including NICAM and +three varieties of A2. To receive audio broadcast on one of these stereo +carriers, the "force_mpx_mode" parameter must be specified to the +wis-sony-tuner module. + + TV band Audio subcarrier force_mpx_mode + ------- ---------------- -------------- + PAL B/G Mono (default) 1 + PAL B/G A2 2 + PAL B/G NICAM 3 + PAL I Mono (default) 4 + PAL I NICAM 5 + PAL D/K Mono (default) 6 + PAL D/K A2 (1) 7 + PAL D/K A2 (2) 8 + PAL D/K A2 (3) 9 + PAL D/K NICAM 10 + SECAM L Mono (default) 11 + SECAM L NICAM 12 + +If the "force_mpx_mode" parameter is not specified, the correct mono-only +mode will be chosen based on the TV band. However, the tuner will not +receive stereo audio or bilingual broadcasts correctly. + +To pass the "force_band" or "force_mpx_mode" parameters to the +wis-sony-tuner module, the following line must be added to the modprobe +configuration file, which varies from one Linux distribution to another. + + options wis-sony-tuner force_band=B force_mpx_mode=2 + +The above example would force the tuner to the PAL B/G TV band and receive +stereo audio broadcasts on the A2 carrier. + +To verify that the configuration has been placed in the correct location, +execute: + + $ modprobe -c | grep wis-sony-tuner + +If the configuration line appears, then modprobe will pass the parameters +correctly the next time the wis-sony-tuner module is loaded into the +kernel. + + + 4. TESTING THE DRIVER + + +Because few Linux applications are able to correctly capture from +Video4Linux2 devices with only compressed formats supported, the new driver +should be tested with the "gorecord" application in the apps/ directory. + +First connect a video source to the device, such as a DVD player or VCR. +This will be captured to a file for testing the driver. If an input source +is unavailable, a test file can still be captured, but the video will be +black and the audio will be silent. + +This application will auto-detect the V4L2 and ALSA/OSS device names of the +hardware and will record video and audio to an AVI file for a specified +number of seconds. For example: + + $ apps/gorecord -duration 60 capture.avi + +If this application does not successfully record an AVI file, the error +messages produced by gorecord and recorded in the system log (usually in +/var/log/messages) should provide information to help resolve the problem. + +Supplying no parameters to gorecord will cause it to probe the available +devices and exit. Use the -help flag for usage information. + + + 5. USING THE DRIVER + + +The V4L2 device implemented by the driver provides a standard compressed +format API, within the following criteria: + + * Applications that only support the original Video4Linux1 API will not + be able to communicate with this driver at all. + + * No raw video modes are supported, so applications like xawtv that + expect only uncompressed video will not function. + + * Supported compression formats are: Motion-JPEG, MPEG1, MPEG2 and MPEG4. + + * MPEG video formats are delivered as Video Elementary Streams only. + Program Stream (PS), Transport Stream (TS) and Packetized Elementary + Stream (PES) formats are not supported. + + * Video parameters such as format and input port may not be changed while + the encoder is active. + + * The audio capture device only functions when the video encoder is + actively capturing video. Attempts to read from the audio device when + the encoder is inactive will result in an I/O error. + + * The native format of the audio device is 48Khz 2-channel 16-bit + little-endian PCM, delivered through the ALSA system. No audio + compression is implemented in the hardware. ALSA may convert to other + uncompressed formats on the fly. + +The include/ directory contains a C header file describing non-standard +features of the GO7007SB encoder, which are described below: + + + GO7007IOC_S_COMP_PARAMS, GO7007IOC_G_COMP_PARAMS + + These ioctls are used to negotiate general compression parameters. + + To query the current parameters, call the GO7007IOC_G_COMP_PARAMS ioctl + with a pointer to a struct go7007_comp_params. If the driver is not + set to MPEG format, the EINVAL error code will be returned. + + To change the current parameters, initialize all fields of a struct + go7007_comp_params and call the GO7007_IOC_S_COMP_PARAMS ioctl with a + pointer to this structure. The driver will return the current + parameters with any necessary changes to conform to the limitations of + the hardware or current compression mode. Any or all fields can be set + to zero to request a reasonable default value. If the driver is not + set to MPEG format, the EINVAL error code will be returned. When I/O + is in progress, the EBUSY error code will be returned. + + Fields in struct go7007_comp_params: + + __u32 The maximum number of frames in each + gop_size Group Of Pictures; i.e. the maximum + number of frames minus one between + each key frame. + + __u32 The maximum number of sequential + max_b_frames bidirectionally-predicted frames. + (B-frames are not yet supported.) + + enum go7007_aspect_ratio The aspect ratio to be encoded in the + aspect_ratio meta-data of the compressed format. + + Choices are: + GO7007_ASPECT_RATIO_1_1 + GO7007_ASPECT_RATIO_4_3_NTSC + GO7007_ASPECT_RATIO_4_3_PAL + GO7007_ASPECT_RATIO_16_9_NTSC + GO7007_ASPECT_RATIO_16_9_PAL + + __u32 Bit-wise OR of control flags (below) + flags + + Flags in struct go7007_comp_params: + + GO7007_COMP_CLOSED_GOP Only produce self-contained GOPs, used + to produce streams appropriate for + random seeking. + + GO7007_COMP_OMIT_SEQ_HEADER Omit the stream sequence header. + + + GO7007IOC_S_MPEG_PARAMS, GO7007IOC_G_MPEG_PARAMS + + These ioctls are used to negotiate MPEG-specific stream parameters when + the pixelformat has been set to V4L2_PIX_FMT_MPEG. + + To query the current parameters, call the GO7007IOC_G_MPEG_PARAMS ioctl + with a pointer to a struct go7007_mpeg_params. If the driver is not + set to MPEG format, the EINVAL error code will be returned. + + To change the current parameters, initialize all fields of a struct + go7007_mpeg_params and call the GO7007_IOC_S_MPEG_PARAMS ioctl with a + pointer to this structure. The driver will return the current + parameters with any necessary changes to conform to the limitations of + the hardware or selected MPEG mode. Any or all fields can be set to + zero to request a reasonable default value. If the driver is not set + to MPEG format, the EINVAL error code will be returned. When I/O is in + progress, the EBUSY error code will be returned. + + Fields in struct go7007_mpeg_params: + + enum go7007_mpeg_video_standard + mpeg_video_standard The MPEG video standard in which to + compress the video. + + Choices are: + GO7007_MPEG_VIDEO_MPEG1 + GO7007_MPEG_VIDEO_MPEG2 + GO7007_MPEG_VIDEO_MPEG4 + + __u32 Bit-wise OR of control flags (below) + flags + + __u32 The profile and level indication to be + pali stored in the sequence header. This + is only used as an indicator to the + decoder, and does not affect the MPEG + features used in the video stream. + Not valid for MPEG1. + + Choices for MPEG2 are: + GO7007_MPEG2_PROFILE_MAIN_MAIN + + Choices for MPEG4 are: + GO7007_MPEG4_PROFILE_S_L0 + GO7007_MPEG4_PROFILE_S_L1 + GO7007_MPEG4_PROFILE_S_L2 + GO7007_MPEG4_PROFILE_S_L3 + GO7007_MPEG4_PROFILE_ARTS_L1 + GO7007_MPEG4_PROFILE_ARTS_L2 + GO7007_MPEG4_PROFILE_ARTS_L3 + GO7007_MPEG4_PROFILE_ARTS_L4 + GO7007_MPEG4_PROFILE_AS_L0 + GO7007_MPEG4_PROFILE_AS_L1 + GO7007_MPEG4_PROFILE_AS_L2 + GO7007_MPEG4_PROFILE_AS_L3 + GO7007_MPEG4_PROFILE_AS_L4 + GO7007_MPEG4_PROFILE_AS_L5 + + Flags in struct go7007_mpeg_params: + + GO7007_MPEG_FORCE_DVD_MODE Force all compression parameters and + bitrate control settings to comply + with DVD MPEG2 stream requirements. + This overrides most compression and + bitrate settings! + + GO7007_MPEG_OMIT_GOP_HEADER Omit the GOP header. + + GO7007_MPEG_REPEAT_SEQHEADER Repeat the MPEG sequence header at + the start of each GOP. + + + GO7007IOC_S_BITRATE, GO7007IOC_G_BITRATE + + These ioctls are used to set and query the target bitrate value for the + compressed video stream. The bitrate may be selected by storing the + target bits per second in an int and calling GO7007IOC_S_BITRATE with a + pointer to the int. The bitrate may be queried by calling + GO7007IOC_G_BITRATE with a pointer to an int where the current bitrate + will be stored. + + Note that this is the primary means of controlling the video quality + for all compression modes, including V4L2_PIX_FMT_MJPEG. The + VIDIOC_S_JPEGCOMP ioctl is not supported. + + +---------------------------------------------------------------------------- + Installing the WIS PCI Voyager Driver +--------------------------------------------------------------------------- + +The WIS PCI Voyager driver requires several patches to the Linux 2.6.11.x +kernel source tree before compiling the driver. These patches update the +in-kernel SAA7134 driver to the newest development version and patch bugs +in the TDA8290/TDA8275 tuner driver. + +The following patches must be downloaded from Gerd Knorr's website and +applied in the order listed: + + http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner + http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner2 + http://dl.bytesex.org/patches/2.6.11-2/v4l2-api-mpeg + http://dl.bytesex.org/patches/2.6.11-2/saa7134-update + +The following patches are included with this SDK and can be applied in any +order: + + patches/2.6.11/saa7134-voyager.diff + patches/2.6.11/tda8275-newaddr.diff + patches/2.6.11/tda8290-ntsc.diff + +Check to make sure the CONFIG_VIDEO_SAA7134 option is enabled in the kernel +configuration, and build and install the kernel. + +After rebooting into the new kernel, the GO7007 driver can be compiled and +installed: + + $ make SAA7134_BUILD=y + $ make install + $ modprobe saa7134-go7007 + +There will be two V4L video devices associated with the PCI Voyager. The +first device (most likely /dev/video0) provides access to the raw video +capture mode of the SAA7133 device and is used to configure the source +video parameters and tune the TV tuner. This device can be used with xawtv +or other V4L(2) video software as a standard uncompressed device. + +The second device (most likely /dev/video1) provides access to the +compression functions of the GO7007. It can be tested using the gorecord +application in the apps/ directory of this SDK: + + $ apps/gorecord -vdevice /dev/video1 -noaudio test.avi + +Currently the frame resolution is fixed at 720x480 (NTSC) or 720x576 (PAL), +and the video standard must be specified to both the raw and the compressed +video devices (xawtv and gorecord, for example). + + +-------------------------------------------------------------------------- +RELEASE NOTES FOR WIS GO7007SB LINUX DRIVER +--------------------------------------------------------------------------- + +Last updated: 5 November 2005 + + - Release 0.9.7 includes new support for using udev to run fxload. The + install script should automatically detect whether the old hotplug + scripts or the new udev rules should be used. To force the use of + hotplug, run "make install USE_UDEV=n". To force the use of udev, run + "make install USE_UDEV=y". + + - Motion detection is supported but undocumented. Try the `modet` app + for a demonstration of how to use the facility. + + - Using USB2.0 devices such as the TV402U with USB1.1 HCDs or hubs can + cause buffer overruns and frame drops, even at low framerates, due to + inconsistency in the bitrate control mechanism. + + - On devices with an SAA7115, including the Plextor ConvertX, video height + values of 96, 128, 160, 192, 256, 320, and 384 do not work in NTSC mode. + All valid heights up to 512 work correctly in PAL mode. + + - The WIS Star Trek and PCI Voyager boards have no support yet for audio + or the TV tuner. diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c new file mode 100644 index 00000000000..fb6845e3788 --- /dev/null +++ b/drivers/staging/go7007/s2250-board.c @@ -0,0 +1,630 @@ +/* + * Copyright (C) 2008 Sensoray Company Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include "go7007-priv.h" +#include "wis-i2c.h" + +extern int s2250loader_init(void); +extern void s2250loader_cleanup(void); + +#define TLV320_ADDRESS 0x34 +#define S2250_VIDDEC 0x86 +#define VPX322_ADDR_ANALOGCONTROL1 0x02 +#define VPX322_ADDR_BRIGHTNESS0 0x0127 +#define VPX322_ADDR_BRIGHTNESS1 0x0131 +#define VPX322_ADDR_CONTRAST0 0x0128 +#define VPX322_ADDR_CONTRAST1 0x0132 +#define VPX322_ADDR_HUE 0x00dc +#define VPX322_ADDR_SAT 0x0030 + +struct go7007_usb_board { + unsigned int flags; + struct go7007_board_info main_info; +}; + +struct go7007_usb { + struct go7007_usb_board *board; + struct semaphore i2c_lock; + struct usb_device *usbdev; + struct urb *video_urbs[8]; + struct urb *audio_urbs[8]; + struct urb *intr_urb; +}; + +static unsigned char aud_regs[] = { + 0x1e, 0x00, + 0x00, 0x17, + 0x02, 0x17, + 0x04, 0xf9, + 0x06, 0xf9, + 0x08, 0x02, + 0x0a, 0x00, + 0x0c, 0x00, + 0x0a, 0x00, + 0x0c, 0x00, + 0x0e, 0x02, + 0x10, 0x00, + 0x12, 0x01, + 0x00, 0x00, +}; + + +static unsigned char vid_regs[] = { + 0xF2, 0x0f, + 0xAA, 0x00, + 0xF8, 0xff, + 0x00, 0x00, +}; + +static u16 vid_regs_fp[] = { + 0x028, 0x067, + 0x120, 0x016, + 0x121, 0xcF2, + 0x122, 0x0F2, + 0x123, 0x00c, + 0x124, 0x2d0, + 0x125, 0x2e0, + 0x126, 0x004, + 0x128, 0x1E0, + 0x12A, 0x016, + 0x12B, 0x0F2, + 0x12C, 0x0F2, + 0x12D, 0x00c, + 0x12E, 0x2d0, + 0x12F, 0x2e0, + 0x130, 0x004, + 0x132, 0x1E0, + 0x140, 0x060, + 0x153, 0x00C, + 0x154, 0x200, + 0x150, 0x801, + 0x000, 0x000 +}; + +/* PAL specific values */ +static u16 vid_regs_fp_pal[] = +{ + 0x120, 0x017, + 0x121, 0xd22, + 0x122, 0x122, + 0x12A, 0x017, + 0x12B, 0x122, + 0x12C, 0x122, + 0x140, 0x060, + 0x000, 0x000, +}; + +struct s2250 { + int std; + int input; + int brightness; + int contrast; + int saturation; + int hue; + int reg12b_val; + int audio_input; +}; + +/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ +static int go7007_usb_vendor_request(struct go7007 *go, u16 request, + u16 value, u16 index, void *transfer_buffer, int length, int in) +{ + struct go7007_usb *usb = go->hpi_context; + int timeout = 5000; + + if (in) { + return usb_control_msg(usb->usbdev, + usb_rcvctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + value, index, transfer_buffer, length, timeout); + } else { + return usb_control_msg(usb->usbdev, + usb_sndctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, transfer_buffer, length, timeout); + } +} +/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ + +static int write_reg(struct i2c_client *client, u8 reg, u8 value) +{ + struct go7007 *go = i2c_get_adapdata(client->adapter); + struct go7007_usb *usb = go->hpi_context; + int rc; + int dev_addr = client->addr; + u8 *buf; + + if (go == NULL) + return -ENODEV; + + if (go->status == STATUS_SHUTDOWN) + return -EBUSY; + + buf = kzalloc(16, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (down_interruptible(&usb->i2c_lock) != 0) { + printk(KERN_INFO "i2c lock failed\n"); + return -EINTR; + } + rc = go7007_usb_vendor_request(go, 0x55, dev_addr, + (reg<<8 | value), + buf, + 16, 1); + + up(&usb->i2c_lock); + kfree(buf); + return rc; +} + +static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) +{ + struct go7007 *go = i2c_get_adapdata(client->adapter); + struct go7007_usb *usb = go->hpi_context; + u8 *buf; + struct s2250 *dec = i2c_get_clientdata(client); + + if (go == NULL) + return -ENODEV; + + if (go->status == STATUS_SHUTDOWN) + return -EBUSY; + + buf = kzalloc(16, GFP_KERNEL); + + if (buf == NULL) + return -ENOMEM; + + + + memset(buf, 0xcd, 6); + + if (down_interruptible(&usb->i2c_lock) != 0) { + printk(KERN_INFO "i2c lock failed\n"); + return -EINTR; + } + if (go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1) < 0) + return -EFAULT; + + up(&usb->i2c_lock); + if (buf[0] == 0) { + unsigned int subaddr, val_read; + + subaddr = (buf[4] << 8) + buf[5]; + val_read = (buf[2] << 8) + buf[3]; + if (val_read != val) { + printk(KERN_INFO "invalid fp write %x %x\n", + val_read, val); + return -EFAULT; + } + if (subaddr != addr) { + printk(KERN_INFO "invalid fp write addr %x %x\n", + subaddr, addr); + return -EFAULT; + } + } else + return -EFAULT; + + /* save last 12b value */ + if (addr == 0x12b) + dec->reg12b_val = val; + + return 0; +} + +static int write_regs(struct i2c_client *client, u8 *regs) +{ + int i; + + for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { + if (write_reg(client, regs[i], regs[i+1]) < 0) { + printk(KERN_INFO "s2250: failed\n"); + return -1; + } + } + return 0; +} + +static int write_regs_fp(struct i2c_client *client, u16 *regs) +{ + int i; + + for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { + if (write_reg_fp(client, regs[i], regs[i+1]) < 0) { + printk(KERN_INFO "s2250: failed fp\n"); + return -1; + } + } + return 0; +} + + +static int s2250_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct s2250 *dec = i2c_get_clientdata(client); + + switch (cmd) { + case VIDIOC_S_INPUT: + { + int vidsys; + int *input = arg; + + vidsys = (dec->std == V4L2_STD_NTSC) ? 0x01 : 0x00; + if (*input == 0) { + /* composite */ + write_reg_fp(client, 0x20, 0x020 | vidsys); + write_reg_fp(client, 0x21, 0x662); + write_reg_fp(client, 0x140, 0x060); + } else { + /* S-Video */ + write_reg_fp(client, 0x20, 0x040 | vidsys); + write_reg_fp(client, 0x21, 0x666); + write_reg_fp(client, 0x140, 0x060); + } + dec->input = *input; + break; + } + case VIDIOC_S_STD: + { + v4l2_std_id *std = arg; + u16 vidsource; + + vidsource = (dec->input == 1) ? 0x040 : 0x020; + dec->std = *std; + switch (dec->std) { + case V4L2_STD_NTSC: + write_regs_fp(client, vid_regs_fp); + write_reg_fp(client, 0x20, vidsource | 1); + break; + case V4L2_STD_PAL: + write_regs_fp(client, vid_regs_fp); + write_regs_fp(client, vid_regs_fp_pal); + write_reg_fp(client, 0x20, vidsource); + break; + default: + return -EINVAL; + } + break; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + static const u32 user_ctrls[] = { + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + 0 + }; + static const u32 *ctrl_classes[] = { + user_ctrls, + NULL + }; + + ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); + break; + case V4L2_CID_CONTRAST: + v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); + break; + case V4L2_CID_SATURATION: + v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); + break; + case V4L2_CID_HUE: + v4l2_ctrl_query_fill(ctrl, -50, 50, 1, 0); + break; + default: + ctrl->name[0] = '\0'; + return -EINVAL; + } + break; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + int value1; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + printk(KERN_INFO "s2250: future setting\n"); + return -EINVAL; + case V4L2_CID_CONTRAST: + printk(KERN_INFO "s2250: future setting\n"); + return -EINVAL; + break; + case V4L2_CID_SATURATION: + if (ctrl->value > 127) + dec->saturation = 127; + else if (ctrl->value < 0) + dec->saturation = 0; + else + dec->saturation = ctrl->value; + + value1 = dec->saturation * 4140 / 100; + if (value1 > 4094) + value1 = 4094; + write_reg_fp(client, VPX322_ADDR_SAT, value1); + break; + case V4L2_CID_HUE: + if (ctrl->value > 50) + dec->hue = 50; + else if (ctrl->value < -50) + dec->hue = -50; + else + dec->hue = ctrl->value; + /* clamp the hue range */ + value1 = dec->hue * 280 / 50; + write_reg_fp(client, VPX322_ADDR_HUE, value1); + break; + } + break; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dec->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dec->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dec->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dec->hue; + break; + } + break; + } + case VIDIOC_S_FMT: + { + struct v4l2_format *fmt = arg; + if (fmt->fmt.pix.height < 640) { + write_reg_fp(client, 0x12b, dec->reg12b_val | 0x400); + write_reg_fp(client, 0x140, 0x060); + } else { + write_reg_fp(client, 0x12b, dec->reg12b_val & ~0x400); + write_reg_fp(client, 0x140, 0x060); + } + return 0; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *audio = arg; + + memset(audio, 0, sizeof(*audio)); + audio->index = dec->audio_input; + /* fall through */ + } + case VIDIOC_ENUMAUDIO: + { + struct v4l2_audio *audio = arg; + + switch (audio->index) { + case 0: + strcpy(audio->name, "Line In"); + break; + case 1: + strcpy(audio->name, "Mic"); + break; + case 2: + strcpy(audio->name, "Mic Boost"); + break; + default: + audio->name[0] = '\0'; + return 0; + } + audio->capability = V4L2_AUDCAP_STEREO; + audio->mode = 0; + return 0; + } + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *audio = arg; + + client->addr = TLV320_ADDRESS; + switch (audio->index) { + case 0: + write_reg(client, 0x08, 0x02); /* Line In */ + break; + case 1: + write_reg(client, 0x08, 0x04); /* Mic */ + break; + case 2: + write_reg(client, 0x08, 0x05); /* Mic Boost */ + break; + default: + return -EINVAL; + } + dec->audio_input = audio->index; + return 0; + } + + default: + printk(KERN_INFO "s2250: unknown command 0x%x\n", cmd); + break; + } + return 0; +} + +static struct i2c_driver s2250_driver; + +static struct i2c_client s2250_client_templ = { + .name = "Sensoray 2250", + .driver = &s2250_driver, +}; + +static int s2250_detect(struct i2c_adapter *adapter, int addr, int kind) +{ + struct i2c_client *client; + struct s2250 *dec; + u8 *data; + struct go7007 *go = i2c_get_adapdata(adapter); + struct go7007_usb *usb = go->hpi_context; + + client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + memcpy(client, &s2250_client_templ, + sizeof(s2250_client_templ)); + client->adapter = adapter; + + dec = kmalloc(sizeof(struct s2250), GFP_KERNEL); + if (dec == NULL) { + kfree(client); + return -ENOMEM; + } + + dec->std = V4L2_STD_NTSC; + dec->brightness = 50; + dec->contrast = 50; + dec->saturation = 50; + dec->hue = 0; + client->addr = TLV320_ADDRESS; + i2c_set_clientdata(client, dec); + + printk(KERN_DEBUG + "s2250: initializing video decoder on %s\n", + adapter->name); + + /* initialize the audio */ + client->addr = TLV320_ADDRESS; + if (write_regs(client, aud_regs) < 0) { + printk(KERN_ERR + "s2250: error initializing audio\n"); + kfree(client); + kfree(dec); + return 0; + } + client->addr = S2250_VIDDEC; + i2c_set_clientdata(client, dec); + + if (write_regs(client, vid_regs) < 0) { + printk(KERN_ERR + "s2250: error initializing decoder\n"); + kfree(client); + kfree(dec); + return 0; + } + if (write_regs_fp(client, vid_regs_fp) < 0) { + printk(KERN_ERR + "s2250: error initializing decoder\n"); + kfree(client); + kfree(dec); + return 0; + } + /* set default channel */ + /* composite */ + write_reg_fp(client, 0x20, 0x020 | 1); + write_reg_fp(client, 0x21, 0x662); + write_reg_fp(client, 0x140, 0x060); + + /* set default audio input */ + dec->audio_input = 0; + write_reg(client, 0x08, 0x02); /* Line In */ + + if (down_interruptible(&usb->i2c_lock) == 0) { + data = kzalloc(16, GFP_KERNEL); + if (data != NULL) { + int rc; + rc = go7007_usb_vendor_request(go, 0x41, 0, 0, + data, 16, 1); + if (rc > 0) { + u8 mask; + data[0] = 0; + mask = 1<<5; + data[0] &= ~mask; + data[1] |= mask; + go7007_usb_vendor_request(go, 0x40, 0, + (data[1]<<8) + + data[1], + data, 16, 0); + } + kfree(data); + } + up(&usb->i2c_lock); + } + + i2c_attach_client(client); + printk("s2250: initialized successfully\n"); + return 0; +} + +static int s2250_detach(struct i2c_client *client) +{ + struct s2250 *dec = i2c_get_clientdata(client); + int r; + + r = i2c_detach_client(client); + if (r < 0) + return r; + + kfree(client); + kfree(dec); + return 0; +} + +static struct i2c_driver s2250_driver = { + .driver = { + .name = "Sensoray 2250 board driver", + }, + .id = I2C_DRIVERID_S2250, + .detach_client = s2250_detach, + .command = s2250_command, +}; + +static int __init s2250_init(void) +{ + int r; + + r = s2250loader_init(); + if (r < 0) + return r; + + r = i2c_add_driver(&s2250_driver); + if (r < 0) + return r; + return wis_i2c_add_driver(s2250_driver.id, s2250_detect); +} + +static void __exit s2250_cleanup(void) +{ + wis_i2c_del_driver(s2250_detect); + i2c_del_driver(&s2250_driver); + + s2250loader_cleanup(); +} + +module_init(s2250_init); +module_exit(s2250_cleanup); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Board driver for Sensoryray 2250"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/s2250-loader.c b/drivers/staging/go7007/s2250-loader.c new file mode 100644 index 00000000000..a5e4acab089 --- /dev/null +++ b/drivers/staging/go7007/s2250-loader.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2008 Sensoray Company Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <dvb-usb.h> + +#define S2250_LOADER_FIRMWARE "s2250_loader.fw" +#define S2250_FIRMWARE "s2250.fw" + +typedef struct device_extension_s { + struct kref kref; + int minor; + struct usb_device *usbdev; +} device_extension_t, *pdevice_extension_t; + +#define USB_s2250loader_MAJOR 240 +#define USB_s2250loader_MINOR_BASE 0 +#define MAX_DEVICES 256 + +static pdevice_extension_t s2250_dev_table[MAX_DEVICES]; +static DECLARE_MUTEX(s2250_dev_table_mutex); + +#define to_s2250loader_dev_common(d) container_of(d, device_extension_t, kref) +static void s2250loader_delete(struct kref *kref) +{ + pdevice_extension_t s = to_s2250loader_dev_common(kref); + s2250_dev_table[s->minor] = NULL; + kfree(s); +} + +static int s2250loader_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + int minor, ret; + pdevice_extension_t s = NULL; + const struct firmware *fw; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + if (!usbdev) { + printk(KERN_ERR "Enter s2250loader_probe failed\n"); + return -1; + } + printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n"); + printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->devnum); + + if (usbdev->descriptor.bNumConfigurations != 1) { + printk(KERN_ERR "can't handle multiple config\n"); + return -1; + } + down(&s2250_dev_table_mutex); + + for (minor = 0; minor < MAX_DEVICES; minor++) { + if (s2250_dev_table[minor] == NULL) + break; + } + + if (minor < 0 || minor >= MAX_DEVICES) { + printk(KERN_ERR "Invalid minor: %d\n", minor); + goto failed; + } + + /* Allocate dev data structure */ + s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); + if (s == NULL) { + printk(KERN_ERR "Out of memory\n"); + goto failed; + } + s2250_dev_table[minor] = s; + + printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n", + usbdev->devnum, usbdev->bus->busnum, minor); + + memset(s, 0, sizeof(device_extension_t)); + s->usbdev = usbdev; + printk(KERN_INFO "loading 2250 loader\n"); + + kref_init(&(s->kref)); + + up(&s2250_dev_table_mutex); + + if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) { + printk(KERN_ERR + "s2250: unable to load firmware from file \"%s\"\n", + S2250_LOADER_FIRMWARE); + goto failed2; + } + ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); + release_firmware(fw); + if (0 != ret) { + printk(KERN_ERR "loader download failed\n"); + goto failed2; + } + + if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) { + printk(KERN_ERR + "s2250: unable to load firmware from file \"%s\"\n", + S2250_FIRMWARE); + goto failed2; + } + ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); + release_firmware(fw); + if (0 != ret) { + printk(KERN_ERR "firmware_s2250 download failed\n"); + goto failed2; + } + + usb_set_intfdata(interface, s); + return 0; + +failed: + up(&s2250_dev_table_mutex); +failed2: + if (s) + kref_put(&(s->kref), s2250loader_delete); + + printk(KERN_ERR "probe failed\n"); + return -1; +} + +static void s2250loader_disconnect(struct usb_interface *interface) +{ + pdevice_extension_t s = usb_get_intfdata(interface); + printk(KERN_INFO "s2250: disconnect\n"); + lock_kernel(); + s = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + kref_put(&(s->kref), s2250loader_delete); + unlock_kernel(); +} + +static struct usb_device_id s2250loader_ids[] = { + {USB_DEVICE(0x1943, 0xa250)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, s2250loader_ids); + +static struct usb_driver s2250loader_driver = { + .name = "s2250-loader", + .probe = s2250loader_probe, + .disconnect = s2250loader_disconnect, + .id_table = s2250loader_ids, +}; + +int s2250loader_init(void) +{ + int r; + unsigned i = 0; + + for (i = 0; i < MAX_DEVICES; i++) + s2250_dev_table[i] = NULL; + + r = usb_register(&s2250loader_driver); + if (r) { + printk(KERN_ERR "usb_register failed. Error number %d\n", r); + return -1; + } + + printk(KERN_INFO "s2250loader_init: driver registered\n"); + return 0; +} +EXPORT_SYMBOL(s2250loader_init); + +void s2250loader_cleanup(void) +{ + printk(KERN_INFO "s2250loader_cleanup\n"); + usb_deregister(&s2250loader_driver); +} +EXPORT_SYMBOL(s2250loader_cleanup); diff --git a/drivers/staging/go7007/saa7134-go7007.c b/drivers/staging/go7007/saa7134-go7007.c index c4a6d8ef907..665bbf59d02 100644 --- a/drivers/staging/go7007/saa7134-go7007.c +++ b/drivers/staging/go7007/saa7134-go7007.c @@ -27,7 +27,7 @@ #include <linux/usb.h> #include <linux/i2c.h> #include <asm/byteorder.h> -#include <media/audiochip.h> +#include <media/v4l2-common.h> #include "saa7134-reg.h" #include "saa7134.h" @@ -314,7 +314,13 @@ static int saa7134_go7007_stream_start(struct go7007 *go) static int saa7134_go7007_stream_stop(struct go7007 *go) { struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; + struct saa7134_dev *dev; + + if (!saa) + return -EINVAL; + dev = saa->dev; + if (!dev) + return -EINVAL; /* Shut down TS FIFO */ saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); @@ -373,6 +379,47 @@ static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len) return 0; } +static int saa7134_go7007_send_command(struct go7007 *go, unsigned int cmd, + void *arg) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + + switch (cmd) { + case VIDIOC_S_STD: + { + v4l2_std_id *std = arg; + return saa7134_s_std_internal(dev, NULL, std); + } + case VIDIOC_G_STD: + { + v4l2_std_id *std = arg; + *std = dev->tvnorm->id; + return 0; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) + return saa7134_queryctrl(NULL, NULL, ctrl); + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) + return saa7134_g_ctrl_internal(dev, NULL, ctrl); + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) + return saa7134_s_ctrl_internal(dev, NULL, ctrl); + } + } + return -EINVAL; + +} + static struct go7007_hpi_ops saa7134_go7007_hpi_ops = { .interface_reset = saa7134_go7007_interface_reset, .write_interrupt = saa7134_go7007_write_interrupt, @@ -380,6 +427,7 @@ static struct go7007_hpi_ops saa7134_go7007_hpi_ops = { .stream_start = saa7134_go7007_stream_start, .stream_stop = saa7134_go7007_stream_stop, .send_firmware = saa7134_go7007_send_firmware, + .send_command = saa7134_go7007_send_command, }; /********************* Add/remove functions *********************/ diff --git a/drivers/staging/go7007/wis-i2c.h b/drivers/staging/go7007/wis-i2c.h index 993f658ad73..431f41dd396 100644 --- a/drivers/staging/go7007/wis-i2c.h +++ b/drivers/staging/go7007/wis-i2c.h @@ -23,6 +23,7 @@ #define I2C_DRIVERID_WIS_SAA7113 0xf0f4 #define I2C_DRIVERID_WIS_OV7640 0xf0f5 #define I2C_DRIVERID_WIS_TW2804 0xf0f6 +#define I2C_DRIVERID_S2250 0xf0f7 #define I2C_ALGO_GO7007 0xf00000 #define I2C_ALGO_GO7007_USB 0xf10000 diff --git a/drivers/staging/go7007/wis-sony-tuner.c b/drivers/staging/go7007/wis-sony-tuner.c index 5a91ee409a7..58fddb12237 100644 --- a/drivers/staging/go7007/wis-sony-tuner.c +++ b/drivers/staging/go7007/wis-sony-tuner.c @@ -604,7 +604,7 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct v4l2_tuner *tun = arg; - memset(t, 0, sizeof(*tun)); + memset(tun, 0, sizeof(*tun)); strcpy(tun->name, "Television"); tun->type = V4L2_TUNER_ANALOG_TV; tun->rangelow = 0UL; /* does anything use these? */ |