diff options
Diffstat (limited to 'drivers/media/video/pwc')
-rw-r--r-- | drivers/media/video/pwc/pwc-ctrl.c | 935 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-dec1.c | 16 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-dec1.h | 6 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-dec23.c | 307 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-dec23.h | 14 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-if.c | 460 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-kiara.h | 2 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-misc.c | 88 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-timon.h | 2 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-uncompress.c | 46 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc-v4l.c | 330 | ||||
-rw-r--r-- | drivers/media/video/pwc/pwc.h | 78 |
12 files changed, 575 insertions, 1709 deletions
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 3977add..1f506fd 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -102,52 +102,18 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = #include "pwc-nala.h" }; -static void pwc_set_image_buffer_size(struct pwc_device *pdev); - /****************************************************************************/ -static int _send_control_msg(struct pwc_device *pdev, - u8 request, u16 value, int index, void *buf, int buflen) -{ - int rc; - void *kbuf = NULL; - - if (buflen) { - kbuf = kmalloc(buflen, GFP_KERNEL); /* not allowed on stack */ - if (kbuf == NULL) - return -ENOMEM; - memcpy(kbuf, buf, buflen); - } - - rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - kbuf, buflen, USB_CTRL_SET_TIMEOUT); - - kfree(kbuf); - return rc; -} - static int recv_control_msg(struct pwc_device *pdev, - u8 request, u16 value, void *buf, int buflen) + u8 request, u16 value, int recv_count) { int rc; - void *kbuf = kmalloc(buflen, GFP_KERNEL); /* not allowed on stack */ - - if (kbuf == NULL) - return -ENOMEM; rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), request, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - pdev->vcinterface, - kbuf, buflen, USB_CTRL_GET_TIMEOUT); - memcpy(buf, kbuf, buflen); - kfree(kbuf); - + value, pdev->vcinterface, + pdev->ctrl_buf, recv_count, USB_CTRL_GET_TIMEOUT); if (rc < 0) PWC_ERROR("recv_control_msg error %d req %02x val %04x\n", rc, request, value); @@ -155,26 +121,39 @@ static int recv_control_msg(struct pwc_device *pdev, } static inline int send_video_command(struct pwc_device *pdev, - int index, void *buf, int buflen) + int index, const unsigned char *buf, int buflen) { - return _send_control_msg(pdev, - SET_EP_STREAM_CTL, - VIDEO_OUTPUT_CONTROL_FORMATTER, - index, - buf, buflen); + int rc; + + memcpy(pdev->ctrl_buf, buf, buflen); + + rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), + SET_EP_STREAM_CTL, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + VIDEO_OUTPUT_CONTROL_FORMATTER, index, + pdev->ctrl_buf, buflen, USB_CTRL_SET_TIMEOUT); + if (rc >= 0) + memcpy(pdev->cmd_buf, buf, buflen); + else + PWC_ERROR("send_video_command error %d\n", rc); + + return rc; } int send_control_msg(struct pwc_device *pdev, u8 request, u16 value, void *buf, int buflen) { - return _send_control_msg(pdev, - request, value, pdev->vcinterface, buf, buflen); + return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, pdev->vcinterface, + buf, buflen, USB_CTRL_SET_TIMEOUT); } -static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) +static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, + int frames, int *compression, int send_to_cam) { - unsigned char buf[3]; - int ret, fps; + int fps, ret = 0; struct Nala_table_entry *pEntry; int frames2frames[31] = { /* closest match of framerate */ @@ -196,35 +175,34 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) 7 /* 30 */ }; - if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) + if (size < 0 || size > PSZ_CIF) return -EINVAL; + if (frames < 4) + frames = 4; + else if (frames > 25) + frames = 25; frames = frames2frames[frames]; fps = frames2table[frames]; pEntry = &Nala_table[size][fps]; if (pEntry->alternate == 0) return -EINVAL; - memcpy(buf, pEntry->mode, 3); - ret = send_video_command(pdev, pdev->vendpoint, buf, 3); - if (ret < 0) { - PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret); + if (send_to_cam) + ret = send_video_command(pdev, pdev->vendpoint, + pEntry->mode, 3); + if (ret < 0) return ret; - } - if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { - ret = pwc_dec1_init(pdev, pdev->type, pdev->release, buf); - if (ret < 0) - return ret; - } - pdev->cmd_len = 3; - memcpy(pdev->cmd_buf, buf, 3); + if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) + pwc_dec1_init(pdev, pEntry->mode); /* Set various parameters */ + pdev->pixfmt = pixfmt; pdev->vframes = frames; - pdev->vsize = size; pdev->valternate = pEntry->alternate; - pdev->image = pwc_image_sizes[size]; - pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2; + pdev->width = pwc_image_sizes[size][0]; + pdev->height = pwc_image_sizes[size][1]; + pdev->frame_size = (pdev->width * pdev->height * 3) / 2; if (pEntry->compressed) { if (pdev->release < 5) { /* 4 fold compression */ pdev->vbandlength = 528; @@ -237,183 +215,142 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) } else pdev->vbandlength = 0; + + /* Let pwc-if.c:isoc_init know we don't support higher compression */ + *compression = 3; + return 0; } -static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, + int frames, int *compression, int send_to_cam) { - unsigned char buf[13]; const struct Timon_table_entry *pChoose; - int ret, fps; + int fps, ret = 0; - if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) - return -EINVAL; - if (size == PSZ_VGA && frames > 15) + if (size >= PSZ_MAX || *compression < 0 || *compression > 3) return -EINVAL; + if (frames < 5) + frames = 5; + else if (size == PSZ_VGA && frames > 15) + frames = 15; + else if (frames > 30) + frames = 30; fps = (frames / 5) - 1; - /* Find a supported framerate with progressively higher compression ratios - if the preferred ratio is not available. - */ + /* Find a supported framerate with progressively higher compression */ pChoose = NULL; - while (compression <= 3) { - pChoose = &Timon_table[size][fps][compression]; - if (pChoose->alternate != 0) - break; - compression++; + while (*compression <= 3) { + pChoose = &Timon_table[size][fps][*compression]; + if (pChoose->alternate != 0) + break; + (*compression)++; } if (pChoose == NULL || pChoose->alternate == 0) return -ENOENT; /* Not supported. */ - memcpy(buf, pChoose->mode, 13); - if (snapshot) - buf[0] |= 0x80; - ret = send_video_command(pdev, pdev->vendpoint, buf, 13); + if (send_to_cam) + ret = send_video_command(pdev, pdev->vendpoint, + pChoose->mode, 13); if (ret < 0) return ret; - if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { - ret = pwc_dec23_init(pdev, pdev->type, buf); - if (ret < 0) - return ret; - } - - pdev->cmd_len = 13; - memcpy(pdev->cmd_buf, buf, 13); + if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) + pwc_dec23_init(pdev, pChoose->mode); /* Set various parameters */ - pdev->vframes = frames; - pdev->vsize = size; - pdev->vsnapshot = snapshot; + pdev->pixfmt = pixfmt; + pdev->vframes = (fps + 1) * 5; pdev->valternate = pChoose->alternate; - pdev->image = pwc_image_sizes[size]; + pdev->width = pwc_image_sizes[size][0]; + pdev->height = pwc_image_sizes[size][1]; pdev->vbandlength = pChoose->bandlength; if (pChoose->bandlength > 0) - pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; + pdev->frame_size = (pChoose->bandlength * pdev->height) / 4; else - pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + pdev->frame_size = (pdev->width * pdev->height * 12) / 8; return 0; } -static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, + int frames, int *compression, int send_to_cam) { const struct Kiara_table_entry *pChoose = NULL; - int fps, ret; - unsigned char buf[12]; - struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}; + int fps, ret = 0; - if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) - return -EINVAL; - if (size == PSZ_VGA && frames > 15) + if (size >= PSZ_MAX || *compression < 0 || *compression > 3) return -EINVAL; + if (frames < 5) + frames = 5; + else if (size == PSZ_VGA && frames > 15) + frames = 15; + else if (frames > 30) + frames = 30; fps = (frames / 5) - 1; - /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ - if (size == PSZ_VGA && frames == 5 && snapshot && pdev->pixfmt != V4L2_PIX_FMT_YUV420) - { - /* Only available in case the raw palette is selected or - we have the decompressor available. This mode is - only available in compressed form - */ - PWC_DEBUG_SIZE("Choosing VGA/5 BAYER mode.\n"); - pChoose = &RawEntry; - } - else - { - /* Find a supported framerate with progressively higher compression ratios - if the preferred ratio is not available. - Skip this step when using RAW modes. - */ - snapshot = 0; - while (compression <= 3) { - pChoose = &Kiara_table[size][fps][compression]; - if (pChoose->alternate != 0) - break; - compression++; - } + /* Find a supported framerate with progressively higher compression */ + while (*compression <= 3) { + pChoose = &Kiara_table[size][fps][*compression]; + if (pChoose->alternate != 0) + break; + (*compression)++; } if (pChoose == NULL || pChoose->alternate == 0) return -ENOENT; /* Not supported. */ - PWC_TRACE("Using alternate setting %d.\n", pChoose->alternate); - - /* usb_control_msg won't take staticly allocated arrays as argument?? */ - memcpy(buf, pChoose->mode, 12); - if (snapshot) - buf[0] |= 0x80; - /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ - ret = send_video_command(pdev, 4 /* pdev->vendpoint */, buf, 12); + if (send_to_cam) + ret = send_video_command(pdev, 4, pChoose->mode, 12); if (ret < 0) return ret; - if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { - ret = pwc_dec23_init(pdev, pdev->type, buf); - if (ret < 0) - return ret; - } + if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) + pwc_dec23_init(pdev, pChoose->mode); - pdev->cmd_len = 12; - memcpy(pdev->cmd_buf, buf, 12); /* All set and go */ - pdev->vframes = frames; - pdev->vsize = size; - pdev->vsnapshot = snapshot; + pdev->pixfmt = pixfmt; + pdev->vframes = (fps + 1) * 5; pdev->valternate = pChoose->alternate; - pdev->image = pwc_image_sizes[size]; + pdev->width = pwc_image_sizes[size][0]; + pdev->height = pwc_image_sizes[size][1]; pdev->vbandlength = pChoose->bandlength; if (pdev->vbandlength > 0) - pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; + pdev->frame_size = (pdev->vbandlength * pdev->height) / 4; else - pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; - PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vsnapshot=%d, vbandlength=%d\n", - pdev->frame_size,pdev->vframes,pdev->vsize,pdev->vsnapshot,pdev->vbandlength); + pdev->frame_size = (pdev->width * pdev->height * 12) / 8; + PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n", + pdev->frame_size, pdev->vframes, size, pdev->vbandlength); return 0; } - - -/** - @pdev: device structure - @width: viewport width - @height: viewport height - @frame: framerate, in fps - @compression: preferred compression ratio - @snapshot: snapshot mode or streaming - */ -int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) +int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, + int pixfmt, int frames, int *compression, int send_to_cam) { int ret, size; - PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", width, height, frames, pdev->pixfmt); - size = pwc_decode_size(pdev, width, height); - if (size < 0) { - PWC_DEBUG_MODULE("Could not find suitable size.\n"); - return -ERANGE; - } + PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", + width, height, frames, pixfmt); + size = pwc_get_size(pdev, width, height); PWC_TRACE("decode_size = %d.\n", size); if (DEVICE_USE_CODEC1(pdev->type)) { - ret = set_video_mode_Nala(pdev, size, frames); - + ret = set_video_mode_Nala(pdev, size, pixfmt, frames, + compression, send_to_cam); } else if (DEVICE_USE_CODEC3(pdev->type)) { - ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); - + ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, + compression, send_to_cam); } else { - ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); + ret = set_video_mode_Timon(pdev, size, pixfmt, frames, + compression, send_to_cam); } if (ret < 0) { PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); return ret; } - pdev->view.x = width; - pdev->view.y = height; - pdev->vcompression = compression; pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; - pwc_set_image_buffer_size(pdev); - PWC_DEBUG_SIZE("Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); + PWC_DEBUG_SIZE("Set resolution to %dx%d\n", pdev->width, pdev->height); return 0; } @@ -470,44 +407,15 @@ unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned i return ret; } -static void pwc_set_image_buffer_size(struct pwc_device *pdev) -{ - int factor = 0; - - /* for V4L2_PIX_FMT_YUV420 */ - switch (pdev->pixfmt) { - case V4L2_PIX_FMT_YUV420: - factor = 6; - break; - case V4L2_PIX_FMT_PWC1: - case V4L2_PIX_FMT_PWC2: - factor = 6; /* can be uncompressed YUV420P */ - break; - } - - /* Set sizes in bytes */ - pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; - pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; - - /* Align offset, or you'll get some very weird results in - YUV420 mode... x must be multiple of 4 (to get the Y's in - place), and y even (or you'll mixup U & V). This is less of a - problem for YUV420P. - */ - pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; - pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; -} - int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) { int ret; - u8 buf; - ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf)); + ret = recv_control_msg(pdev, request, value, 1); if (ret < 0) return ret; - *data = buf; + *data = pdev->ctrl_buf[0]; return 0; } @@ -515,7 +423,8 @@ int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) { int ret; - ret = send_control_msg(pdev, request, value, &data, sizeof(data)); + pdev->ctrl_buf[0] = data; + ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 1); if (ret < 0) return ret; @@ -525,37 +434,34 @@ int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) { int ret; - s8 buf; - ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf)); + ret = recv_control_msg(pdev, request, value, 1); if (ret < 0) return ret; - *data = buf; + *data = ((s8 *)pdev->ctrl_buf)[0]; return 0; } int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) { int ret; - u8 buf[2]; - ret = recv_control_msg(pdev, request, value, buf, sizeof(buf)); + ret = recv_control_msg(pdev, request, value, 2); if (ret < 0) return ret; - *data = (buf[1] << 8) | buf[0]; + *data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0]; return 0; } int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) { int ret; - u8 buf[2]; - buf[0] = data & 0xff; - buf[1] = data >> 8; - ret = send_control_msg(pdev, request, value, buf, sizeof(buf)); + pdev->ctrl_buf[0] = data & 0xff; + pdev->ctrl_buf[1] = data >> 8; + ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 2); if (ret < 0) return ret; @@ -576,7 +482,6 @@ int pwc_button_ctrl(struct pwc_device *pdev, u16 value) /* POWER */ void pwc_camera_power(struct pwc_device *pdev, int power) { - char buf; int r; if (!pdev->power_save) @@ -586,69 +491,18 @@ void pwc_camera_power(struct pwc_device *pdev, int power) return; /* Not supported by Nala or Timon < release 6 */ if (power) - buf = 0x00; /* active */ + pdev->ctrl_buf[0] = 0x00; /* active */ else - buf = 0xFF; /* power save */ - r = send_control_msg(pdev, - SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, - &buf, sizeof(buf)); - + pdev->ctrl_buf[0] = 0xFF; /* power save */ + r = send_control_msg(pdev, SET_STATUS_CTL, + SET_POWER_SAVE_MODE_FORMATTER, pdev->ctrl_buf, 1); if (r < 0) PWC_ERROR("Failed to power %s camera (%d)\n", power ? "on" : "off", r); } -static int pwc_set_wb_speed(struct pwc_device *pdev, int speed) -{ - unsigned char buf; - - /* useful range is 0x01..0x20 */ - buf = speed / 0x7f0; - return send_control_msg(pdev, - SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, &buf, sizeof(buf)); -} - -static int pwc_get_wb_speed(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = recv_control_msg(pdev, - GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - *value = buf * 0x7f0; - return 0; -} - - -static int pwc_set_wb_delay(struct pwc_device *pdev, int delay) -{ - unsigned char buf; - - /* useful range is 0x01..0x3F */ - buf = (delay >> 10); - return send_control_msg(pdev, - SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, &buf, sizeof(buf)); -} - -static int pwc_get_wb_delay(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = recv_control_msg(pdev, - GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - *value = buf << 10; - return 0; -} - - int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) { - unsigned char buf[2]; int r; if (pdev->type < 730) @@ -664,123 +518,20 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) if (off_value > 0xff) off_value = 0xff; - buf[0] = on_value; - buf[1] = off_value; + pdev->ctrl_buf[0] = on_value; + pdev->ctrl_buf[1] = off_value; r = send_control_msg(pdev, - SET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf)); + SET_STATUS_CTL, LED_FORMATTER, pdev->ctrl_buf, 2); if (r < 0) PWC_ERROR("Failed to set LED on/off time (%d)\n", r); return r; } -static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) -{ - unsigned char buf[2]; - int ret; - - if (pdev->type < 730) { - *on_value = -1; - *off_value = -1; - return 0; - } - - ret = recv_control_msg(pdev, - GET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - *on_value = buf[0] * 100; - *off_value = buf[1] * 100; - return 0; -} - -static int _pwc_mpt_reset(struct pwc_device *pdev, int flags) -{ - unsigned char buf; - - buf = flags & 0x03; // only lower two bits are currently used - return send_control_msg(pdev, - SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, &buf, sizeof(buf)); -} - -int pwc_mpt_reset(struct pwc_device *pdev, int flags) -{ - int ret; - ret = _pwc_mpt_reset(pdev, flags); - if (ret >= 0) { - pdev->pan_angle = 0; - pdev->tilt_angle = 0; - } - return ret; -} - -static int _pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) -{ - unsigned char buf[4]; - - /* set new relative angle; angles are expressed in degrees * 100, - but cam as .5 degree resolution, hence divide by 200. Also - the angle must be multiplied by 64 before it's send to - the cam (??) - */ - pan = 64 * pan / 100; - tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ - buf[0] = pan & 0xFF; - buf[1] = (pan >> 8) & 0xFF; - buf[2] = tilt & 0xFF; - buf[3] = (tilt >> 8) & 0xFF; - return send_control_msg(pdev, - SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, &buf, sizeof(buf)); -} - -int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) -{ - int ret; - - /* check absolute ranges */ - if (pan < pdev->angle_range.pan_min || - pan > pdev->angle_range.pan_max || - tilt < pdev->angle_range.tilt_min || - tilt > pdev->angle_range.tilt_max) - return -ERANGE; - - /* go to relative range, check again */ - pan -= pdev->pan_angle; - tilt -= pdev->tilt_angle; - /* angles are specified in degrees * 100, thus the limit = 36000 */ - if (pan < -36000 || pan > 36000 || tilt < -36000 || tilt > 36000) - return -ERANGE; - - ret = _pwc_mpt_set_angle(pdev, pan, tilt); - if (ret >= 0) { - pdev->pan_angle += pan; - pdev->tilt_angle += tilt; - } - if (ret == -EPIPE) /* stall -> out of range */ - ret = -ERANGE; - return ret; -} - -static int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) -{ - int ret; - unsigned char buf[5]; - - ret = recv_control_msg(pdev, - GET_MPT_CTL, PT_STATUS_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - status->status = buf[0] & 0x7; // 3 bits are used for reporting - status->time_pan = (buf[1] << 8) + buf[2]; - status->time_tilt = (buf[3] << 8) + buf[4]; - return 0; -} - #ifdef CONFIG_USB_PWC_DEBUG int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) { - unsigned char buf; int ret = -1, request; if (pdev->type < 675) @@ -790,431 +541,13 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) else request = SENSOR_TYPE_FORMATTER2; - ret = recv_control_msg(pdev, - GET_STATUS_CTL, request, &buf, sizeof(buf)); + ret = recv_control_msg(pdev, GET_STATUS_CTL, request, 1); if (ret < 0) return ret; if (pdev->type < 675) - *sensor = buf | 0x100; + *sensor = pdev->ctrl_buf[0] | 0x100; else - *sensor = buf; + *sensor = pdev->ctrl_buf[0]; return 0; } #endif - - /* End of Add-Ons */ - /* ************************************************* */ - -/* Linux 2.5.something and 2.6 pass direct pointers to arguments of - ioctl() calls. With 2.4, you have to do tedious copy_from_user() - and copy_to_user() calls. With these macros we circumvent this, - and let me maintain only one source file. The functionality is - exactly the same otherwise. - */ - -/* define local variable for arg */ -#define ARG_DEF(ARG_type, ARG_name)\ - ARG_type *ARG_name = arg; -/* copy arg to local variable */ -#define ARG_IN(ARG_name) /* nothing */ -/* argument itself (referenced) */ -#define ARGR(ARG_name) (*ARG_name) -/* argument address */ -#define ARGA(ARG_name) ARG_name -/* copy local variable to arg */ -#define ARG_OUT(ARG_name) /* nothing */ - -/* - * Our ctrls use native values, but the old custom pwc ioctl interface expects - * values from 0 - 65535, define 2 helper functions to scale things. */ -static int pwc_ioctl_g_ctrl(struct v4l2_ctrl *ctrl) -{ - return v4l2_ctrl_g_ctrl(ctrl) * 65535 / ctrl->maximum; -} - -static int pwc_ioctl_s_ctrl(struct v4l2_ctrl *ctrl, int val) -{ - return v4l2_ctrl_s_ctrl(ctrl, val * ctrl->maximum / 65535); -} - -long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) -{ - long ret = 0; - - switch(cmd) { - case VIDIOCPWCRUSER: - ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER); - break; - - case VIDIOCPWCSUSER: - ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER); - break; - - case VIDIOCPWCFACTORY: - ret = pwc_button_ctrl(pdev, RESTORE_FACTORY_DEFAULTS_FORMATTER); - break; - - case VIDIOCPWCSCQUAL: - { - ARG_DEF(int, qual) - - if (vb2_is_streaming(&pdev->vb_queue)) { - ret = -EBUSY; - break; - } - - ARG_IN(qual) - if (ARGR(qual) < 0 || ARGR(qual) > 3) - ret = -EINVAL; - else - ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot); - break; - } - - case VIDIOCPWCGCQUAL: - { - ARG_DEF(int, qual) - - ARGR(qual) = pdev->vcompression; - ARG_OUT(qual) - break; - } - - case VIDIOCPWCPROBE: - { - ARG_DEF(struct pwc_probe, probe) - - strcpy(ARGR(probe).name, pdev->vdev.name); - ARGR(probe).type = pdev->type; - ARG_OUT(probe) - break; - } - - case VIDIOCPWCGSERIAL: - { - ARG_DEF(struct pwc_serial, serial) - - strcpy(ARGR(serial).serial, pdev->serial); - ARG_OUT(serial) - break; - } - - case VIDIOCPWCSAGC: - { - ARG_DEF(int, agc) - ARG_IN(agc) - ret = v4l2_ctrl_s_ctrl(pdev->autogain, ARGR(agc) < 0); - if (ret == 0 && ARGR(agc) >= 0) - ret = pwc_ioctl_s_ctrl(pdev->gain, ARGR(agc)); - break; - } - - case VIDIOCPWCGAGC: - { - ARG_DEF(int, agc) - if (v4l2_ctrl_g_ctrl(pdev->autogain)) - ARGR(agc) = -1; - else - ARGR(agc) = pwc_ioctl_g_ctrl(pdev->gain); - ARG_OUT(agc) - break; - } - - case VIDIOCPWCSSHUTTER: - { - ARG_DEF(int, shutter) - ARG_IN(shutter) - ret = v4l2_ctrl_s_ctrl(pdev->exposure_auto, - /* Menu idx 0 = auto, idx 1 = manual */ - ARGR(shutter) >= 0); - if (ret == 0 && ARGR(shutter) >= 0) - ret = pwc_ioctl_s_ctrl(pdev->exposure, ARGR(shutter)); - break; - } - - case VIDIOCPWCSAWB: - { - ARG_DEF(struct pwc_whitebalance, wb) - ARG_IN(wb) - ret = v4l2_ctrl_s_ctrl(pdev->auto_white_balance, - ARGR(wb).mode); - if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL) - ret = pwc_ioctl_s_ctrl(pdev->red_balance, - ARGR(wb).manual_red); - if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL) - ret = pwc_ioctl_s_ctrl(pdev->blue_balance, - ARGR(wb).manual_blue); - break; - } - - case VIDIOCPWCGAWB: - { - ARG_DEF(struct pwc_whitebalance, wb) - ARGR(wb).mode = v4l2_ctrl_g_ctrl(pdev->auto_white_balance); - ARGR(wb).manual_red = ARGR(wb).read_red = - pwc_ioctl_g_ctrl(pdev->red_balance); - ARGR(wb).manual_blue = ARGR(wb).read_blue = - pwc_ioctl_g_ctrl(pdev->blue_balance); - ARG_OUT(wb) - break; - } - - case VIDIOCPWCSAWBSPEED: - { - ARG_DEF(struct pwc_wb_speed, wbs) - - if (ARGR(wbs).control_speed > 0) { - ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed); - } - if (ARGR(wbs).control_delay > 0) { - ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay); - } - break; - } - - case VIDIOCPWCGAWBSPEED: - { - ARG_DEF(struct pwc_wb_speed, wbs) - - ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed); - if (ret < 0) - break; - ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay); - if (ret < 0) - break; - ARG_OUT(wbs) - break; - } - - case VIDIOCPWCSLED: - { - ARG_DEF(struct pwc_leds, leds) - - ARG_IN(leds) - ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off); - break; - } - - - case VIDIOCPWCGLED: - { - ARG_DEF(struct pwc_leds, leds) - - ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off); - ARG_OUT(leds) - break; - } - - case VIDIOCPWCSCONTOUR: - { - ARG_DEF(int, contour) - ARG_IN(contour) - ret = v4l2_ctrl_s_ctrl(pdev->autocontour, ARGR(contour) < 0); - if (ret == 0 && ARGR(contour) >= 0) - ret = pwc_ioctl_s_ctrl(pdev->contour, ARGR(contour)); - break; - } - - case VIDIOCPWCGCONTOUR: - { - ARG_DEF(int, contour) - if (v4l2_ctrl_g_ctrl(pdev->autocontour)) - ARGR(contour) = -1; - else - ARGR(contour) = pwc_ioctl_g_ctrl(pdev->contour); - ARG_OUT(contour) - break; - } - - case VIDIOCPWCSBACKLIGHT: - { - ARG_DEF(int, backlight) - ARG_IN(backlight) - ret = v4l2_ctrl_s_ctrl(pdev->backlight, ARGR(backlight)); - break; - } - - case VIDIOCPWCGBACKLIGHT: - { - ARG_DEF(int, backlight) - ARGR(backlight) = v4l2_ctrl_g_ctrl(pdev->backlight); - ARG_OUT(backlight) - break; - } - - case VIDIOCPWCSFLICKER: - { - ARG_DEF(int, flicker) - ARG_IN(flicker) - ret = v4l2_ctrl_s_ctrl(pdev->flicker, ARGR(flicker)); - break; - } - - case VIDIOCPWCGFLICKER: - { - ARG_DEF(int, flicker) - ARGR(flicker) = v4l2_ctrl_g_ctrl(pdev->flicker); - ARG_OUT(flicker) - break; - } - - case VIDIOCPWCSDYNNOISE: - { - ARG_DEF(int, dynnoise) - ARG_IN(dynnoise) - ret = v4l2_ctrl_s_ctrl(pdev->noise_reduction, ARGR(dynnoise)); - break; - } - - case VIDIOCPWCGDYNNOISE: - { - ARG_DEF(int, dynnoise) - ARGR(dynnoise) = v4l2_ctrl_g_ctrl(pdev->noise_reduction); - ARG_OUT(dynnoise); - break; - } - - case VIDIOCPWCGREALSIZE: - { - ARG_DEF(struct pwc_imagesize, size) - - ARGR(size).width = pdev->image.x; - ARGR(size).height = pdev->image.y; - ARG_OUT(size) - break; - } - - case VIDIOCPWCMPTRESET: - { - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(int, flags) - - ARG_IN(flags) - ret = pwc_mpt_reset(pdev, ARGR(flags)); - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCMPTGRANGE: - { - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(struct pwc_mpt_range, range) - - ARGR(range) = pdev->angle_range; - ARG_OUT(range) - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCMPTSANGLE: - { - int new_pan, new_tilt; - - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(struct pwc_mpt_angles, angles) - - ARG_IN(angles) - /* The camera can only set relative angles, so - do some calculations when getting an absolute angle . - */ - if (ARGR(angles).absolute) - { - new_pan = ARGR(angles).pan; - new_tilt = ARGR(angles).tilt; - } - else - { - new_pan = pdev->pan_angle + ARGR(angles).pan; - new_tilt = pdev->tilt_angle + ARGR(angles).tilt; - } - ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCMPTGANGLE: - { - - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(struct pwc_mpt_angles, angles) - - ARGR(angles).absolute = 1; - ARGR(angles).pan = pdev->pan_angle; - ARGR(angles).tilt = pdev->tilt_angle; - ARG_OUT(angles) - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCMPTSTATUS: - { - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(struct pwc_mpt_status, status) - - ret = pwc_mpt_get_status(pdev, ARGA(status)); - ARG_OUT(status) - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCGVIDCMD: - { - ARG_DEF(struct pwc_video_command, vcmd); - - ARGR(vcmd).type = pdev->type; - ARGR(vcmd).release = pdev->release; - ARGR(vcmd).command_len = pdev->cmd_len; - memcpy(&ARGR(vcmd).command_buf, pdev->cmd_buf, pdev->cmd_len); - ARGR(vcmd).bandlength = pdev->vbandlength; - ARGR(vcmd).frame_size = pdev->frame_size; - ARG_OUT(vcmd) - break; - } - /* - case VIDIOCPWCGVIDTABLE: - { - ARG_DEF(struct pwc_table_init_buffer, table); - ARGR(table).len = pdev->cmd_len; - memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size); - ARG_OUT(table) - break; - } - */ - - default: - ret = -ENOIOCTLCMD; - break; - } - - if (ret > 0) - return 0; - return ret; -} - - -/* vim: set cinoptions= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/drivers/media/video/pwc/pwc-dec1.c b/drivers/media/video/pwc/pwc-dec1.c index be0e02c..e899036 100644 --- a/drivers/media/video/pwc/pwc-dec1.c +++ b/drivers/media/video/pwc/pwc-dec1.c @@ -22,19 +22,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "pwc-dec1.h" +#include "pwc.h" -int pwc_dec1_init(struct pwc_device *pwc, int type, int release, void *buffer) +void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd) { - struct pwc_dec1_private *pdec; + struct pwc_dec1_private *pdec = &pdev->dec1; - if (pwc->decompress_data == NULL) { - pdec = kmalloc(sizeof(struct pwc_dec1_private), GFP_KERNEL); - if (pdec == NULL) - return -ENOMEM; - pwc->decompress_data = pdec; - } - pdec = pwc->decompress_data; - - return 0; + pdec->version = pdev->release; } diff --git a/drivers/media/video/pwc/pwc-dec1.h b/drivers/media/video/pwc/pwc-dec1.h index a57d860..c565ef8 100644 --- a/drivers/media/video/pwc/pwc-dec1.h +++ b/drivers/media/video/pwc/pwc-dec1.h @@ -25,13 +25,15 @@ #ifndef PWC_DEC1_H #define PWC_DEC1_H -#include "pwc.h" +#include <linux/mutex.h> + +struct pwc_device; struct pwc_dec1_private { int version; }; -int pwc_dec1_init(struct pwc_device *pwc, int type, int release, void *buffer); +void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd); #endif diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/video/pwc/pwc-dec23.c index 06a4e87..3792fed 100644 --- a/drivers/media/video/pwc/pwc-dec23.c +++ b/drivers/media/video/pwc/pwc-dec23.c @@ -27,7 +27,6 @@ #include "pwc-timon.h" #include "pwc-kiara.h" #include "pwc-dec23.h" -#include <media/pwc-ioctl.h> #include <linux/string.h> #include <linux/slab.h> @@ -51,13 +50,6 @@ # define USE_LOOKUP_TABLE_TO_CLAMP 1 #endif -/* - * ENABLE_BAYER_DECODER - * 0: bayer decoder is not build (save some space) - * 1: bayer decoder is build and can be used - */ -#define ENABLE_BAYER_DECODER 0 - static void build_subblock_pattern(struct pwc_dec23_private *pdec) { static const unsigned int initial_values[12] = { @@ -302,20 +294,17 @@ static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE]; /* If the type or the command change, we rebuild the lookup table */ -int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd) +void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd) { int flags, version, shift, i; - struct pwc_dec23_private *pdec; + struct pwc_dec23_private *pdec = &pdev->dec23; - if (pwc->decompress_data == NULL) { - pdec = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); - if (pdec == NULL) - return -ENOMEM; - pwc->decompress_data = pdec; - } - pdec = pwc->decompress_data; + mutex_init(&pdec->lock); - if (DEVICE_USE_CODEC3(type)) { + if (pdec->last_cmd_valid && pdec->last_cmd == cmd[2]) + return; + + if (DEVICE_USE_CODEC3(pdev->type)) { flags = cmd[2] & 0x18; if (flags == 8) pdec->nbits = 7; /* More bits, mean more bits to encode the stream, but better quality */ @@ -362,7 +351,8 @@ int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd) pwc_crop_table[MAX_OUTER_CROP_VALUE+256+i] = 255; #endif - return 0; + pdec->last_cmd = cmd[2]; + pdec->last_cmd_valid = 1; } /* @@ -467,123 +457,6 @@ static void copy_image_block_CrCb(const int *src, unsigned char *dst, unsigned i #endif } -#if ENABLE_BAYER_DECODER -/* - * Format: 8x2 pixels - * . G . G . G . G . G . G . G - * . . . . . . . . . . . . . . - * . G . G . G . G . G . G . G - * . . . . . . . . . . . . . . - * or - * . . . . . . . . . . . . . . - * G . G . G . G . G . G . G . - * . . . . . . . . . . . . . . - * G . G . G . G . G . G . G . -*/ -static void copy_image_block_Green(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) -{ -#if UNROLL_LOOP_FOR_COPY - /* Unroll all loops */ - const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; - unsigned char *d = dst; - const int *c = src; - - d[0] = cm[c[0] >> scalebits]; - d[2] = cm[c[1] >> scalebits]; - d[4] = cm[c[2] >> scalebits]; - d[6] = cm[c[3] >> scalebits]; - d[8] = cm[c[4] >> scalebits]; - d[10] = cm[c[5] >> scalebits]; - d[12] = cm[c[6] >> scalebits]; - d[14] = cm[c[7] >> scalebits]; - - d = dst + bytes_per_line; - d[0] = cm[c[8] >> scalebits]; - d[2] = cm[c[9] >> scalebits]; - d[4] = cm[c[10] >> scalebits]; - d[6] = cm[c[11] >> scalebits]; - d[8] = cm[c[12] >> scalebits]; - d[10] = cm[c[13] >> scalebits]; - d[12] = cm[c[14] >> scalebits]; - d[14] = cm[c[15] >> scalebits]; -#else - int i; - unsigned char *d; - const int *c = src; - - d = dst; - for (i = 0; i < 8; i++, c++) - d[i*2] = CLAMP((*c) >> scalebits); - - d = dst + bytes_per_line; - for (i = 0; i < 8; i++, c++) - d[i*2] = CLAMP((*c) >> scalebits); -#endif -} -#endif - -#if ENABLE_BAYER_DECODER -/* - * Format: 4x4 pixels - * R . R . R . R - * . B . B . B . - * R . R . R . R - * . B . B . B . - */ -static void copy_image_block_RedBlue(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) -{ -#if UNROLL_LOOP_FOR_COPY - /* Unroll all loops */ - const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; - unsigned char *d = dst; - const int *c = src; - - d[0] = cm[c[0] >> scalebits]; - d[2] = cm[c[1] >> scalebits]; - d[4] = cm[c[2] >> scalebits]; - d[6] = cm[c[3] >> scalebits]; - - d = dst + bytes_per_line; - d[1] = cm[c[4] >> scalebits]; - d[3] = cm[c[5] >> scalebits]; - d[5] = cm[c[6] >> scalebits]; - d[7] = cm[c[7] >> scalebits]; - - d = dst + bytes_per_line*2; - d[0] = cm[c[8] >> scalebits]; - d[2] = cm[c[9] >> scalebits]; - d[4] = cm[c[10] >> scalebits]; - d[6] = cm[c[11] >> scalebits]; - - d = dst + bytes_per_line*3; - d[1] = cm[c[12] >> scalebits]; - d[3] = cm[c[13] >> scalebits]; - d[5] = cm[c[14] >> scalebits]; - d[7] = cm[c[15] >> scalebits]; -#else - int i; - unsigned char *d; - const int *c = src; - - d = dst; - for (i = 0; i < 4; i++, c++) - d[i*2] = CLAMP((*c) >> scalebits); - - d = dst + bytes_per_line; - for (i = 0; i < 4; i++, c++) - d[i*2+1] = CLAMP((*c) >> scalebits); - - d = dst + bytes_per_line*2; - for (i = 0; i < 4; i++, c++) - d[i*2] = CLAMP((*c) >> scalebits); - - d = dst + bytes_per_line*3; - for (i = 0; i < 4; i++, c++) - d[i*2+1] = CLAMP((*c) >> scalebits); -#endif -} -#endif - /* * To manage the stream, we keep bits in a 32 bits register. * fill_nbits(n): fill the reservoir with at least n bits @@ -775,146 +648,44 @@ static void DecompressBand23(struct pwc_dec23_private *pdec, } -#if ENABLE_BAYER_DECODER -/* - * Size need to be a multiple of 8 in width - * - * Return a block of four line encoded like this: - * - * G R G R G R G R G R G R G R G R - * B G B G B G B G B G B G B G B G - * G R G R G R G R G R G R G R G R - * B G B G B G B G B G B G B G B G - * - */ -static void DecompressBandBayer(struct pwc_dec23_private *pdec, - const unsigned char *rawyuv, - unsigned char *rgbbayer, - unsigned int compressed_image_width, - unsigned int real_image_width) -{ - int compression_index, nblocks; - const unsigned char *ptable0004; - const unsigned char *ptable8004; - unsigned char *dest; - - pdec->reservoir = 0; - pdec->nbits_in_reservoir = 0; - pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */ - - get_nbits(pdec, 4, compression_index); - - /* pass 1: uncompress RB component */ - nblocks = compressed_image_width / 4; - - ptable0004 = pdec->table_0004_pass1[compression_index]; - ptable8004 = pdec->table_8004_pass1[compression_index]; - dest = rgbbayer; - - /* Each block decode a square of 4x4 */ - while (nblocks) { - decode_block(pdec, ptable0004, ptable8004); - copy_image_block_RedBlue(pdec->temp_colors, rgbbayer, real_image_width, pdec->scalebits); - dest += 8; - nblocks--; - } - - /* pass 2: uncompress G component */ - nblocks = compressed_image_width / 8; - - ptable0004 = pdec->table_0004_pass2[compression_index]; - ptable8004 = pdec->table_8004_pass2[compression_index]; - - /* Each block decode a square of 4x4 */ - while (nblocks) { - decode_block(pdec, ptable0004, ptable8004); - copy_image_block_Green(pdec->temp_colors, rgbbayer+1, real_image_width, pdec->scalebits); - - decode_block(pdec, ptable0004, ptable8004); - copy_image_block_Green(pdec->temp_colors, rgbbayer+real_image_width, real_image_width, pdec->scalebits); - - rgbbayer += 16; - nblocks -= 2; - } -} -#endif - - /** * * Uncompress a pwc23 buffer. * - * pwc.view: size of the image wanted - * pwc.image: size of the image returned by the camera - * pwc.offset: (x,y) to displayer image in the view - * * src: raw data * dst: image output - * flags: PWCX_FLAG_PLANAR or PWCX_FLAG_BAYER */ -void pwc_dec23_decompress(const struct pwc_device *pwc, +void pwc_dec23_decompress(struct pwc_device *pdev, const void *src, - void *dst, - int flags) + void *dst) { - int bandlines_left, stride, bytes_per_block; - - bandlines_left = pwc->image.y / 4; - bytes_per_block = pwc->view.x * 4; - - if (flags & PWCX_FLAG_BAYER) { -#if ENABLE_BAYER_DECODER - /* RGB Bayer format */ - unsigned char *rgbout; - - stride = pwc->view.x * pwc->offset.y; - rgbout = dst + stride + pwc->offset.x; - - - while (bandlines_left--) { - - DecompressBandBayer(pwc->decompress_data, - src, - rgbout, - pwc->image.x, pwc->view.x); - - src += pwc->vbandlength; - rgbout += bytes_per_block; - - } -#else - memset(dst, 0, pwc->view.x * pwc->view.y); -#endif - - } else { - /* YUV420P image format */ - unsigned char *pout_planar_y; - unsigned char *pout_planar_u; - unsigned char *pout_planar_v; - unsigned int plane_size; - - plane_size = pwc->view.x * pwc->view.y; - - /* offset in Y plane */ - stride = pwc->view.x * pwc->offset.y; - pout_planar_y = dst + stride + pwc->offset.x; - - /* offsets in U/V planes */ - stride = (pwc->view.x * pwc->offset.y) / 4 + pwc->offset.x / 2; - pout_planar_u = dst + plane_size + stride; - pout_planar_v = dst + plane_size + plane_size / 4 + stride; - - while (bandlines_left--) { - - DecompressBand23(pwc->decompress_data, - src, - pout_planar_y, pout_planar_u, pout_planar_v, - pwc->image.x, pwc->view.x); - src += pwc->vbandlength; - pout_planar_y += bytes_per_block; - pout_planar_u += pwc->view.x; - pout_planar_v += pwc->view.x; - - } + int bandlines_left, bytes_per_block; + struct pwc_dec23_private *pdec = &pdev->dec23; + + /* YUV420P image format */ + unsigned char *pout_planar_y; + unsigned char *pout_planar_u; + unsigned char *pout_planar_v; + unsigned int plane_size; + + mutex_lock(&pdec->lock); + + bandlines_left = pdev->height / 4; + bytes_per_block = pdev->width * 4; + plane_size = pdev->height * pdev->width; + + pout_planar_y = dst; + pout_planar_u = dst + plane_size; + pout_planar_v = dst + plane_size + plane_size / 4; + + while (bandlines_left--) { + DecompressBand23(pdec, src, + pout_planar_y, pout_planar_u, pout_planar_v, + pdev->width, pdev->width); + src += pdev->vbandlength; + pout_planar_y += bytes_per_block; + pout_planar_u += pdev->width; + pout_planar_v += pdev->width; } + mutex_unlock(&pdec->lock); } diff --git a/drivers/media/video/pwc/pwc-dec23.h b/drivers/media/video/pwc/pwc-dec23.h index a0ac4f3..c655b1c 100644 --- a/drivers/media/video/pwc/pwc-dec23.h +++ b/drivers/media/video/pwc/pwc-dec23.h @@ -25,15 +25,20 @@ #ifndef PWC_DEC23_H #define PWC_DEC23_H -#include "pwc.h" +struct pwc_device; struct pwc_dec23_private { + struct mutex lock; + + unsigned char last_cmd, last_cmd_valid; + unsigned int scalebits; unsigned int nbitsmask, nbits; /* Number of bits of a color in the compressed stream */ unsigned int reservoir; unsigned int nbits_in_reservoir; + const unsigned char *stream; int temp_colors[16]; @@ -49,9 +54,8 @@ struct pwc_dec23_private }; -int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd); -void pwc_dec23_decompress(const struct pwc_device *pwc, +void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd); +void pwc_dec23_decompress(struct pwc_device *pdev, const void *src, - void *dst, - int flags); + void *dst); #endif diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 01ff643..122fbd0 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -128,33 +128,23 @@ static struct usb_driver pwc_driver = { #define MAX_DEV_HINTS 20 #define MAX_ISOC_ERRORS 20 -static int default_fps = 10; #ifdef CONFIG_USB_PWC_DEBUG int pwc_trace = PWC_DEBUG_LEVEL; #endif static int power_save = -1; -static int led_on = 100, led_off; /* defaults to LED that is on while in use */ -static int pwc_preferred_compression = 1; /* 0..3 = uncompressed..high */ -static struct { - int type; - char serial_number[30]; - int device_node; - struct pwc_device *pdev; -} device_hint[MAX_DEV_HINTS]; +static int leds[2] = { 100, 0 }; /***/ -static int pwc_video_open(struct file *file); static int pwc_video_close(struct file *file); static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); static unsigned int pwc_video_poll(struct file *file, poll_table *wait); static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); -static void pwc_video_release(struct video_device *vfd); static const struct v4l2_file_operations pwc_fops = { .owner = THIS_MODULE, - .open = pwc_video_open, + .open = v4l2_fh_open, .release = pwc_video_close, .read = pwc_video_read, .poll = pwc_video_poll, @@ -163,7 +153,7 @@ static const struct v4l2_file_operations pwc_fops = { }; static struct video_device pwc_template = { .name = "Philips Webcam", /* Filled in later */ - .release = pwc_video_release, + .release = video_device_release_empty, .fops = &pwc_fops, .ioctl_ops = &pwc_ioctl_ops, }; @@ -191,7 +181,6 @@ static void pwc_snapshot_button(struct pwc_device *pdev, int down) { if (down) { PWC_TRACE("Snapshot button pressed.\n"); - pdev->snapshot_button_status = 1; } else { PWC_TRACE("Snapshot button released.\n"); } @@ -375,6 +364,7 @@ static int pwc_isoc_init(struct pwc_device *pdev) int i, j, ret; struct usb_interface *intf; struct usb_host_interface *idesc = NULL; + int compression = 0; /* 0..3 = uncompressed..high */ if (pdev->iso_init) return 0; @@ -386,6 +376,12 @@ static int pwc_isoc_init(struct pwc_device *pdev) pdev->visoc_errors = 0; udev = pdev->udev; +retry: + /* We first try with low compression and then retry with a higher + compression setting if there is not enough bandwidth. */ + ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, + pdev->vframes, &compression, 1); + /* Get the current alternate interface, adjust packet size */ intf = usb_ifnum_to_if(udev, 0); if (intf) @@ -408,9 +404,12 @@ static int pwc_isoc_init(struct pwc_device *pdev) } /* Set alternate interface */ - ret = 0; PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate); ret = usb_set_interface(pdev->udev, 0, pdev->valternate); + if (ret == -ENOSPC && compression < 3) { + compression++; + goto retry; + } if (ret < 0) return ret; @@ -454,6 +453,12 @@ static int pwc_isoc_init(struct pwc_device *pdev) /* link */ for (i = 0; i < MAX_ISO_BUFS; i++) { ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL); + if (ret == -ENOSPC && compression < 3) { + compression++; + pdev->iso_init = 1; + pwc_isoc_cleanup(pdev); + goto retry; + } if (ret) { PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret); pdev->iso_init = 1; @@ -517,12 +522,11 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev) PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); } -/* - * Release all queued buffers, no need to take queued_bufs_lock, since all - * iso urbs have been killed when we're called so pwc_isoc_handler won't run. - */ static void pwc_cleanup_queued_bufs(struct pwc_device *pdev) { + unsigned long flags = 0; + + spin_lock_irqsave(&pdev->queued_bufs_lock, flags); while (!list_empty(&pdev->queued_bufs)) { struct pwc_frame_buf *buf; @@ -531,84 +535,7 @@ static void pwc_cleanup_queued_bufs(struct pwc_device *pdev) list_del(&buf->list); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } -} - -/********* - * sysfs - *********/ -static struct pwc_device *cd_to_pwc(struct device *cd) -{ - struct video_device *vdev = to_video_device(cd); - return video_get_drvdata(vdev); -} - -static ssize_t show_pan_tilt(struct device *class_dev, - struct device_attribute *attr, char *buf) -{ - struct pwc_device *pdev = cd_to_pwc(class_dev); - return sprintf(buf, "%d %d\n", pdev->pan_angle, pdev->tilt_angle); -} - -static ssize_t store_pan_tilt(struct device *class_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct pwc_device *pdev = cd_to_pwc(class_dev); - int pan, tilt; - int ret = -EINVAL; - - if (strncmp(buf, "reset", 5) == 0) - ret = pwc_mpt_reset(pdev, 0x3); - - else if (sscanf(buf, "%d %d", &pan, &tilt) > 0) - ret = pwc_mpt_set_angle(pdev, pan, tilt); - - if (ret < 0) - return ret; - return strlen(buf); -} -static DEVICE_ATTR(pan_tilt, S_IRUGO | S_IWUSR, show_pan_tilt, - store_pan_tilt); - -static ssize_t show_snapshot_button_status(struct device *class_dev, - struct device_attribute *attr, char *buf) -{ - struct pwc_device *pdev = cd_to_pwc(class_dev); - int status = pdev->snapshot_button_status; - pdev->snapshot_button_status = 0; - return sprintf(buf, "%d\n", status); -} - -static DEVICE_ATTR(button, S_IRUGO | S_IWUSR, show_snapshot_button_status, - NULL); - -static int pwc_create_sysfs_files(struct pwc_device *pdev) -{ - int rc; - - rc = device_create_file(&pdev->vdev.dev, &dev_attr_button); - if (rc) - goto err; - if (pdev->features & FEATURE_MOTOR_PANTILT) { - rc = device_create_file(&pdev->vdev.dev, &dev_attr_pan_tilt); - if (rc) - goto err_button; - } - - return 0; - -err_button: - device_remove_file(&pdev->vdev.dev, &dev_attr_button); -err: - PWC_ERROR("Could not create sysfs files.\n"); - return rc; -} - -static void pwc_remove_sysfs_files(struct pwc_device *pdev) -{ - if (pdev->features & FEATURE_MOTOR_PANTILT) - device_remove_file(&pdev->vdev.dev, &dev_attr_pan_tilt); - device_remove_file(&pdev->vdev.dev, &dev_attr_button); + spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); } #ifdef CONFIG_USB_PWC_DEBUG @@ -644,85 +571,60 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type) /***************************************************************************/ /* Video4Linux functions */ -static int pwc_video_open(struct file *file) +int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file) { - struct video_device *vdev = video_devdata(file); - struct pwc_device *pdev; - - PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev); - - pdev = video_get_drvdata(vdev); - if (!pdev->udev) - return -ENODEV; + int r = 0; - file->private_data = vdev; - PWC_DEBUG_OPEN("<< video_open() returns 0.\n"); - return 0; + mutex_lock(&pdev->capt_file_lock); + if (pdev->capt_file != NULL && + pdev->capt_file != file) { + r = -EBUSY; + goto leave; + } + pdev->capt_file = file; +leave: + mutex_unlock(&pdev->capt_file_lock); + return r; } -static void pwc_video_release(struct video_device *vfd) +static void pwc_video_release(struct v4l2_device *v) { - struct pwc_device *pdev = container_of(vfd, struct pwc_device, vdev); - int hint; - - /* search device_hint[] table if we occupy a slot, by any chance */ - for (hint = 0; hint < MAX_DEV_HINTS; hint++) - if (device_hint[hint].pdev == pdev) - device_hint[hint].pdev = NULL; - - /* Free intermediate decompression buffer & tables */ - if (pdev->decompress_data != NULL) { - PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", - pdev->decompress_data); - kfree(pdev->decompress_data); - pdev->decompress_data = NULL; - } + struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); v4l2_ctrl_handler_free(&pdev->ctrl_handler); - + kfree(pdev->ctrl_buf); kfree(pdev); } static int pwc_video_close(struct file *file) { - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - - PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); + struct pwc_device *pdev = video_drvdata(file); - pdev = video_get_drvdata(vdev); if (pdev->capt_file == file) { vb2_queue_release(&pdev->vb_queue); pdev->capt_file = NULL; } - - PWC_DEBUG_OPEN("<< video_close()\n"); - return 0; + return v4l2_fh_release(file); } static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct video_device *vdev = file->private_data; - struct pwc_device *pdev = video_get_drvdata(vdev); + struct pwc_device *pdev = video_drvdata(file); if (!pdev->udev) return -ENODEV; - if (pdev->capt_file != NULL && - pdev->capt_file != file) + if (pwc_test_n_set_capt_file(pdev, file)) return -EBUSY; - pdev->capt_file = file; - return vb2_read(&pdev->vb_queue, buf, count, ppos, file->f_flags & O_NONBLOCK); } static unsigned int pwc_video_poll(struct file *file, poll_table *wait) { - struct video_device *vdev = file->private_data; - struct pwc_device *pdev = video_get_drvdata(vdev); + struct pwc_device *pdev = video_drvdata(file); if (!pdev->udev) return POLL_ERR; @@ -732,8 +634,7 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait) static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) { - struct video_device *vdev = file->private_data; - struct pwc_device *pdev = video_get_drvdata(vdev); + struct pwc_device *pdev = video_drvdata(file); if (pdev->capt_file != file) return -EBUSY; @@ -749,6 +650,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, unsigned int sizes[], void *alloc_ctxs[]) { struct pwc_device *pdev = vb2_get_drv_priv(vq); + int size; if (*nbuffers < MIN_FRAMES) *nbuffers = MIN_FRAMES; @@ -757,7 +659,9 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, *nplanes = 1; - sizes[0] = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2); + size = pwc_get_size(pdev, MAX_WIDTH, MAX_HEIGHT); + sizes[0] = PAGE_ALIGN(pwc_image_sizes[size][0] * + pwc_image_sizes[size][1] * 3 / 2); return 0; } @@ -812,56 +716,59 @@ static void buffer_queue(struct vb2_buffer *vb) unsigned long flags = 0; spin_lock_irqsave(&pdev->queued_bufs_lock, flags); - list_add_tail(&buf->list, &pdev->queued_bufs); + /* Check the device has not disconnected between prep and queuing */ + if (pdev->udev) + list_add_tail(&buf->list, &pdev->queued_bufs); + else + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); } static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct pwc_device *pdev = vb2_get_drv_priv(vq); + int r; - if (!pdev->udev) - return -ENODEV; + mutex_lock(&pdev->udevlock); + if (!pdev->udev) { + r = -ENODEV; + goto leave; + } /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); - if (pdev->power_save) { - /* Restore video mode */ - pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, - pdev->vframes, pdev->vcompression, - pdev->vsnapshot); - } - pwc_set_leds(pdev, led_on, led_off); + pwc_set_leds(pdev, leds[0], leds[1]); - return pwc_isoc_init(pdev); + r = pwc_isoc_init(pdev); + if (r) { + /* If we failed turn camera and LEDS back off */ + pwc_set_leds(pdev, 0, 0); + pwc_camera_power(pdev, 0); + /* And cleanup any queued bufs!! */ + pwc_cleanup_queued_bufs(pdev); + } +leave: + mutex_unlock(&pdev->udevlock); + return r; } static int stop_streaming(struct vb2_queue *vq) { struct pwc_device *pdev = vb2_get_drv_priv(vq); + mutex_lock(&pdev->udevlock); if (pdev->udev) { pwc_set_leds(pdev, 0, 0); pwc_camera_power(pdev, 0); pwc_isoc_cleanup(pdev); } + mutex_unlock(&pdev->udevlock); + pwc_cleanup_queued_bufs(pdev); return 0; } -static void pwc_lock(struct vb2_queue *vq) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vq); - mutex_lock(&pdev->modlock); -} - -static void pwc_unlock(struct vb2_queue *vq) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vq); - mutex_unlock(&pdev->modlock); -} - static struct vb2_ops pwc_vb_queue_ops = { .queue_setup = queue_setup, .buf_init = buffer_init, @@ -871,8 +778,6 @@ static struct vb2_ops pwc_vb_queue_ops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, - .wait_prepare = pwc_unlock, - .wait_finish = pwc_lock, }; /***************************************************************************/ @@ -887,9 +792,9 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id struct usb_device *udev = interface_to_usbdev(intf); struct pwc_device *pdev = NULL; int vendor_id, product_id, type_id; - int hint, rc; + int rc; int features = 0; - int video_nr = -1; /* default: use next available device */ + int compression = 0; int my_power_save = power_save; char serial_number[30], *name; @@ -1149,28 +1054,15 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id return -ENOMEM; } pdev->type = type_id; - pdev->vframes = default_fps; - strcpy(pdev->serial, serial_number); pdev->features = features; - if (vendor_id == 0x046D && product_id == 0x08B5) { - /* Logitech QuickCam Orbit - The ranges have been determined experimentally; they may differ from cam to cam. - Also, the exact ranges left-right and up-down are different for my cam - */ - pdev->angle_range.pan_min = -7000; - pdev->angle_range.pan_max = 7000; - pdev->angle_range.tilt_min = -3000; - pdev->angle_range.tilt_max = 2500; - } pwc_construct(pdev); /* set min/max sizes correct */ - mutex_init(&pdev->modlock); + mutex_init(&pdev->capt_file_lock); mutex_init(&pdev->udevlock); spin_lock_init(&pdev->queued_bufs_lock); INIT_LIST_HEAD(&pdev->queued_bufs); pdev->udev = udev; - pdev->vcompression = pwc_preferred_compression; pdev->power_save = my_power_save; /* Init videobuf2 queue structure */ @@ -1185,35 +1077,21 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id /* Init video_device structure */ memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); - pdev->vdev.parent = &intf->dev; - pdev->vdev.lock = &pdev->modlock; strcpy(pdev->vdev.name, name); + set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags); video_set_drvdata(&pdev->vdev, pdev); pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); PWC_DEBUG_PROBE("Release: %04x\n", pdev->release); - /* Now search device_hint[] table for a match, so we can hint a node number. */ - for (hint = 0; hint < MAX_DEV_HINTS; hint++) { - if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) && - (device_hint[hint].pdev == NULL)) { - /* so far, so good... try serial number */ - if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) { - /* match! */ - video_nr = device_hint[hint].device_node; - PWC_DEBUG_PROBE("Found hint, will try to register as /dev/video%d\n", video_nr); - break; - } - } + /* Allocate USB command buffers */ + pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL); + if (!pdev->ctrl_buf) { + PWC_ERROR("Oops, could not allocate memory for pwc_device.\n"); + rc = -ENOMEM; + goto err_free_mem; } - /* occupy slot */ - if (hint < MAX_DEV_HINTS) - device_hint[hint].pdev = pdev; - - PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev); - usb_set_intfdata(intf, pdev); - #ifdef CONFIG_USB_PWC_DEBUG /* Query sensor type */ if (pwc_get_cmos_sensor(pdev, &rc) >= 0) { @@ -1227,8 +1105,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pwc_set_leds(pdev, 0, 0); /* Setup intial videomode */ - rc = pwc_set_video_mode(pdev, pdev->view_max.x, pdev->view_max.y, - pdev->vframes, pdev->vcompression, 0); + rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT, + V4L2_PIX_FMT_YUV420, 30, &compression, 1); if (rc) goto err_free_mem; @@ -1239,20 +1117,25 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id goto err_free_mem; } - pdev->vdev.ctrl_handler = &pdev->ctrl_handler; - /* And powerdown the camera until streaming starts */ pwc_camera_power(pdev, 0); - rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr); - if (rc < 0) { - PWC_ERROR("Failed to register as video device (%d).\n", rc); + /* Register the v4l2_device structure */ + pdev->v4l2_dev.release = pwc_video_release; + rc = v4l2_device_register(&intf->dev, &pdev->v4l2_dev); + if (rc) { + PWC_ERROR("Failed to register v4l2-device (%d).\n", rc); goto err_free_controls; } - rc = pwc_create_sysfs_files(pdev); - if (rc) - goto err_video_unreg; + pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler; + pdev->vdev.v4l2_dev = &pdev->v4l2_dev; + + rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); + if (rc < 0) { + PWC_ERROR("Failed to register as video device (%d).\n", rc); + goto err_unregister_v4l2_dev; + } PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev)); #ifdef CONFIG_USB_PWC_INPUT_EVDEV @@ -1261,7 +1144,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id if (!pdev->button_dev) { PWC_ERROR("Err, insufficient memory for webcam snapshot button device."); rc = -ENOMEM; - pwc_remove_sysfs_files(pdev); goto err_video_unreg; } @@ -1279,7 +1161,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id if (rc) { input_free_device(pdev->button_dev); pdev->button_dev = NULL; - pwc_remove_sysfs_files(pdev); goto err_video_unreg; } #endif @@ -1287,13 +1168,13 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id return 0; err_video_unreg: - if (hint < MAX_DEV_HINTS) - device_hint[hint].pdev = NULL; video_unregister_device(&pdev->vdev); +err_unregister_v4l2_dev: + v4l2_device_unregister(&pdev->v4l2_dev); err_free_controls: v4l2_ctrl_handler_free(&pdev->ctrl_handler); err_free_mem: - usb_set_intfdata(intf, NULL); + kfree(pdev->ctrl_buf); kfree(pdev); return rc; } @@ -1301,27 +1182,26 @@ err_free_mem: /* The user yanked out the cable... */ static void usb_pwc_disconnect(struct usb_interface *intf) { - struct pwc_device *pdev = usb_get_intfdata(intf); + struct v4l2_device *v = usb_get_intfdata(intf); + struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); mutex_lock(&pdev->udevlock); - mutex_lock(&pdev->modlock); - - usb_set_intfdata(intf, NULL); /* No need to keep the urbs around after disconnection */ pwc_isoc_cleanup(pdev); - pwc_cleanup_queued_bufs(pdev); pdev->udev = NULL; - - mutex_unlock(&pdev->modlock); mutex_unlock(&pdev->udevlock); - pwc_remove_sysfs_files(pdev); + pwc_cleanup_queued_bufs(pdev); + video_unregister_device(&pdev->vdev); + v4l2_device_unregister(&pdev->v4l2_dev); #ifdef CONFIG_USB_PWC_INPUT_EVDEV if (pdev->button_dev) input_unregister_device(pdev->button_dev); #endif + + v4l2_device_put(&pdev->v4l2_dev); } @@ -1329,30 +1209,19 @@ static void usb_pwc_disconnect(struct usb_interface *intf) * Initialization code & module stuff */ -static int fps; -static int compression = -1; -static int leds[2] = { -1, -1 }; static unsigned int leds_nargs; -static char *dev_hint[MAX_DEV_HINTS]; -static unsigned int dev_hint_nargs; -module_param(fps, int, 0444); #ifdef CONFIG_USB_PWC_DEBUG module_param_named(trace, pwc_trace, int, 0644); #endif module_param(power_save, int, 0644); -module_param(compression, int, 0444); module_param_array(leds, int, &leds_nargs, 0444); -module_param_array(dev_hint, charp, &dev_hint_nargs, 0444); -MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); #ifdef CONFIG_USB_PWC_DEBUG MODULE_PARM_DESC(trace, "For debugging purposes"); #endif MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off"); -MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)"); MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); -MODULE_PARM_DESC(dev_hint, "Device node hints"); MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); MODULE_AUTHOR("Luc Saillard <luc@saillard.org>"); @@ -1362,122 +1231,13 @@ MODULE_VERSION( PWC_VERSION ); static int __init usb_pwc_init(void) { - int i; - -#ifdef CONFIG_USB_PWC_DEBUG - PWC_INFO("Philips webcam module version " PWC_VERSION " loaded.\n"); - PWC_INFO("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); - PWC_INFO("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); - PWC_INFO("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); - - if (pwc_trace >= 0) { - PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace); - } -#endif - - if (fps) { - if (fps < 4 || fps > 30) { - PWC_ERROR("Framerate out of bounds (4-30).\n"); - return -EINVAL; - } - default_fps = fps; - PWC_DEBUG_MODULE("Default framerate set to %d.\n", default_fps); - } - - if (compression >= 0) { - if (compression > 3) { - PWC_ERROR("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); - return -EINVAL; - } - pwc_preferred_compression = compression; - PWC_DEBUG_MODULE("Preferred compression set to %d.\n", pwc_preferred_compression); - } - if (leds[0] >= 0) - led_on = leds[0]; - if (leds[1] >= 0) - led_off = leds[1]; - - /* Big device node whoopla. Basically, it allows you to assign a - device node (/dev/videoX) to a camera, based on its type - & serial number. The format is [type[.serialnumber]:]node. - - Any camera that isn't matched by these rules gets the next - available free device node. - */ - for (i = 0; i < MAX_DEV_HINTS; i++) { - char *s, *colon, *dot; - - /* This loop also initializes the array */ - device_hint[i].pdev = NULL; - s = dev_hint[i]; - if (s != NULL && *s != '\0') { - device_hint[i].type = -1; /* wildcard */ - strcpy(device_hint[i].serial_number, "*"); - - /* parse string: chop at ':' & '/' */ - colon = dot = s; - while (*colon != '\0' && *colon != ':') - colon++; - while (*dot != '\0' && *dot != '.') - dot++; - /* Few sanity checks */ - if (*dot != '\0' && dot > colon) { - PWC_ERROR("Malformed camera hint: the colon must be after the dot.\n"); - return -EINVAL; - } - - if (*colon == '\0') { - /* No colon */ - if (*dot != '\0') { - PWC_ERROR("Malformed camera hint: no colon + device node given.\n"); - return -EINVAL; - } - else { - /* No type or serial number specified, just a number. */ - device_hint[i].device_node = - simple_strtol(s, NULL, 10); - } - } - else { - /* There's a colon, so we have at least a type and a device node */ - device_hint[i].type = - simple_strtol(s, NULL, 10); - device_hint[i].device_node = - simple_strtol(colon + 1, NULL, 10); - if (*dot != '\0') { - /* There's a serial number as well */ - int k; - - dot++; - k = 0; - while (*dot != ':' && k < 29) { - device_hint[i].serial_number[k++] = *dot; - dot++; - } - device_hint[i].serial_number[k] = '\0'; - } - } - PWC_TRACE("device_hint[%d]:\n", i); - PWC_TRACE(" type : %d\n", device_hint[i].type); - PWC_TRACE(" serial# : %s\n", device_hint[i].serial_number); - PWC_TRACE(" node : %d\n", device_hint[i].device_node); - } - else - device_hint[i].type = 0; /* not filled */ - } /* ..for MAX_DEV_HINTS */ - - PWC_DEBUG_PROBE("Registering driver at address 0x%p.\n", &pwc_driver); return usb_register(&pwc_driver); } static void __exit usb_pwc_exit(void) { - PWC_DEBUG_MODULE("Deregistering driver.\n"); usb_deregister(&pwc_driver); - PWC_INFO("Philips webcam module removed.\n"); } module_init(usb_pwc_init); module_exit(usb_pwc_exit); - -/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/drivers/media/video/pwc/pwc-kiara.h b/drivers/media/video/pwc/pwc-kiara.h index 047dad8..8e02b7a 100644 --- a/drivers/media/video/pwc/pwc-kiara.h +++ b/drivers/media/video/pwc/pwc-kiara.h @@ -27,7 +27,7 @@ #ifndef PWC_KIARA_H #define PWC_KIARA_H -#include <media/pwc-ioctl.h> +#include "pwc.h" #define PWC_FPS_MAX_KIARA 6 diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/video/pwc/pwc-misc.c index 0b03133..9be5adf 100644 --- a/drivers/media/video/pwc/pwc-misc.c +++ b/drivers/media/video/pwc/pwc-misc.c @@ -27,67 +27,47 @@ #include "pwc.h" -const struct pwc_coord pwc_image_sizes[PSZ_MAX] = +const int pwc_image_sizes[PSZ_MAX][2] = { - { 128, 96, 0 }, /* sqcif */ - { 160, 120, 0 }, /* qsif */ - { 176, 144, 0 }, /* qcif */ - { 320, 240, 0 }, /* sif */ - { 352, 288, 0 }, /* cif */ - { 640, 480, 0 }, /* vga */ + { 128, 96 }, /* sqcif */ + { 160, 120 }, /* qsif */ + { 176, 144 }, /* qcif */ + { 320, 240 }, /* sif */ + { 352, 288 }, /* cif */ + { 640, 480 }, /* vga */ }; /* x,y -> PSZ_ */ -int pwc_decode_size(struct pwc_device *pdev, int width, int height) +int pwc_get_size(struct pwc_device *pdev, int width, int height) { - int i, find; - - /* Make sure we don't go beyond our max size. - NB: we have different limits for RAW and normal modes. In case - you don't have the decompressor loaded or use RAW mode, - the maximum viewable size is smaller. - */ - if (pdev->pixfmt != V4L2_PIX_FMT_YUV420) - { - if (width > pdev->abs_max.x || height > pdev->abs_max.y) - { - PWC_DEBUG_SIZE("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); - return -1; - } - } - else - { - if (width > pdev->view_max.x || height > pdev->view_max.y) - { - PWC_DEBUG_SIZE("VIDEO_PALETTE_not RAW: going beyond view_max.\n"); - return -1; - } - } + int i; /* Find the largest size supported by the camera that fits into the - requested size. - */ - find = -1; + requested size. */ + for (i = PSZ_MAX - 1; i >= 0; i--) { + if (!(pdev->image_mask & (1 << i))) + continue; + + if (pwc_image_sizes[i][0] <= width && + pwc_image_sizes[i][1] <= height) + return i; + } + + /* No mode found, return the smallest mode we have */ for (i = 0; i < PSZ_MAX; i++) { - if (pdev->image_mask & (1 << i)) { - if (pwc_image_sizes[i].x <= width && pwc_image_sizes[i].y <= height) - find = i; - } + if (pdev->image_mask & (1 << i)) + return i; } - return find; + + /* Never reached there always is atleast one supported mode */ + return 0; } -/* initialize variables depending on type and decompressor*/ +/* initialize variables depending on type and decompressor */ void pwc_construct(struct pwc_device *pdev) { if (DEVICE_USE_CODEC1(pdev->type)) { - pdev->view_min.x = 128; - pdev->view_min.y = 96; - pdev->view_max.x = 352; - pdev->view_max.y = 288; - pdev->abs_max.x = 352; - pdev->abs_max.y = 288; pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; pdev->vcinterface = 2; pdev->vendpoint = 4; @@ -96,13 +76,7 @@ void pwc_construct(struct pwc_device *pdev) } else if (DEVICE_USE_CODEC3(pdev->type)) { - pdev->view_min.x = 160; - pdev->view_min.y = 120; - pdev->view_max.x = 640; - pdev->view_max.y = 480; pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; - pdev->abs_max.x = 640; - pdev->abs_max.y = 480; pdev->vcinterface = 3; pdev->vendpoint = 5; pdev->frame_header_size = TOUCAM_HEADER_SIZE; @@ -110,20 +84,10 @@ void pwc_construct(struct pwc_device *pdev) } else /* if (DEVICE_USE_CODEC2(pdev->type)) */ { - pdev->view_min.x = 128; - pdev->view_min.y = 96; - /* Anthill bug #38: PWC always reports max size, even without PWCX */ - pdev->view_max.x = 640; - pdev->view_max.y = 480; pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; - pdev->abs_max.x = 640; - pdev->abs_max.y = 480; pdev->vcinterface = 3; pdev->vendpoint = 4; pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; } - pdev->pixfmt = V4L2_PIX_FMT_YUV420; /* default */ - pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; - pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; } diff --git a/drivers/media/video/pwc/pwc-timon.h b/drivers/media/video/pwc/pwc-timon.h index a6e2222..270c5b9 100644 --- a/drivers/media/video/pwc/pwc-timon.h +++ b/drivers/media/video/pwc/pwc-timon.h @@ -42,7 +42,7 @@ #ifndef PWC_TIMON_H #define PWC_TIMON_H -#include <media/pwc-ioctl.h> +#include "pwc.h" #define PWC_FPS_MAX_TIMON 6 diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/video/pwc/pwc-uncompress.c index 5126509..b65903f 100644 --- a/drivers/media/video/pwc/pwc-uncompress.c +++ b/drivers/media/video/pwc/pwc-uncompress.c @@ -35,7 +35,7 @@ int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) { - int n, line, col, stride; + int n, line, col; void *yuv, *image; u16 *src; u16 *dsty, *dstu, *dstv; @@ -60,35 +60,23 @@ int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) return 0; } - vb2_set_plane_payload(&fbuf->vb, 0, pdev->view.size); + vb2_set_plane_payload(&fbuf->vb, 0, + pdev->width * pdev->height * 3 / 2); if (pdev->vbandlength == 0) { /* Uncompressed mode. - * We copy the data into the output buffer, using the viewport - * size (which may be larger than the image size). - * Unfortunately we have to do a bit of byte stuffing to get - * the desired output format/size. * * We do some byte shuffling here to go from the * native format to YUV420P. */ src = (u16 *)yuv; - n = pdev->view.x * pdev->view.y; + n = pdev->width * pdev->height; + dsty = (u16 *)(image); + dstu = (u16 *)(image + n); + dstv = (u16 *)(image + n + n / 4); - /* offset in Y plane */ - stride = pdev->view.x * pdev->offset.y + pdev->offset.x; - dsty = (u16 *)(image + stride); - - /* offsets in U/V planes */ - stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; - dstu = (u16 *)(image + n + stride); - dstv = (u16 *)(image + n + n / 4 + stride); - - /* increment after each line */ - stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ - - for (line = 0; line < pdev->image.y; line++) { - for (col = 0; col < pdev->image.x; col += 4) { + for (line = 0; line < pdev->height; line++) { + for (col = 0; col < pdev->width; col += 4) { *dsty++ = *src++; *dsty++ = *src++; if (line & 1) @@ -96,11 +84,6 @@ int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) else *dstu++ = *src++; } - dsty += stride; - if (line & 1) - dstv += (stride >> 1); - else - dstu += (stride >> 1); } return 0; @@ -111,12 +94,6 @@ int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) * the decompressor routines will write the data in planar format * immediately. */ - if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) { - PWC_ERROR("Mode Bayer is not supported for now\n"); - /* flags |= PWCX_FLAG_BAYER; */ - return -ENXIO; /* No such device or address: missing decompressor */ - } - if (DEVICE_USE_CODEC1(pdev->type)) { /* TODO & FIXME */ @@ -124,10 +101,7 @@ int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) return -ENXIO; /* No such device or address: missing decompressor */ } else { - pwc_dec23_decompress(pdev, yuv, image, PWCX_FLAG_PLANAR); + pwc_dec23_decompress(pdev, yuv, image); } return 0; } - - -/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index a10ff6b..f495eeb 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -49,6 +49,7 @@ static const struct v4l2_ctrl_ops pwc_ctrl_ops = { enum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto }; enum { custom_autocontour, custom_contour, custom_noise_reduction, + custom_awb_speed, custom_awb_delay, custom_save_user, custom_restore_user, custom_restore_factory }; const char * const pwc_auto_whitebal_qmenu[] = { @@ -138,6 +139,26 @@ static const struct v4l2_ctrl_config pwc_restore_factory_cfg = { .name = "Restore Factory Settings", }; +static const struct v4l2_ctrl_config pwc_awb_speed_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(awb_speed), + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto White Balance Speed", + .min = 1, + .max = 32, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_awb_delay_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(awb_delay), + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto White Balance Delay", + .min = 0, + .max = 63, + .step = 1, +}; + int pwc_init_controls(struct pwc_device *pdev) { struct v4l2_ctrl_handler *hdl; @@ -338,6 +359,23 @@ int pwc_init_controls(struct pwc_device *pdev) if (pdev->restore_factory) pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE; + /* Auto White Balance speed & delay */ + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, + AWB_CONTROL_SPEED_FORMATTER, &def); + if (r || def < 1 || def > 32) + def = 1; + cfg = pwc_awb_speed_cfg; + cfg.def = def; + pdev->awb_speed = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, + AWB_CONTROL_DELAY_FORMATTER, &def); + if (r || def > 63) + def = 0; + cfg = pwc_awb_delay_cfg; + cfg.def = def; + pdev->awb_delay = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + if (!(pdev->features & FEATURE_MOTOR_PANTILT)) return hdl->error; @@ -357,25 +395,16 @@ int pwc_init_controls(struct pwc_device *pdev) return hdl->error; } -static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_format *f) +static void pwc_vidioc_fill_fmt(struct v4l2_format *f, + int width, int height, u32 pixfmt) { memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); - f->fmt.pix.width = pdev->view.x; - f->fmt.pix.height = pdev->view.y; + f->fmt.pix.width = width; + f->fmt.pix.height = height; f->fmt.pix.field = V4L2_FIELD_NONE; - if (pdev->pixfmt == V4L2_PIX_FMT_YUV420) { - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; - f->fmt.pix.bytesperline = (f->fmt.pix.width * 3)/2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - } else { - /* vbandlength contains 4 lines ... */ - f->fmt.pix.bytesperline = pdev->vbandlength/4; - f->fmt.pix.sizeimage = pdev->frame_size + sizeof(struct pwc_raw_frame); - if (DEVICE_USE_CODEC1(pdev->type)) - f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC1; - else - f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC2; - } + f->fmt.pix.pixelformat = pixfmt; + f->fmt.pix.bytesperline = f->fmt.pix.width; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width * 3 / 2; PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() " "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n", f->fmt.pix.width, @@ -391,6 +420,8 @@ static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_forma /* ioctl(VIDIOC_TRY_FMT) */ static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) { + int size; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); return -EINVAL; @@ -417,15 +448,11 @@ static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) } - if (f->fmt.pix.width > pdev->view_max.x) - f->fmt.pix.width = pdev->view_max.x; - else if (f->fmt.pix.width < pdev->view_min.x) - f->fmt.pix.width = pdev->view_min.x; - - if (f->fmt.pix.height > pdev->view_max.y) - f->fmt.pix.height = pdev->view_max.y; - else if (f->fmt.pix.height < pdev->view_min.y) - f->fmt.pix.height = pdev->view_min.y; + size = pwc_get_size(pdev, f->fmt.pix.width, f->fmt.pix.height); + pwc_vidioc_fill_fmt(f, + pwc_image_sizes[size][0], + pwc_image_sizes[size][1], + f->fmt.pix.pixelformat); return 0; } @@ -435,68 +462,45 @@ static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { struct pwc_device *pdev = video_drvdata(file); - int ret, fps, snapshot, compression, pixelformat; - - if (!pdev->udev) - return -ENODEV; + int ret, pixelformat, compression = 0; - if (pdev->capt_file != NULL && - pdev->capt_file != file) + if (pwc_test_n_set_capt_file(pdev, file)) return -EBUSY; - pdev->capt_file = file; - ret = pwc_vidioc_try_fmt(pdev, f); - if (ret<0) + if (ret < 0) return ret; pixelformat = f->fmt.pix.pixelformat; - compression = pdev->vcompression; - snapshot = 0; - fps = pdev->vframes; - if (f->fmt.pix.priv) { - compression = (f->fmt.pix.priv & PWC_QLT_MASK) >> PWC_QLT_SHIFT; - snapshot = !!(f->fmt.pix.priv & PWC_FPS_SNAPSHOT); - fps = (f->fmt.pix.priv & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; - if (fps == 0) - fps = pdev->vframes; - } - if (pixelformat != V4L2_PIX_FMT_YUV420 && - pixelformat != V4L2_PIX_FMT_PWC1 && - pixelformat != V4L2_PIX_FMT_PWC2) - return -EINVAL; + mutex_lock(&pdev->udevlock); + if (!pdev->udev) { + ret = -ENODEV; + goto leave; + } - if (vb2_is_streaming(&pdev->vb_queue)) - return -EBUSY; + if (pdev->iso_init) { + ret = -EBUSY; + goto leave; + } PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d " - "compression=%d snapshot=%d format=%c%c%c%c\n", - f->fmt.pix.width, f->fmt.pix.height, fps, - compression, snapshot, + "format=%c%c%c%c\n", + f->fmt.pix.width, f->fmt.pix.height, pdev->vframes, (pixelformat)&255, (pixelformat>>8)&255, (pixelformat>>16)&255, (pixelformat>>24)&255); - ret = pwc_set_video_mode(pdev, - f->fmt.pix.width, - f->fmt.pix.height, - fps, - compression, - snapshot); + ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height, + pixelformat, 30, &compression, 0); PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret); - if (ret) - return ret; - - pdev->pixfmt = pixelformat; - - pwc_vidioc_fill_fmt(pdev, f); - - return 0; - + pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); +leave: + mutex_unlock(&pdev->udevlock); + return ret; } static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) @@ -536,30 +540,14 @@ static int pwc_s_input(struct file *file, void *fh, unsigned int i) return i ? -EINVAL : 0; } -static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl) { struct pwc_device *pdev = container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - /* - * Sometimes it can take quite long for the pwc to complete usb control - * transfers, so release the modlock to give streaming by another - * process / thread the chance to continue with a dqbuf. - */ - mutex_unlock(&pdev->modlock); - - /* - * Take the udev-lock to protect against the disconnect handler - * completing and setting dev->udev to NULL underneath us. Other code - * does not need to do this since it is protected by the modlock. - */ - mutex_lock(&pdev->udevlock); - - if (!pdev->udev) { - ret = -ENODEV; - goto leave; - } + if (!pdev->udev) + return -ENODEV; switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: @@ -624,9 +612,18 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) if (ret) PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret); -leave: + return ret; +} + +static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct pwc_device *pdev = + container_of(ctrl->handler, struct pwc_device, ctrl_handler); + int ret; + + mutex_lock(&pdev->udevlock); + ret = pwc_g_volatile_ctrl_unlocked(ctrl); mutex_unlock(&pdev->udevlock); - mutex_lock(&pdev->modlock); return ret; } @@ -643,6 +640,15 @@ static int pwc_set_awb(struct pwc_device *pdev) if (pdev->auto_white_balance->val != awb_manual) pdev->color_bal_valid = false; /* Force cache update */ + + /* + * If this is a preset, update our red / blue balance values + * so that events get generated for the new preset values + */ + if (pdev->auto_white_balance->val == awb_indoor || + pdev->auto_white_balance->val == awb_outdoor || + pdev->auto_white_balance->val == awb_fl) + pwc_g_volatile_ctrl_unlocked(pdev->auto_white_balance); } if (pdev->auto_white_balance->val != awb_manual) return 0; @@ -766,33 +772,33 @@ static int pwc_set_autogain_expo(struct pwc_device *pdev) static int pwc_set_motor(struct pwc_device *pdev) { int ret; - u8 buf[4]; - buf[0] = 0; + pdev->ctrl_buf[0] = 0; if (pdev->motor_pan_reset->is_new) - buf[0] |= 0x01; + pdev->ctrl_buf[0] |= 0x01; if (pdev->motor_tilt_reset->is_new) - buf[0] |= 0x02; + pdev->ctrl_buf[0] |= 0x02; if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) { ret = send_control_msg(pdev, SET_MPT_CTL, - PT_RESET_CONTROL_FORMATTER, buf, 1); + PT_RESET_CONTROL_FORMATTER, + pdev->ctrl_buf, 1); if (ret < 0) return ret; } - memset(buf, 0, sizeof(buf)); + memset(pdev->ctrl_buf, 0, 4); if (pdev->motor_pan->is_new) { - buf[0] = pdev->motor_pan->val & 0xFF; - buf[1] = (pdev->motor_pan->val >> 8); + pdev->ctrl_buf[0] = pdev->motor_pan->val & 0xFF; + pdev->ctrl_buf[1] = (pdev->motor_pan->val >> 8); } if (pdev->motor_tilt->is_new) { - buf[2] = pdev->motor_tilt->val & 0xFF; - buf[3] = (pdev->motor_tilt->val >> 8); + pdev->ctrl_buf[2] = pdev->motor_tilt->val & 0xFF; + pdev->ctrl_buf[3] = (pdev->motor_tilt->val >> 8); } if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) { ret = send_control_msg(pdev, SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, - buf, sizeof(buf)); + pdev->ctrl_buf, 4); if (ret < 0) return ret; } @@ -806,8 +812,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - /* See the comments on locking in pwc_g_volatile_ctrl */ - mutex_unlock(&pdev->modlock); mutex_lock(&pdev->udevlock); if (!pdev->udev) { @@ -891,6 +895,16 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) ret = pwc_button_ctrl(pdev, RESTORE_FACTORY_DEFAULTS_FORMATTER); break; + case PWC_CID_CUSTOM(awb_speed): + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + AWB_CONTROL_SPEED_FORMATTER, + ctrl->val); + break; + case PWC_CID_CUSTOM(awb_delay): + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + AWB_CONTROL_DELAY_FORMATTER, + ctrl->val); + break; case V4L2_CID_PAN_RELATIVE: ret = pwc_set_motor(pdev); break; @@ -903,7 +917,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) leave: mutex_unlock(&pdev->udevlock); - mutex_lock(&pdev->modlock); return ret; } @@ -933,9 +946,14 @@ static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { struct pwc_device *pdev = video_drvdata(file); + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + mutex_lock(&pdev->udevlock); /* To avoid race with s_fmt */ PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n", - pdev->image.x, pdev->image.y); - pwc_vidioc_fill_fmt(pdev, f); + pdev->width, pdev->height); + pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); + mutex_unlock(&pdev->udevlock); return 0; } @@ -951,12 +969,9 @@ static int pwc_reqbufs(struct file *file, void *fh, { struct pwc_device *pdev = video_drvdata(file); - if (pdev->capt_file != NULL && - pdev->capt_file != file) + if (pwc_test_n_set_capt_file(pdev, file)) return -EBUSY; - pdev->capt_file = file; - return vb2_reqbufs(&pdev->vb_queue, rb); } @@ -1025,25 +1040,21 @@ static int pwc_enum_framesizes(struct file *file, void *fh, struct pwc_device *pdev = video_drvdata(file); unsigned int i = 0, index = fsize->index; - if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) { + if (fsize->pixel_format == V4L2_PIX_FMT_YUV420 || + (fsize->pixel_format == V4L2_PIX_FMT_PWC1 && + DEVICE_USE_CODEC1(pdev->type)) || + (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && + DEVICE_USE_CODEC23(pdev->type))) { for (i = 0; i < PSZ_MAX; i++) { - if (pdev->image_mask & (1UL << i)) { - if (!index--) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = pwc_image_sizes[i].x; - fsize->discrete.height = pwc_image_sizes[i].y; - return 0; - } + if (!(pdev->image_mask & (1UL << i))) + continue; + if (!index--) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = pwc_image_sizes[i][0]; + fsize->discrete.height = pwc_image_sizes[i][1]; + return 0; } } - } else if (fsize->index == 0 && - ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) || - (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) { - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = pdev->abs_max.x; - fsize->discrete.height = pdev->abs_max.y; - return 0; } return -EINVAL; } @@ -1056,8 +1067,8 @@ static int pwc_enum_frameintervals(struct file *file, void *fh, unsigned int i; for (i = 0; i < PSZ_MAX; i++) { - if (pwc_image_sizes[i].x == fival->width && - pwc_image_sizes[i].y == fival->height) { + if (pwc_image_sizes[i][0] == fival->width && + pwc_image_sizes[i][1] == fival->height) { size = i; break; } @@ -1078,20 +1089,69 @@ static int pwc_enum_frameintervals(struct file *file, void *fh, return 0; } -static int pwc_log_status(struct file *file, void *priv) +static int pwc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) { struct pwc_device *pdev = video_drvdata(file); - v4l2_ctrl_handler_log_status(&pdev->ctrl_handler, PWC_NAME); + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(parm, 0, sizeof(*parm)); + + parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm->parm.capture.readbuffers = MIN_FRAMES; + parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe.denominator = pdev->vframes; + parm->parm.capture.timeperframe.numerator = 1; + return 0; } -static long pwc_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) +static int pwc_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) { struct pwc_device *pdev = video_drvdata(file); + int compression = 0; + int ret, fps; - return pwc_ioctl(pdev, cmd, arg); + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + parm->parm.capture.timeperframe.numerator == 0) + return -EINVAL; + + if (pwc_test_n_set_capt_file(pdev, file)) + return -EBUSY; + + fps = parm->parm.capture.timeperframe.denominator / + parm->parm.capture.timeperframe.numerator; + + mutex_lock(&pdev->udevlock); + if (!pdev->udev) { + ret = -ENODEV; + goto leave; + } + + if (pdev->iso_init) { + ret = -EBUSY; + goto leave; + } + + ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, + fps, &compression, 0); + + pwc_g_parm(file, fh, parm); + +leave: + mutex_unlock(&pdev->udevlock); + return ret; +} + +static int pwc_log_status(struct file *file, void *priv) +{ + struct pwc_device *pdev = video_drvdata(file); + + v4l2_ctrl_handler_log_status(&pdev->ctrl_handler, PWC_NAME); + return 0; } const struct v4l2_ioctl_ops pwc_ioctl_ops = { @@ -1112,8 +1172,6 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_log_status = pwc_log_status, .vidioc_enum_framesizes = pwc_enum_framesizes, .vidioc_enum_frameintervals = pwc_enum_frameintervals, - .vidioc_default = pwc_default, + .vidioc_g_parm = pwc_g_parm, + .vidioc_s_parm = pwc_s_parm, }; - - -/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 0e4e2d7..e4d4d71 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -35,14 +35,17 @@ #include <asm/errno.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> #include <media/videobuf2-vmalloc.h> #ifdef CONFIG_USB_PWC_INPUT_EVDEV #include <linux/input.h> #endif - -#include <media/pwc-ioctl.h> +#include "pwc-dec1.h" +#include "pwc-dec23.h" /* Version block */ #define PWC_VERSION "10.0.15" @@ -106,6 +109,9 @@ #define FEATURE_CODEC1 0x0002 #define FEATURE_CODEC2 0x0004 +#define MAX_WIDTH 640 +#define MAX_HEIGHT 480 + /* Ignore errors in the first N frames, to allow for startup delays */ #define FRAME_LOWMARK 5 @@ -128,9 +134,6 @@ #define DEVICE_USE_CODEC3(x) ((x)>=700) #define DEVICE_USE_CODEC23(x) ((x)>=675) -/* from pwc-dec.h */ -#define PWCX_FLAG_PLANAR 0x0001 - /* Request types: video */ #define SET_LUM_CTL 0x01 #define GET_LUM_CTL 0x02 @@ -186,6 +189,24 @@ #define PT_RESET_CONTROL_FORMATTER 0x02 #define PT_STATUS_FORMATTER 0x03 +/* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + +struct pwc_raw_frame { + __le16 type; /* type of the webcam */ + __le16 vbandlength; /* Size of 4 lines compressed (used by the + decompressor) */ + __u8 cmd[4]; /* the four byte of the command (in case of + nala, only the first 3 bytes is filled) */ + __u8 rawframe[0]; /* frame_size = H / 4 * vbandlength */ +} __packed; + /* intermediate buffers with raw data from the USB cam */ struct pwc_frame_buf { @@ -198,39 +219,36 @@ struct pwc_frame_buf struct pwc_device { struct video_device vdev; - struct mutex modlock; + struct v4l2_device v4l2_dev; /* Pointer to our usb_device, may be NULL after unplug */ struct usb_device *udev; - /* Protects the setting of udev to NULL by our disconnect handler */ struct mutex udevlock; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int type; int release; /* release number */ int features; /* feature bits */ - char serial[30]; /* serial number (string) */ /*** Video data ***/ struct file *capt_file; /* file doing video capture */ + struct mutex capt_file_lock; int vendpoint; /* video isoc endpoint */ int vcinterface; /* video control interface */ int valternate; /* alternate interface needed */ - int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ + int vframes; /* frames-per-second */ int pixfmt; /* pixelformat: V4L2_PIX_FMT_YUV420 or _PWCX */ int vframe_count; /* received frames */ int vmax_packet_size; /* USB maxpacket size */ int vlast_packet_size; /* for frame synchronisation */ int visoc_errors; /* number of contiguous ISOC errors */ - int vcompression; /* desired compression factor */ int vbandlength; /* compressed band length; 0 is uncompressed */ - char vsnapshot; /* snapshot mode */ char vsync; /* used by isoc handler */ char vmirror; /* for ToUCaM series */ char power_save; /* Do powersaving for this cam */ - int cmd_len; unsigned char cmd_buf[13]; + unsigned char *ctrl_buf; struct urb *urbs[MAX_ISO_BUFS]; char iso_init; @@ -253,7 +271,10 @@ struct pwc_device int frame_total_size; /* including header & trailer */ int drop_frames; - void *decompress_data; /* private data for decompression engine */ + union { /* private data for decompression engine */ + struct pwc_dec1_private dec1; + struct pwc_dec23_private dec23; + }; /* * We have an 'image' and a 'view', where 'image' is the fixed-size img @@ -262,21 +283,8 @@ struct pwc_device * a gray or black border. view_min <= image <= view <= view_max; */ int image_mask; /* supported sizes */ - struct pwc_coord view_min, view_max; /* minimum and maximum view */ - struct pwc_coord abs_max; /* maximum supported size */ - struct pwc_coord image, view; /* image and viewport size */ - struct pwc_coord offset; /* offset of the viewport */ + int width, height; /* current resolution */ - /*** motorized pan/tilt feature */ - struct pwc_mpt_range angle_range; - int pan_angle; /* in degrees * 100 */ - int tilt_angle; /* absolute angle; 0,0 is home */ - - /* - * Set to 1 when the user push the button, reset to 0 - * when this value is read from sysfs. - */ - int snapshot_button_status; #ifdef CONFIG_USB_PWC_INPUT_EVDEV struct input_dev *button_dev; /* webcam snapshot button input */ char button_phys[64]; @@ -328,6 +336,8 @@ struct pwc_device struct v4l2_ctrl *save_user; struct v4l2_ctrl *restore_user; struct v4l2_ctrl *restore_factory; + struct v4l2_ctrl *awb_speed; + struct v4l2_ctrl *awb_delay; struct { /* motor control cluster */ struct v4l2_ctrl *motor_pan; @@ -344,19 +354,20 @@ struct pwc_device extern int pwc_trace; #endif +int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file); + /** Functions in pwc-misc.c */ /* sizes in pixels */ -extern const struct pwc_coord pwc_image_sizes[PSZ_MAX]; +extern const int pwc_image_sizes[PSZ_MAX][2]; -int pwc_decode_size(struct pwc_device *pdev, int width, int height); +int pwc_get_size(struct pwc_device *pdev, int width, int height); void pwc_construct(struct pwc_device *pdev); /** Functions in pwc-ctrl.c */ /* Request a certain video mode. Returns < 0 if not possible */ -extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); +extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, + int pixfmt, int frames, int *compression, int send_to_cam); extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size); -extern int pwc_mpt_reset(struct pwc_device *pdev, int flags); -extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); extern int send_control_msg(struct pwc_device *pdev, @@ -375,9 +386,6 @@ int pwc_init_controls(struct pwc_device *pdev); /* Power down or up the camera; not supported by all models */ extern void pwc_camera_power(struct pwc_device *pdev, int power); -/* Private ioctl()s; see pwc-ioctl.h */ -extern long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); - extern const struct v4l2_ioctl_ops pwc_ioctl_ops; /** pwc-uncompress.c */ |