All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/15] [RFC] New control handling framework
@ 2010-04-26  7:33 Hans Verkuil
  2010-04-26  7:33 ` [PATCH 01/15] [RFC] v4l: Add new " Hans Verkuil
                   ` (14 more replies)
  0 siblings, 15 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart

This RFC patch series adds the control handling framework and implements
it in ivtv and all subdev drivers used by ivtv.

It is a bare-bones implementation, so no sysfs or debugfs enhancements.

Any comments are welcome.

Once this is in then we can start migrating all subdev drivers to this
framework, followed by all bridge drivers. Converted subdev drivers can
still be used by unconverted bridge drivers. Once all bridge drivers are
converted the subdev backwards compatibility code can be removed.

The same is true for the cx2341x module: both converted and unconverted
bridge drivers are supported. Once all bridge drivers that use this module
are converted the compat code can be removed from cx2341x (and that will
save about 1060 lines of hard to understand code).

Before this can be merged Laurent Pinchart will have to verify that this
new framework can be used with UVC. Note to Laurent: v4l2_ctrl_new_custom()
allows you to pass a 'void *priv' pointer when creating the control. I'm
pretty sure you need this to associate a V4L control with a UVC data struct.
Should you need it, then it will be trivial to add a flag that will cause
the driver to kfree the priv pointer when the control is freed.

Regards,

	Hans

Hans Verkuil (15):
  v4l: Add new control handling framework
  v4l2-ctrls: reorder 'case' statements to match order in header.
  Documentation: add v4l2-controls.txt documenting the new controls
    API.
  v4l: hook up the new control framework into the core framework
  saa7115: convert to the new control framework
  msp3400: convert to the new control framework
  saa717x: convert to the new control framework
  cx25840/ivtv: replace ugly priv control with s_config
  cx25840: convert to the new control framework
  cx2341x: convert to the control framework
  wm8775: convert to the new control framework
  cs53l32a: convert to new control framework.
  wm8739: convert to the new control framework
  ivtv: convert gpio subdev to new control framework.
  ivtv: convert to the new control framework

 Documentation/video4linux/v4l2-controls.txt |  543 ++++++++
 drivers/media/video/Makefile                |    2 +-
 drivers/media/video/cs53l32a.c              |  107 +-
 drivers/media/video/cx2341x.c               |  725 +++++++++--
 drivers/media/video/cx25840/cx25840-audio.c |  144 +--
 drivers/media/video/cx25840/cx25840-core.c  |  201 ++--
 drivers/media/video/cx25840/cx25840-core.h  |   23 +-
 drivers/media/video/ivtv/ivtv-controls.c    |  275 +----
 drivers/media/video/ivtv/ivtv-controls.h    |    6 +-
 drivers/media/video/ivtv/ivtv-driver.c      |   26 +-
 drivers/media/video/ivtv/ivtv-driver.h      |    4 +-
 drivers/media/video/ivtv/ivtv-fileops.c     |   23 +-
 drivers/media/video/ivtv/ivtv-firmware.c    |    6 +-
 drivers/media/video/ivtv/ivtv-gpio.c        |   77 +-
 drivers/media/video/ivtv/ivtv-i2c.c         |    7 +
 drivers/media/video/ivtv/ivtv-ioctl.c       |   31 +-
 drivers/media/video/ivtv/ivtv-streams.c     |   20 +-
 drivers/media/video/msp3400-driver.c        |  248 ++---
 drivers/media/video/msp3400-driver.h        |   16 +-
 drivers/media/video/msp3400-kthreads.c      |   16 +-
 drivers/media/video/saa7115.c               |  180 ++--
 drivers/media/video/saa717x.c               |  323 ++----
 drivers/media/video/v4l2-common.c           |  472 +-------
 drivers/media/video/v4l2-ctrls.c            | 1793 +++++++++++++++++++++++++++
 drivers/media/video/v4l2-device.c           |    7 +
 drivers/media/video/v4l2-ioctl.c            |   46 +-
 drivers/media/video/wm8739.c                |  176 +--
 drivers/media/video/wm8775.c                |   79 +-
 include/media/cx2341x.h                     |   81 ++
 include/media/cx25840.h                     |   11 +
 include/media/v4l2-ctrls.h                  |  271 ++++
 include/media/v4l2-dev.h                    |    4 +
 include/media/v4l2-device.h                 |    4 +
 include/media/v4l2-subdev.h                 |    3 +
 34 files changed, 4051 insertions(+), 1899 deletions(-)
 create mode 100644 Documentation/video4linux/v4l2-controls.txt
 create mode 100644 drivers/media/video/v4l2-ctrls.c
 create mode 100644 include/media/v4l2-ctrls.h


^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH 01/15] [RFC] v4l: Add new control handling framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
@ 2010-04-26  7:33 ` Hans Verkuil
  2010-05-06 21:54   ` Laurent Pinchart
  2010-04-26  7:33 ` [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header Hans Verkuil
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 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>
---
 drivers/media/video/Makefile      |    2 +-
 drivers/media/video/v4l2-common.c |  472 +----------
 drivers/media/video/v4l2-ctrls.c  | 1789 +++++++++++++++++++++++++++++++++++++
 include/media/v4l2-ctrls.h        |  271 ++++++
 include/media/v4l2-dev.h          |    4 +
 include/media/v4l2-device.h       |    4 +
 include/media/v4l2-subdev.h       |    3 +
 7 files changed, 2077 insertions(+), 468 deletions(-)
 create mode 100644 drivers/media/video/v4l2-ctrls.c
 create mode 100644 include/media/v4l2-ctrls.h

diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index b88b617..6239bb3 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -10,7 +10,7 @@ stkwebcam-objs	:=	stk-webcam.o stk-sensor.o
 
 omap2cam-objs	:=	omap24xxcam.o omap24xxcam-dma.o
 
-videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o
+videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-ctrls.o
 
 # V4L2 core modules
 
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 67944f5..68c6690 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>
@@ -176,480 +177,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",
-		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..442f0c5
--- /dev/null
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -0,0 +1,1789 @@
+/*
+    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",
+		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;
+		if (ctrl->strict_validation && val != ctrl->val)
+			return -ERANGE;
+		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) &&
+		    type_is_int(ref->ctrl)) {
+			if (id == 0)
+				return ref;
+			id--;
+		}
+	}
+	return NULL;
+}
+
+/* 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)
+		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 */
+struct v4l2_ctrl *v4l2_ctrl_new_custom(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 (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;
+}
+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_custom(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) {
+		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) {
+		v4l2_ctrl_lock(ctrl);
+		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->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;
+
+		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;
+		}
+	}
+	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_sd_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+	return v4l2_queryctrl(sd->ctrl_handler, qc);
+}
+EXPORT_SYMBOL(v4l2_sd_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_sd_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
+{
+	return v4l2_querymenu(sd->ctrl_handler, qm);
+}
+EXPORT_SYMBOL(v4l2_sd_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_ctrl will update the current control values */
+		if (master->ops->g_ctrl)
+			ret = master->ops->g_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_sd_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+	return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
+}
+EXPORT_SYMBOL(v4l2_sd_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_ctrl will update the current control values */
+	if (master->ops->g_ctrl)
+		ret = master->ops->g_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_sd_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+	return v4l2_g_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_sd_g_ctrl);
+
+s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl)
+{
+	s32 val = 0;
+
+	/* 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);
+
+
+/* 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_sd_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_sd_try_ext_ctrls);
+
+int v4l2_sd_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_sd_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_sd_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+	return v4l2_s_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_sd_s_ctrl);
+
+int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val)
+{
+	/* It's a driver bug if this happens. */
+	WARN_ON(!type_is_int(ctrl));
+	return set_ctrl(ctrl, &val);
+}
+EXPORT_SYMBOL(v4l2_ctrl_s);
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
new file mode 100644
index 0000000..ea23f3d
--- /dev/null
+++ b/include/media/v4l2-ctrls.h
@@ -0,0 +1,271 @@
+/*
+    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;
+
+/* The control operations that the driver has to provide.
+   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 {
+	/* Get a new value for this control. Generally only relevant for
+	   read-only (i.e. reading the current signal strength).
+	   If not set, then the current value will be returned. */
+	int (*g_ctrl)(struct v4l2_ctrl *ctrl);
+	/* Test whether the control's value is valid. Only relevant when the
+	   usual min/max/step checks are not sufficient. */
+	int (*try_ctrl)(struct v4l2_ctrl *ctrl);
+	/* Actually set the new control value. */
+	int (*s_ctrl)(struct v4l2_ctrl *ctrl);
+};
+
+/* The control structure */
+struct v4l2_ctrl {
+	/* Administrative fields */
+
+	/* The list node */
+	struct list_head node;
+
+	/* The handler that owns the control */
+	struct v4l2_ctrl_handler *handler;
+
+	/* Point to start of cluster array */
+	struct v4l2_ctrl **cluster;
+	/* Number of controls in cluster array */
+	unsigned ncontrols;
+
+	/* Internal flag: set when there is a valid new value */
+	unsigned int has_new:1;
+	/* Internal flag: set for each processed control */
+	unsigned int done:1;
+
+	/* 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. */
+	unsigned int is_private:1;
+	/* If set, then this control is validated strictly and no
+	   attempt will be made to modify the value to bring it in
+	   range. Drivers can set this flag. */
+	unsigned int strict_validation:1;
+
+	/* Control ops */
+	const struct v4l2_ctrl_ops *ops;
+
+	/* Control fields */
+	u32 id;
+	const char *name;
+	enum v4l2_ctrl_type type;
+	s32 minimum, maximum, default_value;
+	/* menu_skip_mask 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. */
+	union {
+		u32 step;
+		u32 menu_skip_mask;
+	};
+	/* 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. */
+	const char **qmenu;
+	u32 flags;
+
+	/* The current control value. */
+	union {
+		s32 val;
+		s64 val64;
+		char *string;
+	} cur;
+
+	/* The new control value. */
+	union {
+		s32 val;
+		s64 val64;
+		char *string;
+	};
+
+	/* For use by the driver */
+	void *priv;
+};
+
+/* The control reference. 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;		/* List node for the sorted list */
+	struct v4l2_ctrl_ref *next;	/* Single-link list node for the hash */
+	struct v4l2_ctrl *ctrl;		/* The actual control information */
+};
+
+/* The control handler keeps track of all the controls. Both the controls
+   owned by the handler and those inherited from other handlers. */
+struct v4l2_ctrl_handler {
+	/* Lock to control access to this handler and its controls */
+	struct mutex lock;
+	/* The list of controls owned by this handler */
+	struct list_head ctrls;
+	/* The list of control references */
+	struct list_head ctrl_refs;
+	/* The last found control reference.
+	   It is common that the same control is needed multiple times,
+	   so this is a simple optimization. */
+	struct v4l2_ctrl_ref *cached;
+	/* Buckets for the hashing. Allows for quick control lookup. */
+	struct v4l2_ctrl_ref **buckets;
+	/* Total number of buckets in the array. */
+	u16 nr_of_buckets;
+	/* The error code of the first failed control addition. */
+	int error;
+};
+
+/* 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);
+
+
+/* Initialize the control handler. */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+			   unsigned nr_of_controls_hint);
+
+/* Free all controls owned by the handler and free the control list.
+   Does nothing if hdl == NULL. */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl);
+
+/* Call the s_ctrl op for all controls belonging to the handler
+   to initialize the hardware to the current control values.
+   Button controls will be skipped, as are read-only controls.
+   If hdl == NULL, then this just returns 0. */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl);
+
+/* Log all controls owned by the handler. 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);
+
+
+/* Allocate and initialize a new custom V4L2 control. 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_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);
+
+/* Allocate and initialize a new standard V4L2 control.
+   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);
+
+/* Helper function for standard menu controls. 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);
+
+/* 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);
+
+/* Add all controls from handler 'add' to handler 'hdl'.
+   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);
+
+
+/* Mark all controls in the cluster as belonging to that cluster. */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls);
+
+
+/* Find a control with the given ID. If hdl == NULL this will return
+   NULL as well. Will lock the handler so don't use from inside v4l2_ctrl_ops. */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id);
+
+/* Make the control active or inactive. This sets or clears the
+   V4L2_CTRL_FLAG_INACTIVE flag. Does nothing if ctrl == NULL. */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active);
+
+/* Mark the control as grabbed or not grabbed. This sets or clears the
+   V4L2_CTRL_FLAG_GRABBED flag. Does nothing if ctrl == NULL.
+   Will lock the ctrl->handler. */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed);
+
+/* Helper functions to lock/unlock the handler associated with the control */
+static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl)
+{
+	mutex_lock(&ctrl->handler->lock);
+}
+
+static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl)
+{
+	mutex_unlock(&ctrl->handler->lock);
+}
+
+
+/* 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_sd_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
+int v4l2_sd_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
+int v4l2_sd_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_sd_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_sd_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_sd_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+int v4l2_sd_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+
+/* Helper functions to set/get a control value from within a driver */
+s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl);
+int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);
+
+#endif
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 2dee938..cc9ed09 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
@@ -66,6 +67,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 0975878..3c26172 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;
 
@@ -399,6 +400,8 @@ struct v4l2_subdev {
 	u32 flags;
 	struct v4l2_device *v4l2_dev;
 	const struct v4l2_subdev_ops *ops;
+	/* The control handler of this subdev. May be NULL. */
+	struct v4l2_ctrl_handler *ctrl_handler;
 	/* name must be unique */
 	char name[V4L2_SUBDEV_NAME_SIZE];
 	/* can be used to group similar subdevs, value is driver-specific */
-- 
1.6.4.2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header.
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
  2010-04-26  7:33 ` [PATCH 01/15] [RFC] v4l: Add new " Hans Verkuil
@ 2010-04-26  7:33 ` Hans Verkuil
  2010-05-02 20:42   ` Laurent Pinchart
  2010-04-26  7:33 ` [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API Hans Verkuil
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 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>
---
 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 442f0c5..0193b2d 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -259,6 +259,7 @@ const char *v4l2_ctrl_get_name(u32 id)
 {
 	switch (id) {
 	/* USER controls */
+	/* Keep the order of the 'case's the same as in videodev2.h! */
 	case V4L2_CID_USER_CLASS: 		return "User Controls";
 	case V4L2_CID_BRIGHTNESS: 		return "Brightness";
 	case V4L2_CID_CONTRAST: 		return "Contrast";
@@ -289,28 +290,37 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_SHARPNESS:		return "Sharpness";
 	case V4L2_CID_BACKLIGHT_COMPENSATION:	return "Backlight Compensation";
 	case V4L2_CID_CHROMA_AGC:		return "Chroma AGC";
-	case V4L2_CID_CHROMA_GAIN:		return "Chroma Gain";
 	case V4L2_CID_COLOR_KILLER:		return "Color Killer";
 	case V4L2_CID_COLORFX:			return "Color Effects";
 	case V4L2_CID_AUTOBRIGHTNESS:		return "Brightness, Automatic";
 	case V4L2_CID_BAND_STOP_FILTER:		return "Band-Stop Filter";
 	case V4L2_CID_ROTATE:			return "Rotate";
 	case V4L2_CID_BG_COLOR:			return "Background Color";
+	case V4L2_CID_CHROMA_GAIN:		return "Chroma Gain";
 
 	/* MPEG controls */
+	/* Keep the order of the 'case's the same as in videodev2.h! */
 	case V4L2_CID_MPEG_CLASS: 		return "MPEG Encoder Controls";
+	case V4L2_CID_MPEG_STREAM_TYPE: 	return "Stream Type";
+	case V4L2_CID_MPEG_STREAM_PID_PMT: 	return "Stream PMT Program ID";
+	case V4L2_CID_MPEG_STREAM_PID_AUDIO: 	return "Stream Audio Program ID";
+	case V4L2_CID_MPEG_STREAM_PID_VIDEO: 	return "Stream Video Program ID";
+	case V4L2_CID_MPEG_STREAM_PID_PCR: 	return "Stream PCR Program ID";
+	case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
+	case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
+	case V4L2_CID_MPEG_STREAM_VBI_FMT:	return "Stream VBI Format";
 	case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
 	case V4L2_CID_MPEG_AUDIO_ENCODING: 	return "Audio Encoding";
 	case V4L2_CID_MPEG_AUDIO_L1_BITRATE: 	return "Audio Layer I Bitrate";
 	case V4L2_CID_MPEG_AUDIO_L2_BITRATE: 	return "Audio Layer II Bitrate";
 	case V4L2_CID_MPEG_AUDIO_L3_BITRATE: 	return "Audio Layer III Bitrate";
-	case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: 	return "Audio AAC Bitrate";
-	case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: 	return "Audio AC-3 Bitrate";
 	case V4L2_CID_MPEG_AUDIO_MODE: 		return "Audio Stereo Mode";
 	case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
 	case V4L2_CID_MPEG_AUDIO_EMPHASIS: 	return "Audio Emphasis";
 	case V4L2_CID_MPEG_AUDIO_CRC: 		return "Audio CRC";
 	case V4L2_CID_MPEG_AUDIO_MUTE: 		return "Audio Mute";
+	case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: 	return "Audio AAC Bitrate";
+	case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: 	return "Audio AC-3 Bitrate";
 	case V4L2_CID_MPEG_VIDEO_ENCODING: 	return "Video Encoding";
 	case V4L2_CID_MPEG_VIDEO_ASPECT: 	return "Video Aspect";
 	case V4L2_CID_MPEG_VIDEO_B_FRAMES: 	return "Video B Frames";
@@ -323,16 +333,9 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
 	case V4L2_CID_MPEG_VIDEO_MUTE: 		return "Video Mute";
 	case V4L2_CID_MPEG_VIDEO_MUTE_YUV:	return "Video Mute YUV";
-	case V4L2_CID_MPEG_STREAM_TYPE: 	return "Stream Type";
-	case V4L2_CID_MPEG_STREAM_PID_PMT: 	return "Stream PMT Program ID";
-	case V4L2_CID_MPEG_STREAM_PID_AUDIO: 	return "Stream Audio Program ID";
-	case V4L2_CID_MPEG_STREAM_PID_VIDEO: 	return "Stream Video Program ID";
-	case V4L2_CID_MPEG_STREAM_PID_PCR: 	return "Stream PCR Program ID";
-	case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
-	case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
-	case V4L2_CID_MPEG_STREAM_VBI_FMT:	return "Stream VBI Format";
 
 	/* CAMERA controls */
+	/* Keep the order of the 'case's the same as in videodev2.h! */
 	case V4L2_CID_CAMERA_CLASS:		return "Camera Controls";
 	case V4L2_CID_EXPOSURE_AUTO:		return "Auto Exposure";
 	case V4L2_CID_EXPOSURE_ABSOLUTE:	return "Exposure Time, Absolute";
@@ -346,14 +349,15 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_FOCUS_ABSOLUTE:		return "Focus, Absolute";
 	case V4L2_CID_FOCUS_RELATIVE:		return "Focus, Relative";
 	case V4L2_CID_FOCUS_AUTO:		return "Focus, Automatic";
-	case V4L2_CID_IRIS_ABSOLUTE:		return "Iris, Absolute";
-	case V4L2_CID_IRIS_RELATIVE:		return "Iris, Relative";
 	case V4L2_CID_ZOOM_ABSOLUTE:		return "Zoom, Absolute";
 	case V4L2_CID_ZOOM_RELATIVE:		return "Zoom, Relative";
 	case V4L2_CID_ZOOM_CONTINUOUS:		return "Zoom, Continuous";
 	case V4L2_CID_PRIVACY:			return "Privacy";
+	case V4L2_CID_IRIS_ABSOLUTE:		return "Iris, Absolute";
+	case V4L2_CID_IRIS_RELATIVE:		return "Iris, Relative";
 
 	/* FM Radio Modulator control */
+	/* Keep the order of the 'case's the same as in videodev2.h! */
 	case V4L2_CID_FM_TX_CLASS:		return "FM Radio Modulator Controls";
 	case V4L2_CID_RDS_TX_DEVIATION:		return "RDS Signal Deviation";
 	case V4L2_CID_RDS_TX_PI:		return "RDS Program ID";
-- 
1.6.4.2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
  2010-04-26  7:33 ` [PATCH 01/15] [RFC] v4l: Add new " Hans Verkuil
  2010-04-26  7:33 ` [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header Hans Verkuil
@ 2010-04-26  7:33 ` Hans Verkuil
  2010-05-02 20:39   ` Laurent Pinchart
  2010-04-26  7:33 ` [PATCH 04/15] [RFC] v4l: hook up the new control framework into the core framework Hans Verkuil
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
 Documentation/video4linux/v4l2-controls.txt |  543 +++++++++++++++++++++++++++
 1 files changed, 543 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..29a92b4
