diff options
Diffstat (limited to 'linux/drivers/media/video')
94 files changed, 16569 insertions, 2818 deletions
diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 6c26618b8..27f639763 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -582,7 +582,6 @@ config VIDEO_SAA5249 config VIDEO_VINO tristate "SGI Vino Video For Linux (EXPERIMENTAL)" depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2 - select I2C_ALGO_SGI select VIDEO_SAA7191 if VIDEO_HELPER_CHIPS_AUTO help Say Y here to build in support for the Vino video input system found @@ -806,6 +805,8 @@ source "drivers/media/video/pvrusb2/Kconfig" source "drivers/media/video/em28xx/Kconfig" +source "drivers/media/video/cx231xx/Kconfig" + source "drivers/media/video/usbvision/Kconfig" source "drivers/media/video/usbvideo/Kconfig" diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 307490ebc..99b448e6c 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o -obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o @@ -68,6 +67,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ +obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o diff --git a/linux/drivers/media/video/adv7170.c b/linux/drivers/media/video/adv7170.c index 87bc8ed46..04e6125f2 100644 --- a/linux/drivers/media/video/adv7170.c +++ b/linux/drivers/media/video/adv7170.c @@ -205,7 +205,7 @@ static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) { struct adv7170 *encoder = to_adv7170(sd); - v4l2_dbg(1, debug, sd, "set norm %llx\n", std); + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); if (std & V4L2_STD_NTSC) { adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); @@ -220,10 +220,11 @@ static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) adv7170_write(sd, 0x07, TR0MODE | TR0RST); adv7170_write(sd, 0x07, TR0MODE); } else { - v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", std); + v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", + (unsigned long long)std); return -EINVAL; } - v4l2_dbg(1, debug, sd, "switched to %llx\n", std); + v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); encoder->norm = std; return 0; } diff --git a/linux/drivers/media/video/adv7175.c b/linux/drivers/media/video/adv7175.c index dc036d2fa..c38f648f8 100644 --- a/linux/drivers/media/video/adv7175.c +++ b/linux/drivers/media/video/adv7175.c @@ -238,10 +238,11 @@ static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) adv7175_write(sd, 0x07, TR0MODE | TR0RST); adv7175_write(sd, 0x07, TR0MODE); } else { - v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", std); + v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", + (unsigned long long)std); return -EINVAL; } - v4l2_dbg(1, debug, sd, "switched to %llx\n", std); + v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); encoder->norm = std; return 0; } diff --git a/linux/drivers/media/video/au0828/Kconfig b/linux/drivers/media/video/au0828/Kconfig index deb00e4ac..20993ae17 100644 --- a/linux/drivers/media/video/au0828/Kconfig +++ b/linux/drivers/media/video/au0828/Kconfig @@ -5,9 +5,9 @@ config VIDEO_AU0828 select I2C_ALGOBIT select VIDEO_TVEEPROM select DVB_AU8522 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5007T if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE ---help--- This is a video4linux driver for Auvitek's USB device. diff --git a/linux/drivers/media/video/bt819.c b/linux/drivers/media/video/bt819.c index a024783e5..ea4d0fbc4 100644 --- a/linux/drivers/media/video/bt819.c +++ b/linux/drivers/media/video/bt819.c @@ -255,7 +255,7 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) struct bt819 *decoder = to_bt819(sd); struct timing *timing = NULL; - v4l2_dbg(1, debug, sd, "set norm %llx\n", std); + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) v4l2_err(sd, "no notify found!\n"); @@ -279,7 +279,8 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) /* bt819_setbit(decoder, 0x1a, 5, 0); */ timing = &timing_data[0]; } else { - v4l2_dbg(1, debug, sd, "unsupported norm %llx\n", std); + v4l2_dbg(1, debug, sd, "unsupported norm %llx\n", + (unsigned long long)std); return -EINVAL; } bt819_write(decoder, 0x03, diff --git a/linux/drivers/media/video/bt856.c b/linux/drivers/media/video/bt856.c index fc7b64d91..ab56d7d6e 100644 --- a/linux/drivers/media/video/bt856.c +++ b/linux/drivers/media/video/bt856.c @@ -131,7 +131,7 @@ static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) { struct bt856 *encoder = to_bt856(sd); - v4l2_dbg(1, debug, sd, "set norm %llx\n", std); + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); if (std & V4L2_STD_NTSC) { bt856_setbit(encoder, 0xdc, 2, 0); diff --git a/linux/drivers/media/video/bt866.c b/linux/drivers/media/video/bt866.c index edc1aa348..5ea31375e 100644 --- a/linux/drivers/media/video/bt866.c +++ b/linux/drivers/media/video/bt866.c @@ -97,7 +97,7 @@ static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data) static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) { - v4l2_dbg(1, debug, sd, "set norm %llx\n", std); + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); /* Only PAL supported by this driver at the moment! */ if (!(std & V4L2_STD_NTSC)) diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index aed55398d..57448806c 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -299,6 +299,8 @@ static struct CARD { /* Duplicate PCI ID, reconfigure for this board during the eeprom read. * { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB, "Hauppauge ImpactVCB" }, */ + { 0x109e036e, BTTV_BOARD_CONCEPTRONIC_CTVFMI2, "Conceptronic CTVFMi v2"}, + /* DVB cards (using pci function .1 for mpeg data xfer) */ { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" }, { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" }, @@ -3744,7 +3746,11 @@ static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256]) unsigned short type; for (i = 4*16; i < 8*16; i += 16) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + u16 checksum = ip_compute_csum((unsigned char *)(ee + i), 16); +#else u16 checksum = ip_compute_csum(ee + i, 16); +#endif if ((checksum&0xff) + (checksum>>8) == 0xff) break; diff --git a/linux/drivers/media/video/bt8xx/bttv-i2c.c b/linux/drivers/media/video/bt8xx/bttv-i2c.c index 66fc4e480..3e7812c5a 100644 --- a/linux/drivers/media/video/bt8xx/bttv-i2c.c +++ b/linux/drivers/media/video/bt8xx/bttv-i2c.c @@ -401,7 +401,11 @@ int __devinit init_bttv_i2c(struct bttv *btv) strlcpy(btv->c.i2c_adap.name, "bt878", sizeof(btv->c.i2c_adap.name)); btv->c.i2c_adap.id = I2C_HW_B_BT848; /* FIXME */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) + btv->c.i2c_adap.algo = (struct i2c_algorithm *)&bttv_algo; +#else btv->c.i2c_adap.algo = &bttv_algo; +#endif } else { /* bt848 */ /* Prevents usage of invalid delay values */ diff --git a/linux/drivers/media/video/bt8xx/bttv.h b/linux/drivers/media/video/bt8xx/bttv.h index 35f943337..93a1e989a 100644 --- a/linux/drivers/media/video/bt8xx/bttv.h +++ b/linux/drivers/media/video/bt8xx/bttv.h @@ -14,7 +14,7 @@ #ifndef _BTTV_H_ #define _BTTV_H_ -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/i2c.h> #include "compat.h" #include <media/ir-common.h> diff --git a/linux/drivers/media/video/bt8xx/bttvp.h b/linux/drivers/media/video/bt8xx/bttvp.h index 20b1ad68a..113f28320 100644 --- a/linux/drivers/media/video/bt8xx/bttvp.h +++ b/linux/drivers/media/video/bt8xx/bttvp.h @@ -32,7 +32,6 @@ #include <linux/wait.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> -#include <linux/videodev.h> #include <linux/pci.h> #include <linux/input.h> #include <linux/mutex.h> diff --git a/linux/drivers/media/video/cpia2/cpia2_v4l.c b/linux/drivers/media/video/cpia2/cpia2_v4l.c index 9c25894fd..d4099f531 100644 --- a/linux/drivers/media/video/cpia2/cpia2_v4l.c +++ b/linux/drivers/media/video/cpia2/cpia2_v4l.c @@ -37,6 +37,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/init.h> +#include <linux/videodev.h> #include <media/v4l2-ioctl.h> #include "cpia2.h" diff --git a/linux/drivers/media/video/cx18/cx18-audio.c b/linux/drivers/media/video/cx18/cx18-audio.c index ccd170887..bb5c5165d 100644 --- a/linux/drivers/media/video/cx18/cx18-audio.c +++ b/linux/drivers/media/video/cx18/cx18-audio.c @@ -24,6 +24,7 @@ #include "cx18-driver.h" #include "cx18-io.h" #include "cx18-cards.h" +#include "cx18-audio.h" #define CX18_AUDIO_ENABLE 0xc72014 diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index cf256a999..21f4be839 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/drivers/media/video/cx18/cx18-av-core.c @@ -292,23 +292,29 @@ void cx18_av_std_setup(struct cx18 *cx) * * vsync: always 6 half-lines of vsync pulses * vactive: half lines of active video - * vblank656: half lines, after line 3, of blanked video - * vblank: half lines, after line 9, of blanked video + * vblank656: half lines, after line 3/mid-266, of blanked video + * vblank: half lines, after line 9/272, of blanked video * + * As far as I can tell: * vblank656 starts counting from the falling edge of the first - * vsync pulse (start of line 4) + * vsync pulse (start of line 4 or mid-266) * vblank starts counting from the after the 6 vsync pulses and - * 6 equalization pulses (start of line 10) + * 6 or 5 equalization pulses (start of line 10 or 272) * * For 525 line systems the driver will extract VBI information - * from lines 10 through 21. To avoid the EAV RP code from - * toggling at the start of hblank at line 22, where sliced VBI - * data from line 21 is stuffed, also treat line 22 as blanked. + * from lines 10-21 and lines 273-284. */ - vblank656 = 38; /* lines 4 through 22 */ - vblank = 26; /* lines 10 through 22 */ - vactive = 481; /* lines 23 through 262.5 */ + vblank656 = 38; /* lines 4 - 22 & 266 - 284 */ + vblank = 26; /* lines 10 - 22 & 272 - 284 */ + vactive = 481; /* lines 23 - 263 & 285 - 525 */ + /* + * For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is + * is 858 pixels = 720 active + 138 blanking. The Hsync leading + * edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the + * end of active video, leaving 122 pixels of hblank to ignore + * before active video starts. + */ hactive = 720; hblank = 122; luma_lpf = 1; @@ -867,8 +873,22 @@ static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4; Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4; - Vlines = pix->height + (is_50Hz ? 4 : 7); + /* + * This adjustment reflects the excess of vactive, set in + * cx18_av_std_setup(), above standard values: + * + * 480 + 1 for 60 Hz systems + * 576 + 4 for 50 Hz systems + */ + Vlines = pix->height + (is_50Hz ? 4 : 1); + /* + * Invalid height and width scaling requests are: + * 1. width less than 1/16 of the source width + * 2. width greater than the source width + * 3. height less than 1/8 of the source height + * 4. height greater than the source height + */ if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) || (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n", diff --git a/linux/drivers/media/video/cx18/cx18-av-core.h b/linux/drivers/media/video/cx18/cx18-av-core.h index fd0df4151..2687a2c91 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.h +++ b/linux/drivers/media/video/cx18/cx18-av-core.h @@ -89,16 +89,21 @@ struct cx18_av_state { /* * The VBI slicer starts operating and counting lines, begining at - * slicer line count of 1, at D lines after the deassertion of VRESET - * This staring field line, S, is 6 or 10 for 625 or 525 line systems. - * Sliced ancillary data captured on VBI slicer line M is sent at the - * beginning of the next VBI slicer line, VBI slicer line count N = M+1. - * Thus when the VBI slicer reports a VBI slicer line number with - * ancillary data, the IDID0 byte indicates VBI slicer line N. - * The actual field line that the captured data comes from is + * slicer line count of 1, at D lines after the deassertion of VRESET. + * This staring field line, S, is 6 (& 319) or 10 (& 273) for 625 or 525 + * line systems respectively. Sliced ancillary data captured on VBI + * slicer line M is inserted after the VBI slicer is done with line M, + * when VBI slicer line count is N = M+1. Thus when the VBI slicer + * reports a VBI slicer line number with ancillary data, the IDID0 byte + * indicates VBI slicer line N. The actual field line that the captured + * data comes from is + * * L = M+(S+D-1) = N-1+(S+D-1) = N + (S+D-2). * + * L is the line in the field, not frame, from which the VBI data came. + * N is the line reported by the slicer in the ancillary data. * D is the slicer_line_delay value programmed into register 0x47f. + * S is 6 for 625 line systems or 10 for 525 line systems * (S+D-2) is the slicer_line_offset used to convert slicer reported * line counts to actual field lines. */ diff --git a/linux/drivers/media/video/cx18/cx18-av-vbi.c b/linux/drivers/media/video/cx18/cx18-av-vbi.c index 43267d1af..27699839b 100644 --- a/linux/drivers/media/video/cx18/cx18-av-vbi.c +++ b/linux/drivers/media/video/cx18/cx18-av-vbi.c @@ -142,7 +142,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ V4L2_SLICED_CAPTION_525, /* 6 */ - V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 - unlike cx25840 */ + 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ 0, 0, 0, 0 }; int is_pal = !(state->std & V4L2_STD_525_60); @@ -243,7 +243,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) lcr[i] |= 6 << (4 * x); break; case V4L2_SLICED_VPS: - lcr[i] |= 7 << (4 * x); /*'840 differs*/ + lcr[i] |= 9 << (4 * x); break; } } @@ -301,7 +301,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) sdid = V4L2_SLICED_CAPTION_525; err = !odd_parity(p[0]) || !odd_parity(p[1]); break; - case 7: /* Differs from cx25840 */ + case 9: sdid = V4L2_SLICED_VPS; if (decode_vps(p, p) != 0) err = 1; diff --git a/linux/drivers/media/video/cx18/cx18-controls.c b/linux/drivers/media/video/cx18/cx18-controls.c index 925e01fdb..82fc2f9d4 100644 --- a/linux/drivers/media/video/cx18/cx18-controls.c +++ b/linux/drivers/media/video/cx18/cx18-controls.c @@ -166,15 +166,26 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) return 0; } -static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt) +static int cx18_setup_vbi_fmt(struct cx18 *cx, + enum v4l2_mpeg_stream_vbi_fmt fmt, + enum v4l2_mpeg_stream_type type) { if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE)) return -EINVAL; if (atomic_read(&cx->ana_capturing) > 0) return -EBUSY; - /* First try to allocate sliced VBI buffers if needed. */ - if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) { + if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV || + type != V4L2_MPEG_STREAM_TYPE_MPEG2_PS) { + /* We don't do VBI insertion aside from IVTV format in a PS */ + cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE; + CX18_DEBUG_INFO("disabled insertion of sliced VBI data into " + "the MPEG stream\n"); + return 0; + } + + /* Allocate sliced VBI buffers if needed. */ + if (cx->vbi.sliced_mpeg_data[0] == NULL) { int i; for (i = 0; i < CX18_VBI_FRAMES; i++) { @@ -185,19 +196,27 @@ static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt kfree(cx->vbi.sliced_mpeg_data[i]); cx->vbi.sliced_mpeg_data[i] = NULL; } + cx->vbi.insert_mpeg = + V4L2_MPEG_STREAM_VBI_FMT_NONE; + CX18_WARN("Unable to allocate buffers for " + "sliced VBI data insertion\n"); return -ENOMEM; } } } cx->vbi.insert_mpeg = fmt; + CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS," + "when sliced VBI is enabled\n"); - if (cx->vbi.insert_mpeg == 0) - return 0; - /* Need sliced data for mpeg insertion */ + /* + * If our current settings have no lines set for capture, store a valid, + * default set of service lines to capture, in our current settings. + */ if (cx18_get_service_set(cx->vbi.sliced_in) == 0) { if (cx->is_60hz) - cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; + cx->vbi.sliced_in->service_set = + V4L2_SLICED_CAPTION_525; else cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz); @@ -284,8 +303,11 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) priv.cx = cx; priv.s = &cx->streams[id->type]; err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p); - if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt) - err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt); + if (!err && + (cx->params.stream_vbi_fmt != p.stream_vbi_fmt || + cx->params.stream_type != p.stream_type)) + err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt, + p.stream_type); cx->params = p; cx->dualwatch_stereo_mode = p.audio_properties & 0x0300; idx = p.audio_properties & 0x03; diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 8f294f436..f688ec7db 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -273,8 +273,7 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) u8 eedata[256]; memset(&c, 0, sizeof(c)); - strncpy(c.name, "cx18 tveeprom tmp", sizeof(c.name)); - c.name[sizeof(c.name)-1] = '\0'; + strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name)); c.adapter = &cx->i2c_adap[0]; c.addr = 0xA0 >> 1; diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 0605c2d83..7c1db9c18 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -413,9 +413,8 @@ static void cx18_vbi_setup(struct cx18_stream *s) * 0x90 (Task HorizontalBlank) * 0xd0 (Task EvenField HorizontalBlank) * - * We have set the digitzer to consider the first active line - * as part of VerticalBlank as well so we don't have to look for - * these problem codes nor lose the last line of sliced data. + * We have set the digitzer such that we don't have to worry + * about these problem codes. */ data[4] = 0xB0F0B0F0; /* diff --git a/linux/drivers/media/video/cx18/cx18-vbi.c b/linux/drivers/media/video/cx18/cx18-vbi.c index a81fe2e98..355737bff 100644 --- a/linux/drivers/media/video/cx18/cx18-vbi.c +++ b/linux/drivers/media/video/cx18/cx18-vbi.c @@ -169,7 +169,7 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, int streamtype) { u8 *p = (u8 *) buf->buf; - u32 *q = (u32 *) buf->buf; + __be32 *q = (__be32 *) buf->buf; u32 size = buf->bytesused; u32 pts; int lines; @@ -178,8 +178,9 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, return; /* - * The CX23418 sends us data that is 32 bit LE swapped, but we want - * the raw VBI bytes in the order they were in the raster line + * The CX23418 sends us data that is 32 bit little-endian swapped, + * but we want the raw VBI bytes in the order they were in the raster + * line. This has a side effect of making the 12 byte header big endian */ cx18_buf_swap(buf); @@ -218,7 +219,7 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, /* Sliced VBI data with data insertion */ - pts = (be32_to_cpu(q[0] == 0x3fffffff)) ? be32_to_cpu(q[2]) : 0; + pts = (be32_to_cpu(q[0]) == 0x3fffffff) ? be32_to_cpu(q[2]) : 0; /* * For calls to compress_sliced_buf(), ensure there are an integral diff --git a/linux/drivers/media/video/cx231xx/Kconfig b/linux/drivers/media/video/cx231xx/Kconfig new file mode 100644 index 000000000..7a6700fb0 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/Kconfig @@ -0,0 +1,35 @@ +config VIDEO_CX231XX + tristate "Conexant cx231xx USB video capture support" + depends on VIDEO_DEV && I2C && INPUT + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEO_IR + select VIDEOBUF_VMALLOC + select VIDEO_CX25840 + select VIDEO_CX231XX_ALSA + + ---help--- + This is a video4linux driver for Conexant 231xx USB based TV cards. + + To compile this driver as a module, choose M here: the + module will be called cx231xx + +config VIDEO_CX231XX_ALSA + tristate "Conexant Cx231xx ALSA audio module" + depends on VIDEO_CX231XX && SND + select SND_PCM + + ---help--- + This is an ALSA driver for Cx231xx USB based TV cards. + + To compile this driver as a module, choose M here: the + module will be called cx231xx-alsa + +config VIDEO_CX231XX_DVB + tristate "DVB/ATSC Support for Cx231xx based TV cards" + depends on VIDEO_CX231XX && DVB_CORE + select VIDEOBUF_DVB + select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE + ---help--- + This adds support for DVB cards based on the + Conexant cx231xx chips. diff --git a/linux/drivers/media/video/cx231xx/Makefile b/linux/drivers/media/video/cx231xx/Makefile new file mode 100644 index 000000000..1dad93619 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/Makefile @@ -0,0 +1,12 @@ +cx231xx-objs := cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o \ + cx231xx-avcore.o cx231xx-pcb-cfg.o cx231xx-vbi.o + +obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o +obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-audio.o +obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/common/tuners +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends + diff --git a/linux/drivers/media/video/cx231xx/cx231xx-audio.c b/linux/drivers/media/video/cx231xx/cx231xx-audio.c new file mode 100644 index 000000000..b6f099042 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-audio.c @@ -0,0 +1,667 @@ +/* + * Conexant Cx231xx audio extension + * + * Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + * Based on em28xx 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. + * + * 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/usb.h> +#include <linux/init.h> +#include <linux/sound.h> +#include <linux/spinlock.h> +#include <linux/soundcard.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/proc_fs.h> +#include <linux/module.h> +#include "compat.h" +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/initval.h> +#include <sound/control.h> +#include <media/v4l2-common.h> +#include "cx231xx.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_INFO "cx231xx-audio %s: " fmt, \ + __func__, ##arg); \ + } while (0) + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; + +static int cx231xx_isoc_audio_deinit(struct cx231xx *dev) +{ + int i; + + dprintk("Stopping isoc\n"); + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + if (dev->adev.urb[i]) { + if (!irqs_disabled()) + usb_kill_urb(dev->adev.urb[i]); + else + usb_unlink_urb(dev->adev.urb[i]); + + usb_free_urb(dev->adev.urb[i]); + dev->adev.urb[i] = NULL; + + kfree(dev->adev.transfer_buffer[i]); + dev->adev.transfer_buffer[i] = NULL; + } + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void cx231xx_audio_isocirq(struct urb *urb, struct pt_regs *regs) +#else +static void cx231xx_audio_isocirq(struct urb *urb) +#endif +{ + struct cx231xx *dev = urb->context; + int i; + unsigned int oldptr; +#ifdef NO_PCM_LOCK + unsigned long flags; +#endif + int period_elapsed = 0; + int status; + unsigned char *cp; + unsigned int stride; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; +#else + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; +#endif + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dprintk("urb completition error %d.\n", urb->status); + break; + } + + if (dev->adev.capture_pcm_substream) { + substream = dev->adev.capture_pcm_substream; + runtime = substream->runtime; + stride = runtime->frame_bits >> 3; + + for (i = 0; i < urb->number_of_packets; i++) { + int length = urb->iso_frame_desc[i].actual_length / + stride; + cp = (unsigned char *)urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!length) + continue; + +#ifdef NO_PCM_LOCK + spin_lock_irqsave(&dev->adev.slock, flags); +#endif + oldptr = dev->adev.hwptr_done_capture; + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt; + + cnt = runtime->buffer_size - oldptr; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt * stride, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + +#ifndef NO_PCM_LOCK + snd_pcm_stream_lock(substream); +#endif + + dev->adev.hwptr_done_capture += length; + if (dev->adev.hwptr_done_capture >= + runtime->buffer_size) + dev->adev.hwptr_done_capture -= + runtime->buffer_size; + + dev->adev.capture_transfer_done += length; + if (dev->adev.capture_transfer_done >= + runtime->period_size) { + dev->adev.capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } +#ifdef NO_PCM_LOCK + spin_unlock_irqrestore(&dev->adev.slock, flags); +#else + snd_pcm_stream_unlock(substream); +#endif + } + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + urb->status = 0; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + cx231xx_errdev("resubmit of audio urb failed (error=%i)\n", + status); + } + return; +} + +static int cx231xx_init_audio_isoc(struct cx231xx *dev) +{ + int i, errCode; + int sb_size; + + cx231xx_info("%s: Starting AUDIO transfers\n", __func__); + + sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + struct urb *urb; + int j, k; + + dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); + if (!dev->adev.transfer_buffer[i]) + return -ENOMEM; + + memset(dev->adev.transfer_buffer[i], 0x80, sb_size); + urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); + if (!urb) { + cx231xx_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; + urb->pipe = usb_rcvisocpipe(dev->udev, + dev->adev.end_point_addr); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->adev.transfer_buffer[i]; + urb->interval = 1; + urb->complete = cx231xx_audio_isocirq; + urb->number_of_packets = CX231XX_NUM_AUDIO_PACKETS; + urb->transfer_buffer_length = sb_size; + + for (j = k = 0; j < CX231XX_NUM_AUDIO_PACKETS; + j++, k += dev->adev.max_pkt_size) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = dev->adev.max_pkt_size; + } + dev->adev.urb[i] = urb; + } + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); + if (errCode < 0) { + cx231xx_isoc_audio_deinit(dev); + return errCode; + } + } + + return errCode; +} + +static int cx231xx_cmd(struct cx231xx *dev, int cmd, int arg) +{ + dprintk("%s transfer\n", (dev->adev.capture_stream == STREAM_ON) ? + "stop" : "start"); + + switch (cmd) { + case CX231XX_CAPTURE_STREAM_EN: + if (dev->adev.capture_stream == STREAM_OFF && arg == 1) { + dev->adev.capture_stream = STREAM_ON; + cx231xx_init_audio_isoc(dev); + } else if (dev->adev.capture_stream == STREAM_ON && arg == 0) { + dev->adev.capture_stream = STREAM_OFF; + cx231xx_isoc_audio_deinit(dev); + } else { + cx231xx_errdev("An underrun very likely occurred. " + "Ignoring it.\n"); + } + return 0; + default: + return -EINVAL; + } +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) +#else +static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, + size_t size) +#endif +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_runtime_t *runtime = subs->runtime; +#else + struct snd_pcm_runtime *runtime = subs->runtime; +#endif + + dprintk("Allocating vbuffer\n"); + if (runtime->dma_area) { + if (runtime->dma_bytes > size) + return 0; + + vfree(runtime->dma_area); + } + runtime->dma_area = vmalloc(size); + if (!runtime->dma_area) + return -ENOMEM; + + runtime->dma_bytes = size; + + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_hardware_t snd_cx231xx_hw_capture = { +#else +static struct snd_pcm_hardware snd_cx231xx_hw_capture = { +#endif + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ + .period_bytes_min = 64, /* 12544/2, */ + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, /* 12544, */ +}; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_capture_open(snd_pcm_substream_t *substream) +#else +static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream) +#endif +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif + int ret = 0; + + dprintk("opening device and trying to acquire exclusive lock\n"); + + if (!dev) { + cx231xx_errdev("BUG: cx231xx can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + + /* Sets volume, mute, etc */ + dev->mute = 0; + + /* set alternate setting for audio interface */ + /* 1 - 48000 samples per sec */ + ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1); + if (ret < 0) { + cx231xx_errdev("failed to set alternate setting !\n"); + + return ret; + } + + /* inform hardware to start streaming */ + ret = cx231xx_capture_start(dev, 1, Audio); + + runtime->hw = snd_cx231xx_hw_capture; + + mutex_lock(&dev->lock); + dev->adev.users++; + mutex_unlock(&dev->lock); + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + dev->adev.capture_pcm_substream = substream; + runtime->private_data = dev; + + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_pcm_close(snd_pcm_substream_t *substream) +#else +static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) +#endif +{ + int ret; + struct cx231xx *dev = snd_pcm_substream_chip(substream); + + dprintk("closing device\n"); + + /* set alternate setting for audio interface */ + /* 1 - 48000 samples per sec */ + ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); + if (ret < 0) { + cx231xx_errdev("failed to set alternate setting !\n"); + + return ret; + } + + /* inform hardware to start streaming */ + ret = cx231xx_capture_start(dev, 0, Audio); + + dev->mute = 1; + mutex_lock(&dev->lock); + dev->adev.users--; + mutex_unlock(&dev->lock); + + if (dev->adev.users == 0 && dev->adev.shutdown == 1) { + dprintk("audio users: %d\n", dev->adev.users); + dprintk("disabling audio stream!\n"); + dev->adev.shutdown = 0; + dprintk("released lock\n"); + cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, 0); + } + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_hw_capture_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +#else +static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +#endif +{ + unsigned int channels, rate, format; + int ret; + + dprintk("Setting capture parameters\n"); + + ret = snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + format = params_format(hw_params); + rate = params_rate(hw_params); + channels = params_channels(hw_params); + + /* TODO: set up cx231xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono */ + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_hw_capture_free(snd_pcm_substream_t *substream) +#else +static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream) +#endif +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); + + dprintk("Stop capture, if needed\n"); + + if (dev->adev.capture_stream == STREAM_ON) + cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO); + + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_prepare(snd_pcm_substream_t *substream) +#else +static int snd_cx231xx_prepare(struct snd_pcm_substream *substream) +#endif +{ + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_capture_trigger(snd_pcm_substream_t *substream, int cmd) +#else +static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, + int cmd) +#endif +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); + int retval; + + dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ? + "start" : "stop"); + + spin_lock(&dev->adev.slock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, + CX231XX_START_AUDIO); + retval = 0; + break; + case SNDRV_PCM_TRIGGER_STOP: + cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO); + retval = 0; + break; + default: + retval = -EINVAL; + } + + spin_unlock(&dev->adev.slock); + return retval; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_uframes_t snd_cx231xx_capture_pointer(snd_pcm_substream_t + *substream) +#else +static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream + *substream) +#endif +{ + struct cx231xx *dev; + unsigned long flags; + snd_pcm_uframes_t hwptr_done; + + dev = snd_pcm_substream_chip(substream); + + spin_lock_irqsave(&dev->adev.slock, flags); + hwptr_done = dev->adev.hwptr_done_capture; + spin_unlock_irqrestore(&dev->adev.slock, flags); + + return hwptr_done; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t * subs, + unsigned long offset) +#else +static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, + unsigned long offset) +#endif +{ + void *pageptr = subs->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_ops_t snd_cx231xx_pcm_capture = { +#else +static struct snd_pcm_ops snd_cx231xx_pcm_capture = { +#endif + .open = snd_cx231xx_capture_open, + .close = snd_cx231xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cx231xx_hw_capture_params, + .hw_free = snd_cx231xx_hw_capture_free, + .prepare = snd_cx231xx_prepare, + .trigger = snd_cx231xx_capture_trigger, + .pointer = snd_cx231xx_capture_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + +static int cx231xx_audio_init(struct cx231xx *dev) +{ + struct cx231xx_audio *adev = &dev->adev; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_t *pcm; + snd_card_t *card; +#else + struct snd_pcm *pcm; + struct snd_card *card; +#endif + static int devnr; + int err; + struct usb_interface *uif; + int i, isoc_pipe = 0; + + if (dev->has_alsa_audio != 1) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ + return 0; + } + + cx231xx_info("cx231xx-audio.c: probing for cx231xx " + "non standard usbaudio\n"); + + card = snd_card_new(index[devnr], "Cx231xx Audio", THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + spin_lock_init(&adev->slock); + err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm); + if (err < 0) { + snd_card_free(card); + return err; + } + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_cx231xx_pcm_capture); + pcm->info_flags = 0; + pcm->private_data = dev; + strcpy(pcm->name, "Conexant cx231xx Capture"); + strcpy(card->driver, "Conexant cx231xx Audio"); + strcpy(card->shortname, "Cx231xx Audio"); + strcpy(card->longname, "Conexant cx231xx Audio"); + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + adev->sndcard = card; + adev->udev = dev->udev; + + /* compute alternate max packet sizes for Audio */ + uif = + dev->udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + audio_index + 1]; + + adev->end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + adev->num_alt = uif->num_altsetting; + cx231xx_info(": EndPoint Addr 0x%x, Alternate settings: %i\n", + adev->end_point_addr, adev->num_alt); + adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL); + + if (adev->alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + return -ENOMEM; + } + + for (i = 0; i < adev->num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. + wMaxPacketSize); + adev->alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + adev->alt_max_pkt_size[i]); + } + + return 0; +} + +static int cx231xx_audio_fini(struct cx231xx *dev) +{ + if (dev == NULL) + return 0; + + if (dev->has_alsa_audio != 1) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ + return 0; + } + + if (dev->adev.sndcard) { + snd_card_free(dev->adev.sndcard); + kfree(dev->adev.alt_max_pkt_size); + dev->adev.sndcard = NULL; + } + + return 0; +} + +static struct cx231xx_ops audio_ops = { + .id = CX231XX_AUDIO, + .name = "Cx231xx Audio Extension", + .init = cx231xx_audio_init, + .fini = cx231xx_audio_fini, +}; + +static int __init cx231xx_alsa_register(void) +{ + return cx231xx_register_extension(&audio_ops); +} + +static void __exit cx231xx_alsa_unregister(void) +{ + cx231xx_unregister_extension(&audio_ops); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>"); +MODULE_DESCRIPTION("Cx231xx Audio driver"); + +module_init(cx231xx_alsa_register); +module_exit(cx231xx_alsa_unregister); diff --git a/linux/drivers/media/video/cx231xx/cx231xx-avcore.c b/linux/drivers/media/video/cx231xx/cx231xx-avcore.c new file mode 100644 index 000000000..226299d62 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -0,0 +1,2780 @@ +/* + cx231xx_avcore.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + + This program contains the specific code to control the avdecoder chip and + other related usb control functions for cx231xx based chipset. + + 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/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bitmap.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/version.h> +#include <linux/mm.h> +#include <linux/mutex.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-chip-ident.h> + +#include "cx231xx.h" + +/****************************************************************************** + * C O L I B R I - B L O C K C O N T R O L functions * + ******************************************************************************/ +int cx231xx_colibri_init_super_block(struct cx231xx *dev, u32 ref_count) +{ + int status = 0; + u8 temp = 0; + u32 colibri_power_status = 0; + int i = 0; + + /* super block initialize */ + temp = (u8) (ref_count & 0xff); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + SUP_BLK_TUNE2, 2, temp, 1); + if (status < 0) + return status; + + status = cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + SUP_BLK_TUNE2, 2, + &colibri_power_status, 1); + if (status < 0) + return status; + + temp = (u8) ((ref_count & 0x300) >> 8); + temp |= 0x40; + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + SUP_BLK_TUNE1, 2, temp, 1); + if (status < 0) + return status; + + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + SUP_BLK_PLL2, 2, 0x0f, 1); + if (status < 0) + return status; + + /* enable pll */ + while (colibri_power_status != 0x18) { + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, 0x18, 1); + if (status < 0) { + cx231xx_info( + ": Init Super Block failed in send cmd\n"); + break; + } + + status = cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + &colibri_power_status, 1); + colibri_power_status &= 0xff; + if (status < 0) { + cx231xx_info( + ": Init Super Block failed in receive cmd\n"); + break; + } + i++; + if (i == 10) { + cx231xx_info( + ": Init Super Block force break in loop !!!!\n"); + status = -1; + break; + } + } + + if (status < 0) + return status; + + /* start tuning filter */ + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + SUP_BLK_TUNE3, 2, 0x40, 1); + if (status < 0) + return status; + + msleep(5); + + /* exit tuning */ + status = + cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, SUP_BLK_TUNE3, + 2, 0x00, 1); + + return status; +} + +int cx231xx_colibri_init_channels(struct cx231xx *dev) +{ + int status = 0; + + /* power up all 3 channels, clear pd_buffer */ + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH1, 2, 0x00, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH2, 2, 0x00, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH3, 2, 0x00, 1); + + /* Enable quantizer calibration */ + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_COM_QUANT, 2, 0x02, 1); + + /* channel initialize, force modulator (fb) reset */ + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_FB_FRCRST_CH1, 2, 0x17, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_FB_FRCRST_CH2, 2, 0x17, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_FB_FRCRST_CH3, 2, 0x17, 1); + + /* start quantilizer calibration */ + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_CAL_ATEST_CH1, 2, 0x10, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_CAL_ATEST_CH2, 2, 0x10, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_CAL_ATEST_CH3, 2, 0x10, 1); + msleep(5); + + /* exit modulator (fb) reset */ + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_FB_FRCRST_CH1, 2, 0x07, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_FB_FRCRST_CH2, 2, 0x07, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_FB_FRCRST_CH3, 2, 0x07, 1); + + /* enable the pre_clamp in each channel for single-ended input */ + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_NTF_PRECLMP_EN_CH1, 2, 0xf0, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_NTF_PRECLMP_EN_CH2, 2, 0xf0, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_NTF_PRECLMP_EN_CH3, 2, 0xf0, 1); + + /* use diode instead of resistor, so set term_en to 0, res_en to 0 */ + status = cx231xx_reg_mask_write(dev, Colibri_DEVICE_ADDRESS, 8, + ADC_QGAIN_RES_TRM_CH1, 3, 7, 0x00); + status = cx231xx_reg_mask_write(dev, Colibri_DEVICE_ADDRESS, 8, + ADC_QGAIN_RES_TRM_CH2, 3, 7, 0x00); + status = cx231xx_reg_mask_write(dev, Colibri_DEVICE_ADDRESS, 8, + ADC_QGAIN_RES_TRM_CH3, 3, 7, 0x00); + + /* dynamic element matching off */ + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_DCSERVO_DEM_CH1, 2, 0x03, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_DCSERVO_DEM_CH2, 2, 0x03, 1); + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_DCSERVO_DEM_CH3, 2, 0x03, 1); + + return status; +} + +int cx231xx_colibri_setup_AFE_for_baseband(struct cx231xx *dev) +{ + u32 c_value = 0; + int status = 0; + + status = + cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH2, 2, &c_value, 1); + c_value &= (~(0x50)); + status = + cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH2, 2, c_value, 1); + + return status; +} + +/* + The Analog Front End in Cx231xx has 3 channels. These + channels are used to share between different inputs + like tuner, s-video and composite inputs. + + channel 1 ----- pin 1 to pin4(in reg is 1-4) + channel 2 ----- pin 5 to pin8(in reg is 5-8) + channel 3 ----- pin 9 to pin 12(in reg is 9-11) +*/ +int cx231xx_colibri_set_input_mux(struct cx231xx *dev, u32 input_mux) +{ + u8 ch1_setting = (u8) input_mux; + u8 ch2_setting = (u8) (input_mux >> 8); + u8 ch3_setting = (u8) (input_mux >> 16); + int status = 0; + u32 value = 0; + + if (ch1_setting != 0) { + status = + cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_INPUT_CH1, 2, &value, 1); + value &= (!INPUT_SEL_MASK); + value |= (ch1_setting - 1) << 4; + value &= 0xff; + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_INPUT_CH1, 2, value, 1); + } + + if (ch2_setting != 0) { + status = + cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_INPUT_CH2, 2, &value, 1); + value &= (!INPUT_SEL_MASK); + value |= (ch2_setting - 1) << 4; + value &= 0xff; + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_INPUT_CH2, 2, value, 1); + } + + /* For ch3_setting, the value to put in the register is + 7 less than the input number */ + if (ch3_setting != 0) { + status = cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_INPUT_CH3, 2, &value, 1); + value &= (!INPUT_SEL_MASK); + value |= (ch3_setting - 1) << 4; + value &= 0xff; + status = cx231xx_write_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_INPUT_CH3, 2, value, 1); + } + + return status; +} + +int cx231xx_colibri_set_mode(struct cx231xx *dev, enum AFE_MODE mode) +{ + int status = 0; + + /* + * FIXME: We need to implement the AFE code for LOW IF and for HI IF. + * Currently, only baseband works. + */ + + switch (mode) { + case AFE_MODE_LOW_IF: + /* SetupAFEforLowIF(); */ + break; + case AFE_MODE_BASEBAND: + status = cx231xx_colibri_setup_AFE_for_baseband(dev); + break; + case AFE_MODE_EU_HI_IF: + /* SetupAFEforEuHiIF(); */ + break; + case AFE_MODE_US_HI_IF: + /* SetupAFEforUsHiIF(); */ + break; + case AFE_MODE_JAPAN_HI_IF: + /* SetupAFEforJapanHiIF(); */ + break; + } + + if ((mode != dev->colibri_mode) && + (dev->video_input == CX231XX_VMUX_TELEVISION)) + status = cx231xx_colibri_adjust_ref_count(dev, + CX231XX_VMUX_TELEVISION); + + dev->colibri_mode = mode; + + return status; +} + +int cx231xx_colibri_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode) +{ + u32 colibri_power_status = 0; + int status = 0; + + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_RDU_250: + if (avmode == POLARIS_AVMODE_ANALOGT_TV) { + while (colibri_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL, + 1); + status |= cx231xx_read_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + &colibri_power_status, + 1); + if (status < 0) + break; + } + + status = cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH1, 2, 0x00, + 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH2, 2, 0x00, + 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH3, 2, 0x00, + 1); + } else if (avmode == POLARIS_AVMODE_DIGITAL) { + status = cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH1, 2, 0x70, + 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH2, 2, 0x70, + 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH3, 2, 0x70, + 1); + + status |= cx231xx_read_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + &colibri_power_status, 1); + colibri_power_status |= FLD_PWRDN_PD_BANDGAP | + FLD_PWRDN_PD_BIAS | + FLD_PWRDN_PD_TUNECK; + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + colibri_power_status, 1); + } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) { + while (colibri_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL, + 1); + status |= cx231xx_read_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + &colibri_power_status, + 1); + if (status < 0) + break; + } + + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH1, 2, 0x00, + 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH2, 2, 0x00, + 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH3, 2, 0x00, + 1); + } else { + cx231xx_info("Invalid AV mode input\n"); + status = -1; + } + break; + default: + if (avmode == POLARIS_AVMODE_ANALOGT_TV) { + while (colibri_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL, + 1); + status |= cx231xx_read_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + &colibri_power_status, + 1); + if (status < 0) + break; + } + + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH1, 2, + 0x40, 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH2, 2, + 0x40, 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH3, 2, + 0x00, 1); + } else if (avmode == POLARIS_AVMODE_DIGITAL) { + status = cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH1, 2, + 0x70, 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH2, 2, + 0x70, 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH3, 2, + 0x70, 1); + + status |= cx231xx_read_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + &colibri_power_status, + 1); + colibri_power_status |= FLD_PWRDN_PD_BANDGAP | + FLD_PWRDN_PD_BIAS | + FLD_PWRDN_PD_TUNECK; + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + colibri_power_status, + 1); + } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) { + while (colibri_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL, + 1); + status |= cx231xx_read_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + SUP_BLK_PWRDN, 2, + &colibri_power_status, + 1); + if (status < 0) + break; + } + + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH1, 2, + 0x00, 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH2, 2, + 0x00, 1); + status |= cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + ADC_PWRDN_CLAMP_CH3, 2, + 0x40, 1); + } else { + cx231xx_info("Invalid AV mode input\n"); + status = -1; + } + } /* switch */ + + return status; +} + +int cx231xx_colibri_adjust_ref_count(struct cx231xx *dev, u32 video_input) +{ + u32 input_mode = 0; + u32 ntf_mode = 0; + int status = 0; + + dev->video_input = video_input; + + if (video_input == CX231XX_VMUX_TELEVISION) { + status = cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_INPUT_CH3, 2, &input_mode, 1); + status = cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_NTF_PRECLMP_EN_CH3, 2, &ntf_mode, + 1); + } else { + status = cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_INPUT_CH1, 2, &input_mode, 1); + status = cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + ADC_NTF_PRECLMP_EN_CH1, 2, &ntf_mode, + 1); + } + + input_mode = (ntf_mode & 0x3) | ((input_mode & 0x6) << 1); + + switch (input_mode) { + case SINGLE_ENDED: + dev->colibri_ref_count = 0x23C; + break; + case LOW_IF: + dev->colibri_ref_count = 0x24C; + break; + case EU_IF: + dev->colibri_ref_count = 0x258; + break; + case US_IF: + dev->colibri_ref_count = 0x260; + break; + default: + break; + } + + status = cx231xx_colibri_init_super_block(dev, dev->colibri_ref_count); + + return status; +} + +/****************************************************************************** + * V I D E O / A U D I O D E C O D E R C O N T R O L functions * + ******************************************************************************/ +int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input) +{ + int status = 0; + + switch (INPUT(input)->type) { + case CX231XX_VMUX_COMPOSITE1: + case CX231XX_VMUX_SVIDEO: + if ((dev->current_pcb_config.type == USB_BUS_POWER) && + (dev->power_mode != POLARIS_AVMODE_ENXTERNAL_AV)) { + /* External AV */ + status = cx231xx_set_power_mode(dev, + POLARIS_AVMODE_ENXTERNAL_AV); + if (status < 0) { + cx231xx_errdev("%s: set_power_mode : Failed to" + " set Power - errCode [%d]!\n", + __func__, status); + return status; + } + } + status = cx231xx_set_decoder_video_input(dev, + INPUT(input)->type, + INPUT(input)->vmux); + break; + case CX231XX_VMUX_TELEVISION: + case CX231XX_VMUX_CABLE: + if ((dev->current_pcb_config.type == USB_BUS_POWER) && + (dev->power_mode != POLARIS_AVMODE_ANALOGT_TV)) { + /* Tuner */ + status = cx231xx_set_power_mode(dev, + POLARIS_AVMODE_ANALOGT_TV); + if (status < 0) { + cx231xx_errdev("%s: set_power_mode:Failed" + " to set Power - errCode [%d]!\n", + __func__, status); + return status; + } + } + status = cx231xx_set_decoder_video_input(dev, + CX231XX_VMUX_COMPOSITE1, + INPUT(input)->vmux); + break; + default: + cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n", + __func__, INPUT(input)->type); + break; + } + + /* save the selection */ + dev->video_input = input; + + return status; +} + +int cx231xx_set_decoder_video_input(struct cx231xx *dev, + u8 pin_type, u8 input) +{ + int status = 0; + u32 value = 0; + + if (pin_type != dev->video_input) { + status = cx231xx_colibri_adjust_ref_count(dev, pin_type); + if (status < 0) { + cx231xx_errdev("%s: adjust_ref_count :Failed to set" + "Colibri input mux - errCode [%d]!\n", + __func__, status); + return status; + } + } + + /* call colibri block to set video inputs */ + status = cx231xx_colibri_set_input_mux(dev, input); + if (status < 0) { + cx231xx_errdev("%s: set_input_mux :Failed to set" + " Colibri input mux - errCode [%d]!\n", + __func__, status); + return status; + } + + switch (pin_type) { + case CX231XX_VMUX_COMPOSITE1: + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, &value, 4); + value |= (0 << 13) | (1 << 4); + value &= ~(1 << 5); + + /* set [24:23] [22:15] to 0 */ + value &= (~(0x1ff8000)); + /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */ + value |= 0x1000000; + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, value, 4); + + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, 2, &value, 4); + value |= (1 << 7); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, 2, value, 4); + + /* Set vip 1.1 output mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, + FLD_OUT_MODE, + OUT_MODE_VIP11); + + /* Tell DIF object to go to baseband mode */ + status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Read the DFE_CTRL1 register */ + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL1, 2, &value, 4); + + /* enable the VBI_GATE_EN */ + value |= FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable */ + value |= FLD_VGA_AUTO_EN; + + /* Write it back */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL1, 2, value, 4); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set CVBS input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + MODE_CTRL, FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_CVBS_0)); + break; + case CX231XX_VMUX_SVIDEO: + /* Disable the use of DIF */ + + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, &value, 4); + + /* set [24:23] [22:15] to 0 */ + value &= (~(0x1ff8000)); + /* set FUNC_MODE[24:23] = 2 + IF_MOD[22:15] = 0 DCR_BYP_CH2[4:4] = 1; */ + value |= 0x1000010; + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, value, 4); + + /* Tell DIF object to go to baseband mode */ + status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Read the DFE_CTRL1 register */ + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL1, 2, &value, 4); + + /* enable the VBI_GATE_EN */ + value |= FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable */ + value |= FLD_VGA_AUTO_EN; + + /* Write it back */ + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL1, 2, value, 4); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set YC input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + MODE_CTRL, + FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_YC_1)); + + /* Chroma to ADC2 */ + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, &value, 4); + value |= FLD_CHROMA_IN_SEL; /* set the chroma in select */ + + /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8) + This sets them to use video + rather than audio. Only one of the two will be in use. */ + value &= ~(FLD_VGA_SEL_CH2 | FLD_VGA_SEL_CH3); + + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, value, 4); + + status = cx231xx_colibri_set_mode(dev, AFE_MODE_BASEBAND); + break; + case CX231XX_VMUX_TELEVISION: + case CX231XX_VMUX_CABLE: + default: + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_RDU_250: + /* Disable the use of DIF */ + + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, + &value, 4); + value |= (0 << 13) | (1 << 4); + value &= ~(1 << 5); + + /* set [24:23] [22:15] to 0 */ + value &= (~(0x1FF8000)); + /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */ + value |= 0x1000000; + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, + value, 4); + + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, 2, + &value, 4); + value |= (1 << 7); + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, 2, + value, 4); + + /* Set vip 1.1 output mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, FLD_OUT_MODE, + OUT_MODE_VIP11); + + /* Tell DIF object to go to baseband mode */ + status = cx231xx_dif_set_standard(dev, + DIF_USE_BASEBAND); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Read the DFE_CTRL1 register */ + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL1, 2, + &value, 4); + + /* enable the VBI_GATE_EN */ + value |= FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable */ + value |= FLD_VGA_AUTO_EN; + + /* Write it back */ + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL1, 2, + value, 4); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set CVBS input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + MODE_CTRL, FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, + INPUT_MODE_CVBS_0)); + break; + default: + /* Enable the DIF for the tuner */ + + /* Reinitialize the DIF */ + status = cx231xx_dif_set_standard(dev, dev->norm); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Make sure bypass is cleared */ + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + DIF_MISC_CTRL, + 2, &value, 4); + + /* Clear the bypass bit */ + value &= ~FLD_DIF_DIF_BYPASS; + + /* Enable the use of the DIF block */ + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + DIF_MISC_CTRL, + 2, value, 4); + + /* Read the DFE_CTRL1 register */ + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL1, 2, + &value, 4); + + /* Disable the VBI_GATE_EN */ + value &= ~FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable, AGC, and + set the skip count to 2 */ + value |= FLD_VGA_AUTO_EN | FLD_AGC_AUTO_EN | 0x00200000; + + /* Write it back */ + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL1, 2, + value, 4); + + /* Wait until AGC locks up */ + msleep(1); + + /* Disable the auto-VGA enable AGC */ + value &= ~(FLD_VGA_AUTO_EN); + + /* Write it back */ + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL1, 2, + value, 4); + + /* Enable Polaris B0 AGC output */ + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + PIN_CTRL, 2, + &value, 4); + value |= (FLD_OEF_AGC_RF) | + (FLD_OEF_AGC_IFVGA) | + (FLD_OEF_AGC_IF); + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + PIN_CTRL, 2, + value, 4); + + /* Set vip 1.1 output mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, FLD_OUT_MODE, + OUT_MODE_VIP11); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set CVBS input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + MODE_CTRL, FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, + INPUT_MODE_CVBS_0)); + + /* Set some bits in AFE_CTRL so that channel 2 or 3 + * is ready to receive audio */ + /* Clear clamp for channels 2 and 3 (bit 16-17) */ + /* Clear droop comp (bit 19-20) */ + /* Set VGA_SEL (for audio control) (bit 7-8) */ + status = cx231xx_read_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, + &value, 4); + + value |= FLD_VGA_SEL_CH3 | FLD_VGA_SEL_CH2; + + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + AFE_CTRL, 2, + value, 4); + break; + + } + break; + } + + /* Set raw VBI mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, FLD_VBIHACTRAW_EN, + cx231xx_set_field(FLD_VBIHACTRAW_EN, 1)); + + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, 2, + &value, 4); + if (value & 0x02) { + value |= (1 << 19); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + OUT_CTRL1, 2, value, 4); + } + + return status; +} + +/* + * Handle any video-mode specific overrides that are different + * on a per video standards basis after touching the MODE_CTRL + * register which resets many values for autodetect + */ +int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev) +{ + int status = 0; + + cx231xx_info("do_mode_ctrl_overrides : 0x%x\n", + (unsigned int)dev->norm); + + /* Change the DFE_CTRL3 bp_percent to fix flagging */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DFE_CTRL3, 2, + 0xCD3F0280, 4); + + if (dev->norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) { + cx231xx_info("do_mode_ctrl_overrides NTSC\n"); + + /* Move the close caption lines out of active video, + adjust the active video start point */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VBLANK_CNT, 0x18); + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VACTIVE_CNT, + 0x1E6000); + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_V656BLANK_CNT, + 0x1E000000); + + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + HORIZ_TIM_CTRL, + FLD_HBLANK_CNT, + cx231xx_set_field + (FLD_HBLANK_CNT, 0x79)); + } else if (dev->norm & V4L2_STD_SECAM) { + cx231xx_info("do_mode_ctrl_overrides SECAM\n"); + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VBLANK_CNT, 0x24); + /* Adjust the active video horizontal start point */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + HORIZ_TIM_CTRL, + FLD_HBLANK_CNT, + cx231xx_set_field + (FLD_HBLANK_CNT, 0x85)); + } else { + cx231xx_info("do_mode_ctrl_overrides PAL\n"); + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VBLANK_CNT, 0x24); + /* Adjust the active video horizontal start point */ + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + HORIZ_TIM_CTRL, + FLD_HBLANK_CNT, + cx231xx_set_field + (FLD_HBLANK_CNT, 0x85)); + } + + return status; +} + +int cx231xx_set_audio_input(struct cx231xx *dev, u8 input) +{ + int status = 0; + enum AUDIO_INPUT ainput = AUDIO_INPUT_LINE; + + switch (INPUT(input)->amux) { + case CX231XX_AMUX_VIDEO: + ainput = AUDIO_INPUT_TUNER_TV; + break; + case CX231XX_AMUX_LINE_IN: + status = cx231xx_flatiron_set_audio_input(dev, input); + ainput = AUDIO_INPUT_LINE; + break; + default: + break; + } + + status = cx231xx_set_audio_decoder_input(dev, ainput); + + return status; +} + +int cx231xx_set_audio_decoder_input(struct cx231xx *dev, + enum AUDIO_INPUT audio_input) +{ + u32 dwval; + int status; + u32 gen_ctrl; + u32 value = 0; + + /* Put it in soft reset */ + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + GENERAL_CTL, 2, &gen_ctrl, 1); + gen_ctrl |= 1; + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + GENERAL_CTL, 2, gen_ctrl, 1); + + switch (audio_input) { + case AUDIO_INPUT_LINE: + /* setup AUD_IO control from Merlin paralle output */ + value = cx231xx_set_field(FLD_AUD_CHAN1_SRC, + AUD_CHAN_SRC_PARALLEL); + status = cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + AUD_IO_CTRL, 2, value, 4); + + /* setup input to Merlin, SRC2 connect to AC97 + bypass upsample-by-2, slave mode, sony mode, left justify + adr 091c, dat 01000000 */ + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + AC97_CTL, + 2, &dwval, 4); + + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + AC97_CTL, 2, + (dwval | FLD_AC97_UP2X_BYPASS), 4); + + /* select the parallel1 and SRC3 */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + BAND_OUT_SEL, 2, + cx231xx_set_field(FLD_SRC3_IN_SEL, 0x0) | + cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x0) | + cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x0), + 4); + + /* unmute all, AC97 in, independence mode + adr 08d0, data 0x00063073 */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + PATH1_CTL1, 2, 0x00063073, 4); + + /* set AVC maximum threshold, adr 08d4, dat ffff0024 */ + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + PATH1_VOL_CTL, 2, &dwval, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + PATH1_VOL_CTL, 2, + (dwval | FLD_PATH1_AVC_THRESHOLD), + 4); + + /* set SC maximum threshold, adr 08ec, dat ffffb3a3 */ + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + PATH1_SC_CTL, 2, &dwval, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + PATH1_SC_CTL, 2, + (dwval | FLD_PATH1_SC_THRESHOLD), 4); + break; + + case AUDIO_INPUT_TUNER_TV: + default: + + /* Setup SRC sources and clocks */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + BAND_OUT_SEL, 2, + cx231xx_set_field(FLD_SRC6_IN_SEL, 0x00) | + cx231xx_set_field(FLD_SRC6_CLK_SEL, 0x01) | + cx231xx_set_field(FLD_SRC5_IN_SEL, 0x00) | + cx231xx_set_field(FLD_SRC5_CLK_SEL, 0x02) | + cx231xx_set_field(FLD_SRC4_IN_SEL, 0x02) | + cx231xx_set_field(FLD_SRC4_CLK_SEL, 0x03) | + cx231xx_set_field(FLD_SRC3_IN_SEL, 0x00) | + cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x00) | + cx231xx_set_field(FLD_BASEBAND_BYPASS_CTL, 0x00) | + cx231xx_set_field(FLD_AC97_SRC_SEL, 0x03) | + cx231xx_set_field(FLD_I2S_SRC_SEL, 0x00) | + cx231xx_set_field(FLD_PARALLEL2_SRC_SEL, 0x02) | + cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x01), 4); + + /* Setup the AUD_IO control */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + AUD_IO_CTRL, 2, + cx231xx_set_field(FLD_I2S_PORT_DIR, 0x00) | + cx231xx_set_field(FLD_I2S_OUT_SRC, 0x00) | + cx231xx_set_field(FLD_AUD_CHAN3_SRC, 0x00) | + cx231xx_set_field(FLD_AUD_CHAN2_SRC, 0x00) | + cx231xx_set_field(FLD_AUD_CHAN1_SRC, 0x03), 4); + + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + PATH1_CTL1, 2, 0x1F063870, 4); + + /* setAudioStandard(_audio_standard); */ + + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + PATH1_CTL1, 2, 0x00063870, 4); + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_RDU_250: + status = cx231xx_read_modify_write_i2c_dword(dev, + HAMMERHEAD_I2C_ADDRESS, + CHIP_CTRL, + FLD_SIF_EN, + cx231xx_set_field(FLD_SIF_EN, 1)); + break; + default: + break; + } + break; + + case AUDIO_INPUT_TUNER_FM: + /* use SIF for FM radio + setupFM(); + setAudioStandard(_audio_standard); + */ + break; + + case AUDIO_INPUT_MUTE: + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + PATH1_CTL1, 2, 0x1F011012, 4); + break; + } + + /* Take it out of soft reset */ + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + GENERAL_CTL, 2, &gen_ctrl, 1); + gen_ctrl &= ~1; + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + GENERAL_CTL, 2, gen_ctrl, 1); + + return status; +} + +/* Set resolution of the video */ +int cx231xx_resolution_set(struct cx231xx *dev) +{ + int width, height; + u32 hscale, vscale; + int status = 0; + + width = dev->width; + height = dev->height; + + get_scale(dev, width, height, &hscale, &vscale); + + /* set horzontal scale */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + HSCALE_CTRL, 2, hscale, 4); + + /* set vertical scale */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + VSCALE_CTRL, 2, vscale, 4); + + return status; +} + +/****************************************************************************** + * C H I P Specific C O N T R O L functions * + ******************************************************************************/ +int cx231xx_init_ctrl_pin_status(struct cx231xx *dev) +{ + u32 value; + int status = 0; + + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, PIN_CTRL, + 2, &value, 4); + value |= (~dev->board.ctl_pin_status_mask); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, PIN_CTRL, + 2, value, 4); + + return status; +} + +int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, + u8 analog_or_digital) +{ + int status = 0; + + /* first set the direction to output */ + status = cx231xx_set_gpio_direction(dev, + dev->board. + agc_analog_digital_select_gpio, 1); + + /* 0 - demod ; 1 - Analog mode */ + status = cx231xx_set_gpio_value(dev, + dev->board.agc_analog_digital_select_gpio, + analog_or_digital); + + return status; +} + +int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex) +{ + u8 value[4] = { 0, 0, 0, 0 }; + int status = 0; + + cx231xx_info("Changing the i2c port for tuner to %d\n", I2CIndex); + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, + PWR_CTL_EN, value, 4); + if (status < 0) + return status; + + if (I2CIndex == I2C_1) { + if (value[0] & I2C_DEMOD_EN) { + value[0] &= ~I2C_DEMOD_EN; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + } + } else { + if (!(value[0] & I2C_DEMOD_EN)) { + value[0] |= I2C_DEMOD_EN; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + } + } + + return status; + +} + +/****************************************************************************** + * D I F - B L O C K C O N T R O L functions * + ******************************************************************************/ +int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode, + u32 function_mode, u32 standard) +{ + int status = 0; + + if (mode == V4L2_TUNER_RADIO) { + /* C2HH */ + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xFF); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + } else if (standard != DIF_USE_BASEBAND) { + if (standard & V4L2_STD_MN) { + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, + function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xb); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + /* 0x124, AUD_CHAN1_SRC = 0x3 */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AUD_IO_CTRL, 0, 31, 0x00000003); + } else if ((standard == V4L2_STD_PAL_I) | + (standard & V4L2_STD_SECAM)) { + /* C2HH setup */ + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, + function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xF); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + } else { + /* default PAL BG */ + /* C2HH setup */ + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, + function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xE); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + HAMMERHEAD_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + } + } + + return status; +} + +int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard) +{ + int status = 0; + u32 dif_misc_ctrl_value = 0; + u32 func_mode = 0; + + cx231xx_info("%s: setStandard to %x\n", __func__, standard); + + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_MISC_CTRL, 2, &dif_misc_ctrl_value, + 4); + if (standard != DIF_USE_BASEBAND) + dev->norm = standard; + + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_RDU_250: + func_mode = 0x03; + break; + default: + func_mode = 0x01; + } + + status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, + func_mode, standard); + + if (standard == DIF_USE_BASEBAND) { /* base band */ + /* There is a different SRC_PHASE_INC value + for baseband vs. DIF */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_SRC_PHASE_INC, 2, 0xDF7DF83, + 4); + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_MISC_CTRL, 2, + &dif_misc_ctrl_value, 4); + dif_misc_ctrl_value |= FLD_DIF_DIF_BYPASS; + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_MISC_CTRL, 2, + dif_misc_ctrl_value, 4); + } else if (standard & V4L2_STD_PAL_D) { + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x444C1380); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0x72500800); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3934EA); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a023F11; + } else if (standard & V4L2_STD_PAL_I) { + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x444C1380); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0x72500800); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x5F39A934); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a033F11; + } else if (standard & V4L2_STD_PAL_M) { + /* improved Low Frequency Phase Noise */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL, 2, 0xFF01FF0C, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL1, 2, 0xbd038c85, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL2, 2, 0x1db4640a, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL3, 2, 0x00008800, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_REF, 2, 0x444C1380, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_INT_CURRENT, 2, + 0x26001700, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_RF_CURRENT, 2, 0x00002660, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_VIDEO_AGC_CTRL, 2, 0x72500800, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_VID_AUD_OVERRIDE, 2, 0x27000100, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AV_SEP_CTRL, 2, 0x012c405d, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_COMP_FLT_CTRL, 2, 0x009f50c1, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_SRC_PHASE_INC, 2, 0x1befbf06, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_SRC_GAIN_CONTROL, 2, 0x000035e8, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_SOFT_RST_CTRL_REVB, 2, + 0x00000000, 4); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3A0A3F10; + } else if (standard & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { + /* improved Low Frequency Phase Noise */ + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL, 2, 0xFF01FF0C, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL1, 2, 0xbd038c85, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL2, 2, 0x1db4640a, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL3, 2, 0x00008800, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_REF, 2, 0x444C1380, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_INT_CURRENT, 2, + 0x26001700, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_RF_CURRENT, 2, 0x00002660, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_VIDEO_AGC_CTRL, 2, 0x72500800, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_VID_AUD_OVERRIDE, 2, 0x27000100, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AV_SEP_CTRL, 2, 0x012c405d, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_COMP_FLT_CTRL, 2, 0x009f50c1, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_SRC_PHASE_INC, 2, 0x1befbf06, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_SRC_GAIN_CONTROL, 2, 0x000035e8, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_SOFT_RST_CTRL_REVB, 2, + 0x00000000, 4); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value = 0x3A093F10; + } else if (standard & + (V4L2_STD_SECAM_B | V4L2_STD_SECAM_D | V4L2_STD_SECAM_G | + V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1)) { + + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x888C0380); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xe0262600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xc2171700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xc2262600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0xf4000000); + + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a023F11; + } else if (standard & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) { + /* Is it SECAM_L1? */ + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x888C0380); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xe0262600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xc2171700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xc2262600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0xf2560000); + + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a023F11; + + } else if (standard & V4L2_STD_NTSC_M) { + /* V4L2_STD_NTSC_M (75 IRE Setup) Or + V4L2_STD_NTSC_M_JP (Japan, 0 IRE Setup) */ + + /* For NTSC the centre frequency of video coming out of + sidewinder is around 7.1MHz or 3.6MHz depending on the + spectral inversion. so for a non spectrally inverted channel + the pll freq word is 0x03420c49 + */ + + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL, 2, 0x6503BC0C, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL1, 2, 0xBD038C85, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL2, 2, 0x1DB4640A, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_PLL_CTRL3, 2, 0x00008800, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_REF, 2, 0x444C0380, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_INT_CURRENT, 2, + 0x26001700, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_RF_CURRENT, 2, 0x00002660, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_VIDEO_AGC_CTRL, 2, 0x04000800, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_VID_AUD_OVERRIDE, 2, 0x27000100, + 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AV_SEP_CTRL, 2, 0x01296e1f, 4); + + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_COMP_FLT_CTRL, 2, 0x009f50c1, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_SRC_PHASE_INC, 2, 0x1befbf06, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_SRC_GAIN_CONTROL, 2, 0x000035e8, + 4); + + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_CTRL_IF, 2, 0xC2262600, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_CTRL_INT, 2, 0xC2262600, 4); + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_CTRL_RF, 2, 0xC2262600, 4); + + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a003F10; + } else { + /* default PAL BG */ + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x444C1380); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0x72500800); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3530EC); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00A653A8); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, HAMMERHEAD_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a013F11; + } + + /* The AGC values should be the same for all standards, + AUD_SRC_SEL[19] should always be disabled */ + dif_misc_ctrl_value &= ~FLD_DIF_AUD_SRC_SEL; + + /* It is still possible to get Set Standard calls even when we + are in FM mode. + This is done to override the value for FM. */ + if (dev->active_mode == V4L2_TUNER_RADIO) + dif_misc_ctrl_value = 0x7a080000; + + /* Write the calculated value for misc ontrol register */ + status = + cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, DIF_MISC_CTRL, + 2, dif_misc_ctrl_value, 4); + + return status; +} + +int cx231xx_tuner_pre_channel_change(struct cx231xx *dev) +{ + int status = 0; + u32 dwval; + + /* Set the RF and IF k_agc values to 3 */ + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_REF, 2, &dwval, 4); + dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF); + dwval |= 0x33000000; + + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_REF, 2, dwval, 4); + + return status; +} + +int cx231xx_tuner_post_channel_change(struct cx231xx *dev) +{ + int status = 0; + u32 dwval; + + /* Set the RF and IF k_agc values to 4 for PAL/NTSC and 8 for + * SECAM L/B/D standards */ + status = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_REF, 2, &dwval, 4); + dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF); + + if (dev->norm & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_B | + V4L2_STD_SECAM_D)) + dwval |= 0x88000000; + else + dwval |= 0x44000000; + + status = cx231xx_write_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + DIF_AGC_IF_REF, 2, dwval, 4); + + return status; +} + +/****************************************************************************** + * F L A T I R O N - B L O C K C O N T R O L functions * + ******************************************************************************/ +int cx231xx_flatiron_initialize(struct cx231xx *dev) +{ + int status = 0; + u32 value; + + status = cx231xx_read_i2c_data(dev, Flatrion_DEVICE_ADDRESS, + CH_PWR_CTRL1, 1, &value, 1); + /* enables clock to delta-sigma and decimation filter */ + value |= 0x80; + status = cx231xx_write_i2c_data(dev, Flatrion_DEVICE_ADDRESS, + CH_PWR_CTRL1, 1, value, 1); + /* power up all channel */ + status = cx231xx_write_i2c_data(dev, Flatrion_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, 0x00, 1); + + return status; +} + +int cx231xx_flatiron_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode) +{ + int status = 0; + u32 value = 0; + + if (avmode != POLARIS_AVMODE_ENXTERNAL_AV) { + status = cx231xx_read_i2c_data(dev, Flatrion_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, &value, 1); + value |= 0xfe; + status = cx231xx_write_i2c_data(dev, Flatrion_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, value, 1); + } else { + status = cx231xx_write_i2c_data(dev, Flatrion_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, 0x00, 1); + } + + return status; +} + +/* set flatiron for audio input types */ +int cx231xx_flatiron_set_audio_input(struct cx231xx *dev, u8 audio_input) +{ + int status = 0; + + switch (audio_input) { + case CX231XX_AMUX_LINE_IN: + status = cx231xx_write_i2c_data(dev, Flatrion_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, 0x00, 1); + status = cx231xx_write_i2c_data(dev, Flatrion_DEVICE_ADDRESS, + CH_PWR_CTRL1, 1, 0x80, 1); + break; + case CX231XX_AMUX_VIDEO: + default: + break; + } + + dev->ctl_ainput = audio_input; + + return status; +} + +/****************************************************************************** + * P O W E R C O N T R O L functions * + ******************************************************************************/ +int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode) +{ + u8 value[4] = { 0, 0, 0, 0 }; + u32 tmp = 0; + int status = 0; + + if (dev->power_mode != mode) + dev->power_mode = mode; + else { + cx231xx_info(" setPowerMode::mode = %d, No Change req.\n", + mode); + return 0; + } + + cx231xx_info(" setPowerMode::mode = %d\n", mode); + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, + 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + + switch (mode) { + case POLARIS_AVMODE_ENXTERNAL_AV: + + tmp &= (~PWR_MODE_MASK); + + tmp |= PWR_AV_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + + tmp |= PWR_ISO_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, + value, 4); + msleep(PWR_SLEEP_INTERVAL); + + tmp |= POLARIS_AVMODE_ENXTERNAL_AV; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + + /* reset state of xceive tuner */ + dev->xc_fw_load_done = 0; + break; + + case POLARIS_AVMODE_ANALOGT_TV: + + tmp &= (~PWR_DEMOD_EN); + tmp |= (I2C_DEMOD_EN); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + + if (!(tmp & PWR_TUNER_EN)) { + tmp |= (PWR_TUNER_EN); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if (!(tmp & PWR_AV_EN)) { + tmp |= PWR_AV_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + if (!(tmp & PWR_ISO_EN)) { + tmp |= PWR_ISO_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if (!(tmp & POLARIS_AVMODE_ANALOGT_TV)) { + tmp |= POLARIS_AVMODE_ANALOGT_TV; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) || + (dev->model == CX231XX_BOARD_CNXT_RDU_250)) { + /* tuner path to channel 1 from port 3 */ + cx231xx_enable_i2c_for_tuner(dev, I2C_3); + + if (dev->cx231xx_reset_analog_tuner) + dev->cx231xx_reset_analog_tuner(dev); + } + break; + + case POLARIS_AVMODE_DIGITAL: + if (!(tmp & PWR_TUNER_EN)) { + tmp |= (PWR_TUNER_EN); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + if (!(tmp & PWR_AV_EN)) { + tmp |= PWR_AV_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + if (!(tmp & PWR_ISO_EN)) { + tmp |= PWR_ISO_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + tmp |= POLARIS_AVMODE_DIGITAL | I2C_DEMOD_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + + if (!(tmp & PWR_DEMOD_EN)) { + tmp |= PWR_DEMOD_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) || + (dev->model == CX231XX_BOARD_CNXT_RDU_250)) { + /* tuner path to channel 1 from port 3 */ + cx231xx_enable_i2c_for_tuner(dev, I2C_3); + + if (dev->cx231xx_reset_analog_tuner) + dev->cx231xx_reset_analog_tuner(dev); + } + break; + + default: + break; + } + + msleep(PWR_SLEEP_INTERVAL); + + /* For power saving, only enable Pwr_resetout_n + when digital TV is selected. */ + if (mode == POLARIS_AVMODE_DIGITAL) { + tmp |= PWR_RESETOUT_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + /* update power control for colibri */ + status = cx231xx_colibri_update_power_control(dev, mode); + + /* update power control for flatiron */ + status = cx231xx_flatiron_update_power_control(dev, mode); + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, + 4); + cx231xx_info(" The data of PWR_CTL_EN register 0x74" + "=0x%0x,0x%0x,0x%0x,0x%0x\n", + value[0], value[1], value[2], value[3]); + + return status; +} + +int cx231xx_power_suspend(struct cx231xx *dev) +{ + u8 value[4] = { 0, 0, 0, 0 }; + u32 tmp = 0; + int status = 0; + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, + value, 4); + if (status > 0) + return status; + + tmp = *((u32 *) value); + tmp &= (~PWR_MODE_MASK); + + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, + value, 4); + + return status; +} + +/****************************************************************************** + * S T R E A M C O N T R O L functions * + ******************************************************************************/ +int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask) +{ + u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; + u32 tmp = 0; + int status = 0; + + cx231xx_info("cx231xx_start_stream():: ep_mask = %x\n", ep_mask); + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, + value, 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + tmp |= ep_mask; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET, + value, 4); + + return status; +} + +int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask) +{ + u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; + u32 tmp = 0; + int status = 0; + + cx231xx_info("cx231xx_stop_stream():: ep_mask = %x\n", ep_mask); + status = + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + tmp &= (~ep_mask); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET, + value, 4); + + return status; +} + +int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) +{ + int status = 0; + + if (dev->udev->speed == USB_SPEED_HIGH) { + switch (media_type) { + case 81: /* audio */ + cx231xx_info("%s: Audio enter HANC\n", __func__); + status = + cx231xx_mode_register(dev, TS_MODE_REG, 0x9300); + break; + + case 2: /* vbi */ + cx231xx_info("%s: set vanc registers\n", __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300); + break; + + case 3: /* sliced cc */ + cx231xx_info("%s: set hanc registers\n", __func__); + status = + cx231xx_mode_register(dev, TS_MODE_REG, 0x1300); + break; + + case 0: /* video */ + cx231xx_info("%s: set video registers\n", __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); + break; + + case 4: /* ts1 */ + cx231xx_info("%s: set ts1 registers\n", __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101); + status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400); + break; + case 6: /* ts1 parallel mode */ + cx231xx_info("%s: set ts1 parrallel mode registers\n", + __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); + status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400); + break; + } + } else { + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101); + } + + return status; +} + +int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type) +{ + int rc; + u32 ep_mask = -1; + struct pcb_config *pcb_config; + + /* get EP for media type */ + pcb_config = (struct pcb_config *)&dev->current_pcb_config; + + if (pcb_config->config_num == 1) { + switch (media_type) { + case 0: /* Video */ + ep_mask = ENABLE_EP4; /* ep4 [00:1000] */ + break; + case 1: /* Audio */ + ep_mask = ENABLE_EP3; /* ep3 [00:0100] */ + break; + case 2: /* Vbi */ + ep_mask = ENABLE_EP5; /* ep5 [01:0000] */ + break; + case 3: /* Sliced_cc */ + ep_mask = ENABLE_EP6; /* ep6 [10:0000] */ + break; + case 4: /* ts1 */ + case 6: /* ts1 parallel mode */ + ep_mask = ENABLE_EP1; /* ep1 [00:0001] */ + break; + case 5: /* ts2 */ + ep_mask = ENABLE_EP2; /* ep2 [00:0010] */ + break; + } + + } else if (pcb_config->config_num > 1) { + switch (media_type) { + case 0: /* Video */ + ep_mask = ENABLE_EP4; /* ep4 [00:1000] */ + break; + case 1: /* Audio */ + ep_mask = ENABLE_EP3; /* ep3 [00:0100] */ + break; + case 2: /* Vbi */ + ep_mask = ENABLE_EP5; /* ep5 [01:0000] */ + break; + case 3: /* Sliced_cc */ + ep_mask = ENABLE_EP6; /* ep6 [10:0000] */ + break; + case 4: /* ts1 */ + case 6: /* ts1 parallel mode */ + ep_mask = ENABLE_EP1; /* ep1 [00:0001] */ + break; + case 5: /* ts2 */ + ep_mask = ENABLE_EP2; /* ep2 [00:0010] */ + break; + } + + } + + if (start) { + rc = cx231xx_initialize_stream_xfer(dev, media_type); + + if (rc < 0) + return rc; + + /* enable video capture */ + if (ep_mask > 0) + rc = cx231xx_start_stream(dev, ep_mask); + } else { + /* disable video capture */ + if (ep_mask > 0) + rc = cx231xx_stop_stream(dev, ep_mask); + } + + if (dev->mode == CX231XX_ANALOG_MODE) + ;/* do any in Analog mode */ + else + ;/* do any in digital mode */ + + return rc; +} +EXPORT_SYMBOL_GPL(cx231xx_capture_start); + +/***************************************************************************** +* G P I O B I T control functions * +******************************************************************************/ +int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val) +{ + int status = 0; + + status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 0); + + return status; +} + +int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val) +{ + int status = 0; + + status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 1); + + return status; +} + +/* +* cx231xx_set_gpio_direction +* Sets the direction of the GPIO pin to input or output +* +* Parameters : +* pin_number : The GPIO Pin number to program the direction for +* from 0 to 31 +* pin_value : The Direction of the GPIO Pin under reference. +* 0 = Input direction +* 1 = Output direction +*/ +int cx231xx_set_gpio_direction(struct cx231xx *dev, + int pin_number, int pin_value) +{ + int status = 0; + u32 value = 0; + + /* Check for valid pin_number - if 32 , bail out */ + if (pin_number >= 32) + return -EINVAL; + + /* input */ + if (pin_value == 0) + value = dev->gpio_dir & (~(1 << pin_number)); /* clear */ + else + value = dev->gpio_dir | (1 << pin_number); + + status = cx231xx_set_gpio_bit(dev, value, (u8 *) &dev->gpio_val); + + /* cache the value for future */ + dev->gpio_dir = value; + + return status; +} + +/* +* cx231xx_set_gpio_value +* Sets the value of the GPIO pin to Logic high or low. The Pin under +* reference should ALREADY BE SET IN OUTPUT MODE !!!!!!!!! +* +* Parameters : +* pin_number : The GPIO Pin number to program the direction for +* pin_value : The value of the GPIO Pin under reference. +* 0 = set it to 0 +* 1 = set it to 1 +*/ +int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value) +{ + int status = 0; + u32 value = 0; + + /* Check for valid pin_number - if 0xFF , bail out */ + if (pin_number >= 32) + return -EINVAL; + + /* first do a sanity check - if the Pin is not output, make it output */ + if ((dev->gpio_dir & (1 << pin_number)) == 0x00) { + /* It was in input mode */ + value = dev->gpio_dir | (1 << pin_number); + dev->gpio_dir = value; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *) &dev->gpio_val); + value = 0; + } + + if (pin_value == 0) + value = dev->gpio_val & (~(1 << pin_number)); + else + value = dev->gpio_val | (1 << pin_number); + + /* store the value */ + dev->gpio_val = value; + + /* toggle bit0 of GP_IO */ + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +/***************************************************************************** +* G P I O I2C related functions * +******************************************************************************/ +int cx231xx_gpio_i2c_start(struct cx231xx *dev) +{ + int status = 0; + + /* set SCL to output 1 ; set SDA to output 1 */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to output 1; set SDA to output 0 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + return status; +} + +int cx231xx_gpio_i2c_end(struct cx231xx *dev) +{ + int status = 0; + + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to output 1; set SDA to output 0 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to input ,release SCL cable control + set SDA to input ,release SDA cable control */ + dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + + status = + cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + return status; +} + +int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data) +{ + int status = 0; + u8 i; + + /* set SCL to output ; set SDA to output */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + + for (i = 0; i < 8; i++) { + if (((data << i) & 0x80) == 0) { + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 1; set SDA to output 0 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + } else { + /* set SCL to output 0; set SDA to output 1 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 1; set SDA to output 1 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 0; set SDA to output 1 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + } + } + return status; +} + +int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 * buf) +{ + u8 value = 0; + int status = 0; + u32 gpio_logic_value = 0; + u8 i; + + /* read byte */ + for (i = 0; i < 8; i++) { /* send write I2c addr */ + + /* set SCL to output 0; set SDA to input */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 1; set SDA to input */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* get SDA data bit */ + gpio_logic_value = dev->gpio_val; + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + if ((dev->gpio_val & (1 << dev->board.tuner_sda_gpio)) != 0) + value |= (1 << (8 - i - 1)); + + dev->gpio_val = gpio_logic_value; + } + + /* set SCL to output 0,finish the read latest SCL signal. + !!!set SDA to input, never to modify SDA direction at + the same times */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* store the value */ + *buf = value & 0xff; + + return status; +} + +int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev) +{ + int status = 0; + u32 gpio_logic_value = 0; + int nCnt = 10; + int nInit = nCnt; + + /* clock stretch; set SCL to input; set SDA to input; + get SCL value till SCL = 1 */ + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio); + + gpio_logic_value = dev->gpio_val; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + do { + msleep(2); + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + nCnt--; + } while (((dev->gpio_val & + (1 << dev->board.tuner_scl_gpio)) == 0) && + (nCnt > 0)); + + if (nCnt == 0) + cx231xx_info("No ACK after %d msec -GPIO I2C failed!", + nInit * 10); + + /* readAck + throuth clock stretch ,slave has given a SCL signal, + so the SDA data can be directly read. */ + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) { + dev->gpio_val = gpio_logic_value; + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + status = 0; + } else { + dev->gpio_val = gpio_logic_value; + dev->gpio_val |= (1 << dev->board.tuner_sda_gpio); + } + + /* read SDA end, set the SCL to output 0, after this operation, + SDA direction can be changed. */ + dev->gpio_val = gpio_logic_value; + dev->gpio_dir |= (1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev) +{ + int status = 0; + + /* set SDA to ouput */ + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SCL = 0 (output); set SDA = 0 (output) */ + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SCL = 1 (output); set SDA = 0 (output) */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SCL = 0 (output); set SDA = 0 (output) */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SDA to input,and then the slave will read data from SDA. */ + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev) +{ + int status = 0; + + /* set scl to output ; set sda to input */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set scl to output 0; set sda to input */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set scl to output 1; set sda to input */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +/***************************************************************************** +* G P I O I2C related functions * +******************************************************************************/ +/* cx231xx_gpio_i2c_read + * Function to read data from gpio based I2C interface + */ +int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len) +{ + int status = 0; + int i = 0; + + /* get the lock */ + mutex_lock(&dev->gpio_i2c_lock); + + /* start */ + status = cx231xx_gpio_i2c_start(dev); + + /* write dev_addr */ + status = cx231xx_gpio_i2c_write_byte(dev, (dev_addr << 1) + 1); + + /* readAck */ + status = cx231xx_gpio_i2c_read_ack(dev); + + /* read data */ + for (i = 0; i < len; i++) { + /* read data */ + buf[i] = 0; + status = cx231xx_gpio_i2c_read_byte(dev, &buf[i]); + + if ((i + 1) != len) { + /* only do write ack if we more length */ + status = cx231xx_gpio_i2c_write_ack(dev); + } + } + + /* write NAK - inform reads are complete */ + status = cx231xx_gpio_i2c_write_nak(dev); + + /* write end */ + status = cx231xx_gpio_i2c_end(dev); + + /* release the lock */ + mutex_unlock(&dev->gpio_i2c_lock); + + return status; +} + +/* cx231xx_gpio_i2c_write + * Function to write data to gpio based I2C interface + */ +int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len) +{ + int status = 0; + int i = 0; + + /* get the lock */ + mutex_lock(&dev->gpio_i2c_lock); + + /* start */ + status = cx231xx_gpio_i2c_start(dev); + + /* write dev_addr */ + status = cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); + + /* read Ack */ + status = cx231xx_gpio_i2c_read_ack(dev); + + for (i = 0; i < len; i++) { + /* Write data */ + status = cx231xx_gpio_i2c_write_byte(dev, buf[i]); + + /* read Ack */ + status = cx231xx_gpio_i2c_read_ack(dev); + } + + /* write End */ + status = cx231xx_gpio_i2c_end(dev); + + /* release the lock */ + mutex_unlock(&dev->gpio_i2c_lock); + + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-cards.c b/linux/drivers/media/video/cx231xx/cx231xx-cards.c new file mode 100644 index 000000000..bc8e375ca --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-cards.c @@ -0,0 +1,955 @@ +/* + cx231xx-cards.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx 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. + + 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/init.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/usb.h> +#include "compat.h" +#include <media/tuner.h> +#include <media/tveeprom.h> +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> + +#include <media/cx25840.h> +#include "xc5000.h" + +#include "cx231xx.h" + +static int tuner = -1; +module_param(tuner, int, 0444); +MODULE_PARM_DESC(tuner, "tuner type"); + +static unsigned int disable_ir; +module_param(disable_ir, int, 0444); +MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); + +/* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */ +static unsigned long cx231xx_devused; + +/* + * Reset sequences for analog/digital modes + */ + +static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = { + {0x03, 0x01, 10}, + {0x03, 0x00, 30}, + {0x03, 0x01, 10}, + {-1, -1, -1}, +}; + +/* + * Board definitions + */ +struct cx231xx_board cx231xx_boards[] = { + [CX231XX_BOARD_UNKNOWN] = { + .name = "Unknown CX231xx video grabber", + .tuner_type = TUNER_ABSENT, + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = 0, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + } + }, + }, + [CX231XX_BOARD_CNXT_RDE_250] = { + .name = "Conexant Hybrid TV - RDE250", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x02, + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = 0, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + } + }, + }, + + [CX231XX_BOARD_CNXT_RDU_250] = { + .name = "Conexant Hybrid TV - RDU250", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x32, + .norm = V4L2_STD_NTSC, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = 0, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + } + }, + }, +}; +const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); + +/* table of devices that work with this driver */ +struct usb_device_id cx231xx_id_table[] = { + {USB_DEVICE(0x0572, 0x58A0), + .driver_info = CX231XX_BOARD_UNKNOWN}, + {USB_DEVICE(0x0572, 0x58A2), + .driver_info = CX231XX_BOARD_CNXT_RDE_250}, + {USB_DEVICE(0x0572, 0x5A3C), + .driver_info = CX231XX_BOARD_CNXT_RDU_250}, + {}, +}; + +MODULE_DEVICE_TABLE(usb, cx231xx_id_table); + +/* cx231xx_tuner_callback + * will be used to reset XC5000 tuner using GPIO pin + */ + +int cx231xx_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct cx231xx *dev = ptr; + + if (dev->tuner_type == TUNER_XC5000) { + if (command == XC5000_TUNER_RESET) { + cx231xx_info + ("Tuner CB: RESET: cmd %d : tuner type %d \n", + command, dev->tuner_type); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, + 1); + msleep(10); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, + 0); + msleep(330); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, + 1); + msleep(10); + } + } + return rc; +} +EXPORT_SYMBOL_GPL(cx231xx_tuner_callback); + +static inline void cx231xx_set_model(struct cx231xx *dev) +{ + memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board)); +} + +/* Since cx231xx_pre_card_setup() requires a proper dev->model, + * this won't work for boards with generic PCI IDs + */ +void cx231xx_pre_card_setup(struct cx231xx *dev) +{ + + cx231xx_set_model(dev); + + cx231xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); + + cx231xx_info("Precard: Board is %s\n", dev->board.name); + /* set the direction for GPIO pins */ + cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1); + cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1); + + /* request some modules if any required */ + + /* reset the Tuner */ + cx231xx_gpio_set(dev, dev->board.tuner_gpio); + + /* set the mode to Analog mode initially */ + cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); + + /* Unlock device */ + /* cx231xx_set_mode(dev, CX231XX_SUSPEND); */ + +} + +#if 0 /* Keep */ + +static void cx231xx_config_tuner(struct cx231xx *dev) +{ + struct tuner_setup tun_setup; + struct v4l2_frequency f; + + if (dev->tuner_type == TUNER_ABSENT) + return; + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + tun_setup.tuner_callback = cx231xx_tuner_callback; + + cx231xx_i2c_call_clients(&dev->i2c_bus[1], TUNER_SET_TYPE_ADDR, + &tun_setup); +#if 0 /* Keep */ + if (tun_setup.type == TUNER_XC5000) { + static struct xc2028_ctrl ctrl = { + .fname = XC5000_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = 0; + }; + struct v4l2_priv_tun_config cfg = { + .tuner = dev->tuner_type, + .priv = &ctrl, + }; + cx231xx_i2c_call_clients(&dev->i2c_bus[1], TUNER_SET_CONFIG, + &cfg); + } +#endif + + /* configure tuner */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 9076; /* just a magic number */ + dev->ctl_freq = f.frequency; + cx231xx_i2c_call_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, &f); +} + +#endif + +/* ----------------------------------------------------------------------- */ +void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir) +{ + if (disable_ir) { + ir->get_key = NULL; + return; + } + + /* detect & configure */ + switch (dev->model) { + + case CX231XX_BOARD_CNXT_RDE_250: + break; + case CX231XX_BOARD_CNXT_RDU_250: + break; + default: + break; + } +} + +void cx231xx_card_setup(struct cx231xx *dev) +{ + cx231xx_set_model(dev); + + dev->tuner_type = cx231xx_boards[dev->model].tuner_type; + if (cx231xx_boards[dev->model].tuner_addr) + dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr; + + cx231xx_info(": tuner type %d, tuner address %d \n", + dev->tuner_type, dev->tuner_addr); + + /* Do card specific if any */ + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + /* do card specific GPIO settings if required */ + cx231xx_info("Board is Conexnat RDE 250\n"); + break; + case CX231XX_BOARD_CNXT_RDU_250: + /* do card specific GPIO settings if required */ + cx231xx_info("Board is Conexnat RDU 250\n"); + break; + } + + /* request some modules */ + if (dev->board.decoder == CX231XX_AVDECODER) { + cx231xx_info(": Requesting cx25840 module\n"); + request_module("cx25840"); + } +#if 0 /* Keep */ + if (dev->board.tuner_type != TUNER_ABSENT) { + cx231xx_info(": Requesting Tuner module\n"); + request_module("tuner"); + } + + cx231xx_config_tuner(dev); + + /* TBD IR will be added later */ + cx231xx_ir_init(dev); +#endif +} + +/* + * cx231xx_config() + * inits registers with sane defaults + */ +int cx231xx_config(struct cx231xx *dev) +{ + /* TBD need to add cx231xx specific code */ + dev->mute = 1; /* maybe not the right place... */ + dev->volume = 0x1f; + + return 0; +} + +/* + * cx231xx_config_i2c() + * configure i2c attached devices + */ +void cx231xx_config_i2c(struct cx231xx *dev) +{ + struct v4l2_routing route; + + route.input = INPUT(dev->video_input)->vmux; + route.output = 0; + + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_STREAMON, NULL); +} + +/* + * cx231xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void cx231xx_release_resources(struct cx231xx *dev) +{ + +#if 0 /* Keep */ /* TBD IR related */ + if (dev->ir) + cx231xx_ir_fini(dev); +#endif + + cx231xx_release_analog_resources(dev); + + cx231xx_remove_from_devlist(dev); + + cx231xx_dev_uninit(dev); + + usb_put_dev(dev->udev); + + /* Mark device as unused */ + cx231xx_devused &= ~(1 << dev->devno); +} + +/* + * cx231xx_init_dev() + * allocates and inits the device structs, registers i2c bus and v4l device + */ +static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, + int minor) +{ + struct cx231xx *dev = *devhandle; + int retval = -ENOMEM; + int errCode; + unsigned int maxh, maxw; + + dev->udev = udev; + mutex_init(&dev->lock); + mutex_init(&dev->ctrl_urb_lock); + mutex_init(&dev->gpio_i2c_lock); + + spin_lock_init(&dev->video_mode.slock); + spin_lock_init(&dev->vbi_mode.slock); + spin_lock_init(&dev->sliced_cc_mode.slock); + + init_waitqueue_head(&dev->open); + init_waitqueue_head(&dev->wait_frame); + init_waitqueue_head(&dev->wait_stream); + + dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg; + dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg; + dev->cx231xx_send_usb_command = cx231xx_send_usb_command; + dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read; + dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write; + + /* Query cx231xx to find what pcb config it is related to */ + initialize_cx231xx(dev); + + /* Cx231xx pre card setup */ + cx231xx_pre_card_setup(dev); + + errCode = cx231xx_config(dev); + if (errCode) { + cx231xx_errdev("error configuring device\n"); + return -ENOMEM; + } + + /* set default norm */ + dev->norm = dev->board.norm; + + /* register i2c bus */ + errCode = cx231xx_dev_init(dev); + if (errCode < 0) { + cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* Do board specific init */ + cx231xx_card_setup(dev); + + /* configure the device */ + cx231xx_config_i2c(dev); + + maxw = norm_maxw(dev); + maxh = norm_maxh(dev); + + /* set default image size */ + dev->width = maxw; + dev->height = maxh; + dev->interlaced = 0; + dev->hscale = 0; + dev->vscale = 0; + dev->video_input = 0; + + errCode = cx231xx_config(dev); + if (errCode < 0) { + cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->video_mode.vidq.active); + INIT_LIST_HEAD(&dev->video_mode.vidq.queued); + + /* init vbi dma queues */ + INIT_LIST_HEAD(&dev->vbi_mode.vidq.active); + INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued); + + /* Reset other chips required if they are tied up with GPIO pins */ + + cx231xx_add_into_devlist(dev); + + retval = cx231xx_register_analog_devices(dev); + if (retval < 0) { + cx231xx_release_resources(dev); + goto fail_reg_devices; + } + + cx231xx_init_extension(dev); + + return 0; + +fail_reg_devices: + mutex_unlock(&dev->lock); + return retval; +} + +#if defined(CONFIG_MODULES) && defined(MODULE) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void request_module_async(void *ptr) +{ + struct cx231xx *dev = (struct cx231xx *)ptr; +#else +static void request_module_async(struct work_struct *work) +{ + struct cx231xx *dev = container_of(work, + struct cx231xx, request_module_wk); +#endif + + if (dev->has_alsa_audio) + request_module("cx231xx-alsa"); + + if (dev->board.has_dvb) + request_module("cx231xx-dvb"); + +} + +static void request_modules(struct cx231xx *dev) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&dev->request_module_wk, request_module_async, (void *)dev); +#else + INIT_WORK(&dev->request_module_wk, request_module_async); +#endif + schedule_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + +/* + * cx231xx_usb_probe() + * checks for supported devices + */ +static int cx231xx_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_interface *uif; + struct cx231xx *dev = NULL; + int retval = -ENODEV; + int nr = 0, ifnum; + int i, isoc_pipe = 0; + char *speed; + char descr[255] = ""; + struct usb_interface *lif = NULL; + int skip_interface = 0; + struct usb_interface_assoc_descriptor *assoc_desc; + + udev = usb_get_dev(interface_to_usbdev(interface)); + ifnum = interface->altsetting[0].desc.bInterfaceNumber; + + cx231xx_info(": Interface Number %d\n", ifnum); + + /* Interface number 0 - IR interface */ + if (ifnum == 0) { + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS); + cx231xx_devused |= 1 << nr; + + if (nr >= CX231XX_MAXBOARDS) { + cx231xx_info(": Supports only %i cx231xx boards.\n", + CX231XX_MAXBOARDS); + cx231xx_devused &= ~(1 << nr); + return -ENOMEM; + } + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + cx231xx_err(DRIVER_NAME ": out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + return -ENOMEM; + } + + snprintf(dev->name, 29, "cx231xx #%d", nr); + dev->devno = nr; + dev->model = id->driver_info; + dev->video_mode.alt = -1; + dev->interface_count++; + + /* reset gpio dir and value */ + dev->gpio_dir = 0; + dev->gpio_val = 0; + dev->xc_fw_load_done = 0; + dev->has_alsa_audio = 1; + dev->power_mode = -1; + + /* 0 - vbi ; 1 -sliced cc mode */ + dev->vbi_or_sliced_cc_mode = 0; + + /* get maximum no.of IAD interfaces */ + assoc_desc = udev->actconfig->intf_assoc[0]; + dev->max_iad_interface_count = assoc_desc->bInterfaceCount; + cx231xx_info("Found IAD interface count %d\n", + dev->max_iad_interface_count); + + /* init CIR module TBD */ + + /* store the current interface */ + lif = interface; + + } else if (ifnum == 1) { + + /* Get dev structure first */ + dev = usb_get_intfdata(udev->actconfig->interface[0]); + if (dev == NULL) { + cx231xx_err(DRIVER_NAME ": out of first interface!\n"); + return -ENODEV; + } + + /* store the interface 0 back */ + lif = udev->actconfig->interface[0]; + + /* increment interface count */ + dev->interface_count++; + + /* get device number */ + nr = dev->devno; + + assoc_desc = udev->actconfig->intf_assoc[0]; + if (assoc_desc->bFirstInterface == ifnum) { + cx231xx_info + ("Found IAD interface match: AV Desc Start!! \n"); + } else { + cx231xx_err(" Not found matching interface\n"); + return -ENODEV; + } + + } else if (ifnum >= 2) { + /* Get dev structure first */ + dev = usb_get_intfdata(udev->actconfig->interface[0]); + if (dev == NULL) { + cx231xx_err(DRIVER_NAME ": out of first interface!\n"); + return -ENODEV; + } + + /* store the interface 0 back */ + lif = udev->actconfig->interface[0]; + + /* increment interface count */ + dev->interface_count++; + + /* get device number */ + nr = dev->devno; + + /* set skip interface */ + if ((dev->interface_count - 1) != dev->max_iad_interface_count) + skip_interface = 1; /* set skipping */ + else { + cx231xx_info + ("Found IAD interface no. match with AV Device no.!\n"); + } + } + + switch (udev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + if (udev->manufacturer) + strlcpy(descr, udev->manufacturer, sizeof(descr)); + + if (udev->product) { + if (*descr) + strlcat(descr, " ", sizeof(descr)); + strlcat(descr, udev->product, sizeof(descr)); + } + if (*descr) + strlcat(descr, " ", sizeof(descr)); + + cx231xx_info("New device %s@ %s Mbps " + "(%04x:%04x, interface %d, class %d)\n", + descr, + speed, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, interface->altsetting->desc.bInterfaceNumber); + + /* AV device initialization */ + if ((dev->interface_count - 1) == dev->max_iad_interface_count) { + cx231xx_info(" Calling init_dev\n"); + /* allocate device struct */ + retval = cx231xx_init_dev(&dev, udev, nr); + if (retval) { + cx231xx_devused &= ~(1 << dev->devno); + kfree(dev); + + return retval; + } + + /* compute alternate max packet sizes for video */ + uif = + udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + video_index + 1]; + + dev->video_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + dev->video_mode.num_alt = uif->num_altsetting; + cx231xx_info(": EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->video_mode.end_point_addr, + dev->video_mode.num_alt); + dev->video_mode.alt_max_pkt_size = + kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL); + + if (dev->video_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + kfree(dev); + return -ENOMEM; + } + + for (i = 0; i < dev->video_mode.num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->video_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->video_mode.alt_max_pkt_size[i]); + } + + /* compute alternate max packet sizes for vbi */ + uif = + udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + vanc_index + 1]; + + dev->vbi_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + dev->vbi_mode.num_alt = uif->num_altsetting; + cx231xx_info(": EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->vbi_mode.end_point_addr, + dev->vbi_mode.num_alt); + dev->vbi_mode.alt_max_pkt_size = + kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL); + + if (dev->vbi_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + kfree(dev); + return -ENOMEM; + } + + for (i = 0; i < dev->vbi_mode.num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->vbi_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->vbi_mode.alt_max_pkt_size[i]); + } + + /* compute alternate max packet sizes for sliced CC */ + uif = + udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + hanc_index + 1]; + + dev->sliced_cc_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + dev->sliced_cc_mode.num_alt = uif->num_altsetting; + cx231xx_info(": EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->sliced_cc_mode.end_point_addr, + dev->sliced_cc_mode.num_alt); + dev->sliced_cc_mode.alt_max_pkt_size = + kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL); + + if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + kfree(dev); + return -ENOMEM; + } + + for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->sliced_cc_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->sliced_cc_mode.alt_max_pkt_size[i]); + } + + if (dev->current_pcb_config.ts1_source != 0xff) { + + /* compute alternate max packet sizes for TS1 */ + uif = + udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0]. + interface_info. + ts1_index + 1]; + + dev->ts1_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe]. + desc.bEndpointAddress); + + dev->ts1_mode.num_alt = uif->num_altsetting; + cx231xx_info + (": EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->ts1_mode.end_point_addr, + dev->ts1_mode.num_alt); + dev->ts1_mode.alt_max_pkt_size = + kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL); + + if (dev->ts1_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + kfree(dev); + return -ENOMEM; + } + + for (i = 0; i < dev->ts1_mode.num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i]. + endpoint[isoc_pipe].desc. + wMaxPacketSize); + dev->ts1_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + + 1); + cx231xx_info + ("Alternate setting %i, max size= %i\n", i, + dev->ts1_mode.alt_max_pkt_size[i]); + } + } + + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(lif, dev); + + /* load other modules required */ + if ((dev->interface_count - 1) == dev->max_iad_interface_count) { + cx231xx_info("Calling request modules\n"); + request_modules(dev); + } + + if (skip_interface) { + cx231xx_info("Skipping the interface\n"); + return -ENODEV; + } + + return 0; +} + +/* + * cx231xx_usb_disconnect() + * called when the device gets diconencted + * video device will be unregistered on v4l2_close in case it is still open + */ +static void cx231xx_usb_disconnect(struct usb_interface *interface) +{ + struct cx231xx *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + /* wait until all current v4l2 io is finished then deallocate + resources */ + mutex_lock(&dev->lock); + + wake_up_interruptible_all(&dev->open); + + if (dev->users) { + cx231xx_warn + ("device /dev/video%d is open! Deregistration and memory " + "deallocation are deferred on close.\n", dev->vdev->num); + + dev->state |= DEV_MISCONFIGURED; + cx231xx_uninit_isoc(dev); + dev->state |= DEV_DISCONNECTED; + wake_up_interruptible(&dev->wait_frame); + wake_up_interruptible(&dev->wait_stream); + } else { + dev->state |= DEV_DISCONNECTED; + cx231xx_release_resources(dev); + } + + cx231xx_close_extension(dev); + + mutex_unlock(&dev->lock); + + if (!dev->users) { + kfree(dev->video_mode.alt_max_pkt_size); + kfree(dev->vbi_mode.alt_max_pkt_size); + kfree(dev->sliced_cc_mode.alt_max_pkt_size); + kfree(dev->ts1_mode.alt_max_pkt_size); + kfree(dev); + } +} + +static struct usb_driver cx231xx_usb_driver = { + .name = "cx231xx", + .probe = cx231xx_usb_probe, + .disconnect = cx231xx_usb_disconnect, + .id_table = cx231xx_id_table, +}; + +static int __init cx231xx_module_init(void) +{ + int result; + + printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n", + (CX231XX_VERSION_CODE >> 16) & 0xff, + (CX231XX_VERSION_CODE >> 8) & 0xff, CX231XX_VERSION_CODE & 0xff); + + /* register this driver with the USB subsystem */ + result = usb_register(&cx231xx_usb_driver); + if (result) + cx231xx_err(DRIVER_NAME + " usb_register failed. Error number %d.\n", result); + + return result; +} + +static void __exit cx231xx_module_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&cx231xx_usb_driver); +} + +module_init(cx231xx_module_init); +module_exit(cx231xx_module_exit); diff --git a/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h new file mode 100644 index 000000000..a6f398a17 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h @@ -0,0 +1,494 @@ +/* + cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB + video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot 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 _POLARIS_REG_H_ +#define _POLARIS_REG_H_ + +#define BOARD_CFG_STAT 0x0 +#define TS_MODE_REG 0x4 +#define TS1_CFG_REG 0x8 +#define TS1_LENGTH_REG 0xc +#define TS2_CFG_REG 0x10 +#define TS2_LENGTH_REG 0x14 +#define EP_MODE_SET 0x18 +#define CIR_PWR_PTN1 0x1c +#define CIR_PWR_PTN2 0x20 +#define CIR_PWR_PTN3 0x24 +#define CIR_PWR_MASK0 0x28 +#define CIR_PWR_MASK1 0x2c +#define CIR_PWR_MASK2 0x30 +#define CIR_GAIN 0x34 +#define CIR_CAR_REG 0x38 +#define CIR_OT_CFG1 0x40 +#define CIR_OT_CFG2 0x44 +#define PWR_CTL_EN 0x74 + +/* Polaris Endpoints capture mask for register EP_MODE_SET */ +#define ENABLE_EP1 0x01 /* Bit[0]=1 */ +#define ENABLE_EP2 0x02 /* Bit[1]=1 */ +#define ENABLE_EP3 0x04 /* Bit[2]=1 */ +#define ENABLE_EP4 0x08 /* Bit[3]=1 */ +#define ENABLE_EP5 0x10 /* Bit[4]=1 */ +#define ENABLE_EP6 0x20 /* Bit[5]=1 */ + +/* Bit definition for register PWR_CTL_EN */ +#define PWR_MODE_MASK 0x17f +#define PWR_AV_EN 0x08 /* bit3 */ +#define PWR_ISO_EN 0x40 /* bit6 */ +#define PWR_AV_MODE 0x30 /* bit4,5 */ +#define PWR_TUNER_EN 0x04 /* bit2 */ +#define PWR_DEMOD_EN 0x02 /* bit1 */ +#define I2C_DEMOD_EN 0x01 /* bit0 */ +#define PWR_RESETOUT_EN 0x100 /* bit8 */ + +enum AV_MODE{ + POLARIS_AVMODE_DEFAULT = 0, + POLARIS_AVMODE_DIGITAL = 0x10, + POLARIS_AVMODE_ANALOGT_TV = 0x20, + POLARIS_AVMODE_ENXTERNAL_AV = 0x30, + +}; + +/* Colibri Registers */ + +#define SINGLE_ENDED 0x0 +#define LOW_IF 0x4 +#define EU_IF 0x9 +#define US_IF 0xa + +#define SUP_BLK_TUNE1 0x00 +#define SUP_BLK_TUNE2 0x01 +#define SUP_BLK_TUNE3 0x02 +#define SUP_BLK_XTAL 0x03 +#define SUP_BLK_PLL1 0x04 +#define SUP_BLK_PLL2 0x05 +#define SUP_BLK_PLL3 0x06 +#define SUP_BLK_REF 0x07 +#define SUP_BLK_PWRDN 0x08 +#define SUP_BLK_TESTPAD 0x09 +#define ADC_COM_INT5_STAB_REF 0x0a +#define ADC_COM_QUANT 0x0b +#define ADC_COM_BIAS1 0x0c +#define ADC_COM_BIAS2 0x0d +#define ADC_COM_BIAS3 0x0e +#define TESTBUS_CTRL 0x12 + +#define FLD_PWRDN_TUNING_BIAS 0x10 +#define FLD_PWRDN_ENABLE_PLL 0x08 +#define FLD_PWRDN_PD_BANDGAP 0x04 +#define FLD_PWRDN_PD_BIAS 0x02 +#define FLD_PWRDN_PD_TUNECK 0x01 + + +#define ADC_STATUS_CH1 0x20 +#define ADC_STATUS_CH2 0x40 +#define ADC_STATUS_CH3 0x60 + +#define ADC_STATUS2_CH1 0x21 +#define ADC_STATUS2_CH2 0x41 +#define ADC_STATUS2_CH3 0x61 + +#define ADC_CAL_ATEST_CH1 0x22 +#define ADC_CAL_ATEST_CH2 0x42 +#define ADC_CAL_ATEST_CH3 0x62 + +#define ADC_PWRDN_CLAMP_CH1 0x23 +#define ADC_PWRDN_CLAMP_CH2 0x43 +#define ADC_PWRDN_CLAMP_CH3 0x63 + +#define ADC_CTRL_DAC23_CH1 0x24 +#define ADC_CTRL_DAC23_CH2 0x44 +#define ADC_CTRL_DAC23_CH3 0x64 + +#define ADC_CTRL_DAC1_CH1 0x25 +#define ADC_CTRL_DAC1_CH2 0x45 +#define ADC_CTRL_DAC1_CH3 0x65 + +#define ADC_DCSERVO_DEM_CH1 0x26 +#define ADC_DCSERVO_DEM_CH2 0x46 +#define ADC_DCSERVO_DEM_CH3 0x66 + +#define ADC_FB_FRCRST_CH1 0x27 +#define ADC_FB_FRCRST_CH2 0x47 +#define ADC_FB_FRCRST_CH3 0x67 + +#define ADC_INPUT_CH1 0x28 +#define ADC_INPUT_CH2 0x48 +#define ADC_INPUT_CH3 0x68 +#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */ + +#define ADC_NTF_PRECLMP_EN_CH1 0x29 +#define ADC_NTF_PRECLMP_EN_CH2 0x49 +#define ADC_NTF_PRECLMP_EN_CH3 0x69 + +#define ADC_QGAIN_RES_TRM_CH1 0x2a +#define ADC_QGAIN_RES_TRM_CH2 0x4a +#define ADC_QGAIN_RES_TRM_CH3 0x6a + +#define ADC_SOC_PRECLMP_TERM_CH1 0x2b +#define ADC_SOC_PRECLMP_TERM_CH2 0x4b +#define ADC_SOC_PRECLMP_TERM_CH3 0x6b + +#define TESTBUS_CTRL_CH1 0x32 +#define TESTBUS_CTRL_CH2 0x52 +#define TESTBUS_CTRL_CH3 0x72 + +/****************************************************************************** + * DIF registers * + ******************************************************************************/ +#define DIRECT_IF_REVB_BASE 0x00300 + +/*****************************************************************************/ +#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000) +/*****************************************************************************/ +#define FLD_DIF_PLL_LOCK 0x80000000 +/* Reserved [30:29] */ +#define FLD_DIF_PLL_FREE_RUN 0x10000000 +#define FLD_DIF_PLL_FREQ 0x0fffffff + +/*****************************************************************************/ +#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004) +/*****************************************************************************/ +#define FLD_DIF_KD_PD 0xff000000 +/* Reserved [23:20] */ +#define FLD_DIF_KDS_PD 0x000f0000 +#define FLD_DIF_KI_PD 0x0000ff00 +/* Reserved [7:4] */ +#define FLD_DIF_KIS_PD 0x0000000f + +/*****************************************************************************/ +#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008) +/*****************************************************************************/ +#define FLD_DIF_KD_FD 0xff000000 +/* Reserved [23:20] */ +#define FLD_DIF_KDS_FD 0x000f0000 +#define FLD_DIF_KI_FD 0x0000ff00 +#define FLD_DIF_SIG_PROP_SZ 0x000000f0 +#define FLD_DIF_KIS_FD 0x0000000f + +/*****************************************************************************/ +#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c) +/*****************************************************************************/ +#define FLD_DIF_PLL_AGC_REF 0xfff00000 +#define FLD_DIF_PLL_AGC_KI 0x000f0000 +/* Reserved [15] */ +#define FLD_DIF_FREQ_LIMIT 0x00007000 +#define FLD_DIF_K_FD 0x00000f00 +#define FLD_DIF_DOWNSMPL_FD 0x000000ff + +/*****************************************************************************/ +#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010) +/*****************************************************************************/ +/* Reserved [31:16] */ +#define FLD_DIF_PLL_AGC_EN 0x00008000 +/* Reserved [14:12] */ +#define FLD_DIF_PLL_MAN_GAIN 0x00000fff + +/*****************************************************************************/ +#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014) +/*****************************************************************************/ +#define FLD_DIF_K_AGC_RF 0xf0000000 +#define FLD_DIF_K_AGC_IF 0x0f000000 +#define FLD_DIF_K_AGC_INT 0x00f00000 +/* Reserved [19:12] */ +#define FLD_DIF_IF_REF 0x00000fff + +/*****************************************************************************/ +#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018) +/*****************************************************************************/ +#define FLD_DIF_IF_MAX 0xff000000 +#define FLD_DIF_IF_MIN 0x00ff0000 +#define FLD_DIF_IF_AGC 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c) +/*****************************************************************************/ +#define FLD_DIF_INT_MAX 0xff000000 +#define FLD_DIF_INT_MIN 0x00ff0000 +#define FLD_DIF_INT_AGC 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020) +/*****************************************************************************/ +#define FLD_DIF_RF_MAX 0xff000000 +#define FLD_DIF_RF_MIN 0x00ff0000 +#define FLD_DIF_RF_AGC 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024) +/*****************************************************************************/ +#define FLD_DIF_IF_AGC_IN 0xffff0000 +#define FLD_DIF_INT_AGC_IN 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028) +/*****************************************************************************/ +/* Reserved [31:16] */ +#define FLD_DIF_RF_AGC_IN 0x0000ffff + +/*****************************************************************************/ +#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c) +/*****************************************************************************/ +#define FLD_DIF_AFD 0xc0000000 +#define FLD_DIF_K_VID_AGC 0x30000000 +#define FLD_DIF_LINE_LENGTH 0x0fff0000 +#define FLD_DIF_AGC_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030) +/*****************************************************************************/ +#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000 +/* Reserved [30:30] */ +#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000 +/* Reserved [23:17] */ +#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000 +#define FLD_DIF_VID_MAN_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034) +/*****************************************************************************/ +#define FLD_DIF_LPF_FREQ 0xc0000000 +#define FLD_DIF_AV_PHASE_INC 0x3f000000 +#define FLD_DIF_AUDIO_FREQ 0x00ffffff + +/*****************************************************************************/ +#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038) +/*****************************************************************************/ +/* Reserved [31:24] */ +#define FLD_DIF_IIR23_R2 0x00ff0000 +#define FLD_DIF_IIR23_R1 0x0000ff00 +#define FLD_DIF_IIR1_R1 0x000000ff + +/*****************************************************************************/ +#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c) +/*****************************************************************************/ +#define FLD_DIF_DIF_BYPASS 0x80000000 +#define FLD_DIF_FM_NYQ_GAIN 0x40000000 +#define FLD_DIF_RF_AGC_ENA 0x20000000 +#define FLD_DIF_INT_AGC_ENA 0x10000000 +#define FLD_DIF_IF_AGC_ENA 0x08000000 +#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000 +#define FLD_DIF_VIDEO_AGC_ENA 0x02000000 +#define FLD_DIF_RF_AGC_INV 0x01000000 +#define FLD_DIF_INT_AGC_INV 0x00800000 +#define FLD_DIF_IF_AGC_INV 0x00400000 +#define FLD_DIF_SPEC_INV 0x00200000 +#define FLD_DIF_AUD_FULL_BW 0x00100000 +#define FLD_DIF_AUD_SRC_SEL 0x00080000 +/* Reserved [18] */ +#define FLD_DIF_IF_FREQ 0x00030000 +/* Reserved [15:14] */ +#define FLD_DIF_TIP_OFFSET 0x00003f00 +/* Reserved [7:5] */ +#define FLD_DIF_DITHER_ENA 0x00000010 +/* Reserved [3:1] */ +#define FLD_DIF_RF_IF_LOCK 0x00000001 + +/*****************************************************************************/ +#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_PHASE_INC 0x1fffffff + +/*****************************************************************************/ +#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044) +/*****************************************************************************/ +/* Reserved [31:16] */ +#define FLD_DIF_SRC_KI 0x0000ff00 +#define FLD_DIF_SRC_KD 0x000000ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048) +/*****************************************************************************/ +/* Reserved [31:19] */ +#define FLD_DIF_BPF_COEFF_0 0x00070000 +/* Reserved [15:4] */ +#define FLD_DIF_BPF_COEFF_1 0x0000000f + +/*****************************************************************************/ +#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c) +/*****************************************************************************/ +/* Reserved [31:22] */ +#define FLD_DIF_BPF_COEFF_2 0x003f0000 +/* Reserved [15:7] */ +#define FLD_DIF_BPF_COEFF_3 0x0000007f + +/*****************************************************************************/ +#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050) +/*****************************************************************************/ +/* Reserved [31:24] */ +#define FLD_DIF_BPF_COEFF_4 0x00ff0000 +/* Reserved [15:8] */ +#define FLD_DIF_BPF_COEFF_5 0x000000ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054) +/*****************************************************************************/ +/* Reserved [31:25] */ +#define FLD_DIF_BPF_COEFF_6 0x01ff0000 +/* Reserved [15:9] */ +#define FLD_DIF_BPF_COEFF_7 0x000001ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058) +/*****************************************************************************/ +/* Reserved [31:26] */ +#define FLD_DIF_BPF_COEFF_8 0x03ff0000 +/* Reserved [15:10] */ +#define FLD_DIF_BPF_COEFF_9 0x000003ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c) +/*****************************************************************************/ +/* Reserved [31:27] */ +#define FLD_DIF_BPF_COEFF_10 0x07ff0000 +/* Reserved [15:11] */ +#define FLD_DIF_BPF_COEFF_11 0x000007ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060) +/*****************************************************************************/ +/* Reserved [31:27] */ +#define FLD_DIF_BPF_COEFF_12 0x07ff0000 +/* Reserved [15:12] */ +#define FLD_DIF_BPF_COEFF_13 0x00000fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064) +/*****************************************************************************/ +/* Reserved [31:28] */ +#define FLD_DIF_BPF_COEFF_14 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_DIF_BPF_COEFF_15 0x00000fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_BPF_COEFF_16 0x1fff0000 +/* Reserved [15:13] */ +#define FLD_DIF_BPF_COEFF_17 0x00001fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_BPF_COEFF_18 0x1fff0000 +/* Reserved [15:13] */ +#define FLD_DIF_BPF_COEFF_19 0x00001fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_BPF_COEFF_20 0x1fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_21 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_22 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_23 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_24 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_25 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_26 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_27 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_28 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_29 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_30 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_31 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_32 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_33 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_34 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_35 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_36 0x3fff0000 +/* Reserved [15:0] */ + +/*****************************************************************************/ +#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094) +/*****************************************************************************/ +/* Reserved [31:20] */ +#define FLD_DIF_RPT_VARIANCE 0x000fffff + +/*****************************************************************************/ +#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098) +/*****************************************************************************/ +/* Reserved [31:8] */ +#define FLD_DIF_DIF_SOFT_RST 0x00000080 +#define FLD_DIF_DIF_REG_RST_MSK 0x00000040 +#define FLD_DIF_AGC_RST_MSK 0x00000020 +#define FLD_DIF_CMP_RST_MSK 0x00000010 +#define FLD_DIF_AVS_RST_MSK 0x00000008 +#define FLD_DIF_NYQ_RST_MSK 0x00000004 +#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002 +#define FLD_DIF_PLL_RST_MSK 0x00000001 + +/*****************************************************************************/ +#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c) +/*****************************************************************************/ +/* Reserved [31:25] */ +#define FLD_DIF_CTL_IP 0x01ffffff + +#endif diff --git a/linux/drivers/media/video/cx231xx/cx231xx-core.c b/linux/drivers/media/video/cx231xx/cx231xx-core.c new file mode 100644 index 000000000..2aec3a842 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-core.c @@ -0,0 +1,1231 @@ +/* + cx231xx-core.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx 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. + + 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/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/vmalloc.h> +#include <media/v4l2-common.h> + +#include "cx231xx.h" +#include "cx231xx-reg.h" + +/* #define ENABLE_DEBUG_ISOC_FRAMES */ + +static unsigned int core_debug; +module_param(core_debug, int, 0644); +MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); + +#define cx231xx_coredbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static unsigned int reg_debug; +module_param(reg_debug, int, 0644); +MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); + +#define cx231xx_regdbg(fmt, arg...) do {\ + if (reg_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static int alt = CX231XX_PINOUT; +module_param(alt, int, 0644); +MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); + +#define cx231xx_isocdbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +/***************************************************************** +* Device control list functions * +******************************************************************/ + +static LIST_HEAD(cx231xx_devlist); +static DEFINE_MUTEX(cx231xx_devlist_mutex); + +struct cx231xx *cx231xx_get_device(int minor, + enum v4l2_buf_type *fh_type, int *has_radio) +{ + struct cx231xx *h, *dev = NULL; + + *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + *has_radio = 0; + + mutex_lock(&cx231xx_devlist_mutex); + list_for_each_entry(h, &cx231xx_devlist, devlist) { + if (h->vdev->minor == minor) + dev = h; + if (h->vbi_dev->minor == minor) { + dev = h; + *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; + } + if (h->radio_dev && h->radio_dev->minor == minor) { + dev = h; + *has_radio = 1; + } + } + mutex_unlock(&cx231xx_devlist_mutex); + + return dev; +} + +/* + * cx231xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void cx231xx_remove_from_devlist(struct cx231xx *dev) +{ + mutex_lock(&cx231xx_devlist_mutex); + list_del(&dev->devlist); + mutex_unlock(&cx231xx_devlist_mutex); +}; + +void cx231xx_add_into_devlist(struct cx231xx *dev) +{ + mutex_lock(&cx231xx_devlist_mutex); + list_add_tail(&dev->devlist, &cx231xx_devlist); + mutex_unlock(&cx231xx_devlist_mutex); +}; + +static LIST_HEAD(cx231xx_extension_devlist); +static DEFINE_MUTEX(cx231xx_extension_devlist_lock); + +int cx231xx_register_extension(struct cx231xx_ops *ops) +{ + struct cx231xx *dev = NULL; + + mutex_lock(&cx231xx_devlist_mutex); + mutex_lock(&cx231xx_extension_devlist_lock); + list_add_tail(&ops->next, &cx231xx_extension_devlist); + list_for_each_entry(dev, &cx231xx_devlist, devlist) { + if (dev) + ops->init(dev); + } + cx231xx_info("Cx231xx: Initialized (%s) extension\n", ops->name); + mutex_unlock(&cx231xx_extension_devlist_lock); + mutex_unlock(&cx231xx_devlist_mutex); + return 0; +} +EXPORT_SYMBOL(cx231xx_register_extension); + +void cx231xx_unregister_extension(struct cx231xx_ops *ops) +{ + struct cx231xx *dev = NULL; + + mutex_lock(&cx231xx_devlist_mutex); + list_for_each_entry(dev, &cx231xx_devlist, devlist) { + if (dev) + ops->fini(dev); + } + + mutex_lock(&cx231xx_extension_devlist_lock); + cx231xx_info("Cx231xx: Removed (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&cx231xx_extension_devlist_lock); + mutex_unlock(&cx231xx_devlist_mutex); +} +EXPORT_SYMBOL(cx231xx_unregister_extension); + +void cx231xx_init_extension(struct cx231xx *dev) +{ + struct cx231xx_ops *ops = NULL; + + mutex_lock(&cx231xx_extension_devlist_lock); + if (!list_empty(&cx231xx_extension_devlist)) { + list_for_each_entry(ops, &cx231xx_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + } + mutex_unlock(&cx231xx_extension_devlist_lock); +} + +void cx231xx_close_extension(struct cx231xx *dev) +{ + struct cx231xx_ops *ops = NULL; + + mutex_lock(&cx231xx_extension_devlist_lock); + if (!list_empty(&cx231xx_extension_devlist)) { + list_for_each_entry(ops, &cx231xx_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + } + mutex_unlock(&cx231xx_extension_devlist_lock); +} + +/**************************************************************** +* U S B related functions * +*****************************************************************/ +int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus, + struct cx231xx_i2c_xfer_data *req_data) +{ + int status = 0; + struct cx231xx *dev = i2c_bus->dev; + struct VENDOR_REQUEST_IN ven_req; + + u8 saddr_len = 0; + u8 _i2c_period = 0; + u8 _i2c_nostop = 0; + u8 _i2c_reserve = 0; + + /* Get the I2C period, nostop and reserve parameters */ + _i2c_period = i2c_bus->i2c_period; + _i2c_nostop = i2c_bus->i2c_nostop; + _i2c_reserve = i2c_bus->i2c_reserve; + + saddr_len = req_data->saddr_len; + + /* Set wValue */ + if (saddr_len == 1) /* need check saddr_len == 0 */ + ven_req.wValue = + req_data-> + dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 | + _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6; + else + ven_req.wValue = + req_data-> + dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 | + _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6; + + /* set channel number */ + if (req_data->direction & I2C_M_RD) { + /* channel number, for read,spec required channel_num +4 */ + ven_req.bRequest = i2c_bus->nr + 4; + } else + ven_req.bRequest = i2c_bus->nr; /* channel number, */ + + /* set index value */ + switch (saddr_len) { + case 0: + ven_req.wIndex = 0; /* need check */ + break; + case 1: + ven_req.wIndex = (req_data->saddr_dat & 0xff); + break; + case 2: + ven_req.wIndex = req_data->saddr_dat; + break; + } + + /* set wLength value */ + ven_req.wLength = req_data->buf_size; + + /* set bData value */ + ven_req.bData = 0; + + /* set the direction */ + if (req_data->direction) { + ven_req.direction = USB_DIR_IN; + memset(req_data->p_buffer, 0x00, ven_req.wLength); + } else + ven_req.direction = USB_DIR_OUT; + + /* set the buffer for read / write */ + ven_req.pBuff = req_data->p_buffer; + +#if 0 + { + int i = 0; + + cx231xx_isocdbg(": USB Control Pipe Request: I2C Bus#%d\n", + i2c_bus->nr); + cx231xx_isocdbg("bRequest = %x : ", ven_req.bRequest); + cx231xx_isocdbg("wValue = %x : ", ven_req.wValue); + cx231xx_isocdbg("wIndex = %x : ", ven_req.wIndex); + cx231xx_isocdbg("wLength = %x\n", ven_req.wLength); + cx231xx_isocdbg("pBuff : "); + for (i = 0; i < ven_req.wLength; i++) { + cx231xx_isocdbg(" %2x", ven_req.pBuff[i]); + if ((i > 0) && (i % 10 == 0)) + cx231xx_isocdbg("\n "); + } + cx231xx_isocdbg("\n\n"); + } +#endif + + /* call common vendor command request */ + status = cx231xx_send_vendor_cmd(dev, &ven_req); + if (status < 0) { + cx231xx_info + ("UsbInterface::sendCommand, failed with status -%d\n", + status); + } + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_send_usb_command); + +/* + * cx231xx_read_ctrl_reg() + * reads data from the usb device specifying bRequest and wValue + */ +int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len) +{ + u8 val = 0; + int ret; + int pipe = usb_rcvctrlpipe(dev->udev, 0); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if (len > URB_MAX_CTRL_SIZE) + return -EINVAL; + + switch (len) { + case 1: + val = ENABLE_ONE_BYTE; + break; + case 2: + val = ENABLE_TWE_BYTE; + break; + case 3: + val = ENABLE_THREE_BYTE; + break; + case 4: + val = ENABLE_FOUR_BYTE; + break; + default: + val = 0xFF; /* invalid option */ + } + + if (val == 0xFF) + return -EINVAL; + + if (reg_debug) { + cx231xx_isocdbg("(pipe 0x%08x): " + "IN: %02x %02x %02x %02x %02x %02x %02x %02x ", + pipe, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, val, + reg & 0xff, reg >> 8, len & 0xff, len >> 8); + } + + mutex_lock(&dev->ctrl_urb_lock); + ret = usb_control_msg(dev->udev, pipe, req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, reg, dev->urb_buf, len, HZ); + if (ret < 0) { + cx231xx_isocdbg(" failed!\n"); + /* mutex_unlock(&dev->ctrl_urb_lock); */ + return ret; + } + + if (len) + memcpy(buf, dev->urb_buf, len); + + mutex_unlock(&dev->ctrl_urb_lock); + + if (reg_debug) { + int byte; + + cx231xx_isocdbg("<<<"); + for (byte = 0; byte < len; byte++) + cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]); + cx231xx_isocdbg("\n"); + } + + return ret; +} + +int cx231xx_send_vendor_cmd(struct cx231xx *dev, + struct VENDOR_REQUEST_IN *ven_req) +{ + int ret; + int pipe = 0; + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if ((ven_req->wLength > URB_MAX_CTRL_SIZE)) + return -EINVAL; + + if (ven_req->direction) + pipe = usb_rcvctrlpipe(dev->udev, 0); + else + pipe = usb_sndctrlpipe(dev->udev, 0); + + if (reg_debug) { + int byte; + + cx231xx_isocdbg("(pipe 0x%08x): " + "OUT: %02x %02x %02x %04x %04x %04x >>>", + pipe, + ven_req-> + direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + ven_req->bRequest, 0, ven_req->wValue, + ven_req->wIndex, ven_req->wLength); + + for (byte = 0; byte < ven_req->wLength; byte++) + cx231xx_isocdbg(" %02x", + (unsigned char)ven_req->pBuff[byte]); + cx231xx_isocdbg("\n"); + } + + mutex_lock(&dev->ctrl_urb_lock); + ret = usb_control_msg(dev->udev, pipe, ven_req->bRequest, + ven_req-> + direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + ven_req->wValue, ven_req->wIndex, ven_req->pBuff, + ven_req->wLength, HZ); + mutex_unlock(&dev->ctrl_urb_lock); + + return ret; +} + +/* + * cx231xx_write_ctrl_reg() + * sends data to the usb device, specifying bRequest + */ +int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, char *buf, + int len) +{ + u8 val = 0; + int ret; + int pipe = usb_sndctrlpipe(dev->udev, 0); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) + return -EINVAL; + + switch (len) { + case 1: + val = ENABLE_ONE_BYTE; + break; + case 2: + val = ENABLE_TWE_BYTE; + break; + case 3: + val = ENABLE_THREE_BYTE; + break; + case 4: + val = ENABLE_FOUR_BYTE; + break; + default: + val = 0xFF; /* invalid option */ + } + + if (val == 0xFF) + return -EINVAL; + + if (reg_debug) { + int byte; + + cx231xx_isocdbg("(pipe 0x%08x): " + "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>", + pipe, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, val, reg & 0xff, + reg >> 8, len & 0xff, len >> 8); + + for (byte = 0; byte < len; byte++) + cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]); + cx231xx_isocdbg("\n"); + } + + mutex_lock(&dev->ctrl_urb_lock); + memcpy(dev->urb_buf, buf, len); + ret = usb_control_msg(dev->udev, pipe, req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, reg, dev->urb_buf, len, HZ); + mutex_unlock(&dev->ctrl_urb_lock); + + return ret; +} + +/**************************************************************** +* USB Alternate Setting functions * +*****************************************************************/ + +int cx231xx_set_video_alternate(struct cx231xx *dev) +{ + int errCode, prev_alt = dev->video_mode.alt; + unsigned int min_pkt_size = dev->width * 2 + 4; + u32 usb_interface_index = 0; + + /* When image size is bigger than a certain value, + the frame size should be increased, otherwise, only + green screen will be received. + */ + if (dev->width * 2 * dev->height > 720 * 240 * 2) + min_pkt_size *= 2; + + if (dev->width > 360) { + /* resolutions: 720,704,640 */ + dev->video_mode.alt = 3; + } else if (dev->width > 180) { + /* resolutions: 360,352,320,240 */ + dev->video_mode.alt = 2; + } else if (dev->width > 0) { + /* resolutions: 180,176,160,128,88 */ + dev->video_mode.alt = 1; + } else { + /* Change to alt0 BULK to release USB bandwidth */ + dev->video_mode.alt = 0; + } + + /* Get the correct video interface Index */ + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + video_index + 1; + + if (dev->video_mode.alt != prev_alt) { + cx231xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->video_mode.alt); + dev->video_mode.max_pkt_size = + dev->video_mode.alt_max_pkt_size[dev->video_mode.alt]; + cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", + dev->video_mode.alt, + dev->video_mode.max_pkt_size); + cx231xx_info + (" setting alt %d with wMaxPktSize=%u , Interface = %d\n", + dev->video_mode.alt, dev->video_mode.max_pkt_size, + usb_interface_index); + errCode = + usb_set_interface(dev->udev, usb_interface_index, + dev->video_mode.alt); + if (errCode < 0) { + cx231xx_errdev + ("cannot change alt number to %d (error=%i)\n", + dev->video_mode.alt, errCode); + return errCode; + } + } + return 0; +} + +int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt) +{ + int status = 0; + u32 usb_interface_index = 0; + u32 max_pkt_size = 0; + + switch (index) { + case INDEX_TS1: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + ts1_index + 1; + dev->video_mode.alt = alt; + if (dev->ts1_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->ts1_mode.max_pkt_size = + dev->ts1_mode.alt_max_pkt_size[dev->ts1_mode.alt]; + break; + case INDEX_TS2: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + ts2_index + 1; + break; + case INDEX_AUDIO: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + audio_index + 1; + dev->adev.alt = alt; + if (dev->adev.alt_max_pkt_size != NULL) + max_pkt_size = dev->adev.max_pkt_size = + dev->adev.alt_max_pkt_size[dev->adev.alt]; + break; + case INDEX_VIDEO: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + video_index + 1; + dev->video_mode.alt = alt; + if (dev->video_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->video_mode.max_pkt_size = + dev->video_mode.alt_max_pkt_size[dev->video_mode. + alt]; + break; + case INDEX_VANC: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + vanc_index + 1; + dev->vbi_mode.alt = alt; + if (dev->vbi_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->vbi_mode.max_pkt_size = + dev->vbi_mode.alt_max_pkt_size[dev->vbi_mode.alt]; + break; + case INDEX_HANC: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + hanc_index + 1; + dev->sliced_cc_mode.alt = alt; + if (dev->sliced_cc_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->sliced_cc_mode.max_pkt_size = + dev->sliced_cc_mode.alt_max_pkt_size[dev-> + sliced_cc_mode. + alt]; + break; + default: + break; + } + + if (alt > 0 && max_pkt_size == 0) { + cx231xx_errdev + ("can't change interface %d alt no. to %d: Max. Pkt size = 0\n", + usb_interface_index, alt); + return -1; + } + + cx231xx_info + (" setting alternate %d with wMaxPacketSize=%u , Interface = %d\n", + alt, max_pkt_size, usb_interface_index); + + if (usb_interface_index > 0) { + status = usb_set_interface(dev->udev, usb_interface_index, alt); + if (status < 0) { + cx231xx_errdev + ("can't change interface %d alt no. to %d (err=%i)\n", + usb_interface_index, alt, status); + return status; + } + } + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_set_alt_setting); + +int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio) +{ + int rc = 0; + + if (!gpio) + return rc; + + /* Send GPIO reset sequences specified at board entry */ + while (gpio->sleep >= 0) { + rc = cx231xx_set_gpio_value(dev, gpio->bit, gpio->val); + if (rc < 0) + return rc; + + if (gpio->sleep > 0) + msleep(gpio->sleep); + + gpio++; + } + return rc; +} + +int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) +{ + if (dev->mode == set_mode) + return 0; + + if (set_mode == CX231XX_SUSPEND) { + /* Set the chip in power saving mode */ + dev->mode = set_mode; + } + + /* Resource is locked */ + if (dev->mode != CX231XX_SUSPEND) + return -EINVAL; + + dev->mode = set_mode; + + if (dev->mode == CX231XX_DIGITAL_MODE) + ;/* Set Digital power mode */ + else + ;/* Set Analog Power mode */ + + return 0; +} +EXPORT_SYMBOL_GPL(cx231xx_set_mode); + +/***************************************************************** +* URB Streaming functions * +******************************************************************/ + +/* + * IRQ callback, called by URB callback + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void cx231xx_irq_callback(struct urb *urb, struct pt_regs *regs) +#else +static void cx231xx_irq_callback(struct urb *urb) +#endif +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + int rc, i; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + cx231xx_isocdbg("urb completition error %d.\n", urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->video_mode.slock); + rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->video_mode.slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + cx231xx_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void cx231xx_uninit_isoc(struct cx231xx *dev) +{ + struct urb *urb; + int i; + + cx231xx_isocdbg("cx231xx: called cx231xx_uninit_isoc\n"); + + dev->video_mode.isoc_ctl.nfields = -1; + for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { + urb = dev->video_mode.isoc_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->video_mode.isoc_ctl.transfer_buffer[i]) { + usb_buffer_free(dev->udev, + urb->transfer_buffer_length, + dev->video_mode.isoc_ctl. + transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->video_mode.isoc_ctl.urb[i] = NULL; + } + dev->video_mode.isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->video_mode.isoc_ctl.urb); + kfree(dev->video_mode.isoc_ctl.transfer_buffer); + + dev->video_mode.isoc_ctl.urb = NULL; + dev->video_mode.isoc_ctl.transfer_buffer = NULL; + dev->video_mode.isoc_ctl.num_bufs = 0; + + cx231xx_capture_start(dev, 0, Raw_Video); +} +EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc); + +/* + * Allocate URBs and start IRQ + */ +int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, struct urb *urb)) +{ + struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + int rc; + + cx231xx_isocdbg("cx231xx: called cx231xx_prepare_isoc\n"); + + dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; + + cx231xx_info("Setting Video mux to %d\n", dev->video_input); + video_mux(dev, dev->video_input); + + /* De-allocates all pending stuff */ + cx231xx_uninit_isoc(dev); + + dev->video_mode.isoc_ctl.isoc_copy = isoc_copy; + dev->video_mode.isoc_ctl.num_bufs = num_bufs; + dma_q->pos = 0; + dma_q->is_partial_line = 0; + dma_q->last_sav = 0; + dma_q->current_field = -1; + dma_q->field1_done = 0; + dma_q->lines_per_field = dev->height / 2; + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; + for (i = 0; i < 8; i++) + dma_q->partial_buf[i] = 0; + + dev->video_mode.isoc_ctl.urb = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->video_mode.isoc_ctl.urb) { + cx231xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->video_mode.isoc_ctl.transfer_buffer = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->video_mode.isoc_ctl.transfer_buffer) { + cx231xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->video_mode.isoc_ctl.urb); + return -ENOMEM; + } + + dev->video_mode.isoc_ctl.max_pkt_size = max_pkt_size; + dev->video_mode.isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->video_mode.isoc_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + cx231xx_err("cannot alloc isoc_ctl.urb %i\n", i); + cx231xx_uninit_isoc(dev); + return -ENOMEM; + } + dev->video_mode.isoc_ctl.urb[i] = urb; + + dev->video_mode.isoc_ctl.transfer_buffer[i] = + usb_buffer_alloc(dev->udev, sb_size, GFP_KERNEL, + &urb->transfer_dma); + if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) { + cx231xx_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + cx231xx_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->video_mode.isoc_ctl.transfer_buffer[i], 0, sb_size); + + pipe = + usb_rcvisocpipe(dev->udev, dev->video_mode.end_point_addr); + + usb_fill_int_urb(urb, dev->udev, pipe, + dev->video_mode.isoc_ctl.transfer_buffer[i], + sb_size, cx231xx_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP; + + k = 0; + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + dev->video_mode.isoc_ctl.max_pkt_size; + k += dev->video_mode.isoc_ctl.max_pkt_size; + } + } + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->video_mode.isoc_ctl.urb[i], + GFP_ATOMIC); + if (rc) { + cx231xx_err("submit of urb %i failed (error=%i)\n", i, + rc); + cx231xx_uninit_isoc(dev); + return rc; + } + } + + cx231xx_capture_start(dev, 1, Raw_Video); + + return 0; +} +EXPORT_SYMBOL_GPL(cx231xx_init_isoc); + +/***************************************************************** +* Device Init/UnInit functions * +******************************************************************/ +int cx231xx_dev_init(struct cx231xx *dev) +{ + int errCode = 0; + + /* Initialize I2C bus */ + + /* External Master 1 Bus */ + dev->i2c_bus[0].nr = 0; + dev->i2c_bus[0].dev = dev; + dev->i2c_bus[0].i2c_period = I2C_SPEED_1M; /* 1MHz */ + dev->i2c_bus[0].i2c_nostop = 0; + dev->i2c_bus[0].i2c_reserve = 0; + + /* External Master 2 Bus */ + dev->i2c_bus[1].nr = 1; + dev->i2c_bus[1].dev = dev; + dev->i2c_bus[1].i2c_period = I2C_SPEED_1M; /* 1MHz */ + dev->i2c_bus[1].i2c_nostop = 0; + dev->i2c_bus[1].i2c_reserve = 0; + + /* Internal Master 3 Bus */ + dev->i2c_bus[2].nr = 2; + dev->i2c_bus[2].dev = dev; + dev->i2c_bus[2].i2c_period = I2C_SPEED_400K; /* 400kHz */ + dev->i2c_bus[2].i2c_nostop = 0; + dev->i2c_bus[2].i2c_reserve = 0; + + /* register I2C buses */ + cx231xx_i2c_register(&dev->i2c_bus[0]); + cx231xx_i2c_register(&dev->i2c_bus[1]); + cx231xx_i2c_register(&dev->i2c_bus[2]); + + /* init hardware */ + /* Note : with out calling set power mode function, + colibri can not be set up correctly */ + errCode = cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV); + if (errCode < 0) { + cx231xx_errdev + ("%s: Failed to set Power - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* initialize Colibri block */ + errCode = cx231xx_colibri_init_super_block(dev, 0x23c); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_colibri init super block - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + errCode = cx231xx_colibri_init_channels(dev); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_colibri init channels - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* Set DIF in By pass mode */ + errCode = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_dif set to By pass mode - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* flatiron related functions */ + errCode = cx231xx_flatiron_initialize(dev); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_flatiron initialize - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* init control pins */ + errCode = cx231xx_init_ctrl_pin_status(dev); + if (errCode < 0) { + cx231xx_errdev("%s: cx231xx_init ctrl pins - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* set AGC mode to Analog */ + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* set all alternate settings to zero initially */ + cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + cx231xx_set_alt_setting(dev, INDEX_HANC, 0); + if (dev->board.has_dvb) + cx231xx_set_alt_setting(dev, INDEX_TS1, 0); + + /* set the I2C master port to 3 on channel 1 */ + errCode = cx231xx_enable_i2c_for_tuner(dev, I2C_3); + + return errCode; +} +EXPORT_SYMBOL_GPL(cx231xx_dev_init); + +void cx231xx_dev_uninit(struct cx231xx *dev) +{ + /* Un Initialize I2C bus */ + cx231xx_i2c_unregister(&dev->i2c_bus[2]); + cx231xx_i2c_unregister(&dev->i2c_bus[1]); + cx231xx_i2c_unregister(&dev->i2c_bus[0]); +} +EXPORT_SYMBOL_GPL(cx231xx_dev_uninit); + +/***************************************************************** +* G P I O related functions * +******************************************************************/ +int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val, + u8 len, u8 request, u8 direction) +{ + int status = 0; + struct VENDOR_REQUEST_IN ven_req; + + /* Set wValue */ + ven_req.wValue = (u16) (gpio_bit >> 16 & 0xffff); + + /* set request */ + if (!request) { + if (direction) + ven_req.bRequest = VRT_GET_GPIO; /* 0x8 gpio */ + else + ven_req.bRequest = VRT_SET_GPIO; /* 0x9 gpio */ + } else { + if (direction) + ven_req.bRequest = VRT_GET_GPIE; /* 0xa gpie */ + else + ven_req.bRequest = VRT_SET_GPIE; /* 0xb gpie */ + } + + /* set index value */ + ven_req.wIndex = (u16) (gpio_bit & 0xffff); + + /* set wLength value */ + ven_req.wLength = len; + + /* set bData value */ + ven_req.bData = 0; + + /* set the buffer for read / write */ + ven_req.pBuff = gpio_val; + + /* set the direction */ + if (direction) { + ven_req.direction = USB_DIR_IN; + memset(ven_req.pBuff, 0x00, ven_req.wLength); + } else + ven_req.direction = USB_DIR_OUT; + +#if 0 + cx231xx_isocdbg("bRequest = %x\n", ven_req.bRequest); + cx231xx_isocdbg("wValue = %x\n", ven_req.wValue); + cx231xx_isocdbg("wIndex = %x\n", ven_req.wIndex); + cx231xx_isocdbg("wLength = %x\n", ven_req.wLength); + for (int i = 0; i < ven_req.wLength; i++) + cx231xx_isocdbg("pBuff[i] = %x\n", ven_req.pBuff[i]); +#endif + + /* call common vendor command request */ + status = cx231xx_send_vendor_cmd(dev, &ven_req); + if (status < 0) { + cx231xx_info + ("UsbInterface::sendCommand, failed with status -%d\n", + status); + } + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_send_gpio_cmd); + +/***************************************************************** + * C O N T R O L - Register R E A D / W R I T E functions * + *****************************************************************/ +int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode) +{ + u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; + u32 tmp = 0; + int status = 0; + + status = + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, address, value, 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + tmp |= mode; + + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + + status = + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, address, value, 4); + + return status; +} + +/***************************************************************** + * I 2 C Internal C O N T R O L functions * + *****************************************************************/ +int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 *data, u8 data_len) +{ + int status = 0; + struct cx231xx_i2c_xfer_data req_data; + u8 value[4] = { 0, 0, 0, 0 }; + + if (saddr_len == 0) + saddr = 0; + else if (saddr_len == 0) + saddr &= 0xff; + + /* prepare xfer_data struct */ + req_data.dev_addr = dev_addr >> 1; + req_data.direction = I2C_M_RD; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = saddr; + req_data.buf_size = data_len; + req_data.p_buffer = (u8 *) value; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data); + + if (status >= 0) { + /* Copy the data read back to main buffer */ + if (data_len == 1) + *data = value[0]; + else + *data = + value[0] | value[1] << 8 | value[2] << 16 | value[3] + << 24; + } + + return status; +} + +int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 data, u8 data_len) +{ + int status = 0; + u8 value[4] = { 0, 0, 0, 0 }; + struct cx231xx_i2c_xfer_data req_data; + + value[0] = (u8) data; + value[1] = (u8) (data >> 8); + value[2] = (u8) (data >> 16); + value[3] = (u8) (data >> 24); + + if (saddr_len == 0) + saddr = 0; + else if (saddr_len == 0) + saddr &= 0xff; + + /* prepare xfer_data struct */ + req_data.dev_addr = dev_addr >> 1; + req_data.direction = 0; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = saddr; + req_data.buf_size = data_len; + req_data.p_buffer = value; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data); + + return status; +} + +int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size, + u16 register_address, u8 bit_start, u8 bit_end, + u32 value) +{ + int status = 0; + u32 tmp; + u32 mask = 0; + int i; + + if (bit_start > (size - 1) || bit_end > (size - 1)) + return -1; + + if (size == 8) { + status = + cx231xx_read_i2c_data(dev, dev_addr, register_address, 2, + &tmp, 1); + } else { + status = + cx231xx_read_i2c_data(dev, dev_addr, register_address, 2, + &tmp, 4); + } + + if (status < 0) + return status; + + mask = 1 << bit_end; + for (i = bit_end; i > bit_start && i > 0; i--) + mask = mask + (1 << (i - 1)); + + value <<= bit_start; + + if (size == 8) { + tmp &= ~mask; + tmp |= value; + tmp &= 0xff; + status = + cx231xx_write_i2c_data(dev, dev_addr, register_address, 2, + tmp, 1); + } else { + tmp &= ~mask; + tmp |= value; + status = + cx231xx_write_i2c_data(dev, dev_addr, register_address, 2, + tmp, 4); + } + + return status; +} + +int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u32 mask, u32 value) +{ + u32 temp; + int status = 0; + + status = cx231xx_read_i2c_data(dev, dev_addr, saddr, 2, &temp, 4); + + if (status < 0) + return status; + + temp &= ~mask; + temp |= value; + + status = cx231xx_write_i2c_data(dev, dev_addr, saddr, 2, temp, 4); + + return status; +} + +u32 cx231xx_set_field(u32 field_mask, u32 data) +{ + u32 temp; + + for (temp = field_mask; (temp & 1) == 0; temp >>= 1) + data <<= 1; + + return data; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-dvb.c b/linux/drivers/media/video/cx231xx/cx231xx-dvb.c new file mode 100644 index 000000000..9ff097055 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-dvb.c @@ -0,0 +1,560 @@ +/* + DVB device driver for cx231xx + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx 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. + + 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/usb.h> +#include "compat.h" + +#include "cx231xx.h" +#include <media/v4l2-common.h> +#include <media/videobuf-vmalloc.h> + +#include "xc5000.h" +#include "dvb_dummy_fe.h" + +MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); +MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk(level, fmt, arg...) do { \ +if (debug >= level) \ + printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ +} while (0) + +#define CX231XX_DVB_NUM_BUFS 5 +#define CX231XX_DVB_MAX_PACKETSIZE 564 +#define CX231XX_DVB_MAX_PACKETS 64 + +struct cx231xx_dvb { + struct dvb_frontend *frontend; + + /* feed count management */ + struct mutex lock; + int nfeeds; + + /* general boilerplate stuff */ + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; +}; + +static inline void print_err_status(struct cx231xx *dev, int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(1, "URB status %d [%s].\n", status, errmsg); + } else { + dprintk(1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb) +{ + int i; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + + return 0; +} + +static int start_streaming(struct cx231xx_dvb *dvb) +{ + int rc; + struct cx231xx *dev = dvb->adapter.priv; + + usb_set_interface(dev->udev, 0, 1); + rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + if (rc < 0) + return rc; + + return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS, + CX231XX_DVB_NUM_BUFS, + CX231XX_DVB_MAX_PACKETSIZE, dvb_isoc_copy); +} + +static int stop_streaming(struct cx231xx_dvb *dvb) +{ + struct cx231xx *dev = dvb->adapter.priv; + + cx231xx_uninit_isoc(dev); + + cx231xx_set_mode(dev, CX231XX_SUSPEND); + + return 0; +} + +static int start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct cx231xx_dvb *dvb = demux->priv; + int rc, ret; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->lock); + dvb->nfeeds++; + rc = dvb->nfeeds; + + if (dvb->nfeeds == 1) { + ret = start_streaming(dvb); + if (ret < 0) + rc = ret; + } + + mutex_unlock(&dvb->lock); + return rc; +} + +static int stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct cx231xx_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->lock); + dvb->nfeeds--; + + if (0 == dvb->nfeeds) + err = stop_streaming(dvb); + + mutex_unlock(&dvb->lock); + return err; +} + +/* ------------------------------------------------------------------ */ +static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct cx231xx *dev = fe->dvb->priv; + + if (acquire) + return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + else + return cx231xx_set_mode(dev, CX231XX_SUSPEND); +} + +/* ------------------------------------------------------------------ */ + +static struct xc5000_config cnxt_rde250_tunerconfig = { + .i2c_address = 0x61, + .if_khz = 5380, +}; + +/* ------------------------------------------------------------------ */ +#if 0 /* Keep */ +static int attach_xc5000(u8 addr, struct cx231xx *dev) +{ + + struct dvb_frontend *fe; + struct xc5000_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.i2c_adap = &dev->i2c_bus[1].i2c_adap; + cfg.i2c_addr = addr; + + if (!dev->dvb->frontend) { + printk(KERN_ERR "%s/2: dvb frontend not attached. " + "Can't attach xc5000\n", dev->name); + return -EINVAL; + } + + fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name); + dvb_frontend_detach(dev->dvb->frontend); + dev->dvb->frontend = NULL; + return -EINVAL; + } + + printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name); + + return 0; +} +#endif + +int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq) +{ + int status = 0; + + if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { + + struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; + + if (dops->set_analog_params != NULL) { + struct analog_parameters params; + + params.frequency = freq; + params.std = dev->norm; + params.mode = 0; /* 0- Air; 1 - cable */ + /*params.audmode = ; */ + + /* Set the analog parameters to set the frequency */ + cx231xx_info("Setting Frequency for XC5000\n"); + dops->set_analog_params(dev->dvb->frontend, ¶ms); + } + + } + + return status; +} + +int cx231xx_reset_analog_tuner(struct cx231xx *dev) +{ + int status = 0; + + if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { + + struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; + + if (dops->init != NULL && !dev->xc_fw_load_done) { + + cx231xx_info("Reloading firmware for XC5000\n"); + status = dops->init(dev->dvb->frontend); + if (status == 0) { + dev->xc_fw_load_done = 1; + cx231xx_info + ("XC5000 firmware download completed\n"); + } else { + dev->xc_fw_load_done = 0; + cx231xx_info + ("XC5000 firmware download failed !!!\n"); + } + } + + } + + return status; +} + +/* ------------------------------------------------------------------ */ + +static int register_dvb(struct cx231xx_dvb *dvb, + struct module *module, + struct cx231xx *dev, struct device *device) +{ + int result; + + mutex_init(&dvb->lock); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, + adapter_nr); + if (result < 0) { + printk(KERN_WARNING + "%s: dvb_register_adapter failed (errno = %d)\n", + dev->name, result); + goto fail_adapter; + } + + /* Ensure all frontends negotiate bus access */ + dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl; + + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (result < 0) { + printk(KERN_WARNING + "%s: dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = start_feed; + dvb->demux.stop_feed = stop_feed; + + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING + "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dev->name, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_WARNING + "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + dev->name, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING + "%s: connect_frontend failed (errno = %d)\n", dev->name, + result); + goto fail_fe_conn; + } + + /* register network adapter */ + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +fail_adapter: + return result; +} + +static void unregister_dvb(struct cx231xx_dvb *dvb) +{ + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +} + +static int dvb_init(struct cx231xx *dev) +{ + int result = 0; + struct cx231xx_dvb *dvb; + + if (!dev->board.has_dvb) { + /* This device does not support the extension */ + return 0; + } + + dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL); + + if (dvb == NULL) { + printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n"); + return -ENOMEM; + } + dev->dvb = dvb; + dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq; + dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner; + + cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + /* init frontend */ + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + + /* dev->dvb->frontend = dvb_attach(s5h1411_attach, + &dvico_s5h1411_config, + &dev->i2c_bus[1].i2c_adap); */ + dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach dummy front end\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + if (dvb_attach(xc5000_attach, dev->dvb->frontend, + &dev->i2c_bus[1].i2c_adap, + &cnxt_rde250_tunerconfig) < 0) { + result = -EINVAL; + goto out_free; + } + + break; + case CX231XX_BOARD_CNXT_RDU_250: + + dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach dummy front end\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + if (dvb_attach(xc5000_attach, dev->dvb->frontend, + &dev->i2c_bus[1].i2c_adap, + &cnxt_rde250_tunerconfig) < 0) { + result = -EINVAL; + goto out_free; + } + break; + + default: + printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n", dev->name); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR + "%s/2: frontend initialization failed\n", dev->name); + result = -EINVAL; + goto out_free; + } + + /* register everything */ + result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); + + if (result < 0) + goto out_free; + + cx231xx_set_mode(dev, CX231XX_SUSPEND); + printk(KERN_INFO "Successfully loaded cx231xx-dvb\n"); + return 0; + +out_free: + cx231xx_set_mode(dev, CX231XX_SUSPEND); + kfree(dvb); + dev->dvb = NULL; + return result; +} + +static int dvb_fini(struct cx231xx *dev) +{ + if (!dev->board.has_dvb) { + /* This device does not support the extension */ + return 0; + } + + if (dev->dvb) { + unregister_dvb(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct cx231xx_ops dvb_ops = { + .id = CX231XX_DVB, + .name = "Cx231xx dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init cx231xx_dvb_register(void) +{ + return cx231xx_register_extension(&dvb_ops); +} + +static void __exit cx231xx_dvb_unregister(void) +{ + cx231xx_unregister_extension(&dvb_ops); +} + +module_init(cx231xx_dvb_register); +module_exit(cx231xx_dvb_unregister); diff --git a/linux/drivers/media/video/cx231xx/cx231xx-i2c.c b/linux/drivers/media/video/cx231xx/cx231xx-i2c.c new file mode 100644 index 000000000..87dbd4e4e --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-i2c.c @@ -0,0 +1,601 @@ +/* + cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + Based on Cx23885 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <media/v4l2-common.h> +#include <media/tuner.h> + +#include "cx231xx.h" + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define dprintk1(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(fmt, ##args); \ + } \ +} while (0) + +#define dprintk2(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__ , ##args); \ + } \ +} while (0) + +/* + * cx231xx_i2c_send_bytes() + */ +int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + u16 size = 0; + u8 loop = 0; + u8 saddr_len = 1; + u8 *buf_ptr = NULL; + u16 saddr = 0; + u8 need_gpio = 0; + + if ((bus->nr == 1) && (msg->addr == 0x61) + && (dev->tuner_type == TUNER_XC5000)) { + + size = msg->len; + + if (size == 2) { /* register write sub addr */ + /* Just writing sub address will cause problem + * to XC5000. So ignore the request */ + return 0; + } else if (size == 4) { /* register write with sub addr */ + if (msg->len >= 2) + saddr = msg->buf[0] << 8 | msg->buf[1]; + else if (msg->len == 1) + saddr = msg->buf[0]; + + switch (saddr) { + case 0x0000: /* start tuner calibration mode */ + need_gpio = 1; + /* FW Loading is done */ + dev->xc_fw_load_done = 1; + break; + case 0x000D: /* Set signal source */ + case 0x0001: /* Set TV standard - Video */ + case 0x0002: /* Set TV standard - Audio */ + case 0x0003: /* Set RF Frequency */ + need_gpio = 1; + break; + default: + if (dev->xc_fw_load_done) + need_gpio = 1; + break; + } + + if (need_gpio) { + dprintk1(1, + "GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n", + msg->addr, msg->len, saddr); + + return dev->cx231xx_gpio_i2c_write(dev, + msg->addr, + msg->buf, + msg->len); + } + } + + /* special case for Xc5000 tuner case */ + saddr_len = 1; + + /* adjust the length to correct length */ + size -= saddr_len; + buf_ptr = (u8 *) (msg->buf + 1); + + do { + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = msg->buf[0]; + req_data.buf_size = size > 16 ? 16 : size; + req_data.p_buffer = (u8 *) (buf_ptr + loop * 16); + + bus->i2c_nostop = (size > 16) ? 1 : 0; + bus->i2c_reserve = (loop == 0) ? 0 : 1; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + loop++; + + if (size >= 16) + size -= 16; + else + size = 0; + + } while (size > 0); + + bus->i2c_nostop = 0; + bus->i2c_reserve = 0; + + } else { /* regular case */ + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = 0; + req_data.saddr_dat = 0; + req_data.buf_size = msg->len; + req_data.p_buffer = msg->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + } + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_recv_bytes() + * read a byte from the i2c device + */ +static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + u16 saddr = 0; + u8 need_gpio = 0; + + if ((bus->nr == 1) && (msg->addr == 0x61) + && dev->tuner_type == TUNER_XC5000) { + + if (msg->len == 2) + saddr = msg->buf[0] << 8 | msg->buf[1]; + else if (msg->len == 1) + saddr = msg->buf[0]; + + if (dev->xc_fw_load_done) { + + switch (saddr) { + case 0x0009: /* BUSY check */ + dprintk1(1, + "GPIO R E A D: Special case BUSY check \n"); + /*Try read BUSY register, just set it to zero*/ + msg->buf[0] = 0; + if (msg->len == 2) + msg->buf[1] = 0; + return 0; + case 0x0004: /* read Lock status */ + need_gpio = 1; + break; + + } + + if (need_gpio) { + /* this is a special case to handle Xceive tuner + clock stretch issue with gpio based I2C */ + + dprintk1(1, + "GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n", + msg->addr, msg->len, + msg->buf[0] << 8 | msg->buf[1]); + + status = + dev->cx231xx_gpio_i2c_write(dev, msg->addr, + msg->buf, + msg->len); + status = + dev->cx231xx_gpio_i2c_read(dev, msg->addr, + msg->buf, + msg->len); + return status; + } + } + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = msg->len; + req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1]; + req_data.buf_size = msg->len; + req_data.p_buffer = msg->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + + } else { + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = 0; + req_data.saddr_dat = 0; + req_data.buf_size = msg->len; + req_data.p_buffer = msg->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + } + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_recv_bytes_with_saddr() + * read a byte from the i2c device + */ +static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg1, + const struct i2c_msg *msg2) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + u16 saddr = 0; + u8 need_gpio = 0; + + if (msg1->len == 2) + saddr = msg1->buf[0] << 8 | msg1->buf[1]; + else if (msg1->len == 1) + saddr = msg1->buf[0]; + + if ((bus->nr == 1) && (msg2->addr == 0x61) + && dev->tuner_type == TUNER_XC5000) { + + if ((msg2->len < 16)) { + + dprintk1(1, + "i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n", + msg2->addr, msg2->len, saddr, msg1->len); + + switch (saddr) { + case 0x0008: /* read FW load status */ + need_gpio = 1; + break; + case 0x0004: /* read Lock status */ + need_gpio = 1; + break; + } + + if (need_gpio) { + status = + dev->cx231xx_gpio_i2c_write(dev, msg1->addr, + msg1->buf, + msg1->len); + status = + dev->cx231xx_gpio_i2c_read(dev, msg2->addr, + msg2->buf, + msg2->len); + return status; + } + } + } + + /* prepare xfer_data struct */ + req_data.dev_addr = msg2->addr; + req_data.direction = msg2->flags; + req_data.saddr_len = msg1->len; + req_data.saddr_dat = saddr; + req_data.buf_size = msg2->len; + req_data.p_buffer = msg2->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_check_for_device() + * check if there is a i2c_device at the supplied address + */ +static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = 0; + req_data.saddr_dat = 0; + req_data.buf_size = 0; + req_data.p_buffer = NULL; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_xfer() + * the main i2c transfer function + */ +static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + int addr, rc, i, byte; + + if (num <= 0) + return 0; + + for (i = 0; i < num; i++) { + + addr = msgs[i].addr >> 1; + + dprintk2(2, "%s %s addr=%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (!msgs[i].len) { + /* no len: check only for device presence */ + rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]); + if (rc < 0) { + dprintk2(2, " no device\n"); + return rc; + } + + } else if (msgs[i].flags & I2C_M_RD) { + /* read bytes */ + rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]); + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr + && (msgs[i].len <= 2) && (bus->nr < 2)) { + /* read bytes */ + rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap, + &msgs[i], + &msgs[i + 1]); + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + i++; + } else { + /* write bytes */ + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]); + } + if (rc < 0) + goto err; + if (i2c_debug >= 2) + printk("\n"); + } + + return num; +err: + dprintk2(2, " ERROR: %i\n", rc); + return rc; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; +} + +/* + * attach_inform() + * gets called when a device attaches to the i2c bus + * does some basic configuration + */ +static int attach_inform(struct i2c_client *client) +{ + struct cx231xx_i2c *bus = i2c_get_adapdata(client->adapter); + struct cx231xx *dev = bus->dev; + + switch (client->addr << 1) { + case 0x32: + dprintk1(1, "attach_inform: Geminit III detected.\n"); + break; + case 0x02: + dprintk1(1, "attach_inform: Acquarius detected.\n"); + break; + case 0xa0: + dprintk1(1, "attach_inform: eeprom detected.\n"); + break; + case 0x60: + dprintk1(1, "attach_inform: Colibri detected.\n"); + break; + case 0x8e: + { + struct IR_i2c *ir = i2c_get_clientdata(client); + dprintk1(1, "attach_inform: IR detected (%s).\n", + ir->phys); + cx231xx_set_ir(dev, ir); + break; + } + case 0x80: + case 0x88: + dprintk1(1, "attach_inform: Hammerhead detected.\n"); + break; + + default: + if (!dev->tuner_addr) + dev->tuner_addr = client->addr; + + dprintk1(1, "attach inform: detected I2C address %x\n", + client->addr << 1); + } + + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + dprintk1(1, "i2c detach [client=%s]\n", client->name); + return 0; +} + +static struct i2c_algorithm cx231xx_algo = { + .master_xfer = cx231xx_i2c_xfer, + .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif +}; + +static struct i2c_adapter cx231xx_adap_template = { + .owner = THIS_MODULE, + .class = I2C_CLASS_TV_ANALOG, + .name = "cx231xx", + .id = I2C_HW_B_CX231XX, + .algo = &cx231xx_algo, + .client_register = attach_inform, + .client_unregister = detach_inform, +}; + +static struct i2c_client cx231xx_client_template = { + .name = "cx231xx internal", +}; + +/* ----------------------------------------------------------- */ + +/* + * i2c_devs + * incomplete list of known devices + */ +static char *i2c_devs[128] = { + [0x60 >> 1] = "colibri", + [0x88 >> 1] = "hammerhead", + [0x8e >> 1] = "CIR", + [0x32 >> 1] = "GeminiIII", + [0x02 >> 1] = "Aquarius", + [0xa0 >> 1] = "eeprom", + [0xc0 >> 1] = "tuner/XC3028", + [0xc2 >> 1] = "tuner/XC5000", +}; + +/* + * cx231xx_do_i2c_scan() + * check i2c address range for devices + */ +void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c) +{ + unsigned char buf; + int i, rc; + + cx231xx_info(": Checking for I2C devices ..\n"); + for (i = 0; i < 128; i++) { + c->addr = i; + rc = i2c_master_recv(c, &buf, 0); + if (rc < 0) + continue; + cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n", + dev->name, i << 1, + i2c_devs[i] ? i2c_devs[i] : "???"); + } + cx231xx_info(": Completed Checking for I2C devices.\n"); +} + +/* + * cx231xx_i2c_call_clients() + * send commands to all attached i2c devices + */ +void cx231xx_i2c_call_clients(struct cx231xx_i2c *bus, unsigned int cmd, + void *arg) +{ + /* struct cx231xx *dev = bus->dev; */ + + BUG_ON(NULL == bus->i2c_adap.algo_data); + i2c_clients_command(&bus->i2c_adap, cmd, arg); +} + +/* + * cx231xx_i2c_register() + * register i2c bus + */ +int cx231xx_i2c_register(struct cx231xx_i2c *bus) +{ + struct cx231xx *dev = bus->dev; + + BUG_ON(!dev->cx231xx_send_usb_command); + + cx231xx_info("%s(bus = %d)\n", __func__, bus->nr); + + memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap)); + memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo)); + memcpy(&bus->i2c_client, &cx231xx_client_template, + sizeof(bus->i2c_client)); + + bus->i2c_adap.dev.parent = &dev->udev->dev; + + strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name)); + + bus->i2c_algo.data = bus; + bus->i2c_adap.algo_data = bus; + i2c_set_adapdata(&bus->i2c_adap, bus); + i2c_add_adapter(&bus->i2c_adap); + + bus->i2c_client.adapter = &bus->i2c_adap; + + if (0 == bus->i2c_rc) { + cx231xx_info("%s: i2c bus %d registered\n", dev->name, bus->nr); + if (i2c_scan) + cx231xx_do_i2c_scan(dev, &bus->i2c_client); + } else + cx231xx_warn("%s: i2c bus %d register FAILED\n", + dev->name, bus->nr); + + return bus->i2c_rc; +} + +/* + * cx231xx_i2c_unregister() + * unregister i2c_bus + */ +int cx231xx_i2c_unregister(struct cx231xx_i2c *bus) +{ + i2c_del_adapter(&bus->i2c_adap); + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-input.c b/linux/drivers/media/video/cx231xx/cx231xx-input.c new file mode 100644 index 000000000..04d954cca --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-input.c @@ -0,0 +1,263 @@ +/* + handle cx231xx IR remotes via linux kernel input layer. + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + + < This is a place holder for IR now.> + + 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 <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/usb.h> + +#include "compat.h" +#include "cx231xx.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); + +#define i2cdprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \ + } + +#define dprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } + +/********************************************************** + Polling structure used by cx231xx IR's + **********************************************************/ + +struct cx231xx_ir_poll_result { + unsigned int toggle_bit:1; + unsigned int read_count:7; + u8 rc_address; + u8 rc_data[4]; +}; + +struct cx231xx_IR { + struct cx231xx *dev; + struct input_dev *input; + struct ir_input_state ir; + char name[32]; + char phys[32]; + + /* poll external decoder */ + int polling; + struct work_struct work; + struct timer_list timer; + unsigned int last_toggle:1; + unsigned int last_readcount; + unsigned int repeat_interval; + + int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *); +}; + +/********************************************************** + Polling code for cx231xx + **********************************************************/ + +static void cx231xx_ir_handle_key(struct cx231xx_IR *ir) +{ + int result; + int do_sendkey = 0; + struct cx231xx_ir_poll_result poll_result; + + /* read the registers containing the IR status */ + result = ir->get_key(ir, &poll_result); + if (result < 0) { + dprintk("ir->get_key() failed %d\n", result); + return; + } + + dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n", + poll_result.toggle_bit, poll_result.read_count, + ir->last_readcount, poll_result.rc_data[0]); + + if (ir->dev->chip_id == CHIP_ID_EM2874) { + /* The em2874 clears the readcount field every time the + register is read. The em2860/2880 datasheet says that it + is supposed to clear the readcount, but it doesn't. So with + the em2874, we are looking for a non-zero read count as + opposed to a readcount that is incrementing */ + ir->last_readcount = 0; + } + + if (poll_result.read_count == 0) { + /* The button has not been pressed since the last read */ + } else if (ir->last_toggle != poll_result.toggle_bit) { + /* A button has been pressed */ + dprintk("button has been pressed\n"); + ir->last_toggle = poll_result.toggle_bit; + ir->repeat_interval = 0; + do_sendkey = 1; + } else if (poll_result.toggle_bit == ir->last_toggle && + poll_result.read_count > 0 && + poll_result.read_count != ir->last_readcount) { + /* The button is still being held down */ + dprintk("button being held down\n"); + + /* Debouncer for first keypress */ + if (ir->repeat_interval++ > 9) { + /* Start repeating after 1 second */ + do_sendkey = 1; + } + } + + if (do_sendkey) { + dprintk("sending keypress\n"); + ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], + poll_result.rc_data[0]); + ir_input_nokey(ir->input, &ir->ir); + } + + ir->last_readcount = poll_result.read_count; + return; +} + +static void ir_timer(unsigned long data) +{ + struct cx231xx_IR *ir = (struct cx231xx_IR *)data; + + schedule_work(&ir->work); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void cx231xx_ir_work(void *data) +#else +static void cx231xx_ir_work(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct cx231xx_IR *ir = data; +#else + struct cx231xx_IR *ir = container_of(work, struct cx231xx_IR, work); +#endif + + cx231xx_ir_handle_key(ir); + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); +} + +void cx231xx_ir_start(struct cx231xx_IR *ir) +{ + setup_timer(&ir->timer, ir_timer, (unsigned long)ir); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&ir->work, cx231xx_ir_work, ir); +#else + INIT_WORK(&ir->work, cx231xx_ir_work); +#endif + schedule_work(&ir->work); +} + +static void cx231xx_ir_stop(struct cx231xx_IR *ir) +{ + del_timer_sync(&ir->timer); + flush_scheduled_work(); +} + +int cx231xx_ir_init(struct cx231xx *dev) +{ + struct cx231xx_IR *ir; + struct input_dev *input_dev; + u8 ir_config; + int err = -ENOMEM; + + if (dev->board.ir_codes == NULL) { + /* No remote control support */ + return 0; + } + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ir || !input_dev) + goto err_out_free; + + ir->input = input_dev; + + /* Setup the proper handler based on the chip */ + switch (dev->chip_id) { + default: + printk("Unrecognized cx231xx chip id: IR not supported\n"); + goto err_out_free; + } + + /* This is how often we ask the chip for IR information */ + ir->polling = 100; /* ms */ + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "cx231xx IR (%s)", dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); + input_dev->name = ir->name; + input_dev->phys = ir->phys; + input_dev->id.bustype = BUS_USB; + input_dev->id.version = 1; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + input_dev->dev.parent = &dev->udev->dev; +#else + input_dev->cdev.dev = &dev->udev->dev; +#endif + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + + cx231xx_ir_start(ir); + + /* all done */ + err = input_register_device(ir->input); + if (err) + goto err_out_stop; + + return 0; +err_out_stop: + cx231xx_ir_stop(ir); + dev->ir = NULL; +err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; +} + +int cx231xx_ir_fini(struct cx231xx *dev) +{ + struct cx231xx_IR *ir = dev->ir; + + /* skip detach on non attached boards */ + if (!ir) + return 0; + + cx231xx_ir_stop(ir); + input_unregister_device(ir->input); + kfree(ir); + + /* done */ + dev->ir = NULL; + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c b/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c new file mode 100644 index 000000000..c00f51eae --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c @@ -0,0 +1,793 @@ +/* + cx231xx-pcb-config.c - driver for Conexant + Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot 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 "cx231xx.h" +#include "cx231xx-conf-reg.h" + +/******************************************************************************/ + +struct pcb_config cx231xx_Scenario[] = { + { + INDEX_SELFPOWER_DIGITAL_ONLY, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_DIGITAL, /* mode */ + SOURCE_TS_BDA, /* ts1_source, digital tv only */ + NOT_SUPPORTED, /* ts2_source */ + NOT_SUPPORTED, /* analog source */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + , + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed config */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_DUAL_DIGITAL, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_DIGITAL, /* mode */ + SOURCE_TS_BDA, /* ts1_source, digital tv only */ + 0, /* ts2_source,need update from register */ + NOT_SUPPORTED, /* analog source */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_ANALOG_ONLY, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */ + NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */ + NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */ + 0, /* analog source, need update */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + 3, /* VANC */ + 4, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_DUAL, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv and digital path */ + MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, + 0, /* ts1_source,will update in register */ + NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */ + 0, /* analog source need update */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + 4, /* VANC */ + 5, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_TRIPLE, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv and digital path */ + MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, + 0, /* ts1_source, update in register */ + 0, /* ts2_source,update in register */ + 0, /* analog source, need update */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + 3, /* AUDIO */ + 4, /* VIDEO */ + 5, /* VANC */ + 6, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + 3, /* AUDIO */ + 4, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_COMPRESSOR, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv AND DIGITAL path */ + MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, + NOT_SUPPORTED, /* ts1_source, disable */ + SOURCE_TS_BDA, /* ts2_source */ + 0, /* analog source,need update */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + 1, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + 4, /* VANC */ + 5, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + 1, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_BUSPOWER_DIGITAL_ONLY, /* index */ + USB_BUS_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */ + SOURCE_TS_BDA, /* ts1_source, disable */ + NOT_SUPPORTED, /* ts2_source */ + NOT_SUPPORTED, /* analog source */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index = 2 */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index = 2 */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + { + INDEX_BUSPOWER_ANALOG_ONLY, /* index */ + USB_BUS_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */ + NOT_SUPPORTED, /* ts1_source, disable */ + NOT_SUPPORTED, /* ts2_source */ + SOURCE_ANALOG, /* analog source--analog */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + 3, /* VANC */ + 4, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + { /* full-speed */ + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + { + INDEX_BUSPOWER_DIF_ONLY, /* index */ + USB_BUS_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv AND DIGITAL path */ + MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL, + SOURCE_TS_BDA, /* ts1_source, disable */ + NOT_SUPPORTED, /* ts2_source */ + SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + 4, /* VANC */ + 5, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + { /* full speed */ + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + +}; + +/*****************************************************************/ + +u32 initialize_cx231xx(struct cx231xx *dev) +{ + u32 config_info = 0; + struct pcb_config *p_pcb_info; + u8 usb_speed = 1; /* from register,1--HS, 0--FS */ + u8 data[4] = { 0, 0, 0, 0 }; + u32 ts1_source = 0; + u32 ts2_source = 0; + u32 analog_source = 0; + u8 tmp = 0; + u8 _current_scenario_idx = 0xff; + + cx231xx_info("PcbConfig::initialize \n"); + + ts1_source = SOURCE_TS_BDA; + ts2_source = SOURCE_TS_BDA; + + /* read board config register to find out which + pcb config it is related to */ + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4); + + config_info = *((u32 *) data); + cx231xx_info("SC(0x00) register = 0x%x\n", config_info); + usb_speed = (u8) (config_info & 0x1); + + /* Verify this device belongs to Bus power or Self power device */ + if (config_info & BUS_POWER) { /* bus-power */ + switch (config_info & BUSPOWER_MASK) { + case TS1_PORT | BUS_POWER: + cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed = + usb_speed; + p_pcb_info = + &cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY]; + _current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY; + break; + case AVDEC_ENABLE | BUS_POWER: + cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed = + usb_speed; + p_pcb_info = + &cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY]; + _current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY; + break; + case AVDEC_ENABLE | BUS_POWER | TS1_PORT: + cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed = + usb_speed; + p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY]; + _current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY; + break; + default: + cx231xx_info("bad config in buspower!!!!\n"); + cx231xx_info("config_info=%x\n", + (config_info & BUSPOWER_MASK)); + return 1; + } + } else { /* self-power */ + + switch (config_info & SELFPOWER_MASK) { + case TS1_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed = + usb_speed; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY]; + _current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY; + break; + case TS1_TS2_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL]. + ts2_source = ts2_source; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL]; + _current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL; + break; + case AVDEC_ENABLE | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY]. + analog_source = analog_source; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY]; + _current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY; + break; + case AVDEC_ENABLE | TS1_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source = + ts1_source; + cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source = + analog_source; + p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL]; + _current_scenario_idx = INDEX_SELFPOWER_DUAL; + break; + case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source = + ts1_source; + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source = + ts2_source; + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source = + analog_source; + p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE]; + _current_scenario_idx = INDEX_SELFPOWER_TRIPLE; + break; + case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR]. + analog_source = analog_source; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR]; + _current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR; + break; + default: + cx231xx_info("bad senario!!!!!\n"); + cx231xx_info("config_info=%x\n", + (config_info & SELFPOWER_MASK)); + return 1; + } + } + + dev->current_scenario_idx = _current_scenario_idx; + + memcpy(&dev->current_pcb_config, p_pcb_info, + sizeof(struct pcb_config)); + + /*******************************************************************/ + tmp = (dev->current_pcb_config.index) + 1; + + cx231xx_info("scenario %d\n", tmp); + cx231xx_info("type=%x\n", dev->current_pcb_config.type); + cx231xx_info("mode=%x\n", dev->current_pcb_config.mode); + cx231xx_info("speed=%x\n", dev->current_pcb_config.speed); + cx231xx_info("ts1_source=%x\n", dev->current_pcb_config.ts1_source); + cx231xx_info("ts2_source=%x\n", dev->current_pcb_config.ts2_source); + cx231xx_info("analog_source=%x\n", + dev->current_pcb_config.analog_source); + /*******************************************************************/ + + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h b/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h new file mode 100644 index 000000000..86fec113f --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h @@ -0,0 +1,235 @@ +/* + cx231xx-pcb-cfg.h - driver for Conexant + Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot 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 _PCB_CONFIG_H_ +#define _PCB_CONFIG_H_ + +#include <linux/init.h> +#include <linux/module.h> + +/*************************************************************************** + * Class Information * +***************************************************************************/ +#define CLASS_DEFAULT 0xFF + +enum VENDOR_REQUEST_TYPE { + /* Set/Get I2C */ + VRT_SET_I2C0 = 0x0, + VRT_SET_I2C1 = 0x1, + VRT_SET_I2C2 = 0x2, + VRT_GET_I2C0 = 0x4, + VRT_GET_I2C1 = 0x5, + VRT_GET_I2C2 = 0x6, + + /* Set/Get GPIO */ + VRT_SET_GPIO = 0x8, + VRT_GET_GPIO = 0x9, + + /* Set/Get GPIE */ + VRT_SET_GPIE = 0xA, + VRT_GET_GPIE = 0xB, + + /* Set/Get Register Control/Status */ + VRT_SET_REGISTER = 0xC, + VRT_GET_REGISTER = 0xD, + + /* Get Extended Compat ID Descriptor */ + VRT_GET_EXTCID_DESC = 0xFF, +}; + +enum BYTE_ENABLE_MASK { + ENABLE_ONE_BYTE = 0x1, + ENABLE_TWE_BYTE = 0x3, + ENABLE_THREE_BYTE = 0x7, + ENABLE_FOUR_BYTE = 0xF, +}; + +#define SPEED_MASK 0x1 +enum USB_SPEED{ + FULL_SPEED = 0x0, /* 0: full speed */ + HIGH_SPEED = 0x1 /* 1: high speed */ +}; + +enum _true_false{ + FALSE = 0, + TRUE = 1 +}; + +#define TS_MASK 0x6 +enum TS_PORT{ + NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid, + only offers Analog TV or Video */ + TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode : + Digital or External Analog/Compressed source) */ + TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs + (Dual inputs from Digital and/or + External Analog/Compressed sources) */ + TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector + to external clock */ + TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output, + TS2 Input (from Compressor) */ +}; + +#define EAVP_MASK 0x8 +enum EAV_PRESENT{ + NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs + (no need for Flatiron), + Analog Tuner must be present */ + EXTERNAL_AV = 0x8 /* 1: External A/V inputs + present (requires Flatiron) */ +}; + +#define ATM_MASK 0x30 +enum AT_MODE{ + DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */ + BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite & + Sound-IF Signals present */ + NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */ +}; + +#define PWR_SEL_MASK 0x40 +enum POWE_TYPE{ + SELF_POWER = 0x0, /* 0: self power */ + BUS_POWER = 0x40 /* 1: bus power */ +}; + +enum USB_POWE_TYPE{ + USB_SELF_POWER = 0, + USB_BUS_POWER +}; + +#define BO_0_MASK 0x80 +enum AVDEC_STATUS{ + AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */ + AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */ +}; + +#define BO_1_MASK 0x100 +enum HAMMERHEAD__STATUS{ + HAMMERHEAD_ONLY = 0x0, /* 0:Hammerhead Only */ + HAMMERHEAD_SC = 0x100 /* 1:Hammerhead and SC */ +}; + +#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */ +#define SELFPOWER_MASK 0x86 + +/***************************************************************************/ +#define NOT_DECIDE_YET 0xFE +#define NOT_SUPPORTED 0xFF + +/*************************************************************************** + * for mod field use * +***************************************************************************/ +#define MOD_DIGITAL 0x1 +#define MOD_ANALOG 0x2 +#define MOD_DIF 0x4 +#define MOD_EXTERNAL 0x8 +#define CAP_ALL_MOD 0x0f + +/*************************************************************************** + * source define * +***************************************************************************/ +#define SOURCE_DIGITAL 0x1 +#define SOURCE_ANALOG 0x2 +#define SOURCE_DIF 0x4 +#define SOURCE_EXTERNAL 0x8 +#define SOURCE_TS_BDA 0x10 +#define SOURCE_TS_ENCODE 0x20 +#define SOURCE_TS_EXTERNAL 0x40 + +/*************************************************************************** + * interface information define * +***************************************************************************/ +struct INTERFACE_INFO { + u8 interrupt_index; + u8 ts1_index; + u8 ts2_index; + u8 audio_index; + u8 video_index; + u8 vanc_index; /* VBI */ + u8 hanc_index; /* Sliced CC */ + u8 ir_index; +}; + +enum INDEX_INTERFACE_INFO{ + INDEX_INTERRUPT = 0x0, + INDEX_TS1, + INDEX_TS2, + INDEX_AUDIO, + INDEX_VIDEO, + INDEX_VANC, + INDEX_HANC, + INDEX_IR, +}; + +/*************************************************************************** + * configuration information define * +***************************************************************************/ +struct CONFIG_INFO { + u8 config_index; + struct INTERFACE_INFO interface_info; +}; + +struct pcb_config { + u8 index; + u8 type; /* bus power or self power, + self power--0, bus_power--1 */ + u8 speed; /* usb speed, 2.0--1, 1.1--0 */ + u8 mode; /* digital , anlog, dif or external A/V */ + u32 ts1_source; /* three source -- BDA,External,encode */ + u32 ts2_source; + u32 analog_source; + u8 digital_index; /* bus-power used */ + u8 analog_index; /* bus-power used */ + u8 dif_index; /* bus-power used */ + u8 external_index; /* bus-power used */ + u8 config_num; /* current config num, 0,1,2, + for self-power, always 0 */ + struct CONFIG_INFO hs_config_info[3]; + struct CONFIG_INFO fs_config_info[3]; +}; + +enum INDEX_PCB_CONFIG{ + INDEX_SELFPOWER_DIGITAL_ONLY = 0x0, + INDEX_SELFPOWER_DUAL_DIGITAL, + INDEX_SELFPOWER_ANALOG_ONLY, + INDEX_SELFPOWER_DUAL, + INDEX_SELFPOWER_TRIPLE, + INDEX_SELFPOWER_COMPRESSOR, + INDEX_BUSPOWER_DIGITAL_ONLY, + INDEX_BUSPOWER_ANALOG_ONLY, + INDEX_BUSPOWER_DIF_ONLY, + INDEX_BUSPOWER_EXTERNAL_ONLY, + INDEX_BUSPOWER_EXTERNAL_ANALOG, + INDEX_BUSPOWER_EXTERNAL_DIF, + INDEX_BUSPOWER_EXTERNAL_DIGITAL, + INDEX_BUSPOWER_DIGITAL_ANALOG, + INDEX_BUSPOWER_DIGITAL_DIF, + INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL, + INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL, +}; + +/***************************************************************************/ +struct cx231xx; + +u32 initialize_cx231xx(struct cx231xx *p_dev); + +#endif diff --git a/linux/drivers/media/video/cx231xx/cx231xx-reg.h b/linux/drivers/media/video/cx231xx/cx231xx-reg.h new file mode 100644 index 000000000..750c5d37d --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-reg.h @@ -0,0 +1,1564 @@ +/* + cx231xx-reg.h - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot 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 _CX231XX_REG_H +#define _CX231XX_REG_H + +/***************************************************************************** + * VBI codes * +*****************************************************************************/ + +#define SAV_ACTIVE_VIDEO_FIELD1 0x80 +#define EAV_ACTIVE_VIDEO_FIELD1 0x90 + +#define SAV_ACTIVE_VIDEO_FIELD2 0xc0 +#define EAV_ACTIVE_VIDEO_FIELD2 0xd0 + +#define SAV_VBLANK_FIELD1 0xa0 +#define EAV_VBLANK_FIELD1 0xb0 + +#define SAV_VBLANK_FIELD2 0xe0 +#define EAV_VBLANK_FIELD2 0xf0 + +#define SAV_VBI_FIELD1 0x20 +#define EAV_VBI_FIELD1 0x30 + +#define SAV_VBI_FIELD2 0x60 +#define EAV_VBI_FIELD2 0x70 + +/*****************************************************************************/ +/* Audio ADC Registers */ +#define CH_PWR_CTRL1 0x0000000e +#define CH_PWR_CTRL2 0x0000000f +/*****************************************************************************/ + +#define HOST_REG1 0x000 +#define FLD_FORCE_CHIP_SEL 0x80 +#define FLD_AUTO_INC_DIS 0x20 +#define FLD_PREFETCH_EN 0x10 +/* Reserved [2:3] */ +#define FLD_DIGITAL_PWR_DN 0x02 +#define FLD_SLEEP 0x01 + +/*****************************************************************************/ +#define HOST_REG2 0x001 + +/*****************************************************************************/ +#define HOST_REG3 0x002 + +/*****************************************************************************/ +/* added for polaris */ +#define GPIO_PIN_CTL0 0x3 +#define GPIO_PIN_CTL1 0x4 +#define GPIO_PIN_CTL2 0x5 +#define GPIO_PIN_CTL3 0x6 +#define TS1_PIN_CTL0 0x7 +#define TS1_PIN_CTL1 0x8 +/*****************************************************************************/ + +#define FLD_CLK_IN_EN 0x80 +#define FLD_XTAL_CTRL 0x70 +#define FLD_BB_CLK_MODE 0x0C +#define FLD_REF_DIV_PLL 0x02 +#define FLD_REF_SEL_PLL1 0x01 + +/*****************************************************************************/ +#define CHIP_CTRL 0x100 +/* Reserved [27] */ +/* Reserved [31:21] */ +#define FLD_CHIP_ACFG_DIS 0x00100000 +/* Reserved [19] */ +#define FLD_DUAL_MODE_ADC2 0x00040000 +#define FLD_SIF_EN 0x00020000 +#define FLD_SOFT_RST 0x00010000 +#define FLD_DEVICE_ID 0x0000ffff + +/*****************************************************************************/ +#define AFE_CTRL 0x104 +#define AFE_CTRL_C2HH_SRC_CTRL 0x104 +#define FLD_DIF_OUT_SEL 0xc0000000 +#define FLD_AUX_PLL_CLK_ALT_SEL 0x3c000000 +#define FLD_UV_ORDER_MODE 0x02000000 +#define FLD_FUNC_MODE 0x01800000 +#define FLD_ROT1_PHASE_CTL 0x007f8000 +#define FLD_AUD_IN_SEL 0x00004000 +#define FLD_LUMA_IN_SEL 0x00002000 +#define FLD_CHROMA_IN_SEL 0x00001000 +/* reserve [11:10] */ +#define FLD_INV_SPEC_DIS 0x00000200 +#define FLD_VGA_SEL_CH3 0x00000100 +#define FLD_VGA_SEL_CH2 0x00000080 +#define FLD_VGA_SEL_CH1 0x00000040 +#define FLD_DCR_BYP_CH1 0x00000020 +#define FLD_DCR_BYP_CH2 0x00000010 +#define FLD_DCR_BYP_CH3 0x00000008 +#define FLD_EN_12DB_CH3 0x00000004 +#define FLD_EN_12DB_CH2 0x00000002 +#define FLD_EN_12DB_CH1 0x00000001 + +/* redefine in Cx231xx */ +/*****************************************************************************/ +#define DC_CTRL1 0x108 +/* reserve [31:30] */ +#define FLD_CLAMP_LVL_CH1 0x3fff8000 +#define FLD_CLAMP_LVL_CH2 0x00007fff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL2 0x10c +/* reserve [31:28] */ +#define FLD_CLAMP_LVL_CH3 0x00fffe00 +#define FLD_CLAMP_WIND_LENTH 0x000001e0 +#define FLD_C2HH_SAT_MIN 0x0000001e +#define FLD_FLT_BYP_SEL 0x00000001 +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL3 0x110 +/* reserve [31:16] */ +#define FLD_ERR_GAIN_CTL 0x00070000 +#define FLD_LPF_MIN 0x0000ffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL4 0x114 +/* reserve [31:31] */ +#define FLD_INTG_CH1 0x7fffffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL5 0x118 +/* reserve [31:31] */ +#define FLD_INTG_CH2 0x7fffffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL6 0x11c +/* reserve [31:31] */ +#define FLD_INTG_CH3 0x7fffffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define PIN_CTRL 0x120 +#define FLD_OEF_AGC_RF 0x00000001 +#define FLD_OEF_AGC_IFVGA 0x00000002 +#define FLD_OEF_AGC_IF 0x00000004 +#define FLD_REG_BO_PUD 0x80000000 +#define FLD_IR_IRQ_STAT 0x40000000 +#define FLD_AUD_IRQ_STAT 0x20000000 +#define FLD_VID_IRQ_STAT 0x10000000 +/* Reserved [27:26] */ +#define FLD_IRQ_N_OUT_EN 0x02000000 +#define FLD_IRQ_N_POLAR 0x01000000 +/* Reserved [23:6] */ +#define FLD_OE_AUX_PLL_CLK 0x00000020 +#define FLD_OE_I2S_BCLK 0x00000010 +#define FLD_OE_I2S_WCLK 0x00000008 +#define FLD_OE_AGC_IF 0x00000004 +#define FLD_OE_AGC_IFVGA 0x00000002 +#define FLD_OE_AGC_RF 0x00000001 + +/*****************************************************************************/ +#define AUD_IO_CTRL 0x124 +/* Reserved [31:8] */ +#define FLD_I2S_PORT_DIR 0x00000080 +#define FLD_I2S_OUT_SRC 0x00000040 +#define FLD_AUD_CHAN3_SRC 0x00000030 +#define FLD_AUD_CHAN2_SRC 0x0000000c +#define FLD_AUD_CHAN1_SRC 0x00000003 + +/*****************************************************************************/ +#define AUD_LOCK1 0x128 +#define FLD_AUD_LOCK_KI_SHIFT 0xc0000000 +#define FLD_AUD_LOCK_KD_SHIFT 0x30000000 +/* Reserved [27:25] */ +#define FLD_EN_AV_LOCK 0x01000000 +#define FLD_VID_COUNT 0x00ffffff + +/*****************************************************************************/ +#define AUD_LOCK2 0x12c +#define FLD_AUD_LOCK_KI_MULT 0xf0000000 +#define FLD_AUD_LOCK_KD_MULT 0x0F000000 +/* Reserved [23:22] */ +#define FLD_AUD_LOCK_FREQ_SHIFT 0x00300000 +#define FLD_AUD_COUNT 0x000fffff + +/*****************************************************************************/ +#define AFE_DIAG_CTRL1 0x134 +/* Reserved [31:16] */ +#define FLD_CUV_DLY_LENGTH 0x0000ff00 +#define FLD_YC_DLY_LENGTH 0x000000ff + +/*****************************************************************************/ +/* Poalris redefine */ +#define AFE_DIAG_CTRL3 0x138 +/* Reserved [31:26] */ +#define FLD_AUD_DUAL_FLAG_POL 0x02000000 +#define FLD_VID_DUAL_FLAG_POL 0x01000000 +/* Reserved [23:23] */ +#define FLD_COL_CLAMP_DIS_CH1 0x00400000 +#define FLD_COL_CLAMP_DIS_CH2 0x00200000 +#define FLD_COL_CLAMP_DIS_CH3 0x00100000 + +#define TEST_CTRL1 0x144 +/* Reserved [31:29] */ +#define FLD_LBIST_EN 0x10000000 +/* Reserved [27:10] */ +#define FLD_FI_BIST_INTR_R 0x0000200 +#define FLD_FI_BIST_INTR_L 0x0000100 +#define FLD_BIST_FAIL_AUD_PLL 0x0000080 +#define FLD_BIST_INTR_AUD_PLL 0x0000040 +#define FLD_BIST_FAIL_VID_PLL 0x0000020 +#define FLD_BIST_INTR_VID_PLL 0x0000010 +/* Reserved [3:1] */ +#define FLD_CIR_TEST_DIS 0x00000001 + +/*****************************************************************************/ +#define TEST_CTRL2 0x148 +#define FLD_TSXCLK_POL_CTL 0x80000000 +#define FLD_ISO_CTL_SEL 0x40000000 +#define FLD_ISO_CTL_EN 0x20000000 +#define FLD_BIST_DEBUGZ 0x10000000 +#define FLD_AUD_BIST_TEST_H 0x0f000000 +/* Reserved [23:22] */ +#define FLD_FLTRN_BIST_TEST_H 0x00020000 +#define FLD_VID_BIST_TEST_H 0x00010000 +/* Reserved [19:17] */ +#define FLD_BIST_TEST_H 0x00010000 +/* Reserved [15:13] */ +#define FLD_TAB_EN 0x00001000 +/* Reserved [11:0] */ + +/*****************************************************************************/ +#define BIST_STAT 0x14c +#define FLD_AUD_BIST_FAIL_H 0xfff00000 +#define FLD_FLTRN_BIST_FAIL_H 0x00180000 +#define FLD_VID_BIST_FAIL_H 0x00070000 +#define FLD_AUD_BIST_TST_DONE 0x0000fff0 +#define FLD_FLTRN_BIST_TST_DONE 0x00000008 +#define FLD_VID_BIST_TST_DONE 0x00000007 + +/*****************************************************************************/ +/* DirectIF registers definition have been moved to DIF_reg.h */ +/*****************************************************************************/ +#define MODE_CTRL 0x400 +#define FLD_AFD_PAL60_DIS 0x20000000 +#define FLD_AFD_FORCE_SECAM 0x10000000 +#define FLD_AFD_FORCE_PALNC 0x08000000 +#define FLD_AFD_FORCE_PAL 0x04000000 +#define FLD_AFD_PALM_SEL 0x03000000 +#define FLD_CKILL_MODE 0x00300000 +#define FLD_COMB_NOTCH_MODE 0x00c00000 /* bit[19:18] */ +#define FLD_CLR_LOCK_STAT 0x00020000 +#define FLD_FAST_LOCK_MD 0x00010000 +#define FLD_WCEN 0x00008000 +#define FLD_CAGCEN 0x00004000 +#define FLD_CKILLEN 0x00002000 +#define FLD_AUTO_SC_LOCK 0x00001000 +#define FLD_MAN_SC_FAST_LOCK 0x00000800 +#define FLD_INPUT_MODE 0x00000600 +#define FLD_AFD_ACQUIRE 0x00000100 +#define FLD_AFD_NTSC_SEL 0x00000080 +#define FLD_AFD_PAL_SEL 0x00000040 +#define FLD_ACFG_DIS 0x00000020 +#define FLD_SQ_PIXEL 0x00000010 +#define FLD_VID_FMT_SEL 0x0000000f + +/*****************************************************************************/ +#define OUT_CTRL1 0x404 +#define FLD_POLAR 0x7f000000 +/* Reserved [23] */ +#define FLD_RND_MODE 0x00600000 +#define FLD_VIPCLAMP_EN 0x00100000 +#define FLD_VIPBLANK_EN 0x00080000 +#define FLD_VIP_OPT_AL 0x00040000 +#define FLD_IDID0_SOURCE 0x00020000 +#define FLD_DCMODE 0x00010000 +#define FLD_CLK_GATING 0x0000c000 +#define FLD_CLK_INVERT 0x00002000 +#define FLD_HSFMT 0x00001000 +#define FLD_VALIDFMT 0x00000800 +#define FLD_ACTFMT 0x00000400 +#define FLD_SWAPRAW 0x00000200 +#define FLD_CLAMPRAW_EN 0x00000100 +#define FLD_BLUE_FIELD_EN 0x00000080 +#define FLD_BLUE_FIELD_ACT 0x00000040 +#define FLD_TASKBIT_VAL 0x00000020 +#define FLD_ANC_DATA_EN 0x00000010 +#define FLD_VBIHACTRAW_EN 0x00000008 +#define FLD_MODE10B 0x00000004 +#define FLD_OUT_MODE 0x00000003 + +/*****************************************************************************/ +#define OUT_CTRL2 0x408 +#define FLD_AUD_GRP 0xc0000000 +#define FLD_SAMPLE_RATE 0x30000000 +#define FLD_AUD_ANC_EN 0x08000000 +#define FLD_EN_C 0x04000000 +#define FLD_EN_B 0x02000000 +#define FLD_EN_A 0x01000000 +/* Reserved [23:20] */ +#define FLD_IDID1_LSB 0x000c0000 +#define FLD_IDID0_LSB 0x00030000 +#define FLD_IDID1_MSB 0x0000ff00 +#define FLD_IDID0_MSB 0x000000ff + +/*****************************************************************************/ +#define GEN_STAT 0x40c +#define FLD_VCR_DETECT 0x00800000 +#define FLD_SPECIAL_PLAY_N 0x00400000 +#define FLD_VPRES 0x00200000 +#define FLD_AGC_LOCK 0x00100000 +#define FLD_CSC_LOCK 0x00080000 +#define FLD_VLOCK 0x00040000 +#define FLD_SRC_LOCK 0x00020000 +#define FLD_HLOCK 0x00010000 +#define FLD_VSYNC_N 0x00008000 +#define FLD_SRC_FIFO_UFLOW 0x00004000 +#define FLD_SRC_FIFO_OFLOW 0x00002000 +#define FLD_FIELD 0x00001000 +#define FLD_AFD_FMT_STAT 0x00000f00 +#define FLD_MV_TYPE2_PAIR 0x00000080 +#define FLD_MV_T3CS 0x00000040 +#define FLD_MV_CS 0x00000020 +#define FLD_MV_PSP 0x00000010 +/* Reserved [3] */ +#define FLD_MV_CDAT 0x00000003 + +/*****************************************************************************/ +#define INT_STAT_MASK 0x410 +#define FLD_COMB_3D_FIFO_MSK 0x80000000 +#define FLD_WSS_DAT_AVAIL_MSK 0x40000000 +#define FLD_GS2_DAT_AVAIL_MSK 0x20000000 +#define FLD_GS1_DAT_AVAIL_MSK 0x10000000 +#define FLD_CC_DAT_AVAIL_MSK 0x08000000 +#define FLD_VPRES_CHANGE_MSK 0x04000000 +#define FLD_MV_CHANGE_MSK 0x02000000 +#define FLD_END_VBI_EVEN_MSK 0x01000000 +#define FLD_END_VBI_ODD_MSK 0x00800000 +#define FLD_FMT_CHANGE_MSK 0x00400000 +#define FLD_VSYNC_TRAIL_MSK 0x00200000 +#define FLD_HLOCK_CHANGE_MSK 0x00100000 +#define FLD_VLOCK_CHANGE_MSK 0x00080000 +#define FLD_CSC_LOCK_CHANGE_MSK 0x00040000 +#define FLD_SRC_FIFO_UFLOW_MSK 0x00020000 +#define FLD_SRC_FIFO_OFLOW_MSK 0x00010000 +#define FLD_COMB_3D_FIFO_STAT 0x00008000 +#define FLD_WSS_DAT_AVAIL_STAT 0x00004000 +#define FLD_GS2_DAT_AVAIL_STAT 0x00002000 +#define FLD_GS1_DAT_AVAIL_STAT 0x00001000 +#define FLD_CC_DAT_AVAIL_STAT 0x00000800 +#define FLD_VPRES_CHANGE_STAT 0x00000400 +#define FLD_MV_CHANGE_STAT 0x00000200 +#define FLD_END_VBI_EVEN_STAT 0x00000100 +#define FLD_END_VBI_ODD_STAT 0x00000080 +#define FLD_FMT_CHANGE_STAT 0x00000040 +#define FLD_VSYNC_TRAIL_STAT 0x00000020 +#define FLD_HLOCK_CHANGE_STAT 0x00000010 +#define FLD_VLOCK_CHANGE_STAT 0x00000008 +#define FLD_CSC_LOCK_CHANGE_STAT 0x00000004 +#define FLD_SRC_FIFO_UFLOW_STAT 0x00000002 +#define FLD_SRC_FIFO_OFLOW_STAT 0x00000001 + +/*****************************************************************************/ +#define LUMA_CTRL 0x414 +#define BRIGHTNESS_CTRL_BYTE 0x414 +#define CONTRAST_CTRL_BYTE 0x415 +#define LUMA_CTRL_BYTE_3 0x416 +#define FLD_LUMA_CORE_SEL 0x00c00000 +#define FLD_RANGE 0x00300000 +/* Reserved [19] */ +#define FLD_PEAK_EN 0x00040000 +#define FLD_PEAK_SEL 0x00030000 +#define FLD_CNTRST 0x0000ff00 +#define FLD_BRITE 0x000000ff + +/*****************************************************************************/ +#define HSCALE_CTRL 0x418 +#define FLD_HFILT 0x03000000 +#define FLD_HSCALE 0x00ffffff + +/*****************************************************************************/ +#define VSCALE_CTRL 0x41c +#define FLD_LINE_AVG_DIS 0x01000000 +/* Reserved [23:20] */ +#define FLD_VS_INTRLACE 0x00080000 +#define FLD_VFILT 0x00070000 +/* Reserved [15:13] */ +#define FLD_VSCALE 0x00001fff + +/*****************************************************************************/ +#define CHROMA_CTRL 0x420 +#define USAT_CTRL_BYTE 0x420 +#define VSAT_CTRL_BYTE 0x421 +#define HUE_CTRL_BYTE 0x422 +#define FLD_C_LPF_EN 0x20000000 +#define FLD_CHR_DELAY 0x1c000000 +#define FLD_C_CORE_SEL 0x03000000 +#define FLD_HUE 0x00ff0000 +#define FLD_VSAT 0x0000ff00 +#define FLD_USAT 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL1 0x424 +#define FLD_VBI_MD_LINE4 0xff000000 +#define FLD_VBI_MD_LINE3 0x00ff0000 +#define FLD_VBI_MD_LINE2 0x0000ff00 +#define FLD_VBI_MD_LINE1 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL2 0x428 +#define FLD_VBI_MD_LINE8 0xff000000 +#define FLD_VBI_MD_LINE7 0x00ff0000 +#define FLD_VBI_MD_LINE6 0x0000ff00 +#define FLD_VBI_MD_LINE5 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL3 0x42c +#define FLD_VBI_MD_LINE12 0xff000000 +#define FLD_VBI_MD_LINE11 0x00ff0000 +#define FLD_VBI_MD_LINE10 0x0000ff00 +#define FLD_VBI_MD_LINE9 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL4 0x430 +#define FLD_VBI_MD_LINE16 0xff000000 +#define FLD_VBI_MD_LINE15 0x00ff0000 +#define FLD_VBI_MD_LINE14 0x0000ff00 +#define FLD_VBI_MD_LINE13 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL5 0x434 +#define FLD_VBI_MD_LINE17 0x000000ff + +/*****************************************************************************/ +#define VBI_FC_CFG 0x438 +#define FLD_FC_ALT2 0xff000000 +#define FLD_FC_ALT1 0x00ff0000 +#define FLD_FC_ALT2_TYPE 0x0000f000 +#define FLD_FC_ALT1_TYPE 0x00000f00 +/* Reserved [7:1] */ +#define FLD_FC_SEARCH_MODE 0x00000001 + +/*****************************************************************************/ +#define VBI_MISC_CFG1 0x43c +#define FLD_TTX_PKTADRU 0xfff00000 +#define FLD_TTX_PKTADRL 0x000fff00 +/* Reserved [7:6] */ +#define FLD_MOJI_PACK_DIS 0x00000020 +#define FLD_VPS_DEC_DIS 0x00000010 +#define FLD_CRI_MARG_SCALE 0x0000000c +#define FLD_EDGE_RESYNC_EN 0x00000002 +#define FLD_ADAPT_SLICE_DIS 0x00000001 + +/*****************************************************************************/ +#define VBI_MISC_CFG2 0x440 +#define FLD_HAMMING_TYPE 0x0f000000 +/* Reserved [23:20] */ +#define FLD_WSS_FIFO_RST 0x00080000 +#define FLD_GS2_FIFO_RST 0x00040000 +#define FLD_GS1_FIFO_RST 0x00020000 +#define FLD_CC_FIFO_RST 0x00010000 +/* Reserved [15:12] */ +#define FLD_VBI3_SDID 0x00000f00 +#define FLD_VBI2_SDID 0x000000f0 +#define FLD_VBI1_SDID 0x0000000f + +/*****************************************************************************/ +#define VBI_PAY1 0x444 +#define FLD_GS1_FIFO_DAT 0xFF000000 +#define FLD_GS1_STAT 0x00FF0000 +#define FLD_CC_FIFO_DAT 0x0000FF00 +#define FLD_CC_STAT 0x000000FF + +/*****************************************************************************/ +#define VBI_PAY2 0x448 +#define FLD_WSS_FIFO_DAT 0xff000000 +#define FLD_WSS_STAT 0x00ff0000 +#define FLD_GS2_FIFO_DAT 0x0000ff00 +#define FLD_GS2_STAT 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST1_CFG1 0x44c +/* Reserved [31] */ +#define FLD_VBI1_CRIWIN 0x7f000000 +#define FLD_VBI1_SLICE_DIST 0x00f00000 +#define FLD_VBI1_BITINC 0x000fff00 +#define FLD_VBI1_HDELAY 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST1_CFG2 0x450 +#define FLD_VBI1_FC_LENGTH 0x1f000000 +#define FLD_VBI1_FRAME_CODE 0x00ffffff + +/*****************************************************************************/ +#define VBI_CUST1_CFG3 0x454 +#define FLD_VBI1_HAM_EN 0x80000000 +#define FLD_VBI1_FIFO_MODE 0x70000000 +#define FLD_VBI1_FORMAT_TYPE 0x0f000000 +#define FLD_VBI1_PAYLD_LENGTH 0x00ff0000 +#define FLD_VBI1_CRI_LENGTH 0x0000f000 +#define FLD_VBI1_CRI_MARGIN 0x00000f00 +#define FLD_VBI1_CRI_TIME 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST2_CFG1 0x458 +/* Reserved [31] */ +#define FLD_VBI2_CRIWIN 0x7f000000 +#define FLD_VBI2_SLICE_DIST 0x00f00000 +#define FLD_VBI2_BITINC 0x000fff00 +#define FLD_VBI2_HDELAY 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST2_CFG2 0x45c +#define FLD_VBI2_FC_LENGTH 0x1f000000 +#define FLD_VBI2_FRAME_CODE 0x00ffffff + +/*****************************************************************************/ +#define VBI_CUST2_CFG3 0x460 +#define FLD_VBI2_HAM_EN 0x80000000 +#define FLD_VBI2_FIFO_MODE 0x70000000 +#define FLD_VBI2_FORMAT_TYPE 0x0f000000 +#define FLD_VBI2_PAYLD_LENGTH 0x00ff0000 +#define FLD_VBI2_CRI_LENGTH 0x0000f000 +#define FLD_VBI2_CRI_MARGIN 0x00000f00 +#define FLD_VBI2_CRI_TIME 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST3_CFG1 0x464 +/* Reserved [31] */ +#define FLD_VBI3_CRIWIN 0x7f000000 +#define FLD_VBI3_SLICE_DIST 0x00f00000 +#define FLD_VBI3_BITINC 0x000fff00 +#define FLD_VBI3_HDELAY 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST3_CFG2 0x468 +#define FLD_VBI3_FC_LENGTH 0x1f000000 +#define FLD_VBI3_FRAME_CODE 0x00ffffff + +/*****************************************************************************/ +#define VBI_CUST3_CFG3 0x46c +#define FLD_VBI3_HAM_EN 0x80000000 +#define FLD_VBI3_FIFO_MODE 0x70000000 +#define FLD_VBI3_FORMAT_TYPE 0x0f000000 +#define FLD_VBI3_PAYLD_LENGTH 0x00ff0000 +#define FLD_VBI3_CRI_LENGTH 0x0000f000 +#define FLD_VBI3_CRI_MARGIN 0x00000f00 +#define FLD_VBI3_CRI_TIME 0x000000ff + +/*****************************************************************************/ +#define HORIZ_TIM_CTRL 0x470 +#define FLD_BGDEL_CNT 0xff000000 +/* Reserved [23:22] */ +#define FLD_HACTIVE_CNT 0x003ff000 +/* Reserved [11:10] */ +#define FLD_HBLANK_CNT 0x000003ff + +/*****************************************************************************/ +#define VERT_TIM_CTRL 0x474 +#define FLD_V656BLANK_CNT 0xff000000 +/* Reserved [23:22] */ +#define FLD_VACTIVE_CNT 0x003ff000 +/* Reserved [11:10] */ +#define FLD_VBLANK_CNT 0x000003ff + +/*****************************************************************************/ +#define SRC_COMB_CFG 0x478 +#define FLD_CCOMB_2LN_CHECK 0x80000000 +#define FLD_CCOMB_3LN_EN 0x40000000 +#define FLD_CCOMB_2LN_EN 0x20000000 +#define FLD_CCOMB_3D_EN 0x10000000 +/* Reserved [27] */ +#define FLD_LCOMB_3LN_EN 0x04000000 +#define FLD_LCOMB_2LN_EN 0x02000000 +#define FLD_LCOMB_3D_EN 0x01000000 +#define FLD_LUMA_LPF_SEL 0x00c00000 +#define FLD_UV_LPF_SEL 0x00300000 +#define FLD_BLEND_SLOPE 0x000f0000 +#define FLD_CCOMB_REDUCE_EN 0x00008000 +/* Reserved [14:10] */ +#define FLD_SRC_DECIM_RATIO 0x000003ff + +/*****************************************************************************/ +#define CHROMA_VBIOFF_CFG 0x47c +#define FLD_VBI_VOFFSET 0x1f000000 +/* Reserved [23:20] */ +#define FLD_SC_STEP 0x000fffff + +/*****************************************************************************/ +#define FIELD_COUNT 0x480 +#define FLD_FIELD_COUNT_FLD 0x000003ff + +/*****************************************************************************/ +#define MISC_TIM_CTRL 0x484 +#define FLD_DEBOUNCE_COUNT 0xc0000000 +#define FLD_VT_LINE_CNT_HYST 0x30000000 +/* Reserved [27] */ +#define FLD_AFD_STAT 0x07ff0000 +#define FLD_VPRES_VERT_EN 0x00008000 +/* Reserved [14:12] */ +#define FLD_HR32 0x00000800 +#define FLD_TDALGN 0x00000400 +#define FLD_TDFIELD 0x00000200 +/* Reserved [8:6] */ +#define FLD_TEMPDEC 0x0000003f + +/*****************************************************************************/ +#define DFE_CTRL1 0x488 +#define FLD_CLAMP_AUTO_EN 0x80000000 +#define FLD_AGC_AUTO_EN 0x40000000 +#define FLD_VGA_CRUSH_EN 0x20000000 +#define FLD_VGA_AUTO_EN 0x10000000 +#define FLD_VBI_GATE_EN 0x08000000 +#define FLD_CLAMP_LEVEL 0x07000000 +/* Reserved [23:22] */ +#define FLD_CLAMP_SKIP_CNT 0x00300000 +#define FLD_AGC_GAIN 0x000fff00 +/* Reserved [7:6] */ +#define FLD_VGA_GAIN 0x0000003f + +/*****************************************************************************/ +#define DFE_CTRL2 0x48c +#define FLD_VGA_ACQUIRE_RANGE 0x00ff0000 +#define FLD_VGA_TRACK_RANGE 0x0000ff00 +#define FLD_VGA_SYNC 0x000000ff + +/*****************************************************************************/ +#define DFE_CTRL3 0x490 +#define FLD_BP_PERCENT 0xff000000 +#define FLD_DFT_THRESHOLD 0x00ff0000 +/* Reserved [15:12] */ +#define FLD_SYNC_WIDTH_SEL 0x00000600 +#define FLD_BP_LOOP_GAIN 0x00000300 +#define FLD_SYNC_LOOP_GAIN 0x000000c0 +/* Reserved [5:4] */ +#define FLD_AGC_LOOP_GAIN 0x0000000c +#define FLD_DCC_LOOP_GAIN 0x00000003 + +/*****************************************************************************/ +#define PLL_CTRL 0x494 +#define FLD_PLL_KD 0xff000000 +#define FLD_PLL_KI 0x00ff0000 +#define FLD_PLL_MAX_OFFSET 0x0000ffff + +/*****************************************************************************/ +#define HTL_CTRL 0x498 +/* Reserved [31:24] */ +#define FLD_AUTO_LOCK_SPD 0x00080000 +#define FLD_MAN_FAST_LOCK 0x00040000 +#define FLD_HTL_15K_EN 0x00020000 +#define FLD_HTL_500K_EN 0x00010000 +#define FLD_HTL_KD 0x0000ff00 +#define FLD_HTL_KI 0x000000ff + +/*****************************************************************************/ +#define COMB_CTRL 0x49c +#define FLD_COMB_PHASE_LIMIT 0xff000000 +#define FLD_CCOMB_ERR_LIMIT 0x00ff0000 +#define FLD_LUMA_THRESHOLD 0x0000ff00 +#define FLD_LCOMB_ERR_LIMIT 0x000000ff + +/*****************************************************************************/ +#define CRUSH_CTRL 0x4a0 +#define FLD_WTW_EN 0x00400000 +#define FLD_CRUSH_FREQ 0x00200000 +#define FLD_MAJ_SEL_EN 0x00100000 +#define FLD_MAJ_SEL 0x000c0000 +/* Reserved [17:15] */ +#define FLD_SYNC_TIP_REDUCE 0x00007e00 +/* Reserved [8:6] */ +#define FLD_SYNC_TIP_INC 0x0000003f + +/*****************************************************************************/ +#define SOFT_RST_CTRL 0x4a4 +#define FLD_VD_SOFT_RST 0x00008000 +/* Reserved [14:12] */ +#define FLD_REG_RST_MSK 0x00000800 +#define FLD_VOF_RST_MSK 0x00000400 +#define FLD_MVDET_RST_MSK 0x00000200 +#define FLD_VBI_RST_MSK 0x00000100 +#define FLD_SCALE_RST_MSK 0x00000080 +#define FLD_CHROMA_RST_MSK 0x00000040 +#define FLD_LUMA_RST_MSK 0x00000020 +#define FLD_VTG_RST_MSK 0x00000010 +#define FLD_YCSEP_RST_MSK 0x00000008 +#define FLD_SRC_RST_MSK 0x00000004 +#define FLD_DFE_RST_MSK 0x00000002 +/* Reserved [0] */ + +/*****************************************************************************/ +#define MV_DT_CTRL1 0x4a8 +/* Reserved [31:29] */ +#define FLD_PSP_STOP_LINE 0x1f000000 +/* Reserved [23:21] */ +#define FLD_PSP_STRT_LINE 0x001f0000 +/* Reserved [15] */ +#define FLD_PSP_LLIMW 0x00007f00 +/* Reserved [7] */ +#define FLD_PSP_ULIMW 0x0000007f + +/*****************************************************************************/ +#define MV_DT_CTRL2 0x4aC +#define FLD_CS_STOPWIN 0xff000000 +#define FLD_CS_STRTWIN 0x00ff0000 +#define FLD_CS_WIDTH 0x0000ff00 +#define FLD_PSP_SPEC_VAL 0x000000ff + +/*****************************************************************************/ +#define MV_DT_CTRL3 0x4B0 +#define FLD_AUTO_RATE_DIS 0x80000000 +#define FLD_HLOCK_DIS 0x40000000 +#define FLD_SEL_FIELD_CNT 0x20000000 +#define FLD_CS_TYPE2_SEL 0x10000000 +#define FLD_CS_LINE_THRSH_SEL 0x08000000 +#define FLD_CS_ATHRESH_SEL 0x04000000 +#define FLD_PSP_SPEC_SEL 0x02000000 +#define FLD_PSP_LINES_SEL 0x01000000 +#define FLD_FIELD_CNT 0x00f00000 +#define FLD_CS_TYPE2_CNT 0x000fc000 +#define FLD_CS_LINE_CNT 0x00003f00 +#define FLD_CS_ATHRESH_LEV 0x000000ff + +/*****************************************************************************/ +#define CHIP_VERSION 0x4b4 +/* Cx231xx redefine */ +#define VERSION 0x4b4 +#define FLD_REV_ID 0x000000ff + +/*****************************************************************************/ +#define MISC_DIAG_CTRL 0x4b8 +/* Reserved [31:24] */ +#define FLD_SC_CONVERGE_THRESH 0x00ff0000 +#define FLD_CCOMB_ERR_LIMIT_3D 0x0000ff00 +#define FLD_LCOMB_ERR_LIMIT_3D 0x000000ff + +/*****************************************************************************/ +#define VBI_PASS_CTRL 0x4bc +#define FLD_VBI_PASS_MD 0x00200000 +#define FLD_VBI_SETUP_DIS 0x00100000 +#define FLD_PASS_LINE_CTRL 0x000fffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define VCR_DET_CTRL 0x4c0 +#define FLD_EN_FIELD_PHASE_DET 0x80000000 +#define FLD_EN_HEAD_SW_DET 0x40000000 +#define FLD_FIELD_PHASE_LENGTH 0x01ff0000 +/* Reserved [29:25] */ +#define FLD_FIELD_PHASE_DELAY 0x0000ff00 +#define FLD_FIELD_PHASE_LIMIT 0x000000f0 +#define FLD_HEAD_SW_DET_LIMIT 0x0000000f + +/*****************************************************************************/ +#define DL_CTL 0x800 +#define DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */ +#define DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */ +#define DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */ +#define DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */ +/* Reserved [31:5] */ +#define FLD_START_8051 0x10000000 +#define FLD_DL_ENABLE 0x08000000 +#define FLD_DL_AUTO_INC 0x04000000 +#define FLD_DL_MAP 0x03000000 + +/*****************************************************************************/ +#define STD_DET_STATUS 0x804 +#define FLD_SPARE_STATUS1 0xff000000 +#define FLD_SPARE_STATUS0 0x00ff0000 +#define FLD_MOD_DET_STATUS1 0x0000ff00 +#define FLD_MOD_DET_STATUS0 0x000000ff + +/*****************************************************************************/ +#define AUD_BUILD_NUM 0x806 +#define AUD_VER_NUM 0x807 +#define STD_DET_CTL 0x808 +#define STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */ +#define STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */ +#define FLD_SPARE_CTL0 0xff000000 +#define FLD_DIS_DBX 0x00800000 +#define FLD_DIS_BTSC 0x00400000 +#define FLD_DIS_NICAM_A2 0x00200000 +#define FLD_VIDEO_PRESENT 0x00100000 +#define FLD_DW8051_VIDEO_FORMAT 0x000f0000 +#define FLD_PREF_DEC_MODE 0x0000ff00 +#define FLD_AUD_CONFIG 0x000000ff + +/*****************************************************************************/ +#define DW8051_INT 0x80c +#define FLD_VIDEO_PRESENT_CHANGE 0x80000000 +#define FLD_VIDEO_CHANGE 0x40000000 +#define FLD_RDS_READY 0x20000000 +#define FLD_AC97_INT 0x10000000 +#define FLD_NICAM_BIT_ERROR_TOO_HIGH 0x08000000 +#define FLD_NICAM_LOCK 0x04000000 +#define FLD_NICAM_UNLOCK 0x02000000 +#define FLD_DFT4_TH_CMP 0x01000000 +/* Reserved [23:22] */ +#define FLD_LOCK_IND_INT 0x00200000 +#define FLD_DFT3_TH_CMP 0x00100000 +#define FLD_DFT2_TH_CMP 0x00080000 +#define FLD_DFT1_TH_CMP 0x00040000 +#define FLD_FM2_DFT_TH_CMP 0x00020000 +#define FLD_FM1_DFT_TH_CMP 0x00010000 +#define FLD_VIDEO_PRESENT_EN 0x00008000 +#define FLD_VIDEO_CHANGE_EN 0x00004000 +#define FLD_RDS_READY_EN 0x00002000 +#define FLD_AC97_INT_EN 0x00001000 +#define FLD_NICAM_BIT_ERROR_TOO_HIGH_EN 0x00000800 +#define FLD_NICAM_LOCK_EN 0x00000400 +#define FLD_NICAM_UNLOCK_EN 0x00000200 +#define FLD_DFT4_TH_CMP_EN 0x00000100 +/* Reserved [7] */ +#define FLD_DW8051_INT6_CTL1 0x00000040 +#define FLD_DW8051_INT5_CTL1 0x00000020 +#define FLD_DW8051_INT4_CTL1 0x00000010 +#define FLD_DW8051_INT3_CTL1 0x00000008 +#define FLD_DW8051_INT2_CTL1 0x00000004 +#define FLD_DW8051_INT1_CTL1 0x00000002 +#define FLD_DW8051_INT0_CTL1 0x00000001 + +/*****************************************************************************/ +#define GENERAL_CTL 0x810 +#define FLD_RDS_INT 0x80000000 +#define FLD_NBER_INT 0x40000000 +#define FLD_NLL_INT 0x20000000 +#define FLD_IFL_INT 0x10000000 +#define FLD_FDL_INT 0x08000000 +#define FLD_AFC_INT 0x04000000 +#define FLD_AMC_INT 0x02000000 +#define FLD_AC97_INT_CTL 0x01000000 +#define FLD_RDS_INT_DIS 0x00800000 +#define FLD_NBER_INT_DIS 0x00400000 +#define FLD_NLL_INT_DIS 0x00200000 +#define FLD_IFL_INT_DIS 0x00100000 +#define FLD_FDL_INT_DIS 0x00080000 +#define FLD_FC_INT_DIS 0x00040000 +#define FLD_AMC_INT_DIS 0x00020000 +#define FLD_AC97_INT_DIS 0x00010000 +#define FLD_REV_NUM 0x0000ff00 +/* Reserved [7:5] */ +#define FLD_DBX_SOFT_RESET_REG 0x00000010 +#define FLD_AD_SOFT_RESET_REG 0x00000008 +#define FLD_SRC_SOFT_RESET_REG 0x00000004 +#define FLD_CDMOD_SOFT_RESET 0x00000002 +#define FLD_8051_SOFT_RESET 0x00000001 + +/*****************************************************************************/ +#define AAGC_CTL 0x814 +#define FLD_AFE_12DB_EN 0x80000000 +#define FLD_AAGC_DEFAULT_EN 0x40000000 +#define FLD_AAGC_DEFAULT 0x3f000000 +/* Reserved [23] */ +#define FLD_AAGC_GAIN 0x00600000 +#define FLD_AAGC_TH 0x001f0000 +/* Reserved [15:14] */ +#define FLD_AAGC_HYST2 0x00003f00 +/* Reserved [7:6] */ +#define FLD_AAGC_HYST1 0x0000003f + +/*****************************************************************************/ +#define IF_SRC_CTL 0x818 +#define FLD_DBX_BYPASS 0x80000000 +/* Reserved [30:25] */ +#define FLD_IF_SRC_MODE 0x01000000 +/* Reserved [23:18] */ +#define FLD_IF_SRC_PHASE_INC 0x0001ffff + +/*****************************************************************************/ +#define ANALOG_DEMOD_CTL 0x81c +#define FLD_ROT1_PHACC_PROG 0xffff0000 +/* Reserved [15] */ +#define FLD_FM1_DELAY_FIX 0x00007000 +#define FLD_PDF4_SHIFT 0x00000c00 +#define FLD_PDF3_SHIFT 0x00000300 +#define FLD_PDF2_SHIFT 0x000000c0 +#define FLD_PDF1_SHIFT 0x00000030 +#define FLD_FMBYPASS_MODE2 0x00000008 +#define FLD_FMBYPASS_MODE1 0x00000004 +#define FLD_NICAM_MODE 0x00000002 +#define FLD_BTSC_FMRADIO_MODE 0x00000001 + +/*****************************************************************************/ +#define ROT_FREQ_CTL 0x820 +#define FLD_ROT3_PHACC_PROG 0xffff0000 +#define FLD_ROT2_PHACC_PROG 0x0000ffff + +/*****************************************************************************/ +#define FM_CTL 0x824 +#define FLD_FM2_DC_FB_SHIFT 0xf0000000 +#define FLD_FM2_DC_INT_SHIFT 0x0f000000 +#define FLD_FM2_AFC_RESET 0x00800000 +#define FLD_FM2_DC_PASS_IN 0x00400000 +#define FLD_FM2_DAGC_SHIFT 0x00380000 +#define FLD_FM2_CORDIC_SHIFT 0x00070000 +#define FLD_FM1_DC_FB_SHIFT 0x0000f000 +#define FLD_FM1_DC_INT_SHIFT 0x00000f00 +#define FLD_FM1_AFC_RESET 0x00000080 +#define FLD_FM1_DC_PASS_IN 0x00000040 +#define FLD_FM1_DAGC_SHIFT 0x00000038 +#define FLD_FM1_CORDIC_SHIFT 0x00000007 + +/*****************************************************************************/ +#define LPF_PDF_CTL 0x828 +/* Reserved [31:30] */ +#define FLD_LPF32_SHIFT1 0x30000000 +#define FLD_LPF32_SHIFT2 0x0c000000 +#define FLD_LPF160_SHIFTA 0x03000000 +#define FLD_LPF160_SHIFTB 0x00c00000 +#define FLD_LPF160_SHIFTC 0x00300000 +#define FLD_LPF32_COEF_SEL2 0x000c0000 +#define FLD_LPF32_COEF_SEL1 0x00030000 +#define FLD_LPF160_COEF_SELC 0x0000c000 +#define FLD_LPF160_COEF_SELB 0x00003000 +#define FLD_LPF160_COEF_SELA 0x00000c00 +#define FLD_LPF160_IN_EN_REG 0x00000300 +#define FLD_PDF4_PDF_SEL 0x000000c0 +#define FLD_PDF3_PDF_SEL 0x00000030 +#define FLD_PDF2_PDF_SEL 0x0000000c +#define FLD_PDF1_PDF_SEL 0x00000003 + +/*****************************************************************************/ +#define DFT1_CTL1 0x82c +#define FLD_DFT1_DWELL 0xffff0000 +#define FLD_DFT1_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT1_CTL2 0x830 +#define FLD_DFT1_THRESHOLD 0xffffff00 +#define FLD_DFT1_CMP_CTL 0x00000080 +#define FLD_DFT1_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT1_START 0x00000001 + +/*****************************************************************************/ +#define DFT1_STATUS 0x834 +#define FLD_DFT1_DONE 0x80000000 +#define FLD_DFT1_TH_CMP_STAT 0x40000000 +#define FLD_DFT1_RESULT 0x3fffffff + +/*****************************************************************************/ +#define DFT2_CTL1 0x838 +#define FLD_DFT2_DWELL 0xffff0000 +#define FLD_DFT2_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT2_CTL2 0x83C +#define FLD_DFT2_THRESHOLD 0xffffff00 +#define FLD_DFT2_CMP_CTL 0x00000080 +#define FLD_DFT2_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT2_START 0x00000001 + +/*****************************************************************************/ +#define DFT2_STATUS 0x840 +#define FLD_DFT2_DONE 0x80000000 +#define FLD_DFT2_TH_CMP_STAT 0x40000000 +#define FLD_DFT2_RESULT 0x3fffffff + +/*****************************************************************************/ +#define DFT3_CTL1 0x844 +#define FLD_DFT3_DWELL 0xffff0000 +#define FLD_DFT3_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT3_CTL2 0x848 +#define FLD_DFT3_THRESHOLD 0xffffff00 +#define FLD_DFT3_CMP_CTL 0x00000080 +#define FLD_DFT3_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT3_START 0x00000001 + +/*****************************************************************************/ +#define DFT3_STATUS 0x84c +#define FLD_DFT3_DONE 0x80000000 +#define FLD_DFT3_TH_CMP_STAT 0x40000000 +#define FLD_DFT3_RESULT 0x3fffffff + +/*****************************************************************************/ +#define DFT4_CTL1 0x850 +#define FLD_DFT4_DWELL 0xffff0000 +#define FLD_DFT4_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT4_CTL2 0x854 +#define FLD_DFT4_THRESHOLD 0xffffff00 +#define FLD_DFT4_CMP_CTL 0x00000080 +#define FLD_DFT4_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT4_START 0x00000001 + +/*****************************************************************************/ +#define DFT4_STATUS 0x858 +#define FLD_DFT4_DONE 0x80000000 +#define FLD_DFT4_TH_CMP_STAT 0x40000000 +#define FLD_DFT4_RESULT 0x3fffffff + +/*****************************************************************************/ +#define AM_MTS_DET 0x85c +#define FLD_AM_MTS_MODE 0x80000000 +/* Reserved [30:26] */ +#define FLD_AM_SUB 0x02000000 +#define FLD_AM_GAIN_EN 0x01000000 +/* Reserved [23:16] */ +#define FLD_AMMTS_GAIN_SCALE 0x0000e000 +#define FLD_MTS_PDF_SHIFT 0x00001800 +#define FLD_AM_REG_GAIN 0x00000700 +#define FLD_AGC_REF 0x000000ff + +/*****************************************************************************/ +#define ANALOG_MUX_CTL 0x860 +/* Reserved [31:29] */ +#define FLD_MUX21_SEL 0x10000000 +#define FLD_MUX20_SEL 0x08000000 +#define FLD_MUX19_SEL 0x04000000 +#define FLD_MUX18_SEL 0x02000000 +#define FLD_MUX17_SEL 0x01000000 +#define FLD_MUX16_SEL 0x00800000 +#define FLD_MUX15_SEL 0x00400000 +#define FLD_MUX14_SEL 0x00300000 +#define FLD_MUX13_SEL 0x000C0000 +#define FLD_MUX12_SEL 0x00020000 +#define FLD_MUX11_SEL 0x00018000 +#define FLD_MUX10_SEL 0x00004000 +#define FLD_MUX9_SEL 0x00002000 +#define FLD_MUX8_SEL 0x00001000 +#define FLD_MUX7_SEL 0x00000800 +#define FLD_MUX6_SEL 0x00000600 +#define FLD_MUX5_SEL 0x00000100 +#define FLD_MUX4_SEL 0x000000c0 +#define FLD_MUX3_SEL 0x00000030 +#define FLD_MUX2_SEL 0x0000000c +#define FLD_MUX1_SEL 0x00000003 + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL1 0x864 +#define DIG_PLL_CTL1 0x864 + +#define FLD_PLL_STATUS 0x07000000 +#define FLD_BANDWIDTH_SELECT 0x00030000 +#define FLD_PLL_SHIFT_REG 0x00007000 +#define FLD_PHASE_SHIFT 0x000007ff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL2 0x868 +#define DIG_PLL_CTL2 0x868 +#define FLD_PLL_UNLOCK_THR 0xff000000 +#define FLD_PLL_LOCK_THR 0x00ff0000 +/* Reserved [15:8] */ +#define FLD_AM_PDF_SEL2 0x000000c0 +#define FLD_AM_PDF_SEL1 0x00000030 +#define FLD_DPLL_FSM_CTRL 0x0000000c +/* Reserved [1] */ +#define FLD_PLL_PILOT_DET 0x00000001 + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL3 0x86c +#define DIG_PLL_CTL3 0x86c +#define FLD_DISABLE_LOOP 0x01000000 +#define FLD_A1_DS1_SEL 0x000c0000 +#define FLD_A1_DS2_SEL 0x00030000 +#define FLD_A1_KI 0x0000ff00 +#define FLD_A1_KD 0x000000ff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL4 0x870 +#define DIG_PLL_CTL4 0x870 +#define FLD_A2_DS1_SEL 0x000c0000 +#define FLD_A2_DS2_SEL 0x00030000 +#define FLD_A2_KI 0x0000ff00 +#define FLD_A2_KD 0x000000ff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL5 0x874 +#define DIG_PLL_CTL5 0x874 +#define FLD_TRK_DS1_SEL 0x000c0000 +#define FLD_TRK_DS2_SEL 0x00030000 +#define FLD_TRK_KI 0x0000ff00 +#define FLD_TRK_KD 0x000000ff + +/*****************************************************************************/ +#define DEEMPH_GAIN_CTL 0x878 +#define FLD_DEEMPH2_GAIN 0xFFFF0000 +#define FLD_DEEMPH1_GAIN 0x0000FFFF + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_COEFF1 0x87c +#define DEEMPH_COEF1 0x87c +#define FLD_DEEMPH_B0 0xffff0000 +#define FLD_DEEMPH_A0 0x0000ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_COEFF2 0x880 +#define DEEMPH_COEF2 0x880 +#define FLD_DEEMPH_B1 0xFFFF0000 +#define FLD_DEEMPH_A1 0x0000FFFF + +/*****************************************************************************/ +#define DBX1_CTL1 0x884 +#define FLD_DBX1_WBE_GAIN 0xffff0000 +#define FLD_DBX1_IN_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX1_CTL2 0x888 +#define FLD_DBX1_SE_BYPASS 0xffff0000 +#define FLD_DBX1_SE_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX1_RMS_SE 0x88C +#define FLD_DBX1_RMS_WBE 0xffff0000 +#define FLD_DBX1_RMS_SE_FLD 0x0000ffff + +/*****************************************************************************/ +#define DBX2_CTL1 0x890 +#define FLD_DBX2_WBE_GAIN 0xffff0000 +#define FLD_DBX2_IN_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX2_CTL2 0x894 +#define FLD_DBX2_SE_BYPASS 0xffff0000 +#define FLD_DBX2_SE_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX2_RMS_SE 0x898 +#define FLD_DBX2_RMS_WBE 0xffff0000 +#define FLD_DBX2_RMS_SE_FLD 0x0000ffff + +/*****************************************************************************/ +#define AM_FM_DIFF 0x89c +/* Reserved [31] */ +#define FLD_FM_DIFF_OUT 0x7fff0000 +/* Reserved [15] */ +#define FLD_AM_DIFF_OUT 0x00007fff + +/*****************************************************************************/ +#define NICAM_FAW 0x8a0 +#define FLD_FAWDETWINEND 0xFc000000 +#define FLD_FAWDETWINSTR 0x03ff0000 +/* Reserved [15:12] */ +#define FLD_FAWDETTHRSHLD3 0x00000f00 +#define FLD_FAWDETTHRSHLD2 0x000000f0 +#define FLD_FAWDETTHRSHLD1 0x0000000f + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_GAIN 0x8a4 +#define NICAM_DEEMPHGAIN 0x8a4 +/* Reserved [31:18] */ +#define FLD_DEEMPHGAIN 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_NUMER1 0x8a8 +#define NICAM_DEEMPHNUMER1 0x8a8 +/* Reserved [31:18] */ +#define FLD_DEEMPHNUMER1 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_NUMER2 0x8ac +#define NICAM_DEEMPHNUMER2 0x8ac +/* Reserved [31:18] */ +#define FLD_DEEMPHNUMER2 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_DENOM1 0x8b0 +#define NICAM_DEEMPHDENOM1 0x8b0 +/* Reserved [31:18] */ +#define FLD_DEEMPHDENOM1 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_DENOM2 0x8b4 +#define NICAM_DEEMPHDENOM2 0x8b4 +/* Reserved [31:18] */ +#define FLD_DEEMPHDENOM2 0x0003ffff + +/*****************************************************************************/ +#define NICAM_ERRLOG_CTL1 0x8B8 +/* Reserved [31:28] */ +#define FLD_ERRINTRPTTHSHLD1 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_ERRLOGPERIOD 0x00000fff + +/*****************************************************************************/ +#define NICAM_ERRLOG_CTL2 0x8bc +/* Reserved [31:28] */ +#define FLD_ERRINTRPTTHSHLD3 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_ERRINTRPTTHSHLD2 0x00000fff + +/*****************************************************************************/ +#define NICAM_ERRLOG_STS1 0x8c0 +/* Reserved [31:28] */ +#define FLD_ERRLOG2 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_ERRLOG1 0x00000fff + +/*****************************************************************************/ +#define NICAM_ERRLOG_STS2 0x8c4 +/* Reserved [31:12] */ +#define FLD_ERRLOG3 0x00000fff + +/*****************************************************************************/ +#define NICAM_STATUS 0x8c8 +/* Reserved [31:20] */ +#define FLD_NICAM_CIB 0x000c0000 +#define FLD_NICAM_LOCK_STAT 0x00020000 +#define FLD_NICAM_MUTE 0x00010000 +#define FLD_NICAMADDIT_DATA 0x0000ffe0 +#define FLD_NICAMCNTRL 0x0000001f + +/*****************************************************************************/ +#define DEMATRIX_CTL 0x8cc +#define FLD_AC97_IN_SHIFT 0xf0000000 +#define FLD_I2S_IN_SHIFT 0x0f000000 +#define FLD_DEMATRIX_SEL_CTL 0x00ff0000 +/* Reserved [15:11] */ +#define FLD_DMTRX_BYPASS 0x00000400 +#define FLD_DEMATRIX_MODE 0x00000300 +/* Reserved [7:6] */ +#define FLD_PH_DBX_SEL 0x00000020 +#define FLD_PH_CH_SEL 0x00000010 +#define FLD_PHASE_FIX 0x0000000f + +/*****************************************************************************/ +#define PATH1_CTL1 0x8d0 +/* Reserved [31:29] */ +#define FLD_PATH1_MUTE_CTL 0x1f000000 +/* Reserved [23:22] */ +#define FLD_PATH1_AVC_CG 0x00300000 +#define FLD_PATH1_AVC_RT 0x000f0000 +#define FLD_PATH1_AVC_AT 0x0000f000 +#define FLD_PATH1_AVC_STEREO 0x00000800 +#define FLD_PATH1_AVC_CR 0x00000700 +#define FLD_PATH1_AVC_RMS_CON 0x000000f0 +#define FLD_PATH1_SEL_CTL 0x0000000f + +/*****************************************************************************/ +#define PATH1_VOL_CTL 0x8d4 +#define FLD_PATH1_AVC_THRESHOLD 0x7fff0000 +#define FLD_PATH1_BAL_LEFT 0x00008000 +#define FLD_PATH1_BAL_LEVEL 0x00007f00 +#define FLD_PATH1_VOLUME 0x000000ff + +/*****************************************************************************/ +#define PATH1_EQ_CTL 0x8d8 +/* Reserved [31:30] */ +#define FLD_PATH1_EQ_TREBLE_VOL 0x3f000000 +/* Reserved [23:22] */ +#define FLD_PATH1_EQ_MID_VOL 0x003f0000 +/* Reserved [15:14] */ +#define FLD_PATH1_EQ_BASS_VOL 0x00003f00 +/* Reserved [7:1] */ +#define FLD_PATH1_EQ_BAND_SEL 0x00000001 + +/*****************************************************************************/ +#define PATH1_SC_CTL 0x8dc +#define FLD_PATH1_SC_THRESHOLD 0x7fff0000 +#define FLD_PATH1_SC_RT 0x0000f000 +#define FLD_PATH1_SC_AT 0x00000f00 +#define FLD_PATH1_SC_STEREO 0x00000080 +#define FLD_PATH1_SC_CR 0x00000070 +#define FLD_PATH1_SC_RMS_CON 0x0000000f + +/*****************************************************************************/ +#define PATH2_CTL1 0x8e0 +/* Reserved [31:26] */ +#define FLD_PATH2_MUTE_CTL 0x03000000 +/* Reserved [23:22] */ +#define FLD_PATH2_AVC_CG 0x00300000 +#define FLD_PATH2_AVC_RT 0x000f0000 +#define FLD_PATH2_AVC_AT 0x0000f000 +#define FLD_PATH2_AVC_STEREO 0x00000800 +#define FLD_PATH2_AVC_CR 0x00000700 +#define FLD_PATH2_AVC_RMS_CON 0x000000f0 +#define FLD_PATH2_SEL_CTL 0x0000000f + +/*****************************************************************************/ +#define PATH2_VOL_CTL 0x8e4 +#define FLD_PATH2_AVC_THRESHOLD 0xffff0000 +#define FLD_PATH2_BAL_LEFT 0x00008000 +#define FLD_PATH2_BAL_LEVEL 0x00007f00 +#define FLD_PATH2_VOLUME 0x000000ff + +/*****************************************************************************/ +#define PATH2_EQ_CTL 0x8e8 +/* Reserved [31:30] */ +#define FLD_PATH2_EQ_TREBLE_VOL 0x3f000000 +/* Reserved [23:22] */ +#define FLD_PATH2_EQ_MID_VOL 0x003f0000 +/* Reserved [15:14] */ +#define FLD_PATH2_EQ_BASS_VOL 0x00003f00 +/* Reserved [7:1] */ +#define FLD_PATH2_EQ_BAND_SEL 0x00000001 + +/*****************************************************************************/ +#define PATH2_SC_CTL 0x8eC +#define FLD_PATH2_SC_THRESHOLD 0xffff0000 +#define FLD_PATH2_SC_RT 0x0000f000 +#define FLD_PATH2_SC_AT 0x00000f00 +#define FLD_PATH2_SC_STEREO 0x00000080 +#define FLD_PATH2_SC_CR 0x00000070 +#define FLD_PATH2_SC_RMS_CON 0x0000000f + +/*****************************************************************************/ +#define SRC_CTL 0x8f0 +#define FLD_SRC_STATUS 0xffffff00 +#define FLD_FIFO_LF_EN 0x000000fc +#define FLD_BYPASS_LI 0x00000002 +#define FLD_BYPASS_PF 0x00000001 + +/*****************************************************************************/ +#define SRC_LF_COEF 0x8f4 +#define FLD_LOOP_FILTER_COEF2 0xffff0000 +#define FLD_LOOP_FILTER_COEF1 0x0000ffff + +/*****************************************************************************/ +#define SRC1_CTL 0x8f8 +/* Reserved [31:28] */ +#define FLD_SRC1_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC1_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC2_CTL 0x8fc +/* Reserved [31:28] */ +#define FLD_SRC2_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC2_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC3_CTL 0x900 +/* Reserved [31:28] */ +#define FLD_SRC3_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC3_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC4_CTL 0x904 +/* Reserved [31:28] */ +#define FLD_SRC4_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC4_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC5_CTL 0x908 +/* Reserved [31:28] */ +#define FLD_SRC5_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC5_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC6_CTL 0x90c +/* Reserved [31:28] */ +#define FLD_SRC6_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC6_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define BAND_OUT_SEL 0x910 +#define FLD_SRC6_IN_SEL 0xc0000000 +#define FLD_SRC6_CLK_SEL 0x30000000 +#define FLD_SRC5_IN_SEL 0x0c000000 +#define FLD_SRC5_CLK_SEL 0x03000000 +#define FLD_SRC4_IN_SEL 0x00c00000 +#define FLD_SRC4_CLK_SEL 0x00300000 +#define FLD_SRC3_IN_SEL 0x000c0000 +#define FLD_SRC3_CLK_SEL 0x00030000 +#define FLD_BASEBAND_BYPASS_CTL 0x0000ff00 +#define FLD_AC97_SRC_SEL 0x000000c0 +#define FLD_I2S_SRC_SEL 0x00000030 +#define FLD_PARALLEL2_SRC_SEL 0x0000000c +#define FLD_PARALLEL1_SRC_SEL 0x00000003 + +/*****************************************************************************/ +#define I2S_IN_CTL 0x914 +/* Reserved [31:11] */ +#define FLD_I2S_UP2X_BW20K 0x00000400 +#define FLD_I2S_UP2X_BYPASS 0x00000200 +#define FLD_I2S_IN_MASTER_MODE 0x00000100 +#define FLD_I2S_IN_SONY_MODE 0x00000080 +#define FLD_I2S_IN_RIGHT_JUST 0x00000040 +#define FLD_I2S_IN_WS_SEL 0x00000020 +#define FLD_I2S_IN_BCN_DEL 0x0000001f + +/*****************************************************************************/ +#define I2S_OUT_CTL 0x918 +/* Reserved [31:17] */ +#define FLD_I2S_OUT_SOFT_RESET_EN 0x00010000 +/* Reserved [15:9] */ +#define FLD_I2S_OUT_MASTER_MODE 0x00000100 +#define FLD_I2S_OUT_SONY_MODE 0x00000080 +#define FLD_I2S_OUT_RIGHT_JUST 0x00000040 +#define FLD_I2S_OUT_WS_SEL 0x00000020 +#define FLD_I2S_OUT_BCN_DEL 0x0000001f + +/*****************************************************************************/ +#define AC97_CTL 0x91c +/* Reserved [31:26] */ +#define FLD_AC97_UP2X_BW20K 0x02000000 +#define FLD_AC97_UP2X_BYPASS 0x01000000 +/* Reserved [23:17] */ +#define FLD_AC97_RST_ACL 0x00010000 +/* Reserved [15:9] */ +#define FLD_AC97_WAKE_UP_SYNC 0x00000100 +/* Reserved [7:1] */ +#define FLD_AC97_SHUTDOWN 0x00000001 + +/* Cx231xx redefine */ +#define QPSK_IAGC_CTL1 0x94c +#define QPSK_IAGC_CTL2 0x950 +#define QPSK_FEPR_FREQ 0x954 +#define QPSK_BTL_CTL1 0x958 +#define QPSK_BTL_CTL2 0x95c +#define QPSK_CTL_CTL1 0x960 +#define QPSK_CTL_CTL2 0x964 +#define QPSK_MF_FAGC_CTL 0x968 +#define QPSK_EQ_CTL 0x96c +#define QPSK_LOCK_CTL 0x970 + +/*****************************************************************************/ +#define FM1_DFT_CTL 0x9a8 +#define FLD_FM1_DFT_THRESHOLD 0xffff0000 +/* Reserved [15:8] */ +#define FLD_FM1_DFT_CMP_CTL 0x00000080 +#define FLD_FM1_DFT_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_FM1_DFT_START 0x00000001 + +/*****************************************************************************/ +#define FM1_DFT_STATUS 0x9ac +#define FLD_FM1_DFT_DONE 0x80000000 +/* Reserved [30:19] */ +#define FLD_FM_DFT_TH_CMP 0x00040000 +#define FLD_FM1_DFT 0x0003ffff + +/*****************************************************************************/ +#define FM2_DFT_CTL 0x9b0 +#define FLD_FM2_DFT_THRESHOLD 0xffff0000 +/* Reserved [15:8] */ +#define FLD_FM2_DFT_CMP_CTL 0x00000080 +#define FLD_FM2_DFT_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_FM2_DFT_START 0x00000001 + +/*****************************************************************************/ +#define FM2_DFT_STATUS 0x9b4 +#define FLD_FM2_DFT_DONE 0x80000000 +/* Reserved [30:19] */ +#define FLD_FM2_DFT_TH_CMP_STAT 0x00040000 +#define FLD_FM2_DFT 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define AAGC_STATUS_REG 0x9b8 +#define AAGC_STATUS 0x9b8 +/* Reserved [31:27] */ +#define FLD_FM2_DAGC_OUT 0x07000000 +/* Reserved [23:19] */ +#define FLD_FM1_DAGC_OUT 0x00070000 +/* Reserved [15:6] */ +#define FLD_AFE_VGA_OUT 0x0000003f + +/*****************************************************************************/ +#define MTS_GAIN_STATUS 0x9bc +/* Reserved [31:14] */ +#define FLD_MTS_GAIN 0x00003fff + +#define RDS_OUT 0x9c0 +#define FLD_RDS_Q 0xffff0000 +#define FLD_RDS_I 0x0000ffff + +/*****************************************************************************/ +#define AUTOCONFIG_REG 0x9c4 +/* Reserved [31:4] */ +#define FLD_AUTOCONFIG_MODE 0x0000000f + +#define FM_AFC 0x9c8 +#define FLD_FM2_AFC 0xffff0000 +#define FLD_FM1_AFC 0x0000ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define NEW_SPARE 0x9cc +#define NEW_SPARE_REG 0x9cc + +/*****************************************************************************/ +#define DBX_ADJ 0x9d0 +/* Reserved [31:28] */ +#define FLD_DBX2_ADJ 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_DBX1_ADJ 0x00000fff + +#define VID_FMT_AUTO 0 +#define VID_FMT_NTSC_M 1 +#define VID_FMT_NTSC_J 2 +#define VID_FMT_NTSC_443 3 +#define VID_FMT_PAL_BDGHI 4 +#define VID_FMT_PAL_M 5 +#define VID_FMT_PAL_N 6 +#define VID_FMT_PAL_NC 7 +#define VID_FMT_PAL_60 8 +#define VID_FMT_SECAM 12 +#define VID_FMT_SECAM_60 13 + +#define INPUT_MODE_CVBS_0 0 /* INPUT_MODE_VALUE(0) */ +#define INPUT_MODE_YC_1 1 /* INPUT_MODE_VALUE(1) */ +#define INPUT_MODE_YC2_2 2 /* INPUT_MODE_VALUE(2) */ +#define INPUT_MODE_YUV_3 3 /* INPUT_MODE_VALUE(3) */ + +#define LUMA_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */ +#define LUMA_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */ +#define LUMA_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */ + +#define UV_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */ +#define UV_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */ +#define UV_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */ + +#define TWO_TAP_FILT 0 +#define THREE_TAP_FILT 1 +#define FOUR_TAP_FILT 2 +#define FIVE_TAP_FILT 3 + +#define AUD_CHAN_SRC_PARALLEL 0 +#define AUD_CHAN_SRC_I2S_INPUT 1 +#define AUD_CHAN_SRC_FLATIRON 2 +#define AUD_CHAN_SRC_PARALLEL3 3 + +#define OUT_MODE_601 0 +#define OUT_MODE_656 1 +#define OUT_MODE_VIP11 2 +#define OUT_MODE_VIP20 3 + +#define PHASE_INC_49MHZ 0x0df22 +#define PHASE_INC_56MHZ 0x0fa5b +#define PHASE_INC_28MHZ 0x010000 + +#endif diff --git a/linux/drivers/media/video/cx231xx/cx231xx-vbi.c b/linux/drivers/media/video/cx231xx/cx231xx-vbi.c new file mode 100644 index 000000000..9080d1755 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-vbi.c @@ -0,0 +1,715 @@ +/* + cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on cx88 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. + + 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/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bitmap.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/version.h> +#include <linux/mm.h> +#include <linux/mutex.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-chip-ident.h> +#include <media/msp3400.h> +#include <media/tuner.h> + +#include "cx231xx.h" +#include "cx231xx-vbi.h" + +static inline void print_err_status(struct cx231xx *dev, int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status, + errmsg); + } else { + cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) +{ + struct cx231xx_buffer *buf; + struct cx231xx_dmaqueue *dma_q = urb->context; + int rc = 1; + unsigned char *p_buffer; + u32 bytes_parsed = 0, buffer_size = 0; + u8 sav_eav = 0; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->vbi_mode.isoc_ctl.buf; + + /* get buffer pointer and length */ + p_buffer = urb->transfer_buffer; + buffer_size = urb->actual_length; + + if (buffer_size > 0) { + bytes_parsed = 0; + + if (dma_q->is_partial_line) { + /* Handle the case where we were working on a partial + line */ + sav_eav = dma_q->last_sav; + } else { + /* Check for a SAV/EAV overlapping the + buffer boundary */ + + sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer, + dma_q->partial_buf, + &bytes_parsed); + } + + sav_eav &= 0xF0; + /* Get the first line if we have some portion of an SAV/EAV from + the last buffer or a partial line */ + if (sav_eav) { + bytes_parsed += cx231xx_get_vbi_line(dev, dma_q, + sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed); /* buffer size */ + } + + /* Now parse data that is completely in this buffer */ + dma_q->is_partial_line = 0; + + while (bytes_parsed < buffer_size) { + u32 bytes_used = 0; + + sav_eav = cx231xx_find_next_SAV_EAV( + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed, /* buffer size */ + &bytes_used); /* bytes used to get SAV/EAV */ + + bytes_parsed += bytes_used; + + sav_eav &= 0xF0; + if (sav_eav && (bytes_parsed < buffer_size)) { + bytes_parsed += cx231xx_get_vbi_line(dev, + dma_q, sav_eav, /* SAV/EAV */ + p_buffer+bytes_parsed, /* p_buffer */ + buffer_size-bytes_parsed);/*buf size*/ + } + } + + /* Save the last four bytes of the buffer so we can + check the buffer boundary condition next time */ + memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); + bytes_parsed = 0; + } + + return rc; +} + +/* ------------------------------------------------------------------ + Vbi buf operations + ------------------------------------------------------------------*/ + +static int +vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + u32 height = 0; + + height = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + + *size = (dev->width * height * 2); + if (0 == *count) + *count = CX231XX_DEF_VBI_BUF; + + if (*count < CX231XX_MIN_BUF) + *count = CX231XX_MIN_BUF; + + /* call VBI setup if required */ + /* cx231xx_i2c_call_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, &f); + */ + + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->vbi_mode.slock, flags); + if (dev->vbi_mode.isoc_ctl.buf == buf) + dev->vbi_mode.isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->vbi_mode.slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx *dev = fh->dev; + int rc = 0, urb_init = 0; + u32 height = 0; + + height = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + buf->vb.size = ((dev->width << 1) * height); + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = height; + buf->vb.field = field; + buf->vb.field = V4L2_FIELD_SEQ_TB; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + if (!dev->vbi_mode.isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS, + CX231XX_NUM_VBI_BUFS, + dev->vbi_mode.alt_max_pkt_size[0], + cx231xx_isoc_vbi_copy); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); + +} + +static void vbi_buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + +#if 0 + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = (struct cx231xx *)fh->dev; + + cx231xx_info(DRIVER_NAME "cx231xx: called vbi_buffer_release\n"); +#endif + + free_buffer(vq, buf); +} + +struct videobuf_queue_ops cx231xx_vbi_qops = { + .buf_setup = vbi_buffer_setup, + .buf_prepare = vbi_buffer_prepare, + .buf_queue = vbi_buffer_queue, + .buf_release = vbi_buffer_release, +}; + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ + +/* + * IRQ callback, called by URB callback + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void cx231xx_irq_vbi_callback(struct urb *urb, struct pt_regs *regs) +#else +static void cx231xx_irq_vbi_callback(struct urb *urb) +#endif +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); + int rc; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + cx231xx_err(DRIVER_NAME "urb completition error %d.\n", + urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->vbi_mode.slock); + rc = dev->vbi_mode.isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->vbi_mode.slock); + + /* Reset status */ + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void cx231xx_uninit_vbi_isoc(struct cx231xx *dev) +{ + struct urb *urb; + int i; + + cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n"); + + dev->vbi_mode.isoc_ctl.nfields = -1; + for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) { + urb = dev->vbi_mode.isoc_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->vbi_mode.isoc_ctl.transfer_buffer[i]) { + + kfree(dev->vbi_mode.isoc_ctl. + transfer_buffer[i]); + dev->vbi_mode.isoc_ctl.transfer_buffer[i] = + NULL; + } + usb_free_urb(urb); + dev->vbi_mode.isoc_ctl.urb[i] = NULL; + } + dev->vbi_mode.isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->vbi_mode.isoc_ctl.urb); + kfree(dev->vbi_mode.isoc_ctl.transfer_buffer); + + dev->vbi_mode.isoc_ctl.urb = NULL; + dev->vbi_mode.isoc_ctl.transfer_buffer = NULL; + dev->vbi_mode.isoc_ctl.num_bufs = 0; + + cx231xx_capture_start(dev, 0, Vbi); +} +EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc); + +/* + * Allocate URBs and start IRQ + */ +int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, + struct urb *urb)) +{ + struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int rc; + + cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n"); + + /* De-allocates all pending stuff */ + cx231xx_uninit_vbi_isoc(dev); + + /* clear if any halt */ + usb_clear_halt(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->vbi_mode.end_point_addr)); + + dev->vbi_mode.isoc_ctl.isoc_copy = isoc_copy; + dev->vbi_mode.isoc_ctl.num_bufs = num_bufs; + dma_q->pos = 0; + dma_q->is_partial_line = 0; + dma_q->last_sav = 0; + dma_q->current_field = -1; + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + dma_q->lines_completed = 0; + for (i = 0; i < 8; i++) + dma_q->partial_buf[i] = 0; + + dev->vbi_mode.isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs, + GFP_KERNEL); + if (!dev->vbi_mode.isoc_ctl.urb) { + cx231xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->vbi_mode.isoc_ctl.transfer_buffer = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->vbi_mode.isoc_ctl.transfer_buffer) { + cx231xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->vbi_mode.isoc_ctl.urb); + return -ENOMEM; + } + + dev->vbi_mode.isoc_ctl.max_pkt_size = max_pkt_size; + dev->vbi_mode.isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->vbi_mode.isoc_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) { + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + cx231xx_err(DRIVER_NAME + ": cannot alloc isoc_ctl.urb %i\n", i); + cx231xx_uninit_vbi_isoc(dev); + return -ENOMEM; + } + dev->vbi_mode.isoc_ctl.urb[i] = urb; + urb->transfer_flags = 0; + + dev->vbi_mode.isoc_ctl.transfer_buffer[i] = + kzalloc(sb_size, GFP_KERNEL); + if (!dev->vbi_mode.isoc_ctl.transfer_buffer[i]) { + cx231xx_err(DRIVER_NAME + ": unable to allocate %i bytes for transfer" + " buffer %i%s\n", sb_size, i, + in_interrupt() ? " while in int" : ""); + cx231xx_uninit_vbi_isoc(dev); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr); + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->vbi_mode.isoc_ctl.transfer_buffer[i], + sb_size, cx231xx_irq_vbi_callback, dma_q); + } + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->vbi_mode.isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + cx231xx_err(DRIVER_NAME + ": submit of urb %i failed (error=%i)\n", i, + rc); + cx231xx_uninit_vbi_isoc(dev); + return rc; + } + } + + cx231xx_capture_start(dev, 1, Vbi); + + return 0; +} +EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc); + +u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 sav_eav, u8 *p_buffer, u32 buffer_size) +{ + u32 bytes_copied = 0; + int current_field = -1; + + switch (sav_eav) { + + case SAV_VBI_FIELD1: + current_field = 1; + break; + + case SAV_VBI_FIELD2: + current_field = 2; + break; + default: + break; + } + + if (current_field < 0) + return bytes_copied; + + dma_q->last_sav = sav_eav; + + bytes_copied = + cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size, + current_field); + + return bytes_copied; +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void vbi_buffer_filled(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer *buf) +{ + /* Advice that buffer was filled */ + /* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */ + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->vbi_mode.isoc_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_line, u32 length, int field_number) +{ + u32 bytes_to_copy; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width * 2; + + if (dma_q->current_field != field_number) + cx231xx_reset_vbi_buffer(dev, dma_q); + + /* get the buffer pointer */ + buf = dev->vbi_mode.isoc_ctl.buf; + + /* Remember the field number for next time */ + dma_q->current_field = field_number; + + bytes_to_copy = dma_q->bytes_left_in_line; + if (bytes_to_copy > length) + bytes_to_copy = length; + + if (dma_q->lines_completed >= dma_q->lines_per_field) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = + (dma_q->bytes_left_in_line == 0) ? 0 : 1; + return 0; + } + + dma_q->is_partial_line = 1; + + /* If we don't have a buffer, just return the number of bytes we would + have copied if we had a buffer. */ + if (!buf) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = + (dma_q->bytes_left_in_line == 0) ? 0 : 1; + return bytes_to_copy; + } + + /* copy the data to video buffer */ + cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy); + + dma_q->pos += bytes_to_copy; + dma_q->bytes_left_in_line -= bytes_to_copy; + + if (dma_q->bytes_left_in_line == 0) { + + dma_q->bytes_left_in_line = _line_size; + dma_q->lines_completed++; + dma_q->is_partial_line = 0; + + if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) { + + vbi_buffer_filled(dev, dma_q, buf); + + dma_q->pos = 0; + buf = NULL; + dma_q->lines_completed = 0; + } + } + + return bytes_to_copy; +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer **buf) +{ + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); + char *outp; + + if (list_empty(&dma_q->active)) { + cx231xx_err(DRIVER_NAME ": No active queue to serve\n"); + dev->vbi_mode.isoc_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); + + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0, (*buf)->vb.size); + + dev->vbi_mode.isoc_ctl.buf = *buf; + + return; +} + +void cx231xx_reset_vbi_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q) +{ + struct cx231xx_buffer *buf; + + buf = dev->vbi_mode.isoc_ctl.buf; + + if (buf == NULL) { + /* first try to get the buffer */ + get_next_vbi_buf(dma_q, &buf); + + dma_q->pos = 0; + dma_q->current_field = -1; + } + + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; +} + +int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy) +{ + u8 *p_out_buffer = NULL; + u32 current_line_bytes_copied = 0; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width << 1; + void *startwrite; + int offset, lencopy; + + buf = dev->vbi_mode.isoc_ctl.buf; + + if (buf == NULL) + return -EINVAL; + + p_out_buffer = videobuf_to_vmalloc(&buf->vb); + + if (dma_q->bytes_left_in_line != _line_size) { + current_line_bytes_copied = + _line_size - dma_q->bytes_left_in_line; + } + + offset = (dma_q->lines_completed * _line_size) + + current_line_bytes_copied; + + /* prepare destination address */ + startwrite = p_out_buffer + offset; + + lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? + bytes_to_copy : dma_q->bytes_left_in_line; + + memcpy(startwrite, p_buffer, lencopy); + + return 0; +} + +u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q) +{ + u32 height = 0; + + height = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + return (dma_q->lines_completed == height) ? 1 : 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-vbi.h b/linux/drivers/media/video/cx231xx/cx231xx-vbi.h new file mode 100644 index 000000000..89c7fe80b --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-vbi.h @@ -0,0 +1,65 @@ +/* + cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on cx88 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. + + 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 _CX231XX_VBI_H +#define _CX231XX_VBI_H + +extern struct videobuf_queue_ops cx231xx_vbi_qops; + +#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */ +#define NTSC_VBI_END_LINE 21 +#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1) + +#define PAL_VBI_START_LINE 6 +#define PAL_VBI_END_LINE 23 +#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1) + +#define VBI_STRIDE 1440 +#define VBI_SAMPLES_PER_LINE 1440 + +#define CX231XX_NUM_VBI_PACKETS 4 +#define CX231XX_NUM_VBI_BUFS 5 + +/* stream functions */ +int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, + struct urb *urb)); + +void cx231xx_uninit_vbi_isoc(struct cx231xx *dev); + +/* vbi data copy functions */ +u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 sav_eav, u8 *p_buffer, u32 buffer_size); + +u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_line, u32 length, int field_number); + +void cx231xx_reset_vbi_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q); + +int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy); + +u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q); + +#endif diff --git a/linux/drivers/media/video/cx231xx/cx231xx-video.c b/linux/drivers/media/video/cx231xx/cx231xx-video.c new file mode 100644 index 000000000..2dbe65b83 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-video.c @@ -0,0 +1,2450 @@ +/* + cx231xx-video.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + Based on cx23885 driver + Based on cx88 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. + + 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/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bitmap.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/version.h> +#include <linux/mm.h> +#include <linux/mutex.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-chip-ident.h> +#include <media/msp3400.h> +#include <media/tuner.h> + +#include "dvb_frontend.h" + +#include "cx231xx.h" +#include "cx231xx-vbi.h" + +#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>" +#define DRIVER_DESC "Conexant cx231xx based USB video device driver" + +#define cx231xx_videodbg(fmt, arg...) do {\ + if (video_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +#define cx231xx_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); \ + } \ + } while (0) + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; + +module_param_array(card, int, NULL, 0444); +module_param_array(video_nr, int, NULL, 0444); +module_param_array(vbi_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); + +MODULE_PARM_DESC(card, "card type"); +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); + +static unsigned int video_debug; +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); + +/* supported video standards */ +static struct cx231xx_fmt format[] = { + { + .name = "16bpp YUY2, 4:2:2, packed", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .reg = 0, + }, +}; + +/* supported controls */ +/* Common to all boards */ + +/* ------------------------------------------------------------------- */ + +static const struct v4l2_queryctrl no_ctl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + +static struct cx231xx_ctrl cx231xx_ctls[] = { + /* --- video --- */ + { + .v = { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .minimum = 0x00, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 128, + .reg = LUMA_CTRL, + .mask = 0x00ff, + .shift = 0, + }, { + .v = { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x3f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 0, + .reg = LUMA_CTRL, + .mask = 0xff00, + .shift = 8, + }, { + .v = { + .id = V4L2_CID_HUE, + .name = "Hue", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 128, + .reg = CHROMA_CTRL, + .mask = 0xff0000, + .shift = 16, + }, { + /* strictly, this only describes only U saturation. + * V saturation is handled specially through code. + */ + .v = { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 0, + .reg = CHROMA_CTRL, + .mask = 0x00ff, + .shift = 0, + }, { + /* --- audio --- */ + .v = { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, + .reg = PATH1_CTL1, + .mask = (0x1f << 24), + .shift = 24, + }, { + .v = { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 0x3f, + .step = 1, + .default_value = 0x3f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .reg = PATH1_VOL_CTL, + .mask = 0xff, + .shift = 0, + } +}; +static const int CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls); + +static const u32 cx231xx_user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUDIO_VOLUME, +#if 0 /* Keep */ + V4L2_CID_AUDIO_BALANCE, +#endif + V4L2_CID_AUDIO_MUTE, + 0 +}; + +static const u32 *ctrl_classes[] = { + cx231xx_user_ctrls, + NULL +}; + +/* ------------------------------------------------------------------ + Video buffer and parser functions + ------------------------------------------------------------------*/ + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer *buf) +{ + /* Advice that buffer was filled */ + cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->video_mode.isoc_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static inline void print_err_status(struct cx231xx *dev, int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg); + } else { + cx231xx_isocdbg("URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer **buf) +{ + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + + char *outp; + + if (list_empty(&dma_q->active)) { + cx231xx_isocdbg("No active queue to serve\n"); + dev->video_mode.isoc_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); + + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0, (*buf)->vb.size); + + dev->video_mode.isoc_ctl.buf = *buf; + + return; +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) +{ + struct cx231xx_buffer *buf; + struct cx231xx_dmaqueue *dma_q = urb->context; + unsigned char *outp = NULL; + int i, rc = 1; + unsigned char *p_buffer; + u32 bytes_parsed = 0, buffer_size = 0; + u8 sav_eav = 0; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->video_mode.isoc_ctl.buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* cx231xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->video_mode.max_pkt_size) { + cx231xx_isocdbg("packet bigger than packet size"); + continue; + } + + /* get buffer pointer and length */ + p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + buffer_size = urb->iso_frame_desc[i].actual_length; + bytes_parsed = 0; + + if (dma_q->is_partial_line) { + /* Handle the case of a partial line */ + sav_eav = dma_q->last_sav; + } else { + /* Check for a SAV/EAV overlapping + the buffer boundary */ + sav_eav = + cx231xx_find_boundary_SAV_EAV(p_buffer, + dma_q->partial_buf, + &bytes_parsed); + } + + sav_eav &= 0xF0; + /* Get the first line if we have some portion of an SAV/EAV from + the last buffer or a partial line */ + if (sav_eav) { + bytes_parsed += cx231xx_get_video_line(dev, dma_q, + sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed);/* buf size */ + } + + /* Now parse data that is completely in this buffer */ + /* dma_q->is_partial_line = 0; */ + + while (bytes_parsed < buffer_size) { + u32 bytes_used = 0; + + sav_eav = cx231xx_find_next_SAV_EAV( + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed, /* buf size */ + &bytes_used);/* bytes used to get SAV/EAV */ + + bytes_parsed += bytes_used; + + sav_eav &= 0xF0; + if (sav_eav && (bytes_parsed < buffer_size)) { + bytes_parsed += cx231xx_get_video_line(dev, + dma_q, sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed,/* p_buffer */ + buffer_size - bytes_parsed);/*buf size*/ + } + } + + /* Save the last four bytes of the buffer so we can check the + buffer boundary condition next time */ + memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); + bytes_parsed = 0; + + } + return rc; +} + +u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf, + u32 *p_bytes_used) +{ + u32 bytes_used; + u8 boundary_bytes[8]; + u8 sav_eav = 0; + + *p_bytes_used = 0; + + /* Create an array of the last 4 bytes of the last buffer and the first + 4 bytes of the current buffer. */ + + memcpy(boundary_bytes, partial_buf, 4); + memcpy(boundary_bytes + 4, p_buffer, 4); + + /* Check for the SAV/EAV in the boundary buffer */ + sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8, + &bytes_used); + + if (sav_eav) { + /* found a boundary SAV/EAV. Updates the bytes used to reflect + only those used in the new buffer */ + *p_bytes_used = bytes_used - 4; + } + + return sav_eav; +} + +u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used) +{ + u32 i; + u8 sav_eav = 0; + + /* + * Don't search if the buffer size is less than 4. It causes a page + * fault since buffer_size - 4 evaluates to a large number in that + * case. + */ + if (buffer_size < 4) { + *p_bytes_used = buffer_size; + return 0; + } + + for (i = 0; i < (buffer_size - 3); i++) { + + if ((p_buffer[i] == 0xFF) && + (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) { + + *p_bytes_used = i + 4; + sav_eav = p_buffer[i + 3]; + return sav_eav; + } + } + + *p_bytes_used = buffer_size; + return 0; +} + +u32 cx231xx_get_video_line(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, u8 sav_eav, + u8 *p_buffer, u32 buffer_size) +{ + u32 bytes_copied = 0; + int current_field = -1; + + switch (sav_eav) { + case SAV_ACTIVE_VIDEO_FIELD1: + /* looking for skipped line which occurred in PAL 720x480 mode. + In this case, there will be no active data contained + between the SAV and EAV */ + if ((buffer_size > 3) && (p_buffer[0] == 0xFF) && + (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) && + ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) || + (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) || + (p_buffer[3] == EAV_VBLANK_FIELD1) || + (p_buffer[3] == EAV_VBLANK_FIELD2))) + return bytes_copied; + current_field = 1; + break; + + case SAV_ACTIVE_VIDEO_FIELD2: + /* looking for skipped line which occurred in PAL 720x480 mode. + In this case, there will be no active data contained between + the SAV and EAV */ + if ((buffer_size > 3) && (p_buffer[0] == 0xFF) && + (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) && + ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) || + (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) || + (p_buffer[3] == EAV_VBLANK_FIELD1) || + (p_buffer[3] == EAV_VBLANK_FIELD2))) + return bytes_copied; + current_field = 2; + break; + } + + dma_q->last_sav = sav_eav; + + bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer, + buffer_size, current_field); + + return bytes_copied; +} + +u32 cx231xx_copy_video_line(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, u8 *p_line, + u32 length, int field_number) +{ + u32 bytes_to_copy; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width * 2; + + if (dma_q->current_field != field_number) + cx231xx_reset_video_buffer(dev, dma_q); + + /* get the buffer pointer */ + buf = dev->video_mode.isoc_ctl.buf; + + /* Remember the field number for next time */ + dma_q->current_field = field_number; + + bytes_to_copy = dma_q->bytes_left_in_line; + if (bytes_to_copy > length) + bytes_to_copy = length; + + if (dma_q->lines_completed >= dma_q->lines_per_field) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ? + 0 : 1; + return 0; + } + + dma_q->is_partial_line = 1; + + /* If we don't have a buffer, just return the number of bytes we would + have copied if we had a buffer. */ + if (!buf) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) + ? 0 : 1; + return bytes_to_copy; + } + + /* copy the data to video buffer */ + cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy); + + dma_q->pos += bytes_to_copy; + dma_q->bytes_left_in_line -= bytes_to_copy; + + if (dma_q->bytes_left_in_line == 0) { + dma_q->bytes_left_in_line = _line_size; + dma_q->lines_completed++; + dma_q->is_partial_line = 0; + + if (cx231xx_is_buffer_done(dev, dma_q) && buf) { + buffer_filled(dev, dma_q, buf); + + dma_q->pos = 0; + buf = NULL; + dma_q->lines_completed = 0; + } + } + + return bytes_to_copy; +} + +void cx231xx_reset_video_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q) +{ + struct cx231xx_buffer *buf; + + /* handle the switch from field 1 to field 2 */ + if (dma_q->current_field == 1) { + if (dma_q->lines_completed >= dma_q->lines_per_field) + dma_q->field1_done = 1; + else + dma_q->field1_done = 0; + } + + buf = dev->video_mode.isoc_ctl.buf; + + if (buf == NULL) { + u8 *outp = NULL; + /* first try to get the buffer */ + get_next_buf(dma_q, &buf); + + if (buf) + outp = videobuf_to_vmalloc(&buf->vb); + + dma_q->pos = 0; + dma_q->field1_done = 0; + dma_q->current_field = -1; + } + + /* reset the counters */ + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; +} + +int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy) +{ + u8 *p_out_buffer = NULL; + u32 current_line_bytes_copied = 0; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width << 1; + void *startwrite; + int offset, lencopy; + + buf = dev->video_mode.isoc_ctl.buf; + + if (buf == NULL) + return -1; + + p_out_buffer = videobuf_to_vmalloc(&buf->vb); + + current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line; + + /* Offset field 2 one line from the top of the buffer */ + offset = (dma_q->current_field == 1) ? 0 : _line_size; + + /* Offset for field 2 */ + startwrite = p_out_buffer + offset; + + /* lines already completed in the current field */ + startwrite += (dma_q->lines_completed * _line_size * 2); + + /* bytes already completed in the current line */ + startwrite += current_line_bytes_copied; + + lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? + bytes_to_copy : dma_q->bytes_left_in_line; + + if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size)) + return 0; + + /* The below copies the UYVY data straight into video buffer */ + cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy); + + return 0; +} + +void cx231xx_swab(u16 *from, u16 *to, u16 len) +{ + u16 i; + + if (len <= 0) + return; + + for (i = 0; i < len / 2; i++) + to[i] = (from[i] << 8) | (from[i] >> 8); +} + +u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q) +{ + u8 buffer_complete = 0; + + /* Dual field stream */ + buffer_complete = ((dma_q->current_field == 2) && + (dma_q->lines_completed >= dma_q->lines_per_field) && + dma_q->field1_done); + + return buffer_complete; +} + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + struct v4l2_frequency f; + + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3; + if (0 == *count) + *count = CX231XX_DEF_BUF; + + if (*count < CX231XX_MIN_BUF) + *count = CX231XX_MIN_BUF; + + /* Ask tuner to go to analog mode */ + memset(&f, 0, sizeof(f)); + f.frequency = dev->ctl_freq; + f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + + cx231xx_i2c_call_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, &f); + + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + unsigned long flags = 0; + + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->video_mode.slock, flags); + if (dev->video_mode.isoc_ctl.buf == buf) + dev->video_mode.isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->video_mode.slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx *dev = fh->dev; + int rc = 0, urb_init = 0; + + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + + 7) >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = dev->height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + if (!dev->video_mode.isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + CX231XX_NUM_BUFS, + dev->video_mode.max_pkt_size, + cx231xx_isoc_copy); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); + +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = (struct cx231xx *)fh->dev; + + cx231xx_isocdbg("cx231xx: called buffer_release\n"); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops cx231xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************* v4l2 interface **************************************/ + +void video_mux(struct cx231xx *dev, int index) +{ + + struct v4l2_routing route; + + route.input = INPUT(index)->vmux; + route.output = 0; + dev->video_input = index; + dev->ctl_ainput = INPUT(index)->amux; + + cx231xx_set_video_input_mux(dev, index); + + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_INT_S_VIDEO_ROUTING, + &route); + + cx231xx_set_audio_input(dev, dev->ctl_ainput); + + cx231xx_info("video_mux : %d\n", index); + + /* do mode control overrides if required */ + cx231xx_do_mode_ctrl_overrides(dev); +} + +/* Usage lock check functions */ +static int res_get(struct cx231xx_fh *fh) +{ + struct cx231xx *dev = fh->dev; + int rc = 0; + + /* This instance already has stream_on */ + if (fh->stream_on) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (dev->stream_on) + return -EBUSY; + dev->stream_on = 1; + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (dev->vbi_stream_on) + return -EBUSY; + dev->vbi_stream_on = 1; + } else + return -EINVAL; + + fh->stream_on = 1; + + return rc; +} + +static int res_check(struct cx231xx_fh *fh) +{ + return fh->stream_on; +} + +static void res_free(struct cx231xx_fh *fh) +{ + struct cx231xx *dev = fh->dev; + + fh->stream_on = 0; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + dev->stream_on = 0; + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + dev->vbi_stream_on = 0; +} + +static int check_dev(struct cx231xx *dev) +{ + if (dev->state & DEV_DISCONNECTED) { + cx231xx_errdev("v4l2 ioctl: device not present\n"); + return -ENODEV; + } + + if (dev->state & DEV_MISCONFIGURED) { + cx231xx_errdev("v4l2 ioctl: device is misconfigured; " + "close and open it again\n"); + return -EIO; + } + return 0; +} + +void get_scale(struct cx231xx *dev, + unsigned int width, unsigned int height, + unsigned int *hscale, unsigned int *vscale) +{ + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + + *hscale = (((unsigned long)maxw) << 12) / width - 4096L; + if (*hscale >= 0x4000) + *hscale = 0x3fff; + + *vscale = (((unsigned long)maxh) << 12) / height - 4096L; + if (*vscale >= 0x4000) + *vscale = 0x3fff; + + dev->hscale = *hscale; + dev->vscale = *vscale; + +} + +/* ------------------------------------------------------------------ + IOCTL vidioc handling + ------------------------------------------------------------------*/ + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + mutex_lock(&dev->lock); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.pixelformat = dev->format->fourcc;; + f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + + mutex_unlock(&dev->lock); + + return 0; +} + +static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return &format[i]; + + return NULL; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int width = f->fmt.pix.width; + int height = f->fmt.pix.height; + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + unsigned int hscale, vscale; + struct cx231xx_fmt *fmt; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + cx231xx_videodbg("Fourcc format (%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + /* width must even because of the YUYV format + height must be even because of interlacing */ + height &= 0xfffe; + width &= 0xfffe; + + if (unlikely(height < 32)) + height = 32; + if (unlikely(height > maxh)) + height = maxh; + if (unlikely(width < 48)) + width = 48; + if (unlikely(width > maxw)) + width = maxw; + + get_scale(dev, width, height, &hscale, &vscale); + + width = (((unsigned long)maxw) << 12) / (hscale + 4096L); + height = (((unsigned long)maxh) << 12) / (vscale + 4096L); + + f->fmt.pix.width = width; + f->fmt.pix.height = height; + f->fmt.pix.pixelformat = fmt->fourcc; + f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + struct cx231xx_fmt *fmt; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + + vidioc_try_fmt_vid_cap(file, priv, f); + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + rc = -EINVAL; + goto out; + } + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + cx231xx_errdev("%s queue busy\n", __func__); + rc = -EBUSY; + goto out; + } + + if (dev->stream_on && !fh->stream_on) { + cx231xx_errdev("%s device in use by another fh\n", __func__); + rc = -EBUSY; + goto out; + } + + /* set new image size */ + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->format = fmt; + get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_S_FMT, f); + + /* Set the correct alternate setting for this resolution */ + cx231xx_resolution_set(dev); + +out: + mutex_unlock(&dev->lock); + return rc; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id * id) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + *id = dev->norm; + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + struct v4l2_format f; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm); + + mutex_lock(&dev->lock); + dev->norm = *norm; + + /* Adjusts width/height, if needed */ + f.fmt.pix.width = dev->width; + f.fmt.pix.height = dev->height; + vidioc_try_fmt_vid_cap(file, priv, &f); + + /* set new image size */ + dev->width = f.fmt.pix.width; + dev->height = f.fmt.pix.height; + get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_S_STD, &dev->norm); + + mutex_unlock(&dev->lock); + + cx231xx_resolution_set(dev); + + /* do mode control overrides */ + cx231xx_do_mode_ctrl_overrides(dev); + + return 0; +} + +static const char *iname[] = { + [CX231XX_VMUX_COMPOSITE1] = "Composite1", + [CX231XX_VMUX_SVIDEO] = "S-Video", + [CX231XX_VMUX_TELEVISION] = "Television", + [CX231XX_VMUX_CABLE] = "Cable TV", + [CX231XX_VMUX_DVB] = "DVB", + [CX231XX_VMUX_DEBUG] = "for debug only", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= MAX_CX231XX_INPUT) + return -EINVAL; + if (0 == INPUT(n)->type) + return -EINVAL; + + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + + strcpy(i->name, iname[INPUT(n)->type]); + + if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) || + (CX231XX_VMUX_CABLE == INPUT(n)->type)) + i->type = V4L2_INPUT_TYPE_TUNER; + + i->std = dev->vdev->tvnorms; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + *i = dev->video_input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (i >= MAX_CX231XX_INPUT) + return -EINVAL; + if (0 == INPUT(i)->type) + return -EINVAL; + + mutex_lock(&dev->lock); + + video_mux(dev, i); + + mutex_unlock(&dev->lock); + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + switch (a->index) { + case CX231XX_AMUX_VIDEO: + strcpy(a->name, "Television"); + break; + case CX231XX_AMUX_LINE_IN: + strcpy(a->name, "Line In"); + break; + default: + return -EINVAL; + } + + a->index = dev->ctl_ainput; + a->capability = V4L2_AUDCAP_STEREO; + + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int status = 0; + + /* Doesn't allow manual routing */ + if (a->index != dev->ctl_ainput) + return -EINVAL; + + dev->ctl_ainput = INPUT(a->index)->amux; + status = cx231xx_set_audio_input(dev, dev->ctl_ainput); + + return status; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int id = qc->id; + int i; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + qc->id = v4l2_ctrl_next(ctrl_classes, qc->id); + if (unlikely(qc->id == 0)) + return -EINVAL; + + memset(qc, 0, sizeof(*qc)); + + qc->id = id; + + if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1) + return -EINVAL; + + for (i = 0; i < CX231XX_CTLS; i++) + if (cx231xx_ctls[i].v.id == qc->id) + break; + + if (i == CX231XX_CTLS) { + *qc = no_ctl; + return 0; + } + *qc = cx231xx_ctls[i].v; + + mutex_lock(&dev->lock); + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_QUERYCTRL, qc); + mutex_unlock(&dev->lock); + + if (qc->type) + return 0; + else + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_G_CTRL, ctrl); + + mutex_unlock(&dev->lock); + return rc; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_S_CTRL, ctrl); + + mutex_unlock(&dev->lock); + return rc; +} + +static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Tuner"); + + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + t->signal = 0xffff; /* LOCKED */ + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != t->index) + return -EINVAL; +#if 0 /* Keep */ + mutex_lock(&dev->lock); + + cx231xx_i2c_call_clients(&dev->i2c_bus[1], VIDIOC_S_TUNER, t); + + mutex_unlock(&dev->lock); +#endif + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + mutex_lock(&dev->lock); + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = dev->ctl_freq; + + cx231xx_i2c_call_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f); + + mutex_unlock(&dev->lock); + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != f->tuner) + return -EINVAL; + + if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) + return -EINVAL; + + /* set pre channel change settings in DIF first */ + rc = cx231xx_tuner_pre_channel_change(dev); + + mutex_lock(&dev->lock); + + dev->ctl_freq = f->frequency; + + if (dev->tuner_type == TUNER_XC5000) { + if (dev->cx231xx_set_analog_freq != NULL) + dev->cx231xx_set_analog_freq(dev, f->frequency); + } else { + cx231xx_i2c_call_clients(&dev->i2c_bus[1], + VIDIOC_S_FREQUENCY, f); + } + + mutex_unlock(&dev->lock); + + /* set post channel change settings in DIF first */ + rc = cx231xx_tuner_post_channel_change(dev); + + cx231xx_info("Set New FREQUENCY to %d\n", f->frequency); + + return rc; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG + +/* + -R, --list-registers=type=<host/i2cdrv/i2caddr>, + chip=<chip>[,min=<addr>,max=<addr>] + dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER] + -r, --set-register=type=<host/i2cdrv/i2caddr>, + chip=<chip>,reg=<addr>,val=<val> + set the register [VIDIOC_DBG_S_REGISTER] + + if type == host, then <chip> is the hosts chip ID (default 0) + if type == i2cdrv (default), then <chip> is the I2C driver name or ID + if type == i2caddr, then <chip> is the 7-bit I2C address +*/ + +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int ret = 0; + u8 value[4] = { 0, 0, 0, 0 }; + u32 data = 0; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + switch (reg->match.addr) { + case 0: /* Cx231xx - internal registers */ + ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, + (u16)reg->reg, value, 4); + reg->val = value[0] | value[1] << 8 | + value[2] << 16 | value[3] << 24; + break; + case 1: /* Colibri - read byte */ + ret = cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + (u16)reg->reg, 2, &data, 1); + reg->val = le32_to_cpu(data & 0xff); + break; + case 14: /* Colibri - read dword */ + ret = cx231xx_read_i2c_data(dev, Colibri_DEVICE_ADDRESS, + (u16)reg->reg, 2, &data, 4); + reg->val = le32_to_cpu(data); + break; + case 2: /* Hammerhead - read byte */ + ret = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + (u16)reg->reg, 2, &data, 1); + reg->val = le32_to_cpu(data & 0xff); + break; + case 24: /* Hammerhead - read dword */ + ret = cx231xx_read_i2c_data(dev, HAMMERHEAD_I2C_ADDRESS, + (u16)reg->reg, 2, &data, 4); + reg->val = le32_to_cpu(data); + break; + case 3: /* flatiron - read byte */ + ret = cx231xx_read_i2c_data(dev, + Flatrion_DEVICE_ADDRESS, + (u16)reg->reg, 1, + &data, 1); + reg->val = le32_to_cpu(data & 0xff); + break; + case 34: /* flatiron - read dword */ + ret = + cx231xx_read_i2c_data(dev, Flatrion_DEVICE_ADDRESS, + (u16)reg->reg, 1, &data, 4); + reg->val = le32_to_cpu(data); + break; + } + return ret < 0 ? ret : 0; + + case V4L2_CHIP_MATCH_I2C_DRIVER: + cx231xx_i2c_call_clients(&dev->i2c_bus[0], + VIDIOC_DBG_G_REGISTER, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* Not supported yet */ + return -EINVAL; + default: + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + } + + mutex_lock(&dev->lock); + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_DBG_G_REGISTER, reg); + mutex_unlock(&dev->lock); + + return ret; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int ret = 0; + __le64 buf; + u32 value; + u8 data[4] = { 0, 0, 0, 0 }; + + buf = cpu_to_le64(reg->val); + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + { + value = (u32) buf & 0xffffffff; + + switch (reg->match.addr) { + case 0: /* cx231xx internal registers */ + data[0] = (u8) value; + data[1] = (u8) (value >> 8); + data[2] = (u8) (value >> 16); + data[3] = (u8) (value >> 24); + ret = cx231xx_write_ctrl_reg(dev, + VRT_SET_REGISTER, + (u16)reg->reg, data, + 4); + break; + case 1: /* Colibri - read byte */ + ret = cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + (u16)reg->reg, 2, + value, 1); + break; + case 14: /* Colibri - read dword */ + ret = cx231xx_write_i2c_data(dev, + Colibri_DEVICE_ADDRESS, + (u16)reg->reg, 2, + value, 4); + break; + case 2: /* Hammerhead - read byte */ + ret = + cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + (u16)reg->reg, 2, + value, 1); + break; + case 24: /* Hammerhead - read dword */ + ret = + cx231xx_write_i2c_data(dev, + HAMMERHEAD_I2C_ADDRESS, + (u16)reg->reg, 2, + value, 4); + break; + case 3: /* flatiron - read byte */ + ret = + cx231xx_write_i2c_data(dev, + Flatrion_DEVICE_ADDRESS, + (u16)reg->reg, 1, + value, 1); + break; + case 34: /* flatiron - read dword */ + ret = + cx231xx_write_i2c_data(dev, + Flatrion_DEVICE_ADDRESS, + (u16)reg->reg, 1, + value, 4); + break; + } + } + return ret < 0 ? ret : 0; + + default: + break; + } + + mutex_lock(&dev->lock); + + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_DBG_S_REGISTER, reg); + + mutex_unlock(&dev->lock); + + return ret; +} +#endif + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cc) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cc->bounds.left = 0; + cc->bounds.top = 0; + cc->bounds.width = dev->width; + cc->bounds.height = dev->height; + cc->defrect = cc->bounds; + cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */ + cc->pixelaspect.denominator = 59; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + + if (likely(rc >= 0)) + rc = videobuf_streamon(&fh->vb_vidq); + + mutex_unlock(&dev->lock); + + return rc; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) + return -EINVAL; + if (type != fh->type) + return -EINVAL; + + mutex_lock(&dev->lock); + + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); + + mutex_unlock(&dev->lock); + + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + strlcpy(cap->driver, "cx231xx", sizeof(cap->driver)); + strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&dev->udev->dev), + sizeof(cap->bus_info)); + + cap->version = CX231XX_VERSION_CODE; + + cap->capabilities = V4L2_CAP_VBI_CAPTURE | +#if 0 /* Keep */ + V4L2_CAP_SLICED_VBI_CAPTURE | +#endif + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(format))) + return -EINVAL; + + strlcpy(f->description, format[f->index].name, sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; + + return 0; +} + +/* Sliced VBI ioctls */ +static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + + f->fmt.sliced.service_set = 0; + + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_G_FMT, f); + + if (f->fmt.sliced.service_set == 0) + rc = -EINVAL; + + mutex_unlock(&dev->lock); + return rc; +} + +static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + cx231xx_i2c_call_clients(&dev->i2c_bus[0], VIDIOC_G_FMT, f); + mutex_unlock(&dev->lock); + + if (f->fmt.sliced.service_set == 0) + return -EINVAL; + + return 0; +} + +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ? + 35468950 : 28636363; + f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 64 * 4; + f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE : NTSC_VBI_START_LINE; + f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES; + f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; + f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + + return 0; + +} + +static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + if (dev->vbi_stream_on && !fh->stream_on) { + cx231xx_errdev("%s device in use by another fh\n", __func__); + return -EBUSY; + } + + f->type = V4L2_BUF_TYPE_VBI_CAPTURE; + f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ? + 35468950 : 28636363; + f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 244; + f->fmt.vbi.flags = 0; + f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE : NTSC_VBI_START_LINE; + f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES; + f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; + f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + + return 0; + +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_reqbufs(&fh->vb_vidq, rb); +} + +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_querybuf(&fh->vb_vidq, b); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_qbuf(&fh->vb_vidq, b); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct cx231xx_fh *fh = priv; + + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); +} +#endif + +/* ----------------------------------------------------------- */ +/* RADIO ESPECIFIC IOCTLS */ +/* ----------------------------------------------------------- */ + +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + + strlcpy(cap->driver, "cx231xx", sizeof(cap->driver)); + strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + + cap->version = CX231XX_VERSION_CODE; + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + + if (unlikely(t->index > 0)) + return -EINVAL; + + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + mutex_lock(&dev->lock); + cx231xx_i2c_call_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t); + mutex_unlock(&dev->lock); + + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + if (unlikely(a->index)) + return -EINVAL; + + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + + if (0 != t->index) + return -EINVAL; + + mutex_lock(&dev->lock); + cx231xx_i2c_call_clients(&dev->i2c_bus[1], VIDIOC_S_TUNER, t); + mutex_unlock(&dev->lock); + + return 0; +} + +static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *file, void *fh, unsigned int i) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + int i; + + if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + for (i = 0; i < CX231XX_CTLS; i++) + if (cx231xx_ctls[i].v.id == c->id) + break; + *c = cx231xx_ctls[i].v; + } else + *c = no_ctl; + return 0; +} + +/* + * cx231xx_v4l2_open() + * inits the device and starts isoc transfer + */ +static int cx231xx_v4l2_open(struct file *filp) +{ + int minor = video_devdata(filp)->minor; + int errCode = 0, radio = 0; + struct cx231xx *dev = NULL; + struct cx231xx_fh *fh; + enum v4l2_buf_type fh_type = 0; + + dev = cx231xx_get_device(minor, &fh_type, &radio); + if (NULL == dev) + return -ENODEV; + + mutex_lock(&dev->lock); + + cx231xx_videodbg("open minor=%d type=%s users=%d\n", + minor, v4l2_type_names[fh_type], dev->users); + +#if 0 /* Keep */ + errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); + if (errCode < 0) { + cx231xx_errdev + ("Device locked on digital mode. Can't open analog\n"); + mutex_unlock(&dev->lock); + return -EBUSY; + } +#endif + + fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL); + if (!fh) { + cx231xx_errdev("cx231xx-video.c: Out of memory?!\n"); + mutex_unlock(&dev->lock); + return -ENOMEM; + } + fh->dev = dev; + fh->radio = radio; + fh->type = fh_type; + filp->private_data = fh; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + dev->width = norm_maxw(dev); + dev->height = norm_maxh(dev); + dev->hscale = 0; + dev->vscale = 0; + + /* Power up in Analog TV mode */ + cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV); + +#if 0 /* Keep */ + cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); +#endif + cx231xx_resolution_set(dev); + + /* set video alternate setting */ + cx231xx_set_video_alternate(dev); + + /* Needed, since GPIO might have disabled power of + some i2c device */ + cx231xx_config_i2c(dev); + + /* device needs to be initialized before isoc transfer */ + dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; + video_mux(dev, dev->video_input); + + } + if (fh->radio) { + cx231xx_videodbg("video_open: setting radio device\n"); + + /* cx231xx_start_radio(dev); */ + + cx231xx_i2c_call_clients(&dev->i2c_bus[1], AUDC_SET_RADIO, + NULL); + } + + dev->users++; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops, + NULL, &dev->video_mode.slock, + fh->type, V4L2_FIELD_INTERLACED, + sizeof(struct cx231xx_buffer), fh); + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + /* Set the required alternate setting VBI interface works in + Bulk mode only */ + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops, + NULL, &dev->vbi_mode.slock, + fh->type, V4L2_FIELD_SEQ_TB, + sizeof(struct cx231xx_buffer), fh); + } + + mutex_unlock(&dev->lock); + + return errCode; +} + +/* + * cx231xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void cx231xx_release_analog_resources(struct cx231xx *dev) +{ + + /*FIXME: I2C IR should be disconnected */ + + if (dev->radio_dev) { + if (-1 != dev->radio_dev->minor) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + if (dev->vbi_dev) { + cx231xx_info("V4L2 device /dev/vbi%d deregistered\n", + dev->vbi_dev->num); + if (-1 != dev->vbi_dev->minor) + video_unregister_device(dev->vbi_dev); + else + video_device_release(dev->vbi_dev); + dev->vbi_dev = NULL; + } + if (dev->vdev) { + cx231xx_info("V4L2 device /dev/video%d deregistered\n", + dev->vdev->num); + if (-1 != dev->vdev->minor) + video_unregister_device(dev->vdev); + else + video_device_release(dev->vdev); + dev->vdev = NULL; + } +} + +/* + * cx231xx_v4l2_close() + * stops streaming and deallocates all resources allocated by the v4l2 + * calls and ioctls + */ +static int cx231xx_v4l2_close(struct file *filp) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + + cx231xx_videodbg("users=%d\n", dev->users); + + mutex_lock(&dev->lock); + + if (res_check(fh)) + res_free(fh); + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + /* the device is already disconnect, + free the remaining resources */ + if (dev->state & DEV_DISCONNECTED) { + cx231xx_release_resources(dev); + mutex_unlock(&dev->lock); + kfree(dev); + return 0; + } + + /* do this before setting alternate! */ + cx231xx_uninit_vbi_isoc(dev); + + /* set alternate 0 */ + if (!dev->vbi_or_sliced_cc_mode) + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + else + cx231xx_set_alt_setting(dev, INDEX_HANC, 0); + + kfree(fh); + dev->users--; + wake_up_interruptible_nr(&dev->open, 1); + mutex_unlock(&dev->lock); + return 0; + } + + if (dev->users == 1) { + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + /* the device is already disconnect, + free the remaining resources */ + if (dev->state & DEV_DISCONNECTED) { + cx231xx_release_resources(dev); + mutex_unlock(&dev->lock); + kfree(dev); + return 0; + } + + /* Save some power by putting tuner to sleep */ + cx231xx_i2c_call_clients(&dev->i2c_bus[1], TUNER_SET_STANDBY, + NULL); + + /* do this before setting alternate! */ + cx231xx_uninit_isoc(dev); + cx231xx_set_mode(dev, CX231XX_SUSPEND); + + /* set alternate 0 */ + cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); + } + kfree(fh); + dev->users--; + wake_up_interruptible_nr(&dev->open, 1); + mutex_unlock(&dev->lock); + return 0; +} + +/* + * cx231xx_v4l2_read() + * will allocate buffers when called for the first time + */ +static ssize_t +cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) { + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + + return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; +} + +/* + * cx231xx_v4l2_poll() + * will allocate buffers when called for the first time + */ +static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table * wait) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return POLLERR; + + if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) || + (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)) + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + else + return POLLERR; +} + +/* + * cx231xx_v4l2_mmap() + */ +static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + + cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - + (unsigned long)vma->vm_start, rc); + + return rc; +} + +static const struct v4l2_file_operations cx231xx_v4l_fops = { + .owner = THIS_MODULE, + .open = cx231xx_v4l2_open, + .release = cx231xx_v4l2_close, + .read = cx231xx_v4l2_read, + .poll = cx231xx_v4l2_poll, + .mmap = cx231xx_v4l2_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_std = vidioc_g_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +}; + +static struct video_device cx231xx_vbi_template; + +static const struct video_device cx231xx_video_template = { + .fops = &cx231xx_v4l_fops, + .release = video_device_release, + .ioctl_ops = &video_ioctl_ops, + .minor = -1, + .tvnorms = V4L2_STD_ALL, + .current_norm = V4L2_STD_PAL, +}; + +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = cx231xx_v4l2_open, + .release = cx231xx_v4l2_close, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static struct video_device cx231xx_radio_template = { + .name = "cx231xx-radio", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, + .minor = -1, +}; + +/******************************** usb interface ******************************/ + +static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, + const struct video_device + *template, const char *type_name) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + + *vfd = *template; + vfd->minor = -1; + vfd->parent = &dev->udev->dev; + vfd->release = video_device_release; + vfd->debug = video_debug; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); + + return vfd; +} + +int cx231xx_register_analog_devices(struct cx231xx *dev) +{ + int ret; + + cx231xx_info("%s()\n", __func__); + + cx231xx_info("%s: v4l2 driver version %d.%d.%d\n", + dev->name, + (CX231XX_VERSION_CODE >> 16) & 0xff, + (CX231XX_VERSION_CODE >> 8) & 0xff, + CX231XX_VERSION_CODE & 0xff); + + /* set default norm */ + /*dev->norm = cx231xx_video_template.current_norm; */ + dev->width = norm_maxw(dev); + dev->height = norm_maxh(dev); + dev->interlaced = 0; + dev->hscale = 0; + dev->vscale = 0; + + /* Analog specific initialization */ + dev->format = &format[0]; + /* video_mux(dev, dev->video_input); */ + + /* Audio defaults */ + dev->mute = 1; + dev->volume = 0x1f; + + /* enable vbi capturing */ + /* write code here... */ + + /* allocate and fill video video_device struct */ + dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video"); + if (!dev->vdev) { + cx231xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + + /* register v4l2 video video_device */ + ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, + video_nr[dev->devno]); + if (ret) { + cx231xx_errdev("unable to register video device (error=%i).\n", + ret); + return ret; + } + + cx231xx_info("%s/0: registered device video%d [v4l2]\n", + dev->name, dev->vdev->num); + + /* Initialize VBI template */ + memcpy(&cx231xx_vbi_template, &cx231xx_video_template, + sizeof(cx231xx_vbi_template)); + strcpy(cx231xx_vbi_template.name, "cx231xx-vbi"); + + /* Allocate and fill vbi video_device struct */ + dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi"); + + /* register v4l2 vbi video_device */ + ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]); + if (ret < 0) { + cx231xx_errdev("unable to register vbi device\n"); + return ret; + } + + cx231xx_info("%s/0: registered device vbi%d\n", + dev->name, dev->vbi_dev->num); + + if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) { + dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template, + "radio"); + if (!dev->radio_dev) { + cx231xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr[dev->devno]); + if (ret < 0) { + cx231xx_errdev("can't register radio device\n"); + return ret; + } + cx231xx_info("Registered radio device as /dev/radio%d\n", + dev->radio_dev->num); + } + + cx231xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", + dev->vdev->num, dev->vbi_dev->num); + + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx.h b/linux/drivers/media/video/cx231xx/cx231xx.h new file mode 100644 index 000000000..4606f27b4 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx.h @@ -0,0 +1,776 @@ +/* + cx231xx.h - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx 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. + + 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 _CX231XX_H +#define _CX231XX_H + +#include "compat.h" +#include <linux/videodev2.h> +#include <media/videobuf-vmalloc.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/mutex.h> +#include <media/ir-kbd-i2c.h> +#if defined(CONFIG_VIDEO_CX231XX_DVB) || \ + defined(CONFIG_VIDEO_CX231XX_DVB_MODULE) +#include <media/videobuf-dvb.h> +#endif + +#include "cx231xx-reg.h" +#include "cx231xx-pcb-cfg.h" +#include "cx231xx-conf-reg.h" + +#define CX231XX_VERSION_CODE KERNEL_VERSION(0, 1, 0) +#define DRIVER_NAME "cx231xx" +#define PWR_SLEEP_INTERVAL 5 + +/* I2C addresses for control block in Cx231xx */ +#define Colibri_DEVICE_ADDRESS 0x60 +#define Flatrion_DEVICE_ADDRESS 0x98 +#define HAMMERHEAD_I2C_ADDRESS 0x88 +#define DIF_USE_BASEBAND 0xFFFFFFFF + +/* Boards supported by driver */ +#define CX231XX_BOARD_UNKNOWN 0 +#define CX231XX_BOARD_CNXT_RDE_250 1 +#define CX231XX_BOARD_CNXT_RDU_250 2 + +/* Limits minimum and default number of buffers */ +#define CX231XX_MIN_BUF 4 +#define CX231XX_DEF_BUF 12 +#define CX231XX_DEF_VBI_BUF 6 + +#define VBI_LINE_COUNT 17 +#define VBI_LINE_LENGTH 1440 + +/*Limits the max URB message size */ +#define URB_MAX_CTRL_SIZE 80 + +/* Params for validated field */ +#define CX231XX_BOARD_NOT_VALIDATED 1 +#define CX231XX_BOARD_VALIDATED 0 + +/* maximum number of cx231xx boards */ +#define CX231XX_MAXBOARDS 8 + +/* maximum number of frames that can be queued */ +#define CX231XX_NUM_FRAMES 5 + +/* number of buffers for isoc transfers */ +#define CX231XX_NUM_BUFS 8 + +/* number of packets for each buffer + windows requests only 40 packets .. so we better do the same + this is what I found out for all alternate numbers there! + */ +#define CX231XX_NUM_PACKETS 40 + +/* default alternate; 0 means choose the best */ +#define CX231XX_PINOUT 0 + +#define CX231XX_INTERLACED_DEFAULT 1 + +/* time to wait when stopping the isoc transfer */ +#define CX231XX_URB_TIMEOUT \ + msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS) + +enum cx231xx_mode { + CX231XX_SUSPEND, + CX231XX_ANALOG_MODE, + CX231XX_DIGITAL_MODE, +}; + +enum cx231xx_std_mode { + CX231XX_TV_AIR = 0, + CX231XX_TV_CABLE +}; + +enum cx231xx_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +struct cx231xx; + +struct cx231xx_usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct cx231xx_buffer *buf; + + /* Stores the number of received fields */ + int nfields; + + /* isoc urb callback */ + int (*isoc_copy) (struct cx231xx *dev, struct urb *urb); +}; + +struct cx231xx_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int reg; +}; + +/* buffer for one video frame */ +struct cx231xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct list_head frame; + int top_field; + int receiving; +}; + +struct cx231xx_dmaqueue { + struct list_head active; + struct list_head queued; + + wait_queue_head_t wq; + + /* Counters to control buffer fill */ + int pos; + u8 is_partial_line; + u8 partial_buf[8]; + u8 last_sav; + int current_field; + u32 bytes_left_in_line; + u32 lines_completed; + u8 field1_done; + u32 lines_per_field; +}; + +/* inputs */ + +#define MAX_CX231XX_INPUT 4 + +enum cx231xx_itype { + CX231XX_VMUX_COMPOSITE1 = 1, + CX231XX_VMUX_SVIDEO, + CX231XX_VMUX_TELEVISION, + CX231XX_VMUX_CABLE, + CX231XX_RADIO, + CX231XX_VMUX_DVB, + CX231XX_VMUX_DEBUG +}; + +enum cx231xx_v_input { + CX231XX_VIN_1_1 = 0x1, + CX231XX_VIN_2_1, + CX231XX_VIN_3_1, + CX231XX_VIN_4_1, + CX231XX_VIN_1_2 = 0x01, + CX231XX_VIN_2_2, + CX231XX_VIN_3_2, + CX231XX_VIN_1_3 = 0x1, + CX231XX_VIN_2_3, + CX231XX_VIN_3_3, +}; + +/* cx231xx has two audio inputs: tuner and line in */ +enum cx231xx_amux { + /* This is the only entry for cx231xx tuner input */ + CX231XX_AMUX_VIDEO, /* cx231xx tuner */ + CX231XX_AMUX_LINE_IN, /* Line In */ +}; + +struct cx231xx_reg_seq { + unsigned char bit; + unsigned char val; + int sleep; +}; + +struct cx231xx_input { + enum cx231xx_itype type; + unsigned int vmux; + enum cx231xx_amux amux; + struct cx231xx_reg_seq *gpio; +}; + +#define INPUT(nr) (&cx231xx_boards[dev->model].input[nr]) + +enum cx231xx_decoder { + CX231XX_NODECODER, + CX231XX_AVDECODER +}; + +enum CX231XX_I2C_MASTER_PORT { + I2C_0 = 0, + I2C_1 = 1, + I2C_2 = 2, + I2C_3 = 3 +}; + +struct cx231xx_board { + char *name; + int vchannels; + int tuner_type; + int tuner_addr; + v4l2_std_id norm; /* tv norm */ + + /* demod related */ + int demod_addr; + u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */ + + /* GPIO Pins */ + struct cx231xx_reg_seq *dvb_gpio; + struct cx231xx_reg_seq *suspend_gpio; + struct cx231xx_reg_seq *tuner_gpio; + u8 tuner_sif_gpio; + u8 tuner_scl_gpio; + u8 tuner_sda_gpio; + + /* PIN ctrl */ + u32 ctl_pin_status_mask; + u8 agc_analog_digital_select_gpio; + u32 gpio_pin_status_mask; + + /* i2c masters */ + u8 tuner_i2c_master; + u8 demod_i2c_master; + + unsigned int max_range_640_480:1; + unsigned int has_dvb:1; + unsigned int valid:1; + + unsigned char xclk, i2c_speed; + + enum cx231xx_decoder decoder; + + struct cx231xx_input input[MAX_CX231XX_INPUT]; + struct cx231xx_input radio; + IR_KEYTAB_TYPE *ir_codes; +}; + +/* device states */ +enum cx231xx_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum AFE_MODE { + AFE_MODE_LOW_IF, + AFE_MODE_BASEBAND, + AFE_MODE_EU_HI_IF, + AFE_MODE_US_HI_IF, + AFE_MODE_JAPAN_HI_IF +}; + +enum AUDIO_INPUT { + AUDIO_INPUT_MUTE, + AUDIO_INPUT_LINE, + AUDIO_INPUT_TUNER_TV, + AUDIO_INPUT_SPDIF, + AUDIO_INPUT_TUNER_FM +}; + +#define CX231XX_AUDIO_BUFS 5 +#define CX231XX_NUM_AUDIO_PACKETS 64 +#define CX231XX_CAPTURE_STREAM_EN 1 +#define CX231XX_STOP_AUDIO 0 +#define CX231XX_START_AUDIO 1 + +/* cx231xx extensions */ +#define CX231XX_AUDIO 0x10 +#define CX231XX_DVB 0x20 + +struct cx231xx_audio { + char name[50]; + char *transfer_buffer[CX231XX_AUDIO_BUFS]; + struct urb *urb[CX231XX_AUDIO_BUFS]; + struct usb_device *udev; + unsigned int capture_transfer_done; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *capture_pcm_substream; +#else + struct snd_pcm_substream *capture_pcm_substream; +#endif + + unsigned int hwptr_done_capture; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_card_t *sndcard; +#else + struct snd_card *sndcard; +#endif + + int users, shutdown; + enum cx231xx_stream_state capture_stream; + spinlock_t slock; + + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + u16 end_point_addr; +}; + +struct cx231xx; + +struct cx231xx_fh { + struct cx231xx *dev; + unsigned int stream_on:1; /* Locks streams */ + int radio; + + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + +/*****************************************************************/ +/* set/get i2c */ +/* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */ +#define I2C_SPEED_1M 0x0 +#define I2C_SPEED_400K 0x1 +#define I2C_SPEED_100K 0x2 +#define I2C_SPEED_5M 0x3 + +/* 0-- STOP transaction */ +#define I2C_STOP 0x0 +/* 1-- do not transmit STOP at end of transaction */ +#define I2C_NOSTOP 0x1 +/* 1--alllow slave to insert clock wait states */ +#define I2C_SYNC 0x1 + +struct cx231xx_i2c { + struct cx231xx *dev; + + int nr; + + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + u32 i2c_rc; + + /* different settings for each bus */ + u8 i2c_period; + u8 i2c_nostop; + u8 i2c_reserve; +}; + +struct cx231xx_i2c_xfer_data { + u8 dev_addr; + u8 direction; /* 1 - IN, 0 - OUT */ + u8 saddr_len; /* sub address len */ + u16 saddr_dat; /* sub addr data */ + u8 buf_size; /* buffer size */ + u8 *p_buffer; /* pointer to the buffer */ +}; + +struct VENDOR_REQUEST_IN { + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; + u8 direction; + u8 bData; + u8 *pBuff; +}; + +struct cx231xx_ctrl { + struct v4l2_queryctrl v; + u32 off; + u32 reg; + u32 mask; + u32 shift; +}; + +enum TRANSFER_TYPE { + Raw_Video = 0, + Audio, + Vbi, /* VANC */ + Sliced_cc, /* HANC */ + TS1_serial_mode, + TS2, + TS1_parallel_mode +} ; + +struct cx231xx_video_mode { + /* Isoc control struct */ + struct cx231xx_dmaqueue vidq; + struct cx231xx_usb_isoc_ctl isoc_ctl; + spinlock_t slock; + + /* usb transfer */ + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + u16 end_point_addr; +}; + +/* main device struct */ +struct cx231xx { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + + struct cx231xx_board board; + + unsigned int stream_on:1; /* Locks streams */ + unsigned int vbi_stream_on:1; /* Locks streams for VBI */ + unsigned int has_audio_class:1; + unsigned int has_alsa_audio:1; + + struct cx231xx_fmt *format; + + struct cx231xx_IR *ir; + + struct list_head devlist; + + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + + /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ + struct cx231xx_i2c i2c_bus[3]; + unsigned int xc_fw_load_done:1; + struct mutex gpio_i2c_lock; + + /* video for linux */ + int users; /* user count for exclusive use */ + struct video_device *vdev; /* video for linux device struct */ + v4l2_std_id norm; /* selected tv norm */ + int ctl_freq; /* selected frequency */ + unsigned int ctl_ainput; /* selected audio input */ + int mute; + int volume; + + /* frame properties */ + int width; /* current frame width */ + int height; /* current frame height */ + unsigned hscale; /* horizontal scale factor (see datasheet) */ + unsigned vscale; /* vertical scale factor (see datasheet) */ + int interlaced; /* 1=interlace fileds, 0=just top fileds */ + + struct cx231xx_audio adev; + + /* states */ + enum cx231xx_dev_state state; + + struct work_struct request_module_wk; + + /* locks */ + struct mutex lock; + struct mutex ctrl_urb_lock; /* protects urb_buf */ + struct list_head inqueue, outqueue; + wait_queue_head_t open, wait_frame, wait_stream; + struct video_device *vbi_dev; + struct video_device *radio_dev; + + unsigned char eedata[256]; + + struct cx231xx_video_mode video_mode; + struct cx231xx_video_mode vbi_mode; + struct cx231xx_video_mode sliced_cc_mode; + struct cx231xx_video_mode ts1_mode; + + struct usb_device *udev; /* the usb device */ + char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ + + /* helper funcs that call usb_control_msg */ + int (*cx231xx_read_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); + int (*cx231xx_write_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); + int (*cx231xx_send_usb_command) (struct cx231xx_i2c *i2c_bus, + struct cx231xx_i2c_xfer_data *req_data); + int (*cx231xx_gpio_i2c_read) (struct cx231xx *dev, u8 dev_addr, + u8 *buf, u8 len); + int (*cx231xx_gpio_i2c_write) (struct cx231xx *dev, u8 dev_addr, + u8 *buf, u8 len); + + int (*cx231xx_set_analog_freq) (struct cx231xx *dev, u32 freq); + int (*cx231xx_reset_analog_tuner) (struct cx231xx *dev); + + enum cx231xx_mode mode; + + struct cx231xx_dvb *dvb; + + /* Cx231xx supported PCB config's */ + struct pcb_config current_pcb_config; + u8 current_scenario_idx; + u8 interface_count; + u8 max_iad_interface_count; + + /* GPIO related register direction and values */ + u32 gpio_dir; + u32 gpio_val; + + /* Power Modes */ + int power_mode; + + /* colibri parameters */ + enum AFE_MODE colibri_mode; + u32 colibri_ref_count; + + /* video related parameters */ + u32 video_input; + u32 active_mode; + u8 vbi_or_sliced_cc_mode; /* 0 - vbi ; 1 - sliced cc mode */ + enum cx231xx_std_mode std_mode; /* 0 - Air; 1 - cable */ + +}; + +struct cx231xx_ops { + struct list_head next; + char *name; + int id; + int (*init) (struct cx231xx *); + int (*fini) (struct cx231xx *); +}; + +/* call back functions in dvb module */ +int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq); +int cx231xx_reset_analog_tuner(struct cx231xx *dev); + +/* Provided by cx231xx-i2c.c */ +void cx231xx_i2c_call_clients(struct cx231xx_i2c *bus, unsigned int cmd, + void *arg); +void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c); +int cx231xx_i2c_register(struct cx231xx_i2c *bus); +int cx231xx_i2c_unregister(struct cx231xx_i2c *bus); + +/* Internal block control functions */ +int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u8 saddr_len, u32 *data, u8 data_len); +int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u8 saddr_len, u32 data, u8 data_len); +int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size, + u16 register_address, u8 bit_start, u8 bit_end, + u32 value); +int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u32 mask, u32 value); +u32 cx231xx_set_field(u32 field_mask, u32 data); + +/* Colibri related functions */ +int cx231xx_colibri_init_super_block(struct cx231xx *dev, u32 ref_count); +int cx231xx_colibri_init_channels(struct cx231xx *dev); +int cx231xx_colibri_setup_AFE_for_baseband(struct cx231xx *dev); +int cx231xx_colibri_set_input_mux(struct cx231xx *dev, u32 input_mux); +int cx231xx_colibri_set_mode(struct cx231xx *dev, enum AFE_MODE mode); +int cx231xx_colibri_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode); +int cx231xx_colibri_adjust_ref_count(struct cx231xx *dev, u32 video_input); + +/* flatiron related functions */ +int cx231xx_flatiron_initialize(struct cx231xx *dev); +int cx231xx_flatiron_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode); +int cx231xx_flatiron_set_audio_input(struct cx231xx *dev, u8 audio_input); + +/* DIF related functions */ +int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode, + u32 function_mode, u32 standard); +int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard); +int cx231xx_tuner_pre_channel_change(struct cx231xx *dev); +int cx231xx_tuner_post_channel_change(struct cx231xx *dev); + +/* video parser functions */ +u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, + u32 *p_bytes_used); +u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf, + u32 *p_bytes_used); +int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy); +void cx231xx_reset_video_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q); +u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q); +u32 cx231xx_copy_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_line, u32 length, int field_number); +u32 cx231xx_get_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 sav_eav, u8 *p_buffer, u32 buffer_size); +void cx231xx_swab(u16 *from, u16 *to, u16 len); + +/* Provided by cx231xx-core.c */ + +u32 cx231xx_request_buffers(struct cx231xx *dev, u32 count); +void cx231xx_queue_unusedframes(struct cx231xx *dev); +void cx231xx_release_buffers(struct cx231xx *dev); + +/* read from control pipe */ +int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); + +/* write to control pipe */ +int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); +int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode); + +int cx231xx_send_vendor_cmd(struct cx231xx *dev, + struct VENDOR_REQUEST_IN *ven_req); +int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus, + struct cx231xx_i2c_xfer_data *req_data); + +/* Gpio related functions */ +int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val, + u8 len, u8 request, u8 direction); +int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); +int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); +int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value); +int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number, + int pin_value); + +int cx231xx_gpio_i2c_start(struct cx231xx *dev); +int cx231xx_gpio_i2c_end(struct cx231xx *dev); +int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data); +int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf); +int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev); +int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev); +int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev); + +int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len); +int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len); + +/* audio related functions */ +int cx231xx_set_audio_decoder_input(struct cx231xx *dev, + enum AUDIO_INPUT audio_input); + +int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type); +int cx231xx_resolution_set(struct cx231xx *dev); +int cx231xx_set_video_alternate(struct cx231xx *dev); +int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt); +int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, + struct urb *urb)); +void cx231xx_uninit_isoc(struct cx231xx *dev); +int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode); +int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio); + +/* Device list functions */ +void cx231xx_release_resources(struct cx231xx *dev); +void cx231xx_release_analog_resources(struct cx231xx *dev); +int cx231xx_register_analog_devices(struct cx231xx *dev); +void cx231xx_remove_from_devlist(struct cx231xx *dev); +void cx231xx_add_into_devlist(struct cx231xx *dev); +struct cx231xx *cx231xx_get_device(int minor, + enum v4l2_buf_type *fh_type, int *has_radio); +void cx231xx_init_extension(struct cx231xx *dev); +void cx231xx_close_extension(struct cx231xx *dev); + +/* hardware init functions */ +int cx231xx_dev_init(struct cx231xx *dev); +void cx231xx_dev_uninit(struct cx231xx *dev); +void cx231xx_config_i2c(struct cx231xx *dev); +int cx231xx_config(struct cx231xx *dev); + +/* Stream control functions */ +int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask); +int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask); + +int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type); + +/* Power control functions */ +int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode); +int cx231xx_power_suspend(struct cx231xx *dev); + +/* chip specific control functions */ +int cx231xx_init_ctrl_pin_status(struct cx231xx *dev); +int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, + u8 analog_or_digital); +int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex); + +/* video audio decoder related functions */ +void video_mux(struct cx231xx *dev, int index); +int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input); +int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input); +int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev); +int cx231xx_set_audio_input(struct cx231xx *dev, u8 input); +void get_scale(struct cx231xx *dev, + unsigned int width, unsigned int height, + unsigned int *hscale, unsigned int *vscale); + +/* Provided by cx231xx-video.c */ +int cx231xx_register_extension(struct cx231xx_ops *dev); +void cx231xx_unregister_extension(struct cx231xx_ops *dev); +void cx231xx_init_extension(struct cx231xx *dev); +void cx231xx_close_extension(struct cx231xx *dev); + +/* Provided by cx231xx-cards.c */ +extern void cx231xx_pre_card_setup(struct cx231xx *dev); +extern void cx231xx_card_setup(struct cx231xx *dev); +extern struct cx231xx_board cx231xx_boards[]; +extern struct usb_device_id cx231xx_id_table[]; +extern const unsigned int cx231xx_bcount; +void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir); +int cx231xx_tuner_callback(void *ptr, int component, int command, int arg); + +/* Provided by cx231xx-input.c */ +int cx231xx_ir_init(struct cx231xx *dev); +int cx231xx_ir_fini(struct cx231xx *dev); + +/* printk macros */ + +#define cx231xx_err(fmt, arg...) do {\ + printk(KERN_ERR fmt , ##arg); } while (0) + +#define cx231xx_errdev(fmt, arg...) do {\ + printk(KERN_ERR "%s: "fmt,\ + dev->name , ##arg); } while (0) + +#define cx231xx_info(fmt, arg...) do {\ + printk(KERN_INFO "%s: "fmt,\ + dev->name , ##arg); } while (0) +#define cx231xx_warn(fmt, arg...) do {\ + printk(KERN_WARNING "%s: "fmt,\ + dev->name , ##arg); } while (0) + +static inline unsigned int norm_maxw(struct cx231xx *dev) +{ + if (dev->board.max_range_640_480) + return 640; + else + return 720; +} + +static inline unsigned int norm_maxh(struct cx231xx *dev) +{ + if (dev->board.max_range_640_480) + return 480; + else + return (dev->norm & V4L2_STD_625_50) ? 576 : 480; +} +#endif diff --git a/linux/drivers/media/video/cx23885/Kconfig b/linux/drivers/media/video/cx23885/Kconfig index 28896aa31..e603ceb28 100644 --- a/linux/drivers/media/video/cx23885/Kconfig +++ b/linux/drivers/media/video/cx23885/Kconfig @@ -20,10 +20,10 @@ config VIDEO_CX23885 select DVB_STV6110 if !DVB_FE_CUSTOMISE select DVB_STV0900 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c index 6ae7ee247..d894d4900 100644 --- a/linux/drivers/media/video/cx23885/cx23885-core.c +++ b/linux/drivers/media/video/cx23885/cx23885-core.c @@ -1710,7 +1710,8 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) PCI_MSK_GPIO1); } - if ((pci_status & PCI_MSK_GPIO0) || (pci_status & PCI_MSK_GPIO1)) + if (cx23885_boards[dev->board].cimax > 0 && + ((pci_status & PCI_MSK_GPIO0) || (pci_status & PCI_MSK_GPIO1))) /* handled += cx23885_irq_gpio(dev, pci_status); */ handled += netup_ci_slot_status(dev, pci_status); @@ -1786,7 +1787,12 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, } pci_set_drvdata(pci_dev, dev); - cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */ + + switch (dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */ + break; + } return 0; diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 5529dcf6a..364543987 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -779,7 +779,11 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port) if (fe0->dvb.frontend) videobuf_dvb_unregister_bus(&port->frontends); - netup_ci_exit(port); + switch (port->dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + netup_ci_exit(port); + break; + } return 0; } diff --git a/linux/drivers/media/video/cx23885/cx23885-video.c b/linux/drivers/media/video/cx23885/cx23885-video.c index 131fc740a..ba22520a2 100644 --- a/linux/drivers/media/video/cx23885/cx23885-video.c +++ b/linux/drivers/media/video/cx23885/cx23885-video.c @@ -36,11 +36,6 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#ifdef CONFIG_VIDEO_V4L1_COMPAT -/* Include V4L1 specific functions. Should be removed soon */ -#include <linux/videodev.h> -#endif - MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/cx25840/cx25840-audio.c b/linux/drivers/media/video/cx25840/cx25840-audio.c index 3ee7d7a7d..9dcdef03e 100644 --- a/linux/drivers/media/video/cx25840/cx25840-audio.c +++ b/linux/drivers/media/video/cx25840/cx25840-audio.c @@ -33,7 +33,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) /* common for all inputs and rates */ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ - if (!state->is_cx23885) + if (!state->is_cx23885 && !state->is_cx231xx) cx25840_write(client, 0x127, 0x50); if (state->aud_input != CX25840_AUDIO_SERIAL) { @@ -44,11 +44,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) * so avoid destroying registers. */ break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1006040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x01bb39ee); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x1006040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x01bb39ee); + } if (state->is_cx25836) break; @@ -65,11 +68,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) * so avoid destroying registers. */ break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1009040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x1009040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + } if (state->is_cx25836) break; @@ -86,11 +92,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) * so avoid destroying registers. */ break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x100a040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x0098d6e5); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x100a040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x0098d6e5); + } if (state->is_cx25836) break; @@ -109,11 +118,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) * so avoid destroying registers. */ break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1e08040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x012a0869); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x1e08040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x012a0869); + } if (state->is_cx25836) break; @@ -137,11 +149,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1809040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x1809040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + } if (state->is_cx25836) break; @@ -156,7 +171,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) break; case 48000: - if (!state->is_cx23885) { + if (!state->is_cx23885 && !state->is_cx231xx) { /* VID_PLL and AUX_PLL */ cx25840_write4(client, 0x108, 0x180a040f); @@ -167,7 +182,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) if (state->is_cx25836) break; - if (!state->is_cx23885) { + if (!state->is_cx23885 && !state->is_cx231xx) { /* src1_ctl */ cx25840_write4(client, 0x8f8, 0x08018000); @@ -228,10 +243,9 @@ void cx25840_audio_set_path(struct i2c_client *client) /* deassert soft reset */ cx25840_and_or(client, 0x810, ~0x1, 0x00); - if (state->is_cx23885) { - /* Ensure the controller is running when we exit */ + /* Ensure the controller is running when we exit */ + if (state->is_cx23885 || state->is_cx231xx) cx25840_and_or(client, 0x803, ~0x10, 0x10); - } } static int get_volume(struct i2c_client *client) diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index 921cc96fc..5387a846f 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -367,6 +367,85 @@ static void cx23885_initialize(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static void cx231xx_initialize(struct i2c_client *client) +{ + DEFINE_WAIT(wait); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + struct workqueue_struct *q; + + /* Internal Reset */ + cx25840_and_or(client, 0x102, ~0x01, 0x01); + cx25840_and_or(client, 0x102, ~0x01, 0x00); + + /* Stop microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + + /* DIF in reset? */ + cx25840_write(client, 0x398, 0); + + /* Trust the default xtal, no division */ + /* This changes for the cx23888 products */ + cx25840_write(client, 0x2, 0x76); + + /* Bring down the regulator for AUX clk */ + cx25840_write(client, 0x1, 0x40); + + /* Disable DIF bypass */ + cx25840_write4(client, 0x33c, 0x00000001); + + /* DIF Src phase inc */ + cx25840_write4(client, 0x340, 0x0df7df83); + + /* Luma */ + cx25840_write4(client, 0x414, 0x00107d12); + + /* Chroma */ + cx25840_write4(client, 0x420, 0x3d008282); + + /* ADC2 input select */ + cx25840_write(client, 0x102, 0x10); + + /* VIN1 & VIN5 */ + cx25840_write(client, 0x103, 0x11); + + /* Enable format auto detect */ + cx25840_write(client, 0x400, 0); +#if 0 + /* Force to NTSC-M and Disable autoconf regs */ + cx25840_write(client, 0x400, 0x21); +#endif + /* Fast subchroma lock */ + /* White crush, Chroma AGC & Chroma Killer enabled */ + cx25840_write(client, 0x401, 0xe8); + + /* Do the firmware load in a work handler to prevent. + Otherwise the kernel is blocked waiting for the + bit-banging i2c interface to finish uploading the + firmware. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) + INIT_WORK(&state->fw_work, cx25840_work_handler); +#else + INIT_WORK(&state->fw_work, cx25840_work_handler, state); +#endif + init_waitqueue_head(&state->fw_wait); + q = create_singlethread_workqueue("cx25840_fw"); + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + + cx25840_std_setup(client); + + /* (re)set input */ + set_input(client, state->vid_input, state->aud_input); + + /* start microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); +} + +/* ----------------------------------------------------------------------- */ + void cx25840_std_setup(struct i2c_client *client) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); @@ -436,39 +515,41 @@ void cx25840_std_setup(struct i2c_client *client) } /* DEBUG: Displays configured PLL frequency */ - pll_int = cx25840_read(client, 0x108); - pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; - pll_post = cx25840_read(client, 0x109); - v4l_dbg(1, cx25840_debug, client, - "PLL regs = int: %u, frac: %u, post: %u\n", - pll_int, pll_frac, pll_post); - - if (pll_post) { - int fin, fsc; - int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; - - pll /= pll_post; - v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", - pll / 1000000, pll % 1000000); - v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", - pll / 8000000, (pll / 8) % 1000000); - - fin = ((u64)src_decimation * pll) >> 12; - v4l_dbg(1, cx25840_debug, client, - "ADC Sampling freq = %d.%06d MHz\n", - fin / 1000000, fin % 1000000); - - fsc = (((u64)sc) * pll) >> 24L; + if (!state->is_cx231xx) { + pll_int = cx25840_read(client, 0x108); + pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; + pll_post = cx25840_read(client, 0x109); v4l_dbg(1, cx25840_debug, client, - "Chroma sub-carrier freq = %d.%06d MHz\n", - fsc / 1000000, fsc % 1000000); - - v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " - "vblank %i, vactive %i, vblank656 %i, src_dec %i, " - "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " - "sc 0x%06x\n", - hblank, hactive, vblank, vactive, vblank656, - src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + "PLL regs = int: %u, frac: %u, post: %u\n", + pll_int, pll_frac, pll_post); + + if (pll_post) { + int fin, fsc; + int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; + + pll /= pll_post; + v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", + pll / 1000000, pll % 1000000); + v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", + pll / 8000000, (pll / 8) % 1000000); + + fin = ((u64)src_decimation * pll) >> 12; + v4l_dbg(1, cx25840_debug, client, + "ADC Sampling freq = %d.%06d MHz\n", + fin / 1000000, fin % 1000000); + + fsc = (((u64)sc) * pll) >> 24L; + v4l_dbg(1, cx25840_debug, client, + "Chroma sub-carrier freq = %d.%06d MHz\n", + fsc / 1000000, fsc % 1000000); + + v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " + "vblank %i, vactive %i, vblank656 %i, src_dec %i, " + "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " + "sc 0x%06x\n", + hblank, hactive, vblank, vactive, vblank656, + src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + } } /* Sets horizontal blanking delay and active lines */ @@ -618,7 +699,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp * configuration in reg (for the cx23885) so we have no * need to attempt to flip bits for earlier av decoders. */ - if (!state->is_cx23885) { + if (!state->is_cx23885 && !state->is_cx231xx) { switch (aud_input) { case CX25840_AUDIO_SERIAL: /* do nothing, use serial audio input */ @@ -641,7 +722,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp /* Set INPUT_MODE to Composite (0) or S-Video (1) */ cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); - if (!state->is_cx23885) { + if (!state->is_cx23885 && !state->is_cx231xx) { /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ @@ -681,6 +762,19 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp */ cx25840_write(client, 0x918, 0xa0); cx25840_write(client, 0x919, 0x01); + } else if (state->is_cx231xx) { + /* Audio channel 1 src : Parallel 1 */ + cx25840_write(client, 0x124, 0x03); + + /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ + cx25840_write(client, 0x914, 0xa0); + + /* I2S_OUT_CTL: + * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 + * I2S_OUT_MASTER_MODE = Master + */ + cx25840_write(client, 0x918, 0xa0); + cx25840_write(client, 0x919, 0x01); } return 0; @@ -1142,6 +1236,8 @@ static int cx25840_init(struct v4l2_subdev *sd, u32 val) cx25836_initialize(client); else if (state->is_cx23885) cx23885_initialize(client); + else if (state->is_cx231xx) + cx231xx_initialize(client); else cx25840_initialize(client); } @@ -1197,7 +1293,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) v4l_dbg(1, cx25840_debug, client, "%s output\n", enable ? "enable" : "disable"); if (enable) { - if (state->is_cx23885) { + if (state->is_cx23885 || state->is_cx231xx) { u8 v = (cx25840_read(client, 0x421) | 0x0b); cx25840_write(client, 0x421, v); } else { @@ -1207,7 +1303,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) state->is_cx25836 ? 0x04 : 0x07); } } else { - if (state->is_cx23885) { + if (state->is_cx23885 || state->is_cx231xx) { u8 v = cx25840_read(client, 0x421) & ~(0x0b); cx25840_write(client, 0x421, v); } else { @@ -1388,6 +1484,8 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val) cx25836_initialize(client); else if (state->is_cx23885) cx23885_initialize(client); + else if (state->is_cx231xx) + cx231xx_initialize(client); else cx25840_initialize(client); return 0; @@ -1500,6 +1598,8 @@ static int cx25840_probe(struct i2c_client *client, id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; } else if (device_id == 0x1313) { id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; + } else if ((device_id & 0xfff0) == 0x5A30) { + id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); } else { v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); @@ -1522,6 +1622,7 @@ static int cx25840_probe(struct i2c_client *client, state->c = client; state->is_cx25836 = ((device_id & 0xff00) == 0x8300); state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313); + state->is_cx231xx = (device_id == 0x5a3e); state->vid_input = CX25840_COMPOSITE7; state->aud_input = CX25840_AUDIO8; state->audclk_freq = 48000; diff --git a/linux/drivers/media/video/cx25840/cx25840-core.h b/linux/drivers/media/video/cx25840/cx25840-core.h index 0a99c19df..422c63752 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.h +++ b/linux/drivers/media/video/cx25840/cx25840-core.h @@ -51,6 +51,7 @@ struct cx25840_state { u32 rev; int is_cx25836; int is_cx23885; + int is_cx231xx; int is_initialized; wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ struct work_struct fw_work; /* work entry for fw load */ diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c index 01fbe174e..f01456ec4 100644 --- a/linux/drivers/media/video/cx25840/cx25840-firmware.c +++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c @@ -26,6 +26,7 @@ #define FWFILE "v4l-cx25840.fw" #define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw" +#define FWFILE_CX231XX "v4l-cx231xx-avcore-01.fw" /* * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the @@ -97,9 +98,17 @@ int cx25840_loadfw(struct i2c_client *client) u8 buffer[FWSEND]; const u8 *ptr; int size, retval; + int MAX_BUF_SIZE = FWSEND; if (state->is_cx23885) firmware = FWFILE_CX23885; + else if (state->is_cx231xx) + firmware = FWFILE_CX231XX; + + if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) { + v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); + MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ + } if (request_firmware(&fw, firmware, FWDEV(client)) != 0) { v4l_err(client, "unable to open firmware %s\n", firmware); @@ -114,7 +123,7 @@ int cx25840_loadfw(struct i2c_client *client) size = fw->size; ptr = fw->data; while (size > 0) { - int len = min(FWSEND - 2, size); + int len = min(MAX_BUF_SIZE - 2, size); memcpy(buffer + 2, ptr, len); diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index f7171586d..1e78faba6 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -657,8 +657,12 @@ static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, return changed; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0); +#else +static DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0); +#endif #endif static struct snd_kcontrol_new snd_cx88_volume = { diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index f9b5a9c09..b3d966ad5 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1967,6 +1967,39 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = { + .name = "Terratec Cinergy HT PCI MKII", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .radio_type = TUNER_XC2028, + .radio_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x00001, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x0ff, + }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -2376,6 +2409,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0xb200, .subdevice = 0x4200, .card = CX88_BOARD_SATTRADE_ST4200, + }, { + .subvendor = 0x153b, + .subdevice = 0x1177, + .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII, }, }; @@ -2852,6 +2889,7 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) */ break; case CX88_BOARD_PINNACLE_HYBRID_PCTV: + case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: ctl->demod = XC3028_FE_ZARLINK456; ctl->mts = 1; break; diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 488126ad6..10cf470ce 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -242,6 +242,12 @@ static struct mt352_config dvico_fusionhdtv_dual = { .demod_init = dvico_dual_demod_init, }; +static struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .if2 = 45600, +}; + #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) { @@ -1138,6 +1144,16 @@ static int dvb_register(struct cx8802_dev *dev) if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; break; + case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &cx88_terratec_cinergy_ht_pci_mkii_config, + &core->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; + if (attach_xc3028(0x61, dev) < 0) + goto frontend_detach; + } + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", core->name); diff --git a/linux/drivers/media/video/cx88/cx88-input.c b/linux/drivers/media/video/cx88/cx88-input.c index dbb6ee2d9..8a2e631b7 100644 --- a/linux/drivers/media/video/cx88/cx88-input.c +++ b/linux/drivers/media/video/cx88/cx88-input.c @@ -49,8 +49,7 @@ struct cx88_IR { /* poll external decoder */ int polling; - struct work_struct work; - struct timer_list timer; + struct delayed_work work; u32 gpio_addr; u32 last_gpio; u32 mask_keycode; @@ -144,13 +143,6 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) } } -static void ir_timer(unsigned long data) -{ - struct cx88_IR *ir = (struct cx88_IR *)data; - - schedule_work(&ir->work); -} - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) static void cx88_ir_work(void *data) #else @@ -160,23 +152,22 @@ static void cx88_ir_work(struct work_struct *work) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) struct cx88_IR *ir = data; #else - struct cx88_IR *ir = container_of(work, struct cx88_IR, work); + struct cx88_IR *ir = container_of(work, struct cx88_IR, work.work); #endif cx88_ir_handle_key(ir); - mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) { if (ir->polling) { - setup_timer(&ir->timer, ir_timer, (unsigned long)ir); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - INIT_WORK(&ir->work, cx88_ir_work, ir); + INIT_DELAYED_WORK(&ir->work, cx88_ir_work, ir); #else - INIT_WORK(&ir->work, cx88_ir_work); + INIT_DELAYED_WORK(&ir->work, cx88_ir_work); #endif - schedule_work(&ir->work); + schedule_delayed_work(&ir->work, 0); } if (ir->sampling) { core->pci_irqmask |= PCI_INT_IR_SMPINT; @@ -192,10 +183,8 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) core->pci_irqmask &= ~PCI_INT_IR_SMPINT; } - if (ir->polling) { - del_timer_sync(&ir->timer); - flush_scheduled_work(); - } + if (ir->polling) + cancel_delayed_work_sync(&ir->work); } /* ---------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index 005a78aab..827168d67 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -42,11 +42,6 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#ifdef CONFIG_VIDEO_V4L1_COMPAT -/* Include V4L1 specific functions. Should be removed soon */ -#include <linux/videodev.h> -#endif - MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 2227d93c7..1372d2b7b 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -232,6 +232,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_SATTRADE_ST4200 76 #define CX88_BOARD_TBS_8910 77 #define CX88_BOARD_PROF_6200 78 +#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index b97a1bc85..5382a6064 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -69,8 +69,7 @@ struct em28xx_IR { /* poll external decoder */ int polling; - struct work_struct work; - struct timer_list timer; + struct delayed_work work; unsigned int last_toggle:1; unsigned int last_readcount; unsigned int repeat_interval; @@ -298,13 +297,6 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) return; } -static void ir_timer(unsigned long data) -{ - struct em28xx_IR *ir = (struct em28xx_IR *)data; - - schedule_work(&ir->work); -} - #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) static void em28xx_ir_work(void *data) #else @@ -314,28 +306,26 @@ static void em28xx_ir_work(struct work_struct *work) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) struct em28xx_IR *ir = data; #else - struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work); + struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); #endif em28xx_ir_handle_key(ir); - mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } static void em28xx_ir_start(struct em28xx_IR *ir) { - setup_timer(&ir->timer, ir_timer, (unsigned long)ir); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - INIT_WORK(&ir->work, em28xx_ir_work, ir); + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work, ir); #else - INIT_WORK(&ir->work, em28xx_ir_work); + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); #endif - schedule_work(&ir->work); + schedule_delayed_work(&ir->work, 0); } static void em28xx_ir_stop(struct em28xx_IR *ir) { - del_timer_sync(&ir->timer); - flush_scheduled_work(); + cancel_delayed_work_sync(&ir->work); } int em28xx_ir_init(struct em28xx *dev) diff --git a/linux/drivers/media/video/gspca/mr97310a.c b/linux/drivers/media/video/gspca/mr97310a.c index 5ec5ce6e3..2a901a4a6 100644 --- a/linux/drivers/media/video/gspca/mr97310a.c +++ b/linux/drivers/media/video/gspca/mr97310a.c @@ -29,9 +29,7 @@ MODULE_LICENSE("GPL"); /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - u8 sof_read; - u8 header_read; }; /* V4L2 controls supported by the driver */ @@ -285,7 +283,6 @@ 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; unsigned char *sof; sof = pac_find_sof(gspca_dev, data, len); @@ -300,25 +297,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n = 0; frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, n); - sd->header_read = 0; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); + /* Start next frame. */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + pac_sof_marker, sizeof pac_sof_marker); len -= sof - data; data = sof; } - if (sd->header_read < 7) { - int needed; - - /* skip the rest of the header */ - needed = 7 - sd->header_read; - if (len <= needed) { - sd->header_read += len; - return; - } - data += needed; - len -= needed; - sd->header_read = 7; - } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } diff --git a/linux/drivers/media/video/indycam.c b/linux/drivers/media/video/indycam.c index 84b9e4f2b..f2da0550b 100644 --- a/linux/drivers/media/video/indycam.c +++ b/linux/drivers/media/video/indycam.c @@ -19,10 +19,12 @@ #include <linux/mm.h> #include <linux/slab.h> -#include <linux/videodev.h> /* IndyCam decodes stream of photons into digital image representation ;-) */ -#include <linux/video_decoder.h> +#include <linux/videodev2.h> #include <linux/i2c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> #include "indycam.h" @@ -33,6 +35,12 @@ MODULE_VERSION(INDYCAM_MODULE_VERSION); MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); MODULE_LICENSE("GPL"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +static unsigned short normal_i2c[] = { 0x56 >> 1, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; +#endif + // #define INDYCAM_DEBUG #ifdef INDYCAM_DEBUG @@ -44,11 +52,14 @@ MODULE_LICENSE("GPL"); #endif struct indycam { - struct i2c_client *client; + struct v4l2_subdev sd; u8 version; }; -static struct i2c_driver i2c_driver_indycam; +static inline struct indycam *to_indycam(struct v4l2_subdev *sd) +{ + return container_of(sd, struct indycam, sd); +} static const u8 initseq[] = { INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */ @@ -63,8 +74,9 @@ static const u8 initseq[] = { /* IndyCam register handling */ -static int indycam_read_reg(struct i2c_client *client, u8 reg, u8 *value) +static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; if (reg == INDYCAM_REG_RESET) { @@ -87,12 +99,12 @@ static int indycam_read_reg(struct i2c_client *client, u8 reg, u8 *value) return 0; } -static int indycam_write_reg(struct i2c_client *client, u8 reg, u8 value) +static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int err; - if ((reg == INDYCAM_REG_BRIGHTNESS) - || (reg == INDYCAM_REG_VERSION)) { + if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) { dprintk("indycam_write_reg(): " "skipping read-only register %d\n", reg); return 0; @@ -108,13 +120,13 @@ static int indycam_write_reg(struct i2c_client *client, u8 reg, u8 value) return err; } -static int indycam_write_block(struct i2c_client *client, u8 reg, +static int indycam_write_block(struct v4l2_subdev *sd, u8 reg, u8 length, u8 *data) { int i, err; for (i = 0; i < length; i++) { - err = indycam_write_reg(client, reg + i, data[i]); + err = indycam_write_reg(sd, reg + i, data[i]); if (err) return err; } @@ -125,79 +137,78 @@ static int indycam_write_block(struct i2c_client *client, u8 reg, /* Helper functions */ #ifdef INDYCAM_DEBUG -static void indycam_regdump_debug(struct i2c_client *client) +static void indycam_regdump_debug(struct v4l2_subdev *sd) { int i; u8 val; for (i = 0; i < 9; i++) { - indycam_read_reg(client, i, &val); + indycam_read_reg(sd, i, &val); dprintk("Reg %d = 0x%02x\n", i, val); } } #endif -static int indycam_get_control(struct i2c_client *client, - struct indycam_control *ctrl) +static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct indycam *camera = i2c_get_clientdata(client); + struct indycam *camera = to_indycam(sd); u8 reg; int ret = 0; - switch (ctrl->type) { - case INDYCAM_CONTROL_AGC: - case INDYCAM_CONTROL_AWB: - ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, ®); + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); if (ret) return -EIO; - if (ctrl->type == INDYCAM_CONTROL_AGC) + if (ctrl->id == V4L2_CID_AUTOGAIN) ctrl->value = (reg & INDYCAM_CONTROL_AGCENA) ? 1 : 0; else ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL) ? 1 : 0; break; - case INDYCAM_CONTROL_SHUTTER: - ret = indycam_read_reg(client, INDYCAM_REG_SHUTTER, ®); + case V4L2_CID_EXPOSURE: + ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, ®); if (ret) return -EIO; ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1); break; - case INDYCAM_CONTROL_GAIN: - ret = indycam_read_reg(client, INDYCAM_REG_GAIN, ®); + case V4L2_CID_GAIN: + ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; - case INDYCAM_CONTROL_RED_BALANCE: - ret = indycam_read_reg(client, INDYCAM_REG_RED_BALANCE, ®); + case V4L2_CID_RED_BALANCE: + ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; - case INDYCAM_CONTROL_BLUE_BALANCE: - ret = indycam_read_reg(client, INDYCAM_REG_BLUE_BALANCE, ®); + case V4L2_CID_BLUE_BALANCE: + ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; case INDYCAM_CONTROL_RED_SATURATION: - ret = indycam_read_reg(client, + ret = indycam_read_reg(sd, INDYCAM_REG_RED_SATURATION, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; case INDYCAM_CONTROL_BLUE_SATURATION: - ret = indycam_read_reg(client, + ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_SATURATION, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; - case INDYCAM_CONTROL_GAMMA: + case V4L2_CID_GAMMA: if (camera->version == CAMERA_VERSION_MOOSE) { - ret = indycam_read_reg(client, + ret = indycam_read_reg(sd, INDYCAM_REG_GAMMA, ®); if (ret) return -EIO; @@ -213,21 +224,20 @@ static int indycam_get_control(struct i2c_client *client, return ret; } -static int indycam_set_control(struct i2c_client *client, - struct indycam_control *ctrl) +static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct indycam *camera = i2c_get_clientdata(client); + struct indycam *camera = to_indycam(sd); u8 reg; int ret = 0; - switch (ctrl->type) { - case INDYCAM_CONTROL_AGC: - case INDYCAM_CONTROL_AWB: - ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, ®); + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); if (ret) break; - if (ctrl->type == INDYCAM_CONTROL_AGC) { + if (ctrl->id == V4L2_CID_AUTOGAIN) { if (ctrl->value) reg |= INDYCAM_CONTROL_AGCENA; else @@ -239,34 +249,34 @@ static int indycam_set_control(struct i2c_client *client, reg &= ~INDYCAM_CONTROL_AWBCTL; } - ret = indycam_write_reg(client, INDYCAM_REG_CONTROL, reg); + ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg); break; - case INDYCAM_CONTROL_SHUTTER: + case V4L2_CID_EXPOSURE: reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1); - ret = indycam_write_reg(client, INDYCAM_REG_SHUTTER, reg); + ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg); break; - case INDYCAM_CONTROL_GAIN: - ret = indycam_write_reg(client, INDYCAM_REG_GAIN, ctrl->value); + case V4L2_CID_GAIN: + ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value); break; - case INDYCAM_CONTROL_RED_BALANCE: - ret = indycam_write_reg(client, INDYCAM_REG_RED_BALANCE, + case V4L2_CID_RED_BALANCE: + ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE, ctrl->value); break; - case INDYCAM_CONTROL_BLUE_BALANCE: - ret = indycam_write_reg(client, INDYCAM_REG_BLUE_BALANCE, + case V4L2_CID_BLUE_BALANCE: + ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE, ctrl->value); break; case INDYCAM_CONTROL_RED_SATURATION: - ret = indycam_write_reg(client, INDYCAM_REG_RED_SATURATION, + ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION, ctrl->value); break; case INDYCAM_CONTROL_BLUE_SATURATION: - ret = indycam_write_reg(client, INDYCAM_REG_BLUE_SATURATION, + ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION, ctrl->value); break; - case INDYCAM_CONTROL_GAMMA: + case V4L2_CID_GAMMA: if (camera->version == CAMERA_VERSION_MOOSE) { - ret = indycam_write_reg(client, INDYCAM_REG_GAMMA, + ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA, ctrl->value); } break; @@ -279,192 +289,107 @@ static int indycam_set_control(struct i2c_client *client, /* I2C-interface */ -static int indycam_attach(struct i2c_adapter *adap, int addr, int kind) +static int indycam_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct indycam *camera = to_indycam(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_INDYCAM, + camera->version); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops indycam_core_ops = { + .g_chip_ident = indycam_g_chip_ident, + .g_ctrl = indycam_g_ctrl, + .s_ctrl = indycam_s_ctrl, +}; + +static const struct v4l2_subdev_ops indycam_ops = { + .core = &indycam_core_ops, +}; + +static int indycam_probe(struct i2c_client *client, + const struct i2c_device_id *id) { int err = 0; struct indycam *camera; - struct i2c_client *client; + struct v4l2_subdev *sd; - printk(KERN_INFO "SGI IndyCam driver version %s\n", - INDYCAM_MODULE_VERSION); + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) - return -ENOMEM; camera = kzalloc(sizeof(struct indycam), GFP_KERNEL); - if (!camera) { - err = -ENOMEM; - goto out_free_client; - } - - client->addr = addr; - client->adapter = adap; - client->driver = &i2c_driver_indycam; - client->flags = 0; - strcpy(client->name, "IndyCam client"); - i2c_set_clientdata(client, camera); - - camera->client = client; + if (!camera) + return -ENOMEM; - err = i2c_attach_client(client); - if (err) - goto out_free_camera; + sd = &camera->sd; + v4l2_i2c_subdev_init(sd, client, &indycam_ops); camera->version = i2c_smbus_read_byte_data(client, INDYCAM_REG_VERSION); if (camera->version != CAMERA_VERSION_INDY && camera->version != CAMERA_VERSION_MOOSE) { - err = -ENODEV; - goto out_detach_client; + kfree(camera); + return -ENODEV; } + printk(KERN_INFO "IndyCam v%d.%d detected\n", INDYCAM_VERSION_MAJOR(camera->version), INDYCAM_VERSION_MINOR(camera->version)); - indycam_regdump(client); + indycam_regdump(sd); // initialize - err = indycam_write_block(client, 0, sizeof(initseq), (u8 *)&initseq); + err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq); if (err) { printk(KERN_ERR "IndyCam initialization failed\n"); - err = -EIO; - goto out_detach_client; + kfree(camera); + return -EIO; } - indycam_regdump(client); + indycam_regdump(sd); // white balance - err = indycam_write_reg(client, INDYCAM_REG_CONTROL, + err = indycam_write_reg(sd, INDYCAM_REG_CONTROL, INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL); if (err) { printk(KERN_ERR "IndyCam: White balancing camera failed\n"); - err = -EIO; - goto out_detach_client; + kfree(camera); + return -EIO; } - indycam_regdump(client); + indycam_regdump(sd); printk(KERN_INFO "IndyCam initialized\n"); return 0; - -out_detach_client: - i2c_detach_client(client); -out_free_camera: - kfree(camera); -out_free_client: - kfree(client); - return err; -} - -static int indycam_probe(struct i2c_adapter *adap) -{ - /* Indy specific crap */ - if (adap->id == I2C_HW_SGI_VINO) - return indycam_attach(adap, INDYCAM_ADDR, 0); - /* Feel free to add probe here :-) */ - return -ENODEV; } -static int indycam_detach(struct i2c_client *client) +static int indycam_remove(struct i2c_client *client) { - struct indycam *camera = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); - i2c_detach_client(client); - kfree(camera); - kfree(client); + v4l2_device_unregister_subdev(sd); + kfree(to_indycam(sd)); return 0; } -static int indycam_command(struct i2c_client *client, unsigned int cmd, - void *arg) -{ - // struct indycam *camera = i2c_get_clientdata(client); - - /* The old video_decoder interface just isn't enough, - * so we'll use some custom commands. */ - switch (cmd) { - case DECODER_GET_CAPABILITIES: { - struct video_decoder_capability *cap = arg; - - cap->flags = VIDEO_DECODER_NTSC; - cap->inputs = 1; - cap->outputs = 1; - break; - } - case DECODER_GET_STATUS: { - int *iarg = arg; - - *iarg = DECODER_STATUS_GOOD | DECODER_STATUS_NTSC | - DECODER_STATUS_COLOR; - break; - } - case DECODER_SET_NORM: { - int *iarg = arg; - - switch (*iarg) { - case VIDEO_MODE_NTSC: - break; - default: - return -EINVAL; - } - break; - } - case DECODER_SET_INPUT: { - int *iarg = arg; - - if (*iarg != 0) - return -EINVAL; - break; - } - case DECODER_SET_OUTPUT: { - int *iarg = arg; - - if (*iarg != 0) - return -EINVAL; - break; - } - case DECODER_ENABLE_OUTPUT: { - /* Always enabled */ - break; - } - case DECODER_SET_PICTURE: { - // struct video_picture *pic = arg; - /* TODO: convert values for indycam_set_controls() */ - break; - } - case DECODER_INDYCAM_GET_CONTROL: { - return indycam_get_control(client, arg); - } - case DECODER_INDYCAM_SET_CONTROL: { - return indycam_set_control(client, arg); - } - default: - return -EINVAL; - } - - return 0; -} - -static struct i2c_driver i2c_driver_indycam = { - .driver = { - .name = "indycam", - }, - .id = I2C_DRIVERID_INDYCAM, - .attach_adapter = indycam_probe, - .detach_client = indycam_detach, - .command = indycam_command, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id indycam_id[] = { + { "indycam", 0 }, + { } }; +MODULE_DEVICE_TABLE(i2c, indycam_id); -static int __init indycam_init(void) -{ - return i2c_add_driver(&i2c_driver_indycam); -} - -static void __exit indycam_exit(void) -{ - i2c_del_driver(&i2c_driver_indycam); -} - -module_init(indycam_init); -module_exit(indycam_exit); +#endif +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "indycam", + .probe = indycam_probe, + .remove = indycam_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = indycam_id, +#endif +}; diff --git a/linux/drivers/media/video/indycam.h b/linux/drivers/media/video/indycam.h index e6ee82063..881f21c47 100644 --- a/linux/drivers/media/video/indycam.h +++ b/linux/drivers/media/video/indycam.h @@ -87,22 +87,7 @@ /* Driver interface definitions */ -#define INDYCAM_CONTROL_AGC 0 /* boolean */ -#define INDYCAM_CONTROL_AWB 1 /* boolean */ -#define INDYCAM_CONTROL_SHUTTER 2 -#define INDYCAM_CONTROL_GAIN 3 -#define INDYCAM_CONTROL_RED_BALANCE 4 -#define INDYCAM_CONTROL_BLUE_BALANCE 5 -#define INDYCAM_CONTROL_RED_SATURATION 6 -#define INDYCAM_CONTROL_BLUE_SATURATION 7 -#define INDYCAM_CONTROL_GAMMA 8 - -struct indycam_control { - u8 type; - s32 value; -}; - -#define DECODER_INDYCAM_GET_CONTROL _IOR('d', 193, struct indycam_control) -#define DECODER_INDYCAM_SET_CONTROL _IOW('d', 194, struct indycam_control) +#define INDYCAM_CONTROL_RED_SATURATION (V4L2_CID_PRIVATE_BASE + 0) +#define INDYCAM_CONTROL_BLUE_SATURATION (V4L2_CID_PRIVATE_BASE + 1) #endif diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index a99aea49a..7bae57ad3 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -47,9 +47,9 @@ #include <linux/i2c-id.h> #include <linux/workqueue.h> +#include "compat.h" #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> -#include "compat.h" /* ----------------------------------------------------------------------- */ /* insmod parameters */ @@ -280,12 +280,6 @@ static void ir_key_poll(struct IR_i2c *ir) } } -static void ir_timer(unsigned long data) -{ - struct IR_i2c *ir = (struct IR_i2c*)data; - schedule_work(&ir->work); -} - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) static void ir_work(void *data) #else @@ -295,7 +289,7 @@ static void ir_work(struct work_struct *work) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) struct IR_i2c *ir = data; #else - struct IR_i2c *ir = container_of(work, struct IR_i2c, work); + struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work); #endif int polling_interval = 100; @@ -305,7 +299,7 @@ static void ir_work(struct work_struct *work) polling_interval = 50; ir_key_poll(ir); - mod_timer(&ir->timer, jiffies + msecs_to_jiffies(polling_interval)); + schedule_delayed_work(&ir->work, msecs_to_jiffies(polling_interval)); } /* ----------------------------------------------------------------------- */ @@ -462,14 +456,11 @@ static int ir_attach(struct i2c_adapter *adap, int addr, /* start polling via eventd */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - INIT_WORK(&ir->work, ir_work, ir); + INIT_DELAYED_WORK(&ir->work, ir_work, ir); #else - INIT_WORK(&ir->work, ir_work); + INIT_DELAYED_WORK(&ir->work, ir_work); #endif - init_timer(&ir->timer); - ir->timer.function = ir_timer; - ir->timer.data = (unsigned long)ir; - schedule_work(&ir->work); + schedule_delayed_work(&ir->work, 0); return 0; @@ -486,8 +477,7 @@ static int ir_detach(struct i2c_client *client) struct IR_i2c *ir = i2c_get_clientdata(client); /* kill outstanding polls */ - del_timer_sync(&ir->timer); - flush_scheduled_work(); + cancel_delayed_work_sync(&ir->work); /* unregister devices */ input_unregister_device(ir->input); diff --git a/linux/drivers/media/video/ivtv/ivtv-version.h b/linux/drivers/media/video/ivtv/ivtv-version.h index 8cd753d30..b530dec39 100644 --- a/linux/drivers/media/video/ivtv/ivtv-version.h +++ b/linux/drivers/media/video/ivtv/ivtv-version.h @@ -23,7 +23,7 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 #define IVTV_DRIVER_VERSION_MINOR 4 -#define IVTV_DRIVER_VERSION_PATCHLEVEL 0 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 1 #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) #define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/linux/drivers/media/video/ks0127.c b/linux/drivers/media/video/ks0127.c index eaa481a57..3dbda6e92 100644 --- a/linux/drivers/media/video/ks0127.c +++ b/linux/drivers/media/video/ks0127.c @@ -589,8 +589,8 @@ static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std) /* force to secam mode */ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f); } else { - v4l2_dbg(1, debug, sd, - "VIDIOC_S_STD: Unknown norm %llx\n", std); + v4l2_dbg(1, debug, sd, "VIDIOC_S_STD: Unknown norm %llx\n", + (unsigned long long)std); } return 0; } diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index 61b54317f..fdcc1c1c5 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -57,7 +57,7 @@ #else #include <linux/freezer.h> #endif -#include <linux/videodev2.h> +#include <linux/videodev.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-i2c-drv-legacy.h> @@ -371,29 +371,6 @@ int msp_sleep(struct msp_state *state, int timeout) } /* ------------------------------------------------------------------------ */ -#ifdef CONFIG_VIDEO_ALLOW_V4L1 -static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode) -{ - if (rxsubchans == V4L2_TUNER_SUB_MONO) - return VIDEO_SOUND_MONO; - if (rxsubchans == V4L2_TUNER_SUB_STEREO) - return VIDEO_SOUND_STEREO; - if (audmode == V4L2_TUNER_MODE_LANG2) - return VIDEO_SOUND_LANG2; - return VIDEO_SOUND_LANG1; -} - -static int msp_mode_v4l1_to_v4l2(int mode) -{ - if (mode & VIDEO_SOUND_STEREO) - return V4L2_TUNER_MODE_STEREO; - if (mode & VIDEO_SOUND_LANG2) - return V4L2_TUNER_MODE_LANG2; - if (mode & VIDEO_SOUND_LANG1) - return V4L2_TUNER_MODE_LANG1; - return V4L2_TUNER_MODE_MONO; -} -#endif static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { @@ -487,96 +464,6 @@ static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return 0; } -#ifdef CONFIG_VIDEO_ALLOW_V4L1 -static long msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (cmd) { - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOCGAUDIO: - { - struct video_audio *va = arg; - - va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_MUTABLE; - if (state->has_sound_processing) - va->flags |= VIDEO_AUDIO_BALANCE | - VIDEO_AUDIO_BASS | - VIDEO_AUDIO_TREBLE; - if (state->muted) - va->flags |= VIDEO_AUDIO_MUTE; - va->volume = state->volume; - va->balance = state->volume ? state->balance : 32768; - va->bass = state->bass; - va->treble = state->treble; - - if (state->radio) - break; - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans, state->audmode); - break; - } - - case VIDIOCSAUDIO: - { - struct video_audio *va = arg; - - state->muted = (va->flags & VIDEO_AUDIO_MUTE); - state->volume = va->volume; - state->balance = va->balance; - state->bass = va->bass; - state->treble = va->treble; - msp_set_audio(client); - - if (va->mode != 0 && state->radio == 0 && - state->audmode != msp_mode_v4l1_to_v4l2(va->mode)) { - state->audmode = msp_mode_v4l1_to_v4l2(va->mode); - msp_set_audmode(client); - } - break; - } - - case VIDIOCSCHAN: - { - struct video_channel *vc = arg; - int update = 0; - v4l2_std_id std; - - if (state->radio) - update = 1; - state->radio = 0; - if (vc->norm == VIDEO_MODE_PAL) - std = V4L2_STD_PAL; - else if (vc->norm == VIDEO_MODE_SECAM) - std = V4L2_STD_SECAM; - else - std = V4L2_STD_NTSC; - if (std != state->v4l2_std) { - state->v4l2_std = std; - update = 1; - } - if (update) - msp_wake_thread(client); - break; - } - - case VIDIOCSFREQ: - { - /* new channel -- kick audio carrier scan */ - msp_wake_thread(client); - break; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} -#endif - /* --- v4l2 ioctls --- */ static int msp_s_radio(struct v4l2_subdev *sd) { @@ -827,9 +714,6 @@ static const struct v4l2_subdev_core_ops msp_core_ops = { .g_ctrl = msp_g_ctrl, .s_ctrl = msp_s_ctrl, .queryctrl = msp_queryctrl, -#ifdef CONFIG_VIDEO_ALLOW_V4L1 - .ioctl = msp_ioctl, -#endif }; static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { diff --git a/linux/drivers/media/video/mxb.c b/linux/drivers/media/video/mxb.c index fda3b7581..c85cfd27e 100644 --- a/linux/drivers/media/video/mxb.c +++ b/linux/drivers/media/video/mxb.c @@ -25,7 +25,6 @@ #include <media/saa7146_vv.h> #include <media/tuner.h> -#include <linux/video_decoder.h> #include <media/v4l2-common.h> #include <media/saa7115.h> #include "compat.h" diff --git a/linux/drivers/media/video/omap24xxcam.c b/linux/drivers/media/video/omap24xxcam.c index 73eb656ac..61f3c83db 100644 --- a/linux/drivers/media/video/omap24xxcam.c +++ b/linux/drivers/media/video/omap24xxcam.c @@ -1665,7 +1665,6 @@ static int omap24xxcam_device_register(struct v4l2_int_device *s) vfd->parent = cam->dev; strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name)); - vfd->vfl_type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY; vfd->fops = &omap24xxcam_fops; vfd->minor = -1; vfd->ioctl_ops = &omap24xxcam_ioctl_fops; diff --git a/linux/drivers/media/video/ov7670.c b/linux/drivers/media/video/ov7670.c index e03154536..f0e1a56eb 100644 --- a/linux/drivers/media/video/ov7670.c +++ b/linux/drivers/media/video/ov7670.c @@ -14,7 +14,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> #include <linux/i2c.h> diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c index c841f4e4f..63303b70f 100644 --- a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -15,6 +15,9 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/i2c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-i2c-drv.h> #include "ovcamchip_priv.h" #define DRIVER_VERSION "v2.27 for Linux 2.6" @@ -44,6 +47,20 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +static unsigned short normal_i2c[] = { + /* ov7xxx */ + 0x42 >> 1, 0x46 >> 1, 0x4a >> 1, 0x4e >> 1, + 0x52 >> 1, 0x56 >> 1, 0x5a >> 1, 0x5e >> 1, + /* ov6xxx */ + 0xc0 >> 1, 0xc4 >> 1, 0xc8 >> 1, 0xcc >> 1, + 0xd0 >> 1, 0xd4 >> 1, 0xd8 >> 1, 0xdc >> 1, + I2C_CLIENT_END +}; + +I2C_CLIENT_INSMOD; +#endif + /* Registers common to all chips, that are needed for detection */ #define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ #define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ @@ -61,10 +78,6 @@ static char *chip_names[NUM_CC_TYPES] = { [CC_OV6630AF] = "OV6630AF", }; -/* Forward declarations */ -static struct i2c_driver driver; -static struct i2c_client client_template; - /* ----------------------------------------------------------------------- */ int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals) @@ -253,112 +266,36 @@ static int ovcamchip_detect(struct i2c_client *c) /* Test for 7xx0 */ PDEBUG(3, "Testing for 0V7xx0"); - c->addr = OV7xx0_SID; - if (init_camchip(c) < 0) { - /* Test for 6xx0 */ - PDEBUG(3, "Testing for 0V6xx0"); - c->addr = OV6xx0_SID; - if (init_camchip(c) < 0) { - return -ENODEV; - } else { - if (ov6xx0_detect(c) < 0) { - PERROR("Failed to init OV6xx0"); - return -EIO; - } - } - } else { + if (init_camchip(c) < 0) + return -ENODEV; + /* 7-bit addresses with bit 0 set are for the OV7xx0 */ + if (c->addr & 1) { if (ov7xx0_detect(c) < 0) { PERROR("Failed to init OV7xx0"); return -EIO; } + return 0; + } + /* Test for 6xx0 */ + PDEBUG(3, "Testing for 0V6xx0"); + if (ov6xx0_detect(c) < 0) { + PERROR("Failed to init OV6xx0"); + return -EIO; } - return 0; } /* ----------------------------------------------------------------------- */ -static int ovcamchip_attach(struct i2c_adapter *adap) +static long ovcamchip_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { - int rc = 0; - struct ovcamchip *ov; - struct i2c_client *c; - - /* I2C is not a PnP bus, so we can never be certain that we're talking - * to the right chip. To prevent damage to EEPROMS and such, only - * attach to adapters that are known to contain OV camera chips. */ - - switch (adap->id) { - case I2C_HW_SMBUS_OV511: - case I2C_HW_SMBUS_OV518: - case I2C_HW_SMBUS_W9968CF: - PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id); - break; - default: - PDEBUG(1, "Adapter ID 0x%06x rejected", adap->id); - return -ENODEV; - } - - c = kmalloc(sizeof *c, GFP_KERNEL); - if (!c) { - rc = -ENOMEM; - goto no_client; - } - memcpy(c, &client_template, sizeof *c); - c->adapter = adap; - strcpy(c->name, "OV????"); - - ov = kzalloc(sizeof *ov, GFP_KERNEL); - if (!ov) { - rc = -ENOMEM; - goto no_ov; - } - i2c_set_clientdata(c, ov); - - rc = ovcamchip_detect(c); - if (rc < 0) - goto error; - - strcpy(c->name, chip_names[ov->subtype]); - - PDEBUG(1, "Camera chip detection complete"); - - i2c_attach_client(c); - - return rc; -error: - kfree(ov); -no_ov: - kfree(c); -no_client: - PDEBUG(1, "returning %d", rc); - return rc; -} - -static int ovcamchip_detach(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - int rc; - - rc = ov->sops->free(c); - if (rc < 0) - return rc; - - i2c_detach_client(c); - - kfree(ov); - kfree(c); - return 0; -} - -static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); + struct ovcamchip *ov = to_ovcamchip(sd); + struct i2c_client *c = v4l2_get_subdevdata(sd); if (!ov->initialized && cmd != OVCAMCHIP_CMD_Q_SUBTYPE && cmd != OVCAMCHIP_CMD_INITIALIZE) { - dev_err(&c->dev, "ERROR: Camera chip not initialized yet!\n"); + v4l2_err(sd, "Camera chip not initialized yet!\n"); return -EPERM; } @@ -379,10 +316,10 @@ static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) if (ov->mono) { if (ov->subtype != CC_OV7620) - dev_warn(&c->dev, "Warning: Monochrome not " + v4l2_warn(sd, "Monochrome not " "implemented for this chip\n"); else - dev_info(&c->dev, "Initializing chip as " + v4l2_info(sd, "Initializing chip as " "monochrome\n"); } @@ -398,37 +335,84 @@ static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) } } +static int ovcamchip_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + /* ----------------------------------------------------------------------- */ -static struct i2c_driver driver = { - .driver = { - .name = "ovcamchip", - }, - .id = I2C_DRIVERID_OVCAMCHIP, - .attach_adapter = ovcamchip_attach, - .detach_client = ovcamchip_detach, - .command = ovcamchip_command, +static const struct v4l2_subdev_core_ops ovcamchip_core_ops = { + .ioctl = ovcamchip_ioctl, }; -static struct i2c_client client_template = { - .name = "(unset)", - .driver = &driver, +static const struct v4l2_subdev_ops ovcamchip_ops = { + .core = &ovcamchip_core_ops, }; -static int __init ovcamchip_init(void) +static int ovcamchip_probe(struct i2c_client *client, + const struct i2c_device_id *id) { -#ifdef DEBUG - ovcamchip_debug = debug; -#endif + struct ovcamchip *ov; + struct v4l2_subdev *sd; + int rc = 0; + + ov = kzalloc(sizeof *ov, GFP_KERNEL); + if (!ov) { + rc = -ENOMEM; + goto no_ov; + } + sd = &ov->sd; + v4l2_i2c_subdev_init(sd, client, &ovcamchip_ops); + + rc = ovcamchip_detect(client); + if (rc < 0) + goto error; + + v4l_info(client, "%s found @ 0x%02x (%s)\n", + chip_names[ov->subtype], client->addr << 1, client->adapter->name); + + PDEBUG(1, "Camera chip detection complete"); - PINFO(DRIVER_VERSION " : " DRIVER_DESC); - return i2c_add_driver(&driver); + return rc; +error: + kfree(ov); +no_ov: + PDEBUG(1, "returning %d", rc); + return rc; } -static void __exit ovcamchip_exit(void) +static int ovcamchip_remove(struct i2c_client *client) { - i2c_del_driver(&driver); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ovcamchip *ov = to_ovcamchip(sd); + int rc; + + v4l2_device_unregister_subdev(sd); + rc = ov->sops->free(client); + if (rc < 0) + return rc; + + kfree(ov); + return 0; } -module_init(ovcamchip_init); -module_exit(ovcamchip_exit); +/* ----------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id ovcamchip_id[] = { + { "ovcamchip", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ovcamchip_id); + +#endif +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "ovcamchip", + .command = ovcamchip_command, + .probe = ovcamchip_probe, + .remove = ovcamchip_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = ovcamchip_id, +#endif +}; diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h index a05650fae..4f07b78c8 100644 --- a/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h @@ -16,6 +16,7 @@ #define __LINUX_OVCAMCHIP_PRIV_H #include <linux/i2c.h> +#include <media/v4l2-subdev.h> #include <media/ovcamchip.h> #ifdef DEBUG @@ -46,6 +47,7 @@ struct ovcamchip_ops { }; struct ovcamchip { + struct v4l2_subdev sd; struct ovcamchip_ops *sops; void *spriv; /* Private data for OV7x10.c etc... */ int subtype; /* = SEN_OV7610 etc... */ @@ -53,6 +55,11 @@ struct ovcamchip { int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */ }; +static inline struct ovcamchip *to_ovcamchip(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ovcamchip, sd); +} + extern struct ovcamchip_ops ov6x20_ops; extern struct ovcamchip_ops ov6x30_ops; extern struct ovcamchip_ops ov7x10_ops; diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index bb4271393..17cde1757 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -41,9 +41,9 @@ config VIDEO_PVRUSB2_DVB select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE ---help--- This option enables a DVB interface for the pvrusb2 driver. diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index 49a918636..b3765e280 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -1133,6 +1133,7 @@ static int pwc_video_open(struct file *file) } mutex_lock(&pdev->modlock); + pwc_construct(pdev); /* set min/max sizes correct */ if (!pdev->usb_init) { PWC_DEBUG_OPEN("Doing first time initialization.\n"); pdev->usb_init = 1; @@ -1157,7 +1158,6 @@ static int pwc_video_open(struct file *file) if (pwc_set_leds(pdev, led_on, led_off) < 0) PWC_DEBUG_OPEN("Failed to set LED on/off time.\n"); - pwc_construct(pdev); /* set min/max sizes correct */ /* So far, so good. Allocate memory. */ i = pwc_allocate_buffers(pdev); diff --git a/linux/drivers/media/video/saa6588.c b/linux/drivers/media/video/saa6588.c index ae96de5fd..d89d70892 100644 --- a/linux/drivers/media/video/saa6588.c +++ b/linux/drivers/media/video/saa6588.c @@ -77,8 +77,7 @@ MODULE_LICENSE("GPL"); struct saa6588 { struct v4l2_subdev sd; - struct work_struct work; - struct timer_list timer; + struct delayed_work work; spinlock_t lock; unsigned char *buffer; unsigned int buf_size; @@ -323,13 +322,6 @@ static void saa6588_i2c_poll(struct saa6588 *s) wake_up_interruptible(&s->read_queue); } -static void saa6588_timer(unsigned long data) -{ - struct saa6588 *s = (struct saa6588 *)data; - - schedule_work(&s->work); -} - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) static void saa6588_work(void *data) #else @@ -339,11 +331,11 @@ static void saa6588_work(struct work_struct *work) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) struct saa6588 *s = (struct saa6588 *)data; #else - struct saa6588 *s = container_of(work, struct saa6588, work); + struct saa6588 *s = container_of(work, struct saa6588, work.work); #endif saa6588_i2c_poll(s); - mod_timer(&s->timer, jiffies + msecs_to_jiffies(20)); + schedule_delayed_work(&s->work, msecs_to_jiffies(20)); } static int saa6588_configure(struct saa6588 *s) @@ -500,14 +492,11 @@ static int saa6588_probe(struct i2c_client *client, /* start polling via eventd */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - INIT_WORK(&s->work, saa6588_work, s); + INIT_DELAYED_WORK(&s->work, saa6588_work, s); #else - INIT_WORK(&s->work, saa6588_work); + INIT_DELAYED_WORK(&s->work, saa6588_work); #endif - init_timer(&s->timer); - s->timer.function = saa6588_timer; - s->timer.data = (unsigned long)s; - schedule_work(&s->work); + schedule_delayed_work(&s->work, 0); return 0; } @@ -518,8 +507,7 @@ static int saa6588_remove(struct i2c_client *client) v4l2_device_unregister_subdev(sd); - del_timer_sync(&s->timer); - flush_scheduled_work(); + cancel_delayed_work_sync(&s->work); kfree(s->buffer); kfree(s); diff --git a/linux/drivers/media/video/saa7110.c b/linux/drivers/media/video/saa7110.c index ad21fdec1..bb58477e5 100644 --- a/linux/drivers/media/video/saa7110.c +++ b/linux/drivers/media/video/saa7110.c @@ -257,7 +257,7 @@ static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus) int status = saa7110_read(sd); v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n", - status, decoder->norm); + status, (unsigned long long)decoder->norm); if (!(status & 0x40)) res = 0; if (!(status & 0x03)) diff --git a/linux/drivers/media/video/saa7134/Kconfig b/linux/drivers/media/video/saa7134/Kconfig index 51f17c82b..e62b29967 100644 --- a/linux/drivers/media/video/saa7134/Kconfig +++ b/linux/drivers/media/video/saa7134/Kconfig @@ -42,6 +42,9 @@ config VIDEO_SAA7134_DVB select DVB_MT312 if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 4697a6e48..e8efc883c 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -31,6 +31,7 @@ #include <media/v4l2-common.h> #include <media/tveeprom.h> #include "tea5767.h" +#include "tda18271.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -3330,6 +3331,68 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200100, }, }, + [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { + .name = "Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 3, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_SERIAL, + .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0800100, /* GPIO 23 HI for FM */ + }, + }, + [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = { + .name = "Hauppauge WinTV-HVR1110r3", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 3, + .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0800100, /* GPIO 23 HI for FM */ + }, + }, [SAA7134_BOARD_CINERGY_HT_PCMCIA] = { .name = "Terratec Cinergy HT PCMCIA", .audio_clock = 0x00187de7, @@ -5442,6 +5505,36 @@ struct pci_device_id saa7134_pci_tbl[] = { },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6706, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6707, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6708, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6709, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x670a, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x153b, .subdevice = 0x1172, .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA, @@ -5914,8 +6007,8 @@ static int saa7134_xc5000_callback(struct saa7134_dev *dev, } #endif -static int saa7134_tda8290_callback(struct saa7134_dev *dev, - int command, int arg) +static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev, + int command, int arg) { u8 sync_control; @@ -5941,6 +6034,65 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, return 0; } +static inline int saa7134_tda18271_hvr11x0_toggle_agc(struct saa7134_dev *dev, + enum tda18271_mode mode) +{ + /* toggle AGC switch through GPIO 26 */ + switch (mode) { + case TDA18271_ANALOG: + saa7134_set_gpio(dev, 26, 0); + break; + case TDA18271_DIGITAL: + saa7134_set_gpio(dev, 26, 1); + break; + default: + return -EINVAL; + } + return 0; +} + +static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, + int command, int arg) +{ + int ret = 0; + + switch (command) { + case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ + switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); + break; + default: + break; + } + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int saa7134_tda8290_callback(struct saa7134_dev *dev, + int command, int arg) +{ + int ret; + + switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + /* tda8290 + tda18271 */ + ret = saa7134_tda8290_18271_callback(dev, command, arg); + break; + default: + /* tda8290 + tda827x */ + ret = saa7134_tda8290_827x_callback(dev, command, arg); + break; + } + return ret; +} + int saa7134_tuner_callback(void *priv, int component, int command, int arg) { struct saa7134_dev *dev = priv; @@ -5975,11 +6127,16 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) switch (tv.model) { case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ + case 67201: /* WinTV-HVR1120 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ + case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ case 67569: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM) */ case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ + case 67651: /* WinTV-HVR1120 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ break; default: printk(KERN_WARNING "%s: warning: " @@ -6160,6 +6317,16 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); break; + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + /* GPIO 26 high for digital, low for analog */ + saa7134_set_gpio(dev, 26, 0); + msleep(1); + + saa7134_set_gpio(dev, 22, 0); + msleep(10); + saa7134_set_gpio(dev, 22, 1); + break; /* i2c remotes */ case SAA7134_BOARD_PINNACLE_PCTV_110i: case SAA7134_BOARD_PINNACLE_PCTV_310i: @@ -6412,6 +6579,10 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); } break; + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + hauppauge_eeprom(dev, dev->eedata+0x80); + break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: hauppauge_eeprom(dev, dev->eedata+0x80); /* break intentionally omitted */ diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 7745cad95..2e1344ecf 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -48,6 +48,9 @@ #include "isl6405.h" #include "lnbp21.h" #include "tuner-simple.h" +#include "tda18271.h" +#include "lgdt3305.h" +#include "tda8290.h" #include "zl10353.h" @@ -863,6 +866,7 @@ static struct zl10353_config behold_h6_config = { .demod_address = 0x1e>>1, .no_tuner = 1, .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, }; /* ================================================================== @@ -963,6 +967,34 @@ static struct zl10036_config avertv_a700_tuner = { .tuner_address = 0x60, }; +static struct lgdt3305_config hcw_lgdt3305_config = { + .i2c_addr = 0x0e, + .mpeg_mode = LGDT3305_MPEG_SERIAL, + .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .deny_i2c_rptr = 1, + .spectral_inversion = 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x58, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x58, }, +}; + +static struct tda18271_config hcw_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .config = 3, +}; + +static struct tda829x_config tda829x_no_probe = { + .probe_tuner = TDA829X_DONT_PROBE, +}; + /* ================================================================== * Core code */ @@ -1089,6 +1121,19 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_1) < 0) goto dettach_frontend; break; + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + fe0->dvb.frontend = dvb_attach(lgdt3305_attach, + &hcw_lgdt3305_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &hcw_tda18271_config); + } + break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: if (configure_tda827x_fe(dev, &asus_p7131_dual_config, &tda827x_cfg_0) < 0) diff --git a/linux/drivers/media/video/saa7134/saa7134-ts.c b/linux/drivers/media/video/saa7134/saa7134-ts.c index ef55a59f0..cc8b923af 100644 --- a/linux/drivers/media/video/saa7134/saa7134-ts.c +++ b/linux/drivers/media/video/saa7134/saa7134-ts.c @@ -79,8 +79,19 @@ static int buffer_activate(struct saa7134_dev *dev, saa_writeb(SAA7134_TS_SERIAL1, 0x00); /* Start TS stream */ - saa_writeb(SAA7134_TS_SERIAL0, 0x40); - saa_writeb(SAA7134_TS_PARALLEL, 0xEC); + switch (saa7134_boards[dev->board].ts_type) { + case SAA7134_MPEG_TS_PARALLEL: + saa_writeb(SAA7134_TS_SERIAL0, 0x40); + saa_writeb(SAA7134_TS_PARALLEL, 0xec); + break; + case SAA7134_MPEG_TS_SERIAL: + saa_writeb(SAA7134_TS_SERIAL0, 0xd8); + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); + saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc); + saa_writeb(SAA7134_TS_SERIAL1, 0x02); + break; + } + dev->ts_state = SAA7134_TS_STARTED; } diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index be7f98bc0..d188aae14 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -31,11 +31,6 @@ #include "saa7134.h" #include <media/v4l2-common.h> -#ifdef CONFIG_VIDEO_V4L1_COMPAT -/* Include V4L1 specific functions. Should be removed soon */ -#include <linux/videodev.h> -#endif - /* ------------------------------------------------------------------ */ unsigned int video_debug; diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index c9d4d4354..d2b29ed9e 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -279,6 +279,8 @@ struct saa7134_format { #define SAA7134_BOARD_ASUSTeK_TIGER 152 #define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 +#define SAA7134_BOARD_HAUPPAUGE_HVR1120 155 +#define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -311,6 +313,11 @@ enum saa7134_mpeg_type { SAA7134_MPEG_DVB, }; +enum saa7134_mpeg_ts_type { + SAA7134_MPEG_TS_PARALLEL = 0, + SAA7134_MPEG_TS_SERIAL, +}; + struct saa7134_board { char *name; unsigned int audio_clock; @@ -333,6 +340,7 @@ struct saa7134_board { /* peripheral I/O */ enum saa7134_video_out video_out; enum saa7134_mpeg_type mpeg; + enum saa7134_mpeg_ts_type ts_type; unsigned int vid_port_opts; }; diff --git a/linux/drivers/media/video/saa7146.h b/linux/drivers/media/video/saa7146.h index 2830b5e33..9fadb331a 100644 --- a/linux/drivers/media/video/saa7146.h +++ b/linux/drivers/media/video/saa7146.h @@ -25,8 +25,6 @@ #include <linux/types.h> #include <linux/wait.h> -#include <linux/videodev.h> - #ifndef O_NONCAP #define O_NONCAP O_TRUNC #endif diff --git a/linux/drivers/media/video/saa7191.c b/linux/drivers/media/video/saa7191.c index c4f169c95..9df505e0f 100644 --- a/linux/drivers/media/video/saa7191.c +++ b/linux/drivers/media/video/saa7191.c @@ -19,9 +19,11 @@ #include <linux/mm.h> #include <linux/slab.h> -#include <linux/videodev.h> -#include <linux/video_decoder.h> +#include <linux/videodev2.h> #include <linux/i2c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> #include "compat.h" #include "saa7191.h" @@ -33,6 +35,12 @@ MODULE_VERSION(SAA7191_MODULE_VERSION); MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); MODULE_LICENSE("GPL"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +static unsigned short normal_i2c[] = { 0x8a >> 1, 0x8e >> 1, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; +#endif + // #define SAA7191_DEBUG #ifdef SAA7191_DEBUG @@ -45,17 +53,20 @@ MODULE_LICENSE("GPL"); #define SAA7191_SYNC_DELAY 100 /* milliseconds */ struct saa7191 { - struct i2c_client *client; + struct v4l2_subdev sd; /* the register values are stored here as the actual * I2C-registers are write-only */ u8 reg[25]; int input; - int norm; + v4l2_std_id norm; }; -static struct i2c_driver i2c_driver_saa7191; +static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7191, sd); +} static const u8 initseq[] = { 0, /* Subaddress */ @@ -101,15 +112,14 @@ static const u8 initseq[] = { /* SAA7191 register handling */ -static u8 saa7191_read_reg(struct i2c_client *client, - u8 reg) +static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg) { - return ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg]; + return to_saa7191(sd)->reg[reg]; } -static int saa7191_read_status(struct i2c_client *client, - u8 *value) +static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; ret = i2c_master_recv(client, value, 1); @@ -122,21 +132,23 @@ static int saa7191_read_status(struct i2c_client *client, } -static int saa7191_write_reg(struct i2c_client *client, u8 reg, - u8 value) +static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) { - ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg] = value; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + to_saa7191(sd)->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } /* the first byte of data must be the first subaddress number (register) */ -static int saa7191_write_block(struct i2c_client *client, +static int saa7191_write_block(struct v4l2_subdev *sd, u8 length, const u8 *data) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7191 *decoder = to_saa7191(sd); int i; int ret; - struct saa7191 *decoder = (struct saa7191 *)i2c_get_clientdata(client); for (i = 0; i < (length - 1); i++) { decoder->reg[data[0] + i] = data[i + 1]; } @@ -153,14 +165,15 @@ static int saa7191_write_block(struct i2c_client *client, /* Helper functions */ -static int saa7191_set_input(struct i2c_client *client, int input) +static int saa7191_s_routing(struct v4l2_subdev *sd, + const struct v4l2_routing *route) { - struct saa7191 *decoder = i2c_get_clientdata(client); - u8 luma = saa7191_read_reg(client, SAA7191_REG_LUMA); - u8 iock = saa7191_read_reg(client, SAA7191_REG_IOCK); + struct saa7191 *decoder = to_saa7191(sd); + u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA); + u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK); int err; - switch (input) { + switch (route->input) { case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 | SAA7191_IOCK_GPSW2); @@ -176,54 +189,50 @@ static int saa7191_set_input(struct i2c_client *client, int input) return -EINVAL; } - err = saa7191_write_reg(client, SAA7191_REG_LUMA, luma); + err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma); if (err) return -EIO; - err = saa7191_write_reg(client, SAA7191_REG_IOCK, iock); + err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock); if (err) return -EIO; - decoder->input = input; + decoder->input = route->input; return 0; } -static int saa7191_set_norm(struct i2c_client *client, int norm) +static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { - struct saa7191 *decoder = i2c_get_clientdata(client); - u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); - u8 chcv = saa7191_read_reg(client, SAA7191_REG_CHCV); + struct saa7191 *decoder = to_saa7191(sd); + u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); + u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV); int err; - switch(norm) { - case SAA7191_NORM_PAL: + if (norm & V4L2_STD_PAL) { stdc &= ~SAA7191_STDC_SECS; ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); chcv = SAA7191_CHCV_PAL; - break; - case SAA7191_NORM_NTSC: + } else if (norm & V4L2_STD_NTSC) { stdc &= ~SAA7191_STDC_SECS; ctl3 &= ~SAA7191_CTL3_AUFD; ctl3 |= SAA7191_CTL3_FSEL; chcv = SAA7191_CHCV_NTSC; - break; - case SAA7191_NORM_SECAM: + } else if (norm & V4L2_STD_SECAM) { stdc |= SAA7191_STDC_SECS; ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); chcv = SAA7191_CHCV_PAL; - break; - default: + } else { return -EINVAL; } - err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); if (err) return -EIO; - err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc); + err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); if (err) return -EIO; - err = saa7191_write_reg(client, SAA7191_REG_CHCV, chcv); + err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv); if (err) return -EIO; @@ -231,19 +240,19 @@ static int saa7191_set_norm(struct i2c_client *client, int norm) dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3, stdc, chcv); - dprintk("norm: %d\n", norm); + dprintk("norm: %llx\n", norm); return 0; } -static int saa7191_wait_for_signal(struct i2c_client *client, u8 *status) +static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status) { int i = 0; dprintk("Checking for signal...\n"); for (i = 0; i < SAA7191_SYNC_COUNT; i++) { - if (saa7191_read_status(client, status)) + if (saa7191_read_status(sd, status)) return -EIO; if (((*status) & SAA7191_STATUS_HLCK) == 0) { @@ -259,31 +268,34 @@ static int saa7191_wait_for_signal(struct i2c_client *client, u8 *status) return -EBUSY; } -static int saa7191_autodetect_norm_extended(struct i2c_client *client) +static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm) { - u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); + struct saa7191 *decoder = to_saa7191(sd); + u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); u8 status; + v4l2_std_id old_norm = decoder->norm; int err = 0; dprintk("SAA7191 extended signal auto-detection...\n"); + *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; stdc &= ~SAA7191_STDC_SECS; ctl3 &= ~(SAA7191_CTL3_FSEL); - err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc); + err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); if (err) { err = -EIO; goto out; } - err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); if (err) { err = -EIO; goto out; } ctl3 |= SAA7191_CTL3_AUFD; - err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); if (err) { err = -EIO; goto out; @@ -291,53 +303,54 @@ static int saa7191_autodetect_norm_extended(struct i2c_client *client) msleep(SAA7191_SYNC_DELAY); - err = saa7191_wait_for_signal(client, &status); + err = saa7191_wait_for_signal(sd, &status); if (err) goto out; if (status & SAA7191_STATUS_FIDT) { /* 60Hz signal -> NTSC */ dprintk("60Hz signal: NTSC\n"); - return saa7191_set_norm(client, SAA7191_NORM_NTSC); + *norm = V4L2_STD_NTSC; + return 0; } /* 50Hz signal */ dprintk("50Hz signal: Trying PAL...\n"); /* try PAL first */ - err = saa7191_set_norm(client, SAA7191_NORM_PAL); + err = saa7191_s_std(sd, V4L2_STD_PAL); if (err) goto out; msleep(SAA7191_SYNC_DELAY); - err = saa7191_wait_for_signal(client, &status); + err = saa7191_wait_for_signal(sd, &status); if (err) goto out; /* not 50Hz ? */ if (status & SAA7191_STATUS_FIDT) { dprintk("No 50Hz signal\n"); - err = -EAGAIN; - goto out; + saa7191_s_std(sd, old_norm); + return -EAGAIN; } if (status & SAA7191_STATUS_CODE) { dprintk("PAL\n"); - return 0; + *norm = V4L2_STD_PAL; + return saa7191_s_std(sd, old_norm); } dprintk("No color detected with PAL - Trying SECAM...\n"); /* no color detected ? -> try SECAM */ - err = saa7191_set_norm(client, - SAA7191_NORM_SECAM); + err = saa7191_s_std(sd, V4L2_STD_SECAM); if (err) goto out; msleep(SAA7191_SYNC_DELAY); - err = saa7191_wait_for_signal(client, &status); + err = saa7191_wait_for_signal(sd, &status); if (err) goto out; @@ -351,32 +364,17 @@ static int saa7191_autodetect_norm_extended(struct i2c_client *client) if (status & SAA7191_STATUS_CODE) { /* Color detected -> SECAM */ dprintk("SECAM\n"); - return 0; + *norm = V4L2_STD_SECAM; + return saa7191_s_std(sd, old_norm); } dprintk("No color detected with SECAM - Going back to PAL.\n"); - /* still no color detected ? - * -> set norm back to PAL */ - err = saa7191_set_norm(client, - SAA7191_NORM_PAL); - if (err) - goto out; - out: - ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); - if (ctl3 & SAA7191_CTL3_AUFD) { - ctl3 &= ~(SAA7191_CTL3_AUFD); - err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); - if (err) { - err = -EIO; - } - } - - return err; + return saa7191_s_std(sd, old_norm); } -static int saa7191_autodetect_norm(struct i2c_client *client) +static int saa7191_autodetect_norm(struct v4l2_subdev *sd) { u8 status; @@ -384,7 +382,7 @@ static int saa7191_autodetect_norm(struct i2c_client *client) dprintk("Reading status...\n"); - if (saa7191_read_status(client, &status)) + if (saa7191_read_status(sd, &status)) return -EIO; dprintk("Checking for signal...\n"); @@ -400,26 +398,25 @@ static int saa7191_autodetect_norm(struct i2c_client *client) if (status & SAA7191_STATUS_FIDT) { /* 60hz signal -> NTSC */ dprintk("NTSC\n"); - return saa7191_set_norm(client, SAA7191_NORM_NTSC); + return saa7191_s_std(sd, V4L2_STD_NTSC); } else { /* 50hz signal -> PAL */ dprintk("PAL\n"); - return saa7191_set_norm(client, SAA7191_NORM_PAL); + return saa7191_s_std(sd, V4L2_STD_PAL); } } -static int saa7191_get_control(struct i2c_client *client, - struct saa7191_control *ctrl) +static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { u8 reg; int ret = 0; - switch (ctrl->type) { + switch (ctrl->id) { case SAA7191_CONTROL_BANDPASS: case SAA7191_CONTROL_BANDPASS_WEIGHT: case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(client, SAA7191_REG_LUMA); - switch (ctrl->type) { + reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); + switch (ctrl->id) { case SAA7191_CONTROL_BANDPASS: ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK) >> SAA7191_LUMA_BPSS_SHIFT; @@ -436,15 +433,15 @@ static int saa7191_get_control(struct i2c_client *client, break; case SAA7191_CONTROL_FORCE_COLOUR: case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(client, SAA7191_REG_GAIN); - if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR) + reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); + if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0; else ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK) >> SAA7191_GAIN_LFIS_SHIFT; break; - case SAA7191_CONTROL_HUE: - reg = saa7191_read_reg(client, SAA7191_REG_HUEC); + case V4L2_CID_HUE: + reg = saa7191_read_reg(sd, SAA7191_REG_HUEC); if (reg < 0x80) reg += 0x80; else @@ -452,18 +449,18 @@ static int saa7191_get_control(struct i2c_client *client, ctrl->value = (s32)reg; break; case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(client, SAA7191_REG_STDC); + reg = saa7191_read_reg(sd, SAA7191_REG_STDC); ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0; break; case SAA7191_CONTROL_LUMA_DELAY: - reg = saa7191_read_reg(client, SAA7191_REG_CTL3); + reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK) >> SAA7191_CTL3_YDEL_SHIFT; if (ctrl->value >= 4) ctrl->value -= 8; break; case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(client, SAA7191_REG_CTL4); + reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK) >> SAA7191_CTL4_VNOI_SHIFT; break; @@ -474,18 +471,17 @@ static int saa7191_get_control(struct i2c_client *client, return ret; } -static int saa7191_set_control(struct i2c_client *client, - struct saa7191_control *ctrl) +static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { u8 reg; int ret = 0; - switch (ctrl->type) { + switch (ctrl->id) { case SAA7191_CONTROL_BANDPASS: case SAA7191_CONTROL_BANDPASS_WEIGHT: case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(client, SAA7191_REG_LUMA); - switch (ctrl->type) { + reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); + switch (ctrl->id) { case SAA7191_CONTROL_BANDPASS: reg &= ~SAA7191_LUMA_BPSS_MASK; reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT) @@ -502,12 +498,12 @@ static int saa7191_set_control(struct i2c_client *client, & SAA7191_LUMA_CORI_MASK; break; } - ret = saa7191_write_reg(client, SAA7191_REG_LUMA, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg); break; case SAA7191_CONTROL_FORCE_COLOUR: case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(client, SAA7191_REG_GAIN); - if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR) { + reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); + if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) { if (ctrl->value) reg |= SAA7191_GAIN_COLO; else @@ -517,41 +513,41 @@ static int saa7191_set_control(struct i2c_client *client, reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT) & SAA7191_GAIN_LFIS_MASK; } - ret = saa7191_write_reg(client, SAA7191_REG_GAIN, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg); break; - case SAA7191_CONTROL_HUE: + case V4L2_CID_HUE: reg = ctrl->value & 0xff; if (reg < 0x80) reg += 0x80; else reg -= 0x80; - ret = saa7191_write_reg(client, SAA7191_REG_HUEC, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg); break; case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(client, SAA7191_REG_STDC); + reg = saa7191_read_reg(sd, SAA7191_REG_STDC); if (ctrl->value) reg |= SAA7191_STDC_VTRC; else reg &= ~SAA7191_STDC_VTRC; - ret = saa7191_write_reg(client, SAA7191_REG_STDC, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg); break; case SAA7191_CONTROL_LUMA_DELAY: { s32 value = ctrl->value; if (value < 0) value += 8; - reg = saa7191_read_reg(client, SAA7191_REG_CTL3); + reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); reg &= ~SAA7191_CTL3_YDEL_MASK; reg |= (value << SAA7191_CTL3_YDEL_SHIFT) & SAA7191_CTL3_YDEL_MASK; - ret = saa7191_write_reg(client, SAA7191_REG_CTL3, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg); break; } case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(client, SAA7191_REG_CTL4); + reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); reg &= ~SAA7191_CTL4_VNOI_MASK; reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT) & SAA7191_CTL4_VNOI_MASK; - ret = saa7191_write_reg(client, SAA7191_REG_CTL4, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg); break; default: ret = -EINVAL; @@ -562,247 +558,111 @@ static int saa7191_set_control(struct i2c_client *client, /* I2C-interface */ -static int saa7191_attach(struct i2c_adapter *adap, int addr, int kind) +static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) { - int err = 0; - struct saa7191 *decoder; - struct i2c_client *client; - - printk(KERN_INFO "Philips SAA7191 driver version %s\n", - SAA7191_MODULE_VERSION); - - client = kzalloc(sizeof(*client), GFP_KERNEL); - if (!client) - return -ENOMEM; - decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); - if (!decoder) { - err = -ENOMEM; - goto out_free_client; - } - - client->addr = addr; - client->adapter = adap; - client->driver = &i2c_driver_saa7191; - client->flags = 0; - strcpy(client->name, "saa7191 client"); - i2c_set_clientdata(client, decoder); - - decoder->client = client; - - err = i2c_attach_client(client); - if (err) - goto out_free_decoder; - - err = saa7191_write_block(client, sizeof(initseq), initseq); - if (err) { - printk(KERN_ERR "SAA7191 initialization failed\n"); - goto out_detach_client; - } - - printk(KERN_INFO "SAA7191 initialized\n"); - - decoder->input = SAA7191_INPUT_COMPOSITE; - decoder->norm = SAA7191_NORM_PAL; - - err = saa7191_autodetect_norm(client); - if (err && (err != -EBUSY)) { - printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); - } + u8 status_reg; + int res = V4L2_IN_ST_NO_SIGNAL; + if (saa7191_read_status(sd, &status_reg)) + return -EIO; + if ((status_reg & SAA7191_STATUS_HLCK) == 0) + res = 0; + if (!(status_reg & SAA7191_STATUS_CODE)) + res |= V4L2_IN_ST_NO_COLOR; + *status = res; return 0; - -out_detach_client: - i2c_detach_client(client); -out_free_decoder: - kfree(decoder); -out_free_client: - kfree(client); - return err; } -static int saa7191_probe(struct i2c_adapter *adap) -{ - /* Always connected to VINO */ - if (adap->id == I2C_HW_SGI_VINO) - return saa7191_attach(adap, SAA7191_ADDR, 0); - /* Feel free to add probe here :-) */ - return -ENODEV; -} -static int saa7191_detach(struct i2c_client *client) +static int saa7191_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) { - struct saa7191 *decoder = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(sd); - i2c_detach_client(client); - kfree(decoder); - kfree(client); - return 0; + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0); } -static int saa7191_command(struct i2c_client *client, unsigned int cmd, - void *arg) -{ - struct saa7191 *decoder = i2c_get_clientdata(client); +/* ----------------------------------------------------------------------- */ - switch (cmd) { - case DECODER_GET_CAPABILITIES: { - struct video_decoder_capability *cap = arg; +static const struct v4l2_subdev_core_ops saa7191_core_ops = { + .g_chip_ident = saa7191_g_chip_ident, + .g_ctrl = saa7191_g_ctrl, + .s_ctrl = saa7191_s_ctrl, +}; - cap->flags = VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC | - VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO; - cap->inputs = (client->adapter->id == I2C_HW_SGI_VINO) ? 2 : 1; - cap->outputs = 1; - break; - } - case DECODER_GET_STATUS: { - int *iarg = arg; - u8 status; - int res = 0; +static const struct v4l2_subdev_tuner_ops saa7191_tuner_ops = { + .s_std = saa7191_s_std, +}; - if (saa7191_read_status(client, &status)) { - return -EIO; - } - if ((status & SAA7191_STATUS_HLCK) == 0) - res |= DECODER_STATUS_GOOD; - if (status & SAA7191_STATUS_CODE) - res |= DECODER_STATUS_COLOR; - switch (decoder->norm) { - case SAA7191_NORM_NTSC: - res |= DECODER_STATUS_NTSC; - break; - case SAA7191_NORM_PAL: - res |= DECODER_STATUS_PAL; - break; - case SAA7191_NORM_SECAM: - res |= DECODER_STATUS_SECAM; - break; - case SAA7191_NORM_AUTO: - default: - if (status & SAA7191_STATUS_FIDT) - res |= DECODER_STATUS_NTSC; - else - res |= DECODER_STATUS_PAL; - break; - } - *iarg = res; - break; - } - case DECODER_SET_NORM: { - int *iarg = arg; - - switch (*iarg) { - case VIDEO_MODE_AUTO: - return saa7191_autodetect_norm(client); - case VIDEO_MODE_PAL: - return saa7191_set_norm(client, SAA7191_NORM_PAL); - case VIDEO_MODE_NTSC: - return saa7191_set_norm(client, SAA7191_NORM_NTSC); - case VIDEO_MODE_SECAM: - return saa7191_set_norm(client, SAA7191_NORM_SECAM); - default: - return -EINVAL; - } - break; - } - case DECODER_SET_INPUT: { - int *iarg = arg; - - switch (client->adapter->id) { - case I2C_HW_SGI_VINO: - return saa7191_set_input(client, *iarg); - default: - if (*iarg != 0) - return -EINVAL; - } - break; - } - case DECODER_SET_OUTPUT: { - int *iarg = arg; +static const struct v4l2_subdev_video_ops saa7191_video_ops = { + .s_routing = saa7191_s_routing, + .querystd = saa7191_querystd, + .g_input_status = saa7191_g_input_status, +}; - /* not much choice of outputs */ - if (*iarg != 0) - return -EINVAL; - break; - } - case DECODER_ENABLE_OUTPUT: { - /* Always enabled */ - break; - } - case DECODER_SET_PICTURE: { - struct video_picture *pic = arg; - unsigned val; - int err; +static const struct v4l2_subdev_ops saa7191_ops = { + .core = &saa7191_core_ops, + .video = &saa7191_video_ops, +}; - val = (pic->hue >> 8) - 0x80; +static int saa7191_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct saa7191 *decoder; + struct v4l2_subdev *sd; - err = saa7191_write_reg(client, SAA7191_REG_HUEC, val); - if (err) - return -EIO; + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); - break; - } - case DECODER_SAA7191_GET_STATUS: { - struct saa7191_status *status = arg; - u8 status_reg; + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; - if (saa7191_read_status(client, &status_reg)) - return -EIO; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7191_ops); - status->signal = ((status_reg & SAA7191_STATUS_HLCK) == 0) - ? 1 : 0; - status->signal_60hz = (status_reg & SAA7191_STATUS_FIDT) - ? 1 : 0; - status->color = (status_reg & SAA7191_STATUS_CODE) ? 1 : 0; + err = saa7191_write_block(sd, sizeof(initseq), initseq); + if (err) { + printk(KERN_ERR "SAA7191 initialization failed\n"); + kfree(decoder); + return err; + } - status->input = decoder->input; - status->norm = decoder->norm; + printk(KERN_INFO "SAA7191 initialized\n"); - break; - } - case DECODER_SAA7191_SET_NORM: { - int *norm = arg; - - switch (*norm) { - case SAA7191_NORM_AUTO: - return saa7191_autodetect_norm(client); - case SAA7191_NORM_AUTO_EXT: - return saa7191_autodetect_norm_extended(client); - default: - return saa7191_set_norm(client, *norm); - } - } - case DECODER_SAA7191_GET_CONTROL: { - return saa7191_get_control(client, arg); - } - case DECODER_SAA7191_SET_CONTROL: { - return saa7191_set_control(client, arg); - } - default: - return -EINVAL; - } + decoder->input = SAA7191_INPUT_COMPOSITE; + decoder->norm = V4L2_STD_PAL; + + err = saa7191_autodetect_norm(sd); + if (err && (err != -EBUSY)) + printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); return 0; } -static struct i2c_driver i2c_driver_saa7191 = { - .driver = { - .name = "saa7191", - }, - .id = I2C_DRIVERID_SAA7191, - .attach_adapter = saa7191_probe, - .detach_client = saa7191_detach, - .command = saa7191_command -}; - -static int saa7191_init(void) +static int saa7191_remove(struct i2c_client *client) { - return i2c_add_driver(&i2c_driver_saa7191); -} + struct v4l2_subdev *sd = i2c_get_clientdata(client); -static void saa7191_exit(void) -{ - i2c_del_driver(&i2c_driver_saa7191); + v4l2_device_unregister_subdev(sd); + kfree(to_saa7191(sd)); + return 0; } -module_init(saa7191_init); -module_exit(saa7191_exit); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id saa7191_id[] = { + { "saa7191", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7191_id); + +#endif +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa7191", + .probe = saa7191_probe, + .remove = saa7191_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = saa7191_id, +#endif +}; diff --git a/linux/drivers/media/video/saa7191.h b/linux/drivers/media/video/saa7191.h index a2310da19..803c74d60 100644 --- a/linux/drivers/media/video/saa7191.h +++ b/linux/drivers/media/video/saa7191.h @@ -176,11 +176,9 @@ #define SAA7191_INPUT_COMPOSITE 0 #define SAA7191_INPUT_SVIDEO 1 -#define SAA7191_NORM_AUTO 0 #define SAA7191_NORM_PAL 1 #define SAA7191_NORM_NTSC 2 #define SAA7191_NORM_SECAM 3 -#define SAA7191_NORM_AUTO_EXT 4 /* extended auto-detection */ struct saa7191_status { /* 0=no signal, 1=signal detected */ @@ -232,24 +230,16 @@ struct saa7191_status { #define SAA7191_VNR_MAX 0x03 #define SAA7191_VNR_DEFAULT 0x00 -#define SAA7191_CONTROL_BANDPASS 0 -#define SAA7191_CONTROL_BANDPASS_WEIGHT 1 -#define SAA7191_CONTROL_CORING 2 -#define SAA7191_CONTROL_FORCE_COLOUR 3 /* boolean */ -#define SAA7191_CONTROL_CHROMA_GAIN 4 -#define SAA7191_CONTROL_HUE 5 -#define SAA7191_CONTROL_VTRC 6 /* boolean */ -#define SAA7191_CONTROL_LUMA_DELAY 7 -#define SAA7191_CONTROL_VNR 8 - -struct saa7191_control { - u8 type; - s32 value; -}; +#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0) +#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1) +#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2) +#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3) +#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4) +#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5) +#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6) +#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7) #define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status) #define DECODER_SAA7191_SET_NORM _IOW('d', 196, int) -#define DECODER_SAA7191_GET_CONTROL _IOR('d', 197, struct saa7191_control) -#define DECODER_SAA7191_SET_CONTROL _IOW('d', 198, struct saa7191_control) #endif diff --git a/linux/drivers/media/video/tlv320aic23b.c b/linux/drivers/media/video/tlv320aic23b.c index 46671fb87..1eac4f7e7 100644 --- a/linux/drivers/media/video/tlv320aic23b.c +++ b/linux/drivers/media/video/tlv320aic23b.c @@ -31,16 +31,18 @@ #include <linux/i2c-id.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include "compat.h" MODULE_DESCRIPTION("tlv320aic23b driver"); MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil"); MODULE_LICENSE("GPL"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) static unsigned short normal_i2c[] = { 0x34 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; +#endif /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index a5ce43102..32d0246d5 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -856,11 +856,11 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, We need better support from the kernel so that we can easily wait for the load to finish. */ if (client == NULL || client->driver == NULL) - return NULL; + goto error; /* Lock the module so we can safely get the v4l2_subdev pointer */ if (!try_module_get(client->driver->driver.owner)) - return NULL; + goto error; sd = i2c_get_clientdata(client); /* Register with the v4l2_device which increases the module's @@ -869,8 +869,15 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, sd = NULL; /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); - return sd; +error: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (client && sd == NULL) + i2c_unregister_device(client); +#endif + return sd; } EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); @@ -918,11 +925,11 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, We need better support from the kernel so that we can easily wait for the load to finish. */ if (client == NULL || client->driver == NULL) - return NULL; + goto error; /* Lock the module so we can safely get the v4l2_subdev pointer */ if (!try_module_get(client->driver->driver.owner)) - return NULL; + goto error; sd = i2c_get_clientdata(client); /* Register with the v4l2_device which increases the module's @@ -931,6 +938,14 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, sd = NULL; /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); + +error: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (client && sd == NULL) + i2c_unregister_device(client); +#endif return sd; } EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev); diff --git a/linux/drivers/media/video/v4l2-compat-ioctl32.c b/linux/drivers/media/video/v4l2-compat-ioctl32.c index 05a356776..d9cee38cd 100644 --- a/linux/drivers/media/video/v4l2-compat-ioctl32.c +++ b/linux/drivers/media/video/v4l2-compat-ioctl32.c @@ -1052,7 +1052,6 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_DBG_S_REGISTER: case VIDIOC_DBG_G_REGISTER: case VIDIOC_DBG_G_CHIP_IDENT: - case VIDIOC_G_CHIP_IDENT_OLD: case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index 861e3194f..5e9b46c21 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -433,7 +433,7 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, vdev->vfl_type = type; vdev->cdev = NULL; - if (vdev->v4l2_dev) + if (vdev->v4l2_dev && vdev->v4l2_dev->dev) vdev->parent = vdev->v4l2_dev->dev; /* Part 2: find a free minor, kernel number and device index. */ @@ -654,6 +654,7 @@ module_exit(videodev_exit) MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2"); MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); /* diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c index e079a343a..5cf729c9d 100644 --- a/linux/drivers/media/video/v4l2-ioctl.c +++ b/linux/drivers/media/video/v4l2-ioctl.c @@ -17,6 +17,7 @@ #include <linux/kernel.h> #define __OLD_VIDIOC_ /* To allow fixing old calls */ +#include <linux/videodev.h> #include <linux/videodev2.h> #ifdef CONFIG_VIDEO_V4L1 @@ -25,7 +26,6 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-chip-ident.h> -#include <linux/video_decoder.h> #include "compat.h" #define dbgarg(cmd, fmt, arg...) \ @@ -277,19 +277,6 @@ static const char *v4l2_ioctls[] = { #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) static const char *v4l2_int_ioctls[] = { -#ifdef CONFIG_VIDEO_V4L1_COMPAT - [_IOC_NR(DECODER_GET_CAPABILITIES)] = "DECODER_GET_CAPABILITIES", - [_IOC_NR(DECODER_GET_STATUS)] = "DECODER_GET_STATUS", - [_IOC_NR(DECODER_SET_NORM)] = "DECODER_SET_NORM", - [_IOC_NR(DECODER_SET_INPUT)] = "DECODER_SET_INPUT", - [_IOC_NR(DECODER_SET_OUTPUT)] = "DECODER_SET_OUTPUT", - [_IOC_NR(DECODER_ENABLE_OUTPUT)] = "DECODER_ENABLE_OUTPUT", - [_IOC_NR(DECODER_SET_PICTURE)] = "DECODER_SET_PICTURE", - [_IOC_NR(DECODER_SET_GPIO)] = "DECODER_SET_GPIO", - [_IOC_NR(DECODER_INIT)] = "DECODER_INIT", - [_IOC_NR(DECODER_SET_VBI_BYPASS)] = "DECODER_SET_VBI_BYPASS", - [_IOC_NR(DECODER_DUMP)] = "DECODER_DUMP", -#endif [_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO", [_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR", @@ -1706,11 +1693,6 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); break; } - case VIDIOC_G_CHIP_IDENT_OLD: - printk(KERN_ERR "VIDIOC_G_CHIP_IDENT has been deprecated and will disappear in 2.6.30.\n"); - printk(KERN_ERR "It is a debugging ioctl and must not be used in applications!\n"); - return -EINVAL; - case VIDIOC_S_HW_FREQ_SEEK: { struct v4l2_hw_freq_seek *p = arg; @@ -1815,11 +1797,12 @@ static long __video_do_ioctl(struct file *file, static unsigned long cmd_input_size(unsigned int cmd) { /* Size of structure up to and including 'field' */ -#define CMDINSIZE(cmd, type, field) case _IOC_NR(VIDIOC_##cmd): return \ - offsetof(struct v4l2_##type, field) + \ - sizeof(((struct v4l2_##type *)0)->field); +#define CMDINSIZE(cmd, type, field) \ + case VIDIOC_##cmd: \ + return offsetof(struct v4l2_##type, field) + \ + sizeof(((struct v4l2_##type *)0)->field); - switch (_IOC_NR(cmd)) { + switch (cmd) { CMDINSIZE(ENUM_FMT, fmtdesc, type); CMDINSIZE(G_FMT, format, type); CMDINSIZE(QUERYBUF, buffer, type); diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c index 349ef053f..ba4e8b97d 100644 --- a/linux/drivers/media/video/vino.c +++ b/linux/drivers/media/video/vino.c @@ -8,6 +8,12 @@ * * Based on the previous version of the driver for 2.4 kernels by: * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> + * + * v4l2_device/v4l2_subdev conversion by: + * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl> + * + * Note: this conversion is untested! Please contact the linux-media + * mailinglist if you can test this, together with the test results. */ /* @@ -33,12 +39,10 @@ #include <linux/kmod.h> #include <linux/i2c.h> -#include <linux/i2c-algo-sgi.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> -#include <linux/video_decoder.h> #include <linux/mutex.h> #include <asm/paccess.h> @@ -139,13 +143,23 @@ MODULE_LICENSE("GPL"); #define VINO_DATA_NORM_PAL 1 #define VINO_DATA_NORM_SECAM 2 #define VINO_DATA_NORM_D1 3 -/* The following are special entries that can be used to - * autodetect the norm. */ -#define VINO_DATA_NORM_AUTO 0xfe -#define VINO_DATA_NORM_AUTO_EXT 0xff #define VINO_DATA_NORM_COUNT 4 +/* I2C controller flags */ +#define SGI_I2C_FORCE_IDLE (0 << 0) +#define SGI_I2C_NOT_IDLE (1 << 0) +#define SGI_I2C_WRITE (0 << 1) +#define SGI_I2C_READ (1 << 1) +#define SGI_I2C_RELEASE_BUS (0 << 2) +#define SGI_I2C_HOLD_BUS (1 << 2) +#define SGI_I2C_XFER_DONE (0 << 4) +#define SGI_I2C_XFER_BUSY (1 << 4) +#define SGI_I2C_ACK (0 << 5) +#define SGI_I2C_NACK (1 << 5) +#define SGI_I2C_BUS_OK (0 << 7) +#define SGI_I2C_BUS_ERR (1 << 7) + /* Internal data structure definitions */ struct vino_input { @@ -289,22 +303,20 @@ struct vino_channel_settings { struct vino_interrupt_data int_data; /* V4L support */ - struct video_device *v4l_device; -}; - -struct vino_client { - /* the channel which owns this client: - * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */ - unsigned int owner; - struct i2c_client *driver; + struct video_device *vdev; }; struct vino_settings { + struct v4l2_device v4l2_dev; struct vino_channel_settings a; struct vino_channel_settings b; - struct vino_client decoder; - struct vino_client camera; + /* the channel which owns this client: + * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */ + unsigned int decoder_owner; + struct v4l2_subdev *decoder; + unsigned int camera_owner; + struct v4l2_subdev *camera; /* a lock for vino register access */ spinlock_t vino_lock; @@ -344,11 +356,16 @@ static struct sgi_vino *vino; static struct vino_settings *vino_drvdata; +#define camera_call(o, f, args...) \ + v4l2_subdev_call(vino_drvdata->camera, o, f, ##args) +#define decoder_call(o, f, args...) \ + v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args) + static const char *vino_driver_name = "vino"; static const char *vino_driver_description = "SGI VINO"; static const char *vino_bus_name = "GIO64 bus"; -static const char *vino_v4l_device_name_a = "SGI VINO Channel A"; -static const char *vino_v4l_device_name_b = "SGI VINO Channel B"; +static const char *vino_vdev_name_a = "SGI VINO Channel A"; +static const char *vino_vdev_name_b = "SGI VINO Channel B"; static void vino_capture_tasklet(unsigned long channel); @@ -360,11 +377,11 @@ static const struct vino_input vino_inputs[] = { .name = "Composite", .std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - },{ + }, { .name = "S-Video", .std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - },{ + }, { .name = "D1/IndyCam", .std = V4L2_STD_NTSC, } @@ -376,17 +393,17 @@ static const struct vino_data_format vino_data_formats[] = { .bpp = 1, .pixelformat = V4L2_PIX_FMT_GREY, .colorspace = V4L2_COLORSPACE_SMPTE170M, - },{ + }, { .description = "8-bit dithered RGB 3-3-2", .bpp = 1, .pixelformat = V4L2_PIX_FMT_RGB332, .colorspace = V4L2_COLORSPACE_SRGB, - },{ + }, { .description = "32-bit RGB", .bpp = 4, .pixelformat = V4L2_PIX_FMT_RGB32, .colorspace = V4L2_COLORSPACE_SRGB, - },{ + }, { .description = "YUV 4:2:2", .bpp = 2, .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped? @@ -417,7 +434,7 @@ static const struct vino_data_norm vino_data_norms[] = { + VINO_NTSC_HEIGHT / 2 - 1, .right = VINO_NTSC_WIDTH, }, - },{ + }, { .description = "PAL", .std = V4L2_STD_PAL, .fps_min = 5, @@ -439,7 +456,7 @@ static const struct vino_data_norm vino_data_norms[] = { + VINO_PAL_HEIGHT / 2 - 1, .right = VINO_PAL_WIDTH, }, - },{ + }, { .description = "SECAM", .std = V4L2_STD_SECAM, .fps_min = 5, @@ -461,7 +478,7 @@ static const struct vino_data_norm vino_data_norms[] = { + VINO_PAL_HEIGHT / 2 - 1, .right = VINO_PAL_WIDTH, }, - },{ + }, { .description = "NTSC/D1", .std = V4L2_STD_NTSC, .fps_min = 6, @@ -497,9 +514,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = 1, .step = 1, .default_value = INDYCAM_AGC_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_AGC, 0 }, - },{ + }, { .id = V4L2_CID_AUTO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Automatic White Balance", @@ -507,9 +522,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = 1, .step = 1, .default_value = INDYCAM_AWB_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_AWB, 0 }, - },{ + }, { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", @@ -517,29 +530,23 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_GAIN_MAX, .step = 1, .default_value = INDYCAM_GAIN_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_GAIN, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE, + }, { + .id = INDYCAM_CONTROL_RED_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Red Saturation", .minimum = INDYCAM_RED_SATURATION_MIN, .maximum = INDYCAM_RED_SATURATION_MAX, .step = 1, .default_value = INDYCAM_RED_SATURATION_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_RED_SATURATION, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 1, + }, { + .id = INDYCAM_CONTROL_BLUE_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Blue Saturation", .minimum = INDYCAM_BLUE_SATURATION_MIN, .maximum = INDYCAM_BLUE_SATURATION_MAX, .step = 1, .default_value = INDYCAM_BLUE_SATURATION_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_BLUE_SATURATION, 0 }, - },{ + }, { .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Red Balance", @@ -547,9 +554,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_RED_BALANCE_MAX, .step = 1, .default_value = INDYCAM_RED_BALANCE_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_RED_BALANCE, 0 }, - },{ + }, { .id = V4L2_CID_BLUE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Blue Balance", @@ -557,9 +562,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_BLUE_BALANCE_MAX, .step = 1, .default_value = INDYCAM_BLUE_BALANCE_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_BLUE_BALANCE, 0 }, - },{ + }, { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Shutter Control", @@ -567,9 +570,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_SHUTTER_MAX, .step = 1, .default_value = INDYCAM_SHUTTER_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_SHUTTER, 0 }, - },{ + }, { .id = V4L2_CID_GAMMA, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gamma", @@ -577,8 +578,6 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_GAMMA_MAX, .step = 1, .default_value = INDYCAM_GAMMA_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_GAMMA, 0 }, } }; @@ -593,209 +592,73 @@ struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = { .maximum = SAA7191_HUE_MAX, .step = 1, .default_value = SAA7191_HUE_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_HUE, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE, + }, { + .id = SAA7191_CONTROL_BANDPASS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Luminance Bandpass", .minimum = SAA7191_BANDPASS_MIN, .maximum = SAA7191_BANDPASS_MAX, .step = 1, .default_value = SAA7191_BANDPASS_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_BANDPASS, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 1, + }, { + .id = SAA7191_CONTROL_BANDPASS_WEIGHT, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Luminance Bandpass Weight", .minimum = SAA7191_BANDPASS_WEIGHT_MIN, .maximum = SAA7191_BANDPASS_WEIGHT_MAX, .step = 1, .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_BANDPASS_WEIGHT, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 2, + }, { + .id = SAA7191_CONTROL_CORING, .type = V4L2_CTRL_TYPE_INTEGER, .name = "HF Luminance Coring", .minimum = SAA7191_CORING_MIN, .maximum = SAA7191_CORING_MAX, .step = 1, .default_value = SAA7191_CORING_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_CORING, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 3, + }, { + .id = SAA7191_CONTROL_FORCE_COLOUR, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Force Colour", .minimum = SAA7191_FORCE_COLOUR_MIN, .maximum = SAA7191_FORCE_COLOUR_MAX, .step = 1, .default_value = SAA7191_FORCE_COLOUR_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_FORCE_COLOUR, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 4, + }, { + .id = SAA7191_CONTROL_CHROMA_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Chrominance Gain Control", .minimum = SAA7191_CHROMA_GAIN_MIN, .maximum = SAA7191_CHROMA_GAIN_MAX, .step = 1, .default_value = SAA7191_CHROMA_GAIN_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_CHROMA_GAIN, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 5, + }, { + .id = SAA7191_CONTROL_VTRC, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "VTR Time Constant", .minimum = SAA7191_VTRC_MIN, .maximum = SAA7191_VTRC_MAX, .step = 1, .default_value = SAA7191_VTRC_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_VTRC, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 6, + }, { + .id = SAA7191_CONTROL_LUMA_DELAY, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Luminance Delay Compensation", .minimum = SAA7191_LUMA_DELAY_MIN, .maximum = SAA7191_LUMA_DELAY_MAX, .step = 1, .default_value = SAA7191_LUMA_DELAY_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_LUMA_DELAY, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 7, + }, { + .id = SAA7191_CONTROL_VNR, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Vertical Noise Reduction", .minimum = SAA7191_VNR_MIN, .maximum = SAA7191_VNR_MAX, .step = 1, .default_value = SAA7191_VNR_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_VNR, 0 }, - } -}; - -/* VINO I2C bus functions */ - -unsigned i2c_vino_getctrl(void *data) -{ - return vino->i2c_control; -} - -void i2c_vino_setctrl(void *data, unsigned val) -{ - vino->i2c_control = val; -} - -unsigned i2c_vino_rdata(void *data) -{ - return vino->i2c_data; -} - -void i2c_vino_wdata(void *data, unsigned val) -{ - vino->i2c_data = val; -} - -static struct i2c_algo_sgi_data i2c_sgi_vino_data = -{ - .getctrl = &i2c_vino_getctrl, - .setctrl = &i2c_vino_setctrl, - .rdata = &i2c_vino_rdata, - .wdata = &i2c_vino_wdata, - .xfer_timeout = 200, - .ack_timeout = 1000, -}; - -/* - * There are two possible clients on VINO I2C bus, so we limit usage only - * to them. - */ -static int i2c_vino_client_reg(struct i2c_client *client) -{ - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - switch (client->driver->id) { - case I2C_DRIVERID_SAA7191: - if (vino_drvdata->decoder.driver) - ret = -EBUSY; - else - vino_drvdata->decoder.driver = client; - break; - case I2C_DRIVERID_INDYCAM: - if (vino_drvdata->camera.driver) - ret = -EBUSY; - else - vino_drvdata->camera.driver = client; - break; - default: - ret = -ENODEV; - } - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return ret; -} - -static int i2c_vino_client_unreg(struct i2c_client *client) -{ - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - if (client == vino_drvdata->decoder.driver) { - if (vino_drvdata->decoder.owner != VINO_NO_CHANNEL) - ret = -EBUSY; - else - vino_drvdata->decoder.driver = NULL; - } else if (client == vino_drvdata->camera.driver) { - if (vino_drvdata->camera.owner != VINO_NO_CHANNEL) - ret = -EBUSY; - else - vino_drvdata->camera.driver = NULL; } - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return ret; -} - -static struct i2c_adapter vino_i2c_adapter = -{ - .name = "VINO I2C bus", - .id = I2C_HW_SGI_VINO, - .algo_data = &i2c_sgi_vino_data, - .client_register = &i2c_vino_client_reg, - .client_unregister = &i2c_vino_client_unreg, }; -static int vino_i2c_add_bus(void) -{ - return i2c_sgi_add_bus(&vino_i2c_adapter); -} - -static int vino_i2c_del_bus(void) -{ - return i2c_del_adapter(&vino_i2c_adapter); -} - -static int i2c_camera_command(unsigned int cmd, void *arg) -{ - return vino_drvdata->camera.driver-> - driver->command(vino_drvdata->camera.driver, - cmd, arg); -} - -static int i2c_decoder_command(unsigned int cmd, void *arg) -{ - return vino_drvdata->decoder.driver-> - driver->command(vino_drvdata->decoder.driver, - cmd, arg); -} - /* VINO framebuffer/DMA descriptor management */ static void vino_free_buffer_with_count(struct vino_framebuffer *fb, @@ -1741,6 +1604,184 @@ static inline void vino_set_default_framerate(struct vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max); } +/* VINO I2C bus functions */ + +struct i2c_algo_sgi_data { + void *data; /* private data for lowlevel routines */ + unsigned (*getctrl)(void *data); + void (*setctrl)(void *data, unsigned val); + unsigned (*rdata)(void *data); + void (*wdata)(void *data, unsigned val); + + int xfer_timeout; + int ack_timeout; +}; + +static int wait_xfer_done(struct i2c_algo_sgi_data *adap) +{ + int i; + + for (i = 0; i < adap->xfer_timeout; i++) { + if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int wait_ack(struct i2c_algo_sgi_data *adap) +{ + int i; + + if (wait_xfer_done(adap)) + return -ETIMEDOUT; + for (i = 0; i < adap->ack_timeout; i++) { + if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int force_idle(struct i2c_algo_sgi_data *adap) +{ + int i; + + adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE); + for (i = 0; i < adap->xfer_timeout; i++) { + if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0) + goto out; + udelay(1); + } + return -ETIMEDOUT; +out: + if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR) + return -EIO; + return 0; +} + +static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr, + int rd) +{ + if (rd) + adap->setctrl(adap->data, SGI_I2C_NOT_IDLE); + /* Check if bus is idle, eventually force it to do so */ + if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) + if (force_idle(adap)) + return -EIO; + /* Write out the i2c chip address and specify operation */ + adap->setctrl(adap->data, + SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE); + if (rd) + addr |= 1; + adap->wdata(adap->data, addr); + if (wait_ack(adap)) + return -EIO; + return 0; +} + +static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + + adap->setctrl(adap->data, + SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE); + for (i = 0; i < len; i++) { + if (wait_xfer_done(adap)) + return -EIO; + buf[i] = adap->rdata(adap->data); + } + adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE); + + return 0; + +} + +static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + + /* We are already in write state */ + for (i = 0; i < len; i++) { + adap->wdata(adap->data, buf[i]); + if (wait_ack(adap)) + return -EIO; + } + return 0; +} + +static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_sgi_data *adap = i2c_adap->algo_data; + struct i2c_msg *p; + int i, err = 0; + + for (i = 0; !err && i < num; i++) { + p = &msgs[i]; + err = do_address(adap, p->addr, p->flags & I2C_M_RD); + if (err || !p->len) + continue; + if (p->flags & I2C_M_RD) + err = i2c_read(adap, p->buf, p->len); + else + err = i2c_write(adap, p->buf, p->len); + } + + return (err < 0) ? err : i; +} + +static u32 sgi_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm sgi_algo = { + .master_xfer = sgi_xfer, + .functionality = sgi_func, +}; + +static unsigned i2c_vino_getctrl(void *data) +{ + return vino->i2c_control; +} + +static void i2c_vino_setctrl(void *data, unsigned val) +{ + vino->i2c_control = val; +} + +static unsigned i2c_vino_rdata(void *data) +{ + return vino->i2c_data; +} + +static void i2c_vino_wdata(void *data, unsigned val) +{ + vino->i2c_data = val; +} + +static struct i2c_algo_sgi_data i2c_sgi_vino_data = { + .getctrl = &i2c_vino_getctrl, + .setctrl = &i2c_vino_setctrl, + .rdata = &i2c_vino_rdata, + .wdata = &i2c_vino_wdata, + .xfer_timeout = 200, + .ack_timeout = 1000, +}; + +static struct i2c_adapter vino_i2c_adapter = { + .name = "VINO I2C bus", + .id = I2C_HW_SGI_VINO, + .algo = &sgi_algo, + .algo_data = &i2c_sgi_vino_data, + .owner = THIS_MODULE, +}; + /* * Prepare VINO for DMA transfer... * (execute only with vino_lock and input_lock locked) @@ -2494,86 +2535,15 @@ static int vino_get_saa7191_input(int input) } } -static int vino_get_saa7191_norm(unsigned int data_norm) -{ - switch (data_norm) { - case VINO_DATA_NORM_AUTO: - return SAA7191_NORM_AUTO; - case VINO_DATA_NORM_AUTO_EXT: - return SAA7191_NORM_AUTO_EXT; - case VINO_DATA_NORM_PAL: - return SAA7191_NORM_PAL; - case VINO_DATA_NORM_NTSC: - return SAA7191_NORM_NTSC; - case VINO_DATA_NORM_SECAM: - return SAA7191_NORM_SECAM; - default: - printk(KERN_ERR "VINO: vino_get_saa7191_norm(): " - "invalid norm!\n"); - return -1; - } -} - -static int vino_get_from_saa7191_norm(int saa7191_norm) -{ - switch (saa7191_norm) { - case SAA7191_NORM_PAL: - return VINO_DATA_NORM_PAL; - case SAA7191_NORM_NTSC: - return VINO_DATA_NORM_NTSC; - case SAA7191_NORM_SECAM: - return VINO_DATA_NORM_SECAM; - default: - printk(KERN_ERR "VINO: vino_get_from_saa7191_norm(): " - "invalid norm!\n"); - return VINO_DATA_NORM_NONE; - } -} - -static int vino_saa7191_set_norm(unsigned int *data_norm) -{ - int saa7191_norm, new_data_norm; - int err = 0; - - saa7191_norm = vino_get_saa7191_norm(*data_norm); - - err = i2c_decoder_command(DECODER_SAA7191_SET_NORM, - &saa7191_norm); - if (err) - goto out; - - if ((*data_norm == VINO_DATA_NORM_AUTO) - || (*data_norm == VINO_DATA_NORM_AUTO_EXT)) { - struct saa7191_status status; - - err = i2c_decoder_command(DECODER_SAA7191_GET_STATUS, - &status); - if (err) - goto out; - - new_data_norm = - vino_get_from_saa7191_norm(status.norm); - if (new_data_norm == VINO_DATA_NORM_NONE) { - err = -EINVAL; - goto out; - } - - *data_norm = (unsigned int)new_data_norm; - } - -out: - return err; -} - /* execute with input_lock locked */ static int vino_is_input_owner(struct vino_channel_settings *vcs) { switch(vcs->input) { case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: - return (vino_drvdata->decoder.owner == vcs->channel); + return vino_drvdata->decoder_owner == vcs->channel; case VINO_INPUT_D1: - return (vino_drvdata->camera.owner == vcs->channel); + return vino_drvdata->camera_owner == vcs->channel; default: return 0; } @@ -2589,23 +2559,22 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) spin_lock_irqsave(&vino_drvdata->input_lock, flags); /* First try D1 and then SAA7191 */ - if (vino_drvdata->camera.driver - && (vino_drvdata->camera.owner == VINO_NO_CHANNEL)) { - i2c_use_client(vino_drvdata->camera.driver); - vino_drvdata->camera.owner = vcs->channel; + if (vino_drvdata->camera + && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) { + vino_drvdata->camera_owner = vcs->channel; vcs->input = VINO_INPUT_D1; vcs->data_norm = VINO_DATA_NORM_D1; - } else if (vino_drvdata->decoder.driver - && (vino_drvdata->decoder.owner == VINO_NO_CHANNEL)) { - int input, data_norm; - int saa7191_input; + } else if (vino_drvdata->decoder + && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) { + int input; + int data_norm; + v4l2_std_id norm; + struct v4l2_routing route = { 0, 0 }; - i2c_use_client(vino_drvdata->decoder.driver); input = VINO_INPUT_COMPOSITE; - saa7191_input = vino_get_saa7191_input(input); - ret = i2c_decoder_command(DECODER_SET_INPUT, - &saa7191_input); + route.input = vino_get_saa7191_input(input); + ret = decoder_call(video, s_routing, &route); if (ret) { ret = -EINVAL; goto out; @@ -2616,12 +2585,15 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) /* Don't hold spinlocks while auto-detecting norm * as it may take a while... */ - data_norm = VINO_DATA_NORM_AUTO_EXT; - - ret = vino_saa7191_set_norm(&data_norm); - if ((ret == -EBUSY) || (ret == -EAGAIN)) { - data_norm = VINO_DATA_NORM_PAL; - ret = vino_saa7191_set_norm(&data_norm); + ret = decoder_call(video, querystd, &norm); + if (!ret) { + for (data_norm = 0; data_norm < 3; data_norm++) { + if (vino_data_norms[data_norm].std & norm) + break; + } + if (data_norm == 3) + data_norm = VINO_DATA_NORM_PAL; + ret = decoder_call(tuner, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2631,7 +2603,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) goto out; } - vino_drvdata->decoder.owner = vcs->channel; + vino_drvdata->decoder_owner = vcs->channel; vcs->input = input; vcs->data_norm = data_norm; @@ -2676,25 +2648,24 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) switch (input) { case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: - if (!vino_drvdata->decoder.driver) { + if (!vino_drvdata->decoder) { ret = -EINVAL; goto out; } - if (vino_drvdata->decoder.owner == VINO_NO_CHANNEL) { - i2c_use_client(vino_drvdata->decoder.driver); - vino_drvdata->decoder.owner = vcs->channel; + if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) { + vino_drvdata->decoder_owner = vcs->channel; } - if (vino_drvdata->decoder.owner == vcs->channel) { + if (vino_drvdata->decoder_owner == vcs->channel) { int data_norm; - int saa7191_input; + v4l2_std_id norm; + struct v4l2_routing route = { 0, 0 }; - saa7191_input = vino_get_saa7191_input(input); - ret = i2c_decoder_command(DECODER_SET_INPUT, - &saa7191_input); + route.input = vino_get_saa7191_input(input); + ret = decoder_call(video, s_routing, &route); if (ret) { - vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + vino_drvdata->decoder_owner = VINO_NO_CHANNEL; ret = -EINVAL; goto out; } @@ -2704,18 +2675,21 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) /* Don't hold spinlocks while auto-detecting norm * as it may take a while... */ - data_norm = VINO_DATA_NORM_AUTO_EXT; - - ret = vino_saa7191_set_norm(&data_norm); - if ((ret == -EBUSY) || (ret == -EAGAIN)) { - data_norm = VINO_DATA_NORM_PAL; - ret = vino_saa7191_set_norm(&data_norm); + ret = decoder_call(video, querystd, &norm); + if (!ret) { + for (data_norm = 0; data_norm < 3; data_norm++) { + if (vino_data_norms[data_norm].std & norm) + break; + } + if (data_norm == 3) + data_norm = VINO_DATA_NORM_PAL; + ret = decoder_call(tuner, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); if (ret) { - vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + vino_drvdata->decoder_owner = VINO_NO_CHANNEL; ret = -EINVAL; goto out; } @@ -2732,37 +2706,31 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) vcs->data_norm = vcs2->data_norm; } - if (vino_drvdata->camera.owner == vcs->channel) { + if (vino_drvdata->camera_owner == vcs->channel) { /* Transfer the ownership or release the input */ if (vcs2->input == VINO_INPUT_D1) { - vino_drvdata->camera.owner = vcs2->channel; + vino_drvdata->camera_owner = vcs2->channel; } else { - i2c_release_client(vino_drvdata-> - camera.driver); - vino_drvdata->camera.owner = VINO_NO_CHANNEL; + vino_drvdata->camera_owner = VINO_NO_CHANNEL; } } break; case VINO_INPUT_D1: - if (!vino_drvdata->camera.driver) { + if (!vino_drvdata->camera) { ret = -EINVAL; goto out; } - if (vino_drvdata->camera.owner == VINO_NO_CHANNEL) { - i2c_use_client(vino_drvdata->camera.driver); - vino_drvdata->camera.owner = vcs->channel; - } + if (vino_drvdata->camera_owner == VINO_NO_CHANNEL) + vino_drvdata->camera_owner = vcs->channel; - if (vino_drvdata->decoder.owner == vcs->channel) { + if (vino_drvdata->decoder_owner == vcs->channel) { /* Transfer the ownership or release the input */ if ((vcs2->input == VINO_INPUT_COMPOSITE) || (vcs2->input == VINO_INPUT_SVIDEO)) { - vino_drvdata->decoder.owner = vcs2->channel; + vino_drvdata->decoder_owner = vcs2->channel; } else { - i2c_release_client(vino_drvdata-> - decoder.driver); - vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + vino_drvdata->decoder_owner = VINO_NO_CHANNEL; } } @@ -2799,20 +2767,18 @@ static void vino_release_input(struct vino_channel_settings *vcs) /* Release ownership of the channel * and if the other channel takes input from * the same source, transfer the ownership */ - if (vino_drvdata->camera.owner == vcs->channel) { + if (vino_drvdata->camera_owner == vcs->channel) { if (vcs2->input == VINO_INPUT_D1) { - vino_drvdata->camera.owner = vcs2->channel; + vino_drvdata->camera_owner = vcs2->channel; } else { - i2c_release_client(vino_drvdata->camera.driver); - vino_drvdata->camera.owner = VINO_NO_CHANNEL; + vino_drvdata->camera_owner = VINO_NO_CHANNEL; } - } else if (vino_drvdata->decoder.owner == vcs->channel) { + } else if (vino_drvdata->decoder_owner == vcs->channel) { if ((vcs2->input == VINO_INPUT_COMPOSITE) || (vcs2->input == VINO_INPUT_SVIDEO)) { - vino_drvdata->decoder.owner = vcs2->channel; + vino_drvdata->decoder_owner = vcs2->channel; } else { - i2c_release_client(vino_drvdata->decoder.driver); - vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + vino_drvdata->decoder_owner = VINO_NO_CHANNEL; } } vcs->input = VINO_INPUT_NONE; @@ -2833,18 +2799,16 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs, switch (vcs->input) { case VINO_INPUT_D1: /* only one "norm" supported */ - if ((data_norm != VINO_DATA_NORM_D1) - && (data_norm != VINO_DATA_NORM_AUTO) - && (data_norm != VINO_DATA_NORM_AUTO_EXT)) + if (data_norm != VINO_DATA_NORM_D1) return -EINVAL; break; case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: { + v4l2_std_id norm; + if ((data_norm != VINO_DATA_NORM_PAL) && (data_norm != VINO_DATA_NORM_NTSC) - && (data_norm != VINO_DATA_NORM_SECAM) - && (data_norm != VINO_DATA_NORM_AUTO) - && (data_norm != VINO_DATA_NORM_AUTO_EXT)) + && (data_norm != VINO_DATA_NORM_SECAM)) return -EINVAL; spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags); @@ -2852,7 +2816,8 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs, /* Don't hold spinlocks while setting norm * as it may take a while... */ - err = vino_saa7191_set_norm(&data_norm); + norm = vino_data_norms[data_norm].std; + err = decoder_call(tuner, s_std, norm); spin_lock_irqsave(&vino_drvdata->input_lock, *flags); @@ -2888,41 +2853,13 @@ static int vino_find_data_format(__u32 pixelformat) return VINO_DATA_FMT_NONE; } -static int vino_enum_data_norm(struct vino_channel_settings *vcs, __u32 index) -{ - int data_norm = VINO_DATA_NORM_NONE; - unsigned long flags; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - switch(vcs->input) { - case VINO_INPUT_COMPOSITE: - case VINO_INPUT_SVIDEO: - if (index == 0) { - data_norm = VINO_DATA_NORM_PAL; - } else if (index == 1) { - data_norm = VINO_DATA_NORM_NTSC; - } else if (index == 2) { - data_norm = VINO_DATA_NORM_SECAM; - } - break; - case VINO_INPUT_D1: - if (index == 0) { - data_norm = VINO_DATA_NORM_D1; - } - break; - } - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return data_norm; -} - -static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index) +static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index) { int input = VINO_INPUT_NONE; unsigned long flags; spin_lock_irqsave(&vino_drvdata->input_lock, flags); - if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) { + if (vino_drvdata->decoder && vino_drvdata->camera) { switch (index) { case 0: input = VINO_INPUT_COMPOSITE; @@ -2934,7 +2871,7 @@ static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index) input = VINO_INPUT_D1; break; } - } else if (vino_drvdata->decoder.driver) { + } else if (vino_drvdata->decoder) { switch (index) { case 0: input = VINO_INPUT_COMPOSITE; @@ -2943,7 +2880,7 @@ static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index) input = VINO_INPUT_SVIDEO; break; } - } else if (vino_drvdata->camera.driver) { + } else if (vino_drvdata->camera) { switch (index) { case 0: input = VINO_INPUT_D1; @@ -2961,7 +2898,7 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs) __u32 index = 0; // FIXME: detect when no inputs available - if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) { + if (vino_drvdata->decoder && vino_drvdata->camera) { switch (vcs->input) { case VINO_INPUT_COMPOSITE: index = 0; @@ -2973,7 +2910,7 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs) index = 2; break; } - } else if (vino_drvdata->decoder.driver) { + } else if (vino_drvdata->decoder) { switch (vcs->input) { case VINO_INPUT_COMPOSITE: index = 0; @@ -2982,7 +2919,7 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs) index = 1; break; } - } else if (vino_drvdata->camera.driver) { + } else if (vino_drvdata->camera) { switch (vcs->input) { case VINO_INPUT_D1: index = 0; @@ -2995,7 +2932,8 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs) /* V4L2 ioctls */ -static void vino_v4l2_querycap(struct v4l2_capability *cap) +static int vino_querycap(struct file *file, void *__fh, + struct v4l2_capability *cap) { memset(cap, 0, sizeof(struct v4l2_capability)); @@ -3007,16 +2945,18 @@ static void vino_v4l2_querycap(struct v4l2_capability *cap) V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; // V4L2_CAP_OVERLAY, V4L2_CAP_READWRITE + return 0; } -static int vino_v4l2_enuminput(struct vino_channel_settings *vcs, +static int vino_enum_input(struct file *file, void *__fh, struct v4l2_input *i) { + struct vino_channel_settings *vcs = video_drvdata(file); __u32 index = i->index; int input; dprintk("requested index = %d\n", index); - input = vino_enum_input(vcs, index); + input = vino_int_enum_input(vcs, index); if (input == VINO_INPUT_NONE) return -EINVAL; @@ -3027,20 +2967,15 @@ static int vino_v4l2_enuminput(struct vino_channel_settings *vcs, i->std = vino_inputs[input].std; strcpy(i->name, vino_inputs[input].name); - if ((input == VINO_INPUT_COMPOSITE) - || (input == VINO_INPUT_SVIDEO)) { - struct saa7191_status status; - i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status); - i->status |= status.signal ? 0 : V4L2_IN_ST_NO_SIGNAL; - i->status |= status.color ? 0 : V4L2_IN_ST_NO_COLOR; - } - + if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO) + decoder_call(video, g_input_status, &i->status); return 0; } -static int vino_v4l2_g_input(struct vino_channel_settings *vcs, +static int vino_g_input(struct file *file, void *__fh, unsigned int *i) { + struct vino_channel_settings *vcs = video_drvdata(file); __u32 index; int input; unsigned long flags; @@ -3061,52 +2996,24 @@ static int vino_v4l2_g_input(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_s_input(struct vino_channel_settings *vcs, - unsigned int *i) +static int vino_s_input(struct file *file, void *__fh, + unsigned int i) { + struct vino_channel_settings *vcs = video_drvdata(file); int input; - dprintk("requested input = %d\n", *i); + dprintk("requested input = %d\n", i); - input = vino_enum_input(vcs, *i); + input = vino_int_enum_input(vcs, i); if (input == VINO_INPUT_NONE) return -EINVAL; return vino_set_input(vcs, input); } -static int vino_v4l2_enumstd(struct vino_channel_settings *vcs, - struct v4l2_standard *s) -{ - int index = s->index; - int data_norm; - - data_norm = vino_enum_data_norm(vcs, index); - dprintk("standard index = %d\n", index); - - if (data_norm == VINO_DATA_NORM_NONE) - return -EINVAL; - - dprintk("standard name = %s\n", - vino_data_norms[data_norm].description); - - memset(s, 0, sizeof(struct v4l2_standard)); - s->index = index; - - s->id = vino_data_norms[data_norm].std; - s->frameperiod.numerator = 1; - s->frameperiod.denominator = - vino_data_norms[data_norm].fps_max; - s->framelines = - vino_data_norms[data_norm].framelines; - strcpy(s->name, - vino_data_norms[data_norm].description); - - return 0; -} - -static int vino_v4l2_querystd(struct vino_channel_settings *vcs, +static int vino_querystd(struct file *file, void *__fh, v4l2_std_id *std) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int err = 0; @@ -3118,19 +3025,7 @@ static int vino_v4l2_querystd(struct vino_channel_settings *vcs, break; case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: { - struct saa7191_status status; - - i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status); - - if (status.signal) { - if (status.signal_60hz) { - *std = V4L2_STD_NTSC; - } else { - *std = V4L2_STD_PAL | V4L2_STD_SECAM; - } - } else { - *std = vino_inputs[vcs->input].std; - } + decoder_call(video, querystd, std); break; } default: @@ -3142,9 +3037,10 @@ static int vino_v4l2_querystd(struct vino_channel_settings *vcs, return err; } -static int vino_v4l2_g_std(struct vino_channel_settings *vcs, +static int vino_g_std(struct file *file, void *__fh, v4l2_std_id *std) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -3157,9 +3053,10 @@ static int vino_v4l2_g_std(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_s_std(struct vino_channel_settings *vcs, +static int vino_s_std(struct file *file, void *__fh, v4l2_std_id *std) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int ret = 0; @@ -3180,12 +3077,7 @@ static int vino_v4l2_s_std(struct vino_channel_settings *vcs, if (vcs->input == VINO_INPUT_D1) goto out; - if (((*std) & V4L2_STD_PAL) - && ((*std) & V4L2_STD_NTSC) - && ((*std) & V4L2_STD_SECAM)) { - ret = vino_set_data_norm(vcs, VINO_DATA_NORM_AUTO_EXT, - &flags); - } else if ((*std) & V4L2_STD_PAL) { + if ((*std) & V4L2_STD_PAL) { ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL, &flags); } else if ((*std) & V4L2_STD_NTSC) { @@ -3211,185 +3103,152 @@ out: return ret; } -static int vino_v4l2_enum_fmt(struct vino_channel_settings *vcs, +static int vino_enum_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_fmtdesc *fd) { enum v4l2_buf_type type = fd->type; int index = fd->index; + dprintk("format index = %d\n", index); - switch (fd->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if ((fd->index < 0) || - (fd->index >= VINO_DATA_FMT_COUNT)) - return -EINVAL; - dprintk("format name = %s\n", - vino_data_formats[index].description); - - memset(fd, 0, sizeof(struct v4l2_fmtdesc)); - fd->index = index; - fd->type = type; - fd->pixelformat = vino_data_formats[index].pixelformat; - strcpy(fd->description, vino_data_formats[index].description); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: + if ((fd->index < 0) || + (fd->index >= VINO_DATA_FMT_COUNT)) return -EINVAL; - } - + dprintk("format name = %s\n", + vino_data_formats[index].description); + + memset(fd, 0, sizeof(struct v4l2_fmtdesc)); + fd->index = index; + fd->type = type; + fd->pixelformat = vino_data_formats[index].pixelformat; + strcpy(fd->description, vino_data_formats[index].description); return 0; } -static int vino_v4l2_try_fmt(struct vino_channel_settings *vcs, +static int vino_try_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) { + struct vino_channel_settings *vcs = video_drvdata(file); struct vino_channel_settings tempvcs; unsigned long flags; + struct v4l2_pix_format *pf = &f->fmt.pix; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_pix_format *pf = &f->fmt.pix; - - dprintk("requested: w = %d, h = %d\n", - pf->width, pf->height); - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings)); - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + dprintk("requested: w = %d, h = %d\n", + pf->width, pf->height); - tempvcs.data_format = vino_find_data_format(pf->pixelformat); - if (tempvcs.data_format == VINO_DATA_FMT_NONE) { - tempvcs.data_format = VINO_DATA_FMT_GREY; - pf->pixelformat = - vino_data_formats[tempvcs.data_format]. - pixelformat; - } + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings)); + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - /* data format must be set before clipping/scaling */ - vino_set_scaling(&tempvcs, pf->width, pf->height); + tempvcs.data_format = vino_find_data_format(pf->pixelformat); + if (tempvcs.data_format == VINO_DATA_FMT_NONE) { + tempvcs.data_format = VINO_DATA_FMT_GREY; + pf->pixelformat = + vino_data_formats[tempvcs.data_format]. + pixelformat; + } - dprintk("data format = %s\n", - vino_data_formats[tempvcs.data_format].description); + /* data format must be set before clipping/scaling */ + vino_set_scaling(&tempvcs, pf->width, pf->height); - pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) / - tempvcs.decimation; - pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) / - tempvcs.decimation; + dprintk("data format = %s\n", + vino_data_formats[tempvcs.data_format].description); - pf->field = V4L2_FIELD_INTERLACED; - pf->bytesperline = tempvcs.line_size; - pf->sizeimage = tempvcs.line_size * - (tempvcs.clipping.bottom - tempvcs.clipping.top) / - tempvcs.decimation; - pf->colorspace = - vino_data_formats[tempvcs.data_format].colorspace; + pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) / + tempvcs.decimation; + pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) / + tempvcs.decimation; - pf->priv = 0; - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + pf->field = V4L2_FIELD_INTERLACED; + pf->bytesperline = tempvcs.line_size; + pf->sizeimage = tempvcs.line_size * + (tempvcs.clipping.bottom - tempvcs.clipping.top) / + tempvcs.decimation; + pf->colorspace = + vino_data_formats[tempvcs.data_format].colorspace; + pf->priv = 0; return 0; } -static int vino_v4l2_g_fmt(struct vino_channel_settings *vcs, +static int vino_g_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; + struct v4l2_pix_format *pf = &f->fmt.pix; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_pix_format *pf = &f->fmt.pix; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - pf->width = (vcs->clipping.right - vcs->clipping.left) / - vcs->decimation; - pf->height = (vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation; - pf->pixelformat = - vino_data_formats[vcs->data_format].pixelformat; + spin_lock_irqsave(&vino_drvdata->input_lock, flags); - pf->field = V4L2_FIELD_INTERLACED; - pf->bytesperline = vcs->line_size; - pf->sizeimage = vcs->line_size * - (vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation; - pf->colorspace = - vino_data_formats[vcs->data_format].colorspace; + pf->width = (vcs->clipping.right - vcs->clipping.left) / + vcs->decimation; + pf->height = (vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation; + pf->pixelformat = + vino_data_formats[vcs->data_format].pixelformat; - pf->priv = 0; + pf->field = V4L2_FIELD_INTERLACED; + pf->bytesperline = vcs->line_size; + pf->sizeimage = vcs->line_size * + (vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation; + pf->colorspace = + vino_data_formats[vcs->data_format].colorspace; - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + pf->priv = 0; + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); return 0; } -static int vino_v4l2_s_fmt(struct vino_channel_settings *vcs, +static int vino_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) { + struct vino_channel_settings *vcs = video_drvdata(file); int data_format; unsigned long flags; + struct v4l2_pix_format *pf = &f->fmt.pix; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_pix_format *pf = &f->fmt.pix; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); + spin_lock_irqsave(&vino_drvdata->input_lock, flags); - data_format = vino_find_data_format(pf->pixelformat); + data_format = vino_find_data_format(pf->pixelformat); - if (data_format == VINO_DATA_FMT_NONE) { - vcs->data_format = VINO_DATA_FMT_GREY; - pf->pixelformat = - vino_data_formats[vcs->data_format]. - pixelformat; - } else { - vcs->data_format = data_format; - } - - /* data format must be set before clipping/scaling */ - vino_set_scaling(vcs, pf->width, pf->height); + if (data_format == VINO_DATA_FMT_NONE) { + vcs->data_format = VINO_DATA_FMT_GREY; + pf->pixelformat = + vino_data_formats[vcs->data_format]. + pixelformat; + } else { + vcs->data_format = data_format; + } - dprintk("data format = %s\n", - vino_data_formats[vcs->data_format].description); + /* data format must be set before clipping/scaling */ + vino_set_scaling(vcs, pf->width, pf->height); - pf->width = vcs->clipping.right - vcs->clipping.left; - pf->height = vcs->clipping.bottom - vcs->clipping.top; + dprintk("data format = %s\n", + vino_data_formats[vcs->data_format].description); - pf->field = V4L2_FIELD_INTERLACED; - pf->bytesperline = vcs->line_size; - pf->sizeimage = vcs->line_size * - (vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation; - pf->colorspace = - vino_data_formats[vcs->data_format].colorspace; + pf->width = vcs->clipping.right - vcs->clipping.left; + pf->height = vcs->clipping.bottom - vcs->clipping.top; - pf->priv = 0; + pf->field = V4L2_FIELD_INTERLACED; + pf->bytesperline = vcs->line_size; + pf->sizeimage = vcs->line_size * + (vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation; + pf->colorspace = + vino_data_formats[vcs->data_format].colorspace; - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + pf->priv = 0; + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); return 0; } -static int vino_v4l2_cropcap(struct vino_channel_settings *vcs, +static int vino_cropcap(struct file *file, void *__fh, struct v4l2_cropcap *ccap) { + struct vino_channel_settings *vcs = video_drvdata(file); const struct vino_data_norm *norm; unsigned long flags; @@ -3419,9 +3278,10 @@ static int vino_v4l2_cropcap(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_g_crop(struct vino_channel_settings *vcs, +static int vino_g_crop(struct file *file, void *__fh, struct v4l2_crop *c) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; switch (c->type) { @@ -3443,9 +3303,10 @@ static int vino_v4l2_g_crop(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_s_crop(struct vino_channel_settings *vcs, +static int vino_s_crop(struct file *file, void *__fh, struct v4l2_crop *c) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; switch (c->type) { @@ -3465,9 +3326,10 @@ static int vino_v4l2_s_crop(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_g_parm(struct vino_channel_settings *vcs, +static int vino_g_parm(struct file *file, void *__fh, struct v4l2_streamparm *sp) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; switch (sp->type) { @@ -3495,9 +3357,10 @@ static int vino_v4l2_g_parm(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_s_parm(struct vino_channel_settings *vcs, +static int vino_s_parm(struct file *file, void *__fh, struct v4l2_streamparm *sp) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; switch (sp->type) { @@ -3528,9 +3391,10 @@ static int vino_v4l2_s_parm(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_reqbufs(struct vino_channel_settings *vcs, +static int vino_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *rb) { + struct vino_channel_settings *vcs = video_drvdata(file); if (vcs->reading) return -EBUSY; @@ -3610,9 +3474,10 @@ static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs, fb->id, fb->size, fb->data_size, fb->offset); } -static int vino_v4l2_querybuf(struct vino_channel_settings *vcs, +static int vino_querybuf(struct file *file, void *__fh, struct v4l2_buffer *b) { + struct vino_channel_settings *vcs = video_drvdata(file); if (vcs->reading) return -EBUSY; @@ -3645,9 +3510,10 @@ static int vino_v4l2_querybuf(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_qbuf(struct vino_channel_settings *vcs, +static int vino_qbuf(struct file *file, void *__fh, struct v4l2_buffer *b) { + struct vino_channel_settings *vcs = video_drvdata(file); if (vcs->reading) return -EBUSY; @@ -3683,10 +3549,11 @@ static int vino_v4l2_qbuf(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_dqbuf(struct vino_channel_settings *vcs, - struct v4l2_buffer *b, - unsigned int nonblocking) +static int vino_dqbuf(struct file *file, void *__fh, + struct v4l2_buffer *b) { + struct vino_channel_settings *vcs = video_drvdata(file); + unsigned int nonblocking = file->f_flags & O_NONBLOCK; if (vcs->reading) return -EBUSY; @@ -3758,8 +3625,10 @@ static int vino_v4l2_dqbuf(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_streamon(struct vino_channel_settings *vcs) +static int vino_streamon(struct file *file, void *__fh, + enum v4l2_buf_type i) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned int incoming; int ret; if (vcs->reading) @@ -3796,8 +3665,10 @@ static int vino_v4l2_streamon(struct vino_channel_settings *vcs) return 0; } -static int vino_v4l2_streamoff(struct vino_channel_settings *vcs) +static int vino_streamoff(struct file *file, void *__fh, + enum v4l2_buf_type i) { + struct vino_channel_settings *vcs = video_drvdata(file); if (vcs->reading) return -EBUSY; @@ -3810,9 +3681,10 @@ static int vino_v4l2_streamoff(struct vino_channel_settings *vcs) return 0; } -static int vino_v4l2_queryctrl(struct vino_channel_settings *vcs, +static int vino_queryctrl(struct file *file, void *__fh, struct v4l2_queryctrl *queryctrl) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int i; int err = 0; @@ -3859,9 +3731,10 @@ static int vino_v4l2_queryctrl(struct vino_channel_settings *vcs, return err; } -static int vino_v4l2_g_ctrl(struct vino_channel_settings *vcs, +static int vino_g_ctrl(struct file *file, void *__fh, struct v4l2_control *control) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int i; int err = 0; @@ -3870,56 +3743,38 @@ static int vino_v4l2_g_ctrl(struct vino_channel_settings *vcs, switch (vcs->input) { case VINO_INPUT_D1: { - struct indycam_control indycam_ctrl; - + err = -EINVAL; for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { - if (vino_indycam_v4l2_controls[i].id == - control->id) { - goto found1; + if (vino_indycam_v4l2_controls[i].id == control->id) { + err = 0; + break; } } - err = -EINVAL; - goto out; - -found1: - indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0]; - - err = i2c_camera_command(DECODER_INDYCAM_GET_CONTROL, - &indycam_ctrl); - if (err) { - err = -EINVAL; + if (err) goto out; - } - control->value = indycam_ctrl.value; + err = camera_call(core, g_ctrl, control); + if (err) + err = -EINVAL; break; } case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: { - struct saa7191_control saa7191_ctrl; - + err = -EINVAL; for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { - if (vino_saa7191_v4l2_controls[i].id == - control->id) { - goto found2; + if (vino_saa7191_v4l2_controls[i].id == control->id) { + err = 0; + break; } } - err = -EINVAL; - goto out; - -found2: - saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0]; - - err = i2c_decoder_command(DECODER_SAA7191_GET_CONTROL, - &saa7191_ctrl); - if (err) { - err = -EINVAL; + if (err) goto out; - } - control->value = saa7191_ctrl.value; + err = decoder_call(core, g_ctrl, control); + if (err) + err = -EINVAL; break; } default: @@ -3932,9 +3787,10 @@ out: return err; } -static int vino_v4l2_s_ctrl(struct vino_channel_settings *vcs, +static int vino_s_ctrl(struct file *file, void *__fh, struct v4l2_control *control) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int i; int err = 0; @@ -3948,65 +3804,43 @@ static int vino_v4l2_s_ctrl(struct vino_channel_settings *vcs, switch (vcs->input) { case VINO_INPUT_D1: { - struct indycam_control indycam_ctrl; - + err = -EINVAL; for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { - if (vino_indycam_v4l2_controls[i].id == - control->id) { - if ((control->value >= - vino_indycam_v4l2_controls[i].minimum) - && (control->value <= - vino_indycam_v4l2_controls[i]. - maximum)) { - goto found1; - } else { - err = -ERANGE; - goto out; - } + if (vino_indycam_v4l2_controls[i].id == control->id) { + err = 0; + break; } } - - err = -EINVAL; - goto out; - -found1: - indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0]; - indycam_ctrl.value = control->value; - - err = i2c_camera_command(DECODER_INDYCAM_SET_CONTROL, - &indycam_ctrl); + if (err) + goto out; + if (control->value < vino_indycam_v4l2_controls[i].minimum || + control->value > vino_indycam_v4l2_controls[i].maximum) { + err = -ERANGE; + goto out; + } + err = camera_call(core, s_ctrl, control); if (err) err = -EINVAL; break; } case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: { - struct saa7191_control saa7191_ctrl; - + err = -EINVAL; for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { - if (vino_saa7191_v4l2_controls[i].id == - control->id) { - if ((control->value >= - vino_saa7191_v4l2_controls[i].minimum) - && (control->value <= - vino_saa7191_v4l2_controls[i]. - maximum)) { - goto found2; - } else { - err = -ERANGE; - goto out; - } + if (vino_saa7191_v4l2_controls[i].id == control->id) { + err = 0; + break; } } - err = -EINVAL; - goto out; - -found2: - saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0]; - saa7191_ctrl.value = control->value; + if (err) + goto out; + if (control->value < vino_saa7191_v4l2_controls[i].minimum || + control->value > vino_saa7191_v4l2_controls[i].maximum) { + err = -ERANGE; + goto out; + } - err = i2c_decoder_command(DECODER_SAA7191_SET_CONTROL, - &saa7191_ctrl); + err = decoder_call(core, s_ctrl, control); if (err) err = -EINVAL; break; @@ -4237,116 +4071,9 @@ over: ret = POLLIN | POLLRDNORM; error: - return ret; } -static long vino_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - -#ifdef VINO_DEBUG - switch (_IOC_TYPE(cmd)) { - case 'v': - dprintk("ioctl(): V4L1 unsupported (0x%08x)\n", cmd); - break; - case 'V': - dprintk("ioctl(): V4L2 %s (0x%08x)\n", - v4l2_ioctl_names[_IOC_NR(cmd)], cmd); - break; - default: - dprintk("ioctl(): unsupported command 0x%08x\n", cmd); - } -#endif - - switch (cmd) { - /* V4L2 interface */ - case VIDIOC_QUERYCAP: { - vino_v4l2_querycap(arg); - break; - } - case VIDIOC_ENUMINPUT: { - return vino_v4l2_enuminput(vcs, arg); - } - case VIDIOC_G_INPUT: { - return vino_v4l2_g_input(vcs, arg); - } - case VIDIOC_S_INPUT: { - return vino_v4l2_s_input(vcs, arg); - } - case VIDIOC_ENUMSTD: { - return vino_v4l2_enumstd(vcs, arg); - } - case VIDIOC_QUERYSTD: { - return vino_v4l2_querystd(vcs, arg); - } - case VIDIOC_G_STD: { - return vino_v4l2_g_std(vcs, arg); - } - case VIDIOC_S_STD: { - return vino_v4l2_s_std(vcs, arg); - } - case VIDIOC_ENUM_FMT: { - return vino_v4l2_enum_fmt(vcs, arg); - } - case VIDIOC_TRY_FMT: { - return vino_v4l2_try_fmt(vcs, arg); - } - case VIDIOC_G_FMT: { - return vino_v4l2_g_fmt(vcs, arg); - } - case VIDIOC_S_FMT: { - return vino_v4l2_s_fmt(vcs, arg); - } - case VIDIOC_CROPCAP: { - return vino_v4l2_cropcap(vcs, arg); - } - case VIDIOC_G_CROP: { - return vino_v4l2_g_crop(vcs, arg); - } - case VIDIOC_S_CROP: { - return vino_v4l2_s_crop(vcs, arg); - } - case VIDIOC_G_PARM: { - return vino_v4l2_g_parm(vcs, arg); - } - case VIDIOC_S_PARM: { - return vino_v4l2_s_parm(vcs, arg); - } - case VIDIOC_REQBUFS: { - return vino_v4l2_reqbufs(vcs, arg); - } - case VIDIOC_QUERYBUF: { - return vino_v4l2_querybuf(vcs, arg); - } - case VIDIOC_QBUF: { - return vino_v4l2_qbuf(vcs, arg); - } - case VIDIOC_DQBUF: { - return vino_v4l2_dqbuf(vcs, arg, file->f_flags & O_NONBLOCK); - } - case VIDIOC_STREAMON: { - return vino_v4l2_streamon(vcs); - } - case VIDIOC_STREAMOFF: { - return vino_v4l2_streamoff(vcs); - } - case VIDIOC_QUERYCTRL: { - return vino_v4l2_queryctrl(vcs, arg); - } - case VIDIOC_G_CTRL: { - return vino_v4l2_g_ctrl(vcs, arg); - } - case VIDIOC_S_CTRL: { - return vino_v4l2_s_ctrl(vcs, arg); - } - default: - return -ENOIOCTLCMD; - } - - return 0; -} - static long vino_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -4356,7 +4083,7 @@ static long vino_ioctl(struct file *file, if (mutex_lock_interruptible(&vcs->mutex)) return -EINTR; - ret = video_usercopy(file, cmd, arg, vino_do_ioctl); + ret = video_ioctl2(file, cmd, arg); mutex_unlock(&vcs->mutex); @@ -4368,45 +4095,75 @@ static long vino_ioctl(struct file *file, /* __initdata */ static int vino_init_stage; +const struct v4l2_ioctl_ops vino_ioctl_ops = { + .vidioc_enum_fmt_vid_cap = vino_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vino_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vino_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vino_try_fmt_vid_cap, + .vidioc_querycap = vino_querycap, + .vidioc_enum_input = vino_enum_input, + .vidioc_g_input = vino_g_input, + .vidioc_s_input = vino_s_input, + .vidioc_g_std = vino_g_std, + .vidioc_s_std = vino_s_std, + .vidioc_querystd = vino_querystd, + .vidioc_cropcap = vino_cropcap, + .vidioc_s_crop = vino_s_crop, + .vidioc_g_crop = vino_g_crop, + .vidioc_s_parm = vino_s_parm, + .vidioc_g_parm = vino_g_parm, + .vidioc_reqbufs = vino_reqbufs, + .vidioc_querybuf = vino_querybuf, + .vidioc_qbuf = vino_qbuf, + .vidioc_dqbuf = vino_dqbuf, + .vidioc_streamon = vino_streamon, + .vidioc_streamoff = vino_streamoff, + .vidioc_queryctrl = vino_queryctrl, + .vidioc_g_ctrl = vino_g_ctrl, + .vidioc_s_ctrl = vino_s_ctrl, +}; + static const struct v4l2_file_operations vino_fops = { .owner = THIS_MODULE, .open = vino_open, .release = vino_close, - .ioctl = vino_ioctl, + .unlocked_ioctl = vino_ioctl, .mmap = vino_mmap, .poll = vino_poll, }; -static struct video_device v4l_device_template = { +static struct video_device vdev_template = { .name = "NOT SET", .fops = &vino_fops, + .ioctl_ops = &vino_ioctl_ops, + .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, .minor = -1, }; static void vino_module_cleanup(int stage) { switch(stage) { + case 11: + video_unregister_device(vino_drvdata->b.vdev); + vino_drvdata->b.vdev = NULL; case 10: - video_unregister_device(vino_drvdata->b.v4l_device); - vino_drvdata->b.v4l_device = NULL; + video_unregister_device(vino_drvdata->a.vdev); + vino_drvdata->a.vdev = NULL; case 9: - video_unregister_device(vino_drvdata->a.v4l_device); - vino_drvdata->a.v4l_device = NULL; + i2c_del_adapter(&vino_i2c_adapter); case 8: - vino_i2c_del_bus(); - case 7: free_irq(SGI_VINO_IRQ, NULL); + case 7: + if (vino_drvdata->b.vdev) { + video_device_release(vino_drvdata->b.vdev); + vino_drvdata->b.vdev = NULL; + } case 6: - if (vino_drvdata->b.v4l_device) { - video_device_release(vino_drvdata->b.v4l_device); - vino_drvdata->b.v4l_device = NULL; + if (vino_drvdata->a.vdev) { + video_device_release(vino_drvdata->a.vdev); + vino_drvdata->a.vdev = NULL; } case 5: - if (vino_drvdata->a.v4l_device) { - video_device_release(vino_drvdata->a.v4l_device); - vino_drvdata->a.v4l_device = NULL; - } - case 4: /* all entries in dma_cpu dummy table have the same address */ dma_unmap_single(NULL, vino_drvdata->dummy_desc_table.dma_cpu[0], @@ -4416,8 +4173,10 @@ static void vino_module_cleanup(int stage) (void *)vino_drvdata-> dummy_desc_table.dma_cpu, vino_drvdata->dummy_desc_table.dma); - case 3: + case 4: free_page(vino_drvdata->dummy_page); + case 3: + v4l2_device_unregister(&vino_drvdata->v4l2_dev); case 2: kfree(vino_drvdata); case 1: @@ -4472,6 +4231,7 @@ static int vino_probe(void) static int vino_init(void) { dma_addr_t dma_dummy_address; + int err; int i; vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL); @@ -4480,6 +4240,12 @@ static int vino_init(void) return -ENOMEM; } vino_init_stage++; + strlcpy(vino_drvdata->v4l2_dev.name, "vino", + sizeof(vino_drvdata->v4l2_dev.name)); + err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev); + if (err) + return err; + vino_init_stage++; /* create a dummy dma descriptor */ vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA); @@ -4546,25 +4312,27 @@ static int vino_init_channel_settings(struct vino_channel_settings *vcs, spin_lock_init(&vcs->fb_queue.queue_lock); init_waitqueue_head(&vcs->fb_queue.frame_wait_queue); - vcs->v4l_device = video_device_alloc(); - if (!vcs->v4l_device) { + vcs->vdev = video_device_alloc(); + if (!vcs->vdev) { vino_module_cleanup(vino_init_stage); return -ENOMEM; } vino_init_stage++; - memcpy(vcs->v4l_device, &v4l_device_template, + memcpy(vcs->vdev, &vdev_template, sizeof(struct video_device)); - strcpy(vcs->v4l_device->name, name); - vcs->v4l_device->release = video_device_release; + strcpy(vcs->vdev->name, name); + vcs->vdev->release = video_device_release; + vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev; - video_set_drvdata(vcs->v4l_device, vcs); + video_set_drvdata(vcs->vdev, vcs); return 0; } static int __init vino_module_init(void) { + unsigned short addr[] = { 0, I2C_CLIENT_END }; int ret; printk(KERN_INFO "SGI VINO driver version %s\n", @@ -4584,12 +4352,12 @@ static int __init vino_module_init(void) spin_lock_init(&vino_drvdata->input_lock); ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A, - vino_v4l_device_name_a); + vino_vdev_name_a); if (ret) return ret; ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B, - vino_v4l_device_name_b); + vino_vdev_name_b); if (ret) return ret; @@ -4605,15 +4373,16 @@ static int __init vino_module_init(void) } vino_init_stage++; - ret = vino_i2c_add_bus(); + ret = i2c_add_adapter(&vino_i2c_adapter); if (ret) { printk(KERN_ERR "VINO I2C bus registration failed\n"); vino_module_cleanup(vino_init_stage); return ret; } + i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev); vino_init_stage++; - ret = video_register_device(vino_drvdata->a.v4l_device, + ret = video_register_device(vino_drvdata->a.vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { printk(KERN_ERR "VINO channel A Video4Linux-device " @@ -4623,7 +4392,7 @@ static int __init vino_module_init(void) } vino_init_stage++; - ret = video_register_device(vino_drvdata->b.v4l_device, + ret = video_register_device(vino_drvdata->b.vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { printk(KERN_ERR "VINO channel B Video4Linux-device " @@ -4633,10 +4402,12 @@ static int __init vino_module_init(void) } vino_init_stage++; -#ifdef MODULE - request_module("saa7191"); - request_module("indycam"); -#endif + addr[0] = 0x45; + vino_drvdata->decoder = v4l2_i2c_new_probed_subdev(&vino_i2c_adapter, + "saa7191", "saa7191", addr); + addr[0] = 0x2b; + vino_drvdata->camera = v4l2_i2c_new_probed_subdev(&vino_i2c_adapter, + "indycam", "indycam", addr); dprintk("init complete!\n"); diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 76f7dfc32..e3bc2b0a3 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -29,10 +29,6 @@ #include "compat.h" #include <linux/videodev2.h> #include <linux/dma-mapping.h> -#ifdef CONFIG_VIDEO_V4L1_COMPAT -/* Include V4L1 specific functions. Should be removed soon */ -#include <linux/videodev.h> -#endif #include <linux/interrupt.h> #include <linux/kthread.h> #include <linux/highmem.h> @@ -1413,7 +1409,7 @@ free_dev: */ static int __init vivi_init(void) { - int ret, i; + int ret = 0, i; if (n_devs <= 0) n_devs = 1; diff --git a/linux/drivers/media/video/vpx3220.c b/linux/drivers/media/video/vpx3220.c index 0c1c97f2f..ee6c74176 100644 --- a/linux/drivers/media/video/vpx3220.c +++ b/linux/drivers/media/video/vpx3220.c @@ -374,7 +374,7 @@ static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) choosen video norm */ temp_input = vpx3220_fp_read(sd, 0xf2); - v4l2_dbg(1, debug, sd, "VIDIOC_S_STD %llx\n", std); + v4l2_dbg(1, debug, sd, "VIDIOC_S_STD %llx\n", (unsigned long long)std); if (std & V4L2_STD_NTSC) { vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); diff --git a/linux/drivers/media/video/w9966.c b/linux/drivers/media/video/w9966.c index b8ede9e3f..75d8debe7 100644 --- a/linux/drivers/media/video/w9966.c +++ b/linux/drivers/media/video/w9966.c @@ -58,7 +58,7 @@ #include <linux/init.h> #include <linux/delay.h> #include "compat.h" -#include <linux/videodev2.h> +#include <linux/videodev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <linux/parport.h> diff --git a/linux/drivers/media/video/w9968cf.c b/linux/drivers/media/video/w9968cf.c index 0b5109ed9..0478ee33e 100644 --- a/linux/drivers/media/video/w9968cf.c +++ b/linux/drivers/media/video/w9968cf.c @@ -42,6 +42,7 @@ #include <asm/page.h> #include <asm/uaccess.h> #include <linux/page-flags.h> +#include <linux/videodev.h> #include <media/v4l2-ioctl.h> #include "w9968cf.h" @@ -68,7 +69,6 @@ MODULE_VERSION(W9968CF_MODULE_VERSION); MODULE_LICENSE(W9968CF_MODULE_LICENSE); MODULE_SUPPORTED_DEVICE("Video"); -static int ovmod_load = W9968CF_OVMOD_LOAD; static unsigned short simcams = W9968CF_SIMCAMS; static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/ static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] = @@ -111,9 +111,6 @@ static int specific_debug = W9968CF_SPECIFIC_DEBUG; static unsigned int param_nv[24]; /* number of values per parameter */ -#ifdef CONFIG_MODULES -module_param(ovmod_load, bool, 0644); -#endif module_param(simcams, ushort, 0644); module_param_array(video_nr, short, ¶m_nv[0], 0444); module_param_array(packet_size, uint, ¶m_nv[1], 0444); @@ -144,18 +141,6 @@ module_param(debug, ushort, 0644); module_param(specific_debug, bool, 0644); #endif -#ifdef CONFIG_MODULES -MODULE_PARM_DESC(ovmod_load, - "\n<0|1> Automatic 'ovcamchip' module loading." - "\n0 disabled, 1 enabled." - "\nIf enabled,'insmod' searches for the required 'ovcamchip'" - "\nmodule in the system, according to its configuration, and" - "\nattempts to load that module automatically. This action is" - "\nperformed once as soon as the 'w9968cf' module is loaded" - "\ninto memory." - "\nDefault value is "__MODULE_STRING(W9968CF_OVMOD_LOAD)"." - "\n"); -#endif MODULE_PARM_DESC(simcams, "\n<n> Number of cameras allowed to stream simultaneously." "\nn may vary from 0 to " @@ -447,8 +432,6 @@ static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data*); static u32 w9968cf_i2c_func(struct i2c_adapter*); -static int w9968cf_i2c_attach_inform(struct i2c_client*); -static int w9968cf_i2c_detach_inform(struct i2c_client*); /* Memory management */ static void* rvmalloc(unsigned long size); @@ -1451,19 +1434,11 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { - struct w9968cf_device* cam = i2c_get_adapdata(adapter); + struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); + struct w9968cf_device *cam = to_cam(v4l2_dev); u8 i; int err = 0; - switch (addr) { - case OV6xx0_SID: - case OV7xx0_SID: - break; - default: - DBG(4, "Rejected slave ID 0x%04X", addr) - return -EINVAL; - } - if (size == I2C_SMBUS_BYTE) { /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */ addr <<= 1; @@ -1471,8 +1446,17 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, if (read_write == I2C_SMBUS_WRITE) err = w9968cf_i2c_adap_write_byte(cam, addr, command); else if (read_write == I2C_SMBUS_READ) - err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte); - + for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { + err = w9968cf_i2c_adap_read_byte(cam, addr, + &data->byte); + if (err) { + if (w9968cf_smbus_refresh_bus(cam)) { + err = -EIO; + break; + } + } else + break; + } } else if (size == I2C_SMBUS_BYTE_DATA) { addr <<= 1; @@ -1499,7 +1483,6 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, DBG(4, "Unsupported I2C transfer mode (%d)", size) return -EINVAL; } - return err; } @@ -1512,44 +1495,6 @@ static u32 w9968cf_i2c_func(struct i2c_adapter* adap) } -static int w9968cf_i2c_attach_inform(struct i2c_client* client) -{ - struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); - int id = client->driver->id, err = 0; - - if (id == I2C_DRIVERID_OVCAMCHIP) { - cam->sensor_client = client; - err = w9968cf_sensor_init(cam); - if (err) { - cam->sensor_client = NULL; - return err; - } - } else { - DBG(4, "Rejected client [%s] with driver [%s]", - client->name, client->driver->driver.name) - return -EINVAL; - } - - DBG(5, "I2C attach client [%s] with driver [%s]", - client->name, client->driver->driver.name) - - return 0; -} - - -static int w9968cf_i2c_detach_inform(struct i2c_client* client) -{ - struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); - - if (cam->sensor_client == client) - cam->sensor_client = NULL; - - DBG(5, "I2C detach client [%s]", client->name) - - return 0; -} - - static int w9968cf_i2c_init(struct w9968cf_device* cam) { int err = 0; @@ -1565,15 +1510,16 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) static struct i2c_adapter adap = { .id = I2C_HW_SMBUS_W9968CF, .owner = THIS_MODULE, - .client_register = w9968cf_i2c_attach_inform, - .client_unregister = w9968cf_i2c_detach_inform, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + .class = I2C_CLASS_TV_ANALOG, +#endif .algo = &algo, }; memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter)); strcpy(cam->i2c_adapter.name, "w9968cf"); cam->i2c_adapter.dev.parent = &cam->usbdev->dev; - i2c_set_adapdata(&cam->i2c_adapter, cam); + i2c_set_adapdata(&cam->i2c_adapter, &cam->v4l2_dev); DBG(6, "Registering I2C adapter with kernel...") @@ -2176,13 +2122,9 @@ w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val) static int w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg) { - struct i2c_client* c = cam->sensor_client; - int rc = 0; + int rc; - if (!c || !c->driver || !c->driver->command) - return -EINVAL; - - rc = c->driver->command(c, cmd, arg); + rc = v4l2_subdev_call(cam->sensor_sd, core, ioctl, cmd, arg); /* The I2C driver returns -EPERM on non-supported controls */ return (rc < 0 && rc != -EPERM) ? rc : 0; } @@ -2357,7 +2299,7 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam) goto error; /* NOTE: Make sure width and height are a multiple of 16 */ - switch (cam->sensor_client->addr) { + switch (v4l2_i2c_subdev_addr(cam->sensor_sd)) { case OV6xx0_SID: cam->maxwidth = 352; cam->maxheight = 288; @@ -2662,6 +2604,7 @@ static void w9968cf_release_resources(struct w9968cf_device* cam) w9968cf_deallocate_memory(cam); kfree(cam->control_buffer); kfree(cam->data_buffer); + v4l2_device_unregister(&cam->v4l2_dev); mutex_unlock(&w9968cf_devlist_mutex); } @@ -3491,6 +3434,11 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) struct list_head* ptr; u8 sc = 0; /* number of simultaneous cameras */ static unsigned short dev_nr; /* 0 - we are handling device number n */ + static unsigned short addrs[] = { + OV7xx0_SID, + OV6xx0_SID, + I2C_CLIENT_END + }; if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[0].idVendor && le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[0].idProduct) @@ -3506,12 +3454,14 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) if (!cam) return -ENOMEM; + err = v4l2_device_register(&udev->dev, &cam->v4l2_dev); + if (err) + goto fail0; + mutex_init(&cam->dev_mutex); mutex_lock(&cam->dev_mutex); cam->usbdev = udev; - /* NOTE: a local copy is used to avoid possible race conditions */ - memcpy(&cam->dev, &udev->dev, sizeof(struct device)); DBG(2, "%s detected", symbolic(camlist, mod_id)) @@ -3560,7 +3510,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; video_set_drvdata(cam->v4ldev, cam); - cam->v4ldev->parent = &cam->dev; + cam->v4ldev->v4l2_dev = &cam->v4l2_dev; err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); @@ -3587,9 +3537,13 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) w9968cf_turn_on_led(cam); w9968cf_i2c_init(cam); + cam->sensor_sd = v4l2_i2c_new_probed_subdev(&cam->i2c_adapter, + "ovcamchip", "ovcamchip", addrs); usb_set_intfdata(intf, cam); mutex_unlock(&cam->dev_mutex); + + err = w9968cf_sensor_init(cam); return 0; fail: /* Free unused memory */ @@ -3598,6 +3552,8 @@ fail: /* Free unused memory */ if (cam->v4ldev) video_device_release(cam->v4ldev); mutex_unlock(&cam->dev_mutex); + v4l2_device_unregister(&cam->v4l2_dev); +fail0: kfree(cam); return err; } @@ -3608,9 +3564,8 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) struct w9968cf_device* cam = (struct w9968cf_device*)usb_get_intfdata(intf); - down_write(&w9968cf_disconnect); - if (cam) { + down_write(&w9968cf_disconnect); /* Prevent concurrent accesses to data */ mutex_lock(&cam->dev_mutex); @@ -3632,12 +3587,12 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) w9968cf_release_resources(cam); mutex_unlock(&cam->dev_mutex); + up_write(&w9968cf_disconnect); - if (!cam->users) + if (!cam->users) { kfree(cam); + } } - - up_write(&w9968cf_disconnect); } @@ -3661,9 +3616,6 @@ static int __init w9968cf_module_init(void) KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION) KDBG(3, W9968CF_MODULE_AUTHOR) - if (ovmod_load) - request_module("ovcamchip"); - if ((err = usb_register(&w9968cf_usb_driver))) return err; diff --git a/linux/drivers/media/video/w9968cf.h b/linux/drivers/media/video/w9968cf.h index 62c26b1ed..989d414d6 100644 --- a/linux/drivers/media/video/w9968cf.h +++ b/linux/drivers/media/video/w9968cf.h @@ -33,6 +33,7 @@ #include <linux/rwsem.h> #include <linux/mutex.h> +#include <media/v4l2-device.h> #include <media/ovcamchip.h> #include "compat.h" @@ -43,7 +44,6 @@ * Default values * ****************************************************************************/ -#define W9968CF_OVMOD_LOAD 1 /* automatic 'ovcamchip' module loading */ #define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */ /* Comment/uncomment the following line to enable/disable debugging messages */ @@ -196,10 +196,9 @@ enum w9968cf_vpp_flag { /* Main device driver structure */ struct w9968cf_device { - struct device dev; /* device structure */ - enum w9968cf_model_id id; /* private device identifier */ + struct v4l2_device v4l2_dev; struct video_device* v4ldev; /* -> V4L structure */ struct list_head v4llist; /* entry of the list of V4L cameras */ @@ -266,7 +265,7 @@ struct w9968cf_device { /* I2C interface to kernel */ struct i2c_adapter i2c_adapter; - struct i2c_client* sensor_client; + struct v4l2_subdev *sensor_sd; /* Locks */ struct mutex dev_mutex, /* for probe, disconnect,open and close */ @@ -278,6 +277,11 @@ struct w9968cf_device { char command[16]; /* name of the program holding the device */ }; +static inline struct w9968cf_device *to_cam(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct w9968cf_device, v4l2_dev); +} + /**************************************************************************** * Macros for debugging * @@ -292,14 +296,14 @@ struct w9968cf_device { if ( ((specific_debug) && (debug == (level))) || \ ((!specific_debug) && (debug >= (level))) ) { \ if ((level) == 1) \ - dev_err(&cam->dev, fmt "\n", ## args); \ + v4l2_err(&cam->v4l2_dev, fmt "\n", ## args); \ else if ((level) == 2 || (level) == 3) \ - dev_info(&cam->dev, fmt "\n", ## args); \ + v4l2_info(&cam->v4l2_dev, fmt "\n", ## args); \ else if ((level) == 4) \ - dev_warn(&cam->dev, fmt "\n", ## args); \ + v4l2_warn(&cam->v4l2_dev, fmt "\n", ## args); \ else if ((level) >= 5) \ - dev_info(&cam->dev, "[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ + v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", \ + __func__, __LINE__ , ## args); \ } \ } /* For generic kernel (not device specific) messages */ @@ -322,7 +326,7 @@ struct w9968cf_device { #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->dev, "[%s:%d] " fmt "\n", __func__, __LINE__ , ## args); +v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", __func__, __LINE__ , ## args); #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */ diff --git a/linux/drivers/media/video/zoran/zoran.h b/linux/drivers/media/video/zoran/zoran.h index 8beada961..afecf32f1 100644 --- a/linux/drivers/media/video/zoran/zoran.h +++ b/linux/drivers/media/video/zoran/zoran.h @@ -172,6 +172,8 @@ Private IOCTL to set up for displaying MJPEG #endif #define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) +#define MAX_FRAME (BUZ_MAX_FRAME > VIDEO_MAX_FRAME ? BUZ_MAX_FRAME : VIDEO_MAX_FRAME) + #include "zr36057.h" enum card_type { @@ -280,21 +282,21 @@ struct zoran_mapping { int count; }; -struct zoran_jpg_buffer { - struct zoran_mapping *map; - __le32 *frag_tab; /* addresses of frag table */ - u32 frag_tab_bus; /* same value cached to save time in ISR */ - enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */ - struct zoran_sync bs; /* DONE: info to return to application */ -}; - -struct zoran_v4l_buffer { +struct zoran_buffer { struct zoran_mapping *map; - char *fbuffer; /* virtual address of frame buffer */ - unsigned long fbuffer_phys; /* physical address of frame buffer */ - unsigned long fbuffer_bus; /* bus address of frame buffer */ - enum zoran_buffer_state state; /* state: unused/pending/done */ - struct zoran_sync bs; /* DONE: info to return to application */ + enum zoran_buffer_state state; /* state: unused/pending/dma/done */ + struct zoran_sync bs; /* DONE: info to return to application */ + union { + struct { + __le32 *frag_tab; /* addresses of frag table */ + u32 frag_tab_bus; /* same value cached to save time in ISR */ + } jpg; + struct { + char *fbuffer; /* virtual address of frame buffer */ + unsigned long fbuffer_phys;/* physical address of frame buffer */ + unsigned long fbuffer_bus;/* bus address of frame buffer */ + } v4l; + }; }; enum zoran_lock_activity { @@ -304,19 +306,13 @@ enum zoran_lock_activity { }; /* buffer collections */ -struct zoran_jpg_struct { +struct zoran_buffer_col { enum zoran_lock_activity active; /* feature currently in use? */ - struct zoran_jpg_buffer buffer[BUZ_MAX_FRAME]; /* buffers */ - int num_buffers, buffer_size; + unsigned int num_buffers, buffer_size; + struct zoran_buffer buffer[MAX_FRAME]; /* buffers */ u8 allocated; /* Flag if buffers are allocated */ u8 need_contiguous; /* Flag if contiguous buffers are needed */ -}; - -struct zoran_v4l_struct { - enum zoran_lock_activity active; /* feature currently in use? */ - struct zoran_v4l_buffer buffer[VIDEO_MAX_FRAME]; /* buffers */ - int num_buffers, buffer_size; - u8 allocated; /* Flag if buffers are allocated */ + /* only applies to jpg buffers, raw buffers are always contiguous */ }; struct zoran; @@ -325,17 +321,16 @@ struct zoran; struct zoran_fh { struct zoran *zr; - enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */ + enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */ struct zoran_overlay_settings overlay_settings; - u32 *overlay_mask; /* overlay mask */ - enum zoran_lock_activity overlay_active; /* feature currently in use? */ + u32 *overlay_mask; /* overlay mask */ + enum zoran_lock_activity overlay_active;/* feature currently in use? */ - struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ - struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */ + struct zoran_buffer_col buffers; /* buffers' info */ + struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ - struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */ }; struct card_info { @@ -434,7 +429,7 @@ struct zoran { unsigned long v4l_pend_tail; unsigned long v4l_sync_tail; int v4l_pend[V4L_MAX_FRAME]; - struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */ + struct zoran_buffer_col v4l_buffers; /* V4L buffers' info */ /* Buz MJPEG parameters */ enum zoran_codec_mode codec_mode; /* status of codec */ @@ -461,7 +456,7 @@ struct zoran { int jpg_pend[BUZ_MAX_FRAME]; /* array indexed by frame number */ - struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */ + struct zoran_buffer_col jpg_buffers; /* MJPEG buffers' info */ /* Additional stuff for testing */ #ifdef CONFIG_PROC_FS diff --git a/linux/drivers/media/video/zoran/zoran_card.c b/linux/drivers/media/video/zoran/zoran_card.c index 4bd73d3ae..29007f9b5 100644 --- a/linux/drivers/media/video/zoran/zoran_card.c +++ b/linux/drivers/media/video/zoran/zoran_card.c @@ -255,7 +255,7 @@ zr36016_write (struct videocodec *codec, static void dc10_init (struct zoran *zr) { - dprintk(3, KERN_DEBUG "%s: dc10_init()\n", ZR_DEVNAME(zr)); + dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); /* Pixel clock selection */ GPIO(zr, 4, 0); @@ -267,13 +267,13 @@ dc10_init (struct zoran *zr) static void dc10plus_init (struct zoran *zr) { - dprintk(3, KERN_DEBUG "%s: dc10plus_init()\n", ZR_DEVNAME(zr)); + dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); } static void buz_init (struct zoran *zr) { - dprintk(3, KERN_DEBUG "%s: buz_init()\n", ZR_DEVNAME(zr)); + dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); /* some stuff from Iomega */ pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15); @@ -284,7 +284,7 @@ buz_init (struct zoran *zr) static void lml33_init (struct zoran *zr) { - dprintk(3, KERN_DEBUG "%s: lml33_init()\n", ZR_DEVNAME(zr)); + dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); GPIO(zr, 2, 1); // Set Composite input/output } @@ -759,13 +759,13 @@ zoran_check_jpg_settings (struct zoran *zr, dprintk(4, KERN_DEBUG - "%s: check_jpg_settings() - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", - ZR_DEVNAME(zr), settings->decimation, settings->HorDcm, + "%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", + ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm, settings->VerDcm, settings->TmpDcm); dprintk(4, KERN_DEBUG - "%s: check_jpg_settings() - x: %d, y: %d, w: %d, y: %d\n", - ZR_DEVNAME(zr), settings->img_x, settings->img_y, + "%s: %s - x: %d, y: %d, w: %d, y: %d\n", + ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y, settings->img_width, settings->img_height); /* Check decimation, set default values for decimation = 1, 2, 4 */ switch (settings->decimation) { @@ -797,8 +797,8 @@ zoran_check_jpg_settings (struct zoran *zr, if (zr->card.type == DC10_new) { dprintk(1, KERN_DEBUG - "%s: check_jpg_settings() - HDec by 4 is not supported on the DC10\n", - ZR_DEVNAME(zr)); + "%s: %s - HDec by 4 is not supported on the DC10\n", + ZR_DEVNAME(zr), __func__); err0++; break; } @@ -875,16 +875,16 @@ zoran_check_jpg_settings (struct zoran *zr, if (!try && err0) { dprintk(1, KERN_ERR - "%s: check_jpg_settings() - error in params for decimation = 0\n", - ZR_DEVNAME(zr)); + "%s: %s - error in params for decimation = 0\n", + ZR_DEVNAME(zr), __func__); err++; } break; default: dprintk(1, KERN_ERR - "%s: check_jpg_settings() - decimation = %d, must be 0, 1, 2 or 4\n", - ZR_DEVNAME(zr), settings->decimation); + "%s: %s - decimation = %d, must be 0, 1, 2 or 4\n", + ZR_DEVNAME(zr), __func__, settings->decimation); err++; break; } @@ -964,10 +964,8 @@ zoran_open_init_params (struct zoran *zr) JPEG_MARKER_DHT | JPEG_MARKER_DQT; i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); if (i) - dprintk(1, - KERN_ERR - "%s: zoran_open_init_params() internal error\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s internal error\n", + ZR_DEVNAME(zr), __func__); clear_interrupt_counters(zr); zr->testing = 0; @@ -1006,8 +1004,8 @@ zr36057_init (struct zoran *zr) dprintk(1, KERN_INFO - "%s: zr36057_init() - initializing card[%d], zr=%p\n", - ZR_DEVNAME(zr), zr->id, zr); + "%s: %s - initializing card[%d], zr=%p\n", + ZR_DEVNAME(zr), __func__, zr->id, zr); /* default setup of all parameters which will persist between opens */ zr->user = 0; @@ -1040,8 +1038,8 @@ zr36057_init (struct zoran *zr) if (zr->timing == NULL) { dprintk(1, KERN_WARNING - "%s: zr36057_init() - default TV standard not supported by hardware. PAL will be used.\n", - ZR_DEVNAME(zr)); + "%s: %s - default TV standard not supported by hardware. PAL will be used.\n", + ZR_DEVNAME(zr), __func__); zr->norm = V4L2_STD_PAL; zr->timing = zr->card.tvn[0]; } @@ -1065,8 +1063,8 @@ zr36057_init (struct zoran *zr) if (!zr->stat_com || !zr->video_dev) { dprintk(1, KERN_ERR - "%s: zr36057_init() - kmalloc (STAT_COM) failed\n", - ZR_DEVNAME(zr)); + "%s: %s - kmalloc (STAT_COM) failed\n", + ZR_DEVNAME(zr), __func__); err = -ENOMEM; goto exit_free; } @@ -1160,10 +1158,8 @@ zoran_setup_videocodec (struct zoran *zr, m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL); if (!m) { - dprintk(1, - KERN_ERR - "%s: zoran_setup_videocodec() - no memory\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - no memory\n", + ZR_DEVNAME(zr), __func__); return m; } @@ -1233,19 +1229,15 @@ static int __devinit zoran_probe(struct pci_dev *pdev, nr = zoran_num++; if (nr >= BUZ_MAX) { - dprintk(1, - KERN_ERR - "%s: driver limited to %d card(s) maximum\n", + dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n", ZORAN_NAME, BUZ_MAX); return -ENOENT; } zr = kzalloc(sizeof(struct zoran), GFP_KERNEL); if (!zr) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - kzalloc failed\n", - ZORAN_NAME); + dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n", + ZORAN_NAME, __func__); return -ENOMEM; } zr->v4l2_dev.notify = zoran_subdev_notify; @@ -1321,9 +1313,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0); if (!zr->zr36057_mem) { - dprintk(1, - KERN_ERR - "%s: %s() - ioremap failed\n", + dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n", ZR_DEVNAME(zr), __func__); goto zr_unreg; } @@ -1334,18 +1324,18 @@ static int __devinit zoran_probe(struct pci_dev *pdev, if (result == -EINVAL) { dprintk(1, KERN_ERR - "%s: find_zr36057() - bad irq number or handler\n", - ZR_DEVNAME(zr)); + "%s: %s - bad irq number or handler\n", + ZR_DEVNAME(zr), __func__); } else if (result == -EBUSY) { dprintk(1, KERN_ERR - "%s: find_zr36057() - IRQ %d busy, change your PnP config in BIOS\n", - ZR_DEVNAME(zr), zr->pci_dev->irq); + "%s: %s - IRQ %d busy, change your PnP config in BIOS\n", + ZR_DEVNAME(zr), __func__, zr->pci_dev->irq); } else { dprintk(1, KERN_ERR - "%s: find_zr36057() - can't assign irq, error code %d\n", - ZR_DEVNAME(zr), result); + "%s: %s - can't assign irq, error code %d\n", + ZR_DEVNAME(zr), __func__, result); } goto zr_unmap; } @@ -1355,9 +1345,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, &latency); need_latency = zr->revision > 1 ? 32 : 48; if (latency != need_latency) { - dprintk(2, - KERN_INFO - "%s: Changing PCI latency from %d to %d\n", + dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n", ZR_DEVNAME(zr), latency, need_latency); pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, need_latency); @@ -1369,10 +1357,8 @@ static int __devinit zoran_probe(struct pci_dev *pdev, ZR_DEVNAME(zr)); if (zoran_register_i2c(zr) < 0) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - can't initialize i2c bus\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n", + ZR_DEVNAME(zr), __func__); goto zr_free_irq; } @@ -1424,17 +1410,13 @@ static int __devinit zoran_probe(struct pci_dev *pdev, goto zr_unreg_i2c; zr->codec = videocodec_attach(master_codec); if (!zr->codec) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - no codec found\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - no codec found\n", + ZR_DEVNAME(zr), __func__); goto zr_free_codec; } if (zr->codec->type != zr->card.video_codec) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - wrong codec\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - wrong codec\n", + ZR_DEVNAME(zr), __func__); goto zr_detach_codec; } } @@ -1444,17 +1426,13 @@ static int __devinit zoran_probe(struct pci_dev *pdev, goto zr_detach_codec; zr->vfe = videocodec_attach(master_vfe); if (!zr->vfe) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - no VFE found\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - no VFE found\n", + ZR_DEVNAME(zr), __func__); goto zr_free_vfe; } if (zr->vfe->type != zr->card.video_vfe) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() = wrong VFE\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s = wrong VFE\n", + ZR_DEVNAME(zr), __func__); goto zr_detach_vfe; } } @@ -1462,8 +1440,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, /* take care of Natoma chipset and a revision 1 zr36057 */ if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) { zr->jpg_buffers.need_contiguous = 1; - dprintk(1, - KERN_INFO + dprintk(1, KERN_INFO "%s: ZR36057/Natoma bug, max. buffer size is 128K\n", ZR_DEVNAME(zr)); } diff --git a/linux/drivers/media/video/zoran/zoran_device.c b/linux/drivers/media/video/zoran/zoran_device.c index 409e0d5e1..54a04ea5c 100644 --- a/linux/drivers/media/video/zoran/zoran_device.c +++ b/linux/drivers/media/video/zoran/zoran_device.c @@ -536,12 +536,8 @@ zr36057_overlay (struct zoran *zr, * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. */ -void -write_overlay_mask (struct file *file, - struct v4l2_clip *vp, - int count) +void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; u32 *mask; @@ -1126,7 +1122,7 @@ zoran_feed_stat_com (struct zoran *zr) if (!(zr->stat_com[i] & cpu_to_le32(1))) break; zr->stat_com[i] = - cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus); + cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); } else { /* fill 2 stat_com entries */ i = ((zr->jpg_dma_head - @@ -1134,9 +1130,9 @@ zoran_feed_stat_com (struct zoran *zr) if (!(zr->stat_com[i] & cpu_to_le32(1))) break; zr->stat_com[i] = - cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus); + cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); zr->stat_com[i + 1] = - cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus); + cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); } zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA; zr->jpg_dma_head++; @@ -1156,7 +1152,7 @@ zoran_reap_stat_com (struct zoran *zr) u32 stat_com; unsigned int seq; unsigned int dif; - struct zoran_jpg_buffer *buffer; + struct zoran_buffer *buffer; int frame; /* In motion decompress we don't have a hardware frame counter, @@ -1299,7 +1295,7 @@ error_handler (struct zoran *zr, printk(KERN_INFO "stat_com frames:"); for (j = 0; j < BUZ_NUM_STAT_COM; j++) { for (i = 0; i < zr->jpg_buffers.num_buffers; i++) { - if (le32_to_cpu(zr->stat_com[j]) == zr->jpg_buffers.buffer[i].frag_tab_bus) + if (le32_to_cpu(zr->stat_com[j]) == zr->jpg_buffers.buffer[i].jpg.frag_tab_bus) printk(KERN_CONT "% d->%d", j, i); } } @@ -1443,7 +1439,7 @@ zoran_irq (int irq, /* Buffer address */ - reg = zr->v4l_buffers.buffer[frame].fbuffer_bus; + reg = zr->v4l_buffers.buffer[frame].v4l.fbuffer_bus; btwrite(reg, ZR36057_VDTR); if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) reg += zr->v4l_settings.bytesperline; diff --git a/linux/drivers/media/video/zoran/zoran_device.h b/linux/drivers/media/video/zoran/zoran_device.h index 72d691567..bf8c94d03 100644 --- a/linux/drivers/media/video/zoran/zoran_device.h +++ b/linux/drivers/media/video/zoran/zoran_device.h @@ -54,7 +54,7 @@ extern int jpeg_codec_reset(struct zoran *zr); /* zr360x7 access to raw capture */ extern void zr36057_overlay(struct zoran *zr, int on); -extern void write_overlay_mask(struct file *file, +extern void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count); extern void zr36057_set_memgrab(struct zoran *zr, diff --git a/linux/drivers/media/video/zoran/zoran_driver.c b/linux/drivers/media/video/zoran/zoran_driver.c index e7b0aba57..7ef3c015d 100644 --- a/linux/drivers/media/video/zoran/zoran_driver.c +++ b/linux/drivers/media/video/zoran/zoran_driver.c @@ -59,7 +59,7 @@ #include <linux/spinlock.h> -#include <linux/videodev2.h> +#include <linux/videodev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include "videocodec.h" @@ -163,10 +163,6 @@ const struct zoran_format zoran_formats[] = { }; #define NUM_FORMATS ARRAY_SIZE(zoran_formats) -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)"); - /* small helper function for calculating buffersizes for v4l2 * we calculate the nearest higher power-of-two, which * will be the recommended buffersize */ @@ -191,8 +187,26 @@ zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings) } /* forward references */ -static void v4l_fbuffer_free(struct file *file); -static void jpg_fbuffer_free(struct file *file); +static void v4l_fbuffer_free(struct zoran_fh *fh); +static void jpg_fbuffer_free(struct zoran_fh *fh); + +/* Set mapping mode */ +static void map_mode_raw(struct zoran_fh *fh) +{ + fh->map_mode = ZORAN_MAP_MODE_RAW; + fh->buffers.buffer_size = v4l_bufsize; + fh->buffers.num_buffers = v4l_nbufs; +} +static void map_mode_jpg(struct zoran_fh *fh, int play) +{ + fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC; + fh->buffers.buffer_size = jpg_bufsize; + fh->buffers.num_buffers = jpg_nbufs; +} +static inline const char *mode_name(enum zoran_map_mode mode) +{ + return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG"; +} /* * Allocate the V4L grab buffers @@ -200,75 +214,70 @@ static void jpg_fbuffer_free(struct file *file); * These have to be pysically contiguous. */ -static int -v4l_fbuffer_alloc (struct file *file) +static int v4l_fbuffer_alloc(struct zoran_fh *fh) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int i, off; unsigned char *mem; - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) { - if (fh->v4l_buffers.buffer[i].fbuffer) + for (i = 0; i < fh->buffers.num_buffers; i++) { + if (fh->buffers.buffer[i].v4l.fbuffer) dprintk(2, KERN_WARNING - "%s: v4l_fbuffer_alloc() - buffer %d already allocated!?\n", - ZR_DEVNAME(zr), i); + "%s: %s - buffer %d already allocated!?\n", + ZR_DEVNAME(zr), __func__, i); //udelay(20); - mem = kmalloc(fh->v4l_buffers.buffer_size, GFP_KERNEL); + mem = kmalloc(fh->buffers.buffer_size, + GFP_KERNEL | __GFP_NOWARN); if (!mem) { dprintk(1, KERN_ERR - "%s: v4l_fbuffer_alloc() - kmalloc for V4L buf %d failed\n", - ZR_DEVNAME(zr), i); - v4l_fbuffer_free(file); + "%s: %s - kmalloc for V4L buf %d failed\n", + ZR_DEVNAME(zr), __func__, i); + v4l_fbuffer_free(fh); return -ENOBUFS; } - fh->v4l_buffers.buffer[i].fbuffer = mem; - fh->v4l_buffers.buffer[i].fbuffer_phys = - virt_to_phys(mem); - fh->v4l_buffers.buffer[i].fbuffer_bus = - virt_to_bus(mem); - for (off = 0; off < fh->v4l_buffers.buffer_size; + fh->buffers.buffer[i].v4l.fbuffer = mem; + fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem); + fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem); + for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) SetPageReserved(virt_to_page(mem + off)); dprintk(4, KERN_INFO - "%s: v4l_fbuffer_alloc() - V4L frame %d mem 0x%lx (bus: 0x%lx)\n", - ZR_DEVNAME(zr), i, (unsigned long) mem, + "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%lx)\n", + ZR_DEVNAME(zr), __func__, i, (unsigned long) mem, virt_to_bus(mem)); } - fh->v4l_buffers.allocated = 1; + fh->buffers.allocated = 1; return 0; } /* free the V4L grab buffers */ -static void -v4l_fbuffer_free (struct file *file) +static void v4l_fbuffer_free(struct zoran_fh *fh) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int i, off; unsigned char *mem; - dprintk(4, KERN_INFO "%s: v4l_fbuffer_free()\n", ZR_DEVNAME(zr)); + dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__); - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) { - if (!fh->v4l_buffers.buffer[i].fbuffer) + for (i = 0; i < fh->buffers.num_buffers; i++) { + if (!fh->buffers.buffer[i].v4l.fbuffer) continue; - mem = fh->v4l_buffers.buffer[i].fbuffer; - for (off = 0; off < fh->v4l_buffers.buffer_size; + mem = fh->buffers.buffer[i].v4l.fbuffer; + for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) ClearPageReserved(virt_to_page(mem + off)); - kfree((void *) fh->v4l_buffers.buffer[i].fbuffer); - fh->v4l_buffers.buffer[i].fbuffer = NULL; + kfree(fh->buffers.buffer[i].v4l.fbuffer); + fh->buffers.buffer[i].v4l.fbuffer = NULL; } - fh->v4l_buffers.allocated = 0; + fh->buffers.allocated = 0; } /* @@ -299,140 +308,128 @@ v4l_fbuffer_free (struct file *file) * and fragment buffers are not little-endian. */ -static int -jpg_fbuffer_alloc (struct file *file) +static int jpg_fbuffer_alloc(struct zoran_fh *fh) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int i, j, off; - unsigned long mem; + u8 *mem; - for (i = 0; i < fh->jpg_buffers.num_buffers; i++) { - if (fh->jpg_buffers.buffer[i].frag_tab) + for (i = 0; i < fh->buffers.num_buffers; i++) { + if (fh->buffers.buffer[i].jpg.frag_tab) dprintk(2, KERN_WARNING - "%s: jpg_fbuffer_alloc() - buffer %d already allocated!?\n", - ZR_DEVNAME(zr), i); + "%s: %s - buffer %d already allocated!?\n", + ZR_DEVNAME(zr), __func__, i); /* Allocate fragment table for this buffer */ - mem = get_zeroed_page(GFP_KERNEL); + mem = (void *)get_zeroed_page(GFP_KERNEL); if (mem == 0) { dprintk(1, KERN_ERR - "%s: jpg_fbuffer_alloc() - get_zeroed_page (frag_tab) failed for buffer %d\n", - ZR_DEVNAME(zr), i); - jpg_fbuffer_free(file); + "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n", + ZR_DEVNAME(zr), __func__, i); + jpg_fbuffer_free(fh); return -ENOBUFS; } - fh->jpg_buffers.buffer[i].frag_tab = (__le32 *) mem; - fh->jpg_buffers.buffer[i].frag_tab_bus = - virt_to_bus((void *) mem); - - //if (alloc_contig) { - if (fh->jpg_buffers.need_contiguous) { - mem = - (unsigned long) kmalloc(fh->jpg_buffers. - buffer_size, - GFP_KERNEL); - if (mem == 0) { + fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem; + fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem); + + if (fh->buffers.need_contiguous) { + mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL); + if (mem == NULL) { dprintk(1, KERN_ERR - "%s: jpg_fbuffer_alloc() - kmalloc failed for buffer %d\n", - ZR_DEVNAME(zr), i); - jpg_fbuffer_free(file); + "%s: %s - kmalloc failed for buffer %d\n", + ZR_DEVNAME(zr), __func__, i); + jpg_fbuffer_free(fh); return -ENOBUFS; } - fh->jpg_buffers.buffer[i].frag_tab[0] = - cpu_to_le32(virt_to_bus((void *) mem)); - fh->jpg_buffers.buffer[i].frag_tab[1] = - cpu_to_le32(((fh->jpg_buffers.buffer_size / 4) << 1) | 1); - for (off = 0; off < fh->jpg_buffers.buffer_size; - off += PAGE_SIZE) + fh->buffers.buffer[i].jpg.frag_tab[0] = + cpu_to_le32(virt_to_bus(mem)); + fh->buffers.buffer[i].jpg.frag_tab[1] = + cpu_to_le32((fh->buffers.buffer_size >> 1) | 1); + for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) SetPageReserved(virt_to_page(mem + off)); } else { /* jpg_bufsize is already page aligned */ - for (j = 0; - j < fh->jpg_buffers.buffer_size / PAGE_SIZE; - j++) { - mem = get_zeroed_page(GFP_KERNEL); - if (mem == 0) { + for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) { + mem = (void *)get_zeroed_page(GFP_KERNEL); + if (mem == NULL) { dprintk(1, KERN_ERR - "%s: jpg_fbuffer_alloc() - get_zeroed_page failed for buffer %d\n", - ZR_DEVNAME(zr), i); - jpg_fbuffer_free(file); + "%s: %s - get_zeroed_page failed for buffer %d\n", + ZR_DEVNAME(zr), __func__, i); + jpg_fbuffer_free(fh); return -ENOBUFS; } - fh->jpg_buffers.buffer[i].frag_tab[2 * j] = - cpu_to_le32(virt_to_bus((void *) mem)); - fh->jpg_buffers.buffer[i].frag_tab[2 * j + - 1] = - cpu_to_le32((PAGE_SIZE / 4) << 1); + fh->buffers.buffer[i].jpg.frag_tab[2 * j] = + cpu_to_le32(virt_to_bus(mem)); + fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] = + cpu_to_le32((PAGE_SIZE >> 2) << 1); SetPageReserved(virt_to_page(mem)); } - fh->jpg_buffers.buffer[i].frag_tab[2 * j - 1] |= cpu_to_le32(1); + fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1); } } dprintk(4, - KERN_DEBUG "%s: jpg_fbuffer_alloc() - %d KB allocated\n", - ZR_DEVNAME(zr), - (fh->jpg_buffers.num_buffers * - fh->jpg_buffers.buffer_size) >> 10); + KERN_DEBUG "%s: %s - %d KB allocated\n", + ZR_DEVNAME(zr), __func__, + (fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10); - fh->jpg_buffers.allocated = 1; + fh->buffers.allocated = 1; return 0; } /* free the MJPEG grab buffers */ -static void -jpg_fbuffer_free (struct file *file) +static void jpg_fbuffer_free(struct zoran_fh *fh) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int i, j, off; unsigned char *mem; __le32 frag_tab; + struct zoran_buffer *buffer; - dprintk(4, KERN_DEBUG "%s: jpg_fbuffer_free()\n", ZR_DEVNAME(zr)); + dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); - for (i = 0; i < fh->jpg_buffers.num_buffers; i++) { - if (!fh->jpg_buffers.buffer[i].frag_tab) + for (i = 0, buffer = &fh->buffers.buffer[0]; + i < fh->buffers.num_buffers; i++, buffer++) { + if (!buffer->jpg.frag_tab) continue; - if (fh->jpg_buffers.need_contiguous) { - frag_tab = fh->jpg_buffers.buffer[i].frag_tab[0]; + if (fh->buffers.need_contiguous) { + frag_tab = buffer->jpg.frag_tab[0]; if (frag_tab) { - mem = (unsigned char *)bus_to_virt(le32_to_cpu(frag_tab)); - for (off = 0; off < fh->jpg_buffers.buffer_size; off += PAGE_SIZE) + mem = bus_to_virt(le32_to_cpu(frag_tab)); + for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) ClearPageReserved(virt_to_page(mem + off)); kfree(mem); - fh->jpg_buffers.buffer[i].frag_tab[0] = 0; - fh->jpg_buffers.buffer[i].frag_tab[1] = 0; + buffer->jpg.frag_tab[0] = 0; + buffer->jpg.frag_tab[1] = 0; } } else { - for (j = 0; j < fh->jpg_buffers.buffer_size / PAGE_SIZE; j++) { - frag_tab = fh->jpg_buffers.buffer[i].frag_tab[2 * j]; + for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) { + frag_tab = buffer->jpg.frag_tab[2 * j]; if (!frag_tab) break; ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab)))); free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab))); - fh->jpg_buffers.buffer[i].frag_tab[2 * j] = 0; - fh->jpg_buffers.buffer[i].frag_tab[2 * j + 1] = 0; + buffer->jpg.frag_tab[2 * j] = 0; + buffer->jpg.frag_tab[2 * j + 1] = 0; } } - free_page((unsigned long)fh->jpg_buffers.buffer[i].frag_tab); - fh->jpg_buffers.buffer[i].frag_tab = NULL; + free_page((unsigned long)buffer->jpg.frag_tab); + buffer->jpg.frag_tab = NULL; } - fh->jpg_buffers.allocated = 0; + fh->buffers.allocated = 0; } /* @@ -440,12 +437,11 @@ jpg_fbuffer_free (struct file *file) */ static int -zoran_v4l_set_format (struct file *file, +zoran_v4l_set_format (struct zoran_fh *fh, int width, int height, const struct zoran_format *format) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int bpp; @@ -455,19 +451,19 @@ zoran_v4l_set_format (struct file *file, height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) { dprintk(1, KERN_ERR - "%s: v4l_set_format() - wrong frame size (%dx%d)\n", - ZR_DEVNAME(zr), width, height); + "%s: %s - wrong frame size (%dx%d)\n", + ZR_DEVNAME(zr), __func__, width, height); return -EINVAL; } bpp = (format->depth + 7) / 8; /* Check against available buffer size */ - if (height * width * bpp > fh->v4l_buffers.buffer_size) { + if (height * width * bpp > fh->buffers.buffer_size) { dprintk(1, KERN_ERR - "%s: v4l_set_format() - video buffer size (%d kB) is too small\n", - ZR_DEVNAME(zr), fh->v4l_buffers.buffer_size >> 10); + "%s: %s - video buffer size (%d kB) is too small\n", + ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10); return -EINVAL; } @@ -476,8 +472,8 @@ zoran_v4l_set_format (struct file *file, if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) { dprintk(1, KERN_ERR - "%s: v4l_set_format() - wrong frame alignment\n", - ZR_DEVNAME(zr)); + "%s: %s - wrong frame alignment\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } @@ -489,43 +485,40 @@ zoran_v4l_set_format (struct file *file, return 0; } -static int -zoran_v4l_queue_frame (struct file *file, - int num) +static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned long flags; int res = 0; - if (!fh->v4l_buffers.allocated) { + if (!fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: v4l_queue_frame() - buffers not yet allocated\n", - ZR_DEVNAME(zr)); + "%s: %s - buffers not yet allocated\n", + ZR_DEVNAME(zr), __func__); res = -ENOMEM; } /* No grabbing outside the buffer range! */ - if (num >= fh->v4l_buffers.num_buffers || num < 0) { + if (num >= fh->buffers.num_buffers || num < 0) { dprintk(1, KERN_ERR - "%s: v4l_queue_frame() - buffer %d is out of range\n", - ZR_DEVNAME(zr), num); + "%s: %s - buffer %d is out of range\n", + ZR_DEVNAME(zr), __func__, num); res = -EINVAL; } spin_lock_irqsave(&zr->spinlock, flags); - if (fh->v4l_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { if (zr->v4l_buffers.active == ZORAN_FREE) { - zr->v4l_buffers = fh->v4l_buffers; - fh->v4l_buffers.active = ZORAN_ACTIVE; + zr->v4l_buffers = fh->buffers; + fh->buffers.active = ZORAN_ACTIVE; } else { dprintk(1, KERN_ERR - "%s: v4l_queue_frame() - another session is already capturing\n", - ZR_DEVNAME(zr)); + "%s: %s - another session is already capturing\n", + ZR_DEVNAME(zr), __func__); res = -EBUSY; } } @@ -536,7 +529,7 @@ zoran_v4l_queue_frame (struct file *file, default: case BUZ_STATE_PEND: if (zr->v4l_buffers.active == ZORAN_FREE) { - fh->v4l_buffers.active = ZORAN_FREE; + fh->buffers.active = ZORAN_FREE; zr->v4l_buffers.allocated = 0; } res = -EBUSY; /* what are you doing? */ @@ -544,19 +537,17 @@ zoran_v4l_queue_frame (struct file *file, case BUZ_STATE_DONE: dprintk(2, KERN_WARNING - "%s: v4l_queue_frame() - queueing buffer %d in state DONE!?\n", - ZR_DEVNAME(zr), num); + "%s: %s - queueing buffer %d in state DONE!?\n", + ZR_DEVNAME(zr), __func__, num); case BUZ_STATE_USER: /* since there is at least one unused buffer there's room for at least * one more pend[] entry */ - zr->v4l_pend[zr->v4l_pend_head++ & - V4L_MASK_FRAME] = num; + zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num; zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND; zr->v4l_buffers.buffer[num].bs.length = fh->v4l_settings.bytesperline * zr->v4l_settings.height; - fh->v4l_buffers.buffer[num] = - zr->v4l_buffers.buffer[num]; + fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num]; break; } } @@ -564,7 +555,7 @@ zoran_v4l_queue_frame (struct file *file, spin_unlock_irqrestore(&zr->spinlock, flags); if (!res && zr->v4l_buffers.active == ZORAN_FREE) - zr->v4l_buffers.active = fh->v4l_buffers.active; + zr->v4l_buffers.active = fh->buffers.active; return res; } @@ -573,27 +564,24 @@ zoran_v4l_queue_frame (struct file *file, * Sync on a V4L buffer */ -static int -v4l_sync (struct file *file, - int frame) +static int v4l_sync(struct zoran_fh *fh, int frame) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned long flags; - if (fh->v4l_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: v4l_sync() - no grab active for this session\n", - ZR_DEVNAME(zr)); + "%s: %s - no grab active for this session\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } /* check passed-in frame number */ - if (frame >= fh->v4l_buffers.num_buffers || frame < 0) { + if (frame >= fh->buffers.num_buffers || frame < 0) { dprintk(1, - KERN_ERR "%s: v4l_sync() - frame %d is invalid\n", - ZR_DEVNAME(zr), frame); + KERN_ERR "%s: %s - frame %d is invalid\n", + ZR_DEVNAME(zr), __func__, frame); return -EINVAL; } @@ -601,15 +589,14 @@ v4l_sync (struct file *file, if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) { dprintk(1, KERN_ERR - "%s: v4l_sync() - attempt to sync on a buffer which was not queued?\n", - ZR_DEVNAME(zr)); + "%s: %s - attempt to sync on a buffer which was not queued?\n", + ZR_DEVNAME(zr), __func__); return -EPROTO; } /* wait on this buffer to get ready */ if (!wait_event_interruptible_timeout(zr->v4l_capq, - (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), - 10*HZ)) + (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ)) return -ETIME; if (signal_pending(current)) return -ERESTARTSYS; @@ -617,11 +604,11 @@ v4l_sync (struct file *file, /* buffer should now be in BUZ_STATE_DONE */ if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE) dprintk(2, - KERN_ERR "%s: v4l_sync() - internal state error\n", - ZR_DEVNAME(zr)); + KERN_ERR "%s: %s - internal state error\n", + ZR_DEVNAME(zr), __func__); zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER; - fh->v4l_buffers.buffer[frame] = zr->v4l_buffers.buffer[frame]; + fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame]; spin_lock_irqsave(&zr->spinlock, flags); @@ -629,8 +616,7 @@ v4l_sync (struct file *file, if (zr->v4l_pend_tail == zr->v4l_pend_head) { zr36057_set_memgrab(zr, 0); if (zr->v4l_buffers.active == ZORAN_ACTIVE) { - fh->v4l_buffers.active = zr->v4l_buffers.active = - ZORAN_FREE; + fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE; zr->v4l_buffers.allocated = 0; } } @@ -644,31 +630,28 @@ v4l_sync (struct file *file, * Queue a MJPEG buffer for capture/playback */ -static int -zoran_jpg_queue_frame (struct file *file, - int num, - enum zoran_codec_mode mode) +static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num, + enum zoran_codec_mode mode) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned long flags; int res = 0; /* Check if buffers are allocated */ - if (!fh->jpg_buffers.allocated) { + if (!fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: jpg_queue_frame() - buffers not yet allocated\n", - ZR_DEVNAME(zr)); + "%s: %s - buffers not yet allocated\n", + ZR_DEVNAME(zr), __func__); return -ENOMEM; } /* No grabbing outside the buffer range! */ - if (num >= fh->jpg_buffers.num_buffers || num < 0) { + if (num >= fh->buffers.num_buffers || num < 0) { dprintk(1, KERN_ERR - "%s: jpg_queue_frame() - buffer %d out of range\n", - ZR_DEVNAME(zr), num); + "%s: %s - buffer %d out of range\n", + ZR_DEVNAME(zr), __func__, num); return -EINVAL; } @@ -679,20 +662,20 @@ zoran_jpg_queue_frame (struct file *file, /* wrong codec mode active - invalid */ dprintk(1, KERN_ERR - "%s: jpg_queue_frame() - codec in wrong mode\n", - ZR_DEVNAME(zr)); + "%s: %s - codec in wrong mode\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } - if (fh->jpg_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { if (zr->jpg_buffers.active == ZORAN_FREE) { - zr->jpg_buffers = fh->jpg_buffers; - fh->jpg_buffers.active = ZORAN_ACTIVE; + zr->jpg_buffers = fh->buffers; + fh->buffers.active = ZORAN_ACTIVE; } else { dprintk(1, KERN_ERR - "%s: jpg_queue_frame() - another session is already capturing\n", - ZR_DEVNAME(zr)); + "%s: %s - another session is already capturing\n", + ZR_DEVNAME(zr), __func__); res = -EBUSY; } } @@ -709,23 +692,21 @@ zoran_jpg_queue_frame (struct file *file, case BUZ_STATE_DONE: dprintk(2, KERN_WARNING - "%s: jpg_queue_frame() - queing frame in BUZ_STATE_DONE state!?\n", - ZR_DEVNAME(zr)); + "%s: %s - queing frame in BUZ_STATE_DONE state!?\n", + ZR_DEVNAME(zr), __func__); case BUZ_STATE_USER: /* since there is at least one unused buffer there's room for at *least one more pend[] entry */ - zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = - num; + zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num; zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND; - fh->jpg_buffers.buffer[num] = - zr->jpg_buffers.buffer[num]; + fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num]; zoran_feed_stat_com(zr); break; default: case BUZ_STATE_DMA: case BUZ_STATE_PEND: if (zr->jpg_buffers.active == ZORAN_FREE) { - fh->jpg_buffers.active = ZORAN_FREE; + fh->buffers.active = ZORAN_FREE; zr->jpg_buffers.allocated = 0; } res = -EBUSY; /* what are you doing? */ @@ -735,47 +716,41 @@ zoran_jpg_queue_frame (struct file *file, spin_unlock_irqrestore(&zr->spinlock, flags); - if (!res && zr->jpg_buffers.active == ZORAN_FREE) { - zr->jpg_buffers.active = fh->jpg_buffers.active; - } + if (!res && zr->jpg_buffers.active == ZORAN_FREE) + zr->jpg_buffers.active = fh->buffers.active; return res; } -static int -jpg_qbuf (struct file *file, - int frame, - enum zoran_codec_mode mode) +static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int res = 0; /* Does the user want to stop streaming? */ if (frame < 0) { if (zr->codec_mode == mode) { - if (fh->jpg_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: jpg_qbuf(-1) - session not active\n", - ZR_DEVNAME(zr)); + "%s: %s(-1) - session not active\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } - fh->jpg_buffers.active = zr->jpg_buffers.active = - ZORAN_FREE; + fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE; zr->jpg_buffers.allocated = 0; zr36057_enable_jpg(zr, BUZ_MODE_IDLE); return 0; } else { dprintk(1, KERN_ERR - "%s: jpg_qbuf() - stop streaming but not in streaming mode\n", - ZR_DEVNAME(zr)); + "%s: %s - stop streaming but not in streaming mode\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } } - if ((res = zoran_jpg_queue_frame(file, frame, mode))) + if ((res = zoran_jpg_queue_frame(fh, frame, mode))) return res; /* Start the jpeg codec when the first frame is queued */ @@ -789,28 +764,25 @@ jpg_qbuf (struct file *file, * Sync on a MJPEG buffer */ -static int -jpg_sync (struct file *file, - struct zoran_sync *bs) +static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned long flags; int frame; - if (fh->jpg_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: jpg_sync() - capture is not currently active\n", - ZR_DEVNAME(zr)); + "%s: %s - capture is not currently active\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { dprintk(1, KERN_ERR - "%s: jpg_sync() - codec not in streaming mode\n", - ZR_DEVNAME(zr)); + "%s: %s - codec not in streaming mode\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (!wait_event_interruptible_timeout(zr->jpg_capq, @@ -825,8 +797,8 @@ jpg_sync (struct file *file, sizeof(isr), &isr); dprintk(1, KERN_ERR - "%s: jpg_sync() - timeout: codec isr=0x%02x\n", - ZR_DEVNAME(zr), isr); + "%s: %s - timeout: codec isr=0x%02x\n", + ZR_DEVNAME(zr), __func__, isr); return -ETIME; @@ -844,28 +816,26 @@ jpg_sync (struct file *file, /* buffer should now be in BUZ_STATE_DONE */ if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE) dprintk(2, - KERN_ERR "%s: jpg_sync() - internal state error\n", - ZR_DEVNAME(zr)); + KERN_ERR "%s: %s - internal state error\n", + ZR_DEVNAME(zr), __func__); *bs = zr->jpg_buffers.buffer[frame].bs; bs->frame = frame; zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER; - fh->jpg_buffers.buffer[frame] = zr->jpg_buffers.buffer[frame]; + fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame]; spin_unlock_irqrestore(&zr->spinlock, flags); return 0; } -static void -zoran_open_init_session (struct file *file) +static void zoran_open_init_session(struct zoran_fh *fh) { int i; - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; /* Per default, map the V4L Buffers */ - fh->map_mode = ZORAN_MAP_MODE_RAW; + map_mode_raw(fh); /* take over the card's current settings */ fh->overlay_settings = zr->overlay_settings; @@ -875,38 +845,21 @@ zoran_open_init_session (struct file *file) /* v4l settings */ fh->v4l_settings = zr->v4l_settings; - - /* v4l_buffers */ - memset(&fh->v4l_buffers, 0, sizeof(struct zoran_v4l_struct)); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - fh->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ - fh->v4l_buffers.buffer[i].bs.frame = i; - } - fh->v4l_buffers.allocated = 0; - fh->v4l_buffers.active = ZORAN_FREE; - fh->v4l_buffers.buffer_size = v4l_bufsize; - fh->v4l_buffers.num_buffers = v4l_nbufs; - /* jpg settings */ fh->jpg_settings = zr->jpg_settings; - /* jpg_buffers */ - memset(&fh->jpg_buffers, 0, sizeof(struct zoran_jpg_struct)); - for (i = 0; i < BUZ_MAX_FRAME; i++) { - fh->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ - fh->jpg_buffers.buffer[i].bs.frame = i; + /* buffers */ + memset(&fh->buffers, 0, sizeof(fh->buffers)); + for (i = 0; i < MAX_FRAME; i++) { + fh->buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ + fh->buffers.buffer[i].bs.frame = i; } - fh->jpg_buffers.need_contiguous = zr->jpg_buffers.need_contiguous; - fh->jpg_buffers.allocated = 0; - fh->jpg_buffers.active = ZORAN_FREE; - fh->jpg_buffers.buffer_size = jpg_bufsize; - fh->jpg_buffers.num_buffers = jpg_nbufs; + fh->buffers.allocated = 0; + fh->buffers.active = ZORAN_FREE; } -static void -zoran_close_end_session (struct file *file) +static void zoran_close_end_session(struct zoran_fh *fh) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; /* overlay */ @@ -918,33 +871,33 @@ zoran_close_end_session (struct file *file) zr->overlay_mask = NULL; } - /* v4l capture */ - if (fh->v4l_buffers.active != ZORAN_FREE) { - unsigned long flags; + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { + /* v4l capture */ + if (fh->buffers.active != ZORAN_FREE) { + unsigned long flags; - spin_lock_irqsave(&zr->spinlock, flags); - zr36057_set_memgrab(zr, 0); - zr->v4l_buffers.allocated = 0; - zr->v4l_buffers.active = fh->v4l_buffers.active = - ZORAN_FREE; - spin_unlock_irqrestore(&zr->spinlock, flags); - } + spin_lock_irqsave(&zr->spinlock, flags); + zr36057_set_memgrab(zr, 0); + zr->v4l_buffers.allocated = 0; + zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; + spin_unlock_irqrestore(&zr->spinlock, flags); + } - /* v4l buffers */ - if (fh->v4l_buffers.allocated) - v4l_fbuffer_free(file); + /* v4l buffers */ + if (fh->buffers.allocated) + v4l_fbuffer_free(fh); + } else { + /* jpg capture */ + if (fh->buffers.active != ZORAN_FREE) { + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + zr->jpg_buffers.allocated = 0; + zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE; + } - /* jpg capture */ - if (fh->jpg_buffers.active != ZORAN_FREE) { - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - zr->jpg_buffers.allocated = 0; - zr->jpg_buffers.active = fh->jpg_buffers.active = - ZORAN_FREE; + /* jpg buffers */ + if (fh->buffers.allocated) + jpg_fbuffer_free(fh); } - - /* jpg buffers */ - if (fh->jpg_buffers.allocated) - jpg_fbuffer_free(file); } /* @@ -957,8 +910,8 @@ static int zoran_open(struct file *file) struct zoran_fh *fh; int res, first_open = 0; - dprintk(2, KERN_INFO "%s: zoran_open(%s, pid=[%d]), users(-)=%d\n", - ZR_DEVNAME(zr), current->comm, task_pid_nr(current), zr->user + 1); + dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n", + ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1); lock_kernel(); @@ -974,8 +927,8 @@ static int zoran_open(struct file *file) if (!fh) { dprintk(1, KERN_ERR - "%s: zoran_open() - allocation of zoran_fh failed\n", - ZR_DEVNAME(zr)); + "%s: %s - allocation of zoran_fh failed\n", + ZR_DEVNAME(zr), __func__); res = -ENOMEM; goto fail_unlock; } @@ -986,8 +939,8 @@ static int zoran_open(struct file *file) if (!fh->overlay_mask) { dprintk(1, KERN_ERR - "%s: zoran_open() - allocation of overlay_mask failed\n", - ZR_DEVNAME(zr)); + "%s: %s - allocation of overlay_mask failed\n", + ZR_DEVNAME(zr), __func__); res = -ENOMEM; goto fail_fh; } @@ -1009,7 +962,7 @@ static int zoran_open(struct file *file) /* set file_ops stuff */ file->private_data = fh; fh->zr = zr; - zoran_open_init_session(file); + zoran_open_init_session(fh); unlock_kernel(); return 0; @@ -1031,14 +984,14 @@ zoran_close(struct file *file) struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; - dprintk(2, KERN_INFO "%s: zoran_close(%s, pid=[%d]), users(+)=%d\n", - ZR_DEVNAME(zr), current->comm, task_pid_nr(current), zr->user - 1); + dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n", + ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1); /* kernel locks (fs/device.c), so don't do that ourselves * (prevents deadlocks) */ /*mutex_lock(&zr->resource_lock);*/ - zoran_close_end_session(file); + zoran_close_end_session(fh); if (zr->user-- == 1) { /* Last process */ /* Clean up JPEG process */ @@ -1077,7 +1030,7 @@ zoran_close(struct file *file) kfree(fh->overlay_mask); kfree(fh); - dprintk(4, KERN_INFO "%s: zoran_close() done\n", ZR_DEVNAME(zr)); + dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__); return 0; } @@ -1105,15 +1058,13 @@ zoran_write (struct file *file, return -EINVAL; } -static int -setup_fbuffer (struct file *file, +static int setup_fbuffer(struct zoran_fh *fh, void *base, const struct zoran_format *fmt, int width, int height, int bytesperline) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; /* (Ronald) v4l/v4l2 guidelines */ @@ -1141,8 +1092,8 @@ setup_fbuffer (struct file *file, * friendly and silently do as if nothing went wrong */ dprintk(3, KERN_ERR - "%s: setup_fbuffer() - forced overlay turnoff because framebuffer changed\n", - ZR_DEVNAME(zr)); + "%s: %s - forced overlay turnoff because framebuffer changed\n", + ZR_DEVNAME(zr), __func__); zr36057_overlay(zr, 0); } #endif @@ -1150,22 +1101,22 @@ setup_fbuffer (struct file *file, if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) { dprintk(1, KERN_ERR - "%s: setup_fbuffer() - no valid overlay format given\n", - ZR_DEVNAME(zr)); + "%s: %s - no valid overlay format given\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (height <= 0 || width <= 0 || bytesperline <= 0) { dprintk(1, KERN_ERR - "%s: setup_fbuffer() - invalid height/width/bpl value (%d|%d|%d)\n", - ZR_DEVNAME(zr), width, height, bytesperline); + "%s: %s - invalid height/width/bpl value (%d|%d|%d)\n", + ZR_DEVNAME(zr), __func__, width, height, bytesperline); return -EINVAL; } if (bytesperline & 3) { dprintk(1, KERN_ERR - "%s: setup_fbuffer() - bytesperline (%d) must be 4-byte aligned\n", - ZR_DEVNAME(zr), bytesperline); + "%s: %s - bytesperline (%d) must be 4-byte aligned\n", + ZR_DEVNAME(zr), __func__, bytesperline); return -EINVAL; } @@ -1183,17 +1134,9 @@ setup_fbuffer (struct file *file, } -static int -setup_window (struct file *file, - int x, - int y, - int width, - int height, - struct v4l2_clip __user *clips, - int clipcount, - void __user *bitmap) +static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height, + struct v4l2_clip __user *clips, int clipcount, void __user *bitmap) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; struct v4l2_clip *vcp = NULL; int on, end; @@ -1202,16 +1145,16 @@ setup_window (struct file *file, if (!zr->vbuf_base) { dprintk(1, KERN_ERR - "%s: setup_window() - frame buffer has to be set first\n", - ZR_DEVNAME(zr)); + "%s: %s - frame buffer has to be set first\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (!fh->overlay_settings.format) { dprintk(1, KERN_ERR - "%s: setup_window() - no overlay format set\n", - ZR_DEVNAME(zr)); + "%s: %s - no overlay format set\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } @@ -1241,8 +1184,8 @@ setup_window (struct file *file, width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) { dprintk(1, KERN_ERR - "%s: setup_window() - width = %d or height = %d invalid\n", - ZR_DEVNAME(zr), width, height); + "%s: %s - width = %d or height = %d invalid\n", + ZR_DEVNAME(zr), __func__, width, height); return -EINVAL; } @@ -1284,8 +1227,8 @@ setup_window (struct file *file, if (vcp == NULL) { dprintk(1, KERN_ERR - "%s: setup_window() - Alloc of clip mask failed\n", - ZR_DEVNAME(zr)); + "%s: %s - Alloc of clip mask failed\n", + ZR_DEVNAME(zr), __func__); return -ENOMEM; } if (copy_from_user @@ -1293,7 +1236,7 @@ setup_window (struct file *file, vfree(vcp); return -EFAULT; } - write_overlay_mask(file, vcp, clipcount); + write_overlay_mask(fh, vcp, clipcount); vfree(vcp); } @@ -1309,11 +1252,8 @@ setup_window (struct file *file, return wait_grab_pending(zr); } -static int -setup_overlay (struct file *file, - int on) +static int setup_overlay(struct zoran_fh *fh, int on) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; /* If there is nothing to do, return immediatly */ @@ -1326,16 +1266,16 @@ setup_overlay (struct file *file, fh->overlay_active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: setup_overlay() - overlay is already active for another session\n", - ZR_DEVNAME(zr)); + "%s: %s - overlay is already active for another session\n", + ZR_DEVNAME(zr), __func__); return -EBUSY; } if (!on && zr->overlay_active != ZORAN_FREE && fh->overlay_active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: setup_overlay() - you cannot cancel someone else's session\n", - ZR_DEVNAME(zr)); + "%s: %s - you cannot cancel someone else's session\n", + ZR_DEVNAME(zr), __func__); return -EPERM; } @@ -1351,15 +1291,15 @@ setup_overlay (struct file *file, if (!zr->vbuf_base || !fh->overlay_settings.is_set) { dprintk(1, KERN_ERR - "%s: setup_overlay() - buffer or window not set\n", - ZR_DEVNAME(zr)); + "%s: %s - buffer or window not set\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (!fh->overlay_settings.format) { dprintk(1, KERN_ERR - "%s: setup_overlay() - no overlay format set\n", - ZR_DEVNAME(zr)); + "%s: %s - no overlay format set\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } zr->overlay_active = fh->overlay_active = ZORAN_LOCKED; @@ -1376,41 +1316,54 @@ setup_overlay (struct file *file, return wait_grab_pending(zr); } - /* get the status of a buffer in the clients buffer queue */ -static int -zoran_v4l2_buffer_status (struct file *file, - struct v4l2_buffer *buf, - int num) +/* get the status of a buffer in the clients buffer queue */ +static int zoran_v4l2_buffer_status(struct zoran_fh *fh, + struct v4l2_buffer *buf, int num) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; + unsigned long flags; buf->flags = V4L2_BUF_FLAG_MAPPED; switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: - /* check range */ - if (num < 0 || num >= fh->v4l_buffers.num_buffers || - !fh->v4l_buffers.allocated) { + if (num < 0 || num >= fh->buffers.num_buffers || + !fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n", - ZR_DEVNAME(zr)); + "%s: %s - wrong number or buffers not allocated\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } + spin_lock_irqsave(&zr->spinlock, flags); + dprintk(3, + KERN_DEBUG + "%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n", + ZR_DEVNAME(zr), __func__, + "FAL"[fh->buffers.active], num, + "UPMD"[zr->v4l_buffers.buffer[num].state], + fh->buffers.buffer[num].map ? 'Y' : 'N'); + spin_unlock_irqrestore(&zr->spinlock, flags); +#if 0 + /* Process is the one capturing? */ + if (fh->v4l_buffers.active != ZORAN_FREE && + /* Buffer ready to DQBUF? */ + zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE) + res = POLLIN | POLLRDNORM; +#endif + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->length = fh->v4l_buffers.buffer_size; + buf->length = fh->buffers.buffer_size; /* get buffer */ - buf->bytesused = fh->v4l_buffers.buffer[num].bs.length; - if (fh->v4l_buffers.buffer[num].state == BUZ_STATE_DONE || - fh->v4l_buffers.buffer[num].state == BUZ_STATE_USER) { - buf->sequence = fh->v4l_buffers.buffer[num].bs.seq; + buf->bytesused = fh->buffers.buffer[num].bs.length; + if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || + fh->buffers.buffer[num].state == BUZ_STATE_USER) { + buf->sequence = fh->buffers.buffer[num].bs.seq; buf->flags |= V4L2_BUF_FLAG_DONE; - buf->timestamp = - fh->v4l_buffers.buffer[num].bs.timestamp; + buf->timestamp = fh->buffers.buffer[num].bs.timestamp; } else { buf->flags |= V4L2_BUF_FLAG_QUEUED; } @@ -1426,28 +1379,26 @@ zoran_v4l2_buffer_status (struct file *file, case ZORAN_MAP_MODE_JPG_PLAY: /* check range */ - if (num < 0 || num >= fh->jpg_buffers.num_buffers || - !fh->jpg_buffers.allocated) { + if (num < 0 || num >= fh->buffers.num_buffers || + !fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n", - ZR_DEVNAME(zr)); + "%s: %s - wrong number or buffers not allocated\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? V4L2_BUF_TYPE_VIDEO_CAPTURE : V4L2_BUF_TYPE_VIDEO_OUTPUT; - buf->length = fh->jpg_buffers.buffer_size; + buf->length = fh->buffers.buffer_size; /* these variables are only written after frame has been captured */ - if (fh->jpg_buffers.buffer[num].state == BUZ_STATE_DONE || - fh->jpg_buffers.buffer[num].state == BUZ_STATE_USER) { - buf->sequence = fh->jpg_buffers.buffer[num].bs.seq; - buf->timestamp = - fh->jpg_buffers.buffer[num].bs.timestamp; - buf->bytesused = - fh->jpg_buffers.buffer[num].bs.length; + if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || + fh->buffers.buffer[num].state == BUZ_STATE_USER) { + buf->sequence = fh->buffers.buffer[num].bs.seq; + buf->timestamp = fh->buffers.buffer[num].bs.timestamp; + buf->bytesused = fh->buffers.buffer[num].bs.length; buf->flags |= V4L2_BUF_FLAG_DONE; } else { buf->flags |= V4L2_BUF_FLAG_QUEUED; @@ -1455,14 +1406,11 @@ zoran_v4l2_buffer_status (struct file *file, /* which fields are these? */ if (fh->jpg_settings.TmpDcm != 1) - buf->field = - fh->jpg_settings. - odd_even ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; + buf->field = fh->jpg_settings.odd_even ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; else - buf->field = - fh->jpg_settings. - odd_even ? V4L2_FIELD_SEQ_TB : - V4L2_FIELD_SEQ_BT; + buf->field = fh->jpg_settings.odd_even ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; break; @@ -1470,8 +1418,8 @@ zoran_v4l2_buffer_status (struct file *file, dprintk(5, KERN_ERR - "%s: v4l2_buffer_status() - invalid buffer type|map_mode (%d|%d)\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); + "%s: %s - invalid buffer type|map_mode (%d|%d)\n", + ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode); return -EINVAL; } @@ -1492,31 +1440,15 @@ zoran_set_norm (struct zoran *zr, zr->jpg_buffers.active != ZORAN_FREE) { dprintk(1, KERN_WARNING - "%s: set_norm() called while in playback/capture mode\n", - ZR_DEVNAME(zr)); + "%s: %s called while in playback/capture mode\n", + ZR_DEVNAME(zr), __func__); return -EBUSY; } - if (lock_norm && norm != zr->norm) { - if (lock_norm > 1) { - dprintk(1, - KERN_WARNING - "%s: set_norm() - TV standard is locked, can not switch norm\n", - ZR_DEVNAME(zr)); - return -EPERM; - } else { - dprintk(1, - KERN_WARNING - "%s: set_norm() - TV standard is locked, norm was not changed\n", - ZR_DEVNAME(zr)); - norm = zr->norm; - } - } - if (!(norm & zr->card.norms)) { dprintk(1, - KERN_ERR "%s: set_norm() - unsupported norm %llx\n", - ZR_DEVNAME(zr), norm); + KERN_ERR "%s: %s - unsupported norm %llx\n", + ZR_DEVNAME(zr), __func__, norm); return -EINVAL; } @@ -1534,8 +1466,8 @@ zoran_set_norm (struct zoran *zr, if (status & V4L2_IN_ST_NO_SIGNAL) { dprintk(1, KERN_ERR - "%s: set_norm() - no norm detected\n", - ZR_DEVNAME(zr)); + "%s: %s - no norm detected\n", + ZR_DEVNAME(zr), __func__); /* reset norm */ decoder_call(zr, tuner, s_std, zr->norm); return -EIO; @@ -1582,16 +1514,16 @@ zoran_set_input (struct zoran *zr, zr->jpg_buffers.active != ZORAN_FREE) { dprintk(1, KERN_WARNING - "%s: set_input() called while in playback/capture mode\n", - ZR_DEVNAME(zr)); + "%s: %s called while in playback/capture mode\n", + ZR_DEVNAME(zr), __func__); return -EBUSY; } if (input < 0 || input >= zr->card.inputs) { dprintk(1, KERN_ERR - "%s: set_input() - unnsupported input %d\n", - ZR_DEVNAME(zr), input); + "%s: %s - unnsupported input %d\n", + ZR_DEVNAME(zr), __func__, input); return -EINVAL; } @@ -1745,7 +1677,7 @@ sparams_unlock_and_return: mutex_lock(&zr->resource_lock); - if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) { + if (fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: BUZIOC_REQBUFS - buffers already allocated\n", @@ -1754,17 +1686,17 @@ sparams_unlock_and_return: goto jpgreqbuf_unlock_and_return; } - fh->jpg_buffers.num_buffers = breq->count; - fh->jpg_buffers.buffer_size = breq->size; + /* The next mmap will map the MJPEG buffers - could + * also be *_PLAY, but it doesn't matter here */ + map_mode_jpg(fh, 0); + fh->buffers.num_buffers = breq->count; + fh->buffers.buffer_size = breq->size; - if (jpg_fbuffer_alloc(file)) { + if (jpg_fbuffer_alloc(fh)) { res = -ENOMEM; goto jpgreqbuf_unlock_and_return; } - /* The next mmap will map the MJPEG buffers - could - * also be *_PLAY, but it doesn't matter here */ - fh->map_mode = ZORAN_MAP_MODE_JPG_REC; jpgreqbuf_unlock_and_return: mutex_unlock(&zr->resource_lock); @@ -1779,7 +1711,7 @@ jpgreqbuf_unlock_and_return: ZR_DEVNAME(zr), *frame); mutex_lock(&zr->resource_lock); - res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_COMPRESS); + res = jpg_qbuf(fh, *frame, BUZ_MODE_MOTION_COMPRESS); mutex_unlock(&zr->resource_lock); return res; @@ -1793,7 +1725,7 @@ jpgreqbuf_unlock_and_return: ZR_DEVNAME(zr), *frame); mutex_lock(&zr->resource_lock); - res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_DECOMPRESS); + res = jpg_qbuf(fh, *frame, BUZ_MODE_MOTION_DECOMPRESS); mutex_unlock(&zr->resource_lock); return res; @@ -1807,7 +1739,15 @@ jpgreqbuf_unlock_and_return: dprintk(3, KERN_DEBUG "%s: BUZIOC_SYNC\n", ZR_DEVNAME(zr)); mutex_lock(&zr->resource_lock); - res = jpg_sync(file, bsync); + + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { + dprintk(2, KERN_WARNING + "%s: %s - not in jpg capture mode\n", + ZR_DEVNAME(zr), __func__); + res = -EINVAL; + } else { + res = jpg_sync(fh, bsync); + } mutex_unlock(&zr->resource_lock); return res; @@ -1886,18 +1826,10 @@ static int zoran_vidiocgmbuf(struct file *file, void *__fh, struct video_mbuf *v struct zoran *zr = fh->zr; int i, res = 0; - vmbuf->size = - fh->v4l_buffers.num_buffers * - fh->v4l_buffers.buffer_size; - vmbuf->frames = fh->v4l_buffers.num_buffers; - for (i = 0; i < vmbuf->frames; i++) { - vmbuf->offsets[i] = - i * fh->v4l_buffers.buffer_size; - } mutex_lock(&zr->resource_lock); - if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) { + if (fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: VIDIOCGMBUF - buffers already allocated\n", @@ -1906,13 +1838,19 @@ static int zoran_vidiocgmbuf(struct file *file, void *__fh, struct video_mbuf *v goto v4l1reqbuf_unlock_and_return; } - if (v4l_fbuffer_alloc(file)) { + /* The next mmap will map the V4L buffers */ + map_mode_raw(fh); + + if (v4l_fbuffer_alloc(fh)) { res = -ENOMEM; goto v4l1reqbuf_unlock_and_return; } - /* The next mmap will map the V4L buffers */ - fh->map_mode = ZORAN_MAP_MODE_RAW; + vmbuf->size = fh->buffers.num_buffers * fh->buffers.buffer_size; + vmbuf->frames = fh->buffers.num_buffers; + for (i = 0; i < vmbuf->frames; i++) + vmbuf->offsets[i] = i * fh->buffers.buffer_size; + v4l1reqbuf_unlock_and_return: mutex_unlock(&zr->resource_lock); @@ -2195,14 +2133,10 @@ static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh, fmt->fmt.win.clipcount, fmt->fmt.win.bitmap); mutex_lock(&zr->resource_lock); - res = setup_window(file, fmt->fmt.win.w.left, - fmt->fmt.win.w.top, - fmt->fmt.win.w.width, - fmt->fmt.win.w.height, - (struct v4l2_clip __user *) - fmt->fmt.win.clips, - fmt->fmt.win.clipcount, - fmt->fmt.win.bitmap); + res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top, + fmt->fmt.win.w.width, fmt->fmt.win.w.height, + (struct v4l2_clip __user *)fmt->fmt.win.clips, + fmt->fmt.win.clipcount, fmt->fmt.win.bitmap); mutex_unlock(&zr->resource_lock); return res; } @@ -2225,15 +2159,15 @@ static int zoran_s_fmt_vid_out(struct file *file, void *__fh, mutex_lock(&zr->resource_lock); - settings = fh->jpg_settings; - - if (fh->v4l_buffers.allocated || fh->jpg_buffers.allocated) { + if (fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", - ZR_DEVNAME(zr)); + ZR_DEVNAME(zr)); res = -EBUSY; goto sfmtjpg_unlock_and_return; } + settings = fh->jpg_settings; + /* we actually need to set 'real' parameters now */ if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT) settings.TmpDcm = 1; @@ -2271,6 +2205,9 @@ static int zoran_s_fmt_vid_out(struct file *file, void *__fh, /* it's ok, so set them */ fh->jpg_settings = settings; + map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT); + fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); + /* tell the user what we actually did */ fmt->fmt.pix.width = settings.img_width / settings.HorDcm; fmt->fmt.pix.height = settings.img_height * 2 / @@ -2281,15 +2218,10 @@ static int zoran_s_fmt_vid_out(struct file *file, void *__fh, else fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - 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.sizeimage = fh->buffers.buffer_size; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - /* we hereby abuse this variable to show that - * we're gonna do mjpeg capture */ - fh->map_mode = (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ? - ZORAN_MAP_MODE_JPG_REC : ZORAN_MAP_MODE_JPG_PLAY; sfmtjpg_unlock_and_return: mutex_unlock(&zr->resource_lock); return res; @@ -2314,9 +2246,11 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat); return -EINVAL; } + mutex_lock(&zr->resource_lock); - if (fh->jpg_buffers.allocated || - (fh->v4l_buffers.allocated && fh->v4l_buffers.active != ZORAN_FREE)) { + + if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) || + fh->buffers.active != ZORAN_FREE) { dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", ZR_DEVNAME(zr)); res = -EBUSY; @@ -2327,13 +2261,14 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) fmt->fmt.pix.width = BUZ_MAX_WIDTH; - res = zoran_v4l_set_format(file, fmt->fmt.pix.width, - fmt->fmt.pix.height, &zoran_formats[i]); + map_mode_raw(fh); + + res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height, + &zoran_formats[i]); if (res) goto sfmtv4l_unlock_and_return; - /* tell the user the - * results/missing stuff */ + /* 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; @@ -2342,7 +2277,6 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, else fmt->fmt.pix.field = V4L2_FIELD_TOP; - fh->map_mode = ZORAN_MAP_MODE_RAW; sfmtv4l_unlock_and_return: mutex_unlock(&zr->resource_lock); return res; @@ -2390,9 +2324,8 @@ static int zoran_s_fbuf(struct file *file, void *__fh, } mutex_lock(&zr->resource_lock); - res = setup_fbuffer(file, fb->base, &zoran_formats[i], - fb->fmt.width, fb->fmt.height, - fb->fmt.bytesperline); + res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width, + fb->fmt.height, fb->fmt.bytesperline); mutex_unlock(&zr->resource_lock); return res; @@ -2405,7 +2338,7 @@ static int zoran_overlay(struct file *file, void *__fh, unsigned int on) int res; mutex_lock(&zr->resource_lock); - res = setup_overlay(file, on); + res = setup_overlay(fh, on); mutex_unlock(&zr->resource_lock); return res; @@ -2431,7 +2364,7 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe return zoran_streamoff(file, fh, req->type); mutex_lock(&zr->resource_lock); - if (fh->v4l_buffers.allocated || fh->jpg_buffers.allocated) { + if (fh->buffers.allocated) { dprintk(2, KERN_ERR "%s: VIDIOC_REQBUFS - buffers already allocated\n", @@ -2441,46 +2374,38 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe } if (fh->map_mode == ZORAN_MAP_MODE_RAW && - req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - + req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { /* control user input */ if (req->count < 2) req->count = 2; if (req->count > v4l_nbufs) req->count = v4l_nbufs; - fh->v4l_buffers.num_buffers = req->count; - if (v4l_fbuffer_alloc(file)) { + /* The next mmap will map the V4L buffers */ + map_mode_raw(fh); + fh->buffers.num_buffers = req->count; + + if (v4l_fbuffer_alloc(fh)) { res = -ENOMEM; goto v4l2reqbuf_unlock_and_return; } - - /* The next mmap will map the V4L buffers */ - fh->map_mode = ZORAN_MAP_MODE_RAW; - } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC || - fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { - + fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { /* we need to calculate size ourselves now */ if (req->count < 4) req->count = 4; if (req->count > jpg_nbufs) req->count = jpg_nbufs; - fh->jpg_buffers.num_buffers = req->count; - fh->jpg_buffers.buffer_size = - zoran_v4l2_calc_bufsize(&fh->jpg_settings); - if (jpg_fbuffer_alloc(file)) { + /* The next mmap will map the MJPEG buffers */ + map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT); + fh->buffers.num_buffers = req->count; + fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); + + if (jpg_fbuffer_alloc(fh)) { res = -ENOMEM; goto v4l2reqbuf_unlock_and_return; } - - /* The next mmap will map the MJPEG buffers */ - if (req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - fh->map_mode = ZORAN_MAP_MODE_JPG_REC; - else - fh->map_mode = ZORAN_MAP_MODE_JPG_PLAY; - } else { dprintk(1, KERN_ERR @@ -2502,7 +2427,7 @@ static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf int res; mutex_lock(&zr->resource_lock); - res = zoran_v4l2_buffer_status(file, buf, buf->index); + res = zoran_v4l2_buffer_status(fh, buf, buf->index); mutex_unlock(&zr->resource_lock); return res; @@ -2526,11 +2451,10 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) goto qbuf_unlock_and_return; } - res = zoran_v4l_queue_frame(file, buf->index); + res = zoran_v4l_queue_frame(fh, buf->index); if (res) goto qbuf_unlock_and_return; - if (!zr->v4l_memgrab_active && - fh->v4l_buffers.active == ZORAN_LOCKED) + if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED) zr36057_set_memgrab(zr, 1); break; @@ -2552,14 +2476,13 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) goto qbuf_unlock_and_return; } - res = zoran_jpg_queue_frame(file, buf->index, - codec_mode); + res = zoran_jpg_queue_frame(fh, buf->index, codec_mode); if (res != 0) goto qbuf_unlock_and_return; if (zr->codec_mode == BUZ_MODE_IDLE && - fh->jpg_buffers.active == ZORAN_LOCKED) { + fh->buffers.active == ZORAN_LOCKED) zr36057_enable_jpg(zr, codec_mode); - } + break; default: @@ -2599,11 +2522,11 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) res = -EAGAIN; goto dqbuf_unlock_and_return; } - res = v4l_sync(file, num); + res = v4l_sync(fh, num); if (res) goto dqbuf_unlock_and_return; zr->v4l_sync_tail++; - res = zoran_v4l2_buffer_status(file, buf, num); + res = zoran_v4l2_buffer_status(fh, buf, num); break; case ZORAN_MAP_MODE_JPG_REC: @@ -2631,10 +2554,10 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) res = -EAGAIN; goto dqbuf_unlock_and_return; } - res = jpg_sync(file, &bs); + res = jpg_sync(fh, &bs); if (res) goto dqbuf_unlock_and_return; - res = zoran_v4l2_buffer_status(file, buf, bs.frame); + res = zoran_v4l2_buffer_status(fh, buf, bs.frame); break; } @@ -2662,12 +2585,12 @@ static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: /* raw capture */ if (zr->v4l_buffers.active != ZORAN_ACTIVE || - fh->v4l_buffers.active != ZORAN_ACTIVE) { + fh->buffers.active != ZORAN_ACTIVE) { res = -EBUSY; goto strmon_unlock_and_return; } - zr->v4l_buffers.active = fh->v4l_buffers.active = ZORAN_LOCKED; + zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED; zr->v4l_settings = fh->v4l_settings; zr->v4l_sync_tail = zr->v4l_pend_tail; @@ -2681,12 +2604,12 @@ static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type case ZORAN_MAP_MODE_JPG_PLAY: /* what is the codec mode right now? */ if (zr->jpg_buffers.active != ZORAN_ACTIVE || - fh->jpg_buffers.active != ZORAN_ACTIVE) { + fh->buffers.active != ZORAN_ACTIVE) { res = -EBUSY; goto strmon_unlock_and_return; } - zr->jpg_buffers.active = fh->jpg_buffers.active = ZORAN_LOCKED; + zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED; if (zr->jpg_que_head != zr->jpg_que_tail) { /* Start the jpeg codec when the first frame is queued */ @@ -2713,12 +2636,13 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; int i, res = 0; + unsigned long flags; mutex_lock(&zr->resource_lock); switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: /* raw capture */ - if (fh->v4l_buffers.active == ZORAN_FREE && + if (fh->buffers.active == ZORAN_FREE && zr->v4l_buffers.active != ZORAN_FREE) { res = -EPERM; /* stay off other's settings! */ goto strmoff_unlock_and_return; @@ -2726,30 +2650,30 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ if (zr->v4l_buffers.active == ZORAN_FREE) goto strmoff_unlock_and_return; + spin_lock_irqsave(&zr->spinlock, flags); /* unload capture */ if (zr->v4l_memgrab_active) { - unsigned long flags; - spin_lock_irqsave(&zr->spinlock, flags); zr36057_set_memgrab(zr, 0); - spin_unlock_irqrestore(&zr->spinlock, flags); } - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) + for (i = 0; i < fh->buffers.num_buffers; i++) zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; - fh->v4l_buffers = zr->v4l_buffers; + fh->buffers = zr->v4l_buffers; - zr->v4l_buffers.active = fh->v4l_buffers.active = ZORAN_FREE; + zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; zr->v4l_grab_seq = 0; zr->v4l_pend_head = zr->v4l_pend_tail = 0; zr->v4l_sync_tail = 0; + spin_unlock_irqrestore(&zr->spinlock, flags); + break; case ZORAN_MAP_MODE_JPG_REC: case ZORAN_MAP_MODE_JPG_PLAY: - if (fh->jpg_buffers.active == ZORAN_FREE && + if (fh->buffers.active == ZORAN_FREE && zr->jpg_buffers.active != ZORAN_FREE) { res = -EPERM; /* stay off other's settings! */ goto strmoff_unlock_and_return; @@ -2757,7 +2681,7 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ if (zr->jpg_buffers.active == ZORAN_FREE) goto strmoff_unlock_and_return; - res = jpg_qbuf(file, -1, + res = jpg_qbuf(fh, -1, (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? BUZ_MODE_MOTION_COMPRESS : BUZ_MODE_MOTION_DECOMPRESS); @@ -3018,7 +2942,7 @@ static int zoran_s_crop(struct file *file, void *__fh, struct v4l2_crop *crop) mutex_lock(&zr->resource_lock); - if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) { + if (fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: VIDIOC_S_CROP - cannot change settings while active\n", ZR_DEVNAME(zr)); @@ -3096,8 +3020,7 @@ static int zoran_s_jpegcomp(struct file *file, void *__fh, mutex_lock(&zr->resource_lock); - if (fh->v4l_buffers.active != ZORAN_FREE || - fh->jpg_buffers.active != ZORAN_FREE) { + if (fh->buffers.active != ZORAN_FREE) { dprintk(1, KERN_WARNING "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n", ZR_DEVNAME(zr)); @@ -3108,9 +3031,9 @@ static int zoran_s_jpegcomp(struct file *file, void *__fh, res = zoran_check_jpg_settings(zr, &settings, 0); if (res) goto sjpegc_unlock_and_return; - if (!fh->jpg_buffers.allocated) - fh->jpg_buffers.buffer_size = - zoran_v4l2_calc_bufsize(&fh->jpg_settings); + if (!fh->buffers.allocated) + fh->buffers.buffer_size = + zoran_v4l2_calc_bufsize(&fh->jpg_settings); fh->jpg_settings.jpg_comp = *params = settings.jpg_comp; sjpegc_unlock_and_return: mutex_unlock(&zr->resource_lock); @@ -3147,11 +3070,11 @@ zoran_poll (struct file *file, KERN_DEBUG "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n", ZR_DEVNAME(zr), __func__, - "FAL"[fh->v4l_buffers.active], zr->v4l_sync_tail, + "FAL"[fh->buffers.active], zr->v4l_sync_tail, "UPMD"[zr->v4l_buffers.buffer[frame].state], zr->v4l_pend_tail, zr->v4l_pend_head); /* Process is the one capturing? */ - if (fh->v4l_buffers.active != ZORAN_FREE && + if (fh->buffers.active != ZORAN_FREE && /* Buffer ready to DQBUF? */ zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE) res = POLLIN | POLLRDNORM; @@ -3169,10 +3092,10 @@ zoran_poll (struct file *file, KERN_DEBUG "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n", ZR_DEVNAME(zr), __func__, - "FAL"[fh->jpg_buffers.active], zr->jpg_que_tail, + "FAL"[fh->buffers.active], zr->jpg_que_tail, "UPMD"[zr->jpg_buffers.buffer[frame].state], zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head); - if (fh->jpg_buffers.active != ZORAN_FREE && + if (fh->buffers.active != ZORAN_FREE && zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) { if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) res = POLLIN | POLLRDNORM; @@ -3186,8 +3109,8 @@ zoran_poll (struct file *file, default: dprintk(1, KERN_ERR - "%s: zoran_poll() - internal error, unknown map_mode=%d\n", - ZR_DEVNAME(zr), fh->map_mode); + "%s: %s - internal error, unknown map_mode=%d\n", + ZR_DEVNAME(zr), __func__, fh->map_mode); res = POLLNVAL; } @@ -3221,92 +3144,53 @@ static void zoran_vm_close (struct vm_area_struct *vma) { struct zoran_mapping *map = vma->vm_private_data; - struct file *file = map->file; - struct zoran_fh *fh = file->private_data; + struct zoran_fh *fh = map->file->private_data; struct zoran *zr = fh->zr; int i; - map->count--; - if (map->count == 0) { - switch (fh->map_mode) { - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: + if (--map->count > 0) + return; - dprintk(3, KERN_INFO "%s: munmap(MJPEG)\n", - ZR_DEVNAME(zr)); + dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr), + __func__, mode_name(fh->map_mode)); - for (i = 0; i < fh->jpg_buffers.num_buffers; i++) { - if (fh->jpg_buffers.buffer[i].map == map) { - fh->jpg_buffers.buffer[i].map = - NULL; - } - } - kfree(map); - - for (i = 0; i < fh->jpg_buffers.num_buffers; i++) - if (fh->jpg_buffers.buffer[i].map) - break; - if (i == fh->jpg_buffers.num_buffers) { - mutex_lock(&zr->resource_lock); - - if (fh->jpg_buffers.active != ZORAN_FREE) { - jpg_qbuf(file, -1, zr->codec_mode); - zr->jpg_buffers.allocated = 0; - zr->jpg_buffers.active = - fh->jpg_buffers.active = - ZORAN_FREE; - } - jpg_fbuffer_free(file); - mutex_unlock(&zr->resource_lock); - } - - break; - - case ZORAN_MAP_MODE_RAW: - - dprintk(3, KERN_INFO "%s: munmap(V4L)\n", - ZR_DEVNAME(zr)); + for (i = 0; i < fh->buffers.num_buffers; i++) { + if (fh->buffers.buffer[i].map == map) + fh->buffers.buffer[i].map = NULL; + } + kfree(map); - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) { - if (fh->v4l_buffers.buffer[i].map == map) { - /* unqueue/unmap */ - fh->v4l_buffers.buffer[i].map = - NULL; - } - } - kfree(map); + /* Any buffers still mapped? */ + for (i = 0; i < fh->buffers.num_buffers; i++) + if (fh->buffers.buffer[i].map) + return; - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) - if (fh->v4l_buffers.buffer[i].map) - break; - if (i == fh->v4l_buffers.num_buffers) { - mutex_lock(&zr->resource_lock); - - if (fh->v4l_buffers.active != ZORAN_FREE) { - unsigned long flags; - - spin_lock_irqsave(&zr->spinlock, flags); - zr36057_set_memgrab(zr, 0); - zr->v4l_buffers.allocated = 0; - zr->v4l_buffers.active = - fh->v4l_buffers.active = - ZORAN_FREE; - spin_unlock_irqrestore(&zr->spinlock, flags); - } - v4l_fbuffer_free(file); - mutex_unlock(&zr->resource_lock); - } + dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr), + __func__, mode_name(fh->map_mode)); - break; + mutex_lock(&zr->resource_lock); - default: - printk(KERN_ERR - "%s: munmap() - internal error - unknown map mode %d\n", - ZR_DEVNAME(zr), fh->map_mode); - break; + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { + if (fh->buffers.active != ZORAN_FREE) { + unsigned long flags; + spin_lock_irqsave(&zr->spinlock, flags); + zr36057_set_memgrab(zr, 0); + zr->v4l_buffers.allocated = 0; + zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; + spin_unlock_irqrestore(&zr->spinlock, flags); } + v4l_fbuffer_free(fh); + } else { + if (fh->buffers.active != ZORAN_FREE) { + jpg_qbuf(fh, -1, zr->codec_mode); + zr->jpg_buffers.allocated = 0; + zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE; + } + jpg_fbuffer_free(fh); } + + mutex_unlock(&zr->resource_lock); } static struct vm_operations_struct zoran_vm_ops = { @@ -3329,90 +3213,106 @@ zoran_mmap (struct file *file, int res = 0; dprintk(3, - KERN_INFO "%s: mmap(%s) of 0x%08lx-0x%08lx (size=%lu)\n", - ZR_DEVNAME(zr), - fh->map_mode == ZORAN_MAP_MODE_RAW ? "V4L" : "MJPEG", - vma->vm_start, vma->vm_end, size); + KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n", + ZR_DEVNAME(zr), __func__, + mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size); if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) || !(vma->vm_flags & VM_WRITE)) { dprintk(1, KERN_ERR - "%s: mmap() - no MAP_SHARED/PROT_{READ,WRITE} given\n", - ZR_DEVNAME(zr)); + "%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } - switch (fh->map_mode) { + mutex_lock(&zr->resource_lock); - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: + if (!fh->buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: %s(%s) - buffers not yet allocated\n", + ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); + res = -ENOMEM; + goto mmap_unlock_and_return; + } - /* lock */ - mutex_lock(&zr->resource_lock); + first = offset / fh->buffers.buffer_size; + last = first - 1 + size / fh->buffers.buffer_size; + if (offset % fh->buffers.buffer_size != 0 || + size % fh->buffers.buffer_size != 0 || first < 0 || + last < 0 || first >= fh->buffers.num_buffers || + last >= fh->buffers.buffer_size) { + dprintk(1, + KERN_ERR + "%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", + ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size, + fh->buffers.buffer_size, + fh->buffers.num_buffers); + res = -EINVAL; + goto mmap_unlock_and_return; + } - /* Map the MJPEG buffers */ - if (!fh->jpg_buffers.allocated) { + /* Check if any buffers are already mapped */ + for (i = first; i <= last; i++) { + if (fh->buffers.buffer[i].map) { dprintk(1, KERN_ERR - "%s: zoran_mmap(MJPEG) - buffers not yet allocated\n", - ZR_DEVNAME(zr)); - res = -ENOMEM; - goto jpg_mmap_unlock_and_return; + "%s: %s(%s) - buffer %d already mapped\n", + ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i); + res = -EBUSY; + goto mmap_unlock_and_return; } + } - first = offset / fh->jpg_buffers.buffer_size; - last = first - 1 + size / fh->jpg_buffers.buffer_size; - if (offset % fh->jpg_buffers.buffer_size != 0 || - size % fh->jpg_buffers.buffer_size != 0 || first < 0 || - last < 0 || first >= fh->jpg_buffers.num_buffers || - last >= fh->jpg_buffers.num_buffers) { - dprintk(1, - KERN_ERR - "%s: mmap(MJPEG) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", - ZR_DEVNAME(zr), offset, size, - fh->jpg_buffers.buffer_size, - fh->jpg_buffers.num_buffers); - res = -EINVAL; - goto jpg_mmap_unlock_and_return; - } + /* map these buffers */ + map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); + if (!map) { + res = -ENOMEM; + goto mmap_unlock_and_return; + } + map->file = file; + map->count = 1; + + vma->vm_ops = &zoran_vm_ops; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = map; + + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { for (i = first; i <= last; i++) { - if (fh->jpg_buffers.buffer[i].map) { + todo = size; + if (todo > fh->buffers.buffer_size) + todo = fh->buffers.buffer_size; + page = fh->buffers.buffer[i].v4l.fbuffer_phys; + if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, + todo, PAGE_SHARED)) { dprintk(1, KERN_ERR - "%s: mmap(MJPEG) - buffer %d already mapped\n", - ZR_DEVNAME(zr), i); - res = -EBUSY; - goto jpg_mmap_unlock_and_return; + "%s: %s(V4L) - remap_pfn_range failed\n", + ZR_DEVNAME(zr), __func__); + res = -EAGAIN; + goto mmap_unlock_and_return; } + size -= todo; + start += todo; + fh->buffers.buffer[i].map = map; + if (size == 0) + break; } - - /* map these buffers (v4l_buffers[i]) */ - map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); - if (!map) { - res = -ENOMEM; - goto jpg_mmap_unlock_and_return; - } - map->file = file; - map->count = 1; - - vma->vm_ops = &zoran_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = map; - + } else { for (i = first; i <= last; i++) { for (j = 0; - j < fh->jpg_buffers.buffer_size / PAGE_SIZE; + j < fh->buffers.buffer_size / PAGE_SIZE; j++) { fraglen = - (le32_to_cpu(fh->jpg_buffers.buffer[i]. + (le32_to_cpu(fh->buffers.buffer[i].jpg. frag_tab[2 * j + 1]) & ~1) << 1; todo = size; if (todo > fraglen) todo = fraglen; pos = - le32_to_cpu(fh->jpg_buffers. - buffer[i].frag_tab[2 * j]); + le32_to_cpu(fh->buffers. + buffer[i].jpg.frag_tab[2 * j]); /* should just be pos on i386 */ page = virt_to_phys(bus_to_virt(pos)) >> PAGE_SHIFT; @@ -3420,115 +3320,29 @@ zoran_mmap (struct file *file, todo, PAGE_SHARED)) { dprintk(1, KERN_ERR - "%s: zoran_mmap(V4L) - remap_pfn_range failed\n", - ZR_DEVNAME(zr)); + "%s: %s(V4L) - remap_pfn_range failed\n", + ZR_DEVNAME(zr), __func__); res = -EAGAIN; - goto jpg_mmap_unlock_and_return; + goto mmap_unlock_and_return; } size -= todo; start += todo; if (size == 0) break; - if (le32_to_cpu(fh->jpg_buffers.buffer[i]. + if (le32_to_cpu(fh->buffers.buffer[i].jpg. frag_tab[2 * j + 1]) & 1) break; /* was last fragment */ } - fh->jpg_buffers.buffer[i].map = map; + fh->buffers.buffer[i].map = map; if (size == 0) break; } - jpg_mmap_unlock_and_return: - mutex_unlock(&zr->resource_lock); - - break; - - case ZORAN_MAP_MODE_RAW: - - mutex_lock(&zr->resource_lock); - - /* Map the V4L buffers */ - if (!fh->v4l_buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: zoran_mmap(V4L) - buffers not yet allocated\n", - ZR_DEVNAME(zr)); - res = -ENOMEM; - goto v4l_mmap_unlock_and_return; - } - - first = offset / fh->v4l_buffers.buffer_size; - last = first - 1 + size / fh->v4l_buffers.buffer_size; - if (offset % fh->v4l_buffers.buffer_size != 0 || - size % fh->v4l_buffers.buffer_size != 0 || first < 0 || - last < 0 || first >= fh->v4l_buffers.num_buffers || - last >= fh->v4l_buffers.buffer_size) { - dprintk(1, - KERN_ERR - "%s: mmap(V4L) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", - ZR_DEVNAME(zr), offset, size, - fh->v4l_buffers.buffer_size, - fh->v4l_buffers.num_buffers); - res = -EINVAL; - goto v4l_mmap_unlock_and_return; - } - for (i = first; i <= last; i++) { - if (fh->v4l_buffers.buffer[i].map) { - dprintk(1, - KERN_ERR - "%s: mmap(V4L) - buffer %d already mapped\n", - ZR_DEVNAME(zr), i); - res = -EBUSY; - goto v4l_mmap_unlock_and_return; - } - } - - /* map these buffers (v4l_buffers[i]) */ - map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); - if (!map) { - res = -ENOMEM; - goto v4l_mmap_unlock_and_return; - } - map->file = file; - map->count = 1; - - vma->vm_ops = &zoran_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = map; - - for (i = first; i <= last; i++) { - todo = size; - if (todo > fh->v4l_buffers.buffer_size) - todo = fh->v4l_buffers.buffer_size; - page = fh->v4l_buffers.buffer[i].fbuffer_phys; - if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, - todo, PAGE_SHARED)) { - dprintk(1, - KERN_ERR - "%s: zoran_mmap(V4L)i - remap_pfn_range failed\n", - ZR_DEVNAME(zr)); - res = -EAGAIN; - goto v4l_mmap_unlock_and_return; - } - size -= todo; - start += todo; - fh->v4l_buffers.buffer[i].map = map; - if (size == 0) - break; - } - v4l_mmap_unlock_and_return: - mutex_unlock(&zr->resource_lock); - - break; - - default: - dprintk(1, - KERN_ERR - "%s: zoran_mmap() - internal error - unknown map mode %d\n", - ZR_DEVNAME(zr), fh->map_mode); - break; } +mmap_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return 0; } |