mt9e013: async power up sequence to decrease Lanuch2preview time
authorTao Jing <jing.tao@intel.com>
Fri, 13 Apr 2012 03:26:33 +0000 (11:26 +0800)
committerbuildbot <buildbot@intel.com>
Thu, 3 May 2012 20:12:04 +0000 (13:12 -0700)
BZ: 32405

Currently power up time for mt9e013 takes about 130ms which blocks the v4l2 call
of S_INPUT, since sensor subdev call s_power is called from it. And hense impact
the overall lunch2preview performance.

This patch makes s_power subdev call create a workthread to do the
time-consuming power up procedure and return immediately, which will
not block the s_input and also let the power up procedure can be run
in parallel with other initializations. For other subdev calls that
access sensor hw and need sensor power up to be done, e.g.: s_mbus_fmt,
s_ctrl, g_ctrl, etc, it will check and wait for the power up workthread
to be finished.

Change-Id: I31ef1307c82f4a516aad78409b4881b2aa53469f
Signed-off-by: Tao Jing <jing.tao@intel.com>
Signed-off-by: ziyux.jiang <ziyux.jiang@intel.com>
Reviewed-on: http://android.intel.com:8080/44109
Reviewed-by: Bhat, Sheethal <sheethal.bhat@intel.com>
Reviewed-by: Wang, Wen W <wen.w.wang@intel.com>
Reviewed-by: Lampila, KalleX <kallex.lampila@intel.com>
Tested-by: Lampila, KalleX <kallex.lampila@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
drivers/media/video/mt9e013.c
drivers/media/video/mt9e013.h

index 06374f2..8956e48 100644 (file)
@@ -680,6 +680,39 @@ static int mt9e013_write_reg_array(struct i2c_client *client,
        return __mt9e013_flush_reg_array(client, &ctrl);
 }
 