--- /dev/null
+++ b/Documentation/video4linux/v4l2-controls.txt
@@ -0,0 +1,543 @@
+Introduction
+============
+
+The V4L2 control API seems simple enough, but quickly becomes very hard to
+implement correctly in drivers. But much of the code needed to handle controls
+is actually not driver specific and can be moved to the V4L core framework.
+
+After all, the only part that a driver developer is interested in is:
+
+1) How do I add a control?
+2) How do I set the control? (i.e. s_ctrl)
+
+And occasionally:
+
+3) How do I update the control's value? (i.e. g_ctrl)
+4) How do I validate the user's proposed control value? (i.e. try_ctrl)
+
+All the rest is something that can be done centrally.
+
+The control framework was created in order to implement all the rules of the
+V4L2 specification with respect to controls in a central place. And to make
+life as easy as possible for the driver developer.
+
+
+Objects in the framework
+========================
+
+There are two main objects:
+
+The v4l2_ctrl object describes the control properties and keeps track of the
+control's value (both the current value and the proposed new value).
+
+v4l2_ctrl_handler is the object that keeps track of controls. It maintains a
+list of v4l2_ctrl objects that it owns and another list of references to
+controls, possibly to controls owned by other handlers.
+
+
+Basic usage
+===========
+
+1) Prepare the driver:
+
+- Add the handler to your main bridge driver or sub-device driver top-level
+  struct:
+
+	struct foo_dev {
+		...
+		struct v4l2_ctrl_handler hdl;
+		...
+	};
+
+	struct foo_dev *foo;
+
+- Initialize handler:
+
+	v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls);
+
+  The second argument is a hint telling the function how many controls this
+  handled is expected to handle. It will allocate a hashtable based on this
+  information. It is a hint only.
+
+- Hooking the control handler into a driver:
+
+  When a subdevice is being registered with a bridge driver and the
+  ctrl_handler fields of both v4l2_subdev and v4l2_device are set, then the
+  controls of the subdev will become automatically available in the bridge
+  driver as well. If the subdev driver contains controls that already exist in
+  the bridge driver, then those will be skipped (so a bridge driver can always
+  override a subdev control).
+
+  How to hook the handler into a bridge driver:
+
+	foo->v4l2_dev.ctrl_handler = &foo->hdl;
+
+  And whenever you call video_register_device() you must set the
+  ctrl_handler field of struct video_device as well:
+
+	vdev->ctrl_handler = &foo->hdl;
+
+  Finally, remove all control functions from your v4l2_ioctl_ops:
+  vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl,
+  vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls.
+  Those are now no longer needed.
+
+  How to hook the control handler into a subdev driver:
+
+	foo->sd.ctrl_handler = &foo->hdl;
+
+  And set all core control ops in your struct v4l2_subdev_core_ops to these
+  helpers:
+
+	.queryctrl = v4l2_sd_queryctrl,
+	.querymenu = v4l2_sd_querymenu,
+	.g_ctrl = v4l2_sd_g_ctrl,
+	.s_ctrl = v4l2_sd_s_ctrl,
+	.g_ext_ctrls = v4l2_sd_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
+
+  This is for backwards compatibility. Once all bridge drivers are converted
+  these control ops can be removed just as they are already for bridge drivers.
+
+- Clean up the handler at the end:
+
+	v4l2_ctrl_handler_free(&foo->hdl);
+
+
+2) Add controls:
+
+Typically done right after the handler_init:
+
+	v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls);
+	v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+	v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 255, 1, 128);
+	...
+	if (foo->hdl.error) {
+		int err = foo->hdl.error;
+
+		v4l2_ctrl_handler_free(&foo->hdl);
+		return err;
+	}
+
+The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new
+control, but if you do not need to access the pointer outside the control ops,
+then there is no need to store it.
+
+Note that if something fails, the function will return NULL or an error and
+set hdl->error to the error code. If hdl->error was already set, then it
+will just return and do nothing. This is also true for v4l2_ctrl_handler_init
+if it cannot allocate the internal data structure.
+
+This makes it easy to init the handler and just add all controls and only check
+the error code at the end. Saves a lot of repetitive error checking.
+
+It is recommended to add controls in ascending control ID order: it will be
+a bit faster that way.
+
+3) Optionally force initial control setup:
+
+	v4l2_ctrl_handler_setup(&foo->hdl);
+
+This will call s_ctrl for all controls unconditionally. Effectively this
+initializes the hardware to the default control values. It is recommended
+that you do this.
+
+4) Finally: implement the v4l2_ctrl_ops
+
+	static const struct v4l2_ctrl_ops foo_ctrl_ops = {
+		.s_ctrl = foo_s_ctrl,
+	};
+
+Usually all you need is s_ctrl:
+
+	static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
+	{
+		struct foo *state = container_of(ctrl->handler, struct foo, hdl);
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			write_reg(0x123, ctrl->val);
+			break;
+		case V4L2_CID_CONTRAST:
+			write_reg(0x456, ctrl->val);
+			break;
+		}
+		return 0;
+	}
+
+The control ops are called with the v4l2_ctrl pointer as argument.
+The new control value has already been validated, so all you need to do is
+to actually update the hardware registers.
+
+You're done! And this is sufficient for most of the drivers we have. No need
+to do any validation of control values, or implement QUERYCTRL/QUERYMENU. And
+G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported.
+
+
+===============================================================================
+
+The remainder of this document deals with more advanced topics and scenarios.
+In practice the basic usage as described above is sufficient for most drivers.
+
+===============================================================================
+
+
+Accessing Control Values
+========================
+
+The v4l2_ctrl struct contains these two unions:
+
+	/* The current control value. */
+	union {
+		s32 val;
+		s64 val64;
+		char *string;
+	} cur;
+
+	/* The new control value. */
+	union {
+		s32 val;
+		s64 val64;
+		char *string;
+	};
+
+Within the control ops you can freely use these. The val and val64 speak for
+themselves. The string pointers point to character buffers of length
+ctrl->maximum + 1, and are always 0-terminated.
+
+For g_ctrl you have to update the current control values like this:
+
+	ctrl->cur.val = read_reg(0x123);
+
+The 'new value' union is not relevant in g_ctrl.
+
+For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
+you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
+contains the current value, which you can use (but not change!) as well.
+
+If s_ctrl returns 0 (OK), then the control framework will copy the new final
+values to the 'cur' union.
+
+While in g/s/try_ctrl you can access the value of all controls owned by the
+same handler since the handler's lock is held. Do not attempt to access
+the value of controls owned by other handlers, though.
+
+Elsewhere in the driver you have to be more careful. You cannot just refer
+to the current control values without locking.
+
+There are two simple helper functions defined that will get or set a single
+control value safely:
+
+	s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl);
+	int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);
+
+Don't use these inside the control ops g/s/try_ctrl, though, that will fail.
+
+You can also take the handler lock yourself:
+
+	mutex_lock(&state->hdl.lock);
+	printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string);
+	printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val);
+	mutex_unlock(&state->hdl.lock);
+
+
+Menu Controls
+=============
+
+Menu controls use the 'step' value differently compared to other control
+types. The v4l2_ctrl struct contains this union:
+
+	union {
+		u32 step;
+		u32 menu_skip_mask;
+	};
+
+For menu controls menu_skip_mask is used. What it does is that it allows you
+to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU
+implementation where you can return -EINVAL if a certain menu item is not
+present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for
+menu controls.
+
+A good example is the MPEG Audio Layer II Bitrate menu control where the
+menu is a list of standardized possible bitrates. But in practice hardware
+implementations will only support a subset of those. By setting the skip
+mask you can tell the framework which menu items should be skipped. Setting
+it to 0 means that all menu items are supported.
+
+So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need to remember
+that 'step' means 'skip mask' for menu controls. If you put in '1' by mistake,
+then the first menu item will be skipped.
+
+The v4l2_ctrl_new_std_menu can be used to add menu controls more easily: it
+will calculate the min and max values automatically based on the size of the
+menu, and it has a proper 'mask' argument instead of 'step'.
+
+
+Active and Grabbed Controls
+===========================
+
+If you get more complex relationships between controls, then you may have to
+activate and deactivate controls. For example, if the Chroma AGC control is
+on, then the Chroma Gain control is inactive. That is, you may set it, but
+the value will not be used by the hardware as long as the automatic gain
+control is on. Typically user interfaces can disable such input fields.
+
+You can set the 'active' status using v4l2_ctrl_activate(). By default all
+controls are active. Note that the framework does not check for this flag.
+It is meant purely for GUIs. The function is typically called from within
+s_ctrl.
+
+The other flag is the grabbed flag. A grabbed control means that you cannot
+change it because it is in use by some resource. Typical examples are MPEG
+bitrate controls that cannot be changed while capturing is in progress.
+
+If a control is set to 'grabbed' using v4l2_ctrl_grab(), then the framework
+will return -EBUSY if an attempt is made to set this control.
+
+Since this flag is used by the framework the v4l2_ctrl_grab function will
+take the control handler's lock. So it cannot be called from within the
+control ops. Instead this is typically called from the driver when it
+starts streaming.
+
+
+Control Clusters
+================
+
+By default all controls are independent from the others. But in more
+complex scenarios you can get dependencies from one control to another.
+In that case you need to 'cluster' them:
+
+	struct foo {
+		struct v4l2_ctrl_handler hdl;
+		struct v4l2_ctrl *volume;
+		struct v4l2_ctrl *mute;
+		...
+	};
+
+	state->volume = v4l2_ctrl_new_std(&state->hdl, ...);
+	state->mute = v4l2_ctrl_new_std(&state->hdl, ...);
+	v4l2_ctrl_cluster(2, &state->volume);
+
+From now on whenever one or more of the controls belonging to the same
+cluster is set (or 'gotten', or 'tried'), only the control ops of the first
+control ('volume' in this example) is called. You effectively create a new
+composite control. Similar to how a 'struct' works in C.
+
+So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you should set
+all two controls belonging to the 'volume' cluster:
+
+	static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
+	{
+		struct foo *state = container_of(ctrl->handler, struct foo, hdl);
+
+		switch (ctrl->id) {
+		case V4L2_CID_AUDIO_VOLUME:
+			/* volume cluster */
+			write_reg(0x123, state->mute->val ? 0 : ctrl->val);
+			break;
+		case V4L2_CID_CONTRAST:
+			write_reg(0x456, ctrl->val);
+			break;
+		}
+		return 0;
+	}
+
+In the example above the following are equivalent for the VOLUME case:
+
+	ctrl == state->volume == ctrl->cluster[0]
+	state->mute == ctrl->cluster[1]
+
+Note that controls in a cluster may be NULL. For example, if for some
+reason mute was never added (because the hardware doesn't support that
+particular feature), then mute will be NULL. So in that case we have a
+cluster of 2 controls, of which only 1 is actually instantiated. The
+only restriction is that the first control of the cluster must already be
+present, since that is the 'master' control of the cluster. The master
+control is the one that identifies the cluster and that provides the
+pointer to the v4l2_ctrl_ops struct that is used for that cluster.
+
+
+VIDIOC_LOG_STATUS Support
+=========================
+
+This ioctl allow you to dump the current status of a driver to the kernel log.
+The v4l2_ctrl_handler_log_status(hdl, prefix) can be used to dump the value of
+the controls owned by the given handler to the log. You can supply a prefix
+as well. If the prefix didn't end with a space, then ': ' will be added for
+you.
+
+
+Different Handlers for Different Video Nodes
+============================================
+
+Usually the bridge driver has just one control handler that is global for
+all video nodes. But you can also specify different control handlers for
+different video nodes. It's no problem if there are no subdevs involved.
+But if there are, then you need to block the automatic merging of subdev
+controls to the global control handler. You do that by simply setting the
+ctrl_handler field in struct v4l2_device to NULL.
+
+After each subdev was added, you will then have to call v4l2_ctrl_add_handler
+manually to add the subdev's control handler (sd->ctrl_handler) to the desired
+bridge control handler.
+
+If you want to have one handler (e.g. for a radio device node) have a subset
+of another handler (e.g. for a video device node), then you can first add
+the controls to the first handler, add the other controls to the second
+handler and finally add the first handler to the second. For example:
+
+	v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
+	v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
+	v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...);
+	v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_CONTRAST, ...);
+	v4l2_ctrl_add_handler(&video_hdl, &radio_hdl);
+
+Or you can add specific controls to a handler:
+
+	volume = v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_AUDIO_VOLUME, ...);
+	v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_BRIGHTNESS, ...);
+	v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_CONTRAST, ...);
+	v4l2_ctrl_add_ctrl(&radio_hdl, volume);
+
+What you should not do is make two identical controls for two handlers.
+For example:
+
+	v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
+	v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_AUDIO_MUTE, ...);
+
+This would be bad since muting the radio would not change the video mute
+control. The rule is to have one control for each hardware 'knob' that you
+can twiddle.
+
+
+Finding Controls
+================
+
+Normally you have created the controls yourself and you can store the struct
+v4l2_ctrl pointer into your own struct.
+
+But sometimes you need to find a control from another handler that you do
+not own. For example, if you have to find a volume control from a subdev.
+
+You can do that by calling v4l2_ctrl_find:
+
+	struct v4l2_ctrl *volume;
+
+	volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);
+
+Since v4l2_ctrl_find will lock the handler you have to be careful where you
+use it. For example, this is not a good idea:
+
+	struct v4l2_ctrl_handler hdl;
+
+	v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...);
+	v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_CONTRAST, ...);
+
+...and in video_ops.s_ctrl:
+
+	case V4L2_CID_BRIGHTNESS:
+		contrast = v4l2_find_ctrl(&hdl, V4L2_CID_CONTRAST);
+		...
+
+When s_ctrl is called by the framework the hdl.lock is already taken, so
+attempting to find another control from the same handler will deadlock.
+
+It is recommended not to use this function from inside the control ops.
+
+
+Inheriting Controls
+===================
+
+When one control handler is added to another using v4l2_ctrl_add_handler, then
+by default all controls from one are merged to the other. But a subdev might
+have low-level controls that make sense for some advanced embedded system, but
+not when it is used in consumer-level hardware. In that case you want to keep
+those low-level controls local to the subdev. You can do this by simply
+setting the 'is_private' flag of the control to 1:
+
+	ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...);
+	if (ctrl)
+		ctrl->is_private = 1;
+
+These controls will now be skipped when v4l2_ctrl_add_handler is called.
+
+
+Strict Control Validation
+=========================
+
+By default when the application wants to change an integer control the value
+passed to the framework will automatically be modified to map to the provided
+minimum, maximum and step values of the control. If instead you just want to
+validate the value and not modify it, then set the 'strict_validation' flag of
+the control:
+
+	ctrl->strict_validation = 1;
+
+Now -ERANGE will be returned if the new value does not match the control's
+requirements.
+
+This is currently specific to integer controls. The value for boolean controls
+is always mapped to 0 or 1, menu and string controls are already validated
+strictly, and integer64 controls are not validated at all.
+
+
+V4L2_CTRL_TYPE_CTRL_CLASS Controls
+==================================
+
+Controls of this type can be used by GUIs to get the name of the control class.
+A fully featured GUI can make a dialog with multiple tabs with each tab
+containing the controls belonging to a particular control class. The name of
+each tab can be found by querying a special control with ID <control class | 1>.
+
+Drivers do not have to care about this. The framework will automatically add
+a control of this type whenever the first control belonging to a new control
+class is added.
+
+
+Differences from the Spec
+=========================
+
+There are a few places where the framework acts slightly differently from the
+V4L2 Specification. Those differences are described in this section. We will
+have to see whether we need to adjust the spec or not.
+
+1) It is no longer required to have all controls contained in a
+v4l2_ext_control array be from the same control class. The framework will be
+able to handle any type of control in the array. You need to set ctrl_class
+to 0 in order to enable this. If ctrl_class is non-zero, then it will still
+check that all controls belong to that control class.
+
+If you set ctrl_class to 0 and count to 0, then it will only return an error
+if there are no controls at all.
+
+2) Clarified the way error_idx works. For get and set it will be equal to
+count if nothing was done yet. If it is less than count then only the controls
+up to error_idx-1 were successfully applied.
+
+3) When attempting to read a button control the framework will return -EACCES
+instead of -EINVAL as stated in the spec. It seems to make more sense since
+button controls are write-only controls.
+
+4) Attempting to write to a read-only control will return -EACCES instead of
+-EINVAL as the spec says.
+
+5) The spec does not mention what should happen when you try to set/get a
+control class controls. ivtv currently returns -EINVAL (indicating that the
+control ID does not exist) while the framework will return -EACCES, which
+makes more sense.
+
+
+Proposals for Extensions
+========================
+
+Some ideas for future extensions to the spec:
+
+1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of
+decimal. Useful for e.g. video_mute_yuv.
+
+2) It is possible to mark in the controls array which controls have been
+successfully written and which failed by for example adding a bit to the
+control ID. Not sure if it is worth the effort, though.
-- 
1.6.4.2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 04/15] [RFC] v4l: hook up the new control framework into the core framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (2 preceding siblings ...)
  2010-04-26  7:33 ` [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API Hans Verkuil
@ 2010-04-26  7:33 ` Hans Verkuil
  2010-04-26  7:33 ` [PATCH 05/15] [RFC] saa7115: convert to the new control framework Hans Verkuil
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 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.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
 drivers/media/video/v4l2-device.c |    7 +++++
 drivers/media/video/v4l2-ioctl.c  |   46 ++++++++++++++++++++++++++----------
 2 files changed, 40 insertions(+), 13 deletions(-)

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 e138918..daa9d0c 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-chip-ident.h>
 
 #define dbgarg(cmd, fmt, arg...) \
@@ -1253,9 +1254,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",
@@ -1270,7 +1274,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;
@@ -1300,11 +1306,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;
@@ -1326,10 +1337,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;
 	}
@@ -1338,10 +1351,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;
 	}
@@ -1350,10 +1365,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;
 	}
@@ -1361,9 +1378,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] 29+ messages in thread

* [PATCH 05/15] [RFC] saa7115: convert to the new control framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (3 preceding siblings ...)
  2010-04-26  7:33 ` [PATCH 04/15] [RFC] v4l: hook up the new control framework into the core framework Hans Verkuil
@ 2010-04-26  7:33 ` Hans Verkuil
  2010-04-26 15:53   ` David Ellingsworth
  2010-04-26  7:33 ` [PATCH 06/15] [RFC] msp3400: " Hans Verkuil
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 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 72eaa66..8db056f 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_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct v4l2_subdev *sd = to_sd(ctrl);
 	struct saa711x_state *state = to_state(sd);
-	u8 val;
 
 	switch (ctrl->id) {
-	case V4L2_CID_BRIGHTNESS:
-		if (ctrl->value < 0 || ctrl->value > 255) {
-			v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
-			return -ERANGE;
-		}
-
-		state->bright = ctrl->value;
-		saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright);
-		break;
-
-	case V4L2_CID_CONTRAST:
-		if (ctrl->value < 0 || ctrl->value > 127) {
-			v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
-			return -ERANGE;
-		}
-
-		state->contrast = ctrl->value;
-		saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
-		break;
-
-	case V4L2_CID_SATURATION:
-		if (ctrl->value < 0 || ctrl->value > 127) {
-			v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
-			return -ERANGE;
-		}
-
-		state->sat = ctrl->value;
-		saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat);
-		break;
-
-	case V4L2_CID_HUE:
-		if (ctrl->value < -128 || ctrl->value > 127) {
-			v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
-			return -ERANGE;
-		}
-
-		state->hue = ctrl->value;
-		saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
-		break;
 	case V4L2_CID_CHROMA_AGC:
-		val = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL);
-		state->chroma_agc = ctrl->value;
-		if (ctrl->value)
-			val &= 0x7f;
-		else
-			val |= 0x80;
-		saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val);
+		/* chroma gain cluster */
+		if (state->agc->cur.val)
+			state->gain->cur.val =
+				saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
 		break;
-	case V4L2_CID_CHROMA_GAIN:
-		/* Chroma gain cannot be set when AGC is enabled */
-		if (state->chroma_agc == 1)
-			return -EINVAL;
-		saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80);
-		break;
-	default:
-		return -EINVAL;
 	}
-
 	return 0;
 }
 
-static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct v4l2_subdev *sd = to_sd(ctrl);
 	struct saa711x_state *state = to_state(sd);
 
 	switch (ctrl->id) {
 	case V4L2_CID_BRIGHTNESS:
-		ctrl->value = state->bright;
+		saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
 		break;
+
 	case V4L2_CID_CONTRAST:
-		ctrl->value = state->contrast;
+		saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
 		break;
+
 	case V4L2_CID_SATURATION:
-		ctrl->value = state->sat;
+		saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
 		break;
+
 	case V4L2_CID_HUE:
-		ctrl->value = state->hue;
+		saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
 		break;
+
 	case V4L2_CID_CHROMA_AGC:
-		ctrl->value = state->chroma_agc;
-		break;
-	case V4L2_CID_CHROMA_GAIN:
-		ctrl->value = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
+		/* chroma gain cluster */
+		if (state->agc->val)
+			saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
+		else
+			saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
+		v4l2_ctrl_activate(state->gain, !state->agc->val);
 		break;
+
 	default:
 		return -EINVAL;
 	}
@@ -1221,25 +1185,6 @@ static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
 	return 0;
 }
 
-static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-	switch (qc->id) {
-	case V4L2_CID_BRIGHTNESS:
-		return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
-	case V4L2_CID_CONTRAST:
-	case V4L2_CID_SATURATION:
-		return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
-	case V4L2_CID_HUE:
-		return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
-	case V4L2_CID_CHROMA_AGC:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_CHROMA_GAIN:
-		return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48);
-	default:
-		return -EINVAL;
-	}
-}
-
 static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 {
 	struct saa711x_state *state = to_state(sd);
@@ -1516,17 +1461,27 @@ static int saa711x_log_status(struct v4l2_subdev *sd)
 		break;
 	}
 	v4l2_info(sd, "Width, Height:   %d, %d\n", state->width, state->height);
+	v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
 	return 0;
 }
 
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
+	.s_ctrl = saa711x_s_ctrl,
+	.g_ctrl = saa711x_g_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops saa711x_core_ops = {
 	.log_status = saa711x_log_status,
 	.g_chip_ident = saa711x_g_chip_ident,
-	.g_ctrl = saa711x_g_ctrl,
-	.s_ctrl = saa711x_s_ctrl,
-	.queryctrl = saa711x_queryctrl,
+	.g_ext_ctrls = v4l2_sd_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
+	.g_ctrl = v4l2_sd_g_ctrl,
+	.s_ctrl = v4l2_sd_s_ctrl,
+	.queryctrl = v4l2_sd_queryctrl,
+	.querymenu = v4l2_sd_querymenu,
 	.s_std = saa711x_s_std,
 	.reset = saa711x_reset,
 	.s_gpio = saa711x_s_gpio,
@@ -1571,8 +1526,9 @@ static int saa711x_probe(struct i2c_client *client,
 {
 	struct saa711x_state *state;
 	struct v4l2_subdev *sd;
-	int	i;
-	char	name[17];
+	struct v4l2_ctrl_handler *hdl;
+	int i;
+	char name[17];
 	char chip_id;
 	int autodetect = !id || id->driver_data == 1;
 
@@ -1611,15 +1567,37 @@ static int saa711x_probe(struct i2c_client *client,
 		return -ENOMEM;
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
+
+	hdl = &state->hdl;
+	v4l2_ctrl_handler_init(hdl, 6);
+	/* add in ascending ID order */
+	v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+	v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 127, 1, 64);
+	v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 127, 1, 64);
+	v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+			V4L2_CID_HUE, -128, 127, 1, 0);
+	state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+			V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
+	state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+			V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
+	sd->ctrl_handler = hdl;
+	if (hdl->error) {
+		int err = hdl->error;
+
+		v4l2_ctrl_handler_free(hdl);
+		kfree(state);
+		return err;
+	}
+	state->agc->flags |= V4L2_CTRL_FLAG_UPDATE;
+	v4l2_ctrl_cluster(2, &state->agc);
+
 	state->input = -1;
 	state->output = SAA7115_IPORT_ON;
 	state->enable = 1;
 	state->radio = 0;
-	state->bright = 128;
-	state->contrast = 64;
-	state->hue = 0;
-	state->sat = 64;
-	state->chroma_agc = 1;
 	switch (chip_id) {
 	case '1':
 		state->ident = V4L2_IDENT_SAA7111;
@@ -1667,6 +1645,7 @@ static int saa711x_probe(struct i2c_client *client,
 	if (state->ident > V4L2_IDENT_SAA7111A)
 		saa711x_writeregs(sd, saa7115_init_misc);
 	saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
+	v4l2_ctrl_handler_setup(hdl);
 
 	v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
 		saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
@@ -1681,6 +1660,7 @@ static int saa711x_remove(struct i2c_client *client)
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
 	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
 	kfree(to_state(sd));
 	return 0;
 }
-- 
1.6.4.2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 06/15] [RFC] msp3400: convert to the new control framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (4 preceding siblings ...)
  2010-04-26  7:33 ` [PATCH 05/15] [RFC] saa7115: convert to the new control framework Hans Verkuil
@ 2010-04-26  7:33 ` Hans Verkuil
  2010-04-26 16:17   ` David Ellingsworth
  2010-04-26  7:33 ` [PATCH 07/15] [RFC] saa717x: " Hans Verkuil
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 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..de0da40 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(state->volume, v4l2_ctrl_g(state->volume));
+}
+
 /* --- v4l2 ioctls --- */
 static int msp_s_radio(struct v4l2_subdev *sd)
 {
@@ -472,7 +402,7 @@ static int msp_s_radio(struct v4l2_subdev *sd)
 		msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
 		msp3400c_set_carrier(client, MSP_CARRIER(10.7),
 				MSP_CARRIER(10.7));
-		msp_set_audio(client);
+		msp_update_volume(state);
 		break;
 	case OPMODE_AUTODETECT:
 	case OPMODE_AUTOSELECT:
@@ -592,33 +522,6 @@ static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
 	return 0;
 }
 
-static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-	struct msp_state *state = to_state(sd);
-
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_VOLUME:
-		return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	default:
-		break;
-	}
-	if (!state->has_sound_processing)
-		return -EINVAL;
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_LOUDNESS:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	case V4L2_CID_AUDIO_BALANCE:
-	case V4L2_CID_AUDIO_BASS:
-	case V4L2_CID_AUDIO_TREBLE:
-		return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
-	default:
-		return -EINVAL;
-	}
-	return 0;
-}
-
 static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
 {
 	struct msp_state *state = to_state(sd);
@@ -633,19 +536,14 @@ static int msp_log_status(struct v4l2_subdev *sd)
 	struct msp_state *state = to_state(sd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	const char *p;
+	char prefix[V4L2_SUBDEV_NAME_SIZE + 20];
 
 	if (state->opmode == OPMODE_AUTOSELECT)
 		msp_detect_stereo(client);
 	v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
 			client->name, state->rev1, state->rev2);
-	v4l_info(client, "Audio:    volume %d%s\n",
-			state->volume, state->muted ? " (muted)" : "");
-	if (state->has_sound_processing) {
-		v4l_info(client, "Audio:    balance %d bass %d treble %d loudness %s\n",
-				state->balance, state->bass,
-				state->treble,
-				state->loudness ? "on" : "off");
-	}
+	snprintf(prefix, sizeof(prefix), "%s: Audio:    ", sd->name);
+	v4l2_ctrl_handler_log_status(&state->hdl, prefix);
 	switch (state->mode) {
 		case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
 		case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
@@ -695,12 +593,20 @@ static int msp_resume(struct i2c_client *client)
 
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops msp_ctrl_ops = {
+	.s_ctrl = msp_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops msp_core_ops = {
 	.log_status = msp_log_status,
 	.g_chip_ident = msp_g_chip_ident,
-	.g_ctrl = msp_g_ctrl,
-	.s_ctrl = msp_s_ctrl,
-	.queryctrl = msp_queryctrl,
+	.g_ext_ctrls = v4l2_sd_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
+	.g_ctrl = v4l2_sd_g_ctrl,
+	.s_ctrl = v4l2_sd_s_ctrl,
+	.queryctrl = v4l2_sd_queryctrl,
+	.querymenu = v4l2_sd_querymenu,
 	.s_std = msp_s_std,
 };
 
@@ -728,6 +634,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
 	struct msp_state *state;
 	struct v4l2_subdev *sd;
+	struct v4l2_ctrl_handler *hdl;
 	int (*thread_func)(void *data) = NULL;
 	int msp_hard;
 	int msp_family;
@@ -752,13 +659,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
 	state->v4l2_std = V4L2_STD_NTSC;
 	state->audmode = V4L2_TUNER_MODE_STEREO;
-	state->volume = 58880;	/* 0db gain */
-	state->balance = 32768;	/* 0db gain */
-	state->bass = 32768;
-	state->treble = 32768;
-	state->loudness = 0;
 	state->input = -1;
-	state->muted = 0;
 	state->i2s_mode = 0;
 	init_waitqueue_head(&state->wq);
 	/* These are the reset input/output positions */
@@ -777,8 +678,6 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
 		return -ENODEV;
 	}
 
-	msp_set_audio(client);
-
 	msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
 	msp_product = (state->rev2 >> 8) & 0xff;
 	msp_prod_hi = msp_product / 10;
@@ -849,6 +748,34 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
 			state->opmode = OPMODE_MANUAL;
 	}
 
+	hdl = &state->hdl;
+	v4l2_ctrl_handler_init(hdl, 6);
+	if (state->has_sound_processing) {
+		v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+			V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768);
+		v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+			V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768);
+		v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+			V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0);
+	}
+	state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+			V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880);
+	v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+			V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+	state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+	sd->ctrl_handler = hdl;
+	if (hdl->error) {
+		int err = hdl->error;
+
+		v4l2_ctrl_handler_free(hdl);
+		kfree(state);
+		return err;
+	}
+
+	v4l2_ctrl_cluster(2, &state->volume);
+	v4l2_ctrl_handler_setup(hdl);
+
 	/* hello world :-) */
 	v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
 			msp_family, msp_product,
@@ -903,6 +830,7 @@ static int msp_remove(struct i2c_client *client)
 	}
 	msp_reset(client);
 
+	v4l2_ctrl_handler_free(&state->hdl);
 	kfree(state);
 	return 0;
 }
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] 29+ messages in thread

* [PATCH 07/15] [RFC] saa717x: convert to the new control framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (5 preceding siblings ...)
  2010-04-26  7:33 ` [PATCH 06/15] [RFC] msp3400: " Hans Verkuil
@ 2010-04-26  7:33 ` Hans Verkuil
  2010-04-26  7:33 ` [PATCH 08/15] [RFC] cx25840/ivtv: replace ugly priv control with s_config Hans Verkuil
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 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..79c8957 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_sd_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
+	.g_ctrl = v4l2_sd_g_ctrl,
+	.s_ctrl = v4l2_sd_s_ctrl,
+	.queryctrl = v4l2_sd_queryctrl,
+	.querymenu = v4l2_sd_querymenu,
+	.log_status = saa717x_log_status,
 };
 
 static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
