From 1428b87c861f5e007fa1becd137f5a72d83badd0 Mon Sep 17 00:00:00 2001 From: Tao Jing Date: Fri, 13 Apr 2012 11:26:33 +0800 Subject: [PATCH] mt9e013: async power up sequence to decrease Lanuch2preview time 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 Signed-off-by: ziyux.jiang Reviewed-on: http://android.intel.com:8080/44109 Reviewed-by: Bhat, Sheethal Reviewed-by: Wang, Wen W Reviewed-by: Lampila, KalleX Tested-by: Lampila, KalleX Reviewed-by: buildbot Tested-by: buildbot --- drivers/media/video/mt9e013.c | 108 +++++++++++++++++++++++++++++++++++++++--- drivers/media/video/mt9e013.h | 12 ++++- 2 files changed, 112 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/mt9e013.c b/drivers/media/video/mt9e013.c index 06374f2..8956e48 100644 --- a/drivers/media/video/mt9e013.c +++ b/drivers/media/video/mt9e013.c @@ -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; diff --git a/drivers/media/video/mt9e013.h b/drivers/media/video/mt9e013.h index 9b71384..0955d95 100644 --- a/drivers/media/video/mt9e013.h +++ b/drivers/media/video/mt9e013.h @@ -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 -- 2.7.4