#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#define MAX_RETRY 10
+#define DW9807_PW_MIN_DELAY_US 100
+#define DW9807_PW_DELAY_RANGE_US 10
+
struct dw9807_cfg {
unsigned int idle_pos;
unsigned int default_pos;
struct v4l2_subdev sd;
u16 current_val;
u16 idle_pos;
+ struct regulator *vdd;
+ struct notifier_block notifier;
};
static inline struct dw9807_device *sd_to_dw9807_vcm(
return ret;
}
+static int dw9807_active(struct dw9807_device *dw9807_dev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&dw9807_dev->sd);
+ const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 };
+ int ret;
+
+ /* Power on */
+ ret = i2c_master_send(client, tx_data, sizeof(tx_data));
+ if (ret < 0) {
+ dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret);
+ return ret;
+ }
+
+ return dw9807_ramp(client, dw9807_dev->idle_pos, dw9807_dev->current_val);
+}
+
+static int dw9807_standby(struct dw9807_device *dw9807_dev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&dw9807_dev->sd);
+ const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 };
+ int ret;
+
+ if (abs(dw9807_dev->current_val - dw9807_dev->idle_pos) > DW9807_CTRL_STEPS)
+ dw9807_ramp(client, dw9807_dev->current_val, dw9807_dev->idle_pos);
+
+ /* Power down */
+ ret = i2c_master_send(client, tx_data, sizeof(tx_data));
+ if (ret < 0) {
+ dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dw9807_regulator_event(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct dw9807_device *dw9807_dev =
+ container_of(nb, struct dw9807_device, notifier);
+
+ if (action & REGULATOR_EVENT_ENABLE) {
+ /*
+ * Initialisation delay between VDD low->high and the moment
+ * when the i2c command is available.
+ * From the datasheet, it should be 10ms + 2ms (max power
+ * up sequence duration)
+ */
+ usleep_range(DW9807_PW_MIN_DELAY_US,
+ DW9807_PW_MIN_DELAY_US +
+ DW9807_PW_DELAY_RANGE_US);
+
+ dw9807_active(dw9807_dev);
+ } else if (action & REGULATOR_EVENT_PRE_DISABLE) {
+ dw9807_standby(dw9807_dev);
+ }
+
+ return 0;
+}
+
static int dw9807_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct dw9807_device *dev_vcm = container_of(ctrl->handler,
if (dw9807_dev == NULL)
return -ENOMEM;
+ dw9807_dev->vdd = devm_regulator_get_optional(&client->dev, "VDD");
+ if (IS_ERR(dw9807_dev->vdd)) {
+ if (PTR_ERR(dw9807_dev->vdd) != -ENODEV)
+ return PTR_ERR(dw9807_dev->vdd);
+
+ dw9807_dev->vdd = NULL;
+ } else {
+ dw9807_dev->notifier.notifier_call = dw9807_regulator_event;
+
+ rval = regulator_register_notifier(dw9807_dev->vdd,
+ &dw9807_dev->notifier);
+ if (rval) {
+ dev_err(&client->dev,
+ "could not register regulator notifier\n");
+ return rval;
+ }
+ }
+
match = i2c_of_match_device(dw9807_of_table, client);
if (match) {
cfg = (const struct dw9807_cfg *)match->data;
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
- const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 };
- int ret;
-
- if (abs(dw9807_dev->current_val - dw9807_dev->idle_pos) > DW9807_CTRL_STEPS)
- dw9807_ramp(client, dw9807_dev->current_val, dw9807_dev->idle_pos);
- /* Power down */
- ret = i2c_master_send(client, tx_data, sizeof(tx_data));
- if (ret < 0) {
- dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret);
- return ret;
- }
+ if (dw9807_dev->vdd)
+ return regulator_disable(dw9807_dev->vdd);
- return 0;
+ return dw9807_standby(dw9807_dev);
}
/*
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
- const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 };
- int ret;
-
- /* Power on */
- ret = i2c_master_send(client, tx_data, sizeof(tx_data));
- if (ret < 0) {
- dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret);
- return ret;
- }
- dw9807_ramp(client, dw9807_dev->idle_pos, dw9807_dev->current_val);
+ if (dw9807_dev->vdd)
+ return regulator_enable(dw9807_dev->vdd);
- return 0;
+ return dw9807_active(dw9807_dev);
}
MODULE_DEVICE_TABLE(of, dw9807_of_table);