media: video: tegra: enable DT for ov5693
Jake Park [Thu, 28 Mar 2013 12:58:25 +0000 (21:58 +0900)]
Bug 1255103

Change-Id: I26acd27ae331343d0c8c8c1d6d5216ff45c07867
Signed-off-by: Jake Park <jakep@nvidia.com>
Reviewed-on: http://git-master/r/213972
(cherry picked from commit 54cac095ddb87109a5ab2885c34df0a4adc8e71b)
Reviewed-on: http://git-master/r/223185
Reviewed-by: Mrutyunjay Sawant <msawant@nvidia.com>
Tested-by: Mrutyunjay Sawant <msawant@nvidia.com>

Documentation/devicetree/bindings/video/nvidia,ov5693.txt [new file with mode: 0644]
drivers/media/video/tegra/ov5693.c
include/media/ov5693.h

diff --git a/Documentation/devicetree/bindings/video/nvidia,ov5693.txt b/Documentation/devicetree/bindings/video/nvidia,ov5693.txt
new file mode 100644 (file)
index 0000000..0aeb4b2
--- /dev/null
@@ -0,0 +1,32 @@
+NVIDIA Camera sensor ov5693 driver interface.
+
+Properties:
+- compatible : The driver is compatible with
+       "nvidia,ov5693".
+
+- reg : Should contain I2C slave address of the driver.
+
+- nvidia,num : Indicates the instance of particular camera device, in this
+case it is for camera sensor device. Currently for rear camera sensor "0"
+is used.
+
+- nvidia,dev-name : Camera device driver name.
+
+- nvidia,use-vcm-vdd : For few platforms this driver needs extra power
+regulator "ext_vcm_vdd" to be enabled. This bool property indicates the
+same. It must be programmed only when ov5693 driver needs it for a
+particular platform.
+
+- reset-gpios : ov5693 reset GPIO
+
+Example:
+
+       ov5693@10 {
+               compatible = "nvidia,ov5693";
+               reg = <0x10>;
+               nvidia,num = <0>;
+               nvidia,dev-name = "camera";
+               nvidia,use-vcm-vdd;
+               reset-gpios = <&gpio 219 0>; /* gpio PBB3 */
+               status = "okay";
+       };
index 1832941..f0a8b12 100644 (file)
 #include <linux/gpio.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
-
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 #include <media/ov5693.h>
 
 #define OV5693_ID                      0x5693
@@ -63,6 +66,7 @@ struct ov5693_info {
        u8 bin_en;
        struct ov5693_fuseid fuseid;
        struct regmap *regmap;
+       struct regulator *ext_vcm_vdd;
 };
 
 struct ov5693_reg {
@@ -1892,6 +1896,131 @@ static int ov5693_remove(struct i2c_client *client)
        return 0;
 }
 
