media: video: tegra: ov5650: read sensor fuse id
[linux-2.6.git] / drivers / media / video / tegra / ov5650.c
index 3adb46a..3cfb956 100644 (file)
@@ -39,6 +39,11 @@ struct ov5650_info {
        enum StereoCameraMode camera_mode;
        struct ov5650_sensor left;
        struct ov5650_sensor right;
+       struct ov5650_sensordata sensor_data;
+       struct mutex mutex_le;
+       struct mutex mutex_ri;
+       int power_refcnt_le;
+       int power_refcnt_ri;
        u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF];
 };
 
@@ -788,7 +793,7 @@ static int ov5650_read_reg(struct i2c_client *client, u16 addr, u8 *val)
 
        err = i2c_transfer(client->adapter, msg, 2);
 
-       if (err != 1)
+       if (err != 2)
                return -EINVAL;
 
        *val = data[2];
@@ -1245,31 +1250,71 @@ static int ov5650_test_pattern(struct ov5650_info *info,
 }
 
 static int set_power_helper(struct ov5650_platform_data *pdata,
-                               int powerLevel)
+                               int powerLevel, int *ref_cnt)
 {
        if (pdata) {
-               if (powerLevel && pdata->power_on)
-                       pdata->power_on();
-               else if (pdata->power_off)
-                       pdata->power_off();
+               if (powerLevel && pdata->power_on) {
+                       if (*ref_cnt == 0)
+                               pdata->power_on();
+                       *ref_cnt = *ref_cnt + 1;
+               }
+               else if (pdata->power_off) {
+                       *ref_cnt = *ref_cnt - 1;
+                       if (*ref_cnt <= 0)
+                               pdata->power_off();
+               }
        }
        return 0;
 }
 
-static int ov5650_set_power(int powerLevel)
+static int ov5650_set_power(struct ov5650_info *info, int powerLevel)
 {
        pr_info("%s: powerLevel=%d camera mode=%d\n", __func__, powerLevel,
-                       stereo_ov5650_info->camera_mode);
+                       info->camera_mode);
 
-       if (StereoCameraMode_Left & stereo_ov5650_info->camera_mode)
-               set_power_helper(stereo_ov5650_info->left.pdata, powerLevel);
+       if (StereoCameraMode_Left & info->camera_mode) {
+               mutex_lock(&info->mutex_le);
+               set_power_helper(info->left.pdata, powerLevel,
+                       &info->power_refcnt_le);
+               mutex_unlock(&info->mutex_le);
+       }
 
-       if (StereoCameraMode_Right & stereo_ov5650_info->camera_mode)
-               set_power_helper(stereo_ov5650_info->right.pdata, powerLevel);
+       if (StereoCameraMode_Right & info->camera_mode) {
+               mutex_lock(&info->mutex_ri);
+               set_power_helper(info->right.pdata, powerLevel,
+                       &info->power_refcnt_ri);
+               mutex_unlock(&info->mutex_ri);
+       }
 
        return 0;
 }
 
+static int ov5650_get_sensor_id(struct ov5650_info *info)
+{
+       int ret = 0;
+       int i;
+       u8  bak;
+
+       pr_info("%s\n", __func__);
+       if (info->sensor_data.fuse_id_size)
+               return 0;
+
+       ov5650_set_power(info, 1);
+
+       for (i = 0; i < 5; i++) {
+               ret |= ov5650_write_reg_helper(info, 0x3d00, i);
+               ret |= ov5650_read_reg_helper(info, 0x3d04,
+                               &bak);
+               info->sensor_data.fuse_id[i] = bak;
+       }
+
+       if (!ret)
+               info->sensor_data.fuse_id_size = i;
+
+       ov5650_set_power(info, 0);
+       return ret;
+}
+
 static long ov5650_ioctl(struct file *file,
                         unsigned int cmd, unsigned long arg)
 {
@@ -1280,13 +1325,13 @@ static long ov5650_ioctl(struct file *file,
        case OV5650_IOCTL_SET_CAMERA_MODE:
        {
                if (info->camera_mode != arg) {
-                       err = ov5650_set_power(0);
+                       err = ov5650_set_power(info, 0);
                        if (err) {
                                pr_info("%s %d\n", __func__, __LINE__);
                                return err;
                        }
                        info->camera_mode = arg;
-                       err = ov5650_set_power(1);
+                       err = ov5650_set_power(info, 1);
                        if (err)
                                return err;
                }
@@ -1344,6 +1389,21 @@ static long ov5650_ioctl(struct file *file,
                }
                return ov5650_set_group_hold(info, &ae);
        }
+       case OV5650_IOCTL_GET_SENSORDATA:
+       {
+               err = ov5650_get_sensor_id(info);
+               if (err) {
+                       pr_err("%s %d %d\n", __func__, __LINE__, err);
+                       return err;
+               }
+               if (copy_to_user((void __user *)arg,
+                               &info->sensor_data,
+                               sizeof(struct ov5650_sensordata))) {
+                       pr_info("%s %d\n", __func__, __LINE__);
+                       return -EFAULT;
+               }
+               return 0;
+       }
        default:
                return -EINVAL;
        }
@@ -1354,13 +1414,15 @@ static int ov5650_open(struct inode *inode, struct file *file)
 {
        pr_info("%s\n", __func__);
        file->private_data = stereo_ov5650_info;
-       ov5650_set_power(1);
+       ov5650_set_power(stereo_ov5650_info, 1);
        return 0;
 }
 
 int ov5650_release(struct inode *inode, struct file *file)
 {
-       ov5650_set_power(0);
+       struct ov5650_info *info = file->private_data;
+
+       ov5650_set_power(info, 0);
        file->private_data = NULL;
        return 0;
 }
@@ -1426,6 +1488,8 @@ static int left_ov5650_probe(struct i2c_client *client,
 
        stereo_ov5650_info->left.pdata = client->dev.platform_data;
        stereo_ov5650_info->left.i2c_client = client;
+       mutex_init(&stereo_ov5650_info->mutex_le);
+       mutex_init(&stereo_ov5650_info->mutex_ri);
 
        return 0;
 }