diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-24 06:12:49 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-24 06:12:49 +0200 |
commit | df462b3dbeeaae7141f1b63cbfcc1e1bae6a85fc (patch) | |
tree | bca52fce066159f136d75c69e79016422212cb1d /drivers/media/video | |
parent | Merge git://git.kernel.org/pub/scm/linux/kernel/git/hirofumi/fatfs-2.6 (diff) | |
parent | [media] gspca - sunplus: Fix some warnings and simplify code (diff) | |
download | linux-df462b3dbeeaae7141f1b63cbfcc1e1bae6a85fc.tar.xz linux-df462b3dbeeaae7141f1b63cbfcc1e1bae6a85fc.zip |
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (247 commits)
[media] gspca - sunplus: Fix some warnings and simplify code
[media] gspca: Fix some warnings tied to 'no debug'
[media] gspca: Unset debug by default
[media] gspca - cpia1: Remove a bad conditional compilation instruction
[media] gspca - main: Remove USB traces
[media] gspca - main: Version change to 2.13
[media] gspca - stk014 / t613: Accept the index 0 in querymenu
[media] gspca - kinect: Remove __devinitdata
[media] gspca - cpia1: Fix some warnings
[media] video/Kconfig: Fix mis-classified devices
[media] support for medion dvb stick 1660:1921
[media] tm6000: fix uninitialized field, change prink to dprintk
[media] cx231xx: Add support for Iconbit U100
[media] saa7134 add new TV cards
[media] Use a more consistent value for RC repeat period
[media] cx18: Move spinlock and vb_type initialisation into stream_init
[media] tm6000: remove tm6010 sif audio start and stop
[media] tm6000: remove unused exports
[media] tm6000: add pts logging
[media] tm6000: change from ioctl to unlocked_ioctl
...
Diffstat (limited to 'drivers/media/video')
86 files changed, 4649 insertions, 903 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 00f51dd121f3..3be180b3ba27 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -128,10 +128,10 @@ config VIDEO_IR_I2C # Encoder / Decoder module configuration # -menu "Encoders/decoders and other helper chips" +menu "Encoders, decoders, sensors and other helper chips" visible if !VIDEO_HELPER_CHIPS_AUTO -comment "Audio decoders" +comment "Audio decoders, processors and mixers" config VIDEO_TVAUDIO tristate "Simple audio decoder chips" @@ -210,15 +210,6 @@ config VIDEO_CS53L32A To compile this driver as a module, choose M here: the module will be called cs53l32a. -config VIDEO_M52790 - tristate "Mitsubishi M52790 A/V switch" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Mitsubishi M52790 A/V switch. - - To compile this driver as a module, choose M here: the - module will be called m52790. - config VIDEO_TLV320AIC23B tristate "Texas Instruments TLV320AIC23B audio codec" depends on VIDEO_V4L2 && I2C && EXPERIMENTAL @@ -321,29 +312,6 @@ config VIDEO_KS0127 To compile this driver as a module, choose M here: the module will be called ks0127. -config VIDEO_OV7670 - tristate "OmniVision OV7670 sensor support" - depends on I2C && VIDEO_V4L2 - ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision - OV7670 VGA camera. It currently only works with the M88ALP01 - controller. - -config VIDEO_MT9V011 - tristate "Micron mt9v011 sensor support" - depends on I2C && VIDEO_V4L2 - ---help--- - This is a Video4Linux2 sensor-level driver for the Micron - mt0v011 1.3 Mpixel camera. It currently only works with the - em28xx driver. - -config VIDEO_TCM825X - tristate "TCM825x camera sensor support" - depends on I2C && VIDEO_V4L2 - ---help--- - This is a driver for the Toshiba TCM825x VGA camera sensor. - It is used for example in Nokia N800. - config VIDEO_SAA7110 tristate "Philips SAA7110 video decoder" depends on VIDEO_V4L2 && I2C @@ -362,15 +330,6 @@ config VIDEO_SAA711X To compile this driver as a module, choose M here: the module will be called saa7115. -config VIDEO_SAA717X - tristate "Philips SAA7171/3/4 audio/video decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7171/3/4 audio/video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa717x. - config VIDEO_SAA7191 tristate "Philips SAA7191 video decoder" depends on VIDEO_V4L2 && I2C @@ -420,6 +379,15 @@ config VIDEO_VPX3220 comment "Video and audio decoders" +config VIDEO_SAA717X + tristate "Philips SAA7171/3/4 audio/video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7171/3/4 audio/video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa717x. + source "drivers/media/video/cx25840/Kconfig" comment "MPEG video encoders" @@ -474,15 +442,6 @@ config VIDEO_ADV7175 To compile this driver as a module, choose M here: the module will be called adv7175. -config VIDEO_THS7303 - tristate "THS7303 Video Amplifier" - depends on I2C - help - Support for TI THS7303 video amplifier - - To compile this driver as a module, choose M here: the - module will be called ths7303. - config VIDEO_ADV7343 tristate "ADV7343 video encoder" depends on I2C @@ -498,6 +457,38 @@ config VIDEO_AK881X help Video output driver for AKM AK8813 and AK8814 TV encoders +comment "Camera sensor devices" + +config VIDEO_OV7670 + tristate "OmniVision OV7670 sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7670 VGA camera. It currently only works with the M88ALP01 + controller. + +config VIDEO_MT9V011 + tristate "Micron mt9v011 sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a Video4Linux2 sensor-level driver for the Micron + mt0v011 1.3 Mpixel camera. It currently only works with the + em28xx driver. + +config VIDEO_MT9V032 + tristate "Micron MT9V032 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a Video4Linux2 sensor-level driver for the Micron + MT9V032 752x480 CMOS sensor. + +config VIDEO_TCM825X + tristate "TCM825x camera sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a driver for the Toshiba TCM825x VGA camera sensor. + It is used for example in Nokia N800. + comment "Video improvement chips" config VIDEO_UPD64031A @@ -523,6 +514,26 @@ config VIDEO_UPD64083 To compile this driver as a module, choose M here: the module will be called upd64083. +comment "Miscelaneous helper chips" + +config VIDEO_THS7303 + tristate "THS7303 Video Amplifier" + depends on I2C + help + Support for TI THS7303 video amplifier + + To compile this driver as a module, choose M here: the + module will be called ths7303. + +config VIDEO_M52790 + tristate "Mitsubishi M52790 A/V switch" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Mitsubishi M52790 A/V switch. + + To compile this driver as a module, choose M here: the + module will be called m52790. + endmenu # encoder / decoder chips config VIDEO_SH_VOU @@ -682,7 +693,7 @@ config VIDEO_TIMBERDALE select VIDEO_ADV7180 select VIDEOBUF_DMA_CONTIG ---help--- - Add support for the Video In peripherial of the timberdale FPGA. + Add support for the Video In peripherial of the timberdale FPGA. source "drivers/media/video/cx88/Kconfig" @@ -916,7 +927,7 @@ config VIDEO_OMAP2 This is a v4l2 driver for the TI OMAP2 camera capture interface config VIDEO_MX2_HOSTSUPPORT - bool + bool config VIDEO_MX2 tristate "i.MX27/i.MX25 Camera Sensor Interface driver" @@ -927,6 +938,26 @@ config VIDEO_MX2 This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor Interface +config VIDEO_SAMSUNG_S5P_FIMC + tristate "Samsung S5P and EXYNOS4 camera host interface driver" + depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + ---help--- + This is a v4l2 driver for Samsung S5P and EXYNOS4 camera + host interface and video postprocessor. + + To compile this driver as a module, choose M here: the + module will be called s5p-fimc. + +config VIDEO_S5P_MIPI_CSIS + tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver" + depends on VIDEO_V4L2 && PM_RUNTIME && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver. + + To compile this driver as a module, choose M here: the + module will be called s5p-csis. # # USB Multimedia device configuration @@ -983,7 +1014,7 @@ config USB_STKWEBCAM Supported devices are typically found in some Asus laptops, with USB id 174f:a311 and 05e1:0501. Other Syntek cameras may be supported by the stk11xx driver, from which this is - derived, see <http://sourceforge.net/projects/syntekdriver/> + derived, see <http://sourceforge.net/projects/syntekdriver/> To compile this driver as a module, choose M here: the module will be called stkwebcam. @@ -1022,13 +1053,5 @@ config VIDEO_MEM2MEM_TESTDEV This is a virtual test device for the memory-to-memory driver framework. -config VIDEO_SAMSUNG_S5P_FIMC - tristate "Samsung S5P FIMC (video postprocessor) driver" - depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a v4l2 driver for the S5P camera interface - (video postprocessor) endif # V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index ace5d8b57221..9519160c2e01 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o +obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o @@ -164,6 +165,7 @@ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o + obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 91399c94cd18..a97cf2750bd9 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -4303,7 +4303,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, goto fail0; } - pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision); + btv->revision = dev->revision; pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %s, ", bttv_num,btv->id, btv->revision, pci_name(dev)); diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 5111bbcefad5..0073a8c55336 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -1313,7 +1313,7 @@ static int cpia2_g_priority(struct file *file, void *_fh, enum v4l2_priority *p) static int cpia2_s_priority(struct file *file, void *_fh, enum v4l2_priority prio) { struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh = fh; + struct cpia2_fh *fh = _fh; if (cam->streaming && prio != fh->prio && fh->prio == V4L2_PRIORITY_RECORD) diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index d9d2f6ad6ffb..53b3c7702573 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig @@ -2,6 +2,7 @@ config VIDEO_CX18 tristate "Conexant cx23418 MPEG encoder support" depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL select I2C_ALGOBIT + select VIDEOBUF_VMALLOC depends on RC_CORE select VIDEO_TUNER select VIDEO_TVEEPROM @@ -9,6 +10,9 @@ config VIDEO_CX18 select VIDEO_CS5345 select DVB_S5H1409 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select DVB_S5H1411 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index 68ad1963f421..c07c849b1aaf 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -39,6 +39,16 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = { .tv = { 0x61, 0x60, I2C_CLIENT_END }, }; +/* + * usual i2c tuner addresses to probe with additional demod address for + * an NXP TDA8295 at 0x42 (N.B. it can possibly be at 0x4b or 0x4c too). + */ +static struct cx18_card_tuner_i2c cx18_i2c_nxp = { + .radio = { I2C_CLIENT_END }, + .demod = { 0x42, 0x43, I2C_CLIENT_END }, + .tv = { 0x61, 0x60, I2C_CLIENT_END }, +}; + /* Please add new PCI IDs to: http://pci-ids.ucw.cz/ This keeps the PCI ID database up to date. Note that the entries must be added under vendor 0x4444 (Conexant) as subsystem IDs. @@ -131,15 +141,15 @@ static const struct cx18_card cx18_card_hvr1600_s5h1411 = { .tune_lane = 0, .initial_emrs = 0, }, - .gpio_init.initial_value = 0x3001, - .gpio_init.direction = 0x3001, + .gpio_init.initial_value = 0x3801, + .gpio_init.direction = 0x3801, .gpio_i2c_slave_reset = { - .active_lo_mask = 0x3001, + .active_lo_mask = 0x3801, .msecs_asserted = 10, .msecs_recovery = 40, .ir_reset_mask = 0x0001, }, - .i2c = &cx18_i2c_std, + .i2c = &cx18_i2c_nxp, }; static const struct cx18_card cx18_card_hvr1600_samsung = { diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index 3e750068f275..add7391ecaba 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -109,7 +109,7 @@ struct cx18_card_tuner { struct cx18_card_tuner_i2c { unsigned short radio[2];/* radio tuner i2c address to probe */ - unsigned short demod[2];/* demodulator i2c address to probe */ + unsigned short demod[3];/* demodulator i2c address to probe */ unsigned short tv[4]; /* tv tuner i2c addresses to probe */ }; diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 321c1b79794c..9e2f870f4258 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -423,7 +423,16 @@ static void cx18_process_eeprom(struct cx18 *cx) return; /* autodetect tuner standard */ - if (tv.tuner_formats & V4L2_STD_PAL) { +#define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B | V4L2_STD_GH | \ + V4L2_STD_MN | \ + V4L2_STD_PAL_I | \ + V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \ + V4L2_STD_DK) + if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL) + == TVEEPROM_TUNER_FORMAT_ALL) { + CX18_DEBUG_INFO("Worldwide tuner detected\n"); + cx->std = V4L2_STD_ALL; + } else if (tv.tuner_formats & V4L2_STD_PAL) { CX18_DEBUG_INFO("PAL tuner detected\n"); cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; } else if (tv.tuner_formats & V4L2_STD_NTSC) { @@ -818,7 +827,7 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev, cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; pci_write_config_word(pci_dev, PCI_COMMAND, cmd); - pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &cx->card_rev); + cx->card_rev = pci_dev->revision; pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < 64 && cx18_pci_latency) { @@ -1001,7 +1010,15 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, if (cx->card->hw_all & CX18_HW_TVEEPROM) { /* Based on the model number the cardtype may be changed. The PCI IDs are not always reliable. */ + const struct cx18_card *orig_card = cx->card; cx18_process_eeprom(cx); + + if (cx->card != orig_card) { + /* Changed the cardtype; re-reset the I2C chips */ + cx18_gpio_init(cx); + cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, + core, reset, (u32) CX18_GPIO_RESET_I2C); + } } if (cx->card->comment) CX18_INFO("%s", cx->card->comment); @@ -1087,6 +1104,8 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) are not. */ cx->tuner_std = cx->std; + if (cx->std == V4L2_STD_ALL) + cx->std = V4L2_STD_NTSC_M; retval = cx18_streams_setup(cx); if (retval) { @@ -1133,6 +1152,7 @@ int cx18_init_on_first_open(struct cx18 *cx) int fw_retry_count = 3; struct v4l2_frequency vf; struct cx18_open_id fh; + v4l2_std_id std; fh.cx = cx; @@ -1220,7 +1240,8 @@ int cx18_init_on_first_open(struct cx18 *cx) /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code in one place. */ cx->std++; /* Force full standard initialization */ - cx18_s_std(NULL, &fh, &cx->tuner_std); + std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std; + cx18_s_std(NULL, &fh, &std); cx18_s_frequency(NULL, &fh, &vf); return 0; } diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index b86a740c68df..086427288de8 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -65,6 +65,10 @@ #include "dvb_net.h" #include "dvbdev.h" +/* Videobuf / YUV support */ +#include <media/videobuf-core.h> +#include <media/videobuf-vmalloc.h> + #ifndef CONFIG_PCI # error "This driver requires kernel PCI support." #endif @@ -403,6 +407,23 @@ struct cx18_stream { struct cx18_queue q_idle; /* idle - not in rotation */ struct work_struct out_work_order; + + /* Videobuf for YUV video */ + u32 pixelformat; + struct list_head vb_capture; /* video capture queue */ + spinlock_t vb_lock; + struct timer_list vb_timeout; + + struct videobuf_queue vbuf_q; + spinlock_t vbuf_q_lock; /* Protect vbuf_q */ + enum v4l2_buf_type vb_type; +}; + +struct cx18_videobuf_buffer { + /* Common video buffer sub-system struct */ + struct videobuf_buffer vb; + v4l2_std_id tvnorm; /* selected tv norm */ + u32 bytes_used; }; struct cx18_open_id { @@ -410,6 +431,10 @@ struct cx18_open_id { u32 open_id; int type; struct cx18 *cx; + + struct videobuf_queue vbuf_q; + spinlock_t s_lock; /* Protect vbuf_q */ + enum v4l2_buf_type vb_type; }; static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh) diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index e9802d99439b..07411f34885a 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -597,6 +597,13 @@ ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, mutex_unlock(&cx->serialize_lock); if (rc) return rc; + + if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (id->type == CX18_ENC_STREAM_TYPE_YUV)) { + return videobuf_read_stream(&s->vbuf_q, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); } @@ -622,6 +629,15 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) CX18_DEBUG_FILE("Encoder poll started capture\n"); } + if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (id->type == CX18_ENC_STREAM_TYPE_YUV)) { + int videobuf_poll = videobuf_poll_stream(filp, &s->vbuf_q, wait); + if (eof && videobuf_poll == POLLERR) + return POLLHUP; + else + return videobuf_poll; + } + /* add stream's waitq to the poll list */ CX18_DEBUG_HI_FILE("Encoder poll\n"); poll_wait(filp, &s->waitq, wait); @@ -633,6 +649,58 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) return 0; } +int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cx18_open_id *id = file->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags); + + if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (id->type == CX18_ENC_STREAM_TYPE_YUV)) { + + /* Start a capture if there is none */ + if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) { + int rc; + + mutex_lock(&cx->serialize_lock); + rc = cx18_start_capture(id); + mutex_unlock(&cx->serialize_lock); + if (rc) { + CX18_DEBUG_INFO( + "Could not start capture for %s (%d)\n", + s->name, rc); + return -EINVAL; + } + CX18_DEBUG_FILE("Encoder mmap started capture\n"); + } + + return videobuf_mmap_mapper(&s->vbuf_q, vma); + } + + return -EINVAL; +} + +void cx18_vb_timeout(unsigned long data) +{ + struct cx18_stream *s = (struct cx18_stream *)data; + struct cx18_videobuf_buffer *buf; + unsigned long flags; + + /* Return all of the buffers in error state, so the vbi/vid inode + * can return from blocking. + */ + spin_lock_irqsave(&s->vb_lock, flags); + while (!list_empty(&s->vb_capture)) { + buf = list_entry(s->vb_capture.next, + struct cx18_videobuf_buffer, vb.queue); + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + } + spin_unlock_irqrestore(&s->vb_lock, flags); +} + void cx18_stop_capture(struct cx18_open_id *id, int gop_end) { struct cx18 *cx = id->cx; @@ -716,6 +784,8 @@ int cx18_v4l2_close(struct file *filp) cx18_release_stream(s); } else { cx18_stop_capture(id, 0); + if (id->type == CX18_ENC_STREAM_TYPE_YUV) + videobuf_mmap_free(&id->vbuf_q); } kfree(id); mutex_unlock(&cx->serialize_lock); diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h index 5c8fcb884f0a..b9e5110ad043 100644 --- a/drivers/media/video/cx18/cx18-fileops.h +++ b/drivers/media/video/cx18/cx18-fileops.h @@ -33,6 +33,8 @@ int cx18_start_capture(struct cx18_open_id *id); void cx18_stop_capture(struct cx18_open_id *id, int gop_end); void cx18_mute(struct cx18 *cx); void cx18_unmute(struct cx18 *cx); +int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma); +void cx18_vb_timeout(unsigned long data); /* Shared with cx18-alsa module */ int cx18_claim_stream(struct cx18_open_id *id, int type); diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 4f041c033c54..1933d4d11bf2 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -150,6 +150,7 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; pixfmt->width = cx->cxhdl.width; @@ -158,9 +159,13 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, pixfmt->field = V4L2_FIELD_INTERLACED; pixfmt->priv = 0; if (id->type == CX18_ENC_STREAM_TYPE_YUV) { - pixfmt->pixelformat = V4L2_PIX_FMT_HM12; - /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */ - pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2; + pixfmt->pixelformat = s->pixelformat; + /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2))) + UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */ + if (s->pixelformat == V4L2_PIX_FMT_HM12) + pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2; + else + pixfmt->sizeimage = pixfmt->height * 720 * 2; pixfmt->bytesperline = 720; } else { pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; @@ -237,7 +242,6 @@ static int cx18_try_fmt_vid_cap(struct file *file, void *fh, h = min(h, cx->is_50hz ? 576 : 480); h = max(h, min_h); - cx18_g_fmt_vid_cap(file, fh, fmt); fmt->fmt.pix.width = w; fmt->fmt.pix.height = h; return 0; @@ -274,6 +278,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; struct v4l2_mbus_framefmt mbus_fmt; + struct cx18_stream *s = &cx->streams[id->type]; int ret; int w, h; @@ -283,12 +288,15 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, w = fmt->fmt.pix.width; h = fmt->fmt.pix.height; - if (cx->cxhdl.width == w && cx->cxhdl.height == h) + if (cx->cxhdl.width == w && cx->cxhdl.height == h && + s->pixelformat == fmt->fmt.pix.pixelformat) return 0; if (atomic_read(&cx->ana_capturing) > 0) return -EBUSY; + s->pixelformat = fmt->fmt.pix.pixelformat; + mbus_fmt.width = cx->cxhdl.width = w; mbus_fmt.height = cx->cxhdl.height = h; mbus_fmt.code = V4L2_MBUS_FMT_FIXED; @@ -540,16 +548,19 @@ static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { - static struct v4l2_fmtdesc formats[] = { + static const struct v4l2_fmtdesc formats[] = { { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } }, { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 } - } + }, + { 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, + "UYVY 4:2:2", V4L2_PIX_FMT_UYVY, { 0, 0, 0, 0 } + }, }; - if (fmt->index > 1) + if (fmt->index > ARRAY_SIZE(formats) - 1) return -EINVAL; *fmt = formats[fmt->index]; return 0; @@ -863,6 +874,117 @@ static int cx18_g_enc_index(struct file *file, void *fh, return 0; } +static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id) +{ + struct videobuf_queue *q = NULL; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + + switch (s->vb_type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + q = &s->vbuf_q; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + break; + default: + break; + } + return q; +} + +static int cx18_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct cx18_open_id *id = file->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + + /* Start the hardware only if we're the video device */ + if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) + return -EINVAL; + + if (id->type != CX18_ENC_STREAM_TYPE_YUV) + return -EINVAL; + + /* Establish a buffer timeout */ + mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies); + + return videobuf_streamon(cx18_vb_queue(id)); +} + +static int cx18_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct cx18_open_id *id = file->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + + /* Start the hardware only if we're the video device */ + if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) + return -EINVAL; + + if (id->type != CX18_ENC_STREAM_TYPE_YUV) + return -EINVAL; + + return videobuf_streamoff(cx18_vb_queue(id)); +} + +static int cx18_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct cx18_open_id *id = file->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + + if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) + return -EINVAL; + + return videobuf_reqbufs(cx18_vb_queue(id), rb); +} + +static int cx18_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct cx18_open_id *id = file->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + + if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) + return -EINVAL; + + return videobuf_querybuf(cx18_vb_queue(id), b); +} + +static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx18_open_id *id = file->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + + if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) + return -EINVAL; + + return videobuf_qbuf(cx18_vb_queue(id), b); +} + +static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx18_open_id *id = file->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + + if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) + return -EINVAL; + + return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK); +} + static int cx18_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc) { @@ -1081,6 +1203,12 @@ static const struct v4l2_ioctl_ops cx18_ioctl_ops = { .vidioc_s_register = cx18_s_register, #endif .vidioc_default = cx18_default, + .vidioc_streamon = cx18_streamon, + .vidioc_streamoff = cx18_streamoff, + .vidioc_reqbufs = cx18_reqbufs, + .vidioc_querybuf = cx18_querybuf, + .vidioc_qbuf = cx18_qbuf, + .vidioc_dqbuf = cx18_dqbuf, }; void cx18_set_funcs(struct video_device *vdev) diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 9605d54bd083..c07191e09fcb 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -81,6 +81,7 @@ static const struct cx18_api_info api_info[] = { API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0), API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0), API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), + API_ENTRY(CPU, CX18_CPU_SET_VFC_PARAM, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), @@ -158,6 +159,60 @@ static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl) } } +static void cx18_mdl_send_to_videobuf(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + struct cx18_videobuf_buffer *vb_buf; + struct cx18_buffer *buf; + u8 *p; + u32 offset = 0; + int dispatch = 0; + + if (mdl->bytesused == 0) + return; + + /* Acquire a videobuf buffer, clone to and and release it */ + spin_lock(&s->vb_lock); + if (list_empty(&s->vb_capture)) + goto out; + + vb_buf = list_first_entry(&s->vb_capture, struct cx18_videobuf_buffer, + vb.queue); + + p = videobuf_to_vmalloc(&vb_buf->vb); + if (!p) + goto out; + + offset = vb_buf->bytes_used; + list_for_each_entry(buf, &mdl->buf_list, list) { + if (buf->bytesused == 0) + break; + + if ((offset + buf->bytesused) <= vb_buf->vb.bsize) { + memcpy(p + offset, buf->buf, buf->bytesused); + offset += buf->bytesused; + vb_buf->bytes_used += buf->bytesused; + } + } + + /* If we've filled the buffer as per the callers res then dispatch it */ + if (vb_buf->bytes_used >= (vb_buf->vb.width * vb_buf->vb.height * 2)) { + dispatch = 1; + vb_buf->bytes_used = 0; + } + + if (dispatch) { + vb_buf->vb.ts = ktime_to_timeval(ktime_get()); + list_del(&vb_buf->vb.queue); + vb_buf->vb.state = VIDEOBUF_DONE; + wake_up(&vb_buf->vb.done); + } + + mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies); + +out: + spin_unlock(&s->vb_lock); +} static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s, struct cx18_mdl *mdl) @@ -263,6 +318,9 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) } else { cx18_enqueue(s, mdl, &s->q_full); } + } else if (s->type == CX18_ENC_STREAM_TYPE_YUV) { + cx18_mdl_send_to_videobuf(s, mdl); + cx18_enqueue(s, mdl, &s->q_free); } else { cx18_enqueue(s, mdl, &s->q_full); if (s->type == CX18_ENC_STREAM_TYPE_IDX) diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 6fbc356113c1..852f420fd271 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -44,6 +44,7 @@ static struct v4l2_file_operations cx18_v4l2_enc_fops = { .unlocked_ioctl = cx18_v4l2_ioctl, .release = cx18_v4l2_close, .poll = cx18_v4l2_enc_poll, + .mmap = cx18_v4l2_mmap, }; /* offset from 0 to register ts v4l2 minors on */ @@ -97,6 +98,141 @@ static struct { }, }; + +void cx18_dma_free(struct videobuf_queue *q, + struct cx18_stream *s, struct cx18_videobuf_buffer *buf) +{ + videobuf_waiton(q, &buf->vb, 0, 0); + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int cx18_prepare_buffer(struct videobuf_queue *q, + struct cx18_stream *s, + struct cx18_videobuf_buffer *buf, + u32 pixelformat, + unsigned int width, unsigned int height, + enum v4l2_field field) +{ + struct cx18 *cx = s->cx; + int rc = 0; + + /* check settings */ + buf->bytes_used = 0; + + if ((width < 48) || (height < 32)) + return -EINVAL; + + buf->vb.size = (width * height * 2); + if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size)) + return -EINVAL; + + /* alloc + fill struct (if changed) */ + if (buf->vb.width != width || buf->vb.height != height || + buf->vb.field != field || s->pixelformat != pixelformat || + buf->tvnorm != cx->std) { + + buf->vb.width = width; + buf->vb.height = height; + buf->vb.field = field; + buf->tvnorm = cx->std; + s->pixelformat = pixelformat; + + cx18_dma_free(q, s, buf); + } + + if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size)) + return -EINVAL; + + if (buf->vb.field == 0) + buf->vb.field = V4L2_FIELD_INTERLACED; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + buf->vb.width = width; + buf->vb.height = height; + buf->vb.field = field; + buf->tvnorm = cx->std; + s->pixelformat = pixelformat; + + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc != 0) + goto fail; + } + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + cx18_dma_free(q, s, buf); + return rc; + +} + +/* VB_MIN_BUFSIZE is lcm(1440 * 480, 1440 * 576) + 1440 is a single line of 4:2:2 YUV at 720 luma samples wide +*/ +#define VB_MIN_BUFFERS 32 +#define VB_MIN_BUFSIZE 4147200 + +static int buffer_setup(struct videobuf_queue *q, + unsigned int *count, unsigned int *size) +{ + struct cx18_stream *s = q->priv_data; + struct cx18 *cx = s->cx; + + *size = 2 * cx->cxhdl.width * cx->cxhdl.height; + if (*count == 0) + *count = VB_MIN_BUFFERS; + + while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE) + (*count)--; + + q->field = V4L2_FIELD_INTERLACED; + q->last = V4L2_FIELD_INTERLACED; + + return 0; +} + +static int buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx18_videobuf_buffer *buf = + container_of(vb, struct cx18_videobuf_buffer, vb); + struct cx18_stream *s = q->priv_data; + struct cx18 *cx = s->cx; + + return cx18_prepare_buffer(q, s, buf, s->pixelformat, + cx->cxhdl.width, cx->cxhdl.height, field); +} + +static void buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct cx18_videobuf_buffer *buf = + container_of(vb, struct cx18_videobuf_buffer, vb); + struct cx18_stream *s = q->priv_data; + + cx18_dma_free(q, s, buf); +} + +static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct cx18_videobuf_buffer *buf = + container_of(vb, struct cx18_videobuf_buffer, vb); + struct cx18_stream *s = q->priv_data; + + buf->vb.state = VIDEOBUF_QUEUED; + + list_add_tail(&buf->vb.queue, &s->vb_capture); +} + +static struct videobuf_queue_ops cx18_videobuf_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + static void cx18_stream_init(struct cx18 *cx, int type) { struct cx18_stream *s = &cx->streams[type]; @@ -132,6 +268,26 @@ static void cx18_stream_init(struct cx18 *cx, int type) cx18_queue_init(&s->q_idle); INIT_WORK(&s->out_work_order, cx18_out_work_handler); + + INIT_LIST_HEAD(&s->vb_capture); + s->vb_timeout.function = cx18_vb_timeout; + s->vb_timeout.data = (unsigned long)s; + init_timer(&s->vb_timeout); + spin_lock_init(&s->vb_lock); + if (type == CX18_ENC_STREAM_TYPE_YUV) { + spin_lock_init(&s->vbuf_q_lock); + + s->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + videobuf_queue_vmalloc_init(&s->vbuf_q, &cx18_videobuf_qops, + &cx->pci_dev->dev, &s->vbuf_q_lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx18_videobuf_buffer), + s, &cx->serialize_lock); + + /* Assume the previous pixel default */ + s->pixelformat = V4L2_PIX_FMT_HM12; + } } static int cx18_prep_dev(struct cx18 *cx, int type) @@ -372,6 +528,9 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister) if (vdev == NULL) continue; + if (type == CX18_ENC_STREAM_TYPE_YUV) + videobuf_mmap_free(&cx->streams[type].vbuf_q); + cx18_stream_free(&cx->streams[type]); /* Unregister or release device */ @@ -581,7 +740,10 @@ static void cx18_stream_configure_mdls(struct cx18_stream *s) * Set the MDL size to the exact size needed for one frame. * Use enough buffers per MDL to cover the MDL size */ - s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2; + if (s->pixelformat == V4L2_PIX_FMT_HM12) + s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2; + else + s->mdl_size = 720 * s->cx->cxhdl.height * 2; s->bufs_per_mdl = s->mdl_size / s->buf_size; if (s->mdl_size % s->buf_size) s->bufs_per_mdl++; @@ -729,6 +891,19 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1); + + /* Enable the Video Format Converter for UYVY 4:2:2 support, + * rather than the default HM12 Macroblovk 4:2:0 support. + */ + if (captype == CAPTURE_CHANNEL_TYPE_YUV) { + if (s->pixelformat == V4L2_PIX_FMT_UYVY) + cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, + s->handle, 1); + else + /* If in doubt, default to HM12 */ + cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, + s->handle, 0); + } } if (atomic_read(&cx->tot_capturing) == 0) { diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 3e1aec4bcfde..cd189b6bbe20 100644 --- a/drivers/media/video/cx18/cx18-version.h +++ b/drivers/media/video/cx18/cx18-version.h @@ -24,7 +24,7 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 -#define CX18_DRIVER_VERSION_MINOR 4 +#define CX18_DRIVER_VERSION_MINOR 5 #define CX18_DRIVER_VERSION_PATCHLEVEL 0 #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 935f557acbd0..767a8d23e3f2 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h @@ -342,6 +342,12 @@ ReturnCode */ #define CX18_CPU_GET_ENC_PTS (CPU_CMD_MASK_CAPTURE | 0x0022) +/* Description: Set VFC parameters + IN[0] - task handle + IN[1] - VFC enable flag, 1 - enable, 0 - disable +*/ +#define CX18_CPU_SET_VFC_PARAM (CPU_CMD_MASK_CAPTURE | 0x0023) + /* Below is the list of commands related to the data exchange */ #define CPU_CMD_MASK_DE (CPU_CMD_MASK | 0x040000) diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index f49230d170e6..22703815a31f 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -401,6 +401,44 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_KWORLD_UB430_USB_HYBRID] = { + .name = "Kworld UB430 USB Hybrid", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x11, /* According with PV cxPolaris.inf file */ + .tuner_sif_gpio = -1, + .tuner_scl_gpio = -1, + .tuner_sda_gpio = -1, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 2, + .demod_i2c_master = 1, + .ir_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x10, + .norm = V4L2_STD_PAL_M, + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, [CX231XX_BOARD_PV_PLAYTV_USB_HYBRID] = { .name = "Pixelview PlayTV USB Hybrid", .tuner_type = TUNER_NXP_TDA18271, @@ -469,6 +507,31 @@ struct cx231xx_board cx231xx_boards[] = { } }, }, + + [CX231XX_BOARD_ICONBIT_U100] = { + .name = "Iconbit Analog Stick U100 FM", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x1C, + .gpio_pin_status_mask = 0x4001000, + + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -500,6 +563,10 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_PV_PLAYTV_USB_HYBRID}, {USB_DEVICE(USB_VID_PIXELVIEW, 0x5014), .driver_info = CX231XX_BOARD_PV_XCAPTURE_USB}, + {USB_DEVICE(0x1b80, 0xe424), + .driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID}, + {USB_DEVICE(0x1f4d, 0x0237), + .driver_info = CX231XX_BOARD_ICONBIT_U100}, {}, }; diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c index 363aa6004221..da9a4a0aab79 100644 --- a/drivers/media/video/cx231xx/cx231xx-dvb.c +++ b/drivers/media/video/cx231xx/cx231xx-dvb.c @@ -704,6 +704,7 @@ static int dvb_init(struct cx231xx *dev) break; case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: + case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID: printk(KERN_INFO "%s: looking for demod on i2c bus: %d\n", __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap)); diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index bd4a9cf29577..46dd84067816 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -65,6 +65,8 @@ #define CX231XX_BOARD_HAUPPAUGE_USBLIVE2 9 #define CX231XX_BOARD_PV_PLAYTV_USB_HYBRID 10 #define CX231XX_BOARD_PV_XCAPTURE_USB 11 +#define CX231XX_BOARD_KWORLD_UB430_USB_HYBRID 12 +#define CX231XX_BOARD_ICONBIT_U100 13 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index ea88722cb4ab..2354336862cf 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -1399,6 +1399,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) else altera_init(&netup_config, fw); + release_firmware(fw); break; } } diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 9933810b4e33..64d9b2136ff6 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -2045,7 +2045,7 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, } /* print pci info */ - pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + dev->pci_rev = pci_dev->revision; pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", dev->name, diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index bca307eb1e24..11e49bbc4a66 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -1060,18 +1060,21 @@ static int mpeg_open(struct file *file) /* Make sure we can acquire the hardware */ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); - if (drv) { - err = drv->request_acquire(drv); - if(err != 0) { - dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err); - mutex_unlock(&dev->core->lock); - return err; - } + if (!drv) { + dprintk(1, "%s: blackbird driver is not loaded\n", __func__); + mutex_unlock(&dev->core->lock); + return -ENODEV; + } + + err = drv->request_acquire(drv); + if (err != 0) { + dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err); + mutex_unlock(&dev->core->lock); + return err; } - if (!atomic_read(&dev->core->mpeg_users) && blackbird_initialize_codec(dev) < 0) { - if (drv) - drv->request_release(drv); + if (!dev->core->mpeg_users && blackbird_initialize_codec(dev) < 0) { + drv->request_release(drv); mutex_unlock(&dev->core->lock); return -EINVAL; } @@ -1080,8 +1083,7 @@ static int mpeg_open(struct file *file) /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh),GFP_KERNEL); if (NULL == fh) { - if (drv) - drv->request_release(drv); + drv->request_release(drv); mutex_unlock(&dev->core->lock); return -ENOMEM; } @@ -1099,7 +1101,7 @@ static int mpeg_open(struct file *file) cx88_set_scale(dev->core, dev->width, dev->height, fh->mpegq.field); - atomic_inc(&dev->core->mpeg_users); + dev->core->mpeg_users++; mutex_unlock(&dev->core->lock); return 0; } @@ -1110,7 +1112,9 @@ static int mpeg_release(struct file *file) struct cx8802_dev *dev = fh->dev; struct cx8802_driver *drv = NULL; - if (dev->mpeg_active && atomic_read(&dev->core->mpeg_users) == 1) + mutex_lock(&dev->core->lock); + + if (dev->mpeg_active && dev->core->mpeg_users == 1) blackbird_stop_codec(dev); cx8802_cancel_buffers(fh->dev); @@ -1119,17 +1123,18 @@ static int mpeg_release(struct file *file) videobuf_mmap_free(&fh->mpegq); - mutex_lock(&dev->core->lock); file->private_data = NULL; kfree(fh); - mutex_unlock(&dev->core->lock); /* Make sure we release the hardware */ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); + WARN_ON(!drv); if (drv) drv->request_release(drv); - atomic_dec(&dev->core->mpeg_users); + dev->core->mpeg_users--; + + mutex_unlock(&dev->core->lock); return 0; } @@ -1334,11 +1339,9 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) blackbird_register_video(dev); /* initial device configuration: needed ? */ - mutex_lock(&dev->core->lock); // init_controls(core); cx88_set_tvnorm(core,core->tvnorm); cx88_video_mux(core,0); - mutex_unlock(&dev->core->lock); return 0; diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 7b8c9d3b6efc..c69df7ebb6a7 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -133,6 +133,7 @@ static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) return -EINVAL; } + mutex_lock(&dev->core->lock); drv = cx8802_get_driver(dev, CX88_MPEG_DVB); if (drv) { if (acquire){ @@ -143,6 +144,7 @@ static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) dev->frontends.active_fe_id = 0; } } + mutex_unlock(&dev->core->lock); return ret; } diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index addf9545e9bf..1a7b983f8297 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -78,6 +78,7 @@ static void flush_request_modules(struct cx8802_dev *dev) static LIST_HEAD(cx8802_devlist); +static DEFINE_MUTEX(cx8802_mutex); /* ------------------------------------------------------------------ */ static int cx8802_start_dma(struct cx8802_dev *dev, @@ -474,7 +475,7 @@ static int cx8802_init_common(struct cx8802_dev *dev) return -EIO; } - pci_read_config_byte(dev->pci, PCI_CLASS_REVISION, &dev->pci_rev); + dev->pci_rev = dev->pci->revision; pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat); printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", dev->core->name, @@ -624,13 +625,11 @@ static int cx8802_request_acquire(struct cx8802_driver *drv) if (drv->advise_acquire) { - mutex_lock(&drv->core->lock); core->active_ref++; if (core->active_type_id == CX88_BOARD_NONE) { core->active_type_id = drv->type_id; drv->advise_acquire(drv); } - mutex_unlock(&drv->core->lock); mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); } @@ -643,14 +642,12 @@ static int cx8802_request_release(struct cx8802_driver *drv) { struct cx88_core *core = drv->core; - mutex_lock(&drv->core->lock); if (drv->advise_release && --core->active_ref == 0) { drv->advise_release(drv); core->active_type_id = CX88_BOARD_NONE; mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); } - mutex_unlock(&drv->core->lock); return 0; } @@ -693,6 +690,8 @@ int cx8802_register_driver(struct cx8802_driver *drv) return err; } + mutex_lock(&cx8802_mutex); + list_for_each_entry(dev, &cx8802_devlist, devlist) { printk(KERN_INFO "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n", @@ -702,8 +701,10 @@ int cx8802_register_driver(struct cx8802_driver *drv) /* Bring up a new struct for each driver instance */ driver = kzalloc(sizeof(*drv),GFP_KERNEL); - if (driver == NULL) - return -ENOMEM; + if (driver == NULL) { + err = -ENOMEM; + goto out; + } /* Snapshot of the driver registration data */ drv->core = dev->core; @@ -713,21 +714,23 @@ int cx8802_register_driver(struct cx8802_driver *drv) drv->request_release = cx8802_request_release; memcpy(driver, drv, sizeof(*driver)); + mutex_lock(&drv->core->lock); err = drv->probe(driver); if (err == 0) { i++; - mutex_lock(&drv->core->lock); list_add_tail(&driver->drvlist, &dev->drvlist); - mutex_unlock(&drv->core->lock); } else { printk(KERN_ERR "%s/2: cx8802 probe failed, err = %d\n", dev->core->name, err); } - + mutex_unlock(&drv->core->lock); } - return i ? 0 : -ENODEV; + err = i ? 0 : -ENODEV; +out: + mutex_unlock(&cx8802_mutex); + return err; } int cx8802_unregister_driver(struct cx8802_driver *drv) @@ -741,6 +744,8 @@ int cx8802_unregister_driver(struct cx8802_driver *drv) drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); + mutex_lock(&cx8802_mutex); + list_for_each_entry(dev, &cx8802_devlist, devlist) { printk(KERN_INFO "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n", @@ -748,6 +753,8 @@ int cx8802_unregister_driver(struct cx8802_driver *drv) dev->pci->subsystem_device, dev->core->board.name, dev->core->boardnr); + mutex_lock(&dev->core->lock); + list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) { /* only unregister the correct driver type */ if (d->type_id != drv->type_id) @@ -755,17 +762,18 @@ int cx8802_unregister_driver(struct cx8802_driver *drv) err = d->remove(d); if (err == 0) { - mutex_lock(&drv->core->lock); list_del(&d->drvlist); - mutex_unlock(&drv->core->lock); kfree(d); } else printk(KERN_ERR "%s/2: cx8802 driver remove " "failed (%d)\n", dev->core->name, err); } + mutex_unlock(&dev->core->lock); } + mutex_unlock(&cx8802_mutex); + return err; } @@ -803,7 +811,9 @@ static int __devinit cx8802_probe(struct pci_dev *pci_dev, goto fail_free; INIT_LIST_HEAD(&dev->drvlist); + mutex_lock(&cx8802_mutex); list_add_tail(&dev->devlist,&cx8802_devlist); + mutex_unlock(&cx8802_mutex); /* now autoload cx88-dvb or cx88-blackbird */ request_modules(dev); @@ -827,6 +837,8 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev) flush_request_modules(dev); + mutex_lock(&dev->core->lock); + if (!list_empty(&dev->drvlist)) { struct cx8802_driver *drv, *tmp; int err; @@ -838,9 +850,7 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev) list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) { err = drv->remove(drv); if (err == 0) { - mutex_lock(&drv->core->lock); list_del(&drv->drvlist); - mutex_unlock(&drv->core->lock); } else printk(KERN_ERR "%s/2: cx8802 driver remove " "failed (%d)\n", dev->core->name, err); @@ -848,6 +858,8 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev) } } + mutex_unlock(&dev->core->lock); + /* Destroy any 8802 reference. */ dev->core->dvbdev = NULL; diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 287a41ee1c4f..cef4f282e5aa 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -824,7 +824,7 @@ static int video_open(struct file *file) call_all(core, tuner, s_radio); } - atomic_inc(&core->users); + core->users++; mutex_unlock(&core->lock); return 0; @@ -922,7 +922,8 @@ static int video_release(struct file *file) file->private_data = NULL; kfree(fh); - if(atomic_dec_and_test(&dev->core->users)) + dev->core->users--; + if (!dev->core->users) call_all(dev->core, core, s_power, 0); mutex_unlock(&dev->core->lock); @@ -1832,7 +1833,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, dev->core = core; /* print pci info */ - pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + dev->pci_rev = pci_dev->revision; pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", core->name, diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 9b3742a7746c..a399a8b086ba 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -389,8 +389,8 @@ struct cx88_core { struct mutex lock; /* various v4l controls */ u32 freq; - atomic_t users; - atomic_t mpeg_users; + int users; + int mpeg_users; /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ struct cx8802_dev *dvbdev; @@ -505,6 +505,8 @@ struct cx8802_driver { int (*suspend)(struct pci_dev *pci_dev, pm_message_t state); int (*resume)(struct pci_dev *pci_dev); + /* Callers to the following functions must hold core->lock */ + /* MPEG 8802 -> mini driver - Driver probe and configuration */ int (*probe)(struct cx8802_driver *drv); int (*remove)(struct cx8802_driver *drv); @@ -561,8 +563,9 @@ struct cx8802_dev { /* for switching modulation types */ unsigned char ts_gen_cntrl; - /* List of attached drivers */ + /* List of attached drivers; must hold core->lock to access */ struct list_head drvlist; + struct work_struct request_module_wk; }; @@ -685,6 +688,8 @@ int cx88_audio_thread(void *data); int cx8802_register_driver(struct cx8802_driver *drv); int cx8802_unregister_driver(struct cx8802_driver *drv); + +/* Caller must hold core->lock */ struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index 985100ea17a4..3cb78f26df90 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -38,6 +38,8 @@ config VIDEO_EM28XX_DVB select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_TDA10023 if !DVB_FE_CUSTOMISE select DVB_S921 if !DVB_FE_CUSTOMISE + select DVB_DRXD if !DVB_FE_CUSTOMISE + select DVB_CXD2820R if !DVB_FE_CUSTOMISE select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 69fcea82d01c..4e37375decf5 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -100,6 +100,13 @@ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { { -1, -1, -1, -1}, }; +/* Board Hauppauge WinTV HVR 900 (R2) digital */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = { + {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x0c, 0x0f, 10}, + { -1, -1, -1, -1}, +}; + /* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10}, @@ -282,6 +289,16 @@ static struct em28xx_reg_seq leadership_reset[] = { { -1, -1, -1, -1}, }; +/* 2013:024f PCTV Systems nanoStick T2 290e + * GPIO_6 - demod reset + * GPIO_7 - LED + */ +static struct em28xx_reg_seq pctv_290e[] = { + {EM2874_R80_GPIO, 0x00, 0xff, 80}, + {EM2874_R80_GPIO, 0x40, 0xff, 80}, /* GPIO_6 = 1 */ + {EM2874_R80_GPIO, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */ + {-1, -1, -1, -1}, +}; /* * Board definitions @@ -859,6 +876,8 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900R2_digital, .ir_codes = RC_MAP_HAUPPAUGE, .decoder = EM28XX_TVP5150, .input = { { @@ -1448,12 +1467,14 @@ struct em28xx_board em28xx_boards[] = { .gpio = pinnacle_hybrid_pro_analog, } }, }, - [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { - .name = "Pinnacle Hybrid Pro (2)", - .valid = EM28XX_BOARD_NOT_VALIDATED, + [EM2882_BOARD_PINNACLE_HYBRID_PRO_330E] = { + .name = "Pinnacle Hybrid Pro (330e)", .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900R2_digital, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1749,6 +1770,17 @@ struct em28xx_board em28xx_boards[] = { .dvb_gpio = kworld_a340_digital, .tuner_gpio = default_tuner_gpio, }, + /* 2013:024f PCTV Systems nanoStick T2 290e. + * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */ + [EM28174_BOARD_PCTV_290E] = { + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .name = "PCTV Systems nanoStick T2 290e", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_290e, + .has_dvb = 1, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1863,7 +1895,7 @@ struct usb_device_id em28xx_id_table[] = { { USB_DEVICE(0x2304, 0x021a), .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, { USB_DEVICE(0x2304, 0x0226), - .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO }, + .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO_330E }, { USB_DEVICE(0x2304, 0x0227), .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO }, { USB_DEVICE(0x0413, 0x6023), @@ -1876,6 +1908,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2860_BOARD_GADMEI_UTV330 }, { USB_DEVICE(0x1b80, 0xa340), .driver_info = EM2870_BOARD_KWORLD_A340 }, + { USB_DEVICE(0x2013, 0x024f), + .driver_info = EM28174_BOARD_PCTV_290E }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -2229,7 +2263,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: - /* djh - Not sure which demod we need here */ + case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: ctl->demod = XC3028_FE_DEFAULT; break; case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: @@ -2799,6 +2833,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; + case CHIP_ID_EM28174: + em28xx_info("chip ID is em28174\n"); + dev->reg_gpio_num = EM2874_R80_GPIO; + dev->wait_after_write = 0; + break; case CHIP_ID_EM2883: em28xx_info("chip ID is em2882/em2883\n"); dev->wait_after_write = 0; diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 44c63cbd6dda..e33f145d867a 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -489,7 +489,8 @@ int em28xx_audio_setup(struct em28xx *dev) int vid1, vid2, feat, cfg; u32 vid; - if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874) { + if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874 + || dev->chip_id == CHIP_ID_EM28174) { /* Digital only device - don't load any alsa module */ dev->audio_mode.has_audio = 0; dev->has_audio_class = 0; @@ -614,7 +615,7 @@ int em28xx_capture_start(struct em28xx *dev, int start) { int rc; - if (dev->chip_id == CHIP_ID_EM2874) { + if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) { /* The Transport Stream Enable Register moved in em2874 */ if (!start) { rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, @@ -1111,6 +1112,10 @@ int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev) /* FIXME - for now assume 564 like it was before, but the em2874 code should be added to return the proper value... */ packet_size = 564; + } else if (dev->chip_id == CHIP_ID_EM28174) { + /* FIXME same as em2874. 564 was enough for 22 Mbit DVB-T + but too much for 44 Mbit DVB-C. */ + packet_size = 752; } else { /* TS max packet size stored in bits 1-0 of R01 */ chip_cfg2 = em28xx_read_reg(dev, EM28XX_R01_CHIPCFG2); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index c7c04bf712aa..7904ca4b6913 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -38,6 +38,8 @@ #include "tda1002x.h" #include "tda18271.h" #include "s921.h" +#include "drxd.h" +#include "cxd2820r.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -58,7 +60,7 @@ if (debug >= level) \ #define EM28XX_DVB_MAX_PACKETS 64 struct em28xx_dvb { - struct dvb_frontend *frontend; + struct dvb_frontend *fe[2]; /* feed count management */ struct mutex lock; @@ -285,12 +287,13 @@ static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = { .if2 = 45600, }; -#ifdef EM28XX_DRX397XD_SUPPORT -/* [TODO] djh - not sure yet what the device config needs to contain */ -static struct drx397xD_config em28xx_drx397xD_with_xc3028 = { - .demod_address = (0xe0 >> 1), +static struct drxd_config em28xx_drxd = { + .index = 0, .demod_address = 0x70, .demod_revision = 0xa2, + .demoda_address = 0x00, .pll_address = 0x00, + .pll_type = DRXD_PLL_NONE, .clock = 12000, .insert_rs_byte = 1, + .pll_set = NULL, .osc_deviation = NULL, .IF = 42800000, + .disable_i2c_gate_ctrl = 1, }; -#endif static int mt352_terratec_xs_init(struct dvb_frontend *fe) { @@ -332,6 +335,26 @@ static struct tda10023_config em28xx_tda10023_config = { .invert = 1, }; +static struct cxd2820r_config em28xx_cxd2820r_config = { + .i2c_address = (0xd8 >> 1), + .ts_mode = CXD2820R_TS_SERIAL, + .if_dvbt_6 = 3300, + .if_dvbt_7 = 3500, + .if_dvbt_8 = 4000, + .if_dvbt2_6 = 3300, + .if_dvbt2_7 = 3500, + .if_dvbt2_8 = 4000, + .if_dvbc = 5000, + + /* enable LNA for DVB-T2 and DVB-C */ + .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, + .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, +}; + +static struct tda18271_config em28xx_cxd2820r_tda18271_config = { + .output_opt = TDA18271_OUTPUT_LT_OFF, +}; + /* ------------------------------------------------------------------ */ static int attach_xc3028(u8 addr, struct em28xx *dev) @@ -343,17 +366,17 @@ static int attach_xc3028(u8 addr, struct em28xx *dev) cfg.i2c_adap = &dev->i2c_adap; cfg.i2c_addr = addr; - if (!dev->dvb->frontend) { + if (!dev->dvb->fe[0]) { em28xx_errdev("/2: dvb frontend not attached. " "Can't attach xc3028\n"); return -EINVAL; } - fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg); + fe = dvb_attach(xc2028_attach, dev->dvb->fe[0], &cfg); if (!fe) { em28xx_errdev("/2: xc3028 attach failed\n"); - dvb_frontend_detach(dev->dvb->frontend); - dev->dvb->frontend = NULL; + dvb_frontend_detach(dev->dvb->fe[0]); + dev->dvb->fe[0] = NULL; return -EINVAL; } @@ -383,16 +406,28 @@ static int register_dvb(struct em28xx_dvb *dvb, } /* Ensure all frontends negotiate bus access */ - dvb->frontend->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; + dvb->fe[0]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; + if (dvb->fe[1]) + dvb->fe[1]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; dvb->adapter.priv = dev; /* register frontend */ - result = dvb_register_frontend(&dvb->adapter, dvb->frontend); + result = dvb_register_frontend(&dvb->adapter, dvb->fe[0]); if (result < 0) { printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n", dev->name, result); - goto fail_frontend; + goto fail_frontend0; + } + + /* register 2nd frontend */ + if (dvb->fe[1]) { + result = dvb_register_frontend(&dvb->adapter, dvb->fe[1]); + if (result < 0) { + printk(KERN_WARNING "%s: 2nd dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend1; + } } /* register demux stuff */ @@ -458,9 +493,14 @@ fail_fe_hw: fail_dmxdev: dvb_dmx_release(&dvb->demux); fail_dmx: - dvb_unregister_frontend(dvb->frontend); -fail_frontend: - dvb_frontend_detach(dvb->frontend); + if (dvb->fe[1]) + dvb_unregister_frontend(dvb->fe[1]); + dvb_unregister_frontend(dvb->fe[0]); +fail_frontend1: + if (dvb->fe[1]) + dvb_frontend_detach(dvb->fe[1]); +fail_frontend0: + dvb_frontend_detach(dvb->fe[0]); dvb_unregister_adapter(&dvb->adapter); fail_adapter: return result; @@ -473,12 +513,15 @@ static void unregister_dvb(struct em28xx_dvb *dvb) dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); dvb_dmxdev_release(&dvb->dmxdev); dvb_dmx_release(&dvb->demux); - dvb_unregister_frontend(dvb->frontend); - dvb_frontend_detach(dvb->frontend); + if (dvb->fe[1]) + dvb_unregister_frontend(dvb->fe[1]); + dvb_unregister_frontend(dvb->fe[0]); + if (dvb->fe[1]) + dvb_frontend_detach(dvb->fe[1]); + dvb_frontend_detach(dvb->fe[0]); dvb_unregister_adapter(&dvb->adapter); } - static int dvb_init(struct em28xx *dev) { int result = 0; @@ -497,16 +540,17 @@ static int dvb_init(struct em28xx *dev) return -ENOMEM; } dev->dvb = dvb; + dvb->fe[0] = dvb->fe[1] = NULL; mutex_lock(&dev->lock); em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); /* init frontend */ switch (dev->model) { case EM2874_LEADERSHIP_ISDBT: - dvb->frontend = dvb_attach(s921_attach, + dvb->fe[0] = dvb_attach(s921_attach, &sharp_isdbt, &dev->i2c_adap); - if (!dvb->frontend) { + if (!dvb->fe[0]) { result = -EINVAL; goto out_free; } @@ -516,7 +560,7 @@ static int dvb_init(struct em28xx *dev) case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: - dvb->frontend = dvb_attach(lgdt330x_attach, + dvb->fe[0] = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, &dev->i2c_adap); if (attach_xc3028(0x61, dev) < 0) { @@ -525,7 +569,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_KWORLD_DVB_310U: - dvb->frontend = dvb_attach(zl10353_attach, + dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_with_xc3028, &dev->i2c_adap); if (attach_xc3028(0x61, dev) < 0) { @@ -536,7 +580,7 @@ static int dvb_init(struct em28xx *dev) case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2882_BOARD_TERRATEC_HYBRID_XS: case EM2880_BOARD_EMPIRE_DUAL_TV: - dvb->frontend = dvb_attach(zl10353_attach, + dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); if (attach_xc3028(0x61, dev) < 0) { @@ -549,13 +593,13 @@ static int dvb_init(struct em28xx *dev) case EM2881_BOARD_PINNACLE_HYBRID_PRO: case EM2882_BOARD_DIKOM_DK300: case EM2882_BOARD_KWORLD_VS_DVBT: - dvb->frontend = dvb_attach(zl10353_attach, + dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); - if (dvb->frontend == NULL) { + if (dvb->fe[0] == NULL) { /* This board could have either a zl10353 or a mt352. If the chip id isn't for zl10353, try mt352 */ - dvb->frontend = dvb_attach(mt352_attach, + dvb->fe[0] = dvb_attach(mt352_attach, &terratec_xs_mt352_cfg, &dev->i2c_adap); } @@ -567,7 +611,7 @@ static int dvb_init(struct em28xx *dev) break; case EM2883_BOARD_KWORLD_HYBRID_330U: case EM2882_BOARD_EVGA_INDTUBE: - dvb->frontend = dvb_attach(s5h1409_attach, + dvb->fe[0] = dvb_attach(s5h1409_attach, &em28xx_s5h1409_with_xc3028, &dev->i2c_adap); if (attach_xc3028(0x61, dev) < 0) { @@ -576,11 +620,11 @@ static int dvb_init(struct em28xx *dev) } break; case EM2882_BOARD_KWORLD_ATSC_315U: - dvb->frontend = dvb_attach(lgdt330x_attach, + dvb->fe[0] = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, &dev->i2c_adap); - if (dvb->frontend != NULL) { - if (!dvb_attach(simple_tuner_attach, dvb->frontend, + if (dvb->fe[0] != NULL) { + if (!dvb_attach(simple_tuner_attach, dvb->fe[0], &dev->i2c_adap, 0x61, TUNER_THOMSON_DTT761X)) { result = -EINVAL; goto out_free; @@ -588,25 +632,21 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: -#ifdef EM28XX_DRX397XD_SUPPORT - /* We don't have the config structure properly populated, so - this is commented out for now */ - dvb->frontend = dvb_attach(drx397xD_attach, - &em28xx_drx397xD_with_xc3028, - &dev->i2c_adap); + case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: + dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL, + &dev->i2c_adap, &dev->udev->dev); if (attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; } break; -#endif case EM2870_BOARD_REDDO_DVB_C_USB_BOX: /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */ - dvb->frontend = dvb_attach(tda10023_attach, + dvb->fe[0] = dvb_attach(tda10023_attach, &em28xx_tda10023_config, &dev->i2c_adap, 0x48); - if (dvb->frontend) { - if (!dvb_attach(simple_tuner_attach, dvb->frontend, + if (dvb->fe[0]) { + if (!dvb_attach(simple_tuner_attach, dvb->fe[0], &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) { result = -EINVAL; goto out_free; @@ -614,25 +654,53 @@ static int dvb_init(struct em28xx *dev) } break; case EM2870_BOARD_KWORLD_A340: - dvb->frontend = dvb_attach(lgdt3305_attach, + dvb->fe[0] = dvb_attach(lgdt3305_attach, &em2870_lgdt3304_dev, &dev->i2c_adap); - if (dvb->frontend != NULL) - dvb_attach(tda18271_attach, dvb->frontend, 0x60, + if (dvb->fe[0] != NULL) + dvb_attach(tda18271_attach, dvb->fe[0], 0x60, &dev->i2c_adap, &kworld_a340_config); break; + case EM28174_BOARD_PCTV_290E: + /* MFE + * FE 0 = DVB-T/T2 + FE 1 = DVB-C, both sharing same tuner. */ + /* FE 0 */ + dvb->fe[0] = dvb_attach(cxd2820r_attach, + &em28xx_cxd2820r_config, &dev->i2c_adap, NULL); + if (dvb->fe[0]) { + struct i2c_adapter *i2c_tuner; + i2c_tuner = cxd2820r_get_tuner_i2c_adapter(dvb->fe[0]); + /* FE 0 attach tuner */ + if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + i2c_tuner, &em28xx_cxd2820r_tda18271_config)) { + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; + } + /* FE 1. This dvb_attach() cannot fail. */ + dvb->fe[1] = dvb_attach(cxd2820r_attach, NULL, NULL, + dvb->fe[0]); + dvb->fe[1]->id = 1; + /* FE 1 attach tuner */ + if (!dvb_attach(tda18271_attach, dvb->fe[1], 0x60, + i2c_tuner, &em28xx_cxd2820r_tda18271_config)) { + dvb_frontend_detach(dvb->fe[1]); + /* leave FE 0 still active */ + } + } + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); break; } - if (NULL == dvb->frontend) { + if (NULL == dvb->fe[0]) { em28xx_errdev("/2: frontend initialization failed\n"); result = -EINVAL; goto out_free; } /* define general-purpose callback pointer */ - dvb->frontend->callback = em28xx_tuner_callback; + dvb->fe[0]->callback = em28xx_tuner_callback; /* register everything */ result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 71474d31e155..4739fc7e6eb3 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -332,7 +332,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) struct em28xx_eeprom *em_eeprom = (void *)eedata; int i, err, size = len, block; - if (dev->chip_id == CHIP_ID_EM2874) { + if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) { /* Empia switched to a 16-bit addressable eeprom in newer devices. While we could certainly write a routine to read the eeprom, there is nothing of use in there that cannot be diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index 91e90559642b..e92a28ede434 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -201,6 +201,7 @@ enum em28xx_chip_id { CHIP_ID_EM2870 = 35, CHIP_ID_EM2883 = 36, CHIP_ID_EM2874 = 65, + CHIP_ID_EM28174 = 113, }; /* diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 6f2795a3d4b7..3cca33122450 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -97,7 +97,7 @@ #define EM2881_BOARD_PINNACLE_HYBRID_PRO 53 #define EM2882_BOARD_KWORLD_VS_DVBT 54 #define EM2882_BOARD_TERRATEC_HYBRID_XS 55 -#define EM2882_BOARD_PINNACLE_HYBRID_PRO 56 +#define EM2882_BOARD_PINNACLE_HYBRID_PRO_330E 56 #define EM2883_BOARD_KWORLD_HYBRID_330U 57 #define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 #define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 @@ -118,6 +118,7 @@ #define EM2882_BOARD_DIKOM_DK300 75 #define EM2870_BOARD_KWORLD_A340 76 #define EM2874_LEADERSHIP_ISDBT 77 +#define EM28174_BOARD_PCTV_290E 78 /* Limits minimum and default number of buffers */ diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c index 031af1610154..908d7012c3f2 100644 --- a/drivers/media/video/fsl-viu.c +++ b/drivers/media/video/fsl-viu.c @@ -766,7 +766,7 @@ inline void viu_activate_overlay(struct viu_reg *viu_reg) out_be32(&vr->picture_count, reg_val.picture_count); } -static int viu_start_preview(struct viu_dev *dev, struct viu_fh *fh) +static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh) { int bpp; @@ -805,11 +805,6 @@ static int viu_start_preview(struct viu_dev *dev, struct viu_fh *fh) /* setup the base address of the overlay buffer */ reg_val.field_base_addr = (u32)dev->ovbuf.base; - dev->ovenable = 1; - viu_activate_overlay(dev->vr); - - /* start dma */ - viu_start_dma(dev); return 0; } @@ -825,13 +820,11 @@ static int vidioc_s_fmt_overlay(struct file *file, void *priv, if (err) return err; - mutex_lock(&dev->lock); fh->win = f->fmt.win; spin_lock_irqsave(&dev->slock, flags); - viu_start_preview(dev, fh); + viu_setup_preview(dev, fh); spin_unlock_irqrestore(&dev->slock, flags); - mutex_unlock(&dev->lock); return 0; } @@ -841,6 +834,28 @@ static int vidioc_try_fmt_overlay(struct file *file, void *priv, return 0; } +static int vidioc_overlay(struct file *file, void *priv, unsigned int on) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + unsigned long flags; + + if (on) { + spin_lock_irqsave(&dev->slock, flags); + viu_activate_overlay(dev->vr); + dev->ovenable = 1; + + /* start dma */ + viu_start_dma(dev); + spin_unlock_irqrestore(&dev->slock, flags); + } else { + viu_stop_dma(dev); + dev->ovenable = 0; + } + + return 0; +} + int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) { struct viu_fh *fh = priv; @@ -911,12 +926,16 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (fh->type != i) return -EINVAL; + if (dev->ovenable) + dev->ovenable = 0; + viu_start_dma(fh->dev); return videobuf_streamon(&fh->vb_vidq); @@ -1311,7 +1330,8 @@ static int viu_open(struct file *file) videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops, dev->dev, &fh->vbq_lock, fh->type, V4L2_FIELD_INTERLACED, - sizeof(struct viu_buf), fh, NULL); + sizeof(struct viu_buf), fh, + &fh->dev->lock); return 0; } @@ -1401,7 +1421,7 @@ static struct v4l2_file_operations viu_fops = { .release = viu_release, .read = viu_read, .poll = viu_poll, - .ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ .mmap = viu_mmap, }; @@ -1415,6 +1435,7 @@ static const struct v4l2_ioctl_ops viu_ioctl_ops = { .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay, .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay, .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_overlay, + .vidioc_overlay = vidioc_overlay, .vidioc_g_fbuf = vidioc_g_fbuf, .vidioc_s_fbuf = vidioc_s_fbuf, .vidioc_reqbufs = vidioc_reqbufs, @@ -1498,9 +1519,6 @@ static int __devinit viu_of_probe(struct platform_device *op) INIT_LIST_HEAD(&viu_dev->vidq.active); INIT_LIST_HEAD(&viu_dev->vidq.queued); - /* initialize locks */ - mutex_init(&viu_dev->lock); - snprintf(viu_dev->v4l2_dev.name, sizeof(viu_dev->v4l2_dev.name), "%s", "VIU"); ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev); @@ -1531,8 +1549,15 @@ static int __devinit viu_of_probe(struct platform_device *op) viu_dev->vdev = vdev; + /* initialize locks */ + mutex_init(&viu_dev->lock); + viu_dev->vdev->lock = &viu_dev->lock; + spin_lock_init(&viu_dev->slock); + video_set_drvdata(viu_dev->vdev, viu_dev); + mutex_lock(&viu_dev->lock); + ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { video_device_release(viu_dev->vdev); @@ -1559,6 +1584,8 @@ static int __devinit viu_of_probe(struct platform_device *op) goto err_irq; } + mutex_unlock(&viu_dev->lock); + dev_info(&op->dev, "Freescale VIU Video Capture Board\n"); return ret; @@ -1568,6 +1595,7 @@ err_irq: err_clk: video_unregister_device(viu_dev->vdev); err_vdev: + mutex_unlock(&viu_dev->lock); i2c_put_adapter(ad); v4l2_device_unregister(&viu_dev->v4l2_dev); err: diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index eb04e8b59989..34ae2c299799 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -77,6 +77,15 @@ config USB_GSPCA_JEILINJ To compile this driver as a module, choose M here: the module will be called gspca_jeilinj. +config USB_GSPCA_KINECT + tristate "Kinect sensor device USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for the Microsoft Kinect sensor device. + + To compile this driver as a module, choose M here: the + module will be called gspca_kinect. + config USB_GSPCA_KONICA tristate "Konica USB Camera V4L2 driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 855fbc8c9c47..802fbe1bff4a 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o +obj-$(CONFIG_USB_GSPCA_KINECT) += gspca_kinect.o obj-$(CONFIG_USB_GSPCA_KONICA) += gspca_konica.o obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o @@ -46,6 +47,7 @@ gspca_cpia1-objs := cpia1.o gspca_etoms-objs := etoms.o gspca_finepix-objs := finepix.o gspca_jeilinj-objs := jeilinj.o +gspca_kinect-objs := kinect.o gspca_konica-objs := konica.o gspca_mars-objs := mars.o gspca_mr97310a-objs := mr97310a.o diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c index 9ddbac680663..f2a9451eea19 100644 --- a/drivers/media/video/gspca/cpia1.c +++ b/drivers/media/video/gspca/cpia1.c @@ -1262,7 +1262,7 @@ static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply) static void monitor_exposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 exp_acc, bcomp, gain, coarseL, cmd[8]; + u8 exp_acc, bcomp, cmd[8]; int ret, light_exp, dark_exp, very_dark_exp; int old_exposure, new_exposure, framerate; int setfps = 0, setexp = 0, setflicker = 0; @@ -1284,8 +1284,6 @@ static void monitor_exposure(struct gspca_dev *gspca_dev) } exp_acc = gspca_dev->usb_buf[0]; bcomp = gspca_dev->usb_buf[1]; - gain = gspca_dev->usb_buf[2]; - coarseL = gspca_dev->usb_buf[3]; light_exp = sd->params.colourParams.brightness + TC - 50 + EXP_ACC_LIGHT; @@ -1772,9 +1770,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { -#ifdef GSPCA_DEBUG struct sd *sd = (struct sd *) gspca_dev; -#endif int ret; /* Start / Stop the camera to make sure we are talking to diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index 99083038cec3..e8e071aa212f 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -499,21 +499,8 @@ MODULE_DEVICE_TABLE(usb, device_table); static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct gspca_dev *gspca_dev; - s32 ret; - - ret = gspca_dev_probe(intf, id, + return gspca_dev_probe(intf, id, &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE); - - if (ret >= 0) { - gspca_dev = usb_get_intfdata(intf); - - PDEBUG(D_PROBE, - "Camera is now controlling video device %s", - video_device_node_name(&gspca_dev->vdev)); - } - - return ret; } static void sd_disconnect(struct usb_interface *intf) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index e526aa3dedaf..08ce9948d99b 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -55,7 +55,7 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 12, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 13, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -2495,6 +2495,6 @@ module_exit(gspca_exit); module_param_named(debug, gspca_debug, int, 0644); MODULE_PARM_DESC(debug, "Debug (bit) 0x01:error 0x02:probe 0x04:config" - " 0x08:stream 0x10:frame 0x20:packet 0x40:USBin 0x80:USBout" + " 0x08:stream 0x10:frame 0x20:packet" " 0x0100: v4l2"); #endif diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 41755226d389..49e2fcbe81fb 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -9,7 +9,7 @@ #include <linux/mutex.h> /* compilation option */ -#define GSPCA_DEBUG 1 +/*#define GSPCA_DEBUG 1*/ #ifdef GSPCA_DEBUG /* GSPCA our debug messages */ @@ -25,8 +25,8 @@ extern int gspca_debug; #define D_STREAM 0x08 #define D_FRAM 0x10 #define D_PACK 0x20 -#define D_USBI 0x40 -#define D_USBO 0x80 +#define D_USBI 0x00 +#define D_USBO 0x00 #define D_V4L2 0x0100 #else #define PDEBUG(level, fmt, args...) diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index 36dae38b1e38..1bd9c4b542dd 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -6,6 +6,9 @@ * * Copyright (C) 2009 Theodore Kilgore * + * Sportscam DV15 support and control settings are + * Copyright (C) 2011 Patrice Chotard + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -23,7 +26,6 @@ #define MODULE_NAME "jeilinj" -#include <linux/workqueue.h> #include <linux/slab.h> #include "gspca.h" #include "jpeg.h" @@ -34,29 +36,51 @@ MODULE_LICENSE("GPL"); /* Default timeouts, in ms */ #define JEILINJ_CMD_TIMEOUT 500 +#define JEILINJ_CMD_DELAY 160 #define JEILINJ_DATA_TIMEOUT 1000 /* Maximum transfer size to use. */ #define JEILINJ_MAX_TRANSFER 0x200 - #define FRAME_HEADER_LEN 0x10 +#define FRAME_START 0xFFFFFFFF + +enum { + SAKAR_57379, + SPORTSCAM_DV15, +}; + +#define CAMQUALITY_MIN 0 /* highest cam quality */ +#define CAMQUALITY_MAX 97 /* lowest cam quality */ + +enum e_ctrl { + LIGHTFREQ, + AUTOGAIN, + RED, + GREEN, + BLUE, + NCTRLS /* number of controls */ +}; /* Structure to hold all of our device specific stuff */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + struct gspca_ctrl ctrls[NCTRLS]; + int blocks_left; const struct v4l2_pix_format *cap_mode; /* Driver stuff */ - struct work_struct work_struct; - struct workqueue_struct *work_thread; + u8 type; u8 quality; /* image quality */ - u8 jpegqual; /* webcam quality */ +#define QUALITY_MIN 35 +#define QUALITY_MAX 85 +#define QUALITY_DEF 85 u8 jpeg_hdr[JPEG_HDR_SZ]; }; - struct jlj_command { - unsigned char instruction[2]; - unsigned char ack_wanted; - }; +struct jlj_command { + unsigned char instruction[2]; + unsigned char ack_wanted; + unsigned char delay; +}; /* AFAICT these cameras will only do 320x240. */ static struct v4l2_pix_format jlj_mode[] = { @@ -64,6 +88,11 @@ static struct v4l2_pix_format jlj_mode[] = { .bytesperline = 320, .sizeimage = 320 * 240, .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, + { 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0} }; @@ -73,178 +102,295 @@ static struct v4l2_pix_format jlj_mode[] = { */ /* All commands are two bytes only */ -static int jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command) +static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command) { int retval; + if (gspca_dev->usb_err < 0) + return; memcpy(gspca_dev->usb_buf, command, 2); retval = usb_bulk_msg(gspca_dev->dev, usb_sndbulkpipe(gspca_dev->dev, 3), gspca_dev->usb_buf, 2, NULL, 500); - if (retval < 0) + if (retval < 0) { err("command write [%02x] error %d", gspca_dev->usb_buf[0], retval); - return retval; + gspca_dev->usb_err = retval; + } } /* Responses are one byte only */ -static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response) +static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response) { int retval; + if (gspca_dev->usb_err < 0) + return; retval = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x84), gspca_dev->usb_buf, 1, NULL, 500); response = gspca_dev->usb_buf[0]; - if (retval < 0) + if (retval < 0) { err("read command [%02x] error %d", gspca_dev->usb_buf[0], retval); - return retval; + gspca_dev->usb_err = retval; + } } -static int jlj_start(struct gspca_dev *gspca_dev) +static void setfreq(struct gspca_dev *gspca_dev) { - int i; - int retval = -1; - u8 response = 0xff; - struct jlj_command start_commands[] = { - {{0x71, 0x81}, 0}, - {{0x70, 0x05}, 0}, - {{0x95, 0x70}, 1}, - {{0x71, 0x81}, 0}, - {{0x70, 0x04}, 0}, - {{0x95, 0x70}, 1}, - {{0x71, 0x00}, 0}, - {{0x70, 0x08}, 0}, - {{0x95, 0x70}, 1}, - {{0x94, 0x02}, 0}, - {{0xde, 0x24}, 0}, - {{0x94, 0x02}, 0}, - {{0xdd, 0xf0}, 0}, - {{0x94, 0x02}, 0}, - {{0xe3, 0x2c}, 0}, - {{0x94, 0x02}, 0}, - {{0xe4, 0x00}, 0}, - {{0x94, 0x02}, 0}, - {{0xe5, 0x00}, 0}, - {{0x94, 0x02}, 0}, - {{0xe6, 0x2c}, 0}, - {{0x94, 0x03}, 0}, - {{0xaa, 0x00}, 0}, - {{0x71, 0x1e}, 0}, - {{0x70, 0x06}, 0}, - {{0x71, 0x80}, 0}, - {{0x70, 0x07}, 0} + struct sd *sd = (struct sd *) gspca_dev; + u8 freq_commands[][2] = { + {0x71, 0x80}, + {0x70, 0x07} }; - for (i = 0; i < ARRAY_SIZE(start_commands); i++) { - retval = jlj_write2(gspca_dev, start_commands[i].instruction); - if (retval < 0) - return retval; - if (start_commands[i].ack_wanted) - retval = jlj_read1(gspca_dev, response); - if (retval < 0) - return retval; - } - PDEBUG(D_ERR, "jlj_start retval is %d", retval); - return retval; + + freq_commands[0][1] |= (sd->ctrls[LIGHTFREQ].val >> 1); + + jlj_write2(gspca_dev, freq_commands[0]); + jlj_write2(gspca_dev, freq_commands[1]); } -static int jlj_stop(struct gspca_dev *gspca_dev) +static void setcamquality(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 quality_commands[][2] = { + {0x71, 0x1E}, + {0x70, 0x06} + }; + u8 camquality; + + /* adapt camera quality from jpeg quality */ + camquality = ((QUALITY_MAX - sd->quality) * CAMQUALITY_MAX) + / (QUALITY_MAX - QUALITY_MIN); + quality_commands[0][1] += camquality; + + jlj_write2(gspca_dev, quality_commands[0]); + jlj_write2(gspca_dev, quality_commands[1]); +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 autogain_commands[][2] = { + {0x94, 0x02}, + {0xcf, 0x00} + }; + + autogain_commands[1][1] = (sd->ctrls[AUTOGAIN].val << 4); + + jlj_write2(gspca_dev, autogain_commands[0]); + jlj_write2(gspca_dev, autogain_commands[1]); +} + +static void setred(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 setred_commands[][2] = { + {0x94, 0x02}, + {0xe6, 0x00} + }; + + setred_commands[1][1] = sd->ctrls[RED].val; + + jlj_write2(gspca_dev, setred_commands[0]); + jlj_write2(gspca_dev, setred_commands[1]); +} + +static void setgreen(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 setgreen_commands[][2] = { + {0x94, 0x02}, + {0xe7, 0x00} + }; + + setgreen_commands[1][1] = sd->ctrls[GREEN].val; + + jlj_write2(gspca_dev, setgreen_commands[0]); + jlj_write2(gspca_dev, setgreen_commands[1]); +} + +static void setblue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 setblue_commands[][2] = { + {0x94, 0x02}, + {0xe9, 0x00} + }; + + setblue_commands[1][1] = sd->ctrls[BLUE].val; + + jlj_write2(gspca_dev, setblue_commands[0]); + jlj_write2(gspca_dev, setblue_commands[1]); +} + +static const struct ctrl sd_ctrls[NCTRLS] = { +[LIGHTFREQ] = { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, /* 1 */ + .maximum = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, /* 2 */ + .step = 1, + .default_value = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + }, + .set_control = setfreq + }, +[AUTOGAIN] = { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Automatic Gain (and Exposure)", + .minimum = 0, + .maximum = 3, + .step = 1, +#define AUTOGAIN_DEF 0 + .default_value = AUTOGAIN_DEF, + }, + .set_control = setautogain + }, +[RED] = { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0, + .maximum = 3, + .step = 1, +#define RED_BALANCE_DEF 2 + .default_value = RED_BALANCE_DEF, + }, + .set_control = setred + }, + +[GREEN] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0, + .maximum = 3, + .step = 1, +#define GREEN_BALANCE_DEF 2 + .default_value = GREEN_BALANCE_DEF, + }, + .set_control = setgreen + }, +[BLUE] = { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0, + .maximum = 3, + .step = 1, +#define BLUE_BALANCE_DEF 2 + .default_value = BLUE_BALANCE_DEF, + }, + .set_control = setblue + }, +}; + +static int jlj_start(struct gspca_dev *gspca_dev) { int i; - int retval; - struct jlj_command stop_commands[] = { - {{0x71, 0x00}, 0}, - {{0x70, 0x09}, 0}, - {{0x71, 0x80}, 0}, - {{0x70, 0x05}, 0} + int start_commands_size; + u8 response = 0xff; + struct sd *sd = (struct sd *) gspca_dev; + struct jlj_command start_commands[] = { + {{0x71, 0x81}, 0, 0}, + {{0x70, 0x05}, 0, JEILINJ_CMD_DELAY}, + {{0x95, 0x70}, 1, 0}, + {{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0}, + {{0x70, 0x04}, 0, JEILINJ_CMD_DELAY}, + {{0x95, 0x70}, 1, 0}, + {{0x71, 0x00}, 0, 0}, /* start streaming ??*/ + {{0x70, 0x08}, 0, JEILINJ_CMD_DELAY}, + {{0x95, 0x70}, 1, 0}, +#define SPORTSCAM_DV15_CMD_SIZE 9 + {{0x94, 0x02}, 0, 0}, + {{0xde, 0x24}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xdd, 0xf0}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xe3, 0x2c}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xe4, 0x00}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xe5, 0x00}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xe6, 0x2c}, 0, 0}, + {{0x94, 0x03}, 0, 0}, + {{0xaa, 0x00}, 0, 0} }; - for (i = 0; i < ARRAY_SIZE(stop_commands); i++) { - retval = jlj_write2(gspca_dev, stop_commands[i].instruction); - if (retval < 0) - return retval; + + sd->blocks_left = 0; + /* Under Windows, USB spy shows that only the 9 first start + * commands are used for SPORTSCAM_DV15 webcam + */ + if (sd->type == SPORTSCAM_DV15) + start_commands_size = SPORTSCAM_DV15_CMD_SIZE; + else + start_commands_size = ARRAY_SIZE(start_commands); + + for (i = 0; i < start_commands_size; i++) { + jlj_write2(gspca_dev, start_commands[i].instruction); + if (start_commands[i].delay) + msleep(start_commands[i].delay); + if (start_commands[i].ack_wanted) + jlj_read1(gspca_dev, response); } - return retval; + setcamquality(gspca_dev); + msleep(2); + setfreq(gspca_dev); + if (gspca_dev->usb_err < 0) + PDEBUG(D_ERR, "Start streaming command failed"); + return gspca_dev->usb_err; } -/* This function is called as a workqueue function and runs whenever the camera - * is streaming data. Because it is a workqueue function it is allowed to sleep - * so we can use synchronous USB calls. To avoid possible collisions with other - * threads attempting to use the camera's USB interface the gspca usb_lock is - * used when performing the one USB control operation inside the workqueue, - * which tells the camera to close the stream. In practice the only thing - * which needs to be protected against is the usb_set_interface call that - * gspca makes during stream_off. Otherwise the camera doesn't provide any - * controls that the user could try to change. - */ - -static void jlj_dostream(struct work_struct *work) +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) { - struct sd *dev = container_of(work, struct sd, work_struct); - struct gspca_dev *gspca_dev = &dev->gspca_dev; - int blocks_left; /* 0x200-sized blocks remaining in current frame. */ - int act_len; + struct sd *sd = (struct sd *) gspca_dev; int packet_type; - int ret; - u8 *buffer; + u32 header_marker; - buffer = kmalloc(JEILINJ_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); - if (!buffer) { - err("Couldn't allocate USB buffer"); - goto quit_stream; + PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0", + len, JEILINJ_MAX_TRANSFER); + if (len != JEILINJ_MAX_TRANSFER) { + PDEBUG(D_PACK, "bad length"); + goto discard; } - while (gspca_dev->present && gspca_dev->streaming) { - /* - * Now request data block 0. Line 0 reports the size - * to download, in blocks of size 0x200, and also tells the - * "actual" data size, in bytes, which seems best to ignore. - */ - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x82), - buffer, JEILINJ_MAX_TRANSFER, &act_len, - JEILINJ_DATA_TIMEOUT); - PDEBUG(D_STREAM, - "Got %d bytes out of %d for Block 0", - act_len, JEILINJ_MAX_TRANSFER); - if (ret < 0 || act_len < FRAME_HEADER_LEN) - goto quit_stream; - blocks_left = buffer[0x0a] - 1; - PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left); - + /* check if it's start of frame */ + header_marker = ((u32 *)data)[0]; + if (header_marker == FRAME_START) { + sd->blocks_left = data[0x0a] - 1; + PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left); /* Start a new frame, and add the JPEG header, first thing */ gspca_frame_add(gspca_dev, FIRST_PACKET, - dev->jpeg_hdr, JPEG_HDR_SZ); + sd->jpeg_hdr, JPEG_HDR_SZ); /* Toss line 0 of data block 0, keep the rest. */ gspca_frame_add(gspca_dev, INTER_PACKET, - buffer + FRAME_HEADER_LEN, + data + FRAME_HEADER_LEN, JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); - - while (blocks_left > 0) { - if (!gspca_dev->present) - goto quit_stream; - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x82), - buffer, JEILINJ_MAX_TRANSFER, &act_len, - JEILINJ_DATA_TIMEOUT); - if (ret < 0 || act_len < JEILINJ_MAX_TRANSFER) - goto quit_stream; - PDEBUG(D_STREAM, - "%d blocks remaining for frame", blocks_left); - blocks_left -= 1; - if (blocks_left == 0) - packet_type = LAST_PACKET; - else - packet_type = INTER_PACKET; - gspca_frame_add(gspca_dev, packet_type, - buffer, JEILINJ_MAX_TRANSFER); - } - } -quit_stream: - mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->present) - jlj_stop(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - kfree(buffer); + } else if (sd->blocks_left > 0) { + PDEBUG(D_STREAM, "%d blocks remaining for frame", + sd->blocks_left); + sd->blocks_left -= 1; + if (sd->blocks_left == 0) + packet_type = LAST_PACKET; + else + packet_type = INTER_PACKET; + gspca_frame_add(gspca_dev, packet_type, + data, JEILINJ_MAX_TRANSFER); + } else + goto discard; + return; +discard: + /* Discard data until a new frame starts. */ + gspca_dev->last_packet_type = DISCARD_PACKET; } /* This function is called at probe time just before sd_init */ @@ -254,78 +400,169 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam = &gspca_dev->cam; struct sd *dev = (struct sd *) gspca_dev; - dev->quality = 85; - dev->jpegqual = 85; + dev->type = id->driver_info; + gspca_dev->cam.ctrls = dev->ctrls; + dev->quality = QUALITY_DEF; + dev->ctrls[LIGHTFREQ].def = V4L2_CID_POWER_LINE_FREQUENCY_60HZ; + dev->ctrls[RED].def = RED_BALANCE_DEF; + dev->ctrls[GREEN].def = GREEN_BALANCE_DEF; + dev->ctrls[BLUE].def = BLUE_BALANCE_DEF; PDEBUG(D_PROBE, "JEILINJ camera detected" " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); cam->cam_mode = jlj_mode; - cam->nmodes = 1; + cam->nmodes = ARRAY_SIZE(jlj_mode); cam->bulk = 1; - /* We don't use the buffer gspca allocates so make it small. */ - cam->bulk_size = 32; - INIT_WORK(&dev->work_struct, jlj_dostream); + cam->bulk_nurbs = 1; + cam->bulk_size = JEILINJ_MAX_TRANSFER; return 0; } -/* called on streamoff with alt==0 and on disconnect */ -/* the usb_lock is held at entry - restore on exit */ -static void sd_stop0(struct gspca_dev *gspca_dev) +static void sd_stopN(struct gspca_dev *gspca_dev) { - struct sd *dev = (struct sd *) gspca_dev; + int i; + u8 *buf; + u8 stop_commands[][2] = { + {0x71, 0x00}, + {0x70, 0x09}, + {0x71, 0x80}, + {0x70, 0x05} + }; + + for (;;) { + /* get the image remaining blocks */ + usb_bulk_msg(gspca_dev->dev, + gspca_dev->urb[0]->pipe, + gspca_dev->urb[0]->transfer_buffer, + JEILINJ_MAX_TRANSFER, NULL, + JEILINJ_DATA_TIMEOUT); + + /* search for 0xff 0xd9 (EOF for JPEG) */ + i = 0; + buf = gspca_dev->urb[0]->transfer_buffer; + while ((i < (JEILINJ_MAX_TRANSFER - 1)) && + ((buf[i] != 0xff) || (buf[i+1] != 0xd9))) + i++; - /* wait for the work queue to terminate */ - mutex_unlock(&gspca_dev->usb_lock); - /* This waits for jlj_dostream to finish */ - destroy_workqueue(dev->work_thread); - dev->work_thread = NULL; - mutex_lock(&gspca_dev->usb_lock); + if (i != (JEILINJ_MAX_TRANSFER - 1)) + /* last remaining block found */ + break; + } + + for (i = 0; i < ARRAY_SIZE(stop_commands); i++) + jlj_write2(gspca_dev, stop_commands[i]); } /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - return 0; + return gspca_dev->usb_err; } /* Set up for getting frames. */ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *dev = (struct sd *) gspca_dev; - int ret; /* create the JPEG header */ jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(dev->jpeg_hdr, dev->quality); - PDEBUG(D_STREAM, "Start streaming at 320x240"); - ret = jlj_start(gspca_dev); - if (ret < 0) { - PDEBUG(D_ERR, "Start streaming command failed"); - return ret; - } - /* Start the workqueue function to do the streaming */ - dev->work_thread = create_singlethread_workqueue(MODULE_NAME); - queue_work(dev->work_thread, &dev->work_struct); - - return 0; + PDEBUG(D_STREAM, "Start streaming at %dx%d", + gspca_dev->height, gspca_dev->width); + jlj_start(gspca_dev); + return gspca_dev->usb_err; } /* Table of supported USB devices */ static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0979, 0x0280)}, + {USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379}, + {USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15}, {} }; MODULE_DEVICE_TABLE(usb, device_table); +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "disable"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + if (gspca_dev->streaming) { + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + setcamquality(gspca_dev); + } + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + + /* sub-driver description */ -static const struct sd_desc sd_desc = { +static const struct sd_desc sd_desc_sakar_57379 = { .name = MODULE_NAME, .config = sd_config, .init = sd_init, .start = sd_start, - .stop0 = sd_stop0, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* sub-driver description */ +static const struct sd_desc sd_desc_sportscam_dv15 = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .querymenu = sd_querymenu, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, +}; + +static const struct sd_desc *sd_desc[2] = { + &sd_desc_sakar_57379, + &sd_desc_sportscam_dv15 }; /* -- device connect -- */ @@ -333,7 +570,7 @@ static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { return gspca_dev_probe(intf, id, - &sd_desc, + sd_desc[id->driver_info], sizeof(struct sd), THIS_MODULE); } diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c new file mode 100644 index 000000000000..66671a4092e4 --- /dev/null +++ b/drivers/media/video/gspca/kinect.c @@ -0,0 +1,429 @@ +/* + * kinect sensor device camera, gspca driver + * + * Copyright (C) 2011 Antonio Ospite <ospite@studenti.unina.it> + * + * Based on the OpenKinect project and libfreenect + * http://openkinect.org/wiki/Init_Analysis + * + * Special thanks to Steven Toth and kernellabs.com for sponsoring a Kinect + * sensor device which I tested the driver on. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "kinect" + +#include "gspca.h" + +#define CTRL_TIMEOUT 500 + +MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); +MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#ifdef DEBUG +int gspca_debug = D_ERR | D_PROBE | D_CONF | D_STREAM | D_FRAM | D_PACK | + D_USBI | D_USBO | D_V4L2; +#endif + +struct pkt_hdr { + uint8_t magic[2]; + uint8_t pad; + uint8_t flag; + uint8_t unk1; + uint8_t seq; + uint8_t unk2; + uint8_t unk3; + uint32_t timestamp; +}; + +struct cam_hdr { + uint8_t magic[2]; + uint16_t len; + uint16_t cmd; + uint16_t tag; +}; + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + uint16_t cam_tag; /* a sequence number for packets */ + uint8_t stream_flag; /* to identify different stream types */ + uint8_t obuf[0x400]; /* output buffer for control commands */ + uint8_t ibuf[0x200]; /* input buffer for control commands */ +}; + +/* V4L2 controls supported by the driver */ +/* controls prototypes here */ + +static const struct ctrl sd_ctrls[] = { +}; + +#define MODE_640x480 0x0001 +#define MODE_640x488 0x0002 +#define MODE_1280x1024 0x0004 + +#define FORMAT_BAYER 0x0010 +#define FORMAT_UYVY 0x0020 +#define FORMAT_Y10B 0x0040 + +#define FPS_HIGH 0x0100 + +static const struct v4l2_pix_format video_camera_mode[] = { + {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_640x480 | FORMAT_BAYER | FPS_HIGH}, + {640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_640x480 | FORMAT_UYVY}, + {1280, 1024, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_1280x1024 | FORMAT_BAYER}, + {640, 488, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE, + .bytesperline = 640 * 10 / 8, + .sizeimage = 640 * 488 * 10 / 8, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_640x488 | FORMAT_Y10B | FPS_HIGH}, + {1280, 1024, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE, + .bytesperline = 1280 * 10 / 8, + .sizeimage = 1280 * 1024 * 10 / 8, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_1280x1024 | FORMAT_Y10B}, +}; + +static int kinect_write(struct usb_device *udev, uint8_t *data, + uint16_t wLength) +{ + return usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + 0x00, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, data, wLength, CTRL_TIMEOUT); +} + +static int kinect_read(struct usb_device *udev, uint8_t *data, uint16_t wLength) +{ + return usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, data, wLength, CTRL_TIMEOUT); +} + +static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, + unsigned int cmd_len, void *replybuf, unsigned int reply_len) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *udev = gspca_dev->dev; + int res, actual_len; + uint8_t *obuf = sd->obuf; + uint8_t *ibuf = sd->ibuf; + struct cam_hdr *chdr = (void *)obuf; + struct cam_hdr *rhdr = (void *)ibuf; + + if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) { + err("send_cmd: Invalid command length (0x%x)", cmd_len); + return -1; + } + + chdr->magic[0] = 0x47; + chdr->magic[1] = 0x4d; + chdr->cmd = cpu_to_le16(cmd); + chdr->tag = cpu_to_le16(sd->cam_tag); + chdr->len = cpu_to_le16(cmd_len / 2); + + memcpy(obuf+sizeof(*chdr), cmdbuf, cmd_len); + + res = kinect_write(udev, obuf, cmd_len + sizeof(*chdr)); + PDEBUG(D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d", cmd, + sd->cam_tag, cmd_len, res); + if (res < 0) { + err("send_cmd: Output control transfer failed (%d)", res); + return res; + } + + do { + actual_len = kinect_read(udev, ibuf, 0x200); + } while (actual_len == 0); + PDEBUG(D_USBO, "Control reply: %d", res); + if (actual_len < sizeof(*rhdr)) { + err("send_cmd: Input control transfer failed (%d)", res); + return res; + } + actual_len -= sizeof(*rhdr); + + if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) { + err("send_cmd: Bad magic %02x %02x", rhdr->magic[0], + rhdr->magic[1]); + return -1; + } + if (rhdr->cmd != chdr->cmd) { + err("send_cmd: Bad cmd %02x != %02x", rhdr->cmd, chdr->cmd); + return -1; + } + if (rhdr->tag != chdr->tag) { + err("send_cmd: Bad tag %04x != %04x", rhdr->tag, chdr->tag); + return -1; + } + if (cpu_to_le16(rhdr->len) != (actual_len/2)) { + err("send_cmd: Bad len %04x != %04x", + cpu_to_le16(rhdr->len), (int)(actual_len/2)); + return -1; + } + + if (actual_len > reply_len) { + warn("send_cmd: Data buffer is %d bytes long, but got %d bytes", + reply_len, actual_len); + memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len); + } else { + memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len); + } + + sd->cam_tag++; + + return actual_len; +} + +static int write_register(struct gspca_dev *gspca_dev, uint16_t reg, + uint16_t data) +{ + uint16_t reply[2]; + uint16_t cmd[2]; + int res; + + cmd[0] = cpu_to_le16(reg); + cmd[1] = cpu_to_le16(data); + + PDEBUG(D_USBO, "Write Reg 0x%04x <= 0x%02x", reg, data); + res = send_cmd(gspca_dev, 0x03, cmd, 4, reply, 4); + if (res < 0) + return res; + if (res != 2) { + warn("send_cmd returned %d [%04x %04x], 0000 expected", + res, reply[0], reply[1]); + } + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + sd->cam_tag = 0; + + /* Only video stream is supported for now, + * which has stream flag = 0x80 */ + sd->stream_flag = 0x80; + + cam = &gspca_dev->cam; + + cam->cam_mode = video_camera_mode; + cam->nmodes = ARRAY_SIZE(video_camera_mode); + +#if 0 + /* Setting those values is not needed for video stream */ + cam->npkt = 15; + gspca_dev->pkt_size = 960 * 2; +#endif + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + PDEBUG(D_PROBE, "Kinect Camera device."); + + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + int mode; + uint8_t fmt_reg, fmt_val; + uint8_t res_reg, res_val; + uint8_t fps_reg, fps_val; + uint8_t mode_val; + + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + + if (mode & FORMAT_Y10B) { + fmt_reg = 0x19; + res_reg = 0x1a; + fps_reg = 0x1b; + mode_val = 0x03; + } else { + fmt_reg = 0x0c; + res_reg = 0x0d; + fps_reg = 0x0e; + mode_val = 0x01; + } + + /* format */ + if (mode & FORMAT_UYVY) + fmt_val = 0x05; + else + fmt_val = 0x00; + + if (mode & MODE_1280x1024) + res_val = 0x02; + else + res_val = 0x01; + + if (mode & FPS_HIGH) + fps_val = 0x1e; + else + fps_val = 0x0f; + + + /* turn off IR-reset function */ + write_register(gspca_dev, 0x105, 0x00); + + /* Reset video stream */ + write_register(gspca_dev, 0x05, 0x00); + + /* Due to some ridiculous condition in the firmware, we have to start + * and stop the depth stream before the camera will hand us 1280x1024 + * IR. This is a stupid workaround, but we've yet to find a better + * solution. + * + * Thanks to Drew Fisher for figuring this out. + */ + if (mode & (FORMAT_Y10B | MODE_1280x1024)) { + write_register(gspca_dev, 0x13, 0x01); + write_register(gspca_dev, 0x14, 0x1e); + write_register(gspca_dev, 0x06, 0x02); + write_register(gspca_dev, 0x06, 0x00); + } + + write_register(gspca_dev, fmt_reg, fmt_val); + write_register(gspca_dev, res_reg, res_val); + write_register(gspca_dev, fps_reg, fps_val); + + /* Start video stream */ + write_register(gspca_dev, 0x05, mode_val); + + /* disable Hflip */ + write_register(gspca_dev, 0x47, 0x00); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* reset video stream */ + write_register(gspca_dev, 0x05, 0x00); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + struct pkt_hdr *hdr = (void *)__data; + uint8_t *data = __data + sizeof(*hdr); + int datalen = len - sizeof(*hdr); + + uint8_t sof = sd->stream_flag | 1; + uint8_t mof = sd->stream_flag | 2; + uint8_t eof = sd->stream_flag | 5; + + if (len < 12) + return; + + if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') { + warn("[Stream %02x] Invalid magic %02x%02x", sd->stream_flag, + hdr->magic[0], hdr->magic[1]); + return; + } + + if (hdr->flag == sof) + gspca_frame_add(gspca_dev, FIRST_PACKET, data, datalen); + + else if (hdr->flag == mof) + gspca_frame_add(gspca_dev, INTER_PACKET, data, datalen); + + else if (hdr->flag == eof) + gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen); + + else + warn("Packet type not recognized..."); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + /* + .querymenu = sd_querymenu, + .get_streamparm = sd_get_streamparm, + .set_streamparm = sd_set_streamparm, + */ +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x045e, 0x02ae)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + return usb_register(&sd_driver); +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 41dce49fb43d..9d0b46027b93 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -1375,7 +1375,6 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - int data1, data2; const u16 (*init_data)[2]; static const u16 (*(init_data_tb[]))[2] = { spca508_vista_init_data, /* CreativeVista 0 */ @@ -1386,6 +1385,9 @@ static int sd_config(struct gspca_dev *gspca_dev, spca508_init_data, /* ViewQuestVQ110 5 */ }; +#ifdef GSPCA_DEBUG + int data1, data2; + /* Read from global register the USB product and vendor IDs, just to * prove that we can communicate with the device. This works, which * confirms at we are communicating properly and that the device @@ -1400,6 +1402,7 @@ static int sd_config(struct gspca_dev *gspca_dev, data1 = reg_read(gspca_dev, 0x8621); PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1); +#endif cam = &gspca_dev->cam; cam->cam_mode = sif_mode; diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index 87be52b5e1e3..763747700f10 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -436,17 +436,14 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { + static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"}; + switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; + if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm)) + break; + strcpy((char *) menu->name, freq_nm[menu->index]); + return 0; } return -EINVAL; } diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c index ac47b4c94388..75a5b9c2f15f 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c @@ -217,6 +217,8 @@ static int pb0100_start(struct sd *sd) intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) + return -ENODEV; packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); /* If we don't have enough bandwidth use a lower framerate */ diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 543542af2720..b089c0d3ee9f 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -396,57 +396,6 @@ static void reg_w_riv(struct gspca_dev *gspca_dev, req, index, value); } -/* read 1 byte */ -static u8 reg_r_1(struct gspca_dev *gspca_dev, - u16 value) /* wValue */ -{ - int ret; - - if (gspca_dev->usb_err < 0) - return 0; - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0x20, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - 0, /* index */ - gspca_dev->usb_buf, 1, - 500); /* timeout */ - if (ret < 0) { - err("reg_r_1 err %d", ret); - gspca_dev->usb_err = ret; - return 0; - } - return gspca_dev->usb_buf[0]; -} - -/* read 1 or 2 bytes */ -static u16 reg_r_12(struct gspca_dev *gspca_dev, - u8 req, /* bRequest */ - u16 index, /* wIndex */ - u16 length) /* wLength (1 or 2 only) */ -{ - int ret; - - if (gspca_dev->usb_err < 0) - return 0; - gspca_dev->usb_buf[1] = 0; - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, - gspca_dev->usb_buf, length, - 500); - if (ret < 0) { - err("reg_r_12 err %d", ret); - gspca_dev->usb_err = ret; - return 0; - } - return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; -} - static void write_vector(struct gspca_dev *gspca_dev, const struct cmd *data, int ncmds) { @@ -473,44 +422,46 @@ static void setup_qtable(struct gspca_dev *gspca_dev, static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, u8 req, u16 idx, u16 val) { - u16 notdone; - reg_w_riv(gspca_dev, req, idx, val); - notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1); + reg_r(gspca_dev, 0x01, 0x0001, 1); + PDEBUG(D_FRAM, "before wait 0x%04x", gspca_dev->usb_buf[0]); reg_w_riv(gspca_dev, req, idx, val); - PDEBUG(D_FRAM, "before wait 0x%04x", notdone); - msleep(200); - notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1); - PDEBUG(D_FRAM, "after wait 0x%04x", notdone); + reg_r(gspca_dev, 0x01, 0x0001, 1); + PDEBUG(D_FRAM, "after wait 0x%04x", gspca_dev->usb_buf[0]); } +#ifdef GSPCA_DEBUG static void spca504_read_info(struct gspca_dev *gspca_dev) { int i; u8 info[6]; - for (i = 0; i < 6; i++) - info[i] = reg_r_1(gspca_dev, i); + for (i = 0; i < 6; i++) { + reg_r(gspca_dev, 0, i, 1); + info[i] = gspca_dev->usb_buf[0]; + } PDEBUG(D_STREAM, "Read info: %d %d %d %d %d %d." " Should be 1,0,2,2,0,0", info[0], info[1], info[2], info[3], info[4], info[5]); } +#endif static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, u8 req, - u16 idx, u16 val, u16 endcode, u8 count) + u16 idx, u16 val, u8 endcode, u8 count) { u16 status; reg_w_riv(gspca_dev, req, idx, val); - status = reg_r_12(gspca_dev, 0x01, 0x0001, 1); + reg_r(gspca_dev, 0x01, 0x0001, 1); if (gspca_dev->usb_err < 0) return; - PDEBUG(D_FRAM, "Status 0x%04x Need 0x%04x", status, endcode); + PDEBUG(D_FRAM, "Status 0x%02x Need 0x%02x", + gspca_dev->usb_buf[0], endcode); if (!count) return; count = 200; @@ -518,7 +469,8 @@ static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, msleep(10); /* gsmart mini2 write a each wait setting 1 ms is enough */ /* reg_w_riv(gspca_dev, req, idx, val); */ - status = reg_r_12(gspca_dev, 0x01, 0x0001, 1); + reg_r(gspca_dev, 0x01, 0x0001, 1); + status = gspca_dev->usb_buf[0]; if (status == endcode) { PDEBUG(D_FRAM, "status 0x%04x after wait %d", status, 200 - count); @@ -555,17 +507,19 @@ static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) } } +#ifdef GSPCA_DEBUG static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) { u8 *data; data = gspca_dev->usb_buf; reg_r(gspca_dev, 0x20, 0, 5); - PDEBUG(D_STREAM, "FirmWare : %d %d %d %d %d ", + PDEBUG(D_STREAM, "FirmWare: %d %d %d %d %d", data[0], data[1], data[2], data[3], data[4]); reg_r(gspca_dev, 0x23, 0, 64); reg_r(gspca_dev, 0x23, 1, 64); } +#endif static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) { @@ -578,7 +532,9 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) reg_w_riv(gspca_dev, 0x31, 0, 0); spca504B_WaitCmdStatus(gspca_dev); spca504B_PollingDataReady(gspca_dev); +#ifdef GSPCA_DEBUG spca50x_GetFirmware(gspca_dev); +#endif reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */ reg_r(gspca_dev, 0x24, 8, 1); @@ -628,7 +584,8 @@ static void spca504_wait_status(struct gspca_dev *gspca_dev) cnt = 256; while (--cnt > 0) { /* With this we get the status, when return 0 it's all ok */ - if (reg_r_12(gspca_dev, 0x06, 0x00, 1) == 0) + reg_r(gspca_dev, 0x06, 0x00, 1); + if (gspca_dev->usb_buf[0] == 0) return; msleep(10); } @@ -772,10 +729,14 @@ static int sd_init(struct gspca_dev *gspca_dev) /* fall thru */ case BRIDGE_SPCA533: spca504B_PollingDataReady(gspca_dev); +#ifdef GSPCA_DEBUG spca50x_GetFirmware(gspca_dev); +#endif break; case BRIDGE_SPCA536: +#ifdef GSPCA_DEBUG spca50x_GetFirmware(gspca_dev); +#endif reg_r(gspca_dev, 0x00, 0x5002, 1); reg_w_1(gspca_dev, 0x24, 0, 0, 0); reg_r(gspca_dev, 0x24, 0, 1); @@ -801,7 +762,9 @@ static int sd_init(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA504: */ PDEBUG(D_STREAM, "Opening SPCA504"); if (sd->subtype == AiptekMiniPenCam13) { +#ifdef GSPCA_DEBUG spca504_read_info(gspca_dev); +#endif /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ spca504A_acknowledged_command(gspca_dev, 0x24, @@ -873,7 +836,9 @@ static int sd_start(struct gspca_dev *gspca_dev) break; case BRIDGE_SPCA504: if (sd->subtype == AiptekMiniPenCam13) { +#ifdef GSPCA_DEBUG spca504_read_info(gspca_dev); +#endif /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ spca504A_acknowledged_command(gspca_dev, 0x24, @@ -885,7 +850,9 @@ static int sd_start(struct gspca_dev *gspca_dev) 0, 0, 0x9d, 1); } else { spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); +#ifdef GSPCA_DEBUG spca504_read_info(gspca_dev); +#endif spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); } diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index a3eccd815766..7e762d551099 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -92,8 +92,6 @@ static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val); static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val); static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val); static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_querymenu(struct gspca_dev *gspca_dev, - struct v4l2_querymenu *menu); static const struct ctrl sd_ctrls[] = { { @@ -1379,17 +1377,14 @@ static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val) static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { + static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"}; + switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; + if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm)) + break; + strcpy((char *) menu->name, freq_nm[menu->index]); + return 0; case V4L2_CID_EFFECTS: if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) { strncpy((char *) menu->name, diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index fa164e861cde..61cdd56a74a9 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -3065,15 +3065,10 @@ static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */ {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */ {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */ {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ - {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ - {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ {} }; -static const struct usb_action mc501cb_50HZScale[] = { +static const struct usb_action mc501cb_50HZ[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ @@ -3082,15 +3077,10 @@ static const struct usb_action mc501cb_50HZScale[] = { {0xaa, 0x3c, 0x004c}, /* 00,3C,4C,aa */ {0xaa, 0x3d, 0x001d}, /* 00,3D,1D,aa */ {0xaa, 0x3e, 0x004c}, /* 00,3E,4C,aa */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */ - {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */ - {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */ {} }; -static const struct usb_action mc501cb_50HZ[] = { +static const struct usb_action mc501cb_50HZScale[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */ @@ -3099,15 +3089,10 @@ static const struct usb_action mc501cb_50HZ[] = { {0xaa, 0x3c, 0x0098}, /* 00,3C,98,aa */ {0xaa, 0x3d, 0x003a}, /* 00,3D,3A,aa */ {0xaa, 0x3e, 0x0098}, /* 00,3E,98,aa */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ - {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ - {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ {} }; -static const struct usb_action mc501cb_60HZScale[] = { +static const struct usb_action mc501cb_60HZ[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ @@ -3116,15 +3101,10 @@ static const struct usb_action mc501cb_60HZScale[] = { {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */ {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */ {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ - {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ - {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ {} }; -static const struct usb_action mc501cb_60HZ[] = { +static const struct usb_action mc501cb_60HZScale[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ @@ -3133,15 +3113,10 @@ static const struct usb_action mc501cb_60HZ[] = { {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */ {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ - {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ - {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ {} }; -static const struct usb_action mc501cb_NoFlikerScale[] = { +static const struct usb_action mc501cb_NoFliker[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ @@ -3150,15 +3125,10 @@ static const struct usb_action mc501cb_NoFlikerScale[] = { {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */ {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */ {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ - {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ - {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ {} }; -static const struct usb_action mc501cb_NoFliker[] = { +static const struct usb_action mc501cb_NoFlikerScale[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ @@ -6296,7 +6266,6 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - u8 retbyte; u16 retword; /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ @@ -6389,8 +6358,12 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */ PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword); if (retword == 0x2030) { +#ifdef GSPCA_DEBUG + u8 retbyte; + retbyte = i2c_read(gspca_dev, 0x02); /* revision number */ PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte); +#endif send_unknown(gspca_dev, SENSOR_PO2030); return retword; } diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 39946420b301..a4e4dfdbc2f2 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -810,7 +810,6 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, const struct pci_device_id *pci_id) { u16 cmd; - u8 card_rev; unsigned char pci_latency; IVTV_DEBUG_INFO("Enabling pci device\n"); @@ -857,7 +856,6 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, } IVTV_DEBUG_INFO("Bus Mastering Enabled.\n"); - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &card_rev); pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < 64 && ivtv_pci_latency) { @@ -874,7 +872,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, " "irq: %d, latency: %d, memory: 0x%lx\n", - pdev->device, card_rev, pdev->bus->number, + pdev->device, pdev->revision, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq, pci_latency, (unsigned long)itv->base_addr); diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index 53fa2a7bf156..ebebed929627 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -315,10 +315,20 @@ static int mt9m111_setup_rect(struct i2c_client *client, static int mt9m111_setup_pixfmt(struct i2c_client *client, u16 outfmt) { int ret; + u16 mask = MT9M111_OUTFMT_PROCESSED_BAYER | MT9M111_OUTFMT_RGB | + MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_SWAP_RGB_EVEN | + MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 | + MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr | + MT9M111_OUTFMT_SWAP_YCbCr_C_Y; - ret = reg_write(OUTPUT_FORMAT_CTRL2_A, outfmt); + ret = reg_read(OUTPUT_FORMAT_CTRL2_A); + if (ret >= 0) + ret = reg_write(OUTPUT_FORMAT_CTRL2_A, (ret & ~mask) | outfmt); if (!ret) - ret = reg_write(OUTPUT_FORMAT_CTRL2_B, outfmt); + ret = reg_read(OUTPUT_FORMAT_CTRL2_B); + if (ret >= 0) + ret = reg_write(OUTPUT_FORMAT_CTRL2_B, (ret & ~mask) | outfmt); + return ret; } diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index e313d8390092..fc76ed1c08e5 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -228,7 +228,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, flags = soc_camera_apply_sensor_flags(icl, flags); - if (flags & SOCAM_PCLK_SAMPLE_RISING) + if (flags & SOCAM_PCLK_SAMPLE_FALLING) pixclk |= 0x10; if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH)) diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c new file mode 100644 index 000000000000..1319c2c48aff --- /dev/null +++ b/drivers/media/video/mt9v032.c @@ -0,0 +1,773 @@ +/* + * Driver for MT9V032 CMOS Image Sensor from Micron + * + * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * Based on the MT9M001 driver, + * + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * 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. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/log2.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/v4l2-mediabus.h> + +#include <media/mt9v032.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#define MT9V032_PIXEL_ARRAY_HEIGHT 492 +#define MT9V032_PIXEL_ARRAY_WIDTH 782 + +#define MT9V032_CHIP_VERSION 0x00 +#define MT9V032_CHIP_ID_REV1 0x1311 +#define MT9V032_CHIP_ID_REV3 0x1313 +#define MT9V032_ROW_START 0x01 +#define MT9V032_ROW_START_MIN 4 +#define MT9V032_ROW_START_DEF 10 +#define MT9V032_ROW_START_MAX 482 +#define MT9V032_COLUMN_START 0x02 +#define MT9V032_COLUMN_START_MIN 1 +#define MT9V032_COLUMN_START_DEF 2 +#define MT9V032_COLUMN_START_MAX 752 +#define MT9V032_WINDOW_HEIGHT 0x03 +#define MT9V032_WINDOW_HEIGHT_MIN 1 +#define MT9V032_WINDOW_HEIGHT_DEF 480 +#define MT9V032_WINDOW_HEIGHT_MAX 480 +#define MT9V032_WINDOW_WIDTH 0x04 +#define MT9V032_WINDOW_WIDTH_MIN 1 +#define MT9V032_WINDOW_WIDTH_DEF 752 +#define MT9V032_WINDOW_WIDTH_MAX 752 +#define MT9V032_HORIZONTAL_BLANKING 0x05 +#define MT9V032_HORIZONTAL_BLANKING_MIN 43 +#define MT9V032_HORIZONTAL_BLANKING_MAX 1023 +#define MT9V032_VERTICAL_BLANKING 0x06 +#define MT9V032_VERTICAL_BLANKING_MIN 4 +#define MT9V032_VERTICAL_BLANKING_MAX 3000 +#define MT9V032_CHIP_CONTROL 0x07 +#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) +#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) +#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) +#define MT9V032_SHUTTER_WIDTH1 0x08 +#define MT9V032_SHUTTER_WIDTH2 0x09 +#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a +#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b +#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 +#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 +#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 +#define MT9V032_RESET 0x0c +#define MT9V032_READ_MODE 0x0d +#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) +#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 +#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) +#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 +#define MT9V032_READ_MODE_ROW_FLIP (1 << 4) +#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) +#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) +#define MT9V032_READ_MODE_DARK_ROWS (1 << 7) +#define MT9V032_PIXEL_OPERATION_MODE 0x0f +#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) +#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) +#define MT9V032_ANALOG_GAIN 0x35 +#define MT9V032_ANALOG_GAIN_MIN 16 +#define MT9V032_ANALOG_GAIN_DEF 16 +#define MT9V032_ANALOG_GAIN_MAX 64 +#define MT9V032_MAX_ANALOG_GAIN 0x36 +#define MT9V032_MAX_ANALOG_GAIN_MAX 127 +#define MT9V032_FRAME_DARK_AVERAGE 0x42 +#define MT9V032_DARK_AVG_THRESH 0x46 +#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) +#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 +#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) +#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 +#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 +#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) +#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) +#define MT9V032_PIXEL_CLOCK 0x74 +#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) +#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) +#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) +#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) +#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) +#define MT9V032_TEST_PATTERN 0x7f +#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) +#define MT9V032_TEST_PATTERN_DATA_SHIFT 0 +#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) +#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) +#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) +#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) +#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) +#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) +#define MT9V032_TEST_PATTERN_ENABLE (1 << 13) +#define MT9V032_TEST_PATTERN_FLIP (1 << 14) +#define MT9V032_AEC_AGC_ENABLE 0xaf +#define MT9V032_AEC_ENABLE (1 << 0) +#define MT9V032_AGC_ENABLE (1 << 1) +#define MT9V032_THERMAL_INFO 0xc1 + +struct mt9v032 { + struct v4l2_subdev subdev; + struct media_pad pad; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + + struct v4l2_ctrl_handler ctrls; + + struct mutex power_lock; + int power_count; + + struct mt9v032_platform_data *pdata; + u16 chip_control; + u16 aec_agc; +}; + +static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9v032, subdev); +} + +static int mt9v032_read(struct i2c_client *client, const u8 reg) +{ + s32 data = i2c_smbus_read_word_data(client, reg); + dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, + swab16(data), reg); + return data < 0 ? data : swab16(data); +} + +static int mt9v032_write(struct i2c_client *client, const u8 reg, + const u16 data) +{ + dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, + data, reg); + return i2c_smbus_write_word_data(client, reg, swab16(data)); +} + +static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + u16 value = (mt9v032->chip_control & ~clear) | set; + int ret; + + ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); + if (ret < 0) + return ret; + + mt9v032->chip_control = value; + return 0; +} + +static int +mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + u16 value = mt9v032->aec_agc; + int ret; + + if (enable) + value |= which; + else + value &= ~which; + + ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); + if (ret < 0) + return ret; + + mt9v032->aec_agc = value; + return 0; +} + +static int mt9v032_power_on(struct mt9v032 *mt9v032) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + int ret; + + if (mt9v032->pdata->set_clock) { + mt9v032->pdata->set_clock(&mt9v032->subdev, 25000000); + udelay(1); + } + + /* Reset the chip and stop data read out */ + ret = mt9v032_write(client, MT9V032_RESET, 1); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_RESET, 0); + if (ret < 0) + return ret; + + return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); +} + +static void mt9v032_power_off(struct mt9v032 *mt9v032) +{ + if (mt9v032->pdata->set_clock) + mt9v032->pdata->set_clock(&mt9v032->subdev, 0); +} + +static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + int ret; + + if (!on) { + mt9v032_power_off(mt9v032); + return 0; + } + + ret = mt9v032_power_on(mt9v032); + if (ret < 0) + return ret; + + /* Configure the pixel clock polarity */ + if (mt9v032->pdata && mt9v032->pdata->clk_pol) { + ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, + MT9V032_PIXEL_CLOCK_INV_PXL_CLK); + if (ret < 0) + return ret; + } + + /* Disable the noise correction algorithm and restore the controls. */ + ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); + if (ret < 0) + return ret; + + return v4l2_ctrl_handler_setup(&mt9v032->ctrls); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static struct v4l2_mbus_framefmt * +__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9v032->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9v032->crop; + default: + return NULL; + } +} + +static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) +{ + const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE + | MT9V032_CHIP_CONTROL_DOUT_ENABLE + | MT9V032_CHIP_CONTROL_SEQUENTIAL; + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + struct v4l2_mbus_framefmt *format = &mt9v032->format; + struct v4l2_rect *crop = &mt9v032->crop; + unsigned int hratio; + unsigned int vratio; + int ret; + + if (!enable) + return mt9v032_set_chip_control(mt9v032, mode, 0); + + /* Configure the window size and row/column bin */ + hratio = DIV_ROUND_CLOSEST(crop->width, format->width); + vratio = DIV_ROUND_CLOSEST(crop->height, format->height); + + ret = mt9v032_write(client, MT9V032_READ_MODE, + (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | + (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, + max(43, 660 - crop->width)); + if (ret < 0) + return ret; + + /* Switch to master "normal" mode */ + return mt9v032_set_chip_control(mt9v032, 0, mode); +} + +static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_SGRBG10_1X10; + return 0; +} + +static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; + fse->max_width = fse->min_width; + fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; + fse->max_height = fse->min_height; + + return 0; +} + +static int mt9v032_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, + format->which); + return 0; +} + +static int mt9v032_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, + format->which); + + /* Clamp the width and height to avoid dividing by zero. */ + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), + max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), + __crop->width); + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), + max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), + __crop->height); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int mt9v032_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, + crop->which); + return 0; +} + +static int mt9v032_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), + MT9V032_COLUMN_START_MIN, + MT9V032_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), + MT9V032_ROW_START_MIN, + MT9V032_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), + MT9V032_WINDOW_WIDTH_MIN, + MT9V032_WINDOW_WIDTH_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), + MT9V032_WINDOW_HEIGHT_MIN, + MT9V032_WINDOW_HEIGHT_MAX); + + rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) + +static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v032 *mt9v032 = + container_of(ctrl->handler, struct mt9v032, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + u16 data; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, + ctrl->val); + + case V4L2_CID_GAIN: + return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); + + case V4L2_CID_EXPOSURE_AUTO: + return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, + ctrl->val); + + case V4L2_CID_EXPOSURE: + return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, + ctrl->val); + + case V4L2_CID_TEST_PATTERN: + switch (ctrl->val) { + case 0: + data = 0; + break; + case 1: + data = MT9V032_TEST_PATTERN_GRAY_VERTICAL + | MT9V032_TEST_PATTERN_ENABLE; + break; + case 2: + data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL + | MT9V032_TEST_PATTERN_ENABLE; + break; + case 3: + data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL + | MT9V032_TEST_PATTERN_ENABLE; + break; + default: + data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT) + | MT9V032_TEST_PATTERN_USE_DATA + | MT9V032_TEST_PATTERN_ENABLE + | MT9V032_TEST_PATTERN_FLIP; + break; + } + + return mt9v032_write(client, MT9V032_TEST_PATTERN, data); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9v032_ctrl_ops = { + .s_ctrl = mt9v032_s_ctrl, +}; + +static const struct v4l2_ctrl_config mt9v032_ctrls[] = { + { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Test pattern", + .min = 0, + .max = 1023, + .step = 1, + .def = 0, + .flags = 0, + } +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + int ret = 0; + + mutex_lock(&mt9v032->power_lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (mt9v032->power_count == !on) { + ret = __mt9v032_set_power(mt9v032, !!on); + if (ret < 0) + goto done; + } + + /* Update the power count. */ + mt9v032->power_count += on ? 1 : -1; + WARN_ON(mt9v032->power_count < 0); + +done: + mutex_unlock(&mt9v032->power_lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int mt9v032_registered(struct v4l2_subdev *subdev) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + s32 data; + int ret; + + dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", + client->addr); + + ret = mt9v032_power_on(mt9v032); + if (ret < 0) { + dev_err(&client->dev, "MT9V032 power up failed\n"); + return ret; + } + + /* Read and check the sensor version */ + data = mt9v032_read(client, MT9V032_CHIP_VERSION); + if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { + dev_err(&client->dev, "MT9V032 not detected, wrong version " + "0x%04x\n", data); + return -ENODEV; + } + + mt9v032_power_off(mt9v032); + + dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", + client->addr); + + return ret; +} + +static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = MT9V032_COLUMN_START_DEF; + crop->top = MT9V032_ROW_START_DEF; + crop->width = MT9V032_WINDOW_WIDTH_DEF; + crop->height = MT9V032_WINDOW_HEIGHT_DEF; + + format = v4l2_subdev_get_try_format(fh, 0); + format->code = V4L2_MBUS_FMT_SGRBG10_1X10; + format->width = MT9V032_WINDOW_WIDTH_DEF; + format->height = MT9V032_WINDOW_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return mt9v032_set_power(subdev, 1); +} + +static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return mt9v032_set_power(subdev, 0); +} + +static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { + .s_power = mt9v032_set_power, +}; + +static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { + .s_stream = mt9v032_s_stream, +}; + +static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { + .enum_mbus_code = mt9v032_enum_mbus_code, + .enum_frame_size = mt9v032_enum_frame_size, + .get_fmt = mt9v032_get_format, + .set_fmt = mt9v032_set_format, + .get_crop = mt9v032_get_crop, + .set_crop = mt9v032_set_crop, +}; + +static struct v4l2_subdev_ops mt9v032_subdev_ops = { + .core = &mt9v032_subdev_core_ops, + .video = &mt9v032_subdev_video_ops, + .pad = &mt9v032_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { + .registered = mt9v032_registered, + .open = mt9v032_open, + .close = mt9v032_close, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9v032_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9v032 *mt9v032; + unsigned int i; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL); + if (!mt9v032) + return -ENOMEM; + + mutex_init(&mt9v032->power_lock); + mt9v032->pdata = client->dev.platform_data; + + v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 4); + + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, + MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); + v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, + MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, + MT9V032_TOTAL_SHUTTER_WIDTH_DEF); + + for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i) + v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL); + + mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; + + if (mt9v032->ctrls.error) + printk(KERN_INFO "%s: control initialization error %d\n", + __func__, mt9v032->ctrls.error); + + mt9v032->crop.left = MT9V032_COLUMN_START_DEF; + mt9v032->crop.top = MT9V032_ROW_START_DEF; + mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; + mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; + + mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; + mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; + mt9v032->format.field = V4L2_FIELD_NONE; + mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; + + mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; + + v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); + mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; + mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); + if (ret < 0) + kfree(mt9v032); + + return ret; +} + +static int mt9v032_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + kfree(mt9v032); + return 0; +} + +static const struct i2c_device_id mt9v032_id[] = { + { "mt9v032", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9v032_id); + +static struct i2c_driver mt9v032_driver = { + .driver = { + .name = "mt9v032", + }, + .probe = mt9v032_probe, + .remove = mt9v032_remove, + .id_table = mt9v032_id, +}; + +static int __init mt9v032_init(void) +{ + return i2c_add_driver(&mt9v032_driver); +} + +static void __exit mt9v032_exit(void) +{ + i2c_del_driver(&mt9v032_driver); +} + +module_init(mt9v032_init); +module_exit(mt9v032_exit); + +MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 502e2a40964c..c7680eb83664 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -400,6 +400,35 @@ static int mx3_videobuf_init(struct vb2_buffer *vb) return 0; } +static int mx3_stop_streaming(struct vb2_queue *q) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(q); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; + struct dma_chan *chan; + struct mx3_camera_buffer *buf, *tmp; + unsigned long flags; + + if (ichan) { + chan = &ichan->dma_chan; + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + } + + spin_lock_irqsave(&mx3_cam->lock, flags); + + mx3_cam->active = NULL; + + list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) { + buf->state = CSI_BUF_NEEDS_INIT; + list_del_init(&buf->queue); + } + + spin_unlock_irqrestore(&mx3_cam->lock, flags); + + return 0; +} + static struct vb2_ops mx3_videobuf_ops = { .queue_setup = mx3_videobuf_setup, .buf_prepare = mx3_videobuf_prepare, @@ -408,6 +437,7 @@ static struct vb2_ops mx3_videobuf_ops = { .buf_init = mx3_videobuf_init, .wait_prepare = soc_camera_unlock, .wait_finish = soc_camera_lock, + .stop_streaming = mx3_stop_streaming, }; static int mx3_camera_init_videobuf(struct vb2_queue *q, @@ -658,8 +688,8 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id fmt = soc_mbus_get_fmtdesc(code); if (!fmt) { - dev_err(icd->dev.parent, - "Invalid format code #%u: %d\n", idx, code); + dev_warn(icd->dev.parent, + "Unsupported format code #%u: %d\n", idx, code); return 0; } @@ -712,13 +742,9 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id static void configure_geometry(struct mx3_camera_dev *mx3_cam, unsigned int width, unsigned int height, - enum v4l2_mbus_pixelcode code) + const struct soc_mbus_pixelfmt *fmt) { u32 ctrl, width_field, height_field; - const struct soc_mbus_pixelfmt *fmt; - - fmt = soc_mbus_get_fmtdesc(code); - BUG_ON(!fmt); if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) { /* @@ -726,8 +752,10 @@ static void configure_geometry(struct mx3_camera_dev *mx3_cam, * the width parameter count the number of samples to * capture to complete the whole image width. */ - width *= soc_mbus_samples_per_pixel(fmt); - BUG_ON(width < 0); + unsigned int num, den; + int ret = soc_mbus_samples_per_pixel(fmt, &num, &den); + BUG_ON(ret < 0); + width = width * num / den; } /* Setup frame size - this cannot be changed on-the-fly... */ @@ -774,8 +802,8 @@ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) */ static inline void stride_align(__u32 *width) { - if (((*width + 7) & ~7) < 4096) - *width = (*width + 7) & ~7; + if (ALIGN(*width, 8) < 4096) + *width = ALIGN(*width, 8); else *width = *width & ~7; } @@ -801,11 +829,14 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, if (ret < 0) return ret; - /* The capture device might have changed its output */ + /* The capture device might have changed its output sizes */ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); if (ret < 0) return ret; + if (mf.code != icd->current_fmt->code) + return -EINVAL; + if (mf.width & 7) { /* Ouch! We can only handle 8-byte aligned width... */ stride_align(&mf.width); @@ -815,7 +846,8 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, } if (mf.width != icd->user_width || mf.height != icd->user_height) - configure_geometry(mx3_cam, mf.width, mf.height, mf.code); + configure_geometry(mx3_cam, mf.width, mf.height, + icd->current_fmt->host_fmt); dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n", mf.width, mf.height); @@ -853,7 +885,7 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, * mxc_v4l2_s_fmt() */ - configure_geometry(mx3_cam, pix->width, pix->height, xlate->code); + configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt); mf.width = pix->width; mf.height = pix->height; diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c index 5954b9306630..e7cfc85b0a1c 100644 --- a/drivers/media/video/omap1_camera.c +++ b/drivers/media/video/omap1_camera.c @@ -990,63 +990,80 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd) } /* Duplicate standard formats based on host capability of byte swapping */ -static const struct soc_mbus_pixelfmt omap1_cam_formats[] = { - [V4L2_MBUS_FMT_UYVY8_2X8] = { +static const struct soc_mbus_lookup omap1_cam_formats[] = { +{ + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .fmt = { .fourcc = V4L2_PIX_FMT_YUYV, .name = "YUYV", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, }, - [V4L2_MBUS_FMT_VYUY8_2X8] = { +}, { + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .fmt = { .fourcc = V4L2_PIX_FMT_YVYU, .name = "YVYU", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, }, - [V4L2_MBUS_FMT_YUYV8_2X8] = { +}, { + .code = V4L2_MBUS_FMT_YUYV8_2X8, + .fmt = { .fourcc = V4L2_PIX_FMT_UYVY, .name = "UYVY", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, }, - [V4L2_MBUS_FMT_YVYU8_2X8] = { +}, { + .code = V4L2_MBUS_FMT_YVYU8_2X8, + .fmt = { .fourcc = V4L2_PIX_FMT_VYUY, .name = "VYUY", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, }, - [V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE] = { +}, { + .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, + .fmt = { .fourcc = V4L2_PIX_FMT_RGB555, .name = "RGB555", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, }, - [V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE] = { +}, { + .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, + .fmt = { .fourcc = V4L2_PIX_FMT_RGB555X, .name = "RGB555X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, }, - [V4L2_MBUS_FMT_RGB565_2X8_BE] = { +}, { + .code = V4L2_MBUS_FMT_RGB565_2X8_BE, + .fmt = { .fourcc = V4L2_PIX_FMT_RGB565, .name = "RGB565", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, }, - [V4L2_MBUS_FMT_RGB565_2X8_LE] = { +}, { + .code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .fmt = { .fourcc = V4L2_PIX_FMT_RGB565X, .name = "RGB565X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, }, +}, }; static int omap1_cam_get_formats(struct soc_camera_device *icd, @@ -1065,7 +1082,7 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd, fmt = soc_mbus_get_fmtdesc(code); if (!fmt) { - dev_err(dev, "%s: invalid format code #%d: %d\n", __func__, + dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__, idx, code); return 0; } @@ -1085,12 +1102,14 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd, case V4L2_MBUS_FMT_RGB565_2X8_LE: formats++; if (xlate) { - xlate->host_fmt = &omap1_cam_formats[code]; + xlate->host_fmt = soc_mbus_find_fmtdesc(code, + omap1_cam_formats, + ARRAY_SIZE(omap1_cam_formats)); xlate->code = code; xlate++; dev_dbg(dev, "%s: providing format %s as byte swapped code #%d\n", - __func__, omap1_cam_formats[code].name, code); + __func__, xlate->host_fmt->name, code); } default: if (xlate) diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c index ca9f83a85ca5..453627b07833 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -278,12 +278,10 @@ static struct v4l2_standard generic_standards[] = { } }; -#define generic_standards_cnt ARRAY_SIZE(generic_standards) - static struct v4l2_standard *match_std(v4l2_std_id id) { unsigned int idx; - for (idx = 0; idx < generic_standards_cnt; idx++) { + for (idx = 0; idx < ARRAY_SIZE(generic_standards); idx++) { if (generic_standards[idx].id & id) { return generic_standards + idx; } @@ -370,7 +368,11 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt, GFP_KERNEL); - for (idx = 0; idx < std_cnt; idx++) stddefs[idx].index = idx; + if (!stddefs) + return NULL; + + for (idx = 0; idx < std_cnt; idx++) + stddefs[idx].index = idx; idx = 0; diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 780af5f81642..356cd42b593b 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -1850,7 +1850,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) } else { /* Device is closed, so we can safely unregister it */ PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n"); - pwc_cleanup(pdev); disconnect_out: /* search device_hint[] table if we occupy a slot, by any chance */ @@ -1860,6 +1859,7 @@ disconnect_out: } mutex_unlock(&pdev->modlock); + pwc_cleanup(pdev); } diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index aa87e462a958..f85c51249c7b 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -379,8 +379,27 @@ static int pwc_s_input(struct file *file, void *fh, unsigned int i) static int pwc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) { - int i; - + int i, idx; + u32 id; + + id = c->id; + if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { + id &= V4L2_CTRL_ID_MASK; + id++; + idx = -1; + for (i = 0; i < ARRAY_SIZE(pwc_controls); i++) { + if (pwc_controls[i].id < id) + continue; + if (idx >= 0 + && pwc_controls[i].id > pwc_controls[idx].id) + continue; + idx = i; + } + if (idx < 0) + return -EINVAL; + memcpy(c, &pwc_controls[idx], sizeof pwc_controls[0]); + return 0; + } for (i = 0; i < sizeof(pwc_controls) / sizeof(struct v4l2_queryctrl); i++) { if (pwc_controls[i].id == c->id) { PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n"); diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index c1ee09a043ba..b42bfa5ccdf2 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -1155,15 +1155,11 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; unsigned long bus_flags, camera_flags, common_flags; - const struct soc_mbus_pixelfmt *fmt; int ret; struct pxa_cam *cam = icd->host_priv; - fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code); - if (!fmt) - return -EINVAL; - - ret = test_platform_param(pcdev, fmt->bits_per_sample, &bus_flags); + ret = test_platform_param(pcdev, icd->current_fmt->host_fmt->bits_per_sample, + &bus_flags); if (ret < 0) return ret; diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 561909b65ce6..5b9dce85645c 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -394,12 +394,17 @@ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ /* start video number */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ +/* Enable jpeg capture. */ +static int jpeg_enable = 1; + module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level(0-100) default 0"); module_param(vid_limit, int, 0644); MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)"); module_param(video_nr, int, 0644); MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); +module_param(jpeg_enable, int, 0644); +MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1"); /* USB device table */ #define USB_SENSORAY_VID 0x1943 @@ -413,6 +418,7 @@ MODULE_DEVICE_TABLE(usb, s2255_table); #define BUFFER_TIMEOUT msecs_to_jiffies(400) /* image formats. */ +/* JPEG formats must be defined last to support jpeg_enable parameter */ static const struct s2255_fmt formats[] = { { .name = "4:2:2, planar, YUV422P", @@ -429,13 +435,17 @@ static const struct s2255_fmt formats[] = { .fourcc = V4L2_PIX_FMT_UYVY, .depth = 16 }, { + .name = "8bpp GREY", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8 + }, { .name = "JPG", .fourcc = V4L2_PIX_FMT_JPEG, .depth = 24 }, { - .name = "8bpp GREY", - .fourcc = V4L2_PIX_FMT_GREY, - .depth = 8 + .name = "MJPG", + .fourcc = V4L2_PIX_FMT_MJPEG, + .depth = 24 } }; @@ -610,6 +620,9 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc) for (i = 0; i < ARRAY_SIZE(formats); i++) { if (-1 == formats[i].fourcc) continue; + if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) || + (formats[i].fourcc == V4L2_PIX_FMT_MJPEG))) + continue; if (formats[i].fourcc == fourcc) return formats + i; } @@ -653,6 +666,7 @@ static void s2255_fillbuff(struct s2255_channel *channel, memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height); break; case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_MJPEG: buf->vb.size = jpgsize; memcpy(vbuf, tmpbuf, buf->vb.size); break; @@ -856,7 +870,9 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, if (index >= ARRAY_SIZE(formats)) return -EINVAL; - + if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || + (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) + return -EINVAL; dprintk(4, "name %s\n", formats[index].name); strlcpy(f->description, formats[index].name, sizeof(f->description)); f->pixelformat = formats[index].fourcc; @@ -1037,6 +1053,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, mode.color |= COLOR_Y8; break; case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_MJPEG: mode.color &= ~MASK_COLOR; mode.color |= COLOR_JPG; mode.color |= (channel->jc.quality << 8); @@ -2382,7 +2399,7 @@ static void read_pipe_completion(struct urb *purb) read_pipe_completion, pipe_info); if (pipe_info->state != 0) { - if (usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL)) { + if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) { dev_err(&dev->udev->dev, "error submitting urb\n"); } } else { diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile index 7ea1b1403b1e..df6954ab1d99 100644 --- a/drivers/media/video/s5p-fimc/Makefile +++ b/drivers/media/video/s5p-fimc/Makefile @@ -1,3 +1,5 @@ +s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o +s5p-csis-objs := mipi-csis.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o -s5p-fimc-y := fimc-core.o fimc-reg.o fimc-capture.o +obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc.o diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c new file mode 100644 index 000000000000..ef056d6605ca --- /dev/null +++ b/drivers/media/video/s5p-fimc/mipi-csis.c @@ -0,0 +1,724 @@ +/* + * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> + * + * 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. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/memory.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/videodev2.h> +#include <media/v4l2-subdev.h> +#include <plat/mipi_csis.h> +#include "mipi-csis.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* Register map definition */ + +/* CSIS global control */ +#define S5PCSIS_CTRL 0x00 +#define S5PCSIS_CTRL_DPDN_DEFAULT (0 << 31) +#define S5PCSIS_CTRL_DPDN_SWAP (1 << 31) +#define S5PCSIS_CTRL_ALIGN_32BIT (1 << 20) +#define S5PCSIS_CTRL_UPDATE_SHADOW (1 << 16) +#define S5PCSIS_CTRL_WCLK_EXTCLK (1 << 8) +#define S5PCSIS_CTRL_RESET (1 << 4) +#define S5PCSIS_CTRL_ENABLE (1 << 0) + +/* D-PHY control */ +#define S5PCSIS_DPHYCTRL 0x04 +#define S5PCSIS_DPHYCTRL_HSS_MASK (0x1f << 27) +#define S5PCSIS_DPHYCTRL_ENABLE (0x1f << 0) + +#define S5PCSIS_CONFIG 0x08 +#define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2) +#define S5PCSIS_CFG_FMT_RAW8 (0x2a << 2) +#define S5PCSIS_CFG_FMT_RAW10 (0x2b << 2) +#define S5PCSIS_CFG_FMT_RAW12 (0x2c << 2) +/* User defined formats, x = 1...4 */ +#define S5PCSIS_CFG_FMT_USER(x) ((0x30 + x - 1) << 2) +#define S5PCSIS_CFG_FMT_MASK (0x3f << 2) +#define S5PCSIS_CFG_NR_LANE_MASK 3 + +/* Interrupt mask. */ +#define S5PCSIS_INTMSK 0x10 +#define S5PCSIS_INTMSK_EN_ALL 0xf000003f +#define S5PCSIS_INTSRC 0x14 + +/* Pixel resolution */ +#define S5PCSIS_RESOL 0x2c +#define CSIS_MAX_PIX_WIDTH 0xffff +#define CSIS_MAX_PIX_HEIGHT 0xffff + +enum { + CSIS_CLK_MUX, + CSIS_CLK_GATE, +}; + +static char *csi_clock_name[] = { + [CSIS_CLK_MUX] = "sclk_csis", + [CSIS_CLK_GATE] = "csis", +}; +#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) + +enum { + ST_POWERED = 1, + ST_STREAMING = 2, + ST_SUSPENDED = 4, +}; + +/** + * struct csis_state - the driver's internal state data structure + * @lock: mutex serializing the subdev and power management operations, + * protecting @format and @flags members + * @pads: CSIS pads array + * @sd: v4l2_subdev associated with CSIS device instance + * @pdev: CSIS platform device + * @regs_res: requested I/O register memory resource + * @regs: mmaped I/O registers memory + * @clock: CSIS clocks + * @irq: requested s5p-mipi-csis irq number + * @flags: the state variable for power and streaming control + * @csis_fmt: current CSIS pixel format + * @format: common media bus format for the source and sink pad + */ +struct csis_state { + struct mutex lock; + struct media_pad pads[CSIS_PADS_NUM]; + struct v4l2_subdev sd; + struct platform_device *pdev; + struct resource *regs_res; + void __iomem *regs; + struct clk *clock[NUM_CSIS_CLOCKS]; + int irq; + struct regulator *supply; + u32 flags; + const struct csis_pix_format *csis_fmt; + struct v4l2_mbus_framefmt format; +}; + +/** + * struct csis_pix_format - CSIS pixel format description + * @pix_width_alignment: horizontal pixel alignment, width will be + * multiple of 2^pix_width_alignment + * @code: corresponding media bus code + * @fmt_reg: S5PCSIS_CONFIG register value + */ +struct csis_pix_format { + unsigned int pix_width_alignment; + enum v4l2_mbus_pixelcode code; + u32 fmt_reg; +}; + +static const struct csis_pix_format s5pcsis_formats[] = { + { + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT, + }, { + .code = V4L2_MBUS_FMT_JPEG_1X8, + .fmt_reg = S5PCSIS_CFG_FMT_USER(1), + }, +}; + +#define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r) +#define s5pcsis_read(__csis, __r) readl(__csis->regs + __r) + +static struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev) +{ + return container_of(sdev, struct csis_state, sd); +} + +static const struct csis_pix_format *find_csis_format( + struct v4l2_mbus_framefmt *mf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++) + if (mf->code == s5pcsis_formats[i].code) + return &s5pcsis_formats[i]; + return NULL; +} + +static void s5pcsis_enable_interrupts(struct csis_state *state, bool on) +{ + u32 val = s5pcsis_read(state, S5PCSIS_INTMSK); + + val = on ? val | S5PCSIS_INTMSK_EN_ALL : + val & ~S5PCSIS_INTMSK_EN_ALL; + s5pcsis_write(state, S5PCSIS_INTMSK, val); +} + +static void s5pcsis_reset(struct csis_state *state) +{ + u32 val = s5pcsis_read(state, S5PCSIS_CTRL); + + s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET); + udelay(10); +} + +static void s5pcsis_system_enable(struct csis_state *state, int on) +{ + u32 val; + + val = s5pcsis_read(state, S5PCSIS_CTRL); + if (on) + val |= S5PCSIS_CTRL_ENABLE; + else + val &= ~S5PCSIS_CTRL_ENABLE; + s5pcsis_write(state, S5PCSIS_CTRL, val); + + val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); + if (on) + val |= S5PCSIS_DPHYCTRL_ENABLE; + else + val &= ~S5PCSIS_DPHYCTRL_ENABLE; + s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); +} + +/* Called with the state.lock mutex held */ +static void __s5pcsis_set_format(struct csis_state *state) +{ + struct v4l2_mbus_framefmt *mf = &state->format; + u32 val; + + v4l2_dbg(1, debug, &state->sd, "fmt: %d, %d x %d\n", + mf->code, mf->width, mf->height); + + /* Color format */ + val = s5pcsis_read(state, S5PCSIS_CONFIG); + val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg; + s5pcsis_write(state, S5PCSIS_CONFIG, val); + + /* Pixel resolution */ + val = (mf->width << 16) | mf->height; + s5pcsis_write(state, S5PCSIS_RESOL, val); +} + +static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle) +{ + u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); + + val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27); + s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); +} + +static void s5pcsis_set_params(struct csis_state *state) +{ + struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; + u32 val; + + val = s5pcsis_read(state, S5PCSIS_CONFIG); + val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1); + s5pcsis_write(state, S5PCSIS_CONFIG, val); + + __s5pcsis_set_format(state); + s5pcsis_set_hsync_settle(state, pdata->hs_settle); + + val = s5pcsis_read(state, S5PCSIS_CTRL); + if (pdata->alignment == 32) + val |= S5PCSIS_CTRL_ALIGN_32BIT; + else /* 24-bits */ + val &= ~S5PCSIS_CTRL_ALIGN_32BIT; + /* Not using external clock. */ + val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; + s5pcsis_write(state, S5PCSIS_CTRL, val); + + /* Update the shadow register. */ + val = s5pcsis_read(state, S5PCSIS_CTRL); + s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW); +} + +static void s5pcsis_clk_put(struct csis_state *state) +{ + int i; + + for (i = 0; i < NUM_CSIS_CLOCKS; i++) + if (!IS_ERR_OR_NULL(state->clock[i])) + clk_put(state->clock[i]); +} + +static int s5pcsis_clk_get(struct csis_state *state) +{ + struct device *dev = &state->pdev->dev; + int i; + + for (i = 0; i < NUM_CSIS_CLOCKS; i++) { + state->clock[i] = clk_get(dev, csi_clock_name[i]); + if (IS_ERR(state->clock[i])) { + s5pcsis_clk_put(state); + dev_err(dev, "failed to get clock: %s\n", + csi_clock_name[i]); + return -ENXIO; + } + } + return 0; +} + +static int s5pcsis_s_power(struct v4l2_subdev *sd, int on) +{ + struct csis_state *state = sd_to_csis_state(sd); + struct device *dev = &state->pdev->dev; + + if (on) + return pm_runtime_get_sync(dev); + + return pm_runtime_put_sync(dev); +} + +static void s5pcsis_start_stream(struct csis_state *state) +{ + s5pcsis_reset(state); + s5pcsis_set_params(state); + s5pcsis_system_enable(state, true); + s5pcsis_enable_interrupts(state, true); +} + +static void s5pcsis_stop_stream(struct csis_state *state) +{ + s5pcsis_enable_interrupts(state, false); + s5pcsis_system_enable(state, false); +} + +/* v4l2_subdev operations */ +static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct csis_state *state = sd_to_csis_state(sd); + int ret = 0; + + v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n", + __func__, enable, state->flags); + + if (enable) { + ret = pm_runtime_get_sync(&state->pdev->dev); + if (ret && ret != 1) + return ret; + } + mutex_lock(&state->lock); + if (enable) { + if (state->flags & ST_SUSPENDED) { + ret = -EBUSY; + goto unlock; + } + s5pcsis_start_stream(state); + state->flags |= ST_STREAMING; + } else { + s5pcsis_stop_stream(state); + state->flags &= ~ST_STREAMING; + } +unlock: + mutex_unlock(&state->lock); + if (!enable) + pm_runtime_put(&state->pdev->dev); + + return ret == 1 ? 0 : ret; +} + +static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(s5pcsis_formats)) + return -EINVAL; + + code->code = s5pcsis_formats[code->index].code; + return 0; +} + +static struct csis_pix_format const *s5pcsis_try_format( + struct v4l2_mbus_framefmt *mf) +{ + struct csis_pix_format const *csis_fmt; + + csis_fmt = find_csis_format(mf); + if (csis_fmt == NULL) + csis_fmt = &s5pcsis_formats[0]; + + mf->code = csis_fmt->code; + v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH, + csis_fmt->pix_width_alignment, + &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1, + 0); + return csis_fmt; +} + +static struct v4l2_mbus_framefmt *__s5pcsis_get_format( + struct csis_state *state, struct v4l2_subdev_fh *fh, + u32 pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; + + return &state->format; +} + +static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct csis_state *state = sd_to_csis_state(sd); + struct csis_pix_format const *csis_fmt; + struct v4l2_mbus_framefmt *mf; + + if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK) + return -EINVAL; + + mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which); + + if (fmt->pad == CSIS_PAD_SOURCE) { + if (mf) { + mutex_lock(&state->lock); + fmt->format = *mf; + mutex_unlock(&state->lock); + } + return 0; + } + csis_fmt = s5pcsis_try_format(&fmt->format); + if (mf) { + mutex_lock(&state->lock); + *mf = fmt->format; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + state->csis_fmt = csis_fmt; + mutex_unlock(&state->lock); + } + return 0; +} + +static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct csis_state *state = sd_to_csis_state(sd); + struct v4l2_mbus_framefmt *mf; + + if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK) + return -EINVAL; + + mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which); + if (!mf) + return -EINVAL; + + mutex_lock(&state->lock); + fmt->format = *mf; + mutex_unlock(&state->lock); + return 0; +} + +static struct v4l2_subdev_core_ops s5pcsis_core_ops = { + .s_power = s5pcsis_s_power, +}; + +static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = { + .enum_mbus_code = s5pcsis_enum_mbus_code, + .get_fmt = s5pcsis_get_fmt, + .set_fmt = s5pcsis_set_fmt, +}; + +static struct v4l2_subdev_video_ops s5pcsis_video_ops = { + .s_stream = s5pcsis_s_stream, +}; + +static struct v4l2_subdev_ops s5pcsis_subdev_ops = { + .core = &s5pcsis_core_ops, + .pad = &s5pcsis_pad_ops, + .video = &s5pcsis_video_ops, +}; + +static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) +{ + struct csis_state *state = dev_id; + u32 val; + + /* Just clear the interrupt pending bits. */ + val = s5pcsis_read(state, S5PCSIS_INTSRC); + s5pcsis_write(state, S5PCSIS_INTSRC, val); + + return IRQ_HANDLED; +} + +static int __devinit s5pcsis_probe(struct platform_device *pdev) +{ + struct s5p_platform_mipi_csis *pdata; + struct resource *mem_res; + struct resource *regs_res; + struct csis_state *state; + int ret = -ENOMEM; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + mutex_init(&state->lock); + state->pdev = pdev; + + pdata = pdev->dev.platform_data; + if (pdata == NULL || pdata->phy_enable == NULL) { + dev_err(&pdev->dev, "Platform data not fully specified\n"); + goto e_free; + } + + if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) || + pdata->lanes > CSIS0_MAX_LANES) { + ret = -EINVAL; + dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", + pdata->lanes); + goto e_free; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + dev_err(&pdev->dev, "Failed to get IO memory region\n"); + goto e_free; + } + + regs_res = request_mem_region(mem_res->start, resource_size(mem_res), + pdev->name); + if (!regs_res) { + dev_err(&pdev->dev, "Failed to request IO memory region\n"); + goto e_free; + } + state->regs_res = regs_res; + + state->regs = ioremap(mem_res->start, resource_size(mem_res)); + if (!state->regs) { + dev_err(&pdev->dev, "Failed to remap IO region\n"); + goto e_reqmem; + } + + ret = s5pcsis_clk_get(state); + if (ret) + goto e_unmap; + + clk_enable(state->clock[CSIS_CLK_MUX]); + if (pdata->clk_rate) + clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate); + else + dev_WARN(&pdev->dev, "No clock frequency specified!\n"); + + state->irq = platform_get_irq(pdev, 0); + if (state->irq < 0) { + ret = state->irq; + dev_err(&pdev->dev, "Failed to get irq\n"); + goto e_clkput; + } + + if (!pdata->fixed_phy_vdd) { + state->supply = regulator_get(&pdev->dev, "vdd"); + if (IS_ERR(state->supply)) { + ret = PTR_ERR(state->supply); + state->supply = NULL; + goto e_clkput; + } + } + + ret = request_irq(state->irq, s5pcsis_irq_handler, 0, + dev_name(&pdev->dev), state); + if (ret) { + dev_err(&pdev->dev, "request_irq failed\n"); + goto e_regput; + } + + v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); + state->sd.owner = THIS_MODULE; + strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name)); + state->csis_fmt = &s5pcsis_formats[0]; + + state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&state->sd.entity, + CSIS_PADS_NUM, state->pads, 0); + if (ret < 0) + goto e_irqfree; + + /* This allows to retrieve the platform device id by the host driver */ + v4l2_set_subdevdata(&state->sd, pdev); + + /* .. and a pointer to the subdev. */ + platform_set_drvdata(pdev, &state->sd); + + state->flags = ST_SUSPENDED; + pm_runtime_enable(&pdev->dev); + + return 0; + +e_irqfree: + free_irq(state->irq, state); +e_regput: + if (state->supply) + regulator_put(state->supply); +e_clkput: + clk_disable(state->clock[CSIS_CLK_MUX]); + s5pcsis_clk_put(state); +e_unmap: + iounmap(state->regs); +e_reqmem: + release_mem_region(regs_res->start, resource_size(regs_res)); +e_free: + kfree(state); + return ret; +} + +static int s5pcsis_suspend(struct device *dev) +{ + struct s5p_platform_mipi_csis *pdata = dev->platform_data; + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct csis_state *state = sd_to_csis_state(sd); + int ret = 0; + + v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n", + __func__, state->flags); + + mutex_lock(&state->lock); + if (state->flags & ST_POWERED) { + s5pcsis_stop_stream(state); + ret = pdata->phy_enable(state->pdev, false); + if (ret) + goto unlock; + if (state->supply) { + ret = regulator_disable(state->supply); + if (ret) + goto unlock; + } + clk_disable(state->clock[CSIS_CLK_GATE]); + state->flags &= ~ST_POWERED; + } + state->flags |= ST_SUSPENDED; + unlock: + mutex_unlock(&state->lock); + return ret ? -EAGAIN : 0; +} + +static int s5pcsis_resume(struct device *dev) +{ + struct s5p_platform_mipi_csis *pdata = dev->platform_data; + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct csis_state *state = sd_to_csis_state(sd); + int ret = 0; + + v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n", + __func__, state->flags); + + mutex_lock(&state->lock); + if (!(state->flags & ST_SUSPENDED)) + goto unlock; + + if (!(state->flags & ST_POWERED)) { + if (state->supply) + ret = regulator_enable(state->supply); + if (ret) + goto unlock; + + ret = pdata->phy_enable(state->pdev, true); + if (!ret) { + state->flags |= ST_POWERED; + } else if (state->supply) { + regulator_disable(state->supply); + goto unlock; + } + clk_enable(state->clock[CSIS_CLK_GATE]); + } + if (state->flags & ST_STREAMING) + s5pcsis_start_stream(state); + + state->flags &= ~ST_SUSPENDED; + unlock: + mutex_unlock(&state->lock); + return ret ? -EAGAIN : 0; +} + +#ifdef CONFIG_PM_SLEEP +static int s5pcsis_pm_suspend(struct device *dev) +{ + return s5pcsis_suspend(dev); +} + +static int s5pcsis_pm_resume(struct device *dev) +{ + int ret; + + ret = s5pcsis_resume(dev); + + if (!ret) { + pm_runtime_disable(dev); + ret = pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + return ret; +} +#endif + +static int __devexit s5pcsis_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct csis_state *state = sd_to_csis_state(sd); + struct resource *res = state->regs_res; + + pm_runtime_disable(&pdev->dev); + s5pcsis_suspend(&pdev->dev); + clk_disable(state->clock[CSIS_CLK_MUX]); + pm_runtime_set_suspended(&pdev->dev); + + s5pcsis_clk_put(state); + if (state->supply) + regulator_put(state->supply); + + media_entity_cleanup(&state->sd.entity); + free_irq(state->irq, state); + iounmap(state->regs); + release_mem_region(res->start, resource_size(res)); + kfree(state); + + return 0; +} + +static const struct dev_pm_ops s5pcsis_pm_ops = { + SET_RUNTIME_PM_OPS(s5pcsis_suspend, s5pcsis_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_pm_suspend, s5pcsis_pm_resume) +}; + +static struct platform_driver s5pcsis_driver = { + .probe = s5pcsis_probe, + .remove = __devexit_p(s5pcsis_remove), + .driver = { + .name = CSIS_DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &s5pcsis_pm_ops, + }, +}; + +static int __init s5pcsis_init(void) +{ + return platform_driver_probe(&s5pcsis_driver, s5pcsis_probe); +} + +static void __exit s5pcsis_exit(void) +{ + platform_driver_unregister(&s5pcsis_driver); +} + +module_init(s5pcsis_init); +module_exit(s5pcsis_exit); + +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); +MODULE_DESCRIPTION("S5P/EXYNOS4 MIPI CSI receiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/s5p-fimc/mipi-csis.h b/drivers/media/video/s5p-fimc/mipi-csis.h new file mode 100644 index 000000000000..f5691336dd5c --- /dev/null +++ b/drivers/media/video/s5p-fimc/mipi-csis.h @@ -0,0 +1,22 @@ +/* + * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * + * 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. + */ +#ifndef S5P_MIPI_CSIS_H_ +#define S5P_MIPI_CSIS_H_ + +#define CSIS_DRIVER_NAME "s5p-mipi-csis" +#define CSIS_MAX_ENTITIES 2 +#define CSIS0_MAX_LANES 4 +#define CSIS1_MAX_LANES 2 + +#define CSIS_PAD_SINK 0 +#define CSIS_PAD_SOURCE 1 +#define CSIS_PADS_NUM 2 + +#endif diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 50f1be05ebd3..e2062b240e32 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5591,6 +5591,105 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, }, }, + [SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2] = { + /* Timothy Lee <timothy.lee@siriushk.com> */ + .name = "MagicPro ProHDTV Pro2 DMB-TH/Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_config = 3, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x02050000, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00050000, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x00050000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x00050000, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x00050000, + }, + .mute = { + .name = name_mute, + .vmux = 0, + .amux = TV, + .gpio = 0x00050000, + }, + }, + [SAA7134_BOARD_BEHOLD_501] = { + /* Beholder Intl. Ltd. 2010 */ + /* Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 501", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_BEHOLD_503FM] = { + /* Beholder Intl. Ltd. 2010 */ + /* Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 503 FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, }; @@ -6796,6 +6895,24 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0xc900, .driver_data = SAA7134_BOARD_VIDEOMATE_M1F, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x5030, + .driver_data = SAA7134_BOARD_BEHOLD_503FM, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x5ace, + .subdevice = 0x5010, + .driver_data = SAA7134_BOARD_BEHOLD_501, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x17de, + .subdevice = 0xd136, + .driver_data = SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -6988,6 +7105,7 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, switch (dev->board) { case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); break; case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: @@ -7014,6 +7132,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, case SAA7134_BOARD_HAUPPAUGE_HVR1120: case SAA7134_BOARD_AVERMEDIA_M733A: case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: + case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: /* tda8290 + tda18271 */ ret = saa7134_tda8290_18271_callback(dev, command, arg); break; @@ -7264,6 +7383,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: + dev->has_remote = SAA7134_REMOTE_GPIO; /* GPIO 26 high for digital, low for analog */ saa7134_set_gpio(dev, 26, 0); msleep(1); @@ -7326,6 +7446,11 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa7134_set_gpio(dev, 1, 1); dev->has_remote = SAA7134_REMOTE_GPIO; break; + case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: + /* enable LGS-8G75 */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0e050000, 0x0c050000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0e050000, 0x0c050000); + break; } return 0; } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 41f836fc93ec..f9be737ba6f4 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -927,7 +927,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, } /* print pci info */ - pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + dev->pci_rev = pci_dev->revision; pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", dev->name, diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index f65cad287b83..996a206c6d79 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -53,6 +53,7 @@ #include "lgdt3305.h" #include "tda8290.h" #include "mb86a20s.h" +#include "lgs8gxx.h" #include "zl10353.h" @@ -1123,6 +1124,26 @@ static struct tda18271_config dtv1000s_tda18271_config = { .gate = TDA18271_GATE_ANALOG, }; +static struct lgs8gxx_config prohdtv_pro2_lgs8g75_config = { + .prod = LGS8GXX_PROD_LGS8G75, + .demod_address = 0x1d, + .serial_ts = 0, + .ts_clk_pol = 1, + .ts_clk_gated = 0, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 4000, /* 4.00 MHz */ + .if_neg_center = 0, + .ext_adc = 0, + .adc_signed = 1, + .adc_vpp = 3, /* 2.0 Vpp */ + .if_neg_edge = 1, +}; + +static struct tda18271_config prohdtv_pro2_tda18271_config = { + .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, +}; + /* ================================================================== * Core code */ @@ -1674,6 +1695,19 @@ static int dvb_init(struct saa7134_dev *dev) /* mb86a20s need to use the I2C gateway */ break; + case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: + fe0->dvb.frontend = dvb_attach(lgs8gxx_attach, + &prohdtv_pro2_lgs8g75_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &prohdtv_pro2_tda18271_config); + } + break; default: wprintk("Huh? unknown DVB card?\n"); break; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index be1c2a2de27c..ff6c0e97563e 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -756,6 +756,14 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x0ff00; mask_keyup = 0x040000; break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + ir_codes = RC_MAP_HAUPPAUGE; + mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index f96cd5d761f9..28eb10398323 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -328,6 +328,9 @@ struct saa7134_card_ir { #define SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG 182 #define SAA7134_BOARD_VIDEOMATE_M1F 183 #define SAA7134_BOARD_ENCORE_ENLTV_FM3 184 +#define SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2 185 +#define SAA7134_BOARD_BEHOLD_501 186 +#define SAA7134_BOARD_BEHOLD_503FM 187 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c index b813aec1e456..3b7d7b4e3034 100644 --- a/drivers/media/video/saa7164/saa7164-core.c +++ b/drivers/media/video/saa7164/saa7164-core.c @@ -1247,7 +1247,7 @@ static int __devinit saa7164_initdev(struct pci_dev *pci_dev, } /* print pci info */ - pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + dev->pci_rev = pci_dev->revision; pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", dev->name, diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 134e86bf6d97..3ae5c9c58cba 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/io.h> +#include <linux/completion.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/errno.h> @@ -106,6 +107,7 @@ struct sh_mobile_ceu_dev { struct vb2_alloc_ctx *alloc_ctx; struct sh_mobile_ceu_info *pdata; + struct completion complete; u32 cflcr; @@ -114,6 +116,7 @@ struct sh_mobile_ceu_dev { unsigned int image_mode:1; unsigned int is_16bit:1; + unsigned int frozen:1; }; struct sh_mobile_ceu_cam { @@ -273,7 +276,8 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); status = ceu_read(pcdev, CETCR); ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); + if (!pcdev->frozen) + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); @@ -287,6 +291,11 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) ret = -EIO; } + if (pcdev->frozen) { + complete(&pcdev->complete); + return ret; + } + if (!pcdev->active) return ret; @@ -378,12 +387,11 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); - unsigned long flags; dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock_irq(&pcdev->lock); list_add_tail(&buf->queue, &pcdev->capture); if (!pcdev->active) { @@ -395,7 +403,7 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) pcdev->active = vb; sh_mobile_ceu_capture(pcdev); } - spin_unlock_irqrestore(&pcdev->lock, flags); + spin_unlock_irq(&pcdev->lock); } static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) @@ -404,9 +412,8 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); struct sh_mobile_ceu_dev *pcdev = ici->priv; - unsigned long flags; - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock_irq(&pcdev->lock); if (pcdev->active == vb) { /* disable capture (release DMA buffer), reset */ @@ -417,7 +424,7 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) /* Doesn't hurt also if the list is empty */ list_del_init(&buf->queue); - spin_unlock_irqrestore(&pcdev->lock, flags); + spin_unlock_irq(&pcdev->lock); } static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) @@ -427,6 +434,25 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) return 0; } +static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) +{ + struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + struct list_head *buf_head, *tmp; + + spin_lock_irq(&pcdev->lock); + + pcdev->active = NULL; + + list_for_each_safe(buf_head, tmp, &pcdev->capture) + list_del_init(buf_head); + + spin_unlock_irq(&pcdev->lock); + + return sh_mobile_ceu_soft_reset(pcdev); +} + static struct vb2_ops sh_mobile_ceu_videobuf_ops = { .queue_setup = sh_mobile_ceu_videobuf_setup, .buf_prepare = sh_mobile_ceu_videobuf_prepare, @@ -435,6 +461,7 @@ static struct vb2_ops sh_mobile_ceu_videobuf_ops = { .buf_init = sh_mobile_ceu_videobuf_init, .wait_prepare = soc_camera_unlock, .wait_finish = soc_camera_lock, + .stop_streaming = sh_mobile_ceu_stop_streaming, }; static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) @@ -500,7 +527,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - unsigned long flags; BUG_ON(icd != pcdev->icd); @@ -509,13 +535,13 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) sh_mobile_ceu_soft_reset(pcdev); /* make sure active buffer is canceled */ - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock_irq(&pcdev->lock); if (pcdev->active) { list_del_init(&to_ceu_vb(pcdev->active)->queue); vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); pcdev->active = NULL; } - spin_unlock_irqrestore(&pcdev->lock, flags); + spin_unlock_irq(&pcdev->lock); pm_runtime_put_sync(ici->v4l2_dev.dev); @@ -891,8 +917,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int fmt = soc_mbus_get_fmtdesc(code); if (!fmt) { - dev_err(dev, "Invalid format code #%u: %d\n", idx, code); - return -EINVAL; + dev_warn(dev, "unsupported format code #%u: %d\n", idx, code); + return 0; } if (!pcdev->pdata->csi2_dev) { @@ -1330,7 +1356,7 @@ static int client_scale(struct soc_camera_device *icd, /* * CEU can scale and crop, but we don't want to waste bandwidth and kill the * framerate by always requesting the maximum image from the client. See - * Documentation/video4linux/sh_mobile_camera_ceu.txt for a description of + * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of * scaling and cropping algorithms and for the meaning of referenced here steps. */ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, @@ -1377,10 +1403,6 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, if (mf.width > 2560 || mf.height > 1920) return -EINVAL; - /* Cache camera output window */ - cam->width = mf.width; - cam->height = mf.height; - /* 4. Calculate camera scales */ scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); @@ -1389,6 +1411,39 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, interm_width = scale_down(rect->width, scale_cam_h); interm_height = scale_down(rect->height, scale_cam_v); + if (interm_width < icd->user_width) { + u32 new_scale_h; + + new_scale_h = calc_generic_scale(rect->width, icd->user_width); + + mf.width = scale_down(cam_rect->width, new_scale_h); + } + + if (interm_height < icd->user_height) { + u32 new_scale_v; + + new_scale_v = calc_generic_scale(rect->height, icd->user_height); + + mf.height = scale_down(cam_rect->height, new_scale_v); + } + + if (interm_width < icd->user_width || interm_height < icd->user_height) { + ret = v4l2_device_call_until_err(sd->v4l2_dev, (int)icd, video, + s_mbus_fmt, &mf); + if (ret < 0) + return ret; + + dev_geo(dev, "New camera output %ux%u\n", mf.width, mf.height); + scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); + scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); + interm_width = scale_down(rect->width, scale_cam_h); + interm_height = scale_down(rect->height, scale_cam_v); + } + + /* Cache camera output window */ + cam->width = mf.width; + cam->height = mf.height; + if (pcdev->image_mode) { out_width = min(interm_width, icd->user_width); out_height = min(interm_height, icd->user_height); @@ -1704,6 +1759,63 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, return ret; } +static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, + struct v4l2_crop *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + u32 out_width = icd->user_width, out_height = icd->user_height; + int ret; + + /* Freeze queue */ + pcdev->frozen = 1; + /* Wait for frame */ + ret = wait_for_completion_interruptible(&pcdev->complete); + /* Stop the client */ + ret = v4l2_subdev_call(sd, video, s_stream, 0); + if (ret < 0) + dev_warn(icd->dev.parent, + "Client failed to stop the stream: %d\n", ret); + else + /* Do the crop, if it fails, there's nothing more we can do */ + sh_mobile_ceu_set_crop(icd, a); + + dev_geo(icd->dev.parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height); + + if (icd->user_width != out_width || icd->user_height != out_height) { + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = out_width, + .height = out_height, + .pixelformat = icd->current_fmt->host_fmt->fourcc, + .field = pcdev->field, + .colorspace = icd->colorspace, + }, + }; + ret = sh_mobile_ceu_set_fmt(icd, &f); + if (!ret && (out_width != f.fmt.pix.width || + out_height != f.fmt.pix.height)) + ret = -EINVAL; + if (!ret) { + icd->user_width = out_width; + icd->user_height = out_height; + ret = sh_mobile_ceu_set_bus_param(icd, + icd->current_fmt->host_fmt->fourcc); + } + } + + /* Thaw the queue */ + pcdev->frozen = 0; + spin_lock_irq(&pcdev->lock); + sh_mobile_ceu_capture(pcdev); + spin_unlock_irq(&pcdev->lock); + /* Start the client */ + ret = v4l2_subdev_call(sd, video, s_stream, 1); + return ret; +} + static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt) { struct soc_camera_device *icd = file->private_data; @@ -1790,6 +1902,7 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .put_formats = sh_mobile_ceu_put_formats, .get_crop = sh_mobile_ceu_get_crop, .set_crop = sh_mobile_ceu_set_crop, + .set_livecrop = sh_mobile_ceu_set_livecrop, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, .set_ctrl = sh_mobile_ceu_set_ctrl, @@ -1856,6 +1969,7 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcdev->capture); spin_lock_init(&pcdev->lock); + init_completion(&pcdev->complete); pcdev->pdata = pdev->dev.platform_data; if (!pcdev->pdata) { diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index ddb4c091dedc..398864370267 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -41,6 +41,11 @@ #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 +#define is_streaming(ici, icd) \ + (((ici)->ops->init_videobuf) ? \ + (icd)->vb_vidq.streaming : \ + vb2_is_streaming(&(icd)->vb2_vidq)) + static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ @@ -358,8 +363,6 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) if (!icd->user_formats) return -ENOMEM; - icd->num_user_formats = fmts; - dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts); /* Second pass - actually fill data formats */ @@ -367,9 +370,10 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) for (i = 0; i < raw_fmts; i++) if (!ici->ops->get_formats) { v4l2_subdev_call(sd, video, enum_mbus_fmt, i, &code); - icd->user_formats[i].host_fmt = + icd->user_formats[fmts].host_fmt = soc_mbus_get_fmtdesc(code); - icd->user_formats[i].code = code; + if (icd->user_formats[fmts].host_fmt) + icd->user_formats[fmts++].code = code; } else { ret = ici->ops->get_formats(icd, i, &icd->user_formats[fmts]); @@ -378,12 +382,12 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) fmts += ret; } + icd->num_user_formats = fmts; icd->current_fmt = &icd->user_formats[0]; return 0; egfmt: - icd->num_user_formats = 0; vfree(icd->user_formats); return ret; } @@ -662,7 +666,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, if (icd->streamer && icd->streamer != file) return -EBUSY; - if (icd->vb_vidq.bufs[0]) { + if (is_streaming(to_soc_camera_host(icd->dev.parent), icd)) { dev_err(&icd->dev, "S_FMT denied: queue initialised\n"); return -EBUSY; } @@ -903,14 +907,17 @@ static int soc_camera_s_crop(struct file *file, void *fh, if (ret < 0) { dev_err(&icd->dev, "S_CROP denied: getting current crop failed\n"); - } else if (icd->vb_vidq.bufs[0] && - (a->c.width != current_crop.c.width || - a->c.height != current_crop.c.height)) { + } else if ((a->c.width == current_crop.c.width && + a->c.height == current_crop.c.height) || + !is_streaming(ici, icd)) { + /* same size or not streaming - use .set_crop() */ + ret = ici->ops->set_crop(icd, a); + } else if (ici->ops->set_livecrop) { + ret = ici->ops->set_livecrop(icd, a); + } else { dev_err(&icd->dev, "S_CROP denied: queue initialised and sizes differ\n"); ret = -EBUSY; - } else { - ret = ici->ops->set_crop(icd, a); } return ret; diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index ed77aa055b63..bea7c9cf4f88 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -15,132 +15,329 @@ #include <media/v4l2-mediabus.h> #include <media/soc_mediabus.h> -#define MBUS_IDX(f) (V4L2_MBUS_FMT_ ## f - V4L2_MBUS_FMT_FIXED - 1) - -static const struct soc_mbus_pixelfmt mbus_fmt[] = { - [MBUS_IDX(YUYV8_2X8)] = { +static const struct soc_mbus_lookup mbus_fmt[] = { +{ + .code = V4L2_MBUS_FMT_YUYV8_2X8, + .fmt = { .fourcc = V4L2_PIX_FMT_YUYV, .name = "YUYV", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(YVYU8_2X8)] = { +}, { + .code = V4L2_MBUS_FMT_YVYU8_2X8, + .fmt = { .fourcc = V4L2_PIX_FMT_YVYU, .name = "YVYU", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(UYVY8_2X8)] = { +}, { + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .fmt = { .fourcc = V4L2_PIX_FMT_UYVY, .name = "UYVY", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(VYUY8_2X8)] = { +}, { + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .fmt = { .fourcc = V4L2_PIX_FMT_VYUY, .name = "VYUY", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(RGB555_2X8_PADHI_LE)] = { +}, { + .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, + .fmt = { .fourcc = V4L2_PIX_FMT_RGB555, .name = "RGB555", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(RGB555_2X8_PADHI_BE)] = { +}, { + .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, + .fmt = { .fourcc = V4L2_PIX_FMT_RGB555X, .name = "RGB555X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(RGB565_2X8_LE)] = { +}, { + .code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .fmt = { .fourcc = V4L2_PIX_FMT_RGB565, .name = "RGB565", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(RGB565_2X8_BE)] = { +}, { + .code = V4L2_MBUS_FMT_RGB565_2X8_BE, + .fmt = { .fourcc = V4L2_PIX_FMT_RGB565X, .name = "RGB565X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(SBGGR8_1X8)] = { +}, { + .code = V4L2_MBUS_FMT_SBGGR8_1X8, + .fmt = { .fourcc = V4L2_PIX_FMT_SBGGR8, .name = "Bayer 8 BGGR", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(SBGGR10_1X10)] = { +}, { + .code = V4L2_MBUS_FMT_SBGGR10_1X10, + .fmt = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(Y8_1X8)] = { +}, { + .code = V4L2_MBUS_FMT_Y8_1X8, + .fmt = { .fourcc = V4L2_PIX_FMT_GREY, .name = "Grey", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(Y10_1X10)] = { +}, { + .code = V4L2_MBUS_FMT_Y10_1X10, + .fmt = { .fourcc = V4L2_PIX_FMT_Y10, .name = "Grey 10bit", .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(SBGGR10_2X8_PADHI_LE)] = { +}, { + .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, + .fmt = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(SBGGR10_2X8_PADLO_LE)] = { +}, { + .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, + .fmt = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADLO, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(SBGGR10_2X8_PADHI_BE)] = { +}, { + .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, + .fmt = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, }, - [MBUS_IDX(SBGGR10_2X8_PADLO_BE)] = { +}, { + .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, + .fmt = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADLO, .order = SOC_MBUS_ORDER_BE, }, +}, { + .code = V4L2_MBUS_FMT_JPEG_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_JPEG, + .name = "JPEG", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_VARIABLE, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB444, + .name = "RGB444", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_BE, + }, +}, { + .code = V4L2_MBUS_FMT_YUYV8_1_5X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUV420, + .name = "YUYV 4:2:0", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_1_5X8, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_YVYU8_1_5X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVU420, + .name = "YVYU 4:2:0", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_1_5X8, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_UYVY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_VYUY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "VYUY 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_YUYV8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_YVYU8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YVYU 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_SGRBG8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .name = "Bayer 8 GRBG", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, + .name = "Bayer 10 BGGR DPCM 8", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_SGBRG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .name = "Bayer 10 GBRG", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_SGRBG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .name = "Bayer 10 GRBG", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_SRGGB10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .name = "Bayer 10 RGGB", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_SBGGR12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .name = "Bayer 12 BGGR", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_SGBRG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .name = "Bayer 12 GBRG", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_SGRBG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .name = "Bayer 12 GRBG", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_SRGGB12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .name = "Bayer 12 RGGB", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +}, }; -int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf) +int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, + unsigned int *numerator, unsigned int *denominator) { switch (mf->packing) { case SOC_MBUS_PACKING_NONE: case SOC_MBUS_PACKING_EXTEND16: - return 1; + *numerator = 1; + *denominator = 1; + return 0; case SOC_MBUS_PACKING_2X8_PADHI: case SOC_MBUS_PACKING_2X8_PADLO: - return 2; + *numerator = 2; + *denominator = 1; + return 0; + case SOC_MBUS_PACKING_1_5X8: + *numerator = 3; + *denominator = 2; + return 0; + case SOC_MBUS_PACKING_VARIABLE: + *numerator = 0; + *denominator = 1; + return 0; } return -EINVAL; } @@ -155,18 +352,34 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) case SOC_MBUS_PACKING_2X8_PADLO: case SOC_MBUS_PACKING_EXTEND16: return width * 2; + case SOC_MBUS_PACKING_1_5X8: + return width * 3 / 2; + case SOC_MBUS_PACKING_VARIABLE: + return 0; } return -EINVAL; } EXPORT_SYMBOL(soc_mbus_bytes_per_line); +const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( + enum v4l2_mbus_pixelcode code, + const struct soc_mbus_lookup *lookup, + int n) +{ + int i; + + for (i = 0; i < n; i++) + if (lookup[i].code == code) + return &lookup[i].fmt; + + return NULL; +} +EXPORT_SYMBOL(soc_mbus_find_fmtdesc); + const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( enum v4l2_mbus_pixelcode code) { - if (code - V4L2_MBUS_FMT_FIXED > ARRAY_SIZE(mbus_fmt) || - code <= V4L2_MBUS_FMT_FIXED) - return NULL; - return mbus_fmt + code - V4L2_MBUS_FMT_FIXED - 1; + return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); } EXPORT_SYMBOL(soc_mbus_get_fmtdesc); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 07fabdd9b465..6103d1b1081e 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -267,21 +267,27 @@ hauppauge_tuner[] = { TUNER_ABSENT, "Xceive XC4000"}, { TUNER_ABSENT, "Dibcom 7070"}, { TUNER_PHILIPS_TDA8290, "NXP 18271C2"}, - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, + { TUNER_ABSENT, "Siano SMS1010"}, + { TUNER_ABSENT, "Siano SMS1150"}, + { TUNER_ABSENT, "MaxLinear 5007"}, + { TUNER_ABSENT, "TCL M09WPP_2P_E"}, /* 160-169 */ - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, - { TUNER_ABSENT, "unknown"}, + { TUNER_ABSENT, "Siano SMS1180"}, + { TUNER_ABSENT, "Maxim_MAX2165"}, + { TUNER_ABSENT, "Siano SMS1140"}, + { TUNER_ABSENT, "Siano SMS1150 B1"}, + { TUNER_ABSENT, "MaxLinear 111"}, + { TUNER_ABSENT, "Dibcom 7770"}, + { TUNER_ABSENT, "Siano SMS1180VNS"}, + { TUNER_ABSENT, "Siano SMS1184"}, { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"}, - { TUNER_ABSENT, "unknown"}, + { TUNER_ABSENT, "TCL_M11WPP_2PN_E"}, + /* 170-179 */ + { TUNER_ABSENT, "MaxLinear 301"}, + { TUNER_ABSENT, "Mirics MSi001"}, + { TUNER_ABSENT, "MaxLinear MxL241SF"}, + { TUNER_ABSENT, "Xceive XC5000C"}, + { TUNER_ABSENT, "Montage M68TS2020"}, }; /* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c index 68b998bd203f..8f5266157f15 100644 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -1025,6 +1025,34 @@ struct usbvision_device_data_st usbvision_device_data[] = { .y_offset = -1, .model_string = "Hauppauge WinTv-USB", }, + [MICROCAM_NTSC] = { + .interface = -1, + .codec = CODEC_WEBCAM, + .video_channels = 1, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 0, + .tuner = 0, + .tuner_type = 0, + .x_offset = 71, + .y_offset = 15, + .model_string = "Nogatech USB MicroCam NTSC (NV3000N)", + }, + [MICROCAM_PAL] = { + .interface = -1, + .codec = CODEC_WEBCAM, + .video_channels = 1, + .video_norm = V4L2_STD_PAL, + .audio_channels = 0, + .radio = 0, + .vbi = 0, + .tuner = 0, + .tuner_type = 0, + .x_offset = 71, + .y_offset = 18, + .model_string = "Nogatech USB MicroCam PAL (NV3001P)", + }, }; const int usbvision_device_data_size = ARRAY_SIZE(usbvision_device_data); @@ -1042,6 +1070,8 @@ struct usb_device_id usbvision_table[] = { { USB_DEVICE(0x0573, 0x2d00), .driver_info = HPG_WINTV_LIVE_PAL_BG }, { USB_DEVICE(0x0573, 0x2d01), .driver_info = HPG_WINTV_LIVE_PRO_NTSC_MN }, { USB_DEVICE(0x0573, 0x2101), .driver_info = ZORAN_PMD_NOGATECH }, + { USB_DEVICE(0x0573, 0x3000), .driver_info = MICROCAM_NTSC }, + { USB_DEVICE(0x0573, 0x3001), .driver_info = MICROCAM_PAL }, { USB_DEVICE(0x0573, 0x4100), .driver_info = NOGATECH_USB_TV_NTSC_FM }, { USB_DEVICE(0x0573, 0x4110), .driver_info = PNY_USB_TV_NTSC_FM }, { USB_DEVICE(0x0573, 0x4450), .driver_info = PV_PLAYTV_USB_PRO_PAL_FM }, @@ -1088,8 +1118,7 @@ struct usb_device_id usbvision_table[] = { { USB_DEVICE(0x2304, 0x0110), .driver_info = PINNA_PCTV_USB_PAL_FM }, { USB_DEVICE(0x2304, 0x0111), .driver_info = MIRO_PCTV_USB }, { USB_DEVICE(0x2304, 0x0112), .driver_info = PINNA_PCTV_USB_NTSC_FM }, - { USB_DEVICE(0x2304, 0x0113), - .driver_info = PINNA_PCTV_USB_NTSC_FM_V3 }, + { USB_DEVICE(0x2304, 0x0113), .driver_info = PINNA_PCTV_USB_NTSC_FM_V3 }, { USB_DEVICE(0x2304, 0x0210), .driver_info = PINNA_PCTV_USB_PAL_FM_V2 }, { USB_DEVICE(0x2304, 0x0212), .driver_info = PINNA_PCTV_USB_NTSC_FM_V2 }, { USB_DEVICE(0x2304, 0x0214), .driver_info = PINNA_PCTV_USB_PAL_FM_V3 }, diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h index 9c6ad22960d8..a51cc1185cce 100644 --- a/drivers/media/video/usbvision/usbvision-cards.h +++ b/drivers/media/video/usbvision/usbvision-cards.h @@ -63,5 +63,7 @@ #define PINNA_PCTV_BUNGEE_PAL_FM 62 #define HPG_WINTV 63 #define PINNA_PCTV_USB_NTSC_FM_V3 64 +#define MICROCAM_NTSC 65 +#define MICROCAM_PAL 66 extern const int usbvision_device_data_size; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index c8feb0d6fccf..f344411a4578 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -49,10 +49,6 @@ static unsigned int core_debug; module_param(core_debug, int, 0644); MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); -static unsigned int force_testpattern; -module_param(force_testpattern, int, 0644); -MODULE_PARM_DESC(force_testpattern, "enable test pattern display [core]"); - static int adjust_compression = 1; /* Set the compression to be adaptive */ module_param(adjust_compression, int, 0444); MODULE_PARM_DESC(adjust_compression, " Set the ADPCM compression for the device. Default: 1 (On)"); @@ -388,90 +384,6 @@ void usbvision_scratch_free(struct usb_usbvision *usbvision) } /* - * usbvision_testpattern() - * - * Procedure forms a test pattern (yellow grid on blue background). - * - * Parameters: - * fullframe: if TRUE then entire frame is filled, otherwise the procedure - * continues from the current scanline. - * pmode 0: fill the frame with solid blue color (like on VCR or TV) - * 1: Draw a colored grid - * - */ -static void usbvision_testpattern(struct usb_usbvision *usbvision, - int fullframe, int pmode) -{ - static const char proc[] = "usbvision_testpattern"; - struct usbvision_frame *frame; - unsigned char *f; - int num_cell = 0; - int scan_length = 0; - static int num_pass; - - if (usbvision == NULL) { - printk(KERN_ERR "%s: usbvision == NULL\n", proc); - return; - } - if (usbvision->cur_frame == NULL) { - printk(KERN_ERR "%s: usbvision->cur_frame is NULL.\n", proc); - return; - } - - /* Grab the current frame */ - frame = usbvision->cur_frame; - - /* Optionally start at the beginning */ - if (fullframe) { - frame->curline = 0; - frame->scanlength = 0; - } - - /* Form every scan line */ - for (; frame->curline < frame->frmheight; frame->curline++) { - int i; - - f = frame->data + (usbvision->curwidth * 3 * frame->curline); - for (i = 0; i < usbvision->curwidth; i++) { - unsigned char cb = 0x80; - unsigned char cg = 0; - unsigned char cr = 0; - - if (pmode == 1) { - if (frame->curline % 32 == 0) - cb = 0, cg = cr = 0xFF; - else if (i % 32 == 0) { - if (frame->curline % 32 == 1) - num_cell++; - cb = 0, cg = cr = 0xFF; - } else { - cb = - ((num_cell * 7) + - num_pass) & 0xFF; - cg = - ((num_cell * 5) + - num_pass * 2) & 0xFF; - cr = - ((num_cell * 3) + - num_pass * 3) & 0xFF; - } - } else { - /* Just the blue screen */ - } - - *f++ = cb; - *f++ = cg; - *f++ = cr; - scan_length += 3; - } - } - - frame->grabstate = frame_state_done; - frame->scanlength += scan_length; - ++num_pass; -} - -/* * usbvision_decompress_alloc() * * allocates intermediate buffer for decompression @@ -571,10 +483,6 @@ static enum parse_state usbvision_find_header(struct usb_usbvision *usbvision) frame->scanstate = scan_state_lines; frame->curline = 0; - if (force_testpattern) { - usbvision_testpattern(usbvision, 1, 1); - return parse_state_next_frame; - } return parse_state_continue; } @@ -1679,6 +1587,55 @@ int usbvision_power_off(struct usb_usbvision *usbvision) return err_code; } +/* configure webcam image sensor using the serial port */ +static int usbvision_init_webcam(struct usb_usbvision *usbvision) +{ + int rc; + int i; + static char init_values[38][3] = { + { 0x04, 0x12, 0x08 }, { 0x05, 0xff, 0xc8 }, { 0x06, 0x18, 0x07 }, { 0x07, 0x90, 0x00 }, + { 0x09, 0x00, 0x00 }, { 0x0a, 0x00, 0x00 }, { 0x0b, 0x08, 0x00 }, { 0x0d, 0xcc, 0xcc }, + { 0x0e, 0x13, 0x14 }, { 0x10, 0x9b, 0x83 }, { 0x11, 0x5a, 0x3f }, { 0x12, 0xe4, 0x73 }, + { 0x13, 0x88, 0x84 }, { 0x14, 0x89, 0x80 }, { 0x15, 0x00, 0x20 }, { 0x16, 0x00, 0x00 }, + { 0x17, 0xff, 0xa0 }, { 0x18, 0x6b, 0x20 }, { 0x19, 0x22, 0x40 }, { 0x1a, 0x10, 0x07 }, + { 0x1b, 0x00, 0x47 }, { 0x1c, 0x03, 0xe0 }, { 0x1d, 0x00, 0x00 }, { 0x1e, 0x00, 0x00 }, + { 0x1f, 0x00, 0x00 }, { 0x20, 0x00, 0x00 }, { 0x21, 0x00, 0x00 }, { 0x22, 0x00, 0x00 }, + { 0x23, 0x00, 0x00 }, { 0x24, 0x00, 0x00 }, { 0x25, 0x00, 0x00 }, { 0x26, 0x00, 0x00 }, + { 0x27, 0x00, 0x00 }, { 0x28, 0x00, 0x00 }, { 0x29, 0x00, 0x00 }, { 0x08, 0x80, 0x60 }, + { 0x0f, 0x2d, 0x24 }, { 0x0c, 0x80, 0x80 } + }; + char value[3]; + + /* the only difference between PAL and NTSC init_values */ + if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_NTSC) + init_values[4][1] = 0x34; + + for (i = 0; i < sizeof(init_values) / 3; i++) { + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT); + memcpy(value, init_values[i], 3); + rc = usb_control_msg(usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_SER_DAT1, value, + 3, HZ); + if (rc < 0) + return rc; + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SIO); + /* write 3 bytes to the serial port using SIO mode */ + usbvision_write_reg(usbvision, USBVISION_SER_CONT, 3 | 0x10); + usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, 0); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT); + usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_IO_2); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_DAT_IO); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT | USBVISION_DAT_IO); + } + + return 0; +} + /* * usbvision_set_video_format() * @@ -1797,6 +1754,13 @@ int usbvision_set_output(struct usb_usbvision *usbvision, int width, frame_drop = FRAMERATE_MAX; /* We can allow the maximum here, because dropping is controlled */ + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { + if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_PAL) + frame_drop = 25; + else + frame_drop = 30; + } + /* frame_drop = 7; => frame_phase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ... => frame_skip = 4; => frame_rate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25; @@ -2046,6 +2010,12 @@ int usbvision_set_input(struct usb_usbvision *usbvision) value[7] = 0x00; /* 0x0010 -> 16 Input video v offset */ } + /* webcam is only 480 pixels wide, both PAL and NTSC version */ + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { + value[0] = 0xe0; + value[1] = 0x01; /* 0x01E0 -> 480 Input video line length */ + } + if (usbvision_device_data[usbvision->dev_model].x_offset >= 0) { value[4] = usbvision_device_data[usbvision->dev_model].x_offset & 0xff; value[5] = (usbvision_device_data[usbvision->dev_model].x_offset & 0x0300) >> 8; @@ -2148,7 +2118,7 @@ static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) (__u16) USBVISION_DRM_PRM1, value, 8, HZ); if (rc < 0) { - dev_err(&usbvision->dev->dev, "%sERROR=%d\n", __func__, rc); + dev_err(&usbvision->dev->dev, "%s: ERROR=%d\n", __func__, rc); return rc; } @@ -2180,8 +2150,15 @@ int usbvision_power_on(struct usb_usbvision *usbvision) usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN | USBVISION_RES2); + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { + usbvision_write_reg(usbvision, USBVISION_VIN_REG1, + USBVISION_16_422_SYNC | USBVISION_HVALID_PO); + usbvision_write_reg(usbvision, USBVISION_VIN_REG2, + USBVISION_NOHVALID | USBVISION_KEEP_BLANK); + } usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN | USBVISION_PWR_VID); + mdelay(10); err_code = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2); if (err_code == 1) @@ -2310,6 +2287,8 @@ int usbvision_set_audio(struct usb_usbvision *usbvision, int audio_channel) int usbvision_setup(struct usb_usbvision *usbvision, int format) { + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) + usbvision_init_webcam(usbvision); usbvision_set_video_format(usbvision, format); usbvision_set_dram_settings(usbvision); usbvision_set_compress_params(usbvision); diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 05b1344181cd..d7f97513b289 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -222,7 +222,7 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) i2c_set_adapdata(&usbvision->i2c_adap, &usbvision->v4l2_dev); if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { - printk(KERN_ERR "usbvision_register: can't write reg\n"); + printk(KERN_ERR "usbvision_i2c_register: can't write reg\n"); return -EBUSY; } diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 9855fbe5927a..ea8ea8a48dfe 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -1471,7 +1471,8 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) /* This should be here to make i2c clients to be able to register */ /* first switch off audio */ - usbvision_audio_off(usbvision); + if (usbvision_device_data[model].audio_channels > 0) + usbvision_audio_off(usbvision); if (!power_on_at_open) { /* and then power up the noisy tuner */ usbvision_power_on(usbvision); diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index 8074787fd1ac..43cf61fe4943 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -59,6 +59,11 @@ #define USBVISION_AUDIO_RADIO 2 #define USBVISION_AUDIO_MUTE 3 #define USBVISION_SER_MODE 0x07 + #define USBVISION_CLK_OUT (1 << 0) + #define USBVISION_DAT_IO (1 << 1) + #define USBVISION_SENS_OUT (1 << 2) + #define USBVISION_SER_MODE_SOFT (0 << 4) + #define USBVISION_SER_MODE_SIO (1 << 4) #define USBVISION_SER_ADRS 0x08 #define USBVISION_SER_CONT 0x09 #define USBVISION_SER_DAT1 0x0A @@ -328,6 +333,7 @@ struct usbvision_frame { #define CODEC_SAA7113 7113 #define CODEC_SAA7111 7111 +#define CODEC_WEBCAM 3000 #define BRIDGE_NT1003 1003 #define BRIDGE_NT1004 1004 #define BRIDGE_NT1005 1005 diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 59f8a9ad3796..a4db26fa2f53 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -42,281 +42,313 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_PU_BRIGHTNESS_CONTROL, .index = 0, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_CONTRAST_CONTROL, .index = 1, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_HUE_CONTROL, .index = 2, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_SATURATION_CONTROL, .index = 3, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_SHARPNESS_CONTROL, .index = 4, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_GAMMA_CONTROL, .index = 5, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL, .index = 6, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, .index = 7, .size = 4, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL, .index = 8, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_GAIN_CONTROL, .index = 9, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, .index = 10, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_HUE_AUTO_CONTROL, .index = 11, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, .index = 12, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, .index = 13, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_DIGITAL_MULTIPLIER_CONTROL, .index = 14, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL, .index = 15, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL, .index = 16, .size = 1, - .flags = UVC_CONTROL_GET_CUR, + .flags = UVC_CTRL_FLAG_GET_CUR, }, { .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_ANALOG_LOCK_STATUS_CONTROL, .index = 17, .size = 1, - .flags = UVC_CONTROL_GET_CUR, + .flags = UVC_CTRL_FLAG_GET_CUR, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_SCANNING_MODE_CONTROL, .index = 0, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_AE_MODE_CONTROL, .index = 1, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_GET_DEF | UVC_CONTROL_GET_RES - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_AE_PRIORITY_CONTROL, .index = 2, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, .index = 3, .size = 4, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL, .index = 4, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL, .index = 5, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_FOCUS_RELATIVE_CONTROL, .index = 6, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN - | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES - | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL, .index = 7, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_IRIS_RELATIVE_CONTROL, .index = 8, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL, .index = 9, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, .index = 10, .size = 3, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN - | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES - | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, .index = 11, .size = 8, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, .index = 12, .size = 4, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN - | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES - | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_ROLL_ABSOLUTE_CONTROL, .index = 13, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_ROLL_RELATIVE_CONTROL, .index = 14, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN - | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES - | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_FOCUS_AUTO_CONTROL, .index = 17, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, }, { .entity = UVC_GUID_UVC_CAMERA, .selector = UVC_CT_PRIVACY_CONTROL, .index = 18, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, }, }; @@ -816,7 +848,7 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, { int ret; - if (ctrl->info.flags & UVC_CONTROL_GET_DEF) { + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) { ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id, chain->dev->intfnum, ctrl->info.selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF), @@ -825,7 +857,7 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, return ret; } - if (ctrl->info.flags & UVC_CONTROL_GET_MIN) { + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) { ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id, chain->dev->intfnum, ctrl->info.selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN), @@ -833,7 +865,7 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, if (ret < 0) return ret; } - if (ctrl->info.flags & UVC_CONTROL_GET_MAX) { + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) { ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id, chain->dev->intfnum, ctrl->info.selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX), @@ -841,7 +873,7 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, if (ret < 0) return ret; } - if (ctrl->info.flags & UVC_CONTROL_GET_RES) { + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) { ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id, chain->dev->intfnum, ctrl->info.selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), @@ -879,9 +911,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); v4l2_ctrl->flags = 0; - if (!(ctrl->info.flags & UVC_CONTROL_GET_CUR)) + if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; - if (!(ctrl->info.flags & UVC_CONTROL_SET_CUR)) + if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; if (!ctrl->cached) { @@ -890,7 +922,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, goto done; } - if (ctrl->info.flags & UVC_CONTROL_GET_DEF) { + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) { v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF)); } @@ -927,15 +959,15 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, break; } - if (ctrl->info.flags & UVC_CONTROL_GET_MIN) + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); - if (ctrl->info.flags & UVC_CONTROL_GET_MAX) + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); - if (ctrl->info.flags & UVC_CONTROL_GET_RES) + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); @@ -983,6 +1015,24 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain, } menu_info = &mapping->menu_info[query_menu->index]; + + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) { + s32 bitmap; + + if (!ctrl->cached) { + ret = uvc_ctrl_populate_cache(chain, ctrl); + if (ret < 0) + goto done; + } + + bitmap = mapping->get(mapping, UVC_GET_RES, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + if (!(bitmap & menu_info->value)) { + ret = -EINVAL; + goto done; + } + } + strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name); done: @@ -1039,7 +1089,7 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent * uvc_ctrl_get from using the cached value. */ - if (ctrl->info.flags & UVC_CONTROL_AUTO_UPDATE) + if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE) ctrl->loaded = 0; if (!ctrl->dirty) @@ -1094,7 +1144,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, int ret; ctrl = uvc_find_control(chain, xctrl->id, &mapping); - if (ctrl == NULL || (ctrl->info.flags & UVC_CONTROL_GET_CUR) == 0) + if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) return -EINVAL; if (!ctrl->loaded) { @@ -1136,7 +1186,7 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, int ret; ctrl = uvc_find_control(chain, xctrl->id, &mapping); - if (ctrl == NULL || (ctrl->info.flags & UVC_CONTROL_SET_CUR) == 0) + if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) == 0) return -EINVAL; /* Clamp out of range values. */ @@ -1171,6 +1221,23 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, if (xctrl->value < 0 || xctrl->value >= mapping->menu_count) return -ERANGE; value = mapping->menu_info[xctrl->value].value; + + /* Valid menu indices are reported by the GET_RES request for + * UVC controls that support it. + */ + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) { + if (!ctrl->cached) { + ret = uvc_ctrl_populate_cache(chain, ctrl); + if (ret < 0) + return ret; + } + + step = mapping->get(mapping, UVC_GET_RES, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + if (!(step & value)) + return -ERANGE; + } + break; default: @@ -1183,7 +1250,7 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, * operation. */ if (!ctrl->loaded && (ctrl->info.size * 8) != mapping->size) { - if ((ctrl->info.flags & UVC_CONTROL_GET_CUR) == 0) { + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) { memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), 0, ctrl->info.size); } else { @@ -1230,17 +1297,17 @@ static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev, static const struct uvc_ctrl_fixup fixups[] = { { { USB_DEVICE(0x046d, 0x08c2) }, 9, 1, - UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | - UVC_CONTROL_GET_DEF | UVC_CONTROL_SET_CUR | - UVC_CONTROL_AUTO_UPDATE }, + UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX | + UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR | + UVC_CTRL_FLAG_AUTO_UPDATE }, { { USB_DEVICE(0x046d, 0x08cc) }, 9, 1, - UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | - UVC_CONTROL_GET_DEF | UVC_CONTROL_SET_CUR | - UVC_CONTROL_AUTO_UPDATE }, + UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX | + UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR | + UVC_CTRL_FLAG_AUTO_UPDATE }, { { USB_DEVICE(0x046d, 0x0994) }, 9, 1, - UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | - UVC_CONTROL_GET_DEF | UVC_CONTROL_SET_CUR | - UVC_CONTROL_AUTO_UPDATE }, + UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX | + UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR | + UVC_CTRL_FLAG_AUTO_UPDATE }, }; unsigned int i; @@ -1297,21 +1364,23 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, goto done; } - info->flags = UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX - | UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF - | (data[0] & UVC_CONTROL_CAP_GET ? UVC_CONTROL_GET_CUR : 0) - | (data[0] & UVC_CONTROL_CAP_SET ? UVC_CONTROL_SET_CUR : 0) + info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF + | (data[0] & UVC_CONTROL_CAP_GET ? + UVC_CTRL_FLAG_GET_CUR : 0) + | (data[0] & UVC_CONTROL_CAP_SET ? + UVC_CTRL_FLAG_SET_CUR : 0) | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? - UVC_CONTROL_AUTO_UPDATE : 0); + UVC_CTRL_FLAG_AUTO_UPDATE : 0); uvc_ctrl_fixup_xu_info(dev, ctrl, info); uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, " "flags { get %u set %u auto %u }.\n", info->entity, info->selector, info->size, - (info->flags & UVC_CONTROL_GET_CUR) ? 1 : 0, - (info->flags & UVC_CONTROL_SET_CUR) ? 1 : 0, - (info->flags & UVC_CONTROL_AUTO_UPDATE) ? 1 : 0); + (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0, + (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0, + (info->flags & UVC_CTRL_FLAG_AUTO_UPDATE) ? 1 : 0); done: kfree(data); @@ -1344,32 +1413,33 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, } int uvc_xu_ctrl_query(struct uvc_video_chain *chain, - struct uvc_xu_control *xctrl, int set) + struct uvc_xu_control_query *xqry) { struct uvc_entity *entity; - struct uvc_control *ctrl = NULL; + struct uvc_control *ctrl; unsigned int i, found = 0; - int restore = 0; - __u8 *data; + __u32 reqflags; + __u16 size; + __u8 *data = NULL; int ret; /* Find the extension unit. */ list_for_each_entry(entity, &chain->entities, chain) { if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && - entity->id == xctrl->unit) + entity->id == xqry->unit) break; } - if (entity->id != xctrl->unit) { + if (entity->id != xqry->unit) { uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", - xctrl->unit); - return -EINVAL; + xqry->unit); + return -ENOENT; } /* Find the control and perform delayed initialization if needed. */ for (i = 0; i < entity->ncontrols; ++i) { ctrl = &entity->controls[i]; - if (ctrl->index == xctrl->selector - 1) { + if (ctrl->index == xqry->selector - 1) { found = 1; break; } @@ -1377,8 +1447,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, if (!found) { uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n", - entity->extension.guidExtensionCode, xctrl->selector); - return -EINVAL; + entity->extension.guidExtensionCode, xqry->selector); + return -ENOENT; } if (mutex_lock_interruptible(&chain->ctrl_mutex)) @@ -1390,43 +1460,72 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, goto done; } - /* Validate control data size. */ - if (ctrl->info.size != xctrl->size) { + /* Validate the required buffer size and flags for the request */ + reqflags = 0; + size = ctrl->info.size; + + switch (xqry->query) { + case UVC_GET_CUR: + reqflags = UVC_CTRL_FLAG_GET_CUR; + break; + case UVC_GET_MIN: + reqflags = UVC_CTRL_FLAG_GET_MIN; + break; + case UVC_GET_MAX: + reqflags = UVC_CTRL_FLAG_GET_MAX; + break; + case UVC_GET_DEF: + reqflags = UVC_CTRL_FLAG_GET_DEF; + break; + case UVC_GET_RES: + reqflags = UVC_CTRL_FLAG_GET_RES; + break; + case UVC_SET_CUR: + reqflags = UVC_CTRL_FLAG_SET_CUR; + break; + case UVC_GET_LEN: + size = 2; + break; + case UVC_GET_INFO: + size = 1; + break; + default: ret = -EINVAL; goto done; } - if ((set && !(ctrl->info.flags & UVC_CONTROL_SET_CUR)) || - (!set && !(ctrl->info.flags & UVC_CONTROL_GET_CUR))) { - ret = -EINVAL; + if (size != xqry->size) { + ret = -ENOBUFS; goto done; } - memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT); - restore = set; + if (reqflags && !(ctrl->info.flags & reqflags)) { + ret = -EBADRQC; + goto done; + } - if (set && copy_from_user(data, xctrl->data, xctrl->size)) { + data = kmalloc(size, GFP_KERNEL); + if (data == NULL) { + ret = -ENOMEM; + goto done; + } + + if (xqry->query == UVC_SET_CUR && + copy_from_user(data, xqry->data, size)) { ret = -EFAULT; goto done; } - ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR, - xctrl->unit, chain->dev->intfnum, xctrl->selector, - data, xctrl->size); + ret = uvc_query_ctrl(chain->dev, xqry->query, xqry->unit, + chain->dev->intfnum, xqry->selector, data, size); if (ret < 0) goto done; - if (!set && copy_to_user(xctrl->data, data, xctrl->size)) + if (xqry->query != UVC_SET_CUR && + copy_to_user(xqry->data, data, size)) ret = -EFAULT; done: - if (ret && restore) - memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), - xctrl->size); - + kfree(data); mutex_unlock(&chain->ctrl_mutex); return ret; } @@ -1458,7 +1557,7 @@ int uvc_ctrl_resume_device(struct uvc_device *dev) ctrl = &entity->controls[i]; if (!ctrl->initialized || !ctrl->modified || - (ctrl->info.flags & UVC_CONTROL_RESTORE) == 0) + (ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0) continue; printk(KERN_INFO "restoring control %pUl/%u/%u\n", diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 6459b8cba223..823f4b389745 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -84,6 +84,11 @@ static struct uvc_format_desc uvc_fmts[] = { .fcc = V4L2_PIX_FMT_YUV420, }, { + .name = "YUV 4:2:0 (M420)", + .guid = UVC_GUID_FORMAT_M420, + .fcc = V4L2_PIX_FMT_M420, + }, + { .name = "YUV 4:2:2 (UYVY)", .guid = UVC_GUID_FORMAT_UYVY, .fcc = V4L2_PIX_FMT_UYVY, @@ -103,6 +108,11 @@ static struct uvc_format_desc uvc_fmts[] = { .guid = UVC_GUID_FORMAT_BY8, .fcc = V4L2_PIX_FMT_SBGGR8, }, + { + .name = "RGB565", + .guid = UVC_GUID_FORMAT_RGBP, + .fcc = V4L2_PIX_FMT_RGB565, + }, }; /* ------------------------------------------------------------------------ @@ -2077,6 +2087,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Hercules Classic Silver */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x06f8, + .idProduct = 0x300c, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, /* ViMicro Vega */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2123,6 +2142,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* JMicron USB2.0 XGA WebCam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x152d, + .idProduct = 0x0310, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Syntek (HP Spartan) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index f14581bd707f..109a06384a8f 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -424,7 +424,7 @@ int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) break; } - if (i == queue->count || size != queue->buf_size) { + if (i == queue->count || PAGE_ALIGN(size) != queue->buf_size) { ret = -EINVAL; goto done; } @@ -436,6 +436,7 @@ int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) vma->vm_flags |= VM_IO; addr = (unsigned long)queue->mem + buffer->buf.m.offset; +#ifdef CONFIG_MMU while (size > 0) { page = vmalloc_to_page((void *)addr); if ((ret = vm_insert_page(vma, start, page)) < 0) @@ -445,6 +446,7 @@ int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) addr += PAGE_SIZE; size -= PAGE_SIZE; } +#endif vma->vm_ops = &uvc_vm_ops; vma->vm_private_data = buffer; @@ -488,6 +490,36 @@ done: return mask; } +#ifndef CONFIG_MMU +/* + * Get unmapped area. + * + * NO-MMU arch need this function to make mmap() work correctly. + */ +unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) +{ + struct uvc_buffer *buffer; + unsigned int i; + unsigned long ret; + + mutex_lock(&queue->mutex); + for (i = 0; i < queue->count; ++i) { + buffer = &queue->buffer[i]; + if ((buffer->buf.m.offset >> PAGE_SHIFT) == pgoff) + break; + } + if (i == queue->count) { + ret = -EINVAL; + goto done; + } + ret = (unsigned long)queue->mem + buffer->buf.m.offset; +done: + mutex_unlock(&queue->mutex); + return ret; +} +#endif + /* * Enable or disable the video buffers queue. * diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 9005a8d9d5f8..543a80395b7f 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -538,6 +538,20 @@ static int uvc_v4l2_release(struct file *file) return 0; } +static void uvc_v4l2_ioctl_warn(void) +{ + static int warned; + + if (warned) + return; + + uvc_printk(KERN_INFO, "Deprecated UVCIOC_CTRL_{ADD,MAP_OLD,GET,SET} " + "ioctls will be removed in 2.6.42.\n"); + uvc_printk(KERN_INFO, "See http://www.ideasonboard.org/uvc/upgrade/ " + "for upgrade instructions.\n"); + warned = 1; +} + static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); @@ -1018,21 +1032,40 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd); return -EINVAL; - /* Dynamic controls. */ - case UVCIOC_CTRL_ADD: - /* Legacy ioctl, kept for API compatibility reasons */ + /* Dynamic controls. UVCIOC_CTRL_ADD, UVCIOC_CTRL_MAP_OLD, + * UVCIOC_CTRL_GET and UVCIOC_CTRL_SET are deprecated and scheduled for + * removal in 2.6.42. + */ + case __UVCIOC_CTRL_ADD: + uvc_v4l2_ioctl_warn(); return -EEXIST; - case UVCIOC_CTRL_MAP_OLD: + case __UVCIOC_CTRL_MAP_OLD: + uvc_v4l2_ioctl_warn(); + case __UVCIOC_CTRL_MAP: case UVCIOC_CTRL_MAP: return uvc_ioctl_ctrl_map(chain, arg, - cmd == UVCIOC_CTRL_MAP_OLD); + cmd == __UVCIOC_CTRL_MAP_OLD); - case UVCIOC_CTRL_GET: - return uvc_xu_ctrl_query(chain, arg, 0); + case __UVCIOC_CTRL_GET: + case __UVCIOC_CTRL_SET: + { + struct uvc_xu_control *xctrl = arg; + struct uvc_xu_control_query xqry = { + .unit = xctrl->unit, + .selector = xctrl->selector, + .query = cmd == __UVCIOC_CTRL_GET + ? UVC_GET_CUR : UVC_SET_CUR, + .size = xctrl->size, + .data = xctrl->data, + }; + + uvc_v4l2_ioctl_warn(); + return uvc_xu_ctrl_query(chain, &xqry); + } - case UVCIOC_CTRL_SET: - return uvc_xu_ctrl_query(chain, arg, 1); + case UVCIOC_CTRL_QUERY: + return uvc_xu_ctrl_query(chain, arg); default: uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); @@ -1081,6 +1114,20 @@ static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) return uvc_queue_poll(&stream->queue, file, wait); } +#ifndef CONFIG_MMU +static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + struct uvc_fh *handle = file->private_data; + struct uvc_streaming *stream = handle->stream; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n"); + + return uvc_queue_get_unmapped_area(&stream->queue, pgoff); +} +#endif + const struct v4l2_file_operations uvc_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, @@ -1089,5 +1136,8 @@ const struct v4l2_file_operations uvc_fops = { .read = uvc_v4l2_read, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, +#ifndef CONFIG_MMU + .get_unmapped_area = uvc_v4l2_get_unmapped_area, +#endif }; diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 45f01e7e13d2..7cf224bae2e5 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -4,6 +4,14 @@ #include <linux/kernel.h> #include <linux/videodev2.h> +#ifndef __KERNEL__ +/* + * This header provides binary compatibility with applications using the private + * uvcvideo API. This API is deprecated and will be removed in 2.6.42. + * Applications should be recompiled against the public linux/uvcvideo.h header. + */ +#warn "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead." + /* * Dynamic controls */ @@ -23,32 +31,18 @@ #define UVC_CONTROL_GET_MAX (1 << 3) #define UVC_CONTROL_GET_RES (1 << 4) #define UVC_CONTROL_GET_DEF (1 << 5) -/* Control should be saved at suspend and restored at resume. */ #define UVC_CONTROL_RESTORE (1 << 6) -/* Control can be updated by the camera. */ #define UVC_CONTROL_AUTO_UPDATE (1 << 7) #define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ UVC_CONTROL_GET_DEF) -struct uvc_xu_control_info { - __u8 entity[16]; - __u8 index; - __u8 selector; - __u16 size; - __u32 flags; -}; - struct uvc_menu_info { __u32 value; __u8 name[32]; }; -struct uvc_xu_control_mapping_old { - __u8 reserved[64]; -}; - struct uvc_xu_control_mapping { __u32 id; __u8 name[32]; @@ -57,7 +51,7 @@ struct uvc_xu_control_mapping { __u8 size; __u8 offset; - enum v4l2_ctrl_type v4l2_type; + __u32 v4l2_type; __u32 data_type; struct uvc_menu_info __user *menu_info; @@ -66,6 +60,20 @@ struct uvc_xu_control_mapping { __u32 reserved[4]; }; +#endif + +struct uvc_xu_control_info { + __u8 entity[16]; + __u8 index; + __u8 selector; + __u16 size; + __u32 flags; +}; + +struct uvc_xu_control_mapping_old { + __u8 reserved[64]; +}; + struct uvc_xu_control { __u8 unit; __u8 selector; @@ -73,16 +81,25 @@ struct uvc_xu_control { __u8 __user *data; }; +#ifndef __KERNEL__ #define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) #define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) #define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) +#else +#define __UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) +#define __UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) +#define __UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) +#define __UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) +#define __UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) +#endif #ifdef __KERNEL__ #include <linux/poll.h> #include <linux/usb/video.h> +#include <linux/uvcvideo.h> /* -------------------------------------------------------------------------- * UVC constants @@ -152,13 +169,19 @@ struct uvc_xu_control { #define UVC_GUID_FORMAT_BY8 \ { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_RGBP \ + { 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_M420 \ + { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} /* ------------------------------------------------------------------------ * Driver specific constants. */ -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(1, 0, 0) -#define DRIVER_VERSION "v1.0.0" +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(1, 1, 0) +#define DRIVER_VERSION "v1.1.0" /* Number of isochronous URBs. */ #define UVC_URBS 5 @@ -580,6 +603,10 @@ extern int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma); extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, poll_table *wait); +#ifndef CONFIG_MMU +extern unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff); +#endif extern int uvc_queue_allocated(struct uvc_video_queue *queue); static inline int uvc_queue_streaming(struct uvc_video_queue *queue) { @@ -638,7 +665,7 @@ extern int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, - struct uvc_xu_control *ctrl, int set); + struct uvc_xu_control_query *xqry); /* Utility functions */ extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, @@ -655,4 +682,3 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, #endif /* __KERNEL__ */ #endif - diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 6dc7196296b3..19d5ae293780 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -352,6 +352,23 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return ret; } +#ifdef CONFIG_MMU +#define v4l2_get_unmapped_area NULL +#else +static unsigned long v4l2_get_unmapped_area(struct file *filp, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->get_unmapped_area) + return -ENOSYS; + if (!video_is_registered(vdev)) + return -ENODEV; + return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); +} +#endif + static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) { struct video_device *vdev = video_devdata(filp); @@ -454,6 +471,7 @@ static const struct file_operations v4l2_fops = { .read = v4l2_read, .write = v4l2_write, .open = v4l2_open, + .get_unmapped_area = v4l2_get_unmapped_area, .mmap = v4l2_mmap, .unlocked_ioctl = v4l2_ioctl, #ifdef CONFIG_COMPAT diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c index 8c780c2d937b..85d3048c1d67 100644 --- a/drivers/media/video/via-camera.c +++ b/drivers/media/video/via-camera.c @@ -29,6 +29,7 @@ #include "via-camera.h" +MODULE_ALIAS("platform:viafb-camera"); MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); MODULE_DESCRIPTION("VIA framebuffer-based camera controller driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c index 9f2bac519647..79b04ac0f1ad 100644 --- a/drivers/media/video/zoran/zoran_card.c +++ b/drivers/media/video/zoran/zoran_card.c @@ -64,14 +64,6 @@ static int card[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "Card type"); -static int encoder[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 }; -module_param_array(encoder, int, NULL, 0444); -MODULE_PARM_DESC(encoder, "Video encoder chip"); - -static int decoder[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 }; -module_param_array(decoder, int, NULL, 0444); -MODULE_PARM_DESC(decoder, "Video decoder chip"); - /* The video mem address of the video card. The driver has a little database for some videocards @@ -1230,7 +1222,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, mutex_init(&zr->other_lock); if (pci_enable_device(pdev)) goto zr_unreg; - pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, &zr->revision); + zr->revision = zr->pci_dev->revision; dprintk(1, KERN_INFO |