media: video: tegra: ar0261: move MCLK into sensor
Pablo Ceballos [Tue, 2 Jul 2013 00:51:48 +0000 (17:51 -0700)]
Move MCLK control from tegra_camera into sensor driver,
to allow for full controll over MCLK rate and enable timing,
per sensor power-on/off sequence specification.

Bug 1298672

Change-Id: Iec76d84197c82fcacfb4f21edf375d724a1e3fe1
Signed-off-by: Pablo Ceballos <pceballos@nvidia.com>
Reviewed-on: http://git-master/r/244059
Reviewed-by: Amit Arora <amita@nvidia.com>
Tested-by: Amit Arora <amita@nvidia.com>

drivers/media/video/tegra/ar0261.c
include/media/ar0261.h

index beef9ed..d5f1113 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/delay.h>
 #include <linux/fs.h>
 #include <linux/i2c.h>
+#include <linux/clk.h>
 #include <linux/miscdevice.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
@@ -39,6 +40,7 @@ struct ar0261_info {
        struct ar0261_sensordata sensor_data;
        struct i2c_client *i2c_client;
        struct ar0261_platform_data *pdata;
+       struct clk *mclk;
        atomic_t in_use;
        int mode;
 };
@@ -693,6 +695,26 @@ static int ar0261_get_sensor_id(struct ar0261_info *info)
        return 0;
 }
 
+static void ar0261_mclk_disable(struct ar0261_info *info)
+{
+       dev_dbg(&info->i2c_client->dev, "%s: disable MCLK\n", __func__);
+       clk_disable_unprepare(info->mclk);
+}
+
+static int ar0261_mclk_enable(struct ar0261_info *info)
+{
+       int err;
+       unsigned long mclk_init_rate = 24000000;
+
+       dev_dbg(&info->i2c_client->dev, "%s: enable MCLK with %lu Hz\n",
+                       __func__, mclk_init_rate);
+
+       err = clk_set_rate(info->mclk, mclk_init_rate);
+       if (!err)
+               err = clk_prepare_enable(info->mclk);
+       return err;
+}
+
 static long
 ar0261_ioctl(struct file *file,
                         unsigned int cmd, unsigned long arg)
@@ -772,6 +794,7 @@ ar0261_ioctl(struct file *file,
 static int
 ar0261_open(struct inode *inode, struct file *file)
 {
+       int err;
        struct miscdevice *miscdev = file->private_data;
        struct ar0261_info *info;
 
@@ -784,15 +807,25 @@ ar0261_open(struct inode *inode, struct file *file)
 
        file->private_data = info;
 
+       err = ar0261_mclk_enable(info);
+       if (err < 0)
+               return err;
+
        if (info->pdata && info->pdata->power_on)
-               info->pdata->power_on(&info->power);
+               err = info->pdata->power_on(&info->power);
        else {
                dev_err(&info->i2c_client->dev,
                        "%s:no valid power_on function.\n", __func__);
-               return -EEXIST;
+               err = -EEXIST;
        }
+       if (err < 0)
+               goto ar0261_open_fail;
 
        return 0;
+
+ar0261_open_fail:
+       ar0261_mclk_disable(info);
+       return err;
 }
 
 static int
@@ -802,6 +835,9 @@ ar0261_release(struct inode *inode, struct file *file)
 
        if (info->pdata && info->pdata->power_off)
                info->pdata->power_off(&info->power);
+
+       ar0261_mclk_disable(info);
+
        file->private_data = NULL;
 
        /* warn if device is already released */
@@ -876,6 +912,7 @@ ar0261_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
        struct ar0261_info *info;
+       const char *mclk_name;
        int err = 0;
 
        pr_info("[ar0261]: probing sensor.\n");
@@ -892,6 +929,15 @@ ar0261_probe(struct i2c_client *client,
        atomic_set(&info->in_use, 0);
        info->mode = -1;
 
+       mclk_name = info->pdata->mclk_name ?
+               info->pdata->mclk_name : "default_mclk";
+       info->mclk = devm_clk_get(&client->dev, mclk_name);
+       if (IS_ERR(info->mclk)) {
+               dev_err(&client->dev, "%s: unable to get clock %s\n",
+                       __func__, mclk_name);
+               return PTR_ERR(info->mclk);
+       }
+
        i2c_set_clientdata(client, info);
 
        ar0261_power_get(info);
index 65ba55f..678dd0e 100644 (file)
@@ -62,6 +62,7 @@ struct ar0261_power_rail {
 };
 
 struct ar0261_platform_data {
+       const char *mclk_name; /* NULL for default default_mclk */
        int (*power_on)(struct ar0261_power_rail *pw);
        int (*power_off)(struct ar0261_power_rail *pw);
 };