* it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/kernel.h>
 #include <linux/media.h>
        struct v4l2_subdev sd;
        struct media_pad pad;
        enum v4l2_mbus_type bus_type;
-       int gpios[NUM_GPIOS];
+       struct gpio_desc *gpios[NUM_GPIOS];
        /* External master clock frequency */
        unsigned long mclk_frequency;
+       struct clk *clk;
 
        /* Protects the struct fields below */
        struct mutex lock;
        return 0;
 }
 
-static void ov965x_gpio_set(int gpio, int val)
-{
-       if (gpio_is_valid(gpio))
-               gpio_set_value(gpio, val);
-}
-
-static void __ov965x_set_power(struct ov965x *ov965x, int on)
+static int __ov965x_set_power(struct ov965x *ov965x, int on)
 {
        if (on) {
-               ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 0);
-               ov965x_gpio_set(ov965x->gpios[GPIO_RST], 0);
+               int ret = clk_prepare_enable(ov965x->clk);
+
+               if (ret)
+                       return ret;
+
+               gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 0);
+               gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 0);
                msleep(25);
        } else {
-               ov965x_gpio_set(ov965x->gpios[GPIO_RST], 1);
-               ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 1);
+               gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 1);
+               gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 1);
+
+               clk_disable_unprepare(ov965x->clk);
        }
 
        ov965x->streaming = 0;
+
+       return 0;
 }
 
 static int ov965x_s_power(struct v4l2_subdev *sd, int on)
 
        mutex_lock(&ov965x->lock);
        if (ov965x->power == !on) {
-               __ov965x_set_power(ov965x, on);
-               if (on) {
+               ret = __ov965x_set_power(ov965x, on);
+               if (!ret && on) {
                        ret = ov965x_write_array(client,
                                                 ov965x_init_regs);
                        ov965x->apply_frame_fmt = 1;
 /*
  * Reset and power down GPIOs configuration
  */
-static int ov965x_configure_gpios(struct ov965x *ov965x,
-                                 const struct ov9650_platform_data *pdata)
+static int ov965x_configure_gpios_pdata(struct ov965x *ov965x,
+                               const struct ov9650_platform_data *pdata)
 {
        int ret, i;
+       int gpios[NUM_GPIOS];
 
-       ov965x->gpios[GPIO_PWDN] = pdata->gpio_pwdn;
-       ov965x->gpios[GPIO_RST]  = pdata->gpio_reset;
+       gpios[GPIO_PWDN] = pdata->gpio_pwdn;
+       gpios[GPIO_RST]  = pdata->gpio_reset;
 
        for (i = 0; i < ARRAY_SIZE(ov965x->gpios); i++) {
-               int gpio = ov965x->gpios[i];
+               int gpio = gpios[i];
 
                if (!gpio_is_valid(gpio))
                        continue;
                        return ret;
                v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio);
 
-               gpio_set_value(gpio, 1);
+               gpio_set_value_cansleep(gpio, 1);
                gpio_export(gpio, 0);
-               ov965x->gpios[i] = gpio;
+               ov965x->gpios[i] = gpio_to_desc(gpio);
+       }
+
+       return 0;
+}
+
+static int ov965x_configure_gpios(struct ov965x *ov965x)
+{
+       struct device *dev = &ov965x->client->dev;
+
+       ov965x->gpios[GPIO_PWDN] = devm_gpiod_get_optional(dev, "powerdown",
+                                                       GPIOD_OUT_HIGH);
+       if (IS_ERR(ov965x->gpios[GPIO_PWDN])) {
+               dev_info(dev, "can't get %s GPIO\n", "powerdown");
+               return PTR_ERR(ov965x->gpios[GPIO_PWDN]);
+       }
+
+       ov965x->gpios[GPIO_RST] = devm_gpiod_get_optional(dev, "reset",
+                                                       GPIOD_OUT_HIGH);
+       if (IS_ERR(ov965x->gpios[GPIO_RST])) {
+               dev_info(dev, "can't get %s GPIO\n", "reset");
+               return PTR_ERR(ov965x->gpios[GPIO_RST]);
        }
 
        return 0;
        int ret;
 
        mutex_lock(&ov965x->lock);
-       __ov965x_set_power(ov965x, 1);
+       ret = __ov965x_set_power(ov965x, 1);
+       if (ret)
+               goto out;
+
        msleep(25);
 
        /* Check sensor revision */
                        ret = -ENODEV;
                }
        }
+out:
        mutex_unlock(&ov965x->lock);
 
        return ret;
        struct ov965x *ov965x;
        int ret;
 
-       if (!pdata) {
-               dev_err(&client->dev, "platform data not specified\n");
-               return -EINVAL;
-       }
-
-       if (pdata->mclk_frequency == 0) {
-               dev_err(&client->dev, "MCLK frequency not specified\n");
-               return -EINVAL;
-       }
-
        ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL);
        if (!ov965x)
                return -ENOMEM;
 
-       mutex_init(&ov965x->lock);
        ov965x->client = client;
-       ov965x->mclk_frequency = pdata->mclk_frequency;
+
+       if (pdata) {
+               if (pdata->mclk_frequency == 0) {
+                       dev_err(&client->dev, "MCLK frequency not specified\n");
+                       return -EINVAL;
+               }
+               ov965x->mclk_frequency = pdata->mclk_frequency;
+
+               ret = ov965x_configure_gpios_pdata(ov965x, pdata);
+               if (ret < 0)
+                       return ret;
+       } else if (dev_fwnode(&client->dev)) {
+               ov965x->clk = devm_clk_get(&ov965x->client->dev, NULL);
+               if (IS_ERR(ov965x->clk))
+                       return PTR_ERR(ov965x->clk);
+               ov965x->mclk_frequency = clk_get_rate(ov965x->clk);
+
+               ret = ov965x_configure_gpios(ov965x);
+               if (ret < 0)
+                       return ret;
+       } else {
+               dev_err(&client->dev,
+                       "Neither platform data nor device property specified\n");
+
+               return -EINVAL;
+       }
+
+       mutex_init(&ov965x->lock);
 
        sd = &ov965x->sd;
        v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops);
        sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
                     V4L2_SUBDEV_FL_HAS_EVENTS;
 
-       ret = ov965x_configure_gpios(ov965x, pdata);
-       if (ret < 0)
-               goto err_mutex;
-
        ov965x->pad.flags = MEDIA_PAD_FL_SOURCE;
        sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
        ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad);
 };
 MODULE_DEVICE_TABLE(i2c, ov965x_id);
 
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id ov965x_of_match[] = {
+       { .compatible = "ovti,ov9650", },
+       { .compatible = "ovti,ov9652", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov965x_of_match);
+#endif
+
 static struct i2c_driver ov965x_i2c_driver = {
        .driver = {
                .name   = DRIVER_NAME,
+               .of_match_table = of_match_ptr(ov965x_of_match),
        },
        .probe          = ov965x_probe,
        .remove         = ov965x_remove,