@@ -1427,6 +1249,7 @@ static int saa717x_probe(struct i2c_client *client,
 			 const struct i2c_device_id *did)
 {
 	struct saa717x_state *decoder;
+	struct v4l2_ctrl_handler *hdl;
 	struct v4l2_subdev *sd;
 	u8 id = 0;
 	char *p = "";
@@ -1462,16 +1285,41 @@ static int saa717x_probe(struct i2c_client *client,
 		p = "saa7171";
 	v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
 			client->addr << 1, client->adapter->name);
+
+	hdl = &decoder->hdl;
+	v4l2_ctrl_handler_init(hdl, 9);
+	/* add in ascending ID order */
+	v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+	v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 255, 1, 68);
+	v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 255, 1, 64);
+	v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+			V4L2_CID_HUE, -128, 127, 1, 0);
+	v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+			V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000);
+	v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+			V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+	v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+			V4L2_CID_AUDIO_BASS, -16, 15, 1, 0);
+	v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+			V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0);
+	v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+	sd->ctrl_handler = hdl;
+	if (hdl->error) {
+		int err = hdl->error;
+
+		v4l2_ctrl_handler_free(hdl);
+		kfree(decoder);
+		return err;
+	}
+
 	decoder->std = V4L2_STD_NTSC;
 	decoder->input = -1;
 	decoder->enable = 1;
 
-	/* tune these parameters */
-	decoder->bright = 0x80;
-	decoder->contrast = 0x44;
-	decoder->sat = 0x40;
-	decoder->hue = 0x00;
-
 	/* FIXME!! */
 	decoder->playback = 0;	/* initially capture mode used */
 	decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */
@@ -1482,23 +1330,13 @@ static int saa717x_probe(struct i2c_client *client,
 	/* set volume, bass and treble */
 	decoder->audio_main_vol_l = 6;
 	decoder->audio_main_vol_r = 6;
-	decoder->audio_main_bass = 0;
-	decoder->audio_main_treble = 0;
-	decoder->audio_main_mute = 0;
-	decoder->audio_main_balance = 32768;
-	/* normalize (24 to -40 (not -84) -> 65535 to 0) */
-	decoder->audio_main_volume =
-		(decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40));
 
 	v4l2_dbg(1, debug, sd, "writing init values\n");
 
 	/* FIXME!! */
 	saa717x_write_regs(sd, reg_init_initialize);
-	set_video_output_level_regs(sd, decoder);
-	/* set bass,treble to 0db 20041101 K.Ohta */
-	decoder->audio_main_bass = 0;
-	decoder->audio_main_treble = 0;
-	set_audio_regs(sd, decoder);
+
+	v4l2_ctrl_handler_setup(hdl);
 
 	set_current_state(TASK_INTERRUPTIBLE);
 	schedule_timeout(2*HZ);
@@ -1510,6 +1348,7 @@ static int saa717x_remove(struct i2c_client *client)
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
 	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
 	kfree(to_state(sd));
 	return 0;
 }
-- 
1.6.4.2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 08/15] [RFC] cx25840/ivtv: replace ugly priv control with s_config
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (6 preceding siblings ...)
  2010-04-26  7:33 ` [PATCH 07/15] [RFC] saa717x: " Hans Verkuil
@ 2010-04-26  7:33 ` Hans Verkuil
  2010-05-02 20:41   ` Laurent Pinchart
  2010-04-26  7:33 ` [PATCH 09/15] [RFC] cx25840: convert to the new control framework Hans Verkuil
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 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 f2461cd..b8aa5d2 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;
@@ -1601,10 +1593,25 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
 	return 0;
 }
 
+static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void *platform_data)
+{
+	struct cx25840_state *state = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (platform_data) {
+		struct cx25840_platform_data *pdata = platform_data;
+
+		state->pvr150_workaround = pdata->pvr150_workaround;
+		set_input(client, state->vid_input, state->aud_input);
+	}
+	return 0;
+}
+
 /* ----------------------------------------------------------------------- */
 
 static const struct v4l2_subdev_core_ops cx25840_core_ops = {
 	.log_status = cx25840_log_status,
+	.s_config = cx25840_s_config,
 	.g_chip_ident = cx25840_g_chip_ident,
 	.g_ctrl = cx25840_g_ctrl,
 	.s_ctrl = cx25840_s_ctrl,
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index 5534544..50b7887 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] 29+ messages in thread

* [PATCH 09/15] [RFC] cx25840: convert to the new control framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (7 preceding siblings ...)
  2010-04-26  7:33 ` [PATCH 08/15] [RFC] cx25840/ivtv: replace ugly priv control with s_config Hans Verkuil
@ 2010-04-26  7:33 ` Hans Verkuil
  2010-04-26  7:34 ` [PATCH 10/15] [RFC] cx2341x: convert to the " Hans Verkuil
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:33 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 b8aa5d2..7caa4dc 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;
 	}
@@ -1177,8 +1112,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");
@@ -1395,40 +1328,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);
@@ -1590,6 +1489,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;
 }
 
@@ -1609,13 +1509,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_sd_g_ctrl,
+	.s_ctrl = v4l2_sd_s_ctrl,
+	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
+	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
+	.g_ext_ctrls = v4l2_sd_g_ext_ctrls,
+	.queryctrl = v4l2_sd_queryctrl,
+	.querymenu = v4l2_sd_querymenu,
 	.s_std = cx25840_s_std,
 	.reset = cx25840_reset,
 	.load_fw = cx25840_load_fw,
@@ -1698,6 +1606,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;
 
@@ -1741,6 +1650,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",
@@ -1785,12 +1695,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;
 }
@@ -1798,9 +1744,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 50b7887..cc0b6dd 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] 29+ messages in thread

* [PATCH 10/15] [RFC] cx2341x: convert to the control framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (8 preceding siblings ...)
  2010-04-26  7:33 ` [PATCH 09/15] [RFC] cx25840: convert to the new control framework Hans Verkuil
@ 2010-04-26  7:34 ` Hans Verkuil
  2010-04-26  7:34 ` [PATCH 11/15] [RFC] wm8775: convert to the new " Hans Verkuil
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:34 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 |  725 ++++++++++++++++++++++++++++++++++-------
 include/media/cx2341x.h       |   81 +++++
 2 files changed, 694 insertions(+), 112 deletions(-)

diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index 022b480..9a585e0 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,468 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
 }
 EXPORT_SYMBOL(cx2341x_log_status);
 
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
 
+
+/********************** NEW CODE *********************/
+
+static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct cx2341x_handler, hdl);
+}
+
+static int cx2341x_hdl_api(struct cx2341x_handler *hdl,
+		       u32 cmd, int args, ...)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	va_list vargs;
+	int i;
+
+	va_start(vargs, args);
+
+	for (i = 0; i < args; i++)
+		data[i] = va_arg(vargs, int);
+	va_end(vargs);
+	return hdl->func(hdl->priv, cmd, args, 0, data);
+}
+
+/* ctrl->handler->lock is held, so it is safe to access cur.val */
+static inline int cx2341x_neq(struct v4l2_ctrl *ctrl)
+{
+	return ctrl && ctrl->val != ctrl->cur.val;
+}
+
+static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+	s32 val = ctrl->val;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES: {
+		/* video gop cluster */
+		int b = val + 1;
+		int gop = hdl->video_gop_size->val;
+
+		gop = b * ((gop + b - 1) / b);
+
+		/* Max GOP size = 34 */
+		while (gop > 34)
+			gop -= b;
+		hdl->video_gop_size->val = gop;
+		break;
+	}
+
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		/* stream type cluster */
+		hdl->video_encoding->val =
+		    (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+		     hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+			V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+			V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+		if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+			/* MPEG-1 implies CBR */
+			hdl->video_bitrate_mode->val =
+				V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+		/* peak bitrate shall be >= normal bitrate */
+		if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+		    hdl->video_bitrate_peak->val < hdl->video_bitrate->val)
+			hdl->video_bitrate_peak->val = hdl->video_bitrate->val;
+		break;
+	}
+	return 0;
+}
+
+static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	static const int mpeg_stream_type[] = {
+		0,	/* MPEG-2 PS */
+		1,	/* MPEG-2 TS */
+		2,	/* MPEG-1 SS */
+		14,	/* DVD */
+		11,	/* VCD */
+		12,	/* SVCD */
+	};
+	struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+	s32 val = ctrl->val;
+	u32 props;
+	int err;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_STREAM_VBI_FMT:
+		if (hdl->ops && hdl->ops->s_stream_vbi_fmt)
+			return hdl->ops->s_stream_vbi_fmt(hdl, val);
+		return 0;
+
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		return cx2341x_hdl_api(hdl,
+			CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1);
+
+	case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+		return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val);
+
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val);
+
+	case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+		return cx2341x_hdl_api(hdl,
+			CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val);
+
+	case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+		return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val);
+
+	case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+		/* audio properties cluster */
+		props = (hdl->audio_sampling_freq->val << 0) |
+			(hdl->audio_mode->val << 8) |
+			(hdl->audio_mode_extension->val << 10) |
+			(hdl->audio_crc->val << 14);
+		if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+			props |= 3 << 12;
+		else
+			props |= hdl->audio_emphasis->val << 12;
+
+		if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+			props |=
+#if 1
+				/* Not sure if this MPEG Layer II setting is required */
+				((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
+#endif
+				(hdl->audio_ac3_bitrate->val << 4) |
+				(CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
+		} else {
+			/* Assuming MPEG Layer II */
+			props |=
+				((3 - hdl->audio_encoding->val) << 2) |
+				((1 + hdl->audio_l2_bitrate->val) << 4);
+		}
+		err = cx2341x_hdl_api(hdl,
+				CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props);
+		if (err)
+			return err;
+
+		hdl->audio_properties = props;
+		if (hdl->audio_ac3_bitrate) {
+			int is_ac3 = hdl->audio_encoding->val ==
+						V4L2_MPEG_AUDIO_ENCODING_AC3;
+
+			v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3);
+			v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3);
+		}
+		v4l2_ctrl_activate(hdl->audio_mode_extension,
+			hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO);
+		if (cx2341x_neq(hdl->audio_sampling_freq) &&
+		    hdl->ops && hdl->ops->s_audio_sampling_freq)
+			return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val);
+		if (cx2341x_neq(hdl->audio_mode) &&
+		    hdl->ops && hdl->ops->s_audio_mode)
+			return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val);
+		return 0;
+
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		/* video gop cluster */
+		return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
+				hdl->video_gop_size->val,
+				hdl->video_b_frames->val + 1);
+
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		/* stream type cluster */
+		err = cx2341x_hdl_api(hdl,
+			CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]);
+		if (err)
+			return err;
+
+		err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5,
+				hdl->video_bitrate_mode->val,
+				hdl->video_bitrate->val,
+				hdl->video_bitrate_peak->val / 400, 0, 0);
+		if (err)
+			return err;
+
+		v4l2_ctrl_activate(hdl->video_bitrate_mode,
+			hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1);
+		v4l2_ctrl_activate(hdl->video_bitrate_peak,
+			hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+		if (cx2341x_neq(hdl->video_encoding) &&
+		    hdl->ops && hdl->ops->s_video_encoding)
+			return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val);
+		return 0;
+
+	case V4L2_CID_MPEG_VIDEO_MUTE:
+		/* video mute cluster */
+		return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1,
+				hdl->video_mute->val |
+					(hdl->video_mute_yuv->val << 8));
+
+	case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: {
+		int active_filter;
+
+		/* video filter mode */
+		err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
+				hdl->video_spatial_filter_mode->val |
+					(hdl->video_temporal_filter_mode->val << 1),
+				hdl->video_median_filter_type->val);
+		if (err)
+			return err;
+
+		active_filter = hdl->video_spatial_filter_mode->val !=
+				V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO;
+		v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter);
+		v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter);
+		v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter);
+		active_filter = hdl->video_temporal_filter_mode->val !=
+				V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO;
+		v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter);
+		active_filter = hdl->video_median_filter_type->val !=
+				V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF;
+		v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter);
+		v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter);
+		v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter);
+		v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter);
+		return 0;
+	}
+
+	case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+		/* video filter type cluster */
+		return cx2341x_hdl_api(hdl,
+				CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
+				hdl->video_luma_spatial_filter_type->val,
+				hdl->video_chroma_spatial_filter_type->val);
+
+	case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+		/* video filter cluster */
+		return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
+				hdl->video_spatial_filter->val,
+				hdl->video_temporal_filter->val);
+
+	case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+		/* video median cluster */
+		return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4,
+				hdl->video_luma_median_filter_bottom->val,
+				hdl->video_luma_median_filter_top->val,
+				hdl->video_chroma_median_filter_bottom->val,
+				hdl->video_chroma_median_filter_top->val);
+	}
+	return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops cx2341x_ops = {
+	.try_ctrl = cx2341x_try_ctrl,
+	.s_ctrl = cx2341x_s_ctrl,
+};
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+			u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+	enum v4l2_ctrl_type type;
+	const char *name;
+	u32 flags;
+
+	cx2341x_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+	return v4l2_ctrl_new_custom(hdl, &cx2341x_ops, id,
+			name, type, min, max, step, def, flags,
+			cx2341x_get_menu(id), NULL);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+			u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+	return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl,
+			u32 id, s32 mask, s32 def)
+{
+	return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, mask, def);
+}
+
+int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
+			 unsigned nr_of_controls_hint)
+{
+	struct v4l2_ctrl_handler *hdl = &cxhdl->hdl;
+	u32 caps = cxhdl->capabilities;
+	int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI;
+	int has_ac3 = caps & CX2341X_CAP_HAS_AC3;
+	int has_ts = caps & CX2341X_CAP_HAS_TS;
+
+	cxhdl->width = 720;
+	cxhdl->height = 480;
+
+	v4l2_ctrl_handler_init(hdl, nr_of_controls_hint);
+
+	/* Add controls in ascending control ID order for fastest
+	   insertion time. */
+	cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_STREAM_TYPE, has_ts ? 0 : 2, 0);
+	cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_STREAM_VBI_FMT, has_sliced_vbi ? 0 : 2, 0);
+	cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+			0, V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+	cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_AUDIO_ENCODING,
+			has_ac3 ? ~0x12 : ~0x2, V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+	cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_std(hdl,
+			V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+			V4L2_MPEG_AUDIO_L2_BITRATE_192K,
+			V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0,
+			V4L2_MPEG_AUDIO_L2_BITRATE_224K);
+	cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_AUDIO_MODE, 0, 0);
+	cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, 0, 0);
+	cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_AUDIO_EMPHASIS, 0, 0);
+	cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_AUDIO_CRC, 0, 0);
+
+	cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0);
+	if (has_ac3)
+		cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_std(hdl,
+				V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+				V4L2_MPEG_AUDIO_AC3_BITRATE_48K,
+				V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1,
+				V4L2_MPEG_AUDIO_AC3_BITRATE_224K);
+	cxhdl->video_encoding = cx2341x_ctrl_new_std(hdl,
+			V4L2_CID_MPEG_VIDEO_ENCODING,
+			V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
+			V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0,
+			V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+	cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_VIDEO_ASPECT,
+			0, V4L2_MPEG_VIDEO_ASPECT_4x3);
+	cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl,
+			V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2);
+	cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl,
+			V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+			1, 34, 1, cxhdl->is_50hz ? 12 : 15);
+	cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1);
+	cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl,
+			V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+			0, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+	cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl,
+			V4L2_CID_MPEG_VIDEO_BITRATE,
+			0, 27000000, 1, 6000000);
+	cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl,
+			V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+			0, 27000000, 1, 8000000);
+	cx2341x_ctrl_new_std(hdl,
+			V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0);
+	cxhdl->video_mute = cx2341x_ctrl_new_std(hdl,
+			V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0);
+	cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl,
+			V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080);
+
+	/* CX23415/6 specific */
+	cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+			V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+			V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0,
+			V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
+	cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+			0, 15, 1, 0);
+	cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+			V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+			V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+			0,
+			V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR);
+	cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+			V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+			V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+			0,
+			V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR);
+	cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+			V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+			V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO,
+			0,
+			V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
+	cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+			0, 31, 1, 8);
+	cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+			V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+			V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG,
+			0,
+			V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
+	cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+			0, 255, 1, 0);
+	cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+			0, 255, 1, 255);
+	cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+			0, 255, 1, 0);
+	cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+			V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+			0, 255, 1, 255);
+	cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
+			0, 1, 1, 0);
+
+	if (hdl->error) {
+		int err = hdl->error;
+
+		v4l2_ctrl_handler_free(hdl);
+		return err;
+	}
+
+	v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq);
+	v4l2_ctrl_cluster(2, &cxhdl->video_b_frames);
+	v4l2_ctrl_cluster(5, &cxhdl->stream_type);
+	v4l2_ctrl_cluster(2, &cxhdl->video_mute);
+	v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode);
+	v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type);
+	v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter);
+	v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top);
+
+	return 0;
+}
+EXPORT_SYMBOL(cx2341x_handler_init);
+
+void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz)
+{
+	cxhdl->is_50hz = is_50hz;
+	cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15;
+}
+EXPORT_SYMBOL(cx2341x_handler_set_50hz);
+
+int cx2341x_handler_setup(struct cx2341x_handler *cxhdl)
+{
+	int h = cxhdl->height;
+	int w = cxhdl->width;
+	int err;
+
+	err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0);
+	if (err)
+		return err;
+	err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz);
+	if (err)
+		return err;
+
+	if (v4l2_ctrl_g(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+		w /= 2;
+		h /= 2;
+	}
+	err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
+	if (err)
+		return err;
+	return v4l2_ctrl_handler_setup(&cxhdl->hdl);
+}
+EXPORT_SYMBOL(cx2341x_handler_setup);
+
+void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy)
+{
+	v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy);
+	v4l2_ctrl_grab(cxhdl->audio_encoding, busy);
+	v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy);
+	v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy);
+	v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy);
+	v4l2_ctrl_grab(cxhdl->stream_type, busy);
+	v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy);
+	v4l2_ctrl_grab(cxhdl->video_bitrate, busy);
+	v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy);
+}
+EXPORT_SYMBOL(cx2341x_handler_set_busy);
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] 29+ messages in thread

* [PATCH 11/15] [RFC] wm8775: convert to the new control framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (9 preceding siblings ...)
  2010-04-26  7:34 ` [PATCH 10/15] [RFC] cx2341x: convert to the " Hans Verkuil
@ 2010-04-26  7:34 ` Hans Verkuil
  2010-04-26  7:34 ` [PATCH 12/15] [RFC] cs53l32a: convert to " Hans Verkuil
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:34 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..9a8e3ad 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(state->mute))
 		return 0;
 	wm8775_write(sd, R21, 0x0c0);
 	wm8775_write(sd, R14, 0x1d4);
@@ -103,29 +110,21 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct v4l2_subdev *sd = to_sd(ctrl);
 	struct wm8775_state *state = to_state(sd);
 
-	if (ctrl->id != V4L2_CID_AUDIO_MUTE)
-		return -EINVAL;
-	ctrl->value = state->muted;
-	return 0;
-}
-
-static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-	struct wm8775_state *state = to_state(sd);
-
-	if (ctrl->id != V4L2_CID_AUDIO_MUTE)
-		return -EINVAL;
-	state->muted = ctrl->value;
-	wm8775_write(sd, R21, 0x0c0);
-	wm8775_write(sd, R14, 0x1d4);
-	wm8775_write(sd, R15, 0x1d4);
-	if (!state->muted)
-		wm8775_write(sd, R21, 0x100 + state->input);
-	return 0;
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		wm8775_write(sd, R21, 0x0c0);
+		wm8775_write(sd, R14, 0x1d4);
+		wm8775_write(sd, R15, 0x1d4);
+		if (!ctrl->val)
+			wm8775_write(sd, R21, 0x100 + state->input);
+		return 0;
+	}
+	return -EINVAL;
 }
 
 static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
@@ -139,8 +138,8 @@ static int wm8775_log_status(struct v4l2_subdev *sd)
 {
 	struct wm8775_state *state = to_state(sd);
 
-	v4l2_info(sd, "Input: %d%s\n", state->input,
-			state->muted ? " (muted)" : "");
+	v4l2_info(sd, "Input: %d\n", state->input);
+	v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
 	return 0;
 }
 
@@ -161,11 +160,20 @@ static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fre
 
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
+	.s_ctrl = wm8775_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops wm8775_core_ops = {
 	.log_status = wm8775_log_status,
 	.g_chip_ident = wm8775_g_chip_ident,
-	.g_ctrl = wm8775_g_ctrl,
-	.s_ctrl = wm8775_s_ctrl,
+	.g_ext_ctrls = v4l2_sd_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
+	.g_ctrl = v4l2_sd_g_ctrl,
+	.s_ctrl = v4l2_sd_s_ctrl,
+	.queryctrl = v4l2_sd_queryctrl,
+	.querymenu = v4l2_sd_querymenu,
 };
 
 static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
@@ -204,13 +212,24 @@ static int wm8775_probe(struct i2c_client *client,
 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
 			client->addr << 1, client->adapter->name);
 
-	state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
+	state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL);
 	if (state == NULL)
 		return -ENOMEM;
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
 	state->input = 2;
-	state->muted = 0;
+
+	v4l2_ctrl_handler_init(&state->hdl, 1);
+	state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+	sd->ctrl_handler = &state->hdl;
+	if (state->hdl.error) {
+		int err = state->hdl.error;
+
+		v4l2_ctrl_handler_free(&state->hdl);
+		kfree(state);
+		return err;
+	}
 
 	/* Initialize wm8775 */
 
@@ -247,9 +266,11 @@ static int wm8775_probe(struct i2c_client *client,
 static int wm8775_remove(struct i2c_client *client)
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct wm8775_state *state = to_state(sd);
 
 	v4l2_device_unregister_subdev(sd);
-	kfree(to_state(sd));
+	v4l2_ctrl_handler_free(&state->hdl);
+	kfree(state);
 	return 0;
 }
 
-- 
1.6.4.2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 12/15] [RFC] cs53l32a: convert to new control framework.
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (10 preceding siblings ...)
  2010-04-26  7:34 ` [PATCH 11/15] [RFC] wm8775: convert to the new " Hans Verkuil
@ 2010-04-26  7:34 ` Hans Verkuil
  2010-04-26  7:34 ` [PATCH 13/15] [RFC] wm8739: convert to the " Hans Verkuil
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:34 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..c405d05 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_sd_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
+	.g_ctrl = v4l2_sd_g_ctrl,
+	.s_ctrl = v4l2_sd_s_ctrl,
+	.queryctrl = v4l2_sd_queryctrl,
+	.querymenu = v4l2_sd_querymenu,
 };
 
 static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
@@ -150,6 +161,7 @@ static const struct v4l2_subdev_ops cs53l32a_ops = {
 static int cs53l32a_probe(struct i2c_client *client,
 			  const struct i2c_device_id *id)
 {
+	struct cs53l32a_state *state;
 	struct v4l2_subdev *sd;
 	int i;
 
@@ -163,9 +175,10 @@ static int cs53l32a_probe(struct i2c_client *client,
 	v4l_info(client, "chip found @ 0x%x (%s)\n",
 			client->addr << 1, client->adapter->name);
 
-	sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
-	if (sd == NULL)
+	state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL);
+	if (state == NULL)
 		return -ENOMEM;
+	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
 
 	for (i = 1; i <= 7; i++) {
@@ -174,15 +187,29 @@ static int cs53l32a_probe(struct i2c_client *client,
 		v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
 	}
 
+	v4l2_ctrl_handler_init(&state->hdl, 2);
+	v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+			V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0);
+	v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+	sd->ctrl_handler = &state->hdl;
+	if (state->hdl.error) {
+		int err = state->hdl.error;
+
+		v4l2_ctrl_handler_free(&state->hdl);
+		kfree(state);
+		return err;
+	}
+
 	/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
 
-	cs53l32a_write(sd, 0x01, (u8) 0x21);
-	cs53l32a_write(sd, 0x02, (u8) 0x29);
-	cs53l32a_write(sd, 0x03, (u8) 0x30);
-	cs53l32a_write(sd, 0x04, (u8) 0x00);
-	cs53l32a_write(sd, 0x05, (u8) 0x00);
-	cs53l32a_write(sd, 0x06, (u8) 0x00);
-	cs53l32a_write(sd, 0x07, (u8) 0x00);
+	cs53l32a_write(sd, 0x01, 0x21);
+	cs53l32a_write(sd, 0x02, 0x29);
+	cs53l32a_write(sd, 0x03, 0x30);
+	cs53l32a_write(sd, 0x04, 0x00);
+	cs53l32a_write(sd, 0x05, 0x00);
+	cs53l32a_write(sd, 0x06, 0x00);
+	cs53l32a_write(sd, 0x07, 0x00);
 
 	/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
 
@@ -197,9 +224,11 @@ static int cs53l32a_probe(struct i2c_client *client,
 static int cs53l32a_remove(struct i2c_client *client)
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct cs53l32a_state *state = to_state(sd);
 
 	v4l2_device_unregister_subdev(sd);
-	kfree(sd);
+	v4l2_ctrl_handler_free(&state->hdl);
+	kfree(state);
 	return 0;
 }
 
-- 
1.6.4.2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 13/15] [RFC] wm8739: convert to the new control framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (11 preceding siblings ...)
  2010-04-26  7:34 ` [PATCH 12/15] [RFC] cs53l32a: convert to " Hans Verkuil
@ 2010-04-26  7:34 ` Hans Verkuil
  2010-04-26  7:34 ` [PATCH 14/15] [RFC] ivtv: convert gpio subdev to " Hans Verkuil
  2010-04-26  7:34 ` [PATCH 15/15] [RFC] ivtv: convert to the " Hans Verkuil
  14 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:34 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..fe77086 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_sd_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
+	.g_ctrl = v4l2_sd_g_ctrl,
+	.s_ctrl = v4l2_sd_s_ctrl,
+	.queryctrl = v4l2_sd_queryctrl,
+	.querymenu = v4l2_sd_querymenu,
 };
 
 static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
