diff options
360 files changed, 17961 insertions, 9248 deletions
@@ -19,18 +19,7 @@ install: $(MAKE) -C $(BUILD_DIR) $(MAKECMDGOALS) commit cvscommit hgcommit change changes changelog:: whitespace - @cd $(BUILD_DIR); scripts/cardlist; cd .. - @v4l/scripts/prep_commit_msg.pl $(TMP)/v4l_hg_whitespace > \ - $(TMP)/v4l_hg_commit.msg - @CHECKSUM=`md5sum "$(TMP)/v4l_hg_commit.msg"`; \ - $(EDITOR) $(TMP)/v4l_hg_commit.msg || exit $?; \ - echo "$$CHECKSUM" | md5sum -c --status && \ - echo "*** commit message not changed. Aborting. ***" \ - && exit 13 || exit 0 - $(BUILD_DIR)/scripts/hghead.pl $(TMP)/v4l_hg_commit.msg | grep -v '^#' | hg commit -l - - @echo "*** PLEASE CHECK IF LOG IS OK:" - @hg log -v -r -1 - @echo "*** If not ok, do \"hg rollback\" and \"make commit\" again" + @cd $(BUILD_DIR); scripts/cardlist; scripts/do_commit.sh $(EDITOR) $(TMP)/v4l_hg_whitespace; cd .. qrefresh: Q=q qrefresh:: whitespace diff --git a/linux/Documentation/video4linux/CARDLIST.cx23885 b/linux/Documentation/video4linux/CARDLIST.cx23885 index f0e613ba5..64823ccac 100644 --- a/linux/Documentation/video4linux/CARDLIST.cx23885 +++ b/linux/Documentation/video4linux/CARDLIST.cx23885 @@ -9,3 +9,5 @@ 8 -> Hauppauge WinTV-HVR1700 [0070:8101] 9 -> Hauppauge WinTV-HVR1400 [0070:8010] 10 -> DViCO FusionHDTV7 Dual Express [18ac:d618] + 11 -> DViCO FusionHDTV DVB-T Dual Express [18ac:db78] + 12 -> Leadtek Winfast PxDVR3200 H [107d:6681] diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx index 89c7f32ab..b9181b523 100644 --- a/linux/Documentation/video4linux/CARDLIST.em28xx +++ b/linux/Documentation/video4linux/CARDLIST.em28xx @@ -1,5 +1,5 @@ 0 -> Unknown EM2800 video grabber (em2800) [eb1a:2800] - 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883] + 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883] 2 -> Terratec Cinergy 250 USB (em2820/em2840) [0ccd:0036] 3 -> Pinnacle PCTV USB 2 (em2820/em2840) [2304:0208] 4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200,2040:4201] @@ -12,7 +12,7 @@ 11 -> Terratec Hybrid XS (em2880) [0ccd:0042] 12 -> Kworld PVR TV 2800 RF (em2820/em2840) 13 -> Terratec Prodigy XS (em2880) [0ccd:0047] - 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) + 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) [eb1a:2821] 15 -> V-Gear PocketTV (em2800) 16 -> Hauppauge WinTV HVR 950 (em2883) [2040:6513,2040:6517,2040:651b,2040:651f] 17 -> Pinnacle PCTV HD Pro Stick (em2880) [2304:0227] diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 78a863ab8..308ce4ee2 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -42,6 +42,7 @@ zc3xx 0458:7007 Genius VideoCam V2 zc3xx 0458:700c Genius VideoCam V3 zc3xx 0458:700f Genius VideoCam Web V2 sonixj 0458:7025 Genius Eye 311Q +sonixj 0458:702e Genius Slim 310 NB sonixj 045e:00f5 MicroSoft VX3000 sonixj 045e:00f7 MicroSoft VX1000 ov519 045e:028c Micro$oft xbox cam @@ -88,14 +89,14 @@ zc3xx 0471:0325 Philips SPC 200 NC zc3xx 0471:0326 Philips SPC 300 NC sonixj 0471:0327 Philips SPC 600 NC sonixj 0471:0328 Philips SPC 700 NC -zc3xx 0471:032d Philips spc210nc -zc3xx 0471:032e Philips spc315nc -sonixj 0471:0330 Philips SPC 710NC +zc3xx 0471:032d Philips SPC 210 NC +zc3xx 0471:032e Philips SPC 315 NC +sonixj 0471:0330 Philips SPC 710 NC spca501 0497:c001 Smile International sunplus 04a5:3003 Benq DC 1300 sunplus 04a5:3008 Benq DC 1500 -sunplus 04a5:300a Benq DC3410 -spca500 04a5:300c Benq DC1016 +sunplus 04a5:300a Benq DC 3410 +spca500 04a5:300c Benq DC 1016 sunplus 04f1:1001 JVC GC A50 spca561 04fc:0561 Flexcam 100 sunplus 04fc:500c Sunplus CA500C @@ -175,19 +176,23 @@ sunplus 08ca:2060 Aiptek PocketDV5300 tv8532 0923:010f ICM532 cams mars 093a:050f Mars-Semi Pc-Camera pac207 093a:2460 PAC207 Qtec Webcam 100 -pac207 093a:2463 Philips spc200nc pac207 +pac207 093a:2463 Philips SPC 220 NC pac207 093a:2464 Labtec Webcam 1200 pac207 093a:2468 PAC207 pac207 093a:2470 Genius GF112 -pac207 093a:2471 PAC207 Genius VideoCam ge111 -pac207 093a:2472 PAC207 Genius VideoCam ge110 +pac207 093a:2471 Genius VideoCam ge111 +pac207 093a:2472 Genius VideoCam ge110 +pac207 093a:2476 Genius e-Messenger 112 pac7311 093a:2600 PAC7311 Typhoon -pac7311 093a:2601 PAC7311 Phillips SPC610NC +pac7311 093a:2601 Philips SPC 610 NC pac7311 093a:2603 PAC7312 -pac7311 093a:2608 PAC7311 Trust WB-3300p -pac7311 093a:260e PAC7311 Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350 -pac7311 093a:260f PAC7311 SnakeCam +pac7311 093a:2608 Trust WB-3300p +pac7311 093a:260e Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350 +pac7311 093a:260f SnakeCam pac7311 093a:2621 PAC731x +pac7311 093a:2624 PAC7302 +pac7311 093a:2626 Labtec 2200 +pac7311 093a:262a Webcam 300k zc3xx 0ac8:0302 Z-star Vimicro zc0302 vc032x 0ac8:0321 Vimicro generic vc0321 vc032x 0ac8:0323 Vimicro Vc0323 @@ -220,6 +225,7 @@ sonixj 0c45:60c0 Sangha Sn535 sonixj 0c45:60ec SN9C105+MO4000 sonixj 0c45:60fb Surfer NoName sonixj 0c45:60fc LG-LIC300 +sonixj 0c45:6128 Microdia/Sonix SNP325 sonixj 0c45:612a Avant Camera sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix sonixj 0c45:6130 Sonix Pccam @@ -234,7 +240,7 @@ zc3xx 10fd:0128 Typhoon Webshot II USB 300k 0x0128 spca561 10fd:7e50 FlyCam Usb 100 zc3xx 10fd:8050 Typhoon Webshot II USB 300k spca501 1776:501c Arowana 300K CMOS Camera -t613 17a1:0128 T613/TAS5130A +t613 17a1:0128 TASCORP JPEG Webcam, NGS Cyclops vc032x 17ef:4802 Lenovo Vc0323+MI1310_SOC pac207 2001:f115 D-Link DSB-C120 spca500 2899:012c Toptro Industrial diff --git a/linux/Documentation/video4linux/soc-camera.txt b/linux/Documentation/video4linux/soc-camera.txt new file mode 100644 index 000000000..178ef3c5e --- /dev/null +++ b/linux/Documentation/video4linux/soc-camera.txt @@ -0,0 +1,120 @@ + Soc-Camera Subsystem + ==================== + +Terminology +----------- + +The following terms are used in this document: + - camera / camera device / camera sensor - a video-camera sensor chip, capable + of connecting to a variety of systems and interfaces, typically uses i2c for + control and configuration, and a parallel or a serial bus for data. + - camera host - an interface, to which a camera is connected. Typically a + specialised interface, present on many SoCs, e.g., PXA27x and PXA3xx, SuperH, + AVR32, i.MX27, i.MX31. + - camera host bus - a connection between a camera host and a camera. Can be + parallel or serial, consists of data and control lines, e.g., clock, vertical + and horizontal synchronization signals. + +Purpose of the soc-camera subsystem +----------------------------------- + +The soc-camera subsystem provides a unified API between camera host drivers and +camera sensor drivers. It implements a V4L2 interface to the user, currently +only the mmap method is supported. + +This subsystem has been written to connect drivers for System-on-Chip (SoC) +video capture interfaces with drivers for CMOS camera sensor chips to enable +the reuse of sensor drivers with various hosts. The subsystem has been designed +to support multiple camera host interfaces and multiple cameras per interface, +although most applications have only one camera sensor. + +Existing drivers +---------------- + +As of 2.6.27-rc4 there are two host drivers in the mainline: pxa_camera.c for +PXA27x SoCs and sh_mobile_ceu_camera.c for SuperH SoCs, and four sensor drivers: +mt9m001.c, mt9m111.c, mt9v022.c and a generic soc_camera_platform.c driver. This +list is not supposed to be updated, look for more examples in your tree. + +Camera host API +--------------- + +A host camera driver is registered using the + +soc_camera_host_register(struct soc_camera_host *); + +function. The host object can be initialized as follows: + +static struct soc_camera_host pxa_soc_camera_host = { + .drv_name = PXA_CAM_DRV_NAME, + .ops = &pxa_soc_camera_host_ops, +}; + +All camera host methods are passed in a struct soc_camera_host_ops: + +static struct soc_camera_host_ops pxa_soc_camera_host_ops = { + .owner = THIS_MODULE, + .add = pxa_camera_add_device, + .remove = pxa_camera_remove_device, + .suspend = pxa_camera_suspend, + .resume = pxa_camera_resume, + .set_fmt_cap = pxa_camera_set_fmt_cap, + .try_fmt_cap = pxa_camera_try_fmt_cap, + .init_videobuf = pxa_camera_init_videobuf, + .reqbufs = pxa_camera_reqbufs, + .poll = pxa_camera_poll, + .querycap = pxa_camera_querycap, + .try_bus_param = pxa_camera_try_bus_param, + .set_bus_param = pxa_camera_set_bus_param, +}; + +.add and .remove methods are called when a sensor is attached to or detached +from the host, apart from performing host-internal tasks they shall also call +sensor driver's .init and .release methods respectively. .suspend and .resume +methods implement host's power-management functionality and its their +responsibility to call respective sensor's methods. .try_bus_param and +.set_bus_param are used to negotiate physical connection parameters between the +host and the sensor. .init_videobuf is called by soc-camera core when a +video-device is opened, further video-buffer management is implemented completely +by the specific camera host driver. The rest of the methods are called from +respective V4L2 operations. + +Camera API +---------- + +Sensor drivers can use struct soc_camera_link, typically provided by the +platform, and used to specify to which camera host bus the sensor is connected, +and arbitrarily provide platform .power and .reset methods for the camera. +soc_camera_device_register() and soc_camera_device_unregister() functions are +used to add a sensor driver to or remove one from the system. The registration +function takes a pointer to struct soc_camera_device as the only parameter. +This struct can be initialized as follows: + + /* link to driver operations */ + icd->ops = &mt9m001_ops; + /* link to the underlying physical (e.g., i2c) device */ + icd->control = &client->dev; + /* window geometry */ + icd->x_min = 20; + icd->y_min = 12; + icd->x_current = 20; + icd->y_current = 12; + icd->width_min = 48; + icd->width_max = 1280; + icd->height_min = 32; + icd->height_max = 1024; + icd->y_skip_top = 1; + /* camera bus ID, typically obtained from platform data */ + icd->iface = icl->bus_id; + +struct soc_camera_ops provides .probe and .remove methods, which are called by +the soc-camera core, when a camera is matched against or removed from a camera +host bus, .init, .release, .suspend, and .resume are called from the camera host +driver as discussed above. Other members of this struct provide respective V4L2 +functionality. + +struct soc_camera_device also links to an array of struct soc_camera_data_format, +listing pixel formats, supported by the camera. + +-- +Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> diff --git a/linux/arch/arm/mach-pxa/devices.c b/linux/arch/arm/mach-pxa/devices.c index 84489dc51..c77a4f753 100644 --- a/linux/arch/arm/mach-pxa/devices.c +++ b/linux/arch/arm/mach-pxa/devices.c @@ -4,6 +4,7 @@ #include <linux/platform_device.h> #include <linux/dma-mapping.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) #include <asm/arch/gpio.h> #include <asm/arch/udc.h> #include <asm/arch/pxafb.h> @@ -17,6 +18,21 @@ #include <asm/arch/camera.h> #include <asm/arch/audio.h> #include <asm/arch/pxa3xx_nand.h> +#else +#include <mach/gpio.h> +#include <mach/udc.h> +#include <mach/pxafb.h> +#include <mach/mmc.h> +#include <mach/irda.h> +#include <mach/i2c.h> +#include <mach/mfp-pxa27x.h> +#include <mach/ohci.h> +#include <mach/pxa27x_keypad.h> +#include <mach/pxa2xx_spi.h> +#include <mach/camera.h> +#include <mach/audio.h> +#include <mach/pxa3xx_nand.h> +#endif #include "devices.h" #include "generic.h" diff --git a/linux/arch/arm/mach-pxa/pcm990-baseboard.c b/linux/arch/arm/mach-pxa/pcm990-baseboard.c index 30023b00e..973370295 100644 --- a/linux/arch/arm/mach-pxa/pcm990-baseboard.c +++ b/linux/arch/arm/mach-pxa/pcm990-baseboard.c @@ -22,16 +22,24 @@ #include <linux/irq.h> #include <linux/platform_device.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) #include <linux/ide.h> +#endif #include <linux/i2c.h> #include <linux/pwm_backlight.h> #include <media/soc_camera.h> #include <asm/gpio.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) #include <asm/arch/i2c.h> #include <asm/arch/camera.h> +#else +#include <mach/i2c.h> +#include <mach/camera.h> +#endif #include <asm/mach/map.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) #include <asm/arch/pxa-regs.h> #include <asm/arch/audio.h> #include <asm/arch/mmc.h> @@ -39,6 +47,15 @@ #include <asm/arch/pcm990_baseboard.h> #include <asm/arch/pxafb.h> #include <asm/arch/mfp-pxa27x.h> +#else +#include <mach/pxa-regs.h> +#include <mach/audio.h> +#include <mach/mmc.h> +#include <mach/ohci.h> +#include <mach/pcm990_baseboard.h> +#include <mach/pxafb.h> +#include <mach/mfp-pxa27x.h> +#endif #include "devices.h" #include "generic.h" diff --git a/linux/drivers/media/common/saa7146_core.c b/linux/drivers/media/common/saa7146_core.c index 7bcf1eb14..9bce837f3 100644 --- a/linux/drivers/media/common/saa7146_core.c +++ b/linux/drivers/media/common/saa7146_core.c @@ -234,7 +234,7 @@ void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt) int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) { __le32 *cpu; - dma_addr_t dma_addr; + dma_addr_t dma_addr = 0; cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr); if (NULL == cpu) { diff --git a/linux/drivers/media/common/saa7146_fops.c b/linux/drivers/media/common/saa7146_fops.c index a3dafa4fe..37a227d01 100644 --- a/linux/drivers/media/common/saa7146_fops.c +++ b/linux/drivers/media/common/saa7146_fops.c @@ -534,7 +534,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, memcpy(vfd, &device_template, sizeof(struct video_device)); strlcpy(vfd->name, name, sizeof(vfd->name)); vfd->release = video_device_release; - vfd->priv = dev; + video_set_drvdata(vfd, dev); // fixme: -1 should be an insmod parameter *for the extension* (like "video_nr"); if (video_register_device(vfd, type, -1) < 0) { diff --git a/linux/drivers/media/common/saa7146_video.c b/linux/drivers/media/common/saa7146_video.c index 1c22c39c2..8804d4e78 100644 --- a/linux/drivers/media/common/saa7146_video.c +++ b/linux/drivers/media/common/saa7146_video.c @@ -1069,7 +1069,7 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int { v4l2_std_id *id = arg; int found = 0; - int i, err; + int i; DEB_EE(("VIDIOC_S_STD\n")); @@ -1117,7 +1117,6 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int case VIDIOC_OVERLAY: { int on = *(int *)arg; - int err = 0; DEB_D(("VIDIOC_OVERLAY on:%d\n",on)); if (on != 0) { @@ -1193,7 +1192,6 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int case VIDIOCGMBUF: { struct video_mbuf *mbuf = arg; - struct videobuf_queue *q; int i; /* fixme: number of capture buffers and sizes for v4l apps */ diff --git a/linux/drivers/media/common/tuners/mt2060.c b/linux/drivers/media/common/tuners/mt2060.c index 355817754..d1962bb24 100644 --- a/linux/drivers/media/common/tuners/mt2060.c +++ b/linux/drivers/media/common/tuners/mt2060.c @@ -177,6 +177,9 @@ static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame b[0] = REG_LO1B1; b[1] = 0xFF; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + mt2060_writeregs(priv,b,2); freq = params->frequency / 1000; // Hz -> kHz @@ -240,6 +243,9 @@ static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame i++; } while (i<10); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + return ret; } @@ -303,13 +309,35 @@ static int mt2060_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) static int mt2060_init(struct dvb_frontend *fe) { struct mt2060_priv *priv = fe->tuner_priv; - return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x33); + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + ret = mt2060_writereg(priv, REG_VGAG, + (priv->cfg->clock_out << 6) | 0x33); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return ret; } static int mt2060_sleep(struct dvb_frontend *fe) { struct mt2060_priv *priv = fe->tuner_priv; - return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30); + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + ret = mt2060_writereg(priv, REG_VGAG, + (priv->cfg->clock_out << 6) | 0x30); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return ret; } static int mt2060_release(struct dvb_frontend *fe) @@ -351,6 +379,9 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter priv->i2c = i2c; priv->if1_freq = if1; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { kfree(priv); return NULL; @@ -367,6 +398,9 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter mt2060_calibrate(priv); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + return fe; } EXPORT_SYMBOL(mt2060_attach); diff --git a/linux/drivers/media/common/tuners/mt2131.c b/linux/drivers/media/common/tuners/mt2131.c index becc409d9..c528db6bb 100644 --- a/linux/drivers/media/common/tuners/mt2131.c +++ b/linux/drivers/media/common/tuners/mt2131.c @@ -1,7 +1,7 @@ /* * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/common/tuners/mt2131.h b/linux/drivers/media/common/tuners/mt2131.h index cd8376f6f..6632de640 100644 --- a/linux/drivers/media/common/tuners/mt2131.h +++ b/linux/drivers/media/common/tuners/mt2131.h @@ -1,7 +1,7 @@ /* * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/common/tuners/mt2131_priv.h b/linux/drivers/media/common/tuners/mt2131_priv.h index e930759c2..4e05a67e8 100644 --- a/linux/drivers/media/common/tuners/mt2131_priv.h +++ b/linux/drivers/media/common/tuners/mt2131_priv.h @@ -1,7 +1,7 @@ /* * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/common/tuners/mxl5005s.c b/linux/drivers/media/common/tuners/mxl5005s.c index e356db855..20405506a 100644 --- a/linux/drivers/media/common/tuners/mxl5005s.c +++ b/linux/drivers/media/common/tuners/mxl5005s.c @@ -2,7 +2,7 @@ MaxLinear MXL5005S VSB/QAM/DVBT tuner driver Copyright (C) 2008 MaxLinear - Copyright (C) 2006 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2006 Steven Toth <stoth@linuxtv.org> Functions: mxl5005s_reset() mxl5005s_writereg() @@ -3839,7 +3839,7 @@ static u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis) /* ---------------------------------------------------------------- * Begin: Everything after here is new code to adapt the * proprietary Realtek driver into a Linux API tuner. - * Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> */ static int mxl5005s_reset(struct dvb_frontend *fe) { diff --git a/linux/drivers/media/common/tuners/mxl5005s.h b/linux/drivers/media/common/tuners/mxl5005s.h index 396db150b..7ac6815b3 100644 --- a/linux/drivers/media/common/tuners/mxl5005s.h +++ b/linux/drivers/media/common/tuners/mxl5005s.h @@ -2,7 +2,7 @@ MaxLinear MXL5005S VSB/QAM/DVBT tuner driver Copyright (C) 2008 MaxLinear - Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> 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 diff --git a/linux/drivers/media/common/tuners/mxl5007t.c b/linux/drivers/media/common/tuners/mxl5007t.c index e795650c2..19abe35b9 100644 --- a/linux/drivers/media/common/tuners/mxl5007t.c +++ b/linux/drivers/media/common/tuners/mxl5007t.c @@ -1001,7 +1001,6 @@ struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, switch (instance) { case 0: goto fail; - break; case 1: /* new tuner instance */ state->config = cfg; diff --git a/linux/drivers/media/common/tuners/tda18271-fe.c b/linux/drivers/media/common/tuners/tda18271-fe.c index 68167b5ad..33bc96ea6 100644 --- a/linux/drivers/media/common/tuners/tda18271-fe.c +++ b/linux/drivers/media/common/tuners/tda18271-fe.c @@ -1156,7 +1156,6 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, switch (instance) { case 0: goto fail; - break; case 1: /* new tuner instance */ priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; diff --git a/linux/drivers/media/common/tuners/tda9887.c b/linux/drivers/media/common/tuners/tda9887.c index d588c5d88..effbf31da 100644 --- a/linux/drivers/media/common/tuners/tda9887.c +++ b/linux/drivers/media/common/tuners/tda9887.c @@ -687,7 +687,6 @@ struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, case 0: mutex_unlock(&tda9887_list_mutex); return NULL; - break; case 1: fe->analog_demod_priv = priv; priv->mode = T_STANDBY; diff --git a/linux/drivers/media/common/tuners/tuner-simple.c b/linux/drivers/media/common/tuners/tuner-simple.c index a709d6bee..036504510 100644 --- a/linux/drivers/media/common/tuners/tuner-simple.c +++ b/linux/drivers/media/common/tuners/tuner-simple.c @@ -261,7 +261,7 @@ static struct tuner_params *simple_tuner_params(struct dvb_frontend *fe, static int simple_config_lookup(struct dvb_frontend *fe, struct tuner_params *t_params, - int *frequency, u8 *config, u8 *cb) + unsigned *frequency, u8 *config, u8 *cb) { struct tuner_simple_priv *priv = fe->tuner_priv; int i; @@ -609,45 +609,45 @@ static int simple_set_tv_freq(struct dvb_frontend *fe, priv->last_div = div; if (t_params->has_tda9887) { struct v4l2_priv_tun_config tda9887_cfg; - int config = 0; + int tda_config = 0; int is_secam_l = (params->std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) && !(params->std & ~(V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)); tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &config; + tda9887_cfg.priv = &tda_config; if (params->std == V4L2_STD_SECAM_LC) { if (t_params->port1_active ^ t_params->port1_invert_for_secam_lc) - config |= TDA9887_PORT1_ACTIVE; + tda_config |= TDA9887_PORT1_ACTIVE; if (t_params->port2_active ^ t_params->port2_invert_for_secam_lc) - config |= TDA9887_PORT2_ACTIVE; + tda_config |= TDA9887_PORT2_ACTIVE; } else { if (t_params->port1_active) - config |= TDA9887_PORT1_ACTIVE; + tda_config |= TDA9887_PORT1_ACTIVE; if (t_params->port2_active) - config |= TDA9887_PORT2_ACTIVE; + tda_config |= TDA9887_PORT2_ACTIVE; } if (t_params->intercarrier_mode) - config |= TDA9887_INTERCARRIER; + tda_config |= TDA9887_INTERCARRIER; if (is_secam_l) { if (i == 0 && t_params->default_top_secam_low) - config |= TDA9887_TOP(t_params->default_top_secam_low); + tda_config |= TDA9887_TOP(t_params->default_top_secam_low); else if (i == 1 && t_params->default_top_secam_mid) - config |= TDA9887_TOP(t_params->default_top_secam_mid); + tda_config |= TDA9887_TOP(t_params->default_top_secam_mid); else if (t_params->default_top_secam_high) - config |= TDA9887_TOP(t_params->default_top_secam_high); + tda_config |= TDA9887_TOP(t_params->default_top_secam_high); } else { if (i == 0 && t_params->default_top_low) - config |= TDA9887_TOP(t_params->default_top_low); + tda_config |= TDA9887_TOP(t_params->default_top_low); else if (i == 1 && t_params->default_top_mid) - config |= TDA9887_TOP(t_params->default_top_mid); + tda_config |= TDA9887_TOP(t_params->default_top_mid); else if (t_params->default_top_high) - config |= TDA9887_TOP(t_params->default_top_high); + tda_config |= TDA9887_TOP(t_params->default_top_high); } if (t_params->default_pll_gating_18) - config |= TDA9887_GATING_18; + tda_config |= TDA9887_GATING_18; i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG, &tda9887_cfg); } @@ -835,7 +835,8 @@ static u32 simple_dvb_configure(struct dvb_frontend *fe, u8 *buf, static struct tuner_params *t_params; u8 config, cb; u32 div; - int ret, frequency = params->frequency / 62500; + int ret; + unsigned frequency = params->frequency / 62500; t_params = simple_tuner_params(fe, TUNER_PARAM_TYPE_DIGITAL); ret = simple_config_lookup(fe, t_params, &frequency, &config, &cb); @@ -1067,7 +1068,6 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, case 0: mutex_unlock(&tuner_simple_list_mutex); return NULL; - break; case 1: fe->tuner_priv = priv; diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.h b/linux/drivers/media/common/tuners/tuner-xc2028.h index 216025cf5..2c5b6282b 100644 --- a/linux/drivers/media/common/tuners/tuner-xc2028.h +++ b/linux/drivers/media/common/tuners/tuner-xc2028.h @@ -10,6 +10,7 @@ #include "dvb_frontend.h" #define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw" +#define XC3028L_DEFAULT_FIRMWARE "xc3028L-v36.fw" /* Dmoduler IF (kHz) */ #define XC3028_FE_DEFAULT 0 /* Don't load SCODE */ diff --git a/linux/drivers/media/common/tuners/xc5000.c b/linux/drivers/media/common/tuners/xc5000.c index e0f045e1b..7d4d82d23 100644 --- a/linux/drivers/media/common/tuners/xc5000.c +++ b/linux/drivers/media/common/tuners/xc5000.c @@ -2,7 +2,7 @@ * Driver for Xceive XC5000 "QAM/8VSB single chip tuner" * * Copyright (c) 2007 Xceive Corporation - * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org> * * 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 @@ -30,7 +30,7 @@ #include "dvb_frontend.h" #include "xc5000.h" -#include "xc5000_priv.h" +#include "tuner-i2c.h" static int debug; module_param(debug, int, 0644); @@ -40,12 +40,28 @@ static int xc5000_load_fw_on_attach; module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644); MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization."); +static DEFINE_MUTEX(xc5000_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); + #define dprintk(level,fmt, arg...) if (debug >= level) \ printk(KERN_INFO "%s: " fmt, "xc5000", ## arg) #define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.1.fw" #define XC5000_DEFAULT_FIRMWARE_SIZE 12332 +struct xc5000_priv { + struct tuner_i2c_props i2c_props; + struct list_head hybrid_tuner_instance_list; + + u32 if_khz; + u32 freq_hz; + u32 bandwidth; + u8 video_standard; + u8 rf_mode; + + int (*tuner_callback) (void *priv, int command, int arg); +}; + /* Misc Defines */ #define MAX_TV_STANDARD 23 #define XC_MAX_I2C_WRITE_LENGTH 64 @@ -216,9 +232,11 @@ static void xc5000_TunerReset(struct dvb_frontend *fe) dprintk(1, "%s()\n", __func__); - if (priv->cfg->tuner_callback) { - ret = priv->cfg->tuner_callback(priv->devptr, - XC5000_TUNER_RESET, 0); + if (priv->tuner_callback) { + ret = priv->tuner_callback(((fe->dvb) && (fe->dvb->priv)) ? + fe->dvb->priv : + priv->i2c_props.adap->algo_data, + XC5000_TUNER_RESET, 0); if (ret) printk(KERN_ERR "xc5000: reset failed\n"); } else @@ -528,13 +546,13 @@ static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val) u8 buf[2] = { reg >> 8, reg & 0xff }; u8 bval[2] = { 0, 0 }; struct i2c_msg msg[2] = { - { .addr = priv->cfg->i2c_address, + { .addr = priv->i2c_props.addr, .flags = 0, .buf = &buf[0], .len = 2 }, - { .addr = priv->cfg->i2c_address, + { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, .buf = &bval[0], .len = 2 }, }; - if (i2c_transfer(priv->i2c, msg, 2) != 2) { + if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) { printk(KERN_WARNING "xc5000: I2C read failed\n"); return -EREMOTEIO; } @@ -545,10 +563,10 @@ static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val) static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len) { - struct i2c_msg msg = { .addr = priv->cfg->i2c_address, + struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = 0, .buf = buf, .len = len }; - if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) { printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n", (int)len); return -EREMOTEIO; @@ -558,10 +576,10 @@ static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len) static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len) { - struct i2c_msg msg = { .addr = priv->cfg->i2c_address, + struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, .buf = buf, .len = len }; - if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) { printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n",(int)len); return -EREMOTEIO; } @@ -578,7 +596,7 @@ static int xc5000_fwupload(struct dvb_frontend* fe) printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n", XC5000_DEFAULT_FIRMWARE); - ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c->dev); + ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c_props.adap->dev); if (ret) { printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); ret = XC_RESULT_RESET_FAILURE; @@ -694,10 +712,10 @@ static int xc5000_set_params(struct dvb_frontend *fe, return -EREMOTEIO; } - ret = xc_set_IF_frequency(priv, priv->cfg->if_khz); + ret = xc_set_IF_frequency(priv, priv->if_khz); if (ret != XC_RESULT_SUCCESS) { printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n", - priv->cfg->if_khz); + priv->if_khz); return -EIO; } @@ -916,9 +934,19 @@ static int xc5000_init(struct dvb_frontend *fe) static int xc5000_release(struct dvb_frontend *fe) { + struct xc5000_priv *priv = fe->tuner_priv; + dprintk(1, "%s()\n", __func__); - kfree(fe->tuner_priv); + + mutex_lock(&xc5000_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&xc5000_list_mutex); + fe->tuner_priv = NULL; + return 0; } @@ -943,29 +971,44 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = { struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - struct xc5000_config *cfg, void *devptr) + struct xc5000_config *cfg) { struct xc5000_priv *priv = NULL; + int instance; u16 id = 0; - dprintk(1, "%s()\n", __func__); + dprintk(1, "%s(%d-%04x)\n", __func__, + i2c ? i2c_adapter_id(i2c) : -1, + cfg ? cfg->i2c_address : -1); + + mutex_lock(&xc5000_list_mutex); - priv = kzalloc(sizeof(struct xc5000_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; + instance = hybrid_tuner_request_state(struct xc5000_priv, priv, + hybrid_tuner_instance_list, + i2c, cfg->i2c_address, "xc5000"); + switch (instance) { + case 0: + goto fail; + break; + case 1: + /* new tuner instance */ + priv->bandwidth = BANDWIDTH_6_MHZ; + priv->if_khz = cfg->if_khz; + priv->tuner_callback = cfg->tuner_callback; - priv->cfg = cfg; - priv->bandwidth = BANDWIDTH_6_MHZ; - priv->i2c = i2c; - priv->devptr = devptr; + fe->tuner_priv = priv; + break; + default: + /* existing tuner instance */ + fe->tuner_priv = priv; + break; + } /* Check if firmware has been loaded. It is possible that another instance of the driver has loaded the firmware. */ - if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) { - kfree(priv); - return NULL; - } + if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) + goto fail; switch(id) { case XC_PRODUCT_ID_FW_LOADED: @@ -986,19 +1029,23 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, printk(KERN_ERR "xc5000: Device not found at addr 0x%02x (0x%x)\n", cfg->i2c_address, id); - kfree(priv); - return NULL; + goto fail; } + mutex_unlock(&xc5000_list_mutex); + memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops, sizeof(struct dvb_tuner_ops)); - fe->tuner_priv = priv; - if (xc5000_load_fw_on_attach) xc5000_init(fe); return fe; +fail: + mutex_unlock(&xc5000_list_mutex); + + xc5000_release(fe); + return NULL; } EXPORT_SYMBOL(xc5000_attach); diff --git a/linux/drivers/media/common/tuners/xc5000.h b/linux/drivers/media/common/tuners/xc5000.h index c910715ad..fa0321cfd 100644 --- a/linux/drivers/media/common/tuners/xc5000.h +++ b/linux/drivers/media/common/tuners/xc5000.h @@ -1,7 +1,7 @@ /* * Driver for Xceive XC5000 "QAM/8VSB single chip tuner" * - * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org> * * 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 @@ -49,13 +49,11 @@ struct xc5000_config { (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE)) extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - struct xc5000_config *cfg, - void *devptr); + struct xc5000_config *cfg); #else static inline struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - struct xc5000_config *cfg, - void *devptr) + struct xc5000_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/linux/drivers/media/common/tuners/xc5000_priv.h b/linux/drivers/media/common/tuners/xc5000_priv.h deleted file mode 100644 index a72a9887f..000000000 --- a/linux/drivers/media/common/tuners/xc5000_priv.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Driver for Xceive XC5000 "QAM/8VSB single chip tuner" - * - * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef XC5000_PRIV_H -#define XC5000_PRIV_H - -struct xc5000_priv { - struct xc5000_config *cfg; - struct i2c_adapter *i2c; - - u32 freq_hz; - u32 bandwidth; - u8 video_standard; - u8 rf_mode; - - void *devptr; -}; - -#endif diff --git a/linux/drivers/media/dvb/b2c2/flexcop-dma.c b/linux/drivers/media/dvb/b2c2/flexcop-dma.c index 1df246a7f..02a0f07a1 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-dma.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-dma.c @@ -10,7 +10,7 @@ int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size) { u8 *tcpu; - dma_addr_t tdma; + dma_addr_t tdma = 0; if (size % 2) { err("dma buffersize has to be even."); diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index f9d087669..4eed783f4 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -137,7 +137,8 @@ static int flexcop_send_diseqc_msg(struct dvb_frontend* fe, int len, u8 *msg, un flexcop_diseqc_send_byte(fe, 0xff); else { flexcop_set_tone(fe, SEC_TONE_ON); - udelay(12500); + mdelay(12); + udelay(500); flexcop_set_tone(fe, SEC_TONE_OFF); } msleep(20); diff --git a/linux/drivers/media/dvb/b2c2/flexcop-i2c.c b/linux/drivers/media/dvb/b2c2/flexcop-i2c.c index 55973eaf3..43a112ec6 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-i2c.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-i2c.c @@ -221,12 +221,12 @@ int flexcop_i2c_init(struct flexcop_device *fc) fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM; fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER; - strncpy(fc->fc_i2c_adap[0].i2c_adap.name, - "B2C2 FlexCop I2C to demod", I2C_NAME_SIZE); - strncpy(fc->fc_i2c_adap[1].i2c_adap.name, - "B2C2 FlexCop I2C to eeprom", I2C_NAME_SIZE); - strncpy(fc->fc_i2c_adap[2].i2c_adap.name, - "B2C2 FlexCop I2C to tuner", I2C_NAME_SIZE); + strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod", + sizeof(fc->fc_i2c_adap[0].i2c_adap.name)); + strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom", + sizeof(fc->fc_i2c_adap[1].i2c_adap.name)); + strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner", + sizeof(fc->fc_i2c_adap[2].i2c_adap.name)); i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]); i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]); diff --git a/linux/drivers/media/dvb/bt8xx/dst.c b/linux/drivers/media/dvb/bt8xx/dst.c index a7637562e..aa3db57d3 100644 --- a/linux/drivers/media/dvb/bt8xx/dst.c +++ b/linux/drivers/media/dvb/bt8xx/dst.c @@ -1244,7 +1244,7 @@ static int dst_command(struct dst_state *state, u8 *data, u8 len) goto error; } if (state->type_flags & DST_TYPE_HAS_FW_1) - udelay(3000); + mdelay(3); if (read_dst(state, &reply, GET_ACK)) { dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. "); if ((dst_error_recovery(state)) < 0) { @@ -1260,7 +1260,7 @@ static int dst_command(struct dst_state *state, u8 *data, u8 len) if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3)) goto error; if (state->type_flags & DST_TYPE_HAS_FW_1) - udelay(3000); + mdelay(3); else udelay(2000); if (!dst_wait_dst_ready(state, NO_DELAY)) diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 5fb213cf9..c4b8b0677 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -703,7 +703,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) } if (card->fe == NULL) - printk("dvb-bt8xx: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + printk("dvb-bt8xx: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", card->bt->dev->vendor, card->bt->dev->device, card->bt->dev->subsystem_vendor, diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c index 1cf9fcb6f..069d847ba 100644 --- a/linux/drivers/media/dvb/dvb-core/dmxdev.c +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c @@ -641,7 +641,6 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) struct timespec timeout = { 0 }; struct dmx_pes_filter_params *para = &filter->params.pes; dmx_output_t otype; - int ret; int ts_type; enum dmx_ts_pes ts_pes; struct dmx_ts_feed **tsfeed = &filter->feed.ts; diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c index c2954bf57..f6016feab 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c @@ -1033,7 +1033,7 @@ static int dvb_ca_en50221_thread(void *data) /* we need this extra check for annoying interfaces like the budget-av */ if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) && (ca->pub->poll_slot_status)) { - int status = ca->pub->poll_slot_status(ca->pub, slot, 0); + status = ca->pub->poll_slot_status(ca->pub, slot, 0); if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) { ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; dvb_ca_en50221_thread_update_delay(ca); @@ -1090,7 +1090,7 @@ static int dvb_ca_en50221_thread(void *data) /* we need this extra check for annoying interfaces like the budget-av */ if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) && (ca->pub->poll_slot_status)) { - int status = ca->pub->poll_slot_status(ca->pub, slot, 0); + status = ca->pub->poll_slot_status(ca->pub, slot, 0); if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) { ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; dvb_ca_en50221_thread_update_delay(ca); diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 8a9c2283f..60a94dc85 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -900,13 +900,13 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, * initialization, so parg is 8 bits and does not * include the initialization or start bit */ - unsigned long cmd = ((unsigned long) parg) << 1; + unsigned long swcmd = ((unsigned long) parg) << 1; struct timeval nexttime; struct timeval tv[10]; int i; u8 last = 1; if (dvb_frontend_debug) - printk("%s switch command: 0x%04lx\n", __func__, cmd); + printk("%s switch command: 0x%04lx\n", __func__, swcmd); do_gettimeofday(&nexttime); if (dvb_frontend_debug) memcpy(&tv[0], &nexttime, sizeof(struct timeval)); @@ -919,12 +919,12 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, for (i = 0; i < 9; i++) { if (dvb_frontend_debug) do_gettimeofday(&tv[i + 1]); - if ((cmd & 0x01) != last) { + if ((swcmd & 0x01) != last) { /* set voltage to (last ? 13V : 18V) */ fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18); last = (last) ? 0 : 1; } - cmd = cmd >> 1; + swcmd = swcmd >> 1; if (i != 8) dvb_frontend_sleep_until(&nexttime, 8000); } diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig index e84152b75..b35d7f0db 100644 --- a/linux/drivers/media/dvb/dvb-usb/Kconfig +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -108,6 +108,8 @@ config DVB_USB_CXUSB select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE + select DVB_DIB7000P if !DVB_FE_CUSTOMISE + select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -262,3 +264,22 @@ config DVB_USB_ANYSEE help Say Y here to support the Anysee E30, Anysee E30 Plus or Anysee E30 C Plus DVB USB2.0 receiver. + +config DVB_USB_DTV5100 + tristate "AME DTV-5100 USB2.0 DVB-T support" + depends on DVB_USB + select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + help + Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. + +config DVB_USB_AF9015 + tristate "Afatech AF9015 DVB-T USB2.0 support" + depends on DVB_USB && EXPERIMENTAL + select DVB_AF9013 + select DVB_PLL if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE + help + Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver diff --git a/linux/drivers/media/dvb/dvb-usb/Makefile b/linux/drivers/media/dvb/dvb-usb/Makefile index e206f1ea0..ece8c9dfe 100644 --- a/linux/drivers/media/dvb/dvb-usb/Makefile +++ b/linux/drivers/media/dvb/dvb-usb/Makefile @@ -67,6 +67,12 @@ obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o dvb-usb-dw2102-objs = dw2102.o obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o +dvb-usb-dtv5100-objs = dtv5100.o +obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o + +dvb-usb-af9015-objs = af9015.o +obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/linux/drivers/media/dvb/dvb-usb/af9005-remote.c b/linux/drivers/media/dvb/dvb-usb/af9005-remote.c index ff00c0e8f..7c596f926 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9005-remote.c +++ b/linux/drivers/media/dvb/dvb-usb/af9005-remote.c @@ -25,7 +25,7 @@ */ #include "af9005.h" /* debug */ -int dvb_usb_af9005_remote_debug; +static int dvb_usb_af9005_remote_debug; module_param_named(debug, dvb_usb_af9005_remote_debug, int, 0644); MODULE_PARM_DESC(debug, "enable (1) or disable (0) debug messages." diff --git a/linux/drivers/media/dvb/dvb-usb/af9005-script.h b/linux/drivers/media/dvb/dvb-usb/af9005-script.h index 6eeaae51b..4d6904542 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9005-script.h +++ b/linux/drivers/media/dvb/dvb-usb/af9005-script.h @@ -14,7 +14,7 @@ typedef struct { u8 val; } RegDesc; -RegDesc script[] = { +static RegDesc script[] = { {0xa180, 0x0, 0x8, 0xa}, {0xa181, 0x0, 0x8, 0xd7}, {0xa182, 0x0, 0x8, 0xa3}, diff --git a/linux/drivers/media/dvb/dvb-usb/af9005.c b/linux/drivers/media/dvb/dvb-usb/af9005.c index cfe71feef..ca5a0a4d2 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9005.c +++ b/linux/drivers/media/dvb/dvb-usb/af9005.c @@ -35,17 +35,17 @@ module_param_named(led, dvb_usb_af9005_led, bool, 0644); MODULE_PARM_DESC(led, "enable led (default: 1)."); /* eeprom dump */ -int dvb_usb_af9005_dump_eeprom = 0; +static int dvb_usb_af9005_dump_eeprom; module_param_named(dump_eeprom, dvb_usb_af9005_dump_eeprom, int, 0); MODULE_PARM_DESC(dump_eeprom, "dump contents of the eeprom."); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); /* remote control decoder */ -int (*rc_decode) (struct dvb_usb_device * d, u8 * data, int len, u32 * event, - int *state); -void *rc_keys; -int *rc_keys_size; +static int (*rc_decode) (struct dvb_usb_device *d, u8 *data, int len, + u32 *event, int *state); +static void *rc_keys; +static int *rc_keys_size; u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; @@ -54,8 +54,8 @@ struct af9005_device_state { int led_state; }; -int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 * wbuf, u16 wlen, - u8 * rbuf, u16 rlen, int delay_ms) +static int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, + u8 *rbuf, u16 rlen, int delay_ms) { int actlen, ret = -ENOMEM; @@ -98,12 +98,7 @@ int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 * wbuf, u16 wlen, return ret; } -int af9005_usb_generic_write(struct dvb_usb_device *d, u8 * buf, u16 len) -{ - return af9005_usb_generic_rw(d, buf, len, NULL, 0, 0); -} - -int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, +static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, int readwrite, int type, u8 * values, int len) { struct af9005_device_state *st = d->priv; @@ -765,7 +760,7 @@ static int af9005_boot_packet(struct usb_device *udev, int type, u8 * reply) return 0; } -int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw) +static int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw) { int i, packets, ret, act_len; diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c new file mode 100644 index 000000000..776d01d4a --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -0,0 +1,1474 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "af9015.h" +#include "af9013.h" +#include "mt2060.h" +#include "qt1010.h" +#include "tda18271.h" +#include "mxl5005s.h" +#if 0 /* keep */ +#include "mc44s80x.h" +#endif + +int dvb_usb_af9015_debug; +module_param_named(debug, dvb_usb_af9015_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +int dvb_usb_af9015_remote; +module_param_named(remote, dvb_usb_af9015_remote, int, 0644); +MODULE_PARM_DESC(remote, "select remote"); +int dvb_usb_af9015_dual_mode; +module_param_named(dual_mode, dvb_usb_af9015_dual_mode, int, 0644); +MODULE_PARM_DESC(dual_mode, "enable dual mode"); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static DEFINE_MUTEX(af9015_usb_mutex); + +static struct af9015_config af9015_config; +static struct dvb_usb_device_properties af9015_properties[2]; +int af9015_properties_count = ARRAY_SIZE(af9015_properties); + +static struct af9013_config af9015_af9013_config[] = { + { + .demod_address = AF9015_I2C_DEMOD, + .output_mode = AF9013_OUTPUT_MODE_USB, + .api_version = { 0, 1, 9, 0 }, + .gpio[0] = AF9013_GPIO_HI, + .gpio[1] = AF9013_GPIO_LO, + .gpio[3] = AF9013_GPIO_TUNER_ON, + + }, { + .output_mode = AF9013_OUTPUT_MODE_SERIAL, + .api_version = { 0, 1, 9, 0 }, + .gpio[0] = AF9013_GPIO_TUNER_ON, + .gpio[1] = AF9013_GPIO_LO, + } +}; + +static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) +{ + int act_len, ret; + u8 buf[64]; + u8 write = 1; + u8 msg_len = 8; + static u8 seq; /* packet sequence number */ + + if (mutex_lock_interruptible(&af9015_usb_mutex) < 0) + return -EAGAIN; + + buf[0] = req->cmd; + buf[1] = seq++; + buf[2] = req->i2c_addr; + buf[3] = req->addr >> 8; + buf[4] = req->addr & 0xff; + buf[5] = req->mbox; + buf[6] = req->addr_len; + buf[7] = req->data_len; + + switch (req->cmd) { + case GET_CONFIG: + case BOOT: + case READ_MEMORY: + case RECONNECT_USB: + case GET_IR_CODE: + write = 0; + break; + case READ_I2C: + write = 0; + buf[2] |= 0x01; /* set I2C direction */ + case WRITE_I2C: + buf[0] = READ_WRITE_I2C; + break; + case WRITE_MEMORY: + if (((req->addr & 0xff00) == 0xff00) || + ((req->addr & 0xae00) == 0xae00)) + buf[0] = WRITE_VIRTUAL_MEMORY; + case WRITE_VIRTUAL_MEMORY: + case COPY_FIRMWARE: + case DOWNLOAD_FIRMWARE: + break; + default: + err("unknown command:%d", req->cmd); + ret = -1; + goto error_unlock; + } + + /* write requested */ + if (write) { + memcpy(&buf[8], req->data, req->data_len); + msg_len += req->data_len; + } + deb_xfer(">>> "); + debug_dump(buf, msg_len, deb_xfer); + + /* send req */ + ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len, + &act_len, AF9015_USB_TIMEOUT); + if (ret) + err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len); + else + if (act_len != msg_len) + ret = -1; /* all data is not send */ + if (ret) + goto error_unlock; + + /* no ack for those packets */ + if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB) + goto exit_unlock; + + /* receive ack and data if read req */ + msg_len = 1 + 1 + req->data_len; /* seq + status + data len */ + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, + &act_len, AF9015_USB_TIMEOUT); + if (ret) { + err("recv bulk message failed:%d", ret); + ret = -1; + goto error_unlock; + } + + deb_xfer("<<< "); + debug_dump(buf, act_len, deb_xfer); + + /* remote controller query status is 1 if remote code is not received */ + if (req->cmd == GET_IR_CODE && buf[1] == 1) { + buf[1] = 0; /* clear command "error" status */ + memset(&buf[2], 0, req->data_len); + buf[3] = 1; /* no remote code received mark */ + } + + /* check status */ + if (buf[1]) { + err("command failed:%d", buf[1]); + ret = -1; + goto error_unlock; + } + + /* read request, copy returned data to return buf */ + if (!write) + memcpy(req->data, &buf[2], req->data_len); + +error_unlock: +exit_unlock: + mutex_unlock(&af9015_usb_mutex); + + return ret; +} + +static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) +{ + return af9015_rw_udev(d->udev, req); +} + +static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val, + u8 len) +{ + struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len, + val}; + return af9015_ctrl_msg(d, &req); +} + +static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val) +{ + return af9015_write_regs(d, addr, &val, 1); +} + +static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val) +{ + struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, 1, val}; + return af9015_ctrl_msg(d, &req); +} + +static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, + u8 val) +{ + struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val}; + + if (addr == af9015_af9013_config[0].demod_address || + addr == af9015_af9013_config[1].demod_address) + req.addr_len = 3; + + return af9015_ctrl_msg(d, &req); +} + +static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, + u8 *val) +{ + struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val}; + + if (addr == af9015_af9013_config[0].demod_address || + addr == af9015_af9013_config[1].demod_address) + req.addr_len = 3; + + return af9015_ctrl_msg(d, &req); +} + +static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret = 0, i = 0; + u16 addr; + u8 mbox, addr_len; + struct req_t req; + +/* TODO: implement bus lock + +The bus lock is needed because there is two tuners both using same I2C-address. +Due to that the only way to select correct tuner is use demodulator I2C-gate. + +................................................ +. AF9015 includes integrated AF9013 demodulator. +. ____________ ____________ . ____________ +.| uC | | demod | . | tuner | +.|------------| |------------| . |------------| +.| AF9015 | | AF9013/5 | . | MXL5003 | +.| |--+----I2C-------|-----/ -----|-.-----I2C-------| | +.| | | | addr 0x38 | . | addr 0xc6 | +.|____________| | |____________| . |____________| +.................|.............................. + | ____________ ____________ + | | demod | | tuner | + | |------------| |------------| + | | AF9013 | | MXL5003 | + +----I2C-------|-----/ -----|-------I2C-------| | + | addr 0x3a | | addr 0xc6 | + |____________| |____________| +*/ + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (msg[i].addr == af9015_af9013_config[0].demod_address || + msg[i].addr == af9015_af9013_config[1].demod_address) { + addr = msg[i].buf[0] << 8; + addr += msg[i].buf[1]; + mbox = msg[i].buf[2]; + addr_len = 3; + } else { + addr = msg[i].buf[0]; + addr_len = 1; + mbox = 0; + } + + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + if (msg[i].addr == + af9015_af9013_config[0].demod_address) + req.cmd = READ_MEMORY; + else + req.cmd = READ_I2C; + req.i2c_addr = msg[i].addr; + req.addr = addr; + req.mbox = mbox; + req.addr_len = addr_len; + req.data_len = msg[i+1].len; + req.data = &msg[i+1].buf[0]; + ret = af9015_ctrl_msg(d, &req); + i += 2; + } else { + if (msg[i].addr == + af9015_af9013_config[0].demod_address) + req.cmd = WRITE_MEMORY; + else + req.cmd = WRITE_I2C; + req.i2c_addr = msg[i].addr; + req.addr = addr; + req.mbox = mbox; + req.addr_len = addr_len; + req.data_len = msg[i].len-addr_len; + req.data = &msg[i].buf[addr_len]; + ret = af9015_ctrl_msg(d, &req); + i += 1; + } + if (ret) + goto error; + + } + ret = i; + +error: + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static u32 af9015_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm af9015_i2c_algo = { + .master_xfer = af9015_i2c_xfer, + .functionality = af9015_i2c_func, +}; + +static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op) +{ + int ret; + u8 val, mask = 0x01; + + ret = af9015_read_reg(d, addr, &val); + if (ret) + return ret; + + mask <<= bit; + if (op) { + /* set bit */ + val |= mask; + } else { + /* clear bit */ + mask ^= 0xff; + val &= mask; + } + + return af9015_write_reg(d, addr, val); +} + +static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) +{ + return af9015_do_reg_bit(d, addr, bit, 1); +} + +static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) +{ + return af9015_do_reg_bit(d, addr, bit, 0); +} + +static int af9015_init_endpoint(struct dvb_usb_device *d) +{ + int ret; + u16 frame_size; + u8 packet_size; + deb_info("%s: USB speed:%d\n", __func__, d->udev->speed); + +#define TS_PACKET_SIZE 188 + +#define TS_USB20_PACKET_COUNT 348 +#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT) + +#define TS_USB11_PACKET_COUNT 21 +#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT) + +#define TS_USB20_MAX_PACKET_SIZE 512 +#define TS_USB11_MAX_PACKET_SIZE 64 + + if (d->udev->speed == USB_SPEED_FULL) { + frame_size = TS_USB11_FRAME_SIZE/4; + packet_size = TS_USB11_MAX_PACKET_SIZE/4; + } else { + frame_size = TS_USB20_FRAME_SIZE/4; + packet_size = TS_USB20_MAX_PACKET_SIZE/4; + } + + ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */ + if (ret) + goto error; + ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */ + if (ret) + goto error; + ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */ + if (ret) + goto error; + ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */ + if (ret) + goto error; + ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */ + if (ret) + goto error; + if (af9015_config.dual_mode) { + ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */ + if (ret) + goto error; + } + ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */ + if (ret) + goto error; + if (af9015_config.dual_mode) { + ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */ + if (ret) + goto error; + } + /* EP4 xfer length */ + ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff); + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd89, frame_size >> 8); + if (ret) + goto error; + /* EP5 xfer length */ + ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff); + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8); + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */ + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */ + if (ret) + goto error; + ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */ + if (ret) + goto error; + if (af9015_config.dual_mode) { + ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */ + if (ret) + goto error; + } + + /* enable / disable mp2if2 */ + if (af9015_config.dual_mode) + ret = af9015_set_reg_bit(d, 0xd50b, 0); + else + ret = af9015_clear_reg_bit(d, 0xd50b, 0); +error: + if (ret) + err("endpoint init failed:%d", ret); + return ret; +} + +static int af9015_copy_firmware(struct dvb_usb_device *d) +{ + int ret; + u8 fw_params[4]; + u8 val, i; + struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params), + fw_params }; + deb_info("%s:\n", __func__); + + fw_params[0] = af9015_config.firmware_size >> 8; + fw_params[1] = af9015_config.firmware_size & 0xff; + fw_params[2] = af9015_config.firmware_checksum >> 8; + fw_params[3] = af9015_config.firmware_checksum & 0xff; + + /* wait 2nd demodulator ready */ + msleep(100); + + ret = af9015_read_reg_i2c(d, 0x3a, 0x98be, &val); + if (ret) + goto error; + else + deb_info("%s: firmware status:%02x\n", __func__, val); + + if (val == 0x0c) /* fw is running, no need for download */ + goto exit; + + /* set I2C master clock to fast (to speed up firmware copy) */ + ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */ + if (ret) + goto error; + + msleep(50); + + /* copy firmware */ + ret = af9015_ctrl_msg(d, &req); + if (ret) + err("firmware copy cmd failed:%d", ret); + deb_info("%s: firmware copy done\n", __func__); + + /* set I2C master clock back to normal */ + ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */ + if (ret) + goto error; + + /* request boot firmware */ + ret = af9015_write_reg_i2c(d, af9015_af9013_config[1].demod_address, + 0xe205, 1); + deb_info("%s: firmware boot cmd status:%d\n", __func__, ret); + if (ret) + goto error; + + for (i = 0; i < 15; i++) { + msleep(100); + + /* check firmware status */ + ret = af9015_read_reg_i2c(d, + af9015_af9013_config[1].demod_address, 0x98be, &val); + deb_info("%s: firmware status cmd status:%d fw status:%02x\n", + __func__, ret, val); + if (ret) + goto error; + + if (val == 0x0c || val == 0x04) /* success or fail */ + break; + } + + if (val == 0x04) { + err("firmware did not run"); + ret = -1; + } else if (val != 0x0c) { + err("firmware boot timeout"); + ret = -1; + } + +error: +exit: + return ret; +} + +/* dump eeprom */ +static int af9015_eeprom_dump(struct dvb_usb_device *d) +{ + char buf[52], buf2[4]; + u8 reg, val; + + for (reg = 0; ; reg++) { + if (reg % 16 == 0) { + if (reg) + deb_info("%s\n", buf); + sprintf(buf, "%02x: ", reg); + } + if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0) + sprintf(buf2, "%02x ", val); + else + strcpy(buf2, "-- "); + strcat(buf, buf2); + if (reg == 0xff) + break; + } + deb_info("%s\n", buf); + return 0; +} + +int af9015_download_ir_table(struct dvb_usb_device *d) +{ + int i, packets = 0, ret; + u16 addr = 0x9a56; /* ir-table start address */ + struct req_t req = {WRITE_MEMORY, 0, 0, 0, 0, 1, NULL}; + u8 *data = NULL; + deb_info("%s:\n", __func__); + + data = af9015_config.ir_table; + packets = af9015_config.ir_table_size; + + /* no remote */ + if (!packets) + goto exit; + + /* load remote ir-table */ + for (i = 0; i < packets; i++) { + req.addr = addr + i; + req.data = &data[i]; + ret = af9015_ctrl_msg(d, &req); + if (ret) { + err("ir-table download failed at packet %d with " \ + "code %d", i, ret); + return ret; + } + } + +exit: + return 0; +} + +static int af9015_init(struct dvb_usb_device *d) +{ + int ret; + deb_info("%s:\n", __func__); + + ret = af9015_init_endpoint(d); + if (ret) + goto error; + + ret = af9015_download_ir_table(d); + if (ret) + goto error; + +error: + return ret; +} + +static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + deb_info("%s: onoff:%d\n", __func__, onoff); + + if (onoff) + ret = af9015_set_reg_bit(adap->dev, 0xd503, 0); + else + ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0); + + return ret; +} + +static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, + int onoff) +{ + int ret; + u8 idx; + + deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n", + __func__, index, pid, onoff); + + ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff)); + if (ret) + goto error; + + ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8)); + if (ret) + goto error; + + idx = ((index & 0x1f) | (1 << 5)); + ret = af9015_write_reg(adap->dev, 0xd504, idx); + +error: + return ret; +} + +static int af9015_download_firmware(struct usb_device *udev, + const struct firmware *fw) +{ + int i, len, packets, remainder, ret; + struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL}; + u16 addr = 0x5100; /* firmware start address */ + u16 checksum = 0; + + deb_info("%s:\n", __func__); + + /* calc checksum */ + for (i = 0; i < fw->size; i++) + checksum += fw->data[i]; + + af9015_config.firmware_size = fw->size; + af9015_config.firmware_checksum = checksum; + + #define FW_PACKET_MAX_DATA 55 + + packets = fw->size / FW_PACKET_MAX_DATA; + remainder = fw->size % FW_PACKET_MAX_DATA; + len = FW_PACKET_MAX_DATA; + for (i = 0; i <= packets; i++) { + if (i == packets) /* set size of the last packet */ + len = remainder; + + req.data_len = len; + req.data = (fw->data + i * FW_PACKET_MAX_DATA); + req.addr = addr; + addr += FW_PACKET_MAX_DATA; + + ret = af9015_rw_udev(udev, &req); + if (ret) { + err("firmware download failed at packet %d with " \ + "code %d", i, ret); + goto error; + } + } + #undef FW_PACKET_MAX_DATA + + /* firmware loaded, request boot */ + req.cmd = BOOT; + ret = af9015_rw_udev(udev, &req); + if (ret) { + err("firmware boot failed:%d", ret); + goto error; + } + + /* firmware is running, reconnect device in the usb bus */ + req.cmd = RECONNECT_USB; + ret = af9015_rw_udev(udev, &req); + if (ret) + err("reconnect failed: %d", ret); + +error: + return ret; +} + +static int af9015_read_config(struct usb_device *udev) +{ + int ret; + u8 val, i, offset = 0; + struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; + char manufacturer[10]; + + /* IR remote controller */ + req.addr = AF9015_EEPROM_IR_MODE; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + deb_info("%s: IR mode:%d\n", __func__, val); + for (i = 0; i < af9015_properties_count; i++) { + if (val == AF9015_IR_MODE_DISABLED || val == 0x04) { + af9015_properties[i].rc_key_map = NULL; + af9015_properties[i].rc_key_map_size = 0; + } else if (dvb_usb_af9015_remote) { + /* load remote defined as module param */ + switch (dvb_usb_af9015_remote) { + case AF9015_REMOTE_A_LINK_DTU_M: + af9015_properties[i].rc_key_map = + af9015_rc_keys_a_link; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_a_link); + af9015_config.ir_table = af9015_ir_table_a_link; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_a_link); + break; + case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3: + af9015_properties[i].rc_key_map = + af9015_rc_keys_msi; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_msi); + af9015_config.ir_table = af9015_ir_table_msi; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_msi); + break; + case AF9015_REMOTE_MYGICTV_U718: + af9015_properties[i].rc_key_map = + af9015_rc_keys_mygictv; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_mygictv); + af9015_config.ir_table = + af9015_ir_table_mygictv; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_mygictv); + break; + } + } else { + switch (udev->descriptor.idVendor) { + case cpu_to_le16(USB_VID_LEADTEK): + af9015_properties[i].rc_key_map = + af9015_rc_keys_leadtek; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_leadtek); + af9015_config.ir_table = + af9015_ir_table_leadtek; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_leadtek); + break; + case cpu_to_le16(USB_VID_VISIONPLUS): + if (udev->descriptor.idProduct == + cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) { + af9015_properties[i].rc_key_map = + af9015_rc_keys_twinhan; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_twinhan); + af9015_config.ir_table = + af9015_ir_table_twinhan; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_twinhan); + } + break; + case cpu_to_le16(USB_VID_KWORLD_2): + /* TODO: use correct rc keys */ + af9015_properties[i].rc_key_map = + af9015_rc_keys_twinhan; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_twinhan); + af9015_config.ir_table = af9015_ir_table_kworld; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_kworld); + break; + /* Check USB manufacturer and product strings and try + to determine correct remote in case of chip vendor + reference IDs are used. */ + case cpu_to_le16(USB_VID_AFATECH): + memset(manufacturer, 0, sizeof(manufacturer)); + usb_string(udev, udev->descriptor.iManufacturer, + manufacturer, sizeof(manufacturer)); + if (!strcmp("Geniatech", manufacturer)) { + /* iManufacturer 1 Geniatech + iProduct 2 AF9015 */ + af9015_properties[i].rc_key_map = + af9015_rc_keys_mygictv; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_mygictv); + af9015_config.ir_table = + af9015_ir_table_mygictv; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_mygictv); + } else if (!strcmp("MSI", manufacturer)) { + /* iManufacturer 1 MSI + iProduct 2 MSI K-VOX */ + af9015_properties[i].rc_key_map = + af9015_rc_keys_msi; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_msi); + af9015_config.ir_table = + af9015_ir_table_msi; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_msi); + } + break; + } + } + } + + /* TS mode - one or two receivers */ + req.addr = AF9015_EEPROM_TS_MODE; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_config.dual_mode = val; + deb_info("%s: TS mode:%d\n", __func__, af9015_config.dual_mode); + /* disable dual mode by default because it is buggy */ + if (!dvb_usb_af9015_dual_mode) + af9015_config.dual_mode = 0; + + /* set buffer size according to USB port speed */ + for (i = 0; i < af9015_properties_count; i++) { + /* USB1.1 set smaller buffersize and disable 2nd adapter */ + if (udev->speed == USB_SPEED_FULL) { + af9015_properties[i].adapter->stream.u.bulk.buffersize = + TS_USB11_MAX_PACKET_SIZE; + /* disable 2nd adapter because we don't have + PID-filters */ + af9015_config.dual_mode = 0; + } else { + af9015_properties[i].adapter->stream.u.bulk.buffersize = + TS_USB20_MAX_PACKET_SIZE; + } + } + + if (af9015_config.dual_mode) { + /* read 2nd demodulator I2C address */ + req.addr = AF9015_EEPROM_DEMOD2_I2C; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_af9013_config[1].demod_address = val; + + /* enable 2nd adapter */ + for (i = 0; i < af9015_properties_count; i++) + af9015_properties[i].num_adapters = 2; + + } else { + /* disable 2nd adapter */ + for (i = 0; i < af9015_properties_count; i++) + af9015_properties[i].num_adapters = 1; + } + + for (i = 0; i < af9015_properties[0].num_adapters; i++) { + if (i == 1) + offset = AF9015_EEPROM_OFFSET; + /* xtal */ + req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + switch (val) { + case 0: + af9015_af9013_config[i].adc_clock = 28800; + break; + case 1: + af9015_af9013_config[i].adc_clock = 20480; + break; + case 2: + af9015_af9013_config[i].adc_clock = 28000; + break; + case 3: + af9015_af9013_config[i].adc_clock = 25000; + break; + }; + deb_info("%s: [%d] xtal:%d set adc_clock:%d\n", __func__, i, + val, af9015_af9013_config[i].adc_clock); + + /* tuner IF */ + req.addr = AF9015_EEPROM_IF1H + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_af9013_config[i].tuner_if = val << 8; + req.addr = AF9015_EEPROM_IF1L + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_af9013_config[i].tuner_if += val; + deb_info("%s: [%d] IF1:%d\n", __func__, i, + af9015_af9013_config[0].tuner_if); + + /* MT2060 IF1 */ + req.addr = AF9015_EEPROM_MT2060_IF1H + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_config.mt2060_if1[i] = val << 8; + req.addr = AF9015_EEPROM_MT2060_IF1L + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_config.mt2060_if1[i] += val; + deb_info("%s: [%d] MT2060 IF1:%d\n", __func__, i, + af9015_config.mt2060_if1[i]); + + /* tuner */ + req.addr = AF9015_EEPROM_TUNER_ID1 + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + switch (val) { + case AF9013_TUNER_ENV77H11D5: + case AF9013_TUNER_MT2060: + case AF9013_TUNER_MC44S803: + case AF9013_TUNER_QT1010: + case AF9013_TUNER_UNKNOWN: + case AF9013_TUNER_MT2060_2: + case AF9013_TUNER_TDA18271: + case AF9013_TUNER_QT1010A: + af9015_af9013_config[i].rf_spec_inv = 1; + break; + case AF9013_TUNER_MXL5003D: + case AF9013_TUNER_MXL5005D: + case AF9013_TUNER_MXL5005R: + af9015_af9013_config[i].rf_spec_inv = 0; + break; + default: + warn("tuner id:%d not supported, please report!", val); + return -ENODEV; + }; + + af9015_af9013_config[i].tuner = val; + deb_info("%s: [%d] tuner id:%d\n", __func__, i, val); + } + +error: + if (ret) + err("eeprom read failed:%d", ret); + + return ret; +} + +static int af9015_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int ret; + u8 reply; + struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply}; + + ret = af9015_rw_udev(udev, &req); + if (ret) + return ret; + + deb_info("%s: reply:%02x\n", __func__, reply); + if (reply == 0x02) + *cold = 0; + else + *cold = 1; + + return ret; +} + +static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 buf[8]; + struct req_t req = {GET_IR_CODE, 0, 0, 0, 0, sizeof(buf), buf}; + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + int i, ret; + + memset(buf, 0, sizeof(buf)); + + ret = af9015_ctrl_msg(d, &req); + if (ret) + return ret; + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + for (i = 0; i < d->props.rc_key_map_size; i++) { + if (!buf[1] && keymap[i].custom == buf[0] && + keymap[i].data == buf[2]) { + *event = keymap[i].event; + *state = REMOTE_KEY_PRESSED; + break; + } + } + if (!buf[1]) + deb_rc("%s: %02x %02x %02x %02x %02x %02x %02x %02x\n", + __func__, buf[0], buf[1], buf[2], buf[3], buf[4], + buf[5], buf[6], buf[7]); + + return 0; +} + +/* init 2nd I2C adapter */ +int af9015_i2c_init(struct dvb_usb_device *d) +{ + int ret; + struct af9015_state *state = d->priv; + deb_info("%s:\n", __func__); + + strncpy(state->i2c_adap.name, d->desc->name, + sizeof(state->i2c_adap.name)); +#ifdef I2C_ADAP_CLASS_TV_DIGITAL + state->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL, +#else + state->i2c_adap.class = I2C_CLASS_TV_DIGITAL, +#endif + state->i2c_adap.algo = d->props.i2c_algo; + state->i2c_adap.algo_data = NULL; + state->i2c_adap.dev.parent = &d->udev->dev; + + i2c_set_adapdata(&state->i2c_adap, d); + + ret = i2c_add_adapter(&state->i2c_adap); + if (ret < 0) + err("could not add i2c adapter"); + + return ret; +} + +static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct af9015_state *state = adap->dev->priv; + struct i2c_adapter *i2c_adap; + + if (adap->id == 0) { + /* select I2C adapter */ + i2c_adap = &adap->dev->i2c_adap; + + deb_info("%s: init I2C\n", __func__); + ret = af9015_i2c_init(adap->dev); + + /* dump eeprom (debug) */ + ret = af9015_eeprom_dump(adap->dev); + if (ret) + return ret; + } else { + /* select I2C adapter */ + i2c_adap = &state->i2c_adap; + + /* copy firmware to 2nd demodulator */ + if (af9015_config.dual_mode) { + ret = af9015_copy_firmware(adap->dev); + if (ret) { + err("firmware copy to 2nd frontend " \ + "failed, will disable it"); + af9015_config.dual_mode = 0; + return -ENODEV; + } + } else { + return -ENODEV; + } + } + + /* attach demodulator */ + adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id], + i2c_adap); + + return adap->fe == NULL ? -ENODEV : 0; +} + +static struct mt2060_config af9015_mt2060_config = { + .i2c_address = 0xc0, + .clock_out = 0, +}; + +static struct qt1010_config af9015_qt1010_config = { + .i2c_address = 0xc4, +}; + +static struct tda18271_config af9015_tda18271_config = { + .gate = TDA18271_GATE_DIGITAL, + .small_i2c = 1, +}; + +static struct mxl5005s_config af9015_mxl5003_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_DEFAULT, + .rssi_enable = MXL_RSSI_DISABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static struct mxl5005s_config af9015_mxl5005_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_OFF, + .rssi_enable = MXL_RSSI_DISABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static int af9015_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct af9015_state *state = adap->dev->priv; + struct i2c_adapter *i2c_adap; + int ret; + deb_info("%s: \n", __func__); + + /* select I2C adapter */ + if (adap->id == 0) + i2c_adap = &adap->dev->i2c_adap; + else + i2c_adap = &state->i2c_adap; + + switch (af9015_af9013_config[adap->id].tuner) { + case AF9013_TUNER_MT2060: + case AF9013_TUNER_MT2060_2: + ret = dvb_attach(mt2060_attach, adap->fe, i2c_adap, + &af9015_mt2060_config, + af9015_config.mt2060_if1[adap->id]) + == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_QT1010: + case AF9013_TUNER_QT1010A: + ret = dvb_attach(qt1010_attach, adap->fe, i2c_adap, + &af9015_qt1010_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_TDA18271: + ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap, + &af9015_tda18271_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MXL5003D: + ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap, + &af9015_mxl5003_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MXL5005D: + case AF9013_TUNER_MXL5005R: + ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap, + &af9015_mxl5005_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_ENV77H11D5: + ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, i2c_adap, + DVB_PLL_TDA665X) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MC44S803: +#if 0 /* keep */ + ret = dvb_attach(mc44s80x_attach, adap->fe, i2c_adap) + == NULL ? -ENODEV : 0; +#else + ret = -ENODEV; + info("Freescale MC44S803 tuner found but no driver for that" \ + "tuner. Look at the Linuxtv.org for tuner driver" \ + "status."); +#endif + break; + case AF9013_TUNER_UNKNOWN: + default: + ret = -ENODEV; + err("Unknown tuner id:%d", + af9015_af9013_config[adap->id].tuner); + } + return ret; +} + +static struct usb_device_id af9015_usb_table[] = { +/* 0 */{USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)}, + {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)}, + {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)}, + {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)}, +/* 5 */{USB_DEVICE(USB_VID_VISIONPLUS, + USB_PID_TINYTWIN)}, + {USB_DEVICE(USB_VID_VISIONPLUS, + USB_PID_AZUREWAVE_AD_TU700)}, + {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)}, + {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)}, +/* 10 */{USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)}, + {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, + {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, + {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)}, + {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)}, + {0}, +}; +MODULE_DEVICE_TABLE(usb, af9015_usb_table); + +static struct dvb_usb_device_properties af9015_properties[] = { + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9015_download_firmware, + .firmware = "dvb-usb-af9015.fw", + + .size_of_priv = sizeof(struct af9015_state), \ + + .num_adapters = 2, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, + + .frontend_attach = + af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + }, + { + .frontend_attach = + af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + }, + } + }, + + .identify_state = af9015_identify_state, + + .rc_query = af9015_rc_query, + .rc_interval = 150, + + .i2c_algo = &af9015_i2c_algo, + + .num_device_descs = 9, + .devices = { + { + .name = "Afatech AF9015 DVB-T USB2.0 stick", + .cold_ids = {&af9015_usb_table[0], + &af9015_usb_table[1], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "Leadtek WinFast DTV Dongle Gold", + .cold_ids = {&af9015_usb_table[2], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "Pinnacle PCTV 71e", + .cold_ids = {&af9015_usb_table[3], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "KWorld PlusTV Dual DVB-T Stick " \ + "(DVB-T 399U)", + .cold_ids = {&af9015_usb_table[4], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "DigitalNow TinyTwin DVB-T Receiver", + .cold_ids = {&af9015_usb_table[5], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "TwinHan AzureWave AD-TU700(704J)", + .cold_ids = {&af9015_usb_table[6], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "TerraTec Cinergy T USB XE", + .cold_ids = {&af9015_usb_table[7], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "KWorld PlusTV Dual DVB-T PCI " \ + "(DVB-T PC160-2T)", + .cold_ids = {&af9015_usb_table[8], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "AVerMedia AVerTV DVB-T Volar X", + .cold_ids = {&af9015_usb_table[9], NULL}, + .warm_ids = {NULL}, + }, + } + }, { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9015_download_firmware, + .firmware = "dvb-usb-af9015.fw", + + .size_of_priv = sizeof(struct af9015_state), \ + + .num_adapters = 2, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, + + .frontend_attach = + af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + }, + { + .frontend_attach = + af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + }, + } + }, + + .identify_state = af9015_identify_state, + + .rc_query = af9015_rc_query, + .rc_interval = 150, + + .i2c_algo = &af9015_i2c_algo, + + .num_device_descs = 5, + .devices = { + { + .name = "Xtensions XD-380", + .cold_ids = {&af9015_usb_table[10], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "MSI DIGIVOX Duo", + .cold_ids = {&af9015_usb_table[11], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "Fujitsu-Siemens Slim Mobile USB DVB-T", + .cold_ids = {&af9015_usb_table[12], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "Telestar Starstick 2", + .cold_ids = {&af9015_usb_table[13], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "AVerMedia A309", + .cold_ids = {&af9015_usb_table[14], NULL}, + .warm_ids = {NULL}, + }, + } + } +}; +#undef AF9015_DEFAULT_PROPERTIES + +static int af9015_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret = 0; + struct dvb_usb_device *d = NULL; + struct usb_device *udev = interface_to_usbdev(intf); + u8 i; + + deb_info("%s: interface:%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + /* interface 0 is used by DVB-T receiver and + interface 1 is for remote controller (HID) */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + ret = af9015_read_config(udev); + if (ret) + return ret; + + for (i = 0; i < af9015_properties_count; i++) { + ret = dvb_usb_device_init(intf, &af9015_properties[i], + THIS_MODULE, &d, adapter_nr); + if (!ret) + break; + if (ret != -ENODEV) + return ret; + } + if (ret) + return ret; + + if (d) + ret = af9015_init(d); + } + + return ret; +} + +void af9015_i2c_exit(struct dvb_usb_device *d) +{ + struct af9015_state *state = d->priv; + deb_info("%s: \n", __func__); + + /* remove 2nd I2C adapter */ + if (d->state & DVB_USB_STATE_I2C) + i2c_del_adapter(&state->i2c_adap); +} + +static void af9015_usb_device_exit(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + deb_info("%s: \n", __func__); + + /* remove 2nd I2C adapter */ + if (d != NULL && d->desc != NULL) + af9015_i2c_exit(d); + + dvb_usb_device_exit(intf); +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver af9015_usb_driver = { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) + .owner = THIS_MODULE, +#endif + .name = "dvb_usb_af9015", + .probe = af9015_usb_probe, + .disconnect = af9015_usb_device_exit, + .id_table = af9015_usb_table, +}; + +/* module stuff */ +static int __init af9015_usb_module_init(void) +{ + int ret; + ret = usb_register(&af9015_usb_driver); + if (ret) + err("module init failed:%d", ret); + + return ret; +} + +static void __exit af9015_usb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&af9015_usb_driver); +} + +module_init(af9015_usb_module_init); +module_exit(af9015_usb_module_exit); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.h b/linux/drivers/media/dvb/dvb-usb/af9015.h new file mode 100644 index 000000000..882e8a4b3 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/af9015.h @@ -0,0 +1,524 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _DVB_USB_AF9015_H_ +#define _DVB_USB_AF9015_H_ + +#define DVB_USB_LOG_PREFIX "af9015" +#include "dvb-usb.h" + +extern int dvb_usb_af9015_debug; +#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args) +#define deb_rc(args...) dprintk(dvb_usb_af9015_debug, 0x02, args) +#define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args) +#define deb_reg(args...) dprintk(dvb_usb_af9015_debug, 0x08, args) +#define deb_i2c(args...) dprintk(dvb_usb_af9015_debug, 0x10, args) +#define deb_fw(args...) dprintk(dvb_usb_af9015_debug, 0x20, args) + +#define AF9015_I2C_EEPROM 0xa0 +#define AF9015_I2C_DEMOD 0x38 +#define AF9015_USB_TIMEOUT 2000 + +/* EEPROM locations */ +#define AF9015_EEPROM_IR_MODE 0x18 +#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34 +#define AF9015_EEPROM_TS_MODE 0x31 +#define AF9015_EEPROM_DEMOD2_I2C 0x32 + +#define AF9015_EEPROM_SAW_BW1 0x35 +#define AF9015_EEPROM_XTAL_TYPE1 0x36 +#define AF9015_EEPROM_SPEC_INV1 0x37 +#define AF9015_EEPROM_IF1L 0x38 +#define AF9015_EEPROM_IF1H 0x39 +#define AF9015_EEPROM_MT2060_IF1L 0x3a +#define AF9015_EEPROM_MT2060_IF1H 0x3b +#define AF9015_EEPROM_TUNER_ID1 0x3c + +#define AF9015_EEPROM_SAW_BW2 0x45 +#define AF9015_EEPROM_XTAL_TYPE2 0x46 +#define AF9015_EEPROM_SPEC_INV2 0x47 +#define AF9015_EEPROM_IF2L 0x48 +#define AF9015_EEPROM_IF2H 0x49 +#define AF9015_EEPROM_MT2060_IF2L 0x4a +#define AF9015_EEPROM_MT2060_IF2H 0x4b +#define AF9015_EEPROM_TUNER_ID2 0x4c + +#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1) + +#define AF9015_GPIO_ON (1 << 0) +#define AF9015_GPIO_EN (1 << 1) +#define AF9015_GPIO_O (1 << 2) +#define AF9015_GPIO_I (1 << 3) + +#define AF9015_GPIO_TUNER_ON (AF9015_GPIO_ON|AF9015_GPIO_EN) +#define AF9015_GPIO_TUNER_OFF (AF9015_GPIO_ON|AF9015_GPIO_EN|AF9015_GPIO_O) + +struct req_t { + u8 cmd; /* [0] */ + /* seq */ /* [1] */ + u8 i2c_addr; /* [2] */ + u16 addr; /* [3|4] */ + u8 mbox; /* [5] */ + u8 addr_len; /* [6] */ + u8 data_len; /* [7] */ + u8 *data; +}; + +enum af9015_cmd { + GET_CONFIG = 0x10, + DOWNLOAD_FIRMWARE = 0x11, + BOOT = 0x13, + READ_MEMORY = 0x20, + WRITE_MEMORY = 0x21, + READ_WRITE_I2C = 0x22, + COPY_FIRMWARE = 0x23, + RECONNECT_USB = 0x5a, + WRITE_VIRTUAL_MEMORY = 0x26, + GET_IR_CODE = 0x27, + READ_I2C, + WRITE_I2C, +}; + +enum af9015_ir_mode { + AF9015_IR_MODE_DISABLED = 0, + AF9015_IR_MODE_HID, + AF9015_IR_MODE_RLC, + AF9015_IR_MODE_RC6, +}; + +struct af9015_state { + struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */ +}; + +struct af9015_config { + u8 dual_mode:1; + u16 mt2060_if1[2]; + u16 firmware_size; + u16 firmware_checksum; + u8 *ir_table; + u16 ir_table_size; +}; + +enum af9015_remote { + AF9015_REMOTE_NONE = 0, + AF9015_REMOTE_A_LINK_DTU_M, + AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, + AF9015_REMOTE_MYGICTV_U718, +}; + +/* Leadtek WinFast DTV Dongle Gold */ +static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = { + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x28, KEY_ENTER }, + { 0x00, 0x4f, KEY_VOLUMEUP }, + { 0x00, 0x50, KEY_VOLUMEDOWN }, + { 0x00, 0x51, KEY_CHANNELDOWN }, + { 0x00, 0x52, KEY_CHANNELUP }, +}; + +static u8 af9015_ir_table_leadtek[] = { + 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00, + 0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00, + 0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00, + 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00, + 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00, + 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00, + 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00, + 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00, + 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00, + 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00, + 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00, + 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00, + 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00, + 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00, + 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00, + 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00, + 0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00, + 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00, + 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00, + 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00, + 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00, + 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00, + 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00, + 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00, + 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00, + 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00, + 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00, + 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00, + 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00, + 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00, + 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00, + 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00, + 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00, + 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, + 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00, + 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00, + 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00, + 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00, + 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00, + 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00, + 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00, + 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00, + 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00, + 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00, + 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00, +}; + +/* TwinHan AzureWave AD-TU700(704J) */ +static struct dvb_usb_rc_key af9015_rc_keys_twinhan[] = { + { 0x05, 0x3f, KEY_POWER }, + { 0x00, 0x19, KEY_FAVORITES }, /* Favorite List */ + { 0x00, 0x04, KEY_TEXT }, /* Teletext */ + { 0x00, 0x0e, KEY_POWER }, + { 0x00, 0x0e, KEY_INFO }, /* Preview */ + { 0x00, 0x08, KEY_EPG }, /* Info/EPG */ + { 0x00, 0x0f, KEY_LIST }, /* Record List */ + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x29, KEY_CANCEL }, /* Cancel */ + { 0x00, 0x4c, KEY_CLEAR }, /* Clear */ + { 0x00, 0x2a, KEY_BACK }, /* Back */ + { 0x00, 0x2b, KEY_TAB }, /* Tab */ + { 0x00, 0x52, KEY_UP }, /* up arrow */ + { 0x00, 0x51, KEY_DOWN }, /* down arrow */ + { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */ + { 0x00, 0x50, KEY_LEFT }, /* left arrow */ + { 0x00, 0x28, KEY_ENTER }, /* Enter / ok */ + { 0x02, 0x52, KEY_VOLUMEUP }, + { 0x02, 0x51, KEY_VOLUMEDOWN }, + { 0x00, 0x4e, KEY_CHANNELDOWN }, + { 0x00, 0x4b, KEY_CHANNELUP }, + { 0x00, 0x4a, KEY_RECORD }, + { 0x01, 0x11, KEY_PLAY }, + { 0x00, 0x17, KEY_PAUSE }, + { 0x00, 0x0c, KEY_REWIND }, /* FR << */ + { 0x00, 0x11, KEY_FASTFORWARD }, /* FF >> */ + { 0x01, 0x15, KEY_PREVIOUS }, /* Replay */ + { 0x01, 0x0e, KEY_NEXT }, /* Skip */ + { 0x00, 0x13, KEY_CAMERA }, /* Capture */ + { 0x01, 0x0f, KEY_LANGUAGE }, /* SAP */ + { 0x01, 0x13, KEY_TV2 }, /* PIP */ + { 0x00, 0x1d, KEY_ZOOM }, /* Full Screen */ + { 0x01, 0x17, KEY_SUBTITLE }, /* Subtitle / CC */ + { 0x00, 0x10, KEY_MUTE }, + { 0x01, 0x19, KEY_AUDIO }, /* L/R */ /* TODO better event */ + { 0x01, 0x16, KEY_SLEEP }, /* Hibernate */ + { 0x01, 0x16, KEY_SWITCHVIDEOMODE }, + /* A/V */ /* TODO does not work */ + { 0x00, 0x06, KEY_AGAIN }, /* Recall */ + { 0x01, 0x16, KEY_KPPLUS }, /* Zoom+ */ /* TODO does not work */ + { 0x01, 0x16, KEY_KPMINUS }, /* Zoom- */ /* TODO does not work */ + { 0x02, 0x15, KEY_RED }, + { 0x02, 0x0a, KEY_GREEN }, + { 0x02, 0x1c, KEY_YELLOW }, + { 0x02, 0x05, KEY_BLUE }, +}; + +static u8 af9015_ir_table_twinhan[] = { + 0x00, 0xff, 0x16, 0xe9, 0x3f, 0x05, 0x00, + 0x00, 0xff, 0x07, 0xf8, 0x16, 0x01, 0x00, + 0x00, 0xff, 0x14, 0xeb, 0x11, 0x01, 0x00, + 0x00, 0xff, 0x1a, 0xe5, 0x4d, 0x00, 0x00, + 0x00, 0xff, 0x4c, 0xb3, 0x17, 0x00, 0x00, + 0x00, 0xff, 0x12, 0xed, 0x11, 0x00, 0x00, + 0x00, 0xff, 0x40, 0xbf, 0x0c, 0x00, 0x00, + 0x00, 0xff, 0x11, 0xee, 0x4a, 0x00, 0x00, + 0x00, 0xff, 0x54, 0xab, 0x13, 0x00, 0x00, + 0x00, 0xff, 0x41, 0xbe, 0x15, 0x01, 0x00, + 0x00, 0xff, 0x42, 0xbd, 0x0e, 0x01, 0x00, + 0x00, 0xff, 0x43, 0xbc, 0x17, 0x01, 0x00, + 0x00, 0xff, 0x50, 0xaf, 0x0f, 0x01, 0x00, + 0x00, 0xff, 0x4d, 0xb2, 0x1d, 0x00, 0x00, + 0x00, 0xff, 0x47, 0xb8, 0x13, 0x01, 0x00, + 0x00, 0xff, 0x05, 0xfa, 0x4b, 0x00, 0x00, + 0x00, 0xff, 0x02, 0xfd, 0x4e, 0x00, 0x00, + 0x00, 0xff, 0x0e, 0xf1, 0x06, 0x00, 0x00, + 0x00, 0xff, 0x1e, 0xe1, 0x52, 0x02, 0x00, + 0x00, 0xff, 0x0a, 0xf5, 0x51, 0x02, 0x00, + 0x00, 0xff, 0x10, 0xef, 0x10, 0x00, 0x00, + 0x00, 0xff, 0x49, 0xb6, 0x19, 0x01, 0x00, + 0x00, 0xff, 0x15, 0xea, 0x27, 0x00, 0x00, + 0x00, 0xff, 0x03, 0xfc, 0x1e, 0x00, 0x00, + 0x00, 0xff, 0x01, 0xfe, 0x1f, 0x00, 0x00, + 0x00, 0xff, 0x06, 0xf9, 0x20, 0x00, 0x00, + 0x00, 0xff, 0x09, 0xf6, 0x21, 0x00, 0x00, + 0x00, 0xff, 0x1d, 0xe2, 0x22, 0x00, 0x00, + 0x00, 0xff, 0x1f, 0xe0, 0x23, 0x00, 0x00, + 0x00, 0xff, 0x0d, 0xf2, 0x24, 0x00, 0x00, + 0x00, 0xff, 0x19, 0xe6, 0x25, 0x00, 0x00, + 0x00, 0xff, 0x1b, 0xe4, 0x26, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x2b, 0x00, 0x00, + 0x00, 0xff, 0x4a, 0xb5, 0x4c, 0x00, 0x00, + 0x00, 0xff, 0x4b, 0xb4, 0x52, 0x00, 0x00, + 0x00, 0xff, 0x51, 0xae, 0x51, 0x00, 0x00, + 0x00, 0xff, 0x52, 0xad, 0x4f, 0x00, 0x00, + 0x00, 0xff, 0x4e, 0xb1, 0x50, 0x00, 0x00, + 0x00, 0xff, 0x0c, 0xf3, 0x29, 0x00, 0x00, + 0x00, 0xff, 0x4f, 0xb0, 0x28, 0x00, 0x00, + 0x00, 0xff, 0x13, 0xec, 0x2a, 0x00, 0x00, + 0x00, 0xff, 0x17, 0xe8, 0x19, 0x00, 0x00, + 0x00, 0xff, 0x04, 0xfb, 0x0f, 0x00, 0x00, + 0x00, 0xff, 0x48, 0xb7, 0x0e, 0x00, 0x00, + 0x00, 0xff, 0x0f, 0xf0, 0x04, 0x00, 0x00, + 0x00, 0xff, 0x1c, 0xe3, 0x08, 0x00, 0x00, + 0x00, 0xff, 0x18, 0xe7, 0x15, 0x02, 0x00, + 0x00, 0xff, 0x53, 0xac, 0x0a, 0x02, 0x00, + 0x00, 0xff, 0x5e, 0xa1, 0x1c, 0x02, 0x00, + 0x00, 0xff, 0x5f, 0xa0, 0x05, 0x02, 0x00, +}; + +/* A-Link DTU(m) */ +static struct dvb_usb_rc_key af9015_rc_keys_a_link[] = { + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x2e, KEY_CHANNELUP }, + { 0x00, 0x2d, KEY_CHANNELDOWN }, + { 0x04, 0x28, KEY_ZOOM }, + { 0x00, 0x41, KEY_MUTE }, + { 0x00, 0x42, KEY_VOLUMEDOWN }, + { 0x00, 0x43, KEY_VOLUMEUP }, + { 0x00, 0x44, KEY_GOTO }, /* jump */ + { 0x05, 0x45, KEY_POWER }, +}; + +static u8 af9015_ir_table_a_link[] = { + 0x08, 0xf7, 0x12, 0xed, 0x45, 0x05, 0x00, /* power */ + 0x08, 0xf7, 0x1a, 0xe5, 0x41, 0x00, 0x00, /* mute */ + 0x08, 0xf7, 0x01, 0xfe, 0x1e, 0x00, 0x00, /* 1 */ + 0x08, 0xf7, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */ + 0x08, 0xf7, 0x03, 0xfc, 0x24, 0x00, 0x00, /* 7 */ + 0x08, 0xf7, 0x05, 0xfa, 0x28, 0x04, 0x00, /* zoom */ + 0x08, 0xf7, 0x00, 0xff, 0x43, 0x00, 0x00, /* volume up */ + 0x08, 0xf7, 0x16, 0xe9, 0x42, 0x00, 0x00, /* volume down */ + 0x08, 0xf7, 0x0f, 0xf0, 0x1f, 0x00, 0x00, /* 2 */ + 0x08, 0xf7, 0x0d, 0xf2, 0x22, 0x00, 0x00, /* 5 */ + 0x08, 0xf7, 0x1b, 0xe4, 0x25, 0x00, 0x00, /* 8 */ + 0x08, 0xf7, 0x06, 0xf9, 0x27, 0x00, 0x00, /* 0 */ + 0x08, 0xf7, 0x14, 0xeb, 0x2e, 0x00, 0x00, /* channel up */ + 0x08, 0xf7, 0x1d, 0xe2, 0x2d, 0x00, 0x00, /* channel down */ + 0x08, 0xf7, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */ + 0x08, 0xf7, 0x18, 0xe7, 0x23, 0x00, 0x00, /* 6 */ + 0x08, 0xf7, 0x04, 0xfb, 0x26, 0x00, 0x00, /* 9 */ + 0x08, 0xf7, 0x07, 0xf8, 0x44, 0x00, 0x00, /* jump */ +}; + +/* MSI DIGIVOX mini II V3.0 */ +static struct dvb_usb_rc_key af9015_rc_keys_msi[] = { + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x03, 0x0f, KEY_CHANNELUP }, + { 0x03, 0x0e, KEY_CHANNELDOWN }, + { 0x00, 0x42, KEY_VOLUMEDOWN }, + { 0x00, 0x43, KEY_VOLUMEUP }, + { 0x05, 0x45, KEY_POWER }, + { 0x00, 0x52, KEY_UP }, /* up */ + { 0x00, 0x51, KEY_DOWN }, /* down */ + { 0x00, 0x28, KEY_ENTER }, +}; + +static u8 af9015_ir_table_msi[] = { + 0x03, 0xfc, 0x17, 0xe8, 0x45, 0x05, 0x00, /* power */ + 0x03, 0xfc, 0x0d, 0xf2, 0x51, 0x00, 0x00, /* down */ + 0x03, 0xfc, 0x03, 0xfc, 0x52, 0x00, 0x00, /* up */ + 0x03, 0xfc, 0x1a, 0xe5, 0x1e, 0x00, 0x00, /* 1 */ + 0x03, 0xfc, 0x02, 0xfd, 0x1f, 0x00, 0x00, /* 2 */ + 0x03, 0xfc, 0x04, 0xfb, 0x20, 0x00, 0x00, /* 3 */ + 0x03, 0xfc, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */ + 0x03, 0xfc, 0x08, 0xf7, 0x22, 0x00, 0x00, /* 5 */ + 0x03, 0xfc, 0x1d, 0xe2, 0x23, 0x00, 0x00, /* 6 */ + 0x03, 0xfc, 0x11, 0xee, 0x24, 0x00, 0x00, /* 7 */ + 0x03, 0xfc, 0x0b, 0xf4, 0x25, 0x00, 0x00, /* 8 */ + 0x03, 0xfc, 0x10, 0xef, 0x26, 0x00, 0x00, /* 9 */ + 0x03, 0xfc, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */ + 0x03, 0xfc, 0x14, 0xeb, 0x43, 0x00, 0x00, /* volume up */ + 0x03, 0xfc, 0x1f, 0xe0, 0x42, 0x00, 0x00, /* volume down */ + 0x03, 0xfc, 0x15, 0xea, 0x0f, 0x03, 0x00, /* channel up */ + 0x03, 0xfc, 0x05, 0xfa, 0x0e, 0x03, 0x00, /* channel down */ + 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* enter */ +}; + +/* MYGICTV U718 */ +static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = { + { 0x00, 0x3d, KEY_SWITCHVIDEOMODE }, + /* TV / AV */ + { 0x05, 0x45, KEY_POWER }, + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x41, KEY_MUTE }, + { 0x00, 0x2a, KEY_ESC }, /* Esc */ + { 0x00, 0x2e, KEY_CHANNELUP }, + { 0x00, 0x2d, KEY_CHANNELDOWN }, + { 0x00, 0x42, KEY_VOLUMEDOWN }, + { 0x00, 0x43, KEY_VOLUMEUP }, + { 0x00, 0x52, KEY_UP }, /* up arrow */ + { 0x00, 0x51, KEY_DOWN }, /* down arrow */ + { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */ + { 0x00, 0x50, KEY_LEFT }, /* left arrow */ + { 0x00, 0x28, KEY_ENTER }, /* ok */ + { 0x01, 0x15, KEY_RECORD }, + { 0x03, 0x13, KEY_PLAY }, + { 0x01, 0x13, KEY_PAUSE }, + { 0x01, 0x16, KEY_STOP }, + { 0x03, 0x07, KEY_REWIND }, /* FR << */ + { 0x03, 0x09, KEY_FASTFORWARD }, /* FF >> */ + { 0x00, 0x3b, KEY_TIME }, /* TimeShift */ + { 0x00, 0x3e, KEY_CAMERA }, /* Snapshot */ + { 0x03, 0x16, KEY_CYCLEWINDOWS }, /* yellow, min / max */ + { 0x00, 0x00, KEY_ZOOM }, /* 'select' (?) */ + { 0x03, 0x16, KEY_SHUFFLE }, /* Shuffle */ + { 0x03, 0x45, KEY_POWER }, +}; + +static u8 af9015_ir_table_mygictv[] = { + 0x02, 0xbd, 0x0c, 0xf3, 0x3d, 0x00, 0x00, /* TV / AV */ + 0x02, 0xbd, 0x14, 0xeb, 0x45, 0x05, 0x00, /* power */ + 0x02, 0xbd, 0x00, 0xff, 0x1e, 0x00, 0x00, /* 1 */ + 0x02, 0xbd, 0x01, 0xfe, 0x1f, 0x00, 0x00, /* 2 */ + 0x02, 0xbd, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */ + 0x02, 0xbd, 0x03, 0xfc, 0x21, 0x00, 0x00, /* 4 */ + 0x02, 0xbd, 0x04, 0xfb, 0x22, 0x00, 0x00, /* 5 */ + 0x02, 0xbd, 0x05, 0xfa, 0x23, 0x00, 0x00, /* 6 */ + 0x02, 0xbd, 0x06, 0xf9, 0x24, 0x00, 0x00, /* 7 */ + 0x02, 0xbd, 0x07, 0xf8, 0x25, 0x00, 0x00, /* 8 */ + 0x02, 0xbd, 0x08, 0xf7, 0x26, 0x00, 0x00, /* 9 */ + 0x02, 0xbd, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */ + 0x02, 0xbd, 0x0a, 0xf5, 0x41, 0x00, 0x00, /* mute */ + 0x02, 0xbd, 0x1c, 0xe3, 0x2a, 0x00, 0x00, /* esc */ + 0x02, 0xbd, 0x1f, 0xe0, 0x43, 0x00, 0x00, /* volume up */ + 0x02, 0xbd, 0x12, 0xed, 0x52, 0x00, 0x00, /* up arrow */ + 0x02, 0xbd, 0x11, 0xee, 0x50, 0x00, 0x00, /* left arrow */ + 0x02, 0xbd, 0x15, 0xea, 0x28, 0x00, 0x00, /* ok */ + 0x02, 0xbd, 0x10, 0xef, 0x4f, 0x00, 0x00, /* right arrow */ + 0x02, 0xbd, 0x13, 0xec, 0x51, 0x00, 0x00, /* down arrow */ + 0x02, 0xbd, 0x0e, 0xf1, 0x42, 0x00, 0x00, /* volume down */ + 0x02, 0xbd, 0x19, 0xe6, 0x15, 0x01, 0x00, /* record */ + 0x02, 0xbd, 0x1e, 0xe1, 0x13, 0x03, 0x00, /* play */ + 0x02, 0xbd, 0x16, 0xe9, 0x16, 0x01, 0x00, /* stop */ + 0x02, 0xbd, 0x0b, 0xf4, 0x28, 0x04, 0x00, /* yellow, min / max */ + 0x02, 0xbd, 0x0f, 0xf0, 0x3b, 0x00, 0x00, /* time shift */ + 0x02, 0xbd, 0x18, 0xe7, 0x2e, 0x00, 0x00, /* channel up */ + 0x02, 0xbd, 0x1a, 0xe5, 0x2d, 0x00, 0x00, /* channel down */ + 0x02, 0xbd, 0x17, 0xe8, 0x3e, 0x00, 0x00, /* snapshot */ + 0x02, 0xbd, 0x40, 0xbf, 0x13, 0x01, 0x00, /* pause */ + 0x02, 0xbd, 0x41, 0xbe, 0x09, 0x03, 0x00, /* FF >> */ + 0x02, 0xbd, 0x42, 0xbd, 0x07, 0x03, 0x00, /* FR << */ + 0x02, 0xbd, 0x43, 0xbc, 0x00, 0x00, 0x00, /* 'select' (?) */ + 0x02, 0xbd, 0x44, 0xbb, 0x16, 0x03, 0x00, /* shuffle */ + 0x02, 0xbd, 0x45, 0xba, 0x45, 0x03, 0x00, /* power */ +}; + +/* KWorld PlusTV Dual DVB-T Stick (DVB-T 399U) */ +static u8 af9015_ir_table_kworld[] = { + 0x86, 0x6b, 0x0c, 0xf3, 0x2e, 0x07, 0x00, + 0x86, 0x6b, 0x16, 0xe9, 0x2d, 0x07, 0x00, + 0x86, 0x6b, 0x1d, 0xe2, 0x37, 0x07, 0x00, + 0x86, 0x6b, 0x00, 0xff, 0x1e, 0x07, 0x00, + 0x86, 0x6b, 0x01, 0xfe, 0x1f, 0x07, 0x00, + 0x86, 0x6b, 0x02, 0xfd, 0x20, 0x07, 0x00, + 0x86, 0x6b, 0x03, 0xfc, 0x21, 0x07, 0x00, + 0x86, 0x6b, 0x04, 0xfb, 0x22, 0x07, 0x00, + 0x86, 0x6b, 0x05, 0xfa, 0x23, 0x07, 0x00, + 0x86, 0x6b, 0x06, 0xf9, 0x24, 0x07, 0x00, + 0x86, 0x6b, 0x07, 0xf8, 0x25, 0x07, 0x00, + 0x86, 0x6b, 0x08, 0xf7, 0x26, 0x07, 0x00, + 0x86, 0x6b, 0x09, 0xf6, 0x4d, 0x07, 0x00, + 0x86, 0x6b, 0x0a, 0xf5, 0x4e, 0x07, 0x00, + 0x86, 0x6b, 0x14, 0xeb, 0x4f, 0x07, 0x00, + 0x86, 0x6b, 0x1e, 0xe1, 0x50, 0x07, 0x00, + 0x86, 0x6b, 0x17, 0xe8, 0x52, 0x07, 0x00, + 0x86, 0x6b, 0x1f, 0xe0, 0x51, 0x07, 0x00, + 0x86, 0x6b, 0x0e, 0xf1, 0x0b, 0x07, 0x00, + 0x86, 0x6b, 0x20, 0xdf, 0x0c, 0x07, 0x00, + 0x86, 0x6b, 0x42, 0xbd, 0x0d, 0x07, 0x00, + 0x86, 0x6b, 0x0b, 0xf4, 0x0e, 0x07, 0x00, + 0x86, 0x6b, 0x43, 0xbc, 0x0f, 0x07, 0x00, + 0x86, 0x6b, 0x10, 0xef, 0x10, 0x07, 0x00, + 0x86, 0x6b, 0x21, 0xde, 0x11, 0x07, 0x00, + 0x86, 0x6b, 0x13, 0xec, 0x12, 0x07, 0x00, + 0x86, 0x6b, 0x11, 0xee, 0x13, 0x07, 0x00, + 0x86, 0x6b, 0x12, 0xed, 0x14, 0x07, 0x00, + 0x86, 0x6b, 0x19, 0xe6, 0x15, 0x07, 0x00, + 0x86, 0x6b, 0x1a, 0xe5, 0x16, 0x07, 0x00, + 0x86, 0x6b, 0x1b, 0xe4, 0x17, 0x07, 0x00, + 0x86, 0x6b, 0x4b, 0xb4, 0x18, 0x07, 0x00, + 0x86, 0x6b, 0x40, 0xbf, 0x19, 0x07, 0x00, + 0x86, 0x6b, 0x44, 0xbb, 0x1a, 0x07, 0x00, + 0x86, 0x6b, 0x41, 0xbe, 0x1b, 0x07, 0x00, + 0x86, 0x6b, 0x22, 0xdd, 0x1c, 0x07, 0x00, + 0x86, 0x6b, 0x15, 0xea, 0x1d, 0x07, 0x00, + 0x86, 0x6b, 0x0f, 0xf0, 0x3f, 0x07, 0x00, + 0x86, 0x6b, 0x1c, 0xe3, 0x40, 0x07, 0x00, + 0x86, 0x6b, 0x4a, 0xb5, 0x41, 0x07, 0x00, + 0x86, 0x6b, 0x48, 0xb7, 0x42, 0x07, 0x00, + 0x86, 0x6b, 0x49, 0xb6, 0x43, 0x07, 0x00, + 0x86, 0x6b, 0x18, 0xe7, 0x44, 0x07, 0x00, + 0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00, +}; + +#endif diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index aaa0b6f0b..1a746127c 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -36,6 +36,9 @@ #include "tuner-xc2028.h" #include "tuner-simple.h" #include "mxl5005s.h" +#include "dib7000p.h" +#include "dib0070.h" +#include "lgs8gl5.h" /* debug */ static int dvb_usb_cxusb_debug; @@ -109,6 +112,25 @@ static void cxusb_nano2_led(struct dvb_usb_device *d, int onoff) cxusb_bluebird_gpio_rw(d, 0x40, onoff ? 0 : 0x40); } +static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d, + u8 addr, int onoff) +{ + u8 o[2] = {addr, onoff}; + u8 i; + int rc; + + rc = cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); + + if (rc < 0) + return rc; + if (i == 0x01) + return 0; + else { + deb_info("gpio_write failed.\n"); + return -EIO; + } +} + /* I2C */ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) @@ -210,7 +232,7 @@ static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) if (d->state == DVB_USB_STATE_INIT && usb_set_interface(d->udev, 0, 0) < 0) err("set interface failed"); - do; while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) && + do {} while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) && !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) && !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0); if (!ret) { @@ -262,6 +284,20 @@ static int cxusb_nano2_power_ctrl(struct dvb_usb_device *d, int onoff) return rc; } +static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + u8 b; + ret = cxusb_power_ctrl(d, onoff); + if (!onoff) + return ret; + + msleep(128); + cxusb_ctrl_msg(d, CMD_DIGITAL, NULL, 0, &b, 1); + msleep(100); + return ret; +} + static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { u8 buf[2] = { 0x03, 0x00 }; @@ -283,6 +319,67 @@ static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return 0; } +static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d) +{ + int ep = d->props.generic_bulk_ctrl_endpoint; + const int timeout = 100; + const int junk_len = 32; + u8 *junk; + int rd_count; + + /* Discard remaining data in video pipe */ + junk = kmalloc(junk_len, GFP_KERNEL); + if (!junk) + return; + while (1) { + if (usb_bulk_msg(d->udev, + usb_rcvbulkpipe(d->udev, ep), + junk, junk_len, &rd_count, timeout) < 0) + break; + if (!rd_count) + break; + } + kfree(junk); +} + +static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d) +{ + struct usb_data_stream_properties *p = &d->props.adapter[0].stream; + const int timeout = 100; + const int junk_len = p->u.bulk.buffersize; + u8 *junk; + int rd_count; + + /* Discard remaining data in video pipe */ + junk = kmalloc(junk_len, GFP_KERNEL); + if (!junk) + return; + while (1) { + if (usb_bulk_msg(d->udev, + usb_rcvbulkpipe(d->udev, p->endpoint), + junk, junk_len, &rd_count, timeout) < 0) + break; + if (!rd_count) + break; + } + kfree(junk); +} + +static int cxusb_d680_dmb_streaming_ctrl( + struct dvb_usb_adapter *adap, int onoff) +{ + if (onoff) { + u8 buf[2] = { 0x03, 0x00 }; + cxusb_d680_dmb_drain_video(adap->dev); + return cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, + buf, sizeof(buf), NULL, 0); + } else { + int ret = cxusb_ctrl_msg(adap->dev, + CMD_STREAMING_OFF, NULL, 0, NULL, 0); + return ret; + } +} + static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { struct dvb_usb_rc_key *keymap = d->props.rc_key_map; @@ -335,6 +432,32 @@ static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event, return 0; } +static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, + int *state) +{ + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + u8 ircode[2]; + int i; + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + if (cxusb_ctrl_msg(d, 0x10, NULL, 0, ircode, 2) < 0) + return 0; + + for (i = 0; i < d->props.rc_key_map_size; i++) { + if (keymap[i].custom == ircode[0] && + keymap[i].data == ircode[1]) { + *event = keymap[i].event; + *state = REMOTE_KEY_PRESSED; + + return 0; + } + } + + return 0; +} + static struct dvb_usb_rc_key dvico_mce_rc_keys[] = { { 0xfe, 0x02, KEY_TV }, { 0xfe, 0x0e, KEY_MP3 }, @@ -422,6 +545,44 @@ static struct dvb_usb_rc_key dvico_portable_rc_keys[] = { { 0xfc, 0x00, KEY_UNKNOWN }, /* HD */ }; +static struct dvb_usb_rc_key d680_dmb_rc_keys[] = { + { 0x00, 0x38, KEY_UNKNOWN }, /* TV/AV */ + { 0x08, 0x0c, KEY_ZOOM }, + { 0x08, 0x00, KEY_0 }, + { 0x00, 0x01, KEY_1 }, + { 0x08, 0x02, KEY_2 }, + { 0x00, 0x03, KEY_3 }, + { 0x08, 0x04, KEY_4 }, + { 0x00, 0x05, KEY_5 }, + { 0x08, 0x06, KEY_6 }, + { 0x00, 0x07, KEY_7 }, + { 0x08, 0x08, KEY_8 }, + { 0x00, 0x09, KEY_9 }, + { 0x00, 0x0a, KEY_MUTE }, + { 0x08, 0x29, KEY_BACK }, + { 0x00, 0x12, KEY_CHANNELUP }, + { 0x08, 0x13, KEY_CHANNELDOWN }, + { 0x00, 0x2b, KEY_VOLUMEUP }, + { 0x08, 0x2c, KEY_VOLUMEDOWN }, + { 0x00, 0x20, KEY_UP }, + { 0x08, 0x21, KEY_DOWN }, + { 0x00, 0x11, KEY_LEFT }, + { 0x08, 0x10, KEY_RIGHT }, + { 0x00, 0x0d, KEY_OK }, + { 0x08, 0x1f, KEY_RECORD }, + { 0x00, 0x17, KEY_PLAYPAUSE }, + { 0x08, 0x16, KEY_PLAYPAUSE }, + { 0x00, 0x0b, KEY_STOP }, + { 0x08, 0x27, KEY_FASTFORWARD }, + { 0x00, 0x26, KEY_REWIND }, + { 0x08, 0x1e, KEY_UNKNOWN }, /* Time Shift */ + { 0x00, 0x0e, KEY_UNKNOWN }, /* Snapshot */ + { 0x08, 0x2d, KEY_UNKNOWN }, /* Mouse Cursor */ + { 0x00, 0x0f, KEY_UNKNOWN }, /* Minimize/Maximize */ + { 0x08, 0x14, KEY_UNKNOWN }, /* Shuffle */ + { 0x00, 0x25, KEY_POWER }, +}; + static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) { static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; @@ -527,6 +688,24 @@ static struct mxl5005s_config aver_a868r_tuner = { .AgcMasterByte = 0x00, }; +/* FIXME: needs tweaking */ +static struct mxl5005s_config d680_dmb_tuner = { + .i2c_address = 0x63, + .if_freq = 36125000UL, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_C, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + /* Callbacks for DVB USB */ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) { @@ -594,7 +773,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) .callback = dvico_bluebird_xc2028_callback, }; static struct xc2028_ctrl ctl = { - .fname = "xc3028-v27.fw", + .fname = XC2028_DEFAULT_FIRMWARE, .max_len = 64, .demod = XC3028_FE_ZARLINK456, }; @@ -615,6 +794,14 @@ static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_frontend *fe; + fe = dvb_attach(mxl5005s_attach, adap->fe, + &adap->dev->i2c_adap, &d680_dmb_tuner); + return (fe == NULL) ? -EIO : 0; +} + static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) { u8 b; @@ -726,6 +913,159 @@ no_IR: return 0; } +static struct dibx000_agc_config dib7070_agc_config = { + .band_caps = BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, + + /* + * P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 + */ + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | + (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + .inv_gain = 600, + .time_stabiliz = 10, + .alpha_level = 0, + .thlock = 118, + .wbd_inv = 0, + .wbd_ref = 3530, + .wbd_sel = 1, + .wbd_alpha = 5, + .agc1_max = 65535, + .agc1_min = 0, + .agc2_max = 65535, + .agc2_min = 0, + .agc1_pt1 = 0, + .agc1_pt2 = 40, + .agc1_pt3 = 183, + .agc1_slope1 = 206, + .agc1_slope2 = 255, + .agc2_pt1 = 72, + .agc2_pt2 = 152, + .agc2_slope1 = 88, + .agc2_slope2 = 90, + .alpha_mant = 17, + .alpha_exp = 27, + .beta_mant = 23, + .beta_exp = 51, + .perform_agc_softsplit = 0, +}; + +static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { + .internal = 60000, + .sampling = 15000, + .pll_prediv = 1, + .pll_ratio = 20, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 2, + /* refsel, sel, freq_15k */ + .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), + .ifreq = (0 << 25) | 0, + .timf = 20452225, + .xtal_hz = 12000000, +}; + +static struct dib7000p_config cxusb_dualdig4_rev2_config = { + .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 1, + .agc = &dib7070_agc_config, + .bw = &dib7070_bw_config_12_mhz, + .tuner_is_baseband = 1, + .spur_protect = 1, + + .gpio_dir = 0xfcef, + .gpio_val = 0x0110, + + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, +}; + +static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (usb_set_interface(adap->dev->udev, 0, 1) < 0) + err("set interface failed"); + + cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); + + cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); + + dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + &cxusb_dualdig4_rev2_config); + + adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + &cxusb_dualdig4_rev2_config); + if (adap->fe == NULL) + return -EIO; + + return 0; +} + +static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) +{ + return dib7000p_set_gpio(fe, 8, 0, !onoff); +} + +static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + return 0; +} + +static struct dib0070_config dib7070p_dib0070_config = { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib7070_tuner_reset, + .sleep = dib7070_tuner_sleep, + .clock_khz = 12000, +}; + +struct dib0700_adapter_state { + int (*set_param_save) (struct dvb_frontend *, + struct dvb_frontend_parameters *); +}; + +static int dib7070_set_param_override(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + u16 offset; + u8 band = BAND_OF_FREQUENCY(fep->frequency/1000); + switch (band) { + case BAND_VHF: offset = 950; break; + default: + case BAND_UHF: offset = 550; break; + } + + dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + + return state->set_param_save(fe, fep); +} + +static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = + dib7000p_get_i2c_master(adap->fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + + if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + &dib7070p_dib0070_config) == NULL) + return -ENODEV; + + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib7070_set_param_override; + return 0; +} + static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap) { if (usb_set_interface(adap->dev->udev, 0, 1) < 0) @@ -751,6 +1091,54 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap) return -EIO; } +static struct lgs8gl5_config lgs8gl5_cfg = { + .demod_address = 0x19, +}; + +static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + int n; + + /* Select required USB configuration */ + if (usb_set_interface(d->udev, 0, 0) < 0) + err("set interface failed"); + + /* Unblock all USB pipes */ + usb_clear_halt(d->udev, + usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint)); + + /* Drain USB pipes to avoid hang after reboot */ + for (n = 0; n < 5; n++) { + cxusb_d680_dmb_drain_message(d); + cxusb_d680_dmb_drain_video(d); + msleep(200); + } + + /* Reset the tuner */ + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) { + err("clear tuner gpio failed"); + return -EIO; + } + msleep(100); + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) { + err("set tuner gpio failed"); + return -EIO; + } + msleep(100); + + /* Attach frontend */ + adap->fe = dvb_attach(lgs8gl5_attach, &lgs8gl5_cfg, &d->i2c_adap); + if (adap->fe == NULL) + return -EIO; + + return 0; +} + /* * DViCO has shipped two devices with the same USB ID, but only one of them * needs a firmware download. Check the device class details to see if they @@ -826,9 +1214,11 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties; static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties; static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties; static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties; +static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties; static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties; static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties; static struct dvb_usb_device_properties cxusb_aver_a868r_properties; +static struct dvb_usb_device_properties cxusb_d680_dmb_properties; static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -852,6 +1242,11 @@ static int cxusb_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &cxusb_bluebird_dualdig4_rev2_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, + THIS_MODULE, NULL, adapter_nr) || 0) return 0; @@ -876,6 +1271,8 @@ static struct usb_device_id cxusb_table [] = { { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) }, { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) }, { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) }, + { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cxusb_table); @@ -1321,6 +1718,104 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = { } }; +static +struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_dualdig4_rev2_frontend_attach, + .tuner_attach = cxusb_dualdig4_rev2_tuner_attach, + .size_of_priv = sizeof(struct dib0700_adapter_state), + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }, + }, + + .power_ctrl = cxusb_bluebird_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc_interval = 100, + .rc_key_map = dvico_mce_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dvico_mce_rc_keys), + .rc_query = cxusb_rc_query, + + .num_device_descs = 1, + .devices = { + { "DViCO FusionHDTV DVB-T Dual Digital 4 (rev 2)", + { NULL }, + { &cxusb_table[17], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl, + .frontend_attach = cxusb_d680_dmb_frontend_attach, + .tuner_attach = cxusb_d680_dmb_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }, + }, + + .power_ctrl = cxusb_d680_dmb_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc_interval = 100, + .rc_key_map = d680_dmb_rc_keys, + .rc_key_map_size = ARRAY_SIZE(d680_dmb_rc_keys), + .rc_query = cxusb_d680_dmb_rc_query, + + .num_device_descs = 1, + .devices = { + { + "Conexant DMB-TH Stick", + { NULL }, + { &cxusb_table[18], NULL }, + }, + } +}; + static struct usb_driver cxusb_driver = { .name = "dvb_usb_cxusb", .probe = cxusb_probe, diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c index 3dd20bfbe..6c0e5c5f4 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1117,7 +1117,8 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_HT_EXPRESS) }, { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS) }, { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P_2) }, - { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009) }, +/* 35 */{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_3) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -1373,7 +1374,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { } }, - .num_device_descs = 3, + .num_device_descs = 4, .devices = { { "DiBcom STK7070PD reference design", { &dib0700_usb_id_table[17], NULL }, @@ -1386,6 +1387,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { "Hauppauge Nova-TD Stick (52009)", { &dib0700_usb_id_table[35], NULL }, { NULL }, + }, + { "Hauppauge Nova-TD-500 (84xxx)", + { &dib0700_usb_id_table[36], NULL }, + { NULL }, } } }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, diff --git a/linux/drivers/media/dvb/dvb-usb/dtv5100.c b/linux/drivers/media/dvb/dvb-usb/dtv5100.c new file mode 100644 index 000000000..078ce92ca --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dtv5100.c @@ -0,0 +1,240 @@ +/* + * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T + * + * Copyright (C) 2008 Antoine Jacquet <royale@zerezo.com> + * http://royale.zerezo.com/dtv5100/ + * + * Inspired by gl861.c and au6610.c drivers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "dtv5100.h" +#include "zl10353.h" +#include "qt1010.h" + +/* debug */ +static int dvb_usb_dtv5100_debug; +module_param_named(debug, dvb_usb_dtv5100_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u8 request; + u8 type; + u16 value; + u16 index; + + switch (wlen) { + case 1: + /* write { reg }, read { value } */ + request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ : + DTV5100_TUNER_READ); + type = USB_TYPE_VENDOR | USB_DIR_IN; + value = 0; + break; + case 2: + /* write { reg, value } */ + request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE : + DTV5100_TUNER_WRITE); + type = USB_TYPE_VENDOR | USB_DIR_OUT; + value = wbuf[1]; + break; + default: + warn("wlen = %x, aborting.", wlen); + return -EINVAL; + } + index = (addr << 8) + wbuf[0]; + + msleep(1); /* avoid I2C errors */ + return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), request, + type, value, index, rbuf, rlen, + DTV5100_USB_TIMEOUT); +} + +/* I2C */ +static int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, msg[i+1].buf, + msg[i+1].len) < 0) + break; + i++; + } else if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, NULL, 0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 dtv5100_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dtv5100_i2c_algo = { + .master_xfer = dtv5100_i2c_xfer, + .functionality = dtv5100_i2c_func, +}; + +/* Callbacks for DVB USB */ +static struct zl10353_config dtv5100_zl10353_config = { + .demod_address = DTV5100_DEMOD_ADDR, + .no_tuner = 1, + .parallel_ts = 1, +}; + +static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap) +{ + adap->fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config, + &adap->dev->i2c_adap); + if (adap->fe == NULL) + return -EIO; + + /* disable i2c gate, or it won't work... is this safe? */ + adap->fe->ops.i2c_gate_ctrl = NULL; + + return 0; +} + +static struct qt1010_config dtv5100_qt1010_config = { + .i2c_address = DTV5100_TUNER_ADDR +}; + +static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap) +{ + return dvb_attach(qt1010_attach, + adap->fe, &adap->dev->i2c_adap, + &dtv5100_qt1010_config) == NULL ? -ENODEV : 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties dtv5100_properties; + +static int dtv5100_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int i, ret; + struct usb_device *udev = interface_to_usbdev(intf); + + /* initialize non qt1010/zl10353 part? */ + for (i = 0; dtv5100_init[i].request; i++) { + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + dtv5100_init[i].request, + USB_TYPE_VENDOR | USB_DIR_OUT, + dtv5100_init[i].value, + dtv5100_init[i].index, NULL, 0, + DTV5100_USB_TIMEOUT); + if (ret) + return ret; + } + + ret = dvb_usb_device_init(intf, &dtv5100_properties, + THIS_MODULE, NULL, adapter_nr); + if (ret) + return ret; + + return 0; +} + +static struct usb_device_id dtv5100_table[] = { + { USB_DEVICE(0x06be, 0xa232) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, dtv5100_table); + +static struct dvb_usb_device_properties dtv5100_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = 0, + + .num_adapters = 1, + .adapter = {{ + .frontend_attach = dtv5100_frontend_attach, + .tuner_attach = dtv5100_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } }, + + .i2c_algo = &dtv5100_i2c_algo, + + .num_device_descs = 1, + .devices = { + { + .name = "AME DTV-5100 USB2.0 DVB-T", + .cold_ids = { NULL }, + .warm_ids = { &dtv5100_table[0], NULL }, + }, + } +}; + +static struct usb_driver dtv5100_driver = { + .name = "dvb_usb_dtv5100", + .probe = dtv5100_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dtv5100_table, +}; + +/* module stuff */ +static int __init dtv5100_module_init(void) +{ + int ret; + + ret = usb_register(&dtv5100_driver); + if (ret) + err("usb_register failed. Error number %d", ret); + + return ret; +} + +static void __exit dtv5100_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&dtv5100_driver); +} + +module_init(dtv5100_module_init); +module_exit(dtv5100_module_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/dvb-usb/dtv5100.h b/linux/drivers/media/dvb/dvb-usb/dtv5100.h new file mode 100644 index 000000000..93e96e04a --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dtv5100.h @@ -0,0 +1,51 @@ +/* + * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T + * + * Copyright (C) 2008 Antoine Jacquet <royale@zerezo.com> + * http://royale.zerezo.com/dtv5100/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DVB_USB_DTV5100_H_ +#define _DVB_USB_DTV5100_H_ + +#define DVB_USB_LOG_PREFIX "dtv5100" +#include "dvb-usb.h" + +#define DTV5100_USB_TIMEOUT 500 + +#define DTV5100_DEMOD_ADDR 0x00 +#define DTV5100_DEMOD_WRITE 0xc0 +#define DTV5100_DEMOD_READ 0xc1 + +#define DTV5100_TUNER_ADDR 0xc4 +#define DTV5100_TUNER_WRITE 0xc7 +#define DTV5100_TUNER_READ 0xc8 + +#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +#define DRIVER_DESC "AME DTV-5100 USB2.0 DVB-T" + +static struct { + u8 request; + u8 value; + u16 index; +} dtv5100_init[] = { + { 0x000000c5, 0x00000000, 0x00000001 }, + { 0x000000c5, 0x00000001, 0x00000001 }, + { } /* Terminating entry */ +}; + +#endif diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 029b437ca..7ae262e08 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -22,6 +22,7 @@ #define USB_VID_AVERMEDIA 0x07ca #define USB_VID_COMPRO 0x185b #define USB_VID_COMPRO_UNK 0x145f +#define USB_VID_CONEXANT 0x0572 #define USB_VID_CYPRESS 0x04b4 #define USB_VID_DIBCOM 0x10b8 #define USB_VID_DPOSH 0x1498 @@ -33,16 +34,19 @@ #define USB_VID_HAUPPAUGE 0x2040 #define USB_VID_HYPER_PALTEK 0x1025 #define USB_VID_KWORLD 0xeb2a +#define USB_VID_KWORLD_2 0x1b80 #define USB_VID_KYE 0x0458 #define USB_VID_LEADTEK 0x0413 #define USB_VID_LITEON 0x04ca #define USB_VID_MEDION 0x1660 #define USB_VID_MIGLIA 0x18f3 #define USB_VID_MSI 0x0db0 +#define USB_VID_MSI_2 0x1462 #define USB_VID_OPERA1 0x695c #define USB_VID_PINNACLE 0x2304 #define USB_VID_TECHNOTREND 0x0b48 #define USB_VID_TERRATEC 0x0ccd +#define USB_VID_TELESTAR 0x10b9 #define USB_VID_VISIONPLUS 0x13d3 #define USB_VID_TWINHAN 0x1822 #define USB_VID_ULTIMA_ELECTRONIC 0x05d8 @@ -50,15 +54,18 @@ #define USB_VID_WIDEVIEW 0x14aa #define USB_VID_GIGABYTE 0x1044 #define USB_VID_YUAN 0x1164 - +#define USB_VID_XTENSIONS 0x1ae7 /* Product IDs */ #define USB_PID_ADSTECH_USB2_COLD 0xa333 #define USB_PID_ADSTECH_USB2_WARM 0xa334 #define USB_PID_AFATECH_AF9005 0x9020 +#define USB_PID_AFATECH_AF9015_9015 0x9015 +#define USB_PID_AFATECH_AF9015_9016 0x9016 #define USB_VID_ALINK_DTU 0xf170 #define USB_PID_ANSONIC_DVBT_USB 0x6000 #define USB_PID_ANYSEE 0x861f +#define USB_PID_AZUREWAVE_AD_TU700 0x3237 #define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 #define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 @@ -69,6 +76,7 @@ #define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d #define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78 #define USB_PID_COMPRO_VIDEOMATE_U500_PC 0x1e80 +#define USB_PID_CONEXANT_D680_DMB 0x86d6 #define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065 #define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 @@ -87,9 +95,12 @@ #define USB_PID_UNIWILL_STK7700P 0x6003 #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 +#define USB_PID_KWORLD_399U 0xe399 +#define USB_PID_KWORLD_PC160_2T 0xc160 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 +#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069 #define USB_PID_TWINHAN_VP7041_COLD 0x3201 #define USB_PID_TWINHAN_VP7041_WARM 0x3202 #define USB_PID_TWINHAN_VP7020_COLD 0x3203 @@ -98,6 +109,7 @@ #define USB_PID_TWINHAN_VP7045_WARM 0x3206 #define USB_PID_TWINHAN_VP7021_COLD 0x3207 #define USB_PID_TWINHAN_VP7021_WARM 0x3208 +#define USB_PID_TINYTWIN 0x3226 #define USB_PID_DNTV_TINYUSB2_COLD 0x3223 #define USB_PID_DNTV_TINYUSB2_WARM 0x3224 #define USB_PID_ULTIMA_TVBOX_COLD 0x8105 @@ -129,6 +141,7 @@ #define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 #define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941 #define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950 +#define USB_PID_HAUPPAUGE_NOVA_T_500_3 0x8400 #define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050 #define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060 #define USB_PID_HAUPPAUGE_NOVA_T_STICK_3 0x7070 @@ -143,6 +156,9 @@ #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039 +#define USB_PID_AVERMEDIA_VOLAR_X 0xa815 +#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150 +#define USB_PID_AVERMEDIA_A309 0xa309 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a #define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058 @@ -152,6 +168,7 @@ #define USB_PID_PINNACLE_PCTV2000E 0x022c #define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228 #define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T 0x0229 +#define USB_PID_PINNACLE_PCTV71E 0x022b #define USB_PID_PINNACLE_PCTV72E 0x0236 #define USB_PID_PINNACLE_PCTV73E 0x0237 #define USB_PID_PCTV_200E 0x020e @@ -170,6 +187,7 @@ #define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59 #define USB_PID_DVICO_BLUEBIRD_DUAL_4 0xdb78 +#define USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2 0xdb98 #define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2 0xdb70 #define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM 0xdb71 #define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54 @@ -189,6 +207,7 @@ #define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01 +#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029 #define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200 #define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201 #define USB_PID_GENPIX_8PSK_REV_2 0x0202 @@ -196,6 +215,7 @@ #define USB_PID_GENPIX_SKYWALKER_CW3K 0x0204 #define USB_PID_SIGMATEK_DVB_110 0x6610 #define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513 +#define USB_PID_MSI_DIGIVOX_DUO 0x8801 #define USB_PID_OPERA1_COLD 0x2830 #define USB_PID_OPERA1_WARM 0x3829 #define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514 @@ -205,5 +225,7 @@ #define USB_PID_ASUS_U3100 0x173f #define USB_PID_YUAN_EC372S 0x1edc #define USB_PID_DW2102 0x2102 +#define USB_PID_XTENSIONS_XD_380 0x0381 +#define USB_PID_TELESTAR_STARSTICK_2 0x8000 #endif diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 7dbb4a223..097d9f001 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -385,4 +385,23 @@ config DVB_ISL6421 help An SEC control chip. +config DVB_LGS8GL5 + tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DMB-TH tuner module. Say Y when you want to support this frontend. + +comment "Tools to develop new frontends" + +config DVB_DUMMY_FE + tristate "Dummy frontend driver" + default n + +config DVB_AF9013 + tristate "Afatech AF9013 demodulator" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + Say Y when you want to support this frontend. endmenu diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 028da5561..1b5478886 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -48,3 +48,6 @@ obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o obj-$(CONFIG_DVB_AU8522) += au8522.o obj-$(CONFIG_DVB_TDA10048) += tda10048.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o +obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o +obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o +obj-$(CONFIG_DVB_AF9013) += af9013.o diff --git a/linux/drivers/media/dvb/frontends/af9013.c b/linux/drivers/media/dvb/frontends/af9013.c new file mode 100644 index 000000000..739d885b3 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/af9013.c @@ -0,0 +1,1687 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/firmware.h> + +#include "dvb_frontend.h" +#include "af9013_priv.h" +#include "af9013.h" + +int debug; + +struct af9013_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + + struct af9013_config config; + + u16 signal_strength; + u32 ber; + u32 ucblocks; + u16 snr; + u32 frequency; + unsigned long next_statistics_check; +}; + +static u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + +static int af9013_write_regs(struct af9013_state *state, u8 mbox, u16 reg, + u8 *val, u8 len) +{ + u8 buf[3+len]; + struct i2c_msg msg = { + .addr = state->config.demod_address, + .flags = 0, + .len = sizeof(buf), + .buf = buf }; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + buf[2] = mbox; + memcpy(&buf[3], val, len); + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + warn("I2C write failed reg:%04x len:%d", reg, len); + return -EREMOTEIO; + } + return 0; +} + +static int af9013_write_ofdm_regs(struct af9013_state *state, u16 reg, u8 *val, + u8 len) +{ + u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(0 << 6)|(0 << 7); + return af9013_write_regs(state, mbox, reg, val, len); +} + +static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val, + u8 len) +{ + u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(1 << 6)|(1 << 7); + return af9013_write_regs(state, mbox, reg, val, len); +} + +/* write single register */ +static int af9013_write_reg(struct af9013_state *state, u16 reg, u8 val) +{ + return af9013_write_ofdm_regs(state, reg, &val, 1); +} + +/* read single register */ +static int af9013_read_reg(struct af9013_state *state, u16 reg, u8 *val) +{ + u8 obuf[3] = { reg >> 8, reg & 0xff, 0 }; + u8 ibuf[1]; + struct i2c_msg msg[2] = { + { + .addr = state->config.demod_address, + .flags = 0, + .len = sizeof(obuf), + .buf = obuf + }, { + .addr = state->config.demod_address, + .flags = I2C_M_RD, + .len = sizeof(ibuf), + .buf = ibuf + } + }; + + if (i2c_transfer(state->i2c, msg, 2) != 2) { + warn("I2C read failed reg:%04x", reg); + return -EREMOTEIO; + } + *val = ibuf[0]; + return 0; +} + +static int af9013_write_reg_bits(struct af9013_state *state, u16 reg, u8 pos, + u8 len, u8 val) +{ + int ret; + u8 tmp, mask; + + ret = af9013_read_reg(state, reg, &tmp); + if (ret) + return ret; + + mask = regmask[len - 1] << pos; + tmp = (tmp & ~mask) | ((val << pos) & mask); + + return af9013_write_reg(state, reg, tmp); +} + +static int af9013_read_reg_bits(struct af9013_state *state, u16 reg, u8 pos, + u8 len, u8 *val) +{ + int ret; + u8 tmp; + + ret = af9013_read_reg(state, reg, &tmp); + if (ret) + return ret; + *val = (tmp >> pos) & regmask[len - 1]; + return 0; +} + +static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval) +{ + int ret; + u8 pos; + u16 addr; + deb_info("%s: gpio:%d gpioval:%02x\n", __func__, gpio, gpioval); + +/* GPIO0 & GPIO1 0xd735 + GPIO2 & GPIO3 0xd736 */ + + switch (gpio) { + case 0: + case 1: + addr = 0xd735; + break; + case 2: + case 3: + addr = 0xd736; + break; + + default: + err("invalid gpio:%d\n", gpio); + ret = -EINVAL; + goto error; + }; + + switch (gpio) { + case 0: + case 2: + pos = 0; + break; + case 1: + case 3: + default: + pos = 4; + break; + }; + + ret = af9013_write_reg_bits(state, addr, pos, 4, gpioval); + +error: + return ret; +} + +static u32 af913_div(u32 a, u32 b, u32 x) +{ + u32 r = 0, c = 0, i; + deb_info("%s: a:%d b:%d x:%d\n", __func__, a, b, x); + + if (a > b) { + c = a / b; + a = a - c * b; + } + + for (i = 0; i < x; i++) { + if (a >= b) { + r += 1; + a -= b; + } + a <<= 1; + r <<= 1; + } + r = (c << (u32)x) + r; + + deb_info("%s: a:%d b:%d x:%d r:%d r:%x\n", __func__, a, b, x, r, r); + return r; +} + +static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw) +{ + int ret = 0; + u8 i = 0; + u8 buf[24]; + u32 ns_coeff1_2048nu; + u32 ns_coeff1_8191nu; + u32 ns_coeff1_8192nu; + u32 ns_coeff1_8193nu; + u32 ns_coeff2_2k; + u32 ns_coeff2_8k; + + deb_info("%s: adc_clock:%d bw:%d\n", __func__, + state->config.adc_clock, bw); + + switch (state->config.adc_clock) { + case 28800: /* 28.800 MHz */ + switch (bw) { + case BANDWIDTH_6_MHZ: + ns_coeff1_2048nu = 0x01e79e7a; + ns_coeff1_8191nu = 0x0079eb6e; + ns_coeff1_8192nu = 0x0079e79e; + ns_coeff1_8193nu = 0x0079e3cf; + ns_coeff2_2k = 0x00f3cf3d; + ns_coeff2_8k = 0x003cf3cf; + break; + case BANDWIDTH_7_MHZ: + ns_coeff1_2048nu = 0x0238e38e; + ns_coeff1_8191nu = 0x008e3d55; + ns_coeff1_8192nu = 0x008e38e4; + ns_coeff1_8193nu = 0x008e3472; + ns_coeff2_2k = 0x011c71c7; + ns_coeff2_8k = 0x00471c72; + break; + case BANDWIDTH_8_MHZ: + ns_coeff1_2048nu = 0x028a28a3; + ns_coeff1_8191nu = 0x00a28f3d; + ns_coeff1_8192nu = 0x00a28a29; + ns_coeff1_8193nu = 0x00a28514; + ns_coeff2_2k = 0x01451451; + ns_coeff2_8k = 0x00514514; + break; + default: + ret = -EINVAL; + } + break; + case 20480: /* 20.480 MHz */ + switch (bw) { + case BANDWIDTH_6_MHZ: + ns_coeff1_2048nu = 0x02adb6dc; + ns_coeff1_8191nu = 0x00ab7313; + ns_coeff1_8192nu = 0x00ab6db7; + ns_coeff1_8193nu = 0x00ab685c; + ns_coeff2_2k = 0x0156db6e; + ns_coeff2_8k = 0x0055b6dc; + break; + case BANDWIDTH_7_MHZ: + ns_coeff1_2048nu = 0x03200001; + ns_coeff1_8191nu = 0x00c80640; + ns_coeff1_8192nu = 0x00c80000; + ns_coeff1_8193nu = 0x00c7f9c0; + ns_coeff2_2k = 0x01900000; + ns_coeff2_8k = 0x00640000; + break; + case BANDWIDTH_8_MHZ: + ns_coeff1_2048nu = 0x03924926; + ns_coeff1_8191nu = 0x00e4996e; + ns_coeff1_8192nu = 0x00e49249; + ns_coeff1_8193nu = 0x00e48b25; + ns_coeff2_2k = 0x01c92493; + ns_coeff2_8k = 0x00724925; + break; + default: + ret = -EINVAL; + } + break; + case 28000: /* 28.000 MHz */ + switch (bw) { + case BANDWIDTH_6_MHZ: + ns_coeff1_2048nu = 0x01f58d10; + ns_coeff1_8191nu = 0x007d672f; + ns_coeff1_8192nu = 0x007d6344; + ns_coeff1_8193nu = 0x007d5f59; + ns_coeff2_2k = 0x00fac688; + ns_coeff2_8k = 0x003eb1a2; + break; + case BANDWIDTH_7_MHZ: + ns_coeff1_2048nu = 0x02492492; + ns_coeff1_8191nu = 0x00924db7; + ns_coeff1_8192nu = 0x00924925; + ns_coeff1_8193nu = 0x00924492; + ns_coeff2_2k = 0x01249249; + ns_coeff2_8k = 0x00492492; + break; + case BANDWIDTH_8_MHZ: + ns_coeff1_2048nu = 0x029cbc15; + ns_coeff1_8191nu = 0x00a7343f; + ns_coeff1_8192nu = 0x00a72f05; + ns_coeff1_8193nu = 0x00a729cc; + ns_coeff2_2k = 0x014e5e0a; + ns_coeff2_8k = 0x00539783; + break; + default: + ret = -EINVAL; + } + break; + case 25000: /* 25.000 MHz */ + switch (bw) { + case BANDWIDTH_6_MHZ: + ns_coeff1_2048nu = 0x0231bcb5; + ns_coeff1_8191nu = 0x008c7391; + ns_coeff1_8192nu = 0x008c6f2d; + ns_coeff1_8193nu = 0x008c6aca; + ns_coeff2_2k = 0x0118de5b; + ns_coeff2_8k = 0x00463797; + break; + case BANDWIDTH_7_MHZ: + ns_coeff1_2048nu = 0x028f5c29; + ns_coeff1_8191nu = 0x00a3dc29; + ns_coeff1_8192nu = 0x00a3d70a; + ns_coeff1_8193nu = 0x00a3d1ec; + ns_coeff2_2k = 0x0147ae14; + ns_coeff2_8k = 0x0051eb85; + break; + case BANDWIDTH_8_MHZ: + ns_coeff1_2048nu = 0x02ecfb9d; + ns_coeff1_8191nu = 0x00bb44c1; + ns_coeff1_8192nu = 0x00bb3ee7; + ns_coeff1_8193nu = 0x00bb390d; + ns_coeff2_2k = 0x01767dce; + ns_coeff2_8k = 0x005d9f74; + break; + default: + ret = -EINVAL; + } + break; + default: + err("invalid xtal"); + return -EINVAL; + } + if (ret) { + err("invalid bandwidth"); + return ret; + } + + buf[i++] = (u8) ((ns_coeff1_2048nu & 0x03000000) >> 24); + buf[i++] = (u8) ((ns_coeff1_2048nu & 0x00ff0000) >> 16); + buf[i++] = (u8) ((ns_coeff1_2048nu & 0x0000ff00) >> 8); + buf[i++] = (u8) ((ns_coeff1_2048nu & 0x000000ff)); + buf[i++] = (u8) ((ns_coeff2_2k & 0x01c00000) >> 22); + buf[i++] = (u8) ((ns_coeff2_2k & 0x003fc000) >> 14); + buf[i++] = (u8) ((ns_coeff2_2k & 0x00003fc0) >> 6); + buf[i++] = (u8) ((ns_coeff2_2k & 0x0000003f)); + buf[i++] = (u8) ((ns_coeff1_8191nu & 0x03000000) >> 24); + buf[i++] = (u8) ((ns_coeff1_8191nu & 0x00ffc000) >> 16); + buf[i++] = (u8) ((ns_coeff1_8191nu & 0x0000ff00) >> 8); + buf[i++] = (u8) ((ns_coeff1_8191nu & 0x000000ff)); + buf[i++] = (u8) ((ns_coeff1_8192nu & 0x03000000) >> 24); + buf[i++] = (u8) ((ns_coeff1_8192nu & 0x00ffc000) >> 16); + buf[i++] = (u8) ((ns_coeff1_8192nu & 0x0000ff00) >> 8); + buf[i++] = (u8) ((ns_coeff1_8192nu & 0x000000ff)); + buf[i++] = (u8) ((ns_coeff1_8193nu & 0x03000000) >> 24); + buf[i++] = (u8) ((ns_coeff1_8193nu & 0x00ffc000) >> 16); + buf[i++] = (u8) ((ns_coeff1_8193nu & 0x0000ff00) >> 8); + buf[i++] = (u8) ((ns_coeff1_8193nu & 0x000000ff)); + buf[i++] = (u8) ((ns_coeff2_8k & 0x01c00000) >> 22); + buf[i++] = (u8) ((ns_coeff2_8k & 0x003fc000) >> 14); + buf[i++] = (u8) ((ns_coeff2_8k & 0x00003fc0) >> 6); + buf[i++] = (u8) ((ns_coeff2_8k & 0x0000003f)); + + deb_info("%s: coeff:", __func__); + debug_dump(buf, sizeof(buf), deb_info); + + /* program */ + for (i = 0; i < sizeof(buf); i++) { + ret = af9013_write_reg(state, 0xae00 + i, buf[i]); + if (ret) + break; + } + + return ret; +} + +static int af9013_set_adc_ctrl(struct af9013_state *state) +{ + int ret; + u8 buf[3], tmp, i; + u32 adc_cw; + + deb_info("%s: adc_clock:%d\n", __func__, state->config.adc_clock); + + /* adc frequency type */ + switch (state->config.adc_clock) { + case 28800: /* 28.800 MHz */ + tmp = 0; + break; + case 20480: /* 20.480 MHz */ + tmp = 1; + break; + case 28000: /* 28.000 MHz */ + tmp = 2; + break; + case 25000: /* 25.000 MHz */ + tmp = 3; + break; + default: + err("invalid xtal"); + return -EINVAL; + } + + adc_cw = af913_div(state->config.adc_clock*1000, 1000000ul, 19ul); + + buf[0] = (u8) ((adc_cw & 0x000000ff)); + buf[1] = (u8) ((adc_cw & 0x0000ff00) >> 8); + buf[2] = (u8) ((adc_cw & 0x00ff0000) >> 16); + + deb_info("%s: adc_cw:", __func__); + debug_dump(buf, sizeof(buf), deb_info); + + /* program */ + for (i = 0; i < sizeof(buf); i++) { + ret = af9013_write_reg(state, 0xd180 + i, buf[i]); + if (ret) + goto error; + } + ret = af9013_write_reg_bits(state, 0x9bd2, 0, 4, tmp); +error: + return ret; +} + +static int af9013_set_freq_ctrl(struct af9013_state *state, fe_bandwidth_t bw) +{ + int ret; + u16 addr; + u8 buf[3], i, j; + u32 adc_freq, freq_cw; + s8 bfs_spec_inv; + int if_sample_freq; + + for (j = 0; j < 3; j++) { + if (j == 0) { + addr = 0xd140; /* fcw normal */ + bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1; + } else if (j == 1) { + addr = 0x9be7; /* fcw dummy ram */ + bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1; + } else { + addr = 0x9bea; /* fcw inverted */ + bfs_spec_inv = state->config.rf_spec_inv ? 1 : -1; + } + + adc_freq = state->config.adc_clock * 1000; + if_sample_freq = state->config.tuner_if * 1000; + + /* TDA18271 uses different sampling freq for every bw */ + if (state->config.tuner == AF9013_TUNER_TDA18271) { + switch (bw) { + case BANDWIDTH_6_MHZ: + if_sample_freq = 3300000; /* 3.3 MHz */ + break; + case BANDWIDTH_7_MHZ: + if_sample_freq = 3800000; /* 3.8 MHz */ + break; + case BANDWIDTH_8_MHZ: + default: + if_sample_freq = 4300000; /* 4.3 MHz */ + break; + } + } + + while (if_sample_freq > (adc_freq / 2)) + if_sample_freq = if_sample_freq - adc_freq; + + if (if_sample_freq >= 0) + bfs_spec_inv = bfs_spec_inv * (-1); + else + if_sample_freq = if_sample_freq * (-1); + + freq_cw = af913_div(if_sample_freq, adc_freq, 23ul); + + if (bfs_spec_inv == -1) + freq_cw = 0x00800000 - freq_cw; + + buf[0] = (u8) ((freq_cw & 0x000000ff)); + buf[1] = (u8) ((freq_cw & 0x0000ff00) >> 8); + buf[2] = (u8) ((freq_cw & 0x007f0000) >> 16); + + + deb_info("%s: freq_cw:", __func__); + debug_dump(buf, sizeof(buf), deb_info); + + /* program */ + for (i = 0; i < sizeof(buf); i++) { + ret = af9013_write_reg(state, addr++, buf[i]); + if (ret) + goto error; + } + } +error: + return ret; +} + +static int af9013_set_ofdm_params(struct af9013_state *state, + struct dvb_ofdm_parameters *params, u8 *auto_mode) +{ + int ret; + u8 i, buf[3] = {0, 0, 0}; + *auto_mode = 0; /* set if parameters are requested to auto set */ + + switch (params->transmission_mode) { + case TRANSMISSION_MODE_AUTO: + *auto_mode = 1; + case TRANSMISSION_MODE_2K: + break; + case TRANSMISSION_MODE_8K: + buf[0] |= (1 << 0); + break; + default: + return -EINVAL; + } + + switch (params->guard_interval) { + case GUARD_INTERVAL_AUTO: + *auto_mode = 1; + case GUARD_INTERVAL_1_32: + break; + case GUARD_INTERVAL_1_16: + buf[0] |= (1 << 2); + break; + case GUARD_INTERVAL_1_8: + buf[0] |= (2 << 2); + break; + case GUARD_INTERVAL_1_4: + buf[0] |= (3 << 2); + break; + default: + return -EINVAL; + } + + switch (params->hierarchy_information) { + case HIERARCHY_AUTO: + *auto_mode = 1; + case HIERARCHY_NONE: + break; + case HIERARCHY_1: + buf[0] |= (1 << 4); + break; + case HIERARCHY_2: + buf[0] |= (2 << 4); + break; + case HIERARCHY_4: + buf[0] |= (3 << 4); + break; + default: + return -EINVAL; + }; + + switch (params->constellation) { + case QAM_AUTO: + *auto_mode = 1; + case QPSK: + break; + case QAM_16: + buf[1] |= (1 << 6); + break; + case QAM_64: + buf[1] |= (2 << 6); + break; + default: + return -EINVAL; + } + + /* Use HP. How and which case we can switch to LP? */ + buf[1] |= (1 << 4); + + switch (params->code_rate_HP) { + case FEC_AUTO: + *auto_mode = 1; + case FEC_1_2: + break; + case FEC_2_3: + buf[2] |= (1 << 0); + break; + case FEC_3_4: + buf[2] |= (2 << 0); + break; + case FEC_5_6: + buf[2] |= (3 << 0); + break; + case FEC_7_8: + buf[2] |= (4 << 0); + break; + default: + return -EINVAL; + } + + switch (params->code_rate_LP) { + case FEC_AUTO: + /* if HIERARCHY_NONE and FEC_NONE then LP FEC is set to FEC_AUTO + by dvb_frontend.c for compatibility */ + if (params->hierarchy_information != HIERARCHY_NONE) + *auto_mode = 1; + case FEC_1_2: + break; + case FEC_2_3: + buf[2] |= (1 << 3); + break; + case FEC_3_4: + buf[2] |= (2 << 3); + break; + case FEC_5_6: + buf[2] |= (3 << 3); + break; + case FEC_7_8: + buf[2] |= (4 << 3); + break; + case FEC_NONE: + if (params->hierarchy_information == HIERARCHY_AUTO) + break; + default: + return -EINVAL; + } + + switch (params->bandwidth) { + case BANDWIDTH_6_MHZ: + break; + case BANDWIDTH_7_MHZ: + buf[1] |= (1 << 2); + break; + case BANDWIDTH_8_MHZ: + buf[1] |= (2 << 2); + break; + default: + return -EINVAL; + } + + /* program */ + for (i = 0; i < sizeof(buf); i++) { + ret = af9013_write_reg(state, 0xd3c0 + i, buf[i]); + if (ret) + break; + } + + return ret; +} + +static int af9013_reset(struct af9013_state *state, u8 sleep) +{ + int ret; + u8 tmp, i; + deb_info("%s\n", __func__); + + /* enable OFDM reset */ + ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 1); + if (ret) + goto error; + + /* start reset mechanism */ + ret = af9013_write_reg(state, 0xaeff, 1); + if (ret) + goto error; + + /* reset is done when bit 1 is set */ + for (i = 0; i < 150; i++) { + ret = af9013_read_reg_bits(state, 0xd417, 1, 1, &tmp); + if (ret) + goto error; + if (tmp) + break; /* reset done */ + msleep(10); + } + if (!tmp) + return -ETIMEDOUT; + + /* don't clear reset when going to sleep */ + if (!sleep) { + /* clear OFDM reset */ + ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0); + if (ret) + goto error; + + /* disable OFDM reset */ + ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0); + } +error: + return ret; +} + +static int af9013_power_ctrl(struct af9013_state *state, u8 onoff) +{ + int ret; + deb_info("%s: onoff:%d\n", __func__, onoff); + + if (onoff) { + /* power on */ + ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 0); + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0); + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0); + } else { + /* power off */ + ret = af9013_reset(state, 1); + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 1); + } +error: + return ret; +} + +static int af9013_lock_led(struct af9013_state *state, u8 onoff) +{ + deb_info("%s: onoff:%d\n", __func__, onoff); + + return af9013_write_reg_bits(state, 0xd730, 0, 1, onoff); +} + +static int af9013_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 auto_mode; /* auto set TPS */ + + deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency, + params->u.ofdm.bandwidth); + + state->frequency = params->frequency; + + /* program CFOE coefficients */ + ret = af9013_set_coeff(state, params->u.ofdm.bandwidth); + if (ret) + goto error; + + /* program frequency control */ + ret = af9013_set_freq_ctrl(state, params->u.ofdm.bandwidth); + if (ret) + goto error; + + /* clear TPS lock flag (inverted flag) */ + ret = af9013_write_reg_bits(state, 0xd330, 3, 1, 1); + if (ret) + goto error; + + /* clear MPEG2 lock flag */ + ret = af9013_write_reg_bits(state, 0xd507, 6, 1, 0); + if (ret) + goto error; + + /* empty channel function */ + ret = af9013_write_reg_bits(state, 0x9bfe, 0, 1, 0); + if (ret) + goto error; + + /* empty DVB-T channel function */ + ret = af9013_write_reg_bits(state, 0x9bc2, 0, 1, 0); + if (ret) + goto error; + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, params); + + /* program TPS and bandwidth, check if auto mode needed */ + ret = af9013_set_ofdm_params(state, ¶ms->u.ofdm, &auto_mode); + if (ret) + goto error; + + if (auto_mode) { + /* clear easy mode flag */ + ret = af9013_write_reg(state, 0xaefd, 0); + deb_info("%s: auto TPS\n", __func__); + } else { + /* set easy mode flag */ + ret = af9013_write_reg(state, 0xaefd, 1); + if (ret) + goto error; + ret = af9013_write_reg(state, 0xaefe, 0); + deb_info("%s: manual TPS\n", __func__); + } + if (ret) + goto error; + + /* everything is set, lets try to receive channel - OFSM GO! */ + ret = af9013_write_reg(state, 0xffff, 0); + if (ret) + goto error; + +error: + return ret; +} + +static int af9013_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 i, buf[3]; + deb_info("%s\n", __func__); + + /* read TPS registers */ + for (i = 0; i < 3; i++) { + ret = af9013_read_reg(state, 0xd3c0 + i, &buf[i]); + if (ret) + goto error; + } + + switch ((buf[1] >> 6) & 3) { + case 0: + p->u.ofdm.constellation = QPSK; + break; + case 1: + p->u.ofdm.constellation = QAM_16; + break; + case 2: + p->u.ofdm.constellation = QAM_64; + break; + } + + switch ((buf[0] >> 0) & 3) { + case 0: + p->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + p->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; + } + + switch ((buf[0] >> 2) & 3) { + case 0: + p->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + p->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + p->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + p->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[0] >> 4) & 7) { + case 0: + p->u.ofdm.hierarchy_information = HIERARCHY_NONE; + break; + case 1: + p->u.ofdm.hierarchy_information = HIERARCHY_1; + break; + case 2: + p->u.ofdm.hierarchy_information = HIERARCHY_2; + break; + case 3: + p->u.ofdm.hierarchy_information = HIERARCHY_4; + break; + } + + switch ((buf[2] >> 0) & 7) { + case 0: + p->u.ofdm.code_rate_HP = FEC_1_2; + break; + case 1: + p->u.ofdm.code_rate_HP = FEC_2_3; + break; + case 2: + p->u.ofdm.code_rate_HP = FEC_3_4; + break; + case 3: + p->u.ofdm.code_rate_HP = FEC_5_6; + break; + case 4: + p->u.ofdm.code_rate_HP = FEC_7_8; + break; + } + + switch ((buf[2] >> 3) & 7) { + case 0: + p->u.ofdm.code_rate_LP = FEC_1_2; + break; + case 1: + p->u.ofdm.code_rate_LP = FEC_2_3; + break; + case 2: + p->u.ofdm.code_rate_LP = FEC_3_4; + break; + case 3: + p->u.ofdm.code_rate_LP = FEC_5_6; + break; + case 4: + p->u.ofdm.code_rate_LP = FEC_7_8; + break; + } + + switch ((buf[1] >> 2) & 3) { + case 0: + p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + break; + case 1: + p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; + break; + case 2: + p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + break; + } + + p->inversion = INVERSION_AUTO; + p->frequency = state->frequency; + +error: + return ret; +} + +static int af9013_update_ber_unc(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 buf[3], i; + u32 error_bit_count = 0; + u32 total_bit_count = 0; + u32 abort_packet_count = 0; + + state->ber = 0; + + /* check if error bit count is ready */ + ret = af9013_read_reg_bits(state, 0xd391, 4, 1, &buf[0]); + if (ret) + goto error; + if (!buf[0]) + goto exit; + + /* get RSD packet abort count */ + for (i = 0; i < 2; i++) { + ret = af9013_read_reg(state, 0xd38a + i, &buf[i]); + if (ret) + goto error; + } + abort_packet_count = (buf[1] << 8) + buf[0]; + + /* get error bit count */ + for (i = 0; i < 3; i++) { + ret = af9013_read_reg(state, 0xd387 + i, &buf[i]); + if (ret) + goto error; + } + error_bit_count = (buf[2] << 16) + (buf[1] << 8) + buf[0]; + error_bit_count = error_bit_count - abort_packet_count * 8 * 8; + + /* get used RSD counting period (10000 RSD packets used) */ + for (i = 0; i < 2; i++) { + ret = af9013_read_reg(state, 0xd385 + i, &buf[i]); + if (ret) + goto error; + } + total_bit_count = (buf[1] << 8) + buf[0]; + total_bit_count = total_bit_count - abort_packet_count; + total_bit_count = total_bit_count * 204 * 8; + + if (total_bit_count) + state->ber = error_bit_count * 1000000000 / total_bit_count; + + state->ucblocks += abort_packet_count; + + deb_info("%s: err bits:%d total bits:%d abort count:%d\n", __func__, + error_bit_count, total_bit_count, abort_packet_count); + + /* set BER counting range */ + ret = af9013_write_reg(state, 0xd385, 10000 & 0xff); + if (ret) + goto error; + ret = af9013_write_reg(state, 0xd386, 10000 >> 8); + if (ret) + goto error; + /* reset and start BER counter */ + ret = af9013_write_reg_bits(state, 0xd391, 4, 1, 1); + if (ret) + goto error; + +exit: +error: + return ret; +} + +static int af9013_update_snr(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 buf[3], i, len; + u32 quant = 0; + struct snr_table *snr_table; + + /* check if quantizer ready (for snr) */ + ret = af9013_read_reg_bits(state, 0xd2e1, 3, 1, &buf[0]); + if (ret) + goto error; + if (buf[0]) { + /* quantizer ready - read it */ + for (i = 0; i < 3; i++) { + ret = af9013_read_reg(state, 0xd2e3 + i, &buf[i]); + if (ret) + goto error; + } + quant = (buf[2] << 16) + (buf[1] << 8) + buf[0]; + + /* read current constellation */ + ret = af9013_read_reg(state, 0xd3c1, &buf[0]); + if (ret) + goto error; + + switch ((buf[0] >> 6) & 3) { + case 0: + len = ARRAY_SIZE(qpsk_snr_table); + snr_table = qpsk_snr_table; + break; + case 1: + len = ARRAY_SIZE(qam16_snr_table); + snr_table = qam16_snr_table; + break; + case 2: + len = ARRAY_SIZE(qam64_snr_table); + snr_table = qam64_snr_table; + break; + default: + len = 0; + break; + } + + if (len) { + for (i = 0; i < len; i++) { + if (quant < snr_table[i].val) { + state->snr = snr_table[i].snr * 10; + break; + } + } + } + + /* set quantizer super frame count */ + ret = af9013_write_reg(state, 0xd2e2, 1); + if (ret) + goto error; + + /* check quantizer availability */ + for (i = 0; i < 10; i++) { + msleep(10); + ret = af9013_read_reg_bits(state, 0xd2e6, 0, 1, + &buf[0]); + if (ret) + goto error; + if (!buf[0]) + break; + } + + /* reset quantizer */ + ret = af9013_write_reg_bits(state, 0xd2e1, 3, 1, 1); + if (ret) + goto error; + } + +error: + return ret; +} + +static int af9013_update_signal_strength(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 tmp0; + u8 rf_gain, rf_50, rf_80, if_gain, if_50, if_80; + int signal_strength; + + deb_info("%s\n", __func__); + + state->signal_strength = 0; + + ret = af9013_read_reg_bits(state, 0x9bee, 0, 1, &tmp0); + if (ret) + goto error; + if (tmp0) { + ret = af9013_read_reg(state, 0x9bbd, &rf_50); + if (ret) + goto error; + ret = af9013_read_reg(state, 0x9bd0, &rf_80); + if (ret) + goto error; + ret = af9013_read_reg(state, 0x9be2, &if_50); + if (ret) + goto error; + ret = af9013_read_reg(state, 0x9be4, &if_80); + if (ret) + goto error; + ret = af9013_read_reg(state, 0xd07c, &rf_gain); + if (ret) + goto error; + ret = af9013_read_reg(state, 0xd07d, &if_gain); + if (ret) + goto error; + signal_strength = (0xffff / (9 * (rf_50 + if_50) - \ + 11 * (rf_80 + if_80))) * (10 * (rf_gain + if_gain) - \ + 11 * (rf_80 + if_80)); + if (signal_strength < 0) + signal_strength = 0; + else if (signal_strength > 0xffff) + signal_strength = 0xffff; + + state->signal_strength = signal_strength; + } + +error: + return ret; +} + +static int af9013_update_statistics(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + + if (time_before(jiffies, state->next_statistics_check)) + return 0; + + /* set minimum statistic update interval */ + state->next_statistics_check = jiffies + msecs_to_jiffies(1200); + + ret = af9013_update_signal_strength(fe); + if (ret) + goto error; + ret = af9013_update_snr(fe); + if (ret) + goto error; + ret = af9013_update_ber_unc(fe); + if (ret) + goto error; + +error: + return ret; +} + +static int af9013_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 800; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret = 0; + u8 tmp; + *status = 0; + + /* TPS lock */ + ret = af9013_read_reg_bits(state, 0xd330, 3, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL; + + /* MPEG2 lock */ + ret = af9013_read_reg_bits(state, 0xd507, 6, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + + if (!*status & FE_HAS_SIGNAL) { + /* AGC lock */ + ret = af9013_read_reg_bits(state, 0xd1a0, 6, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_SIGNAL; + } + + if (!*status & FE_HAS_CARRIER) { + /* CFO lock */ + ret = af9013_read_reg_bits(state, 0xd333, 7, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_CARRIER; + } + + if (!*status & FE_HAS_CARRIER) { + /* SFOE lock */ + ret = af9013_read_reg_bits(state, 0xd334, 6, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_CARRIER; + } + + ret = af9013_update_statistics(fe); + +error: + return ret; +} + + +static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + ret = af9013_update_statistics(fe); + *ber = state->ber; + return ret; +} + +static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + ret = af9013_update_statistics(fe); + *strength = state->signal_strength; + return ret; +} + +static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + ret = af9013_update_statistics(fe); + *snr = state->snr; + return ret; +} + +static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + ret = af9013_update_statistics(fe); + *ucblocks = state->ucblocks; + return ret; +} + +static int af9013_sleep(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + deb_info("%s\n", __func__); + + ret = af9013_lock_led(state, 0); + if (ret) + goto error; + + ret = af9013_power_ctrl(state, 0); +error: + return ret; +} + +static int af9013_init(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret, i, len; + u8 tmp0, tmp1; + struct regdesc *init; + deb_info("%s\n", __func__); + + /* reset OFDM */ + ret = af9013_reset(state, 0); + if (ret) + goto error; + + /* power on */ + ret = af9013_power_ctrl(state, 1); + if (ret) + goto error; + + /* enable ADC */ + ret = af9013_write_reg(state, 0xd73a, 0xa4); + if (ret) + goto error; + + /* write API version to firmware */ + for (i = 0; i < sizeof(state->config.api_version); i++) { + ret = af9013_write_reg(state, 0x9bf2 + i, + state->config.api_version[i]); + if (ret) + goto error; + } + + /* program ADC control */ + ret = af9013_set_adc_ctrl(state); + if (ret) + goto error; + + /* set I2C master clock */ + ret = af9013_write_reg(state, 0xd416, 0x14); + if (ret) + goto error; + + /* set 16 embx */ + ret = af9013_write_reg_bits(state, 0xd700, 1, 1, 1); + if (ret) + goto error; + + /* set no trigger */ + ret = af9013_write_reg_bits(state, 0xd700, 2, 1, 0); + if (ret) + goto error; + + /* set read-update bit for constellation */ + ret = af9013_write_reg_bits(state, 0xd371, 1, 1, 1); + if (ret) + goto error; + + /* enable FEC monitor */ + ret = af9013_write_reg_bits(state, 0xd392, 1, 1, 1); + if (ret) + goto error; + + /* load OFSM settings */ + deb_info("%s: load ofsm settings\n", __func__); + len = ARRAY_SIZE(ofsm_init); + init = ofsm_init; + for (i = 0; i < len; i++) { + ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos, + init[i].len, init[i].val); + if (ret) + goto error; + } + + /* load tuner specific settings */ + deb_info("%s: load tuner specific settings\n", __func__); + switch (state->config.tuner) { + case AF9013_TUNER_MXL5003D: + len = ARRAY_SIZE(tuner_init_mxl5003d); + init = tuner_init_mxl5003d; + break; + case AF9013_TUNER_MXL5005D: + case AF9013_TUNER_MXL5005R: + len = ARRAY_SIZE(tuner_init_mxl5005); + init = tuner_init_mxl5005; + break; + case AF9013_TUNER_ENV77H11D5: + len = ARRAY_SIZE(tuner_init_env77h11d5); + init = tuner_init_env77h11d5; + break; + case AF9013_TUNER_MT2060: + len = ARRAY_SIZE(tuner_init_mt2060); + init = tuner_init_mt2060; + break; + case AF9013_TUNER_MC44S803: + len = ARRAY_SIZE(tuner_init_mc44s803); + init = tuner_init_mc44s803; + break; + case AF9013_TUNER_QT1010: + case AF9013_TUNER_QT1010A: + len = ARRAY_SIZE(tuner_init_qt1010); + init = tuner_init_qt1010; + break; + case AF9013_TUNER_MT2060_2: + len = ARRAY_SIZE(tuner_init_mt2060_2); + init = tuner_init_mt2060_2; + break; + case AF9013_TUNER_TDA18271: + len = ARRAY_SIZE(tuner_init_tda18271); + init = tuner_init_tda18271; + break; + case AF9013_TUNER_UNKNOWN: + default: + len = ARRAY_SIZE(tuner_init_unknown); + init = tuner_init_unknown; + break; + } + + for (i = 0; i < len; i++) { + ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos, + init[i].len, init[i].val); + if (ret) + goto error; + } + + /* set TS mode */ + deb_info("%s: setting ts mode\n", __func__); + tmp0 = 0; /* parallel mode */ + tmp1 = 0; /* serial mode */ + switch (state->config.output_mode) { + case AF9013_OUTPUT_MODE_PARALLEL: + tmp0 = 1; + break; + case AF9013_OUTPUT_MODE_SERIAL: + tmp1 = 1; + break; + case AF9013_OUTPUT_MODE_USB: + /* usb mode for AF9015 */ + default: + break; + } + ret = af9013_write_reg_bits(state, 0xd500, 1, 1, tmp0); /* parallel */ + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd500, 2, 1, tmp1); /* serial */ + if (ret) + goto error; + + /* enable lock led */ + ret = af9013_lock_led(state, 1); + if (ret) + goto error; + +error: + return ret; +} + +static struct dvb_frontend_ops af9013_ops; + +static int af9013_download_firmware(struct af9013_state *state) +{ + int i, len, packets, remainder, ret; + const struct firmware *fw; + u16 addr = 0x5100; /* firmware start address */ + u16 checksum = 0; + u8 val; + u8 fw_params[4]; + u8 *data; + u8 *fw_file = AF9013_DEFAULT_FIRMWARE; + + msleep(100); + /* check whether firmware is already running */ + ret = af9013_read_reg(state, 0x98be, &val); + if (ret) + goto error; + else + deb_info("%s: firmware status:%02x\n", __func__, val); + + if (val == 0x0c) /* fw is running, no need for download */ + goto exit; + + info("found a '%s' in cold state, will try to load a firmware", + af9013_ops.info.name); + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, &state->i2c->dev); + if (ret) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details" \ + " on firmware-problems. (%d)", + fw_file, ret); + goto error; + } + + info("downloading firmware from file '%s'", fw_file); + + /* calc checksum */ + for (i = 0; i < fw->size; i++) + checksum += fw->data[i]; + + fw_params[0] = checksum >> 8; + fw_params[1] = checksum & 0xff; + fw_params[2] = fw->size >> 8; + fw_params[3] = fw->size & 0xff; + + /* write fw checksum & size */ + ret = af9013_write_ofsm_regs(state, 0x50fc, + fw_params, sizeof(fw_params)); + if (ret) + goto error_release; + + #define FW_PACKET_MAX_DATA 16 + + packets = fw->size / FW_PACKET_MAX_DATA; + remainder = fw->size % FW_PACKET_MAX_DATA; + len = FW_PACKET_MAX_DATA; + for (i = 0; i <= packets; i++) { + if (i == packets) /* set size of the last packet */ + len = remainder; + + data = (fw->data + i * FW_PACKET_MAX_DATA); + ret = af9013_write_ofsm_regs(state, addr, data, len); + addr += FW_PACKET_MAX_DATA; + + if (ret) { + err("firmware download failed at %d with %d", i, ret); + goto error_release; + } + } + + #undef FW_PACKET_MAX_DATA + + /* request boot firmware */ + ret = af9013_write_reg(state, 0xe205, 1); + if (ret) + goto error_release; + + for (i = 0; i < 15; i++) { + msleep(100); + + /* check firmware status */ + ret = af9013_read_reg(state, 0x98be, &val); + if (ret) + goto error_release; + + deb_info("%s: firmware status:%02x\n", __func__, val); + + if (val == 0x0c || val == 0x04) /* success or fail */ + break; + } + + if (val == 0x04) { + err("firmware did not run"); + ret = -1; + } else if (val != 0x0c) { + err("firmware boot timeout"); + ret = -1; + } + +error_release: + release_firmware(fw); +error: +exit: + if (!ret) + info("found a '%s' in warm state.", af9013_ops.info.name); + return ret; +} + +static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + int ret; + struct af9013_state *state = fe->demodulator_priv; + deb_info("%s: enable:%d\n", __func__, enable); + + if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) + ret = af9013_write_reg_bits(state, 0xd417, 3, 1, enable); + else + ret = af9013_write_reg_bits(state, 0xd607, 2, 1, enable); + + return ret; +} + +static void af9013_release(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops af9013_ops; + +struct dvb_frontend *af9013_attach(const struct af9013_config *config, + struct i2c_adapter *i2c) +{ + int ret; + struct af9013_state *state = NULL; + u8 buf[3], i; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config, config, sizeof(struct af9013_config)); + + /* chip version */ + ret = af9013_read_reg_bits(state, 0xd733, 4, 4, &buf[2]); + if (ret) + goto error; + + /* ROM version */ + for (i = 0; i < 2; i++) { + ret = af9013_read_reg(state, 0x116b + i, &buf[i]); + if (ret) + goto error; + } + deb_info("%s: chip version:%d ROM version:%d.%d\n", __func__, + buf[2], buf[0], buf[1]); + + /* download firmware */ + if (state->config.output_mode != AF9013_OUTPUT_MODE_USB) { + ret = af9013_download_firmware(state); + if (ret) + goto error; + } + + /* firmware version */ + for (i = 0; i < 3; i++) { + ret = af9013_read_reg(state, 0x5103 + i, &buf[i]); + if (ret) + goto error; + } + info("firmware version:%d.%d.%d", buf[0], buf[1], buf[2]); + + /* settings for mp2if */ + if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) { + /* AF9015 split PSB to 1.5k + 0.5k */ + ret = af9013_write_reg_bits(state, 0xd50b, 2, 1, 1); + } else { + /* AF9013 change the output bit to data7 */ + ret = af9013_write_reg_bits(state, 0xd500, 3, 1, 1); + if (ret) + goto error; + /* AF9013 set mpeg to full speed */ + ret = af9013_write_reg_bits(state, 0xd502, 4, 1, 1); + } + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd520, 4, 1, 1); + if (ret) + goto error; + + /* set GPIOs */ + for (i = 0; i < sizeof(state->config.gpio); i++) { + ret = af9013_set_gpio(state, i, state->config.gpio[i]); + if (ret) + goto error; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &af9013_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(af9013_attach); + +static struct dvb_frontend_ops af9013_ops = { + .info = { + .name = "Afatech AF9013 DVB-T", + .type = FE_OFDM, + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 250000, + .frequency_tolerance = 0, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .release = af9013_release, + .init = af9013_init, + .sleep = af9013_sleep, + .i2c_gate_ctrl = af9013_i2c_gate_ctrl, + + .set_frontend = af9013_set_frontend, + .get_frontend = af9013_get_frontend, + + .get_tune_settings = af9013_get_tune_settings, + + .read_status = af9013_read_status, + .read_ber = af9013_read_ber, + .read_signal_strength = af9013_read_signal_strength, + .read_snr = af9013_read_snr, + .read_ucblocks = af9013_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/af9013.h b/linux/drivers/media/dvb/frontends/af9013.h new file mode 100644 index 000000000..28b90c91c --- /dev/null +++ b/linux/drivers/media/dvb/frontends/af9013.h @@ -0,0 +1,107 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _AF9013_H_ +#define _AF9013_H_ + +#include <linux/dvb/frontend.h> + +enum af9013_ts_mode { + AF9013_OUTPUT_MODE_PARALLEL, + AF9013_OUTPUT_MODE_SERIAL, + AF9013_OUTPUT_MODE_USB, /* only for AF9015 */ +}; + +enum af9013_tuner { + AF9013_TUNER_MXL5003D = 3, /* MaxLinear */ + AF9013_TUNER_MXL5005D = 13, /* MaxLinear */ + AF9013_TUNER_MXL5005R = 30, /* MaxLinear */ + AF9013_TUNER_ENV77H11D5 = 129, /* Panasonic */ + AF9013_TUNER_MT2060 = 130, /* Microtune */ + AF9013_TUNER_MC44S803 = 133, /* Freescale */ + AF9013_TUNER_QT1010 = 134, /* Quantek */ + AF9013_TUNER_UNKNOWN = 140, /* for can tuners ? */ + AF9013_TUNER_MT2060_2 = 147, /* Microtune */ + AF9013_TUNER_TDA18271 = 156, /* NXP */ + AF9013_TUNER_QT1010A = 162, /* Quantek */ +}; + +/* AF9013/5 GPIOs (mostly guessed) + demod#1-gpio#0 - set demod#2 i2c-addr for dual devices + demod#1-gpio#1 - xtal setting (?) + demod#1-gpio#3 - tuner#1 + demod#2-gpio#0 - tuner#2 + demod#2-gpio#1 - xtal setting (?) +*/ +#define AF9013_GPIO_ON (1 << 0) +#define AF9013_GPIO_EN (1 << 1) +#define AF9013_GPIO_O (1 << 2) +#define AF9013_GPIO_I (1 << 3) + +#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN) +#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) + +#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN) +#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) + +struct af9013_config { + /* demodulator's I2C address */ + u8 demod_address; + + /* frequencies in kHz */ + u32 adc_clock; + + /* tuner ID */ + u8 tuner; + + /* tuner IF */ + u16 tuner_if; + + /* TS data output mode */ + u8 output_mode:2; + + /* RF spectrum inversion */ + u8 rf_spec_inv:1; + + /* API version */ + u8 api_version[4]; + + /* GPIOs */ + u8 gpio[4]; +}; + + +#if defined(CONFIG_DVB_AF9013) || \ + (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE)) +extern struct dvb_frontend *af9013_attach(const struct af9013_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *af9013_attach( +const struct af9013_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_AF9013 */ + +#endif /* _AF9013_H_ */ diff --git a/linux/drivers/media/dvb/frontends/af9013_priv.h b/linux/drivers/media/dvb/frontends/af9013_priv.h new file mode 100644 index 000000000..b62e4cc9b --- /dev/null +++ b/linux/drivers/media/dvb/frontends/af9013_priv.h @@ -0,0 +1,869 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _AF9013_PRIV_ +#define _AF9013_PRIV_ + +#define LOG_PREFIX "af9013" +extern int debug; + +#define dprintk(var, level, args...) \ + do { if ((var & level)) printk(args); } while (0) + +#define debug_dump(b, l, func) {\ + int loop_; \ + for (loop_ = 0; loop_ < l; loop_++) \ + func("%02x ", b[loop_]); \ + func("\n");\ +} + +#define deb_info(args...) dprintk(debug, 0x01, args) + +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +#define AF9013_DEFAULT_FIRMWARE "dvb-fe-af9013.fw" + +struct regdesc { + u16 addr; + u8 pos:4; + u8 len:4; + u8 val; +}; + +struct snr_table { + u32 val; + u8 snr; +}; + +/* QPSK SNR lookup table */ +static struct snr_table qpsk_snr_table[] = { + { 0x0b4771, 0 }, + { 0x0c1aed, 1 }, + { 0x0d0d27, 2 }, + { 0x0e4d19, 3 }, + { 0x0e5da8, 4 }, + { 0x107097, 5 }, + { 0x116975, 6 }, + { 0x1252d9, 7 }, + { 0x131fa4, 8 }, + { 0x13d5e1, 9 }, + { 0x148e53, 10 }, + { 0x15358b, 11 }, + { 0x15dd29, 12 }, + { 0x168112, 13 }, + { 0x170b61, 14 }, + { 0xffffff, 15 }, +}; + +/* QAM16 SNR lookup table */ +static struct snr_table qam16_snr_table[] = { + { 0x05eb62, 5 }, + { 0x05fecf, 6 }, + { 0x060b80, 7 }, + { 0x062501, 8 }, + { 0x064865, 9 }, + { 0x069604, 10 }, + { 0x06f356, 11 }, + { 0x07706a, 12 }, + { 0x0804d3, 13 }, + { 0x089d1a, 14 }, + { 0x093e3d, 15 }, + { 0x09e35d, 16 }, + { 0x0a7c3c, 17 }, + { 0x0afaf8, 18 }, + { 0x0b719d, 19 }, + { 0xffffff, 20 }, +}; + +/* QAM64 SNR lookup table */ +static struct snr_table qam64_snr_table[] = { + { 0x03109b, 12 }, + { 0x0310d4, 13 }, + { 0x031920, 14 }, + { 0x0322d0, 15 }, + { 0x0339fc, 16 }, + { 0x0364a1, 17 }, + { 0x038bcc, 18 }, + { 0x03c7d3, 19 }, + { 0x0408cc, 20 }, + { 0x043bed, 21 }, + { 0x048061, 22 }, + { 0x04be95, 23 }, + { 0x04fa7d, 24 }, + { 0x052405, 25 }, + { 0x05570d, 26 }, + { 0xffffff, 27 }, +}; + +static struct regdesc ofsm_init[] = { + { 0xd73a, 0, 8, 0xa1 }, + { 0xd73b, 0, 8, 0x1f }, + { 0xd73c, 4, 4, 0x0a }, + { 0xd732, 3, 1, 0x00 }, + { 0xd731, 4, 2, 0x03 }, + { 0xd73d, 7, 1, 0x01 }, + { 0xd740, 0, 1, 0x00 }, + { 0xd740, 1, 1, 0x00 }, + { 0xd740, 2, 1, 0x00 }, + { 0xd740, 3, 1, 0x01 }, + { 0xd3c1, 4, 1, 0x01 }, + { 0xd3a2, 0, 8, 0x00 }, + { 0xd3a3, 0, 8, 0x04 }, + { 0xd305, 0, 8, 0x32 }, + { 0xd306, 0, 8, 0x10 }, + { 0xd304, 0, 8, 0x04 }, + { 0x9112, 0, 1, 0x01 }, + { 0x911d, 0, 1, 0x01 }, + { 0x911a, 0, 1, 0x01 }, + { 0x911b, 0, 1, 0x01 }, + { 0x9bce, 0, 4, 0x02 }, + { 0x9116, 0, 1, 0x01 }, + { 0x9bd1, 0, 1, 0x01 }, + { 0xd2e0, 0, 8, 0xd0 }, + { 0xd2e9, 0, 4, 0x0d }, + { 0xd38c, 0, 8, 0xfc }, + { 0xd38d, 0, 8, 0x00 }, + { 0xd38e, 0, 8, 0x7e }, + { 0xd38f, 0, 8, 0x00 }, + { 0xd390, 0, 8, 0x2f }, + { 0xd145, 4, 1, 0x01 }, + { 0xd1a9, 4, 1, 0x01 }, + { 0xd158, 5, 3, 0x01 }, + { 0xd159, 0, 6, 0x06 }, + { 0xd167, 0, 8, 0x00 }, + { 0xd168, 0, 4, 0x07 }, + { 0xd1c3, 5, 3, 0x00 }, + { 0xd1c4, 0, 6, 0x00 }, + { 0xd1c5, 0, 7, 0x10 }, + { 0xd1c6, 0, 3, 0x02 }, + { 0xd080, 2, 5, 0x03 }, + { 0xd081, 4, 4, 0x09 }, + { 0xd098, 4, 4, 0x0f }, + { 0xd098, 0, 4, 0x03 }, + { 0xdbc0, 3, 1, 0x01 }, + { 0xdbc0, 4, 1, 0x01 }, + { 0xdbc7, 0, 8, 0x08 }, + { 0xdbc8, 4, 4, 0x00 }, + { 0xdbc9, 0, 5, 0x01 }, + { 0xd280, 0, 8, 0xe0 }, + { 0xd281, 0, 8, 0xff }, + { 0xd282, 0, 8, 0xff }, + { 0xd283, 0, 8, 0xc3 }, + { 0xd284, 0, 8, 0xff }, + { 0xd285, 0, 4, 0x01 }, + { 0xd0f0, 0, 7, 0x1a }, + { 0xd0f1, 4, 1, 0x01 }, + { 0xd0f2, 0, 8, 0x0c }, + { 0xd103, 0, 4, 0x08 }, + { 0xd0f8, 0, 7, 0x20 }, + { 0xd111, 5, 1, 0x00 }, + { 0xd111, 6, 1, 0x00 }, + { 0x910b, 0, 8, 0x0a }, + { 0x9115, 0, 8, 0x02 }, + { 0x910c, 0, 8, 0x02 }, + { 0x910d, 0, 8, 0x08 }, + { 0x910e, 0, 8, 0x0a }, + { 0x9bf6, 0, 8, 0x06 }, + { 0x9bf8, 0, 8, 0x02 }, + { 0x9bf7, 0, 8, 0x05 }, + { 0x9bf9, 0, 8, 0x0f }, + { 0x9bfc, 0, 8, 0x13 }, + { 0x9bd3, 0, 8, 0xff }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, +}; + +/* Panasonic ENV77H11D5 tuner init + AF9013_TUNER_ENV77H11D5 = 129 */ +static struct regdesc tuner_init_env77h11d5[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x03 }, + { 0x9bbe, 0, 8, 0x01 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x00 }, + { 0x9be3, 0, 8, 0x00 }, + { 0xd015, 0, 8, 0x50 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0xdf }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x44 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0xeb }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0xf4 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bba, 0, 8, 0xf9 }, + { 0x9bc3, 0, 8, 0xdf }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0xeb }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bc9, 0, 8, 0x52 }, + { 0xd011, 0, 8, 0x3c }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0xf7 }, + { 0xd014, 0, 2, 0x02 }, + { 0xd040, 0, 8, 0x0b }, + { 0xd041, 0, 2, 0x02 }, + { 0xd042, 0, 8, 0x4d }, + { 0xd043, 0, 2, 0x00 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, +}; + +/* Microtune MT2060 tuner init + AF9013_TUNER_MT2060 = 130 */ +static struct regdesc tuner_init_mt2060[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x07 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x00 }, + { 0x9be3, 0, 8, 0x00 }, + { 0x9bbe, 0, 1, 0x00 }, + { 0x9bcc, 0, 1, 0x00 }, + { 0x9bb9, 0, 8, 0x75 }, + { 0x9bcd, 0, 8, 0x24 }, + { 0x9bff, 0, 8, 0x30 }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x32 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x36 }, + { 0xd00d, 0, 2, 0x03 }, + { 0xd00a, 0, 8, 0x35 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x07 }, + { 0x9bc8, 0, 8, 0x90 }, + { 0x9bc3, 0, 8, 0x0f }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x36 }, + { 0x9bc6, 0, 8, 0x03 }, + { 0x9bba, 0, 8, 0xc9 }, + { 0x9bc9, 0, 8, 0x79 }, + { 0xd011, 0, 8, 0x10 }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0x45 }, + { 0xd014, 0, 2, 0x03 }, + { 0xd040, 0, 8, 0x98 }, + { 0xd041, 0, 2, 0x00 }, + { 0xd042, 0, 8, 0xcf }, + { 0xd043, 0, 2, 0x03 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0xcc }, + { 0x9be4, 0, 8, 0xa0 }, + { 0x9bbd, 0, 8, 0x8e }, + { 0x9be2, 0, 8, 0x4d }, + { 0x9bee, 0, 1, 0x01 }, +}; + +/* Microtune MT2060 tuner init + AF9013_TUNER_MT2060_2 = 147 */ +static struct regdesc tuner_init_mt2060_2[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x06 }, + { 0x9bbe, 0, 8, 0x01 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x32 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x36 }, + { 0xd00d, 0, 2, 0x03 }, + { 0xd00a, 0, 8, 0x35 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x07 }, + { 0x9bc8, 0, 8, 0x90 }, + { 0x9bc3, 0, 8, 0x0f }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x36 }, + { 0x9bc6, 0, 8, 0x03 }, + { 0x9bba, 0, 8, 0xc9 }, + { 0x9bc9, 0, 8, 0x79 }, + { 0xd011, 0, 8, 0x10 }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0x45 }, + { 0xd014, 0, 2, 0x03 }, + { 0xd040, 0, 8, 0x98 }, + { 0xd041, 0, 2, 0x00 }, + { 0xd042, 0, 8, 0xcf }, + { 0xd043, 0, 2, 0x03 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 8, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x96 }, + { 0xd054, 0, 8, 0x46 }, + { 0xd045, 7, 1, 0x00 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, +}; + +/* MaxLinear MXL5003 tuner init + AF9013_TUNER_MXL5003D = 3 */ +static struct regdesc tuner_init_mxl5003d[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x09 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x00 }, + { 0x9be3, 0, 8, 0x00 }, + { 0x9bfc, 0, 8, 0x0f }, + { 0x9bf6, 0, 8, 0x01 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0xd015, 0, 8, 0x33 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x40 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x6c }, + { 0xd007, 0, 2, 0x00 }, + { 0xd00c, 0, 8, 0x3d }, + { 0xd00d, 0, 2, 0x00 }, + { 0xd00a, 0, 8, 0x45 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x07 }, + { 0x9bc8, 0, 8, 0x52 }, + { 0x9bc3, 0, 8, 0x0f }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x3d }, + { 0x9bc6, 0, 8, 0x00 }, + { 0x9bba, 0, 8, 0xa2 }, + { 0x9bc9, 0, 8, 0xa0 }, + { 0xd011, 0, 8, 0x56 }, + { 0xd012, 0, 2, 0x00 }, + { 0xd013, 0, 8, 0x50 }, + { 0xd014, 0, 2, 0x00 }, + { 0xd040, 0, 8, 0x56 }, + { 0xd041, 0, 2, 0x00 }, + { 0xd042, 0, 8, 0x50 }, + { 0xd043, 0, 2, 0x00 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 8, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, +}; + +/* MaxLinear MXL5005 tuner init + AF9013_TUNER_MXL5005D = 13 + AF9013_TUNER_MXL5005R = 30 */ +static struct regdesc tuner_init_mxl5005[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x07 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x01 }, + { 0x9be3, 0, 8, 0x01 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x28 }, + { 0x9bff, 0, 8, 0x24 }, + { 0xd015, 0, 8, 0x40 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x40 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x73 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0xfa }, + { 0xd00d, 0, 2, 0x01 }, + { 0xd00a, 0, 8, 0xff }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x23 }, + { 0x9bc8, 0, 8, 0x55 }, + { 0x9bc3, 0, 8, 0x01 }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0xfa }, + { 0x9bc6, 0, 8, 0x01 }, + { 0x9bba, 0, 8, 0xff }, + { 0x9bc9, 0, 8, 0xff }, + { 0x9bd3, 0, 8, 0x95 }, + { 0xd011, 0, 8, 0x70 }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0xfb }, + { 0xd014, 0, 2, 0x01 }, + { 0xd040, 0, 8, 0x70 }, + { 0xd041, 0, 2, 0x01 }, + { 0xd042, 0, 8, 0xfb }, + { 0xd043, 0, 2, 0x01 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0x93 }, + { 0x9be4, 0, 8, 0xfe }, + { 0x9bbd, 0, 8, 0x63 }, + { 0x9be2, 0, 8, 0xfe }, + { 0x9bee, 0, 1, 0x01 }, +}; + +/* Quantek QT1010 tuner init + AF9013_TUNER_QT1010 = 134 + AF9013_TUNER_QT1010A = 162 */ +static struct regdesc tuner_init_qt1010[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x09 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x01 }, + { 0x9be3, 0, 8, 0x01 }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x28 }, + { 0x9bff, 0, 8, 0x20 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x99 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x0f }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0x50 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x00 }, + { 0x9bc8, 0, 8, 0x00 }, + { 0x9bc3, 0, 8, 0x0f }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x0f }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bba, 0, 8, 0xc5 }, + { 0x9bc9, 0, 8, 0xff }, + { 0xd011, 0, 8, 0x58 }, + { 0xd012, 0, 2, 0x02 }, + { 0xd013, 0, 8, 0x89 }, + { 0xd014, 0, 2, 0x01 }, + { 0xd040, 0, 8, 0x58 }, + { 0xd041, 0, 2, 0x02 }, + { 0xd042, 0, 8, 0x89 }, + { 0xd043, 0, 2, 0x01 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0xcd }, + { 0x9be4, 0, 8, 0xbb }, + { 0x9bbd, 0, 8, 0x93 }, + { 0x9be2, 0, 8, 0x80 }, + { 0x9bee, 0, 1, 0x01 }, +}; + +/* Freescale MC44S803 tuner init + AF9013_TUNER_MC44S803 = 133 */ +static struct regdesc tuner_init_mc44s803[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x06 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x00 }, + { 0x9be3, 0, 8, 0x00 }, + { 0x9bf6, 0, 8, 0x01 }, + { 0x9bf8, 0, 8, 0x02 }, + { 0x9bf9, 0, 8, 0x02 }, + { 0x9bfc, 0, 8, 0x1f }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x24 }, + { 0x9bff, 0, 8, 0x24 }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x01 }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x7b }, + { 0xd007, 0, 2, 0x00 }, + { 0xd00c, 0, 8, 0x7c }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0xfe }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x08 }, + { 0x9bc8, 0, 8, 0x9a }, + { 0x9bc3, 0, 8, 0x01 }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x7c }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bba, 0, 8, 0xfc }, + { 0x9bc9, 0, 8, 0xaa }, + { 0xd011, 0, 8, 0x6b }, + { 0xd012, 0, 2, 0x00 }, + { 0xd013, 0, 8, 0x88 }, + { 0xd014, 0, 2, 0x02 }, + { 0xd040, 0, 8, 0x6b }, + { 0xd041, 0, 2, 0x00 }, + { 0xd042, 0, 8, 0x7c }, + { 0xd043, 0, 2, 0x02 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0x9e }, + { 0x9be4, 0, 8, 0xff }, + { 0x9bbd, 0, 8, 0x9e }, + { 0x9be2, 0, 8, 0x25 }, + { 0x9bee, 0, 1, 0x01 }, + { 0xd73b, 3, 1, 0x00 }, +}; + +/* unknown, probably for tin can tuner, tuner init + AF9013_TUNER_UNKNOWN = 140 */ +static struct regdesc tuner_init_unknown[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x02 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x01 }, + { 0x9be3, 0, 8, 0x01 }, + { 0xd1a0, 1, 1, 0x00 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x18 }, + { 0x9bff, 0, 8, 0x2c }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0xdf }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x44 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x00 }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0xf6 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bba, 0, 8, 0xf9 }, + { 0x9bc8, 0, 8, 0xaa }, + { 0x9bc3, 0, 8, 0xdf }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x00 }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bc9, 0, 8, 0xf0 }, + { 0xd011, 0, 8, 0x3c }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0xf7 }, + { 0xd014, 0, 2, 0x02 }, + { 0xd040, 0, 8, 0x0b }, + { 0xd041, 0, 2, 0x02 }, + { 0xd042, 0, 8, 0x4d }, + { 0xd043, 0, 2, 0x00 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, +}; + +/* NXP TDA18271 tuner init + AF9013_TUNER_TDA18271 = 156 */ +static struct regdesc tuner_init_tda18271[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x04 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x01 }, + { 0x9be3, 0, 8, 0x01 }, + { 0xd1a0, 1, 1, 0x00 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x18 }, + { 0x9bff, 0, 8, 0x2c }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0xdf }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x44 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x00 }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0xf6 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bba, 0, 8, 0xf9 }, + { 0x9bc8, 0, 8, 0xaa }, + { 0x9bc3, 0, 8, 0xdf }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x00 }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bc9, 0, 8, 0xf0 }, + { 0xd011, 0, 8, 0x3c }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0xf7 }, + { 0xd014, 0, 2, 0x02 }, + { 0xd040, 0, 8, 0x0b }, + { 0xd041, 0, 2, 0x02 }, + { 0xd042, 0, 8, 0x4d }, + { 0xd043, 0, 2, 0x00 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0xa8 }, + { 0x9be4, 0, 8, 0x7f }, + { 0x9bbd, 0, 8, 0xa8 }, + { 0x9be2, 0, 8, 0x20 }, + { 0x9bee, 0, 1, 0x01 }, +}; + +#endif /* _AF9013_PRIV_ */ diff --git a/linux/drivers/media/dvb/frontends/au8522.c b/linux/drivers/media/dvb/frontends/au8522.c index d946c8beb..78e270ee0 100644 --- a/linux/drivers/media/dvb/frontends/au8522.c +++ b/linux/drivers/media/dvb/frontends/au8522.c @@ -1,7 +1,7 @@ /* Auvitek AU8522 QAM/8VSB demodulator driver - Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> 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 diff --git a/linux/drivers/media/dvb/frontends/au8522.h b/linux/drivers/media/dvb/frontends/au8522.h index afc9af26e..595915ade 100644 --- a/linux/drivers/media/dvb/frontends/au8522.h +++ b/linux/drivers/media/dvb/frontends/au8522.h @@ -1,7 +1,7 @@ /* Auvitek AU8522 QAM/8VSB demodulator driver - Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> 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 diff --git a/linux/drivers/media/dvb/frontends/cx22702.c b/linux/drivers/media/dvb/frontends/cx22702.c index cc1db4e37..9430e03db 100644 --- a/linux/drivers/media/dvb/frontends/cx22702.c +++ b/linux/drivers/media/dvb/frontends/cx22702.c @@ -7,7 +7,7 @@ Copyright (C) 2001-2002 Convergence Integrated Media GmbH Holger Waechtler <holger@convergence.de> - Copyright (C) 2004 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2004 Steven Toth <stoth@linuxtv.org> 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 diff --git a/linux/drivers/media/dvb/frontends/cx22702.h b/linux/drivers/media/dvb/frontends/cx22702.h index 8af766a31..b1e465c6c 100644 --- a/linux/drivers/media/dvb/frontends/cx22702.h +++ b/linux/drivers/media/dvb/frontends/cx22702.h @@ -7,7 +7,7 @@ Copyright (C) 2001-2002 Convergence Integrated Media GmbH Holger Waechtler <holger@convergence.de> - Copyright (C) 2004 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2004 Steven Toth <stoth@linuxtv.org> 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 diff --git a/linux/drivers/media/dvb/frontends/cx24110.h b/linux/drivers/media/dvb/frontends/cx24110.h index 1792adb23..fdcceee91 100644 --- a/linux/drivers/media/dvb/frontends/cx24110.h +++ b/linux/drivers/media/dvb/frontends/cx24110.h @@ -33,12 +33,17 @@ struct cx24110_config u8 demod_address; }; -static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) { - int r = 0; - u8 buf[] = {(u8) (val>>24), (u8) (val>>16), (u8) (val>>8)}; +static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) +{ + u8 buf[] = { + (u8)((val >> 24) & 0xff), + (u8)((val >> 16) & 0xff), + (u8)((val >> 8) & 0xff) + }; + if (fe->ops.write) - r = fe->ops.write(fe, buf, 3); - return r; + return fe->ops.write(fe, buf, 3); + return 0; } #if defined(CONFIG_DVB_CX24110) || (defined(CONFIG_DVB_CX24110_MODULE) && defined(MODULE)) diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index 83b6793de..970e4df08 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -1,7 +1,7 @@ /* * Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver * - * Copyright (C) 2005 Steven Toth <stoth@hauppauge.com> + * Copyright (C) 2005 Steven Toth <stoth@linuxtv.org> * * Support for KWorld DVB-S 100 by Vadim Catana <skystar@moldova.cc> * @@ -1083,8 +1083,8 @@ struct dvb_frontend* cx24123_attach(const struct cx24123_config* config, if (config->dont_use_pll) cx24123_repeater_mode(state, 1, 0); - strncpy(state->tuner_i2c_adapter.name, - "CX24123 tuner I2C bus", I2C_NAME_SIZE); + strlcpy(state->tuner_i2c_adapter.name, "CX24123 tuner I2C bus", + sizeof(state->tuner_i2c_adapter.name)); state->tuner_i2c_adapter.class = I2C_CLASS_TV_DIGITAL, state->tuner_i2c_adapter.algo = &cx24123_tuner_i2c_algo; state->tuner_i2c_adapter.algo_data = NULL; diff --git a/linux/drivers/media/dvb/frontends/cx24123.h b/linux/drivers/media/dvb/frontends/cx24123.h index 81ebc3d2f..cc6b411d6 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.h +++ b/linux/drivers/media/dvb/frontends/cx24123.h @@ -1,7 +1,7 @@ /* Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver - Copyright (C) 2005 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2005 Steven Toth <stoth@linuxtv.org> 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 diff --git a/linux/drivers/media/dvb/frontends/dib0070.h b/linux/drivers/media/dvb/frontends/dib0070.h index 3eedfdf50..21f2c5161 100644 --- a/linux/drivers/media/dvb/frontends/dib0070.h +++ b/linux/drivers/media/dvb/frontends/dib0070.h @@ -41,6 +41,7 @@ struct dib0070_config { extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); +extern u16 dib0070_wbd_offset(struct dvb_frontend *); #else static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, @@ -49,9 +50,14 @@ static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } + +static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} #endif extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, uint8_t open); -extern u16 dib0070_wbd_offset(struct dvb_frontend *); #endif diff --git a/linux/drivers/media/dvb/frontends/dib7000m.c b/linux/drivers/media/dvb/frontends/dib7000m.c index 188f6e853..763407d06 100644 --- a/linux/drivers/media/dvb/frontends/dib7000m.c +++ b/linux/drivers/media/dvb/frontends/dib7000m.c @@ -1320,7 +1320,10 @@ struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum di } EXPORT_SYMBOL(dib7000m_get_i2c_master); -int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000m_config cfg[]) +#if 0 /* keep */ +/* used with some prototype boards */ +int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, + u8 default_addr, struct dib7000m_config cfg[]) { struct dib7000m_state st = { .i2c_adap = i2c }; int k = 0; @@ -1365,6 +1368,7 @@ int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau return 0; } EXPORT_SYMBOL(dib7000m_i2c_enumeration); +#endif static struct dvb_frontend_ops dib7000m_ops; struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg) diff --git a/linux/drivers/media/dvb/frontends/dib7000p.c b/linux/drivers/media/dvb/frontends/dib7000p.c index c74d373b6..ebb7594e3 100644 --- a/linux/drivers/media/dvb/frontends/dib7000p.c +++ b/linux/drivers/media/dvb/frontends/dib7000p.c @@ -1359,7 +1359,8 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, /* Ensure the output mode remains at the previous default if it's * not specifically set by the caller. */ - if (st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) + if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && + (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) st->cfg.output_mode = OUTMODE_MPEG2_FIFO; demod = &st->demod; diff --git a/linux/drivers/media/dvb/frontends/dib7000p.h b/linux/drivers/media/dvb/frontends/dib7000p.h index 07c4d12ed..3e8126857 100644 --- a/linux/drivers/media/dvb/frontends/dib7000p.h +++ b/linux/drivers/media/dvb/frontends/dib7000p.h @@ -41,6 +41,14 @@ struct dib7000p_config { extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); +extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *, + enum dibx000_i2c_interface, + int); +extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, + int no_of_demods, u8 default_addr, + struct dib7000p_config cfg[]); +extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); +extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value); #else static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, @@ -49,13 +57,36 @@ static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -#endif -extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); +static inline +struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe, + enum dibx000_i2c_interface i, int x) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, + int no_of_demods, u8 default_addr, + struct dib7000p_config cfg[]) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +extern int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +extern int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} +#endif -extern struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); -extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); -extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value); #endif diff --git a/linux/drivers/media/dvb/frontends/drx397xD.c b/linux/drivers/media/dvb/frontends/drx397xD.c index f1ea891d7..7a2b2d461 100644 --- a/linux/drivers/media/dvb/frontends/drx397xD.c +++ b/linux/drivers/media/dvb/frontends/drx397xD.c @@ -39,35 +39,32 @@ static const char mod_name[] = "drx397xD"; #define F_SET_0D0h 1 #define F_SET_0D4h 2 -typedef enum fw_ix { +enum fw_ix { #define _FW_ENTRY(a, b) b #include "drx397xD_fw.h" -} fw_ix_t; +}; /* chip specifics */ struct drx397xD_state { struct i2c_adapter *i2c; struct dvb_frontend frontend; struct drx397xD_config config; - fw_ix_t chip_rev; + enum fw_ix chip_rev; int flags; u32 bandwidth_parm; /* internal bandwidth conversions */ u32 f_osc; /* w90: actual osc frequency [Hz] */ }; -/******************************************************************************* - * Firmware - ******************************************************************************/ - +/* Firmware */ static const char *blob_name[] = { #define _BLOB_ENTRY(a, b) a #include "drx397xD_fw.h" }; -typedef enum blob_ix { +enum blob_ix { #define _BLOB_ENTRY(a, b) b #include "drx397xD_fw.h" -} blob_ix_t; +}; static struct { const char *name; @@ -86,7 +83,7 @@ static struct { }; /* use only with writer lock aquired */ -static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix) +static void _drx_release_fw(struct drx397xD_state *s, enum fw_ix ix) { memset(&fw[ix].data[0], 0, sizeof(fw[0].data)); if (fw[ix].file) @@ -95,7 +92,7 @@ static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix) static void drx_release_fw(struct drx397xD_state *s) { - fw_ix_t ix = s->chip_rev; + enum fw_ix ix = s->chip_rev; pr_debug("%s\n", __func__); @@ -108,7 +105,7 @@ static void drx_release_fw(struct drx397xD_state *s) write_unlock(&fw[ix].lock); } -static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix) +static int drx_load_fw(struct drx397xD_state *s, enum fw_ix ix) { const u8 *data; size_t size, len; @@ -176,26 +173,28 @@ static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix) goto exit_corrupt; } } while (i < size); - exit_corrupt: + +exit_corrupt: printk(KERN_ERR "%s: Firmware is corrupt\n", mod_name); - exit_err: +exit_err: _drx_release_fw(s, ix); fw[ix].refcnt--; - exit_ok: +exit_ok: fw[ix].refcnt++; write_unlock(&fw[ix].lock); + return rc; } -/******************************************************************************* - * i2c bus IO - ******************************************************************************/ - -static int write_fw(struct drx397xD_state *s, blob_ix_t ix) +/* i2c bus IO */ +static int write_fw(struct drx397xD_state *s, enum blob_ix ix) { - struct i2c_msg msg = {.addr = s->config.demod_address,.flags = 0 }; const u8 *data; int len, rc = 0, i = 0; + struct i2c_msg msg = { + .addr = s->config.demod_address, + .flags = 0 + }; if (ix < 0 || ix >= ARRAY_SIZE(blob_name)) { pr_debug("%s drx_fw_ix_t out of range\n", __func__); @@ -230,33 +229,33 @@ static int write_fw(struct drx397xD_state *s, blob_ix_t ix) goto exit_rc; } } - exit_rc: +exit_rc: read_unlock(&fw[s->chip_rev].lock); + return 0; } /* Function is not endian safe, use the RD16 wrapper below */ -static int _read16(struct drx397xD_state *s, u32 i2c_adr) +static int _read16(struct drx397xD_state *s, __le32 i2c_adr) { int rc; u8 a[4]; - u16 v; + __le16 v; struct i2c_msg msg[2] = { { - .addr = s->config.demod_address, - .flags = 0, - .buf = a, - .len = sizeof(a) - } - , { - .addr = s->config.demod_address, - .flags = I2C_M_RD, - .buf = (u8 *) & v, - .len = sizeof(v) - } + .addr = s->config.demod_address, + .flags = 0, + .buf = a, + .len = sizeof(a) + }, { + .addr = s->config.demod_address, + .flags = I2C_M_RD, + .buf = (u8 *)&v, + .len = sizeof(v) + } }; - *(u32 *) a = i2c_adr; + *(__le32 *) a = i2c_adr; rc = i2c_transfer(s->i2c, msg, 2); if (rc != 2) @@ -266,7 +265,7 @@ static int _read16(struct drx397xD_state *s, u32 i2c_adr) } /* Function is not endian safe, use the WR16.. wrappers below */ -static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val) +static int _write16(struct drx397xD_state *s, __le32 i2c_adr, __le16 val) { u8 a[6]; int rc; @@ -277,28 +276,28 @@ static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val) .len = sizeof(a) }; - *(u32 *) a = i2c_adr; - *(u16 *) & a[4] = val; + *(__le32 *)a = i2c_adr; + *(__le16 *)&a[4] = val; rc = i2c_transfer(s->i2c, &msg, 1); if (rc != 1) return -EIO; + return 0; } -#define WR16(ss,adr, val) \ +#define WR16(ss, adr, val) \ _write16(ss, I2C_ADR_C0(adr), cpu_to_le16(val)) -#define WR16_E0(ss,adr, val) \ +#define WR16_E0(ss, adr, val) \ _write16(ss, I2C_ADR_E0(adr), cpu_to_le16(val)) -#define RD16(ss,adr) \ +#define RD16(ss, adr) \ _read16(ss, I2C_ADR_C0(adr)) -#define EXIT_RC( cmd ) if ( (rc = (cmd)) < 0) goto exit_rc - -/******************************************************************************* - * Tuner callback - ******************************************************************************/ +#define EXIT_RC(cmd) \ + if ((rc = (cmd)) < 0) \ + goto exit_rc +/* Tuner callback */ static int PLL_Set(struct drx397xD_state *s, struct dvb_frontend_parameters *fep, int *df_tuner) { @@ -332,10 +331,7 @@ static int PLL_Set(struct drx397xD_state *s, return 0; } -/******************************************************************************* - * Demodulator helper functions - ******************************************************************************/ - +/* Demodulator helper functions */ static int SC_WaitForReady(struct drx397xD_state *s) { int cnt = 1000; @@ -348,6 +344,7 @@ static int SC_WaitForReady(struct drx397xD_state *s) if (rc == 0) return 0; } + return -1; } @@ -362,6 +359,7 @@ static int SC_SendCommand(struct drx397xD_state *s, int cmd) rc = RD16(s, 0x820042); if ((rc & 0xffff) == 0xffff) return -1; + return 0; } @@ -384,6 +382,7 @@ static int HI_Command(struct drx397xD_state *s, u16 cmd) if (rc < 0) return rc; } while (--cnt); + return rc; } @@ -393,13 +392,14 @@ static int HI_CfgCommand(struct drx397xD_state *s) pr_debug("%s\n", __func__); WR16(s, 0x420033, 0x3973); - WR16(s, 0x420034, s->config.w50); // code 4, log 4 - WR16(s, 0x420035, s->config.w52); // code 15, log 9 + WR16(s, 0x420034, s->config.w50); /* code 4, log 4 */ + WR16(s, 0x420035, s->config.w52); /* code 15, log 9 */ WR16(s, 0x420036, s->config.demod_address << 1); - WR16(s, 0x420037, s->config.w56); // code (set_i2c ?? initX 1 ), log 1 -// WR16(s, 0x420033, 0x3973); + WR16(s, 0x420037, s->config.w56); /* code (set_i2c ?? initX 1 ), log 1 */ + /* WR16(s, 0x420033, 0x3973); */ if ((s->config.w56 & 8) == 0) return HI_Command(s, 3); + return WR16(s, 0x420032, 0x3); } @@ -469,7 +469,7 @@ static int SetCfgIfAgc(struct drx397xD_state *s, struct drx397xD_CfgIfAgc *agc) i = slowIncrDecLUT_15272[rem / 28]; EXIT_RC(WR16(s, 0x0c2002b, i)); rc = WR16(s, 0x0c2002c, i); - exit_rc: +exit_rc: return rc; } @@ -499,7 +499,7 @@ static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc) rc &= ~2; break; case 0: - // loc_8000659 + /* loc_8000659 */ s->config.w9C &= ~2; EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); EXIT_RC(RD16(s, 0x0c20010)); @@ -523,7 +523,8 @@ static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc) rc |= 2; } rc = WR16(s, 0x0c20013, rc); - exit_rc: + +exit_rc: return rc; } @@ -603,7 +604,8 @@ static int CorrectSysClockDeviation(struct drx397xD_state *s) s->config.f_osc * 1000, clk - s->config.f_osc * 1000); } rc = WR16(s, 0x08200e8, 0); - exit_rc: + +exit_rc: return rc; } @@ -621,17 +623,17 @@ static int ConfigureMPEGOutput(struct drx397xD_state *s, int type) si &= ~1; bp = 0x200; } - if (s->config.w9A == 0) { + if (s->config.w9A == 0) si |= 0x80; - } else { + else si &= ~0x80; - } EXIT_RC(WR16(s, 0x2150045, 0)); EXIT_RC(WR16(s, 0x2150010, si)); EXIT_RC(WR16(s, 0x2150011, bp)); rc = WR16(s, 0x2150012, (type == 0 ? 0xfff : 0)); - exit_rc: + +exit_rc: return rc; } @@ -695,7 +697,7 @@ static int drx_tune(struct drx397xD_state *s, } loc_800111A: #endif - set_tuner: +set_tuner: rc = PLL_Set(s, fep, &df_tuner); if (rc < 0) { @@ -872,16 +874,16 @@ static int drx_tune(struct drx397xD_state *s, rc = WR16(s, 0x2010012, 0); if (rc < 0) goto exit_rc; - // QPSK QAM16 QAM64 - ebx = 0x19f; // 62 - ebp = 0x1fb; // 15 - v20 = 0x16a; // 62 - v1E = 0x195; // 62 - v16 = 0x1bb; // 15 - v14 = 0x1ef; // 15 - v12 = 5; // 16 - v10 = 5; // 16 - v0E = 5; // 16 + /* QPSK QAM16 QAM64 */ + ebx = 0x19f; /* 62 */ + ebp = 0x1fb; /* 15 */ + v20 = 0x16a; /* 62 */ + v1E = 0x195; /* 62 */ + v16 = 0x1bb; /* 15 */ + v14 = 0x1ef; /* 15 */ + v12 = 5; /* 16 */ + v10 = 5; /* 16 */ + v0E = 5; /* 16 */ } switch (fep->u.ofdm.constellation) { @@ -1034,17 +1036,17 @@ static int drx_tune(struct drx397xD_state *s, case BANDWIDTH_8_MHZ: /* 0 */ case BANDWIDTH_AUTO: rc = WR16(s, 0x0c2003f, 0x32); - s->bandwidth_parm = ebx = 0x8b8249; // 9142857 + s->bandwidth_parm = ebx = 0x8b8249; edx = 0; break; case BANDWIDTH_7_MHZ: rc = WR16(s, 0x0c2003f, 0x3b); - s->bandwidth_parm = ebx = 0x7a1200; // 8000000 + s->bandwidth_parm = ebx = 0x7a1200; edx = 0x4807; break; case BANDWIDTH_6_MHZ: rc = WR16(s, 0x0c2003f, 0x47); - s->bandwidth_parm = ebx = 0x68a1b6; // 6857142 + s->bandwidth_parm = ebx = 0x68a1b6; edx = 0x0f07; break; }; @@ -1102,8 +1104,11 @@ static int drx_tune(struct drx397xD_state *s, WR16(s, 0x0820040, 1); SC_SendCommand(s, 1); -// rc = WR16(s, 0x2150000, 1); -// if (rc < 0) goto exit_rc; +#if 0 + rc = WR16(s, 0x2150000, 1); + if (rc < 0) + goto exit_rc; +#endif rc = WR16(s, 0x2150000, 2); rc = WR16(s, 0x2150016, a); @@ -1111,7 +1116,8 @@ static int drx_tune(struct drx397xD_state *s, rc = WR16(s, 0x2150036, 0); rc = WR16(s, 0x2150000, 1); s->config.d60 = 2; - exit_rc: + +exit_rc: return rc; } @@ -1144,18 +1150,18 @@ static int drx397x_init(struct dvb_frontend *fe) /* HI_CfgCommand */ s->config.w50 = 4; - s->config.w52 = 9; // 0xf; + s->config.w52 = 9; - s->config.f_if = 42800000; /* d14: intermediate frequency [Hz] */ - s->config.f_osc = 48000; /* s66 : oscillator frequency [kHz] */ - s->config.w92 = 12000; // 20000; + s->config.f_if = 42800000; /* d14: intermediate frequency [Hz] */ + s->config.f_osc = 48000; /* s66 : oscillator frequency [kHz] */ + s->config.w92 = 12000; s->config.w9C = 0x000e; s->config.w9E = 0x0000; /* ConfigureMPEGOutput params */ s->config.wA0 = 4; - s->config.w98 = 1; // 0; + s->config.w98 = 1; s->config.w9A = 1; /* get chip revision */ @@ -1312,7 +1318,7 @@ static int drx397x_init(struct dvb_frontend *fe) rc = WR16(s, 0x0c20012, 1); } - write_DRXD_InitFE_1: +write_DRXD_InitFE_1: rc = write_fw(s, DRXD_InitFE_1); if (rc < 0) @@ -1390,7 +1396,8 @@ static int drx397x_init(struct dvb_frontend *fe) #endif s->config.d60 = 1; s->config.d48 = 1; - error: + +error: return rc; } @@ -1405,7 +1412,8 @@ static int drx397x_set_frontend(struct dvb_frontend *fe, { struct drx397xD_state *s = fe->demodulator_priv; - s->config.s20d24 = 1; // 0; + s->config.s20d24 = 1; + return drx_tune(s, params); } @@ -1416,18 +1424,21 @@ static int drx397x_get_tune_settings(struct dvb_frontend *fe, fe_tune_settings->min_delay_ms = 10000; fe_tune_settings->step_size = 0; fe_tune_settings->max_drift = 0; + return 0; } -static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status) +static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct drx397xD_state *s = fe->demodulator_priv; int lockstat; GetLockStatus(s, &lockstat); +#if 0 /* TODO */ -// if (lockstat & 1) -// CorrectSysClockDeviation(s); + if (lockstat & 1) + CorrectSysClockDeviation(s); +#endif *status = 0; if (lockstat & 2) { @@ -1435,9 +1446,8 @@ static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status) ConfigureMPEGOutput(s, 1); *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; } - if (lockstat & 4) { + if (lockstat & 4) *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; - } return 0; } @@ -1445,16 +1455,18 @@ static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status) static int drx397x_read_ber(struct dvb_frontend *fe, unsigned int *ber) { *ber = 0; + return 0; } -static int drx397x_read_snr(struct dvb_frontend *fe, u16 * snr) +static int drx397x_read_snr(struct dvb_frontend *fe, u16 *snr) { *snr = 0; + return 0; } -static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct drx397xD_state *s = fe->demodulator_priv; int rc; @@ -1480,6 +1492,7 @@ static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength) * The following does the same but with less rounding errors: */ *strength = ~(7720 + (rc * 30744 >> 10)); + return 0; } @@ -1487,6 +1500,7 @@ static int drx397x_read_ucblocks(struct dvb_frontend *fe, unsigned int *ucblocks) { *ucblocks = 0; + return 0; } @@ -1515,22 +1529,22 @@ static struct dvb_frontend_ops drx397x_ops = { .frequency_max = 855250000, .frequency_stepsize = 166667, .frequency_tolerance = 0, - .caps = /* 0x0C01B2EAE */ - FE_CAN_FEC_1_2 | // = 0x2, - FE_CAN_FEC_2_3 | // = 0x4, - FE_CAN_FEC_3_4 | // = 0x8, - FE_CAN_FEC_5_6 | // = 0x20, - FE_CAN_FEC_7_8 | // = 0x80, - FE_CAN_FEC_AUTO | // = 0x200, - FE_CAN_QPSK | // = 0x400, - FE_CAN_QAM_16 | // = 0x800, - FE_CAN_QAM_64 | // = 0x2000, - FE_CAN_QAM_AUTO | // = 0x10000, - FE_CAN_TRANSMISSION_MODE_AUTO | // = 0x20000, - FE_CAN_GUARD_INTERVAL_AUTO | // = 0x80000, - FE_CAN_HIERARCHY_AUTO | // = 0x100000, - FE_CAN_RECOVER | // = 0x40000000, - FE_CAN_MUTE_TS // = 0x80000000 + .caps = /* 0x0C01B2EAE */ + FE_CAN_FEC_1_2 | /* = 0x2, */ + FE_CAN_FEC_2_3 | /* = 0x4, */ + FE_CAN_FEC_3_4 | /* = 0x8, */ + FE_CAN_FEC_5_6 | /* = 0x20, */ + FE_CAN_FEC_7_8 | /* = 0x80, */ + FE_CAN_FEC_AUTO | /* = 0x200, */ + FE_CAN_QPSK | /* = 0x400, */ + FE_CAN_QAM_16 | /* = 0x800, */ + FE_CAN_QAM_64 | /* = 0x2000, */ + FE_CAN_QAM_AUTO | /* = 0x10000, */ + FE_CAN_TRANSMISSION_MODE_AUTO | /* = 0x20000, */ + FE_CAN_GUARD_INTERVAL_AUTO | /* = 0x80000, */ + FE_CAN_HIERARCHY_AUTO | /* = 0x100000, */ + FE_CAN_RECOVER | /* = 0x40000000, */ + FE_CAN_MUTE_TS /* = 0x80000000 */ }, .release = drx397x_release, @@ -1551,33 +1565,35 @@ static struct dvb_frontend_ops drx397x_ops = { struct dvb_frontend *drx397xD_attach(const struct drx397xD_config *config, struct i2c_adapter *i2c) { - struct drx397xD_state *s = NULL; + struct drx397xD_state *state; /* allocate memory for the internal state */ - s = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL); - if (s == NULL) + state = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL); + if (!state) goto error; /* setup the state */ - s->i2c = i2c; - memcpy(&s->config, config, sizeof(struct drx397xD_config)); + state->i2c = i2c; + memcpy(&state->config, config, sizeof(struct drx397xD_config)); /* check if the demod is there */ - if (RD16(s, 0x2410019) < 0) + if (RD16(state, 0x2410019) < 0) goto error; /* create dvb_frontend */ - memcpy(&s->frontend.ops, &drx397x_ops, sizeof(struct dvb_frontend_ops)); - s->frontend.demodulator_priv = s; + memcpy(&state->frontend.ops, &drx397x_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +error: + kfree(state); - return &s->frontend; - error: - kfree(s); return NULL; } +EXPORT_SYMBOL(drx397xD_attach); MODULE_DESCRIPTION("Micronas DRX397xD DVB-T Frontend"); MODULE_AUTHOR("Henk Vergonet"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(drx397xD_attach); diff --git a/linux/drivers/media/dvb/frontends/drx397xD.h b/linux/drivers/media/dvb/frontends/drx397xD.h index 2ad1ea0cc..ba05d1729 100644 --- a/linux/drivers/media/dvb/frontends/drx397xD.h +++ b/linux/drivers/media/dvb/frontends/drx397xD.h @@ -28,7 +28,7 @@ #define DRX_F_OFFSET 36000000 #define I2C_ADR_C0(x) \ -( (u32)cpu_to_le32( \ +( cpu_to_le32( \ (u32)( \ (((u32)(x) & (u32)0x000000ffUL) ) | \ (((u32)(x) & (u32)0x0000ff00UL) << 16) | \ @@ -38,7 +38,7 @@ ) #define I2C_ADR_E0(x) \ -( (u32)cpu_to_le32( \ +( cpu_to_le32( \ (u32)( \ (((u32)(x) & (u32)0x000000ffUL) ) | \ (((u32)(x) & (u32)0x0000ff00UL) << 16) | \ diff --git a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c index fed09dfb2..db8a937cc 100644 --- a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c +++ b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c @@ -75,9 +75,10 @@ static int dvb_dummy_fe_get_frontend(struct dvb_frontend* fe, struct dvb_fronten static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - if (fe->ops->tuner_ops->set_params) { - fe->ops->tuner_ops->set_params(fe, p); - if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, p); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); } return 0; @@ -131,7 +132,7 @@ error: static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops; -struct dvb_frontend* dvb_dummy_fe_qpsk_attach() +struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) { struct dvb_dummy_fe_state* state = NULL; @@ -151,7 +152,7 @@ error: static struct dvb_frontend_ops dvb_dummy_fe_qam_ops; -struct dvb_frontend* dvb_dummy_fe_qam_attach() +struct dvb_frontend *dvb_dummy_fe_qam_attach(void) { struct dvb_dummy_fe_state* state = NULL; diff --git a/linux/drivers/media/dvb/frontends/lgs8gl5.c b/linux/drivers/media/dvb/frontends/lgs8gl5.c new file mode 100644 index 000000000..380ae1868 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/lgs8gl5.c @@ -0,0 +1,478 @@ +/* + Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver + + Copyright (C) 2008 Sirius International (Hong Kong) Limited + Timothy Lee <timothy.lee@siriushk.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include "dvb_frontend.h" +#include "lgs8gl5.h" + + +#define REG_RESET 0x02 +#define REG_RESET_OFF 0x01 +#define REG_03 0x03 +#define REG_04 0x04 +#define REG_07 0x07 +#define REG_09 0x09 +#define REG_0A 0x0a +#define REG_0B 0x0b +#define REG_0C 0x0c +#define REG_37 0x37 +#define REG_STRENGTH 0x4b +#define REG_STRENGTH_MASK 0x7f +#define REG_STRENGTH_CARRIER 0x80 +#define REG_INVERSION 0x7c +#define REG_INVERSION_ON 0x80 +#define REG_7D 0x7d +#define REG_7E 0x7e +#define REG_A2 0xa2 +#define REG_STATUS 0xa4 +#define REG_STATUS_SYNC 0x04 +#define REG_STATUS_LOCK 0x01 + + +struct lgs8gl5_state { + struct i2c_adapter *i2c; + const struct lgs8gl5_config *config; + struct dvb_frontend frontend; +}; + + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "lgs8gl5: " args); \ + } while (0) + + +/* Writes into demod's register */ +static int +lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf[] = {reg, data}; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) + dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n", + __func__, reg, data, ret); + return (ret != 1) ? -1 : 0; +} + + +/* Reads from demod's register */ +static int +lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg) +{ + int ret; + u8 b0[] = {reg}; + u8 b1[] = {0}; + struct i2c_msg msg[2] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 1 + }, + { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) + return -EIO; + + return b1[0]; +} + + +static int +lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data) +{ + lgs8gl5_read_reg(state, reg); + lgs8gl5_write_reg(state, reg, data); + return 0; +} + + +/* Writes into alternate device's register */ +/* TODO: Find out what that device is for! */ +static int +lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data) +{ + int ret; + u8 b0[] = {reg}; + u8 b1[] = {0}; + u8 b2[] = {reg, data}; + struct i2c_msg msg[3] = { + { + .addr = state->config->demod_address + 2, + .flags = 0, + .buf = b0, + .len = 1 + }, + { + .addr = state->config->demod_address + 2, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + }, + { + .addr = state->config->demod_address + 2, + .flags = 0, + .buf = b2, + .len = 2 + }, + }; + + ret = i2c_transfer(state->i2c, msg, 3); + return (ret != 3) ? -1 : 0; +} + + +static void +lgs8gl5_soft_reset(struct lgs8gl5_state *state) +{ + u8 val; + + dprintk("%s\n", __func__); + + val = lgs8gl5_read_reg(state, REG_RESET); + lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF); + lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF); + msleep(5); +} + +#if 0 +static int +lgs8gl5_set_inversion(struct lgs8gl5_state *state, int inversion) +{ + u8 val; + + dprintk("%s\n", __func__); + + switch (inversion) { + case INVERSION_AUTO: + return -EOPNOTSUPP; + case INVERSION_ON: + val = lgs8gl5_read_reg(state, REG_INVERSION); + return lgs8gl5_write_reg(state, REG_INVERSION, + val | REG_INVERSION_ON); + case INVERSION_OFF: + val = lgs8gl5_read_reg(state, REG_INVERSION); + return lgs8gl5_write_reg(state, REG_INVERSION, + val & ~REG_INVERSION_ON); + default: + return -EINVAL; + } +} +#endif + +/* Starts demodulation */ +static void +lgs8gl5_start_demod(struct lgs8gl5_state *state) +{ + u8 val; + int n; + + dprintk("%s\n", __func__); + + lgs8gl5_update_alt_reg(state, 0xc2, 0x28); + lgs8gl5_soft_reset(state); + lgs8gl5_update_reg(state, REG_07, 0x10); + lgs8gl5_update_reg(state, REG_07, 0x10); + lgs8gl5_write_reg(state, REG_09, 0x0e); + lgs8gl5_write_reg(state, REG_0A, 0xe5); + lgs8gl5_write_reg(state, REG_0B, 0x35); + lgs8gl5_write_reg(state, REG_0C, 0x30); + + lgs8gl5_update_reg(state, REG_03, 0x00); + lgs8gl5_update_reg(state, REG_7E, 0x01); + lgs8gl5_update_alt_reg(state, 0xc5, 0x00); + lgs8gl5_update_reg(state, REG_04, 0x02); + lgs8gl5_update_reg(state, REG_37, 0x01); + lgs8gl5_soft_reset(state); + + /* Wait for carrier */ + for (n = 0; n < 10; n++) { + val = lgs8gl5_read_reg(state, REG_STRENGTH); + dprintk("Wait for carrier[%d] 0x%02X\n", n, val); + if (val & REG_STRENGTH_CARRIER) + break; + msleep(4); + } + if (!(val & REG_STRENGTH_CARRIER)) + return; + + /* Wait for lock */ + for (n = 0; n < 20; n++) { + val = lgs8gl5_read_reg(state, REG_STATUS); + dprintk("Wait for lock[%d] 0x%02X\n", n, val); + if (val & REG_STATUS_LOCK) + break; + msleep(12); + } + if (!(val & REG_STATUS_LOCK)) + return; + + lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2)); + lgs8gl5_soft_reset(state); +} + + +static int +lgs8gl5_init(struct dvb_frontend *fe) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + lgs8gl5_update_alt_reg(state, 0xc2, 0x28); + lgs8gl5_soft_reset(state); + lgs8gl5_update_reg(state, REG_07, 0x10); + lgs8gl5_update_reg(state, REG_07, 0x10); + lgs8gl5_write_reg(state, REG_09, 0x0e); + lgs8gl5_write_reg(state, REG_0A, 0xe5); + lgs8gl5_write_reg(state, REG_0B, 0x35); + lgs8gl5_write_reg(state, REG_0C, 0x30); + + return 0; +} + + +static int +lgs8gl5_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); + u8 flags = lgs8gl5_read_reg(state, REG_STATUS); + + *status = 0; + + if ((level & REG_STRENGTH_MASK) > 0) + *status |= FE_HAS_SIGNAL; + if (level & REG_STRENGTH_CARRIER) + *status |= FE_HAS_CARRIER; + if (flags & REG_STATUS_SYNC) + *status |= FE_HAS_SYNC; + if (flags & REG_STATUS_LOCK) + *status |= FE_HAS_LOCK; + + return 0; +} + + +static int +lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + + return 0; +} + + +static int +lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); + *signal_strength = (level & REG_STRENGTH_MASK) << 8; + + return 0; +} + + +static int +lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); + *snr = (level & REG_STRENGTH_MASK) << 8; + + return 0; +} + + +static int +lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + + return 0; +} + + +static int +lgs8gl5_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (p->u.ofdm.bandwidth != BANDWIDTH_8_MHZ) + return -EINVAL; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, p); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* lgs8gl5_set_inversion(state, p->inversion); */ + + lgs8gl5_start_demod(state); + + return 0; +} + + +static int +lgs8gl5_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + u8 inv = lgs8gl5_read_reg(state, REG_INVERSION); + struct dvb_ofdm_parameters *o = &p->u.ofdm; + + p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF; + + o->code_rate_HP = FEC_1_2; + o->code_rate_LP = FEC_7_8; + o->guard_interval = GUARD_INTERVAL_1_32; + o->transmission_mode = TRANSMISSION_MODE_2K; + o->constellation = QAM_64; + o->hierarchy_information = HIERARCHY_NONE; + o->bandwidth = BANDWIDTH_8_MHZ; + + return 0; +} + + +static int +lgs8gl5_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 240; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + + +static void +lgs8gl5_release(struct dvb_frontend *fe) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + kfree(state); +} + + +static struct dvb_frontend_ops lgs8gl5_ops; + + +struct dvb_frontend* +lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c) +{ + struct lgs8gl5_state *state = NULL; + + dprintk("%s\n", __func__); + + /* Allocate memory for the internal state */ + state = kmalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* Setup the state */ + state->config = config; + state->i2c = i2c; + + /* Check if the demod is there */ + if (lgs8gl5_read_reg(state, REG_RESET) < 0) + goto error; + + /* Create dvb_frontend */ + memcpy(&state->frontend.ops, &lgs8gl5_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(lgs8gl5_attach); + + +static struct dvb_frontend_ops lgs8gl5_ops = { + .info = { + .name = "Legend Silicon LGS-8GL5 DMB-TH", + .type = FE_OFDM, + .frequency_min = 474000000, + .frequency_max = 858000000, + .frequency_stepsize = 10000, + .frequency_tolerance = 0, + .caps = FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_BANDWIDTH_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER + }, + + .release = lgs8gl5_release, + + .init = lgs8gl5_init, + + .set_frontend = lgs8gl5_set_frontend, + .get_frontend = lgs8gl5_get_frontend, + .get_tune_settings = lgs8gl5_get_tune_settings, + + .read_status = lgs8gl5_read_status, + .read_ber = lgs8gl5_read_ber, + .read_signal_strength = lgs8gl5_read_signal_strength, + .read_snr = lgs8gl5_read_snr, + .read_ucblocks = lgs8gl5_read_ucblocks, +}; + + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver"); +MODULE_AUTHOR("Timothy Lee"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/lgs8gl5.h b/linux/drivers/media/dvb/frontends/lgs8gl5.h new file mode 100644 index 000000000..d14176787 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/lgs8gl5.h @@ -0,0 +1,45 @@ +/* + Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver + + Copyright (C) 2008 Sirius International (Hong Kong) Limited + Timothy Lee <timothy.lee@siriushk.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LGS8GL5_H +#define LGS8GL5_H + +#include <linux/dvb/frontend.h> + +struct lgs8gl5_config { + /* the demodulator's i2c address */ + u8 demod_address; +}; + +#if defined(CONFIG_DVB_LGS8GL5) || \ + (defined(CONFIG_DVB_LGS8GL5_MODULE) && defined(MODULE)) +extern struct dvb_frontend *lgs8gl5_attach( + const struct lgs8gl5_config *config, struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *lgs8gl5_attach( + const struct lgs8gl5_config *config, struct i2c_adapter *i2c) { + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LGS8GL5 */ + +#endif /* LGS8GL5_H */ diff --git a/linux/drivers/media/dvb/frontends/nxt200x.c b/linux/drivers/media/dvb/frontends/nxt200x.c index a079fa824..846350bc4 100644 --- a/linux/drivers/media/dvb/frontends/nxt200x.c +++ b/linux/drivers/media/dvb/frontends/nxt200x.c @@ -81,7 +81,7 @@ static int i2c_writebytes (struct nxt200x_state* state, u8 addr, u8 *buf, u8 len return 0; } -static u8 i2c_readbytes (struct nxt200x_state* state, u8 addr, u8* buf, u8 len) +static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len) { int err; struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len }; @@ -112,7 +112,7 @@ static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg, return 0; } -static u8 nxt200x_readbytes (struct nxt200x_state* state, u8 reg, u8* buf, u8 len) +static int nxt200x_readbytes(struct nxt200x_state *state, u8 reg, u8 *buf, u8 len) { u8 reg2 [] = { reg }; diff --git a/linux/drivers/media/dvb/frontends/or51211.c b/linux/drivers/media/dvb/frontends/or51211.c index 6afe12aac..16cf2fdd5 100644 --- a/linux/drivers/media/dvb/frontends/or51211.c +++ b/linux/drivers/media/dvb/frontends/or51211.c @@ -88,7 +88,7 @@ static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf, return 0; } -static u8 i2c_readbytes (struct or51211_state* state, u8 reg, u8* buf, int len) +static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len) { int err; struct i2c_msg msg; diff --git a/linux/drivers/media/dvb/frontends/s5h1409.c b/linux/drivers/media/dvb/frontends/s5h1409.c index 5ddb2dca3..7500a1c53 100644 --- a/linux/drivers/media/dvb/frontends/s5h1409.c +++ b/linux/drivers/media/dvb/frontends/s5h1409.c @@ -1,7 +1,7 @@ /* Samsung S5H1409 VSB/QAM demodulator driver - Copyright (C) 2006 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2006 Steven Toth <stoth@linuxtv.org> 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 @@ -404,6 +404,7 @@ static int s5h1409_enable_modulation(struct dvb_frontend* fe, break; case QAM_64: case QAM_256: + case QAM_AUTO: dprintk("%s() QAM_AUTO (64/256)\n", __func__); if (state->if_freq != S5H1409_QAM_IF_FREQ) s5h1409_set_if_freq(fe, S5H1409_QAM_IF_FREQ); diff --git a/linux/drivers/media/dvb/frontends/s5h1409.h b/linux/drivers/media/dvb/frontends/s5h1409.h index 59f433596..d1a1d2eb8 100644 --- a/linux/drivers/media/dvb/frontends/s5h1409.h +++ b/linux/drivers/media/dvb/frontends/s5h1409.h @@ -1,7 +1,7 @@ /* Samsung S5H1409 VSB/QAM demodulator driver - Copyright (C) 2006 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2006 Steven Toth <stoth@linuxtv.org> 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 diff --git a/linux/drivers/media/dvb/frontends/s5h1411.c b/linux/drivers/media/dvb/frontends/s5h1411.c index cff360ce1..2da1a3763 100644 --- a/linux/drivers/media/dvb/frontends/s5h1411.c +++ b/linux/drivers/media/dvb/frontends/s5h1411.c @@ -1,7 +1,7 @@ /* Samsung S5H1411 VSB/QAM demodulator driver - Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> 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 @@ -488,6 +488,7 @@ static int s5h1411_enable_modulation(struct dvb_frontend *fe, break; case QAM_64: case QAM_256: + case QAM_AUTO: dprintk("%s() QAM_AUTO (64/256)\n", __func__); s5h1411_set_if_freq(fe, state->config->qam_if); s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x00, 0x0171); diff --git a/linux/drivers/media/dvb/frontends/s5h1411.h b/linux/drivers/media/dvb/frontends/s5h1411.h index 1855f64ed..7d542bc00 100644 --- a/linux/drivers/media/dvb/frontends/s5h1411.h +++ b/linux/drivers/media/dvb/frontends/s5h1411.h @@ -1,7 +1,7 @@ /* Samsung S5H1411 VSB/QAM demodulator driver - Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> 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 diff --git a/linux/drivers/media/dvb/frontends/s5h1420.c b/linux/drivers/media/dvb/frontends/s5h1420.c index 996f37704..b768da972 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.c +++ b/linux/drivers/media/dvb/frontends/s5h1420.c @@ -946,7 +946,8 @@ struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, state->frontend.demodulator_priv = state; /* create tuner i2c adapter */ - strncpy(state->tuner_i2c_adapter.name, "S5H1420-PN1010 tuner I2C bus", I2C_NAME_SIZE); + strlcpy(state->tuner_i2c_adapter.name, "S5H1420-PN1010 tuner I2C bus", + sizeof(state->tuner_i2c_adapter.name)); state->tuner_i2c_adapter.class = I2C_CLASS_TV_DIGITAL, state->tuner_i2c_adapter.algo = &s5h1420_tuner_i2c_algo; state->tuner_i2c_adapter.algo_data = NULL; diff --git a/linux/drivers/media/dvb/frontends/sp887x.c b/linux/drivers/media/dvb/frontends/sp887x.c index 4543609e1..559509ab4 100644 --- a/linux/drivers/media/dvb/frontends/sp887x.c +++ b/linux/drivers/media/dvb/frontends/sp887x.c @@ -337,7 +337,8 @@ static int sp887x_setup_frontend_parameters (struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { struct sp887x_state* state = fe->demodulator_priv; - int actual_freq, err; + unsigned actual_freq; + int err; u16 val, reg0xc05; if (p->u.ofdm.bandwidth != BANDWIDTH_8_MHZ && diff --git a/linux/drivers/media/dvb/frontends/tda10048.c b/linux/drivers/media/dvb/frontends/tda10048.c index 7c718e247..7a882faf5 100644 --- a/linux/drivers/media/dvb/frontends/tda10048.c +++ b/linux/drivers/media/dvb/frontends/tda10048.c @@ -1,7 +1,7 @@ /* NXP TDA10048HN DVB OFDM demodulator driver - Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> 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 @@ -303,7 +303,7 @@ static int tda10048_firmware_upload(struct dvb_frontend *fe) if (fw->size != TDA10048_DEFAULT_FIRMWARE_SIZE) { printk(KERN_ERR "%s: firmware incorrect size\n", __func__); - return -EIO; + ret = -EIO; } else { printk(KERN_INFO "%s: firmware uploading\n", __func__); diff --git a/linux/drivers/media/dvb/frontends/tda10048.h b/linux/drivers/media/dvb/frontends/tda10048.h index 2b5c78e62..0457b2460 100644 --- a/linux/drivers/media/dvb/frontends/tda10048.h +++ b/linux/drivers/media/dvb/frontends/tda10048.h @@ -1,7 +1,7 @@ /* NXP TDA10048HN DVB OFDM demodulator driver - Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> 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 diff --git a/linux/drivers/media/dvb/frontends/tdhd1.h b/linux/drivers/media/dvb/frontends/tdhd1.h new file mode 100644 index 000000000..51f170678 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/tdhd1.h @@ -0,0 +1,73 @@ +/* + * tdhd1.h - ALPS TDHD1-204A tuner support + * + * Copyright (C) 2008 Oliver Endriss <o.endriss@gmx.de> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * The project's page is at http://www.linuxtv.org + */ + +#ifndef TDHD1_H +#define TDHD1_H + +#include "tda1004x.h" + +static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name); + +static struct tda1004x_config alps_tdhd1_204a_config = { + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = alps_tdhd1_204_request_firmware +}; + +static int alps_tdhd1_204a_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct i2c_adapter *i2c = fe->tuner_priv; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + u32 div; + + div = (params->frequency + 36166666) / 166666; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85; + + if (params->frequency >= 174000000 && params->frequency <= 230000000) + data[3] = 0x02; + else if (params->frequency >= 470000000 && params->frequency <= 823000000) + data[3] = 0x0C; + else if (params->frequency > 823000000 && params->frequency <= 862000000) + data[3] = 0x8C; + else + return -EINVAL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(i2c, &msg, 1) != 1) + return -EIO; + + return 0; +} + +#endif /* TDHD1_H */ diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index e7a8ac0c4..9da260fe3 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -4,7 +4,7 @@ * Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org> * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as + * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS IS" @@ -40,6 +40,8 @@ struct usb_device_id smsusb_id_table[] = { .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B }, { USB_DEVICE(0x2040, 0x5500), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0x5510), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { USB_DEVICE(0x2040, 0x5580), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { USB_DEVICE(0x2040, 0x5590), @@ -87,7 +89,7 @@ static struct sms_board sms_boards[] = { .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", }, [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { - .name = "Hauppauge WinTV-Nova-T-MiniStick", + .name = "Hauppauge WinTV MiniStick", .type = SMS_NOVA_B0, .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-01.fw", }, diff --git a/linux/drivers/media/dvb/siano/sms-cards.h b/linux/drivers/media/dvb/siano/sms-cards.h index 83b39bc20..c8f3da6f9 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.h +++ b/linux/drivers/media/dvb/siano/sms-cards.h @@ -4,7 +4,7 @@ * Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org> * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as + * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS IS" diff --git a/linux/drivers/media/dvb/siano/smscoreapi.c b/linux/drivers/media/dvb/siano/smscoreapi.c index 4781997f6..700a0a6a7 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.c +++ b/linux/drivers/media/dvb/siano/smscoreapi.c @@ -8,7 +8,7 @@ * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as + * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS IS" diff --git a/linux/drivers/media/dvb/siano/smscoreapi.h b/linux/drivers/media/dvb/siano/smscoreapi.h index dbedc315f..3bba565d7 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.h +++ b/linux/drivers/media/dvb/siano/smscoreapi.h @@ -6,7 +6,7 @@ * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as + * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS IS" diff --git a/linux/drivers/media/dvb/siano/smsdvb.c b/linux/drivers/media/dvb/siano/smsdvb.c index e64478b4e..f2a6c6f9e 100644 --- a/linux/drivers/media/dvb/siano/smsdvb.c +++ b/linux/drivers/media/dvb/siano/smsdvb.c @@ -6,7 +6,7 @@ * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as + * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS IS" diff --git a/linux/drivers/media/dvb/siano/smsusb.c b/linux/drivers/media/dvb/siano/smsusb.c index 40582bf70..a50ea8259 100644 --- a/linux/drivers/media/dvb/siano/smsusb.c +++ b/linux/drivers/media/dvb/siano/smsusb.c @@ -6,7 +6,7 @@ * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as + * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS IS" diff --git a/linux/drivers/media/dvb/ttpci/Kconfig b/linux/drivers/media/dvb/ttpci/Kconfig index 41b5a988b..867027cea 100644 --- a/linux/drivers/media/dvb/ttpci/Kconfig +++ b/linux/drivers/media/dvb/ttpci/Kconfig @@ -86,6 +86,7 @@ config DVB_BUDGET select DVB_TDA10086 if !DVB_FE_CUSTOMISE select DVB_TDA826X if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard MPEG2 decoder, and without diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 0909eb861..bb63e2121 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -88,6 +88,7 @@ static int budgetpatch; static int wss_cfg_4_3 = 0x4008; static int wss_cfg_16_9 = 0x0007; static int tv_standard; +static int full_ts; module_param_named(debug, av7110_debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); @@ -106,6 +107,8 @@ module_param(volume, int, 0444); MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)"); module_param(budgetpatch, int, 0444); MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)"); +module_param(full_ts, int, 0444); +MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable"); module_param(wss_cfg_4_3, int, 0444); MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"); module_param(wss_cfg_16_9, int, 0444); @@ -116,6 +119,8 @@ MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC"); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static void restart_feeds(struct av7110 *av7110); +static int budget_start_feed(struct dvb_demux_feed *feed); +static int budget_stop_feed(struct dvb_demux_feed *feed); static int av7110_num; @@ -376,9 +381,9 @@ static inline void start_debi_dma(struct av7110 *av7110, int dir, irdebi(av7110, DEBISWAB, addr, 0, len); } -static void debiirq(unsigned long data) +static void debiirq(unsigned long cookie) { - struct av7110 *av7110 = (struct av7110 *) data; + struct av7110 *av7110 = (struct av7110 *)cookie; int type = av7110->debitype; int handle = (type >> 8) & 0x1f; unsigned int xfer = 0; @@ -487,9 +492,9 @@ debi_done: } /* irq from av7110 firmware writing the mailbox register in the DPRAM */ -static void gpioirq(unsigned long data) +static void gpioirq(unsigned long cookie) { - struct av7110 *av7110 = (struct av7110 *) data; + struct av7110 *av7110 = (struct av7110 *)cookie; u32 rxbuf, txbuf; int len; @@ -806,6 +811,9 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) dprintk(4, "%p\n", av7110); + if (av7110->full_ts) + return 0; + if (dvbdmxfilter->type == DMX_TYPE_SEC) { if (hw_sections) { buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) | @@ -854,6 +862,9 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) dprintk(4, "%p\n", av7110); + if (av7110->full_ts) + return 0; + handle = dvbdmxfilter->hw_handle; if (handle >= 32) { printk("%s tried to stop invalid filter %04x, filter type = %x\n", @@ -913,7 +924,7 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) return ret; } - if ((dvbdmxfeed->ts_type & TS_PACKET)) { + if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) { if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000)) ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed); if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000)) @@ -974,7 +985,7 @@ static int av7110_start_feed(struct dvb_demux_feed *feed) if (!demux->dmx.frontend) return -EINVAL; - if (feed->pid > 0x1fff) + if (!av7110->full_ts && feed->pid > 0x1fff) return -EINVAL; if (feed->type == DMX_TYPE_TS) { @@ -1003,7 +1014,12 @@ static int av7110_start_feed(struct dvb_demux_feed *feed) } } - else if (feed->type == DMX_TYPE_SEC) { + if (av7110->full_ts) { + budget_start_feed(feed); + return ret; + } + + if (feed->type == DMX_TYPE_SEC) { int i; for (i = 0; i < demux->filternum; i++) { @@ -1050,7 +1066,12 @@ static int av7110_stop_feed(struct dvb_demux_feed *feed) ret = StopHWFilter(feed->filter); } - if (!ret && feed->type == DMX_TYPE_SEC) { + if (av7110->full_ts) { + budget_stop_feed(feed); + return ret; + } + + if (feed->type == DMX_TYPE_SEC) { for (i = 0; i<demux->filternum; i++) { if (demux->filter[i].state == DMX_STATE_GO && demux->filter[i].filter.parent == &feed->feed.sec) { @@ -1074,6 +1095,7 @@ static void restart_feeds(struct av7110 *av7110) struct dvb_demux *dvbdmx = &av7110->demux; struct dvb_demux_feed *feed; int mode; + int feeding; int i, j; dprintk(4, "%p\n", av7110); @@ -1082,6 +1104,8 @@ static void restart_feeds(struct av7110 *av7110) av7110->playing = 0; av7110->rec_mode = 0; + feeding = av7110->feeding1; /* full_ts mod */ + for (i = 0; i < dvbdmx->feednum; i++) { feed = &dvbdmx->feed[i]; if (feed->state == DMX_STATE_GO) { @@ -1099,6 +1123,8 @@ static void restart_feeds(struct av7110 *av7110) } } + av7110->feeding1 = feeding; /* full_ts mod */ + if (mode) av7110_av_start_play(av7110, mode); } @@ -1197,8 +1223,9 @@ static int start_ts_capture(struct av7110 *budget) if (budget->feeding1) return ++budget->feeding1; - memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH); + memset(budget->grabbing, 0x00, TS_BUFLEN); budget->ttbp = 0; + SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ return ++budget->feeding1; @@ -1233,18 +1260,14 @@ static int budget_stop_feed(struct dvb_demux_feed *feed) return status; } -static void vpeirq(unsigned long data) +static void vpeirq(unsigned long cookie) { - struct av7110 *budget = (struct av7110 *) data; + struct av7110 *budget = (struct av7110 *)cookie; u8 *mem = (u8 *) (budget->grabbing); u32 olddma = budget->ttbp; u32 newdma = saa7146_read(budget->dev, PCI_VDP3); + struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1; - if (!budgetpatch) { - printk("av7110.c: vpeirq() called while budgetpatch disabled!" - " check saa7146 IER register\n"); - BUG(); - } /* nearest lower position divisible by 188 */ newdma -= newdma % 188; @@ -1268,11 +1291,11 @@ static void vpeirq(unsigned long data) if (newdma > olddma) /* no wraparound, dump olddma..newdma */ - dvb_dmx_swfilter_packets(&budget->demux1, mem + olddma, (newdma - olddma) / 188); + dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188); else { /* wraparound, dump olddma..buflen and 0..newdma */ - dvb_dmx_swfilter_packets(&budget->demux1, mem + olddma, (TS_BUFLEN - olddma) / 188); - dvb_dmx_swfilter_packets(&budget->demux1, mem, newdma / 188); + dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188); + dvb_dmx_swfilter_packets(demux, mem, newdma / 188); } } @@ -1294,8 +1317,8 @@ static int av7110_register(struct av7110 *av7110) for (i = 0; i < 32; i++) av7110->handle2filter[i] = NULL; - dvbdemux->filternum = 32; - dvbdemux->feednum = 32; + dvbdemux->filternum = (av7110->full_ts) ? 256 : 32; + dvbdemux->feednum = (av7110->full_ts) ? 256 : 32; dvbdemux->start_feed = av7110_start_feed; dvbdemux->stop_feed = av7110_stop_feed; dvbdemux->write_to_decoder = av7110_write_to_decoder; @@ -1305,7 +1328,7 @@ static int av7110_register(struct av7110 *av7110) dvb_dmx_init(&av7110->demux); av7110->demux.dmx.get_stc = dvb_get_stc; - av7110->dmxdev.filternum = 32; + av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32; av7110->dmxdev.demux = &dvbdemux->dmx; av7110->dmxdev.capabilities = 0; @@ -1422,7 +1445,6 @@ int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val) return i2c_transfer(&av7110->i2c_adap, &msgs, 1); } -#if 0 /* keep */ u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) { u8 mm1[] = {0x00}; @@ -1439,7 +1461,6 @@ u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) return mm2[0]; } -#endif /**************************************************************************** * INITIALIZATION @@ -2256,7 +2277,7 @@ static int frontend_init(struct av7110 *av7110) if (!av7110->fe) { /* FIXME: propagate the failure code from the lower layers */ ret = -ENOMEM; - printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", av7110->dev->pci->vendor, av7110->dev->pci->device, av7110->dev->pci->subsystem_vendor, @@ -2484,7 +2505,47 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, av7110->dvb_adapter.proposed_mac); ret = -ENOMEM; - if (budgetpatch) { + /* full-ts mod? */ + if (full_ts) + av7110->full_ts = true; + + /* check for full-ts flag in eeprom */ + if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) { + u8 flags = i2c_readreg(av7110, 0xaa, 2); + if (flags != 0xff && (flags & 0x01)) + av7110->full_ts = true; + } + + if (av7110->full_ts) { + printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n"); + spin_lock_init(&av7110->feedlock1); + av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, + &av7110->pt); + if (!av7110->grabbing) + goto err_i2c_del_3; + + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_10 | MASK_26)); + + saa7146_write(dev, DD1_INIT, 0x00000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + saa7146_write(dev, BRS_CTRL, 0x60000000); + saa7146_write(dev, MC2, MASK_08 | MASK_24); + + /* dma3 */ + saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); + saa7146_write(dev, BASE_ODD3, 0); + saa7146_write(dev, BASE_EVEN3, 0); + saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); + saa7146_write(dev, PITCH3, TS_WIDTH); + saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); + saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); + saa7146_write(dev, MC2, MASK_04 | MASK_20); + + tasklet_init(&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); + + } else if (budgetpatch) { spin_lock_init(&av7110->feedlock1); av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, &av7110->pt); @@ -2710,11 +2771,13 @@ static int __devexit av7110_detach(struct saa7146_dev* saa) #if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) av7110_ir_exit(av7110); #endif - if (budgetpatch) { - /* Disable RPS1 */ - saa7146_write(saa, MC1, MASK_29); - /* VSYNC LOW (inactive) */ - saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + if (budgetpatch || av7110->full_ts) { + if (budgetpatch) { + /* Disable RPS1 */ + saa7146_write(saa, MC1, MASK_29); + /* VSYNC LOW (inactive) */ + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + } saa7146_write(saa, MC1, MASK_20); /* DMA3 off */ SAA7146_IER_DISABLE(saa, MASK_10); SAA7146_ISR_CLEAR(saa, MASK_10); @@ -2794,7 +2857,7 @@ static void av7110_irq(struct saa7146_dev* dev, u32 *isr) tasklet_schedule(&av7110->gpio_tasklet); } - if ((*isr & MASK_10) && budgetpatch) + if (*isr & MASK_10) tasklet_schedule(&av7110->vpe_tasklet); } diff --git a/linux/drivers/media/dvb/ttpci/av7110.h b/linux/drivers/media/dvb/ttpci/av7110.h index 61e4a7569..e82519e21 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.h +++ b/linux/drivers/media/dvb/ttpci/av7110.h @@ -194,6 +194,7 @@ struct av7110 { unsigned char *grabbing; struct saa7146_pgtable pt; struct tasklet_struct vpe_tasklet; + bool full_ts; int fe_synced; struct mutex pid_mutex; diff --git a/linux/drivers/media/dvb/ttpci/av7110_av.c b/linux/drivers/media/dvb/ttpci/av7110_av.c index 184647ad1..bdc62acf2 100644 --- a/linux/drivers/media/dvb/ttpci/av7110_av.c +++ b/linux/drivers/media/dvb/ttpci/av7110_av.c @@ -788,6 +788,9 @@ int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t l dprintk(2, "av7110:%p, \n", av7110); + if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE) + return 0; + switch (feed->pes_type) { case 0: if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index b7d1f2f18..1032ea778 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -57,6 +57,8 @@ #define SLOTSTATUS_READY 8 #define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + struct budget_av { struct budget budget; struct video_device *vd; @@ -1049,7 +1051,7 @@ static void frontend_init(struct budget_av *budget_av) if (fe == NULL) { printk(KERN_ERR "budget-av: A frontend driver was not found " - "for device %04x/%04x subsystem %04x/%04x\n", + "for device [%04x:%04x] subsystem [%04x:%04x]\n", saa->pci->vendor, saa->pci->device, saa->pci->subsystem_vendor, @@ -1127,7 +1129,9 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio dev->ext_priv = budget_av; - if ((err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE))) { + err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE, + adapter_nr); + if (err) { kfree(budget_av); return err; } diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index f7388e5fc..969877f58 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -34,6 +34,10 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/input.h> +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) +/* Fix compile warning */ +#undef BIT +#endif #include <linux/spinlock.h> #include <media/ir-common.h> @@ -92,6 +96,8 @@ static int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + struct budget_ci_ir { struct input_dev *dev; struct tasklet_struct msp430_irq_tasklet; @@ -1157,7 +1163,7 @@ static void frontend_init(struct budget_ci *budget_ci) } if (budget_ci->budget.dvb_frontend == NULL) { - printk("budget-ci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", budget_ci->budget.dev->pci->vendor, budget_ci->budget.dev->pci->device, budget_ci->budget.dev->pci->subsystem_vendor, @@ -1187,7 +1193,8 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio dev->ext_priv = budget_ci; - err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE); + err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE, + adapter_nr); if (err) goto out2; diff --git a/linux/drivers/media/dvb/ttpci/budget-core.c b/linux/drivers/media/dvb/ttpci/budget-core.c index 6f4ddb643..ba18e56d5 100644 --- a/linux/drivers/media/dvb/ttpci/budget-core.c +++ b/linux/drivers/media/dvb/ttpci/budget-core.c @@ -57,8 +57,6 @@ module_param_named(bufsize, dma_buffer_size, int, 0444); MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - /**************************************************************************** * TT budget / WinTV Nova ****************************************************************************/ @@ -411,7 +409,7 @@ static void budget_unregister(struct budget *budget) int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, struct saa7146_pci_extension_data *info, - struct module *owner) + struct module *owner, short *adapter_nums) { int ret = 0; struct budget_info *bi = info->ext_priv; @@ -474,7 +472,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, - owner, &budget->dev->pci->dev, adapter_nr); + owner, &budget->dev->pci->dev, adapter_nums); if (ret < 0) return ret; diff --git a/linux/drivers/media/dvb/ttpci/budget-patch.c b/linux/drivers/media/dvb/ttpci/budget-patch.c index 5139b481e..0f0bfcad1 100644 --- a/linux/drivers/media/dvb/ttpci/budget-patch.c +++ b/linux/drivers/media/dvb/ttpci/budget-patch.c @@ -39,6 +39,8 @@ #include "bsru6.h" +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + #define budget_patch budget static struct saa7146_extension budget_extension; @@ -116,7 +118,8 @@ static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long DiseqcSendByte(budget, 0xff); else { saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - udelay(12500); + mdelay(12); + udelay(500); saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); } msleep(20); @@ -359,7 +362,7 @@ static void frontend_init(struct budget_patch* budget) } if (budget->dvb_frontend == NULL) { - printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", budget->dev->pci->vendor, budget->dev->pci->device, budget->dev->pci->subsystem_vendor, @@ -591,8 +594,9 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte dprintk(2, "budget: %p\n", budget); - if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) { - kfree (budget); + err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); + if (err) { + kfree(budget); return err; } diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c index 2293d80c6..1638e1d9f 100644 --- a/linux/drivers/media/dvb/ttpci/budget.c +++ b/linux/drivers/media/dvb/ttpci/budget.c @@ -46,11 +46,14 @@ #include "lnbp21.h" #include "bsru6.h" #include "bsbe1.h" +#include "tdhd1.h" static int diseqc_method; module_param(diseqc_method, int, 0444); MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + static void Set22K (struct budget *budget, int state) { struct saa7146_dev *dev=budget->dev; @@ -108,7 +111,8 @@ static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long DiseqcSendByte(budget, 0xff); else { saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - udelay(12500); + mdelay(12); + udelay(500); saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); } msleep(20); @@ -389,6 +393,13 @@ static struct stv0299_config alps_bsbe1_config_activy = { .set_symbol_rate = alps_bsbe1_set_symbol_rate, }; +static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) +{ + struct budget *budget = (struct budget *)fe->dvb->priv; + + return request_firmware(fw, name, &budget->dev->pci->dev); +} + static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg) { @@ -510,6 +521,14 @@ static void frontend_init(struct budget *budget) } break; + case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */ + budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + } + break; + case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */ budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap); if (budget->dvb_frontend) { @@ -549,7 +568,7 @@ static void frontend_init(struct budget *budget) } if (budget->dvb_frontend == NULL) { - printk("budget: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", budget->dev->pci->vendor, budget->dev->pci->device, budget->dev->pci->subsystem_vendor, @@ -581,7 +600,8 @@ static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_ dev->ext_priv = budget; - if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) { + err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); + if (err) { printk("==> failed\n"); kfree (budget); return err; @@ -623,6 +643,7 @@ MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT); MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY); MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY); MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY); static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), @@ -633,6 +654,7 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018), MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60), MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61), + MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60), MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), { .vendor = 0, diff --git a/linux/drivers/media/dvb/ttpci/budget.h b/linux/drivers/media/dvb/ttpci/budget.h index 6539c0171..6129196b8 100644 --- a/linux/drivers/media/dvb/ttpci/budget.h +++ b/linux/drivers/media/dvb/ttpci/budget.h @@ -110,7 +110,7 @@ static struct saa7146_pci_extension_data x_var = { \ extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, struct saa7146_pci_extension_data *info, - struct module *owner); + struct module *owner, short *adapter_nums); extern void ttpci_budget_init_hooks(struct budget *budget); extern int ttpci_budget_deinit(struct budget *budget); extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr); diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index ceae085cb..13085a8e2 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1634,7 +1634,7 @@ static void frontend_init(struct ttusb* ttusb) } if (ttusb->fe == NULL) { - printk("dvb-ttusb-budget: A frontend driver was not found for device %04x/%04x\n", + printk("dvb-ttusb-budget: A frontend driver was not found for device [%04x:%04x]\n", le16_to_cpu(ttusb->dev->descriptor.idVendor), le16_to_cpu(ttusb->dev->descriptor.idProduct)); } else { diff --git a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c index 4c233c41f..5c2885e17 100644 --- a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -1674,7 +1674,7 @@ static int ttusb_dec_probe(struct usb_interface *intf, } if (dec->fe == NULL) { - printk("dvb-ttusb-dec: A frontend driver was not found for device %04x/%04x\n", + printk("dvb-ttusb-dec: A frontend driver was not found for device [%04x:%04x]\n", le16_to_cpu(dec->udev->descriptor.idVendor), le16_to_cpu(dec->udev->descriptor.idProduct)); } else { diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig index 01ee71e82..1b41b3f77 100644 --- a/linux/drivers/media/radio/Kconfig +++ b/linux/drivers/media/radio/Kconfig @@ -195,38 +195,6 @@ config RADIO_MAESTRO To compile this driver as a module, choose M here: the module will be called radio-maestro. -config RADIO_MIROPCM20 - tristate "miroSOUND PCM20 radio" - depends on ISA && VIDEO_V4L1 && SOUND_ACI_MIXER - ---help--- - Choose Y here if you have this FM radio card. You also need to say Y - to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound") - for this to work. - - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - <file:Documentation/video4linux/API.html>. - - To compile this driver as a module, choose M here: the - module will be called miropcm20. - -config RADIO_MIROPCM20_RDS - tristate "miroSOUND PCM20 radio RDS user interface (EXPERIMENTAL)" - depends on RADIO_MIROPCM20 && EXPERIMENTAL - ---help--- - Choose Y here if you want to see RDS/RBDS information like - RadioText, Programme Service name, Clock Time and date, Programme - Type and Traffic Announcement/Programme identification. - - It's not possible to read the raw RDS packets from the device, so - the driver cant provide an V4L interface for this. But the - availability of RDS is reported over V4L by the basic driver - already. Here RDS can be read from files in /dev/v4l/rds. - - To compile this driver as a module, choose M here: the - module will be called miropcm20-rds. - config RADIO_SF16FMI tristate "SF16FMI Radio" depends on ISA && VIDEO_V4L2 diff --git a/linux/drivers/media/radio/Makefile b/linux/drivers/media/radio/Makefile index a30159f6f..7ca71ab96 100644 --- a/linux/drivers/media/radio/Makefile +++ b/linux/drivers/media/radio/Makefile @@ -2,8 +2,6 @@ # Makefile for the kernel character device drivers. # -miropcm20-objs := miropcm20-rds-core.o miropcm20-radio.o - obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o @@ -14,8 +12,6 @@ obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o -obj-$(CONFIG_RADIO_MIROPCM20) += miropcm20.o -obj-$(CONFIG_RADIO_MIROPCM20_RDS) += miropcm20-rds.o obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o diff --git a/linux/drivers/media/radio/dsbr100.c b/linux/drivers/media/radio/dsbr100.c index 93a871985..c03274e46 100644 --- a/linux/drivers/media/radio/dsbr100.c +++ b/linux/drivers/media/radio/dsbr100.c @@ -275,7 +275,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -307,7 +307,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio = video_drvdata(file); radio->curfreq = f->frequency; if (dsbr100_setfreq(radio, radio->curfreq)==-1) @@ -318,7 +318,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; @@ -343,7 +343,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -356,7 +356,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -406,7 +406,7 @@ static int vidioc_s_audio(struct file *file, void *priv, static int usb_dsbr100_open(struct inode *inode, struct file *file) { - struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio = video_drvdata(file); lock_kernel(); radio->users = 1; @@ -425,7 +425,7 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) static int usb_dsbr100_close(struct inode *inode, struct file *file) { - struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio = video_drvdata(file); if (!radio) return -ENODEV; @@ -497,7 +497,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf, radio->usbdev = interface_to_usbdev(intf); radio->curfreq = FREQ_MIN*FREQ_MUL; video_set_drvdata(radio->videodev, radio); - if (video_register_device(radio->videodev, VFL_TYPE_RADIO,radio_nr)) { + if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) { warn("Could not register video device"); video_device_release(radio->videodev); kfree(radio->transfer_buffer); diff --git a/linux/drivers/media/radio/miropcm20-radio.c b/linux/drivers/media/radio/miropcm20-radio.c deleted file mode 100644 index efbb9cce8..000000000 --- a/linux/drivers/media/radio/miropcm20-radio.c +++ /dev/null @@ -1,267 +0,0 @@ -/* Miro PCM20 radio driver for Linux radio support - * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl> - * Thanks to Norberto Pellici for the ACI device interface specification - * The API part is based on the radiotrack driver by M. Kirkwood - * This driver relies on the aci mixer (drivers/sound/aci.c) - * Look there for further info... - */ - -/* Revision history: - * - * 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl> - * 2000-09-05 Robert Siemer <Robert.Siemer@gmx.de> - * removed unfinished volume control (maybe adding it later again) - * use OSS-mixer; added stereo control - */ - -/* What ever you think about the ACI, version 0x07 is not very well! - * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono - * conditions... Robert - */ - -#include <linux/module.h> -#include <linux/init.h> -#include "compat.h" -#include <linux/videodev.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include "oss/aci.h" -#include "miropcm20-rds-core.h" - -static int radio_nr = -1; -module_param(radio_nr, int, 0); - -struct pcm20_device { - unsigned long freq; - int muted; - int stereo; -}; - - -static int pcm20_mute(struct pcm20_device *dev, unsigned char mute) -{ - dev->muted = mute; - return aci_write_cmd(ACI_SET_TUNERMUTE, mute); -} - -static int pcm20_stereo(struct pcm20_device *dev, unsigned char stereo) -{ - dev->stereo = stereo; - return aci_write_cmd(ACI_SET_TUNERMONO, !stereo); -} - -static int pcm20_setfreq(struct pcm20_device *dev, unsigned long freq) -{ - unsigned char freql; - unsigned char freqh; - - dev->freq=freq; - - freq /= 160; - if (!(aci_version==0x07 || aci_version>=0xb0)) - freq /= 10; /* I don't know exactly which version - * needs this hack */ - freql = freq & 0xff; - freqh = freq >> 8; - - aci_rds_cmd(RDS_RESET, NULL, 0); - pcm20_stereo(dev, 1); - - return aci_rw_cmd(ACI_WRITE_TUNE, freql, freqh); -} - -static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal) -{ - /* okay, check for signal, stereo and rds here... */ - int i; - unsigned char buf; - - if ((i=aci_rw_cmd(ACI_READ_TUNERSTATION, -1, -1))<0) - return i; - pr_debug("check_sig: 0x%x\n", i); - if (i & 0x80) { - /* no signal from tuner */ - *flags=0; - *signal=0; - return 0; - } else - *signal=0xffff; - - if ((i=aci_rw_cmd(ACI_READ_TUNERSTEREO, -1, -1))<0) - return i; - if (i & 0x40) { - *flags=0; - } else { - /* stereo */ - *flags=VIDEO_TUNER_STEREO_ON; - /* I can't see stereo, when forced to mono */ - dev->stereo=1; - } - - if ((i=aci_rds_cmd(RDS_STATUS, &buf, 1))<0) - return i; - if (buf & 1) - /* RDS available */ - *flags|=VIDEO_TUNER_RDS_ON; - else - return 0; - - if ((i=aci_rds_cmd(RDS_RXVALUE, &buf, 1))<0) - return i; - pr_debug("rds-signal: %d\n", buf); - if (buf > 15) { - printk("miropcm20-radio: RX strengths unexpected high...\n"); - buf=15; - } - /* refine signal */ - if ((*signal=SCALE(15, 0xffff, buf))==0) - *signal = 1; - - return 0; -} - -static int pcm20_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *dev = video_devdata(file); - struct pcm20_device *pcm20 = dev->priv; - int i; - - switch(cmd) - { - case VIDIOCGCAP: - { - struct video_capability *v = arg; - memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - strcpy(v->name, "Miro PCM20"); - v->channels=1; - v->audios=1; - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ - return -EINVAL; - v->rangelow=87*16000; - v->rangehigh=108*16000; - pcm20_getflags(pcm20, &v->flags, &v->signal); - v->flags|=VIDEO_TUNER_LOW; - v->mode=VIDEO_MODE_AUTO; - strcpy(v->name, "FM"); - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *v = arg; - if(v->tuner!=0) - return -EINVAL; - /* Only 1 tuner so no setting needed ! */ - return 0; - } - case VIDIOCGFREQ: - { - unsigned long *freq = arg; - *freq = pcm20->freq; - return 0; - } - case VIDIOCSFREQ: - { - unsigned long *freq = arg; - pcm20->freq = *freq; - i=pcm20_setfreq(pcm20, pcm20->freq); - pr_debug("First view (setfreq): 0x%x\n", i); - return i; - } - case VIDIOCGAUDIO: - { - struct video_audio *v = arg; - memset(v,0, sizeof(*v)); - v->flags=VIDEO_AUDIO_MUTABLE; - if (pcm20->muted) - v->flags|=VIDEO_AUDIO_MUTE; - v->mode=VIDEO_SOUND_STEREO; - if (pcm20->stereo) - v->mode|=VIDEO_SOUND_MONO; - /* v->step=2048; */ - strcpy(v->name, "Radio"); - return 0; - } - case VIDIOCSAUDIO: - { - struct video_audio *v = arg; - if(v->audio) - return -EINVAL; - - pcm20_mute(pcm20, !!(v->flags&VIDEO_AUDIO_MUTE)); - if(v->flags&VIDEO_SOUND_MONO) - pcm20_stereo(pcm20, 0); - if(v->flags&VIDEO_SOUND_STEREO) - pcm20_stereo(pcm20, 1); - - return 0; - } - default: - return -ENOIOCTLCMD; - } -} - -static int pcm20_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(inode, file, cmd, arg, pcm20_do_ioctl); -} - -static struct pcm20_device pcm20_unit = { - .freq = 87*16000, - .muted = 1, -}; - -static const struct file_operations pcm20_fops = { - .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, - .ioctl = pcm20_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = v4l_compat_ioctl32, -#endif - .llseek = no_llseek, -}; - -static struct video_device pcm20_radio = { - .name = "Miro PCM 20 radio", - .fops = &pcm20_fops, - .priv = &pcm20_unit -}; - -static int __init pcm20_init(void) -{ - if(video_register_device(&pcm20_radio, VFL_TYPE_RADIO, radio_nr)==-1) - goto video_register_device; - - if(attach_aci_rds()<0) - goto attach_aci_rds; - - printk(KERN_INFO "Miro PCM20 radio card driver.\n"); - - return 0; - - attach_aci_rds: - video_unregister_device(&pcm20_radio); - video_register_device: - return -EINVAL; -} - -MODULE_AUTHOR("Ruurd Reitsma"); -MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card."); -MODULE_LICENSE("GPL"); - -static void __exit pcm20_cleanup(void) -{ - unload_aci_rds(); - video_unregister_device(&pcm20_radio); -} - -module_init(pcm20_init); -module_exit(pcm20_cleanup); diff --git a/linux/drivers/media/radio/miropcm20-rds-core.c b/linux/drivers/media/radio/miropcm20-rds-core.c deleted file mode 100644 index e18e2a4a8..000000000 --- a/linux/drivers/media/radio/miropcm20-rds-core.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Many thanks to Fred Seidel <seidel@metabox.de>, the - * designer of the RDS decoder hardware. With his help - * I was able to code this driver. - * Thanks also to Norberto Pellicci, Dominic Mounteney - * <DMounteney@pinnaclesys.com> and www.teleauskunft.de - * for good hints on finding Fred. It was somewhat hard - * to locate him here in Germany... [: - * - * Revision history: - * - * 2000-08-09 Robert Siemer <Robert.Siemer@gmx.de> - * RDS support for MiroSound PCM20 radio - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/slab.h> -#include "compat.h" -#include <linux/mutex.h> - -#include <asm/io.h> -#include "oss/aci.h" -#include "miropcm20-rds-core.h" - -#define DEBUG 0 - -static struct mutex aci_rds_mutex; - -#define RDS_DATASHIFT 2 /* Bit 2 */ -#define RDS_DATAMASK (1 << RDS_DATASHIFT) -#define RDS_BUSYMASK 0x10 /* Bit 4 */ -#define RDS_CLOCKMASK 0x08 /* Bit 3 */ - -#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1) - - -#if DEBUG -static void print_matrix(char array[], unsigned int length) -{ - int i, j; - - for (i=0; i<length; i++) { - printk(KERN_DEBUG "aci-rds: "); - for (j=7; j>=0; j--) { - printk("%d", (array[i] >> j) & 0x1); - } - if (i%8 == 0) - printk(" byte-border\n"); - else - printk("\n"); - } -} -#endif /* DEBUG */ - -static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size) -{ - int i; - - if (size != 8) - return -1; - for (i = 7; i >= 0; i--) - sendbuffer[7-i] = (byte & (1 << i)) ? RDS_DATAMASK : 0; - sendbuffer[0] |= RDS_CLOCKMASK; - - return 0; -} - -static int rds_waitread(void) -{ - unsigned char byte; - int i=2000; - - do { - byte=inb(RDS_REGISTER); - i--; - } - while ((byte & RDS_BUSYMASK) && i); - - if (i) { - #if DEBUG - printk(KERN_DEBUG "rds_waitread()"); - print_matrix(&byte, 1); - #endif - return (byte); - } else { - printk(KERN_WARNING "aci-rds: rds_waitread() timeout...\n"); - return -1; - } -} - -/* don't use any ..._nowait() function if you are not sure what you do... */ - -static inline void rds_rawwrite_nowait(unsigned char byte) -{ - #if DEBUG - printk(KERN_DEBUG "rds_rawwrite()"); - print_matrix(&byte, 1); - #endif - outb(byte, RDS_REGISTER); -} - -static int rds_rawwrite(unsigned char byte) -{ - if (rds_waitread() >= 0) { - rds_rawwrite_nowait(byte); - return 0; - } else - return -1; -} - -static int rds_write(unsigned char cmd) -{ - unsigned char sendbuffer[8]; - int i; - - if (byte2trans(cmd, sendbuffer, 8) != 0){ - return -1; - } else { - for (i=0; i<8; i++) { - rds_rawwrite(sendbuffer[i]); - } - } - return 0; -} - -static int rds_readcycle_nowait(void) -{ - rds_rawwrite_nowait(0); - return rds_waitread(); -} - -static int rds_readcycle(void) -{ - if (rds_rawwrite(0) < 0) - return -1; - return rds_waitread(); -} - -static int rds_read(unsigned char databuffer[], int datasize) -{ - #define READSIZE (8*datasize) - - int i,j; - - if (datasize < 1) /* nothing to read */ - return 0; - - /* to be able to use rds_readcycle_nowait() - I have to waitread() here */ - if (rds_waitread() < 0) - return -1; - - memset(databuffer, 0, datasize); - - for (i=0; i< READSIZE; i++) - if((j=rds_readcycle_nowait()) < 0) { - return -1; - } else { - databuffer[i/8]|=(RDS_DATA(j) << (7-(i%8))); - } - - return 0; -} - -static int rds_ack(void) -{ - int i=rds_readcycle(); - - if (i < 0) - return -1; - if (i & RDS_DATAMASK) { - return 0; /* ACK */ - } else { - printk(KERN_DEBUG "aci-rds: NACK\n"); - return 1; /* NACK */ - } -} - -int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize) -{ - int ret; - - if (mutex_lock_interruptible(&aci_rds_mutex)) - return -EINTR; - - rds_write(cmd); - - /* RDS_RESET doesn't need further processing */ - if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize))) - ret = -1; - else - ret = 0; - - mutex_unlock(&aci_rds_mutex); - - return ret; -} -EXPORT_SYMBOL(aci_rds_cmd); - -int __init attach_aci_rds(void) -{ - mutex_init(&aci_rds_mutex); - return 0; -} - -void __exit unload_aci_rds(void) -{ -} -MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/radio/miropcm20-rds-core.h b/linux/drivers/media/radio/miropcm20-rds-core.h deleted file mode 100644 index aeb5761f0..000000000 --- a/linux/drivers/media/radio/miropcm20-rds-core.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _MIROPCM20_RDS_CORE_H_ -#define _MIROPCM20_RDS_CORE_H_ - -extern int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize); - -#define RDS_STATUS 0x01 -#define RDS_STATIONNAME 0x02 -#define RDS_TEXT 0x03 -#define RDS_ALTFREQ 0x04 -#define RDS_TIMEDATE 0x05 -#define RDS_PI_CODE 0x06 -#define RDS_PTYTATP 0x07 -#define RDS_RESET 0x08 -#define RDS_RXVALUE 0x09 - -extern void __exit unload_aci_rds(void); -extern int __init attach_aci_rds(void); - -#endif /* _MIROPCM20_RDS_CORE_H_ */ diff --git a/linux/drivers/media/radio/miropcm20-rds.c b/linux/drivers/media/radio/miropcm20-rds.c deleted file mode 100644 index af90cdcf2..000000000 --- a/linux/drivers/media/radio/miropcm20-rds.c +++ /dev/null @@ -1,140 +0,0 @@ -/* MiroSOUND PCM20 radio rds interface driver - * (c) 2001 Robert Siemer <Robert.Siemer@gmx.de> - * Thanks to Fred Seidel. See miropcm20-rds-core.c for further information. - */ - -/* Revision history: - * - * 2001-04-18 Robert Siemer <Robert.Siemer@gmx.de> - * separate file for user interface driver - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/smp_lock.h> -#include <linux/fs.h> -#include <linux/miscdevice.h> -#include <linux/delay.h> -#include <asm/uaccess.h> -#include "miropcm20-rds-core.h" - -static char * text_buffer; -static int rds_users; - - -static int rds_f_open(struct inode *in, struct file *fi) -{ - if (rds_users) - return -EBUSY; - - lock_kernel(); - rds_users++; - if ((text_buffer=kmalloc(66, GFP_KERNEL)) == 0) { - rds_users--; - printk(KERN_NOTICE "aci-rds: Out of memory by open()...\n"); - unlock_kernel(); - return -ENOMEM; - } - - unlock_kernel(); - return 0; -} - -static int rds_f_release(struct inode *in, struct file *fi) -{ - kfree(text_buffer); - - rds_users--; - return 0; -} - -static void print_matrix(char *ch, char out[]) -{ - int j; - - for (j=7; j>=0; j--) { - out[7-j] = ((*ch >> j) & 0x1) + '0'; - } -} - -static ssize_t rds_f_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) -{ -// i = sprintf(text_buffer, "length: %d, offset: %d\n", length, *offset); - - char c; - char bits[8]; - - msleep(2000); - aci_rds_cmd(RDS_STATUS, &c, 1); - print_matrix(&c, bits); - if (copy_to_user(buffer, bits, 8)) - return -EFAULT; - -/* if ((c >> 3) & 1) { - aci_rds_cmd(RDS_STATIONNAME, text_buffer+1, 8); - text_buffer[0] = ' ' ; - text_buffer[9] = '\n'; - return copy_to_user(buffer+8, text_buffer, 10) ? -EFAULT: 18; - } -*/ -/* if ((c >> 6) & 1) { - aci_rds_cmd(RDS_PTYTATP, &c, 1); - if ( c & 1) - sprintf(text_buffer, " M"); - else - sprintf(text_buffer, " S"); - if ((c >> 1) & 1) - sprintf(text_buffer+2, " TA"); - else - sprintf(text_buffer+2, " --"); - if ((c >> 7) & 1) - sprintf(text_buffer+5, " TP"); - else - sprintf(text_buffer+5, " --"); - sprintf(text_buffer+8, " %2d\n", (c >> 2) & 0x1f); - return copy_to_user(buffer+8, text_buffer, 12) ? -EFAULT: 20; - } -*/ - - if ((c >> 4) & 1) { - aci_rds_cmd(RDS_TEXT, text_buffer, 65); - text_buffer[0] = ' ' ; - text_buffer[65] = '\n'; - return copy_to_user(buffer+8, text_buffer,66) ? -EFAULT : 66+8; - } else { - put_user('\n', buffer+8); - return 9; - } -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) -static const struct file_operations rds_fops = { -#else -static struct file_operations rds_fops = { -#endif - .owner = THIS_MODULE, - .read = rds_f_read, - .open = rds_f_open, - .release = rds_f_release -}; - -static struct miscdevice rds_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "radiotext", - .fops = &rds_fops, -}; - -static int __init miropcm20_rds_init(void) -{ - return misc_register(&rds_miscdev); -} - -static void __exit miropcm20_rds_cleanup(void) -{ - misc_deregister(&rds_miscdev); -} - -module_init(miropcm20_rds_init); -module_exit(miropcm20_rds_cleanup); -MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/radio/radio-aimslab.c b/linux/drivers/media/radio/radio-aimslab.c index 516677a98..483ada689 100644 --- a/linux/drivers/media/radio/radio-aimslab.c +++ b/linux/drivers/media/radio/radio-aimslab.c @@ -52,6 +52,7 @@ static struct mutex lock; struct rt_device { + unsigned long in_use; int port; int curvol; unsigned long curfreq; @@ -246,8 +247,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -274,8 +274,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); rt->curfreq = f->frequency; rt_setfreq(rt, rt->curfreq); @@ -285,8 +284,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = rt->curfreq; @@ -311,8 +309,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -328,8 +325,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -379,10 +375,21 @@ static int vidioc_s_audio(struct file *file, void *priv, static struct rt_device rtrack_unit; +static int rtrack_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &rtrack_unit.in_use) ? -EBUSY : 0; +} + +static int rtrack_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &rtrack_unit.in_use); + return 0; +} + static const struct file_operations rtrack_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = rtrack_exclusive_open, + .release = rtrack_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -409,6 +416,7 @@ static struct video_device rtrack_radio = { .name = "RadioTrack radio", .fops = &rtrack_fops, .ioctl_ops = &rtrack_ioctl_ops, + .release = video_device_release_empty, }; static int __init rtrack_init(void) @@ -425,10 +433,9 @@ static int __init rtrack_init(void) return -EBUSY; } - rtrack_radio.priv=&rtrack_unit; + video_set_drvdata(&rtrack_radio, &rtrack_unit); - if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr)==-1) - { + if (video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io, 2); return -EINVAL; } diff --git a/linux/drivers/media/radio/radio-aztech.c b/linux/drivers/media/radio/radio-aztech.c index 8adb1b003..9ce1908ac 100644 --- a/linux/drivers/media/radio/radio-aztech.c +++ b/linux/drivers/media/radio/radio-aztech.c @@ -71,6 +71,7 @@ static struct mutex lock; struct az_device { + unsigned long in_use; int curvol; unsigned long curfreq; int stereo; @@ -196,8 +197,7 @@ static int vidioc_querycap (struct file *file, void *priv, static int vidioc_g_tuner (struct file *file, void *priv, struct v4l2_tuner *v) { - struct video_device *dev = video_devdata(file); - struct az_device *az = dev->priv; + struct az_device *az = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -265,8 +265,7 @@ static int vidioc_s_audio (struct file *file, void *priv, static int vidioc_s_frequency (struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct az_device *az = dev->priv; + struct az_device *az = video_drvdata(file); az->curfreq = f->frequency; az_setfreq(az, az->curfreq); @@ -276,8 +275,7 @@ static int vidioc_s_frequency (struct file *file, void *priv, static int vidioc_g_frequency (struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct az_device *az = dev->priv; + struct az_device *az = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = az->curfreq; @@ -303,8 +301,7 @@ static int vidioc_queryctrl (struct file *file, void *priv, static int vidioc_g_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct az_device *az = dev->priv; + struct az_device *az = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -323,8 +320,7 @@ static int vidioc_g_ctrl (struct file *file, void *priv, static int vidioc_s_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct az_device *az = dev->priv; + struct az_device *az = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -343,10 +339,21 @@ static int vidioc_s_ctrl (struct file *file, void *priv, static struct az_device aztech_unit; +static int aztech_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &aztech_unit.in_use) ? -EBUSY : 0; +} + +static int aztech_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &aztech_unit.in_use); + return 0; +} + static const struct file_operations aztech_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = aztech_exclusive_open, + .release = aztech_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -370,9 +377,10 @@ static const struct v4l2_ioctl_ops aztech_ioctl_ops = { }; static struct video_device aztech_radio = { - .name = "Aztech radio", - .fops = &aztech_fops, - .ioctl_ops = &aztech_ioctl_ops, + .name = "Aztech radio", + .fops = &aztech_fops, + .ioctl_ops = &aztech_ioctl_ops, + .release = video_device_release_empty, }; module_param_named(debug,aztech_radio.debug, int, 0644); @@ -393,10 +401,9 @@ static int __init aztech_init(void) } mutex_init(&lock); - aztech_radio.priv=&aztech_unit; + video_set_drvdata(&aztech_radio, &aztech_unit); - if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1) - { + if (video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io,2); return -EINVAL; } diff --git a/linux/drivers/media/radio/radio-cadet.c b/linux/drivers/media/radio/radio-cadet.c index 6588f0cd3..f40c079ea 100644 --- a/linux/drivers/media/radio/radio-cadet.c +++ b/linux/drivers/media/radio/radio-cadet.c @@ -619,6 +619,7 @@ static struct video_device cadet_radio = { .name = "Cadet radio", .fops = &cadet_fops, .ioctl_ops = &cadet_ioctl_ops, + .release = video_device_release_empty, }; #ifdef CONFIG_PNP @@ -712,7 +713,7 @@ static int __init cadet_init(void) } if (!request_region(io,2,"cadet")) goto fail; - if(video_register_device(&cadet_radio,VFL_TYPE_RADIO,radio_nr)==-1) { + if (video_register_device(&cadet_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io,2); goto fail; } diff --git a/linux/drivers/media/radio/radio-gemtek-pci.c b/linux/drivers/media/radio/radio-gemtek-pci.c index efee362c7..06a91be87 100644 --- a/linux/drivers/media/radio/radio-gemtek-pci.c +++ b/linux/drivers/media/radio/radio-gemtek-pci.c @@ -101,9 +101,8 @@ struct gemtek_pci_card { u8 mute; }; -static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1 2001/07/23 08:08:16 ted Exp ted $"; - static int nr_radio = -1; +static unsigned long in_use; static inline u8 gemtek_pci_out( u16 value, u32 port ) { @@ -206,8 +205,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct video_device *dev = video_devdata(file); - struct gemtek_pci_card *card = dev->priv; + struct gemtek_pci_card *card = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -234,8 +232,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct gemtek_pci_card *card = dev->priv; + struct gemtek_pci_card *card = video_drvdata(file); if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) || (f->frequency > GEMTEK_PCI_RANGE_HIGH) ) @@ -249,8 +246,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct gemtek_pci_card *card = dev->priv; + struct gemtek_pci_card *card = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = card->current_frequency; @@ -274,8 +270,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct gemtek_pci_card *card = dev->priv; + struct gemtek_pci_card *card = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -294,8 +289,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct gemtek_pci_card *card = dev->priv; + struct gemtek_pci_card *card = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -365,10 +359,21 @@ MODULE_DEVICE_TABLE( pci, gemtek_pci_id ); static int mx = 1; +static int gemtek_pci_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &in_use) ? -EBUSY : 0; +} + +static int gemtek_pci_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &in_use); + return 0; +} + static const struct file_operations gemtek_pci_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = gemtek_pci_exclusive_open, + .release = gemtek_pci_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -392,9 +397,10 @@ static const struct v4l2_ioctl_ops gemtek_pci_ioctl_ops = { }; static struct video_device vdev_template = { - .name = "Gemtek PCI Radio", - .fops = &gemtek_pci_fops, - .ioctl_ops = &gemtek_pci_ioctl_ops, + .name = "Gemtek PCI Radio", + .fops = &gemtek_pci_fops, + .ioctl_ops = &gemtek_pci_ioctl_ops, + .release = video_device_release_empty, }; static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id ) @@ -426,13 +432,13 @@ static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci } *devradio = vdev_template; - if ( video_register_device( devradio, VFL_TYPE_RADIO , nr_radio) == -1 ) { + if (video_register_device(devradio, VFL_TYPE_RADIO, nr_radio) < 0) { kfree( devradio ); goto err_video; } card->videodev = devradio; - devradio->priv = card; + video_set_drvdata(devradio, card); gemtek_pci_mute( card ); #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22) diff --git a/linux/drivers/media/radio/radio-gemtek.c b/linux/drivers/media/radio/radio-gemtek.c index bf1837ab2..003c7b2f7 100644 --- a/linux/drivers/media/radio/radio-gemtek.c +++ b/linux/drivers/media/radio/radio-gemtek.c @@ -58,6 +58,7 @@ static int shutdown = 1; static int keepmuted = 1; static int initmute = 1; static int radio_nr = -1; +static unsigned long in_use; module_param(io, int, 0444); MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic " @@ -394,10 +395,21 @@ static struct v4l2_queryctrl radio_qctrl[] = { } }; +static int gemtek_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &in_use) ? -EBUSY : 0; +} + +static int gemtek_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &in_use); + return 0; +} + static const struct file_operations gemtek_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = gemtek_exclusive_open, + .release = gemtek_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -448,8 +460,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct gemtek_device *rt = dev->priv; + struct gemtek_device *rt = video_drvdata(file); gemtek_setfreq(rt, f->frequency); @@ -459,8 +470,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct gemtek_device *rt = dev->priv; + struct gemtek_device *rt = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = rt->lastfreq; @@ -484,8 +494,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct gemtek_device *rt = dev->priv; + struct gemtek_device *rt = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -504,8 +513,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct gemtek_device *rt = dev->priv; + struct gemtek_device *rt = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -570,9 +578,10 @@ static const struct v4l2_ioctl_ops gemtek_ioctl_ops = { }; static struct video_device gemtek_radio = { - .name = "GemTek Radio card", - .fops = &gemtek_fops, - .ioctl_ops = &gemtek_ioctl_ops, + .name = "GemTek Radio card", + .fops = &gemtek_fops, + .ioctl_ops = &gemtek_ioctl_ops, + .release = video_device_release_empty, }; /* @@ -611,10 +620,9 @@ static int __init gemtek_init(void) return -EINVAL; } - gemtek_radio.priv = &gemtek_unit; + video_set_drvdata(&gemtek_radio, &gemtek_unit); - if (video_register_device(&gemtek_radio, VFL_TYPE_RADIO, - radio_nr) == -1) { + if (video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io, 1); return -EBUSY; } diff --git a/linux/drivers/media/radio/radio-maestro.c b/linux/drivers/media/radio/radio-maestro.c index 3b382e2b3..326405d3b 100644 --- a/linux/drivers/media/radio/radio-maestro.c +++ b/linux/drivers/media/radio/radio-maestro.c @@ -76,7 +76,21 @@ static struct v4l2_queryctrl radio_qctrl[] = { static int radio_nr = -1; module_param(radio_nr, int, 0); +static unsigned long in_use; + static int maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent); + +static int maestro_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &in_use) ? -EBUSY : 0; +} + +static int maestro_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &in_use); + return 0; +} + static void maestro_remove(struct pci_dev *pdev); static struct pci_device_id maestro_r_pci_tbl[] = { @@ -99,8 +113,8 @@ static struct pci_driver maestro_r_driver = { static const struct file_operations maestro_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = maestro_exclusive_open, + .release = maestro_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -197,8 +211,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct video_device *dev = video_devdata(file); - struct radio_device *card = video_get_drvdata(dev); + struct radio_device *card = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -230,8 +243,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct radio_device *card = video_get_drvdata(dev); + struct radio_device *card = video_drvdata(file); if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) return -EINVAL; @@ -242,8 +254,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct radio_device *card = video_get_drvdata(dev); + struct radio_device *card = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = BITS2FREQ(radio_bits_get(card)); @@ -268,8 +279,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct radio_device *card = video_get_drvdata(dev); + struct radio_device *card = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -282,8 +292,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct radio_device *card = video_get_drvdata(dev); + struct radio_device *card = video_drvdata(file); register u16 io = card->io; register u16 omask = inw(io + IO_MASK); @@ -375,6 +384,7 @@ static struct video_device maestro_radio = { .name = "Maestro radio", .fops = &maestro_fops, .ioctl_ops = &maestro_ioctl_ops, + .release = video_device_release, }; static int __devinit maestro_probe(struct pci_dev *pdev, @@ -410,8 +420,7 @@ static int __devinit maestro_probe(struct pci_dev *pdev, video_set_drvdata(maestro_radio_inst, radio_unit); pci_set_drvdata(pdev, maestro_radio_inst); - retval = video_register_device(maestro_radio_inst, VFL_TYPE_RADIO, - radio_nr); + retval = video_register_device(maestro_radio_inst, VFL_TYPE_RADIO, radio_nr); if (retval) { printk(KERN_ERR "can't register video device!\n"); goto errfr1; diff --git a/linux/drivers/media/radio/radio-maxiradio.c b/linux/drivers/media/radio/radio-maxiradio.c index 0f6bbd81b..4aea352cb 100644 --- a/linux/drivers/media/radio/radio-maxiradio.c +++ b/linux/drivers/media/radio/radio-maxiradio.c @@ -86,6 +86,7 @@ static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ; static int radio_nr = -1; module_param(radio_nr, int, 0); +static unsigned long in_use; #define FREQ_LO 50*16000 #define FREQ_HI 150*16000 @@ -100,10 +101,21 @@ module_param(radio_nr, int, 0); #define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF) +static int maxiradio_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &in_use) ? -EBUSY : 0; +} + +static int maxiradio_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &in_use); + return 0; +} + static const struct file_operations maxiradio_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = maxiradio_exclusive_open, + .release = maxiradio_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -157,28 +169,28 @@ static void set_freq(__u16 io, __u32 freq) { unsigned long int si; int bl; - int data = FREQ2BITS(freq); + int val = FREQ2BITS(freq); /* TEA5757 shift register bits (see pdf) */ - outbit(0,io); // 24 search - outbit(1,io); // 23 search up/down + outbit(0, io); /* 24 search */ + outbit(1, io); /* 23 search up/down */ - outbit(0,io); // 22 stereo/mono + outbit(0, io); /* 22 stereo/mono */ - outbit(0,io); // 21 band - outbit(0,io); // 20 band (only 00=FM works I think) + outbit(0, io); /* 21 band */ + outbit(0, io); /* 20 band (only 00=FM works I think) */ - outbit(0,io); // 19 port ? - outbit(0,io); // 18 port ? + outbit(0, io); /* 19 port ? */ + outbit(0, io); /* 18 port ? */ - outbit(0,io); // 17 search level - outbit(0,io); // 16 search level + outbit(0, io); /* 17 search level */ + outbit(0, io); /* 16 search level */ si = 0x8000; - for (bl = 1; bl <= 16 ; bl++) { - outbit(data & si,io); - si >>=1; + for (bl = 1; bl <= 16; bl++) { + outbit(val & si, io); + si >>= 1; } dprintk(1, "Radio freq set to %d.%02d MHz\n", @@ -220,8 +232,7 @@ static int vidioc_querycap (struct file *file, void *priv, static int vidioc_g_tuner (struct file *file, void *priv, struct v4l2_tuner *v) { - struct video_device *dev = video_devdata(file); - struct radio_device *card=dev->priv; + struct radio_device *card = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -291,8 +302,7 @@ static int vidioc_s_audio (struct file *file, void *priv, static int vidioc_s_frequency (struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct radio_device *card=dev->priv; + struct radio_device *card = video_drvdata(file); if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) { dprintk(1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n", @@ -313,8 +323,7 @@ static int vidioc_s_frequency (struct file *file, void *priv, static int vidioc_g_frequency (struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct radio_device *card=dev->priv; + struct radio_device *card = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = card->freq; @@ -344,8 +353,7 @@ static int vidioc_queryctrl (struct file *file, void *priv, static int vidioc_g_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct radio_device *card=dev->priv; + struct radio_device *card = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -359,8 +367,7 @@ static int vidioc_g_ctrl (struct file *file, void *priv, static int vidioc_s_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct radio_device *card=dev->priv; + struct radio_device *card = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -391,9 +398,10 @@ static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = { }; static struct video_device maxiradio_radio = { - .name = "Maxi Radio FM2000 radio", - .fops = &maxiradio_fops, - .ioctl_ops = &maxiradio_ioctl_ops, + .name = "Maxi Radio FM2000 radio", + .fops = &maxiradio_fops, + .ioctl_ops = &maxiradio_ioctl_ops, + .release = video_device_release_empty, }; static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -409,9 +417,9 @@ static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_d radio_unit.io = pci_resource_start(pdev, 0); mutex_init(&radio_unit.lock); - maxiradio_radio.priv = &radio_unit; + video_set_drvdata(&maxiradio_radio, &radio_unit); - if (video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) { + if (video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr) < 0) { printk("radio-maxiradio: can't register device!"); goto err_out_free_region; } diff --git a/linux/drivers/media/radio/radio-rtrack2.c b/linux/drivers/media/radio/radio-rtrack2.c index 31d6eabbb..59592bb84 100644 --- a/linux/drivers/media/radio/radio-rtrack2.c +++ b/linux/drivers/media/radio/radio-rtrack2.c @@ -53,6 +53,7 @@ static spinlock_t lock; struct rt_device { + unsigned long in_use; int port; unsigned long curfreq; int muted; @@ -154,8 +155,7 @@ static int rt_getsigstr(struct rt_device *dev) static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -174,8 +174,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); rt->curfreq = f->frequency; rt_setfreq(rt, rt->curfreq); @@ -185,8 +184,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = rt->curfreq; @@ -211,8 +209,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -231,8 +228,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct rt_device *rt = dev->priv; + struct rt_device *rt = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -285,10 +281,21 @@ static int vidioc_s_audio(struct file *file, void *priv, static struct rt_device rtrack2_unit; +static int rtrack2_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &rtrack2_unit.in_use) ? -EBUSY : 0; +} + +static int rtrack2_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &rtrack2_unit.in_use); + return 0; +} + static const struct file_operations rtrack2_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = rtrack2_exclusive_open, + .release = rtrack2_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -315,6 +322,7 @@ static struct video_device rtrack2_radio = { .name = "RadioTrack II radio", .fops = &rtrack2_fops, .ioctl_ops = &rtrack2_ioctl_ops, + .release = video_device_release_empty, }; static int __init rtrack2_init(void) @@ -330,11 +338,10 @@ static int __init rtrack2_init(void) return -EBUSY; } - rtrack2_radio.priv=&rtrack2_unit; + video_set_drvdata(&rtrack2_radio, &rtrack2_unit); spin_lock_init(&lock); - if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr)==-1) - { + if (video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io, 4); return -EINVAL; } diff --git a/linux/drivers/media/radio/radio-sf16fmi.c b/linux/drivers/media/radio/radio-sf16fmi.c index d4433b6a3..edf7480bc 100644 --- a/linux/drivers/media/radio/radio-sf16fmi.c +++ b/linux/drivers/media/radio/radio-sf16fmi.c @@ -46,6 +46,7 @@ static struct v4l2_queryctrl radio_qctrl[] = { struct fmi_device { + unsigned long in_use; int port; int curvol; /* 1 or 0 */ unsigned long curfreq; /* freq in kHz */ @@ -147,8 +148,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { int mult; - struct video_device *dev = video_devdata(file); - struct fmi_device *fmi = dev->priv; + struct fmi_device *fmi = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -176,8 +176,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct fmi_device *fmi = dev->priv; + struct fmi_device *fmi = video_drvdata(file); if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) f->frequency *= 1000; @@ -194,8 +193,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct fmi_device *fmi = dev->priv; + struct fmi_device *fmi = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = fmi->curfreq; @@ -222,8 +220,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct fmi_device *fmi = dev->priv; + struct fmi_device *fmi = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -236,8 +233,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct fmi_device *fmi = dev->priv; + struct fmi_device *fmi = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -285,10 +281,21 @@ static int vidioc_s_audio(struct file *file, void *priv, static struct fmi_device fmi_unit; +static int fmi_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &fmi_unit.in_use) ? -EBUSY : 0; +} + +static int fmi_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &fmi_unit.in_use); + return 0; +} + static const struct file_operations fmi_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = fmi_exclusive_open, + .release = fmi_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -315,6 +322,7 @@ static struct video_device fmi_radio = { .name = "SF16FMx radio", .fops = &fmi_fops, .ioctl_ops = &fmi_ioctl_ops, + .release = video_device_release_empty, }; /* ladis: this is my card. does any other types exist? */ @@ -374,11 +382,11 @@ static int __init fmi_init(void) fmi_unit.curvol = 0; fmi_unit.curfreq = 0; fmi_unit.flags = V4L2_TUNER_CAP_LOW; - fmi_radio.priv = &fmi_unit; + video_set_drvdata(&fmi_radio, &fmi_unit); mutex_init(&lock); - if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) { + if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io, 2); return -EINVAL; } diff --git a/linux/drivers/media/radio/radio-sf16fmr2.c b/linux/drivers/media/radio/radio-sf16fmr2.c index 7b7748561..f5aebc9da 100644 --- a/linux/drivers/media/radio/radio-sf16fmr2.c +++ b/linux/drivers/media/radio/radio-sf16fmr2.c @@ -65,6 +65,7 @@ static struct v4l2_queryctrl radio_qctrl[] = { /* this should be static vars for module size */ struct fmr2_device { + unsigned long in_use; int port; int curvol; /* 0-15 */ int mute; @@ -230,8 +231,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { int mult; - struct video_device *dev = video_devdata(file); - struct fmr2_device *fmr2 = dev->priv; + struct fmr2_device *fmr2 = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -263,8 +263,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct fmr2_device *fmr2 = dev->priv; + struct fmr2_device *fmr2 = video_drvdata(file); if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) f->frequency *= 1000; @@ -287,8 +286,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct fmr2_device *fmr2 = dev->priv; + struct fmr2_device *fmr2 = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = fmr2->curfreq; @@ -314,8 +312,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct fmr2_device *fmr2 = dev->priv; + struct fmr2_device *fmr2 = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -331,8 +328,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct fmr2_device *fmr2 = dev->priv; + struct fmr2_device *fmr2 = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -401,10 +397,21 @@ static int vidioc_s_audio(struct file *file, void *priv, static struct fmr2_device fmr2_unit; +static int fmr2_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &fmr2_unit.in_use) ? -EBUSY : 0; +} + +static int fmr2_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &fmr2_unit.in_use); + return 0; +} + static const struct file_operations fmr2_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = fmr2_exclusive_open, + .release = fmr2_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -431,6 +438,7 @@ static struct video_device fmr2_radio = { .name = "SF16FMR2 radio", .fops = &fmr2_fops, .ioctl_ops = &fmr2_ioctl_ops, + .release = video_device_release_empty, }; static int __init fmr2_init(void) @@ -442,7 +450,7 @@ static int __init fmr2_init(void) fmr2_unit.stereo = 1; fmr2_unit.flags = V4L2_TUNER_CAP_LOW; fmr2_unit.card_type = 0; - fmr2_radio.priv = &fmr2_unit; + video_set_drvdata(&fmr2_radio, &fmr2_unit); mutex_init(&lock); diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c index 3b1f630a9..871a63a87 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -991,7 +991,7 @@ static void si470x_work(struct work_struct *work) static ssize_t si470x_fops_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; unsigned int block_count = 0; @@ -1052,7 +1052,7 @@ done: static unsigned int si470x_fops_poll(struct file *file, struct poll_table_struct *pts) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; /* switch on rds reception */ @@ -1076,7 +1076,7 @@ static unsigned int si470x_fops_poll(struct file *file, */ static int si470x_fops_open(struct inode *inode, struct file *file) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval; lock_kernel(); @@ -1106,7 +1106,7 @@ done: */ static int si470x_fops_release(struct inode *inode, struct file *file) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety check */ @@ -1289,7 +1289,7 @@ done: static int si470x_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ @@ -1325,7 +1325,7 @@ done: static int si470x_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ @@ -1412,7 +1412,7 @@ done: static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ @@ -1478,7 +1478,7 @@ done: static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ @@ -1512,7 +1512,7 @@ done: static int si470x_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ @@ -1541,7 +1541,7 @@ done: static int si470x_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ @@ -1570,7 +1570,7 @@ done: static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, struct v4l2_hw_freq_seek *seek) { - struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ @@ -1701,8 +1701,8 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, INIT_DELAYED_WORK(&radio->work, si470x_work); /* register video device */ - if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { - retval = -EIO; + retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); + if (retval) { printk(KERN_WARNING DRIVER_NAME ": Could not register video device\n"); goto err_all; diff --git a/linux/drivers/media/radio/radio-terratec.c b/linux/drivers/media/radio/radio-terratec.c index 768582f53..3066b08f0 100644 --- a/linux/drivers/media/radio/radio-terratec.c +++ b/linux/drivers/media/radio/radio-terratec.c @@ -80,6 +80,7 @@ static spinlock_t lock; struct tt_device { + unsigned long in_use; int port; int curvol; unsigned long curfreq; @@ -221,8 +222,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct video_device *dev = video_devdata(file); - struct tt_device *tt = dev->priv; + struct tt_device *tt = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -249,8 +249,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct tt_device *tt = dev->priv; + struct tt_device *tt = video_drvdata(file); tt->curfreq = f->frequency; tt_setfreq(tt, tt->curfreq); @@ -260,8 +259,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct tt_device *tt = dev->priv; + struct tt_device *tt = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = tt->curfreq; @@ -286,8 +284,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct tt_device *tt = dev->priv; + struct tt_device *tt = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -306,8 +303,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct tt_device *tt = dev->priv; + struct tt_device *tt = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -357,10 +353,21 @@ static int vidioc_s_audio(struct file *file, void *priv, static struct tt_device terratec_unit; +static int terratec_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &terratec_unit.in_use) ? -EBUSY : 0; +} + +static int terratec_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &terratec_unit.in_use); + return 0; +} + static const struct file_operations terratec_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = terratec_exclusive_open, + .release = terratec_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -387,6 +394,7 @@ static struct video_device terratec_radio = { .name = "TerraTec ActiveRadio", .fops = &terratec_fops, .ioctl_ops = &terratec_ioctl_ops, + .release = video_device_release_empty, }; static int __init terratec_init(void) @@ -402,12 +410,11 @@ static int __init terratec_init(void) return -EBUSY; } - terratec_radio.priv=&terratec_unit; + video_set_drvdata(&terratec_radio, &terratec_unit); spin_lock_init(&lock); - if(video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr)==-1) - { + if (video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io,2); return -EINVAL; } diff --git a/linux/drivers/media/radio/radio-trust.c b/linux/drivers/media/radio/radio-trust.c index 648007027..a334ce27e 100644 --- a/linux/drivers/media/radio/radio-trust.c +++ b/linux/drivers/media/radio/radio-trust.c @@ -79,6 +79,7 @@ static __u16 curtreble; static unsigned long curfreq; static int curstereo; static int curmute; +static unsigned long in_use; /* i2c addresses */ #define TDA7318_ADDR 0x88 @@ -340,10 +341,21 @@ static int vidioc_s_audio(struct file *file, void *priv, return 0; } +static int trust_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &in_use) ? -EBUSY : 0; +} + +static int trust_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &in_use); + return 0; +} + static const struct file_operations trust_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = trust_exclusive_open, + .release = trust_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -370,6 +382,7 @@ static struct video_device trust_radio = { .name = "Trust FM Radio", .fops = &trust_fops, .ioctl_ops = &trust_ioctl_ops, + .release = video_device_release_empty, }; static int __init trust_init(void) @@ -382,8 +395,7 @@ static int __init trust_init(void) printk(KERN_ERR "trust: port 0x%x already in use\n", io); return -EBUSY; } - if(video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr)==-1) - { + if (video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io, 2); return -EINVAL; } diff --git a/linux/drivers/media/radio/radio-typhoon.c b/linux/drivers/media/radio/radio-typhoon.c index fa66168d7..c7ea37389 100644 --- a/linux/drivers/media/radio/radio-typhoon.c +++ b/linux/drivers/media/radio/radio-typhoon.c @@ -80,7 +80,7 @@ static struct v4l2_queryctrl radio_qctrl[] = { #endif struct typhoon_device { - int users; + unsigned long in_use; int iobase; int curvol; int muted; @@ -224,8 +224,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct typhoon_device *typhoon = dev->priv; + struct typhoon_device *typhoon = video_drvdata(file); typhoon->curfreq = f->frequency; typhoon_setfreq(typhoon, typhoon->curfreq); @@ -235,8 +234,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct typhoon_device *typhoon = dev->priv; + struct typhoon_device *typhoon = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = typhoon->curfreq; @@ -262,8 +260,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct typhoon_device *typhoon = dev->priv; + struct typhoon_device *typhoon = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -279,8 +276,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct typhoon_device *typhoon = dev->priv; + struct typhoon_device *typhoon = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -335,10 +331,21 @@ static struct typhoon_device typhoon_unit = .mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ, }; +static int typhoon_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &typhoon_unit.in_use) ? -EBUSY : 0; +} + +static int typhoon_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &typhoon_unit.in_use); + return 0; +} + static const struct file_operations typhoon_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = typhoon_exclusive_open, + .release = typhoon_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -365,6 +372,7 @@ static struct video_device typhoon_radio = { .name = "Typhoon Radio", .fops = &typhoon_fops, .ioctl_ops = &typhoon_ioctl_ops, + .release = video_device_release_empty, }; #ifdef CONFIG_RADIO_TYPHOON_PROC_FS @@ -447,9 +455,8 @@ static int __init typhoon_init(void) return -EBUSY; } - typhoon_radio.priv = &typhoon_unit; - if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) == -1) - { + video_set_drvdata(&typhoon_radio, &typhoon_unit); + if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io, 8); return -EINVAL; } diff --git a/linux/drivers/media/radio/radio-zoltrix.c b/linux/drivers/media/radio/radio-zoltrix.c index ef6d6a66f..615165b57 100644 --- a/linux/drivers/media/radio/radio-zoltrix.c +++ b/linux/drivers/media/radio/radio-zoltrix.c @@ -70,6 +70,7 @@ static int io = CONFIG_RADIO_ZOLTRIX_PORT; static int radio_nr = -1; struct zol_device { + unsigned long in_use; int port; int curvol; unsigned long curfreq; @@ -246,8 +247,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct video_device *dev = video_devdata(file); - struct zol_device *zol = dev->priv; + struct zol_device *zol = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -277,8 +277,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct zol_device *zol = dev->priv; + struct zol_device *zol = video_drvdata(file); zol->curfreq = f->frequency; zol_setfreq(zol, zol->curfreq); @@ -288,8 +287,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct video_device *dev = video_devdata(file); - struct zol_device *zol = dev->priv; + struct zol_device *zol = video_drvdata(file); f->type = V4L2_TUNER_RADIO; f->frequency = zol->curfreq; @@ -314,8 +312,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct zol_device *zol = dev->priv; + struct zol_device *zol = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -331,8 +328,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct zol_device *zol = dev->priv; + struct zol_device *zol = video_drvdata(file); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -397,11 +393,22 @@ static int vidioc_s_audio(struct file *file, void *priv, static struct zol_device zoltrix_unit; +static int zoltrix_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &zoltrix_unit.in_use) ? -EBUSY : 0; +} + +static int zoltrix_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &zoltrix_unit.in_use); + return 0; +} + static const struct file_operations zoltrix_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = zoltrix_exclusive_open, + .release = zoltrix_exclusive_release, .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -428,6 +435,7 @@ static struct video_device zoltrix_radio = { .name = "Zoltrix Radio Plus", .fops = &zoltrix_fops, .ioctl_ops = &zoltrix_ioctl_ops, + .release = video_device_release_empty, }; static int __init zoltrix_init(void) @@ -441,14 +449,13 @@ static int __init zoltrix_init(void) return -ENXIO; } - zoltrix_radio.priv = &zoltrix_unit; + video_set_drvdata(&zoltrix_radio, &zoltrix_unit); if (!request_region(io, 2, "zoltrix")) { printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io); return -EBUSY; } - if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) == -1) - { + if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) < 0) { release_region(io, 2); return -EINVAL; } diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index d6b1a106a..e28e292fe 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -34,6 +34,7 @@ config VIDEOBUF_DVB select VIDEOBUF_GEN config VIDEO_BTCX + depends on PCI tristate config VIDEO_IR @@ -578,13 +579,6 @@ config VIDEO_SAA5249 To compile this driver as a module, choose M here: the module will be called saa5249. -config TUNER_3036 - tristate "SAB3036 tuner" - depends on I2C && VIDEO_V4L1 - help - Say Y here to include support for Philips SAB3036 compatible tuners. - If in doubt, say N. - config VIDEO_VINO tristate "SGI Vino Video For Linux (EXPERIMENTAL)" depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2 @@ -697,7 +691,7 @@ config VIDEO_MXB depends on PCI && VIDEO_V4L1 && I2C select VIDEO_SAA7146_VV select VIDEO_TUNER - select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA7115 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO @@ -708,21 +702,6 @@ config VIDEO_MXB To compile this driver as a module, choose M here: the module will be called mxb. -config VIDEO_DPC - tristate "Philips-Semiconductors 'dpc7146 demonstration board'" - depends on PCI && VIDEO_V4L1 && I2C - select VIDEO_SAA7146_VV - select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO - ---help--- - This is a video4linux driver for the 'dpc7146 demonstration - board' by Philips-Semiconductors. It's the reference design - for SAA7146 bases boards, so if you have some unsupported - saa7146 based, analog video card, chances are good that it - will work with this skeleton driver. - - To compile this driver as a module, choose M here: the - module will be called dpc7146. - config VIDEO_HEXIUM_ORION tristate "Hexium HV-PCI6 and Orion frame grabber" depends on PCI && VIDEO_V4L2 && I2C @@ -808,6 +787,12 @@ config MT9M001_PCA9536_SWITCH Select this if your MT9M001 camera uses a PCA9536 I2C GPIO extender to switch between 8 and 10 bit datawidth modes +config SOC_CAMERA_MT9M111 + tristate "mt9m111 support" + depends on SOC_CAMERA && I2C + help + This driver supports MT9M111 cameras from Micron + config SOC_CAMERA_MT9V022 tristate "mt9v022 support" depends on SOC_CAMERA && I2C @@ -837,7 +822,7 @@ config VIDEO_PXA27x config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" - depends on VIDEO_DEV && SOC_CAMERA + depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA select VIDEOBUF_DMA_CONTIG ---help--- This is a v4l2 driver for the SuperH Mobile CEU Interface diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index bf429b023..bcd23e3cb 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -20,6 +20,8 @@ ifeq ($(CONFIG_VIDEO_V4L1_COMPAT),y) obj-$(CONFIG_VIDEO_DEV) += v4l1-compat.o endif +obj-$(CONFIG_VIDEO_TUNER) += tuner.o + obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o @@ -83,10 +85,6 @@ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o -obj-$(CONFIG_VIDEO_DPC) += dpc7146.o -obj-$(CONFIG_TUNER_3036) += tuner-3036.o - -obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o @@ -138,6 +136,7 @@ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o +obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o diff --git a/linux/drivers/media/video/arv.c b/linux/drivers/media/video/arv.c index e100d4cfb..a6c2ff1b4 100644 --- a/linux/drivers/media/video/arv.c +++ b/linux/drivers/media/video/arv.c @@ -39,7 +39,7 @@ #include <asm/dma.h> #include <asm/byteorder.h> -#if 0 /* keep */; +#if 0 /* keep */ #define DEBUG(n, args...) printk(args) #define CHECK_LOST 1 #else @@ -117,6 +117,7 @@ struct ar_device { int width, height; int frame_bytes, line_bytes; wait_queue_head_t wait; + unsigned long in_use; struct mutex lock; }; @@ -270,7 +271,7 @@ static inline void wait_for_vertical_sync(int exp_line) static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct video_device *v = video_devdata(file); - struct ar_device *ar = v->priv; + struct ar_device *ar = video_get_drvdata(v); long ret = ar->frame_bytes; /* return read bytes */ unsigned long arvcr1 = 0; unsigned long flags; @@ -400,7 +401,7 @@ static int ar_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); - struct ar_device *ar = dev->priv; + struct ar_device *ar = video_get_drvdata(dev); DEBUG(1, "ar_ioctl()\n"); switch(cmd) { @@ -579,11 +580,11 @@ static void ar_interrupt(int irq, void *dev) * we have to start capture. */ disable_dma(); -#if 0 /* keep */; +#if 0 /* keep */ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */ #endif memcpy(ar->frame[0], ar->line_buff, ar->line_bytes); -#if 0 /* keep */; +#if 0 /* keep */ ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); #endif enable_dma(); @@ -609,7 +610,7 @@ static void ar_interrupt(int irq, void *dev) ar_outl(arvcr1, ARVCR1); /* disable */ wake_up_interruptible(&ar->wait); } else { -#if 0 /* keep */; +#if 0 /* keep */ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); #endif @@ -630,7 +631,7 @@ static void ar_interrupt(int irq, void *dev) */ static int ar_initialize(struct video_device *dev) { - struct ar_device *ar = dev->priv; + struct ar_device *ar = video_get_drvdata(dev); unsigned long cr = 0; int i,found=0; @@ -693,7 +694,7 @@ static int ar_initialize(struct video_device *dev) printk("."); iic(2,0x78,0x8e,0x0c,0x00); iic(2,0x78,0x8f,0x00,0x00); -#if 0 /* keep */; +#if 0 /* keep */ iic(2,0x78,0x90,0x00,0x00); /* AWB on=1 off=0 */ #endif iic(2,0x78,0x93,0x01,0x00); @@ -712,7 +713,7 @@ static int ar_initialize(struct video_device *dev) iic(2,0x78,0x9e,0x2e,0x00); iic(2,0x78,0xb8,0x78,0x00); iic(2,0x78,0xba,0x05,0x00); -#if 0 /* keep */; +#if 0 /* keep */ iic(2,0x78,0x83,0x8c,0x00); /* brightness */ #endif printk("."); @@ -737,7 +738,7 @@ static int ar_initialize(struct video_device *dev) void ar_release(struct video_device *vfd) { - struct ar_device *ar = vfd->priv; + struct ar_device *ar = video_get_drvdata(vfd); mutex_lock(&ar->lock); video_device_release(vfd); } @@ -747,10 +748,23 @@ void ar_release(struct video_device *vfd) * Video4Linux Module functions * ****************************************************************************/ +static struct ar_device ardev; + +static int ar_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &ardev.in_use) ? -EBUSY : 0; +} + +static int ar_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &ardev.in_use); + return 0; +} + static const struct file_operations ar_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = ar_exclusive_open, + .release = ar_exclusive_release, .read = ar_read, .ioctl = ar_ioctl, #ifdef CONFIG_COMPAT @@ -767,7 +781,6 @@ static struct video_device ar_template = { }; #define ALIGN4(x) ((((int)(x)) & 0x3) == 0) -static struct ar_device ardev; static int __init ar_init(void) { @@ -807,7 +820,7 @@ static int __init ar_init(void) return -ENOMEM; } memcpy(ar->vdev, &ar_template, sizeof(ar_template)); - ar->vdev->priv = ar; + video_set_drvdata(ar->vdev, ar); if (vga) { ar->width = AR_WIDTH_VGA; diff --git a/linux/drivers/media/video/au0828/au0828-cards.c b/linux/drivers/media/video/au0828/au0828-cards.c index 14b9fc9bb..ed48908a9 100644 --- a/linux/drivers/media/video/au0828/au0828-cards.c +++ b/linux/drivers/media/video/au0828/au0828-cards.c @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/video/au0828/au0828-cards.h b/linux/drivers/media/video/au0828/au0828-cards.h index 102ed03dc..48a1882c2 100644 --- a/linux/drivers/media/video/au0828/au0828-cards.h +++ b/linux/drivers/media/video/au0828/au0828-cards.h @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/video/au0828/au0828-core.c b/linux/drivers/media/video/au0828/au0828-core.c index 123422f82..59dcb3255 100644 --- a/linux/drivers/media/video/au0828/au0828-core.c +++ b/linux/drivers/media/video/au0828/au0828-core.c @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> * * 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 @@ -253,5 +253,5 @@ module_init(au0828_init); module_exit(au0828_exit); MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products"); -MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); +MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/au0828/au0828-dvb.c b/linux/drivers/media/video/au0828/au0828-dvb.c index 1fa77ba11..24160ca59 100644 --- a/linux/drivers/media/video/au0828/au0828-dvb.c +++ b/linux/drivers/media/video/au0828/au0828-dvb.c @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> * * 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 @@ -363,9 +363,8 @@ int au0828_dvb_register(struct au0828_dev *dev) &hauppauge_hvr950q_config, &dev->i2c_adap); if (dvb->frontend != NULL) - dvb_attach(xc5000_attach, dvb->frontend, - &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig, dev); + dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, + &hauppauge_hvr950q_tunerconfig); break; case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: dvb->frontend = dvb_attach(au8522_attach, diff --git a/linux/drivers/media/video/au0828/au0828-i2c.c b/linux/drivers/media/video/au0828/au0828-i2c.c index 32858dceb..03b797b9b 100644 --- a/linux/drivers/media/video/au0828/au0828-i2c.c +++ b/linux/drivers/media/video/au0828/au0828-i2c.c @@ -1,7 +1,7 @@ /* * Driver for the Auvitek AU0828 USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/video/au0828/au0828-reg.h b/linux/drivers/media/video/au0828/au0828-reg.h index 398275508..1e87fa0c6 100644 --- a/linux/drivers/media/video/au0828/au0828-reg.h +++ b/linux/drivers/media/video/au0828/au0828-reg.h @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/video/au0828/au0828.h b/linux/drivers/media/video/au0828/au0828.h index 7beb57179..4f10ff300 100644 --- a/linux/drivers/media/video/au0828/au0828.h +++ b/linux/drivers/media/video/au0828/au0828.h @@ -1,7 +1,7 @@ /* * Driver for the Auvitek AU0828 USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/video/bt856.c b/linux/drivers/media/video/bt856.c index 50ca987ad..e31c9d4b9 100644 --- a/linux/drivers/media/video/bt856.c +++ b/linux/drivers/media/video/bt856.c @@ -69,8 +69,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* ----------------------------------------------------------------------- */ -#define REG_OFFSET 0xDA -#define BT856_NR_REG 6 +#define BT856_REG_OFFSET 0xDA +#define BT856_NR_REG 6 struct bt856 { unsigned char reg[BT856_NR_REG]; @@ -90,7 +90,7 @@ bt856_write (struct i2c_client *client, { struct bt856 *encoder = i2c_get_clientdata(client); - encoder->reg[reg - REG_OFFSET] = value; + encoder->reg[reg - BT856_REG_OFFSET] = value; return i2c_smbus_write_byte_data(client, reg, value); } @@ -104,7 +104,7 @@ bt856_setbit (struct i2c_client *client, return bt856_write(client, reg, (encoder-> - reg[reg - REG_OFFSET] & ~(1 << bit)) | + reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) | (value ? (1 << bit) : 0)); } diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index bf3339436..d67e127f1 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -3201,8 +3201,9 @@ static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256]) static void flyvideo_gpio(struct bttv *btv) { - int gpio,has_remote,has_radio,is_capture_only,is_lr90,has_tda9820_tda9821; - int tuner=UNSET,ttype; + int gpio, has_remote, has_radio, is_capture_only; + int is_lr90, has_tda9820_tda9821; + int tuner_type = UNSET, ttype; gpio_inout(0xffffff, 0); udelay(8); /* without this we would see the 0x1800 mask */ @@ -3220,20 +3221,26 @@ static void flyvideo_gpio(struct bttv *btv) * xxxF00(LR26/LR50), xxxFE0(LR90): Remote control chip (LVA001 or CF45) soldered * Note: Some bits are Audio_Mask ! */ - ttype=(gpio&0x0f0000)>>16; - switch(ttype) { - case 0x0: tuner=2; /* NTSC, e.g. TPI8NSR11P */ + ttype = (gpio & 0x0f0000) >> 16; + switch (ttype) { + case 0x0: + tuner_type = 2; /* NTSC, e.g. TPI8NSR11P */ break; - case 0x2: tuner=39;/* LG NTSC (newer TAPC series) TAPC-H701P */ + case 0x2: + tuner_type = 39; /* LG NTSC (newer TAPC series) TAPC-H701P */ break; - case 0x4: tuner=5; /* Philips PAL TPI8PSB02P, TPI8PSB12P, TPI8PSB12D or FI1216, FM1216 */ + case 0x4: + tuner_type = 5; /* Philips PAL TPI8PSB02P, TPI8PSB12P, TPI8PSB12D or FI1216, FM1216 */ break; - case 0x6: tuner=37;/* LG PAL (newer TAPC series) TAPC-G702P */ + case 0x6: + tuner_type = 37; /* LG PAL (newer TAPC series) TAPC-G702P */ break; - case 0xC: tuner=3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */ + case 0xC: + tuner_type = 3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */ break; default: printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->c.nr); + break; } has_remote = gpio & 0x800000; @@ -3246,23 +3253,26 @@ static void flyvideo_gpio(struct bttv *btv) /* * gpio & 0x001000 output bit for audio routing */ - if(is_capture_only) - tuner = TUNER_ABSENT; /* No tuner present */ + if (is_capture_only) + tuner_type = TUNER_ABSENT; /* No tuner present */ printk(KERN_INFO "bttv%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", - btv->c.nr, has_radio? "yes":"no ", has_remote? "yes":"no ", tuner, gpio); + btv->c.nr, has_radio ? "yes" : "no ", + has_remote ? "yes" : "no ", tuner_type, gpio); printk(KERN_INFO "bttv%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n", - btv->c.nr, is_lr90?"yes":"no ", has_tda9820_tda9821?"yes":"no ", - is_capture_only?"yes":"no "); + btv->c.nr, is_lr90 ? "yes" : "no ", + has_tda9820_tda9821 ? "yes" : "no ", + is_capture_only ? "yes" : "no "); - if (tuner != UNSET) /* only set if known tuner autodetected, else let insmod option through */ - btv->tuner_type = tuner; + if (tuner_type != UNSET) /* only set if known tuner autodetected, else let insmod option through */ + btv->tuner_type = tuner_type; btv->has_radio = has_radio; /* LR90 Audio Routing is done by 2 hef4052, so Audio_Mask has 4 bits: 0x001c80 * LR26/LR50 only has 1 hef4052, Audio_Mask 0x000c00 * Audio options: from tuner, from tda9821/tda9821(mono,stereo,sap), from tda9874, ext., mute */ - if(has_tda9820_tda9821) btv->audio_mode_gpio = lt9415_audio; + if (has_tda9820_tda9821) + btv->audio_mode_gpio = lt9415_audio; /* todo: if(has_tda9874) btv->audio_mode_gpio = fv2000s_audio; */ } @@ -4036,7 +4046,7 @@ static int tuner_1_table[] = { static void __devinit avermedia_eeprom(struct bttv *btv) { - int tuner_make,tuner_tv_fm,tuner_format,tuner=0; + int tuner_make, tuner_tv_fm, tuner_format, tuner_type = 0; tuner_make = (eeprom_data[0x41] & 0x7); tuner_tv_fm = (eeprom_data[0x41] & 0x18) >> 3; @@ -4044,24 +4054,24 @@ static void __devinit avermedia_eeprom(struct bttv *btv) btv->has_remote = (eeprom_data[0x42] & 0x01); if (tuner_make == 0 || tuner_make == 2) - if(tuner_format <=0x0a) - tuner = tuner_0_table[tuner_format]; + if (tuner_format <= 0x0a) + tuner_type = tuner_0_table[tuner_format]; if (tuner_make == 1) - if(tuner_format <=9) - tuner = tuner_1_table[tuner_format]; + if (tuner_format <= 9) + tuner_type = tuner_1_table[tuner_format]; if (tuner_make == 4) - if(tuner_format == 0x09) - tuner = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */ + if (tuner_format == 0x09) + tuner_type = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */ printk(KERN_INFO "bttv%d: Avermedia eeprom[0x%02x%02x]: tuner=", - btv->c.nr,eeprom_data[0x41],eeprom_data[0x42]); - if(tuner) { - btv->tuner_type=tuner; - printk("%d",tuner); + btv->c.nr, eeprom_data[0x41], eeprom_data[0x42]); + if (tuner_type) { + btv->tuner_type = tuner_type; + printk(KERN_CONT "%d", tuner_type); } else - printk("Unknown type"); - printk(" radio:%s remote control:%s\n", + printk(KERN_CONT "Unknown type"); + printk(KERN_CONT " radio:%s remote control:%s\n", tuner_tv_fm ? "yes" : "no", btv->has_remote ? "yes" : "no"); } @@ -4103,7 +4113,8 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin) gpio_inout(mask,mask); gpio_bits(mask,0); - udelay(2500); + mdelay(2); + udelay(500); gpio_bits(mask,mask); if (bttv_gpio) diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 012eeba5e..af7272665 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -76,9 +76,9 @@ static unsigned int gbuffers = 8; static unsigned int gbufsize = 0x208000; static unsigned int reset_crop = 1; -static int video_nr = -1; -static int radio_nr = -1; -static int vbi_nr = -1; +static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; +static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; +static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; static int debug_latency; static unsigned int fdsr; @@ -96,7 +96,6 @@ static unsigned int irq_iswitch; static unsigned int uv_ratio = 50; static unsigned int full_luma_range; static unsigned int coring; -extern int no_overlay; /* API features (turn on/off stuff for testing) */ static unsigned int v4l2 = 1; @@ -109,9 +108,6 @@ module_param(irq_debug, int, 0644); module_param(debug_latency, int, 0644); module_param(fdsr, int, 0444); -module_param(video_nr, int, 0444); -module_param(radio_nr, int, 0444); -module_param(vbi_nr, int, 0444); module_param(gbuffers, int, 0444); module_param(gbufsize, int, 0444); module_param(reset_crop, int, 0444); @@ -131,7 +127,10 @@ module_param(uv_ratio, int, 0444); module_param(full_luma_range, int, 0444); module_param(coring, int, 0444); -module_param_array(radio, int, NULL, 0444); +module_param_array(radio, int, NULL, 0444); +module_param_array(video_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); +module_param_array(vbi_nr, int, NULL, 0444); MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)"); MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian"); @@ -153,6 +152,9 @@ MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler"); MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50"); MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)"); MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)"); +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards"); MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); @@ -1388,7 +1390,7 @@ static void init_irqreg(struct bttv *btv) (btv->gpioirq ? BT848_INT_GPINT : 0) | BT848_INT_SCERR | (fdsr ? BT848_INT_FDSR : 0) | - BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| + BT848_INT_RISCI | BT848_INT_OCERR | BT848_INT_FMTCHG|BT848_INT_HLOCK| BT848_INT_I2CDONE, BT848_INT_MASK); @@ -3464,7 +3466,7 @@ static int radio_open(struct inode *inode, struct file *file) lock_kernel(); for (i = 0; i < bttv_num; i++) { - if (bttvs[i].radio_dev->minor == minor) { + if (bttvs[i].radio_dev && bttvs[i].radio_dev->minor == minor) { btv = &bttvs[i]; break; } @@ -4277,7 +4279,8 @@ static int __devinit bttv_register_video(struct bttv *btv) if (NULL == btv->video_dev) goto err; - if (video_register_device(btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0) + if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER, + video_nr[btv->c.nr]) < 0) goto err; printk(KERN_INFO "bttv%d: registered device video%d\n", btv->c.nr,btv->video_dev->minor & 0x1f); @@ -4293,7 +4296,8 @@ static int __devinit bttv_register_video(struct bttv *btv) if (NULL == btv->vbi_dev) goto err; - if (video_register_device(btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) + if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI, + vbi_nr[btv->c.nr]) < 0) goto err; printk(KERN_INFO "bttv%d: registered device vbi%d\n", btv->c.nr,btv->vbi_dev->minor & 0x1f); @@ -4304,7 +4308,8 @@ static int __devinit bttv_register_video(struct bttv *btv) btv->radio_dev = vdev_init(btv, &radio_template, "radio"); if (NULL == btv->radio_dev) goto err; - if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0) + if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO, + radio_nr[btv->c.nr]) < 0) goto err; printk(KERN_INFO "bttv%d: registered device radio%d\n", btv->c.nr,btv->radio_dev->minor & 0x1f); diff --git a/linux/drivers/media/video/bt8xx/bttv-risc.c b/linux/drivers/media/video/bt8xx/bttv-risc.c index 0cffd3a72..310924f77 100644 --- a/linux/drivers/media/video/bt8xx/bttv-risc.c +++ b/linux/drivers/media/video/bt8xx/bttv-risc.c @@ -244,7 +244,8 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, const struct bttv_format *fmt, struct bttv_overlay *ov, int skip_even, int skip_odd) { - int dwords,rc,line,maxy,start,end,skip,nskips; + int dwords, rc, line, maxy, start, end; + unsigned skip, nskips; struct btcx_skiplist *skips; __le32 *rp; u32 ri,ra; diff --git a/linux/drivers/media/video/bt8xx/bttvp.h b/linux/drivers/media/video/bt8xx/bttvp.h index 63d1d4451..403695f9a 100644 --- a/linux/drivers/media/video/bt8xx/bttvp.h +++ b/linux/drivers/media/video/bt8xx/bttvp.h @@ -269,6 +269,11 @@ int bttv_sub_add_device(struct bttv_core *core, char *name); int bttv_sub_del_devices(struct bttv_core *core); /* ---------------------------------------------------------- */ +/* bttv-cards.c */ + +extern int no_overlay; + +/* ---------------------------------------------------------- */ /* bttv-driver.c */ /* insmod options */ diff --git a/linux/drivers/media/video/btcx-risc.c b/linux/drivers/media/video/btcx-risc.c index e0e1158ce..1c2413571 100644 --- a/linux/drivers/media/video/btcx-risc.c +++ b/linux/drivers/media/video/btcx-risc.c @@ -65,7 +65,7 @@ int btcx_riscmem_alloc(struct pci_dev *pci, unsigned int size) { __le32 *cpu; - dma_addr_t dma; + dma_addr_t dma = 0; if (NULL != risc->cpu && risc->size < size) btcx_riscmem_free(pci,risc); @@ -185,12 +185,12 @@ btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) } void -btcx_calc_skips(int line, int width, unsigned int *maxy, +btcx_calc_skips(int line, int width, int *maxy, struct btcx_skiplist *skips, unsigned int *nskips, const struct v4l2_clip *clips, unsigned int nclips) { unsigned int clip,skip; - int end,maxline; + int end, maxline; skip=0; maxline = 9999; diff --git a/linux/drivers/media/video/btcx-risc.h b/linux/drivers/media/video/btcx-risc.h index 861bc8112..f8bc6e8e7 100644 --- a/linux/drivers/media/video/btcx-risc.h +++ b/linux/drivers/media/video/btcx-risc.h @@ -23,7 +23,7 @@ int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask); void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); -void btcx_calc_skips(int line, int width, unsigned int *maxy, +void btcx_calc_skips(int line, int width, int *maxy, struct btcx_skiplist *skips, unsigned int *nskips, const struct v4l2_clip *clips, unsigned int nclips); diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c index df2b25b69..ca8fabdf3 100644 --- a/linux/drivers/media/video/bw-qcam.c +++ b/linux/drivers/media/video/bw-qcam.c @@ -496,7 +496,7 @@ static void qc_set(struct qcam_device *q) val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * q->transfer_scale; } - val = (val + val2 - 1) / val2; + val = DIV_ROUND_UP(val, val2); qc_command(q, 0x13); qc_command(q, val); @@ -652,7 +652,7 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l transperline = q->width * q->bpp; divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * q->transfer_scale; - transperline = (transperline + divisor - 1) / divisor; + transperline = DIV_ROUND_UP(transperline, divisor); for (i = 0, yield = yieldlines; i < linestotrans; i++) { @@ -895,10 +895,27 @@ static ssize_t qcam_read(struct file *file, char __user *buf, return len; } +static int qcam_exclusive_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct qcam_device *qcam = (struct qcam_device *)dev; + + return test_and_set_bit(0, &qcam->in_use) ? -EBUSY : 0; +} + +static int qcam_exclusive_release(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct qcam_device *qcam = (struct qcam_device *)dev; + + clear_bit(0, &qcam->in_use); + return 0; +} + static const struct file_operations qcam_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = qcam_exclusive_open, + .release = qcam_exclusive_release, .ioctl = qcam_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -910,6 +927,7 @@ static struct video_device qcam_template= { .name = "Connectix Quickcam", .fops = &qcam_fops, + .release = video_device_release_empty, }; #define MAX_CAMS 4 @@ -947,8 +965,7 @@ static int init_bwqcam(struct parport *port) printk(KERN_INFO "Connectix Quickcam on %s\n", qcam->pport->name); - if(video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr)==-1) - { + if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { parport_unregister_device(qcam->pdev); kfree(qcam); return -ENODEV; diff --git a/linux/drivers/media/video/bw-qcam.h b/linux/drivers/media/video/bw-qcam.h index 6701dafbc..8a60c5de0 100644 --- a/linux/drivers/media/video/bw-qcam.h +++ b/linux/drivers/media/video/bw-qcam.h @@ -65,4 +65,5 @@ struct qcam_device { int top, left; int status; unsigned int saved_bits; + unsigned long in_use; }; diff --git a/linux/drivers/media/video/c-qcam.c b/linux/drivers/media/video/c-qcam.c index fe8937ed1..ba4d43b90 100644 --- a/linux/drivers/media/video/c-qcam.c +++ b/linux/drivers/media/video/c-qcam.c @@ -52,6 +52,7 @@ struct qcam_device { int contrast, brightness, whitebal; int top, left; unsigned int bidirectional; + unsigned long in_use; struct mutex lock; }; @@ -688,11 +689,28 @@ static ssize_t qcam_read(struct file *file, char __user *buf, return len; } +static int qcam_exclusive_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct qcam_device *qcam = (struct qcam_device *)dev; + + return test_and_set_bit(0, &qcam->in_use) ? -EBUSY : 0; +} + +static int qcam_exclusive_release(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct qcam_device *qcam = (struct qcam_device *)dev; + + clear_bit(0, &qcam->in_use); + return 0; +} + /* video device template */ static const struct file_operations qcam_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = qcam_exclusive_open, + .release = qcam_exclusive_release, .ioctl = qcam_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -705,6 +723,7 @@ static struct video_device qcam_template= { .name = "Colour QuickCam", .fops = &qcam_fops, + .release = video_device_release_empty, }; /* Initialize the QuickCam driver control structure. */ @@ -788,8 +807,7 @@ static int init_cqcam(struct parport *port) parport_release(qcam->pdev); - if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr)==-1) - { + if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { printk(KERN_ERR "Unable to register Colour QuickCam on %s\n", qcam->pport->name); parport_unregister_device(qcam->pdev); diff --git a/linux/drivers/media/video/cafe_ccic.c b/linux/drivers/media/video/cafe_ccic.c index 4c0d44c5d..26c08f00b 100644 --- a/linux/drivers/media/video/cafe_ccic.c +++ b/linux/drivers/media/video/cafe_ccic.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> +#include <linux/mm.h> #include <linux/pci.h> #include <linux/i2c.h> #include <linux/interrupt.h> diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c index 21c1f4dbb..0bee440ab 100644 --- a/linux/drivers/media/video/cpia.c +++ b/linux/drivers/media/video/cpia.c @@ -44,6 +44,7 @@ #endif #include "cpia.h" +#include "compat.h" static int video_nr = -1; @@ -3155,7 +3156,7 @@ static void put_cam(struct cpia_camera_ops* ops) static int cpia_open(struct inode *inode, struct file *file) { struct video_device *dev = video_devdata(file); - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); int err; if (!cam) { @@ -3202,7 +3203,7 @@ static int cpia_open(struct inode *inode, struct file *file) /* Set ownership of /proc/cpia/videoX to current user */ if(cam->proc_entry) - cam->proc_entry->uid = current->uid; + cam->proc_entry->uid = current_uid(); /* set mark for loading first frame uncompressed */ cam->first_frame = 1; @@ -3232,7 +3233,7 @@ static int cpia_open(struct inode *inode, struct file *file) static int cpia_close(struct inode *inode, struct file *file) { struct video_device *dev = file->private_data; - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); if (cam->ops) { /* Return ownership of /proc/cpia/videoX to root */ @@ -3284,7 +3285,7 @@ static ssize_t cpia_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct video_device *dev = file->private_data; - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); int err; /* make this _really_ smp and multithread-safe */ @@ -3341,7 +3342,7 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, void *arg) { struct video_device *dev = file->private_data; - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); int retval = 0; if (!cam || !cam->ops) @@ -3739,7 +3740,7 @@ static int cpia_mmap(struct file *file, struct vm_area_struct *vma) unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; unsigned long page, pos; - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); int retval; if (!cam || !cam->ops) @@ -3801,6 +3802,7 @@ static const struct file_operations cpia_fops = { static struct video_device cpia_template = { .name = "CPiA Camera", .fops = &cpia_fops, + .release = video_device_release_empty, }; /* initialise cam_data structure */ @@ -3928,7 +3930,7 @@ static void init_camera_struct(struct cam_data *cam, cam->proc_entry = NULL; memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); - cam->vdev.priv = cam; + video_set_drvdata(&cam->vdev, cam); cam->curframe = 0; for (i = 0; i < FRAME_NUM; i++) { @@ -3955,7 +3957,7 @@ struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowleve camera->lowlevel_data = lowlevel; /* register v4l device */ - if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { kfree(camera); printk(KERN_DEBUG "video_register_device failed\n"); return NULL; diff --git a/linux/drivers/media/video/cpia2/cpia2_core.c b/linux/drivers/media/video/cpia2/cpia2_core.c index ce43a054a..8e55d3a21 100644 --- a/linux/drivers/media/video/cpia2/cpia2_core.c +++ b/linux/drivers/media/video/cpia2/cpia2_core.c @@ -1563,7 +1563,7 @@ static int config_sensor_500(struct camera_data *cam, * * This sets all user changeable properties to the values in cam->params. *****************************************************************************/ -int set_all_properties(struct camera_data *cam) +static int set_all_properties(struct camera_data *cam) { /** * Don't set target_kb here, it will be set later. @@ -1614,7 +1614,7 @@ void cpia2_save_camera_state(struct camera_data *cam) * get_color_params * *****************************************************************************/ -void get_color_params(struct camera_data *cam) +static void get_color_params(struct camera_data *cam) { cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, TRANSFER_READ, 0); cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, TRANSFER_READ, 0); @@ -1907,7 +1907,7 @@ void cpia2_set_saturation(struct camera_data *cam, unsigned char value) * wake_system * *****************************************************************************/ -void wake_system(struct camera_data *cam) +static void wake_system(struct camera_data *cam) { cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0); } @@ -1918,7 +1918,7 @@ void wake_system(struct camera_data *cam) * * Valid for STV500 sensor only *****************************************************************************/ -void set_lowlight_boost(struct camera_data *cam) +static void set_lowlight_boost(struct camera_data *cam) { struct cpia2_command cmd; @@ -2195,7 +2195,7 @@ void cpia2_dbg_dump_registers(struct camera_data *cam) * * Sets all values to the defaults *****************************************************************************/ -void reset_camera_struct(struct camera_data *cam) +static void reset_camera_struct(struct camera_data *cam) { /*** * The following parameter values are the defaults from the register map. diff --git a/linux/drivers/media/video/cpia2/cpia2_usb.c b/linux/drivers/media/video/cpia2/cpia2_usb.c index 01131e984..bd9277873 100644 --- a/linux/drivers/media/video/cpia2/cpia2_usb.c +++ b/linux/drivers/media/video/cpia2/cpia2_usb.c @@ -486,7 +486,7 @@ int cpia2_usb_change_streaming_alternate(struct camera_data *cam, * set_alternate * *****************************************************************************/ -int set_alternate(struct camera_data *cam, unsigned int alt) +static int set_alternate(struct camera_data *cam, unsigned int alt) { int ret = 0; @@ -640,7 +640,7 @@ int cpia2_usb_transfer_cmd(struct camera_data *cam, static int submit_urbs(struct camera_data *cam) { struct urb *urb; - int fx, err, i; + int fx, err, i, j; for(i=0; i<NUM_SBUF; ++i) { if (cam->sbuf[i].data) @@ -665,6 +665,9 @@ static int submit_urbs(struct camera_data *cam) } urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (!urb) { + ERR("%s: usb_alloc_urb error!\n", __func__); + for (j = 0; j < i; j++) + usb_free_urb(cam->sbuf[j].urb); return -ENOMEM; } diff --git a/linux/drivers/media/video/cpia2/cpia2_v4l.c b/linux/drivers/media/video/cpia2/cpia2_v4l.c index 515c8b57a..897e8d1a5 100644 --- a/linux/drivers/media/video/cpia2/cpia2_v4l.c +++ b/linux/drivers/media/video/cpia2/cpia2_v4l.c @@ -241,8 +241,7 @@ static struct v4l2_queryctrl controls[] = { *****************************************************************************/ static int cpia2_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); + struct camera_data *cam = video_drvdata(file); int retval = 0; if (!cam) { @@ -357,8 +356,7 @@ static int cpia2_close(struct inode *inode, struct file *file) static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, loff_t *off) { - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); + struct camera_data *cam = video_drvdata(file); int noblock = file->f_flags&O_NONBLOCK; struct cpia2_fh *fh = file->private_data; @@ -382,9 +380,7 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, *****************************************************************************/ static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) { - struct video_device *dev = video_devdata(filp); - struct camera_data *cam = video_get_drvdata(dev); - + struct camera_data *cam = video_drvdata(filp); struct cpia2_fh *fh = filp->private_data; if(!cam) @@ -1024,7 +1020,6 @@ static int ioctl_queryctrl(void *arg,struct camera_data *cam) if(cam->params.pnp_id.device_type == DEVICE_STV_672 && cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){ // Maximum 15fps - int i; for(i=0; i<c->maximum; ++i) { if(framerate_controls[i].value == CPIA2_VP_FRAMERATE_15) { @@ -1580,8 +1575,7 @@ static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file) static int cpia2_do_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_nr, void *arg) { - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); + struct camera_data *cam = video_drvdata(file); int retval = 0; if (!cam) @@ -1861,9 +1855,8 @@ static int cpia2_ioctl(struct inode *inode, struct file *file, *****************************************************************************/ static int cpia2_mmap(struct file *file, struct vm_area_struct *area) { + struct camera_data *cam = video_drvdata(file); int retval; - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); /* Priority check */ struct cpia2_fh *fh = file->private_data; @@ -1959,8 +1952,7 @@ int cpia2_register_camera(struct camera_data *cam) reset_camera_struct_v4l(cam); /* register v4l device */ - if (video_register_device - (cam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { ERR("video_register_device failed\n"); video_device_release(cam->vdev); return -ENODEV; diff --git a/linux/drivers/media/video/cs5345.c b/linux/drivers/media/video/cs5345.c index e4c505d2f..decbc5f1f 100644 --- a/linux/drivers/media/video/cs5345.c +++ b/linux/drivers/media/video/cs5345.c @@ -36,7 +36,7 @@ static int debug; module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debugging messages\n\t\t\t0=Off (default), 1=On"); +MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) static unsigned short normal_i2c[] = { 0x98 >> 1, I2C_CLIENT_END }; diff --git a/linux/drivers/media/video/cs53l32a.c b/linux/drivers/media/video/cs53l32a.c index f5ea3f4a9..68f31270c 100644 --- a/linux/drivers/media/video/cs53l32a.c +++ b/linux/drivers/media/video/cs53l32a.c @@ -40,7 +40,7 @@ static int debug; module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debugging messages\n\t\t\t0=Off (default), 1=On"); +MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; diff --git a/linux/drivers/media/video/cx18/Makefile b/linux/drivers/media/video/cx18/Makefile index b23d2e261..f7bf0edf9 100644 --- a/linux/drivers/media/video/cx18/Makefile +++ b/linux/drivers/media/video/cx18/Makefile @@ -2,7 +2,7 @@ cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio. cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \ cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \ cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \ - cx18-dvb.o + cx18-dvb.o cx18-io.o obj-$(CONFIG_VIDEO_CX18) += cx18.o diff --git a/linux/drivers/media/video/cx18/cx18-audio.c b/linux/drivers/media/video/cx18/cx18-audio.c index 6d5b94fc7..57beddf0a 100644 --- a/linux/drivers/media/video/cx18/cx18-audio.c +++ b/linux/drivers/media/video/cx18/cx18-audio.c @@ -22,6 +22,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-i2c.h" #include "cx18-cards.h" #include "cx18-audio.h" @@ -60,10 +61,10 @@ int cx18_audio_set_io(struct cx18 *cx) if (err) return err; - val = read_reg(CX18_AUDIO_ENABLE) & ~0x30; + val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30; val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : (audio_input << 4); - write_reg(val | 0xb00, CX18_AUDIO_ENABLE); + cx18_write_reg(cx, val | 0xb00, CX18_AUDIO_ENABLE); cx18_vapi(cx, CX18_APU_RESETAI, 1, 0); return 0; } diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 3b0a2c450..d8626e354 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/drivers/media/video/cx18/cx18-av-core.c @@ -22,27 +22,29 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) { - u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3)); + u32 reg = 0xc40000 + (addr & ~3); u32 mask = 0xff; int shift = (addr & 3) * 8; + u32 x = cx18_read_reg(cx, reg); x = (x & ~(mask << shift)) | ((u32)value << shift); - writel(x, cx->reg_mem + 0xc40000 + (addr & ~3)); + cx18_write_reg(cx, x, reg); return 0; } int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value) { - writel(value, cx->reg_mem + 0xc40000 + addr); + cx18_write_reg(cx, value, 0xc40000 + addr); return 0; } u8 cx18_av_read(struct cx18 *cx, u16 addr) { - u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3)); + u32 x = cx18_read_reg(cx, 0xc40000 + (addr & ~3)); int shift = (addr & 3) * 8; return (x >> shift) & 0xff; @@ -50,7 +52,7 @@ u8 cx18_av_read(struct cx18 *cx, u16 addr) u32 cx18_av_read4(struct cx18 *cx, u16 addr) { - return readl(cx->reg_mem + 0xc40000 + addr); + return cx18_read_reg(cx, 0xc40000 + addr); } int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask, diff --git a/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c index 834b92482..0488b6297 100644 --- a/linux/drivers/media/video/cx18/cx18-av-firmware.c +++ b/linux/drivers/media/video/cx18/cx18-av-firmware.c @@ -20,6 +20,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include <linux/firmware.h> #define CX18_AUDIO_ENABLE 0xc72014 @@ -32,7 +33,7 @@ int cx18_av_loadfw(struct cx18 *cx) u32 v; const u8 *ptr; int i; - int retries = 0; + int retries1 = 0; if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) { CX18_ERR("unable to open firmware %s\n", FWFILE); @@ -41,7 +42,7 @@ int cx18_av_loadfw(struct cx18 *cx) /* The firmware load often has byte errors, so allow for several retries, both at byte level and at the firmware load level. */ - while (retries < 5) { + while (retries1 < 5) { cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); @@ -57,9 +58,9 @@ int cx18_av_loadfw(struct cx18 *cx) for (i = 0; i < size; i++) { u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16); u32 value = 0; - int retries; + int retries2; - for (retries = 0; retries < 5; retries++) { + for (retries2 = 0; retries2 < 5; retries2++) { cx18_av_write4(cx, CXADEC_DL_CTL, dl_control); udelay(10); value = cx18_av_read4(cx, CXADEC_DL_CTL); @@ -69,18 +70,18 @@ int cx18_av_loadfw(struct cx18 *cx) the address. We can only write the lower address byte of the address. */ if ((value & 0x3F00) != (dl_control & 0x3F00)) { - retries = 5; + retries2 = 5; break; } } - if (retries >= 5) + if (retries2 >= 5) break; } if (i == size) break; - retries++; + retries1++; } - if (retries >= 5) { + if (retries1 >= 5) { CX18_ERR("unable to load firmware %s\n", FWFILE); release_firmware(fw); return -EIO; @@ -119,10 +120,10 @@ int cx18_av_loadfw(struct cx18 *cx) have a name in the spec. */ cx18_av_write4(cx, 0x09CC, 1); - v = read_reg(CX18_AUDIO_ENABLE); - /* If bit 11 is 1 */ + v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); + /* If bit 11 is 1, clear bit 10 */ if (v & 0x800) - write_reg(v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Clear bit 10 */ + cx18_write_reg(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Enable WW auto audio standard detection */ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); diff --git a/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c index 8fe5f38c4..3cb9734ec 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.c +++ b/linux/drivers/media/video/cx18/cx18-cards.c @@ -163,7 +163,7 @@ static const struct cx18_card cx18_card_h900 = { }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX18_AV_AUDIO8, 0 }, + CX18_AV_AUDIO5, 0 }, { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 0 }, }, diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 22434aadd..4de7b501f 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -4,6 +4,7 @@ * Derived from ivtv-driver.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> * * 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 @@ -22,6 +23,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-version.h" #include "cx18-cards.h" #include "cx18-i2c.h" @@ -73,10 +75,14 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - -static int cardtype_c = 1; -static int tuner_c = 1; -static int radio_c = 1; +static int mmio_ndelay[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; +static unsigned cardtype_c = 1; +static unsigned tuner_c = 1; +static unsigned radio_c = 1; +static unsigned mmio_ndelay_c = 1; static char pal[] = "--"; static char secam[] = "--"; static char ntsc[] = "-"; @@ -95,6 +101,7 @@ int cx18_debug; module_param_array(tuner, int, &tuner_c, 0644); module_param_array(radio, bool, &radio_c, 0644); module_param_array(cardtype, int, &cardtype_c, 0644); +module_param_array(mmio_ndelay, int, &mmio_ndelay_c, 0644); module_param_string(pal, pal, sizeof(pal), 0644); module_param_string(secam, secam, sizeof(secam), 0644); module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); @@ -140,6 +147,11 @@ MODULE_PARM_DESC(debug, MODULE_PARM_DESC(cx18_pci_latency, "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" "\t\t\tDefault: Yes"); +MODULE_PARM_DESC(mmio_ndelay, + "Delay (ns) for each CX23418 memory mapped IO access.\n" + "\t\t\tTry larger values that are close to a multiple of the\n" + "\t\t\tPCI clock period, 30.3 ns, if your card doesn't work.\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_MMIO_NDELAY)); MODULE_PARM_DESC(enc_mpg_buffers, "Encoder MPG Buffers (in MB)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); @@ -356,6 +368,11 @@ static void cx18_process_options(struct cx18 *cx) cx->options.tuner = tuner[cx->num]; cx->options.radio = radio[cx->num]; + if (mmio_ndelay[cx->num] < 0) + cx->options.mmio_ndelay = CX18_DEFAULT_MMIO_NDELAY; + else + cx->options.mmio_ndelay = mmio_ndelay[cx->num]; + cx->std = cx18_parse_std(cx); if (cx->options.cardtype == -1) { CX18_INFO("Ignore card\n"); @@ -395,9 +412,9 @@ done: if (cx->card == NULL) { cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); - CX18_ERR("Unknown card: vendor/device: %04x/%04x\n", + CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n", cx->dev->vendor, cx->dev->device); - CX18_ERR(" subsystem vendor/device: %04x/%04x\n", + CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n", cx->dev->subsystem_vendor, cx->dev->subsystem_device); CX18_ERR("Defaulting to %s card\n", cx->card->name); CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); @@ -511,9 +528,9 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, return -EIO; } - /* Check for bus mastering */ + /* Enable bus mastering and memory mapped IO for the CX23418 */ pci_read_config_word(dev, PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, cmd); pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev); @@ -525,11 +542,6 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); } - /* This config space value relates to DMA latencies. The - default value 0x8080 is too low however and will lead - to DMA errors. 0xffff is the max value which solves - these problems. */ - pci_write_config_dword(dev, 0x40, 0xffff); CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, " "irq: %d, latency: %d, memory: 0x%lx\n", @@ -656,7 +668,7 @@ static int __devinit cx18_probe(struct pci_dev *dev, goto free_mem; } cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET; - devtype = read_reg(0xC72028); + devtype = cx18_read_reg(cx, 0xC72028); switch (devtype & 0xff000000) { case 0xff000000: CX18_INFO("cx23418 revision %08x (A)\n", devtype); @@ -902,8 +914,8 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_stop_all_captures(cx); /* Interrupts */ - sw1_irq_disable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); - sw2_irq_disable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); + cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); cx18_halt_firmware(cx); diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 4801bc7fb..66cb74887 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -38,7 +38,6 @@ #include <linux/i2c-algo-bit.h> #include <linux/list.h> #include <linux/unistd.h> -#include <linux/byteorder/swab.h> #include <linux/pagemap.h> #include <linux/workqueue.h> #include <linux/mutex.h> @@ -64,6 +63,9 @@ # error "This driver requires kernel PCI support." #endif +/* Default delay to throttle mmio access to the CX23418 */ +#define CX18_DEFAULT_MMIO_NDELAY 0 /* 0 ns = 0 PCI clock(s) / 33 MHz */ + #define CX18_MEM_OFFSET 0x00000000 #define CX18_MEM_SIZE 0x04000000 #define CX18_REG_OFFSET 0x02000000 @@ -177,6 +179,7 @@ struct cx18_options { int cardtype; /* force card type on load */ int tuner; /* set tuner on load */ int radio; /* enable/disable radio */ + unsigned long mmio_ndelay; /* delay in ns after every PCI mmio access */ }; /* per-buffer bit flags */ @@ -216,8 +219,7 @@ struct cx18_buffer { struct cx18_queue { struct list_head list; - u32 buffers; - u32 length; + atomic_t buffers; u32 bytesused; }; @@ -237,6 +239,8 @@ struct cx18_dvb { struct cx18; /* forward reference */ struct cx18_scb; /* forward reference */ +#define CX18_INVALID_TASK_HANDLE 0xffffffff + struct cx18_stream { /* These first four fields are always set, even if the stream is not actually created. */ @@ -259,7 +263,6 @@ struct cx18_stream { /* Buffer Stats */ u32 buffers; u32 buf_size; - u32 buffers_stolen; /* Buffer Queues */ struct cx18_queue q_free; /* free buffers */ @@ -458,47 +461,4 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv); /* First-open initialization: load firmware, etc. */ int cx18_init_on_first_open(struct cx18 *cx); -/* This is a PCI post thing, where if the pci register is not read, then - the write doesn't always take effect right away. By reading back the - register any pending PCI writes will be performed (in order), and so - you can be sure that the writes are guaranteed to be done. - - Rarely needed, only in some timing sensitive cases. - Apparently if this is not done some motherboards seem - to kill the firmware and get into the broken state until computer is - rebooted. */ -#define write_sync(val, reg) \ - do { writel(val, reg); readl(reg); } while (0) - -#define read_reg(reg) readl(cx->reg_mem + (reg)) -#define write_reg(val, reg) writel(val, cx->reg_mem + (reg)) -#define write_reg_sync(val, reg) \ - do { write_reg(val, reg); read_reg(reg); } while (0) - -#define read_enc(addr) readl(cx->enc_mem + (u32)(addr)) -#define write_enc(val, addr) writel(val, cx->enc_mem + (u32)(addr)) -#define write_enc_sync(val, addr) \ - do { write_enc(val, addr); read_enc(addr); } while (0) - -#define sw1_irq_enable(val) do { \ - write_reg(val, SW1_INT_STATUS); \ - write_reg(read_reg(SW1_INT_ENABLE_PCI) | (val), SW1_INT_ENABLE_PCI); \ -} while (0) - -#define sw1_irq_disable(val) \ - write_reg(read_reg(SW1_INT_ENABLE_PCI) & ~(val), SW1_INT_ENABLE_PCI); - -#define sw2_irq_enable(val) do { \ - write_reg(val, SW2_INT_STATUS); \ - write_reg(read_reg(SW2_INT_ENABLE_PCI) | (val), SW2_INT_ENABLE_PCI); \ -} while (0) - -#define sw2_irq_disable(val) \ - write_reg(read_reg(SW2_INT_ENABLE_PCI) & ~(val), SW2_INT_ENABLE_PCI); - -#define setup_page(addr) do { \ - u32 val = read_reg(0xD000F8) & ~0x1f00; \ - write_reg(val | (((addr) >> 17) & 0x1f00), 0xD000F8); \ -} while (0) - #endif /* CX18_DRIVER_H */ diff --git a/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index cae38985b..afc694e7b 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/drivers/media/video/cx18/cx18-dvb.c @@ -1,7 +1,7 @@ /* * cx18 functions for DVB support * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> * * 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 @@ -21,6 +21,7 @@ #include "cx18-version.h" #include "cx18-dvb.h" +#include "cx18-io.h" #include "cx18-streams.h" #include "cx18-cards.h" #include "s5h1409.h" @@ -87,13 +88,13 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) switch (cx->card->type) { case CX18_CARD_HVR_1600_ESMT: case CX18_CARD_HVR_1600_SAMSUNG: - v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL); + v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL); v |= 0x00400000; /* Serial Mode */ v |= 0x00002000; /* Data Length - Byte */ v |= 0x00010000; /* Error - Polarity */ v |= 0x00020000; /* Error - Passthru */ v |= 0x000c0000; /* Error - Ignore */ - write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); + cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); break; default: diff --git a/linux/drivers/media/video/cx18/cx18-dvb.h b/linux/drivers/media/video/cx18/cx18-dvb.h index d6a6ccda7..bf8d8f6f5 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.h +++ b/linux/drivers/media/video/cx18/cx18-dvb.h @@ -1,7 +1,7 @@ /* * cx18 functions for DVB support * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index 12f9de74d..e884271b8 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/drivers/media/video/cx18/cx18-fileops.c @@ -132,6 +132,7 @@ static void cx18_dualwatch(struct cx18 *cx) u16 new_stereo_mode; const u16 stereo_mask = 0x0300; const u16 dual = 0x0200; + u32 h; new_stereo_mode = cx->params.audio_properties & stereo_mask; memset(&vt, 0, sizeof(vt)); @@ -143,13 +144,21 @@ static void cx18_dualwatch(struct cx18 *cx) if (new_stereo_mode == cx->dualwatch_stereo_mode) return; - new_bitmap = new_stereo_mode | (cx->params.audio_properties & ~stereo_mask); + new_bitmap = new_stereo_mode + | (cx->params.audio_properties & ~stereo_mask); - CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n", - cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); + CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. " + "new audio_bitmask=0x%ux\n", + cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); - if (cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, - cx18_find_handle(cx), new_bitmap) == 0) { + h = cx18_find_handle(cx); + if (h == CX18_INVALID_TASK_HANDLE) { + CX18_DEBUG_INFO("dualwatch: can't find valid task handle\n"); + return; + } + + if (cx18_vapi(cx, + CX18_CPU_SET_AUDIO_PARAMETERS, 2, h, new_bitmap) == 0) { cx->dualwatch_stereo_mode = new_stereo_mode; return; } @@ -159,7 +168,8 @@ static void cx18_dualwatch(struct cx18 *cx) #if 0 static void cx18_update_pgm_info(struct cx18 *cx) { - u32 wr_idx = (read_enc(cx->pgm_info_offset) - cx->pgm_info_offset - 4) / 24; + u32 wr_idx = (cx18_read_enc(cx, cx->pgm_info_offset) + - cx->pgm_info_offset - 4) / 24; int cnt; int i = 0; @@ -174,13 +184,15 @@ static void cx18_update_pgm_info(struct cx18 *cx) u32 addr = cx->pgm_info_offset + 4 + idx * 24; const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 }; - e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32); + e->offset = cx18_read_enc(cx, addr + 4) + + ((u64)cx18_read_enc(cx, addr + 8) << 32); if (e->offset > cx->mpg_data_received) break; e->offset += cx->vbi_data_inserted; - e->length = read_enc(addr); - e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32); - e->flags = mapping[read_enc(addr + 12) & 3]; + e->length = cx18_read_enc(cx, addr); + e->pts = cx18_read_enc(cx, addr + 16) + + ((u64)(cx18_read_enc(cx, addr + 20) & 1) << 32); + e->flags = mapping[cx18_read_enc(cx, addr + 12) & 3]; i++; } cx->pgm_info_write_idx = (cx->pgm_info_write_idx + i) % cx->pgm_info_num; @@ -258,7 +270,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); /* New buffers might have become available before we were added to the waitqueue */ - if (!s->q_full.buffers) + if (!atomic_read(&s->q_full.buffers)) schedule(); finish_wait(&s->waitq, &wait); if (signal_pending(current)) { @@ -544,7 +556,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) CX18_DEBUG_HI_FILE("Encoder poll\n"); poll_wait(filp, &s->waitq, wait); - if (s->q_full.length || s->q_io.length) + if (atomic_read(&s->q_full.buffers) || atomic_read(&s->q_io.buffers)) return POLLIN | POLLRDNORM; if (eof) return POLLHUP; @@ -730,20 +742,28 @@ int cx18_v4l2_open(struct inode *inode, struct file *filp) void cx18_mute(struct cx18 *cx) { - if (atomic_read(&cx->ana_capturing)) - cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, - cx18_find_handle(cx), 1); + u32 h; + if (atomic_read(&cx->ana_capturing)) { + h = cx18_find_handle(cx); + if (h != CX18_INVALID_TASK_HANDLE) + cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 1); + else + CX18_ERR("Can't find valid task handle for mute\n"); + } CX18_DEBUG_INFO("Mute\n"); } void cx18_unmute(struct cx18 *cx) { + u32 h; if (atomic_read(&cx->ana_capturing)) { - cx18_msleep_timeout(100, 0); - cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, - cx18_find_handle(cx), 12); - cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, - cx18_find_handle(cx), 0); + h = cx18_find_handle(cx); + if (h != CX18_INVALID_TASK_HANDLE) { + cx18_msleep_timeout(100, 0); + cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, h, 12); + cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 0); + } else + CX18_ERR("Can't find valid task handle for unmute\n"); } CX18_DEBUG_INFO("Unmute\n"); } diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index 78fadd2ad..51534428c 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/drivers/media/video/cx18/cx18-firmware.c @@ -20,6 +20,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-scb.h" #include "cx18-irq.h" #include "cx18-firmware.h" @@ -113,11 +114,11 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) src = (const u32 *)fw->data; for (i = 0; i < fw->size; i += 4096) { - setup_page(i); + cx18_setup_page(cx, i); for (j = i; j < fw->size && j < i + 4096; j += 4) { /* no need for endianness conversion on the ppc */ - __raw_writel(*src, dst); - if (__raw_readl(dst) != *src) { + cx18_raw_writel(cx, *src, dst); + if (cx18_raw_readl(cx, dst) != *src) { CX18_ERR("Mismatch at offset %x\n", i); release_firmware(fw); return -EIO; @@ -170,12 +171,15 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) if (offset + seghdr.size > sz) break; for (i = 0; i < seghdr.size; i += 4096) { - setup_page(offset + i); + cx18_setup_page(cx, offset + i); for (j = i; j < seghdr.size && j < i + 4096; j += 4) { /* no need for endianness conversion on the ppc */ - __raw_writel(src[(offset + j) / 4], dst + seghdr.addr + j); - if (__raw_readl(dst + seghdr.addr + j) != src[(offset + j) / 4]) { - CX18_ERR("Mismatch at offset %x\n", offset + j); + cx18_raw_writel(cx, src[(offset + j) / 4], + dst + seghdr.addr + j); + if (cx18_raw_readl(cx, dst + seghdr.addr + j) + != src[(offset + j) / 4]) { + CX18_ERR("Mismatch at offset %x\n", + offset + j); release_firmware(fw); return -EIO; } @@ -189,43 +193,45 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) size = fw->size; release_firmware(fw); /* Clear bit0 for APU to start from 0 */ - write_reg(read_reg(0xc72030) & ~1, 0xc72030); + cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030); return size; } void cx18_halt_firmware(struct cx18 *cx) { CX18_DEBUG_INFO("Preparing for firmware halt.\n"); - write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ - write_reg(0x00020002, CX18_ADEC_CONTROL); + cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ + cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL); } void cx18_init_power(struct cx18 *cx, int lowpwr) { /* power-down Spare and AOM PLLs */ /* power-up fast, slow and mpeg PLLs */ - write_reg(0x00000008, CX18_PLL_POWER_DOWN); + cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); /* ADEC out of sleep */ - write_reg(0x00020000, CX18_ADEC_CONTROL); + cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL); /* The fast clock is at 200/245 MHz */ - write_reg(lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); - write_reg(lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC); + cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); + cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7, + CX18_FAST_CLOCK_PLL_FRAC); - write_reg(2, CX18_FAST_CLOCK_PLL_POST); - write_reg(1, CX18_FAST_CLOCK_PLL_PRESCALE); - write_reg(4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); + cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST); + cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE); + cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); /* set slow clock to 125/120 MHz */ - write_reg(lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); - write_reg(lowpwr ? 0xEBAF05 : 0x18618A8, CX18_SLOW_CLOCK_PLL_FRAC); - write_reg(4, CX18_SLOW_CLOCK_PLL_POST); + cx18_write_reg(cx, lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); + cx18_write_reg(cx, lowpwr ? 0xEBAF05 : 0x18618A8, + CX18_SLOW_CLOCK_PLL_FRAC); + cx18_write_reg(cx, 4, CX18_SLOW_CLOCK_PLL_POST); /* mpeg clock pll 54MHz */ - write_reg(0xF, CX18_MPEG_CLOCK_PLL_INT); - write_reg(0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); - write_reg(8, CX18_MPEG_CLOCK_PLL_POST); + cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT); + cx18_write_reg(cx, 0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); + cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); /* Defaults */ /* APU = SC or SC/2 = 125/62.5 */ @@ -242,81 +248,84 @@ void cx18_init_power(struct cx18 *cx, int lowpwr) /* VFC = disabled */ /* USB = disabled */ - write_reg(lowpwr ? 0xFFFF0020 : 0x00060004, CX18_CLOCK_SELECT1); - write_reg(lowpwr ? 0xFFFF0004 : 0x00060006, CX18_CLOCK_SELECT2); + cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004, + CX18_CLOCK_SELECT1); + cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006, + CX18_CLOCK_SELECT2); - write_reg(0xFFFF0002, CX18_HALF_CLOCK_SELECT1); - write_reg(0xFFFF0104, CX18_HALF_CLOCK_SELECT2); + cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1); + cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2); - write_reg(0xFFFF9026, CX18_CLOCK_ENABLE1); - write_reg(0xFFFF3105, CX18_CLOCK_ENABLE2); + cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1); + cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2); } void cx18_init_memory(struct cx18 *cx) { cx18_msleep_timeout(10, 0); - write_reg(0x10000, CX18_DDR_SOFT_RESET); + cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET); cx18_msleep_timeout(10, 0); - write_reg(cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); + cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); cx18_msleep_timeout(10, 0); - write_reg(cx->card->ddr.refresh, CX18_DDR_REFRESH); - write_reg(cx->card->ddr.timing1, CX18_DDR_TIMING1); - write_reg(cx->card->ddr.timing2, CX18_DDR_TIMING2); + cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH); + cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1); + cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2); cx18_msleep_timeout(10, 0); /* Initialize DQS pad time */ - write_reg(cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); - write_reg(cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); + cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); + cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); cx18_msleep_timeout(10, 0); - write_reg(0x20000, CX18_DDR_SOFT_RESET); + cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET); cx18_msleep_timeout(10, 0); /* use power-down mode when idle */ - write_reg(0x00000010, CX18_DDR_POWER_REG); - - write_reg(0x10001, CX18_REG_BUS_TIMEOUT_EN); - - write_reg(0x48, CX18_DDR_MB_PER_ROW_7); - write_reg(0xE0000, CX18_DDR_BASE_63_ADDR); - - write_reg(0x00000101, CX18_WMB_CLIENT02); /* AO */ - write_reg(0x00000101, CX18_WMB_CLIENT09); /* AI2 */ - write_reg(0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ - write_reg(0x00000101, CX18_WMB_CLIENT06); /* AI1 */ - write_reg(0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ - write_reg(0x00000101, CX18_WMB_CLIENT10); /* ME */ - write_reg(0x00000101, CX18_WMB_CLIENT12); /* ENC */ - write_reg(0x00000101, CX18_WMB_CLIENT13); /* PK */ - write_reg(0x00000101, CX18_WMB_CLIENT11); /* RC */ - write_reg(0x00000101, CX18_WMB_CLIENT14); /* AVO */ + cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); + + cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN); + + cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7); + cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR); + + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */ } int cx18_firmware_init(struct cx18 *cx) { /* Allow chip to control CLKRUN */ - write_reg(0x5, CX18_DSP0_INTERRUPT_MASK); + cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); - write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ + cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ cx18_msleep_timeout(1, 0); - sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); - sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); + cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); /* Only if the processor is not running */ - if (read_reg(CX18_PROC_SOFT_RESET) & 8) { + if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) { int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx); - write_enc(0xE51FF004, 0); - write_enc(0xa00000, 4); /* todo: not hardcoded */ - write_reg(0x00010000, CX18_PROC_SOFT_RESET); /* Start APU */ + cx18_write_enc(cx, 0xE51FF004, 0); + cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */ + /* Start APU */ + cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET); cx18_msleep_timeout(500, 0); sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", @@ -326,9 +335,10 @@ int cx18_firmware_init(struct cx18 *cx) int retries = 0; /* start the CPU */ - write_reg(0x00080000, CX18_PROC_SOFT_RESET); + cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET); while (retries++ < 50) { /* Loop for max 500mS */ - if ((read_reg(CX18_PROC_SOFT_RESET) & 1) == 0) + if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) + & 1) == 0) break; cx18_msleep_timeout(10, 0); } @@ -342,6 +352,6 @@ int cx18_firmware_init(struct cx18 *cx) return -EIO; } /* initialize GPIO */ - write_reg(0x14001400, 0xC78110); + cx18_write_reg(cx, 0x14001400, 0xC78110); return 0; } diff --git a/linux/drivers/media/video/cx18/cx18-gpio.c b/linux/drivers/media/video/cx18/cx18-gpio.c index 3d495dba4..3bdffbf7a 100644 --- a/linux/drivers/media/video/cx18/cx18-gpio.c +++ b/linux/drivers/media/video/cx18/cx18-gpio.c @@ -22,6 +22,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-cards.h" #include "cx18-gpio.h" #include "tuner-xc2028.h" @@ -49,11 +50,11 @@ static void gpio_write(struct cx18 *cx) u32 dir = cx->gpio_dir; u32 val = cx->gpio_val; - write_reg((dir & 0xffff) << 16, CX18_REG_GPIO_DIR1); - write_reg(((dir & 0xffff) << 16) | (val & 0xffff), + cx18_write_reg(cx, (dir & 0xffff) << 16, CX18_REG_GPIO_DIR1); + cx18_write_reg(cx, ((dir & 0xffff) << 16) | (val & 0xffff), CX18_REG_GPIO_OUT1); - write_reg(dir & 0xffff0000, CX18_REG_GPIO_DIR2); - write_reg_sync((dir & 0xffff0000) | ((val & 0xffff0000) >> 16), + cx18_write_reg(cx, dir & 0xffff0000, CX18_REG_GPIO_DIR2); + cx18_write_reg_sync(cx, (dir & 0xffff0000) | ((val & 0xffff0000) >> 16), CX18_REG_GPIO_OUT2); } @@ -141,8 +142,10 @@ void cx18_gpio_init(struct cx18 *cx) } CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n", - read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_DIR2), - read_reg(CX18_REG_GPIO_OUT1), read_reg(CX18_REG_GPIO_OUT2)); + cx18_read_reg(cx, CX18_REG_GPIO_DIR1), + cx18_read_reg(cx, CX18_REG_GPIO_DIR2), + cx18_read_reg(cx, CX18_REG_GPIO_OUT1), + cx18_read_reg(cx, CX18_REG_GPIO_OUT2)); gpio_write(cx); mutex_unlock(&cx->gpio_lock); diff --git a/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index 64a259405..f28a1ff2e 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/drivers/media/video/cx18/cx18-i2c.c @@ -22,13 +22,12 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-cards.h" #include "cx18-gpio.h" #include "cx18-av-core.h" #include "cx18-i2c.h" -#include <media/ir-kbd-i2c.h> - #define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_RD 0xf15008 #define CX18_REG_I2C_2_WR 0xf25100 @@ -181,12 +180,12 @@ static void cx18_setscl(void *data, int state) struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; - u32 r = read_reg(addr); + u32 r = cx18_read_reg(cx, addr); if (state) - write_reg_sync(r | SETSCL_BIT, addr); + cx18_write_reg_sync(cx, r | SETSCL_BIT, addr); else - write_reg_sync(r & ~SETSCL_BIT, addr); + cx18_write_reg_sync(cx, r & ~SETSCL_BIT, addr); } static void cx18_setsda(void *data, int state) @@ -194,12 +193,12 @@ static void cx18_setsda(void *data, int state) struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; - u32 r = read_reg(addr); + u32 r = cx18_read_reg(cx, addr); if (state) - write_reg_sync(r | SETSDL_BIT, addr); + cx18_write_reg_sync(cx, r | SETSDL_BIT, addr); else - write_reg_sync(r & ~SETSDL_BIT, addr); + cx18_write_reg_sync(cx, r & ~SETSDL_BIT, addr); } static int cx18_getscl(void *data) @@ -208,7 +207,7 @@ static int cx18_getscl(void *data) int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; - return read_reg(addr) & GETSCL_BIT; + return cx18_read_reg(cx, addr) & GETSCL_BIT; } static int cx18_getsda(void *data) @@ -217,7 +216,7 @@ static int cx18_getsda(void *data) int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; - return read_reg(addr) & GETSDL_BIT; + return cx18_read_reg(cx, addr) & GETSDL_BIT; } /* template for i2c-bit-algo */ @@ -422,29 +421,33 @@ int init_cx18_i2c(struct cx18 *cx) #endif } - if (read_reg(CX18_REG_I2C_2_WR) != 0x0003c02f) { + if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) { /* Reset/Unreset I2C hardware block */ - write_reg(0x10000000, 0xc71004); /* Clock select 220MHz */ - write_reg_sync(0x10001000, 0xc71024); /* Clock Enable */ + /* Clock select 220MHz */ + cx18_write_reg(cx, 0x10000000, 0xc71004); + /* Clock Enable */ + cx18_write_reg_sync(cx, 0x10001000, 0xc71024); } /* courtesy of Steven Toth <stoth@hauppauge.com> */ - write_reg_sync(0x00c00000, 0xc7001c); + cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); mdelay(10); - write_reg_sync(0x00c000c0, 0xc7001c); + cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c); mdelay(10); - write_reg_sync(0x00c00000, 0xc7001c); + cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); mdelay(10); - write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */ - write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */ + /* Set to edge-triggered intrs. */ + cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8); + /* Clear any stale intrs */ + cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4); /* Hw I2C1 Clock Freq ~100kHz */ - write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_1_WR); + cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR); cx18_setscl(&cx->i2c_algo_cb_data[0], 1); cx18_setsda(&cx->i2c_algo_cb_data[0], 1); /* Hw I2C2 Clock Freq ~100kHz */ - write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_2_WR); + cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR); cx18_setscl(&cx->i2c_algo_cb_data[1], 1); cx18_setsda(&cx->i2c_algo_cb_data[1], 1); @@ -458,8 +461,10 @@ void exit_cx18_i2c(struct cx18 *cx) { int i; CX18_DEBUG_I2C("i2c exit\n"); - write_reg(read_reg(CX18_REG_I2C_1_WR) | 4, CX18_REG_I2C_1_WR); - write_reg(read_reg(CX18_REG_I2C_2_WR) | 4, CX18_REG_I2C_2_WR); + cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_1_WR) | 4, + CX18_REG_I2C_1_WR); + cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_2_WR) | 4, + CX18_REG_I2C_2_WR); for (i = 0; i < 2; i++) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) diff --git a/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c new file mode 100644 index 000000000..55d1df932 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-io.c @@ -0,0 +1,139 @@ +/* + * cx18 driver PCI memory mapped IO access routines + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-io.h" +#include "cx18-irq.h" + +void cx18_memcpy_fromio(struct cx18 *cx, void *to, + const void __iomem *from, unsigned int len) +{ + const u8 __iomem *src = from; + u8 *dst = to; + + /* Align reads on the CX23418's addresses */ + if ((len > 0) && ((unsigned long) src & 1)) { + *dst = cx18_readb(cx, src); + len--; + dst++; + src++; + } + if ((len > 1) && ((unsigned long) src & 2)) { + *((u16 *)dst) = cx18_raw_readw(cx, src); + len -= 2; + dst += 2; + src += 2; + } + while (len > 3) { + *((u32 *)dst) = cx18_raw_readl(cx, src); + len -= 4; + dst += 4; + src += 4; + } + if (len > 1) { + *((u16 *)dst) = cx18_raw_readw(cx, src); + len -= 2; + dst += 2; + src += 2; + } + if (len > 0) + *dst = cx18_readb(cx, src); +} + +void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) +{ + u8 __iomem *dst = addr; + u16 val2 = val | (val << 8); + u32 val4 = val2 | (val2 << 16); + + /* Align writes on the CX23418's addresses */ + if ((count > 0) && ((unsigned long)dst & 1)) { + cx18_writeb(cx, (u8) val, dst); + count--; + dst++; + } + if ((count > 1) && ((unsigned long)dst & 2)) { + cx18_writew(cx, val2, dst); + count -= 2; + dst += 2; + } + while (count > 3) { + cx18_writel(cx, val4, dst); + count -= 4; + dst += 4; + } + if (count > 1) { + cx18_writew(cx, val2, dst); + count -= 2; + dst += 2; + } + if (count > 0) + cx18_writeb(cx, (u8) val, dst); +} + +void cx18_sw1_irq_enable(struct cx18 *cx, u32 val) +{ + u32 r; + cx18_write_reg(cx, val, SW1_INT_STATUS); + r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); + cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI); +} + +void cx18_sw1_irq_disable(struct cx18 *cx, u32 val) +{ + u32 r; + r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); + cx18_write_reg(cx, r & ~val, SW1_INT_ENABLE_PCI); +} + +void cx18_sw2_irq_enable(struct cx18 *cx, u32 val) +{ + u32 r; + cx18_write_reg(cx, val, SW2_INT_STATUS); + r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); + cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI); +} + +void cx18_sw2_irq_disable(struct cx18 *cx, u32 val) +{ + u32 r; + r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); + cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_PCI); +} + +void cx18_setup_page(struct cx18 *cx, u32 addr) +{ + u32 val; + val = cx18_read_reg(cx, 0xD000F8); + val = (val & ~0x1f00) | ((addr >> 17) & 0x1f00); + cx18_write_reg(cx, val, 0xD000F8); +} + +/* Tries to recover from the CX23418 responding improperly on the PCI bus */ +int cx18_pci_try_recover(struct cx18 *cx) +{ + u16 status; + + pci_read_config_word(cx->dev, PCI_STATUS, &status); + pci_write_config_word(cx->dev, PCI_STATUS, status); + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h new file mode 100644 index 000000000..7ab7be253 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-io.h @@ -0,0 +1,140 @@ +/* + * cx18 driver PCI memory mapped IO access routines + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#ifndef CX18_IO_H +#define CX18_IO_H + +#include "cx18-driver.h" + +static inline void cx18_io_delay(struct cx18 *cx) +{ + if (cx->options.mmio_ndelay) + ndelay(cx->options.mmio_ndelay); +} + +/* Non byteswapping memory mapped IO */ +static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr) +{ + __raw_writel(val, addr); + cx18_io_delay(cx); +} + +static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr) +{ + u32 ret = __raw_readl(addr); + cx18_io_delay(cx); + return ret; +} + +static inline u16 cx18_raw_readw(struct cx18 *cx, const void __iomem *addr) +{ + u16 ret = __raw_readw(addr); + cx18_io_delay(cx); + return ret; +} + +/* Normal memory mapped IO */ +static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr) +{ + writel(val, addr); + cx18_io_delay(cx); +} + +static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr) +{ + writew(val, addr); + cx18_io_delay(cx); +} + +static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr) +{ + writeb(val, addr); + cx18_io_delay(cx); +} + +static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr) +{ + u32 ret = readl(addr); + cx18_io_delay(cx); + return ret; +} + +static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr) +{ + u8 ret = readb(addr); + cx18_io_delay(cx); + return ret; +} + +static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr) +{ + cx18_writel(cx, val, addr); + return cx18_readl(cx, addr); +} + +void cx18_memcpy_fromio(struct cx18 *cx, void *to, + const void __iomem *from, unsigned int len); +void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count); + +/* Access "register" region of CX23418 memory mapped I/O */ +static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg) +{ + cx18_writel(cx, val, cx->reg_mem + reg); +} + +static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg) +{ + return cx18_readl(cx, cx->reg_mem + reg); +} + +static inline u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg) +{ + return cx18_write_sync(cx, val, cx->reg_mem + reg); +} + +/* Access "encoder memory" region of CX23418 memory mapped I/O */ +static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr) +{ + cx18_writel(cx, val, cx->enc_mem + addr); +} + +static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr) +{ + return cx18_readl(cx, cx->enc_mem + addr); +} + +static inline u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr) +{ + return cx18_write_sync(cx, val, cx->enc_mem + addr); +} + + +void cx18_sw1_irq_enable(struct cx18 *cx, u32 val); +void cx18_sw1_irq_disable(struct cx18 *cx, u32 val); +void cx18_sw2_irq_enable(struct cx18 *cx, u32 val); +void cx18_sw2_irq_disable(struct cx18 *cx, u32 val); +void cx18_setup_page(struct cx18 *cx, u32 addr); + +/* Tries to recover from the CX23418 responding improperly on the PCI bus */ +int cx18_pci_try_recover(struct cx18 *cx); + +#endif /* CX18_IO_H */ diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c index e6c2bfa5d..b909c78d3 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.c +++ b/linux/drivers/media/video/cx18/cx18-ioctl.c @@ -22,6 +22,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-version.h" #include "cx18-mailbox.h" #include "cx18-i2c.h" @@ -200,7 +201,6 @@ static int cx18_try_fmt_vid_cap(struct file *file, void *fh, { struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; - int w = fmt->fmt.pix.width; int h = fmt->fmt.pix.height; @@ -248,8 +248,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; int ret; - int w = fmt->fmt.pix.width; - int h = fmt->fmt.pix.height; + int w, h; ret = v4l2_prio_check(&cx->prio, &id->prio); if (ret) @@ -258,6 +257,8 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, ret = cx18_try_fmt_vid_cap(file, fh, fmt); if (ret) return ret; + w = fmt->fmt.pix.width; + h = fmt->fmt.pix.height; if (cx->params.width == w && cx->params.height == h) return 0; @@ -358,9 +359,9 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) spin_lock_irqsave(&cx18_cards_lock, flags); if (cmd == VIDIOC_DBG_G_REGISTER) - regs->val = read_enc(regs->reg); + regs->val = cx18_read_enc(cx, regs->reg); else - write_enc(regs->val, regs->reg); + cx18_write_enc(cx, regs->val, regs->reg); spin_unlock_irqrestore(&cx18_cards_lock, flags); return 0; } @@ -727,6 +728,7 @@ static int cx18_encoder_cmd(struct file *file, void *fh, { struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; + u32 h; switch (enc->cmd) { case V4L2_ENC_CMD_START: @@ -748,8 +750,14 @@ static int cx18_encoder_cmd(struct file *file, void *fh, return -EPERM; if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) return 0; + h = cx18_find_handle(cx); + if (h == CX18_INVALID_TASK_HANDLE) { + CX18_ERR("Can't find valid task handle for " + "V4L2_ENC_CMD_PAUSE\n"); + return -EBADFD; + } cx18_mute(cx); - cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx)); + cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h); break; case V4L2_ENC_CMD_RESUME: @@ -759,7 +767,13 @@ static int cx18_encoder_cmd(struct file *file, void *fh, return -EPERM; if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) return 0; - cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx)); + h = cx18_find_handle(cx); + if (h == CX18_INVALID_TASK_HANDLE) { + CX18_ERR("Can't find valid task handle for " + "V4L2_ENC_CMD_RESUME\n"); + return -EBADFD; + } + cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h); cx18_unmute(cx); break; @@ -836,7 +850,8 @@ static int cx18_log_status(struct file *file, void *fh) continue; CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, - (s->buffers - s->q_free.buffers) * 100 / s->buffers, + (s->buffers - atomic_read(&s->q_free.buffers)) + * 100 / s->buffers, (s->buffers * s->buf_size) / 1024, s->buffers); } CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", diff --git a/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index 654664783..f117af782 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -20,6 +20,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-firmware.h" #include "cx18-fileops.h" #include "cx18-queue.h" @@ -60,8 +61,8 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) if (mb->args[2] != 1) CX18_WARN("Ack struct = %d for %s\n", mb->args[2], s->name); - id = read_enc(off); - buf = cx18_queue_find_buf(s, id, read_enc(off + 4)); + id = cx18_read_enc(cx, off); + buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4)); CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); if (buf) { cx18_buf_sync_for_cpu(s, buf); @@ -81,7 +82,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); } else { CX18_WARN("Could not find buf %d for stream %s\n", - read_enc(off), s->name); + cx18_read_enc(cx, off), s->name); } mb->error = 0; mb->cmd = 0; @@ -97,8 +98,8 @@ static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) char *p; if (mb->args[1]) { - setup_page(mb->args[1]); - memcpy_fromio(str, cx->enc_mem + mb->args[1], 252); + cx18_setup_page(cx, mb->args[1]); + cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252); str[252] = 0; } cx18_mb_ack(cx, mb); @@ -113,7 +114,7 @@ static void hpu_cmd(struct cx18 *cx, u32 sw1) struct cx18_mailbox mb; if (sw1 & IRQ_CPU_TO_EPU) { - memcpy_fromio(&mb, &cx->scb->cpu2epu_mb, sizeof(mb)); + cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb)); mb.error = 0; switch (mb.cmd) { @@ -145,16 +146,16 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) spin_lock(&cx->dma_reg_lock); - hw2_mask = read_reg(HW2_INT_MASK5_PCI); - hw2 = read_reg(HW2_INT_CLR_STATUS) & hw2_mask; - sw2_mask = read_reg(SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; - sw2 = read_reg(SW2_INT_STATUS) & sw2_mask; - sw1_mask = read_reg(SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; - sw1 = read_reg(SW1_INT_STATUS) & sw1_mask; + hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI); + hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask; + sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; + sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask; + sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; + sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask; - write_reg(sw2&sw2_mask, SW2_INT_STATUS); - write_reg(sw1&sw1_mask, SW1_INT_STATUS); - write_reg(hw2&hw2_mask, HW2_INT_CLR_STATUS); + cx18_write_reg(cx, sw2&sw2_mask, SW2_INT_STATUS); + cx18_write_reg(cx, sw1&sw1_mask, SW1_INT_STATUS); + cx18_write_reg(cx, hw2&hw2_mask, HW2_INT_CLR_STATUS); if (sw1 || sw2 || hw2) CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); @@ -165,15 +166,15 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) */ if (sw2) { - if (sw2 & (readl(&cx->scb->cpu2hpu_irq_ack) | - readl(&cx->scb->cpu2epu_irq_ack))) + if (sw2 & (cx18_readl(cx, &cx->scb->cpu2hpu_irq_ack) | + cx18_readl(cx, &cx->scb->cpu2epu_irq_ack))) wake_up(&cx->mb_cpu_waitq); - if (sw2 & (readl(&cx->scb->apu2hpu_irq_ack) | - readl(&cx->scb->apu2epu_irq_ack))) + if (sw2 & (cx18_readl(cx, &cx->scb->apu2hpu_irq_ack) | + cx18_readl(cx, &cx->scb->apu2epu_irq_ack))) wake_up(&cx->mb_apu_waitq); - if (sw2 & readl(&cx->scb->epu2hpu_irq_ack)) + if (sw2 & cx18_readl(cx, &cx->scb->epu2hpu_irq_ack)) wake_up(&cx->mb_epu_waitq); - if (sw2 & readl(&cx->scb->hpu2epu_irq_ack)) + if (sw2 & cx18_readl(cx, &cx->scb->hpu2epu_irq_ack)) wake_up(&cx->mb_hpu_waitq); } diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 93177514e..9d18dd22d 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -22,6 +22,7 @@ #include <stdarg.h> #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-scb.h" #include "cx18-irq.h" #include "cx18-mailbox.h" @@ -82,6 +83,7 @@ static const struct cx18_api_info api_info[] = { API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST), + API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, 0), API_ENTRY(0, 0, 0), }; @@ -105,20 +107,20 @@ static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu switch (rpu) { case APU: mb = &cx->scb->epu2apu_mb; - *state = readl(&cx->scb->apu_state); - *irq = readl(&cx->scb->epu2apu_irq); + *state = cx18_readl(cx, &cx->scb->apu_state); + *irq = cx18_readl(cx, &cx->scb->epu2apu_irq); break; case CPU: mb = &cx->scb->epu2cpu_mb; - *state = readl(&cx->scb->cpu_state); - *irq = readl(&cx->scb->epu2cpu_irq); + *state = cx18_readl(cx, &cx->scb->cpu_state); + *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); break; case HPU: mb = &cx->scb->epu2hpu_mb; - *state = readl(&cx->scb->hpu_state); - *irq = readl(&cx->scb->epu2hpu_irq); + *state = cx18_readl(cx, &cx->scb->hpu_state); + *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq); break; } @@ -126,8 +128,8 @@ static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu return mb; do { - *req = readl(&mb->request); - ack = readl(&mb->ack); + *req = cx18_readl(cx, &mb->request); + ack = cx18_readl(cx, &mb->ack); wait_count++; } while (*req != ack && wait_count < 600); @@ -172,9 +174,9 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) return -EINVAL; } - setup_page(SCB_OFFSET); - write_sync(mb->request, &ack_mb->ack); - write_reg(ack_irq, SW2_INT_SET); + cx18_setup_page(cx, SCB_OFFSET); + cx18_write_sync(cx, mb->request, &ack_mb->ack); + cx18_write_reg(cx, ack_irq, SW2_INT_SET); return 0; } @@ -199,7 +201,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) CX18_DEBUG_HI_API("%s\n", info->name); else CX18_DEBUG_API("%s\n", info->name); - setup_page(SCB_OFFSET); + cx18_setup_page(cx, SCB_OFFSET); mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); if (mb == NULL) { @@ -208,11 +210,11 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) } oldreq = req - 1; - writel(cmd, &mb->cmd); + cx18_writel(cx, cmd, &mb->cmd); for (i = 0; i < args; i++) - writel(data[i], &mb->args[i]); - writel(0, &mb->error); - writel(req, &mb->request); + cx18_writel(cx, data[i], &mb->args[i]); + cx18_writel(cx, 0, &mb->error); + cx18_writel(cx, req, &mb->request); switch (info->rpu) { case APU: waitq = &cx->mb_apu_waitq; break; @@ -223,9 +225,10 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) } if (info->flags & API_FAST) timeout /= 2; - write_reg(irq, SW1_INT_SET); + cx18_write_reg(cx, irq, SW1_INT_SET); - while (!sig && readl(&mb->ack) != readl(&mb->request) && cnt < 660) { + while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request) + && cnt < 660) { if (cnt > 200 && !in_atomic()) sig = cx18_msleep_timeout(10, 1); cnt++; @@ -233,13 +236,13 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) if (sig) return -EINTR; if (cnt == 660) { - writel(oldreq, &mb->request); + cx18_writel(cx, oldreq, &mb->request); CX18_ERR("mb %s failed\n", info->name); return -EINVAL; } for (i = 0; i < MAX_MB_ARGUMENTS; i++) - data[i] = readl(&mb->args[i]); - err = readl(&mb->error); + data[i] = cx18_readl(cx, &mb->args[i]); + err = cx18_readl(cx, &mb->error); if (!in_atomic() && (info->flags & API_SLOW)) cx18_msleep_timeout(300, 0); if (err) diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index 6990b77c6..a33ba04a2 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/drivers/media/video/cx18/cx18-queue.c @@ -37,8 +37,7 @@ void cx18_buf_swap(struct cx18_buffer *buf) void cx18_queue_init(struct cx18_queue *q) { INIT_LIST_HEAD(&q->list); - q->buffers = 0; - q->length = 0; + atomic_set(&q->buffers, 0); q->bytesused = 0; } @@ -55,8 +54,7 @@ void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, } spin_lock_irqsave(&s->qlock, flags); list_add_tail(&buf->list, &q->list); - q->buffers++; - q->length += s->buf_size; + atomic_inc(&q->buffers); q->bytesused += buf->bytesused - buf->readpos; spin_unlock_irqrestore(&s->qlock, flags); } @@ -70,20 +68,20 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) if (!list_empty(&q->list)) { buf = list_entry(q->list.next, struct cx18_buffer, list); list_del_init(q->list.next); - q->buffers--; - q->length -= s->buf_size; + atomic_dec(&q->buffers); q->bytesused -= buf->bytesused - buf->readpos; } spin_unlock_irqrestore(&s->qlock, flags); return buf; } -struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id, +struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, u32 bytesused) { struct cx18 *cx = s->cx; struct list_head *p; + spin_lock(&s->qlock); list_for_each(p, &s->q_free.list) { struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); @@ -92,114 +90,45 @@ struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id, continue; buf->bytesused = bytesused; /* the transport buffers are handled differently, - so there is no need to move them to the full queue */ - if (s->type == CX18_ENC_STREAM_TYPE_TS) - return buf; - s->q_free.buffers--; - s->q_free.length -= s->buf_size; - s->q_full.buffers++; - s->q_full.length += s->buf_size; - s->q_full.bytesused += buf->bytesused; - list_move_tail(&buf->list, &s->q_full.list); + they are not moved to the full queue */ + if (s->type != CX18_ENC_STREAM_TYPE_TS) { + atomic_dec(&s->q_free.buffers); + atomic_inc(&s->q_full.buffers); + s->q_full.bytesused += buf->bytesused; + list_move_tail(&buf->list, &s->q_full.list); + } + spin_unlock(&s->qlock); return buf; } + spin_unlock(&s->qlock); CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); return NULL; } -static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from, - struct cx18_queue *to, int clear, int full) -{ - struct cx18_buffer *buf = - list_entry(from->list.next, struct cx18_buffer, list); - - list_move_tail(from->list.next, &to->list); - from->buffers--; - from->length -= s->buf_size; - from->bytesused -= buf->bytesused - buf->readpos; - /* special handling for q_free */ - if (clear) - buf->bytesused = buf->readpos = buf->b_flags = 0; - else if (full) { - /* special handling for stolen buffers, assume - all bytes are used. */ - buf->bytesused = s->buf_size; - buf->readpos = buf->b_flags = 0; - } - to->buffers++; - to->length += s->buf_size; - to->bytesused += buf->bytesused - buf->readpos; -} - -/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'. - If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'. - If 'steal' != NULL, then buffers may also taken from that queue if - needed. - - The buffer is automatically cleared if it goes to the free queue. It is - also cleared if buffers need to be taken from the 'steal' queue and - the 'from' queue is the free queue. - - When 'from' is q_free, then needed_bytes is compared to the total - available buffer length, otherwise needed_bytes is compared to the - bytesused value. For the 'steal' queue the total available buffer - length is always used. - - -ENOMEM is returned if the buffers could not be obtained, 0 if all - buffers where obtained from the 'from' list and if non-zero then - the number of stolen buffers is returned. */ -static int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from, - struct cx18_queue *steal, struct cx18_queue *to, - int needed_bytes) +/* Move all buffers of a queue to q_free, while flushing the buffers */ +static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) { unsigned long flags; - int rc = 0; - int from_free = from == &s->q_free; - int to_free = to == &s->q_free; - int bytes_available; - - spin_lock_irqsave(&s->qlock, flags); - if (needed_bytes == 0) { - from_free = 1; - needed_bytes = from->length; - } - - bytes_available = from_free ? from->length : from->bytesused; - bytes_available += steal ? steal->length : 0; + struct cx18_buffer *buf; - if (bytes_available < needed_bytes) { - spin_unlock_irqrestore(&s->qlock, flags); - return -ENOMEM; - } - if (from_free) { - u32 old_length = to->length; + if (q == &s->q_free) + return; - while (to->length - old_length < needed_bytes) { - if (list_empty(&from->list)) - from = steal; - if (from == steal) - rc++; /* keep track of 'stolen' buffers */ - cx18_queue_move_buf(s, from, to, 1, 0); - } - } else { - u32 old_bytesused = to->bytesused; - - while (to->bytesused - old_bytesused < needed_bytes) { - if (list_empty(&from->list)) - from = steal; - if (from == steal) - rc++; /* keep track of 'stolen' buffers */ - cx18_queue_move_buf(s, from, to, to_free, rc); - } + spin_lock_irqsave(&s->qlock, flags); + while (!list_empty(&q->list)) { + buf = list_entry(q->list.next, struct cx18_buffer, list); + list_move_tail(q->list.next, &s->q_free.list); + buf->bytesused = buf->readpos = buf->b_flags = 0; + atomic_inc(&s->q_free.buffers); } + cx18_queue_init(q); spin_unlock_irqrestore(&s->qlock, flags); - return rc; } void cx18_flush_queues(struct cx18_stream *s) { - cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0); - cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0); + cx18_queue_flush(s, &s->q_io); + cx18_queue_flush(s, &s->q_full); } int cx18_stream_alloc(struct cx18_stream *s) @@ -214,10 +143,10 @@ int cx18_stream_alloc(struct cx18_stream *s) s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024); - if (((char *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - - (char *)cx->scb) > SCB_RESERVED_SIZE) { - unsigned bufsz = (((char *)cx->scb) + SCB_RESERVED_SIZE - - ((char *)cx->scb->cpu_mdl)); + if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - + (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) { + unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE - + ((char __iomem *)cx->scb->cpu_mdl)); CX18_ERR("Too many buffers, cannot fit in SCB area\n"); CX18_ERR("Max buffers = %zd\n", diff --git a/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h index 91423b986..7f93bb13c 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.h +++ b/linux/drivers/media/video/cx18/cx18-queue.h @@ -46,7 +46,7 @@ void cx18_queue_init(struct cx18_queue *q); void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, struct cx18_queue *q); struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); -struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id, +struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, u32 bytesused); void cx18_flush_queues(struct cx18_stream *s); diff --git a/linux/drivers/media/video/cx18/cx18-scb.c b/linux/drivers/media/video/cx18/cx18-scb.c index 30bc803e3..f56d3772a 100644 --- a/linux/drivers/media/video/cx18/cx18-scb.c +++ b/linux/drivers/media/video/cx18/cx18-scb.c @@ -20,102 +20,103 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-scb.h" void cx18_init_scb(struct cx18 *cx) { - setup_page(SCB_OFFSET); - memset_io(cx->scb, 0, 0x10000); + cx18_setup_page(cx, SCB_OFFSET); + cx18_memset_io(cx, cx->scb, 0, 0x10000); - writel(IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq); - writel(IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack); - writel(IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq); - writel(IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack); - writel(IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq); - writel(IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack); - writel(IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq); - writel(IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack); + cx18_writel(cx, IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq); + cx18_writel(cx, IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack); + cx18_writel(cx, IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq); + cx18_writel(cx, IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack); + cx18_writel(cx, IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq); + cx18_writel(cx, IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack); + cx18_writel(cx, IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq); + cx18_writel(cx, IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack); - writel(IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq); - writel(IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack); - writel(IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq); - writel(IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack); - writel(IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq); - writel(IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack); - writel(IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq); - writel(IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack); + cx18_writel(cx, IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq); + cx18_writel(cx, IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack); + cx18_writel(cx, IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq); + cx18_writel(cx, IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack); + cx18_writel(cx, IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq); + cx18_writel(cx, IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack); + cx18_writel(cx, IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq); + cx18_writel(cx, IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack); - writel(IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq); - writel(IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack); - writel(IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq); - writel(IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack); - writel(IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq); - writel(IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack); - writel(IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq); - writel(IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack); + cx18_writel(cx, IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq); + cx18_writel(cx, IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack); + cx18_writel(cx, IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq); + cx18_writel(cx, IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack); + cx18_writel(cx, IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq); + cx18_writel(cx, IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack); + cx18_writel(cx, IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq); + cx18_writel(cx, IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack); - writel(IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq); - writel(IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack); - writel(IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq); - writel(IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack); - writel(IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq); - writel(IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack); - writel(IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq); - writel(IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack); + cx18_writel(cx, IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq); + cx18_writel(cx, IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack); + cx18_writel(cx, IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq); + cx18_writel(cx, IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack); + cx18_writel(cx, IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq); + cx18_writel(cx, IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack); + cx18_writel(cx, IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq); + cx18_writel(cx, IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack); - writel(IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq); - writel(IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack); - writel(IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq); - writel(IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack); - writel(IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq); - writel(IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack); - writel(IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq); - writel(IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack); + cx18_writel(cx, IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq); + cx18_writel(cx, IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack); + cx18_writel(cx, IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq); + cx18_writel(cx, IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack); + cx18_writel(cx, IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq); + cx18_writel(cx, IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack); + cx18_writel(cx, IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq); + cx18_writel(cx, IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack); - writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb), &cx->scb->apu2cpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb), &cx->scb->hpu2cpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb), &cx->scb->ppu2cpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb), &cx->scb->epu2cpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb), &cx->scb->cpu2apu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb), &cx->scb->hpu2apu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb), &cx->scb->ppu2apu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb), &cx->scb->epu2apu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb), &cx->scb->cpu2hpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb), &cx->scb->apu2hpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb), &cx->scb->ppu2hpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb), &cx->scb->epu2hpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb), &cx->scb->cpu2ppu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb), &cx->scb->apu2ppu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb), &cx->scb->hpu2ppu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb), &cx->scb->epu2ppu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb), &cx->scb->cpu2epu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb), &cx->scb->apu2epu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb), &cx->scb->hpu2epu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb), &cx->scb->ppu2epu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), &cx->scb->ipc_offset); - writel(1, &cx->scb->hpu_state); - writel(1, &cx->scb->epu_state); + cx18_writel(cx, 1, &cx->scb->hpu_state); + cx18_writel(cx, 1, &cx->scb->epu_state); } diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 911a97ce3..fc793e5f2 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -22,6 +22,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-fileops.h" #include "cx18-mailbox.h" #include "cx18-i2c.h" @@ -119,7 +120,7 @@ static void cx18_stream_init(struct cx18 *cx, int type) s->cx = cx; s->type = type; s->name = cx18_stream_info[type].name; - s->handle = 0xffffffff; + s->handle = CX18_INVALID_TASK_HANDLE; s->dma = cx18_stream_info[type].dma; s->buf_size = cx->stream_buf_size[type]; @@ -432,7 +433,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) default: return -EINVAL; } - s->buffers_stolen = 0; /* mute/unmute video */ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, @@ -470,7 +470,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) if (atomic_read(&cx->tot_capturing) == 0) { clear_bit(CX18_F_I_EOS, &cx->i_flags); - write_reg(7, CX18_DSP0_INTERRUPT_MASK); + cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK); } cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, @@ -480,8 +480,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) list_for_each(p, &s->q_free.list) { struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); - writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr); - writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length); + cx18_writel(cx, buf->dma_handle, + &cx->scb->cpu_mdl[buf->id].paddr); + cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 1, buf->id, s->buf_size); @@ -489,7 +490,14 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) /* begin_capture */ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { CX18_DEBUG_WARN("Error starting capture!\n"); + /* Ensure we're really not capturing before releasing MDLs */ + if (s->type == CX18_ENC_STREAM_TYPE_MPG) + cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); + else + cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); + cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); + /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */ return -EINVAL; } @@ -568,6 +576,9 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) #endif } + /* Tell the CX23418 it can't use our buffers anymore */ + cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); + if (s->type != CX18_ENC_STREAM_TYPE_TS) atomic_dec(&cx->ana_capturing); atomic_dec(&cx->tot_capturing); @@ -576,12 +587,12 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) clear_bit(CX18_F_S_STREAMING, &s->s_flags); cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); - s->handle = 0xffffffff; + s->handle = CX18_INVALID_TASK_HANDLE; if (atomic_read(&cx->tot_capturing) > 0) return 0; - write_reg(5, CX18_DSP0_INTERRUPT_MASK); + cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); wake_up(&s->waitq); return 0; @@ -595,8 +606,8 @@ u32 cx18_find_handle(struct cx18 *cx) for (i = 0; i < CX18_MAX_STREAMS; i++) { struct cx18_stream *s = &cx->streams[i]; - if (s->v4l2dev && s->handle) + if (s->v4l2dev && (s->handle != CX18_INVALID_TASK_HANDLE)) return s->handle; } - return 0; + return CX18_INVALID_TASK_HANDLE; } diff --git a/linux/drivers/media/video/cx18/cx23418.h b/linux/drivers/media/video/cx18/cx23418.h index e7ed05305..668f968d7 100644 --- a/linux/drivers/media/video/cx18/cx23418.h +++ b/linux/drivers/media/video/cx18/cx23418.h @@ -351,7 +351,7 @@ Descriptor Lists to the driver IN[0] - Task handle. Handle of the task to start ReturnCode - One of the ERR_DE_... */ -/* #define CX18_CPU_DE_ReleaseMDL (CPU_CMD_MASK_DE | 0x0006) */ +#define CX18_CPU_DE_RELEASE_MDL (CPU_CMD_MASK_DE | 0x0006) /* Description: This command signals the cpu that the dat buffer has been consumed and ready for re-use. diff --git a/linux/drivers/media/video/cx2341x.c b/linux/drivers/media/video/cx2341x.c index 835062f5b..f31f11e3f 100644 --- a/linux/drivers/media/video/cx2341x.c +++ b/linux/drivers/media/video/cx2341x.c @@ -509,7 +509,10 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, /* this setting is read-only for the cx2341x since the V4L2_CID_MPEG_STREAM_TYPE really determines the MPEG-1/2 setting */ - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); if (err == 0) qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; return err; diff --git a/linux/drivers/media/video/cx23885/Kconfig b/linux/drivers/media/video/cx23885/Kconfig index e60bd31b5..8c1b7fa47 100644 --- a/linux/drivers/media/video/cx23885/Kconfig +++ b/linux/drivers/media/video/cx23885/Kconfig @@ -15,6 +15,7 @@ config VIDEO_CX23885 select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c index 106b594ed..25349f6b9 100644 --- a/linux/drivers/media/video/cx23885/cx23885-417.c +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -4,7 +4,7 @@ * * (c) 2004 Jelle Foks <jelle@foks.8m.com> * (c) 2004 Gerd Knorr <kraxel@bytesex.org> - * (c) 2008 Steven Toth <stoth@hauppauge.com> + * (c) 2008 Steven Toth <stoth@linuxtv.org> * - CX23885/7/8 support * * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), @@ -632,7 +632,7 @@ int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value) /* ------------------------------------------------------------------ */ /* MPEG encoder API */ -char *cmd_to_str(int cmd) +static char *cmd_to_str(int cmd) { switch (cmd) { case CX2341X_ENC_PING_FW: diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index ee01ff8bf..be9a6d8ec 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org> * * 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 @@ -27,6 +27,7 @@ #include "compat.h" #include "cx23885.h" +#include "tuner-xc2028.h" /* ------------------------------------------------------------------ */ /* board config info */ @@ -149,6 +150,15 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP] = { + .name = "DViCO FusionHDTV DVB-T Dual Express", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H] = { + .name = "Leadtek Winfast PxDVR3200 H", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -220,6 +230,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x18ac, .subdevice = 0xd618, .card = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP, + },{ + .subvendor = 0x18ac, + .subdevice = 0xdb78, + .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP, + }, { + .subvendor = 0x107d, + .subdevice = 0x6681, + .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -320,15 +338,15 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) dev->name, tv.model); } -/* Tuner callback function for cx23885 boards. Currently only needed - * for HVR1500Q, which has an xc5000 tuner. - */ int cx23885_tuner_callback(void *priv, int command, int arg) { - struct cx23885_i2c *bus = priv; - struct cx23885_dev *dev = bus->dev; + struct cx23885_tsport *port = priv; + struct cx23885_dev *dev = port->dev; u32 bitmask = 0; + if (command == XC2028_RESET_CLK) + return 0; + if (command != 0) { printk(KERN_ERR "%s(): Unknown command 0x%x.\n", __func__, command); @@ -336,21 +354,21 @@ int cx23885_tuner_callback(void *priv, int command, int arg) } switch(dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1400: + case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: - /* Tuner Reset Command from xc5000 */ - if (command == 0) - bitmask = 0x04; + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + /* Tuner Reset Command */ + bitmask = 0x04; break; case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: - if (command == 0) { - - /* Two identical tuners on two different i2c buses, - * we need to reset the correct gpio. */ - if (bus->nr == 0) - bitmask = 0x01; - else if (bus->nr == 1) - bitmask = 0x04; - } + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + /* Two identical tuners on two different i2c buses, + * we need to reset the correct gpio. */ + if (port->nr == 0) + bitmask = 0x01; + else if (port->nr == 1) + bitmask = 0x04; break; } @@ -466,6 +484,32 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) mdelay(20); cx_set(GP0_IO, 0x000f000f); break; + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + /* GPIO-0 portb xc3028 reset */ + /* GPIO-1 portb zl10353 reset */ + /* GPIO-2 portc xc3028 reset */ + /* GPIO-3 portc zl10353 reset */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x000f0000); + mdelay(20); + cx_clear(GP0_IO, 0x0000000f); + mdelay(20); + cx_set(GP0_IO, 0x000f000f); + break; + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + /* GPIO-2 xc3028 tuner reset */ + + /* The following GPIO's are on the internal AVCore (cx25840) */ + /* GPIO-? zl10353 demod reset */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00040000); + mdelay(20); + cx_clear(GP0_IO, 0x00000004); + mdelay(20); + cx_set(GP0_IO, 0x00040004); + break; } } @@ -480,6 +524,9 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1400: /* FIXME: Implement me */ break; + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + request_module("ir-kbd-i2c"); + break; } return 0; @@ -517,6 +564,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) switch (dev->board) { case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; @@ -549,6 +597,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1200: case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_HAUPPAUGE_HVR1400: + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -562,6 +611,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: case CX23885_BOARD_HAUPPAUGE_HVR1700: + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: request_module("cx25840"); break; } diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c index 0c34db9f7..c050cd2ce 100644 --- a/linux/drivers/media/video/cx23885/cx23885-core.c +++ b/linux/drivers/media/video/cx23885/cx23885-core.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org> * * 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 @@ -34,7 +34,7 @@ #include "cx23885.h" MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); -MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); +MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); MODULE_LICENSE("GPL"); static unsigned int debug; diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 3c01ef2bf..c84688aba 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org> * * 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 @@ -43,6 +43,7 @@ #include "tuner-simple.h" #include "dib7000p.h" #include "dibx000_common.h" +#include "zl10353.h" static unsigned int debug; @@ -189,13 +190,13 @@ static struct s5h1411_config dvico_s5h1411_config = { static struct xc5000_config hauppauge_hvr1500q_tunerconfig = { .i2c_address = 0x61, .if_khz = 5380, - .tuner_callback = cx23885_tuner_callback + .tuner_callback = cx23885_tuner_callback, }; static struct xc5000_config dvico_xc5000_tunerconfig = { .i2c_address = 0x64, .if_khz = 5380, - .tuner_callback = cx23885_tuner_callback + .tuner_callback = cx23885_tuner_callback, }; static struct tda829x_config tda829x_no_probe = { @@ -304,35 +305,11 @@ static struct dib7000p_config hauppauge_hvr1400_dib7000_config = { .output_mode = OUTMODE_MPEG2_SERIAL, }; -static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg) -{ - struct cx23885_tsport *port = ptr; - struct cx23885_dev *dev = port->dev; - - switch (command) { - case XC2028_TUNER_RESET: - /* Send the tuner in then out of reset */ - /* GPIO-2 xc3028 tuner */ - dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __func__, arg); - - cx_set(GP0_IO, 0x00040000); - cx_clear(GP0_IO, 0x00000004); - msleep(5); - - cx_set(GP0_IO, 0x00040004); - msleep(5); - break; - case XC2028_RESET_CLK: - dprintk(1, "%s: XC2028_RESET_CLK %d\n", __func__, arg); - break; - default: - dprintk(1, "%s: unknown command %d, arg %d\n", __func__, - command, arg); - return -EINVAL; - } - - return 0; -} +static struct zl10353_config dvico_fusionhdtv_xc3028 = { + .demod_address = 0x0f, + .if2 = 45600, + .no_tuner = 1, +}; static int dvb_register(struct cx23885_tsport *port) { @@ -414,8 +391,8 @@ static int dvb_register(struct cx23885_tsport *port) &dev->i2c_bus[0].i2c_adap); if (port->dvb.frontend != NULL) dvb_attach(xc5000_attach, port->dvb.frontend, - &i2c_bus->i2c_adap, - &hauppauge_hvr1500q_tunerconfig, i2c_bus); + &i2c_bus->i2c_adap, + &hauppauge_hvr1500q_tunerconfig); break; case CX23885_BOARD_HAUPPAUGE_HVR1500: i2c_bus = &dev->i2c_bus[1]; @@ -427,10 +404,11 @@ static int dvb_register(struct cx23885_tsport *port) struct xc2028_config cfg = { .i2c_adap = &i2c_bus->i2c_adap, .i2c_addr = 0x61, - .callback = cx23885_hvr1500_xc3028_callback, + .video_dev = port, + .callback = cx23885_tuner_callback, }; static struct xc2028_ctrl ctl = { - .fname = "xc3028-v27.fw", + .fname = XC2028_DEFAULT_FIRMWARE, .max_len = 64, .scode_table = XC3028_FE_OREN538, }; @@ -466,10 +444,11 @@ static int dvb_register(struct cx23885_tsport *port) struct xc2028_config cfg = { .i2c_adap = &dev->i2c_bus[1].i2c_adap, .i2c_addr = 0x64, - .callback = cx23885_hvr1500_xc3028_callback, + .video_dev = port, + .callback = cx23885_tuner_callback, }; static struct xc2028_ctrl ctl = { - .fname = "xc3028L-v36.fw", + .fname = XC3028L_DEFAULT_FIRMWARE, .max_len = 64, .demod = 5000, .d2633 = 1 @@ -493,8 +472,61 @@ static int dvb_register(struct cx23885_tsport *port) &i2c_bus->i2c_adap); if (port->dvb.frontend != NULL) dvb_attach(xc5000_attach, port->dvb.frontend, - &i2c_bus->i2c_adap, - &dvico_xc5000_tunerconfig, i2c_bus); + &i2c_bus->i2c_adap, + &dvico_xc5000_tunerconfig); + break; + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: { + i2c_bus = &dev->i2c_bus[port->nr - 1]; + + port->dvb.frontend = dvb_attach(zl10353_attach, + &dvico_fusionhdtv_xc3028, + &i2c_bus->i2c_adap); + if (port->dvb.frontend != NULL) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &i2c_bus->i2c_adap, + .i2c_addr = 0x61, + .video_dev = port, + .callback = cx23885_tuner_callback, + }; + static struct xc2028_ctrl ctl = { + .fname = XC2028_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = XC3028_FE_ZARLINK456, + }; + + fe = dvb_attach(xc2028_attach, port->dvb.frontend, + &cfg); + if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) + fe->ops.tuner_ops.set_config(fe, &ctl); + } + break; + } + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + i2c_bus = &dev->i2c_bus[0]; + + port->dvb.frontend = dvb_attach(zl10353_attach, + &dvico_fusionhdtv_xc3028, + &i2c_bus->i2c_adap); + if (port->dvb.frontend != NULL) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_bus[1].i2c_adap, + .i2c_addr = 0x61, + .video_dev = port, + .callback = cx23885_tuner_callback, + }; + static struct xc2028_ctrl ctl = { + .fname = XC2028_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = XC3028_FE_ZARLINK456, + }; + + fe = dvb_attach(xc2028_attach, port->dvb.frontend, + &cfg); + if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) + fe->ops.tuner_ops.set_config(fe, &ctl); + } break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", diff --git a/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c index f0ee47256..6f3a43607 100644 --- a/linux/drivers/media/video/cx23885/cx23885-i2c.c +++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/video/cx23885/cx23885-reg.h b/linux/drivers/media/video/cx23885/cx23885-reg.h index bdd11bc51..20b68a236 100644 --- a/linux/drivers/media/video/cx23885/cx23885-reg.h +++ b/linux/drivers/media/video/cx23885/cx23885-reg.h @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org> * * 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 diff --git a/linux/drivers/media/video/cx23885/cx23885-vbi.c b/linux/drivers/media/video/cx23885/cx23885-vbi.c index a6cf4146d..4b65552b3 100644 --- a/linux/drivers/media/video/cx23885/cx23885-vbi.c +++ b/linux/drivers/media/video/cx23885/cx23885-vbi.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org> * * 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 @@ -86,7 +86,9 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev, return 0; } -int cx23885_stop_vbi_dma(struct cx23885_dev *dev) +#if 0 +/* not (yet) used */ +static int cx23885_stop_vbi_dma(struct cx23885_dev *dev) { /* stop dma */ cx_clear(VID_A_DMA_CTL, 0x00000022); @@ -96,8 +98,9 @@ int cx23885_stop_vbi_dma(struct cx23885_dev *dev) cx_clear(VID_A_INT_MSK, 0x00000022); return 0; } +#endif -int cx23885_restart_vbi_queue(struct cx23885_dev *dev, +static int cx23885_restart_vbi_queue(struct cx23885_dev *dev, struct cx23885_dmaqueue *q) { struct cx23885_buffer *buf; diff --git a/linux/drivers/media/video/cx23885/cx23885-video.c b/linux/drivers/media/video/cx23885/cx23885-video.c index 98a7dc1c0..70333ee2b 100644 --- a/linux/drivers/media/video/cx23885/cx23885-video.c +++ b/linux/drivers/media/video/cx23885/cx23885-video.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org> * * 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 @@ -42,7 +42,7 @@ #endif MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); -MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); +MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); MODULE_LICENSE("GPL"); /* ------------------------------------------------------------------ */ @@ -276,7 +276,7 @@ static struct cx23885_ctrl cx23885_ctls[] = { }; static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls); -const u32 cx23885_user_ctrls[] = { +static const u32 cx23885_user_ctrls[] = { V4L2_CID_USER_CLASS, V4L2_CID_BRIGHTNESS, V4L2_CID_CONTRAST, @@ -289,14 +289,13 @@ const u32 cx23885_user_ctrls[] = { V4L2_CID_AUDIO_MUTE, 0 }; -EXPORT_SYMBOL(cx23885_user_ctrls); static const u32 *ctrl_classes[] = { cx23885_user_ctrls, NULL }; -void cx23885_video_wakeup(struct cx23885_dev *dev, +static void cx23885_video_wakeup(struct cx23885_dev *dev, struct cx23885_dmaqueue *q, u32 count) { struct cx23885_buffer *buf; @@ -331,7 +330,7 @@ void cx23885_video_wakeup(struct cx23885_dev *dev, __func__, bc); } -int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) +static int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) { dprintk(1, "%s(norm = 0x%08x) name: [%s]\n", __func__, @@ -349,7 +348,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) return 0; } -struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, +static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, struct pci_dev *pci, struct video_device *template, char *type) @@ -369,7 +368,7 @@ struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, return vfd; } -int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl) +static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl) { int i; @@ -386,7 +385,6 @@ int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl) *qctrl = cx23885_ctls[i].v; return 0; } -EXPORT_SYMBOL(cx23885_ctrl_query); /* ------------------------------------------------------------------- */ /* resource management */ @@ -437,7 +435,7 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh, mutex_unlock(&dev->lock); } -int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) +static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) { struct v4l2_routing route; memset(&route, 0, sizeof(route)); @@ -462,10 +460,9 @@ int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) return 0; } -EXPORT_SYMBOL(cx23885_video_mux); /* ------------------------------------------------------------------ */ -int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width, +static int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width, unsigned int height, enum v4l2_field field) { dprintk(1, "%s()\n", __func__); @@ -967,15 +964,14 @@ static int video_mmap(struct file *file, struct vm_area_struct *vma) /* ------------------------------------------------------------------ */ /* VIDEO CTRL IOCTLS */ -int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl) +static int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl) { dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__); cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl); return 0; } -EXPORT_SYMBOL(cx23885_get_control); -int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl) +static int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl) { dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)" " (disabled - no action)\n", __func__); @@ -984,7 +980,6 @@ int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl) #endif return 0; } -EXPORT_SYMBOL(cx23885_set_control); static void init_controls(struct cx23885_dev *dev) { @@ -1235,7 +1230,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms) return 0; } -int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) +static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) { static const char *iname[] = { [CX23885_VMUX_COMPOSITE1] = "Composite1", @@ -1268,7 +1263,6 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) i->std = CX23885_NORMS; return 0; } -EXPORT_SYMBOL(cx23885_enum_input); static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) @@ -1383,7 +1377,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, return 0; } -int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) +static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) { if (unlikely(UNSET == dev->tuner_type)) return -EINVAL; @@ -1402,7 +1396,6 @@ int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) return 0; } -EXPORT_SYMBOL(cx23885_set_freq); static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index 9a0b4bf3a..49e3eaec7 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org> * * 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 @@ -65,6 +65,8 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1700 8 #define CX23885_BOARD_HAUPPAUGE_HVR1400 9 #define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10 +#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11 +#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index 7b48e0285..cb4a9c64e 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -13,7 +13,7 @@ * NTSC sliced VBI support by Christopher Neufeld <television@cneufeld.ca> * with additional fixes by Hans Verkuil <hverkuil@xs4all.nl>. * - * CX23885 support by Steven Toth <stoth@hauppauge.com>. + * CX23885 support by Steven Toth <stoth@linuxtv.org>. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 0cd510814..5f44368f6 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1092,6 +1092,7 @@ static int mpeg_open(struct inode *inode, struct file *file) err = drv->request_acquire(drv); if(err != 0) { dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err); + unlock_kernel(); return err; } } diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index fc5807aeb..f5fef4983 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -820,13 +820,9 @@ static int dvb_register(struct cx8802_dev *dev) &pinnacle_pctv_hd_800i_config, &core->i2c_adap); if (dev->dvb.frontend != NULL) { - /* tuner_config.video_dev must point to - * i2c_adap.algo_data - */ if (!dvb_attach(xc5000_attach, dev->dvb.frontend, &core->i2c_adap, - &pinnacle_pctv_hd_800i_tuner_config, - core->i2c_adap.algo_data)) + &pinnacle_pctv_hd_800i_tuner_config)) goto frontend_detach; } break; @@ -842,7 +838,7 @@ static int dvb_register(struct cx8802_dev *dev) .callback = cx88_pci_nano_callback, }; static struct xc2028_ctrl ctl = { - .fname = "xc3028-v27.fw", + .fname = XC2028_DEFAULT_FIRMWARE, .max_len = 64, .scode_table = XC3028_FE_OREN538, }; @@ -881,13 +877,9 @@ static int dvb_register(struct cx8802_dev *dev) &dvico_fusionhdtv7_config, &core->i2c_adap); if (dev->dvb.frontend != NULL) { - /* tuner_config.video_dev must point to - * i2c_adap.algo_data - */ if (!dvb_attach(xc5000_attach, dev->dvb.frontend, &core->i2c_adap, - &dvico_fusionhdtv7_tuner_config, - core->i2c_adap.algo_data)) + &dvico_fusionhdtv7_tuner_config)) goto frontend_detach; } break; diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c index f23e6b84d..4acba6e92 100644 --- a/linux/drivers/media/video/dabusb.c +++ b/linux/drivers/media/video/dabusb.c @@ -451,6 +451,7 @@ static int dabusb_fpga_download (pdabusb_t s, const char *fname) ret = request_firmware(&fw, "dabusb/bitstream.bin", &s->usbdev->dev); if (ret) { err("Failed to load \"dabusb/bitstream.bin\": %d\n", ret); + kfree(b); return ret; } diff --git a/linux/drivers/media/video/dpc7146.c b/linux/drivers/media/video/dpc7146.c deleted file mode 100644 index 4bfb3076b..000000000 --- a/linux/drivers/media/video/dpc7146.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - dpc7146.c - v4l2 driver for the dpc7146 demonstration board - - Copyright (C) 2000-2003 Michael Hunold <michael@mihu.de> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#define DEBUG_VARIABLE debug - -#include <media/saa7146_vv.h> -#include <linux/video_decoder.h> /* for saa7111a */ -#include "compat.h" - -#define I2C_SAA7111A 0x24 - -/* All unused bytes are reserverd. */ -#define SAA711X_CHIP_VERSION 0x00 -#define SAA711X_ANALOG_INPUT_CONTROL_1 0x02 -#define SAA711X_ANALOG_INPUT_CONTROL_2 0x03 -#define SAA711X_ANALOG_INPUT_CONTROL_3 0x04 -#define SAA711X_ANALOG_INPUT_CONTROL_4 0x05 -#define SAA711X_HORIZONTAL_SYNC_START 0x06 -#define SAA711X_HORIZONTAL_SYNC_STOP 0x07 -#define SAA711X_SYNC_CONTROL 0x08 -#define SAA711X_LUMINANCE_CONTROL 0x09 -#define SAA711X_LUMINANCE_BRIGHTNESS 0x0A -#define SAA711X_LUMINANCE_CONTRAST 0x0B -#define SAA711X_CHROMA_SATURATION 0x0C -#define SAA711X_CHROMA_HUE_CONTROL 0x0D -#define SAA711X_CHROMA_CONTROL 0x0E -#define SAA711X_FORMAT_DELAY_CONTROL 0x10 -#define SAA711X_OUTPUT_CONTROL_1 0x11 -#define SAA711X_OUTPUT_CONTROL_2 0x12 -#define SAA711X_OUTPUT_CONTROL_3 0x13 -#define SAA711X_V_GATE_1_START 0x15 -#define SAA711X_V_GATE_1_STOP 0x16 -#define SAA711X_V_GATE_1_MSB 0x17 -#define SAA711X_TEXT_SLICER_STATUS 0x1A -#define SAA711X_DECODED_BYTES_OF_TS_1 0x1B -#define SAA711X_DECODED_BYTES_OF_TS_2 0x1C -#define SAA711X_STATUS_BYTE 0x1F - -#define DPC_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "debug verbosity"); - -static int dpc_num; - -#define DPC_INPUTS 2 -static struct v4l2_input dpc_inputs[DPC_INPUTS] = { - { 0, "Port A", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, - { 1, "Port B", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, -}; - -#define DPC_AUDIOS 0 - -static struct saa7146_extension_ioctls ioctls[] = { - { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_S_STD, SAA7146_AFTER }, - { 0, 0 } -}; - -struct dpc -{ - struct video_device *video_dev; - struct video_device *vbi_dev; - - struct i2c_adapter i2c_adapter; - struct i2c_client *saa7111a; - - int cur_input; /* current input */ -}; - -static int dpc_check_clients(struct device *dev, void *data) -{ - struct dpc* dpc = data; - struct i2c_client *client = i2c_verify_client(dev); - - if( !client ) - return 0; - - if( I2C_SAA7111A == client->addr ) - dpc->saa7111a = client; - - return 0; -} - -/* fixme: add vbi stuff here */ -static int dpc_probe(struct saa7146_dev* dev) -{ - struct dpc* dpc = NULL; - - dpc = kzalloc(sizeof(struct dpc), GFP_KERNEL); - if( NULL == dpc ) { - printk("dpc_v4l2.o: dpc_probe: not enough kernel memory.\n"); - return -ENOMEM; - } - - /* FIXME: enable i2c-port pins, video-port-pins - video port pins should be enabled here ?! */ - saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); - - dpc->i2c_adapter = (struct i2c_adapter) { - .class = I2C_CLASS_TV_ANALOG, - .name = "dpc7146", - }; - saa7146_i2c_adapter_prepare(dev, &dpc->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); - if(i2c_add_adapter(&dpc->i2c_adapter) < 0) { - DEB_S(("cannot register i2c-device. skipping.\n")); - kfree(dpc); - return -EFAULT; - } - - /* loop through all i2c-devices on the bus and look who is there */ - device_for_each_child(&dpc->i2c_adapter.dev, dpc, dpc_check_clients); - - /* check if all devices are present */ - if (!dpc->saa7111a) { - DEB_D(("dpc_v4l2.o: dpc_attach failed for this device.\n")); - i2c_del_adapter(&dpc->i2c_adapter); - kfree(dpc); - return -ENODEV; - } - - /* all devices are present, probe was successful */ - DEB_D(("dpc_v4l2.o: dpc_probe succeeded for this device.\n")); - - /* we store the pointer in our private data field */ - dev->ext_priv = dpc; - - return 0; -} - -/* bring hardware to a sane state. this has to be done, just in case someone - wants to capture from this device before it has been properly initialized. - the capture engine would badly fail, because no valid signal arrives on the - saa7146, thus leading to timeouts and stuff. */ -static int dpc_init_done(struct saa7146_dev* dev) -{ - struct dpc* dpc = (struct dpc*)dev->ext_priv; - - DEB_D(("dpc_v4l2.o: dpc_init_done called.\n")); - - /* initialize the helper ics to useful values */ - i2c_smbus_write_byte_data(dpc->saa7111a, 0x00, 0x11); - - i2c_smbus_write_byte_data(dpc->saa7111a, 0x02, 0xc0); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x03, 0x30); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x04, 0x00); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x05, 0x00); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x06, 0xde); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x07, 0xad); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x08, 0xa8); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x09, 0x00); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0a, 0x80); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0b, 0x47); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0c, 0x40); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0d, 0x00); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0e, 0x03); - - i2c_smbus_write_byte_data(dpc->saa7111a, 0x10, 0xd0); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x11, 0x1c); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x12, 0xc1); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x13, 0x30); - - i2c_smbus_write_byte_data(dpc->saa7111a, 0x1f, 0x81); - - return 0; -} - -static struct saa7146_ext_vv vv_data; - -/* this function only gets called when the probing was successful */ -static int dpc_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) -{ - struct dpc* dpc = (struct dpc*)dev->ext_priv; - - DEB_D(("dpc_v4l2.o: dpc_attach called.\n")); - - /* checking for i2c-devices can be omitted here, because we - already did this in "dpc_vl42_probe" */ - - saa7146_vv_init(dev,&vv_data); - if( 0 != saa7146_register_device(&dpc->video_dev, dev, "dpc", VFL_TYPE_GRABBER)) { - ERR(("cannot register capture v4l2 device. skipping.\n")); - return -1; - } - - /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ - if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) { - if( 0 != saa7146_register_device(&dpc->vbi_dev, dev, "dpc", VFL_TYPE_VBI)) { - ERR(("cannot register vbi v4l2 device. skipping.\n")); - } - } - - i2c_use_client(dpc->saa7111a); - - printk("dpc: found 'dpc7146 demonstration board'-%d.\n",dpc_num); - dpc_num++; - - /* the rest */ - dpc->cur_input = 0; - dpc_init_done(dev); - - return 0; -} - -static int dpc_detach(struct saa7146_dev* dev) -{ - struct dpc* dpc = (struct dpc*)dev->ext_priv; - - DEB_EE(("dev:%p\n",dev)); - - i2c_release_client(dpc->saa7111a); - - saa7146_unregister_device(&dpc->video_dev,dev); - if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) { - saa7146_unregister_device(&dpc->vbi_dev,dev); - } - saa7146_vv_release(dev); - - dpc_num--; - - i2c_del_adapter(&dpc->i2c_adapter); - kfree(dpc); - return 0; -} - -#ifdef axa -int dpc_vbi_bypass(struct saa7146_dev* dev) -{ - struct dpc* dpc = (struct dpc*)dev->ext_priv; - - int i = 1; - - /* switch bypass in saa7111a */ - if ( 0 != dpc->saa7111a->driver->command(dpc->saa7111a,SAA711X_VBI_BYPASS, &i)) { - printk("dpc_v4l2.o: VBI_BYPASS: could not address saa7111a.\n"); - return -1; - } - - return 0; -} -#endif - -static int dpc_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) -{ - struct saa7146_dev *dev = fh->dev; - struct dpc* dpc = (struct dpc*)dev->ext_priv; -/* - struct saa7146_vv *vv = dev->vv_data; -*/ - switch(cmd) - { - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; - DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index)); - - if( i->index < 0 || i->index >= DPC_INPUTS) { - return -EINVAL; - } - - memcpy(i, &dpc_inputs[i->index], sizeof(struct v4l2_input)); - - DEB_D(("dpc_v4l2.o: v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n",i->index)); - return 0; - } - case VIDIOC_G_INPUT: - { - int *input = (int *)arg; - *input = dpc->cur_input; - - DEB_D(("dpc_v4l2.o: VIDIOC_G_INPUT: %d\n",*input)); - return 0; - } - case VIDIOC_S_INPUT: - { - int input = *(int *)arg; - - if (input < 0 || input >= DPC_INPUTS) { - return -EINVAL; - } - - dpc->cur_input = input; - - /* fixme: switch input here, switch audio, too! */ -// saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync); - printk("dpc_v4l2.o: VIDIOC_S_INPUT: fixme switch input.\n"); - - return 0; - } - default: -/* - DEB_D(("dpc_v4l2.o: v4l2_ioctl does not handle this ioctl.\n")); -*/ - return -ENOIOCTLCMD; - } - return 0; -} - -static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) -{ - return 0; -} - -static struct saa7146_standard standard[] = { - { - .name = "PAL", .id = V4L2_STD_PAL, - .v_offset = 0x17, .v_field = 288, - .h_offset = 0x14, .h_pixels = 680, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 0x16, .v_field = 240, - .h_offset = 0x06, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - }, { - .name = "SECAM", .id = V4L2_STD_SECAM, - .v_offset = 0x14, .v_field = 288, - .h_offset = 0x14, .h_pixels = 720, - .v_max_out = 576, .h_max_out = 768, - } -}; - -static struct saa7146_extension extension; - -static struct saa7146_pci_extension_data dpc = { - .ext_priv = "Multimedia eXtension Board", - .ext = &extension, -}; - -static struct pci_device_id pci_tbl[] = { - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x0000, - .subdevice = 0x0000, - .driver_data = (unsigned long)&dpc, - }, { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_ext_vv vv_data = { - .inputs = DPC_INPUTS, - .capabilities = V4L2_CAP_VBI_CAPTURE, - .stds = &standard[0], - .num_stds = sizeof(standard)/sizeof(struct saa7146_standard), - .std_callback = &std_callback, - .ioctls = &ioctls[0], - .ioctl = dpc_ioctl, -}; - -static struct saa7146_extension extension = { - .name = "dpc7146 demonstration board", - .flags = SAA7146_USE_I2C_IRQ, - - .pci_tbl = &pci_tbl[0], - .module = THIS_MODULE, - - .probe = dpc_probe, - .attach = dpc_attach, - .detach = dpc_detach, - - .irq_mask = 0, - .irq_func = NULL, -}; - -static int __init dpc_init_module(void) -{ - if( 0 != saa7146_register_extension(&extension)) { - DEB_S(("failed to register extension.\n")); - return -ENODEV; - } - - return 0; -} - -static void __exit dpc_cleanup_module(void) -{ - saa7146_unregister_extension(&extension); -} - -module_init(dpc_init_module); -module_exit(dpc_cleanup_module); - -MODULE_DESCRIPTION("video4linux-2 driver for the 'dpc7146 demonstration board'"); -MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); -MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index 904973305..14ef4da24 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -171,8 +171,14 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) memset(dev->adev->transfer_buffer[i], 0x80, sb_size); urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); - if (!urb) + if (!urb) { + em28xx_errdev("usb_alloc_urb failed!\n"); + for (j = 0; j < i; j++) { + usb_free_urb(dev->adev->urb[j]); + kfree(dev->adev->transfer_buffer[j]); + } return -ENOMEM; + } urb->dev = dev->udev; urb->context = dev; diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index eae5ebd0e..5abdefdf0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -94,28 +94,6 @@ struct em28xx_board em28xx_boards[] = { .amux = 0, } }, }, - [EM2800_BOARD_KWORLD_USB2800] = { - .name = "Kworld USB2800", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .is_em2800 = 1, - .vchannels = 3, - .tuner_type = TUNER_PHILIPS_FCV1236D, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = 0, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = 1, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = 1, - } }, - }, [EM2820_BOARD_KWORLD_PVRTV2800RF] = { .name = "Kworld PVR TV 2800 RF", .is_em2800 = 0, @@ -1123,7 +1101,7 @@ struct usb_device_id em28xx_id_table [] = { { USB_DEVICE(0xeb1a, 0x2820), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2821), - .driver_info = EM2820_BOARD_UNKNOWN }, + .driver_info = EM2820_BOARD_PROLINK_PLAYTV_USB2 }, { USB_DEVICE(0xeb1a, 0x2860), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2861), @@ -1556,9 +1534,12 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) /* djh - Not sure which demod we need here */ ctl->demod = XC3028_FE_DEFAULT; break; + case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: + ctl->demod = XC3028_FE_DEFAULT; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + break; case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: - case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: /* FIXME: Better to specify the needed IF */ ctl->demod = XC3028_FE_DEFAULT; break; diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 5b532ccec..79c11b1d0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -275,7 +275,7 @@ static int attach_xc3028(u8 addr, struct em28xx *dev) /* ------------------------------------------------------------------ */ -int register_dvb(struct em28xx_dvb *dvb, +static int register_dvb(struct em28xx_dvb *dvb, struct module *module, struct em28xx *dev, struct device *device) diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index 97853384c..3bab56b99 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -143,10 +143,11 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) } for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; write_timeout -= 5) { - unsigned msg = dev->em28xx_read_reg(dev, 0x5); - if (msg == 0x94) + unsigned reg = dev->em28xx_read_reg(dev, 0x5); + + if (reg == 0x94) return -ENODEV; - else if (msg == 0x84) + else if (reg == 0x84) return 0; msleep(5); } @@ -335,8 +336,11 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) /* Check if board has eeprom */ err = i2c_master_recv(&dev->i2c_client, &buf, 0); - if (err < 0) - return -1; + if (err < 0) { + em28xx_errdev("%s: i2c_master_recv failed! err [%d]\n", + __func__, err); + return err; + } buf = 0; @@ -344,7 +348,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) if (err != 1) { printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", dev->name, err); - return -1; + return err; } while (size > 0) { if (size > 16) @@ -357,7 +361,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n", dev->name, err); - return -1; + return err; } size -= block; p += block; @@ -585,18 +589,31 @@ void em28xx_i2c_call_clients(struct em28xx *dev, unsigned int cmd, void *arg) */ int em28xx_i2c_register(struct em28xx *dev) { + int retval; + BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg); BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req); dev->i2c_adap = em28xx_adap_template; dev->i2c_adap.dev.parent = &dev->udev->dev; strcpy(dev->i2c_adap.name, dev->name); dev->i2c_adap.algo_data = dev; - i2c_add_adapter(&dev->i2c_adap); + + retval = i2c_add_adapter(&dev->i2c_adap); + if (retval < 0) { + em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n", + __func__, retval); + return retval; + } dev->i2c_client = em28xx_client_template; dev->i2c_client.adapter = &dev->i2c_adap; - em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata)); + retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata)); + if (retval < 0) { + em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n", + __func__, retval); + return retval; + } if (i2c_scan) em28xx_do_i2c_scan(dev); diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index c9a2d4808..e94b50f00 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -517,10 +517,17 @@ static struct videobuf_queue_ops em28xx_video_qops = { */ static int em28xx_config(struct em28xx *dev) { + int retval; /* Sets I2C speed to 100 KHz */ - if (!dev->is_em2800) - em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1); + if (!dev->is_em2800) { + retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1); + if (retval < 0) { + em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n", + __func__, retval); + return retval; + } + } #if 1 /* enable vbi capturing */ @@ -2015,13 +2022,23 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } /* register i2c bus */ - em28xx_i2c_register(dev); + errCode = em28xx_i2c_register(dev); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n", + __func__, errCode); + return errCode; + } /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); /* Configure audio */ - em28xx_audio_analog_set(dev); + errCode = em28xx_audio_analog_set(dev); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n", + __func__, errCode); + return errCode; + } /* configure the device */ em28xx_config_i2c(dev); @@ -2041,6 +2058,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->ctl_input = 2; errCode = em28xx_config(dev); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_config - errCode [%d]!\n", + __func__, errCode); + return errCode; + } list_add_tail(&dev->devlist, &em28xx_devlist); @@ -2096,9 +2118,20 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, if (dev->has_msp34xx) { /* Send a reset to other chips via gpio */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); + errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n", + __func__, errCode); + return errCode; + } msleep(3); - em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); + + errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n", + __func__, errCode); + return errCode; + } msleep(3); } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index e5e47fe66..4bdff9619 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -420,8 +420,8 @@ struct em28xx { /* frame properties */ int width; /* current frame width */ int height; /* current frame height */ - int hscale; /* horizontal scale factor (see datasheet) */ - int vscale; /* vertical scale factor (see datasheet) */ + unsigned hscale; /* horizontal scale factor (see datasheet) */ + unsigned vscale; /* vertical scale factor (see datasheet) */ int interlaced; /* 1=interlace fileds, 0=just top fileds */ unsigned int video_bytesread; /* Number of bytes read */ diff --git a/linux/drivers/media/video/et61x251/et61x251_core.c b/linux/drivers/media/video/et61x251/et61x251_core.c index 7d0f31db5..f58aee5aa 100644 --- a/linux/drivers/media/video/et61x251/et61x251_core.c +++ b/linux/drivers/media/video/et61x251/et61x251_core.c @@ -1218,7 +1218,7 @@ static int et61x251_open(struct inode* inode, struct file* filp) if (!down_read_trylock(&et61x251_dev_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); if (wait_for_completion_interruptible(&cam->probe)) { up_read(&et61x251_dev_lock); @@ -1301,7 +1301,7 @@ static int et61x251_release(struct inode* inode, struct file* filp) down_write(&et61x251_dev_lock); - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); et61x251_stop_transfer(cam); et61x251_release_buffers(cam); @@ -1322,7 +1322,7 @@ static ssize_t et61x251_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); struct et61x251_frame_t* f, * i; unsigned long lock_flags; long timeout; @@ -1430,7 +1430,7 @@ exit: static unsigned int et61x251_poll(struct file *filp, poll_table *wait) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); struct et61x251_frame_t* f; unsigned long lock_flags; unsigned int mask = 0; @@ -1506,7 +1506,7 @@ static struct vm_operations_struct et61x251_vm_ops = { static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; void *pos; @@ -2399,7 +2399,7 @@ et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg) static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); switch (cmd) { @@ -2494,7 +2494,7 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, static int et61x251_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); int err = 0; if (mutex_lock_interruptible(&cam->fileop_mutex)) @@ -2592,6 +2592,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->fops = &et61x251_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; + cam->v4ldev->parent = &udev->dev; video_set_drvdata(cam->v4ldev, cam); init_completion(&cam->probe); diff --git a/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c index cd3a3f582..4d9f4cc25 100644 --- a/linux/drivers/media/video/gspca/conex.c +++ b/linux/drivers/media/video/gspca/conex.c @@ -124,7 +124,7 @@ static void reg_r(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { err("reg_r: buffer overflow"); return; } @@ -164,7 +164,7 @@ static void reg_w(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { err("reg_w: buffer overflow"); return; } @@ -731,13 +731,13 @@ static void cx11646_jpeg(struct gspca_dev*gspca_dev) reg_w_val(gspca_dev, 0x0000, 0x00); /* wait for completion */ retry = 50; - while (retry--) { + do { reg_r(gspca_dev, 0x0002, 1); /* 0x07 until 0x00 */ if (gspca_dev->usb_buf[0] == 0x00) break; reg_w_val(gspca_dev, 0x0053, 0x00); - } + } while (--retry); if (retry == 0) PDEBUG(D_ERR, "Damned Errors sending jpeg Table"); /* send the qtable now */ @@ -826,8 +826,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { cx11646_init1(gspca_dev); cx11646_initsize(gspca_dev); @@ -845,10 +845,6 @@ static void sd_start(struct gspca_dev *gspca_dev) cx11646_jpeg(gspca_dev); } -static void sd_stopN(struct gspca_dev *gspca_dev) -{ -} - static void sd_stop0(struct gspca_dev *gspca_dev) { int retry = 50; @@ -871,10 +867,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w_val(gspca_dev, 0x00fc, 0xe0); } -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -998,11 +990,9 @@ static struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, - .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -1026,6 +1016,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/etoms.c b/linux/drivers/media/video/gspca/etoms.c index 60b316a37..86f3932c2 100644 --- a/linux/drivers/media/video/gspca/etoms.c +++ b/linux/drivers/media/video/gspca/etoms.c @@ -81,6 +81,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, +#define COLOR_IDX 2 { { .id = V4L2_CID_SATURATION, @@ -234,7 +235,7 @@ static void reg_r(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { err("reg_r: buffer overflow"); return; } @@ -272,7 +273,7 @@ static void reg_w(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { err("reg_w: buffer overflow"); return; } @@ -665,6 +666,7 @@ static int sd_config(struct gspca_dev *gspca_dev, } else { cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + gspca_dev->ctrl_dis = (1 << COLOR_IDX); } sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; @@ -674,8 +676,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -709,14 +711,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) et_video(gspca_dev, 0); /* video off */ } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static __u8 Et_getgainG(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -899,21 +893,19 @@ static struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, }; /* -- module initialisation -- */ static __devinitdata struct usb_device_id device_table[] = { -#ifndef CONFIG_USB_ET61X251 {USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106}, -#endif +#if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE {USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX}, +#endif {} }; @@ -932,6 +924,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 61071a4f4..8c98c7a05 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -47,7 +47,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 2, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 3, 0) static int video_nr = -1; @@ -118,7 +118,10 @@ static void fill_frame(struct gspca_dev *gspca_dev, cam_pkt_op pkt_scan; if (urb->status != 0) { - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); +#ifdef CONFIG_PM + if (!gspca_dev->frozen) +#endif + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); return; /* disconnection ? */ } pkt_scan = gspca_dev->sd_desc->pkt_scan; @@ -182,6 +185,46 @@ static void isoc_irq(struct urb *urb } /* + * bulk message interrupt from the USB device + */ +static void bulk_irq(struct urb *urb +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) + , struct pt_regs *regs +#endif +) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + struct gspca_frame *frame; + int j; + + PDEBUG(D_PACK, "bulk irq"); + if (!gspca_dev->streaming) + return; + if (urb->status != 0 && urb->status != -ECONNRESET) { +#ifdef CONFIG_PM + if (!gspca_dev->frozen) +#endif + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + return; /* disconnection ? */ + } + + /* check the availability of the frame buffer */ + j = gspca_dev->fr_i; + j = gspca_dev->fr_queue[j]; + frame = &gspca_dev->frame[j]; + if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) + != V4L2_BUF_FLAG_QUEUED) { + gspca_dev->last_packet_type = DISCARD_PACKET; + } else { + PDEBUG(D_PACK, "packet l:%d", urb->actual_length); + gspca_dev->sd_desc->pkt_scan(gspca_dev, + frame, + urb->transfer_buffer, + urb->actual_length); + } +} + +/* * add data to the current frame * * This function is called by the subdrivers at interrupt level. @@ -378,10 +421,11 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) } /* - * search an input isochronous endpoint in an alternate setting + * search an input transfer endpoint in an alternate setting */ -static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt, - __u8 epaddr) +static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, + __u8 epaddr, + __u8 xfer) { struct usb_host_endpoint *ep; int i, attr; @@ -392,7 +436,7 @@ static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt, if (ep->desc.bEndpointAddress == epaddr) { attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - if (attr == USB_ENDPOINT_XFER_ISOC) + if (attr == xfer) return ep; break; } @@ -401,14 +445,14 @@ static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt, } /* - * search an input isochronous endpoint + * search an input (isoc or bulk) endpoint * * The endpoint is defined by the subdriver. * Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep). * This routine may be called many times when the bandwidth is too small * (the bandwidth is checked on urb submit). */ -static struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) +static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) { struct usb_interface *intf; struct usb_host_endpoint *ep; @@ -418,15 +462,19 @@ static struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) ep = NULL; i = gspca_dev->alt; /* previous alt setting */ while (--i > 0) { /* alt 0 is unusable */ - ep = alt_isoc(&intf->altsetting[i], gspca_dev->cam.epaddr); + ep = alt_xfer(&intf->altsetting[i], + gspca_dev->cam.epaddr, + gspca_dev->bulk + ? USB_ENDPOINT_XFER_BULK + : USB_ENDPOINT_XFER_ISOC); if (ep) break; } if (ep == NULL) { - err("no ISOC endpoint found"); + err("no transfer endpoint found"); return NULL; } - PDEBUG(D_STREAM, "use ISOC alt %d ep 0x%02x", + PDEBUG(D_STREAM, "use alt %d ep 0x%02x", i, ep->desc.bEndpointAddress); ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); if (ret < 0) { @@ -438,7 +486,7 @@ static struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) } /* - * create the isochronous URBs + * create the URBs for image transfer */ static int create_urbs(struct gspca_dev *gspca_dev, struct usb_host_endpoint *ep) @@ -451,18 +499,28 @@ static int create_urbs(struct gspca_dev *gspca_dev, /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - npkt = ISO_MAX_SIZE / psize; - if (npkt > ISO_MAX_PKT) - npkt = ISO_MAX_PKT; - bsize = psize * npkt; - PDEBUG(D_STREAM, - "isoc %d pkts size %d (bsize:%d)", npkt, psize, bsize); - nurbs = DEF_NURBS; + if (!gspca_dev->bulk) { + npkt = ISO_MAX_SIZE / psize; + if (npkt > ISO_MAX_PKT) + npkt = ISO_MAX_PKT; + bsize = psize * npkt; + PDEBUG(D_STREAM, + "isoc %d pkts size %d = bsize:%d", + npkt, psize, bsize); + nurbs = DEF_NURBS; + } else { + npkt = 0; + bsize = psize; + PDEBUG(D_STREAM, "bulk bsize:%d", bsize); + nurbs = 1; + } + gspca_dev->nurbs = nurbs; for (n = 0; n < nurbs; n++) { urb = usb_alloc_urb(npkt, GFP_KERNEL); if (!urb) { err("usb_alloc_urb failed"); + destroy_urbs(gspca_dev); return -ENOMEM; } urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev, @@ -472,24 +530,30 @@ static int create_urbs(struct gspca_dev *gspca_dev, if (urb->transfer_buffer == NULL) { usb_free_urb(urb); - destroy_urbs(gspca_dev); err("usb_buffer_urb failed"); + destroy_urbs(gspca_dev); return -ENOMEM; } gspca_dev->urb[n] = urb; urb->dev = gspca_dev->dev; urb->context = gspca_dev; - urb->pipe = usb_rcvisocpipe(gspca_dev->dev, - ep->desc.bEndpointAddress); - urb->transfer_flags = URB_ISO_ASAP - | URB_NO_TRANSFER_DMA_MAP; - urb->interval = ep->desc.bInterval; - urb->complete = isoc_irq; - urb->number_of_packets = npkt; urb->transfer_buffer_length = bsize; - for (i = 0; i < npkt; i++) { - urb->iso_frame_desc[i].length = psize; - urb->iso_frame_desc[i].offset = psize * i; + if (npkt != 0) { /* ISOC */ + urb->pipe = usb_rcvisocpipe(gspca_dev->dev, + ep->desc.bEndpointAddress); + urb->transfer_flags = URB_ISO_ASAP + | URB_NO_TRANSFER_DMA_MAP; + urb->interval = ep->desc.bInterval; + urb->complete = isoc_irq; + urb->number_of_packets = npkt; + for (i = 0; i < npkt; i++) { + urb->iso_frame_desc[i].length = psize; + urb->iso_frame_desc[i].offset = psize * i; + } + } else { /* bulk */ + urb->pipe = usb_rcvbulkpipe(gspca_dev->dev, + ep->desc.bEndpointAddress), + urb->complete = bulk_irq; } } return 0; @@ -511,7 +575,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->alt = gspca_dev->nbalt; for (;;) { PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt); - ep = get_isoc_ep(gspca_dev); + ep = get_ep(gspca_dev); if (ep == NULL) { ret = -EIO; goto out; @@ -525,6 +589,10 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->streaming = 1; atomic_set(&gspca_dev->nevent, 0); + /* start the bulk transfer is done by the subdriver */ + if (gspca_dev->bulk) + break; + /* submit the URBs */ for (n = 0; n < gspca_dev->nurbs; n++) { ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL); @@ -562,10 +630,12 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev) gspca_dev->streaming = 0; atomic_set(&gspca_dev->nevent, 0); if (gspca_dev->present) { - gspca_dev->sd_desc->stopN(gspca_dev); + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); gspca_set_alt0(gspca_dev); - gspca_dev->sd_desc->stop0(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); PDEBUG(D_STREAM, "stream off OK"); } } @@ -774,19 +844,7 @@ static int dev_open(struct inode *inode, struct file *file) goto out; } - /* if not done yet, initialize the sensor */ - if (gspca_dev->users == 0) { - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { - ret = -ERESTARTSYS; - goto out; - } - ret = gspca_dev->sd_desc->open(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - if (ret != 0) { - PDEBUG(D_ERR|D_CONF, "init device failed %d", ret); - goto out; - } - } else if (gspca_dev->users > 4) { /* (arbitrary value) */ + if (gspca_dev->users > 4) { /* (arbitrary value) */ ret = -EBUSY; goto out; } @@ -799,6 +857,7 @@ static int dev_open(struct inode *inode, struct file *file) else gspca_dev->vdev.debug &= ~3; #endif + ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); if (ret != 0) @@ -819,11 +878,11 @@ static int dev_close(struct inode *inode, struct file *file) /* if the file did the capture, free the streaming resources */ if (gspca_dev->capt_file == file) { - mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->streaming) + if (gspca_dev->streaming) { + mutex_lock(&gspca_dev->usb_lock); gspca_stream_off(gspca_dev); - gspca_dev->sd_desc->close(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + } frame_free(gspca_dev); gspca_dev->capt_file = NULL; gspca_dev->memory = GSPCA_MEMORY_NO; @@ -860,42 +919,44 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -/* the use of V4L2_CTRL_FLAG_NEXT_CTRL asks for the controls to be sorted */ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *q_ctrl) { struct gspca_dev *gspca_dev = priv; - int i; + int i, ix; u32 id; + ix = -1; id = q_ctrl->id; if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { id &= V4L2_CTRL_ID_MASK; id++; for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (id >= gspca_dev->sd_desc->ctrls[i].qctrl.id) { - memcpy(q_ctrl, - &gspca_dev->sd_desc->ctrls[i].qctrl, - sizeof *q_ctrl); - return 0; + if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) + continue; + if (ix < 0) { + ix = i; + continue; } + if (gspca_dev->sd_desc->ctrls[i].qctrl.id + > gspca_dev->sd_desc->ctrls[ix].qctrl.id) + continue; + ix = i; } - return -EINVAL; } for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) { - memcpy(q_ctrl, - &gspca_dev->sd_desc->ctrls[i].qctrl, - sizeof *q_ctrl); - return 0; + ix = i; + break; } } - if (id >= V4L2_CID_BASE - && id <= V4L2_CID_LASTP1) { + if (ix < 0) + return -EINVAL; + memcpy(q_ctrl, &gspca_dev->sd_desc->ctrls[ix].qctrl, + sizeof *q_ctrl); + if (gspca_dev->ctrl_dis & (1 << ix)) q_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - } - return -EINVAL; + return 0; } static int vidioc_s_ctrl(struct file *file, void *priv, @@ -910,6 +971,8 @@ static int vidioc_s_ctrl(struct file *file, void *priv, i++, ctrls++) { if (ctrl->id != ctrls->qctrl.id) continue; + if (gspca_dev->ctrl_dis & (1 << i)) + return -EINVAL; if (ctrl->value < ctrls->qctrl.minimum || ctrl->value > ctrls->qctrl.maximum) return -ERANGE; @@ -936,6 +999,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv, i++, ctrls++) { if (ctrl->id != ctrls->qctrl.id) continue; + if (gspca_dev->ctrl_dis & (1 << i)) + return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; ret = ctrls->get(gspca_dev, &ctrl->value); @@ -1410,7 +1475,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, i = ret; /* frame index */ frame = &gspca_dev->frame[i]; if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { - if (copy_to_user((__u8 *) frame->v4l2_buf.m.userptr, + if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, frame->data, frame->v4l2_buf.bytesused)) { PDEBUG(D_ERR|D_STREAM, @@ -1738,6 +1803,12 @@ int gspca_dev_probe(struct usb_interface *intf, err("couldn't kzalloc gspca struct"); return -EIO; } + gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); + if (!gspca_dev->usb_buf) { + err("out of memory"); + ret = -EIO; + goto out; + } gspca_dev->dev = dev; gspca_dev->iface = interface->bInterfaceNumber; gspca_dev->nbalt = intf->num_altsetting; @@ -1745,10 +1816,13 @@ int gspca_dev_probe(struct usb_interface *intf, /* gspca_dev->users = 0; (done by kzalloc) */ gspca_dev->nbufread = 2; - /* configure the subdriver */ + /* configure the subdriver and initialize the USB device */ ret = gspca_dev->sd_desc->config(gspca_dev, id); if (ret < 0) goto out; + ret = gspca_dev->sd_desc->init(gspca_dev); + if (ret < 0) + goto out; ret = gspca_set_alt0(gspca_dev); if (ret < 0) goto out; @@ -1778,6 +1852,7 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probe ok"); return 0; out: + kfree(gspca_dev->usb_buf); kfree(gspca_dev); return ret; } @@ -1810,11 +1885,42 @@ void gspca_disconnect(struct usb_interface *intf) /* We don't want people trying to open up the device */ video_unregister_device(&gspca_dev->vdev); /* Free the memory */ + kfree(gspca_dev->usb_buf); kfree(gspca_dev); PDEBUG(D_PROBE, "disconnect complete"); } EXPORT_SYMBOL(gspca_disconnect); +#ifdef CONFIG_PM +int gspca_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + + if (!gspca_dev->streaming) + return 0; + gspca_dev->frozen = 1; /* avoid urb error messages */ + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_set_alt0(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + return 0; +} +EXPORT_SYMBOL(gspca_suspend); + +int gspca_resume(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + + gspca_dev->frozen = 0; + gspca_dev->sd_desc->init(gspca_dev); + if (gspca_dev->streaming) + return gspca_init_transfer(gspca_dev); + return 0; +} +EXPORT_SYMBOL(gspca_resume); +#endif /* -- cam driver utility functions -- */ /* auto gain and exposure algorithm based on the knee algorithm described here: diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 67e448940..78a35a88c 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -49,14 +49,13 @@ extern int gspca_debug; } while (0) #define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ -/* ISOC transfers */ -#define MAX_NURBS 16 /* max number of URBs */ +/* image transfers */ +#define MAX_NURBS 4 /* max number of URBs */ #define ISO_MAX_PKT 32 /* max number of packets in an ISOC transfer */ #define ISO_MAX_SIZE 0x8000 /* max size of one URB buffer (32 Kb) */ /* device information - set at probe time */ struct cam { - char *dev_name; struct v4l2_pix_format *cam_mode; /* size nmodes */ char nmodes; __u8 epaddr; @@ -91,15 +90,14 @@ struct sd_desc { /* controls */ const struct ctrl *ctrls; int nctrls; -/* operations */ +/* mandatory operations */ cam_cf_op config; /* called on probe */ - cam_op open; /* called on open */ + cam_op init; /* called on probe and resume */ cam_v_op start; /* called on stream on */ - cam_v_op stopN; /* called on stream off - main alt */ - cam_v_op stop0; /* called on stream off - alt 0 */ - cam_v_op close; /* called on close */ cam_pkt_op pkt_scan; /* optional operations */ + cam_v_op stopN; /* called on stream off - main alt */ + cam_v_op stop0; /* called on stream off - alt 0 */ cam_v_op dq_callback; /* called when a frame has been dequeued */ cam_jpg_op get_jcomp; cam_jpg_op set_jcomp; @@ -127,8 +125,10 @@ struct gspca_dev { struct cam cam; /* device information */ const struct sd_desc *sd_desc; /* subdriver description */ + unsigned ctrl_dis; /* disabled controls (bit map) */ - __u8 usb_buf[8]; /* buffer for USB exchanges */ +#define USB_BUF_SZ 64 + __u8 *usb_buf; /* buffer for USB exchanges */ struct urb *urb[MAX_NURBS]; __u8 *frbuf; /* buffer for nframes */ @@ -143,6 +143,7 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ + __u8 bulk; /* image transfer by isoc (0) or bulk (1) */ __u8 curr_mode; /* current camera mode */ __u32 pixfmt; /* current mode parameters */ __u16 width; @@ -155,6 +156,9 @@ struct gspca_dev { struct mutex queue_lock; /* ISOC queue protection */ __u32 sequence; /* frame sequence number */ char streaming; +#ifdef CONFIG_PM + char frozen; /* suspend - resume */ +#endif char users; /* number of opens */ char present; /* device connected */ char nbufread; /* number of buffers for read() */ @@ -174,6 +178,10 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, struct gspca_frame *frame, const __u8 *data, int len); +#ifdef CONFIG_PM +int gspca_suspend(struct usb_interface *intf, pm_message_t message); +int gspca_resume(struct usb_interface *intf); +#endif int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); #endif /* GSPCAV2_H */ diff --git a/linux/drivers/media/video/gspca/mars.c b/linux/drivers/media/video/gspca/mars.c index 21c4ee56a..4d5db47ba 100644 --- a/linux/drivers/media/video/gspca/mars.c +++ b/linux/drivers/media/video/gspca/mars.c @@ -100,22 +100,6 @@ static int reg_w(struct gspca_dev *gspca_dev, return rc; } -static int reg_w_buf(struct gspca_dev *gspca_dev, - __u16 index, __u8 *buf, int len) -{ - int rc; - - rc = usb_control_msg(gspca_dev->dev, - usb_sndbulkpipe(gspca_dev->dev, 4), - 0x12, - 0xc8, /* ?? */ - 0, /* value */ - index, buf, len, 500); - if (rc < 0) - PDEBUG(D_ERR, "reg write [%02x] error %d", index, rc); - return rc; -} - static void bulk_w(struct gspca_dev *gspca_dev, __u16 *pch, __u16 Address) @@ -144,8 +128,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { return 0; } @@ -175,7 +159,6 @@ static void sd_start(struct gspca_dev *gspca_dev) /* Initialize the MR97113 chip register */ - data = kmalloc(16, GFP_KERNEL); data[0] = 0x00; /* address */ data[1] = 0x0c | 0x01; /* reg 0 */ data[2] = 0x01; /* reg 1 */ @@ -195,12 +178,10 @@ static void sd_start(struct gspca_dev *gspca_dev) data[10] = 0x5d; /* reg 9, I2C device address * [for PAS5101 (0x40)] [for MI (0x5d)] */ - err_code = reg_w_buf(gspca_dev, data[0], data, 11); - kfree(data); + err_code = reg_w(gspca_dev, data[0], 11); if (err_code < 0) return; - data = gspca_dev->usb_buf; data[0] = 0x23; /* address */ data[1] = 0x09; /* reg 35, append frame header */ @@ -358,14 +339,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_ERR, "Camera Stop failed"); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -411,11 +384,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -439,6 +410,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/ov519.c b/linux/drivers/media/video/gspca/ov519.c index 2379433f0..bb2842114 100644 --- a/linux/drivers/media/video/gspca/ov519.c +++ b/linux/drivers/media/video/gspca/ov519.c @@ -63,11 +63,10 @@ struct sd { #define SEN_OV6630 2 #define SEN_OV7610 3 #define SEN_OV7620 4 -#define SEN_OV7630 5 -#define SEN_OV7640 6 -#define SEN_OV7670 7 -#define SEN_OV76BE 8 -#define SEN_OV8610 9 +#define SEN_OV7640 5 +#define SEN_OV7670 6 +#define SEN_OV76BE 7 +#define SEN_OV8610 8 }; @@ -127,6 +126,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getcolors, }, /* next controls work with ov7670 only */ +#define HFLIP_IDX 3 { { .id = V4L2_CID_HFLIP, @@ -141,6 +141,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_sethflip, .get = sd_gethflip, }, +#define VFLIP_IDX 4 { { .id = V4L2_CID_VFLIP, @@ -293,6 +294,541 @@ static struct v4l2_pix_format sif_mode[] = { #define OV7670_REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ #define OV7670_REG_BD60MAX 0xab /* 60hz banding step limit */ +struct ov_regvals { + __u8 reg; + __u8 val; +}; +struct ov_i2c_regvals { + __u8 reg; + __u8 val; +}; + +static const struct ov_i2c_regvals norm_6x20[] = { + { 0x12, 0x80 }, /* reset */ + { 0x11, 0x01 }, + { 0x03, 0x60 }, + { 0x05, 0x7f }, /* For when autoadjust is off */ + { 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x0f, 0x15 }, /* COMS */ + { 0x10, 0x75 }, /* AEC Exposure time */ + { 0x12, 0x24 }, /* Enable AGC */ + { 0x14, 0x04 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + { 0x16, 0x06 }, +/* { 0x20, 0x30 }, * Aperture correction enable */ + { 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { 0x28, 0x05 }, + { 0x2a, 0x04 }, /* Disable framerate adjust */ +/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ + { 0x2d, 0x99 }, + { 0x33, 0xa0 }, /* Color Processing Parameter */ + { 0x34, 0xd2 }, /* Max A/D range */ + { 0x38, 0x8b }, + { 0x39, 0x40 }, + + { 0x3c, 0x39 }, /* Enable AEC mode changing */ + { 0x3c, 0x3c }, /* Change AEC mode */ + { 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. + * They control the color balance */ + { 0x4a, 0x80 }, + { 0x4b, 0x80 }, + { 0x4d, 0xd2 }, /* This reduces noise a bit */ + { 0x4e, 0xc1 }, + { 0x4f, 0x04 }, +/* Do 50-53 have any effect? */ +/* Toggle 0x12[2] off and on here? */ +}; + +static const struct ov_i2c_regvals norm_6x30[] = { + { 0x12, 0x80 }, /* Reset */ + { 0x00, 0x1f }, /* Gain */ + { 0x01, 0x99 }, /* Blue gain */ + { 0x02, 0x7c }, /* Red gain */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x0a }, /* Contrast */ + { 0x06, 0x95 }, /* Brightness */ + { 0x07, 0x2d }, /* Sharpness */ + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x0e, 0x20 }, + { 0x0f, 0x05 }, + { 0x10, 0x9a }, + { 0x11, 0x00 }, /* Pixel clock = fastest */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x21 }, + { 0x14, 0x80 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x38 }, + { 0x18, 0xea }, + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, + { 0x1e, 0xc4 }, + { 0x1f, 0x04 }, + { 0x20, 0x20 }, + { 0x21, 0x10 }, + { 0x22, 0x88 }, + { 0x23, 0xc0 }, /* Crystal circuit power level */ + { 0x25, 0x9a }, /* Increase AEC black ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x84 }, /* 60 Hz power */ + { 0x2b, 0xa8 }, /* 60 Hz power */ + { 0x2c, 0xa0 }, + { 0x2d, 0x95 }, /* Enable auto-brightness */ + { 0x2e, 0x88 }, + { 0x33, 0x26 }, + { 0x34, 0x03 }, + { 0x36, 0x8f }, + { 0x37, 0x80 }, + { 0x38, 0x83 }, + { 0x39, 0x80 }, + { 0x3a, 0x0f }, + { 0x3b, 0x3c }, + { 0x3c, 0x1a }, + { 0x3d, 0x80 }, + { 0x3e, 0x80 }, + { 0x3f, 0x0e }, + { 0x40, 0x00 }, /* White bal */ + { 0x41, 0x00 }, /* White bal */ + { 0x42, 0x80 }, + { 0x43, 0x3f }, /* White bal */ + { 0x44, 0x80 }, + { 0x45, 0x20 }, + { 0x46, 0x20 }, + { 0x47, 0x80 }, + { 0x48, 0x7f }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x80 }, + { 0x4c, 0xd0 }, + { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { 0x4e, 0x40 }, + { 0x4f, 0x07 }, /* UV avg., col. killer: max */ + { 0x50, 0xff }, + { 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { 0x55, 0xff }, + { 0x56, 0x12 }, + { 0x57, 0x81 }, + { 0x58, 0x75 }, + { 0x59, 0x01 }, /* AGC dark current comp.: +1 */ + { 0x5a, 0x2c }, + { 0x5b, 0x0f }, /* AWB chrominance levels */ + { 0x5c, 0x10 }, + { 0x3d, 0x80 }, + { 0x27, 0xa6 }, + { 0x12, 0x20 }, /* Toggle AWB */ + { 0x12, 0x24 }, +}; + +/* Lawrence Glaister <lg@jfm.bc.ca> reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ +static const struct ov_i2c_regvals norm_7610[] = { + { 0x10, 0xff }, + { 0x16, 0x06 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, +}; + +static const struct ov_i2c_regvals norm_7620[] = { + { 0x00, 0x00 }, /* gain */ + { 0x01, 0x80 }, /* blue gain */ + { 0x02, 0x80 }, /* red gain */ + { 0x03, 0xc0 }, /* OV7670_REG_VREF */ + { 0x06, 0x60 }, + { 0x07, 0x00 }, + { 0x0c, 0x24 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x11, 0x01 }, + { 0x12, 0x24 }, + { 0x13, 0x01 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x18 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x20 }, + { 0x29, 0x00 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x91 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x27 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x02 }, + { 0x6d, 0x44 }, + { 0x6e, 0x80 }, + { 0x6f, 0x1d }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe2 }, + { 0x7c, 0x00 }, +}; + +/* 7640 and 7648. The defaults should be OK for most registers. */ +static const struct ov_i2c_regvals norm_7640[] = { + { 0x12, 0x80 }, + { 0x12, 0x14 }, +}; + +/* 7670. Defaults taken from OmniVision provided data, +* as provided by Jonathan Corbet of OLPC */ +static const struct ov_i2c_regvals norm_7670[] = { + { OV7670_REG_COM7, OV7670_COM7_RESET }, + { OV7670_REG_TSLB, 0x04 }, /* OV */ + { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */ + { OV7670_REG_CLKRC, 0x01 }, +/* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { OV7670_REG_HSTART, 0x13 }, + { OV7670_REG_HSTOP, 0x01 }, + { OV7670_REG_HREF, 0xb6 }, + { OV7670_REG_VSTART, 0x02 }, + { OV7670_REG_VSTOP, 0x7a }, + { OV7670_REG_VREF, 0x0a }, + + { OV7670_REG_COM3, 0 }, + { OV7670_REG_COM14, 0 }, +/* Mystery scaling numbers */ + { 0x70, 0x3a }, + { 0x71, 0x35 }, + { 0x72, 0x11 }, + { 0x73, 0xf0 }, + { 0xa2, 0x02 }, +/* { OV7670_REG_COM10, 0x0 }, */ + +/* Gamma curve values */ + { 0x7a, 0x20 }, + { 0x7b, 0x10 }, + { 0x7c, 0x1e }, + { 0x7d, 0x35 }, + { 0x7e, 0x5a }, + { 0x7f, 0x69 }, + { 0x80, 0x76 }, + { 0x81, 0x80 }, + { 0x82, 0x88 }, + { 0x83, 0x8f }, + { 0x84, 0x96 }, + { 0x85, 0xa3 }, + { 0x86, 0xaf }, + { 0x87, 0xc4 }, + { 0x88, 0xd7 }, + { 0x89, 0xe8 }, + +/* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT }, + { OV7670_REG_GAIN, 0 }, + { OV7670_REG_AECH, 0 }, + { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */ + { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { OV7670_REG_BD50MAX, 0x05 }, + { OV7670_REG_BD60MAX, 0x07 }, + { OV7670_REG_AEW, 0x95 }, + { OV7670_REG_AEB, 0x33 }, + { OV7670_REG_VPT, 0xe3 }, + { OV7670_REG_HAECC1, 0x78 }, + { OV7670_REG_HAECC2, 0x68 }, + { 0xa1, 0x03 }, /* magic */ + { OV7670_REG_HAECC3, 0xd8 }, + { OV7670_REG_HAECC4, 0xd8 }, + { OV7670_REG_HAECC5, 0xf0 }, + { OV7670_REG_HAECC6, 0x90 }, + { OV7670_REG_HAECC7, 0x94 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC }, + +/* Almost all of these are magic "reserved" values. */ + { OV7670_REG_COM5, 0x61 }, + { OV7670_REG_COM6, 0x4b }, + { 0x16, 0x02 }, + { OV7670_REG_MVFP, 0x07 }, + { 0x21, 0x02 }, + { 0x22, 0x91 }, + { 0x29, 0x07 }, + { 0x33, 0x0b }, + { 0x35, 0x0b }, + { 0x37, 0x1d }, + { 0x38, 0x71 }, + { 0x39, 0x2a }, + { OV7670_REG_COM12, 0x78 }, + { 0x4d, 0x40 }, + { 0x4e, 0x20 }, + { OV7670_REG_GFIX, 0 }, + { 0x6b, 0x4a }, + { 0x74, 0x10 }, + { 0x8d, 0x4f }, + { 0x8e, 0 }, + { 0x8f, 0 }, + { 0x90, 0 }, + { 0x91, 0 }, + { 0x96, 0 }, + { 0x9a, 0 }, + { 0xb0, 0x84 }, + { 0xb1, 0x0c }, + { 0xb2, 0x0e }, + { 0xb3, 0x82 }, + { 0xb8, 0x0a }, + +/* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, + { 0x44, 0xf0 }, + { 0x45, 0x34 }, + { 0x46, 0x58 }, + { 0x47, 0x28 }, + { 0x48, 0x3a }, + { 0x59, 0x88 }, + { 0x5a, 0x88 }, + { 0x5b, 0x44 }, + { 0x5c, 0x67 }, + { 0x5d, 0x49 }, + { 0x5e, 0x0e }, + { 0x6c, 0x0a }, + { 0x6d, 0x55 }, + { 0x6e, 0x11 }, + { 0x6f, 0x9f }, + /* "9e for advance AWB" */ + { 0x6a, 0x40 }, + { OV7670_REG_BLUE, 0x40 }, + { OV7670_REG_RED, 0x60 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC + | OV7670_COM8_AWB }, + +/* Matrix coefficients */ + { 0x4f, 0x80 }, + { 0x50, 0x80 }, + { 0x51, 0 }, + { 0x52, 0x22 }, + { 0x53, 0x5e }, + { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { OV7670_REG_COM16, OV7670_COM16_AWBGAIN }, + { OV7670_REG_EDGE, 0 }, + { 0x75, 0x05 }, + { 0x76, 0xe1 }, + { 0x4c, 0 }, + { 0x77, 0x01 }, + { OV7670_REG_COM13, OV7670_COM13_GAMMA + | OV7670_COM13_UVSAT + | 2}, /* was 3 */ + { 0x4b, 0x09 }, + { 0xc9, 0x60 }, + { OV7670_REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, + { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, + { 0xa4, 0x88 }, + { 0x96, 0 }, + { 0x97, 0x30 }, + { 0x98, 0x20 }, + { 0x99, 0x30 }, + { 0x9a, 0x84 }, + { 0x9b, 0x29 }, + { 0x9c, 0x03 }, + { 0x9d, 0x4c }, + { 0x9e, 0x3f }, + { 0x78, 0x04 }, + +/* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, + { 0xc8, 0xf0 }, + { 0x79, 0x0f }, + { 0xc8, 0x00 }, + { 0x79, 0x10 }, + { 0xc8, 0x7e }, + { 0x79, 0x0a }, + { 0xc8, 0x80 }, + { 0x79, 0x0b }, + { 0xc8, 0x01 }, + { 0x79, 0x0c }, + { 0xc8, 0x0f }, + { 0x79, 0x0d }, + { 0xc8, 0x20 }, + { 0x79, 0x09 }, + { 0xc8, 0x80 }, + { 0x79, 0x02 }, + { 0xc8, 0xc0 }, + { 0x79, 0x03 }, + { 0xc8, 0x40 }, + { 0x79, 0x05 }, + { 0xc8, 0x30 }, + { 0x79, 0x26 }, +}; + +static const struct ov_i2c_regvals norm_8610[] = { + { 0x12, 0x80 }, + { 0x00, 0x00 }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, 0xc0 }, + { 0x04, 0x30 }, + { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ + { 0x0a, 0x86 }, + { 0x0b, 0xb0 }, + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x11, 0x01 }, + { 0x12, 0x25 }, + { 0x13, 0x01 }, + { 0x14, 0x04 }, + { 0x15, 0x01 }, /* Lin and Win think different about UV order */ + { 0x16, 0x03 }, + { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ + { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ + { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ + { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ + { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ + { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x80 }, + { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ + { 0x2c, 0xac }, + { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ + { 0x2e, 0x80 }, + { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ + { 0x4c, 0x00 }, + { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ + { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ + { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ + { 0x63, 0xff }, + { 0x64, 0x53 }, /* new windrv 090403 says 0x57, + * maybe thats wrong */ + { 0x65, 0x00 }, + { 0x66, 0x55 }, + { 0x67, 0xb0 }, + { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ + { 0x69, 0x02 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but + * deleting bit7 colors the first images red */ + { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6f, 0x01 }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */ + { 0x75, 0x0e }, + { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ + { 0x7c, 0x00 }, + { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ + { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ + { 0x7f, 0xfb }, + { 0x80, 0x28 }, + { 0x81, 0x00 }, + { 0x82, 0x23 }, + { 0x83, 0x0b }, + { 0x84, 0x00 }, + { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ + { 0x86, 0xc9 }, + { 0x87, 0x00 }, + { 0x88, 0x00 }, + { 0x89, 0x01 }, + { 0x12, 0x20 }, + { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ +}; + static unsigned char ov7670_abs_to_sm(unsigned char v) { if (v > 127) @@ -537,18 +1073,10 @@ static int ov51x_set_slave_ids(struct sd *sd, rc = reg_w(sd, R51x_I2C_W_SID, slave); if (rc < 0) return rc; + sd->primary_i2c_slave = slave; return reg_w(sd, R51x_I2C_R_SID, slave + 1); } -struct ov_regvals { - __u8 reg; - __u8 val; -}; -struct ov_i2c_regvals { - __u8 reg; - __u8 val; -}; - static int write_regvals(struct sd *sd, const struct ov_regvals *regvals, int n) @@ -591,101 +1119,9 @@ static int write_i2c_regvals(struct sd *sd, static int ov8xx0_configure(struct sd *sd) { int rc; - static const struct ov_i2c_regvals norm_8610[] = { - { 0x12, 0x80 }, - { 0x00, 0x00 }, - { 0x01, 0x80 }, - { 0x02, 0x80 }, - { 0x03, 0xc0 }, - { 0x04, 0x30 }, - { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ - { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ - { 0x0a, 0x86 }, - { 0x0b, 0xb0 }, - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x11, 0x01 }, - { 0x12, 0x25 }, - { 0x13, 0x01 }, - { 0x14, 0x04 }, - { 0x15, 0x01 }, /* Lin and Win think different about UV order */ - { 0x16, 0x03 }, - { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ - { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ - { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ - { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ - { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ - { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ - { 0x26, 0xa2 }, - { 0x27, 0xea }, - { 0x28, 0x00 }, - { 0x29, 0x00 }, - { 0x2a, 0x80 }, - { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ - { 0x2c, 0xac }, - { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ - { 0x2e, 0x80 }, - { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ - { 0x4c, 0x00 }, - { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ - { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ - { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ - { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ - { 0x63, 0xff }, - { 0x64, 0x53 }, /* new windrv 090403 says 0x57, - * maybe thats wrong */ - { 0x65, 0x00 }, - { 0x66, 0x55 }, - { 0x67, 0xb0 }, - { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ - { 0x69, 0x02 }, - { 0x6a, 0x22 }, - { 0x6b, 0x00 }, - { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but - deleting bit7 colors the first images red */ - { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ - { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ - { 0x6f, 0x01 }, - { 0x70, 0x8b }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */ - { 0x75, 0x0e }, - { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ - { 0x7c, 0x00 }, - { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ - { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ - { 0x7f, 0xfb }, - { 0x80, 0x28 }, - { 0x81, 0x00 }, - { 0x82, 0x23 }, - { 0x83, 0x0b }, - { 0x84, 0x00 }, - { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ - { 0x86, 0xc9 }, - { 0x87, 0x00 }, - { 0x88, 0x00 }, - { 0x89, 0x01 }, - { 0x12, 0x20 }, - { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ - }; PDEBUG(D_PROBE, "starting ov8xx0 configuration"); - if (init_ov_sensor(sd) < 0) - PDEBUG(D_ERR|D_PROBE, "Failed to read sensor ID"); - else - PDEBUG(D_PROBE, "OV86x0 initialized"); - /* Detect sensor (sub)type */ rc = i2c_r(sd, OV7610_REG_COM_I); if (rc < 0) { @@ -698,9 +1134,6 @@ static int ov8xx0_configure(struct sd *sd) PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3); return -1; } - PDEBUG(D_PROBE, "Writing 8610 registers"); - if (write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610))) - return -1; /* Set sensor-specific vars */ /* sd->sif = 0; already done */ @@ -714,252 +1147,6 @@ static int ov7xx0_configure(struct sd *sd) { int rc, high, low; - /* Lawrence Glaister <lg@jfm.bc.ca> reports: - * - * Register 0x0f in the 7610 has the following effects: - * - * 0x85 (AEC method 1): Best overall, good contrast range - * 0x45 (AEC method 2): Very overexposed - * 0xa5 (spec sheet default): Ok, but the black level is - * shifted resulting in loss of contrast - * 0x05 (old driver setting): very overexposed, too much - * contrast - */ - static const struct ov_i2c_regvals norm_7610[] = { - { 0x10, 0xff }, - { 0x16, 0x06 }, - { 0x28, 0x24 }, - { 0x2b, 0xac }, - { 0x12, 0x00 }, - { 0x38, 0x81 }, - { 0x28, 0x24 }, /* 0c */ - { 0x0f, 0x85 }, /* lg's setting */ - { 0x15, 0x01 }, - { 0x20, 0x1c }, - { 0x23, 0x2a }, - { 0x24, 0x10 }, - { 0x25, 0x8a }, - { 0x26, 0xa2 }, - { 0x27, 0xc2 }, - { 0x2a, 0x04 }, - { 0x2c, 0xfe }, - { 0x2d, 0x93 }, - { 0x30, 0x71 }, - { 0x31, 0x60 }, - { 0x32, 0x26 }, - { 0x33, 0x20 }, - { 0x34, 0x48 }, - { 0x12, 0x24 }, - { 0x11, 0x01 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - }; - - static const struct ov_i2c_regvals norm_7620[] = { - { 0x00, 0x00 }, /* gain */ - { 0x01, 0x80 }, /* blue gain */ - { 0x02, 0x80 }, /* red gain */ - { 0x03, 0xc0 }, /* OV7670_REG_VREF */ - { 0x06, 0x60 }, - { 0x07, 0x00 }, - { 0x0c, 0x24 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x11, 0x01 }, - { 0x12, 0x24 }, - { 0x13, 0x01 }, - { 0x14, 0x84 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x2f }, - { 0x18, 0xcf }, - { 0x19, 0x06 }, - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0x18 }, - { 0x21, 0x80 }, - { 0x22, 0x80 }, - { 0x23, 0x00 }, - { 0x26, 0xa2 }, - { 0x27, 0xea }, - { 0x28, 0x20 }, - { 0x29, 0x00 }, - { 0x2a, 0x10 }, - { 0x2b, 0x00 }, - { 0x2c, 0x88 }, - { 0x2d, 0x91 }, - { 0x2e, 0x80 }, - { 0x2f, 0x44 }, - { 0x60, 0x27 }, - { 0x61, 0x02 }, - { 0x62, 0x5f }, - { 0x63, 0xd5 }, - { 0x64, 0x57 }, - { 0x65, 0x83 }, - { 0x66, 0x55 }, - { 0x67, 0x92 }, - { 0x68, 0xcf }, - { 0x69, 0x76 }, - { 0x6a, 0x22 }, - { 0x6b, 0x00 }, - { 0x6c, 0x02 }, - { 0x6d, 0x44 }, - { 0x6e, 0x80 }, - { 0x6f, 0x1d }, - { 0x70, 0x8b }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0x00 }, - { 0x75, 0x8e }, - { 0x76, 0x00 }, - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0xe2 }, - { 0x7c, 0x00 }, - }; - - /* 7640 and 7648. The defaults should be OK for most registers. */ - static const struct ov_i2c_regvals norm_7640[] = { - { 0x12, 0x80 }, - { 0x12, 0x14 }, - }; - - /* 7670. Defaults taken from OmniVision provided data, - * as provided by Jonathan Corbet of OLPC */ - static const struct ov_i2c_regvals norm_7670[] = { - { OV7670_REG_COM7, OV7670_COM7_RESET }, - { OV7670_REG_TSLB, 0x04 }, /* OV */ - { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */ - { OV7670_REG_CLKRC, 0x01 }, - /* - * Set the hardware window. These values from OV don't entirely - * make sense - hstop is less than hstart. But they work... - */ - { OV7670_REG_HSTART, 0x13 }, { OV7670_REG_HSTOP, 0x01 }, - { OV7670_REG_HREF, 0xb6 }, { OV7670_REG_VSTART, 0x02 }, - { OV7670_REG_VSTOP, 0x7a }, { OV7670_REG_VREF, 0x0a }, - - { OV7670_REG_COM3, 0 }, { OV7670_REG_COM14, 0 }, - /* Mystery scaling numbers */ - { 0x70, 0x3a }, { 0x71, 0x35 }, - { 0x72, 0x11 }, { 0x73, 0xf0 }, - { 0xa2, 0x02 }, -/* { OV7670_REG_COM10, 0x0 }, */ - - /* Gamma curve values */ - { 0x7a, 0x20 }, - { 0x7b, 0x10 }, - { 0x7c, 0x1e }, - { 0x7d, 0x35 }, - { 0x7e, 0x5a }, { 0x7f, 0x69 }, - { 0x80, 0x76 }, { 0x81, 0x80 }, - { 0x82, 0x88 }, { 0x83, 0x8f }, - { 0x84, 0x96 }, { 0x85, 0xa3 }, - { 0x86, 0xaf }, { 0x87, 0xc4 }, - { 0x88, 0xd7 }, { 0x89, 0xe8 }, - - /* AGC and AEC parameters. Note we start by disabling those features, - then turn them only after tweaking the values. */ - { OV7670_REG_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT }, - { OV7670_REG_GAIN, 0 }, { OV7670_REG_AECH, 0 }, - { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */ - { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ - { OV7670_REG_BD50MAX, 0x05 }, { OV7670_REG_BD60MAX, 0x07 }, - { OV7670_REG_AEW, 0x95 }, { OV7670_REG_AEB, 0x33 }, - { OV7670_REG_VPT, 0xe3 }, { OV7670_REG_HAECC1, 0x78 }, - { OV7670_REG_HAECC2, 0x68 }, - { 0xa1, 0x03 }, /* magic */ - { OV7670_REG_HAECC3, 0xd8 }, { OV7670_REG_HAECC4, 0xd8 }, - { OV7670_REG_HAECC5, 0xf0 }, { OV7670_REG_HAECC6, 0x90 }, - { OV7670_REG_HAECC7, 0x94 }, - { OV7670_REG_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT - | OV7670_COM8_AGC - | OV7670_COM8_AEC }, - - /* Almost all of these are magic "reserved" values. */ - { OV7670_REG_COM5, 0x61 }, { OV7670_REG_COM6, 0x4b }, - { 0x16, 0x02 }, - { OV7670_REG_MVFP, 0x07 }, - { 0x21, 0x02 }, { 0x22, 0x91 }, - { 0x29, 0x07 }, { 0x33, 0x0b }, - { 0x35, 0x0b }, { 0x37, 0x1d }, - { 0x38, 0x71 }, { 0x39, 0x2a }, - { OV7670_REG_COM12, 0x78 }, { 0x4d, 0x40 }, - { 0x4e, 0x20 }, { OV7670_REG_GFIX, 0 }, - { 0x6b, 0x4a }, { 0x74, 0x10 }, - { 0x8d, 0x4f }, { 0x8e, 0 }, - { 0x8f, 0 }, { 0x90, 0 }, - { 0x91, 0 }, { 0x96, 0 }, - { 0x9a, 0 }, { 0xb0, 0x84 }, - { 0xb1, 0x0c }, { 0xb2, 0x0e }, - { 0xb3, 0x82 }, { 0xb8, 0x0a }, - - /* More reserved magic, some of which tweaks white balance */ - { 0x43, 0x0a }, { 0x44, 0xf0 }, - { 0x45, 0x34 }, { 0x46, 0x58 }, - { 0x47, 0x28 }, { 0x48, 0x3a }, - { 0x59, 0x88 }, { 0x5a, 0x88 }, - { 0x5b, 0x44 }, { 0x5c, 0x67 }, - { 0x5d, 0x49 }, { 0x5e, 0x0e }, - { 0x6c, 0x0a }, { 0x6d, 0x55 }, - { 0x6e, 0x11 }, { 0x6f, 0x9f }, - /* "9e for advance AWB" */ - { 0x6a, 0x40 }, { OV7670_REG_BLUE, 0x40 }, - { OV7670_REG_RED, 0x60 }, - { OV7670_REG_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT - | OV7670_COM8_AGC - | OV7670_COM8_AEC - | OV7670_COM8_AWB }, - - /* Matrix coefficients */ - { 0x4f, 0x80 }, { 0x50, 0x80 }, - { 0x51, 0 }, { 0x52, 0x22 }, - { 0x53, 0x5e }, { 0x54, 0x80 }, - { 0x58, 0x9e }, - - { OV7670_REG_COM16, OV7670_COM16_AWBGAIN }, - { OV7670_REG_EDGE, 0 }, - { 0x75, 0x05 }, { 0x76, 0xe1 }, - { 0x4c, 0 }, { 0x77, 0x01 }, - { OV7670_REG_COM13, OV7670_COM13_GAMMA - | OV7670_COM13_UVSAT - | 2}, /* was 3 */ - { 0x4b, 0x09 }, - { 0xc9, 0x60 }, { OV7670_REG_COM16, 0x38 }, - { 0x56, 0x40 }, - - { 0x34, 0x11 }, - { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, - { 0xa4, 0x88 }, { 0x96, 0 }, - { 0x97, 0x30 }, { 0x98, 0x20 }, - { 0x99, 0x30 }, { 0x9a, 0x84 }, - { 0x9b, 0x29 }, { 0x9c, 0x03 }, - { 0x9d, 0x4c }, { 0x9e, 0x3f }, - { 0x78, 0x04 }, - - /* Extra-weird stuff. Some sort of multiplexor register */ - { 0x79, 0x01 }, { 0xc8, 0xf0 }, - { 0x79, 0x0f }, { 0xc8, 0x00 }, - { 0x79, 0x10 }, { 0xc8, 0x7e }, - { 0x79, 0x0a }, { 0xc8, 0x80 }, - { 0x79, 0x0b }, { 0xc8, 0x01 }, - { 0x79, 0x0c }, { 0xc8, 0x0f }, - { 0x79, 0x0d }, { 0xc8, 0x20 }, - { 0x79, 0x09 }, { 0xc8, 0x80 }, - { 0x79, 0x02 }, { 0xc8, 0xc0 }, - { 0x79, 0x03 }, { 0xc8, 0x40 }, - { 0x79, 0x05 }, { 0xc8, 0x30 }, - { 0x79, 0x26 }, - }; PDEBUG(D_PROBE, "starting OV7xx0 configuration"); @@ -1011,8 +1198,9 @@ static int ov7xx0_configure(struct sd *sd) switch (low) { case 0x30: PDEBUG(D_PROBE, "Sensor is an OV7630/OV7635"); - sd->sensor = SEN_OV7630; - break; + PDEBUG(D_ERR, + "7630 is not supported by this driver"); + return -1; case 0x40: PDEBUG(D_PROBE, "Sensor is an OV7645"); sd->sensor = SEN_OV7640; /* FIXME */ @@ -1038,32 +1226,6 @@ static int ov7xx0_configure(struct sd *sd) return -1; } - switch (sd->sensor) { - case SEN_OV7620: - PDEBUG(D_PROBE, "Writing 7620 registers"); - if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620))) - return -1; - break; - case SEN_OV7630: - PDEBUG(D_ERR, "7630 is not supported by this driver version"); - return -1; - case SEN_OV7640: - PDEBUG(D_PROBE, "Writing 7640 registers"); - if (write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640))) - return -1; - break; - case SEN_OV7670: - PDEBUG(D_PROBE, "Writing 7670 registers"); - if (write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670))) - return -1; - break; - default: - PDEBUG(D_PROBE, "Writing 7610 registers"); - if (write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610))) - return -1; - break; - } - /* Set sensor-specific vars */ /* sd->sif = 0; already done */ return 0; @@ -1073,141 +1235,7 @@ static int ov7xx0_configure(struct sd *sd) static int ov6xx0_configure(struct sd *sd) { int rc; - static const struct ov_i2c_regvals norm_6x20[] = { - { 0x12, 0x80 }, /* reset */ - { 0x11, 0x01 }, - { 0x03, 0x60 }, - { 0x05, 0x7f }, /* For when autoadjust is off */ - { 0x07, 0xa8 }, - /* The ratio of 0x0c and 0x0d controls the white point */ - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x0f, 0x15 }, /* COMS */ - { 0x10, 0x75 }, /* AEC Exposure time */ - { 0x12, 0x24 }, /* Enable AGC */ - { 0x14, 0x04 }, - /* 0x16: 0x06 helps frame stability with moving objects */ - { 0x16, 0x06 }, -/* { 0x20, 0x30 }, * Aperture correction enable */ - { 0x26, 0xb2 }, /* BLC enable */ - /* 0x28: 0x05 Selects RGB format if RGB on */ - { 0x28, 0x05 }, - { 0x2a, 0x04 }, /* Disable framerate adjust */ -/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ - { 0x2d, 0x99 }, - { 0x33, 0xa0 }, /* Color Processing Parameter */ - { 0x34, 0xd2 }, /* Max A/D range */ - { 0x38, 0x8b }, - { 0x39, 0x40 }, - - { 0x3c, 0x39 }, /* Enable AEC mode changing */ - { 0x3c, 0x3c }, /* Change AEC mode */ - { 0x3c, 0x24 }, /* Disable AEC mode changing */ - - { 0x3d, 0x80 }, - /* These next two registers (0x4a, 0x4b) are undocumented. - * They control the color balance */ - { 0x4a, 0x80 }, - { 0x4b, 0x80 }, - { 0x4d, 0xd2 }, /* This reduces noise a bit */ - { 0x4e, 0xc1 }, - { 0x4f, 0x04 }, -/* Do 50-53 have any effect? */ -/* Toggle 0x12[2] off and on here? */ - }; - - static const struct ov_i2c_regvals norm_6x30[] = { - { 0x12, 0x80 }, /* Reset */ - { 0x00, 0x1f }, /* Gain */ - { 0x01, 0x99 }, /* Blue gain */ - { 0x02, 0x7c }, /* Red gain */ - { 0x03, 0xc0 }, /* Saturation */ - { 0x05, 0x0a }, /* Contrast */ - { 0x06, 0x95 }, /* Brightness */ - { 0x07, 0x2d }, /* Sharpness */ - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x0e, 0x20 }, - { 0x0f, 0x05 }, - { 0x10, 0x9a }, - { 0x11, 0x00 }, /* Pixel clock = fastest */ - { 0x12, 0x24 }, /* Enable AGC and AWB */ - { 0x13, 0x21 }, - { 0x14, 0x80 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x38 }, - { 0x18, 0xea }, - { 0x19, 0x04 }, - { 0x1a, 0x93 }, - { 0x1b, 0x00 }, - { 0x1e, 0xc4 }, - { 0x1f, 0x04 }, - { 0x20, 0x20 }, - { 0x21, 0x10 }, - { 0x22, 0x88 }, - { 0x23, 0xc0 }, /* Crystal circuit power level */ - { 0x25, 0x9a }, /* Increase AEC black ratio */ - { 0x26, 0xb2 }, /* BLC enable */ - { 0x27, 0xa2 }, - { 0x28, 0x00 }, - { 0x29, 0x00 }, - { 0x2a, 0x84 }, /* 60 Hz power */ - { 0x2b, 0xa8 }, /* 60 Hz power */ - { 0x2c, 0xa0 }, - { 0x2d, 0x95 }, /* Enable auto-brightness */ - { 0x2e, 0x88 }, - { 0x33, 0x26 }, - { 0x34, 0x03 }, - { 0x36, 0x8f }, - { 0x37, 0x80 }, - { 0x38, 0x83 }, - { 0x39, 0x80 }, - { 0x3a, 0x0f }, - { 0x3b, 0x3c }, - { 0x3c, 0x1a }, - { 0x3d, 0x80 }, - { 0x3e, 0x80 }, - { 0x3f, 0x0e }, - { 0x40, 0x00 }, /* White bal */ - { 0x41, 0x00 }, /* White bal */ - { 0x42, 0x80 }, - { 0x43, 0x3f }, /* White bal */ - { 0x44, 0x80 }, - { 0x45, 0x20 }, - { 0x46, 0x20 }, - { 0x47, 0x80 }, - { 0x48, 0x7f }, - { 0x49, 0x00 }, - { 0x4a, 0x00 }, - { 0x4b, 0x80 }, - { 0x4c, 0xd0 }, - { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ - { 0x4e, 0x40 }, - { 0x4f, 0x07 }, /* UV avg., col. killer: max */ - { 0x50, 0xff }, - { 0x54, 0x23 }, /* Max AGC gain: 18dB */ - { 0x55, 0xff }, - { 0x56, 0x12 }, - { 0x57, 0x81 }, - { 0x58, 0x75 }, - { 0x59, 0x01 }, /* AGC dark current comp.: +1 */ - { 0x5a, 0x2c }, - { 0x5b, 0x0f }, /* AWB chrominance levels */ - { 0x5c, 0x10 }, - { 0x3d, 0x80 }, - { 0x27, 0xa6 }, - { 0x12, 0x20 }, /* Toggle AWB */ - { 0x12, 0x24 }, - }; - - PDEBUG(D_PROBE, "starting sensor configuration"); - - if (init_ov_sensor(sd) < 0) { - PDEBUG(D_ERR, "Failed to read sensor ID."); - return -1; - } - PDEBUG(D_PROBE, "OV6xx0 sensor detected"); + PDEBUG(D_PROBE, "starting OV6xx0 configuration"); /* Detect sensor (sub)type */ rc = i2c_r(sd, OV7610_REG_COM_I); @@ -1251,15 +1279,6 @@ static int ov6xx0_configure(struct sd *sd) /* Set sensor-specific vars */ sd->sif = 1; - if (sd->sensor == SEN_OV6620) { - PDEBUG(D_PROBE, "Writing 6x20 registers"); - if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20))) - return -1; - } else { - PDEBUG(D_PROBE, "Writing 6x30 registers"); - if (write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30))) - return -1; - } return 0; } @@ -1298,22 +1317,31 @@ static int sd_config(struct gspca_dev *gspca_dev, ov51x_led_control(sd, 0); /* turn LED off */ /* Test for 76xx */ - sd->primary_i2c_slave = OV7xx0_SID; if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0) goto error; /* The OV519 must be more aggressive about sensor detection since * I2C write will never fail if the sensor is not present. We have * to try to initialize the sensor to detect its presence */ - if (init_ov_sensor(sd) < 0) { + if (init_ov_sensor(sd) >= 0) { + if (ov7xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV7xx0"); + goto error; + } + } else { + /* Test for 6xx0 */ - sd->primary_i2c_slave = OV6xx0_SID; if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0) goto error; - if (init_ov_sensor(sd) < 0) { + if (init_ov_sensor(sd) >= 0) { + if (ov6xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV6xx0"); + goto error; + } + } else { + /* Test for 8xx0 */ - sd->primary_i2c_slave = OV8xx0_SID; if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0) goto error; @@ -1321,24 +1349,13 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_ERR, "Can't determine sensor slave IDs"); goto error; - } else { - if (ov8xx0_configure(sd) < 0) { - PDEBUG(D_ERR, - "Failed to configure OV8xx0 sensor"); - goto error; - } } - } else { - if (ov6xx0_configure(sd) < 0) { - PDEBUG(D_ERR, "Failed to configure OV6xx0"); + if (ov8xx0_configure(sd) < 0) { + PDEBUG(D_ERR, + "Failed to configure OV8xx0 sensor"); goto error; } } - } else { - if (ov7xx0_configure(sd) < 0) { - PDEBUG(D_ERR, "Failed to configure OV7xx0"); - goto error; - } } cam = &gspca_dev->cam; @@ -1355,15 +1372,53 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->colors = COLOR_DEF; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; + if (sd->sensor != SEN_OV7670) + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) + | (1 << VFLIP_IDX); return 0; error: PDEBUG(D_ERR, "OV519 Config failed"); return -EBUSY; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + /* initialize the sensor */ + switch (sd->sensor) { + case SEN_OV6620: + if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20))) + return -EIO; + break; + case SEN_OV6630: + if (write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30))) + return -EIO; + break; + default: +/* case SEN_OV7610: */ +/* case SEN_OV76BE: */ + if (write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610))) + return -EIO; + break; + case SEN_OV7620: + if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620))) + return -EIO; + break; + case SEN_OV7640: + if (write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640))) + return -EIO; + break; + case SEN_OV7670: + if (write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670))) + return -EIO; + break; + case SEN_OV8610: + if (write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610))) + return -EIO; + break; + } return 0; } @@ -1852,14 +1907,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ov51x_led_control((struct sd *) gspca_dev, 0); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -2116,11 +2163,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -2157,6 +2202,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/pac207.c b/linux/drivers/media/video/gspca/pac207.c index 7ef18d578..34e923d00 100644 --- a/linux/drivers/media/video/gspca/pac207.c +++ b/linux/drivers/media/video/gspca/pac207.c @@ -56,12 +56,6 @@ MODULE_LICENSE("GPL"); #define PAC207_GAIN_KNEE 20 #define PAC207_AUTOGAIN_DEADZONE 30 -/* We calculating the autogain at the end of the transfer of a frame, at this - moment a frame with the old settings is being transmitted, and a frame is - being captured with the old settings. So if we adjust the autogain we must - ignore atleast the 2 next frames for the new settings to come into effect - before doing any other adjustments */ -#define PAC207_AUTOGAIN_IGNORE_FRAMES 3 /* specific webcam descriptor */ struct sd { @@ -131,7 +125,8 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, .flags = 0, }, .set = sd_setautogain, @@ -181,9 +176,6 @@ static const __u8 pac207_sensor_init[][8] = { /* 48 reg_72 Rate Control end BalSize_4a =0x36 */ static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 }; -static const unsigned char pac207_sof_marker[5] = - { 0xff, 0xff, 0x00, 0xff, 0x96 }; - static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, const u8 *buffer, u16 length) { @@ -259,35 +251,32 @@ static int sd_config(struct gspca_dev *gspca_dev, return -ENODEV; } - pac207_write_reg(gspca_dev, 0x41, 0x00); - /* Bit_0=Image Format, - * Bit_1=LED, - * Bit_2=Compression test mode enable */ - pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ - pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */ - PDEBUG(D_PROBE, "Pixart PAC207BCA Image Processor and Control Chip detected" " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x05; cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); sd->brightness = PAC207_BRIGHTNESS_DEFAULT; sd->exposure = PAC207_EXPOSURE_DEFAULT; sd->gain = PAC207_GAIN_DEFAULT; + sd->autogain = AUTOGAIN_DEF; return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; + pac207_write_reg(gspca_dev, 0x41, 0x00); + /* Bit_0=Image Format, + * Bit_1=LED, + * Bit_2=Compression test mode enable */ + pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ + pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */ - sd->autogain = 1; return 0; } @@ -343,14 +332,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ -} +/* Include pac common sof detection functions */ +#include "pac_common.h" static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) { @@ -365,33 +348,7 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, 100 + sd->brightness / 2, PAC207_AUTOGAIN_DEADZONE, PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE)) - sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES; -} - -static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev, - unsigned char *m, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - - /* Search for the SOF marker (fixed part) in the header */ - for (i = 0; i < len; i++) { - if (m[i] == pac207_sof_marker[sd->sof_read]) { - sd->sof_read++; - if (sd->sof_read == sizeof(pac207_sof_marker)) { - PDEBUG(D_STREAM, - "SOF found, bytes to analyze: %u." - " Frame starts at byte #%u", - len, i + 1); - sd->sof_read = 0; - return m + i + 1; - } - } else { - sd->sof_read = 0; - } - } - - return NULL; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -402,14 +359,14 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac207_find_sof(gspca_dev, data, len); + sof = pac_find_sof(gspca_dev, data, len); if (sof) { int n; /* finish decoding current frame */ n = sof - data; - if (n > sizeof pac207_sof_marker) - n -= sizeof pac207_sof_marker; + if (n > sizeof pac_sof_marker) + n -= sizeof pac_sof_marker; else n = 0; frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, @@ -537,7 +494,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) sd->gain = PAC207_GAIN_DEFAULT; if (gspca_dev->streaming) { sd->autogain_ignore_frames = - PAC207_AUTOGAIN_IGNORE_FRAMES; + PAC_AUTOGAIN_IGNORE_FRAMES; setexposure(gspca_dev); setgain(gspca_dev); } @@ -560,11 +517,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .dq_callback = pac207_do_auto_gain, .pkt_scan = sd_pkt_scan, }; @@ -579,6 +534,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2470)}, {USB_DEVICE(0x093a, 0x2471)}, {USB_DEVICE(0x093a, 0x2472)}, + {USB_DEVICE(0x093a, 0x2476)}, {USB_DEVICE(0x2001, 0xf115)}, {} }; @@ -597,6 +553,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c index 1b3ea1e93..296ba82e6 100644 --- a/linux/drivers/media/video/gspca/pac7311.c +++ b/linux/drivers/media/video/gspca/pac7311.c @@ -19,6 +19,36 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* Some documentation about various registers as determined by trial and error. + When the register addresses differ between the 7202 and the 7311 the 2 + different addresses are written as 7302addr/7311addr, when one of the 2 + addresses is a - sign that register description is not valid for the + matching IC. + + Register page 1: + + Address Description + -/0x08 Unknown compressor related, must always be 8 except when not + in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! + -/0x1b Auto white balance related, bit 0 is AWB enable (inverted) + bits 345 seem to toggle per color gains on/off (inverted) + 0x78 Global control, bit 6 controls the LED (inverted) + -/0x80 JPEG compression ratio ? Best not touched + + Register page 3/4: + + Address Description + 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on + the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + -/0x0f Master gain 1-245, low value = high gain + 0x10/- Master gain 0-31 + -/0x10 Another gain 0-15, limited influence (1-2x gain I guess) + 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to + completely disable the analog amplification block. Set to 0x68 + for max gain, 0x14 for minimal gain. +*/ + #define MODULE_NAME "pac7311" #include "gspca.h" @@ -31,18 +61,23 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int lum_sum; - atomic_t avg_lum; - atomic_t do_gain; - unsigned char brightness; unsigned char contrast; unsigned char colors; + unsigned char gain; + unsigned char exposure; unsigned char autogain; + __u8 hflip; + __u8 vflip; + + __u8 sensor; +#define SENSOR_PAC7302 0 +#define SENSOR_PAC7311 1 - char ffseq; - signed char ag_cnt; -#define AG_CNT_START 13 + u8 sof_read; + u8 autogain_ignore_frames; + + atomic_t avg_lum; }; /* V4L2 controls supported by the driver */ @@ -54,8 +89,18 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +/* This control is pac7302 only */ +#define BRIGHTNESS_IDX 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -71,13 +116,15 @@ static struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, +/* This control is for both the 7302 and the 7311 */ { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, - .maximum = 255, +#define CONTRAST_MAX 255 + .maximum = CONTRAST_MAX, .step = 1, #define CONTRAST_DEF 127 .default_value = CONTRAST_DEF, @@ -85,13 +132,16 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, +/* This control is pac7302 only */ +#define SATURATION_IDX 2 { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Color", + .name = "Saturation", .minimum = 0, - .maximum = 255, +#define COLOR_MAX 255 + .maximum = COLOR_MAX, .step = 1, #define COLOR_DEF 127 .default_value = COLOR_DEF, @@ -99,6 +149,39 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcolors, .get = sd_getcolors, }, +/* All controls below are for both the 7302 and the 7311 */ + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, +#define GAIN_MAX 255 + .maximum = GAIN_MAX, + .step = 1, +#define GAIN_DEF 127 +#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, +#define EXPOSURE_MAX 255 + .maximum = EXPOSURE_MAX, + .step = 1, +#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ +#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, { { .id = V4L2_CID_AUTOGAIN, @@ -113,101 +196,207 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + {320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; -#define PAC7311_JPEG_HEADER_SIZE (sizeof pac7311_jpeg_header) /* (594) */ - -static const __u8 pac7311_jpeg_header[] = { - 0xff, 0xd8, - 0xff, 0xe0, 0x00, 0x03, 0x20, - 0xff, 0xc0, 0x00, 0x11, 0x08, - 0x01, 0xe0, /* 12: height */ - 0x02, 0x80, /* 14: width */ - 0x03, /* 16 */ - 0x01, 0x21, 0x00, - 0x02, 0x11, 0x01, - 0x03, 0x11, 0x01, - 0xff, 0xdb, 0x00, 0x84, - 0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, - 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, - 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d, - 0x3c, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, - 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, 0x5f, - 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64, - 0x78, 0x5c, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12, 0x12, 0x18, - 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, - 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, - 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, - 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, - 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, - 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, - 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, - 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, - 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, - 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, - 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, - 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, - 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, - 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, - 0x11, 0x00, 0x3f, 0x00 +/* pac 7302 */ +static const __u8 init_7302[] = { +/* index,value */ + 0xff, 0x01, /* page 1 */ + 0x78, 0x00, /* deactivate */ + 0xff, 0x01, + 0x78, 0x40, /* led off */ +}; +static const __u8 start_7302[] = { +/* index, len, [value]* */ + 0xff, 1, 0x00, /* page 0 */ + 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, + 0x00, 0x00, 0x00, 0x00, + 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, + 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, + 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, + 0x26, 2, 0xaa, 0xaa, + 0x2e, 1, 0x31, + 0x38, 1, 0x01, + 0x3a, 3, 0x14, 0xff, 0x5a, + 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, + 0x00, 0x54, 0x11, + 0x55, 1, 0x00, + 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, + 0x6b, 1, 0x00, + 0x6e, 3, 0x08, 0x06, 0x00, + 0x72, 3, 0x00, 0xff, 0x00, + 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, + 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, + 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, + 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, + 0xd2, 0xeb, + 0xaf, 1, 0x02, + 0xb5, 2, 0x08, 0x08, + 0xb8, 2, 0x08, 0x88, + 0xc4, 4, 0xae, 0x01, 0x04, 0x01, + 0xcc, 1, 0x00, + 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, + 0xc1, 0xd7, 0xec, + 0xdc, 1, 0x01, + 0xff, 1, 0x01, /* page 1 */ + 0x12, 3, 0x02, 0x00, 0x01, + 0x3e, 2, 0x00, 0x00, + 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, + 0x7c, 1, 0x00, + 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, + 0x02, 0x00, + 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, + 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, + 0xd8, 1, 0x01, + 0xdb, 2, 0x00, 0x01, + 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, + 0xe6, 4, 0x00, 0x00, 0x00, 0x01, + 0xeb, 1, 0x00, + 0xff, 1, 0x02, /* page 2 */ + 0x22, 1, 0x00, + 0xff, 1, 0x03, /* page 3 */ + 0x00, 255, /* load the page 3 */ + 0x11, 1, 0x01, + 0xff, 1, 0x02, /* page 2 */ + 0x13, 1, 0x00, + 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, + 0x27, 2, 0x14, 0x0c, + 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, + 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, + 0x6e, 1, 0x08, + 0xff, 1, 0x01, /* page 1 */ + 0x78, 1, 0x00, + 0, 0 /* end of sequence */ +}; + +/* page 3 - the value 0xaa says skip the index - see reg_w_page() */ +static const __u8 page3_7302[] = { + 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, + 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, + 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, + 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, + 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, + 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, + 0x00 +}; + +/* pac 7311 */ +static const __u8 init_7311[] = { + 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ + 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ + 0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */ + 0xff, 0x04, + 0x27, 0x80, + 0x28, 0xca, + 0x29, 0x53, + 0x2a, 0x0e, + 0xff, 0x01, + 0x3e, 0x20, +}; + +static const __u8 start_7311[] = { +/* index, len, [value]* */ + 0xff, 1, 0x01, /* page 1 */ + 0x02, 43, 0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c, + 0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x3e, 42, 0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e, + 0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49, + 0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48, + 0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78, + 0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b, + 0xd0, 0xff, + 0x78, 6, 0x44, 0x00, 0xf2, 0x01, 0x01, 0x80, + 0x7f, 18, 0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84, + 0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14, + 0x18, 0x20, + 0x96, 3, 0x01, 0x08, 0x04, + 0xa0, 4, 0x44, 0x44, 0x44, 0x04, + 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00, + 0x3f, 0x00, 0x0a, 0x01, 0x00, + 0xff, 1, 0x04, /* page 4 */ + 0x00, 254, /* load the page 4 */ + 0x11, 1, 0x01, + 0, 0 /* end of sequence */ +}; + +/* page 4 - the value 0xaa says skip the index - see reg_w_page() */ +static const __u8 page4_7311[] = { + 0xaa, 0xaa, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, + 0x09, 0x00, 0xaa, 0xaa, 0x07, 0x00, 0x00, 0x62, + 0x08, 0xaa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, 0xaa, + 0xaa, 0x00, 0x08, 0xaa, 0x03, 0xaa, 0x00, 0x68, + 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x28, 0x04, 0x11, 0x00, 0x00 }; static void reg_w_buf(struct gspca_dev *gspca_dev, - __u16 index, - const char *buffer, __u16 len) + __u8 index, + const char *buffer, int len) { memcpy(gspca_dev->usb_buf, buffer, len); usb_control_msg(gspca_dev->dev, @@ -219,8 +408,9 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, 500); } +#if 0 /* not used */ static __u8 reg_r(struct gspca_dev *gspca_dev, - __u16 index) + __u8 index) { usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), @@ -231,9 +421,10 @@ static __u8 reg_r(struct gspca_dev *gspca_dev, 500); return gspca_dev->usb_buf[0]; } +#endif static void reg_w(struct gspca_dev *gspca_dev, - __u16 index, + __u8 index, __u8 value) { gspca_dev->usb_buf[0] = value; @@ -241,10 +432,78 @@ static void reg_w(struct gspca_dev *gspca_dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, gspca_dev->usb_buf, 1, + 0, index, gspca_dev->usb_buf, 1, 500); } +static void reg_w_seq(struct gspca_dev *gspca_dev, + const __u8 *seq, int len) +{ + while (--len >= 0) { + reg_w(gspca_dev, seq[0], seq[1]); + seq += 2; + } +} + +/* load the beginning of a page */ +static void reg_w_page(struct gspca_dev *gspca_dev, + const __u8 *page, int len) +{ + int index; + + for (index = 0; index < len; index++) { + if (page[index] == 0xaa) /* skip this index */ + continue; + gspca_dev->usb_buf[0] = page[index]; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + } +} + +/* output a variable sequence */ +static void reg_w_var(struct gspca_dev *gspca_dev, + const __u8 *seq) +{ + int index, len; + + for (;;) { + index = *seq++; + len = *seq++; + switch (len) { + case 0: + return; + case 254: + reg_w_page(gspca_dev, page4_7311, sizeof page4_7311); + break; + case 255: + reg_w_page(gspca_dev, page3_7302, sizeof page3_7302); + break; + default: + if (len > 64) { + PDEBUG(D_ERR|D_STREAM, + "Incorrect variable sequence"); + return; + } + while (len > 0) { + if (len < 8) { + reg_w_buf(gspca_dev, index, seq, len); + seq += len; + break; + } + reg_w_buf(gspca_dev, index, seq, 8); + seq += 8; + index += 8; + len -= 8; + } + } + } + /* not reached */ +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -252,203 +511,245 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - PDEBUG(D_CONF, "Find Sensor PAC7311"); - reg_w(gspca_dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x27, 0x80); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x53); - reg_w(gspca_dev, 0x2a, 0x0e); - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x3e, 0x20); - cam = &gspca_dev->cam; cam->epaddr = 0x05; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->sensor = id->driver_info; + if (sd->sensor == SENSOR_PAC7302) { + PDEBUG(D_CONF, "Find Sensor PAC7302"); + cam->cam_mode = &vga_mode[2]; /* only 640x480 */ + cam->nmodes = 1; + } else { + PDEBUG(D_CONF, "Find Sensor PAC7311"); + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) + | (1 << SATURATION_IDX); + } sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPOSURE_DEF; sd->autogain = AUTOGAIN_DEF; - sd->ag_cnt = -1; + sd->hflip = HFLIP_DEF; + sd->vflip = VFLIP_DEF; return 0; } -static void setbrightness(struct gspca_dev *gspca_dev) +/* This function is used by pac7302 only */ +static void setbrightcont(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + static const __u8 max[10] = + {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, + 0xd4, 0xec}; + static const __u8 delta[10] = + {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, + 0x11, 0x0b}; + + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 10; i++) { + v = max[i]; + v += (sd->brightness - BRIGHTNESS_MAX) + * 150 / BRIGHTNESS_MAX; /* 200 ? */ + v -= delta[i] * sd->contrast / CONTRAST_MAX; + if (v < 0) + v = 0; + else if (v > 0xff) + v = 0xff; + reg_w(gspca_dev, 0xa2 + i, v); + } + reg_w(gspca_dev, 0xdc, 0x01); +} + +/* This function is used by pac7311 only */ +static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int brightness; -/*jfm: inverted?*/ - brightness = BRIGHTNESS_MAX - sd->brightness; reg_w(gspca_dev, 0xff, 0x04); -/* reg_w(gspca_dev, 0x0e, 0x00); */ - reg_w(gspca_dev, 0x0f, brightness); + reg_w(gspca_dev, 0x10, sd->contrast >> 4); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); - PDEBUG(D_CONF|D_STREAM, "brightness: %i", brightness); } -static void setcontrast(struct gspca_dev *gspca_dev) +/* This function is used by pac7302 only */ +static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int i, v; + static const int a[9] = + {217, -212, 0, -101, 170, -67, -38, -315, 355}; + static const int b[9] = + {19, 106, 0, 19, 106, 1, 19, 106, 1}; - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x80, sd->contrast); - /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ reg_w(gspca_dev, 0x11, 0x01); - PDEBUG(D_CONF|D_STREAM, "contrast: %i", sd->contrast); + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 9; i++) { + v = a[i] * sd->colors / COLOR_MAX + b[i]; + reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); + reg_w(gspca_dev, 0x0f + 2 * i + 1, v); + } + reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); } -static void setcolors(struct gspca_dev *gspca_dev) +static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x10, sd->colors); + if (sd->sensor == SENSOR_PAC7302) { + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x10, sd->gain >> 3); + } else { + int gain = GAIN_MAX - sd->gain; + if (gain < 1) + gain = 1; + else if (gain > 245) + gain = 245; + reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + reg_w(gspca_dev, 0x0e, 0x00); + reg_w(gspca_dev, 0x0f, gain); + } /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); - PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); } -static void setautogain(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + __u8 reg; + + /* register 2 of frame 3/4 contains the clock divider configuring the + no fps according to the formula: 60 / reg. sd->exposure is the + desired exposure time in ms. */ + reg = 120 * sd->exposure / 1000; + if (reg < 2) + reg = 2; + else if (reg > 63) + reg = 63; + + if (sd->sensor == SENSOR_PAC7302) { + /* On the pac7302 reg2 MUST be a multiple of 3, so round it to + the nearest multiple of 3, except when between 6 and 12? */ + if (reg < 6 || reg > 12) + reg = ((reg + 1) / 3) * 3; + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x02, reg); + } else { + reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + reg_w(gspca_dev, 0x02, reg); + /* Page 1 register 8 must always be 0x08 except when not in + 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ + reg_w(gspca_dev, 0xff, 0x01); + if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && + reg <= 3) + reg_w(gspca_dev, 0x08, 0x09); + else + reg_w(gspca_dev, 0x08, 0x08); + } + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); +} - if (sd->autogain) { - sd->lum_sum = 0; - sd->ag_cnt = AG_CNT_START; +static void sethvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; + + if (sd->sensor == SENSOR_PAC7302) { + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + data = (sd->hflip ? 0x08 : 0x00) + | (sd->vflip ? 0x04 : 0x00); } else { - sd->ag_cnt = -1; + reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + data = (sd->hflip ? 0x04 : 0x00) + | (sd->vflip ? 0x08 : 0x00); } + reg_w(gspca_dev, 0x21, data); + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { - reg_w(gspca_dev, 0x78, 0x00); /* Turn on LED */ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAC7302) + reg_w_seq(gspca_dev, init_7302, sizeof init_7302); + else + reg_w_seq(gspca_dev, init_7311, sizeof init_7311); + return 0; } static void sd_start(struct gspca_dev *gspca_dev) { - reg_w(gspca_dev, 0xff, 0x01); - reg_w_buf(gspca_dev, 0x0002, "\x48\x0a\x40\x08\x00\x00\x08\x00", 8); - reg_w_buf(gspca_dev, 0x000a, "\x06\xff\x11\xff\x5a\x30\x90\x4c", 8); - reg_w_buf(gspca_dev, 0x0012, "\x00\x07\x00\x0a\x10\x00\xa0\x10", 8); - reg_w_buf(gspca_dev, 0x001a, "\x02\x00\x00\x00\x00\x0b\x01\x00", 8); - reg_w_buf(gspca_dev, 0x0022, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); - reg_w_buf(gspca_dev, 0x002a, "\x00\x00\x00", 3); - reg_w_buf(gspca_dev, 0x003e, "\x00\x00\x78\x52\x4a\x52\x78\x6e", 8); - reg_w_buf(gspca_dev, 0x0046, "\x48\x46\x48\x6e\x5f\x49\x42\x49", 8); - reg_w_buf(gspca_dev, 0x004e, "\x5f\x5f\x49\x42\x49\x5f\x6e\x48", 8); - reg_w_buf(gspca_dev, 0x0056, "\x46\x48\x6e\x78\x52\x4a\x52\x78", 8); - reg_w_buf(gspca_dev, 0x005e, "\x00\x00\x09\x1b\x34\x49\x5c\x9b", 8); - reg_w_buf(gspca_dev, 0x0066, "\xd0\xff", 2); - reg_w_buf(gspca_dev, 0x0078, "\x44\x00\xf2\x01\x01\x80", 6); - reg_w_buf(gspca_dev, 0x007f, "\x2a\x1c\x00\xc8\x02\x58\x03\x84", 8); - reg_w_buf(gspca_dev, 0x0087, "\x12\x00\x1a\x04\x08\x0c\x10\x14", 8); - reg_w_buf(gspca_dev, 0x008f, "\x18\x20", 2); - reg_w_buf(gspca_dev, 0x0096, "\x01\x08\x04", 3); - reg_w_buf(gspca_dev, 0x00a0, "\x44\x44\x44\x04", 4); - reg_w_buf(gspca_dev, 0x00f0, "\x01\x00\x00\x00\x22\x00\x20\x00", 8); - reg_w_buf(gspca_dev, 0x00f8, "\x3f\x00\x0a\x01\x00", 5); + struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x02, 0x04); - reg_w(gspca_dev, 0x03, 0x54); - reg_w(gspca_dev, 0x04, 0x07); - reg_w(gspca_dev, 0x05, 0x2b); - reg_w(gspca_dev, 0x06, 0x09); - reg_w(gspca_dev, 0x07, 0x0f); - reg_w(gspca_dev, 0x08, 0x09); - reg_w(gspca_dev, 0x09, 0x00); - reg_w(gspca_dev, 0x0c, 0x07); - reg_w(gspca_dev, 0x0d, 0x00); - reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, 0x62); - reg_w(gspca_dev, 0x10, 0x08); - reg_w(gspca_dev, 0x12, 0x07); - reg_w(gspca_dev, 0x13, 0x00); - reg_w(gspca_dev, 0x14, 0x00); - reg_w(gspca_dev, 0x15, 0x00); - reg_w(gspca_dev, 0x16, 0x00); - reg_w(gspca_dev, 0x17, 0x00); - reg_w(gspca_dev, 0x18, 0x00); - reg_w(gspca_dev, 0x19, 0x00); - reg_w(gspca_dev, 0x1a, 0x00); - reg_w(gspca_dev, 0x1b, 0x03); - reg_w(gspca_dev, 0x1c, 0xa0); - reg_w(gspca_dev, 0x1d, 0x01); - reg_w(gspca_dev, 0x1e, 0xf4); - reg_w(gspca_dev, 0x21, 0x00); - reg_w(gspca_dev, 0x22, 0x08); - reg_w(gspca_dev, 0x24, 0x03); - reg_w(gspca_dev, 0x26, 0x00); - reg_w(gspca_dev, 0x27, 0x01); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x10); - reg_w(gspca_dev, 0x2a, 0x06); - reg_w(gspca_dev, 0x2b, 0x78); - reg_w(gspca_dev, 0x2c, 0x00); - reg_w(gspca_dev, 0x2d, 0x00); - reg_w(gspca_dev, 0x2e, 0x00); - reg_w(gspca_dev, 0x2f, 0x00); - reg_w(gspca_dev, 0x30, 0x23); - reg_w(gspca_dev, 0x31, 0x28); - reg_w(gspca_dev, 0x32, 0x04); - reg_w(gspca_dev, 0x33, 0x11); - reg_w(gspca_dev, 0x34, 0x00); - reg_w(gspca_dev, 0x35, 0x00); - reg_w(gspca_dev, 0x11, 0x01); - setcontrast(gspca_dev); - setbrightness(gspca_dev); - setcolors(gspca_dev); - setautogain(gspca_dev); + sd->sof_read = 0; + + if (sd->sensor == SENSOR_PAC7302) { + reg_w_var(gspca_dev, start_7302); + setbrightcont(gspca_dev); + setcolors(gspca_dev); + } else { + reg_w_var(gspca_dev, start_7311); + setcontrast(gspca_dev); + } + setgain(gspca_dev); + setexposure(gspca_dev); + sethvflip(gspca_dev); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 2: /* 160x120 */ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x02, 0x03); + case 2: /* 160x120 pac7311 */ reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x08, 0x09); reg_w(gspca_dev, 0x17, 0x20); - reg_w(gspca_dev, 0x1b, 0x00); -/* reg_w(gspca_dev, 0x80, 0x69); */ reg_w(gspca_dev, 0x87, 0x10); break; - case 1: /* 320x240 */ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x02, 0x03); + case 1: /* 320x240 pac7311 */ reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x08, 0x09); reg_w(gspca_dev, 0x17, 0x30); -/* reg_w(gspca_dev, 0x80, 0x3f); */ reg_w(gspca_dev, 0x87, 0x11); break; case 0: /* 640x480 */ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x02, 0x03); + if (sd->sensor == SENSOR_PAC7302) + break; reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x08, 0x08); reg_w(gspca_dev, 0x17, 0x00); -/* reg_w(gspca_dev, 0x80, 0x1c); */ reg_w(gspca_dev, 0x87, 0x12); break; } + sd->sof_read = 0; + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, -1); + /* start stream */ reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x04); - reg_w(gspca_dev, 0x78, 0x05); + if (sd->sensor == SENSOR_PAC7302) + reg_w(gspca_dev, 0x78, 0x01); + else + reg_w(gspca_dev, 0x78, 0x05); } static void sd_stopN(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAC7302) { + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x00); + reg_w(gspca_dev, 0x78, 0x00); + return; + } reg_w(gspca_dev, 0xff, 0x04); reg_w(gspca_dev, 0x27, 0x80); reg_w(gspca_dev, 0x28, 0xca); @@ -456,200 +757,147 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x2a, 0x0e); reg_w(gspca_dev, 0xff, 0x01); reg_w(gspca_dev, 0x3e, 0x20); - reg_w(gspca_dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ } static void sd_stop0(struct gspca_dev *gspca_dev) { -} + struct sd *sd = (struct sd *) gspca_dev; -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x27, 0x80); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x53); - reg_w(gspca_dev, 0x2a, 0x0e); - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x3e, 0x20); - reg_w(gspca_dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + if (sd->sensor == SENSOR_PAC7302) { + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x40); + } } +/* Include pac common sof detection functions */ +#include "pac_common.h" + static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int luma; - int luma_mean = 128; - int luma_delta = 20; - __u8 spring = 5; - int Gbright; + int avg_lum = atomic_read(&sd->avg_lum); + int desired_lum, deadzone; - if (!atomic_read(&sd->do_gain)) + if (avg_lum == -1) return; - atomic_set(&sd->do_gain, 0); - - luma = atomic_read(&sd->avg_lum); - Gbright = reg_r(gspca_dev, 0x02); - PDEBUG(D_FRAM, "luma mean %d", luma); - if (luma < luma_mean - luma_delta || - luma > luma_mean + luma_delta) { - Gbright += (luma_mean - luma) >> spring; - if (Gbright > 0x1a) - Gbright = 0x1a; - else if (Gbright < 4) - Gbright = 4; - PDEBUG(D_FRAM, "gbright %d", Gbright); - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x0f, Gbright); - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + + if (sd->sensor == SENSOR_PAC7302) { + desired_lum = 270 + sd->brightness * 4; + /* Hack hack, with the 7202 the first exposure step is + pretty large, so if we're about to make the first + exposure increase make the deadzone large to avoid + oscilating */ + if (desired_lum > avg_lum && sd->gain == GAIN_DEF && + sd->exposure > EXPOSURE_DEF && + sd->exposure < 42) + deadzone = 90; + else + deadzone = 30; + } else { + desired_lum = 200; + deadzone = 20; } + + if (sd->autogain_ignore_frames > 0) + sd->autogain_ignore_frames--; + else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, + deadzone, GAIN_KNEE, EXPOSURE_KNEE)) + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } +static const unsigned char pac7311_jpeg_header1[] = { + 0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08 +}; + +static const unsigned char pac7311_jpeg_header2[] = { + 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, + 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +}; + +/* this function is run at interrupt level */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - unsigned char tmpbuf[4]; - int i, p, ffseq; - -/* if (len < 5) { */ - if (len < 6) { -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; - } - - ffseq = sd->ffseq; - - for (p = 0; p < len - 6; p++) { - if ((data[0 + p] == 0xff) - && (data[1 + p] == 0xff) - && (data[2 + p] == 0x00) - && (data[3 + p] == 0xff) - && (data[4 + p] == 0x96)) { - - /* start of frame */ - if (sd->ag_cnt >= 0 && p > 28) { - sd->lum_sum += data[p - 23]; - if (--sd->ag_cnt < 0) { - sd->ag_cnt = AG_CNT_START; - atomic_set(&sd->avg_lum, - sd->lum_sum / AG_CNT_START); - sd->lum_sum = 0; - atomic_set(&sd->do_gain, 1); - } - } + unsigned char *sof; + + sof = pac_find_sof(gspca_dev, data, len); + if (sof) { + unsigned char tmpbuf[4]; + int n, lum_offset, footer_length; + + if (sd->sensor == SENSOR_PAC7302) { + /* 6 bytes after the FF D9 EOF marker a number of lumination + bytes are send corresponding to different parts of the + image, the 14th and 15th byte after the EOF seem to + correspond to the center of the image */ + lum_offset = 61 + sizeof pac_sof_marker; + footer_length = 74; + } else { + lum_offset = 24 + sizeof pac_sof_marker; + footer_length = 26; + } - /* copy the end of data to the current frame */ + /* Finish decoding current frame */ + n = (sof - data) - (footer_length + sizeof pac_sof_marker); + if (n < 0) { + frame->data_end += n; + n = 0; + } + frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, n); + if (gspca_dev->last_packet_type != DISCARD_PACKET && + frame->data_end[-2] == 0xff && + frame->data_end[-1] == 0xd9) frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, p); - - /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - (unsigned char *) pac7311_jpeg_header, - 12); + NULL, 0); + + n = sof - data; + len -= n; + data = sof; + + /* Get average lumination */ + if (gspca_dev->last_packet_type == LAST_PACKET && + n >= lum_offset) + atomic_set(&sd->avg_lum, data[-lum_offset] + + data[-lum_offset + 1]); + else + atomic_set(&sd->avg_lum, -1); + + /* Start the new frame with the jpeg header */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1)); + if (sd->sensor == SENSOR_PAC7302) { + /* The PAC7302 has the image rotated 90 degrees */ + tmpbuf[0] = gspca_dev->width >> 8; + tmpbuf[1] = gspca_dev->width & 0xff; + tmpbuf[2] = gspca_dev->height >> 8; + tmpbuf[3] = gspca_dev->height & 0xff; + } else { tmpbuf[0] = gspca_dev->height >> 8; tmpbuf[1] = gspca_dev->height & 0xff; tmpbuf[2] = gspca_dev->width >> 8; tmpbuf[3] = gspca_dev->width & 0xff; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - tmpbuf, 4); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - (unsigned char *) &pac7311_jpeg_header[16], - PAC7311_JPEG_HEADER_SIZE - 16); - - data += p + 7; - len -= p + 7; - ffseq = 0; - break; - } - } - - /* remove the 'ff ff ff xx' sequences */ - switch (ffseq) { - case 3: - data += 1; - len -= 1; - break; - case 2: - if (data[0] == 0xff) { - data += 2; - len -= 2; - frame->data_end -= 2; - } - break; - case 1: - if (data[0] == 0xff - && data[1] == 0xff) { - data += 3; - len -= 3; - frame->data_end -= 1; - } - break; - } - for (i = 0; i < len - 4; i++) { - if (data[i] == 0xff - && data[i + 1] == 0xff - && data[i + 2] == 0xff) { - memmove(&data[i], &data[i + 4], len - i - 4); - len -= 4; } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2)); } - ffseq = 0; - if (data[len - 4] == 0xff) { - if (data[len - 3] == 0xff - && data[len - 2] == 0xff) { - len -= 4; - } - } else if (data[len - 3] == 0xff) { - if (data[len - 2] == 0xff - && data[len - 1] == 0xff) - ffseq = 3; - } else if (data[len - 2] == 0xff) { - if (data[len - 1] == 0xff) - ffseq = 2; - } else if (data[len - 1] == 0xff) - ffseq = 1; - sd->ffseq = ffseq; gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -static void getbrightness(struct gspca_dev *gspca_dev) -{ -/* sd->brightness = reg_r(gspca_dev, 0x08); - return sd->brightness; */ -/* PDEBUG(D_CONF, "Called pac7311_getbrightness: Not implemented yet"); */ -} - -#if 0 -static void getcontrast(struct gspca_dev *gspca_dev) -{ -/* sd->contrast = reg_r(gspca_dev, 0x0e); - return sd->contrast; */ - PDEBUG(D_CONF, "getcontrast: Not implemented yet"); -} -#endif - -#if 0 -static void getcolors(struct gspca_dev *gspca_dev) -{ -} -#endif - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; sd->brightness = val; if (gspca_dev->streaming) - setbrightness(gspca_dev); + setbrightcont(gspca_dev); return 0; } @@ -657,7 +905,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -667,8 +914,12 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_PAC7302) + setbrightcont(gspca_dev); + else + setcontrast(gspca_dev); + } return 0; } @@ -676,7 +927,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; -/* getcontrast(gspca_dev); */ *val = sd->contrast; return 0; } @@ -695,18 +945,66 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; -/* getcolors(gspca_dev); */ *val = sd->colors; return 0; } +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; sd->autogain = val; - if (gspca_dev->streaming) - setautogain(gspca_dev); + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + if (sd->autogain) { + sd->exposure = EXPOSURE_DEF; + sd->gain = GAIN_DEF; + if (gspca_dev->streaming) { + sd->autogain_ignore_frames = + PAC_AUTOGAIN_IGNORE_FRAMES; + setexposure(gspca_dev); + setgain(gspca_dev); + } + } + return 0; } @@ -718,30 +1016,68 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + /* sub-driver description */ static struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, }; /* -- module initialisation -- */ static __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x093a, 0x2600)}, - {USB_DEVICE(0x093a, 0x2601)}, - {USB_DEVICE(0x093a, 0x2603)}, - {USB_DEVICE(0x093a, 0x2608)}, - {USB_DEVICE(0x093a, 0x260e)}, - {USB_DEVICE(0x093a, 0x260f)}, - {USB_DEVICE(0x093a, 0x2621)}, + {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -759,6 +1095,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/pac_common.h b/linux/drivers/media/video/gspca/pac_common.h new file mode 100644 index 000000000..34d4b1494 --- /dev/null +++ b/linux/drivers/media/video/gspca/pac_common.h @@ -0,0 +1,60 @@ +/* + * Pixart PAC207BCA / PAC73xx common functions + * + * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* We calculate the autogain at the end of the transfer of a frame, at this + moment a frame with the old settings is being transmitted, and a frame is + being captured with the old settings. So if we adjust the autogain we must + ignore atleast the 2 next frames for the new settings to come into effect + before doing any other adjustments */ +#define PAC_AUTOGAIN_IGNORE_FRAMES 3 + +static const unsigned char pac_sof_marker[5] = + { 0xff, 0xff, 0x00, 0xff, 0x96 }; + +static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev, + unsigned char *m, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + + /* Search for the SOF marker (fixed part) in the header */ + for (i = 0; i < len; i++) { + if (m[i] == pac_sof_marker[sd->sof_read]) { + sd->sof_read++; + if (sd->sof_read == sizeof(pac_sof_marker)) { + PDEBUG(D_FRAM, + "SOF found, bytes to analyze: %u." + " Frame starts at byte #%u", + len, i + 1); + sd->sof_read = 0; + return m + i + 1; + } + } else { + sd->sof_read = 0; + } + } + + return NULL; +} diff --git a/linux/drivers/media/video/gspca/sonixb.c b/linux/drivers/media/video/gspca/sonixb.c index 4d979fa39..53cb724bf 100644 --- a/linux/drivers/media/video/gspca/sonixb.c +++ b/linux/drivers/media/video/gspca/sonixb.c @@ -20,6 +20,26 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* Some documentation on known sonixb registers: + +Reg Use +0x10 high nibble red gain low nibble blue gain +0x11 low nibble green gain +0x12 hstart +0x13 vstart +0x15 hsize (hsize = register-value * 16) +0x16 vsize (vsize = register-value * 16) +0x17 bit 0 toggle compression quality (according to sn9c102 driver) +0x18 bit 7 enables compression, bit 4-5 set image down scaling: + 00 scale 1, 01 scale 1/2, 10, scale 1/4 +0x19 high-nibble is sensor clock divider, changes exposure on sensors which + use a clock generated by the bridge. Some sensors have their own clock. +0x1c auto_exposure area (for avg_lum) startx (startx = register-value * 32) +0x1d auto_exposure area (for avg_lum) starty (starty = register-value * 32) +0x1e auto_exposure area (for avg_lum) stopx (hsize = (0x1e - 0x1c) * 32) +0x1f auto_exposure area (for avg_lum) stopy (vsize = (0x1f - 0x1d) * 32) +*/ + #define MODULE_NAME "sonixb" #include "gspca.h" @@ -31,10 +51,8 @@ MODULE_LICENSE("GPL"); /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct sd_desc sd_desc; /* our nctrls differ dependend upon the - sensor, so we use a per cam copy */ atomic_t avg_lum; + int prev_avg_lum; unsigned char gain; unsigned char exposure; @@ -44,8 +62,12 @@ struct sd { unsigned char frames_to_drop; unsigned char freq; /* light freq filter setting */ - unsigned char fr_h_sz; /* size of frame header */ - char sensor; /* Type of image sensor chip */ + __u8 bridge; /* Type of bridge */ +#define BRIDGE_101 0 +#define BRIDGE_102 0 /* We make no difference between 101 and 102 */ +#define BRIDGE_103 1 + + __u8 sensor; /* Type of image sensor chip */ #define SENSOR_HV7131R 0 #define SENSOR_OV6650 1 #define SENSOR_OV7630 2 @@ -53,16 +75,35 @@ struct sd { #define SENSOR_PAS202 4 #define SENSOR_TAS5110 5 #define SENSOR_TAS5130CXX 6 - char sensor_has_gain; - __u8 sensor_addr; __u8 reg11; }; -/* flags used in the device id table */ +typedef const __u8 sensor_init_t[8]; + +struct sensor_data { + const __u8 *bridge_init[2]; + int bridge_init_size[2]; + sensor_init_t *sensor_init; + int sensor_init_size; + sensor_init_t *sensor_bridge_init[2]; + int sensor_bridge_init_size[2]; + int flags; + unsigned ctrl_dis; + __u8 sensor_addr; +}; + +/* sensor_data flags */ #define F_GAIN 0x01 /* has gain */ -#define F_AUTO 0x02 /* has autogain */ -#define F_SIF 0x04 /* sif or vga */ -#define F_H18 0x08 /* long (18 b) or short (12 b) frame header */ +#define F_SIF 0x02 /* sif or vga */ + +/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */ +#define MODE_RAW 0x10 /* raw bayer mode */ +#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */ + +/* ctrl_dis helper macros */ +#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)) +#define NO_FREQ (1 << FREQ_IDX) +#define NO_BRIGHTNESS (1 << BRIGHTNESS_IDX) #define COMP2 0x8f #define COMP 0xc7 /* 0x87 //0x07 */ @@ -73,6 +114,18 @@ struct sd { #define SYS_CLK 0x04 +#define SENS(bridge_1, bridge_3, sensor, sensor_1, \ + sensor_3, _flags, _ctrl_dis, _sensor_addr) \ +{ \ + .bridge_init = { bridge_1, bridge_3 }, \ + .bridge_init_size = { sizeof(bridge_1), sizeof(bridge_3) }, \ + .sensor_init = sensor, \ + .sensor_init_size = sizeof(sensor), \ + .sensor_bridge_init = { sensor_1, sensor_3,}, \ + .sensor_bridge_init_size = { sizeof(sensor_1), sizeof(sensor_3)}, \ + .flags = _flags, .ctrl_dis = _ctrl_dis, .sensor_addr = _sensor_addr \ +} + /* We calculate the autogain at the end of the transfer of a frame, at this moment a frame with the old settings is being transmitted, and a frame is being captured with the old settings. So if we adjust the autogain we must @@ -95,6 +148,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +#define BRIGHTNESS_IDX 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -109,6 +163,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, +#define GAIN_IDX 1 { { .id = V4L2_CID_GAIN, @@ -124,6 +179,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgain, .get = sd_getgain, }, +#define EXPOSURE_IDX 2 { { .id = V4L2_CID_EXPOSURE, @@ -140,6 +196,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setexposure, .get = sd_getexposure, }, +#define AUTOGAIN_IDX 3 { { .id = V4L2_CID_AUTOGAIN, @@ -155,6 +212,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, +#define FREQ_IDX 4 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -172,31 +230,56 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120, .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, .bytesperline = 320, - .sizeimage = 320 * 240, + .sizeimage = 320 * 240 * 5 / 4, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, {640, 480, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, .bytesperline = 640, - .sizeimage = 640 * 480, + .sizeimage = 640 * 480 * 5 / 4, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; static struct v4l2_pix_format sif_mode[] = { - {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW | MODE_REDUCED_SIF}, + {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_REDUCED_SIF}, + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144, .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW}, + {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, + {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 | MODE_REDUCED_SIF}, {352, 288, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, .bytesperline = 352, - .sizeimage = 352 * 288, + .sizeimage = 352 * 288 * 5 / 4, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -204,7 +287,7 @@ static struct v4l2_pix_format sif_mode[] = { static const __u8 initHv7131[] = { 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, /* shift from 0x02 0x01 0x00 */ + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x28, 0x1e, 0x60, 0x8a, 0x20, 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c }; @@ -219,13 +302,13 @@ static const __u8 initOv6650[] = { #if 1 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x0b, - 0x10, 0x1d, 0x10, 0x00, 0x06, 0x1f, 0x00 + 0x00, 0x01, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x8b, + 0x10, 0x1d, 0x10, 0x02, 0x02, 0x09, 0x07 #else /* old version? */ 0x64, 0x44, 0x28, 0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x01, 0x0a, 0x14, 0x0f, 0x68, 0x8b, + 0x00, 0x01, 0x01, 0x0a, 0x14, 0x0f, 0x68, 0x8b, 0x10, 0x1d, 0x10, 0x01, 0x01, 0x07, 0x06 #endif }; @@ -283,22 +366,19 @@ static const __u8 ov6650_sensor_init[][8] = static const __u8 initOv7630[] = { 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ -#if 1 - 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ + 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ 0x28, 0x1e, /* H & V sizes r15 .. r16 */ - 0x68, COMP1, MCK_INIT1, /* r17 .. r19 */ + 0x68, COMP2, MCK_INIT1, /* r17 .. r19 */ +#if 1 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c /* r1a .. r1f */ #else /* jfm from win */ - 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ - 0x16, 0x12, /* H & V sizes r15 .. r16 */ - 0x68, COMP2, MCK_INIT1, /* r17 .. r19 */ 0x1d, 0x10, 0x06, 0x01, 0x00, 0x03 /* r1a .. r1f */ #endif }; static const __u8 initOv7630_3[] = { 0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* r09 .. r10 */ - 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ + 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ 0x28, 0x1e, /* H & V sizes r15 .. r16 */ 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */ 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c, 0x00, /* r1a .. r20 */ @@ -327,47 +407,65 @@ static const __u8 ov7630_sensor_init[][8] = { {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, }; +static const __u8 ov7630_sensor_init_3[][8] = { + {0xa0, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10}, +}; + static const __u8 initPas106[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, - 0x16, 0x12, 0x28, COMP1, MCK_INIT1, - 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x16, 0x12, 0x24, COMP1, MCK_INIT1, + 0x18, 0x10, 0x02, 0x02, 0x09, 0x07 }; /* compression 0x86 mckinit1 0x2b */ -static const __u8 pas106_data[][2] = { - {0x02, 0x04}, /* Pixel Clock Divider 6 */ - {0x03, 0x13}, /* Frame Time MSB */ -/* {0x03, 0x12}, * Frame Time MSB */ - {0x04, 0x06}, /* Frame Time LSB */ -/* {0x04, 0x05}, * Frame Time LSB */ - {0x05, 0x65}, /* Shutter Time Line Offset */ -/* {0x05, 0x6d}, * Shutter Time Line Offset */ -/* {0x06, 0xb1}, * Shutter Time Pixel Offset */ - {0x06, 0xcd}, /* Shutter Time Pixel Offset */ - {0x07, 0xc1}, /* Black Level Subtract Sign */ -/* {0x07, 0x00}, * Black Level Subtract Sign */ - {0x08, 0x06}, /* Black Level Subtract Level */ - {0x08, 0x06}, /* Black Level Subtract Level */ -/* {0x08, 0x01}, * Black Level Subtract Level */ - {0x09, 0x05}, /* Color Gain B Pixel 5 a */ - {0x0a, 0x04}, /* Color Gain G1 Pixel 1 5 */ - {0x0b, 0x04}, /* Color Gain G2 Pixel 1 0 5 */ - {0x0c, 0x05}, /* Color Gain R Pixel 3 1 */ - {0x0d, 0x00}, /* Color GainH Pixel */ - {0x0e, 0x0e}, /* Global Gain */ - {0x0f, 0x00}, /* Contrast */ - {0x10, 0x06}, /* H&V synchro polarity */ - {0x11, 0x06}, /* ?default */ - {0x12, 0x06}, /* DAC scale */ - {0x14, 0x02}, /* ?default */ - {0x13, 0x01}, /* Validate Settings */ +static const __u8 pas106_sensor_init[][8] = { + /* Pixel Clock Divider 6 */ + { 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 }, + /* Frame Time MSB (also seen as 0x12) */ + { 0xa1, 0x40, 0x03, 0x13, 0x00, 0x00, 0x00, 0x14 }, + /* Frame Time LSB (also seen as 0x05) */ + { 0xa1, 0x40, 0x04, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* Shutter Time Line Offset (also seen as 0x6d) */ + { 0xa1, 0x40, 0x05, 0x65, 0x00, 0x00, 0x00, 0x14 }, + /* Shutter Time Pixel Offset (also seen as 0xb1) */ + { 0xa1, 0x40, 0x06, 0xcd, 0x00, 0x00, 0x00, 0x14 }, + /* Black Level Subtract Sign (also seen 0x00) */ + { 0xa1, 0x40, 0x07, 0xc1, 0x00, 0x00, 0x00, 0x14 }, + /* Black Level Subtract Level (also seen 0x01) */ + { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, + { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain B Pixel 5 a */ + { 0xa1, 0x40, 0x09, 0x05, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain G1 Pixel 1 5 */ + { 0xa1, 0x40, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain G2 Pixel 1 0 5 */ + { 0xa1, 0x40, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain R Pixel 3 1 */ + { 0xa1, 0x40, 0x0c, 0x05, 0x00, 0x00, 0x00, 0x14 }, + /* Color GainH Pixel */ + { 0xa1, 0x40, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x14 }, + /* Global Gain */ + { 0xa1, 0x40, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x14 }, + /* Contrast */ + { 0xa1, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14 }, + /* H&V synchro polarity */ + { 0xa1, 0x40, 0x10, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* ?default */ + { 0xa1, 0x40, 0x11, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* DAC scale */ + { 0xa1, 0x40, 0x12, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* ?default */ + { 0xa1, 0x40, 0x14, 0x02, 0x00, 0x00, 0x00, 0x14 }, + /* Validate Settings */ + { 0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14 }, }; + static const __u8 initPas202[] = { 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x03, 0x0a, /* 6 */ - 0x28, 0x1e, 0x28, 0x89, 0x30, + 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, + 0x28, 0x1e, 0x28, 0x89, 0x20, 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c }; static const __u8 pas202_sensor_init[][8] = { @@ -397,7 +495,7 @@ static const __u8 pas202_sensor_init[][8] = { static const __u8 initTas5110[] = { 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x46, 0x09, 0x0a, /* shift from 0x45 0x09 0x0a */ + 0x00, 0x01, 0x00, 0x45, 0x09, 0x0a, 0x16, 0x12, 0x60, 0x86, 0x2b, 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 }; @@ -410,7 +508,7 @@ static const __u8 tas5110_sensor_init[][8] = { static const __u8 initTas5130[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x69, 0x0c, 0x0a, + 0x00, 0x01, 0x00, 0x68, 0x0c, 0x0a, 0x28, 0x1e, 0x60, COMP, MCK_INIT, 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c }; @@ -422,6 +520,21 @@ static const __u8 tas5130_sensor_init[][8] = { {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}, }; +static struct sensor_data sensor_data[] = { +SENS(initHv7131, NULL, hv7131_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0), +SENS(initOv6650, NULL, ov6650_sensor_init, NULL, NULL, F_GAIN|F_SIF, 0, 0x60), +SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3, + F_GAIN, 0, 0x21), +SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ, + 0), +SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0, + NO_EXPO|NO_FREQ, 0), +SENS(initTas5110, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, + NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, + 0), +}; + /* get one byte in gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, __u16 value) @@ -442,7 +555,7 @@ static void reg_w(struct gspca_dev *gspca_dev, int len) { #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { PDEBUG(D_ERR|D_PACK, "reg_w: buffer overflow"); return; } @@ -458,26 +571,6 @@ static void reg_w(struct gspca_dev *gspca_dev, 500); } -static void reg_w_big(struct gspca_dev *gspca_dev, - __u16 value, - const __u8 *buffer, - int len) -{ - __u8 *tmpbuf; - - tmpbuf = kmalloc(len, GFP_KERNEL); - memcpy(tmpbuf, buffer, len); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, - 0, /* index */ - tmpbuf, len, - 500); - kfree(tmpbuf); -} - static int i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) { int retry = 60; @@ -520,7 +613,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}; /* change reg 0x06 */ - i2cOV[1] = sd->sensor_addr; + i2cOV[1] = sensor_data[sd->sensor].sensor_addr; i2cOV[3] = sd->brightness; if (i2c_w(gspca_dev, i2cOV) < 0) goto err; @@ -578,9 +671,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) goto err; break; } - case SENSOR_TAS5110: - /* FIXME figure out howto control brightness on TAS5110 */ - break; } return; err: @@ -610,7 +700,7 @@ static void setsensorgain(struct gspca_dev *gspca_dev) case SENSOR_OV7630: { __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; - i2c[1] = sd->sensor_addr; + i2c[1] = sensor_data[sd->sensor].sensor_addr; i2c[3] = gain >> 2; if (i2c_w(gspca_dev, i2c) < 0) goto err; @@ -637,7 +727,7 @@ static void setgain(struct gspca_dev *gspca_dev) rgb_value = gain; reg_w(gspca_dev, 0x11, &rgb_value, 1); - if (sd->sensor_has_gain) + if (sensor_data[sd->sensor].flags & F_GAIN) setsensorgain(gspca_dev); } @@ -698,6 +788,11 @@ static void setexposure(struct gspca_dev *gspca_dev) else if (reg11 > 16) reg11 = 16; + /* In 640x480, if the reg11 has less than 3, the image is + unstable (not enough bandwidth). */ + if (gspca_dev->width == 640 && reg11 < 3) + reg11 = 3; + /* frame exposure time in ms = 1000 * reg11 / 30 -> reg10 = sd->exposure * 2 * reg10_max / (1000 * reg11 / 30) */ reg10 = (sd->exposure * 60 * reg10_max) / (1000 * reg11); @@ -711,13 +806,8 @@ static void setexposure(struct gspca_dev *gspca_dev) else if (reg10 > reg10_max) reg10 = reg10_max; - /* In 640x480, if the reg11 has less than 3, the image is - unstable (not enough bandwidth). */ - if (gspca_dev->width == 640 && reg11 < 3) - reg11 = 3; - /* Write reg 10 and reg11 low nibble */ - i2c[1] = sd->sensor_addr; + i2c[1] = sensor_data[sd->sensor].sensor_addr; i2c[3] = reg10; i2c[4] |= reg11 - 1; @@ -757,7 +847,7 @@ static void setfreq(struct gspca_dev *gspca_dev) ? 0x4f : 0x8a; break; } - i2c[1] = sd->sensor_addr; + i2c[1] = sensor_data[sd->sensor].sensor_addr; if (i2c_w(gspca_dev, i2c) < 0) PDEBUG(D_ERR, "i2c error setfreq"); break; @@ -790,30 +880,19 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - int sif = 0; - /* nctrls depends upon the sensor, so we use a per cam copy */ - memcpy(&sd->sd_desc, gspca_dev->sd_desc, sizeof(struct sd_desc)); - gspca_dev->sd_desc = &sd->sd_desc; + reg_r(gspca_dev, 0x00); + if (gspca_dev->usb_buf[0] != 0x10) + return -ENODEV; /* copy the webcam info from the device id */ - sd->sensor = (id->driver_info >> 24) & 0xff; - if (id->driver_info & (F_GAIN << 16)) - sd->sensor_has_gain = 1; - if (id->driver_info & (F_AUTO << 16)) - sd->sd_desc.dq_callback = do_autogain; - if (id->driver_info & (F_SIF << 16)) - sif = 1; - if (id->driver_info & (F_H18 << 16)) - sd->fr_h_sz = 18; /* size of frame header */ - else - sd->fr_h_sz = 12; - sd->sd_desc.nctrls = (id->driver_info >> 8) & 0xff; - sd->sensor_addr = id->driver_info & 0xff; + sd->sensor = id->driver_info >> 8; + sd->bridge = id->driver_info & 0xff; + gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis; cam = &gspca_dev->cam; cam->epaddr = 0x01; - if (!sif) { + if (!(sensor_data[sd->sensor].flags & F_SIF)) { cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); } else { @@ -823,157 +902,98 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->gain = GAIN_DEF; sd->exposure = EXPOSURE_DEF; - sd->autogain = AUTOGAIN_DEF; + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) + sd->autogain = 0; /* Disable do_autogain callback */ + else + sd->autogain = AUTOGAIN_DEF; sd->freq = FREQ_DEF; return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { - reg_r(gspca_dev, 0x00); - if (gspca_dev->usb_buf[0] != 0x10) - return -ENODEV; - return 0; -} + const __u8 stop = 0x09; /* Disable stream turn of LED */ -static void pas106_i2cinit(struct gspca_dev *gspca_dev) -{ - int i; - const __u8 *data; - __u8 i2c1[] = { 0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 }; - - i = ARRAY_SIZE(pas106_data); - data = pas106_data[0]; - while (--i >= 0) { - memcpy(&i2c1[2], data, 2); - /* copy 2 bytes from the template */ - if (i2c_w(gspca_dev, i2c1) < 0) - PDEBUG(D_ERR, "i2c error pas106"); - data += 2; - } + reg_w(gspca_dev, 0x01, &stop, 1); + + return 0; } /* -- start the camera -- */ static void sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int mode, l = 0x1f; + struct cam *cam = &gspca_dev->cam; + int mode, l; const __u8 *sn9c10x; - __u8 reg17_19[3]; - - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + __u8 reg12_19[8]; + + mode = cam->cam_mode[gspca_dev->curr_mode].priv & 0x07; + sn9c10x = sensor_data[sd->sensor].bridge_init[sd->bridge]; + l = sensor_data[sd->sensor].bridge_init_size[sd->bridge]; + memcpy(reg12_19, &sn9c10x[0x12 - 1], 8); + reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4); + /* Special cases where reg 17 and or 19 value depends on mode */ switch (sd->sensor) { - case SENSOR_HV7131R: - sn9c10x = initHv7131; - reg17_19[0] = 0x60; - reg17_19[1] = (mode << 4) | 0x8a; - reg17_19[2] = 0x20; - break; - case SENSOR_OV6650: - sn9c10x = initOv6650; - reg17_19[0] = 0x68; - reg17_19[1] = (mode << 4) | 0x8b; - reg17_19[2] = 0x20; - break; - case SENSOR_OV7630: - if (sd->fr_h_sz == 18) { /* SN9C103 */ - sn9c10x = initOv7630_3; - l = sizeof initOv7630_3; - } else - sn9c10x = initOv7630; - reg17_19[0] = 0x68; - reg17_19[1] = (mode << 4) | COMP2; - reg17_19[2] = MCK_INIT1; - break; - case SENSOR_PAS106: - sn9c10x = initPas106; - reg17_19[0] = 0x24; /* 0x28 */ - reg17_19[1] = (mode << 4) | COMP1; - reg17_19[2] = MCK_INIT1; - break; case SENSOR_PAS202: - sn9c10x = initPas202; - reg17_19[0] = mode ? 0x24 : 0x20; - reg17_19[1] = (mode << 4) | 0x89; - reg17_19[2] = 0x20; - break; - case SENSOR_TAS5110: - sn9c10x = initTas5110; - reg17_19[0] = 0x60; - reg17_19[1] = (mode << 4) | 0x86; - reg17_19[2] = 0x2b; /* 0xf3; */ + reg12_19[5] = mode ? 0x24 : 0x20; break; - default: -/* case SENSOR_TAS5130CXX: */ - sn9c10x = initTas5130; - reg17_19[0] = 0x60; - reg17_19[1] = (mode << 4) | COMP; - reg17_19[2] = mode ? 0x23 : 0x43; + case SENSOR_TAS5130CXX: + /* probably not mode specific at all most likely the upper + nibble of 0x19 is exposure (clock divider) just as with + the tas5110, we need someone to test this. */ + reg12_19[7] = mode ? 0x23 : 0x43; break; } + /* Disable compression when the raw bayer format has been selected */ + if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) + reg12_19[6] &= ~0x80; + + /* Vga mode emulation on SIF sensor? */ + if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_REDUCED_SIF) { + reg12_19[0] += 16; /* 0x12: hstart adjust */ + reg12_19[1] += 24; /* 0x13: vstart adjust */ + reg12_19[3] = 320 / 16; /* 0x15: hsize */ + reg12_19[4] = 240 / 16; /* 0x16: vsize */ + } /* reg 0x01 bit 2 video transfert on */ reg_w(gspca_dev, 0x01, &sn9c10x[0x01 - 1], 1); /* reg 0x17 SensorClk enable inv Clk 0x60 */ reg_w(gspca_dev, 0x17, &sn9c10x[0x17 - 1], 1); /* Set the registers from the template */ - reg_w_big(gspca_dev, 0x01, sn9c10x, l); - switch (sd->sensor) { - case SENSOR_HV7131R: - i2c_w_vector(gspca_dev, hv7131_sensor_init, - sizeof hv7131_sensor_init); - break; - case SENSOR_OV6650: - i2c_w_vector(gspca_dev, ov6650_sensor_init, - sizeof ov6650_sensor_init); - break; - case SENSOR_OV7630: - i2c_w_vector(gspca_dev, ov7630_sensor_init, - sizeof ov7630_sensor_init); - if (sd->fr_h_sz == 18) { /* SN9C103 */ - const __u8 i2c[] = { 0xa0, 0x21, 0x13, 0x80, 0x00, - 0x00, 0x00, 0x10 }; - i2c_w(gspca_dev, i2c); - } - break; - case SENSOR_PAS106: - pas106_i2cinit(gspca_dev); - break; - case SENSOR_PAS202: - i2c_w_vector(gspca_dev, pas202_sensor_init, - sizeof pas202_sensor_init); - break; - case SENSOR_TAS5110: - i2c_w_vector(gspca_dev, tas5110_sensor_init, - sizeof tas5110_sensor_init); - break; - default: -/* case SENSOR_TAS5130CXX: */ - i2c_w_vector(gspca_dev, tas5130_sensor_init, - sizeof tas5130_sensor_init); - break; - } + reg_w(gspca_dev, 0x01, sn9c10x, l); + + /* Init the sensor */ + i2c_w_vector(gspca_dev, sensor_data[sd->sensor].sensor_init, + sensor_data[sd->sensor].sensor_init_size); + if (sensor_data[sd->sensor].sensor_bridge_init[sd->bridge]) + i2c_w_vector(gspca_dev, + sensor_data[sd->sensor].sensor_bridge_init[sd->bridge], + sensor_data[sd->sensor].sensor_bridge_init_size[ + sd->bridge]); + /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ - reg_w(gspca_dev, 0x15, &sn9c10x[0x15 - 1], 2); + reg_w(gspca_dev, 0x15, ®12_19[3], 2); /* compression register */ - reg_w(gspca_dev, 0x18, ®17_19[1], 1); + reg_w(gspca_dev, 0x18, ®12_19[6], 1); /* H_start */ - reg_w(gspca_dev, 0x12, &sn9c10x[0x12 - 1], 1); + reg_w(gspca_dev, 0x12, ®12_19[0], 1); /* V_START */ - reg_w(gspca_dev, 0x13, &sn9c10x[0x13 - 1], 1); + reg_w(gspca_dev, 0x13, ®12_19[1], 1); /* reset 0x17 SensorClk enable inv Clk 0x60 */ /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ - reg_w(gspca_dev, 0x17, ®17_19[0], 1); + reg_w(gspca_dev, 0x17, ®12_19[5], 1); /*MCKSIZE ->3 */ /*fixme: not ov7630*/ - reg_w(gspca_dev, 0x19, ®17_19[2], 1); + reg_w(gspca_dev, 0x19, ®12_19[7], 1); /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ reg_w(gspca_dev, 0x1c, &sn9c10x[0x1c - 1], 4); /* Enable video transfert */ reg_w(gspca_dev, 0x01, &sn9c10x[0], 1); /* Compression */ - reg_w(gspca_dev, 0x18, ®17_19[1], 2); + reg_w(gspca_dev, 0x18, ®12_19[6], 2); msleep(20); sd->reg11 = -1; @@ -990,18 +1010,7 @@ static void sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { - __u8 ByteSend; - - ByteSend = 0x09; /* 0X00 */ - reg_w(gspca_dev, 0x01, &ByteSend, 1); -} - -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ + sd_init(gspca_dev); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -1011,6 +1020,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { int i; struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; /* frames start with: * ff ff 00 c4 c4 96 synchro @@ -1031,20 +1041,31 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, && data[5 + i] == 0x96) { /* start of frame */ int lum = -1; int pkt_type = LAST_PACKET; + int fr_h_sz = (sd->bridge == BRIDGE_103) ? + 18 : 12; - if (len - i < sd->fr_h_sz) { + if (len - i < fr_h_sz) { PDEBUG(D_STREAM, "packet too short to" " get avg brightness"); - } else if (sd->fr_h_sz == 12) { - lum = data[i + 8] + (data[i + 9] << 8); - } else { + } else if (sd->bridge == BRIDGE_103) { lum = data[i + 9] + (data[i + 10] << 8); + } else { + lum = data[i + 8] + (data[i + 9] << 8); } - if (lum == 0) { + /* When exposure changes midway a frame we + get a lum of 0 in this case drop 2 frames + as the frames directly after an exposure + change have an unstable image. Sometimes lum + *really* is 0 (cam used in low light with + low exposure setting), so do not drop frames + if the previous lum was 0 too. */ + if (lum == 0 && sd->prev_avg_lum != 0) { lum = -1; sd->frames_to_drop = 2; - } + sd->prev_avg_lum = 0; + } else + sd->prev_avg_lum = lum; atomic_set(&sd->avg_lum, lum); if (sd->frames_to_drop) { @@ -1054,14 +1075,25 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame = gspca_frame_add(gspca_dev, pkt_type, frame, data, 0); - data += i + sd->fr_h_sz; - len -= i + sd->fr_h_sz; + data += i + fr_h_sz; + len -= i + fr_h_sz; gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); return; } } } + + if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { + /* In raw mode we sometimes get some garbage after the frame + ignore this */ + int used = frame->data_end - frame->data; + int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; + + if (used + len > size) + len = size - used; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } @@ -1195,58 +1227,45 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, + .dq_callback = do_autogain, }; /* -- module initialisation -- */ -#define SFCI(sensor, flags, nctrls, i2c_addr) \ - .driver_info = (SENSOR_ ## sensor << 24) \ - | ((flags) << 16) \ - | ((nctrls) << 8) \ - | (i2c_addr) +#define SB(sensor, bridge) \ + .driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge + + static __devinitdata struct usb_device_id device_table[] = { -#ifndef CONFIG_USB_SN9C102 - {USB_DEVICE(0x0c45, 0x6001), /* SN9C102 */ - SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, - {USB_DEVICE(0x0c45, 0x6005), /* SN9C101 */ - SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, - {USB_DEVICE(0x0c45, 0x6007), /* SN9C101 */ - SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, - {USB_DEVICE(0x0c45, 0x6009), /* SN9C101 */ - SFCI(PAS106, F_SIF, 2, 0)}, - {USB_DEVICE(0x0c45, 0x600d), /* SN9C101 */ - SFCI(PAS106, F_SIF, 2, 0)}, + {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110, 102)}, /* TAS5110C1B */ + {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110, 101)}, /* TAS5110C1B */ +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110, 101)}, /* TAS5110D */ + {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)}, + {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, +#endif + {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)}, + {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)}, + {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, + {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)}, + {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)}, + {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, #endif - {USB_DEVICE(0x0c45, 0x6011), /* SN9C101 - SN9C101G */ - SFCI(OV6650, F_GAIN|F_AUTO|F_SIF, 5, 0x60)}, -#ifndef CONFIG_USB_SN9C102 - {USB_DEVICE(0x0c45, 0x6019), /* SN9C101 */ - SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, - {USB_DEVICE(0x0c45, 0x6024), /* SN9C102 */ - SFCI(TAS5130CXX, 0, 2, 0)}, - {USB_DEVICE(0x0c45, 0x6025), /* SN9C102 */ - SFCI(TAS5130CXX, 0, 2, 0)}, - {USB_DEVICE(0x0c45, 0x6028), /* SN9C102 */ - SFCI(PAS202, 0, 2, 0)}, - {USB_DEVICE(0x0c45, 0x6029), /* SN9C101 */ - SFCI(PAS106, F_SIF, 2, 0)}, - {USB_DEVICE(0x0c45, 0x602c), /* SN9C102 */ - SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, - {USB_DEVICE(0x0c45, 0x602d), /* SN9C102 */ - SFCI(HV7131R, 0, 2, 0)}, - {USB_DEVICE(0x0c45, 0x602e), /* SN9C102 */ - SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, - {USB_DEVICE(0x0c45, 0x60af), /* SN9C103 */ - SFCI(PAS202, F_H18, 2, 0)}, - {USB_DEVICE(0x0c45, 0x60b0), /* SN9C103 */ - SFCI(OV7630, F_GAIN|F_AUTO|F_H18, 5, 0x21)}, + {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)}, #endif + {USB_DEVICE(0x0c45, 0x608f), SB(OV7630, 103)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x60af), SB(PAS202, 103)}, +#endif + {USB_DEVICE(0x0c45, 0x60b0), SB(OV7630, 103)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1264,6 +1283,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index b30f7eeda..a77164581 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -39,6 +39,7 @@ struct sd { unsigned char contrast; unsigned char colors; unsigned char autogain; + __u8 vflip; /* ov7630 only */ signed char ag_cnt; #define AG_CNT_START 13 @@ -54,8 +55,10 @@ struct sd { #define SENSOR_HV7131R 0 #define SENSOR_MI0360 1 #define SENSOR_MO4000 2 -#define SENSOR_OV7648 3 -#define SENSOR_OV7660 4 +#define SENSOR_OM6802 3 +#define SENSOR_OV7630 4 +#define SENSOR_OV7648 5 +#define SENSOR_OV7660 6 unsigned char i2c_base; }; @@ -68,6 +71,8 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { { @@ -76,7 +81,8 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Brightness", .minimum = 0, - .maximum = 0xffff, +#define BRIGHTNESS_MAX 0xffff + .maximum = BRIGHTNESS_MAX, .step = 1, #define BRIGHTNESS_DEF 0x7fff .default_value = BRIGHTNESS_DEF, @@ -90,7 +96,8 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, - .maximum = 127, +#define CONTRAST_MAX 127 + .maximum = CONTRAST_MAX, .step = 1, #define CONTRAST_DEF 63 .default_value = CONTRAST_DEF, @@ -104,14 +111,15 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Color", .minimum = 0, - .maximum = 255, + .maximum = 64, .step = 1, -#define COLOR_DEF 127 +#define COLOR_DEF 32 .default_value = COLOR_DEF, }, .set = sd_setcolors, .get = sd_getcolors, }, +#define AUTOGAIN_IDX 3 { { .id = V4L2_CID_AUTOGAIN, @@ -126,12 +134,28 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, +/* ov7630 only */ +#define VFLIP_IDX 4 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, }; static struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 8 + 590, + .sizeimage = 160 * 120 * 4 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -180,6 +204,31 @@ static const __u8 sn_mo4000[] = { 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const __u8 sn_om6802[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x23, 0x72, 0x00, 0x1a, 0x34, 0x27, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x51, 0x01, 0x00, 0x28, 0x1e, 0x40, +/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x22, 0x44, 0x63, 0x7d, 0x92, 0xa3, 0xaf, + 0xbc, 0xc4, 0xcd, 0xd5, 0xdc, 0xe1, 0xe8, 0xef, + 0xf7 +}; + +static const __u8 sn_ov7630[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0xa1, 0x21, 0x76, 0x21, 0x00, 0x00, 0x00, 0x10, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2, +/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + static const __u8 sn_ov7648[] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, @@ -219,31 +268,26 @@ static const __u8 *sn_tb[] = { sn_hv7131, sn_mi0360, sn_mo4000, + sn_om6802, + sn_ov7630, sn_ov7648, sn_ov7660 }; -static const __u8 regsn20[] = { +static const __u8 gamma_def[] = { 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99, 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff }; -static const __u8 regsn20_sn9c325[] = { - 0x0a, 0x3a, 0x56, 0x6c, 0x7e, 0x8d, 0x9a, 0xa4, - 0xaf, 0xbb, 0xc5, 0xcd, 0xd5, 0xde, 0xe8, 0xed, 0xf5 -}; static const __u8 reg84[] = { 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f, 0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f, -/* 0x00, 0x00, 0x00, 0x00, 0x00 */ +#if 1 + 0xf7, 0x0f, 0x00, 0x00, 0x00 +#else 0xf7, 0x0f, 0x0a, 0x00, 0x00 +#endif }; -static const __u8 reg84_sn9c325[] = { - 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe4, 0x0f, - 0xd3, 0x0f, 0x4b, 0x00, 0x48, 0x00, 0xc0, 0x0f, - 0xf8, 0x0f, 0x00, 0x00, 0x00 -}; - static const __u8 hv7131r_sensor_init[][8] = { {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10}, @@ -352,6 +396,93 @@ static const __u8 mo4000_sensor_init[][8] = { {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, {} }; +static __u8 om6802_sensor_init[][8] = { + {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10}, +/* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */ + {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10}, + /* white balance & auto-exposure */ +/* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10}, + * set color mode */ +/* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10}, + * max AGC value in AE */ +/* {0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10}, + * preset AGC */ +/* {0xa0, 0x34, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10}, + * preset brightness */ +/* {0xa0, 0x34, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x10}, + * preset contrast */ +/* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10}, + * preset gamma */ + {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10}, + /* luminance mode (0x4f = AE) */ + {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10}, + /* preset shutter */ +/* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10}, + * auto frame rate */ +/* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */ + +/* {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, */ +/* {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, */ +/* {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, */ +/* {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */ + {} +}; +static const __u8 ov7630_sensor_init[][8] = { + {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, +/* win: delay 20ms */ + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, +/* win: delay 20ms */ + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, +/* win: i2c_r from 00 to 80 */ + {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, + {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x11, 0x00, 0x48, 0xc0, 0x00, 0x10}, + {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, + {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x1f, 0x00, 0x80, 0x80, 0x80, 0x10}, + {0xd1, 0x21, 0x23, 0xde, 0x10, 0x8a, 0xa0, 0x10}, + {0xc1, 0x21, 0x27, 0xca, 0xa2, 0x74, 0x00, 0x10}, + {0xd1, 0x21, 0x2a, 0x88, 0x00, 0x88, 0x01, 0x10}, + {0xc1, 0x21, 0x2e, 0x80, 0x00, 0x18, 0x00, 0x10}, + {0xa1, 0x21, 0x21, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x32, 0xc2, 0x08, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x60, 0x05, 0x40, 0x12, 0x57, 0x10}, + {0xa1, 0x21, 0x64, 0x73, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x65, 0x00, 0x55, 0x01, 0xac, 0x10}, + {0xa1, 0x21, 0x69, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x6f, 0x1f, 0x01, 0x00, 0x10, 0x10}, + {0xd1, 0x21, 0x73, 0x50, 0x20, 0x02, 0x01, 0x10}, + {0xd1, 0x21, 0x77, 0xf3, 0x90, 0x98, 0x98, 0x10}, + {0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10}, + {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, + {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, +/* */ + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, +/*fixme: + 0x12, 0x04*/ +/* {0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10}, * COMN + * set by setvflip */ + {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10}, +/* */ + {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, +/* */ + {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */ + {} +}; static const __u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ /* (delay 20ms) */ @@ -518,10 +649,16 @@ static const __u8 qtable4[] = { 0x29, 0x29, 0x29, 0x29 }; -/* read <len> bytes (len < sizeof gspca_dev->usb_buf) to gspca_dev->usb_buf */ +/* read <len> bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, __u16 value, int len) { +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + err("reg_r: buffer overflow"); + return; + } +#endif usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 0, @@ -554,29 +691,20 @@ static void reg_w(struct gspca_dev *gspca_dev, { PDEBUG(D_USBO, "reg_w [%02x] = %02x %02x ..", value, buffer[0], buffer[1]); - if (len <= sizeof gspca_dev->usb_buf) { - memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, 0, - gspca_dev->usb_buf, len, - 500); - } else { - __u8 *tmpbuf; - - tmpbuf = kmalloc(len, GFP_KERNEL); - memcpy(tmpbuf, buffer, len); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, 0, - tmpbuf, len, - 500); - kfree(tmpbuf); +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + err("reg_w: buffer overflow"); + return; } +#endif + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + gspca_dev->usb_buf, len, + 500); } /* I2C write 1 byte */ @@ -677,7 +805,11 @@ static int configure_gpio(struct gspca_dev *gspca_dev, static const __u8 regd4[] = {0x60, 0x00, 0x00}; reg_w1(gspca_dev, 0xf1, 0x00); - reg_w1(gspca_dev, 0x01, 0x00); /*jfm was sn9c1xx[1] in v1*/ +#if 1 + reg_w1(gspca_dev, 0x01, sn9c1xx[1]); +#else + reg_w1(gspca_dev, 0x01, 0x00); /*jfm: in some win traces*/ +#endif /* configure gpio */ reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2); @@ -697,29 +829,45 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); - switch (sd->bridge) { -#if 0 + switch (sd->sensor) { + case SENSOR_OM6802: + reg_w1(gspca_dev, 0x02, 0x71); + reg_w1(gspca_dev, 0x01, 0x42); + reg_w1(gspca_dev, 0x17, 0x64); + reg_w1(gspca_dev, 0x01, 0x42); + break; +#if 1 /*jfm: from win trace */ - case BRIDGE_SN9C120: + case SENSOR_OV7630: reg_w1(gspca_dev, 0x01, 0x61); - reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x17, 0xe2); reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); break; #endif - case BRIDGE_SN9C325: + case SENSOR_OV7648: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0xae); reg_w1(gspca_dev, 0x01, 0x42); break; +#if 1 +/*jfm: from win trace */ + case SENSOR_OV7660: + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + break; +#endif default: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0x61); reg_w1(gspca_dev, 0x01, 0x42); - } - - if (sd->sensor == SENSOR_HV7131R) { - if (probesensor(gspca_dev) < 0) - return -ENODEV; + if (sd->sensor == SENSOR_HV7131R) { + if (probesensor(gspca_dev) < 0) + return -ENODEV; + } + break; } return 0; } @@ -757,6 +905,40 @@ static void mo4000_InitSensor(struct gspca_dev *gspca_dev) } } +static void om6802_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + while (om6802_sensor_init[i][0]) { + i2c_w8(gspca_dev, om6802_sensor_init[i]); + i++; + } +} + +static void ov7630_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 76 01 */ + i++; + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 (RGB+SRST) */ + i++; + msleep(20); + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ + i++; + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 */ + i++; + msleep(20); + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ + i++; +/*jfm:win i2c_r from 00 to 80*/ + + while (ov7630_sensor_init[i][0]) { + i2c_w8(gspca_dev, ov7630_sensor_init[i]); + i++; + } +} + static void ov7648_InitSensor(struct gspca_dev *gspca_dev) { int i = 0; @@ -803,11 +985,21 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->autogain = AUTOGAIN_DEF; sd->ag_cnt = -1; + switch (sd->sensor) { + case SENSOR_OV7630: + case SENSOR_OV7648: + case SENSOR_OV7660: + gspca_dev->ctrl_dis = (1 << AUTOGAIN_IDX); + break; + } + if (sd->sensor != SENSOR_OV7630) + gspca_dev->ctrl_dis |= (1 << VFLIP_IDX); + return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; /* const __u8 *sn9c1xx; */ @@ -916,16 +1108,53 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev, | ((expoMo10[3] & 0x30) >> 4)); break; } + case SENSOR_OM6802: { + __u8 gainOm[] = + { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 }; + + if (expo > 0x03ff) + expo = 0x03ff; + if (expo < 0x0001) + expo = 0x0001; + gainOm[3] = expo >> 2; + i2c_w8(gspca_dev, gainOm); + reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f); + PDEBUG(D_CONF, "set exposure %d", gainOm[3]); + break; + } } return expo; } +/* this function is used for sensors o76xx only */ +static void setbrightcont(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned val; + __u8 reg84_full[0x15]; + + memset(reg84_full, 0, sizeof reg84_full); + val = sd->contrast * 0x20 / CONTRAST_MAX + 0x10; /* 10..30 */ + reg84_full[2] = val; + reg84_full[0] = (val + 1) / 2; + reg84_full[4] = (val + 1) / 5; + if (val > BRIGHTNESS_DEF) + val = (sd->brightness - BRIGHTNESS_DEF) * 0x20 + / BRIGHTNESS_MAX; + else + val = 0; + reg84_full[0x12] = val; /* 00..1f */ + reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full); +} + +/* sensor != ov76xx */ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; unsigned int expo; __u8 k2; + k2 = sd->brightness >> 10; switch (sd->sensor) { case SENSOR_HV7131R: expo = sd->brightness << 4; @@ -940,12 +1169,17 @@ static void setbrightness(struct gspca_dev *gspca_dev) expo = sd->brightness >> 4; sd->exposure = setexposure(gspca_dev, expo); break; + case SENSOR_OM6802: + expo = sd->brightness >> 6; + sd->exposure = setexposure(gspca_dev, expo); + k2 = sd->brightness >> 11; + break; } - k2 = sd->brightness >> 10; reg_w1(gspca_dev, 0x96, k2); } +/* sensor != ov76xx */ static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -962,31 +1196,38 @@ static void setcontrast(struct gspca_dev *gspca_dev) static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data; - int colour; + __u8 blue, red; - colour = sd->colors - 128; - if (colour > 0) - data = (colour + 32) & 0x7f; /* blue */ - else - data = (-colour + 32) & 0x7f; /* red */ - reg_w1(gspca_dev, 0x05, data); + if (sd->colors >= 32) { + red = 32 + (sd->colors - 32) / 2; + blue = 64 - sd->colors; + } else { + red = sd->colors; + blue = 32 + (32 - sd->colors) / 2; + } + reg_w1(gspca_dev, 0x05, red); +/* reg_w1(gspca_dev, 0x07, 32); */ + reg_w1(gspca_dev, 0x06, blue); } static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_MO4000: - case SENSOR_MI0360: - if (sd->autogain) - sd->ag_cnt = AG_CNT_START; - else - sd->ag_cnt = -1; - break; - } + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) + return; + if (sd->autogain) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; +} + +static void setvflip(struct sd *sd) +{ + if (sd->sensor != SENSOR_OV7630) + return; + i2c_w1(&sd->gspca_dev, 0x75, /* COMN */ + sd->vflip ? 0x82 : 0x02); } /* -- start the camera -- */ @@ -1000,13 +1241,12 @@ static void sd_start(struct gspca_dev *gspca_dev) static const __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; static const __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ - static const __u8 CE_sn9c325[] = - { 0x32, 0xdd, 0x32, 0xdd }; /* OV7648 - SN9C325 */ + static const __u8 CE_ov76xx[] = + { 0x32, 0xdd, 0x32, 0xdd }; /* OV7630/48 */ sn9c1xx = sn_tb[(int) sd->sensor]; configure_gpio(gspca_dev, sn9c1xx); -/* reg_w1(gspca_dev, 0x01, 0x44); jfm from win trace*/ reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]); @@ -1019,13 +1259,16 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xc8, 0x50); reg_w1(gspca_dev, 0xc9, 0x3c); reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); - switch (sd->bridge) { - case BRIDGE_SN9C325: + switch (sd->sensor) { + case SENSOR_OV7630: + reg17 = 0xe2; + break; + case SENSOR_OV7648: reg17 = 0xae; break; -#if 0 +#if 1 /*jfm: from win trace */ - case BRIDGE_SN9C120: + case SENSOR_OV7660: reg17 = 0xa0; break; #endif @@ -1038,24 +1281,23 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x07, sn9c1xx[7]); reg_w1(gspca_dev, 0x06, sn9c1xx[6]); reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]); + reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def); + for (i = 0; i < 8; i++) + reg_w(gspca_dev, 0x84, reg84, sizeof reg84); +#if 0 switch (sd->bridge) { case BRIDGE_SN9C325: - reg_w(gspca_dev, 0x20, regsn20_sn9c325, - sizeof regsn20_sn9c325); - for (i = 0; i < 8; i++) - reg_w(gspca_dev, 0x84, reg84_sn9c325, - sizeof reg84_sn9c325); reg_w1(gspca_dev, 0x9a, 0x0a); reg_w1(gspca_dev, 0x99, 0x60); break; default: - reg_w(gspca_dev, 0x20, regsn20, sizeof regsn20); - for (i = 0; i < 8; i++) - reg_w(gspca_dev, 0x84, reg84, sizeof reg84); +#endif reg_w1(gspca_dev, 0x9a, 0x08); reg_w1(gspca_dev, 0x99, 0x59); +#if 0 break; } +#endif mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; if (mode) @@ -1080,6 +1322,16 @@ static void sd_start(struct gspca_dev *gspca_dev) /* reg1 = 0x06; * 640 clk 24Mz (done) */ } break; + case SENSOR_OM6802: + om6802_InitSensor(gspca_dev); + reg17 = 0x64; /* 640 MCKSIZE */ + break; + case SENSOR_OV7630: + ov7630_InitSensor(gspca_dev); + setvflip(sd); + reg17 = 0xe2; + reg1 = 0x44; + break; case SENSOR_OV7648: ov7648_InitSensor(gspca_dev); reg17 = 0xa2; @@ -1110,9 +1362,10 @@ static void sd_start(struct gspca_dev *gspca_dev) } reg_w(gspca_dev, 0xc0, C0, 6); reg_w(gspca_dev, 0xca, CA, 4); - switch (sd->bridge) { - case BRIDGE_SN9C325: - reg_w(gspca_dev, 0xce, CE_sn9c325, 4); + switch (sd->sensor) { + case SENSOR_OV7630: + case SENSOR_OV7648: + reg_w(gspca_dev, 0xce, CE_ov76xx, 4); break; default: reg_w(gspca_dev, 0xce, CE, 4); @@ -1130,10 +1383,20 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x18, reg18); reg_w1(gspca_dev, 0x17, reg17); - reg_w1(gspca_dev, 0x01, reg1); - setbrightness(gspca_dev); - setcontrast(gspca_dev); + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_MI0360: + case SENSOR_MO4000: + case SENSOR_OM6802: + setbrightness(gspca_dev); + setcontrast(gspca_dev); + break; + default: /* OV76xx */ + setbrightcont(gspca_dev); + break; + } setautogain(gspca_dev); + reg_w1(gspca_dev, 0x01, reg1); } static void sd_stopN(struct gspca_dev *gspca_dev) @@ -1156,6 +1419,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) i2c_w8(gspca_dev, stopmi0360); data = 0x29; break; + case SENSOR_OV7630: case SENSOR_OV7648: data = 0x29; break; @@ -1169,15 +1433,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]); reg_w1(gspca_dev, 0x01, sn9c1xx[1]); reg_w1(gspca_dev, 0x01, data); - reg_w1(gspca_dev, 0xf1, 0x01); -} - -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ + reg_w1(gspca_dev, 0xf1, 0x00); } static void do_autogain(struct gspca_dev *gspca_dev) @@ -1211,6 +1467,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) default: /* case SENSOR_MO4000: */ /* case SENSOR_MI0360: */ +/* case SENSOR_OM6802: */ expotimes = sd->exposure; expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) @@ -1266,69 +1523,24 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -static unsigned int getexposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 hexpo, mexpo, lexpo; - - switch (sd->sensor) { - case SENSOR_HV7131R: - /* read sensor exposure */ - i2c_r5(gspca_dev, 0x25); - return (gspca_dev->usb_buf[0] << 16) - | (gspca_dev->usb_buf[1] << 8) - | gspca_dev->usb_buf[2]; - case SENSOR_MI0360: - /* read sensor exposure */ - i2c_r5(gspca_dev, 0x09); - return (gspca_dev->usb_buf[0] << 8) - | gspca_dev->usb_buf[1]; - case SENSOR_MO4000: - i2c_r5(gspca_dev, 0x0e); - hexpo = 0; /* gspca_dev->usb_buf[1] & 0x07; */ - mexpo = 0x40; /* gspca_dev->usb_buf[2] & 0xff; */ - lexpo = (gspca_dev->usb_buf[1] & 0x30) >> 4; - PDEBUG(D_CONF, "exposure %d", - (hexpo << 10) | (mexpo << 2) | lexpo); - return (hexpo << 10) | (mexpo << 2) | lexpo; - default: -/* case SENSOR_OV7648: * jfm: is it ok for 7648? */ -/* case SENSOR_OV7660: */ - /* read sensor exposure */ - i2c_r5(gspca_dev, 0x04); - hexpo = gspca_dev->usb_buf[3] & 0x2f; - lexpo = gspca_dev->usb_buf[0] & 0x02; - i2c_r5(gspca_dev, 0x08); - mexpo = gspca_dev->usb_buf[2]; - return (hexpo << 10) | (mexpo << 2) | lexpo; - } -} - -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* hardcoded registers seem not readable */ - switch (sd->sensor) { - case SENSOR_HV7131R: - sd->brightness = getexposure(gspca_dev) >> 4; - break; - case SENSOR_MI0360: - sd->brightness = getexposure(gspca_dev) << 4; - break; - case SENSOR_MO4000: - sd->brightness = getexposure(gspca_dev) << 4; - break; - } -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; sd->brightness = val; - if (gspca_dev->streaming) - setbrightness(gspca_dev); + if (gspca_dev->streaming) { + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_MI0360: + case SENSOR_MO4000: + case SENSOR_OM6802: + setbrightness(gspca_dev); + break; + default: /* OV76xx */ + setbrightcont(gspca_dev); + break; + } + } return 0; } @@ -1336,7 +1548,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -1346,8 +1557,19 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); + if (gspca_dev->streaming) { + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_MI0360: + case SENSOR_MO4000: + case SENSOR_OM6802: + setcontrast(gspca_dev); + break; + default: /* OV76xx */ + setbrightcont(gspca_dev); + break; + } + } return 0; } @@ -1395,17 +1617,32 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + setvflip(sd); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, }; @@ -1416,8 +1653,9 @@ static const struct sd_desc sd_desc = { | (SENSOR_ ## sensor << 8) \ | (i2c_addr) static const __devinitdata struct usb_device_id device_table[] = { -#ifndef CONFIG_USB_SN9C102 +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)}, + {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)}, {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, @@ -1443,15 +1681,17 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C325, OV7648, 0x21)}, -/* bw600.inf: - {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, */ + {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/ +/*bw600.inf:*/ + {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, /*sn9c325?*/ {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)}, /* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */ -#ifndef CONFIG_USB_SN9C102 +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, +#endif {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE /* {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x??)}, */ {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, @@ -1475,6 +1715,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca500.c b/linux/drivers/media/video/gspca/spca500.c index be88d30a3..c33bfe48c 100644 --- a/linux/drivers/media/video/gspca/spca500.c +++ b/linux/drivers/media/video/gspca/spca500.c @@ -662,8 +662,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -897,14 +897,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0]); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1068,11 +1060,9 @@ static struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -1110,6 +1100,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c index f8df18e85..49e512cac 100644 --- a/linux/drivers/media/video/gspca/spca501.c +++ b/linux/drivers/media/video/gspca/spca501.c @@ -1964,8 +1964,8 @@ error: return -EINVAL; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2034,11 +2034,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_stop0(struct gspca_dev *gspca_dev) { -} - -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00); } @@ -2131,11 +2126,10 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -2165,6 +2159,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca505.c b/linux/drivers/media/video/gspca/spca505.c index 1cde4b34f..e33666df4 100644 --- a/linux/drivers/media/video/gspca/spca505.c +++ b/linux/drivers/media/video/gspca/spca505.c @@ -724,8 +724,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int ret; @@ -812,11 +812,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_stop0(struct gspca_dev *gspca_dev) { -} - -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ /* This maybe reset or power control */ reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); reg_write(gspca_dev->dev, 0x03, 0x01, 0x0); @@ -894,11 +889,10 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -924,6 +918,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca506.c b/linux/drivers/media/video/gspca/spca506.c index f622fa757..195dce96e 100644 --- a/linux/drivers/media/video/gspca/spca506.c +++ b/linux/drivers/media/video/gspca/spca506.c @@ -313,8 +313,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; @@ -560,14 +560,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(dev, 0x03, 0x00, 0x0003); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -740,11 +732,9 @@ static struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -772,6 +762,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca508.c b/linux/drivers/media/video/gspca/spca508.c index 52e2f7462..5e3f66ae2 100644 --- a/linux/drivers/media/video/gspca/spca508.c +++ b/linux/drivers/media/video/gspca/spca508.c @@ -1559,8 +1559,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; /* success */ } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { /* write_vector(gspca_dev, spca508_open_data); */ return 0; @@ -1592,15 +1592,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, 0x8112, 0x20); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1671,11 +1662,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -1705,6 +1694,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index 1073ac3d2..b1329a149 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -32,69 +32,48 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned short contrast; - __u8 brightness; + __u16 contrast; /* rev72a only */ +#define CONTRAST_MIN 0x0000 +#define CONTRAST_DEF 0x2000 +#define CONTRAST_MAX 0x3fff + + __u16 exposure; /* rev12a only */ +#define EXPOSURE_MIN 1 +#define EXPOSURE_DEF 200 +#define EXPOSURE_MAX (4095 - 900) /* see set_exposure */ + + __u8 brightness; /* rev72a only */ +#define BRIGHTNESS_MIN 0 +#define BRIGHTNESS_DEF 32 +#define BRIGHTNESS_MAX 63 + + __u8 white; /* rev12a only */ +#define WHITE_MIN 1 +#define WHITE_DEF 0x40 +#define WHITE_MAX 0x7f + __u8 autogain; +#define AUTOGAIN_MIN 0 +#define AUTOGAIN_DEF 1 +#define AUTOGAIN_MAX 1 + + __u8 gain; /* rev12a only */ +#define GAIN_MIN 0x0 +#define GAIN_DEF 0x24 +#define GAIN_MAX 0x24 + +#define EXPO12A_DEF 3 + __u8 expo12a; /* expo/gain? for rev 12a */ __u8 chip_revision; +#define Rev012A 0 +#define Rev072A 1 + signed char ag_cnt; #define AG_CNT_START 13 }; -/* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); - -static struct ctrl sd_ctrls[] = { -#define SD_BRIGHTNESS 0 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 63, - .step = 1, - .default_value = 32, - }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, -#define SD_CONTRAST 1 - { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 0x3fff, - .step = 1, - .default_value = 0x2000, - }, - .set = sd_setcontrast, - .get = sd_getcontrast, - }, -#define SD_AUTOGAIN 2 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - .set = sd_setautogain, - .get = sd_getautogain, - }, -}; - -static struct v4l2_pix_format sif_mode[] = { +static struct v4l2_pix_format sif_012a_mode[] = { {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120, @@ -117,6 +96,29 @@ static struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; +static struct v4l2_pix_format sif_072a_mode[] = { + {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + /* * Initialization data * I'm not very sure how to split initialization from open data @@ -143,12 +145,8 @@ static struct v4l2_pix_format sif_mode[] = { #define SPCA561_INDEX_I2C_BASE 0x8800 #define SPCA561_SNAPBIT 0x20 #define SPCA561_SNAPCTRL 0x40 -enum { - Rev072A = 0, - Rev012A, -}; -static void reg_w_val(struct usb_device *dev, __u16 index, __u16 value) +static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) { int ret; @@ -198,12 +196,6 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, len, 500); } -static void i2c_init(struct gspca_dev *gspca_dev, __u8 mode) -{ - reg_w_val(gspca_dev->dev, 0x92, 0x8804); - reg_w_val(gspca_dev->dev, mode, 0x8802); -} - static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg) { int retry = 60; @@ -212,9 +204,9 @@ static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg) DataLow = valeur; DataHight = valeur >> 8; - reg_w_val(gspca_dev->dev, reg, 0x8801); - reg_w_val(gspca_dev->dev, DataLow, 0x8805); - reg_w_val(gspca_dev->dev, DataHight, 0x8800); + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8805, DataLow); + reg_w_val(gspca_dev->dev, 0x8800, DataHight); while (retry--) { reg_r(gspca_dev, 0x8803, 1); if (!gspca_dev->usb_buf[0]) @@ -228,14 +220,14 @@ static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) __u8 value; __u8 vallsb; - reg_w_val(gspca_dev->dev, 0x92, 0x8804); - reg_w_val(gspca_dev->dev, reg, 0x8801); - reg_w_val(gspca_dev->dev, (mode | 0x01), 0x8802); - while (retry--) { + reg_w_val(gspca_dev->dev, 0x8804, 0x92); + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8802, (mode | 0x01)); + do { reg_r(gspca_dev, 0x8803, 1); if (!gspca_dev->usb_buf) break; - } + } while (--retry); if (retry == 0) return -1; reg_r(gspca_dev, 0x8800, 1); @@ -438,10 +430,11 @@ static const __u16 spca561_init_data[][2] = { {0x0035, 0x8801}, /* 0x14 - set gain general */ {0x001f, 0x8805}, /* 0x14 */ {0x0000, 0x8800}, - {0x0030, 0x8112}, + {0x000e, 0x8112}, /* white balance - was 30 */ {} }; +#if 0 static void sensor_reset(struct gspca_dev *gspca_dev) { reg_w_val(gspca_dev->dev, 0x8631, 0xc8); @@ -449,10 +442,12 @@ static void sensor_reset(struct gspca_dev *gspca_dev) reg_w_val(gspca_dev->dev, 0x8112, 0x00); reg_w_val(gspca_dev->dev, 0x8114, 0x00); reg_w_val(gspca_dev->dev, 0x8118, 0x21); - i2c_init(gspca_dev, 0x14); - i2c_write(gspca_dev, 1, 0x0d); - i2c_write(gspca_dev, 0, 0x0d); + reg_w_val(gspca_dev->dev, 0x8804, 0x92); /* i2c init */ + reg_w_val(gspca_dev->dev, 0x8802, 0x14); + i2c_write(gspca_dev, 0x0001, 0x0d); + i2c_write(gspca_dev, 0x0000, 0x0d); } +#endif /******************** QC Express etch2 stuff ********************/ static const __u16 Pb100_1map8300[][2] = { @@ -462,9 +457,9 @@ static const __u16 Pb100_1map8300[][2] = { {0x8303, 0x0125}, /* image area */ {0x8304, 0x0169}, {0x8328, 0x000b}, - {0x833c, 0x0001}, + {0x833c, 0x0001}, /*fixme: win:07*/ - {0x832f, 0x0419}, + {0x832f, 0x1904}, /*fixme: was 0419*/ {0x8307, 0x00aa}, {0x8301, 0x0003}, {0x8302, 0x000e}, @@ -478,9 +473,10 @@ static const __u16 Pb100_2map8300[][2] = { }; static const __u16 spca561_161rev12A_data1[][2] = { - {0x21, 0x8118}, - {0x01, 0x8114}, - {0x00, 0x8112}, + {0x29, 0x8118}, /* white balance - was 21 */ + {0x08, 0x8114}, /* white balance - was 01 */ + {0x0e, 0x8112}, /* white balance - was 00 */ + {0x00, 0x8102}, /* white balance - new */ {0x92, 0x8804}, {0x04, 0x8802}, /* windows uses 08 */ {} @@ -505,14 +501,16 @@ static const __u16 spca561_161rev12A_data2[][2] = { {0xb0, 0x8603}, /* sensor gains */ + {0x07, 0x8601}, /* white balance - new */ + {0x07, 0x8602}, /* white balance - new */ {0x00, 0x8610}, /* *red */ {0x00, 0x8611}, /* 3f *green */ {0x00, 0x8612}, /* green *blue */ {0x00, 0x8613}, /* blue *green */ - {0x35, 0x8614}, /* green *red */ - {0x35, 0x8615}, /* 40 *green */ - {0x35, 0x8616}, /* 7a *blue */ - {0x35, 0x8617}, /* 40 *green */ + {0x43, 0x8614}, /* green *red - white balance - was 0x35 */ + {0x40, 0x8615}, /* 40 *green - white balance - was 0x35 */ + {0x71, 0x8616}, /* 7a *blue - white balance - was 0x35 */ + {0x40, 0x8617}, /* 40 *green - white balance - was 0x35 */ {0x0c, 0x8620}, /* 0c */ {0xc8, 0x8631}, /* c8 */ @@ -527,6 +525,7 @@ static const __u16 spca561_161rev12A_data2[][2] = { {0xdf, 0x863c}, /* df */ {0xf0, 0x8505}, {0x32, 0x850a}, +/* {0x99, 0x8700}, * - white balance - new (removed) */ {} }; @@ -545,9 +544,10 @@ static void sensor_mapwrite(struct gspca_dev *gspca_dev, } static void init_161rev12A(struct gspca_dev *gspca_dev) { - sensor_reset(gspca_dev); +/* sensor_reset(gspca_dev); (not in win) */ write_vector(gspca_dev, spca561_161rev12A_data1); sensor_mapwrite(gspca_dev, Pb100_1map8300); +/*fixme: should be in sd_start*/ write_vector(gspca_dev, spca561_161rev12A_data2); sensor_mapwrite(gspca_dev, Pb100_2map8300); } @@ -581,35 +581,38 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */ - cam->cam_mode = sif_mode; - cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; sd->chip_revision = id->driver_info; - sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; - sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; - sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + if (sd->chip_revision == Rev012A) { + cam->cam_mode = sif_012a_mode; + cam->nmodes = ARRAY_SIZE(sif_012a_mode); + } else { + cam->cam_mode = sif_072a_mode; + cam->nmodes = ARRAY_SIZE(sif_072a_mode); + } + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->white = WHITE_DEF; + sd->exposure = EXPOSURE_DEF; + sd->autogain = AUTOGAIN_DEF; + sd->gain = GAIN_DEF; + sd->expo12a = EXPO12A_DEF; return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init_12a(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->chip_revision) { - case Rev072A: - PDEBUG(D_STREAM, "Chip revision id: 072a"); - write_vector(gspca_dev, spca561_init_data); - break; - default: -/* case Rev012A: */ - PDEBUG(D_STREAM, "Chip revision id: 012a"); - init_161rev12A(gspca_dev); - break; - } + PDEBUG(D_STREAM, "Chip revision: 012a"); + init_161rev12A(gspca_dev); + return 0; +} +static int sd_init_72a(struct gspca_dev *gspca_dev) +{ + PDEBUG(D_STREAM, "Chip revision: 072a"); + write_vector(gspca_dev, spca561_init_data); return 0; } @@ -618,25 +621,20 @@ static void setcontrast(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; __u8 lowb; - int expotimes; switch (sd->chip_revision) { case Rev072A: lowb = sd->contrast >> 8; - reg_w_val(dev, lowb, 0x8651); - reg_w_val(dev, lowb, 0x8652); - reg_w_val(dev, lowb, 0x8653); - reg_w_val(dev, lowb, 0x8654); + reg_w_val(dev, 0x8651, lowb); + reg_w_val(dev, 0x8652, lowb); + reg_w_val(dev, 0x8653, lowb); + reg_w_val(dev, 0x8654, lowb); break; - case Rev012A: { - __u8 Reg8391[] = - { 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00 }; - - /* Write camera sensor settings */ - expotimes = (sd->contrast >> 5) & 0x07ff; - Reg8391[0] = expotimes & 0xff; /* exposure */ - Reg8391[1] = 0x18 | (expotimes >> 8); - Reg8391[2] = sd->brightness; /* gain */ + default: { +/* case Rev012A: { */ + static const __u8 Reg8391[] = + { 0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00 }; + reg_w_buf(gspca_dev, 0x8391, Reg8391, 8); reg_w_buf(gspca_dev, 0x8390, Reg8391, 8); break; @@ -644,93 +642,151 @@ static void setcontrast(struct gspca_dev *gspca_dev) } } -static void setautogain(struct gspca_dev *gspca_dev) +/* rev12a only */ +static void setwhite(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + __u16 white; + __u8 reg8614, reg8616; + + white = sd->white; + /* try to emulate MS-win as possible */ + reg8616 = 0x90 - white * 5 / 8; + reg_w_val(gspca_dev->dev, 0x8616, reg8616); + reg8614 = 0x20 + white * 3 / 8; + reg_w_val(gspca_dev->dev, 0x8614, reg8614); +} - if (sd->chip_revision == Rev072A) { - if (sd->autogain) - sd->ag_cnt = AG_CNT_START; - else - sd->ag_cnt = -1; +/* rev 12a only */ +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int expo; + int clock_divider; + __u8 data[2]; + + /* Register 0x8309 controls exposure for the spca561, + the basic exposure setting goes from 1-2047, where 1 is completely + dark and 2047 is very bright. It not only influences exposure but + also the framerate (to allow for longer exposure) from 1 - 300 it + only raises the exposure time then from 300 - 600 it halves the + framerate to be able to further raise the exposure time and for every + 300 more it halves the framerate again. This allows for a maximum + exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps). + Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12 + configure a divider for the base framerate which us used at the + exposure setting of 1-300. These bits configure the base framerate + according to the following formula: fps = 60 / (value + 2) */ + if (sd->exposure < 2048) { + expo = sd->exposure; + clock_divider = 0; + } else { + /* Add 900 to make the 0 setting of the second part of the + exposure equal to the 2047 setting of the first part. */ + expo = (sd->exposure - 2048) + 900; + clock_divider = 3; } + expo |= clock_divider << 11; + data[0] = expo; + data[1] = expo >> 8; + reg_w_buf(gspca_dev, 0x8309, data, 2); +} + +/* rev 12a only */ +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data[2]; + + data[0] = sd->gain; + data[1] = 0; + reg_w_buf(gspca_dev, 0x8335, data, 2); } -static void sd_start(struct gspca_dev *gspca_dev) +static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + + if (sd->autogain) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; +} + +static void sd_start_12a(struct gspca_dev *gspca_dev) +{ struct usb_device *dev = gspca_dev->dev; - int Clck; + int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */ __u8 Reg8307[] = { 0xaa, 0x00 }; int mode; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - switch (sd->chip_revision) { - case Rev072A: - switch (mode) { - default: -/* case 0: - case 1: */ - Clck = 0x25; - break; - case 2: - Clck = 0x22; - break; - case 3: - Clck = 0x21; - break; - } - reg_w_val(dev, 0x8500, mode); /* mode */ - reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ - reg_w_val(dev, 0x8112, 0x10 | 0x20); - setautogain(gspca_dev); - break; + if (mode <= 1) { + /* Use compression on 320x240 and above */ + reg_w_val(dev, 0x8500, 0x10 | mode); + } else { + /* I couldn't get the compression to work below 320x240 + * Fortunately at these resolutions the bandwidth + * is sufficient to push raw frames at ~20fps */ + reg_w_val(dev, 0x8500, mode); + } /* -- qq@kuku.eu.org */ + reg_w_buf(gspca_dev, 0x8307, Reg8307, 2); + reg_w_val(gspca_dev->dev, 0x8700, Clck); + /* 0x8f 0x85 0x27 clock */ + reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); + reg_w_val(gspca_dev->dev, 0x850b, 0x03); + setcontrast(gspca_dev); + setwhite(gspca_dev); + setautogain(gspca_dev); + setexposure(gspca_dev); +} +static void sd_start_72a(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int Clck; + int mode; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + switch (mode) { default: -/* case Rev012A: */ - switch (mode) { - case 0: - case 1: - Clck = 0x8a; - break; - case 2: - Clck = 0x85; - break; - default: - Clck = 0x83; - break; - } - if (mode <= 1) { - /* Use compression on 320x240 and above */ - reg_w_val(dev, 0x8500, 0x10 | mode); - } else { - /* I couldn't get the compression to work below 320x240 - * Fortunately at these resolutions the bandwidth - * is sufficient to push raw frames at ~20fps */ - reg_w_val(dev, 0x8500, mode); - } /* -- qq@kuku.eu.org */ - reg_w_buf(gspca_dev, 0x8307, Reg8307, 2); - reg_w_val(gspca_dev->dev, 0x8700, Clck); - /* 0x8f 0x85 0x27 clock */ - reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); - reg_w_val(gspca_dev->dev, 0x850b, 0x03); - setcontrast(gspca_dev); +/* case 0: + case 1: */ + Clck = 0x25; + break; + case 2: + Clck = 0x22; + break; + case 3: + Clck = 0x21; break; } + reg_w_val(dev, 0x8500, mode); /* mode */ + reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ + reg_w_val(dev, 0x8112, 0x10 | 0x20); + setautogain(gspca_dev); } static void sd_stopN(struct gspca_dev *gspca_dev) { - reg_w_val(gspca_dev->dev, 0x8112, 0x20); + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->chip_revision == Rev012A) { + reg_w_val(gspca_dev->dev, 0x8112, 0x0e); + } else { + reg_w_val(gspca_dev->dev, 0x8112, 0x20); +/* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */ + } } static void sd_stop0(struct gspca_dev *gspca_dev) { -} + struct sd *sd = (struct sd *) gspca_dev; -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ - reg_w_val(gspca_dev->dev, 0x8114, 0); + if (sd->chip_revision == Rev012A) { + reg_w_val(gspca_dev->dev, 0x8118, 0x29); + reg_w_val(gspca_dev->dev, 0x8114, 0x08); + } +/* reg_w_val(gspca_dev->dev, 0x8114, 0); */ } static void do_autogain(struct gspca_dev *gspca_dev) @@ -744,6 +800,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) __u8 luma_mean = 110; __u8 luma_delta = 20; __u8 spring = 4; + __u8 reg8339[2]; if (sd->ag_cnt < 0) return; @@ -798,13 +855,16 @@ static void do_autogain(struct gspca_dev *gspca_dev) } break; case Rev012A: - /* sensor registers is access and memory mapped to 0x8300 */ - /* readind all 0x83xx block the sensor */ - /* - * The data from the header seem wrong where is the luma - * and chroma mean value - * at the moment set exposure in contrast set - */ + reg_r(gspca_dev, 0x8330, 2); + if (gspca_dev->usb_buf[1] > 0x08) { + reg8339[0] = ++sd->expo12a; + reg8339[1] = 0; + reg_w_buf(gspca_dev, 0x8339, reg8339, 2); + } else if (gspca_dev->usb_buf[1] < 0x02) { + reg8339[0] = --sd->expo12a; + reg8339[1] = 0; + reg_w_buf(gspca_dev, 0x8339, reg8339, 2); + } break; } } @@ -814,6 +874,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + switch (data[0]) { case 0: /* start of frame */ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, @@ -826,8 +888,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame, data, len); } else { /* raw bayer (with a header, which we skip) */ - data += 20; - len -= 20; + if (sd->chip_revision == Rev012A) { + data += 20; + len -= 20; + } else { + data += 16; + len -= 16; + } gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); } @@ -841,24 +908,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } +/* rev 72a only */ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 value; - switch (sd->chip_revision) { - case Rev072A: - value = sd->brightness; - reg_w_val(gspca_dev->dev, value, 0x8611); - reg_w_val(gspca_dev->dev, value, 0x8612); - reg_w_val(gspca_dev->dev, value, 0x8613); - reg_w_val(gspca_dev->dev, value, 0x8614); - break; - default: -/* case Rev012A: */ - setcontrast(gspca_dev); - break; - } + value = sd->brightness; + reg_w_val(gspca_dev->dev, 0x8611, value); + reg_w_val(gspca_dev->dev, 0x8612, value); + reg_w_val(gspca_dev->dev, 0x8613, value); + reg_w_val(gspca_dev->dev, 0x8614, value); } static void getbrightness(struct gspca_dev *gspca_dev) @@ -866,52 +926,38 @@ static void getbrightness(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; __u16 tot; - switch (sd->chip_revision) { - case Rev072A: - tot = 0; - reg_r(gspca_dev, 0x8611, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8612, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8613, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8614, 1); - tot += gspca_dev->usb_buf[0]; - sd->brightness = tot >> 2; - break; - default: -/* case Rev012A: */ - /* no way to read sensor settings */ - break; - } + tot = 0; + reg_r(gspca_dev, 0x8611, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8612, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8613, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8614, 1); + tot += gspca_dev->usb_buf[0]; + sd->brightness = tot >> 2; } +/* rev72a only */ static void getcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u16 tot; - switch (sd->chip_revision) { - case Rev072A: - tot = 0; - reg_r(gspca_dev, 0x8651, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8652, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8653, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8654, 1); - tot += gspca_dev->usb_buf[0]; - sd->contrast = tot << 6; - break; - default: -/* case Rev012A: */ - /* no way to read sensor settings */ - break; - } + tot = 0; + reg_r(gspca_dev, 0x8651, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8652, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8653, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8654, 1); + tot += gspca_dev->usb_buf[0]; + sd->contrast = tot << 6; PDEBUG(D_CONF, "get contrast %d", sd->contrast); } +/* rev 72a only */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -931,6 +977,7 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +/* rev 72a only */ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -968,20 +1015,190 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +/* rev12a only */ +static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->white = val; + if (gspca_dev->streaming) + setwhite(gspca_dev); + return 0; +} + +static int sd_getwhite(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->white; + return 0; +} + +/* rev12a only */ +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +/* rev12a only */ +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +/* control tables */ +static struct ctrl sd_ctrls_12a[] = { + { + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "While Balance", + .minimum = WHITE_MIN, + .maximum = WHITE_MAX, + .step = 1, + .default_value = WHITE_DEF, + }, + .set = sd_setwhite, + .get = sd_getwhite, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = EXPOSURE_MIN, + .maximum = EXPOSURE_MAX, + .step = 1, + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = AUTOGAIN_MIN, + .maximum = AUTOGAIN_MAX, + .step = 1, + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = GAIN_MIN, + .maximum = GAIN_MAX, + .step = 1, + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, +}; + +static struct ctrl sd_ctrls_72a[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = BRIGHTNESS_MIN, + .maximum = BRIGHTNESS_MAX, + .step = 1, + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = CONTRAST_MIN, + .maximum = CONTRAST_MAX, + .step = 1, + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = AUTOGAIN_MIN, + .maximum = AUTOGAIN_MAX, + .step = 1, + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + /* sub-driver description */ -static const struct sd_desc sd_desc = { +static const struct sd_desc sd_desc_12a = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_12a, + .nctrls = ARRAY_SIZE(sd_ctrls_12a), + .config = sd_config, + .init = sd_init_12a, + .start = sd_start_12a, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +/* .dq_callback = do_autogain, * fixme */ +}; +static const struct sd_desc sd_desc_72a = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), + .ctrls = sd_ctrls_72a, + .nctrls = ARRAY_SIZE(sd_ctrls_72a), .config = sd_config, - .open = sd_open, - .start = sd_start, + .init = sd_init_72a, + .start = sd_start_72a, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, }; +static const struct sd_desc *sd_desc[2] = { + &sd_desc_12a, + &sd_desc_72a +}; /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { @@ -1009,7 +1226,9 @@ MODULE_DEVICE_TABLE(usb, device_table); 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), + return gspca_dev_probe(intf, id, + sd_desc[id->driver_info], + sizeof(struct sd), THIS_MODULE); } @@ -1018,6 +1237,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c index 16219cf6a..2f2de429e 100644 --- a/linux/drivers/media/video/gspca/stk014.c +++ b/linux/drivers/media/video/gspca/stk014.c @@ -306,8 +306,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { int ret; @@ -398,14 +398,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "camera stopped"); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -535,11 +527,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, }; @@ -564,6 +554,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c index 8701b07c8..e8e5c47ba 100644 --- a/linux/drivers/media/video/gspca/sunplus.c +++ b/linux/drivers/media/video/gspca/sunplus.c @@ -480,31 +480,47 @@ static const __u8 qtable_spca504_default[2][64] = { 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} }; -static void reg_r(struct usb_device *dev, - __u16 req, - __u16 index, - __u8 *buffer, __u16 length) +/* read <len> bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 req, + __u16 index, + __u16 len) { - usb_control_msg(dev, - usb_rcvctrlpipe(dev, 0), +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + err("reg_r: buffer overflow"); + return; + } +#endif + 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, buffer, length, + index, + len ? gspca_dev->usb_buf : NULL, len, 500); } -static void reg_w(struct usb_device *dev, - __u16 req, - __u16 value, - __u16 index, - __u8 *buffer, __u16 length) +/* write <len> bytes from gspca_dev->usb_buf */ +static void reg_w(struct gspca_dev *gspca_dev, + __u16 req, + __u16 value, + __u16 index, + __u16 len) { - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + err("reg_w: buffer overflow"); + return; + } +#endif + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, buffer, length, + value, index, + len ? gspca_dev->usb_buf : NULL, len, 500); } @@ -665,7 +681,7 @@ static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev) int count = 10; while (--count > 0) { - reg_r(gspca_dev->dev, 0x21, 0, gspca_dev->usb_buf, 1); + reg_r(gspca_dev, 0x21, 0, 1); if ((gspca_dev->usb_buf[0] & 0x01) == 0) break; msleep(10); @@ -675,15 +691,14 @@ static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev) static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; int count = 50; while (--count > 0) { - reg_r(dev, 0x21, 1, gspca_dev->usb_buf, 1); + reg_r(gspca_dev, 0x21, 1, 1); if (gspca_dev->usb_buf[0] != 0) { gspca_dev->usb_buf[0] = 0; - reg_w(dev, 0x21, 0, 1, gspca_dev->usb_buf, 1); - reg_r(dev, 0x21, 1, gspca_dev->usb_buf, 1); + reg_w(gspca_dev, 0x21, 0, 1, 1); + reg_r(gspca_dev, 0x21, 1, 1); spca504B_PollingDataReady(gspca_dev); break; } @@ -693,16 +708,14 @@ static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; __u8 *data; - data = kmalloc(64, GFP_KERNEL); - reg_r(dev, 0x20, 0, data, 5); + data = gspca_dev->usb_buf; + reg_r(gspca_dev, 0x20, 0, 5); PDEBUG(D_STREAM, "FirmWare : %d %d %d %d %d ", data[0], data[1], data[2], data[3], data[4]); - reg_r(dev, 0x23, 0, data, 64); - reg_r(dev, 0x23, 1, data, 64); - kfree(data); + reg_r(gspca_dev, 0x23, 0, 64); + reg_r(gspca_dev, 0x23, 1, 64); } static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) @@ -717,21 +730,21 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) Type = 0; switch (sd->bridge) { case BRIDGE_SPCA533: - reg_w(dev, 0x31, 0, 0, NULL, 0); + reg_w(gspca_dev, 0x31, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); spca50x_GetFirmware(gspca_dev); gspca_dev->usb_buf[0] = 2; /* type */ - reg_w(dev, 0x24, 0, 8, gspca_dev->usb_buf, 1); - reg_r(dev, 0x24, 8, gspca_dev->usb_buf, 1); + reg_w(gspca_dev, 0x24, 0, 8, 1); + reg_r(gspca_dev, 0x24, 8, 1); gspca_dev->usb_buf[0] = Size; - reg_w(dev, 0x25, 0, 4, gspca_dev->usb_buf, 1); - reg_r(dev, 0x25, 4, gspca_dev->usb_buf, 1); /* size */ + reg_w(gspca_dev, 0x25, 0, 4, 1); + reg_r(gspca_dev, 0x25, 4, 1); /* size */ rc = spca504B_PollingDataReady(gspca_dev); /* Init the cam width height with some values get on init ? */ - reg_w(dev, 0x31, 0, 4, NULL, 0); + reg_w(gspca_dev, 0x31, 0, 4, 0); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); break; @@ -739,12 +752,12 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA504B: */ /* case BRIDGE_SPCA536: */ gspca_dev->usb_buf[0] = Size; - reg_w(dev, 0x25, 0, 4, gspca_dev->usb_buf, 1); - reg_r(dev, 0x25, 4, gspca_dev->usb_buf, 1); /* size */ + reg_w(gspca_dev, 0x25, 0, 4, 1); + reg_r(gspca_dev, 0x25, 4, 1); /* size */ Type = 6; gspca_dev->usb_buf[0] = Type; - reg_w(dev, 0x27, 0, 0, gspca_dev->usb_buf, 1); - reg_r(dev, 0x27, 0, gspca_dev->usb_buf, 1); /* type */ + reg_w(gspca_dev, 0x27, 0, 0, 1); + reg_r(gspca_dev, 0x27, 0, 1); /* type */ rc = spca504B_PollingDataReady(gspca_dev); break; case BRIDGE_SPCA504: @@ -783,18 +796,15 @@ static void spca504_wait_status(struct gspca_dev *gspca_dev) static void spca504B_setQtable(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; - gspca_dev->usb_buf[0] = 3; - reg_w(dev, 0x26, 0, 0, gspca_dev->usb_buf, 1); - reg_r(dev, 0x26, 0, gspca_dev->usb_buf, 1); + reg_w(gspca_dev, 0x26, 0, 0, 1); + reg_r(gspca_dev, 0x26, 0, 1); spca504B_PollingDataReady(gspca_dev); } static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int pollreg = 1; switch (sd->bridge) { @@ -805,20 +815,20 @@ static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) default: /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA504B: */ - reg_w(dev, 0, 0, 0x21a7, NULL, 0); /* brightness */ - reg_w(dev, 0, 0x20, 0x21a8, NULL, 0); /* contrast */ - reg_w(dev, 0, 0, 0x21ad, NULL, 0); /* hue */ - reg_w(dev, 0, 1, 0x21ac, NULL, 0); /* sat/hue */ - reg_w(dev, 0, 0x20, 0x21ae, NULL, 0); /* saturation */ - reg_w(dev, 0, 0, 0x21a3, NULL, 0); /* gamma */ + reg_w(gspca_dev, 0, 0, 0x21a7, 0); /* brightness */ + reg_w(gspca_dev, 0, 0x20, 0x21a8, 0); /* contrast */ + reg_w(gspca_dev, 0, 0, 0x21ad, 0); /* hue */ + reg_w(gspca_dev, 0, 1, 0x21ac, 0); /* sat/hue */ + reg_w(gspca_dev, 0, 0x20, 0x21ae, 0); /* saturation */ + reg_w(gspca_dev, 0, 0, 0x21a3, 0); /* gamma */ break; case BRIDGE_SPCA536: - reg_w(dev, 0, 0, 0x20f0, NULL, 0); - reg_w(dev, 0, 0x21, 0x20f1, NULL, 0); - reg_w(dev, 0, 0x40, 0x20f5, NULL, 0); - reg_w(dev, 0, 1, 0x20f4, NULL, 0); - reg_w(dev, 0, 0x40, 0x20f6, NULL, 0); - reg_w(dev, 0, 0, 0x2089, NULL, 0); + reg_w(gspca_dev, 0, 0, 0x20f0, 0); + reg_w(gspca_dev, 0, 0x21, 0x20f1, 0); + reg_w(gspca_dev, 0, 0x40, 0x20f5, 0); + reg_w(gspca_dev, 0, 1, 0x20f4, 0); + reg_w(gspca_dev, 0, 0x40, 0x20f6, 0); + reg_w(gspca_dev, 0, 0, 0x2089, 0); break; } if (pollreg) @@ -830,7 +840,6 @@ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; struct cam *cam; cam = &gspca_dev->cam; @@ -842,7 +851,7 @@ static int sd_config(struct gspca_dev *gspca_dev, if (sd->subtype == AiptekMiniPenCam13) { /* try to get the firmware as some cam answer 2.0.1.2.2 * and should be a spca504b then overwrite that setting */ - reg_r(dev, 0x20, 0, gspca_dev->usb_buf, 1); + reg_r(gspca_dev, 0x20, 0, 1); switch (gspca_dev->usb_buf[0]) { case 1: break; /* (right bridge/subtype) */ @@ -879,8 +888,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; @@ -891,12 +900,12 @@ static int sd_open(struct gspca_dev *gspca_dev) switch (sd->bridge) { case BRIDGE_SPCA504B: - reg_w(dev, 0x1d, 0, 0, NULL, 0); - reg_w(dev, 0, 1, 0x2306, NULL, 0); - reg_w(dev, 0, 0, 0x0d04, NULL, 0); - reg_w(dev, 0, 0, 0x2000, NULL, 0); - reg_w(dev, 0, 0x13, 0x2301, NULL, 0); - reg_w(dev, 0, 0, 0x2306, NULL, 0); + reg_w(gspca_dev, 0x1d, 0, 0, 0); + reg_w(gspca_dev, 0, 1, 0x2306, 0); + reg_w(gspca_dev, 0, 0, 0x0d04, 0); + reg_w(gspca_dev, 0, 0, 0x2000, 0); + reg_w(gspca_dev, 0, 0x13, 0x2301, 0); + reg_w(gspca_dev, 0, 0, 0x2306, 0); /* fall thru */ case BRIDGE_SPCA533: rc = spca504B_PollingDataReady(gspca_dev); @@ -904,12 +913,12 @@ static int sd_open(struct gspca_dev *gspca_dev) break; case BRIDGE_SPCA536: spca50x_GetFirmware(gspca_dev); - reg_r(dev, 0x00, 0x5002, gspca_dev->usb_buf, 1); + reg_r(gspca_dev, 0x00, 0x5002, 1); gspca_dev->usb_buf[0] = 0; - reg_w(dev, 0x24, 0, 0, gspca_dev->usb_buf, 1); - reg_r(dev, 0x24, 0, gspca_dev->usb_buf, 1); + reg_w(gspca_dev, 0x24, 0, 0, 1); + reg_r(gspca_dev, 0x24, 0, 1); rc = spca504B_PollingDataReady(gspca_dev); - reg_w(dev, 0x34, 0, 0, NULL, 0); + reg_w(gspca_dev, 0x34, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); break; case BRIDGE_SPCA504C: /* pccam600 */ @@ -1002,12 +1011,12 @@ static void sd_start(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA536: */ if (sd->subtype == MegapixV4 || sd->subtype == LogitechClickSmart820) { - reg_w(dev, 0xf0, 0, 0, NULL, 0); + reg_w(gspca_dev, 0xf0, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); - reg_r(dev, 0xf0, 4, NULL, 0); + reg_r(gspca_dev, 0xf0, 4, 0); spca504B_WaitCmdStatus(gspca_dev); } else { - reg_w(dev, 0x31, 0, 4, NULL, 0); + reg_w(gspca_dev, 0x31, 0, 4, 0); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); } @@ -1076,7 +1085,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA536: */ /* case BRIDGE_SPCA504B: */ - reg_w(dev, 0x31, 0, 0, NULL, 0); + reg_w(gspca_dev, 0x31, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); spca504B_PollingDataReady(gspca_dev); break; @@ -1100,14 +1109,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1400,11 +1401,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -1487,6 +1486,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/t613.c b/linux/drivers/media/video/gspca/t613.c index 56b1dfa72..c7673661a 100644 --- a/linux/drivers/media/video/gspca/t613.c +++ b/linux/drivers/media/video/gspca/t613.c @@ -30,7 +30,7 @@ #define MAX_GAMMA 0x10 /* 0 to 15 */ -#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 0) MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>"); MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver"); @@ -233,7 +233,7 @@ static char *effects_control[] = { static struct v4l2_pix_format vga_mode_t16[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 8 + 590, + .sizeimage = 160 * 120 * 4 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 4}, {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -391,7 +391,7 @@ static void reg_w(struct gspca_dev *gspca_dev, NULL, 0, 500); return; } - if (len <= sizeof gspca_dev->usb_buf) { + if (len <= USB_BUF_SZ) { memcpy(gspca_dev->usb_buf, buffer, len); usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -563,6 +563,13 @@ static int init_default_parameters(struct gspca_dev *gspca_dev) return 0; } +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + init_default_parameters(gspca_dev); + return 0; +} + static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -916,18 +923,6 @@ static void sd_start(struct gspca_dev *gspca_dev) #endif } -static void sd_stopN(struct gspca_dev *gspca_dev) -{ -} - -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -995,24 +990,14 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) -{ - init_default_parameters(gspca_dev); - return 0; -} - /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, }; @@ -1037,6 +1022,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/tv8532.c b/linux/drivers/media/video/gspca/tv8532.c index fcac077bb..01de48d4e 100644 --- a/linux/drivers/media/video/gspca/tv8532.c +++ b/linux/drivers/media/video/gspca/tv8532.c @@ -331,8 +331,8 @@ static void tv_8532_PollReg(struct gspca_dev *gspca_dev) } } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32); reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00); @@ -450,14 +450,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void tv8532_preprocess(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -620,11 +612,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -653,6 +643,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 0e23edb16..cfb66c257 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -69,6 +69,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, +#define LIGHTFREQ_IDX 1 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -79,7 +80,6 @@ static struct ctrl sd_ctrls[] = { .step = 1, #define FREQ_DEF 1 .default_value = FREQ_DEF, - .default_value = 1, }, .set = sd_setfreq, .get = sd_getfreq, @@ -87,12 +87,12 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vc0321_mode[] = { - {320, 240, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE, + {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE, + {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_SRGB, @@ -1643,6 +1643,8 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->qindex = 7; sd->autogain = AUTOGAIN_DEF; sd->lightfreq = FREQ_DEF; + if (sd->sensor != SENSOR_OV7670) + gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); if (sd->bridge == BRIDGE_VC0321) { reg_r(gspca_dev, 0x8a, 0, 3); @@ -1654,8 +1656,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and time */ +static int sd_init(struct gspca_dev *gspca_dev) { return 0; } @@ -1834,19 +1836,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w(dev, 0x89, 0xffff, 0xffff); } -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ -/* struct usb_device *dev = gspca_dev->dev; - __u8 buffread; - - reg_w(dev, 0x89, 0xffff, 0xffff); - reg_w(dev, 0xa0, 0x01, 0xb301); - reg_w(dev, 0xa0, 0x09, 0xb303); - reg_w(dev, 0x89, 0xffff, 0xffff); -*/ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1935,11 +1924,10 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, }; @@ -1971,6 +1959,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index ff0c30e4a..3153bd4cd 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -85,6 +85,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +#define BRIGHTNESS_IDX 0 #define SD_BRIGHTNESS 0 { { @@ -141,6 +142,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, +#define LIGHTFREQ_IDX 4 #define SD_FREQ 4 { { @@ -6579,8 +6581,8 @@ static int setlightfreq(struct gspca_dev *gspca_dev) cs2102_60HZ, cs2102_60HZScale}, /* SENSOR_CS2102K 1 */ {cs2102_NoFliker, cs2102_NoFlikerScale, - cs2102_50HZ, cs2102_50HZScale, - cs2102_60HZ, cs2102_60HZScale}, + NULL, NULL, /* currently disabled */ + NULL, NULL}, /* SENSOR_GC0305 2 */ {gc0305_NoFliker, gc0305_NoFliker, gc0305_50HZ, gc0305_50HZ, @@ -6969,8 +6971,13 @@ static int zcxx_probeSensor(struct gspca_dev *gspca_dev) case SENSOR_MC501CB: return -1; /* don't probe */ case SENSOR_TAS5130C_VF0250: - /* may probe but with write in reg 0x0010 */ + /* may probe but with no write in reg 0x0010 */ return -1; /* don't probe */ + case SENSOR_PAS106: + sensor = sif_probe(gspca_dev); + if (sensor >= 0) + return sensor; + break; } sensor = vga_2wr_probe(gspca_dev); if (sensor >= 0) { @@ -6979,12 +6986,10 @@ static int zcxx_probeSensor(struct gspca_dev *gspca_dev) /* next probe is needed for OmniVision ? */ } sensor2 = vga_3wr_probe(gspca_dev); - if (sensor2 >= 0) { - if (sensor >= 0) - return sensor; - return sensor2; - } - return sif_probe(gspca_dev); + if (sensor2 >= 0 + && sensor >= 0) + return sensor; + return sensor2; } /* this function is called at probe time */ @@ -7152,13 +7157,27 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; + switch (sd->sensor) { + case SENSOR_GC0305: + case SENSOR_OV7620: + case SENSOR_PO2030: + gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); + break; + case SENSOR_HDCS2020: + case SENSOR_HV7131B: + case SENSOR_HV7131C: + case SENSOR_OV7630C: + gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); + break; + } + /* switch the led off */ reg_w(gspca_dev->dev, 0x01, 0x0000); return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { reg_w(gspca_dev->dev, 0x01, 0x0000); return 0; @@ -7319,10 +7338,6 @@ static void sd_start(struct gspca_dev *gspca_dev) } } -static void sd_stopN(struct gspca_dev *gspca_dev) -{ -} - static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -7330,18 +7345,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) send_unknown(gspca_dev->dev, sd->sensor); } -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ -#if 0 /* test */ - struct usb_device *dev = gspca_dev->dev; - - reg_r(gspca_dev, 0x0180); - reg_w(dev, 0x00, 0x0180); - reg_w(dev, 0x01, 0x0000); -#endif -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u8 *data, @@ -7501,37 +7504,30 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0], .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, - .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, }; static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x041e)}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x041e, 0x4017)}, - {USB_DEVICE(0x041e, 0x401c)}, + {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x041e, 0x401e)}, {USB_DEVICE(0x041e, 0x401f)}, -#endif + {USB_DEVICE(0x041e, 0x4022)}, {USB_DEVICE(0x041e, 0x4029)}, -#ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x041e, 0x4034)}, - {USB_DEVICE(0x041e, 0x4035)}, + {USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x041e, 0x4036)}, {USB_DEVICE(0x041e, 0x403a)}, -#endif {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_TAS5130C_VF0250}, {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_TAS5130C_VF0250}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0458, 0x7007)}, {USB_DEVICE(0x0458, 0x700c)}, {USB_DEVICE(0x0458, 0x700f)}, -#endif {USB_DEVICE(0x0461, 0x0a00)}, {USB_DEVICE(0x046d, 0x08a0)}, {USB_DEVICE(0x046d, 0x08a1)}, @@ -7543,7 +7539,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x08aa)}, {USB_DEVICE(0x046d, 0x08ac)}, {USB_DEVICE(0x046d, 0x08ad)}, -#ifndef CONFIG_USB_ZC0301 +#if !defined CONFIG_USB_ZC0301 && !defined CONFIG_USB_ZC0301_MODULE {USB_DEVICE(0x046d, 0x08ae)}, #endif {USB_DEVICE(0x046d, 0x08af)}, @@ -7553,27 +7549,25 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x08d8)}, {USB_DEVICE(0x046d, 0x08da)}, {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB}, - {USB_DEVICE(0x0471, 0x0325)}, - {USB_DEVICE(0x0471, 0x0326)}, - {USB_DEVICE(0x0471, 0x032d)}, - {USB_DEVICE(0x0471, 0x032e)}, + {USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x055f, 0xc005)}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x055f, 0xd003)}, {USB_DEVICE(0x055f, 0xd004)}, -#endif {USB_DEVICE(0x0698, 0x2003)}, + {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x0ac8, 0x0302)}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0ac8, 0x301b)}, +#if !defined CONFIG_USB_ZC0301 && !defined CONFIG_USB_ZC0301_MODULE {USB_DEVICE(0x0ac8, 0x303b)}, #endif {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0ac8, 0x307b)}, {USB_DEVICE(0x10fd, 0x0128)}, + {USB_DEVICE(0x10fd, 0x804d)}, {USB_DEVICE(0x10fd, 0x8050)}, -#endif {} /* end of entry */ }; #undef DVNAME @@ -7593,6 +7587,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; static int __init sd_mod_init(void) diff --git a/linux/drivers/media/video/ivtv/ivtv-cards.h b/linux/drivers/media/video/ivtv/ivtv-cards.h index 381af1bce..0b8fe85fb 100644 --- a/linux/drivers/media/video/ivtv/ivtv-cards.h +++ b/linux/drivers/media/video/ivtv/ivtv-cards.h @@ -154,7 +154,7 @@ #define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \ V4L2_CAP_SLICED_VBI_CAPTURE) -#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \ +#define IVTV_CAP_DECODER (V4L2_CAP_VIDEO_OUTPUT | \ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY) struct ivtv_card_video_input { diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index efdb59193..def2c5502 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -655,9 +655,9 @@ done: if (itv->card == NULL) { itv->card = ivtv_get_card(IVTV_CARD_PVR_150); - IVTV_ERR("Unknown card: vendor/device: %04x/%04x\n", + IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n", itv->dev->vendor, itv->dev->device); - IVTV_ERR(" subsystem vendor/device: %04x/%04x\n", + IVTV_ERR(" subsystem vendor/device: [%04x:%04x]\n", itv->dev->subsystem_vendor, itv->dev->subsystem_device); IVTV_ERR(" %s based\n", chipname); IVTV_ERR("Defaulting to %s card\n", itv->card->name); @@ -688,7 +688,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) spin_lock_init(&itv->lock); spin_lock_init(&itv->dma_reg_lock); - itv->irq_work_queues = create_workqueue(itv->name); + itv->irq_work_queues = create_singlethread_workqueue(itv->name); if (itv->irq_work_queues == NULL) { IVTV_ERR("Could not create ivtv workqueue\n"); return -1; diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h index d66ee14b6..80673a3da 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.h +++ b/linux/drivers/media/video/ivtv/ivtv-driver.h @@ -49,7 +49,6 @@ #include <linux/i2c-algo-bit.h> #include <linux/list.h> #include <linux/unistd.h> -#include <linux/byteorder/swab.h> #include <linux/pagemap.h> #include <linux/scatterlist.h> #include <linux/workqueue.h> @@ -252,6 +251,7 @@ struct ivtv_mailbox_data { #define IVTV_F_I_DEC_PAUSED 20 /* the decoder is paused */ #define IVTV_F_I_INITED 21 /* set after first open */ #define IVTV_F_I_FAILED 22 /* set if first open failed */ +#define IVTV_F_I_WORK_INITED 23 /* worker thread was initialized */ /* Event notifications */ #define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c index 1488bc7ce..180f541f0 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.c +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c @@ -64,8 +64,6 @@ #include "ivtv-gpio.h" #include "ivtv-i2c.h" -#include <media/ir-kbd-i2c.h> - /* i2c implementation for cx23415/6 chip, ivtv project. * Author: Kevin Thayer (nufan_wfk at yahoo.com) */ diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c index 61030309d..f00854ad6 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c @@ -101,18 +101,15 @@ void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) } } -static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +static void check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) { int f, l; - u16 set = 0; for (f = 0; f < 2; f++) { for (l = 0; l < 24; l++) { fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); - set |= fmt->service_lines[f][l]; } } - return set != 0; } u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt) @@ -474,7 +471,7 @@ static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format int h = fmt->fmt.pix.height; w = min(w, 720); - w = max(w, 1); + w = max(w, 2); h = min(h, itv->is_50hz ? 576 : 480); h = max(h, 2); ivtv_g_fmt_vid_cap(file, fh, fmt); @@ -512,27 +509,20 @@ static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) { struct ivtv_open_id *id = fh; - s32 w, h; - int field; - int ret; + struct ivtv *itv = id->itv; + s32 w = fmt->fmt.pix.width; + s32 h = fmt->fmt.pix.height; + int field = fmt->fmt.pix.field; + int ret = ivtv_g_fmt_vid_out(file, fh, fmt); - w = fmt->fmt.pix.width; - h = fmt->fmt.pix.height; - field = fmt->fmt.pix.field; - ret = ivtv_g_fmt_vid_out(file, fh, fmt); + w = min(w, 720); + w = max(w, 2); + h = min(h, itv->is_out_50hz ? 576 : 480); + h = max(h, 2); + if (id->type == IVTV_DEC_STREAM_TYPE_YUV) + fmt->fmt.pix.field = field; fmt->fmt.pix.width = w; fmt->fmt.pix.height = h; - if (!ret && id->type == IVTV_DEC_STREAM_TYPE_YUV) { - fmt->fmt.pix.field = field; - if (fmt->fmt.pix.width < 2) - fmt->fmt.pix.width = 2; - if (fmt->fmt.pix.width > 720) - fmt->fmt.pix.width = 720; - if (fmt->fmt.pix.height < 2) - fmt->fmt.pix.height = 2; - if (fmt->fmt.pix.height > 576) - fmt->fmt.pix.height = 576; - } return ret; } @@ -560,9 +550,9 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f struct ivtv_open_id *id = fh; struct ivtv *itv = id->itv; struct cx2341x_mpeg_params *p = &itv->params; + int ret = ivtv_try_fmt_vid_cap(file, fh, fmt); int w = fmt->fmt.pix.width; int h = fmt->fmt.pix.height; - int ret = ivtv_try_fmt_vid_cap(file, fh, fmt); if (ret) return ret; @@ -600,8 +590,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI) return ret; - if (check_service_set(vbifmt, itv->is_50hz) == 0) - return -EINVAL; + check_service_set(vbifmt, itv->is_50hz); if (atomic_read(&itv->capturing) > 0) return -EBUSY; itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c index d5ac766c1..59fe56171 100644 --- a/linux/drivers/media/video/ivtv/ivtv-irq.c +++ b/linux/drivers/media/video/ivtv/ivtv-irq.c @@ -82,6 +82,13 @@ void ivtv_irq_work_handler(void *arg) DEFINE_WAIT(wait); + if (test_and_clear_bit(IVTV_F_I_WORK_INITED, &itv->i_flags)) { + struct sched_param param = { .sched_priority = 99 }; + + /* This thread must use the FIFO scheduler as it + is realtime sensitive. */ + sched_setscheduler(current, SCHED_FIFO, ¶m); + } if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags)) ivtv_pio_work_handler(itv); @@ -684,34 +691,14 @@ static void ivtv_irq_enc_start_cap(struct ivtv *itv) static void ivtv_irq_enc_vbi_cap(struct ivtv *itv) { - struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG]; u32 data[CX2341X_MBOX_MAX_DATA]; struct ivtv_stream *s; IVTV_DEBUG_HI_IRQ("ENC START VBI CAP\n"); s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; - /* If more than two VBI buffers are pending, then - clear the old ones and start with this new one. - This can happen during transition stages when MPEG capturing is - started, but the first interrupts haven't arrived yet. During - that period VBI requests can accumulate without being able to - DMA the data. Since at most four VBI DMA buffers are available, - we just drop the old requests when there are already three - requests queued. */ - if (s->sg_pending_size > 2) { - struct ivtv_buffer *buf; - list_for_each_entry(buf, &s->q_predma.list, list) - ivtv_buf_sync_for_cpu(s, buf); - ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0); - s->sg_pending_size = 0; - } - /* if we can append the data, and the MPEG stream isn't capturing, - then start a DMA request for just the VBI data. */ - if (!stream_enc_dma_append(s, data) && - !test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) { + if (!stream_enc_dma_append(s, data)) set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags); - } } static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv) diff --git a/linux/drivers/media/video/ivtv/ivtv-queue.h b/linux/drivers/media/video/ivtv/ivtv-queue.h index 7cfc0c9ab..476556afd 100644 --- a/linux/drivers/media/video/ivtv/ivtv-queue.h +++ b/linux/drivers/media/video/ivtv/ivtv-queue.h @@ -23,7 +23,7 @@ #define IVTV_QUEUE_H #define IVTV_DMA_UNMAPPED ((u32) -1) -#define SLICED_VBI_PIO 1 +#define SLICED_VBI_PIO 0 /* ivtv_buffer utility functions */ diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index 54d2023b2..730e85d86 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -363,7 +363,7 @@ static void ivtv_vbi_setup(struct ivtv *itv) /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ data[1] = 1; /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */ - data[2] = raw ? 4 : 8; + data[2] = raw ? 4 : 4 * (itv->vbi.raw_size / itv->vbi.enc_size); /* The start/stop codes determine which VBI lines end up in the raw VBI data area. The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video) diff --git a/linux/drivers/media/video/ivtv/ivtv-vbi.c b/linux/drivers/media/video/ivtv/ivtv-vbi.c index 71798f0da..1ce9deb11 100644 --- a/linux/drivers/media/video/ivtv/ivtv-vbi.c +++ b/linux/drivers/media/video/ivtv/ivtv-vbi.c @@ -293,6 +293,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 u32 line_size = itv->vbi.sliced_decoder_line_size; struct v4l2_decode_vbi_line vbi; int i; + unsigned lines = 0; /* find the first valid line */ for (i = 0; i < size; i++, buf++) { @@ -313,7 +314,8 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 } vbi.p = p + 4; itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi); - if (vbi.type) { + if (vbi.type && !(lines & (1 << vbi.line))) { + lines |= 1 << vbi.line; itv->vbi.sliced_data[line].id = vbi.type; itv->vbi.sliced_data[line].field = vbi.is_second_field; itv->vbi.sliced_data[line].line = vbi.line; diff --git a/linux/drivers/media/video/ivtv/ivtv-version.h b/linux/drivers/media/video/ivtv/ivtv-version.h index 442f43f11..8cd753d30 100644 --- a/linux/drivers/media/video/ivtv/ivtv-version.h +++ b/linux/drivers/media/video/ivtv/ivtv-version.h @@ -22,7 +22,7 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 -#define IVTV_DRIVER_VERSION_MINOR 3 +#define IVTV_DRIVER_VERSION_MINOR 4 #define IVTV_DRIVER_VERSION_PATCHLEVEL 0 #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/linux/drivers/media/video/ks0127.c b/linux/drivers/media/video/ks0127.c index 90451230d..b21b6f097 100644 --- a/linux/drivers/media/video/ks0127.c +++ b/linux/drivers/media/video/ks0127.c @@ -680,26 +680,27 @@ static int ks0127_command(struct i2c_client *client, case DECODER_ENABLE_OUTPUT: { + int enable; - int *iarg = arg; - int enable = (*iarg != 0); - if (enable) { - dprintk("ks0127: command " + iarg = arg; + enable = (*iarg != 0); + if (enable) { + dprintk("ks0127: command " "DECODER_ENABLE_OUTPUT on " "(%d)\n", enable); - /* All output pins on */ - ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x30); - /* Obey the OEN pin */ - ks0127_and_or(ks, KS_CDEM, 0x7f, 0x00); - } else { - dprintk("ks0127: command " + /* All output pins on */ + ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x30); + /* Obey the OEN pin */ + ks0127_and_or(ks, KS_CDEM, 0x7f, 0x00); + } else { + dprintk("ks0127: command " "DECODER_ENABLE_OUTPUT off " "(%d)\n", enable); - /* Video output pins off */ - ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x00); - /* Ignore the OEN pin */ - ks0127_and_or(ks, KS_CDEM, 0x7f, 0x80); - } + /* Video output pins off */ + ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x00); + /* Ignore the OEN pin */ + ks0127_and_or(ks, KS_CDEM, 0x7f, 0x80); + } } break; diff --git a/linux/drivers/media/video/meye.c b/linux/drivers/media/video/meye.c index a0cdae0c1..535443887 100644 --- a/linux/drivers/media/video/meye.c +++ b/linux/drivers/media/video/meye.c @@ -848,21 +848,16 @@ again: static int meye_open(struct inode *inode, struct file *file) { - int i, err; + int i; - lock_kernel(); - err = video_exclusive_open(inode, file); - if (err < 0) { - unlock_kernel(); - return err; - } + if (test_and_set_bit(0, &meye.in_use)) + return -EBUSY; mchip_hic_stop(); if (mchip_dma_alloc()) { printk(KERN_ERR "meye: mchip framebuffer allocation failed\n"); - video_exclusive_release(inode, file); - unlock_kernel(); + clear_bit(0, &meye.in_use); return -ENOBUFS; } @@ -870,7 +865,6 @@ static int meye_open(struct inode *inode, struct file *file) meye.grab_buffer[i].state = MEYE_BUF_UNUSED; kfifo_reset(meye.grabq); kfifo_reset(meye.doneq); - unlock_kernel(); return 0; } @@ -878,7 +872,7 @@ static int meye_release(struct inode *inode, struct file *file) { mchip_hic_stop(); mchip_dma_free(); - video_exclusive_release(inode, file); + clear_bit(0, &meye.in_use); return 0; } @@ -1784,6 +1778,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev, goto outnotdev; } + ret = -ENOMEM; meye.mchip_dev = pcidev; meye.video_dev = video_device_alloc(); if (!meye.video_dev) { @@ -1791,7 +1786,6 @@ static int __devinit meye_probe(struct pci_dev *pcidev, goto outnotdev; } - ret = -ENOMEM; meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE); if (!meye.grab_temp) { printk(KERN_ERR "meye: grab buffer allocation failed\n"); @@ -1816,6 +1810,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev, memcpy(meye.video_dev, &meye_template, sizeof(meye_template)); meye.video_dev->parent = &meye.mchip_dev->dev; + ret = -EIO; if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) { printk(KERN_ERR "meye: unable to power on the camera\n"); printk(KERN_ERR "meye: did you enable the camera in " @@ -1823,7 +1818,6 @@ static int __devinit meye_probe(struct pci_dev *pcidev, goto outsonypienable; } - ret = -EIO; if ((ret = pci_enable_device(meye.mchip_dev))) { printk(KERN_ERR "meye: pci_enable_device failed\n"); goto outenabledev; diff --git a/linux/drivers/media/video/meye.h b/linux/drivers/media/video/meye.h index 4c0c031d3..b4a589f21 100644 --- a/linux/drivers/media/video/meye.h +++ b/linux/drivers/media/video/meye.h @@ -315,6 +315,7 @@ struct meye { struct video_device *video_dev; /* video device parameters */ struct video_picture picture; /* video picture parameters */ struct meye_params params; /* additional parameters */ + unsigned long in_use; /* set to 1 if the device is in use */ #ifdef CONFIG_PM u8 pm_mchip_mode; /* old mchip mode */ #endif diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c index f4742936c..228183c54 100644 --- a/linux/drivers/media/video/mt9m001.c +++ b/linux/drivers/media/video/mt9m001.c @@ -117,24 +117,51 @@ static int reg_clear(struct soc_camera_device *icd, const u8 reg, static int mt9m001_init(struct soc_camera_device *icd) { + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; int ret; - /* Disable chip, synchronous option update */ dev_dbg(icd->vdev->parent, "%s\n", __func__); - ret = reg_write(icd, MT9M001_RESET, 1); - if (ret >= 0) - ret = reg_write(icd, MT9M001_RESET, 0); - if (ret >= 0) + if (icl->power) { + ret = icl->power(&mt9m001->client->dev, 1); + if (ret < 0) { + dev_err(icd->vdev->parent, + "Platform failed to power-on the camera.\n"); + return ret; + } + } + + /* The camera could have been already on, we reset it additionally */ + if (icl->reset) + ret = icl->reset(&mt9m001->client->dev); + else + ret = -ENODEV; + + if (ret < 0) { + /* Either no platform reset, or platform reset failed */ + ret = reg_write(icd, MT9M001_RESET, 1); + if (!ret) + ret = reg_write(icd, MT9M001_RESET, 0); + } + /* Disable chip, synchronous option update */ + if (!ret) ret = reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); - return ret >= 0 ? 0 : -EIO; + return ret; } static int mt9m001_release(struct soc_camera_device *icd) { + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + /* Disable the chip */ reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); + + if (icl->power) + icl->power(&mt9m001->client->dev, 0); + return 0; } @@ -267,24 +294,24 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, /* Blanking and start values - default... */ ret = reg_write(icd, MT9M001_HORIZONTAL_BLANKING, hblank); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank); /* The caller provides a supported format, as verified per * call to icd->try_fmt_cap() */ - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_COLUMN_START, rect->left); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_ROW_START, rect->top); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_WINDOW_WIDTH, rect->width - 1); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_WINDOW_HEIGHT, rect->height + icd->y_skip_top - 1); - if (ret >= 0 && mt9m001->autoexposure) { + if (!ret && mt9m001->autoexposure) { ret = reg_write(icd, MT9M001_SHUTTER_WIDTH, rect->height + icd->y_skip_top + vblank); - if (ret >= 0) { + if (!ret) { const struct v4l2_queryctrl *qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); @@ -295,7 +322,7 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, } } - return ret < 0 ? ret : 0; + return ret; } static int mt9m001_try_fmt_cap(struct soc_camera_device *icd, diff --git a/linux/drivers/media/video/mt9m111.c b/linux/drivers/media/video/mt9m111.c new file mode 100644 index 000000000..ceff40edc --- /dev/null +++ b/linux/drivers/media/video/mt9m111.c @@ -0,0 +1,981 @@ +/* + * Driver for MT9M111 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr> + * + * 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/videodev2.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/log2.h> +#include <linux/gpio.h> +#include <linux/delay.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +/* + * mt9m111 i2c address is 0x5d or 0x48 (depending on SAddr pin) + * The platform has to define i2c_board_info and call i2c_register_board_info() + */ + +/* mt9m111: Sensor register addresses */ +#define MT9M111_CHIP_VERSION 0x000 +#define MT9M111_ROW_START 0x001 +#define MT9M111_COLUMN_START 0x002 +#define MT9M111_WINDOW_HEIGHT 0x003 +#define MT9M111_WINDOW_WIDTH 0x004 +#define MT9M111_HORIZONTAL_BLANKING_B 0x005 +#define MT9M111_VERTICAL_BLANKING_B 0x006 +#define MT9M111_HORIZONTAL_BLANKING_A 0x007 +#define MT9M111_VERTICAL_BLANKING_A 0x008 +#define MT9M111_SHUTTER_WIDTH 0x009 +#define MT9M111_ROW_SPEED 0x00a +#define MT9M111_EXTRA_DELAY 0x00b +#define MT9M111_SHUTTER_DELAY 0x00c +#define MT9M111_RESET 0x00d +#define MT9M111_READ_MODE_B 0x020 +#define MT9M111_READ_MODE_A 0x021 +#define MT9M111_FLASH_CONTROL 0x023 +#define MT9M111_GREEN1_GAIN 0x02b +#define MT9M111_BLUE_GAIN 0x02c +#define MT9M111_RED_GAIN 0x02d +#define MT9M111_GREEN2_GAIN 0x02e +#define MT9M111_GLOBAL_GAIN 0x02f +#define MT9M111_CONTEXT_CONTROL 0x0c8 +#define MT9M111_PAGE_MAP 0x0f0 +#define MT9M111_BYTE_WISE_ADDR 0x0f1 + +#define MT9M111_RESET_SYNC_CHANGES (1 << 15) +#define MT9M111_RESET_RESTART_BAD_FRAME (1 << 9) +#define MT9M111_RESET_SHOW_BAD_FRAMES (1 << 8) +#define MT9M111_RESET_RESET_SOC (1 << 5) +#define MT9M111_RESET_OUTPUT_DISABLE (1 << 4) +#define MT9M111_RESET_CHIP_ENABLE (1 << 3) +#define MT9M111_RESET_ANALOG_STANDBY (1 << 2) +#define MT9M111_RESET_RESTART_FRAME (1 << 1) +#define MT9M111_RESET_RESET_MODE (1 << 0) + +#define MT9M111_RMB_MIRROR_COLS (1 << 1) +#define MT9M111_RMB_MIRROR_ROWS (1 << 0) +#define MT9M111_CTXT_CTRL_RESTART (1 << 15) +#define MT9M111_CTXT_CTRL_DEFECTCOR_B (1 << 12) +#define MT9M111_CTXT_CTRL_RESIZE_B (1 << 10) +#define MT9M111_CTXT_CTRL_CTRL2_B (1 << 9) +#define MT9M111_CTXT_CTRL_GAMMA_B (1 << 8) +#define MT9M111_CTXT_CTRL_XENON_EN (1 << 7) +#define MT9M111_CTXT_CTRL_READ_MODE_B (1 << 3) +#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2) +#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1) +#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0) +/* + * mt9m111: Colorpipe register addresses (0x100..0x1ff) + */ +#define MT9M111_OPER_MODE_CTRL 0x106 +#define MT9M111_OUTPUT_FORMAT_CTRL 0x108 +#define MT9M111_REDUCER_XZOOM_B 0x1a0 +#define MT9M111_REDUCER_XSIZE_B 0x1a1 +#define MT9M111_REDUCER_YZOOM_B 0x1a3 +#define MT9M111_REDUCER_YSIZE_B 0x1a4 +#define MT9M111_REDUCER_XZOOM_A 0x1a6 +#define MT9M111_REDUCER_XSIZE_A 0x1a7 +#define MT9M111_REDUCER_YZOOM_A 0x1a9 +#define MT9M111_REDUCER_YSIZE_A 0x1aa + +#define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a +#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b + +#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14) + + +#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14) +#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10) +#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9) +#define MT9M111_OUTFMT_RGB (1 << 8) +#define MT9M111_OUTFMT_RGB565 (0x0 << 6) +#define MT9M111_OUTFMT_RGB555 (0x1 << 6) +#define MT9M111_OUTFMT_RGB444x (0x2 << 6) +#define MT9M111_OUTFMT_RGBx444 (0x3 << 6) +#define MT9M111_OUTFMT_TST_RAMP_OFF (0x0 << 4) +#define MT9M111_OUTFMT_TST_RAMP_COL (0x1 << 4) +#define MT9M111_OUTFMT_TST_RAMP_ROW (0x2 << 4) +#define MT9M111_OUTFMT_TST_RAMP_FRAME (0x3 << 4) +#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3) +#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2) +#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y (1 << 1) +#define MT9M111_OUTFMT_SWAP_RGB_EVEN (1 << 1) +#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr (1 << 0) +/* + * mt9m111: Camera control register addresses (0x200..0x2ff not implemented) + */ + +#define reg_read(reg) mt9m111_reg_read(icd, MT9M111_##reg) +#define reg_write(reg, val) mt9m111_reg_write(icd, MT9M111_##reg, (val)) +#define reg_set(reg, val) mt9m111_reg_set(icd, MT9M111_##reg, (val)) +#define reg_clear(reg, val) mt9m111_reg_clear(icd, MT9M111_##reg, (val)) + +#define MT9M111_MIN_DARK_ROWS 8 +#define MT9M111_MIN_DARK_COLS 24 +#define MT9M111_MAX_HEIGHT 1024 +#define MT9M111_MAX_WIDTH 1280 + +#define COL_FMT(_name, _depth, _fourcc, _colorspace) \ + { .name = _name, .depth = _depth, .fourcc = _fourcc, \ + .colorspace = _colorspace } +#define RGB_FMT(_name, _depth, _fourcc) \ + COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_SRGB) + +static const struct soc_camera_data_format mt9m111_colour_formats[] = { + COL_FMT("YCrYCb 8 bit", 8, V4L2_PIX_FMT_YUYV, V4L2_COLORSPACE_JPEG), + RGB_FMT("RGB 565", 16, V4L2_PIX_FMT_RGB565), + RGB_FMT("RGB 555", 16, V4L2_PIX_FMT_RGB555), + RGB_FMT("Bayer (sRGB) 10 bit", 10, V4L2_PIX_FMT_SBGGR16), + RGB_FMT("Bayer (sRGB) 8 bit", 8, V4L2_PIX_FMT_SBGGR8), +}; + +enum mt9m111_context { + HIGHPOWER = 0, + LOWPOWER, +}; + +struct mt9m111 { + struct i2c_client *client; + struct soc_camera_device icd; + int model; /* V4L2_IDENT_MT9M111* codes from v4l2-chip-ident.h */ + enum mt9m111_context context; + unsigned int left, top, width, height; + u32 pixfmt; + unsigned char autoexposure; + unsigned char datawidth; + unsigned int powered:1; + unsigned int hflip:1; + unsigned int vflip:1; + unsigned int swap_rgb_even_odd:1; + unsigned int swap_rgb_red_blue:1; + unsigned int swap_yuv_y_chromas:1; + unsigned int swap_yuv_cb_cr:1; +}; + +static int reg_page_map_set(struct i2c_client *client, const u16 reg) +{ + int ret; + u16 page; + static int lastpage = -1; /* PageMap cache value */ + + page = (reg >> 8); + if (page == lastpage) + return 0; + if (page > 2) + return -EINVAL; + + ret = i2c_smbus_write_word_data(client, MT9M111_PAGE_MAP, swab16(page)); + if (!ret) + lastpage = page; + return ret; +} + +static int mt9m111_reg_read(struct soc_camera_device *icd, const u16 reg) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = mt9m111->client; + int ret; + + ret = reg_page_map_set(client, reg); + if (!ret) + ret = swab16(i2c_smbus_read_word_data(client, (reg & 0xff))); + + dev_dbg(&icd->dev, "read reg.%03x -> %04x\n", reg, ret); + return ret; +} + +static int mt9m111_reg_write(struct soc_camera_device *icd, const u16 reg, + const u16 data) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = mt9m111->client; + int ret; + + ret = reg_page_map_set(client, reg); + if (!ret) + ret = i2c_smbus_write_word_data(mt9m111->client, (reg & 0xff), + swab16(data)); + dev_dbg(&icd->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret); + return ret; +} + +static int mt9m111_reg_set(struct soc_camera_device *icd, const u16 reg, + const u16 data) +{ + int ret; + + ret = mt9m111_reg_read(icd, reg); + if (ret >= 0) + ret = mt9m111_reg_write(icd, reg, ret | data); + return ret; +} + +static int mt9m111_reg_clear(struct soc_camera_device *icd, const u16 reg, + const u16 data) +{ + int ret; + + ret = mt9m111_reg_read(icd, reg); + return mt9m111_reg_write(icd, reg, ret & ~data); +} + +static int mt9m111_set_context(struct soc_camera_device *icd, + enum mt9m111_context ctxt) +{ + int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B + | MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B + | MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B + | MT9M111_CTXT_CTRL_VBLANK_SEL_B + | MT9M111_CTXT_CTRL_HBLANK_SEL_B; + int valA = MT9M111_CTXT_CTRL_RESTART; + + if (ctxt == HIGHPOWER) + return reg_write(CONTEXT_CONTROL, valB); + else + return reg_write(CONTEXT_CONTROL, valA); +} + +static int mt9m111_setup_rect(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret, is_raw_format; + int width = mt9m111->width; + int height = mt9m111->height; + + if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8) + || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16)) + is_raw_format = 1; + else + is_raw_format = 0; + + ret = reg_write(COLUMN_START, mt9m111->left); + if (!ret) + ret = reg_write(ROW_START, mt9m111->top); + + if (is_raw_format) { + if (!ret) + ret = reg_write(WINDOW_WIDTH, width); + if (!ret) + ret = reg_write(WINDOW_HEIGHT, height); + } else { + if (!ret) + ret = reg_write(REDUCER_XZOOM_B, MT9M111_MAX_WIDTH); + if (!ret) + ret = reg_write(REDUCER_YZOOM_B, MT9M111_MAX_HEIGHT); + if (!ret) + ret = reg_write(REDUCER_XSIZE_B, width); + if (!ret) + ret = reg_write(REDUCER_YSIZE_B, height); + if (!ret) + ret = reg_write(REDUCER_XZOOM_A, MT9M111_MAX_WIDTH); + if (!ret) + ret = reg_write(REDUCER_YZOOM_A, MT9M111_MAX_HEIGHT); + if (!ret) + ret = reg_write(REDUCER_XSIZE_A, width); + if (!ret) + ret = reg_write(REDUCER_YSIZE_A, height); + } + + return ret; +} + +static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt) +{ + int ret; + + ret = reg_write(OUTPUT_FORMAT_CTRL2_A, outfmt); + if (!ret) + ret = reg_write(OUTPUT_FORMAT_CTRL2_B, outfmt); + return ret; +} + +static int mt9m111_setfmt_bayer8(struct soc_camera_device *icd) +{ + return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_PROCESSED_BAYER); +} + +static int mt9m111_setfmt_bayer10(struct soc_camera_device *icd) +{ + return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_BYPASS_IFP); +} + +static int mt9m111_setfmt_rgb565(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int val = 0; + + if (mt9m111->swap_rgb_red_blue) + val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr; + if (mt9m111->swap_rgb_even_odd) + val |= MT9M111_OUTFMT_SWAP_RGB_EVEN; + val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565; + + return mt9m111_setup_pixfmt(icd, val); +} + +static int mt9m111_setfmt_rgb555(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int val = 0; + + if (mt9m111->swap_rgb_red_blue) + val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr; + if (mt9m111->swap_rgb_even_odd) + val |= MT9M111_OUTFMT_SWAP_RGB_EVEN; + val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555; + + return mt9m111_setup_pixfmt(icd, val); +} + +static int mt9m111_setfmt_yuv(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int val = 0; + + if (mt9m111->swap_yuv_cb_cr) + val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr; + if (mt9m111->swap_yuv_y_chromas) + val |= MT9M111_OUTFMT_SWAP_YCbCr_C_Y; + + return mt9m111_setup_pixfmt(icd, val); +} + +static int mt9m111_enable(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + int ret; + + if (icl->power) { + ret = icl->power(&mt9m111->client->dev, 1); + if (ret < 0) { + dev_err(icd->vdev->parent, + "Platform failed to power-on the camera.\n"); + return ret; + } + } + + ret = reg_set(RESET, MT9M111_RESET_CHIP_ENABLE); + if (!ret) + mt9m111->powered = 1; + return ret; +} + +static int mt9m111_disable(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + int ret; + + ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE); + if (!ret) + mt9m111->powered = 0; + + if (icl->power) + icl->power(&mt9m111->client->dev, 0); + + return ret; +} + +static int mt9m111_reset(struct soc_camera_device *icd) +{ + int ret; + + ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); + if (!ret) + ret = reg_set(RESET, MT9M111_RESET_RESET_SOC); + if (!ret) + ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE + | MT9M111_RESET_RESET_SOC); + return ret; +} + +static int mt9m111_start_capture(struct soc_camera_device *icd) +{ + return 0; +} + +static int mt9m111_stop_capture(struct soc_camera_device *icd) +{ + return 0; +} + +static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd) +{ + return SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_DATAWIDTH_8; +} + +static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f) +{ + return 0; +} + +static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + switch (pixfmt) { + case V4L2_PIX_FMT_SBGGR8: + ret = mt9m111_setfmt_bayer8(icd); + break; + case V4L2_PIX_FMT_SBGGR16: + ret = mt9m111_setfmt_bayer10(icd); + break; + case V4L2_PIX_FMT_RGB555: + ret = mt9m111_setfmt_rgb555(icd); + break; + case V4L2_PIX_FMT_RGB565: + ret = mt9m111_setfmt_rgb565(icd); + break; + case V4L2_PIX_FMT_YUYV: + ret = mt9m111_setfmt_yuv(icd); + break; + default: + dev_err(&icd->dev, "Pixel format not handled : %x\n", pixfmt); + ret = -EINVAL; + } + + if (!ret) + mt9m111->pixfmt = pixfmt; + + return ret; +} + +static int mt9m111_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + mt9m111->left = rect->left; + mt9m111->top = rect->top; + mt9m111->width = rect->width; + mt9m111->height = rect->height; + + dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", + __func__, pixfmt, mt9m111->left, mt9m111->top, mt9m111->width, + mt9m111->height); + + ret = mt9m111_setup_rect(icd); + if (!ret) + ret = mt9m111_set_pixfmt(icd, pixfmt); + return ret; +} + +static int mt9m111_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + if (f->fmt.pix.height > MT9M111_MAX_HEIGHT) + f->fmt.pix.height = MT9M111_MAX_HEIGHT; + if (f->fmt.pix.width > MT9M111_MAX_WIDTH) + f->fmt.pix.width = MT9M111_MAX_WIDTH; + + return 0; +} + +static int mt9m111_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match_chip != mt9m111->client->addr) + return -ENODEV; + + id->ident = mt9m111->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m111_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + int val; + + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) + return -EINVAL; + if (reg->match_chip != mt9m111->client->addr) + return -ENODEV; + + val = mt9m111_reg_read(icd, reg->reg); + reg->val = (u64)val; + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9m111_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) + return -EINVAL; + + if (reg->match_chip != mt9m111->client->addr) + return -ENODEV; + + if (mt9m111_reg_write(icd, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static const struct v4l2_queryctrl mt9m111_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Verticaly", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontaly", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { /* gain = 1/32*val (=>gain=1 if val==32) */ + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 63 * 2 * 2, + .step = 1, + .default_value = 32, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + } +}; + +static int mt9m111_video_probe(struct soc_camera_device *); +static void mt9m111_video_remove(struct soc_camera_device *); +static int mt9m111_get_control(struct soc_camera_device *, + struct v4l2_control *); +static int mt9m111_set_control(struct soc_camera_device *, + struct v4l2_control *); +static int mt9m111_resume(struct soc_camera_device *icd); +static int mt9m111_init(struct soc_camera_device *icd); +static int mt9m111_release(struct soc_camera_device *icd); + +static struct soc_camera_ops mt9m111_ops = { + .owner = THIS_MODULE, + .probe = mt9m111_video_probe, + .remove = mt9m111_video_remove, + .init = mt9m111_init, + .resume = mt9m111_resume, + .release = mt9m111_release, + .start_capture = mt9m111_start_capture, + .stop_capture = mt9m111_stop_capture, + .set_fmt_cap = mt9m111_set_fmt_cap, + .try_fmt_cap = mt9m111_try_fmt_cap, + .query_bus_param = mt9m111_query_bus_param, + .set_bus_param = mt9m111_set_bus_param, + .controls = mt9m111_controls, + .num_controls = ARRAY_SIZE(mt9m111_controls), + .get_control = mt9m111_get_control, + .set_control = mt9m111_set_control, + .get_chip_id = mt9m111_get_chip_id, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = mt9m111_get_register, + .set_register = mt9m111_set_register, +#endif +}; + +static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + if (mt9m111->context == HIGHPOWER) { + if (flip) + ret = reg_set(READ_MODE_B, mask); + else + ret = reg_clear(READ_MODE_B, mask); + } else { + if (flip) + ret = reg_set(READ_MODE_A, mask); + else + ret = reg_clear(READ_MODE_A, mask); + } + + return ret; +} + +static int mt9m111_get_global_gain(struct soc_camera_device *icd) +{ + unsigned int data, gain; + + data = reg_read(GLOBAL_GAIN); + if (data >= 0) + gain = ((data & (1 << 10)) * 2) + | ((data & (1 << 9)) * 2) + | (data & 0x2f); + else + gain = data; + + return gain; +} +static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain) +{ + u16 val; + + if (gain > 63 * 2 * 2) + return -EINVAL; + + icd->gain = gain; + if ((gain >= 64 * 2) && (gain < 63 * 2 * 2)) + val = (1 << 10) | (1 << 9) | (gain / 4); + else if ((gain >= 64) && (gain < 64 * 2)) + val = (1 << 9) | (gain / 2); + else + val = gain; + + return reg_write(GLOBAL_GAIN, val); +} + +static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + if (on) + ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); + else + ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); + + if (!ret) + mt9m111->autoexposure = on; + + return ret; +} +static int mt9m111_get_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (mt9m111->context == HIGHPOWER) + data = reg_read(READ_MODE_B); + else + data = reg_read(READ_MODE_A); + + if (data < 0) + return -EIO; + ctrl->value = !!(data & MT9M111_RMB_MIRROR_ROWS); + break; + case V4L2_CID_HFLIP: + if (mt9m111->context == HIGHPOWER) + data = reg_read(READ_MODE_B); + else + data = reg_read(READ_MODE_A); + + if (data < 0) + return -EIO; + ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS); + break; + case V4L2_CID_GAIN: + data = mt9m111_get_global_gain(icd); + if (data < 0) + return data; + ctrl->value = data; + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = mt9m111->autoexposure; + break; + } + return 0; +} + +static int mt9m111_set_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + const struct v4l2_queryctrl *qctrl; + int ret; + + qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id); + + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + mt9m111->vflip = ctrl->value; + ret = mt9m111_set_flip(icd, ctrl->value, + MT9M111_RMB_MIRROR_ROWS); + break; + case V4L2_CID_HFLIP: + mt9m111->hflip = ctrl->value; + ret = mt9m111_set_flip(icd, ctrl->value, + MT9M111_RMB_MIRROR_COLS); + break; + case V4L2_CID_GAIN: + ret = mt9m111_set_global_gain(icd, ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = mt9m111_set_autoexposure(icd, ctrl->value); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int mt9m111_restore_state(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + mt9m111_set_context(icd, mt9m111->context); + mt9m111_set_pixfmt(icd, mt9m111->pixfmt); + mt9m111_setup_rect(icd); + mt9m111_set_flip(icd, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS); + mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS); + mt9m111_set_global_gain(icd, icd->gain); + mt9m111_set_autoexposure(icd, mt9m111->autoexposure); + return 0; +} + +static int mt9m111_resume(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret = 0; + + if (mt9m111->powered) { + ret = mt9m111_enable(icd); + if (!ret) + ret = mt9m111_reset(icd); + if (!ret) + ret = mt9m111_restore_state(icd); + } + return ret; +} + +static int mt9m111_init(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + mt9m111->context = HIGHPOWER; + ret = mt9m111_enable(icd); + if (!ret) + ret = mt9m111_reset(icd); + if (!ret) + ret = mt9m111_set_context(icd, mt9m111->context); + if (!ret) + ret = mt9m111_set_autoexposure(icd, mt9m111->autoexposure); + if (ret) + dev_err(&icd->dev, "mt9m111 init failed: %d\n", ret); + return ret; +} + +static int mt9m111_release(struct soc_camera_device *icd) +{ + int ret; + + ret = mt9m111_disable(icd); + if (ret < 0) + dev_err(&icd->dev, "mt9m111 release failed: %d\n", ret); + + return ret; +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int mt9m111_video_probe(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + s32 data; + int ret; + + /* + * We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + ret = mt9m111_enable(icd); + if (ret) + goto ei2c; + ret = mt9m111_reset(icd); + if (ret) + goto ei2c; + + data = reg_read(CHIP_VERSION); + + switch (data) { + case 0x143a: + mt9m111->model = V4L2_IDENT_MT9M111; + icd->formats = mt9m111_colour_formats; + icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats); + break; + default: + ret = -ENODEV; + dev_err(&icd->dev, + "No MT9M111 chip detected, register read %x\n", data); + goto ei2c; + } + + dev_info(&icd->dev, "Detected a MT9M111 chip ID 0x143a\n"); + + ret = soc_camera_video_start(icd); + if (ret) + goto eisis; + + mt9m111->autoexposure = 1; + + mt9m111->swap_rgb_even_odd = 1; + mt9m111->swap_rgb_red_blue = 1; + + return 0; +eisis: +ei2c: + return ret; +} + +static void mt9m111_video_remove(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m111->client->addr, + mt9m111->icd.dev.parent, mt9m111->icd.vdev); + soc_camera_video_stop(&mt9m111->icd); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) +static int mt9m111_probe(struct i2c_client *client) +#else +static int mt9m111_probe(struct i2c_client *client, + const struct i2c_device_id *did) +#endif +{ + struct mt9m111 *mt9m111; + struct soc_camera_device *icd; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl = client->dev.platform_data; + int ret; + + if (!icl) { + dev_err(&client->dev, "MT9M111 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9m111 = kzalloc(sizeof(struct mt9m111), GFP_KERNEL); + if (!mt9m111) + return -ENOMEM; + + mt9m111->client = client; + i2c_set_clientdata(client, mt9m111); + + /* Second stage probe - when a capture adapter is there */ + icd = &mt9m111->icd; + icd->ops = &mt9m111_ops; + icd->control = &client->dev; + icd->x_min = MT9M111_MIN_DARK_COLS; + icd->y_min = MT9M111_MIN_DARK_ROWS; + icd->x_current = icd->x_min; + icd->y_current = icd->y_min; + icd->width_min = MT9M111_MIN_DARK_ROWS; + icd->width_max = MT9M111_MAX_WIDTH; + icd->height_min = MT9M111_MIN_DARK_COLS; + icd->height_max = MT9M111_MAX_HEIGHT; + icd->y_skip_top = 0; + icd->iface = icl->bus_id; + + ret = soc_camera_device_register(icd); + if (ret) + goto eisdr; + return 0; + +eisdr: + kfree(mt9m111); + return ret; +} + +static int mt9m111_remove(struct i2c_client *client) +{ + struct mt9m111 *mt9m111 = i2c_get_clientdata(client); + soc_camera_device_unregister(&mt9m111->icd); + kfree(mt9m111); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id mt9m111_id[] = { + { "mt9m111", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9m111_id); +#endif + +static struct i2c_driver mt9m111_i2c_driver = { + .driver = { + .name = "mt9m111", + }, + .probe = mt9m111_probe, + .remove = mt9m111_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = mt9m111_id, +#endif +}; + +static int __init mt9m111_mod_init(void) +{ + return i2c_add_driver(&mt9m111_i2c_driver); +} + +static void __exit mt9m111_mod_exit(void) +{ + i2c_del_driver(&mt9m111_i2c_driver); +} + +module_init(mt9m111_mod_init); +module_exit(mt9m111_mod_exit); + +MODULE_DESCRIPTION("Micron MT9M111 Camera driver"); +MODULE_AUTHOR("Robert Jarzmik"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c index 1ff36edd7..bea695a2c 100644 --- a/linux/drivers/media/video/mt9v022.c +++ b/linux/drivers/media/video/mt9v022.c @@ -25,7 +25,7 @@ static char *sensor_type; module_param(sensor_type, charp, S_IRUGO); -MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"\n"); +MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); /* mt9v022 selected register addresses */ #define MT9V022_CHIP_VERSION 0x00 @@ -134,34 +134,56 @@ static int reg_clear(struct soc_camera_device *icd, const u8 reg, static int mt9v022_init(struct soc_camera_device *icd) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; int ret; + if (icl->power) { + ret = icl->power(&mt9v022->client->dev, 1); + if (ret < 0) { + dev_err(icd->vdev->parent, + "Platform failed to power-on the camera.\n"); + return ret; + } + } + + /* + * The camera could have been already on, we hard-reset it additionally, + * if available. Soft reset is done in video_probe(). + */ + if (icl->reset) + icl->reset(&mt9v022->client->dev); + /* Almost the default mode: master, parallel, simultaneous, and an * undocumented bit 0x200, which is present in table 7, but not in 8, * plus snapshot mode to disable scan for now */ mt9v022->chip_control |= 0x10; ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (ret >= 0) - reg_write(icd, MT9V022_READ_MODE, 0x300); + if (!ret) + ret = reg_write(icd, MT9V022_READ_MODE, 0x300); /* All defaults */ - if (ret >= 0) + if (!ret) /* AEC, AGC on */ ret = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x3); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480); - if (ret >= 0) + if (!ret) /* default - auto */ ret = reg_clear(icd, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_DIGITAL_TEST_PATTERN, 0); - return ret >= 0 ? 0 : -EIO; + return ret; } static int mt9v022_release(struct soc_camera_device *icd) { - /* Nothing? */ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + + if (icl->power) + icl->power(&mt9v022->client->dev, 0); + return 0; } @@ -352,21 +374,21 @@ static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, rect->height + icd->y_skip_top + 43); } /* Setup frame format: defaults apart from width and height */ - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_COLUMN_START, rect->left); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_ROW_START, rect->top); - if (ret >= 0) + if (!ret) /* Default 94, Phytec driver says: * "width + horizontal blank >= 660" */ ret = reg_write(icd, MT9V022_HORIZONTAL_BLANKING, rect->width > 660 - 43 ? 43 : 660 - rect->width); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_VERTICAL_BLANKING, 45); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_WINDOW_WIDTH, rect->width); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_WINDOW_HEIGHT, rect->height + icd->y_skip_top); @@ -717,7 +739,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) icd->num_formats = 1; } - if (ret >= 0) + if (!ret) ret = soc_camera_video_start(icd); if (ret < 0) goto eisis; diff --git a/linux/drivers/media/video/mxb.c b/linux/drivers/media/video/mxb.c index bf2462a58..8252ad94c 100644 --- a/linux/drivers/media/video/mxb.c +++ b/linux/drivers/media/video/mxb.c @@ -27,6 +27,7 @@ #include <media/tuner.h> #include <linux/video_decoder.h> #include <media/v4l2-common.h> +#include <media/saa7115.h> #include "compat.h" #include "mxb.h" @@ -123,6 +124,8 @@ static struct saa7146_extension_ioctls ioctls[] = { { VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE }, { VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE }, { VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE }, + { VIDIOC_DBG_G_REGISTER, SAA7146_EXCLUSIVE }, + { VIDIOC_DBG_S_REGISTER, SAA7146_EXCLUSIVE }, { MXB_S_AUDIO_CD, SAA7146_EXCLUSIVE }, /* custom control */ { MXB_S_AUDIO_LINE, SAA7146_EXCLUSIVE }, /* custom control */ { 0, 0 } @@ -135,12 +138,12 @@ struct mxb struct i2c_adapter i2c_adapter; - struct i2c_client* saa7111a; - struct i2c_client* tda9840; - struct i2c_client* tea6415c; - struct i2c_client* tuner; - struct i2c_client* tea6420_1; - struct i2c_client* tea6420_2; + struct i2c_client *saa7111a; + struct i2c_client *tda9840; + struct i2c_client *tea6415c; + struct i2c_client *tuner; + struct i2c_client *tea6420_1; + struct i2c_client *tea6420_2; int cur_mode; /* current audio mode (mono, stereo, ...) */ int cur_input; /* current input */ @@ -152,23 +155,23 @@ static struct saa7146_extension extension; static int mxb_check_clients(struct device *dev, void *data) { - struct mxb* mxb = data; + struct mxb *mxb = data; struct i2c_client *client = i2c_verify_client(dev); - if( !client ) + if (!client) return 0; - if( I2C_ADDR_TEA6420_1 == client->addr ) + if (I2C_ADDR_TEA6420_1 == client->addr) mxb->tea6420_1 = client; - if( I2C_ADDR_TEA6420_2 == client->addr ) + if (I2C_ADDR_TEA6420_2 == client->addr) mxb->tea6420_2 = client; - if( I2C_TEA6415C_2 == client->addr ) + if (I2C_TEA6415C_2 == client->addr) mxb->tea6415c = client; - if( I2C_ADDR_TDA9840 == client->addr ) + if (I2C_ADDR_TDA9840 == client->addr) mxb->tda9840 = client; - if( I2C_SAA7111 == client->addr ) + if (I2C_SAA7111 == client->addr) mxb->saa7111a = client; - if( 0x60 == client->addr ) + if (0x60 == client->addr) mxb->tuner = client; return 0; @@ -179,23 +182,28 @@ static int mxb_probe(struct saa7146_dev* dev) struct mxb* mxb = NULL; int result; - if ((result = request_module("saa7111")) < 0) { + result = request_module("saa7115"); + if (result < 0) { printk("mxb: saa7111 i2c module not available.\n"); return -ENODEV; } - if ((result = request_module("tea6420")) < 0) { + result = request_module("tea6420"); + if (result < 0) { printk("mxb: tea6420 i2c module not available.\n"); return -ENODEV; } - if ((result = request_module("tea6415c")) < 0) { + result = request_module("tea6415c"); + if (result < 0) { printk("mxb: tea6415c i2c module not available.\n"); return -ENODEV; } - if ((result = request_module("tda9840")) < 0) { + result = request_module("tda9840"); + if (result < 0) { printk("mxb: tda9840 i2c module not available.\n"); return -ENODEV; } - if ((result = request_module("tuner")) < 0) { + result = request_module("tuner"); + if (result < 0) { printk("mxb: tuner i2c module not available.\n"); return -ENODEV; } @@ -208,9 +216,10 @@ static int mxb_probe(struct saa7146_dev* dev) mxb->i2c_adapter = (struct i2c_adapter) { .class = I2C_CLASS_TV_ANALOG, - .name = "mxb", }; + snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); + saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); if(i2c_add_adapter(&mxb->i2c_adapter) < 0) { DEB_S(("cannot register i2c-device. skipping.\n")); @@ -291,38 +300,7 @@ static struct { { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, { 3, { 0x80, 0xb3, 0x0a } }, - {-1, { 0} } -}; - -static const unsigned char mxb_saa7111_init[] = { - 0x00, 0x00, /* 00 - ID byte */ - 0x01, 0x00, /* 01 - reserved */ - - /*front end */ - 0x02, 0xd8, /* 02 - FUSE=x, GUDL=x, MODE=x */ - 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ - 0x04, 0x00, /* 04 - GAI1=256 */ - 0x05, 0x00, /* 05 - GAI2=256 */ - - /* decoder */ - 0x06, 0xf0, /* 06 - HSB at xx(50Hz) / xx(60Hz) pixels after end of last line */ - 0x07, 0x30, /* 07 - HSS at xx(50Hz) / xx(60Hz) pixels after end of last line */ - 0x08, 0xa8, /* 08 - AUFD=x, FSEL=x, EXFIL=x, VTRC=x, HPLL=x, VNOI=x */ - 0x09, 0x02, /* 09 - BYPS=x, PREF=x, BPSS=x, VBLB=x, UPTCV=x, APER=x */ - 0x0a, 0x80, /* 0a - BRIG=128 */ - 0x0b, 0x47, /* 0b - CONT=1.109 */ - 0x0c, 0x40, /* 0c - SATN=1.0 */ - 0x0d, 0x00, /* 0d - HUE=0 */ - 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ - 0x0f, 0x00, /* 0f - reserved */ - 0x10, 0xd0, /* 10 - OFTS=x, HDEL=x, VRLN=x, YDEL=x */ - 0x11, 0x8c, /* 11 - GPSW=x, CM99=x, FECO=x, COMPO=x, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ - 0x12, 0x80, /* 12 - xx output control 2 */ - 0x13, 0x30, /* 13 - xx output control 3 */ - 0x14, 0x00, /* 14 - reserved */ - 0x15, 0x15, /* 15 - VBI */ - 0x16, 0x04, /* 16 - VBI */ - 0x17, 0x00, /* 17 - VBI */ + {-1, { 0 } } }; /* bring hardware to a sane state. this has to be done, just in case someone @@ -332,37 +310,28 @@ static const unsigned char mxb_saa7111_init[] = { static int mxb_init_done(struct saa7146_dev* dev) { struct mxb* mxb = (struct mxb*)dev->ext_priv; - struct video_decoder_init init; struct i2c_msg msg; struct tuner_setup tun_setup; v4l2_std_id std = V4L2_STD_PAL_BG; + struct v4l2_routing route; int i = 0, err = 0; - struct tea6415c_multiplex vm; + struct tea6415c_multiplex vm; /* select video mode in saa7111a */ - i = VIDEO_MODE_PAL; - /* fixme: currently pointless: gets overwritten by configuration below */ - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_NORM, &i); - - /* write configuration to saa7111a */ - init.data = mxb_saa7111_init; - init.len = sizeof(mxb_saa7111_init); - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_INIT, &init); + mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_S_STD, &std); /* select tuner-output on saa7111a */ i = 0; - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i); - - /* enable vbi bypass */ - i = 1; - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_VBI_BYPASS, &i); + route.input = SAA7115_COMPOSITE0; + route.output = SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS; + mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route); /* select a tuner type */ tun_setup.mode_mask = T_ANALOG_TV; tun_setup.addr = ADDR_UNSET; tun_setup.type = TUNER_PHILIPS_PAL; - mxb->tuner->driver->command(mxb->tuner,TUNER_SET_TYPE_ADDR, &tun_setup); + mxb->tuner->driver->command(mxb->tuner, TUNER_SET_TYPE_ADDR, &tun_setup); /* tune in some frequency on tuner */ mxb->cur_freq.tuner = 0; mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; @@ -374,27 +343,26 @@ static int mxb_init_done(struct saa7146_dev* dev) mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); /* mute audio on tea6420s */ - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]); - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[6][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[6][1]); + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[6][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[6][1]); + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_cd[6][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_cd[6][1]); /* switch to tuner-channel on tea6415c*/ vm.out = 17; vm.in = 3; - mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm); + mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm); /* select tuner-output on multicable on tea6415c*/ vm.in = 3; vm.out = 13; - mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm); + mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm); /* the rest for mxb */ mxb->cur_input = 0; mxb->cur_mute = 1; mxb->cur_mode = V4L2_TUNER_MODE_STEREO; - mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &mxb->cur_mode); /* check if the saa7740 (aka 'sound arena module') is present on the mxb. if so, we must initialize it. due to lack of @@ -405,21 +373,22 @@ static int mxb_init_done(struct saa7146_dev* dev) msg.len = mxb_saa7740_init[0].length; msg.buf = &mxb_saa7740_init[0].data[0]; - if( 1 == (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) { + err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); + if (err == 1) { /* the sound arena module is a pos, that's probably the reason philips refuses to hand out a datasheet for the saa7740... it seems to screw up the i2c bus, so we disable fast irq based i2c transactions here and rely on the slow and safe polling method ... */ extension.flags &= ~SAA7146_USE_I2C_IRQ; - for(i = 1;;i++) { - if( -1 == mxb_saa7740_init[i].length ) { + for (i = 1; ; i++) { + if (-1 == mxb_saa7740_init[i].length) break; - } msg.len = mxb_saa7740_init[i].length; msg.buf = &mxb_saa7740_init[i].data[0]; - if( 1 != (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) { + err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); + if (err != 1) { DEB_D(("failed to initialize 'sound arena module'.\n")); goto err; } @@ -433,7 +402,8 @@ err: /* ext->saa has been filled by the core driver */ /* some stuff is done via variables */ - saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, input_port_selection[mxb->cur_input].hps_sync); + saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, + input_port_selection[mxb->cur_input].hps_sync); /* some stuff is done via direct write to the registers */ @@ -458,24 +428,24 @@ void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ -static int mxb_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) +static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) { - struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct mxb *mxb = (struct mxb *)dev->ext_priv; - DEB_EE(("dev:%p\n",dev)); + DEB_EE(("dev:%p\n", dev)); /* checking for i2c-devices can be omitted here, because we already did this in "mxb_vl42_probe" */ - saa7146_vv_init(dev,&vv_data); - if( 0 != saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { + saa7146_vv_init(dev, &vv_data); + if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { ERR(("cannot register capture v4l2 device. skipping.\n")); return -1; } /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ - if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) { - if( 0 != saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { + if (MXB_BOARD_CAN_DO_VBI(dev)) { + if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { ERR(("cannot register vbi v4l2 device. skipping.\n")); } } @@ -487,18 +457,18 @@ static int mxb_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data i2c_use_client(mxb->saa7111a); i2c_use_client(mxb->tuner); - printk("mxb: found 'Multimedia eXtension Board'-%d.\n",mxb_num); + printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num); mxb_num++; mxb_init_done(dev); return 0; } -static int mxb_detach(struct saa7146_dev* dev) +static int mxb_detach(struct saa7146_dev *dev) { - struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct mxb *mxb = (struct mxb *)dev->ext_priv; - DEB_EE(("dev:%p\n",dev)); + DEB_EE(("dev:%p\n", dev)); i2c_release_client(mxb->tea6420_1); i2c_release_client(mxb->tea6420_2); @@ -508,9 +478,8 @@ static int mxb_detach(struct saa7146_dev* dev) i2c_release_client(mxb->tuner); saa7146_unregister_device(&mxb->video_dev,dev); - if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) { - saa7146_unregister_device(&mxb->vbi_dev,dev); - } + if (MXB_BOARD_CAN_DO_VBI(dev)) + saa7146_unregister_device(&mxb->vbi_dev, dev); saa7146_vv_release(dev); mxb_num--; @@ -524,7 +493,7 @@ static int mxb_detach(struct saa7146_dev* dev) static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { struct saa7146_dev *dev = fh->dev; - struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct mxb *mxb = (struct mxb *)dev->ext_priv; struct saa7146_vv *vv = dev->vv_data; switch(cmd) { @@ -533,11 +502,9 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) struct v4l2_input *i = arg; DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index)); - if( i->index < 0 || i->index >= MXB_INPUTS) { + if (i->index < 0 || i->index >= MXB_INPUTS) return -EINVAL; - } memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); - return 0; } /* the saa7146 provides some controls (brightness, contrast, saturation) @@ -551,7 +518,7 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) for (i = MAXCONTROLS - 1; i >= 0; i--) { if (mxb_controls[i].id == qc->id) { *qc = mxb_controls[i]; - DEB_D(("VIDIOC_QUERYCTRL %d.\n",qc->id)); + DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id)); return 0; } } @@ -563,56 +530,51 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) int i; for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) { + if (mxb_controls[i].id == vc->id) break; - } } - if( i < 0 ) { + if (i < 0) return -EAGAIN; - } - switch (vc->id ) { - case V4L2_CID_AUDIO_MUTE: { - vc->value = mxb->cur_mute; - DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value)); - return 0; - } + if (vc->id == V4L2_CID_AUDIO_MUTE) { + vc->value = mxb->cur_mute; + DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); + return 0; } - DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value)); + DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); return 0; } case VIDIOC_S_CTRL: { - struct v4l2_control *vc = arg; + struct v4l2_control *vc = arg; int i = 0; for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) { + if (mxb_controls[i].id == vc->id) break; - } } - if( i < 0 ) { + if (i < 0) return -EAGAIN; - } - switch (vc->id ) { - case V4L2_CID_AUDIO_MUTE: { - mxb->cur_mute = vc->value; - if( 0 == vc->value ) { - /* switch the audio-source */ - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][1]); - } else { - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]); - } - DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n",vc->value)); - break; + if (vc->id == V4L2_CID_AUDIO_MUTE) { + mxb->cur_mute = vc->value; + if (!vc->value) { + /* switch the audio-source */ + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, + &TEA6420_line[video_audio_connect[mxb->cur_input]][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, + &TEA6420_line[video_audio_connect[mxb->cur_input]][1]); + } else { + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, + &TEA6420_line[6][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, + &TEA6420_line[6][1]); } + DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value)); } return 0; } @@ -621,106 +583,84 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) int *input = (int *)arg; *input = mxb->cur_input; - DEB_EE(("VIDIOC_G_INPUT %d.\n",*input)); + DEB_EE(("VIDIOC_G_INPUT %d.\n", *input)); return 0; } case VIDIOC_S_INPUT: { int input = *(int *)arg; - struct tea6415c_multiplex vm; + struct tea6415c_multiplex vm; + struct v4l2_routing route; int i = 0; - DEB_EE(("VIDIOC_S_INPUT %d.\n",input)); + DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - if (input < 0 || input >= MXB_INPUTS) { + if (input < 0 || input >= MXB_INPUTS) return -EINVAL; - } - - /* fixme: locke das setzen des inputs mit hilfe des mutexes - mutex_lock(&dev->lock); - video_mux(dev,*i); - mutex_unlock(&dev->lock); - */ - - /* fixme: check if streaming capture - if ( 0 != dev->streaming ) { - DEB_D(("VIDIOC_S_INPUT illegal while streaming.\n")); - return -EPERM; - } - */ mxb->cur_input = input; - saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync); + saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, + input_port_selection[input].hps_sync); /* prepare switching of tea6415c and saa7111a; have a look at the 'background'-file for further informations */ - switch( input ) { - - case TUNER: - { - i = 0; - vm.in = 3; - vm.out = 17; - - if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) { - printk("VIDIOC_S_INPUT: could not address tea6415c #1\n"); - return -EFAULT; - } - /* connect tuner-output always to multicable */ - vm.in = 3; - vm.out = 13; - break; - } - case AUX3_YC: - { - /* nothing to be done here. aux3_yc is - directly connected to the saa711a */ - i = 5; - break; - } - case AUX3: - { - /* nothing to be done here. aux3 is - directly connected to the saa711a */ - i = 1; - break; - } - case AUX1: - { - i = 0; - vm.in = 1; - vm.out = 17; - break; + switch (input) { + case TUNER: + i = SAA7115_COMPOSITE0; + vm.in = 3; + vm.out = 17; + + if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { + printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n"); + return -EFAULT; } + /* connect tuner-output always to multicable */ + vm.in = 3; + vm.out = 13; + break; + case AUX3_YC: + /* nothing to be done here. aux3_yc is + directly connected to the saa711a */ + i = SAA7115_SVIDEO1; + break; + case AUX3: + /* nothing to be done here. aux3 is + directly connected to the saa711a */ + i = SAA7115_COMPOSITE1; + break; + case AUX1: + i = SAA7115_COMPOSITE0; + vm.in = 1; + vm.out = 17; + break; } /* switch video in tea6415c only if necessary */ - switch( input ) { - case TUNER: - case AUX1: - { - if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) { - printk("VIDIOC_S_INPUT: could not address tea6415c #3\n"); - return -EFAULT; - } - break; - } - default: - { - break; + switch (input) { + case TUNER: + case AUX1: + if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { + printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n"); + return -EFAULT; } + break; + default: + break; } /* switch video in saa7111a */ - if ( 0 != mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i)) { + route.input = i; + route.output = 0; + if (mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route)) printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n"); - } /* switch the audio-source only if necessary */ if( 0 == mxb->cur_mute ) { - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][1]); + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, + &TEA6420_line[video_audio_connect[input]][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, + &TEA6420_line[video_audio_connect[input]][1]); } return 0; @@ -728,114 +668,44 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) case VIDIOC_G_TUNER: { struct v4l2_tuner *t = arg; - int byte = 0; - if( 0 != t->index ) { + if (t->index) { DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index)); return -EINVAL; } DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index)); - memset(t,0,sizeof(*t)); - strcpy(t->name, "Television"); + memset(t, 0, sizeof(*t)); + i2c_clients_command(&mxb->i2c_adapter, cmd, arg); + strlcpy(t->name, "TV Tuner", sizeof(t->name)); t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ - t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ - /* FIXME: add the real signal strength here */ - t->signal = 0xffff; - t->afc = 0; - - mxb->tda9840->driver->command(mxb->tda9840,TDA9840_DETECT, &byte); + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | \ + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; t->audmode = mxb->cur_mode; - - if( byte < 0 ) { - t->rxsubchans = V4L2_TUNER_SUB_MONO; - } else { - switch(byte) { - case TDA9840_MONO_DETECT: { - t->rxsubchans = V4L2_TUNER_SUB_MONO; - DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_MONO.\n")); - break; - } - case TDA9840_DUAL_DETECT: { - t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_LANG1.\n")); - break; - } - case TDA9840_STEREO_DETECT: { - t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_STEREO.\n")); - break; - } - default: { /* TDA9840_INCORRECT_DETECT */ - t->rxsubchans = V4L2_TUNER_MODE_MONO; - DEB_D(("VIDIOC_G_TUNER: TDA9840_INCORRECT_DETECT => V4L2_TUNER_MODE_MONO\n")); - break; - } - } - } - return 0; } case VIDIOC_S_TUNER: { struct v4l2_tuner *t = arg; - int result = 0; - int byte = 0; - if( 0 != t->index ) { + if (t->index) { DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index)); return -EINVAL; } - switch(t->audmode) { - case V4L2_TUNER_MODE_STEREO: { - mxb->cur_mode = V4L2_TUNER_MODE_STEREO; - byte = TDA9840_SET_STEREO; - DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n")); - break; - } - case V4L2_TUNER_MODE_LANG1_LANG2: { - mxb->cur_mode = V4L2_TUNER_MODE_LANG1_LANG2; - byte = TDA9840_SET_BOTH; - DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n")); - break; - } - case V4L2_TUNER_MODE_LANG1: { - mxb->cur_mode = V4L2_TUNER_MODE_LANG1; - byte = TDA9840_SET_LANG1; - DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n")); - break; - } - case V4L2_TUNER_MODE_LANG2: { - mxb->cur_mode = V4L2_TUNER_MODE_LANG2; - byte = TDA9840_SET_LANG2; - DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n")); - break; - } - default: { /* case V4L2_TUNER_MODE_MONO: {*/ - mxb->cur_mode = V4L2_TUNER_MODE_MONO; - byte = TDA9840_SET_MONO; - DEB_D(("VIDIOC_S_TUNER: TDA9840_SET_MONO\n")); - break; - } - } - - if( 0 != (result = mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &byte))) { - printk("VIDIOC_S_TUNER error. result:%d, byte:%d\n",result,byte); - } - + mxb->cur_mode = t->audmode; + i2c_clients_command(&mxb->i2c_adapter, cmd, arg); return 0; } case VIDIOC_G_FREQUENCY: { struct v4l2_frequency *f = arg; - if(0 != mxb->cur_input) { - DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",mxb->cur_input)); + if (mxb->cur_input) { + DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", + mxb->cur_input)); return -EINVAL; } @@ -848,14 +718,14 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { struct v4l2_frequency *f = arg; - if (0 != f->tuner) + if (f->tuner) return -EINVAL; if (V4L2_TUNER_ANALOG_TV != f->type) return -EINVAL; - if(0 != mxb->cur_input) { - DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",mxb->cur_input)); + if (mxb->cur_input) { + DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input)); return -EINVAL; } @@ -876,7 +746,7 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { int i = *(int*)arg; - if( i < 0 || i >= MXB_AUDIOS ) { + if (i < 0 || i >= MXB_AUDIOS) { DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i)); return -EINVAL; } @@ -892,7 +762,7 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { int i = *(int*)arg; - if( i < 0 || i >= MXB_AUDIOS ) { + if (i < 0 || i >= MXB_AUDIOS) { DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i)); return -EINVAL; } @@ -907,12 +777,12 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { struct v4l2_audio *a = arg; - if( a->index < 0 || a->index > MXB_INPUTS ) { - DEB_D(("VIDIOC_G_AUDIO %d out of range.\n",a->index)); + if (a->index < 0 || a->index > MXB_INPUTS) { + DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index)); return -EINVAL; } - DEB_EE(("VIDIOC_G_AUDIO %d.\n",a->index)); + DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index)); memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); return 0; @@ -920,9 +790,16 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) case VIDIOC_S_AUDIO: { struct v4l2_audio *a = arg; - DEB_D(("VIDIOC_S_AUDIO %d.\n",a->index)); + + DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index)); return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_DBG_G_REGISTER: + i2c_clients_command(&mxb->i2c_adapter, cmd, arg); + return 0; +#endif default: /* DEB2(printk("does not handle this ioctl.\n")); @@ -932,27 +809,29 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) return 0; } -static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) +static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) { - struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct mxb *mxb = (struct mxb *)dev->ext_priv; int zero = 0; int one = 1; - if(V4L2_STD_PAL_I == std->id ) { + if (V4L2_STD_PAL_I == standard->id) { v4l2_std_id std = V4L2_STD_PAL_I; + DEB_D(("VIDIOC_S_STD: setting mxb for PAL_I.\n")); /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* unset the 7111 gpio register -- I don't know what this does exactly */ - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_GPIO, &zero); + mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &zero); mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; + DEB_D(("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM.\n")); /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* set the 7111 gpio register -- I don't know what this does exactly */ - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_GPIO, &one); + mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &one); mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); } return 0; @@ -1028,7 +907,7 @@ static struct saa7146_extension extension = { static int __init mxb_init_module(void) { - if( 0 != saa7146_register_extension(&extension)) { + if (saa7146_register_extension(&extension)) { DEB_S(("failed to register extension.\n")); return -ENODEV; } diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c index c693b6fb0..3f1d7a660 100644 --- a/linux/drivers/media/video/ov511.c +++ b/linux/drivers/media/video/ov511.c @@ -626,9 +626,9 @@ ov511_i2c_write_internal(struct usb_ov511 *ov, break; /* Retry until idle */ - do + do { rc = reg_r(ov, R511_I2C_CTL); - while (rc > 0 && ((rc&1) == 0)); + } while (rc > 0 && ((rc&1) == 0)); if (rc < 0) break; @@ -703,9 +703,9 @@ ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) return rc; /* Retry until idle */ - do - rc = reg_r(ov, R511_I2C_CTL); - while (rc > 0 && ((rc&1) == 0)); + do { + rc = reg_r(ov, R511_I2C_CTL); + } while (rc > 0 && ((rc & 1) == 0)); if (rc < 0) return rc; @@ -729,9 +729,9 @@ ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) return rc; /* Retry until idle */ - do + do { rc = reg_r(ov, R511_I2C_CTL); - while (rc > 0 && ((rc&1) == 0)); + } while (rc > 0 && ((rc&1) == 0)); if (rc < 0) return rc; @@ -3595,7 +3595,7 @@ static int ov51x_init_isoc(struct usb_ov511 *ov) { struct urb *urb; - int fx, err, n, size; + int fx, err, n, i, size; PDEBUG(3, "*** Initializing capture ***"); @@ -3666,6 +3666,8 @@ ov51x_init_isoc(struct usb_ov511 *ov) urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (!urb) { err("init isoc: usb_alloc_urb ret. NULL"); + for (i = 0; i < n; i++) + usb_free_urb(ov->sbuf[i].urb); return -ENOMEM; } ov->sbuf[n].urb = urb; @@ -5655,7 +5657,7 @@ static ssize_t show_exposure(struct device *cd, if (!ov->dev) return -ENODEV; sensor_get_exposure(ov, &exp); - return sprintf(buf, "%d\n", exp >> 8); + return sprintf(buf, "%d\n", exp); } static DEVICE_ATTR(exposure, S_IRUGO, show_exposure, NULL); diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c index 065c24541..2c4acbf5a 100644 --- a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -49,12 +49,6 @@ MODULE_LICENSE("GPL"); #define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ #define GENERIC_REG_COM_I 0x29 /* misc ID bits */ -extern struct ovcamchip_ops ov6x20_ops; -extern struct ovcamchip_ops ov6x30_ops; -extern struct ovcamchip_ops ov7x10_ops; -extern struct ovcamchip_ops ov7x20_ops; -extern struct ovcamchip_ops ov76be_ops; - static char *chip_names[NUM_CC_TYPES] = { [CC_UNKNOWN] = "Unknown chip", [CC_OV76BE] = "OV76BE", diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h index 9afa4fe47..a05650fae 100644 --- a/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h @@ -53,6 +53,12 @@ struct ovcamchip { int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */ }; +extern struct ovcamchip_ops ov6x20_ops; +extern struct ovcamchip_ops ov6x30_ops; +extern struct ovcamchip_ops ov7x10_ops; +extern struct ovcamchip_ops ov7x20_ops; +extern struct ovcamchip_ops ov76be_ops; + /* --------------------------------- */ /* I2C I/O */ /* --------------------------------- */ diff --git a/linux/drivers/media/video/pms.c b/linux/drivers/media/video/pms.c index e36b39b44..00b623e00 100644 --- a/linux/drivers/media/video/pms.c +++ b/linux/drivers/media/video/pms.c @@ -48,6 +48,7 @@ struct pms_device struct video_picture picture; int height; int width; + unsigned long in_use; struct mutex lock; }; @@ -882,10 +883,27 @@ static ssize_t pms_read(struct file *file, char __user *buf, return len; } +static int pms_exclusive_open(struct inode *inode, struct file *file) +{ + struct video_device *v = video_devdata(file); + struct pms_device *pd = (struct pms_device *)v; + + return test_and_set_bit(0, &pd->in_use) ? -EBUSY : 0; +} + +static int pms_exclusive_release(struct inode *inode, struct file *file) +{ + struct video_device *v = video_devdata(file); + struct pms_device *pd = (struct pms_device *)v; + + clear_bit(0, &pd->in_use); + return 0; +} + static const struct file_operations pms_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = pms_exclusive_open, + .release = pms_exclusive_release, .ioctl = pms_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -898,6 +916,7 @@ static struct video_device pms_template= { .name = "Mediavision PMS", .fops = &pms_fops, + .release = video_device_release_empty, }; static struct pms_device pms_device; @@ -1020,10 +1039,23 @@ static int init_mediavision(void) * Initialization and module stuff */ +#ifndef MODULE +static int enable; +module_param(enable, int, 0); +#endif + static int __init init_pms_cards(void) { printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); +#ifndef MODULE + if (!enable) { + printk(KERN_INFO "PMS: not enabled, use pms.enable=1 to " + "probe\n"); + return -ENODEV; + } +#endif + data_port = io_port +1; if(init_mediavision()) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index 3b982d963..5fa8af50b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -135,13 +135,17 @@ int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) /* Retrieve control's default value (any type) */ -int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr) +int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr) { int ret = 0; if (!cptr) return 0; LOCK_TAKE(cptr->hdw->big_lock); do { if (cptr->info->type == pvr2_ctl_int) { - ret = cptr->info->default_value; + if (cptr->info->get_def_value) { + ret = cptr->info->get_def_value(cptr, valptr); + } else { + *valptr = cptr->info->default_value; + } } } while(0); LOCK_GIVE(cptr->hdw->big_lock); return ret; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h index 0371ae6e6..794ff9012 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h @@ -49,7 +49,7 @@ int pvr2_ctrl_get_max(struct pvr2_ctrl *); int pvr2_ctrl_get_min(struct pvr2_ctrl *); /* Retrieve control's default value (any type) */ -int pvr2_ctrl_get_def(struct pvr2_ctrl *); +int pvr2_ctrl_get_def(struct pvr2_ctrl *, int *valptr); /* Retrieve control's enumeration count (enum only) */ int pvr2_ctrl_get_cnt(struct pvr2_ctrl *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 88e175168..cbe2a3417 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -489,6 +489,8 @@ static const struct pvr2_device_desc pvr2_device_751xx = { struct usb_device_id pvr2_device_table[] = { { USB_DEVICE(0x2040, 0x2900), .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, + { USB_DEVICE(0x2040, 0x2950), /* Logically identical to 2900 */ + .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, { USB_DEVICE(0x2040, 0x2400), .driver_info = (kernel_ulong_t)&pvr2_device_24xxx}, { USB_DEVICE(0x1164, 0x0622), diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 657f86159..de7ee7264 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -82,6 +82,7 @@ struct pvr2_ctl_info { /* Control's implementation */ pvr2_ctlf_get_value get_value; /* Get its value */ + pvr2_ctlf_get_value get_def_value; /* Get its default value */ pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */ pvr2_ctlf_get_value get_max_value; /* Get maximum allowed value */ pvr2_ctlf_set_value set_value; /* Set its value */ @@ -307,6 +308,10 @@ struct pvr2_hdw { struct v4l2_tuner tuner_signal_info; int tuner_signal_stale; + /* Cropping capability info */ + struct v4l2_cropcap cropcap_info; + int cropcap_stale; + /* Video standard handling */ v4l2_std_id std_mask_eeprom; // Hardware supported selections v4l2_std_id std_mask_avail; // Which standards we may select from @@ -367,6 +372,10 @@ struct pvr2_hdw { VCREATE_DATA(bass); VCREATE_DATA(treble); VCREATE_DATA(mute); + VCREATE_DATA(cropl); + VCREATE_DATA(cropt); + VCREATE_DATA(cropw); + VCREATE_DATA(croph); VCREATE_DATA(input); VCREATE_DATA(audiomode); VCREATE_DATA(res_hor); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 419a473cf..b79829f8a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -299,6 +299,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, void *write_data,unsigned int write_len, void *read_data,unsigned int read_len); +static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw); static void trace_stbit(const char *name,int val) @@ -403,6 +404,194 @@ static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) return 0; } +static int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *left = cap->bounds.left; + return 0; +} + +static int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *left = cap->bounds.left; + if (cap->bounds.width > cptr->hdw->cropw_val) { + *left += cap->bounds.width - cptr->hdw->cropw_val; + } + return 0; +} + +static int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *top = cap->bounds.top; + return 0; +} + +static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *top = cap->bounds.top; + if (cap->bounds.height > cptr->hdw->croph_val) { + *top += cap->bounds.height - cptr->hdw->croph_val; + } + return 0; +} + +static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = 0; + if (cap->bounds.width > cptr->hdw->cropl_val) { + *val = cap->bounds.width - cptr->hdw->cropl_val; + } + return 0; +} + +static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = 0; + if (cap->bounds.height > cptr->hdw->cropt_val) { + *val = cap->bounds.height - cptr->hdw->cropt_val; + } + return 0; +} + +static int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.left; + return 0; +} + +static int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.top; + return 0; +} + +static int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.width; + return 0; +} + +static int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.height; + return 0; +} + +static int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.left; + return 0; +} + +static int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.top; + return 0; +} + +static int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.width; + return 0; +} + +static int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.height; + return 0; +} + +static int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->pixelaspect.numerator; + return 0; +} + +static int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->pixelaspect.denominator; + return 0; +} + static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp) { /* Actual maximum depends on the video standard in effect. */ @@ -780,6 +969,10 @@ VCREATE_FUNCS(balance) VCREATE_FUNCS(bass) VCREATE_FUNCS(treble) VCREATE_FUNCS(mute) +VCREATE_FUNCS(cropl) +VCREATE_FUNCS(cropt) +VCREATE_FUNCS(cropw) +VCREATE_FUNCS(croph) VCREATE_FUNCS(audiomode) VCREATE_FUNCS(res_hor) VCREATE_FUNCS(res_ver) @@ -850,6 +1043,72 @@ static const struct pvr2_ctl_info control_defs[] = { .default_value = 0, DEFREF(mute), DEFBOOL, + }, { + .desc = "Capture crop left margin", + .name = "crop_left", + .internal_id = PVR2_CID_CROPL, + .default_value = 0, + DEFREF(cropl), + DEFINT(-129, 340), + .get_min_value = ctrl_cropl_min_get, + .get_max_value = ctrl_cropl_max_get, + .get_def_value = ctrl_get_cropcapdl, + }, { + .desc = "Capture crop top margin", + .name = "crop_top", + .internal_id = PVR2_CID_CROPT, + .default_value = 0, + DEFREF(cropt), + DEFINT(-35, 544), + .get_min_value = ctrl_cropt_min_get, + .get_max_value = ctrl_cropt_max_get, + .get_def_value = ctrl_get_cropcapdt, + }, { + .desc = "Capture crop width", + .name = "crop_width", + .internal_id = PVR2_CID_CROPW, + .default_value = 720, + DEFREF(cropw), + .get_max_value = ctrl_cropw_max_get, + .get_def_value = ctrl_get_cropcapdw, + }, { + .desc = "Capture crop height", + .name = "crop_height", + .internal_id = PVR2_CID_CROPH, + .default_value = 480, + DEFREF(croph), + .get_max_value = ctrl_croph_max_get, + .get_def_value = ctrl_get_cropcapdh, + }, { + .desc = "Capture capability pixel aspect numerator", + .name = "cropcap_pixel_numerator", + .internal_id = PVR2_CID_CROPCAPPAN, + .get_value = ctrl_get_cropcappan, + }, { + .desc = "Capture capability pixel aspect denominator", + .name = "cropcap_pixel_denominator", + .internal_id = PVR2_CID_CROPCAPPAD, + .get_value = ctrl_get_cropcappad, + }, { + .desc = "Capture capability bounds top", + .name = "cropcap_bounds_top", + .internal_id = PVR2_CID_CROPCAPBT, + .get_value = ctrl_get_cropcapbt, + }, { + .desc = "Capture capability bounds left", + .name = "cropcap_bounds_left", + .internal_id = PVR2_CID_CROPCAPBL, + .get_value = ctrl_get_cropcapbl, + }, { + .desc = "Capture capability bounds width", + .name = "cropcap_bounds_width", + .internal_id = PVR2_CID_CROPCAPBW, + .get_value = ctrl_get_cropcapbw, + }, { + .desc = "Capture capability bounds height", + .name = "cropcap_bounds_height", + .internal_id = PVR2_CID_CROPCAPBH, + .get_value = ctrl_get_cropcapbh, },{ .desc = "Video Source", .name = "input", @@ -1323,9 +1582,19 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE; memcpy(fw_ptr, fw_entry->data + fw_done, bcnt); /* Usbsnoop log shows that we must swap bytes... */ + /* Some background info: The data being swapped here is a + firmware image destined for the mpeg encoder chip that + lives at the other end of a USB endpoint. The encoder + chip always talks in 32 bit chunks and its storage is + organized into 32 bit words. However from the file + system to the encoder chip everything is purely a byte + stream. The firmware file's contents are always 32 bit + swapped from what the encoder expects. Thus the need + always exists to swap the bytes regardless of the endian + type of the host processor and therefore swab32() makes + the most sense. */ for (icnt = 0; icnt < bcnt/4 ; icnt++) - ((u32 *)fw_ptr)[icnt] = - ___swab32(((u32 *)fw_ptr)[icnt]); + ((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]); ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt, &actual_length, HZ); @@ -1922,7 +2191,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid) { unsigned int idx,cnt1,cnt2,m; - struct pvr2_hdw *hdw; + struct pvr2_hdw *hdw = NULL; int valid_std_mask; struct pvr2_ctrl *cptr; const struct pvr2_device_desc *hdw_desc; @@ -1932,6 +2201,16 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info); + if (hdw_desc == NULL) { + pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create:" + " No device description pointer," + " unable to continue."); + pvr2_trace(PVR2_TRACE_INIT, "If you have a new device type," + " please contact Mike Isely <isely@pobox.com>" + " to get it included in the driver\n"); + goto fail; + } + hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", hdw,hdw_desc->description); @@ -2089,6 +2368,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, valid_std_mask; } + hdw->cropcap_stale = !0; hdw->eeprom_addr = -1; hdw->unit_number = -1; hdw->v4l_minor_number_video = -1; @@ -2532,6 +2812,28 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) /* Can't commit anything until pathway is ok. */ return 0; } + /* The broadcast decoder can only scale down, so if + * res_*_dirty && crop window < output format ==> enlarge crop. + * + * The mpeg encoder receives fields of res_hor_val dots and + * res_ver_val halflines. Limits: hor<=720, ver<=576. + */ + if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) { + hdw->cropw_val = hdw->res_hor_val; + hdw->cropw_dirty = !0; + } else if (hdw->cropw_dirty) { + hdw->res_hor_dirty = !0; /* must rescale */ + hdw->res_hor_val = min(720, hdw->cropw_val); + } + if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) { + hdw->croph_val = hdw->res_ver_val; + hdw->croph_dirty = !0; + } else if (hdw->croph_dirty) { + int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576; + hdw->res_ver_dirty = !0; + hdw->res_ver_val = min(nvres, hdw->croph_val); + } + /* If any of the below has changed, then we can't do the update while the pipeline is running. Pipeline must be paused first and decoder -> encoder connection be made quiescent before we @@ -2542,6 +2844,8 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) hdw->srate_dirty || hdw->res_ver_dirty || hdw->res_hor_dirty || + hdw->cropw_dirty || + hdw->croph_dirty || hdw->input_dirty || (hdw->active_stream_type != hdw->desired_stream_type)); if (disruptive_change && !hdw->state_pipeline_idle) { @@ -2611,6 +2915,9 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } hdw->state_pipeline_config = !0; + /* Hardware state may have changed in a way to cause the cropping + capabilities to have changed. So mark it stale, which will + cause a later re-fetch. */ trace_stbit("state_pipeline_config",hdw->state_pipeline_config); return !0; } @@ -2701,6 +3008,33 @@ void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw) } +static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw) +{ + if (!hdw->cropcap_stale) { + return 0; + } + pvr2_i2c_core_status_poll(hdw); + if (hdw->cropcap_stale) { + return -EIO; + } + return 0; +} + + +/* Return information about cropping capabilities */ +int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp) +{ + int stat = 0; + LOCK_TAKE(hdw->big_lock); + stat = pvr2_hdw_check_cropcap(hdw); + if (!stat) { + memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info)); + } + LOCK_GIVE(hdw->big_lock); + return stat; +} + + /* Return information about the tuner */ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 4e60f6262..29fb0729d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -37,6 +37,16 @@ #define PVR2_CID_FREQUENCY 6 #define PVR2_CID_HRES 7 #define PVR2_CID_VRES 8 +#define PVR2_CID_CROPL 9 +#define PVR2_CID_CROPT 10 +#define PVR2_CID_CROPW 11 +#define PVR2_CID_CROPH 12 +#define PVR2_CID_CROPCAPPAN 13 +#define PVR2_CID_CROPCAPPAD 14 +#define PVR2_CID_CROPCAPBL 15 +#define PVR2_CID_CROPCAPBT 16 +#define PVR2_CID_CROPCAPBW 17 +#define PVR2_CID_CROPCAPBH 18 /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 @@ -176,6 +186,9 @@ void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *); /* Return information about the tuner */ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *); +/* Return information about cropping capabilities */ +int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *); + /* Query device and see if it thinks it is on a high-speed USB link */ int pvr2_hdw_is_hsm(struct pvr2_hdw *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index 25e611b2e..1b1424955 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -38,8 +38,9 @@ #define OP_VOLUME 3 #define OP_FREQ 4 #define OP_AUDIORATE 5 -#define OP_SIZE 6 -#define OP_LOG 7 +#define OP_CROP 6 +#define OP_SIZE 7 +#define OP_LOG 8 static const struct pvr2_i2c_op * const ops[] = { [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard, @@ -47,6 +48,7 @@ static const struct pvr2_i2c_op * const ops[] = { [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh, [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume, [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency, + [OP_CROP] = &pvr2_i2c_op_v4l2_crop, [OP_SIZE] = &pvr2_i2c_op_v4l2_size, [OP_LOG] = &pvr2_i2c_op_v4l2_log, }; @@ -60,6 +62,7 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) (1 << OP_BCSH) | (1 << OP_VOLUME) | (1 << OP_FREQ) | + (1 << OP_CROP) | (1 << OP_SIZE) | (1 << OP_LOG)); cp->status_poll = pvr2_v4l2_cmd_status_poll; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index d715c80ef..ab703d6d5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -38,6 +38,7 @@ static void set_standard(struct pvr2_hdw *hdw) pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); } hdw->tuner_signal_stale = !0; + hdw->cropcap_stale = !0; } @@ -234,6 +235,37 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = { }; +static void set_crop(struct pvr2_hdw *hdw) +{ + struct v4l2_crop crop; + + memset(&crop, 0, sizeof crop); + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c.left = hdw->cropl_val; + crop.c.top = hdw->cropt_val; + crop.c.height = hdw->croph_val; + crop.c.width = hdw->cropw_val; + + pvr2_trace(PVR2_TRACE_CHIPS, + "i2c v4l2 set_crop crop=%d:%d:%d:%d", + crop.c.width, crop.c.height, crop.c.left, crop.c.top); + + pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop); +} + +static int check_crop(struct pvr2_hdw *hdw) +{ + return (hdw->cropl_dirty || hdw->cropt_dirty || + hdw->cropw_dirty || hdw->croph_dirty); +} + +const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop = { + .check = check_crop, + .update = set_crop, + .name = "v4l2_crop", +}; + + static void do_log(struct pvr2_hdw *hdw) { pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()"); @@ -264,7 +296,19 @@ void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp) { - pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&cp->hdw->tuner_signal_info); + int stat; + struct pvr2_hdw *hdw = cp->hdw; + if (hdw->cropcap_stale) { + hdw->cropcap_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + stat = pvr2_i2c_client_cmd(cp, VIDIOC_CROPCAP, + &hdw->cropcap_info); + if (stat == 0) { + /* Check was successful, so the data is no + longer considered stale. */ + hdw->cropcap_stale = 0; + } + } + pvr2_i2c_client_cmd(cp, VIDIOC_G_TUNER, &hdw->tuner_signal_info); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h index 7fa38683b..eb744a206 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -29,6 +29,7 @@ extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_radio; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 9159686c3..edce8fc34 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -902,7 +902,7 @@ static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp, if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) { unsigned int idx; unsigned long msk,sm; - int spcfl; + bcnt = scnprintf(buf,maxlen," ["); ccnt += bcnt; buf += bcnt; maxlen -= bcnt; sm = 0; @@ -966,6 +966,7 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client) INIT_LIST_HEAD(&cp->list); cp->client = client; mutex_lock(&hdw->i2c_list_lock); do { + hdw->cropcap_stale = !0; list_add_tail(&cp->list,&hdw->i2c_clients); hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT; } while (0); mutex_unlock(&hdw->i2c_list_lock); @@ -980,6 +981,7 @@ static int pvr2_i2c_detach_inform(struct i2c_client *client) unsigned long amask = 0; int foundfl = 0; mutex_lock(&hdw->i2c_list_lock); do { + hdw->cropcap_stale = !0; list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) { if (cp->client == client) { trace_i2c("pvr2_i2c_detach" @@ -1021,22 +1023,32 @@ static struct i2c_adapter pvr2_i2c_adap_template = { .client_unregister = pvr2_i2c_detach_inform, }; -static void do_i2c_scan(struct pvr2_hdw *hdw) + +/* Return true if device exists at given address */ +static int do_i2c_probe(struct pvr2_hdw *hdw, int addr) { struct i2c_msg msg[1]; - int i,rc; + int rc; msg[0].addr = 0; msg[0].flags = I2C_M_RD; msg[0].len = 0; msg[0].buf = NULL; - printk("%s: i2c scan beginning\n",hdw->name); + msg[0].addr = addr; + rc = i2c_transfer(&hdw->i2c_adap, msg, ARRAY_SIZE(msg)); + return rc == 1; +} + +static void do_i2c_scan(struct pvr2_hdw *hdw) +{ + int i; + printk(KERN_INFO "%s: i2c scan beginning\n", hdw->name); for (i = 0; i < 128; i++) { - msg[0].addr = i; - rc = i2c_transfer(&hdw->i2c_adap,msg, ARRAY_SIZE(msg)); - if (rc != 1) continue; - printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i); + if (do_i2c_probe(hdw, i)) { + printk(KERN_INFO "%s: i2c scan: found device @ 0x%x\n", + hdw->name, i); + } } - printk("%s: i2c scan done.\n",hdw->name); + printk(KERN_INFO "%s: i2c scan done.\n", hdw->name); } void pvr2_i2c_core_init(struct pvr2_hdw *hdw) @@ -1055,8 +1067,6 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) hdw->i2c_func[0x18] = i2c_black_hole; } else if (ir_mode[hdw->unit_number] == 1) { if (hdw->hdw_desc->ir_scheme == PVR2_IR_SCHEME_24XXX) { - /* This comment is present PURELY to get - checkpatch.pl to STFU. Lovely, eh? */ hdw->i2c_func[0x18] = i2c_24xxx_ir; } } @@ -1081,6 +1091,16 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) mutex_init(&hdw->i2c_list_lock); hdw->i2c_linked = !0; i2c_add_adapter(&hdw->i2c_adap); + if (hdw->i2c_func[0x18] == i2c_24xxx_ir) { + /* Probe for a different type of IR receiver on this + device. If present, disable the emulated IR receiver. */ + if (do_i2c_probe(hdw, 0x71)) { + pvr2_trace(PVR2_TRACE_INFO, + "Device has newer IR hardware;" + " disabling unneeded virtual IR device"); + hdw->i2c_func[0x18] = NULL; + } + } if (i2c_scan) do_i2c_scan(hdw); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 90cb36435..16461dd92 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -66,6 +66,7 @@ struct pvr2_sysfs_ctl_item { struct device_attribute attr_type; struct device_attribute attr_min; struct device_attribute attr_max; + struct device_attribute attr_def; struct device_attribute attr_enum; struct device_attribute attr_bits; struct device_attribute attr_val; @@ -146,6 +147,23 @@ static ssize_t show_max(struct device *class_dev, return scnprintf(buf, PAGE_SIZE, "%ld\n", val); } +static ssize_t show_def(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + int val; + int ret; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def); + ret = pvr2_ctrl_get_def(cip->cptr, &val); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %d, stat=%d", + cip->chptr, cip->ctl_id, val, ret); + if (ret < 0) { + return ret; + } + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + static ssize_t show_val_norm(struct device *class_dev, struct device_attribute *attr, char *buf) @@ -321,6 +339,10 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_max.attr.mode = S_IRUGO; cip->attr_max.show = show_max; + cip->attr_def.attr.name = "def_val"; + cip->attr_def.attr.mode = S_IRUGO; + cip->attr_def.show = show_def; + cip->attr_val.attr.name = "cur_val"; cip->attr_val.attr.mode = S_IRUGO; @@ -344,6 +366,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_gen[acnt++] = &cip->attr_name.attr; cip->attr_gen[acnt++] = &cip->attr_type.attr; cip->attr_gen[acnt++] = &cip->attr_val.attr; + cip->attr_gen[acnt++] = &cip->attr_def.attr; cip->attr_val.show = show_val_norm; cip->attr_val.store = store_val_norm; if (pvr2_ctrl_has_custom_symbols(cptr)) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index c1cc0e88b..429c96d4f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -534,7 +534,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, lmin = pvr2_ctrl_get_min(hcp); lmax = pvr2_ctrl_get_max(hcp); - ldef = pvr2_ctrl_get_def(hcp); + pvr2_ctrl_get_def(hcp, &ldef); if (w == -1) { w = ldef; } else if (w < lmin) { @@ -544,7 +544,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, } lmin = pvr2_ctrl_get_min(vcp); lmax = pvr2_ctrl_get_max(vcp); - ldef = pvr2_ctrl_get_def(vcp); + pvr2_ctrl_get_def(vcp, &ldef); if (h == -1) { h = ldef; } else if (h < lmin) { @@ -605,6 +605,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_QUERYCTRL: { struct pvr2_ctrl *cptr; + int val; struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; ret = 0; if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { @@ -628,7 +629,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, pvr2_ctrl_get_desc(cptr)); strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name)); vc->flags = pvr2_ctrl_get_v4lflags(cptr); - vc->default_value = pvr2_ctrl_get_def(cptr); + pvr2_ctrl_get_def(cptr, &val); + vc->default_value = val; switch (pvr2_ctrl_get_type(cptr)) { case pvr2_ctl_enum: vc->type = V4L2_CTRL_TYPE_MENU; @@ -754,6 +756,92 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, break; } + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg; + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + ret = -EINVAL; + break; + } + ret = pvr2_hdw_get_cropcap(hdw, cap); + cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ + break; + } + case VIDIOC_G_CROP: + { + struct v4l2_crop *crop = (struct v4l2_crop *)arg; + int val = 0; + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + ret = -EINVAL; + break; + } + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); + if (ret != 0) { + ret = -EINVAL; + break; + } + crop->c.left = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); + if (ret != 0) { + ret = -EINVAL; + break; + } + crop->c.top = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); + if (ret != 0) { + ret = -EINVAL; + break; + } + crop->c.width = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); + if (ret != 0) { + ret = -EINVAL; + break; + } + crop->c.height = val; + } + case VIDIOC_S_CROP: + { + struct v4l2_crop *crop = (struct v4l2_crop *)arg; + struct v4l2_cropcap cap; + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + ret = -EINVAL; + break; + } + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), + crop->c.left); + if (ret != 0) { + ret = -EINVAL; + break; + } + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), + crop->c.top); + if (ret != 0) { + ret = -EINVAL; + break; + } + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), + crop->c.width); + if (ret != 0) { + ret = -EINVAL; + break; + } + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), + crop->c.height); + if (ret != 0) { + ret = -EINVAL; + break; + } + } case VIDIOC_LOG_STATUS: { pvr2_hdw_trigger_module_log(hdw); @@ -933,7 +1021,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) unsigned int input_cnt,idx; int ret = 0; - lock_kernel(); dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase); vp = dip->v4lp; @@ -944,13 +1031,11 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) if (!pvr2_hdw_dev_ok(hdw)) { pvr2_trace(PVR2_TRACE_OPEN_CLOSE, "pvr2_v4l2_open: hardware not ready"); - unlock_kernel(); return -EIO; } fhp = kzalloc(sizeof(*fhp),GFP_KERNEL); if (!fhp) { - unlock_kernel(); return -ENOMEM; } @@ -980,7 +1065,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) fhp); kfree(fhp); - unlock_kernel(); return ret; } @@ -997,7 +1081,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) "Destroying pvr_v4l2_fh id=%p (input map failure)", fhp); kfree(fhp); - unlock_kernel(); return -ENOMEM; } input_cnt = 0; @@ -1021,7 +1104,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) v4l2_prio_open(&vp->prio,&fhp->prio); fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw); - unlock_kernel(); return 0; } diff --git a/linux/drivers/media/video/pwc/pwc-ctrl.c b/linux/drivers/media/video/pwc/pwc-ctrl.c index 1cccd5c77..c66530210 100644 --- a/linux/drivers/media/video/pwc/pwc-ctrl.c +++ b/linux/drivers/media/video/pwc/pwc-ctrl.c @@ -41,7 +41,6 @@ #include <asm/uaccess.h> #endif #include <asm/errno.h> -#include <linux/version.h> #include "pwc.h" #include "pwc-uncompress.h" @@ -1635,15 +1634,15 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCGVIDCMD: { - ARG_DEF(struct pwc_video_command, cmd); - - ARGR(cmd).type = pdev->type; - ARGR(cmd).release = pdev->release; - ARGR(cmd).command_len = pdev->cmd_len; - memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len); - ARGR(cmd).bandlength = pdev->vbandlength; - ARGR(cmd).frame_size = pdev->frame_size; - ARG_OUT(cmd) + ARG_DEF(struct pwc_video_command, vcmd); + + ARGR(vcmd).type = pdev->type; + ARGR(vcmd).release = pdev->release; + ARGR(vcmd).command_len = pdev->cmd_len; + memcpy(&ARGR(vcmd).command_buf, pdev->cmd_buf, pdev->cmd_len); + ARGR(vcmd).bandlength = pdev->vbandlength; + ARGR(vcmd).frame_size = pdev->frame_size; + ARG_OUT(vcmd) break; } /* diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index f005e2338..69917b2f9 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -1116,7 +1116,7 @@ static int pwc_video_open(struct inode *inode, struct file *file) PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev); - pdev = (struct pwc_device *)vdev->priv; + pdev = video_get_drvdata(vdev); BUG_ON(!pdev); if (pdev->vopen) { PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n"); @@ -1237,7 +1237,7 @@ static int pwc_video_close(struct inode *inode, struct file *file) PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); lock_kernel(); - pdev = (struct pwc_device *)vdev->priv; + pdev = video_get_drvdata(vdev); if (pdev->vopen == 0) PWC_DEBUG_MODULE("video_close() called on closed device?\n"); @@ -1308,7 +1308,7 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, vdev, buf, count); if (vdev == NULL) return -EFAULT; - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); if (pdev == NULL) return -EFAULT; @@ -1390,7 +1390,7 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait) if (vdev == NULL) return -EFAULT; - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); if (pdev == NULL) return -EFAULT; @@ -1412,7 +1412,7 @@ static int pwc_video_ioctl(struct inode *inode, struct file *file, if (!vdev) goto out; - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); mutex_lock(&pdev->modlock); if (!pdev->unplugged) @@ -1432,7 +1432,7 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) int index; PWC_DEBUG_MEMORY(">> %s\n", __func__); - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); size = vma->vm_end - vma->vm_start; start = vma->vm_start; diff --git a/linux/drivers/media/video/pwc/pwc-v4l.c b/linux/drivers/media/video/pwc/pwc-v4l.c index 174288987..76a1376c9 100644 --- a/linux/drivers/media/video/pwc/pwc-v4l.c +++ b/linux/drivers/media/video/pwc/pwc-v4l.c @@ -346,7 +346,7 @@ int pwc_video_do_ioctl(struct inode *inode, struct file *file, if (vdev == NULL) return -EFAULT; - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); if (pdev == NULL) return -EFAULT; diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index ead87ddaf..00aa421fe 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -36,8 +36,13 @@ #include <linux/videodev2.h> #include <asm/dma.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#include <mach/pxa-regs.h> +#include <mach/camera.h> +#else #include <asm/arch/pxa-regs.h> #include <asm/arch/camera.h> +#endif #define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) #define PXA_CAM_DRV_NAME "pxa27x-camera" @@ -629,17 +634,6 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) pdata->init(pcdev->dev); } - if (pdata && pdata->power) { - dev_dbg(pcdev->dev, "%s: Power on camera\n", __func__); - pdata->power(pcdev->dev, 1); - } - - if (pdata && pdata->reset) { - dev_dbg(pcdev->dev, "%s: Releasing camera reset\n", - __func__); - pdata->reset(pcdev->dev, 1); - } - CICR0 = 0x3FF; /* disable all interrupts */ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) @@ -660,20 +654,7 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) { - struct pxacamera_platform_data *board = pcdev->pdata; - clk_disable(pcdev->clk); - - if (board && board->reset) { - dev_dbg(pcdev->dev, "%s: Asserting camera reset\n", - __func__); - board->reset(pcdev->dev, 0); - } - - if (board && board->power) { - dev_dbg(pcdev->dev, "%s: Power off camera\n", __func__); - board->power(pcdev->dev, 0); - } } static irqreturn_t pxa_camera_irq(int irq, void *data) @@ -1144,31 +1125,31 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->dev = &pdev->dev; /* request dma */ - pcdev->dma_chans[0] = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, - pxa_camera_dma_irq_y, pcdev); - if (pcdev->dma_chans[0] < 0) { + err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, + pxa_camera_dma_irq_y, pcdev); + if (err < 0) { dev_err(pcdev->dev, "Can't request DMA for Y\n"); - err = -ENOMEM; goto exit_iounmap; } + pcdev->dma_chans[0] = err; dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); - pcdev->dma_chans[1] = pxa_request_dma("CI_U", DMA_PRIO_HIGH, - pxa_camera_dma_irq_u, pcdev); - if (pcdev->dma_chans[1] < 0) { + err = pxa_request_dma("CI_U", DMA_PRIO_HIGH, + pxa_camera_dma_irq_u, pcdev); + if (err < 0) { dev_err(pcdev->dev, "Can't request DMA for U\n"); - err = -ENOMEM; goto exit_free_dma_y; } + pcdev->dma_chans[1] = err; dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]); - pcdev->dma_chans[2] = pxa_request_dma("CI_V", DMA_PRIO_HIGH, - pxa_camera_dma_irq_v, pcdev); - if (pcdev->dma_chans[0] < 0) { + err = pxa_request_dma("CI_V", DMA_PRIO_HIGH, + pxa_camera_dma_irq_v, pcdev); + if (err < 0) { dev_err(pcdev->dev, "Can't request DMA for V\n"); - err = -ENOMEM; goto exit_free_dma_u; } + pcdev->dma_chans[2] = err; dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]); DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD; diff --git a/linux/drivers/media/video/s2255drv.c b/linux/drivers/media/video/s2255drv.c index 939edff48..5a872787e 100644 --- a/linux/drivers/media/video/s2255drv.c +++ b/linux/drivers/media/video/s2255drv.c @@ -59,6 +59,8 @@ +/* default JPEG quality */ +#define S2255_DEF_JPEG_QUAL 50 /* vendor request in */ #define S2255_VR_IN 0 /* vendor request out */ @@ -68,20 +70,21 @@ /* USB endpoint number for configuring the device */ #define S2255_CONFIG_EP 2 /* maximum time for DSP to start responding after last FW word loaded(ms) */ -#define S2255_DSP_BOOTTIME 400 +#define S2255_DSP_BOOTTIME 800 /* maximum time to wait for firmware to load (ms) */ #define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) #define S2255_DEF_BUFS 16 +#define S2255_SETMODE_TIMEOUT 500 #define MAX_CHANNELS 4 -#define FRAME_MARKER 0x2255DA4AL -#define MAX_PIPE_USBBLOCK (40 * 1024) -#define DEFAULT_PIPE_USBBLOCK (16 * 1024) +#define S2255_MARKER_FRAME 0x2255DA4AL +#define S2255_MARKER_RESPONSE 0x2255ACACL +#define S2255_USB_XFER_SIZE (16 * 1024) #define MAX_CHANNELS 4 #define MAX_PIPE_BUFFERS 1 #define SYS_FRAMES 4 /* maximum size is PAL full size plus room for the marker header(s) */ -#define SYS_FRAMES_MAXSIZE (720 * 288 * 2 * 2 + 4096) -#define DEF_USB_BLOCK (4096) +#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) +#define DEF_USB_BLOCK S2255_USB_XFER_SIZE #define LINE_SZ_4CIFS_NTSC 640 #define LINE_SZ_2CIFS_NTSC 640 #define LINE_SZ_1CIFS_NTSC 320 @@ -109,6 +112,9 @@ #define COLOR_YUVPL 1 /* YUV planar */ #define COLOR_YUVPK 2 /* YUV packed */ #define COLOR_Y8 4 /* monochrome */ +#define COLOR_JPG 5 /* JPEG */ +#define MASK_COLOR 0xff +#define MASK_JPG_QUALITY 0xff00 /* frame decimation. Not implemented by V4L yet(experimental in V4L) */ #define FDEC_1 1 /* capture every frame. default */ @@ -149,16 +155,14 @@ struct s2255_mode { u32 restart; /* if DSP requires restart */ }; -/* frame structure */ -#define FRAME_STATE_UNUSED 0 -#define FRAME_STATE_FILLING 1 -#define FRAME_STATE_FULL 2 +#define S2255_READ_IDLE 0 +#define S2255_READ_FRAME 1 +/* frame structure */ struct s2255_framei { unsigned long size; - - unsigned long ulState; /* ulState ==0 unused, 1 being filled, 2 full */ + unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/ void *lpvbits; /* image data */ unsigned long cur_size; /* current data copied to it */ }; @@ -189,6 +193,10 @@ struct s2255_dmaqueue { #define S2255_FW_FAILED 3 #define S2255_FW_DISCONNECTING 4 +#define S2255_FW_MARKER 0x22552f2f +/* 2255 read states */ +#define S2255_READ_IDLE 0 +#define S2255_READ_FRAME 1 struct s2255_fw { int fw_loaded; int fw_size; @@ -196,7 +204,6 @@ struct s2255_fw { atomic_t fw_state; void *pfw_data; wait_queue_head_t wait_fw; - struct timer_list dsp_wait; const struct firmware *fw; }; @@ -238,15 +245,27 @@ struct s2255_dev { struct s2255_pipeinfo pipes[MAX_PIPE_BUFFERS]; struct s2255_bufferi buffer[MAX_CHANNELS]; struct s2255_mode mode[MAX_CHANNELS]; + /* jpeg compression */ + struct v4l2_jpegcompression jc[MAX_CHANNELS]; const struct s2255_fmt *cur_fmt[MAX_CHANNELS]; int cur_frame[MAX_CHANNELS]; int last_frame[MAX_CHANNELS]; u32 cc; /* current channel */ int b_acquire[MAX_CHANNELS]; + /* allocated image size */ unsigned long req_image_size[MAX_CHANNELS]; + /* received packet size */ + unsigned long pkt_size[MAX_CHANNELS]; int bad_payload[MAX_CHANNELS]; unsigned long frame_count[MAX_CHANNELS]; int frame_ready; + /* if JPEG image */ + int jpg_size[MAX_CHANNELS]; + /* if channel configured to default state */ + int chn_configured[MAX_CHANNELS]; + wait_queue_head_t wait_setmode[MAX_CHANNELS]; + int setmode_ready[MAX_CHANNELS]; + int chn_ready; struct kref kref; spinlock_t slock; }; @@ -307,12 +326,16 @@ static void s2255_stop_readpipe(struct s2255_dev *dev); static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn); static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn); static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, - int chn); + int chn, int jpgsize); static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, struct s2255_mode *mode); static int s2255_board_shutdown(struct s2255_dev *dev); static void s2255_exit_v4l(struct s2255_dev *dev); -static void s2255_fwload_start(struct s2255_dev *dev); +static void s2255_fwload_start(struct s2255_dev *dev, int reset); +static void s2255_destroy(struct kref *kref); +static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, + u16 index, u16 value, void *buf, + s32 buf_len, int bOut); #define dprintk(level, fmt, arg...) \ do { \ @@ -408,6 +431,10 @@ static const struct s2255_fmt formats[] = { .fourcc = V4L2_PIX_FMT_UYVY, .depth = 16 }, { + .name = "JPG", + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + }, { .name = "8bpp GREY", .fourcc = V4L2_PIX_FMT_GREY, .depth = 8 @@ -465,6 +492,13 @@ static void planar422p_to_yuv_packed(const unsigned char *in, return; } +static void s2255_reset_dsppower(struct s2255_dev *dev) +{ + s2255_vendor_req(dev, 0x40, 0x0b0b, 0x0b0b, NULL, 0, 1); + msleep(10); + s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1); + return; +} /* kickstarts the firmware loading. from probe */ @@ -481,18 +515,6 @@ static void s2255_timer(unsigned long user_data) } } -/* called when DSP is up and running. DSP is guaranteed to - be running after S2255_DSP_BOOTTIME */ -static void s2255_dsp_running(unsigned long user_data) -{ - struct s2255_fw *data = (struct s2255_fw *)user_data; - dprintk(1, "dsp running\n"); - atomic_set(&data->fw_state, S2255_FW_SUCCESS); - wake_up(&data->wait_fw); - printk(KERN_INFO "s2255: firmware loaded successfully\n"); - return; -} - /* this loads the firmware asynchronously. Originally this was done synchroously in probe. @@ -550,19 +572,14 @@ static void s2255_fwchunk_complete(struct urb *urb) } data->fw_loaded += len; } else { - init_timer(&data->dsp_wait); - data->dsp_wait.function = s2255_dsp_running; - data->dsp_wait.data = (unsigned long)data; atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); - mod_timer(&data->dsp_wait, msecs_to_jiffies(S2255_DSP_BOOTTIME) - + jiffies); } dprintk(100, "2255 complete done\n"); return; } -static int s2255_got_frame(struct s2255_dev *dev, int chn) +static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) { struct s2255_dmaqueue *dma_q = &dev->vidq[chn]; struct s2255_buffer *buf; @@ -587,8 +604,7 @@ static int s2255_got_frame(struct s2255_dev *dev, int chn) list_del(&buf->vb.queue); do_gettimeofday(&buf->vb.ts); dprintk(100, "[%p/%d] wakeup\n", buf, buf->vb.i); - - s2255_fillbuff(dev, buf, dma_q->channel); + s2255_fillbuff(dev, buf, dma_q->channel, jpgsize); wake_up(&buf->vb.done); dprintk(2, "wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); unlock: @@ -622,7 +638,7 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc) * */ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, - int chn) + int chn, int jpgsize) { int pos = 0; struct timeval ts; @@ -650,6 +666,10 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, case V4L2_PIX_FMT_GREY: memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height); break; + case V4L2_PIX_FMT_JPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; case V4L2_PIX_FMT_YUV422P: memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height * 2); @@ -658,9 +678,6 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, printk(KERN_DEBUG "s2255: unknown format?\n"); } dev->last_frame[chn] = -1; - /* done with the frame, free it */ - frm->ulState = 0; - dprintk(4, "freeing buffer\n"); } else { printk(KERN_ERR "s2255: =======no frame\n"); return; @@ -670,7 +687,7 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, (unsigned long)vbuf, pos); /* tell v4l buffer was filled */ - buf->vb.field_count++; + buf->vb.field_count = dev->frame_count[chn] * 2; do_gettimeofday(&ts); buf->vb.ts = ts; buf->vb.state = VIDEOBUF_DONE; @@ -1022,6 +1039,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, case V4L2_PIX_FMT_GREY: fh->mode.color = COLOR_Y8; break; + case V4L2_PIX_FMT_JPEG: + fh->mode.color = COLOR_JPG | + (fh->dev->jc[fh->channel].quality << 8); + break; case V4L2_PIX_FMT_YUV422P: fh->mode.color = COLOR_YUVPL; break; @@ -1140,7 +1161,7 @@ static u32 get_transfer_size(struct s2255_mode *mode) } } outImageSize = linesPerFrame * pixelsPerLine; - if (mode->color != COLOR_Y8) { + if ((mode->color & MASK_COLOR) != COLOR_Y8) { /* 2 bytes/pixel if not monochrome */ outImageSize *= 2; } @@ -1186,12 +1207,17 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, u32 *buffer; unsigned long chn_rev; + mutex_lock(&dev->lock); chn_rev = G_chnmap[chn]; dprintk(3, "mode scale [%ld] %p %d\n", chn, mode, mode->scale); dprintk(3, "mode scale [%ld] %p %d\n", chn, &dev->mode[chn], dev->mode[chn].scale); dprintk(2, "mode contrast %x\n", mode->contrast); + /* if JPEG, set the quality */ + if ((mode->color & MASK_COLOR) == COLOR_JPG) + mode->color = (dev->jc[chn].quality << 8) | COLOR_JPG; + /* save the mode */ dev->mode[chn] = *mode; dev->req_image_size[chn] = get_transfer_size(mode); @@ -1200,6 +1226,7 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); + mutex_unlock(&dev->lock); return -ENOMEM; } @@ -1215,12 +1242,20 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, dprintk(1, "set mode done chn %lu, %d\n", chn, res); /* wait at least 3 frames before continuing */ - if (mode->restart) - msleep(125); + if (mode->restart) { + dev->setmode_ready[chn] = 0; + wait_event_timeout(dev->wait_setmode[chn], + (dev->setmode_ready[chn] != 0), + msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); + if (dev->setmode_ready[chn] != 1) { + printk(KERN_DEBUG "s2255: no set mode response\n"); + res = -EFAULT; + } + } /* clear the restart flag */ dev->mode[chn].restart = 0; - + mutex_unlock(&dev->lock); return res; } @@ -1269,8 +1304,9 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) dev->last_frame[chn] = -1; dev->bad_payload[chn] = 0; dev->cur_frame[chn] = 0; + dev->frame_count[chn] = 0; for (j = 0; j < SYS_FRAMES; j++) { - dev->buffer[chn].frame[j].ulState = 0; + dev->buffer[chn].frame[j].ulState = S2255_READ_IDLE; dev->buffer[chn].frame[j].cur_size = 0; } res = videobuf_streamon(&fh->vb_vidq); @@ -1446,6 +1482,27 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return -EINVAL; } +static int vidioc_g_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jc) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + *jc = dev->jc[fh->channel]; + dprintk(2, "getting jpegcompression, quality %d\n", jc->quality); + return 0; +} + +static int vidioc_s_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jc) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + if (jc->quality < 0 || jc->quality > 100) + return -EINVAL; + dev->jc[fh->channel].quality = jc->quality; + dprintk(2, "setting jpeg quality %d\n", jc->quality); + return 0; +} static int s2255_open(struct inode *inode, struct file *file) { int minor = iminor(inode); @@ -1455,6 +1512,7 @@ static int s2255_open(struct inode *inode, struct file *file) enum v4l2_buf_type type = 0; int i = 0; int cur_channel = -1; + int state; dprintk(1, "s2255: open called (minor=%d)\n", minor); lock_kernel(); @@ -1471,47 +1529,77 @@ static int s2255_open(struct inode *inode, struct file *file) if ((NULL == dev) || (cur_channel == -1)) { unlock_kernel(); - dprintk(1, "s2255: openv4l no dev\n"); + printk(KERN_INFO "s2255: openv4l no dev\n"); return -ENODEV; } + if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING) { + unlock_kernel(); + printk(KERN_INFO "disconnecting\n"); + return -ENODEV; + } + kref_get(&dev->kref); mutex_lock(&dev->open_lock); dev->users[cur_channel]++; dprintk(4, "s2255: open_handles %d\n", dev->users[cur_channel]); - if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_FAILED) { + switch (atomic_read(&dev->fw_data->fw_state)) { + case S2255_FW_FAILED: err("2255 firmware load failed. retrying.\n"); - s2255_fwload_start(dev); + s2255_fwload_start(dev, 1); wait_event_timeout(dev->fw_data->wait_fw, - (atomic_read(&dev->fw_data->fw_state) - != S2255_FW_NOTLOADED), + ((atomic_read(&dev->fw_data->fw_state) + == S2255_FW_SUCCESS) || + (atomic_read(&dev->fw_data->fw_state) + == S2255_FW_DISCONNECTING)), msecs_to_jiffies(S2255_LOAD_TIMEOUT)); - if (atomic_read(&dev->fw_data->fw_state) - != S2255_FW_SUCCESS) { - printk(KERN_INFO "2255 FW load failed.\n"); - dev->users[cur_channel]--; - mutex_unlock(&dev->open_lock); - unlock_kernel(); - return -EFAULT; - } - } else if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_NOTLOADED) { + break; + case S2255_FW_NOTLOADED: + case S2255_FW_LOADED_DSPWAIT: /* give S2255_LOAD_TIMEOUT time for firmware to load in case driver loaded and then device immediately opened */ printk(KERN_INFO "%s waiting for firmware load\n", __func__); wait_event_timeout(dev->fw_data->wait_fw, - (atomic_read(&dev->fw_data->fw_state) - != S2255_FW_NOTLOADED), - msecs_to_jiffies(S2255_LOAD_TIMEOUT)); - if (atomic_read(&dev->fw_data->fw_state) - != S2255_FW_SUCCESS) { - printk(KERN_INFO "2255 firmware not loaded" - "try again\n"); - dev->users[cur_channel]--; - mutex_unlock(&dev->open_lock); - unlock_kernel(); - return -EBUSY; + ((atomic_read(&dev->fw_data->fw_state) + == S2255_FW_SUCCESS) || + (atomic_read(&dev->fw_data->fw_state) + == S2255_FW_DISCONNECTING)), + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + break; + case S2255_FW_SUCCESS: + default: + break; + } + state = atomic_read(&dev->fw_data->fw_state); + if (state != S2255_FW_SUCCESS) { + int rc; + switch (state) { + case S2255_FW_FAILED: + printk(KERN_INFO "2255 FW load failed. %d\n", state); + rc = -ENODEV; + break; + case S2255_FW_DISCONNECTING: + printk(KERN_INFO "%s: disconnecting\n", __func__); + rc = -ENODEV; + break; + case S2255_FW_LOADED_DSPWAIT: + case S2255_FW_NOTLOADED: + printk(KERN_INFO "%s: firmware not loaded yet" + "please try again later\n", + __func__); + rc = -EAGAIN; + break; + default: + printk(KERN_INFO "%s: unknown state\n", __func__); + rc = -EFAULT; + break; } + dev->users[cur_channel]--; + mutex_unlock(&dev->open_lock); + kref_put(&dev->kref, s2255_destroy); + unlock_kernel(); + return rc; } /* allocate + initialize per filehandle data */ @@ -1519,6 +1607,7 @@ static int s2255_open(struct inode *inode, struct file *file) if (NULL == fh) { dev->users[cur_channel]--; mutex_unlock(&dev->open_lock); + kref_put(&dev->kref, s2255_destroy); unlock_kernel(); return -ENOMEM; } @@ -1533,6 +1622,13 @@ static int s2255_open(struct inode *inode, struct file *file) fh->height = NUM_LINES_4CIFS_NTSC * 2; fh->channel = cur_channel; + /* configure channel to default state */ + if (!dev->chn_configured[cur_channel]) { + s2255_set_mode(dev, cur_channel, &fh->mode); + dev->chn_configured[cur_channel] = 1; + } + + /* Put all controls at a sane state */ for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) qctl_regs[i] = s2255_qctrl[i].default_value; @@ -1551,7 +1647,6 @@ static int s2255_open(struct inode *inode, struct file *file) V4L2_FIELD_INTERLACED, sizeof(struct s2255_buffer), fh); - kref_get(&dev->kref); mutex_unlock(&dev->open_lock); unlock_kernel(); return 0; @@ -1575,30 +1670,24 @@ static unsigned int s2255_poll(struct file *file, static void s2255_destroy(struct kref *kref) { struct s2255_dev *dev = to_s2255_dev(kref); + struct list_head *list; + int i; if (!dev) { printk(KERN_ERR "s2255drv: kref problem\n"); return; } - - /* - * Wake up any firmware load waiting (only done in .open, - * which holds the open_lock mutex) - */ atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); wake_up(&dev->fw_data->wait_fw); - - /* prevent s2255_disconnect from racing s2255_open */ + for (i = 0; i < MAX_CHANNELS; i++) { + dev->setmode_ready[i] = 1; + wake_up(&dev->wait_setmode[i]); + } mutex_lock(&dev->open_lock); + /* reset the DSP so firmware can be reload next time */ + s2255_reset_dsppower(dev); s2255_exit_v4l(dev); - /* - * device unregistered so no longer possible to open. open_mutex - * can be unlocked and timers deleted afterwards. - */ - mutex_unlock(&dev->open_lock); - /* board shutdown stops the read pipe if it is running */ s2255_board_shutdown(dev); - /* make sure firmware still not trying to load */ del_timer(&dev->timer); /* only started in .probe and .open */ @@ -1608,23 +1697,19 @@ static void s2255_destroy(struct kref *kref) usb_free_urb(dev->fw_data->fw_urb); dev->fw_data->fw_urb = NULL; } - - /* - * delete the dsp_wait timer, which sets the firmware - * state on completion. This is done before fw_data - * is freed below. - */ - - del_timer(&dev->fw_data->dsp_wait); /* only started in .open */ - if (dev->fw_data->fw) release_firmware(dev->fw_data->fw); kfree(dev->fw_data->pfw_data); kfree(dev->fw_data); - usb_put_dev(dev->udev); dprintk(1, "%s", __func__); kfree(dev); + + while (!list_empty(&s2255_devlist)) { + list = s2255_devlist.next; + list_del(list); + } + mutex_unlock(&dev->open_lock); } static int s2255_close(struct inode *inode, struct file *file) @@ -1708,6 +1793,8 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidioc_cgmbuf, #endif + .vidioc_s_jpegcomp = vidioc_s_jpegcomp, + .vidioc_g_jpegcomp = vidioc_g_jpegcomp, }; static struct video_device template = { @@ -1746,7 +1833,7 @@ static int s2255_probe_v4l(struct s2255_dev *dev) ret = video_register_device(dev->vdev[i], VFL_TYPE_GRABBER, cur_nr + i); - dev->vdev[i]->priv = dev; + video_set_drvdata(dev->vdev[i], dev); if (ret != 0) { dev_err(&dev->udev->dev, @@ -1760,18 +1847,16 @@ static int s2255_probe_v4l(struct s2255_dev *dev) static void s2255_exit_v4l(struct s2255_dev *dev) { - struct list_head *list; + int i; - /* unregister the video devices */ - while (!list_empty(&s2255_devlist)) { - list = s2255_devlist.next; - list_del(list); - } for (i = 0; i < MAX_CHANNELS; i++) { - if (-1 != dev->vdev[i]->minor) + if (-1 != dev->vdev[i]->minor) { video_unregister_device(dev->vdev[i]); - else + printk(KERN_INFO "s2255 unregistered\n"); + } else { video_device_release(dev->vdev[i]); + printk(KERN_INFO "s2255 released\n"); + } } } @@ -1781,134 +1866,123 @@ static void s2255_exit_v4l(struct s2255_dev *dev) * function again). * * Received frame structure: - * bytes 0-3: marker : 0x2255DA4AL (FRAME_MARKER) + * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME) * bytes 4-7: channel: 0-3 * bytes 8-11: payload size: size of the frame * bytes 12-payloadsize+12: frame data */ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) { - static int dbgsync; /* = 0; */ char *pdest; u32 offset = 0; - int bsync = 0; - int btrunc = 0; + int bframe = 0; char *psrc; unsigned long copy_size; unsigned long size; s32 idx = -1; struct s2255_framei *frm; unsigned char *pdata; - unsigned long cur_size; - int bsearch = 0; - struct s2255_bufferi *buf; + dprintk(100, "buffer to user\n"); idx = dev->cur_frame[dev->cc]; - buf = &dev->buffer[dev->cc]; - frm = &buf->frame[idx]; - - if (frm->ulState == 0) { - frm->ulState = 1; - frm->cur_size = 0; - bsearch = 1; - } else if (frm->ulState == 2) { - /* system frame was not freed */ - dprintk(2, "sys frame not free. overrun ringbuf\n"); - bsearch = 1; - frm->ulState = 1; - frm->cur_size = 0; - } - - if (bsearch) { - if (*(s32 *) pipe_info->transfer_buffer != FRAME_MARKER) { - u32 jj; - if (dbgsync == 0) { - dprintk(3, "not synched, discarding all packets" - "until marker\n"); + frm = &dev->buffer[dev->cc].frame[idx]; - dbgsync++; - } - pdata = (unsigned char *)pipe_info->transfer_buffer; - for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); - jj++) { - if (*(s32 *) pdata == FRAME_MARKER) { - int cc; - dprintk(3, - "found frame marker at offset:" - " %d [%x %x]\n", jj, pdata[0], - pdata[1]); - offset = jj; - bsync = 1; - cc = *(u32 *) (pdata + sizeof(u32)); - if (cc >= MAX_CHANNELS) { - printk(KERN_ERR - "bad channel\n"); - return -EINVAL; - } - /* reverse it */ - dev->cc = G_chnmap[cc]; + if (frm->ulState == S2255_READ_IDLE) { + int jj; + unsigned int cc; + s32 *pdword; + int payload; + /* search for marker codes */ + pdata = (unsigned char *)pipe_info->transfer_buffer; + for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { + switch (*(s32 *) pdata) { + case S2255_MARKER_FRAME: + pdword = (s32 *)pdata; + dprintk(4, "found frame marker at offset:" + " %d [%x %x]\n", jj, pdata[0], + pdata[1]); + offset = jj + PREFIX_SIZE; + bframe = 1; + cc = pdword[1]; + if (cc >= MAX_CHANNELS) { + printk(KERN_ERR + "bad channel\n"); + return -EINVAL; + } + /* reverse it */ + dev->cc = G_chnmap[cc]; + payload = pdword[3]; + if (payload > dev->req_image_size[dev->cc]) { + dev->bad_payload[dev->cc]++; + /* discard the bad frame */ + return -EINVAL; + } + dev->pkt_size[dev->cc] = payload; + dev->jpg_size[dev->cc] = pdword[4]; + break; + case S2255_MARKER_RESPONSE: + pdword = (s32 *)pdata; + pdata += DEF_USB_BLOCK; + jj += DEF_USB_BLOCK; + if (pdword[1] >= MAX_CHANNELS) + break; + cc = G_chnmap[pdword[1]]; + if (!(cc >= 0 && cc < MAX_CHANNELS)) + break; + switch (pdword[2]) { + case 0x01: + /* check if channel valid */ + /* set mode ready */ + dev->setmode_ready[cc] = 1; + wake_up(&dev->wait_setmode[cc]); + dprintk(5, "setmode ready %d\n", cc); break; + case 0x10: + + dev->chn_ready |= (1 << cc); + if ((dev->chn_ready & 0x0f) != 0x0f) + break; + /* all channels ready */ + printk(KERN_INFO "s2255: fw loaded\n"); + atomic_set(&dev->fw_data->fw_state, + S2255_FW_SUCCESS); + wake_up(&dev->fw_data->wait_fw); + break; + default: + printk(KERN_INFO "s2255 unknwn resp\n"); } + default: pdata++; + break; } - if (bsync == 0) - return -EINVAL; - } else { - u32 *pword; - u32 payload; - int cc; - dbgsync = 0; - bsync = 1; - pword = (u32 *) pipe_info->transfer_buffer; - cc = pword[1]; - - if (cc >= MAX_CHANNELS) { - printk("invalid channel found. " - "throwing out data!\n"); - return -EINVAL; - } - dev->cc = G_chnmap[cc]; - payload = pword[2]; - if (payload != dev->req_image_size[dev->cc]) { - dprintk(1, "[%d][%d]unexpected payload: %d" - "required: %lu \n", cc, dev->cc, - payload, dev->req_image_size[dev->cc]); - dev->bad_payload[dev->cc]++; - /* discard the bad frame */ - return -EINVAL; - } - - } - } - /* search done. now find out if should be acquiring - on this channel */ - if (!dev->b_acquire[dev->cc]) { - frm->ulState = 0; - return -EINVAL; + if (bframe) + break; + } /* for */ + if (!bframe) + return -EINVAL; } + idx = dev->cur_frame[dev->cc]; frm = &dev->buffer[dev->cc].frame[idx]; - if (frm->ulState == 0) { - frm->ulState = 1; - frm->cur_size = 0; - } else if (frm->ulState == 2) { - /* system frame ring buffer overrun */ - dprintk(2, "sys frame overrun. overwriting frame %d %d\n", - dev->cc, idx); - frm->ulState = 1; - frm->cur_size = 0; + /* search done. now find out if should be acquiring on this channel */ + if (!dev->b_acquire[dev->cc]) { + /* we found a frame, but this channel is turned off */ + frm->ulState = S2255_READ_IDLE; + return -EINVAL; } - if (bsync) { - /* skip the marker 512 bytes (and offset if out of sync) */ - psrc = (u8 *)pipe_info->transfer_buffer + offset + PREFIX_SIZE; - } else { - psrc = (u8 *)pipe_info->transfer_buffer; + if (frm->ulState == S2255_READ_IDLE) { + frm->ulState = S2255_READ_FRAME; + frm->cur_size = 0; } + /* skip the marker 512 bytes (and offset if out of sync) */ + psrc = (u8 *)pipe_info->transfer_buffer + offset; + + if (frm->lpvbits == NULL) { dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d", frm, dev, dev->cc, idx); @@ -1917,33 +1991,20 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) pdest = frm->lpvbits + frm->cur_size; - if (bsync) { - copy_size = - (pipe_info->cur_transfer_size - offset) - PREFIX_SIZE; - if (copy_size > pipe_info->cur_transfer_size) { - printk("invalid copy size, overflow!\n"); - return -ENOMEM; - } - } else { - copy_size = pipe_info->cur_transfer_size; - } + copy_size = (pipe_info->cur_transfer_size - offset); - cur_size = frm->cur_size; - size = dev->req_image_size[dev->cc]; + size = dev->pkt_size[dev->cc] - PREFIX_SIZE; - if ((copy_size + cur_size) > size) { - copy_size = size - cur_size; - btrunc = 1; - } + /* sanity check on pdest */ + if ((copy_size + frm->cur_size) < dev->req_image_size[dev->cc]) + memcpy(pdest, psrc, copy_size); - memcpy(pdest, psrc, copy_size); - cur_size += copy_size; frm->cur_size += copy_size; - dprintk(50, "cur_size size %lu size %lu \n", cur_size, size); + dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size); + + if (frm->cur_size >= size) { - if (cur_size >= (size - PREFIX_SIZE)) { u32 cc = dev->cc; - frm->ulState = 2; dprintk(2, "****************[%d]Buffer[%d]full*************\n", cc, idx); dev->last_frame[cc] = dev->cur_frame[cc]; @@ -1952,16 +2013,13 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if ((dev->cur_frame[cc] == SYS_FRAMES) || (dev->cur_frame[cc] == dev->buffer[cc].dwFrames)) dev->cur_frame[cc] = 0; - - /* signal the semaphore for this channel */ + /* frame ready */ if (dev->b_acquire[cc]) - s2255_got_frame(dev, cc); + s2255_got_frame(dev, cc, dev->jpg_size[cc]); dev->frame_count[cc]++; - } - /* frame was truncated */ - if (btrunc) { - /* return more data to process */ - return EAGAIN; + frm->ulState = S2255_READ_IDLE; + frm->cur_size = 0; + } /* done successfully */ return 0; @@ -1980,8 +2038,8 @@ static void s2255_read_video_callback(struct s2255_dev *dev, } /* otherwise copy to the system buffers */ res = save_frame(dev, pipe_info); - if (res == EAGAIN) - save_frame(dev, pipe_info); + if (res != 0) + dprintk(4, "s2255: read callback failed\n"); dprintk(50, "callback read video done\n"); return; @@ -2101,11 +2159,9 @@ static int s2255_board_init(struct s2255_dev *dev) memset(pipe, 0, sizeof(*pipe)); pipe->dev = dev; - pipe->cur_transfer_size = DEFAULT_PIPE_USBBLOCK; - pipe->max_transfer_size = MAX_PIPE_USBBLOCK; + pipe->cur_transfer_size = S2255_USB_XFER_SIZE; + pipe->max_transfer_size = S2255_USB_XFER_SIZE; - if (pipe->cur_transfer_size > pipe->max_transfer_size) - pipe->cur_transfer_size = pipe->max_transfer_size; pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, GFP_KERNEL); if (pipe->transfer_buffer == NULL) { @@ -2125,6 +2181,7 @@ static int s2255_board_init(struct s2255_dev *dev) for (j = 0; j < MAX_CHANNELS; j++) { dev->b_acquire[j] = 0; dev->mode[j] = mode_def; + dev->jc[j].quality = S2255_DEF_JPEG_QUAL; dev->cur_fmt[j] = &formats[0]; dev->mode[j].restart = 1; dev->req_image_size[j] = get_transfer_size(&mode_def); @@ -2329,7 +2386,7 @@ static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn) kfree(buffer); dev->b_acquire[chn] = 0; - return 0; + return res; } static void s2255_stop_readpipe(struct s2255_dev *dev) @@ -2365,8 +2422,10 @@ static void s2255_stop_readpipe(struct s2255_dev *dev) return; } -static void s2255_fwload_start(struct s2255_dev *dev) +static void s2255_fwload_start(struct s2255_dev *dev, int reset) { + if (reset) + s2255_reset_dsppower(dev); dev->fw_data->fw_size = dev->fw_data->fw->size; atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED); memcpy(dev->fw_data->pfw_data, @@ -2389,6 +2448,8 @@ static int s2255_probe(struct usb_interface *interface, struct usb_endpoint_descriptor *endpoint; int i; int retval = -ENOMEM; + __le32 *pdata; + int fw_size; dprintk(2, "s2255: probe\n"); @@ -2443,6 +2504,8 @@ static int s2255_probe(struct usb_interface *interface, dev->timer.data = (unsigned long)dev->fw_data; init_waitqueue_head(&dev->fw_data->wait_fw); + for (i = 0; i < MAX_CHANNELS; i++) + init_waitqueue_head(&dev->wait_setmode[i]); dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -2462,16 +2525,30 @@ static int s2255_probe(struct usb_interface *interface, printk(KERN_ERR "sensoray 2255 failed to get firmware\n"); goto error; } + /* check the firmware is valid */ + fw_size = dev->fw_data->fw->size; + pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; + if (*pdata != S2255_FW_MARKER) { + printk(KERN_INFO "Firmware invalid.\n"); + retval = -ENODEV; + goto error; + } else { + /* make sure firmware is the latest */ + __le32 *pRel; + pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; + printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); + } /* loads v4l specific */ s2255_probe_v4l(dev); + usb_reset_device(dev->udev); /* load 2255 board specific */ s2255_board_init(dev); dprintk(4, "before probe done %p\n", dev); spin_lock_init(&dev->slock); - s2255_fwload_start(dev); + s2255_fwload_start(dev, 0); dev_info(&interface->dev, "Sensoray 2255 detected\n"); return 0; error: @@ -2482,14 +2559,30 @@ error: static void s2255_disconnect(struct usb_interface *interface) { struct s2255_dev *dev = NULL; + int i; dprintk(1, "s2255: disconnect interface %p\n", interface); dev = usb_get_intfdata(interface); + + /* + * wake up any of the timers to allow open_lock to be + * acquired sooner + */ + atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); + wake_up(&dev->fw_data->wait_fw); + for (i = 0; i < MAX_CHANNELS; i++) { + dev->setmode_ready[i] = 1; + wake_up(&dev->wait_setmode[i]); + } + + mutex_lock(&dev->open_lock); + usb_set_intfdata(interface, NULL); + mutex_unlock(&dev->open_lock); + if (dev) { kref_put(&dev->kref, s2255_destroy); dprintk(1, "s2255drv: disconnect\n"); dev_info(&interface->dev, "s2255usb now disconnected\n"); } - usb_set_intfdata(interface, NULL); } static struct usb_driver s2255_driver = { diff --git a/linux/drivers/media/video/saa5246a.c b/linux/drivers/media/video/saa5246a.c index 1bfc49822..355490467 100644 --- a/linux/drivers/media/video/saa5246a.c +++ b/linux/drivers/media/video/saa5246a.c @@ -43,137 +43,364 @@ #include <linux/mm.h> #include <linux/init.h> #include <linux/i2c.h> -#include <linux/videotext.h> #include <linux/smp_lock.h> -#include "compat.h" +#include <linux/mutex.h> +#include <linux/videotext.h> #include <linux/videodev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#include <linux/mutex.h> - -#include "saa5246a.h" +#include <media/v4l2-i2c-drv-legacy.h> +#include "compat.h" MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver"); MODULE_LICENSE("GPL"); -struct saa5246a_device -{ - u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; - int is_searching[NUM_DAUS]; - struct i2c_client *client; - struct mutex lock; -}; +#define MAJOR_VERSION 1 /* driver major version number */ +#define MINOR_VERSION 8 /* driver minor version number */ + +/* Number of DAUs = number of pages that can be searched at the same time. */ +#define NUM_DAUS 4 + +#define NUM_ROWS_PER_PAGE 40 + +/* first column is 0 (not 1) */ +#define POS_TIME_START 32 +#define POS_TIME_END 39 + +#define POS_HEADER_START 7 +#define POS_HEADER_END 31 + +/* Returns 'true' if the part of the videotext page described with req contains + (at least parts of) the time field */ +#define REQ_CONTAINS_TIME(p_req) \ + ((p_req)->start <= POS_TIME_END && \ + (p_req)->end >= POS_TIME_START) + +/* Returns 'true' if the part of the videotext page described with req contains + (at least parts of) the page header */ +#define REQ_CONTAINS_HEADER(p_req) \ + ((p_req)->start <= POS_HEADER_END && \ + (p_req)->end >= POS_HEADER_START) + +/*****************************************************************************/ +/* Mode register numbers of the SAA5246A */ +/*****************************************************************************/ +#define SAA5246A_REGISTER_R0 0 +#define SAA5246A_REGISTER_R1 1 +#define SAA5246A_REGISTER_R2 2 +#define SAA5246A_REGISTER_R3 3 +#define SAA5246A_REGISTER_R4 4 +#define SAA5246A_REGISTER_R5 5 +#define SAA5246A_REGISTER_R6 6 +#define SAA5246A_REGISTER_R7 7 +#define SAA5246A_REGISTER_R8 8 +#define SAA5246A_REGISTER_R9 9 +#define SAA5246A_REGISTER_R10 10 +#define SAA5246A_REGISTER_R11 11 +#define SAA5246A_REGISTER_R11B 11 + +/* SAA5246A mode registers often autoincrement to the next register. + Therefore we use variable argument lists. The following macro indicates + the end of a command list. */ +#define COMMAND_END (-1) + +/*****************************************************************************/ +/* Contents of the mode registers of the SAA5246A */ +/*****************************************************************************/ +/* Register R0 (Advanced Control) */ +#define R0_SELECT_R11 0x00 +#define R0_SELECT_R11B 0x01 + +#define R0_PLL_TIME_CONSTANT_LONG 0x00 +#define R0_PLL_TIME_CONSTANT_SHORT 0x02 + +#define R0_ENABLE_nODD_EVEN_OUTPUT 0x00 +#define R0_DISABLE_nODD_EVEN_OUTPUT 0x04 + +#define R0_ENABLE_HDR_POLL 0x00 +#define R0_DISABLE_HDR_POLL 0x10 + +#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00 +#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20 + +#define R0_NO_FREE_RUN_PLL 0x00 +#define R0_FREE_RUN_PLL 0x40 + +#define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00 +#define R0_AUTOMATIC_FASTEXT_PROMPT 0x80 + +/* Register R1 (Mode) */ +#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00 +#define R1_NON_INTERLACED_312_313_LINES 0x01 +#define R1_NON_INTERLACED_312_312_LINES 0x02 +#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03 +#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07 + +#define R1_DEW 0x00 +#define R1_FULL_FIELD 0x08 + +#define R1_EXTENDED_PACKET_DISABLE 0x00 +#define R1_EXTENDED_PACKET_ENABLE 0x10 + +#define R1_DAUS_ALL_ON 0x00 +#define R1_DAUS_ALL_OFF 0x20 + +#define R1_7_BITS_PLUS_PARITY 0x00 +#define R1_8_BITS_NO_PARITY 0x40 + +#define R1_VCS_TO_SCS 0x00 +#define R1_NO_VCS_TO_SCS 0x80 + +/* Register R2 (Page request address) */ +#define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00 +#define R2_IN_R3_SELECT_PAGE_TENS 0x01 +#define R2_IN_R3_SELECT_PAGE_UNITS 0x02 +#define R2_IN_R3_SELECT_HOURS_TENS 0x03 +#define R2_IN_R3_SELECT_HOURS_UNITS 0x04 +#define R2_IN_R3_SELECT_MINUTES_TENS 0x05 +#define R2_IN_R3_SELECT_MINUTES_UNITS 0x06 + +#define R2_DAU_0 0x00 +#define R2_DAU_1 0x10 +#define R2_DAU_2 0x20 +#define R2_DAU_3 0x30 + +#define R2_BANK_0 0x00 +#define R2_BANK 1 0x40 + +#define R2_HAMMING_CHECK_ON 0x80 +#define R2_HAMMING_CHECK_OFF 0x00 + +/* Register R3 (Page request data) */ +#define R3_PAGE_HUNDREDS_0 0x00 +#define R3_PAGE_HUNDREDS_1 0x01 +#define R3_PAGE_HUNDREDS_2 0x02 +#define R3_PAGE_HUNDREDS_3 0x03 +#define R3_PAGE_HUNDREDS_4 0x04 +#define R3_PAGE_HUNDREDS_5 0x05 +#define R3_PAGE_HUNDREDS_6 0x06 +#define R3_PAGE_HUNDREDS_7 0x07 -static struct video_device saa_template; /* Declared near bottom */ +#define R3_HOLD_PAGE 0x00 +#define R3_UPDATE_PAGE 0x08 -/* Addresses to scan */ -static unsigned short normal_i2c[] = { I2C_ADDRESS, I2C_CLIENT_END }; +#define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00 +#define R3_PAGE_HUNDREDS_DO_CARE 0x10 -I2C_CLIENT_INSMOD; +#define R3_PAGE_TENS_DO_NOT_CARE 0x00 +#define R3_PAGE_TENS_DO_CARE 0x10 -static struct i2c_client client_template; +#define R3_PAGE_UNITS_DO_NOT_CARE 0x00 +#define R3_PAGE_UNITS_DO_CARE 0x10 -static int saa5246a_attach(struct i2c_adapter *adap, int addr, int kind) -{ - int pgbuf; - int err; - struct i2c_client *client; - struct video_device *vd; - struct saa5246a_device *t; +#define R3_HOURS_TENS_DO_NOT_CARE 0x00 +#define R3_HOURS_TENS_DO_CARE 0x10 - printk(KERN_INFO "saa5246a: teletext chip found.\n"); - client=kmalloc(sizeof(*client), GFP_KERNEL); - if(client==NULL) - return -ENOMEM; - client_template.adapter = adap; - client_template.addr = addr; - memcpy(client, &client_template, sizeof(*client)); - t = kzalloc(sizeof(*t), GFP_KERNEL); - if(t==NULL) - { - kfree(client); - return -ENOMEM; - } - strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); - mutex_init(&t->lock); +#define R3_HOURS_UNITS_DO_NOT_CARE 0x00 +#define R3_HOURS_UNITS_DO_CARE 0x10 - /* - * Now create a video4linux device - */ +#define R3_MINUTES_TENS_DO_NOT_CARE 0x00 +#define R3_MINUTES_TENS_DO_CARE 0x10 - vd = video_device_alloc(); - if(vd==NULL) - { - kfree(t); - kfree(client); - return -ENOMEM; - } - i2c_set_clientdata(client, vd); - memcpy(vd, &saa_template, sizeof(*vd)); +#define R3_MINUTES_UNITS_DO_NOT_CARE 0x00 +#define R3_MINUTES_UNITS_DO_CARE 0x10 - for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) - { - memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); - t->is_searching[pgbuf] = false; - } - vd->priv=t; +/* Register R4 (Display chapter) */ +#define R4_DISPLAY_PAGE_0 0x00 +#define R4_DISPLAY_PAGE_1 0x01 +#define R4_DISPLAY_PAGE_2 0x02 +#define R4_DISPLAY_PAGE_3 0x03 +#define R4_DISPLAY_PAGE_4 0x04 +#define R4_DISPLAY_PAGE_5 0x05 +#define R4_DISPLAY_PAGE_6 0x06 +#define R4_DISPLAY_PAGE_7 0x07 +/* Register R5 (Normal display control) */ +#define R5_PICTURE_INSIDE_BOXING_OFF 0x00 +#define R5_PICTURE_INSIDE_BOXING_ON 0x01 - /* - * Register it - */ +#define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00 +#define R5_PICTURE_OUTSIDE_BOXING_ON 0x02 - if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0) - { - kfree(t); - kfree(client); - video_device_release(vd); - return err; - } - t->client = client; - i2c_attach_client(client); - return 0; -} +#define R5_TEXT_INSIDE_BOXING_OFF 0x00 +#define R5_TEXT_INSIDE_BOXING_ON 0x04 -/* - * We do most of the hard work when we become a device on the i2c. - */ -static int saa5246a_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, saa5246a_attach); - return 0; -} +#define R5_TEXT_OUTSIDE_BOXING_OFF 0x00 +#define R5_TEXT_OUTSIDE_BOXING_ON 0x08 -static int saa5246a_detach(struct i2c_client *client) -{ - struct video_device *vd = i2c_get_clientdata(client); - i2c_detach_client(client); - video_unregister_device(vd); - kfree(vd->priv); - kfree(client); - return 0; -} +#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 +#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 -/* - * I2C interfaces - */ +#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 +#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 + +#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 +#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 + +#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 +#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 + +/* Register R6 (Newsflash display) */ +#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01 + +#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02 + +#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04 + +#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08 + +#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 + +#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 + +#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 + +#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 + +/* Register R7 (Display mode) */ +#define R7_BOX_OFF_ROW_0 0x00 +#define R7_BOX_ON_ROW_0 0x01 + +#define R7_BOX_OFF_ROW_1_TO_23 0x00 +#define R7_BOX_ON_ROW_1_TO_23 0x02 + +#define R7_BOX_OFF_ROW_24 0x00 +#define R7_BOX_ON_ROW_24 0x04 + +#define R7_SINGLE_HEIGHT 0x00 +#define R7_DOUBLE_HEIGHT 0x08 + +#define R7_TOP_HALF 0x00 +#define R7_BOTTOM_HALF 0x10 + +#define R7_REVEAL_OFF 0x00 +#define R7_REVEAL_ON 0x20 + +#define R7_CURSER_OFF 0x00 +#define R7_CURSER_ON 0x40 + +#define R7_STATUS_BOTTOM 0x00 +#define R7_STATUS_TOP 0x80 + +/* Register R8 (Active chapter) */ +#define R8_ACTIVE_CHAPTER_0 0x00 +#define R8_ACTIVE_CHAPTER_1 0x01 +#define R8_ACTIVE_CHAPTER_2 0x02 +#define R8_ACTIVE_CHAPTER_3 0x03 +#define R8_ACTIVE_CHAPTER_4 0x04 +#define R8_ACTIVE_CHAPTER_5 0x05 +#define R8_ACTIVE_CHAPTER_6 0x06 +#define R8_ACTIVE_CHAPTER_7 0x07 + +#define R8_CLEAR_MEMORY 0x08 +#define R8_DO_NOT_CLEAR_MEMORY 0x00 + +/* Register R9 (Curser row) */ +#define R9_CURSER_ROW_0 0x00 +#define R9_CURSER_ROW_1 0x01 +#define R9_CURSER_ROW_2 0x02 +#define R9_CURSER_ROW_25 0x19 + +/* Register R10 (Curser column) */ +#define R10_CURSER_COLUMN_0 0x00 +#define R10_CURSER_COLUMN_6 0x06 +#define R10_CURSER_COLUMN_8 0x08 + +/*****************************************************************************/ +/* Row 25 control data in column 0 to 9 */ +/*****************************************************************************/ +#define ROW25_COLUMN0_PAGE_UNITS 0x0F + +#define ROW25_COLUMN1_PAGE_TENS 0x0F -static struct i2c_driver i2c_driver_videotext = +#define ROW25_COLUMN2_MINUTES_UNITS 0x0F + +#define ROW25_COLUMN3_MINUTES_TENS 0x07 +#define ROW25_COLUMN3_DELETE_PAGE 0x08 + +#define ROW25_COLUMN4_HOUR_UNITS 0x0F + +#define ROW25_COLUMN5_HOUR_TENS 0x03 +#define ROW25_COLUMN5_INSERT_HEADLINE 0x04 +#define ROW25_COLUMN5_INSERT_SUBTITLE 0x08 + +#define ROW25_COLUMN6_SUPPRESS_HEADER 0x01 +#define ROW25_COLUMN6_UPDATE_PAGE 0x02 +#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04 +#define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08 + +#define ROW25_COLUMN7_SERIAL_MODE 0x01 +#define ROW25_COLUMN7_CHARACTER_SET 0x0E + +#define ROW25_COLUMN8_PAGE_HUNDREDS 0x07 +#define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10 + +#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20 + +#define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10 + +/*****************************************************************************/ +/* Helper macros for extracting page, hour and minute digits */ +/*****************************************************************************/ +/* BYTE_POS 0 is at row 0, column 0, + BYTE_POS 1 is at row 0, column 1, + BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40) + BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40), + ... */ +#define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE) +#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE) + +/*****************************************************************************/ +/* Helper macros for extracting page, hour and minute digits */ +/*****************************************************************************/ +/* Macros for extracting hundreds, tens and units of a page number which + must be in the range 0 ... 0x799. + Note that page is coded in hexadecimal, i.e. 0x123 means page 123. + page 0x.. means page 8.. */ +#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7) +#define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF) +#define UNITS_OF_PAGE(page) ((page) & 0xF) + +/* Macros for extracting tens and units of a hour information which + must be in the range 0 ... 0x24. + Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */ +#define TENS_OF_HOUR(hour) ((hour) / 0x10) +#define UNITS_OF_HOUR(hour) ((hour) & 0xF) + +/* Macros for extracting tens and units of a minute information which + must be in the range 0 ... 0x59. + Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */ +#define TENS_OF_MINUTE(minute) ((minute) / 0x10) +#define UNITS_OF_MINUTE(minute) ((minute) & 0xF) + +#define HOUR_MAX 0x23 +#define MINUTE_MAX 0x59 +#define PAGE_MAX 0x8FF + + +struct saa5246a_device { - .driver = { - .name = IF_NAME, /* name */ - }, - .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ - .attach_adapter = saa5246a_probe, - .detach_client = saa5246a_detach, + u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; + int is_searching[NUM_DAUS]; + struct i2c_client *client; + unsigned long in_use; + struct mutex lock; }; -static struct i2c_client client_template = { - .driver = &i2c_driver_videotext, - .name = "(unset)", -}; +static struct video_device saa_template; /* Declared near bottom */ + +/* + * I2C interfaces + */ static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) { @@ -581,8 +808,8 @@ static inline int saa5246a_stop_dau(struct saa5246a_device *t, static int do_saa5246a_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct video_device *vd = video_devdata(file); - struct saa5246a_device *t=vd->priv; + struct saa5246a_device *t = video_drvdata(file); + switch(cmd) { case VTXIOCGETINFO: @@ -722,8 +949,7 @@ static inline unsigned int vtx_fix_command(unsigned int cmd) static int saa5246a_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct video_device *vd = video_devdata(file); - struct saa5246a_device *t = vd->priv; + struct saa5246a_device *t = video_drvdata(file); int err; cmd = vtx_fix_command(cmd); @@ -735,24 +961,15 @@ static int saa5246a_ioctl(struct inode *inode, struct file *file, static int saa5246a_open(struct inode *inode, struct file *file) { - struct video_device *vd = video_devdata(file); - struct saa5246a_device *t = vd->priv; - int err; + struct saa5246a_device *t = video_drvdata(file); - lock_kernel(); - err = video_exclusive_open(inode,file); - if (err < 0) { - unlock_kernel(); - return err; - } + if (t->client == NULL) + return -ENODEV; - if (t->client==NULL) { - err = -ENODEV; - goto fail; - } + if (test_and_set_bit(0, &t->in_use)) + return -EBUSY; if (i2c_senddata(t, SAA5246A_REGISTER_R0, - R0_SELECT_R11 | R0_PLL_TIME_CONSTANT_LONG | R0_ENABLE_nODD_EVEN_OUTPUT | @@ -778,23 +995,15 @@ static int saa5246a_open(struct inode *inode, struct file *file) COMMAND_END)) { - err = -EIO; - goto fail; + clear_bit(0, &t->in_use); + return -EIO; } - unlock_kernel(); - return 0; - -fail: - video_exclusive_release(inode,file); - unlock_kernel(); - return err; } static int saa5246a_release(struct inode *inode, struct file *file) { - struct video_device *vd = video_devdata(file); - struct saa5246a_device *t = vd->priv; + struct saa5246a_device *t = video_drvdata(file); /* Stop all acquisition circuits. */ i2c_senddata(t, SAA5246A_REGISTER_R1, @@ -807,26 +1016,10 @@ static int saa5246a_release(struct inode *inode, struct file *file) R1_VCS_TO_SCS, COMMAND_END); - video_exclusive_release(inode,file); + clear_bit(0, &t->in_use); return 0; } -static int __init init_saa_5246a (void) -{ - printk(KERN_INFO - "SAA5246A (or compatible) Teletext decoder driver version %d.%d\n", - MAJOR_VERSION, MINOR_VERSION); - return i2c_add_driver(&i2c_driver_videotext); -} - -static void __exit cleanup_saa_5246a (void) -{ - i2c_del_driver(&i2c_driver_videotext); -} - -module_init(init_saa_5246a); -module_exit(cleanup_saa_5246a); - static const struct file_operations saa_fops = { .owner = THIS_MODULE, .open = saa5246a_open, @@ -837,8 +1030,83 @@ static const struct file_operations saa_fops = { static struct video_device saa_template = { - .name = IF_NAME, + .name = "saa5246a", .fops = &saa_fops, .release = video_device_release, .minor = -1, }; + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +static int saa5246a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int pgbuf; + int err; + struct video_device *vd; + struct saa5246a_device *t; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + v4l_info(client, "VideoText version %d.%d\n", + MAJOR_VERSION, MINOR_VERSION); + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (t == NULL) + return -ENOMEM; + mutex_init(&t->lock); + + /* Now create a video4linux device */ + vd = video_device_alloc(); + if (vd == NULL) { + kfree(t); + return -ENOMEM; + } + i2c_set_clientdata(client, vd); + memcpy(vd, &saa_template, sizeof(*vd)); + + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { + memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); + t->is_searching[pgbuf] = false; + } + video_set_drvdata(vd, t); + + /* Register it */ + err = video_register_device(vd, VFL_TYPE_VTX, -1); + if (err < 0) { + kfree(t); + video_device_release(vd); + return err; + } + t->client = client; + return 0; +} + +static int saa5246a_remove(struct i2c_client *client) +{ + struct video_device *vd = i2c_get_clientdata(client); + + video_unregister_device(vd); + kfree(video_get_drvdata(vd)); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id saa5246a_id[] = { + { "saa5246a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa5246a_id); +#endif + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa5246a", + .driverid = I2C_DRIVERID_SAA5249, + .probe = saa5246a_probe, + .remove = saa5246a_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = saa5246a_id, +#endif +}; diff --git a/linux/drivers/media/video/saa5246a.h b/linux/drivers/media/video/saa5246a.h deleted file mode 100644 index 64394c036..000000000 --- a/linux/drivers/media/video/saa5246a.h +++ /dev/null @@ -1,359 +0,0 @@ -/* - Driver for the SAA5246A or SAA5281 Teletext (=Videotext) decoder chips from - Philips. - - Copyright (C) 2004 Michael Geng (linux@MichaelGeng.de) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - */ -#ifndef __SAA5246A_H__ -#define __SAA5246A_H__ - -#define MAJOR_VERSION 1 /* driver major version number */ -#define MINOR_VERSION 8 /* driver minor version number */ - -#define IF_NAME "SAA5246A" - -#define I2C_ADDRESS 17 - -/* Number of DAUs = number of pages that can be searched at the same time. */ -#define NUM_DAUS 4 - -#define NUM_ROWS_PER_PAGE 40 - -/* first column is 0 (not 1) */ -#define POS_TIME_START 32 -#define POS_TIME_END 39 - -#define POS_HEADER_START 7 -#define POS_HEADER_END 31 - -/* Returns 'true' if the part of the videotext page described with req contains - (at least parts of) the time field */ -#define REQ_CONTAINS_TIME(p_req) \ - ((p_req)->start <= POS_TIME_END && \ - (p_req)->end >= POS_TIME_START) - -/* Returns 'true' if the part of the videotext page described with req contains - (at least parts of) the page header */ -#define REQ_CONTAINS_HEADER(p_req) \ - ((p_req)->start <= POS_HEADER_END && \ - (p_req)->end >= POS_HEADER_START) - -/*****************************************************************************/ -/* Mode register numbers of the SAA5246A */ -/*****************************************************************************/ -#define SAA5246A_REGISTER_R0 0 -#define SAA5246A_REGISTER_R1 1 -#define SAA5246A_REGISTER_R2 2 -#define SAA5246A_REGISTER_R3 3 -#define SAA5246A_REGISTER_R4 4 -#define SAA5246A_REGISTER_R5 5 -#define SAA5246A_REGISTER_R6 6 -#define SAA5246A_REGISTER_R7 7 -#define SAA5246A_REGISTER_R8 8 -#define SAA5246A_REGISTER_R9 9 -#define SAA5246A_REGISTER_R10 10 -#define SAA5246A_REGISTER_R11 11 -#define SAA5246A_REGISTER_R11B 11 - -/* SAA5246A mode registers often autoincrement to the next register. - Therefore we use variable argument lists. The following macro indicates - the end of a command list. */ -#define COMMAND_END (- 1) - -/*****************************************************************************/ -/* Contents of the mode registers of the SAA5246A */ -/*****************************************************************************/ -/* Register R0 (Advanced Control) */ -#define R0_SELECT_R11 0x00 -#define R0_SELECT_R11B 0x01 - -#define R0_PLL_TIME_CONSTANT_LONG 0x00 -#define R0_PLL_TIME_CONSTANT_SHORT 0x02 - -#define R0_ENABLE_nODD_EVEN_OUTPUT 0x00 -#define R0_DISABLE_nODD_EVEN_OUTPUT 0x04 - -#define R0_ENABLE_HDR_POLL 0x00 -#define R0_DISABLE_HDR_POLL 0x10 - -#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00 -#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20 - -#define R0_NO_FREE_RUN_PLL 0x00 -#define R0_FREE_RUN_PLL 0x40 - -#define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00 -#define R0_AUTOMATIC_FASTEXT_PROMPT 0x80 - -/* Register R1 (Mode) */ -#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00 -#define R1_NON_INTERLACED_312_313_LINES 0x01 -#define R1_NON_INTERLACED_312_312_LINES 0x02 -#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03 -#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07 - -#define R1_DEW 0x00 -#define R1_FULL_FIELD 0x08 - -#define R1_EXTENDED_PACKET_DISABLE 0x00 -#define R1_EXTENDED_PACKET_ENABLE 0x10 - -#define R1_DAUS_ALL_ON 0x00 -#define R1_DAUS_ALL_OFF 0x20 - -#define R1_7_BITS_PLUS_PARITY 0x00 -#define R1_8_BITS_NO_PARITY 0x40 - -#define R1_VCS_TO_SCS 0x00 -#define R1_NO_VCS_TO_SCS 0x80 - -/* Register R2 (Page request address) */ -#define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00 -#define R2_IN_R3_SELECT_PAGE_TENS 0x01 -#define R2_IN_R3_SELECT_PAGE_UNITS 0x02 -#define R2_IN_R3_SELECT_HOURS_TENS 0x03 -#define R2_IN_R3_SELECT_HOURS_UNITS 0x04 -#define R2_IN_R3_SELECT_MINUTES_TENS 0x05 -#define R2_IN_R3_SELECT_MINUTES_UNITS 0x06 - -#define R2_DAU_0 0x00 -#define R2_DAU_1 0x10 -#define R2_DAU_2 0x20 -#define R2_DAU_3 0x30 - -#define R2_BANK_0 0x00 -#define R2_BANK 1 0x40 - -#define R2_HAMMING_CHECK_ON 0x80 -#define R2_HAMMING_CHECK_OFF 0x00 - -/* Register R3 (Page request data) */ -#define R3_PAGE_HUNDREDS_0 0x00 -#define R3_PAGE_HUNDREDS_1 0x01 -#define R3_PAGE_HUNDREDS_2 0x02 -#define R3_PAGE_HUNDREDS_3 0x03 -#define R3_PAGE_HUNDREDS_4 0x04 -#define R3_PAGE_HUNDREDS_5 0x05 -#define R3_PAGE_HUNDREDS_6 0x06 -#define R3_PAGE_HUNDREDS_7 0x07 - -#define R3_HOLD_PAGE 0x00 -#define R3_UPDATE_PAGE 0x08 - -#define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00 -#define R3_PAGE_HUNDREDS_DO_CARE 0x10 - -#define R3_PAGE_TENS_DO_NOT_CARE 0x00 -#define R3_PAGE_TENS_DO_CARE 0x10 - -#define R3_PAGE_UNITS_DO_NOT_CARE 0x00 -#define R3_PAGE_UNITS_DO_CARE 0x10 - -#define R3_HOURS_TENS_DO_NOT_CARE 0x00 -#define R3_HOURS_TENS_DO_CARE 0x10 - -#define R3_HOURS_UNITS_DO_NOT_CARE 0x00 -#define R3_HOURS_UNITS_DO_CARE 0x10 - -#define R3_MINUTES_TENS_DO_NOT_CARE 0x00 -#define R3_MINUTES_TENS_DO_CARE 0x10 - -#define R3_MINUTES_UNITS_DO_NOT_CARE 0x00 -#define R3_MINUTES_UNITS_DO_CARE 0x10 - -/* Register R4 (Display chapter) */ -#define R4_DISPLAY_PAGE_0 0x00 -#define R4_DISPLAY_PAGE_1 0x01 -#define R4_DISPLAY_PAGE_2 0x02 -#define R4_DISPLAY_PAGE_3 0x03 -#define R4_DISPLAY_PAGE_4 0x04 -#define R4_DISPLAY_PAGE_5 0x05 -#define R4_DISPLAY_PAGE_6 0x06 -#define R4_DISPLAY_PAGE_7 0x07 - -/* Register R5 (Normal display control) */ -#define R5_PICTURE_INSIDE_BOXING_OFF 0x00 -#define R5_PICTURE_INSIDE_BOXING_ON 0x01 - -#define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00 -#define R5_PICTURE_OUTSIDE_BOXING_ON 0x02 - -#define R5_TEXT_INSIDE_BOXING_OFF 0x00 -#define R5_TEXT_INSIDE_BOXING_ON 0x04 - -#define R5_TEXT_OUTSIDE_BOXING_OFF 0x00 -#define R5_TEXT_OUTSIDE_BOXING_ON 0x08 - -#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 -#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 - -#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 -#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 - -#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 -#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 - -#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 -#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 - -/* Register R6 (Newsflash display) */ -#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01 - -#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02 - -#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04 - -#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08 - -#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 - -#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 - -#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 - -#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 - -/* Register R7 (Display mode) */ -#define R7_BOX_OFF_ROW_0 0x00 -#define R7_BOX_ON_ROW_0 0x01 - -#define R7_BOX_OFF_ROW_1_TO_23 0x00 -#define R7_BOX_ON_ROW_1_TO_23 0x02 - -#define R7_BOX_OFF_ROW_24 0x00 -#define R7_BOX_ON_ROW_24 0x04 - -#define R7_SINGLE_HEIGHT 0x00 -#define R7_DOUBLE_HEIGHT 0x08 - -#define R7_TOP_HALF 0x00 -#define R7_BOTTOM_HALF 0x10 - -#define R7_REVEAL_OFF 0x00 -#define R7_REVEAL_ON 0x20 - -#define R7_CURSER_OFF 0x00 -#define R7_CURSER_ON 0x40 - -#define R7_STATUS_BOTTOM 0x00 -#define R7_STATUS_TOP 0x80 - -/* Register R8 (Active chapter) */ -#define R8_ACTIVE_CHAPTER_0 0x00 -#define R8_ACTIVE_CHAPTER_1 0x01 -#define R8_ACTIVE_CHAPTER_2 0x02 -#define R8_ACTIVE_CHAPTER_3 0x03 -#define R8_ACTIVE_CHAPTER_4 0x04 -#define R8_ACTIVE_CHAPTER_5 0x05 -#define R8_ACTIVE_CHAPTER_6 0x06 -#define R8_ACTIVE_CHAPTER_7 0x07 - -#define R8_CLEAR_MEMORY 0x08 -#define R8_DO_NOT_CLEAR_MEMORY 0x00 - -/* Register R9 (Curser row) */ -#define R9_CURSER_ROW_0 0x00 -#define R9_CURSER_ROW_1 0x01 -#define R9_CURSER_ROW_2 0x02 -#define R9_CURSER_ROW_25 0x19 - -/* Register R10 (Curser column) */ -#define R10_CURSER_COLUMN_0 0x00 -#define R10_CURSER_COLUMN_6 0x06 -#define R10_CURSER_COLUMN_8 0x08 - -/*****************************************************************************/ -/* Row 25 control data in column 0 to 9 */ -/*****************************************************************************/ -#define ROW25_COLUMN0_PAGE_UNITS 0x0F - -#define ROW25_COLUMN1_PAGE_TENS 0x0F - -#define ROW25_COLUMN2_MINUTES_UNITS 0x0F - -#define ROW25_COLUMN3_MINUTES_TENS 0x07 -#define ROW25_COLUMN3_DELETE_PAGE 0x08 - -#define ROW25_COLUMN4_HOUR_UNITS 0x0F - -#define ROW25_COLUMN5_HOUR_TENS 0x03 -#define ROW25_COLUMN5_INSERT_HEADLINE 0x04 -#define ROW25_COLUMN5_INSERT_SUBTITLE 0x08 - -#define ROW25_COLUMN6_SUPPRESS_HEADER 0x01 -#define ROW25_COLUMN6_UPDATE_PAGE 0x02 -#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04 -#define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08 - -#define ROW25_COLUMN7_SERIAL_MODE 0x01 -#define ROW25_COLUMN7_CHARACTER_SET 0x0E - -#define ROW25_COLUMN8_PAGE_HUNDREDS 0x07 -#define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10 - -#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20 - -#define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10 - -/*****************************************************************************/ -/* Helper macros for extracting page, hour and minute digits */ -/*****************************************************************************/ -/* BYTE_POS 0 is at row 0, column 0, - BYTE_POS 1 is at row 0, column 1, - BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40) - BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40), - ... */ -#define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE) -#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE) - -/*****************************************************************************/ -/* Helper macros for extracting page, hour and minute digits */ -/*****************************************************************************/ -/* Macros for extracting hundreds, tens and units of a page number which - must be in the range 0 ... 0x799. - Note that page is coded in hexadecimal, i.e. 0x123 means page 123. - page 0x.. means page 8.. */ -#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7) -#define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF) -#define UNITS_OF_PAGE(page) ((page) & 0xF) - -/* Macros for extracting tens and units of a hour information which - must be in the range 0 ... 0x24. - Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */ -#define TENS_OF_HOUR(hour) ((hour) / 0x10) -#define UNITS_OF_HOUR(hour) ((hour) & 0xF) - -/* Macros for extracting tens and units of a minute information which - must be in the range 0 ... 0x59. - Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */ -#define TENS_OF_MINUTE(minute) ((minute) / 0x10) -#define UNITS_OF_MINUTE(minute) ((minute) & 0xF) - -#define HOUR_MAX 0x23 -#define MINUTE_MAX 0x59 -#define PAGE_MAX 0x8FF - -#endif /* __SAA5246A_H__ */ diff --git a/linux/drivers/media/video/saa5249.c b/linux/drivers/media/video/saa5249.c index 81d341a80..763e6441b 100644 --- a/linux/drivers/media/video/saa5249.c +++ b/linux/drivers/media/video/saa5249.c @@ -15,8 +15,6 @@ * * Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de> * - * $Id: saa5249.c,v 1.1 1998/03/30 22:23:23 alan Exp $ - * * Derived From * * vtx.c: @@ -45,35 +43,28 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> #include <linux/mm.h> -#include <linux/errno.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/slab.h> #include <linux/init.h> -#include <linux/smp_lock.h> -#include <stdarg.h> #include <linux/i2c.h> +#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <linux/videotext.h> -#include "compat.h" #include <linux/videodev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#include <linux/mutex.h> - +#include <media/v4l2-i2c-drv-legacy.h> +#include "compat.h" -#include <asm/io.h> -#include <asm/uaccess.h> +MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); +MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver"); +MODULE_LICENSE("GPL"); #define VTX_VER_MAJ 1 #define VTX_VER_MIN 8 - #define NUM_DAUS 4 #define NUM_BUFS 8 -#define IF_NAME "SAA5249" static const int disp_modes[8][3] = { @@ -111,6 +102,7 @@ struct saa5249_device int disp_mode; int virtual_mode; struct i2c_client *client; + unsigned long in_use; struct mutex lock; }; @@ -125,125 +117,8 @@ struct saa5249_device #define VTX_DEV_MINOR 0 -/* General defines and debugging support */ - -#define RESCHED do { cond_resched(); } while(0) - static struct video_device saa_template; /* Declared near bottom */ -/* Addresses to scan */ -static unsigned short normal_i2c[] = {34>>1,I2C_CLIENT_END}; - -I2C_CLIENT_INSMOD; - -static struct i2c_client client_template; - -static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind) -{ - int pgbuf; - int err; - struct i2c_client *client; - struct video_device *vd; - struct saa5249_device *t; - - printk(KERN_INFO "saa5249: teletext chip found.\n"); - client=kmalloc(sizeof(*client), GFP_KERNEL); - if(client==NULL) - return -ENOMEM; - client_template.adapter = adap; - client_template.addr = addr; - memcpy(client, &client_template, sizeof(*client)); - t = kzalloc(sizeof(*t), GFP_KERNEL); - if(t==NULL) - { - kfree(client); - return -ENOMEM; - } - strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); - mutex_init(&t->lock); - - /* - * Now create a video4linux device - */ - - vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); - if(vd==NULL) - { - kfree(t); - kfree(client); - return -ENOMEM; - } - i2c_set_clientdata(client, vd); - memcpy(vd, &saa_template, sizeof(*vd)); - - for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) - { - memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); - memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); - memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); - t->vdau[pgbuf].expire = 0; - t->vdau[pgbuf].clrfound = true; - t->vdau[pgbuf].stopped = true; - t->is_searching[pgbuf] = false; - } - vd->priv=t; - - - /* - * Register it - */ - - if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0) - { - kfree(t); - kfree(vd); - kfree(client); - return err; - } - t->client = client; - i2c_attach_client(client); - return 0; -} - -/* - * We do most of the hard work when we become a device on the i2c. - */ - -static int saa5249_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, saa5249_attach); - return 0; -} - -static int saa5249_detach(struct i2c_client *client) -{ - struct video_device *vd = i2c_get_clientdata(client); - i2c_detach_client(client); - video_unregister_device(vd); - kfree(vd->priv); - kfree(vd); - kfree(client); - return 0; -} - -/* new I2C driver support */ - -static struct i2c_driver i2c_driver_videotext = -{ - .driver = { - .name = IF_NAME, /* name */ - }, - .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ - .attach_adapter = saa5249_probe, - .detach_client = saa5249_detach, -}; - -static struct i2c_client client_template = { - .driver = &i2c_driver_videotext, - .name = "(unset)", -}; - /* * Wait the given number of jiffies (10ms). This calls the scheduler, so the actual * delay may be longer. @@ -277,7 +152,7 @@ static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data) buf[0] = reg; memcpy(buf+1, data, count); - if(i2c_master_send(t->client, buf, count+1)==count+1) + if (i2c_master_send(t->client, buf, count + 1) == count + 1) return 0; return -1; } @@ -319,246 +194,236 @@ static int do_saa5249_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { static int virtual_mode = false; - struct video_device *vd = video_devdata(file); - struct saa5249_device *t=vd->priv; + struct saa5249_device *t = video_drvdata(file); - switch(cmd) + switch (cmd) { + case VTXIOCGETINFO: { - case VTXIOCGETINFO: - { - vtx_info_t *info = arg; - info->version_major = VTX_VER_MAJ; - info->version_minor = VTX_VER_MIN; - info->numpages = NUM_DAUS; - /*info->cct_type = CCT_TYPE;*/ - return 0; - } + vtx_info_t *info = arg; + info->version_major = VTX_VER_MAJ; + info->version_minor = VTX_VER_MIN; + info->numpages = NUM_DAUS; + /*info->cct_type = CCT_TYPE;*/ + return 0; + } - case VTXIOCCLRPAGE: - { - vtx_pagereq_t *req = arg; + case VTXIOCCLRPAGE: + { + vtx_pagereq_t *req = arg; - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); - t->vdau[req->pgbuf].clrfound = true; - return 0; - } + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + t->vdau[req->pgbuf].clrfound = true; + return 0; + } - case VTXIOCCLRFOUND: - { - vtx_pagereq_t *req = arg; + case VTXIOCCLRFOUND: + { + vtx_pagereq_t *req = arg; - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - t->vdau[req->pgbuf].clrfound = true; - return 0; - } + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req->pgbuf].clrfound = true; + return 0; + } - case VTXIOCPAGEREQ: - { - vtx_pagereq_t *req = arg; - if (!(req->pagemask & PGMASK_PAGE)) - req->page = 0; - if (!(req->pagemask & PGMASK_HOUR)) - req->hour = 0; - if (!(req->pagemask & PGMASK_MINUTE)) - req->minute = 0; - if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */ - return -EINVAL; - req->page &= 0x7ff; - if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f || - req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100); - t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf); - t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf); - t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10); - t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf); - t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10); - t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf); - t->vdau[req->pgbuf].stopped = false; - t->vdau[req->pgbuf].clrfound = true; - t->is_searching[req->pgbuf] = true; - return 0; - } + case VTXIOCPAGEREQ: + { + vtx_pagereq_t *req = arg; + if (!(req->pagemask & PGMASK_PAGE)) + req->page = 0; + if (!(req->pagemask & PGMASK_HOUR)) + req->hour = 0; + if (!(req->pagemask & PGMASK_MINUTE)) + req->minute = 0; + if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */ + return -EINVAL; + req->page &= 0x7ff; + if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f || + req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100); + t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf); + t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf); + t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10); + t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf); + t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10); + t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf); + t->vdau[req->pgbuf].stopped = false; + t->vdau[req->pgbuf].clrfound = true; + t->is_searching[req->pgbuf] = true; + return 0; + } - case VTXIOCGETSTAT: - { - vtx_pagereq_t *req = arg; - u8 infobits[10]; - vtx_pageinfo_t info; - int a; - - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - if (!t->vdau[req->pgbuf].stopped) - { - if (i2c_senddata(t, 2, 0, -1) || - i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) || - i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) || - i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) || - i2c_senddata(t, 8, 0, 25, 0, -1)) - return -EIO; - jdelay(PAGE_WAIT); - if (i2c_getdata(t, 10, infobits)) - return -EIO; + case VTXIOCGETSTAT: + { + vtx_pagereq_t *req = arg; + u8 infobits[10]; + vtx_pageinfo_t info; + int a; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + if (!t->vdau[req->pgbuf].stopped) { + if (i2c_senddata(t, 2, 0, -1) || + i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) || + i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) || + i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) || + i2c_senddata(t, 8, 0, 25, 0, -1)) + return -EIO; + jdelay(PAGE_WAIT); + if (i2c_getdata(t, 10, infobits)) + return -EIO; - if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ - (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) || - time_after_eq(jiffies, t->vdau[req->pgbuf].expire))) - { /* check if new page arrived */ - if (i2c_senddata(t, 8, 0, 0, 0, -1) || - i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf)) + if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ + (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) || + time_after_eq(jiffies, t->vdau[req->pgbuf].expire))) + { /* check if new page arrived */ + if (i2c_senddata(t, 8, 0, 0, 0, -1) || + i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf)) + return -EIO; + t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE; + memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); + if (t->virtual_mode) { + /* Packet X/24 */ + if (i2c_senddata(t, 8, 0, 0x20, 0, -1) || + i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40)) + return -EIO; + /* Packet X/27/0 */ + if (i2c_senddata(t, 8, 0, 0x21, 0, -1) || + i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40)) + return -EIO; + /* Packet 8/30/0...8/30/15 + * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, + * so we should undo this here. + */ + if (i2c_senddata(t, 8, 0, 0x22, 0, -1) || + i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40)) return -EIO; - t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE; - memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); - if (t->virtual_mode) - { - /* Packet X/24 */ - if (i2c_senddata(t, 8, 0, 0x20, 0, -1) || - i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40)) - return -EIO; - /* Packet X/27/0 */ - if (i2c_senddata(t, 8, 0, 0x21, 0, -1) || - i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40)) - return -EIO; - /* Packet 8/30/0...8/30/15 - * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, - * so we should undo this here. - */ - if (i2c_senddata(t, 8, 0, 0x22, 0, -1) || - i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40)) - return -EIO; - } - t->vdau[req->pgbuf].clrfound = false; - memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits)); - } - else - { - memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); } - } - else - { + t->vdau[req->pgbuf].clrfound = false; + memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits)); + } else { memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); } + } else { + memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); + } - info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); - if (info.pagenum < 0x100) - info.pagenum += 0x800; - info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); - info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); - info.charset = ((infobits[7] >> 1) & 7); - info.delete = !!(infobits[3] & 8); - info.headline = !!(infobits[5] & 4); - info.subtitle = !!(infobits[5] & 8); - info.supp_header = !!(infobits[6] & 1); - info.update = !!(infobits[6] & 2); - info.inter_seq = !!(infobits[6] & 4); - info.dis_disp = !!(infobits[6] & 8); - info.serial = !!(infobits[7] & 1); - info.notfound = !!(infobits[8] & 0x10); - info.pblf = !!(infobits[9] & 0x20); - info.hamming = 0; - for (a = 0; a <= 7; a++) - { - if (infobits[a] & 0xf0) - { - info.hamming = 1; - break; - } + info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); + if (info.pagenum < 0x100) + info.pagenum += 0x800; + info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); + info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); + info.charset = ((infobits[7] >> 1) & 7); + info.delete = !!(infobits[3] & 8); + info.headline = !!(infobits[5] & 4); + info.subtitle = !!(infobits[5] & 8); + info.supp_header = !!(infobits[6] & 1); + info.update = !!(infobits[6] & 2); + info.inter_seq = !!(infobits[6] & 4); + info.dis_disp = !!(infobits[6] & 8); + info.serial = !!(infobits[7] & 1); + info.notfound = !!(infobits[8] & 0x10); + info.pblf = !!(infobits[9] & 0x20); + info.hamming = 0; + for (a = 0; a <= 7; a++) { + if (infobits[a] & 0xf0) { + info.hamming = 1; + break; } - if (t->vdau[req->pgbuf].clrfound) - info.notfound = 1; - if(copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t))) - return -EFAULT; - if (!info.hamming && !info.notfound) - { - t->is_searching[req->pgbuf] = false; - } - return 0; } + if (t->vdau[req->pgbuf].clrfound) + info.notfound = 1; + if (copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t))) + return -EFAULT; + if (!info.hamming && !info.notfound) + t->is_searching[req->pgbuf] = false; + return 0; + } - case VTXIOCGETPAGE: - { - vtx_pagereq_t *req = arg; - int start, end; - - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 || - req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) - return -EINVAL; - if(copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1)) + case VTXIOCGETPAGE: + { + vtx_pagereq_t *req = arg; + int start, end; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 || + req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) + return -EINVAL; + if (copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1)) + return -EFAULT; + + /* + * Always read the time directly from SAA5249 + */ + + if (req->start <= 39 && req->end >= 32) { + int len; + char buf[16]; + start = max(req->start, 32); + end = min(req->end, 39); + len = end - start + 1; + if (i2c_senddata(t, 8, 0, 0, start, -1) || + i2c_getdata(t, len, buf)) + return -EIO; + if (copy_to_user(req->buffer + start - req->start, buf, len)) return -EFAULT; - - /* - * Always read the time directly from SAA5249 - */ - - if (req->start <= 39 && req->end >= 32) - { - int len; - char buf[16]; - start = max(req->start, 32); - end = min(req->end, 39); - len=end-start+1; - if (i2c_senddata(t, 8, 0, 0, start, -1) || - i2c_getdata(t, len, buf)) - return -EIO; - if(copy_to_user(req->buffer+start-req->start, buf, len)) - return -EFAULT; - } - /* Insert the current header if DAU is still searching for a page */ - if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) - { - char buf[32]; - int len; - start = max(req->start, 7); - end = min(req->end, 31); - len=end-start+1; - if (i2c_senddata(t, 8, 0, 0, start, -1) || - i2c_getdata(t, len, buf)) - return -EIO; - if(copy_to_user(req->buffer+start-req->start, buf, len)) - return -EFAULT; - } - return 0; } + /* Insert the current header if DAU is still searching for a page */ + if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) { + char buf[32]; + int len; + + start = max(req->start, 7); + end = min(req->end, 31); + len = end - start + 1; + if (i2c_senddata(t, 8, 0, 0, start, -1) || + i2c_getdata(t, len, buf)) + return -EIO; + if (copy_to_user(req->buffer + start - req->start, buf, len)) + return -EFAULT; + } + return 0; + } - case VTXIOCSTOPDAU: - { - vtx_pagereq_t *req = arg; + case VTXIOCSTOPDAU: + { + vtx_pagereq_t *req = arg; - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - t->vdau[req->pgbuf].stopped = true; - t->is_searching[req->pgbuf] = false; - return 0; - } + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req->pgbuf].stopped = true; + t->is_searching[req->pgbuf] = false; + return 0; + } - case VTXIOCPUTPAGE: - case VTXIOCSETDISP: - case VTXIOCPUTSTAT: - return 0; + case VTXIOCPUTPAGE: + case VTXIOCSETDISP: + case VTXIOCPUTSTAT: + return 0; - case VTXIOCCLRCACHE: - { - if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11, - ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', - ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1)) - return -EIO; - if (i2c_senddata(t, 3, 0x20, -1)) - return -EIO; - jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ - return 0; - } + case VTXIOCCLRCACHE: + { + if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11, + ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', + -1)) + return -EIO; + if (i2c_senddata(t, 3, 0x20, -1)) + return -EIO; + jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ + return 0; + } - case VTXIOCSETVIRT: - { - /* The SAA5249 has virtual-row reception turned on always */ - t->virtual_mode = (int)(long)arg; - return 0; - } + case VTXIOCSETVIRT: + { + /* The SAA5249 has virtual-row reception turned on always */ + t->virtual_mode = (int)(long)arg; + return 0; + } } return -EINVAL; } @@ -618,8 +483,7 @@ static inline unsigned int vtx_fix_command(unsigned int cmd) static int saa5249_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct video_device *vd = video_devdata(file); - struct saa5249_device *t=vd->priv; + struct saa5249_device *t = video_drvdata(file); int err; cmd = vtx_fix_command(cmd); @@ -631,35 +495,27 @@ static int saa5249_ioctl(struct inode *inode, struct file *file, static int saa5249_open(struct inode *inode, struct file *file) { - struct video_device *vd = video_devdata(file); - struct saa5249_device *t=vd->priv; - int err,pgbuf; + struct saa5249_device *t = video_drvdata(file); + int pgbuf; - lock_kernel(); - err = video_exclusive_open(inode,file); - if (err < 0) { - unlock_kernel(); - return err; - } + if (t->client == NULL) + return -ENODEV; - if (t->client==NULL) { - err = -ENODEV; - goto fail; - } + if (test_and_set_bit(0, &t->in_use)) + return -EBUSY; - if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */ - /* Turn off parity checks (we do this ourselves) */ + if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */ + /* Turn off parity checks (we do this ourselves) */ i2c_senddata(t, 1, disp_modes[t->disp_mode][0], 0, -1) || - /* Display TV-picture, no virtual rows */ - i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) /* Set display to page 4 */ - + /* Display TV-picture, no virtual rows */ + i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) + /* Set display to page 4 */ { - err = -EIO; - goto fail; + clear_bit(0, &t->in_use); + return -EIO; } - for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) - { + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); @@ -669,42 +525,21 @@ static int saa5249_open(struct inode *inode, struct file *file) t->is_searching[pgbuf] = false; } t->virtual_mode = false; - unlock_kernel(); return 0; - - fail: - video_exclusive_release(inode,file); - unlock_kernel(); - return err; } static int saa5249_release(struct inode *inode, struct file *file) { - struct video_device *vd = video_devdata(file); - struct saa5249_device *t=vd->priv; + struct saa5249_device *t = video_drvdata(file); + i2c_senddata(t, 1, 0x20, -1); /* Turn off CCT */ i2c_senddata(t, 5, 3, 3, -1); /* Turn off TV-display */ - video_exclusive_release(inode,file); + clear_bit(0, &t->in_use); return 0; } -static int __init init_saa_5249 (void) -{ - printk(KERN_INFO "SAA5249 driver (" IF_NAME " interface) for VideoText version %d.%d\n", - VTX_VER_MAJ, VTX_VER_MIN); - return i2c_add_driver(&i2c_driver_videotext); -} - -static void __exit cleanup_saa_5249 (void) -{ - i2c_del_driver(&i2c_driver_videotext); -} - -module_init(init_saa_5249); -module_exit(cleanup_saa_5249); - static const struct file_operations saa_fops = { .owner = THIS_MODULE, .open = saa5249_open, @@ -718,8 +553,88 @@ static const struct file_operations saa_fops = { static struct video_device saa_template = { - .name = IF_NAME, + .name = "saa5249", .fops = &saa_fops, + .release = video_device_release, }; -MODULE_LICENSE("GPL"); +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +static int saa5249_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int pgbuf; + int err; + struct video_device *vd; + struct saa5249_device *t; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + v4l_info(client, "VideoText version %d.%d\n", + VTX_VER_MAJ, VTX_VER_MIN); + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (t == NULL) + return -ENOMEM; + mutex_init(&t->lock); + + /* Now create a video4linux device */ + vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); + if (vd == NULL) { + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, vd); + memcpy(vd, &saa_template, sizeof(*vd)); + + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { + memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); + memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); + t->vdau[pgbuf].expire = 0; + t->vdau[pgbuf].clrfound = true; + t->vdau[pgbuf].stopped = true; + t->is_searching[pgbuf] = false; + } + video_set_drvdata(vd, t); + + /* Register it */ + err = video_register_device(vd, VFL_TYPE_VTX, -1); + if (err < 0) { + kfree(t); + kfree(vd); + return err; + } + t->client = client; + return 0; +} + +static int saa5249_remove(struct i2c_client *client) +{ + struct video_device *vd = i2c_get_clientdata(client); + + video_unregister_device(vd); + kfree(video_get_drvdata(vd)); + kfree(vd); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id saa5249_id[] = { + { "saa5249", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa5249_id); +#endif + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa5249", + .driverid = I2C_DRIVERID_SAA5249, + .probe = saa5249_probe, + .remove = saa5249_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = saa5249_id, +#endif +}; diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 5488679ed..6b9df354b 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -1310,10 +1310,13 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar case VIDIOC_INT_S_VIDEO_ROUTING: { struct v4l2_routing *route = arg; + u32 input = route->input; + u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output); - /* saa7113 does not have these inputs */ - if (state->ident == V4L2_IDENT_SAA7113 && + /* saa7111/3 does not have these inputs */ + if ((state->ident == V4L2_IDENT_SAA7113 || + state->ident == V4L2_IDENT_SAA7111) && (route->input == SAA7115_COMPOSITE4 || route->input == SAA7115_COMPOSITE5)) { return -EINVAL; @@ -1328,10 +1331,23 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); state->input = route->input; + /* saa7111 has slightly different input numbering */ + if (state->ident == V4L2_IDENT_SAA7111) { + if (input >= SAA7115_COMPOSITE4) + input -= 2; + /* saa7111 specific */ + saa711x_write(client, R_10_CHROMA_CNTL_2, + (saa711x_read(client, R_10_CHROMA_CNTL_2) & 0x3f) | + ((route->output & 0xc0) ^ 0x40)); + saa711x_write(client, R_13_RT_X_PORT_OUT_CNTL, + (saa711x_read(client, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | + ((route->output & 2) ? 0x0a : 0)); + } + /* select mode */ saa711x_write(client, R_02_INPUT_CNTL_1, - (saa711x_read(client, R_02_INPUT_CNTL_1) & 0xf0) | - state->input); + (saa711x_read(client, R_02_INPUT_CNTL_1) & mask) | + input); /* bypass chrominance trap for S-Video modes */ saa711x_write(client, R_09_LUMA_CNTL, @@ -1385,6 +1401,13 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar saa711x_writeregs(client, saa7115_cfg_reset_scaler); break; + case VIDIOC_INT_S_GPIO: + if (state->ident != V4L2_IDENT_SAA7111) + return -EINVAL; + saa711x_write(client, 0x11, (saa711x_read(client, 0x11) & 0x7f) | + (*(u32 *)arg ? 0x80 : 0)); + break; + case VIDIOC_INT_G_VBI_DATA: { struct v4l2_sliced_vbi_data *data = arg; @@ -1498,10 +1521,9 @@ static int saa7115_probe(struct i2c_client *client, client->addr << 1, client->adapter->name); state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); - i2c_set_clientdata(client, state); - if (state == NULL) { + if (state == NULL) return -ENOMEM; - } + i2c_set_clientdata(client, state); state->input = -1; state->output = SAA7115_IPORT_ON; state->enable = 1; @@ -1549,7 +1571,8 @@ static int saa7115_probe(struct i2c_client *client, state->crystal_freq = SAA7115_FREQ_32_11_MHZ; saa711x_writeregs(client, saa7115_init_auto_input); } - saa711x_writeregs(client, saa7115_init_misc); + if (state->ident != V4L2_IDENT_SAA7111) + saa711x_writeregs(client, saa7115_init_misc); saa711x_set_v4lstd(client, V4L2_STD_NTSC); v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n", diff --git a/linux/drivers/media/video/saa7134/saa6752hs.c b/linux/drivers/media/video/saa7134/saa6752hs.c index c5c19a2e3..8ccca4d56 100644 --- a/linux/drivers/media/video/saa7134/saa6752hs.c +++ b/linux/drivers/media/video/saa7134/saa6752hs.c @@ -1,3 +1,27 @@ + /* + saa6752hs - i2c-driver for the saa6752hs by Philips + + Copyright (C) 2004 Andrew de Quincey + + AC-3 support: + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License vs published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mvss Ave, Cambridge, MA 02139, USA. + */ + #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> @@ -11,6 +35,8 @@ #include "compat.h" #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv-legacy.h> #include <linux/init.h> #include <linux/crc32.h> @@ -28,9 +54,6 @@ MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder"); MODULE_AUTHOR("Andrew de Quincey"); MODULE_LICENSE("GPL"); -static struct i2c_driver driver; -static struct i2c_client client_template; - enum saa6752hs_videoformat { SAA6752HS_VF_D1 = 0, /* standard D1 video format: 720x576 */ SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */ @@ -47,7 +70,9 @@ struct saa6752hs_mpeg_params { __u16 ts_pid_pcr; /* audio */ - enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate; + enum v4l2_mpeg_audio_encoding au_encoding; + enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate; + enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate; /* video */ enum v4l2_mpeg_video_aspect vi_aspect; @@ -71,7 +96,9 @@ static const struct v4l2_format v4l2_format_table[] = }; struct saa6752hs_state { - struct i2c_client client; + int chip; + u32 revision; + int has_ac3; struct saa6752hs_mpeg_params params; enum saa6752hs_videoformat video_format; v4l2_std_id standard; @@ -146,6 +173,39 @@ static u8 PMT[] = { 0x00, 0x00, 0x00, 0x00 /* CRC32 */ }; +static u8 PMT_AC3[] = { + 0xc2, /* i2c register */ + 0x01, /* table number for encoder(1) */ + 0x47, /* sync */ + + 0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */ + 0x10, /* PMT PID (0x0010) */ + 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + + 0x00, /* PSI pointer to start of table */ + + 0x02, /* TID (2) */ + 0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */ + + 0x00, 0x01, /* program_number(1) */ + + 0xc1, /* version_number(0), current_next_indicator(1) */ + + 0x00, 0x00, /* section_number(0), last_section_number(0) */ + + 0xe1, 0x04, /* PCR_PID (0x0104) */ + + 0xf0, 0x00, /* program_info_length(0) */ + + 0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ + 0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */ + 0x6a, /* AC3 */ + 0x01, /* Descriptor_length(1) */ + 0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */ + + 0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */ +}; + static struct saa6752hs_mpeg_params param_defaults = { .ts_pid_pmt = 16, @@ -158,12 +218,14 @@ static struct saa6752hs_mpeg_params param_defaults = .vi_bitrate_peak = 6000, .vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .au_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, .au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_256K, + .au_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_256K, }; /* ---------------------------------------------------------------------- */ -static int saa6752hs_chip_command(struct i2c_client* client, +static int saa6752hs_chip_command(struct i2c_client *client, enum saa6752hs_command command) { unsigned char buf[3]; @@ -230,45 +292,61 @@ static int saa6752hs_chip_command(struct i2c_client* client, } -static int saa6752hs_set_bitrate(struct i2c_client* client, - struct saa6752hs_mpeg_params* params) +static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val) +{ + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + i2c_master_send(client, buf, 2); +} + +static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val) { u8 buf[3]; + + buf[0] = reg; + buf[1] = val >> 8; + buf[2] = val & 0xff; + i2c_master_send(client, buf, 3); +} + +static int saa6752hs_set_bitrate(struct i2c_client *client, + struct saa6752hs_state *h) +{ + struct saa6752hs_mpeg_params *params = &h->params; int tot_bitrate; + int is_384k; /* set the bitrate mode */ - buf[0] = 0x71; - buf[1] = (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) ? 0 : 1; - i2c_master_send(client, buf, 2); + set_reg8(client, 0x71, + params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); /* set the video bitrate */ if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { /* set the target bitrate */ - buf[0] = 0x80; - buf[1] = params->vi_bitrate >> 8; - buf[2] = params->vi_bitrate & 0xff; - i2c_master_send(client, buf, 3); + set_reg16(client, 0x80, params->vi_bitrate); /* set the max bitrate */ - buf[0] = 0x81; - buf[1] = params->vi_bitrate_peak >> 8; - buf[2] = params->vi_bitrate_peak & 0xff; - i2c_master_send(client, buf, 3); + set_reg16(client, 0x81, params->vi_bitrate_peak); tot_bitrate = params->vi_bitrate_peak; } else { /* set the target bitrate (no max bitrate for CBR) */ - buf[0] = 0x81; - buf[1] = params->vi_bitrate >> 8; - buf[2] = params->vi_bitrate & 0xff; - i2c_master_send(client, buf, 3); + set_reg16(client, 0x81, params->vi_bitrate); tot_bitrate = params->vi_bitrate; } + /* set the audio encoding */ + set_reg8(client, 0x93, + params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3); + /* set the audio bitrate */ - buf[0] = 0x94; - buf[1] = (V4L2_MPEG_AUDIO_L2_BITRATE_256K == params->au_l2_bitrate) ? 0 : 1; - i2c_master_send(client, buf, 2); - tot_bitrate += (V4L2_MPEG_AUDIO_L2_BITRATE_256K == params->au_l2_bitrate) ? 256 : 384; + if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) + is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate; + else + is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate; + set_reg8(client, 0x94, is_384k); + tot_bitrate += is_384k ? 384 : 256; /* Note: the total max bitrate is determined by adding the video and audio bitrates together and also adding an extra 768kbit/s to stay on the @@ -279,16 +357,12 @@ static int saa6752hs_set_bitrate(struct i2c_client* client, tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX; /* set the total bitrate */ - buf[0] = 0xb1; - buf[1] = tot_bitrate >> 8; - buf[2] = tot_bitrate & 0xff; - i2c_master_send(client, buf, 3); - + set_reg16(client, 0xb1, tot_bitrate); return 0; } -static void saa6752hs_set_subsampling(struct i2c_client* client, - struct v4l2_format* f) +static void saa6752hs_set_subsampling(struct i2c_client *client, + struct v4l2_format *f) { struct saa6752hs_state *h = i2c_get_clientdata(client); int dist_352, dist_480, dist_720; @@ -333,7 +407,7 @@ static void saa6752hs_set_subsampling(struct i2c_client* client, } -static int handle_ctrl(struct saa6752hs_mpeg_params *params, +static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, struct v4l2_ext_control *ctrl, unsigned int cmd) { int old = 0, new; @@ -380,8 +454,9 @@ static int handle_ctrl(struct saa6752hs_mpeg_params *params, params->ts_pid_pcr = new; break; case V4L2_CID_MPEG_AUDIO_ENCODING: - old = V4L2_MPEG_AUDIO_ENCODING_LAYER_2; - if (set && new != old) + old = params->au_encoding; + if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && + (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3)) return -ERANGE; new = old; break; @@ -396,6 +471,19 @@ static int handle_ctrl(struct saa6752hs_mpeg_params *params, new = V4L2_MPEG_AUDIO_L2_BITRATE_384K; params->au_l2_bitrate = new; break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!has_ac3) + return -EINVAL; + old = params->au_ac3_bitrate; + if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K && + new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K) + return -ERANGE; + if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K) + new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K; + else + new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K; + params->au_ac3_bitrate = new; + break; case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; if (set && new != old) @@ -449,17 +537,19 @@ static int handle_ctrl(struct saa6752hs_mpeg_params *params, return 0; } -static int saa6752hs_qctrl(struct saa6752hs_mpeg_params *params, +static int saa6752hs_qctrl(struct saa6752hs_state *h, struct v4l2_queryctrl *qctrl) { + struct saa6752hs_mpeg_params *params = &h->params; int err; switch (qctrl->id) { case V4L2_CID_MPEG_AUDIO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return v4l2_ctrl_query_fill(qctrl, @@ -467,6 +557,14 @@ static int saa6752hs_qctrl(struct saa6752hs_mpeg_params *params, V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, V4L2_MPEG_AUDIO_L2_BITRATE_256K); + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!h->has_ac3) + return -EINVAL; + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K, 1, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K); + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, @@ -513,44 +611,57 @@ static int saa6752hs_qctrl(struct saa6752hs_mpeg_params *params, return -EINVAL; } -static int saa6752hs_qmenu(struct saa6752hs_mpeg_params *params, +static int saa6752hs_qmenu(struct saa6752hs_state *h, struct v4l2_querymenu *qmenu) { - static const char *mpeg_audio_l2_bitrate[] = { - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "256 kbps", - "", - "384 kbps", - NULL + static const u32 mpeg_audio_encoding[] = { + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_CTRL_MENU_IDS_END + }; + static const u32 mpeg_audio_ac3_encoding[] = { + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_AC3, + V4L2_CTRL_MENU_IDS_END + }; + static u32 mpeg_audio_l2_bitrate[] = { + V4L2_MPEG_AUDIO_L2_BITRATE_256K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, + V4L2_CTRL_MENU_IDS_END + }; + static u32 mpeg_audio_ac3_bitrate[] = { + V4L2_MPEG_AUDIO_AC3_BITRATE_256K, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K, + V4L2_CTRL_MENU_IDS_END }; struct v4l2_queryctrl qctrl; int err; qctrl.id = qmenu->id; - err = saa6752hs_qctrl(params, &qctrl); + err = saa6752hs_qctrl(h, &qctrl); if (err) return err; - if (qmenu->id == V4L2_CID_MPEG_AUDIO_L2_BITRATE) - return v4l2_ctrl_query_menu(qmenu, &qctrl, + switch (qmenu->id) { + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + return v4l2_ctrl_query_menu_valid_items(qmenu, mpeg_audio_l2_bitrate); - return v4l2_ctrl_query_menu(qmenu, &qctrl, - v4l2_ctrl_get_menu(qmenu->id)); + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!h->has_ac3) + return -EINVAL; + return v4l2_ctrl_query_menu_valid_items(qmenu, + mpeg_audio_ac3_bitrate); + case V4L2_CID_MPEG_AUDIO_ENCODING: + return v4l2_ctrl_query_menu_valid_items(qmenu, + h->has_ac3 ? mpeg_audio_ac3_encoding : + mpeg_audio_encoding); + } + return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); } -static int saa6752hs_init(struct i2c_client* client) +static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes) { unsigned char buf[9], buf2[4]; struct saa6752hs_state *h; + unsigned size; u32 crc; unsigned char localPAT[256]; unsigned char localPMT[256]; @@ -558,45 +669,31 @@ static int saa6752hs_init(struct i2c_client* client) h = i2c_get_clientdata(client); /* Set video format - must be done first as it resets other settings */ - buf[0] = 0x41; - buf[1] = h->video_format; - i2c_master_send(client, buf, 2); + set_reg8(client, 0x41, h->video_format); /* Set number of lines in input signal */ - buf[0] = 0x40; - buf[1] = 0x00; - if (h->standard & V4L2_STD_525_60) - buf[1] = 0x01; - i2c_master_send(client, buf, 2); + set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0); /* set bitrate */ - saa6752hs_set_bitrate(client, &h->params); + saa6752hs_set_bitrate(client, h); /* Set GOP structure {3, 13} */ - buf[0] = 0x72; - buf[1] = 0x03; - buf[2] = 0x0D; - i2c_master_send(client,buf,3); + set_reg16(client, 0x72, 0x030d); /* Set minimum Q-scale {4} */ - buf[0] = 0x82; - buf[1] = 0x04; - i2c_master_send(client,buf,2); + set_reg8(client, 0x82, 0x04); /* Set maximum Q-scale {12} */ - buf[0] = 0x83; - buf[1] = 0x0C; - i2c_master_send(client,buf,2); + set_reg8(client, 0x83, 0x0c); /* Set Output Protocol */ - buf[0] = 0xD0; - buf[1] = 0x81; - i2c_master_send(client,buf,2); + set_reg8(client, 0xd0, 0x81); /* Set video output stream format {TS} */ - buf[0] = 0xB0; - buf[1] = 0x05; - i2c_master_send(client,buf,2); + set_reg8(client, 0xb0, 0x05); + + /* Set leading null byte for TS */ + set_reg16(client, 0xf6, leading_null_bytes); /* compute PAT */ memcpy(localPAT, PAT, sizeof(PAT)); @@ -609,7 +706,13 @@ static int saa6752hs_init(struct i2c_client* client) localPAT[sizeof(PAT) - 1] = crc & 0xFF; /* compute PMT */ - memcpy(localPMT, PMT, sizeof(PMT)); + if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { + size = sizeof(PMT_AC3); + memcpy(localPMT, PMT_AC3, size); + } else { + size = sizeof(PMT); + memcpy(localPMT, PMT, size); + } localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f); localPMT[4] = h->params.ts_pid_pmt & 0xff; localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F); @@ -618,40 +721,28 @@ static int saa6752hs_init(struct i2c_client* client) localPMT[21] = h->params.ts_pid_video & 0xFF; localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F); localPMT[26] = h->params.ts_pid_audio & 0xFF; - crc = crc32_be(~0, &localPMT[7], sizeof(PMT) - 7 - 4); - localPMT[sizeof(PMT) - 4] = (crc >> 24) & 0xFF; - localPMT[sizeof(PMT) - 3] = (crc >> 16) & 0xFF; - localPMT[sizeof(PMT) - 2] = (crc >> 8) & 0xFF; - localPMT[sizeof(PMT) - 1] = crc & 0xFF; + crc = crc32_be(~0, &localPMT[7], size - 7 - 4); + localPMT[size - 4] = (crc >> 24) & 0xFF; + localPMT[size - 3] = (crc >> 16) & 0xFF; + localPMT[size - 2] = (crc >> 8) & 0xFF; + localPMT[size - 1] = crc & 0xFF; /* Set Audio PID */ - buf[0] = 0xC1; - buf[1] = (h->params.ts_pid_audio >> 8) & 0xFF; - buf[2] = h->params.ts_pid_audio & 0xFF; - i2c_master_send(client,buf,3); + set_reg16(client, 0xc1, h->params.ts_pid_audio); /* Set Video PID */ - buf[0] = 0xC0; - buf[1] = (h->params.ts_pid_video >> 8) & 0xFF; - buf[2] = h->params.ts_pid_video & 0xFF; - i2c_master_send(client,buf,3); + set_reg16(client, 0xc0, h->params.ts_pid_video); /* Set PCR PID */ - buf[0] = 0xC4; - buf[1] = (h->params.ts_pid_pcr >> 8) & 0xFF; - buf[2] = h->params.ts_pid_pcr & 0xFF; - i2c_master_send(client,buf,3); + set_reg16(client, 0xc4, h->params.ts_pid_pcr); /* Send SI tables */ - i2c_master_send(client,localPAT,sizeof(PAT)); - i2c_master_send(client,localPMT,sizeof(PMT)); + i2c_master_send(client, localPAT, sizeof(PAT)); + i2c_master_send(client, localPMT, size); /* mute then unmute audio. This removes buzzing artefacts */ - buf[0] = 0xa4; - buf[1] = 1; - i2c_master_send(client, buf, 2); - buf[1] = 0; - i2c_master_send(client, buf, 2); + set_reg8(client, 0xa4, 1); + set_reg8(client, 0xa4, 0); /* start it going */ saa6752hs_chip_command(client, SAA6752HS_COMMAND_START); @@ -689,45 +780,6 @@ static int saa6752hs_init(struct i2c_client* client) return 0; } -static int saa6752hs_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct saa6752hs_state *h; - - - if (NULL == (h = kzalloc(sizeof(*h), GFP_KERNEL))) - return -ENOMEM; - h->client = client_template; - h->params = param_defaults; - h->client.adapter = adap; - h->client.addr = addr; - - /* Assume 625 input lines */ - h->standard = 0; - - i2c_set_clientdata(&h->client, h); - i2c_attach_client(&h->client); - - v4l_info(&h->client,"saa6752hs: chip found @ 0x%x\n", addr<<1); - return 0; -} - -static int saa6752hs_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, saa6752hs_attach); - return 0; -} - -static int saa6752hs_detach(struct i2c_client *client) -{ - struct saa6752hs_state *h; - - h = i2c_get_clientdata(client); - i2c_detach_client(client); - kfree(h); - return 0; -} - static int saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) { @@ -738,14 +790,13 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) int i; switch (cmd) { + case VIDIOC_INT_INIT: + /* apply settings and start encoder */ + saa6752hs_init(client, *(u32 *)arg); + break; case VIDIOC_S_EXT_CTRLS: if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) return -EINVAL; - if (ctrls->count == 0) { - /* apply settings and start encoder */ - saa6752hs_init(client); - break; - } /* fall through */ case VIDIOC_TRY_EXT_CTRLS: case VIDIOC_G_EXT_CTRLS: @@ -753,7 +804,8 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) return -EINVAL; params = h->params; for (i = 0; i < ctrls->count; i++) { - if ((err = handle_ctrl(¶ms, ctrls->controls + i, cmd))) { + err = handle_ctrl(h->has_ac3, ¶ms, ctrls->controls + i, cmd); + if (err) { ctrls->error_idx = i; return err; } @@ -761,9 +813,9 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) h->params = params; break; case VIDIOC_QUERYCTRL: - return saa6752hs_qctrl(&h->params, arg); + return saa6752hs_qctrl(h, arg); case VIDIOC_QUERYMENU: - return saa6752hs_qmenu(&h->params, arg); + return saa6752hs_qmenu(h, arg); case VIDIOC_G_FMT: { struct v4l2_format *f = arg; @@ -786,6 +838,11 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) case VIDIOC_S_STD: h->standard = *((v4l2_std_id *) arg); break; + + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, + arg, h->chip, h->revision); + default: /* nothing */ break; @@ -794,36 +851,59 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) return err; } -/* ----------------------------------------------------------------------- */ +static int saa6752hs_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL); + u8 addr = 0x13; + u8 data[12]; -static struct i2c_driver driver = { - .driver = { - .name = "saa6752hs", - }, - .id = I2C_DRIVERID_SAA6752HS, - .attach_adapter = saa6752hs_probe, - .detach_client = saa6752hs_detach, - .command = saa6752hs_command, -}; + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + if (h == NULL) + return -ENOMEM; -static struct i2c_client client_template = -{ - .name = "saa6752hs", - .driver = &driver, -}; + i2c_master_send(client, &addr, 1); + i2c_master_recv(client, data, sizeof(data)); + h->chip = V4L2_IDENT_SAA6752HS; + h->revision = (data[8] << 8) | data[9]; + h->has_ac3 = 0; + if (h->revision == 0x0206) { + h->chip = V4L2_IDENT_SAA6752HS_AC3; + h->has_ac3 = 1; + v4l_info(client, "support AC-3\n"); + } + h->params = param_defaults; + h->standard = 0; /* Assume 625 input lines */ -static int __init saa6752hs_init_module(void) -{ - return i2c_add_driver(&driver); + i2c_set_clientdata(client, h); + return 0; } -static void __exit saa6752hs_cleanup_module(void) +static int saa6752hs_remove(struct i2c_client *client) { - i2c_del_driver(&driver); + kfree(i2c_get_clientdata(client)); + return 0; } -module_init(saa6752hs_init_module); -module_exit(saa6752hs_cleanup_module); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id saa6752hs_id[] = { + { "saa6752hs", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa6752hs_id); + +#endif +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa6752hs", + .driverid = I2C_DRIVERID_SAA6752HS, + .command = saa6752hs_command, + .probe = saa6752hs_probe, + .remove = saa6752hs_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = saa6752hs_id, +#endif +}; /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index e02c3c1b1..ce8bcc967 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -264,7 +264,7 @@ unsigned long saa7134_buffer_base(struct saa7134_buf *buf) int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt) { __le32 *cpu; - dma_addr_t dma_addr; + dma_addr_t dma_addr = 0; cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr); if (NULL == cpu) @@ -408,32 +408,6 @@ void saa7134_buffer_timeout(unsigned long data) spin_unlock_irqrestore(&dev->slock,flags); } -/* resends a current buffer in queue after resume */ - -static int saa7134_buffer_requeue(struct saa7134_dev *dev, - struct saa7134_dmaqueue *q) -{ - struct saa7134_buf *buf, *next; - - assert_spin_locked(&dev->slock); - - buf = q->curr; - next = buf; - dprintk("buffer_requeue\n"); - - if (!buf) - return 0; - - dprintk("buffer_requeue : resending active buffers \n"); - - if (!list_empty(&q->queue)) - next = list_entry(q->queue.next, struct saa7134_buf, - vb.queue); - buf->activate(dev, buf, next); - - return 0; -} - /* ------------------------------------------------------------------ */ int saa7134_set_dmabits(struct saa7134_dev *dev) @@ -491,9 +465,7 @@ int saa7134_set_dmabits(struct saa7134_dev *dev) /* TS capture -- dma 5 */ if (dev->ts_q.curr) { ctrl |= SAA7134_MAIN_CTRL_TE5; - irq |= SAA7134_IRQ1_INTE_RA2_3 | - SAA7134_IRQ1_INTE_RA2_2 | - SAA7134_IRQ1_INTE_RA2_1 | + irq |= SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0; } @@ -1203,6 +1175,32 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev) } #ifdef CONFIG_PM + +/* resends a current buffer in queue after resume */ +static int saa7134_buffer_requeue(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q) +{ + struct saa7134_buf *buf, *next; + + assert_spin_locked(&dev->slock); + + buf = q->curr; + next = buf; + dprintk("buffer_requeue\n"); + + if (!buf) + return 0; + + dprintk("buffer_requeue : resending active buffers \n"); + + if (!list_empty(&q->queue)) + next = list_entry(q->queue.next, struct saa7134_buf, + vb.queue); + buf->activate(dev, buf, next); + + return 0; +} + static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state) { diff --git a/linux/drivers/media/video/saa7134/saa7134-empress.c b/linux/drivers/media/video/saa7134/saa7134-empress.c index a454968f7..9ebe166cc 100644 --- a/linux/drivers/media/video/saa7134/saa7134-empress.c +++ b/linux/drivers/media/video/saa7134/saa7134-empress.c @@ -29,6 +29,7 @@ #include <media/saa6752hs.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> /* ------------------------------------------------------------------ */ @@ -63,10 +64,19 @@ static void ts_reset_encoder(struct saa7134_dev* dev) static int ts_init_encoder(struct saa7134_dev* dev) { - struct v4l2_ext_controls ctrls = { V4L2_CTRL_CLASS_MPEG, 0 }; + u32 leading_null_bytes = 0; + /* If more cards start to need this, then this + should probably be added to the card definitions. */ + switch (dev->board) { + case SAA7134_BOARD_BEHOLD_M6: + case SAA7134_BOARD_BEHOLD_M63: + case SAA7134_BOARD_BEHOLD_M6_EXTRA: + leading_null_bytes = 1; + break; + } ts_reset_encoder(dev); - saa7134_i2c_call_clients(dev, VIDIOC_S_EXT_CTRLS, &ctrls); + saa7134_i2c_call_clients(dev, VIDIOC_INT_INIT, &leading_null_bytes); dev->empress_started = 1; return 0; } @@ -293,15 +303,6 @@ static int empress_streamoff(struct file *file, void *priv, return videobuf_streamoff(&dev->empress_tsq); } -static int saa7134_i2c_call_saa6752(struct saa7134_dev *dev, - unsigned int cmd, void *arg) -{ - if (dev->mpeg_i2c_client == NULL) - return -EINVAL; - return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client, - cmd, arg); -} - static int empress_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls) { @@ -403,6 +404,39 @@ static int empress_querymenu(struct file *file, void *priv, return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYMENU, c); } +static int empress_g_chip_ident(struct file *file, void *fh, + struct v4l2_chip_ident *chip) +{ + struct saa7134_dev *dev = file->private_data; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (dev->mpeg_i2c_client == NULL) + return -EINVAL; + if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match_chip == I2C_DRIVERID_SAA6752HS) + return saa7134_i2c_call_saa6752(dev, VIDIOC_G_CHIP_IDENT, chip); + if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR && + chip->match_chip == dev->mpeg_i2c_client->addr) + return saa7134_i2c_call_saa6752(dev, VIDIOC_G_CHIP_IDENT, chip); + return -EINVAL; +} + +static int empress_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_dev *dev = file->private_data; + + return saa7134_s_std_internal(dev, NULL, id); +} + +static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_dev *dev = file->private_data; + + *id = dev->tvnorm->id; + return 0; +} + static const struct file_operations ts_fops = { .owner = THIS_MODULE, @@ -431,11 +465,13 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { .vidioc_enum_input = empress_enum_input, .vidioc_g_input = empress_g_input, .vidioc_s_input = empress_s_input, - .vidioc_queryctrl = empress_queryctrl, .vidioc_querymenu = empress_querymenu, .vidioc_g_ctrl = empress_g_ctrl, .vidioc_s_ctrl = empress_s_ctrl, + .vidioc_g_chip_ident = empress_g_chip_ident, + .vidioc_s_std = empress_s_std, + .vidioc_g_std = empress_g_std, }; /* ----------------------------------------------------------- */ diff --git a/linux/drivers/media/video/saa7134/saa7134-i2c.c b/linux/drivers/media/video/saa7134/saa7134-i2c.c index bdec276e0..2ca03df8c 100644 --- a/linux/drivers/media/video/saa7134/saa7134-i2c.c +++ b/linux/drivers/media/video/saa7134/saa7134-i2c.c @@ -432,6 +432,16 @@ void saa7134_i2c_call_clients(struct saa7134_dev *dev, i2c_clients_command(&dev->i2c_adap, cmd, arg); } +int saa7134_i2c_call_saa6752(struct saa7134_dev *dev, + unsigned int cmd, void *arg) +{ + if (dev->mpeg_i2c_client == NULL) + return -EINVAL; + return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client, + cmd, arg); +} +EXPORT_SYMBOL_GPL(saa7134_i2c_call_saa6752); + int saa7134_i2c_register(struct saa7134_dev *dev) { dev->i2c_adap = saa7134_adap_template; diff --git a/linux/drivers/media/video/saa7134/saa7134-ts.c b/linux/drivers/media/video/saa7134/saa7134-ts.c index eae72fd60..ef55a59f0 100644 --- a/linux/drivers/media/video/saa7134/saa7134-ts.c +++ b/linux/drivers/media/video/saa7134/saa7134-ts.c @@ -66,11 +66,29 @@ static int buffer_activate(struct saa7134_dev *dev, saa7134_set_dmabits(dev); mod_timer(&dev->ts_q.timeout, jiffies+BUFFER_TIMEOUT); + + if (dev->ts_state == SAA7134_TS_BUFF_DONE) { + /* Clear TS cache */ + dev->buff_cnt = 0; + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + saa_writeb(SAA7134_TS_SERIAL1, 0x03); + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + saa_writeb(SAA7134_TS_SERIAL1, 0x01); + + /* TS clock non-inverted */ + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + + /* Start TS stream */ + saa_writeb(SAA7134_TS_SERIAL0, 0x40); + saa_writeb(SAA7134_TS_PARALLEL, 0xEC); + dev->ts_state = SAA7134_TS_STARTED; + } + return 0; } static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) + enum v4l2_field field) { struct saa7134_dev *dev = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); @@ -110,16 +128,22 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, goto oops; } - /* dma: setup channel 5 (= TS) */ - control = SAA7134_RS_CONTROL_BURST_16 | - SAA7134_RS_CONTROL_ME | - (buf->pt->dma >> 12); - - saa_writeb(SAA7134_TS_DMA0, ((lines-1)&0xff)); - saa_writeb(SAA7134_TS_DMA1, (((lines-1)>>8)&0xff)); - saa_writeb(SAA7134_TS_DMA2, ((((lines-1)>>16)&0x3f) | 0x00)); /* TSNOPIT=0, TSCOLAP=0 */ - saa_writel(SAA7134_RS_PITCH(5),TS_PACKET_SIZE); - saa_writel(SAA7134_RS_CONTROL(5),control); + dev->buff_cnt++; + + if (dev->buff_cnt == dev->ts.nr_bufs) { + dev->ts_state = SAA7134_TS_BUFF_DONE; + /* dma: setup channel 5 (= TS) */ + control = SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (buf->pt->dma >> 12); + + saa_writeb(SAA7134_TS_DMA0, (lines - 1) & 0xff); + saa_writeb(SAA7134_TS_DMA1, ((lines - 1) >> 8) & 0xff); + /* TSNOPIT=0, TSCOLAP=0 */ + saa_writeb(SAA7134_TS_DMA2, (((lines - 1) >> 16) & 0x3f) | 0x00); + saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE); + saa_writel(SAA7134_RS_CONTROL(5), control); + } buf->vb.state = VIDEOBUF_PREPARED; buf->activate = buffer_activate; @@ -140,6 +164,8 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) if (0 == *count) *count = dev->ts.nr_bufs; *count = saa7134_buffer_count(*size,*count); + dev->buff_cnt = 0; + dev->ts_state = SAA7134_TS_STOPPED; return 0; } @@ -154,7 +180,13 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_dev *dev = q->priv_data; + if (dev->ts_state == SAA7134_TS_STARTED) { + /* Stop TS transport */ + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); + dev->ts_state = SAA7134_TS_STOPPED; + } saa7134_dma_free(q,buf); } @@ -182,7 +214,7 @@ int saa7134_ts_init_hw(struct saa7134_dev *dev) /* deactivate TS softreset */ saa_writeb(SAA7134_TS_SERIAL1, 0x00); /* TSSOP high active, TSVAL high active, TSLOCK ignored */ - saa_writeb(SAA7134_TS_PARALLEL, 0xec); + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1)); saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff)); saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff)); diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 547c0401a..241b60e3f 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -628,6 +628,9 @@ void saa7134_set_tvnorm_hw(struct saa7134_dev *dev) if (card_in(dev, dev->ctl_input).tv) saa7134_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id); + /* Set the correct norm for the saa6752hs. This function + does nothing if there is no saa6752hs. */ + saa7134_i2c_call_saa6752(dev, VIDIOC_S_STD, &dev->tvnorm->id); } static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) @@ -1803,18 +1806,25 @@ static int saa7134_querycap(struct file *file, void *priv, return 0; } -static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) +int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; unsigned long flags; unsigned int i; v4l2_std_id fixup; int err; - err = v4l2_prio_check(&dev->prio, &fh->prio); - if (0 != err) - return err; + /* When called from the empress code fh == NULL. + That needs to be fixed somehow, but for now this is + good enough. */ + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } else if (res_locked(dev, RESOURCE_OVERLAY)) { + /* Don't change the std from the mpeg device + if overlay is active. */ + return -EBUSY; + } for (i = 0; i < TVNORMS; i++) if (*id == tvnorms[i].id) @@ -1847,7 +1857,7 @@ static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) *id = tvnorms[i].id; mutex_lock(&dev->lock); - if (res_check(fh, RESOURCE_OVERLAY)) { + if (fh && res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock, flags); stop_preview(dev, fh); spin_unlock_irqrestore(&dev->slock, flags); @@ -1864,6 +1874,23 @@ static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) mutex_unlock(&dev->lock); return 0; } +EXPORT_SYMBOL_GPL(saa7134_s_std_internal); + +static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_fh *fh = priv; + + return saa7134_s_std_internal(fh->dev, fh, id); +} + +static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + *id = dev->tvnorm->id; + return 0; +} static int saa7134_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) @@ -2404,6 +2431,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_qbuf = saa7134_qbuf, .vidioc_dqbuf = saa7134_dqbuf, .vidioc_s_std = saa7134_s_std, + .vidioc_g_std = saa7134_g_std, .vidioc_enum_input = saa7134_enum_input, .vidioc_g_input = saa7134_g_input, .vidioc_s_input = saa7134_s_input, diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index e3ad9d9e7..50319493d 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -467,6 +467,12 @@ struct saa7134_mpeg_ops { void (*signal_change)(struct saa7134_dev *dev); }; +enum saa7134_ts_status { + SAA7134_TS_STOPPED, + SAA7134_TS_BUFF_DONE, + SAA7134_TS_STARTED, +}; + /* global device status */ struct saa7134_dev { struct list_head devlist; @@ -560,6 +566,8 @@ struct saa7134_dev { /* SAA7134_MPEG_* */ struct saa7134_ts ts; struct saa7134_dmaqueue ts_q; + enum saa7134_ts_status ts_state; + unsigned int buff_cnt; struct saa7134_mpeg_ops *mops; struct i2c_client *mpeg_i2c_client; @@ -659,6 +667,8 @@ int saa7134_i2c_register(struct saa7134_dev *dev); int saa7134_i2c_unregister(struct saa7134_dev *dev); void saa7134_i2c_call_clients(struct saa7134_dev *dev, unsigned int cmd, void *arg); +int saa7134_i2c_call_saa6752(struct saa7134_dev *dev, + unsigned int cmd, void *arg); /* ----------------------------------------------------------- */ @@ -671,6 +681,7 @@ extern struct video_device saa7134_radio_template; int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c); +int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id); int saa7134_videoport_init(struct saa7134_dev *dev); void saa7134_set_tvnorm_hw(struct saa7134_dev *dev); diff --git a/linux/drivers/media/video/se401.c b/linux/drivers/media/video/se401.c index 04897413d..4b55b7dd7 100644 --- a/linux/drivers/media/video/se401.c +++ b/linux/drivers/media/video/se401.c @@ -1244,6 +1244,7 @@ static const struct file_operations se401_fops = { static struct video_device se401_template = { .name = "se401 USB camera", .fops = &se401_fops, + .release = video_device_release_empty, }; @@ -1409,7 +1410,7 @@ static int se401_probe(struct usb_interface *intf, mutex_init(&se401->lock); wmb(); - if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { kfree(se401); err("video_register_device failed"); return -EIO; diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index 318754e73..76838091d 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -304,9 +304,6 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) "SuperH Mobile CEU driver attached to camera %d\n", icd->devnum); - if (pcdev->pdata->enable_camera) - pcdev->pdata->enable_camera(); - ret = icd->ops->init(icd); if (ret) goto err; @@ -333,8 +330,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) ceu_write(pcdev, CEIER, 0); ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ icd->ops->release(icd); - if (pcdev->pdata->disable_camera) - pcdev->pdata->disable_camera(); dev_info(&icd->dev, "SuperH Mobile CEU driver detached from camera %d\n", diff --git a/linux/drivers/media/video/sn9c102/sn9c102_core.c b/linux/drivers/media/video/sn9c102/sn9c102_core.c index e92e79da3..fd867dadf 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_core.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_core.c @@ -116,6 +116,26 @@ MODULE_PARM_DESC(debug, "\n"); #endif +/* + Add the probe entries to this table. Be sure to add the entry in the right + place, since, on failure, the next probing routine is called according to + the order of the list below, from top to bottom. +*/ +static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = { + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ + &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ + &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ +}; + /*****************************************************************************/ static u32 @@ -1750,7 +1770,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) if (!down_read_trylock(&sn9c102_dev_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); if (wait_for_completion_interruptible(&cam->probe)) { up_read(&sn9c102_dev_lock); @@ -1847,7 +1867,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp) down_write(&sn9c102_dev_lock); - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); sn9c102_stop_transfer(cam); sn9c102_release_buffers(cam); @@ -1867,7 +1887,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp) static ssize_t sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); struct sn9c102_frame_t* f, * i; unsigned long lock_flags; long timeout; @@ -1991,7 +2011,7 @@ exit: static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); struct sn9c102_frame_t* f; unsigned long lock_flags; unsigned int mask = 0; @@ -2067,7 +2087,7 @@ static struct vm_operations_struct sn9c102_vm_ops = { static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; void *pos; @@ -3079,7 +3099,7 @@ sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg) static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); switch (cmd) { @@ -3183,7 +3203,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, static int sn9c102_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); int err = 0; if (mutex_lock_interruptible(&cam->fileop_mutex)) @@ -3316,6 +3336,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->fops = &sn9c102_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; + cam->v4ldev->parent = &udev->dev; init_completion(&cam->probe); diff --git a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h index 6ff489baa..e23734f6d 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -40,11 +40,14 @@ struct sn9c102_device; static const struct usb_device_id sn9c102_id_table[] = { /* SN9C101 and SN9C102 */ +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, @@ -53,29 +56,33 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* SN9C103 */ { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5130 */ { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, @@ -105,7 +112,7 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, @@ -133,24 +140,4 @@ extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); -/* - Add the above entries to this table. Be sure to add the entry in the right - place, since, on failure, the next probing routine is called according to - the order of the list below, from top to bottom. -*/ -static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ - &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ - &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ -}; - #endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c index eaf9ad0dc..db2434948 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int hv7131d_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/linux/drivers/media/video/sn9c102/sn9c102_hv7131r.c index 0fc401223..4295887ff 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_hv7131r.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_hv7131r.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int hv7131r_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c b/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c index 00b134ca0..1f5b09bec 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int mi0343_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_mi0360.c b/linux/drivers/media/video/sn9c102/sn9c102_mi0360.c index f8d81d82e..d973fc197 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_mi0360.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_mi0360.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int mi0360_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_mt9v111.c b/linux/drivers/media/video/sn9c102/sn9c102_mt9v111.c index 3b98ac3bb..95986eb49 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_mt9v111.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_mt9v111.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int mt9v111_init(struct sn9c102_device *cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c b/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c index e4856fd77..803712c29 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int ov7630_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_ov7660.c b/linux/drivers/media/video/sn9c102/sn9c102_ov7660.c index 8aae416ba..7977795d3 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_ov7660.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_ov7660.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int ov7660_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c b/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c index 360f2a848..81cd969c1 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c @@ -21,6 +21,7 @@ #include <linux/delay.h> #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int pas106b_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c index ca4a1506e..2782f94cf 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int pas202bcb_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c index e7d2de2ba..04cdfdde8 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int tas5110c1b_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5110d.c index d32fdbccd..9372e6f9f 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_tas5110d.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5110d.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int tas5110d_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c index 56fb1d575..a30bbc438 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int tas5130d1b_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c index c6660b363..055e92549 100644 --- a/linux/drivers/media/video/stk-webcam.c +++ b/linux/drivers/media/video/stk-webcam.c @@ -73,7 +73,7 @@ static void stk_camera_cleanup(struct kref *kref) STK_INFO("Syntek USB2.0 Camera release resources" " video device /dev/video%d\n", dev->vdev.minor); video_unregister_device(&dev->vdev); - dev->vdev.priv = NULL; + video_set_drvdata(&dev->vdev, NULL); if (dev->sio_bufs != NULL || dev->isobufs != NULL) STK_ERROR("We are leaking memory\n"); @@ -1380,7 +1380,7 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev = stk_v4l_data; dev->vdev.debug = debug; dev->vdev.parent = &dev->interface->dev; - dev->vdev.priv = dev; + video_set_drvdata(&dev->vdev, dev); err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); if (err) STK_ERROR("v4l registration failed\n"); diff --git a/linux/drivers/media/video/stradis.c b/linux/drivers/media/video/stradis.c index a081307c7..0e2b246c7 100644 --- a/linux/drivers/media/video/stradis.c +++ b/linux/drivers/media/video/stradis.c @@ -1930,6 +1930,7 @@ static struct video_device saa_template = { .name = "SAA7146A", .fops = &saa_fops, .minor = -1, + .release = video_device_release_empty, }; static int __devinit configure_saa7146(struct pci_dev *pdev, int num) diff --git a/linux/drivers/media/video/stv680.c b/linux/drivers/media/video/stv680.c index de32a5266..8df48a547 100644 --- a/linux/drivers/media/video/stv680.c +++ b/linux/drivers/media/video/stv680.c @@ -1469,7 +1469,7 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id mutex_init (&stv680->lock); wmb (); - if (video_register_device (stv680->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + if (video_register_device(stv680->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { PDEBUG (0, "STV(e): video_register_device failed"); retval = -EIO; goto error_vdev; diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c index f4ebfd84b..72adcb450 100644 --- a/linux/drivers/media/video/tda9840.c +++ b/linux/drivers/media/video/tda9840.c @@ -2,6 +2,7 @@ tda9840 - i2c-driver for the tda9840 by SGS Thomson Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> The tda9840 is a stereo/dual sound processor with digital identification. It can be found at address 0x84 on the i2c-bus. @@ -28,60 +29,120 @@ #include <linux/module.h> #include <linux/ioctl.h> #include <linux/i2c.h> +#include <media/v4l2-common.h> +#include <media/v4l2-i2c-drv-legacy.h> +#include "tda9840.h" #include "compat.h" -#include "tda9840.h" +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_DESCRIPTION("tda9840 driver"); +MODULE_LICENSE("GPL"); -static int debug; /* insmod parameter */ +static int debug; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); -#define dprintk(args...) \ - do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) +MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define SWITCH 0x00 #define LEVEL_ADJUST 0x02 #define STEREO_ADJUST 0x03 #define TEST 0x04 +#define TDA9840_SET_MUTE 0x00 +#define TDA9840_SET_MONO 0x10 +#define TDA9840_SET_STEREO 0x2a +#define TDA9840_SET_LANG1 0x12 +#define TDA9840_SET_LANG2 0x1e +#define TDA9840_SET_BOTH 0x1a +#define TDA9840_SET_BOTH_R 0x16 +#define TDA9840_SET_EXTERNAL 0x7a + /* addresses to scan, found only at 0x42 (7-Bit) */ static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END }; /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; -static struct i2c_driver driver; -static struct i2c_client client_template; +static void tda9840_write(struct i2c_client *client, u8 reg, u8 val) +{ + if (i2c_smbus_write_byte_data(client, reg, val)) + v4l_dbg(1, debug, client, "error writing %02x to %02x\n", + val, reg); +} -static int command(struct i2c_client *client, unsigned int cmd, void *arg) +static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) { - int result; + int result = 0; int byte = *(int *)arg; switch (cmd) { - case TDA9840_SWITCH: - - dprintk("TDA9840_SWITCH: 0x%02x\n", byte); - - if (byte != TDA9840_SET_MONO - && byte != TDA9840_SET_MUTE - && byte != TDA9840_SET_STEREO - && byte != TDA9840_SET_LANG1 - && byte != TDA9840_SET_LANG2 - && byte != TDA9840_SET_BOTH - && byte != TDA9840_SET_BOTH_R - && byte != TDA9840_SET_EXTERNAL) { + case VIDIOC_S_TUNER: { + struct v4l2_tuner *t = arg; + int byte; + + if (t->index) return -EINVAL; + + switch (t->audmode) { + case V4L2_TUNER_MODE_STEREO: + byte = TDA9840_SET_STEREO; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + byte = TDA9840_SET_BOTH; + break; + case V4L2_TUNER_MODE_LANG1: + byte = TDA9840_SET_LANG1; + break; + case V4L2_TUNER_MODE_LANG2: + byte = TDA9840_SET_LANG2; + break; + default: + byte = TDA9840_SET_MONO; + break; } + v4l_dbg(1, debug, client, "TDA9840_SWITCH: 0x%02x\n", byte); + tda9840_write(client, SWITCH, byte); + break; + } + + case VIDIOC_G_TUNER: { + struct v4l2_tuner *t = arg; + u8 byte; - result = i2c_smbus_write_byte_data(client, SWITCH, byte); - if (result) - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); + t->rxsubchans = V4L2_TUNER_SUB_MONO; + if (1 != i2c_master_recv(client, &byte, 1)) { + v4l_dbg(1, debug, client, + "i2c_master_recv() failed\n"); + return -EIO; + } + + if (byte & 0x80) { + v4l_dbg(1, debug, client, + "TDA9840_DETECT: register contents invalid\n"); + return -EINVAL; + } + + v4l_dbg(1, debug, client, "TDA9840_DETECT: byte: 0x%02x\n", byte); + + switch (byte & 0x60) { + case 0x00: + t->rxsubchans = V4L2_TUNER_SUB_MONO; + break; + case 0x20: + t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + case 0x40: + t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + break; + default: /* Incorrect detect */ + t->rxsubchans = V4L2_TUNER_MODE_MONO; + break; + } break; + } case TDA9840_LEVEL_ADJUST: - - dprintk("TDA9840_LEVEL_ADJUST: %d\n", byte); + v4l_dbg(1, debug, client, "TDA9840_LEVEL_ADJUST: %d\n", byte); /* check for correct range */ if (byte > 25 || byte < -20) @@ -93,15 +154,11 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) byte += 0x8; else byte = -byte; - - result = i2c_smbus_write_byte_data(client, LEVEL_ADJUST, byte); - if (result) - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); + tda9840_write(client, LEVEL_ADJUST, byte); break; case TDA9840_STEREO_ADJUST: - - dprintk("TDA9840_STEREO_ADJUST: %d\n", byte); + v4l_dbg(1, debug, client, "TDA9840_STEREO_ADJUST: %d\n", byte); /* check for correct range */ if (byte > 25 || byte < -24) @@ -114,39 +171,7 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) else byte = -byte; - result = i2c_smbus_write_byte_data(client, STEREO_ADJUST, byte); - if (result) - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); - break; - - case TDA9840_DETECT: { - int *ret = (int *)arg; - - byte = i2c_smbus_read_byte_data(client, STEREO_ADJUST); - if (byte == -1) { - dprintk("i2c_smbus_read_byte_data() failed\n"); - return -EIO; - } - - if (0 != (byte & 0x80)) { - dprintk("TDA9840_DETECT: register contents invalid\n"); - return -EINVAL; - } - - dprintk("TDA9840_DETECT: byte: 0x%02x\n", byte); - *ret = ((byte & 0x60) >> 5); - result = 0; - break; - } - case TDA9840_TEST: - dprintk("TDA9840_TEST: 0x%02x\n", byte); - - /* mask out irrelevant bits */ - byte &= 0x3; - - result = i2c_smbus_write_byte_data(client, TEST, byte); - if (result) - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); + tda9840_write(client, STEREO_ADJUST, byte); break; default: return -ENOIOCTLCMD; @@ -158,99 +183,54 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } -static int detect(struct i2c_adapter *adapter, int address, int kind) +static int tda9840_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct i2c_client *client; - int result = 0; - - int byte = 0x0; + int result; + int byte; /* let's see whether this adapter can support what we need */ - if (0 == i2c_check_functionality(adapter, - I2C_FUNC_SMBUS_READ_BYTE_DATA | - I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) return 0; - } - - /* allocate memory for client structure */ - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) { - printk("not enough kernel memory\n"); - return -ENOMEM; - } - - /* fill client structure */ - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->addr = address; - client->adapter = adapter; - /* tell the i2c layer a new client has arrived */ - if (0 != (result = i2c_attach_client(client))) { - kfree(client); - return result; - } + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); /* set initial values for level & stereo - adjustment, mode */ byte = 0; - result = command(client, TDA9840_LEVEL_ADJUST, &byte); - result += command(client, TDA9840_STEREO_ADJUST, &byte); - byte = TDA9840_SET_MONO; - result = command(client, TDA9840_SWITCH, &byte); + result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte); + result += tda9840_command(client, TDA9840_STEREO_ADJUST, &byte); + tda9840_write(client, SWITCH, TDA9840_SET_STEREO); if (result) { - dprintk("could not initialize tda9840\n"); + v4l_dbg(1, debug, client, "could not initialize tda9840\n"); return -ENODEV; } - - printk("tda9840: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]); return 0; } -static int attach(struct i2c_adapter *adapter) +static int tda9840_legacy_probe(struct i2c_adapter *adapter) { - /* let's see whether this is a know adapter we can attach to */ - if (adapter->id != I2C_HW_SAA7146) { - dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id); - return -ENODEV; - } - - return i2c_probe(adapter, &addr_data, &detect); -} - -static int detach(struct i2c_client *client) -{ - int ret = i2c_detach_client(client); - kfree(client); - return ret; + /* Let's see whether this is a known adapter we can attach to. + Prevents conflicts with tvaudio.c. */ + return adapter->id == I2C_HW_SAA7146; } - -static struct i2c_driver driver = { - .driver = { - .name = "tda9840", - }, - .id = I2C_DRIVERID_TDA9840, - .attach_adapter = attach, - .detach_client = detach, - .command = command, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id tda9840_id[] = { + { "tda9840", 0 }, + { } }; +MODULE_DEVICE_TABLE(i2c, tda9840_id); +#endif -static struct i2c_client client_template = { +static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tda9840", - .driver = &driver, + .driverid = I2C_DRIVERID_TDA9840, + .command = tda9840_command, + .probe = tda9840_probe, + .legacy_probe = tda9840_legacy_probe, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = tda9840_id, +#endif }; - -static int __init this_module_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit this_module_exit(void) -{ - i2c_del_driver(&driver); -} - -module_init(this_module_init); -module_exit(this_module_exit); - -MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); -MODULE_DESCRIPTION("tda9840 driver"); -MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/tda9840.h b/linux/drivers/media/video/tda9840.h index 7da8432cd..dc12ae7ca 100644 --- a/linux/drivers/media/video/tda9840.h +++ b/linux/drivers/media/video/tda9840.h @@ -3,24 +3,6 @@ #define I2C_ADDR_TDA9840 0x42 -#define TDA9840_DETECT _IOR('v',1,int) -/* return values for TDA9840_DETCT */ -#define TDA9840_MONO_DETECT 0x0 -#define TDA9840_DUAL_DETECT 0x1 -#define TDA9840_STEREO_DETECT 0x2 -#define TDA9840_INCORRECT_DETECT 0x3 - -#define TDA9840_SWITCH _IOW('v',2,int) -/* modes than can be set with TDA9840_SWITCH */ -#define TDA9840_SET_MUTE 0x00 -#define TDA9840_SET_MONO 0x10 -#define TDA9840_SET_STEREO 0x2a -#define TDA9840_SET_LANG1 0x12 -#define TDA9840_SET_LANG2 0x1e -#define TDA9840_SET_BOTH 0x1a -#define TDA9840_SET_BOTH_R 0x16 -#define TDA9840_SET_EXTERNAL 0x7a - /* values may range between +2.5 and -2.0; the value has to be multiplied with 10 */ #define TDA9840_LEVEL_ADJUST _IOW('v',3,int) @@ -29,7 +11,4 @@ the value has to be multiplied with 10 */ #define TDA9840_STEREO_ADJUST _IOW('v',4,int) -/* currently not implemented */ -#define TDA9840_TEST _IOW('v',5,int) - #endif diff --git a/linux/drivers/media/video/tea6415c.c b/linux/drivers/media/video/tea6415c.c index da654bd15..073f020fa 100644 --- a/linux/drivers/media/video/tea6415c.c +++ b/linux/drivers/media/video/tea6415c.c @@ -2,6 +2,7 @@ tea6415c - i2c-driver for the tea6415c by SGS Thomson Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> The tea6415c is a bus controlled video-matrix-switch with 8 inputs and 6 outputs. @@ -30,19 +31,19 @@ #include <linux/module.h> #include <linux/ioctl.h> #include <linux/i2c.h> +#include <media/v4l2-common.h> +#include <media/v4l2-i2c-drv-legacy.h> +#include "tea6415c.h" #include "compat.h" -#include "tea6415c.h" +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_DESCRIPTION("tea6415c driver"); +MODULE_LICENSE("GPL"); -static int debug; /* insmod parameter */ +static int debug; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); - -#define dprintk(args...) \ - do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) -#define TEA6415C_NUM_INPUTS 8 -#define TEA6415C_NUM_OUTPUTS 6 +MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* addresses to scan, found only at 0x03 and/or 0x43 (7-bit) */ static unsigned short normal_i2c[] = { I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIENT_END }; @@ -50,60 +51,6 @@ static unsigned short normal_i2c[] = { I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIEN /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; -static struct i2c_driver driver; -static struct i2c_client client_template; - -/* this function is called by i2c_probe */ -static int detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *client = NULL; - int err = 0; - - /* let's see whether this adapter can support what we need */ - if (0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) { - return 0; - } - - /* allocate memory for client structure */ - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) { - return -ENOMEM; - } - - /* fill client structure */ - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->addr = address; - client->adapter = adapter; - - /* tell the i2c layer a new client has arrived */ - if (0 != (err = i2c_attach_client(client))) { - kfree(client); - return err; - } - - printk("tea6415c: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]); - - return 0; -} - -static int attach(struct i2c_adapter *adapter) -{ - /* let's see whether this is a know adapter we can attach to */ - if (adapter->id != I2C_HW_SAA7146) { - dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id); - return -ENODEV; - } - - return i2c_probe(adapter, &addr_data, &detect); -} - -static int detach(struct i2c_client *client) -{ - int ret = i2c_detach_client(client); - kfree(client); - return ret; -} - /* makes a connection between the input-pin 'i' and the output-pin 'o' for the tea6415c-client 'client' */ static int switch_matrix(struct i2c_client *client, int i, int o) @@ -111,7 +58,7 @@ static int switch_matrix(struct i2c_client *client, int i, int o) u8 byte = 0; int ret; - dprintk("adr:0x%02x, i:%d, o:%d\n", client->addr, i, o); + v4l_dbg(1, debug, client, "i=%d, o=%d\n", i, o); /* check if the pins are valid */ if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i) @@ -169,14 +116,14 @@ static int switch_matrix(struct i2c_client *client, int i, int o) ret = i2c_smbus_write_byte(client, byte); if (ret) { - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", ret); + v4l_dbg(1, debug, client, + "i2c_smbus_write_byte() failed, ret:%d\n", ret); return -EIO; } - return ret; } -static int command(struct i2c_client *client, unsigned int cmd, void *arg) +static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg) { struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg; int result = 0; @@ -188,38 +135,44 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) default: return -ENOIOCTLCMD; } - return result; } -static struct i2c_driver driver = { - .driver = { - .name = "tea6415c", - }, - .id = I2C_DRIVERID_TEA6415C, - .attach_adapter = attach, - .detach_client = detach, - .command = command, -}; - -static struct i2c_client client_template = { - .name = "tea6415c", - .driver = &driver, -}; - -static int __init this_module_init(void) +/* this function is called by i2c_probe */ +static int tea6415c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - return i2c_add_driver(&driver); + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) + return 0; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + return 0; } -static void __exit this_module_exit(void) +static int tea6415c_legacy_probe(struct i2c_adapter *adapter) { - i2c_del_driver(&driver); + /* Let's see whether this is a known adapter we can attach to. + Prevents conflicts with tvaudio.c. */ + return adapter->id == I2C_HW_SAA7146; } -module_init(this_module_init); -module_exit(this_module_exit); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id tea6415c_id[] = { + { "tea6415c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tea6415c_id); +#endif -MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); -MODULE_DESCRIPTION("tea6415c driver"); -MODULE_LICENSE("GPL"); +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "tea6415c", + .driverid = I2C_DRIVERID_TEA6415C, + .command = tea6415c_command, + .probe = tea6415c_probe, + .legacy_probe = tea6415c_legacy_probe, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = tea6415c_id, +#endif +}; diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c index d0c63703c..8ded72ade 100644 --- a/linux/drivers/media/video/tea6420.c +++ b/linux/drivers/media/video/tea6420.c @@ -2,6 +2,7 @@ tea6420 - i2c-driver for the tea6420 by SGS Thomson Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> The tea6420 is a bus controlled audio-matrix with 5 stereo inputs, 4 stereo outputs and gain control for each output. @@ -30,16 +31,19 @@ #include <linux/module.h> #include <linux/ioctl.h> #include <linux/i2c.h> +#include <media/v4l2-common.h> +#include <media/v4l2-i2c-drv-legacy.h> +#include "tea6420.h" #include "compat.h" -#include "tea6420.h" +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_DESCRIPTION("tea6420 driver"); +MODULE_LICENSE("GPL"); -static int debug; /* insmod parameter */ +static int debug; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); -#define dprintk(args...) \ - do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) +MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */ static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I2C_CLIENT_END }; @@ -47,23 +51,20 @@ static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; -static struct i2c_driver driver; -static struct i2c_client client_template; - /* make a connection between the input 'i' and the output 'o' with gain 'g' for the tea6420-client 'client' (note: i = 6 means 'mute') */ static int tea6420_switch(struct i2c_client *client, int i, int o, int g) { - u8 byte = 0; + u8 byte; int ret; - dprintk("adr:0x%02x, i:%d, o:%d, g:%d\n", client->addr, i, o, g); + v4l_dbg(1, debug, client, "i=%d, o=%d, g=%d\n", i, o, g); /* check if the parameters are valid */ if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) return -1; - byte = ((o - 1) << 5); + byte = ((o - 1) << 5); byte |= (i - 1); /* to understand this, have a look at the tea6420-specs (p.5) */ @@ -83,40 +84,41 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g) ret = i2c_smbus_write_byte(client, byte); if (ret) { - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", ret); + v4l_dbg(1, debug, client, + "i2c_smbus_write_byte() failed, ret:%d\n", ret); return -EIO; } - return 0; } -/* this function is called by i2c_probe */ -static int tea6420_detect(struct i2c_adapter *adapter, int address, int kind) +static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg) { - struct i2c_client *client; - int err = 0, i = 0; + struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg; + int result = 0; - /* let's see whether this adapter can support what we need */ - if (0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) { - return 0; + switch (cmd) { + case TEA6420_SWITCH: + result = tea6420_switch(client, a->in, a->out, a->gain); + break; + default: + return -ENOIOCTLCMD; } - /* allocate memory for client structure */ - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) { - return -ENOMEM; - } + return result; +} - /* fill client structure */ - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->addr = address; - client->adapter = adapter; +/* this function is called by i2c_probe */ +static int tea6420_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err, i; - /* tell the i2c layer a new client has arrived */ - if (0 != (err = i2c_attach_client(client))) { - kfree(client); - return err; - } + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); /* set initial values: set "mute"-input to all outputs at gain 0 */ err = 0; @@ -124,78 +126,35 @@ static int tea6420_detect(struct i2c_adapter *adapter, int address, int kind) err += tea6420_switch(client, 6, i, 0); } if (err) { - dprintk("could not initialize tea6420\n"); + v4l_dbg(1, debug, client, "could not initialize tea6420\n"); kfree(client); return -ENODEV; } - - printk("tea6420: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]); - return 0; } -static int attach(struct i2c_adapter *adapter) +static int tea6420_legacy_probe(struct i2c_adapter *adapter) { - /* let's see whether this is a know adapter we can attach to */ - if (adapter->id != I2C_HW_SAA7146) { - dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id); - return -ENODEV; - } - - return i2c_probe(adapter, &addr_data, &tea6420_detect); -} - -static int detach(struct i2c_client *client) -{ - int ret = i2c_detach_client(client); - kfree(client); - return ret; -} - -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg; - int result = 0; - - switch (cmd) { - case TEA6420_SWITCH: - result = tea6420_switch(client, a->in, a->out, a->gain); - break; - default: - return -ENOIOCTLCMD; - } - - return result; + /* Let's see whether this is a known adapter we can attach to. + Prevents conflicts with tvaudio.c. */ + return adapter->id == I2C_HW_SAA7146; } -static struct i2c_driver driver = { - .driver = { - .name = "tea6420", - }, - .id = I2C_DRIVERID_TEA6420, - .attach_adapter = attach, - .detach_client = detach, - .command = command, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id tea6420_id[] = { + { "tea6420", 0 }, + { } }; +MODULE_DEVICE_TABLE(i2c, tea6420_id); +#endif -static struct i2c_client client_template = { +static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tea6420", - .driver = &driver, + .driverid = I2C_DRIVERID_TEA6420, + .command = tea6420_command, + .probe = tea6420_probe, + .legacy_probe = tea6420_legacy_probe, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = tea6420_id, +#endif }; - -static int __init this_module_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit this_module_exit(void) -{ - i2c_del_driver(&driver); -} - -module_init(this_module_init); -module_exit(this_module_exit); - -MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); -MODULE_DESCRIPTION("tea6420 driver"); -MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/tuner-3036.c b/linux/drivers/media/video/tuner-3036.c deleted file mode 100644 index 8b87d75f4..000000000 --- a/linux/drivers/media/video/tuner-3036.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Driver for Philips SAB3036 "CITAC" tuner control chip. - * - * Author: Phil Blundell <philb@gnu.org> - * - * The SAB3036 is just about different enough from the chips that - * tuner.c copes with to make it not worth the effort to crowbar - * the support into that file. So instead we have a separate driver. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/init.h> - -#include <linux/i2c.h> -#include "compat.h" -#include <linux/videodev.h> -#include <media/v4l2-common.h> - -#include <media/tuner.h> - -static int debug; /* insmod parameter */ -static int this_adap; - -static struct i2c_client client_template; - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x60, 0x61, I2C_CLIENT_END }; -static unsigned short ignore = I2C_CLIENT_END; - -static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_i2c, - .probe = &ignore, - .ignore = &ignore, -}; - -/* ---------------------------------------------------------------------- */ - -static unsigned char -tuner_getstatus (struct i2c_client *c) -{ - unsigned char byte; - if (i2c_master_recv(c, &byte, 1) != 1) - printk(KERN_ERR "tuner-3036: I/O error.\n"); - return byte; -} - -#define TUNER_FL 0x80 - -static int -tuner_islocked (struct i2c_client *c) -{ - return (tuner_getstatus(c) & TUNER_FL); -} - -/* ---------------------------------------------------------------------- */ - -static void -set_tv_freq(struct i2c_client *c, int freq) -{ - u16 div = ((freq * 20) / 16); - unsigned long give_up = jiffies + HZ; - unsigned char buffer[2]; - - if (debug) - printk(KERN_DEBUG "tuner: setting frequency %dMHz, divisor %x\n", freq / 16, div); - - /* Select high tuning current */ - buffer[0] = 0x29; - buffer[1] = 0x3e; - - if (i2c_master_send(c, buffer, 2) != 2) - printk("tuner: i2c i/o error 1\n"); - - buffer[0] = 0x80 | ((div>>8) & 0x7f); - buffer[1] = div & 0xff; - - if (i2c_master_send(c, buffer, 2) != 2) - printk("tuner: i2c i/o error 2\n"); - - while (!tuner_islocked(c) && time_before(jiffies, give_up)) - schedule(); - - if (!tuner_islocked(c)) - printk(KERN_WARNING "tuner: failed to achieve PLL lock\n"); - - /* Select low tuning current and engage AFC */ - buffer[0] = 0x29; - buffer[1] = 0xb2; - - if (i2c_master_send(c, buffer, 2) != 2) - printk("tuner: i2c i/o error 3\n"); - - if (debug) - printk(KERN_DEBUG "tuner: status %02x\n", tuner_getstatus(c)); -} - -/* ---------------------------------------------------------------------- */ - -static int -tuner_attach(struct i2c_adapter *adap, int addr, int kind) -{ - static unsigned char buffer[] = { 0x29, 0x32, 0x2a, 0, 0x2b, 0 }; - - struct i2c_client *client; - - if (this_adap > 0) - return -1; - this_adap++; - - client_template.adapter = adap; - client_template.addr = addr; - - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == NULL) - return -ENOMEM; - memcpy(client, &client_template, sizeof(struct i2c_client)); - - printk("tuner: SAB3036 found, status %02x\n", tuner_getstatus(client)); - - i2c_attach_client(client); - - if (i2c_master_send(client, buffer, 2) != 2) - printk("tuner: i2c i/o error 1\n"); - if (i2c_master_send(client, buffer+2, 2) != 2) - printk("tuner: i2c i/o error 2\n"); - if (i2c_master_send(client, buffer+4, 2) != 2) - printk("tuner: i2c i/o error 3\n"); - return 0; -} - -static int -tuner_detach(struct i2c_client *c) -{ - return 0; -} - -static int -tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - int *iarg = (int*)arg; - - switch (cmd) - { - case VIDIOCSFREQ: - set_tv_freq(client, *iarg); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int -tuner_probe(struct i2c_adapter *adap) -{ - this_adap = 0; - if (adap->id == I2C_HW_B_LP) - return i2c_probe(adap, &addr_data, tuner_attach); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static struct i2c_driver -i2c_driver_tuner = -{ - .driver = { - .name = "sab3036", - }, - .id = I2C_DRIVERID_SAB3036, - .attach_adapter = tuner_probe, - .detach_client = tuner_detach, - .command = tuner_command -}; - -static struct i2c_client client_template = -{ - .driver = &i2c_driver_tuner, - .name = "SAB3036", -}; - -static int __init -tuner3036_init(void) -{ - return i2c_add_driver(&i2c_driver_tuner); -} - -static void __exit -tuner3036_exit(void) -{ - i2c_del_driver(&i2c_driver_tuner); -} - -MODULE_DESCRIPTION("SAB3036 tuner driver"); -MODULE_AUTHOR("Philip Blundell <philb@gnu.org>"); -MODULE_LICENSE("GPL"); - -module_param(debug, int, 0); -MODULE_PARM_DESC(debug,"Enable debugging output"); - -module_init(tuner3036_init); -module_exit(tuner3036_exit); diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 9649281a9..d6ce6f6a5 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -453,8 +453,7 @@ static void set_type(struct i2c_client *c, unsigned int type, xc5000_cfg.if_khz = 5380; xc5000_cfg.tuner_callback = t->tuner_callback; if (!dvb_attach(xc5000_attach, - &t->fe, t->i2c->adapter, &xc5000_cfg, - c->adapter->algo_data)) + &t->fe, t->i2c->adapter, &xc5000_cfg)) goto attach_failed; xc_tuner_ops = &t->fe.ops.tuner_ops; diff --git a/linux/drivers/media/video/usbvideo/ibmcam.c b/linux/drivers/media/video/usbvideo/ibmcam.c index 1afbcc147..697e74a24 100644 --- a/linux/drivers/media/video/usbvideo/ibmcam.c +++ b/linux/drivers/media/video/usbvideo/ibmcam.c @@ -736,12 +736,12 @@ static enum ParseState ibmcam_model2_320x240_parse_lines( * make black color and quit the horizontal scanning loop. */ if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) { - const int j = i * V4L_BYTES_PER_PIXEL; + const int offset = i * V4L_BYTES_PER_PIXEL; #if USES_IBMCAM_PUTPIXEL /* Refresh 'f' because we don't use it much with PUTPIXEL */ - f = frame->data + (v4l_linesize * frame->curline) + j; + f = frame->data + (v4l_linesize * frame->curline) + offset; #endif - memset(f, 0, v4l_linesize - j); + memset(f, 0, v4l_linesize - offset); break; } diff --git a/linux/drivers/media/video/usbvideo/usbvideo.c b/linux/drivers/media/video/usbvideo/usbvideo.c index 5bda83f7d..2e29d95b6 100644 --- a/linux/drivers/media/video/usbvideo/usbvideo.c +++ b/linux/drivers/media/video/usbvideo/usbvideo.c @@ -1039,7 +1039,8 @@ int usbvideo_RegisterVideoDevice(struct uvd *uvd) return -EINVAL; } uvd->vdev.parent = &uvd->dev->dev; - if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + uvd->vdev.release = video_device_release_empty; + if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { err("%s: video_register_device failed", __func__); return -EPIPE; } diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c index e7330fe4f..bad26ccf8 100644 --- a/linux/drivers/media/video/usbvideo/vicam.c +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -782,9 +782,8 @@ vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsign static int vicam_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct vicam_camera *cam = - (struct vicam_camera *) dev->priv; + struct vicam_camera *cam = video_drvdata(file); + DBG("open\n"); if (!cam) { @@ -1111,6 +1110,7 @@ static struct video_device vicam_template = { .name = "ViCam-based USB Camera", .fops = &vicam_fops, .minor = -1, + .release = video_device_release_empty, }; /* table of devices that work with this driver */ @@ -1175,14 +1175,13 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) mutex_init(&cam->cam_lock); - memcpy(&cam->vdev, &vicam_template, - sizeof (vicam_template)); - cam->vdev.priv = cam; // sort of a reverse mapping for those functions that get vdev only + memcpy(&cam->vdev, &vicam_template, sizeof(vicam_template)); + video_set_drvdata(&cam->vdev, cam); cam->udev = dev; cam->bulkEndpoint = bulkEndpoint; - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) < 0) { kfree(cam); printk(KERN_WARNING "video_register_device failed\n"); return -EIO; diff --git a/linux/drivers/media/video/usbvision/usbvision-video.c b/linux/drivers/media/video/usbvision/usbvision-video.c index b76295a5b..782ee6436 100644 --- a/linux/drivers/media/video/usbvision/usbvision-video.c +++ b/linux/drivers/media/video/usbvision/usbvision-video.c @@ -360,9 +360,7 @@ static void usbvision_remove_sysfs(struct video_device *vdev) */ static int usbvision_v4l2_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode = 0; PDEBUG(DBG_IO, "open"); @@ -439,9 +437,7 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) */ static int usbvision_v4l2_close(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); PDEBUG(DBG_IO, "close"); mutex_lock(&usbvision->lock); @@ -486,9 +482,7 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file) static int vidioc_g_register (struct file *file, void *priv, struct v4l2_register *reg) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode; if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) @@ -507,9 +501,7 @@ static int vidioc_g_register (struct file *file, void *priv, static int vidioc_s_register (struct file *file, void *priv, struct v4l2_register *reg) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode; if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) @@ -528,9 +520,7 @@ static int vidioc_s_register (struct file *file, void *priv, static int vidioc_querycap (struct file *file, void *priv, struct v4l2_capability *vc) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); strlcpy(vc->card, @@ -550,9 +540,7 @@ static int vidioc_querycap (struct file *file, void *priv, static int vidioc_enum_input (struct file *file, void *priv, struct v4l2_input *vi) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int chan; if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) ) @@ -605,9 +593,7 @@ static int vidioc_enum_input (struct file *file, void *priv, static int vidioc_g_input (struct file *file, void *priv, unsigned int *input) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); *input = usbvision->ctl_input; return 0; @@ -615,9 +601,7 @@ static int vidioc_g_input (struct file *file, void *priv, unsigned int *input) static int vidioc_s_input (struct file *file, void *priv, unsigned int input) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); if ((input >= usbvision->video_inputs) || (input < 0) ) return -EINVAL; @@ -634,9 +618,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int input) static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); + usbvision->tvnormId=*id; mutex_lock(&usbvision->lock); @@ -652,9 +635,7 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) static int vidioc_g_tuner (struct file *file, void *priv, struct v4l2_tuner *vt) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); if (!usbvision->have_tuner || vt->index) // Only tuner 0 return -EINVAL; @@ -673,9 +654,7 @@ static int vidioc_g_tuner (struct file *file, void *priv, static int vidioc_s_tuner (struct file *file, void *priv, struct v4l2_tuner *vt) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); // Only no or one tuner for now if (!usbvision->have_tuner || vt->index) @@ -689,9 +668,7 @@ static int vidioc_s_tuner (struct file *file, void *priv, static int vidioc_g_frequency (struct file *file, void *priv, struct v4l2_frequency *freq) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); freq->tuner = 0; // Only one tuner if(usbvision->radio) { @@ -707,9 +684,7 @@ static int vidioc_g_frequency (struct file *file, void *priv, static int vidioc_s_frequency (struct file *file, void *priv, struct v4l2_frequency *freq) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); // Only no or one tuner for now if (!usbvision->have_tuner || freq->tuner) @@ -723,9 +698,7 @@ static int vidioc_s_frequency (struct file *file, void *priv, static int vidioc_g_audio (struct file *file, void *priv, struct v4l2_audio *a) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); memset(a,0,sizeof(*a)); if(usbvision->radio) { @@ -750,9 +723,7 @@ static int vidioc_s_audio (struct file *file, void *fh, static int vidioc_queryctrl (struct file *file, void *priv, struct v4l2_queryctrl *ctrl) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int id=ctrl->id; memset(ctrl,0,sizeof(*ctrl)); @@ -769,9 +740,7 @@ static int vidioc_queryctrl (struct file *file, void *priv, static int vidioc_g_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); return 0; @@ -780,9 +749,7 @@ static int vidioc_g_ctrl (struct file *file, void *priv, static int vidioc_s_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); return 0; @@ -791,9 +758,7 @@ static int vidioc_s_ctrl (struct file *file, void *priv, static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *vr) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int ret; RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES); @@ -821,9 +786,7 @@ static int vidioc_reqbufs (struct file *file, static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *vb) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); struct usbvision_frame *frame; /* FIXME : must control @@ -859,9 +822,7 @@ static int vidioc_querybuf (struct file *file, static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); struct usbvision_frame *frame; unsigned long lock_flags; @@ -898,9 +859,7 @@ static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb) static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int ret; struct usbvision_frame *f; unsigned long lock_flags; @@ -941,9 +900,7 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb) static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; usbvision->streaming = Stream_On; @@ -955,9 +912,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -990,9 +945,7 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *vf) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); vf->fmt.pix.width = usbvision->curwidth; vf->fmt.pix.height = usbvision->curheight; vf->fmt.pix.pixelformat = usbvision->palette.format; @@ -1008,9 +961,7 @@ static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *vf) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int formatIdx; /* Find requested format in available ones */ @@ -1038,9 +989,7 @@ static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int ret; if( 0 != (ret=vidioc_try_fmt_vid_cap (file, priv, vf)) ) { @@ -1068,9 +1017,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int noblock = file->f_flags & O_NONBLOCK; unsigned long lock_flags; @@ -1179,10 +1126,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) start = vma->vm_start; void *pos; u32 i; - - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); PDEBUG(DBG_MMAP, "mmap"); @@ -1239,9 +1183,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) */ static int usbvision_radio_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode = 0; PDEBUG(DBG_IO, "%s:", __func__); @@ -1291,9 +1233,7 @@ out: static int usbvision_radio_close(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode = 0; PDEBUG(DBG_IO, ""); diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 7e102034d..b2482502a 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1663,7 +1663,7 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message) return uvc_video_suspend(&dev->video); } -static int uvc_resume(struct usb_interface *intf) +static int __uvc_resume(struct usb_interface *intf, int reset) { struct uvc_device *dev = usb_get_intfdata(intf); int ret; @@ -1672,7 +1672,7 @@ static int uvc_resume(struct usb_interface *intf) intf->cur_altsetting->desc.bInterfaceNumber); if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) { - if ((ret = uvc_ctrl_resume_device(dev)) < 0) + if (reset && (ret = uvc_ctrl_resume_device(dev)) < 0) return ret; return uvc_status_resume(dev); @@ -1687,6 +1687,18 @@ static int uvc_resume(struct usb_interface *intf) return uvc_video_resume(&dev->video); } +static int uvc_resume(struct usb_interface *intf) +{ + return __uvc_resume(intf, 0); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) +static int uvc_reset_resume(struct usb_interface *intf) +{ + return __uvc_resume(intf, 1); +} +#endif + /* ------------------------------------------------------------------------ * Driver initialization and cleanup */ @@ -1920,6 +1932,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Fujitsu Amilo SI2636 - Bison Electronics */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0202, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Bison Electronics */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1952,6 +1973,9 @@ struct uvc_driver uvc_driver = { .disconnect = uvc_disconnect, .suspend = uvc_suspend, .resume = uvc_resume, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) + .reset_resume = uvc_reset_resume, +#endif .id_table = uvc_ids, .supports_autosuspend = 1, }, diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index d7bd71be4..d4758c8e1 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -400,15 +400,13 @@ static int uvc_has_privileges(struct uvc_fh *handle) static int uvc_v4l2_open(struct inode *inode, struct file *file) { - struct video_device *vdev; struct uvc_video_device *video; struct uvc_fh *handle; int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); mutex_lock(&uvc_driver.open_mutex); - vdev = video_devdata(file); - video = video_get_drvdata(vdev); + video = video_drvdata(file); if (video->dev->state & UVC_DEV_DISCONNECTED) { ret = -ENODEV; @@ -440,8 +438,7 @@ done: static int uvc_v4l2_release(struct inode *inode, struct file *file) { - struct video_device *vdev = video_devdata(file); - struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_video_device *video = video_drvdata(file); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); @@ -1031,8 +1028,7 @@ static struct vm_operations_struct uvc_vm_ops = { static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { - struct video_device *vdev = video_devdata(file); - struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_video_device *video = video_drvdata(file); struct uvc_buffer *uninitialized_var(buffer); struct page *page; unsigned long addr, start, size; @@ -1085,8 +1081,7 @@ done: static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) { - struct video_device *vdev = video_devdata(file); - struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_video_device *video = video_drvdata(file); uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index 6854ac78a..593aebffe 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -455,7 +455,8 @@ static void uvc_video_decode_isoc(struct urb *urb, urb->iso_frame_desc[i].actual_length - ret); /* Process the header again. */ - uvc_video_decode_end(video, buf, mem, ret); + uvc_video_decode_end(video, buf, mem, + urb->iso_frame_desc[i].actual_length); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) @@ -512,7 +513,7 @@ static void uvc_video_decode_bulk(struct urb *urb, video->bulk.payload_size >= video->bulk.max_payload_size) { if (!video->bulk.skip_payload && buf != NULL) { uvc_video_decode_end(video, buf, video->bulk.header, - video->bulk.header_size); + video->bulk.payload_size); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) buf = uvc_queue_next_buffer(&video->queue, buf); @@ -655,7 +656,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, if (size > UVC_MAX_FRAME_SIZE) return -EINVAL; - npackets = (size + psize - 1) / psize; + npackets = DIV_ROUND_UP(size, psize); if (npackets > UVC_MAX_ISO_PACKETS) npackets = UVC_MAX_ISO_PACKETS; diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index a027d9baa..b5baf8f7e 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -188,9 +188,11 @@ const char **v4l2_ctrl_get_menu(u32 id) NULL }; static const char *mpeg_audio_encoding[] = { - "Layer I", - "Layer II", - "Layer III", + "MPEG-1/2 Layer I", + "MPEG-1/2 Layer II", + "MPEG-1/2 Layer III", + "MPEG-2/4 AAC", + "AC-3", NULL }; static const char *mpeg_audio_l1_bitrate[] = { @@ -244,6 +246,28 @@ const char **v4l2_ctrl_get_menu(u32 id) "320 kbps", NULL }; + static const char *mpeg_audio_ac3_bitrate[] = { + "32 kbps", + "40 kbps", + "48 kbps", + "56 kbps", + "64 kbps", + "80 kbps", + "96 kbps", + "112 kbps", + "128 kbps", + "160 kbps", + "192 kbps", + "224 kbps", + "256 kbps", + "320 kbps", + "384 kbps", + "448 kbps", + "512 kbps", + "576 kbps", + "640 kbps", + NULL + }; static const char *mpeg_audio_mode[] = { "Stereo", "Joint Stereo", @@ -272,6 +296,7 @@ const char **v4l2_ctrl_get_menu(u32 id) static const char *mpeg_video_encoding[] = { "MPEG-1", "MPEG-2", + "MPEG-4 AVC", NULL }; static const char *mpeg_video_aspect[] = { @@ -312,6 +337,8 @@ const char **v4l2_ctrl_get_menu(u32 id) return mpeg_audio_l2_bitrate; case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return mpeg_audio_l3_bitrate; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + return mpeg_audio_ac3_bitrate; case V4L2_CID_MPEG_AUDIO_MODE: return mpeg_audio_mode; case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: @@ -336,62 +363,73 @@ const char **v4l2_ctrl_get_menu(u32 id) } EXPORT_SYMBOL(v4l2_ctrl_get_menu); -/* Fill in a struct v4l2_queryctrl */ -int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def) +/* Return the control name. */ +const char *v4l2_ctrl_get_name(u32 id) { - const char *name; - - qctrl->flags = 0; - switch (qctrl->id) { + switch (id) { /* USER controls */ - case V4L2_CID_USER_CLASS: name = "User Controls"; break; - case V4L2_CID_AUDIO_VOLUME: name = "Volume"; break; - case V4L2_CID_AUDIO_MUTE: name = "Mute"; break; - case V4L2_CID_AUDIO_BALANCE: name = "Balance"; break; - case V4L2_CID_AUDIO_BASS: name = "Bass"; break; - case V4L2_CID_AUDIO_TREBLE: name = "Treble"; break; - case V4L2_CID_AUDIO_LOUDNESS: name = "Loudness"; break; - case V4L2_CID_BRIGHTNESS: name = "Brightness"; break; - case V4L2_CID_CONTRAST: name = "Contrast"; break; - case V4L2_CID_SATURATION: name = "Saturation"; break; - case V4L2_CID_HUE: name = "Hue"; break; + case V4L2_CID_USER_CLASS: return "User Controls"; + case V4L2_CID_AUDIO_VOLUME: return "Volume"; + case V4L2_CID_AUDIO_MUTE: return "Mute"; + case V4L2_CID_AUDIO_BALANCE: return "Balance"; + case V4L2_CID_AUDIO_BASS: return "Bass"; + case V4L2_CID_AUDIO_TREBLE: return "Treble"; + case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; + case V4L2_CID_BRIGHTNESS: return "Brightness"; + case V4L2_CID_CONTRAST: return "Contrast"; + case V4L2_CID_SATURATION: return "Saturation"; + case V4L2_CID_HUE: return "Hue"; /* MPEG controls */ - case V4L2_CID_MPEG_CLASS: name = "MPEG Encoder Controls"; break; - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: name = "Audio Sampling Frequency"; break; - case V4L2_CID_MPEG_AUDIO_ENCODING: name = "Audio Encoding Layer"; break; - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: name = "Audio Layer I Bitrate"; break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: name = "Audio Layer II Bitrate"; break; - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: name = "Audio Layer III Bitrate"; break; - case V4L2_CID_MPEG_AUDIO_MODE: name = "Audio Stereo Mode"; break; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: name = "Audio Stereo Mode Extension"; break; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: name = "Audio Emphasis"; break; - case V4L2_CID_MPEG_AUDIO_CRC: name = "Audio CRC"; break; - case V4L2_CID_MPEG_AUDIO_MUTE: name = "Audio Mute"; break; - case V4L2_CID_MPEG_VIDEO_ENCODING: name = "Video Encoding"; break; - case V4L2_CID_MPEG_VIDEO_ASPECT: name = "Video Aspect"; break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: name = "Video B Frames"; break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: name = "Video GOP Size"; break; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: name = "Video GOP Closure"; break; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: name = "Video Pulldown"; break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: name = "Video Bitrate Mode"; break; - case V4L2_CID_MPEG_VIDEO_BITRATE: name = "Video Bitrate"; break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: name = "Video Peak Bitrate"; break; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: name = "Video Temporal Decimation"; break; - case V4L2_CID_MPEG_VIDEO_MUTE: name = "Video Mute"; break; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: name = "Video Mute YUV"; break; - case V4L2_CID_MPEG_STREAM_TYPE: name = "Stream Type"; break; - case V4L2_CID_MPEG_STREAM_PID_PMT: name = "Stream PMT Program ID"; break; - case V4L2_CID_MPEG_STREAM_PID_AUDIO: name = "Stream Audio Program ID"; break; - case V4L2_CID_MPEG_STREAM_PID_VIDEO: name = "Stream Video Program ID"; break; - case V4L2_CID_MPEG_STREAM_PID_PCR: name = "Stream PCR Program ID"; break; - case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: name = "Stream PES Audio ID"; break; - case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: name = "Stream PES Video ID"; break; - case V4L2_CID_MPEG_STREAM_VBI_FMT: name = "Stream VBI Format"; break; + case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency"; + case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding"; + case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate"; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate"; + case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate"; + case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate"; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate"; + case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode"; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension"; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis"; + case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC"; + case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute"; + case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding"; + case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect"; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames"; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size"; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure"; + case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown"; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode"; + case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate"; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate"; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation"; + case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute"; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV"; + case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type"; + case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID"; + case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID"; + case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID"; + case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID"; + case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID"; + case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID"; + case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format"; default: - return -EINVAL; + return NULL; } +} +EXPORT_SYMBOL(v4l2_ctrl_get_name); + +/* Fill in a struct v4l2_queryctrl */ +int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def) +{ + const char *name = v4l2_ctrl_get_name(qctrl->id); + + qctrl->flags = 0; + if (name == NULL) + return -EINVAL; + switch (qctrl->id) { case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_LOUDNESS: @@ -408,6 +446,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_AUDIO_L1_BITRATE: case V4L2_CID_MPEG_AUDIO_L2_BITRATE: case V4L2_CID_MPEG_AUDIO_L3_BITRATE: + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: case V4L2_CID_MPEG_AUDIO_MODE: case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: case V4L2_CID_MPEG_AUDIO_EMPHASIS: @@ -494,7 +533,7 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) case V4L2_CID_MPEG_AUDIO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_ENCODING_LAYER_1, - V4L2_MPEG_AUDIO_ENCODING_LAYER_3, 1, + V4L2_MPEG_AUDIO_ENCODING_AC3, 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return v4l2_ctrl_query_fill(qctrl, @@ -511,6 +550,13 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) V4L2_MPEG_AUDIO_L3_BITRATE_32K, V4L2_MPEG_AUDIO_L3_BITRATE_320K, 1, V4L2_MPEG_AUDIO_L3_BITRATE_192K); + case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 6400, 1, 3200000); + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_32K, + V4L2_MPEG_AUDIO_AC3_BITRATE_640K, 1, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K); case V4L2_CID_MPEG_AUDIO_MODE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_MODE_STEREO, @@ -536,7 +582,7 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) case V4L2_CID_MPEG_VIDEO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, V4L2_MPEG_VIDEO_ENCODING_MPEG_2); case V4L2_CID_MPEG_VIDEO_ASPECT: return v4l2_ctrl_query_fill(qctrl, @@ -595,12 +641,17 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) EXPORT_SYMBOL(v4l2_ctrl_query_fill_std); /* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and - the menu. The qctrl pointer may be NULL, in which case it is ignored. */ + the menu. The qctrl pointer may be NULL, in which case it is ignored. + If menu_items is NULL, then the menu items are retrieved using + v4l2_ctrl_get_menu. */ int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl, const char **menu_items) { int i; + qmenu->reserved = 0; + if (menu_items == NULL) + menu_items = v4l2_ctrl_get_menu(qmenu->id); if (menu_items == NULL || (qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum))) return -EINVAL; @@ -608,11 +659,31 @@ int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qc if (menu_items[i] == NULL || menu_items[i][0] == '\0') return -EINVAL; snprintf(qmenu->name, sizeof(qmenu->name), menu_items[qmenu->index]); - qmenu->reserved = 0; return 0; } EXPORT_SYMBOL(v4l2_ctrl_query_menu); +/* Fill in a struct v4l2_querymenu based on the specified array of valid + menu items (terminated by V4L2_CTRL_MENU_IDS_END). + Use this if there are 'holes' in the list of valid menu items. */ +int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids) +{ + const char **menu_items = v4l2_ctrl_get_menu(qmenu->id); + + qmenu->reserved = 0; + if (menu_items == NULL || ids == NULL) + return -EINVAL; + while (*ids != V4L2_CTRL_MENU_IDS_END) { + if (*ids++ == qmenu->index) { + snprintf(qmenu->name, sizeof(qmenu->name), + menu_items[qmenu->index]); + return 0; + } + } + return -EINVAL; +} +EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items); + /* ctrl_classes points to an array of u32 pointers, the last element is a NULL pointer. Each u32 array is a 0-terminated array of control IDs. Each array must be sorted low to high and belong to the same control diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index 398376a40..a2daea31c 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -44,6 +44,7 @@ static ssize_t show_index(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%i\n", vfd->index); } @@ -51,6 +52,7 @@ static ssize_t show_name(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); } @@ -63,12 +65,14 @@ static struct device_attribute video_device_attrs[] = { static ssize_t show_index(struct class_device *cd, char *buf) { struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%i\n", vfd->index); } static ssize_t show_name(struct class_device *cd, char *buf) { struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); } @@ -76,12 +80,15 @@ static CLASS_DEVICE_ATTR(index, S_IRUGO, show_index, NULL); static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL); #endif +/* + * Active devices + */ +static struct video_device *video_device[VIDEO_NUM_DEVICES]; +static DEFINE_MUTEX(videodev_lock); + struct video_device *video_device_alloc(void) { - struct video_device *vfd; - - vfd = kzalloc(sizeof(*vfd), GFP_KERNEL); - return vfd; + return kzalloc(sizeof(struct video_device), GFP_KERNEL); } EXPORT_SYMBOL(video_device_alloc); @@ -91,6 +98,42 @@ void video_device_release(struct video_device *vfd) } EXPORT_SYMBOL(video_device_release); +void video_device_release_empty(struct video_device *vfd) +{ + /* Do nothing */ + /* Only valid when the video_device struct is a static. */ +} +EXPORT_SYMBOL(video_device_release_empty); + +/* Called when the last user of the character device is gone. */ +static void v4l2_chardev_release(struct kobject *kobj) +{ + struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj); + + mutex_lock(&videodev_lock); + if (video_device[vfd->minor] != vfd) { + mutex_unlock(&videodev_lock); + BUG(); + return; + } + + /* Free up this device for reuse */ + video_device[vfd->minor] = NULL; + mutex_unlock(&videodev_lock); + + /* Release the character device */ + vfd->cdev_release(kobj); + /* Release video_device and perform other + cleanups as needed. */ + if (vfd->release) + vfd->release(vfd); +} + +/* The new kobj_type for the character device */ +static struct kobj_type v4l2_ktype_cdev_default = { + .release = v4l2_chardev_release, +}; + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) static void video_release(struct class_device *cd) #else @@ -99,12 +142,11 @@ static void video_release(struct device *cd) { struct video_device *vfd = container_of(cd, struct video_device, dev); -#if 1 /* keep */ - /* needed until all drivers are fixed */ - if (!vfd->release) - return; -#endif - vfd->release(vfd); + /* It's now safe to delete the char device. + This will either trigger the v4l2_chardev_release immediately (if + the refcount goes to 0) or later when the last user of the + character device closes it. */ + cdev_del(&vfd->cdev); } static struct class video_class = { @@ -117,13 +159,6 @@ static struct class video_class = { #endif }; -/* - * Active devices - */ - -static struct video_device *video_device[VIDEO_NUM_DEVICES]; -static DEFINE_MUTEX(videodev_lock); - struct video_device *video_devdata(struct file *file) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) @@ -134,79 +169,6 @@ struct video_device *video_devdata(struct file *file) } EXPORT_SYMBOL(video_devdata); -/* - * Open a video device - FIXME: Obsoleted - */ -static int video_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - int err = 0; - struct video_device *vfl; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - const struct file_operations *old_fops; -#else - struct file_operations *old_fops; -#endif - - if (minor >= VIDEO_NUM_DEVICES) - return -ENODEV; - mutex_lock(&videodev_lock); - vfl = video_device[minor]; - if (vfl == NULL) { - mutex_unlock(&videodev_lock); - request_module("char-major-%d-%d", VIDEO_MAJOR, minor); - mutex_lock(&videodev_lock); - vfl = video_device[minor]; - if (vfl == NULL) { - mutex_unlock(&videodev_lock); - return -ENODEV; - } - } - old_fops = file->f_op; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - file->f_op = fops_get(vfl->fops); -#else - file->f_op = (struct file_operations *)fops_get(vfl->fops); -#endif - if (file->f_op->open) - err = file->f_op->open(inode, file); - if (err) { - fops_put(file->f_op); - file->f_op = fops_get(old_fops); - } - fops_put(old_fops); - mutex_unlock(&videodev_lock); - return err; -} - -/* - * open/release helper functions -- handle exclusive opens - * Should be removed soon - */ -int video_exclusive_open(struct inode *inode, struct file *file) -{ - struct video_device *vfl = video_devdata(file); - int retval = 0; - - mutex_lock(&vfl->lock); - if (vfl->users) - retval = -EBUSY; - else - vfl->users++; - mutex_unlock(&vfl->lock); - return retval; -} -EXPORT_SYMBOL(video_exclusive_open); - -int video_exclusive_release(struct inode *inode, struct file *file) -{ - struct video_device *vfl = video_devdata(file); - - vfl->users--; - return 0; -} -EXPORT_SYMBOL(video_exclusive_release); - /** * get_index - assign stream number based on parent device * @vdev: video_device to assign index number to, vdev->dev should be assigned @@ -291,6 +253,13 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, int end; int ret; char *name_base; + void *priv = video_get_drvdata(vfd); + + /* the release callback MUST be present */ + BUG_ON(!vfd->release); + + if (vfd == NULL) + return -EINVAL; switch (type) { case VFL_TYPE_GRABBER: @@ -316,12 +285,19 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, default: printk(KERN_ERR "%s called with unknown type: %d\n", __func__, type); - return -1; + return -EINVAL; } + /* Initialize the character device */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) + cdev_init(&vfd->cdev, vfd->fops); +#else + cdev_init(&vfd->cdev, (struct file_operations *)vfd->fops); +#endif + vfd->cdev.owner = vfd->fops->owner; /* pick a minor number */ mutex_lock(&videodev_lock); - if (nr >= 0 && nr < end-base) { + if (nr >= 0 && nr < end-base) { /* use the one the driver asked for */ i = base + nr; if (NULL != video_device[i]) { @@ -352,14 +328,18 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, goto fail_minor; } - mutex_init(&vfd->lock); - + ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1); + if (ret < 0) { + printk(KERN_ERR "%s: cdev_add failed\n", __func__); + goto fail_minor; + } /* sysfs class */ - memset(&vfd->dev, 0x00, sizeof(vfd->dev)); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) + memset(&vfd->dev, 0, sizeof(vfd->dev)); + /* The memset above cleared the device's drvdata, so + put back the copy we made earlier. */ + video_set_drvdata(vfd, priv); vfd->dev.class = &video_class; vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); -#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) if (vfd->parent) vfd->dev.dev = vfd->parent; @@ -373,41 +353,38 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, #endif if (ret < 0) { printk(KERN_ERR "%s: device_register failed\n", __func__); - goto fail_minor; + goto del_cdev; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - ret = class_device_create_file(&vfd->dev, - &class_device_attr_name); + ret = class_device_create_file(&vfd->dev, &class_device_attr_name); if (ret < 0) { printk(KERN_ERR "%s: class_device_create_file 'name' failed\n", __func__); class_device_unregister(&vfd->dev); - goto fail_minor; + goto del_cdev; } - ret = class_device_create_file(&vfd->dev, - &class_device_attr_index); + ret = class_device_create_file(&vfd->dev, &class_device_attr_index); if (ret < 0) { printk(KERN_ERR "%s: class_device_create_file 'index' failed\n", __func__); class_device_unregister(&vfd->dev); - goto fail_minor; + goto del_cdev; } #endif - -#if 1 /* keep */ - /* needed until all drivers are fixed */ - if (!vfd->release) - printk(KERN_WARNING "videodev: \"%s\" has no release callback. " - "Please fix your driver for proper sysfs support, see " - "http://lwn.net/Articles/36850/\n", vfd->name); -#endif + /* Remember the cdev's release function */ + vfd->cdev_release = vfd->cdev.kobj.ktype->release; + /* Install our own */ + vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default; return 0; +del_cdev: + cdev_del(&vfd->cdev); + fail_minor: mutex_lock(&videodev_lock); video_device[vfd->minor] = NULL; - vfd->minor = -1; mutex_unlock(&videodev_lock); + vfd->minor = -1; return ret; } EXPORT_SYMBOL(video_register_device_index); @@ -422,51 +399,33 @@ EXPORT_SYMBOL(video_register_device_index); void video_unregister_device(struct video_device *vfd) { - mutex_lock(&videodev_lock); - if (video_device[vfd->minor] != vfd) - panic("videodev: bad unregister"); - - video_device[vfd->minor] = NULL; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) class_device_unregister(&vfd->dev); #else device_unregister(&vfd->dev); #endif - mutex_unlock(&videodev_lock); } EXPORT_SYMBOL(video_unregister_device); /* - * Video fs operations - */ -static const struct file_operations video_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = video_open, -}; - -/* * Initialise video for linux */ - static int __init videodev_init(void) { + dev_t dev = MKDEV(VIDEO_MAJOR, 0); int ret; printk(KERN_INFO "Linux video capture interface: v2.00\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) { -#else - if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, - (struct file_operations *)&video_fops)) { -#endif - printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR); - return -EIO; + ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME); + if (ret < 0) { + printk(KERN_WARNING "videodev: unable to get major %d\n", + VIDEO_MAJOR); + return ret; } ret = class_register(&video_class); if (ret < 0) { - unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); + unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); printk(KERN_WARNING "video_dev: class_register failed\n"); return -EIO; } @@ -476,8 +435,10 @@ static int __init videodev_init(void) static void __exit videodev_exit(void) { + dev_t dev = MKDEV(VIDEO_MAJOR, 0); + class_unregister(&video_class); - unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); + unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); } module_init(videodev_init) diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c index a56e3643b..51569f888 100644 --- a/linux/drivers/media/video/v4l2-ioctl.c +++ b/linux/drivers/media/video/v4l2-ioctl.c @@ -500,7 +500,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd, p->timestamp.tv_sec / 3600, (int)(p->timestamp.tv_sec / 60) % 60, (int)(p->timestamp.tv_sec % 60), - p->timestamp.tv_usec, + (long)p->timestamp.tv_usec, p->index, prt_names(p->type, v4l2_type_names), p->bytesused, p->flags, @@ -675,7 +675,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, __video_do_ioctl will be called again, with one or more V4L2 ioctls. ********************************************************/ - if (_IOC_TYPE(cmd) == 'v') + if (_IOC_TYPE(cmd) == 'v' && _IOC_NR(cmd) < BASE_VIDIOCPRIVATE) return v4l_compat_translate_ioctl(inode, file, cmd, arg, __video_do_ioctl); #endif diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c index 9ada3fff2..08eb57818 100644 --- a/linux/drivers/media/video/vino.c +++ b/linux/drivers/media/video/vino.c @@ -809,7 +809,7 @@ static void vino_free_buffer_with_count(struct vino_framebuffer *fb, dprintk("vino_free_buffer_with_count(): count = %d\n", count); for (i = 0; i < count; i++) { - ClearPageReserved(virt_to_page(fb->desc_table.virtual[i])); + ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i])); dma_unmap_single(NULL, fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i], PAGE_SIZE, DMA_FROM_DEVICE); @@ -887,7 +887,7 @@ static int vino_allocate_buffer(struct vino_framebuffer *fb, dma_data_addr + VINO_PAGE_SIZE * j; } - SetPageReserved(virt_to_page(fb->desc_table.virtual[i])); + SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i])); } /* page_count needs to be set anyway, because the descriptor table has @@ -974,7 +974,7 @@ static int vino_prepare_user_buffer(struct vino_framebuffer *fb, dma_data_addr + VINO_PAGE_SIZE * j; } - SetPageReserved(virt_to_page(fb->desc_table.virtual[i])); + SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i])); } /* page_count needs to be set anyway, because the descriptor table has @@ -4028,8 +4028,7 @@ out: static int vino_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); int ret = 0; dprintk("open(): channel = %c\n", (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B'); @@ -4060,8 +4059,7 @@ static int vino_open(struct inode *inode, struct file *file) static int vino_close(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); dprintk("close():\n"); mutex_lock(&vcs->mutex); @@ -4104,8 +4102,7 @@ static struct vm_operations_struct vino_vm_ops = { static int vino_mmap(struct file *file, struct vm_area_struct *vma) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; @@ -4210,8 +4207,7 @@ out: static unsigned int vino_poll(struct file *file, poll_table *pt) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); unsigned int outgoing; unsigned int ret = 0; @@ -4251,8 +4247,7 @@ error: static int vino_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); #ifdef VINO_DEBUG switch (_IOC_TYPE(cmd)) { @@ -4359,8 +4354,7 @@ static int vino_do_ioctl(struct inode *inode, struct file *file, static int vino_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); int ret; if (mutex_lock_interruptible(&vcs->mutex)) diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 2f18381b6..f6a12b184 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -1029,13 +1029,13 @@ static int vivi_release(void) dev = list_entry(list, struct vivi_dev, vivi_devlist); if (-1 != dev->vfd->minor) { - video_unregister_device(dev->vfd); - printk(KERN_INFO "%s: /dev/video%d unregistered.\n", + printk(KERN_INFO "%s: unregistering /dev/video%d\n", VIVI_MODULE_NAME, dev->vfd->minor); + video_unregister_device(dev->vfd); } else { - video_device_release(dev->vfd); - printk(KERN_INFO "%s: /dev/video%d released.\n", + printk(KERN_INFO "%s: releasing /dev/video%d\n", VIVI_MODULE_NAME, dev->vfd->minor); + video_device_release(dev->vfd); } kfree(dev); @@ -1112,19 +1112,29 @@ static struct video_device vivi_template = { Initialization and module stuff ------------------------------------------------------------------*/ +/* This routine allocates from 1 to n_devs virtual drivers. + + The real maximum number of virtual drivers will depend on how many drivers + will succeed. This is limited to the maximum number of devices that + videodev supports. Since there are 64 minors for video grabbers, this is + currently the theoretical maximum limit. However, a further limit does + exist at videodev that forbids any driver to register more than 32 video + grabbers. + */ static int __init vivi_init(void) { int ret = -ENOMEM, i; struct vivi_dev *dev; struct video_device *vfd; + if (n_devs <= 0) + n_devs = 1; + for (i = 0; i < n_devs; i++) { dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (NULL == dev) + if (!dev) break; - list_add_tail(&dev->vivi_devlist, &vivi_devlist); - /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); init_waitqueue_head(&dev->vidq.wq); @@ -1134,14 +1144,27 @@ static int __init vivi_init(void) mutex_init(&dev->mutex); vfd = video_device_alloc(); - if (NULL == vfd) + if (!vfd) { + kfree(dev); break; + } *vfd = vivi_template; ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); - if (ret < 0) + if (ret < 0) { + video_device_release(vfd); + kfree(dev); + + /* If some registers succeeded, keep driver */ + if (i) + ret = 0; + break; + } + + /* Now that everything is fine, let's add it to device list */ + list_add_tail(&dev->vivi_devlist, &vivi_devlist); snprintf(vfd->name, sizeof(vfd->name), "%s (%i)", vivi_template.name, vfd->minor); @@ -1157,11 +1180,16 @@ static int __init vivi_init(void) if (ret < 0) { vivi_release(); printk(KERN_INFO "Error %d while loading vivi driver\n", ret); - } else + } else { printk(KERN_INFO "Video Technology Magazine Virtual Video " "Capture Board ver %u.%u.%u successfully loaded.\n", (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF, VIVI_VERSION & 0xFF); + + /* n_devs will reflect the actual number of allocated devices */ + n_devs = i; + } + return ret; } @@ -1177,10 +1205,10 @@ MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board"); MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol"); MODULE_LICENSE("Dual BSD/GPL"); -module_param(video_nr, int, 0); +module_param(video_nr, uint, 0444); MODULE_PARM_DESC(video_nr, "video iminor start number"); -module_param(n_devs, int, 0); +module_param(n_devs, uint, 0444); MODULE_PARM_DESC(n_devs, "number of video devices to create"); module_param_named(debug, vivi_template.debug, int, 0444); diff --git a/linux/drivers/media/video/vpx3220.c b/linux/drivers/media/video/vpx3220.c index 989166eec..896af1a12 100644 --- a/linux/drivers/media/video/vpx3220.c +++ b/linux/drivers/media/video/vpx3220.c @@ -24,8 +24,6 @@ #include <linux/types.h> #include <linux/slab.h> -#include <linux/byteorder/swab.h> - #include <asm/io.h> #include <asm/uaccess.h> diff --git a/linux/drivers/media/video/w9966.c b/linux/drivers/media/video/w9966.c index 6aefc6566..f3f28c232 100644 --- a/linux/drivers/media/video/w9966.c +++ b/linux/drivers/media/video/w9966.c @@ -114,6 +114,7 @@ struct w9966_dev { signed char contrast; signed char color; signed char hue; + unsigned long in_use; }; /* @@ -185,10 +186,25 @@ static int w9966_v4l_ioctl(struct inode *inode, struct file *file, static ssize_t w9966_v4l_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); +static int w9966_exclusive_open(struct inode *inode, struct file *file) +{ + struct w9966_dev *cam = video_drvdata(file); + + return test_and_set_bit(0, &cam->in_use) ? -EBUSY : 0; +} + +static int w9966_exclusive_release(struct inode *inode, struct file *file) +{ + struct w9966_dev *cam = video_drvdata(file); + + clear_bit(0, &cam->in_use); + return 0; +} + static const struct file_operations w9966_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = w9966_exclusive_open, + .release = w9966_exclusive_release, .ioctl = w9966_v4l_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -199,6 +215,7 @@ static const struct file_operations w9966_fops = { static struct video_device w9966_template = { .name = W9966_DRIVERNAME, .fops = &w9966_fops, + .release = video_device_release_empty, }; /* @@ -333,9 +350,9 @@ static int w9966_init(struct w9966_dev* cam, struct parport* port) // Fill in the video_device struct and register us to v4l memcpy(&cam->vdev, &w9966_template, sizeof(struct video_device)); - cam->vdev.priv = cam; + video_set_drvdata(&cam->vdev, cam); - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) return -1; w9966_setState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); @@ -714,8 +731,7 @@ static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data) static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct video_device *vdev = video_devdata(file); - struct w9966_dev *cam = vdev->priv; + struct w9966_dev *cam = video_drvdata(file); switch(cmd) { @@ -873,8 +889,7 @@ static int w9966_v4l_ioctl(struct inode *inode, struct file *file, static ssize_t w9966_v4l_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct video_device *vdev = video_devdata(file); - struct w9966_dev *cam = vdev->priv; + struct w9966_dev *cam = video_drvdata(file); unsigned char addr = 0xa0; // ECP, read, CCD-transfer, 00000 unsigned char __user *dest = (unsigned char __user *)buf; unsigned long dleft = count; diff --git a/linux/drivers/media/video/w9968cf.c b/linux/drivers/media/video/w9968cf.c index 666438e8d..4e7a8400b 100644 --- a/linux/drivers/media/video/w9968cf.c +++ b/linux/drivers/media/video/w9968cf.c @@ -919,7 +919,6 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam) for (i = 0; i < W9968CF_URBS; i++) { urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; if (!urb) { for (j = 0; j < i; j++) usb_free_urb(cam->urb[j]); @@ -927,6 +926,7 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam) return -ENOMEM; } + cam->urb[i] = urb; urb->dev = udev; urb->context = (void*)cam; urb->pipe = usb_rcvisocpipe(udev, 1); diff --git a/linux/drivers/media/video/wm8739.c b/linux/drivers/media/video/wm8739.c index 3ad19cf13..f9d13b89a 100644 --- a/linux/drivers/media/video/wm8739.c +++ b/linux/drivers/media/video/wm8739.c @@ -280,10 +280,8 @@ static int wm8739_probe(struct i2c_client *client, client->addr << 1, client->adapter->name); state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); - if (state == NULL) { - kfree(client); + if (state == NULL) return -ENOMEM; - } state->vol_l = 0x17; /* 0dB */ state->vol_r = 0x17; /* 0dB */ state->muted = 0; diff --git a/linux/drivers/media/video/zc0301/zc0301_core.c b/linux/drivers/media/video/zc0301/zc0301_core.c index e59145f65..0be0f095e 100644 --- a/linux/drivers/media/video/zc0301/zc0301_core.c +++ b/linux/drivers/media/video/zc0301/zc0301_core.c @@ -661,7 +661,7 @@ static int zc0301_open(struct inode* inode, struct file* filp) if (!down_read_trylock(&zc0301_dev_lock)) return -EAGAIN; - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); if (wait_for_completion_interruptible(&cam->probe)) { up_read(&zc0301_dev_lock); @@ -743,7 +743,7 @@ static int zc0301_release(struct inode* inode, struct file* filp) down_write(&zc0301_dev_lock); - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); zc0301_stop_transfer(cam); zc0301_release_buffers(cam); @@ -763,7 +763,7 @@ static int zc0301_release(struct inode* inode, struct file* filp) static ssize_t zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); struct zc0301_frame_t* f, * i; unsigned long lock_flags; long timeout; @@ -870,7 +870,7 @@ exit: static unsigned int zc0301_poll(struct file *filp, poll_table *wait) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); struct zc0301_frame_t* f; unsigned long lock_flags; unsigned int mask = 0; @@ -945,7 +945,7 @@ static struct vm_operations_struct zc0301_vm_ops = { static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; void *pos; @@ -1800,7 +1800,7 @@ zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); switch (cmd) { @@ -1895,7 +1895,7 @@ static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, static int zc0301_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); int err = 0; if (mutex_lock_interruptible(&cam->fileop_mutex)) @@ -1992,6 +1992,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->fops = &zc0301_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; + cam->v4ldev->parent = &udev->dev; video_set_drvdata(cam->v4ldev, cam); init_completion(&cam->probe); diff --git a/linux/drivers/media/video/zc0301/zc0301_sensor.h b/linux/drivers/media/video/zc0301/zc0301_sensor.h index 8e8276f27..4dc95b5c2 100644 --- a/linux/drivers/media/video/zc0301/zc0301_sensor.h +++ b/linux/drivers/media/video/zc0301/zc0301_sensor.h @@ -61,27 +61,8 @@ zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); #define ZC0301_ID_TABLE \ static const struct usb_device_id zc0301_id_table[] = { \ - { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, /* ICM105 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x401c, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x401f, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x4022, 0xff), }, \ - { ZC0301_USB_DEVICE(0x041e, 0x4034, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x4035, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x4036, 0xff), }, /* HV7131 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x403a, 0xff), }, /* HV7131 */ \ - { ZC0301_USB_DEVICE(0x0458, 0x7007, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x0458, 0x700c, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x0458, 0x700f, 0xff), }, /* TAS5130 */ \ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ - { ZC0301_USB_DEVICE(0x055f, 0xd003, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x055f, 0xd004, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \ - { ZC0301_USB_DEVICE(0x0ac8, 0x301b, 0xff), }, /* PB-0330/HV7131 */ \ { ZC0301_USB_DEVICE(0x0ac8, 0x303b, 0xff), }, /* PB-0330 */ \ - { ZC0301_USB_DEVICE(0x10fd, 0x0128, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x10fd, 0x804e, 0xff), }, /* TAS5130 */ \ { } \ }; diff --git a/linux/drivers/media/video/zoran_card.c b/linux/drivers/media/video/zoran_card.c index 6678c6de9..081b72a45 100644 --- a/linux/drivers/media/video/zoran_card.c +++ b/linux/drivers/media/video/zoran_card.c @@ -989,7 +989,7 @@ zoran_open_init_params (struct zoran *zr) zr->v4l_grab_seq = 0; zr->v4l_settings.width = 192; zr->v4l_settings.height = 144; - zr->v4l_settings.format = &zoran_formats[4]; /* YUY2 - YUV-4:2:2 packed */ + zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */ zr->v4l_settings.bytesperline = zr->v4l_settings.width * ((zr->v4l_settings.format->depth + 7) / 8); diff --git a/linux/drivers/media/video/zoran_device.c b/linux/drivers/media/video/zoran_device.c index b0a014d09..87f894fa2 100644 --- a/linux/drivers/media/video/zoran_device.c +++ b/linux/drivers/media/video/zoran_device.c @@ -59,8 +59,6 @@ ZR36057_ISR_GIRQ1 | \ ZR36057_ISR_JPEGRepIRQ ) -extern const struct zoran_format zoran_formats[]; - static int lml33dpath; /* default = 0 * 1 will use digital path in capture * mode instead of analog. It can be @@ -378,7 +376,7 @@ zr36057_set_vfe (struct zoran *zr, /* horizontal */ VidWinWid = video_width; - X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa; + X = DIV_ROUND_UP(VidWinWid * 64, tvn->Wa); We = (VidWinWid * 64) / X; HorDcm = 64 - X; hcrop1 = 2 * ((tvn->Wa - We) / 4); @@ -404,7 +402,7 @@ zr36057_set_vfe (struct zoran *zr, /* Vertical */ DispMode = !(video_height > BUZ_MAX_HEIGHT / 2); VidWinHt = DispMode ? video_height : video_height / 2; - Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha; + Y = DIV_ROUND_UP(VidWinHt * 64 * 2, tvn->Ha); He = (VidWinHt * 64) / Y; VerDcm = 64 - Y; vcrop1 = (tvn->Ha / 2 - He) / 2; diff --git a/linux/drivers/media/video/zoran_device.h b/linux/drivers/media/video/zoran_device.h index c46d1b076..d4e0b9b5a 100644 --- a/linux/drivers/media/video/zoran_device.h +++ b/linux/drivers/media/video/zoran_device.h @@ -84,6 +84,14 @@ extern void zoran_set_pci_master(struct zoran *zr, extern void zoran_init_hardware(struct zoran *zr); extern void zr36057_restart(struct zoran *zr); +extern const struct zoran_format zoran_formats[]; + +extern int v4l_nbufs; +extern int v4l_bufsize; +extern int jpg_nbufs; +extern int jpg_bufsize; +extern int pass_through; + /* i2c */ extern int decoder_command(struct zoran *zr, int cmd, diff --git a/linux/drivers/media/video/zoran_driver.c b/linux/drivers/media/video/zoran_driver.c index 78e8430f4..9d57a630c 100644 --- a/linux/drivers/media/video/zoran_driver.c +++ b/linux/drivers/media/video/zoran_driver.c @@ -135,7 +135,7 @@ const struct zoran_format zoran_formats[] = { }, { .name = "16-bit RGB BE", ZFMT(-1, - V4L2_PIX_FMT_RGB565, V4L2_COLORSPACE_SRGB), + V4L2_PIX_FMT_RGB565X, V4L2_COLORSPACE_SRGB), .depth = 16, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, @@ -202,12 +202,6 @@ const struct zoran_format zoran_formats[] = { # include <linux/bigphysarea.h> #endif -extern int v4l_nbufs; -extern int v4l_bufsize; -extern int jpg_nbufs; -extern int jpg_bufsize; -extern int pass_through; - static int lock_norm; /* 0 = default 1 = Don't change TV standard (norm) */ module_param(lock_norm, int, 0644); MODULE_PARM_DESC(lock_norm, "Prevent norm changes (1 = ignore, >1 = fail)"); @@ -2805,7 +2799,8 @@ zoran_do_ioctl (struct inode *inode, fh->v4l_settings.format->fourcc; fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace; - fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.bytesperline = + fh->v4l_settings.bytesperline; if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) fmt->fmt.pix.field = @@ -2901,13 +2896,6 @@ zoran_do_ioctl (struct inode *inode, fmt->fmt.pix.pixelformat, (char *) &printformat); - if (fmt->fmt.pix.bytesperline > 0) { - dprintk(5, - KERN_ERR "%s: bpl not supported\n", - ZR_DEVNAME(zr)); - return -EINVAL; - } - /* we can be requested to do JPEG/raw playback/capture */ if (! (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE || @@ -2991,8 +2979,11 @@ zoran_do_ioctl (struct inode *inode, fh->jpg_buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh-> jpg_settings); + fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.sizeimage = fh->jpg_buffers.buffer_size; + fmt->fmt.pix.colorspace = + V4L2_COLORSPACE_SMPTE170M; /* we hereby abuse this variable to show that * we're gonna do mjpeg capture */ @@ -3047,9 +3038,13 @@ zoran_do_ioctl (struct inode *inode, /* tell the user the * results/missing stuff */ + fmt->fmt.pix.bytesperline = + fh->v4l_settings.bytesperline; fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline; + fmt->fmt.pix.colorspace = + fh->v4l_settings.format->colorspace; if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) fmt->fmt.pix.field = diff --git a/linux/drivers/media/video/zoran_procfs.c b/linux/drivers/media/video/zoran_procfs.c index d7bc097da..961950a21 100644 --- a/linux/drivers/media/video/zoran_procfs.c +++ b/linux/drivers/media/video/zoran_procfs.c @@ -162,10 +162,11 @@ static ssize_t zoran_write(struct file *file, const char __user *buffer, return -EFAULT; } string[count] = 0; - dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n", #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n", ZR_DEVNAME(zr), file->f_dentry->d_name.name, count, zr); #else + dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n", ZR_DEVNAME(zr), file->f_path.dentry->d_name.name, count, zr); #endif ldelim = " \t\n"; diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c index dbfdd6966..a2a632534 100644 --- a/linux/drivers/media/video/zr364xx.c +++ b/linux/drivers/media/video/zr364xx.c @@ -117,6 +117,7 @@ struct zr364xx_camera { int height; int method; struct mutex lock; + int users; }; @@ -642,25 +643,22 @@ static int zr364xx_open(struct inode *inode, struct file *file) DBG("zr364xx_open"); - cam->skip = 2; + mutex_lock(&cam->lock); - lock_kernel(); - err = video_exclusive_open(inode, file); - if (err < 0) { - unlock_kernel(); - return err; + if (cam->users) { + err = -EBUSY; + goto out; } if (!cam->framebuf) { cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES); if (!cam->framebuf) { info("vmalloc_32 failed!"); - unlock_kernel(); - return -ENOMEM; + err = -ENOMEM; + goto out; } } - mutex_lock(&cam->lock); for (i = 0; init[cam->method][i].size != -1; i++) { err = send_control_msg(udev, 1, init[cam->method][i].value, @@ -668,22 +666,23 @@ static int zr364xx_open(struct inode *inode, struct file *file) init[cam->method][i].size); if (err < 0) { info("error during open sequence: %d", i); - mutex_unlock(&cam->lock); - unlock_kernel(); - return err; + goto out; } } + cam->skip = 2; + cam->users++; file->private_data = vdev; /* Added some delay here, since opening/closing the camera quickly, * like Ekiga does during its startup, can crash the webcam */ mdelay(100); + err = 0; +out: mutex_unlock(&cam->lock); - unlock_kernel(); - return 0; + return err; } @@ -704,6 +703,10 @@ static int zr364xx_release(struct inode *inode, struct file *file) udev = cam->udev; mutex_lock(&cam->lock); + + cam->users--; + file->private_data = NULL; + for (i = 0; i < 2; i++) { err = send_control_msg(udev, 1, init[cam->method][i].value, @@ -711,21 +714,19 @@ static int zr364xx_release(struct inode *inode, struct file *file) init[cam->method][i].size); if (err < 0) { info("error during release sequence"); - mutex_unlock(&cam->lock); - return err; + goto out; } } - file->private_data = NULL; - video_exclusive_release(inode, file); - /* Added some delay here, since opening/closing the camera quickly, * like Ekiga does during its startup, can crash the webcam */ mdelay(100); + err = 0; +out: mutex_unlock(&cam->lock); - return 0; + return err; } diff --git a/linux/firmware/ihex2fw.c b/linux/firmware/ihex2fw.c index 660b191ed..8f7fdaa9e 100644 --- a/linux/firmware/ihex2fw.c +++ b/linux/firmware/ihex2fw.c @@ -250,19 +250,19 @@ static void file_record(struct ihex_binrec *record) static int output_records(int outfd) { - unsigned char zeroes[5] = {0, 0, 0, 0, 0}; + unsigned char zeroes[6] = {0, 0, 0, 0, 0, 0}; struct ihex_binrec *p = records; while (p) { uint16_t writelen = (p->len + 9) & ~3; p->addr = htonl(p->addr); - p->len = htonl(p->len); + p->len = htons(p->len); write(outfd, &p->addr, writelen); p = p->next; } /* EOF record is zero length, since we don't bother to represent the type field in the binary version */ - write(outfd, zeroes, 5); + write(outfd, zeroes, 6); return 0; } diff --git a/linux/include/asm-arm/arch-pxa/camera.h b/linux/include/asm-arm/arch-pxa/camera.h index 39516ced8..31abe6d51 100644 --- a/linux/include/asm-arm/arch-pxa/camera.h +++ b/linux/include/asm-arm/arch-pxa/camera.h @@ -36,8 +36,6 @@ struct pxacamera_platform_data { int (*init)(struct device *); - int (*power)(struct device *, int); - int (*reset)(struct device *, int); unsigned long flags; unsigned long mclk_10khz; diff --git a/linux/include/linux/i2c-id.h b/linux/include/linux/i2c-id.h index 4862398e0..493435bcd 100644 --- a/linux/include/linux/i2c-id.h +++ b/linux/include/linux/i2c-id.h @@ -39,10 +39,8 @@ #define I2C_DRIVERID_SAA7111A 8 /* video input processor */ #define I2C_DRIVERID_SAA7185B 13 /* video encoder */ #define I2C_DRIVERID_SAA7110 22 /* video decoder */ -#define I2C_DRIVERID_MGATVO 23 /* Matrox TVOut */ #define I2C_DRIVERID_SAA5249 24 /* SAA5249 and compatibles */ #define I2C_DRIVERID_PCF8583 25 /* real time clock */ -#define I2C_DRIVERID_SAB3036 26 /* SAB3036 tuner */ #define I2C_DRIVERID_TDA7432 27 /* Stereo sound processor */ #define I2C_DRIVERID_TVMIXER 28 /* Mixer driver for tv cards */ #define I2C_DRIVERID_TVAUDIO 29 /* Generic TV sound driver */ @@ -95,7 +93,6 @@ #define I2C_HW_B_BT848 0x010005 /* BT848 video boards */ #define I2C_HW_B_VIA 0x010007 /* Via vt82c586b */ #define I2C_HW_B_HYDRA 0x010008 /* Apple Hydra Mac I/O */ -#define I2C_HW_B_G400 0x010009 /* Matrox G400 */ #define I2C_HW_B_I810 0x01000a /* Intel I810 */ #define I2C_HW_B_VOO 0x01000b /* 3dfx Voodoo 3 / Banshee */ #define I2C_HW_B_SCX200 0x01000e /* Nat'l Semi SCx200 I2C */ diff --git a/linux/include/linux/ivtv.h b/linux/include/linux/ivtv.h index 794b8daa9..f2720280b 100644 --- a/linux/include/linux/ivtv.h +++ b/linux/include/linux/ivtv.h @@ -21,12 +21,9 @@ #ifndef __LINUX_IVTV_H__ #define __LINUX_IVTV_H__ -#ifdef __KERNEL__ -#include <linux/compiler.h> /* need __user */ -#else -#define __user -#endif +#include <linux/compiler.h> #include <linux/types.h> +#include <linux/videodev2.h> /* ivtv knows several distinct output modes: MPEG streaming, YUV streaming, YUV updates through user DMA and the passthrough diff --git a/linux/include/linux/ivtvfb.h b/linux/include/linux/ivtvfb.h index e980ba62d..e20af47b5 100644 --- a/linux/include/linux/ivtvfb.h +++ b/linux/include/linux/ivtvfb.h @@ -21,11 +21,7 @@ #ifndef __LINUX_IVTVFB_H__ #define __LINUX_IVTVFB_H__ -#ifdef __KERNEL__ -#include <linux/compiler.h> /* need __user */ -#else -#define __user -#endif +#include <linux/compiler.h> #include <linux/types.h> /* Framebuffer external API */ diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index f852028e7..63b2787e6 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -55,13 +55,13 @@ */ #ifndef __LINUX_VIDEODEV2_H #define __LINUX_VIDEODEV2_H + #ifdef __KERNEL__ #include <linux/time.h> /* need struct timeval */ -#include <linux/compiler.h> /* need __user */ #else -#define __user #include <sys/time.h> #endif +#include <linux/compiler.h> #include <linux/ioctl.h> #include <linux/types.h> @@ -334,6 +334,8 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */ #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ +#define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */ /* * F O R M A T E N U M E R A T I O N @@ -908,6 +910,8 @@ enum v4l2_mpeg_audio_encoding { V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0, V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2, + V4L2_MPEG_AUDIO_ENCODING_AAC = 3, + V4L2_MPEG_AUDIO_ENCODING_AC3 = 4, }; #define V4L2_CID_MPEG_AUDIO_L1_BITRATE (V4L2_CID_MPEG_BASE+102) enum v4l2_mpeg_audio_l1_bitrate { @@ -986,12 +990,36 @@ enum v4l2_mpeg_audio_crc { V4L2_MPEG_AUDIO_CRC_CRC16 = 1, }; #define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_MPEG_BASE+109) +#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE (V4L2_CID_MPEG_BASE+110) +#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE (V4L2_CID_MPEG_BASE+111) +enum v4l2_mpeg_audio_ac3_bitrate { + V4L2_MPEG_AUDIO_AC3_BITRATE_32K = 0, + V4L2_MPEG_AUDIO_AC3_BITRATE_40K = 1, + V4L2_MPEG_AUDIO_AC3_BITRATE_48K = 2, + V4L2_MPEG_AUDIO_AC3_BITRATE_56K = 3, + V4L2_MPEG_AUDIO_AC3_BITRATE_64K = 4, + V4L2_MPEG_AUDIO_AC3_BITRATE_80K = 5, + V4L2_MPEG_AUDIO_AC3_BITRATE_96K = 6, + V4L2_MPEG_AUDIO_AC3_BITRATE_112K = 7, + V4L2_MPEG_AUDIO_AC3_BITRATE_128K = 8, + V4L2_MPEG_AUDIO_AC3_BITRATE_160K = 9, + V4L2_MPEG_AUDIO_AC3_BITRATE_192K = 10, + V4L2_MPEG_AUDIO_AC3_BITRATE_224K = 11, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K = 12, + V4L2_MPEG_AUDIO_AC3_BITRATE_320K = 13, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K = 14, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K = 15, + V4L2_MPEG_AUDIO_AC3_BITRATE_512K = 16, + V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17, + V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18, +}; /* MPEG video */ #define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200) enum v4l2_mpeg_video_encoding { - V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC = 2, }; #define V4L2_CID_MPEG_VIDEO_ASPECT (V4L2_CID_MPEG_BASE+201) enum v4l2_mpeg_video_aspect { diff --git a/linux/include/media/saa7115.h b/linux/include/media/saa7115.h index f677dfb9d..bab212719 100644 --- a/linux/include/media/saa7115.h +++ b/linux/include/media/saa7115.h @@ -1,5 +1,5 @@ /* - saa7115.h - definition for saa7113/4/5 inputs and frequency flags + saa7115.h - definition for saa7111/3/4/5 inputs and frequency flags Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl) @@ -21,13 +21,13 @@ #ifndef _SAA7115_H_ #define _SAA7115_H_ -/* SAA7113/4/5 HW inputs */ +/* SAA7111/3/4/5 HW inputs */ #define SAA7115_COMPOSITE0 0 #define SAA7115_COMPOSITE1 1 #define SAA7115_COMPOSITE2 2 #define SAA7115_COMPOSITE3 3 -#define SAA7115_COMPOSITE4 4 /* not available for the saa7113 */ -#define SAA7115_COMPOSITE5 5 /* not available for the saa7113 */ +#define SAA7115_COMPOSITE4 4 /* not available for the saa7111/3 */ +#define SAA7115_COMPOSITE5 5 /* not available for the saa7111/3 */ #define SAA7115_SVIDEO0 6 #define SAA7115_SVIDEO1 7 #define SAA7115_SVIDEO2 8 @@ -42,8 +42,15 @@ #define SAA7115_FREQ_FL_CGCDIV (1 << 1) /* SA 3A[6], CGCDIV, SAA7115 only */ #define SAA7115_FREQ_FL_APLL (1 << 2) /* SA 3A[3], APLL, SAA7114/5 only */ -#define SAA7115_IPORT_ON 1 -#define SAA7115_IPORT_OFF 0 +#define SAA7115_IPORT_ON 1 +#define SAA7115_IPORT_OFF 0 + +/* SAA7111 specific output flags */ +#define SAA7111_VBI_BYPASS 2 +#define SAA7111_FMT_YUV422 0x00 +#define SAA7111_FMT_RGB 0x40 +#define SAA7111_FMT_CCIR 0x80 +#define SAA7111_FMT_YUV411 0xc0 #endif diff --git a/linux/include/media/sh_mobile_ceu.h b/linux/include/media/sh_mobile_ceu.h index 234a4711d..b5dbefea3 100644 --- a/linux/include/media/sh_mobile_ceu.h +++ b/linux/include/media/sh_mobile_ceu.h @@ -5,8 +5,6 @@ struct sh_mobile_ceu_info { unsigned long flags; /* SOCAM_... */ - void (*enable_camera)(void); - void (*disable_camera)(void); }; #endif /* __ASM_SH_MOBILE_CEU_H__ */ diff --git a/linux/include/media/soc_camera.h b/linux/include/media/soc_camera.h index d548de326..c5de7bb19 100644 --- a/linux/include/media/soc_camera.h +++ b/linux/include/media/soc_camera.h @@ -83,6 +83,9 @@ struct soc_camera_link { int bus_id; /* GPIO number to switch between 8 and 10 bit modes */ unsigned int gpio; + /* Optional callbacks to power on or off and reset the sensor */ + int (*power)(struct device *, int); + int (*reset)(struct device *); }; static inline struct soc_camera_device *to_soc_camera_dev(struct device *dev) diff --git a/linux/include/media/v4l2-chip-ident.h b/linux/include/media/v4l2-chip-ident.h index 41b509bab..d73a8e902 100644 --- a/linux/include/media/v4l2-chip-ident.h +++ b/linux/include/media/v4l2-chip-ident.h @@ -72,6 +72,10 @@ enum { /* module cs5345: just ident 5345 */ V4L2_IDENT_CS5345 = 5345, + /* module saa6752hs: reserved range 6750-6759 */ + V4L2_IDENT_SAA6752HS = 6752, + V4L2_IDENT_SAA6752HS_AC3 = 6753, + /* module wm8739: just ident 8739 */ V4L2_IDENT_WM8739 = 8739, @@ -161,6 +165,7 @@ enum { /* Micron CMOS sensor chips: 45000-45099 */ V4L2_IDENT_MT9M001C12ST = 45000, V4L2_IDENT_MT9M001C12STM = 45005, + V4L2_IDENT_MT9M111 = 45007, V4L2_IDENT_MT9V022IX7ATC = 45010, /* No way to detect "normal" I77ATx */ V4L2_IDENT_MT9V022IX7ATM = 45015, /* and "lead free" IA7ATx chips */ }; diff --git a/linux/include/media/v4l2-common.h b/linux/include/media/v4l2-common.h index 07d3a9a57..2f8719abf 100644 --- a/linux/include/media/v4l2-common.h +++ b/linux/include/media/v4l2-common.h @@ -76,11 +76,14 @@ int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority *local); int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, const char **menu_items); +const char *v4l2_ctrl_get_name(u32 id); const char **v4l2_ctrl_get_menu(u32 id); int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def); int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl); int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl, const char **menu_items); +#define V4L2_CTRL_MENU_IDS_END (0xffffffff) +int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids); u32 v4l2_ctrl_next(const u32 * const *ctrl_classes, u32 id); /* ------------------------------------------------------------------------- */ @@ -222,18 +225,22 @@ struct v4l2_crystal_freq { An extra flags field allows device specific configuration regarding clock frequency dividers, etc. If not used, then set flags to 0. If the frequency is not supported, then -EINVAL is returned. */ -#define VIDIOC_INT_S_CRYSTAL_FREQ _IOW ('d', 113, struct v4l2_crystal_freq) +#define VIDIOC_INT_S_CRYSTAL_FREQ _IOW('d', 113, struct v4l2_crystal_freq) /* Initialize the sensor registors to some sort of reasonable default values. */ -#define VIDIOC_INT_INIT _IOW ('d', 114, u32) +#define VIDIOC_INT_INIT _IOW('d', 114, u32) /* Set v4l2_std_id for video OUTPUT devices. This is ignored by video input devices. */ -#define VIDIOC_INT_S_STD_OUTPUT _IOW ('d', 115, v4l2_std_id) +#define VIDIOC_INT_S_STD_OUTPUT _IOW('d', 115, v4l2_std_id) /* Get v4l2_std_id for video OUTPUT devices. This is ignored by video input devices. */ -#define VIDIOC_INT_G_STD_OUTPUT _IOW ('d', 116, v4l2_std_id) +#define VIDIOC_INT_G_STD_OUTPUT _IOW('d', 116, v4l2_std_id) + +/* Set GPIO pins. Very simple right now, might need to be extended with + a v4l2_gpio struct if a direction is also needed. */ +#define VIDIOC_INT_S_GPIO _IOW('d', 117, u32) #endif /* V4L2_COMMON_H_ */ diff --git a/linux/include/media/v4l2-dev.h b/linux/include/media/v4l2-dev.h index 4e1511b0b..9158ad8b5 100644 --- a/linux/include/media/v4l2-dev.h +++ b/linux/include/media/v4l2-dev.h @@ -9,16 +9,15 @@ #ifndef _V4L2_DEV_H #define _V4L2_DEV_H -#define OBSOLETE_DEVDATA 1 /* to be removed soon */ - #include <linux/poll.h> #include <linux/fs.h> #include <linux/device.h> +#include <linux/cdev.h> #include <linux/mutex.h> -#include <linux/compiler.h> /* need __user */ #include <linux/videodev2.h> #define VIDEO_MAJOR 81 + /* Minor device allocation */ #define MINOR_VFL_TYPE_GRABBER_MIN 0 #define MINOR_VFL_TYPE_GRABBER_MAX 63 @@ -53,6 +52,8 @@ struct video_device #else struct class_device dev; #endif + struct cdev cdev; /* character device */ + void (*cdev_release)(struct kobject *kobj); struct device *parent; /* device parent */ /* device info */ @@ -73,50 +74,58 @@ struct video_device /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; - -#ifdef OBSOLETE_DEVDATA /* to be removed soon */ - /* dev->driver_data will be used instead some day. - * Use the video_{get|set}_drvdata() helper functions, - * so the switch over will be transparent for you. - * Or use {pci|usb}_{get|set}_drvdata() directly. */ - void *priv; -#endif - - /* for videodev.c internal usage -- please don't touch */ - int users; /* video_exclusive_{open|close} ... */ - struct mutex lock; /* ... helper function uses these */ }; -/* Class-dev to video-device */ +/* dev to video-device */ #define to_video_device(cd) container_of(cd, struct video_device, dev) -/* Version 2 functions */ -extern int video_register_device(struct video_device *vfd, int type, int nr); -int video_register_device_index(struct video_device *vfd, int type, int nr, - int index); -void video_unregister_device(struct video_device *); +/* Register and unregister devices. Note that if video_register_device fails, + the release() callback of the video_device structure is *not* called, so + the caller is responsible for freeing any data. Usually that means that + you call video_device_release() on failure. */ +int __must_check video_register_device(struct video_device *vfd, int type, int nr); +int __must_check video_register_device_index(struct video_device *vfd, + int type, int nr, int index); +void video_unregister_device(struct video_device *vfd); + +/* helper functions to alloc/release struct video_device, the + latter can also be used for video_device->release(). */ +struct video_device * __must_check video_device_alloc(void); -/* helper functions to alloc / release struct video_device, the - later can be used for video_device->release() */ -struct video_device *video_device_alloc(void); +/* this release function frees the vfd pointer */ void video_device_release(struct video_device *vfd); -#ifdef OBSOLETE_DEVDATA /* to be removed soon */ +/* this release function does nothing, use when the video_device is a + static global struct. Note that having a static video_device is + a dubious construction at best. */ +void video_device_release_empty(struct video_device *vfd); + /* helper functions to access driver private data. */ static inline void *video_get_drvdata(struct video_device *dev) { - return dev->priv; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) + return class_get_devdata(&dev->dev); +#else + return dev_get_drvdata(&dev->dev); +#endif } static inline void video_set_drvdata(struct video_device *dev, void *data) { - dev->priv = data; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) + class_set_devdata(&dev->dev, data); +#else + dev_set_drvdata(&dev->dev, data); +#endif } -/* Obsolete stuff - Still needed for radio devices and obsolete drivers */ -extern struct video_device* video_devdata(struct file*); -extern int video_exclusive_open(struct inode *inode, struct file *file); -extern int video_exclusive_release(struct inode *inode, struct file *file); -#endif +struct video_device *video_devdata(struct file *file); + +/* Combine video_get_drvdata and video_devdata as this is + used very often. */ +static inline void *video_drvdata(struct file *file) +{ + return video_get_drvdata(video_devdata(file)); +} #endif /* _V4L2_DEV_H */ diff --git a/linux/include/sound/tea575x-tuner.h b/linux/include/sound/tea575x-tuner.h index c85aeca78..4076c6dfb 100644 --- a/linux/include/sound/tea575x-tuner.h +++ b/linux/include/sound/tea575x-tuner.h @@ -57,6 +57,7 @@ struct snd_tea575x { unsigned int freq_fixup; /* crystal onboard */ unsigned int val; /* hw value */ unsigned long freq; /* frequency */ + unsigned long in_use; /* set if the device is in use */ struct snd_tea575x_ops *ops; void *private_data; }; diff --git a/linux/sound/i2c/other/tea575x-tuner.c b/linux/sound/i2c/other/tea575x-tuner.c index c4bd65c94..2d07c8ea5 100644 --- a/linux/sound/i2c/other/tea575x-tuner.c +++ b/linux/sound/i2c/other/tea575x-tuner.c @@ -92,11 +92,10 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea) static int snd_tea575x_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data) { - struct video_device *dev = video_devdata(file); #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) - tea575x_t *tea = video_get_drvdata(dev); + tea575x_t *tea = video_drvdata(file); #else - struct snd_tea575x *tea = video_get_drvdata(dev); + struct snd_tea575x *tea = video_drvdata(file); #endif void __user *arg = (void __user *)data; @@ -186,6 +185,29 @@ static void snd_tea575x_release(struct video_device *vfd) { } +static int snd_tea575x_exclusive_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) + tea575x_t *tea = video_drvdata(file); +#else + struct snd_tea575x *tea = video_drvdata(file); +#endif + + return test_and_set_bit(0, &tea->in_use) ? -EBUSY : 0; +} + +static int snd_tea575x_exclusive_release(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) + tea575x_t *tea = video_drvdata(file); +#else + struct snd_tea575x *tea = video_drvdata(file); +#endif + + clear_bit(0, &tea->in_use); + return 0; +} + /* * initialize all the tea575x chips */ @@ -208,9 +230,10 @@ void snd_tea575x_init(struct snd_tea575x *tea) tea->vd.release = snd_tea575x_release; video_set_drvdata(&tea->vd, tea); tea->vd.fops = &tea->fops; + tea->in_use = 0; tea->fops.owner = tea->card->module; - tea->fops.open = video_exclusive_open; - tea->fops.release = video_exclusive_release; + tea->fops.open = snd_tea575x_exclusive_open; + tea->fops.release = snd_tea575x_exclusive_release; tea->fops.ioctl = snd_tea575x_ioctl; if (video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->dev_nr - 1) < 0) { snd_printk(KERN_ERR "unable to register tea575x tuner\n"); diff --git a/linux/sound/oss/aci.c b/linux/sound/oss/aci.c deleted file mode 100644 index 4c407b77f..000000000 --- a/linux/sound/oss/aci.c +++ /dev/null @@ -1,713 +0,0 @@ -/* - * Audio Command Interface (ACI) driver (sound/aci.c) - * - * ACI is a protocol used to communicate with the microcontroller on - * some sound cards produced by miro, e.g. the miroSOUND PCM12 and - * PCM20. The ACI has been developed for miro by Norberto Pellicci - * <pellicci@home.com>. Special thanks to both him and miro for - * providing the ACI specification. - * - * The main function of the ACI is to control the mixer and to get a - * product identification. On the PCM20, ACI also controls the radio - * tuner on this card, this is supported in the Video for Linux - * miropcm20 driver. - * - - * This is a fullfeatured implementation. Unsupported features - * are bugs... (: - * - * It is not longer necessary to load the mad16 module first. The - * user is currently responsible to set the mad16 mixer correctly. - * - * To toggle the solo mode for full duplex operation just use the OSS - * record switch for the pcm ('wave') controller. Robert - * - - * - * Revision history: - * - * 1995-11-10 Markus Kuhn <mskuhn@cip.informatik.uni-erlangen.de> - * First version written. - * 1995-12-31 Markus Kuhn - * Second revision, general code cleanup. - * 1996-05-16 Hannu Savolainen - * Integrated with other parts of the driver. - * 1996-05-28 Markus Kuhn - * Initialize CS4231A mixer, make ACI first mixer, - * use new private mixer API for solo mode. - * 1998-08-18 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl> - * Small modification to export ACI functions and - * complete modularisation. - * 2000-06-20 Robert Siemer <Robert.Siemer@gmx.de> - * Don't initialize the CS4231A mixer anymore, so the code is - * working again, and other small changes to fit in todays - * kernels. - * 2000-08-26 Robert Siemer - * Clean up and rewrite for 2.4.x. Maybe it's SMP safe now... (: - * ioctl bugfix, and integration of solo-mode into OSS-API, - * added (OSS-limited) equalizer support, return value bugfix, - * changed param aci_reset to reset, new params: ide, wss. - * 2001-04-20 Robert Siemer - * even more cleanups... - * 2001-10-08 Arnaldo Carvalho de Melo <acme@conectiva.com.br> - * Get rid of check_region, .bss optimizations, use set_current_state - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/proc_fs.h> -#include <linux/slab.h> -#include <linux/mutex.h> - -#include <asm/io.h> -#include <asm/uaccess.h> -#include "sound_config.h" -#include "compat.h" - -int aci_port; /* as determined by bit 4 in the OPTi 929 MC4 register */ -static int aci_idcode[2]; /* manufacturer and product ID */ -int aci_version; /* ACI firmware version */ - -EXPORT_SYMBOL(aci_port); -EXPORT_SYMBOL(aci_version); - -#include "aci.h" - - -static int aci_solo; /* status bit of the card that can't be * - * checked with ACI versions prior to 0xb0 */ -static int aci_amp; /* status bit for power-amp/line-out level - but I have no docs about what is what... */ -static int aci_micpreamp=3; /* microphone preamp-level that can't be * - * checked with ACI versions prior to 0xb0 */ - -static int mixer_device; -static struct mutex aci_mutex; - -#ifdef MODULE -static int reset; -module_param(reset, bool, 0); -MODULE_PARM_DESC(reset,"When set to 1, reset aci mixer."); -#else -static int reset = 1; -#endif - -static int ide=-1; -module_param(ide, int, 0); -MODULE_PARM_DESC(ide,"1 enable, 0 disable ide-port - untested" - " default: do nothing"); -static int wss=-1; -module_param(wss, int, 0); -MODULE_PARM_DESC(wss,"change between ACI/WSS-mixer; use 0 and 1 - untested" - " default: do nothing; for PCM1-pro only"); - -#ifdef DEBUG -static void print_bits(unsigned char c) -{ - int j; - printk(KERN_DEBUG "aci: "); - - for (j=7; j>=0; j--) { - printk("%d", (c >> j) & 0x1); - } - - printk("\n"); -} -#endif - -/* - * This busy wait code normally requires less than 15 loops and - * practically always less than 100 loops on my i486/DX2 66 MHz. - * - * Warning: Waiting on the general status flag after reseting the MUTE - * function can take a VERY long time, because the PCM12 does some kind - * of fade-in effect. For this reason, access to the MUTE function has - * not been implemented at all. - * - * - The OSS interface has no mute option. It takes about 3 seconds to - * fade-in on my PCM20. busy_wait() handles it great now... Robert - */ - -static int busy_wait(void) -{ - #define MINTIME 500 - long timeout; - unsigned char byte; - - for (timeout = 1; timeout <= MINTIME+30; timeout++) { - if (((byte=inb(BUSY_REGISTER)) & 1) == 0) { - if (timeout >= MINTIME) - printk(KERN_DEBUG "aci: Got READYFLAG in round %ld.\n", timeout-MINTIME); - return byte; - } - if (timeout >= MINTIME) { - long out=10*HZ; - switch (timeout-MINTIME) { - case 0 ... 9: - out /= 10; - case 10 ... 19: - out /= 10; - case 20 ... 30: - out /= 10; - default: - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(out); - break; - } - } - } - printk(KERN_WARNING "aci: busy_wait() time out.\n"); - return -EBUSY; -} - -/* The four ACI command types are fucked up. [-: - * implied is: 1w - special case for INIT - * write is: 2w1r - * read is: x(1w1r) where x is 1 or 2 (1 CHECK_SIG, 1 CHECK_STER, - * 1 VERSION, 2 IDCODE) - * the command is only in the first write, rest is protocol overhead - * - * indexed is technically a write and used for STATUS - * and the special case for TUNE is: 3w1r - * - * Here the new general sheme: TUNE --> aci_rw_cmd(x, y, z) - * indexed and write --> aci_rw_cmd(x, y, -1) - * implied and read (x=1) --> aci_rw_cmd(x, -1, -1) - * - * Read (x>=2) is not implemented (only used during initialization). - * Use aci_idcode[2] and aci_version... Robert - */ - -/* Some notes for error detection: theoretically it is possible. - * But it doubles the I/O-traffic from ww(r) to wwwrw(r) in the normal - * case and doesn't seem to be designed for that... Robert - */ - -static inline int aci_rawwrite(unsigned char byte) -{ - if (busy_wait() >= 0) { -#ifdef DEBUG - printk(KERN_DEBUG "aci_rawwrite(%d)\n", byte); -#endif - outb(byte, COMMAND_REGISTER); - return 0; - } else - return -EBUSY; -} - -static inline int aci_rawread(void) -{ - unsigned char byte; - - if (busy_wait() >= 0) { - byte=inb(STATUS_REGISTER); -#ifdef DEBUG - printk(KERN_DEBUG "%d = aci_rawread()\n", byte); -#endif - return byte; - } else - return -EBUSY; -} - - -int aci_rw_cmd(int write1, int write2, int write3) -{ - int write[] = {write1, write2, write3}; - int read = -EINTR, i; - - if (mutex_lock_interruptible(&aci_mutex)) - goto out; - - for (i=0; i<3; i++) { - if (write[i]< 0 || write[i] > 255) - break; - else { - read = aci_rawwrite(write[i]); - if (read < 0) - goto out_up; - } - - } - - read = aci_rawread(); -out_up: mutex_unlock(&aci_mutex); -out: return read; -} - -EXPORT_SYMBOL(aci_rw_cmd); - -static int setvolume(int __user *arg, - unsigned char left_index, unsigned char right_index) -{ - int vol, ret, uservol, buf; - - __get_user(uservol, arg); - - /* left channel */ - vol = uservol & 0xff; - if (vol > 100) - vol = 100; - vol = SCALE(100, 0x20, vol); - if ((buf=aci_write_cmd(left_index, 0x20 - vol))<0) - return buf; - ret = SCALE(0x20, 100, vol); - - - /* right channel */ - vol = (uservol >> 8) & 0xff; - if (vol > 100) - vol = 100; - vol = SCALE(100, 0x20, vol); - if ((buf=aci_write_cmd(right_index, 0x20 - vol))<0) - return buf; - ret |= SCALE(0x20, 100, vol) << 8; - - __put_user(ret, arg); - - return 0; -} - -static int getvolume(int __user *arg, - unsigned char left_index, unsigned char right_index) -{ - int vol; - int buf; - - /* left channel */ - if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) - return buf; - vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0); - - /* right channel */ - if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) - return buf; - vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8; - - __put_user(vol, arg); - - return 0; -} - - -/* The equalizer is somewhat strange on the ACI. From -12dB to +12dB - * write: 0xff..down.to..0x80==0x00..up.to..0x7f - */ - -static inline unsigned int eq_oss2aci(unsigned int vol) -{ - int boost=0; - unsigned int ret; - - if (vol > 100) - vol = 100; - if (vol > 50) { - vol -= 51; - boost=1; - } - if (boost) - ret=SCALE(49, 0x7e, vol)+1; - else - ret=0xff - SCALE(50, 0x7f, vol); - return ret; -} - -static inline unsigned int eq_aci2oss(unsigned int vol) -{ - if (vol < 0x80) - return SCALE(0x7f, 50, vol) + 50; - else - return SCALE(0x7f, 50, 0xff-vol); -} - - -static int setequalizer(int __user *arg, - unsigned char left_index, unsigned char right_index) -{ - int buf; - unsigned int vol; - - __get_user(vol, arg); - - /* left channel */ - if ((buf=aci_write_cmd(left_index, eq_oss2aci(vol & 0xff)))<0) - return buf; - - /* right channel */ - if ((buf=aci_write_cmd(right_index, eq_oss2aci((vol>>8) & 0xff)))<0) - return buf; - - /* the ACI equalizer is more precise */ - return 0; -} - -static int getequalizer(int __user *arg, - unsigned char left_index, unsigned char right_index) -{ - int buf; - unsigned int vol; - - /* left channel */ - if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) - return buf; - vol = eq_aci2oss(buf); - - /* right channel */ - if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) - return buf; - vol |= eq_aci2oss(buf) << 8; - - __put_user(vol, arg); - - return 0; -} - -static int aci_mixer_ioctl (int dev, unsigned int cmd, void __user * arg) -{ - int vol, buf; - int __user *p = arg; - - switch (cmd) { - case SOUND_MIXER_WRITE_VOLUME: - return setvolume(p, 0x01, 0x00); - case SOUND_MIXER_WRITE_CD: - return setvolume(p, 0x3c, 0x34); - case SOUND_MIXER_WRITE_MIC: - return setvolume(p, 0x38, 0x30); - case SOUND_MIXER_WRITE_LINE: - return setvolume(p, 0x39, 0x31); - case SOUND_MIXER_WRITE_SYNTH: - return setvolume(p, 0x3b, 0x33); - case SOUND_MIXER_WRITE_PCM: - return setvolume(p, 0x3a, 0x32); - case MIXER_WRITE(SOUND_MIXER_RADIO): /* fall through */ - case SOUND_MIXER_WRITE_LINE1: /* AUX1 or radio */ - return setvolume(p, 0x3d, 0x35); - case SOUND_MIXER_WRITE_LINE2: /* AUX2 */ - return setvolume(p, 0x3e, 0x36); - case SOUND_MIXER_WRITE_BASS: /* set band one and two */ - if (aci_idcode[1]=='C') { - if ((buf=setequalizer(p, 0x48, 0x40)) || - (buf=setequalizer(p, 0x49, 0x41))); - return buf; - } - break; - case SOUND_MIXER_WRITE_TREBLE: /* set band six and seven */ - if (aci_idcode[1]=='C') { - if ((buf=setequalizer(p, 0x4d, 0x45)) || - (buf=setequalizer(p, 0x4e, 0x46))); - return buf; - } - break; - case SOUND_MIXER_WRITE_IGAIN: /* MIC pre-amp */ - if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { - __get_user(vol, p); - vol = vol & 0xff; - if (vol > 100) - vol = 100; - vol = SCALE(100, 3, vol); - if ((buf=aci_write_cmd(ACI_WRITE_IGAIN, vol))<0) - return buf; - aci_micpreamp = vol; - vol = SCALE(3, 100, vol); - vol |= (vol << 8); - __put_user(vol, p); - return 0; - } - break; - case SOUND_MIXER_WRITE_OGAIN: /* Power-amp/line-out level */ - if (aci_idcode[1]=='A' || aci_idcode[1]=='B') { - __get_user(buf, p); - buf = buf & 0xff; - if (buf > 50) - vol = 1; - else - vol = 0; - if ((buf=aci_write_cmd(ACI_SET_POWERAMP, vol))<0) - return buf; - aci_amp = vol; - if (aci_amp) - buf = (100 || 100<<8); - else - buf = 0; - __put_user(buf, p); - return 0; - } - break; - case SOUND_MIXER_WRITE_RECSRC: - /* handle solo mode control */ - __get_user(buf, p); - /* unset solo when RECSRC for PCM is requested */ - if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { - vol = !(buf & SOUND_MASK_PCM); - if ((buf=aci_write_cmd(ACI_SET_SOLOMODE, vol))<0) - return buf; - aci_solo = vol; - } - buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE| - SOUND_MASK_SYNTH| SOUND_MASK_LINE2); - if (aci_idcode[1] == 'C') /* PCM20 radio */ - buf |= SOUND_MASK_RADIO; - else - buf |= SOUND_MASK_LINE1; - if (!aci_solo) - buf |= SOUND_MASK_PCM; - __put_user(buf, p); - return 0; - case SOUND_MIXER_READ_DEVMASK: - buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | - SOUND_MASK_MIC | SOUND_MASK_LINE | - SOUND_MASK_SYNTH | SOUND_MASK_PCM | - SOUND_MASK_LINE2); - switch (aci_idcode[1]) { - case 'C': /* PCM20 radio */ - buf |= (SOUND_MASK_RADIO | SOUND_MASK_IGAIN | - SOUND_MASK_BASS | SOUND_MASK_TREBLE); - break; - case 'B': /* PCM12 */ - buf |= (SOUND_MASK_LINE1 | SOUND_MASK_IGAIN | - SOUND_MASK_OGAIN); - break; - case 'A': /* PCM1-pro */ - buf |= (SOUND_MASK_LINE1 | SOUND_MASK_OGAIN); - break; - default: - buf |= SOUND_MASK_LINE1; - } - __put_user(buf, p); - return 0; - case SOUND_MIXER_READ_STEREODEVS: - buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | - SOUND_MASK_MIC | SOUND_MASK_LINE | - SOUND_MASK_SYNTH | SOUND_MASK_PCM | - SOUND_MASK_LINE2); - switch (aci_idcode[1]) { - case 'C': /* PCM20 radio */ - buf |= (SOUND_MASK_RADIO | - SOUND_MASK_BASS | SOUND_MASK_TREBLE); - break; - default: - buf |= SOUND_MASK_LINE1; - } - __put_user(buf, p); - return 0; - case SOUND_MIXER_READ_RECMASK: - buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE| - SOUND_MASK_SYNTH| SOUND_MASK_LINE2| SOUND_MASK_PCM); - if (aci_idcode[1] == 'C') /* PCM20 radio */ - buf |= SOUND_MASK_RADIO; - else - buf |= SOUND_MASK_LINE1; - - __put_user(buf, p); - return 0; - case SOUND_MIXER_READ_RECSRC: - buf = (SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | - SOUND_MASK_SYNTH | SOUND_MASK_LINE2); - /* do we need aci_solo or can I get it from the ACI? */ - switch (aci_idcode[1]) { - case 'B': /* PCM12 */ - case 'C': /* PCM20 radio */ - if (aci_version >= 0xb0) { - if ((vol=aci_rw_cmd(ACI_STATUS, - ACI_S_GENERAL, -1))<0) - return vol; - if (vol & 0x20) - buf |= SOUND_MASK_PCM; - } - else - if (!aci_solo) - buf |= SOUND_MASK_PCM; - break; - default: - buf |= SOUND_MASK_PCM; - } - if (aci_idcode[1] == 'C') /* PCM20 radio */ - buf |= SOUND_MASK_RADIO; - else - buf |= SOUND_MASK_LINE1; - - __put_user(buf, p); - return 0; - case SOUND_MIXER_READ_CAPS: - __put_user(0, p); - return 0; - case SOUND_MIXER_READ_VOLUME: - return getvolume(p, 0x04, 0x03); - case SOUND_MIXER_READ_CD: - return getvolume(p, 0x0a, 0x09); - case SOUND_MIXER_READ_MIC: - return getvolume(p, 0x06, 0x05); - case SOUND_MIXER_READ_LINE: - return getvolume(p, 0x08, 0x07); - case SOUND_MIXER_READ_SYNTH: - return getvolume(p, 0x0c, 0x0b); - case SOUND_MIXER_READ_PCM: - return getvolume(p, 0x0e, 0x0d); - case MIXER_READ(SOUND_MIXER_RADIO): /* fall through */ - case SOUND_MIXER_READ_LINE1: /* AUX1 */ - return getvolume(p, 0x11, 0x10); - case SOUND_MIXER_READ_LINE2: /* AUX2 */ - return getvolume(p, 0x13, 0x12); - case SOUND_MIXER_READ_BASS: /* get band one */ - if (aci_idcode[1]=='C') { - return getequalizer(p, 0x23, 0x22); - } - break; - case SOUND_MIXER_READ_TREBLE: /* get band seven */ - if (aci_idcode[1]=='C') { - return getequalizer(p, 0x2f, 0x2e); - } - break; - case SOUND_MIXER_READ_IGAIN: /* MIC pre-amp */ - if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { - /* aci_micpreamp or ACI? */ - if (aci_version >= 0xb0) { - if ((buf=aci_indexed_cmd(ACI_STATUS, - ACI_S_READ_IGAIN))<0) - return buf; - } - else - buf=aci_micpreamp; - vol = SCALE(3, 100, buf <= 3 ? buf : 3); - vol |= vol << 8; - __put_user(vol, p); - return 0; - } - break; - case SOUND_MIXER_READ_OGAIN: - if (aci_amp) - buf = (100 || 100<<8); - else - buf = 0; - __put_user(buf, p); - return 0; - } - return -EINVAL; -} - -static struct mixer_operations aci_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "ACI", - .ioctl = aci_mixer_ioctl -}; - -/* - * There is also an internal mixer in the codec (CS4231A or AD1845), - * that deserves no purpose in an ACI based system which uses an - * external ACI controlled stereo mixer. Make sure that this codec - * mixer has the AUX1 input selected as the recording source, that the - * input gain is set near maximum and that the other channels going - * from the inputs to the codec output are muted. - */ - -static int __init attach_aci(void) -{ - char *boardname; - int i, rc = -EBUSY; - - mutex_init(&aci_mutex); - - outb(0xE3, 0xf8f); /* Write MAD16 password */ - aci_port = (inb(0xf90) & 0x10) ? - 0x344: 0x354; /* Get aci_port from MC4_PORT */ - - if (!request_region(aci_port, 3, "sound mixer (ACI)")) { - printk(KERN_NOTICE - "aci: I/O area 0x%03x-0x%03x already used.\n", - aci_port, aci_port+2); - goto out; - } - - /* force ACI into a known state */ - rc = -EFAULT; - for (i=0; i<3; i++) - if (aci_rw_cmd(ACI_ERROR_OP, -1, -1)<0) - goto out_release_region; - - /* official this is one aci read call: */ - rc = -EFAULT; - if ((aci_idcode[0]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0 || - (aci_idcode[1]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0) { - printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n", - aci_port); - goto out_release_region; - } - - if ((aci_version=aci_rw_cmd(ACI_READ_VERSION, -1, -1))<0) { - printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n", - aci_port); - goto out_release_region; - } - - if (aci_idcode[0] == 'm') { - /* It looks like a miro sound card. */ - switch (aci_idcode[1]) { - case 'A': - boardname = "PCM1 pro / early PCM12"; - break; - case 'B': - boardname = "PCM12"; - break; - case 'C': - boardname = "PCM20 radio"; - break; - default: - boardname = "unknown miro"; - } - } else { - printk(KERN_WARNING "aci: Warning: unsupported card! - " - "no hardware, no specs...\n"); - boardname = "unknown Cardinal Technologies"; - } - - printk(KERN_INFO "<ACI 0x%02x, id %02x/%02x \"%c/%c\", (%s)> at 0x%03x\n", - aci_version, - aci_idcode[0], aci_idcode[1], - aci_idcode[0], aci_idcode[1], - boardname, aci_port); - - rc = -EBUSY; - if (reset) { - /* first write()s after reset fail with my PCM20 */ - if (aci_rw_cmd(ACI_INIT, -1, -1)<0 || - aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0 || - aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0) - goto out_release_region; - } - - /* the PCM20 is muted after reset (and reboot) */ - if (aci_rw_cmd(ACI_SET_MUTE, 0x00, -1)<0) - goto out_release_region; - - if (ide>=0) - if (aci_rw_cmd(ACI_SET_IDE, !ide, -1)<0) - goto out_release_region; - - if (wss>=0 && aci_idcode[1]=='A') - if (aci_rw_cmd(ACI_SET_WSS, !!wss, -1)<0) - goto out_release_region; - - mixer_device = sound_install_mixer(MIXER_DRIVER_VERSION, boardname, - &aci_mixer_operations, - sizeof(aci_mixer_operations), NULL); - rc = 0; - if (mixer_device < 0) { - printk(KERN_ERR "aci: Failed to install mixer.\n"); - rc = mixer_device; - goto out_release_region; - } /* else Maybe initialize the CS4231A mixer here... */ -out: return rc; -out_release_region: - release_region(aci_port, 3); - goto out; -} - -static void __exit unload_aci(void) -{ - sound_unload_mixerdev(mixer_device); - release_region(aci_port, 3); -} - -module_init(attach_aci); -module_exit(unload_aci); -MODULE_LICENSE("GPL"); diff --git a/linux/sound/oss/aci.h b/linux/sound/oss/aci.h deleted file mode 100644 index 20102ee08..000000000 --- a/linux/sound/oss/aci.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef _ACI_H_ -#define _ACI_H_ - -extern int aci_port; -extern int aci_version; /* ACI firmware version */ -extern int aci_rw_cmd(int write1, int write2, int write3); - -#define aci_indexed_cmd(a, b) aci_rw_cmd(a, b, -1) -#define aci_write_cmd(a, b) aci_rw_cmd(a, b, -1) -#define aci_read_cmd(a) aci_rw_cmd(a,-1, -1) - -#define COMMAND_REGISTER (aci_port) /* write register */ -#define STATUS_REGISTER (aci_port + 1) /* read register */ -#define BUSY_REGISTER (aci_port + 2) /* also used for rds */ - -#define RDS_REGISTER BUSY_REGISTER - -#define ACI_SET_MUTE 0x0d -#define ACI_SET_POWERAMP 0x0f -#define ACI_SET_TUNERMUTE 0xa3 -#define ACI_SET_TUNERMONO 0xa4 -#define ACI_SET_IDE 0xd0 -#define ACI_SET_WSS 0xd1 -#define ACI_SET_SOLOMODE 0xd2 -#define ACI_WRITE_IGAIN 0x03 -#define ACI_WRITE_TUNE 0xa7 -#define ACI_READ_TUNERSTEREO 0xa8 -#define ACI_READ_TUNERSTATION 0xa9 -#define ACI_READ_VERSION 0xf1 -#define ACI_READ_IDCODE 0xf2 -#define ACI_INIT 0xff -#define ACI_STATUS 0xf0 -#define ACI_S_GENERAL 0x00 -#define ACI_S_READ_IGAIN 0x21 -#define ACI_ERROR_OP 0xdf - -/* - * The following macro SCALE can be used to scale one integer volume - * value into another one using only integer arithmetic. If the input - * value x is in the range 0 <= x <= xmax, then the result will be in - * the range 0 <= SCALE(xmax,ymax,x) <= ymax. - * - * This macro has for all xmax, ymax > 0 and all 0 <= x <= xmax the - * following nice properties: - * - * - SCALE(xmax,ymax,xmax) = ymax - * - SCALE(xmax,ymax,0) = 0 - * - SCALE(xmax,ymax,SCALE(ymax,xmax,SCALE(xmax,ymax,x))) = SCALE(xmax,ymax,x) - * - * In addition, the rounding error is minimal and nicely distributed. - * The proofs are left as an exercise to the reader. - */ - -#define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax)) - - -#endif /* _ACI_H_ */ diff --git a/mailimport b/mailimport index bd6d36c2e..8ed624179 100755 --- a/mailimport +++ b/mailimport @@ -165,6 +165,12 @@ proccess_patch () { i="$_" + CHECK="`cat $i| perl -ne 'if (m/^Subject:.*FOLDER INTERNAL DATA/) { print -1; }'`" + + if [ "$CHECK" != "" ]; then + return + fi + echo $i if [ "`diffstat -p1 -l "$i"`" == "" ]; then echo "*** ERROR nothing to commit" diff --git a/v4l/Kconfig.sound b/v4l/Kconfig.sound index cab280c40..597c9c72b 100644 --- a/v4l/Kconfig.sound +++ b/v4l/Kconfig.sound @@ -5,7 +5,7 @@ comment "ALSA sound" config SND_BT87X tristate "Bt87x Audio Capture" - depends on SND + depends on SND && PCI select SND_PCM help If you want to record audio from TV cards based on @@ -64,23 +64,6 @@ config SOUND_BT878 To compile this driver as a module, choose M here: the module will be called btaudio. -config SOUND_ACI_MIXER - tristate "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20)" - depends on SOUND_OSS - ---help--- - ACI (Audio Command Interface) is a protocol used to communicate with - the microcontroller on some sound cards produced by miro and - Cardinal Technologies. The main function of the ACI is to control - the mixer and to get a product identification. - - This VoxWare ACI driver currently supports the ACI functions on the - miroSOUND PCM1-pro, PCM12 and PCM20 radio. On the PCM20 radio, ACI - also controls the radio tuner. This is supported in the video4linux - miropcm20 driver (say M or Y here and go back to "Multimedia - devices" -> "Radio Adapters"). - - This driver is also available as a module and will be called aci. - config SOUND_TVMIXER tristate "TV card (bt848) mixer support" depends on SOUND_PRIME && I2C && VIDEO_V4L1 diff --git a/v4l/Makefile b/v4l/Makefile index b43b59867..2aa856974 100644 --- a/v4l/Makefile +++ b/v4l/Makefile @@ -109,10 +109,6 @@ ifeq ($(CONFIG_DVB_FIRESAT),m) EXTRA_CFLAGS += -I$(srctree)/drivers/ieee1394/ endif -ifeq ($(CONFIG_SOUND_ACI_MIXER),m) - EXTRA_CFLAGS += -I$(srctree)/sound/oss/ -endif - EXTRA_CFLAGS += -g EXTRA_CFLAGS += $(if $(wildcard $(srctree)/.mm), -DMM_KERNEL) @@ -121,7 +117,7 @@ EXTRA_CFLAGS += $(if $(wildcard $(srctree)/.mm), -DMM_KERNEL) EXTRA_CFLAGS += -include $(obj)/config-compat.h # Allow kernel version compat tests without adding #include's -EXTRA_CFLAGS += -include linux/version.h +EXTRA_CFLAGS += -include include/linux/version.h ################################################# # Kernel 2.4/2.6 specific rules diff --git a/v4l/Makefile.sound b/v4l/Makefile.sound index 0f218b78b..941dbc65c 100644 --- a/v4l/Makefile.sound +++ b/v4l/Makefile.sound @@ -5,7 +5,6 @@ obj-$(CONFIG_SND_BT87X) += snd-bt87x.o # From sound/oss/Makefile -obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o obj-$(CONFIG_SOUND_BT878) += btaudio.o # From sound/i2c/other/Makefile @@ -17,22 +16,22 @@ KDIRA := /lib/modules/$(KERNELRELEASE)/kernel sound-install install-sound:: @dir="sound/pci"; \ - files='snd-bt87x.ko'; \ - echo -e "\nInstalling $(KDIRA)/$$dir files:"; \ - install -d $(KDIRA)/$$dir; \ - for i in $$files;do if [ -e $$i ]; then echo -n "$$i "; \ + files='snd-bt87x.ko'; \ + echo -e "\nInstalling $(KDIRA)/$$dir files:"; \ + install -d $(KDIRA)/$$dir; \ + for i in $$files;do if [ -e $$i ]; then echo -n "$$i "; \ install -m 644 -c $$i $(KDIRA)/$$dir; fi; done; echo; @dir="sound/oss"; \ - files='aci.ko btaudio.ko'; \ - echo -e "\nInstalling $(KDIRA)/$$dir files:"; \ - install -d $(KDIRA)/$$dir; \ + files='btaudio.ko'; \ + echo -e "\nInstalling $(KDIRA)/$$dir files:"; \ + install -d $(KDIRA)/$$dir; \ for i in $$files;do if [ -e $$i ]; then echo -n "$$i "; \ install -m 644 -c $$i $(KDIRA)/$$dir; fi; done; echo; @dir="sound/i2c/other"; \ files='snd-tea575x-tuner.ko'; \ - echo -e "\nInstalling $(KDIRA)/$$dir files:"; \ - install -d $(KDIRA)/$$dir; \ + echo -e "\nInstalling $(KDIRA)/$$dir files:"; \ + install -d $(KDIRA)/$$dir; \ for i in $$files;do if [ -e $$i ]; then echo -n "$$i "; \ install -m 644 -c $$i $(KDIRA)/$$dir; fi; done; echo; diff --git a/v4l/compat.h b/v4l/compat.h index 551696082..6e90c0e63 100644 --- a/v4l/compat.h +++ b/v4l/compat.h @@ -52,14 +52,11 @@ #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -# define PCIAGP_FAIL 0 - +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#define PCIAGP_FAIL 0 #define vmalloc_32_user(a) vmalloc_32(a) -#endif - /* bool type and enum-based definition of true and false was added in 2.6.19 */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) typedef int bool; #define true 1 #define false 0 @@ -231,8 +228,13 @@ typedef unsigned long uintptr_t; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static inline int list_is_singular(const struct list_head *head) { - return !list_empty(head) && (head->next == head->prev); + return !list_empty(head) && (head->next == head->prev); } + +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) +#define current_uid() (current->uid) #endif #endif diff --git a/v4l/firmware/Makefile b/v4l/firmware/Makefile index e018ad92a..272b2e668 100644 --- a/v4l/firmware/Makefile +++ b/v4l/firmware/Makefile @@ -7,27 +7,26 @@ TARGETS = ihex2fw vicam/firmware.fw dabusb/firmware.fw dabusb/bitstream.bin ttus default: $(TARGETS) clean: - -rm $(TARGETS) + -rm -f $(TARGETS) distclean: clean - -rmdir $(DIRS) + -for i in $(DIRS); do if [ -d $$i ]; then rmdir $$i; fi; done install: default - -for i in $(DIRS); do mkdir /lib/firmware/$$i; done + -for i in $(DIRS); do if [ ! -d /lib/firmware/$$i ]; then mkdir -p /lib/firmware/$$i; fi; done -for i in $(TARGETS); do cp $$i /lib/firmware/$$i; done - + ###### mkdir: - -mkdir $(DIRS) + -for i in $(DIRS); do if [ ! -d $$i ]; then mkdir -p $$i; fi; done ihex2fw: ../../linux/firmware/ihex2fw.c gcc -Wall -o $@ $< vicam/firmware.fw: ../../linux/firmware/vicam/firmware.H16 mkdir - -mkdir vicam ./ihex2fw -w $< $@ dabusb/firmware.fw: ../../linux/firmware/dabusb/firmware.HEX mkdir diff --git a/v4l/scripts/checkpatch.pl b/v4l/scripts/checkpatch.pl index 6971bf078..a4f512753 100755 --- a/v4l/scripts/checkpatch.pl +++ b/v4l/scripts/checkpatch.pl @@ -9,7 +9,7 @@ use strict; my $P = $0; $P =~ s@.*/@@g; -my $V = '0.19'; +my $V = '0.21'; use Getopt::Long qw(:config no_auto_abbrev); @@ -17,7 +17,6 @@ my $quiet = 0; my $tree = 1; my $chk_signoff = 1; my $chk_patch = 1; -my $tst_type = 0; my $tst_only; my $emacs = 0; my $terse = 0; @@ -44,7 +43,6 @@ GetOptions( 'summary-file!' => \$summary_file, 'debug=s' => \%debug, - 'test-type!' => \$tst_type, 'test-only=s' => \$tst_only, ) or exit; @@ -67,6 +65,7 @@ if ($#ARGV < 0) { my $dbg_values = 0; my $dbg_possible = 0; +my $dbg_type = 0; for my $key (keys %debug) { eval "\${dbg_$key} = '$debug{$key}';" } @@ -169,24 +168,23 @@ our @modifierList = ( ); sub build_types { - my $mods = "(?: \n" . join("|\n ", @modifierList) . "\n)"; - my $all = "(?: \n" . join("|\n ", @typeList) . "\n)"; + my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; + my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)"; + $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; $NonptrType = qr{ - (?:const\s+)? - (?:$mods\s+)? + (?:$Modifier\s+|const\s+)* (?: (?:typeof|__typeof__)\s*\(\s*\**\s*$Ident\s*\)| (?:${all}\b) ) - (?:\s+$Sparse|\s+const)* + (?:\s+$Modifier|\s+const)* }x; $Type = qr{ $NonptrType (?:\s*\*+\s*const|\s*\*+|(?:\s*\[\s*\])+)? - (?:\s+$Inline|\s+$Sparse|\s+$Attribute|\s+$mods)* + (?:\s+$Inline|\s+$Modifier)* }x; $Declare = qr{(?:$Storage\s+)?$Type}; - $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; } build_types(); @@ -470,7 +468,9 @@ sub ctx_statement_block { } $off++; } + # We are truly at the end, so shuffle to the next line. if ($off == $len) { + $loff = $len + 1; $line++; $remain--; } @@ -631,7 +631,7 @@ sub ctx_locate_comment { my ($first_line, $end_line) = @_; # Catch a comment on the end of the line itself. - my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*$@); + my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); return $current_comment if (defined $current_comment); # Look through the context and try and figure out if there is a @@ -689,17 +689,20 @@ sub cat_vet { my $av_preprocessor = 0; my $av_pending; my @av_paren_type; +my $av_pend_colon; sub annotate_reset { $av_preprocessor = 0; $av_pending = '_'; @av_paren_type = ('E'); + $av_pend_colon = 'O'; } sub annotate_values { my ($stream, $type) = @_; my $res; + my $var = '_' x length($stream); my $cur = $stream; print "$stream\n" if ($dbg_values > 1); @@ -715,10 +718,14 @@ sub annotate_values { $av_preprocessor = 0; } - } elsif ($cur =~ /^($Type)/) { + } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\()/) { print "DECLARE($1)\n" if ($dbg_values > 1); $type = 'T'; + } elsif ($cur =~ /^($Modifier)\s*/) { + print "MODIFIER($1)\n" if ($dbg_values > 1); + $type = 'T'; + } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { print "DEFINE($1,$2)\n" if ($dbg_values > 1); $av_preprocessor = 1; @@ -780,7 +787,12 @@ sub annotate_values { $av_pending = 'N'; $type = 'N'; - } elsif ($cur =~/^(return|case|else)/o) { + } elsif ($cur =~/^(case)/o) { + print "CASE($1)\n" if ($dbg_values > 1); + $av_pend_colon = 'C'; + $type = 'N'; + + } elsif ($cur =~/^(return|else|goto)/o) { print "KEYWORD($1)\n" if ($dbg_values > 1); $type = 'N'; @@ -800,10 +812,20 @@ sub annotate_values { print "PAREN('$1')\n" if ($dbg_values > 1); } - } elsif ($cur =~ /^($Ident)\(/o) { + } elsif ($cur =~ /^($Ident)\s*\(/o) { print "FUNC($1)\n" if ($dbg_values > 1); + $type = 'V'; $av_pending = 'V'; + } elsif ($cur =~ /^($Ident\s*):/) { + if ($type eq 'E') { + $av_pend_colon = 'L'; + } elsif ($type eq 'T') { + $av_pend_colon = 'B'; + } + print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); + $type = 'V'; + } elsif ($cur =~ /^($Ident|$Constant)/o) { print "IDENT($1)\n" if ($dbg_values > 1); $type = 'V'; @@ -815,11 +837,40 @@ sub annotate_values { } elsif ($cur =~/^(;|{|})/) { print "END($1)\n" if ($dbg_values > 1); $type = 'E'; + $av_pend_colon = 'O'; + + } elsif ($cur =~ /^(\?)/o) { + print "QUESTION($1)\n" if ($dbg_values > 1); + $type = 'N'; - } elsif ($cur =~ /^(;|\?|:|\[)/o) { + } elsif ($cur =~ /^(:)/o) { + print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); + + substr($var, length($res), 1, $av_pend_colon); + if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { + $type = 'E'; + } else { + $type = 'N'; + } + $av_pend_colon = 'O'; + + } elsif ($cur =~ /^(;|\[)/o) { print "CLOSE($1)\n" if ($dbg_values > 1); $type = 'N'; + } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&(?!\&))/o) { + my $variant; + + print "OPV($1)\n" if ($dbg_values > 1); + if ($type eq 'V') { + $variant = 'B'; + } else { + $variant = 'U'; + } + + substr($var, length($res), 1, $variant); + $type = 'N'; + } elsif ($cur =~ /^($Operators)/o) { print "OP($1)\n" if ($dbg_values > 1); if ($1 ne '++' && $1 ne '--') { @@ -835,17 +886,17 @@ sub annotate_values { } } - return $res; + return ($res, $var); } sub possible { my ($possible, $line) = @_; print "CHECK<$possible> ($line)\n" if ($dbg_possible > 1); - if ($possible !~ /^(?:$Storage|$Type|DEFINE_\S+)$/ && + if ($possible !~ /^(?:$Modifier|$Storage|$Type|DEFINE_\S+)$/ && $possible ne 'goto' && $possible ne 'return' && $possible ne 'case' && $possible ne 'else' && - $possible ne 'asm' && + $possible ne 'asm' && $possible ne '__asm__' && $possible !~ /^(typedef|struct|enum)\b/) { # Check for modifiers. $possible =~ s/\s*$Storage\s*//g; @@ -854,8 +905,10 @@ sub possible { } elsif ($possible =~ /\s/) { $possible =~ s/\s*$Type\s*//g; - warn "MODIFIER: $possible ($line)\n" if ($dbg_possible); - push(@modifierList, $possible); + for my $modifier (split(' ', $possible)) { + warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); + push(@modifierList, $modifier); + } } else { warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); @@ -1135,7 +1188,9 @@ sub process { } #80 column limit if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && - $rawline !~ /^.\s*\*\s*\@$Ident\s/ && $length > 80) + $rawline !~ /^.\s*\*\s*\@$Ident\s/ && + $line !~ /^\+\s*printk\s*\(\s*(?:KERN_\S+\s*)?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ && + $length > 80) { WARN("line over 80 characters\n" . $herecurr); } @@ -1162,10 +1217,10 @@ sub process { } # Check for potential 'bare' types - my ($stat, $cond); + my ($stat, $cond, $line_nr_next, $remain_next); if ($realcnt && $line =~ /.\s*\S/) { - ($stat, $cond) = ctx_statement_block($linenr, - $realcnt, 0); + ($stat, $cond, $line_nr_next, $remain_next) = + ctx_statement_block($linenr, $realcnt, 0); $stat =~ s/\n./\n /g; $cond =~ s/\n./\n /g; @@ -1179,7 +1234,7 @@ sub process { } elsif ($s =~ /^.\s*$Ident\s*\(/s) { # declarations always start with types - } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))\s*(?:;|=|,|\()/s) { + } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { my $type = $1; $type =~ s/\s+/ /g; possible($type, "A:" . $s); @@ -1239,6 +1294,10 @@ sub process { ERROR("switch and case should be at the same indent\n$hereline$err"); } } + if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && + $line !~ /\G(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$/g) { + ERROR("trailing statements should be on next line\n" . $herecurr); + } # if/while/etc brace do not go on next line, unless defining a do while loop, # or if that brace on the next line is for something else @@ -1246,17 +1305,22 @@ sub process { my $pre_ctx = "$1$2"; my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); - my $ctx_ln = $linenr + $#ctx + 1; my $ctx_cnt = $realcnt - $#ctx - 1; my $ctx = join("\n", @ctx); - ##warn "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; + my $ctx_ln = $linenr; + my $ctx_skip = $realcnt; - # Skip over any removed lines in the context following statement. - while (defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^-/) { + while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && + defined $lines[$ctx_ln - 1] && + $lines[$ctx_ln - 1] =~ /^-/)) { + ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; + $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); $ctx_ln++; } - ##warn "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; + + #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; + #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { ERROR("that open brace { should be on the previous line\n" . @@ -1276,12 +1340,14 @@ sub process { # Track the 'values' across context and added lines. my $opline = $line; $opline =~ s/^./ /; - my $curr_values = annotate_values($opline . "\n", $prev_values); + my ($curr_values, $curr_vars) = + annotate_values($opline . "\n", $prev_values); $curr_values = $prev_values . $curr_values; if ($dbg_values) { my $outline = $opline; $outline =~ s/\t/ /g; print "$linenr > .$outline\n"; print "$linenr > $curr_values\n"; + print "$linenr > $curr_vars\n"; } $prev_values = substr($curr_values, -1); @@ -1289,8 +1355,12 @@ sub process { if ($line=~/^[^\+]/) {next;} # TEST: allow direct testing of the type matcher. - if ($tst_type && $line =~ /^.$Declare$/) { - ERROR("TEST: is type $Declare\n" . $herecurr); + if ($dbg_type) { + if ($line =~ /^.\s*$Declare\s*$/) { + ERROR("TEST: is type\n" . $herecurr); + } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { + ERROR("TEST: is not type ($1 is)\n". $herecurr); + } next; } @@ -1365,11 +1435,11 @@ sub process { ERROR("\"(foo $1 )\" should be \"(foo $1)\"\n" . $herecurr); - } elsif ($line =~ m{$NonptrType(\*+)(?:\s+(?:$Attribute|$Sparse))?\s+[A-Za-z\d_]+}) { + } elsif ($line =~ m{\b$NonptrType(\*+)(?:\s+(?:$Attribute|$Sparse))?\s+[A-Za-z\d_]+}) { ERROR("\"foo$1 bar\" should be \"foo $1bar\"\n" . $herecurr); - } elsif ($line =~ m{$NonptrType\s+(\*+)(?!\s+(?:$Attribute|$Sparse))\s+[A-Za-z\d_]+}) { + } elsif ($line =~ m{\b$NonptrType\s+(\*+)(?!\s+(?:$Attribute|$Sparse))\s+[A-Za-z\d_]+}) { ERROR("\"foo $1 bar\" should be \"foo $1bar\"\n" . $herecurr); } @@ -1421,6 +1491,17 @@ sub process { ERROR("open brace '{' following $1 go on the same line\n" . $hereprev); } +# check for spacing round square brackets; allowed: +# 1. with a type on the left -- int [] a; +# 2. at the beginning of a line for slice initialisers -- [0..10] = 5, + while ($line =~ /(.*?\s)\[/g) { + my ($where, $prefix) = ($-[1], $1); + if ($prefix !~ /$Type\s+$/ && + ($where != 0 || $prefix !~ /^.\s+$/)) { + ERROR("space prohibited before open square bracket '['\n" . $herecurr); + } + } + # check for spaces between functions and their parentheses. while ($line =~ /($Ident)\s+\(/g) { my $name = $1; @@ -1457,7 +1538,8 @@ sub process { <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| =>|->|<<|>>|<|>|=|!|~| - &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|% + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| + \?|: }x; my @elements = split(/($ops|;)/, $opline); my $off = 0; @@ -1504,22 +1586,11 @@ sub process { my $ptr = substr($blank, 0, $off) . "^"; my $hereptr = "$hereline$ptr\n"; - # Classify operators into binary, unary, or - # definitions (* only) where they have more - # than one mode. + # Pull out the value of this operator. my $op_type = substr($curr_values, $off + 1, 1); - my $op_left = substr($curr_values, $off, 1); - my $is_unary; - if ($op_type eq 'T') { - $is_unary = 2; - } elsif ($op_left eq 'V') { - $is_unary = 0; - } else { - $is_unary = 1; - } - #if ($op eq '-' || $op eq '&' || $op eq '*') { - # print "UNARY: <$op_left$op_type $is_unary $a:$op:$c> <$ca:$op:$cc> <$unary_ctx>\n"; - #} + + # Get the full operator variant. + my $opv = $op . substr($curr_vars, $off, 1); # Ignore operators passed as parameters. if ($op_type ne 'V' && @@ -1538,8 +1609,10 @@ sub process { # // is a comment } elsif ($op eq '//') { - # -> should have no spaces - } elsif ($op eq '->') { + # No spaces for: + # -> + # : when part of a bitfield + } elsif ($op eq '->' || $opv eq ':B') { if ($ctx =~ /Wx.|.xW/) { ERROR("spaces prohibited around that '$op' $at\n" . $hereptr); } @@ -1551,18 +1624,19 @@ sub process { } # '*' as part of a type definition -- reported already. - } elsif ($op eq '*' && $is_unary == 2) { + } elsif ($opv eq '*_') { #warn "'*' is part of type\n"; # unary operators should have a space before and # none after. May be left adjacent to another # unary operator, or a cast } elsif ($op eq '!' || $op eq '~' || - ($is_unary && ($op eq '*' || $op eq '-' || $op eq '&'))) { + $opv eq '*U' || $opv eq '-U' || + $opv eq '&U') { if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { ERROR("space required before that '$op' $at\n" . $hereptr); } - if ($op eq '*' && $cc =~/\s*const\b/) { + if ($op eq '*' && $cc =~/\s*const\b/) { # A unary '*' may be const } elsif ($ctx =~ /.xW/) { @@ -1595,11 +1669,33 @@ sub process { $hereptr); } + # A colon needs no spaces before when it is + # terminating a case value or a label. + } elsif ($opv eq ':C' || $opv eq ':L') { + if ($ctx =~ /Wx./) { + ERROR("space prohibited before that '$op' $at\n" . $hereptr); + } + # All the others need spaces both sides. } elsif ($ctx !~ /[EWC]x[CWE]/) { + my $ok = 0; + # Ignore email addresses <foo@bar> - if (!($op eq '<' && $cb =~ /$;\S+\@\S+>/) && - !($op eq '>' && $cb =~ /<\S+\@\S+$;/)) { + if (($op eq '<' && + $cc =~ /^\S+\@\S+>/) || + ($op eq '>' && + $ca =~ /<\S+\@\S+$/)) + { + $ok = 1; + } + + # Ignore ?: + if (($opv eq ':O' && $ca =~ /\?$/) || + ($op eq '?' && $cc =~ /^:/)) { + $ok = 1; + } + + if ($ok == 0) { ERROR("spaces required around that '$op' $at\n" . $hereptr); } } @@ -1670,6 +1766,7 @@ sub process { my $value = $2; # Flatten any parentheses and braces + $value =~ s/\)\(/\) \(/g; while ($value =~ s/\([^\(\)]*\)/1/) { } @@ -1686,8 +1783,9 @@ sub process { ERROR("space required before the open parenthesis '('\n" . $herecurr); } -# Check for illegal assignment in if conditional. - if ($line =~ /\bif\s*\(/) { +# Check for illegal assignment in if conditional -- and check for trailing +# statements after the conditional. + if ($line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { my ($s, $c) = ($stat, $cond); if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/) { @@ -1699,13 +1797,63 @@ sub process { substr($s, 0, length($c), ''); $s =~ s/\n.*//g; $s =~ s/$;//g; # Remove any comments - if (length($c) && $s !~ /^\s*({|;|)\s*\\*\s*$/ && - $c !~ /^.\s*\#\s*if/) + if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && + $c !~ /}\s*while\s*/) { ERROR("trailing statements should be on next line\n" . $herecurr); } } +# Check relative indent for conditionals and blocks. + if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + my ($s, $c) = ($stat, $cond); + + substr($s, 0, length($c), ''); + + # Make sure we remove the line prefixes as we have + # none on the first line, and are going to readd them + # where necessary. + $s =~ s/\n./\n/gs; + + # We want to check the first line inside the block + # starting at the end of the conditional, so remove: + # 1) any blank line termination + # 2) any opening brace { on end of the line + # 3) any do (...) { + my $continuation = 0; + my $check = 0; + $s =~ s/^.*\bdo\b//; + $s =~ s/^\s*{//; + if ($s =~ s/^\s*\\//) { + $continuation = 1; + } + if ($s =~ s/^\s*\n//) { + $check = 1; + } + + # Also ignore a loop construct at the end of a + # preprocessor statement. + if (($prevline =~ /^.\s*#\s*define\s/ || + $prevline =~ /\\\s*$/) && $continuation == 0) { + $check = 0; + } + + # Ignore the current line if its is a preprocessor + # line. + if ($s =~ /^\s*#\s*/) { + $check = 0; + } + + my (undef, $sindent) = line_stats("+" . $s); + + ##print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s>\n"; + + if ($check && (($sindent % 8) != 0 || + ($sindent <= $indent && $s ne ''))) { + WARN("suspect code indent for conditional statements\n" . $herecurr); + } + } + # Check for bitwise tests written as boolean if ($line =~ / (?: @@ -1777,7 +1925,8 @@ sub process { # multi-statement macros should be enclosed in a do while loop, grab the # first statement and ensure its the whole macro if its not enclosed # in a known good container - if ($line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { + if ($realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { my $ln = $linenr; my $cnt = $realcnt; my ($off, $dstat, $dcond, $rest); @@ -1791,30 +1940,26 @@ sub process { $lines[$ln - 1] =~ /^(?:-|..*\\$)/) { $ctx .= $rawlines[$ln - 1] . "\n"; + $cnt-- if ($lines[$ln - 1] !~ /^-/); $ln++; - $cnt--; } $ctx .= $rawlines[$ln - 1]; ($dstat, $dcond, $ln, $cnt, $off) = ctx_statement_block($linenr, $ln - $linenr + 1, 0); #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; - #print "LINE<$lines[$ln]> len<" . length($lines[$ln]) . "\n"; + #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; # Extract the remainder of the define (if any) and # rip off surrounding spaces, and trailing \'s. $rest = ''; - if (defined $lines[$ln - 1] && - $off > length($lines[$ln - 1])) - { - $ln++; - $cnt--; - $off = 0; - } - while ($cnt > 0) { - $rest .= substr($lines[$ln - 1], $off) . "\n"; + while ($off != 0 || ($cnt > 0 && $rest =~ /(?:^|\\)\s*$/)) { + #print "ADDING $off <" . substr($lines[$ln - 1], $off) . ">\n"; + if ($off != 0 || $lines[$ln - 1] !~ /^-/) { + $rest .= substr($lines[$ln - 1], $off) . "\n"; + $cnt--; + } $ln++; - $cnt--; $off = 0; } $rest =~ s/\\\n.//g; @@ -1827,6 +1972,7 @@ sub process { } else { $dstat =~ s/^.\s*\#\s*define\s+$Ident\s*//; } + $dstat =~ s/$;//g; $dstat =~ s/\\\n.//g; $dstat =~ s/^\s*//s; $dstat =~ s/\s*$//s; @@ -1845,6 +1991,7 @@ sub process { DEFINE_PER_CPU| __typeof__\( }x; + #print "REST<$rest>\n"; if ($rest ne '') { if ($rest !~ /while\s*\(/ && $dstat !~ /$exceptions/) @@ -2001,7 +2148,14 @@ sub process { if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { my $expr = $1; if ($line =~ /\bkfree\(\Q$expr\E\);/) { - WARN("kfree(NULL) is safe this check is probabally not required\n" . $hereprev); + WARN("kfree(NULL) is safe this check is probably not required\n" . $hereprev); + } + } +# check for needless usb_free_urb() checks + if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { + my $expr = $1; + if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) { + WARN("usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev); } } @@ -2106,6 +2260,10 @@ sub process { if ($line =~ /\bsimple_(strto.*?)\s*\(/) { WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr); } +# check for __initcall(), use device_initcall() explicitly please + if ($line =~ /^.\s*__initcall\s*\(/) { + WARN("please use device_initcall() instead of __initcall()\n" . $herecurr); + } # use of NR_CPUS is usually wrong # ignore definitions of NR_CPUS and usage to define arrays as likely right diff --git a/v4l/scripts/do_commit.sh b/v4l/scripts/do_commit.sh new file mode 100755 index 000000000..4fa4e1847 --- /dev/null +++ b/v4l/scripts/do_commit.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +EDITOR=$1 +WHITESPCE=$2 + +if [ "$WHITESPCE" == "" ]; then + exit 13 +fi + +TMPMSG=$1 + +scripts/cardlist +scripts/prep_commit_msg.pl $WHITESPCE > $TMPMSG + +#trap 'rm -rf $TMPMSG' EXIT + +CHECKSUM=`md5sum "$TMPMSG"` +$EDITOR $TMPMSG || exit $? +echo "$CHECKSUM" | md5sum -c --status && echo "*** commit message not changed. Aborting. ***" && exit 13 +DATE="`scripts/hghead.pl $TMPMSG|perl -ne 'if (m/\#[dD]ate:\s+(.*)/) { print $1; }'`" + +if [ "$DATE" != "" ]; then + echo Patch date is $DATE + scripts/hghead.pl $TMPMSG| grep -v '^#' | hg commit -d "$DATE" -l - +else + scripts/hghead.pl $TMPMSG| grep -v '^#' | hg commit -l - +fi + +if [ "$?" != "0" ]; then + echo "Couldn't apply the patch" + exit 13 +fi + +echo "*** PLEASE CHECK IF LOG IS OK:" +hg log -v -r -1 +echo "*** If not ok, do \"hg rollback\" and \"make commit\" again" diff --git a/v4l/scripts/headers_convert.pl b/v4l/scripts/headers_convert.pl new file mode 100755 index 000000000..800a453e1 --- /dev/null +++ b/v4l/scripts/headers_convert.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl +# +# headers_install prepare the listed header files for use in +# user space and copy the files to their destination. +# + +use strict; +use warnings; + +foreach (@ARGV) { + my $file = $_; + my $tmpfile = $file . ".tmp"; + + open(my $infile, '<', "$file") + or die "$file: $!\n"; + open(my $outfile, '>', "$tmpfile") or die "$tmpfile: $!\n"; + while (my $line = <$infile>) { + $line =~ s/([\s(])__user\s/$1/g; + $line =~ s/([\s(])__force\s/$1/g; + $line =~ s/([\s(])__iomem\s/$1/g; + $line =~ s/\s__attribute_const__\s/ /g; + $line =~ s/\s__attribute_const__$//g; + $line =~ s/^#include <linux\/compiler.h>//; + printf $outfile "%s", $line; + } + close $outfile; + close $infile; + system "mv $tmpfile $file"; +} +exit 0; diff --git a/v4l/scripts/hghead.pl b/v4l/scripts/hghead.pl index 7dd40380a..dc989dd61 100755 --- a/v4l/scripts/hghead.pl +++ b/v4l/scripts/hghead.pl @@ -1,6 +1,5 @@ #!/usr/bin/perl use strict; -use Date::Parse; ################################################################# # analyse diffs @@ -46,18 +45,13 @@ while ($line = <IN>) { last; } - if ($line =~ m/^# Date\s*(.*)/) { + if ($line =~ m/^#\s*Date\s*(.*)/) { print "#Date: $1\n"; + next; } if ($line =~ m/^Date:\s*(.*)/) { - my $time = str2time($1); - - if ($time) { - print "#Date: $time 0\n"; - } else { - print "#Date: $1\n"; - } + print "#Date: $1\n"; next; } diff --git a/v4l/scripts/make_kconfig.pl b/v4l/scripts/make_kconfig.pl index b0dc16133..20b94f0c1 100755 --- a/v4l/scripts/make_kconfig.pl +++ b/v4l/scripts/make_kconfig.pl @@ -571,74 +571,6 @@ config VIDEO_KERNEL_VERSION Unless you know what you are doing, you should answer N. -config PREVENT_FIRMWARE_BUILD - default n - -config FIRMWARE_IN_KERNEL - default y - - bool "Include in-kernel firmware blobs in kernel binary" - depends on FW_LOADER - default y - help - The kernel source tree includes a number of firmware 'blobs' - which are used by various drivers. The recommended way to - use these is to run "make firmware_install" and to copy the - resulting binary files created in usr/lib/firmware directory - of the kernel tree to the /lib/firmware on your system so - that they can be loaded by userspace helpers on request. - - Enabling this option will build each required firmware blob - into the kernel directly, where request_firmware() will find - them without having to call out to userspace. This may be - useful if your root file system requires a device which uses - such firmware, and do not wish to use an initrd. - - This single option controls the inclusion of firmware for - every driver which usees request_firmare() and ships its - firmware in the kernel source tree, to avoid a proliferation - of 'Include firmware for xxx device' options. - - Say 'N' and let firmware be loaded from userspace. - -config EXTRA_FIRMWARE - string "External firmware blobs to build into the kernel binary" - depends on FW_LOADER - help - This option allows firmware to be built into the kernel, for the - cases where the user either cannot or doesn't want to provide it from - userspace at runtime (for example, when the firmware in question is - required for accessing the boot device, and the user doesn't want to - use an initrd). - This option is a string, and takes the (space-separated) names of the - firmware files -- the same names which appear in MODULE_FIRMWARE() - and request_firmware() in the source. These files should exist under - the directory specified by the EXTRA_FIRMWARE_DIR option, which is - by default the firmware/ subdirectory of the kernel source tree. - - So, for example, you might set CONFIG_EXTRA_FIRMWARE="usb8388.bin", - copy the usb8388.bin file into the firmware/ directory, and build the - kernel. Then any request_firmware("usb8388.bin") will be - satisfied internally without needing to call out to userspace. - - WARNING: If you include additional firmware files into your binary - kernel image which are not available under the terms of the GPL, - then it may be a violation of the GPL to distribute the resulting - image -- since it combines both GPL and non-GPL work. You should - consult a lawyer of your own before distributing such an image. - -config EXTRA_FIRMWARE_DIR - string "Firmware blobs root directory" - depends on EXTRA_FIRMWARE != "" - default "firmware" - help - This option controls the directory in which the kernel build system - looks for the firmware files listed in the EXTRA_FIRMWARE option. - The default is the firmware/ directory in the kernel source tree, - but by changing this option you can point it elsewhere, such as - the /lib/firmware/ directory or another separate directory - containing firmware files. - EOF open_kconfig('../linux', '../linux/drivers/media/Kconfig'); @@ -651,9 +583,6 @@ disable_config('DVB_CINERGYT2_TUNING'); disable_config('DVB_FE_CUSTOMISE'); disable_config('VIDEO_HELPER_CHIPS_AUTO'); -# ACI needs some kernel includes that might not be there -disable_config('SOUND_ACI_MIXER') if (! -e "$kernsrc/sound/oss/sound_config.h"); - # Check dependencies my %newconfig = checkdeps(); diff --git a/v4l/versions.txt b/v4l/versions.txt index acdee0ec1..d3efd816a 100644 --- a/v4l/versions.txt +++ b/v4l/versions.txt @@ -1,6 +1,10 @@ # Use this for stuff for drivers that don't compile [2.6.99] +[2.6.26] +# Needs camera.h +VIDEO_PXA27x + [2.6.25] # Requires gpiolib SOC_CAMERA diff --git a/v4l2-apps/Makefile b/v4l2-apps/Makefile index 550473435..2bba8fcd4 100644 --- a/v4l2-apps/Makefile +++ b/v4l2-apps/Makefile @@ -1,11 +1,24 @@ # Makefile for linuxtv.org v4l2-apps -.PHONY: all clean install +.PHONY: all distclean clean install -all clean install: +all:: prepare-includes + +all clean install:: $(MAKE) -C lib $@ $(MAKE) -C util $@ $(MAKE) -C test $@ %: make -C .. $(MAKECMDGOALS) + +clean:: + -$(RM) -rf include + +distclean:: clean + +prepare-includes: + -if [ ! -d include ]; then \ + cp -r ../linux/include include ; \ + ../v4l/scripts/headers_convert.pl `find include -type f` ; \ + fi diff --git a/v4l2-apps/lib/Makefile b/v4l2-apps/lib/Makefile index f123f3380..8443039b4 100644 --- a/v4l2-apps/lib/Makefile +++ b/v4l2-apps/lib/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/lib -CPPFLAGS += -I../../linux/include -I.. +CPPFLAGS += -I../include -I.. includes = v4l2.h diff --git a/v4l2-apps/lib/frequencies.c b/v4l2-apps/lib/frequencies.c index 77a79e61c..64379d2b0 100644 --- a/v4l2-apps/lib/frequencies.c +++ b/v4l2-apps/lib/frequencies.c @@ -21,7 +21,7 @@ #include <stdlib.h> -#include "linux/videodev2.h" +#include <linux/videodev2.h> #include "v4l2.h" /* This source was originally written by Nathan Laredo <laredo@gnu.org>. diff --git a/v4l2-apps/lib/libv4l/libv4l1/Makefile b/v4l2-apps/lib/libv4l/libv4l1/Makefile index d12e747fb..27848477e 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l1/Makefile @@ -1,4 +1,4 @@ -override CPPFLAGS += -I../include -I../../../../linux/include -fvisibility=hidden +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes diff --git a/v4l2-apps/lib/libv4l/libv4l2/Makefile b/v4l2-apps/lib/libv4l/libv4l2/Makefile index 5ed35934a..648d27c0c 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l2/Makefile @@ -1,4 +1,4 @@ -override CPPFLAGS += -I../include -I../../../../linux/include -fvisibility=hidden +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/Makefile b/v4l2-apps/lib/libv4l/libv4lconvert/Makefile index 798194434..641d19d6e 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/Makefile +++ b/v4l2-apps/lib/libv4l/libv4lconvert/Makefile @@ -1,4 +1,4 @@ -override CPPFLAGS += -I../include -I../../../../linux/include -fvisibility=hidden +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes diff --git a/v4l2-apps/test/Makefile b/v4l2-apps/test/Makefile index ae8868c21..faafed14d 100644 --- a/v4l2-apps/test/Makefile +++ b/v4l2-apps/test/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/test -CPPFLAGS += -I../../linux/include +CPPFLAGS += -I../include binaries = ioctl-test \ sliced-vbi-test \ diff --git a/v4l2-apps/test/capture_example.c b/v4l2-apps/test/capture_example.c index b2260b05f..a15ef3c09 100644 --- a/v4l2-apps/test/capture_example.c +++ b/v4l2-apps/test/capture_example.c @@ -36,22 +36,21 @@ typedef enum { } io_method; struct buffer { - void * start; - size_t length; + void *start; + size_t length; }; -static char * dev_name = NULL; -static io_method io = IO_METHOD_MMAP; -static int fd = -1; -struct buffer * buffers = NULL; -static unsigned int n_buffers = 0; -static int out_buf = 0; +static char *dev_name; +static io_method io = IO_METHOD_MMAP; +static int fd = -1; +struct buffer *buffers; +static unsigned int n_buffers; +static int out_buf; +static int force_format; static void errno_exit(const char *s) { - fprintf(stderr, "%s error %d, %s\n", - s, errno, strerror(errno)); - + fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); exit(EXIT_FAILURE); } @@ -68,11 +67,11 @@ static int xioctl(int fh, int request, void *arg) static void process_image(const void *p, int size) { - if (!out_buf) - fputc('.', stdout); - else + if (out_buf) fwrite(p, size, 1, stdout); + fflush(stderr); + fprintf(stderr, "."); fflush(stdout); } @@ -99,7 +98,6 @@ static int read_frame(void) } process_image(buffers[0].start, buffers[0].length); - break; case IO_METHOD_MMAP: @@ -125,11 +123,10 @@ static int read_frame(void) assert(buf.index < n_buffers); - process_image(buffers[buf.index].start, buffers[buf.index].length); + process_image(buffers[buf.index].start, buf.bytesused); if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); - break; case IO_METHOD_USERPTR: @@ -154,17 +151,16 @@ static int read_frame(void) } for (i = 0; i < n_buffers; ++i) - if (buf.m.userptr == (unsigned long) buffers[i].start + if (buf.m.userptr == (unsigned long)buffers[i].start && buf.length == buffers[i].length) break; assert(i < n_buffers); - process_image((void *) buf.m.userptr, buf.length); + process_image((void *)buf.m.userptr, buf.bytesused); if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); - break; } @@ -195,7 +191,6 @@ static void mainloop(void) if (-1 == r) { if (EINTR == errno) continue; - errno_exit("select"); } @@ -206,7 +201,6 @@ static void mainloop(void) if (read_frame()) break; - /* EAGAIN - continue select loop. */ } } @@ -224,10 +218,8 @@ static void stop_capturing(void) case IO_METHOD_MMAP: case IO_METHOD_USERPTR: type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) errno_exit("VIDIOC_STREAMOFF"); - break; } } @@ -247,20 +239,16 @@ static void start_capturing(void) struct v4l2_buffer buf; CLEAR(buf); - - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = i; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); } - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) errno_exit("VIDIOC_STREAMON"); - break; case IO_METHOD_USERPTR: @@ -268,22 +256,18 @@ static void start_capturing(void) struct v4l2_buffer buf; CLEAR(buf); - - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_USERPTR; - buf.index = i; - buf.m.userptr = (unsigned long) buffers[i].start; - buf.length = buffers[i].length; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + buf.index = i; + buf.m.userptr = (unsigned long)buffers[i].start; + buf.length = buffers[i].length; if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); } - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) errno_exit("VIDIOC_STREAMON"); - break; } } @@ -336,9 +320,9 @@ static void init_mmap(void) CLEAR(req); - req.count = 4; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_MMAP; + req.count = 4; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { if (EINVAL == errno) { @@ -457,7 +441,6 @@ static void init_device(void) dev_name); exit(EXIT_FAILURE); } - break; case IO_METHOD_MMAP: @@ -467,7 +450,6 @@ static void init_device(void) dev_name); exit(EXIT_FAILURE); } - break; } @@ -500,16 +482,22 @@ static void init_device(void) CLEAR(fmt); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = 640; - fmt.fmt.pix.height = 480; - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (force_format) { + fmt.fmt.pix.width = 640; + fmt.fmt.pix.height = 480; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; - if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) - errno_exit("VIDIOC_S_FMT"); + if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) + errno_exit("VIDIOC_S_FMT"); - /* Note VIDIOC_S_FMT may change width and height. */ + /* Note VIDIOC_S_FMT may change width and height. */ + } else { + /* Preserve original settings as set by v4l2-ctl for example */ + if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) + errno_exit("VIDIOC_G_FMT"); + } /* Buggy driver paranoia. */ min = fmt.fmt.pix.width * 2; @@ -571,40 +559,41 @@ static void usage(FILE *fp, int argc, char **argv) fprintf(fp, "Usage: %s [options]\n\n" "Options:\n" - "-d | --device name Video device name [/dev/video]\n" + "-d | --device name Video device name [/dev/video0]\n" "-h | --help Print this message\n" "-m | --mmap Use memory mapped buffers\n" "-r | --read Use read() calls\n" "-u | --userp Use application allocated buffers\n" "-o | --output Outputs stream to stdout\n" + "-f | --format Force format to 640x480 YUYV\n" "", argv[0]); } -static const char short_options [] = "d:hmruo"; +static const char short_options[] = "d:hmruof"; static const struct option -long_options [] = { - { "device", required_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - { "mmap", no_argument, NULL, 'm' }, - { "read", no_argument, NULL, 'r' }, - { "userp", no_argument, NULL, 'u' }, - { "output", no_argument, NULL, 'o' }, +long_options[] = { + { "device", required_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "mmap", no_argument, NULL, 'm' }, + { "read", no_argument, NULL, 'r' }, + { "userp", no_argument, NULL, 'u' }, + { "output", no_argument, NULL, 'o' }, + { "format", no_argument, NULL, 'f' }, { 0, 0, 0, 0 } }; int main(int argc, char **argv) { - dev_name = "/dev/video"; + dev_name = "/dev/video0"; for (;;) { int idx; int c; c = getopt_long(argc, argv, - short_options, long_options, - &idx); + short_options, long_options, &idx); if (-1 == c) break; @@ -637,6 +626,10 @@ int main(int argc, char **argv) out_buf++; break; + case 'f': + force_format++; + break; + default: usage(stderr, argc, argv); exit(EXIT_FAILURE); @@ -650,5 +643,6 @@ int main(int argc, char **argv) stop_capturing(); uninit_device(); close_device(); + fprintf(stderr, "\n"); return 0; } diff --git a/v4l2-apps/test/ioctl-test.c b/v4l2-apps/test/ioctl-test.c index f483338fb..99ad717e5 100644 --- a/v4l2-apps/test/ioctl-test.c +++ b/v4l2-apps/test/ioctl-test.c @@ -42,7 +42,7 @@ typedef __u8 u8; typedef __u32 u32; #include <linux/version.h> -#include "../linux/include/media/v4l2-common.h" +#include <media/v4l2-common.h> #include <linux/video_decoder.h> #else typedef u_int32_t u32; diff --git a/v4l2-apps/util/Makefile b/v4l2-apps/util/Makefile index 41d4712e1..dcef94e0f 100644 --- a/v4l2-apps/util/Makefile +++ b/v4l2-apps/util/Makefile @@ -4,10 +4,10 @@ ifeq ($(KERNEL_DIR),) KERNEL_DIR = /usr endif -CPPFLAGS += -I../../linux/include -D_GNU_SOURCE +CPPFLAGS += -I../include -D_GNU_SOURCE LDFLAGS += -lm -binaries = v4l2-ctl v4l2-dbg ivtv-ctl cx18-ctl v4l-board-dbg +binaries = v4l2-ctl v4l2-dbg v4l2-compliance ivtv-ctl cx18-ctl ifeq ($(prefix),) prefix = /usr @@ -26,12 +26,14 @@ clean:: rm -rf keycodes parse.h keytable qv4l2: - if [ ! -f qv4l2/Makefile ]; then (cd qv4l2; qmake); fi - make -C qv4l2 + -if [ ! -f qv4l2/Makefile ]; then (cd qv4l2; qmake); fi + $(MAKE) -C qv4l2 v4l2-dbg: v4l2-dbg.o v4l2-driverids.o v4l2-chipids.o $(CXX) $^ -o $@ +v4l2-dbg.o: v4l2-dbg.h v4l2-dbg-bttv.h v4l2-dbg-em28xx.h v4l2-dbg-saa7134.h + install: mkdir -p $(prefix)/bin cp $(binaries) $(prefix)/bin @@ -57,14 +59,12 @@ keytables: keytable: keytable.c parse.h keytables -v4l-board-dbg: v4l-board-dbg.c bttv-dbg.h saa7134-dbg.h em28xx-dbg.h - -v4l2-driverids.cpp: ../../linux/include/linux/i2c-id.h +v4l2-driverids.cpp: ../include/linux/i2c-id.h @echo "struct driverid { const char *name; unsigned id; } driverids[] = {" >$@ @grep I2C_DRIVERID_ $^ | sed -e 's/.*I2C_DRIVERID_\([0-9A-Z_]*\)[^0-9]*\([0-9]*\).*/{ "\1", \2 },/' | tr A-Z a-z >>$@ @echo "{ 0, 0 }};" >>$@ -v4l2-chipids.cpp: ../../linux/include/media/v4l2-chip-ident.h +v4l2-chipids.cpp: ../include/media/v4l2-chip-ident.h @echo "struct chipid { const char *name; unsigned id; } chipids[] = {" >$@ @grep V4L2_IDENT_ $^ | sed -e 's/.*V4L2_IDENT_\([0-9A-Z_]*\)[^=]*=[^0-9]*\([0-9]*\).*/{ "\1", \2 },/' | tr A-Z a-z >>$@ @echo "{ 0, 0 }};" >>$@ diff --git a/v4l2-apps/util/ivtv-ctl.c b/v4l2-apps/util/ivtv-ctl.c index ab836e240..af84164e3 100644 --- a/v4l2-apps/util/ivtv-ctl.c +++ b/v4l2-apps/util/ivtv-ctl.c @@ -351,9 +351,9 @@ int main(int argc, char **argv) unsigned short gpio_dir = 0x0; /* GPIO direction bits */ int gpio_set_dir = 0; int passthrough = 0; - int audio_mute = 0; - int stereo_mode = 0; - int bilingual_mode = 0; + long audio_mute = 0; + long stereo_mode = 0; + long bilingual_mode = 0; int debug_level = 0; __u32 reset = 0; int new_debug_level, gdebug_level; @@ -621,7 +621,7 @@ int main(int argc, char **argv) } if (options[OptPassThrough]) { - int source = passthrough ? VIDEO_SOURCE_DEMUX : VIDEO_SOURCE_MEMORY; + long source = passthrough ? VIDEO_SOURCE_DEMUX : VIDEO_SOURCE_MEMORY; doioctl(fd, VIDEO_SELECT_SOURCE, (void *)source, "IVTV_IOC_PASSTHROUGH"); diff --git a/v4l2-apps/util/qv4l2/qv4l2.pro b/v4l2-apps/util/qv4l2/qv4l2.pro index c53a098b5..5e0b7555d 100644 --- a/v4l2-apps/util/qv4l2/qv4l2.pro +++ b/v4l2-apps/util/qv4l2/qv4l2.pro @@ -3,7 +3,7 @@ ###################################################################### TEMPLATE = app -INCLUDEPATH += . ../../../linux/include ../../lib +INCLUDEPATH += . ../../include ../../lib CONFIG += debug # Input diff --git a/v4l2-apps/util/rds/Makefile b/v4l2-apps/util/rds/Makefile index 4f6ebd9f2..fb2213c99 100644 --- a/v4l2-apps/util/rds/Makefile +++ b/v4l2-apps/util/rds/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/util/xc3028-firmware -CPPFLAGS += -I../../../linux/include +CPPFLAGS += -I../../include binaries = rds-saa6588 diff --git a/v4l2-apps/util/v4l-board-dbg.c b/v4l2-apps/util/v4l-board-dbg.c deleted file mode 100644 index e74c7300e..000000000 --- a/v4l2-apps/util/v4l-board-dbg.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - Copyright (C) 2008 Mauro Carvalho Chehab <mchehab@infradead.org> - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <getopt.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/ioctl.h> -#include <sys/time.h> -#include <linux/types.h> -#include <linux/videodev2.h> - -#include "bttv-dbg.h" -#include "saa7134-dbg.h" -#include "em28xx-dbg.h" - -#define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0]))) - -struct board_list { - char *name; - int prefix; /* Register prefix size */ - struct board_regs *regs; - int regs_size; - struct board_regs *alt_regs; - int alt_regs_size; -}; - -struct board_list boards[] = { - [0] = { /* From bttv-dbg.h */ - .name = BTTV_IDENT, - .prefix = sizeof(BTTV_PREFIX) - 1, - .regs = bt8xx_regs, - .regs_size = ARRAY_SIZE(bt8xx_regs), - .alt_regs = bt8xx_regs_other, - .alt_regs_size = ARRAY_SIZE(bt8xx_regs_other), - }, - [1] = { /* From saa7134-dbg.h */ - .name = SAA7134_IDENT, - .prefix = sizeof(SAA7134_PREFIX) - 1, - .regs = saa7134_regs, - .regs_size = ARRAY_SIZE(saa7134_regs), - .alt_regs = NULL, - .alt_regs_size = 0, - }, - [2] = { /* From em28xx-dbg.h */ - .name = EM28XX_IDENT, - .prefix = sizeof(EM28XX_PREFIX) - 1, - .regs = em28xx_regs, - .regs_size = ARRAY_SIZE(em28xx_regs), - .alt_regs = NULL, - .alt_regs_size = 0, - }, -}; - -static int is_get=0, is_set=0; - -static int doioctl(int fd, int request, void *parm, const char *name) -{ - int retVal; - - printf("ioctl %s ", name); - retVal = ioctl(fd, request, parm); - if (retVal < 0) - printf("failed: %s\n", strerror(errno)); - else - printf("ok\n"); - - return retVal; -} - -static void usage(void) -{ - printf("bttv-dbg <args>\n"); -} - -enum Option { - OptGetReg = 'g', - OptSetReg = 's', - OptHelp = 'h', -}; - -static void print_bin (int val, int size) -{ - int i, j, v; - - printf("("); - for (i = size-1; i >= 0; i--) { - v = (val >> (i * 8)) & 0xff; - for (j = 7; j >= 0; j--) { - int bit = (v >> j) & 0x1; - if (bit) - printf("1"); - else - printf("0"); - } - if (i) - printf(" "); - else - printf(")"); - } -} - -int main(int argc, char **argv) -{ - char *device = strdup("/dev/video0"); - char *reg_set = NULL; - int ch; - int i; - int fd = -1; - struct v4l2_register reg; - struct v4l2_capability cap; - struct board_list *curr_bd; - int board = 0; - struct option long_options[] = { - /* Please keep in alphabetical order of the short option. - That makes it easier to see which options are still free. */ - {"get-reg", no_argument, 0, OptGetReg}, - {"set-reg", required_argument, 0, OptSetReg}, - {"help", no_argument, 0, OptHelp}, - {0, 0, 0, 0} - }; - - /* command args */ - if (argc == 1) { - usage(); - return 0; - } - while (1) { - int option_index = 0; - - ch = getopt_long(argc, argv, "gs:", long_options, &option_index); - if (ch == -1) - break; - - switch (ch) { - case OptHelp: - usage(); - return 0; - case OptGetReg: - is_get++; - break; - case OptSetReg: - is_set++; - reg_set = optarg; - - break; - case '?': - fprintf(stderr, "Unknown argument `%s'\n", - argv[optind]); - usage(); - return 1; - } - } - if (optind < argc) { - printf("unknown arguments: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - usage(); - return 1; - } - - fd = open(device, O_RDWR); - if (fd < 0) { - fprintf(stderr, "Failed to open %s: %s\n", device, - strerror(errno)); - exit(1); - } - free(device); - - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { - printf("Error while reading capabilities\n"); - exit(2); - } - - for (board = ARRAY_SIZE(boards)-1; board >= 0; board--) { - if (!strcasecmp((char *)cap.driver, boards[board].name)) - break; - } - if (board < 0) { - printf("This software doesn't support %s yet\n", cap.driver); - exit(3); - } - - curr_bd = &boards[board]; - - reg.match_type = V4L2_CHIP_MATCH_HOST; - reg.match_chip = 0; - - if (is_get) { - for (i = 0; i < curr_bd->regs_size; i++) { - char name[256]; - reg.reg = curr_bd->regs[i].reg; - if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) { - printf("Error while reading. Maybe you're not root?\n"); - continue; - } - sprintf(name, "%s:", curr_bd->regs[i].name); - - switch (curr_bd->regs[i].size) { - case 1: - printf("%-32s %02llx ", name, reg.val & 0xff); - break; - case 2: - printf("%-32s %04llx ", name, reg.val & 0xffff); - break; - case 4: - printf("%-32s %08llx ", name, reg.val & 0xffffffff); - break; - } - print_bin (reg.val, curr_bd->regs[i].size); - printf("\n"); - } - return 0; - } - - if (is_set) { - char *reg_name; - int val; - int r, size; - unsigned prev; - struct board_regs *bd_reg; - - reg_name = strtok(reg_set, "=:"); - val = strtol(strtok(NULL, "=:"), 0L, 0); - - if (!reg_name) { - printf("set argument is invalid\n"); - return -1; - } - - for (i = curr_bd->regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->regs[i].name)) { - bd_reg = &curr_bd->regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - - if (i < 0) { - for (i = curr_bd->alt_regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->alt_regs[i].name)) { - bd_reg = &curr_bd->alt_regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - } - - if (i < 0) { - for (i = curr_bd->regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->regs[i].name + curr_bd->prefix)) { - bd_reg = &curr_bd->regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - } - - if (i < 0) { - for (i = curr_bd->alt_regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->alt_regs[i].name + curr_bd->prefix)) { - bd_reg = &curr_bd->regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - } - - if (i < 0) { - printf("Register not found\n"); - return -1; - } - - reg.reg = r; - if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) { - printf("Error while reading register 0x%02x\n", r); - return -1; - } - prev = reg.val; - - switch (size) { - case 1: - reg.val = (reg.val & (~0xff)) | val; - break; - case 2: - reg.val = (reg.val & (~0xffff)) | val; - break; - case 4: - reg.val = val; - break; - } - - printf("Changing value of register %s(0x%x) from 0x%02x to 0x%02x\n", - bd_reg->name, r, prev, (unsigned int)reg.val); - - prev = reg.val; - - if (ioctl(fd, VIDIOC_DBG_S_REGISTER, ®) < 0) { - printf("Error while writing\n"); - return -1; - } - if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) { - printf("Error while reading register 0x%02x\n", r); - return -1; - } - if (reg.val != prev) { - printf("Value of register %s(0x%x) is now 0x%02x\n", - bd_reg->name, r, (unsigned int)reg.val); - } - } - - close(fd); - exit(0); -} diff --git a/v4l2-apps/util/v4l2-compliance.cpp b/v4l2-apps/util/v4l2-compliance.cpp new file mode 100644 index 000000000..2fe5292fe --- /dev/null +++ b/v4l2-apps/util/v4l2-compliance.cpp @@ -0,0 +1,1122 @@ +/* + V4L2 API compliance test tool. + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <unistd.h> +#include <features.h> /* Uses _GNU_SOURCE to define getsubopt in stdlib.h */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <math.h> +#include <sys/klog.h> + +#include <linux/videodev2.h> + +#include <list> +#include <vector> +#include <map> +#include <string> + +/* Short option list + + Please keep in alphabetical order. + That makes it easier to see which short options are still free. + + In general the lower case is used to set something and the upper + case is used to retrieve a setting. */ +enum Option { + OptSetDevice = 'd', + OptGetDriverInfo = 'D', + OptHelp = 'h', + OptTest = 't', + OptVerbose = 'v', + OptLast = 256 +}; + +enum Test { + TestCap = 0, + TestChipIdent, + TestRegister, + TestLogStatus, + TestMax +}; + +static int test[TestMax]; + +static char options[OptLast]; + +static int app_result; +static int verbose; + +static unsigned caps; + +typedef std::vector<struct v4l2_ext_control> ctrl_list; +static ctrl_list user_ctrls; +static ctrl_list mpeg_ctrls; + +typedef std::map<std::string, unsigned> ctrl_strmap; +static ctrl_strmap ctrl_str2id; +typedef std::map<unsigned, std::string> ctrl_idmap; +static ctrl_idmap ctrl_id2str; + +typedef std::list<std::string> ctrl_get_list; +static ctrl_get_list get_ctrls; + +typedef std::map<std::string,std::string> ctrl_set_map; +static ctrl_set_map set_ctrls; + +typedef struct { + unsigned flag; + const char *str; +} flag_def; + +static const flag_def service_def[] = { + { V4L2_SLICED_TELETEXT_B, "teletext" }, + { V4L2_SLICED_VPS, "vps" }, + { V4L2_SLICED_CAPTION_525, "cc" }, + { V4L2_SLICED_WSS_625, "wss" }, + { 0, NULL } +}; + +/* fmts specified */ +#define FmtWidth (1L<<0) +#define FmtHeight (1L<<1) +#define FmtChromaKey (1L<<2) +#define FmtGlobalAlpha (1L<<3) + +/* crop specified */ +#define CropWidth (1L<<0) +#define CropHeight (1L<<1) +#define CropLeft (1L<<2) +#define CropTop (1L<<3) + +static struct option long_options[] = { + {"device", required_argument, 0, OptSetDevice}, + {"help", no_argument, 0, OptHelp}, + {"info", no_argument, 0, OptGetDriverInfo}, + {"verbose", no_argument, 0, OptVerbose}, + {"test", required_argument, 0, OptTest}, + {0, 0, 0, 0} +}; + +static void usage(void) +{ + printf("Usage:\n"); + printf("Common options:\n"); + printf(" -D, --info show driver info [VIDIOC_QUERYCAP]\n"); + printf(" -d, --device=<dev> use device <dev> instead of /dev/video0\n"); + printf(" if <dev> is a single digit, then /dev/video<dev> is used\n"); + printf(" -h, --help display this help message\n"); + printf(" -t, --test=<num> run specified test.\n"); + printf(" By default all tests are run.\n"); + printf(" 0 = test VIDIOC_QUERYCAP\n"); + printf(" -v, --verbose turn on verbose ioctl error reporting.\n"); + exit(0); +} + +static std::string num2s(unsigned num) +{ + char buf[10]; + + sprintf(buf, "%08x", num); + return buf; +} + +static std::string buftype2s(int type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return "Video Capture"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return "Video Output"; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return "Video Overlay"; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return "VBI Capture"; + case V4L2_BUF_TYPE_VBI_OUTPUT: + return "VBI Output"; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return "Sliced VBI Capture"; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return "Sliced VBI Output"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + return "Video Output Overlay"; + case V4L2_BUF_TYPE_PRIVATE: + return "Private"; + default: + return "Unknown (" + num2s(type) + ")"; + } +} + +static std::string fcc2s(unsigned int val) +{ + std::string s; + + s += val & 0xff; + s += (val >> 8) & 0xff; + s += (val >> 16) & 0xff; + s += (val >> 24) & 0xff; + return s; +} + +static std::string field2s(int val) +{ + switch (val) { + case V4L2_FIELD_ANY: + return "Any"; + case V4L2_FIELD_NONE: + return "None"; + case V4L2_FIELD_TOP: + return "Top"; + case V4L2_FIELD_BOTTOM: + return "Bottom"; + case V4L2_FIELD_INTERLACED: + return "Interlaced"; + case V4L2_FIELD_SEQ_TB: + return "Sequential Top-Bottom"; + case V4L2_FIELD_SEQ_BT: + return "Sequential Bottom-Top"; + case V4L2_FIELD_ALTERNATE: + return "Alternating"; + case V4L2_FIELD_INTERLACED_TB: + return "Interlaced Top-Bottom"; + case V4L2_FIELD_INTERLACED_BT: + return "Interlaced Bottom-Top"; + default: + return "Unknown (" + num2s(val) + ")"; + } +} + +static std::string colorspace2s(int val) +{ + switch (val) { + case V4L2_COLORSPACE_SMPTE170M: + return "Broadcast NTSC/PAL (SMPTE170M/ITU601)"; + case V4L2_COLORSPACE_SMPTE240M: + return "1125-Line (US) HDTV (SMPTE240M)"; + case V4L2_COLORSPACE_REC709: + return "HDTV and modern devices (ITU709)"; + case V4L2_COLORSPACE_BT878: + return "Broken Bt878"; + case V4L2_COLORSPACE_470_SYSTEM_M: + return "NTSC/M (ITU470/ITU601)"; + case V4L2_COLORSPACE_470_SYSTEM_BG: + return "PAL/SECAM BG (ITU470/ITU601)"; + case V4L2_COLORSPACE_JPEG: + return "JPEG (JFIF/ITU601)"; + case V4L2_COLORSPACE_SRGB: + return "SRGB"; + default: + return "Unknown (" + num2s(val) + ")"; + } +} + +static std::string flags2s(unsigned val, const flag_def *def) +{ + std::string s; + + while (def->flag) { + if (val & def->flag) { + if (s.length()) s += " "; + s += def->str; + } + def++; + } + return s; +} + +static void print_sliced_vbi_cap(struct v4l2_sliced_vbi_cap &cap) +{ + printf("\tType : %s\n", buftype2s(cap.type).c_str()); + printf("\tService Set : %s\n", + flags2s(cap.service_set, service_def).c_str()); + for (int i = 0; i < 24; i++) { + printf("\tService Line %2d: %8s / %-8s\n", i, + flags2s(cap.service_lines[0][i], service_def).c_str(), + flags2s(cap.service_lines[1][i], service_def).c_str()); + } +} + +static std::string name2var(unsigned char *name) +{ + std::string s; + + while (*name) { + if (*name == ' ') s += "_"; + else s += std::string(1, tolower(*name)); + name++; + } + return s; +} + +static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl, + struct v4l2_ext_control *ctrl, int show_menus) +{ + struct v4l2_querymenu qmenu = { 0 }; + std::string s = name2var(queryctrl->name); + int i; + + qmenu.id = queryctrl->id; + switch (queryctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + printf("%31s (int) : min=%d max=%d step=%d default=%d value=%d", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->step, queryctrl->default_value, + ctrl->value); + break; + case V4L2_CTRL_TYPE_INTEGER64: + printf("%31s (int64): value=%lld", s.c_str(), ctrl->value64); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + printf("%31s (bool) : default=%d value=%d", + s.c_str(), + queryctrl->default_value, ctrl->value); + break; + case V4L2_CTRL_TYPE_MENU: + printf("%31s (menu) : min=%d max=%d default=%d value=%d", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->default_value, ctrl->value); + break; + case V4L2_CTRL_TYPE_BUTTON: + printf("%31s (button)\n", s.c_str()); + break; + default: break; + } + if (queryctrl->flags) { + const flag_def def[] = { + { V4L2_CTRL_FLAG_GRABBED, "grabbed" }, + { V4L2_CTRL_FLAG_READ_ONLY, "readonly" }, + { V4L2_CTRL_FLAG_UPDATE, "update" }, + { V4L2_CTRL_FLAG_INACTIVE, "inactive" }, + { V4L2_CTRL_FLAG_SLIDER, "slider" }, + { 0, NULL } + }; + printf(" flags=%s", flags2s(queryctrl->flags, def).c_str()); + } + printf("\n"); + if (queryctrl->type == V4L2_CTRL_TYPE_MENU && show_menus) { + for (i = 0; i <= queryctrl->maximum; i++) { + qmenu.index = i; + if (ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) + continue; + printf("\t\t\t\t%d: %s\n", i, qmenu.name); + } + } +} + +static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus) +{ + struct v4l2_control ctrl = { 0 }; + struct v4l2_ext_control ext_ctrl = { 0 }; + struct v4l2_ext_controls ctrls = { 0 }; + + if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) + return 1; + if (qctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) { + printf("\n%s\n\n", qctrl.name); + return 1; + } + ext_ctrl.id = qctrl.id; + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(qctrl.id); + ctrls.count = 1; + ctrls.controls = &ext_ctrl; + if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_USER && + qctrl.id < V4L2_CID_PRIVATE_BASE) { + if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls)) { + printf("error %d getting ext_ctrl %s\n", + errno, qctrl.name); + return 0; + } + } + else { + ctrl.id = qctrl.id; + if (ioctl(fd, VIDIOC_G_CTRL, &ctrl)) { + printf("error %d getting ctrl %s\n", + errno, qctrl.name); + return 0; + } + ext_ctrl.value = ctrl.value; + } + print_qctrl(fd, &qctrl, &ext_ctrl, show_menus); + return 1; +} + +static void list_controls(int fd, int show_menus) +{ + struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL }; + int id; + + while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + print_control(fd, qctrl, show_menus); + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + return; + for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { + qctrl.id = id; + if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) + print_control(fd, qctrl, show_menus); + } + for (qctrl.id = V4L2_CID_PRIVATE_BASE; + ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + print_control(fd, qctrl, show_menus); + } +} + +static void find_controls(int fd) +{ + struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL }; + int id; + + while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS && + !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + ctrl_id2str[qctrl.id] = name2var(qctrl.name); + } + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + return; + for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { + qctrl.id = id; + if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0 && + !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + } + for (qctrl.id = V4L2_CID_PRIVATE_BASE; + ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + } +} + +static std::string fbufcap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_FBUF_CAP_EXTERNOVERLAY) + s += "\t\t\tExtern Overlay\n"; + if (cap & V4L2_FBUF_CAP_CHROMAKEY) + s += "\t\t\tChromakey\n"; + if (cap & V4L2_FBUF_CAP_GLOBAL_ALPHA) + s += "\t\t\tGlobal Alpha\n"; + if (cap & V4L2_FBUF_CAP_LOCAL_ALPHA) + s += "\t\t\tLocal Alpha\n"; + if (cap & V4L2_FBUF_CAP_LOCAL_INV_ALPHA) + s += "\t\t\tLocal Inverted Alpha\n"; + if (cap & V4L2_FBUF_CAP_LIST_CLIPPING) + s += "\t\t\tClipping List\n"; + if (cap & V4L2_FBUF_CAP_BITMAP_CLIPPING) + s += "\t\t\tClipping Bitmap\n"; + if (s.empty()) s += "\t\t\t\n"; + return s; +} + +static std::string fbufflags2s(unsigned fl) +{ + std::string s; + + if (fl & V4L2_FBUF_FLAG_PRIMARY) + s += "\t\t\tPrimary Graphics Surface\n"; + if (fl & V4L2_FBUF_FLAG_OVERLAY) + s += "\t\t\tOverlay Matches Capture/Output Size\n"; + if (fl & V4L2_FBUF_FLAG_CHROMAKEY) + s += "\t\t\tChromakey\n"; + if (fl & V4L2_FBUF_FLAG_GLOBAL_ALPHA) + s += "\t\t\tGlobal Alpha\n"; + if (fl & V4L2_FBUF_FLAG_LOCAL_ALPHA) + s += "\t\t\tLocal Alpha\n"; + if (fl & V4L2_FBUF_FLAG_LOCAL_INV_ALPHA) + s += "\t\t\tLocal Inverted Alpha\n"; + if (s.empty()) s += "\t\t\t\n"; + return s; +} + +static void printfbuf(const struct v4l2_framebuffer &fb) +{ + int is_ext = fb.capability & V4L2_FBUF_CAP_EXTERNOVERLAY; + + printf("Framebuffer Format:\n"); + printf("\tCapability : %s", fbufcap2s(fb.capability).c_str() + 3); + printf("\tFlags : %s", fbufflags2s(fb.flags).c_str() + 3); + if (fb.base) + printf("\tBase : 0x%p\n", fb.base); + printf("\tWidth : %d\n", fb.fmt.width); + printf("\tHeight : %d\n", fb.fmt.height); + printf("\tPixel Format : %s\n", fcc2s(fb.fmt.pixelformat).c_str()); + if (!is_ext) { + printf("\tBytes per Line: %d\n", fb.fmt.bytesperline); + printf("\tSize image : %d\n", fb.fmt.sizeimage); + printf("\tColorspace : %s\n", colorspace2s(fb.fmt.colorspace).c_str()); + if (fb.fmt.priv) + printf("\tCustom Info : %08x\n", fb.fmt.priv); + } +} + +static void printcrop(const struct v4l2_crop &crop) +{ + printf("Crop: Left %d, Top %d, Width %d, Height %d\n", + crop.c.left, crop.c.top, crop.c.width, crop.c.height); +} + +static void printcropcap(const struct v4l2_cropcap &cropcap) +{ + printf("Crop Capability %s:\n", buftype2s(cropcap.type).c_str()); + printf("\tBounds : Left %d, Top %d, Width %d, Height %d\n", + cropcap.bounds.left, cropcap.bounds.top, cropcap.bounds.width, cropcap.bounds.height); + printf("\tDefault : Left %d, Top %d, Width %d, Height %d\n", + cropcap.defrect.left, cropcap.defrect.top, cropcap.defrect.width, cropcap.defrect.height); + printf("\tPixel Aspect: %u/%u\n", cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator); +} + +static void printfmt(struct v4l2_format vfmt) +{ + const flag_def vbi_def[] = { + { V4L2_VBI_UNSYNC, "unsynchronized" }, + { V4L2_VBI_INTERLACED, "interlaced" }, + { 0, NULL } + }; + printf("Format %s:\n", buftype2s(vfmt.type).c_str()); + + switch (vfmt.type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + printf("\tWidth/Height : %u/%u\n", vfmt.fmt.pix.width, vfmt.fmt.pix.height); + printf("\tPixel Format : %s\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str()); + printf("\tField : %s\n", field2s(vfmt.fmt.pix.field).c_str()); + printf("\tBytes per Line: %u\n", vfmt.fmt.pix.bytesperline); + printf("\tSize Image : %u\n", vfmt.fmt.pix.sizeimage); + printf("\tColorspace : %s\n", colorspace2s(vfmt.fmt.pix.colorspace).c_str()); + if (vfmt.fmt.pix.priv) + printf("\tCustom Info : %08x\n", vfmt.fmt.pix.priv); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + printf("\tLeft/Top : %d/%d\n", + vfmt.fmt.win.w.left, vfmt.fmt.win.w.top); + printf("\tWidth/Height: %d/%d\n", + vfmt.fmt.win.w.width, vfmt.fmt.win.w.height); + printf("\tField : %s\n", field2s(vfmt.fmt.win.field).c_str()); + printf("\tChroma Key : 0x%08x\n", vfmt.fmt.win.chromakey); + printf("\tGlobal Alpha: 0x%02x\n", vfmt.fmt.win.global_alpha); + printf("\tClip Count : %u\n", vfmt.fmt.win.clipcount); + printf("\tClip Bitmap : %s\n", vfmt.fmt.win.bitmap ? "Yes" : "No"); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + printf("\tSampling Rate : %u Hz\n", vfmt.fmt.vbi.sampling_rate); + printf("\tOffset : %u samples (%g secs after leading edge)\n", + vfmt.fmt.vbi.offset, + (double)vfmt.fmt.vbi.offset / (double)vfmt.fmt.vbi.sampling_rate); + printf("\tSamples per Line: %u\n", vfmt.fmt.vbi.samples_per_line); + printf("\tSample Format : %s\n", fcc2s(vfmt.fmt.vbi.sample_format).c_str()); + printf("\tStart 1st Field : %u\n", vfmt.fmt.vbi.start[0]); + printf("\tCount 1st Field : %u\n", vfmt.fmt.vbi.count[0]); + printf("\tStart 2nd Field : %u\n", vfmt.fmt.vbi.start[1]); + printf("\tCount 2nd Field : %u\n", vfmt.fmt.vbi.count[1]); + if (vfmt.fmt.vbi.flags) + printf("\tFlags : %s\n", flags2s(vfmt.fmt.vbi.flags, vbi_def).c_str()); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + printf("\tService Set : %s\n", + flags2s(vfmt.fmt.sliced.service_set, service_def).c_str()); + for (int i = 0; i < 24; i++) { + printf("\tService Line %2d: %8s / %-8s\n", i, + flags2s(vfmt.fmt.sliced.service_lines[0][i], service_def).c_str(), + flags2s(vfmt.fmt.sliced.service_lines[1][i], service_def).c_str()); + } + printf("\tI/O Size : %u\n", vfmt.fmt.sliced.io_size); + break; + case V4L2_BUF_TYPE_PRIVATE: + break; + } +} + +static void print_video_formats(int fd, enum v4l2_buf_type type) +{ + struct v4l2_fmtdesc fmt; + + fmt.index = 0; + fmt.type = type; + while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) { + printf("\tType : %s\n", buftype2s(type).c_str()); + printf("\tPixelformat : %s", fcc2s(fmt.pixelformat).c_str()); + if (fmt.flags) + printf(" (compressed)"); + printf("\n"); + printf("\tName : %s\n", fmt.description); + printf("\n"); + fmt.index++; + } +} + +static char *pts_to_string(char *str, unsigned long pts) +{ + static char buf[256]; + int hours, minutes, seconds, fracsec; + float fps; + int frame; + char *p = (str) ? str : buf; + + static const int MPEG_CLOCK_FREQ = 90000; + seconds = pts / MPEG_CLOCK_FREQ; + fracsec = pts % MPEG_CLOCK_FREQ; + + minutes = seconds / 60; + seconds = seconds % 60; + + hours = minutes / 60; + minutes = minutes % 60; + + fps = 30; + frame = (int)ceilf(((float)fracsec / (float)MPEG_CLOCK_FREQ) * fps); + + snprintf(p, sizeof(buf), "%d:%02d:%02d:%d", hours, minutes, seconds, + frame); + return p; +} + +static const char *audmode2s(int audmode) +{ + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: return "stereo"; + case V4L2_TUNER_MODE_LANG1: return "lang1"; + case V4L2_TUNER_MODE_LANG2: return "lang2"; + case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual"; + case V4L2_TUNER_MODE_MONO: return "mono"; + default: return "unknown"; + } +} + +static std::string rxsubchans2s(int rxsubchans) +{ + std::string s; + + if (rxsubchans & V4L2_TUNER_SUB_MONO) + s += "mono "; + if (rxsubchans & V4L2_TUNER_SUB_STEREO) + s += "stereo "; + if (rxsubchans & V4L2_TUNER_SUB_LANG1) + s += "lang1 "; + if (rxsubchans & V4L2_TUNER_SUB_LANG2) + s += "lang2 "; + return s; +} + +static std::string tcap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_TUNER_CAP_LOW) + s += "62.5 Hz "; + else + s += "62.5 kHz "; + if (cap & V4L2_TUNER_CAP_NORM) + s += "multi-standard "; + if (cap & V4L2_TUNER_CAP_STEREO) + s += "stereo "; + if (cap & V4L2_TUNER_CAP_LANG1) + s += "lang1 "; + if (cap & V4L2_TUNER_CAP_LANG2) + s += "lang2 "; + return s; +} + +static std::string cap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_CAP_VIDEO_CAPTURE) + s += "\t\tVideo Capture\n"; + if (cap & V4L2_CAP_VIDEO_OUTPUT) + s += "\t\tVideo Output\n"; + if (cap & V4L2_CAP_VIDEO_OVERLAY) + s += "\t\tVideo Overlay\n"; + if (cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY) + s += "\t\tVideo Output Overlay\n"; + if (cap & V4L2_CAP_VBI_CAPTURE) + s += "\t\tVBI Capture\n"; + if (cap & V4L2_CAP_VBI_OUTPUT) + s += "\t\tVBI Output\n"; + if (cap & V4L2_CAP_SLICED_VBI_CAPTURE) + s += "\t\tSliced VBI Capture\n"; + if (cap & V4L2_CAP_SLICED_VBI_OUTPUT) + s += "\t\tSliced VBI Output\n"; + if (cap & V4L2_CAP_RDS_CAPTURE) + s += "\t\tRDS Capture\n"; + if (cap & V4L2_CAP_TUNER) + s += "\t\tTuner\n"; + if (cap & V4L2_CAP_AUDIO) + s += "\t\tAudio\n"; + if (cap & V4L2_CAP_RADIO) + s += "\t\tRadio\n"; + if (cap & V4L2_CAP_READWRITE) + s += "\t\tRead/Write\n"; + if (cap & V4L2_CAP_ASYNCIO) + s += "\t\tAsync I/O\n"; + if (cap & V4L2_CAP_STREAMING) + s += "\t\tStreaming\n"; + return s; +} + +static v4l2_std_id parse_pal(const char *pal) +{ + if (pal[0] == '-') { + switch (pal[1]) { + case '6': + return V4L2_STD_PAL_60; + case 'b': + case 'B': + case 'g': + case 'G': + return V4L2_STD_PAL_BG; + case 'h': + case 'H': + return V4L2_STD_PAL_H; + case 'n': + case 'N': + if (pal[2] == 'c' || pal[2] == 'C') + return V4L2_STD_PAL_Nc; + return V4L2_STD_PAL_N; + case 'i': + case 'I': + return V4L2_STD_PAL_I; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_PAL_DK; + case 'M': + case 'm': + return V4L2_STD_PAL_M; + case '-': + break; + } + } + fprintf(stderr, "pal specifier not recognised\n"); + return 0; +} + +static v4l2_std_id parse_secam(const char *secam) +{ + if (secam[0] == '-') { + switch (secam[1]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_SECAM_DK; + case 'l': + case 'L': + if (secam[2] == 'C' || secam[2] == 'c') + return V4L2_STD_SECAM_LC; + return V4L2_STD_SECAM_L; + case '-': + break; + } + } + fprintf(stderr, "secam specifier not recognised\n"); + return 0; +} + +static v4l2_std_id parse_ntsc(const char *ntsc) +{ + if (ntsc[0] == '-') { + switch (ntsc[1]) { + case 'm': + case 'M': + return V4L2_STD_NTSC_M; + case 'j': + case 'J': + return V4L2_STD_NTSC_M_JP; + case 'k': + case 'K': + return V4L2_STD_NTSC_M_KR; + case '-': + break; + } + } + fprintf(stderr, "ntsc specifier not recognised\n"); + return 0; +} + +static int doioctl(int fd, int request, void *parm, const char *name) +{ + int retVal; + int e; + + errno = 0; + retVal = ioctl(fd, request, parm); + e = errno; + if (verbose) + printf("\t\t%s returned %d (%s)\n", name, retVal, strerror(e)); + if (retVal == 0) return retVal; + if (retVal != -1) { + return -1; + } + retVal = e; + return retVal; +} + +static int parse_subopt(char **subs, char * const *subopts, char **value) +{ + int opt = getsubopt(subs, subopts, value); + + if (opt == -1) { + fprintf(stderr, "Invalid suboptions specified\n"); + usage(); + exit(1); + } + if (value == NULL) { + fprintf(stderr, "No value given to suboption <%s>\n", + subopts[opt]); + usage(); + exit(1); + } + return opt; +} + +static void parse_next_subopt(char **subs, char **value) +{ + static char *const subopts[] = { + NULL + }; + int opt = getsubopt(subs, subopts, value); + + if (value == NULL) { + fprintf(stderr, "No value given to suboption <%s>\n", + subopts[opt]); + usage(); + exit(1); + } +} + +static void print_std(const char *prefix, const char *stds[], unsigned long long std) +{ + int first = 1; + + printf("\t%s-", prefix); + while (*stds) { + if (std & 1) { + if (!first) + printf("/"); + first = 0; + printf("%s", *stds); + } + stds++; + std >>= 1; + } + printf("\n"); +} + +static const char *ok(int res) +{ + if (res) + app_result = res; + return res ? "FAIL" : "OK"; +} + +static int check_string(const char *s, int len, const char *fld) +{ + if (strlen(s) == 0) { + if (verbose) + printf("%s field empty\n", fld); + return -1; + } + if (strlen(s) >= len) { + if (verbose) + printf("%s field not 0-terminated\n", fld); + return -1; + } + return 0; +} + +static int check_ustring(const __u8 *s, int len, const char *fld) +{ + return check_string((const char *)s, len, fld); +} + +static int check_0(void *p, int len) +{ + __u8 *q = (__u8 *)p; + + while (len--) + if (*q++) { + if (verbose) + printf("array not zeroed by driver\n"); + return -1; + } + return 0; +} + +static int testCap(int fd) +{ + struct v4l2_capability vcap; + __u32 caps; + + if (doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP")) + return -1; + if (check_ustring(vcap.driver, sizeof(vcap.driver), "driver")) + return -1; + if (check_ustring(vcap.card, sizeof(vcap.card), "card")) + return -1; + if (check_ustring(vcap.bus_info, sizeof(vcap.bus_info), "bus_info")) + return -1; + if (check_0(vcap.reserved, sizeof(vcap.reserved))) + return -1; + caps = vcap.capabilities; + if (caps == 0) { + if (verbose) printf("no capabilities set\n"); + return -1; + } + return 0; +} + +static int testChipIdent(int fd) +{ + struct v4l2_chip_ident chip; + int ret; + + chip.match_type = V4L2_CHIP_MATCH_HOST; + chip.match_chip = 0; + ret = doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip, "VIDIOC_G_CHIP_IDENT"); + // Must return either 0 (OK) or EINVAL (not supported) + if (ret == 0) { + struct v4l2_chip_ident orig; + + // set invalid match_type + chip.match_type = V4L2_CHIP_MATCH_I2C_ADDR + 1; + chip.match_chip = 0xdeadbeef; + chip.ident = 0xdeadbeef; + chip.revision = 0xdeadbeef; + orig = chip; + ret = doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip, "VIDIOC_G_CHIP_IDENT"); + if (ret != EINVAL) { + if (verbose) + printf("Invalid match_type accepted\n"); + return -1; + } + if (memcmp(&orig, &chip, sizeof(chip))) { + if (verbose) + printf("Error, but struct modified\n"); + return -1; + } + return 0; + } + return ret != EINVAL; +} + +static int testRegister(int fd) +{ + struct v4l2_register reg; + struct v4l2_chip_ident chip; + int ret; + int uid = getuid(); + + reg.match_type = V4L2_CHIP_MATCH_HOST; + reg.match_chip = 0; + reg.reg = 0; + ret = doioctl(fd, VIDIOC_DBG_G_REGISTER, ®, "VIDIOC_DBG_G_REGISTER"); + if (ret == EINVAL) + return 0; + if (uid && ret != EPERM) { + printf("Not allowed to call VIDIOC_DBG_G_REGISTER unless root\n"); + return -1; + } + if (uid == 0 && ret) { + printf("Not allowed to call VIDIOC_DBG_G_REGISTER even though we are root\n"); + return -1; + } + chip.match_type = V4L2_CHIP_MATCH_HOST; + chip.match_chip = 0; + if (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip, "VIDIOC_G_CHIP_IDENT")) { + printf("Must support VIDIOC_G_CHIP_IDENT\n"); + return -1; + } + if (uid) { + // Don't test S_REGISTER as root, don't want to risk + // messing with registers in the compliance test. + reg.reg = reg.val = 0; + ret = doioctl(fd, VIDIOC_DBG_S_REGISTER, ®, "VIDIOC_DBG_S_REGISTER"); + if (ret != EINVAL && ret != EPERM) { + printf("Invalid error calling VIDIOC_DBG_S_REGISTER as non-root\n"); + return -1; + } + } + return 0; +} + +static int testLogStatus(int fd) +{ + int ret = doioctl(fd, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS"); + + return (ret == 0 || ret == EINVAL) ? 0 : -1; +} + +int main(int argc, char **argv) +{ + char *value, *subs; + int i; + unsigned t; + int fd = -1; + + /* command args */ + int ch; + const char *device = "/dev/video0"; /* -d device */ + struct v4l2_capability vcap; /* list_cap */ + char short_options[26 * 2 * 2 + 1]; + int idx = 0; + int tests = 0; + + for (i = 0; long_options[i].name; i++) { + if (!isalpha(long_options[i].val)) + continue; + short_options[idx++] = long_options[i].val; + if (long_options[i].has_arg == required_argument) + short_options[idx++] = ':'; + } + while (1) { + int option_index = 0; + + short_options[idx] = 0; + ch = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (ch == -1) + break; + + options[(int)ch] = 1; + switch (ch) { + case OptHelp: + usage(); + return 0; + case OptTest: + t = strtoul(optarg, NULL, 0); + + if (t >= TestMax) + usage(); + test[t] = 1; + tests++; + break; + case OptSetDevice: + device = optarg; + if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + static char newdev[20]; + char dev = device[0]; + + sprintf(newdev, "/dev/video%c", dev); + device = newdev; + } + break; + case ':': + fprintf(stderr, "Option `%s' requires a value\n", + argv[optind]); + usage(); + return 1; + case '?': + fprintf(stderr, "Unknown argument `%s'\n", + argv[optind]); + usage(); + return 1; + } + } + if (optind < argc) { + printf("unknown arguments: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + usage(); + return 1; + } + verbose = options[OptVerbose]; + if (!tests) { + for (t = 0; t < TestMax; t++) + test[t] = 1; + } + + if ((fd = open(device, O_RDWR)) < 0) { + fprintf(stderr, "Failed to open %s: %s\n", device, + strerror(errno)); + exit(1); + } + + ioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); + caps = vcap.capabilities; + find_controls(fd); + for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) { + if (ctrl_str2id.find(*iter) == ctrl_str2id.end()) { + fprintf(stderr, "unknown control '%s'\n", (*iter).c_str()); + exit(1); + } + } + for (ctrl_set_map::iterator iter = set_ctrls.begin(); iter != set_ctrls.end(); ++iter) { + if (ctrl_str2id.find(iter->first) == ctrl_str2id.end()) { + fprintf(stderr, "unknown control '%s'\n", iter->first.c_str()); + exit(1); + } + } + + /* Information Opts */ + + if (options[OptGetDriverInfo]) { + printf("Driver Info:\n"); + printf("\tDriver name : %s\n", vcap.driver); + printf("\tCard type : %s\n", vcap.card); + printf("\tBus info : %s\n", vcap.bus_info); + printf("\tDriver version: %d\n", vcap.version); + printf("\tCapabilities : 0x%08X\n", vcap.capabilities); + printf("%s", cap2s(vcap.capabilities).c_str()); + } + + printf("Compliance test for device %s:\n\n", device); + + printf("Required ioctls:\n"); + if (test[TestCap]) + printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(fd))); + + printf("Debug ioctls:\n"); + if (test[TestChipIdent]) + printf("\ttest VIDIOC_G_CHIP_IDENT: %s\n", ok(testChipIdent(fd))); + if (test[TestRegister]) + printf("\ttest VIDIOC_DBG_G/S_REGISTER: %s\n", ok(testRegister(fd))); + if (test[TestLogStatus]) + printf("\ttest VIDIOC_LOG_STATUS: %s\n", ok(testLogStatus(fd))); + + close(fd); + exit(app_result); +} diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index 41946978a..417721c57 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -34,6 +34,7 @@ #include <errno.h> #include <sys/ioctl.h> #include <sys/time.h> +#include <dirent.h> #include <math.h> #include <sys/klog.h> @@ -43,6 +44,7 @@ #include <vector> #include <map> #include <string> +#include <algorithm> /* Short option list @@ -77,15 +79,25 @@ enum Option { OptSetVideoFormat = 'v', OptGetSlicedVbiOutFormat = 128, - OptSetSlicedVbiOutFormat, OptGetOverlayFormat, - //OptSetOverlayFormat, TODO OptGetOutputOverlayFormat, - OptSetOutputOverlayFormat, OptGetVbiFormat, - //OptSetVbiFormat, TODO OptGetVbiOutFormat, + OptGetVideoOutFormat, + OptSetSlicedVbiOutFormat, + OptSetOutputOverlayFormat, + OptSetOverlayFormat, + //OptSetVbiFormat, TODO //OptSetVbiOutFormat, TODO + OptSetVideoOutFormat, + OptTryVideoOutFormat, + OptTrySlicedVbiOutFormat, + OptTrySlicedVbiFormat, + OptTryVideoFormat, + OptTryOutputOverlayFormat, + OptTryOverlayFormat, + //OptTryVbiFormat, TODO + //OptTryVbiOutFormat, TODO OptAll, OptStreamOff, OptStreamOn, @@ -93,8 +105,7 @@ enum Option { OptListFormats, OptLogStatus, OptVerbose, - OptGetVideoOutFormat, - OptSetVideoOutFormat, + OptSilent, OptGetSlicedVbiCap, OptGetSlicedVbiOutCap, OptGetFBuf, @@ -118,12 +129,14 @@ enum Option { OptGetOverlayCropCap, OptGetOutputOverlayCropCap, OptOverlay, + OptListDevices, OptLast = 256 }; static char options[OptLast]; static int app_result; +static int verbose; static unsigned capabilities; @@ -142,6 +155,9 @@ static ctrl_get_list get_ctrls; typedef std::map<std::string,std::string> ctrl_set_map; static ctrl_set_map set_ctrls; +typedef std::vector<std::string> dev_vec; +typedef std::map<std::string, std::string> dev_map; + typedef struct { unsigned flag; const char *str; @@ -160,6 +176,10 @@ static const flag_def service_def[] = { #define FmtHeight (1L<<1) #define FmtChromaKey (1L<<2) #define FmtGlobalAlpha (1L<<3) +#define FmtPixelFormat (1L<<4) +#define FmtLeft (1L<<5) +#define FmtTop (1L<<6) +#define FmtField (1L<<7) /* crop specified */ #define CropWidth (1L<<0) @@ -174,8 +194,10 @@ static struct option long_options[] = { {"device", required_argument, 0, OptSetDevice}, {"get-fmt-video", no_argument, 0, OptGetVideoFormat}, {"set-fmt-video", required_argument, 0, OptSetVideoFormat}, + {"try-fmt-video", required_argument, 0, OptTryVideoFormat}, {"get-fmt-video-out", no_argument, 0, OptGetVideoOutFormat}, {"set-fmt-video-out", required_argument, 0, OptSetVideoOutFormat}, + {"try-fmt-video-out", required_argument, 0, OptTryVideoOutFormat}, {"help", no_argument, 0, OptHelp}, {"get-output", no_argument, 0, OptGetOutput}, {"set-output", required_argument, 0, OptSetOutput}, @@ -205,12 +227,17 @@ static struct option long_options[] = { {"verbose", no_argument, 0, OptVerbose}, {"log-status", no_argument, 0, OptLogStatus}, {"get-fmt-overlay", no_argument, 0, OptGetOverlayFormat}, + {"set-fmt-overlay", required_argument, 0, OptSetOverlayFormat}, + {"try-fmt-overlay", required_argument, 0, OptTryOverlayFormat}, {"get-fmt-output-overlay", no_argument, 0, OptGetOutputOverlayFormat}, {"set-fmt-output-overlay", required_argument, 0, OptSetOutputOverlayFormat}, + {"try-fmt-output-overlay", required_argument, 0, OptTryOutputOverlayFormat}, {"get-fmt-sliced-vbi", no_argument, 0, OptGetSlicedVbiFormat}, {"set-fmt-sliced-vbi", required_argument, 0, OptSetSlicedVbiFormat}, + {"try-fmt-sliced-vbi", required_argument, 0, OptTrySlicedVbiFormat}, {"get-fmt-sliced-vbi-out", no_argument, 0, OptGetSlicedVbiOutFormat}, {"set-fmt-sliced-vbi-out", required_argument, 0, OptSetSlicedVbiOutFormat}, + {"try-fmt-sliced-vbi-out", required_argument, 0, OptTrySlicedVbiOutFormat}, {"get-fmt-vbi", no_argument, 0, OptGetVbiFormat}, {"get-fmt-vbi-out", no_argument, 0, OptGetVbiOutFormat}, {"get-sliced-vbi-cap", no_argument, 0, OptGetSlicedVbiCap}, @@ -230,132 +257,144 @@ static struct option long_options[] = { {"get-crop-output-overlay", no_argument, 0, OptGetOutputOverlayCrop}, {"set-crop-output-overlay", required_argument, 0, OptSetOutputOverlayCrop}, {"overlay", required_argument, 0, OptOverlay}, + {"list-devices", no_argument, 0, OptListDevices}, {0, 0, 0, 0} }; static void usage(void) { printf("Usage:\n"); - printf("Common options:\n"); - printf(" --all display all information available\n"); - printf(" -B, --get-fmt-sliced-vbi\n"); - printf(" query the sliced VBI capture format [VIDIOC_G_FMT]\n"); - printf(" -b, --set-fmt-sliced-vbi=<mode>\n"); - printf(" set the sliced VBI capture format to <mode> [VIDIOC_S_FMT]\n"); - printf(" <mode> is a comma separated list of:\n"); - printf(" off: turn off sliced VBI (cannot be combined with other modes)\n"); - printf(" teletext: teletext (PAL/SECAM)\n"); - printf(" cc: closed caption (NTSC)\n"); - printf(" wss: widescreen signal (PAL/SECAM)\n"); - printf(" vps: VPS (PAL/SECAM)\n"); - printf(" -C, --get-ctrl=<ctrl>[,<ctrl>...]\n"); - printf(" get the value of the controls [VIDIOC_G_EXT_CTRLS]\n"); - printf(" -c, --set-ctrl=<ctrl>=<val>[,<ctrl>=<val>...]\n"); - printf(" set the controls to the values specified [VIDIOC_S_EXT_CTRLS]\n"); - printf(" -D, --info show driver info [VIDIOC_QUERYCAP]\n"); - printf(" -d, --device=<dev> use device <dev> instead of /dev/video0\n"); - printf(" if <dev> is a single digit, then /dev/video<dev> is used\n"); - printf(" -F, --get-freq query the frequency [VIDIOC_G_FREQUENCY]\n"); - printf(" -f, --set-freq=<freq>\n"); - printf(" set the frequency to <freq> MHz [VIDIOC_S_FREQUENCY]\n"); - printf(" -h, --help display this help message\n"); - printf(" -I, --get-input query the video input [VIDIOC_G_INPUT]\n"); - printf(" -i, --set-input=<num>\n"); - printf(" set the video input to <num> [VIDIOC_S_INPUT]\n"); - printf(" -l, --list-ctrls display all controls and their values [VIDIOC_QUERYCTRL]\n"); - printf(" -L, --list-ctrls-menus\n"); - printf(" display all controls, their values and the menus [VIDIOC_QUERYMENU]\n"); - printf(" -N, --list-outputs display video outputs [VIDIOC_ENUMOUTPUT]\n"); - printf(" -n, --list-inputs display video inputs [VIDIOC_ENUMINPUT]\n"); - printf(" -O, --get-output query the video output [VIDIOC_G_OUTPUT]\n"); - printf(" -o, --set-output=<num>\n"); - printf(" set the video output to <num> [VIDIOC_S_OUTPUT]\n"); - printf(" -S, --get-standard\n"); - printf(" query the video standard [VIDIOC_G_STD]\n"); - printf(" -s, --set-standard=<num>\n"); - printf(" set the video standard to <num> [VIDIOC_S_STD]\n"); - printf(" <num> can be a numerical v4l2_std value, or it can be one of:\n"); - printf(" pal-X (X = B/G/H/N/Nc/I/D/K/M/60) or just 'pal' (V4L2_STD_PAL)\n"); - printf(" ntsc-X (X = M/J/K) or just 'ntsc' (V4L2_STD_NTSC)\n"); - printf(" secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n"); - printf(" --list-standards display supported video standards [VIDIOC_ENUMSTD]\n"); - printf(" -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n"); - printf(" -t, --set-tuner=<mode>\n"); - printf(" set the audio mode of the tuner [VIDIOC_S_TUNER]\n"); - printf(" Possible values: mono, stereo, lang2, lang1, bilingual\n"); - printf(" --list-formats display supported video formats [VIDIOC_ENUM_FMT]\n"); - printf(" -V, --get-fmt-video\n"); - printf(" query the video capture format [VIDIOC_G_FMT]\n"); - printf(" -v, --set-fmt-video=width=<w>,height=<h>\n"); - printf(" set the video capture format [VIDIOC_S_FMT]\n"); - printf(" --verbose turn on verbose ioctl error reporting.\n"); - printf("\n"); - printf("Uncommon options:\n"); - printf(" --get-fmt-video-out\n"); - printf(" query the video output format [VIDIOC_G_FMT]\n"); - printf(" --set-fmt-video-out=width=<w>,height=<h>\n"); - printf(" set the video output format [VIDIOC_S_FMT]\n"); - printf(" --get-fmt-overlay\n"); - printf(" query the video overlay format [VIDIOC_G_FMT]\n"); - printf(" --get-fmt-output-overlay\n"); - printf(" query the video output overlay format [VIDIOC_G_FMT]\n"); - printf(" --set-fmt-output-overlay=chromakey=<key>,global_alpha=<alpha>\n"); - printf(" set the video output overlay format [VIDIOC_S_FMT]\n"); - printf(" --get-sliced-vbi-cap\n"); - printf(" query the sliced VBI capture capabilities [VIDIOC_G_SLICED_VBI_CAP]\n"); - printf(" --get-sliced-vbi-out-cap\n"); - printf(" query the sliced VBI output capabilities [VIDIOC_G_SLICED_VBI_CAP]\n"); - printf(" --get-fmt-sliced-vbi-out\n"); - printf(" query the sliced VBI output format [VIDIOC_G_FMT]\n"); - printf(" --set-fmt-sliced-vbi-out=<mode>\n"); - printf(" set the sliced VBI output format to <mode> [VIDIOC_S_FMT]\n"); - printf(" <mode> is a comma separated list of:\n"); - printf(" off: turn off sliced VBI (cannot be combined with other modes)\n"); - printf(" teletext: teletext (PAL/SECAM)\n"); - printf(" cc: closed caption (NTSC)\n"); - printf(" wss: widescreen signal (PAL/SECAM)\n"); - printf(" vps: VPS (PAL/SECAM)\n"); - printf(" --get-fmt-vbi query the VBI capture format [VIDIOC_G_FMT]\n"); - printf(" --get-fmt-vbi-out query the VBI output format [VIDIOC_G_FMT]\n"); - printf(" --overlay=<on> turn overlay on (1) or off (0) (VIDIOC_OVERLAY)\n"); - printf(" --get-fbuf query the overlay framebuffer data [VIDIOC_G_FBUF]\n"); - printf(" --set-fbuf=chromakey=<0/1>,global_alpha=<0/1>,local_alpha=<0/1>,local_inv_alpha=<0/1>\n"); - printf(" set the overlay framebuffer [VIDIOC_S_FBUF]\n"); - printf(" --get-cropcap query the crop capabilities [VIDIOC_CROPCAP]\n"); - printf(" --get-crop query the video capture crop window [VIDIOC_G_CROP]\n"); - printf(" --set-crop=top=<x>,left=<y>,width=<w>,height=<h>\n"); - printf(" set the video capture crop window [VIDIOC_S_CROP]\n"); - printf(" --get-cropcap-output\n"); - printf(" query the crop capabilities for video output [VIDIOC_CROPCAP]\n"); - printf(" --get-crop-output query the video output crop window [VIDIOC_G_CROP]\n"); - printf(" --set-crop-output=top=<x>,left=<y>,width=<w>,height=<h>\n"); - printf(" set the video output crop window [VIDIOC_S_CROP]\n"); - printf(" --get-cropcap-overlay\n"); - printf(" query the crop capabilities for video overlay [VIDIOC_CROPCAP]\n"); - printf(" --get-crop-overlay query the video overlay crop window [VIDIOC_G_CROP]\n"); - printf(" --set-crop-overlay=top=<x>,left=<y>,width=<w>,height=<h>\n"); - printf(" set the video overlay crop window [VIDIOC_S_CROP]\n"); - printf(" --get-cropcap-output-overlay\n"); - printf(" query the crop capabilities for video output overlays [VIDIOC_CROPCAP]\n"); - printf(" --get-crop-output-overlay\n"); - printf(" query the video output overlay crop window [VIDIOC_G_CROP]\n"); - printf(" --set-crop-output-overlay=top=<x>,left=<y>,width=<w>,height=<h>\n"); - printf(" set the video output overlay crop window [VIDIOC_S_CROP]\n"); - printf(" --get-audio-input query the audio input [VIDIOC_G_AUDIO]\n"); - printf(" --set-audio-input=<num>\n"); - printf(" set the audio input to <num> [VIDIOC_S_AUDIO]\n"); - printf(" --get-audio-output query the audio output [VIDIOC_G_AUDOUT]\n"); - printf(" --set-audio-output=<num>\n"); - printf(" set the audio output to <num> [VIDIOC_S_AUDOUT]\n"); - printf(" --list-audio-outputs\n"); - printf(" display audio outputs [VIDIOC_ENUMAUDOUT]\n"); - printf(" --list-audio-inputs\n"); - printf(" display audio inputs [VIDIOC_ENUMAUDIO]\n"); - printf("\n"); - printf("Expert options:\n"); - printf(" --streamoff turn the stream off [VIDIOC_STREAMOFF]\n"); - printf(" --streamon turn the stream on [VIDIOC_STREAMOFF]\n"); - printf(" --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\n"); + printf("Common options:\n" + " --all display all information available\n" + " -C, --get-ctrl=<ctrl>[,<ctrl>...]\n" + " get the value of the controls [VIDIOC_G_EXT_CTRLS]\n" + " -c, --set-ctrl=<ctrl>=<val>[,<ctrl>=<val>...]\n" + " set the controls to the values specified [VIDIOC_S_EXT_CTRLS]\n" + " -D, --info show driver info [VIDIOC_QUERYCAP]\n" + " -d, --device=<dev> use device <dev> instead of /dev/video0\n" + " if <dev> is a single digit, then /dev/video<dev> is used\n" + " -F, --get-freq query the frequency [VIDIOC_G_FREQUENCY]\n" + " -f, --set-freq=<freq>\n" + " set the frequency to <freq> MHz [VIDIOC_S_FREQUENCY]\n" + " -h, --help display this help message\n" + " -I, --get-input query the video input [VIDIOC_G_INPUT]\n" + " -i, --set-input=<num>\n" + " set the video input to <num> [VIDIOC_S_INPUT]\n" + " -l, --list-ctrls display all controls and their values [VIDIOC_QUERYCTRL]\n" + " -L, --list-ctrls-menus\n" + " display all controls, their values and the menus [VIDIOC_QUERYMENU]\n" + " -N, --list-outputs display video outputs [VIDIOC_ENUMOUTPUT]\n" + " -n, --list-inputs display video inputs [VIDIOC_ENUMINPUT]\n" + " -O, --get-output query the video output [VIDIOC_G_OUTPUT]\n" + " -o, --set-output=<num>\n" + " set the video output to <num> [VIDIOC_S_OUTPUT]\n" + " -S, --get-standard\n" + " query the video standard [VIDIOC_G_STD]\n" + " -s, --set-standard=<num>\n" + " set the video standard to <num> [VIDIOC_S_STD]\n" + " <num> can be a numerical v4l2_std value, or it can be one of:\n" + " pal-X (X = B/G/H/N/Nc/I/D/K/M/60) or just 'pal' (V4L2_STD_PAL)\n" + " ntsc-X (X = M/J/K) or just 'ntsc' (V4L2_STD_NTSC)\n" + " secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n" + " --list-standards display supported video standards [VIDIOC_ENUMSTD]\n" + " -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n" + " -t, --set-tuner=<mode>\n" + " set the audio mode of the tuner [VIDIOC_S_TUNER]\n" + " Possible values: mono, stereo, lang2, lang1, bilingual\n" + " --list-formats display supported video formats [VIDIOC_ENUM_FMT]\n" + " -V, --get-fmt-video\n" + " query the video capture format [VIDIOC_G_FMT]\n" + " -v, --set-fmt-video=width=<w>,height=<h>,pixelformat=<f>\n" + " set the video capture format [VIDIOC_S_FMT]\n" + " pixelformat is either the format index as reported by\n" + " --list-formats, or the fourcc value as a string\n" + " --list-devices list all v4l devices\n" + " --silent only set the result code, do not print any messages\n" + " --verbose turn on verbose ioctl status reporting\n" + "\n"); + printf("Uncommon options:\n" + " --try-fmt-video=width=<w>,height=<h>,pixelformat=<f>\n" + " try the video capture format [VIDIOC_TRY_FMT]\n" + " pixelformat is either the format index as reported by\n" + " --list-formats, or the fourcc value as a string\n" + " --get-fmt-video-out\n" + " query the video output format [VIDIOC_G_FMT]\n" + " --set-fmt-video-out=width=<w>,height=<h>\n" + " set the video output format [VIDIOC_S_FMT]\n" + " --try-fmt-video-out=width=<w>,height=<h>\n" + " try the video output format [VIDIOC_TRY_FMT]\n" + " --get-fmt-overlay query the video overlay format [VIDIOC_G_FMT]\n" + " --get-fmt-output-overlay\n" + " query the video output overlay format [VIDIOC_G_FMT]\n" + " --set-fmt-overlay\n" + " --try-fmt-overlay\n" + " --set-fmt-output-overlay\n" + " --try-fmt-output-overlay=chromakey=<key>,global_alpha=<alpha>,\n" + " top=<t>,left=<l>,width=<w>,height=<h>,field=<f>\n" + " set/try the video or video output overlay format [VIDIOC_TRY_FMT]\n" + " <f> can be one of:\n" + " any, none, top, bottom, interlaced, seq_tb, seq_bt, alternate,\n" + " interlaced_tb, interlaced_bt\n" + " --get-sliced-vbi-cap\n" + " query the sliced VBI capture capabilities [VIDIOC_G_SLICED_VBI_CAP]\n" + " --get-sliced-vbi-out-cap\n" + " query the sliced VBI output capabilities [VIDIOC_G_SLICED_VBI_CAP]\n" + " -B, --get-fmt-sliced-vbi\n" + " query the sliced VBI capture format [VIDIOC_G_FMT]\n" + " --get-fmt-sliced-vbi-out\n" + " query the sliced VBI output format [VIDIOC_G_FMT]\n" + " -b, --set-fmt-sliced-vbi\n" + " --try-fmt-sliced-vbi\n" + " --set-fmt-sliced-vbi-out\n" + " --try-fmt-sliced-vbi-out=<mode>\n" + " (try to) set the sliced VBI capture/output format to <mode> [VIDIOC_S/TRY_FMT]\n" + " <mode> is a comma separated list of:\n" + " off: turn off sliced VBI (cannot be combined with other modes)\n" + " teletext: teletext (PAL/SECAM)\n" + " cc: closed caption (NTSC)\n" + " wss: widescreen signal (PAL/SECAM)\n" + " vps: VPS (PAL/SECAM)\n" + " --get-fmt-vbi query the VBI capture format [VIDIOC_G_FMT]\n" + " --get-fmt-vbi-out query the VBI output format [VIDIOC_G_FMT]\n" + " --overlay=<on> turn overlay on (1) or off (0) (VIDIOC_OVERLAY)\n" + " --get-fbuf query the overlay framebuffer data [VIDIOC_G_FBUF]\n" + " --set-fbuf=chromakey=<0/1>,global_alpha=<0/1>,local_alpha=<0/1>,local_inv_alpha=<0/1>\n" + " set the overlay framebuffer [VIDIOC_S_FBUF]\n" + " --get-cropcap query the crop capabilities [VIDIOC_CROPCAP]\n" + " --get-crop query the video capture crop window [VIDIOC_G_CROP]\n" + " --set-crop=top=<x>,left=<y>,width=<w>,height=<h>\n" + " set the video capture crop window [VIDIOC_S_CROP]\n" + " --get-cropcap-output\n" + " query the crop capabilities for video output [VIDIOC_CROPCAP]\n" + " --get-crop-output query the video output crop window [VIDIOC_G_CROP]\n" + " --set-crop-output=top=<x>,left=<y>,width=<w>,height=<h>\n" + " set the video output crop window [VIDIOC_S_CROP]\n" + " --get-cropcap-overlay\n" + " query the crop capabilities for video overlay [VIDIOC_CROPCAP]\n" + " --get-crop-overlay query the video overlay crop window [VIDIOC_G_CROP]\n" + " --set-crop-overlay=top=<x>,left=<y>,width=<w>,height=<h>\n" + " set the video overlay crop window [VIDIOC_S_CROP]\n" + " --get-cropcap-output-overlay\n" + " query the crop capabilities for video output overlays [VIDIOC_CROPCAP]\n" + " --get-crop-output-overlay\n" + " query the video output overlay crop window [VIDIOC_G_CROP]\n" + " --set-crop-output-overlay=top=<x>,left=<y>,width=<w>,height=<h>\n" + " set the video output overlay crop window [VIDIOC_S_CROP]\n" + " --get-audio-input query the audio input [VIDIOC_G_AUDIO]\n" + " --set-audio-input=<num>\n" + " set the audio input to <num> [VIDIOC_S_AUDIO]\n" + " --get-audio-output query the audio output [VIDIOC_G_AUDOUT]\n" + " --set-audio-output=<num>\n" + " set the audio output to <num> [VIDIOC_S_AUDOUT]\n" + " --list-audio-outputs\n" + " display audio outputs [VIDIOC_ENUMAUDOUT]\n" + " --list-audio-inputs\n" + " display audio inputs [VIDIOC_ENUMAUDIO]\n" + "\n"); + printf("Expert options:\n" + " --streamoff turn the stream off [VIDIOC_STREAMOFF]\n" + " --streamon turn the stream on [VIDIOC_STREAMOFF]\n" + " --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\n"); exit(0); } @@ -701,7 +740,7 @@ static void printfbuf(const struct v4l2_framebuffer &fb) printf("\tBase : 0x%p\n", fb.base); printf("\tWidth : %d\n", fb.fmt.width); printf("\tHeight : %d\n", fb.fmt.height); - printf("\tPixel Format : %s\n", fcc2s(fb.fmt.pixelformat).c_str()); + printf("\tPixel Format : '%s'\n", fcc2s(fb.fmt.pixelformat).c_str()); if (!is_ext) { printf("\tBytes per Line: %d\n", fb.fmt.bytesperline); printf("\tSize image : %d\n", fb.fmt.sizeimage); @@ -740,7 +779,7 @@ static void printfmt(struct v4l2_format vfmt) case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: printf("\tWidth/Height : %u/%u\n", vfmt.fmt.pix.width, vfmt.fmt.pix.height); - printf("\tPixel Format : %s\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str()); + printf("\tPixel Format : '%s'\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str()); printf("\tField : %s\n", field2s(vfmt.fmt.pix.field).c_str()); printf("\tBytes per Line: %u\n", vfmt.fmt.pix.bytesperline); printf("\tSize Image : %u\n", vfmt.fmt.pix.sizeimage); @@ -798,8 +837,9 @@ static void print_video_formats(int fd, enum v4l2_buf_type type) fmt.index = 0; fmt.type = type; while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) { + printf("\tIndex : %d\n", fmt.index); printf("\tType : %s\n", buftype2s(type).c_str()); - printf("\tPixelformat : %s", fcc2s(fmt.pixelformat).c_str()); + printf("\tPixel Format: '%s'", fcc2s(fmt.pixelformat).c_str()); if (fmt.flags) printf(" (compressed)"); printf("\n"); @@ -1013,16 +1053,94 @@ static int doioctl(int fd, int request, void *parm, const char *name) if (retVal < 0) { app_result = -1; } - if (!options[OptVerbose]) return retVal; - printf("%s: ", name); + if (options[OptSilent]) return retVal; if (retVal < 0) - printf("failed: %s\n", strerror(errno)); - else - printf("ok\n"); + printf("%s: failed: %s\n", name, strerror(errno)); + else if (verbose) + printf("%s: ok\n", name); return retVal; } +static bool is_v4l_dev(const char *name) +{ + return !memcmp(name, "vtx", 3) || + !memcmp(name, "video", 5) || + !memcmp(name, "radio", 5) || + !memcmp(name, "vbi", 3); +} + +static int calc_node_val(const char *s) +{ + int n = 0; + + s = strrchr(s, '/') + 1; + if (!memcmp(s, "video", 5)) n = 0; + else if (!memcmp(s, "radio", 5)) n = 0x100; + else if (!memcmp(s, "vbi", 3)) n = 0x200; + else if (!memcmp(s, "vtx", 3)) n = 0x300; + n += atol(s + (n >= 0x200 ? 3 : 5)); + return n; +} + +static bool sort_on_device_name(const std::string &s1, const std::string &s2) +{ + int n1 = calc_node_val(s1.c_str()); + int n2 = calc_node_val(s2.c_str()); + + return n1 < n2; +} + +static void list_devices() +{ + DIR *dp; + struct dirent *ep; + dev_vec files; + dev_map cards; + struct v4l2_capability vcap; + + dp = opendir("/dev"); + if (dp == NULL) { + perror ("Couldn't open the directory"); + return; + } + while (ep = readdir(dp)) + if (is_v4l_dev(ep->d_name)) + files.push_back(std::string("/dev/") + ep->d_name); + closedir(dp); + +#if 0 + dp = opendir("/dev/v4l"); + if (dp) { + while (ep = readdir(dp)) + if (is_v4l_dev(ep->d_name)) + files.push_back(std::string("/dev/v4l/") + ep->d_name); + closedir(dp); + } +#endif + + std::sort(files.begin(), files.end(), sort_on_device_name); + + for (dev_vec::iterator iter = files.begin(); + iter != files.end(); ++iter) { + int fd = open(iter->c_str(), O_RDWR); + std::string bus_info; + + if (fd < 0) + continue; + doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); + close(fd); + bus_info = (const char *)vcap.bus_info; + if (cards[bus_info].empty()) + cards[bus_info] += std::string((char *)vcap.card) + " (" + bus_info + "):\n"; + cards[bus_info] += "\t" + (*iter) + "\n"; + } + for (dev_map::iterator iter = cards.begin(); + iter != cards.end(); ++iter) { + printf("%s\n", iter->second.c_str()); + } +} + static int parse_subopt(char **subs, const char * const *subopts, char **value) { int opt = getsubopt(subs, (char * const *)subopts, value); @@ -1127,6 +1245,21 @@ static void parse_crop(char *optarg, unsigned int &set_crop, v4l2_rect &vcrop) } } +static enum v4l2_field parse_field(const char *s) +{ + if (!strcmp(s, "any")) return V4L2_FIELD_ANY; + if (!strcmp(s, "none")) return V4L2_FIELD_NONE; + if (!strcmp(s, "top")) return V4L2_FIELD_TOP; + if (!strcmp(s, "bottom")) return V4L2_FIELD_BOTTOM; + if (!strcmp(s, "interlaced")) return V4L2_FIELD_INTERLACED; + if (!strcmp(s, "seq_tb")) return V4L2_FIELD_SEQ_TB; + if (!strcmp(s, "seq_bt")) return V4L2_FIELD_SEQ_BT; + if (!strcmp(s, "alternate")) return V4L2_FIELD_ALTERNATE; + if (!strcmp(s, "interlaced_tb")) return V4L2_FIELD_INTERLACED_TB; + if (!strcmp(s, "interlaced_bt")) return V4L2_FIELD_INTERLACED_BT; + return V4L2_FIELD_ANY; +} + int main(int argc, char **argv) { char *value, *subs; @@ -1142,6 +1275,7 @@ int main(int argc, char **argv) unsigned int set_crop_overlay = 0; unsigned int set_crop_out_overlay = 0; unsigned int set_fbuf = 0; + unsigned int set_overlay_fmt = 0; unsigned int set_overlay_fmt_out = 0; int mode = V4L2_TUNER_MODE_STEREO; /* set audio mode */ @@ -1155,6 +1289,7 @@ int main(int argc, char **argv) struct v4l2_format vbi_fmt_out; /* set_format/get_format for sliced VBI output */ struct v4l2_format raw_fmt; /* set_format/get_format for VBI */ struct v4l2_format raw_fmt_out; /* set_format/get_format for VBI output */ + struct v4l2_format overlay_fmt; /* set_format/get_format video overlay */ struct v4l2_format overlay_fmt_out; /* set_format/get_format video overlay output */ struct v4l2_tuner tuner; /* set_tuner/get_tuner */ struct v4l2_capability vcap; /* list_cap */ @@ -1174,14 +1309,18 @@ int main(int argc, char **argv) struct v4l2_frequency vf; /* get_freq/set_freq */ struct v4l2_standard vs; /* list_std */ int overlay; /* overlay */ + unsigned int *set_overlay_fmt_ptr; + struct v4l2_format *overlay_fmt_ptr; char short_options[26 * 2 * 2 + 1]; int idx = 0; + int ret; memset(&vfmt, 0, sizeof(vfmt)); memset(&vbi_fmt, 0, sizeof(vbi_fmt)); memset(&raw_fmt, 0, sizeof(raw_fmt)); memset(&vfmt_out, 0, sizeof(vfmt_out)); memset(&vbi_fmt_out, 0, sizeof(vbi_fmt_out)); + memset(&overlay_fmt, 0, sizeof(overlay_fmt)); memset(&overlay_fmt_out, 0, sizeof(overlay_fmt_out)); memset(&raw_fmt_out, 0, sizeof(raw_fmt_out)); memset(&tuner, 0, sizeof(tuner)); @@ -1234,11 +1373,13 @@ int main(int argc, char **argv) } break; case OptSetVideoFormat: + case OptTryVideoFormat: subs = optarg; while (*subs != '\0') { static const char *const subopts[] = { "width", "height", + "pixelformat", NULL }; @@ -1251,10 +1392,20 @@ int main(int argc, char **argv) vfmt.fmt.pix.height = strtol(value, 0L, 0); set_fmts |= FmtHeight; break; + case 2: + if (strlen(value) == 4) + vfmt.fmt.pix.pixelformat = + v4l2_fourcc(value[0], value[1], + value[2], value[3]); + else + vfmt.fmt.pix.pixelformat = strtol(value, 0L, 0); + set_fmts |= FmtPixelFormat; + break; } } break; case OptSetVideoOutFormat: + case OptTryVideoOutFormat: subs = optarg; while (*subs != '\0') { static const char *const subopts[] = { @@ -1275,23 +1426,63 @@ int main(int argc, char **argv) } } break; + case OptSetOverlayFormat: + case OptTryOverlayFormat: case OptSetOutputOverlayFormat: + case OptTryOutputOverlayFormat: + switch (ch) { + case OptSetOverlayFormat: + case OptTryOverlayFormat: + set_overlay_fmt_ptr = &set_overlay_fmt; + overlay_fmt_ptr = &overlay_fmt; + break; + case OptSetOutputOverlayFormat: + case OptTryOutputOverlayFormat: + set_overlay_fmt_ptr = &set_overlay_fmt_out; + overlay_fmt_ptr = &overlay_fmt_out; + break; + } subs = optarg; while (*subs != '\0') { static const char *const subopts[] = { "chromakey", "global_alpha", + "left", + "top", + "width", + "height", + "field", NULL }; switch (parse_subopt(&subs, subopts, &value)) { case 0: - overlay_fmt_out.fmt.win.chromakey = strtol(value, 0L, 0); - set_overlay_fmt_out |= FmtChromaKey; + overlay_fmt_ptr->fmt.win.chromakey = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtChromaKey; break; case 1: - overlay_fmt_out.fmt.win.global_alpha = strtol(value, 0L, 0); - set_overlay_fmt_out |= FmtGlobalAlpha; + overlay_fmt_ptr->fmt.win.global_alpha = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtGlobalAlpha; + break; + case 2: + overlay_fmt_ptr->fmt.win.w.left = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtLeft; + break; + case 3: + overlay_fmt_ptr->fmt.win.w.top = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtTop; + break; + case 4: + overlay_fmt_ptr->fmt.win.w.width = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtWidth; + break; + case 5: + overlay_fmt_ptr->fmt.win.w.height = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtHeight; + break; + case 6: + overlay_fmt_ptr->fmt.win.field = parse_field(value); + *set_overlay_fmt_ptr |= FmtField; break; } } @@ -1425,11 +1616,13 @@ int main(int argc, char **argv) break; case OptSetSlicedVbiFormat: case OptSetSlicedVbiOutFormat: + case OptTrySlicedVbiFormat: + case OptTrySlicedVbiOutFormat: { bool foundOff = false; v4l2_format *fmt = &vbi_fmt; - if (ch == OptSetSlicedVbiOutFormat) + if (ch == OptSetSlicedVbiOutFormat || ch == OptTrySlicedVbiOutFormat) fmt = &vbi_fmt_out; fmt->fmt.sliced.service_set = 0; subs = optarg; @@ -1472,6 +1665,9 @@ int main(int argc, char **argv) } break; } + case OptListDevices: + list_devices(); + break; case ':': fprintf(stderr, "Option `%s' requires a value\n", argv[optind]); @@ -1499,6 +1695,7 @@ int main(int argc, char **argv) exit(1); } + verbose = options[OptVerbose]; doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); capabilities = vcap.capabilities; find_controls(fd); @@ -1536,6 +1733,7 @@ int main(int argc, char **argv) options[OptGetFBuf] = 1; options[OptGetCropCap] = 1; options[OptGetOutputCropCap] = 1; + options[OptSilent] = 1; } /* Information Opts */ @@ -1618,7 +1816,7 @@ int main(int argc, char **argv) } } - if (options[OptSetVideoFormat]) { + if (options[OptSetVideoFormat] || options[OptTryVideoFormat]) { struct v4l2_format in_vfmt; in_vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1627,11 +1825,29 @@ int main(int argc, char **argv) in_vfmt.fmt.pix.width = vfmt.fmt.pix.width; if (set_fmts & FmtHeight) in_vfmt.fmt.pix.height = vfmt.fmt.pix.height; - doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); + if (set_fmts & FmtPixelFormat) { + in_vfmt.fmt.pix.pixelformat = vfmt.fmt.pix.pixelformat; + if (in_vfmt.fmt.pix.pixelformat < 256) { + struct v4l2_fmtdesc fmt; + + fmt.index = in_vfmt.fmt.pix.pixelformat; + fmt.type = in_vfmt.type; + if (doioctl(fd, VIDIOC_ENUM_FMT, &fmt, "VIDIOC_ENUM_FMT")) + goto set_vid_fmt_error; + in_vfmt.fmt.pix.pixelformat = fmt.pixelformat; + } + } + if (options[OptSetVideoFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &in_vfmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(in_vfmt); } } +set_vid_fmt_error: - if (options[OptSetVideoOutFormat]) { + if (options[OptSetVideoOutFormat] || options[OptTryVideoOutFormat]) { struct v4l2_format in_vfmt; in_vfmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; @@ -1640,35 +1856,65 @@ int main(int argc, char **argv) in_vfmt.fmt.pix.width = vfmt_out.fmt.pix.width; if (set_fmts_out & FmtHeight) in_vfmt.fmt.pix.height = vfmt_out.fmt.pix.height; - doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); + + if (options[OptSetVideoOutFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &in_vfmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(in_vfmt); } } - if (options[OptSetSlicedVbiFormat]) { - if (vbi_fmt.fmt.sliced.service_set == 0) { - // switch to raw mode - vbi_fmt.type = V4L2_BUF_TYPE_VBI_CAPTURE; - if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt, "VIDIOC_G_FMT") == 0) - doioctl(fd, VIDIOC_S_FMT, &vbi_fmt, "VIDIOC_S_FMT"); - } else { - vbi_fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - doioctl(fd, VIDIOC_S_FMT, &vbi_fmt, "VIDIOC_S_FMT"); - } + if (options[OptSetSlicedVbiFormat] || options[OptTrySlicedVbiFormat]) { + vbi_fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + if (options[OptSetSlicedVbiFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &vbi_fmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &vbi_fmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(vbi_fmt); + } + + if (options[OptSetSlicedVbiOutFormat] || options[OptTrySlicedVbiOutFormat]) { + vbi_fmt_out.type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; + if (options[OptSetSlicedVbiOutFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &vbi_fmt_out, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &vbi_fmt_out, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(vbi_fmt_out); } - if (options[OptSetSlicedVbiOutFormat]) { - if (vbi_fmt_out.fmt.sliced.service_set == 0) { - // switch to raw mode - vbi_fmt_out.type = V4L2_BUF_TYPE_VBI_OUTPUT; - if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt_out, "VIDIOC_G_FMT") == 0) - doioctl(fd, VIDIOC_S_FMT, &vbi_fmt_out, "VIDIOC_S_FMT"); - } else { - vbi_fmt_out.type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; - doioctl(fd, VIDIOC_S_FMT, &vbi_fmt_out, "VIDIOC_S_FMT"); + if (options[OptSetOverlayFormat] || options[OptTryOverlayFormat]) { + struct v4l2_format fmt; + + fmt.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + if (doioctl(fd, VIDIOC_G_FMT, &fmt, "VIDIOC_G_FMT") == 0) { + if (set_overlay_fmt & FmtChromaKey) + fmt.fmt.win.chromakey = overlay_fmt.fmt.win.chromakey; + if (set_overlay_fmt & FmtGlobalAlpha) + fmt.fmt.win.global_alpha = overlay_fmt.fmt.win.global_alpha; + if (set_overlay_fmt & FmtLeft) + fmt.fmt.win.w.left = overlay_fmt.fmt.win.w.left; + if (set_overlay_fmt & FmtTop) + fmt.fmt.win.w.top = overlay_fmt.fmt.win.w.top; + if (set_overlay_fmt & FmtWidth) + fmt.fmt.win.w.width = overlay_fmt.fmt.win.w.width; + if (set_overlay_fmt & FmtHeight) + fmt.fmt.win.w.height = overlay_fmt.fmt.win.w.height; + if (set_overlay_fmt & FmtField) + fmt.fmt.win.field = overlay_fmt.fmt.win.field; + if (options[OptSetOverlayFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &fmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &fmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(fmt); } } - if (options[OptSetOutputOverlayFormat]) { + if (options[OptSetOutputOverlayFormat] || options[OptTryOutputOverlayFormat]) { struct v4l2_format fmt; fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY; @@ -1677,7 +1923,22 @@ int main(int argc, char **argv) fmt.fmt.win.chromakey = overlay_fmt_out.fmt.win.chromakey; if (set_overlay_fmt_out & FmtGlobalAlpha) fmt.fmt.win.global_alpha = overlay_fmt_out.fmt.win.global_alpha; - doioctl(fd, VIDIOC_S_FMT, &fmt, "VIDIOC_S_FMT"); + if (set_overlay_fmt_out & FmtLeft) + fmt.fmt.win.w.left = overlay_fmt_out.fmt.win.w.left; + if (set_overlay_fmt_out & FmtTop) + fmt.fmt.win.w.top = overlay_fmt_out.fmt.win.w.top; + if (set_overlay_fmt_out & FmtWidth) + fmt.fmt.win.w.width = overlay_fmt_out.fmt.win.w.width; + if (set_overlay_fmt_out & FmtHeight) + fmt.fmt.win.w.height = overlay_fmt_out.fmt.win.w.height; + if (set_overlay_fmt_out & FmtField) + fmt.fmt.win.field = overlay_fmt_out.fmt.win.field; + if (options[OptSetOutputOverlayFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &fmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &fmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(fmt); } } @@ -1997,6 +2258,7 @@ int main(int argc, char **argv) memset(&vt, 0, sizeof(struct v4l2_tuner)); if (doioctl(fd, VIDIOC_G_TUNER, &vt, "VIDIOC_G_TUNER") == 0) { printf("Tuner:\n"); + printf("\tName : %s\n", vt.name); printf("\tCapabilities : %s\n", tcap2s(vt.capability).c_str()); if (vt.capability & V4L2_TUNER_CAP_LOW) printf("\tFrequency range : %.1f MHz - %.1f MHz\n", @@ -2004,7 +2266,7 @@ int main(int argc, char **argv) else printf("\tFrequency range : %.1f MHz - %.1f MHz\n", vt.rangelow / 16.0, vt.rangehigh / 16.0); - printf("\tSignal strength : %d%%\n", (int)(vt.signal / 655.35)); + printf("\tSignal strength/AFC : %d%%/%d\n", (int)(vt.signal / 655.35), vt.afc); printf("\tCurrent audio mode : %s\n", audmode2s(vt.audmode)); printf("\tAvailable subchannels: %s\n", rxsubchans2s(vt.rxsubchans).c_str()); @@ -2118,7 +2380,7 @@ int main(int argc, char **argv) while (ioctl(fd, VIDIOC_ENUMSTD, &vs) >= 0) { if (vs.index) printf("\n"); - printf("\tindex : %d\n", vs.index); + printf("\tIndex : %d\n", vs.index); printf("\tID : 0x%016llX\n", (unsigned long long)vs.id); printf("\tName : %s\n", vs.name); printf("\tFrame period: %d/%d\n", diff --git a/v4l2-apps/util/bttv-dbg.h b/v4l2-apps/util/v4l2-dbg-bttv.h index 02f829773..cf6274284 100644 --- a/v4l2-apps/util/bttv-dbg.h +++ b/v4l2-apps/util/v4l2-dbg-bttv.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "v4l-board-dbg.h" +#include "v4l2-dbg.h" #define BTTV_IDENT "bttv" diff --git a/v4l2-apps/util/em28xx-dbg.h b/v4l2-apps/util/v4l2-dbg-em28xx.h index 3d3600c44..c5117c6e7 100644 --- a/v4l2-apps/util/em28xx-dbg.h +++ b/v4l2-apps/util/v4l2-dbg-em28xx.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "v4l-board-dbg.h" +#include "v4l2-dbg.h" #define EM28XX_IDENT "em28xx" diff --git a/v4l2-apps/util/saa7134-dbg.h b/v4l2-apps/util/v4l2-dbg-saa7134.h index aee29da76..70fd4e068 100644 --- a/v4l2-apps/util/saa7134-dbg.h +++ b/v4l2-apps/util/v4l2-dbg-saa7134.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "v4l-board-dbg.h" +#include "v4l2-dbg.h" #define SAA7134_IDENT "saa7134" diff --git a/v4l2-apps/util/v4l2-dbg.cpp b/v4l2-apps/util/v4l2-dbg.cpp index 2561f42b2..e0d6153fe 100644 --- a/v4l2-apps/util/v4l2-dbg.cpp +++ b/v4l2-apps/util/v4l2-dbg.cpp @@ -42,6 +42,48 @@ #include <map> #include <string> +#include "v4l2-dbg-bttv.h" +#include "v4l2-dbg-saa7134.h" +#include "v4l2-dbg-em28xx.h" + +#define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0]))) + +struct board_list { + const char *name; + int prefix; /* Register prefix size */ + const struct board_regs *regs; + int regs_size; + const struct board_regs *alt_regs; + int alt_regs_size; +}; + +static const struct board_list boards[] = { + { /* From bttv-dbg.h */ + BTTV_IDENT, + sizeof(BTTV_PREFIX) - 1, + bt8xx_regs, + ARRAY_SIZE(bt8xx_regs), + bt8xx_regs_other, + ARRAY_SIZE(bt8xx_regs_other), + }, + { /* From saa7134-dbg.h */ + SAA7134_IDENT, + sizeof(SAA7134_PREFIX) - 1, + saa7134_regs, + ARRAY_SIZE(saa7134_regs), + NULL, + 0, + }, + { /* From em28xx-dbg.h */ + EM28XX_IDENT, + sizeof(EM28XX_PREFIX) - 1, + em28xx_regs, + ARRAY_SIZE(em28xx_regs), + NULL, + 0, + }, +}; + struct driverid { const char *name; unsigned id; @@ -64,19 +106,21 @@ extern struct chipid chipids[]; In general the lower case is used to set something and the upper case is used to retrieve a setting. */ enum Option { - OptListRegisters = 'R', - OptSetRegister = 'r', - OptSetSlicedVbiFormat = 'b', + OptListRegisters = 'l', + OptGetRegister = 'g', + OptSetRegister = 's', OptSetDevice = 'd', OptGetDriverInfo = 'D', - OptScanChipIdents = 'C', - OptGetChipIdent = 'c', + OptChip = 'c', + OptScanChipIdents = 'S', + OptGetChipIdent = 'i', OptSetStride = 'w', OptHelp = 'h', OptLogStatus = 128, OptVerbose, OptListDriverIDs, + OptListSymbols, OptLast = 256 }; @@ -87,56 +131,56 @@ static unsigned capabilities; static struct option long_options[] = { {"device", required_argument, 0, OptSetDevice}, {"help", no_argument, 0, OptHelp}, - {"list-registers", required_argument, 0, OptListRegisters}, + {"list-registers", optional_argument, 0, OptListRegisters}, + {"get-register", required_argument, 0, OptGetRegister}, {"set-register", required_argument, 0, OptSetRegister}, + {"chip", required_argument, 0, OptChip}, {"scan-chip-idents", no_argument, 0, OptScanChipIdents}, {"get-chip-ident", required_argument, 0, OptGetChipIdent}, {"info", no_argument, 0, OptGetDriverInfo}, {"verbose", no_argument, 0, OptVerbose}, {"log-status", no_argument, 0, OptLogStatus}, {"list-driverids", no_argument, 0, OptListDriverIDs}, + {"list-symbols", no_argument, 0, OptListSymbols}, {"wide", required_argument, 0, OptSetStride}, {0, 0, 0, 0} }; static void usage(void) { - printf("Usage:\n"); - printf(" -D, --info show driver info [VIDIOC_QUERYCAP]\n"); - printf(" -d, --device=<dev> use device <dev> instead of /dev/video0\n"); - printf(" if <dev> is a single digit, then /dev/video<dev> is used\n"); - printf(" -h, --help display this help message\n"); - printf(" --verbose turn on verbose ioctl error reporting.\n"); - printf(" -R, --list-registers=type=<host/i2cdrv/i2caddr>,chip=<chip>[,min=<addr>,max=<addr>] \n"); - printf(" dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]\n"); - printf(" -r, --set-register=type=<host/i2cdrv/i2caddr>,chip=<chip>,reg=<addr>,val=<val>\n"); - printf(" set the register [VIDIOC_DBG_S_REGISTER]\n"); - printf(" -C, --scan-chip-idents\n"); - printf(" Scan the available host and i2c chips [VIDIOC_G_CHIP_IDENT]\n"); - printf(" -c, --get-chip-ident=type=<host/i2cdrv/i2caddr>,chip=<chip>\n"); - printf(" Get the chip identifier [VIDIOC_G_CHIP_IDENT]\n"); - printf(" -w, --wide=<reg length>\n"); - printf(" Sets step between two registers\n"); - printf(" --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\n"); - printf(" --list-driverids list the known I2C driver IDs for use with the i2cdrv type\n"); - printf("\n"); - printf(" if type == host, then <chip> is the host's chip ID (default 0)\n"); - printf(" if type == i2cdrv (default), then <chip> is the I2C driver name or ID\n"); - printf(" if type == i2caddr, then <chip> is the 7-bit I2C address\n"); + printf("Usage: v4l2-dbg [options] [values]\n" + " -D, --info Show driver info [VIDIOC_QUERYCAP]\n" + " -d, --device=<dev> Use device <dev> instead of /dev/video0\n" + " If <dev> is a single digit, then /dev/video<dev> is used\n" + " -h, --help Display this help message\n" + " --verbose Turn on verbose ioctl error reporting\n" + " -c, --chip=<chip> The chip identifier to use with other commands\n" + " It can be one of:\n" + " I2C driver ID (see --list-driverids)\n" + " I2C 7-bit address\n" + " host<num>: host chip number <num>\n" + " host (default): same as host0\n" + " -l, --list-registers[=min=<addr>[,max=<addr>]]\n" + " Dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]\n" + " -g, --get-register=<addr>\n" + " Get the specified register [VIDIOC_DBG_G_REGISTER]\n" + " -s, --set-register=<addr>\n" + " Set the register with the commandline arguments\n" + " The register will autoincrement [VIDIOC_DBG_S_REGISTER]\n" + " -S, --scan-chip-idents\n" + " Scan the available host and i2c chips [VIDIOC_G_CHIP_IDENT]\n" + " -i, --get-chip-ident\n" + " Get the chip identifier [VIDIOC_G_CHIP_IDENT]\n" + " -w, --wide=<reg length>\n" + " Sets step between two registers\n" + " --list-symbols List the symbolic register names you can use, if any\n" + " --log-status Log the board status in the kernel log [VIDIOC_LOG_STATUS]\n" + " --list-driverids List the known I2C driver IDs for use with the i2cdrv type\n"); exit(0); } -static unsigned parse_type(const std::string &s) -{ - if (s == "host") return V4L2_CHIP_MATCH_HOST; - if (s == "i2caddr") return V4L2_CHIP_MATCH_I2C_ADDR; - return V4L2_CHIP_MATCH_I2C_DRIVER; -} - -static unsigned parse_chip(int type, const std::string &s) +static unsigned parse_chip(const std::string &s) { - if (type == V4L2_CHIP_MATCH_HOST || type == V4L2_CHIP_MATCH_I2C_ADDR || isdigit(s[0])) - return strtoul(s.c_str(), 0, 0); for (int i = 0; driverids[i].name; i++) if (!strcasecmp(s.c_str(), driverids[i].name)) return driverids[i].id; @@ -230,6 +274,56 @@ static void print_chip(struct v4l2_chip_ident *chip) printf("%-10d revision 0x%08x\n", chip->ident, chip->revision); } +static unsigned long long parse_reg(const struct board_list *curr_bd, const std::string ®) +{ + if (curr_bd) { + for (int i = 0; i < curr_bd->regs_size; i++) { + if (!strcasecmp(reg.c_str(), curr_bd->regs[i].name) || + !strcasecmp(reg.c_str(), curr_bd->regs[i].name + curr_bd->prefix)) { + return curr_bd->regs[i].reg; + } + } + for (int i = 0; i < curr_bd->alt_regs_size; i++) { + if (!strcasecmp(reg.c_str(), curr_bd->alt_regs[i].name) || + !strcasecmp(reg.c_str(), curr_bd->alt_regs[i].name + curr_bd->prefix)) { + return curr_bd->alt_regs[i].reg; + } + } + } + return strtoull(reg.c_str(), NULL, 0); +} + +static const char *binary(unsigned long long val) +{ + static char bin[80]; + char *p = bin; + int i, j; + int bits = 64; + + if ((val & 0xffffffff00000000LL) == 0) { + if ((val & 0xffff0000) == 0) { + if ((val & 0xff00) == 0) + bits = 8; + else + bits= 16; + } + else + bits = 32; + } + + for (i = bits - 1; i >= 0; i -= 8) { + for (j = i; j >= i - 7; j--) { + if (val & (1LL << j)) + *p++ = '1'; + else + *p++ = '0'; + } + *p++ = ' '; + } + p[-1] = 0; + return bin; +} + static int doioctl(int fd, int request, void *parm, const char *name) { int retVal; @@ -277,9 +371,15 @@ int main(int argc, char **argv) struct v4l2_register set_reg; struct v4l2_register get_reg; struct v4l2_chip_ident chip_id; + const struct board_list *curr_bd = NULL; char short_options[26 * 2 * 2 + 1]; int idx = 0; + std::string reg_min_arg, reg_max_arg; + std::string reg_set_arg; unsigned long long reg_min = 0, reg_max = 0; + std::vector<std::string> get_regs; + int match_type = V4L2_CHIP_MATCH_HOST; + int match_chip = 0; memset(&set_reg, 0, sizeof(set_reg)); memset(&get_reg, 0, sizeof(get_reg)); @@ -310,6 +410,7 @@ int main(int argc, char **argv) case OptHelp: usage(); return 0; + case OptSetDevice: device = optarg; if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { @@ -320,43 +421,44 @@ int main(int argc, char **argv) device = newdev; } break; - case OptSetRegister: - subs = optarg; - set_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; - while (*subs != '\0') { - static const char * const subopts[] = { - "type", - "chip", - "reg", - "val", - NULL - }; - switch (parse_subopt(&subs, subopts, &value)) { - case 0: - set_reg.match_type = parse_type(value); - break; - case 1: - set_reg.match_chip = parse_chip(set_reg.match_type, value); - break; - case 2: - set_reg.reg = strtoull(value, 0L, 0); - break; - case 3: - set_reg.val = strtoull(value, 0L, 0); - break; - } + case OptChip: + if (isdigit(optarg[0])) { + match_type = V4L2_CHIP_MATCH_I2C_ADDR; + match_chip = strtoul(optarg, NULL, 0); + break; + } + if (!memcmp(optarg, "host", 4)) { + match_type = V4L2_CHIP_MATCH_HOST; + match_chip = strtoul(optarg + 4, NULL, 0); + break; } + match_type = V4L2_CHIP_MATCH_I2C_DRIVER; + match_chip = parse_chip(optarg); + if (!match_chip) { + fprintf(stderr, "unknown driver ID %s\n", optarg); + exit(-1); + } + break; + + case OptSetRegister: + reg_set_arg = optarg; + break; + + case OptGetRegister: + get_regs.push_back(optarg); + break; + case OptSetStride: forcedstride = strtoull(optarg, 0L, 0); break; + case OptListRegisters: subs = optarg; - get_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; + if (subs == NULL) + break; while (*subs != '\0') { static const char * const subopts[] = { - "type", - "chip", "min", "max", NULL @@ -364,47 +466,27 @@ int main(int argc, char **argv) switch (parse_subopt(&subs, subopts, &value)) { case 0: - get_reg.match_type = parse_type(value); + reg_min_arg = value; + //if (reg_max == 0) + // reg_max = reg_min + 0xff; break; case 1: - get_reg.match_chip = parse_chip(get_reg.match_type, value); - break; - case 2: - reg_min = strtoull(value, 0L, 0); - if (reg_max == 0) - reg_max = reg_min + 0xff; - break; - case 3: - reg_max = strtoull(value, 0L, 0); + reg_max_arg = value; break; } } break; - case OptGetChipIdent: - subs = optarg; - set_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; - while (*subs != '\0') { - static const char *const subopts[] = { - "type", - "chip", - NULL - }; - switch (parse_subopt(&subs, subopts, &value)) { - case 0: - chip_id.match_type = parse_type(value); - break; - case 1: - chip_id.match_chip = parse_chip(chip_id.match_type, value); - break; - } - } + case OptGetChipIdent: + case OptListSymbols: break; + case ':': fprintf(stderr, "Option `%s' requires a value\n", argv[optind]); usage(); return 1; + case '?': fprintf(stderr, "Unknown argument `%s'\n", argv[optind]); @@ -412,14 +494,6 @@ int main(int argc, char **argv) return 1; } } - if (optind < argc) { - printf("unknown arguments: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - usage(); - return 1; - } if ((fd = open(device, O_RDWR)) < 0) { fprintf(stderr, "Failed to open %s: %s\n", device, @@ -442,15 +516,33 @@ int main(int argc, char **argv) printf("%s", cap2s(vcap.capabilities).c_str()); } + for (int board = ARRAY_SIZE(boards) - 1; board >= 0; board--) { + if (!strcasecmp((char *)vcap.driver, boards[board].name)) { + curr_bd = &boards[board]; + break; + } + } + /* Set options */ if (options[OptSetRegister]) { - if (doioctl(fd, VIDIOC_DBG_S_REGISTER, &set_reg, - "VIDIOC_DBG_S_REGISTER") == 0) - printf("register 0x%llx set to 0x%llx\n", set_reg.reg, set_reg.val); + set_reg.match_type = match_type; + set_reg.match_chip = match_chip; + if (optind >= argc) + usage(); + set_reg.reg = parse_reg(curr_bd, reg_set_arg); + while (optind < argc) { + set_reg.val = strtoull(argv[optind++], NULL, 0); + if (doioctl(fd, VIDIOC_DBG_S_REGISTER, &set_reg, + "VIDIOC_DBG_S_REGISTER") == 0) + printf("register 0x%llx set to 0x%llx\n", set_reg.reg, set_reg.val); + set_reg.reg++; + } } if (options[OptGetChipIdent]) { + chip_id.match_type = match_type; + chip_id.match_chip = match_chip; if (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip_id, "VIDIOC_G_CHIP_IDENT") == 0) print_chip(&chip_id); } @@ -462,7 +554,7 @@ int main(int argc, char **argv) chip_id.match_chip = 0; while (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip_id, "VIDIOC_G_CHIP_IDENT") == 0 && chip_id.ident) { - printf("host 0x%x: ", chip_id.match_chip); + printf("host%d: ", chip_id.match_chip); print_chip(&chip_id); chip_id.match_chip++; } @@ -477,8 +569,30 @@ int main(int argc, char **argv) } } + if (options[OptGetRegister]) { + int stride = 1; + + get_reg.match_type = match_type; + get_reg.match_chip = match_chip; + printf("ioctl: VIDIOC_DBG_G_REGISTER\n"); + + for (std::vector<std::string>::iterator iter = get_regs.begin(); + iter != get_regs.end(); ++iter) { + get_reg.reg = parse_reg(curr_bd, *iter); + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, &get_reg) < 0) + fprintf(stderr, "ioctl: VIDIOC_DBG_G_REGISTER " + "failed for 0x%llx\n", get_reg.reg); + else + printf("%llx = %llxh = %lldd = %sb\n", get_reg.reg, + get_reg.val, get_reg.val, binary(get_reg.val)); + } + } + if (options[OptListRegisters]) { int stride = 1; + + get_reg.match_type = match_type; + get_reg.match_chip = match_chip; if (forcedstride) { stride = forcedstride; } else { @@ -487,7 +601,12 @@ int main(int argc, char **argv) } printf("ioctl: VIDIOC_DBG_G_REGISTER\n"); - if (reg_max != 0) { + if (!reg_min_arg.empty()) { + reg_min = parse_reg(curr_bd, reg_min_arg); + if (reg_max_arg.empty()) + reg_max = reg_min + 0xff; + else + reg_max = parse_reg(curr_bd, reg_max_arg); /* Explicit memory range: just do it */ print_regs(fd, &get_reg, reg_min, reg_max, stride); goto list_done; @@ -580,6 +699,19 @@ list_done: printf("%s\n", driverids[i].name); } + if (options[OptListSymbols]) { + if (curr_bd == NULL) { + printf("No symbols found for driver %s\n", vcap.driver); + } + else { + printf("Symbols for driver %s:\n", vcap.driver); + for (int i = 0; i < curr_bd->regs_size; i++) + printf("0x%08x: %s\n", curr_bd->regs[i], curr_bd->regs[i].name); + for (int i = 0; i < curr_bd->alt_regs_size; i++) + printf("0x%08x: %s\n", curr_bd->alt_regs[i], curr_bd->alt_regs[i].name); + } + } + close(fd); exit(0); } diff --git a/v4l2-apps/util/v4l-board-dbg.h b/v4l2-apps/util/v4l2-dbg.h index d7e7a9ef3..06ceb0ea2 100644 --- a/v4l2-apps/util/v4l-board-dbg.h +++ b/v4l2-apps/util/v4l2-dbg.h @@ -14,11 +14,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _V4L_BOARD_DBG -#define _V4L_BOARD_DBG +#ifndef _V4L2_DBG_H_ +#define _V4L2_DBG_H_ + struct board_regs { unsigned int reg; - char *name; + const char *name; int size; }; + #endif diff --git a/v4l2-apps/util/xc3028-firmware/Makefile b/v4l2-apps/util/xc3028-firmware/Makefile index 102f712fd..6894e984e 100644 --- a/v4l2-apps/util/xc3028-firmware/Makefile +++ b/v4l2-apps/util/xc3028-firmware/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/util/xc3028-firmware -CPPFLAGS += -I../../../linux/include +CPPFLAGS += -I../../include binaries = firmware-tool |