/*
- * 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
if (ret)
return ret;
+ ret = v4l2_ctrl_handler_setup(&usbtv->ctrl);
+ if (ret)
+ return ret;
+
return 0;
}
.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[3];
+ u16 index, size;
+ int ret;
+
+ /*
+ * 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);
+ }
+
+ 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;
+ default:
+ 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);
+ if (ret < 0) {
+ dev_warn(usbtv->dev, "Failed to submit a control request.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+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);
}
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);
+ 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) {
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;