@@ -288,17 +217,28 @@ static int wm8739_probe(struct i2c_client *client,
 	v4l_info(client, "chip found @ 0x%x (%s)\n",
 			client->addr << 1, client->adapter->name);
 
-	state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
+	state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL);
 	if (state == NULL)
 		return -ENOMEM;
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
-	state->vol_l = 0x17; /* 0dB */
-	state->vol_r = 0x17; /* 0dB */
-	state->muted = 0;
-	state->balance = 32768;
-	/* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */
-	state->volume = ((long)state->vol_l + 1) * 65535 / 31;
+	v4l2_ctrl_handler_init(&state->hdl, 2);
+	state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+			V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736);
+	state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+	state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+			V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+	sd->ctrl_handler = &state->hdl;
+	if (state->hdl.error) {
+		int err = state->hdl.error;
+
+		v4l2_ctrl_handler_free(&state->hdl);
+		kfree(state);
+		return err;
+	}
+	v4l2_ctrl_cluster(3, &state->volume);
+
 	state->clock_freq = 48000;
 
 	/* Initialize wm8739 */
@@ -317,15 +257,17 @@ static int wm8739_probe(struct i2c_client *client,
 	/* activate */
 	wm8739_write(sd, R9, 0x001);
 	/* set volume/mute */
-	wm8739_set_audio(sd);
+	v4l2_ctrl_handler_setup(&state->hdl);
 	return 0;
 }
 
 static int wm8739_remove(struct i2c_client *client)
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct wm8739_state *state = to_state(sd);
 
 	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(&state->hdl);
 	kfree(to_state(sd));
 	return 0;
 }
-- 
1.6.4.2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 14/15] [RFC] ivtv: convert gpio subdev to new control framework.
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (12 preceding siblings ...)
  2010-04-26  7:34 ` [PATCH 13/15] [RFC] wm8739: convert to the " Hans Verkuil
@ 2010-04-26  7:34 ` Hans Verkuil
  2010-04-26  7:34 ` [PATCH 15/15] [RFC] ivtv: convert to the " Hans Verkuil
  14 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:34 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 bf05c34..0a85705 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -622,6 +622,7 @@ struct ivtv {
 
 	struct v4l2_device v4l2_dev;
 	struct v4l2_subdev sd_gpio;	/* GPIO sub-device */
+	struct v4l2_ctrl_handler hdl_gpio;
 	u16 instance;
 
 	/* High-level state info */
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index aede061..463d58f 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_sd_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
+	.g_ctrl = v4l2_sd_g_ctrl,
+	.s_ctrl = v4l2_sd_s_ctrl,
+	.queryctrl = v4l2_sd_queryctrl,
+	.querymenu = v4l2_sd_querymenu,
 };
 
 static const struct v4l2_subdev_tuner_ops 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] 29+ messages in thread

* [PATCH 15/15] [RFC] ivtv: convert to the new control framework
  2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
                   ` (13 preceding siblings ...)
  2010-04-26  7:34 ` [PATCH 14/15] [RFC] ivtv: convert gpio subdev to " Hans Verkuil
@ 2010-04-26  7:34 ` Hans Verkuil
  14 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-04-26  7:34 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 0a85705..ff49e89 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -62,6 +62,7 @@
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 #include <media/tuner.h>
 #include <media/cx2341x.h>
 #include <media/ir-kbd-i2c.h>
@@ -621,8 +622,9 @@ struct ivtv {
 	struct ivtv_options options; 	/* user options */
 
 	struct v4l2_device v4l2_dev;
-	struct v4l2_subdev sd_gpio;	/* GPIO sub-device */
+	struct cx2341x_handler cxhdl;
 	struct v4l2_ctrl_handler hdl_gpio;
+	struct v4l2_subdev sd_gpio;	/* GPIO sub-device */
 	u16 instance;
 
 	/* High-level state info */
@@ -639,7 +641,6 @@ struct ivtv {
 	v4l2_std_id std_out;            /* current TV output standard */
 	u8 audio_stereo_mode;           /* decoder setting how to handle stereo MPEG audio */
 	u8 audio_bilingual_mode;        /* decoder setting how to handle bilingual MPEG audio */
-	struct cx2341x_mpeg_params params;              /* current encoder parameters */
 
 
 	/* Locking */
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index babcabd..0552044 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -148,12 +148,10 @@ void ivtv_release_stream(struct ivtv_stream *s)
 static void ivtv_dualwatch(struct ivtv *itv)
 {
 	struct v4l2_tuner vt;
-	u32 new_bitmap;
 	u32 new_stereo_mode;
-	const u32 stereo_mask = 0x0300;
-	const u32 dual = 0x0200;
+	const u32 dual = 0x02;
 
-	new_stereo_mode = itv->params.audio_properties & stereo_mask;
+	new_stereo_mode = v4l2_ctrl_g(itv->cxhdl.audio_mode);
 	memset(&vt, 0, sizeof(vt));
 	ivtv_call_all(itv, tuner, g_tuner, &vt);
 	if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
@@ -162,16 +160,10 @@ static void ivtv_dualwatch(struct ivtv *itv)
 	if (new_stereo_mode == itv->dualwatch_stereo_mode)
 		return;
 
-	new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);
-
-	IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
-			   itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
-
-	if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
-		itv->dualwatch_stereo_mode = new_stereo_mode;
-		return;
-	}
-	IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+	IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+			   itv->dualwatch_stereo_mode, new_stereo_mode);
+	if (v4l2_ctrl_s(itv->cxhdl.audio_mode, new_stereo_mode))
+		IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
 }
 
 static void ivtv_update_pgm_info(struct ivtv *itv)
@@ -867,7 +859,8 @@ int ivtv_v4l2_close(struct file *filp)
 		if (atomic_read(&itv->capturing) > 0) {
 			/* Undo video mute */
 			ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
-				itv->params.video_mute | (itv->params.video_mute_yuv << 8));
+				v4l2_ctrl_g(itv->cxhdl.video_mute) |
+				(v4l2_ctrl_g(itv->cxhdl.video_mute_yuv) << 8));
 		}
 		/* Done! Unmute and continue. */
 		ivtv_unmute(itv);
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 6422cf8..e47d38e 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -161,7 +161,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed)
 	data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
 	data[1] = (speed < 0);
 	data[2] = speed < 0 ? 3 : 7;
-	data[3] = itv->params.video_b_frames;
+	data[3] = v4l2_ctrl_g(itv->cxhdl.video_b_frames);
 	data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
 	data[5] = 0;
 	data[6] = 0;
@@ -338,8 +338,8 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *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;
@@ -567,7 +567,6 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
 {
 	struct ivtv_open_id *id = fh;
 	struct ivtv *itv = id->itv;
-	struct cx2341x_mpeg_params *p = &itv->params;
 	int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
 	int w = fmt->fmt.pix.width;
 	int h = fmt->fmt.pix.height;
@@ -575,15 +574,15 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *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(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
 		fmt->fmt.pix.width /= 2;
 	v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
 	return ivtv_g_fmt_vid_cap(file, fh, fmt);
@@ -1109,9 +1108,10 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
 
 	itv->std = *std;
 	itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
-	itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
-	itv->params.width = 720;
-	itv->params.height = itv->is_50hz ? 576 : 480;
+	itv->is_50hz = !itv->is_60hz;
+	cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+	itv->cxhdl.width = 720;
+	itv->cxhdl.height = itv->is_50hz ? 576 : 480;
 	itv->vbi.count = itv->is_50hz ? 18 : 12;
 	itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
 	itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
@@ -1152,7 +1152,7 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
 		ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
 		itv->main_rect.left = itv->main_rect.top = 0;
 		itv->main_rect.width = 720;
-		itv->main_rect.height = itv->params.height;
+		itv->main_rect.height = itv->cxhdl.height;
 		ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
 			720, itv->main_rect.height, 0, 0);
 		yi->main_rect = itv->main_rect;
@@ -1537,7 +1537,7 @@ static int ivtv_log_status(struct file *file, void *fh)
 	}
 	IVTV_INFO("Tuner:  %s\n",
 		test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
-	cx2341x_log_status(&itv->params, itv->v4l2_dev.name);
+	v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
 	IVTV_INFO("Status flags:    0x%08lx\n", itv->i_flags);
 	for (i = 0; i < IVTV_MAX_STREAMS; i++) {
 		struct ivtv_stream *s = &itv->streams[i];
@@ -1921,11 +1921,6 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
 	.vidioc_s_register 		    = ivtv_s_register,
 #endif
 	.vidioc_default 		    = ivtv_default,
-	.vidioc_queryctrl 		    = ivtv_queryctrl,
-	.vidioc_querymenu 		    = ivtv_querymenu,
-	.vidioc_g_ext_ctrls 		    = ivtv_g_ext_ctrls,
-	.vidioc_s_ext_ctrls 		    = ivtv_s_ext_ctrls,
-	.vidioc_try_ext_ctrls    	    = ivtv_try_ext_ctrls,
 };
 
 void ivtv_set_funcs(struct video_device *vdev)
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 6917c49..c735d03 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -208,6 +208,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
 
 	s->vdev->num = num;
 	s->vdev->v4l2_dev = &itv->v4l2_dev;
+	s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
 	s->vdev->fops = ivtv_stream_info[type].fops;
 	s->vdev->release = video_device_release;
 	s->vdev->tvnorms = V4L2_STD_ALL;
@@ -446,7 +447,6 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
 {
 	u32 data[CX2341X_MBOX_MAX_DATA];
 	struct ivtv *itv = s->itv;
-	struct cx2341x_mpeg_params *p = &itv->params;
 	int captype = 0, subtype = 0;
 	int enable_passthrough = 0;
 
@@ -467,7 +467,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
 		}
 		itv->mpg_data_received = itv->vbi_data_inserted = 0;
 		itv->dualwatch_jiffies = jiffies;
-		itv->dualwatch_stereo_mode = p->audio_properties & 0x0300;
+		itv->dualwatch_stereo_mode = v4l2_ctrl_g(itv->cxhdl.audio_mode);
 		itv->search_pack_header = 0;
 		break;
 
@@ -555,12 +555,12 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
 				itv->pgm_info_offset, itv->pgm_info_num);
 
 		/* Setup API for Stream */
-		cx2341x_update(itv, ivtv_api_func, NULL, p);
+		cx2341x_handler_setup(&itv->cxhdl);
 
 		/* mute if capturing radio */
 		if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
 			ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
-				1 | (p->video_mute_yuv << 8));
+				1 | (v4l2_ctrl_g(itv->cxhdl.video_mute_yuv) << 8));
 	}
 
 	/* Vsync Setup */
@@ -576,6 +576,8 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
 
 		clear_bit(IVTV_F_I_EOS, &itv->i_flags);
 
+		cx2341x_handler_set_busy(&itv->cxhdl, 1);
+
 		/* Initialize Digitizer for Capture */
 		/* Avoid tinny audio problem - ensure audio clocks are going */
 		v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
@@ -612,7 +614,6 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
 {
 	u32 data[CX2341X_MBOX_MAX_DATA];
 	struct ivtv *itv = s->itv;
-	struct cx2341x_mpeg_params *p = &itv->params;
 	int datatype;
 
 	if (s->vdev == NULL)
@@ -651,7 +652,7 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
 		break;
 	}
 	if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
-			p->width, p->height, p->audio_properties)) {
+			itv->cxhdl.width, itv->cxhdl.height, itv->cxhdl.audio_properties)) {
 		IVTV_DEBUG_WARN("Couldn't initialize decoder source\n");
 	}
 	return 0;
@@ -817,6 +818,8 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
 		return 0;
 	}
 
+	cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
 	/* Set the following Interrupt mask bits for capture */
 	ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
 	del_timer(&itv->dma_timer);
@@ -930,7 +933,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
 
 		/* Setup capture if not already done */
 		if (atomic_read(&itv->capturing) == 0) {
-			cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+			cx2341x_handler_setup(&itv->cxhdl);
+			cx2341x_handler_set_busy(&itv->cxhdl, 1);
 		}
 
 		/* Start Passthrough Mode */
@@ -951,6 +955,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
 	clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
 	clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
 	itv->output_mode = OUT_NONE;
+	if (atomic_read(&itv->capturing) == 0)
+		cx2341x_handler_set_busy(&itv->cxhdl, 0);
 
 	return 0;
 }
-- 
1.6.4.2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH 05/15] [RFC] saa7115: convert to the new control framework
  2010-04-26  7:33 ` [PATCH 05/15] [RFC] saa7115: convert to the new control framework Hans Verkuil
@ 2010-04-26 15:53   ` David Ellingsworth
  0 siblings, 0 replies; 29+ messages in thread
From: David Ellingsworth @ 2010-04-26 15:53 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, laurent.pinchart

On Mon, Apr 26, 2010 at 3:33 AM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> 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 72eaa66..8db056f 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_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;
>        }

This might not have been done before, but errors from sa71xx_write
should be propagated to caller. A successful write should not be
assumed. This applies for all uses of sa711x_write above. I'd probably
do the following:

1. Declare a local variable called rc initialized to -EINVAL
2. Store the result of sa711x_write into rc for each case.
3. Remove the default case.
4. Return the value of rc.

Regards,

David Ellingsworth

