* [PATCH 01/15] [RFCv2] v4l2: Add new control handling framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
@ 2010-05-16 13:20 ` Hans Verkuil
2010-05-16 13:20 ` [PATCH 02/15] [RFCv2] v4l2-ctrls: reorder 'case' statements to match order in header Hans Verkuil
` (14 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:20 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
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.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/media/video/Makefile | 2 +-
drivers/media/video/v4l2-common.c | 479 +----------
drivers/media/video/v4l2-ctrls.c | 1840 +++++++++++++++++++++++++++++++++++++
include/media/v4l2-ctrls.h | 448 +++++++++
include/media/v4l2-dev.h | 4 +
include/media/v4l2-device.h | 4 +
include/media/v4l2-subdev.h | 3 +
7 files changed, 2305 insertions(+), 475 deletions(-)
create mode 100644 drivers/media/video/v4l2-ctrls.c
create mode 100644 include/media/v4l2-ctrls.h
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index cc93859..2ea34a6 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -11,7 +11,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 v4l2-fh.o \
- v4l2-event.o
+ v4l2-event.o v4l2-ctrls.o
# V4L2 core modules
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 4e53b0b..3ce7c64 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -62,6 +62,7 @@
#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
@@ -172,487 +173,17 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
}
EXPORT_SYMBOL(v4l2_ctrl_check);
-/* Returns NULL or a character pointer array containing the menu for
- the given control ID. The pointer array ends with a NULL pointer.
- An empty string signifies a menu entry that is invalid. This allows
- drivers to disable certain options if it is not supported. */
-const char **v4l2_ctrl_get_menu(u32 id)
-{
- static const char *mpeg_audio_sampling_freq[] = {
- "44.1 kHz",
- "48 kHz",
- "32 kHz",
- NULL
- };
- static const char *mpeg_audio_encoding[] = {
- "MPEG-1/2 Layer I",
- "MPEG-1/2 Layer II",
- "MPEG-1/2 Layer III",
- "MPEG-2/4 AAC",
- "AC-3",
- NULL
- };
- static const char *mpeg_audio_l1_bitrate[] = {
- "32 kbps",
- "64 kbps",
- "96 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "288 kbps",
- "320 kbps",
- "352 kbps",
- "384 kbps",
- "416 kbps",
- "448 kbps",
- NULL
- };
- static const char *mpeg_audio_l2_bitrate[] = {
- "32 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- "384 kbps",
- NULL
- };
- static const char *mpeg_audio_l3_bitrate[] = {
- "32 kbps",
- "40 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- NULL
- };
- static const char *mpeg_audio_ac3_bitrate[] = {
- "32 kbps",
- "40 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- "384 kbps",
- "448 kbps",
- "512 kbps",
- "576 kbps",
- "640 kbps",
- NULL
- };
- static const char *mpeg_audio_mode[] = {
- "Stereo",
- "Joint Stereo",
- "Dual",
- "Mono",
- NULL
- };
- static const char *mpeg_audio_mode_extension[] = {
- "Bound 4",
- "Bound 8",
- "Bound 12",
- "Bound 16",
- NULL
- };
- static const char *mpeg_audio_emphasis[] = {
- "No Emphasis",
- "50/15 us",
- "CCITT J17",
- NULL
- };
- static const char *mpeg_audio_crc[] = {
- "No CRC",
- "16-bit CRC",
- NULL
- };
- static const char *mpeg_video_encoding[] = {
- "MPEG-1",
- "MPEG-2",
- "MPEG-4 AVC",
- NULL
- };
- static const char *mpeg_video_aspect[] = {
- "1x1",
- "4x3",
- "16x9",
- "2.21x1",
- NULL
- };
- static const char *mpeg_video_bitrate_mode[] = {
- "Variable Bitrate",
- "Constant Bitrate",
- NULL
- };
- static const char *mpeg_stream_type[] = {
- "MPEG-2 Program Stream",
- "MPEG-2 Transport Stream",
- "MPEG-1 System Stream",
- "MPEG-2 DVD-compatible Stream",
- "MPEG-1 VCD-compatible Stream",
- "MPEG-2 SVCD-compatible Stream",
- NULL
- };
- static const char *mpeg_stream_vbi_fmt[] = {
- "No VBI",
- "Private packet, IVTV format",
- NULL
- };
- static const char *camera_power_line_frequency[] = {
- "Disabled",
- "50 Hz",
- "60 Hz",
- NULL
- };
- static const char *camera_exposure_auto[] = {
- "Auto Mode",
- "Manual Mode",
- "Shutter Priority Mode",
- "Aperture Priority Mode",
- NULL
- };
- static const char *colorfx[] = {
- "None",
- "Black & White",
- "Sepia",
- "Negative",
- "Emboss",
- "Sketch",
- "Sky blue",
- "Grass green",
- "Skin whiten",
- "Vivid",
- NULL
- };
- static const char *tune_preemphasis[] = {
- "No preemphasis",
- "50 useconds",
- "75 useconds",
- NULL,
- };
-
- switch (id) {
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- return mpeg_audio_sampling_freq;
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- return mpeg_audio_encoding;
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
- return mpeg_audio_l1_bitrate;
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- return mpeg_audio_l2_bitrate;
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
- return mpeg_audio_l3_bitrate;
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- return mpeg_audio_ac3_bitrate;
- case V4L2_CID_MPEG_AUDIO_MODE:
- return mpeg_audio_mode;
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
- return mpeg_audio_mode_extension;
- case V4L2_CID_MPEG_AUDIO_EMPHASIS:
- return mpeg_audio_emphasis;
- case V4L2_CID_MPEG_AUDIO_CRC:
- return mpeg_audio_crc;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return mpeg_video_encoding;
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- return mpeg_video_aspect;
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- return mpeg_video_bitrate_mode;
- case V4L2_CID_MPEG_STREAM_TYPE:
- return mpeg_stream_type;
- case V4L2_CID_MPEG_STREAM_VBI_FMT:
- return mpeg_stream_vbi_fmt;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- return camera_power_line_frequency;
- case V4L2_CID_EXPOSURE_AUTO:
- return camera_exposure_auto;
- case V4L2_CID_COLORFX:
- return colorfx;
- case V4L2_CID_TUNE_PREEMPHASIS:
- return tune_preemphasis;
- default:
- return NULL;
- }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_menu);
-
-/* Return the control name. */
-const char *v4l2_ctrl_get_name(u32 id)
-{
- switch (id) {
- /* USER controls */
- case V4L2_CID_USER_CLASS: return "User Controls";
- case V4L2_CID_BRIGHTNESS: return "Brightness";
- case V4L2_CID_CONTRAST: return "Contrast";
- case V4L2_CID_SATURATION: return "Saturation";
- case V4L2_CID_HUE: return "Hue";
- case V4L2_CID_AUDIO_VOLUME: return "Volume";
- case V4L2_CID_AUDIO_BALANCE: return "Balance";
- case V4L2_CID_AUDIO_BASS: return "Bass";
- case V4L2_CID_AUDIO_TREBLE: return "Treble";
- case V4L2_CID_AUDIO_MUTE: return "Mute";
- case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
- case V4L2_CID_BLACK_LEVEL: return "Black Level";
- case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
- case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
- case V4L2_CID_RED_BALANCE: return "Red Balance";
- case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
- case V4L2_CID_GAMMA: return "Gamma";
- case V4L2_CID_EXPOSURE: return "Exposure";
- case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
- case V4L2_CID_GAIN: return "Gain";
- case V4L2_CID_HFLIP: return "Horizontal Flip";
- case V4L2_CID_VFLIP: return "Vertical Flip";
- case V4L2_CID_HCENTER: return "Horizontal Center";
- case V4L2_CID_VCENTER: return "Vertical Center";
- case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
- case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
- case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
- 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";
-
- /* MPEG controls */
- case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
- 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_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";
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
- case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
- case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
- 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 */
- 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";
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
- case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
- case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
- case V4L2_CID_PAN_RESET: return "Pan, Reset";
- case V4L2_CID_TILT_RESET: return "Tilt, Reset";
- case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
- case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
- 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";
-
- /* FM Radio Modulator control */
- 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";
- case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
- case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
- case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
- case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
- case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
- case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
- case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled";
- case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
- case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
- case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
- case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
- case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
- case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
- case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
- case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings";
- case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
-
- default:
- return NULL;
- }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_name);
-
/* Fill in a struct v4l2_queryctrl */
int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
{
- const char *name = v4l2_ctrl_get_name(qctrl->id);
+ const char *name;
+
+ v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
+ &min, &max, &step, &def, &qctrl->flags);
- qctrl->flags = 0;
if (name == NULL)
return -EINVAL;
- switch (qctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_LOUDNESS:
- case V4L2_CID_AUTO_WHITE_BALANCE:
- case V4L2_CID_AUTOGAIN:
- case V4L2_CID_HFLIP:
- case V4L2_CID_VFLIP:
- case V4L2_CID_HUE_AUTO:
- case V4L2_CID_CHROMA_AGC:
- case V4L2_CID_COLOR_KILLER:
- case V4L2_CID_MPEG_AUDIO_MUTE:
- case V4L2_CID_MPEG_VIDEO_MUTE:
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
- case V4L2_CID_MPEG_VIDEO_PULLDOWN:
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
- case V4L2_CID_FOCUS_AUTO:
- case V4L2_CID_PRIVACY:
- case V4L2_CID_AUDIO_LIMITER_ENABLED:
- case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
- case V4L2_CID_PILOT_TONE_ENABLED:
- qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
- min = 0;
- max = step = 1;
- break;
- case V4L2_CID_PAN_RESET:
- case V4L2_CID_TILT_RESET:
- qctrl->type = V4L2_CTRL_TYPE_BUTTON;
- qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
- min = max = step = def = 0;
- break;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- case V4L2_CID_MPEG_AUDIO_MODE:
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
- case V4L2_CID_MPEG_AUDIO_EMPHASIS:
- case V4L2_CID_MPEG_AUDIO_CRC:
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- case V4L2_CID_MPEG_STREAM_TYPE:
- case V4L2_CID_MPEG_STREAM_VBI_FMT:
- case V4L2_CID_EXPOSURE_AUTO:
- case V4L2_CID_COLORFX:
- case V4L2_CID_TUNE_PREEMPHASIS:
- qctrl->type = V4L2_CTRL_TYPE_MENU;
- step = 1;
- break;
- case V4L2_CID_RDS_TX_PS_NAME:
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- qctrl->type = V4L2_CTRL_TYPE_STRING;
- break;
- case V4L2_CID_USER_CLASS:
- case V4L2_CID_CAMERA_CLASS:
- case V4L2_CID_MPEG_CLASS:
- case V4L2_CID_FM_TX_CLASS:
- qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
- qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- min = max = step = def = 0;
- break;
- case V4L2_CID_BG_COLOR:
- qctrl->type = V4L2_CTRL_TYPE_INTEGER;
- step = 1;
- min = 0;
- /* Max is calculated as RGB888 that is 2^24 */
- max = 0xFFFFFF;
- break;
- default:
- qctrl->type = V4L2_CTRL_TYPE_INTEGER;
- break;
- }
- switch (qctrl->id) {
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- case V4L2_CID_MPEG_AUDIO_MODE:
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- case V4L2_CID_MPEG_VIDEO_B_FRAMES:
- case V4L2_CID_MPEG_STREAM_TYPE:
- qctrl->flags |= V4L2_CTRL_FLAG_UPDATE;
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- case V4L2_CID_RED_BALANCE:
- case V4L2_CID_BLUE_BALANCE:
- case V4L2_CID_GAMMA:
- case V4L2_CID_SHARPNESS:
- case V4L2_CID_CHROMA_GAIN:
- case V4L2_CID_RDS_TX_DEVIATION:
- case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
- case V4L2_CID_AUDIO_LIMITER_DEVIATION:
- case V4L2_CID_AUDIO_COMPRESSION_GAIN:
- case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
- case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
- case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
- case V4L2_CID_PILOT_TONE_DEVIATION:
- case V4L2_CID_PILOT_TONE_FREQUENCY:
- case V4L2_CID_TUNE_POWER_LEVEL:
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
- qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- break;
- case V4L2_CID_PAN_RELATIVE:
- case V4L2_CID_TILT_RELATIVE:
- case V4L2_CID_FOCUS_RELATIVE:
- case V4L2_CID_IRIS_RELATIVE:
- case V4L2_CID_ZOOM_RELATIVE:
- qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
- break;
- }
qctrl->minimum = min;
qctrl->maximum = max;
qctrl->step = step;
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
new file mode 100644
index 0000000..6c97ff0
--- /dev/null
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -0,0 +1,1840 @@
+/*
+ V4L2 controls framework implementation.
+
+ Copyright (C) 2010 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/ctype.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+
+/* Internal temporary helper struct, one for each v4l2_ext_control */
+struct ctrl_helper {
+ /* The control corresponding to the v4l2_ext_control ID field. */
+ struct v4l2_ctrl *ctrl;
+ /* Used internally to mark whether this control was already
+ processed. */
+ bool handled;
+};
+
+/* Returns NULL or a character pointer array containing the menu for
+ the given control ID. The pointer array ends with a NULL pointer.
+ An empty string signifies a menu entry that is invalid. This allows
+ drivers to disable certain options if it is not supported. */
+const char **v4l2_ctrl_get_menu(u32 id)
+{
+ static const char *mpeg_audio_sampling_freq[] = {
+ "44.1 kHz",
+ "48 kHz",
+ "32 kHz",
+ NULL
+ };
+ static const char *mpeg_audio_encoding[] = {
+ "MPEG-1/2 Layer I",
+ "MPEG-1/2 Layer II",
+ "MPEG-1/2 Layer III",
+ "MPEG-2/4 AAC",
+ "AC-3",
+ NULL
+ };
+ static const char *mpeg_audio_l1_bitrate[] = {
+ "32 kbps",
+ "64 kbps",
+ "96 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "288 kbps",
+ "320 kbps",
+ "352 kbps",
+ "384 kbps",
+ "416 kbps",
+ "448 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_l2_bitrate[] = {
+ "32 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_l3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_ac3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ "448 kbps",
+ "512 kbps",
+ "576 kbps",
+ "640 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_mode[] = {
+ "Stereo",
+ "Joint Stereo",
+ "Dual",
+ "Mono",
+ NULL
+ };
+ static const char *mpeg_audio_mode_extension[] = {
+ "Bound 4",
+ "Bound 8",
+ "Bound 12",
+ "Bound 16",
+ NULL
+ };
+ static const char *mpeg_audio_emphasis[] = {
+ "No Emphasis",
+ "50/15 us",
+ "CCITT J17",
+ NULL
+ };
+ static const char *mpeg_audio_crc[] = {
+ "No CRC",
+ "16-bit CRC",
+ NULL
+ };
+ static const char *mpeg_video_encoding[] = {
+ "MPEG-1",
+ "MPEG-2",
+ "MPEG-4 AVC",
+ NULL
+ };
+ static const char *mpeg_video_aspect[] = {
+ "1x1",
+ "4x3",
+ "16x9",
+ "2.21x1",
+ NULL
+ };
+ static const char *mpeg_video_bitrate_mode[] = {
+ "Variable Bitrate",
+ "Constant Bitrate",
+ NULL
+ };
+ static const char *mpeg_stream_type[] = {
+ "MPEG-2 Program Stream",
+ "MPEG-2 Transport Stream",
+ "MPEG-1 System Stream",
+ "MPEG-2 DVD-compatible Stream",
+ "MPEG-1 VCD-compatible Stream",
+ "MPEG-2 SVCD-compatible Stream",
+ NULL
+ };
+ static const char *mpeg_stream_vbi_fmt[] = {
+ "No VBI",
+ "Private packet, IVTV format",
+ NULL
+ };
+ static const char *camera_power_line_frequency[] = {
+ "Disabled",
+ "50 Hz",
+ "60 Hz",
+ NULL
+ };
+ static const char *camera_exposure_auto[] = {
+ "Auto Mode",
+ "Manual Mode",
+ "Shutter Priority Mode",
+ "Aperture Priority Mode",
+ NULL
+ };
+ static const char *colorfx[] = {
+ "None",
+ "Black & White",
+ "Sepia",
+ "Negative",
+ "Emboss",
+ "Sketch",
+ "Sky blue",
+ "Grass green",
+ "Skin whiten",
+ "Vivid",
+ NULL
+ };
+ static const char *tune_preemphasis[] = {
+ "No preemphasis",
+ "50 useconds",
+ "75 useconds",
+ NULL,
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ return mpeg_audio_sampling_freq;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return mpeg_audio_encoding;
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ return mpeg_audio_l1_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ return mpeg_audio_l2_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ return mpeg_audio_l3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ return mpeg_audio_ac3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ return mpeg_audio_mode;
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ return mpeg_audio_mode_extension;
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ return mpeg_audio_emphasis;
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ return mpeg_audio_crc;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return mpeg_video_encoding;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return mpeg_video_aspect;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ return mpeg_video_bitrate_mode;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return mpeg_stream_type;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ return mpeg_stream_vbi_fmt;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return camera_power_line_frequency;
+ case V4L2_CID_EXPOSURE_AUTO:
+ return camera_exposure_auto;
+ case V4L2_CID_COLORFX:
+ return colorfx;
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ return tune_preemphasis;
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_menu);
+
+/* Return the control name. */
+const char *v4l2_ctrl_get_name(u32 id)
+{
+ switch (id) {
+ /* USER controls */
+ case V4L2_CID_USER_CLASS: return "User Controls";
+ case V4L2_CID_BRIGHTNESS: return "Brightness";
+ case V4L2_CID_CONTRAST: return "Contrast";
+ case V4L2_CID_SATURATION: return "Saturation";
+ case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_AUDIO_VOLUME: return "Volume";
+ case V4L2_CID_AUDIO_BALANCE: return "Balance";
+ case V4L2_CID_AUDIO_BASS: return "Bass";
+ case V4L2_CID_AUDIO_TREBLE: return "Treble";
+ case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
+ case V4L2_CID_BLACK_LEVEL: return "Black Level";
+ case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
+ case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
+ case V4L2_CID_RED_BALANCE: return "Red Balance";
+ case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
+ case V4L2_CID_GAMMA: return "Gamma";
+ case V4L2_CID_EXPOSURE: return "Exposure";
+ case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
+ case V4L2_CID_GAIN: return "Gain";
+ case V4L2_CID_HFLIP: return "Horizontal Flip";
+ case V4L2_CID_VFLIP: return "Vertical Flip";
+ case V4L2_CID_HCENTER: return "Horizontal Center";
+ case V4L2_CID_VCENTER: return "Vertical Center";
+ case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
+ case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+ 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";
+
+ /* MPEG controls */
+ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
+ 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_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";
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
+ case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
+ 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 */
+ 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";
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
+ case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
+ case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
+ case V4L2_CID_PAN_RESET: return "Pan, Reset";
+ case V4L2_CID_TILT_RESET: return "Tilt, Reset";
+ case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
+ case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
+ 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";
+
+ /* FM Radio Modulator control */
+ 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";
+ case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
+ case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
+ case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
+ case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled";
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
+ case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
+ case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
+ case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
+ case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings";
+ case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_name);
+
+void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+ *name = v4l2_ctrl_get_name(id);
+ *flags = 0;
+
+ switch (id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HUE_AUTO:
+ case V4L2_CID_CHROMA_AGC:
+ case V4L2_CID_COLOR_KILLER:
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+ case V4L2_CID_FOCUS_AUTO:
+ case V4L2_CID_PRIVACY:
+ case V4L2_CID_AUDIO_LIMITER_ENABLED:
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
+ case V4L2_CID_PILOT_TONE_ENABLED:
+ *type = V4L2_CTRL_TYPE_BOOLEAN;
+ *min = 0;
+ *max = *step = 1;
+ break;
+ case V4L2_CID_PAN_RESET:
+ case V4L2_CID_TILT_RESET:
+ *type = V4L2_CTRL_TYPE_BUTTON;
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ case V4L2_CID_EXPOSURE_AUTO:
+ case V4L2_CID_COLORFX:
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ *type = V4L2_CTRL_TYPE_MENU;
+ break;
+ case V4L2_CID_RDS_TX_PS_NAME:
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ *type = V4L2_CTRL_TYPE_STRING;
+ break;
+ case V4L2_CID_USER_CLASS:
+ case V4L2_CID_CAMERA_CLASS:
+ case V4L2_CID_MPEG_CLASS:
+ case V4L2_CID_FM_TX_CLASS:
+ *type = V4L2_CTRL_TYPE_CTRL_CLASS;
+ /* You can neither read not write these */
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_BG_COLOR:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ *step = 1;
+ *min = 0;
+ /* Max is calculated as RGB888 that is 2^24 */
+ *max = 0xFFFFFF;
+ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+ }
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ *flags |= V4L2_CTRL_FLAG_UPDATE;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ case V4L2_CID_GAMMA:
+ case V4L2_CID_SHARPNESS:
+ case V4L2_CID_CHROMA_GAIN:
+ case V4L2_CID_RDS_TX_DEVIATION:
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION:
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
+ case V4L2_CID_PILOT_TONE_DEVIATION:
+ case V4L2_CID_PILOT_TONE_FREQUENCY:
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+ *flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
+ case V4L2_CID_PAN_RELATIVE:
+ case V4L2_CID_TILT_RELATIVE:
+ case V4L2_CID_FOCUS_RELATIVE:
+ case V4L2_CID_IRIS_RELATIVE:
+ case V4L2_CID_ZOOM_RELATIVE:
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ break;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_fill);
+
+/* Helper function to determine whether the control type is compatible with
+ VIDIOC_G/S_CTRL. */
+static bool type_is_int(const struct v4l2_ctrl *ctrl)
+{
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ case V4L2_CTRL_TYPE_STRING:
+ /* Nope, these need v4l2_ext_control */
+ return false;
+ default:
+ return true;
+ }
+}
+
+/* Helper function: copy the current control value back to the caller */
+static int cur_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->cur.string);
+ if (c->size < len + 1) {
+ c->size = len + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->cur.string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->cur.val64;
+ break;
+ default:
+ c->value = ctrl->cur.val;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the caller-provider value as the new control value */
+static int user_to_new(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ int ret;
+ u32 size;
+
+ ctrl->has_new = 1;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = c->value64;
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ size = c->size;
+ if (size == 0)
+ return -ERANGE;
+ if (size > ctrl->maximum + 1)
+ size = ctrl->maximum + 1;
+ ret = copy_from_user(ctrl->string, c->string, size);
+ if (!ret) {
+ char last = ctrl->string[size - 1];
+
+ ctrl->string[size - 1] = 0;
+ /* If the string was longer than ctrl->maximum,
+ then return an error. */
+ if (strlen(ctrl->string) == ctrl->maximum && last)
+ return -ERANGE;
+ }
+ return ret ? -EFAULT : 0;
+ default:
+ ctrl->val = c->value;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the new control value back to the caller */
+static int new_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->string);
+ if (c->size < len + 1) {
+ c->size = ctrl->maximum + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->val64;
+ break;
+ default:
+ c->value = ctrl->val;
+ break;
+ }
+ return 0;
+}
+
+/* Copy the new value to the current value. */
+static void new_to_cur(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ strcpy(ctrl->cur.string, ctrl->string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->cur.val64 = ctrl->val64;
+ break;
+ default:
+ ctrl->cur.val = ctrl->val;
+ break;
+ }
+}
+
+/* Copy the current value to the new value */
+static void cur_to_new(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ strcpy(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = ctrl->cur.val64;
+ break;
+ default:
+ ctrl->val = ctrl->cur.val;
+ break;
+ }
+}
+
+/* Return non-zero if one or more of the controls in the cluster has a new
+ value that differs from the current value. */
+static int cluster_changed(struct v4l2_ctrl *master)
+{
+ int diff = 0;
+ int i;
+
+ for (i = 0; !diff && i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BUTTON:
+ /* Button controls are always 'different' */
+ return 1;
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ diff = strcmp(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ diff = ctrl->val64 != ctrl->cur.val64;
+ break;
+ default:
+ diff = ctrl->val != ctrl->cur.val;
+ break;
+ }
+ }
+ return diff;
+}
+
+/* Validate a new control */
+static int validate_new(struct v4l2_ctrl *ctrl)
+{
+ s32 val = ctrl->val;
+ char *s = ctrl->string;
+ u32 offset;
+ size_t len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ /* Round towards the closest legal value */
+ val += ctrl->step / 2;
+ if (val < ctrl->minimum)
+ val = ctrl->minimum;
+ if (val > ctrl->maximum)
+ val = ctrl->maximum;
+ offset = val - ctrl->minimum;
+ offset = ctrl->step * (offset / ctrl->step);
+ val = ctrl->minimum + offset;
+ ctrl->val = val;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ ctrl->val = !!ctrl->val;
+ return 0;
+
+ case V4L2_CTRL_TYPE_MENU:
+ if (val < ctrl->minimum || val > ctrl->maximum)
+ return -ERANGE;
+ if (ctrl->qmenu[val][0] == '\0' ||
+ (ctrl->menu_skip_mask & (1 << val)))
+ return -EINVAL;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BUTTON:
+ case V4L2_CTRL_TYPE_CTRL_CLASS:
+ ctrl->val64 = 0;
+ return 0;
+
+ case V4L2_CTRL_TYPE_INTEGER64:
+ return 0;
+
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(s);
+ if (len < ctrl->minimum)
+ return -ERANGE;
+ if ((len - ctrl->minimum) % ctrl->step)
+ return -ERANGE;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static inline u32 node2id(struct list_head *node)
+{
+ return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id;
+}
+
+/* Set the handler's error code if it wasn't set earlier already */
+static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
+{
+ if (hdl->error == 0)
+ hdl->error = err;
+ return err;
+}
+
+/* Initialize the handler */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+ unsigned nr_of_controls_hint)
+{
+ mutex_init(&hdl->lock);
+ INIT_LIST_HEAD(&hdl->ctrls);
+ INIT_LIST_HEAD(&hdl->ctrl_refs);
+ hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
+ hdl->buckets = kzalloc(sizeof(hdl->buckets[0]) * hdl->nr_of_buckets,
+ GFP_KERNEL);
+ hdl->error = hdl->buckets ? 0 : -ENOMEM;
+ return hdl->error;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_init);
+
+/* Free all controls and control refs */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl_ref *ref, *next_ref;
+ struct v4l2_ctrl *ctrl, *next_ctrl;
+
+ if (hdl == NULL || hdl->buckets == NULL)
+ return;
+
+ mutex_lock(&hdl->lock);
+ /* Free all nodes */
+ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
+ list_del(&ref->node);
+ kfree(ref);
+ }
+ /* Free all controls owned by the handler */
+ list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
+ list_del(&ctrl->node);
+ kfree(ctrl);
+ }
+ kfree(hdl->buckets);
+ hdl->buckets = NULL;
+ hdl->cached = NULL;
+ hdl->error = 0;
+ mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_free);
+
+/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer
+ be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing
+ with applications that do not use the NEXT_CTRL flag.
+
+ We just find the n-th private user control. It's O(N), but that should not
+ be an issue in this particular case. */
+static struct v4l2_ctrl_ref *find_private_ref(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+
+ id -= V4L2_CID_PRIVATE_BASE;
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ /* Search for private user controls that are compatible with
+ VIDIOC_G/S_CTRL. */
+ if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
+ V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id) &&
+ ref->ctrl->ops->init == NULL &&
+ type_is_int(ref->ctrl)) {
+ if (id == 0)
+ return ref;
+ id--;
+ }
+ }
+ return NULL;
+}
+
+static void v4l2_ctrl_initialize(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int i;
+
+ master->ops->init(master);
+ for (i = 0; i < master->ncontrols; i++)
+ master->cluster[i]->initialized = 1;
+}
+
+/* Find a control with the given ID. */
+static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+ int bucket;
+
+ id &= V4L2_CTRL_ID_MASK;
+
+ /* Old-style private controls need special handling */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return find_private_ref(hdl, id);
+ bucket = id % hdl->nr_of_buckets;
+
+ /* Simple optimization: cache the last control found */
+ if (hdl->cached && hdl->cached->ctrl->id == id)
+ return hdl->cached;
+
+ /* Not in cache, search the hash */
+ ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
+ while (ref && ref->ctrl->id != id)
+ ref = ref->next;
+
+ if (ref) {
+ if (!ref->ctrl->initialized)
+ v4l2_ctrl_initialize(ref->ctrl);
+ hdl->cached = ref; /* cache it! */
+ }
+ return ref;
+}
+
+/* Find a control with the given ID. Take the handler's lock first. */
+static struct v4l2_ctrl_ref *find_ref_lock(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = NULL;
+
+ if (hdl) {
+ mutex_lock(&hdl->lock);
+ ref = find_ref(hdl, id);
+ mutex_unlock(&hdl->lock);
+ }
+ return ref;
+}
+
+/* Find a control with the given ID. */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
+
+ return ref ? ref->ctrl : NULL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_find);
+
+/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
+static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl_ref *new_ref;
+ u32 id = ctrl->id;
+ u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
+ int bucket = id % hdl->nr_of_buckets; /* which bucket to use */
+
+ /* Automatically add the control class if it is not yet present. */
+ if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
+ if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
+ return hdl->error;
+
+ if (hdl->error)
+ return hdl->error;
+
+ new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
+ if (!new_ref)
+ return handler_set_err(hdl, -ENOMEM);
+ new_ref->ctrl = ctrl;
+ if (ctrl->handler == hdl) {
+ /* By default each control starts in a cluster of its own.
+ new_ref->ctrl is basically a cluster array with one
+ element, so that's perfect to use as the cluster pointer.
+ But only do this for the handler that owns the control. */
+ ctrl->cluster = &new_ref->ctrl;
+ ctrl->ncontrols = 1;
+ }
+
+ INIT_LIST_HEAD(&new_ref->node);
+
+ mutex_lock(&hdl->lock);
+
+ /* Add immediately at the end of the list if the list is empty, or if
+ the last element in the list has a lower ID.
+ This ensures that when elements are added in ascending order the
+ insertion is an O(1) operation. */
+ if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
+ list_add_tail(&new_ref->node, &hdl->ctrl_refs);
+ goto insert_in_hash;
+ }
+
+ /* Find insert position in sorted list */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ if (ref->ctrl->id < id)
+ continue;
+ /* Don't add duplicates */
+ if (ref->ctrl->id == id) {
+ kfree(new_ref);
+ goto unlock;
+ }
+ list_add(&new_ref->node, ref->node.prev);
+ break;
+ }
+
+insert_in_hash:
+ /* Insert the control node in the hash */
+ new_ref->next = hdl->buckets[bucket];
+ hdl->buckets[bucket] = new_ref;
+
+unlock:
+ mutex_unlock(&hdl->lock);
+ return 0;
+}
+
+/* Add a new control */
+static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, const char *name, enum v4l2_ctrl_type type,
+ s32 min, s32 max, u32 step, s32 def,
+ u32 flags, const char **qmenu, void *priv)
+{
+ struct v4l2_ctrl *ctrl;
+ unsigned sz_extra = 0;
+
+ if (hdl->error)
+ return NULL;
+
+ /* Sanity checks */
+ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
+ def < min || def > max || max < min ||
+ (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+ (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+
+ if (type == V4L2_CTRL_TYPE_BUTTON)
+ flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ else if (type == V4L2_CTRL_TYPE_STRING)
+ sz_extra += 2 * (max + 1);
+
+ ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -ENOMEM);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&ctrl->node);
+ ctrl->handler = hdl;
+ ctrl->ops = ops;
+ ctrl->id = id;
+ ctrl->name = name;
+ ctrl->type = type;
+ ctrl->flags = flags;
+ ctrl->minimum = min;
+ ctrl->maximum = max;
+ ctrl->step = step;
+ ctrl->qmenu = qmenu;
+ ctrl->priv = priv;
+ ctrl->cur.val = ctrl->val = ctrl->default_value = def;
+ if (!ops || !ops->init)
+ ctrl->initialized = 1;
+
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
+ ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
+ ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
+ if (ctrl->minimum)
+ memset(ctrl->cur.string, ' ', ctrl->minimum);
+ }
+ if (handler_new_ref(hdl, ctrl)) {
+ kfree(ctrl);
+ return NULL;
+ }
+ mutex_lock(&hdl->lock);
+ list_add_tail(&ctrl->node, &hdl->ctrls);
+ mutex_unlock(&hdl->lock);
+ return ctrl;
+}
+
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_config *cfg, void *priv)
+{
+ bool is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU);
+
+ if (is_menu)
+ WARN_ON(cfg->step);
+ else
+ WARN_ON(cfg->menu_skip_mask);
+
+ return v4l2_ctrl_new(hdl, cfg->ops, cfg->id, cfg->name,
+ cfg->type, cfg->min, cfg->max,
+ is_menu ? cfg->menu_skip_mask : cfg->step,
+ cfg->def, cfg->flags, cfg->qmenu, priv);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_custom);
+
+/* Helper function for standard controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 min, s32 max, u32 step, s32 def)
+{
+ const char *name;
+ enum v4l2_ctrl_type type;
+ u32 flags;
+ const char **qmenu = v4l2_ctrl_get_menu(id);
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ min, max, step, def, flags, qmenu, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std);
+
+/* Helper function for standard menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 mask, s32 def)
+{
+ const char **menu = v4l2_ctrl_get_menu(id);
+ s32 items = -1;
+
+ if (menu)
+ while (*menu++)
+ items++;
+ return v4l2_ctrl_new_std(hdl, ops, id, 0, items, mask, def);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
+
+/* Add a control from another handler to this handler */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ if (hdl == NULL || hdl->error)
+ return NULL;
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ if (ctrl->handler == hdl)
+ return ctrl;
+ return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
+
+/* Add the controls from another handler to our own. */
+int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl_handler *add)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret = 0;
+
+ /* Do nothing if either handler is NULL or if they are the same */
+ if (!hdl || !add || hdl == add)
+ return 0;
+ if (hdl->error)
+ return hdl->error;
+ mutex_lock(&add->lock);
+ list_for_each_entry(ctrl, &add->ctrls, node) {
+ /* Skip handler-private controls. */
+ if (ctrl->is_private)
+ continue;
+ ret = handler_new_ref(hdl, ctrl);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&add->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_handler);
+
+/* Cluster controls */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
+{
+ int i;
+
+ /* The first control is the master control and it must not be NULL */
+ BUG_ON(controls[0] == NULL);
+
+ for (i = 0; i < ncontrols; i++) {
+ if (controls[i]) {
+ controls[i]->cluster = controls;
+ controls[i]->ncontrols = ncontrols;
+ }
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_cluster);
+
+/* Activate/deactivate a control. */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
+{
+ if (ctrl && ctrl->initialized) {
+ if (active)
+ ctrl->flags &= ~V4L2_CTRL_FLAG_INACTIVE;
+ else
+ ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_activate);
+
+/* Grab/ungrab a control.
+ Typically used when streaming starts and you want to lock controls,
+ preventing the user from changing them.
+
+ Just call this and the framework will block any attempts to change
+ these controls. */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
+{
+ if (ctrl && ctrl->initialized) {
+ if (grabbed)
+ ctrl->flags |= V4L2_CTRL_FLAG_GRABBED;
+ else
+ ctrl->flags &= ~V4L2_CTRL_FLAG_GRABBED;
+ v4l2_ctrl_unlock(ctrl);
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_grab);
+
+/* Log the control name and value */
+static void log_ctrl(const struct v4l2_ctrl *ctrl,
+ const char *prefix, const char *colon)
+{
+ int fl_inact = ctrl->flags & V4L2_CTRL_FLAG_INACTIVE;
+ int fl_grabbed = ctrl->flags & V4L2_CTRL_FLAG_GRABBED;
+
+ if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
+ return;
+ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return;
+
+ printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ printk(KERN_CONT "%d", ctrl->cur.val);
+ break;
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
+ break;
+ case V4L2_CTRL_TYPE_MENU:
+ printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ printk(KERN_CONT "%lld", ctrl->cur.val64);
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ printk(KERN_CONT "%s", ctrl->cur.string);
+ break;
+ default:
+ printk(KERN_CONT "unknown type %d", ctrl->type);
+ break;
+ }
+ if (fl_inact && fl_grabbed)
+ printk(KERN_CONT " (inactive, grabbed)\n");
+ else if (fl_inact)
+ printk(KERN_CONT " (inactive)\n");
+ else if (fl_grabbed)
+ printk(KERN_CONT " (grabbed)\n");
+ else
+ printk(KERN_CONT "\n");
+}
+
+/* Log all controls owned by the handler */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+ const char *prefix)
+{
+ struct v4l2_ctrl *ctrl;
+ const char *colon = "";
+ int len;
+
+ if (hdl == NULL)
+ return;
+ if (prefix == NULL)
+ prefix = "";
+ len = strlen(prefix);
+ if (len && prefix[len - 1] != ' ')
+ colon = ": ";
+ mutex_lock(&hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node) {
+ if (!ctrl->initialized)
+ v4l2_ctrl_initialize(ctrl);
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
+ log_ctrl(ctrl, prefix, colon);
+ }
+ mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
+
+/* Call s_ctrl for all controls owned by the handler */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret = 0;
+
+ if (hdl == NULL)
+ return 0;
+ mutex_lock(&hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ ctrl->done = false;
+
+ list_for_each_entry(ctrl, &hdl->ctrls, node) {
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int i;
+
+ /* Skip if this control was already handled by a cluster. */
+ if (ctrl->done)
+ continue;
+
+ /* Skip if this control is not yet initialized. */
+ if (!ctrl->initialized)
+ continue;
+
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+
+ /* Skip button controls and read-only controls. */
+ if (ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
+ continue;
+ ret = master->ops->s_ctrl(master);
+ if (ret)
+ break;
+ for (i = 0; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->done = true;
+ }
+ mutex_unlock(&hdl->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
+
+/* Implement VIDIOC_QUERYCTRL */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+{
+ u32 id = qc->id & V4L2_CTRL_ID_MASK;
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl *ctrl;
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ mutex_lock(&hdl->lock);
+
+ /* Try to find it */
+ ref = find_ref(hdl, id);
+
+ if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
+ /* Find the next control with ID > qc->id */
+
+ /* Did we reach the end of the control list? */
+ if (id >= node2id(hdl->ctrl_refs.prev)) {
+ ref = NULL; /* Yes, so there is no next control */
+ } else if (ref) {
+ /* We found a control with the given ID, so just get
+ the next one in the list. */
+ ref = list_entry(ref->node.next, typeof(*ref), node);
+ } else {
+ /* No control with the given ID exists, so start
+ searching for the next largest ID. We know there
+ is one, otherwise the first 'if' above would have
+ been true. */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node)
+ if (id < ref->ctrl->id)
+ break;
+ }
+ }
+ if (ref && !ref->ctrl->initialized)
+ v4l2_ctrl_initialize(ref->ctrl);
+ mutex_unlock(&hdl->lock);
+ if (!ref)
+ return -EINVAL;
+
+ ctrl = ref->ctrl;
+ memset(qc, 0, sizeof(*qc));
+ qc->id = ctrl->id;
+ strlcpy(qc->name, ctrl->name, sizeof(qc->name));
+ qc->minimum = ctrl->minimum;
+ qc->maximum = ctrl->maximum;
+ qc->default_value = ctrl->default_value;
+ if (qc->type == V4L2_CTRL_TYPE_MENU)
+ qc->step = 1;
+ else
+ qc->step = ctrl->step;
+ qc->flags = ctrl->flags;
+ qc->type = ctrl->type;
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_queryctrl);
+
+int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ return v4l2_queryctrl(sd->ctrl_handler, qc);
+}
+EXPORT_SYMBOL(v4l2_subdev_queryctrl);
+
+/* Implement VIDIOC_QUERYMENU */
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
+{
+ struct v4l2_ctrl *ctrl;
+ u32 i = qm->index;
+
+ ctrl = v4l2_ctrl_find(hdl, qm->id);
+ if (!ctrl)
+ return -EINVAL;
+
+ qm->reserved = 0;
+ /* Sanity checks */
+ if (ctrl->qmenu == NULL ||
+ i < ctrl->minimum || i > ctrl->maximum)
+ return -EINVAL;
+ /* Use mask to see if this menu item should be skipped */
+ if (ctrl->menu_skip_mask & (1 << i))
+ return -EINVAL;
+ /* Empty menu items should also be skipped */
+ if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_querymenu);
+
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
+{
+ return v4l2_querymenu(sd->ctrl_handler, qm);
+}
+EXPORT_SYMBOL(v4l2_subdev_querymenu);
+
+
+
+/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
+
+ It is not a fully atomic operation, just best-effort only. After all, if
+ multiple controls have to be set through multiple i2c writes (for example)
+ then some initial writes may succeed while others fail. Thus leaving the
+ system in an inconsistent state. The question is how much effort you are
+ willing to spend on trying to make something atomic that really isn't.
+
+ From the point of view of an application the main requirement is that
+ when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an
+ error should be returned without actually affecting any controls.
+
+ If all the values are correct, then it is acceptable to just give up
+ in case of low-level errors.
+
+ It is important though that the application can tell when only a partial
+ configuration was done. The way we do that is through the error_idx field
+ of struct v4l2_ext_controls: if that is equal to the count field then no
+ controls were affected. Otherwise all controls before that index were
+ successful in performing their 'get' or 'set' operation, the control at
+ the given index failed, and you don't know what happened with the controls
+ after the failed one. Since if they were part of a control cluster they
+ could have been successfully processed (if a cluster member was encountered
+ at index < error_idx), they could have failed (if a cluster member was at
+ error_idx), or they may not have been processed yet (if the first cluster
+ member appeared after error_idx).
+
+ It is all fairly theoretical, though. In practice all you can do is to
+ bail out. If error_idx == count, then it is an application bug. If
+ error_idx < count then it is only an application bug if the error code was
+ EBUSY. That usually means that something started streaming just when you
+ tried to set the controls. In all other cases it is a driver/hardware
+ problem and all you can do is to retry or bail out.
+
+ Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that
+ never modifies controls the error_idx is just set to whatever control
+ has an invalid value.
+ */
+
+/* Prepare for the extended g/s/try functions.
+ Find the controls in the control array and do some basic checks. */
+static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers,
+ bool try)
+{
+ u32 i;
+
+ for (i = 0; i < cs->count; i++) {
+ struct v4l2_ext_control *c = &cs->controls[i];
+ struct v4l2_ctrl *ctrl;
+ u32 id = c->id & V4L2_CTRL_ID_MASK;
+
+ if (try)
+ cs->error_idx = i;
+
+ if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
+ return -EINVAL;
+
+ /* Old-style private controls are not allowed for
+ extended controls */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return -EINVAL;
+ ctrl = v4l2_ctrl_find(hdl, id);
+ if (ctrl == NULL)
+ return -EINVAL;
+ if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+
+ helpers[i].ctrl = ctrl;
+ helpers[i].handled = false;
+ }
+ return 0;
+}
+
+typedef int (*cluster_func)(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl);
+
+/* Walk over all controls in v4l2_ext_controls belonging to the same cluster
+ and call the provided function. */
+static int cluster_walk(unsigned from,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers,
+ cluster_func f)
+{
+ struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
+ int ret = 0;
+ int i;
+
+ /* Find any controls from the same cluster and call the function */
+ for (i = from; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+ if (!helpers[i].handled && ctrl->cluster == cluster)
+ ret = f(&cs->controls[i], ctrl);
+ }
+ return ret;
+}
+
+static void cluster_done(unsigned from,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers)
+{
+ struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
+ int i;
+
+ /* Find any controls from the same cluster and mark them as handled */
+ for (i = from; i < cs->count; i++)
+ if (helpers[i].ctrl->cluster == cluster)
+ helpers[i].handled = true;
+}
+
+/* Handles the corner case where cs->count == 0. It checks whether the
+ specified control class exists. If that class ID is 0, then it checks
+ whether there are any controls at all. */
+static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
+{
+ if (ctrl_class == 0)
+ return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
+ return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
+}
+
+
+
+/* Get extended controls. Allocates the helpers array if needed. */
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ struct ctrl_helper helper[4];
+ struct ctrl_helper *helpers = helper;
+ int ret;
+ int i;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ if (helpers == NULL)
+ return -ENOMEM;
+ }
+
+ ret = prepare_ext_ctrls(hdl, cs, helpers, false);
+
+ for (i = 0; !ret && i < cs->count; i++)
+ if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ ret = -EACCES;
+
+ for (i = 0; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+
+ if (helpers[i].handled)
+ continue;
+
+ cs->error_idx = i;
+
+ v4l2_ctrl_lock(master);
+ /* g_volatile_ctrl will update the current control values */
+ if (master->ops->g_volatile_ctrl)
+ ret = master->ops->g_volatile_ctrl(master);
+ /* If OK, then copy the current control values to the caller */
+ if (!ret)
+ ret = cluster_walk(i, cs, helpers, cur_to_user);
+ v4l2_ctrl_unlock(master);
+ cluster_done(i, cs, helpers);
+ }
+
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_g_ext_ctrls);
+
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls);
+
+/* Helper function to get a single control */
+static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret = 0;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ return -EACCES;
+
+ v4l2_ctrl_lock(master);
+ /* g_volatile_ctrl will update the current control values */
+ if (master->ops->g_volatile_ctrl)
+ ret = master->ops->g_volatile_ctrl(master);
+ *val = ctrl->cur.val;
+ v4l2_ctrl_unlock(master);
+ return ret;
+}
+
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+ return get_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_g_ctrl);
+
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_g_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ctrl);
+
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ s32 val = 0;
+
+ if (!ctrl->initialized)
+ v4l2_ctrl_initialize(ctrl);
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ get_ctrl(ctrl, &val);
+ return val;
+}
+EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
+
+
+/* Core function that calls try/s_ctrl and ensures that the new value is
+ copied to the current value on a set.
+ Must be called with ctrl->handler->lock held. */
+static int try_or_set_control_cluster(struct v4l2_ctrl *master, bool set)
+{
+ bool try = !set;
+ int ret = 0;
+ int i;
+
+ /* Go through the cluster and either validate the new value or
+ (if no new value was set), copy the current value to the new
+ value, ensuring a consistent view for the control ops when
+ called. */
+ for (i = 0; !ret && i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+
+ if (ctrl->has_new) {
+ /* Double check this: it may have changed since the
+ last check in try_or_set_ext_ctrls(). */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+
+ /* Validate if required */
+ if (!set)
+ ret = validate_new(ctrl);
+ continue;
+ }
+ /* No new value was set, so copy the current and force
+ a call to try_ctrl later, since the values for the cluster
+ may now have changed and the end result might be invalid. */
+ try = true;
+ cur_to_new(ctrl);
+ }
+
+ /* For larger clusters you have to call try_ctrl again to
+ verify that the controls are still valid after the
+ 'cur_to_new' above. */
+ if (!ret && master->ops->try_ctrl && try)
+ ret = master->ops->try_ctrl(master);
+
+ /* Don't set if there is no change */
+ if (!ret && set && cluster_changed(master)) {
+ ret = master->ops->s_ctrl(master);
+ /* If OK, then make the new values permanent. */
+ if (!ret)
+ for (i = 0; i < master->ncontrols; i++)
+ new_to_cur(master->cluster[i]);
+ }
+ return ret;
+}
+
+/* Try or set controls. */
+static int try_or_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers,
+ bool set)
+{
+ unsigned i, j;
+ int ret = 0;
+
+ cs->error_idx = cs->count;
+ for (i = 0; i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+ if (!set)
+ cs->error_idx = i;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return -EACCES;
+ /* This test is also done in try_set_control_cluster() which
+ is called in atomic context, so that has the final say,
+ but it makes sense to do an up-front check as well. Once
+ an error occurs in try_set_control_cluster() some other
+ controls may have been set already and we want to do a
+ best-effort to avoid that. */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+ }
+
+ for (i = 0; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+
+ cs->error_idx = i;
+
+ if (helpers[i].handled)
+ continue;
+
+ v4l2_ctrl_lock(ctrl);
+
+ /* Reset the 'has_new' flags of the cluster */
+ for (j = 0; j < master->ncontrols; j++)
+ if (master->cluster[j])
+ master->cluster[j]->has_new = 0;
+
+ /* Copy the new caller-supplied control values.
+ user_to_new() sets 'has_new' to 1. */
+ ret = cluster_walk(i, cs, helpers, user_to_new);
+
+ if (!ret)
+ ret = try_or_set_control_cluster(master, set);
+
+ /* Copy the new values back to userspace. */
+ if (!ret)
+ ret = cluster_walk(i, cs, helpers, new_to_user);
+
+ v4l2_ctrl_unlock(ctrl);
+ cluster_done(i, cs, helpers);
+ }
+ return ret;
+}
+
+/* Try or try-and-set controls */
+static int try_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ bool set)
+{
+ struct ctrl_helper helper[4];
+ struct ctrl_helper *helpers = helper;
+ int ret;
+ int i;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ if (!helpers)
+ return -ENOMEM;
+ }
+ ret = prepare_ext_ctrls(hdl, cs, helpers, !set);
+ if (ret)
+ goto free;
+
+ /* First 'try' all controls and abort on error */
+ ret = try_or_set_ext_ctrls(hdl, cs, helpers, false);
+ /* If this is a 'set' operation and the initial 'try' failed,
+ then set error_idx to count to tell the application that no
+ controls changed value yet. */
+ if (set)
+ cs->error_idx = cs->count;
+ if (!ret && set) {
+ /* Reset 'handled' state */
+ for (i = 0; i < cs->count; i++)
+ helpers[i].handled = false;
+ ret = try_or_set_ext_ctrls(hdl, cs, helpers, true);
+ }
+
+free:
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(hdl, cs, false);
+}
+EXPORT_SYMBOL(v4l2_try_ext_ctrls);
+
+int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(hdl, cs, true);
+}
+EXPORT_SYMBOL(v4l2_s_ext_ctrls);
+
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(sd->ctrl_handler, cs, false);
+}
+EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
+
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(sd->ctrl_handler, cs, true);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
+
+/* Helper function for VIDIOC_S_CTRL compatibility */
+static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret;
+ int i;
+
+ v4l2_ctrl_lock(ctrl);
+
+ /* Reset the 'has_new' flags of the cluster */
+ for (i = 0; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->has_new = 0;
+
+ ctrl->val = *val;
+ ctrl->has_new = 1;
+ ret = try_or_set_control_cluster(master, false);
+ if (!ret)
+ ret = try_or_set_control_cluster(master, true);
+ *val = ctrl->cur.val;
+ v4l2_ctrl_unlock(ctrl);
+ return ret;
+}
+
+int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+
+ return set_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_s_ctrl);
+
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_s_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
+
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
+{
+ if (!ctrl->initialized)
+ v4l2_ctrl_initialize(ctrl);
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ return set_ctrl(ctrl, &val);
+}
+EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
new file mode 100644
index 0000000..1bb3a17
--- /dev/null
+++ b/include/media/v4l2-ctrls.h
@@ -0,0 +1,448 @@
+/*
+ V4L2 controls support header.
+
+ Copyright (C) 2010 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _V4L2_CTRLS_H
+#define _V4L2_CTRLS_H
+
+#include <linux/list.h>
+#include <linux/device.h>
+
+/* forward references */
+struct v4l2_ctrl_handler;
+struct v4l2_ctrl;
+struct video_device;
+struct v4l2_subdev;
+
+/** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
+ * @init: Initialize struct v4l2_ctrl fields on first use. The following
+ * fields may be initialized here:
+ * type, minimum, maximum, default_value, step/menu_skip_mask,
+ * qmenu, flags, cur and priv.
+ * Typically only used if one or more of these values only become
+ * known during runtime or if obtaining these values is an
+ * expensive operation.
+ * @g_volatile_ctrl: Get a new value for this control. Generally only relevant
+ * for volatile (and usually read-only) controls such as a control
+ * that returns the current signal strength which changes
+ * continuously.
+ * If not set, then the currently cached value will be returned.
+ * @try_ctrl: Test whether the control's value is valid. Only relevant when
+ * the usual min/max/step checks are not sufficient.
+ * @s_ctrl: Actually set the new control value. s_ctrl is compulsory. The
+ * ctrl->handler->lock is held when these ops are called, so no
+ * one else can access controls owned by that handler.
+ */
+struct v4l2_ctrl_ops {
+ void (*init)(struct v4l2_ctrl *ctrl);
+ int (*g_volatile_ctrl)(struct v4l2_ctrl *ctrl);
+ int (*try_ctrl)(struct v4l2_ctrl *ctrl);
+ int (*s_ctrl)(struct v4l2_ctrl *ctrl);
+};
+
+/** struct v4l2_ctrl - The control structure.
+ * @node: The list node.
+ * @handler: The handler that owns the control.
+ * @cluster: Point to start of cluster array.
+ * @ncontrols: Number of controls in cluster array.
+ * @has_new: Internal flag: set when there is a valid new value.
+ * @done: Internal flag: set for each processed control.
+ * @initialized: Internal flag: set if the control was initialized.
+ * @is_private: If set, then this control is private to its handler and it
+ * will not be added to any other handlers. Drivers can set
+ * this flag.
+ * @ops: The control ops.
+ * @id: The control ID.
+ * @name: The control name.
+ * @type: The control type.
+ * @minimum: The control's minimum value.
+ * @maximum: The control's maximum value.
+ * @default_value: The control's default value.
+ * @step: The control's step value for non-menu controls.
+ * @menu_skip_mask: The control's skip mask for menu controls. This makes it
+ * easy to skip menu items that are not valid. If bit X is set,
+ * then menu item X is skipped. Of course, this only works for
+ * menus with <= 32 menu items. There are no menus that come
+ * close to that number, so this is OK. Should we ever need more,
+ * then this will have to be extended to a u64 or a bit array.
+ * @qmenu: A const char * array for all menu items. Array entries that are
+ * empty strings ("") correspond to non-existing menu items (this
+ * is in addition to the menu_skip_mask above). The last entry
+ * must be NULL.
+ * @flags: The control's flags.
+ * @cur: The control's current value.
+ * @val: The control's new s32 value.
+ * @val64: The control's new s64 value.
+ * @string: The control's new string value.
+ * @priv: The control's private pointer. For use by the driver. It is
+ * untouched by the control framework. Note that this pointer is
+ * not freed when the control is deleted. Should this be needed
+ * then a new internal bitfield can be added to tell the framework
+ * to free this pointer.
+ */
+struct v4l2_ctrl {
+ /* Administrative fields */
+ struct list_head node;
+ struct v4l2_ctrl_handler *handler;
+ struct v4l2_ctrl **cluster;
+ unsigned ncontrols;
+ unsigned int has_new:1;
+ unsigned int done:1;
+ unsigned int initialized:1;
+
+ unsigned int is_private:1;
+
+ const struct v4l2_ctrl_ops *ops;
+ u32 id;
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 minimum, maximum, default_value;
+ union {
+ u32 step;
+ u32 menu_skip_mask;
+ };
+ const char **qmenu;
+ u32 flags;
+ union {
+ s32 val;
+ s64 val64;
+ char *string;
+ } cur;
+ union {
+ s32 val;
+ s64 val64;
+ char *string;
+ };
+ void *priv;
+};
+
+/** struct v4l2_ctrl_ref - The control reference.
+ * @node: List node for the sorted list.
+ * @next: Single-link list node for the hash.
+ * @ctrl: The actual control information.
+ *
+ * Each control handler has a list of these refs. The list_head is used to
+ * keep a sorted-by-control-ID list of all controls, while the next pointer
+ * is used to link the control in the hash's bucket.
+ */
+struct v4l2_ctrl_ref {
+ struct list_head node;
+ struct v4l2_ctrl_ref *next;
+ struct v4l2_ctrl *ctrl;
+};
+
+/** struct v4l2_ctrl_handler - The control handler keeps track of all the
+ * controls: both the controls owned by the handler and those inherited
+ * from other handlers.
+ * @lock: Lock to control access to this handler and its controls.
+ * @ctrls: The list of controls owned by this handler.
+ * @ctrl_refs: The list of control references.
+ * @cached: The last found control reference. It is common that the same
+ * control is needed multiple times, so this is a simple
+ * optimization.
+ * @buckets: Buckets for the hashing. Allows for quick control lookup.
+ * @nr_of_buckets: Total number of buckets in the array.
+ * @error: The error code of the first failed control addition.
+ */
+struct v4l2_ctrl_handler {
+ struct mutex lock;
+ struct list_head ctrls;
+ struct list_head ctrl_refs;
+ struct v4l2_ctrl_ref *cached;
+ struct v4l2_ctrl_ref **buckets;
+ u16 nr_of_buckets;
+ int error;
+};
+
+/** struct v4l2_ctrl_config - Control configuration structure.
+ * @ops: The control ops.
+ * @id: The control ID.
+ * @name: The control name.
+ * @type: The control type.
+ * @min: The control's minimum value.
+ * @max: The control's maximum value.
+ * @step: The control's step value for non-menu controls.
+ * @def: The control's default value.
+ * @flags: The control's flags.
+ * @menu_skip_mask: The control's skip mask for menu controls. This makes it
+ * easy to skip menu items that are not valid. If bit X is set,
+ * then menu item X is skipped. Of course, this only works for
+ * menus with <= 32 menu items. There are no menus that come
+ * close to that number, so this is OK. Should we ever need more,
+ * then this will have to be extended to a u64 or a bit array.
+ * @qmenu: A const char * array for all menu items. Array entries that are
+ * empty strings ("") correspond to non-existing menu items (this
+ * is in addition to the menu_skip_mask above). The last entry
+ * must be NULL.
+ */
+struct v4l2_ctrl_config {
+ const struct v4l2_ctrl_ops *ops;
+ u32 id;
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 min;
+ s32 max;
+ u32 step;
+ s32 def;
+ u32 flags;
+ u32 menu_skip_mask;
+ const char **qmenu;
+};
+
+/** v4l2_ctrl_fill() - 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 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, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags);
+
+
+/** v4l2_ctrl_handler_init() - Initialize the control handler.
+ * @hdl: The control handler.
+ * @nr_of_controls_hint: A hint of how many controls this handler is
+ * expected to refer to. This is the total number, so including
+ * any inherited controls. It doesn't have to be precise, but if
+ * it is way off, then you either waste memory (too many buckets
+ * are allocated) or the control lookup becomes slower (not enough
+ * buckets are allocated, so there are more slow list lookups).
+ * It will always work, though.
+ *
+ * Returns an error if the buckets could not be allocated. This error will
+ * also be stored in @hdl->error.
+ */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+ unsigned nr_of_controls_hint);
+
+/** v4l2_ctrl_handler_free() - Free all controls owned by the handler and free
+ * the control list.
+ * @hdl: The control handler.
+ *
+ * Does nothing if @hdl == NULL.
+ */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl);
+
+/** v4l2_ctrl_handler_setup() - Call the s_ctrl op for all controls belonging
+ * to the handler to initialize the hardware to the current control values.
+ * @hdl: The control handler.
+ *
+ * Button controls will be skipped, as are read-only controls. Controls that
+ * require initialization (@ops->init is set) are also skipped.
+ *
+ * If @hdl == NULL, then this just returns 0.
+ */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl);
+
+/** v4l2_ctrl_handler_log_status() - Log all controls owned by the handler.
+ * @hdl: The control handler.
+ * @prefix: The prefix to use when logging the control values. If the
+ * prefix does not end with a space, then ": " will be added
+ * after the prefix. If @prefix == NULL, then no prefix will be
+ * used.
+ *
+ * For use with VIDIOC_LOG_STATUS.
+ *
+ * Does nothing if @hdl == NULL.
+ */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+ const char *prefix);
+
+/** v4l2_ctrl_new_custom() - Allocate and initialize a new custom V4L2
+ * control.
+ * @hdl: The control handler.
+ * @cfg: The control's configuration data.
+ * @priv: The control's driver-specific private data.
+ *
+ * If the &v4l2_ctrl struct could not be allocated then NULL is returned
+ * and @hdl->error is set to the error code (if it wasn't set already).
+ */
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_config *cfg, void *priv);
+
+/** v4l2_ctrl_new_std() - Allocate and initialize a new standard V4L2 control.
+ * @hdl: The control handler.
+ * @ops: The control ops.
+ * @id: The control ID.
+ * @min: The control's minimum value.
+ * @max: The control's maximum value.
+ * @step: The control's step value for non-menu controls.
+ * @def: The control's default value.
+ *
+ * If the &v4l2_ctrl struct could not be allocated, or the control
+ * ID is not known, then NULL is returned and @hdl->error is set to the
+ * appropriate error code (if it wasn't set already).
+ */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 min, s32 max, u32 step, s32 def);
+
+/** v4l2_ctrl_new_std_menu() - Helper function for standard menu controls.
+ * @hdl: The control handler.
+ * @ops: The control ops.
+ * @id: The control ID.
+ * @mask: The control's skip mask for menu controls. This makes it
+ * easy to skip menu items that are not valid. If bit X is set,
+ * then menu item X is skipped. Of course, this only works for
+ * menus with <= 32 menu items. There are no menus that come
+ * close to that number, so this is OK. Should we ever need more,
+ * then this will have to be extended to a u64 or a bit array.
+ * @def: The control's default value.
+ *
+ * Same as v4l2_ctrl_new_std(), but @min is set to 0, @max is calculated based
+ * on the size of the menu and the @mask value determines which menu items
+ * are to be skipped.
+ */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 mask, s32 def);
+
+/** v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler.
+ * @hdl: The control handler.
+ * @ctrl: The control to add.
+ *
+ * It will return NULL if it was unable to add the control reference.
+ * If the control already belonged to the handler, then it will do
+ * nothing and just return @ctrl.
+ */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl);
+
+/** v4l2_ctrl_add_handler() - Add all controls from handler @add to
+ * handler @hdl.
+ * @hdl: The control handler.
+ * @add: The control handler whose controls you want to add to
+ * the @hdl control handler.
+ *
+ * Does nothing if either of the two is a NULL pointer.
+ * In case of an error @hdl->error will be set to the error code (if it
+ * wasn't set already).
+ */
+int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl_handler *add);
+
+
+/** v4l2_ctrl_cluster() - Mark all controls in the cluster as belonging to that cluster.
+ * @ncontrols: The number of controls in this cluster.
+ * @controls: The cluster control array of size @ncontrols.
+ */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls);
+
+
+/** v4l2_ctrl_find() - Find a control with the given ID.
+ * @hdl: The control handler.
+ * @id: The control ID to find.
+ *
+ * If @hdl == NULL this will return NULL as well. Will lock the handler so
+ * do not use from inside &v4l2_ctrl_ops.
+ */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id);
+
+/** v4l2_ctrl_activate() - Make the control active or inactive.
+ * @ctrl: The control to (de)activate.
+ * @active: True if the control should become active.
+ *
+ * This sets or clears the V4L2_CTRL_FLAG_INACTIVE flag.
+ * Does nothing if @ctrl == NULL or if the control is uninitialized.
+ * Does not lock the @ctrl->handler. This will usually be called from
+ * within the s_ctrl op.
+ */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active);
+
+/** v4l2_ctrl_grab() - Mark the control as grabbed or not grabbed.
+ * @ctrl: The control to (de)activate.
+ * @grabbed: True if the control should become grabbed.
+ *
+ * This sets or clears the V4L2_CTRL_FLAG_GRABBED flag. Does nothing
+ * if @ctrl == NULL or if the control is uninitialized.
+ * Will lock the @ctrl->handler. This will usually be called when starting
+ * or stopping streaming in the driver.
+ */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed);
+
+/** v4l2_ctrl_lock() - Helper function to lock the handler
+ * associated with the control.
+ * @ctrl: The control to lock.
+ */
+static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl)
+{
+ mutex_lock(&ctrl->handler->lock);
+}
+
+/** v4l2_ctrl_lock() - Helper function to unlock the handler
+ * associated with the control.
+ * @ctrl: The control to unlock.
+ */
+static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl)
+{
+ mutex_unlock(&ctrl->handler->lock);
+}
+
+/** v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver.
+ * @ctrl: The control.
+ *
+ * This returns the control's value safely by going through the control
+ * framework. This function will lock the control's handler, so it cannot be
+ * used from within the &v4l2_ctrl_ops functions.
+ *
+ * This function is for integer type controls only.
+ */
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
+
+/** v4l2_ctrl_s_ctrl() - Helper function to set the control's value from within a driver.
+ * @ctrl: The control.
+ * @val: The new value.
+ *
+ * This set the control's new value safely by going through the control
+ * framework. This function will lock the control's handler, so it cannot be
+ * used from within the &v4l2_ctrl_ops functions.
+ *
+ * This function is for integer type controls only.
+ */
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
+
+
+/* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc);
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm);
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl);
+int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl);
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+
+/* Helpers for subdevices. If the associated ctrl_handler == NULL then they
+ will all return -EINVAL. */
+int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+
+#endif
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index bebe44b..1efcacb 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -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
@@ -67,6 +68,9 @@ struct video_device
struct 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;
diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
index 5d5d550..8bcbd7a 100644
--- a/include/media/v4l2-device.h
+++ b/include/media/v4l2-device.h
@@ -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.
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index a888893..927b4a6 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -35,6 +35,7 @@
#define V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ 0x00000001
struct v4l2_device;
+struct v4l2_ctrl_handler;
struct v4l2_subdev;
struct tuner_setup;
@@ -415,6 +416,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 */
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 02/15] [RFCv2] v4l2-ctrls: reorder 'case' statements to match order in header.
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
2010-05-16 13:20 ` [PATCH 01/15] [RFCv2] v4l2: Add new " Hans Verkuil
@ 2010-05-16 13:20 ` Hans Verkuil
2010-05-16 13:20 ` [PATCH 03/15] [RFCv2] Documentation: add v4l2-controls.txt documenting the new controls API Hans Verkuil
` (13 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:20 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
To 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.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/media/video/v4l2-ctrls.c | 30 +++++++++++++++++-------------
1 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 6c97ff0..21bf740 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -266,6 +266,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";
@@ -296,28 +297,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";
@@ -330,16 +340,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";
@@ -353,14 +356,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";
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 03/15] [RFCv2] Documentation: add v4l2-controls.txt documenting the new controls API.
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
2010-05-16 13:20 ` [PATCH 01/15] [RFCv2] v4l2: Add new " Hans Verkuil
2010-05-16 13:20 ` [PATCH 02/15] [RFCv2] v4l2-ctrls: reorder 'case' statements to match order in header Hans Verkuil
@ 2010-05-16 13:20 ` Hans Verkuil
2010-05-23 23:17 ` Laurent Pinchart
2010-05-16 13:21 ` [PATCH 04/15] [RFCv2] v4l2: hook up the new control framework into the core framework Hans Verkuil
` (12 subsequent siblings)
15 siblings, 1 reply; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:20 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Documentation/video4linux/v4l2-controls.txt | 600 +++++++++++++++++++++++++++
1 files changed, 600 insertions(+), 0 deletions(-)
create mode 100644 Documentation/video4linux/v4l2-controls.txt
diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt
new file mode 100644
index 0000000..44246b2
--- /dev/null
+++ b/Documentation/video4linux/v4l2-controls.txt
@@ -0,0 +1,600 @@
+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's value? (i.e. s_ctrl)
+
+And occasionally:
+
+3) How do I get the control's value? (i.e. g_volatile_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.
+
+Note that the control framework relies on the presence of a struct v4l2_device
+for bridge drivers and struct v4l2_subdev for sub-device drivers.
+
+
+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 for bridge and sub-device drivers
+=============================================
+
+1) Prepare the driver:
+
+1.1) Add the handler to your driver's top-level struct:
+
+ struct foo_dev {
+ ...
+ struct v4l2_ctrl_handler ctrl_handler;
+ ...
+ };
+
+ struct foo_dev *foo;
+
+1.2) Initialize the handler:
+
+ v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
+
+ The second argument is a hint telling the function how many controls this
+ handler is expected to handle. It will allocate a hashtable based on this
+ information. It is a hint only.
+
+1.3) Hook the control handler into the driver:
+
+1.3.1) For bridge drivers do this:
+
+ foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;
+
+ Where foo->v4l2_dev is of type struct v4l2_device.
+
+ 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.
+
+1.3.2) For sub-device drivers do this:
+
+ foo->sd.ctrl_handler = &foo->ctrl_handler;
+
+ Where foo->sd is of type struct v4l2_subdev.
+
+ And set all core control ops in your struct v4l2_subdev_core_ops to these
+ helpers:
+
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+
+ Note: this is a temporary solution only. Once everything is converted to
+ the control framework these helpers will no longer be needed.
+
+1.4) Clean up the handler at the end:
+
+ v4l2_ctrl_handler_free(&foo->ctrl_handler);
+
+
+2) Add controls:
+
+Typically done right after the v4l2_ctrl_handler_init:
+
+ v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
+ v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ ...
+ if (foo->ctrl_handler.error) {
+ int err = foo->ctrl_handler.error;
+
+ v4l2_ctrl_handler_free(&foo->ctrl_handler);
+ 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.
+
+The v4l2_ctrl_new_std function will fill in most fields based on the control
+ID except for the min, max, step and default values. These are passed in the
+last four arguments. These values are driver specific while control attributes
+like type, name, flags are all global. The control's current value will be set
+to the default value.
+
+Note that if something fails, the function will return NULL or an error and
+set ctrl_handler->error to the error code. If ctrl_handler->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->ctrl_handler);
+
+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, ctrl_handler);
+
+ 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.
+
+===============================================================================
+
+
+Inheriting Controls
+===================
+
+When a sub-device is registered with a bridge driver by calling
+v4l2_device_register_subdev() 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).
+
+What happens here is that v4l2_device_register_subdev() calls
+v4l2_ctrl_add_handler() adding the controls of the subdev to the controls
+of v4l2_device.
+
+
+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.
+
+In most cases 'cur' contains the current cached control value. When you create
+a new control this value is made identical to the default value. After calling
+v4l2_ctrl_handler_setup() this value is passed to the hardware. It is generally
+a good idea to call this function.
+
+Whenever a new value is set that new value is automatically cached. This means
+that most drivers do not need to implement the g_volatile_ctrl() op. The
+exception is for controls that return a volatile register such as a signal
+strength read-out that changes continuously. In that case you will need to
+implement g_volatile_ctrl like this:
+
+ ctrl->cur.val = read_reg(0x123);
+
+The 'new value' union is not relevant in g_volatile_ctrl. In general controls
+that need to implement g_volatile_ctrl are read-only controls.
+
+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_volatile/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 from
+within the driver:
+
+ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
+ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
+
+These functions go through the control framework just as VIDIOC_G/S_CTRL ioctls
+do. Don't use these inside the control ops g_volatile/s/try_ctrl, though, that
+will fail since these helpers lock the handler.
+
+You can also take the handler lock yourself:
+
+ mutex_lock(&state->ctrl_handler.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->ctrl_handler.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 (see below) 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 function 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'.
+
+
+Custom Controls
+===============
+
+Driver specific controls can be created using v4l2_ctrl_new_custom():
+
+ static const struct v4l2_ctrl_config ctrl_filter = {
+ .ops = &ctrl_custom_ops,
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ .name = "Spatial Filter",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .max = 15,
+ .step = 1,
+ };
+
+ ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_filter, NULL);
+
+The last argument is the priv pointer which can be set to driver-specific
+private data.
+
+
+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 ctrl_handler;
+#define AUDIO_CL_VOLUME (0)
+#define AUDIO_CL_MUTE (1)
+ struct v4l2_ctrl *audio_cluster[2];
+ ...
+ };
+
+ state->audio_cluster[AUDIO_CL_VOLUME] =
+ v4l2_ctrl_new_std(&state->ctrl_handler, ...);
+ state->audio_cluster[AUDIO_CL_MUTE] =
+ v4l2_ctrl_new_std(&state->ctrl_handler, ...);
+ v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);
+
+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 audio_cluster:
+
+ static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME: {
+ struct v4l2_ctrl *mute = ctrl->cluster[AUDIO_CL_MUTE];
+
+ write_reg(0x123, 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 == ctrl->cluster[AUDIO_CL_VOLUME] == state->audio_cluster[AUDIO_CL_VOLUME]
+ ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]
+
+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 always 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(ctrl_handler, 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. You can do that by manually setting the ctrl_handler
+field of struct video_device.
+
+That is 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. Now v4l2_device_register_subdev() will no longer
+merge subdev controls.
+
+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
+control handler. This control handler may be specific to the video_device or
+for a subset of video_device's. For example: the radio device nodes only have
+audio controls, while the video and vbi device nodes share the same control
+handler for the audio and video controls.
+
+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 should 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_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
+ v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
+ v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler);
+
+Or you can add specific controls to a handler:
+
+ volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_AUDIO_VOLUME, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
+ v4l2_ctrl_add_ctrl(&radio_ctrl_handler, volume);
+
+What you should not do is make two identical controls for two handlers.
+For example:
+
+ v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &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 ctrl_handler;
+
+ v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
+ v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
+
+...and in video_ops.s_ctrl:
+
+ case V4L2_CID_BRIGHTNESS:
+ contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
+ ...
+
+When s_ctrl is called by the framework the ctrl_handler.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->ctrl_handler, ...);
+ if (ctrl)
+ ctrl->is_private = 1;
+
+These controls will now be skipped when v4l2_ctrl_add_handler is called.
+
+
+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.
+
+
+Initializing Controls
+=====================
+
+Usually controls are initialized when you call one of the v4l2_ctrl_new_*()
+functions. But sometimes you do not know all the details of the control's
+boundaries and possibly even type at this stage. Or obtaining those details
+may require expensive hardware accesses.
+
+In that case you can specify a .init op in struct v4l2_ctrl_ops. This op
+returns a void, so it may not fail. Should the init run into problems, then
+it should disable the control by setting V4L2_CTRL_FLAG_DISABLED.
+
+The init op can be used to setup the type and boundaries of the control on
+first use. Any controls with a .init op will be skipped by
+v4l2_ctrl_handler_setup().
+
+
+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.
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH 03/15] [RFCv2] Documentation: add v4l2-controls.txt documenting the new controls API.
2010-05-16 13:20 ` [PATCH 03/15] [RFCv2] Documentation: add v4l2-controls.txt documenting the new controls API Hans Verkuil
@ 2010-05-23 23:17 ` Laurent Pinchart
2010-05-24 0:15 ` Andy Walls
2010-05-24 9:44 ` Hans Verkuil
0 siblings, 2 replies; 22+ messages in thread
From: Laurent Pinchart @ 2010-05-23 23:17 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media
Hi Hans,
Thanks for the second RFC version. I've finally had time to review it. Many
issues from the first version have been fixed, but there are some left (or at
least topics that are still open for discussion). Here are my comments.
On Sunday 16 May 2010 15:20:57 Hans Verkuil wrote:
> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> Documentation/video4linux/v4l2-controls.txt | 600
> +++++++++++++++++++++++++++ 1 files changed, 600 insertions(+), 0
> deletions(-)
> create mode 100644 Documentation/video4linux/v4l2-controls.txt
>
> diff --git a/Documentation/video4linux/v4l2-controls.txt
> b/Documentation/video4linux/v4l2-controls.txt new file mode 100644
> index 0000000..44246b2
> --- /dev/null
> +++ b/Documentation/video4linux/v4l2-controls.txt
> @@ -0,0 +1,600 @@
> +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's value? (i.e. s_ctrl)
> +
> +And occasionally:
> +
> +3) How do I get the control's value? (i.e. g_volatile_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.
> +
> +Note that the control framework relies on the presence of a struct
> v4l2_device
> +for bridge drivers and struct v4l2_subdev for sub-device drivers.
Didn't we agree on replacing the name bridge by something else ? "bridges" are
a subset of the V4L2 devices.
> +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 for bridge and sub-device drivers
> +=============================================
> +
> +1) Prepare the driver:
> +
> +1.1) Add the handler to your driver's top-level struct:
> +
> + struct foo_dev {
> + ...
> + struct v4l2_ctrl_handler ctrl_handler;
> + ...
> + };
> +
> + struct foo_dev *foo;
> +
> +1.2) Initialize the handler:
> +
> + v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
> +
> + The second argument is a hint telling the function how many controls
> this
> + handler is expected to handle. It will allocate a hashtable based on this
> + information. It is a hint only.
> +
> +1.3) Hook the control handler into the driver:
> +
> +1.3.1) For bridge drivers do this:
> +
> + foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;
> +
> + Where foo->v4l2_dev is of type struct v4l2_device.
Please add struct v4l2_device v4l2_dev in the foo_dev structure above.
> + 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.
> +
> +1.3.2) For sub-device drivers do this:
> +
> + foo->sd.ctrl_handler = &foo->ctrl_handler;
> +
> + Where foo->sd is of type struct v4l2_subdev.
> +
> + And set all core control ops in your struct v4l2_subdev_core_ops to
> these
> + helpers:
> +
> + .queryctrl = v4l2_subdev_queryctrl,
> + .querymenu = v4l2_subdev_querymenu,
> + .g_ctrl = v4l2_subdev_g_ctrl,
> + .s_ctrl = v4l2_subdev_s_ctrl,
> + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
> + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
> + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
> +
> + Note: this is a temporary solution only. Once everything is converted to
> + the control framework these helpers will no longer be needed.
If I understand things properly, those operations are still needed to allow
"legacy" V4L2 device drivers to use converted subdevices. I would then phrase
it a bit differently:
"Note: this is a temporary solution only. Once all devices using the subdev
are converted to the control framework these helpers will no longer be
needed."
This will make it clear that subdev drivers can be cleaned up of those
backward-compatibility helpers as soon all device drivers using the subdevs
are converted, and not when all device drivers are converted.
1.3.1 is for bridge drivers. 1.3.2 is for subdev drivers. What about drivers
with no subdevs and bridge (just a "simple" device such as a webcam) ?
> +1.4) Clean up the handler at the end:
> +
> + v4l2_ctrl_handler_free(&foo->ctrl_handler);
> +
> +
> +2) Add controls:
> +
> +Typically done right after the v4l2_ctrl_handler_init:
> +
> + v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
> + v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
> + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
Adding the function prototype to the documentation is still a good idea :-)
> + v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
> + V4L2_CID_CONTRAST, 0, 255, 1, 128);
> + ...
> + if (foo->ctrl_handler.error) {
> + int err = foo->ctrl_handler.error;
> +
> + v4l2_ctrl_handler_free(&foo->ctrl_handler);
> + 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.
> +
> +The v4l2_ctrl_new_std function will fill in most fields based on the
> control
> +ID except for the min, max, step and default values. These are passed in
> the
> +last four arguments. These values are driver specific while control
> attributes
> +like type, name, flags are all global. The control's current value will be
> set
> +to the default value.
> +
> +Note that if something fails, the function will return NULL or an error
> and
> +set ctrl_handler->error to the error code. If ctrl_handler->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.
You will find me a bit annoying, but if standard controls could be initialized
using structures, we could pass a pointer to an array to a single control add
function :-) There would be less code in drivers, and they would be easier to
read.
> +3) Optionally force initial control setup:
> +
> + v4l2_ctrl_handler_setup(&foo->ctrl_handler);
> +
> +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.
Copying my comments from the previous review, I think they got lost somewhere
(or I have lost your answer to them).
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 ?
> +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,
> ctrl_handler); +
> + 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.
> +
> +==========================================================================
> =====
> +
> +
> +Inheriting Controls
> +===================
> +
> +When a sub-device is registered with a bridge driver by calling
> +v4l2_device_register_subdev() 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).
> +
> +What happens here is that v4l2_device_register_subdev() calls
> +v4l2_ctrl_add_handler() adding the controls of the subdev to the controls
> +of v4l2_device.
> +
> +
> +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.
Does the string controls spec require the maximum length to be fixed, or can
it vary ?
> +In most cases 'cur' contains the current cached control value. When you
> create
> +a new control this value is made identical to the default value. After
> calling
> +v4l2_ctrl_handler_setup() this value is passed to the hardware. It is
> generally
> +a good idea to call this function.
> +
> +Whenever a new value is set that new value is automatically cached. This
> means
> +that most drivers do not need to implement the g_volatile_ctrl() op. The
> +exception is for controls that return a volatile register such as a signal
> +strength read-out that changes continuously. In that case you will need to
> +implement g_volatile_ctrl like this:
> +
> + ctrl->cur.val = read_reg(0x123);
Do we really need to call the operation g_volatile_ctrl ? I was OK with
g_ctrl.
If some controls are volatile and others are not, you said that the operation
can ignore non-volatile controls. How does it do so ? By just returning
success ? It might be worth mentioning it in the doc.
Another comment from my previous review for which I haven't seen an answer:
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).
> +The 'new value' union is not relevant in g_volatile_ctrl. In general
> controls
> +that need to implement g_volatile_ctrl are read-only controls.
> +
> +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.
Instead of playing with cur and new, wouldn't it be less confusing to pass a
pointer to the new value as an argument to s_ctrl (and a pointer to the
current value as an argument to g_ctrl) ?
> +While in g_volatile/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.
Can we access all fields of other controls in s_ctrl, or just the value ? More
specifically, can s_ctrl on an auto control modify the enabled flag on the
associated manual control ?
> +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 from
> +within the driver:
> +
> + s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
> + int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
> +
> +These functions go through the control framework just as VIDIOC_G/S_CTRL
> ioctls
> +do. Don't use these inside the control ops g_volatile/s/try_ctrl, though,
> that
> +will fail since these helpers lock the handler.
This sounds a bit confusing. Would it be possible to avoid taking the lock in
v4l2_ctrl_g/s_ctrl if the control belongs to the same handler as the one being
accessed in g/s/try_ctrl ? Just throwing an idea, not sure if it's a good one,
but I'm pretty sure it will be confusing to have to access controls directly
and use the helper functions in drivers depending on which control you access
and in which context.
> +You can also take the handler lock yourself:
> +
> + mutex_lock(&state->ctrl_handler.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->ctrl_handler.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 (see below) 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.
What about renaming the argument to step_or_skip_mask then ? Or make it two
arguments ?
The comment doesn't seem to apply to v4l2_ctrl_new_custom anymore, as you have
two different fields there.
> +The v4l2_ctrl_new_std_menu function 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'.
> +
> +
> +Custom Controls
> +===============
> +
> +Driver specific controls can be created using v4l2_ctrl_new_custom():
> +
> + static const struct v4l2_ctrl_config ctrl_filter = {
> + .ops = &ctrl_custom_ops,
> + .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
> + .name = "Spatial Filter",
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .flags = V4L2_CTRL_FLAG_SLIDER,
> + .max = 15,
> + .step = 1,
> + };
> +
> + ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_filter, NULL);
> +
> +The last argument is the priv pointer which can be set to driver-specific
> +private data.
If I add a standard control using v4l2_ctrl_new_custom, will it fill the name,
type, ... fields for me ?
> +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.
Shouldn't you mention that the driver needs to take the handler lock if it
wants to (de)activate a control belonging to another handler, or if it wants
to do it outside the s_ctrl callback ?
> +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.
This makes v4l2_ctrl_activate and v4l2_ctrl_grab behave differently. It might
be confusing.
> +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 ctrl_handler;
> +#define AUDIO_CL_VOLUME (0)
> +#define AUDIO_CL_MUTE (1)
> + struct v4l2_ctrl *audio_cluster[2];
> + ...
> + };
> +
> + state->audio_cluster[AUDIO_CL_VOLUME] =
> + v4l2_ctrl_new_std(&state->ctrl_handler, ...);
> + state->audio_cluster[AUDIO_CL_MUTE] =
> + v4l2_ctrl_new_std(&state->ctrl_handler, ...);
> + v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);
> +
> +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 audio_cluster:
> +
> + static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
> + {
> + struct foo *state = container_of(ctrl->handler, struct foo,
> ctrl_handler); +
> + switch (ctrl->id) {
> + case V4L2_CID_AUDIO_VOLUME: {
> + struct v4l2_ctrl *mute = ctrl->cluster[AUDIO_CL_MUTE];
> +
> + write_reg(0x123, 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 == ctrl->cluster[AUDIO_CL_VOLUME] ==
> state->audio_cluster[AUDIO_CL_VOLUME]
> + ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]
> +
> +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.
The driver needs to set state->audio_cluster[AUDIO_CL_MUTE] to NULL
explicitly, right ? In that case I think you should mention it.
> 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 always 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(ctrl_handler, 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. You can do that by manually setting the ctrl_handler
> +field of struct video_device.
> +
> +That is 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. Now v4l2_device_register_subdev() will no
> longer
> +merge subdev controls.
> +
> +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
> +control handler. This control handler may be specific to the video_device
> or
> +for a subset of video_device's. For example: the radio device nodes only
> have
> +audio controls, while the video and vbi device nodes share the same control
> +handler for the audio and video controls.
> +
> +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 should 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_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_VOLUME,
> ...);
> + v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE,
> ...);
> + v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS,
> ...);
> + v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST,
> ...);
> + v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler);
> +
> +Or you can add specific controls to a handler:
> +
> + volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops,
> V4L2_CID_AUDIO_VOLUME, ...);
> + v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
> + v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
> + v4l2_ctrl_add_ctrl(&radio_ctrl_handler, volume);
> +
> +What you should not do is make two identical controls for two handlers.
> +For example:
> +
> + v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE,
> ...);
> + v4l2_ctrl_new_std(&video_ctrl_handler, &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 ctrl_handler;
> +
> + v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
> + v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
> +
> +...and in video_ops.s_ctrl:
> +
> + case V4L2_CID_BRIGHTNESS:
> + contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
> + ...
> +
> +When s_ctrl is called by the framework the ctrl_handler.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.
Doesn't that call for an unlocked version of the find function ?
> +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->ctrl_handler, ...);
> + if (ctrl)
> + ctrl->is_private = 1;
> +
> +These controls will now be skipped when v4l2_ctrl_add_handler is called.
One more unresolved comment from the previous review:
> > 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.
"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.
> +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.
> +
> +
> +Initializing Controls
> +=====================
> +
> +Usually controls are initialized when you call one of the
> v4l2_ctrl_new_*()
> +functions. But sometimes you do not know all the details of the control's
> +boundaries and possibly even type at this stage. Or obtaining those details
> +may require expensive hardware accesses.
> +
> +In that case you can specify a .init op in struct v4l2_ctrl_ops. This op
> +returns a void, so it may not fail. Should the init run into problems,
> then
> +it should disable the control by setting V4L2_CTRL_FLAG_DISABLED.
If it can fail, why does it return void ? The operation will fail from times
to times with buggy UVC devices. It's better to return an error to userspace
rather than silently disabling the control. I can already see support e-mails
asking why a control suddenly becomes disabled.
> +The init op can be used to setup the type and boundaries of the control on
> +first use. Any controls with a .init op will be skipped by
> +v4l2_ctrl_handler_setup().
> +
> +
> +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.
What about non-button write-only controls, are they handled the same way ?
> +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.
Shown where ?
> +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.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH 03/15] [RFCv2] Documentation: add v4l2-controls.txt documenting the new controls API.
2010-05-23 23:17 ` Laurent Pinchart
@ 2010-05-24 0:15 ` Andy Walls
2010-05-24 10:01 ` Hans Verkuil
2010-05-24 9:44 ` Hans Verkuil
1 sibling, 1 reply; 22+ messages in thread
From: Andy Walls @ 2010-05-24 0:15 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: Hans Verkuil, linux-media
On Mon, 2010-05-24 at 01:17 +0200, Laurent Pinchart wrote:
> Hi Hans,
>
> > +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.
>
> Shown where ?
In the output of `v4l2-ctl -L` or any other app that builds controls for
the user to see. This is really just a formatting hint for the
application, analogous to "%x" in a printf() format. I think there may
be a larger issue of control formatting hints here.
So now for a digression:
video_mute_yuv is both a very good and bad example:
$ v4l2-ctl -L
...
video_mute_yuv (int) : min=0 max=16777215 step=1 default=32896 value=32896
...
The value is YUV values encoded as 0x00YYUUVV. For the cx18 driver the
default is 32869 or 0x008080 (no luminance and neutral red and blue
chrominance). A hex readout makes the control setting more readable.
But even with improved formatting, this control is a still poor UI
element. Most people can't map a color word, like "green", to a (Y, U,
V) coordinate easily.
I'll assert
a. a menu with a few human-readable color names would be more useful
or
b. the control could really be broken up into three separate sliders,
obviating the need for the HEX formatting hint. Just because the MPEG
encoding engine wants it set as one value, doesn't mean humans must set
it that way.
I suppose since it's in ivtv and cx18, it's mine to fix. :)
Regards,
Andy
> > +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.
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 03/15] [RFCv2] Documentation: add v4l2-controls.txt documenting the new controls API.
2010-05-24 0:15 ` Andy Walls
@ 2010-05-24 10:01 ` Hans Verkuil
0 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-24 10:01 UTC (permalink / raw)
To: Andy Walls; +Cc: Laurent Pinchart, linux-media
On Monday 24 May 2010 02:15:08 Andy Walls wrote:
> On Mon, 2010-05-24 at 01:17 +0200, Laurent Pinchart wrote:
> > Hi Hans,
> >
>
> > > +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.
> >
> > Shown where ?
>
> In the output of `v4l2-ctl -L` or any other app that builds controls for
> the user to see. This is really just a formatting hint for the
> application, analogous to "%x" in a printf() format. I think there may
> be a larger issue of control formatting hints here.
>
>
>
> So now for a digression:
>
> video_mute_yuv is both a very good and bad example:
>
> $ v4l2-ctl -L
> ...
> video_mute_yuv (int) : min=0 max=16777215 step=1 default=32896 value=32896
> ...
>
> The value is YUV values encoded as 0x00YYUUVV. For the cx18 driver the
> default is 32869 or 0x008080 (no luminance and neutral red and blue
> chrominance). A hex readout makes the control setting more readable.
>
> But even with improved formatting, this control is a still poor UI
> element. Most people can't map a color word, like "green", to a (Y, U,
> V) coordinate easily.
>
> I'll assert
>
> a. a menu with a few human-readable color names would be more useful
True, but not as flexible.
> or
>
> b. the control could really be broken up into three separate sliders,
> obviating the need for the HEX formatting hint. Just because the MPEG
> encoding engine wants it set as one value, doesn't mean humans must set
> it that way.
Hmm, I don't really like this idea. What you really want to communicate
is that this is a color. How it is represented in a user interface is up
to the application.
So:
c. we add a V4L2_CTRL_FLAG_COLOR flag.
The problem is: how do you specify the colorspace and possibly color depth.
And perhaps there is an alpha channel as well.
The reason I went with a simple hex flag is that it is, well, simple. It
greatly improves the readability of colors and other values that are more
easily readable in hex format, without introducing the complexity that
you get once you start talking about colors.
Right now all the color controls that we have are in the 'advanced user'
category so I would be happy with just providing a hex flag.
Should color controls become much more important, then we can revisit this
and make true color controls.
Obviously, if someone wants to make a proposal for this now, then feel free.
But I don't think it's worth the time and effort.
Regards,
Hans
> I suppose since it's in ivtv and cx18, it's mine to fix. :)
>
> Regards,
> Andy
>
>
> > > +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.
> >
>
>
>
--
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 03/15] [RFCv2] Documentation: add v4l2-controls.txt documenting the new controls API.
2010-05-23 23:17 ` Laurent Pinchart
2010-05-24 0:15 ` Andy Walls
@ 2010-05-24 9:44 ` Hans Verkuil
2010-05-24 13:43 ` Hans Verkuil
1 sibling, 1 reply; 22+ messages in thread
From: Hans Verkuil @ 2010-05-24 9:44 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: linux-media
Hi Laurent,
Thanks for your review! As always, it was very useful.
On Monday 24 May 2010 01:17:34 Laurent Pinchart wrote:
> Hi Hans,
>
> Thanks for the second RFC version. I've finally had time to review it. Many
> issues from the first version have been fixed, but there are some left (or at
> least topics that are still open for discussion). Here are my comments.
>
> On Sunday 16 May 2010 15:20:57 Hans Verkuil wrote:
> > Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> > Documentation/video4linux/v4l2-controls.txt | 600
> > +++++++++++++++++++++++++++ 1 files changed, 600 insertions(+), 0
> > deletions(-)
> > create mode 100644 Documentation/video4linux/v4l2-controls.txt
> >
> > diff --git a/Documentation/video4linux/v4l2-controls.txt
> > b/Documentation/video4linux/v4l2-controls.txt new file mode 100644
> > index 0000000..44246b2
> > --- /dev/null
> > +++ b/Documentation/video4linux/v4l2-controls.txt
> > @@ -0,0 +1,600 @@
> > +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's value? (i.e. s_ctrl)
> > +
> > +And occasionally:
> > +
> > +3) How do I get the control's value? (i.e. g_volatile_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.
> > +
> > +Note that the control framework relies on the presence of a struct
> > v4l2_device
> > +for bridge drivers and struct v4l2_subdev for sub-device drivers.
>
> Didn't we agree on replacing the name bridge by something else ? "bridges" are
> a subset of the V4L2 devices.
What is a good name for such drivers? V4L2 drivers?
> > +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 for bridge and sub-device drivers
> > +=============================================
> > +
> > +1) Prepare the driver:
> > +
> > +1.1) Add the handler to your driver's top-level struct:
> > +
> > + struct foo_dev {
> > + ...
> > + struct v4l2_ctrl_handler ctrl_handler;
> > + ...
> > + };
> > +
> > + struct foo_dev *foo;
> > +
> > +1.2) Initialize the handler:
> > +
> > + v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
> > +
> > + The second argument is a hint telling the function how many controls
> > this
> > + handler is expected to handle. It will allocate a hashtable based on this
> > + information. It is a hint only.
> > +
> > +1.3) Hook the control handler into the driver:
> > +
> > +1.3.1) For bridge drivers do this:
> > +
> > + foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;
> > +
> > + Where foo->v4l2_dev is of type struct v4l2_device.
>
> Please add struct v4l2_device v4l2_dev in the foo_dev structure above.
Will do.
>
> > + 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.
> > +
> > +1.3.2) For sub-device drivers do this:
> > +
> > + foo->sd.ctrl_handler = &foo->ctrl_handler;
> > +
> > + Where foo->sd is of type struct v4l2_subdev.
> > +
> > + And set all core control ops in your struct v4l2_subdev_core_ops to
> > these
> > + helpers:
> > +
> > + .queryctrl = v4l2_subdev_queryctrl,
> > + .querymenu = v4l2_subdev_querymenu,
> > + .g_ctrl = v4l2_subdev_g_ctrl,
> > + .s_ctrl = v4l2_subdev_s_ctrl,
> > + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
> > + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
> > + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
> > +
> > + Note: this is a temporary solution only. Once everything is converted to
> > + the control framework these helpers will no longer be needed.
>
> If I understand things properly, those operations are still needed to allow
> "legacy" V4L2 device drivers to use converted subdevices. I would then phrase
> it a bit differently:
>
> "Note: this is a temporary solution only. Once all devices using the subdev
> are converted to the control framework these helpers will no longer be
> needed."
>
> This will make it clear that subdev drivers can be cleaned up of those
> backward-compatibility helpers as soon all device drivers using the subdevs
> are converted, and not when all device drivers are converted.
I'll change the phrasing.
> 1.3.1 is for bridge drivers. 1.3.2 is for subdev drivers. What about drivers
> with no subdevs and bridge (just a "simple" device such as a webcam) ?
Wherever I wrote 'bridge driver' replace that with 'V4L2 driver'. So 1.3.1 is
for any V4L2 driver.
> > +1.4) Clean up the handler at the end:
> > +
> > + v4l2_ctrl_handler_free(&foo->ctrl_handler);
> > +
> > +
> > +2) Add controls:
> > +
> > +Typically done right after the v4l2_ctrl_handler_init:
> > +
> > + v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
> > + v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
> > + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
>
> Adding the function prototype to the documentation is still a good idea :-)
Will do.
> > + v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
> > + V4L2_CID_CONTRAST, 0, 255, 1, 128);
> > + ...
> > + if (foo->ctrl_handler.error) {
> > + int err = foo->ctrl_handler.error;
> > +
> > + v4l2_ctrl_handler_free(&foo->ctrl_handler);
> > + 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.
> > +
> > +The v4l2_ctrl_new_std function will fill in most fields based on the
> > control
> > +ID except for the min, max, step and default values. These are passed in
> > the
> > +last four arguments. These values are driver specific while control
> > attributes
> > +like type, name, flags are all global. The control's current value will be
> > set
> > +to the default value.
> > +
> > +Note that if something fails, the function will return NULL or an error
> > and
> > +set ctrl_handler->error to the error code. If ctrl_handler->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.
>
> You will find me a bit annoying, but if standard controls could be initialized
> using structures, we could pass a pointer to an array to a single control add
> function :-) There would be less code in drivers, and they would be easier to
> read.
It's not at all annoying :-)
I hadn't thought of creating a function to initialize multiple controls
from one array. That's actually a nice idea. I'll look into this.
The same idea can be used for custom controls as well.
> > +3) Optionally force initial control setup:
> > +
> > + v4l2_ctrl_handler_setup(&foo->ctrl_handler);
> > +
> > +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.
>
> Copying my comments from the previous review, I think they got lost somewhere
> (or I have lost your answer to them).
>
> What should be recommended, calling v4l2_ctrl_handler_setup, or initializing
> the framework with the current value ?
Calling v4l2_ctrl_handler_setup is recommended. It's easy and guarantees that
the controls and the hardware are in sync. I think that is what I do for all
drivers I have converted until now. UVC will be one of the drivers where this
is not a suitable call, though.
For UVC you will probably want to use the init callback and/or initialize 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.
Use the init callback for that. There you can fill in both the boundaries and
the initial current value. It's called only once when the control is referenced
for the first time by the application.
> 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 ?
Currently it depends on whether the g_volatile_ctrl op is set. If set, then
that is called, otherwise it will use the cached value. But see my notes on
g_ctrl below.
> 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 ?
The framework will not do a read of the control. The reason is that that is
pointless. There are two situations:
1) The control values are cached (i.e. none of the controls are volatile). In
that case you can just use the cached values since it will be up to date.
2) The control values are volatile. In that (very unlikely) case you need to
do some sort of atomic read-update-write operation and that needs to be done
when you set the control. Having the framework do a g_ctrl first before calling
s_ctrl is hardly atomic so that won't work anyway.
> > +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,
> > ctrl_handler); +
> > + 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.
> > +
> > +==========================================================================
> > =====
> > +
> > +
> > +Inheriting Controls
> > +===================
> > +
> > +When a sub-device is registered with a bridge driver by calling
> > +v4l2_device_register_subdev() 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).
> > +
> > +What happens here is that v4l2_device_register_subdev() calls
> > +v4l2_ctrl_add_handler() adding the controls of the subdev to the controls
> > +of v4l2_device.
> > +
> > +
> > +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.
>
> Does the string controls spec require the maximum length to be fixed, or can
> it vary ?
No, that maximum length is fixed.
> > +In most cases 'cur' contains the current cached control value. When you
> > create
> > +a new control this value is made identical to the default value. After
> > calling
> > +v4l2_ctrl_handler_setup() this value is passed to the hardware. It is
> > generally
> > +a good idea to call this function.
> > +
> > +Whenever a new value is set that new value is automatically cached. This
> > means
> > +that most drivers do not need to implement the g_volatile_ctrl() op. The
> > +exception is for controls that return a volatile register such as a signal
> > +strength read-out that changes continuously. In that case you will need to
> > +implement g_volatile_ctrl like this:
> > +
> > + ctrl->cur.val = read_reg(0x123);
>
> Do we really need to call the operation g_volatile_ctrl ? I was OK with
> g_ctrl.
>
> If some controls are volatile and others are not, you said that the operation
> can ignore non-volatile controls. How does it do so ? By just returning
> success ? It might be worth mentioning it in the doc.
Correct.
> Another comment from my previous review for which I haven't seen an answer:
>
> 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).
First I want to clarify something: the only time g_volatile_ctrl is needed
is to get the value of a volatile control. There is no 'first time g_ctrl'.
If you need that, then use the init op. This is why I renamed it to
g_volatile_ctrl to make that use explicit.
The majority of the drivers can just create the controls with a good default
value and call the setup function. No need to implement a g_ctrl. That makes
life very easy for driver developers.
Now, if you do have volatile controls, then I envisaged that you make two
v4l2_ctrl_ops structs: one with and one without g_volatile_ctrl. The first ops
struct is passed to volatile controls, and the second to non-volatile controls.
The presence of the g_volatile_ctrl op would decide whether or not the
framework would call that function.
However, both from your comments and my own experience I am beginning to realize
that that is probably too confusing and an is_volatile bit controlling whether or
not the g_volatile_ctrl op is called is better. So I will change that. I will
also introduce a needs_init flag which controls whether the .init needs to be
called on first use.
I prefer to keep the g_volatile_ctrl name, since this is really only for
volatile controls. And the name is even more appropriate when the is_volatile
bit is introduced.
But this makes another change possible and I'd like your feedback on this.
At the moment the ops struct is set per-control. The idea is that this makes
it possible if desired to have per-control s/g/try functions. With these flags
I can move the ops struct to the control handler instead. But that means that
the control ops always consist of a big switch and that it is no longer possible
to write small ops functions for specific controls, so you are less flexible.
I am not sure about this, but it will be interesting to hear your opinion.
> > +The 'new value' union is not relevant in g_volatile_ctrl. In general
> > controls
> > +that need to implement g_volatile_ctrl are read-only controls.
> > +
> > +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.
>
> Instead of playing with cur and new, wouldn't it be less confusing to pass a
> pointer to the new value as an argument to s_ctrl (and a pointer to the
> current value as an argument to g_ctrl) ?
Nice idea, but that doesn't work for clustered controls.
> > +While in g_volatile/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.
>
> Can we access all fields of other controls in s_ctrl, or just the value ? More
> specifically, can s_ctrl on an auto control modify the enabled flag on the
> associated manual control ?
All fields are accessible.
> > +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 from
> > +within the driver:
> > +
> > + s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
> > + int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
> > +
> > +These functions go through the control framework just as VIDIOC_G/S_CTRL
> > ioctls
> > +do. Don't use these inside the control ops g_volatile/s/try_ctrl, though,
> > that
> > +will fail since these helpers lock the handler.
>
> This sounds a bit confusing. Would it be possible to avoid taking the lock in
> v4l2_ctrl_g/s_ctrl if the control belongs to the same handler as the one being
> accessed in g/s/try_ctrl ? Just throwing an idea, not sure if it's a good one,
> but I'm pretty sure it will be confusing to have to access controls directly
> and use the helper functions in drivers depending on which control you access
> and in which context.
One small problem: how do I know that v4l2_ctrl_s_ctrl is called from within
a control op?
>
> > +You can also take the handler lock yourself:
> > +
> > + mutex_lock(&state->ctrl_handler.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->ctrl_handler.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 (see below) 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.
>
> What about renaming the argument to step_or_skip_mask then ? Or make it two
> arguments ?
>
> The comment doesn't seem to apply to v4l2_ctrl_new_custom anymore, as you have
> two different fields there.
I'm going to experiment a bit with these calls. I agree that it needs more work.
Another thing that is currently wrong is that v4l2_ctrl_new_std_menu() doesn't
have a 'max' argument. Instead I calculate it based on the size of the qmenu
array. The problem with that is that if the menu is extended with new values,
then you don't want to have those new values automatically appear in any driver
that uses that menu. The max value must be passed in when the control is created.
> > +The v4l2_ctrl_new_std_menu function 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'.
> > +
> > +
> > +Custom Controls
> > +===============
> > +
> > +Driver specific controls can be created using v4l2_ctrl_new_custom():
> > +
> > + static const struct v4l2_ctrl_config ctrl_filter = {
> > + .ops = &ctrl_custom_ops,
> > + .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
> > + .name = "Spatial Filter",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .flags = V4L2_CTRL_FLAG_SLIDER,
> > + .max = 15,
> > + .step = 1,
> > + };
> > +
> > + ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_filter, NULL);
> > +
> > +The last argument is the priv pointer which can be set to driver-specific
> > +private data.
>
> If I add a standard control using v4l2_ctrl_new_custom, will it fill the name,
> type, ... fields for me ?
No. That's why it is called 'custom'. That said, it would be easy to add
something like that. E.g. if the name is NULL, then assume it is a std control
and fill in the field accordingly.
But is that sensible or featurism?
> > +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.
>
> Shouldn't you mention that the driver needs to take the handler lock if it
> wants to (de)activate a control belonging to another handler, or if it wants
> to do it outside the s_ctrl callback ?
I just realized I have a much better idea: if I make the v4l2_ctrl flags field
an unsigned long, then I can use the atomic bit ops, thus avoiding the whole
problem of locking.
> > +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.
>
> This makes v4l2_ctrl_activate and v4l2_ctrl_grab behave differently. It might
> be confusing.
Should be fixed when I use the atomic bit ops.
> > +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 ctrl_handler;
> > +#define AUDIO_CL_VOLUME (0)
> > +#define AUDIO_CL_MUTE (1)
> > + struct v4l2_ctrl *audio_cluster[2];
> > + ...
> > + };
> > +
> > + state->audio_cluster[AUDIO_CL_VOLUME] =
> > + v4l2_ctrl_new_std(&state->ctrl_handler, ...);
> > + state->audio_cluster[AUDIO_CL_MUTE] =
> > + v4l2_ctrl_new_std(&state->ctrl_handler, ...);
> > + v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);
> > +
> > +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 audio_cluster:
> > +
> > + static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
> > + {
> > + struct foo *state = container_of(ctrl->handler, struct foo,
> > ctrl_handler); +
> > + switch (ctrl->id) {
> > + case V4L2_CID_AUDIO_VOLUME: {
> > + struct v4l2_ctrl *mute = ctrl->cluster[AUDIO_CL_MUTE];
> > +
> > + write_reg(0x123, 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 == ctrl->cluster[AUDIO_CL_VOLUME] ==
> > state->audio_cluster[AUDIO_CL_VOLUME]
> > + ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]
> > +
> > +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.
>
> The driver needs to set state->audio_cluster[AUDIO_CL_MUTE] to NULL
> explicitly, right ? In that case I think you should mention it.
Good point.
> > 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 always 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(ctrl_handler, 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. You can do that by manually setting the ctrl_handler
> > +field of struct video_device.
> > +
> > +That is 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. Now v4l2_device_register_subdev() will no
> > longer
> > +merge subdev controls.
> > +
> > +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
> > +control handler. This control handler may be specific to the video_device
> > or
> > +for a subset of video_device's. For example: the radio device nodes only
> > have
> > +audio controls, while the video and vbi device nodes share the same control
> > +handler for the audio and video controls.
> > +
> > +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 should 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_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_VOLUME,
> > ...);
> > + v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE,
> > ...);
> > + v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS,
> > ...);
> > + v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST,
> > ...);
> > + v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler);
> > +
> > +Or you can add specific controls to a handler:
> > +
> > + volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops,
> > V4L2_CID_AUDIO_VOLUME, ...);
> > + v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
> > + v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
> > + v4l2_ctrl_add_ctrl(&radio_ctrl_handler, volume);
> > +
> > +What you should not do is make two identical controls for two handlers.
> > +For example:
> > +
> > + v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE,
> > ...);
> > + v4l2_ctrl_new_std(&video_ctrl_handler, &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 ctrl_handler;
> > +
> > + v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
> > + v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
> > +
> > +...and in video_ops.s_ctrl:
> > +
> > + case V4L2_CID_BRIGHTNESS:
> > + contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
> > + ...
> > +
> > +When s_ctrl is called by the framework the ctrl_handler.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.
>
> Doesn't that call for an unlocked version of the find function ?
Normally you will store the pointer to the control directly in the driver's
state struct. Why try to find it when you have direct access to the control
through the pointer?
But should there be a need for this, then an unlocked version can be added.
It actually exists already internally.
> > +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->ctrl_handler, ...);
> > + if (ctrl)
> > + ctrl->is_private = 1;
> > +
> > +These controls will now be skipped when v4l2_ctrl_add_handler is called.
>
> One more unresolved comment from the previous review:
>
> > > 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.
>
> "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.
I will look at this. I will definitely add this to struct v4l2_ctrl_config.
> > +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.
> > +
> > +
> > +Initializing Controls
> > +=====================
> > +
> > +Usually controls are initialized when you call one of the
> > v4l2_ctrl_new_*()
> > +functions. But sometimes you do not know all the details of the control's
> > +boundaries and possibly even type at this stage. Or obtaining those details
> > +may require expensive hardware accesses.
> > +
> > +In that case you can specify a .init op in struct v4l2_ctrl_ops. This op
> > +returns a void, so it may not fail. Should the init run into problems,
> > then
> > +it should disable the control by setting V4L2_CTRL_FLAG_DISABLED.
>
> If it can fail, why does it return void ? The operation will fail from times
> to times with buggy UVC devices. It's better to return an error to userspace
> rather than silently disabling the control. I can already see support e-mails
> asking why a control suddenly becomes disabled.
What is userspace supposed to do when this fails? That was the reason for
making this a void initially: I have no idea what to do with it when it fails.
But I'll see if I can change this.
> > +The init op can be used to setup the type and boundaries of the control on
> > +first use. Any controls with a .init op will be skipped by
> > +v4l2_ctrl_handler_setup().
> > +
> > +
> > +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.
>
> What about non-button write-only controls, are they handled the same way ?
According to the spec they should already return -EACCES when an attempt is
made to read them.
> > +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.
>
> Shown where ?
In v4l2-ctl, qv4l2 and any other GUI.
> > +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.
>
>
Again, thank you very much for this review. It gave me several good ideas.
Regards,
Hans
--
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH 03/15] [RFCv2] Documentation: add v4l2-controls.txt documenting the new controls API.
2010-05-24 9:44 ` Hans Verkuil
@ 2010-05-24 13:43 ` Hans Verkuil
0 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-24 13:43 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: linux-media
On Monday 24 May 2010 11:44:16 Hans Verkuil wrote:
> Hi Laurent,
>
> Thanks for your review! As always, it was very useful.
I've incorporated most points in my ctrlfw3 branch:
http://git.linuxtv.org/hverkuil/v4l-dvb.git?a=shortlog;h=refs/heads/ctrlfw3
Main changes:
- Replaced 'bridge driver' by 'V4L2 driver'.
- Added is_volatile and is_uninitialized flags (to control whether g_volatile_ctrl or
init should be called).
- Added is_volatile, is_uninitialized and is_private flags to v4l2_ctrl_config.
- If the name field in struct v4l2_ctrl_config is NULL, then assume it is a standard
control and fill in the defaults accordingly.
These two changes together make it possible to make an array of struct v4l2_ctrl_config
to create all controls. Perhaps v4l2_ctrl_new_custom should be renamed to v4l2_ctrl_new?
- v4l2_ctrl_new_std_menu now has a 'max' argument.
- v4l2_ctrl_new_std can no longer be used to create a standard menu control.
This should prevent confusion regarding step and skip_mask.
- v4l2_ctrl_active and v4l2_ctrl_grab set the flag atomically, so can be called
from anywhere.
I did not yet change anything regarding the init return type. I'm really not
sure what userspace is supposed to do here. If you fail initializing a control,
does that mean that -EIO should be returned? The only way this can fail is if
there is a hardware problem, right?
Can you give some background info on how this is currently handled in uvc?
Regards,
Hans
--
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 04/15] [RFCv2] v4l2: hook up the new control framework into the core framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (2 preceding siblings ...)
2010-05-16 13:20 ` [PATCH 03/15] [RFCv2] Documentation: add v4l2-controls.txt documenting the new controls API Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 05/15] [RFCv2] saa7115: convert to the new control framework Hans Verkuil
` (11 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Add the calls needed to automatically merge subdev controls into a bridge
control handler.
Hook up the control framework in __video_ioctl2 and video_register_device.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
drivers/media/video/v4l2-dev.c | 8 +++++-
drivers/media/video/v4l2-device.c | 7 +++++
drivers/media/video/v4l2-ioctl.c | 46 ++++++++++++++++++++++++++----------
3 files changed, 46 insertions(+), 15 deletions(-)
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 0ca7ec9..773ffe1 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -447,8 +447,12 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
vdev->vfl_type = type;
vdev->cdev = NULL;
- if (vdev->v4l2_dev && vdev->v4l2_dev->dev)
- vdev->parent = vdev->v4l2_dev->dev;
+ if (vdev->v4l2_dev) {
+ if (vdev->v4l2_dev->dev)
+ vdev->parent = vdev->v4l2_dev->dev;
+ if (vdev->ctrl_handler == NULL)
+ vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
+ }
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
index 5a7dc4a..0b08f96 100644
--- a/drivers/media/video/v4l2-device.c
+++ b/drivers/media/video/v4l2-device.c
@@ -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);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 0395b1c..94776c3 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -25,6 +25,7 @@
#endif
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-chip-ident.h>
@@ -1258,9 +1259,12 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_queryctrl *p = arg;
- if (!ops->vidioc_queryctrl)
+ if (vfd->ctrl_handler)
+ ret = v4l2_queryctrl(vfd->ctrl_handler, p);
+ else if (ops->vidioc_queryctrl)
+ ret = ops->vidioc_queryctrl(file, fh, p);
+ else
break;
- ret = ops->vidioc_queryctrl(file, fh, p);
if (!ret)
dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
"step=%d, default=%d, flags=0x%08x\n",
@@ -1275,7 +1279,9 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_control *p = arg;
- if (ops->vidioc_g_ctrl)
+ if (vfd->ctrl_handler)
+ ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
+ else if (ops->vidioc_g_ctrl)
ret = ops->vidioc_g_ctrl(file, fh, p);
else if (ops->vidioc_g_ext_ctrls) {
struct v4l2_ext_controls ctrls;
@@ -1305,11 +1311,16 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl;
- if (!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
+ if (!vfd->ctrl_handler &&
+ !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
break;
dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+ if (vfd->ctrl_handler) {
+ ret = v4l2_s_ctrl(vfd->ctrl_handler, p);
+ break;
+ }
if (ops->vidioc_s_ctrl) {
ret = ops->vidioc_s_ctrl(file, fh, p);
break;
@@ -1331,10 +1342,12 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!ops->vidioc_g_ext_ctrls)
- break;
- if (check_ext_ctrls(p, 0))
+ if (vfd->ctrl_handler)
+ ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+ else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0))
ret = ops->vidioc_g_ext_ctrls(file, fh, p);
+ else
+ break;
v4l_print_ext_ctrls(cmd, vfd, p, !ret);
break;
}
@@ -1343,10 +1356,12 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!ops->vidioc_s_ext_ctrls)
+ if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls)
break;
v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (check_ext_ctrls(p, 0))
+ if (vfd->ctrl_handler)
+ ret = v4l2_s_ext_ctrls(vfd->ctrl_handler, p);
+ else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_s_ext_ctrls(file, fh, p);
break;
}
@@ -1355,10 +1370,12 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!ops->vidioc_try_ext_ctrls)
+ if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls)
break;
v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (check_ext_ctrls(p, 0))
+ if (vfd->ctrl_handler)
+ ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+ else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_try_ext_ctrls(file, fh, p);
break;
}
@@ -1366,9 +1383,12 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_querymenu *p = arg;
- if (!ops->vidioc_querymenu)
+ if (vfd->ctrl_handler)
+ ret = v4l2_querymenu(vfd->ctrl_handler, p);
+ else if (ops->vidioc_querymenu)
+ ret = ops->vidioc_querymenu(file, fh, p);
+ else
break;
- ret = ops->vidioc_querymenu(file, fh, p);
if (!ret)
dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
p->id, p->index, p->name);
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 05/15] [RFCv2] saa7115: convert to the new control framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (3 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 04/15] [RFCv2] v4l2: hook up the new control framework into the core framework Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 06/15] [RFCv2] msp3400: " Hans Verkuil
` (10 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
drivers/media/video/saa7115.c | 180 ++++++++++++++++++-----------------------
1 files changed, 80 insertions(+), 100 deletions(-)
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 53b6fcd..4f97c3e 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -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_volatile_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;
}
@@ -1229,25 +1193,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);
@@ -1524,17 +1469,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_volatile_ctrl = saa711x_g_volatile_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_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = saa711x_s_std,
.reset = saa711x_reset,
.s_gpio = saa711x_s_gpio,
@@ -1586,8 +1541,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;
@@ -1626,15 +1582,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;
@@ -1682,6 +1660,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),
@@ -1696,6 +1675,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
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 06/15] [RFCv2] msp3400: convert to the new control framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (4 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 05/15] [RFCv2] saa7115: convert to the new control framework Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 07/15] [RFCv2] saa717x: " Hans Verkuil
` (9 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
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(-)
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index e9df3cb..0e41213 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -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_ctrl(state->volume, v4l2_ctrl_g_ctrl(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_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_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;
}
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h
index d6b3e6d..cba4559 100644
--- a/drivers/media/video/msp3400-driver.h
+++ b/drivers/media/video/msp3400-driver.h
@@ -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 */
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c
index 168bca7..107d9c6 100644
--- a/drivers/media/video/msp3400-kthreads.c
+++ b/drivers/media/video/msp3400-kthreads.c
@@ -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))
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 07/15] [RFCv2] saa717x: convert to the new control framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (5 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 06/15] [RFCv2] msp3400: " Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 08/15] [RFCv2] cx25840/ivtv: replace ugly priv control with s_config Hans Verkuil
` (8 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
drivers/media/video/saa717x.c | 323 ++++++++++------------------------------
1 files changed, 81 insertions(+), 242 deletions(-)
diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c
index 6818df5..d551411 100644
--- a/drivers/media/video/saa717x.c
+++ b/drivers/media/video/saa717x.c
@@ -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_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_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;
}
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 08/15] [RFCv2] cx25840/ivtv: replace ugly priv control with s_config
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (6 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 07/15] [RFCv2] saa717x: " Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 09/15] [RFCv2] cx25840: convert to the new control framework Hans Verkuil
` (7 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
The 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.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
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(-)
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 8b6fb35..528bd00 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -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;
@@ -1595,10 +1587,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,
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index 04393b9..32ab9d5 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -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;
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 1b79475..85aab0e 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -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;
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index a5b92d1..d391bbd 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -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);
diff --git a/include/media/cx25840.h b/include/media/cx25840.h
index 0b0cb17..df28412 100644
--- a/include/media/cx25840.h
+++ b/include/media/cx25840.h
@@ -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
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 09/15] [RFCv2] cx25840: convert to the new control framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (7 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 08/15] [RFCv2] cx25840/ivtv: replace ugly priv control with s_config Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 10/15] [RFCv2] cx2341x: convert to the " Hans Verkuil
` (6 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
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(-)
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index 45608d5..6faad34 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -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,
+};
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 528bd00..d88605b 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -909,94 +909,29 @@ static int set_v4lstd(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct cx25840_state *state = to_state(sd);
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l_err(client, "invalid brightness setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x414, ctrl->value - 128);
+ cx25840_write(client, 0x414, ctrl->val - 128);
break;
case V4L2_CID_CONTRAST:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid contrast setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x415, ctrl->value << 1);
+ cx25840_write(client, 0x415, ctrl->val << 1);
break;
case V4L2_CID_SATURATION:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid saturation setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x420, ctrl->value << 1);
- cx25840_write(client, 0x421, ctrl->value << 1);
+ cx25840_write(client, 0x420, ctrl->val << 1);
+ cx25840_write(client, 0x421, ctrl->val << 1);
break;
case V4L2_CID_HUE:
- if (ctrl->value < -128 || ctrl->value > 127) {
- v4l_err(client, "invalid hue setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x422, ctrl->value);
+ cx25840_write(client, 0x422, ctrl->val);
break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_MUTE:
- if (is_cx2583x(state))
- return -EINVAL;
- return cx25840_audio_s_ctrl(sd, ctrl);
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct cx25840_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = (s8)cx25840_read(client, 0x414) + 128;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = cx25840_read(client, 0x415) >> 1;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = cx25840_read(client, 0x420) >> 1;
- break;
- case V4L2_CID_HUE:
- ctrl->value = (s8)cx25840_read(client, 0x422);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_MUTE:
- if (is_cx2583x(state))
- return -EINVAL;
- return cx25840_audio_g_ctrl(sd, ctrl);
default:
return -EINVAL;
}
@@ -1171,8 +1106,6 @@ static void log_audio_status(struct i2c_client *client)
default: p = "not defined";
}
v4l_info(client, "Detected audio standard: %s\n", p);
- v4l_info(client, "Audio muted: %s\n",
- (state->unmute_volume >= 0) ? "yes" : "no");
v4l_info(client, "Audio microcontroller: %s\n",
(download_ctl & 0x10) ?
((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
@@ -1389,40 +1322,6 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- struct cx25840_state *state = to_state(sd);
-
- 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);
- default:
- break;
- }
- if (is_cx2583x(state))
- return -EINVAL;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535,
- 65535 / 100, state->default_volume);
- case V4L2_CID_AUDIO_MUTE:
- 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 -EINVAL;
-}
-
static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct cx25840_state *state = to_state(sd);
@@ -1584,6 +1483,7 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
log_video_status(client);
if (!is_cx2583x(state))
log_audio_status(client);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
@@ -1603,13 +1503,21 @@ static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void *platform_data
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops cx25840_ctrl_ops = {
+ .s_ctrl = cx25840_s_ctrl,
+};
+
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,
- .queryctrl = cx25840_queryctrl,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = cx25840_s_std,
.reset = cx25840_reset,
.load_fw = cx25840_load_fw,
@@ -1699,6 +1607,7 @@ static int cx25840_probe(struct i2c_client *client,
{
struct cx25840_state *state;
struct v4l2_subdev *sd;
+ int default_volume;
u32 id = V4L2_IDENT_NONE;
u16 device_id;
@@ -1742,6 +1651,7 @@ static int cx25840_probe(struct i2c_client *client,
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
+
switch (id) {
case V4L2_IDENT_CX23885_AV:
v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
@@ -1786,12 +1696,48 @@ static int cx25840_probe(struct i2c_client *client,
state->audclk_freq = 48000;
state->pvr150_workaround = 0;
state->audmode = V4L2_TUNER_MODE_LANG1;
- state->unmute_volume = -1;
- state->default_volume = 228 - cx25840_read(client, 0x8d4);
- state->default_volume = ((state->default_volume / 2) + 23) << 9;
state->vbi_line_offset = 8;
state->id = id;
state->rev = device_id;
+ v4l2_ctrl_handler_init(&state->hdl, 9);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ if (!is_cx2583x(state)) {
+ default_volume = 228 - cx25840_read(client, 0x8d4);
+ default_volume = ((default_volume / 2) + 23) << 9;
+
+ state->volume = v4l2_ctrl_new_std(&state->hdl,
+ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
+ 0, 65335, 65535 / 100, default_volume);
+ state->mute = v4l2_ctrl_new_std(&state->hdl,
+ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BASS,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE,
+ 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(2, &state->volume);
+ v4l2_ctrl_handler_setup(&state->hdl);
return 0;
}
@@ -1799,9 +1745,11 @@ static int cx25840_probe(struct i2c_client *client,
static int cx25840_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index 32ab9d5..0b8f808 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -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 */
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 10/15] [RFCv2] cx2341x: convert to the control framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (8 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 09/15] [RFCv2] cx25840: convert to the new control framework Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 11/15] [RFCv2] wm8775: convert to the new " Hans Verkuil
` (5 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Since 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.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
drivers/media/video/cx2341x.c | 734 ++++++++++++++++++++++++++++++++++-------
include/media/cx2341x.h | 81 +++++
2 files changed, 703 insertions(+), 112 deletions(-)
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index 022b480..ac2ebb5 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/video/cx2341x.c
@@ -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,477 @@ 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)
+{
+ struct v4l2_ctrl_config cfg;
+
+ cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags);
+ cfg.ops = &cx2341x_ops;
+ cfg.id = id;
+ cfg.min = min;
+ cfg.max = max;
+ cfg.def = def;
+ if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+ cfg.step = 0;
+ cfg.menu_skip_mask = step;
+ cfg.qmenu = cx2341x_get_menu(id);
+ } else {
+ cfg.step = step;
+ cfg.menu_skip_mask = 0;
+ }
+ return v4l2_ctrl_new_custom(hdl, &cfg, 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_ctrl(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);
diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h
index 9ebe855..91cdd07 100644
--- a/include/media/cx2341x.h
+++ b/include/media/cx2341x.h
@@ -19,6 +19,8 @@
#ifndef CX2341X_H
#define CX2341X_H
+#include <media/v4l2-ctrls.h>
+
enum cx2341x_port {
CX2341X_PORT_MEMORY = 0,
CX2341X_PORT_STREAMING = 1,
@@ -99,6 +101,85 @@ int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p);
void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix);
+struct cx2341x_handler;
+
+struct cx2341x_handler_ops {
+ /* needed for the video clock freq */
+ int (*s_audio_sampling_freq)(struct cx2341x_handler *hdl, u32 val);
+ /* needed for dualwatch */
+ int (*s_audio_mode)(struct cx2341x_handler *hdl, u32 val);
+ /* needed for setting up the video resolution */
+ int (*s_video_encoding)(struct cx2341x_handler *hdl, u32 val);
+ /* needed for setting up the sliced vbi insertion data structures */
+ int (*s_stream_vbi_fmt)(struct cx2341x_handler *hdl, u32 val);
+};
+
+struct cx2341x_handler {
+ u32 capabilities;
+ enum cx2341x_port port;
+ u16 width;
+ u16 height;
+ u16 is_50hz;
+ u32 audio_properties;
+
+ struct v4l2_ctrl_handler hdl;
+ void *priv;
+ cx2341x_mbox_func func;
+ const struct cx2341x_handler_ops *ops;
+
+ struct v4l2_ctrl *stream_vbi_fmt;
+
+ /* audio cluster */
+ struct v4l2_ctrl *audio_sampling_freq;
+ struct v4l2_ctrl *audio_encoding;
+ struct v4l2_ctrl *audio_l2_bitrate;
+ struct v4l2_ctrl *audio_mode;
+ struct v4l2_ctrl *audio_mode_extension;
+ struct v4l2_ctrl *audio_emphasis;
+ struct v4l2_ctrl *audio_crc;
+ struct v4l2_ctrl *audio_ac3_bitrate;
+
+ /* video gop cluster */
+ struct v4l2_ctrl *video_b_frames;
+ struct v4l2_ctrl *video_gop_size;
+
+ /* stream type cluster */
+ struct v4l2_ctrl *stream_type;
+ struct v4l2_ctrl *video_encoding;
+ struct v4l2_ctrl *video_bitrate_mode;
+ struct v4l2_ctrl *video_bitrate;
+ struct v4l2_ctrl *video_bitrate_peak;
+
+ /* video mute cluster */
+ struct v4l2_ctrl *video_mute;
+ struct v4l2_ctrl *video_mute_yuv;
+
+ /* video filter mode cluster */
+ struct v4l2_ctrl *video_spatial_filter_mode;
+ struct v4l2_ctrl *video_temporal_filter_mode;
+ struct v4l2_ctrl *video_median_filter_type;
+
+ /* video filter type cluster */
+ struct v4l2_ctrl *video_luma_spatial_filter_type;
+ struct v4l2_ctrl *video_chroma_spatial_filter_type;
+
+ /* video filter cluster */
+ struct v4l2_ctrl *video_spatial_filter;
+ struct v4l2_ctrl *video_temporal_filter;
+
+ /* video median cluster */
+ struct v4l2_ctrl *video_luma_median_filter_top;
+ struct v4l2_ctrl *video_luma_median_filter_bottom;
+ struct v4l2_ctrl *video_chroma_median_filter_top;
+ struct v4l2_ctrl *video_chroma_median_filter_bottom;
+};
+
+int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
+ unsigned nr_of_controls_hint);
+void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz);
+int cx2341x_handler_setup(struct cx2341x_handler *cxhdl);
+void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy);
+
/* Firmware names */
#define CX2341X_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw"
/* Decoder firmware for the cx23415 only */
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 11/15] [RFCv2] wm8775: convert to the new control framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (9 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 10/15] [RFCv2] cx2341x: convert to the " Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 12/15] [RFCv2] cs53l32a: convert to " Hans Verkuil
` (4 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
drivers/media/video/wm8775.c | 79 ++++++++++++++++++++++++++---------------
1 files changed, 50 insertions(+), 29 deletions(-)
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index f1f261a..09a9663 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -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_ctrl(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_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_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;
}
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 12/15] [RFCv2] cs53l32a: convert to new control framework.
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (10 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 11/15] [RFCv2] wm8775: convert to the new " Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 13/15] [RFCv2] wm8739: convert to the " Hans Verkuil
` (3 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
drivers/media/video/cs53l32a.c | 107 +++++++++++++++++++++++++--------------
1 files changed, 68 insertions(+), 39 deletions(-)
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
index 80bca8d..a46b1d0 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/video/cs53l32a.c
@@ -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_id
static 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_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_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;
}
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 13/15] [RFCv2] wm8739: convert to the new control framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (11 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 12/15] [RFCv2] cs53l32a: convert to " Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:21 ` [PATCH 14/15] [RFCv2] ivtv: convert gpio subdev to " Hans Verkuil
` (2 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
drivers/media/video/wm8739.c | 176 ++++++++++++++----------------------------
1 files changed, 59 insertions(+), 117 deletions(-)
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
index b572ce2..af4e61f 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/video/wm8739.c
@@ -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_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_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;
}
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 14/15] [RFCv2] ivtv: convert gpio subdev to new control framework.
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (12 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 13/15] [RFCv2] wm8739: convert to the " Hans Verkuil
@ 2010-05-16 13:21 ` Hans Verkuil
2010-05-16 13:22 ` [PATCH 15/15] [RFCv2] ivtv: convert to the " Hans Verkuil
2010-05-23 22:03 ` [PATCH 00/15] [RFCv2] [RFC] New control handling framework Laurent Pinchart
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:21 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
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(-)
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 85aab0e..1232d92 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -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);
}
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 584c8e0..ba6c2fc 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -629,6 +629,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 */
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index aede061..8f0d077 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -24,6 +24,7 @@
#include "ivtv-gpio.h"
#include "tuner-xc2028.h"
#include <media/tuner.h>
+#include <media/v4l2-ctrls.h>
/*
* GPIO assignment of Yuan MPG600/MPG160
@@ -149,16 +150,10 @@ static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd)
return container_of(sd, struct ivtv, sd_gpio);
}
-static struct v4l2_queryctrl gpio_ctrl_mute = {
- .id = V4L2_CID_AUDIO_MUTE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = 0,
-};
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct ivtv, hdl_gpio)->sd_gpio;
+}
static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
@@ -262,40 +257,24 @@ static int subdev_s_audio_routing(struct v4l2_subdev *sd,
return 0;
}
-static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int subdev_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct ivtv *itv = sd_to_ivtv(sd);
u16 mask, data;
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = itv->card->gpio_audio_mute.mute;
- ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
- return 0;
-}
-
-static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct ivtv *itv = sd_to_ivtv(sd);
- u16 mask, data;
-
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
- if (mask)
- write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ mask = itv->card->gpio_audio_mute.mask;
+ data = ctrl->val ? itv->card->gpio_audio_mute.mute : 0;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) |
+ (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+ }
+ return -EINVAL;
}
-static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- if (qc->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- *qc = gpio_ctrl_mute;
- return 0;
-}
static int subdev_log_status(struct v4l2_subdev *sd)
{
@@ -304,6 +283,7 @@ static int subdev_log_status(struct v4l2_subdev *sd)
IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
read_reg(IVTV_REG_GPIO_IN));
+ v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name);
return 0;
}
@@ -327,11 +307,19 @@ static int subdev_s_video_routing(struct v4l2_subdev *sd,
return 0;
}
+static const struct v4l2_ctrl_ops gpio_ctrl_ops = {
+ .s_ctrl = subdev_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops subdev_core_ops = {
.log_status = subdev_log_status,
- .g_ctrl = subdev_g_ctrl,
- .s_ctrl = subdev_s_ctrl,
- .queryctrl = subdev_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
@@ -375,5 +363,12 @@ int ivtv_gpio_init(struct ivtv *itv)
v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+ v4l2_ctrl_handler_init(&itv->hdl_gpio, 1);
+ v4l2_ctrl_new_std(&itv->hdl_gpio, &gpio_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ if (itv->hdl_gpio.error)
+ return itv->hdl_gpio.error;
+ itv->sd_gpio.ctrl_handler = &itv->hdl_gpio;
+ v4l2_ctrl_handler_setup(&itv->hdl_gpio);
return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio);
}
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 15/15] [RFCv2] ivtv: convert to the new control framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (13 preceding siblings ...)
2010-05-16 13:21 ` [PATCH 14/15] [RFCv2] ivtv: convert gpio subdev to " Hans Verkuil
@ 2010-05-16 13:22 ` Hans Verkuil
2010-05-23 22:03 ` [PATCH 00/15] [RFCv2] [RFC] New control handling framework Laurent Pinchart
15 siblings, 0 replies; 22+ messages in thread
From: Hans Verkuil @ 2010-05-16 13:22 UTC (permalink / raw)
To: linux-media; +Cc: laurent.pinchart
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
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(-)
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
index 4a9c8ce..57f74c2 100644
--- a/drivers/media/video/ivtv/ivtv-controls.c
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -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 fm
return 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,
+};
diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h
index 1c7721e..d12893d 100644
--- a/drivers/media/video/ivtv/ivtv-controls.h
+++ b/drivers/media/video/ivtv/ivtv-controls.h
@@ -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
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 1232d92..54f25f3 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -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);
}
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index ba6c2fc..2fdbac6 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -61,6 +61,7 @@
#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/tuner.h>
@@ -628,8 +629,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 */
@@ -646,7 +648,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 */
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index abf4109..553b0de 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -149,12 +149,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_ctrl(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))
@@ -163,16 +161,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_ctrl(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)
@@ -883,7 +875,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_ctrl(itv->cxhdl.video_mute) |
+ (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
}
/* Done! Unmute and continue. */
ivtv_unmute(itv);
diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c
index a71e8ba..11ca89a 100644
--- a/drivers/media/video/ivtv/ivtv-firmware.c
+++ b/drivers/media/video/ivtv/ivtv-firmware.c
@@ -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");
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index fa9f0d9..a24cfac 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -162,7 +162,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_ctrl(itv->cxhdl.video_b_frames);
data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
data[5] = 0;
data[6] = 0;
@@ -339,8 +339,8 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
struct 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;
@@ -568,7 +568,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;
@@ -576,15 +575,15 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
if (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_ctrl(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);
@@ -1110,9 +1109,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;
@@ -1153,7 +1153,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;
@@ -1550,7 +1550,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];
@@ -1938,11 +1938,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,
.vidioc_subscribe_event = ivtv_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index de4288c..52a9585 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -209,6 +209,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;
@@ -450,7 +451,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;
@@ -471,7 +471,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_ctrl(itv->cxhdl.audio_mode);
itv->search_pack_header = 0;
break;
@@ -559,12 +559,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_ctrl(itv->cxhdl.video_mute_yuv) << 8));
}
/* Vsync Setup */
@@ -580,6 +580,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);
@@ -616,7 +618,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)
@@ -655,7 +656,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;
@@ -821,6 +822,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);
@@ -938,7 +941,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 */
@@ -959,6 +963,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;
}
--
1.6.4.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH 00/15] [RFCv2] [RFC] New control handling framework
2010-05-16 13:20 [PATCH 00/15] [RFCv2] [RFC] New control handling framework Hans Verkuil
` (14 preceding siblings ...)
2010-05-16 13:22 ` [PATCH 15/15] [RFCv2] ivtv: convert to the " Hans Verkuil
@ 2010-05-23 22:03 ` Laurent Pinchart
15 siblings, 0 replies; 22+ messages in thread
From: Laurent Pinchart @ 2010-05-23 22:03 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media
Hi Hans,
Thanks for the update.
On Sunday 16 May 2010 15:20:43 Hans Verkuil wrote:
> 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.
>
> It is the second version of this framework, incorporating comments from
> Laurent.
>
> Changes compared to the first version:
>
> - Updated the documentation, hopefully making it easier to understand.
> - v4l2_ctrl_new_custom now uses a new v4l2_ctrl_config struct instead of
> a long argument list.
> - v4l2_ctrl_g/s is now renamed to v4l2_ctrl_g/s_ctrl.
> - The v4l2_ctrl.h header now uses kernel doc comments.
> - Removed the 'strict validation' feature.
> - Added a new .init op that allows you to initialize many of the v4l2_ctrl
> fields on first use. Required by uvc.
> - No longer needed to initialize ctrl_handler in struct video_device. It
> will copy the ctrl_handler from struct v4l2_device if needed.
> - Renamed the v4l2_sd_* helper functions to v4l2_subdev_*.
>
> I decided *not* to rename the v4l2_ctrl struct. What does the struct
> describe? A control. Period. So I really don't know what else to call it.
> Every other name I can think of is contrived. It really encapsulates all
> the data and info that describes a control and its state. Yes, it is close
> to struct v4l2_control, but on the other hand any driver that uses this
> framework will no longer use v4l2_control (or v4l2_ext_controls for that
> matter). It will only use v4l2_ctrl. So I do not think there will be much
> cause for confusion here.
OK. It will still be a bit confusing, but renaming the structure might be
worse.
Should we decide on a naming policy for kernel vs. user structures in V4L2 for
new APIs ?
> Anyway, 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).
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 22+ messages in thread