From ba09044961948d93db7aa166f2829d46e81e875a Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dsteve@broadcom.com>
Date: Fri, 14 Feb 2014 17:12:08 +0000
Subject: [PATCH 173/174] V4L2: Initial pass at scene modes.

Only supports exposure mode and metering modes.

Signed-off-by: Dave Stevenson <dsteve@broadcom.com>
---
 drivers/media/platform/bcm2835/bcm2835-camera.h |  10 +-
 drivers/media/platform/bcm2835/controls.c       | 225 ++++++++++++++++++++----
 2 files changed, 199 insertions(+), 36 deletions(-)

--- a/drivers/media/platform/bcm2835/bcm2835-camera.h
+++ b/drivers/media/platform/bcm2835/bcm2835-camera.h
@@ -15,7 +15,7 @@
  * core driver device
  */
 
-#define V4L2_CTRL_COUNT 24 /* number of v4l controls */
+#define V4L2_CTRL_COUNT 25 /* number of v4l controls */
 
 enum {
 	MMAL_COMPONENT_CAMERA = 0,
@@ -45,11 +45,15 @@ struct bm2835_mmal_dev {
 	/* controls */
 	struct v4l2_ctrl_handler  ctrl_handler;
 	struct v4l2_ctrl          *ctrls[V4L2_CTRL_COUNT];
+	enum v4l2_scene_mode	  scene_mode;
 	struct mmal_colourfx      colourfx;
 	int                       hflip;
 	int                       vflip;
-	enum mmal_parameter_exposuremode exposure_mode;
-	enum v4l2_exposure_auto_type exposure_mode_v4l2;
+	enum mmal_parameter_exposuremode exposure_mode_user;
+	enum v4l2_exposure_auto_type exposure_mode_v4l2_user;
+	/* active exposure mode may differ if selected via a scene mode */
+	enum mmal_parameter_exposuremode exposure_mode_active;
+	enum mmal_parameter_exposuremeteringmode metering_mode;
 	unsigned int		  manual_shutter_speed;
 	bool			  exp_auto_priority;
 
--- a/drivers/media/platform/bcm2835/controls.c
+++ b/drivers/media/platform/bcm2835/controls.c
@@ -145,6 +145,25 @@ static const struct v4l2_to_mmal_effects
 		1,   1,    0,    0,   0, {0, 0, 0, 0, 0} }
 };
 
+struct v4l2_mmal_scene_config {
+	enum v4l2_scene_mode			v4l2_scene;
+	enum mmal_parameter_exposuremode	exposure_mode;
+	enum mmal_parameter_exposuremeteringmode metering_mode;
+};
+
+static const struct v4l2_mmal_scene_config scene_configs[] = {
+	/* V4L2_SCENE_MODE_NONE automatically added */
+	{
+		V4L2_SCENE_MODE_NIGHT,
+		MMAL_PARAM_EXPOSUREMODE_NIGHT,
+		MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE
+	},
+	{
+		V4L2_SCENE_MODE_SPORTS,
+		MMAL_PARAM_EXPOSUREMODE_SPORTS,
+		MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE
+	},
+};
 
 /* control handlers*/
 
@@ -296,7 +315,7 @@ static int ctrl_set_exposure(struct bm28
 		      struct v4l2_ctrl *ctrl,
 		      const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl)
 {
-	enum mmal_parameter_exposuremode exp_mode = dev->exposure_mode;
+	enum mmal_parameter_exposuremode exp_mode = dev->exposure_mode_user;
 	u32 shutter_speed = 0;
 	struct vchiq_mmal_port *control;
 	int ret = 0;
@@ -317,31 +336,32 @@ static int ctrl_set_exposure(struct bm28
 		case V4L2_EXPOSURE_MANUAL:
 			exp_mode = MMAL_PARAM_EXPOSUREMODE_OFF;
 			break;
-
-		case V4L2_EXPOSURE_SHUTTER_PRIORITY:
-			exp_mode = MMAL_PARAM_EXPOSUREMODE_SPORTS;
-			break;
-
-		case V4L2_EXPOSURE_APERTURE_PRIORITY:
-			exp_mode = MMAL_PARAM_EXPOSUREMODE_NIGHT;
-			break;
-
 		}
-		dev->exposure_mode = exp_mode;
-		dev->exposure_mode_v4l2 = ctrl->val;
+		dev->exposure_mode_user = exp_mode;
+		dev->exposure_mode_v4l2_user = ctrl->val;
 	} else if (mmal_ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) {
 		dev->exp_auto_priority = ctrl->val;
 	}
 