> @@ -1221,25 +1185,6 @@ static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
>        return 0;
>  }
>
> -static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
> -{
> -       switch (qc->id) {
> -       case V4L2_CID_BRIGHTNESS:
> -               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
> -       case V4L2_CID_CONTRAST:
> -       case V4L2_CID_SATURATION:
> -               return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
> -       case V4L2_CID_HUE:
> -               return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
> -       case V4L2_CID_CHROMA_AGC:
> -               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
> -       case V4L2_CID_CHROMA_GAIN:
> -               return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48);
> -       default:
> -               return -EINVAL;
> -       }
> -}
> -
>  static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
>  {
>        struct saa711x_state *state = to_state(sd);
> @@ -1516,17 +1461,27 @@ static int saa711x_log_status(struct v4l2_subdev *sd)
>                break;
>        }
>        v4l2_info(sd, "Width, Height:   %d, %d\n", state->width, state->height);
> +       v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
>        return 0;
>  }
>
>  /* ----------------------------------------------------------------------- */
>
> +static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
> +       .s_ctrl = saa711x_s_ctrl,
> +       .g_ctrl = saa711x_g_ctrl,
> +};
> +
>  static const struct v4l2_subdev_core_ops saa711x_core_ops = {
>        .log_status = saa711x_log_status,
>        .g_chip_ident = saa711x_g_chip_ident,
> -       .g_ctrl = saa711x_g_ctrl,
> -       .s_ctrl = saa711x_s_ctrl,
> -       .queryctrl = saa711x_queryctrl,
> +       .g_ext_ctrls = v4l2_sd_g_ext_ctrls,
> +       .try_ext_ctrls = v4l2_sd_try_ext_ctrls,
> +       .s_ext_ctrls = v4l2_sd_s_ext_ctrls,
> +       .g_ctrl = v4l2_sd_g_ctrl,
> +       .s_ctrl = v4l2_sd_s_ctrl,
> +       .queryctrl = v4l2_sd_queryctrl,
> +       .querymenu = v4l2_sd_querymenu,
>        .s_std = saa711x_s_std,
>        .reset = saa711x_reset,
>        .s_gpio = saa711x_s_gpio,
> @@ -1571,8 +1526,9 @@ static int saa711x_probe(struct i2c_client *client,
>  {
>        struct saa711x_state *state;
>        struct v4l2_subdev *sd;
> -       int     i;
> -       char    name[17];
> +       struct v4l2_ctrl_handler *hdl;
> +       int i;
> +       char name[17];
>        char chip_id;
>        int autodetect = !id || id->driver_data == 1;
>
> @@ -1611,15 +1567,37 @@ static int saa711x_probe(struct i2c_client *client,
>                return -ENOMEM;
>        sd = &state->sd;
>        v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
> +
> +       hdl = &state->hdl;
> +       v4l2_ctrl_handler_init(hdl, 6);
> +       /* add in ascending ID order */
> +       v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
> +                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
> +       v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
> +                       V4L2_CID_CONTRAST, 0, 127, 1, 64);
> +       v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
> +                       V4L2_CID_SATURATION, 0, 127, 1, 64);
> +       v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
> +                       V4L2_CID_HUE, -128, 127, 1, 0);
> +       state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
> +                       V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
> +       state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
> +                       V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
> +       sd->ctrl_handler = hdl;
> +       if (hdl->error) {
> +               int err = hdl->error;
> +
> +               v4l2_ctrl_handler_free(hdl);
> +               kfree(state);
> +               return err;
> +       }
> +       state->agc->flags |= V4L2_CTRL_FLAG_UPDATE;
> +       v4l2_ctrl_cluster(2, &state->agc);
> +
>        state->input = -1;
>        state->output = SAA7115_IPORT_ON;
>        state->enable = 1;
>        state->radio = 0;
> -       state->bright = 128;
> -       state->contrast = 64;
> -       state->hue = 0;
> -       state->sat = 64;
> -       state->chroma_agc = 1;
>        switch (chip_id) {
>        case '1':
>                state->ident = V4L2_IDENT_SAA7111;
> @@ -1667,6 +1645,7 @@ static int saa711x_probe(struct i2c_client *client,
>        if (state->ident > V4L2_IDENT_SAA7111A)
>                saa711x_writeregs(sd, saa7115_init_misc);
>        saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
> +       v4l2_ctrl_handler_setup(hdl);
>
>        v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
>                saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
> @@ -1681,6 +1660,7 @@ static int saa711x_remove(struct i2c_client *client)
>        struct v4l2_subdev *sd = i2c_get_clientdata(client);
>
>        v4l2_device_unregister_subdev(sd);
> +       v4l2_ctrl_handler_free(sd->ctrl_handler);
>        kfree(to_state(sd));
>        return 0;
>  }
> --
> 1.6.4.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 06/15] [RFC] msp3400: convert to the new control framework
  2010-04-26  7:33 ` [PATCH 06/15] [RFC] msp3400: " Hans Verkuil
@ 2010-04-26 16:17   ` David Ellingsworth
  0 siblings, 0 replies; 29+ messages in thread
From: David Ellingsworth @ 2010-04-26 16:17 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, laurent.pinchart

On Mon, Apr 26, 2010 at 3:33 AM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> 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..de0da40 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;

The return value here should reflect if the update was successful or
not. msp_write_dsp can fail and if does the error should be propagated
to the caller and the value of the control should not be updated.
Also, msp_write_dsp and msp_read_dsp should probably return -EIO in
case of failures rather than -1.

Regards,

David Ellingsworth

<snip to end>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
  2010-04-26  7:33 ` [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API Hans Verkuil
@ 2010-05-02 20:39   ` Laurent Pinchart
  2010-05-02 22:21     ` Hans Verkuil
  0 siblings, 1 reply; 29+ messages in thread
From: Laurent Pinchart @ 2010-05-02 20:39 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

I finally got some time to review your RFC. Let's see until what time it will 
keep me awake :-)

On Monday 26 April 2010 09:33:38 Hans Verkuil wrote:
> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
> ---
>  Documentation/video4linux/v4l2-controls.txt |  543 ++++++++++++++++++++++++
>  1 files changed, 543 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..29a92b4
> --- /dev/null
> +++ b/Documentation/video4linux/v4l2-controls.txt
> @@ -0,0 +1,543 @@
> +Introduction
> +============
> +
> +The V4L2 control API seems simple enough, but quickly becomes very hard to
> +implement correctly in drivers. But much of the code needed to handle
> controls
> +is actually not driver specific and can be moved to the V4L core framework.
> +
> +After all, the only part that a driver developer is interested in is:
> +
> +1) How do I add a control?

Do you think we will need the ability to remove controls ?

> +2) How do I set the control? (i.e. s_ctrl)
> +
> +And occasionally:
> +
> +3) How do I update the control's value? (i.e. g_ctrl)

The wording of 2 and 3 got me a bit confused. "set"ting a control usually 
refers to the operation performed by userspace. Similarly, without the "i.e. 
g_ctrl" comment, I would have thought "update" meant writing the value to the 
hardware.

> +4) How do I validate the user's proposed control value? (i.e. try_ctrl)
> +
> +All the rest is something that can be done centrally.
> +
> +The control framework was created in order to implement all the rules of
> the
> +V4L2 specification with respect to controls in a central place. And
> to make +life as easy as possible for the driver developer.
> +
> +
> +Objects in the framework
> +========================
> +
> +There are two main objects:
> +
> +The v4l2_ctrl object describes the control properties and keeps track of
> the
> +control's value (both the current value and the proposed new value).

I'm not sure v4l2_ctrl is a good name. We already have a v4l2_control 
structure, and using the abbreviated name for the in-kernel version is going 
to be confusing.

It's obviously too late to change v4l2_control to something else. 
v4l2_control_info and v4l2_control_value are two names that come to my mind. 
Actually, it might be a good idea to split the v4l2_ctrl structure into a 
static driver-wide structure (v4l2_control_info) and an instance-specific 
structure (v4l2_control_value). There's no point in storing the same static 
data (function pointers, names, ...) for identical controls in different 
devices.

> +
> +v4l2_ctrl_handler is the object that keeps track of controls. It maintains
> a
> +list of v4l2_ctrl objects that it owns and another list of references to
> +controls, possibly to controls owned by other handlers.
> +
> +
> +Basic usage
> +===========
> +
> +1) Prepare the driver:
> +
> +- Add the handler to your main bridge driver or sub-device driver top-level
> +  struct:
> +
> +	struct foo_dev {
> +		...
> +		struct v4l2_ctrl_handler hdl;

Please use more descriptive names in the examples, such as "ctrl_handler". I 
expect some (many ?) developers to use the same names as the documentation 
does, making the driver code a bit difficult to read.

> +		...
> +	};
> +
> +	struct foo_dev *foo;
> +
> +- Initialize handler:
> +
> +	v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls);
> +
> +  The second argument is a hint telling the function how many controls
> this
> +  handled is expected to handle. It will allocate a hashtable based on this

s/handled/handler/

> +  information. It is a hint only.
> +
> +- Hooking the control handler into a driver:
> +
> +  When a subdevice is being registered with a bridge driver and the
> +  ctrl_handler fields of both v4l2_subdev and v4l2_device are set, then
> the
> +  controls of the subdev will become automatically available in the
> bridge
> +  driver as well. If the subdev driver contains controls that already exist
> in
> +  the bridge driver, then those will be skipped (so a bridge driver can
> always
> +  override a subdev control).

I think you should split the documentation differently. You're mixing video 
devices, bridges and subdevs. Developers of drivers that use no subdev will 
have trouble understanding the documentation. You should first describe how to 
use the framework in a simple driver (no host, bridge, subdev, ... just a 
foo_dev and a single video_device), and then add sections to describe bridge-
specific and subdev-specific APIs.

> +
> +  How to hook the handler into a bridge driver:
> +
> +	foo->v4l2_dev.ctrl_handler = &foo->hdl;

Is v4l2_dev an instance of video_device or v4l2_device ? It would be nice to 
add the field to the foo_dev structure in the example.

> +
> +  And whenever you call video_register_device() you must set the
> +  ctrl_handler field of struct video_device as well:
> +
> +	vdev->ctrl_handler = &foo->hdl;

I suppose that the above foo->v4l2_dev is an instance of v4l2_device then. 
Does the framework require v4l2_device ? If so, that should be explicit in the 
introduction.

Why can't the framework find the ctrl_handler instance from the video_device's 
v4l2_device parent ?

> +  Finally, remove all control functions from your v4l2_ioctl_ops:
> +  vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl,
> +  vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls.
> +  Those are now no longer needed.
> +
> +  How to hook the control handler into a subdev driver:
> +
> +	foo->sd.ctrl_handler = &foo->hdl;
> +
> +  And set all core control ops in your struct v4l2_subdev_core_ops to
> these
> +  helpers:
> +
> +	.queryctrl = v4l2_sd_queryctrl,
> +	.querymenu = v4l2_sd_querymenu,
> +	.g_ctrl = v4l2_sd_g_ctrl,
> +	.s_ctrl = v4l2_sd_s_ctrl,
> +	.g_ext_ctrls = v4l2_sd_g_ext_ctrls,
> +	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
> +	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,

s/sd/subdev/ ?

> +
> +  This is for backwards compatibility. Once all bridge drivers are
> converted
> +  these control ops can be removed just as they are already for bridge
> drivers.
> +
> +- Clean up the handler at the end:
> +
> +	v4l2_ctrl_handler_free(&foo->hdl);
> +
> +
> +2) Add controls:
> +
> +Typically done right after the handler_init:
> +
> +	v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls);
> +	v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops,
> +			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
> +	v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops,
> +			V4L2_CID_CONTRAST, 0, 255, 1, 128);

Could you add the v4l2_ctrl_new_std function prototype to the documentation ? 
I expect the parameters to be min, max, step and default, but it would be 
better to make it explicit.

Should't v4l2_ctrl_new_std take a control type as well ?

What about hardware for which the boundaries are only known at runtime, or 
could depend on the values of other controls ? I'm thinking about UVC devices 
for instance, the boundaries, step and default values need to be retrieved 
from the hardware. I currently do that at runtime when the control is queried 
for the first time and cache the values, as doing it during initialization 
(probe function) crashes a few webcams. That doesn't seem to be possible with 
the control framework.

> +	...
> +	if (foo->hdl.error) {
> +		int err = foo->hdl.error;
> +
> +		v4l2_ctrl_handler_free(&foo->hdl);
> +		return err;
> +	}
> +
> +The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new
> +control, but if you do not need to access the pointer outside the control
> ops,
> +then there is no need to store it.
> +
> +Note that if something fails, the function will return NULL or an error
> and
> +set hdl->error to the error code. If hdl->error was already set, then it
> +will just return and do nothing. This is also true for
> v4l2_ctrl_handler_init
> +if it cannot allocate the internal data structure.
> +
> +This makes it easy to init the handler and just add all controls and only
> check
> +the error code at the end. Saves a lot of repetitive error checking.

I would still check the v4l2_ctrl_handler_init return value explicitly, but 
that may be just me.

> +It is recommended to add controls in ascending control ID order: it will be
> +a bit faster that way.

As long as it's not required that's OK. It would be difficult to do so on UVC 
hardware, as controls are discovered based on information reported by the 
device, and those are obviously not ordered based on the V4L2 CIDs :-)

> +3) Optionally force initial control setup:
> +
> +	v4l2_ctrl_handler_setup(&foo->hdl);
> +
> +This will call s_ctrl for all controls unconditionally. Effectively this
> +initializes the hardware to the default control values. It is recommended
> +that you do this.

What about the other way around, if I want to initialize the framework with 
the current values retrieved from the hardware ?

> +4) Finally: implement the v4l2_ctrl_ops
> +
> +	static const struct v4l2_ctrl_ops foo_ctrl_ops = {
> +		.s_ctrl = foo_s_ctrl,
> +	};
> +
> +Usually all you need is s_ctrl:
> +
> +	static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
> +	{
> +		struct foo *state = container_of(ctrl->handler, struct foo, hdl);
> +
> +		switch (ctrl->id) {
> +		case V4L2_CID_BRIGHTNESS:
> +			write_reg(0x123, ctrl->val);
> +			break;
> +		case V4L2_CID_CONTRAST:
> +			write_reg(0x456, ctrl->val);
> +			break;
> +		}
> +		return 0;
> +	}
> +
> +The control ops are called with the v4l2_ctrl pointer as argument.
> +The new control value has already been validated, so all you need to do is
> +to actually update the hardware registers.
> +
> +You're done! And this is sufficient for most of the drivers we have. No
> need
> +to do any validation of control values, or implement QUERYCTRL/QUERYMENU.
> And
> +G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported.
> +
> +
> +==========================================================================
> =====
> +
> +The remainder of this document deals with more advanced topics and
> scenarios.
> +In practice the basic usage as described above is sufficient for most
> drivers.
> +
> +==========================================================================
> =====
> +
> +
> +Accessing Control Values
> +========================
> +
> +The v4l2_ctrl struct contains these two unions:
> +
> +	/* The current control value. */
> +	union {
> +		s32 val;
> +		s64 val64;
> +		char *string;
> +	} cur;
> +
> +	/* The new control value. */
> +	union {
> +		s32 val;
> +		s64 val64;
> +		char *string;
> +	};
> +
> +Within the control ops you can freely use these. The val and val64 speak
> for
> +themselves. The string pointers point to character buffers of length
> +ctrl->maximum + 1, and are always 0-terminated.
> +
> +For g_ctrl you have to update the current control values like this:
> +
> +	ctrl->cur.val = read_reg(0x123);
> +
> +The 'new value' union is not relevant in g_ctrl.

When is g_ctrl called ? When the userspace applications issues a VIDIOC_G_CTRL 
or VIDIOC_G_EXT_CTRLS ioctl ? I suppose the operation only needs to be 
implemented if the device needs to be queried because it modified the control 
value on its own (possibly as a result of setting another control). What if 
some controls need to be handled that way, while the others are not self-
modifying ?

> +For try/s_ctrl the new values (i.e. as passed by the user) are filled in
> and
> +you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
> +contains the current value, which you can use (but not change!) as well.
> +
> +If s_ctrl returns 0 (OK), then the control framework will copy the new
> final
> +values to the 'cur' union.
> +
> +While in g/s/try_ctrl you can access the value of all controls owned by
> the
> +same handler since the handler's lock is held. Do not attempt to access
> +the value of controls owned by other handlers, though.
> +
> +Elsewhere in the driver you have to be more careful. You cannot just refer
> +to the current control values without locking.
> +
> +There are two simple helper functions defined that will get or set a
> single
> +control value safely:
> +
> +	s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl);
> +	int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);

I suppose that v4l2_ctrl_s can be used if the device notifies the driver that 
a control has changed. Is that correct ?

I don't really like the names of those two functions. For instance, 
v4l2_ctrl_s looks like it will call the driver to set a control.

> +
> +Don't use these inside the control ops g/s/try_ctrl, though, that will
> fail.
> +
> +You can also take the handler lock yourself:
> +
> +	mutex_lock(&state->hdl.lock);
> +	printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string);
> +	printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val);
> +	mutex_unlock(&state->hdl.lock);
> +
> +
> +Menu Controls
> +=============
> +
> +Menu controls use the 'step' value differently compared to other control
> +types. The v4l2_ctrl struct contains this union:
> +
> +	union {
> +		u32 step;
> +		u32 menu_skip_mask;
> +	};
> +
> +For menu controls menu_skip_mask is used. What it does is that it allows
> you
> +to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU
> +implementation where you can return -EINVAL if a certain menu item is not
> +present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for
> +menu controls.
> +
> +A good example is the MPEG Audio Layer II Bitrate menu control where the
> +menu is a list of standardized possible bitrates. But in practice hardware
> +implementations will only support a subset of those. By setting the skip
> +mask you can tell the framework which menu items should be skipped.
> Setting
> +it to 0 means that all menu items are supported.
> +
> +So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need to
> remember
> +that 'step' means 'skip mask' for menu controls. If you put in '1' by
> mistake,
> +then the first menu item will be skipped.

What about adding a v4l2_ctrl_new_menu then ? That will avoid such a mistake.

> +The v4l2_ctrl_new_std_menu can be used to add menu controls more easily:
> it
> +will calculate the min and max values automatically based on the size of
> the
> +menu, and it has a proper 'mask' argument instead of 'step'.

OK :-) Shouldn't v4l2_ctrl_new_custom be restricted to non-menu controls then 
? Looking at the code, it should probably become an internal function, with 
another function added for custom controls without menu support.

> +
> +
> +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.

What you refer to as active/inactive is usually referred to as 
enabled/disabled in the GUI world. Might be worth using the same convention.

> +
> +The other flag is the grabbed flag. A grabbed control means that you
> cannot
> +change it because it is in use by some resource. Typical examples are MPEG
> +bitrate controls that cannot be changed while capturing is in progress.
> +
> +If a control is set to 'grabbed' using v4l2_ctrl_grab(), then the
> framework
> +will return -EBUSY if an attempt is made to set this control.
> +
> +Since this flag is used by the framework the v4l2_ctrl_grab function will
> +take the control handler's lock. So it cannot be called from within the
> +control ops. Instead this is typically called from the driver when it
> +starts streaming.
> +
> +
> +Control Clusters
> +================
> +
> +By default all controls are independent from the others. But in more
> +complex scenarios you can get dependencies from one control to another.
> +In that case you need to 'cluster' them:
> +
> +	struct foo {
> +		struct v4l2_ctrl_handler hdl;
> +		struct v4l2_ctrl *volume;
> +		struct v4l2_ctrl *mute;
> +		...
> +	};
> +
> +	state->volume = v4l2_ctrl_new_std(&state->hdl, ...);
> +	state->mute = v4l2_ctrl_new_std(&state->hdl, ...);
> +	v4l2_ctrl_cluster(2, &state->volume);

What's the first argument to v4l2_ctrl_cluster ? The number of controls in the 
cluster ? Does that imply that they need to be added in a sequence, right 
before v4l2_ctrl_cluster is called ? That seems a bit awkward. Wouldn't it be 
better to specify the relationships between controls explicitly, maybe by 
passing a pointer to the master control when creating the 'slave' controls in 
the cluster ?

> +From now on whenever one or more of the controls belonging to the same
> +cluster is set (or 'gotten', or 'tried'), only the control ops of the
> first
> +control ('volume' in this example) is called. You effectively create a new
> +composite control. Similar to how a 'struct' works in C.
> +
> +So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you should
> set
> +all two controls belonging to the 'volume' cluster:
> +
> +	static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
> +	{
> +		struct foo *state = container_of(ctrl->handler, struct foo, hdl);
> +
> +		switch (ctrl->id) {
> +		case V4L2_CID_AUDIO_VOLUME:
> +			/* volume cluster */
> +			write_reg(0x123, state->mute->val ? 0 : ctrl->val);
> +			break;
> +		case V4L2_CID_CONTRAST:
> +			write_reg(0x456, ctrl->val);
> +			break;
> +		}
> +		return 0;
> +	}
> +
> +In the example above the following are equivalent for the VOLUME case:
> +
> +	ctrl == state->volume == ctrl->cluster[0]
> +	state->mute == ctrl->cluster[1]
> +
> +Note that controls in a cluster may be NULL. For example, if for some
> +reason mute was never added (because the hardware doesn't support that
> +particular feature), then mute will be NULL. So in that case we have a
> +cluster of 2 controls, of which only 1 is actually instantiated. The
> +only restriction is that the first control of the cluster must already be
> +present, since that is the 'master' control of the cluster. The master
> +control is the one that identifies the cluster and that provides the
> +pointer to the v4l2_ctrl_ops struct that is used for that cluster.
> +
> +
> +VIDIOC_LOG_STATUS Support
> +=========================
> +
> +This ioctl allow you to dump the current status of a driver to the kernel
> log.

This has nothing to do with the controls framework, but shouldn't that ioctl 
be restricted to root only ? It can potentially dump a lot of information to 
the kernel log, and thus system logs.

> +The v4l2_ctrl_handler_log_status(hdl, prefix) can be used to dump the value
> of
> +the controls owned by the given handler to the log. You can supply a prefix
> +as well. If the prefix didn't end with a space, then ': ' will be added for
> +you.
> +
> +
> +Different Handlers for Different Video Nodes
> +============================================
> +
> +Usually the bridge driver has just one control handler that is global for
> +all video nodes. But you can also specify different control handlers for
> +different video nodes. It's no problem if there are no subdevs involved.
> +But if there are, then you need to block the automatic merging of subdev
> +controls to the global control handler. You do that by simply setting the
> +ctrl_handler field in struct v4l2_device to NULL.
> +
> +After each subdev was added, you will then have to call
> v4l2_ctrl_add_handler
> +manually to add the subdev's control handler (sd->ctrl_handler) to the
> desired
> +bridge control handler.

Do you mean video device instead of bridge here ?

I wouldn't mention "bridge" in here. I assume that by bridge you mean 
v4l2_device. Please use that name directly. The term "bridge" is only 
applicable to a subset of the v4l2_device use cases.

Can controls for a specific subdev be reported through more than one video 
device node, but not all of them ?

> +
> +If you want to have one handler (e.g. for a radio device node) have a
> subset
> +of another handler (e.g. for a video device node), then you can first add
> +the controls to the first handler, add the other controls to the second
> +handler and finally add the first handler to the second. For example:
> +
> +	v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
> +	v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
> +	v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...);
> +	v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_CONTRAST, ...);
> +	v4l2_ctrl_add_handler(&video_hdl, &radio_hdl);
> +
> +Or you can add specific controls to a handler:
> +
> +	volume = v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_AUDIO_VOLUME, ...);
> +	v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_BRIGHTNESS, ...);
> +	v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_CONTRAST, ...);
> +	v4l2_ctrl_add_ctrl(&radio_hdl, volume);
> +
> +What you should not do is make two identical controls for two handlers.
> +For example:
> +
> +	v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
> +	v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_AUDIO_MUTE, ...);
> +
> +This would be bad since muting the radio would not change the video mute
> +control. The rule is to have one control for each hardware 'knob' that you
> +can twiddle.
> +
> +
> +Finding Controls
> +================
> +
> +Normally you have created the controls yourself and you can store the
> struct
> +v4l2_ctrl pointer into your own struct.
> +
> +But sometimes you need to find a control from another handler that you do
> +not own. For example, if you have to find a volume control from a subdev.
> +
> +You can do that by calling v4l2_ctrl_find:
> +
> +	struct v4l2_ctrl *volume;
> +
> +	volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);
> +
> +Since v4l2_ctrl_find will lock the handler you have to be careful where
> you
> +use it. For example, this is not a good idea:
> +
> +	struct v4l2_ctrl_handler hdl;
> +
> +	v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...);
> +	v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_CONTRAST, ...);
> +
> +...and in video_ops.s_ctrl:
> +
> +	case V4L2_CID_BRIGHTNESS:
> +		contrast = v4l2_find_ctrl(&hdl, V4L2_CID_CONTRAST);
> +		...
> +
> +When s_ctrl is called by the framework the hdl.lock is already taken, so
> +attempting to find another control from the same handler will deadlock.
> +
> +It is recommended not to use this function from inside the control ops.
> +
> +
> +Inheriting Controls
> +===================
> +
> +When one control handler is added to another using v4l2_ctrl_add_handler,
> then
> +by default all controls from one are merged to the other. But a subdev
> might
> +have low-level controls that make sense for some advanced embedded system,
> but
> +not when it is used in consumer-level hardware. In that case you want to
> keep
> +those low-level controls local to the subdev. You can do this by simply
> +setting the 'is_private' flag of the control to 1:
> +
> +	ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...);
> +	if (ctrl)
> +		ctrl->is_private = 1;

Wouldn't it make more sense to pass that as an argument to 
v4l2_ctrl_new_custom ?

I'm actually wondering if it wouldn't be better to pass a pointer to a 
structure with the required information (maybe the v4l2_control_info structure 
I mentioned earlier ?) to v4l2_ctrl_new_custom.

> +These controls will now be skipped when v4l2_ctrl_add_handler is called.
> +
> +
> +Strict Control Validation
> +=========================
> +
> +By default when the application wants to change an integer control the
> value
> +passed to the framework will automatically be modified to map to the
> provided
> +minimum, maximum and step values of the control. If instead you just want
> to
> +validate the value and not modify it, then set the 'strict_validation' flag
> of
> +the control:
> +
> +	ctrl->strict_validation = 1;
> +
> +Now -ERANGE will be returned if the new value does not match the control's
> +requirements.

Why do we need the two behaviours ? Wouldn't it be better to standardize on 
one of them ?

> +This is currently specific to integer controls. The value for boolean
> controls
> +is always mapped to 0 or 1, menu and string controls are already validated
> +strictly, and integer64 controls are not validated at all.
> +
> +
> +V4L2_CTRL_TYPE_CTRL_CLASS Controls
> +==================================
> +
> +Controls of this type can be used by GUIs to get the name of the control
> class.
> +A fully featured GUI can make a dialog with multiple tabs with each tab
> +containing the controls belonging to a particular control class. The name
> of
> +each tab can be found by querying a special control with ID <control class
> | 1>.
> +
> +Drivers do not have to care about this. The framework will automatically
> add
> +a control of this type whenever the first control belonging to a new
> control
> +class is added.
> +
> +
> +Differences from the Spec
> +=========================
> +
> +There are a few places where the framework acts slightly differently from
> the
> +V4L2 Specification. Those differences are described in this section. We
> will
> +have to see whether we need to adjust the spec or not.
> +
> +1) It is no longer required to have all controls contained in a
> +v4l2_ext_control array be from the same control class. The framework will
> be
> +able to handle any type of control in the array. You need to set ctrl_class
> +to 0 in order to enable this. If ctrl_class is non-zero, then it will still
> +check that all controls belong to that control class.
> +
> +If you set ctrl_class to 0 and count to 0, then it will only return an
> error
> +if there are no controls at all.

I don't know why we had such a limitation in the first place. I would indeed 
remove it completely.

> +
> +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.

Agreed. We should change the spec.

> +4) Attempting to write to a read-only control will return -EACCES instead
> of
> +-EINVAL as the spec says.

This makes sense as well.

> +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.

I still feel a bit awkward about the interface. One particular point that 
might require attention is the split of the v4l2_ctrl structure into 
v4l2_control_info and v4l2_control_value structures (the names are not set 
into stone). I would also like to see if we can't pass pointers to 
v4l2_control_info (or similar) to the control creation functions instead of a 
plethora of arguments.

I'll now have a look at the code. As there are already quite a few comments on 
the documentation I won't perform a very in-depth code review though, I'll 
save that for later after we agree on the spec :-)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 08/15] [RFC] cx25840/ivtv: replace ugly priv control with s_config
  2010-04-26  7:33 ` [PATCH 08/15] [RFC] cx25840/ivtv: replace ugly priv control with s_config Hans Verkuil
@ 2010-05-02 20:41   ` Laurent Pinchart
  2010-05-02 22:25     ` Hans Verkuil
  0 siblings, 1 reply; 29+ messages in thread
From: Laurent Pinchart @ 2010-05-02 20:41 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

On Monday 26 April 2010 09:33:54 Hans Verkuil wrote:
> 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 f2461cd..b8aa5d2 100644
> --- a/drivers/media/video/cx25840/cx25840-core.c
> +++ b/drivers/media/video/cx25840/cx25840-core.c

[snip]

> @@ -1601,10 +1593,25 @@ static int cx25840_log_status(struct v4l2_subdev
> *sd) return 0;
>  }
> 
> +static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void
> *platform_data) +{
> +	struct cx25840_state *state = to_state(sd);
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	if (platform_data) {
> +		struct cx25840_platform_data *pdata = platform_data;
> +
> +		state->pvr150_workaround = pdata->pvr150_workaround;
> +		set_input(client, state->vid_input, state->aud_input);
> +	}
> +	return 0;
> +}
> +

You've told me that s_config was only meant for I2C devices in pre-2.6.26 
kernels. Shouldn't this be done in the probe function instead ?

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header.
  2010-04-26  7:33 ` [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header Hans Verkuil
@ 2010-05-02 20:42   ` Laurent Pinchart
  2010-05-03  6:42     ` Hans Verkuil
  0 siblings, 1 reply; 29+ messages in thread
From: Laurent Pinchart @ 2010-05-02 20:42 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

On Monday 26 April 2010 09:33:33 Hans Verkuil wrote:
> To make it easier to determine whether all controls are added in
> v4l2-ctrls.c the case statements inside the switch are re-ordered to match
> the header.
> 
> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>

This patch should be merged with the previous one.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
  2010-05-02 20:39   ` Laurent Pinchart
