diff options
Diffstat (limited to 'drivers/media/usb/usbtv')
-rw-r--r-- | drivers/media/usb/usbtv/usbtv-core.c | 7 | ||||
-rw-r--r-- | drivers/media/usb/usbtv/usbtv-video.c | 113 | ||||
-rw-r--r-- | drivers/media/usb/usbtv/usbtv.h | 3 |
3 files changed, 120 insertions, 3 deletions
diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index dc76fd41e00f..ceb953be0770 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -71,6 +71,7 @@ static int usbtv_probe(struct usb_interface *intf, int size; struct device *dev = &intf->dev; struct usbtv *usbtv; + struct usb_host_endpoint *ep; /* Checks that the device is what we think it is. */ if (intf->num_altsetting != 2) @@ -78,10 +79,12 @@ static int usbtv_probe(struct usb_interface *intf, if (intf->altsetting[1].desc.bNumEndpoints != 4) return -ENODEV; + ep = &intf->altsetting[1].endpoint[0]; + /* Packet size is split into 11 bits of base size and count of * extra multiplies of it.*/ - size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc); - size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1); + size = usb_endpoint_maxp(&ep->desc); + size = (size & 0x07ff) * usb_endpoint_maxp_mult(&ep->desc); /* Device structure */ usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL); diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 6cbe4a245c9f..8135614f395a 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Lubomir Rintel + * Copyright (c) 2013,2016 Lubomir Rintel * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -259,6 +259,10 @@ static int usbtv_setup_capture(struct usbtv *usbtv) if (ret) return ret; + ret = v4l2_ctrl_handler_setup(&usbtv->ctrl); + if (ret) + return ret; + return 0; } @@ -696,11 +700,97 @@ static const struct vb2_ops usbtv_vb2_ops = { .stop_streaming = usbtv_stop_streaming, }; +static int usbtv_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct usbtv *usbtv = container_of(ctrl->handler, struct usbtv, + ctrl); + u8 *data; + u16 index, size; + int ret; + + data = kmalloc(3, GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* + * Read in the current brightness/contrast registers. We need them + * both, because the values are for some reason interleaved. + */ + if (ctrl->id == V4L2_CID_BRIGHTNESS || ctrl->id == V4L2_CID_CONTRAST) { + ret = usb_control_msg(usbtv->udev, + usb_sndctrlpipe(usbtv->udev, 0), USBTV_CONTROL_REG, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, USBTV_BASE + 0x0244, (void *)data, 3, 0); + if (ret < 0) + goto error; + } + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + index = USBTV_BASE + 0x0244; + size = 3; + data[0] &= 0xf0; + data[0] |= (ctrl->val >> 8) & 0xf; + data[2] = ctrl->val & 0xff; + break; + case V4L2_CID_CONTRAST: + index = USBTV_BASE + 0x0244; + size = 3; + data[0] &= 0x0f; + data[0] |= (ctrl->val >> 4) & 0xf0; + data[1] = ctrl->val & 0xff; + break; + case V4L2_CID_SATURATION: + index = USBTV_BASE + 0x0242; + data[0] = ctrl->val >> 8; + data[1] = ctrl->val & 0xff; + size = 2; + break; + case V4L2_CID_HUE: + index = USBTV_BASE + 0x0240; + size = 2; + if (ctrl->val > 0) { + data[0] = 0x92 + (ctrl->val >> 8); + data[1] = ctrl->val & 0xff; + } else { + data[0] = 0x82 + (-ctrl->val >> 8); + data[1] = -ctrl->val & 0xff; + } + break; + case V4L2_CID_SHARPNESS: + index = USBTV_BASE + 0x0239; + data[0] = 0; + data[1] = ctrl->val; + size = 2; + break; + default: + kfree(data); + return -EINVAL; + } + + ret = usb_control_msg(usbtv->udev, usb_sndctrlpipe(usbtv->udev, 0), + USBTV_CONTROL_REG, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, (void *)data, size, 0); + +error: + if (ret < 0) + dev_warn(usbtv->dev, "Failed to submit a control request.\n"); + + kfree(data); + return ret; +} + +static const struct v4l2_ctrl_ops usbtv_ctrl_ops = { + .s_ctrl = usbtv_s_ctrl, +}; + static void usbtv_release(struct v4l2_device *v4l2_dev) { struct usbtv *usbtv = container_of(v4l2_dev, struct usbtv, v4l2_dev); v4l2_device_unregister(&usbtv->v4l2_dev); + v4l2_ctrl_handler_free(&usbtv->ctrl); vb2_queue_release(&usbtv->vb2q); kfree(usbtv); } @@ -731,7 +821,26 @@ int usbtv_video_init(struct usbtv *usbtv) return ret; } + /* controls */ + v4l2_ctrl_handler_init(&usbtv->ctrl, 4); + v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0x3ff, 1, 0x1d0); + v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 0x3ff, 1, 0x1c0); + v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, + V4L2_CID_SATURATION, 0, 0x3ff, 1, 0x200); + v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, + V4L2_CID_HUE, -0xdff, 0xdff, 1, 0x000); + v4l2_ctrl_new_std(&usbtv->ctrl, &usbtv_ctrl_ops, + V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x60); + ret = usbtv->ctrl.error; + if (ret < 0) { + dev_warn(usbtv->dev, "Could not initialize controls\n"); + goto ctrl_fail; + } + /* v4l2 structure */ + usbtv->v4l2_dev.ctrl_handler = &usbtv->ctrl; usbtv->v4l2_dev.release = usbtv_release; ret = v4l2_device_register(usbtv->dev, &usbtv->v4l2_dev); if (ret < 0) { @@ -760,6 +869,8 @@ int usbtv_video_init(struct usbtv *usbtv) vdev_fail: v4l2_device_unregister(&usbtv->v4l2_dev); v4l2_fail: +ctrl_fail: + v4l2_ctrl_handler_free(&usbtv->ctrl); vb2_queue_release(&usbtv->vb2q); return ret; diff --git a/drivers/media/usb/usbtv/usbtv.h b/drivers/media/usb/usbtv/usbtv.h index 011f9fdc77a9..0231e449877e 100644 --- a/drivers/media/usb/usbtv/usbtv.h +++ b/drivers/media/usb/usbtv/usbtv.h @@ -38,6 +38,7 @@ #include <linux/usb.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/videobuf2-v4l2.h> #include <media/videobuf2-vmalloc.h> @@ -45,6 +46,7 @@ #define USBTV_VIDEO_ENDP 0x81 #define USBTV_AUDIO_ENDP 0x83 #define USBTV_BASE 0xc000 +#define USBTV_CONTROL_REG 11 #define USBTV_REQUEST_REG 12 /* Number of concurrent isochronous urbs submitted. @@ -87,6 +89,7 @@ struct usbtv { /* video */ struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl; struct video_device vdev; struct vb2_queue vb2q; struct mutex v4l2_lock; |