-	if (dev->exposure_mode == MMAL_PARAM_EXPOSUREMODE_OFF)
-		shutter_speed = dev->manual_shutter_speed;
+	if (dev->scene_mode == V4L2_SCENE_MODE_NONE) {
+		if (exp_mode == MMAL_PARAM_EXPOSUREMODE_OFF)
+			shutter_speed = dev->manual_shutter_speed;
 
-	ret = vchiq_mmal_port_parameter_set(dev->instance, control,
-				     MMAL_PARAMETER_SHUTTER_SPEED,
-				     &shutter_speed, sizeof(shutter_speed));
-	ret += vchiq_mmal_port_parameter_set(dev->instance, control,
-					     MMAL_PARAMETER_EXPOSURE_MODE,
-					     &exp_mode, sizeof(u32));
+		ret = vchiq_mmal_port_parameter_set(dev->instance,
+					control,
+					MMAL_PARAMETER_SHUTTER_SPEED,
+					&shutter_speed,
+					sizeof(shutter_speed));
+		ret += vchiq_mmal_port_parameter_set(dev->instance,
+					control,
+					MMAL_PARAMETER_EXPOSURE_MODE,
+					&exp_mode,
+					sizeof(u32));
+		dev->exposure_mode_active = exp_mode;
+	}
+	/* exposure_dynamic_framerate (V4L2_CID_EXPOSURE_AUTO_PRIORITY) should
+	 * always apply irrespective of scene mode.
+	 */
 	ret += set_framerate_params(dev);
 
 	return ret;
@@ -351,35 +371,38 @@ static int ctrl_set_metering_mode(struct
 			   struct v4l2_ctrl *ctrl,
 			   const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl)
 {
-	u32 u32_value;
-	struct vchiq_mmal_port *control;
-
-	control = &dev->component[MMAL_COMPONENT_CAMERA]->control;
-
 	switch (ctrl->val) {
 	case V4L2_EXPOSURE_METERING_AVERAGE:
-		u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE;
+		dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE;
 		break;
 
 	case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED:
-		u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT;
+		dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT;
 		break;
 
 	case V4L2_EXPOSURE_METERING_SPOT:
-		u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT;
+		dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT;
 		break;
 
 	/* todo matrix weighting not added to Linux API till 3.9
 	case V4L2_EXPOSURE_METERING_MATRIX:
-		u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX;
+		dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX;
 		break;
 	*/
 
 	}
 
-	return vchiq_mmal_port_parameter_set(dev->instance, control,
+	if (dev->scene_mode == V4L2_SCENE_MODE_NONE) {
+		struct vchiq_mmal_port *control;
+		u32 u32_value = dev->metering_mode;
+
+		control = &dev->component[MMAL_COMPONENT_CAMERA]->control;
+
+		return vchiq_mmal_port_parameter_set(dev->instance, control,
 					     mmal_ctrl->mmal_id,
 					     &u32_value, sizeof(u32_value));
+	} else
+		return 0;
 }
 
 static int ctrl_set_flicker_avoidance(struct bm2835_mmal_dev *dev,
@@ -738,6 +761,113 @@ static int ctrl_set_video_encode_profile
 	return ret;
 }
 
+static int ctrl_set_scene_mode(struct bm2835_mmal_dev *dev,
+		      struct v4l2_ctrl *ctrl,
+		      const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl)
+{
+	int ret = 0;
+	int shutter_speed;
+	struct vchiq_mmal_port *control;
+
+	v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev,
+		"scene mode selected %d, was %d\n", ctrl->val,
+		dev->scene_mode);
+	control = &dev->component[MMAL_COMPONENT_CAMERA]->control;
+
+	if (ctrl->val == dev->scene_mode)
+		return 0;
+
+	if (ctrl->val == V4L2_SCENE_MODE_NONE) {
+		/* Restore all user selections */
+		dev->scene_mode = V4L2_SCENE_MODE_NONE;
+
+		if (dev->exposure_mode_user == MMAL_PARAM_EXPOSUREMODE_OFF)
+			shutter_speed = dev->manual_shutter_speed;
+		else
+			shutter_speed = 0;
+
+		v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev,
+			"%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n",
+			__func__, shutter_speed, dev->exposure_mode_user,
+			dev->metering_mode);
+		ret = vchiq_mmal_port_parameter_set(dev->instance,
+					control,
+					MMAL_PARAMETER_SHUTTER_SPEED,
+					&shutter_speed,
+					sizeof(shutter_speed));
+		ret += vchiq_mmal_port_parameter_set(dev->instance,
+					control,
+					MMAL_PARAMETER_EXPOSURE_MODE,
+					&dev->exposure_mode_user,
+					sizeof(u32));
+		dev->exposure_mode_active = dev->exposure_mode_user;
+		ret += vchiq_mmal_port_parameter_set(dev->instance,
+					control,
+					MMAL_PARAMETER_EXP_METERING_MODE,
+					&dev->metering_mode,
+					sizeof(u32));
+		ret += set_framerate_params(dev);
+	} else {
+		/* Set up scene mode */
+		int i;
+		const struct v4l2_mmal_scene_config *scene = NULL;
+		int shutter_speed;
+		enum mmal_parameter_exposuremode exposure_mode;
+		enum mmal_parameter_exposuremeteringmode metering_mode;
+
+		for (i = 0; i < ARRAY_SIZE(scene_configs); i++) {
+			if (scene_configs[i].v4l2_scene ==
+				ctrl->val) {
+				scene = &scene_configs[i];
+				break;
+			}
+		}
+		if (i >= ARRAY_SIZE(scene_configs))
+			return -EINVAL;
+
+		/* Set all the values */
+		dev->scene_mode = ctrl->val;
+
+		if (scene->exposure_mode == MMAL_PARAM_EXPOSUREMODE_OFF)
+			shutter_speed = dev->manual_shutter_speed;
+		else
+			shutter_speed = 0;
+		exposure_mode = scene->exposure_mode;
+		metering_mode = scene->metering_mode;
+
+		v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev,
+			"%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n",
+			__func__, shutter_speed, exposure_mode, metering_mode);
+
+		ret = vchiq_mmal_port_parameter_set(dev->instance, control,
+					MMAL_PARAMETER_SHUTTER_SPEED,
+					&shutter_speed,
+					sizeof(shutter_speed));
+		ret += vchiq_mmal_port_parameter_set(dev->instance,
+					control,
+					MMAL_PARAMETER_EXPOSURE_MODE,
+					&exposure_mode,
+					sizeof(u32));
+		dev->exposure_mode_active = exposure_mode;
+		ret += vchiq_mmal_port_parameter_set(dev->instance, control,
+					MMAL_PARAMETER_EXPOSURE_MODE,
+					&exposure_mode,
+					sizeof(u32));
+		ret += vchiq_mmal_port_parameter_set(dev->instance, control,
+					MMAL_PARAMETER_EXP_METERING_MODE,
+					&metering_mode,
+					sizeof(u32));
+		ret += set_framerate_params(dev);
+	}
+	if (ret) {
+		v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev,
+			"%s: Setting scene to %d, ret=%d\n",
+			__func__, ctrl->val, ret);
+		ret = -EINVAL;
+	}
+	return 0;
+}
+
 static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct bm2835_mmal_dev *dev =