@ 2010-05-02 22:21     ` Hans Verkuil
  2010-05-06 21:36       ` Laurent Pinchart
  2010-05-12 11:00       ` Sakari Ailus
  0 siblings, 2 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-05-02 22:21 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media

On Sunday 02 May 2010 22:39:11 Laurent Pinchart wrote:
> Hi Hans,
> 
> I finally got some time to review your RFC. Let's see until what time it will 
> keep me awake :-)
> 
> On Monday 26 April 2010 09:33:38 Hans Verkuil wrote:
> > Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
> > ---
> >  Documentation/video4linux/v4l2-controls.txt |  543 ++++++++++++++++++++++++
> >  1 files changed, 543 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..29a92b4
> > --- /dev/null
> > +++ b/Documentation/video4linux/v4l2-controls.txt
> > @@ -0,0 +1,543 @@
> > +Introduction
> > +============
> > +
> > +The V4L2 control API seems simple enough, but quickly becomes very hard to
> > +implement correctly in drivers. But much of the code needed to handle
> > controls
> > +is actually not driver specific and can be moved to the V4L core framework.
> > +
> > +After all, the only part that a driver developer is interested in is:
> > +
> > +1) How do I add a control?
> 
> Do you think we will need the ability to remove controls ?

I am not aware of any use case. The data structures used will make it hard
to actually remove a control. But you can still disable a control by setting
a flag, should this become necessary.

> > +2) How do I set the control? (i.e. s_ctrl)
> > +
> > +And occasionally:
> > +
> > +3) How do I update the control's value? (i.e. g_ctrl)
> 
> The wording of 2 and 3 got me a bit confused. "set"ting a control usually 
> refers to the operation performed by userspace. Similarly, without the "i.e. 
> g_ctrl" comment, I would have thought "update" meant writing the value to the 
> hardware.

Good point, that was a poor choice of words. I'll fix this.

> 
> > +4) How do I validate the user's proposed control value? (i.e. try_ctrl)
> > +
> > +All the rest is something that can be done centrally.
> > +
> > +The control framework was created in order to implement all the rules of
> > the
> > +V4L2 specification with respect to controls in a central place. And
> > to make +life as easy as possible for the driver developer.
> > +
> > +
> > +Objects in the framework
> > +========================
> > +
> > +There are two main objects:
> > +
> > +The v4l2_ctrl object describes the control properties and keeps track of
> > the
> > +control's value (both the current value and the proposed new value).
> 
> I'm not sure v4l2_ctrl is a good name. We already have a v4l2_control 
> structure, and using the abbreviated name for the in-kernel version is going 
> to be confusing.

Originally it was called v4l2_ctrl_info but that became very cumbersome.
It's not really an info object anyway, it really describes a control. When
using this framework the driver no longer sees the v4l2_control anymore, so
from the point of view of the driver there is only v4l2_ctrl.

> It's obviously too late to change v4l2_control to something else. 
> v4l2_control_info and v4l2_control_value are two names that come to my mind. 
> Actually, it might be a good idea to split the v4l2_ctrl structure into a 
> static driver-wide structure (v4l2_control_info) and an instance-specific 
> structure (v4l2_control_value). There's no point in storing the same static 
> data (function pointers, names, ...) for identical controls in different 
> devices.

Other than the name there isn't anything that is static. And the name is
already static.

> 
> > +
> > +v4l2_ctrl_handler is the object that keeps track of controls. It maintains
> > a
> > +list of v4l2_ctrl objects that it owns and another list of references to
> > +controls, possibly to controls owned by other handlers.
> > +
> > +
> > +Basic usage
> > +===========
> > +
> > +1) Prepare the driver:
> > +
> > +- Add the handler to your main bridge driver or sub-device driver top-level
> > +  struct:
> > +
> > +	struct foo_dev {
> > +		...
> > +		struct v4l2_ctrl_handler hdl;
> 
> Please use more descriptive names in the examples, such as "ctrl_handler". I 
> expect some (many ?) developers to use the same names as the documentation 
> does, making the driver code a bit difficult to read.

Good point.

> 
> > +		...
> > +	};
> > +
> > +	struct foo_dev *foo;
> > +
> > +- Initialize handler:
> > +
> > +	v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls);
> > +
> > +  The second argument is a hint telling the function how many controls
> > this
> > +  handled is expected to handle. It will allocate a hashtable based on this
> 
> s/handled/handler/

Thanks.

> 
> > +  information. It is a hint only.
> > +
> > +- Hooking the control handler into a driver:
> > +
> > +  When a subdevice is being registered with a bridge driver and the
> > +  ctrl_handler fields of both v4l2_subdev and v4l2_device are set, then
> > the
> > +  controls of the subdev will become automatically available in the
> > bridge
> > +  driver as well. If the subdev driver contains controls that already exist
> > in
> > +  the bridge driver, then those will be skipped (so a bridge driver can
> > always
> > +  override a subdev control).
> 
> I think you should split the documentation differently. You're mixing video 
> devices, bridges and subdevs. Developers of drivers that use no subdev will 
> have trouble understanding the documentation. You should first describe how to 
> use the framework in a simple driver (no host, bridge, subdev, ... just a 
> foo_dev and a single video_device), and then add sections to describe bridge-
> specific and subdev-specific APIs.

Good idea.
 
> > +
> > +  How to hook the handler into a bridge driver:
> > +
> > +	foo->v4l2_dev.ctrl_handler = &foo->hdl;
> 
> Is v4l2_dev an instance of video_device or v4l2_device ? It would be nice to 
> add the field to the foo_dev structure in the example.

Good point. I'll add that.
 
> > +
> > +  And whenever you call video_register_device() you must set the
> > +  ctrl_handler field of struct video_device as well:
> > +
> > +	vdev->ctrl_handler = &foo->hdl;
> 
> I suppose that the above foo->v4l2_dev is an instance of v4l2_device then. 
> Does the framework require v4l2_device ? If so, that should be explicit in the 
> introduction.

Yes, it does require v4l2_device. I'll make that explicit.
 
> Why can't the framework find the ctrl_handler instance from the video_device's 
> v4l2_device parent ?

Sometimes you want to provide different control handlers to different
video devices. E.g. /dev/radio0 may have only a small subset of the controls
of /dev/video0 (bttv does that, for example).

One option is to automatically let video_register_device copy ctrl_handler
from the v4l2_dev struct, but I think it is clearer if it is explicitly set.

> > +  Finally, remove all control functions from your v4l2_ioctl_ops:
> > +  vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl,
> > +  vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls.
> > +  Those are now no longer needed.
> > +
> > +  How to hook the control handler into a subdev driver:
> > +
> > +	foo->sd.ctrl_handler = &foo->hdl;
> > +
> > +  And set all core control ops in your struct v4l2_subdev_core_ops to
> > these
> > +  helpers:
> > +
> > +	.queryctrl = v4l2_sd_queryctrl,
> > +	.querymenu = v4l2_sd_querymenu,
> > +	.g_ctrl = v4l2_sd_g_ctrl,
> > +	.s_ctrl = v4l2_sd_s_ctrl,
> > +	.g_ext_ctrls = v4l2_sd_g_ext_ctrls,
> > +	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
> > +	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
> 
> s/sd/subdev/ ?

I have to sleep on that... :-)

> 
> > +
> > +  This is for backwards compatibility. Once all bridge drivers are
> > converted
> > +  these control ops can be removed just as they are already for bridge
> > drivers.
> > +
> > +- Clean up the handler at the end:
> > +
> > +	v4l2_ctrl_handler_free(&foo->hdl);
> > +
> > +
> > +2) Add controls:
> > +
> > +Typically done right after the handler_init:
> > +
> > +	v4l2_ctrl_handler_init(&foo->hdl, nr_of_controls);
> > +	v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops,
> > +			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
> > +	v4l2_ctrl_new_std(&foo->hdl, &foo_ctrl_ops,
> > +			V4L2_CID_CONTRAST, 0, 255, 1, 128);
> 
> Could you add the v4l2_ctrl_new_std function prototype to the documentation ? 
> I expect the parameters to be min, max, step and default, but it would be 
> better to make it explicit.

OK.
 
> Should't v4l2_ctrl_new_std take a control type as well ?

The type is set automatically as that is determined by the control ID.

> What about hardware for which the boundaries are only known at runtime, or 
> could depend on the values of other controls ? I'm thinking about UVC devices 
> for instance, the boundaries, step and default values need to be retrieved 
> from the hardware. I currently do that at runtime when the control is queried 
> for the first time and cache the values, as doing it during initialization 
> (probe function) crashes a few webcams. That doesn't seem to be possible with 
> the control framework.

It is possible to add controls to an existing control handler at runtime.
It is also possible to change boundaries at runtime: you just change the
relevant values in v4l2_ctrl. There is no function for that, it's enough
to call v4l2_ctrl_lock(), change the values and call unlock().

I could make a function that does this, but UVC is the only driver that I
know of that might need this.

> 
> > +	...
> > +	if (foo->hdl.error) {
> > +		int err = foo->hdl.error;
> > +
> > +		v4l2_ctrl_handler_free(&foo->hdl);
> > +		return err;
> > +	}
> > +
> > +The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new
> > +control, but if you do not need to access the pointer outside the control
> > ops,
> > +then there is no need to store it.
> > +
> > +Note that if something fails, the function will return NULL or an error
> > and
> > +set hdl->error to the error code. If hdl->error was already set, then it
> > +will just return and do nothing. This is also true for
> > v4l2_ctrl_handler_init
> > +if it cannot allocate the internal data structure.
> > +
> > +This makes it easy to init the handler and just add all controls and only
> > check
> > +the error code at the end. Saves a lot of repetitive error checking.
> 
> I would still check the v4l2_ctrl_handler_init return value explicitly, but 
> that may be just me.

Feel free to do that :-)
 
> > +It is recommended to add controls in ascending control ID order: it will be
> > +a bit faster that way.
> 
> As long as it's not required that's OK. It would be difficult to do so on UVC 
> hardware, as controls are discovered based on information reported by the 
> device, and those are obviously not ordered based on the V4L2 CIDs :-)

It is a recommendation only, it will work in any order.

> 
> > +3) Optionally force initial control setup:
> > +
> > +	v4l2_ctrl_handler_setup(&foo->hdl);
> > +
> > +This will call s_ctrl for all controls unconditionally. Effectively this
> > +initializes the hardware to the default control values. It is recommended
> > +that you do this.
> 
> What about the other way around, if I want to initialize the framework with 
> the current values retrieved from the hardware ?

In that case you would set the default value of the control based on the value
reported by the hardware.
 
> > +4) Finally: implement the v4l2_ctrl_ops
> > +
> > +	static const struct v4l2_ctrl_ops foo_ctrl_ops = {
> > +		.s_ctrl = foo_s_ctrl,
> > +	};
> > +
> > +Usually all you need is s_ctrl:
> > +
> > +	static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
> > +	{
> > +		struct foo *state = container_of(ctrl->handler, struct foo, hdl);
> > +
> > +		switch (ctrl->id) {
> > +		case V4L2_CID_BRIGHTNESS:
> > +			write_reg(0x123, ctrl->val);
> > +			break;
> > +		case V4L2_CID_CONTRAST:
> > +			write_reg(0x456, ctrl->val);
> > +			break;
> > +		}
> > +		return 0;
> > +	}
> > +
> > +The control ops are called with the v4l2_ctrl pointer as argument.
> > +The new control value has already been validated, so all you need to do is
> > +to actually update the hardware registers.
> > +
> > +You're done! And this is sufficient for most of the drivers we have. No
> > need
> > +to do any validation of control values, or implement QUERYCTRL/QUERYMENU.
> > And
> > +G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported.
> > +
> > +
> > +==========================================================================
> > =====
> > +
> > +The remainder of this document deals with more advanced topics and
> > scenarios.
> > +In practice the basic usage as described above is sufficient for most
> > drivers.
> > +
> > +==========================================================================
> > =====
> > +
> > +
> > +Accessing Control Values
> > +========================
> > +
> > +The v4l2_ctrl struct contains these two unions:
> > +
> > +	/* The current control value. */
> > +	union {
> > +		s32 val;
> > +		s64 val64;
> > +		char *string;
> > +	} cur;
> > +
> > +	/* The new control value. */
> > +	union {
> > +		s32 val;
> > +		s64 val64;
> > +		char *string;
> > +	};
> > +
> > +Within the control ops you can freely use these. The val and val64 speak
> > for
> > +themselves. The string pointers point to character buffers of length
> > +ctrl->maximum + 1, and are always 0-terminated.
> > +
> > +For g_ctrl you have to update the current control values like this:
> > +
> > +	ctrl->cur.val = read_reg(0x123);
> > +
> > +The 'new value' union is not relevant in g_ctrl.
> 
> When is g_ctrl called ? When the userspace applications issues a VIDIOC_G_CTRL 
> or VIDIOC_G_EXT_CTRLS ioctl ?

Yes.

> I suppose the operation only needs to be 
> implemented if the device needs to be queried because it modified the control 
> value on its own (possibly as a result of setting another control). What if 
> some controls need to be handled that way, while the others are not self-
> modifying ?

g_ctrl only has to take care of self-modifying controls and can ignore the
others. saa7115 has a use case like that.

> > +For try/s_ctrl the new values (i.e. as passed by the user) are filled in
> > and
> > +you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
> > +contains the current value, which you can use (but not change!) as well.
> > +
> > +If s_ctrl returns 0 (OK), then the control framework will copy the new
> > final
> > +values to the 'cur' union.
> > +
> > +While in g/s/try_ctrl you can access the value of all controls owned by
> > the
> > +same handler since the handler's lock is held. Do not attempt to access
> > +the value of controls owned by other handlers, though.
> > +
> > +Elsewhere in the driver you have to be more careful. You cannot just refer
> > +to the current control values without locking.
> > +
> > +There are two simple helper functions defined that will get or set a
> > single
> > +control value safely:
> > +
> > +	s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl);
> > +	int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);
> 
> I suppose that v4l2_ctrl_s can be used if the device notifies the driver that 
> a control has changed. Is that correct ?
> 
> I don't really like the names of those two functions. For instance, 
> v4l2_ctrl_s looks like it will call the driver to set a control.

I think there is a misunderstanding here: v4l2_ctrl_s() will indeed change
the control's value.

> 
> > +
> > +Don't use these inside the control ops g/s/try_ctrl, though, that will
> > fail.
> > +
> > +You can also take the handler lock yourself:
> > +
> > +	mutex_lock(&state->hdl.lock);
> > +	printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string);
> > +	printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val);
> > +	mutex_unlock(&state->hdl.lock);
> > +
> > +
> > +Menu Controls
> > +=============
> > +
> > +Menu controls use the 'step' value differently compared to other control
> > +types. The v4l2_ctrl struct contains this union:
> > +
> > +	union {
> > +		u32 step;
> > +		u32 menu_skip_mask;
> > +	};
> > +
> > +For menu controls menu_skip_mask is used. What it does is that it allows
> > you
> > +to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU
> > +implementation where you can return -EINVAL if a certain menu item is not
> > +present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for
> > +menu controls.
> > +
> > +A good example is the MPEG Audio Layer II Bitrate menu control where the
> > +menu is a list of standardized possible bitrates. But in practice hardware
> > +implementations will only support a subset of those. By setting the skip
> > +mask you can tell the framework which menu items should be skipped.
> > Setting
> > +it to 0 means that all menu items are supported.
> > +
> > +So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need to
> > remember
> > +that 'step' means 'skip mask' for menu controls. If you put in '1' by
> > mistake,
> > +then the first menu item will be skipped.
> 
> What about adding a v4l2_ctrl_new_menu then ? That will avoid such a mistake.
> 
> > +The v4l2_ctrl_new_std_menu can be used to add menu controls more easily:
> > it
> > +will calculate the min and max values automatically based on the size of
> > the
> > +menu, and it has a proper 'mask' argument instead of 'step'.
> 
> OK :-) Shouldn't v4l2_ctrl_new_custom be restricted to non-menu controls then 
> ? Looking at the code, it should probably become an internal function, with 
> another function added for custom controls without menu support.

I might add that in the future. I want to avoid creating a long series of
different functions, all alike :-)

> 
> > +
> > +
> > +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.
> 
> What you refer to as active/inactive is usually referred to as 
> enabled/disabled in the GUI world. Might be worth using the same convention.

Strictly speaking it is not quite the same. An inactive control can still be
written, the new value will simply not be used until the control becomes
active again. In a GUI that would be confusing, so the control is disabled
instead. It's a subtle difference that may or may not be important.
 
> > +
> > +The other flag is the grabbed flag. A grabbed control means that you
> > cannot
> > +change it because it is in use by some resource. Typical examples are MPEG
> > +bitrate controls that cannot be changed while capturing is in progress.
> > +
> > +If a control is set to 'grabbed' using v4l2_ctrl_grab(), then the
> > framework
> > +will return -EBUSY if an attempt is made to set this control.
> > +
> > +Since this flag is used by the framework the v4l2_ctrl_grab function will
> > +take the control handler's lock. So it cannot be called from within the
> > +control ops. Instead this is typically called from the driver when it
> > +starts streaming.
> > +
> > +
> > +Control Clusters
> > +================
> > +
> > +By default all controls are independent from the others. But in more
> > +complex scenarios you can get dependencies from one control to another.
> > +In that case you need to 'cluster' them:
> > +
> > +	struct foo {
> > +		struct v4l2_ctrl_handler hdl;
> > +		struct v4l2_ctrl *volume;
> > +		struct v4l2_ctrl *mute;
> > +		...
> > +	};
> > +
> > +	state->volume = v4l2_ctrl_new_std(&state->hdl, ...);
> > +	state->mute = v4l2_ctrl_new_std(&state->hdl, ...);
> > +	v4l2_ctrl_cluster(2, &state->volume);
> 
> What's the first argument to v4l2_ctrl_cluster ? The number of controls in the 
> cluster ?

Yes.

> Does that imply that they need to be added in a sequence, right 
> before v4l2_ctrl_cluster is called ? That seems a bit awkward. Wouldn't it be 
> better to specify the relationships between controls explicitly, maybe by 
> passing a pointer to the master control when creating the 'slave' controls in 
> the cluster ?

OK, I clearly need to explain this better.

What you pass is an array of v4l2_ctrl pointers, e.g.:

	struct foo {
		struct v4l2_ctrl *cluster[2];
		...
	};

	foo->cluster[CTRL_VOLUME] = v4l2_ctrl_new_std();
	foo->cluster[CTRL_MUTE] = v4l2_ctrl_new_std();
	v4l2_ctrl_cluster(2, foo->cluster);

cluster[0] is always the master.

Now, using arrays here becomes quickly very annoying, so instead you can just
say:
	struct foo {
		/* volume/mute cluster */
		struct v4l2_ctrl *volume;
		struct v4l2_ctrl *mute;
		...
	};

	foo->volume = v4l2_ctrl_new_std();
	foo->mute = v4l2_ctrl_new_std();
	v4l2_ctrl_cluster(2, &foo->volume);

Same thing, but without the cumbersome array indices.

> 
> > +From now on whenever one or more of the controls belonging to the same
> > +cluster is set (or 'gotten', or 'tried'), only the control ops of the
> > first
> > +control ('volume' in this example) is called. You effectively create a new
> > +composite control. Similar to how a 'struct' works in C.
> > +
> > +So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you should
> > set
> > +all two controls belonging to the 'volume' cluster:
> > +
> > +	static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
> > +	{
> > +		struct foo *state = container_of(ctrl->handler, struct foo, hdl);
> > +
> > +		switch (ctrl->id) {
> > +		case V4L2_CID_AUDIO_VOLUME:
> > +			/* volume cluster */
> > +			write_reg(0x123, state->mute->val ? 0 : ctrl->val);
> > +			break;
> > +		case V4L2_CID_CONTRAST:
> > +			write_reg(0x456, ctrl->val);
> > +			break;
> > +		}
> > +		return 0;
> > +	}
> > +
> > +In the example above the following are equivalent for the VOLUME case:
> > +
> > +	ctrl == state->volume == ctrl->cluster[0]
> > +	state->mute == ctrl->cluster[1]
> > +
> > +Note that controls in a cluster may be NULL. For example, if for some
> > +reason mute was never added (because the hardware doesn't support that
> > +particular feature), then mute will be NULL. So in that case we have a
> > +cluster of 2 controls, of which only 1 is actually instantiated. The
> > +only restriction is that the first control of the cluster must already be
> > +present, since that is the 'master' control of the cluster. The master
> > +control is the one that identifies the cluster and that provides the
> > +pointer to the v4l2_ctrl_ops struct that is used for that cluster.
> > +
> > +
> > +VIDIOC_LOG_STATUS Support
> > +=========================
> > +
> > +This ioctl allow you to dump the current status of a driver to the kernel
> > log.
> 
> This has nothing to do with the controls framework, but shouldn't that ioctl 
> be restricted to root only ? It can potentially dump a lot of information to 
> the kernel log, and thus system logs.

Personally I see no reason to change this.
 
> > +The v4l2_ctrl_handler_log_status(hdl, prefix) can be used to dump the value
> > of
> > +the controls owned by the given handler to the log. You can supply a prefix
> > +as well. If the prefix didn't end with a space, then ': ' will be added for
> > +you.
> > +
> > +
> > +Different Handlers for Different Video Nodes
> > +============================================
> > +
> > +Usually the bridge driver has just one control handler that is global for
> > +all video nodes. But you can also specify different control handlers for
> > +different video nodes. It's no problem if there are no subdevs involved.
> > +But if there are, then you need to block the automatic merging of subdev
> > +controls to the global control handler. You do that by simply setting the
> > +ctrl_handler field in struct v4l2_device to NULL.
> > +
> > +After each subdev was added, you will then have to call
> > v4l2_ctrl_add_handler
> > +manually to add the subdev's control handler (sd->ctrl_handler) to the
> > desired
> > +bridge control handler.
> 
> Do you mean video device instead of bridge here ?

struct v4l2_device.

> 
> I wouldn't mention "bridge" in here. I assume that by bridge you mean 
> v4l2_device. Please use that name directly. The term "bridge" is only 
> applicable to a subset of the v4l2_device use cases.

Good point.

> Can controls for a specific subdev be reported through more than one video 
> device node, but not all of them ?

Yes. It will be a bit fiddly to set up, but that's to be expected if you want
to do things like that.

> 
> > +
> > +If you want to have one handler (e.g. for a radio device node) have a
> > subset
> > +of another handler (e.g. for a video device node), then you can first add
> > +the controls to the first handler, add the other controls to the second
> > +handler and finally add the first handler to the second. For example:
> > +
> > +	v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
> > +	v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
> > +	v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...);
> > +	v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_CONTRAST, ...);
> > +	v4l2_ctrl_add_handler(&video_hdl, &radio_hdl);
> > +
> > +Or you can add specific controls to a handler:
> > +
> > +	volume = v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_AUDIO_VOLUME, ...);
> > +	v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_BRIGHTNESS, ...);
> > +	v4l2_ctrl_new_std(&video_hdl, &ops, V4L2_CID_CONTRAST, ...);
> > +	v4l2_ctrl_add_ctrl(&radio_hdl, volume);
> > +
> > +What you should not do is make two identical controls for two handlers.
> > +For example:
> > +
> > +	v4l2_ctrl_new_std(&radio_hdl, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
> > +	v4l2_ctrl_new_std(&video_hdl, &video_ops, V4L2_CID_AUDIO_MUTE, ...);
> > +
> > +This would be bad since muting the radio would not change the video mute
> > +control. The rule is to have one control for each hardware 'knob' that you
> > +can twiddle.
> > +
> > +
> > +Finding Controls
> > +================
> > +
> > +Normally you have created the controls yourself and you can store the
> > struct
> > +v4l2_ctrl pointer into your own struct.
> > +
> > +But sometimes you need to find a control from another handler that you do
> > +not own. For example, if you have to find a volume control from a subdev.
> > +
> > +You can do that by calling v4l2_ctrl_find:
> > +
> > +	struct v4l2_ctrl *volume;
> > +
> > +	volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);
> > +
> > +Since v4l2_ctrl_find will lock the handler you have to be careful where
> > you
> > +use it. For example, this is not a good idea:
> > +
> > +	struct v4l2_ctrl_handler hdl;
> > +
> > +	v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_BRIGHTNESS, ...);
> > +	v4l2_ctrl_new_std(&hdl, &video_ops, V4L2_CID_CONTRAST, ...);
> > +
> > +...and in video_ops.s_ctrl:
> > +
> > +	case V4L2_CID_BRIGHTNESS:
> > +		contrast = v4l2_find_ctrl(&hdl, V4L2_CID_CONTRAST);
> > +		...
> > +
> > +When s_ctrl is called by the framework the hdl.lock is already taken, so
> > +attempting to find another control from the same handler will deadlock.
> > +
> > +It is recommended not to use this function from inside the control ops.
> > +
> > +
> > +Inheriting Controls
> > +===================
> > +
> > +When one control handler is added to another using v4l2_ctrl_add_handler,
> > then
> > +by default all controls from one are merged to the other. But a subdev
> > might
> > +have low-level controls that make sense for some advanced embedded system,
> > but
> > +not when it is used in consumer-level hardware. In that case you want to
> > keep
> > +those low-level controls local to the subdev. You can do this by simply
> > +setting the 'is_private' flag of the control to 1:
> > +
> > +	ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...);
> > +	if (ctrl)
> > +		ctrl->is_private = 1;
> 
> 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.
 
> I'm actually wondering if it wouldn't be better to pass a pointer to a 
> structure with the required information (maybe the v4l2_control_info structure 
> I mentioned earlier ?) to v4l2_ctrl_new_custom.

I thought about that, but 1) that means yet another structure and 2) I dislike
code like that because the definition is usually quite far from the place were
it is actually used.
 
> > +These controls will now be skipped when v4l2_ctrl_add_handler is called.
> > +
> > +
> > +Strict Control Validation
> > +=========================
> > +
> > +By default when the application wants to change an integer control the
> > value
> > +passed to the framework will automatically be modified to map to the
> > provided
> > +minimum, maximum and step values of the control. If instead you just want
> > to
> > +validate the value and not modify it, then set the 'strict_validation' flag
> > of
> > +the control:
> > +
> > +	ctrl->strict_validation = 1;
> > +
> > +Now -ERANGE will be returned if the new value does not match the control's
> > +requirements.
> 
> Why do we need the two behaviours ? Wouldn't it be better to standardize on 
> one of them ?

I may have fallen in the pitfall of featurism here. Just because it is easy to
implement, that doesn't mean that you should actually do it.

I think I'll remove strict_validation.
 
> > +This is currently specific to integer controls. The value for boolean
> > controls
> > +is always mapped to 0 or 1, menu and string controls are already validated
> > +strictly, and integer64 controls are not validated at all.
> > +
> > +
> > +V4L2_CTRL_TYPE_CTRL_CLASS Controls
> > +==================================
> > +
> > +Controls of this type can be used by GUIs to get the name of the control
> > class.
> > +A fully featured GUI can make a dialog with multiple tabs with each tab
> > +containing the controls belonging to a particular control class. The name
> > of
> > +each tab can be found by querying a special control with ID <control class
> > | 1>.
> > +
> > +Drivers do not have to care about this. The framework will automatically
> > add
> > +a control of this type whenever the first control belonging to a new
> > control
> > +class is added.
> > +
> > +
> > +Differences from the Spec
> > +=========================
> > +
> > +There are a few places where the framework acts slightly differently from
> > the
> > +V4L2 Specification. Those differences are described in this section. We
> > will
> > +have to see whether we need to adjust the spec or not.
> > +
> > +1) It is no longer required to have all controls contained in a
> > +v4l2_ext_control array be from the same control class. The framework will
> > be
> > +able to handle any type of control in the array. You need to set ctrl_class
> > +to 0 in order to enable this. If ctrl_class is non-zero, then it will still
> > +check that all controls belong to that control class.
> > +
> > +If you set ctrl_class to 0 and count to 0, then it will only return an
> > error
> > +if there are no controls at all.
> 
> I don't know why we had such a limitation in the first place. I would indeed 
> remove it completely.
> 
> > +
> > +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.
> 
> Agreed. We should change the spec.
> 
> > +4) Attempting to write to a read-only control will return -EACCES instead
> > of
> > +-EINVAL as the spec says.
> 
> This makes sense as well.
> 
> > +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.
> 
> I still feel a bit awkward about the interface. One particular point that 
> might require attention is the split of the v4l2_ctrl structure into 
> v4l2_control_info and v4l2_control_value structures (the names are not set 
> into stone). I would also like to see if we can't pass pointers to 
> v4l2_control_info (or similar) to the control creation functions instead of a 
> plethora of arguments.

One of my original attempts was indeed to have two structs. But that made the
code only more complex. Having two structs only makes sense if the 'static' part
can be, well, static. But then you have UVC where that isn't static. So the code
then has to differentiate between these two cases and it quickly becomes a big
mess.

Regarding the plethora of arguments: remember that 99% of all controls will just
use v4l2_ctrl_new_std(). And that ain't too bad. new_custom is indeed a big one,
but currently only used by cx2341x where it is wrapped inside a much easier to
digest function. I've tried to make the 90% of easy cases easy, and for the
remaining 10% you will indeed have to do more work.
 
> I'll now have a look at the code. As there are already quite a few comments on 
> the documentation I won't perform a very in-depth code review though, I'll 
> save that for later after we agree on the spec :-)

Thanks for the review!

This was very useful.

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 08/15] [RFC] cx25840/ivtv: replace ugly priv control with s_config
  2010-05-02 20:41   ` Laurent Pinchart
@ 2010-05-02 22:25     ` Hans Verkuil
  0 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-05-02 22:25 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media

On Sunday 02 May 2010 22:41:29 Laurent Pinchart wrote:
> Hi Hans,
> 
> On Monday 26 April 2010 09:33:54 Hans Verkuil wrote:
> > 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 f2461cd..b8aa5d2 100644
> > --- a/drivers/media/video/cx25840/cx25840-core.c
> > +++ b/drivers/media/video/cx25840/cx25840-core.c
> 
> [snip]
> 
> > @@ -1601,10 +1593,25 @@ static int cx25840_log_status(struct v4l2_subdev
> > *sd) return 0;
> >  }
> > 
> > +static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void
> > *platform_data) +{
> > +	struct cx25840_state *state = to_state(sd);
> > +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> > +
> > +	if (platform_data) {
> > +		struct cx25840_platform_data *pdata = platform_data;
> > +
> > +		state->pvr150_workaround = pdata->pvr150_workaround;
> > +		set_input(client, state->vid_input, state->aud_input);
> > +	}
> > +	return 0;
> > +}
> > +
> 
> You've told me that s_config was only meant for I2C devices in pre-2.6.26 
> kernels. Shouldn't this be done in the probe function instead ?

s_config is only meant for i2c drivers that have to support pre-2.6.26
kernels (only applicable for the hg tree, of course). And that is definitely
the case for cx25840.

As a side note: we could decide to ditch that backwards compatibility support
in the git tree of course, but I don't know how problematic that may turn out
to be for the hg tree maintenance. Anyway, I have too much to do already :-)

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header.
  2010-05-02 20:42   ` Laurent Pinchart
@ 2010-05-03  6:42     ` Hans Verkuil
  2010-05-03  7:23       ` Laurent Pinchart
  0 siblings, 1 reply; 29+ messages in thread
From: Hans Verkuil @ 2010-05-03  6:42 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media

On Sunday 02 May 2010 22:42:26 Laurent Pinchart wrote:
> Hi Hans,
> 
> On Monday 26 April 2010 09:33:33 Hans Verkuil wrote:
> > To make it easier to determine whether all controls are added in
> > v4l2-ctrls.c the case statements inside the switch are re-ordered to match
> > the header.
> > 
> > Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
> 
> This patch should be merged with the previous one.

I didn't do that in order to make the previous patch easier to review.
That patch just moves this table unchanged from v4l2-common.c to v4l2-ctrl.c.
Since the table doesn't change it is easy to verify that, well, no changes
were made.

I will keep this as a separate patch if you don't mind.

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header.
  2010-05-03  6:42     ` Hans Verkuil
@ 2010-05-03  7:23       ` Laurent Pinchart
  0 siblings, 0 replies; 29+ messages in thread
From: Laurent Pinchart @ 2010-05-03  7:23 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

On Monday 03 May 2010 08:42:59 Hans Verkuil wrote:
> On Sunday 02 May 2010 22:42:26 Laurent Pinchart wrote:
> > Hi Hans,
> > 
> > On Monday 26 April 2010 09:33:33 Hans Verkuil wrote:
> > > To make it easier to determine whether all controls are added in
> > > v4l2-ctrls.c the case statements inside the switch are re-ordered to
> > > match the header.
> > > 
> > > Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
> > 
> > This patch should be merged with the previous one.
> 
> I didn't do that in order to make the previous patch easier to review.
> That patch just moves this table unchanged from v4l2-common.c to
> v4l2-ctrl.c. Since the table doesn't change it is easy to verify that,
> well, no changes were made.
> 
> I will keep this as a separate patch if you don't mind.

N/P.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
  2010-05-02 22:21     ` Hans Verkuil
@ 2010-05-06 21:36       ` Laurent Pinchart
  2010-05-07  9:23         ` Hans Verkuil
  2010-05-12 11:00       ` Sakari Ailus
  1 sibling, 1 reply; 29+ messages in thread
From: Laurent Pinchart @ 2010-05-06 21:36 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

On Monday 03 May 2010 00:21:46 Hans Verkuil wrote:
> On Sunday 02 May 2010 22:39:11 Laurent Pinchart wrote:
> > On Monday 26 April 2010 09:33:38 Hans Verkuil wrote:

[snip]

> > > +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).
> > 
> > I'm not sure v4l2_ctrl is a good name. We already have a v4l2_control
> > structure, and using the abbreviated name for the in-kernel version is
> > going to be confusing.
> 
> Originally it was called v4l2_ctrl_info but that became very cumbersome.
> It's not really an info object anyway, it really describes a control. When
> using this framework the driver no longer sees the v4l2_control anymore, so
> from the point of view of the driver there is only v4l2_ctrl.

Sure, but it's still misleading. Using an both the abbreviated and non-
abbreviated names to refer to two different things has been done too often in 
V4L2, and it's starting to bother me. It makes the API look inconsistent. We 
need to define a naming policy and stick to it for the future. That can be a 
topic for the V4L2 summit in June.

