diff options
author | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-01-20 15:53:08 -0200 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-01-20 15:53:08 -0200 |
commit | f01b6a9483f1dc1f6cb992ff34f75b2727805a8d (patch) | |
tree | f5697c12cbd352d96cd3f2b5127e972a10b99dca /linux/drivers/media | |
parent | b8fc762c42367e93f26bba3a3eca84dec4d0fe76 (diff) | |
parent | be389978b07c5cb7efeb8c0d10e52b7bd236f4b7 (diff) | |
download | mediapointer-dvb-s2-f01b6a9483f1dc1f6cb992ff34f75b2727805a8d.tar.gz mediapointer-dvb-s2-f01b6a9483f1dc1f6cb992ff34f75b2727805a8d.tar.bz2 |
merge: http://linuxtv.org/hg/~mkrufky/v4l-dvb
From: Mauro Carvalho Chehab <mchehab@infradead.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'linux/drivers/media')
35 files changed, 2631 insertions, 1886 deletions
diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 225c6e821..118c6e800 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -9,6 +9,10 @@ some v4l2 code lines are taken from Justin's bttv2 driver which is (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za> + Cropping and overscan support + Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at> + Sponsored by OPQ Systems AB + 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 @@ -66,6 +70,7 @@ static unsigned int radio[BTTV_MAX]; static unsigned int irq_debug; static unsigned int gbuffers = 8; static unsigned int gbufsize = 0x208000; +static unsigned int reset_crop = 1; static int video_nr = -1; static int radio_nr = -1; @@ -105,6 +110,7 @@ module_param(radio_nr, int, 0444); module_param(vbi_nr, int, 0444); module_param(gbuffers, int, 0444); module_param(gbufsize, int, 0444); +module_param(reset_crop, int, 0444); module_param(v4l2, int, 0644); module_param(bigendian, int, 0644); @@ -140,6 +146,8 @@ MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)"); MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)"); MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8"); MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000"); +MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default " + "is 1 (yes) for compatibility with older applications"); MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)"); MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)"); MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)"); @@ -205,6 +213,33 @@ static u8 SRAM_Table[][60] = } }; +/* minhdelayx1 first video pixel we can capture on a line and + hdelayx1 start of active video, both relative to rising edge of + /HRESET pulse (0H) in 1 / fCLKx1. + swidth width of active video and + totalwidth total line width, both in 1 / fCLKx1. + sqwidth total line width in square pixels. + vdelay start of active video in 2 * field lines relative to + trailing edge of /VRESET pulse (VDELAY register). + sheight height of active video in 2 * field lines. + videostart0 ITU-R frame line number of the line corresponding + to vdelay in the first field. */ +#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth, \ + vdelay, sheight, videostart0) \ + .cropcap.bounds.left = minhdelayx1, \ + /* * 2 because vertically we count field lines times two, */ \ + /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */ \ + .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \ + /* 4 is a safety margin at the end of the line. */ \ + .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4, \ + .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY, \ + .cropcap.defrect.left = hdelayx1, \ + .cropcap.defrect.top = (videostart0) * 2, \ + .cropcap.defrect.width = swidth, \ + .cropcap.defrect.height = sheight, \ + .cropcap.pixelaspect.numerator = totalwidth, \ + .cropcap.pixelaspect.denominator = sqwidth, + const struct bttv_tvnorm bttv_tvnorms[] = { /* PAL-BDGHI */ /* max. active video is actually 922, but 924 is divisible by 4 and 3! */ @@ -223,11 +258,26 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .hdelayx1 = 186, .hactivex1 = 924, .vdelay = 0x20, - .vbipack = 255, + .vbipack = 255, /* min (2048 / 4, 0x1ff) & 0xff */ .sram = 0, /* ITU-R frame line number of the first VBI line - we can capture, of the first and second field. */ - .vbistart = { 7,320 }, + we can capture, of the first and second field. + The last line is determined by cropcap.bounds. */ + .vbistart = { 7, 320 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 186, + /* Should be (768 * 1135 + 944 / 2) / 944. + cropcap.defrect is used for image width + checks, so we keep the old value 924. */ + /* swidth */ 924, + /* totalwidth */ 1135, + /* sqwidth */ 944, + /* vdelay */ 0x20, + /* sheight */ 576, + /* videostart0 */ 23) + /* bt878 (and bt848?) can capture another + line below active video. */ + .cropcap.bounds.height = (576 + 2) + 0x20 - 2, },{ .v4l2_id = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, .name = "NTSC", @@ -242,9 +292,18 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .hdelayx1 = 128, .hactivex1 = 910, .vdelay = 0x1a, - .vbipack = 144, + .vbipack = 144, /* min (1600 / 4, 0x1ff) & 0xff */ .sram = 1, .vbistart = { 10, 273 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 128, + /* Should be (640 * 910 + 780 / 2) / 780? */ + /* swidth */ 768, + /* totalwidth */ 910, + /* sqwidth */ 780, + /* vdelay */ 0x1a, + /* sheight */ 480, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_SECAM, .name = "SECAM", @@ -262,6 +321,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vbipack = 255, .sram = 0, /* like PAL, correct? */ .vbistart = { 7, 320 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 186, + /* swidth */ 924, + /* totalwidth */ 1135, + /* sqwidth */ 944, + /* vdelay */ 0x20, + /* sheight */ 576, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_PAL_Nc, .name = "PAL-Nc", @@ -279,6 +346,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vbipack = 144, .sram = -1, .vbistart = { 7, 320 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 130, + /* swidth */ (640 * 910 + 780 / 2) / 780, + /* totalwidth */ 910, + /* sqwidth */ 780, + /* vdelay */ 0x1a, + /* sheight */ 576, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_PAL_M, .name = "PAL-M", @@ -296,6 +371,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vbipack = 144, .sram = -1, .vbistart = { 10, 273 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 135, + /* swidth */ (640 * 910 + 780 / 2) / 780, + /* totalwidth */ 910, + /* sqwidth */ 780, + /* vdelay */ 0x1a, + /* sheight */ 480, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_PAL_N, .name = "PAL-N", @@ -312,7 +395,15 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x20, .vbipack = 144, .sram = -1, - .vbistart = { 7, 320}, + .vbistart = { 7, 320 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 186, + /* swidth */ (768 * 1135 + 944 / 2) / 944, + /* totalwidth */ 1135, + /* sqwidth */ 944, + /* vdelay */ 0x20, + /* sheight */ 576, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_NTSC_M_JP, .name = "NTSC-JP", @@ -329,7 +420,15 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x16, .vbipack = 144, .sram = -1, - .vbistart = {10, 273}, + .vbistart = { 10, 273 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 135, + /* swidth */ (640 * 910 + 780 / 2) / 780, + /* totalwidth */ 910, + /* sqwidth */ 780, + /* vdelay */ 0x16, + /* sheight */ 480, + /* videostart0 */ 23) },{ /* that one hopefully works with the strange timing * which video recorders produce when playing a NTSC @@ -351,6 +450,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vtotal = 524, .sram = -1, .vbistart = { 10, 273 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 186, + /* swidth */ 924, + /* totalwidth */ 1135, + /* sqwidth */ 944, + /* vdelay */ 0x1a, + /* sheight */ 480, + /* videostart0 */ 23) } }; static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms); @@ -691,25 +798,89 @@ static const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls); /* ----------------------------------------------------------------------- */ /* resource management */ +/* + RESOURCE_ allocated by freed by + + VIDEO_READ bttv_read 1) bttv_read 2) + + VIDEO_STREAM VIDIOC_STREAMON VIDIOC_STREAMOFF + VIDIOC_QBUF 1) bttv_release + VIDIOCMCAPTURE 1) + + OVERLAY VIDIOCCAPTURE on VIDIOCCAPTURE off + VIDIOC_OVERLAY on VIDIOC_OVERLAY off + 3) bttv_release + + VBI VIDIOC_STREAMON VIDIOC_STREAMOFF + VIDIOC_QBUF 1) bttv_release + bttv_read, bttv_poll 1) 4) + + 1) The resource must be allocated when we enter buffer prepare functions + and remain allocated while buffers are in the DMA queue. + 2) This is a single frame read. + 3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when + RESOURCE_OVERLAY is allocated. + 4) This is a continuous read, implies VIDIOC_STREAMON. + + Note this driver permits video input and standard changes regardless if + resources are allocated. +*/ + +#define VBI_RESOURCES (RESOURCE_VBI) +#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \ + RESOURCE_VIDEO_STREAM | \ + RESOURCE_OVERLAY) + static int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit) { + int xbits; /* mutual exclusive resources */ + if (fh->resources & bit) /* have it already allocated */ return 1; + xbits = bit; + if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM)) + xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM; + /* is it free? */ - mutex_lock(&btv->reslock); - if (btv->resources & bit) { + mutex_lock(&btv->lock); + if (btv->resources & xbits) { /* no, someone else uses it */ - mutex_unlock(&btv->reslock); - return 0; + goto fail; + } + + if ((bit & VIDEO_RESOURCES) + && 0 == (btv->resources & VIDEO_RESOURCES)) { + /* Do crop - use current, don't - use default parameters. */ + __s32 top = btv->crop[!!fh->do_crop].rect.top; + + if (btv->vbi_end > top) + goto fail; + + /* We cannot capture the same line as video and VBI data. + Claim scan lines crop[].rect.top to bottom. */ + btv->crop_start = top; + } else if (bit & VBI_RESOURCES) { + __s32 end = fh->vbi_fmt.end; + + if (end > btv->crop_start) + goto fail; + + /* Claim scan lines above fh->vbi_fmt.end. */ + btv->vbi_end = end; } + /* it's free, grab it */ fh->resources |= bit; btv->resources |= bit; - mutex_unlock(&btv->reslock); + mutex_unlock(&btv->lock); return 1; + + fail: + mutex_unlock(&btv->lock); + return 0; } static @@ -724,6 +895,35 @@ int locked_btres(struct bttv *btv, int bit) return (btv->resources & bit); } +/* Call with btv->lock down. */ +static void +disclaim_vbi_lines(struct bttv *btv) +{ + btv->vbi_end = 0; +} + +/* Call with btv->lock down. */ +static void +disclaim_video_lines(struct bttv *btv) +{ + const struct bttv_tvnorm *tvnorm; + u8 crop; + + tvnorm = &bttv_tvnorms[btv->tvnorm]; + btv->crop_start = tvnorm->cropcap.bounds.top + + tvnorm->cropcap.bounds.height; + + /* VBI capturing ends at VDELAY, start of video capturing, no + matter how many lines the VBI RISC program expects. When video + capturing is off, it shall no longer "preempt" VBI capturing, + so we set VDELAY to maximum. */ + crop = btread(BT848_E_CROP) | 0xc0; + btwrite(crop, BT848_E_CROP); + btwrite(0xfe, BT848_E_VDELAY_LO); + btwrite(crop, BT848_O_CROP); + btwrite(0xfe, BT848_O_VDELAY_LO); +} + static void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits) { @@ -733,10 +933,19 @@ void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits) printk("bttv: BUG! (btres)\n"); } #endif - mutex_lock(&btv->reslock); + mutex_lock(&btv->lock); fh->resources &= ~bits; btv->resources &= ~bits; - mutex_unlock(&btv->reslock); + + bits = btv->resources; + + if (0 == (bits & VIDEO_RESOURCES)) + disclaim_video_lines(btv); + + if (0 == (bits & VBI_RESOURCES)) + disclaim_vbi_lines(btv); + + mutex_unlock(&btv->lock); } /* ----------------------------------------------------------------------- */ @@ -1050,6 +1259,36 @@ i2c_vidiocschan(struct bttv *btv) bttv_tda9880_setnorm(btv,btv->tvnorm); } +static void +bttv_crop_calc_limits(struct bttv_crop *c) +{ + /* Scale factor min. 1:1, max. 16:1. Min. image size + 48 x 32. Scaled width must be a multiple of 4. */ + + if (1) { + /* For bug compatibility with VIDIOCGCAP and image + size checks in earlier driver versions. */ + c->min_scaled_width = 48; + c->min_scaled_height = 32; + } else { + c->min_scaled_width = + (max(48, c->rect.width >> 4) + 3) & ~3; + c->min_scaled_height = + max(32, c->rect.height >> 4); + } + + c->max_scaled_width = c->rect.width & ~3; + c->max_scaled_height = c->rect.height; +} + +static void +bttv_crop_reset(struct bttv_crop *c, int norm) +{ + c->rect = bttv_tvnorms[norm].cropcap.defrect; + bttv_crop_calc_limits(c); +} + +/* Call with btv->lock down. */ static int set_tvnorm(struct bttv *btv, unsigned int norm) { @@ -1058,9 +1297,24 @@ set_tvnorm(struct bttv *btv, unsigned int norm) if (norm < 0 || norm >= BTTV_TVNORMS) return -EINVAL; - btv->tvnorm = norm; tvnorm = &bttv_tvnorms[norm]; + if (btv->tvnorm < 0 || + btv->tvnorm >= BTTV_TVNORMS || + 0 != memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, + &tvnorm->cropcap, + sizeof (tvnorm->cropcap))) { + bttv_crop_reset(&btv->crop[0], norm); + btv->crop[1] = btv->crop[0]; /* current = default */ + + if (0 == (btv->resources & VIDEO_RESOURCES)) { + btv->crop_start = tvnorm->cropcap.bounds.top + + tvnorm->cropcap.bounds.height; + } + } + + btv->tvnorm = norm; + btwrite(tvnorm->adelay, BT848_ADELAY); btwrite(tvnorm->bdelay, BT848_BDELAY); btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), @@ -1082,6 +1336,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm) return 0; } +/* Call with btv->lock down. */ static void set_input(struct bttv *btv, unsigned int input) { @@ -1484,13 +1739,13 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, btv->loop_irq |= 1; bttv_set_dma(btv, 0x03); spin_unlock_irqrestore(&btv->s_lock,flags); - if (NULL == new) - free_btres(btv,fh,RESOURCE_OVERLAY); if (NULL != old) { dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state); bttv_dma_free(&fh->cap,btv, old); kfree(old); } + if (NULL == new) + free_btres(btv,fh,RESOURCE_OVERLAY); dprintk("switch_overlay: done\n"); return retval; } @@ -1504,7 +1759,10 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, unsigned int width, unsigned int height, enum v4l2_field field) { + struct bttv_fh *fh = q->priv_data; int redo_dma_risc = 0; + struct bttv_crop c; + int norm; int rc; /* check settings */ @@ -1516,12 +1774,52 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, if (width*height > buf->vb.bsize) return -EINVAL; buf->vb.size = buf->vb.bsize; + + /* Make sure tvnorm and vbi_end remain consistent + until we're done. */ + mutex_lock(&btv->lock); + + norm = btv->tvnorm; + + /* In this mode capturing always starts at defrect.top + (default VDELAY), ignoring cropping parameters. */ + if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) { + mutex_unlock(&btv->lock); + return -EINVAL; + } + + mutex_unlock(&btv->lock); + + c.rect = bttv_tvnorms[norm].cropcap.defrect; } else { - if (width < 48 || - height < 32 || - width > bttv_tvnorms[btv->tvnorm].swidth || - height > bttv_tvnorms[btv->tvnorm].sheight) + mutex_lock(&btv->lock); + + norm = btv->tvnorm; + c = btv->crop[!!fh->do_crop]; + + mutex_unlock(&btv->lock); + + if (width < c.min_scaled_width || + width > c.max_scaled_width || + height < c.min_scaled_height) return -EINVAL; + + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + /* btv->crop counts frame lines. Max. scale + factor is 16:1 for frames, 8:1 for fields. */ + if (height * 2 > c.max_scaled_height) + return -EINVAL; + break; + + default: + if (height > c.max_scaled_height) + return -EINVAL; + break; + } + buf->vb.size = (width * height * fmt->depth) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; @@ -1530,12 +1828,17 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, /* alloc + fill struct bttv_buffer (if changed) */ if (buf->vb.width != width || buf->vb.height != height || buf->vb.field != field || - buf->tvnorm != btv->tvnorm || buf->fmt != fmt) { + buf->tvnorm != norm || buf->fmt != fmt || + buf->crop.top != c.rect.top || + buf->crop.left != c.rect.left || + buf->crop.width != c.rect.width || + buf->crop.height != c.rect.height) { buf->vb.width = width; buf->vb.height = height; buf->vb.field = field; - buf->tvnorm = btv->tvnorm; + buf->tvnorm = norm; buf->fmt = fmt; + buf->crop = c.rect; redo_dma_risc = 1; } @@ -1964,11 +2267,179 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return 0; } -static int verify_window(const struct bttv_tvnorm *tvn, - struct v4l2_window *win, int fixup) +/* Given cropping boundaries b and the scaled width and height of a + single field or frame, which must not exceed hardware limits, this + function adjusts the cropping parameters c. */ +static void +bttv_crop_adjust (struct bttv_crop * c, + const struct v4l2_rect * b, + __s32 width, + __s32 height, + enum v4l2_field field) +{ + __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field); + __s32 max_left; + __s32 max_top; + + if (width < c->min_scaled_width) { + /* Max. hor. scale factor 16:1. */ + c->rect.width = width * 16; + } else if (width > c->max_scaled_width) { + /* Min. hor. scale factor 1:1. */ + c->rect.width = width; + + max_left = b->left + b->width - width; + max_left = min(max_left, (__s32) MAX_HDELAY); + if (c->rect.left > max_left) + c->rect.left = max_left; + } + + if (height < c->min_scaled_height) { + /* Max. vert. scale factor 16:1, single fields 8:1. */ + c->rect.height = height * 16; + } else if (frame_height > c->max_scaled_height) { + /* Min. vert. scale factor 1:1. + Top and height count field lines times two. */ + c->rect.height = (frame_height + 1) & ~1; + + max_top = b->top + b->height - c->rect.height; + if (c->rect.top > max_top) + c->rect.top = max_top; + } + + bttv_crop_calc_limits(c); +} + +/* Returns an error if scaling to a frame or single field with the given + width and height is not possible with the current cropping parameters + and width aligned according to width_mask. If adjust_size is TRUE the + function may adjust the width and/or height instead, rounding width + to (width + width_bias) & width_mask. If adjust_crop is TRUE it may + also adjust the current cropping parameters to get closer to the + desired image size. */ +static int +limit_scaled_size (struct bttv_fh * fh, + __s32 * width, + __s32 * height, + enum v4l2_field field, + unsigned int width_mask, + unsigned int width_bias, + int adjust_size, + int adjust_crop) +{ + struct bttv *btv = fh->btv; + const struct v4l2_rect *b; + struct bttv_crop *c; + __s32 min_width; + __s32 min_height; + __s32 max_width; + __s32 max_height; + int rc; + + BUG_ON((int) width_mask >= 0 || + width_bias >= (unsigned int) -width_mask); + + /* Make sure tvnorm, vbi_end and the current cropping parameters + remain consistent until we're done. */ + mutex_lock(&btv->lock); + + b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds; + + /* Do crop - use current, don't - use default parameters. */ + c = &btv->crop[!!fh->do_crop]; + + if (fh->do_crop + && adjust_size + && adjust_crop + && !locked_btres(btv, VIDEO_RESOURCES)) { + min_width = 48; + min_height = 32; + + /* We cannot scale up. When the scaled image is larger + than crop.rect we adjust the crop.rect as required + by the V4L2 spec, hence cropcap.bounds are our limit. */ + max_width = min(b->width, (__s32) MAX_HACTIVE); + max_height = b->height; + + /* We cannot capture the same line as video and VBI data. + Note btv->vbi_end is really a minimum, see + bttv_vbi_try_fmt(). */ + if (btv->vbi_end > b->top) { + max_height -= btv->vbi_end - b->top; + rc = -EBUSY; + if (min_height > max_height) + goto fail; + } + } else { + rc = -EBUSY; + if (btv->vbi_end > c->rect.top) + goto fail; + + min_width = c->min_scaled_width; + min_height = c->min_scaled_height; + max_width = c->max_scaled_width; + max_height = c->max_scaled_height; + + adjust_crop = 0; + } + + min_width = (min_width - width_mask - 1) & width_mask; + max_width = max_width & width_mask; + + /* Max. scale factor is 16:1 for frames, 8:1 for fields. */ + min_height = min_height; + /* Min. scale factor is 1:1. */ + max_height >>= !V4L2_FIELD_HAS_BOTH(field); + + if (adjust_size) { + *width = clamp(*width, min_width, max_width); + *height = clamp(*height, min_height, max_height); + + /* Round after clamping to avoid overflow. */ + *width = (*width + width_bias) & width_mask; + + if (adjust_crop) { + bttv_crop_adjust(c, b, *width, *height, field); + + if (btv->vbi_end > c->rect.top) { + /* Move the crop window out of the way. */ + c->rect.top = btv->vbi_end; + } + } + } else { + rc = -EINVAL; + if (*width < min_width || + *height < min_height || + *width > max_width || + *height > max_height || + 0 != (*width & ~width_mask)) + goto fail; + } + + rc = 0; /* success */ + + fail: + mutex_unlock(&btv->lock); + + return rc; +} + +/* Returns an error if the given overlay window dimensions are not + possible with the current cropping parameters. If adjust_size is + TRUE the function may adjust the window width and/or height + instead, however it always rounds the horizontal position and + width as btcx_align() does. If adjust_crop is TRUE the function + may also adjust the current cropping parameters to get closer + to the desired window size. */ +static int +verify_window (struct bttv_fh * fh, + struct v4l2_window * win, + int adjust_size, + int adjust_crop) { enum v4l2_field field; - int maxw, maxh; + unsigned int width_mask; + int rc; if (win->w.width < 48 || win->w.height < 32) return -EINVAL; @@ -1976,32 +2447,52 @@ static int verify_window(const struct bttv_tvnorm *tvn, return -EINVAL; field = win->field; - maxw = tvn->swidth; - maxh = tvn->sheight; if (V4L2_FIELD_ANY == field) { - field = (win->w.height > maxh/2) + __s32 height2; + + height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1; + field = (win->w.height > height2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; } switch (field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: - maxh = maxh / 2; - break; case V4L2_FIELD_INTERLACED: break; default: return -EINVAL; } - if (!fixup && (win->w.width > maxw || win->w.height > maxh)) + /* 4-byte alignment. */ + if (NULL == fh->ovfmt) return -EINVAL; + width_mask = ~0; + switch (fh->ovfmt->depth) { + case 8: + case 24: + width_mask = ~3; + break; + case 16: + width_mask = ~1; + break; + case 32: + break; + default: + BUG(); + } + + win->w.width -= win->w.left & ~width_mask; + win->w.left = (win->w.left - width_mask - 1) & width_mask; + + rc = limit_scaled_size(fh, &win->w.width, &win->w.height, + field, width_mask, + /* width_bias: round down */ 0, + adjust_size, adjust_crop); + if (0 != rc) + return rc; - if (win->w.width > maxw) - win->w.width = maxw; - if (win->w.height > maxh) - win->w.height = maxh; win->field = field; return 0; } @@ -2016,7 +2507,9 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv, return -EINVAL; if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED)) return -EINVAL; - retval = verify_window(&bttv_tvnorms[btv->tvnorm],win,fixup); + retval = verify_window(fh, win, + /* adjust_size */ fixup, + /* adjust_crop */ fixup); if (0 != retval) return retval; @@ -2073,6 +2566,7 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv, struct bttv_buffer *new; new = videobuf_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); retval = bttv_switch_overlay(btv,fh,new); } @@ -2105,7 +2599,7 @@ static int bttv_resource(struct bttv_fh *fh) switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - res = RESOURCE_VIDEO; + res = RESOURCE_VIDEO_STREAM; break; case V4L2_BUF_TYPE_VBI_CAPTURE: res = RESOURCE_VBI; @@ -2163,7 +2657,7 @@ static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f) f->fmt.win.field = fh->ov.field; return 0; case V4L2_BUF_TYPE_VBI_CAPTURE: - bttv_vbi_get_fmt(fh,f); + bttv_vbi_get_fmt(fh, &f->fmt.vbi); return 0; default: return -EINVAL; @@ -2171,35 +2665,35 @@ static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f) } static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv, - struct v4l2_format *f) + struct v4l2_format *f, int adjust_crop) { switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { const struct bttv_format *fmt; enum v4l2_field field; - unsigned int maxw,maxh; + __s32 width, height; + int rc; fmt = format_by_fourcc(f->fmt.pix.pixelformat); if (NULL == fmt) return -EINVAL; - /* fixup format */ - maxw = bttv_tvnorms[btv->tvnorm].swidth; - maxh = bttv_tvnorms[btv->tvnorm].sheight; field = f->fmt.pix.field; - if (V4L2_FIELD_ANY == field) - field = (f->fmt.pix.height > maxh/2) + if (V4L2_FIELD_ANY == field) { + __s32 height2; + + height2 = btv->crop[!!fh->do_crop].rect.height >> 1; + field = (f->fmt.pix.height > height2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; + } if (V4L2_FIELD_SEQ_BT == field) field = V4L2_FIELD_SEQ_TB; switch (field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: case V4L2_FIELD_ALTERNATE: - maxh = maxh/2; - break; case V4L2_FIELD_INTERLACED: break; case V4L2_FIELD_SEQ_TB: @@ -2210,28 +2704,29 @@ static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv, return -EINVAL; } + width = f->fmt.pix.width; + height = f->fmt.pix.height; + + rc = limit_scaled_size(fh, &width, &height, field, + /* width_mask: 4 pixels */ ~3, + /* width_bias: nearest */ 2, + /* adjust_size */ 1, + adjust_crop); + if (0 != rc) + return rc; + /* update data for the application */ f->fmt.pix.field = field; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - pix_format_set_size (&f->fmt.pix, fmt, - f->fmt.pix.width & ~3, - f->fmt.pix.height); + pix_format_set_size(&f->fmt.pix, fmt, width, height); return 0; } case V4L2_BUF_TYPE_VIDEO_OVERLAY: - return verify_window(&bttv_tvnorms[btv->tvnorm], - &f->fmt.win, 1); + return verify_window(fh, &f->fmt.win, + /* adjust_size */ 1, + /* adjust_crop */ 0); case V4L2_BUF_TYPE_VBI_CAPTURE: - bttv_vbi_try_fmt(fh,f); - return 0; + return bttv_vbi_try_fmt(fh, &f->fmt.vbi); default: return -EINVAL; } @@ -2250,7 +2745,7 @@ static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv, retval = bttv_switch_type(fh,f->type); if (0 != retval) return retval; - retval = bttv_try_fmt(fh,btv,f); + retval = bttv_try_fmt(fh,btv,f, /* adjust_crop */ 1); if (0 != retval) return retval; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -2279,12 +2774,7 @@ static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv, retval = bttv_switch_type(fh,f->type); if (0 != retval) return retval; - if (locked_btres(fh->btv, RESOURCE_VBI)) - return -EBUSY; - bttv_vbi_try_fmt(fh,f); - bttv_vbi_setlines(fh,btv,f->fmt.vbi.count[0]); - bttv_vbi_get_fmt(fh,f); - return 0; + return bttv_vbi_set_fmt(fh, &f->fmt.vbi); default: return -EINVAL; } @@ -2542,6 +3032,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, if (*on) { fh->ov.tvnorm = btv->tvnorm; new = videobuf_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); } else { new = NULL; @@ -2576,10 +3067,16 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, struct video_mmap *vm = arg; struct bttv_buffer *buf; enum v4l2_field field; + __s32 height2; + int res; if (vm->frame >= VIDEO_MAX_FRAME) return -EINVAL; + res = bttv_resource(fh); + if (!check_alloc_btres(btv, fh, res)) + return -EBUSY; + mutex_lock(&fh->cap.lock); retval = -EINVAL; buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame]; @@ -2591,7 +3088,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, buf->vb.state == STATE_ACTIVE) goto fh_unlock_and_return; - field = (vm->height > bttv_tvnorms[btv->tvnorm].sheight/2) + height2 = btv->crop[!!fh->do_crop].rect.height >> 1; + field = (vm->height > height2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; retval = bttv_prepare_buffer(&fh->cap,btv,buf, @@ -2638,54 +3136,17 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, } case VIDIOCGVBIFMT: - { - struct vbi_format *fmt = (void *) arg; - struct v4l2_format fmt2; - if (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) { retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); if (0 != retval) return retval; } - bttv_vbi_get_fmt(fh, &fmt2); - - memset(fmt,0,sizeof(*fmt)); - fmt->sampling_rate = fmt2.fmt.vbi.sampling_rate; - fmt->samples_per_line = fmt2.fmt.vbi.samples_per_line; - fmt->sample_format = VIDEO_PALETTE_RAW; - fmt->start[0] = fmt2.fmt.vbi.start[0]; - fmt->count[0] = fmt2.fmt.vbi.count[0]; - fmt->start[1] = fmt2.fmt.vbi.start[1]; - fmt->count[1] = fmt2.fmt.vbi.count[1]; - if (fmt2.fmt.vbi.flags & V4L2_VBI_UNSYNC) - fmt->flags |= VBI_UNSYNC; - if (fmt2.fmt.vbi.flags & V4L2_VBI_INTERLACED) - fmt->flags |= VBI_INTERLACED; - return 0; - } - case VIDIOCSVBIFMT: - { - struct vbi_format *fmt = (void *) arg; - struct v4l2_format fmt2; - retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); - if (0 != retval) - return retval; - bttv_vbi_get_fmt(fh, &fmt2); - - if (fmt->sampling_rate != fmt2.fmt.vbi.sampling_rate || - fmt->samples_per_line != fmt2.fmt.vbi.samples_per_line || - fmt->sample_format != VIDEO_PALETTE_RAW || - fmt->start[0] != fmt2.fmt.vbi.start[0] || - fmt->start[1] != fmt2.fmt.vbi.start[1] || - fmt->count[0] != fmt->count[1] || - fmt->count[0] < 1 || - fmt->count[0] > 32 /* VBI_MAXLINES */) - return -EINVAL; + /* fall through */ - bttv_vbi_setlines(fh,btv,fmt->count[0]); - return 0; - } + case VIDIOCSVBIFMT: + return v4l_compat_translate_ioctl(inode, file, cmd, + arg, bttv_do_ioctl); case BTTV_VERSION: case VIDIOCGFREQ: @@ -2778,7 +3239,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_TRY_FMT: { struct v4l2_format *f = arg; - return bttv_try_fmt(fh,btv,f); + return bttv_try_fmt(fh,btv,f, /* adjust_crop */ 0); } case VIDIOC_G_FMT: { @@ -2817,16 +3278,23 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, if (0 == (fmt->flags & FORMAT_FLAGS_PACKED)) return -EINVAL; - mutex_lock(&fh->cap.lock); retval = -EINVAL; if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { - if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth) - goto fh_unlock_and_return; - if (fb->fmt.height > bttv_tvnorms[btv->tvnorm].sheight) - goto fh_unlock_and_return; + __s32 width = fb->fmt.width; + __s32 height = fb->fmt.height; + + retval = limit_scaled_size(fh, &width, &height, + V4L2_FIELD_INTERLACED, + /* width_mask */ ~3, + /* width_bias */ 2, + /* adjust_size */ 0, + /* adjust_crop */ 0); + if (0 != retval) + return retval; } /* ok, accept it */ + mutex_lock(&fh->cap.lock); btv->fbuf.base = fb->base; btv->fbuf.fmt.width = fb->fmt.width; btv->fbuf.fmt.height = fb->fmt.height; @@ -2853,6 +3321,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, struct bttv_buffer *new; new = videobuf_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new); retval = bttv_switch_overlay(btv,fh,new); } @@ -2868,7 +3337,13 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, return videobuf_querybuf(bttv_queue(fh),arg); case VIDIOC_QBUF: + { + int res = bttv_resource(fh); + + if (!check_alloc_btres(btv, fh, res)) + return -EBUSY; return videobuf_qbuf(bttv_queue(fh),arg); + } case VIDIOC_DQBUF: return videobuf_dqbuf(bttv_queue(fh),arg, @@ -2967,6 +3442,122 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, return v4l2_prio_change(&btv->prio, &fh->prio, *prio); } + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *cap = arg; + enum v4l2_buf_type type; + + type = cap->type; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + + *cap = bttv_tvnorms[btv->tvnorm].cropcap; + cap->type = type; + + return 0; + } + case VIDIOC_G_CROP: + { + struct v4l2_crop * crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + + /* No fh->do_crop = 1; because btv->crop[1] may be + inconsistent with fh->width or fh->height and apps + do not expect a change here. */ + + crop->c = btv->crop[!!fh->do_crop].rect; + + return 0; + } + case VIDIOC_S_CROP: + { + struct v4l2_crop *crop = arg; + const struct v4l2_rect *b; + struct bttv_crop c; + __s32 b_left; + __s32 b_top; + __s32 b_right; + __s32 b_bottom; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + + retval = v4l2_prio_check(&btv->prio,&fh->prio); + if (0 != retval) + return retval; + + /* Make sure tvnorm, vbi_end and the current cropping + parameters remain consistent until we're done. Note + read() may change vbi_end in check_alloc_btres(). */ + mutex_lock(&btv->lock); + + retval = -EBUSY; + + if (locked_btres(fh->btv, VIDEO_RESOURCES)) + goto btv_unlock_and_return; + + b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds; + + b_left = b->left; + b_right = b_left + b->width; + b_bottom = b->top + b->height; + + b_top = max(b->top, btv->vbi_end); + if (b_top + 32 >= b_bottom) + goto btv_unlock_and_return; + + /* Min. scaled size 48 x 32. */ + c.rect.left = clamp(crop->c.left, b_left, b_right - 48); + c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY); + + c.rect.width = clamp(crop->c.width, + 48, b_right - c.rect.left); + + c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32); + /* Top and height must be a multiple of two. */ + c.rect.top = (c.rect.top + 1) & ~1; + + c.rect.height = clamp(crop->c.height, + 32, b_bottom - c.rect.top); + c.rect.height = (c.rect.height + 1) & ~1; + + bttv_crop_calc_limits(&c); + + btv->crop[1] = c; + + mutex_unlock(&btv->lock); + + fh->do_crop = 1; + + mutex_lock(&fh->cap.lock); + + if (fh->width < c.min_scaled_width) { + fh->width = c.min_scaled_width; + btv->init.width = c.min_scaled_width; + } else if (fh->width > c.max_scaled_width) { + fh->width = c.max_scaled_width; + btv->init.width = c.max_scaled_width; + } + + if (fh->height < c.min_scaled_height) { + fh->height = c.min_scaled_height; + btv->init.height = c.min_scaled_height; + } else if (fh->height > c.max_scaled_height) { + fh->height = c.max_scaled_height; + btv->init.height = c.max_scaled_height; + } + + mutex_unlock(&fh->cap.lock); + + return 0; + } + case VIDIOC_ENUMSTD: case VIDIOC_G_STD: case VIDIOC_S_STD: @@ -2988,6 +3579,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, fh_unlock_and_return: mutex_unlock(&fh->cap.lock); return retval; + + btv_unlock_and_return: + mutex_unlock(&btv->lock); + return retval; } static int bttv_ioctl(struct inode *inode, struct file *file, @@ -2997,8 +3592,26 @@ static int bttv_ioctl(struct inode *inode, struct file *file, switch (cmd) { case BTTV_VBISIZE: + { + const struct bttv_tvnorm *tvnorm; + + tvnorm = fh->vbi_fmt.tvnorm; + + if (fh->vbi_fmt.fmt.start[0] != tvnorm->vbistart[0] || + fh->vbi_fmt.fmt.start[1] != tvnorm->vbistart[1] || + fh->vbi_fmt.fmt.count[0] != fh->vbi_fmt.fmt.count[1]) { + /* BTTV_VBISIZE cannot express these parameters, + however open() resets the paramters to defaults + and apps shouldn't call BTTV_VBISIZE after + VIDIOC_S_FMT. */ + return -EINVAL; + } + bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); - return fh->lines * 2 * 2048; + return (fh->vbi_fmt.fmt.count[0] * 2 + * fh->vbi_fmt.fmt.samples_per_line); + } + default: return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl); } @@ -3017,10 +3630,14 @@ static ssize_t bttv_read(struct file *file, char __user *data, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (locked_btres(fh->btv,RESOURCE_VIDEO)) + if (!check_alloc_btres(fh->btv, fh, RESOURCE_VIDEO_READ)) { + /* VIDEO_READ in use by another fh, + or VIDEO_STREAM by any fh. */ return -EBUSY; + } retval = videobuf_read_one(&fh->cap, data, count, ppos, file->f_flags & O_NONBLOCK); + free_btres(fh->btv, fh, RESOURCE_VIDEO_READ); break; case V4L2_BUF_TYPE_VBI_CAPTURE: if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI)) @@ -3046,7 +3663,7 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) return videobuf_poll_stream(file, &fh->vbi, wait); } - if (check_btres(fh,RESOURCE_VIDEO)) { + if (check_btres(fh,RESOURCE_VIDEO_STREAM)) { /* streaming capture */ if (list_empty(&fh->cap.stream)) return POLLERR; @@ -3056,7 +3673,7 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) mutex_lock(&fh->cap.lock); if (NULL == fh->cap.read_buf) { /* need to capture a new frame */ - if (locked_btres(fh->btv,RESOURCE_VIDEO)) { + if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM)) { mutex_unlock(&fh->cap.lock); return POLLERR; } @@ -3142,8 +3759,23 @@ static int bttv_open(struct inode *inode, struct file *file) i2c_vidiocschan(btv); btv->users++; - if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) - bttv_vbi_setlines(fh,btv,16); + + /* The V4L2 spec requires one global set of cropping parameters + which only change on request. These are stored in btv->crop[1]. + However for compatibility with V4L apps and cropping unaware + V4L2 apps we now reset the cropping parameters as seen through + this fh, which is to say VIDIOC_G_CROP and scaling limit checks + will use btv->crop[0], the default cropping parameters for the + current video standard, and VIDIOC_S_FMT will not implicitely + change the cropping parameters until VIDIOC_S_CROP has been + called. */ + fh->do_crop = !reset_crop; /* module parameter */ + + /* Likewise there should be one global set of VBI capture + parameters, but for compatibility with V4L apps and earlier + driver versions each fh has its own parameters. */ + bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm); + bttv_field_count(btv); return 0; } @@ -3158,14 +3790,17 @@ static int bttv_release(struct inode *inode, struct file *file) bttv_switch_overlay(btv,fh,NULL); /* stop video capture */ - if (check_btres(fh, RESOURCE_VIDEO)) { + if (check_btres(fh, RESOURCE_VIDEO_STREAM)) { videobuf_streamoff(&fh->cap); - free_btres(btv,fh,RESOURCE_VIDEO); + free_btres(btv,fh,RESOURCE_VIDEO_STREAM); } if (fh->cap.read_buf) { buffer_release(&fh->cap,fh->cap.read_buf); kfree(fh->cap.read_buf); } + if (check_btres(fh, RESOURCE_VIDEO_READ)) { + free_btres(btv, fh, RESOURCE_VIDEO_READ); + } /* stop vbi capture */ if (check_btres(fh, RESOURCE_VBI)) { @@ -4034,7 +4669,6 @@ static int __devinit bttv_probe(struct pci_dev *dev, /* initialize structs / fill in defaults */ mutex_init(&btv->lock); - mutex_init(&btv->reslock); spin_lock_init(&btv->s_lock); spin_lock_init(&btv->gpio_lock); init_waitqueue_head(&btv->gpioq); @@ -4136,7 +4770,6 @@ static int __devinit bttv_probe(struct pci_dev *dev, btv->init.fmt = format_by_palette(VIDEO_PALETTE_RGB24); btv->init.width = 320; btv->init.height = 240; - btv->init.lines = 16; btv->input = 0; /* initialize hardware */ @@ -4171,6 +4804,10 @@ static int __devinit bttv_probe(struct pci_dev *dev, bt848_sat(btv,32768); audio_mute(btv, 1); set_input(btv,0); + bttv_crop_reset(&btv->crop[0], btv->tvnorm); + btv->crop[1] = btv->crop[0]; /* current = default */ + disclaim_vbi_lines(btv); + disclaim_video_lines(btv); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) diff --git a/linux/drivers/media/video/bt8xx/bttv-risc.c b/linux/drivers/media/video/bt8xx/bttv-risc.c index 824e17d2a..a52b2bf28 100644 --- a/linux/drivers/media/video/bt8xx/bttv-risc.c +++ b/linux/drivers/media/video/bt8xx/bttv-risc.c @@ -43,7 +43,8 @@ int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, struct scatterlist *sglist, unsigned int offset, unsigned int bpl, - unsigned int padding, unsigned int lines) + unsigned int padding, unsigned int skip_lines, + unsigned int store_lines) { u32 instructions,line,todo; struct scatterlist *sg; @@ -54,9 +55,11 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, one write per scan line + sync + jump (all 2 dwords). padding can cause next bpl to start close to a page border. First DMA region may be smaller than PAGE_SIZE */ - instructions = 1 + ((bpl + padding) * lines) / PAGE_SIZE + lines; - instructions += 2; - if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0) + instructions = skip_lines * 4; + instructions += (1 + ((bpl + padding) * store_lines) + / PAGE_SIZE + store_lines) * 8; + instructions += 2 * 8; + if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions)) < 0) return rc; /* sync instruction */ @@ -64,11 +67,16 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(rp++) = cpu_to_le32(0); + while (skip_lines-- > 0) { + *(rp++) = cpu_to_le32(BT848_RISC_SKIP | BT848_RISC_SOL | + BT848_RISC_EOL | bpl); + } + /* scan lines */ sg = sglist; - for (line = 0; line < lines; line++) { + for (line = 0; line < store_lines; line++) { if ((btv->opt_vcr_hack) && - (line >= (lines - VCR_HACK_LINES))) + (line >= (store_lines - VCR_HACK_LINES))) continue; while (offset && offset >= sg_dma_len(sg)) { offset -= sg_dma_len(sg); @@ -130,7 +138,8 @@ bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc, /* estimate risc mem: worst case is one write per page border + one write per scan line (5 dwords) plus sync + jump (2 dwords) */ - instructions = (ybpl * ylines * 2) / PAGE_SIZE + ylines; + instructions = ((3 + (ybpl + ypadding) * ylines * 2) + / PAGE_SIZE) + ylines; instructions += 2; if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0) return rc; @@ -317,10 +326,10 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, /* ---------------------------------------------------------- */ static void -bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo, - int width, int height, int interleaved, int norm) +bttv_calc_geo_old(struct bttv *btv, struct bttv_geometry *geo, + int width, int height, int interleaved, + const struct bttv_tvnorm *tvnorm) { - const struct bttv_tvnorm *tvnorm = &bttv_tvnorms[norm]; u32 xsf, sr; int vdelay; @@ -365,6 +374,62 @@ bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo, } static void +bttv_calc_geo (struct bttv * btv, + struct bttv_geometry * geo, + unsigned int width, + unsigned int height, + int both_fields, + const struct bttv_tvnorm * tvnorm, + const struct v4l2_rect * crop) +{ + unsigned int c_width; + unsigned int c_height; + u32 sr; + + if ((crop->left == tvnorm->cropcap.defrect.left + && crop->top == tvnorm->cropcap.defrect.top + && crop->width == tvnorm->cropcap.defrect.width + && crop->height == tvnorm->cropcap.defrect.height + && width <= tvnorm->swidth /* see PAL-Nc et al */) + || bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) { + bttv_calc_geo_old(btv, geo, width, height, + both_fields, tvnorm); + return; + } + + /* For bug compatibility the image size checks permit scale + factors > 16. See bttv_crop_calc_limits(). */ + c_width = min((unsigned int) crop->width, width * 16); + c_height = min((unsigned int) crop->height, height * 16); + + geo->width = width; + geo->hscale = (c_width * 4096U + (width >> 1)) / width - 4096; + /* Even to store Cb first, odd for Cr. */ + geo->hdelay = ((crop->left * width + c_width) / c_width) & ~1; + + geo->sheight = c_height; + geo->vdelay = crop->top - tvnorm->cropcap.bounds.top + MIN_VDELAY; + sr = c_height >> !both_fields; + sr = (sr * 512U + (height >> 1)) / height - 512; + geo->vscale = (0x10000UL - sr) & 0x1fff; + geo->vscale |= both_fields ? (BT848_VSCALE_INT << 8) : 0; + geo->vtotal = tvnorm->vtotal; + + geo->crop = (((geo->width >> 8) & 0x03) | + ((geo->hdelay >> 6) & 0x0c) | + ((geo->sheight >> 4) & 0x30) | + ((geo->vdelay >> 2) & 0xc0)); + + if (btv->opt_combfilter) { + geo->vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0); + geo->comb = (width < 769) ? 1 : 0; + } else { + geo->vtc = 0; + geo->comb = 0; + } +} + +static void bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd) { int off = odd ? 0x80 : 0x00; @@ -526,16 +591,51 @@ int bttv_buffer_activate_vbi(struct bttv *btv, struct bttv_buffer *vbi) { - /* vbi capture */ + struct btcx_riscmem *top; + struct btcx_riscmem *bottom; + int top_irq_flags; + int bottom_irq_flags; + + top = NULL; + bottom = NULL; + top_irq_flags = 0; + bottom_irq_flags = 0; + if (vbi) { + unsigned int crop, vdelay; + vbi->vb.state = STATE_ACTIVE; list_del(&vbi->vb.queue); - bttv_risc_hook(btv, RISC_SLOT_O_VBI, &vbi->top, 0); - bttv_risc_hook(btv, RISC_SLOT_E_VBI, &vbi->bottom, 4); - } else { - bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0); - bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0); + + /* VDELAY is start of video, end of VBI capturing. */ + crop = btread(BT848_E_CROP); + vdelay = btread(BT848_E_VDELAY_LO) + ((crop & 0xc0) << 2); + + if (vbi->geo.vdelay > vdelay) { + vdelay = vbi->geo.vdelay & 0xfe; + crop = (crop & 0x3f) | ((vbi->geo.vdelay >> 2) & 0xc0); + + btwrite(vdelay, BT848_E_VDELAY_LO); + btwrite(crop, BT848_E_CROP); + btwrite(vdelay, BT848_O_VDELAY_LO); + btwrite(crop, BT848_O_CROP); + } + + if (vbi->vbi_count[0] > 0) { + top = &vbi->top; + top_irq_flags = 4; + } + + if (vbi->vbi_count[1] > 0) { + top_irq_flags = 0; + bottom = &vbi->bottom; + bottom_irq_flags = 4; + } } + + bttv_risc_hook(btv, RISC_SLOT_O_VBI, top, top_irq_flags); + bttv_risc_hook(btv, RISC_SLOT_E_VBI, bottom, bottom_irq_flags); + return 0; } @@ -615,28 +715,31 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) int bpf = bpl * (buf->vb.height >> 1); bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height, - V4L2_FIELD_HAS_BOTH(buf->vb.field),buf->tvnorm); + V4L2_FIELD_HAS_BOTH(buf->vb.field), + tvnorm,&buf->crop); switch (buf->vb.field) { case V4L2_FIELD_TOP: bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, - 0,bpl,0,buf->vb.height); + /* offset */ 0,bpl, + /* padding */ 0,/* skip_lines */ 0, + buf->vb.height); break; case V4L2_FIELD_BOTTOM: bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, - 0,bpl,0,buf->vb.height); + 0,bpl,0,0,buf->vb.height); break; case V4L2_FIELD_INTERLACED: bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, - 0,bpl,bpl,buf->vb.height >> 1); + 0,bpl,bpl,0,buf->vb.height >> 1); bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, - bpl,bpl,bpl,buf->vb.height >> 1); + bpl,bpl,bpl,0,buf->vb.height >> 1); break; case V4L2_FIELD_SEQ_TB: bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, - 0,bpl,0,buf->vb.height >> 1); + 0,bpl,0,0,buf->vb.height >> 1); bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, - bpf,bpl,0,buf->vb.height >> 1); + bpf,bpl,0,0,buf->vb.height >> 1); break; default: BUG(); @@ -666,7 +769,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) switch (buf->vb.field) { case V4L2_FIELD_TOP: bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,0,buf->tvnorm); + buf->vb.height,/* both_fields */ 0, + tvnorm,&buf->crop); bttv_risc_planar(btv, &buf->top, buf->vb.dma.sglist, 0,buf->vb.width,0,buf->vb.height, uoffset,voffset,buf->fmt->hshift, @@ -674,7 +778,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) break; case V4L2_FIELD_BOTTOM: bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,0,buf->tvnorm); + buf->vb.height,0, + tvnorm,&buf->crop); bttv_risc_planar(btv, &buf->bottom, buf->vb.dma.sglist, 0,buf->vb.width,0,buf->vb.height, uoffset,voffset,buf->fmt->hshift, @@ -682,7 +787,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) break; case V4L2_FIELD_INTERLACED: bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,1,buf->tvnorm); + buf->vb.height,1, + tvnorm,&buf->crop); lines = buf->vb.height >> 1; ypadding = buf->vb.width; cpadding = buf->vb.width >> buf->fmt->hshift; @@ -704,7 +810,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) break; case V4L2_FIELD_SEQ_TB: bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,1,buf->tvnorm); + buf->vb.height,1, + tvnorm,&buf->crop); lines = buf->vb.height >> 1; ypadding = buf->vb.width; cpadding = buf->vb.width >> buf->fmt->hshift; @@ -735,11 +842,12 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) /* build risc code */ buf->vb.field = V4L2_FIELD_SEQ_TB; bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight, - 1,buf->tvnorm); + 1,tvnorm,&buf->crop); bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist, - 0, RAW_BPL, 0, RAW_LINES); + /* offset */ 0, RAW_BPL, /* padding */ 0, + /* skip_lines */ 0, RAW_LINES); bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist, - buf->vb.size/2 , RAW_BPL, 0, RAW_LINES); + buf->vb.size/2 , RAW_BPL, 0, 0, RAW_LINES); } /* copy format info */ @@ -765,7 +873,8 @@ bttv_overlay_risc(struct bttv *btv, /* calculate geometry */ bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height, - V4L2_FIELD_HAS_BOTH(ov->field), ov->tvnorm); + V4L2_FIELD_HAS_BOTH(ov->field), + &bttv_tvnorms[ov->tvnorm],&buf->crop); /* build risc code */ switch (ov->field) { diff --git a/linux/drivers/media/video/bt8xx/bttv-vbi.c b/linux/drivers/media/video/bt8xx/bttv-vbi.c index 63676e7bd..cac2273f7 100644 --- a/linux/drivers/media/video/bt8xx/bttv-vbi.c +++ b/linux/drivers/media/video/bt8xx/bttv-vbi.c @@ -5,6 +5,9 @@ (c) 2002 Gerd Knorr <kraxel@bytesex.org> + Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at> + Sponsored by OPQ Systems AB + 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 @@ -42,8 +45,15 @@ to be about 244. */ #define VBI_OFFSET 244 +/* 2048 for compatibility with earlier driver versions. The driver + really stores 1024 + tvnorm->vbipack * 4 samples per line in the + buffer. Note tvnorm->vbipack is <= 0xFF (limit of VBIPACK_LO + HI + is 0x1FF DWORDs) and VBI read()s store a frame counter in the last + four bytes of the VBI image. */ +#define VBI_BPL 2048 + +/* Compatibility. */ #define VBI_DEFLINES 16 -#define VBI_MAXLINES 32 static unsigned int vbibufs = 4; static unsigned int vbi_debug = 0; @@ -59,21 +69,12 @@ MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)"); #define dprintk(fmt, arg...) if (vbi_debug) \ printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->c.nr , ## arg) +#define IMAGE_SIZE(fmt) \ + (((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line) + /* ----------------------------------------------------------------------- */ /* vbi risc code + mm */ -static int -vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf, int lines) -{ - int bpl = 2048; - - bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist, - 0, bpl-4, 4, lines); - bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist, - lines * bpl, bpl-4, 4, lines); - return 0; -} - static int vbi_buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { @@ -82,8 +83,16 @@ static int vbi_buffer_setup(struct videobuf_queue *q, if (0 == *count) *count = vbibufs; - *size = fh->lines * 2 * 2048; - dprintk("setup: lines=%d\n",fh->lines); + + *size = IMAGE_SIZE(&fh->vbi_fmt.fmt); + + dprintk("setup: samples=%u start=%d,%d count=%u,%u\n", + fh->vbi_fmt.fmt.samples_per_line, + fh->vbi_fmt.fmt.start[0], + fh->vbi_fmt.fmt.start[1], + fh->vbi_fmt.fmt.count[0], + fh->vbi_fmt.fmt.count[1]); + return 0; } @@ -94,18 +103,93 @@ static int vbi_buffer_prepare(struct videobuf_queue *q, struct bttv_fh *fh = q->priv_data; struct bttv *btv = fh->btv; struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); + const struct bttv_tvnorm *tvnorm; + unsigned int skip_lines0, skip_lines1, min_vdelay; + int redo_dma_risc; int rc; - buf->vb.size = fh->lines * 2 * 2048; + buf->vb.size = IMAGE_SIZE(&fh->vbi_fmt.fmt); if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; + tvnorm = fh->vbi_fmt.tvnorm; + + /* There's no VBI_VDELAY register, RISC must skip the lines + we don't want. With default parameters we skip zero lines + as earlier driver versions did. The driver permits video + standard changes while capturing, so we use vbi_fmt.tvnorm + instead of btv->tvnorm to skip zero lines after video + standard changes as well. */ + + skip_lines0 = 0; + skip_lines1 = 0; + + if (fh->vbi_fmt.fmt.count[0] > 0) + skip_lines0 = max(0, (fh->vbi_fmt.fmt.start[0] + - tvnorm->vbistart[0])); + if (fh->vbi_fmt.fmt.count[1] > 0) + skip_lines1 = max(0, (fh->vbi_fmt.fmt.start[1] + - tvnorm->vbistart[1])); + + redo_dma_risc = 0; + + if (buf->vbi_skip[0] != skip_lines0 || + buf->vbi_skip[1] != skip_lines1 || + buf->vbi_count[0] != fh->vbi_fmt.fmt.count[0] || + buf->vbi_count[1] != fh->vbi_fmt.fmt.count[1]) { + buf->vbi_skip[0] = skip_lines0; + buf->vbi_skip[1] = skip_lines1; + buf->vbi_count[0] = fh->vbi_fmt.fmt.count[0]; + buf->vbi_count[1] = fh->vbi_fmt.fmt.count[1]; + redo_dma_risc = 1; + } + if (STATE_NEEDS_INIT == buf->vb.state) { + redo_dma_risc = 1; if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL))) goto fail; - if (0 != (rc = vbi_buffer_risc(btv,buf,fh->lines))) - goto fail; } + + if (redo_dma_risc) { + unsigned int bpl, padding, offset; + + bpl = 2044; /* max. vbipack */ + padding = VBI_BPL - bpl; + + if (fh->vbi_fmt.fmt.count[0] > 0) { + rc = bttv_risc_packed(btv, &buf->top, + buf->vb.dma.sglist, + /* offset */ 0, bpl, + padding, skip_lines0, + fh->vbi_fmt.fmt.count[0]); + if (0 != rc) + goto fail; + } + + if (fh->vbi_fmt.fmt.count[1] > 0) { + offset = fh->vbi_fmt.fmt.count[0] * VBI_BPL; + + rc = bttv_risc_packed(btv, &buf->bottom, + buf->vb.dma.sglist, + offset, bpl, + padding, skip_lines1, + fh->vbi_fmt.fmt.count[1]); + if (0 != rc) + goto fail; + } + } + + /* VBI capturing ends at VDELAY, start of video capturing, + no matter where the RISC program ends. VDELAY minimum is 2, + bounds.top is the corresponding first field line number + times two. VDELAY counts half field lines. */ + min_vdelay = MIN_VDELAY; + if (fh->vbi_fmt.end >= tvnorm->cropcap.bounds.top) + min_vdelay += fh->vbi_fmt.end - tvnorm->cropcap.bounds.top; + + /* For bttv_buffer_activate_vbi(). */ + buf->geo.vdelay = min_vdelay; + buf->vb.state = STATE_PREPARED; buf->vb.field = field; dprintk("buf prepare %p: top=%p bottom=%p field=%s\n", @@ -153,69 +237,215 @@ struct videobuf_queue_ops bttv_vbi_qops = { /* ----------------------------------------------------------------------- */ -void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines) +static int +try_fmt (struct v4l2_vbi_format * f, + const struct bttv_tvnorm * tvnorm, + __s32 crop_start) { - int vdelay; - - if (lines < 1) - lines = 1; - if (lines > VBI_MAXLINES) - lines = VBI_MAXLINES; - fh->lines = lines; - - vdelay = btread(BT848_E_VDELAY_LO); - if (vdelay < lines*2) { - vdelay = lines*2; - btwrite(vdelay,BT848_E_VDELAY_LO); - btwrite(vdelay,BT848_O_VDELAY_LO); + __s32 min_start, max_start, max_end, f2_offset; + unsigned int i; + + /* For compatibility with earlier driver versions we must pretend + the VBI and video capture window may overlap. In reality RISC + magic aborts VBI capturing at the first line of video capturing, + leaving the rest of the buffer unchanged, usually all zero. + VBI capturing must always start before video capturing. >> 1 + because cropping counts field lines times two. */ + min_start = tvnorm->vbistart[0]; + max_start = (crop_start >> 1) - 1; + max_end = (tvnorm->cropcap.bounds.top + + tvnorm->cropcap.bounds.height) >> 1; + + if (min_start > max_start) + return -EBUSY; + + BUG_ON(max_start >= max_end); + + f->sampling_rate = tvnorm->Fsc; + f->samples_per_line = VBI_BPL; + f->sample_format = V4L2_PIX_FMT_GREY; + f->offset = VBI_OFFSET; + + f2_offset = tvnorm->vbistart[1] - tvnorm->vbistart[0]; + + for (i = 0; i < 2; ++i) { + if (0 == f->count[i]) { + /* No data from this field. We leave f->start[i] + alone because VIDIOCSVBIFMT is w/o and EINVALs + when a driver does not support exactly the + requested parameters. */ + } else { + s64 start, count; + + start = clamp(f->start[i], min_start, max_start); + /* s64 to prevent overflow. */ + count = (s64) f->start[i] + f->count[i] - start; + f->start[i] = start; + f->count[i] = clamp(count, (s64) 1, + max_end - start); + } + + min_start += f2_offset; + max_start += f2_offset; + max_end += f2_offset; } + + if (0 == (f->count[0] | f->count[1])) { + /* As in earlier driver versions. */ + f->start[0] = tvnorm->vbistart[0]; + f->start[1] = tvnorm->vbistart[1]; + f->count[0] = 1; + f->count[1] = 1; + } + + f->flags = 0; + + f->reserved[0] = 0; + f->reserved[1] = 0; + + return 0; } -void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f) +int +bttv_vbi_try_fmt (struct bttv_fh * fh, + struct v4l2_vbi_format * f) { + struct bttv *btv = fh->btv; const struct bttv_tvnorm *tvnorm; - s64 count0,count1,count; + __s32 crop_start; - tvnorm = &bttv_tvnorms[fh->btv->tvnorm]; - f->type = V4L2_BUF_TYPE_VBI_CAPTURE; - f->fmt.vbi.sampling_rate = tvnorm->Fsc; - f->fmt.vbi.samples_per_line = 2048; - f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = VBI_OFFSET; - f->fmt.vbi.flags = 0; - - /* s64 to prevent overflow. */ - count0 = (s64) f->fmt.vbi.start[0] + f->fmt.vbi.count[0] - - tvnorm->vbistart[0]; - count1 = (s64) f->fmt.vbi.start[1] + f->fmt.vbi.count[1] - - tvnorm->vbistart[1]; - count = clamp (max (count0, count1), (s64) 1, (s64) VBI_MAXLINES); - - f->fmt.vbi.start[0] = tvnorm->vbistart[0]; - f->fmt.vbi.start[1] = tvnorm->vbistart[1]; - f->fmt.vbi.count[0] = count; - f->fmt.vbi.count[1] = count; - - f->fmt.vbi.reserved[0] = 0; - f->fmt.vbi.reserved[1] = 0; + mutex_lock(&btv->lock); + + tvnorm = &bttv_tvnorms[btv->tvnorm]; + crop_start = btv->crop_start; + + mutex_unlock(&btv->lock); + + return try_fmt(f, tvnorm, crop_start); } -void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f) +int +bttv_vbi_set_fmt (struct bttv_fh * fh, + struct v4l2_vbi_format * f) { + struct bttv *btv = fh->btv; const struct bttv_tvnorm *tvnorm; + __s32 start1, end; + int rc; + + mutex_lock(&btv->lock); + + rc = -EBUSY; + if (fh->resources & RESOURCE_VBI) + goto fail; + + tvnorm = &bttv_tvnorms[btv->tvnorm]; + + rc = try_fmt(f, tvnorm, btv->crop_start); + if (0 != rc) + goto fail; + + start1 = f->start[1] - tvnorm->vbistart[1] + tvnorm->vbistart[0]; + + /* First possible line of video capturing. Should be + max(f->start[0] + f->count[0], start1 + f->count[1]) * 2 + when capturing both fields. But for compatibility we must + pretend the VBI and video capture window may overlap, + so end = start + 1, the lowest possible value, times two + because vbi_fmt.end counts field lines times two. */ + end = max(f->start[0], start1) * 2 + 2; + + mutex_lock(&fh->vbi.lock); + + fh->vbi_fmt.fmt = *f; + fh->vbi_fmt.tvnorm = tvnorm; + fh->vbi_fmt.end = end; + + mutex_unlock(&fh->vbi.lock); + + rc = 0; + + fail: + mutex_unlock(&btv->lock); + + return rc; +} + +void +bttv_vbi_get_fmt (struct bttv_fh * fh, + struct v4l2_vbi_format * f) +{ + const struct bttv_tvnorm *tvnorm; + + *f = fh->vbi_fmt.fmt; tvnorm = &bttv_tvnorms[fh->btv->tvnorm]; - memset(f,0,sizeof(*f)); - f->type = V4L2_BUF_TYPE_VBI_CAPTURE; - f->fmt.vbi.sampling_rate = tvnorm->Fsc; - f->fmt.vbi.samples_per_line = 2048; - f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = VBI_OFFSET; - f->fmt.vbi.start[0] = tvnorm->vbistart[0]; - f->fmt.vbi.start[1] = tvnorm->vbistart[1]; - f->fmt.vbi.count[0] = fh->lines; - f->fmt.vbi.count[1] = fh->lines; - f->fmt.vbi.flags = 0; + + if (tvnorm != fh->vbi_fmt.tvnorm) { + __s32 max_end; + unsigned int i; + + /* As in vbi_buffer_prepare() this imitates the + behaviour of earlier driver versions after video + standard changes, with default parameters anyway. */ + + max_end = (tvnorm->cropcap.bounds.top + + tvnorm->cropcap.bounds.height) >> 1; + + f->sampling_rate = tvnorm->Fsc; + + for (i = 0; i < 2; ++i) { + __s32 new_start; + + new_start = f->start[i] + + tvnorm->vbistart[i] + - fh->vbi_fmt.tvnorm->vbistart[i]; + + f->start[i] = min(new_start, max_end - 1); + f->count[i] = min((__s32) f->count[i], + max_end - f->start[i]); + + max_end += tvnorm->vbistart[1] + - tvnorm->vbistart[0]; + } + } +} + +void +bttv_vbi_fmt_reset (struct bttv_vbi_fmt * f, + int norm) +{ + const struct bttv_tvnorm *tvnorm; + unsigned int real_samples_per_line; + unsigned int real_count; + + tvnorm = &bttv_tvnorms[norm]; + + f->fmt.sampling_rate = tvnorm->Fsc; + f->fmt.samples_per_line = VBI_BPL; + f->fmt.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.offset = VBI_OFFSET; + f->fmt.start[0] = tvnorm->vbistart[0]; + f->fmt.start[1] = tvnorm->vbistart[1]; + f->fmt.count[0] = VBI_DEFLINES; + f->fmt.count[1] = VBI_DEFLINES; + f->fmt.flags = 0; + f->fmt.reserved[0] = 0; + f->fmt.reserved[1] = 0; + + /* For compatibility the buffer size must be 2 * VBI_DEFLINES * + VBI_BPL regardless of the current video standard. */ + real_samples_per_line = 1024 + tvnorm->vbipack * 4; + real_count = ((tvnorm->cropcap.defrect.top >> 1) + - tvnorm->vbistart[0]); + + BUG_ON(real_samples_per_line > VBI_BPL); + BUG_ON(real_count > VBI_DEFLINES); + + f->tvnorm = tvnorm; + + /* See bttv_vbi_fmt_set(). */ + f->end = tvnorm->vbistart[0] * 2 + 2; } /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/bt8xx/bttvp.h b/linux/drivers/media/video/bt8xx/bttvp.h index 70f9f879c..a273c424b 100644 --- a/linux/drivers/media/video/bt8xx/bttvp.h +++ b/linux/drivers/media/video/bt8xx/bttvp.h @@ -27,7 +27,7 @@ #include "compat.h" #include <linux/version.h> -#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,16) +#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,17) #include <linux/types.h> #include <linux/wait.h> @@ -75,14 +75,22 @@ #define RISC_SLOT_LOOP 14 #define RESOURCE_OVERLAY 1 -#define RESOURCE_VIDEO 2 +#define RESOURCE_VIDEO_STREAM 2 #define RESOURCE_VBI 4 +#define RESOURCE_VIDEO_READ 8 #define RAW_LINES 640 #define RAW_BPL 1024 #define UNSET (-1U) +/* Min. value in VDELAY register. */ +#define MIN_VDELAY 2 +/* Even to get Cb first, odd for Cr. */ +#define MAX_HDELAY (0x3FF & -2) +/* Limits scaled width, which must be a multiple of 4. */ +#define MAX_HACTIVE (0x3FF & -4) + #define clamp(x, low, high) min (max (low, x), high) /* ---------------------------------------------------------- */ @@ -101,8 +109,13 @@ struct bttv_tvnorm { u16 vtotal; int sram; /* ITU-R frame line number of the first VBI line we can - capture, of the first and second field. */ + capture, of the first and second field. The last possible line + is determined by cropcap.bounds. */ u16 vbistart[2]; + /* Horizontally this counts fCLKx1 samples following the leading + edge of the horizontal sync pulse, vertically ITU-R frame line + numbers of the first field times two (2, 4, 6, ... 524 or 624). */ + struct v4l2_cropcap cropcap; }; extern const struct bttv_tvnorm bttv_tvnorms[]; @@ -137,6 +150,9 @@ struct bttv_buffer { struct bttv_geometry geo; struct btcx_riscmem top; struct btcx_riscmem bottom; + struct v4l2_rect crop; + unsigned int vbi_skip[2]; + unsigned int vbi_count[2]; }; struct bttv_buffer_set { @@ -155,6 +171,34 @@ struct bttv_overlay { int setup_ok; }; +struct bttv_vbi_fmt { + struct v4l2_vbi_format fmt; + + /* fmt.start[] and count[] refer to this video standard. */ + const struct bttv_tvnorm *tvnorm; + + /* Earliest possible start of video capturing with this + v4l2_vbi_format, in struct bttv_crop.rect units. */ + __s32 end; +}; + +/* bttv-vbi.c */ +void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, int norm); + +struct bttv_crop { + /* A cropping rectangle in struct bttv_tvnorm.cropcap units. */ + struct v4l2_rect rect; + + /* Scaled image size limits with this crop rect. Divide + max_height, but not min_height, by two when capturing + single fields. See also bttv_crop_reset() and + bttv_crop_adjust() in bttv-driver.c. */ + __s32 min_scaled_width; + __s32 min_scaled_height; + __s32 max_scaled_width; + __s32 max_scaled_height; +}; + struct bttv_fh { struct bttv *btv; int resources; @@ -169,13 +213,19 @@ struct bttv_fh { int width; int height; - /* current settings */ + /* video overlay */ const struct bttv_format *ovfmt; struct bttv_overlay ov; - /* video overlay */ + /* Application called VIDIOC_S_CROP. */ + int do_crop; + + /* vbi capture */ struct videobuf_queue vbi; - int lines; + /* Current VBI capture window as seen through this fh (cannot + be global for compatibility with earlier drivers). Protected + by struct bttv.lock and struct bttv_fh.vbi.lock. */ + struct bttv_vbi_fmt vbi_fmt; }; /* ---------------------------------------------------------- */ @@ -185,7 +235,8 @@ struct bttv_fh { int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, struct scatterlist *sglist, unsigned int offset, unsigned int bpl, - unsigned int pitch, unsigned int lines); + unsigned int pitch, unsigned int skip_lines, + unsigned int store_lines); /* control dma register + risc main loop */ void bttv_set_dma(struct bttv *btv, int override); @@ -211,9 +262,9 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov, /* ---------------------------------------------------------- */ /* bttv-vbi.c */ -void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f); -void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f); -void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines); +int bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f); +void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f); +int bttv_vbi_set_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f); extern struct videobuf_queue_ops bttv_vbi_qops; @@ -244,7 +295,6 @@ extern int fini_bttv_i2c(struct bttv *btv); #define d2printk if (bttv_debug >= 2) printk #define BTTV_MAX_FBUF 0x208000 -#define VBIBUF_SIZE (2048*VBI_MAXLINES*2) #define BTTV_TIMEOUT (HZ/2) /* 0.5 seconds */ #define BTTV_FREE_IDLE (HZ) /* one second */ @@ -332,11 +382,6 @@ struct bttv { struct mutex lock; #endif int resources; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) - struct semaphore reslock; -#else - struct mutex reslock; -#endif #ifdef VIDIOC_G_PRIORITY struct v4l2_prio_state prio; #endif @@ -406,6 +451,21 @@ struct bttv { unsigned int users; struct bttv_fh init; + + /* Default (0) and current (1) video capturing and overlay + cropping parameters in bttv_tvnorm.cropcap units. Protected + by bttv.lock. */ + struct bttv_crop crop[2]; + + /* Earliest possible start of video capturing in + bttv_tvnorm.cropcap line units. Set by check_alloc_btres() + and free_btres(). Protected by bttv.lock. */ + __s32 vbi_end; + + /* Latest possible end of VBI capturing (= crop[x].rect.top when + VIDEO_RESOURCES are locked). Set by check_alloc_btres() + and free_btres(). Protected by bttv.lock. */ + __s32 crop_start; }; /* our devices */ diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c index 9214c15c4..2494bb844 100644 --- a/linux/drivers/media/video/cpia.c +++ b/linux/drivers/media/video/cpia.c @@ -1353,13 +1353,13 @@ out: static void create_proc_cpia_cam(struct cam_data *cam) { - char name[7]; + char name[5 + 1 + 10 + 1]; struct proc_dir_entry *ent; if (!cpia_proc_root || !cam) return; - sprintf(name, "video%d", cam->vdev.minor); + snprintf(name, sizeof(name), "video%d", cam->vdev.minor); ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root); if (!ent) @@ -1379,12 +1379,12 @@ static void create_proc_cpia_cam(struct cam_data *cam) static void destroy_proc_cpia_cam(struct cam_data *cam) { - char name[7]; + char name[5 + 1 + 10 + 1]; if (!cam || !cam->proc_entry) return; - sprintf(name, "video%d", cam->vdev.minor); + snprintf(name, sizeof(name), "video%d", cam->vdev.minor); remove_proc_entry(name, cpia_proc_root); cam->proc_entry = NULL; } diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 896beb7ed..3bb0a80de 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -6,6 +6,9 @@ * (c) 2004 Jelle Foks <jelle@foks.8m.com> * (c) 2004 Gerd Knorr <kraxel@bytesex.org> * + * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> + * - video_ioctl2 conversion + * * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), * * This program is free software; you can redistribute it and/or modify @@ -531,7 +534,7 @@ static void blackbird_codec_settings(struct cx8802_dev *dev) dev->params.width = dev->width; dev->params.height = dev->height; - dev->params.is_50hz = (dev->core->tvnorm->id & V4L2_STD_625_50) != 0; + dev->params.is_50hz = (dev->core->tvnorm & V4L2_STD_625_50) != 0; cx2341x_update(dev, blackbird_mbox_func, NULL, &dev->params); } @@ -729,8 +732,13 @@ static int blackbird_queryctrl(struct cx8802_dev *dev, struct v4l2_queryctrl *qc return 0; } -static int blackbird_querymenu(struct cx8802_dev *dev, struct v4l2_querymenu *qmenu) +/* ------------------------------------------------------------------ */ +/* IOCTL Handlers */ + +static int vidioc_querymenu (struct file *file, void *priv, + struct v4l2_querymenu *qmenu) { + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; struct v4l2_queryctrl qctrl; qctrl.id = qmenu->id; @@ -738,235 +746,355 @@ static int blackbird_querymenu(struct cx8802_dev *dev, struct v4l2_querymenu *qm return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id)); } -/* ------------------------------------------------------------------ */ - -static int mpeg_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap (struct file *file, void *priv, + struct v4l2_capability *cap) { - struct cx8802_fh *fh = file->private_data; - struct cx8802_dev *dev = fh->dev; + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; struct cx88_core *core = dev->core; - if (debug > 1) - v4l_print_ioctl(core->name,cmd); - - switch (cmd) { - - /* --- capabilities ------------------------------------------ */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->driver, "cx88_blackbird"); - strlcpy(cap->card, cx88_boards[core->board].name,sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); - cap->version = CX88_VERSION_CODE; - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | + strcpy(cap->driver, "cx88_blackbird"); + strlcpy(cap->card, cx88_boards[core->board].name,sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); + cap->version = CX88_VERSION_CODE; + cap->capabilities = #if 0 - V4L2_CAP_VBI_CAPTURE | - V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_VIDEO_OVERLAY | #endif - 0; - if (UNSET != core->tuner_type) - cap->capabilities |= V4L2_CAP_TUNER; + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + if (UNSET != core->tuner_type) + cap->capabilities |= V4L2_CAP_TUNER; + return 0; +} - return 0; - } +static int vidioc_enum_fmt_cap (struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; - /* --- capture ioctls ---------------------------------------- */ - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *f = arg; - int index; - - index = f->index; - if (index != 0) - return -EINVAL; - - memset(f,0,sizeof(*f)); - f->index = index; - strlcpy(f->description, "MPEG", sizeof(f->description)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->pixelformat = V4L2_PIX_FMT_MPEG; - return 0; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *f = arg; - - memset(f,0,sizeof(*f)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */ - f->fmt.pix.colorspace = 0; - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.field = fh->mpegq.field; - dprintk(0,"VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", - dev->width, dev->height, fh->mpegq.field ); - return 0; - } - case VIDIOC_TRY_FMT: - { - struct v4l2_format *f = arg; - - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; - f->fmt.pix.colorspace = 0; - dprintk(0,"VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", - dev->width, dev->height, fh->mpegq.field ); - return 0; - } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = arg; - - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; - f->fmt.pix.colorspace = 0; -#if 0 - dev->width = f->fmt.pix.width; - dev->height = f->fmt.pix.height; - fh->mpegq.field = f->fmt.pix.field; + strlcpy(f->description, "MPEG", sizeof(f->description)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->pixelformat = V4L2_PIX_FMT_MPEG; + return 0; +} + +static int vidioc_g_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx8802_fh *fh = priv; + struct cx8802_dev *dev = fh->dev; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */ + f->fmt.pix.colorspace = 0; + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = fh->mpegq.field; + dprintk(0,"VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", + dev->width, dev->height, fh->mpegq.field ); + return 0; +} + +static int vidioc_try_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx8802_fh *fh = priv; + struct cx8802_dev *dev = fh->dev; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; + f->fmt.pix.colorspace = 0; + dprintk(0,"VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", + dev->width, dev->height, fh->mpegq.field ); + return 0; +} + +static int vidioc_s_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx8802_fh *fh = priv; + struct cx8802_dev *dev = fh->dev; + struct cx88_core *core = dev->core; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; + f->fmt.pix.colorspace = 0; +#if 1 + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + fh->mpegq.field = f->fmt.pix.field; #endif -#if 0 - cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); - blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, - f->fmt.pix.height, f->fmt.pix.width); +#if 1 + cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); + blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, + f->fmt.pix.height, f->fmt.pix.width); #endif - dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", - f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field ); - return 0; - } + dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field ); + return 0; +} - /* --- streaming capture ------------------------------------- */ - case VIDIOC_REQBUFS: - return videobuf_reqbufs(&fh->mpegq, arg); +static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p) +{ + struct cx8802_fh *fh = priv; + return (videobuf_reqbufs(&fh->mpegq, p)); +} - case VIDIOC_QUERYBUF: - return videobuf_querybuf(&fh->mpegq, arg); +static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8802_fh *fh = priv; + return (videobuf_querybuf(&fh->mpegq, p)); +} - case VIDIOC_QBUF: - return videobuf_qbuf(&fh->mpegq, arg); +static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8802_fh *fh = priv; + return (videobuf_qbuf(&fh->mpegq, p)); +} - case VIDIOC_DQBUF: - return videobuf_dqbuf(&fh->mpegq, arg, - file->f_flags & O_NONBLOCK); +static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8802_fh *fh = priv; + return (videobuf_dqbuf(&fh->mpegq, p, + file->f_flags & O_NONBLOCK)); +} - case VIDIOC_STREAMON: - return videobuf_streamon(&fh->mpegq); +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx8802_fh *fh = priv; + return videobuf_streamon(&fh->mpegq); +} - case VIDIOC_STREAMOFF: - return videobuf_streamoff(&fh->mpegq); +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx8802_fh *fh = priv; + return videobuf_streamoff(&fh->mpegq); +} - /* --- mpeg compression -------------------------------------- */ - case VIDIOC_G_MPEGCOMP: - { - struct v4l2_mpeg_compression *f = arg; +static int vidioc_g_mpegcomp (struct file *file, void *fh, + struct v4l2_mpeg_compression *f) +{ + printk(KERN_WARNING "VIDIOC_G_MPEGCOMP is obsolete. " + "Replace with VIDIOC_G_EXT_CTRLS!"); + memcpy(f,&default_mpeg_params,sizeof(*f)); + return 0; +} - printk(KERN_WARNING "VIDIOC_G_MPEGCOMP is obsolete. " - "Replace with VIDIOC_G_EXT_CTRLS!"); - memcpy(f,&default_mpeg_params,sizeof(*f)); - return 0; - } - case VIDIOC_S_MPEGCOMP: - printk(KERN_WARNING "VIDIOC_S_MPEGCOMP is obsolete. " - "Replace with VIDIOC_S_EXT_CTRLS!"); - return 0; - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *f = arg; +static int vidioc_s_mpegcomp (struct file *file, void *fh, + struct v4l2_mpeg_compression *f) +{ + printk(KERN_WARNING "VIDIOC_S_MPEGCOMP is obsolete. " + "Replace with VIDIOC_S_EXT_CTRLS!"); + return 0; +} - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - return cx2341x_ext_ctrls(&dev->params, f, cmd); - } - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *f = arg; - struct cx2341x_mpeg_params p; - int err; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - p = dev->params; - err = cx2341x_ext_ctrls(&p, f, cmd); - if (err == 0 && cmd == VIDIOC_S_EXT_CTRLS) { - err = cx2341x_update(dev, blackbird_mbox_func, &dev->params, &p); - dev->params = p; - } - return err; - } - case VIDIOC_S_FREQUENCY: - { - blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, - BLACKBIRD_END_NOW, - BLACKBIRD_MPEG_CAPTURE, - BLACKBIRD_RAW_BITS_NONE); - - cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); - - blackbird_initialize_codec(dev); - cx88_set_scale(dev->core, dev->width, dev->height, - fh->mpegq.field); - return 0; +static int vidioc_g_ext_ctrls (struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + return cx2341x_ext_ctrls(&dev->params, f, VIDIOC_G_EXT_CTRLS); +} + +static int vidioc_s_ext_ctrls (struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + struct cx2341x_mpeg_params p; + int err; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + p = dev->params; + err = cx2341x_ext_ctrls(&p, f, VIDIOC_S_EXT_CTRLS); + if (!err) { + err = cx2341x_update(dev, blackbird_mbox_func, &dev->params, &p); + dev->params = p; } - case VIDIOC_LOG_STATUS: - { - char name[32 + 2]; - - snprintf(name, sizeof(name), "%s/2", core->name); - printk("%s/2: ============ START LOG STATUS ============\n", - core->name); - cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, NULL); - cx2341x_log_status(&dev->params, name); - printk("%s/2: ============= END LOG STATUS =============\n", - core->name); + return err; +} + +static int vidioc_try_ext_ctrls (struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + struct cx2341x_mpeg_params p; + int err; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + p = dev->params; + err = cx2341x_ext_ctrls(&p, f, VIDIOC_TRY_EXT_CTRLS); + + return err; +} + +static int vidioc_s_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx8802_fh *fh = priv; + struct cx8802_dev *dev = fh->dev; + struct cx88_core *core = dev->core; + + blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + BLACKBIRD_END_NOW, + BLACKBIRD_MPEG_CAPTURE, + BLACKBIRD_RAW_BITS_NONE); + cx88_set_freq (core,f); + blackbird_initialize_codec(dev); + cx88_set_scale(dev->core, dev->width, dev->height, + fh->mpegq.field); + return 0; +} + +static int vidioc_log_status (struct file *file, void *priv) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + struct cx88_core *core = dev->core; + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", core->name); + printk("%s/2: ============ START LOG STATUS ============\n", + core->name); + cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, NULL); + cx2341x_log_status(&dev->params, name); + printk("%s/2: ============= END LOG STATUS =============\n", + core->name); + return 0; +} + +static int vidioc_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + + if (blackbird_queryctrl(dev, qctrl) == 0) return 0; - } - case VIDIOC_QUERYMENU: - return blackbird_querymenu(dev, arg); - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *c = arg; - if (blackbird_queryctrl(dev, c) == 0) - return 0; - return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); - } + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (unlikely(qctrl->id == 0)) + return -EINVAL; + return cx8800_ctrl_query(qctrl); +} - default: - return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); - } +static int vidioc_enum_input (struct file *file, void *priv, + struct v4l2_input *i) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + return cx88_enum_input (core,i); +} + +static int vidioc_g_ctrl (struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + return + cx88_get_control(core,ctl); +} + +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + return + cx88_set_control(core,ctl); +} + +static int vidioc_g_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx8802_fh *fh = priv; + struct cx88_core *core = fh->dev->core; + + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; + + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = core->freq; + cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); + + return 0; +} + +static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + + *i = core->input; + return 0; +} + +static int vidioc_s_input (struct file *file, void *priv, unsigned int i) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + + if (i >= 4) + return -EINVAL; + + mutex_lock(&core->lock); + cx88_newstation(core); + cx88_video_mux(core,i); + mutex_unlock(&core->lock); return 0; } -int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg); -unsigned int (*cx88_ioctl_translator)(unsigned int cmd); +static int vidioc_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + u32 reg; + + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; + + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; -static unsigned int mpeg_translate_ioctl(unsigned int cmd) + cx88_get_stereo(core ,t); + reg = cx_read(MO_DEVICE_STATUS); + t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; + return 0; +} + +static int vidioc_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) { - return cmd; + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + + if (UNSET == core->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + cx88_set_stereo(core, t->audmode, 1); + return 0; } -static int mpeg_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) { - cmd = cx88_ioctl_translator( cmd ); - return video_usercopy(inode, file, cmd, arg, cx88_ioctl_hook); + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + + mutex_lock(&core->lock); + cx88_set_tvnorm(core,*id); + mutex_unlock(&core->lock); + return 0; } +/* FIXME: cx88_ioctl_hook not implemented */ + static int mpeg_open(struct inode *inode, struct file *file) { int minor = iminor(inode); @@ -1092,17 +1220,47 @@ static struct file_operations mpeg_fops = .read = mpeg_read, .poll = mpeg_poll, .mmap = mpeg_mmap, - .ioctl = mpeg_ioctl, + .ioctl = video_ioctl2, .llseek = no_llseek, }; static struct video_device cx8802_mpeg_template = { - .name = "cx8802", - .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES|VID_TYPE_MPEG_ENCODER, - .hardware = 0, - .fops = &mpeg_fops, - .minor = -1, + .name = "cx8802", + .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES|VID_TYPE_MPEG_ENCODER, + .fops = &mpeg_fops, + .minor = -1, + .vidioc_querymenu = vidioc_querymenu, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, + .vidioc_g_fmt_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_mpegcomp = vidioc_g_mpegcomp, + .vidioc_s_mpegcomp = vidioc_s_mpegcomp, + .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, + .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, + .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = vidioc_log_status, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_s_std = vidioc_s_std, + .tvnorms = CX88_NORMS, + .current_norm = V4L2_STD_NTSC_M, }; /* ------------------------------------------------------------------ */ @@ -1200,7 +1358,9 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) cx2341x_fill_defaults(&dev->params); dev->params.port = CX2341X_PORT_STREAMING; - if (core->tvnorm->id & V4L2_STD_525_60) { + cx8802_mpeg_template.current_norm = core->tvnorm; + + if (core->tvnorm & V4L2_STD_525_60) { dev->height = 480; } else { dev->height = 576; @@ -1214,25 +1374,12 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) blackbird_register_video(dev); /* initial device configuration: needed ? */ -#if 0 - down(&dev->lock); - init_controls(core); -/* #if 1 */ - /* force norm to PAL */ - { - int i; - for (i = 0; i < TVNORMS; i++) - if (tvnorms[i].id == V4L2_STD_PAL_BG) - { - cx88_set_tvnorm(core,&tvnorms[i]); - break; - } - } -/* #else */ - cx88_set_tvnorm(core,tvnorms); -/* #endif */ - video_mux(core,0); - up(&dev->lock); +#if 1 + mutex_lock(&dev->core->lock); +// init_controls(core); + cx88_set_tvnorm(core,core->tvnorm); + cx88_video_mux(core,0); + mutex_unlock(&dev->core->lock); #endif return 0; @@ -1268,8 +1415,6 @@ static int blackbird_init(void) printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif - cx88_ioctl_hook = mpeg_do_ioctl; - cx88_ioctl_translator = mpeg_translate_ioctl; return cx8802_register_driver(&cx8802_blackbird_driver); } @@ -1281,8 +1426,8 @@ static void blackbird_fini(void) module_init(blackbird_init); module_exit(blackbird_fini); -EXPORT_SYMBOL(cx88_ioctl_hook); -EXPORT_SYMBOL(cx88_ioctl_translator); +module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644); +MODULE_PARM_DESC(debug,"enable debug messages [video]"); /* ----------------------------------------------------------- */ /* diff --git a/linux/drivers/media/video/cx88/cx88-core.c b/linux/drivers/media/video/cx88/cx88-core.c index fc8dbebbc..367dc3c7b 100644 --- a/linux/drivers/media/video/cx88/cx88-core.c +++ b/linux/drivers/media/video/cx88/cx88-core.c @@ -5,6 +5,11 @@ * * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * + * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> + * - Multituner support + * - video_ioctl2 conversion + * - PAL/M fixes + * * 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 @@ -671,30 +676,30 @@ int cx88_reset(struct cx88_core *core) /* ------------------------------------------------------------------ */ -static unsigned int inline norm_swidth(struct cx88_tvnorm *norm) +static unsigned int inline norm_swidth(v4l2_std_id norm) { - return (norm->id & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; + return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; } -static unsigned int inline norm_hdelay(struct cx88_tvnorm *norm) +static unsigned int inline norm_hdelay(v4l2_std_id norm) { - return (norm->id & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186; + return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186; } -static unsigned int inline norm_vdelay(struct cx88_tvnorm *norm) +static unsigned int inline norm_vdelay(v4l2_std_id norm) { - return (norm->id & V4L2_STD_625_50) ? 0x24 : 0x18; + return (norm & V4L2_STD_625_50) ? 0x24 : 0x18; } -static unsigned int inline norm_fsc8(struct cx88_tvnorm *norm) +static unsigned int inline norm_fsc8(v4l2_std_id norm) { - if (norm->id & V4L2_STD_PAL_M) + if (norm & V4L2_STD_PAL_M) return 28604892; // 3.575611 MHz - if (norm->id & (V4L2_STD_PAL_Nc)) + if (norm & (V4L2_STD_PAL_Nc)) return 28656448; // 3.582056 MHz - if (norm->id & V4L2_STD_NTSC) // All NTSC/M and variants + if (norm & V4L2_STD_NTSC) // All NTSC/M and variants return 28636360; // 3.57954545 MHz +/- 10 Hz /* SECAM have also different sub carrier for chroma, @@ -706,20 +711,20 @@ static unsigned int inline norm_fsc8(struct cx88_tvnorm *norm) return 35468950; // 4.43361875 MHz +/- 5 Hz } -static unsigned int inline norm_htotal(struct cx88_tvnorm *norm) +static unsigned int inline norm_htotal(v4l2_std_id norm) { unsigned int fsc4=norm_fsc8(norm)/2; /* returns 4*FSC / vtotal / frames per seconds */ - return (norm->id & V4L2_STD_625_50) ? + return (norm & V4L2_STD_625_50) ? ((fsc4+312)/625+12)/25 : ((fsc4+262)/525*1001+15000)/30000; } -static unsigned int inline norm_vbipack(struct cx88_tvnorm *norm) +static unsigned int inline norm_vbipack(v4l2_std_id norm) { - return (norm->id & V4L2_STD_625_50) ? 511 : 400; + return (norm & V4L2_STD_625_50) ? 511 : 400; } int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height, @@ -732,7 +737,7 @@ int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int heig dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height, V4L2_FIELD_HAS_TOP(field) ? "T" : "", V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "", - core->tvnorm->name); + v4l2_norm_to_name(core->tvnorm)); if (!V4L2_FIELD_HAS_BOTH(field)) height *= 2; @@ -769,7 +774,7 @@ int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int heig // setup filters value = 0; value |= (1 << 19); // CFILT (default) - if (core->tvnorm->id & V4L2_STD_SECAM) { + if (core->tvnorm & V4L2_STD_SECAM) { value |= (1 << 15); value |= (1 << 16); } @@ -866,36 +871,36 @@ int cx88_stop_audio_dma(struct cx88_core *core) static int set_tvaudio(struct cx88_core *core) { - struct cx88_tvnorm *norm = core->tvnorm; + v4l2_std_id norm = core->tvnorm; if (CX88_VMUX_TELEVISION != INPUT(core->input)->type) return 0; - if (V4L2_STD_PAL_BG & norm->id) { + if (V4L2_STD_PAL_BG & norm) { core->tvaudio = WW_BG; - } else if (V4L2_STD_PAL_DK & norm->id) { + } else if (V4L2_STD_PAL_DK & norm) { core->tvaudio = WW_DK; - } else if (V4L2_STD_PAL_I & norm->id) { + } else if (V4L2_STD_PAL_I & norm) { core->tvaudio = WW_I; - } else if (V4L2_STD_SECAM_L & norm->id) { + } else if (V4L2_STD_SECAM_L & norm) { core->tvaudio = WW_L; - } else if (V4L2_STD_SECAM_DK & norm->id) { + } else if (V4L2_STD_SECAM_DK & norm) { core->tvaudio = WW_DK; - } else if ((V4L2_STD_NTSC_M & norm->id) || - (V4L2_STD_PAL_M & norm->id)) { + } else if ((V4L2_STD_NTSC_M & norm) || + (V4L2_STD_PAL_M & norm)) { core->tvaudio = WW_BTSC; - } else if (V4L2_STD_NTSC_M_JP & norm->id) { + } else if (V4L2_STD_NTSC_M_JP & norm) { core->tvaudio = WW_EIAJ; } else { printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n", - core->name, norm->name); + core->name, v4l2_norm_to_name(core->tvnorm)); core->tvaudio = 0; return 0; } @@ -916,7 +921,7 @@ static int set_tvaudio(struct cx88_core *core) -int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) +int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) { u32 fsc8; u32 adc_clock; @@ -924,6 +929,7 @@ int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) u32 step_db,step_dr; u64 tmp64; u32 bdelay,agcdelay,htotal; + u32 cxiformat, cxoformat; core->tvnorm = norm; fsc8 = norm_fsc8(norm); @@ -932,24 +938,52 @@ int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) step_db = fsc8; step_dr = fsc8; - if (norm->id & V4L2_STD_SECAM) { + if (norm & V4L2_STD_NTSC_M_JP) { + cxiformat = VideoFormatNTSCJapan; + cxoformat = 0x181f0008; + } else if (norm & V4L2_STD_NTSC_443) { + cxiformat = VideoFormatNTSC443; + cxoformat = 0x181f0008; + } else if (norm & V4L2_STD_PAL_M) { + cxiformat = VideoFormatPALM; + cxoformat = 0x1c1f0008; + } else if (norm & V4L2_STD_PAL_N) { + cxiformat = VideoFormatPALN; + cxoformat = 0x1c1f0008; + } else if (norm & V4L2_STD_PAL_Nc) { + cxiformat = VideoFormatPALNC; + cxoformat = 0x1c1f0008; + } else if (norm & V4L2_STD_PAL_60) { + cxiformat = VideoFormatPAL60; + cxoformat = 0x181f0008; + } else if (norm & V4L2_STD_NTSC) { + cxiformat = VideoFormatNTSC; + cxoformat = 0x181f0008; + } else if (norm & V4L2_STD_SECAM) { step_db = 4250000 * 8; step_dr = 4406250 * 8; + + cxiformat = VideoFormatSECAM; + cxoformat = 0x181f0008; + } else { /* PAL */ + cxiformat = VideoFormatPAL; + cxoformat = 0x181f0008; } dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n", - norm->name, fsc8, adc_clock, vdec_clock, step_db, step_dr); + v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock, + step_db, step_dr); set_pll(core,2,vdec_clock); dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n", - norm->cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); - cx_andor(MO_INPUT_FORMAT, 0xf, norm->cxiformat); + cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); + cx_andor(MO_INPUT_FORMAT, 0xf, cxiformat); #if 1 // FIXME: as-is from DScaler dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n", - norm->cxoformat, cx_read(MO_OUTPUT_FORMAT)); - cx_write(MO_OUTPUT_FORMAT, norm->cxoformat); + cxoformat, cx_read(MO_OUTPUT_FORMAT)); + cx_write(MO_OUTPUT_FORMAT, cxoformat); #endif // MO_SCONV_REG = adc clock / video dec clock * 2^17 @@ -999,7 +1033,7 @@ int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) set_tvaudio(core); // tell i2c chips - cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm->id); + cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm); // done return 0; diff --git a/linux/drivers/media/video/cx88/cx88-vbi.c b/linux/drivers/media/video/cx88/cx88-vbi.c index 5cd0ee9ce..d22c5f8cf 100644 --- a/linux/drivers/media/video/cx88/cx88-vbi.c +++ b/linux/drivers/media/video/cx88/cx88-vbi.c @@ -22,9 +22,11 @@ MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); /* ------------------------------------------------------------------ */ -void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f) +int cx8800_vbi_fmt (struct file *file, void *priv, + struct v4l2_format *f) { - memset(&f->fmt.vbi,0,sizeof(f->fmt.vbi)); + struct cx8800_fh *fh = priv; + struct cx8800_dev *dev = fh->dev; f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; @@ -32,18 +34,19 @@ void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f) f->fmt.vbi.count[0] = VBI_LINE_COUNT; f->fmt.vbi.count[1] = VBI_LINE_COUNT; - if (dev->core->tvnorm->id & V4L2_STD_525_60) { + if (dev->core->tvnorm & V4L2_STD_525_60) { /* ntsc */ f->fmt.vbi.sampling_rate = 28636363; f->fmt.vbi.start[0] = 10; f->fmt.vbi.start[1] = 273; - } else if (dev->core->tvnorm->id & V4L2_STD_625_50) { + } else if (dev->core->tvnorm & V4L2_STD_625_50) { /* pal */ f->fmt.vbi.sampling_rate = 35468950; f->fmt.vbi.start[0] = 7 -1; f->fmt.vbi.start[1] = 319 -1; } + return 0; } static int cx8800_start_vbi_dma(struct cx8800_dev *dev, diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index 6a420d6db..8a33b7e16 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -1,3 +1,4 @@ + /* * * device driver for Conexant 2388x based TV cards @@ -5,6 +6,11 @@ * * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * + * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> + * - Multituner support + * - video_ioctl2 conversion + * - PAL/M fixes + * * 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 @@ -96,71 +102,7 @@ static LIST_HEAD(cx8800_devlist); /* ------------------------------------------------------------------- */ /* static data */ -static struct cx88_tvnorm tvnorms[] = { - { - .name = "NTSC-M", - .id = V4L2_STD_NTSC_M, - .cxiformat = VideoFormatNTSC, - .cxoformat = 0x181f0008, - },{ - .name = "NTSC-JP", - .id = V4L2_STD_NTSC_M_JP, - .cxiformat = VideoFormatNTSCJapan, - .cxoformat = 0x181f0008, -#if 0 - },{ - .name = "NTSC-4.43", - .id = FIXME, - .cxiformat = VideoFormatNTSC443, - .cxoformat = 0x181f0008, -#endif - },{ - .name = "PAL-BG", - .id = V4L2_STD_PAL_BG, - .cxiformat = VideoFormatPAL, - .cxoformat = 0x181f0008, - },{ - .name = "PAL-DK", - .id = V4L2_STD_PAL_DK, - .cxiformat = VideoFormatPAL, - .cxoformat = 0x181f0008, - },{ - .name = "PAL-I", - .id = V4L2_STD_PAL_I, - .cxiformat = VideoFormatPAL, - .cxoformat = 0x181f0008, - },{ - .name = "PAL-M", - .id = V4L2_STD_PAL_M, - .cxiformat = VideoFormatPALM, - .cxoformat = 0x1c1f0008, - },{ - .name = "PAL-N", - .id = V4L2_STD_PAL_N, - .cxiformat = VideoFormatPALN, - .cxoformat = 0x1c1f0008, - },{ - .name = "PAL-Nc", - .id = V4L2_STD_PAL_Nc, - .cxiformat = VideoFormatPALNC, - .cxoformat = 0x1c1f0008, - },{ - .name = "PAL-60", - .id = V4L2_STD_PAL_60, - .cxiformat = VideoFormatPAL60, - .cxoformat = 0x181f0008, - },{ - .name = "SECAM-L", - .id = V4L2_STD_SECAM_L, - .cxiformat = VideoFormatSECAM, - .cxoformat = 0x181f0008, - },{ - .name = "SECAM-DK", - .id = V4L2_STD_SECAM_DK, - .cxiformat = VideoFormatSECAM, - .cxoformat = 0x181f0008, - } -}; +v4l2_std_id radionorms[] = { 0 }; static struct cx8800_fmt formats[] = { { @@ -387,14 +329,6 @@ int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl) } EXPORT_SYMBOL(cx8800_ctrl_query); -static int cx88_queryctrl(struct v4l2_queryctrl *qctrl) -{ - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (qctrl->id == 0) - return -EINVAL; - return cx8800_ctrl_query(qctrl); -} - /* ------------------------------------------------------------------- */ /* resource management */ @@ -447,8 +381,7 @@ void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits) /* ------------------------------------------------------------------ */ -/* static int video_mux(struct cx8800_dev *dev, unsigned int input) */ -static int video_mux(struct cx88_core *core, unsigned int input) +int cx88_video_mux(struct cx88_core *core, unsigned int input) { /* struct cx88_core *core = dev->core; */ @@ -487,6 +420,7 @@ static int video_mux(struct cx88_core *core, unsigned int input) } return 0; } +EXPORT_SYMBOL(cx88_video_mux); /* ------------------------------------------------------------------ */ @@ -1192,19 +1126,18 @@ video_mmap(struct file *file, struct vm_area_struct * vma) } /* ------------------------------------------------------------------ */ +/* VIDEO CTRL IOCTLS */ -/* static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl) */ -static int get_control(struct cx88_core *core, struct v4l2_control *ctl) +int cx88_get_control (struct cx88_core *core, struct v4l2_control *ctl) { - /* struct cx88_core *core = dev->core; */ - struct cx88_ctrl *c = NULL; + struct cx88_ctrl *c = NULL; u32 value; int i; for (i = 0; i < CX8800_CTLS; i++) if (cx8800_ctls[i].v.id == ctl->id) c = &cx8800_ctls[i]; - if (NULL == c) + if (unlikely(NULL == c)) return -EINVAL; value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg); @@ -1225,20 +1158,20 @@ static int get_control(struct cx88_core *core, struct v4l2_control *ctl) value,c->mask, c->sreg ? " [shadowed]" : ""); return 0; } +EXPORT_SYMBOL(cx88_get_control); -/* static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl) */ -static int set_control(struct cx88_core *core, struct v4l2_control *ctl) +int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl) { - /* struct cx88_core *core = dev->core; */ struct cx88_ctrl *c = NULL; u32 value,mask; int i; + for (i = 0; i < CX8800_CTLS; i++) { if (cx8800_ctls[i].v.id == ctl->id) { c = &cx8800_ctls[i]; } } - if (NULL == c) + if (unlikely(NULL == c)) return -EINVAL; if (ctl->value < c->v.minimum) @@ -1266,7 +1199,7 @@ static int set_control(struct cx88_core *core, struct v4l2_control *ctl) value = ((ctl->value - c->off) << c->shift) & c->mask; - if (core->tvnorm->id & V4L2_STD_SECAM) { + if (core->tvnorm & V4L2_STD_SECAM) { /* For SECAM, both U and V sat should be equal */ value=value<<8|value; } else { @@ -1289,6 +1222,7 @@ static int set_control(struct cx88_core *core, struct v4l2_control *ctl) } return 0; } +EXPORT_SYMBOL(cx88_set_control); static void init_controls(struct cx88_core *core) { @@ -1298,681 +1232,562 @@ static void init_controls(struct cx88_core *core) for (i = 0; i < CX8800_CTLS; i++) { ctrl.id=cx8800_ctls[i].v.id; ctrl.value=cx8800_ctls[i].v.default_value; - set_control(core, &ctrl); + + cx88_set_control(core, &ctrl); } } /* ------------------------------------------------------------------ */ +/* VIDEO IOCTLS */ -static int cx8800_g_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, - struct v4l2_format *f) +static int vidioc_g_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) { - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; - f->fmt.pix.field = fh->vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; - case V4L2_BUF_TYPE_VBI_CAPTURE: - cx8800_vbi_fmt(dev, f); - return 0; - default: - return -EINVAL; - } + struct cx8800_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; } -static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, - struct v4l2_format *f) +static int vidioc_try_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) { - struct cx88_core *core = dev->core; + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + struct cx8800_fmt *fmt; + enum v4l2_field field; + unsigned int maxw, maxh; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - { - struct cx8800_fmt *fmt; - enum v4l2_field field; - unsigned int maxw, maxh; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (NULL == fmt) - return -EINVAL; - - field = f->fmt.pix.field; - maxw = norm_maxw(core->tvnorm); - maxh = norm_maxh(core->tvnorm); - - if (V4L2_FIELD_ANY == field) { - field = (f->fmt.pix.height > maxh/2) - ? V4L2_FIELD_INTERLACED - : V4L2_FIELD_BOTTOM; - } - - switch (field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - maxh = maxh / 2; - break; - case V4L2_FIELD_INTERLACED: - break; - default: - return -EINVAL; - } + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; - f->fmt.pix.field = field; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - f->fmt.pix.width &= ~0x03; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + field = f->fmt.pix.field; + maxw = norm_maxw(core->tvnorm); + maxh = norm_maxh(core->tvnorm); - return 0; + if (V4L2_FIELD_ANY == field) { + field = (f->fmt.pix.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; } - case V4L2_BUF_TYPE_VBI_CAPTURE: - cx8800_vbi_fmt(dev, f); - return 0; + + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; default: return -EINVAL; } + + f->fmt.pix.field = field; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; } -static int cx8800_s_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, - struct v4l2_format *f) +static int vidioc_s_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) { - int err; - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - err = cx8800_try_fmt(dev,fh,f); - if (0 != err) - return err; + struct cx8800_fh *fh = priv; + int err = vidioc_try_fmt_cap (file,priv,f); - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vidq.field = f->fmt.pix.field; - return 0; - case V4L2_BUF_TYPE_VBI_CAPTURE: - cx8800_vbi_fmt(dev, f); - return 0; - default: - return -EINVAL; - } + if (0 != err) + return err; + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vidq.field = f->fmt.pix.field; + return 0; } -/* - * This function is _not_ called directly, but from - * video_generic_ioctl (and maybe others). userspace - * copying is done already, arg is a kernel pointer. - */ -static int video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap (struct file *file, void *priv, + struct v4l2_capability *cap) { - struct cx8800_fh *fh = file->private_data; - struct cx8800_dev *dev = fh->dev; + struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev; struct cx88_core *core = dev->core; - int err; - - if (video_debug > 1) - v4l_print_ioctl(core->name,cmd); - switch (cmd) { - /* --- capabilities ------------------------------------------ */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->driver, "cx8800"); - strlcpy(cap->card, cx88_boards[core->board].name, - sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); - cap->version = CX88_VERSION_CODE; - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_VBI_CAPTURE | + strcpy(cap->driver, "cx8800"); + strlcpy(cap->card, cx88_boards[core->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); + cap->version = CX88_VERSION_CODE; + cap->capabilities = #if 0 - V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_VIDEO_OVERLAY | #endif - 0; - if (UNSET != core->tuner_type) - cap->capabilities |= V4L2_CAP_TUNER; - return 0; - } + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + V4L2_CAP_VBI_CAPTURE; + if (UNSET != core->tuner_type) + cap->capabilities |= V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_enum_fmt_cap (struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(formats))) + return -EINVAL; + + strlcpy(f->description,formats[f->index].name,sizeof(f->description)); + f->pixelformat = formats[f->index].fourcc; + + return 0; +} - /* --- capture ioctls ---------------------------------------- */ - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *f = arg; - enum v4l2_buf_type type; - unsigned int index; - - index = f->index; - type = f->type; - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (index >= ARRAY_SIZE(formats)) - return -EINVAL; - memset(f,0,sizeof(*f)); - f->index = index; - f->type = type; - strlcpy(f->description,formats[index].name,sizeof(f->description)); - f->pixelformat = formats[index].fourcc; - break; - default: - return -EINVAL; - } - return 0; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *f = arg; - return cx8800_g_fmt(dev,fh,f); - } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = arg; - return cx8800_s_fmt(dev,fh,f); - } - case VIDIOC_TRY_FMT: - { - struct v4l2_format *f = arg; - return cx8800_try_fmt(dev,fh,f); - } #ifdef CONFIG_VIDEO_V4L1_COMPAT - /* --- streaming capture ------------------------------------- */ - case VIDIOCGMBUF: - { - struct video_mbuf *mbuf = arg; - struct videobuf_queue *q; - struct v4l2_requestbuffers req; - unsigned int i; - - q = get_queue(fh); - memset(&req,0,sizeof(req)); - req.type = q->type; - req.count = 8; - req.memory = V4L2_MEMORY_MMAP; - err = videobuf_reqbufs(q,&req); - if (err < 0) - return err; - memset(mbuf,0,sizeof(*mbuf)); - mbuf->frames = req.count; - mbuf->size = 0; - for (i = 0; i < mbuf->frames; i++) { - mbuf->offsets[i] = q->bufs[i]->boff; - mbuf->size += q->bufs[i]->bsize; - } - return 0; +static int vidiocgmbuf (struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct cx8800_fh *fh = priv; + struct videobuf_queue *q; + struct v4l2_requestbuffers req; + unsigned int i; + int err; + + q = get_queue(fh); + memset(&req,0,sizeof(req)); + req.type = q->type; + req.count = 8; + req.memory = V4L2_MEMORY_MMAP; + err = videobuf_reqbufs(q,&req); + if (err < 0) + return err; + + mbuf->frames = req.count; + mbuf->size = 0; + for (i = 0; i < mbuf->frames; i++) { + mbuf->offsets[i] = q->bufs[i]->boff; + mbuf->size += q->bufs[i]->bsize; } + return 0; +} #endif - case VIDIOC_REQBUFS: - return videobuf_reqbufs(get_queue(fh), arg); - case VIDIOC_QUERYBUF: - return videobuf_querybuf(get_queue(fh), arg); +static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p) +{ + struct cx8800_fh *fh = priv; + return (videobuf_reqbufs(get_queue(fh), p)); +} - case VIDIOC_QBUF: - return videobuf_qbuf(get_queue(fh), arg); +static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8800_fh *fh = priv; + return (videobuf_querybuf(get_queue(fh), p)); +} - case VIDIOC_DQBUF: - return videobuf_dqbuf(get_queue(fh), arg, - file->f_flags & O_NONBLOCK); +static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8800_fh *fh = priv; + return (videobuf_qbuf(get_queue(fh), p)); +} - case VIDIOC_STREAMON: - { - int res = get_ressource(fh); +static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8800_fh *fh = priv; + return (videobuf_dqbuf(get_queue(fh), p, + file->f_flags & O_NONBLOCK)); +} - if (!res_get(dev,fh,res)) - return -EBUSY; - return videobuf_streamon(get_queue(fh)); - } - case VIDIOC_STREAMOFF: - { - int res = get_ressource(fh); +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx8800_fh *fh = priv; + struct cx8800_dev *dev = fh->dev; - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - res_free(dev,fh,res); - return 0; - } - default: - return cx88_do_ioctl( inode, file, fh->radio, core, cmd, arg, video_do_ioctl ); - } + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + if (unlikely(i != fh->type)) + return -EINVAL; + + if (unlikely(!res_get(dev,fh,get_ressource(fh)))) + return -EBUSY; + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx8800_fh *fh = priv; + struct cx8800_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_ressource(fh); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev,fh,res); return 0; } -int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, - struct cx88_core *core, unsigned int cmd, void *arg, v4l2_kioctl driver_ioctl) +static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms) { - int err; + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - if (video_debug) { - if (video_debug > 1) { - if (_IOC_DIR(cmd) & _IOC_WRITE) - v4l_printk_ioctl_arg("cx88(w)",cmd, arg); - else if (!_IOC_DIR(cmd) & _IOC_READ) { - v4l_print_ioctl("cx88", cmd); - } - } else - v4l_print_ioctl(core->name,cmd); - - } - - switch (cmd) { - /* ---------- tv norms ---------- */ - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *e = arg; - unsigned int i; - - i = e->index; - if (i >= ARRAY_SIZE(tvnorms)) - return -EINVAL; - err = v4l2_video_std_construct(e, tvnorms[e->index].id, - tvnorms[e->index].name); - e->index = i; - if (err < 0) - return err; - return 0; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; + mutex_lock(&core->lock); + cx88_set_tvnorm(core,*tvnorms); + mutex_unlock(&core->lock); - *id = core->tvnorm->id; - return 0; - } - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - unsigned int i; + return 0; +} - for(i = 0; i < ARRAY_SIZE(tvnorms); i++) - if (*id & tvnorms[i].id) - break; - if (i == ARRAY_SIZE(tvnorms)) - return -EINVAL; +/* only one input in this sample driver */ +int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i) +{ + static const char *iname[] = { + [ CX88_VMUX_COMPOSITE1 ] = "Composite1", + [ CX88_VMUX_COMPOSITE2 ] = "Composite2", + [ CX88_VMUX_COMPOSITE3 ] = "Composite3", + [ CX88_VMUX_COMPOSITE4 ] = "Composite4", + [ CX88_VMUX_SVIDEO ] = "S-Video", + [ CX88_VMUX_TELEVISION ] = "Television", + [ CX88_VMUX_CABLE ] = "Cable TV", + [ CX88_VMUX_DVB ] = "DVB", + [ CX88_VMUX_DEBUG ] = "for debug only", + }; + unsigned int n; - mutex_lock(&core->lock); - cx88_set_tvnorm(core,&tvnorms[i]); - mutex_unlock(&core->lock); - return 0; - } + n = i->index; + if (n >= 4) + return -EINVAL; + if (0 == INPUT(n)->type) + return -EINVAL; + memset(i,0,sizeof(*i)); + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(i->name,iname[INPUT(n)->type]); + if ((CX88_VMUX_TELEVISION == INPUT(n)->type) || + (CX88_VMUX_CABLE == INPUT(n)->type)) + i->type = V4L2_INPUT_TYPE_TUNER; + i->std = CX88_NORMS; + return 0; +} +EXPORT_SYMBOL(cx88_enum_input); - /* ------ input switching ---------- */ - case VIDIOC_ENUMINPUT: - { - static const char *iname[] = { - [ CX88_VMUX_COMPOSITE1 ] = "Composite1", - [ CX88_VMUX_COMPOSITE2 ] = "Composite2", - [ CX88_VMUX_COMPOSITE3 ] = "Composite3", - [ CX88_VMUX_COMPOSITE4 ] = "Composite4", - [ CX88_VMUX_SVIDEO ] = "S-Video", - [ CX88_VMUX_TELEVISION ] = "Television", - [ CX88_VMUX_CABLE ] = "Cable TV", - [ CX88_VMUX_DVB ] = "DVB", - [ CX88_VMUX_DEBUG ] = "for debug only", - }; - struct v4l2_input *i = arg; - unsigned int n; - - n = i->index; - if (n >= 4) - return -EINVAL; - if (0 == INPUT(n)->type) - return -EINVAL; - memset(i,0,sizeof(*i)); - i->index = n; - i->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(i->name,iname[INPUT(n)->type]); - if ((CX88_VMUX_TELEVISION == INPUT(n)->type) || - (CX88_VMUX_CABLE == INPUT(n)->type)) - i->type = V4L2_INPUT_TYPE_TUNER; - for (n = 0; n < ARRAY_SIZE(tvnorms); n++) - i->std |= tvnorms[n].id; - return 0; - } - case VIDIOC_G_INPUT: - { - unsigned int *i = arg; +static int vidioc_enum_input (struct file *file, void *priv, + struct v4l2_input *i) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + return cx88_enum_input (core,i); +} - *i = core->input; - return 0; - } - case VIDIOC_S_INPUT: - { - unsigned int *i = arg; +static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - if (*i >= 4) - return -EINVAL; - mutex_lock(&core->lock); - cx88_newstation(core); - video_mux(core,*i); - mutex_unlock(&core->lock); - return 0; - } + *i = core->input; + return 0; +} + +static int vidioc_s_input (struct file *file, void *priv, unsigned int i) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + + if (i >= 4) + return -EINVAL; + + mutex_lock(&core->lock); + cx88_newstation(core); + cx88_video_mux(core,i); + mutex_unlock(&core->lock); + return 0; +} #if 0 - /* needs review */ - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; - unsigned int n = a->index; - - memset(a,0,sizeof(*a)); - a->index = n; - switch (n) { - case 0: - if ((CX88_VMUX_TELEVISION == INPUT(n)->type) - || (CX88_VMUX_CABLE == INPUT(n)->type)) { - strcpy(a->name,"Television"); - /* FIXME figure out if stereo received and set V4L2_AUDCAP_STEREO. */ - return 0; - } - break; - case 1: - if (CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q == core->board) { - strcpy(a->name,"Line In"); - a->capability = V4L2_AUDCAP_STEREO; - return 0; - } - break; +static int vidioc_g_audio (struct file *file, void *priv, unsigned int i) +{ + struct v4l2_audio *a = arg; + unsigned int n = a->index; + + memset(a,0,sizeof(*a)); + a->index = n; + switch (n) { + case 0: + if ((CX88_VMUX_TELEVISION == INPUT(n)->type) + || (CX88_VMUX_CABLE == INPUT(n)->type)) { + strcpy(a->name,"Television"); + /* FIXME figure out if stereo received and set V4L2_AUDCAP_STEREO. */ + return 0; } - /* Audio input not available. */ - return -EINVAL; + break; + case 1: + if (CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q == core->board) { + strcpy(a->name,"Line In"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; + } + break; } + /* Audio input not available. */ + return -EINVAL; +} #endif - /* --- controls ---------------------------------------------- */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *c = arg; +static int vidioc_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (unlikely(qctrl->id == 0)) + return -EINVAL; + return cx8800_ctrl_query(qctrl); +} - return cx88_queryctrl(c); - } - case VIDIOC_G_CTRL: - return get_control(core,arg); - case VIDIOC_S_CTRL: - return set_control(core,arg); +static int vidioc_g_ctrl (struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + return + cx88_get_control(core,ctl); +} - /* --- tuner ioctls ------------------------------------------ */ - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; - u32 reg; - - if (UNSET == core->tuner_type) - return -EINVAL; - if (0 != t->index) - return -EINVAL; - - memset(t,0,sizeof(*t)); - strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; - - cx88_get_stereo(core ,t); - reg = cx_read(MO_DEVICE_STATUS); - t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + return + cx88_set_control(core,ctl); +} - if (UNSET == core->tuner_type) - return -EINVAL; - if (0 != t->index) - return -EINVAL; - cx88_set_stereo(core, t->audmode, 1); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + u32 reg; - memset(f,0,sizeof(*f)); + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; + if (0 != t->index) + return -EINVAL; - if (UNSET == core->tuner_type) - return -EINVAL; + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; - /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ - f->type = radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = core->freq; + cx88_get_stereo(core ,t); + reg = cx_read(MO_DEVICE_STATUS); + t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; + return 0; +} - cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); +static int vidioc_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - if (UNSET == core->tuner_type) - return -EINVAL; - if (f->tuner != 0) - return -EINVAL; - if (0 == radio && f->type != V4L2_TUNER_ANALOG_TV) - return -EINVAL; - if (1 == radio && f->type != V4L2_TUNER_RADIO) - return -EINVAL; - mutex_lock(&core->lock); - core->freq = f->frequency; - cx88_newstation(core); - cx88_call_i2c_clients(core,VIDIOC_S_FREQUENCY,f); - - /* When changing channels it is required to reset TVAUDIO */ - msleep (10); - cx88_set_tvaudio(core); + if (UNSET == core->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; - mutex_unlock(&core->lock); - return 0; - } -#ifdef CONFIG_VIDEO_ADV_DEBUG - /* ioctls to allow direct acces to the cx2388x registers */ - case VIDIOC_INT_G_REGISTER: - { - struct v4l2_register *reg = arg; + cx88_set_stereo(core, t->audmode, 1); + return 0; +} - if (reg->i2c_id != 0) - return -EINVAL; - /* cx2388x has a 24-bit register space */ - reg->val = cx_read(reg->reg&0xffffff); - return 0; - } - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; +static int vidioc_g_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx8800_fh *fh = priv; + struct cx88_core *core = fh->dev->core; - if (reg->i2c_id != 0) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - cx_write(reg->reg&0xffffff, reg->val); - return 0; - } -#endif + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; + + /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = core->freq; + + cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); + + return 0; +} + +int cx88_set_freq (struct cx88_core *core, + struct v4l2_frequency *f) +{ + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; + if (unlikely(f->tuner != 0)) + return -EINVAL; + + mutex_lock(&core->lock); + core->freq = f->frequency; + cx88_newstation(core); + cx88_call_i2c_clients(core,VIDIOC_S_FREQUENCY,f); + + /* When changing channels it is required to reset TVAUDIO */ + msleep (10); + cx88_set_tvaudio(core); + + mutex_unlock(&core->lock); - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - driver_ioctl); - } return 0; } +EXPORT_SYMBOL(cx88_set_freq); -static int video_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) { - int retval; + struct cx8800_fh *fh = priv; + struct cx88_core *core = fh->dev->core; - retval=video_usercopy(inode, file, cmd, arg, video_do_ioctl); + 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; - if (video_debug > 1) { - if (retval < 0) { - v4l_print_ioctl("cx88(err)", cmd); - printk(KERN_DEBUG "cx88(err): errcode=%d\n",retval); - } else if (_IOC_DIR(cmd) & _IOC_READ) - v4l_printk_ioctl_arg("cx88(r)",cmd, (void *)arg); - } + return + cx88_set_freq (core,f); +} - return retval; +#if 0 //ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register (struct file *file, void *priv, + v4l2_register *reg) +{ + if (reg->i2c_id != 0) + return -EINVAL; + /* cx2388x has a 24-bit register space */ + reg->val = cx_read(reg->reg&0xffffff); + return 0; } +static int vidioc_s_register (struct file *file, void *fh, + v4l2_register *reg) +{ + + if (reg->i2c_id != 0) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx_write(reg->reg&0xffffff, reg->val); + return 0; +} +#endif + +/* ----------------------------------------------------------- */ +/* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ -static int radio_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int radio_querycap (struct file *file, void *priv, + struct v4l2_capability *cap) { - struct cx8800_fh *fh = file->private_data; - struct cx8800_dev *dev = fh->dev; + struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev; struct cx88_core *core = dev->core; - if (video_debug > 1) - v4l_print_ioctl(core->name,cmd); - - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->driver, "cx8800"); - strlcpy(cap->card, cx88_boards[core->board].name, - sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); - cap->version = CX88_VERSION_CODE; - cap->capabilities = V4L2_CAP_TUNER; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; + strcpy(cap->driver, "cx8800"); + strlcpy(cap->card, cx88_boards[core->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); + cap->version = CX88_VERSION_CODE; + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} - if (t->index > 0) - return -EINVAL; +static int radio_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - memset(t,0,sizeof(*t)); - strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; + if (unlikely(t->index > 0)) + return -EINVAL; - cx88_call_i2c_clients(core,VIDIOC_G_TUNER,t); - return 0; - } - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; - if (i->index != 0) - return -EINVAL; - strcpy(i->name,"Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - return 0; - } - case VIDIOC_G_INPUT: - { - int *i = arg; - *i = 0; - return 0; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; + cx88_call_i2c_clients(core,VIDIOC_G_TUNER,t); + return 0; +} - memset(a,0,sizeof(*a)); - strcpy(a->name,"Radio"); - return 0; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; - *id = 0; - return 0; - } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCSTUNER: - { - struct video_tuner *v = arg; +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; - if (v->tuner) /* Only tuner 0 */ - return -EINVAL; + return 0; +} - cx88_call_i2c_clients(core,VIDIOCSTUNER,v); - return 0; - } -#endif - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; +static int radio_g_audio (struct file *file, void *priv, struct v4l2_audio *a) +{ + if (unlikely(a->index)) + return -EINVAL; - if (0 != t->index) - return -EINVAL; + memset(a,0,sizeof(*a)); + strcpy(a->name,"Radio"); + return 0; +} - cx88_call_i2c_clients(core,VIDIOC_S_TUNER,t); +/* FIXME: Should add a standard for radio */ - return 0; - } +static int radio_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - case VIDIOC_S_AUDIO: - case VIDIOC_S_INPUT: - case VIDIOC_S_STD: - return 0; + if (0 != t->index) + return -EINVAL; - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *c = arg; - 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 < CX8800_CTLS; i++) - if (cx8800_ctls[i].v.id == c->id) - break; - *c = cx8800_ctls[i].v; - } else - *c = no_ctl; - return 0; - } + cx88_call_i2c_clients(core,VIDIOC_S_TUNER,t); + return 0; +} - case VIDIOC_G_CTRL: - case VIDIOC_S_CTRL: - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: - return video_do_ioctl(inode,file,cmd,arg); +static int radio_s_audio (struct file *file, void *fh, + struct v4l2_audio *a) +{ + return 0; +} - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - radio_do_ioctl); - } +static int radio_s_input (struct file *file, void *fh, unsigned int i) +{ return 0; -}; +} -static int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int radio_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *c) { - return video_usercopy(inode, file, cmd, arg, radio_do_ioctl); -}; + 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 < CX8800_CTLS; i++) + if (cx8800_ctls[i].v.id == c->id) + break; + *c = cx8800_ctls[i].v; + } else + *c = no_ctl; + return 0; +} /* ----------------------------------------------------------- */ @@ -2109,29 +1924,50 @@ static struct file_operations video_fops = .read = video_read, .poll = video_poll, .mmap = video_mmap, - .ioctl = video_ioctl, + .ioctl = video_ioctl2, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) .compat_ioctl = v4l_compat_ioctl32, #endif .llseek = no_llseek, }; +static struct video_device cx8800_vbi_template; static struct video_device cx8800_video_template = { - .name = "cx8800-video", - .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES, - .hardware = 0, - .fops = &video_fops, - .minor = -1, -}; - -static struct video_device cx8800_vbi_template = -{ - .name = "cx8800-vbi", - .type = VID_TYPE_TELETEXT|VID_TYPE_TUNER, - .hardware = 0, - .fops = &video_fops, - .minor = -1, + .name = "cx8800-video", + .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES, + .fops = &video_fops, + .minor = -1, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, + .vidioc_g_fmt_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_g_fmt_vbi = cx8800_vbi_fmt, + .vidioc_try_fmt_vbi = cx8800_vbi_fmt, + .vidioc_s_fmt_vbi = cx8800_vbi_fmt, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .tvnorms = CX88_NORMS, + .current_norm = V4L2_STD_NTSC_M, }; static struct file_operations radio_fops = @@ -2139,7 +1975,7 @@ static struct file_operations radio_fops = .owner = THIS_MODULE, .open = video_open, .release = video_release, - .ioctl = radio_ioctl, + .ioctl = video_ioctl2, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) .compat_ioctl = v4l_compat_ioctl32, #endif @@ -2148,11 +1984,23 @@ static struct file_operations radio_fops = static struct video_device cx8800_radio_template = { - .name = "cx8800-radio", - .type = VID_TYPE_TUNER, - .hardware = 0, - .fops = &radio_fops, - .minor = -1, + .name = "cx8800-radio", + .type = VID_TYPE_TUNER, + .hardware = 0, + .fops = &radio_fops, + .minor = -1, + .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, }; /* ----------------------------------------------------------- */ @@ -2187,6 +2035,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, { struct cx8800_dev *dev; struct cx88_core *core; + int err; dev = kzalloc(sizeof(*dev),GFP_KERNEL); @@ -2221,13 +2070,19 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, goto fail_core; } + /* Initialize VBI template */ + memcpy( &cx8800_vbi_template, &cx8800_video_template, + sizeof(cx8800_vbi_template) ); + strcpy(cx8800_vbi_template.name,"cx8800-vbi"); + cx8800_vbi_template.type = VID_TYPE_TELETEXT|VID_TYPE_TUNER; + /* initialize driver struct */ #if 0 /* moved to cx88_core_get */ init_MUTEX(&dev->lock); #endif spin_lock_init(&dev->slock); - core->tvnorm = tvnorms; + core->tvnorm = cx8800_video_template.current_norm; /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); @@ -2308,9 +2163,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* initial device configuration */ mutex_lock(&core->lock); - cx88_set_tvnorm(core,tvnorms); + cx88_set_tvnorm(core,core->tvnorm); init_controls(core); - video_mux(core,0); + cx88_video_mux(core,0); mutex_unlock(&core->lock); /* start tvaudio thread */ @@ -2499,8 +2354,6 @@ static void cx8800_fini(void) module_init(cx8800_init); module_exit(cx8800_fini); -EXPORT_SYMBOL(cx88_do_ioctl); - /* ----------------------------------------------------------- */ /* * Local variables: diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index d94a2f7f4..5bb190eb2 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -55,6 +55,13 @@ /* ----------------------------------------------------------- */ /* defines and enums */ +/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ +#define CX88_NORMS (\ + V4L2_STD_NTSC_M| V4L2_STD_NTSC_M_JP| V4L2_STD_NTSC_443 | \ + V4L2_STD_PAL_BG| V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \ + V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \ + V4L2_STD_PAL_60| V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK ) + #define FORMAT_FLAGS_PACKED 0x01 #define FORMAT_FLAGS_PLANAR 0x02 @@ -87,22 +94,15 @@ enum cx8802_board_access { /* ----------------------------------------------------------- */ /* tv norms */ -struct cx88_tvnorm { - char *name; - v4l2_std_id id; - u32 cxiformat; - u32 cxoformat; -}; - -static unsigned int inline norm_maxw(struct cx88_tvnorm *norm) +static unsigned int inline norm_maxw(v4l2_std_id norm) { - return (norm->id & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768; + return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768; } -static unsigned int inline norm_maxh(struct cx88_tvnorm *norm) +static unsigned int inline norm_maxh(v4l2_std_id norm) { - return (norm->id & V4L2_STD_625_50) ? 576 : 480; + return (norm & V4L2_STD_625_50) ? 576 : 480; } /* ----------------------------------------------------------- */ @@ -329,7 +329,7 @@ struct cx88_core { /* state info */ struct task_struct *kthread; - struct cx88_tvnorm *tvnorm; + v4l2_std_id tvnorm; u32 tvaudio; u32 audiomode_manual; u32 audiomode_current; @@ -575,7 +575,7 @@ extern void cx88_sram_channel_dump(struct cx88_core *core, extern int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height, enum v4l2_field field); -extern int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm); +extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm); extern struct video_device *cx88_vdev_init(struct cx88_core *core, struct pci_dev *pci, @@ -592,7 +592,10 @@ extern int cx88_stop_audio_dma(struct cx88_core *core); /* ----------------------------------------------------------- */ /* cx88-vbi.c */ -void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f); +/* Can be used as g_vbi_fmt, try_vbi_fmt and s_vbi_fmt */ +int cx8800_vbi_fmt (struct file *file, void *priv, + struct v4l2_format *f); + /* int cx8800_start_vbi_dma(struct cx8800_dev *dev, struct cx88_dmaqueue *q, @@ -672,19 +675,14 @@ int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state); int cx8802_resume_common(struct pci_dev *pci_dev); /* ----------------------------------------------------------- */ -/* cx88-video.c */ -extern int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, - struct cx88_core *core, unsigned int cmd, - void *arg, v4l2_kioctl driver_ioctl); +/* cx88-video.c*/ extern const u32 cx88_user_ctrls[]; extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl); - -/* ----------------------------------------------------------- */ -/* cx88-blackbird.c */ -/* used by cx88-ivtv ioctl emulation layer */ -extern int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg); -extern unsigned int (*cx88_ioctl_translator)(unsigned int cmd); +int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i); +int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f); +int cx88_get_control(struct cx88_core *core, struct v4l2_control *ctl); +int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl); +int cx88_video_mux(struct cx88_core *core, unsigned int input); /* * Local variables: diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 9d1a126be..7f38dae17 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -32,7 +32,6 @@ struct pvr2_msp3400_handler { struct pvr2_hdw *hdw; struct pvr2_i2c_client *client; struct pvr2_i2c_handler i2c_handler; - struct pvr2_audio_stat astat; unsigned long stale_mask; }; @@ -45,13 +44,6 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); - if (hdw->input_val == PVR2_CVAL_INPUT_TV) { - struct v4l2_tuner vt; - memset(&vt,0,sizeof(vt)); - vt.audmode = hdw->audiomode_val; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_TUNER,&vt); - } - route.input = MSP_INPUT_DEFAULT; route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); switch (hdw->input_val) { @@ -79,8 +71,7 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) static int check_stereo(struct pvr2_msp3400_handler *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return (hdw->input_dirty || - hdw->audiomode_dirty); + return hdw->input_dirty; } @@ -100,8 +91,7 @@ static int msp3400_check(struct pvr2_msp3400_handler *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) { msk = 1 << idx; if (ctxt->stale_mask & msk) continue; if (msp3400_ops[idx].check(ctxt)) { @@ -117,8 +107,7 @@ static void msp3400_update(struct pvr2_msp3400_handler *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) { msk = 1 << idx; if (!(ctxt->stale_mask & msk)) continue; ctxt->stale_mask &= ~msk; @@ -127,27 +116,9 @@ static void msp3400_update(struct pvr2_msp3400_handler *ctxt) } -/* This reads back the current signal type */ -static int get_audio_status(struct pvr2_msp3400_handler *ctxt) -{ - struct v4l2_tuner vt; - int stat; - - memset(&vt,0,sizeof(vt)); - stat = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); - if (stat < 0) return stat; - - ctxt->hdw->flag_stereo = (vt.audmode & V4L2_TUNER_MODE_STEREO) != 0; - ctxt->hdw->flag_bilingual = - (vt.audmode & V4L2_TUNER_MODE_LANG2) != 0; - return 0; -} - - static void pvr2_msp3400_detach(struct pvr2_msp3400_handler *ctxt) { ctxt->client->handler = NULL; - ctxt->hdw->audio_stat = NULL; kfree(ctxt); } @@ -170,24 +141,17 @@ const static struct pvr2_i2c_handler_functions msp3400_funcs = { int pvr2_i2c_msp3400_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) { struct pvr2_msp3400_handler *ctxt; - if (hdw->audio_stat) return 0; if (cp->handler) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->i2c_handler.func_data = ctxt; ctxt->i2c_handler.func_table = &msp3400_funcs; ctxt->client = cp; ctxt->hdw = hdw; - ctxt->astat.ctxt = ctxt; - ctxt->astat.status = (int (*)(void *))get_audio_status; - ctxt->astat.detach = (void (*)(void *))pvr2_msp3400_detach; - ctxt->stale_mask = (1 << (sizeof(msp3400_ops)/ - sizeof(msp3400_ops[0]))) - 1; + ctxt->stale_mask = (1 << ARRAY_SIZE(msp3400_ops)) - 1; cp->handler = &ctxt->i2c_handler; - hdw->audio_stat = &ctxt->astat; pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x msp3400 V4L2 handler set up", cp->client->addr); return !0; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c index 19787dc81..3103c808c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -95,9 +95,8 @@ struct pvr2_context *pvr2_context_create( void (*setup_func)(struct pvr2_context *)) { struct pvr2_context *mp = NULL; - mp = kmalloc(sizeof(*mp),GFP_KERNEL); + mp = kzalloc(sizeof(*mp),GFP_KERNEL); if (!mp) goto done; - memset(mp,0,sizeof(*mp)); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp); mp->setup_func = setup_func; mutex_init(&mp->mutex); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index 3bff4e8aa..83d1484a9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -520,9 +520,8 @@ int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, } if (maskptr) *maskptr = ~0; } else if (cptr->info->type == pvr2_ctl_bool) { - ret = parse_token( - ptr,len,valptr,boolNames, - sizeof(boolNames)/sizeof(boolNames[0])); + ret = parse_token(ptr,len,valptr,boolNames, + ARRAY_SIZE(boolNames)); if (ret == 1) { *valptr = *valptr ? !0 : 0; } else if (ret == 0) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index bb2125340..f44048186 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -150,8 +150,7 @@ static int decoder_check(struct pvr2_v4l_cx2584x *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { msk = 1 << idx; if (ctxt->stale_mask & msk) continue; if (decoder_ops[idx].check(ctxt)) { @@ -167,8 +166,7 @@ static void decoder_update(struct pvr2_v4l_cx2584x *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { msk = 1 << idx; if (!(ctxt->stale_mask & msk)) continue; ctxt->stale_mask &= ~msk; @@ -199,18 +197,6 @@ static int decoder_detect(struct pvr2_i2c_client *cp) } -static int decoder_is_tuned(struct pvr2_v4l_cx2584x *ctxt) -{ - struct v4l2_tuner vt; - int ret; - - memset(&vt,0,sizeof(vt)); - ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); - if (ret < 0) return -EINVAL; - return vt.signal ? 1 : 0; -} - - static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt, char *buf,unsigned int cnt) { @@ -243,21 +229,18 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, if (cp->handler) return 0; if (!decoder_detect(cp)) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->handler.func_data = ctxt; ctxt->handler.func_table = &hfuncs; ctxt->ctrl.ctxt = ctxt; ctxt->ctrl.detach = (void (*)(void *))decoder_detach; ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; - ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned; ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset; ctxt->client = cp; ctxt->hdw = hdw; - ctxt->stale_mask = (1 << (sizeof(decoder_ops)/ - sizeof(decoder_ops[0]))) - 1; + ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; hdw->decoder_ctrl = &ctxt->ctrl; cp->handler = &ctxt->handler; { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index f985f00d8..e9da9bb8f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -152,7 +152,7 @@ static unsigned long debugifc_find_mask(const char *buf,unsigned int count) { struct debugifc_mask_item *mip; unsigned int idx; - for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) { + for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) { mip = mask_items + idx; if (debugifc_match_keyword(buf,count,mip->name)) { return mip->msk; @@ -169,7 +169,7 @@ static int debugifc_print_mask(char *buf,unsigned int sz, unsigned int idx; int bcnt = 0; int ccnt; - for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) { + for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) { mip = mask_items + idx; if (!(mip->msk & msk)) continue; ccnt = scnprintf(buf,sz,"%s%c%s", diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index e0e248b83..bd5feef0e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -103,9 +103,8 @@ static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) } msg[1].len = pcnt; msg[1].buf = eeprom+tcnt; - if ((ret = i2c_transfer( - &hdw->i2c_adap, - msg,sizeof(msg)/sizeof(msg[0]))) != 2) { + if ((ret = i2c_transfer(&hdw->i2c_adap, + msg,ARRAY_SIZE(msg))) != 2) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "eeprom fetch set offs err=%d",ret); kfree(eeprom); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 707fa1b67..b9ee07638 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -191,25 +191,23 @@ static int pvr2_encoder_cmd(void *ctxt, */ - if (arg_cnt_send > (sizeof(wrData)/sizeof(wrData[0]))-4) { + if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Failed to write cx23416 command" " - too many input arguments" " (was given %u limit %u)", - arg_cnt_send, - (unsigned int)(sizeof(wrData)/sizeof(wrData[0])) - 4); + arg_cnt_send, ARRAY_SIZE(wrData) - 4); return -EINVAL; } - if (arg_cnt_recv > (sizeof(rdData)/sizeof(rdData[0]))-4) { + if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Failed to write cx23416 command" " - too many return arguments" " (was given %u limit %u)", - arg_cnt_recv, - (unsigned int)(sizeof(rdData)/sizeof(rdData[0])) - 4); + arg_cnt_recv, ARRAY_SIZE(rdData) - 4); return -EINVAL; } @@ -223,7 +221,7 @@ static int pvr2_encoder_cmd(void *ctxt, for (idx = 0; idx < arg_cnt_send; idx++) { wrData[idx+4] = argp[idx]; } - for (; idx < (sizeof(wrData)/sizeof(wrData[0]))-4; idx++) { + for (; idx < ARRAY_SIZE(wrData) - 4; idx++) { wrData[idx+4] = 0; } @@ -267,8 +265,7 @@ static int pvr2_encoder_cmd(void *ctxt, if (ret) break; wrData[0] = 0x7; ret = pvr2_encoder_read_words( - hdw,0,rdData, - sizeof(rdData)/sizeof(rdData[0])); + hdw,0,rdData, ARRAY_SIZE(rdData)); if (ret) break; #if 0 for (idx = 0; idx < args; idx++) { @@ -302,13 +299,13 @@ static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, unsigned int idx; u32 data[12]; - if (args > sizeof(data)/sizeof(data[0])) { + if (args > ARRAY_SIZE(data)) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Failed to write cx23416 command" " - too many arguments" " (was given %u limit %u)", - args,(unsigned int)(sizeof(data)/sizeof(data[0]))); + args, ARRAY_SIZE(data)); return -EINVAL; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 8ac9c7d7f..47b1365d7 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -142,17 +142,10 @@ struct pvr2_ctrl { }; -struct pvr2_audio_stat { - void *ctxt; - void (*detach)(void *); - int (*status)(void *); -}; - struct pvr2_decoder_ctrl { void *ctxt; void (*detach)(void *); void (*enable)(void *,int); - int (*tuned)(void *); void (*force_reset)(void *); }; @@ -283,6 +276,10 @@ struct pvr2_hdw { unsigned int freqSelector; /* 0=radio 1=television */ int freqDirty; + /* Current tuner info - this information is polled from the I2C bus */ + struct v4l2_tuner tuner_signal_info; + int tuner_signal_stale; + /* Video standard handling */ v4l2_std_id std_mask_eeprom; // Hardware supported selections v4l2_std_id std_mask_avail; // Which standards we may select from @@ -314,11 +311,6 @@ struct pvr2_hdw { enum pvr2_config config; - /* Information about what audio signal we're hearing */ - int flag_stereo; - int flag_bilingual; - struct pvr2_audio_stat *audio_stat; - /* Control state needed for cx2341x module */ struct cx2341x_mpeg_params enc_cur_state; struct cx2341x_mpeg_params enc_ctl_state; @@ -343,7 +335,6 @@ struct pvr2_hdw { VCREATE_DATA(res_hor); VCREATE_DATA(res_ver); VCREATE_DATA(srate); - VCREATE_DATA(automodeswitch); #undef VCREATE_DATA struct pvr2_ctld_info *mpeg_ctrl_info; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 0fce806ea..b07bd58ea 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -40,8 +40,6 @@ #define TV_MIN_FREQ 55250000L #define TV_MAX_FREQ 850000000L -#define RADIO_MIN_FREQ 87000000L -#define RADIO_MAX_FREQ 108000000L struct usb_device_id pvr2_device_table[] = { [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, @@ -77,12 +75,10 @@ static const char *pvr2_client_29xxx[] = { static struct pvr2_string_table pvr2_client_lists[] = { [PVR2_HDW_TYPE_29XXX] = { - pvr2_client_29xxx, - sizeof(pvr2_client_29xxx)/sizeof(pvr2_client_29xxx[0]), + pvr2_client_29xxx, ARRAY_SIZE(pvr2_client_29xxx) }, [PVR2_HDW_TYPE_24XXX] = { - pvr2_client_24xxx, - sizeof(pvr2_client_24xxx)/sizeof(pvr2_client_24xxx[0]), + pvr2_client_24xxx, ARRAY_SIZE(pvr2_client_24xxx) }, }; @@ -95,7 +91,6 @@ static int procreload = 0; static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; -static int auto_mode_switch[PVR_NUM]; static int init_pause_msec = 0; module_param(ctlchg, int, S_IRUGO|S_IWUSR); @@ -113,8 +108,6 @@ module_param_array(video_std, int, NULL, 0444); MODULE_PARM_DESC(video_std,"specify initial video standard"); module_param_array(tolerance, int, NULL, 0444); MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); -module_param_array(auto_mode_switch, int, NULL, 0444); -MODULE_PARM_DESC(auto_mode_switch,"Enable TV/Radio automatic mode switch based on freq"); #define PVR2_CTL_WRITE_ENDPOINT 0x01 #define PVR2_CTL_READ_ENDPOINT 0x81 @@ -218,7 +211,7 @@ static const struct pvr2_mpeg_ids mpeg_ids[] = { .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, } }; -#define MPEGDEF_COUNT (sizeof(mpeg_ids)/sizeof(mpeg_ids[0])) +#define MPEGDEF_COUNT ARRAY_SIZE(mpeg_ids) static const char *control_values_srate[] = { @@ -265,7 +258,6 @@ static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); -static unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw); @@ -437,34 +429,48 @@ static void ctrl_cleardirty_input(struct pvr2_ctrl *cptr) cptr->hdw->input_dirty = 0; } -static int ctrl_freq_check(struct pvr2_ctrl *cptr,int v) -{ - /* Both ranges are simultaneously considered legal, in order to - permit implicit mode switching, i.e. set a frequency in the - other range and the mode will switch */ - return (((v >= RADIO_MIN_FREQ) && (v <= RADIO_MAX_FREQ)) || - ((v >= TV_MIN_FREQ) && (v <= TV_MAX_FREQ))); -} static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp) { - /* Actual maximum depends on radio/tv mode */ - if (cptr->hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - *vp = RADIO_MAX_FREQ; - } else { + unsigned long fv; + struct pvr2_hdw *hdw = cptr->hdw; + if (hdw->tuner_signal_stale) { + pvr2_i2c_core_status_poll(hdw); + } + fv = hdw->tuner_signal_info.rangehigh; + if (!fv) { + /* Safety fallback */ *vp = TV_MAX_FREQ; + return 0; + } + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + fv = (fv * 125) / 2; + } else { + fv = fv * 62500; } + *vp = fv; return 0; } static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp) { - /* Actual minimum depends on radio/tv mode */ - if (cptr->hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - *vp = RADIO_MIN_FREQ; - } else { + unsigned long fv; + struct pvr2_hdw *hdw = cptr->hdw; + if (hdw->tuner_signal_stale) { + pvr2_i2c_core_status_poll(hdw); + } + fv = hdw->tuner_signal_info.rangelow; + if (!fv) { + /* Safety fallback */ *vp = TV_MIN_FREQ; + return 0; + } + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + fv = (fv * 125) / 2; + } else { + fv = fv * 62500; } + *vp = fv; return 0; } @@ -624,8 +630,32 @@ static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr) static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = ((pvr2_hdw_get_signal_status_internal(cptr->hdw) & - PVR2_SIGNAL_OK) ? 1 : 0); + struct pvr2_hdw *hdw = cptr->hdw; + pvr2_i2c_core_status_poll(hdw); + *vp = hdw->tuner_signal_info.signal; + return 0; +} + +static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) +{ + int val = 0; + unsigned int subchan; + struct pvr2_hdw *hdw = cptr->hdw; + pvr2_i2c_core_status_poll(hdw); + subchan = hdw->tuner_signal_info.rxsubchans; + if (subchan & V4L2_TUNER_SUB_MONO) { + val |= (1 << V4L2_TUNER_MODE_MONO); + } + if (subchan & V4L2_TUNER_SUB_STEREO) { + val |= (1 << V4L2_TUNER_MODE_STEREO); + } + if (subchan & V4L2_TUNER_SUB_LANG1) { + val |= (1 << V4L2_TUNER_MODE_LANG1); + } + if (subchan & V4L2_TUNER_SUB_LANG2) { + val |= (1 << V4L2_TUNER_MODE_LANG2); + } + *vp = val; return 0; } @@ -694,7 +724,7 @@ static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr) #define DEFENUM(tab) \ .type = pvr2_ctl_enum, \ - .def.type_enum.count = (sizeof(tab)/sizeof((tab)[0])), \ + .def.type_enum.count = ARRAY_SIZE(tab), \ .def.type_enum.value_names = tab #define DEFBOOL \ @@ -735,7 +765,6 @@ VCREATE_FUNCS(audiomode) VCREATE_FUNCS(res_hor) VCREATE_FUNCS(res_ver) VCREATE_FUNCS(srate) -VCREATE_FUNCS(automodeswitch) /* Table definition of all controls which can be manipulated */ static const struct pvr2_ctl_info control_defs[] = { @@ -835,12 +864,6 @@ static const struct pvr2_ctl_info control_defs[] = { .get_max_value = ctrl_vres_max_get, .get_min_value = ctrl_vres_min_get, },{ - .desc = "Automatic TV / Radio mode switch based on frequency", - .name = "auto_mode_switch", - .default_value = 0, - DEFREF(automodeswitch), - DEFBOOL, - },{ .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, .default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, .desc = "Audio Sampling Frequency", @@ -856,10 +879,9 @@ static const struct pvr2_ctl_info control_defs[] = { .get_value = ctrl_freq_get, .is_dirty = ctrl_freq_is_dirty, .clear_dirty = ctrl_freq_clear_dirty, - DEFINT(TV_MIN_FREQ,TV_MAX_FREQ), + DEFINT(0,0), /* Hook in check for input value (tv/radio) and adjust max/min values accordingly */ - .check_value = ctrl_freq_check, .get_max_value = ctrl_freq_max_get, .get_min_value = ctrl_freq_min_get, },{ @@ -873,10 +895,9 @@ static const struct pvr2_ctl_info control_defs[] = { .name = "freq_table_value", .set_value = ctrl_channelfreq_set, .get_value = ctrl_channelfreq_get, - DEFINT(TV_MIN_FREQ,TV_MAX_FREQ), + DEFINT(0,0), /* Hook in check for input value (tv/radio) and adjust max/min values accordingly */ - .check_value = ctrl_freq_check, .get_max_value = ctrl_freq_max_get, .get_min_value = ctrl_freq_min_get, },{ @@ -899,7 +920,20 @@ static const struct pvr2_ctl_info control_defs[] = { .desc = "Signal Present", .name = "signal_present", .get_value = ctrl_signal_get, - DEFBOOL, + DEFINT(0,65535), + },{ + .desc = "Audio Modes Present", + .name = "audio_modes_present", + .get_value = ctrl_audio_modes_present_get, + /* For this type we "borrow" the V4L2_TUNER_MODE enum from + v4l. Nothing outside of this module cares about this, + but I reuse it in order to also reuse the + control_values_audiomode string table. */ + DEFMASK(((1 << V4L2_TUNER_MODE_MONO)| + (1 << V4L2_TUNER_MODE_STEREO)| + (1 << V4L2_TUNER_MODE_LANG1)| + (1 << V4L2_TUNER_MODE_LANG2)), + control_values_audiomode), },{ .desc = "Video Standards Available Mask", .name = "video_standard_mask_available", @@ -949,7 +983,7 @@ static const struct pvr2_ctl_info control_defs[] = { } }; -#define CTRLDEF_COUNT (sizeof(control_defs)/sizeof(control_defs[0])) +#define CTRLDEF_COUNT ARRAY_SIZE(control_defs) const char *pvr2_config_get_name(enum pvr2_config cfg) @@ -985,71 +1019,28 @@ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) driver-core side effects of this action. */ void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) { - int mode = 0; - - /* If hdw->automodeswitch_val is set, then we do something clever: - Look at the desired frequency and see if it looks like FM or TV. - Execute a possible mode switch based on this result. Otherwise - we use the current input setting to determine which frequency - register we need to adjust. */ - if (hdw->automodeswitch_val) { - /* Note that since FM RADIO frequency range sits *inside* - the TV spectrum that we must therefore check the radio - range first... */ - if ((val >= RADIO_MIN_FREQ) && (val <= RADIO_MAX_FREQ)) { - mode = 1; - } else if ((val >= TV_MIN_FREQ) && (val <= TV_MAX_FREQ)) { - mode = 2; - } - } else { - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - mode = 1; - } else { - mode = 2; - } - } - - switch (mode) { - case 1: + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { if (hdw->freqSelector) { /* Swing over to radio frequency selection */ hdw->freqSelector = 0; hdw->freqDirty = !0; } - if (hdw->input_val == PVR2_CVAL_INPUT_TV) { - /* Force switch to radio mode */ - hdw->input_val = PVR2_CVAL_INPUT_RADIO; - hdw->input_dirty = !0; - } if (hdw->freqValRadio != val) { hdw->freqValRadio = val; hdw->freqSlotRadio = 0; - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - hdw->freqDirty = !0; - } + hdw->freqDirty = !0; } - break; - case 2: + } else { if (!(hdw->freqSelector)) { /* Swing over to television frequency selection */ hdw->freqSelector = 1; hdw->freqDirty = !0; } - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - /* Force switch to television mode */ - hdw->input_val = PVR2_CVAL_INPUT_TV; - hdw->input_dirty = !0; - } if (hdw->freqValTelevision != val) { hdw->freqValTelevision = val; hdw->freqSlotTelevision = 0; - if (hdw->input_val == PVR2_CVAL_INPUT_TV) { - hdw->freqDirty = !0; - } + hdw->freqDirty = !0; } - break; - default: - break; } } @@ -1150,12 +1141,10 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) }; static const struct pvr2_string_table fw_file_defs[] = { [PVR2_HDW_TYPE_29XXX] = { - fw_files_29xxx, - sizeof(fw_files_29xxx)/sizeof(fw_files_29xxx[0]), + fw_files_29xxx, ARRAY_SIZE(fw_files_29xxx) }, [PVR2_HDW_TYPE_24XXX] = { - fw_files_24xxx, - sizeof(fw_files_24xxx)/sizeof(fw_files_24xxx[0]), + fw_files_24xxx, ARRAY_SIZE(fw_files_24xxx) }, }; hdw->fw1_state = FW1_STATE_FAILED; // default result @@ -1242,8 +1231,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) trace_firmware("pvr2_upload_firmware2"); ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder", - sizeof(fw_files)/sizeof(fw_files[0]), - fw_files); + ARRAY_SIZE(fw_files), fw_files); if (ret < 0) return ret; fwidx = ret; ret = 0; @@ -1828,11 +1816,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) /* 104.3 MHz, a usable FM station for my area */ hdw->freqValRadio = 104300000L; - /* Default value for auto mode switch based on module option */ - if ((hdw->unit_number >= 0) && (hdw->unit_number < PVR_NUM)) { - hdw->automodeswitch_val = auto_mode_switch[hdw->unit_number]; - } - // Do not use pvr2_reset_ctl_endpoints() here. It is not // thread-safe against the normal pvr2_send_request() mechanism. // (We should make it thread safe). @@ -1972,26 +1955,24 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, struct pvr2_ctl_info *ciptr; hdw_type = devid - pvr2_device_table; - if (hdw_type >= - sizeof(pvr2_device_names)/sizeof(pvr2_device_names[0])) { + if (hdw_type >= ARRAY_SIZE(pvr2_device_names)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Bogus device type of %u reported",hdw_type); return NULL; } - hdw = kmalloc(sizeof(*hdw),GFP_KERNEL); + hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", hdw,pvr2_device_names[hdw_type]); if (!hdw) goto fail; - memset(hdw,0,sizeof(*hdw)); + hdw->tuner_signal_stale = !0; cx2341x_fill_defaults(&hdw->enc_ctl_state); hdw->control_cnt = CTRLDEF_COUNT; hdw->control_cnt += MPEGDEF_COUNT; - hdw->controls = kmalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, + hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, GFP_KERNEL); if (!hdw->controls) goto fail; - memset(hdw->controls,0,sizeof(struct pvr2_ctrl) * hdw->control_cnt); hdw->hdw_type = hdw_type; for (idx = 0; idx < hdw->control_cnt; idx++) { cptr = hdw->controls + idx; @@ -2005,11 +1986,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr->info = control_defs+idx; } /* Define and configure additional controls from cx2341x module. */ - hdw->mpeg_ctrl_info = kmalloc( + hdw->mpeg_ctrl_info = kzalloc( sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT, GFP_KERNEL); if (!hdw->mpeg_ctrl_info) goto fail; - memset(hdw->mpeg_ctrl_info,0, - sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT); for (idx = 0; idx < MPEGDEF_COUNT; idx++) { cptr = hdw->controls + idx + CTRLDEF_COUNT; ciptr = &(hdw->mpeg_ctrl_info[idx].info); @@ -2206,9 +2185,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) pvr2_stream_destroy(hdw->vid_stream); hdw->vid_stream = NULL; } - if (hdw->audio_stat) { - hdw->audio_stat->detach(hdw->audio_stat->ctxt); - } if (hdw->decoder_ctrl) { hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt); } @@ -2434,10 +2410,9 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) cptr = hdw->controls + idx; if (cptr->info->is_dirty == 0) continue; if (!cptr->info->is_dirty(cptr)) continue; - if (!commit_flag) { - commit_flag = !0; - } + commit_flag = !0; + if (!(pvrusb2_debug & PVR2_TRACE_CTL)) continue; bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ", cptr->info->name); value = 0; @@ -2586,34 +2561,6 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) } -/* Return bit mask indicating signal status */ -static unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw) -{ - unsigned int msk = 0; - switch (hdw->input_val) { - case PVR2_CVAL_INPUT_TV: - case PVR2_CVAL_INPUT_RADIO: - if (hdw->decoder_ctrl && - hdw->decoder_ctrl->tuned(hdw->decoder_ctrl->ctxt)) { - msk |= PVR2_SIGNAL_OK; - if (hdw->audio_stat && - hdw->audio_stat->status(hdw->audio_stat->ctxt)) { - if (hdw->flag_stereo) { - msk |= PVR2_SIGNAL_STEREO; - } - if (hdw->flag_bilingual) { - msk |= PVR2_SIGNAL_SAP; - } - } - } - break; - default: - msk |= PVR2_SIGNAL_OK | PVR2_SIGNAL_STEREO; - } - return msk; -} - - int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) { int result; @@ -2629,14 +2576,25 @@ int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) } -/* Return bit mask indicating signal status */ -unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *hdw) +/* Execute poll of tuner status */ +void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw) +{ + LOCK_TAKE(hdw->big_lock); do { + pvr2_i2c_core_status_poll(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); +} + + +/* Return information about the tuner */ +int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp) { - unsigned int msk = 0; LOCK_TAKE(hdw->big_lock); do { - msk = pvr2_hdw_get_signal_status_internal(hdw); + if (hdw->tuner_signal_stale) { + pvr2_i2c_core_status_poll(hdw); + } + memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner)); } while (0); LOCK_GIVE(hdw->big_lock); - return msk; + return 0; } @@ -2685,14 +2643,12 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, int enable_flag) pvr2_trace(PVR2_TRACE_FIRMWARE, "Preparing to suck out CPU firmware"); hdw->fw_size = 0x2000; - hdw->fw_buffer = kmalloc(hdw->fw_size,GFP_KERNEL); + hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL); if (!hdw->fw_buffer) { hdw->fw_size = 0; break; } - memset(hdw->fw_buffer,0,hdw->fw_size); - /* We have to hold the CPU during firmware upload. */ pvr2_hdw_cpureset_assert(hdw,1); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 8ae874440..ab99bea88 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -45,12 +45,6 @@ #define PVR2_CVAL_INPUT_COMPOSITE 2 #define PVR2_CVAL_INPUT_RADIO 3 -/* Values that pvr2_hdw_get_signal_status() returns */ -#define PVR2_SIGNAL_OK 0x0001 -#define PVR2_SIGNAL_STEREO 0x0002 -#define PVR2_SIGNAL_SAP 0x0004 - - /* Subsystem definitions - these are various pieces that can be independently stopped / started. Usually you don't want to mess with this directly (let the driver handle things itself), but it is useful @@ -164,8 +158,11 @@ int pvr2_hdw_commit_ctl(struct pvr2_hdw *); /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); -/* Return PVR2_SIGNAL_XXXX bit mask indicating signal status */ -unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *); +/* Mark tuner status stale so that it will be re-fetched */ +void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *); + +/* Return information about the tuner */ +int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *); /* Query device and see if it thinks it is on a high-speed USB link */ int pvr2_hdw_is_hsm(struct pvr2_hdw *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index dbd2466ef..40200c79f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -34,15 +34,17 @@ #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) #define OP_STANDARD 0 -#define OP_BCSH 1 -#define OP_VOLUME 2 -#define OP_FREQ 3 -#define OP_AUDIORATE 4 -#define OP_SIZE 5 -#define OP_LOG 6 +#define OP_AUDIOMODE 1 +#define OP_BCSH 2 +#define OP_VOLUME 3 +#define OP_FREQ 4 +#define OP_AUDIORATE 5 +#define OP_SIZE 6 +#define OP_LOG 7 static const struct pvr2_i2c_op * const ops[] = { [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard, + [OP_AUDIOMODE] = &pvr2_i2c_op_v4l2_audiomode, [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh, [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume, [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency, @@ -55,11 +57,13 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) int id; id = cp->client->driver->id; cp->ctl_mask = ((1 << OP_STANDARD) | + (1 << OP_AUDIOMODE) | (1 << OP_BCSH) | (1 << OP_VOLUME) | (1 << OP_FREQ) | (1 << OP_SIZE) | (1 << OP_LOG)); + cp->status_poll = pvr2_v4l2_cmd_status_poll; if (id == I2C_DRIVERID_MSP3400) { if (pvr2_i2c_msp3400_setup(hdw,cp)) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index 51da8945e..c650e02cc 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -37,6 +37,7 @@ static void set_standard(struct pvr2_hdw *hdw) vs = hdw->std_mask_cur; pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); } + hdw->tuner_signal_stale = !0; } @@ -139,19 +140,50 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume = { }; +static void set_audiomode(struct pvr2_hdw *hdw) +{ + struct v4l2_tuner vt; + memset(&vt,0,sizeof(vt)); + vt.audmode = hdw->audiomode_val; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_TUNER,&vt); +} + + +static int check_audiomode(struct pvr2_hdw *hdw) +{ + return (hdw->input_dirty || + hdw->audiomode_dirty); +} + + +const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode = { + .check = check_audiomode, + .update = set_audiomode, + .name = "v4l2_audiomode", +}; + + static void set_frequency(struct pvr2_hdw *hdw) { unsigned long fv; struct v4l2_frequency freq; fv = pvr2_hdw_get_cur_freq(hdw); pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv); + if (hdw->tuner_signal_stale) { + pvr2_i2c_core_status_poll(hdw); + } memset(&freq,0,sizeof(freq)); - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { // ((fv * 1000) / 62500) freq.frequency = (fv * 2) / 125; - freq.type = V4L2_TUNER_RADIO; } else { freq.frequency = fv / 62500; + } + /* tuner-core currently doesn't seem to care about this, but + let's set it anyway for completeness. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + freq.type = V4L2_TUNER_RADIO; + } else { freq.type = V4L2_TUNER_ANALOG_TV; } freq.tuner = 0; @@ -230,6 +262,12 @@ void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) } +void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp) +{ + pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&cp->hdw->tuner_signal_info); +} + + /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h index 4419e3d43..6c2379fc5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -32,9 +32,11 @@ extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int); +void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *); #endif /* __PVRUSB2_CMD_V4L2_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 760cb5c7f..4a4f1d64b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -673,6 +673,33 @@ static int handler_check(struct pvr2_i2c_client *cp) #define BUFSIZE 500 + +void pvr2_i2c_core_status_poll(struct pvr2_hdw *hdw) +{ + struct list_head *item; + struct pvr2_i2c_client *cp; + mutex_lock(&hdw->i2c_list_lock); do { + struct v4l2_tuner *vtp = &hdw->tuner_signal_info; + memset(vtp,0,sizeof(*vtp)); + list_for_each(item,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client,list); + if (!cp->detected_flag) continue; + if (!cp->status_poll) continue; + cp->status_poll(cp); + } + hdw->tuner_signal_stale = 0; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c status poll" + " type=%u strength=%u audio=0x%x cap=0x%x" + " low=%u hi=%u", + vtp->type, + vtp->signal,vtp->rxsubchans,vtp->capability, + vtp->rangelow,vtp->rangehigh); + } while (0); mutex_unlock(&hdw->i2c_list_lock); +} + + +/* Issue various I2C operations to bring chip-level drivers into sync with + state stored in this driver. */ void pvr2_i2c_core_sync(struct pvr2_hdw *hdw) { unsigned long msk; @@ -953,12 +980,12 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client) struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); struct pvr2_i2c_client *cp; int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL); - cp = kmalloc(sizeof(*cp),GFP_KERNEL); + cp = kzalloc(sizeof(*cp),GFP_KERNEL); trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]", client->name, client->addr,cp); if (!cp) return -ENOMEM; - memset(cp,0,sizeof(*cp)); + cp->hdw = hdw; INIT_LIST_HEAD(&cp->list); cp->client = client; mutex_lock(&hdw->i2c_list_lock); do { @@ -1034,8 +1061,7 @@ static void do_i2c_scan(struct pvr2_hdw *hdw) printk("%s: i2c scan beginning\n",hdw->name); for (i = 0; i < 128; i++) { msg[0].addr = i; - rc = i2c_transfer(&hdw->i2c_adap,msg, - sizeof(msg)/sizeof(msg[0])); + rc = i2c_transfer(&hdw->i2c_adap,msg, ARRAY_SIZE(msg)); if (rc != 1) continue; printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h index 6d7e25247..bd0807b90 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h @@ -35,10 +35,12 @@ struct pvr2_i2c_client { struct i2c_client *client; struct pvr2_i2c_handler *handler; struct list_head list; + struct pvr2_hdw *hdw; int detected_flag; int recv_enable; unsigned long pend_mask; unsigned long ctl_mask; + void (*status_poll)(struct pvr2_i2c_client *); }; struct pvr2_i2c_handler { @@ -67,6 +69,7 @@ int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg); int pvr2_i2c_core_check_stale(struct pvr2_hdw *); void pvr2_i2c_core_sync(struct pvr2_hdw *); +void pvr2_i2c_core_status_poll(struct pvr2_hdw *); unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen); #define PVR2_I2C_DETAIL_DEBUG 0x0001 #define PVR2_I2C_DETAIL_HANDLER 0x0002 diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c index 61c553ddf..ef7293bf1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c @@ -498,9 +498,8 @@ static void buffer_complete(struct urb *urb) struct pvr2_stream *pvr2_stream_create(void) { struct pvr2_stream *sp; - sp = kmalloc(sizeof(*sp),GFP_KERNEL); + sp = kzalloc(sizeof(*sp),GFP_KERNEL); if (!sp) return sp; - memset(sp,0,sizeof(*sp)); pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp); pvr2_stream_init(sp); return sp; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c index 28385b0c9..c42eef3c2 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -96,10 +96,9 @@ static void pvr2_ioread_done(struct pvr2_ioread *cp) struct pvr2_ioread *pvr2_ioread_create(void) { struct pvr2_ioread *cp; - cp = kmalloc(sizeof(*cp),GFP_KERNEL); + cp = kzalloc(sizeof(*cp),GFP_KERNEL); if (!cp) return NULL; pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp); - memset(cp,0,sizeof(*cp)); if (pvr2_ioread_init(cp) < 0) { kfree(cp); return NULL; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c index f95c598ff..4de535498 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -141,10 +141,8 @@ int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, cnt = 0; while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++; if (cnt >= bufSize) return 0; // No more characters - sp = find_std_name( - std_groups, - sizeof(std_groups)/sizeof(std_groups[0]), - bufPtr,cnt); + sp = find_std_name(std_groups, ARRAY_SIZE(std_groups), + bufPtr,cnt); if (!sp) return 0; // Illegal color system name cnt++; bufPtr += cnt; @@ -163,8 +161,7 @@ int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, if (ch == '/') break; cnt++; } - sp = find_std_name(std_items, - sizeof(std_items)/sizeof(std_items[0]), + sp = find_std_name(std_items, ARRAY_SIZE(std_items), bufPtr,cnt); if (!sp) return 0; // Illegal modulation system ID t = sp->id & cmsk; @@ -189,14 +186,10 @@ unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, unsigned int c1,c2; cfl = 0; c1 = 0; - for (idx1 = 0; - idx1 < sizeof(std_groups)/sizeof(std_groups[0]); - idx1++) { + for (idx1 = 0; idx1 < ARRAY_SIZE(std_groups); idx1++) { gp = std_groups + idx1; gfl = 0; - for (idx2 = 0; - idx2 < sizeof(std_items)/sizeof(std_items[0]); - idx2++) { + for (idx2 = 0; idx2 < ARRAY_SIZE(std_items); idx2++) { ip = std_items + idx2; if (!(gp->id & ip->id & id)) continue; if (!gfl) { @@ -279,7 +272,7 @@ static struct v4l2_standard generic_standards[] = { } }; -#define generic_standards_cnt (sizeof(generic_standards)/sizeof(generic_standards[0])) +#define generic_standards_cnt ARRAY_SIZE(generic_standards) static struct v4l2_standard *match_std(v4l2_std_id id) { @@ -348,7 +341,7 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, fmsk |= idmsk; } - for (idx2 = 0; idx2 < sizeof(std_mixes)/sizeof(std_mixes[0]); idx2++) { + for (idx2 = 0; idx2 < ARRAY_SIZE(std_mixes); idx2++) { if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; } @@ -366,16 +359,15 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, std_cnt); if (!std_cnt) return NULL; // paranoia - stddefs = kmalloc(sizeof(struct v4l2_standard) * std_cnt, + stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt, GFP_KERNEL); - memset(stddefs,0,sizeof(struct v4l2_standard) * std_cnt); for (idx = 0; idx < std_cnt; idx++) stddefs[idx].index = idx; idx = 0; /* Enumerate potential special cases */ - for (idx2 = 0; ((idx2 < sizeof(std_mixes)/sizeof(std_mixes[0])) && - (idx < std_cnt)); idx2++) { + for (idx2 = 0; (idx2 < ARRAY_SIZE(std_mixes)) && (idx < std_cnt); + idx2++) { if (!(id & std_mixes[idx2])) continue; if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 45add19b9..4e3dd17dc 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -494,7 +494,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) unsigned int cnt,acnt; int ret; - if ((ctl_id < 0) || (ctl_id >= (sizeof(funcs)/sizeof(funcs[0])))) { + if ((ctl_id < 0) || (ctl_id >= ARRAY_SIZE(funcs))) { return; } @@ -502,9 +502,8 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); if (!cptr) return; - cip = kmalloc(sizeof(*cip),GFP_KERNEL); + cip = kzalloc(sizeof(*cip),GFP_KERNEL); if (!cip) return; - memset(cip,0,sizeof(*cip)); pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); cip->cptr = cptr; @@ -614,9 +613,8 @@ static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) struct pvr2_sysfs_debugifc *dip; int ret; - dip = kmalloc(sizeof(*dip),GFP_KERNEL); + dip = kzalloc(sizeof(*dip),GFP_KERNEL); if (!dip) return; - memset(dip,0,sizeof(*dip)); dip->attr_debugcmd.attr.owner = THIS_MODULE; dip->attr_debugcmd.attr.name = "debugcmd"; dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP; @@ -769,9 +767,8 @@ static void class_dev_create(struct pvr2_sysfs *sfp, usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw); if (!usb_dev) return; - class_dev = kmalloc(sizeof(*class_dev),GFP_KERNEL); + class_dev = kzalloc(sizeof(*class_dev),GFP_KERNEL); if (!class_dev) return; - memset(class_dev,0,sizeof(*class_dev)); pvr2_sysfs_trace("Creating class_dev id=%p",class_dev); @@ -863,9 +860,8 @@ struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp, struct pvr2_sysfs_class *class_ptr) { struct pvr2_sysfs *sfp; - sfp = kmalloc(sizeof(*sfp),GFP_KERNEL); + sfp = kzalloc(sizeof(*sfp),GFP_KERNEL); if (!sfp) return sfp; - memset(sfp,0,sizeof(*sfp)); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp); pvr2_channel_init(&sfp->channel,mp); sfp->channel.check_func = pvr2_sysfs_internal_check; @@ -886,9 +882,8 @@ static int pvr2_sysfs_hotplug(struct class_device *cd,char **envp, struct pvr2_sysfs_class *pvr2_sysfs_class_create(void) { struct pvr2_sysfs_class *clp; - clp = kmalloc(sizeof(*clp),GFP_KERNEL); + clp = kzalloc(sizeof(*clp),GFP_KERNEL); if (!clp) return clp; - memset(clp,0,sizeof(*clp)); pvr2_sysfs_trace("Creating pvr2_sysfs_class id=%p",clp); clp->class.name = "pvrusb2"; clp->class.class_release = pvr2_sysfs_class_release; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c index e196d92a6..e234cd3c9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c @@ -94,9 +94,8 @@ int pvr2_i2c_tuner_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) struct pvr2_tuner_handler *ctxt; if (cp->handler) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->i2c_handler.func_data = ctxt; ctxt->i2c_handler.func_table = &tuner_funcs; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 5acb67286..13dc436f6 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -96,40 +96,6 @@ static struct v4l2_capability pvr_capability ={ .reserved = {0,0,0,0} }; -static struct v4l2_tuner pvr_v4l2_tuners[]= { - { - .index = 0, - .name = "TV Tuner", - .type = V4L2_TUNER_ANALOG_TV, - .capability = (V4L2_TUNER_CAP_NORM | - V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2), - .rangelow = 0, - .rangehigh = 0, - .rxsubchans = V4L2_TUNER_SUB_STEREO, - .audmode = V4L2_TUNER_MODE_STEREO, - .signal = 0, - .afc = 0, - .reserved = {0,0,0,0} - } -#if 0 - { - .index = 1, - .name = "Radio Tuner", - .type = V4L2_TUNER_RADIO, - .capability = (V4L2_TUNER_CAP_STEREO), - .rangelow = 0, - .rangehigh = 0, - .rxsubchans = 0, - .audmode = V4L2_TUNER_MODE_STEREO, - .signal = 0, - .afc = 0, - .reserved = {0,0,0,0} - } -#endif -}; - static struct v4l2_fmtdesc pvr_fmtdesc [] = { { .index = 0, @@ -356,13 +322,39 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMAUDIO: { + /* pkt: FIXME: We are returning one "fake" input here + which could very well be called "whatever_we_like". + This is for apps that want to see an audio input + just to feel comfortable, as well as to test if + it can do stereo or sth. There is actually no guarantee + that the actual audio input cannot change behind the app's + back, but most applications should not mind that either. + + Hopefully, mplayer people will work with us on this (this + whole mess is to support mplayer pvr://), or Hans will come + up with a more standard way to say "we have inputs but we + don 't want you to change them independent of video" which + will sort this mess. + */ + struct v4l2_audio *vin = arg; ret = -EINVAL; + if (vin->index > 0) break; + strncpy(vin->name, "PVRUSB2 Audio",14); + vin->capability = V4L2_AUDCAP_STEREO; + ret = 0; + break; break; } case VIDIOC_G_AUDIO: { - ret = -EINVAL; + /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ + struct v4l2_audio *vin = arg; + memset(vin,0,sizeof(*vin)); + vin->index = 0; + strncpy(vin->name, "PVRUSB2 Audio",14); + vin->capability = V4L2_AUDCAP_STEREO; + ret = 0; break; } @@ -374,34 +366,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_TUNER: { struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - unsigned int status_mask; - int val; - if (vt->index !=0) break; - - status_mask = pvr2_hdw_get_signal_status(hdw); - - memcpy(vt, &pvr_v4l2_tuners[vt->index], - sizeof(struct v4l2_tuner)); - - vt->signal = 0; - if (status_mask & PVR2_SIGNAL_OK) { - if (status_mask & PVR2_SIGNAL_STEREO) { - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - } else { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - } - if (status_mask & PVR2_SIGNAL_SAP) { - vt->rxsubchans |= (V4L2_TUNER_SUB_LANG1 | - V4L2_TUNER_SUB_LANG2); - } - vt->signal = 65535; - } - - val = 0; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), - &val); - vt->audmode = val; + pvr2_hdw_execute_tuner_poll(hdw); + ret = pvr2_hdw_get_tuner_status(hdw,vt); break; } @@ -415,14 +381,34 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), vt->audmode); + break; } case VIDIOC_S_FREQUENCY: { const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; unsigned long fv; - fv = vf->frequency; + struct v4l2_tuner vt; + int cur_input; + struct pvr2_ctrl *ctrlp; + ret = pvr2_hdw_get_tuner_status(hdw,&vt); + if (ret != 0) break; + ctrlp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); + ret = pvr2_ctrl_get_value(ctrlp,&cur_input); + if (ret != 0) break; if (vf->type == V4L2_TUNER_RADIO) { + if (cur_input != PVR2_CVAL_INPUT_RADIO) { + pvr2_ctrl_set_value(ctrlp, + PVR2_CVAL_INPUT_RADIO); + } + } else { + if (cur_input == PVR2_CVAL_INPUT_RADIO) { + pvr2_ctrl_set_value(ctrlp, + PVR2_CVAL_INPUT_TV); + } + } + fv = vf->frequency; + if (vt.capability & V4L2_TUNER_CAP_LOW) { fv = (fv * 125) / 2; } else { fv = fv * 62500; @@ -436,7 +422,10 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, { struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; int val = 0; - int cur_input = PVR2_CVAL_INPUT_TV; + int cur_input; + struct v4l2_tuner vt; + ret = pvr2_hdw_get_tuner_status(hdw,&vt); + if (ret != 0) break; ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), &val); @@ -445,14 +434,16 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), &cur_input); if (cur_input == PVR2_CVAL_INPUT_RADIO) { - val = (val * 2) / 125; - vf->frequency = val; vf->type = V4L2_TUNER_RADIO; } else { - val /= 62500; - vf->frequency = val; vf->type = V4L2_TUNER_ANALOG_TV; } + if (vt.capability & V4L2_TUNER_CAP_LOW) { + val = (val * 2) / 125; + } else { + val /= 62500; + } + vf->frequency = val; break; } @@ -508,7 +499,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, ret = 0; switch(vf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - int lmin,lmax; + int lmin,lmax,ldef; struct pvr2_ctrl *hcp,*vcp; int h = vf->fmt.pix.height; int w = vf->fmt.pix.width; @@ -517,14 +508,20 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, lmin = pvr2_ctrl_get_min(hcp); lmax = pvr2_ctrl_get_max(hcp); - if (w < lmin) { + ldef = pvr2_ctrl_get_def(hcp); + if (w == -1) { + w = ldef; + } else if (w < lmin) { w = lmin; } else if (w > lmax) { w = lmax; } lmin = pvr2_ctrl_get_min(vcp); lmax = pvr2_ctrl_get_max(vcp); - if (h < lmin) { + ldef = pvr2_ctrl_get_def(vcp); + if (h == -1) { + h = ldef; + } else if (h < lmin) { h = lmin; } else if (h > lmax) { h = lmax; @@ -672,6 +669,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, struct v4l2_ext_control *ctrl; unsigned int idx; int val; + ret = 0; for (idx = 0; idx < ctls->count; idx++) { ctrl = ctls->controls + idx; ret = pvr2_ctrl_get_value( @@ -694,6 +692,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, (struct v4l2_ext_controls *)arg; struct v4l2_ext_control *ctrl; unsigned int idx; + ret = 0; for (idx = 0; idx < ctls->count; idx++) { ctrl = ctls->controls + idx; ret = pvr2_ctrl_set_value( @@ -716,6 +715,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, unsigned int idx; /* For the moment just validate that the requested control actually exists. */ + ret = 0; for (idx = 0; idx < ctls->count; idx++) { ctrl = ctls->controls + idx; pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id); @@ -930,11 +930,10 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) return -EIO; } - fhp = kmalloc(sizeof(*fhp),GFP_KERNEL); + fhp = kzalloc(sizeof(*fhp),GFP_KERNEL); if (!fhp) { return -ENOMEM; } - memset(fhp,0,sizeof(*fhp)); init_waitqueue_head(&fhp->wait_data); fhp->dev_info = dip; @@ -1166,7 +1165,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, nr_ptr = vbi_nr; break; case VFL_TYPE_RADIO: - dip->config = pvr2_config_pcm; + dip->stream = &vp->channel.mc_head->video_stream; + dip->config = pvr2_config_mpeg; dip->minor_type = pvr2_v4l_type_radio; nr_ptr = radio_nr; break; @@ -1178,13 +1178,6 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, } memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); -#if 0 - /* ????? This relation may be problematic on a disconnect. Is this - really needed? I can't seem to find a reason for it. This - can't be a required thing - what if the video device being set - up doesn't have a real hardware device under it? */ - dip->vdev->dev = &usbdev->dev; -#endif dip->devbase.release = pvr2_video_device_release; mindevnum = -1; @@ -1212,19 +1205,16 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) { struct pvr2_v4l2 *vp; - vp = kmalloc(sizeof(*vp),GFP_KERNEL); + vp = kzalloc(sizeof(*vp),GFP_KERNEL); if (!vp) return vp; - memset(vp,0,sizeof(*vp)); - vp->dev_video = kmalloc(sizeof(*vp->dev_video),GFP_KERNEL); - vp->dev_radio = kmalloc(sizeof(*vp->dev_radio),GFP_KERNEL); + vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); + vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); if (!(vp->dev_video && vp->dev_radio)) { - if (vp->dev_video) kfree(vp->dev_video); - if (vp->dev_radio) kfree(vp->dev_radio); + kfree(vp->dev_video); + kfree(vp->dev_radio); kfree(vp); return NULL; } - memset(vp->dev_video,0,sizeof(*vp->dev_video)); - memset(vp->dev_radio,0,sizeof(*vp->dev_radio)); pvr2_channel_init(&vp->channel,mnp); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index 05f2cddeb..df042509a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -66,7 +66,9 @@ static void set_input(struct pvr2_v4l_decoder *ctxt) route.input = SAA7115_SVIDEO2; break; case PVR2_CVAL_INPUT_RADIO: - // ????? No idea yet what to do here + // In radio mode, we mute the video, but point at one + // spot just to stay consistent + route.input = SAA7115_COMPOSITE5; default: return; } @@ -137,8 +139,7 @@ static int decoder_check(struct pvr2_v4l_decoder *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { msk = 1 << idx; if (ctxt->stale_mask & msk) continue; if (decoder_ops[idx].check(ctxt)) { @@ -154,8 +155,7 @@ static void decoder_update(struct pvr2_v4l_decoder *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { msk = 1 << idx; if (!(ctxt->stale_mask & msk)) continue; ctxt->stale_mask &= ~msk; @@ -183,18 +183,6 @@ static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl) } -static int decoder_is_tuned(struct pvr2_v4l_decoder *ctxt) -{ - struct v4l2_tuner vt; - int ret; - - memset(&vt,0,sizeof(vt)); - ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); - if (ret < 0) return -EINVAL; - return vt.signal ? 1 : 0; -} - - static unsigned int decoder_describe(struct pvr2_v4l_decoder *ctxt,char *buf,unsigned int cnt) { return scnprintf(buf,cnt,"handler: pvrusb2-video-v4l"); @@ -218,20 +206,17 @@ int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw, if (cp->handler) return 0; if (!decoder_detect(cp)) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->handler.func_data = ctxt; ctxt->handler.func_table = &hfuncs; ctxt->ctrl.ctxt = ctxt; ctxt->ctrl.detach = (void (*)(void *))decoder_detach; ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; - ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned; ctxt->client = cp; ctxt->hdw = hdw; - ctxt->stale_mask = (1 << (sizeof(decoder_ops)/ - sizeof(decoder_ops[0]))) - 1; + ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; hdw->decoder_ctrl = &ctxt->ctrl; cp->handler = &ctxt->handler; pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up", diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c index ce08dee9b..7890b45d2 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -105,8 +105,7 @@ static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) { msk = 1 << idx; if (ctxt->stale_mask & msk) continue; if (wm8775_ops[idx].check(ctxt)) { @@ -122,8 +121,7 @@ static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) { msk = 1 << idx; if (!(ctxt->stale_mask & msk)) continue; ctxt->stale_mask &= ~msk; @@ -146,16 +144,14 @@ int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) if (cp->handler) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->handler.func_data = ctxt; ctxt->handler.func_table = &hfuncs; ctxt->client = cp; ctxt->hdw = hdw; - ctxt->stale_mask = (1 << (sizeof(wm8775_ops)/ - sizeof(wm8775_ops[0]))) - 1; + ctxt->stale_mask = (1 << ARRAY_SIZE(wm8775_ops)) - 1; cp->handler = &ctxt->handler; pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up", cp->client->addr); diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c b/linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c deleted file mode 100644 index c8f1ae215..000000000 --- a/linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c +++ /dev/null @@ -1,238 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS202BCA image sensor connected to the SN9C10x PC Camera * - * Controllers * - * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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/delay.h> -#include "sn9c102_sensor.h" - - -static struct sn9c102_sensor pas202bca; - - -static int pas202bca_init(struct sn9c102_device* cam) -{ - int err = 0; - - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_write_reg(cam, 0x09, 0x18); - - err += sn9c102_i2c_write(cam, 0x02, 0x14); - err += sn9c102_i2c_write(cam, 0x03, 0x40); - err += sn9c102_i2c_write(cam, 0x0d, 0x2c); - err += sn9c102_i2c_write(cam, 0x0e, 0x01); - err += sn9c102_i2c_write(cam, 0x0f, 0xa9); - err += sn9c102_i2c_write(cam, 0x10, 0x08); - err += sn9c102_i2c_write(cam, 0x13, 0x63); - err += sn9c102_i2c_write(cam, 0x15, 0x70); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - msleep(400); - - return err; -} - - -static int pas202bca_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x24, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static int pas202bca_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); - err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x07, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - return err ? -EIO : 0; -} - - -static int pas202bca_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = &pas202bca; - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static struct sn9c102_sensor pas202bca = { - .name = "PAS202BCA", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas202bca_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x01e5, - .maximum = 0x3fff, - .step = 0x0001, - .default_value = 0x01e5, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0c, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - }, - .set_ctrl = &pas202bca_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &pas202bca_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &pas202bca_set_pix_format -}; - - -int sn9c102_probe_pas202bca(struct sn9c102_device* cam) -{ - const struct usb_device_id pas202bca_id_table[] = { - { USB_DEVICE(0x0c45, 0x60af), }, - { } - }; - int err = 0; - - if (!sn9c102_match_id(cam,pas202bca_id_table)) - return -ENODEV; - - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x40, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); - if (err) - return -EIO; - - if (sn9c102_i2c_try_write(cam, &pas202bca, 0x10, 0)) /* try to write */ - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas202bca); - - return 0; -} diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 155c9a75a..ddedd7fbb 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -610,6 +610,7 @@ static int vivi_start_thread(struct vivi_dmaqueue *dma_q) dma_q->kthread = NULL; dma_q->notify = &sem; + dma_q->rmmod = 0; if (kernel_thread(vivi_thread, dma_q, 0) < 0) { printk (KERN_ERR "sdim: kernel_thread() failed\n"); |