+static int wait_power_on_lock(struct v4l2_subdev *sd)
+{
+       struct mt9e013_device *dev = to_mt9e013_sensor(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int power_state;
+
+       power_state = atomic_read(&dev->power_state);
+
+       /* not powered on before, this is not allowed */
+       if (power_state == POWER_OFF)
+               return -EINVAL;
+
+       if (power_state == POWER_ON_INPROGRESS) {
+               if (!wait_for_completion_timeout(&dev->power_complete,
+                                                2 * HZ)) {
+                       /*
+                        * this is really abnormal case that power on
+                        * should not take such long time.
+                        */
+                       dev_err(&client->dev, "waiting power up timeout!\n");
+                       return -EAGAIN;
+               }
+               power_state = atomic_read(&dev->power_state);
+       }
+
+       if (power_state == POWER_ON_ERROR) {
+               dev_err(&client->dev, "power up err!\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
 static int mt9e013_t_focus_abs(struct v4l2_subdev *sd, s32 value)
 {
        struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -1050,8 +1083,14 @@ out:
 
 static long mt9e013_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
 {
+       int ret;
+
        switch (cmd) {
        case ATOMISP_IOC_S_EXPOSURE:
+               ret = wait_power_on_lock(sd);
+               if (ret)
+                       return ret;
+
                return mt9e013_s_exposure(sd, (struct atomisp_exposure *)arg);
        case ATOMISP_IOC_G_SENSOR_PRIV_INT_DATA:
                return mt9e013_g_priv_int_data(sd, arg);
@@ -1194,21 +1233,51 @@ static int power_down(struct v4l2_subdev *sd)
        return ret;
 }
 
+static void mt9e013_powerup_work(struct work_struct *work)
+{
+       struct mt9e013_device *dev = container_of(work,
+                                                 struct mt9e013_device, work);
+
+       int ret;
+
+       ret = power_up(&dev->sd);
+       if (!ret) {
+               /* init motor initial position */
+               ret =  __mt9e013_init(&dev->sd, 0);
+       }
+
+       if (ret)
+               atomic_set(&dev->power_state, POWER_ON_ERROR);
+       else
+               atomic_set(&dev->power_state, POWER_ON);
+
+       complete(&dev->power_complete);
+}
+
 static int mt9e013_s_power(struct v4l2_subdev *sd, int on)
 {
        struct mt9e013_device *dev = to_mt9e013_sensor(sd);
-       int ret;
+       int ret = 0;
+       int power_state;
+
+       power_state = atomic_read(&dev->power_state);
 
        if (on == 0) {
+               if (power_state == POWER_OFF)
+                       return 0;
+               else
+                       ret = wait_power_on_lock(sd);
+
+               if (ret == -EAGAIN)
+                       cancel_work_sync(&dev->work);
+
                mt9e013_uninit(sd);
                ret = power_down(sd);
-               dev->power = 0;
+               atomic_set(&dev->power_state, POWER_OFF);
        } else {
-               ret = power_up(sd);
-               if (!ret) {
-                       dev->power = 1;
-                       /* init motor initial position */
-                       return __mt9e013_init(sd, 0);
+               if (power_state == POWER_OFF) {
+                       atomic_set(&dev->power_state, POWER_ON_INPROGRESS);
+                       queue_work(dev->work_queue, &dev->work);
                }
        }
 
@@ -1649,6 +1718,10 @@ static int mt9e013_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
        if ((octrl == NULL) || (octrl->tweak == NULL))
                return -EINVAL;
 
+       ret = wait_power_on_lock(sd);
+       if (ret)
+               return ret;
+
        mutex_lock(&dev->input_lock);
        ret = octrl->tweak(sd, ctrl->value);
        mutex_unlock(&dev->input_lock);
@@ -1783,6 +1856,10 @@ static int mt9e013_s_mbus_fmt(struct v4l2_subdev *sd,
                return ret;
        }
 
+       ret = wait_power_on_lock(sd);
+       if (ret)
+               return ret;
+
        mutex_lock(&dev->input_lock);
        dev->fmt_idx = get_resolution_index(fmt->width, fmt->height);
 
@@ -1874,6 +1951,10 @@ static int mt9e013_s_stream(struct v4l2_subdev *sd, int enable)
        struct i2c_client *client = v4l2_get_subdevdata(sd);
        struct mt9e013_device *dev = to_mt9e013_sensor(sd);
 
+       ret = wait_power_on_lock(sd);
+       if (ret)
+               return ret;
+
        mutex_lock(&dev->input_lock);
        if (enable) {
                if (!dev->keeps_focus_pos) {
@@ -1986,6 +2067,15 @@ static int mt9e013_s_config(struct v4l2_subdev *sd,
 
        dev->platform_data = pdata;
 
+       atomic_set(&dev->power_state, POWER_OFF);
+       dev->work_queue = create_singlethread_workqueue("mt9e013_work");
+       if (dev->work_queue == NULL) {
+               v4l2_err(client, "Failed to initialize work queue\n");
+               return -EINVAL;
+       }
+       INIT_WORK(&dev->work, mt9e013_powerup_work);
+       init_completion(&dev->power_complete);
+
        mutex_lock(&dev->input_lock);
 
        ret = mt9e013_s_power(sd, 1);
@@ -1995,6 +2085,10 @@ static int mt9e013_s_config(struct v4l2_subdev *sd,
                return ret;
        }
 
+       ret = wait_power_on_lock(sd);
+       if (ret)
+               goto fail_csi_cfg;
+
        ret = dev->platform_data->csi_cfg(sd, 1);
        if (ret)
                goto fail_csi_cfg;
index 9b71384..0955d95 100644 (file)
@@ -182,6 +182,12 @@ enum mt9e013_tok_type {
        MT9E013_TOK_DELAY  = 0xfe00     /* delay token for reg list */
 };
 
+enum mt9e013_power_state {
+       POWER_OFF,
+       POWER_ON_INPROGRESS,
+       POWER_ON,
+       POWER_ON_ERROR
+};
 /*
  * If register address or register width is not 32 bit width,
  * user needs to convert it manually
@@ -286,7 +292,6 @@ struct mt9e013_device {
        int fmt_idx;
        int status;
        int streaming;
-       int power;
        u8 res;
        u8 type;
        u16 sensor_id;
@@ -306,6 +311,11 @@ struct mt9e013_device {
        void *fuseid;
        /* Older VCMs could not maintain the focus position in standby mode. */
        bool keeps_focus_pos;
+
+       struct workqueue_struct *work_queue;
+       struct work_struct work;
+       atomic_t power_state;
+       struct completion power_complete;
 };
 
 #define MT9E013_MAX_WRITE_BUF_SIZE     32