> > It's obviously too late to change v4l2_control to something else.
> > v4l2_control_info and v4l2_control_value are two names that come to my
> > mind. Actually, it might be a good idea to split the v4l2_ctrl structure
> > into a static driver-wide structure (v4l2_control_info) and an
> > instance-specific structure (v4l2_control_value). There's no point in
> > storing the same static data (function pointers, names, ...) for
> > identical controls in different devices.
> 
> Other than the name there isn't anything that is static. And the name is
> already static.

The following fields are static (they're identical across all v4l2_ctrl 
instances for the same control):

- ops
- id
- name
- type
- menu (not too sure about this one)

[snip]

> > > +
> > > +  How to hook the handler into a bridge driver:
> > > +
> > > +	foo->v4l2_dev.ctrl_handler = &foo->hdl;

[snip]

> > > +
> > > +  And whenever you call video_register_device() you must set the
> > > +  ctrl_handler field of struct video_device as well:
> > > +
> > > +	vdev->ctrl_handler = &foo->hdl;

[snip]

> > Why can't the framework find the ctrl_handler instance from the
> > video_device's v4l2_device parent ?
> 
> Sometimes you want to provide different control handlers to different
> video devices. E.g. /dev/radio0 may have only a small subset of the
> controls of /dev/video0 (bttv does that, for example).
> 
> One option is to automatically let video_register_device copy ctrl_handler
> from the v4l2_dev struct, but I think it is clearer if it is explicitly
> set.

I was thinking about copying it implicitly if it's not set, yes. That way only 
the v4l2_device instance would need an explicit handler, video devices would 
inherit it from their v4l2_device parent. The driver would still have the 
ability to set it if it wants to override the default behaviour.

It might be clearer to always set it explicitly, I have no strong opinion on 
this one.

> > > +  Finally, remove all control functions from your v4l2_ioctl_ops:
> > > +  vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl,
> > > +  vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls.
> > > +  Those are now no longer needed.
> > > +
> > > +  How to hook the control handler into a subdev driver:
> > > +
> > > +	foo->sd.ctrl_handler = &foo->hdl;
> > > +
> > > +  And set all core control ops in your struct v4l2_subdev_core_ops to
> > > these
> > > +  helpers:
> > > +
> > > +	.queryctrl = v4l2_sd_queryctrl,
> > > +	.querymenu = v4l2_sd_querymenu,
> > > +	.g_ctrl = v4l2_sd_g_ctrl,
> > > +	.s_ctrl = v4l2_sd_s_ctrl,
> > > +	.g_ext_ctrls = v4l2_sd_g_ext_ctrls,
> > > +	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
> > > +	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
> > 
> > s/sd/subdev/ ?
> 
> I have to sleep on that... :-)

You've slept on it for a week now :-)

We really have too many abbreviations in V4L2, or at least no consistent usage 
of them.

[snip]

> > What about hardware for which the boundaries are only known at runtime,
> > or could depend on the values of other controls ? I'm thinking about UVC
> > devices for instance, the boundaries, step and default values need to be
> > retrieved from the hardware. I currently do that at runtime when the
> > control is queried for the first time and cache the values, as doing it
> > during initialization (probe function) crashes a few webcams. That
> > doesn't seem to be possible with the control framework.
> 
> It is possible to add controls to an existing control handler at runtime.
> It is also possible to change boundaries at runtime: you just change the
> relevant values in v4l2_ctrl. There is no function for that, it's enough
> to call v4l2_ctrl_lock(), change the values and call unlock().
> 
> I could make a function that does this, but UVC is the only driver that I
> know of that might need this.

My issue is that the UVC driver would need to set the boundaries the first 
time the control is queried. There's no callback for that at the moment.

> > > +	...
> > > +	if (foo->hdl.error) {
> > > +		int err = foo->hdl.error;
> > > +
> > > +		v4l2_ctrl_handler_free(&foo->hdl);
> > > +		return err;
> > > +	}
> > > +
> > > +The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new
> > > +control, but if you do not need to access the pointer outside the
> > > control ops,
> > > +then there is no need to store it.
> > > +
> > > +Note that if something fails, the function will return NULL or an error
> > > and
> > > +set hdl->error to the error code. If hdl->error was already set, then
> > > it
> > > +will just return and do nothing. This is also true for
> > > v4l2_ctrl_handler_init
> > > +if it cannot allocate the internal data structure.
> > > +
> > > +This makes it easy to init the handler and just add all controls and
> > > only check
> > > +the error code at the end. Saves a lot of repetitive error checking.
> > 
> > I would still check the v4l2_ctrl_handler_init return value explicitly,
> > but that may be just me.
> 
> Feel free to do that :-)

You know what I meant :-) I agree this is a good way to handle errors for 
v4l2_ctrl_new_*, but v4l2_ctrl_handler_init feels... different.

[snip]

> > > +3) Optionally force initial control setup:
> > > +
> > > +	v4l2_ctrl_handler_setup(&foo->hdl);
> > > +
> > > +This will call s_ctrl for all controls unconditionally. Effectively
> > > this
> > > +initializes the hardware to the default control values. It is
> > > recommended +that you do this.
> > 
> > What about the other way around, if I want to initialize the framework
> > with the current values retrieved from the hardware ?
> 
> In that case you would set the default value of the control based on the
> value reported by the hardware.

The default value can still be different than the current value. Don't you 
mean I should initialize the current value, not the default one ?

What should be recommended, calling v4l2_ctrl_handler_setup, or initializing 
the framework with the current value ?

Thinking some more about this, it can be a problem for UVC devices, much like 
querying the min/max/step/default values is. Some devices just crash if you 
send too many requests too fast. I would like to get a g_ctrl call the first 
time the current value needs to be read, and then have the framework cache 
that value.

By the way (still more thinking :-)), does the framework handle a value cache 
that can be configured per control ? Some controls are volatile and need to be 
read from the driver every time, some others can be cached. How is that 
handled ?

Last thought: does the framework query the driver for the current control 
values when setting a control in a cluster ? If two clustered controls are 
stored in a single hardware "register", and one of them needs to be modified, 
a read-update-write operation needs to be performed. The update part will be 
done by the framework, the write part by the driver, but what about the read 
part ?

[snip]

> > > +Accessing Control Values
> > > +========================
> > > +
> > > +The v4l2_ctrl struct contains these two unions:
> > > +
> > > +	/* The current control value. */
> > > +	union {
> > > +		s32 val;
> > > +		s64 val64;
> > > +		char *string;
> > > +	} cur;
> > > +
> > > +	/* The new control value. */
> > > +	union {
> > > +		s32 val;
> > > +		s64 val64;
> > > +		char *string;
> > > +	};
> > > +
> > > +Within the control ops you can freely use these. The val and val64
> > > speak for
> > > +themselves. The string pointers point to character buffers of length
> > > +ctrl->maximum + 1, and are always 0-terminated.
> > > +
> > > +For g_ctrl you have to update the current control values like this:
> > > +
> > > +	ctrl->cur.val = read_reg(0x123);
> > > +
> > > +The 'new value' union is not relevant in g_ctrl.
> > 
> > When is g_ctrl called ? When the userspace applications issues a
> > VIDIOC_G_CTRL or VIDIOC_G_EXT_CTRLS ioctl ?
> 
> Yes.

OK. Can you make that explicit ?

> > I suppose the operation only needs to be implemented if the device needs
> > to be queried because it modified the control value on its own (possibly
> > as a result of setting another control). What if some controls need to be
> > handled that way, while the others are not self-modifying ?
> 
> g_ctrl only has to take care of self-modifying controls and can ignore the
> others. saa7115 has a use case like that.

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).

> > > +For try/s_ctrl the new values (i.e. as passed by the user) are filled
> > > in and
> > > +you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
> > > +contains the current value, which you can use (but not change!) as
> > > well. +
> > > +If s_ctrl returns 0 (OK), then the control framework will copy the new
> > > final
> > > +values to the 'cur' union.
> > > +
> > > +While in g/s/try_ctrl you can access the value of all controls owned
> > > by the
> > > +same handler since the handler's lock is held. Do not attempt to
> > > access +the value of controls owned by other handlers, though.
> > > +
> > > +Elsewhere in the driver you have to be more careful. You cannot just
> > > refer +to the current control values without locking.
> > > +
> > > +There are two simple helper functions defined that will get or set a
> > > single
> > > +control value safely:
> > > +
> > > +	s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl);
> > > +	int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);
> > 
> > I suppose that v4l2_ctrl_s can be used if the device notifies the driver
> > that a control has changed. Is that correct ?
> > 
> > I don't really like the names of those two functions. For instance,
> > v4l2_ctrl_s looks like it will call the driver to set a control.
> 
> I think there is a misunderstanding here: v4l2_ctrl_s() will indeed change
> the control's value.

Do you mean that it will call the driver's s_ctrl operation or that it will 
modify the current value in the v4l2_ctrl structure ? The misunderstanding 
probably calls for a function name change :-)

[snip]

> > > +Menu Controls
> > > +=============
> > > +
> > > +Menu controls use the 'step' value differently compared to other
> > > control +types. The v4l2_ctrl struct contains this union:
> > > +
> > > +	union {
> > > +		u32 step;
> > > +		u32 menu_skip_mask;
> > > +	};
> > > +
> > > +For menu controls menu_skip_mask is used. What it does is that it
> > > allows you
> > > +to easily exclude certain menu items. This is used in the
> > > VIDIOC_QUERYMENU +implementation where you can return -EINVAL if a
> > > certain menu item is not +present. Note that VIDIOC_QUERYCTRL always
> > > returns a step value of 1 for +menu controls.
> > > +
> > > +A good example is the MPEG Audio Layer II Bitrate menu control where
> > > the +menu is a list of standardized possible bitrates. But in practice
> > > hardware +implementations will only support a subset of those. By
> > > setting the skip +mask you can tell the framework which menu items
> > > should be skipped. Setting
> > > +it to 0 means that all menu items are supported.
> > > +
> > > +So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need to
> > > remember
> > > +that 'step' means 'skip mask' for menu controls. If you put in '1' by
> > > mistake,
> > > +then the first menu item will be skipped.
> > 
> > What about adding a v4l2_ctrl_new_menu then ? That will avoid such a
> > mistake.
> > 
> > > +The v4l2_ctrl_new_std_menu can be used to add menu controls more
> > > easily: it
> > > +will calculate the min and max values automatically based on the size
> > > of the
> > > +menu, and it has a proper 'mask' argument instead of 'step'.
> > 
> > OK :-) Shouldn't v4l2_ctrl_new_custom be restricted to non-menu controls
> > then ? Looking at the code, it should probably become an internal
> > function, with another function added for custom controls without menu
> > support.
> 
> I might add that in the future. I want to avoid creating a long series of
> different functions, all alike :-)

Then drivers will use the function that will later become internal. You'll 
have to change a bunch of drivers later, and you will tell me that it would be 
too much work and wouldn't be worth it ;-)

> > > +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.
> > 
> > What you refer to as active/inactive is usually referred to as
> > enabled/disabled in the GUI world. Might be worth using the same
> > convention.
> 
> Strictly speaking it is not quite the same. An inactive control can still
> be written, the new value will simply not be used until the control
> becomes active again. In a GUI that would be confusing, so the control is
> disabled instead. It's a subtle difference that may or may not be
> important.

What does the framework do when the userspace application attempts to write to 
a disabled control ?

[snip]

> > > +Control Clusters
> > > +================
> > > +
> > > +By default all controls are independent from the others. But in more
> > > +complex scenarios you can get dependencies from one control to
> > > another. +In that case you need to 'cluster' them:
> > > +
> > > +	struct foo {
> > > +		struct v4l2_ctrl_handler hdl;
> > > +		struct v4l2_ctrl *volume;
> > > +		struct v4l2_ctrl *mute;
> > > +		...
> > > +	};
> > > +
> > > +	state->volume = v4l2_ctrl_new_std(&state->hdl, ...);
> > > +	state->mute = v4l2_ctrl_new_std(&state->hdl, ...);
> > > +	v4l2_ctrl_cluster(2, &state->volume);
> > 
> > What's the first argument to v4l2_ctrl_cluster ? The number of controls
> > in the cluster ?
> 
> Yes.
> 
> > Does that imply that they need to be added in a sequence, right
> > before v4l2_ctrl_cluster is called ? That seems a bit awkward. Wouldn't
> > it be better to specify the relationships between controls explicitly,
> > maybe by passing a pointer to the master control when creating the
> > 'slave' controls in the cluster ?
> 
> OK, I clearly need to explain this better.
> 
> What you pass is an array of v4l2_ctrl pointers, e.g.:
> 
> 	struct foo {
> 		struct v4l2_ctrl *cluster[2];
> 		...
> 	};
> 
> 	foo->cluster[CTRL_VOLUME] = v4l2_ctrl_new_std();
> 	foo->cluster[CTRL_MUTE] = v4l2_ctrl_new_std();
> 	v4l2_ctrl_cluster(2, foo->cluster);
> 
> cluster[0] is always the master.
> 
> Now, using arrays here becomes quickly very annoying, so instead you can
> just say:
> 	struct foo {
> 		/* volume/mute cluster */
> 		struct v4l2_ctrl *volume;
> 		struct v4l2_ctrl *mute;
> 		...
> 	};
> 
> 	foo->volume = v4l2_ctrl_new_std();
> 	foo->mute = v4l2_ctrl_new_std();
> 	v4l2_ctrl_cluster(2, &foo->volume);
> 
> Same thing, but without the cumbersome array indices.

That looks *very* dangerous. People will not get it (I haven't) and all kind 
of bad things will happen, like adding a field between two struct v4l2_ctrl 
pointers. This part of the API needs to be reworked, if you require an array 
then an array must be passed to the function.

[snip]

> > > +VIDIOC_LOG_STATUS Support
> > > +=========================
> > > +
> > > +This ioctl allow you to dump the current status of a driver to the
> > > kernel log.
> > 
> > This has nothing to do with the controls framework, but shouldn't that
> > ioctl be restricted to root only ? It can potentially dump a lot of
> > information to the kernel log, and thus system logs.
> 
> Personally I see no reason to change this.

You don't think it opens a door for unprivileged applications to flood the 
kernel and system logs ?

[snip]

> > > +Different Handlers for Different Video Nodes
> > > +============================================
> > > +
> > > +Usually the bridge driver has just one control handler that is global
> > > for +all video nodes. But you can also specify different control
> > > handlers for +different video nodes. It's no problem if there are no
> > > subdevs involved. +But if there are, then you need to block the
> > > automatic merging of subdev +controls to the global control handler.
> > > You do that by simply setting the +ctrl_handler field in struct
> > > v4l2_device to NULL.
> > > +
> > > +After each subdev was added, you will then have to call
> > > v4l2_ctrl_add_handler
> > > +manually to add the subdev's control handler (sd->ctrl_handler) to the
> > > desired
> > > +bridge control handler.
> > 
> > Do you mean video device instead of bridge here ?
> 
> struct v4l2_device.

I don't get it then. If you want to use different handlers for different video 
nodes, you need to add the subdev handler to the video node (video_device) 
handler, not the subdev handler to the v4l2_device handle.

> > I wouldn't mention "bridge" in here. I assume that by bridge you mean
> > v4l2_device. Please use that name directly. The term "bridge" is only
> > applicable to a subset of the v4l2_device use cases.
> 
> Good point.
> 
> > Can controls for a specific subdev be reported through more than one
> > video device node, but not all of them ?
> 
> Yes. It will be a bit fiddly to set up, but that's to be expected if you
> want to do things like that.

I was just wondering if it was possible, I have no use case in mind. I wonder 
if we should even allow that...

[snip]

> > > +Inheriting Controls
> > > +===================
> > > +
> > > +When one control handler is added to another using
> > > v4l2_ctrl_add_handler, then
> > > +by default all controls from one are merged to the other. But a subdev
> > > might
> > > +have low-level controls that make sense for some advanced embedded
> > > system, but
> > > +not when it is used in consumer-level hardware. In that case you want
> > > to keep
> > > +those low-level controls local to the subdev. You can do this by
> > > simply +setting the 'is_private' flag of the control to 1:
> > > +
> > > +	ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...);
> > > +	if (ctrl)
> > > +		ctrl->is_private = 1;
> > 
> > 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'm actually wondering if it wouldn't be better to pass a pointer to a
> > structure with the required information (maybe the v4l2_control_info
> > structure I mentioned earlier ?) to v4l2_ctrl_new_custom.
> 
> I thought about that, but 1) that means yet another structure and 2) I
> dislike code like that because the definition is usually quite far from
> the place were it is actually used.

Yes, it means yet another structure. On the other hand all those structures 
can be nicely grouped in an array, and controls can then be added using a loop 
instead of a bunch of function calls. I think it would be much cleaner, code 
would be more readable and error handling would be easier.

> > > +These controls will now be skipped when v4l2_ctrl_add_handler is
> > > called. +
> > > +
> > > +Strict Control Validation
> > > +=========================
> > > +
> > > +By default when the application wants to change an integer control the
> > > value
> > > +passed to the framework will automatically be modified to map to the
> > > provided
> > > +minimum, maximum and step values of the control. If instead you just
> > > want to
> > > +validate the value and not modify it, then set the 'strict_validation'
> > > flag of
> > > +the control:
> > > +
> > > +	ctrl->strict_validation = 1;
> > > +
> > > +Now -ERANGE will be returned if the new value does not match the
> > > control's +requirements.
> > 
> > Why do we need the two behaviours ? Wouldn't it be better to standardize
> > on one of them ?
> 
> I may have fallen in the pitfall of featurism here. Just because it is easy
> to implement, that doesn't mean that you should actually do it.
> 
> I think I'll remove strict_validation.

Thanks.

[snip]

> > > +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.
> > 
> > I still feel a bit awkward about the interface. One particular point that
> > might require attention is the split of the v4l2_ctrl structure into
> > v4l2_control_info and v4l2_control_value structures (the names are not
> > set into stone). I would also like to see if we can't pass pointers to
> > v4l2_control_info (or similar) to the control creation functions instead
> > of a plethora of arguments.
> 
> One of my original attempts was indeed to have two structs. But that made
> the code only more complex. Having two structs only makes sense if the
> 'static' part can be, well, static.

Indeed :-) Like I pointed out above there's some static information, but maybe 
not enough of them to split them into another structure. On the other hand, 
like I also pointed above, I think passing a structure, even if it's not big, 
to the control creation functions would make driver code cleaner.

> But then you have UVC where that isn't static. So the code then has to
> differentiate between these two cases and it quickly becomes a big mess.

There's static information in UVC. Look at uvc_ctrl.c, it starts with two big 
arrays.

> Regarding the plethora of arguments: remember that 99% of all controls will
> just use v4l2_ctrl_new_std(). And that ain't too bad. new_custom is indeed
> a big one, but currently only used by cx2341x where it is wrapped inside a
> much easier to digest function. I've tried to make the 90% of easy cases
> easy, and for the remaining 10% you will indeed have to do more work.

If we pass structures to the functions, drivers could only fill the fields 
they need and skip the other ones. For instance, a name field would be left 
untouched by drivers that call v4l2_ctrl_new_std(). The ones that call 
v4l2_ctrl_new_custom() would just need to fill the field in the 
v4l2_ctrl_whatever array. Clean and easy :-)

This (whether we should use a structure to create new controls) is something 
on which I'd like to hear other people's opinions (Sakari, I asked you to 
review this RFC, so please answer :-)).

> > I'll now have a look at the code. As there are already quite a few
> > comments on the documentation I won't perform a very in-depth code
> > review though, I'll save that for later after we agree on the spec :-)
> 
> Thanks for the review!
> 
> This was very useful.

You're welcome. I might bother you with other issues later if I catch new one 
:-)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 01/15] [RFC] v4l: Add new control handling framework
  2010-04-26  7:33 ` [PATCH 01/15] [RFC] v4l: Add new " Hans Verkuil
@ 2010-05-06 21:54   ` Laurent Pinchart
  0 siblings, 0 replies; 29+ messages in thread
From: Laurent Pinchart @ 2010-05-06 21:54 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

I don't think I should review the code in details before we agree on the 
architecture (please correct me if I'm wrong). Two comments though.

On Monday 26 April 2010 09:33:30 Hans Verkuil wrote:

[snip]

> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> new file mode 100644
> index 0000000..ea23f3d
> --- /dev/null
> +++ b/include/media/v4l2-ctrls.h

[snip]

> +/* 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);

Using kerneldoc comments in the source file would provide a much better 
documentation than a few lines of comment in the header.

[snip]

> diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
> index 2dee938..cc9ed09 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
> @@ -66,6 +67,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. */

Shouldn't we talk about a control*s* handler ? It handles more than one 
control (would be a bit pointless otherwise :-)).

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
  2010-05-06 21:36       ` Laurent Pinchart
@ 2010-05-07  9:23         ` Hans Verkuil
  0 siblings, 0 replies; 29+ messages in thread
From: Hans Verkuil @ 2010-05-07  9:23 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media

On Thursday 06 May 2010 23:36:48 Laurent Pinchart wrote:
> Hi Hans,
> 
> On Monday 03 May 2010 00:21:46 Hans Verkuil wrote:
> > On Sunday 02 May 2010 22:39:11 Laurent Pinchart wrote:
> > > On Monday 26 April 2010 09:33:38 Hans Verkuil wrote:
> 
> [snip]
> 
> > > > +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).
> > > 
> > > I'm not sure v4l2_ctrl is a good name. We already have a v4l2_control
> > > structure, and using the abbreviated name for the in-kernel version is
> > > going to be confusing.
> > 
> > Originally it was called v4l2_ctrl_info but that became very cumbersome.
> > It's not really an info object anyway, it really describes a control. When
> > using this framework the driver no longer sees the v4l2_control anymore, so
> > from the point of view of the driver there is only v4l2_ctrl.
> 
> Sure, but it's still misleading. Using an both the abbreviated and non-
> abbreviated names to refer to two different things has been done too often in 
> V4L2, and it's starting to bother me. It makes the API look inconsistent. We 
> need to define a naming policy and stick to it for the future. That can be a 
> topic for the V4L2 summit in June.

I can go back to v4l2_ctrl_info, unless you have a better name?

> > > It's obviously too late to change v4l2_control to something else.
> > > v4l2_control_info and v4l2_control_value are two names that come to my
> > > mind. Actually, it might be a good idea to split the v4l2_ctrl structure
> > > into a static driver-wide structure (v4l2_control_info) and an
> > > instance-specific structure (v4l2_control_value). There's no point in
> > > storing the same static data (function pointers, names, ...) for
> > > identical controls in different devices.
> > 
> > Other than the name there isn't anything that is static. And the name is
> > already static.
> 
> The following fields are static (they're identical across all v4l2_ctrl 
> instances for the same control):
> 
> - ops
> - id
> - name
> - type
> - menu (not too sure about this one)

It's honestly not worth it. The fields name, menu and ops already point to
static const datastructs, so that leaves only the pointers themselves that
you can save. And most of these fields are used very frequently as well, so
having do an extra dereference will reduce the performance.

I tried something like this in the beginning and it simply isn't worth the
pain it introduces.

> 
> [snip]
> 
> > > > +
> > > > +  How to hook the handler into a bridge driver:
> > > > +
> > > > +	foo->v4l2_dev.ctrl_handler = &foo->hdl;
> 
> [snip]
> 
> > > > +
> > > > +  And whenever you call video_register_device() you must set the
> > > > +  ctrl_handler field of struct video_device as well:
> > > > +
> > > > +	vdev->ctrl_handler = &foo->hdl;
> 
> [snip]
> 
> > > Why can't the framework find the ctrl_handler instance from the
> > > video_device's v4l2_device parent ?
> > 
> > Sometimes you want to provide different control handlers to different
> > video devices. E.g. /dev/radio0 may have only a small subset of the
> > controls of /dev/video0 (bttv does that, for example).
> > 
> > One option is to automatically let video_register_device copy ctrl_handler
> > from the v4l2_dev struct, but I think it is clearer if it is explicitly
> > set.
> 
> I was thinking about copying it implicitly if it's not set, yes. That way only 
> the v4l2_device instance would need an explicit handler, video devices would 
> inherit it from their v4l2_device parent. The driver would still have the 
> ability to set it if it wants to override the default behaviour.
> 
> It might be clearer to always set it explicitly, I have no strong opinion on 
> this one.

In that case I will leave it as is.
 
> > > > +  Finally, remove all control functions from your v4l2_ioctl_ops:
> > > > +  vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl,
> > > > +  vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls.
> > > > +  Those are now no longer needed.
> > > > +
> > > > +  How to hook the control handler into a subdev driver:
> > > > +
> > > > +	foo->sd.ctrl_handler = &foo->hdl;
> > > > +
> > > > +  And set all core control ops in your struct v4l2_subdev_core_ops to
> > > > these
> > > > +  helpers:
> > > > +
> > > > +	.queryctrl = v4l2_sd_queryctrl,
> > > > +	.querymenu = v4l2_sd_querymenu,
> > > > +	.g_ctrl = v4l2_sd_g_ctrl,
> > > > +	.s_ctrl = v4l2_sd_s_ctrl,
> > > > +	.g_ext_ctrls = v4l2_sd_g_ext_ctrls,
> > > > +	.try_ext_ctrls = v4l2_sd_try_ext_ctrls,
> > > > +	.s_ext_ctrls = v4l2_sd_s_ext_ctrls,
> > > 
> > > s/sd/subdev/ ?
> > 
> > I have to sleep on that... :-)
> 
> You've slept on it for a week now :-)
> 
> We really have too many abbreviations in V4L2, or at least no consistent usage 
> of them.

I'll change it to subdev. There helpers are temporarily anyway.
 
> [snip]
> 
> > > What about hardware for which the boundaries are only known at runtime,
> > > or could depend on the values of other controls ? I'm thinking about UVC
> > > devices for instance, the boundaries, step and default values need to be
> > > retrieved from the hardware. I currently do that at runtime when the
> > > control is queried for the first time and cache the values, as doing it
> > > during initialization (probe function) crashes a few webcams. That
> > > doesn't seem to be possible with the control framework.
> > 
> > It is possible to add controls to an existing control handler at runtime.
> > It is also possible to change boundaries at runtime: you just change the
> > relevant values in v4l2_ctrl. There is no function for that, it's enough
> > to call v4l2_ctrl_lock(), change the values and call unlock().
> > 
> > I could make a function that does this, but UVC is the only driver that I
> > know of that might need this.
> 
> My issue is that the UVC driver would need to set the boundaries the first 
> time the control is queried. There's no callback for that at the moment.

