New control handling framework
Subsystems (2)
cover New control handling framework
This RFC patch series adds the control handling framework and implements it in ivtv and all subdev drivers used by ivtv.
It is a bare-bones implementation, so no sysfs or debugfs enhancements.
Any comments are welcome.
Once this is in then we can start migrating all subdev drivers to this framework, followed by all bridge drivers. Converted subdev drivers can still be used by unconverted bridge drivers. Once all bridge drivers are converted the subdev backwards compatibility code can be removed.
The same is true for the cx2341x module: both converted and unconverted bridge drivers are supported. Once all bridge drivers that use this module are converted the compat code can be removed from cx2341x (and that will save about 1060 lines of hard to understand code).
Before this can be merged Laurent Pinchart will have to verify that this new framework can be used with UVC. Note to Laurent: v4l2_ctrl_new_custom() allows you to pass a 'void *priv' pointer when creating the control. I'm pretty sure you need this to associate a V4L control with a UVC data struct. Should you need it, then it will be trivial to add a flag that will cause the driver to kfree the priv pointer when the control is freed.
Regards,
Hans
Hans Verkuil (15):
v4l: Add new control handling framework
v4l2-ctrls: reorder 'case' statements to match order in header.
Documentation: add v4l2-controls.txt documenting the new controls
API.
v4l: hook up the new control framework into the core framework
saa7115: convert to the new control framework
msp3400: convert to the new control framework
saa717x: convert to the new control framework cx25840/ivtv: replace ugly priv control with s_config
cx25840: convert to the new control framework
cx2341x: convert to the control framework
wm8775: convert to the new control framework
cs53l32a: convert to new control framework.
wm8739: convert to the new control framework
ivtv: convert gpio subdev to new control framework.
ivtv: convert to the new control framework
Documentation/video4linux/v4l2-controls.txt | 543 ++++++++ drivers/media/video/Makefile | 2 +- drivers/media/video/cs53l32a.c | 107 +- drivers/media/video/cx2341x.c | 725 +++++++++-- drivers/media/video/cx25840/cx25840-audio.c | 144 +-- drivers/media/video/cx25840/cx25840-core.c | 201 ++-- drivers/media/video/cx25840/cx25840-core.h | 23 +- drivers/media/video/ivtv/ivtv-controls.c | 275 +---- drivers/media/video/ivtv/ivtv-controls.h | 6 +- drivers/media/video/ivtv/ivtv-driver.c | 26 +- drivers/media/video/ivtv/ivtv-driver.h | 4 +- drivers/media/video/ivtv/ivtv-fileops.c | 23 +- drivers/media/video/ivtv/ivtv-firmware.c | 6 +- drivers/media/video/ivtv/ivtv-gpio.c | 77 +- drivers/media/video/ivtv/ivtv-i2c.c | 7 + drivers/media/video/ivtv/ivtv-ioctl.c | 31 +- drivers/media/video/ivtv/ivtv-streams.c | 20 +- drivers/media/video/msp3400-driver.c | 248 ++--- drivers/media/video/msp3400-driver.h | 16 +- drivers/media/video/msp3400-kthreads.c | 16 +- drivers/media/video/saa7115.c | 180 ++-- drivers/media/video/saa717x.c | 323 ++---- drivers/media/video/v4l2-common.c | 472 +------- drivers/media/video/v4l2-ctrls.c | 1793 +++++++++++++++++++++++++++ drivers/media/video/v4l2-device.c | 7 + drivers/media/video/v4l2-ioctl.c | 46 +- drivers/media/video/wm8739.c | 176 +-- drivers/media/video/wm8775.c | 79 +- include/media/cx2341x.h | 81 ++ include/media/cx25840.h | 11 + include/media/v4l2-ctrls.h | 271 ++++ include/media/v4l2-dev.h | 4 + include/media/v4l2-device.h | 4 + include/media/v4l2-subdev.h | 3 + 34 files changed, 4051 insertions(+), 1899 deletions(-) create mode 100644 Documentation/video4linux/v4l2-controls.txt create mode 100644 drivers/media/video/v4l2-ctrls.c create mode 100644 include/media/v4l2-ctrls.h
RFC PATCH 1/15 v4l: Add new control handling framework
Add a new framework to handle controls which makes life for driver developers much easier.
Note that this patch moves some of the control support that used to be in v4l2-common.c to v4l2-ctrls.c. The tables were copied unchanged. The body of v4l2_ctrl_query_fill() was copied to a new v4l2_ctrl_fill() function in v4l2-ctrls.c. This new function doesn't use the v4l2_queryctrl struct anymore, which makes it more general.
The remainder of v4l2-ctrls.c is all new. Highlights include:
- No need to implement VIDIOC_QUERYCTRL, QUERYMENU, S_CTRL, G_CTRL, S_EXT_CTRLS, G_EXT_CTRLS or TRY_EXT_CTRLS in either bridge drivers or subdevs. New wrapper functions are provided that can just be plugged in. Once everything has been converted these wrapper functions can be removed as well.
- When subdevices are added their controls can be automatically merged with the bridge driver's controls.
- Most drivers just need to implement s_ctrl to set the controls. The framework handles the locking and tries to be as 'atomic' as possible.
- Ready for the subdev device nodes: the same mechanism applies to subdevs and their device nodes as well. Sub-device drivers can make controls local, preventing them from being merged with bridge drivers.
- Takes care of backwards compatibility handling of VIDIOC_S_CTRL and VIDIOC_G_CTRL. Handling of V4L2_CID_PRIVATE_BASE is fully transparent. CTRL_CLASS controls are automatically added.
--- drivers/media/video/Makefile | 2 +- drivers/media/video/v4l2-common.c | 472 +---------- drivers/media/video/v4l2-ctrls.c | 1789 +++++++++++++++++++++++++++++++++++++ include/media/v4l2-ctrls.h | 271 ++++++ include/media/v4l2-dev.h | 4 + include/media/v4l2-device.h | 4 + include/media/v4l2-subdev.h | 3 + 7 files changed, 2077 insertions(+), 468 deletions(-) create mode 100644 drivers/media/video/v4l2-ctrls.c create mode 100644 include/media/v4l2-ctrls.h
drivers/media/video/Makefile +1 -1
@@ -10,7 +10,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o -videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o +videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-ctrls.o # V4L2 core modules
drivers/media/video/v4l2-common.c +5 -467
491 lines not on this page · raw message
drivers/media/video/v4l2-ctrls.c new file +1789 -0
1,789 lines not on this page · raw message
include/media/v4l2-ctrls.h new file +271 -0
271 lines not on this page · raw message
include/media/v4l2-dev.h +4 -0
@@ -27,6 +27,7 @@struct v4l2_ioctl_callbacks ;struct video_device ;struct v4l2_device ; +struct v4l2_ctrl_handler ;/* Flag to mark the video_device struct as registered. Drivers can clear this flag if they want to block all future
@@ -66,6 +67,9 @@ struct video_devicestruct device *parent;/* device parent */ struct v4l2_device *v4l2_dev;/* v4l2_device parent */ +/* Control handler associated with this device node. May be NULL. */ +struct v4l2_ctrl_handler *ctrl_handler; +/* device info */ char name[32 ];int vfl_type;
include/media/v4l2-device.h +4 -0
@@ -32,6 +32,8 @@#define V4L2_DEVICE_NAME_SIZE (20 + 16) +struct v4l2_ctrl_handler ; +struct v4l2_device {/* dev->driver_data points to this struct. Note: dev might be NULL if there is no parent device
@@ -47,6 +49,8 @@ struct v4l2_device {/* notify callback called by some sub-devices. */ void (*notify)(struct v4l2_subdev *sd,unsigned int notification,void *arg); +/* The control handler. May be NULL. */ +struct v4l2_ctrl_handler *ctrl_handler; };/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev.
include/media/v4l2-subdev.h +3 -0
@@ -35,6 +35,7 @@#define V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ 0x00000001struct v4l2_device ; +struct v4l2_ctrl_handler ;struct v4l2_subdev ;struct tuner_setup ;
@@ -399,6 +400,8 @@ struct v4l2_subdev {u32 flags;struct v4l2_device *v4l2_dev;const struct v4l2_subdev_ops *ops; +/* The control handler of this subdev. May be NULL. */ +struct v4l2_ctrl_handler *ctrl_handler;/* name must be unique */ char name[V4L2_SUBDEV_NAME_SIZE ];/* can be used to group similar subdevs, value is driver-specific */
Signature · 1 line
1.6.4.2
Re: [PATCH 01/15] [RFC] v4l: Add new control handling framework
Hi Hans,
I don't think I should review the code in details before we agree on the architecture (please correct me if I'm wrong). Two comments though.
On Monday 26 April 2010 09:33:30 Hans Verkuil wrote:
[snip]
Quoted 5 lines
include/media/v4l2-ctrls.h new file +0
-0
[snip]
Quoted 8 lines
include/media/v4l2-ctrls.h new file +8
-0+/* Fill in the control fields based on the control ID. This works for all +standard V4L2 controls. +For non-standard controls it will only fill in the given arguments +and name will be NULL. +This function will overwrite the contents of name, type and flags. +The contents of min, max, step and def may be modified depending on +the type. +Do not use in drivers! It is used internally for backwards
Quoted 5 lines
compatibility + control handling only. Once all drivers are converted to
use the new + control framework this function will no longer be
exported. */ +void v4l2_ctrl_fill(u32 id, const char **name, enumv4l2_ctrl_type*type, + s32 *min, s32 *max, s32 *step, s32 *def, u32
*flags);
Using kerneldoc comments in the source file would provide a much better documentation than a few lines of comment in the header.
[snip]
Quoted 13 lines
include/media/v4l2-dev.h +1
-0@@ -27,6 +27,7 @@struct v4l2_ioctl_callbacks ;struct video_device ;struct v4l2_device ; +struct v4l2_ctrl_handler ;/* Flag to mark the video_device struct as registered. Drivers can clear this flag if they want to block all future
include/media/v4l2-dev.h struct video_device · line 70
Quoted 4 lines
include/media/v4l2-dev.h +1
-0@@ -66,6 +67,9 @@ struct video_devicestruct device *parent;/* device parent */ struct v4l2_device *v4l2_dev;/* v4l2_device parent */ +/* Control handler associated with this device node. May be NULL. */
Shouldn't we talk about a control*s* handler ? It handles more than one control (would be a bit pointless otherwise :-)).
Signature · 3 lines
Regards, Laurent Pinchart
RFC PATCH 2/15 v4l2-ctrls: reorder 'case' statements to match order in header.
In reply to Hans VerkuilTo make it easier to determine whether all controls are added in v4l2-ctrls.c the case statements inside the switch are re-ordered to match the header.
--- drivers/media/video/v4l2-ctrls.c | 30 +++++++++++++++++------------- 1 files changed, 17 insertions(+), 13 deletions(-)
drivers/media/video/v4l2-ctrls.c +17 -13
@@ -259,6 +259,7 @@ const char *v4l2_ctrl_get_name(u32 id) {switch (id) {/* USER controls */ +/* Keep the order of the 'case's the same as in videodev2.h! */ case V4L2_CID_USER_CLASS :return "User Controls" ;case V4L2_CID_BRIGHTNESS :return "Brightness" ;case V4L2_CID_CONTRAST :return "Contrast" ;
@@ -289,28 +290,37 @@ const char *v4l2_ctrl_get_name(u32 id)case V4L2_CID_SHARPNESS :return "Sharpness" ;case V4L2_CID_BACKLIGHT_COMPENSATION :return "Backlight Compensation" ;case V4L2_CID_CHROMA_AGC :return "Chroma AGC" ; -case V4L2_CID_CHROMA_GAIN :return "Chroma Gain" ;case V4L2_CID_COLOR_KILLER :return "Color Killer" ;case V4L2_CID_COLORFX :return "Color Effects" ;case V4L2_CID_AUTOBRIGHTNESS :return "Brightness, Automatic" ;case V4L2_CID_BAND_STOP_FILTER :return "Band-Stop Filter" ;case V4L2_CID_ROTATE :return "Rotate" ;case V4L2_CID_BG_COLOR :return "Background Color" ; +case V4L2_CID_CHROMA_GAIN :return "Chroma Gain" ;/* MPEG controls */ +/* Keep the order of the 'case's the same as in videodev2.h! */ case V4L2_CID_MPEG_CLASS :return "MPEG Encoder Controls" ; +case V4L2_CID_MPEG_STREAM_TYPE :return "Stream Type" ; +case V4L2_CID_MPEG_STREAM_PID_PMT :return "Stream PMT Program ID" ; +case V4L2_CID_MPEG_STREAM_PID_AUDIO :return "Stream Audio Program ID" ; +case V4L2_CID_MPEG_STREAM_PID_VIDEO :return "Stream Video Program ID" ; +case V4L2_CID_MPEG_STREAM_PID_PCR :return "Stream PCR Program ID" ; +case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO :return "Stream PES Audio ID" ; +case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO :return "Stream PES Video ID" ; +case V4L2_CID_MPEG_STREAM_VBI_FMT :return "Stream VBI Format" ;case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ :return "Audio Sampling Frequency" ;case V4L2_CID_MPEG_AUDIO_ENCODING :return "Audio Encoding" ;case V4L2_CID_MPEG_AUDIO_L1_BITRATE :return "Audio Layer I Bitrate" ;case V4L2_CID_MPEG_AUDIO_L2_BITRATE :return "Audio Layer II Bitrate" ;case V4L2_CID_MPEG_AUDIO_L3_BITRATE :return "Audio Layer III Bitrate" ; -case V4L2_CID_MPEG_AUDIO_AAC_BITRATE :return "Audio AAC Bitrate" ; -case V4L2_CID_MPEG_AUDIO_AC3_BITRATE :return "Audio AC-3 Bitrate" ;case V4L2_CID_MPEG_AUDIO_MODE :return "Audio Stereo Mode" ;case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION :return "Audio Stereo Mode Extension" ;case V4L2_CID_MPEG_AUDIO_EMPHASIS :return "Audio Emphasis" ;case V4L2_CID_MPEG_AUDIO_CRC :return "Audio CRC" ;case V4L2_CID_MPEG_AUDIO_MUTE :return "Audio Mute" ; +case V4L2_CID_MPEG_AUDIO_AAC_BITRATE :return "Audio AAC Bitrate" ; +case V4L2_CID_MPEG_AUDIO_AC3_BITRATE :return "Audio AC-3 Bitrate" ;case V4L2_CID_MPEG_VIDEO_ENCODING :return "Video Encoding" ;case V4L2_CID_MPEG_VIDEO_ASPECT :return "Video Aspect" ;case V4L2_CID_MPEG_VIDEO_B_FRAMES :return "Video B Frames" ;
@@ -323,16 +333,9 @@ const char *v4l2_ctrl_get_name(u32 id)case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION :return "Video Temporal Decimation" ;case V4L2_CID_MPEG_VIDEO_MUTE :return "Video Mute" ;case V4L2_CID_MPEG_VIDEO_MUTE_YUV :return "Video Mute YUV" ; -case V4L2_CID_MPEG_STREAM_TYPE :return "Stream Type" ; -case V4L2_CID_MPEG_STREAM_PID_PMT :return "Stream PMT Program ID" ; -case V4L2_CID_MPEG_STREAM_PID_AUDIO :return "Stream Audio Program ID" ; -case V4L2_CID_MPEG_STREAM_PID_VIDEO :return "Stream Video Program ID" ; -case V4L2_CID_MPEG_STREAM_PID_PCR :return "Stream PCR Program ID" ; -case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO :return "Stream PES Audio ID" ; -case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO :return "Stream PES Video ID" ; -case V4L2_CID_MPEG_STREAM_VBI_FMT :return "Stream VBI Format" ;/* CAMERA controls */ +/* Keep the order of the 'case's the same as in videodev2.h! */ case V4L2_CID_CAMERA_CLASS :return "Camera Controls" ;case V4L2_CID_EXPOSURE_AUTO :return "Auto Exposure" ;case V4L2_CID_EXPOSURE_ABSOLUTE :return "Exposure Time, Absolute" ;
@@ -346,14 +349,15 @@ const char *v4l2_ctrl_get_name(u32 id)case V4L2_CID_FOCUS_ABSOLUTE :return "Focus, Absolute" ;case V4L2_CID_FOCUS_RELATIVE :return "Focus, Relative" ;case V4L2_CID_FOCUS_AUTO :return "Focus, Automatic" ; -case V4L2_CID_IRIS_ABSOLUTE :return "Iris, Absolute" ; -case V4L2_CID_IRIS_RELATIVE :return "Iris, Relative" ;case V4L2_CID_ZOOM_ABSOLUTE :return "Zoom, Absolute" ;case V4L2_CID_ZOOM_RELATIVE :return "Zoom, Relative" ;case V4L2_CID_ZOOM_CONTINUOUS :return "Zoom, Continuous" ;case V4L2_CID_PRIVACY :return "Privacy" ; +case V4L2_CID_IRIS_ABSOLUTE :return "Iris, Absolute" ; +case V4L2_CID_IRIS_RELATIVE :return "Iris, Relative" ;/* FM Radio Modulator control */ +/* Keep the order of the 'case's the same as in videodev2.h! */ case V4L2_CID_FM_TX_CLASS :return "FM Radio Modulator Controls" ;case V4L2_CID_RDS_TX_DEVIATION :return "RDS Signal Deviation" ;case V4L2_CID_RDS_TX_PI :return "RDS Program ID" ;
Signature · 1 line
1.6.4.2
Re: [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header.
Hi Hans,
Quoted 5 lines · On Monday 26 April 2010 09:33:33 Hans Verkuil wrote:
To make it easier to determine whether all controls are added in
v4l2-ctrls.cthe case statements inside the switch are re-ordered to match the header.Signed-off-by Hans Verkuil <[email protected]>
This patch should be merged with the previous one.
Signature · 3 lines
Regards, Laurent Pinchart
Re: [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header.
Quoted 10 lines · On Sunday 02 May 2010 22:42:26 Laurent Pinchart wrote:
Hi Hans,
Quoted 5 lines · On Monday 26 April 2010 09:33:33 Hans Verkuil wrote:
To make it easier to determine whether all controls are added in
v4l2-ctrls.cthe case statements inside the switch are re-ordered to match the header.Signed-off-by Hans Verkuil <[email protected]>This patch should be merged with the previous one.
I didn't do that in order to make the previous patch easier to review. That patch just moves this table unchanged from v4l2-common.c to v4l2-ctrl.c. Since the table doesn't change it is easy to verify that, well, no changes were made.
I will keep this as a separate patch if you don't mind.
Regards,
Hans
Signature · 1 line
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
Re: [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header.
Hi Hans,
Quoted 10 lines · On Monday 03 May 2010 08:42:59 Hans Verkuil wrote:
Quoted 9 lines · On Sunday 02 May 2010 22:42:26 Laurent Pinchart wrote:
Hi Hans,
Quoted 5 lines · On Monday 26 April 2010 09:33:33 Hans Verkuil wrote:
To make it easier to determine whether all controls are added in
v4l2-ctrls.cthe case statements inside the switch are re-ordered to match the header.Signed-off-by Hans Verkuil <[email protected]>
Quoted 8 lines
Quoted 1 line
This patch should be merged with the previous one.
I didn't do that in order to make the previous patch easier to review. That patch just moves this table unchanged from
v4l2-common.ctov4l2-ctrl.c. Since the table doesn't change it is easy to verify that, well, no changes were made.I will keep this as a separate patch if you don't mind.
N/P.
Signature · 3 lines
Regards, Laurent Pinchart
RFC PATCH 3/15 Documentation: add v4l2-controls.txt documenting the new controls API.
In reply to Hans Verkuil--- Documentation/video4linux/v4l2-controls.txt | 543 +++++++++++++++++++++++++++ 1 files changed, 543 insertions(+), 0 deletions(-) create mode 100644 Documentation/video4linux/v4l2-controls.txt
Documentation/video4linux/v4l2-controls.txt new file +543 -0
@@ -0,0 +1,543 @@ +Introduction +============ + +The V4L2 control API seems simple enough, but quickly becomes very hard to +implement correctly in drivers. But much of the code needed to handle controls +is actually not driver specific and can be moved to the V4L core framework. + +After all, the only part that a driver developer is interested in is: + +1) How do I add a control? +2) How do I set the control? (i.e. s_ctrl) + +And occasionally: + +3) How do I update the control's value? (i.e. g_ctrl) +4) How do I validate the user's proposed control value? (i.e. try_ctrl) + +All the rest is something that can be done centrally. + +The control framework was created in order to implement all the rules of the +V4L2 specification with respect to controls in a central place. And to make +life as easy as possible for the driver developer. + + +Objects in the framework +======================== + +There are two main objects: + +The v4l2_ctrl object describes the control properties and keeps track of the +control's value (both the current value and the proposed new value). + +v4l2_ctrl_handler is the object that keeps track of controls. It maintains a +list of v4l2_ctrl objects that it owns and another list of references to +controls, possibly to controls owned by other handlers. + + +Basic usage +=========== + +1) Prepare the driver: + +- Add the handler to your main bridge driver or sub-device driver top-level + struct: + + struct foo_dev { + ... + struct v4l2_ctrl_handler hdl; + ... + }; + + struct foo_dev *foo; + +- Initialize handler: + + v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls); + + The second argument is a hint telling the function how many controls this + handled is expected to handle. It will allocate a hashtable based on this + information. It is a hint only. + +- Hooking the control handler into a driver: + + When a subdevice is being registered with a bridge driver and the + ctrl_handler fields of both v4l2_subdev and v4l2_device are set, then the + controls of the subdev will become automatically available in the bridge + driver as well. If the subdev driver contains controls that already exist in + the bridge driver, then those will be skipped (so a bridge driver can always + override a subdev control). + + How to hook the handler into a bridge driver: + + foo->v4l2_dev.ctrl_handler = &foo->hdl; + + And whenever you call video_register_device() you must set the + ctrl_handler field of struct video_device as well: + + vdev->ctrl_handler = &foo->hdl; + + Finally, remove all control functions from your v4l2_ioctl_ops: + vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl, + vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls. + Those are now no longer needed. + + How to hook the control handler into a subdev driver: + + foo->sd.ctrl_handler = &foo->hdl; + + And set all core control ops in your struct v4l2_subdev_core_ops to these + helpers: + + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls, + + This is for backwards compatibility. Once all bridge drivers are converted + these control ops can be removed just as they are already for bridge drivers. + +- Clean up the handler at the end: + + v4l2_ctrl_handler_free(&foo->hdl); + + +2) Add controls: + +Typically done right after the handler_init: + + v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls); + v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + ... + if (foo->hdl.error) { + int err = foo->hdl.error; + + v4l2_ctrl_handler_free(&foo->hdl); + return err; + } + +The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new +control, but if you do not need to access the pointer outside the control ops, +then there is no need to store it. + +Note that if something fails, the function will return NULL or an error and +set hdl->error to the error code. If hdl->error was already set, then it +will just return and do nothing. This is also true for v4l2_ctrl_handler_init +if it cannot allocate the internal data structure. + +This makes it easy to init the handler and just add all controls and only check +the error code at the end. Saves a lot of repetitive error checking. + +It is recommended to add controls in ascending control ID order: it will be +a bit faster that way. + +3) Optionally force initial control setup: + + v4l2_ctrl_handler_setup(&foo->hdl); + +This will call s_ctrl for all controls unconditionally. Effectively this +initializes the hardware to the default control values. It is recommended +that you do this. + +4) Finally: implement the v4l2_ctrl_ops + + static const struct v4l2_ctrl_ops foo_ctrl_ops = { + .s_ctrl = foo_s_ctrl, + }; + +Usually all you need is s_ctrl: + + static int foo_s_ctrl(struct v4l2_ctrl *ctrl) + { + struct foo *state = container_of(ctrl->handler, struct foo, hdl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + write_reg(0x123, ctrl->val); + break; + case V4L2_CID_CONTRAST: + write_reg(0x456, ctrl->val); + break; + } + return 0; + } + +The control ops are called with the v4l2_ctrl pointer as argument. +The new control value has already been validated, so all you need to do is +to actually update the hardware registers. + +You're done! And this is sufficient for most of the drivers we have. No need +to do any validation of control values, or implement QUERYCTRL/QUERYMENU. And +G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported. + + +=============================================================================== + +The remainder of this document deals with more advanced topics and scenarios. +In practice the basic usage as described above is sufficient for most drivers. + +=============================================================================== + + +Accessing Control Values +======================== + +The v4l2_ctrl struct contains these two unions: + + /* The current control value. */ + union { + s32 val; + s64 val64; + char *string; + } cur; + + /* The new control value. */ + union { + s32 val; + s64 val64; + char *string; + }; + +Within the control ops you can freely use these. The val and val64 speak for +themselves. The string pointers point to character buffers of length +ctrl->maximum + 1, and are always 0-terminated. + +For g_ctrl you have to update the current control values like this: + + ctrl->cur.val = read_reg(0x123); + +The 'new value' union is not relevant in g_ctrl. + +For try/s_ctrl the new values (i.e. as passed by the user) are filled in and +you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union +contains the current value, which you can use (but not change!) as well. + +If s_ctrl returns 0 (OK), then the control framework will copy the new final +values to the 'cur' union. + +While in g/s/try_ctrl you can access the value of all controls owned by the +same handler since the handler's lock is held. Do not attempt to access +the value of controls owned by other handlers, though. + +Elsewhere in the driver you have to be more careful. You cannot just refer +to the current control values without locking. + +There are two simple helper functions defined that will get or set a single +control value safely: + + s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl); + int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val); + +Don't use these inside the control ops g/s/try_ctrl, though, that will fail. + +You can also take the handler lock yourself: + + mutex_lock(&state->hdl.lock); + printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string); + printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val); + mutex_unlock(&state->hdl.lock); + + +Menu Controls +============= + +Menu controls use the 'step' value differently compared to other control +types. The v4l2_ctrl struct contains this union: + + union { + u32 step; + u32 menu_skip_mask; + }; + +For menu controls menu_skip_mask is used. What it does is that it allows you +to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU +implementation where you can return -EINVAL if a certain menu item is not +present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for +menu controls. + +A good example is the MPEG Audio Layer II Bitrate menu control where the +menu is a list of standardized possible bitrates. But in practice hardware +implementations will only support a subset of those. By setting the skip +mask you can tell the framework which menu items should be skipped. Setting +it to 0 means that all menu items are supported. + +So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need to remember +that 'step' means 'skip mask' for menu controls. If you put in '1' by mistake, +then the first menu item will be skipped. + +The v4l2_ctrl_new_std_menu can be used to add menu controls more easily: it +will calculate the min and max values automatically based on the size of the +menu, and it has a proper 'mask' argument instead of 'step'. + + +Active and Grabbed Controls +=========================== + +If you get more complex relationships between controls, then you may have to +activate and deactivate controls. For example, if the Chroma AGC control is +on, then the Chroma Gain control is inactive. That is, you may set it, but +the value will not be used by the hardware as long as the automatic gain +control is on. Typically user interfaces can disable such input fields. + +You can set the 'active' status using v4l2_ctrl_activate(). By default all +controls are active. Note that the framework does not check for this flag. +It is meant purely for GUIs. The function is typically called from within +s_ctrl. + +The other flag is the grabbed flag. A grabbed control means that you cannot +change it because it is in use by some resource. Typical examples are MPEG +bitrate controls that cannot be changed while capturing is in progress. + +If a control is set to 'grabbed' using v4l2_ctrl_grab(), then the framework +will return -EBUSY if an attempt is made to set this control. + +Since this flag is used by the framework the v4l2_ctrl_grab function will +take the control handler's lock. So it cannot be called from within the +control ops. Instead this is typically called from the driver when it +starts streaming. + + +Control Clusters +================ + +By default all controls are independent from the others. But in more +complex scenarios you can get dependencies from one control to another. +In that case you need to 'cluster' them: + + struct foo { + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + ... + }; + + state->volume = v4l2_ctrl_new_std(&state->hdl, ...); + state->mute = v4l2_ctrl_new_std(&state->hdl, ...); + v4l2_ctrl_cluster(2, &state->volume); + +From now on whenever one or more of the controls belonging to the same +cluster is set (or 'gotten', or 'tried'), only the control ops of the first +control ('volume' in this example) is called. You effectively create a new +composite control. Similar to how a 'struct' works in C. + +So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you should set +all two controls belonging to the 'volume' cluster: + + static int foo_s_ctrl(struct v4l2_ctrl *ctrl) + { + struct foo *state = container_of(ctrl->handler, struct foo, hdl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + /* volume cluster */ + write_reg(0x123, state->mute->val ? 0 : ctrl->val); + break; + case V4L2_CID_CONTRAST: + write_reg(0x456, ctrl->val); + break; + } + return 0; + } + +In the example above the following are equivalent for the VOLUME case: + + ctrl == state->volume == ctrl->cluster[0] + state->mute == ctrl->cluster[1] + +Note that controls in a cluster may be NULL. For example, if for some +reason mute was never added (because the hardware doesn't support that +particular feature), then mute will be NULL. So in that case we have a +cluster of 2 controls, of which only 1 is actually instantiated. The +only restriction is that the first control of the cluster must already be +present, since that is the 'master' control of the cluster. The master +control is the one that identifies the cluster and that provides the +pointer to the v4l2_ctrl_ops struct that is used for that cluster. + + +VIDIOC_LOG_STATUS Support +========================= + +This ioctl allow you to dump the current status of a driver to the kernel log. +The v4l2_ctrl_handler_log_status(hdl, prefix) can be used to dump the value of +the controls owned by the given handler to the log. You can supply a prefix +as well. If the prefix didn't end with a space, then ': ' will be added for +you. + + +Different Handlers for Different Video Nodes +============================================ + +Usually the bridge driver has just one control handler that is global for +all video nodes. But you can also specify different control handlers for +different video nodes. It's no problem if there are no subdevs involved. +But if there are, then you need to block the automatic merging of subdev +controls to the global control handler. You do that by simply setting the +ctrl_handler field in struct v4l2_device to NULL. + +After each subdev was added, you will then have to call v4l2_ctrl_add_handler +manually to add the subdev's control handler (sd->ctrl_handler) to the desired +bridge control handler. + +If you want to have one handler (e.g. for a radio device node) have a subset +of another handler (e.g. for a video device node), then you can first add +the controls to the first handler, add the other controls to the second +handler and finally add the first handler to the second. For example: + + v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...); + v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...); + v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...); + v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_CONTRAST, ...); + v4l2_ctrl_add_handler(&video_hdl, &radio_hdl); + +Or you can add specific controls to a handler: + + volume = v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_AUDIO_VOLUME, ...); + v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_BRIGHTNESS, ...); + v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_CONTRAST, ...); + v4l2_ctrl_add_ctrl(&radio_hdl, volume); + +What you should not do is make two identical controls for two handlers. +For example: + + v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...); + v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_AUDIO_MUTE, ...); + +This would be bad since muting the radio would not change the video mute +control. The rule is to have one control for each hardware 'knob' that you +can twiddle. + + +Finding Controls +================ + +Normally you have created the controls yourself and you can store the struct +v4l2_ctrl pointer into your own struct. + +But sometimes you need to find a control from another handler that you do +not own. For example, if you have to find a volume control from a subdev. + +You can do that by calling v4l2_ctrl_find: + + struct v4l2_ctrl *volume; + + volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME); + +Since v4l2_ctrl_find will lock the handler you have to be careful where you +use it. For example, this is not a good idea: + + struct v4l2_ctrl_handler hdl; + + v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...); + v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_CONTRAST, ...); + +...and in video_ops.s_ctrl: + + case V4L2_CID_BRIGHTNESS: + contrast = v4l2_find_ctrl(&hdl, V4L2_CID_CONTRAST); + ... + +When s_ctrl is called by the framework the hdl.lock is already taken, so +attempting to find another control from the same handler will deadlock. + +It is recommended not to use this function from inside the control ops. + + +Inheriting Controls +=================== + +When one control handler is added to another using v4l2_ctrl_add_handler, then +by default all controls from one are merged to the other. But a subdev might +have low-level controls that make sense for some advanced embedded system, but +not when it is used in consumer-level hardware. In that case you want to keep +those low-level controls local to the subdev. You can do this by simply +setting the 'is_private' flag of the control to 1: + + ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...); + if (ctrl) + ctrl->is_private = 1; + +These controls will now be skipped when v4l2_ctrl_add_handler is called. + + +Strict Control Validation +========================= + +By default when the application wants to change an integer control the value +passed to the framework will automatically be modified to map to the provided +minimum, maximum and step values of the control. If instead you just want to +validate the value and not modify it, then set the 'strict_validation' flag of +the control: + + ctrl->strict_validation = 1; + +Now -ERANGE will be returned if the new value does not match the control's +requirements. + +This is currently specific to integer controls. The value for boolean controls +is always mapped to 0 or 1, menu and string controls are already validated +strictly, and integer64 controls are not validated at all. + + +V4L2_CTRL_TYPE_CTRL_CLASS Controls +================================== + +Controls of this type can be used by GUIs to get the name of the control class. +A fully featured GUI can make a dialog with multiple tabs with each tab +containing the controls belonging to a particular control class. The name of +each tab can be found by querying a special control with ID <control class | 1>. + +Drivers do not have to care about this. The framework will automatically add +a control of this type whenever the first control belonging to a new control +class is added. + + +Differences from the Spec +========================= + +There are a few places where the framework acts slightly differently from the +V4L2 Specification. Those differences are described in this section. We will +have to see whether we need to adjust the spec or not. + +1) It is no longer required to have all controls contained in a +v4l2_ext_control array be from the same control class. The framework will be +able to handle any type of control in the array. You need to set ctrl_class +to 0 in order to enable this. If ctrl_class is non-zero, then it will still +check that all controls belong to that control class. + +If you set ctrl_class to 0 and count to 0, then it will only return an error +if there are no controls at all. + +2) Clarified the way error_idx works. For get and set it will be equal to +count if nothing was done yet. If it is less than count then only the controls +up to error_idx-1 were successfully applied. + +3) When attempting to read a button control the framework will return -EACCES +instead of -EINVAL as stated in the spec. It seems to make more sense since +button controls are write-only controls. + +4) Attempting to write to a read-only control will return -EACCES instead of +-EINVAL as the spec says. + +5) The spec does not mention what should happen when you try to set/get a +control class controls. ivtv currently returns -EINVAL (indicating that the +control ID does not exist) while the framework will return -EACCES, which +makes more sense. + + +Proposals for Extensions +======================== + +Some ideas for future extensions to the spec: + +1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of +decimal. Useful for e.g. video_mute_yuv. + +2) It is possible to mark in the controls array which controls have been +successfully written and which failed by for example adding a bit to the +control ID. Not sure if it is worth the effort, though.
Signature · 1 line
1.6.4.2
Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
Hi Hans,
I finally got some time to review your RFC. Let's see until what time it will keep me awake :-)
Quoted 19 lines · On Monday 26 April 2010 09:33:38 Hans Verkuil wrote:
Signed-off-by Hans Verkuil <[email protected]>--- Documentation/video4linux/v4l2-controls.txt | 543 ++++++++++++++++++++++++ 1 files changed, 543 insertions(+), 0 deletions(-) create mode 100644 Documentation/video4linux/v4l2-controls.txtDocumentation/video4linux/v4l2-controls.txt new file +5
-0@@ -0,0 +1,543 @@ +Introduction +============ + +The V4L2 control API seems simple enough, but quickly becomes very hard to +implement correctly in drivers. But much of the code needed to handlecontrols
Documentation/video4linux/v4l2-controls.txt line 10
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+is actually not driver specific and can be moved to the V4L core framework. + +After all, the only part that a driver developer is interested in is: + +1) How do I add a control?
Do you think we will need the ability to remove controls ?
Documentation/video4linux/v4l2-controls.txt line 15
Quoted 5 lines
+2) How do I set the control? (i.e.
s_ctrl)Documentation/video4linux/v4l2-controls.txt new file +4
-0+ +And occasionally: + +3) How do I update the control's value? (i.e. g_ctrl)
The wording of 2 and 3 got me a bit confused. "set"ting a control usually refers to the operation performed by userspace. Similarly, without the "i.e. g_ctrl" comment, I would have thought "update" meant writing the value to the hardware.
Quoted 16 lines
+4) How do I validate the user's proposed control value? (i.e.
try_ctrl)Documentation/video4linux/v4l2-controls.txt new file +4
-0+ +All the rest is something that can be done centrally. + +The control framework was created in order to implement all the rules ofthe
+V4L2 specification with respect to controls in a central place. And to make +life as easy as possible for the driver developer.Documentation/video4linux/v4l2-controls.txt new file +8
-0+ + +Objects in the framework +======================== + +There are two main objects: + +The v4l2_ctrl object describes the control properties and keeps track of
Quoted 2 lines
the
+control's value (both the current value and the proposed new value).
I'm not sure v4l2_ctrl is a good name. We already have a v4l2_control structure, and using the abbreviated name for the in-kernel version is going to be confusing.
It's obviously too late to change v4l2_control to something else. v4l2_control_info and v4l2_control_value are two names that come to my mind. Actually, it might be a good idea to split the v4l2_ctrl structure into a static driver-wide structure (v4l2_control_info) and an instance-specific structure (v4l2_control_value). There's no point in storing the same static data (function pointers, names, ...) for identical controls in different devices.
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +v4l2_ctrl_handler is the object that keeps track of controls. It maintainsa
Documentation/video4linux/v4l2-controls.txt new file +5
-0+list of v4l2_ctrl objects that it owns and another list of references to +controls, possibly to controls owned by other handlers. + + +Basic usage
Documentation/video4linux/v4l2-controls.txt line 48
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+=========== + +1) Prepare the driver: + +- Add the handler to your main bridge driver or sub-device driver top-level + struct: + + struct foo_dev { + ... + struct v4l2_ctrl_handler hdl;
Please use more descriptive names in the examples, such as "ctrl_handler". I expect some (many ?) developers to use the same names as the documentation does, making the driver code a bit difficult to read.
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ ... + }; + + struct foo_dev *foo; + +- Initialize handler: + + v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls); + + The second argument is a hint telling the function how many controls
Quoted 2 lines
this
+ handled is expected to handle. It will allocate a hashtable based on this
s/handled/handler/
Quoted 6 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ information. It is a hint only. + +- Hooking the control handler into a driver: + + When a subdevice is being registered with a bridge driver and the + ctrl_handler fields of both v4l2_subdev and v4l2_device are set, then
Quoted 8 lines
the
+ controls of the subdev will become automatically available in the bridge
+ driver as well. If the subdev driver contains controls that already exist in
+ the bridge driver, then those will be skipped (so a bridge driver can always
+ override a subdev control).
I think you should split the documentation differently. You're mixing video devices, bridges and subdevs. Developers of drivers that use no subdev will have trouble understanding the documentation. You should first describe how to use the framework in a simple driver (no host, bridge, subdev, ... just a foo_dev and a single video_device), and then add sections to describe bridge- specific and subdev-specific APIs.
Documentation/video4linux/v4l2-controls.txt line 73
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + How to hook the handler into a bridge driver: + + foo->v4l2_dev.ctrl_handler = &foo->hdl;
Is v4l2_dev an instance of video_device or v4l2_device ? It would be nice to add the field to the foo_dev structure in the example.
Documentation/video4linux/v4l2-controls.txt line 78
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+ + And whenever you call video_register_device() you must set the + ctrl_handler field of struct video_device as well: + + vdev->ctrl_handler = &foo->hdl;
I suppose that the above foo->v4l2_dev is an instance of v4l2_device then. Does the framework require v4l2_device ? If so, that should be explicit in the introduction.
Why can't the framework find the ctrl_handler instance from the video_device's v4l2_device parent ?
Quoted 11 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ Finally, remove all control functions from your v4l2_ioctl_ops: + vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl, + vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls. + Those are now no longer needed. + + How to hook the control handler into a subdev driver: + + foo->sd.ctrl_handler = &foo->hdl; + + And set all core control ops in your struct v4l2_subdev_core_ops tothese
Documentation/video4linux/v4l2-controls.txt line 98
Quoted 9 lines
Documentation/video4linux/v4l2-controls.txt new file +9
-0+ helpers: + + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls,
s/sd/subdev/ ?
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ + This is for backwards compatibility. Once all bridge drivers areconverted
+ these control ops can be removed just as they are already for bridge drivers.Documentation/video4linux/v4l2-controls.txt new file +5
-0+ +- Clean up the handler at the end: + + v4l2_ctrl_handler_free(&foo->hdl); +
Documentation/video4linux/v4l2-controls.txt line 116
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ +2) Add controls: + +Typically done right after the handler_init: + + v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls); + v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128);
Could you add the v4l2_ctrl_new_std function prototype to the documentation ? I expect the parameters to be min, max, step and default, but it would be better to make it explicit.
Should't v4l2_ctrl_new_std take a control type as well ?
What about hardware for which the boundaries are only known at runtime, or could depend on the values of other controls ? I'm thinking about UVC devices for instance, the boundaries, step and default values need to be retrieved from the hardware. I currently do that at runtime when the control is queried for the first time and cache the values, as doing it during initialization (probe function) crashes a few webcams. That doesn't seem to be possible with the control framework.
Quoted 21 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ ... + if (foo->hdl.error) { + int err = foo->hdl.error; + + v4l2_ctrl_handler_free(&foo->hdl); + return err; + } + +The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new +control, but if you do not need to access the pointer outside the controlops,
Documentation/video4linux/v4l2-controls.txt new file +3
-0+then there is no need to store it. + +Note that if something fails, the function will return NULL or an errorand
Documentation/video4linux/v4l2-controls.txt new file +2
-0+set hdl->error to the error code. If hdl->error was already set, then it +will just return and do nothing. This is also true for
v4l2_ctrl_handler_initDocumentation/video4linux/v4l2-controls.txt new file +3
-0+if it cannot allocate the internal data structure. + +This makes it easy to init the handler and just add all controls and only
Quoted 2 lines
check
+the error code at the end. Saves a lot of repetitive error checking.
I would still check the v4l2_ctrl_handler_init return value explicitly, but that may be just me.
Documentation/video4linux/v4l2-controls.txt line 138
Quoted 2 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+It is recommended to add controls in ascending control ID order: it will be +a bit faster that way.
As long as it's not required that's OK. It would be difficult to do so on UVC hardware, as controls are discovered based on information reported by the device, and those are obviously not ordered based on the V4L2 CIDs :-)
Documentation/video4linux/v4l2-controls.txt line 146
Quoted 7 lines
+3) Optionally force initial control setup:
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ + v4l2_ctrl_handler_setup(&foo->hdl); + +This will call s_ctrl for all controls unconditionally. Effectively this +initializes the hardware to the default control values. It is recommended +that you do this.
What about the other way around, if I want to initialize the framework with the current values retrieved from the hardware ?
Quoted 67 lines
+4) Finally: implement the
v4l2_ctrl_opsDocumentation/video4linux/v4l2-controls.txt new file +27
-0+ + static const struct v4l2_ctrl_ops foo_ctrl_ops = { + .s_ctrl = foo_s_ctrl, + }; + +Usually all you need is s_ctrl: + + static int foo_s_ctrl(struct v4l2_ctrl *ctrl) + { + struct foo *state = container_of(ctrl->handler, struct foo, hdl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + write_reg(0x123, ctrl->val); + break; + case V4L2_CID_CONTRAST: + write_reg(0x456, ctrl->val); + break; + } + return 0; + } + +The control ops are called with the v4l2_ctrl pointer as argument. +The new control value has already been validated, so all you need to do is +to actually update the hardware registers. + +You're done! And this is sufficient for most of the drivers we have. Noneed
+to do any validation of control values, or implement QUERYCTRL/QUERYMENU. AndDocumentation/video4linux/v4l2-controls.txt new file +4
-0+G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported. + + +===============================================================================
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +The remainder of this document deals with more advanced topics andscenarios.
+In practice the basic usage as described above is sufficient for most drivers.
+
+==========================================================================
=====Documentation/video4linux/v4l2-controls.txt new file +22
-0+ + +Accessing Control Values +======================== + +The v4l2_ctrl struct contains these two unions: + + /* The current control value. */ + union { + s32 val; + s64 val64; + char *string; + } cur; + + /* The new control value. */ + union { + s32 val; + s64 val64; + char *string; + }; + +Within the control ops you can freely use these. The val and val64 speakfor
Documentation/video4linux/v4l2-controls.txt line 215
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt new file +8
-0+themselves. The string pointers point to character buffers of length +ctrl->maximum + 1, and are always 0-terminated. + +For g_ctrl you have to update the current control values like this: + + ctrl->cur.val = read_reg(0x123); + +The 'new value' union is not relevant in g_ctrl.
When is g_ctrl called ? When the userspace applications issues a VIDIOC_G_CTRL or VIDIOC_G_EXT_CTRLS ioctl ? I suppose the operation only needs to be implemented if the device needs to be queried because it modified the control value on its own (possibly as a result of setting another control). What if some controls need to be handled that way, while the others are not self- modifying ?
Quoted 19 lines
+For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
Documentation/video4linux/v4l2-controls.txt new file +4
-0+you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union +contains the current value, which you can use (but not change!) as well. + +If s_ctrl returns 0 (OK), then the control framework will copy the newfinal
Documentation/video4linux/v4l2-controls.txt new file +3
-0+values to the 'cur' union. + +While in g/s/try_ctrl you can access the value of all controls owned bythe
Documentation/video4linux/v4l2-controls.txt new file +7
-0+same handler since the handler's lock is held. Do not attempt to access +the value of controls owned by other handlers, though. + +Elsewhere in the driver you have to be more careful. You cannot just refer +to the current control values without locking. + +There are two simple helper functions defined that will get or set asingle
Documentation/video4linux/v4l2-controls.txt line 235
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+control value safely: + + s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl); + int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);
I suppose that v4l2_ctrl_s can be used if the device notifies the driver that a control has changed. Is that correct ?
I don't really like the names of those two functions. For instance, v4l2_ctrl_s looks like it will call the driver to set a control.
Quoted 38 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +Don't use these inside the control ops g/s/try_ctrl, though, that willfail.
Documentation/video4linux/v4l2-controls.txt new file +21
-0+ +You can also take the handler lock yourself: + + mutex_lock(&state->hdl.lock); + printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string); + printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val); + mutex_unlock(&state->hdl.lock); + + +Menu Controls +============= + +Menu controls use the 'step' value differently compared to other control +types. The v4l2_ctrl struct contains this union: + + union { + u32 step; + u32 menu_skip_mask; + }; + +For menu controls menu_skip_mask is used. What it does is that it allowsyou
Documentation/video4linux/v4l2-controls.txt new file +9
-0+to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU +implementation where you can return -EINVAL if a certain menu item is not +present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for +menu controls. + +A good example is the MPEG Audio Layer II Bitrate menu control where the +menu is a list of standardized possible bitrates. But in practice hardware +implementations will only support a subset of those. By setting the skip +mask you can tell the framework which menu items should be skipped.Setting
Documentation/video4linux/v4l2-controls.txt new file +3
-0+it to 0 means that all menu items are supported. + +So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need to
Quoted 4 lines
remember
+that 'step' means 'skip mask' for menu controls. If you put in '1' by mistake,
+then the first menu item will be skipped.
What about adding a v4l2_ctrl_new_menu then ? That will avoid such a mistake.
Quoted 5 lines
+The
v4l2_ctrl_new_std_menucan be used to add menu controls more easily:
it
+will calculate the min and max values automatically based on the size of the
+menu, and it has a proper 'mask' argument instead of 'step'.
OK :-) Shouldn't v4l2_ctrl_new_custom be restricted to non-menu controls then ? Looking at the code, it should probably become an internal function, with another function added for custom controls without menu support.
Quoted 7 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ + +Active and Grabbed Controls +=========================== + +If you get more complex relationships between controls, then you may haveto
Documentation/video4linux/v4l2-controls.txt line 291
Quoted 9 lines
Documentation/video4linux/v4l2-controls.txt new file +9
-0+activate and deactivate controls. For example, if the Chroma AGC control is +on, then the Chroma Gain control is inactive. That is, you may set it, but +the value will not be used by the hardware as long as the automatic gain +control is on. Typically user interfaces can disable such input fields. + +You can set the 'active' status using v4l2_ctrl_activate(). By default all +controls are active. Note that the framework does not check for this flag. +It is meant purely for GUIs. The function is typically called from within +s_ctrl.
What you refer to as active/inactive is usually referred to as enabled/disabled in the GUI world. Might be worth using the same convention.
Quoted 23 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +The other flag is the grabbed flag. A grabbed control means that youcannot
Documentation/video4linux/v4l2-controls.txt new file +4
-0+change it because it is in use by some resource. Typical examples are MPEG +bitrate controls that cannot be changed while capturing is in progress. + +If a control is set to 'grabbed' using v4l2_ctrl_grab(), then theframework
Documentation/video4linux/v4l2-controls.txt new file +15
-0+will return -EBUSY if an attempt is made to set this control. + +Since this flag is used by the framework the v4l2_ctrl_grab function will +take the control handler's lock. So it cannot be called from within the +control ops. Instead this is typically called from the driver when it +starts streaming. + + +Control Clusters +================ + +By default all controls are independent from the others. But in more +complex scenarios you can get dependencies from one control to another. +In that case you need to 'cluster' them: +
Documentation/video4linux/v4l2-controls.txt line 322
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ struct foo { + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + ... + }; + + state->volume = v4l2_ctrl_new_std(&state->hdl, ...); + state->mute = v4l2_ctrl_new_std(&state->hdl, ...); + v4l2_ctrl_cluster(2, &state->volume);
What's the first argument to v4l2_ctrl_cluster ? The number of controls in the cluster ? Does that imply that they need to be added in a sequence, right before v4l2_ctrl_cluster is called ? That seems a bit awkward. Wouldn't it be better to specify the relationships between controls explicitly, maybe by passing a pointer to the master control when creating the 'slave' controls in the cluster ?
Quoted 45 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+From now on whenever one or more of the controls belonging to the same +cluster is set (or 'gotten', or 'tried'), only the control ops of thefirst
Documentation/video4linux/v4l2-controls.txt new file +4
-0+control ('volume' in this example) is called. You effectively create a new +composite control. Similar to how a 'struct' works in C. + +So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you shouldset
Documentation/video4linux/v4l2-controls.txt new file +37
-0+all two controls belonging to the 'volume' cluster: + + static int foo_s_ctrl(struct v4l2_ctrl *ctrl) + { + struct foo *state = container_of(ctrl->handler, struct foo, hdl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + /* volume cluster */ + write_reg(0x123, state->mute->val ? 0 : ctrl->val); + break; + case V4L2_CID_CONTRAST: + write_reg(0x456, ctrl->val); + break; + } + return 0; + } + +In the example above the following are equivalent for the VOLUME case: + + ctrl == state->volume == ctrl->cluster[0] + state->mute == ctrl->cluster[1] + +Note that controls in a cluster may be NULL. For example, if for some +reason mute was never added (because the hardware doesn't support that +particular feature), then mute will be NULL. So in that case we have a +cluster of 2 controls, of which only 1 is actually instantiated. The +only restriction is that the first control of the cluster must already be +present, since that is the 'master' control of the cluster. The master +control is the one that identifies the cluster and that provides the +pointer to the v4l2_ctrl_ops struct that is used for that cluster. + + +VIDIOC_LOG_STATUS Support +========================= + +This ioctl allow you to dump the current status of a driver to the kernel
Quoted 1 line
log.
This has nothing to do with the controls framework, but shouldn't that ioctl be restricted to root only ? It can potentially dump a lot of information to the kernel log, and thus system logs.
Quoted 18 lines
+The
v4l2_ctrl_handler_log_status(hdl, prefix)can be used to dump the value ofDocumentation/video4linux/v4l2-controls.txt new file +16
-0+the controls owned by the given handler to the log. You can supply a prefix +as well. If the prefix didn't end with a space, then ': ' will be added for +you. + + +Different Handlers for Different Video Nodes +============================================ + +Usually the bridge driver has just one control handler that is global for +all video nodes. But you can also specify different control handlers for +different video nodes. It's no problem if there are no subdevs involved. +But if there are, then you need to block the automatic merging of subdev +controls to the global control handler. You do that by simply setting the +ctrl_handler field in struct v4l2_device to NULL. + +After each subdev was added, you will then have to call
Quoted 4 lines
v4l2_ctrl_add_handler
+manually to add the subdev's control handler (sd->ctrl_handler) to the desired
+bridge control handler.
Do you mean video device instead of bridge here ?
I wouldn't mention "bridge" in here. I assume that by bridge you mean v4l2_device. Please use that name directly. The term "bridge" is only applicable to a subset of the v4l2_device use cases.
Can controls for a specific subdev be reported through more than one video device node, but not all of them ?
Quoted 79 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +If you want to have one handler (e.g. for a radio device node) have asubset
Documentation/video4linux/v4l2-controls.txt new file +32
-0+of another handler (e.g. for a video device node), then you can first add +the controls to the first handler, add the other controls to the second +handler and finally add the first handler to the second. For example: + + v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...); + v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...); + v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...); + v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_CONTRAST, ...); + v4l2_ctrl_add_handler(&video_hdl, &radio_hdl); + +Or you can add specific controls to a handler: + + volume = v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_AUDIO_VOLUME, ...); + v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_BRIGHTNESS, ...); + v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_CONTRAST, ...); + v4l2_ctrl_add_ctrl(&radio_hdl, volume); + +What you should not do is make two identical controls for two handlers. +For example: + + v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...); + v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_AUDIO_MUTE, ...); + +This would be bad since muting the radio would not change the video mute +control. The rule is to have one control for each hardware 'knob' that you +can twiddle. + + +Finding Controls +================ + +Normally you have created the controls yourself and you can store thestruct
Documentation/video4linux/v4l2-controls.txt new file +12
-0+v4l2_ctrl pointer into your own struct. + +But sometimes you need to find a control from another handler that you do +not own. For example, if you have to find a volume control from a subdev. + +You can do that by calling v4l2_ctrl_find: + + struct v4l2_ctrl *volume; + + volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME); + +Since v4l2_ctrl_find will lock the handler you have to be careful whereyou
Documentation/video4linux/v4l2-controls.txt new file +23
-0+use it. For example, this is not a good idea: + + struct v4l2_ctrl_handler hdl; + + v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...); + v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_CONTRAST, ...); + +...and in video_ops.s_ctrl: + + case V4L2_CID_BRIGHTNESS: + contrast = v4l2_find_ctrl(&hdl, V4L2_CID_CONTRAST); + ... + +When s_ctrl is called by the framework the hdl.lock is already taken, so +attempting to find another control from the same handler will deadlock. + +It is recommended not to use this function from inside the control ops. + + +Inheriting Controls +=================== + +When one control handler is added to another using v4l2_ctrl_add_handler,then
+by default all controls from one are merged to the other. But a subdev might
+have low-level controls that make sense for some advanced embedded system, but
+not when it is used in consumer-level hardware. In that case you want to keep
Documentation/video4linux/v4l2-controls.txt line 463
Quoted 6 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+those low-level controls local to the subdev. You can do this by simply +setting the 'is_private' flag of the control to 1: + + ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...); + if (ctrl) + ctrl->is_private = 1;
Wouldn't it make more sense to pass that as an argument to v4l2_ctrl_new_custom ?
I'm actually wondering if it wouldn't be better to pass a pointer to a structure with the required information (maybe the v4l2_control_info structure I mentioned earlier ?) to v4l2_ctrl_new_custom.
Quoted 14 lines
Documentation/video4linux/v4l2-controls.txt new file +7
-0+These controls will now be skipped when v4l2_ctrl_add_handler is called. + + +Strict Control Validation +========================= + +By default when the application wants to change an integer control thevalue
+passed to the framework will automatically be modified to map to the provided
+minimum, maximum and step values of the control. If instead you just want to
+validate the value and not modify it, then set the 'strict_validation' flag of
Documentation/video4linux/v4l2-controls.txt line 480
Quoted 6 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+the control: + + ctrl->strict_validation = 1; + +Now -ERANGE will be returned if the new value does not match the control's +requirements.
Why do we need the two behaviours ? Wouldn't it be better to standardize on one of them ?
Quoted 41 lines
+This is currently specific to integer controls. The value for boolean controls
Documentation/video4linux/v4l2-controls.txt new file +8
-0+is always mapped to 0 or 1, menu and string controls are already validated +strictly, and integer64 controls are not validated at all. + + +V4L2_CTRL_TYPE_CTRL_CLASS Controls +================================== + +Controls of this type can be used by GUIs to get the name of the controlclass.
Documentation/video4linux/v4l2-controls.txt new file +2
-0+A fully featured GUI can make a dialog with multiple tabs with each tab +containing the controls belonging to a particular control class. The nameof
+each tab can be found by querying a special control with ID <control class | 1>.Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +Drivers do not have to care about this. The framework will automaticallyadd
+a control of this type whenever the first control belonging to a new controlDocumentation/video4linux/v4l2-controls.txt new file +7
-0+class is added. + + +Differences from the Spec +========================= + +There are a few places where the framework acts slightly differently fromthe
+V4L2 Specification. Those differences are described in this section. We willDocumentation/video4linux/v4l2-controls.txt new file +4
-0+have to see whether we need to adjust the spec or not. + +1) It is no longer required to have all controls contained in a +v4l2_ext_control array be from the same control class. The framework willbe
Documentation/video4linux/v4l2-controls.txt new file +5
-0+able to handle any type of control in the array. You need to set ctrl_class +to 0 in order to enable this. If ctrl_class is non-zero, then it will still +check that all controls belong to that control class. + +If you set ctrl_class to 0 and count to 0, then it will only return an
Quoted 2 lines
error
+if there are no controls at all.
I don't know why we had such a limitation in the first place. I would indeed remove it completely.
Documentation/video4linux/v4l2-controls.txt line 522
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +3
-0+ +2) Clarified the way error_idx works. For get and set it will be equal to +count if nothing was done yet. If it is less than count then only thecontrols
Documentation/video4linux/v4l2-controls.txt new file +5
-1+up to error_idx-1 were successfully applied. + +3) When attempting to read a button control the framework will return -EACCES +instead of -EINVAL as stated in the spec. It seems to make more sense since +button controls are write-only controls.
Agreed. We should change the spec.
Quoted 3 lines
+4) Attempting to write to a read-only control will return
-EACCESinstead of
+-EINVAL as the spec says.
This makes sense as well.
Quoted 7 lines
+5) The spec does not mention what should happen when you try to set/get a
Documentation/video4linux/v4l2-controls.txt new file +6
-0+control class controls. ivtv currently returns -EINVAL (indicating that the +control ID does not exist) while the framework will return -EACCES, which +makes more sense. + + +Proposals for Extensions
Documentation/video4linux/v4l2-controls.txt line 543
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+======================== + +Some ideas for future extensions to the spec: + +1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of +decimal. Useful for e.g. video_mute_yuv. + +2) It is possible to mark in the controls array which controls have been +successfully written and which failed by for example adding a bit to the +control ID. Not sure if it is worth the effort, though.
I still feel a bit awkward about the interface. One particular point that might require attention is the split of the v4l2_ctrl structure into v4l2_control_info and v4l2_control_value structures (the names are not set into stone). I would also like to see if we can't pass pointers to v4l2_control_info (or similar) to the control creation functions instead of a plethora of arguments.
I'll now have a look at the code. As there are already quite a few comments on the documentation I won't perform a very in-depth code review though, I'll save that for later after we agree on the spec :-)
Signature · 3 lines
Regards, Laurent Pinchart
Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
Quoted 24 lines · On Sunday 02 May 2010 22:39:11 Laurent Pinchart wrote:
Hi Hans,
I finally got some time to review your RFC. Let's see until what time it will keep me awake :-)
Quoted 18 lines · On Monday 26 April 2010 09:33:38 Hans Verkuil wrote:
Signed-off-by Hans Verkuil <[email protected]>--- Documentation/video4linux/v4l2-controls.txt | 543 ++++++++++++++++++++++++ 1 files changed, 543 insertions(+), 0 deletions(-) create mode 100644 Documentation/video4linux/v4l2-controls.txtDocumentation/video4linux/v4l2-controls.txt new file +5
-0@@ -0,0 +1,543 @@ +Introduction +============ + +The V4L2 control API seems simple enough, but quickly becomes very hard to +implement correctly in drivers. But much of the code needed to handle
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 10
Quoted 6 lines
controls
Documentation/video4linux/v4l2-controls.txt new file +5
-0+is actually not driver specific and can be moved to the V4L core framework. + +After all, the only part that a driver developer is interested in is: + +1) How do I add a control?Do you think we will need the ability to remove controls ?
I am not aware of any use case. The data structures used will make it hard to actually remove a control. But you can still disable a control by setting a flag, should this become necessary.
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt line 15
Quoted 5 lines
+2) How do I set the control? (i.e.
s_ctrl)Documentation/video4linux/v4l2-controls.txt new file +4
-0+ +And occasionally: + +3) How do I update the control's value? (i.e. g_ctrl)The wording of 2 and 3 got me a bit confused. "set"ting a control usually refers to the operation performed by userspace. Similarly, without the "i.e.
g_ctrl" comment, I would have thought "update" meant writing the value to the hardware.
Good point, that was a poor choice of words. I'll fix this.
Quoted 15 lines
Quoted 15 lines
+4) How do I validate the user's proposed control value? (i.e.
try_ctrl)Documentation/video4linux/v4l2-controls.txt new file +4
-0+ +All the rest is something that can be done centrally. + +The control framework was created in order to implement all the rules ofthe
+V4L2 specification with respect to controls in a central place. And to make +life as easy as possible for the driver developer.Documentation/video4linux/v4l2-controls.txt new file +6
-0+ + +Objects in the framework +======================== + +There are two main objects:
Quoted 8 lines
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +The v4l2_ctrl object describes the control properties and keeps track ofthe
+control's value (both the current value and the proposed new value).I'm not sure
v4l2_ctrlis a good name. We already have av4l2_controlstructure, and using the abbreviated name for the in-kernel version is going to be confusing.
Originally it was called v4l2_ctrl_info but that became very cumbersome. It's not really an info object anyway, it really describes a control. When using this framework the driver no longer sees the v4l2_control anymore, so from the point of view of the driver there is only v4l2_ctrl.
Quoted 7 lines
It's obviously too late to change
v4l2_controlto something else.v4l2_control_infoandv4l2_control_valueare two names that come to my mind. Actually, it might be a good idea to split thev4l2_ctrlstructure into a static driver-wide structure (v4l2_control_info) and an instance-specific structure (v4l2_control_value). There's no point in storing the same static data (function pointers, names, ...) for identical controls in different devices.
Other than the name there isn't anything that is static. And the name is already static.
Quoted 15 lines
Quoted 15 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +v4l2_ctrl_handler is the object that keeps track of controls. It maintainsa
Documentation/video4linux/v4l2-controls.txt new file +11
-0+list of v4l2_ctrl objects that it owns and another list of references to +controls, possibly to controls owned by other handlers. + + +Basic usage +=========== + +1) Prepare the driver: + +- Add the handler to your main bridge driver or sub-device driver top-level + struct:
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 48
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + struct foo_dev { + ... + struct v4l2_ctrl_handler hdl;Please use more descriptive names in the examples, such as "
ctrl_handler". I expect some (many ?) developers to use the same names as the documentation does, making the driver code a bit difficult to read.
Good point.
Quoted 15 lines
Quoted 11 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ ... + }; + + struct foo_dev *foo; + +- Initialize handler: + + v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls); + + The second argument is a hint telling the function how many controlsQuoted 2 lines
this
+ handled is expected to handle. It will allocate a hashtable based on thiss/handled/handler/
Thanks.
Quoted 14 lines
Quoted 14 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ information. It is a hint only. + +- Hooking the control handler into a driver: + + When a subdevice is being registered with a bridge driver and the + ctrl_handler fields of both v4l2_subdev and v4l2_device are set, thenthe
+ controls of the subdev will become automatically available in the bridge
+ driver as well. If the subdev driver contains controls that already exist in
+ the bridge driver, then those will be skipped (so a bridge driver can always
Quoted 8 lines
Quoted 1 line
+ override a subdev control).
I think you should split the documentation differently. You're mixing video devices, bridges and subdevs. Developers of drivers that use no subdev will have trouble understanding the documentation. You should first describe how to use the framework in a simple driver (no host, bridge, subdev, ... just a
foo_devand a singlevideo_device), and then add sections to describe bridge- specific and subdev-specific APIs.
Good idea.
Quoted 7 lines
Documentation/video4linux/v4l2-controls.txt line 73
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + How to hook the handler into a bridge driver: + + foo->v4l2_dev.ctrl_handler = &foo->hdl;Is
v4l2_devan instance ofvideo_deviceorv4l2_device? It would be nice to add the field to thefoo_devstructure in the example.
Good point. I'll add that.
Quoted 9 lines
Documentation/video4linux/v4l2-controls.txt line 78
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+ + And whenever you call video_register_device() you must set the + ctrl_handler field of struct video_device as well: + + vdev->ctrl_handler = &foo->hdl;I suppose that the above
foo->v4l2_devis an instance ofv4l2_devicethen. Does the framework requirev4l2_device? If so, that should be explicit in the introduction.
Yes, it does require v4l2_device. I'll make that explicit.
Quoted 2 lines
Why can't the framework find the
ctrl_handlerinstance from the video_device'sv4l2_deviceparent ?
Sometimes you want to provide different control handlers to different video devices. E.g. /dev/radio0 may have only a small subset of the controls of /dev/video0 (bttv does that, for example).
One option is to automatically let video_register_device copy ctrl_handler from the v4l2_dev struct, but I think it is clearer if it is explicitly set.
Quoted 14 lines
Quoted 14 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ Finally, remove all control functions from your v4l2_ioctl_ops: + vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl, + vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls. + Those are now no longer needed. + + How to hook the control handler into a subdev driver: + + foo->sd.ctrl_handler = &foo->hdl; + + And set all core control ops in your struct v4l2_subdev_core_ops tothese
Documentation/video4linux/v4l2-controls.txt new file +3
-0+ helpers: + + .queryctrl = v4l2_sd_queryctrl,
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 98
Quoted 6 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ .querymenu = v4l2_sd_querymenu, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls,s/sd/subdev/ ?
I have to sleep on that... :-)
Quoted 17 lines
Quoted 17 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ + This is for backwards compatibility. Once all bridge drivers areconverted
+ these control ops can be removed just as they are already for bridge drivers.Documentation/video4linux/v4l2-controls.txt new file +11
-0+ +- Clean up the handler at the end: + + v4l2_ctrl_handler_free(&foo->hdl); + + +2) Add controls: + +Typically done right after the handler_init: + + v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls);
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 116
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128);Could you add the
v4l2_ctrl_new_stdfunction prototype to the documentation ? I expect the parameters to be min, max, step and default, but it would be better to make it explicit.
OK.
Quoted 1 line
Should't
v4l2_ctrl_new_stdtake a control type as well ?
The type is set automatically as that is determined by the control ID.
Quoted 7 lines
What about hardware for which the boundaries are only known at runtime, or could depend on the values of other controls ? I'm thinking about UVC devices for instance, the boundaries, step and default values need to be retrieved from the hardware. I currently do that at runtime when the control is queried for the first time and cache the values, as doing it during initialization (probe function) crashes a few webcams. That doesn't seem to be possible with the control framework.
It is possible to add controls to an existing control handler at runtime. It is also possible to change boundaries at runtime: you just change the relevant values in v4l2_ctrl. There is no function for that, it's enough to call v4l2_ctrl_lock(), change the values and call unlock().
I could make a function that does this, but UVC is the only driver that I know of that might need this.
Quoted 19 lines
Quoted 19 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ ... + if (foo->hdl.error) { + int err = foo->hdl.error; + + v4l2_ctrl_handler_free(&foo->hdl); + return err; + } + +The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new +control, but if you do not need to access the pointer outside the controlops,
Documentation/video4linux/v4l2-controls.txt new file +3
-0+then there is no need to store it. + +Note that if something fails, the function will return NULL or an errorand
Documentation/video4linux/v4l2-controls.txt new file +2
-0+set hdl->error to the error code. If hdl->error was already set, then it +will just return and do nothing. This is also true for
v4l2_ctrl_handler_init
Quoted 8 lines
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +3
-0+if it cannot allocate the internal data structure. + +This makes it easy to init the handler and just add all controls and onlycheck
+the error code at the end. Saves a lot of repetitive error checking.I would still check the
v4l2_ctrl_handler_initreturn value explicitly, but that may be just me.
Feel free to do that :-)
Quoted 6 lines
Documentation/video4linux/v4l2-controls.txt line 138
Quoted 2 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+It is recommended to add controls in ascending control ID order: it will be +a bit faster that way.As long as it's not required that's OK. It would be difficult to do so on UVC hardware, as controls are discovered based on information reported by the device, and those are obviously not ordered based on the V4L2 CIDs :-)
It is a recommendation only, it will work in any order.
Quoted 11 lines
Documentation/video4linux/v4l2-controls.txt line 146
Quoted 8 lines
+3) Optionally force initial control setup:
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ + v4l2_ctrl_handler_setup(&foo->hdl); + +This will call s_ctrl for all controls unconditionally. Effectively this +initializes the hardware to the default control values. It is recommended +that you do this.What about the other way around, if I want to initialize the framework with the current values retrieved from the hardware ?
In that case you would set the default value of the control based on the value reported by the hardware.
Quoted 70 lines
Quoted 70 lines
+4) Finally: implement the
v4l2_ctrl_opsDocumentation/video4linux/v4l2-controls.txt new file +27
-0+ + static const struct v4l2_ctrl_ops foo_ctrl_ops = { + .s_ctrl = foo_s_ctrl, + }; + +Usually all you need is s_ctrl: + + static int foo_s_ctrl(struct v4l2_ctrl *ctrl) + { + struct foo *state = container_of(ctrl->handler, struct foo, hdl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + write_reg(0x123, ctrl->val); + break; + case V4L2_CID_CONTRAST: + write_reg(0x456, ctrl->val); + break; + } + return 0; + } + +The control ops are called with the v4l2_ctrl pointer as argument. +The new control value has already been validated, so all you need to do is +to actually update the hardware registers. + +You're done! And this is sufficient for most of the drivers we have. Noneed
+to do any validation of control values, or implement QUERYCTRL/QUERYMENU. AndDocumentation/video4linux/v4l2-controls.txt new file +4
-0+G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported. + + +===============================================================================
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +The remainder of this document deals with more advanced topics andscenarios.
+In practice the basic usage as described above is sufficient for most drivers.
+
+==========================================================================
=====Documentation/video4linux/v4l2-controls.txt new file +22
-0+ + +Accessing Control Values +======================== + +The v4l2_ctrl struct contains these two unions: + + /* The current control value. */ + union { + s32 val; + s64 val64; + char *string; + } cur; + + /* The new control value. */ + union { + s32 val; + s64 val64; + char *string; + }; + +Within the control ops you can freely use these. The val and val64 speakfor
Documentation/video4linux/v4l2-controls.txt new file +3
-0+themselves. The string pointers point to character buffers of length +ctrl->maximum + 1, and are always 0-terminated. +
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 215
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+For g_ctrl you have to update the current control values like this: + + ctrl->cur.val = read_reg(0x123); + +The 'new value' union is not relevant in g_ctrl.When is
g_ctrlcalled ? When the userspace applications issues aVIDIOC_G_CTRLorVIDIOC_G_EXT_CTRLSioctl ?
Yes.
Quoted 5 lines
I suppose the operation only needs to be
implemented if the device needs to be queried because it modified the control value on its own (possibly as a result of setting another control). What if some controls need to be handled that way, while the others are not self- modifying ?
g_ctrl only has to take care of self-modifying controls and can ignore the others. saa7115 has a use case like that.
Quoted 27 lines
Quoted 19 lines
+For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
Documentation/video4linux/v4l2-controls.txt new file +4
-0+you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union +contains the current value, which you can use (but not change!) as well. + +If s_ctrl returns 0 (OK), then the control framework will copy the newfinal
Documentation/video4linux/v4l2-controls.txt new file +3
-0+values to the 'cur' union. + +While in g/s/try_ctrl you can access the value of all controls owned bythe
Documentation/video4linux/v4l2-controls.txt new file +7
-0+same handler since the handler's lock is held. Do not attempt to access +the value of controls owned by other handlers, though. + +Elsewhere in the driver you have to be more careful. You cannot just refer +to the current control values without locking. + +There are two simple helper functions defined that will get or set asingle
Documentation/video4linux/v4l2-controls.txt line 235
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+control value safely: + + s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl); + int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);I suppose that
v4l2_ctrl_scan be used if the device notifies the driver that a control has changed. Is that correct ?
Quoted 2 lines
I don't really like the names of those two functions. For instance,
v4l2_ctrl_slooks like it will call the driver to set a control.
I think there is a misunderstanding here: v4l2_ctrl_s() will indeed change the control's value.
Quoted 46 lines
Quoted 39 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +Don't use these inside the control ops g/s/try_ctrl, though, that willfail.
Documentation/video4linux/v4l2-controls.txt new file +21
-0+ +You can also take the handler lock yourself: + + mutex_lock(&state->hdl.lock); + printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string); + printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val); + mutex_unlock(&state->hdl.lock); + + +Menu Controls +============= + +Menu controls use the 'step' value differently compared to other control +types. The v4l2_ctrl struct contains this union: + + union { + u32 step; + u32 menu_skip_mask; + }; + +For menu controls menu_skip_mask is used. What it does is that it allowsyou
Documentation/video4linux/v4l2-controls.txt new file +9
-0+to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU +implementation where you can return -EINVAL if a certain menu item is not +present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for +menu controls. + +A good example is the MPEG Audio Layer II Bitrate menu control where the +menu is a list of standardized possible bitrates. But in practice hardware +implementations will only support a subset of those. By setting the skip +mask you can tell the framework which menu items should be skipped.Setting
Documentation/video4linux/v4l2-controls.txt new file +3
-0+it to 0 means that all menu items are supported. + +So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need toQuoted 4 lines
remember
+that 'step' means 'skip mask' for menu controls. If you put in '1' by mistake,
+then the first menu item will be skipped.What about adding a
v4l2_ctrl_new_menuthen ? That will avoid such a mistake.
Quoted 9 lines
Quoted 5 lines
+The
v4l2_ctrl_new_std_menucan be used to add menu controls more easily:
it
+will calculate the min and max values automatically based on the size of the
+menu, and it has a proper 'mask' argument instead of 'step'.OK :-) Shouldn't
v4l2_ctrl_new_custombe restricted to non-menu controls then ? Looking at the code, it should probably become an internal function, with another function added for custom controls without menu support.
I might add that in the future. I want to avoid creating a long series of different functions, all alike :-)
Quoted 12 lines
Quoted 12 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ + +Active and Grabbed Controls +=========================== + +If you get more complex relationships between controls, then you may haveto
Documentation/video4linux/v4l2-controls.txt new file +4
-0+activate and deactivate controls. For example, if the Chroma AGC control is +on, then the Chroma Gain control is inactive. That is, you may set it, but +the value will not be used by the hardware as long as the automatic gain +control is on. Typically user interfaces can disable such input fields.
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 291
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+ +You can set the 'active' status using v4l2_ctrl_activate(). By default all +controls are active. Note that the framework does not check for this flag. +It is meant purely for GUIs. The function is typically called from within +s_ctrl.What you refer to as active/inactive is usually referred to as enabled/disabled in the GUI world. Might be worth using the same convention.
Strictly speaking it is not quite the same. An inactive control can still be written, the new value will simply not be used until the control becomes active again. In a GUI that would be confusing, so the control is disabled instead. It's a subtle difference that may or may not be important.
Quoted 28 lines
Quoted 28 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +The other flag is the grabbed flag. A grabbed control means that youcannot
Documentation/video4linux/v4l2-controls.txt new file +4
-0+change it because it is in use by some resource. Typical examples are MPEG +bitrate controls that cannot be changed while capturing is in progress. + +If a control is set to 'grabbed' using v4l2_ctrl_grab(), then theframework
Documentation/video4linux/v4l2-controls.txt new file +20
-0+will return -EBUSY if an attempt is made to set this control. + +Since this flag is used by the framework the v4l2_ctrl_grab function will +take the control handler's lock. So it cannot be called from within the +control ops. Instead this is typically called from the driver when it +starts streaming. + + +Control Clusters +================ + +By default all controls are independent from the others. But in more +complex scenarios you can get dependencies from one control to another. +In that case you need to 'cluster' them: + + struct foo { + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + ...
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 322
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+ }; + + state->volume = v4l2_ctrl_new_std(&state->hdl, ...); + state->mute = v4l2_ctrl_new_std(&state->hdl, ...); + v4l2_ctrl_cluster(2, &state->volume);What's the first argument to
v4l2_ctrl_cluster? The number of controls in the cluster ?
Yes.
Quoted 5 lines
Does that imply that they need to be added in a sequence, right before
v4l2_ctrl_clusteris called ? That seems a bit awkward. Wouldn't it be better to specify the relationships between controls explicitly, maybe by passing a pointer to the master control when creating the 'slave' controls in the cluster ?
OK, I clearly need to explain this better.
What you pass is an array of v4l2_ctrl pointers, e.g.:
struct foo {
struct v4l2_ctrl *cluster[2];
...
};
foo->cluster[CTRL_VOLUME] = v4l2_ctrl_new_std();
foo->cluster[CTRL_MUTE] = v4l2_ctrl_new_std();
v4l2_ctrl_cluster(2, foo->cluster);cluster[0] is always the master.
Now, using arrays here becomes quickly very annoying, so instead you can just say:
struct foo {
/* volume/mute cluster */
struct v4l2_ctrl *volume;
struct v4l2_ctrl *mute;
...
};
foo->volume = v4l2_ctrl_new_std();
foo->mute = v4l2_ctrl_new_std();
v4l2_ctrl_cluster(2, &foo->volume);Same thing, but without the cumbersome array indices.
Quoted 43 lines
Quoted 43 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+From now on whenever one or more of the controls belonging to the same +cluster is set (or 'gotten', or 'tried'), only the control ops of thefirst
Documentation/video4linux/v4l2-controls.txt new file +4
-0+control ('volume' in this example) is called. You effectively create a new +composite control. Similar to how a 'struct' works in C. + +So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you shouldset
Documentation/video4linux/v4l2-controls.txt new file +34
-0+all two controls belonging to the 'volume' cluster: + + static int foo_s_ctrl(struct v4l2_ctrl *ctrl) + { + struct foo *state = container_of(ctrl->handler, struct foo, hdl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + /* volume cluster */ + write_reg(0x123, state->mute->val ? 0 : ctrl->val); + break; + case V4L2_CID_CONTRAST: + write_reg(0x456, ctrl->val); + break; + } + return 0; + } + +In the example above the following are equivalent for the VOLUME case: + + ctrl == state->volume == ctrl->cluster[0] + state->mute == ctrl->cluster[1] + +Note that controls in a cluster may be NULL. For example, if for some +reason mute was never added (because the hardware doesn't support that +particular feature), then mute will be NULL. So in that case we have a +cluster of 2 controls, of which only 1 is actually instantiated. The +only restriction is that the first control of the cluster must already be +present, since that is the 'master' control of the cluster. The master +control is the one that identifies the cluster and that provides the +pointer to the v4l2_ctrl_ops struct that is used for that cluster. + + +VIDIOC_LOG_STATUS Support
Quoted 8 lines
Quoted 4 lines
+=========================
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +This ioctl allow you to dump the current status of a driver to the kernellog.
This has nothing to do with the controls framework, but shouldn't that ioctl be restricted to root only ? It can potentially dump a lot of information to the kernel log, and thus system logs.
Personally I see no reason to change this.
Quoted 16 lines
Quoted 16 lines
+The
v4l2_ctrl_handler_log_status(hdl, prefix)can be used to dump the value ofDocumentation/video4linux/v4l2-controls.txt new file +14
-0+the controls owned by the given handler to the log. You can supply a prefix +as well. If the prefix didn't end with a space, then ': ' will be added for +you. + + +Different Handlers for Different Video Nodes +============================================ + +Usually the bridge driver has just one control handler that is global for +all video nodes. But you can also specify different control handlers for +different video nodes. It's no problem if there are no subdevs involved. +But if there are, then you need to block the automatic merging of subdev +controls to the global control handler. You do that by simply setting the +ctrl_handler field in struct v4l2_device to NULL.
Quoted 8 lines
Quoted 6 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +After each subdev was added, you will then have to call
v4l2_ctrl_add_handler
+manually to add the subdev's control handler (sd->ctrl_handler) to the desired
+bridge control handler.Do you mean video device instead of bridge here ?
struct v4l2_device.
Quoted 4 lines
I wouldn't mention "bridge" in here. I assume that by bridge you mean
v4l2_device. Please use that name directly. The term "bridge" is only applicable to a subset of thev4l2_deviceuse cases.
Good point.
Quoted 2 lines
Can controls for a specific subdev be reported through more than one video device node, but not all of them ?
Yes. It will be a bit fiddly to set up, but that's to be expected if you want to do things like that.
Quoted 81 lines
Quoted 81 lines
Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +If you want to have one handler (e.g. for a radio device node) have asubset
Documentation/video4linux/v4l2-controls.txt new file +32
-0+of another handler (e.g. for a video device node), then you can first add +the controls to the first handler, add the other controls to the second +handler and finally add the first handler to the second. For example: + + v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...); + v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...); + v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...); + v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_CONTRAST, ...); + v4l2_ctrl_add_handler(&video_hdl, &radio_hdl); + +Or you can add specific controls to a handler: + + volume = v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_AUDIO_VOLUME, ...); + v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_BRIGHTNESS, ...); + v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_CONTRAST, ...); + v4l2_ctrl_add_ctrl(&radio_hdl, volume); + +What you should not do is make two identical controls for two handlers. +For example: + + v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...); + v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_AUDIO_MUTE, ...); + +This would be bad since muting the radio would not change the video mute +control. The rule is to have one control for each hardware 'knob' that you +can twiddle. + + +Finding Controls +================ + +Normally you have created the controls yourself and you can store thestruct
Documentation/video4linux/v4l2-controls.txt new file +12
-0+v4l2_ctrl pointer into your own struct. + +But sometimes you need to find a control from another handler that you do +not own. For example, if you have to find a volume control from a subdev. + +You can do that by calling v4l2_ctrl_find: + + struct v4l2_ctrl *volume; + + volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME); + +Since v4l2_ctrl_find will lock the handler you have to be careful whereyou
Documentation/video4linux/v4l2-controls.txt new file +23
-0+use it. For example, this is not a good idea: + + struct v4l2_ctrl_handler hdl; + + v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...); + v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_CONTRAST, ...); + +...and in video_ops.s_ctrl: + + case V4L2_CID_BRIGHTNESS: + contrast = v4l2_find_ctrl(&hdl, V4L2_CID_CONTRAST); + ... + +When s_ctrl is called by the framework the hdl.lock is already taken, so +attempting to find another control from the same handler will deadlock. + +It is recommended not to use this function from inside the control ops. + + +Inheriting Controls +=================== + +When one control handler is added to another using v4l2_ctrl_add_handler,then
+by default all controls from one are merged to the other. But a subdev might
+have low-level controls that make sense for some advanced embedded system, but
+not when it is used in consumer-level hardware. In that case you want to keep
+those low-level controls local to the subdev. You can do this by simply
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 463
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+setting the 'is_private' flag of the control to 1: + + ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...); + if (ctrl) + ctrl->is_private = 1;Wouldn't it make more sense to pass that as an argument to
v4l2_ctrl_new_custom?
As I said earlier, I tried to avoid creating zillions of very similar functions, or functions with very long arg lists. For now at least private controls are not used at all.
It is usually easier for the driver to write a few special static inlines that do exactly what the driver needs. Of course, if many drivers need to do the same thing, then such inlines should be moved to the core header.
Quoted 3 lines
I'm actually wondering if it wouldn't be better to pass a pointer to a structure with the required information (maybe the
v4l2_control_infostructure I mentioned earlier ?) tov4l2_ctrl_new_custom.
I thought about that, but 1) that means yet another structure and 2) I dislike code like that because the definition is usually quite far from the place were it is actually used.
Quoted 15 lines
Quoted 15 lines
Documentation/video4linux/v4l2-controls.txt new file +7
-0+These controls will now be skipped when v4l2_ctrl_add_handler is called. + + +Strict Control Validation +========================= + +By default when the application wants to change an integer control thevalue
+passed to the framework will automatically be modified to map to the provided
+minimum, maximum and step values of the control. If instead you just want to
+validate the value and not modify it, then set the 'strict_validation' flag of
+the control:
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 480
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+ + ctrl->strict_validation = 1; + +Now -ERANGE will be returned if the new value does not match the control's +requirements.Why do we need the two behaviours ? Wouldn't it be better to standardize on one of them ?
I may have fallen in the pitfall of featurism here. Just because it is easy to implement, that doesn't mean that you should actually do it.
I think I'll remove strict_validation.
Quoted 82 lines
Quoted 41 lines
+This is currently specific to integer controls. The value for boolean controls
Documentation/video4linux/v4l2-controls.txt new file +8
-0+is always mapped to 0 or 1, menu and string controls are already validated +strictly, and integer64 controls are not validated at all. + + +V4L2_CTRL_TYPE_CTRL_CLASS Controls +================================== + +Controls of this type can be used by GUIs to get the name of the controlclass.
Documentation/video4linux/v4l2-controls.txt new file +2
-0+A fully featured GUI can make a dialog with multiple tabs with each tab +containing the controls belonging to a particular control class. The nameof
+each tab can be found by querying a special control with ID <control class | 1>.Documentation/video4linux/v4l2-controls.txt new file +2
-0+ +Drivers do not have to care about this. The framework will automaticallyadd
+a control of this type whenever the first control belonging to a new controlDocumentation/video4linux/v4l2-controls.txt new file +7
-0+class is added. + + +Differences from the Spec +========================= + +There are a few places where the framework acts slightly differently fromthe
+V4L2 Specification. Those differences are described in this section. We willDocumentation/video4linux/v4l2-controls.txt new file +4
-0+have to see whether we need to adjust the spec or not. + +1) It is no longer required to have all controls contained in a +v4l2_ext_control array be from the same control class. The framework willbe
Documentation/video4linux/v4l2-controls.txt new file +5
-0+able to handle any type of control in the array. You need to set ctrl_class +to 0 in order to enable this. If ctrl_class is non-zero, then it will still +check that all controls belong to that control class. + +If you set ctrl_class to 0 and count to 0, then it will only return anQuoted 2 lines
error
+if there are no controls at all.I don't know why we had such a limitation in the first place. I would indeed remove it completely.
Documentation/video4linux/v4l2-controls.txt line 522
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +3
-0+ +2) Clarified the way error_idx works. For get and set it will be equal to +count if nothing was done yet. If it is less than count then only thecontrols
Documentation/video4linux/v4l2-controls.txt new file +5
-1+up to error_idx-1 were successfully applied. + +3) When attempting to read a button control the framework will return -EACCES +instead of -EINVAL as stated in the spec. It seems to make more sense since +button controls are write-only controls.Agreed. We should change the spec.
Quoted 3 lines
+4) Attempting to write to a read-only control will return
-EACCESinstead of
+-EINVAL as the spec says.This makes sense as well.
Quoted 16 lines
+5) The spec does not mention what should happen when you try to set/get a
Documentation/video4linux/v4l2-controls.txt new file +15
-0+control class controls. ivtv currently returns -EINVAL (indicating that the +control ID does not exist) while the framework will return -EACCES, which +makes more sense. + + +Proposals for Extensions +======================== + +Some ideas for future extensions to the spec: + +1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of +decimal. Useful for e.g. video_mute_yuv. + +2) It is possible to mark in the controls array which controls have been +successfully written and which failed by for example adding a bit to the
Quoted 8 lines
Quoted 1 line
+control ID. Not sure if it is worth the effort, though.
I still feel a bit awkward about the interface. One particular point that might require attention is the split of the
v4l2_ctrlstructure intov4l2_control_infoandv4l2_control_valuestructures (the names are not set into stone). I would also like to see if we can't pass pointers tov4l2_control_info(or similar) to the control creation functions instead of a plethora of arguments.
One of my original attempts was indeed to have two structs. But that made the code only more complex. Having two structs only makes sense if the 'static' part can be, well, static. But then you have UVC where that isn't static. So the code then has to differentiate between these two cases and it quickly becomes a big mess.
Regarding the plethora of arguments: remember that 99% of all controls will just use v4l2_ctrl_new_std(). And that ain't too bad. new_custom is indeed a big one, but currently only used by cx2341x where it is wrapped inside a much easier to digest function. I've tried to make the 90% of easy cases easy, and for the remaining 10% you will indeed have to do more work.
Quoted 3 lines
I'll now have a look at the code. As there are already quite a few comments on the documentation I won't perform a very in-depth code review though, I'll save that for later after we agree on the spec :-)
Thanks for the review!
This was very useful.
Regards,
Hans
Signature · 1 line
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
Hi Hans,
Quoted 2 lines · On Monday 03 May 2010 00:21:46 Hans Verkuil wrote:
Quoted 1 line · On Sunday 02 May 2010 22:39:11 Laurent Pinchart wrote:
On Monday 26 April 2010 09:33:38 Hans Verkuil wrote:
[snip]
Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Objects in the framework +======================== + +There are two main objects:Quoted 8 lines
Quoted 4 lines
(quoted diff) +2
-0+ +The v4l2_ctrl object describes the control properties and keeps trackof the
+control's value (both the current value and the proposed new value).I'm not sure
v4l2_ctrlis a good name. We already have av4l2_controlstructure, and using the abbreviated name for the in-kernel version is going to be confusing.
Quoted 4 lines
Originally it was called
v4l2_ctrl_infobut that became very cumbersome. It's not really an info object anyway, it really describes a control. When using this framework the driver no longer sees thev4l2_controlanymore, so from the point of view of the driver there is onlyv4l2_ctrl.
Sure, but it's still misleading. Using an both the abbreviated and non- abbreviated names to refer to two different things has been done too often in V4L2, and it's starting to bother me. It makes the API look inconsistent. We need to define a naming policy and stick to it for the future. That can be a topic for the V4L2 summit in June.
Quoted 10 lines
Quoted 7 lines
It's obviously too late to change
v4l2_controlto something else.v4l2_control_infoandv4l2_control_valueare two names that come to my mind. Actually, it might be a good idea to split thev4l2_ctrlstructure into a static driver-wide structure (v4l2_control_info) and an instance-specific structure (v4l2_control_value). There's no point in storing the same static data (function pointers, names, ...) for identical controls in different devices.Other than the name there isn't anything that is static. And the name is already static.
The following fields are static (they're identical across all v4l2_ctrl instances for the same control):
- ops
- id
- name
- type
- menu (not too sure about this one)
[snip]
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + How to hook the handler into a bridge driver: + + foo->v4l2_dev.ctrl_handler = &foo->hdl;
[snip]
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+ + And whenever you call video_register_device() you must set the + ctrl_handler field of struct video_device as well: + + vdev->ctrl_handler = &foo->hdl;
[snip]
Quoted 10 lines
Quoted 2 lines
Why can't the framework find the
ctrl_handlerinstance from the video_device'sv4l2_deviceparent ?Sometimes you want to provide different control handlers to different video devices. E.g.
/dev/radio0may have only a small subset of the controls of/dev/video0(bttv does that, for example).One option is to automatically let
video_register_devicecopyctrl_handlerfrom thev4l2_devstruct, but I think it is clearer if it is explicitly set.
I was thinking about copying it implicitly if it's not set, yes. That way only the v4l2_device instance would need an explicit handler, video devices would inherit it from their v4l2_device parent. The driver would still have the ability to set it if it wants to override the default behaviour.
It might be clearer to always set it explicitly, I have no strong opinion on this one.
Quoted 23 lines
Quoted 14 lines
Quoted 14 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ Finally, remove all control functions from your v4l2_ioctl_ops: + vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl, + vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls. + Those are now no longer needed. + + How to hook the control handler into a subdev driver: + + foo->sd.ctrl_handler = &foo->hdl; + + And set all core control ops in your struct v4l2_subdev_core_ops tothese
Documentation/video4linux/v4l2-controls.txt new file +3
-0+ helpers: + + .queryctrl = v4l2_sd_queryctrl,Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 98
Quoted 6 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ .querymenu = v4l2_sd_querymenu, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls,s/sd/subdev/ ?
Quoted 1 line
I have to sleep on that... :-)
You've slept on it for a week now :-)
We really have too many abbreviations in V4L2, or at least no consistent usage of them.
[snip]
Quoted 8 lines
Quoted 7 lines
What about hardware for which the boundaries are only known at runtime, or could depend on the values of other controls ? I'm thinking about UVC devices for instance, the boundaries, step and default values need to be retrieved from the hardware. I currently do that at runtime when the control is queried for the first time and cache the values, as doing it during initialization (probe function) crashes a few webcams. That doesn't seem to be possible with the control framework.
Quoted 7 lines
It is possible to add controls to an existing control handler at runtime. It is also possible to change boundaries at runtime: you just change the relevant values in
v4l2_ctrl. There is no function for that, it's enough to callv4l2_ctrl_lock(), change the values and callunlock().I could make a function that does this, but UVC is the only driver that I know of that might need this.
My issue is that the UVC driver would need to set the boundaries the first time the control is queried. There's no callback for that at the moment.
Quoted 28 lines
Quoted 19 lines
Quoted 19 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ ... + if (foo->hdl.error) { + int err = foo->hdl.error; + + v4l2_ctrl_handler_free(&foo->hdl); + return err; + } + +The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new +control, but if you do not need to access the pointer outside thecontrol ops,
Documentation/video4linux/v4l2-controls.txt new file +3
-0+then there is no need to store it. + +Note that if something fails, the function will return NULL or an errorand
+sethdl->errorto the error code. Ifhdl->errorwas already set, then it
+will just return and do nothing. This is also true forv4l2_ctrl_handler_initQuoted 8 lines
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +3
-0+if it cannot allocate the internal data structure. + +This makes it easy to init the handler and just add all controls andonly check
+the error code at the end. Saves a lot of repetitive error checking.I would still check the
v4l2_ctrl_handler_initreturn value explicitly, but that may be just me.
Quoted 1 line
Feel free to do that :-)
You know what I meant :-) I agree this is a good way to handle errors for v4l2_ctrl_new_*, but v4l2_ctrl_handler_init feels... different.
[snip]
Quoted 12 lines
Quoted 11 lines
Quoted 8 lines
+3) Optionally force initial control setup:
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + v4l2_ctrl_handler_setup(&foo->hdl); + +This will call s_ctrl for all controls unconditionally. Effectivelythis
+initializes the hardware to the default control values. It is recommended +that you do this.What about the other way around, if I want to initialize the framework with the current values retrieved from the hardware ?
Quoted 2 lines
In that case you would set the default value of the control based on the value reported by the hardware.
The default value can still be different than the current value. Don't you mean I should initialize the current value, not the default one ?
What should be recommended, calling v4l2_ctrl_handler_setup, or initializing the framework with the current value ?
Thinking some more about this, it can be a problem for UVC devices, much like querying the min/max/step/default values is. Some devices just crash if you send too many requests too fast. I would like to get a g_ctrl call the first time the current value needs to be read, and then have the framework cache that value.
By the way (still more thinking :-)), does the framework handle a value cache that can be configured per control ? Some controls are volatile and need to be read from the driver every time, some others can be cached. How is that handled ?
Last thought: does the framework query the driver for the current control values when setting a control in a cluster ? If two clustered controls are stored in a single hardware "register", and one of them needs to be modified, a read-update-write operation needs to be performed. The update part will be done by the framework, the write part by the driver, but what about the read part ?
[snip]
Quoted 33 lines
Quoted 24 lines
Quoted 24 lines
Documentation/video4linux/v4l2-controls.txt new file +20
-0+Accessing Control Values +======================== + +The v4l2_ctrl struct contains these two unions: + + /* The current control value. */ + union { + s32 val; + s64 val64; + char *string; + } cur; + + /* The new control value. */ + union { + s32 val; + s64 val64; + char *string; + }; + +Within the control ops you can freely use these. The val and val64speak for
Documentation/video4linux/v4l2-controls.txt new file +3
-0+themselves. The string pointers point to character buffers of length +ctrl->maximum + 1, and are always 0-terminated. +Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 215
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+For g_ctrl you have to update the current control values like this: + + ctrl->cur.val = read_reg(0x123); + +The 'new value' union is not relevant in g_ctrl.When is
g_ctrlcalled ? When the userspace applications issues aVIDIOC_G_CTRLorVIDIOC_G_EXT_CTRLSioctl ?
Quoted 1 line
Yes.
OK. Can you make that explicit ?
Quoted 7 lines
Quoted 4 lines
I suppose the operation only needs to be implemented if the device needs to be queried because it modified the control value on its own (possibly as a result of setting another control). What if some controls need to be handled that way, while the others are not self-modifying ?
g_ctrlonly has to take care of self-modifying controls and can ignore the others. saa7115 has a use case like that.
Instead of calling g_ctrl for all drivers, have it go through a big switch and do nothing, wouldn't it be better to add a volatile flag to the v4l2_ctrl structure ? g_ctrl calls would be skipped for non-volatile controls (except on the first read of course).
Quoted 27 lines
Quoted 27 lines
Quoted 19 lines
+For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
(quoted diff) +2
-0+you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union +contains the current value, which you can use (but not change!) aswell. +
+Ifs_ctrlreturns 0 (OK), then the control framework will copy the new finalDocumentation/video4linux/v4l2-controls.txt new file +3
-0+values to the 'cur' union. + +While in g/s/try_ctrl you can access the value of all controls ownedby the
+same handler since the handler's lock is held. Do not attempt to access +the value of controls owned by other handlers, though.(quoted diff) +2
-0+ +Elsewhere in the driver you have to be more careful. You cannot justrefer +to the current control values without locking.
(quoted diff) +2
-0+ +There are two simple helper functions defined that will get or set asingle
Documentation/video4linux/v4l2-controls.txt line 235
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+control value safely: + + s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl); + int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);I suppose that
v4l2_ctrl_scan be used if the device notifies the driver that a control has changed. Is that correct ?
Quoted 5 lines
Quoted 2 lines
I don't really like the names of those two functions. For instance,
v4l2_ctrl_slooks like it will call the driver to set a control.I think there is a misunderstanding here:
v4l2_ctrl_s()will indeed change the control's value.
Do you mean that it will call the driver's s_ctrl operation or that it will modify the current value in the v4l2_ctrl structure ? The misunderstanding probably calls for a function name change :-)
[snip]
Quoted 45 lines
Quoted 34 lines
Quoted 26 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Menu Controls +============= + +Menu controls use the 'step' value differently compared to othercontrol +types. The
v4l2_ctrlstruct contains this union:Documentation/video4linux/v4l2-controls.txt new file +7
-0+ + union { + u32 step; + u32 menu_skip_mask; + }; + +For menu controls menu_skip_mask is used. What it does is that itallows you
+to easily exclude certain menu items. This is used in theVIDIOC_QUERYMENU+implementation where you can return-EINVALif a certain menu item is not +present. Note thatVIDIOC_QUERYCTRLalways returns a step value of 1 for +menu controls.(quoted diff) +2
-0+ +A good example is the MPEG Audio Layer II Bitrate menu control wherethe +menu is a list of standardized possible bitrates. But in practice hardware +implementations will only support a subset of those. By setting the skip +mask you can tell the framework which menu items should be skipped. Setting
Documentation/video4linux/v4l2-controls.txt new file +3
-0+it to 0 means that all menu items are supported. + +So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need toQuoted 4 lines
remember
+that 'step' means 'skip mask' for menu controls. If you put in '1' by mistake,
+then the first menu item will be skipped.What about adding a
v4l2_ctrl_new_menuthen ? That will avoid such a mistake.Quoted 10 lines
Quoted 5 lines
+The
v4l2_ctrl_new_std_menucan be used to add menu controls more
easily: it
+will calculate the min and max values automatically based on the size of the
+menu, and it has a proper 'mask' argument instead of 'step'.OK :-) Shouldn't
v4l2_ctrl_new_custombe restricted to non-menu controls then ? Looking at the code, it should probably become an internal function, with another function added for custom controls without menu support.
Quoted 2 lines
I might add that in the future. I want to avoid creating a long series of different functions, all alike :-)
Then drivers will use the function that will later become internal. You'll have to change a bunch of drivers later, and you will tell me that it would be too much work and wouldn't be worth it ;-)
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Active and Grabbed Controls +=========================== + +If you get more complex relationships between controls, then you mayhave to
+activate and deactivate controls. For example, if the Chroma AGC control is +on, then the Chroma Gain control is inactive. That is, you may set it, but +the value will not be used by the hardware as long as the automatic gain +control is on. Typically user interfaces can disable such input fields. +Quoted 8 lines
Quoted 4 lines
+You can set the 'active' status using
v4l2_ctrl_activate(). By default all +controls are active. Note that the framework does not check for this flag. +It is meant purely for GUIs. The function is typically called from within +s_ctrl.What you refer to as active/inactive is usually referred to as enabled/disabled in the GUI world. Might be worth using the same convention.
Quoted 5 lines
Strictly speaking it is not quite the same. An inactive control can still be written, the new value will simply not be used until the control becomes active again. In a GUI that would be confusing, so the control is disabled instead. It's a subtle difference that may or may not be important.
What does the framework do when the userspace application attempts to write to a disabled control ?
[snip]
Quoted 57 lines
Quoted 12 lines
Quoted 12 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+Control Clusters +================ + +By default all controls are independent from the others. But in more +complex scenarios you can get dependencies from one control toanother. +In that case you need to 'cluster' them:
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ + struct foo { + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + ...Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 322
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+ }; + + state->volume = v4l2_ctrl_new_std(&state->hdl, ...); + state->mute = v4l2_ctrl_new_std(&state->hdl, ...); + v4l2_ctrl_cluster(2, &state->volume);What's the first argument to
v4l2_ctrl_cluster? The number of controls in the cluster ?Yes.
Quoted 5 lines
Does that imply that they need to be added in a sequence, right before
v4l2_ctrl_clusteris called ? That seems a bit awkward. Wouldn't it be better to specify the relationships between controls explicitly, maybe by passing a pointer to the master control when creating the 'slave' controls in the cluster ?OK, I clearly need to explain this better.
What you pass is an array of
v4l2_ctrlpointers, e.g.:struct foo { struct v4l2_ctrl *cluster[2]; ... }; foo->cluster[CTRL_VOLUME] = v4l2_ctrl_new_std(); foo->cluster[CTRL_MUTE] = v4l2_ctrl_new_std(); v4l2_ctrl_cluster(2, foo->cluster);cluster[0] is always the master.
Now, using arrays here becomes quickly very annoying, so instead you can just say:
struct foo { /* volume/mute cluster */ struct v4l2_ctrl *volume; struct v4l2_ctrl *mute; ... }; foo->volume = v4l2_ctrl_new_std(); foo->mute = v4l2_ctrl_new_std(); v4l2_ctrl_cluster(2, &foo->volume);
Quoted 1 line
Same thing, but without the cumbersome array indices.
That looks *very* dangerous. People will not get it (I haven't) and all kind of bad things will happen, like adding a field between two struct v4l2_ctrl pointers. This part of the API needs to be reworked, if you require an array then an array must be passed to the function.
[snip]
Quoted 11 lines
Quoted 9 lines
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+VIDIOC_LOG_STATUS Support +========================= + +This ioctl allow you to dump the current status of a driver to thekernel log.
This has nothing to do with the controls framework, but shouldn't that ioctl be restricted to root only ? It can potentially dump a lot of information to the kernel log, and thus system logs.
Personally I see no reason to change this.
You don't think it opens a door for unprivileged applications to flood the kernel and system logs ?
[snip]
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Different Handlers for Different Video Nodes +============================================ + +Usually the bridge driver has just one control handler that is globalfor +all video nodes. But you can also specify different control handlers for +different video nodes. It's no problem if there are no subdevs involved. +But if there are, then you need to block the automatic merging of subdev +controls to the global control handler. You do that by simply setting the +ctrl_handler field in struct
v4l2_deviceto NULL.Quoted 8 lines
Quoted 6 lines
(quoted diff) +2
-0+ +After each subdev was added, you will then have to call
v4l2_ctrl_add_handler
+manually to add the subdev's control handler (sd->ctrl_handler) to the desired
+bridge control handler.Do you mean video device instead of bridge here ?
Quoted 1 line
struct
v4l2_device.
I don't get it then. If you want to use different handlers for different video nodes, you need to add the subdev handler to the video node (video_device) handler, not the subdev handler to the v4l2_device handle.
Quoted 11 lines
Quoted 3 lines
I wouldn't mention "bridge" in here. I assume that by bridge you mean
v4l2_device. Please use that name directly. The term "bridge" is only applicable to a subset of thev4l2_deviceuse cases.Good point.
Quoted 2 lines
Can controls for a specific subdev be reported through more than one video device node, but not all of them ?
Yes. It will be a bit fiddly to set up, but that's to be expected if you want to do things like that.
I was just wondering if it was possible, I have no use case in mind. I wonder if we should even allow that...
[snip]
Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Inheriting Controls +=================== + +When one control handler is added to another using
v4l2_ctrl_add_handler, then
+by default all controls from one are merged to the other. But a subdev might
+have low-level controls that make sense for some advanced embedded system, but
+not when it is used in consumer-level hardware. In that case you want to keep
+those low-level controls local to the subdev. You can do this byQuoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 463
Quoted 5 lines
simply +setting the '
is_private' flag of the control to 1:Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...); + if (ctrl) + ctrl->is_private = 1;Wouldn't it make more sense to pass that as an argument to
v4l2_ctrl_new_custom?
Quoted 7 lines
As I said earlier, I tried to avoid creating zillions of very similar functions, or functions with very long arg lists. For now at least private controls are not used at all.
It is usually easier for the driver to write a few special static inlines that do exactly what the driver needs. Of course, if many drivers need to do the same thing, then such inlines should be moved to the core header.
"Maybe this feature won't be used" sounds more like a reason to remove it than to implement it badly and leave it to be fixed later :-)
The control framework is a big change to the V4L2 framework, we should take great care to get it as good as possible on the first try.
Quoted 7 lines
Quoted 3 lines
I'm actually wondering if it wouldn't be better to pass a pointer to a structure with the required information (maybe the
v4l2_control_infostructure I mentioned earlier ?) tov4l2_ctrl_new_custom.I thought about that, but 1) that means yet another structure and 2) I dislike code like that because the definition is usually quite far from the place were it is actually used.
Yes, it means yet another structure. On the other hand all those structures can be nicely grouped in an array, and controls can then be added using a loop instead of a bunch of function calls. I think it would be much cleaner, code would be more readable and error handling would be easier.
Quoted 24 lines
Quoted 15 lines
Quoted 15 lines
+These controls will now be skipped when
v4l2_ctrl_add_handleris called. +Documentation/video4linux/v4l2-controls.txt new file +5
-0+ +Strict Control Validation +========================= + +By default when the application wants to change an integer control thevalue
+passed to the framework will automatically be modified to map to the provided
+minimum, maximum and step values of the control. If instead you just want to
+validate the value and not modify it, then set the 'strict_validation' flag of
+the control:Quoted 8 lines
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + ctrl->strict_validation = 1; + +Now -ERANGE will be returned if the new value does not match thecontrol's +requirements.
Why do we need the two behaviours ? Wouldn't it be better to standardize on one of them ?
Quoted 4 lines
I may have fallen in the pitfall of featurism here. Just because it is easy to implement, that doesn't mean that you should actually do it.
I think I'll remove
strict_validation.
Thanks.
[snip]
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+Proposals for Extensions +======================== + +Some ideas for future extensions to the spec: + +1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimalinstead of +decimal. Useful for e.g.
video_mute_yuv.
+
+2) It is possible to mark in the controls array which controls have been +successfully written and which failed by for example adding aQuoted 8 lines
Quoted 1 line
bit to the +control ID. Not sure if it is worth the effort, though.
I still feel a bit awkward about the interface. One particular point that might require attention is the split of the
v4l2_ctrlstructure intov4l2_control_infoandv4l2_control_valuestructures (the names are not set into stone). I would also like to see if we can't pass pointers tov4l2_control_info(or similar) to the control creation functions instead of a plethora of arguments.
Quoted 3 lines
One of my original attempts was indeed to have two structs. But that made the code only more complex. Having two structs only makes sense if the 'static' part can be, well, static.
Indeed :-) Like I pointed out above there's some static information, but maybe not enough of them to split them into another structure. On the other hand, like I also pointed above, I think passing a structure, even if it's not big, to the control creation functions would make driver code cleaner.
Quoted 2 lines
But then you have UVC where that isn't static. So the code then has to differentiate between these two cases and it quickly becomes a big mess.
There's static information in UVC. Look at uvc_ctrl.c, it starts with two big arrays.
Quoted 5 lines
Regarding the plethora of arguments: remember that 99% of all controls will just use
v4l2_ctrl_new_std(). And that ain't too bad.new_customis indeed a big one, but currently only used by cx2341x where it is wrapped inside a much easier to digest function. I've tried to make the 90% of easy cases easy, and for the remaining 10% you will indeed have to do more work.
If we pass structures to the functions, drivers could only fill the fields they need and skip the other ones. For instance, a name field would be left untouched by drivers that call v4l2_ctrl_new_std(). The ones that call v4l2_ctrl_new_custom() would just need to fill the field in the v4l2_ctrl_whatever array. Clean and easy :-)
This (whether we should use a structure to create new controls) is something on which I'd like to hear other people's opinions (Sakari, I asked you to review this RFC, so please answer :-)).
Quoted 7 lines
Quoted 3 lines
I'll now have a look at the code. As there are already quite a few comments on the documentation I won't perform a very in-depth code review though, I'll save that for later after we agree on the spec :-)
Thanks for the review!
This was very useful.
You're welcome. I might bother you with other issues later if I catch new one :-)
Signature · 3 lines
Regards, Laurent Pinchart
Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
Quoted 26 lines · On Thursday 06 May 2010 23:36:48 Laurent Pinchart wrote:
Hi Hans,
Quoted 2 lines · On Monday 03 May 2010 00:21:46 Hans Verkuil wrote:
Quoted 1 line · On Sunday 02 May 2010 22:39:11 Laurent Pinchart wrote:
On Monday 26 April 2010 09:33:38 Hans Verkuil wrote:
[snip]
Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Objects in the framework +======================== + +There are two main objects:Quoted 8 lines
Quoted 4 lines
(quoted diff) +2
-0+ +The v4l2_ctrl object describes the control properties and keeps trackof the
+control's value (both the current value and the proposed new value).I'm not sure
v4l2_ctrlis a good name. We already have av4l2_controlstructure, and using the abbreviated name for the in-kernel version is going to be confusing.Quoted 4 lines
Originally it was called
v4l2_ctrl_infobut that became very cumbersome. It's not really an info object anyway, it really describes a control. When using this framework the driver no longer sees thev4l2_controlanymore, so from the point of view of the driver there is onlyv4l2_ctrl.
Quoted 5 lines
Sure, but it's still misleading. Using an both the abbreviated and non- abbreviated names to refer to two different things has been done too often in V4L2, and it's starting to bother me. It makes the API look inconsistent. We need to define a naming policy and stick to it for the future. That can be a topic for the V4L2 summit in June.
I can go back to v4l2_ctrl_info, unless you have a better name?
Quoted 11 lines
Quoted 10 lines
Quoted 7 lines
It's obviously too late to change
v4l2_controlto something else.v4l2_control_infoandv4l2_control_valueare two names that come to my mind. Actually, it might be a good idea to split thev4l2_ctrlstructure into a static driver-wide structure (v4l2_control_info) and an instance-specific structure (v4l2_control_value). There's no point in storing the same static data (function pointers, names, ...) for identical controls in different devices.Other than the name there isn't anything that is static. And the name is already static.
Quoted 8 lines
The following fields are static (they're identical across all
v4l2_ctrlinstances for the same control):- ops
- id
- name
- type
- menu (not too sure about this one)
It's honestly not worth it. The fields name, menu and ops already point to static const datastructs, so that leaves only the pointers themselves that you can save. And most of these fields are used very frequently as well, so having do an extra dereference will reduce the performance.
I tried something like this in the beginning and it simply isn't worth the pain it introduces.
Quoted 29 lines
[snip]
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + How to hook the handler into a bridge driver: + + foo->v4l2_dev.ctrl_handler = &foo->hdl;[snip]
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+ + And whenever you call video_register_device() you must set the + ctrl_handler field of struct video_device as well: + + vdev->ctrl_handler = &foo->hdl;[snip]
Quoted 10 lines
Quoted 2 lines
Why can't the framework find the
ctrl_handlerinstance from the video_device'sv4l2_deviceparent ?Sometimes you want to provide different control handlers to different video devices. E.g.
/dev/radio0may have only a small subset of the controls of/dev/video0(bttv does that, for example).One option is to automatically let
video_register_devicecopyctrl_handlerfrom thev4l2_devstruct, but I think it is clearer if it is explicitly set.
Quoted 7 lines
I was thinking about copying it implicitly if it's not set, yes. That way only the
v4l2_deviceinstance would need an explicit handler, video devices would inherit it from theirv4l2_deviceparent. The driver would still have the ability to set it if it wants to override the default behaviour.It might be clearer to always set it explicitly, I have no strong opinion on this one.
In that case I will leave it as is.
Quoted 23 lines
Quoted 23 lines
Quoted 22 lines
Quoted 11 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ Finally, remove all control functions from your v4l2_ioctl_ops: + vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl, + vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls. + Those are now no longer needed. + + How to hook the control handler into a subdev driver: + + foo->sd.ctrl_handler = &foo->hdl; + + And set all core control ops in your struct v4l2_subdev_core_ops tothese
Documentation/video4linux/v4l2-controls.txt line 98
Quoted 9 lines
Documentation/video4linux/v4l2-controls.txt new file +9
-0+ helpers: + + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls,s/sd/subdev/ ?
Quoted 6 lines
Quoted 1 line
I have to sleep on that... :-)
You've slept on it for a week now :-)
We really have too many abbreviations in V4L2, or at least no consistent usage of them.
I'll change it to subdev. There helpers are temporarily anyway.
Quoted 15 lines
[snip]
Quoted 13 lines
Quoted 7 lines
What about hardware for which the boundaries are only known at runtime, or could depend on the values of other controls ? I'm thinking about UVC devices for instance, the boundaries, step and default values need to be retrieved from the hardware. I currently do that at runtime when the control is queried for the first time and cache the values, as doing it during initialization (probe function) crashes a few webcams. That doesn't seem to be possible with the control framework.
It is possible to add controls to an existing control handler at runtime. It is also possible to change boundaries at runtime: you just change the relevant values in
v4l2_ctrl. There is no function for that, it's enough to callv4l2_ctrl_lock(), change the values and callunlock().
Quoted 5 lines
Quoted 2 lines
I could make a function that does this, but UVC is the only driver that I know of that might need this.
My issue is that the UVC driver would need to set the boundaries the first time the control is queried. There's no callback for that at the moment.
Ah! Now I understand your problem. What you need is an .init op that is called the first time the control is used. That shouldn't be hard to add.
Quoted 28 lines
Quoted 28 lines
Quoted 27 lines
Quoted 22 lines
Documentation/video4linux/v4l2-controls.txt new file +10
-0+ ... + if (foo->hdl.error) { + int err = foo->hdl.error; + + v4l2_ctrl_handler_free(&foo->hdl); + return err; + } + +The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new +control, but if you do not need to access the pointer outside thecontrol ops,
Documentation/video4linux/v4l2-controls.txt new file +3
-0+then there is no need to store it. + +Note that if something fails, the function will return NULL or an errorand
+sethdl->errorto the error code. Ifhdl->errorwas already set, then it
+will just return and do nothing. This is also true forv4l2_ctrl_handler_initDocumentation/video4linux/v4l2-controls.txt new file +3
-0+if it cannot allocate the internal data structure. + +This makes it easy to init the handler and just add all controls andQuoted 2 lines
only check
+the error code at the end. Saves a lot of repetitive error checking.I would still check the
v4l2_ctrl_handler_initreturn value explicitly, but that may be just me.
Quoted 4 lines
Quoted 1 line
Feel free to do that :-)
You know what I meant :-) I agree this is a good way to handle errors for v4l2_ctrl_new_*, but
v4l2_ctrl_handler_initfeels... different.
I know what you mean, but it is just so useful to do it like this. Remember that many drivers have just 1-4 controls, so having to introduce yet another error path to handle the failure of v4l2_ctrl_handler_init is getting really frustrating. I definitely like to keep this functionality, it saves unnecessary repetitive code in a lot of drivers.
Quoted 15 lines
[snip]
Quoted 12 lines
Quoted 11 lines
Quoted 8 lines
+3) Optionally force initial control setup:
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + v4l2_ctrl_handler_setup(&foo->hdl); + +This will call s_ctrl for all controls unconditionally. Effectivelythis
+initializes the hardware to the default control values. It is recommended +that you do this.What about the other way around, if I want to initialize the framework with the current values retrieved from the hardware ?
Quoted 8 lines
Quoted 2 lines
In that case you would set the default value of the control based on the value reported by the hardware.
The default value can still be different than the current value. Don't you mean I should initialize the current value, not the default one ?
What should be recommended, calling
v4l2_ctrl_handler_setup, or initializing the framework with the current value ?
OK, you want to initialize the current value to something that isn't the default value. I didn't catch that nuance.
There are two ways of doing this that I can think of up front:
The first is to use the proposed .init op. In the case of UVC that might be a good choice actually. The second approach is to create the control and call v4l2_ctrl_s to set it explicitly. Actually, I would favor using init for this. Note that I suspect that in uvc you will not use v4l2_ctrl_handler_setup at all.
Quoted 10 lines
Thinking some more about this, it can be a problem for UVC devices, much like querying the min/max/step/default values is. Some devices just crash if you send too many requests too fast. I would like to get a
g_ctrlcall the first time the current value needs to be read, and then have the framework cache that value.By the way (still more thinking :-)), does the framework handle a value cache that can be configured per control ? Some controls are volatile and need to be read from the driver every time, some others can be cached. How is that handled ?
It is already cached. Based on your misunderstanding I realized that I should rename the g_ctrl op to g_volatile_ctrl. Because g_ctrl is only meant for volatile controls. As you can see from my patches that convert existing drivers to the control framework, the majority doesn't implement g_ctrl.
In the case of uvc (and if the .init op is added), the .init can take care of the initial setup, querying the hardware for the current value, and if the control isn't volatile, then the control's current value will be cached.
Quoted 6 lines
Last thought: does the framework query the driver for the current control values when setting a control in a cluster ? If two clustered controls are stored in a single hardware "register", and one of them needs to be modified, a read-update-write operation needs to be performed. The update part will be done by the framework, the write part by the driver, but what about the read part ?
No, it does not read the current control value before calling s_ctrl. I don't think it is useful: if you need to do a read-update-write cycle, then you want to do that in one operation. Having the read done well before the write is not useful. If you need to, then you can call g_ctrl directly from within s_ctrl. I have yet to see a driver that needs this, though.
Quoted 35 lines
[snip]
Quoted 33 lines
Quoted 32 lines
Quoted 21 lines
Documentation/video4linux/v4l2-controls.txt new file +20
-0+Accessing Control Values +======================== + +The v4l2_ctrl struct contains these two unions: + + /* The current control value. */ + union { + s32 val; + s64 val64; + char *string; + } cur; + + /* The new control value. */ + union { + s32 val; + s64 val64; + char *string; + }; + +Within the control ops you can freely use these. The val and val64speak for
Documentation/video4linux/v4l2-controls.txt line 215
Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt new file +8
-0+themselves. The string pointers point to character buffers of length +ctrl->maximum + 1, and are always 0-terminated. + +For g_ctrl you have to update the current control values like this: + + ctrl->cur.val = read_reg(0x123); + +The 'new value' union is not relevant in g_ctrl.When is
g_ctrlcalled ? When the userspace applications issues aVIDIOC_G_CTRLorVIDIOC_G_EXT_CTRLSioctl ?
Quoted 3 lines
Quoted 1 line
Yes.
OK. Can you make that explicit ?
I clearly need to clarify this. It is called when someone (usually userspace through these ioctls) needs to get the control's value. But normally drivers do not implement and so the cached current value is returned. Only if this is a volatile control do the drivers implement this op.
One other thing that needs to be clarified: when you create a new control the initial current value will be set to the default value.
Calling v4l2_ctrl_handler_setup will set all controls with that initial current value. In other words it syncs the hardware with the internal control state.
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
I suppose the operation only needs to be implemented if the device needs to be queried because it modified the control value on its own (possibly as a result of setting another control). What if some controls need to be handled that way, while the others are not self-modifying ?
Quoted 7 lines
Quoted 2 lines
g_ctrlonly has to take care of self-modifying controls and can ignore the others. saa7115 has a use case like that.Instead of calling
g_ctrlfor all drivers, have it go through a big switch and do nothing, wouldn't it be better to add a volatile flag to thev4l2_ctrlstructure ?g_ctrlcalls would be skipped for non-volatile controls (except on the first read of course).
I thought about this, but you can also create two op structs: one for volatile controls containing a g_ctrl, and one for non-volatile controls which doesn't have that. That way you don't need to set a flag afterwards.
What op struct you provide will give you the flexibility that you need. I didn't think it was necessary to add yet another flag.
Quoted 30 lines
Quoted 30 lines
Quoted 29 lines
Quoted 19 lines
+For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
(quoted diff) +2
-0+you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union +contains the current value, which you can use (but not change!) aswell. +
+Ifs_ctrlreturns 0 (OK), then the control framework will copy the new finalDocumentation/video4linux/v4l2-controls.txt new file +3
-0+values to the 'cur' union. + +While in g/s/try_ctrl you can access the value of all controls ownedby the
+same handler since the handler's lock is held. Do not attempt to access +the value of controls owned by other handlers, though.(quoted diff) +2
-0+ +Elsewhere in the driver you have to be more careful. You cannot justrefer +to the current control values without locking.
(quoted diff) +2
-0+ +There are two simple helper functions defined that will get or set asingle
Documentation/video4linux/v4l2-controls.txt line 235
Quoted 4 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+control value safely: + + s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl); + int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);I suppose that
v4l2_ctrl_scan be used if the device notifies the driver that a control has changed. Is that correct ?I don't really like the names of those two functions. For instance,
v4l2_ctrl_slooks like it will call the driver to set a control.
Quoted 6 lines
Quoted 2 lines
I think there is a misunderstanding here:
v4l2_ctrl_s()will indeed change the control's value.Do you mean that it will call the driver's
s_ctrloperation or that it will modify the current value in thev4l2_ctrlstructure ? The misunderstanding probably calls for a function name change :-)
It will call the driver's s_ctrl operation. And a better name is indeed needed. Suggestions?
Quoted 48 lines
[snip]
Quoted 45 lines
Quoted 44 lines
Quoted 26 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Menu Controls +============= + +Menu controls use the 'step' value differently compared to othercontrol +types. The
v4l2_ctrlstruct contains this union:Documentation/video4linux/v4l2-controls.txt new file +7
-0+ + union { + u32 step; + u32 menu_skip_mask; + }; + +For menu controls menu_skip_mask is used. What it does is that itallows you
+to easily exclude certain menu items. This is used in theVIDIOC_QUERYMENU+implementation where you can return-EINVALif a certain menu item is not +present. Note thatVIDIOC_QUERYCTRLalways returns a step value of 1 for +menu controls.(quoted diff) +2
-0+ +A good example is the MPEG Audio Layer II Bitrate menu control wherethe +menu is a list of standardized possible bitrates. But in practice hardware +implementations will only support a subset of those. By setting the skip +mask you can tell the framework which menu items should be skipped. Setting
Documentation/video4linux/v4l2-controls.txt new file +3
-0+it to 0 means that all menu items are supported. + +So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need toQuoted 4 lines
remember
+that 'step' means 'skip mask' for menu controls. If you put in '1' by mistake,
+then the first menu item will be skipped.What about adding a
v4l2_ctrl_new_menuthen ? That will avoid such a mistake.Quoted 5 lines
+The
v4l2_ctrl_new_std_menucan be used to add menu controls more
easily: it
+will calculate the min and max values automatically based on the size of the
+menu, and it has a proper 'mask' argument instead of 'step'.OK :-) Shouldn't
v4l2_ctrl_new_custombe restricted to non-menu controls then ? Looking at the code, it should probably become an internal function, with another function added for custom controls without menu support.
Quoted 6 lines
Quoted 2 lines
I might add that in the future. I want to avoid creating a long series of different functions, all alike :-)
Then drivers will use the function that will later become internal. You'll have to change a bunch of drivers later, and you will tell me that it would be too much work and wouldn't be worth it ;-)
It's more that I don't want to add functions unless there is at least one driver that uses it.
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
Quoted 6 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Active and Grabbed Controls +=========================== + +If you get more complex relationships between controls, then you mayhave to
+activate and deactivate controls. For example, if the Chroma AGCQuoted 8 lines
control is +on, then the Chroma Gain control is inactive. That is, you may set it, but +the value will not be used by the hardware as long as the automatic gain +control is on. Typically user interfaces can disable such input fields. +
+You can set the 'active' status usingv4l2_ctrl_activate(). By default all +controls are active. Note that the framework does not check for this flag. +It is meant purely for GUIs. The function is typically called from within +s_ctrl.What you refer to as active/inactive is usually referred to as enabled/disabled in the GUI world. Might be worth using the same convention.
Quoted 8 lines
Quoted 5 lines
Strictly speaking it is not quite the same. An inactive control can still be written, the new value will simply not be used until the control becomes active again. In a GUI that would be confusing, so the control is disabled instead. It's a subtle difference that may or may not be important.
What does the framework do when the userspace application attempts to write to a disabled control ?
It returns -EINVAL as per spec.
Quoted 60 lines
[snip]
Quoted 57 lines
Quoted 12 lines
Quoted 12 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+Control Clusters +================ + +By default all controls are independent from the others. But in more +complex scenarios you can get dependencies from one control toanother. +In that case you need to 'cluster' them:
Documentation/video4linux/v4l2-controls.txt new file +6
-0+ + struct foo { + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + ...Quoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 322
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +5
-0+ }; + + state->volume = v4l2_ctrl_new_std(&state->hdl, ...); + state->mute = v4l2_ctrl_new_std(&state->hdl, ...); + v4l2_ctrl_cluster(2, &state->volume);What's the first argument to
v4l2_ctrl_cluster? The number of controls in the cluster ?Yes.
Quoted 5 lines
Does that imply that they need to be added in a sequence, right before
v4l2_ctrl_clusteris called ? That seems a bit awkward. Wouldn't it be better to specify the relationships between controls explicitly, maybe by passing a pointer to the master control when creating the 'slave' controls in the cluster ?OK, I clearly need to explain this better.
What you pass is an array of
v4l2_ctrlpointers, e.g.:struct foo { struct v4l2_ctrl *cluster[2]; ... }; foo->cluster[CTRL_VOLUME] = v4l2_ctrl_new_std(); foo->cluster[CTRL_MUTE] = v4l2_ctrl_new_std(); v4l2_ctrl_cluster(2, foo->cluster);cluster[0] is always the master.
Now, using arrays here becomes quickly very annoying, so instead you can just say:
struct foo { /* volume/mute cluster */ struct v4l2_ctrl *volume; struct v4l2_ctrl *mute; ... }; foo->volume = v4l2_ctrl_new_std(); foo->mute = v4l2_ctrl_new_std(); v4l2_ctrl_cluster(2, &foo->volume);
Quoted 6 lines
Quoted 1 line
Same thing, but without the cumbersome array indices.
That looks *very* dangerous. People will not get it (I haven't) and all kind of bad things will happen, like adding a field between two struct
v4l2_ctrlpointers. This part of the API needs to be reworked, if you require an array then an array must be passed to the function.
I realized the same: I need to write this as an array. It's annoying since it requires a lot of macros for the array indices.
An alternative might be to move the pointers into a struct:
struct foo {
/* volume/mute cluster */
struct cluster {
struct v4l2_ctrl *volume;
struct v4l2_ctrl *mute;
};
...};
Better, but I fear that it is not enough. I hate having to do
struct foo {
/* volume/mute cluster */
#define FOO_VOL 0
#define FOO_MUTE 1
struct v4l2_ctrl *vol_mute[2];
...};
And have to refer to controls using foo->vol_mute[FOO_MUTE]. It's a mouthful.
Hmm. Perhaps some static inline helpers like
static inline struct v4l2_ctrl *foo_volume(struct foo *foo)
{
return foo->vol_mute[FOO_VOL];
}Quoted 13 lines
[snip]
Quoted 10 lines
Quoted 9 lines
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+VIDIOC_LOG_STATUS Support +========================= + +This ioctl allow you to dump the current status of a driver to thekernel log.
This has nothing to do with the controls framework, but shouldn't that ioctl be restricted to root only ? It can potentially dump a lot of information to the kernel log, and thus system logs.
Quoted 4 lines
Quoted 1 line
Personally I see no reason to change this.
You don't think it opens a door for unprivileged applications to flood the kernel and system logs ?
There is a printk variant that prevents against flooding. The name escapes me at the moment. But it is probably a good idea to use that instead of the normal printk.
Quoted 22 lines
[snip]
Quoted 19 lines
Quoted 18 lines
Quoted 12 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Different Handlers for Different Video Nodes +============================================ + +Usually the bridge driver has just one control handler that is globalfor +all video nodes. But you can also specify different control handlers for +different video nodes. It's no problem if there are no subdevs involved. +But if there are, then you need to block the automatic merging of subdev +controls to the global control handler. You do that by simply setting the +ctrl_handler field in struct
v4l2_deviceto NULL.(quoted diff) +2
-0+ +After each subdev was added, you will then have to callQuoted 4 lines
v4l2_ctrl_add_handler
+manually to add the subdev's control handler (sd->ctrl_handler) to the desired
+bridge control handler.Do you mean video device instead of bridge here ?
Quoted 5 lines
Quoted 1 line
struct
v4l2_device.I don't get it then. If you want to use different handlers for different video nodes, you need to add the subdev handler to the video node (
video_device) handler, not the subdev handler to thev4l2_devicehandle.
I'm sorry, you are completely right. I didn't read this carefully enough. It is indeed video device instead of v4l2_device.
BTW: I'd love to do a search-and-replace for video_device to something like v4l2_devnode. It's very confusing at the moment.
Quoted 46 lines
Quoted 11 lines
Quoted 3 lines
I wouldn't mention "bridge" in here. I assume that by bridge you mean
v4l2_device. Please use that name directly. The term "bridge" is only applicable to a subset of thev4l2_deviceuse cases.Good point.
Quoted 2 lines
Can controls for a specific subdev be reported through more than one video device node, but not all of them ?
Yes. It will be a bit fiddly to set up, but that's to be expected if you want to do things like that.
I was just wondering if it was possible, I have no use case in mind. I wonder if we should even allow that...
[snip]
Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+Inheriting Controls +=================== + +When one control handler is added to another using
v4l2_ctrl_add_handler, then
+by default all controls from one are merged to the other. But a subdev might
+have low-level controls that make sense for some advanced embedded system, but
+not when it is used in consumer-level hardware. In that case you want to keep
+those low-level controls local to the subdev. You can do this byQuoted 8 lines
Documentation/video4linux/v4l2-controls.txt line 463
Quoted 5 lines
simply +setting the '
is_private' flag of the control to 1:Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...); + if (ctrl) + ctrl->is_private = 1;Wouldn't it make more sense to pass that as an argument to
v4l2_ctrl_new_custom?Quoted 7 lines
As I said earlier, I tried to avoid creating zillions of very similar functions, or functions with very long arg lists. For now at least private controls are not used at all.
It is usually easier for the driver to write a few special static inlines that do exactly what the driver needs. Of course, if many drivers need to do the same thing, then such inlines should be moved to the core header.
Quoted 5 lines
"Maybe this feature won't be used" sounds more like a reason to remove it than to implement it badly and leave it to be fixed later :-)
The control framework is a big change to the V4L2 framework, we should take great care to get it as good as possible on the first try.
It's an internal API, not a public one. And I think it is important not to implement something unless it is actually used.
Quoted 93 lines
Quoted 8 lines
Quoted 4 lines
I'm actually wondering if it wouldn't be better to pass a pointer to a structure with the required information (maybe the
v4l2_control_infostructure I mentioned earlier ?) tov4l2_ctrl_new_custom.I thought about that, but 1) that means yet another structure and 2) I dislike code like that because the definition is usually quite far from the place were it is actually used.
Yes, it means yet another structure. On the other hand all those structures can be nicely grouped in an array, and controls can then be added using a loop instead of a bunch of function calls. I think it would be much cleaner, code would be more readable and error handling would be easier.
Quoted 24 lines
Quoted 15 lines
Quoted 15 lines
+These controls will now be skipped when
v4l2_ctrl_add_handleris called. +Documentation/video4linux/v4l2-controls.txt new file +5
-0+ +Strict Control Validation +========================= + +By default when the application wants to change an integer control thevalue
+passed to the framework will automatically be modified to map to the provided
+minimum, maximum and step values of the control. If instead you just want to
+validate the value and not modify it, then set the 'strict_validation' flag of
+the control:Quoted 8 lines
Quoted 5 lines
Documentation/video4linux/v4l2-controls.txt new file +4
-0+ + ctrl->strict_validation = 1; + +Now -ERANGE will be returned if the new value does not match thecontrol's +requirements.
Why do we need the two behaviours ? Wouldn't it be better to standardize on one of them ?
Quoted 4 lines
I may have fallen in the pitfall of featurism here. Just because it is easy to implement, that doesn't mean that you should actually do it.
I think I'll remove
strict_validation.Thanks.
[snip]
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
Documentation/video4linux/v4l2-controls.txt new file +6
-0+Proposals for Extensions +======================== + +Some ideas for future extensions to the spec: + +1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimalinstead of +decimal. Useful for e.g.
video_mute_yuv.
+
+2) It is possible to mark in the controls array which controls have been +successfully written and which failed by for example adding aQuoted 8 lines
Quoted 1 line
bit to the +control ID. Not sure if it is worth the effort, though.
I still feel a bit awkward about the interface. One particular point that might require attention is the split of the
v4l2_ctrlstructure intov4l2_control_infoandv4l2_control_valuestructures (the names are not set into stone). I would also like to see if we can't pass pointers tov4l2_control_info(or similar) to the control creation functions instead of a plethora of arguments.Quoted 3 lines
One of my original attempts was indeed to have two structs. But that made the code only more complex. Having two structs only makes sense if the 'static' part can be, well, static.
Indeed :-) Like I pointed out above there's some static information, but maybe not enough of them to split them into another structure. On the other hand, like I also pointed above, I think passing a structure, even if it's not big, to the control creation functions would make driver code cleaner.
Quoted 2 lines
But then you have UVC where that isn't static. So the code then has to differentiate between these two cases and it quickly becomes a big mess.
There's static information in UVC. Look at
uvc_ctrl.c, it starts with two big arrays.Quoted 5 lines
Regarding the plethora of arguments: remember that 99% of all controls will just use
v4l2_ctrl_new_std(). And that ain't too bad.new_customis indeed a big one, but currently only used by cx2341x where it is wrapped inside a much easier to digest function. I've tried to make the 90% of easy cases easy, and for the remaining 10% you will indeed have to do more work.If we pass structures to the functions, drivers could only fill the fields they need and skip the other ones. For instance, a name field would be left untouched by drivers that call
v4l2_ctrl_new_std(). The ones that callv4l2_ctrl_new_custom()would just need to fill the field in thev4l2_ctrl_whateverarray. Clean and easy :-)
Quoted 3 lines
This (whether we should use a structure to create new controls) is something on which I'd like to hear other people's opinions (Sakari, I asked you to review this RFC, so please answer :-)).
I have thought about this a bit more. I think it makes sense to have v4l2_ctrl_new_custom use a struct as input. But for v4l2_ctrl_new_std it makes no sense IMHO. This function is already very simple and straightforward.
I will make a more detailed proposal for this later.
Regards,
Hans
Quoted 13 lines
Quoted 8 lines
Quoted 4 lines
I'll now have a look at the code. As there are already quite a few comments on the documentation I won't perform a very in-depth code review though, I'll save that for later after we agree on the spec :-)
Thanks for the review!
This was very useful.
You're welcome. I might bother you with other issues later if I catch new one :-)
Signature · 1 line
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
In reply to Hans VerkuilHi Hans,
Hans Verkuil wrote:
[clip]
Quoted 12 lines
Quoted 1 line
Should't
v4l2_ctrl_new_stdtake a control type as well ?The type is set automatically as that is determined by the control ID.
Quoted 7 lines
What about hardware for which the boundaries are only known at runtime, or could depend on the values of other controls ? I'm thinking about UVC devices for instance, the boundaries, step and default values need to be retrieved from the hardware. I currently do that at runtime when the control is queried for the first time and cache the values, as doing it during initialization (probe function) crashes a few webcams. That doesn't seem to be possible with the control framework.
Quoted 7 lines
It is possible to add controls to an existing control handler at runtime. It is also possible to change boundaries at runtime: you just change the relevant values in
v4l2_ctrl. There is no function for that, it's enough to callv4l2_ctrl_lock(), change the values and callunlock().I could make a function that does this, but UVC is the only driver that I know of that might need this.
It won't be for long. Think of the sensor drivers, for example. The maximum exposure time (often expressed in lines) is dependent on the active sensor area (lines) plus vertical blanking. I'd assume the active area to be selected in other ways than using controls, however.
The vertical blanking, though, also affects the frame rate, and thus available exposure time. Not quite sure whether the user should be left to touch the vertical blanking directly.
Regards,
Signature · 2 lines
Sakari Ailus [email protected]
RFC PATCH 4/15 v4l: hook up the new control framework into the core framework
In reply to Hans VerkuilAdd the calls needed to automatically merge subdev controls into a bridge control handler.
Hook up the control framework in __video_ioctl2.
--- drivers/media/video/v4l2-device.c | 7 +++++ drivers/media/video/v4l2-ioctl.c | 46 ++++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 13 deletions(-)
drivers/media/video/v4l2-device.c +7 -0
@@ -26,6 +26,7 @@#endif #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> int v4l2_device_register (struct device *dev,struct v4l2_device *v4l2_dev) {
@@ -115,6 +116,8 @@ EXPORT_SYMBOL_GPL(v4l2_device_unregister);int v4l2_device_register_subdev (struct v4l2_device *v4l2_dev,struct v4l2_subdev *sd) { +int err; +/* Check for valid input */ if (v4l2_dev ==NULL || sd ==NULL || !sd->name[0 ])return -EINVAL ;
@@ -122,6 +125,10 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,WARN_ON (sd->v4l2_dev !=NULL );if (!try_module_get (sd->owner))return -ENODEV ; +/* This just returns 0 if either of the two args is NULL */ + err =v4l2_ctrl_add_handler (v4l2_dev->ctrl_handler, sd->ctrl_handler); +if (err) +return err; sd->v4l2_dev = v4l2_dev;spin_lock (&v4l2_dev->lock);list_add_tail (&sd->list, &v4l2_dev->subdevs);
drivers/media/video/v4l2-ioctl.c +33 -13
105 lines not on this page · raw message
Signature · 1 line
1.6.4.2
RFC PATCH 5/15 saa7115: convert to the new control framework
In reply to Hans Verkuil--- drivers/media/video/saa7115.c | 180 ++++++++++++++++++----------------------- 1 files changed, 80 insertions(+), 100 deletions(-)
drivers/media/video/saa7115.c +80 -100
@@ -45,6 +45,7 @@#include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include <media/saa7115.h>
@@ -65,16 +66,17 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");struct saa711x_state {struct v4l2_subdev sd; +struct v4l2_ctrl_handler hdl; + +/* chroma gain control cluster */ +struct v4l2_ctrl *agc; +struct v4l2_ctrl *gain; +v4l2_std_id std;int input;int output;int enable;int radio; -int bright; -int contrast; -int hue; -int sat; -int chroma_agc;int width;int height;u32 ident;
@@ -90,6 +92,11 @@ static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct saa711x_state, sd); } +static inline struct v4l2_subdev *to_sd (struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd; +} +/* ----------------------------------------------------------------------- */ static inline int saa711x_write (struct v4l2_subdev *sd,u8 reg,u8 value)
@@ -741,96 +748,53 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)return 0 ; } -static int saa711x_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int saa711x_g_ctrl (struct v4l2_ctrl *ctrl) { +struct v4l2_subdev *sd =to_sd (ctrl);struct saa711x_state *state =to_state (sd); -u8 val;switch (ctrl->id) { -case V4L2_CID_BRIGHTNESS : -if (ctrl->value <0 || ctrl->value >255 ) { -v4l2_err (sd,"invalid brightness setting %d\n" , ctrl->value); -return -ERANGE ; - } - - state->bright = ctrl->value; -saa711x_write (sd,R_0A_LUMA_BRIGHT_CNTL , state->bright); -break ; - -case V4L2_CID_CONTRAST : -if (ctrl->value <0 || ctrl->value >127 ) { -v4l2_err (sd,"invalid contrast setting %d\n" , ctrl->value); -return -ERANGE ; - } - - state->contrast = ctrl->value; -saa711x_write (sd,R_0B_LUMA_CONTRAST_CNTL , state->contrast); -break ; - -case V4L2_CID_SATURATION : -if (ctrl->value <0 || ctrl->value >127 ) { -v4l2_err (sd,"invalid saturation setting %d\n" , ctrl->value); -return -ERANGE ; - } - - state->sat = ctrl->value; -saa711x_write (sd,R_0C_CHROMA_SAT_CNTL , state->sat); -break ; - -case V4L2_CID_HUE : -if (ctrl->value <-128 || ctrl->value >127 ) { -v4l2_err (sd,"invalid hue setting %d\n" , ctrl->value); -return -ERANGE ; - } - - state->hue = ctrl->value; -saa711x_write (sd,R_0D_CHROMA_HUE_CNTL , state->hue); -break ;case V4L2_CID_CHROMA_AGC : - val =saa711x_read (sd,R_0F_CHROMA_GAIN_CNTL ); - state->chroma_agc = ctrl->value; -if (ctrl->value) - val &=0x7f ; -else - val |=0x80 ; -saa711x_write (sd,R_0F_CHROMA_GAIN_CNTL , val); +/* chroma gain cluster */ +if (state->agc->cur.val) + state->gain->cur.val = +saa711x_read (sd,R_0F_CHROMA_GAIN_CNTL ) &0x7f ;break ; -case V4L2_CID_CHROMA_GAIN : -/* Chroma gain cannot be set when AGC is enabled */ -if (state->chroma_agc ==1 ) -return -EINVAL ; -saa711x_write (sd,R_0F_CHROMA_GAIN_CNTL , ctrl->value |0x80 ); -break ; -default : -return -EINVAL ; } -return 0 ; } -static int saa711x_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int saa711x_s_ctrl (struct v4l2_ctrl *ctrl) { +struct v4l2_subdev *sd =to_sd (ctrl);struct saa711x_state *state =to_state (sd);switch (ctrl->id) {case V4L2_CID_BRIGHTNESS : - ctrl->value = state->bright; +saa711x_write (sd,R_0A_LUMA_BRIGHT_CNTL , ctrl->val);break ; +case V4L2_CID_CONTRAST : - ctrl->value = state->contrast; +saa711x_write (sd,R_0B_LUMA_CONTRAST_CNTL , ctrl->val);break ; +case V4L2_CID_SATURATION : - ctrl->value = state->sat; +saa711x_write (sd,R_0C_CHROMA_SAT_CNTL , ctrl->val);break ; +case V4L2_CID_HUE : - ctrl->value = state->hue; +saa711x_write (sd,R_0D_CHROMA_HUE_CNTL , ctrl->val);break ; +case V4L2_CID_CHROMA_AGC : - ctrl->value = state->chroma_agc; -break ; -case V4L2_CID_CHROMA_GAIN : - ctrl->value =saa711x_read (sd,R_0F_CHROMA_GAIN_CNTL ) &0x7f ; +/* chroma gain cluster */ +if (state->agc->val) +saa711x_write (sd,R_0F_CHROMA_GAIN_CNTL , state->gain->val); +else +saa711x_write (sd,R_0F_CHROMA_GAIN_CNTL , state->gain->val |0x80 ); +v4l2_ctrl_activate (state->gain, !state->agc->val);break ; +default :return -EINVAL ; }
@@ -1221,25 +1185,6 @@ static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)return 0 ; } -static int saa711x_queryctrl (struct v4l2_subdev *sd,struct v4l2_queryctrl *qc) -{ -switch (qc->id) { -case V4L2_CID_BRIGHTNESS : -return v4l2_ctrl_query_fill (qc,0 ,255 ,1 ,128 ); -case V4L2_CID_CONTRAST : -case V4L2_CID_SATURATION : -return v4l2_ctrl_query_fill (qc,0 ,127 ,1 ,64 ); -case V4L2_CID_HUE : -return v4l2_ctrl_query_fill (qc,-128 ,127 ,1 ,0 ); -case V4L2_CID_CHROMA_AGC : -return v4l2_ctrl_query_fill (qc,0 ,1 ,1 ,1 ); -case V4L2_CID_CHROMA_GAIN : -return v4l2_ctrl_query_fill (qc,0 ,127 ,1 ,48 ); -default : -return -EINVAL ; - } -} -static int saa711x_s_std (struct v4l2_subdev *sd,v4l2_std_id std) {struct saa711x_state *state =to_state (sd);
@@ -1516,17 +1461,27 @@ static int saa711x_log_status(struct v4l2_subdev *sd)break ; }v4l2_info (sd,"Width, Height: %d, %d\n" , state->width, state->height); +v4l2_ctrl_handler_log_status (&state->hdl, sd->name);return 0 ; }/* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops saa711x_ctrl_ops = { + .s_ctrl = saa711x_s_ctrl, + .g_ctrl = saa711x_g_ctrl, +}; +static const struct v4l2_subdev_core_ops saa711x_core_ops = { .log_status = saa711x_log_status, .g_chip_ident = saa711x_g_chip_ident, - .g_ctrl = saa711x_g_ctrl, - .s_ctrl = saa711x_s_ctrl, - .queryctrl = saa711x_queryctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, .s_std = saa711x_s_std, .reset = saa711x_reset, .s_gpio = saa711x_s_gpio,
@@ -1571,8 +1526,9 @@ static int saa711x_probe(struct i2c_client *client, {struct saa711x_state *state;struct v4l2_subdev *sd; -int i; -char name[17 ]; +struct v4l2_ctrl_handler *hdl; +int i; +char name[17 ];char chip_id;int autodetect = !id || id->driver_data ==1 ;
@@ -1611,15 +1567,37 @@ static int saa711x_probe(struct i2c_client *client,return -ENOMEM ; sd = &state->sd;v4l2_i2c_subdev_init (sd, client, &saa711x_ops); + + hdl = &state->hdl; +v4l2_ctrl_handler_init (hdl,6 ); +/* add in ascending ID order */ +v4l2_ctrl_new_std (hdl, &saa711x_ctrl_ops, +V4L2_CID_BRIGHTNESS ,0 ,255 ,1 ,128 ); +v4l2_ctrl_new_std (hdl, &saa711x_ctrl_ops, +V4L2_CID_CONTRAST ,0 ,127 ,1 ,64 ); +v4l2_ctrl_new_std (hdl, &saa711x_ctrl_ops, +V4L2_CID_SATURATION ,0 ,127 ,1 ,64 ); +v4l2_ctrl_new_std (hdl, &saa711x_ctrl_ops, +V4L2_CID_HUE ,-128 ,127 ,1 ,0 ); + state->agc =v4l2_ctrl_new_std (hdl, &saa711x_ctrl_ops, +V4L2_CID_CHROMA_AGC ,0 ,1 ,1 ,1 ); + state->gain =v4l2_ctrl_new_std (hdl, &saa711x_ctrl_ops, +V4L2_CID_CHROMA_GAIN ,0 ,127 ,1 ,40 ); + sd->ctrl_handler = hdl; +if (hdl->error) { +int err = hdl->error; + +v4l2_ctrl_handler_free (hdl); +kfree (state); +return err; + } + state->agc->flags |=V4L2_CTRL_FLAG_UPDATE ; +v4l2_ctrl_cluster (2 , &state->agc); + state->input =-1 ; state->output =SAA7115_IPORT_ON ; state->enable =1 ; state->radio =0 ; - state->bright =128 ; - state->contrast =64 ; - state->hue =0 ; - state->sat =64 ; - state->chroma_agc =1 ;switch (chip_id) {case '1' : state->ident =V4L2_IDENT_SAA7111 ;
@@ -1667,6 +1645,7 @@ static int saa711x_probe(struct i2c_client *client,if (state->ident >V4L2_IDENT_SAA7111A )saa711x_writeregs (sd, saa7115_init_misc);saa711x_set_v4lstd (sd,V4L2_STD_NTSC ); +v4l2_ctrl_handler_setup (hdl); v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
@@ -1681,6 +1660,7 @@ static int saa711x_remove(struct i2c_client *client)struct v4l2_subdev *sd =i2c_get_clientdata (client);v4l2_device_unregister_subdev (sd); +v4l2_ctrl_handler_free (sd->ctrl_handler);kfree (to_state (sd));return 0 ; }
Signature · 1 line
1.6.4.2
Re: [PATCH 05/15] [RFC] saa7115: convert to the new control framework
Quoted 164 lines · Hans Verkuil
Signed-off-by Hans Verkuil <[email protected]>---
drivers/media/video/saa7115.c | 180 ++++++++++++++++++-----------------------1 files changed, 80 insertions(+), 100 deletions(-)drivers/media/video/saa7115.c +14
-46@@ -45,6 +45,7 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include <media/saa7115.h>@@ -65,16 +66,17 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); struct saa711x_state { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + + /* chroma gain control cluster */ + struct v4l2_ctrl *agc; + struct v4l2_ctrl *gain; + v4l2_std_id std; int input; int output; int enable; int radio; - int bright; - int contrast; - int hue; - int sat; - int chroma_agc; int width; int height; u32 ident;@@ -90,6 +92,11 @@ static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct saa711x_state, sd); } +static inline struct v4l2_subdev *to_sd (struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd; +} + /* ----------------------------------------------------------------------- */ static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)@@ -741,96 +748,53 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) return 0; } -static int saa711x_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int saa711x_g_ctrl (struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct saa711x_state *state = to_state(sd); - u8 val; switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value < 0 || ctrl->value > 255) { - v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); - return -ERANGE; - } - - state->bright = ctrl->value; - saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright); - break; - - case V4L2_CID_CONTRAST: - if (ctrl->value < 0 || ctrl->value > 127) { - v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); - return -ERANGE; - } - - state->contrast = ctrl->value; - saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast); - break; - - case V4L2_CID_SATURATION: - if (ctrl->value < 0 || ctrl->value > 127) { - v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); - return -ERANGE; - } - - state->sat = ctrl->value; - saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat); - break; - - case V4L2_CID_HUE: - if (ctrl->value < -128 || ctrl->value > 127) { - v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); - return -ERANGE; - } - - state->hue = ctrl->value; - saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue); - break;case
V4L2_CID_CHROMA_AGC:
- val =saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL);
-state->chroma_agc=ctrl->value;
- if (ctrl->value)
- val &=0x7f;
- else
- val |=0x80;
-saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val);
+ /* chroma gain cluster */
+ if (state->agc->cur.val)
+state->gain->cur.val=
+saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL)&0x7f; break;
- caseV4L2_CID_CHROMA_GAIN:
- /* Chroma gain cannot be set when AGC is enabled */
- if (state->chroma_agc== 1)
- return-EINVAL;
-saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80);
- break;
- default:
- return-EINVAL;
}
-
return 0;
}drivers/media/video/saa7115.c +1
-1-static int saa711x_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int saa711x_s_ctrl (struct v4l2_ctrl *ctrl){
+ structv4l2_subdev*sd =to_sd(ctrl); structsaa711x_state*state =to_state(sd);switch (
ctrl->id) {
caseV4L2_CID_BRIGHTNESS:
-ctrl->value=state->bright;
+saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val); break;
+
caseV4L2_CID_CONTRAST:
-ctrl->value=state->contrast;
+saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val); break;
+
caseV4L2_CID_SATURATION:
-ctrl->value=state->sat;
+saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val); break;
+
caseV4L2_CID_HUE:
-ctrl->value=state->hue;
+saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val); break;
+
caseV4L2_CID_CHROMA_AGC:
-ctrl->value=state->chroma_agc;
- break;
- caseV4L2_CID_CHROMA_GAIN:
-ctrl->value=saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL)&0x7f;
+ /* chroma gain cluster */
+ if (state->agc->val)
+saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
Quoted 8 lines
+ else
+saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
+v4l2_ctrl_activate(state->gain, !state->agc->val);
break;
+
default:
return-EINVAL;
}
This might not have been done before, but errors from sa71xx_write should be propagated to caller. A successful write should not be assumed. This applies for all uses of sa711x_write above. I'd probably do the following:
1. Declare a local variable called rc initialized to -EINVAL
2. Store the result of sa711x_write into rc for each case.
3. Remove the default case.
4. Return the value of rc.
Regards,
David Ellingsworth
Quoted 136 lines
drivers/media/video/saa7115.c +43
-29@@ -1221,25 +1185,6 @@ static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int saa711x_queryctrl (struct v4l2_subdev *sd,struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - case V4L2_CID_CHROMA_AGC: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_CHROMA_GAIN: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48); - default: - return -EINVAL; - } -} - static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct saa711x_state *state = to_state(sd);@@ -1516,17 +1461,27 @@ static int saa711x_log_status(struct v4l2_subdev *sd) break; } v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); return 0; } /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops saa711x_ctrl_ops = { + .s_ctrl = saa711x_s_ctrl, + .g_ctrl = saa711x_g_ctrl, +}; + static const struct v4l2_subdev_core_ops saa711x_core_ops = { .log_status = saa711x_log_status, .g_chip_ident = saa711x_g_chip_ident, - .g_ctrl = saa711x_g_ctrl, - .s_ctrl = saa711x_s_ctrl, - .queryctrl = saa711x_queryctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, .s_std = saa711x_s_std, .reset = saa711x_reset, .s_gpio = saa711x_s_gpio,@@ -1571,8 +1526,9 @@ static int saa711x_probe(struct i2c_client *client, { struct saa711x_state *state; struct v4l2_subdev *sd; - int i; - char name[17]; + struct v4l2_ctrl_handler *hdl; + int i; + char name[17]; char chip_id; int autodetect = !id || id->driver_data == 1;@@ -1611,15 +1567,37 @@ static int saa711x_probe(struct i2c_client *client, return -ENOMEM; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &saa711x_ops); + + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 6); + /* add in ascending ID order */ + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); + state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(state); + return err; + } + state->agc->flags |= V4L2_CTRL_FLAG_UPDATE; + v4l2_ctrl_cluster(2, &state->agc); + state->input = -1; state->output = SAA7115_IPORT_ON; state->enable = 1; state->radio = 0; - state->bright = 128; - state->contrast = 64; - state->hue = 0; - state->sat = 64; - state->chroma_agc = 1; switch (chip_id) {case '1':
state->ident =V4L2_IDENT_SAA7111;drivers/media/video/saa7115.c +2
-1@@ -1667,6 +1645,7 @@ static int saa711x_probe(struct i2c_client *client, if (state->ident > V4L2_IDENT_SAA7111A) saa711x_writeregs(sd, saa7115_init_misc); saa711x_set_v4lstd(sd, V4L2_STD_NTSC); + v4l2_ctrl_handler_setup(hdl); v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),@@ -1681,6 +1660,7 @@ static int saa711x_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); kfree(to_state(sd)); return 0; } --1.6.4.2
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC PATCH 6/15 msp3400: convert to the new control framework
In reply to Hans Verkuil--- drivers/media/video/msp3400-driver.c | 248 +++++++++++-------------------- drivers/media/video/msp3400-driver.h | 16 ++- drivers/media/video/msp3400-kthreads.c | 16 +- 3 files changed, 108 insertions(+), 172 deletions(-)
drivers/media/video/msp3400-driver.c +88 -160
@@ -283,51 +283,6 @@ void msp_set_scart(struct i2c_client *client, int in, int out)msp_write_dem (client,0x40 , state->i2s_mode); } -void msp_set_audio (struct i2c_client *client) -{ -struct msp_state *state =to_state (i2c_get_clientdata (client)); -int bal =0 , bass, treble, loudness; -int val =0 ; -int reallymuted = state->muted | state->scan_in_progress; - -if (!reallymuted) - val = (state->volume *0x7f /65535 ) <<8 ; - -v4l_dbg (1 , msp_debug, client,"mute=%s scanning=%s volume=%d\n" , - state->muted ?"on" :"off" , - state->scan_in_progress ?"yes" :"no" , - state->volume); - -msp_write_dsp (client,0x0000 , val); -msp_write_dsp (client,0x0007 , reallymuted ?0x1 : (val |0x1 )); -if (state->has_scart2_out_volume) -msp_write_dsp (client,0x0040 , reallymuted ?0x1 : (val |0x1 )); -if (state->has_headphones) -msp_write_dsp (client,0x0006 , val); -if (!state->has_sound_processing) -return ; - -if (val) - bal = (u8)((state->balance /256 ) -128 ); - bass = ((state->bass -32768 ) *0x60 /65535 ) <<8 ; - treble = ((state->treble -32768 ) *0x60 /65535 ) <<8 ; - loudness = state->loudness ? ((5 *4 ) <<8 ) :0 ; - -v4l_dbg (1 , msp_debug, client,"balance=%d bass=%d treble=%d loudness=%d\n" , - state->balance, state->bass, state->treble, state->loudness); - -msp_write_dsp (client,0x0001 , bal <<8 ); -msp_write_dsp (client,0x0002 , bass); -msp_write_dsp (client,0x0003 , treble); -msp_write_dsp (client,0x0004 , loudness); -if (!state->has_headphones) -return ; -msp_write_dsp (client,0x0030 , bal <<8 ); -msp_write_dsp (client,0x0031 , bass); -msp_write_dsp (client,0x0032 , treble); -msp_write_dsp (client,0x0033 , loudness); -} -/* ------------------------------------------------------------------------ */ static void msp_wake_thread (struct i2c_client *client)
@@ -363,98 +318,73 @@ int msp_sleep(struct msp_state *state, int timeout)/* ------------------------------------------------------------------------ */ -static int msp_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int msp_s_ctrl (struct v4l2_ctrl *ctrl) { -struct msp_state *state =to_state (sd); +struct msp_state *state =ctrl_to_state (ctrl); +struct i2c_client *client =v4l2_get_subdevdata (&state->sd); +int val = ctrl->val;switch (ctrl->id) { -case V4L2_CID_AUDIO_VOLUME : - ctrl->value = state->volume; -break ; - -case V4L2_CID_AUDIO_MUTE : - ctrl->value = state->muted; -break ; - -case V4L2_CID_AUDIO_BALANCE : -if (!state->has_sound_processing) -return -EINVAL ; - ctrl->value = state->balance; -break ; - -case V4L2_CID_AUDIO_BASS : -if (!state->has_sound_processing) -return -EINVAL ; - ctrl->value = state->bass; +case V4L2_CID_AUDIO_VOLUME : { +/* audio volume cluster */ +int reallymuted = state->muted->val | state->scan_in_progress; + +if (!reallymuted) + val = (val *0x7f /65535 ) <<8 ; + +v4l_dbg (1 , msp_debug, client,"mute=%s scanning=%s volume=%d\n" , + state->muted->val ?"on" :"off" , + state->scan_in_progress ?"yes" :"no" , + state->volume->val); + +msp_write_dsp (client,0x0000 , val); +msp_write_dsp (client,0x0007 , reallymuted ?0x1 : (val |0x1 )); +if (state->has_scart2_out_volume) +msp_write_dsp (client,0x0040 , reallymuted ?0x1 : (val |0x1 )); +if (state->has_headphones) +msp_write_dsp (client,0x0006 , val);break ; - -case V4L2_CID_AUDIO_TREBLE : -if (!state->has_sound_processing) -return -EINVAL ; - ctrl->value = state->treble; -break ; - -case V4L2_CID_AUDIO_LOUDNESS : -if (!state->has_sound_processing) -return -EINVAL ; - ctrl->value = state->loudness; -break ; - -default : -return -EINVAL ; } -return 0 ; -} - -static int msp_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) -{ -struct msp_state *state =to_state (sd); -struct i2c_client *client =v4l2_get_subdevdata (sd); - -switch (ctrl->id) { -case V4L2_CID_AUDIO_VOLUME : - state->volume = ctrl->value; -if (state->volume ==0 ) - state->balance =32768 ; -break ; - -case V4L2_CID_AUDIO_MUTE : -if (ctrl->value <0 || ctrl->value >=2 ) -return -ERANGE ; - state->muted = ctrl->value; -break ;case V4L2_CID_AUDIO_BASS : -if (!state->has_sound_processing) -return -EINVAL ; - state->bass = ctrl->value; + val = ((val -32768 ) *0x60 /65535 ) <<8 ; +msp_write_dsp (client,0x0002 , val); +if (state->has_headphones) +msp_write_dsp (client,0x0031 , val);break ;case V4L2_CID_AUDIO_TREBLE : -if (!state->has_sound_processing) -return -EINVAL ; - state->treble = ctrl->value; + val = ((val -32768 ) *0x60 /65535 ) <<8 ; +msp_write_dsp (client,0x0003 , val); +if (state->has_headphones) +msp_write_dsp (client,0x0032 , val);break ;case V4L2_CID_AUDIO_LOUDNESS : -if (!state->has_sound_processing) -return -EINVAL ; - state->loudness = ctrl->value; + val = val ? ((5 *4 ) <<8 ) :0 ; +msp_write_dsp (client,0x0004 , val); +if (state->has_headphones) +msp_write_dsp (client,0x0033 , val);break ;case V4L2_CID_AUDIO_BALANCE : -if (!state->has_sound_processing) -return -EINVAL ; - state->balance = ctrl->value; + val = (u8)((val /256 ) -128 ); +msp_write_dsp (client,0x0001 , val <<8 ); +if (state->has_headphones) +msp_write_dsp (client,0x0030 , val <<8 );break ;default :return -EINVAL ; } -msp_set_audio (client);return 0 ; } +void msp_update_volume (struct msp_state *state) +{ +v4l2_ctrl_s (state->volume,v4l2_ctrl_g (state->volume)); +} +/* --- v4l2 ioctls --- */ static int msp_s_radio (struct v4l2_subdev *sd) {
@@ -472,7 +402,7 @@ static int msp_s_radio(struct v4l2_subdev *sd)msp3400c_set_mode (client,MSP_MODE_FM_RADIO );msp3400c_set_carrier (client,MSP_CARRIER (10.7 ),MSP_CARRIER (10.7 )); -msp_set_audio (client); +msp_update_volume (state);break ;case OPMODE_AUTODETECT :case OPMODE_AUTOSELECT :
@@ -592,33 +522,6 @@ static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)return 0 ; } -static int msp_queryctrl (struct v4l2_subdev *sd,struct v4l2_queryctrl *qc) -{ -struct msp_state *state =to_state (sd); - -switch (qc->id) { -case V4L2_CID_AUDIO_VOLUME : -return v4l2_ctrl_query_fill (qc,0 ,65535 ,65535 /100 ,58880 ); -case V4L2_CID_AUDIO_MUTE : -return v4l2_ctrl_query_fill (qc,0 ,1 ,1 ,0 ); -default : -break ; - } -if (!state->has_sound_processing) -return -EINVAL ; -switch (qc->id) { -case V4L2_CID_AUDIO_LOUDNESS : -return v4l2_ctrl_query_fill (qc,0 ,1 ,1 ,0 ); -case V4L2_CID_AUDIO_BALANCE : -case V4L2_CID_AUDIO_BASS : -case V4L2_CID_AUDIO_TREBLE : -return v4l2_ctrl_query_fill (qc,0 ,65535 ,65535 /100 ,32768 ); -default : -return -EINVAL ; - } -return 0 ; -} -static int msp_g_chip_ident (struct v4l2_subdev *sd,struct v4l2_dbg_chip_ident *chip) {struct msp_state *state =to_state (sd);
@@ -633,19 +536,14 @@ static int msp_log_status(struct v4l2_subdev *sd)struct msp_state *state =to_state (sd);struct i2c_client *client =v4l2_get_subdevdata (sd);const char *p; +char prefix[V4L2_SUBDEV_NAME_SIZE +20 ];if (state->opmode ==OPMODE_AUTOSELECT )msp_detect_stereo (client);v4l_info (client,"%s rev1 = 0x%04x rev2 = 0x%04x\n" , client->name, state->rev1, state->rev2); -v4l_info (client,"Audio: volume %d%s\n" , - state->volume, state->muted ?" (muted)" :"" ); -if (state->has_sound_processing) { -v4l_info (client,"Audio: balance %d bass %d treble %d loudness %s\n" , - state->balance, state->bass, - state->treble, - state->loudness ?"on" :"off" ); - } +snprintf (prefix,sizeof (prefix),"%s: Audio: " , sd->name); +v4l2_ctrl_handler_log_status (&state->hdl, prefix);switch (state->mode) {case MSP_MODE_AM_DETECT : p ="AM (for carrier detect)" ;break ;case MSP_MODE_FM_RADIO : p ="FM Radio" ;break ;
@@ -695,12 +593,20 @@ static int msp_resume(struct i2c_client *client)/* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops msp_ctrl_ops = { + .s_ctrl = msp_s_ctrl, +}; +static const struct v4l2_subdev_core_ops msp_core_ops = { .log_status = msp_log_status, .g_chip_ident = msp_g_chip_ident, - .g_ctrl = msp_g_ctrl, - .s_ctrl = msp_s_ctrl, - .queryctrl = msp_queryctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, .s_std = msp_s_std, };
@@ -728,6 +634,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) {struct msp_state *state;struct v4l2_subdev *sd; +struct v4l2_ctrl_handler *hdl;int (*thread_func)(void *data) =NULL ;int msp_hard;int msp_family;
@@ -752,13 +659,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) state->v4l2_std =V4L2_STD_NTSC ; state->audmode =V4L2_TUNER_MODE_STEREO ; - state->volume =58880 ;/* 0db gain */ - state->balance =32768 ;/* 0db gain */ - state->bass =32768 ; - state->treble =32768 ; - state->loudness =0 ; state->input =-1 ; - state->muted =0 ; state->i2s_mode =0 ;init_waitqueue_head (&state->wq);/* These are the reset input/output positions */
@@ -777,8 +678,6 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)return -ENODEV ; } -msp_set_audio (client); - msp_family = ((state->rev1 >>4 ) &0x0f ) +3 ; msp_product = (state->rev2 >>8 ) &0xff ; msp_prod_hi = msp_product /10 ;
@@ -849,6 +748,34 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) state->opmode =OPMODE_MANUAL ; } + hdl = &state->hdl; +v4l2_ctrl_handler_init (hdl,6 ); +if (state->has_sound_processing) { +v4l2_ctrl_new_std (hdl, &msp_ctrl_ops, +V4L2_CID_AUDIO_BASS ,0 ,65535 ,65535 /100 ,32768 ); +v4l2_ctrl_new_std (hdl, &msp_ctrl_ops, +V4L2_CID_AUDIO_TREBLE ,0 ,65535 ,65535 /100 ,32768 ); +v4l2_ctrl_new_std (hdl, &msp_ctrl_ops, +V4L2_CID_AUDIO_LOUDNESS ,0 ,1 ,1 ,0 ); + } + state->volume =v4l2_ctrl_new_std (hdl, &msp_ctrl_ops, +V4L2_CID_AUDIO_VOLUME ,0 ,65535 ,65535 /100 ,58880 ); +v4l2_ctrl_new_std (hdl, &msp_ctrl_ops, +V4L2_CID_AUDIO_BALANCE ,0 ,65535 ,65535 /100 ,32768 ); + state->muted =v4l2_ctrl_new_std (hdl, &msp_ctrl_ops, +V4L2_CID_AUDIO_MUTE ,0 ,1 ,1 ,0 ); + sd->ctrl_handler = hdl; +if (hdl->error) { +int err = hdl->error; + +v4l2_ctrl_handler_free (hdl); +kfree (state); +return err; + } + +v4l2_ctrl_cluster (2 , &state->volume); +v4l2_ctrl_handler_setup (hdl); +/* hello world :-) */ v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n", msp_family, msp_product,
@@ -903,6 +830,7 @@ static int msp_remove(struct i2c_client *client) }msp_reset (client); +v4l2_ctrl_handler_free (&state->hdl);kfree (state);return 0 ; }
drivers/media/video/msp3400-driver.h +12 -4
@@ -6,6 +6,7 @@#include <media/msp3400.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> /* ---------------------------------------------------------------------- */
@@ -51,6 +52,7 @@ extern int msp_stereo_thresh;struct msp_state {struct v4l2_subdev sd; +struct v4l2_ctrl_handler hdl;int rev1, rev2;int ident;u8 has_nicam;
@@ -87,9 +89,10 @@ struct msp_state {int audmode;int rxsubchans; -int volume, muted; -int balance, loudness; -int bass, treble; +/* volume cluster */ +struct v4l2_ctrl *volume; +struct v4l2_ctrl *muted; +int scan_in_progress;/* thread */
@@ -104,6 +107,11 @@ static inline struct msp_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct msp_state, sd); } +static inline struct msp_state *ctrl_to_state (struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct msp_state, hdl); +} +/* msp3400-driver.c */ int msp_write_dem (struct i2c_client *client,int addr,int val);int msp_write_dsp (struct i2c_client *client,int addr,int val);
@@ -111,7 +119,7 @@ int msp_read_dem(struct i2c_client *client, int addr);int msp_read_dsp (struct i2c_client *client,int addr);int msp_reset (struct i2c_client *client);void msp_set_scart (struct i2c_client *client,int in,int out); -void msp_set_audio (struct i2c_client *client); +void msp_update_volume (struct msp_state *state);int msp_sleep (struct msp_state *state,int timeout);/* msp3400-kthreads.c */
drivers/media/video/msp3400-kthreads.c +8 -8
@@ -497,13 +497,13 @@ restart:v4l_dbg (1 , msp_debug, client,"thread: no carrier scan\n" ); state->scan_in_progress =0 ; -msp_set_audio (client); +msp_update_volume (state);continue ; }/* mute audio */ state->scan_in_progress =1 ; -msp_set_audio (client); +msp_update_volume (state);msp3400c_set_mode (client,MSP_MODE_AM_DETECT ); val1 = val2 =0 ;
@@ -635,7 +635,7 @@ no_second:/* unmute */ state->scan_in_progress =0 ;msp3400c_set_audmode (client); -msp_set_audio (client); +msp_update_volume (state);if (msp_debug)msp3400c_print_mode (client);
@@ -680,13 +680,13 @@ restart:v4l_dbg (1 , msp_debug, client,"thread: no carrier scan\n" ); state->scan_in_progress =0 ; -msp_set_audio (client); +msp_update_volume (state);continue ; }/* mute audio */ state->scan_in_progress =1 ; -msp_set_audio (client); +msp_update_volume (state);/* start autodetect. Note: autodetect is not supported for NTSC-M and radio, hence we force the standard in those
@@ -798,7 +798,7 @@ restart:/* unmute */ msp3400c_set_audmode (client); state->scan_in_progress =0 ; -msp_set_audio (client); +msp_update_volume (state);/* monitor tv audio mode, the first time don't wait so long to get a quick stereo/bilingual result */
@@ -975,7 +975,7 @@ restart:v4l_dbg (1 , msp_debug, client,"thread: no carrier scan\n" ); state->scan_in_progress =0 ; -msp_set_audio (client); +msp_update_volume (state);continue ; }
@@ -1021,7 +1021,7 @@ unmute: }/* unmute: dispatch sound to scart output, set scart volume */ -msp_set_audio (client); +msp_update_volume (state);/* restore ACB */ if (msp_write_dsp (client,0x13 , state->acb))
Signature · 1 line
1.6.4.2
Re: [PATCH 06/15] [RFC] msp3400: convert to the new control framework
Quoted 190 lines · Hans Verkuil
Signed-off-by Hans Verkuil <[email protected]>---
drivers/media/video/msp3400-driver.c | 248 +++++++++++--------------------drivers/media/video/msp3400-driver.h| 16 ++-drivers/media/video/msp3400-kthreads.c| 16 +-3 files changed, 108 insertions(+), 172 deletions(-)drivers/media/video/msp3400-driver.c +22
-100@@ -283,51 +283,6 @@ void msp_set_scart(struct i2c_client *client, int in, int out) msp_write_dem(client, 0x40, state->i2s_mode); } -void msp_set_audio (struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int bal = 0, bass, treble, loudness; - int val = 0; - int reallymuted = state->muted | state->scan_in_progress; - - if (!reallymuted) - val = (state->volume * 0x7f / 65535) << 8; - - v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", - state->muted ? "on" : "off", - state->scan_in_progress ? "yes" : "no", - state->volume); - - msp_write_dsp(client, 0x0000, val); - msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); - if (state->has_scart2_out_volume) - msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); - if (state->has_headphones) - msp_write_dsp(client, 0x0006, val); - if (!state->has_sound_processing) - return; - - if (val) - bal = (u8)((state->balance / 256) - 128); - bass = ((state->bass - 32768) * 0x60 / 65535) << 8; - treble = ((state->treble - 32768) * 0x60 / 65535) << 8; - loudness = state->loudness ? ((5 * 4) << 8) : 0; - - v4l_dbg(1, msp_debug, client, "balance=%d bass=%d treble=%d loudness=%d\n", - state->balance, state->bass, state->treble, state->loudness); - - msp_write_dsp(client, 0x0001, bal << 8); - msp_write_dsp(client, 0x0002, bass); - msp_write_dsp(client, 0x0003, treble); - msp_write_dsp(client, 0x0004, loudness); - if (!state->has_headphones) - return; - msp_write_dsp(client, 0x0030, bal << 8); - msp_write_dsp(client, 0x0031, bass); - msp_write_dsp(client, 0x0032, treble); - msp_write_dsp(client, 0x0033, loudness); -} - /* ------------------------------------------------------------------------ */ static void msp_wake_thread(struct i2c_client *client)@@ -363,98 +318,73 @@ int msp_sleep(struct msp_state *state, int timeout) /* ------------------------------------------------------------------------ */ -static int msp_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int msp_s_ctrl (struct v4l2_ctrl *ctrl) { - struct msp_state *state = to_state(sd); + struct msp_state *state = ctrl_to_state(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + int val = ctrl->val; switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = state->volume; - break; - - case V4L2_CID_AUDIO_MUTE: - ctrl->value = state->muted; - break; - - case V4L2_CID_AUDIO_BALANCE: - if (!state->has_sound_processing) - return -EINVAL; - ctrl->value = state->balance; - break; - - case V4L2_CID_AUDIO_BASS: - if (!state->has_sound_processing) - return -EINVAL; - ctrl->value = state->bass; + case V4L2_CID_AUDIO_VOLUME: { + /* audio volume cluster */ + int reallymuted = state->muted->val | state->scan_in_progress; + + if (!reallymuted) + val = (val * 0x7f / 65535) << 8; + + v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", + state->muted->val ? "on" : "off", + state->scan_in_progress ? "yes" : "no", + state->volume->val); + + msp_write_dsp(client, 0x0000, val); + msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); + if (state->has_scart2_out_volume) + msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); + if (state->has_headphones) + msp_write_dsp(client, 0x0006, val); break; - - case V4L2_CID_AUDIO_TREBLE: - if (!state->has_sound_processing) - return -EINVAL; - ctrl->value = state->treble; - break; - - case V4L2_CID_AUDIO_LOUDNESS: - if (!state->has_sound_processing) - return -EINVAL; - ctrl->value = state->loudness; - break; - - default: - return -EINVAL; } - return 0; -} - -static int msp_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - state->volume = ctrl->value; - if (state->volume == 0) - state->balance = 32768; - break; - - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value < 0 || ctrl->value >= 2) - return -ERANGE; - state->muted = ctrl->value; - break;case
V4L2_CID_AUDIO_BASS:
- if (!state->has_sound_processing)
- return-EINVAL;
-state->bass=ctrl->value;
+ val = ((val - 32768) *0x60/ 65535) << 8;
+msp_write_dsp(client, 0x0002, val);
+ if (state->has_headphones)
+msp_write_dsp(client, 0x0031, val); break;case
V4L2_CID_AUDIO_TREBLE:
- if (!state->has_sound_processing)
- return-EINVAL;
-state->treble=ctrl->value;
+ val = ((val - 32768) *0x60/ 65535) << 8;
+msp_write_dsp(client, 0x0003, val);
+ if (state->has_headphones)
+msp_write_dsp(client, 0x0032, val); break;case
V4L2_CID_AUDIO_LOUDNESS:
- if (!state->has_sound_processing)
- return-EINVAL;
-state->loudness=ctrl->value;
+ val = val ? ((5 * 4) << 8) : 0;
+msp_write_dsp(client, 0x0004, val);
+ if (state->has_headphones)
+msp_write_dsp(client, 0x0033, val); break;case
V4L2_CID_AUDIO_BALANCE:
- if (!state->has_sound_processing)
- return-EINVAL;
-state->balance=ctrl->value;
+ val = (u8)((val / 256) - 128);
+msp_write_dsp(client, 0x0001, val << 8);
+ if (state->has_headphones)
+msp_write_dsp(client, 0x0030, val << 8); break;
Quoted 5 lines
default:
return-EINVAL;
}
-msp_set_audio(client);
return 0;
The return value here should reflect if the update was successful or not. msp_write_dsp can fail and if does the error should be propagated to the caller and the value of the control should not be updated. Also, msp_write_dsp and msp_read_dsp should probably return -EIO in case of failures rather than -1.
Regards,
David Ellingsworth
<snip to end>
RFC PATCH 7/15 saa717x: convert to the new control framework
In reply to Hans Verkuil--- drivers/media/video/saa717x.c | 323 ++++++++++------------------------------ 1 files changed, 81 insertions(+), 242 deletions(-)
drivers/media/video/saa717x.c +81 -242
@@ -37,6 +37,7 @@#include <linux/videodev2.h> #include <linux/i2c.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION ("Philips SAA717x audio/video decoder driver" );
@@ -54,14 +55,11 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");struct saa717x_state {struct v4l2_subdev sd; +struct v4l2_ctrl_handler hdl;v4l2_std_id std;int input;int enable;int radio; -int bright; -int contrast; -int hue; -int sat;int playback;int audio;int tuner_audio_mode;
@@ -80,6 +78,11 @@ static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct saa717x_state, sd); } +static inline struct v4l2_subdev *to_sd (struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd; +} +/* ----------------------------------------------------------------------- */ /* for audio mode */
@@ -773,29 +776,6 @@ static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode)saa717x_write (sd,0x470 , reg_set_audio_template[audio_mode][1 ]); } -/* write regs to video output level (bright,contrast,hue,sat) */ -static void set_video_output_level_regs (struct v4l2_subdev *sd, -struct saa717x_state *decoder) -{ -/* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */ -saa717x_write (sd,0x10a , decoder->bright); - -/* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) - -0h (luminance off) 40: i2c dump -c0h (-1.0 inverse chrominance) -80h (-2.0 inverse chrominance) */ -saa717x_write (sd,0x10b , decoder->contrast); - -/* saturation? 7fh(max)-40h(ITU)-0h(color off) -c0h (-1.0 inverse chrominance) -80h (-2.0 inverse chrominance) */ -saa717x_write (sd,0x10c , decoder->sat); - -/* color hue (phase) control -7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */ -saa717x_write (sd,0x10d , decoder->hue); -} -/* write regs to set audio volume, bass and treble */ static int set_audio_regs (struct v4l2_subdev *sd,struct saa717x_state *decoder)
@@ -828,9 +808,9 @@ static int set_audio_regs(struct v4l2_subdev *sd,saa717x_write (sd,0x480 , val); -/* bass and treble; go to another function */ /* set bass and treble */ - val = decoder->audio_main_bass | (decoder->audio_main_treble <<8 ); + val = decoder->audio_main_bass &0x1f ; + val |= (decoder->audio_main_treble &0x1f ) <<5 ;saa717x_write (sd,0x488 , val);return 0 ; }
@@ -892,218 +872,55 @@ static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale)saa717x_write (sd,0x71 + task_shift, yscale >>8 ); } -static int saa717x_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) -{ -struct saa717x_state *state =to_state (sd); - -switch (ctrl->id) { -case V4L2_CID_BRIGHTNESS : -if (ctrl->value <0 || ctrl->value >255 ) { -v4l2_err (sd,"invalid brightness setting %d\n" , ctrl->value); -return -ERANGE ; - } - - state->bright = ctrl->value; -v4l2_dbg (1 , debug, sd,"bright:%d\n" , state->bright); -saa717x_write (sd,0x10a , state->bright); -break ; - -case V4L2_CID_CONTRAST : -if (ctrl->value <0 || ctrl->value >127 ) { -v4l2_err (sd,"invalid contrast setting %d\n" , ctrl->value); -return -ERANGE ; - } - - state->contrast = ctrl->value; -v4l2_dbg (1 , debug, sd,"contrast:%d\n" , state->contrast); -saa717x_write (sd,0x10b , state->contrast); -break ; - -case V4L2_CID_SATURATION : -if (ctrl->value <0 || ctrl->value >127 ) { -v4l2_err (sd,"invalid saturation setting %d\n" , ctrl->value); -return -ERANGE ; - } - - state->sat = ctrl->value; -v4l2_dbg (1 , debug, sd,"sat:%d\n" , state->sat); -saa717x_write (sd,0x10c , state->sat); -break ; - -case V4L2_CID_HUE : -if (ctrl->value <-128 || ctrl->value >127 ) { -v4l2_err (sd,"invalid hue setting %d\n" , ctrl->value); -return -ERANGE ; - } - - state->hue = ctrl->value; -v4l2_dbg (1 , debug, sd,"hue:%d\n" , state->hue); -saa717x_write (sd,0x10d , state->hue); -break ; - -case V4L2_CID_AUDIO_MUTE : - state->audio_main_mute = ctrl->value; -set_audio_regs (sd, state); -break ; - -case V4L2_CID_AUDIO_VOLUME : - state->audio_main_volume = ctrl->value; -set_audio_regs (sd, state); -break ; - -case V4L2_CID_AUDIO_BALANCE : - state->audio_main_balance = ctrl->value; -set_audio_regs (sd, state); -break ; - -case V4L2_CID_AUDIO_TREBLE : - state->audio_main_treble = ctrl->value; -set_audio_regs (sd, state); -break ; - -case V4L2_CID_AUDIO_BASS : - state->audio_main_bass = ctrl->value; -set_audio_regs (sd, state); -break ; - -default : -return -EINVAL ; - } - -return 0 ; -} - -static int saa717x_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int saa717x_s_ctrl (struct v4l2_ctrl *ctrl) { +struct v4l2_subdev *sd =to_sd (ctrl);struct saa717x_state *state =to_state (sd);switch (ctrl->id) {case V4L2_CID_BRIGHTNESS : - ctrl->value = state->bright; -break ; +saa717x_write (sd,0x10a , ctrl->val); +return 0 ;case V4L2_CID_CONTRAST : - ctrl->value = state->contrast; -break ; +saa717x_write (sd,0x10b , ctrl->val); +return 0 ;case V4L2_CID_SATURATION : - ctrl->value = state->sat; -break ; +saa717x_write (sd,0x10c , ctrl->val); +return 0 ;case V4L2_CID_HUE : - ctrl->value = state->hue; -break ; +saa717x_write (sd,0x10d , ctrl->val); +return 0 ;case V4L2_CID_AUDIO_MUTE : - ctrl->value = state->audio_main_mute; + state->audio_main_mute = ctrl->val;break ;case V4L2_CID_AUDIO_VOLUME : - ctrl->value = state->audio_main_volume; + state->audio_main_volume = ctrl->val;break ;case V4L2_CID_AUDIO_BALANCE : - ctrl->value = state->audio_main_balance; + state->audio_main_balance = ctrl->val;break ;case V4L2_CID_AUDIO_TREBLE : - ctrl->value = state->audio_main_treble; + state->audio_main_treble = ctrl->val;break ;case V4L2_CID_AUDIO_BASS : - ctrl->value = state->audio_main_bass; + state->audio_main_bass = ctrl->val;break ;default : -return -EINVAL ; +return 0 ; } - +set_audio_regs (sd, state);return 0 ; } -static struct v4l2_queryctrl saa717x_qctrl[] = { - { - .id =V4L2_CID_BRIGHTNESS , - .type =V4L2_CTRL_TYPE_INTEGER , - .name ="Brightness" , - .minimum =0 , - .maximum =255 , - .step =1 , - .default_value =128 , - .flags =0 , - }, { - .id =V4L2_CID_CONTRAST , - .type =V4L2_CTRL_TYPE_INTEGER , - .name ="Contrast" , - .minimum =0 , - .maximum =255 , - .step =1 , - .default_value =64 , - .flags =0 , - }, { - .id =V4L2_CID_SATURATION , - .type =V4L2_CTRL_TYPE_INTEGER , - .name ="Saturation" , - .minimum =0 , - .maximum =255 , - .step =1 , - .default_value =64 , - .flags =0 , - }, { - .id =V4L2_CID_HUE , - .type =V4L2_CTRL_TYPE_INTEGER , - .name ="Hue" , - .minimum =-128 , - .maximum =127 , - .step =1 , - .default_value =0 , - .flags =0 , - }, { - .id =V4L2_CID_AUDIO_VOLUME , - .type =V4L2_CTRL_TYPE_INTEGER , - .name ="Volume" , - .minimum =0 , - .maximum =65535 , - .step =65535 /100 , - .default_value =58880 , - .flags =0 , - }, { - .id =V4L2_CID_AUDIO_BALANCE , - .type =V4L2_CTRL_TYPE_INTEGER , - .name ="Balance" , - .minimum =0 , - .maximum =65535 , - .step =65535 /100 , - .default_value =32768 , - .flags =0 , - }, { - .id =V4L2_CID_AUDIO_MUTE , - .type =V4L2_CTRL_TYPE_BOOLEAN , - .name ="Mute" , - .minimum =0 , - .maximum =1 , - .step =1 , - .default_value =1 , - .flags =0 , - }, { - .id =V4L2_CID_AUDIO_BASS , - .type =V4L2_CTRL_TYPE_INTEGER , - .name ="Bass" , - .minimum =0 , - .maximum =65535 , - .step =65535 /100 , - .default_value =32768 , - }, { - .id =V4L2_CID_AUDIO_TREBLE , - .type =V4L2_CTRL_TYPE_INTEGER , - .name ="Treble" , - .minimum =0 , - .maximum =65535 , - .step =65535 /100 , - .default_value =32768 , - }, -}; -static int saa717x_s_video_routing (struct v4l2_subdev *sd,u32 input,u32 output,u32 config) {
@@ -1157,18 +974,6 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd,return 0 ; } -static int saa717x_queryctrl (struct v4l2_subdev *sd,struct v4l2_queryctrl *qc) -{ -int i; - -for (i =0 ; i <ARRAY_SIZE (saa717x_qctrl); i++) -if (qc->id && qc->id == saa717x_qctrl[i].id) { -memcpy (qc, &saa717x_qctrl[i],sizeof (*qc)); -return 0 ; - } -return -EINVAL ; -} -#ifdef CONFIG_VIDEO_ADV_DEBUG static int saa717x_g_register (struct v4l2_subdev *sd,struct v4l2_dbg_register *reg) {
@@ -1381,17 +1186,34 @@ static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)return 0 ; } +static int saa717x_log_status (struct v4l2_subdev *sd) +{ +struct saa717x_state *state =to_state (sd); + +v4l2_ctrl_handler_log_status (&state->hdl, sd->name); +return 0 ; +} +/* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops saa717x_ctrl_ops = { + .s_ctrl = saa717x_s_ctrl, +}; +static const struct v4l2_subdev_core_ops saa717x_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = saa717x_g_register, .s_register = saa717x_s_register, #endif - .queryctrl = saa717x_queryctrl, - .g_ctrl = saa717x_g_ctrl, - .s_ctrl = saa717x_s_ctrl, .s_std = saa717x_s_std, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, + .log_status = saa717x_log_status, };static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
@@ -1427,6 +1249,7 @@ static int saa717x_probe(struct i2c_client *client, const struct i2c_device_id *did) {struct saa717x_state *decoder; +struct v4l2_ctrl_handler *hdl;struct v4l2_subdev *sd;u8 id =0 ;char *p ="" ;
@@ -1462,16 +1285,41 @@ static int saa717x_probe(struct i2c_client *client, p ="saa7171" ;v4l2_info (sd,"%s found @ 0x%x (%s)\n" , p, client->addr <<1 , client->adapter->name); + + hdl = &decoder->hdl; +v4l2_ctrl_handler_init (hdl,9 ); +/* add in ascending ID order */ +v4l2_ctrl_new_std (hdl, &saa717x_ctrl_ops, +V4L2_CID_BRIGHTNESS ,0 ,255 ,1 ,128 ); +v4l2_ctrl_new_std (hdl, &saa717x_ctrl_ops, +V4L2_CID_CONTRAST ,0 ,255 ,1 ,68 ); +v4l2_ctrl_new_std (hdl, &saa717x_ctrl_ops, +V4L2_CID_SATURATION ,0 ,255 ,1 ,64 ); +v4l2_ctrl_new_std (hdl, &saa717x_ctrl_ops, +V4L2_CID_HUE ,-128 ,127 ,1 ,0 ); +v4l2_ctrl_new_std (hdl, &saa717x_ctrl_ops, +V4L2_CID_AUDIO_VOLUME ,0 ,65535 ,65535 /100 ,42000 ); +v4l2_ctrl_new_std (hdl, &saa717x_ctrl_ops, +V4L2_CID_AUDIO_BALANCE ,0 ,65535 ,65535 /100 ,32768 ); +v4l2_ctrl_new_std (hdl, &saa717x_ctrl_ops, +V4L2_CID_AUDIO_BASS ,-16 ,15 ,1 ,0 ); +v4l2_ctrl_new_std (hdl, &saa717x_ctrl_ops, +V4L2_CID_AUDIO_TREBLE ,-16 ,15 ,1 ,0 ); +v4l2_ctrl_new_std (hdl, &saa717x_ctrl_ops, +V4L2_CID_AUDIO_MUTE ,0 ,1 ,1 ,0 ); + sd->ctrl_handler = hdl; +if (hdl->error) { +int err = hdl->error; + +v4l2_ctrl_handler_free (hdl); +kfree (decoder); +return err; + } + decoder->std =V4L2_STD_NTSC ; decoder->input =-1 ; decoder->enable =1 ; -/* tune these parameters */ - decoder->bright =0x80 ; - decoder->contrast =0x44 ; - decoder->sat =0x40 ; - decoder->hue =0x00 ; -/* FIXME!! */ decoder->playback =0 ;/* initially capture mode used */ decoder->audio =1 ;/* DECODER_AUDIO_48_KHZ */
@@ -1482,23 +1330,13 @@ static int saa717x_probe(struct i2c_client *client,/* set volume, bass and treble */ decoder->audio_main_vol_l =6 ; decoder->audio_main_vol_r =6 ; - decoder->audio_main_bass =0 ; - decoder->audio_main_treble =0 ; - decoder->audio_main_mute =0 ; - decoder->audio_main_balance =32768 ; -/* normalize (24 to -40 (not -84) -> 65535 to 0) */ - decoder->audio_main_volume = - (decoder->audio_main_vol_r +41 ) *65535 / (24 - (-40 ));v4l2_dbg (1 , debug, sd,"writing init values\n" );/* FIXME!! */ saa717x_write_regs (sd, reg_init_initialize); -set_video_output_level_regs (sd, decoder); -/* set bass,treble to 0db 20041101 K.Ohta */ - decoder->audio_main_bass =0 ; - decoder->audio_main_treble =0 ; -set_audio_regs (sd, decoder); + +v4l2_ctrl_handler_setup (hdl);set_current_state (TASK_INTERRUPTIBLE );schedule_timeout (2 *HZ );
@@ -1510,6 +1348,7 @@ static int saa717x_remove(struct i2c_client *client)struct v4l2_subdev *sd =i2c_get_clientdata (client);v4l2_device_unregister_subdev (sd); +v4l2_ctrl_handler_free (sd->ctrl_handler);kfree (to_state (sd));return 0 ; }
Signature · 1 line
1.6.4.2
RFC PATCH 8/15 cx25840/ivtv: replace ugly priv control with s_config
In reply to Hans VerkuilThe cx25840 used a private control CX25840_CID_ENABLE_PVR150_WORKAROUND to be told whether to enable a workaround for certain pvr150 cards.
This is really config data that it needs to get at load time.
Implemented this in cx25840 and ivtv.
--- drivers/media/video/cx25840/cx25840-core.c | 23 +++++++++++++++-------- drivers/media/video/cx25840/cx25840-core.h | 8 -------- drivers/media/video/ivtv/ivtv-driver.c | 9 +-------- drivers/media/video/ivtv/ivtv-i2c.c | 7 +++++++ include/media/cx25840.h | 11 +++++++++++ 5 files changed, 34 insertions(+), 24 deletions(-)
drivers/media/video/cx25840/cx25840-core.c +15 -8
@@ -915,11 +915,6 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)struct i2c_client *client =v4l2_get_subdevdata (sd);switch (ctrl->id) { -case CX25840_CID_ENABLE_PVR150_WORKAROUND : - state->pvr150_workaround = ctrl->value; -set_input (client, state->vid_input, state->aud_input); -break ; -case V4L2_CID_BRIGHTNESS :if (ctrl->value <0 || ctrl->value >255 ) { v4l_err(client, "invalid brightness setting %d\n",
@@ -982,9 +977,6 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)struct i2c_client *client =v4l2_get_subdevdata (sd);switch (ctrl->id) { -case CX25840_CID_ENABLE_PVR150_WORKAROUND : - ctrl->value = state->pvr150_workaround; -break ;case V4L2_CID_BRIGHTNESS : ctrl->value = (s8 )cx25840_read (client,0x414 ) +128 ;break ;
@@ -1601,10 +1593,25 @@ static int cx25840_log_status(struct v4l2_subdev *sd)return 0 ; } +static int cx25840_s_config (struct v4l2_subdev *sd,int irq,void *platform_data) +{ +struct cx25840_state *state =to_state (sd); +struct i2c_client *client =v4l2_get_subdevdata (sd); + +if (platform_data) { +struct cx25840_platform_data *pdata = platform_data; + + state->pvr150_workaround = pdata->pvr150_workaround; +set_input (client, state->vid_input, state->aud_input); + } +return 0 ; +} +/* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops cx25840_core_ops = { .log_status = cx25840_log_status, + .s_config = cx25840_s_config, .g_chip_ident = cx25840_g_chip_ident, .g_ctrl = cx25840_g_ctrl, .s_ctrl = cx25840_s_ctrl,
drivers/media/video/cx25840/cx25840-core.h +0 -8
@@ -26,14 +26,6 @@#include <media/v4l2-chip-ident.h> #include <linux/i2c.h> -/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is -present in Hauppauge PVR-150 (and possibly PVR-500) cards that have -certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The -audio autodetect fails on some channels for these models and the workaround -is to select the audio standard explicitly. Many thanks to Hauppauge for -providing this information. */ -#define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0) -struct cx25840_state {struct i2c_client *c;struct v4l2_subdev sd;
drivers/media/video/ivtv/ivtv-driver.c +1 -8
@@ -1253,15 +1253,8 @@ int ivtv_init_on_first_open(struct ivtv *itv)IVTV_DEBUG_INFO ("Getting firmware version..\n" );ivtv_firmware_versions (itv); -if (itv->card->hw_all &IVTV_HW_CX25840 ) { -struct v4l2_control ctrl; - +if (itv->card->hw_all &IVTV_HW_CX25840 )v4l2_subdev_call (itv->sd_video, core, load_fw); -/* CX25840_CID_ENABLE_PVR150_WORKAROUND */ - ctrl.id =V4L2_CID_PRIVATE_BASE ; - ctrl.value = itv->pvr150_workaround; -v4l2_subdev_call (itv->sd_video, core, s_ctrl, &ctrl); - } vf.tuner =0 ; vf.type =V4L2_TUNER_ANALOG_TV ;
drivers/media/video/ivtv/ivtv-i2c.c +7 -0
@@ -63,6 +63,7 @@#include "ivtv-cards.h" #include "ivtv-gpio.h" #include "ivtv-i2c.h" +#include <media/cx25840.h> /* i2c implementation for cx23415/6 chip, ivtv project. * Author: Kevin Thayer (nufan_wfk at yahoo.com)
@@ -292,6 +293,12 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx)if (hw ==IVTV_HW_UPD64031A || hw ==IVTV_HW_UPD6408X ) { sd =v4l2_i2c_new_subdev (&itv->v4l2_dev, adap, mod, type,0 ,I2C_ADDRS (hw_addrs[idx])); + }else if (hw ==IVTV_HW_CX25840 ) { +struct cx25840_platform_data pdata; + + pdata.pvr150_workaround = itv->pvr150_workaround; + sd =v4l2_i2c_new_subdev_cfg (&itv->v4l2_dev, + adap, mod, type,0 , &pdata, hw_addrs[idx],NULL ); }else { sd =v4l2_i2c_new_subdev (&itv->v4l2_dev, adap, mod, type, hw_addrs[idx],NULL );
include/media/cx25840.h +11 -0
@@ -97,4 +97,15 @@ enum cx25840_audio_input { CX25840_AUDIO8, }; +/* pvr150_workaround activates a workaround for a hardware bug that is +present in Hauppauge PVR-150 (and possibly PVR-500) cards that have +certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The +audio autodetect fails on some channels for these models and the workaround +is to select the audio standard explicitly. Many thanks to Hauppauge for +providing this information. +This platform data only needs to be supplied by the ivtv driver. */ +struct cx25840_platform_data { +int pvr150_workaround; +}; +#endif
Signature · 1 line
1.6.4.2
Re: [PATCH 08/15] [RFC] cx25840/ivtv: replace ugly priv control with s_config
Hi Hans,
Quoted 20 lines · On Monday 26 April 2010 09:33:54 Hans Verkuil wrote:
The cx25840 used a private control
CX25840_CID_ENABLE_PVR150_WORKAROUNDto be told whether to enable a workaround for certain pvr150 cards.This is really config data that it needs to get at load time.
Implemented this in cx25840 and ivtv.
Signed-off-by Hans Verkuil <[email protected]>--- drivers/media/video/cx25840/cx25840-core.c | 23 +++++++++++++++-------- drivers/media/video/cx25840/cx25840-core.h | 8 -------- drivers/media/video/ivtv/ivtv-driver.c | 9 +-------- drivers/media/video/ivtv/ivtv-i2c.c | 7 +++++++ include/media/cx25840.h | 11 +++++++++++ 5 files changed, 34 insertions(+), 24 deletions(-)(quoted diff) +0
-0
b/drivers/media/video/cx25840/cx25840-core.cindex f2461cd..b8aa5d2 100644drivers/media/video/cx25840/cx25840-core.c +0
-0
[snip]
Quoted 8 lines
drivers/media/video/cx25840/cx25840-core.c +3
-0@@ -1601,10 +1593,25 @@ static int cx25840_log_status(struct v4l2_subdev *sd) return 0; } +static int cx25840_s_config (struct v4l2_subdev *sd,int irq,void *platform_data) +{ +struct cx25840_state *state =to_state (sd); +struct i2c_client *client =v4l2_get_subdevdata (sd);
drivers/media/video/cx25840/cx25840-core.c static int cx25840_log_status(struct v4l2_subdev · line 1609
Quoted 10 lines
drivers/media/video/cx25840/cx25840-core.c +10
-0+ +if (platform_data) { +struct cx25840_platform_data *pdata = platform_data; + + state->pvr150_workaround = pdata->pvr150_workaround; +set_input (client, state->vid_input, state->aud_input); + } +return 0 ; +} +
You've told me that s_config was only meant for I2C devices in pre-2.6.26 kernels. Shouldn't this be done in the probe function instead ?
Signature · 3 lines
Regards, Laurent Pinchart
Re: [PATCH 08/15] [RFC] cx25840/ivtv: replace ugly priv control with s_config
Quoted 39 lines · On Sunday 02 May 2010 22:41:29 Laurent Pinchart wrote:
Hi Hans,
Quoted 20 lines · On Monday 26 April 2010 09:33:54 Hans Verkuil wrote:
The cx25840 used a private control
CX25840_CID_ENABLE_PVR150_WORKAROUNDto be told whether to enable a workaround for certain pvr150 cards.This is really config data that it needs to get at load time.
Implemented this in cx25840 and ivtv.
Signed-off-by Hans Verkuil <[email protected]>--- drivers/media/video/cx25840/cx25840-core.c | 23 +++++++++++++++-------- drivers/media/video/cx25840/cx25840-core.h | 8 -------- drivers/media/video/ivtv/ivtv-driver.c | 9 +-------- drivers/media/video/ivtv/ivtv-i2c.c | 7 +++++++ include/media/cx25840.h | 11 +++++++++++ 5 files changed, 34 insertions(+), 24 deletions(-)(quoted diff) +0
-0
b/drivers/media/video/cx25840/cx25840-core.cindex f2461cd..b8aa5d2 100644drivers/media/video/cx25840/cx25840-core.c +0
-0[snip]
Quoted 13 lines
drivers/media/video/cx25840/cx25840-core.c +8
-0@@ -1601,10 +1593,25 @@ static int cx25840_log_status(struct v4l2_subdev *sd) return 0; } +static int cx25840_s_config (struct v4l2_subdev *sd,int irq,void *platform_data) +{ +struct cx25840_state *state =to_state (sd); +struct i2c_client *client =v4l2_get_subdevdata (sd); + +if (platform_data) { +struct cx25840_platform_data *pdata = platform_data; + + state->pvr150_workaround = pdata->pvr150_workaround;
Quoted 8 lines
drivers/media/video/cx25840/cx25840-core.c static int cx25840_log_status(struct v4l2_subdev *sd) · line 1609
Quoted 5 lines
drivers/media/video/cx25840/cx25840-core.c +5
-0+set_input (client, state->vid_input, state->aud_input); + } +return 0 ; +} +You've told me that
s_configwas only meant for I2C devices in pre-2.6.26 kernels. Shouldn't this be done in the probe function instead ?
s_config is only meant for i2c drivers that have to support pre-2.6.26 kernels (only applicable for the hg tree, of course). And that is definitely the case for cx25840.
As a side note: we could decide to ditch that backwards compatibility support in the git tree of course, but I don't know how problematic that may turn out to be for the hg tree maintenance. Anyway, I have too much to do already :-)
Regards,
Hans
Signature · 1 line
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
RFC PATCH 9/15 cx25840: convert to the new control framework
In reply to Hans Verkuil--- drivers/media/video/cx25840/cx25840-audio.c | 144 +++------------------- drivers/media/video/cx25840/cx25840-core.c | 178 ++++++++++----------------- drivers/media/video/cx25840/cx25840-core.h | 15 ++- 3 files changed, 91 insertions(+), 246 deletions(-)
drivers/media/video/cx25840/cx25840-audio.c +17 -127
@@ -474,33 +474,10 @@ void cx25840_audio_set_path(struct i2c_client *client)cx25840_and_or (client,0x803 , ~0x10 ,0x10 ); } -static int get_volume (struct i2c_client *client) -{ -struct cx25840_state *state =to_state (i2c_get_clientdata (client)); -int vol; - -if (state->unmute_volume >=0 ) -return state->unmute_volume; - -/* Volume runs +18dB to -96dB in 1/2dB steps -* change to fit the msp3400 -114dB to +12dB range */ - -/* check PATH1_VOLUME */ - vol =228 -cx25840_read (client,0x8d4 ); - vol = (vol /2 ) +23 ; -return vol <<9 ; -} -static void set_volume (struct i2c_client *client,int volume) { -struct cx25840_state *state =to_state (i2c_get_clientdata (client));int vol; -if (state->unmute_volume >=0 ) { - state->unmute_volume = volume; -return ; - } -/* Convert the volume to msp3400 values (0-127) */ vol = volume >>9 ;
@@ -517,52 +494,6 @@ static void set_volume(struct i2c_client *client, int volume)cx25840_write (client,0x8d4 ,228 - (vol *2 )); } -static int get_bass (struct i2c_client *client) -{ -/* bass is 49 steps +12dB to -12dB */ - -/* check PATH1_EQ_BASS_VOL */ -int bass =cx25840_read (client,0x8d9 ) &0x3f ; - bass = (((48 - bass) *0xffff ) +47 ) /48 ; -return bass; -} - -static void set_bass (struct i2c_client *client,int bass) -{ -/* PATH1_EQ_BASS_VOL */ -cx25840_and_or (client,0x8d9 , ~0x3f ,48 - (bass *48 /0xffff )); -} - -static int get_treble (struct i2c_client *client) -{ -/* treble is 49 steps +12dB to -12dB */ - -/* check PATH1_EQ_TREBLE_VOL */ -int treble =cx25840_read (client,0x8db ) &0x3f ; - treble = (((48 - treble) *0xffff ) +47 ) /48 ; -return treble; -} - -static void set_treble (struct i2c_client *client,int treble) -{ -/* PATH1_EQ_TREBLE_VOL */ -cx25840_and_or (client,0x8db , ~0x3f ,48 - (treble *48 /0xffff )); -} - -static int get_balance (struct i2c_client *client) -{ -/* balance is 7 bit, 0 to -96dB */ - -/* check PATH1_BAL_LEVEL */ -int balance =cx25840_read (client,0x8d5 ) &0x7f ; -/* check PATH1_BAL_LEFT */ -if ((cx25840_read (client,0x8d5 ) &0x80 ) ==0 ) - balance =0x80 - balance; -else - balance =0x80 + balance; -return balance <<8 ; -} -static void set_balance (struct i2c_client *client,int balance) {int bal = balance >>8 ;
@@ -579,31 +510,6 @@ static void set_balance(struct i2c_client *client, int balance) } } -static int get_mute (struct i2c_client *client) -{ -struct cx25840_state *state =to_state (i2c_get_clientdata (client)); - -return state->unmute_volume >=0 ; -} - -static void set_mute (struct i2c_client *client,int mute) -{ -struct cx25840_state *state =to_state (i2c_get_clientdata (client)); - -if (mute && state->unmute_volume ==-1 ) { -int vol =get_volume (client); - -set_volume (client,0 ); - state->unmute_volume = vol; - } -else if (!mute && state->unmute_volume !=-1 ) { -int vol = state->unmute_volume; - - state->unmute_volume =-1 ; -set_volume (client, vol); - } -} -int cx25840_s_clock_freq (struct v4l2_subdev *sd,u32 freq) {struct i2c_client *client =v4l2_get_subdevdata (sd);
@@ -624,25 +530,31 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)return retval; } -int cx25840_audio_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int cx25840_audio_s_ctrl (struct v4l2_ctrl *ctrl) { +struct v4l2_subdev *sd =to_sd (ctrl); +struct cx25840_state *state =to_state (sd);struct i2c_client *client =v4l2_get_subdevdata (sd);switch (ctrl->id) {case V4L2_CID_AUDIO_VOLUME : - ctrl->value =get_volume (client); +if (state->mute->val) +set_volume (client,0 ); +else +set_volume (client, state->volume->val);break ;case V4L2_CID_AUDIO_BASS : - ctrl->value =get_bass (client); +/* PATH1_EQ_BASS_VOL */ +cx25840_and_or (client,0x8d9 , ~0x3f , +48 - (ctrl->val *48 /0xffff ));break ;case V4L2_CID_AUDIO_TREBLE : - ctrl->value =get_treble (client); +/* PATH1_EQ_TREBLE_VOL */ +cx25840_and_or (client,0x8db , ~0x3f , +48 - (ctrl->val *48 /0xffff ));break ;case V4L2_CID_AUDIO_BALANCE : - ctrl->value =get_balance (client); -break ; -case V4L2_CID_AUDIO_MUTE : - ctrl->value =get_mute (client); +set_balance (client, ctrl->val);break ;default :return -EINVAL ;
@@ -650,28 +562,6 @@ int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)return 0 ; } -int cx25840_audio_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) -{ -struct i2c_client *client =v4l2_get_subdevdata (sd); - -switch (ctrl->id) { -case V4L2_CID_AUDIO_VOLUME : -set_volume (client, ctrl->value); -break ; -case V4L2_CID_AUDIO_BASS : -set_bass (client, ctrl->value); -break ; -case V4L2_CID_AUDIO_TREBLE : -set_treble (client, ctrl->value); -break ; -case V4L2_CID_AUDIO_BALANCE : -set_balance (client, ctrl->value); -break ; -case V4L2_CID_AUDIO_MUTE : -set_mute (client, ctrl->value); -break ; -default : -return -EINVAL ; - } -return 0 ; -} +const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = { + .s_ctrl = cx25840_audio_s_ctrl, +};
drivers/media/video/cx25840/cx25840-core.c +63 -115
257 lines not on this page · raw message
drivers/media/video/cx25840/cx25840-core.h +11 -4
@@ -24,11 +24,15 @@#include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> #include <linux/i2c.h> struct cx25840_state {struct i2c_client *c;struct v4l2_subdev sd; +struct v4l2_ctrl_handler hdl; +struct v4l2_ctrl *volume; +struct v4l2_ctrl *mute;int pvr150_workaround;int radio;v4l2_std_id std;
@@ -36,8 +40,6 @@ struct cx25840_state {enum cx25840_audio_input aud_input;u32 audclk_freq;int audmode; -int unmute_volume;/* -1 if not muted */ -int default_volume;int vbi_line_offset;u32 id;u32 rev;
@@ -51,6 +53,11 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct cx25840_state, sd); } +static inline struct v4l2_subdev *to_sd (struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd; +} +static inline bool is_cx2583x (struct cx25840_state *state) { return state->id == V4L2_IDENT_CX25836 ||
@@ -86,8 +93,8 @@ int cx25840_loadfw(struct i2c_client *client);/* cx25850-audio.c */ void cx25840_audio_set_path (struct i2c_client *client);int cx25840_s_clock_freq (struct v4l2_subdev *sd,u32 freq); -int cx25840_audio_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl); -int cx25840_audio_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl); + +extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops;/* ----------------------------------------------------------------------- */ /* cx25850-vbi.c */
Signature · 1 line
1.6.4.2
RFC PATCH 10/15 cx2341x: convert to the control framework
In reply to Hans VerkuilSince this module is also used by drivers that are not yet converted, the old and new code have to co-exist.
The source is split into three parts: a common part at the top, which is used by both old and new code, then the old code followed by the new control framework implementation. This new code is much more readable (and shorter!) than the original code.
Once all bridge drivers that use this are converted the old code can be deleted.
--- drivers/media/video/cx2341x.c | 725 ++++++++++++++++++++++++++++++++++------- include/media/cx2341x.h | 81 +++++ 2 files changed, 694 insertions(+), 112 deletions(-)
drivers/media/video/cx2341x.c +613 -112
@@ -38,6 +38,145 @@ static int debug;module_param (debug, int,0644 );MODULE_PARM_DESC (debug,"Debug level (0-1)" ); +/********************** COMMON CODE *********************/ + +/* definitions for audio properties bits 29-28 */ +#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 +#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 +#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 + +static const char *cx2341x_get_name (u32 id) +{ +switch (id) { +case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE : +return "Spatial Filter Mode" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER : +return "Spatial Filter" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE : +return "Spatial Luma Filter Type" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE : +return "Spatial Chroma Filter Type" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE : +return "Temporal Filter Mode" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER : +return "Temporal Filter" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE : +return "Median Filter Type" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP : +return "Median Luma Filter Maximum" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM : +return "Median Luma Filter Minimum" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP : +return "Median Chroma Filter Maximum" ; +case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM : +return "Median Chroma Filter Minimum" ; +case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS : +return "Insert Navigation Packets" ; + } +return NULL ; +} + +static const char **cx2341x_get_menu (u32 id) +{ +static const char *cx2341x_video_spatial_filter_mode_menu[] = { +"Manual" , +"Auto" , +NULL + }; + +static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { +"Off" , +"1D Horizontal" , +"1D Vertical" , +"2D H/V Separable" , +"2D Symmetric non-separable" , +NULL + }; + +static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { +"Off" , +"1D Horizontal" , +NULL + }; + +static const char *cx2341x_video_temporal_filter_mode_menu[] = { +"Manual" , +"Auto" , +NULL + }; + +static const char *cx2341x_video_median_filter_type_menu[] = { +"Off" , +"Horizontal" , +"Vertical" , +"Horizontal/Vertical" , +"Diagonal" , +NULL + }; + +switch (id) { +case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE : +return cx2341x_video_spatial_filter_mode_menu; +case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE : +return cx2341x_video_luma_spatial_filter_type_menu; +case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE : +return cx2341x_video_chroma_spatial_filter_type_menu; +case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE : +return cx2341x_video_temporal_filter_mode_menu; +case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE : +return cx2341x_video_median_filter_type_menu; + } +return NULL ; +} + +static void cx2341x_ctrl_fill (u32 id,const char **name,enum v4l2_ctrl_type *type, +s32 *min,s32 *max,s32 *step,s32 *def,u32 *flags) +{ + *name =cx2341x_get_name (id); + *flags =0 ; + +switch (id) { +case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE : +case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE : +case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE : +case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE : +case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE : + *type =V4L2_CTRL_TYPE_MENU ; + *min =0 ; + *step =0 ; +break ; +case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS : + *type =V4L2_CTRL_TYPE_BOOLEAN ; + *min =0 ; + *max = *step =1 ; +break ; +default : + *type =V4L2_CTRL_TYPE_INTEGER ; +break ; + } +switch (id) { +case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE : +case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE : +case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE : + *flags |=V4L2_CTRL_FLAG_UPDATE ; +break ; +case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER : +case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER : +case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP : +case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM : +case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP : +case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM : + *flags |=V4L2_CTRL_FLAG_SLIDER ; +break ; +case V4L2_CID_MPEG_VIDEO_ENCODING : + *flags |=V4L2_CTRL_FLAG_READ_ONLY ; +break ; + } +} + + +/********************** OLD CODE *********************/ +/* Must be sorted from low to high control ID! */ const u32 cx2341x_mpeg_ctrls[] = {V4L2_CID_MPEG_CLASS ,
@@ -134,8 +273,6 @@ static const struct cx2341x_mpeg_params default_params = { .video_chroma_median_filter_top = 255, .video_chroma_median_filter_bottom = 0, }; - -/* Map the control ID to the correct field in the cx2341x_mpeg_params struct. Return -EINVAL if the ID is unknown, else return 0. */ static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
@@ -415,83 +552,33 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, {const char *name; - qctrl->flags =0 ;switch (qctrl->id) {/* MPEG controls */ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE : - name ="Spatial Filter Mode" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER : - name ="Spatial Filter" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE : - name ="Spatial Luma Filter Type" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE : - name ="Spatial Chroma Filter Type" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE : - name ="Temporal Filter Mode" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER : - name ="Temporal Filter" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE : - name ="Median Filter Type" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP : - name ="Median Luma Filter Maximum" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM : - name ="Median Luma Filter Minimum" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP : - name ="Median Chroma Filter Maximum" ; -break ;case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM : - name ="Median Chroma Filter Minimum" ; -break ;case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS : - name ="Insert Navigation Packets" ; -break ; +cx2341x_ctrl_fill (qctrl->id, &name, &qctrl->type, + &min, &max, &step, &def, &qctrl->flags); + qctrl->minimum = min; + qctrl->maximum = max; + qctrl->step = step; + qctrl->default_value = def; + qctrl->reserved[0 ] = qctrl->reserved[1 ] =0 ; +strlcpy (qctrl->name, name,sizeof (qctrl->name)); +return 0 ;default :return v4l2_ctrl_query_fill (qctrl, min, max, step, def); } -switch (qctrl->id) { -case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE : -case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE : -case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE : -case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE : -case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE : - qctrl->type =V4L2_CTRL_TYPE_MENU ; - min =0 ; - step =1 ; -break ; -case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS : - qctrl->type =V4L2_CTRL_TYPE_BOOLEAN ; - min =0 ; - max =1 ; - step =1 ; -break ; -default : - qctrl->type =V4L2_CTRL_TYPE_INTEGER ; -break ; - } -switch (qctrl->id) { -case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE : -case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE : -case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE : - qctrl->flags |=V4L2_CTRL_FLAG_UPDATE ; -break ; - } - qctrl->minimum = min; - qctrl->maximum = max; - qctrl->step = step; - qctrl->default_value = def; - qctrl->reserved[0 ] = qctrl->reserved[1 ] =0 ; -snprintf (qctrl->name,sizeof (qctrl->name), name); -return 0 ; } int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
@@ -797,42 +884,6 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)NULL }; -static const char *cx2341x_video_spatial_filter_mode_menu[] = { -"Manual" , -"Auto" , -NULL - }; - -static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { -"Off" , -"1D Horizontal" , -"1D Vertical" , -"2D H/V Separable" , -"2D Symmetric non-separable" , -NULL - }; - -static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { -"Off" , -"1D Horizontal" , -NULL - }; - -static const char *cx2341x_video_temporal_filter_mode_menu[] = { -"Manual" , -"Auto" , -NULL - }; - -static const char *cx2341x_video_median_filter_type_menu[] = { -"Off" , -"Horizontal" , -"Vertical" , -"Horizontal/Vertical" , -"Diagonal" , -NULL - }; -switch (id) {case V4L2_CID_MPEG_STREAM_TYPE : return (p->capabilities & CX2341X_CAP_HAS_TS) ?
@@ -844,26 +895,17 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)case V4L2_CID_MPEG_AUDIO_L3_BITRATE :return NULL ;case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE : -return cx2341x_video_spatial_filter_mode_menu;case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE : -return cx2341x_video_luma_spatial_filter_type_menu;case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE : -return cx2341x_video_chroma_spatial_filter_type_menu;case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE : -return cx2341x_video_temporal_filter_mode_menu;case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE : -return cx2341x_video_median_filter_type_menu; +return cx2341x_get_menu (id);default :return v4l2_ctrl_get_menu (id); } }EXPORT_SYMBOL (cx2341x_ctrl_get_menu); -/* definitions for audio properties bits 29-28 */ -#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 -#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 -#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 -static void cx2341x_calc_audio_properties (struct cx2341x_mpeg_params *params) { params->audio_properties =
@@ -1199,9 +1241,468 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) }EXPORT_SYMBOL (cx2341x_log_status); -/* -* Local variables: -* c-basic-offset: 8 -* End: -*/ + +/********************** NEW CODE *********************/ + +static inline struct cx2341x_handler *to_cxhdl (struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct cx2341x_handler, hdl); +} + +static int cx2341x_hdl_api (struct cx2341x_handler *hdl, +u32 cmd,int args, ...) +{ +u32 data[CX2341X_MBOX_MAX_DATA ]; +va_list vargs; +int i; + +va_start (vargs, args); + +for (i =0 ; i < args; i++) + data[i] =va_arg (vargs, int); +va_end (vargs); +return hdl->func (hdl->priv, cmd, args,0 , data); +} + +/* ctrl->handler->lock is held, so it is safe to access cur.val */ +static inline int cx2341x_neq (struct v4l2_ctrl *ctrl) +{ +return ctrl && ctrl->val != ctrl->cur.val; +} + +static int cx2341x_try_ctrl (struct v4l2_ctrl *ctrl) +{ +struct cx2341x_handler *hdl =to_cxhdl (ctrl); +s32 val = ctrl->val; + +switch (ctrl->id) { +case V4L2_CID_MPEG_VIDEO_B_FRAMES : { +/* video gop cluster */ +int b = val +1 ; +int gop = hdl->video_gop_size->val; + + gop = b * ((gop + b -1 ) / b); + +/* Max GOP size = 34 */ +while (gop >34 ) + gop -= b; + hdl->video_gop_size->val = gop; +break ; + } + +case V4L2_CID_MPEG_STREAM_TYPE : +/* stream type cluster */ + hdl->video_encoding->val = + (hdl->stream_type->val ==V4L2_MPEG_STREAM_TYPE_MPEG1_SS || + hdl->stream_type->val ==V4L2_MPEG_STREAM_TYPE_MPEG1_VCD ) ? +V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : +V4L2_MPEG_VIDEO_ENCODING_MPEG_2 ; +if (hdl->video_encoding->val ==V4L2_MPEG_VIDEO_ENCODING_MPEG_1 ) +/* MPEG-1 implies CBR */ + hdl->video_bitrate_mode->val = +V4L2_MPEG_VIDEO_BITRATE_MODE_CBR ; +/* peak bitrate shall be >= normal bitrate */ +if (hdl->video_bitrate_mode->val ==V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + hdl->video_bitrate_peak->val < hdl->video_bitrate->val) + hdl->video_bitrate_peak->val = hdl->video_bitrate->val; +break ; + } +return 0 ; +} + +static int cx2341x_s_ctrl (struct v4l2_ctrl *ctrl) +{ +static const int mpeg_stream_type[] = { +0 ,/* MPEG-2 PS */ +1 ,/* MPEG-2 TS */ +2 ,/* MPEG-1 SS */ +14 ,/* DVD */ +11 ,/* VCD */ +12 ,/* SVCD */ + }; +struct cx2341x_handler *hdl =to_cxhdl (ctrl); +s32 val = ctrl->val; +u32 props; +int err; + +switch (ctrl->id) { +case V4L2_CID_MPEG_STREAM_VBI_FMT : +if (hdl->ops && hdl->ops->s_stream_vbi_fmt) +return hdl->ops->s_stream_vbi_fmt (hdl, val); +return 0 ; + +case V4L2_CID_MPEG_VIDEO_ASPECT : +return cx2341x_hdl_api (hdl, +CX2341X_ENC_SET_ASPECT_RATIO ,1 , val +1 ); + +case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE : +return cx2341x_hdl_api (hdl,CX2341X_ENC_SET_GOP_CLOSURE ,1 , val); + +case V4L2_CID_MPEG_AUDIO_MUTE : +return cx2341x_hdl_api (hdl,CX2341X_ENC_MUTE_AUDIO ,1 , val); + +case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION : +return cx2341x_hdl_api (hdl, +CX2341X_ENC_SET_FRAME_DROP_RATE ,1 , val); + +case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS : +return cx2341x_hdl_api (hdl,CX2341X_ENC_MISC ,2 ,7 , val); + +case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ : +/* audio properties cluster */ + props = (hdl->audio_sampling_freq->val <<0 ) | + (hdl->audio_mode->val <<8 ) | + (hdl->audio_mode_extension->val <<10 ) | + (hdl->audio_crc->val <<14 ); +if (hdl->audio_emphasis->val ==V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17 ) + props |=3 <<12 ; +else + props |= hdl->audio_emphasis->val <<12 ; + +if (hdl->audio_encoding->val ==V4L2_MPEG_AUDIO_ENCODING_AC3 ) { + props |= +#if 1 +/* Not sure if this MPEG Layer II setting is required */ + ((3 -V4L2_MPEG_AUDIO_ENCODING_LAYER_2 ) <<2 ) | +#endif + (hdl->audio_ac3_bitrate->val <<4 ) | + (CX2341X_AUDIO_ENCODING_METHOD_AC3 <<28 ); + }else { +/* Assuming MPEG Layer II */ + props |= + ((3 - hdl->audio_encoding->val) <<2 ) | + ((1 + hdl->audio_l2_bitrate->val) <<4 ); + } + err =cx2341x_hdl_api (hdl, +CX2341X_ENC_SET_AUDIO_PROPERTIES ,1 , props); +if (err) +return err; + + hdl->audio_properties = props; +if (hdl->audio_ac3_bitrate) { +int is_ac3 = hdl->audio_encoding->val == +V4L2_MPEG_AUDIO_ENCODING_AC3 ; + +v4l2_ctrl_activate (hdl->audio_ac3_bitrate, is_ac3); +v4l2_ctrl_activate (hdl->audio_l2_bitrate, !is_ac3); + } +v4l2_ctrl_activate (hdl->audio_mode_extension, + hdl->audio_mode->val ==V4L2_MPEG_AUDIO_MODE_JOINT_STEREO ); +if (cx2341x_neq (hdl->audio_sampling_freq) && + hdl->ops && hdl->ops->s_audio_sampling_freq) +return hdl->ops->s_audio_sampling_freq (hdl, hdl->audio_sampling_freq->val); +if (cx2341x_neq (hdl->audio_mode) && + hdl->ops && hdl->ops->s_audio_mode) +return hdl->ops->s_audio_mode (hdl, hdl->audio_mode->val); +return 0 ; + +case V4L2_CID_MPEG_VIDEO_B_FRAMES : +/* video gop cluster */ +return cx2341x_hdl_api (hdl,CX2341X_ENC_SET_GOP_PROPERTIES ,2 , + hdl->video_gop_size->val, + hdl->video_b_frames->val +1 ); + +case V4L2_CID_MPEG_STREAM_TYPE : +/* stream type cluster */ + err =cx2341x_hdl_api (hdl, +CX2341X_ENC_SET_STREAM_TYPE ,1 , mpeg_stream_type[val]); +if (err) +return err; + + err =cx2341x_hdl_api (hdl,CX2341X_ENC_SET_BIT_RATE ,5 , + hdl->video_bitrate_mode->val, + hdl->video_bitrate->val, + hdl->video_bitrate_peak->val /400 ,0 ,0 ); +if (err) +return err; + +v4l2_ctrl_activate (hdl->video_bitrate_mode, + hdl->video_encoding->val !=V4L2_MPEG_VIDEO_ENCODING_MPEG_1 ); +v4l2_ctrl_activate (hdl->video_bitrate_peak, + hdl->video_bitrate_mode->val !=V4L2_MPEG_VIDEO_BITRATE_MODE_CBR ); +if (cx2341x_neq (hdl->video_encoding) && + hdl->ops && hdl->ops->s_video_encoding) +return hdl->ops->s_video_encoding (hdl, hdl->video_encoding->val); +return 0 ; + +case V4L2_CID_MPEG_VIDEO_MUTE : +/* video mute cluster */ +return cx2341x_hdl_api (hdl,CX2341X_ENC_MUTE_VIDEO ,1 , + hdl->video_mute->val | + (hdl->video_mute_yuv->val <<8 )); + +case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE : { +int active_filter; + +/* video filter mode */ + err =cx2341x_hdl_api (hdl,CX2341X_ENC_SET_DNR_FILTER_MODE ,2 , + hdl->video_spatial_filter_mode->val | + (hdl->video_temporal_filter_mode->val <<1 ), + hdl->video_median_filter_type->val); +if (err) +return err; + + active_filter = hdl->video_spatial_filter_mode->val != +V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO ; +v4l2_ctrl_activate (hdl->video_spatial_filter, active_filter); +v4l2_ctrl_activate (hdl->video_luma_spatial_filter_type, active_filter); +v4l2_ctrl_activate (hdl->video_chroma_spatial_filter_type, active_filter); + active_filter = hdl->video_temporal_filter_mode->val != +V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO ; +v4l2_ctrl_activate (hdl->video_temporal_filter, active_filter); + active_filter = hdl->video_median_filter_type->val != +V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF ; +v4l2_ctrl_activate (hdl->video_luma_median_filter_bottom, active_filter); +v4l2_ctrl_activate (hdl->video_luma_median_filter_top, active_filter); +v4l2_ctrl_activate (hdl->video_chroma_median_filter_bottom, active_filter); +v4l2_ctrl_activate (hdl->video_chroma_median_filter_top, active_filter); +return 0 ; + } + +case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE : +/* video filter type cluster */ +return cx2341x_hdl_api (hdl, +CX2341X_ENC_SET_SPATIAL_FILTER_TYPE ,2 , + hdl->video_luma_spatial_filter_type->val, + hdl->video_chroma_spatial_filter_type->val); + +case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER : +/* video filter cluster */ +return cx2341x_hdl_api (hdl,CX2341X_ENC_SET_DNR_FILTER_PROPS ,2 , + hdl->video_spatial_filter->val, + hdl->video_temporal_filter->val); + +case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP : +/* video median cluster */ +return cx2341x_hdl_api (hdl,CX2341X_ENC_SET_CORING_LEVELS ,4 , + hdl->video_luma_median_filter_bottom->val, + hdl->video_luma_median_filter_top->val, + hdl->video_chroma_median_filter_bottom->val, + hdl->video_chroma_median_filter_top->val); + } +return -EINVAL ; +} + +static const struct v4l2_ctrl_ops cx2341x_ops = { + .try_ctrl = cx2341x_try_ctrl, + .s_ctrl = cx2341x_s_ctrl, +}; + +static struct v4l2_ctrl *cx2341x_ctrl_new_custom (struct v4l2_ctrl_handler *hdl, +u32 id,s32 min,s32 max,s32 step,s32 def) +{ +enum v4l2_ctrl_type type; +const char *name; +u32 flags; + +cx2341x_ctrl_fill (id, &name, &type, &min, &max, &step, &def, &flags); +return v4l2_ctrl_new_custom (hdl, &cx2341x_ops, id, + name, type, min, max, step, def, flags, +cx2341x_get_menu (id),NULL ); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_std (struct v4l2_ctrl_handler *hdl, +u32 id,s32 min,s32 max,s32 step,s32 def) +{ +return v4l2_ctrl_new_std (hdl, &cx2341x_ops, id, min, max, step, def); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_menu (struct v4l2_ctrl_handler *hdl, +u32 id,s32 mask,s32 def) +{ +return v4l2_ctrl_new_std_menu (hdl, &cx2341x_ops, id, mask, def); +} + +int cx2341x_handler_init (struct cx2341x_handler *cxhdl, +unsigned nr_of_controls_hint) +{ +struct v4l2_ctrl_handler *hdl = &cxhdl->hdl; +u32 caps = cxhdl->capabilities; +int has_sliced_vbi = caps &CX2341X_CAP_HAS_SLICED_VBI ; +int has_ac3 = caps &CX2341X_CAP_HAS_AC3 ; +int has_ts = caps &CX2341X_CAP_HAS_TS ; + + cxhdl->width =720 ; + cxhdl->height =480 ; + +v4l2_ctrl_handler_init (hdl, nr_of_controls_hint); + +/* Add controls in ascending control ID order for fastest +insertion time. */ + cxhdl->stream_type =cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_STREAM_TYPE , has_ts ?0 :2 ,0 ); + cxhdl->stream_vbi_fmt =cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_STREAM_VBI_FMT , has_sliced_vbi ?0 :2 ,0 ); + cxhdl->audio_sampling_freq =cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ , +0 ,V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000 ); + cxhdl->audio_encoding =cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_AUDIO_ENCODING , + has_ac3 ? ~0x12 : ~0x2 ,V4L2_MPEG_AUDIO_ENCODING_LAYER_2 ); + cxhdl->audio_l2_bitrate =cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_AUDIO_L2_BITRATE , +V4L2_MPEG_AUDIO_L2_BITRATE_192K , +V4L2_MPEG_AUDIO_L2_BITRATE_384K ,0 , +V4L2_MPEG_AUDIO_L2_BITRATE_224K ); + cxhdl->audio_mode =cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_AUDIO_MODE ,0 ,0 ); + cxhdl->audio_mode_extension =cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_AUDIO_MODE_EXTENSION ,0 ,0 ); + cxhdl->audio_emphasis =cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_AUDIO_EMPHASIS ,0 ,0 ); + cxhdl->audio_crc =cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_AUDIO_CRC ,0 ,0 ); + +cx2341x_ctrl_new_std (hdl,V4L2_CID_MPEG_AUDIO_MUTE ,0 ,1 ,1 ,0 ); +if (has_ac3) + cxhdl->audio_ac3_bitrate =cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_AUDIO_AC3_BITRATE , +V4L2_MPEG_AUDIO_AC3_BITRATE_48K , +V4L2_MPEG_AUDIO_AC3_BITRATE_448K ,1 , +V4L2_MPEG_AUDIO_AC3_BITRATE_224K ); + cxhdl->video_encoding =cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_VIDEO_ENCODING , +V4L2_MPEG_VIDEO_ENCODING_MPEG_1 , +V4L2_MPEG_VIDEO_ENCODING_MPEG_2 ,0 , +V4L2_MPEG_VIDEO_ENCODING_MPEG_2 ); +cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_VIDEO_ASPECT , +0 , V4L2_MPEG_VIDEO_ASPECT_4x3); + cxhdl->video_b_frames =cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_VIDEO_B_FRAMES ,0 ,33 ,1 ,2 ); + cxhdl->video_gop_size =cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_VIDEO_GOP_SIZE , +1 ,34 ,1 , cxhdl->is_50hz ?12 :15 ); +cx2341x_ctrl_new_std (hdl,V4L2_CID_MPEG_VIDEO_GOP_CLOSURE ,0 ,1 ,1 ,1 ); + cxhdl->video_bitrate_mode =cx2341x_ctrl_new_menu (hdl, +V4L2_CID_MPEG_VIDEO_BITRATE_MODE , +0 ,V4L2_MPEG_VIDEO_BITRATE_MODE_VBR ); + cxhdl->video_bitrate =cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_VIDEO_BITRATE , +0 ,27000000 ,1 ,6000000 ); + cxhdl->video_bitrate_peak =cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_VIDEO_BITRATE_PEAK , +0 ,27000000 ,1 ,8000000 ); +cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION ,0 ,255 ,1 ,0 ); + cxhdl->video_mute =cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_VIDEO_MUTE ,0 ,1 ,1 ,0 ); + cxhdl->video_mute_yuv =cx2341x_ctrl_new_std (hdl, +V4L2_CID_MPEG_VIDEO_MUTE_YUV ,0 ,0xffffff ,1 ,0x008080 ); + +/* CX23415/6 specific */ + cxhdl->video_spatial_filter_mode =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE , +V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL , +V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO ,0 , +V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL ); + cxhdl->video_spatial_filter =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER , +0 ,15 ,1 ,0 ); + cxhdl->video_luma_spatial_filter_type =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE , +V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF , +V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE , +0 , +V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR ); + cxhdl->video_chroma_spatial_filter_type =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE , +V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF , +V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR , +0 , +V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR ); + cxhdl->video_temporal_filter_mode =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE , +V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL , +V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO , +0 , +V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL ); + cxhdl->video_temporal_filter =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER , +0 ,31 ,1 ,8 ); + cxhdl->video_median_filter_type =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE , +V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF , +V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG , +0 , +V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF ); + cxhdl->video_luma_median_filter_bottom =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM , +0 ,255 ,1 ,0 ); + cxhdl->video_luma_median_filter_top =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP , +0 ,255 ,1 ,255 ); + cxhdl->video_chroma_median_filter_bottom =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM , +0 ,255 ,1 ,0 ); + cxhdl->video_chroma_median_filter_top =cx2341x_ctrl_new_custom (hdl, +V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP , +0 ,255 ,1 ,255 ); +cx2341x_ctrl_new_custom (hdl,V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS , +0 ,1 ,1 ,0 ); + +if (hdl->error) { +int err = hdl->error; + +v4l2_ctrl_handler_free (hdl); +return err; + } + +v4l2_ctrl_cluster (8 , &cxhdl->audio_sampling_freq); +v4l2_ctrl_cluster (2 , &cxhdl->video_b_frames); +v4l2_ctrl_cluster (5 , &cxhdl->stream_type); +v4l2_ctrl_cluster (2 , &cxhdl->video_mute); +v4l2_ctrl_cluster (3 , &cxhdl->video_spatial_filter_mode); +v4l2_ctrl_cluster (2 , &cxhdl->video_luma_spatial_filter_type); +v4l2_ctrl_cluster (2 , &cxhdl->video_spatial_filter); +v4l2_ctrl_cluster (4 , &cxhdl->video_luma_median_filter_top); + +return 0 ; +} +EXPORT_SYMBOL (cx2341x_handler_init); + +void cx2341x_handler_set_50hz (struct cx2341x_handler *cxhdl,int is_50hz) +{ + cxhdl->is_50hz = is_50hz; + cxhdl->video_gop_size->default_value = cxhdl->is_50hz ?12 :15 ; +} +EXPORT_SYMBOL (cx2341x_handler_set_50hz); + +int cx2341x_handler_setup (struct cx2341x_handler *cxhdl) +{ +int h = cxhdl->height; +int w = cxhdl->width; +int err; + + err =cx2341x_hdl_api (cxhdl,CX2341X_ENC_SET_OUTPUT_PORT ,2 , cxhdl->port,0 ); +if (err) +return err; + err =cx2341x_hdl_api (cxhdl,CX2341X_ENC_SET_FRAME_RATE ,1 , cxhdl->is_50hz); +if (err) +return err; + +if (v4l2_ctrl_g (cxhdl->video_encoding) ==V4L2_MPEG_VIDEO_ENCODING_MPEG_1 ) { + w /=2 ; + h /=2 ; + } + err =cx2341x_hdl_api (cxhdl,CX2341X_ENC_SET_FRAME_SIZE ,2 , h, w); +if (err) +return err; +return v4l2_ctrl_handler_setup (&cxhdl->hdl); +} +EXPORT_SYMBOL (cx2341x_handler_setup); + +void cx2341x_handler_set_busy (struct cx2341x_handler *cxhdl,int busy) +{ +v4l2_ctrl_grab (cxhdl->audio_sampling_freq, busy); +v4l2_ctrl_grab (cxhdl->audio_encoding, busy); +v4l2_ctrl_grab (cxhdl->audio_l2_bitrate, busy); +v4l2_ctrl_grab (cxhdl->audio_ac3_bitrate, busy); +v4l2_ctrl_grab (cxhdl->stream_vbi_fmt, busy); +v4l2_ctrl_grab (cxhdl->stream_type, busy); +v4l2_ctrl_grab (cxhdl->video_bitrate_mode, busy); +v4l2_ctrl_grab (cxhdl->video_bitrate, busy); +v4l2_ctrl_grab (cxhdl->video_bitrate_peak, busy); +} +EXPORT_SYMBOL (cx2341x_handler_set_busy);
include/media/cx2341x.h +81 -0
93 lines not on this page · raw message
Signature · 1 line
1.6.4.2
RFC PATCH 11/15 wm8775: convert to the new control framework
In reply to Hans Verkuil--- drivers/media/video/wm8775.c | 79 ++++++++++++++++++++++++++--------------- 1 files changed, 50 insertions(+), 29 deletions(-)
drivers/media/video/wm8775.c +50 -29
@@ -34,6 +34,7 @@#include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION ("wm8775 driver" );
@@ -52,8 +53,9 @@ enum {struct wm8775_state {struct v4l2_subdev sd; +struct v4l2_ctrl_handler hdl; +struct v4l2_ctrl *mute;u8 input;/* Last selected input (0-0xf) */ -u8 muted; };static inline struct wm8775_state *to_state (struct v4l2_subdev *sd)
@@ -61,6 +63,11 @@ static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct wm8775_state, sd); } +static inline struct v4l2_subdev *to_sd (struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd; +} +static int wm8775_write (struct v4l2_subdev *sd,int reg,u16 val) {struct i2c_client *client =v4l2_get_subdevdata (sd);
@@ -94,7 +101,7 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,return -EINVAL ; } state->input = input; -if (state->muted) +if (!v4l2_ctrl_g (state->mute))return 0 ;wm8775_write (sd,R21 ,0x0c0 );wm8775_write (sd,R14 ,0x1d4 );
@@ -103,29 +110,21 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,return 0 ; } -static int wm8775_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int wm8775_s_ctrl (struct v4l2_ctrl *ctrl) { +struct v4l2_subdev *sd =to_sd (ctrl);struct wm8775_state *state =to_state (sd); -if (ctrl->id !=V4L2_CID_AUDIO_MUTE ) -return -EINVAL ; - ctrl->value = state->muted; -return 0 ; -} - -static int wm8775_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) -{ -struct wm8775_state *state =to_state (sd); - -if (ctrl->id !=V4L2_CID_AUDIO_MUTE ) -return -EINVAL ; - state->muted = ctrl->value; -wm8775_write (sd,R21 ,0x0c0 ); -wm8775_write (sd,R14 ,0x1d4 ); -wm8775_write (sd,R15 ,0x1d4 ); -if (!state->muted) -wm8775_write (sd,R21 ,0x100 + state->input); -return 0 ; +switch (ctrl->id) { +case V4L2_CID_AUDIO_MUTE : +wm8775_write (sd,R21 ,0x0c0 ); +wm8775_write (sd,R14 ,0x1d4 ); +wm8775_write (sd,R15 ,0x1d4 ); +if (!ctrl->val) +wm8775_write (sd,R21 ,0x100 + state->input); +return 0 ; + } +return -EINVAL ; }static int wm8775_g_chip_ident (struct v4l2_subdev *sd,struct v4l2_dbg_chip_ident *chip)
@@ -139,8 +138,8 @@ static int wm8775_log_status(struct v4l2_subdev *sd) {struct wm8775_state *state =to_state (sd); -v4l2_info (sd,"Input: %d%s\n" , state->input, - state->muted ?" (muted)" :"" ); +v4l2_info (sd,"Input: %d\n" , state->input); +v4l2_ctrl_handler_log_status (&state->hdl, sd->name);return 0 ; }
@@ -161,11 +160,20 @@ static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fre/* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops wm8775_ctrl_ops = { + .s_ctrl = wm8775_s_ctrl, +}; +static const struct v4l2_subdev_core_ops wm8775_core_ops = { .log_status = wm8775_log_status, .g_chip_ident = wm8775_g_chip_ident, - .g_ctrl = wm8775_g_ctrl, - .s_ctrl = wm8775_s_ctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, };static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
@@ -204,13 +212,24 @@ static int wm8775_probe(struct i2c_client *client,v4l_info (client,"chip found @ 0x%02x (%s)\n" , client->addr <<1 , client->adapter->name); - state =kmalloc (sizeof (struct wm8775_state ),GFP_KERNEL ); + state =kzalloc (sizeof (struct wm8775_state ),GFP_KERNEL );if (state ==NULL )return -ENOMEM ; sd = &state->sd;v4l2_i2c_subdev_init (sd, client, &wm8775_ops); state->input =2 ; - state->muted =0 ; + +v4l2_ctrl_handler_init (&state->hdl,1 ); + state->mute =v4l2_ctrl_new_std (&state->hdl, &wm8775_ctrl_ops, +V4L2_CID_AUDIO_MUTE ,0 ,1 ,1 ,0 ); + sd->ctrl_handler = &state->hdl; +if (state->hdl.error) { +int err = state->hdl.error; + +v4l2_ctrl_handler_free (&state->hdl); +kfree (state); +return err; + }/* Initialize wm8775 */
@@ -247,9 +266,11 @@ static int wm8775_probe(struct i2c_client *client,static int wm8775_remove (struct i2c_client *client) {struct v4l2_subdev *sd =i2c_get_clientdata (client); +struct wm8775_state *state =to_state (sd);v4l2_device_unregister_subdev (sd); -kfree (to_state (sd)); +v4l2_ctrl_handler_free (&state->hdl); +kfree (state);return 0 ; }
Signature · 1 line
1.6.4.2
RFC PATCH 12/15 cs53l32a: convert to new control framework.
In reply to Hans Verkuil--- drivers/media/video/cs53l32a.c | 107 +++++++++++++++++++++++++-------------- 1 files changed, 68 insertions(+), 39 deletions(-)
drivers/media/video/cs53l32a.c +68 -39
@@ -25,10 +25,10 @@#include <linux/ioctl.h> #include <asm/uaccess.h> #include <linux/i2c.h> -#include <linux/i2c-id.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION ("i2c device driver for cs53l32a Audio ADC" );
@@ -42,6 +42,21 @@ module_param(debug, bool, 0644);MODULE_PARM_DESC (debug,"Debugging messages, 0=Off (default), 1=On" ); +struct cs53l32a_state { +struct v4l2_subdev sd; +struct v4l2_ctrl_handler hdl; +}; + +static inline struct cs53l32a_state *to_state (struct v4l2_subdev *sd) +{ + return container_of(sd, struct cs53l32a_state, sd); +} + +static inline struct v4l2_subdev *to_sd (struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd; +} +/* ----------------------------------------------------------------------- */ static int cs53l32a_write (struct v4l2_subdev *sd,u8 reg,u8 value)
@@ -73,31 +88,20 @@ static int cs53l32a_s_routing(struct v4l2_subdev *sd,return 0 ; } -static int cs53l32a_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int cs53l32a_s_ctrl (struct v4l2_ctrl *ctrl) { -if (ctrl->id ==V4L2_CID_AUDIO_MUTE ) { - ctrl->value = (cs53l32a_read (sd,0x03 ) &0xc0 ) !=0 ; -return 0 ; - } -if (ctrl->id !=V4L2_CID_AUDIO_VOLUME ) -return -EINVAL ; - ctrl->value = (s8 )cs53l32a_read (sd,0x04 ); -return 0 ; -} +struct v4l2_subdev *sd =to_sd (ctrl); -static int cs53l32a_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) -{ -if (ctrl->id ==V4L2_CID_AUDIO_MUTE ) { -cs53l32a_write (sd,0x03 , ctrl->value ?0xf0 :0x30 ); +switch (ctrl->id) { +case V4L2_CID_AUDIO_MUTE : +cs53l32a_write (sd,0x03 , ctrl->val ?0xf0 :0x30 ); +return 0 ; +case V4L2_CID_AUDIO_VOLUME : +cs53l32a_write (sd,0x04 , (u8 )ctrl->val); +cs53l32a_write (sd,0x05 , (u8 )ctrl->val);return 0 ; } -if (ctrl->id !=V4L2_CID_AUDIO_VOLUME ) -return -EINVAL ; -if (ctrl->value >12 || ctrl->value <-96 ) -return -EINVAL ; -cs53l32a_write (sd,0x04 , (u8 ) ctrl->value); -cs53l32a_write (sd,0x05 , (u8 ) ctrl->value); -return 0 ; +return -EINVAL ; }static int cs53l32a_g_chip_ident (struct v4l2_subdev *sd,struct v4l2_dbg_chip_ident *chip)
@@ -110,23 +114,30 @@ static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_idstatic int cs53l32a_log_status (struct v4l2_subdev *sd) { +struct cs53l32a_state *state =to_state (sd);u8 v =cs53l32a_read (sd,0x01 ); -u8 m =cs53l32a_read (sd,0x03 ); -s8 vol =cs53l32a_read (sd,0x04 ); -v4l2_info (sd,"Input: %d%s\n" , (v >>4 ) &3 , - (m &0xC0 ) ?" (muted)" :"" ); -v4l2_info (sd,"Volume: %d dB\n" , vol); +v4l2_info (sd,"Input: %d\n" , (v >>4 ) &3 ); +v4l2_ctrl_handler_log_status (&state->hdl, sd->name);return 0 ; }/* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = { + .s_ctrl = cs53l32a_s_ctrl, +}; +static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { .log_status = cs53l32a_log_status, .g_chip_ident = cs53l32a_g_chip_ident, - .g_ctrl = cs53l32a_g_ctrl, - .s_ctrl = cs53l32a_s_ctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, };static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
@@ -150,6 +161,7 @@ static const struct v4l2_subdev_ops cs53l32a_ops = {static int cs53l32a_probe (struct i2c_client *client,const struct i2c_device_id *id) { +struct cs53l32a_state *state;struct v4l2_subdev *sd;int i;
@@ -163,9 +175,10 @@ static int cs53l32a_probe(struct i2c_client *client,v4l_info (client,"chip found @ 0x%x (%s)\n" , client->addr <<1 , client->adapter->name); - sd =kmalloc (sizeof (struct v4l2_subdev ),GFP_KERNEL ); -if (sd ==NULL ) + state =kzalloc (sizeof (struct cs53l32a_state ),GFP_KERNEL ); +if (state ==NULL )return -ENOMEM ; + sd = &state->sd;v4l2_i2c_subdev_init (sd, client, &cs53l32a_ops);for (i =1 ; i <=7 ; i++) {
@@ -174,15 +187,29 @@ static int cs53l32a_probe(struct i2c_client *client,v4l2_dbg (1 , debug, sd,"Read Reg %d %02x\n" , i, v); } +v4l2_ctrl_handler_init (&state->hdl,2 ); +v4l2_ctrl_new_std (&state->hdl, &cs53l32a_ctrl_ops, +V4L2_CID_AUDIO_VOLUME ,-96 ,12 ,1 ,0 ); +v4l2_ctrl_new_std (&state->hdl, &cs53l32a_ctrl_ops, +V4L2_CID_AUDIO_MUTE ,0 ,1 ,1 ,0 ); + sd->ctrl_handler = &state->hdl; +if (state->hdl.error) { +int err = state->hdl.error; + +v4l2_ctrl_handler_free (&state->hdl); +kfree (state); +return err; + } +/* Set cs53l32a internal register for Adaptec 2010/2410 setup */ -cs53l32a_write (sd,0x01 , (u8 )0x21 ); -cs53l32a_write (sd,0x02 , (u8 )0x29 ); -cs53l32a_write (sd,0x03 , (u8 )0x30 ); -cs53l32a_write (sd,0x04 , (u8 )0x00 ); -cs53l32a_write (sd,0x05 , (u8 )0x00 ); -cs53l32a_write (sd,0x06 , (u8 )0x00 ); -cs53l32a_write (sd,0x07 , (u8 )0x00 ); +cs53l32a_write (sd,0x01 ,0x21 ); +cs53l32a_write (sd,0x02 ,0x29 ); +cs53l32a_write (sd,0x03 ,0x30 ); +cs53l32a_write (sd,0x04 ,0x00 ); +cs53l32a_write (sd,0x05 ,0x00 ); +cs53l32a_write (sd,0x06 ,0x00 ); +cs53l32a_write (sd,0x07 ,0x00 );/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
@@ -197,9 +224,11 @@ static int cs53l32a_probe(struct i2c_client *client,static int cs53l32a_remove (struct i2c_client *client) {struct v4l2_subdev *sd =i2c_get_clientdata (client); +struct cs53l32a_state *state =to_state (sd);v4l2_device_unregister_subdev (sd); -kfree (sd); +v4l2_ctrl_handler_free (&state->hdl); +kfree (state);return 0 ; }
Signature · 1 line
1.6.4.2
RFC PATCH 13/15 wm8739: convert to the new control framework
In reply to Hans Verkuil--- drivers/media/video/wm8739.c | 176 ++++++++++++++---------------------------- 1 files changed, 59 insertions(+), 117 deletions(-)
drivers/media/video/wm8739.c +59 -117
@@ -26,11 +26,11 @@#include <linux/ioctl.h> #include <asm/uaccess.h> #include <linux/i2c.h> -#include <linux/i2c-id.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> +#include <media/v4l2-ctrls.h> MODULE_DESCRIPTION ("wm8739 driver" );MODULE_AUTHOR ("T. Adachi, Hans Verkuil" );
@@ -53,12 +53,11 @@ enum {struct wm8739_state {struct v4l2_subdev sd; +struct v4l2_ctrl_handler hdl; +struct v4l2_ctrl *volume; +struct v4l2_ctrl *mute; +struct v4l2_ctrl *balance;u32 clock_freq; -u8 muted; -u16 volume; -u16 balance; -u8 vol_l;/* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ -u8 vol_r;/* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ };static inline struct wm8739_state *to_state (struct v4l2_subdev *sd)
@@ -66,6 +65,11 @@ static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct wm8739_state, sd); } +static inline struct v4l2_subdev *to_sd (struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd; +} +/* ------------------------------------------------------------------------ */ static int wm8739_write (struct v4l2_subdev *sd,int reg,u16 val)
@@ -88,58 +92,17 @@ static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)return -1 ; } -/* write regs to set audio volume etc */ -static void wm8739_set_audio (struct v4l2_subdev *sd) -{ -struct wm8739_state *state =to_state (sd); -u16 mute = state->muted ?0x80 :0 ; - -/* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB -* Default setting: 0x17 = 0 dB -*/ -wm8739_write (sd,R0 , (state->vol_l &0x1f ) | mute); -wm8739_write (sd,R1 , (state->vol_r &0x1f ) | mute); -} - -static int wm8739_g_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) -{ -struct wm8739_state *state =to_state (sd); - -switch (ctrl->id) { -case V4L2_CID_AUDIO_MUTE : - ctrl->value = state->muted; -break ; - -case V4L2_CID_AUDIO_VOLUME : - ctrl->value = state->volume; -break ; - -case V4L2_CID_AUDIO_BALANCE : - ctrl->value = state->balance; -break ; - -default : -return -EINVAL ; - } -return 0 ; -} - -static int wm8739_s_ctrl (struct v4l2_subdev *sd,struct v4l2_control *ctrl) +static int wm8739_s_ctrl (struct v4l2_ctrl *ctrl) { +struct v4l2_subdev *sd =to_sd (ctrl);struct wm8739_state *state =to_state (sd);unsigned int work_l, work_r; +u8 vol_l;/* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ +u8 vol_r;/* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ +u16 mute;switch (ctrl->id) { -case V4L2_CID_AUDIO_MUTE : - state->muted = ctrl->value; -break ; -case V4L2_CID_AUDIO_VOLUME : - state->volume = ctrl->value; -break ; - -case V4L2_CID_AUDIO_BALANCE : - state->balance = ctrl->value;break ;default :
@@ -147,52 +110,25 @@ static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) }/* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ - work_l = (min (65536 - state->balance,32768 ) * state->volume) /32768 ; - work_r = (min (state->balance, (u16 )32768 ) * state->volume) /32768 ; + work_l = (min (65536 - state->balance->val,32768 ) * state->volume->val) /32768 ; + work_r = (min (state->balance->val,32768 ) * state->volume->val) /32768 ; - state->vol_l = (long )work_l *31 /65535 ; - state->vol_r = (long )work_r *31 /65535 ; + vol_l = (long )work_l *31 /65535 ; + vol_r = (long )work_r *31 /65535 ;/* set audio volume etc. */ -wm8739_set_audio (sd); + mute = state->mute->val ?0x80 :0 ; + +/* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB +* Default setting: 0x17 = 0 dB +*/ +wm8739_write (sd,R0 , (vol_l &0x1f ) | mute); +wm8739_write (sd,R1 , (vol_r &0x1f ) | mute);return 0 ; }/* ------------------------------------------------------------------------ */ -static struct v4l2_queryctrl wm8739_qctrl[] = { - { - .id =V4L2_CID_AUDIO_VOLUME , - .name ="Volume" , - .minimum =0 , - .maximum =65535 , - .step =65535 /100 , - .default_value =58880 , - .flags =0 , - .type =V4L2_CTRL_TYPE_INTEGER , - }, { - .id =V4L2_CID_AUDIO_MUTE , - .name ="Mute" , - .minimum =0 , - .maximum =1 , - .step =1 , - .default_value =1 , - .flags =0 , - .type =V4L2_CTRL_TYPE_BOOLEAN , - }, { - .id =V4L2_CID_AUDIO_BALANCE , - .name ="Balance" , - .minimum =0 , - .maximum =65535 , - .step =65535 /100 , - .default_value =32768 , - .flags =0 , - .type =V4L2_CTRL_TYPE_INTEGER , - } -}; - -/* ------------------------------------------------------------------------ */ -static int wm8739_s_clock_freq (struct v4l2_subdev *sd,u32 audiofreq) {struct wm8739_state *state =to_state (sd);
@@ -221,18 +157,6 @@ static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)return 0 ; } -static int wm8739_queryctrl (struct v4l2_subdev *sd,struct v4l2_queryctrl *qc) -{ -int i; - -for (i =0 ; i <ARRAY_SIZE (wm8739_qctrl); i++) -if (qc->id && qc->id == wm8739_qctrl[i].id) { -memcpy (qc, &wm8739_qctrl[i],sizeof (*qc)); -return 0 ; - } -return -EINVAL ; -} -static int wm8739_g_chip_ident (struct v4l2_subdev *sd,struct v4l2_dbg_chip_ident *chip) {struct i2c_client *client =v4l2_get_subdevdata (sd);
@@ -245,21 +169,26 @@ static int wm8739_log_status(struct v4l2_subdev *sd)struct wm8739_state *state =to_state (sd);v4l2_info (sd,"Frequency: %u Hz\n" , state->clock_freq); -v4l2_info (sd,"Volume L: %02x%s\n" , state->vol_l &0x1f , - state->muted ?" (muted)" :"" ); -v4l2_info (sd,"Volume R: %02x%s\n" , state->vol_r &0x1f , - state->muted ?" (muted)" :"" ); +v4l2_ctrl_handler_log_status (&state->hdl, sd->name);return 0 ; }/* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops wm8739_ctrl_ops = { + .s_ctrl = wm8739_s_ctrl, +}; +static const struct v4l2_subdev_core_ops wm8739_core_ops = { .log_status = wm8739_log_status, .g_chip_ident = wm8739_g_chip_ident, - .queryctrl = wm8739_queryctrl, - .g_ctrl = wm8739_g_ctrl, - .s_ctrl = wm8739_s_ctrl, + .g_ext_ctrls = v4l2_sd_g_ext_ctrls, + .try_ext_ctrls = v4l2_sd_try_ext_ctrls, + .s_ext_ctrls = v4l2_sd_s_ext_ctrls, + .g_ctrl = v4l2_sd_g_ctrl, + .s_ctrl = v4l2_sd_s_ctrl, + .queryctrl = v4l2_sd_queryctrl, + .querymenu = v4l2_sd_querymenu, };static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
@@ -288,17 +217,28 @@ static int wm8739_probe(struct i2c_client *client,v4l_info (client,"chip found @ 0x%x (%s)\n" , client->addr <<1 , client->adapter->name); - state =kmalloc (sizeof (struct wm8739_state ),GFP_KERNEL ); + state =kzalloc (sizeof (struct wm8739_state ),GFP_KERNEL );if (state ==NULL )return -ENOMEM ; sd = &state->sd;v4l2_i2c_subdev_init (sd, client, &wm8739_ops); - state->vol_l =0x17 ;/* 0dB */ - state->vol_r =0x17 ;/* 0dB */ - state->muted =0 ; - state->balance =32768 ; -/* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */ - state->volume = ((long )state->vol_l +1 ) *65535 /31 ; +v4l2_ctrl_handler_init (&state->hdl,2 ); + state->volume =v4l2_ctrl_new_std (&state->hdl, &wm8739_ctrl_ops, +V4L2_CID_AUDIO_VOLUME ,0 ,65535 ,65535 /100 ,50736 ); + state->mute =v4l2_ctrl_new_std (&state->hdl, &wm8739_ctrl_ops, +V4L2_CID_AUDIO_MUTE ,0 ,1 ,1 ,0 ); + state->balance =v4l2_ctrl_new_std (&state->hdl, &wm8739_ctrl_ops, +V4L2_CID_AUDIO_BALANCE ,0 ,65535 ,65535 /100 ,32768 ); + sd->ctrl_handler = &state->hdl; +if (state->hdl.error) { +int err = state->hdl.error; + +v4l2_ctrl_handler_free (&state->hdl); +kfree (state); +return err; + } +v4l2_ctrl_cluster (3 , &state->volume); + state->clock_freq =48000 ;/* Initialize wm8739 */
@@ -317,15 +257,17 @@ static int wm8739_probe(struct i2c_client *client,/* activate */ wm8739_write (sd,R9 ,0x001 );/* set volume/mute */ -wm8739_set_audio (sd); +v4l2_ctrl_handler_setup (&state->hdl);return 0 ; }static int wm8739_remove (struct i2c_client *client) {struct v4l2_subdev *sd =i2c_get_clientdata (client); +struct wm8739_state *state =to_state (sd);v4l2_device_unregister_subdev (sd); +v4l2_ctrl_handler_free (&state->hdl);kfree (to_state (sd));return 0 ; }
Signature · 1 line
1.6.4.2
RFC PATCH 14/15 ivtv: convert gpio subdev to new control framework.
In reply to Hans Verkuil--- drivers/media/video/ivtv/ivtv-driver.c | 1 + drivers/media/video/ivtv/ivtv-driver.h | 1 + drivers/media/video/ivtv/ivtv-gpio.c | 77 +++++++++++++++----------------- 3 files changed, 38 insertions(+), 41 deletions(-)
drivers/media/video/ivtv/ivtv-driver.c +1 -0
@@ -1370,6 +1370,7 @@ static void ivtv_remove(struct pci_dev *pdev)printk (KERN_INFO "ivtv: Removed %s\n" , itv->card_name);v4l2_device_unregister (&itv->v4l2_dev); +v4l2_ctrl_handler_free (&itv->hdl_gpio);kfree (itv); }
drivers/media/video/ivtv/ivtv-driver.h +1 -0
@@ -622,6 +622,7 @@ struct ivtv {struct v4l2_device v4l2_dev;struct v4l2_subdev sd_gpio;/* GPIO sub-device */ +struct v4l2_ctrl_handler hdl_gpio;u16 instance;/* High-level state info */
drivers/media/video/ivtv/ivtv-gpio.c +36 -41
120 lines not on this page · raw message
Signature · 1 line
1.6.4.2
RFC PATCH 15/15 ivtv: convert to the new control framework
In reply to Hans Verkuil--- drivers/media/video/ivtv/ivtv-controls.c | 275 ++++-------------------------- drivers/media/video/ivtv/ivtv-controls.h | 6 +- drivers/media/video/ivtv/ivtv-driver.c | 18 ++- drivers/media/video/ivtv/ivtv-driver.h | 5 +- drivers/media/video/ivtv/ivtv-fileops.c | 23 +-- drivers/media/video/ivtv/ivtv-firmware.c | 6 +- drivers/media/video/ivtv/ivtv-ioctl.c | 31 ++-- drivers/media/video/ivtv/ivtv-streams.c | 20 ++- 8 files changed, 86 insertions(+), 298 deletions(-)
drivers/media/video/ivtv/ivtv-controls.c +32 -243
@@ -17,162 +17,14 @@along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <linux/kernel.h> #include "ivtv-driver.h" -#include "ivtv-cards.h" #include "ivtv-ioctl.h" -#include "ivtv-routing.h" -#include "ivtv-i2c.h" -#include "ivtv-mailbox.h" #include "ivtv-controls.h" -/* Must be sorted from low to high control ID! */ -static const u32 user_ctrls[] = { -V4L2_CID_USER_CLASS , -V4L2_CID_BRIGHTNESS , -V4L2_CID_CONTRAST , -V4L2_CID_SATURATION , -V4L2_CID_HUE , -V4L2_CID_AUDIO_VOLUME , -V4L2_CID_AUDIO_BALANCE , -V4L2_CID_AUDIO_BASS , -V4L2_CID_AUDIO_TREBLE , -V4L2_CID_AUDIO_MUTE , -V4L2_CID_AUDIO_LOUDNESS , -0 -}; - -static const u32 *ctrl_classes[] = { - user_ctrls, - cx2341x_mpeg_ctrls, -NULL -}; - - -int ivtv_queryctrl (struct file *file,void *fh,struct v4l2_queryctrl *qctrl) -{ -struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; -const char *name; - - qctrl->id =v4l2_ctrl_next (ctrl_classes, qctrl->id); -if (qctrl->id ==0 ) -return -EINVAL ; - -switch (qctrl->id) { -/* Standard V4L2 controls */ -case V4L2_CID_USER_CLASS : -return v4l2_ctrl_query_fill (qctrl,0 ,0 ,0 ,0 ); -case V4L2_CID_BRIGHTNESS : -case V4L2_CID_HUE : -case V4L2_CID_SATURATION : -case V4L2_CID_CONTRAST : -if (v4l2_subdev_call (itv->sd_video, core, queryctrl, qctrl)) - qctrl->flags |=V4L2_CTRL_FLAG_DISABLED ; -return 0 ; - -case V4L2_CID_AUDIO_VOLUME : -case V4L2_CID_AUDIO_MUTE : -case V4L2_CID_AUDIO_BALANCE : -case V4L2_CID_AUDIO_BASS : -case V4L2_CID_AUDIO_TREBLE : -case V4L2_CID_AUDIO_LOUDNESS : -if (v4l2_subdev_call (itv->sd_audio, core, queryctrl, qctrl)) - qctrl->flags |=V4L2_CTRL_FLAG_DISABLED ; -return 0 ; - -default : -if (cx2341x_ctrl_query (&itv->params, qctrl)) - qctrl->flags |=V4L2_CTRL_FLAG_DISABLED ; -return 0 ; - } -strncpy (qctrl->name, name,sizeof (qctrl->name) -1 ); - qctrl->name[sizeof (qctrl->name) -1 ] =0 ; -return 0 ; -} - -int ivtv_querymenu (struct file *file,void *fh,struct v4l2_querymenu *qmenu) -{ -struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; -struct v4l2_queryctrl qctrl; - - qctrl.id = qmenu->id; -ivtv_queryctrl (file, fh, &qctrl); -return v4l2_ctrl_query_menu (qmenu, &qctrl, -cx2341x_ctrl_get_menu (&itv->params, qmenu->id)); -} - -static int ivtv_try_ctrl (struct file *file,void *fh, -struct v4l2_ext_control *vctrl) -{ -struct v4l2_queryctrl qctrl; -const char **menu_items =NULL ; -int err; - - qctrl.id = vctrl->id; - err =ivtv_queryctrl (file, fh, &qctrl); -if (err) -return err; -if (qctrl.type ==V4L2_CTRL_TYPE_MENU ) - menu_items =v4l2_ctrl_get_menu (qctrl.id); -return v4l2_ctrl_check (vctrl, &qctrl, menu_items); -} - -static int ivtv_s_ctrl (struct ivtv *itv,struct v4l2_control *vctrl) -{ -switch (vctrl->id) { -/* Standard V4L2 controls */ -case V4L2_CID_BRIGHTNESS : -case V4L2_CID_HUE : -case V4L2_CID_SATURATION : -case V4L2_CID_CONTRAST : -return v4l2_subdev_call (itv->sd_video, core, s_ctrl, vctrl); - -case V4L2_CID_AUDIO_VOLUME : -case V4L2_CID_AUDIO_MUTE : -case V4L2_CID_AUDIO_BALANCE : -case V4L2_CID_AUDIO_BASS : -case V4L2_CID_AUDIO_TREBLE : -case V4L2_CID_AUDIO_LOUDNESS : -return v4l2_subdev_call (itv->sd_audio, core, s_ctrl, vctrl); - -default : -IVTV_DEBUG_IOCTL ("invalid control 0x%x\n" , vctrl->id); -return -EINVAL ; - } -return 0 ; -} - -static int ivtv_g_ctrl (struct ivtv *itv,struct v4l2_control *vctrl) +static int ivtv_s_stream_vbi_fmt (struct cx2341x_handler *cxhdl,u32 fmt) { -switch (vctrl->id) { -/* Standard V4L2 controls */ -case V4L2_CID_BRIGHTNESS : -case V4L2_CID_HUE : -case V4L2_CID_SATURATION : -case V4L2_CID_CONTRAST : -return v4l2_subdev_call (itv->sd_video, core, g_ctrl, vctrl); - -case V4L2_CID_AUDIO_VOLUME : -case V4L2_CID_AUDIO_MUTE : -case V4L2_CID_AUDIO_BALANCE : -case V4L2_CID_AUDIO_BASS : -case V4L2_CID_AUDIO_TREBLE : -case V4L2_CID_AUDIO_LOUDNESS : -return v4l2_subdev_call (itv->sd_audio, core, g_ctrl, vctrl); -default : -IVTV_DEBUG_IOCTL ("invalid control 0x%x\n" , vctrl->id); -return -EINVAL ; - } -return 0 ; -} - -static int ivtv_setup_vbi_fmt (struct ivtv *itv,enum v4l2_mpeg_stream_vbi_fmt fmt) -{ -if (!(itv->v4l2_cap &V4L2_CAP_SLICED_VBI_CAPTURE )) -return -EINVAL ; -if (atomic_read (&itv->capturing) >0 ) -return -EBUSY ; + struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);/* First try to allocate sliced VBI buffers if needed. */ if (fmt && itv->vbi.sliced_mpeg_data[0 ] ==NULL ) {
@@ -207,106 +59,43 @@ static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmreturn 0 ; } -int ivtv_g_ext_ctrls (struct file *file,void *fh,struct v4l2_ext_controls *c) +static int ivtv_s_video_encoding (struct cx2341x_handler *cxhdl,u32 val) { -struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; -struct v4l2_control ctrl; - -if (c->ctrl_class ==V4L2_CTRL_CLASS_USER ) { -int i; -int err =0 ; - -for (i =0 ; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err =ivtv_g_ctrl (itv, &ctrl); - c->controls[i].value = ctrl.value; -if (err) { - c->error_idx = i; -break ; - } - } -return err; - } -if (c->ctrl_class ==V4L2_CTRL_CLASS_MPEG ) -return cx2341x_ext_ctrls (&itv->params,0 , c,VIDIOC_G_EXT_CTRLS ); -return -EINVAL ; + struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); +int is_mpeg1 = val ==V4L2_MPEG_VIDEO_ENCODING_MPEG_1 ; +struct v4l2_format fmt; + +/* fix videodecoder resolution */ + fmt.type =V4L2_BUF_TYPE_VIDEO_CAPTURE ; + fmt.fmt.pix.width = cxhdl->width / (is_mpeg1 ?2 :1 ); + fmt.fmt.pix.height = cxhdl->height; +v4l2_subdev_call (itv->sd_video, video, s_fmt, &fmt); +return 0 ; } -int ivtv_s_ext_ctrls (struct file *file,void *fh,struct v4l2_ext_controls *c) +static int ivtv_s_audio_sampling_freq (struct cx2341x_handler *cxhdl,u32 idx) { -struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; -struct v4l2_control ctrl; - -if (c->ctrl_class ==V4L2_CTRL_CLASS_USER ) { -int i; -int err =0 ; - -for (i =0 ; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err =ivtv_s_ctrl (itv, &ctrl); - c->controls[i].value = ctrl.value; -if (err) { - c->error_idx = i; -break ; - } - } -return err; - } -if (c->ctrl_class ==V4L2_CTRL_CLASS_MPEG ) { -static u32 freqs[3 ] = {44100 ,48000 ,32000 }; -struct cx2341x_mpeg_params p = itv->params; -int err =cx2341x_ext_ctrls (&p,atomic_read (&itv->capturing), c,VIDIOC_S_EXT_CTRLS ); -unsigned idx; - -if (err) -return err; +static const u32 freqs[3 ] = {44100 ,48000 ,32000 }; + struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); -if (p.video_encoding != itv->params.video_encoding) { -int is_mpeg1 = p.video_encoding == -V4L2_MPEG_VIDEO_ENCODING_MPEG_1 ; -struct v4l2_format fmt; - -/* fix videodecoder resolution */ - fmt.type =V4L2_BUF_TYPE_VIDEO_CAPTURE ; - fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ?2 :1 ); - fmt.fmt.pix.height = itv->params.height; -v4l2_subdev_call (itv->sd_video, video, s_fmt, &fmt); - } - err =cx2341x_update (itv, ivtv_api_func, &itv->params, &p); -if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) - err =ivtv_setup_vbi_fmt (itv, p.stream_vbi_fmt); - itv->params = p; - itv->dualwatch_stereo_mode = p.audio_properties &0x0300 ; - idx = p.audio_properties &0x03 ; -/* The audio clock of the digitizer must match the codec sample -rate otherwise you get some very strange effects. */ -if (idx <ARRAY_SIZE (freqs)) -ivtv_call_all (itv, audio, s_clock_freq, freqs[idx]); -return err; - } -return -EINVAL ; +/* The audio clock of the digitizer must match the codec sample +rate otherwise you get some very strange effects. */ +if (idx <ARRAY_SIZE (freqs)) +ivtv_call_all (itv, audio, s_clock_freq, freqs[idx]); +return 0 ; } -int ivtv_try_ext_ctrls (struct file *file,void *fh,struct v4l2_ext_controls *c) +static int ivtv_s_audio_mode (struct cx2341x_handler *cxhdl,u32 val) { -struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); -if (c->ctrl_class ==V4L2_CTRL_CLASS_USER ) { -int i; -int err =0 ; - -for (i =0 ; i < c->count; i++) { - err =ivtv_try_ctrl (file, fh, &c->controls[i]); -if (err) { - c->error_idx = i; -break ; - } - } -return err; - } -if (c->ctrl_class ==V4L2_CTRL_CLASS_MPEG ) -return cx2341x_ext_ctrls (&itv->params,atomic_read (&itv->capturing), c,VIDIOC_TRY_EXT_CTRLS ); -return -EINVAL ; + itv->dualwatch_stereo_mode = val; +return 0 ; } + +struct cx2341x_handler_ops ivtv_cxhdl_ops = { + .s_audio_mode = ivtv_s_audio_mode, + .s_audio_sampling_freq = ivtv_s_audio_sampling_freq, + .s_video_encoding = ivtv_s_video_encoding, + .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt, +};
drivers/media/video/ivtv/ivtv-controls.h +1 -5
@@ -21,10 +21,6 @@#ifndef IVTV_CONTROLS_H #define IVTV_CONTROLS_H -int ivtv_queryctrl (struct file *file,void *fh,struct v4l2_queryctrl *a); -int ivtv_g_ext_ctrls (struct file *file,void *fh,struct v4l2_ext_controls *a); -int ivtv_s_ext_ctrls (struct file *file,void *fh,struct v4l2_ext_controls *a); -int ivtv_try_ext_ctrls (struct file *file,void *fh,struct v4l2_ext_controls *a); -int ivtv_querymenu (struct file *file,void *fh,struct v4l2_querymenu *a); +extern struct cx2341x_handler_ops ivtv_cxhdl_ops;#endif
drivers/media/video/ivtv/ivtv-driver.c +13 -5
@@ -53,6 +53,7 @@#include "ivtv-cards.h" #include "ivtv-vbi.h" #include "ivtv-routing.h" +#include "ivtv-controls.h" #include "ivtv-gpio.h" #include <media/tveeprom.h>
@@ -718,9 +719,8 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) itv->open_id =1 ;/* Initial settings */ -cx2341x_fill_defaults (&itv->params); - itv->params.port =CX2341X_PORT_MEMORY ; - itv->params.capabilities =CX2341X_CAP_HAS_SLICED_VBI ; + itv->cxhdl.port =CX2341X_PORT_MEMORY ; + itv->cxhdl.capabilities =CX2341X_CAP_HAS_SLICED_VBI ;init_waitqueue_head (&itv->eos_waitq);init_waitqueue_head (&itv->event_waitq);init_waitqueue_head (&itv->vsync_waitq);
@@ -990,6 +990,13 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, retval = -ENOMEM ; goto err; } + retval =cx2341x_handler_init (&itv->cxhdl,50 ); +if (retval) + goto err; + itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl; + itv->cxhdl.ops = &ivtv_cxhdl_ops; + itv->cxhdl.priv = itv; + itv->cxhdl.func = ivtv_api_func;IVTV_DEBUG_INFO ("base addr: 0x%08x\n" , itv->base_addr);
@@ -1111,7 +1118,7 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w; itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h; - itv->params.video_gop_size = itv->is_60hz ?15 :12 ; +cx2341x_handler_set_50hz (&itv->cxhdl, itv->is_50hz); itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG ] =0x08000 ; itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM ] =0x01200 ;
@@ -1306,6 +1313,8 @@ int ivtv_init_on_first_open(struct ivtv *itv)/* For cards with video out, this call needs interrupts enabled */ ivtv_s_std (NULL , &fh, &itv->tuner_std); +/* Setup initial controls */ +cx2341x_handler_setup (&itv->cxhdl);return 0 ; }
@@ -1370,7 +1379,6 @@ static void ivtv_remove(struct pci_dev *pdev)printk (KERN_INFO "ivtv: Removed %s\n" , itv->card_name);v4l2_device_unregister (&itv->v4l2_dev); -v4l2_ctrl_handler_free (&itv->hdl_gpio);kfree (itv); }
drivers/media/video/ivtv/ivtv-driver.h +3 -2
@@ -62,6 +62,7 @@#include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/tuner.h> #include <media/cx2341x.h> #include <media/ir-kbd-i2c.h>
@@ -621,8 +622,9 @@ struct ivtv {struct ivtv_options options;/* user options */ struct v4l2_device v4l2_dev; -struct v4l2_subdev sd_gpio;/* GPIO sub-device */ +struct cx2341x_handler cxhdl;struct v4l2_ctrl_handler hdl_gpio; +struct v4l2_subdev sd_gpio;/* GPIO sub-device */ u16 instance;/* High-level state info */
@@ -639,7 +641,6 @@ struct ivtv {v4l2_std_id std_out;/* current TV output standard */ u8 audio_stereo_mode;/* decoder setting how to handle stereo MPEG audio */ u8 audio_bilingual_mode;/* decoder setting how to handle bilingual MPEG audio */ -struct cx2341x_mpeg_params params;/* current encoder parameters */ /* Locking */
drivers/media/video/ivtv/ivtv-fileops.c +8 -15
@@ -148,12 +148,10 @@ void ivtv_release_stream(struct ivtv_stream *s)static void ivtv_dualwatch (struct ivtv *itv) {struct v4l2_tuner vt; -u32 new_bitmap;u32 new_stereo_mode; -const u32 stereo_mask =0x0300 ; -const u32 dual =0x0200 ; +const u32 dual =0x02 ; - new_stereo_mode = itv->params.audio_properties & stereo_mask; + new_stereo_mode =v4l2_ctrl_g (itv->cxhdl.audio_mode);memset (&vt,0 ,sizeof (vt));ivtv_call_all (itv, tuner, g_tuner, &vt);if (vt.audmode ==V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans &V4L2_TUNER_SUB_LANG2 ))
@@ -162,16 +160,10 @@ static void ivtv_dualwatch(struct ivtv *itv)if (new_stereo_mode == itv->dualwatch_stereo_mode)return ; - new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask); - -IVTV_DEBUG_INFO ("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n" , - itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); - -if (ivtv_vapi (itv,CX2341X_ENC_SET_AUDIO_PROPERTIES ,1 , new_bitmap) ==0 ) { - itv->dualwatch_stereo_mode = new_stereo_mode; -return ; - } -IVTV_DEBUG_INFO ("dualwatch: changing stereo flag failed\n" ); +IVTV_DEBUG_INFO ("dualwatch: change stereo flag from 0x%x to 0x%x.\n" , + itv->dualwatch_stereo_mode, new_stereo_mode); +if (v4l2_ctrl_s (itv->cxhdl.audio_mode, new_stereo_mode)) +IVTV_DEBUG_INFO ("dualwatch: changing stereo flag failed\n" ); }static void ivtv_update_pgm_info (struct ivtv *itv)
@@ -867,7 +859,8 @@ int ivtv_v4l2_close(struct file *filp)if (atomic_read (&itv->capturing) >0 ) {/* Undo video mute */ ivtv_vapi (itv,CX2341X_ENC_MUTE_VIDEO ,1 , - itv->params.video_mute | (itv->params.video_mute_yuv <<8 )); +v4l2_ctrl_g (itv->cxhdl.video_mute) | + (v4l2_ctrl_g (itv->cxhdl.video_mute_yuv) <<8 )); }/* Done! Unmute and continue. */ ivtv_unmute (itv);
drivers/media/video/ivtv/ivtv-firmware.c +3 -3
@@ -245,9 +245,9 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv)volatile u8 __iomem *mem_offset; data[0 ] =0 ; - data[1 ] = itv->params.width;/* YUV source width */ - data[2 ] = itv->params.height; - data[3 ] = itv->params.audio_properties;/* Audio settings to use, + data[1 ] = itv->cxhdl.width;/* YUV source width */ + data[2 ] = itv->cxhdl.height; + data[3 ] = itv->cxhdl.audio_properties;/* Audio settings to use, bitmap. see docs. */ if (ivtv_api (itv,CX2341X_DEC_SET_DECODER_SOURCE ,4 , data)) {IVTV_ERR ("ivtv_init_mpeg_decoder failed to set decoder source\n" );
drivers/media/video/ivtv/ivtv-ioctl.c +13 -18
@@ -161,7 +161,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed) data[0 ] |= (speed >1000 || speed <-1500 ) ?0x40000000 :0 ; data[1 ] = (speed <0 ); data[2 ] = speed <0 ?3 :7 ; - data[3 ] = itv->params.video_b_frames; + data[3 ] =v4l2_ctrl_g (itv->cxhdl.video_b_frames); data[4 ] = (speed ==1500 || speed ==500 ) ? itv->speed_mute_audio :0 ; data[5 ] =0 ; data[6 ] =0 ;
@@ -338,8 +338,8 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fstruct ivtv *itv = id->itv;struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - pixfmt->width = itv->params.width; - pixfmt->height = itv->params.height; + pixfmt->width = itv->cxhdl.width; + pixfmt->height = itv->cxhdl.height; pixfmt->colorspace =V4L2_COLORSPACE_SMPTE170M ; pixfmt->field =V4L2_FIELD_INTERLACED ; pixfmt->priv =0 ;
@@ -567,7 +567,6 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f {struct ivtv_open_id *id = fh;struct ivtv *itv = id->itv; -struct cx2341x_mpeg_params *p = &itv->params;int ret =ivtv_try_fmt_vid_cap (file, fh, fmt);int w = fmt->fmt.pix.width;int h = fmt->fmt.pix.height;
@@ -575,15 +574,15 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fif (ret)return ret; -if (p->width == w && p->height == h) +if (itv->cxhdl.width == w && itv->cxhdl.height == h)return 0 ;if (atomic_read (&itv->capturing) >0 )return -EBUSY ; - p->width = w; - p->height = h; -if (p->video_encoding ==V4L2_MPEG_VIDEO_ENCODING_MPEG_1 ) + itv->cxhdl.width = w; + itv->cxhdl.height = h; +if (v4l2_ctrl_g (itv->cxhdl.video_encoding) ==V4L2_MPEG_VIDEO_ENCODING_MPEG_1 ) fmt->fmt.pix.width /=2 ;v4l2_subdev_call (itv->sd_video, video, s_fmt, fmt);return ivtv_g_fmt_vid_cap (file, fh, fmt);
@@ -1109,9 +1108,10 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) itv->std = *std; itv->is_60hz = (*std &V4L2_STD_525_60 ) ?1 :0 ; - itv->params.is_50hz = itv->is_50hz = !itv->is_60hz; - itv->params.width =720 ; - itv->params.height = itv->is_50hz ?576 :480 ; + itv->is_50hz = !itv->is_60hz; +cx2341x_handler_set_50hz (&itv->cxhdl, itv->is_50hz); + itv->cxhdl.width =720 ; + itv->cxhdl.height = itv->is_50hz ?576 :480 ; itv->vbi.count = itv->is_50hz ?18 :12 ; itv->vbi.start[0 ] = itv->is_50hz ?6 :10 ; itv->vbi.start[1 ] = itv->is_50hz ?318 :273 ;
@@ -1152,7 +1152,7 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)ivtv_vapi (itv,CX2341X_DEC_SET_STANDARD ,1 , itv->is_out_50hz); itv->main_rect.left = itv->main_rect.top =0 ; itv->main_rect.width =720 ; - itv->main_rect.height = itv->params.height; + itv->main_rect.height = itv->cxhdl.height;ivtv_vapi (itv,CX2341X_OSD_SET_FRAMEBUFFER_WINDOW ,4 ,720 , itv->main_rect.height,0 ,0 ); yi->main_rect = itv->main_rect;
@@ -1537,7 +1537,7 @@ static int ivtv_log_status(struct file *file, void *fh) }IVTV_INFO ("Tuner: %s\n" ,test_bit (IVTV_F_I_RADIO_USER , &itv->i_flags) ?"Radio" :"TV" ); -cx2341x_log_status (&itv->params, itv->v4l2_dev.name); +v4l2_ctrl_handler_log_status (&itv->cxhdl.hdl, itv->v4l2_dev.name);IVTV_INFO ("Status flags: 0x%08lx\n" , itv->i_flags);for (i =0 ; i <IVTV_MAX_STREAMS ; i++) {struct ivtv_stream *s = &itv->streams[i];
@@ -1921,11 +1921,6 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { .vidioc_s_register = ivtv_s_register, #endif .vidioc_default = ivtv_default, - .vidioc_queryctrl = ivtv_queryctrl, - .vidioc_querymenu = ivtv_querymenu, - .vidioc_g_ext_ctrls = ivtv_g_ext_ctrls, - .vidioc_s_ext_ctrls = ivtv_s_ext_ctrls, - .vidioc_try_ext_ctrls = ivtv_try_ext_ctrls, };void ivtv_set_funcs (struct video_device *vdev)
drivers/media/video/ivtv/ivtv-streams.c +13 -7
@@ -208,6 +208,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->num = num; s->vdev->v4l2_dev = &itv->v4l2_dev; + s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler; s->vdev->fops = ivtv_stream_info[type].fops; s->vdev->release = video_device_release; s->vdev->tvnorms =V4L2_STD_ALL ;
@@ -446,7 +447,6 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) {u32 data[CX2341X_MBOX_MAX_DATA ];struct ivtv *itv = s->itv; -struct cx2341x_mpeg_params *p = &itv->params;int captype =0 , subtype =0 ;int enable_passthrough =0 ;
@@ -467,7 +467,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) } itv->mpg_data_received = itv->vbi_data_inserted =0 ; itv->dualwatch_jiffies = jiffies; - itv->dualwatch_stereo_mode = p->audio_properties &0x0300 ; + itv->dualwatch_stereo_mode =v4l2_ctrl_g (itv->cxhdl.audio_mode); itv->search_pack_header =0 ;break ;
@@ -555,12 +555,12 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) itv->pgm_info_offset, itv->pgm_info_num);/* Setup API for Stream */ -cx2341x_update (itv, ivtv_api_func,NULL , p); +cx2341x_handler_setup (&itv->cxhdl);/* mute if capturing radio */ if (test_bit (IVTV_F_I_RADIO_USER , &itv->i_flags))ivtv_vapi (itv,CX2341X_ENC_MUTE_VIDEO ,1 , -1 | (p->video_mute_yuv <<8 )); +1 | (v4l2_ctrl_g (itv->cxhdl.video_mute_yuv) <<8 )); }/* Vsync Setup */
@@ -576,6 +576,8 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)clear_bit (IVTV_F_I_EOS , &itv->i_flags); +cx2341x_handler_set_busy (&itv->cxhdl,1 ); +/* Initialize Digitizer for Capture */ /* Avoid tinny audio problem - ensure audio clocks are going */ v4l2_subdev_call (itv->sd_audio, audio, s_stream,1 );
@@ -612,7 +614,6 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) {u32 data[CX2341X_MBOX_MAX_DATA ];struct ivtv *itv = s->itv; -struct cx2341x_mpeg_params *p = &itv->params;int datatype;if (s->vdev ==NULL )
@@ -651,7 +652,7 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)break ; }if (ivtv_vapi (itv,CX2341X_DEC_SET_DECODER_SOURCE ,4 , datatype, - p->width, p->height, p->audio_properties)) { + itv->cxhdl.width, itv->cxhdl.height, itv->cxhdl.audio_properties)) {IVTV_DEBUG_WARN ("Couldn't initialize decoder source\n" ); }return 0 ;
@@ -817,6 +818,8 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)return 0 ; } +cx2341x_handler_set_busy (&itv->cxhdl,0 ); +/* Set the following Interrupt mask bits for capture */ ivtv_set_irq_mask (itv,IVTV_IRQ_MASK_CAPTURE );del_timer (&itv->dma_timer);
@@ -930,7 +933,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)/* Setup capture if not already done */ if (atomic_read (&itv->capturing) ==0 ) { -cx2341x_update (itv, ivtv_api_func,NULL , &itv->params); +cx2341x_handler_setup (&itv->cxhdl); +cx2341x_handler_set_busy (&itv->cxhdl,1 ); }/* Start Passthrough Mode */
@@ -951,6 +955,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)clear_bit (IVTV_F_S_PASSTHROUGH , &dec_stream->s_flags);clear_bit (IVTV_F_S_STREAMING , &dec_stream->s_flags); itv->output_mode =OUT_NONE ; +if (atomic_read (&itv->capturing) ==0 ) +cx2341x_handler_set_busy (&itv->cxhdl,0 );return 0 ; }
Signature · 1 line
1.6.4.2