@@ -18,6 +18,8 @@
#include <linux/i2c.h>
#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
@@ -141,6 +143,12 @@ module_param(debug, int, 0644);
#define EXPOS_MIN_MS 1
#define EXPOS_MAX_MS 125
+static const char * const sr030pc30_supply_name[] = {
+ "vdd_core", "vddio", "vdda"
+};
+
+#define SR030PC30_NUM_SUPPLIES ARRAY_SIZE(sr030pc30_supply_name)
+
struct sr030pc30_info {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
@@ -160,7 +168,11 @@ struct sr030pc30_info {
const struct sr030pc30_platform_data *pdata;
const struct sr030pc30_format *curr_fmt;
const struct sr030pc30_frmsize *curr_win;
+ unsigned int power:1;
unsigned int sleep:1;
+ struct regulator_bulk_data supply[SR030PC30_NUM_SUPPLIES];
+ u32 gpio_nreset;
+ u32 gpio_nstby;
u8 i2c_reg_page;
};
@@ -473,7 +485,7 @@ static int sr030pc30_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_AUTO_WHITE_BALANCE:
- if (!ctrl->has_new)
+ if (!ctrl->is_new)
ctrl->val = 0;
ret = sr030pc30_enable_autowhitebalance(sd, ctrl->val);
@@ -487,7 +499,7 @@ static int sr030pc30_s_ctrl(struct v4l2_ctrl *ctrl)
return ret;
case V4L2_CID_EXPOSURE_AUTO:
- if (!ctrl->has_new)
+ if (!ctrl->is_new)
ctrl->val = V4L2_EXPOSURE_MANUAL;
if (ctrl->val == V4L2_EXPOSURE_MANUAL)
@@ -622,9 +634,66 @@ static int sr030pc30_base_config(struct v4l2_subdev *sd)
return v4l2_ctrl_handler_setup(&info->hdl);
}
+static int sr030pc30_power_enable(struct sr030pc30_info *info)
+{
+ int ret;
+
+ if (info->power)
+ return 0;
+
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_set_value(info->gpio_nstby, 0);
+
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_set_value(info->gpio_nreset, 0);
+
+ ret = regulator_bulk_enable(SR030PC30_NUM_SUPPLIES, info->supply);
+ if (ret)
+ return ret;
+
+ if (gpio_is_valid(info->gpio_nreset)) {
+ msleep(50);
+ gpio_set_value(info->gpio_nreset, 1);
+ }
+ if (gpio_is_valid(info->gpio_nstby)) {
+ udelay(1000);
+ gpio_set_value(info->gpio_nstby, 1);
+ }
+ if (gpio_is_valid(info->gpio_nreset)) {
+ udelay(1000);
+ gpio_set_value(info->gpio_nreset, 0);
+ msleep(100);
+ gpio_set_value(info->gpio_nreset, 1);
+ msleep(20);
+ }
+
+ info->power = 1;
+ return 0;
+}
+
+static int sr030pc30_power_disable(struct sr030pc30_info *info)
+{
+ int ret;
+
+ if (!info->power)
+ return 0;
+
+ ret = regulator_bulk_disable(SR030PC30_NUM_SUPPLIES, info->supply);
+ if (ret)
+ return ret;
+
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_set_value(info->gpio_nstby, 0);
+
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_set_value(info->gpio_nreset, 0);
+
+ info->power = 0;
+ return 0;
+}
+
static int sr030pc30_s_power(struct v4l2_subdev *sd, int on)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
struct sr030pc30_info *info = to_sr030pc30(sd);
const struct sr030pc30_platform_data *pdata = info->pdata;
int ret;
@@ -632,23 +701,15 @@ static int sr030pc30_s_power(struct v4l2_subdev *sd, int on)
if (WARN(pdata == NULL, "No platform data!\n"))
return -ENOMEM;
- /*
- * Put sensor into power sleep mode before switching off
- * power and disabling MCLK.
- */
- if (!on)
- sr030pc30_pwr_ctrl(sd, false, true);
-
- /* set_power controls sensor's power and clock */
- if (pdata->set_power) {
- ret = pdata->set_power(&client->dev, on);
+ /* Put sensor into power sleep mode before switching supply off. */
+ if (on) {
+ ret = sr030pc30_power_enable(info);
if (ret)
return ret;
- }
-
- if (on) {
ret = sr030pc30_base_config(sd);
} else {
+ sr030pc30_pwr_ctrl(sd, false, true);
+ ret = sr030pc30_power_disable(info);
info->curr_win = NULL;
info->curr_fmt = NULL;
}
@@ -696,30 +757,40 @@ static const struct v4l2_subdev_ops sr030pc30_ops = {
* Detect sensor type. Return 0 if SR030PC30 was detected
* or -ENODEV otherwise.
*/
-static int sr030pc30_detect(struct i2c_client *client)
+static int sr030pc30_detect(struct i2c_client *client, struct sr030pc30_info *info)
{
- const struct sr030pc30_platform_data *pdata
- = client->dev.platform_data;
int ret;
- /* Enable sensor's power and clock */
- if (pdata->set_power) {
- ret = pdata->set_power(&client->dev, 1);
- if (ret)
- return ret;
- }
+ ret = sr030pc30_power_enable(info);
+ if (ret)
+ return ret;
ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG);
+ if (ret < 0)
+ dev_err(&client->dev, "%s: I2C read failed\n", __func__);
- if (pdata->set_power)
- pdata->set_power(&client->dev, 0);
+ sr030pc30_power_disable(info);
- if (ret < 0) {
- dev_err(&client->dev, "%s: I2C read failed\n", __func__);
+ return ret == SR030PC30_ID ? 0 : -ENODEV;
+}
+
+static int sr030pc30_configure_gpio(struct v4l2_subdev *sd,
+ int nr, const char *name)
+{
+ int ret = 0;
+
+ if (!gpio_is_valid(nr))
return ret;
- }
- return ret == SR030PC30_ID ? 0 : -ENODEV;
+ ret = gpio_request(nr, name);
+ if (!ret)
+ ret = gpio_direction_output(nr, 0);
+ if (!ret)
+ ret = gpio_export(nr, 0);
+ if (ret)
+ v4l2_err(sd, "gpio configuration error: %d\n", ret);
+
+ return ret;
}
static int sr030pc30_probe(struct i2c_client *client,
@@ -729,17 +800,13 @@ static int sr030pc30_probe(struct i2c_client *client,
struct v4l2_subdev *sd;
const struct sr030pc30_platform_data *pdata
= client->dev.platform_data;
- int ret;
+ int i, ret;
if (!pdata) {
dev_err(&client->dev, "No platform data!\n");
return -EIO;
}
- ret = sr030pc30_detect(client);
- if (ret)
- return ret;
-
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -774,16 +841,49 @@ static int sr030pc30_probe(struct i2c_client *client,
1, 0, V4L2_EXPOSURE_AUTO);
sd->ctrl_handler = &info->hdl;
+ ret = info->hdl.error;
- if (info->hdl.error) {
- v4l2_ctrl_handler_free(&info->hdl);
- kfree(info);
- return info->hdl.error;
- }
+ if (ret)
+ goto sp_err;
v4l2_ctrl_cluster(2, &info->autoexposure);
v4l2_ctrl_cluster(3, &info->autowb);
- return 0;
+
+ ret = sr030pc30_configure_gpio(sd, pdata->gpio_nreset,
+ "SR030PC30_NRST");
+ if (ret)
+ goto sp_err;
+ info->gpio_nreset = pdata->gpio_nreset;
+
+ ret = sr030pc30_configure_gpio(sd, pdata->gpio_nstby,
+ "SR030PC30_NSTBY");
+ if (ret)
+ goto sp_gpio_err;
+ info->gpio_nstby = pdata->gpio_nstby;
+
+ for (i = 0; i < SR030PC30_NUM_SUPPLIES; i++)
+ info->supply[i].supply = sr030pc30_supply_name[i];
+
+ ret = regulator_bulk_get(&client->dev, SR030PC30_NUM_SUPPLIES,
+ info->supply);
+ if (ret)
+ goto sp_reg_err;
+
+ ret = sr030pc30_detect(client, info);
+ if (!ret)
+ return 0;
+
+ regulator_bulk_free(SR030PC30_NUM_SUPPLIES, info->supply);
+sp_reg_err:
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_free(info->gpio_nstby);
+sp_gpio_err:
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_free(info->gpio_nreset);
+sp_err:
+ v4l2_ctrl_handler_free(&info->hdl);
+ kfree(info);
+ return ret;
}
static int sr030pc30_remove(struct i2c_client *client)
@@ -793,6 +893,15 @@ static int sr030pc30_remove(struct i2c_client *client)
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&info->hdl);
+
+ regulator_bulk_free(SR030PC30_NUM_SUPPLIES, info->supply);
+
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_free(info->gpio_nreset);
+
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_free(info->gpio_nstby);
+
kfree(info);
return 0;
}
@@ -13,9 +13,17 @@
#ifndef SR030PC30_H
#define SR030PC30_H
+/**
+ * @clk_rate: the sensor's master clock frequency in Hz
+ * @gpio_nreset: GPIO driving nRESET pin
+ * @gpio_nstby: GPIO driving nSTBY pin
+ *
+ * When the gpio pins are not used gpio_nreset
+ * and gpio_nstby must be set to -EINVAL.
+ */
struct sr030pc30_platform_data {
- unsigned long clk_rate; /* master clock frequency in Hz */
- int (*set_power)(struct device *dev, int on);
+ unsigned long clk_rate;
+ int gpio_nreset;
+ int gpio_nstby;
};
-
#endif /* SR030PC30_H */