@@ -973,6 +1103,15 @@ static const struct bm2835_mmal_v4l2_ctr
 		&ctrl_set_video_encode_profile_level,
 		false
 	},
+	{
+		V4L2_CID_SCENE_MODE, MMAL_CONTROL_TYPE_STD_MENU,
+		-1,	/* Min is computed at runtime */
+		V4L2_SCENE_MODE_TEXT,
+		V4L2_SCENE_MODE_NONE, 1, NULL,
+		MMAL_PARAMETER_PROFILE,
+		&ctrl_set_scene_mode,
+		false
+	},
 };
 
 int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev)
@@ -1000,8 +1139,7 @@ int set_framerate_params(struct bm2835_m
 	struct mmal_parameter_fps_range fps_range;
 	int ret;
 
-	if ((dev->exposure_mode_v4l2 == V4L2_EXPOSURE_AUTO ||
-	     dev->exposure_mode_v4l2 == V4L2_EXPOSURE_APERTURE_PRIORITY) &&
+	if ((dev->exposure_mode_active != MMAL_PARAM_EXPOSUREMODE_OFF) &&
 	     (dev->exp_auto_priority)) {
 		/* Variable FPS. Define min FPS as 1fps.
 		 * Max as max defined FPS.
@@ -1049,6 +1187,7 @@ int set_framerate_params(struct bm2835_m
 	return ret;
 
 }
+
 int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev,
 			      struct v4l2_ctrl_handler *hdl)
 {
@@ -1068,10 +1207,30 @@ int bm2835_mmal_init_controls(struct bm2
 			break;
 
 		case MMAL_CONTROL_TYPE_STD_MENU:
+		{
+			int mask = ctrl->min;
+
+			if (ctrl->id == V4L2_CID_SCENE_MODE) {
+				/* Special handling to work out the mask
+				 * value based on the scene_configs array
+				 * at runtime. Reduces the chance of
+				 * mismatches.
+				 */
+				int i;
+				mask = 1<<V4L2_SCENE_MODE_NONE;
+				for (i = 0;
+				     i < ARRAY_SIZE(scene_configs);
+				     i++) {
+					mask |= 1<<scene_configs[i].v4l2_scene;
+				}
+				mask = ~mask;
+			}
+
 			dev->ctrls[c] = v4l2_ctrl_new_std_menu(hdl,
 			&bm2835_mmal_ctrl_ops, ctrl->id,
-			ctrl->max, ctrl->min, ctrl->def);
+			ctrl->max, mask, ctrl->def);
 			break;
+		}
 
 		case MMAL_CONTROL_TYPE_INT_MENU:
 			dev->ctrls[c] = v4l2_ctrl_new_int_menu(hdl,