+static struct of_device_id ov5693_of_match[] = {
+       { .compatible = "nvidia,ov5693", },
+       { },
+};
+
+MODULE_DEVICE_TABLE(of, ov5693_of_match);
+
+static int ov5693_platform_power_on(struct ov5693_power_rail *pw)
+{
+       int err;
+       struct ov5693_info *info = container_of(pw, struct ov5693_info,
+                                               regulators);
+
+       if (info->pdata->use_vcm_vdd) {
+               err = regulator_enable(info->ext_vcm_vdd);
+               if (unlikely(err))
+                       goto ov5693_vcm_fail;
+       }
+
+       ov5693_gpio_wr(info, OV5693_GPIO_TYPE_PWRDN, 0);
+       usleep_range(10, 20);
+
+       err = regulator_enable(pw->avdd);
+       if (err)
+               goto ov5693_avdd_fail;
+
+       err = regulator_enable(pw->dovdd);
+       if (err)
+               goto ov5693_iovdd_fail;
+
+       usleep_range(1, 2);
+       ov5693_gpio_wr(info, OV5693_GPIO_TYPE_PWRDN, 1);
+
+       usleep_range(300, 310);
+
+       return 0;
+
+ov5693_iovdd_fail:
+       regulator_disable(pw->avdd);
+
+ov5693_avdd_fail:
+       if (info->pdata->use_vcm_vdd)
+               regulator_disable(info->ext_vcm_vdd);
+
+ov5693_vcm_fail:
+       pr_err("%s FAILED\n", __func__);
+       return err;
+}
+
+static int ov5693_platform_power_off(struct ov5693_power_rail *pw)
+{
+       struct ov5693_info *info = container_of(pw, struct ov5693_info,
+                                               regulators);
+
+       usleep_range(21, 25);
+       ov5693_gpio_wr(info, OV5693_GPIO_TYPE_PWRDN, 0);
+       usleep_range(1, 2);
+
+       regulator_disable(pw->dovdd);
+       regulator_disable(pw->avdd);
+       if (info->pdata->use_vcm_vdd)
+               regulator_disable(info->ext_vcm_vdd);
+
+       return 0;
+}
+
+static int ov5693_parse_dt_gpio(struct device_node *np, const char *name,
+                               enum ov5693_gpio_type type,
+                               struct nvc_gpio_pdata *pdata)
+{
+       enum of_gpio_flags gpio_flags;
+
+       if (of_find_property(np, name, NULL)) {
+               pdata->gpio = of_get_named_gpio_flags(np, name, 0, &gpio_flags);
+               pdata->gpio_type = type;
+               pdata->init_en = true;
+               pdata->active_high = !(gpio_flags & OF_GPIO_ACTIVE_LOW);
+               return 1;
+       }
+       return 0;
+}
+
+static struct ov5693_platform_data *ov5693_parse_dt(struct i2c_client *client)
+{
+       struct device_node *np = client->dev.of_node;
+       struct ov5693_platform_data *pdata;
+       struct nvc_gpio_pdata *gpio_pdata = NULL;
+
+       pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata) {
+               dev_err(&client->dev, "Failed to allocate pdata\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       gpio_pdata = devm_kzalloc(&client->dev,
+               sizeof(*gpio_pdata) * ARRAY_SIZE(ov5693_gpio), GFP_KERNEL);
+       if (!gpio_pdata) {
+               dev_err(&client->dev, "cannot allocate gpio data memory\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       /* init with default platform data values */
+       memcpy(pdata, &ov5693_dflt_pdata, sizeof(*pdata));
+
+       /* extra regulators info */
+       pdata->use_vcm_vdd = of_property_read_bool(np, "nvidia,use-vcm-vdd");
+
+       /* generic info */
+       of_property_read_u32(np, "nvidia,num", &pdata->num);
+       of_property_read_string(np, "nvidia,dev-name", &pdata->dev_name);
+
+       /* ov5693 gpios */
+       pdata->gpio_count = 0;
+       pdata->gpio_count += ov5693_parse_dt_gpio(np,
+                               "reset-gpios", OV5693_GPIO_TYPE_PWRDN,
+                               &gpio_pdata[pdata->gpio_count]);
+       pdata->gpio = gpio_pdata;
+
+       /* ov5693 power functions */
+       pdata->power_on = ov5693_platform_power_on;
+       pdata->power_off = ov5693_platform_power_off;
+
+       return pdata;
+}
+
 static int ov5693_probe(
        struct i2c_client *client,
        const struct i2c_device_id *id)
@@ -1914,7 +2043,15 @@ static int ov5693_probe(
        }
 
        info->i2c_client = client;
-       if (client->dev.platform_data)
+       if (client->dev.of_node) {
+               info->pdata = ov5693_parse_dt(client);
+               if (IS_ERR(info->pdata)) {
+                       err = PTR_ERR(info->pdata);
+                       dev_err(&client->dev,
+                               "Failed to parse OF node: %d\n", err);
+                       return err;
+               }
+       } else if (client->dev.platform_data)
                info->pdata = client->dev.platform_data;
        else {
                info->pdata = &ov5693_dflt_pdata;
@@ -1922,6 +2059,17 @@ static int ov5693_probe(
                        "%s No platform data. Using defaults.\n",
                        __func__);
        }
+       if (info->pdata->use_vcm_vdd) {
+               info->ext_vcm_vdd = devm_regulator_get(&info->i2c_client->dev,
+                                                       "ext_vcm_vdd");
+               if (WARN_ON(IS_ERR(info->ext_vcm_vdd))) {
+                       err = PTR_ERR(info->ext_vcm_vdd);
+                       dev_err(&client->dev,
+                               "ext_vcm_vdd get failed %d\n", err);
+                       info->ext_vcm_vdd = NULL;
+                       return err;
+               }
+       }
 
        info->regmap = devm_regmap_init_i2c(client, &ad5823_regmap_config);
        if (IS_ERR(info->regmap)) {
@@ -1934,6 +2082,9 @@ static int ov5693_probe(
 
        i2c_set_clientdata(client, info);
        ov5693_pm_init(info);
+       if (!info->regulators.avdd || !info->regulators.dovdd)
+               return -EFAULT;
+
        ov5693_sdata_init(info);
        if (info->pdata->cfg & (NVC_CFG_NODEV | NVC_CFG_BOOT_INIT)) {
                if (info->pdata->probe_clock) {
@@ -1979,6 +2130,7 @@ static struct i2c_driver ov5693_i2c_driver = {
        .driver = {
                .name = "ov5693",
                .owner = THIS_MODULE,
+               .of_match_table = ov5693_of_match,
        },
        .id_table = ov5693_id,
        .probe = ov5693_probe,
index 21e10e8..d81c36b 100644 (file)
@@ -81,6 +81,7 @@ struct ov5693_platform_data {
        unsigned lens_fnumber; /* / _INT2FLOAT_DIVISOR */
        unsigned lens_view_angle_h; /* / _INT2FLOAT_DIVISOR */
        unsigned lens_view_angle_v; /* / _INT2FLOAT_DIVISOR */
+       bool use_vcm_vdd;
        int (*probe_clock)(unsigned long);
        int (*power_on)(struct ov5693_power_rail *);
        int (*power_off)(struct ov5693_power_rail *);