Ah! Now I understand your problem. What you need is an .init op that is called
the first time the control is used. That shouldn't be hard to add.
 
> > > > +	...
> > > > +	if (foo->hdl.error) {
> > > > +		int err = foo->hdl.error;
> > > > +
> > > > +		v4l2_ctrl_handler_free(&foo->hdl);
> > > > +		return err;
> > > > +	}
> > > > +
> > > > +The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new
> > > > +control, but if you do not need to access the pointer outside the
> > > > control ops,
> > > > +then there is no need to store it.
> > > > +
> > > > +Note that if something fails, the function will return NULL or an error
> > > > and
> > > > +set hdl->error to the error code. If hdl->error was already set, then
> > > > it
> > > > +will just return and do nothing. This is also true for
> > > > v4l2_ctrl_handler_init
> > > > +if it cannot allocate the internal data structure.
> > > > +
> > > > +This makes it easy to init the handler and just add all controls and
> > > > only check
> > > > +the error code at the end. Saves a lot of repetitive error checking.
> > > 
> > > I would still check the v4l2_ctrl_handler_init return value explicitly,
> > > but that may be just me.
> > 
> > Feel free to do that :-)
> 
> You know what I meant :-) I agree this is a good way to handle errors for 
> v4l2_ctrl_new_*, but v4l2_ctrl_handler_init feels... different.

I know what you mean, but it is just so useful to do it like this. Remember
that many drivers have just 1-4 controls, so having to introduce yet another
error path to handle the failure of v4l2_ctrl_handler_init is getting really
frustrating. I definitely like to keep this functionality, it saves unnecessary
repetitive code in a lot of drivers.

> 
> [snip]
> 
> > > > +3) Optionally force initial control setup:
> > > > +
> > > > +	v4l2_ctrl_handler_setup(&foo->hdl);
> > > > +
> > > > +This will call s_ctrl for all controls unconditionally. Effectively
> > > > this
> > > > +initializes the hardware to the default control values. It is
> > > > recommended +that you do this.
> > > 
> > > What about the other way around, if I want to initialize the framework
> > > with the current values retrieved from the hardware ?
> > 
> > In that case you would set the default value of the control based on the
> > value reported by the hardware.
> 
> The default value can still be different than the current value. Don't you 
> mean I should initialize the current value, not the default one ?
> 
> What should be recommended, calling v4l2_ctrl_handler_setup, or initializing 
> the framework with the current value ?

OK, you want to initialize the current value to something that isn't the
default value. I didn't catch that nuance.

There are two ways of doing this that I can think of up front:

The first is to use the proposed .init op. In the case of UVC that might be a
good choice actually. The second approach is to create the control and call
v4l2_ctrl_s to set it explicitly. Actually, I would favor using init for this.
Note that I suspect that in uvc you will not use v4l2_ctrl_handler_setup at all.
 
> 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 ?

It is already cached. Based on your misunderstanding I realized that I should
rename the g_ctrl op to g_volatile_ctrl. Because g_ctrl is only meant for
volatile controls. As you can see from my patches that convert existing drivers
to the control framework, the majority doesn't implement g_ctrl.

In the case of uvc (and if the .init op is added), the .init can take care of
the initial setup, querying the hardware for the current value, and if the
control isn't volatile, then the control's current value will be cached.
 
> Last thought: does the framework query the driver for the current control 
> values when setting a control in a cluster ? If two clustered controls are 
> stored in a single hardware "register", and one of them needs to be modified, 
> a read-update-write operation needs to be performed. The update part will be 
> done by the framework, the write part by the driver, but what about the read 
> part ?

No, it does not read the current control value before calling s_ctrl. I don't
think it is useful: if you need to do a read-update-write cycle, then you want
to do that in one operation. Having the read done well before the write is not
useful. If you need to, then you can call g_ctrl directly from within s_ctrl.
I have yet to see a driver that needs this, though.
 
> [snip]
> 
> > > > +Accessing Control Values
> > > > +========================
> > > > +
> > > > +The v4l2_ctrl struct contains these two unions:
> > > > +
> > > > +	/* The current control value. */
> > > > +	union {
> > > > +		s32 val;
> > > > +		s64 val64;
> > > > +		char *string;
> > > > +	} cur;
> > > > +
> > > > +	/* The new control value. */
> > > > +	union {
> > > > +		s32 val;
> > > > +		s64 val64;
> > > > +		char *string;
> > > > +	};
> > > > +
> > > > +Within the control ops you can freely use these. The val and val64
> > > > speak for
> > > > +themselves. The string pointers point to character buffers of length
> > > > +ctrl->maximum + 1, and are always 0-terminated.
> > > > +
> > > > +For g_ctrl you have to update the current control values like this:
> > > > +
> > > > +	ctrl->cur.val = read_reg(0x123);
> > > > +
> > > > +The 'new value' union is not relevant in g_ctrl.
> > > 
> > > When is g_ctrl called ? When the userspace applications issues a
> > > VIDIOC_G_CTRL or VIDIOC_G_EXT_CTRLS ioctl ?
> > 
> > Yes.
> 
> OK. Can you make that explicit ?

I clearly need to clarify this. It is called when someone (usually userspace
through these ioctls) needs to get the control's value. But normally drivers
do not implement and so the cached current value is returned. Only if this is
a volatile control do the drivers implement this op.

One other thing that needs to be clarified: when you create a new control the
initial current value will be set to the default value.

Calling v4l2_ctrl_handler_setup will set all controls with that initial current
value. In other words it syncs the hardware with the internal control state.
 
> > > I suppose the operation only needs to be implemented if the device needs
> > > to be queried because it modified the control value on its own (possibly
> > > as a result of setting another control). What if some controls need to be
> > > handled that way, while the others are not self-modifying ?
> > 
> > g_ctrl only has to take care of self-modifying controls and can ignore the
> > others. saa7115 has a use case like that.
> 
> 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).

I thought about this, but you can also create two op structs: one for volatile
controls containing a g_ctrl, and one for non-volatile controls which doesn't
have that. That way you don't need to set a flag afterwards.

What op struct you provide will give you the flexibility that you need.
I didn't think it was necessary to add yet another flag.

> > > > +For try/s_ctrl the new values (i.e. as passed by the user) are filled
> > > > in and
> > > > +you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
> > > > +contains the current value, which you can use (but not change!) as
> > > > well. +
> > > > +If s_ctrl returns 0 (OK), then the control framework will copy the new
> > > > final
> > > > +values to the 'cur' union.
> > > > +
> > > > +While in g/s/try_ctrl you can access the value of all controls owned
> > > > by the
> > > > +same handler since the handler's lock is held. Do not attempt to
> > > > access +the value of controls owned by other handlers, though.
> > > > +
> > > > +Elsewhere in the driver you have to be more careful. You cannot just
> > > > refer +to the current control values without locking.
> > > > +
> > > > +There are two simple helper functions defined that will get or set a
> > > > single
> > > > +control value safely:
> > > > +
> > > > +	s32 v4l2_ctrl_g(struct v4l2_ctrl *ctrl);
> > > > +	int v4l2_ctrl_s(struct v4l2_ctrl *ctrl, s32 val);
> > > 
> > > I suppose that v4l2_ctrl_s can be used if the device notifies the driver
> > > that a control has changed. Is that correct ?
> > > 
> > > I don't really like the names of those two functions. For instance,
> > > v4l2_ctrl_s looks like it will call the driver to set a control.
> > 
> > I think there is a misunderstanding here: v4l2_ctrl_s() will indeed change
> > the control's value.
> 
> Do you mean that it will call the driver's s_ctrl operation or that it will 
> modify the current value in the v4l2_ctrl structure ? The misunderstanding 
> probably calls for a function name change :-)

It will call the driver's s_ctrl operation. And a better name is indeed needed.
Suggestions?

> 
> [snip]
> 
> > > > +Menu Controls
> > > > +=============
> > > > +
> > > > +Menu controls use the 'step' value differently compared to other
> > > > control +types. The v4l2_ctrl struct contains this union:
> > > > +
> > > > +	union {
> > > > +		u32 step;
> > > > +		u32 menu_skip_mask;
> > > > +	};
> > > > +
> > > > +For menu controls menu_skip_mask is used. What it does is that it
> > > > allows you
> > > > +to easily exclude certain menu items. This is used in the
> > > > VIDIOC_QUERYMENU +implementation where you can return -EINVAL if a
> > > > certain menu item is not +present. Note that VIDIOC_QUERYCTRL always
> > > > returns a step value of 1 for +menu controls.
> > > > +
> > > > +A good example is the MPEG Audio Layer II Bitrate menu control where
> > > > the +menu is a list of standardized possible bitrates. But in practice
> > > > hardware +implementations will only support a subset of those. By
> > > > setting the skip +mask you can tell the framework which menu items
> > > > should be skipped. Setting
> > > > +it to 0 means that all menu items are supported.
> > > > +
> > > > +So when using v4l2_ctrl_new_std or v4l2_ctrl_new_custom you need to
> > > > remember
> > > > +that 'step' means 'skip mask' for menu controls. If you put in '1' by
> > > > mistake,
> > > > +then the first menu item will be skipped.
> > > 
> > > What about adding a v4l2_ctrl_new_menu then ? That will avoid such a
> > > mistake.
> > > 
> > > > +The v4l2_ctrl_new_std_menu can be used to add menu controls more
> > > > easily: it
> > > > +will calculate the min and max values automatically based on the size
> > > > of the
> > > > +menu, and it has a proper 'mask' argument instead of 'step'.
> > > 
> > > OK :-) Shouldn't v4l2_ctrl_new_custom be restricted to non-menu controls
> > > then ? Looking at the code, it should probably become an internal
> > > function, with another function added for custom controls without menu
> > > support.
> > 
> > I might add that in the future. I want to avoid creating a long series of
> > different functions, all alike :-)
> 
> Then drivers will use the function that will later become internal. You'll 
> have to change a bunch of drivers later, and you will tell me that it would be 
> too much work and wouldn't be worth it ;-)

It's more that I don't want to add functions unless there is at least one driver
that uses it.
 
> > > > +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.
> > > 
> > > What you refer to as active/inactive is usually referred to as
> > > enabled/disabled in the GUI world. Might be worth using the same
> > > convention.
> > 
> > Strictly speaking it is not quite the same. An inactive control can still
> > be written, the new value will simply not be used until the control
> > becomes active again. In a GUI that would be confusing, so the control is
> > disabled instead. It's a subtle difference that may or may not be
> > important.
> 
> What does the framework do when the userspace application attempts to write to 
> a disabled control ?

It returns -EINVAL as per spec.

> 
> [snip]
> 
> > > > +Control Clusters
> > > > +================
> > > > +
> > > > +By default all controls are independent from the others. But in more
> > > > +complex scenarios you can get dependencies from one control to
> > > > another. +In that case you need to 'cluster' them:
> > > > +
> > > > +	struct foo {
> > > > +		struct v4l2_ctrl_handler hdl;
> > > > +		struct v4l2_ctrl *volume;
> > > > +		struct v4l2_ctrl *mute;
> > > > +		...
> > > > +	};
> > > > +
> > > > +	state->volume = v4l2_ctrl_new_std(&state->hdl, ...);
> > > > +	state->mute = v4l2_ctrl_new_std(&state->hdl, ...);
> > > > +	v4l2_ctrl_cluster(2, &state->volume);
> > > 
> > > What's the first argument to v4l2_ctrl_cluster ? The number of controls
> > > in the cluster ?
> > 
> > Yes.
> > 
> > > Does that imply that they need to be added in a sequence, right
> > > before v4l2_ctrl_cluster is called ? That seems a bit awkward. Wouldn't
> > > it be better to specify the relationships between controls explicitly,
> > > maybe by passing a pointer to the master control when creating the
> > > 'slave' controls in the cluster ?
> > 
> > OK, I clearly need to explain this better.
> > 
> > What you pass is an array of v4l2_ctrl pointers, e.g.:
> > 
> > 	struct foo {
> > 		struct v4l2_ctrl *cluster[2];
> > 		...
> > 	};
> > 
> > 	foo->cluster[CTRL_VOLUME] = v4l2_ctrl_new_std();
> > 	foo->cluster[CTRL_MUTE] = v4l2_ctrl_new_std();
> > 	v4l2_ctrl_cluster(2, foo->cluster);
> > 
> > cluster[0] is always the master.
> > 
> > Now, using arrays here becomes quickly very annoying, so instead you can
> > just say:
> > 	struct foo {
> > 		/* volume/mute cluster */
> > 		struct v4l2_ctrl *volume;
> > 		struct v4l2_ctrl *mute;
> > 		...
> > 	};
> > 
> > 	foo->volume = v4l2_ctrl_new_std();
> > 	foo->mute = v4l2_ctrl_new_std();
> > 	v4l2_ctrl_cluster(2, &foo->volume);
> > 
> > Same thing, but without the cumbersome array indices.
> 
> That looks *very* dangerous. People will not get it (I haven't) and all kind 
> of bad things will happen, like adding a field between two struct v4l2_ctrl 
> pointers. This part of the API needs to be reworked, if you require an array 
> then an array must be passed to the function.

I realized the same: I need to write this as an array. It's annoying since it
requires a lot of macros for the array indices.

An alternative might be to move the pointers into a struct:

	struct foo {
		/* volume/mute cluster */
		struct cluster {
			struct v4l2_ctrl *volume;
			struct v4l2_ctrl *mute;
		};
		...
 	};

Better, but I fear that it is not enough. I hate having to do

	struct foo {
		/* volume/mute cluster */
		#define FOO_VOL 0
		#define FOO_MUTE 1
		struct v4l2_ctrl *vol_mute[2];
		...
 	};

And have to refer to controls using foo->vol_mute[FOO_MUTE]. It's a mouthful.

Hmm. Perhaps some static inline helpers like

static inline struct v4l2_ctrl *foo_volume(struct foo *foo)
{
	return foo->vol_mute[FOO_VOL];
}

> 
> [snip]
> 
> > > > +VIDIOC_LOG_STATUS Support
> > > > +=========================
> > > > +
> > > > +This ioctl allow you to dump the current status of a driver to the
> > > > kernel log.
> > > 
> > > This has nothing to do with the controls framework, but shouldn't that
> > > ioctl be restricted to root only ? It can potentially dump a lot of
> > > information to the kernel log, and thus system logs.
> > 
> > Personally I see no reason to change this.
> 
> You don't think it opens a door for unprivileged applications to flood the 
> kernel and system logs ?

There is a printk variant that prevents against flooding. The name escapes
me at the moment. But it is probably a good idea to use that instead of the
normal printk.

> 
> [snip]
> 
> > > > +Different Handlers for Different Video Nodes
> > > > +============================================
> > > > +
> > > > +Usually the bridge driver has just one control handler that is global
> > > > for +all video nodes. But you can also specify different control
> > > > handlers for +different video nodes. It's no problem if there are no
> > > > subdevs involved. +But if there are, then you need to block the
> > > > automatic merging of subdev +controls to the global control handler.
> > > > You do that by simply setting the +ctrl_handler field in struct
> > > > v4l2_device to NULL.
> > > > +
> > > > +After each subdev was added, you will then have to call
> > > > v4l2_ctrl_add_handler
> > > > +manually to add the subdev's control handler (sd->ctrl_handler) to the
> > > > desired
> > > > +bridge control handler.
> > > 
> > > Do you mean video device instead of bridge here ?
> > 
> > struct v4l2_device.
> 
> I don't get it then. If you want to use different handlers for different video 
> nodes, you need to add the subdev handler to the video node (video_device) 
> handler, not the subdev handler to the v4l2_device handle.

I'm sorry, you are completely right. I didn't read this carefully enough. It is
indeed video device instead of v4l2_device.

BTW: I'd love to do a search-and-replace for video_device to something like
v4l2_devnode. It's very confusing at the moment.
 
> > > I wouldn't mention "bridge" in here. I assume that by bridge you mean
> > > v4l2_device. Please use that name directly. The term "bridge" is only
> > > applicable to a subset of the v4l2_device use cases.
> > 
> > Good point.
> > 
> > > Can controls for a specific subdev be reported through more than one
> > > video device node, but not all of them ?
> > 
> > Yes. It will be a bit fiddly to set up, but that's to be expected if you
> > want to do things like that.
> 
> I was just wondering if it was possible, I have no use case in mind. I wonder 
> if we should even allow that...
> 
> [snip]
> 
> > > > +Inheriting Controls
> > > > +===================
> > > > +
> > > > +When one control handler is added to another using
> > > > v4l2_ctrl_add_handler, then
> > > > +by default all controls from one are merged to the other. But a subdev
> > > > might
> > > > +have low-level controls that make sense for some advanced embedded
> > > > system, but
> > > > +not when it is used in consumer-level hardware. In that case you want
> > > > to keep
> > > > +those low-level controls local to the subdev. You can do this by
> > > > simply +setting the 'is_private' flag of the control to 1:
> > > > +
> > > > +	ctrl = v4l2_ctrl_new_custom(&sd->hdl, &sd_ctrl_ops, ...);
> > > > +	if (ctrl)
> > > > +		ctrl->is_private = 1;
> > > 
> > > 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.

It's an internal API, not a public one. And I think it is important not to
implement something unless it is actually used.

> 
> > > I'm actually wondering if it wouldn't be better to pass a pointer to a
> > > structure with the required information (maybe the v4l2_control_info
> > > structure I mentioned earlier ?) to v4l2_ctrl_new_custom.
> > 
> > I thought about that, but 1) that means yet another structure and 2) I
> > dislike code like that because the definition is usually quite far from
> > the place were it is actually used.
> 
> Yes, it means yet another structure. On the other hand all those structures 
> can be nicely grouped in an array, and controls can then be added using a loop 
> instead of a bunch of function calls. I think it would be much cleaner, code 
> would be more readable and error handling would be easier.
> 
> > > > +These controls will now be skipped when v4l2_ctrl_add_handler is
> > > > called. +
> > > > +
> > > > +Strict Control Validation
> > > > +=========================
> > > > +
> > > > +By default when the application wants to change an integer control the
> > > > value
> > > > +passed to the framework will automatically be modified to map to the
> > > > provided
> > > > +minimum, maximum and step values of the control. If instead you just
> > > > want to
> > > > +validate the value and not modify it, then set the 'strict_validation'
> > > > flag of
> > > > +the control:
> > > > +
> > > > +	ctrl->strict_validation = 1;
> > > > +
> > > > +Now -ERANGE will be returned if the new value does not match the
> > > > control's +requirements.
> > > 
> > > Why do we need the two behaviours ? Wouldn't it be better to standardize
> > > on one of them ?
> > 
> > I may have fallen in the pitfall of featurism here. Just because it is easy
> > to implement, that doesn't mean that you should actually do it.
> > 
> > I think I'll remove strict_validation.
> 
> Thanks.
> 
> [snip]
> 
> > > > +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.
> > > 
> > > I still feel a bit awkward about the interface. One particular point that
> > > might require attention is the split of the v4l2_ctrl structure into
> > > v4l2_control_info and v4l2_control_value structures (the names are not
> > > set into stone). I would also like to see if we can't pass pointers to
> > > v4l2_control_info (or similar) to the control creation functions instead
> > > of a plethora of arguments.
> > 
> > One of my original attempts was indeed to have two structs. But that made
> > the code only more complex. Having two structs only makes sense if the
> > 'static' part can be, well, static.
> 
> Indeed :-) Like I pointed out above there's some static information, but maybe 
> not enough of them to split them into another structure. On the other hand, 
> like I also pointed above, I think passing a structure, even if it's not big, 
> to the control creation functions would make driver code cleaner.
> 
> > But then you have UVC where that isn't static. So the code then has to
> > differentiate between these two cases and it quickly becomes a big mess.
> 
> There's static information in UVC. Look at uvc_ctrl.c, it starts with two big 
> arrays.
> 
> > Regarding the plethora of arguments: remember that 99% of all controls will
> > just use v4l2_ctrl_new_std(). And that ain't too bad. new_custom is indeed
> > a big one, but currently only used by cx2341x where it is wrapped inside a
> > much easier to digest function. I've tried to make the 90% of easy cases
> > easy, and for the remaining 10% you will indeed have to do more work.
> 
> If we pass structures to the functions, drivers could only fill the fields 
> they need and skip the other ones. For instance, a name field would be left 
> untouched by drivers that call v4l2_ctrl_new_std(). The ones that call 
> v4l2_ctrl_new_custom() would just need to fill the field in the 
> v4l2_ctrl_whatever array. Clean and easy :-)
> 
> This (whether we should use a structure to create new controls) is something 
> on which I'd like to hear other people's opinions (Sakari, I asked you to 
> review this RFC, so please answer :-)).

I have thought about this a bit more. I think it makes sense to have
v4l2_ctrl_new_custom use a struct as input. But for v4l2_ctrl_new_std it makes
no sense IMHO. This function is already very simple and straightforward.

I will make a more detailed proposal for this later.

Regards,

	Hans

> 
> > > I'll now have a look at the code. As there are already quite a few
> > > comments on the documentation I won't perform a very in-depth code
> > > review though, I'll save that for later after we agree on the spec :-)
> > 
> > Thanks for the review!
> > 
> > This was very useful.
> 
> You're welcome. I might bother you with other issues later if I catch new one 
> :-)
> 
> 

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API.
  2010-05-02 22:21     ` Hans Verkuil
  2010-05-06 21:36       ` Laurent Pinchart
@ 2010-05-12 11:00       ` Sakari Ailus
  1 sibling, 0 replies; 29+ messages in thread
From: Sakari Ailus @ 2010-05-12 11:00 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Laurent Pinchart, linux-media

Hi Hans,

Hans Verkuil wrote:
[clip]
>> Should't v4l2_ctrl_new_std take a control type as well ?
> 
> The type is set automatically as that is determined by the control ID.
> 
>> What about hardware for which the boundaries are only known at runtime, or 
>> could depend on the values of other controls ? I'm thinking about UVC devices 
>> for instance, the boundaries, step and default values need to be retrieved 
>> from the hardware. I currently do that at runtime when the control is queried 
>> for the first time and cache the values, as doing it during initialization 
>> (probe function) crashes a few webcams. That doesn't seem to be possible with 
>> the control framework.
> 
> It is possible to add controls to an existing control handler at runtime.
> It is also possible to change boundaries at runtime: you just change the
> relevant values in v4l2_ctrl. There is no function for that, it's enough
> to call v4l2_ctrl_lock(), change the values and call unlock().
> 
> I could make a function that does this, but UVC is the only driver that I
> know of that might need this.

It won't be for long. Think of the sensor drivers, for example. The
maximum exposure time (often expressed in lines) is dependent on the
active sensor area (lines) plus vertical blanking. I'd assume the active
area to be selected in other ways than using controls, however.

The vertical blanking, though, also affects the frame rate, and thus
available exposure time. Not quite sure whether the user should be left
to touch the vertical blanking directly.

Regards,

-- 
Sakari Ailus
sakari.ailus@maxwell.research.nokia.com

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2010-05-12 11:00 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-26  7:33 [PATCH 00/15] [RFC] New control handling framework Hans Verkuil
2010-04-26  7:33 ` [PATCH 01/15] [RFC] v4l: Add new " Hans Verkuil
2010-05-06 21:54   ` Laurent Pinchart
2010-04-26  7:33 ` [PATCH 02/15] [RFC] v4l2-ctrls: reorder 'case' statements to match order in header Hans Verkuil
2010-05-02 20:42   ` Laurent Pinchart
2010-05-03  6:42     ` Hans Verkuil
2010-05-03  7:23       ` Laurent Pinchart
2010-04-26  7:33 ` [PATCH 03/15] [RFC] Documentation: add v4l2-controls.txt documenting the new controls API Hans Verkuil
2010-05-02 20:39   ` Laurent Pinchart
2010-05-02 22:21     ` Hans Verkuil
2010-05-06 21:36       ` Laurent Pinchart
2010-05-07  9:23         ` Hans Verkuil
2010-05-12 11:00       ` Sakari Ailus
2010-04-26  7:33 ` [PATCH 04/15] [RFC] v4l: hook up the new control framework into the core framework Hans Verkuil
2010-04-26  7:33 ` [PATCH 05/15] [RFC] saa7115: convert to the new control framework Hans Verkuil
2010-04-26 15:53   ` David Ellingsworth
2010-04-26  7:33 ` [PATCH 06/15] [RFC] msp3400: " Hans Verkuil
2010-04-26 16:17   ` David Ellingsworth
2010-04-26  7:33 ` [PATCH 07/15] [RFC] saa717x: " Hans Verkuil
2010-04-26  7:33 ` [PATCH 08/15] [RFC] cx25840/ivtv: replace ugly priv control with s_config Hans Verkuil
2010-05-02 20:41   ` Laurent Pinchart
2010-05-02 22:25     ` Hans Verkuil
2010-04-26  7:33 ` [PATCH 09/15] [RFC] cx25840: convert to the new control framework Hans Verkuil
2010-04-26  7:34 ` [PATCH 10/15] [RFC] cx2341x: convert to the " Hans Verkuil
2010-04-26  7:34 ` [PATCH 11/15] [RFC] wm8775: convert to the new " Hans Verkuil
2010-04-26  7:34 ` [PATCH 12/15] [RFC] cs53l32a: convert to " Hans Verkuil
2010-04-26  7:34 ` [PATCH 13/15] [RFC] wm8739: convert to the " Hans Verkuil
2010-04-26  7:34 ` [PATCH 14/15] [RFC] ivtv: convert gpio subdev to " Hans Verkuil
2010-04-26  7:34 ` [PATCH 15/15] [RFC] ivtv: convert to the " Hans Verkuil

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.